./PaxHeaders/nghttp2-1.68.00000644000000000000000000000013215077107335012342 xustar0030 mtime=1761382109.757298482 30 atime=1761382109.794298375 30 ctime=1761382109.757298482 nghttp2-1.68.0/0000755000175100017510000000000015077107335012652 5ustar00runnerrunnernghttp2-1.68.0/PaxHeaders/CMakeOptions.txt0000644000000000000000000000013115077107270015354 xustar0030 mtime=1761382072.960444291 30 atime=1761382104.870316304 29 ctime=1761382107.85530398 nghttp2-1.68.0/CMakeOptions.txt0000644000175100017510000000253115077107270015746 0ustar00runnerrunner# Features that can be enabled for cmake (see CMakeLists.txt) option(ENABLE_WERROR "Turn on compile time warnings") option(ENABLE_DEBUG "Turn on debug output") option(ENABLE_THREADS "Turn on threading in apps" ON) option(ENABLE_APP "Build applications (nghttp, nghttpd, nghttpx and h2load)" ${ENABLE_APP_DEFAULT}) option(ENABLE_HPACK_TOOLS "Build HPACK tools" ${ENABLE_HPACK_TOOLS_DEFAULT}) option(ENABLE_EXAMPLES "Build examples" ${ENABLE_EXAMPLES_DEFAULT}) option(ENABLE_FAILMALLOC "Build failmalloc test program" ON) option(ENABLE_LIB_ONLY "Build libnghttp2 only. This is a short hand for -DENABLE_APP=0 -DENABLE_EXAMPLES=0 -DENABLE_HPACK_TOOLS=0") option(BUILD_SHARED_LIBS "Build libnghttp2 as a shared library" ON) option(BUILD_STATIC_LIBS "Build libnghttp2 in static mode also" OFF) option(ENABLE_STATIC_CRT "Build libnghttp2 against the MS LIBCMT[d]") option(ENABLE_HTTP3 "Enable HTTP/3 support" OFF) option(ENABLE_DOC "Build documentation" ON) cmake_dependent_option(BUILD_TESTING "Enable tests" ON "BUILD_STATIC_LIBS" OFF) option(WITH_LIBXML2 "Use libxml2" ${WITH_LIBXML2_DEFAULT}) option(WITH_JEMALLOC "Use jemalloc" ${WITH_JEMALLOC_DEFAULT}) option(WITH_MRUBY "Use mruby") option(WITH_NEVERBLEED "Use neverbleed") option(WITH_LIBBPF "Use libbpf") option(WITH_WOLFSSL "Use wolfSSL") # vim: ft=cmake: nghttp2-1.68.0/PaxHeaders/doc0000644000000000000000000000013215077107335012752 xustar0030 mtime=1761382109.750298502 30 atime=1761382109.794298375 30 ctime=1761382109.750298502 nghttp2-1.68.0/doc/0000755000175100017510000000000015077107335013417 5ustar00runnerrunnernghttp2-1.68.0/doc/PaxHeaders/nghttp2_submit_priority_update.rst0000644000000000000000000000013215077107332022032 xustar0030 mtime=1761382106.505309101 30 atime=1761382106.771307929 30 ctime=1761382109.717298598 nghttp2-1.68.0/doc/nghttp2_submit_priority_update.rst0000644000175100017510000000265515077107332022432 0ustar00runnerrunner nghttp2_submit_priority_update ============================== Synopsis -------- *#include * .. function:: int nghttp2_submit_priority_update(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *field_value, size_t field_value_len) Submits PRIORITY_UPDATE frame. PRIORITY_UPDATE frame is a non-critical extension to HTTP/2, and defined in :rfc:`9218#section-7.1`. The *flags* is currently ignored and should be :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. The *stream_id* is the ID of stream which is prioritized. The *field_value* points to the Priority field value. The *field_value_len* is the length of the Priority field value. If this function is called by server, :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` is returned. If :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` of value of 0 is received by a remote endpoint (or it is omitted), this function does nothing and returns 0. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` The function is called from server side session :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The *field_value_len* is larger than 16380; or *stream_id* is 0. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_consume_connection.rst0000644000000000000000000000013215077107332022657 xustar0030 mtime=1761382106.502309114 30 atime=1761382106.690308286 30 ctime=1761382109.636298832 nghttp2-1.68.0/doc/nghttp2_session_consume_connection.rst0000644000175100017510000000132215077107332023245 0ustar00runnerrunner nghttp2_session_consume_connection ================================== Synopsis -------- *#include * .. function:: int nghttp2_session_consume_connection(nghttp2_session *session, size_t size) Like `nghttp2_session_consume()`, but this only tells library that *size* bytes were consumed only for connection level. Note that HTTP/2 maintains connection and stream level flow control windows independently. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` Automatic WINDOW_UPDATE is not disabled. nghttp2-1.68.0/doc/PaxHeaders/nghttpx.10000644000000000000000000000013215077107270014603 xustar0030 mtime=1761382072.965444268 30 atime=1761382106.803307788 30 ctime=1761382109.749298505 nghttp2-1.68.0/doc/nghttpx.10000644000175100017510000026123515077107270015204 0ustar00runnerrunner.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "NGHTTPX" "1" "Oct 25, 2025" "1.68.0" "nghttp2" .SH NAME nghttpx \- HTTP/2 proxy .SH SYNOPSIS .sp \fBnghttpx\fP [OPTIONS]... [ ] .SH DESCRIPTION .sp A reverse proxy for HTTP/3, HTTP/2, and HTTP/1. .INDENT 0.0 .TP .B Set path to server\(aqs private key. Required unless \(dqno\-tls\(dq parameter is used in \fI\%\-\-frontend\fP option. .UNINDENT .INDENT 0.0 .TP .B Set path to server\(aqs certificate. Required unless \(dqno\-tls\(dq parameter is used in \fI\%\-\-frontend\fP option. .UNINDENT .SH OPTIONS .sp The options are categorized into several groups. .SS Connections .INDENT 0.0 .TP .B \-b, \-\-backend=(,|unix:)[;[[:...]][[;]...] Set backend host and port. The multiple backend addresses are accepted by repeating this option. UNIX domain socket can be specified by prefixing path name with \(dqunix:\(dq (e.g., unix:/var/run/backend.sock). .sp Optionally, if s are given, the backend address is only used if request matches the pattern. The pattern matching is closely designed to ServeMux in net/http package of Go programming language. consists of path, host + path or just host. The path must start with \(dq\fI/\fP\(dq. If it ends with \(dq\fI/\fP\(dq, it matches all request path in its subtree. To deal with the request to the directory without trailing slash, the path which ends with \(dq\fI/\fP\(dq also matches the request path which only lacks trailing \(aq\fI/\fP\(aq (e.g., path \(dq\fI/foo/\fP\(dq matches request path \(dq\fI/foo\fP\(dq). If it does not end with \(dq\fI/\fP\(dq, it performs exact match against the request path. If host is given, it performs a match against the request host. For a request received on the frontend listener with \(dqsni\-fwd\(dq parameter enabled, SNI host is used instead of a request host. If host alone is given, \(dq\fI/\fP\(dq is appended to it, so that it matches all request paths under the host (e.g., specifying \(dqnghttp2.org\(dq equals to \(dqnghttp2.org/\(dq). CONNECT method is treated specially. It does not have path, and we don\(aqt allow empty path. To workaround this, we assume that CONNECT method has \(dq\fI/\fP\(dq as path. .sp Patterns with host take precedence over patterns with just path. Then, longer patterns take precedence over shorter ones. .sp Host can include \(dq*\(dq in the left most position to indicate wildcard match (only suffix match is done). The \(dq*\(dq must match at least one character. For example, host pattern \(dq*.nghttp2.org\(dq matches against \(dqwww.nghttp2.org\(dq and \(dqgit.ngttp2.org\(dq, but does not match against \(dqnghttp2.org\(dq. The exact hosts match takes precedence over the wildcard hosts match. .sp If path part ends with \(dq*\(dq, it is treated as wildcard path. The wildcard path behaves differently from the normal path. For normal path, match is made around the boundary of path component separator,\(dq\fI/\fP\(dq. On the other hand, the wildcard path does not take into account the path component separator. All paths which include the wildcard path without last \(dq*\(dq as prefix, and are strictly longer than wildcard path without last \(dq*\(dq are matched. \(dq*\(dq must match at least one character. For example, the pattern \(dq\fI/foo*\fP\(dq matches \(dq\fI/foo/\fP\(dq and \(dq\fI/foobar\fP\(dq. But it does not match \(dq\fI/foo\fP\(dq, or \(dq\fI/fo\fP\(dq. .sp If is omitted or empty string, \(dq\fI/\fP\(dq is used as pattern, which matches all request paths (catch\-all pattern). The catch\-all backend must be given. .sp When doing a match, nghttpx made some normalization to pattern, request host and path. For host part, they are converted to lower case. For path part, percent\-encoded unreserved characters defined in RFC 3986 are decoded, and any dot\-segments (\(dq..\(dq and \(dq.\(dq) are resolved and removed. .sp For example, \fI\%\-b\fP\(aq127.0.0.1,8080;nghttp2.org/httpbin/\(aq matches the request host \(dqnghttp2.org\(dq and the request path \(dq\fI/httpbin/get\fP\(dq, but does not match the request host \(dqnghttp2.org\(dq and the request path \(dq\fI/index.html\fP\(dq. .sp The multiple s can be specified, delimiting them by \(dq:\(dq. Specifying \fI\%\-b\fP\(aq127.0.0.1,8080;nghttp2.org:www.nghttp2.org\(aq has the same effect to specify \fI\%\-b\fP\(aq127.0.0.1,8080;nghttp2.org\(aq and \fI\%\-b\fP\(aq127.0.0.1,8080;www.nghttp2.org\(aq. .sp The backend addresses sharing same are grouped together forming load balancing group. .sp Several parameters are accepted after . The parameters are delimited by \(dq;\(dq. The available parameters are: \(dqproto=\(dq, \(dqtls\(dq, \(dqsni=\(dq, \(dqfall=\(dq, \(dqrise=\(dq, \(dqaffinity=\(dq, \(dqdns\(dq, \(dqredirect\-if\-not\-tls\(dq, \(dqupgrade\-scheme\(dq, \(dqmruby=\(dq, \(dqread\-timeout=\(dq, \(dqwrite\-timeout=\(dq, \(dqgroup=\(dq, \(dqgroup\-weight=\(dq, \(dqweight=\(dq, and \(dqdnf\(dq. The parameter consists of keyword, and optionally followed by \(dq=\(dq and value. For example, the parameter \(dqproto=h2\(dq consists of the keyword \(dqproto\(dq and value \(dqh2\(dq. The parameter \(dqtls\(dq consists of the keyword \(dqtls\(dq without value. Each parameter is described as follows. .sp The backend application protocol can be specified using optional \(dqproto\(dq parameter, and in the form of \(dqproto=\(dq. should be one of the following list without quotes: \(dqh2\(dq, \(dqhttp/1.1\(dq. The default value of is \(dqhttp/1.1\(dq. Note that usually \(dqh2\(dq refers to HTTP/2 over TLS. But in this option, it may mean HTTP/2 over cleartext TCP unless \(dqtls\(dq keyword is used (see below). .sp TLS can be enabled by specifying optional \(dqtls\(dq parameter. TLS is not enabled by default. .sp With \(dqsni=\(dq parameter, it can override the TLS SNI field value with given . This will default to the backend name .sp The feature to detect whether backend is online or offline can be enabled using optional \(dqfall\(dq and \(dqrise\(dq parameters. Using \(dqfall=\(dq parameter, if nghttpx cannot connect to a this backend times in a row, this backend is assumed to be offline, and it is excluded from load balancing. If is 0, this backend never be excluded from load balancing whatever times nghttpx cannot connect to it, and this is the default. There is also \(dqrise=\(dq parameter. After backend was excluded from load balancing group, nghttpx periodically attempts to make a connection to the failed backend, and if the connection is made successfully times in a row, the backend is assumed to be online, and it is now eligible for load balancing target. If is 0, a backend is permanently offline, once it goes in that state, and this is the default behaviour. .sp The session affinity is enabled using \(dqaffinity=\(dq parameter. If \(dqip\(dq is given in , client IP based session affinity is enabled. If \(dqcookie\(dq is given in , cookie based session affinity is enabled. If \(dqnone\(dq is given in , session affinity is disabled, and this is the default. The session affinity is enabled per . If at least one backend has \(dqaffinity\(dq parameter, and its is not \(dqnone\(dq, session affinity is enabled for all backend servers sharing the same . It is advised to set \(dqaffinity\(dq parameter to all backend explicitly if session affinity is desired. The session affinity may break if one of the backend gets unreachable, or backend settings are reloaded or replaced by API. .sp If \(dqaffinity=cookie\(dq is used, the additional configuration is required. \(dqaffinity\-cookie\-name=\(dq must be used to specify a name of cookie to use. Optionally, \(dqaffinity\-cookie\-path=\(dq can be used to specify a path which cookie is applied. The optional \(dqaffinity\-cookie\-secure=\(dq controls the Secure attribute of a cookie. The default value is \(dqauto\(dq, and the Secure attribute is determined by a request scheme. If a request scheme is \(dqhttps\(dq, then Secure attribute is set. Otherwise, it is not set. If is \(dqyes\(dq, the Secure attribute is always set. If is \(dqno\(dq, the Secure attribute is always omitted. \(dqaffinity\-cookie\-stickiness=\(dq controls stickiness of this affinity. If is \(dqloose\(dq, removing or adding a backend server might break the affinity and the request might be forwarded to a different backend server. If is \(dqstrict\(dq, removing the designated backend server breaks affinity, but adding new backend server does not cause breakage. If the designated backend server becomes unavailable, new backend server is chosen as if the request does not have an affinity cookie. defaults to \(dqloose\(dq. .sp By default, name resolution of backend host name is done at start up, or reloading configuration. If \(dqdns\(dq parameter is given, name resolution takes place dynamically. This is useful if backend address changes frequently. If \(dqdns\(dq is given, name resolution of backend host name at start up, or reloading configuration is skipped. .sp If \(dqredirect\-if\-not\-tls\(dq parameter is used, the matched backend requires that frontend connection is TLS encrypted. If it isn\(aqt, nghttpx responds to the request with 308 status code, and https URI the client should use instead is included in Location header field. The port number in redirect URI is 443 by default, and can be changed using \fI\%\-\-redirect\-https\-port\fP option. If at least one backend has \(dqredirect\-if\-not\-tls\(dq parameter, this feature is enabled for all backend servers sharing the same . It is advised to set \(dqredirect\-if\-no\-tls\(dq parameter to all backends explicitly if this feature is desired. .sp If \(dqupgrade\-scheme\(dq parameter is used along with \(dqtls\(dq parameter, HTTP/2 :scheme pseudo header field is changed to \(dqhttps\(dq from \(dqhttp\(dq when forwarding a request to this particular backend. This is a workaround for a backend server which requires \(dqhttps\(dq :scheme pseudo header field on TLS encrypted connection. .sp \(dqmruby=\(dq parameter specifies a path to mruby script file which is invoked when this pattern is matched. All backends which share the same pattern must have the same mruby path. .sp \(dqread\-timeout=\(dq and \(dqwrite\-timeout=\(dq parameters specify the read and write timeout of the backend connection when this pattern is matched. All backends which share the same pattern must have the same timeouts. If these timeouts are entirely omitted for a pattern, \fI\%\-\-backend\-read\-timeout\fP and \fI\%\-\-backend\-write\-timeout\fP are used. .sp \(dqgroup=\(dq parameter specifies the name of group this backend address belongs to. By default, it belongs to the unnamed default group. The name of group is unique per pattern. \(dqgroup\-weight=\(dq parameter specifies the weight of the group. The higher weight gets more frequently selected by the load balancing algorithm. must be [1, 256] inclusive. The weight 8 has 4 times more weight than 2. must be the same for all addresses which share the same . If \(dqgroup\-weight\(dq is omitted in an address, but the other address which belongs to the same group specifies \(dqgroup\-weight\(dq, its weight is used. If no \(dqgroup\-weight\(dq is specified for all addresses, the weight of a group becomes 1. \(dqgroup\(dq and \(dqgroup\-weight\(dq are ignored if session affinity is enabled. .sp \(dqweight=\(dq parameter specifies the weight of the backend address inside a group which this address belongs to. The higher weight gets more frequently selected by the load balancing algorithm. must be [1, 256] inclusive. The weight 8 has 4 times more weight than weight 2. If this parameter is omitted, weight becomes 1. \(dqweight\(dq is ignored if session affinity is enabled. .sp If \(dqdnf\(dq parameter is specified, an incoming request is not forwarded to a backend and just consumed along with the request body (actually a backend server never be contacted). It is expected that the HTTP response is generated by mruby script (see \(dqmruby=\(dq parameter above). \(dqdnf\(dq is an abbreviation of \(dqdo not forward\(dq. .sp Since \(dq;\(dq and \(dq:\(dq are used as delimiter, must not contain these characters. In order to include \(dq:\(dq in , one has to specify \(dq%3A\(dq (which is percent\-encoded from of \(dq:\(dq) instead. Since \(dq;\(dq has special meaning in shell, the option value must be quoted. .sp Default: \fB127.0.0.1,80\fP .UNINDENT .INDENT 0.0 .TP .B \-f, \-\-frontend=(,|unix:)[[;]...] Set frontend host and port. If is \(aq*\(aq, it assumes all addresses including both IPv4 and IPv6. UNIX domain socket can be specified by prefixing path name with \(dqunix:\(dq (e.g., unix:/var/run/nghttpx.sock). This option can be used multiple times to listen to multiple addresses. .sp This option can take 0 or more parameters, which are described below. Note that \(dqapi\(dq and \(dqhealthmon\(dq parameters are mutually exclusive. .sp Optionally, TLS can be disabled by specifying \(dqno\-tls\(dq parameter. TLS is enabled by default. .sp If \(dqsni\-fwd\(dq parameter is used, when performing a match to select a backend server, SNI host name received from the client is used instead of the request host. See \fI\%\-\-backend\fP option about the pattern match. .sp To make this frontend as API endpoint, specify \(dqapi\(dq parameter. This is disabled by default. It is important to limit the access to the API frontend. Otherwise, someone may change the backend server, and break your services, or expose confidential information to the outside the world. .sp To make this frontend as health monitor endpoint, specify \(dqhealthmon\(dq parameter. This is disabled by default. Any requests which come through this address are replied with 200 HTTP status, without no body. .sp To accept PROXY protocol version 1 and 2 on frontend connection, specify \(dqproxyproto\(dq parameter. This is disabled by default. .sp To receive HTTP/3 (QUIC) traffic, specify \(dqquic\(dq parameter. It makes nghttpx listen on UDP port rather than TCP port. UNIX domain socket, \(dqapi\(dq, and \(dqhealthmon\(dq parameters cannot be used with \(dqquic\(dq parameter. .sp Default: \fB*,3000\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backlog= Set listen backlog size. .sp Default: \fB65536\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-address\-family=(auto|IPv4|IPv6) Specify address family of backend connections. If \(dqauto\(dq is given, both IPv4 and IPv6 are considered. If \(dqIPv4\(dq is given, only IPv4 address is considered. If \(dqIPv6\(dq is given, only IPv6 address is considered. .sp Default: \fBauto\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-http\-proxy\-uri= Specify proxy URI in the form \X'tty: link http:/'\fI\%http:/\fP\X'tty: link'/[:@]:. If a proxy requires authentication, specify and . Note that they must be properly percent\-encoded. This proxy is used when the backend connection is HTTP/2. First, make a CONNECT request to the proxy and it connects to the backend on behalf of nghttpx. This forms tunnel. After that, nghttpx performs SSL/TLS handshake with the downstream through the tunnel. The timeouts when connecting and making CONNECT request can be specified by \fI\%\-\-backend\-read\-timeout\fP and \fI\%\-\-backend\-write\-timeout\fP options. .UNINDENT .SS Performance .INDENT 0.0 .TP .B \-n, \-\-workers= Set the number of worker threads. .sp Default: \fB1\fP .UNINDENT .INDENT 0.0 .TP .B \-\-single\-thread Run everything in one thread inside the worker process. This feature is provided for better debugging experience, or for the platforms which lack thread support. If threading is disabled, this option is always enabled. .UNINDENT .INDENT 0.0 .TP .B \-\-read\-rate= Set maximum average read rate on frontend connection. Setting 0 to this option means read rate is unlimited. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-read\-burst= Set maximum read burst size on frontend connection. Setting 0 to this option means read burst size is unlimited. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-write\-rate= Set maximum average write rate on frontend connection. Setting 0 to this option means write rate is unlimited. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-write\-burst= Set maximum write burst size on frontend connection. Setting 0 to this option means write burst size is unlimited. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-worker\-read\-rate= Set maximum average read rate on frontend connection per worker. Setting 0 to this option means read rate is unlimited. Not implemented yet. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-worker\-read\-burst= Set maximum read burst size on frontend connection per worker. Setting 0 to this option means read burst size is unlimited. Not implemented yet. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-worker\-write\-rate= Set maximum average write rate on frontend connection per worker. Setting 0 to this option means write rate is unlimited. Not implemented yet. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-worker\-write\-burst= Set maximum write burst size on frontend connection per worker. Setting 0 to this option means write burst size is unlimited. Not implemented yet. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-worker\-frontend\-connections= Set maximum number of simultaneous connections frontend accepts. Setting 0 means unlimited. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-connections\-per\-host= Set maximum number of backend concurrent connections (and/or streams in case of HTTP/2) per origin host. This option is meaningful when \fI\%\-\-http2\-proxy\fP option is used. The origin host is determined by authority portion of request URI (or :authority header field for HTTP/2). To limit the number of connections per frontend for default mode, use \fI\%\-\-backend\-connections\-per\-frontend\fP\&. .sp Default: \fB8\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-connections\-per\-frontend= Set maximum number of backend concurrent connections (and/or streams in case of HTTP/2) per frontend. This option is only used for default mode. 0 means unlimited. To limit the number of connections per host with \fI\%\-\-http2\-proxy\fP option, use \fI\%\-\-backend\-connections\-per\-host\fP\&. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-rlimit\-nofile= Set maximum number of open files (RLIMIT_NOFILE) to . If 0 is given, nghttpx does not set the limit. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-rlimit\-memlock= Set maximum number of bytes of memory that may be locked into RAM. If 0 is given, nghttpx does not set the limit. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-request\-buffer= Set buffer size used to store backend request. .sp Default: \fB16K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-response\-buffer= Set buffer size used to store backend response. .sp Default: \fB128K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-fastopen= Enables \(dqTCP Fast Open\(dq for the listening socket and limits the maximum length for the queue of connections that have not yet completed the three\-way handshake. If value is 0 then fast open is disabled. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-no\-kqueue Don\(aqt use kqueue. This option is only applicable for the platforms which have kqueue. For other platforms, this option will be simply ignored. .UNINDENT .SS Timeout .INDENT 0.0 .TP .B \-\-frontend\-http2\-idle\-timeout= Specify idle timeout for HTTP/2 frontend connection. If no active streams exist for this duration, connection is closed. .sp Default: \fB3m\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http3\-idle\-timeout= Specify idle timeout for HTTP/3 frontend connection. If no active streams exist for this duration, connection is closed. .sp Default: \fB3m\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-write\-timeout= Specify write timeout for all frontend connections. .sp Default: \fB30s\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-keep\-alive\-timeout= Specify keep\-alive timeout for frontend HTTP/1 connection. .sp Default: \fB1m\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-header\-timeout= Specify duration that the server waits for an HTTP request header fields to be received completely. On timeout, HTTP/1 and HTTP/2 connections are closed. For HTTP/3, the stream is shutdown, and the connection itself is left intact. .sp Default: \fB1m\fP .UNINDENT .INDENT 0.0 .TP .B \-\-stream\-read\-timeout= Specify read timeout for HTTP/2 streams. 0 means no timeout. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-stream\-write\-timeout= Specify write timeout for HTTP/2 streams. 0 means no timeout. .sp Default: \fB1m\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-read\-timeout= Specify read timeout for backend connection. .sp Default: \fB1m\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-write\-timeout= Specify write timeout for backend connection. .sp Default: \fB30s\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-connect\-timeout= Specify timeout before establishing TCP connection to backend. .sp Default: \fB30s\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-keep\-alive\-timeout= Specify keep\-alive timeout for backend HTTP/1 connection. .sp Default: \fB2s\fP .UNINDENT .INDENT 0.0 .TP .B \-\-listener\-disable\-timeout= After accepting connection failed, connection listener is disabled for a given amount of time. Specifying 0 disables this feature. .sp Default: \fB30s\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http2\-setting\-timeout= Specify timeout before SETTINGS ACK is received from client. .sp Default: \fB10s\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-http2\-settings\-timeout= Specify timeout before SETTINGS ACK is received from backend server. .sp Default: \fB10s\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-max\-backoff= Specify maximum backoff interval. This is used when doing health check against offline backend (see \(dqfail\(dq parameter in \fI\%\-\-backend\fP option). It is also used to limit the maximum interval to temporarily disable backend when nghttpx failed to connect to it. These intervals are calculated using exponential backoff, and consecutive failed attempts increase the interval. This option caps its maximum value. .sp Default: \fB2m\fP .UNINDENT .SS SSL/TLS .INDENT 0.0 .TP .B \-\-ciphers= Set allowed cipher list for frontend connection. The format of the string is described in OpenSSL ciphers(1). This option sets cipher suites for TLSv1.2. Use \fI\%\-\-tls13\-ciphers\fP for TLSv1.3. .sp Default: \fBECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:DHE\-RSA\-AES128\-GCM\-SHA256:DHE\-RSA\-AES256\-GCM\-SHA384\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls13\-ciphers= Set allowed cipher list for frontend connection. The format of the string is described in OpenSSL ciphers(1). This option sets cipher suites for TLSv1.3. Use \fI\%\-\-ciphers\fP for TLSv1.2. .sp Default: \fBTLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256\fP .UNINDENT .INDENT 0.0 .TP .B \-\-client\-ciphers= Set allowed cipher list for backend connection. The format of the string is described in OpenSSL ciphers(1). This option sets cipher suites for TLSv1.2. Use \fI\%\-\-tls13\-client\-ciphers\fP for TLSv1.3. .sp Default: \fBECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:DHE\-RSA\-AES128\-GCM\-SHA256:DHE\-RSA\-AES256\-GCM\-SHA384\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls13\-client\-ciphers= Set allowed cipher list for backend connection. The format of the string is described in OpenSSL ciphers(1). This option sets cipher suites for TLSv1.3. Use \fI\%\-\-client\-ciphers\fP for TLSv1.2. .sp Default: \fBTLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256\fP .UNINDENT .INDENT 0.0 .TP .B \-\-groups= Set the supported group list for frontend connections. is a colon separated list of group NID or names in the preference order. The supported curves depend on the linked OpenSSL library. This function requires OpenSSL >= 1.0.2. .sp Default: \fBX25519:P\-256:P\-384:P\-521\fP .UNINDENT .INDENT 0.0 .TP .B \-k, \-\-insecure Don\(aqt verify backend server\(aqs certificate if TLS is enabled for backend connections. .UNINDENT .INDENT 0.0 .TP .B \-\-cacert= Set path to trusted CA certificate file. It is used in backend TLS connections to verify peer\(aqs certificate. The file must be in PEM format. It can contain multiple certificates. If the linked OpenSSL is configured to load system wide certificates, they are loaded at startup regardless of this option. .UNINDENT .INDENT 0.0 .TP .B \-\-private\-key\-passwd\-file= Path to file that contains password for the server\(aqs private key. If none is given and the private key is password protected it\(aqll be requested interactively. .UNINDENT .INDENT 0.0 .TP .B \-\-subcert=:[[;]...] Specify additional certificate and private key file. nghttpx will choose certificates based on the hostname indicated by client using TLS SNI extension. If nghttpx is built with OpenSSL >= 1.0.2, the signature algorithms (e.g., ECDSA+SHA256) presented by client are also taken into consideration. This allows nghttpx to send ML\-DSA or ECDSA certificate to modern clients, while sending RSA based certificate to older clients. This option can be used multiple times. .sp Additional parameter can be specified in . The available is \(dqsct\-dir=\(dq. .sp \(dqsct\-dir=\(dq specifies the path to directory which contains *.sct files for TLS signed_certificate_timestamp extension (RFC 6962). This feature requires OpenSSL >= 1.0.2. See also \fI\%\-\-tls\-sct\-dir\fP option. .UNINDENT .INDENT 0.0 .TP .B \-\-dh\-param\-file= Path to file that contains DH parameters in PEM format. Without this option, DHE cipher suites are not available. .UNINDENT .INDENT 0.0 .TP .B \-\-alpn\-list= Comma delimited list of ALPN protocol identifier sorted in the order of preference. That means most desirable protocol comes first. The parameter must be delimited by a single comma only and any white spaces are treated as a part of protocol string. .sp Default: \fBh2,http/1.1\fP .UNINDENT .INDENT 0.0 .TP .B \-\-verify\-client Require and verify client certificate. .UNINDENT .INDENT 0.0 .TP .B \-\-verify\-client\-cacert= Path to file that contains CA certificates to verify client certificate. The file must be in PEM format. It can contain multiple certificates. .UNINDENT .INDENT 0.0 .TP .B \-\-verify\-client\-tolerate\-expired Accept expired client certificate. Operator should handle the expired client certificate by some means (e.g., mruby script). Otherwise, this option might cause a security risk. .UNINDENT .INDENT 0.0 .TP .B \-\-client\-private\-key\-file= Path to file that contains client private key used in backend client authentication. .UNINDENT .INDENT 0.0 .TP .B \-\-client\-cert\-file= Path to file that contains client certificate used in backend client authentication. .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-min\-proto\-version= Specify minimum SSL/TLS protocol. The name matching is done in case\-insensitive manner. The versions between \fI\%\-\-tls\-min\-proto\-version\fP and \fI\%\-\-tls\-max\-proto\-version\fP are enabled. If the protocol list advertised by client does not overlap this range, you will receive the error message \(dqunknown protocol\(dq. The available versions are: TLSv1.3 and TLSv1.2 .sp Default: \fBTLSv1.2\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-max\-proto\-version= Specify maximum SSL/TLS protocol. The name matching is done in case\-insensitive manner. The versions between \fI\%\-\-tls\-min\-proto\-version\fP and \fI\%\-\-tls\-max\-proto\-version\fP are enabled. If the protocol list advertised by client does not overlap this range, you will receive the error message \(dqunknown protocol\(dq. The available versions are: TLSv1.3 and TLSv1.2 .sp Default: \fBTLSv1.3\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-ticket\-key\-file= Path to file that contains random data to construct TLS session ticket parameters. If aes\-128\-cbc is given in \fI\%\-\-tls\-ticket\-key\-cipher\fP, the file must contain exactly 48 bytes. If aes\-256\-cbc is given in \fI\%\-\-tls\-ticket\-key\-cipher\fP, the file must contain exactly 80 bytes. This options can be used repeatedly to specify multiple ticket parameters. If several files are given, only the first key is used to encrypt TLS session tickets. Other keys are accepted but server will issue new session ticket with first key. This allows session key rotation. Please note that key rotation does not occur automatically. User should rearrange files or change options values and restart nghttpx gracefully. If opening or reading given file fails, all loaded keys are discarded and it is treated as if none of this option is given. If this option is not given or an error occurred while opening or reading a file, key is generated every 1 hour internally and they are valid for 12 hours. This is recommended if ticket key sharing between nghttpx instances is not required. .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-ticket\-key\-memcached=,[;tls] Specify address of memcached server to get TLS ticket keys for session resumption. This enables shared TLS ticket key between multiple nghttpx instances. nghttpx does not set TLS ticket key to memcached. The external ticket key generator is required. nghttpx just gets TLS ticket keys from memcached, and use them, possibly replacing current set of keys. It is up to extern TLS ticket key generator to rotate keys frequently. See \(dqTLS SESSION TICKET RESUMPTION\(dq section in manual page to know the data format in memcached entry. Optionally, memcached connection can be encrypted with TLS by specifying \(dqtls\(dq parameter. .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-ticket\-key\-memcached\-address\-family=(auto|IPv4|IPv6) Specify address family of memcached connections to get TLS ticket keys. If \(dqauto\(dq is given, both IPv4 and IPv6 are considered. If \(dqIPv4\(dq is given, only IPv4 address is considered. If \(dqIPv6\(dq is given, only IPv6 address is considered. .sp Default: \fBauto\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-ticket\-key\-memcached\-interval= Set interval to get TLS ticket keys from memcached. .sp Default: \fB10m\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-ticket\-key\-memcached\-max\-retry= Set maximum number of consecutive retries before abandoning TLS ticket key retrieval. If this number is reached, the attempt is considered as failure, and \(dqfailure\(dq count is incremented by 1, which contributed to the value controlled \fI\%\-\-tls\-ticket\-key\-memcached\-max\-fail\fP option. .sp Default: \fB3\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-ticket\-key\-memcached\-max\-fail= Set maximum number of consecutive failure before disabling TLS ticket until next scheduled key retrieval. .sp Default: \fB2\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-ticket\-key\-cipher= Specify cipher to encrypt TLS session ticket. Specify either aes\-128\-cbc or aes\-256\-cbc. By default, aes\-128\-cbc is used. .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-ticket\-key\-memcached\-cert\-file= Path to client certificate for memcached connections to get TLS ticket keys. .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-ticket\-key\-memcached\-private\-key\-file= Path to client private key for memcached connections to get TLS ticket keys. .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-dyn\-rec\-warmup\-threshold= Specify the threshold size for TLS dynamic record size behaviour. During a TLS session, after the threshold number of bytes have been written, the TLS record size will be increased to the maximum allowed (16K). The max record size will continue to be used on the active TLS session. After \fI\%\-\-tls\-dyn\-rec\-idle\-timeout\fP has elapsed, the record size is reduced to 1300 bytes. Specify 0 to always use the maximum record size, regardless of idle period. This behaviour applies to all TLS based frontends, and TLS HTTP/2 backends. .sp Default: \fB1M\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-dyn\-rec\-idle\-timeout= Specify TLS dynamic record size behaviour timeout. See \fI\%\-\-tls\-dyn\-rec\-warmup\-threshold\fP for more information. This behaviour applies to all TLS based frontends, and TLS HTTP/2 backends. .sp Default: \fB1s\fP .UNINDENT .INDENT 0.0 .TP .B \-\-no\-http2\-cipher\-block\-list Allow block listed cipher suite on frontend HTTP/2 connection. See \X'tty: link https://tools.ietf.org/html/rfc7540#appendix-A'\fI\%https://tools.ietf.org/html/rfc7540#appendix\-A\fP\X'tty: link' for the complete HTTP/2 cipher suites block list. .UNINDENT .INDENT 0.0 .TP .B \-\-client\-no\-http2\-cipher\-block\-list Allow block listed cipher suite on backend HTTP/2 connection. See \X'tty: link https://tools.ietf.org/html/rfc7540#appendix-A'\fI\%https://tools.ietf.org/html/rfc7540#appendix\-A\fP\X'tty: link' for the complete HTTP/2 cipher suites block list. .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-sct\-dir= Specifies the directory where *.sct files exist. All *.sct files in are read, and sent as extension_data of TLS signed_certificate_timestamp (RFC 6962) to client. These *.sct files are for the certificate specified in positional command\-line argument , or certificate option in configuration file. For additional certificates, use \fI\%\-\-subcert\fP option. This option requires OpenSSL >= 1.0.2. .UNINDENT .INDENT 0.0 .TP .B \-\-psk\-secrets= Read list of PSK identity and secrets from . This is used for frontend connection. The each line of input file is formatted as :, where is PSK identity, and is secret in hex. An empty line, and line which starts with \(aq#\(aq are skipped. The default enabled cipher list might not contain any PSK cipher suite. In that case, desired PSK cipher suites must be enabled using \fI\%\-\-ciphers\fP option. The desired PSK cipher suite may be block listed by HTTP/2. To use those cipher suites with HTTP/2, consider to use \fI\%\-\-no\-http2\-cipher\-block\-list\fP option. But be aware its implications. .UNINDENT .INDENT 0.0 .TP .B \-\-client\-psk\-secrets= Read PSK identity and secrets from . This is used for backend connection. The each line of input file is formatted as :, where is PSK identity, and is secret in hex. An empty line, and line which starts with \(aq#\(aq are skipped. The first identity and secret pair encountered is used. The default enabled cipher list might not contain any PSK cipher suite. In that case, desired PSK cipher suites must be enabled using \fI\%\-\-client\-ciphers\fP option. The desired PSK cipher suite may be block listed by HTTP/2. To use those cipher suites with HTTP/2, consider to use \fI\%\-\-client\-no\-http2\-cipher\-block\-list\fP option. But be aware its implications. .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-no\-postpone\-early\-data By default, except for QUIC connections, nghttpx postpones forwarding HTTP requests sent in early data, including those sent in partially in it, until TLS handshake finishes. If all backend server recognizes \(dqEarly\-Data\(dq header field, using this option makes nghttpx not postpone forwarding request and get full potential of 0\-RTT data. .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-max\-early\-data= Sets the maximum amount of 0\-RTT data that server accepts. .sp Default: \fB16K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-ktls Enable ktls. .UNINDENT .SS HTTP/2 .INDENT 0.0 .TP .B \-c, \-\-frontend\-http2\-max\-concurrent\-streams= Set the maximum number of the concurrent streams in one frontend HTTP/2 session. .sp Default: \fB100\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-http2\-max\-concurrent\-streams= Set the maximum number of the concurrent streams in one backend HTTP/2 session. This sets maximum number of concurrent opened pushed streams. The maximum number of concurrent requests are set by a remote server. .sp Default: \fB100\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http2\-window\-size= Sets the per\-stream initial window size of HTTP/2 frontend connection. .sp Default: \fB65535\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http2\-connection\-window\-size= Sets the per\-connection window size of HTTP/2 frontend connection. .sp Default: \fB65535\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-http2\-window\-size= Sets the initial window size of HTTP/2 backend connection. .sp Default: \fB65535\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-http2\-connection\-window\-size= Sets the per\-connection window size of HTTP/2 backend connection. .sp Default: \fB2147483647\fP .UNINDENT .INDENT 0.0 .TP .B \-\-http2\-no\-cookie\-crumbling Don\(aqt crumble cookie header field. .UNINDENT .INDENT 0.0 .TP .B \-\-padding= Add at most bytes to a HTTP/2 frame payload as padding. Specify 0 to disable padding. This option is meant for debugging purpose and not intended to enhance protocol security. .UNINDENT .INDENT 0.0 .TP .B \-\-no\-server\-push Disable HTTP/2 server push. Server push is supported by default mode and HTTP/2 frontend via Link header field. It is also supported if both frontend and backend are HTTP/2 in default mode. In this case, server push from backend session is relayed to frontend, and server push via Link header field is also supported. .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http2\-optimize\-write\-buffer\-size (Experimental) Enable write buffer size optimization in frontend HTTP/2 TLS connection. This optimization aims to reduce write buffer size so that it only contains bytes which can send immediately. This makes server more responsive to prioritized HTTP/2 stream because the buffering of lower priority stream is reduced. This option is only effective on recent Linux platform. .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http2\-optimize\-window\-size (Experimental) Automatically tune connection level window size of frontend HTTP/2 TLS connection. If this feature is enabled, connection window size starts with the default window size, 65535 bytes. nghttpx automatically adjusts connection window size based on TCP receiving window size. The maximum window size is capped by the value specified by \fI\%\-\-frontend\-http2\-connection\-window\-size\fP\&. Since the stream is subject to stream level window size, it should be adjusted using \fI\%\-\-frontend\-http2\-window\-size\fP option as well. This option is only effective on recent Linux platform. .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http2\-encoder\-dynamic\-table\-size= Specify the maximum dynamic table size of HPACK encoder in the frontend HTTP/2 connection. The decoder (client) specifies the maximum dynamic table size it accepts. Then the negotiated dynamic table size is the minimum of this option value and the value which client specified. .sp Default: \fB4K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http2\-decoder\-dynamic\-table\-size= Specify the maximum dynamic table size of HPACK decoder in the frontend HTTP/2 connection. .sp Default: \fB4K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-http2\-encoder\-dynamic\-table\-size= Specify the maximum dynamic table size of HPACK encoder in the backend HTTP/2 connection. The decoder (backend) specifies the maximum dynamic table size it accepts. Then the negotiated dynamic table size is the minimum of this option value and the value which backend specified. .sp Default: \fB4K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-http2\-decoder\-dynamic\-table\-size= Specify the maximum dynamic table size of HPACK decoder in the backend HTTP/2 connection. .sp Default: \fB4K\fP .UNINDENT .SS Mode .INDENT 0.0 .TP .B (default mode) Accept HTTP/2, and HTTP/1.1 over SSL/TLS. \(dqno\-tls\(dq parameter is used in \fI\%\-\-frontend\fP option, accept HTTP/2 and HTTP/1.1 over cleartext TCP. The incoming HTTP/1.1 connection can be upgraded to HTTP/2 through HTTP Upgrade. .UNINDENT .INDENT 0.0 .TP .B \-s, \-\-http2\-proxy Like default mode, but enable forward proxy. This is so called HTTP/2 proxy mode. .UNINDENT .SS Logging .INDENT 0.0 .TP .B \-L, \-\-log\-level= Set the severity level of log output. must be one of INFO, NOTICE, WARN, ERROR and FATAL. .sp Default: \fBNOTICE\fP .UNINDENT .INDENT 0.0 .TP .B \-\-accesslog\-file= Set path to write access log. To reopen file, send USR1 signal to nghttpx. .UNINDENT .INDENT 0.0 .TP .B \-\-accesslog\-syslog Send access log to syslog. If this option is used, \fI\%\-\-accesslog\-file\fP option is ignored. .UNINDENT .INDENT 0.0 .TP .B \-\-accesslog\-format= Specify format string for access log. The default format is combined format. The following variables are available: .INDENT 7.0 .IP \(bu 2 $remote_addr: client IP address. .IP \(bu 2 $time_local: local time in Common Log format. .IP \(bu 2 $time_iso8601: local time in ISO 8601 format. .IP \(bu 2 $request: HTTP request line. .IP \(bu 2 $status: HTTP response status code. .IP \(bu 2 $body_bytes_sent: the number of bytes sent to client as response body. .IP \(bu 2 $http_: value of HTTP request header where \(aq_\(aq in is replaced with \(aq\-\(aq. .IP \(bu 2 $remote_port: client port. .IP \(bu 2 $server_port: server port. .IP \(bu 2 $request_time: request processing time in seconds with milliseconds resolution. .IP \(bu 2 $pid: PID of the running process. .IP \(bu 2 $alpn: ALPN identifier of the protocol which generates the response. For HTTP/1, ALPN is always http/1.1, regardless of minor version. .IP \(bu 2 $tls_cipher: cipher used for SSL/TLS connection. .IP \(bu 2 $tls_client_fingerprint_sha256: SHA\-256 fingerprint of client certificate. .IP \(bu 2 $tls_client_fingerprint_sha1: SHA\-1 fingerprint of client certificate. .IP \(bu 2 $tls_client_subject_name: subject name in client certificate. .IP \(bu 2 $tls_client_issuer_name: issuer name in client certificate. .IP \(bu 2 $tls_client_serial: serial number in client certificate. .IP \(bu 2 $tls_protocol: protocol for SSL/TLS connection. .IP \(bu 2 $tls_session_id: session ID for SSL/TLS connection. .IP \(bu 2 $tls_session_reused: \(dqr\(dq if SSL/TLS session was reused. Otherwise, \(dq.\(dq .IP \(bu 2 $tls_sni: SNI server name for SSL/TLS connection. .IP \(bu 2 $backend_host: backend host used to fulfill the request. \(dq\-\(dq if backend host is not available. .IP \(bu 2 $backend_port: backend port used to fulfill the request. \(dq\-\(dq if backend host is not available. .IP \(bu 2 $method: HTTP method .IP \(bu 2 $path: Request path including query. For CONNECT request, authority is recorded. .IP \(bu 2 $path_without_query: $path up to the first \(aq?\(aq character. For CONNECT request, authority is recorded. .IP \(bu 2 $protocol_version: HTTP version (e.g., HTTP/1.1, HTTP/2) .UNINDENT .sp The variable can be enclosed by \(dq{\(dq and \(dq}\(dq for disambiguation (e.g., ${remote_addr}). .sp Default: \fB$remote_addr \- \- [$time_local] \(dq$request\(dq $status $body_bytes_sent \(dq$http_referer\(dq \(dq$http_user_agent\(dq\fP .UNINDENT .INDENT 0.0 .TP .B \-\-accesslog\-write\-early Write access log when response header fields are received from backend rather than when request transaction finishes. .UNINDENT .INDENT 0.0 .TP .B \-\-errorlog\-file= Set path to write error log. To reopen file, send USR1 signal to nghttpx. stderr will be redirected to the error log file unless \fI\%\-\-errorlog\-syslog\fP is used. .sp Default: \fB/dev/stderr\fP .UNINDENT .INDENT 0.0 .TP .B \-\-errorlog\-syslog Send error log to syslog. If this option is used, \fI\%\-\-errorlog\-file\fP option is ignored. .UNINDENT .INDENT 0.0 .TP .B \-\-syslog\-facility= Set syslog facility to . .sp Default: \fBdaemon\fP .UNINDENT .SS HTTP .INDENT 0.0 .TP .B \-\-add\-x\-forwarded\-for Append X\-Forwarded\-For header field to the downstream request. .UNINDENT .INDENT 0.0 .TP .B \-\-strip\-incoming\-x\-forwarded\-for Strip X\-Forwarded\-For header field from inbound client requests. .UNINDENT .INDENT 0.0 .TP .B \-\-no\-add\-x\-forwarded\-proto Don\(aqt append additional X\-Forwarded\-Proto header field to the backend request. If inbound client sets X\-Forwarded\-Proto, and \fI\%\-\-no\-strip\-incoming\-x\-forwarded\-proto\fP option is used, they are passed to the backend. .UNINDENT .INDENT 0.0 .TP .B \-\-no\-strip\-incoming\-x\-forwarded\-proto Don\(aqt strip X\-Forwarded\-Proto header field from inbound client requests. .UNINDENT .INDENT 0.0 .TP .B \-\-add\-forwarded= Append RFC 7239 Forwarded header field with parameters specified in comma delimited list . The supported parameters are \(dqby\(dq, \(dqfor\(dq, \(dqhost\(dq, and \(dqproto\(dq. By default, the value of \(dqby\(dq and \(dqfor\(dq parameters are obfuscated string. See \fI\%\-\-forwarded\-by\fP and \fI\%\-\-forwarded\-for\fP options respectively. Note that nghttpx does not translate non\-standard X\-Forwarded\-* header fields into Forwarded header field, and vice versa. .UNINDENT .INDENT 0.0 .TP .B \-\-strip\-incoming\-forwarded Strip Forwarded header field from inbound client requests. .UNINDENT .INDENT 0.0 .TP .B \-\-forwarded\-by=(obfuscated|ip|) Specify the parameter value sent out with \(dqby\(dq parameter of Forwarded header field. If \(dqobfuscated\(dq is given, the string is randomly generated at startup. If \(dqip\(dq is given, the interface address of the connection, including port number, is sent with \(dqby\(dq parameter. In case of UNIX domain socket, \(dqlocalhost\(dq is used instead of address and port. User can also specify the static obfuscated string. The limitation is that it must start with \(dq_\(dq, and only consists of character set [A\-Za\-z0\-9._\-], as described in RFC 7239. .sp Default: \fBobfuscated\fP .UNINDENT .INDENT 0.0 .TP .B \-\-forwarded\-for=(obfuscated|ip) Specify the parameter value sent out with \(dqfor\(dq parameter of Forwarded header field. If \(dqobfuscated\(dq is given, the string is randomly generated for each client connection. If \(dqip\(dq is given, the remote client address of the connection, without port number, is sent with \(dqfor\(dq parameter. In case of UNIX domain socket, \(dqlocalhost\(dq is used instead of address. .sp Default: \fBobfuscated\fP .UNINDENT .INDENT 0.0 .TP .B \-\-no\-via Don\(aqt append to Via header field. If Via header field is received, it is left unaltered. .UNINDENT .INDENT 0.0 .TP .B \-\-no\-strip\-incoming\-early\-data Don\(aqt strip Early\-Data header field from inbound client requests. .UNINDENT .INDENT 0.0 .TP .B \-\-no\-location\-rewrite Don\(aqt rewrite location header field in default mode. When \fI\%\-\-http2\-proxy\fP is used, location header field will not be altered regardless of this option. .UNINDENT .INDENT 0.0 .TP .B \-\-host\-rewrite Rewrite host and :authority header fields in default mode. When \fI\%\-\-http2\-proxy\fP is used, these headers will not be altered regardless of this option. .UNINDENT .INDENT 0.0 .TP .B \-\-altsvc= Specify protocol ID, port, host and origin of alternative service. , and are optional. Empty and are allowed and they are treated as nothing is specified. They are advertised in alt\-svc header field only in HTTP/1.1 frontend. This option can be used multiple times to specify multiple alternative services. Example: \fI\%\-\-altsvc\fP=\(dqh2,443,,,ma=3600; persist=1\(dq .UNINDENT .INDENT 0.0 .TP .B \-\-http2\-altsvc= Just like \fI\%\-\-altsvc\fP option, but this altsvc is only sent in HTTP/2 frontend. .UNINDENT .INDENT 0.0 .TP .B \-\-add\-request\-header=
Specify additional header field to add to request header set. The field name must be lowercase. This option just appends header field and won\(aqt replace anything already set. This option can be used several times to specify multiple header fields. Example: \fI\%\-\-add\-request\-header\fP=\(dqfoo: bar\(dq .UNINDENT .INDENT 0.0 .TP .B \-\-add\-response\-header=
Specify additional header field to add to response header set. The field name must be lowercase. This option just appends header field and won\(aqt replace anything already set. This option can be used several times to specify multiple header fields. Example: \fI\%\-\-add\-response\-header\fP=\(dqfoo: bar\(dq .UNINDENT .INDENT 0.0 .TP .B \-\-request\-header\-field\-buffer= Set maximum buffer size for incoming HTTP request header field list. This is the sum of header name and value in bytes. If trailer fields exist, they are counted towards this number. .sp Default: \fB64K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-max\-request\-header\-fields= Set maximum number of incoming HTTP request header fields. If trailer fields exist, they are counted towards this number. .sp Default: \fB100\fP .UNINDENT .INDENT 0.0 .TP .B \-\-response\-header\-field\-buffer= Set maximum buffer size for incoming HTTP response header field list. This is the sum of header name and value in bytes. If trailer fields exist, they are counted towards this number. .sp Default: \fB64K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-max\-response\-header\-fields= Set maximum number of incoming HTTP response header fields. If trailer fields exist, they are counted towards this number. .sp Default: \fB500\fP .UNINDENT .INDENT 0.0 .TP .B \-\-error\-page=(|*)= Set file path to custom error page served when nghttpx originally generates HTTP error status code . must be greater than or equal to 400, and at most 599. If \(dq*\(dq is used instead of , it matches all HTTP status code. If error status code comes from backend server, the custom error pages are not used. .UNINDENT .INDENT 0.0 .TP .B \-\-server\-name= Change server response header field value to . .sp Default: \fBnghttpx\fP .UNINDENT .INDENT 0.0 .TP .B \-\-no\-server\-rewrite Don\(aqt rewrite server header field in default mode. When \fI\%\-\-http2\-proxy\fP is used, these headers will not be altered regardless of this option. .UNINDENT .INDENT 0.0 .TP .B \-\-redirect\-https\-port= Specify the port number which appears in Location header field when redirect to HTTPS URI is made due to \(dqredirect\-if\-not\-tls\(dq parameter in \fI\%\-\-backend\fP option. .sp Default: \fB443\fP .UNINDENT .INDENT 0.0 .TP .B \-\-require\-http\-scheme Always require http or https scheme in HTTP request. It also requires that https scheme must be used for an encrypted connection. Otherwise, http scheme must be used. This option is recommended for a server deployment which directly faces clients and the services it provides only require http or https scheme. .UNINDENT .SS API .INDENT 0.0 .TP .B \-\-api\-max\-request\-body= Set the maximum size of request body for API request. .sp Default: \fB32M\fP .UNINDENT .SS DNS .INDENT 0.0 .TP .B \-\-dns\-cache\-timeout= Set duration that cached DNS results remain valid. Note that nghttpx caches the unsuccessful results as well. .sp Default: \fB10s\fP .UNINDENT .INDENT 0.0 .TP .B \-\-dns\-lookup\-timeout= Set timeout that DNS server is given to respond to the initial DNS query. For the 2nd and later queries, server is given time based on this timeout, and it is scaled linearly. .sp Default: \fB250ms\fP .UNINDENT .INDENT 0.0 .TP .B \-\-dns\-max\-try= Set the number of DNS query before nghttpx gives up name lookup. .sp Default: \fB3\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-max\-requests= The number of requests that single frontend connection can process. For HTTP/2, this is the number of streams in one HTTP/2 connection. For HTTP/1, this is the number of keep alive requests. This is hint to nghttpx, and it may allow additional few requests. The default value is unlimited. .UNINDENT .SS Debug .INDENT 0.0 .TP .B \-\-frontend\-http2\-dump\-request\-header= Dumps request headers received by HTTP/2 frontend to the file denoted in . The output is done in HTTP/1 header field format and each header block is followed by an empty line. This option is not thread safe and MUST NOT be used with option \fI\%\-n\fP, where >= 2. .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http2\-dump\-response\-header= Dumps response headers sent from HTTP/2 frontend to the file denoted in . The output is done in HTTP/1 header field format and each header block is followed by an empty line. This option is not thread safe and MUST NOT be used with option \fI\%\-n\fP, where >= 2. .UNINDENT .INDENT 0.0 .TP .B \-o, \-\-frontend\-frame\-debug Print HTTP/2 frames in frontend to stderr. This option is not thread safe and MUST NOT be used with option \fI\%\-n\fP=N, where N >= 2. .UNINDENT .SS Process .INDENT 0.0 .TP .B \-D, \-\-daemon Run in a background. If \fI\%\-D\fP is used, the current working directory is changed to \(aq\fI/\fP\(aq. .UNINDENT .INDENT 0.0 .TP .B \-\-pid\-file= Set path to save PID of this program. .UNINDENT .INDENT 0.0 .TP .B \-\-user= Run this program as . This option is intended to be used to drop root privileges. .UNINDENT .INDENT 0.0 .TP .B \-\-single\-process Run this program in a single process mode for debugging purpose. Without this option, nghttpx creates at least 2 processes: main and worker processes. If this option is used, main and worker are unified into a single process. nghttpx still spawns additional process if neverbleed is used. In the single process mode, the signal handling feature is disabled. .UNINDENT .INDENT 0.0 .TP .B \-\-max\-worker\-processes= The maximum number of worker processes. nghttpx spawns new worker process when it reloads its configuration. The previous worker process enters graceful termination period and will terminate when it finishes handling the existing connections. However, if reloading configurations happen very frequently, the worker processes might be piled up if they take a bit long time to finish the existing connections. With this option, if the number of worker processes exceeds the given value, the oldest worker process is terminated immediately. Specifying 0 means no limit and it is the default behaviour. .UNINDENT .INDENT 0.0 .TP .B \-\-worker\-process\-grace\-shutdown\-period= Maximum period for a worker process to terminate gracefully. When a worker process enters in graceful shutdown period (e.g., when nghttpx reloads its configuration) and it does not finish handling the existing connections in the given period of time, it is immediately terminated. Specifying 0 means no limit and it is the default behaviour. .UNINDENT .SS Scripting .INDENT 0.0 .TP .B \-\-mruby\-file= Set mruby script file .UNINDENT .INDENT 0.0 .TP .B \-\-ignore\-per\-pattern\-mruby\-error Ignore mruby compile error for per\-pattern mruby script file. If error occurred, it is treated as if no mruby file were specified for the pattern. .UNINDENT .SS HTTP/3 and QUIC .INDENT 0.0 .TP .B \-\-frontend\-quic\-idle\-timeout= Specify an idle timeout for QUIC connection. .sp Default: \fB30s\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-quic\-debug\-log Output QUIC debug log to \fI/dev/stderr.\fP .UNINDENT .INDENT 0.0 .TP .B \-\-quic\-bpf\-program\-file= Specify a path to eBPF program file reuseport_kern.o to direct an incoming QUIC UDP datagram to a correct socket. .sp Default: \fB/usr/local/lib/nghttp2/reuseport_kern.o\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-quic\-early\-data Enable early data on frontend QUIC connections. nghttpx sends \(dqEarly\-Data\(dq header field to a backend server if a request is received in early data and handshake has not finished. All backend servers should deal with possibly replayed requests. .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-quic\-qlog\-dir= Specify a directory where a qlog file is written for frontend QUIC connections. A qlog file is created per each QUIC connection. The file name is ISO8601 basic format, followed by \(dq\-\(dq, server Source Connection ID and \(dq.sqlog\(dq. .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-quic\-require\-token Require an address validation token for a frontend QUIC connection. Server sends a token in Retry packet or NEW_TOKEN frame in the previous connection. .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-quic\-congestion\-controller= Specify a congestion controller algorithm for a frontend QUIC connection. should be either \(dqcubic\(dq or \(dqbbr\(dq. .sp Default: \fBcubic\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-quic\-secret\-file= Path to file that contains secure random data to be used as QUIC keying materials. It is used to derive keys for encrypting tokens and Connection IDs. It is not used to encrypt QUIC packets. Each line of this file must contain exactly 136 bytes hex\-encoded string (when decoded the byte string is 68 bytes long). The first 3 bits of decoded byte string are used to identify the keying material. An empty line or a line which starts \(aq#\(aq is ignored. The file can contain more than one keying materials. Because the identifier is 3 bits, at most 8 keying materials are read and the remaining data is discarded. The first keying material in the file is primarily used for encryption and decryption for new connection. The other ones are used to decrypt data for the existing connections. Specifying multiple keying materials enables key rotation. Please note that key rotation does not occur automatically. User should update files or change options values and restart nghttpx gracefully. If opening or reading given file fails, all loaded keying materials are discarded and it is treated as if none of this option is given. If this option is not given or an error occurred while opening or reading a file, a keying material is generated internally on startup and reload. .UNINDENT .INDENT 0.0 .TP .B \-\-quic\-server\-id= Specify server ID encoded in Connection ID to identify this particular server instance. Connection ID is encrypted and this part is not visible in public. It must be 4 bytes long and must be encoded in hex string (which is 8 bytes long). If this option is omitted, a random server ID is generated on startup and configuration reload. .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-quic\-initial\-rtt= Specify the initial RTT of the frontend QUIC connection. .sp Default: \fB333ms\fP .UNINDENT .INDENT 0.0 .TP .B \-\-no\-quic\-bpf Disable eBPF. .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http3\-window\-size= Sets the per\-stream initial window size of HTTP/3 frontend connection. .sp Default: \fB256K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http3\-connection\-window\-size= Sets the per\-connection window size of HTTP/3 frontend connection. .sp Default: \fB1M\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http3\-max\-window\-size= Sets the maximum per\-stream window size of HTTP/3 frontend connection. The window size is adjusted based on the receiving rate of stream data. The initial value is the value specified by \fI\%\-\-frontend\-http3\-window\-size\fP and the window size grows up to bytes. .sp Default: \fB6M\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http3\-max\-connection\-window\-size= Sets the maximum per\-connection window size of HTTP/3 frontend connection. The window size is adjusted based on the receiving rate of stream data. The initial value is the value specified by \fI\%\-\-frontend\-http3\-connection\-window\-size\fP and the window size grows up to bytes. .sp Default: \fB8M\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http3\-max\-concurrent\-streams= Set the maximum number of the concurrent streams in one frontend HTTP/3 connection. .sp Default: \fB100\fP .UNINDENT .SS Misc .INDENT 0.0 .TP .B \-\-conf= Load configuration from . Please note that nghttpx always tries to read the default configuration file if \fI\%\-\-conf\fP is not given. .sp Default: \fB/etc/nghttpx/nghttpx.conf\fP .UNINDENT .INDENT 0.0 .TP .B \-\-include= Load additional configurations from . File is read when configuration parser encountered this option. This option can be used multiple times, or even recursively. .UNINDENT .INDENT 0.0 .TP .B \-v, \-\-version Print version and exit. .UNINDENT .INDENT 0.0 .TP .B \-h, \-\-help Print this help and exit. .UNINDENT .sp The argument is an integer and an optional unit (e.g., 10K is 10 * 1024). Units are K, M and G (powers of 1024). .sp The argument is an integer and an optional unit (e.g., 1s is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms (hours, minutes, seconds and milliseconds, respectively). If a unit is omitted, a second is used as unit. .SH FILES .INDENT 0.0 .TP .B \fI/etc/nghttpx/nghttpx.conf\fP The default configuration file path nghttpx searches at startup. The configuration file path can be changed using \fI\%\-\-conf\fP option. .sp Those lines which are staring \fB#\fP are treated as comment. .sp The option name in the configuration file is the long command\-line option name with leading \fB\-\-\fP stripped (e.g., \fBfrontend\fP). Put \fB=\fP between option name and value. Don\(aqt put extra leading or trailing spaces. .sp When specifying arguments including characters which have special meaning to a shell, we usually use quotes so that shell does not interpret them. When writing this configuration file, quotes for this purpose must not be used. For example, specify additional request header field, do this: .INDENT 7.0 .INDENT 3.5 .sp .EX add\-request\-header=foo: bar .EE .UNINDENT .UNINDENT .sp instead of: .INDENT 7.0 .INDENT 3.5 .sp .EX add\-request\-header=\(dqfoo: bar\(dq .EE .UNINDENT .UNINDENT .sp The options which do not take argument in the command\-line \fItake\fP argument in the configuration file. Specify \fByes\fP as an argument (e.g., \fBhttp2\-proxy=yes\fP). If other string is given, it is ignored. .sp To specify private key and certificate file which are given as positional arguments in command\-line, use \fBprivate\-key\-file\fP and \fBcertificate\-file\fP\&. .sp \fI\%\-\-conf\fP option cannot be used in the configuration file and will be ignored if specified. .TP .B Error log Error log is written to stderr by default. It can be configured using \fI\%\-\-errorlog\-file\fP\&. The format of log message is as follows: .sp (:) .INDENT 7.0 .TP .B It is a combination of date and time when the log is written. It is in ISO 8601 format. .TP .B It is a main process ID. .TP .B It is a process ID which writes this log. .TP .B It is a thread ID which writes this log. It would be unique within . .TP .B and They are source file name, and line number which produce this log. .TP .B It is a log message body. .UNINDENT .UNINDENT .SH SIGNALS .INDENT 0.0 .TP .B SIGQUIT Shutdown gracefully. First accept pending connections and stop accepting connection. After all connections are handled, nghttpx exits. .TP .B SIGHUP Reload configuration file given in \fI\%\-\-conf\fP\&. .TP .B SIGUSR1 Reopen log files. .UNINDENT .sp SIGUSR2 .INDENT 0.0 .INDENT 3.5 Fork and execute nghttpx. It will execute the binary in the same path with same command\-line arguments and environment variables. As of nghttpx version 1.20.0, the new main process sends SIGQUIT to the original main process when it is ready to serve requests. For the earlier versions of nghttpx, user has to send SIGQUIT to the original main process. .sp The difference between SIGUSR2 (+ SIGQUIT) and SIGHUP is that former is usually used to execute new binary, and the main process is newly spawned. On the other hand, the latter just reloads configuration file, and the same main process continues to exist. .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 nghttpx consists of multiple processes: one process for processing these signals, and another one for processing requests. The former spawns the latter. The former is called main process, and the latter is called worker process. If neverbleed is enabled, the worker process spawns neverbleed daemon process which does RSA key processing. The above signal must be sent to the main process. If the other processes received one of them, it is ignored. This behaviour of these processes may change in the future release. In other words, in the future release, the processes other than main process may terminate upon the reception of these signals. Therefore these signals should not be sent to the processes other than main process. .UNINDENT .UNINDENT .SH SERVER PUSH .sp nghttpx supports HTTP/2 server push in default mode with Link header field. nghttpx looks for Link header field (\X'tty: link http://tools.ietf.org/html/rfc5988'\fI\%RFC 5988\fP\X'tty: link') in response headers from backend server and extracts URI\-reference with parameter \fBrel=preload\fP (see \X'tty: link http://w3c.github.io/preload/#interoperability-with-http-link-header'\fI\%preload\fP\X'tty: link') and pushes those URIs to the frontend client. Here is a sample Link header field to initiate server push: .INDENT 0.0 .INDENT 3.5 .sp .EX Link: ; rel=preload Link: ; rel=preload .EE .UNINDENT .UNINDENT .sp Currently, the following restriction is applied for server push: .INDENT 0.0 .IP 1. 3 The associated stream must have method \(dqGET\(dq or \(dqPOST\(dq. The associated stream\(aqs status code must be 200. .UNINDENT .sp This limitation may be loosened in the future release. .sp nghttpx also supports server push if both frontend and backend are HTTP/2 in default mode. In this case, in addition to server push via Link header field, server push from backend is forwarded to frontend HTTP/2 session. .sp HTTP/2 server push will be disabled if \fI\%\-\-http2\-proxy\fP is used. .SH UNIX DOMAIN SOCKET .sp nghttpx supports UNIX domain socket with a filename for both frontend and backend connections. .sp Please note that current nghttpx implementation does not delete a socket with a filename. And on start up, if nghttpx detects that the specified socket already exists in the file system, nghttpx first deletes it. However, if SIGUSR2 is used to execute new binary and both old and new configurations use same filename, new binary does not delete the socket and continues to use it. .SH TLS SESSION RESUMPTION .sp nghttpx supports TLS session resumption through both session ID and session ticket. .SS SESSION ID RESUMPTION .sp By default, session ID is shared by all worker threads. .SS TLS SESSION TICKET RESUMPTION .sp By default, session ticket is shared by all worker threads. The automatic key rotation is also enabled by default. Every an hour, new encryption key is generated, and previous encryption key becomes decryption only key. We set session timeout to 12 hours, and thus we keep at most 12 keys. .sp If \fI\%\-\-tls\-ticket\-key\-memcached\fP is given, encryption keys are retrieved from memcached. nghttpx just reads keys from memcached; one has to deploy key generator program to update keys frequently (e.g., every 1 hour). The example key generator tlsticketupdate.go is available under contrib directory in nghttp2 archive. The memcached entry key is \fBnghttpx:tls\-ticket\-key\fP\&. The data format stored in memcached is the binary format described below: .INDENT 0.0 .INDENT 3.5 .sp .EX +\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ | VERSION (4) |LEN (2)|KEY(48 or 80) ... +\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ ^ | | | +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ (LEN, KEY) pair can be repeated .EE .UNINDENT .UNINDENT .sp All numbers in the above figure is bytes. All integer fields are network byte order. .sp First 4 bytes integer VERSION field, which must be 1. The 2 bytes integer LEN field gives the length of following KEY field, which contains key. If \fI\%\-\-tls\-ticket\-key\-cipher\fP=aes\-128\-cbc is used, LEN must be 48. If \fI\%\-\-tls\-ticket\-key\-cipher\fP=aes\-256\-cbc is used, LEN must be 80. LEN and KEY pair can be repeated multiple times to store multiple keys. The key appeared first is used as encryption key. All the remaining keys are used as decryption only. .sp By default, connections to memcached server are not encrypted. To enable encryption, use \fBtls\fP keyword in \fI\%\-\-tls\-ticket\-key\-memcached\fP option. .sp If \fI\%\-\-tls\-ticket\-key\-file\fP is given, encryption key is read from the given file. In this case, nghttpx does not rotate key automatically. To rotate key, one has to restart nghttpx (see SIGNALS). .SH CERTIFICATE TRANSPARENCY .sp nghttpx supports TLS \fBsigned_certificate_timestamp\fP extension (\X'tty: link https://tools.ietf.org/html/rfc6962'\fI\%RFC 6962\fP\X'tty: link'). The relevant options are \fI\%\-\-tls\-sct\-dir\fP and \fBsct\-dir\fP parameter in \fI\%\-\-subcert\fP\&. They takes a directory, and nghttpx reads all files whose extension is \fB\&.sct\fP under the directory. The \fB*.sct\fP files are encoded as \fBSignedCertificateTimestamp\fP struct described in \X'tty: link https://tools.ietf.org/html/rfc6962#section-3.2'\fI\%section 3.2 of RFC 69662\fP\X'tty: link'\&. This format is the same one used by \X'tty: link https://github.com/grahamedgecombe/nginx-ct'\fI\%nginx\-ct\fP\X'tty: link' and \X'tty: link https://httpd.apache.org/docs/trunk/mod/mod_ssl_ct.html'\fI\%mod_ssl_ct\fP\X'tty: link'\&. \X'tty: link https://github.com/grahamedgecombe/ct-submit'\fI\%ct\-submit\fP\X'tty: link' can be used to submit certificates to log servers, and obtain the \fBSignedCertificateTimestamp\fP struct which can be used with nghttpx. .SH MRUBY SCRIPTING .sp \fBWARNING:\fP .INDENT 0.0 .INDENT 3.5 The current mruby extension API is experimental and not frozen. The API is subject to change in the future release. .UNINDENT .UNINDENT .sp \fBWARNING:\fP .INDENT 0.0 .INDENT 3.5 Almost all string value returned from method, or attribute is a fresh new mruby string, which involves memory allocation, and copies. Therefore, it is strongly recommended to store a return value in a local variable, and use it, instead of calling method or accessing attribute repeatedly. .UNINDENT .UNINDENT .sp nghttpx allows users to extend its capability using mruby scripts. nghttpx has 2 hook points to execute mruby script: request phase and response phase. The request phase hook is invoked after all request header fields are received from client. The response phase hook is invoked after all response header fields are received from backend server. These hooks allows users to modify header fields, or common HTTP variables, like authority or request path, and even return custom response without forwarding request to backend servers. .sp There are 2 levels of mruby script invocations: global and per\-pattern. The global mruby script is set by \fI\%\-\-mruby\-file\fP option and is called for all requests. The per\-pattern mruby script is set by \(dqmruby\(dq parameter in \fI\%\-b\fP option. It is invoked for a request which matches the particular pattern. The order of hook invocation is: global request phase hook, per\-pattern request phase hook, per\-pattern response phase hook, and finally global response phase hook. If a hook returns a response, any later hooks are not invoked. The global request hook is invoked before the pattern matching is made and changing request path may affect the pattern matching. .sp Please note that request and response hooks of per\-pattern mruby script for a single request might not come from the same script. This might happen after a request hook is executed, backend failed for some reason, and at the same time, backend configuration is replaced by API request, and then the request uses new configuration on retry. The response hook from new configuration, if it is specified, will be invoked. .sp The all mruby script will be evaluated once per thread on startup, and it must instantiate object and evaluate it as the return value (e.g., \fBApp.new\fP). This object is called app object. If app object defines \fBon_req\fP method, it is called with \fI\%Nghttpx::Env\fP object on request hook. Similarly, if app object defines \fBon_resp\fP method, it is called with \fI\%Nghttpx::Env\fP object on response hook. For each method invocation, user can can access \fI\%Nghttpx::Request\fP and \fI\%Nghttpx::Response\fP objects via \fI\%Nghttpx::Env#req\fP and \fI\%Nghttpx::Env#resp\fP respectively. .INDENT 0.0 .TP .B Nghttpx::REQUEST_PHASE Constant to represent request phase. .UNINDENT .INDENT 0.0 .TP .B Nghttpx::RESPONSE_PHASE Constant to represent response phase. .UNINDENT .INDENT 0.0 .TP .B class Nghttpx::Env Object to represent current request specific context. .INDENT 7.0 .TP .B attribute [R] req Return \fI\%Request\fP object. .UNINDENT .INDENT 7.0 .TP .B attribute [R] resp Return \fI\%Response\fP object. .UNINDENT .INDENT 7.0 .TP .B attribute [R] ctx Return Ruby hash object. It persists until request finishes. So values set in request phase hook can be retrieved in response phase hook. .UNINDENT .INDENT 7.0 .TP .B attribute [R] phase Return the current phase. .UNINDENT .INDENT 7.0 .TP .B attribute [R] remote_addr Return IP address of a remote client. If connection is made via UNIX domain socket, this returns the string \(dqlocalhost\(dq. .UNINDENT .INDENT 7.0 .TP .B attribute [R] server_addr Return address of server that accepted the connection. This is a string which specified in \fI\%\-\-frontend\fP option, excluding port number, and not a resolved IP address. For UNIX domain socket, this is a path to UNIX domain socket. .UNINDENT .INDENT 7.0 .TP .B attribute [R] server_port Return port number of the server frontend which accepted the connection from client. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_used Return true if TLS is used on the connection. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_sni Return the TLS SNI value which client sent in this connection. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_client_fingerprint_sha256 Return the SHA\-256 fingerprint of a client certificate. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_client_fingerprint_sha1 Return the SHA\-1 fingerprint of a client certificate. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_client_issuer_name Return the issuer name of a client certificate. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_client_subject_name Return the subject name of a client certificate. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_client_serial Return the serial number of a client certificate. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_client_not_before Return the start date of a client certificate in seconds since the epoch. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_client_not_after Return the end date of a client certificate in seconds since the epoch. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_cipher Return a TLS cipher negotiated in this connection. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_protocol Return a TLS protocol version negotiated in this connection. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_session_id Return a session ID for this connection in hex string. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_session_reused Return true if, and only if a SSL/TLS session is reused. .UNINDENT .INDENT 7.0 .TP .B attribute [R] alpn Return ALPN identifier negotiated in this connection. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_handshake_finished Return true if SSL/TLS handshake has finished. If it returns false in the request phase hook, the request is received in TLSv1.3 early data (0\-RTT) and might be vulnerable to the replay attack. nghttpx will send Early\-Data header field to backend servers to indicate this. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class Nghttpx::Request Object to represent request from client. The modification to Request object is allowed only in request phase hook. .INDENT 7.0 .TP .B attribute [R] http_version_major Return HTTP major version. .UNINDENT .INDENT 7.0 .TP .B attribute [R] http_version_minor Return HTTP minor version. .UNINDENT .INDENT 7.0 .TP .B attribute [R/W] method HTTP method. On assignment, copy of given value is assigned. We don\(aqt accept arbitrary method name. We will document them later, but well known methods, like GET, PUT and POST, are all supported. .UNINDENT .INDENT 7.0 .TP .B attribute [R/W] authority Authority (i.e., example.org), including optional port component . On assignment, copy of given value is assigned. .UNINDENT .INDENT 7.0 .TP .B attribute [R/W] scheme Scheme (i.e., http, https). On assignment, copy of given value is assigned. .UNINDENT .INDENT 7.0 .TP .B attribute [R/W] path Request path, including query component (i.e., /index.html). On assignment, copy of given value is assigned. The path does not include authority component of URI. This may include query component. nghttpx makes certain normalization for path. It decodes percent\-encoding for unreserved characters (see \X'tty: link https://tools.ietf.org/html/rfc3986#section-2.3'\fI\%https://tools.ietf.org/html/rfc3986#section\-2.3\fP\X'tty: link'), and resolves \(dq..\(dq and \(dq.\(dq. But it may leave characters which should be percent\-encoded as is. So be careful when comparing path against desired string. .UNINDENT .INDENT 7.0 .TP .B attribute [R] headers Return Ruby hash containing copy of request header fields. Changing values in returned hash does not change request header fields actually used in request processing. Use \fI\%Nghttpx::Request#add_header\fP or \fI\%Nghttpx::Request#set_header\fP to change request header fields. .UNINDENT .INDENT 7.0 .TP .B add_header(key, value) Add header entry associated with key. The value can be single string or array of string. It does not replace any existing values associated with key. .UNINDENT .INDENT 7.0 .TP .B set_header(key, value) Set header entry associated with key. The value can be single string or array of string. It replaces any existing values associated with key. .UNINDENT .INDENT 7.0 .TP .B clear_headers() Clear all existing request header fields. .UNINDENT .INDENT 7.0 .TP .B push(uri) Initiate to push resource identified by \fIuri\fP\&. Only HTTP/2 protocol supports this feature. For the other protocols, this method is noop. \fIuri\fP can be absolute URI, absolute path or relative path to the current request. For absolute or relative path, scheme and authority are inherited from the current request. Currently, method is always GET. nghttpx will issue request to backend servers to fulfill this request. The request and response phase hooks will be called for pushed resource as well. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class Nghttpx::Response Object to represent response from backend server. .INDENT 7.0 .TP .B attribute [R] http_version_major Return HTTP major version. .UNINDENT .INDENT 7.0 .TP .B attribute [R] http_version_minor Return HTTP minor version. .UNINDENT .INDENT 7.0 .TP .B attribute [R/W] status HTTP status code. It must be in the range [200, 999], inclusive. The non\-final status code is not supported in mruby scripting at the moment. .UNINDENT .INDENT 7.0 .TP .B attribute [R] headers Return Ruby hash containing copy of response header fields. Changing values in returned hash does not change response header fields actually used in response processing. Use \fI\%Nghttpx::Response#add_header\fP or \fI\%Nghttpx::Response#set_header\fP to change response header fields. .UNINDENT .INDENT 7.0 .TP .B add_header(key, value) Add header entry associated with key. The value can be single string or array of string. It does not replace any existing values associated with key. .UNINDENT .INDENT 7.0 .TP .B set_header(key, value) Set header entry associated with key. The value can be single string or array of string. It replaces any existing values associated with key. .UNINDENT .INDENT 7.0 .TP .B clear_headers() Clear all existing response header fields. .UNINDENT .INDENT 7.0 .TP .B return(body) Return custom response \fIbody\fP to a client. When this method is called in request phase hook, the request is not forwarded to the backend, and response phase hook for this request will not be invoked. When this method is called in response phase hook, response from backend server is canceled and discarded. The status code and response header fields should be set before using this method. To set status code, use \fI\%Nghttpx::Response#status\fP\&. If status code is not set, 200 is used. To set response header fields, \fI\%Nghttpx::Response#add_header\fP and \fI\%Nghttpx::Response#set_header\fP\&. When this method is invoked in response phase hook, the response headers are filled with the ones received from backend server. To send completely custom header fields, first call \fI\%Nghttpx::Response#clear_headers\fP to erase all existing header fields, and then add required header fields. It is an error to call this method twice for a given request. .UNINDENT .INDENT 7.0 .TP .B send_info(status, headers) Send non\-final (informational) response to a client. \fIstatus\fP must be in the range [100, 199], inclusive. \fIheaders\fP is a hash containing response header fields. Its key must be a string, and the associated value must be either string or array of strings. Since this is not a final response, even if this method is invoked, request is still forwarded to a backend unless \fI\%Nghttpx::Response#return\fP is called. This method can be called multiple times. It cannot be called after \fI\%Nghttpx::Response#return\fP is called. .UNINDENT .UNINDENT .SS MRUBY EXAMPLES .sp Modify request path: .INDENT 0.0 .INDENT 3.5 .sp .EX class App def on_req(env) env.req.path = \(dq/apps#{env.req.path}\(dq end end App.new .EE .UNINDENT .UNINDENT .sp Don\(aqt forget to instantiate and evaluate object at the last line. .sp Restrict permission of viewing a content to a specific client addresses: .INDENT 0.0 .INDENT 3.5 .sp .EX class App def on_req(env) allowed_clients = [\(dq127.0.0.1\(dq, \(dq::1\(dq] if env.req.path.start_with?(\(dq/log/\(dq) && !allowed_clients.include?(env.remote_addr) then env.resp.status = 404 env.resp.return \(dqpermission denied\(dq end end end App.new .EE .UNINDENT .UNINDENT .SH API ENDPOINTS .sp nghttpx exposes API endpoints to manipulate it via HTTP based API. By default, API endpoint is disabled. To enable it, add a dedicated frontend for API using \fI\%\-\-frontend\fP option with \(dqapi\(dq parameter. All requests which come from this frontend address, will be treated as API request. .sp The response is normally JSON dictionary, and at least includes the following keys: .INDENT 0.0 .TP .B status The status of the request processing. The following values are defined: .INDENT 7.0 .TP .B Success The request was successful. .TP .B Failure The request was failed. No change has been made. .UNINDENT .TP .B code HTTP status code .UNINDENT .sp Additionally, depending on the API endpoint, \fBdata\fP key may be present, and its value contains the API endpoint specific data. .sp We wrote \(dqnormally\(dq, since nghttpx may return ordinal HTML response in some cases where the error has occurred before reaching API endpoint (e.g., header field is too large). .sp The following section describes available API endpoints. .SS POST /api/v1beta1/backendconfig .sp This API replaces the current backend server settings with the requested ones. The request method should be POST, but PUT is also acceptable. The request body must be nghttpx configuration file format. For configuration file format, see \fI\%FILES\fP section. The line separator inside the request body must be single LF (0x0A). Currently, only \fI\%backend\fP option is parsed, the others are simply ignored. The semantics of this API is replace the current backend with the backend options in request body. Describe the desired set of backend severs, and nghttpx makes it happen. If there is no \fI\%backend\fP option is found in request body, the current set of backend is replaced with the \fI\%backend\fP option\(aqs default value, which is \fB127.0.0.1,80\fP\&. .sp The replacement is done instantly without breaking existing connections or requests. It also avoids any process creation as is the case with hot swapping with signals. .sp The one limitation is that only numeric IP address is allowed in \fI\%backend\fP in request body unless \(dqdns\(dq parameter is used while non numeric hostname is allowed in command\-line or configuration file is read using \fI\%\-\-conf\fP\&. .SS GET /api/v1beta1/configrevision .sp This API returns configuration revision of the current nghttpx. The configuration revision is opaque string, and it changes after each reloading by SIGHUP. With this API, an external application knows that whether nghttpx has finished reloading its configuration by comparing the configuration revisions between before and after reloading. It is recommended to disable persistent (keep\-alive) connection for this purpose in order to avoid to send a request using the reused connection which may bound to an old process. .sp This API returns response including \fBdata\fP key. Its value is JSON object, and it contains at least the following key: .INDENT 0.0 .TP .B configRevision The configuration revision of the current nghttpx .UNINDENT .SH SEE ALSO .sp \fBnghttp(1)\fP, \fBnghttpd(1)\fP, \fBh2load(1)\fP .SH AUTHOR Tatsuhiro Tsujikawa .SH COPYRIGHT 2012, 2015, 2016, Tatsuhiro Tsujikawa .\" Generated by docutils manpage writer. . nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_change_extpri_stream_priority.rst0000644000000000000000000000013115077107332025122 xustar0030 mtime=1761382106.502309114 29 atime=1761382106.68030833 30 ctime=1761382109.626298861 nghttp2-1.68.0/doc/nghttp2_session_change_extpri_stream_priority.rst0000644000175100017510000000267615077107332025526 0ustar00runnerrunner nghttp2_session_change_extpri_stream_priority ============================================= Synopsis -------- *#include * .. function:: int nghttp2_session_change_extpri_stream_priority( nghttp2_session *session, int32_t stream_id, const nghttp2_extpri *extpri, int ignore_client_signal) Changes the priority of the existing stream denoted by *stream_id*. The new priority is *extpri*. This function is meant to be used by server for :rfc:`9218` extensible prioritization scheme. If *session* is initialized as client, this function returns :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. For client, use `nghttp2_submit_priority_update()` instead. If :member:`extpri->urgency ` is out of bound, it is set to :macro:`NGHTTP2_EXTPRI_URGENCY_LOW`. If *ignore_client_signal* is nonzero, server starts to ignore client priority signals for this stream. If :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` of value of 1 is not submitted via `nghttp2_submit_settings()`, this function does nothing and returns 0. :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` The *session* is initialized as client. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` *stream_id* is zero; or a stream denoted by *stream_id* is not found. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_get_local_window_size.rst0000644000000000000000000000013115077107332023340 xustar0029 mtime=1761382106.50330911 30 atime=1761382106.706308216 30 ctime=1761382109.651298788 nghttp2-1.68.0/doc/nghttp2_session_get_local_window_size.rst0000644000175100017510000000106615077107332023734 0ustar00runnerrunner nghttp2_session_get_local_window_size ===================================== Synopsis -------- *#include * .. function:: int32_t nghttp2_session_get_local_window_size(nghttp2_session *session) Returns the amount of flow-controlled payload (e.g., DATA) that the remote endpoint can send without receiving connection level WINDOW_UPDATE frame. Note that each stream is still subject to the stream level flow control (see `nghttp2_session_get_stream_local_window_size()`). This function returns -1 if it fails. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_client_new.rst0000644000000000000000000000013215077107332021116 xustar0030 mtime=1761382106.502309114 30 atime=1761382106.685308308 30 ctime=1761382109.631298846 nghttp2-1.68.0/doc/nghttp2_session_client_new.rst0000644000175100017510000000175315077107332021514 0ustar00runnerrunner nghttp2_session_client_new ========================== Synopsis -------- *#include * .. function:: int nghttp2_session_client_new(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data) Initializes *\*session_ptr* for client use. The all members of *callbacks* are copied to *\*session_ptr*. Therefore *\*session_ptr* does not store *callbacks*. The *user_data* is an arbitrary user supplied data, which will be passed to the callback functions. The :type:`nghttp2_send_callback2` must be specified. If the application code uses `nghttp2_session_recv()`, the :type:`nghttp2_recv_callback` must be specified. The other members of *callbacks* can be ``NULL``. If this function fails, *\*session_ptr* is left untouched. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_submit_origin.rst0000644000000000000000000000013215077107332020076 xustar0030 mtime=1761382106.505309101 30 atime=1761382106.767307947 30 ctime=1761382109.713298609 nghttp2-1.68.0/doc/nghttp2_submit_origin.rst0000644000175100017510000000224415077107332020470 0ustar00runnerrunner nghttp2_submit_origin ===================== Synopsis -------- *#include * .. function:: int nghttp2_submit_origin(nghttp2_session *session, uint8_t flags, const nghttp2_origin_entry *ov, size_t nov) Submits ORIGIN frame. ORIGIN frame is a non-critical extension to HTTP/2 and defined by `RFC 8336 `_. The *flags* is currently ignored and should be :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. The *ov* points to the array of origins. The *nov* specifies the number of origins included in *ov*. This function creates copies of all elements in *ov*. The ORIGIN frame is only usable by a server. If this function is invoked with client side session, this function returns :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` The function is called from client side session. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` There are too many origins, or an origin is too large to fit into a default frame payload. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_select_padding_callback.rst0000644000000000000000000000013215077107332026422 xustar0030 mtime=1761382106.501309119 30 atime=1761382106.672308365 30 ctime=1761382109.618298884 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_select_padding_callback.rst0000644000175100017510000000122415077107332027011 0ustar00runnerrunner nghttp2_session_callbacks_set_select_padding_callback ===================================================== Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_select_padding_callback( nghttp2_session_callbacks *cbs, nghttp2_select_padding_callback select_padding_callback) .. warning:: Deprecated. Use `nghttp2_session_callbacks_set_select_padding_callback2()` with :type:`nghttp2_select_padding_callback2` instead. Sets callback function invoked when the library asks application how many padding bytes are required for the transmission of the given frame. nghttp2-1.68.0/doc/PaxHeaders/contribute.rst.in0000644000000000000000000000013215077107270016342 xustar0030 mtime=1761382072.964444273 30 atime=1761382104.141319515 30 ctime=1761382109.473299303 nghttp2-1.68.0/doc/contribute.rst.in0000644000175100017510000000006515077107270016733 0ustar00runnerrunner.. include:: @top_srcdir@/doc/sources/contribute.rst nghttp2-1.68.0/doc/PaxHeaders/nghttpx-howto.rst.in0000644000000000000000000000013215077107270017016 xustar0030 mtime=1761382072.965444268 30 atime=1761382104.072319819 30 ctime=1761382109.479299286 nghttp2-1.68.0/doc/nghttpx-howto.rst.in0000644000175100017510000000007015077107270017403 0ustar00runnerrunner.. include:: @top_srcdir@/doc/sources/nghttpx-howto.rst nghttp2-1.68.0/doc/PaxHeaders/nghttp2_submit_window_update.rst0000644000000000000000000000013115077107332021457 xustar0030 mtime=1761382106.506309097 30 atime=1761382106.784307872 29 ctime=1761382109.73029856 nghttp2-1.68.0/doc/nghttp2_submit_window_update.rst0000644000175100017510000000340115077107332022046 0ustar00runnerrunner nghttp2_submit_window_update ============================ Synopsis -------- *#include * .. function:: int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags, int32_t stream_id, int32_t window_size_increment) Submits WINDOW_UPDATE frame. The *flags* is currently ignored and should be :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. The *stream_id* is the stream ID to send this WINDOW_UPDATE. To send connection level WINDOW_UPDATE, specify 0 to *stream_id*. If the *window_size_increment* is positive, the WINDOW_UPDATE with that value as window_size_increment is queued. If the *window_size_increment* is larger than the received bytes from the remote endpoint, the local window size is increased by that difference. If the sole purpose is to increase the local window size, consider to use `nghttp2_session_set_local_window_size()`. If the *window_size_increment* is negative, the local window size is decreased by -*window_size_increment*. If automatic WINDOW_UPDATE is enabled (`nghttp2_option_set_no_auto_window_update()`), and the library decided that the WINDOW_UPDATE should be submitted, then WINDOW_UPDATE is queued with the current received bytes count. If the sole purpose is to decrease the local window size, consider to use `nghttp2_session_set_local_window_size()`. If the *window_size_increment* is 0, the function does nothing and returns 0. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_FLOW_CONTROL` The local window size overflow or gets negative. :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_before_frame_send_callback.rst0000644000000000000000000000013115077107332027101 xustar0030 mtime=1761382106.500309123 30 atime=1761382106.643308493 29 ctime=1761382109.58829897 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_before_frame_send_callback.rst0000644000175100017510000000063515077107332027476 0ustar00runnerrunner nghttp2_session_callbacks_set_before_frame_send_callback ======================================================== Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_before_frame_send_callback( nghttp2_session_callbacks *cbs, nghttp2_before_frame_send_callback before_frame_send_callback) Sets callback function invoked before a non-DATA frame is sent. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_mem_recv.rst0000644000000000000000000000013115077107332020563 xustar0029 mtime=1761382106.50330911 30 atime=1761382106.722308145 30 ctime=1761382109.668298739 nghttp2-1.68.0/doc/nghttp2_session_mem_recv.rst0000644000175100017510000000370715077107332021163 0ustar00runnerrunner nghttp2_session_mem_recv ======================== Synopsis -------- *#include * .. function:: ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, size_t inlen) .. warning:: Deprecated. Use `nghttp2_session_mem_recv2()` instead. Processes data *in* as an input from the remote endpoint. The *inlen* indicates the number of bytes to receive in the *in*. This function behaves like `nghttp2_session_recv()` except that it does not use :type:`nghttp2_recv_callback` to receive data; the *in* is the only data for the invocation of this function. If all bytes are processed, this function returns. The other callbacks are called in the same way as they are in `nghttp2_session_recv()`. In the current implementation, this function always tries to processes *inlen* bytes of input data unless either an error occurs or :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is returned from :type:`nghttp2_on_header_callback` or :type:`nghttp2_on_data_chunk_recv_callback`. If :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is used, the return value includes the number of bytes which was used to produce the data or frame for the callback. This function returns the number of processed bytes, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` The callback function failed. :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC` Invalid client magic was detected. This error only returns when *session* was configured as server and `nghttp2_option_set_no_recv_client_magic()` is not used with nonzero value. :enum:`nghttp2_error.NGHTTP2_ERR_FLOODED` Flooding was detected in this HTTP/2 session, and it must be closed. This is most likely caused by misbehaviour of peer. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_submit_goaway.rst0000644000000000000000000000013215077107332020076 xustar0030 mtime=1761382106.505309101 30 atime=1761382106.765307956 30 ctime=1761382109.710298618 nghttp2-1.68.0/doc/nghttp2_submit_goaway.rst0000644000175100017510000000417315077107332020473 0ustar00runnerrunner nghttp2_submit_goaway ===================== Synopsis -------- *#include * .. function:: int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags, int32_t last_stream_id, uint32_t error_code, const uint8_t *opaque_data, size_t opaque_data_len) Submits GOAWAY frame with the last stream ID *last_stream_id* and the error code *error_code*. The pre-defined error code is one of :enum:`nghttp2_error_code`. The *flags* is currently ignored and should be :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. The *last_stream_id* is peer's stream ID or 0. So if *session* is initialized as client, *last_stream_id* must be even or 0. If *session* is initialized as server, *last_stream_id* must be odd or 0. The HTTP/2 specification says last_stream_id must not be increased from the value previously sent. So the actual value sent as last_stream_id is the minimum value between the given *last_stream_id* and the last_stream_id previously sent to the peer. If the *opaque_data* is not ``NULL`` and *opaque_data_len* is not zero, those data will be sent as additional debug data. The library makes a copy of the memory region pointed by *opaque_data* with the length *opaque_data_len*, so the caller does not need to keep this memory after the return of this function. If the *opaque_data_len* is 0, the *opaque_data* could be ``NULL``. After successful transmission of GOAWAY, following things happen. All incoming streams having strictly more than *last_stream_id* are closed. All incoming HEADERS which starts new stream are simply ignored. After all active streams are handled, both `nghttp2_session_want_read()` and `nghttp2_session_want_write()` return 0 and the application can close session. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The *opaque_data_len* is too large; the *last_stream_id* is invalid. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_stream_get_parent.rst0000644000000000000000000000013215077107332020727 xustar0030 mtime=1761382106.504309105 30 atime=1761382106.752308013 30 ctime=1761382109.698298652 nghttp2-1.68.0/doc/nghttp2_stream_get_parent.rst0000644000175100017510000000063415077107332021322 0ustar00runnerrunner nghttp2_stream_get_parent ========================= Synopsis -------- *#include * .. function:: nghttp2_stream * nghttp2_stream_get_parent(nghttp2_stream *stream) .. warning:: Deprecated. :rfc:`7540` priorities are deprecated by :rfc:`9113`. Consider migrating to :rfc:`9218` extensible prioritization scheme. This function always returns NULL. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_get_root_stream.rst0000644000000000000000000000013115077107332022163 xustar0029 mtime=1761382106.50330911 30 atime=1761382106.712308189 30 ctime=1761382109.658298768 nghttp2-1.68.0/doc/nghttp2_session_get_root_stream.rst0000644000175100017510000000106615077107332022557 0ustar00runnerrunner nghttp2_session_get_root_stream =============================== Synopsis -------- *#include * .. function:: nghttp2_stream * nghttp2_session_get_root_stream(nghttp2_session *session) .. warning:: Deprecated. :rfc:`7540` priorities are deprecated by :rfc:`9113`. Consider migrating to :rfc:`9218` extensible prioritization scheme. Returns root of dependency tree, which is imaginary stream with stream ID 0. The returned pointer is valid until *session* is freed by `nghttp2_session_del()`. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_change_stream_priority.rst0000644000000000000000000000013215077107332023530 xustar0030 mtime=1761382106.502309114 30 atime=1761382106.681308326 30 ctime=1761382109.627298858 nghttp2-1.68.0/doc/nghttp2_session_change_stream_priority.rst0000644000175100017510000000077415077107332024130 0ustar00runnerrunner nghttp2_session_change_stream_priority ====================================== Synopsis -------- *#include * .. function:: int nghttp2_session_change_stream_priority(nghttp2_session *session, int32_t stream_id, const nghttp2_priority_spec *pri_spec) .. warning:: Deprecated. :rfc:`7540` priorities are deprecated by :rfc:`9113`. Consider migrating to :rfc:`9218` extensible prioritization scheme. This function is noop. It always returns 0. nghttp2-1.68.0/doc/PaxHeaders/types.rst0000644000000000000000000000013215077107332014722 xustar0030 mtime=1761382106.507309092 30 atime=1761382106.554308885 30 ctime=1761382109.498299231 nghttp2-1.68.0/doc/types.rst0000644000175100017510000020612715077107332015322 0ustar00runnerrunner Types (structs, unions and typedefs) ==================================== .. type:: ptrdiff_t nghttp2_ssize :type:`nghttp2_ssize` is a signed counterpart of size_t. .. type:: nghttp2_session The primary structure to hold the resources needed for a HTTP/2 session. The details of this structure are intentionally hidden from the public API. .. type:: nghttp2_info This struct is what `nghttp2_version()` returns. It holds information about the particular nghttp2 version. .. member:: int age Age of this struct. This instance of nghttp2 sets it to :macro:`NGHTTP2_VERSION_AGE` but a future version may bump it and add more struct fields at the bottom .. member:: int version_num the :macro:`NGHTTP2_VERSION_NUM` number (since age ==1) .. member:: const char *version_str points to the :macro:`NGHTTP2_VERSION` string (since age ==1) .. member:: const char *proto_str points to the :macro:`NGHTTP2_PROTO_VERSION_ID` string this instance implements (since age ==1) .. type:: nghttp2_vec The object representing single contiguous buffer. .. member:: uint8_t *base The pointer to the buffer. .. member:: size_t len The length of the buffer. .. type:: nghttp2_rcbuf The object representing reference counted buffer. The details of this structure are intentionally hidden from the public API. .. type:: nghttp2_nv The name/value pair, which mainly used to represent header fields. .. member:: uint8_t *name The *name* byte string. If this struct is presented from library (e.g., :type:`nghttp2_on_frame_recv_callback`), *name* is guaranteed to be NULL-terminated. For some callbacks (:type:`nghttp2_before_frame_send_callback`, :type:`nghttp2_on_frame_send_callback`, and :type:`nghttp2_on_frame_not_send_callback`), it may not be NULL-terminated if header field is passed from application with the flag :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`). When application is constructing this struct, *name* is not required to be NULL-terminated. .. member:: uint8_t *value The *value* byte string. If this struct is presented from library (e.g., :type:`nghttp2_on_frame_recv_callback`), *value* is guaranteed to be NULL-terminated. For some callbacks (:type:`nghttp2_before_frame_send_callback`, :type:`nghttp2_on_frame_send_callback`, and :type:`nghttp2_on_frame_not_send_callback`), it may not be NULL-terminated if header field is passed from application with the flag :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE`). When application is constructing this struct, *value* is not required to be NULL-terminated. .. member:: size_t namelen The length of the *name*, excluding terminating NULL. .. member:: size_t valuelen The length of the *value*, excluding terminating NULL. .. member:: uint8_t flags Bitwise OR of one or more of :type:`nghttp2_nv_flag`. .. type:: nghttp2_frame_hd The frame header. .. member:: size_t length The length field of this frame, excluding frame header. .. member:: int32_t stream_id The stream identifier (aka, stream ID) .. member:: uint8_t type The type of this frame. See `nghttp2_frame_type`. .. member:: uint8_t flags The flags. .. member:: uint8_t reserved Reserved bit in frame header. Currently, this is always set to 0 and application should not expect something useful in here. .. type:: nghttp2_data_source This union represents the some kind of data source passed to :type:`nghttp2_data_source_read_callback2`. .. member:: int fd The integer field, suitable for a file descriptor. .. member:: void *ptr The pointer to an arbitrary object. .. type:: ssize_t (*nghttp2_data_source_read_callback)( nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *user_data) .. warning:: Deprecated. Use :type:`nghttp2_data_source_read_callback2` instead. Callback function invoked when the library wants to read data from the *source*. The read data is sent in the stream *stream_id*. The implementation of this function must read at most *length* bytes of data from *source* (or possibly other places) and store them in *buf* and return number of data stored in *buf*. If EOF is reached, set :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag in *\*data_flags*. Sometime it is desirable to avoid copying data into *buf* and let application to send data directly. To achieve this, set :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` to *\*data_flags* (and possibly other flags, just like when we do copy), and return the number of bytes to send without copying data into *buf*. The library, seeing :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY`, will invoke :type:`nghttp2_send_data_callback`. The application must send complete DATA frame in that callback. If this callback is set by `nghttp2_submit_request()`, `nghttp2_submit_response()` or `nghttp2_submit_headers()` and `nghttp2_submit_data()` with flag parameter :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` set, and :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag is set to *\*data_flags*, DATA frame will have END_STREAM flag set. Usually, this is expected behaviour and all are fine. One exception is send trailer fields. You cannot send trailer fields after sending frame with END_STREAM set. To avoid this problem, one can set :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_END_STREAM` along with :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` to signal the library not to set END_STREAM in DATA frame. Then application can use `nghttp2_submit_trailer()` to send trailer fields. `nghttp2_submit_trailer()` can be called inside this callback. If the application wants to postpone DATA frames (e.g., asynchronous I/O, or reading data blocks for long time), it is achieved by returning :enum:`nghttp2_error.NGHTTP2_ERR_DEFERRED` without reading any data in this invocation. The library removes DATA frame from the outgoing queue temporarily. To move back deferred DATA frame to outgoing queue, call `nghttp2_session_resume_data()`. By default, *length* is limited to 16KiB at maximum. If peer allows larger frames, application can enlarge transmission buffer size. See :type:`nghttp2_data_source_read_length_callback` for more details. If the application just wants to return from `nghttp2_session_send()` or `nghttp2_session_mem_send()` without sending anything, return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE`. In case of error, there are 2 choices. Returning :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close the stream by issuing RST_STREAM with :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. If a different error code is desirable, use `nghttp2_submit_rst_stream()` with a desired error code and then return :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Returning :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session failure. .. type:: nghttp2_ssize (*nghttp2_data_source_read_callback2)( nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *user_data) Callback function invoked when the library wants to read data from the *source*. The read data is sent in the stream *stream_id*. The implementation of this function must read at most *length* bytes of data from *source* (or possibly other places) and store them in *buf* and return number of data stored in *buf*. If EOF is reached, set :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag in *\*data_flags*. Sometime it is desirable to avoid copying data into *buf* and let application to send data directly. To achieve this, set :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` to *\*data_flags* (and possibly other flags, just like when we do copy), and return the number of bytes to send without copying data into *buf*. The library, seeing :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY`, will invoke :type:`nghttp2_send_data_callback`. The application must send complete DATA frame in that callback. If this callback is set by `nghttp2_submit_request2()`, `nghttp2_submit_response2()` or `nghttp2_submit_headers()` and `nghttp2_submit_data2()` with flag parameter :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` set, and :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag is set to *\*data_flags*, DATA frame will have END_STREAM flag set. Usually, this is expected behaviour and all are fine. One exception is send trailer fields. You cannot send trailer fields after sending frame with END_STREAM set. To avoid this problem, one can set :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_END_STREAM` along with :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` to signal the library not to set END_STREAM in DATA frame. Then application can use `nghttp2_submit_trailer()` to send trailer fields. `nghttp2_submit_trailer()` can be called inside this callback. If the application wants to postpone DATA frames (e.g., asynchronous I/O, or reading data blocks for long time), it is achieved by returning :enum:`nghttp2_error.NGHTTP2_ERR_DEFERRED` without reading any data in this invocation. The library removes DATA frame from the outgoing queue temporarily. To move back deferred DATA frame to outgoing queue, call `nghttp2_session_resume_data()`. By default, *length* is limited to 16KiB at maximum. If peer allows larger frames, application can enlarge transmission buffer size. See :type:`nghttp2_data_source_read_length_callback` for more details. If the application just wants to return from `nghttp2_session_send()` or `nghttp2_session_mem_send2()` without sending anything, return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE`. In case of error, there are 2 choices. Returning :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close the stream by issuing RST_STREAM with :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. If a different error code is desirable, use `nghttp2_submit_rst_stream()` with a desired error code and then return :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Returning :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session failure. .. type:: nghttp2_data_provider .. warning:: Deprecated. Use :type:`nghttp2_data_provider2` instead. This struct represents the data source and the way to read a chunk of data from it. .. member:: nghttp2_data_source source The data source. .. member:: nghttp2_data_source_read_callback read_callback The callback function to read a chunk of data from the *source*. .. type:: nghttp2_data_provider2 This struct represents the data source and the way to read a chunk of data from it. .. member:: nghttp2_data_source source The data source. .. member:: nghttp2_data_source_read_callback2 read_callback The callback function to read a chunk of data from the *source*. .. type:: nghttp2_data The DATA frame. The received data is delivered via :type:`nghttp2_on_data_chunk_recv_callback`. .. member:: size_t padlen The length of the padding in this frame. This includes PAD_HIGH and PAD_LOW. .. type:: nghttp2_priority_spec .. warning:: Deprecated. :rfc:`7540` priorities are deprecated by :rfc:`9113`. Consider migrating to :rfc:`9218` extensible prioritization scheme. The structure to specify stream dependency. .. member:: int32_t stream_id The stream ID of the stream to depend on. Specifying 0 makes stream not depend any other stream. .. member:: int32_t weight The weight of this dependency. .. member:: uint8_t exclusive nonzero means exclusive dependency .. type:: nghttp2_headers The HEADERS frame. It has the following members: .. member:: nghttp2_frame_hd hd The frame header. .. member:: size_t padlen The length of the padding in this frame. This includes PAD_HIGH and PAD_LOW. .. member:: nghttp2_priority_spec pri_spec .. warning:: Deprecated. :rfc:`7540` priorities are deprecated by :rfc:`9113`. Consider migrating to :rfc:`9218` extensible prioritization scheme. The priority specification .. member:: nghttp2_nv *nva The name/value pairs. .. member:: size_t nvlen The number of name/value pairs in *nva*. .. member:: nghttp2_headers_category cat The category of this HEADERS frame. .. type:: nghttp2_priority .. warning:: Deprecated. :rfc:`7540` priorities are deprecated by :rfc:`9113`. Consider migrating to :rfc:`9218` extensible prioritization scheme. The PRIORITY frame. It has the following members: .. member:: nghttp2_frame_hd hd The frame header. .. member:: nghttp2_priority_spec pri_spec The priority specification. .. type:: nghttp2_rst_stream The RST_STREAM frame. It has the following members: .. member:: nghttp2_frame_hd hd The frame header. .. member:: uint32_t error_code The error code. See :type:`nghttp2_error_code`. .. type:: nghttp2_settings_entry The SETTINGS ID/Value pair. It has the following members: .. member:: int32_t settings_id The SETTINGS ID. See :type:`nghttp2_settings_id`. .. member:: uint32_t value The value of this entry. .. type:: nghttp2_settings The SETTINGS frame. It has the following members: .. member:: nghttp2_frame_hd hd The frame header. .. member:: size_t niv The number of SETTINGS ID/Value pairs in *iv*. .. member:: nghttp2_settings_entry *iv The pointer to the array of SETTINGS ID/Value pair. .. type:: nghttp2_push_promise The PUSH_PROMISE frame. It has the following members: .. member:: nghttp2_frame_hd hd The frame header. .. member:: size_t padlen The length of the padding in this frame. This includes PAD_HIGH and PAD_LOW. .. member:: nghttp2_nv *nva The name/value pairs. .. member:: size_t nvlen The number of name/value pairs in *nva*. .. member:: int32_t promised_stream_id The promised stream ID .. member:: uint8_t reserved Reserved bit. Currently this is always set to 0 and application should not expect something useful in here. .. type:: nghttp2_ping The PING frame. It has the following members: .. member:: nghttp2_frame_hd hd The frame header. .. member:: uint8_t opaque_data[8] The opaque data .. type:: nghttp2_goaway The GOAWAY frame. It has the following members: .. member:: nghttp2_frame_hd hd The frame header. .. member:: int32_t last_stream_id The last stream stream ID. .. member:: uint32_t error_code The error code. See :type:`nghttp2_error_code`. .. member:: uint8_t *opaque_data The additional debug data .. member:: size_t opaque_data_len The length of *opaque_data* member. .. member:: uint8_t reserved Reserved bit. Currently this is always set to 0 and application should not expect something useful in here. .. type:: nghttp2_window_update The WINDOW_UPDATE frame. It has the following members: .. member:: nghttp2_frame_hd hd The frame header. .. member:: int32_t window_size_increment The window size increment. .. member:: uint8_t reserved Reserved bit. Currently this is always set to 0 and application should not expect something useful in here. .. type:: nghttp2_extension The extension frame. It has following members: .. member:: nghttp2_frame_hd hd The frame header. .. member:: void *payload The pointer to extension payload. The exact pointer type is determined by hd.type. Currently, no extension is supported. This is a place holder for the future extensions. .. type:: nghttp2_frame This union includes all frames to pass them to various function calls as nghttp2_frame type. The CONTINUATION frame is omitted from here because the library deals with it internally. .. member:: nghttp2_frame_hd hd The frame header, which is convenient to inspect frame header. .. member:: nghttp2_data data The DATA frame. .. member:: nghttp2_headers headers The HEADERS frame. .. member:: nghttp2_priority priority The PRIORITY frame. .. member:: nghttp2_rst_stream rst_stream The RST_STREAM frame. .. member:: nghttp2_settings settings The SETTINGS frame. .. member:: nghttp2_push_promise push_promise The PUSH_PROMISE frame. .. member:: nghttp2_ping ping The PING frame. .. member:: nghttp2_goaway goaway The GOAWAY frame. .. member:: nghttp2_window_update window_update The WINDOW_UPDATE frame. .. member:: nghttp2_extension ext The extension frame. .. type:: ssize_t (*nghttp2_send_callback)(nghttp2_session *session, const uint8_t *data, size_t length, int flags, void *user_data) .. warning:: Deprecated. Use :type:`nghttp2_send_callback2` instead. Callback function invoked when *session* wants to send data to the remote peer. The implementation of this function must send at most *length* bytes of data stored in *data*. The *flags* is currently not used and always 0. It must return the number of bytes sent if it succeeds. If it cannot send any single byte without blocking, it must return :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. For other errors, it must return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. The *user_data* pointer is the third argument passed in to the call to `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. This callback is required if the application uses `nghttp2_session_send()` to send data to the remote endpoint. If the application uses solely `nghttp2_session_mem_send()` instead, this callback function is unnecessary. To set this callback to :type:`nghttp2_session_callbacks`, use `nghttp2_session_callbacks_set_send_callback()`. .. note:: The *length* may be very small. If that is the case, and application disables Nagle algorithm (``TCP_NODELAY``), then just writing *data* to the network stack leads to very small packet, and it is very inefficient. An application should be responsible to buffer up small chunks of data as necessary to avoid this situation. .. type:: nghttp2_ssize (*nghttp2_send_callback2)(nghttp2_session *session, const uint8_t *data, size_t length, int flags, void *user_data) Callback function invoked when *session* wants to send data to the remote peer. The implementation of this function must send at most *length* bytes of data stored in *data*. The *flags* is currently not used and always 0. It must return the number of bytes sent if it succeeds. If it cannot send any single byte without blocking, it must return :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. For other errors, it must return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. The *user_data* pointer is the third argument passed in to the call to `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. This callback is required if the application uses `nghttp2_session_send()` to send data to the remote endpoint. If the application uses solely `nghttp2_session_mem_send2()` instead, this callback function is unnecessary. To set this callback to :type:`nghttp2_session_callbacks`, use `nghttp2_session_callbacks_set_send_callback2()`. .. note:: The *length* may be very small. If that is the case, and application disables Nagle algorithm (``TCP_NODELAY``), then just writing *data* to the network stack leads to very small packet, and it is very inefficient. An application should be responsible to buffer up small chunks of data as necessary to avoid this situation. .. type:: int (*nghttp2_send_data_callback)(nghttp2_session *session, nghttp2_frame *frame, const uint8_t *framehd, size_t length, nghttp2_data_source *source, void *user_data) Callback function invoked when :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` is used in :type:`nghttp2_data_source_read_callback` to send complete DATA frame. The *frame* is a DATA frame to send. The *framehd* is the serialized frame header (9 bytes). The *length* is the length of application data to send (this does not include padding). The *source* is the same pointer passed to :type:`nghttp2_data_source_read_callback`. The application first must send frame header *framehd* of length 9 bytes. If ``frame->data.padlen > 0``, send 1 byte of value ``frame->data.padlen - 1``. Then send exactly *length* bytes of application data. Finally, if ``frame->data.padlen > 1``, send ``frame->data.padlen - 1`` bytes of zero as padding. The application has to send complete DATA frame in this callback. If all data were written successfully, return 0. If it cannot send any data at all, just return :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`; the library will call this callback with the same parameters later (It is recommended to send complete DATA frame at once in this function to deal with error; if partial frame data has already sent, it is impossible to send another data in that state, and all we can do is tear down connection). When data is fully processed, but application wants to make `nghttp2_session_mem_send2()` or `nghttp2_session_send()` return immediately without processing next frames, return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE`. If application decided to reset this stream, return :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`, then the library will send RST_STREAM with INTERNAL_ERROR as error code. The application can also return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, which will result in connection closure. Returning any other value is treated as :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned. .. type:: ssize_t (*nghttp2_recv_callback)(nghttp2_session *session, uint8_t *buf, size_t length, int flags, void *user_data) .. warning:: Deprecated. Use :type:`nghttp2_recv_callback2` instead. Callback function invoked when *session* wants to receive data from the remote peer. The implementation of this function must read at most *length* bytes of data and store it in *buf*. The *flags* is currently not used and always 0. It must return the number of bytes written in *buf* if it succeeds. If it cannot read any single byte without blocking, it must return :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. If it gets EOF before it reads any single byte, it must return :enum:`nghttp2_error.NGHTTP2_ERR_EOF`. For other errors, it must return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. Returning 0 is treated as :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. The *user_data* pointer is the third argument passed in to the call to `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. This callback is required if the application uses `nghttp2_session_recv()` to receive data from the remote endpoint. If the application uses solely `nghttp2_session_mem_recv()` instead, this callback function is unnecessary. To set this callback to :type:`nghttp2_session_callbacks`, use `nghttp2_session_callbacks_set_recv_callback()`. .. type:: nghttp2_ssize (*nghttp2_recv_callback2)(nghttp2_session *session, uint8_t *buf, size_t length, int flags, void *user_data) Callback function invoked when *session* wants to receive data from the remote peer. The implementation of this function must read at most *length* bytes of data and store it in *buf*. The *flags* is currently not used and always 0. It must return the number of bytes written in *buf* if it succeeds. If it cannot read any single byte without blocking, it must return :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. If it gets EOF before it reads any single byte, it must return :enum:`nghttp2_error.NGHTTP2_ERR_EOF`. For other errors, it must return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. Returning 0 is treated as :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. The *user_data* pointer is the third argument passed in to the call to `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. This callback is required if the application uses `nghttp2_session_recv()` to receive data from the remote endpoint. If the application uses solely `nghttp2_session_mem_recv2()` instead, this callback function is unnecessary. To set this callback to :type:`nghttp2_session_callbacks`, use `nghttp2_session_callbacks_set_recv_callback2()`. .. type:: int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) Callback function invoked by `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` when a frame is received. The *user_data* pointer is the third argument passed in to the call to `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` member of their data structure are always ``NULL`` and 0 respectively. The header name/value pairs are emitted via :type:`nghttp2_on_header_callback`. Only HEADERS and DATA frame can signal the end of incoming data. If ``frame->hd.flags & NGHTTP2_FLAG_END_STREAM`` is nonzero, the *frame* is the last frame from the remote peer in this stream. This callback won't be called for CONTINUATION frames. HEADERS/PUSH_PROMISE + CONTINUATIONs are treated as single frame. The implementation of this function must return 0 if it succeeds. If nonzero value is returned, it is treated as fatal error and `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` functions immediately return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. To set this callback to :type:`nghttp2_session_callbacks`, use `nghttp2_session_callbacks_set_on_frame_recv_callback()`. .. type:: int (*nghttp2_on_invalid_frame_recv_callback)( nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, void *user_data) Callback function invoked by `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` when an invalid non-DATA frame is received. The error is indicated by the *lib_error_code*, which is one of the values defined in :type:`nghttp2_error`. When this callback function is invoked, the library automatically submits either RST_STREAM or GOAWAY frame. The *user_data* pointer is the third argument passed in to the call to `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` member of their data structure are always ``NULL`` and 0 respectively. The implementation of this function must return 0 if it succeeds. If nonzero is returned, it is treated as fatal error and `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` functions immediately return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. To set this callback to :type:`nghttp2_session_callbacks`, use `nghttp2_session_callbacks_set_on_invalid_frame_recv_callback()`. .. type:: int (*nghttp2_on_data_chunk_recv_callback)(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data) Callback function invoked when a chunk of data in DATA frame is received. The *stream_id* is the stream ID this DATA frame belongs to. The *flags* is the flags of DATA frame which this data chunk is contained. ``(flags & NGHTTP2_FLAG_END_STREAM) != 0`` does not necessarily mean this chunk of data is the last one in the stream. You should use :type:`nghttp2_on_frame_recv_callback` to know all data frames are received. The *user_data* pointer is the third argument passed in to the call to `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. If the application uses `nghttp2_session_mem_recv2()`, it can return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv2()` return without processing further input bytes. The memory by pointed by the *data* is retained until `nghttp2_session_mem_recv2()` or `nghttp2_session_recv()` is called. The application must retain the input bytes which was used to produce the *data* parameter, because it may refer to the memory region included in the input bytes. The implementation of this function must return 0 if it succeeds. If nonzero is returned, it is treated as fatal error, and `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` functions immediately return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. To set this callback to :type:`nghttp2_session_callbacks`, use `nghttp2_session_callbacks_set_on_data_chunk_recv_callback()`. .. type:: int (*nghttp2_before_frame_send_callback)(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) Callback function invoked just before the non-DATA frame *frame* is sent. The *user_data* pointer is the third argument passed in to the call to `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. The implementation of this function must return 0 if it succeeds. It can also return :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL` to cancel the transmission of the given frame. If there is a fatal error while executing this callback, the implementation should return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, which makes `nghttp2_session_send()` and `nghttp2_session_mem_send2()` functions immediately return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other value is returned, it is treated as if :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned. But the implementation should not rely on this since the library may define new return value to extend its capability. To set this callback to :type:`nghttp2_session_callbacks`, use `nghttp2_session_callbacks_set_before_frame_send_callback()`. .. type:: int (*nghttp2_on_frame_send_callback)(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) Callback function invoked after the frame *frame* is sent. The *user_data* pointer is the third argument passed in to the call to `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. The implementation of this function must return 0 if it succeeds. If nonzero is returned, it is treated as fatal error and `nghttp2_session_send()` and `nghttp2_session_mem_send2()` functions immediately return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. To set this callback to :type:`nghttp2_session_callbacks`, use `nghttp2_session_callbacks_set_on_frame_send_callback()`. .. type:: int (*nghttp2_on_frame_not_send_callback)(nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, void *user_data) Callback function invoked after the non-DATA frame *frame* is not sent because of the error. The error is indicated by the *lib_error_code*, which is one of the values defined in :type:`nghttp2_error`. The *user_data* pointer is the third argument passed in to the call to `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. The implementation of this function must return 0 if it succeeds. If nonzero is returned, it is treated as fatal error and `nghttp2_session_send()` and `nghttp2_session_mem_send2()` functions immediately return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. `nghttp2_session_get_stream_user_data()` can be used to get associated data. To set this callback to :type:`nghttp2_session_callbacks`, use `nghttp2_session_callbacks_set_on_frame_not_send_callback()`. .. type:: int (*nghttp2_on_stream_close_callback)(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *user_data) Callback function invoked when the stream *stream_id* is closed. The reason of closure is indicated by the *error_code*. The *error_code* is usually one of :enum:`nghttp2_error_code`, but that is not guaranteed. The stream_user_data, which was specified in `nghttp2_submit_request2()` or `nghttp2_submit_headers()`, is still available in this function. The *user_data* pointer is the third argument passed in to the call to `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. This function is also called for a stream in reserved state. The implementation of this function must return 0 if it succeeds. If nonzero is returned, it is treated as fatal error and `nghttp2_session_recv()`, `nghttp2_session_mem_recv2()`, `nghttp2_session_send()`, and `nghttp2_session_mem_send2()` functions immediately return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. To set this callback to :type:`nghttp2_session_callbacks`, use `nghttp2_session_callbacks_set_on_stream_close_callback()`. .. type:: int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) Callback function invoked when the reception of header block in HEADERS or PUSH_PROMISE is started. Each header name/value pair will be emitted by :type:`nghttp2_on_header_callback`. The ``frame->hd.flags`` may not have :enum:`nghttp2_flag.NGHTTP2_FLAG_END_HEADERS` flag set, which indicates that one or more CONTINUATION frames are involved. But the application does not need to care about that because the header name/value pairs are emitted transparently regardless of CONTINUATION frames. The server applications probably create an object to store information about new stream if ``frame->hd.type == NGHTTP2_HEADERS`` and ``frame->headers.cat == NGHTTP2_HCAT_REQUEST``. If *session* is configured as server side, ``frame->headers.cat`` is either ``NGHTTP2_HCAT_REQUEST`` containing request headers or ``NGHTTP2_HCAT_HEADERS`` containing trailer fields and never get PUSH_PROMISE in this callback. For the client applications, ``frame->hd.type`` is either ``NGHTTP2_HEADERS`` or ``NGHTTP2_PUSH_PROMISE``. In case of ``NGHTTP2_HEADERS``, ``frame->headers.cat == NGHTTP2_HCAT_RESPONSE`` means that it is the first response headers, but it may be non-final response which is indicated by 1xx status code. In this case, there may be zero or more HEADERS frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` which has non-final response code and finally client gets exactly one HEADERS frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` containing final response headers (non-1xx status code). The trailer fields also has ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` which does not contain any status code. Returning :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close the stream (promised stream if frame is PUSH_PROMISE) by issuing RST_STREAM with :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. In this case, :type:`nghttp2_on_header_callback` and :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a different error code is desirable, use `nghttp2_submit_rst_stream()` with a desired error code and then return :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use ``frame->push_promise.promised_stream_id`` as stream_id parameter in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE. The implementation of this function must return 0 if it succeeds. It can return :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` to reset the stream (promised stream if frame is PUSH_PROMISE). For critical errors, it must return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other value is returned, it is treated as if :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned. If :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned, `nghttp2_session_mem_recv2()` function will immediately return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. To set this callback to :type:`nghttp2_session_callbacks`, use `nghttp2_session_callbacks_set_on_begin_headers_callback()`. .. type:: int (*nghttp2_on_header_callback)(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *user_data) Callback function invoked when a header name/value pair is received for the *frame*. The *name* of length *namelen* is header name. The *value* of length *valuelen* is header value. The *flags* is bitwise OR of one or more of :type:`nghttp2_nv_flag`. If :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_INDEX` is set in *flags*, the receiver must not index this name/value pair when forwarding it to the next hop. More specifically, "Literal Header Field never Indexed" representation must be used in HPACK encoding. When this callback is invoked, ``frame->hd.type`` is either :enum:`nghttp2_frame_type.NGHTTP2_HEADERS` or :enum:`nghttp2_frame_type.NGHTTP2_PUSH_PROMISE`. After all header name/value pairs are processed with this callback, and no error has been detected, :type:`nghttp2_on_frame_recv_callback` will be invoked. If there is an error in decompression, :type:`nghttp2_on_frame_recv_callback` for the *frame* will not be invoked. Both *name* and *value* are guaranteed to be NULL-terminated. The *namelen* and *valuelen* do not include terminal NULL. If `nghttp2_option_set_no_http_messaging()` is used with nonzero value, NULL character may be included in *name* or *value* before terminating NULL. Please note that unless `nghttp2_option_set_no_http_messaging()` is used, nghttp2 library does perform validation against the *name* and the *value* using `nghttp2_check_header_name()` and `nghttp2_check_header_value()`. In addition to this, nghttp2 performs validation based on HTTP Messaging rule, which is briefly explained in :ref:`http-messaging` section. If the application uses `nghttp2_session_mem_recv2()`, it can return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv2()` return without processing further input bytes. The memory pointed by *frame*, *name* and *value* parameters are retained until `nghttp2_session_mem_recv2()` or `nghttp2_session_recv()` is called. The application must retain the input bytes which was used to produce these parameters, because it may refer to the memory region included in the input bytes. Returning :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close the stream (promised stream if frame is PUSH_PROMISE) by issuing RST_STREAM with :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. In this case, :type:`nghttp2_on_header_callback` and :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a different error code is desirable, use `nghttp2_submit_rst_stream()` with a desired error code and then return :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use ``frame->push_promise.promised_stream_id`` as stream_id parameter in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE. The implementation of this function must return 0 if it succeeds. It may return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` or :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. For other critical failures, it must return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other nonzero value is returned, it is treated as :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned, `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` functions immediately return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. To set this callback to :type:`nghttp2_session_callbacks`, use `nghttp2_session_callbacks_set_on_header_callback()`. .. warning:: Application should properly limit the total buffer size to store incoming header fields. Without it, peer may send large number of header fields or large header fields to cause out of memory in local endpoint. Due to how HPACK works, peer can do this effectively without using much memory on their own. .. type:: int (*nghttp2_on_header_callback2)(nghttp2_session *session, const nghttp2_frame *frame, nghttp2_rcbuf *name, nghttp2_rcbuf *value, uint8_t flags, void *user_data) Callback function invoked when a header name/value pair is received for the *frame*. The *name* is header name. The *value* is header value. The *flags* is bitwise OR of one or more of :type:`nghttp2_nv_flag`. This callback behaves like :type:`nghttp2_on_header_callback`, except that *name* and *value* are stored in reference counted buffer. If application wishes to keep these references without copying them, use `nghttp2_rcbuf_incref()` to increment their reference count. It is the application's responsibility to call `nghttp2_rcbuf_decref()` if they called `nghttp2_rcbuf_incref()` so as not to leak memory. If the *session* is created by `nghttp2_session_server_new3()` or `nghttp2_session_client_new3()`, the function to free memory is the one belongs to the mem parameter. As long as this free function alives, *name* and *value* can live after *session* was destroyed. .. type:: int (*nghttp2_on_invalid_header_callback)( nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *user_data) Callback function invoked when an invalid header name/value pair is received for the *frame*. The parameter and behaviour are similar to :type:`nghttp2_on_header_callback`. The difference is that this callback is only invoked when an invalid header name/value pair is received which is treated as stream error if this callback returns :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` and :type:`nghttp2_on_invalid_header_callback2` is not set. Only invalid regular header field are passed to this callback. In other words, invalid pseudo header field is not passed to this callback. Also header fields which includes upper cased latter are also treated as error without passing them to this callback. This callback is only considered if HTTP messaging validation is turned on (which is on by default, see `nghttp2_option_set_no_http_messaging()`). With this callback, application inspects the incoming invalid field, and it also can reset stream from this callback by returning :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. By default, the error code is :enum:`nghttp2_error_code.NGHTTP2_PROTOCOL_ERROR`. To change the error code, call `nghttp2_submit_rst_stream()` with the error code of choice in addition to returning :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. If 0 is returned, the header field is ignored, and the stream is not reset. .. type:: int (*nghttp2_on_invalid_header_callback2)( nghttp2_session *session, const nghttp2_frame *frame, nghttp2_rcbuf *name, nghttp2_rcbuf *value, uint8_t flags, void *user_data) Callback function invoked when an invalid header name/value pair is received for the *frame*. The parameter and behaviour are similar to :type:`nghttp2_on_header_callback2`. The difference is that this callback is only invoked when an invalid header name/value pair is received which is silently ignored if neither this callback nor :type:`nghttp2_on_invalid_header_callback` is set. Only invalid regular header field are passed to this callback. In other words, invalid pseudo header field is not passed to this callback. Also header fields which includes upper cased latter are also treated as error without passing them to this callback. This callback is only considered if HTTP messaging validation is turned on (which is on by default, see `nghttp2_option_set_no_http_messaging()`). With this callback, application inspects the incoming invalid field, and it also can reset stream from this callback by returning :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. By default, the error code is :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. To change the error code, call `nghttp2_submit_rst_stream()` with the error code of choice in addition to returning :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. .. type:: ssize_t (*nghttp2_select_padding_callback)(nghttp2_session *session, const nghttp2_frame *frame, size_t max_payloadlen, void *user_data) .. warning:: Deprecated. Use :type:`nghttp2_select_padding_callback2` instead. Callback function invoked when the library asks application how many padding bytes are required for the transmission of the *frame*. The application must choose the total length of payload including padded bytes in range [frame->hd.length, max_payloadlen], inclusive. Choosing number not in this range will be treated as :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. Returning ``frame->hd.length`` means no padding is added. Returning :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will make `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions immediately return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. To set this callback to :type:`nghttp2_session_callbacks`, use `nghttp2_session_callbacks_set_select_padding_callback()`. .. type:: nghttp2_ssize (*nghttp2_select_padding_callback2)( nghttp2_session *session, const nghttp2_frame *frame, size_t max_payloadlen, void *user_data) Callback function invoked when the library asks application how many padding bytes are required for the transmission of the *frame*. The application must choose the total length of payload including padded bytes in range [frame->hd.length, max_payloadlen], inclusive. Choosing number not in this range will be treated as :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. Returning ``frame->hd.length`` means no padding is added. Returning :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will make `nghttp2_session_send()` and `nghttp2_session_mem_send2()` functions immediately return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. To set this callback to :type:`nghttp2_session_callbacks`, use `nghttp2_session_callbacks_set_select_padding_callback2()`. .. type:: ssize_t (*nghttp2_data_source_read_length_callback)( nghttp2_session *session, uint8_t frame_type, int32_t stream_id, int32_t session_remote_window_size, int32_t stream_remote_window_size, uint32_t remote_max_frame_size, void *user_data) .. warning:: Deprecated. Use :type:`nghttp2_data_source_read_length_callback2` instead. Callback function invoked when library wants to get max length of data to send data to the remote peer. The implementation of this function should return a value in the following range. [1, min(*session_remote_window_size*, *stream_remote_window_size*, *remote_max_frame_size*)]. If a value greater than this range is returned than the max allow value will be used. Returning a value smaller than this range is treated as :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. The *frame_type* is provided for future extensibility and identifies the type of frame (see :type:`nghttp2_frame_type`) for which to get the length for. Currently supported frame types are: :enum:`nghttp2_frame_type.NGHTTP2_DATA`. This callback can be used to control the length in bytes for which :type:`nghttp2_data_source_read_callback` is allowed to send to the remote endpoint. This callback is optional. Returning :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session failure. To set this callback to :type:`nghttp2_session_callbacks`, use `nghttp2_session_callbacks_set_data_source_read_length_callback()`. .. type:: nghttp2_ssize (*nghttp2_data_source_read_length_callback2)( nghttp2_session *session, uint8_t frame_type, int32_t stream_id, int32_t session_remote_window_size, int32_t stream_remote_window_size, uint32_t remote_max_frame_size, void *user_data) Callback function invoked when library wants to get max length of data to send data to the remote peer. The implementation of this function should return a value in the following range. [1, min(*session_remote_window_size*, *stream_remote_window_size*, *remote_max_frame_size*)]. If a value greater than this range is returned than the max allow value will be used. Returning a value smaller than this range is treated as :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. The *frame_type* is provided for future extensibility and identifies the type of frame (see :type:`nghttp2_frame_type`) for which to get the length for. Currently supported frame types are: :enum:`nghttp2_frame_type.NGHTTP2_DATA`. This callback can be used to control the length in bytes for which :type:`nghttp2_data_source_read_callback` is allowed to send to the remote endpoint. This callback is optional. Returning :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session failure. To set this callback to :type:`nghttp2_session_callbacks`, use `nghttp2_session_callbacks_set_data_source_read_length_callback2()`. .. type:: int (*nghttp2_on_begin_frame_callback)(nghttp2_session *session, const nghttp2_frame_hd *hd, void *user_data) Callback function invoked when a frame header is received. The *hd* points to received frame header. Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will also be called when frame header of CONTINUATION frame is received. If both :type:`nghttp2_on_begin_frame_callback` and :type:`nghttp2_on_begin_headers_callback` are set and HEADERS or PUSH_PROMISE is received, :type:`nghttp2_on_begin_frame_callback` will be called first. The implementation of this function must return 0 if it succeeds. If nonzero value is returned, it is treated as fatal error and `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` functions immediately return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. To set this callback to :type:`nghttp2_session_callbacks`, use `nghttp2_session_callbacks_set_on_begin_frame_callback()`. .. type:: int (*nghttp2_on_extension_chunk_recv_callback)( nghttp2_session *session, const nghttp2_frame_hd *hd, const uint8_t *data, size_t len, void *user_data) Callback function invoked when chunk of extension frame payload is received. The *hd* points to frame header. The received chunk is *data* of length *len*. The implementation of this function must return 0 if it succeeds. To abort processing this extension frame, return :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`. If fatal error occurred, application should return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` functions immediately return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other values are returned, currently they are treated as :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. .. type:: int (*nghttp2_unpack_extension_callback)(nghttp2_session *session, void **payload, const nghttp2_frame_hd *hd, void *user_data) Callback function invoked when library asks the application to unpack extension payload from its wire format. The extension payload has been passed to the application using :type:`nghttp2_on_extension_chunk_recv_callback`. The frame header is already unpacked by the library and provided as *hd*. To receive extension frames, the application must tell desired extension frame type to the library using `nghttp2_option_set_user_recv_extension_type()`. The implementation of this function may store the pointer to the created object as a result of unpacking in *\*payload*, and returns 0. The pointer stored in *\*payload* is opaque to the library, and the library does not own its pointer. *\*payload* is initialized as ``NULL``. The *\*payload* is available as ``frame->ext.payload`` in :type:`nghttp2_on_frame_recv_callback`. Therefore if application can free that memory inside :type:`nghttp2_on_frame_recv_callback` callback. Of course, application has a liberty not to use *\*payload*, and do its own mechanism to process extension frames. To abort processing this extension frame, return :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`. If fatal error occurred, application should return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` functions immediately return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other values are returned, currently they are treated as :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. .. type:: ssize_t (*nghttp2_pack_extension_callback)(nghttp2_session *session, uint8_t *buf, size_t len, const nghttp2_frame *frame, void *user_data) .. warning:: Deprecated. Use :type:`nghttp2_pack_extension_callback2` instead. Callback function invoked when library asks the application to pack extension payload in its wire format. The frame header will be packed by library. Application must pack payload only. ``frame->ext.payload`` is the object passed to `nghttp2_submit_extension()` as payload parameter. Application must pack extension payload to the *buf* of its capacity *len* bytes. The *len* is at least 16KiB. The implementation of this function should return the number of bytes written into *buf* when it succeeds. To abort processing this extension frame, return :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`, and :type:`nghttp2_on_frame_not_send_callback` will be invoked. If fatal error occurred, application should return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions immediately return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other values are returned, currently they are treated as :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the return value is strictly larger than *len*, it is treated as :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. .. type:: nghttp2_ssize (*nghttp2_pack_extension_callback2)( nghttp2_session *session, uint8_t *buf, size_t len, const nghttp2_frame *frame, void *user_data) Callback function invoked when library asks the application to pack extension payload in its wire format. The frame header will be packed by library. Application must pack payload only. ``frame->ext.payload`` is the object passed to `nghttp2_submit_extension()` as payload parameter. Application must pack extension payload to the *buf* of its capacity *len* bytes. The *len* is at least 16KiB. The implementation of this function should return the number of bytes written into *buf* when it succeeds. To abort processing this extension frame, return :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`, and :type:`nghttp2_on_frame_not_send_callback` will be invoked. If fatal error occurred, application should return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, `nghttp2_session_send()` and `nghttp2_session_mem_send2()` functions immediately return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other values are returned, currently they are treated as :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the return value is strictly larger than *len*, it is treated as :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. .. type:: int (*nghttp2_error_callback)(nghttp2_session *session, const char *msg, size_t len, void *user_data) .. warning:: Deprecated. Use :type:`nghttp2_error_callback2` instead. Callback function invoked when library provides the error message intended for human consumption. This callback is solely for debugging purpose. The *msg* is typically NULL-terminated string of length *len*. *len* does not include the sentinel NULL character. The format of error message may change between nghttp2 library versions. The application should not depend on the particular format. Normally, application should return 0 from this callback. If fatal error occurred while doing something in this callback, application should return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, library will return immediately with return value :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. Currently, if nonzero value is returned from this callback, they are treated as :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, but application should not rely on this details. .. type:: int (*nghttp2_error_callback2)(nghttp2_session *session, int lib_error_code, const char *msg, size_t len, void *user_data) Callback function invoked when library provides the error code, and message. This callback is solely for debugging purpose. *lib_error_code* is one of error code defined in :enum:`nghttp2_error`. The *msg* is typically NULL-terminated string of length *len*, and intended for human consumption. *len* does not include the sentinel NULL character. The format of error message may change between nghttp2 library versions. The application should not depend on the particular format. Normally, application should return 0 from this callback. If fatal error occurred while doing something in this callback, application should return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, library will return immediately with return value :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. Currently, if nonzero value is returned from this callback, they are treated as :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, but application should not rely on this details. .. type:: void (*nghttp2_rand_callback)(uint8_t *dest, size_t destlen) Callback function invoked when unpredictable data of *destlen* bytes are needed. The implementation must write unpredictable data of *destlen* bytes into the buffer pointed by *dest*. .. type:: nghttp2_session_callbacks Callback functions for :type:`nghttp2_session`. The details of this structure are intentionally hidden from the public API. .. type:: void *(*nghttp2_malloc)(size_t size, void *mem_user_data) Custom memory allocator to replace malloc(). The *mem_user_data* is the mem_user_data member of :type:`nghttp2_mem` structure. .. type:: void (*nghttp2_free)(void *ptr, void *mem_user_data) Custom memory allocator to replace free(). The *mem_user_data* is the mem_user_data member of :type:`nghttp2_mem` structure. .. type:: void *(*nghttp2_calloc)(size_t nmemb, size_t size, void *mem_user_data) Custom memory allocator to replace calloc(). The *mem_user_data* is the mem_user_data member of :type:`nghttp2_mem` structure. .. type:: void *(*nghttp2_realloc)(void *ptr, size_t size, void *mem_user_data) Custom memory allocator to replace realloc(). The *mem_user_data* is the mem_user_data member of :type:`nghttp2_mem` structure. .. type:: nghttp2_mem Custom memory allocator functions and user defined pointer. The *mem_user_data* member is passed to each allocator function. This can be used, for example, to achieve per-session memory pool. In the following example code, ``my_malloc``, ``my_free``, ``my_calloc`` and ``my_realloc`` are the replacement of the standard allocators ``malloc``, ``free``, ``calloc`` and ``realloc`` respectively:: void *my_malloc_cb(size_t size, void *mem_user_data) { return my_malloc(size); } void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { return my_calloc(nmemb, size); } void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { return my_realloc(ptr, size); } void session_new() { nghttp2_session *session; nghttp2_session_callbacks *callbacks; nghttp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb, my_realloc_cb}; ... nghttp2_session_client_new3(&session, callbacks, NULL, NULL, &mem); ... } .. member:: void *mem_user_data An arbitrary user supplied data. This is passed to each allocator function. .. member:: nghttp2_malloc malloc Custom allocator function to replace malloc(). .. member:: nghttp2_free free Custom allocator function to replace free(). .. member:: nghttp2_calloc calloc Custom allocator function to replace calloc(). .. member:: nghttp2_realloc realloc Custom allocator function to replace realloc(). .. type:: nghttp2_option Configuration options for :type:`nghttp2_session`. The details of this structure are intentionally hidden from the public API. .. type:: nghttp2_extpri :type:`nghttp2_extpri` is :rfc:`9218` extensible priorities specification for a stream. .. member:: uint32_t urgency :member:`urgency` is the urgency of a stream, it must be in [:macro:`NGHTTP2_EXTPRI_URGENCY_HIGH`, :macro:`NGHTTP2_EXTPRI_URGENCY_LOW`], inclusive, and 0 is the highest urgency. .. member:: int inc :member:`inc` indicates that a content can be processed incrementally or not. If inc is 0, it cannot be processed incrementally. If inc is 1, it can be processed incrementally. Other value is not permitted. .. type:: nghttp2_ext_altsvc The payload of ALTSVC frame. ALTSVC frame is a non-critical extension to HTTP/2. If this frame is received, and `nghttp2_option_set_user_recv_extension_type()` is not set, and `nghttp2_option_set_builtin_recv_extension_type()` is set for :enum:`nghttp2_frame_type.NGHTTP2_ALTSVC`, ``nghttp2_extension.payload`` will point to this struct. It has the following members: .. member:: uint8_t *origin The pointer to origin which this alternative service is associated with. This is not necessarily NULL-terminated. .. member:: size_t origin_len The length of the *origin*. .. member:: uint8_t *field_value The pointer to Alt-Svc field value contained in ALTSVC frame. This is not necessarily NULL-terminated. .. member:: size_t field_value_len The length of the *field_value*. .. type:: nghttp2_origin_entry The single entry of an origin. .. member:: uint8_t *origin The pointer to origin. No validation is made against this field by the library. This is not necessarily NULL-terminated. .. member:: size_t origin_len The length of the *origin*. .. type:: nghttp2_ext_origin The payload of ORIGIN frame. ORIGIN frame is a non-critical extension to HTTP/2 and defined by `RFC 8336 `_. If this frame is received, and `nghttp2_option_set_user_recv_extension_type()` is not set, and `nghttp2_option_set_builtin_recv_extension_type()` is set for :enum:`nghttp2_frame_type.NGHTTP2_ORIGIN`, ``nghttp2_extension.payload`` will point to this struct. It has the following members: .. member:: size_t nov The number of origins contained in *ov*. .. member:: nghttp2_origin_entry *ov The pointer to the array of origins contained in ORIGIN frame. .. type:: nghttp2_ext_priority_update The payload of PRIORITY_UPDATE frame. PRIORITY_UPDATE frame is a non-critical extension to HTTP/2. If this frame is received, and `nghttp2_option_set_user_recv_extension_type()` is not set, and `nghttp2_option_set_builtin_recv_extension_type()` is set for :enum:`nghttp2_frame_type.NGHTTP2_PRIORITY_UPDATE`, ``nghttp2_extension.payload`` will point to this struct. It has the following members: .. member:: int32_t stream_id The stream ID of the stream whose priority is updated. .. member:: uint8_t *field_value The pointer to Priority field value. It is not necessarily NULL-terminated. .. member:: size_t field_value_len The length of the :member:`field_value`. .. type:: nghttp2_hd_deflater HPACK deflater object. .. type:: nghttp2_hd_inflater HPACK inflater object. .. type:: nghttp2_stream The structure to represent HTTP/2 stream. The details of this structure are intentionally hidden from the public API. .. type:: void (*nghttp2_debug_vprintf_callback)(const char *format, va_list args) Callback function invoked when the library outputs debug logging. The function is called with arguments suitable for ``vfprintf(3)`` The debug output is only enabled if the library is built with ``DEBUGBUILD`` macro defined. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_priority_spec_check_default.rst0000644000000000000000000000013215077107332022760 xustar0030 mtime=1761382106.500309123 30 atime=1761382106.629308555 30 ctime=1761382109.573299014 nghttp2-1.68.0/doc/nghttp2_priority_spec_check_default.rst0000644000175100017510000000073215077107332023352 0ustar00runnerrunner nghttp2_priority_spec_check_default =================================== Synopsis -------- *#include * .. function:: int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec) .. warning:: Deprecated. :rfc:`7540` priorities are deprecated by :rfc:`9113`. Consider migrating to :rfc:`9218` extensible prioritization scheme. Returns nonzero if the *pri_spec* is filled with default values. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_submit_data.rst0000644000000000000000000000013215077107332017520 xustar0030 mtime=1761382106.505309101 30 atime=1761382106.761307973 30 ctime=1761382109.707298626 nghttp2-1.68.0/doc/nghttp2_submit_data.rst0000644000175100017510000000404315077107332020111 0ustar00runnerrunner nghttp2_submit_data =================== Synopsis -------- *#include * .. function:: int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_data_provider *data_prd) .. warning:: Deprecated. Use `nghttp2_submit_data2()` instead. Submits one or more DATA frames to the stream *stream_id*. The data to be sent are provided by *data_prd*. If *flags* contains :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM`, the last DATA frame has END_STREAM flag set. This function does not take ownership of the *data_prd*. The function copies the members of the *data_prd*. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` DATA or HEADERS has been already submitted and not fully processed yet. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The *stream_id* is 0. :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_CLOSED` The stream was already closed; or the *stream_id* is invalid. .. note:: Currently, only one DATA or HEADERS is allowed for a stream at a time. Submitting these frames more than once before first DATA or HEADERS is finished results in :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` error code. The earliest callback which tells that previous frame is done is :type:`nghttp2_on_frame_send_callback`. In side that callback, new data can be submitted using `nghttp2_submit_data()`. Of course, all data except for last one must not have :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` flag set in *flags*. This sounds a bit complicated, and we recommend to use `nghttp2_submit_request()` and `nghttp2_submit_response()` to avoid this cascading issue. The experience shows that for HTTP use, these two functions are enough to implement both client and server. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_get_stream_local_window_size.rst0000644000000000000000000000013115077107332024713 xustar0029 mtime=1761382106.50330911 30 atime=1761382106.717308167 30 ctime=1761382109.663298754 nghttp2-1.68.0/doc/nghttp2_session_get_stream_local_window_size.rst0000644000175100017510000000124315077107332025304 0ustar00runnerrunner nghttp2_session_get_stream_local_window_size ============================================ Synopsis -------- *#include * .. function:: int32_t nghttp2_session_get_stream_local_window_size( nghttp2_session *session, int32_t stream_id) Returns the amount of flow-controlled payload (e.g., DATA) that the remote endpoint can send without receiving stream level WINDOW_UPDATE frame. It is also subject to the connection level flow control. So the actual amount of data to send is min(`nghttp2_session_get_stream_local_window_size()`, `nghttp2_session_get_local_window_size()`). This function returns -1 if it fails. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_set_debug_vprintf_callback.rst0000644000000000000000000000013115077107332022550 xustar0030 mtime=1761382106.504309105 29 atime=1761382106.74830803 30 ctime=1761382109.694298664 nghttp2-1.68.0/doc/nghttp2_set_debug_vprintf_callback.rst0000644000175100017510000000170315077107332023142 0ustar00runnerrunner nghttp2_set_debug_vprintf_callback ================================== Synopsis -------- *#include * .. function:: void nghttp2_set_debug_vprintf_callback( nghttp2_debug_vprintf_callback debug_vprintf_callback) Sets a debug output callback called by the library when built with ``DEBUGBUILD`` macro defined. If this option is not used, debug log is written into standard error output. For builds without ``DEBUGBUILD`` macro defined, this function is noop. Note that building with ``DEBUGBUILD`` may cause significant performance penalty to libnghttp2 because of extra processing. It should be used for debugging purpose only. .. Warning:: Building with ``DEBUGBUILD`` may cause significant performance penalty to libnghttp2 because of extra processing. It should be used for debugging purpose only. We write this two times because this is important. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_upgrade.rst0000644000000000000000000000013215077107332020416 xustar0030 mtime=1761382106.504309105 30 atime=1761382106.743308053 30 ctime=1761382109.689298678 nghttp2-1.68.0/doc/nghttp2_session_upgrade.rst0000644000175100017510000000472015077107332021011 0ustar00runnerrunner nghttp2_session_upgrade ======================= Synopsis -------- *#include * .. function:: int nghttp2_session_upgrade(nghttp2_session *session, const uint8_t *settings_payload, size_t settings_payloadlen, void *stream_user_data) .. warning:: This function is deprecated in favor of `nghttp2_session_upgrade2()`, because this function lacks the parameter to tell the library the request method used in the original HTTP request. This information is required for client to validate actual response body length against content-length header field (see `nghttp2_option_set_no_http_messaging()`). If HEAD is used in request, the length of response body must be 0 regardless of value included in content-length header field. Performs post-process of HTTP Upgrade request. This function can be called from both client and server, but the behavior is very different in each other. If called from client side, the *settings_payload* must be the value sent in ``HTTP2-Settings`` header field and must be decoded by base64url decoder. The *settings_payloadlen* is the length of *settings_payload*. The *settings_payload* is unpacked and its setting values will be submitted using `nghttp2_submit_settings()`. This means that the client application code does not need to submit SETTINGS by itself. The stream with stream ID=1 is opened and the *stream_user_data* is used for its stream_user_data. The opened stream becomes half-closed (local) state. If called from server side, the *settings_payload* must be the value received in ``HTTP2-Settings`` header field and must be decoded by base64url decoder. The *settings_payloadlen* is the length of *settings_payload*. It is treated as if the SETTINGS frame with that payload is received. Thus, callback functions for the reception of SETTINGS frame will be invoked. The stream with stream ID=1 is opened. The *stream_user_data* is ignored. The opened stream becomes half-closed (remote). This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The *settings_payload* is badly formed. :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` The stream ID 1 is already used or closed; or is not available. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_server_new2.rst0000644000000000000000000000013115077107332021227 xustar0030 mtime=1761382106.504309105 30 atime=1761382106.732308101 29 ctime=1761382109.67829871 nghttp2-1.68.0/doc/nghttp2_session_server_new2.rst0000644000175100017510000000157315077107332021626 0ustar00runnerrunner nghttp2_session_server_new2 =========================== Synopsis -------- *#include * .. function:: int nghttp2_session_server_new2(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option) Like `nghttp2_session_server_new()`, but with additional options specified in the *option*. The *option* can be ``NULL`` and the call is equivalent to `nghttp2_session_server_new()`. This function does not take ownership *option*. The application is responsible for freeing *option* if it finishes using the object. The library code does not refer to *option* after this function returns. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_set_next_stream_id.rst0000644000000000000000000000013215077107332022647 xustar0030 mtime=1761382106.504309105 30 atime=1761382106.736308083 30 ctime=1761382109.682298699 nghttp2-1.68.0/doc/nghttp2_session_set_next_stream_id.rst0000644000175100017510000000144215077107332023240 0ustar00runnerrunner nghttp2_session_set_next_stream_id ================================== Synopsis -------- *#include * .. function:: int nghttp2_session_set_next_stream_id(nghttp2_session *session, int32_t next_stream_id) Tells the *session* that next stream ID is *next_stream_id*. The *next_stream_id* must be equal or greater than the value returned by `nghttp2_session_get_next_stream_id()`. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The *next_stream_id* is strictly less than the value `nghttp2_session_get_next_stream_id()` returns; or *next_stream_id* is invalid (e.g., even integer for client, or odd integer for server). nghttp2-1.68.0/doc/PaxHeaders/nghttpd.1.rst0000644000000000000000000000013215077107270015366 xustar0030 mtime=1761382072.965444268 30 atime=1761382106.548308911 30 ctime=1761382109.492299248 nghttp2-1.68.0/doc/nghttpd.1.rst0000644000175100017510000001060515077107270015760 0ustar00runnerrunner .. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY. .. program:: nghttpd nghttpd(1) ========== SYNOPSIS -------- **nghttpd** [OPTION]... [ ] DESCRIPTION ----------- HTTP/2 server .. describe:: Specify listening port number. .. describe:: Set path to server's private key. Required unless :option:`--no-tls` is specified. .. describe:: Set path to server's certificate. Required unless :option:`--no-tls` is specified. OPTIONS ------- .. option:: -a, --address= The address to bind to. If not specified the default IP address determined by getaddrinfo is used. .. option:: -D, --daemon Run in a background. If :option:`-D` is used, the current working directory is changed to '*/*'. Therefore if this option is used, :option:`-d` option must be specified. .. option:: -V, --verify-client The server sends a client certificate request. If the client did not return a certificate, the handshake is terminated. Currently, this option just requests a client certificate and does not verify it. .. option:: -d, --htdocs= Specify document root. If this option is not specified, the document root is the current working directory. .. option:: -v, --verbose Print debug information such as reception/ transmission of frames and name/value pairs. .. option:: --no-tls Disable SSL/TLS. .. option:: -c, --header-table-size= Specify decoder header table size. .. option:: --encoder-header-table-size= Specify encoder header table size. The decoder (client) specifies the maximum dynamic table size it accepts. Then the negotiated dynamic table size is the minimum of this option value and the value which client specified. .. option:: --color Force colored log output. .. option:: -p, --push== Push resources s when is requested. This option can be used repeatedly to specify multiple push configurations. and s are relative to document root. See :option:`--htdocs` option. Example: :option:`-p`\/=/foo.png :option:`-p`\/doc=/bar.css .. option:: -b, --padding= Add at most bytes to a frame payload as padding. Specify 0 to disable padding. .. option:: -m, --max-concurrent-streams= Set the maximum number of the concurrent streams in one HTTP/2 session. Default: ``100`` .. option:: -n, --workers= Set the number of worker threads. Default: ``1`` .. option:: -e, --error-gzip Make error response gzipped. .. option:: -w, --window-bits= Sets the stream level initial window size to 2\*\*-1. .. option:: -W, --connection-window-bits= Sets the connection level initial window size to 2\*\*-1. .. option:: --dh-param-file= Path to file that contains DH parameters in PEM format. Without this option, DHE cipher suites are not available. .. option:: --early-response Start sending response when request HEADERS is received, rather than complete request is received. .. option:: --trailer=
Add a trailer header to a response.
must not include pseudo header field (header field name starting with ':'). The trailer is sent only if a response has body part. Example: :option:`--trailer` 'foo: bar'. .. option:: --hexdump Display the incoming traffic in hexadecimal (Canonical hex+ASCII display). If SSL/TLS is used, decrypted data are used. .. option:: --echo-upload Send back uploaded content if method is POST or PUT. .. option:: --mime-types-file= Path to file that contains MIME media types and the extensions that represent them. Default: ``/etc/mime.types`` .. option:: --no-content-length Don't send content-length header field. .. option:: --groups= Specify the supported groups. Default: ``X25519:P-256:P-384:P-521`` .. option:: --ktls Enable ktls. .. option:: --version Display version information and exit. .. option:: -h, --help Display this help and exit. The argument is an integer and an optional unit (e.g., 10K is 10 * 1024). Units are K, M and G (powers of 1024). SEE ALSO -------- :manpage:`nghttp(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)` nghttp2-1.68.0/doc/PaxHeaders/nghttp2_rcbuf_is_static.rst0000644000000000000000000000013215077107332020367 xustar0030 mtime=1761382106.500309123 30 atime=1761382106.637308519 30 ctime=1761382109.582298988 nghttp2-1.68.0/doc/nghttp2_rcbuf_is_static.rst0000644000175100017510000000057215077107332020763 0ustar00runnerrunner nghttp2_rcbuf_is_static ======================= Synopsis -------- *#include * .. function:: int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf) Returns nonzero if the underlying buffer is statically allocated, and 0 otherwise. This can be useful for language bindings that wish to avoid creating duplicate strings for these buffers. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_find_stream.rst0000644000000000000000000000013215077107332021262 xustar0030 mtime=1761382106.502309114 30 atime=1761382106.695308264 30 ctime=1761382109.641298817 nghttp2-1.68.0/doc/nghttp2_session_find_stream.rst0000644000175100017510000000123015077107332021646 0ustar00runnerrunner nghttp2_session_find_stream =========================== Synopsis -------- *#include * .. function:: nghttp2_stream * nghttp2_session_find_stream(nghttp2_session *session, int32_t stream_id) Returns pointer to :type:`nghttp2_stream` object denoted by *stream_id*. If stream was not found, returns NULL. Returns imaginary root stream (see `nghttp2_session_get_root_stream()`) if 0 is given in *stream_id*. Unless *stream_id* == 0, the returned pointer is valid until next call of `nghttp2_session_send()`, `nghttp2_session_mem_send2()`, `nghttp2_session_recv()`, and `nghttp2_session_mem_recv2()`. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_submit_request2.rst0000644000000000000000000000013215077107332020361 xustar0030 mtime=1761382106.505309101 30 atime=1761382106.775307911 30 ctime=1761382109.721298586 nghttp2-1.68.0/doc/nghttp2_submit_request2.rst0000644000175100017510000000611215077107332020751 0ustar00runnerrunner nghttp2_submit_request2 ======================= Synopsis -------- *#include * .. function:: int32_t nghttp2_submit_request2( nghttp2_session *session, const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider2 *data_prd, void *stream_user_data) Submits HEADERS frame and optionally one or more DATA frames. The *pri_spec* is ignored. The *nva* is an array of name/value pair :type:`nghttp2_nv` with *nvlen* elements. The application is responsible to include required pseudo-header fields (header field whose name starts with ":") in *nva* and must place pseudo-headers before regular header fields. This function creates copies of all name/value pairs in *nva*. It also lower-cases all names in *nva*. The order of elements in *nva* is preserved. For header fields with :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name and value are not copied respectively. With :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to pass header field name in lowercase. The application should maintain the references to them until :type:`nghttp2_on_frame_send_callback` or :type:`nghttp2_on_frame_not_send_callback` is called. HTTP/2 specification has requirement about header fields in the request HEADERS. See the specification for more details. If *data_prd* is not ``NULL``, it provides data which will be sent in subsequent DATA frames. In this case, a method that allows request message bodies (https://tools.ietf.org/html/rfc7231#section-4) must be specified with ``:method`` key in *nva* (e.g. ``POST``). This function does not take ownership of the *data_prd*. The function copies the members of the *data_prd*. If *data_prd* is ``NULL``, HEADERS have END_STREAM set. The *stream_user_data* is data associated to the stream opened by this request and can be an arbitrary pointer, which can be retrieved later by `nghttp2_session_get_stream_user_data()`. This function returns assigned stream ID if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` No stream ID is available because maximum stream ID was reached. :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` The *session* is server session. .. warning:: This function returns assigned stream ID if it succeeds. But that stream is not created yet. The application must not submit frame to that stream ID before :type:`nghttp2_before_frame_send_callback` is called for this frame. This means `nghttp2_session_get_stream_user_data()` does not work before the callback. But `nghttp2_session_set_stream_user_data()` handles this situation specially, and it can set data to a stream during this period. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_get_outbound_queue_size.rst0000644000000000000000000000013015077107332023721 xustar0029 mtime=1761382106.50330911 30 atime=1761382106.708308207 29 ctime=1761382109.65429878 nghttp2-1.68.0/doc/nghttp2_session_get_outbound_queue_size.rst0000644000175100017510000000051715077107332024316 0ustar00runnerrunner nghttp2_session_get_outbound_queue_size ======================================= Synopsis -------- *#include * .. function:: size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) Returns the number of frames in the outbound queue. This does not include the deferred DATA frames. nghttp2-1.68.0/doc/PaxHeaders/README.rst0000644000000000000000000000013215077107270014514 xustar0030 mtime=1761382072.963444278 30 atime=1761382106.544308929 30 ctime=1761382109.488299259 nghttp2-1.68.0/doc/README.rst0000644000175100017510000001204115077107270015102 0ustar00runnerrunnernghttp2 Documentation ===================== The documentation of nghttp2 is generated using Sphinx. This directory contains the source files to be processed by Sphinx. The source file for API reference is generated using a script called ``mkapiref.py`` from the nghttp2 C source code. Generating API reference ------------------------ As described earlier, we use ``mkapiref.py`` to generate rst formatted text of API reference from C source code. The ``mkapiref.py`` is not so flexible and it requires that C source code is formatted in rather strict rules. To generate API reference, just run ``make html``. It runs ``mkapiref.py`` and then run Sphinx to build the entire document. The ``mkapiref.py`` reads C source code and searches the comment block starts with ``/**``. In other words, it only processes the comment block starting ``/**``. The comment block must end with ``*/``. The ``mkapiref.py`` requires that which type of the object this comment block refers to. To specify the type of the object, the next line must contain the so-called action keyword. Currently, the following action keywords are supported: ``@function``, ``@functypedef``, ``@enum``, ``@struct`` and ``@union``. The following sections describes each action keyword. @function ######### ``@function`` is used to refer to the function. The comment block is used for the document for the function. After the script sees the end of the comment block, it consumes the lines as the function declaration until the line which ends with ``;`` is encountered. In Sphinx doc, usually the function argument is formatted like ``*this*``. But in C, ``*`` is used for dereferencing a pointer and we must escape ``*`` with a back slash. To avoid this, we format the argument like ``|this|``. The ``mkapiref.py`` translates it with ``*this*``, as escaping ``*`` inside ``|`` and ``|`` as necessary. Note that this shadows the substitution feature of Sphinx. The example follows:: /** * @function * * Submits PING frame to the |session|. */ int nghttp2_submit_ping(nghttp2_session *session); @functypedef ############ ``@functypedef`` is used to refer to the typedef of the function pointer. The formatting rule is pretty much the same with ``@function``, but this outputs ``type`` domain, rather than ``function`` domain. The example follows:: /** * @functypedef * * Callback function invoked when |session| wants to send data to * remote peer. */ typedef nghttp2_ssize (*nghttp2_send_callback2) (nghttp2_session *session, const uint8_t *data, size_t length, int flags, void *user_data); @enum ##### ``@enum`` is used to refer to the enum. Currently, only enum typedefs are supported. The comment block is used for the document for the enum type itself. To document each values, put comment block starting with the line ``/**`` and ending with the ``*/`` just before the enum value. When the line starts with ``}`` is encountered, the ``mkapiref.py`` extracts strings next to ``}`` as the name of enum. At the time of this writing, Sphinx does not support enum type. So we use ``type`` domain for enum it self and ``macro`` domain for each value. To refer to the enum value, use ``:enum:`` pseudo role. The ``mkapiref.py`` replaces it with ``:macro:``. By doing this, when Sphinx will support enum officially, we can replace ``:enum:`` with the official role easily. The example follows:: /** * @enum * Error codes used in the nghttp2 library. */ typedef enum { /** * Invalid argument passed. */ NGHTTP2_ERR_INVALID_ARGUMENT = -501, /** * Zlib error. */ NGHTTP2_ERR_ZLIB = -502, } nghttp2_error; @struct ####### ``@struct`` is used to refer to the struct. Currently, only struct typedefs are supported. The comment block is used for the document for the struct type itself. To document each member, put comment block starting with the line ``/**`` and ending with the ``*/`` just before the member. When the line starts with ``}`` is encountered, the ``mkapiref.py`` extracts strings next to ``}`` as the name of struct. The block-less typedef is also supported. In this case, typedef declaration must be all in one line and the ``mkapiref.py`` uses last word as the name of struct. Some examples follow:: /** * @struct * The control frame header. */ typedef struct { /** * SPDY protocol version. */ uint16_t version; /** * The type of this control frame. */ uint16_t type; /** * The control frame flags. */ uint8_t flags; /** * The length field of this control frame. */ int32_t length; } nghttp2_ctrl_hd; /** * @struct * * The primary structure to hold the resources needed for a SPDY * session. The details of this structure is hidden from the public * API. */ typedef struct nghttp2_session nghttp2_session; @union ###### ``@union`` is used to refer to the union. Currently, ``@union`` is an alias of ``@struct``. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_set_stream_user_data.rst0000644000000000000000000000013215077107332023164 xustar0030 mtime=1761382106.504309105 30 atime=1761382106.737308079 30 ctime=1761382109.683298696 nghttp2-1.68.0/doc/nghttp2_session_set_stream_user_data.rst0000644000175100017510000000150715077107332023557 0ustar00runnerrunner nghttp2_session_set_stream_user_data ==================================== Synopsis -------- *#include * .. function:: int nghttp2_session_set_stream_user_data(nghttp2_session *session, int32_t stream_id, void *stream_user_data) Sets the *stream_user_data* to the stream denoted by the *stream_id*. If a stream user data is already set to the stream, it is replaced with the *stream_user_data*. It is valid to specify ``NULL`` in the *stream_user_data*, which nullifies the associated data pointer. It is valid to set the *stream_user_data* to the stream reserved by PUSH_PROMISE frame. This function returns 0 if it succeeds, or one of following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The stream does not exist nghttp2-1.68.0/doc/PaxHeaders/nghttp2_submit_response2.rst0000644000000000000000000000013115077107332020526 xustar0030 mtime=1761382106.505309101 30 atime=1761382106.778307898 29 ctime=1761382109.72329858 nghttp2-1.68.0/doc/nghttp2_submit_response2.rst0000644000175100017510000000611715077107332021124 0ustar00runnerrunner nghttp2_submit_response2 ======================== Synopsis -------- *#include * .. function:: int nghttp2_submit_response2(nghttp2_session *session, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider2 *data_prd) Submits response HEADERS frame and optionally one or more DATA frames against the stream *stream_id*. The *nva* is an array of name/value pair :type:`nghttp2_nv` with *nvlen* elements. The application is responsible to include required pseudo-header fields (header field whose name starts with ":") in *nva* and must place pseudo-headers before regular header fields. This function creates copies of all name/value pairs in *nva*. It also lower-cases all names in *nva*. The order of elements in *nva* is preserved. For header fields with :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name and value are not copied respectively. With :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to pass header field name in lowercase. The application should maintain the references to them until :type:`nghttp2_on_frame_send_callback` or :type:`nghttp2_on_frame_not_send_callback` is called. HTTP/2 specification has requirement about header fields in the response HEADERS. See the specification for more details. If *data_prd* is not ``NULL``, it provides data which will be sent in subsequent DATA frames. This function does not take ownership of the *data_prd*. The function copies the members of the *data_prd*. If *data_prd* is ``NULL``, HEADERS will have END_STREAM flag set. This method can be used as normal HTTP response and push response. When pushing a resource using this function, the *session* must be configured using `nghttp2_session_server_new()` or its variants and the target stream denoted by the *stream_id* must be reserved using `nghttp2_submit_push_promise()`. To send non-final response headers (e.g., HTTP status 101), don't use this function because this function half-closes the outbound stream. Instead, use `nghttp2_submit_headers()` for this purpose. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The *stream_id* is 0. :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` DATA or HEADERS has been already submitted and not fully processed yet. Normally, this does not happen, but when application wrongly calls `nghttp2_submit_response2()` twice, this may happen. :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` The *session* is client session. .. warning:: Calling this function twice for the same stream ID may lead to program crash. It is generally considered to a programming error to commit response twice. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_on_frame_send_callback.rst0000644000000000000000000000013215077107332026254 xustar0030 mtime=1761382106.501309119 30 atime=1761382106.657308431 30 ctime=1761382109.603298927 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_on_frame_send_callback.rst0000644000175100017510000000057715077107332026655 0ustar00runnerrunner nghttp2_session_callbacks_set_on_frame_send_callback ==================================================== Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_on_frame_send_callback( nghttp2_session_callbacks *cbs, nghttp2_on_frame_send_callback on_frame_send_callback) Sets callback function invoked after a frame is sent. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_server_new.rst0000644000000000000000000000013215077107332021146 xustar0030 mtime=1761382106.504309105 30 atime=1761382106.731308105 30 ctime=1761382109.677298713 nghttp2-1.68.0/doc/nghttp2_session_server_new.rst0000644000175100017510000000175215077107332021543 0ustar00runnerrunner nghttp2_session_server_new ========================== Synopsis -------- *#include * .. function:: int nghttp2_session_server_new(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data) Initializes *\*session_ptr* for server use. The all members of *callbacks* are copied to *\*session_ptr*. Therefore *\*session_ptr* does not store *callbacks*. The *user_data* is an arbitrary user supplied data, which will be passed to the callback functions. The :type:`nghttp2_send_callback2` must be specified. If the application code uses `nghttp2_session_recv()`, the :type:`nghttp2_recv_callback` must be specified. The other members of *callbacks* can be ``NULL``. If this function fails, *\*session_ptr* is left untouched. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_option_set_no_closed_streams.rst0000644000000000000000000000013215077107332023172 xustar0030 mtime=1761382106.499309127 30 atime=1761382106.611308634 30 ctime=1761382109.555299066 nghttp2-1.68.0/doc/nghttp2_option_set_no_closed_streams.rst0000644000175100017510000000063515077107332023566 0ustar00runnerrunner nghttp2_option_set_no_closed_streams ==================================== Synopsis -------- *#include * .. function:: void nghttp2_option_set_no_closed_streams(nghttp2_option *option, int val) .. warning:: Deprecated. Closed streams are not retained anymore. This function works as before, but it does not take any effect against :type:`nghttp2_session`. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_submit_request.rst0000644000000000000000000000013215077107332020277 xustar0030 mtime=1761382106.505309101 30 atime=1761382106.774307916 30 ctime=1761382109.719298592 nghttp2-1.68.0/doc/nghttp2_submit_request.rst0000644000175100017510000000623515077107332020675 0ustar00runnerrunner nghttp2_submit_request ====================== Synopsis -------- *#include * .. function:: int32_t nghttp2_submit_request( nghttp2_session *session, const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider *data_prd, void *stream_user_data) .. warning:: Deprecated. Use `nghttp2_submit_request2()` instead. Submits HEADERS frame and optionally one or more DATA frames. The *pri_spec* is ignored. The *nva* is an array of name/value pair :type:`nghttp2_nv` with *nvlen* elements. The application is responsible to include required pseudo-header fields (header field whose name starts with ":") in *nva* and must place pseudo-headers before regular header fields. This function creates copies of all name/value pairs in *nva*. It also lower-cases all names in *nva*. The order of elements in *nva* is preserved. For header fields with :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name and value are not copied respectively. With :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to pass header field name in lowercase. The application should maintain the references to them until :type:`nghttp2_on_frame_send_callback` or :type:`nghttp2_on_frame_not_send_callback` is called. HTTP/2 specification has requirement about header fields in the request HEADERS. See the specification for more details. If *data_prd* is not ``NULL``, it provides data which will be sent in subsequent DATA frames. In this case, a method that allows request message bodies (https://tools.ietf.org/html/rfc7231#section-4) must be specified with ``:method`` key in *nva* (e.g. ``POST``). This function does not take ownership of the *data_prd*. The function copies the members of the *data_prd*. If *data_prd* is ``NULL``, HEADERS have END_STREAM set. The *stream_user_data* is data associated to the stream opened by this request and can be an arbitrary pointer, which can be retrieved later by `nghttp2_session_get_stream_user_data()`. This function returns assigned stream ID if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` No stream ID is available because maximum stream ID was reached. :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` The *session* is server session. .. warning:: This function returns assigned stream ID if it succeeds. But that stream is not created yet. The application must not submit frame to that stream ID before :type:`nghttp2_before_frame_send_callback` is called for this frame. This means `nghttp2_session_get_stream_user_data()` does not work before the callback. But `nghttp2_session_set_stream_user_data()` handles this situation specially, and it can set data to a stream during this period. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_del.rst0000644000000000000000000000013115077107332017532 xustar0030 mtime=1761382106.502309114 30 atime=1761382106.694308268 29 ctime=1761382109.64029882 nghttp2-1.68.0/doc/nghttp2_session_del.rst0000644000175100017510000000042015077107332020117 0ustar00runnerrunner nghttp2_session_del =================== Synopsis -------- *#include * .. function:: void nghttp2_session_del(nghttp2_session *session) Frees any resources allocated for *session*. If *session* is ``NULL``, this function does nothing. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_get_next_stream_id.rst0000644000000000000000000000013115077107332022632 xustar0029 mtime=1761382106.50330911 30 atime=1761382106.707308211 30 ctime=1761382109.653298783 nghttp2-1.68.0/doc/nghttp2_session_get_next_stream_id.rst0000644000175100017510000000057115077107332023226 0ustar00runnerrunner nghttp2_session_get_next_stream_id ================================== Synopsis -------- *#include * .. function:: uint32_t nghttp2_session_get_next_stream_id(nghttp2_session *session) Returns the next outgoing stream ID. Notice that return type is uint32_t. If we run out of stream ID for this session, this function returns 1 << 31. nghttp2-1.68.0/doc/PaxHeaders/nghttp.1.rst0000644000000000000000000000013215077107270015222 xustar0030 mtime=1761382072.964444273 30 atime=1761382106.547308916 30 ctime=1761382109.491299251 nghttp2-1.68.0/doc/nghttp.1.rst0000644000175100017510000001322215077107270015612 0ustar00runnerrunner .. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY. .. program:: nghttp nghttp(1) ========= SYNOPSIS -------- **nghttp** [OPTIONS]... ... DESCRIPTION ----------- HTTP/2 client .. describe:: Specify URI to access. OPTIONS ------- .. option:: -v, --verbose Print debug information such as reception and transmission of frames and name/value pairs. Specifying this option multiple times increases verbosity. .. option:: -n, --null-out Discard downloaded data. .. option:: -O, --remote-name Save download data in the current directory. The filename is derived from URI. If URI ends with '*/*', 'index.html' is used as a filename. Not implemented yet. .. option:: -t, --timeout= Timeout each request after . Set 0 to disable timeout. .. option:: -w, --window-bits= Sets the stream level initial window size to 2\*\*-1. .. option:: -W, --connection-window-bits= Sets the connection level initial window size to 2\*\*-1. .. option:: -a, --get-assets Download assets such as stylesheets, images and script files linked from the downloaded resource. Only links whose origins are the same with the linking resource will be downloaded. nghttp prioritizes resources using HTTP/2 dependency based priority. The priority order, from highest to lowest, is html itself, css, javascript and images. .. option:: -s, --stat Print statistics. .. option:: -H, --header=
Add a header to the requests. Example: :option:`-H`\':method: PUT' .. option:: --trailer=
Add a trailer header to the requests.
must not include pseudo header field (header field name starting with ':'). To send trailer, one must use :option:`-d` option to send request body. Example: :option:`--trailer` 'foo: bar'. .. option:: --cert= Use the specified client certificate file. The file must be in PEM format. .. option:: --key= Use the client private key file. The file must be in PEM format. .. option:: -d, --data= Post FILE to server. If '-' is given, data will be read from stdin. .. option:: -m, --multiply= Request each URI times. By default, same URI is not requested twice. This option disables it too. .. option:: -u, --upgrade Perform HTTP Upgrade for HTTP/2. This option is ignored if the request URI has https scheme. If :option:`-d` is used, the HTTP upgrade request is performed with OPTIONS method. .. option:: --extpri= Sets RFC 9218 priority of given URI. must be the wire format of priority header field (e.g., "u=3,i"). This option can be used multiple times, and N-th :option:`--extpri` option sets priority of N-th URI in the command line. If the number of this option is less than the number of URI, the last option value is repeated. If there is no :option:`--extpri` option, urgency is 3, and incremental is false. .. option:: -M, --peer-max-concurrent-streams= Use as SETTINGS_MAX_CONCURRENT_STREAMS value of remote endpoint as if it is received in SETTINGS frame. Default: ``100`` .. option:: -c, --header-table-size= Specify decoder header table size. If this option is used multiple times, and the minimum value among the given values except for last one is strictly less than the last value, that minimum value is set in SETTINGS frame payload before the last value, to simulate multiple header table size change. .. option:: --encoder-header-table-size= Specify encoder header table size. The decoder (server) specifies the maximum dynamic table size it accepts. Then the negotiated dynamic table size is the minimum of this option value and the value which server specified. .. option:: -b, --padding= Add at most bytes to a frame payload as padding. Specify 0 to disable padding. .. option:: -r, --har= Output HTTP transactions in HAR format. If '-' is given, data is written to stdout. .. option:: --color Force colored log output. .. option:: --continuation Send large header to test CONTINUATION. .. option:: --no-content-length Don't send content-length header field. .. option:: --hexdump Display the incoming traffic in hexadecimal (Canonical hex+ASCII display). If SSL/TLS is used, decrypted data are used. .. option:: --no-push Disable server push. .. option:: --max-concurrent-streams= The number of concurrent pushed streams this client accepts. .. option:: --expect-continue Perform an Expect/Continue handshake: wait to send DATA (up to a short timeout) until the server sends a 100 Continue interim response. This option is ignored unless combined with the :option:`-d` option. .. option:: -y, --no-verify-peer Suppress warning on server certificate verification failure. .. option:: --ktls Enable ktls. .. option:: --version Display version information and exit. .. option:: -h, --help Display this help and exit. The argument is an integer and an optional unit (e.g., 10K is 10 * 1024). Units are K, M and G (powers of 1024). The argument is an integer and an optional unit (e.g., 1s is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms (hours, minutes, seconds and milliseconds, respectively). If a unit is omitted, a second is used as unit. SEE ALSO -------- :manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)` nghttp2-1.68.0/doc/PaxHeaders/nghttp2_check_method.rst0000644000000000000000000000013115077107332017640 xustar0030 mtime=1761382106.497309136 30 atime=1761382106.561308854 29 ctime=1761382109.50529921 nghttp2-1.68.0/doc/nghttp2_check_method.rst0000644000175100017510000000065115077107332020233 0ustar00runnerrunner nghttp2_check_method ==================== Synopsis -------- *#include * .. function:: int nghttp2_check_method(const uint8_t *value, size_t len) Returns nonzero if the *value* which is supposed to be the value of the :method header field is valid according to https://datatracker.ietf.org/doc/html/rfc7231#section-4 and https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6 nghttp2-1.68.0/doc/PaxHeaders/nghttp2_option_set_builtin_recv_extension_type.rst0000644000000000000000000000013215077107332025311 xustar0030 mtime=1761382106.499309127 30 atime=1761382106.603308669 30 ctime=1761382109.547299089 nghttp2-1.68.0/doc/nghttp2_option_set_builtin_recv_extension_type.rst0000644000175100017510000000156115077107332025704 0ustar00runnerrunner nghttp2_option_set_builtin_recv_extension_type ============================================== Synopsis -------- *#include * .. function:: void nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option, uint8_t type) Sets extension frame type the application is willing to receive using builtin handler. The *type* is the extension frame type to receive, and must be strictly greater than 0x9. Otherwise, this function does nothing. The application can call this function multiple times to set more than one frame type to receive. The application does not have to call this function if it just sends extension frames. If same frame type is passed to both `nghttp2_option_set_builtin_recv_extension_type()` and `nghttp2_option_set_user_recv_extension_type()`, the latter takes precedence. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_get_last_proc_stream_id.rst0000644000000000000000000000013115077107332023642 xustar0029 mtime=1761382106.50330911 30 atime=1761382106.703308229 30 ctime=1761382109.649298794 nghttp2-1.68.0/doc/nghttp2_session_get_last_proc_stream_id.rst0000644000175100017510000000102715077107332024233 0ustar00runnerrunner nghttp2_session_get_last_proc_stream_id ======================================= Synopsis -------- *#include * .. function:: int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session) Returns the last stream ID of a stream for which :type:`nghttp2_on_frame_recv_callback` was invoked most recently. The returned value can be used as last_stream_id parameter for `nghttp2_submit_goaway()` and `nghttp2_session_terminate_session2()`. This function always succeeds. nghttp2-1.68.0/doc/PaxHeaders/h2load.10000644000000000000000000000013215077107270014260 xustar0030 mtime=1761382072.964444273 30 atime=1761382106.805307779 30 ctime=1761382109.750298502 nghttp2-1.68.0/doc/h2load.10000644000175100017510000003673115077107270014662 0ustar00runnerrunner.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "H2LOAD" "1" "Oct 25, 2025" "1.68.0" "nghttp2" .SH NAME h2load \- HTTP/2 benchmarking tool .SH SYNOPSIS .sp \fBh2load\fP [OPTIONS]... [URI]... .SH DESCRIPTION .sp benchmarking tool for HTTP/2 server .INDENT 0.0 .TP .B Specify URI to access. Multiple URIs can be specified. URIs are used in this order for each client. All URIs are used, then first URI is used and then 2nd URI, and so on. The scheme, host and port in the subsequent URIs, if present, are ignored. Those in the first URI are used solely. Definition of a base URI overrides all scheme, host or port values. .UNINDENT .SH OPTIONS .INDENT 0.0 .TP .B \-n, \-\-requests= Number of requests across all clients. If it is used with \fI\%\-\-timing\-script\-file\fP option, this option specifies the number of requests each client performs rather than the number of requests across all clients. This option is ignored if timing\-based benchmarking is enabled (see \fI\%\-\-duration\fP option). .sp Default: \fB1\fP .UNINDENT .INDENT 0.0 .TP .B \-c, \-\-clients= Number of concurrent clients. With \fI\%\-r\fP option, this specifies the maximum number of connections to be made. .sp Default: \fB1\fP .UNINDENT .INDENT 0.0 .TP .B \-t, \-\-threads= Number of native threads. .sp Default: \fB1\fP .UNINDENT .INDENT 0.0 .TP .B \-i, \-\-input\-file= Path of a file with multiple URIs are separated by EOLs. This option will disable URIs getting from command\-line. If \(aq\-\(aq is given as , URIs will be read from stdin. URIs are used in this order for each client. All URIs are used, then first URI is used and then 2nd URI, and so on. The scheme, host and port in the subsequent URIs, if present, are ignored. Those in the first URI are used solely. Definition of a base URI overrides all scheme, host or port values. .UNINDENT .INDENT 0.0 .TP .B \-m, \-\-max\-concurrent\-streams= Max concurrent streams to issue per session. When http/1.1 is used, this specifies the number of HTTP pipelining requests in\-flight. .sp Default: \fB1\fP .UNINDENT .INDENT 0.0 .TP .B \-f, \-\-max\-frame\-size= Maximum frame size that the local endpoint is willing to receive. .sp Default: \fB16K\fP .UNINDENT .INDENT 0.0 .TP .B \-w, \-\-window\-bits= Sets the stream level initial window size to (2**)\-1. For QUIC, is capped to 26 (roughly 64MiB). It defaults to 24 (16MiB) for QUIC, and 30 for other protocols. .UNINDENT .INDENT 0.0 .TP .B \-W, \-\-connection\-window\-bits= Sets the connection level initial window size to (2**)\-1. .sp Default: \fB30\fP .UNINDENT .INDENT 0.0 .TP .B \-H, \-\-header=
Add/Override a header to the requests. .UNINDENT .INDENT 0.0 .TP .B \-\-ciphers= Set allowed cipher list for TLSv1.2 or earlier. The format of the string is described in OpenSSL ciphers(1). .sp Default: \fBECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:DHE\-RSA\-AES128\-GCM\-SHA256:DHE\-RSA\-AES256\-GCM\-SHA384\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls13\-ciphers= Set allowed cipher list for TLSv1.3. The format of the string is described in OpenSSL ciphers(1). .sp Default: \fBTLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256\fP .UNINDENT .INDENT 0.0 .TP .B \-p, \-\-no\-tls\-proto= Specify ALPN identifier of the protocol to be used when accessing http URI without SSL/TLS. Available protocols: h2c and http/1.1 .sp Default: \fBh2c\fP .UNINDENT .INDENT 0.0 .TP .B \-d, \-\-data= Post FILE to server. The request method is changed to POST. For http/1.1 connection, if \fI\%\-d\fP is used, the maximum number of in\-flight pipelined requests is set to 1. .UNINDENT .INDENT 0.0 .TP .B \-r, \-\-rate= Specifies the fixed rate at which connections are created. The rate must be a positive integer, representing the number of connections to be made per rate period. The maximum number of connections to be made is given in \fI\%\-c\fP option. This rate will be distributed among threads as evenly as possible. For example, with \fI\%\-t\fP2 and \fI\%\-r\fP4, each thread gets 2 connections per period. When the rate is 0, the program will run as it normally does, creating connections at whatever variable rate it wants. The default value for this option is 0. \fI\%\-r\fP and \fI\%\-D\fP are mutually exclusive. .UNINDENT .INDENT 0.0 .TP .B \-\-rate\-period= Specifies the time period between creating connections. The period must be a positive number, representing the length of the period in time. This option is ignored if the rate option is not used. The default value for this option is 1s. .UNINDENT .INDENT 0.0 .TP .B \-D, \-\-duration= Specifies the main duration for the measurements in case of timing\-based benchmarking. \fI\%\-D\fP and \fI\%\-r\fP are mutually exclusive. .UNINDENT .INDENT 0.0 .TP .B \-\-warm\-up\-time= Specifies the time period before starting the actual measurements, in case of timing\-based benchmarking. Needs to provided along with \fI\%\-D\fP option. .UNINDENT .INDENT 0.0 .TP .B \-T, \-\-connection\-active\-timeout= Specifies the maximum time that h2load is willing to keep a connection open, regardless of the activity on said connection. must be a positive integer, specifying the amount of time to wait. When no timeout value is set (either active or inactive), h2load will keep a connection open indefinitely, waiting for a response. .UNINDENT .INDENT 0.0 .TP .B \-N, \-\-connection\-inactivity\-timeout= Specifies the amount of time that h2load is willing to wait to see activity on a given connection. must be a positive integer, specifying the amount of time to wait. When no timeout value is set (either active or inactive), h2load will keep a connection open indefinitely, waiting for a response. .UNINDENT .INDENT 0.0 .TP .B \-\-timing\-script\-file= Path of a file containing one or more lines separated by EOLs. Each script line is composed of two tab\-separated fields. The first field represents the time offset from the start of execution, expressed as a positive value of milliseconds with microsecond resolution. The second field represents the URI. This option will disable URIs getting from command\-line. If \(aq\-\(aq is given as , script lines will be read from stdin. Script lines are used in order for each client. If \fI\%\-n\fP is given, it must be less than or equal to the number of script lines, larger values are clamped to the number of script lines. If \fI\%\-n\fP is not given, the number of requests will default to the number of script lines. The scheme, host and port defined in the first URI are used solely. Values contained in other URIs, if present, are ignored. Definition of a base URI overrides all scheme, host or port values. \fI\%\-\-timing\-script\-file\fP and \fI\%\-\-rps\fP are mutually exclusive. .UNINDENT .INDENT 0.0 .TP .B \-B, \-\-base\-uri=(|unix:) Specify URI from which the scheme, host and port will be used for all requests. The base URI overrides all values defined either at the command line or inside input files. If argument starts with \(dqunix:\(dq, then the rest of the argument will be treated as UNIX domain socket path. The connection is made through that path instead of TCP. In this case, scheme is inferred from the first URI appeared in the command line or inside input files as usual. .UNINDENT .INDENT 0.0 .TP .B \-\-alpn\-list= Comma delimited list of ALPN protocol identifier sorted in the order of preference. That means most desirable protocol comes first. The parameter must be delimited by a single comma only and any white spaces are treated as a part of protocol string. .sp Default: \fBh2,http/1.1\fP .UNINDENT .INDENT 0.0 .TP .B \-\-h1 Short hand for \fI\%\-\-alpn\-list\fP=http/1.1 \fI\%\-\-no\-tls\-proto\fP=http/1.1, which effectively force http/1.1 for both http and https URI. .UNINDENT .INDENT 0.0 .TP .B \-\-header\-table\-size= Specify decoder header table size. .sp Default: \fB4K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-encoder\-header\-table\-size= Specify encoder header table size. The decoder (server) specifies the maximum dynamic table size it accepts. Then the negotiated dynamic table size is the minimum of this option value and the value which server specified. .sp Default: \fB4K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-log\-file= Write per\-request information to a file as tab\-separated columns: start time as microseconds since epoch; HTTP status code; microseconds until end of response. More columns may be added later. Rows are ordered by end\-of\- response time when using one worker thread, but may appear slightly out of order with multiple threads due to buffering. Status code is \-1 for failed streams. .UNINDENT .INDENT 0.0 .TP .B \-\-qlog\-file\-base= Enable qlog output and specify base file name for qlogs. Qlog is emitted for each connection. For a given base name \(dqbase\(dq, each output file name becomes \(dqbase.M.N.sqlog\(dq where M is worker ID and N is client ID (e.g. \(dqbase.0.3.sqlog\(dq). Only effective in QUIC runs. .UNINDENT .INDENT 0.0 .TP .B \-\-connect\-to=[:] Host and port to connect instead of using the authority in . .UNINDENT .INDENT 0.0 .TP .B \-\-rps= Specify request per second for each client. \fI\%\-\-rps\fP and \fI\%\-\-timing\-script\-file\fP are mutually exclusive. .UNINDENT .INDENT 0.0 .TP .B \-\-groups= Specify the supported groups. .sp Default: \fBX25519:P\-256:P\-384:P\-521\fP .UNINDENT .INDENT 0.0 .TP .B \-\-no\-udp\-gso Disable UDP GSO. .UNINDENT .INDENT 0.0 .TP .B \-\-max\-udp\-payload\-size= Specify the maximum outgoing UDP datagram payload size. .UNINDENT .INDENT 0.0 .TP .B \-\-ktls Enable ktls. .UNINDENT .INDENT 0.0 .TP .B \-\-sni= Send in TLS SNI, overriding the host name specified in URI. .UNINDENT .INDENT 0.0 .TP .B \-v, \-\-verbose Output debug information. .UNINDENT .INDENT 0.0 .TP .B \-\-version Display version information and exit. .UNINDENT .INDENT 0.0 .TP .B \-h, \-\-help Display this help and exit. .UNINDENT .sp The argument is an integer and an optional unit (e.g., 10K is 10 * 1024). Units are K, M and G (powers of 1024). .sp The argument is an integer and an optional unit (e.g., 1s is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms (hours, minutes, seconds and milliseconds, respectively). If a unit is omitted, a second is used as unit. .SH OUTPUT .INDENT 0.0 .TP .B requests .INDENT 7.0 .TP .B total The number of requests h2load was instructed to make. .TP .B started The number of requests h2load has started. .TP .B done The number of requests completed. .TP .B succeeded The number of requests completed successfully. Only HTTP status code 2xx or 3xx are considered as success. .TP .B failed The number of requests failed, including HTTP level failures (non\-successful HTTP status code). .TP .B errored The number of requests failed, except for HTTP level failures. This is the subset of the number reported in \fBfailed\fP and most likely the network level failures or stream was reset by RST_STREAM. .TP .B timeout The number of requests whose connection timed out before they were completed. This is the subset of the number reported in \fBerrored\fP\&. .UNINDENT .TP .B status codes The number of status code h2load received. .TP .B traffic .INDENT 7.0 .TP .B total The number of bytes received from the server \(dqon the wire\(dq. If requests were made via TLS, this value is the number of decrypted bytes. .TP .B headers The number of response header bytes from the server without decompression. The \fBspace savings\fP shows efficiency of header compression. Let \fBdecompressed(headers)\fP to the number of bytes used for header fields after decompression. The \fBspace savings\fP is calculated by (1 \- \fBheaders\fP / \fBdecompressed(headers)\fP) * 100. For HTTP/1.1, this is usually 0.00%, since it does not have header compression. For HTTP/2, it shows some insightful numbers. .TP .B data The number of response body bytes received from the server. .UNINDENT .TP .B time for request .INDENT 7.0 .TP .B min The minimum time taken for request and response. .TP .B max The maximum time taken for request and response. .TP .B mean The mean time taken for request and response. .TP .B sd The standard deviation of the time taken for request and response. .TP .B +/\- sd The fraction of the number of requests within standard deviation range (mean +/\- sd) against total number of successful requests. .UNINDENT .TP .B time for connect .INDENT 7.0 .TP .B min The minimum time taken to connect to a server including TLS handshake. .TP .B max The maximum time taken to connect to a server including TLS handshake. .TP .B mean The mean time taken to connect to a server including TLS handshake. .TP .B sd The standard deviation of the time taken to connect to a server. .TP .B +/\- sd The fraction of the number of connections within standard deviation range (mean +/\- sd) against total number of successful connections. .UNINDENT .TP .B time for 1st byte (of (decrypted in case of TLS) application data) .INDENT 7.0 .TP .B min The minimum time taken to get 1st byte from a server. .TP .B max The maximum time taken to get 1st byte from a server. .TP .B mean The mean time taken to get 1st byte from a server. .TP .B sd The standard deviation of the time taken to get 1st byte from a server. .TP .B +/\- sd The fraction of the number of connections within standard deviation range (mean +/\- sd) against total number of successful connections. .UNINDENT .TP .B req/s .INDENT 7.0 .TP .B min The minimum request per second among all clients. .TP .B max The maximum request per second among all clients. .TP .B mean The mean request per second among all clients. .TP .B sd The standard deviation of request per second among all clients. server. .TP .B +/\- sd The fraction of the number of connections within standard deviation range (mean +/\- sd) against total number of successful connections. .UNINDENT .UNINDENT .SH FLOW CONTROL .sp h2load sets large flow control window by default, and effectively disables flow control to avoid under utilization of server performance. To set smaller flow control window, use \fI\%\-w\fP and \fI\%\-W\fP options. For example, use \fB\-w16 \-W16\fP to set default window size described in HTTP/2 protocol specification. .SH SEE ALSO .sp \fBnghttp(1)\fP, \fBnghttpd(1)\fP, \fBnghttpx(1)\fP .SH AUTHOR Tatsuhiro Tsujikawa .SH COPYRIGHT 2012, 2015, 2016, Tatsuhiro Tsujikawa .\" Generated by docutils manpage writer. . nghttp2-1.68.0/doc/PaxHeaders/nghttp2_option_set_glitch_rate_limit.rst0000644000000000000000000000013215077107332023152 xustar0030 mtime=1761382106.499309127 30 atime=1761382106.625308572 30 ctime=1761382109.569299025 nghttp2-1.68.0/doc/nghttp2_option_set_glitch_rate_limit.rst0000644000175100017510000000150115077107332023537 0ustar00runnerrunner nghttp2_option_set_glitch_rate_limit ==================================== Synopsis -------- *#include * .. function:: void nghttp2_option_set_glitch_rate_limit(nghttp2_option *option, uint64_t burst, uint64_t rate) This function sets the rate limit for the "glitches", the suspicious activities from a remote endpoint. It is a token-bucket based rate limiter. *burst* specifies the number of tokens that is initially available. The maximum number of tokens is capped to this value. *rate* specifies the number of tokens that are regenerated per second. When a suspicious activity is detected, some amount of tokens are consumed. If there is no token available, GOAWAY is sent to tear down the connection. *burst* and *rate* default to 1000 and 33 respectively. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_inflate_new2.rst0000644000000000000000000000013015077107332020252 xustar0030 mtime=1761382106.499309127 28 atime=1761382106.5963087 30 ctime=1761382109.539299112 nghttp2-1.68.0/doc/nghttp2_hd_inflate_new2.rst0000644000175100017510000000121015077107332020636 0ustar00runnerrunner nghttp2_hd_inflate_new2 ======================= Synopsis -------- *#include * .. function:: int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr, nghttp2_mem *mem) Like `nghttp2_hd_inflate_new()`, but with additional custom memory allocator specified in the *mem*. The *mem* can be ``NULL`` and the call is equivalent to `nghttp2_hd_inflate_new()`. This function does not take ownership *mem*. The application is responsible for freeing *mem*. The library code does not refer to *mem* pointer after this function returns, so the application can safely free it. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_on_begin_frame_callback.rst0000644000000000000000000000013115077107332026406 xustar0030 mtime=1761382106.501309119 30 atime=1761382106.649308467 29 ctime=1761382109.59529895 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_on_begin_frame_callback.rst0000644000175100017510000000061615077107332027002 0ustar00runnerrunner nghttp2_session_callbacks_set_on_begin_frame_callback ===================================================== Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_on_begin_frame_callback( nghttp2_session_callbacks *cbs, nghttp2_on_begin_frame_callback on_begin_frame_callback) Sets callback function invoked when a frame header is received. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_priority_spec_default_init.rst0000644000000000000000000000013115077107332022645 xustar0030 mtime=1761382106.500309123 29 atime=1761382106.63030855 30 ctime=1761382109.575299008 nghttp2-1.68.0/doc/nghttp2_priority_spec_default_init.rst0000644000175100017510000000105515077107332023237 0ustar00runnerrunner nghttp2_priority_spec_default_init ================================== Synopsis -------- *#include * .. function:: void nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec) .. warning:: Deprecated. :rfc:`7540` priorities are deprecated by :rfc:`9113`. Consider migrating to :rfc:`9218` extensible prioritization scheme. Initializes *pri_spec* with the default values. The default values are: stream_id = 0, weight = :macro:`NGHTTP2_DEFAULT_WEIGHT` and exclusive = 0. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_submit_altsvc.rst0000644000000000000000000000013215077107332020103 xustar0030 mtime=1761382106.505309101 30 atime=1761382106.759307982 30 ctime=1761382109.705298632 nghttp2-1.68.0/doc/nghttp2_submit_altsvc.rst0000644000175100017510000000305315077107332020474 0ustar00runnerrunner nghttp2_submit_altsvc ===================== Synopsis -------- *#include * .. function:: int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *origin, size_t origin_len, const uint8_t *field_value, size_t field_value_len) Submits ALTSVC frame. ALTSVC frame is a non-critical extension to HTTP/2, and defined in `RFC 7383 `_. The *flags* is currently ignored and should be :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. The *origin* points to the origin this alternative service is associated with. The *origin_len* is the length of the origin. If *stream_id* is 0, the origin must be specified. If *stream_id* is not zero, the origin must be empty (in other words, *origin_len* must be 0). The ALTSVC frame is only usable from server side. If this function is invoked with client side session, this function returns :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` The function is called from client side session :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The sum of *origin_len* and *field_value_len* is larger than 16382; or *origin_len* is 0 while *stream_id* is 0; or *origin_len* is not 0 while *stream_id* is not 0. nghttp2-1.68.0/doc/PaxHeaders/_exts0000644000000000000000000000013215077107335014074 xustar0030 mtime=1761382109.466299323 30 atime=1761382109.794298375 30 ctime=1761382109.466299323 nghttp2-1.68.0/doc/_exts/0000755000175100017510000000000015077107335014541 5ustar00runnerrunnernghttp2-1.68.0/doc/_exts/PaxHeaders/rubydomain0000644000000000000000000000013215077107335016245 xustar0030 mtime=1761382109.745298517 30 atime=1761382109.794298375 30 ctime=1761382109.745298517 nghttp2-1.68.0/doc/_exts/rubydomain/0000755000175100017510000000000015077107335016712 5ustar00runnerrunnernghttp2-1.68.0/doc/_exts/rubydomain/PaxHeaders/rubydomain.py0000644000000000000000000000013215077107270021043 xustar0030 mtime=1761382072.963444278 30 atime=1761382106.799307806 30 ctime=1761382109.745298517 nghttp2-1.68.0/doc/_exts/rubydomain/rubydomain.py0000644000175100017510000006256215077107270021446 0ustar00runnerrunner# -*- coding: utf-8 -*- """ sphinx.domains.ruby ~~~~~~~~~~~~~~~~~~~ The Ruby domain. :copyright: Copyright 2010 by SHIBUKAWA Yoshiki :license: BSD, see LICENSE for details. """ import re from docutils import nodes from docutils.parsers.rst import directives from docutils.parsers.rst import Directive from sphinx import addnodes from sphinx import version_info from sphinx.roles import XRefRole from sphinx.locale import _ from sphinx.domains import Domain, ObjType, Index from sphinx.directives import ObjectDescription from sphinx.util.nodes import make_refnode from sphinx.util.docfields import Field, GroupedField, TypedField # REs for Ruby signatures rb_sig_re = re.compile( r'''^ ([\w.]*\.)? # class name(s) (\$?\w+\??!?) \s* # thing name (?: \((.*)\) # optional: arguments (?:\s* -> \s* (.*))? # return annotation )? $ # and nothing more ''', re.VERBOSE) rb_paramlist_re = re.compile(r'([\[\],])') # split at '[', ']' and ',' separators = { 'method':'#', 'attr_reader':'#', 'attr_writer':'#', 'attr_accessor':'#', 'function':'.', 'classmethod':'.', 'class':'::', 'module':'::', 'global':'', 'const':'::'} rb_separator = re.compile(r"(?:\w+)?(?:::)?(?:\.)?(?:#)?") def _iteritems(d): for k in d: yield k, d[k] def ruby_rsplit(fullname): items = [item for item in rb_separator.findall(fullname)] return ''.join(items[:-2]), items[-1] class RubyObject(ObjectDescription): """ Description of a general Ruby object. """ option_spec = { 'noindex': directives.flag, 'module': directives.unchanged, } doc_field_types = [ TypedField('parameter', label=_('Parameters'), names=('param', 'parameter', 'arg', 'argument'), typerolename='obj', typenames=('paramtype', 'type')), TypedField('variable', label=_('Variables'), rolename='obj', names=('var', 'ivar', 'cvar'), typerolename='obj', typenames=('vartype',)), GroupedField('exceptions', label=_('Raises'), rolename='exc', names=('raises', 'raise', 'exception', 'except'), can_collapse=True), Field('returnvalue', label=_('Returns'), has_arg=False, names=('returns', 'return')), Field('returntype', label=_('Return type'), has_arg=False, names=('rtype',)), ] def get_signature_prefix(self, sig): """ May return a prefix to put before the object name in the signature. """ return '' def needs_arglist(self): """ May return true if an empty argument list is to be generated even if the document contains none. """ return False def handle_signature(self, sig, signode): """ Transform a Ruby signature into RST nodes. Returns (fully qualified name of the thing, classname if any). If inside a class, the current class name is handled intelligently: * it is stripped from the displayed name if present * it is added to the full name (return value) if not present """ m = rb_sig_re.match(sig) if m is None: raise ValueError name_prefix, name, arglist, retann = m.groups() if not name_prefix: name_prefix = "" # determine module and class name (if applicable), as well as full name modname = self.options.get( 'module', self.env.temp_data.get('rb:module')) classname = self.env.temp_data.get('rb:class') if self.objtype == 'global': add_module = False modname = None classname = None fullname = name elif classname: add_module = False if name_prefix and name_prefix.startswith(classname): fullname = name_prefix + name # class name is given again in the signature name_prefix = name_prefix[len(classname):].lstrip('.') else: separator = separators[self.objtype] fullname = classname + separator + name_prefix + name else: add_module = True if name_prefix: classname = name_prefix.rstrip('.') fullname = name_prefix + name else: classname = '' fullname = name signode['module'] = modname signode['class'] = self.class_name = classname signode['fullname'] = fullname sig_prefix = self.get_signature_prefix(sig) if sig_prefix: signode += addnodes.desc_annotation(sig_prefix, sig_prefix) if name_prefix: signode += addnodes.desc_addname(name_prefix, name_prefix) # exceptions are a special case, since they are documented in the # 'exceptions' module. elif add_module and self.env.config.add_module_names: if self.objtype == 'global': nodetext = '' signode += addnodes.desc_addname(nodetext, nodetext) else: modname = self.options.get( 'module', self.env.temp_data.get('rb:module')) if modname and modname != 'exceptions': nodetext = modname + separators[self.objtype] signode += addnodes.desc_addname(nodetext, nodetext) signode += addnodes.desc_name(name, name) if not arglist: if self.needs_arglist(): # for callables, add an empty parameter list signode += addnodes.desc_parameterlist() if retann: signode += addnodes.desc_returns(retann, retann) return fullname, name_prefix signode += addnodes.desc_parameterlist() stack = [signode[-1]] for token in rb_paramlist_re.split(arglist): if token == '[': opt = addnodes.desc_optional() stack[-1] += opt stack.append(opt) elif token == ']': try: stack.pop() except IndexError: raise ValueError elif not token or token == ',' or token.isspace(): pass else: token = token.strip() stack[-1] += addnodes.desc_parameter(token, token) if len(stack) != 1: raise ValueError if retann: signode += addnodes.desc_returns(retann, retann) return fullname, name_prefix def get_index_text(self, modname, name): """ Return the text for the index entry of the object. """ raise NotImplementedError('must be implemented in subclasses') def _is_class_member(self): return self.objtype.endswith('method') or self.objtype.startswith('attr') def add_target_and_index(self, name_cls, sig, signode): if self.objtype == 'global': modname = '' else: modname = self.options.get( 'module', self.env.temp_data.get('rb:module')) separator = separators[self.objtype] if self._is_class_member(): if signode['class']: prefix = modname and modname + '::' or '' else: prefix = modname and modname + separator or '' else: prefix = modname and modname + separator or '' fullname = prefix + name_cls[0] # note target if fullname not in self.state.document.ids: signode['names'].append(fullname) signode['ids'].append(fullname) signode['first'] = (not self.names) self.state.document.note_explicit_target(signode) objects = self.env.domaindata['rb']['objects'] if fullname in objects: self.env.warn( self.env.docname, 'duplicate object description of %s, ' % fullname + 'other instance in ' + self.env.doc2path(objects[fullname][0]), self.lineno) objects[fullname] = (self.env.docname, self.objtype) indextext = self.get_index_text(modname, name_cls) if indextext: self.indexnode['entries'].append( _make_index('single', indextext, fullname, fullname)) def before_content(self): # needed for automatic qualification of members (reset in subclasses) self.clsname_set = False def after_content(self): if self.clsname_set: self.env.temp_data['rb:class'] = None class RubyModulelevel(RubyObject): """ Description of an object on module level (functions, data). """ def needs_arglist(self): return self.objtype == 'function' def get_index_text(self, modname, name_cls): if self.objtype == 'function': if not modname: return _('%s() (global function)') % name_cls[0] return _('%s() (module function in %s)') % (name_cls[0], modname) else: return '' class RubyGloballevel(RubyObject): """ Description of an object on module level (functions, data). """ def get_index_text(self, modname, name_cls): if self.objtype == 'global': return _('%s (global variable)') % name_cls[0] else: return '' class RubyEverywhere(RubyObject): """ Description of a class member (methods, attributes). """ def needs_arglist(self): return self.objtype == 'method' def get_index_text(self, modname, name_cls): name, cls = name_cls add_modules = self.env.config.add_module_names if self.objtype == 'method': try: clsname, methname = ruby_rsplit(name) except ValueError: if modname: return _('%s() (in module %s)') % (name, modname) else: return '%s()' % name if modname and add_modules: return _('%s() (%s::%s method)') % (methname, modname, clsname) else: return _('%s() (%s method)') % (methname, clsname) else: return '' class RubyClasslike(RubyObject): """ Description of a class-like object (classes, exceptions). """ def get_signature_prefix(self, sig): return self.objtype + ' ' def get_index_text(self, modname, name_cls): if self.objtype == 'class': if not modname: return _('%s (class)') % name_cls[0] return _('%s (class in %s)') % (name_cls[0], modname) elif self.objtype == 'exception': return name_cls[0] else: return '' def before_content(self): RubyObject.before_content(self) if self.names: self.env.temp_data['rb:class'] = self.names[0][0] self.clsname_set = True class RubyClassmember(RubyObject): """ Description of a class member (methods, attributes). """ def needs_arglist(self): return self.objtype.endswith('method') def get_signature_prefix(self, sig): if self.objtype == 'classmethod': return "classmethod %s." % self.class_name elif self.objtype == 'attr_reader': return "attribute [R] " elif self.objtype == 'attr_writer': return "attribute [W] " elif self.objtype == 'attr_accessor': return "attribute [R/W] " return '' def get_index_text(self, modname, name_cls): name, cls = name_cls add_modules = self.env.config.add_module_names if self.objtype == 'classmethod': try: clsname, methname = ruby_rsplit(name) except ValueError: return '%s()' % name if modname: return _('%s() (%s.%s class method)') % (methname, modname, clsname) else: return _('%s() (%s class method)') % (methname, clsname) elif self.objtype.startswith('attr'): try: clsname, attrname = ruby_rsplit(name) except ValueError: return name if modname and add_modules: return _('%s (%s.%s attribute)') % (attrname, modname, clsname) else: return _('%s (%s attribute)') % (attrname, clsname) else: return '' def before_content(self): RubyObject.before_content(self) lastname = self.names and self.names[-1][1] if lastname and not self.env.temp_data.get('rb:class'): self.env.temp_data['rb:class'] = lastname.strip('.') self.clsname_set = True class RubyModule(Directive): """ Directive to mark description of a new module. """ has_content = False required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False option_spec = { 'platform': lambda x: x, 'synopsis': lambda x: x, 'noindex': directives.flag, 'deprecated': directives.flag, } def run(self): env = self.state.document.settings.env modname = self.arguments[0].strip() noindex = 'noindex' in self.options env.temp_data['rb:module'] = modname env.domaindata['rb']['modules'][modname] = \ (env.docname, self.options.get('synopsis', ''), self.options.get('platform', ''), 'deprecated' in self.options) targetnode = nodes.target('', '', ids=['module-' + modname], ismod=True) self.state.document.note_explicit_target(targetnode) ret = [targetnode] # XXX this behavior of the module directive is a mess... if 'platform' in self.options: platform = self.options['platform'] node = nodes.paragraph() node += nodes.emphasis('', _('Platforms: ')) node += nodes.Text(platform, platform) ret.append(node) # the synopsis isn't printed; in fact, it is only used in the # modindex currently if not noindex: indextext = _('%s (module)') % modname inode = addnodes.index(entries=[_make_index( 'single', indextext, 'module-' + modname, modname)]) ret.append(inode) return ret def _make_index(entrytype, entryname, target, ignored, key=None): # Sphinx 1.4 introduced backward incompatible changes, it now # requires 5 tuples. Last one is categorization key. See # http://www.sphinx-doc.org/en/stable/extdev/nodes.html#sphinx.addnodes.index if version_info >= (1, 4, 0, '', 0): return (entrytype, entryname, target, ignored, key) else: return (entrytype, entryname, target, ignored) class RubyCurrentModule(Directive): """ This directive is just to tell Sphinx that we're documenting stuff in module foo, but links to module foo won't lead here. """ has_content = False required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False option_spec = {} def run(self): env = self.state.document.settings.env modname = self.arguments[0].strip() if modname == 'None': env.temp_data['rb:module'] = None else: env.temp_data['rb:module'] = modname return [] class RubyXRefRole(XRefRole): def process_link(self, env, refnode, has_explicit_title, title, target): if not has_explicit_title: title = title.lstrip('.') # only has a meaning for the target title = title.lstrip('#') if title.startswith("::"): title = title[2:] target = target.lstrip('~') # only has a meaning for the title # if the first character is a tilde, don't display the module/class # parts of the contents if title[0:1] == '~': m = re.search(r"(?:\.)?(?:#)?(?:::)?(.*)\Z", title) if m: title = m.group(1) if not title.startswith("$"): refnode['rb:module'] = env.temp_data.get('rb:module') refnode['rb:class'] = env.temp_data.get('rb:class') # if the first character is a dot, search more specific namespaces first # else search builtins first if target[0:1] == '.': target = target[1:] refnode['refspecific'] = True return title, target class RubyModuleIndex(Index): """ Index subclass to provide the Ruby module index. """ name = 'modindex' localname = _('Ruby Module Index') shortname = _('modules') def generate(self, docnames=None): content = {} # list of prefixes to ignore ignores = self.domain.env.config['modindex_common_prefix'] ignores = sorted(ignores, key=len, reverse=True) # list of all modules, sorted by module name modules = sorted(_iteritems(self.domain.data['modules']), key=lambda x: x[0].lower()) # sort out collapsible modules prev_modname = '' num_toplevels = 0 for modname, (docname, synopsis, platforms, deprecated) in modules: if docnames and docname not in docnames: continue for ignore in ignores: if modname.startswith(ignore): modname = modname[len(ignore):] stripped = ignore break else: stripped = '' # we stripped the whole module name? if not modname: modname, stripped = stripped, '' entries = content.setdefault(modname[0].lower(), []) package = modname.split('::')[0] if package != modname: # it's a submodule if prev_modname == package: # first submodule - make parent a group head entries[-1][1] = 1 elif not prev_modname.startswith(package): # submodule without parent in list, add dummy entry entries.append([stripped + package, 1, '', '', '', '', '']) subtype = 2 else: num_toplevels += 1 subtype = 0 qualifier = deprecated and _('Deprecated') or '' entries.append([stripped + modname, subtype, docname, 'module-' + stripped + modname, platforms, qualifier, synopsis]) prev_modname = modname # apply heuristics when to collapse modindex at page load: # only collapse if number of toplevel modules is larger than # number of submodules collapse = len(modules) - num_toplevels < num_toplevels # sort by first letter content = sorted(_iteritems(content)) return content, collapse class RubyDomain(Domain): """Ruby language domain.""" name = 'rb' label = 'Ruby' object_types = { 'function': ObjType(_('function'), 'func', 'obj'), 'global': ObjType(_('global variable'), 'global', 'obj'), 'method': ObjType(_('method'), 'meth', 'obj'), 'class': ObjType(_('class'), 'class', 'obj'), 'exception': ObjType(_('exception'), 'exc', 'obj'), 'classmethod': ObjType(_('class method'), 'meth', 'obj'), 'attr_reader': ObjType(_('attribute'), 'attr', 'obj'), 'attr_writer': ObjType(_('attribute'), 'attr', 'obj'), 'attr_accessor': ObjType(_('attribute'), 'attr', 'obj'), 'const': ObjType(_('const'), 'const', 'obj'), 'module': ObjType(_('module'), 'mod', 'obj'), } directives = { 'function': RubyModulelevel, 'global': RubyGloballevel, 'method': RubyEverywhere, 'const': RubyEverywhere, 'class': RubyClasslike, 'exception': RubyClasslike, 'classmethod': RubyClassmember, 'attr_reader': RubyClassmember, 'attr_writer': RubyClassmember, 'attr_accessor': RubyClassmember, 'module': RubyModule, 'currentmodule': RubyCurrentModule, } roles = { 'func': RubyXRefRole(fix_parens=False), 'global':RubyXRefRole(), 'class': RubyXRefRole(), 'exc': RubyXRefRole(), 'meth': RubyXRefRole(fix_parens=False), 'attr': RubyXRefRole(), 'const': RubyXRefRole(), 'mod': RubyXRefRole(), 'obj': RubyXRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, objtype 'modules': {}, # modname -> docname, synopsis, platform, deprecated } indices = [ RubyModuleIndex, ] def clear_doc(self, docname): for fullname, (fn, _) in list(self.data['objects'].items()): if fn == docname: del self.data['objects'][fullname] for modname, (fn, _, _, _) in list(self.data['modules'].items()): if fn == docname: del self.data['modules'][modname] def find_obj(self, env, modname, classname, name, type, searchorder=0): """ Find a Ruby object for "name", perhaps using the given module and/or classname. """ # skip parens if name[-2:] == '()': name = name[:-2] if not name: return None, None objects = self.data['objects'] newname = None if searchorder == 1: if modname and classname and \ modname + '::' + classname + '#' + name in objects: newname = modname + '::' + classname + '#' + name elif modname and classname and \ modname + '::' + classname + '.' + name in objects: newname = modname + '::' + classname + '.' + name elif modname and modname + '::' + name in objects: newname = modname + '::' + name elif modname and modname + '#' + name in objects: newname = modname + '#' + name elif modname and modname + '.' + name in objects: newname = modname + '.' + name elif classname and classname + '.' + name in objects: newname = classname + '.' + name elif classname and classname + '#' + name in objects: newname = classname + '#' + name elif name in objects: newname = name else: if name in objects: newname = name elif classname and classname + '.' + name in objects: newname = classname + '.' + name elif classname and classname + '#' + name in objects: newname = classname + '#' + name elif modname and modname + '::' + name in objects: newname = modname + '::' + name elif modname and modname + '#' + name in objects: newname = modname + '#' + name elif modname and modname + '.' + name in objects: newname = modname + '.' + name elif modname and classname and \ modname + '::' + classname + '#' + name in objects: newname = modname + '::' + classname + '#' + name elif modname and classname and \ modname + '::' + classname + '.' + name in objects: newname = modname + '::' + classname + '.' + name # special case: object methods elif type in ('func', 'meth') and '.' not in name and \ 'object.' + name in objects: newname = 'object.' + name if newname is None: return None, None return newname, objects[newname] def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): if (typ == 'mod' or typ == 'obj' and target in self.data['modules']): docname, synopsis, platform, deprecated = \ self.data['modules'].get(target, ('','','', '')) if not docname: return None else: title = '%s%s%s' % ((platform and '(%s) ' % platform), synopsis, (deprecated and ' (deprecated)' or '')) return make_refnode(builder, fromdocname, docname, 'module-' + target, contnode, title) else: modname = node.get('rb:module') clsname = node.get('rb:class') searchorder = node.hasattr('refspecific') and 1 or 0 name, obj = self.find_obj(env, modname, clsname, target, typ, searchorder) if not obj: return None else: return make_refnode(builder, fromdocname, obj[0], name, contnode, name) def get_objects(self): for modname, info in _iteritems(self.data['modules']): yield (modname, modname, 'module', info[0], 'module-' + modname, 0) for refname, (docname, type) in _iteritems(self.data['objects']): yield (refname, refname, type, docname, refname, 1) def setup(app): app.add_domain(RubyDomain) nghttp2-1.68.0/doc/_exts/rubydomain/PaxHeaders/__init__.py0000644000000000000000000000013115077107270020430 xustar0030 mtime=1761382072.963444278 29 atime=1761382106.79830781 30 ctime=1761382109.744298519 nghttp2-1.68.0/doc/_exts/rubydomain/__init__.py0000644000175100017510000000000015077107270021007 0ustar00runnerrunnernghttp2-1.68.0/doc/_exts/rubydomain/PaxHeaders/LICENSE.rubydomain0000644000000000000000000000013215077107270021475 xustar0030 mtime=1761382072.963444278 30 atime=1761382106.797307815 30 ctime=1761382109.742298525 nghttp2-1.68.0/doc/_exts/rubydomain/LICENSE.rubydomain0000644000175100017510000000261215077107270022066 0ustar00runnerrunnerIf not otherwise noted, the extensions in this package are licensed under the following license. Copyright (c) 2010 by the contributors (see AUTHORS file). All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nghttp2-1.68.0/doc/PaxHeaders/sources0000644000000000000000000000013215077107335014435 xustar0030 mtime=1761382109.741298528 30 atime=1761382109.794298375 30 ctime=1761382109.741298528 nghttp2-1.68.0/doc/sources/0000755000175100017510000000000015077107335015102 5ustar00runnerrunnernghttp2-1.68.0/doc/sources/PaxHeaders/tutorial-client.rst0000644000000000000000000000013215077107270020361 xustar0030 mtime=1761382072.967444259 30 atime=1761382106.788307854 30 ctime=1761382109.733298551 nghttp2-1.68.0/doc/sources/tutorial-client.rst0000644000175100017510000004513215077107270020756 0ustar00runnerrunnerTutorial: HTTP/2 client ========================= In this tutorial, we are going to write a very primitive HTTP/2 client. The complete source code, `libevent-client.c`_, is attached at the end of this page. It also resides in the examples directory in the archive or repository. This simple client takes a single HTTPS URI and retrieves the resource at the URI. The synopsis is: .. code-block:: text $ libevent-client HTTPS_URI We use libevent in this tutorial to handle networking I/O. Please note that nghttp2 itself does not depend on libevent. The client starts with some libevent and OpenSSL setup in the ``main()`` and ``run()`` functions. This setup isn't specific to nghttp2, but one thing you should look at is setup of ALPN. Client tells application protocols that it supports to server via ALPN:: static SSL_CTX *create_ssl_ctx(void) { SSL_CTX *ssl_ctx; ssl_ctx = SSL_CTX_new(SSLv23_client_method()); if (!ssl_ctx) { errx(1, "Could not create SSL/TLS context: %s", ERR_error_string(ERR_get_error(), NULL)); } SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); SSL_CTX_set_alpn_protos(ssl_ctx, (const unsigned char *)"\x02h2", 3); return ssl_ctx; } Here we see ``SSL_CTX_get_alpn_protos()`` function call. We instructs OpenSSL to notify the server that we support h2, ALPN identifier for HTTP/2. The example client defines a couple of structs: We define and use a ``http2_session_data`` structure to store data related to the HTTP/2 session:: typedef struct { nghttp2_session *session; struct evdns_base *dnsbase; struct bufferevent *bev; http2_stream_data *stream_data; } http2_session_data; Since this program only handles one URI, it uses only one stream. We store the single stream's data in a ``http2_stream_data`` structure and the ``stream_data`` points to it. The ``http2_stream_data`` structure is defined as follows:: typedef struct { /* The NULL-terminated URI string to retrieve. */ const char *uri; /* Parsed result of the |uri| */ urlparse_url *u; /* The authority portion of the |uri|, not NULL-terminated */ char *authority; /* The path portion of the |uri|, including query, not NULL-terminated */ char *path; /* The length of the |authority| */ size_t authoritylen; /* The length of the |path| */ size_t pathlen; /* The stream ID of this stream */ int32_t stream_id; } http2_stream_data; We create and initialize these structures in ``create_http2_session_data()`` and ``create_http2_stream_data()`` respectively. ``initiate_connection()`` is called to start the connection to the remote server. It's defined as:: static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx, const char *host, uint16_t port, http2_session_data *session_data) { int rv; struct bufferevent *bev; SSL *ssl; ssl = create_ssl(ssl_ctx); bev = bufferevent_openssl_socket_new( evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING, BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE); bufferevent_enable(bev, EV_READ | EV_WRITE); bufferevent_setcb(bev, readcb, writecb, eventcb, session_data); rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase, AF_UNSPEC, host, port); if (rv != 0) { errx(1, "Could not connect to the remote host %s", host); } session_data->bev = bev; } ``initiate_connection()`` creates a bufferevent for the connection and sets up three callbacks: ``readcb``, ``writecb``, and ``eventcb``. The ``eventcb()`` is invoked by the libevent event loop when an event (e.g. connection has been established, timeout, etc.) occurs on the underlying network socket:: static void eventcb(struct bufferevent *bev, short events, void *ptr) { http2_session_data *session_data = (http2_session_data *)ptr; if (events & BEV_EVENT_CONNECTED) { int fd = bufferevent_getfd(bev); int val = 1; const unsigned char *alpn = NULL; unsigned int alpnlen = 0; SSL *ssl; fprintf(stderr, "Connected\n"); ssl = bufferevent_openssl_get_ssl(session_data->bev); SSL_get0_alpn_selected(ssl, &alpn, &alpnlen); if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) { fprintf(stderr, "h2 is not negotiated\n"); delete_http2_session_data(session_data); return; } setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); initialize_nghttp2_session(session_data); send_client_connection_header(session_data); submit_request(session_data); if (session_send(session_data) != 0) { delete_http2_session_data(session_data); } return; } if (events & BEV_EVENT_EOF) { warnx("Disconnected from the remote host"); } else if (events & BEV_EVENT_ERROR) { warnx("Network error"); } else if (events & BEV_EVENT_TIMEOUT) { warnx("Timeout"); } delete_http2_session_data(session_data); } Here we validate that HTTP/2 is negotiated, and if not, drop connection. For ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR``, and ``BEV_EVENT_TIMEOUT`` events, we just simply tear down the connection. The ``BEV_EVENT_CONNECTED`` event is invoked when the SSL/TLS handshake has completed successfully. After this we're ready to begin communicating via HTTP/2. The ``initialize_nghttp2_session()`` function initializes the nghttp2 session object and several callbacks:: static void initialize_nghttp2_session(http2_session_data *session_data) { nghttp2_session_callbacks *callbacks; nghttp2_session_callbacks_new(&callbacks); nghttp2_session_callbacks_set_send_callback2(callbacks, send_callback); nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, on_frame_recv_callback); nghttp2_session_callbacks_set_on_data_chunk_recv_callback( callbacks, on_data_chunk_recv_callback); nghttp2_session_callbacks_set_on_stream_close_callback( callbacks, on_stream_close_callback); nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header_callback); nghttp2_session_callbacks_set_on_begin_headers_callback( callbacks, on_begin_headers_callback); nghttp2_session_client_new(&session_data->session, callbacks, session_data); nghttp2_session_callbacks_del(callbacks); } Since we are creating a client, we use `nghttp2_session_client_new()` to initialize the nghttp2 session object. The callbacks setup are explained later. The `delete_http2_session_data()` function destroys ``session_data`` and frees its bufferevent, so the underlying connection is closed. It also calls `nghttp2_session_del()` to delete the nghttp2 session object. A HTTP/2 connection begins by sending the client connection preface, which is a 24 byte magic byte string (:macro:`NGHTTP2_CLIENT_MAGIC`), followed by a SETTINGS frame. The 24 byte magic string is sent automatically by nghttp2. We send the SETTINGS frame in ``send_client_connection_header()``:: static void send_client_connection_header(http2_session_data *session_data) { nghttp2_settings_entry iv[1] = { {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; int rv; /* client 24 bytes magic string will be sent by nghttp2 library */ rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv, ARRLEN(iv)); if (rv != 0) { errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv)); } } Here we specify SETTINGS_MAX_CONCURRENT_STREAMS as 100. This is not needed for this tiny example program, it just demonstrates use of the SETTINGS frame. To queue the SETTINGS frame for transmission, we call `nghttp2_submit_settings()`. Note that `nghttp2_submit_settings()` only queues the frame for transmission, and doesn't actually send it. All ``nghttp2_submit_*()`` family functions have this property. To actually send the frame, `nghttp2_session_send()` has to be called, which is described (and called) later. After the transmission of the client connection header, we enqueue the HTTP request in the ``submit_request()`` function:: static void submit_request(http2_session_data *session_data) { int32_t stream_id; http2_stream_data *stream_data = session_data->stream_data; const char *uri = stream_data->uri; const urlparse_url *u = stream_data->u; nghttp2_nv hdrs[] = { MAKE_NV2(":method", "GET"), MAKE_NV(":scheme", &uri[u->field_data[URLPARSE_SCHEMA].off], u->field_data[URLPARSE_SCHEMA].len), MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen), MAKE_NV(":path", stream_data->path, stream_data->pathlen)}; fprintf(stderr, "Request headers:\n"); print_headers(stderr, hdrs, ARRLEN(hdrs)); stream_id = nghttp2_submit_request2(session_data->session, NULL, hdrs, ARRLEN(hdrs), NULL, stream_data); if (stream_id < 0) { errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id)); } stream_data->stream_id = stream_id; } We build the HTTP request header fields in ``hdrs``, which is an array of :type:`nghttp2_nv`. There are four header fields to be sent: ``:method``, ``:scheme``, ``:authority``, and ``:path``. To queue the HTTP request, we call `nghttp2_submit_request2()`. The ``stream_data`` is passed via the *stream_user_data* parameter, which is helpfully later passed back to callback functions. `nghttp2_submit_request2()` returns the newly assigned stream ID for the request. The next bufferevent callback is ``readcb()``, which is invoked when data is available to read from the bufferevent input buffer:: static void readcb(struct bufferevent *bev, void *ptr) { http2_session_data *session_data = (http2_session_data *)ptr; nghttp2_ssize readlen; struct evbuffer *input = bufferevent_get_input(bev); size_t datalen = evbuffer_get_length(input); unsigned char *data = evbuffer_pullup(input, -1); readlen = nghttp2_session_mem_recv2(session_data->session, data, datalen); if (readlen < 0) { warnx("Fatal error: %s", nghttp2_strerror((int)readlen)); delete_http2_session_data(session_data); return; } if (evbuffer_drain(input, (size_t)readlen) != 0) { warnx("Fatal error: evbuffer_drain failed"); delete_http2_session_data(session_data); return; } if (session_send(session_data) != 0) { delete_http2_session_data(session_data); return; } } In this function we feed all unprocessed, received data to the nghttp2 session object using the `nghttp2_session_mem_recv2()` function. `nghttp2_session_mem_recv2()` processes the received data and may invoke nghttp2 callbacks and queue frames for transmission. Since there may be pending frames for transmission, we call immediately ``session_send()`` to send them. ``session_send()`` is defined as follows:: static int session_send(http2_session_data *session_data) { int rv; rv = nghttp2_session_send(session_data->session); if (rv != 0) { warnx("Fatal error: %s", nghttp2_strerror(rv)); return -1; } return 0; } The `nghttp2_session_send()` function serializes pending frames into wire format and calls the ``send_callback()`` function to send them. ``send_callback()`` has type :type:`nghttp2_send_callback2` and is defined as:: static nghttp2_ssize send_callback(nghttp2_session *session _U_, const uint8_t *data, size_t length, int flags _U_, void *user_data) { http2_session_data *session_data = (http2_session_data *)user_data; struct bufferevent *bev = session_data->bev; bufferevent_write(bev, data, length); return (nghttp2_ssize)length; } Since we use bufferevent to abstract network I/O, we just write the data to the bufferevent object. Note that `nghttp2_session_send()` continues to write all frames queued so far. If we were writing the data to the non-blocking socket directly using the ``write()`` system call, we'd soon receive an ``EAGAIN`` or ``EWOULDBLOCK`` error, since sockets have a limited send buffer. If that happens, it's possible to return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the nghttp2 library to stop sending further data. When writing to a bufferevent, you should regulate the amount of data written, to avoid possible huge memory consumption. In this example client however we don't implement a limit. To see how to regulate the amount of buffered data, see the ``send_callback()`` in the server tutorial. The third bufferevent callback is ``writecb()``, which is invoked when all data written in the bufferevent output buffer has been sent:: static void writecb(struct bufferevent *bev _U_, void *ptr) { http2_session_data *session_data = (http2_session_data *)ptr; if (nghttp2_session_want_read(session_data->session) == 0 && nghttp2_session_want_write(session_data->session) == 0 && evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) { delete_http2_session_data(session_data); } } As described earlier, we just write off all data in `send_callback()`, so there is no data to write in this function. All we have to do is check if the connection should be dropped or not. The nghttp2 session object keeps track of reception and transmission of GOAWAY frames and other error conditions. Using this information, the nghttp2 session object can state whether the connection should be dropped or not. More specifically, when both `nghttp2_session_want_read()` and `nghttp2_session_want_write()` return 0, the connection is no-longer required and can be closed. Since we're using bufferevent and its deferred callback option, the bufferevent output buffer may still contain pending data when the ``writecb()`` is called. To handle this situation, we also check whether the output buffer is empty or not. If all of these conditions are met, then we drop the connection. Now let's look at the remaining nghttp2 callbacks setup in the ``initialize_nghttp2_setup()`` function. A server responds to the request by first sending a HEADERS frame. The HEADERS frame consists of response header name/value pairs, and the ``on_header_callback()`` is called for each name/value pair:: static int on_header_callback(nghttp2_session *session _U_, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags _U_, void *user_data) { http2_session_data *session_data = (http2_session_data *)user_data; switch (frame->hd.type) { case NGHTTP2_HEADERS: if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && session_data->stream_data->stream_id == frame->hd.stream_id) { /* Print response headers for the initiated request. */ print_header(stderr, name, namelen, value, valuelen); break; } } return 0; } In this tutorial, we just print the name/value pairs on stderr. After the HEADERS frame has been fully received (and thus all response header name/value pairs have been received), the ``on_frame_recv_callback()`` function is called:: static int on_frame_recv_callback(nghttp2_session *session _U_, const nghttp2_frame *frame, void *user_data) { http2_session_data *session_data = (http2_session_data *)user_data; switch (frame->hd.type) { case NGHTTP2_HEADERS: if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && session_data->stream_data->stream_id == frame->hd.stream_id) { fprintf(stderr, "All headers received\n"); } break; } return 0; } ``on_frame_recv_callback()`` is called for other frame types too. In this tutorial, we are just interested in the HTTP response HEADERS frame. We check the frame type and its category (it should be :macro:`NGHTTP2_HCAT_RESPONSE` for HTTP response HEADERS). We also check its stream ID. Next, zero or more DATA frames can be received. The ``on_data_chunk_recv_callback()`` function is invoked when a chunk of data is received from the remote peer:: static int on_data_chunk_recv_callback(nghttp2_session *session _U_, uint8_t flags _U_, int32_t stream_id, const uint8_t *data, size_t len, void *user_data) { http2_session_data *session_data = (http2_session_data *)user_data; if (session_data->stream_data->stream_id == stream_id) { fwrite(data, len, 1, stdout); } return 0; } In our case, a chunk of data is HTTP response body. After checking the stream ID, we just write the received data to stdout. Note the output in the terminal may be corrupted if the response body contains some binary data. The ``on_stream_close_callback()`` function is invoked when the stream is about to close:: static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, nghttp2_error_code error_code, void *user_data) { http2_session_data *session_data = (http2_session_data *)user_data; int rv; if (session_data->stream_data->stream_id == stream_id) { fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id, error_code); rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); if (rv != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } } return 0; } If the stream ID matches the one we initiated, it means that its stream is going to be closed. Since we have finished receiving resource we wanted (or the stream was reset by RST_STREAM from the remote peer), we call `nghttp2_session_terminate_session()` to commence closure of the HTTP/2 session gracefully. If you have some data associated for the stream to be closed, you may delete it here. nghttp2-1.68.0/doc/sources/PaxHeaders/tutorial-hpack.rst0000644000000000000000000000013215077107270020171 xustar0030 mtime=1761382072.967444259 30 atime=1761382106.790307845 30 ctime=1761382109.736298543 nghttp2-1.68.0/doc/sources/tutorial-hpack.rst0000644000175100017510000001307015077107270020562 0ustar00runnerrunnerTutorial: HPACK API =================== In this tutorial, we describe basic use of nghttp2's HPACK API. We briefly describe the APIs for deflating and inflating header fields. The full example of using these APIs, `deflate.c`_, is attached at the end of this page. It also resides in the examples directory in the archive or repository. Deflating (encoding) headers ---------------------------- First we need to initialize a :type:`nghttp2_hd_deflater` object using the `nghttp2_hd_deflate_new()` function:: int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, size_t deflate_hd_table_bufsize_max); This function allocates a :type:`nghttp2_hd_deflater` object, initializes it, and assigns its pointer to ``*deflater_ptr``. The *deflate_hd_table_bufsize_max* is the upper bound of header table size the deflater will use. This will limit the memory usage by the deflater object for the dynamic header table. If in doubt, just specify 4096 here, which is the default upper bound of dynamic header table buffer size. To encode header fields, use the `nghttp2_hd_deflate_hd2()` function:: nghttp2_ssize nghttp2_hd_deflate_hd2(nghttp2_hd_deflater *deflater, uint8_t *buf, size_t buflen, const nghttp2_nv *nva, size_t nvlen); The *deflater* is the deflater object initialized by `nghttp2_hd_deflate_new()` described above. The encoded byte string is written to the buffer *buf*, which has length *buflen*. The *nva* is a pointer to an array of headers fields, each of type :type:`nghttp2_nv`. *nvlen* is the number of header fields which *nva* contains. It is important to initialize and assign all members of :type:`nghttp2_nv`. For security sensitive header fields (such as cookies), set the :macro:`NGHTTP2_NV_FLAG_NO_INDEX` flag in :member:`nghttp2_nv.flags`. Setting this flag prevents recovery of sensitive header fields by compression based attacks: This is achieved by not inserting the header field into the dynamic header table. `nghttp2_hd_deflate_hd2()` processes all headers given in *nva*. The *nva* must include all request or response header fields to be sent in one HEADERS (or optionally following (multiple) CONTINUATION frame(s)). The *buf* must have enough space to store the encoded result, otherwise the function will fail. To estimate the upper bound of the encoded result length, use `nghttp2_hd_deflate_bound()`:: size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, const nghttp2_nv *nva, size_t nvlen); Pass this function the same parameters (*deflater*, *nva*, and *nvlen*) which will be passed to `nghttp2_hd_deflate_hd2()`. Subsequent calls to `nghttp2_hd_deflate_hd2()` will use the current encoder state and perform differential encoding, which yields HPAC's fundamental compression gain. If `nghttp2_hd_deflate_hd2()` fails, the failure is fatal and any further calls with the same deflater object will fail. Thus it's very important to use `nghttp2_hd_deflate_bound()` to determine the required size of the output buffer. To delete a :type:`nghttp2_hd_deflater` object, use the `nghttp2_hd_deflate_del()` function. Inflating (decoding) headers ---------------------------- A :type:`nghttp2_hd_inflater` object is used to inflate compressed header data. To initialize the object, use `nghttp2_hd_inflate_new()`:: int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr); To inflate header data, use `nghttp2_hd_inflate_hd3()`:: nghttp2_ssize nghttp2_hd_inflate_hd3(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags, const uint8_t *in, size_t inlen, int in_final); `nghttp2_hd_inflate_hd3()` reads a stream of bytes and outputs a single header field at a time. Multiple calls are normally required to read a full stream of bytes and output all of the header fields. The *inflater* is the inflater object initialized above. The *nv_out* is a pointer to a :type:`nghttp2_nv` into which one header field may be stored. The *in* is a pointer to input data, and *inlen* is its length. The caller is not required to specify the whole deflated header data via *in* at once: Instead it can call this function multiple times as additional data bytes become available. If *in_final* is nonzero, it tells the function that the passed data is the final sequence of deflated header data. The *inflate_flags* is an output parameter; on success the function sets it to a bitset of flags. It will be described later. This function returns when each header field is inflated. When this happens, the function sets the :macro:`NGHTTP2_HD_INFLATE_EMIT` flag in *inflate_flags*, and a header field is stored in *nv_out*. The return value indicates the number of bytes read from *in* processed so far, which may be less than *inlen*. The caller should call the function repeatedly until all bytes are processed. Processed bytes should be removed from *in*, and *inlen* should be adjusted appropriately. If *in_final* is nonzero and all given data was processed, the function sets the :macro:`NGHTTP2_HD_INFLATE_FINAL` flag in *inflate_flags*. When you see this flag set, call the `nghttp2_hd_inflate_end_headers()` function. If *in_final* is zero and the :macro:`NGHTTP2_HD_INFLATE_EMIT` flag is not set, it indicates that all given data was processed. The caller is required to pass additional data. Example usage of `nghttp2_hd_inflate_hd3()` is shown in the `inflate_header_block()` function in `deflate.c`_. Finally, to delete a :type:`nghttp2_hd_inflater` object, use `nghttp2_hd_inflate_del()`. nghttp2-1.68.0/doc/sources/PaxHeaders/tutorial-server.rst0000644000000000000000000000013115077107270020410 xustar0030 mtime=1761382072.967444259 29 atime=1761382106.78930785 30 ctime=1761382109.735298545 nghttp2-1.68.0/doc/sources/tutorial-server.rst0000644000175100017510000005407015077107270021007 0ustar00runnerrunnerTutorial: HTTP/2 server ========================= In this tutorial, we are going to write a single-threaded, event-based HTTP/2 web server, which supports HTTPS only. It can handle concurrent multiple requests, but only the GET method is supported. The complete source code, `libevent-server.c`_, is attached at the end of this page. The source also resides in the examples directory in the archive or repository. This simple server takes 3 arguments: The port number to listen on, the path to your SSL/TLS private key file, and the path to your certificate file. The synopsis is: .. code-block:: text $ libevent-server PORT /path/to/server.key /path/to/server.crt We use libevent in this tutorial to handle networking I/O. Please note that nghttp2 itself does not depend on libevent. The server starts with some libevent and OpenSSL setup in the ``main()`` and ``run()`` functions. This setup isn't specific to nghttp2, but one thing you should look at is setup of ALPN callback. The ALPN callback is used by the server to select application protocols offered by client. In ALPN, client sends the list of supported application protocols, and server selects one of them. We provide the callback for it:: static int alpn_select_proto_cb(SSL *ssl _U_, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg _U_) { int rv; rv = nghttp2_select_alpn(out, outlen, in, inlen); if (rv != 1) { return SSL_TLSEXT_ERR_NOACK; } return SSL_TLSEXT_ERR_OK; } static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) { SSL_CTX *ssl_ctx; EC_KEY *ecdh; ssl_ctx = SSL_CTX_new(SSLv23_server_method()); ... SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, NULL); return ssl_ctx; } In ``alpn_select_proto_cb()``, we use `nghttp2_select_alpn()` to select application protocol. The `nghttp2_select_alpn()` returns 1 only if it selected h2 (ALPN identifier for HTTP/2), and out parameters were assigned accordingly. Next, let's take a look at the main structures used by the example application: We use the ``app_context`` structure to store application-wide data:: struct app_context { SSL_CTX *ssl_ctx; struct event_base *evbase; }; We use the ``http2_session_data`` structure to store session-level (which corresponds to one HTTP/2 connection) data:: typedef struct http2_session_data { struct http2_stream_data root; struct bufferevent *bev; app_context *app_ctx; nghttp2_session *session; char *client_addr; } http2_session_data; We use the ``http2_stream_data`` structure to store stream-level data:: typedef struct http2_stream_data { struct http2_stream_data *prev, *next; char *request_path; int32_t stream_id; int fd; } http2_stream_data; A single HTTP/2 session can have multiple streams. To manage them, we use a doubly linked list: The first element of this list is pointed to by the ``root->next`` in ``http2_session_data``. Initially, ``root->next`` is ``NULL``. libevent's bufferevent structure is used to perform network I/O, with the pointer to the bufferevent stored in the ``http2_session_data`` structure. Note that the bufferevent object is kept in ``http2_session_data`` and not in ``http2_stream_data``. This is because ``http2_stream_data`` is just a logical stream multiplexed over the single connection managed by the bufferevent in ``http2_session_data``. We first create a listener object to accept incoming connections. libevent's ``struct evconnlistener`` is used for this purpose:: static void start_listen(struct event_base *evbase, const char *service, app_context *app_ctx) { int rv; struct addrinfo hints; struct addrinfo *res, *rp; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; #ifdef AI_ADDRCONFIG hints.ai_flags |= AI_ADDRCONFIG; #endif /* AI_ADDRCONFIG */ rv = getaddrinfo(NULL, service, &hints, &res); if (rv != 0) { errx(1, NULL); } for (rp = res; rp; rp = rp->ai_next) { struct evconnlistener *listener; listener = evconnlistener_new_bind( evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 16, rp->ai_addr, (int)rp->ai_addrlen); if (listener) { freeaddrinfo(res); return; } } errx(1, "Could not start listener"); } We specify the ``acceptcb`` callback, which is called when a new connection is accepted:: static void acceptcb(struct evconnlistener *listener _U_, int fd, struct sockaddr *addr, int addrlen, void *arg) { app_context *app_ctx = (app_context *)arg; http2_session_data *session_data; session_data = create_http2_session_data(app_ctx, fd, addr, addrlen); bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data); } Here we create the ``http2_session_data`` object. The connection's bufferevent is initialized at the same time. We specify three callbacks for the bufferevent: ``readcb``, ``writecb``, and ``eventcb``. The ``eventcb()`` callback is invoked by the libevent event loop when an event (e.g. connection has been established, timeout, etc.) occurs on the underlying network socket:: static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) { http2_session_data *session_data = (http2_session_data *)ptr; if (events & BEV_EVENT_CONNECTED) { const unsigned char *alpn = NULL; unsigned int alpnlen = 0; SSL *ssl; fprintf(stderr, "%s connected\n", session_data->client_addr); ssl = bufferevent_openssl_get_ssl(session_data->bev); SSL_get0_alpn_selected(ssl, &alpn, &alpnlen); if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) { fprintf(stderr, "%s h2 is not negotiated\n", session_data->client_addr); delete_http2_session_data(session_data); return; } initialize_nghttp2_session(session_data); if (send_server_connection_header(session_data) != 0 || session_send(session_data) != 0) { delete_http2_session_data(session_data); return; } return; } if (events & BEV_EVENT_EOF) { fprintf(stderr, "%s EOF\n", session_data->client_addr); } else if (events & BEV_EVENT_ERROR) { fprintf(stderr, "%s network error\n", session_data->client_addr); } else if (events & BEV_EVENT_TIMEOUT) { fprintf(stderr, "%s timeout\n", session_data->client_addr); } delete_http2_session_data(session_data); } Here we validate that HTTP/2 is negotiated, and if not, drop connection. For the ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR``, and ``BEV_EVENT_TIMEOUT`` events, we just simply tear down the connection. The ``delete_http2_session_data()`` function destroys the ``http2_session_data`` object and its associated bufferevent member. As a result, the underlying connection is closed. The ``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake has completed successfully. After this we are ready to begin communicating via HTTP/2. The ``initialize_nghttp2_session()`` function initializes the nghttp2 session object and several callbacks:: static void initialize_nghttp2_session(http2_session_data *session_data) { nghttp2_session_callbacks *callbacks; nghttp2_session_callbacks_new(&callbacks); nghttp2_session_callbacks_set_send_callback2(callbacks, send_callback); nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, on_frame_recv_callback); nghttp2_session_callbacks_set_on_stream_close_callback( callbacks, on_stream_close_callback); nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header_callback); nghttp2_session_callbacks_set_on_begin_headers_callback( callbacks, on_begin_headers_callback); nghttp2_session_server_new(&session_data->session, callbacks, session_data); nghttp2_session_callbacks_del(callbacks); } Since we are creating a server, we use `nghttp2_session_server_new()` to initialize the nghttp2 session object. We also setup 5 callbacks for the nghttp2 session, these are explained later. The server now begins by sending the server connection preface, which always consists of a SETTINGS frame. ``send_server_connection_header()`` configures and submits it:: static int send_server_connection_header(http2_session_data *session_data) { nghttp2_settings_entry iv[1] = { {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; int rv; rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv, ARRLEN(iv)); if (rv != 0) { warnx("Fatal error: %s", nghttp2_strerror(rv)); return -1; } return 0; } In the example SETTINGS frame we've set SETTINGS_MAX_CONCURRENT_STREAMS to 100. `nghttp2_submit_settings()` is used to queue the frame for transmission, but note it only queues the frame for transmission, and doesn't actually send it. All functions in the ``nghttp2_submit_*()`` family have this property. To actually send the frame, `nghttp2_session_send()` should be used, as described later. Since bufferevent may buffer more than the first 24 bytes from the client, we have to process them here since libevent won't invoke callback functions for this pending data. To process the received data, we call the ``session_recv()`` function:: static int session_recv(http2_session_data *session_data) { nghttp2_ssize readlen; struct evbuffer *input = bufferevent_get_input(session_data->bev); size_t datalen = evbuffer_get_length(input); unsigned char *data = evbuffer_pullup(input, -1); readlen = nghttp2_session_mem_recv2(session_data->session, data, datalen); if (readlen < 0) { warnx("Fatal error: %s", nghttp2_strerror((int)readlen)); return -1; } if (evbuffer_drain(input, (size_t)readlen) != 0) { warnx("Fatal error: evbuffer_drain failed"); return -1; } if (session_send(session_data) != 0) { return -1; } return 0; } In this function, we feed all unprocessed but already received data to the nghttp2 session object using the `nghttp2_session_mem_recv2()` function. The `nghttp2_session_mem_recv2()` function processes the data and may both invoke the previously setup callbacks and also queue outgoing frames. To send any pending outgoing frames, we immediately call ``session_send()``. The ``session_send()`` function is defined as follows:: static int session_send(http2_session_data *session_data) { int rv; rv = nghttp2_session_send(session_data->session); if (rv != 0) { warnx("Fatal error: %s", nghttp2_strerror(rv)); return -1; } return 0; } The `nghttp2_session_send()` function serializes the frame into wire format and calls the ``send_callback()``, which is of type :type:`nghttp2_send_callback2`. The ``send_callback()`` is defined as follows:: static nghttp2_ssize send_callback(nghttp2_session *session _U_, const uint8_t *data, size_t length, int flags _U_, void *user_data) { http2_session_data *session_data = (http2_session_data *)user_data; struct bufferevent *bev = session_data->bev; /* Avoid excessive buffering in server side. */ if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >= OUTPUT_WOULDBLOCK_THRESHOLD) { return NGHTTP2_ERR_WOULDBLOCK; } bufferevent_write(bev, data, length); return (nghttp2_ssize)length; } Since we use bufferevent to abstract network I/O, we just write the data to the bufferevent object. Note that `nghttp2_session_send()` continues to write all frames queued so far. If we were writing the data to a non-blocking socket directly using the ``write()`` system call in the ``send_callback()``, we'd soon receive an ``EAGAIN`` or ``EWOULDBLOCK`` error since sockets have a limited send buffer. If that happens, it's possible to return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the nghttp2 library to stop sending further data. But here, when writing to the bufferevent, we have to regulate the amount data to buffered ourselves to avoid using huge amounts of memory. To achieve this, we check the size of the output buffer and if it reaches more than or equal to ``OUTPUT_WOULDBLOCK_THRESHOLD`` bytes, we stop writing data and return :macro:`NGHTTP2_ERR_WOULDBLOCK`. The next bufferevent callback is ``readcb()``, which is invoked when data is available to read in the bufferevent input buffer:: static void readcb(struct bufferevent *bev _U_, void *ptr) { http2_session_data *session_data = (http2_session_data *)ptr; if (session_recv(session_data) != 0) { delete_http2_session_data(session_data); return; } } In this function, we just call ``session_recv()`` to process incoming data. The third bufferevent callback is ``writecb()``, which is invoked when all data in the bufferevent output buffer has been sent:: static void writecb(struct bufferevent *bev, void *ptr) { http2_session_data *session_data = (http2_session_data *)ptr; if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) { return; } if (nghttp2_session_want_read(session_data->session) == 0 && nghttp2_session_want_write(session_data->session) == 0) { delete_http2_session_data(session_data); return; } if (session_send(session_data) != 0) { delete_http2_session_data(session_data); return; } } First we check whether we should drop the connection or not. The nghttp2 session object keeps track of reception and transmission of GOAWAY frames and other error conditions as well. Using this information, the nghttp2 session object can state whether the connection should be dropped or not. More specifically, if both `nghttp2_session_want_read()` and `nghttp2_session_want_write()` return 0, the connection is no-longer required and can be closed. Since we are using bufferevent and its deferred callback option, the bufferevent output buffer may still contain pending data when the ``writecb()`` is called. To handle this, we check whether the output buffer is empty or not. If all of these conditions are met, we drop connection. Otherwise, we call ``session_send()`` to process the pending output data. Remember that in ``send_callback()``, we must not write all data to bufferevent to avoid excessive buffering. We continue processing pending data when the output buffer becomes empty. We have already described the nghttp2 callback ``send_callback()``. Let's learn about the remaining nghttp2 callbacks setup in ``initialize_nghttp2_setup()`` function. The ``on_begin_headers_callback()`` function is invoked when the reception of a header block in HEADERS or PUSH_PROMISE frame is started:: static int on_begin_headers_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { http2_session_data *session_data = (http2_session_data *)user_data; http2_stream_data *stream_data; if (frame->hd.type != NGHTTP2_HEADERS || frame->headers.cat != NGHTTP2_HCAT_REQUEST) { return 0; } stream_data = create_http2_stream_data(session_data, frame->hd.stream_id); nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, stream_data); return 0; } We are only interested in the HEADERS frame in this function. Since the HEADERS frame has several roles in the HTTP/2 protocol, we check that it is a request HEADERS, which opens new stream. If the frame is a request HEADERS, we create a ``http2_stream_data`` object to store the stream related data. We associate the created ``http2_stream_data`` object with the stream in the nghttp2 session object using `nghttp2_set_stream_user_data()`. The ``http2_stream_data`` object can later be easily retrieved from the stream, without searching through the doubly linked list. In this example server, we want to serve files relative to the current working directory in which the program was invoked. Each header name/value pair is emitted via ``on_header_callback`` function, which is called after ``on_begin_headers_callback()``:: static int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags _U_, void *user_data _U_) { http2_stream_data *stream_data; const char PATH[] = ":path"; switch (frame->hd.type) { case NGHTTP2_HEADERS: if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) { break; } stream_data = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); if (!stream_data || stream_data->request_path) { break; } if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) { size_t j; for (j = 0; j < valuelen && value[j] != '?'; ++j) ; stream_data->request_path = percent_decode(value, j); } break; } return 0; } We search for the ``:path`` header field among the request headers and store the requested path in the ``http2_stream_data`` object. In this example program, we ignore the ``:method`` header field and always treat the request as a GET request. The ``on_frame_recv_callback()`` function is invoked when a frame is fully received:: static int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { http2_session_data *session_data = (http2_session_data *)user_data; http2_stream_data *stream_data; switch (frame->hd.type) { case NGHTTP2_DATA: case NGHTTP2_HEADERS: /* Check that the client request has finished */ if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { stream_data = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); /* For DATA and HEADERS frame, this callback may be called after on_stream_close_callback. Check that stream still alive. */ if (!stream_data) { return 0; } return on_request_recv(session, session_data, stream_data); } break; default: break; } return 0; } First we retrieve the ``http2_stream_data`` object associated with the stream in ``on_begin_headers_callback()`` using `nghttp2_session_get_stream_user_data()`. If the requested path cannot be served for some reason (e.g. file is not found), we send a 404 response using ``error_reply()``. Otherwise, we open the requested file and send its content. We send the header field ``:status`` as a single response header. Sending the file content is performed by the ``send_response()`` function:: static int send_response(nghttp2_session *session, int32_t stream_id, nghttp2_nv *nva, size_t nvlen, int fd) { int rv; nghttp2_data_provider2 data_prd; data_prd.source.fd = fd; data_prd.read_callback = file_read_callback; rv = nghttp2_submit_response2(session, stream_id, nva, nvlen, &data_prd); if (rv != 0) { warnx("Fatal error: %s", nghttp2_strerror(rv)); return -1; } return 0; } nghttp2 uses the :type:`nghttp2_data_provider2` structure to send the entity body to the remote peer. The ``source`` member of this structure is a union, which can be either a void pointer or an int (which is intended to be used as file descriptor). In this example server, we use it as a file descriptor. We also set the ``file_read_callback()`` callback function to read the contents of the file:: static nghttp2_ssize file_read_callback(nghttp2_session *session _U_, int32_t stream_id _U_, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *user_data _U_) { int fd = source->fd; ssize_t r; while ((r = read(fd, buf, length)) == -1 && errno == EINTR) ; if (r == -1) { return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } if (r == 0) { *data_flags |= NGHTTP2_DATA_FLAG_EOF; } return (nghttp2_ssize)r; } If an error occurs while reading the file, we return :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. This tells the library to send RST_STREAM to the stream. When all data has been read, the :macro:`NGHTTP2_DATA_FLAG_EOF` flag is set to signal nghttp2 that we have finished reading the file. The `nghttp2_submit_response2()` function is used to send the response to the remote peer. The ``on_stream_close_callback()`` function is invoked when the stream is about to close:: static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, uint32_t error_code _U_, void *user_data) { http2_session_data *session_data = (http2_session_data *)user_data; http2_stream_data *stream_data; stream_data = nghttp2_session_get_stream_user_data(session, stream_id); if (!stream_data) { return 0; } remove_stream(session_data, stream_data); delete_http2_stream_data(stream_data); return 0; } Lastly, we destroy the ``http2_stream_data`` object in this function, since the stream is about to close and we no longer need the object. nghttp2-1.68.0/doc/sources/PaxHeaders/building-android-binary.rst0000644000000000000000000000013215077107270021737 xustar0030 mtime=1761382072.966444264 30 atime=1761382106.794307828 30 ctime=1761382109.740298531 nghttp2-1.68.0/doc/sources/building-android-binary.rst0000644000175100017510000000627215077107270022336 0ustar00runnerrunnerBuilding Android binary ======================= In this article, we briefly describe how to build Android binary using `Android NDK `_ cross-compiler on Debian Linux. The easiest way to build android binary is use Dockerfile.android. See Dockerfile.android for more details. If you cannot use Dockerfile.android for whatever reason, continue to read the rest of this article. We offer ``android-config`` script to make the build easier. To make the script work, NDK directory must be set to ``NDK`` environment variable. NDK directory is the directory where NDK is unpacked: .. code-block:: text $ unzip android-ndk-$NDK_VERSION-linux.zip $ cd android-ndk-$NDK_VERSION $ export NDK=$PWD The dependent libraries, such as OpenSSL, libev, and c-ares should be built with the same NDK toolchain and installed under ``$NDK/usr/local``. We recommend to build these libraries as static library to make the deployment easier. libxml2 support is currently disabled. Although zlib comes with Android NDK, it seems not to be a part of public API, so we have to built it for our own. That also provides us proper .pc file as a bonus. Before running ``android-config``, ``NDK`` environment variable must be set to point to the correct path. You need to set ``NGHTTP2`` environment variable to the absolute path to the source directory of nghttp2. To configure OpenSSL, use the following script: .. code-block:: sh #!/bin/sh . $NGHTTP2/android-env export ANDROID_NDK_HOME=$NDK export PATH=$TOOLCHAIN/bin:$PATH ./Configure no-shared --prefix=$PREFIX android-arm64 And run the following script to build and install without documentation: .. code-block:: sh #!/bin/sh . $NGHTTP2/android-env export PATH=$TOOLCHAIN/bin:$PATH make install_sw To configure libev, use the following script: .. code-block:: sh #!/bin/sh . $NGHTTP2/android-env ./configure \ --host=$TARGET \ --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ --prefix=$PREFIX \ --disable-shared \ --enable-static \ CPPFLAGS=-I$PREFIX/include \ LDFLAGS=-L$PREFIX/lib And run ``make install`` to build and install. To configure c-ares, use the following script: .. code-block:: sh #!/bin/sh -e . $NGHTTP2/android-env ./configure \ --host=$TARGET \ --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ --prefix=$PREFIX \ --disable-shared And run ``make install`` to build and install. To configure zlib, use the following script: .. code-block:: sh #!/bin/sh -e . $NGHTTP2/android-env export HOST=$TARGET ./configure \ --prefix=$PREFIX \ --libdir=$PREFIX/lib \ --includedir=$PREFIX/include \ --static And run ``make install`` to build and install. After prerequisite libraries are prepared, run ``android-config`` and then ``make`` to compile nghttp2 source files. If all went well, application binaries, such as nghttpx, are created under src directory. Strip debugging information from the binary using the following command: .. code-block:: text $ $NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip src/nghttpx nghttp2-1.68.0/doc/sources/PaxHeaders/h2load-howto.rst0000644000000000000000000000013215077107270017551 xustar0030 mtime=1761382072.966444264 30 atime=1761382106.793307832 30 ctime=1761382109.738298537 nghttp2-1.68.0/doc/sources/h2load-howto.rst0000644000175100017510000001127015077107270020142 0ustar00runnerrunner.. program:: h2load h2load - HTTP/2 benchmarking tool - HOW-TO ========================================== :doc:`h2load.1` is benchmarking tool for HTTP/2 and HTTP/1.1. It supports SSL/TLS and clear text for all supported protocols. Compiling from source --------------------- h2load is compiled alongside nghttp2 and requires that the ``--enable-app`` flag is passed to ``./configure`` and `required dependencies `_ are available during compilation. For details on compiling, see `nghttp2: Building from Git `_. Basic Usage ----------- In order to set benchmark settings, specify following 3 options. :option:`-n` The number of total requests. Default: 1 :option:`-c` The number of concurrent clients. Default: 1 :option:`-m` The max concurrent streams to issue per client. Default: 1 For SSL/TLS connection, the protocol will be negotiated via ALPN. You can set specific protocols in :option:`--alpn-list` option. For cleartext connection, the default protocol is HTTP/2. To change the protocol in cleartext connection, use :option:`--no-tls-proto` option. For convenience, :option:`--h1` option forces HTTP/1.1 for both cleartext and SSL/TLS connections. Here is a command-line to perform benchmark to URI \https://localhost using total 100000 requests, 100 concurrent clients and 10 max concurrent streams: .. code-block:: text $ h2load -n100000 -c100 -m10 https://localhost The benchmarking result looks like this: .. code-block:: text finished in 7.08s, 141164.80 req/s, 555.33MB/s requests: 1000000 total, 1000000 started, 1000000 done, 1000000 succeeded, 0 failed, 0 errored, 0 timeout status codes: 1000000 2xx, 0 3xx, 0 4xx, 0 5xx traffic: 4125025824 bytes total, 11023424 bytes headers (space savings 93.07%), 4096000000 bytes data min max mean sd +/- sd time for request: 15.31ms 146.85ms 69.78ms 9.26ms 92.43% time for connect: 1.08ms 25.04ms 10.71ms 9.80ms 64.00% time to 1st byte: 25.36ms 184.96ms 79.11ms 53.97ms 78.00% req/s (client) : 1412.04 1447.84 1426.52 10.57 63.00% See the h2load manual page :ref:`h2load-1-output` section for the explanation of the above numbers. Timing-based load-testing ------------------------- As of v1.26.0, h2load supports timing-based load-testing. This method performs load-testing in terms of a given duration instead of a pre-defined number of requests. The new option :option:`--duration` specifies how long the load-testing takes. For example, ``--duration=10`` makes h2load perform load-testing against a server for 10 seconds. You can also specify a “warming-up†period with :option:`--warm-up-time`. If :option:`--duration` is used, :option:`-n` option is ignored. The following command performs load-testing for 10 seconds after 5 seconds warming up period: .. code-block:: text $ h2load -c100 -m100 --duration=10 --warm-up-time=5 https://localhost Flow Control ------------ HTTP/2 has flow control and it may affect benchmarking results. By default, h2load uses large enough flow control window, which effectively disables flow control. To adjust receiver flow control window size, there are following options: :option:`-w` Sets the stream level initial window size to (2**)-1. :option:`-W` Sets the connection level initial window size to (2**)-1. Multi-Threading --------------- Sometimes benchmarking client itself becomes a bottleneck. To remedy this situation, use :option:`-t` option to specify the number of native thread to use. :option:`-t` The number of native threads. Default: 1 Selecting protocol for clear text --------------------------------- By default, if \http:// URI is given, HTTP/2 protocol is used. To change the protocol to use for clear text, use :option:`-p` option. Multiple URIs ------------- If multiple URIs are specified, they are used in round robin manner. .. note:: Please note that h2load uses scheme, host and port in the first URI and ignores those parts in the rest of the URIs. UNIX domain socket ------------------ To request against UNIX domain socket, use :option:`--base-uri`, and specify ``unix:`` followed by the path to UNIX domain socket. For example, if UNIX domain socket is ``/tmp/nghttpx.sock``, use ``--base-uri=unix:/tmp/nghttpx.sock``. h2load uses scheme, host and port in the first URI in command-line or input file. HTTP/3 ------ h2load supports HTTP/3 if it is built with HTTP/3 enabled. HTTP/3 support is experimental. In order to send HTTP/3 request, specify ``h3`` to :option:`--alpn-list`. nghttp2-1.68.0/doc/sources/PaxHeaders/nghttpx-howto.rst0000644000000000000000000000013115077107270020073 xustar0030 mtime=1761382072.966444264 30 atime=1761382106.792307837 29 ctime=1761382109.73729854 nghttp2-1.68.0/doc/sources/nghttpx-howto.rst0000644000175100017510000005667215077107270020504 0ustar00runnerrunner.. program:: nghttpx nghttpx - HTTP/2 proxy - HOW-TO =============================== :doc:`nghttpx.1` is a proxy translating protocols between HTTP/2 and other protocols (e.g., HTTP/1). It operates in several modes and each mode may require additional programs to work with. This article describes each operation mode and explains the intended use-cases. It also covers some useful options later. Default mode ------------ If nghttpx is invoked without :option:`--http2-proxy`, it operates in default mode. In this mode, it works as reverse proxy (gateway) for HTTP/3, HTTP/2 and HTTP/1 clients to backend servers. This is also known as "HTTP/2 router". By default, frontend connection is encrypted using SSL/TLS. So server's private key and certificate must be supplied to the command line (or through configuration file). In this case, the frontend protocol selection will be done via ALPN. To turn off encryption on frontend connection, use ``no-tls`` keyword in :option:`--frontend` option. HTTP/2 and HTTP/1 are available on the frontend, and an HTTP/1 connection can be upgraded to HTTP/2 using HTTP Upgrade. Starting HTTP/2 connection by sending HTTP/2 connection preface is also supported. In order to receive HTTP/3 traffic, use ``quic`` parameter in :option:`--frontend` option (.e.g, ``--frontend='*,443;quic'``) nghttpx can listen on multiple frontend addresses. This is achieved by using multiple :option:`--frontend` options. For each frontend address, TLS can be enabled or disabled. By default, backend connections are not encrypted. To enable TLS encryption on backend connections, use ``tls`` keyword in :option:`--backend` option. Using patterns and ``proto`` keyword in :option:`--backend` option, backend application protocol can be specified per host/request path pattern. It means that you can use both HTTP/2 and HTTP/1 in backend connections at the same time. Note that default backend protocol is HTTP/1.1. To use HTTP/2 in backend, you have to specify ``h2`` in ``proto`` keyword in :option:`--backend` explicitly. The backend is supposed to be a Web server. For example, to make nghttpx listen to encrypted HTTP/2 requests at port 8443, and a backend Web server is configured to listen to HTTP requests at port 8080 on the same host, run nghttpx command-line like this: .. code-block:: text $ nghttpx -f0.0.0.0,8443 -b127.0.0.1,8080 /path/to/server.key /path/to/server.crt Then an HTTP/2 enabled client can access the nghttpx server using HTTP/2. For example, you can send a GET request using nghttp: .. code-block:: text $ nghttp -nv https://localhost:8443/ HTTP/2 proxy mode ----------------- If nghttpx is invoked with :option:`--http2-proxy` (or its shorthand :option:`-s`) option, it operates in HTTP/2 proxy mode. The supported protocols in frontend and backend connections are the same as in `default mode`_. The difference is that this mode acts like a forward proxy and assumes the backend is an HTTP proxy server (e.g., Squid, Apache Traffic Server). HTTP/1 requests must include an absolute URI in request line. By default, the frontend connection is encrypted. So this mode is also called secure proxy. To turn off encryption on the frontend connection, use ``no-tls`` keyword in :option:`--frontend` option. The backend must be an HTTP proxy server. nghttpx supports multiple backend server addresses. It translates incoming requests to HTTP request to backend server. The backend server performs real proxy work for each request, for example, dispatching requests to the origin server and caching contents. The backend connection is not encrypted by default. To enable encryption, use ``tls`` keyword in :option:`--backend` option. The default backend protocol is HTTP/1.1. To use HTTP/2 in backend connection, use :option:`--backend` option, and specify ``h2`` in ``proto`` keyword explicitly. For example, to make nghttpx listen to encrypted HTTP/2 requests at port 8443, and a backend HTTP proxy server is configured to listen to HTTP/1 requests at port 8080 on the same host, run nghttpx command-line like this: .. code-block:: text $ nghttpx -s -f'*,8443' -b127.0.0.1,8080 /path/to/server.key /path/to/server.crt At the time of this writing, Firefox 41 and Chromium v46 can use nghttpx as HTTP/2 proxy. To make Firefox or Chromium use nghttpx as HTTP/2 proxy, user has to create proxy.pac script file like this: .. code-block:: javascript function FindProxyForURL(url, host) { return "HTTPS SERVERADDR:PORT"; } ``SERVERADDR`` and ``PORT`` is the hostname/address and port of the machine nghttpx is running. Please note that both Firefox and Chromium require valid certificate for secure proxy. For Firefox, open Preference window and select Advanced then click Network tab. Clicking Connection Settings button will show the dialog. Select "Automatic proxy configuration URL" and enter the path to proxy.pac file, something like this: .. code-block:: text file:///path/to/proxy.pac For Chromium, use following command-line: .. code-block:: text $ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn As HTTP/1 proxy server, Squid may work as out-of-box. Traffic server requires to be configured as forward proxy. Here is the minimum configuration items to edit: .. code-block:: text CONFIG proxy.config.reverse_proxy.enabled INT 0 CONFIG proxy.config.url_remap.remap_required INT 0 Consult Traffic server `documentation `_ to know how to configure traffic server as forward proxy and its security implications. ALPN support ------------ ALPN support requires OpenSSL >= 1.0.2. Disable frontend SSL/TLS ------------------------ The frontend connections are encrypted with SSL/TLS by default. To turn off SSL/TLS, use ``no-tls`` keyword in :option:`--frontend` option. If this option is used, the private key and certificate are not required to run nghttpx. Enable backend SSL/TLS ---------------------- The backend connections are not encrypted by default. To enable SSL/TLS encryption, use ``tls`` keyword in :option:`--backend` option. Enable SSL/TLS on memcached connection -------------------------------------- By default, memcached connection is not encrypted. To enable encryption, use ``tls`` keyword in :option:`--tls-ticket-key-memcached`. Specifying additional server certificates ----------------------------------------- nghttpx accepts additional server private key and certificate pairs using :option:`--subcert` option. It can be used multiple times. Specifying additional CA certificate ------------------------------------ By default, nghttpx tries to read CA certificate from system. But depending on the system you use, this may fail or is not supported. To specify CA certificate manually, use :option:`--cacert` option. The specified file must be PEM format and can contain multiple certificates. By default, nghttpx validates server's certificate. If you want to turn off this validation, knowing this is really insecure and what you are doing, you can use :option:`--insecure` option to disable certificate validation. Read/write rate limit --------------------- nghttpx supports transfer rate limiting on frontend connections. You can do rate limit per frontend connection for reading and writing individually. To perform rate limit for reading, use :option:`--read-rate` and :option:`--read-burst` options. For writing, use :option:`--write-rate` and :option:`--write-burst`. Please note that rate limit is performed on top of TCP and nothing to do with HTTP/2 flow control. Rewriting location header field ------------------------------- nghttpx automatically rewrites location response header field if the following all conditions satisfy: * In the default mode (:option:`--http2-proxy` is not used) * :option:`--no-location-rewrite` is not used * URI in location header field is an absolute URI * URI in location header field includes non empty host component. * host (without port) in URI in location header field must match the host appearing in ``:authority`` or ``host`` header field. When rewrite happens, URI scheme is replaced with the ones used in frontend, and authority is replaced with which appears in ``:authority``, or ``host`` request header field. ``:authority`` header field has precedence over ``host``. Hot swapping ------------ nghttpx supports hot swapping using signals. The hot swapping in nghttpx is multi step process. First send USR2 signal to nghttpx process. It will do fork and execute new executable, using same command-line arguments and environment variables. As of nghttpx version 1.20.0, that is all you have to do. The new main process sends QUIT signal to the original process, when it is ready to serve requests, to shut it down gracefully. For earlier versions of nghttpx, you have to do one more thing. At this point, both current and new processes can accept requests. To gracefully shutdown current process, send QUIT signal to current nghttpx process. When all existing frontend connections are done, the current process will exit. At this point, only new nghttpx process exists and serves incoming requests. If you want to just reload configuration file without executing new binary, send SIGHUP to nghttpx main process. Re-opening log files -------------------- When rotating log files, it is desirable to re-open log files after log rotation daemon renamed existing log files. To tell nghttpx to re-open log files, send USR1 signal to nghttpx process. It will re-open files specified by :option:`--accesslog-file` and :option:`--errorlog-file` options. Multiple frontend addresses --------------------------- nghttpx can listen on multiple frontend addresses. To specify them, just use :option:`--frontend` (or its shorthand :option:`-f`) option repeatedly. TLS can be enabled or disabled per frontend address basis. For example, to listen on port 443 with TLS enabled, and on port 80 without TLS: .. code-block:: text frontend=*,443 frontend=*,80;no-tls Multiple backend addresses -------------------------- nghttpx supports multiple backend addresses. To specify them, just use :option:`--backend` (or its shorthand :option:`-b`) option repeatedly. For example, to use ``192.168.0.10:8080`` and ``192.168.0.11:8080``, use command-line like this: ``-b192.168.0.10,8080 -b192.168.0.11,8080``. In configuration file, this looks like: .. code-block:: text backend=192.168.0.10,8080 backend=192.168.0.11,8008 nghttpx can route request to different backend according to request host and path. For example, to route request destined to host ``doc.example.com`` to backend server ``docserv:3000``, you can write like so: .. code-block:: text backend=docserv,3000;doc.example.com/ When you write this option in command-line, you should enclose argument with single or double quotes, since the character ``;`` has a special meaning in shell. To route, request to request path ``/foo`` to backend server ``[::1]:8080``, you can write like so: .. code-block:: text backend=::1,8080;/foo If the last character of path pattern is ``/``, all request paths which start with that pattern match: .. code-block:: text backend=::1,8080;/bar/ The request path ``/bar/buzz`` matches the ``/bar/``. You can use ``*`` at the end of the path pattern to make it wildcard pattern. ``*`` must match at least one character: .. code-block:: text backend=::1,8080;/sample* The request path ``/sample1/foo`` matches the ``/sample*`` pattern. Of course, you can specify both host and request path at the same time: .. code-block:: text backend=192.168.0.10,8080;example.com/foo We can use ``*`` in the left most position of host to achieve wildcard suffix match. If ``*`` is the left most character, then the remaining string should match the request host suffix. ``*`` must match at least one character. For example, ``*.example.com`` matches ``www.example.com`` and ``dev.example.com``, and does not match ``example.com`` and ``nghttp2.org``. The exact match (without ``*``) always takes precedence over wildcard match. One important thing you have to remember is that we have to specify default routing pattern for so called "catch all" pattern. To write "catch all" pattern, just specify backend server address, without pattern. Usually, host is the value of ``Host`` header field. In HTTP/2, the value of ``:authority`` pseudo header field is used. When you write multiple backend addresses sharing the same routing pattern, they are used as load balancing. For example, to use 2 servers ``serv1:3000`` and ``serv2:3000`` for request host ``example.com`` and path ``/myservice``, you can write like so: .. code-block:: text backend=serv1,3000;example.com/myservice backend=serv2,3000;example.com/myservice You can also specify backend application protocol in :option:`--backend` option using ``proto`` keyword after pattern. Utilizing this allows ngttpx to route certain request to HTTP/2, other requests to HTTP/1. For example, to route requests to ``/ws/`` in backend HTTP/1.1 connection, and use backend HTTP/2 for other requests, do this: .. code-block:: text backend=serv1,3000;/;proto=h2 backend=serv1,3000;/ws/;proto=http/1.1 The default backend protocol is HTTP/1.1. TLS can be enabled per pattern basis: .. code-block:: text backend=serv1,8443;/;proto=h2;tls backend=serv2,8080;/ws/;proto=http/1.1 In the above case, connection to serv1 will be encrypted by TLS. On the other hand, connection to serv2 will not be encrypted by TLS. Dynamic hostname lookup ----------------------- By default, nghttpx performs backend hostname lookup at start up, or configuration reload, and keeps using them in its entire session. To make nghttpx perform hostname lookup dynamically, use ``dns`` parameter in :option:`--backend` option, like so: .. code-block:: text backend=foo.example.com,80;;dns nghttpx will cache resolved addresses for certain period of time. To change this cache period, use :option:`--dns-cache-timeout`. Enable PROXY protocol --------------------- PROXY protocol can be enabled per frontend. In order to enable PROXY protocol, use ``proxyproto`` parameter in :option:`--frontend` option, like so: .. code-block:: text frontend=*,443;proxyproto nghttpx supports both PROXY protocol v1 and v2. AF_UNIX in PROXY protocol version 2 is ignored. Session affinity ---------------- Two kinds of session affinity are available: client IP, and HTTP Cookie. To enable client IP based affinity, specify ``affinity=ip`` parameter in :option:`--backend` option. If PROXY protocol is enabled, then an address obtained from PROXY protocol is taken into consideration. To enable HTTP Cookie based affinity, specify ``affinity=cookie`` parameter, and specify a name of cookie in ``affinity-cookie-name`` parameter. Optionally, a Path attribute can be specified in ``affinity-cookie-path`` parameter: .. code-block:: text backend=127.0.0.1,3000;;affinity=cookie;affinity-cookie-name=nghttpxlb;affinity-cookie-path=/ Secure attribute of cookie is set if client connection is protected by TLS. ``affinity-cookie-stickiness`` specifies the stickiness of this affinity. If ``loose`` is given, which is the default, removing or adding a backend server might break affinity. While ``strict`` is given, removing the designated backend server breaks affinity, but adding new backend server does not cause breakage. PSK cipher suites ----------------- nghttpx supports pre-shared key (PSK) cipher suites for both frontend and backend TLS connections. For frontend connection, use :option:`--psk-secrets` option to specify a file which contains PSK identity and secrets. The format of the file is ``:``, where ```` is PSK identity, and ```` is PSK secret in hex, like so: .. code-block:: text client1:9567800e065e078085c241d54a01c6c3f24b3bab71a606600f4c6ad2c134f3b9 client2:b1376c3f8f6dcf7c886c5bdcceecd1e6f1d708622b6ddd21bda26ebd0c0bca99 nghttpx server accepts any of the identity and secret pairs in the file. The default cipher suite list does not contain PSK cipher suites. In order to use PSK, PSK cipher suite must be enabled by using :option:`--ciphers` option. The desired PSK cipher suite may be listed in `HTTP/2 cipher block list `_. In order to use such PSK cipher suite with HTTP/2, disable HTTP/2 cipher block list by using :option:`--no-http2-cipher-block-list` option. But you should understand its implications. At the time of writing, even if only PSK cipher suites are specified in :option:`--ciphers` option, certificate and private key are still required. For backend connection, use :option:`--client-psk-secrets` option to specify a file which contains single PSK identity and secret. The format is the same as the file used by :option:`--psk-secrets` described above, but only first identity and secret pair is solely used, like so: .. code-block:: text client2:b1376c3f8f6dcf7c886c5bdcceecd1e6f1d708622b6ddd21bda26ebd0c0bca99 The default cipher suite list does not contain PSK cipher suites. In order to use PSK, PSK cipher suite must be enabled by using :option:`--client-ciphers` option. The desired PSK cipher suite may be listed in `HTTP/2 cipher block list `_. In order to use such PSK cipher suite with HTTP/2, disable HTTP/2 cipher block list by using :option:`--client-no-http2-cipher-block-list` option. But you should understand its implications. TLSv1.3 ------- As of nghttpx v1.34.0, if it is built with OpenSSL 1.1.1 or later, it supports TLSv1.3. 0-RTT data is supported, but by default its processing is postponed until TLS handshake completes to mitigate replay attack. This costs extra round trip and reduces effectiveness of 0-RTT data. :option:`--tls-no-postpone-early-data` makes nghttpx not wait for handshake to complete before forwarding request included in 0-RTT to get full potential of 0-RTT data. In this case, nghttpx adds ``Early-Data: 1`` header field when forwarding a request to a backend server. All backend servers should recognize this header field and understand that there is a risk for replay attack. See `RFC 8470 `_ for ``Early-Data`` header field. nghttpx disables anti replay protection provided by OpenSSL. The anti replay protection of OpenSSL requires that a resumed request must hit the same server which generates the session ticket. Therefore it might not work nicely in a deployment where there are multiple nghttpx instances sharing ticket encryption keys via memcached. Because TLSv1.3 completely changes the semantics of cipher suite naming scheme and structure, nghttpx provides the new option :option:`--tls13-ciphers` and :option:`--tls13-client-ciphers` to change preferred cipher list for TLSv1.3. WebSockets over HTTP/2 ---------------------- nghttpx supports `RFC 8441 `_ Bootstrapping WebSockets with HTTP/2 for both frontend and backend connections. This feature is enabled by default and no configuration is required. WebSockets over HTTP/3 is also supported. HTTP/3 ------ nghttpx supports HTTP/3 if it is built with HTTP/3 support enabled. HTTP/3 support is experimental. In order to listen UDP port to receive HTTP/3 traffic, :option:`--frontend` option must have ``quic`` parameter: .. code-block:: text frontend=*,443;quic The above example makes nghttpx receive HTTP/3 traffic on UDP port 443. nghttpx does not support HTTP/3 on backend connection. Hot swapping (SIGUSR2) or configuration reload (SIGHUP) require eBPF program. Without eBPF, old worker processes keep getting HTTP/3 traffic and do not work as intended. The QUIC keying material to encrypt Connection ID must be set with :option:`--frontend-quic-secret-file` and must provide the existing keys in order to keep the existing connections alive during reload. The construction of Connection ID closely follows Block Cipher CID Algorithm described in `QUIC-LB draft `_. A Connection ID that nghttpx generates is always 17 bytes long. It uses first 3 bits as a configuration ID. The remaining bits in the first byte are reserved and random. The next 4 bytes are server ID. The next 4 bytes are used to route UDP datagram to a correct ``SO_REUSEPORT`` socket. The remaining bytes are randomly generated. The server ID and the next 12 bytes are encrypted with AES-ECB. The key is derived from the keying materials stored in a file specified by :option:`--frontend-quic-secret-file`. The first 2 bits of keying material in the file is used as a configuration ID. The remaining bits and following 3 bytes are reserved and unused. The next 32 bytes are used as an initial secret. The remaining 32 bytes are used as a salt. The encryption key is generated by `HKDF `_ with SHA256 and these keying materials and ``connection id encryption key`` as info. In order announce that HTTP/3 endpoint is available, you should specify alt-svc header field. For example, the following options send alt-svc header field in HTTP/1.1 and HTTP/2 response: .. code-block:: text altsvc=h3,443,,,ma=3600 http2-altsvc=h3,443,,,ma=3600 Migration from nghttpx v1.18.x or earlier ----------------------------------------- As of nghttpx v1.19.0, :option:`--ciphers` option only changes cipher list for frontend TLS connection. In order to change cipher list for backend connection, use :option:`--client-ciphers` option. Similarly, :option:`--no-http2-cipher-block-list` option only disables HTTP/2 cipher block list for frontend connection. In order to disable HTTP/2 cipher block list for backend connection, use :option:`--client-no-http2-cipher-block-list` option. ``--accept-proxy-protocol`` option was deprecated. Instead, use ``proxyproto`` parameter in :option:`--frontend` option to enable PROXY protocol support per frontend. Migration from nghttpx v1.8.0 or earlier ---------------------------------------- As of nghttpx 1.9.0, ``--frontend-no-tls`` and ``--backend-no-tls`` have been removed. To disable encryption on frontend connection, use ``no-tls`` keyword in :option:`--frontend` potion: .. code-block:: text frontend=*,3000;no-tls The TLS encryption is now disabled on backend connection in all modes by default. To enable encryption on backend connection, use ``tls`` keyword in :option:`--backend` option: .. code-block:: text backend=127.0.0.1,8080;tls As of nghttpx 1.9.0, ``--http2-bridge``, ``--client`` and ``--client-proxy`` options have been removed. These functionality can be used using combinations of options. Use following option instead of ``--http2-bridge``: .. code-block:: text backend=,;;proto=h2;tls Use following options instead of ``--client``: .. code-block:: text frontend=,;no-tls backend=,;;proto=h2;tls Use following options instead of ``--client-proxy``: .. code-block:: text http2-proxy=yes frontend=,;no-tls backend=,;;proto=h2;tls We also removed ``--backend-http2-connections-per-worker`` option. It was present because previously the number of backend h2 connection was statically configured, and defaulted to 1. Now the number of backend h2 connection is increased on demand. We know the maximum number of concurrent streams per connection. When we push as many request as the maximum concurrency to the one connection, we create another new connection so that we can distribute load and avoid delay the request processing. This is done automatically without any configuration. nghttp2-1.68.0/doc/sources/PaxHeaders/index.rst0000644000000000000000000000013215077107270016351 xustar0030 mtime=1761382072.966444264 30 atime=1761382106.787307859 30 ctime=1761382109.732298554 nghttp2-1.68.0/doc/sources/index.rst0000644000175100017510000000215315077107270016742 0ustar00runnerrunner.. nghttp2 documentation main file, created by sphinx-quickstart on Sun Mar 11 22:57:49 2012. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. nghttp2 - HTTP/2 C Library ============================ This is an implementation of Hypertext Transfer Protocol version 2. The project is hosted at `github.com/nghttp2/nghttp2 `_. Contents: .. toctree:: :maxdepth: 2 package_README contribute building-android-binary tutorial-client tutorial-server tutorial-hpack nghttp.1 nghttpd.1 nghttpx.1 h2load.1 nghttpx-howto h2load-howto programmers-guide apiref nghttp2.h nghttp2ver.h Source Issues nghttp2.org Released Versions ================= https://github.com/nghttp2/nghttp2/releases Resources --------- * HTTP/2 https://tools.ietf.org/html/rfc7540 * HPACK https://tools.ietf.org/html/rfc7541 * HTTP Alternative Services https://tools.ietf.org/html/rfc7838 nghttp2-1.68.0/doc/sources/PaxHeaders/contribute.rst0000644000000000000000000000013215077107270017420 xustar0030 mtime=1761382072.966444264 30 atime=1761382106.796307819 30 ctime=1761382109.741298528 nghttp2-1.68.0/doc/sources/contribute.rst0000644000175100017510000000453215077107270020014 0ustar00runnerrunnerContribution Guidelines ======================= [This text was composed based on 1.2. License section of curl/libcurl project.] When contributing with code, you agree to put your changes and new code under the same license nghttp2 is already using unless stated and agreed otherwise. When changing existing source code, you do not alter the copyright of the original file(s). The copyright will still be owned by the original creator(s) or those who have been assigned copyright by the original author(s). By submitting a patch to the nghttp2 project, you are assumed to have the right to the code and to be allowed by your employer or whatever to hand over that patch/code to us. We will credit you for your changes as far as possible, to give credit but also to keep a trace back to who made what changes. Please always provide us with your full real name when contributing! Coding style ------------ We use clang-format to format source code consistently. The clang-format configuration file .clang-format is located at the root directory. Since clang-format produces slightly different results between versions, we currently use clang-format-19. To detect any violation to the coding style, we recommend to setup git pre-commit hook to check coding style of the changes you introduced. The pre-commit file is located at the root directory. Copy it under .git/hooks and make sure that it is executable. The pre-commit script uses clang-format-diff.py to detect any style errors. If it is not in your PATH or it exists under different name (e.g., clang-format-diff-18 in debian), either add it to PATH variable or add git option ``clangformatdiff.binary`` to point to the script. For emacs users, integrating clang-format to emacs is very easy. clang-format.el should come with clang distribution. If it is not found, download it from `here `_. And add these lines to your .emacs file: .. code-block:: lisp ;; From ;; https://code.google.com/p/chromium/wiki/Emacs#Use_Google's_C++_style! (load "//clang-format.el") (add-hook 'c-mode-common-hook (function (lambda () (local-set-key (kbd "TAB") 'clang-format-region)))) You can find other editor integration in http://clang.llvm.org/docs/ClangFormat.html. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_deflate_bound.rst0000644000000000000000000000013215077107332020472 xustar0030 mtime=1761382106.497309136 30 atime=1761382106.565308837 30 ctime=1761382109.509299199 nghttp2-1.68.0/doc/nghttp2_hd_deflate_bound.rst0000644000175100017510000000050115077107332021056 0ustar00runnerrunner nghttp2_hd_deflate_bound ======================== Synopsis -------- *#include * .. function:: size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, const nghttp2_nv *nva, size_t nvlen) Returns an upper bound on the compressed size after deflation of *nva* of length *nvlen*. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_get_stream_local_close.rst0000644000000000000000000000013115077107332023457 xustar0029 mtime=1761382106.50330911 30 atime=1761382106.716308171 30 ctime=1761382109.662298756 nghttp2-1.68.0/doc/nghttp2_session_get_stream_local_close.rst0000644000175100017510000000057015077107332024052 0ustar00runnerrunner nghttp2_session_get_stream_local_close ====================================== Synopsis -------- *#include * .. function:: int nghttp2_session_get_stream_local_close(nghttp2_session *session, int32_t stream_id) Returns 1 if local peer half closed the given stream *stream_id*. Returns 0 if it did not. Returns -1 if no such stream exists. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_option_set_max_settings.rst0000644000000000000000000000013215077107332022174 xustar0030 mtime=1761382106.499309127 30 atime=1761382106.623308581 30 ctime=1761382109.566299034 nghttp2-1.68.0/doc/nghttp2_option_set_max_settings.rst0000644000175100017510000000071515077107332022567 0ustar00runnerrunner nghttp2_option_set_max_settings =============================== Synopsis -------- *#include * .. function:: void nghttp2_option_set_max_settings(nghttp2_option *option, size_t val) This function sets the maximum number of SETTINGS entries per SETTINGS frame that will be accepted. If more than those entries are received, the peer is considered to be misbehaving and session will be closed. The default value is 32. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_send_data_callback.rst0000644000000000000000000000013215077107332025377 xustar0030 mtime=1761382106.502309114 30 atime=1761382106.678308339 30 ctime=1761382109.623298869 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_send_data_callback.rst0000644000175100017510000000073715077107332025776 0ustar00runnerrunner nghttp2_session_callbacks_set_send_data_callback ================================================ Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_send_data_callback( nghttp2_session_callbacks *cbs, nghttp2_send_data_callback send_data_callback) Sets callback function invoked when :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` is used in :type:`nghttp2_data_source_read_callback2` to avoid data copy. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_server_new3.rst0000644000000000000000000000013215077107332021231 xustar0030 mtime=1761382106.504309105 30 atime=1761382106.734308092 30 ctime=1761382109.679298707 nghttp2-1.68.0/doc/nghttp2_session_server_new3.rst0000644000175100017510000000164015077107332021622 0ustar00runnerrunner nghttp2_session_server_new3 =========================== Synopsis -------- *#include * .. function:: int nghttp2_session_server_new3( nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option, nghttp2_mem *mem) Like `nghttp2_session_server_new2()`, but with additional custom memory allocator specified in the *mem*. The *mem* can be ``NULL`` and the call is equivalent to `nghttp2_session_server_new2()`. This function does not take ownership *mem*. The application is responsible for freeing *mem*. The library code does not refer to *mem* pointer after this function returns, so the application can safely free it. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_on_stream_close_callback.rst0000644000000000000000000000013215077107332026631 xustar0030 mtime=1761382106.501309119 30 atime=1761382106.665308396 30 ctime=1761382109.610298907 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_on_stream_close_callback.rst0000644000175100017510000000061515077107332027223 0ustar00runnerrunner nghttp2_session_callbacks_set_on_stream_close_callback ====================================================== Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_on_stream_close_callback( nghttp2_session_callbacks *cbs, nghttp2_on_stream_close_callback on_stream_close_callback) Sets callback function invoked when the stream is closed. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_http2_strerror.rst0000644000000000000000000000013215077107332020227 xustar0030 mtime=1761382106.499309127 30 atime=1761382106.597308696 30 ctime=1761382109.541299106 nghttp2-1.68.0/doc/nghttp2_http2_strerror.rst0000644000175100017510000000067215077107332020624 0ustar00runnerrunner nghttp2_http2_strerror ====================== Synopsis -------- *#include * .. function:: const char *nghttp2_http2_strerror(uint32_t error_code) Returns string representation of HTTP/2 error code *error_code* (e.g., ``PROTOCOL_ERROR`` is returned if ``error_code == NGHTTP2_PROTOCOL_ERROR``). If string representation is unknown for given *error_code*, this function returns string ``unknown``. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_check_header_value_rfc9113.rst0000644000000000000000000000013215077107332022155 xustar0030 mtime=1761382106.497309136 30 atime=1761382106.560308859 30 ctime=1761382109.503299216 nghttp2-1.68.0/doc/nghttp2_check_header_value_rfc9113.rst0000644000175100017510000000066215077107332022551 0ustar00runnerrunner nghttp2_check_header_value_rfc9113 ================================== Synopsis -------- *#include * .. function:: int nghttp2_check_header_value_rfc9113(const uint8_t *value, size_t len) Returns nonzero if HTTP header field value *value* of length *len* is valid according to http://tools.ietf.org/html/rfc7230#section-3.2, plus https://datatracker.ietf.org/doc/html/rfc9113#section-8.2.1 nghttp2-1.68.0/doc/PaxHeaders/nghttp2ver.h.rst.in0000644000000000000000000000013215077107270016515 xustar0030 mtime=1761382072.964444273 30 atime=1761382104.127319577 30 ctime=1761382109.478299288 nghttp2-1.68.0/doc/nghttp2ver.h.rst.in0000644000175100017510000000014015077107270017100 0ustar00runnerrunnernghttp2ver.h ============ .. literalinclude:: @top_builddir@/lib/includes/nghttp2/nghttp2ver.h nghttp2-1.68.0/doc/PaxHeaders/nghttp2_check_path.rst0000644000000000000000000000013115077107332017314 xustar0030 mtime=1761382106.497309136 29 atime=1761382106.56230885 30 ctime=1761382109.506299208 nghttp2-1.68.0/doc/nghttp2_check_path.rst0000644000175100017510000000115215077107332017704 0ustar00runnerrunner nghttp2_check_path ================== Synopsis -------- *#include * .. function:: int nghttp2_check_path(const uint8_t *value, size_t len) Returns nonzero if the *value* which is supposed to be the value of the :path header field is valid according to https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.3 *value* is valid if it merely consists of the allowed characters. In particular, it does not check whether *value* follows the syntax of path. The allowed characters are all characters valid by `nghttp2_check_header_value` minus SPC and HT. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_get_effective_local_window_size.rst0000644000000000000000000000013215077107332025361 xustar0030 mtime=1761382106.502309114 30 atime=1761382106.697308255 30 ctime=1761382109.642298814 nghttp2-1.68.0/doc/nghttp2_session_get_effective_local_window_size.rst0000644000175100017510000000155715077107332025761 0ustar00runnerrunner nghttp2_session_get_effective_local_window_size =============================================== Synopsis -------- *#include * .. function:: int32_t nghttp2_session_get_effective_local_window_size(nghttp2_session *session) Returns the local (receive) window size for a connection. The local window size can be adjusted by `nghttp2_submit_window_update()`. This function takes into account that and returns effective window size. This function does not take into account the amount of received data from the remote endpoint. Use `nghttp2_session_get_local_window_size()` to know the amount of data the remote endpoint can send without receiving connection-level WINDOW_UPDATE frame. Note that each stream is still subject to the stream level flow control. This function returns -1 if it fails. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_rand_callback.rst0000644000000000000000000000013215077107332024401 xustar0030 mtime=1761382106.501309119 30 atime=1761382106.669308379 30 ctime=1761382109.614298895 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_rand_callback.rst0000644000175100017510000000104715077107332024773 0ustar00runnerrunner nghttp2_session_callbacks_set_rand_callback =========================================== Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_rand_callback( nghttp2_session_callbacks *cbs, nghttp2_rand_callback rand_callback) Sets callback function invoked when unpredictable data is needed. Although this callback is optional due to the backward compatibility, it is recommended to specify it to harden the runtime behavior against suspicious activities of a remote endpoint. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_del.rst0000644000000000000000000000013215077107332021532 xustar0030 mtime=1761382106.500309123 30 atime=1761382106.640308506 30 ctime=1761382109.586298976 nghttp2-1.68.0/doc/nghttp2_session_callbacks_del.rst0000644000175100017510000000047615077107332022131 0ustar00runnerrunner nghttp2_session_callbacks_del ============================= Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks) Frees any resources allocated for *callbacks*. If *callbacks* is ``NULL``, this function does nothing. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_option_set_no_http_messaging.rst0000644000000000000000000000013115077107332023176 xustar0030 mtime=1761382106.499309127 29 atime=1761382106.61230863 30 ctime=1761382109.556299063 nghttp2-1.68.0/doc/nghttp2_option_set_no_http_messaging.rst0000644000175100017510000000140115077107332023563 0ustar00runnerrunner nghttp2_option_set_no_http_messaging ==================================== Synopsis -------- *#include * .. function:: void nghttp2_option_set_no_http_messaging(nghttp2_option *option, int val) By default, nghttp2 library enforces subset of HTTP Messaging rules described in `HTTP/2 specification, section 8 `_. See :ref:`http-messaging` section for details. For those applications who use nghttp2 library as non-HTTP use, give nonzero to *val* to disable this enforcement. Please note that disabling this feature does not change the fundamental client and server model of HTTP. That is, even if the validation is disabled, only client can send requests. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_submit_ping.rst0000644000000000000000000000013215077107332017544 xustar0030 mtime=1761382106.505309101 30 atime=1761382106.769307938 30 ctime=1761382109.714298606 nghttp2-1.68.0/doc/nghttp2_submit_ping.rst0000644000175100017510000000200515077107332020131 0ustar00runnerrunner nghttp2_submit_ping =================== Synopsis -------- *#include * .. function:: int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, const uint8_t *opaque_data) Submits PING frame. You don't have to send PING back when you received PING frame. The library automatically submits PING frame in this case. The *flags* is bitwise OR of 0 or more of the following value. * :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` Unless `nghttp2_option_set_no_auto_ping_ack()` is used, the *flags* should be :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. If the *opaque_data* is non ``NULL``, then it should point to the 8 bytes array of memory to specify opaque data to send with PING frame. If the *opaque_data* is ``NULL``, zero-cleared 8 bytes will be sent as opaque data. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_submit_response.rst0000644000000000000000000000013215077107332020445 xustar0030 mtime=1761382106.505309101 30 atime=1761382106.776307907 30 ctime=1761382109.722298583 nghttp2-1.68.0/doc/nghttp2_submit_response.rst0000644000175100017510000000624215077107332021041 0ustar00runnerrunner nghttp2_submit_response ======================= Synopsis -------- *#include * .. function:: int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider *data_prd) .. warning:: Deprecated. Use `nghttp2_submit_response2()` instead. Submits response HEADERS frame and optionally one or more DATA frames against the stream *stream_id*. The *nva* is an array of name/value pair :type:`nghttp2_nv` with *nvlen* elements. The application is responsible to include required pseudo-header fields (header field whose name starts with ":") in *nva* and must place pseudo-headers before regular header fields. This function creates copies of all name/value pairs in *nva*. It also lower-cases all names in *nva*. The order of elements in *nva* is preserved. For header fields with :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name and value are not copied respectively. With :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to pass header field name in lowercase. The application should maintain the references to them until :type:`nghttp2_on_frame_send_callback` or :type:`nghttp2_on_frame_not_send_callback` is called. HTTP/2 specification has requirement about header fields in the response HEADERS. See the specification for more details. If *data_prd* is not ``NULL``, it provides data which will be sent in subsequent DATA frames. This function does not take ownership of the *data_prd*. The function copies the members of the *data_prd*. If *data_prd* is ``NULL``, HEADERS will have END_STREAM flag set. This method can be used as normal HTTP response and push response. When pushing a resource using this function, the *session* must be configured using `nghttp2_session_server_new()` or its variants and the target stream denoted by the *stream_id* must be reserved using `nghttp2_submit_push_promise()`. To send non-final response headers (e.g., HTTP status 101), don't use this function because this function half-closes the outbound stream. Instead, use `nghttp2_submit_headers()` for this purpose. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The *stream_id* is 0. :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` DATA or HEADERS has been already submitted and not fully processed yet. Normally, this does not happen, but when application wrongly calls `nghttp2_submit_response()` twice, this may happen. :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` The *session* is client session. .. warning:: Calling this function twice for the same stream ID may lead to program crash. It is generally considered to a programming error to commit response twice. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_deflate_get_num_table_entries.rst0000644000000000000000000000013115077107332023720 xustar0030 mtime=1761382106.498309132 29 atime=1761382106.57130881 30 ctime=1761382109.515299181 nghttp2-1.68.0/doc/nghttp2_hd_deflate_get_num_table_entries.rst0000644000175100017510000000064415077107332024315 0ustar00runnerrunner nghttp2_hd_deflate_get_num_table_entries ======================================== Synopsis -------- *#include * .. function:: size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater) Returns the number of entries that header table of *deflater* contains. This is the sum of the number of static table and dynamic table, so the return value is at least 61. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_on_header_callback.rst0000644000000000000000000000013215077107332025401 xustar0030 mtime=1761382106.501309119 30 atime=1761382106.658308427 30 ctime=1761382109.604298924 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_on_header_callback.rst0000644000175100017510000000110115077107332025762 0ustar00runnerrunner nghttp2_session_callbacks_set_on_header_callback ================================================ Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_on_header_callback( nghttp2_session_callbacks *cbs, nghttp2_on_header_callback on_header_callback) Sets callback function invoked when a header name/value pair is received. If both `nghttp2_session_callbacks_set_on_header_callback()` and `nghttp2_session_callbacks_set_on_header_callback2()` are used to set callbacks, the latter has the precedence. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_option_set_peer_max_concurrent_streams.rst0000644000000000000000000000013215077107332025267 xustar0030 mtime=1761382106.499309127 30 atime=1761382106.616308612 30 ctime=1761382109.560299052 nghttp2-1.68.0/doc/nghttp2_option_set_peer_max_concurrent_streams.rst0000644000175100017510000000174715077107332025670 0ustar00runnerrunner nghttp2_option_set_peer_max_concurrent_streams ============================================== Synopsis -------- *#include * .. function:: void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, uint32_t val) This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of remote endpoint as if it is received in SETTINGS frame. Without specifying this option, the maximum number of outgoing concurrent streams is initially limited to 100 to avoid issues when the local endpoint submits lots of requests before receiving initial SETTINGS frame from the remote endpoint, since sending them at once to the remote endpoint could lead to rejection of some of the requests. This value will be overwritten when the local endpoint receives initial SETTINGS frame from the remote endpoint, either to the value advertised in SETTINGS_MAX_CONCURRENT_STREAMS or to the default value (unlimited) if none was advertised. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_get_stream_effective_recv_data_length.rst0000644000000000000000000000013115077107332026511 xustar0029 mtime=1761382106.50330911 30 atime=1761382106.715308176 30 ctime=1761382109.660298762 nghttp2-1.68.0/doc/nghttp2_session_get_stream_effective_recv_data_length.rst0000644000175100017510000000152315077107332027103 0ustar00runnerrunner nghttp2_session_get_stream_effective_recv_data_length ===================================================== Synopsis -------- *#include * .. function:: int32_t nghttp2_session_get_stream_effective_recv_data_length( nghttp2_session *session, int32_t stream_id) Returns the number of DATA payload in bytes received without WINDOW_UPDATE transmission for the stream *stream_id*. The local (receive) window size can be adjusted by `nghttp2_submit_window_update()`. This function takes into account that and returns effective data length. In particular, if the local window size is reduced by submitting negative window_size_increment with `nghttp2_submit_window_update()`, this function returns the number of bytes less than actually received. This function returns -1 if it fails. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_option_set_stream_reset_rate_limit.rst0000644000000000000000000000013215077107332024375 xustar0030 mtime=1761382106.500309123 30 atime=1761382106.624308577 30 ctime=1761382109.568299028 nghttp2-1.68.0/doc/nghttp2_option_set_stream_reset_rate_limit.rst0000644000175100017510000000146515077107332024773 0ustar00runnerrunner nghttp2_option_set_stream_reset_rate_limit ========================================== Synopsis -------- *#include * .. function:: void nghttp2_option_set_stream_reset_rate_limit(nghttp2_option *option, uint64_t burst, uint64_t rate) This function sets the rate limit for the incoming stream reset (RST_STREAM frame). It is server use only. It is a token-bucket based rate limiter. *burst* specifies the number of tokens that is initially available. The maximum number of tokens is capped to this value. *rate* specifies the number of tokens that are regenerated per second. An incoming RST_STREAM consumes one token. If there is no token available, GOAWAY is sent to tear down the connection. *burst* and *rate* default to 1000 and 33 respectively. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_inflate_get_num_table_entries.rst0000644000000000000000000000013215077107332023737 xustar0030 mtime=1761382106.498309132 30 atime=1761382106.588308735 30 ctime=1761382109.532299132 nghttp2-1.68.0/doc/nghttp2_hd_inflate_get_num_table_entries.rst0000644000175100017510000000064415077107332024333 0ustar00runnerrunner nghttp2_hd_inflate_get_num_table_entries ======================================== Synopsis -------- *#include * .. function:: size_t nghttp2_hd_inflate_get_num_table_entries(nghttp2_hd_inflater *inflater) Returns the number of entries that header table of *inflater* contains. This is the sum of the number of static table and dynamic table, so the return value is at least 61. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_submit_rst_stream.rst0000644000000000000000000000013215077107332020772 xustar0030 mtime=1761382106.505309101 30 atime=1761382106.779307894 30 ctime=1761382109.725298574 nghttp2-1.68.0/doc/nghttp2_submit_rst_stream.rst0000644000175100017510000000140515077107332021362 0ustar00runnerrunner nghttp2_submit_rst_stream ========================= Synopsis -------- *#include * .. function:: int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags, int32_t stream_id, uint32_t error_code) Submits RST_STREAM frame to cancel/reject the stream *stream_id* with the error code *error_code*. The pre-defined error code is one of :enum:`nghttp2_error_code`. The *flags* is currently ignored and should be :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The *stream_id* is 0. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_get_effective_recv_data_length.rst0000644000000000000000000000013215077107332025137 xustar0030 mtime=1761382106.502309114 30 atime=1761382106.698308251 30 ctime=1761382109.644298809 nghttp2-1.68.0/doc/nghttp2_session_get_effective_recv_data_length.rst0000644000175100017510000000143715077107332025534 0ustar00runnerrunner nghttp2_session_get_effective_recv_data_length ============================================== Synopsis -------- *#include * .. function:: int32_t nghttp2_session_get_effective_recv_data_length(nghttp2_session *session) Returns the number of DATA payload in bytes received without WINDOW_UPDATE transmission for a connection. The local (receive) window size can be adjusted by `nghttp2_submit_window_update()`. This function takes into account that and returns effective data length. In particular, if the local window size is reduced by submitting negative window_size_increment with `nghttp2_submit_window_update()`, this function returns the number of bytes less than actually received. This function returns -1 if it fails. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_extpri_parse_priority.rst0000644000000000000000000000013215077107332021672 xustar0030 mtime=1761382106.497309136 30 atime=1761382106.564308841 30 ctime=1761382109.507299204 nghttp2-1.68.0/doc/nghttp2_extpri_parse_priority.rst0000644000175100017510000000144515077107332022266 0ustar00runnerrunner nghttp2_extpri_parse_priority ============================= Synopsis -------- *#include * .. function:: int nghttp2_extpri_parse_priority(nghttp2_extpri *extpri, const uint8_t *value, size_t len) Parses Priority header field value pointed by *value* of length *len*, and stores the result in the object pointed by *extpri*. Priority header field is defined in :rfc:`9218`. This function does not initialize the object pointed by *extpri* before storing the result. It only assigns the values that the parser correctly extracted to fields. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` Failed to parse the header field value. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_select_next_protocol.rst0000644000000000000000000000013215077107332021462 xustar0030 mtime=1761382106.500309123 30 atime=1761382106.638308515 30 ctime=1761382109.583298985 nghttp2-1.68.0/doc/nghttp2_select_next_protocol.rst0000644000175100017510000000436615077107332022063 0ustar00runnerrunner nghttp2_select_next_protocol ============================ Synopsis -------- *#include * .. function:: int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen) .. warning:: Deprecated. Use `nghttp2_select_alpn` instead. A helper function for dealing with ALPN in server side. The *in* contains peer's protocol list in preferable order. The format of *in* is length-prefixed and not null-terminated. For example, ``h2`` and ``http/1.1`` stored in *in* like this:: in[0] = 2 in[1..2] = "h2" in[3] = 8 in[4..11] = "http/1.1" inlen = 12 The selection algorithm is as follows: 1. If peer's list contains HTTP/2 protocol the library supports, it is selected and returns 1. The following step is not taken. 2. If peer's list contains ``http/1.1``, this function selects ``http/1.1`` and returns 0. The following step is not taken. 3. This function selects nothing and returns -1 (So called non-overlap case). In this case, *out* and *outlen* are left untouched. Selecting ``h2`` means that ``h2`` is written into *\*out* and its length (which is 2) is assigned to *\*outlen*. For ALPN, refer to https://tools.ietf.org/html/rfc7301 To use this method you should do something like:: static int alpn_select_proto_cb(SSL* ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { int rv; rv = nghttp2_select_next_protocol((unsigned char**)out, outlen, in, inlen); if (rv == -1) { return SSL_TLSEXT_ERR_NOACK; } if (rv == 1) { ((MyType*)arg)->http2_selected = 1; } return SSL_TLSEXT_ERR_OK; } ... SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, my_obj); nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_data_source_read_length_callback2.rst0000644000000000000000000000013215077107332030364 xustar0030 mtime=1761382106.500309123 30 atime=1761382106.645308484 30 ctime=1761382109.591298962 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_data_source_read_length_callback2.rst0000644000175100017510000000074615077107332030763 0ustar00runnerrunner nghttp2_session_callbacks_set_data_source_read_length_callback2 =============================================================== Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_data_source_read_length_callback2( nghttp2_session_callbacks *cbs, nghttp2_data_source_read_length_callback2 data_source_read_length_callback) Sets callback function determine the length allowed in :type:`nghttp2_data_source_read_callback2`. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_option_set_max_reserved_remote_streams.rst0000644000000000000000000000013115077107332025263 xustar0030 mtime=1761382106.499309127 30 atime=1761382106.606308656 29 ctime=1761382109.55029908 nghttp2-1.68.0/doc/nghttp2_option_set_max_reserved_remote_streams.rst0000644000175100017510000000175015077107332025657 0ustar00runnerrunner nghttp2_option_set_max_reserved_remote_streams ============================================== Synopsis -------- *#include * .. function:: void nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option, uint32_t val) RFC 7540 does not enforce any limit on the number of incoming reserved streams (in RFC 7540 terms, streams in reserved (remote) state). This only affects client side, since only server can push streams. Malicious server can push arbitrary number of streams, and make client's memory exhausted. This option can set the maximum number of such incoming streams to avoid possible memory exhaustion. If this option is set, and pushed streams are automatically closed on reception, without calling user provided callback, if they exceed the given limit. The default value is 200. If session is configured as server side, this option has no effect. Server can control the number of streams to push. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_deflate_get_dynamic_table_size.rst0000644000000000000000000000013115077107332024046 xustar0030 mtime=1761382106.498309132 30 atime=1761382106.569308819 29 ctime=1761382109.51229919 nghttp2-1.68.0/doc/nghttp2_hd_deflate_get_dynamic_table_size.rst0000644000175100017510000000053315077107332024440 0ustar00runnerrunner nghttp2_hd_deflate_get_dynamic_table_size ========================================= Synopsis -------- *#include * .. function:: size_t nghttp2_hd_deflate_get_dynamic_table_size(nghttp2_hd_deflater *deflater) Returns the used dynamic table size, including the overhead 32 bytes per entry described in RFC 7541. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_pack_extension_callback2.rst0000644000000000000000000000013215077107332026551 xustar0030 mtime=1761382106.501309119 30 atime=1761382106.667308387 30 ctime=1761382109.613298898 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_pack_extension_callback2.rst0000644000175100017510000000071315077107332027142 0ustar00runnerrunner nghttp2_session_callbacks_set_pack_extension_callback2 ====================================================== Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_pack_extension_callback2( nghttp2_session_callbacks *cbs, nghttp2_pack_extension_callback2 pack_extension_callback) Sets callback function invoked when the library asks the application to pack extension frame payload in wire format. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_option_set_no_auto_window_update.rst0000644000000000000000000000013215077107332024064 xustar0030 mtime=1761382106.499309127 30 atime=1761382106.610308639 30 ctime=1761382109.554299069 nghttp2-1.68.0/doc/nghttp2_option_set_no_auto_window_update.rst0000644000175100017510000000116215077107332024454 0ustar00runnerrunner nghttp2_option_set_no_auto_window_update ======================================== Synopsis -------- *#include * .. function:: void nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val) This option prevents the library from sending WINDOW_UPDATE for a connection automatically. If this option is set to nonzero, the library won't send WINDOW_UPDATE for DATA until application calls `nghttp2_session_consume()` to indicate the consumed amount of data. Don't use `nghttp2_submit_window_update()` for this purpose. By default, this option is set to zero. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_recv_callback.rst0000644000000000000000000000013215077107332024414 xustar0030 mtime=1761382106.501309119 30 atime=1761382106.670308374 30 ctime=1761382109.615298892 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_recv_callback.rst0000644000175100017510000000122215077107332025001 0ustar00runnerrunner nghttp2_session_callbacks_set_recv_callback =========================================== Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback) .. warning:: Deprecated. Use `nghttp2_session_callbacks_set_recv_callback2()` with :type:`nghttp2_recv_callback2` instead. Sets callback function invoked when the a session wants to receive data from the remote peer. This callback is not necessary if the application uses solely `nghttp2_session_mem_recv()` to process received data. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_option_del.rst0000644000000000000000000000013215077107332017360 xustar0030 mtime=1761382106.499309127 30 atime=1761382106.601308678 30 ctime=1761382109.545299095 nghttp2-1.68.0/doc/nghttp2_option_del.rst0000644000175100017510000000041115077107332017744 0ustar00runnerrunner nghttp2_option_del ================== Synopsis -------- *#include * .. function:: void nghttp2_option_del(nghttp2_option *option) Frees any resources allocated for *option*. If *option* is ``NULL``, this function does nothing. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_create_idle_stream.rst0000644000000000000000000000013215077107332022602 xustar0030 mtime=1761382106.502309114 30 atime=1761382106.693308273 30 ctime=1761382109.639298823 nghttp2-1.68.0/doc/nghttp2_session_create_idle_stream.rst0000644000175100017510000000076015077107332023175 0ustar00runnerrunner nghttp2_session_create_idle_stream ================================== Synopsis -------- *#include * .. function:: int nghttp2_session_create_idle_stream(nghttp2_session *session, int32_t stream_id, const nghttp2_priority_spec *pri_spec) .. warning:: Deprecated. :rfc:`7540` priorities are deprecated by :rfc:`9113`. Consider migrating to :rfc:`9218` extensible prioritization scheme. This function is noop. It always returns 0. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_submit_priority.rst0000644000000000000000000000013015077107332020466 xustar0030 mtime=1761382106.505309101 30 atime=1761382106.770307933 28 ctime=1761382109.7162986 nghttp2-1.68.0/doc/nghttp2_submit_priority.rst0000644000175100017510000000073615077107332021066 0ustar00runnerrunner nghttp2_submit_priority ======================= Synopsis -------- *#include * .. function:: int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_priority_spec *pri_spec) .. warning:: Deprecated. :rfc:`7540` priorities are deprecated by :rfc:`9113`. Consider migrating to :rfc:`9218` extensible prioritization scheme. This function is noop. It always returns 0. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_check_header_value.rst0000644000000000000000000000013215077107332021005 xustar0030 mtime=1761382106.497309136 30 atime=1761382106.558308868 30 ctime=1761382109.502299219 nghttp2-1.68.0/doc/nghttp2_check_header_value.rst0000644000175100017510000000073615077107332021403 0ustar00runnerrunner nghttp2_check_header_value ========================== Synopsis -------- *#include * .. function:: int nghttp2_check_header_value(const uint8_t *value, size_t len) Returns nonzero if HTTP header field value *value* of length *len* is valid according to http://tools.ietf.org/html/rfc7230#section-3.2 This function is considered obsolete, and application should consider to use `nghttp2_check_header_value_rfc9113()` instead. nghttp2-1.68.0/doc/PaxHeaders/h2load-howto.rst.in0000644000000000000000000000013015077107270016471 xustar0030 mtime=1761382072.964444273 30 atime=1761382104.085319762 28 ctime=1761382109.4742993 nghttp2-1.68.0/doc/h2load-howto.rst.in0000644000175100017510000000006715077107270017066 0ustar00runnerrunner.. include:: @top_srcdir@/doc/sources/h2load-howto.rst nghttp2-1.68.0/doc/PaxHeaders/bash_completion0000644000000000000000000000013215077107335016120 xustar0030 mtime=1761382109.755298488 30 atime=1761382109.794298375 30 ctime=1761382109.755298488 nghttp2-1.68.0/doc/bash_completion/0000755000175100017510000000000015077107335016565 5ustar00runnerrunnernghttp2-1.68.0/doc/bash_completion/PaxHeaders/nghttpd0000644000000000000000000000013115077107270017565 xustar0030 mtime=1761382072.964444273 29 atime=1761382106.80730777 30 ctime=1761382109.752298496 nghttp2-1.68.0/doc/bash_completion/nghttpd0000644000175100017510000000133315077107270020156 0ustar00runnerrunner_nghttpd() { local cur prev split=false COMPREPLY=() COMP_WORDBREAKS=${COMP_WORDBREAKS//=} cmd=${COMP_WORDS[0]} _get_comp_words_by_ref cur prev case $cur in -*) COMPREPLY=( $( compgen -W '--address --daemon --verify-client --htdocs --verbose --no-tls --header-table-size --encoder-header-table-size --color --push --padding --max-concurrent-streams --workers --error-gzip --window-bits --connection-window-bits --dh-param-file --early-response --trailer --hexdump --echo-upload --mime-types-file --no-content-length --groups --ktls --version --help ' -- "$cur" ) ) ;; *) _filedir return 0 esac return 0 } complete -F _nghttpd nghttpd nghttp2-1.68.0/doc/bash_completion/PaxHeaders/h2load0000644000000000000000000000013215077107270017267 xustar0030 mtime=1761382072.963444278 30 atime=1761382106.810307757 30 ctime=1761382109.755298488 nghttp2-1.68.0/doc/bash_completion/h2load0000644000175100017510000000156515077107270017666 0ustar00runnerrunner_h2load() { local cur prev split=false COMPREPLY=() COMP_WORDBREAKS=${COMP_WORDBREAKS//=} cmd=${COMP_WORDS[0]} _get_comp_words_by_ref cur prev case $cur in -*) COMPREPLY=( $( compgen -W '--requests --clients --threads --input-file --max-concurrent-streams --max-frame-size --window-bits --connection-window-bits --header --ciphers --tls13-ciphers --no-tls-proto --data --rate --rate-period --duration --warm-up-time --connection-active-timeout --connection-inactivity-timeout --timing-script-file --base-uri --alpn-list --h1 --header-table-size --encoder-header-table-size --log-file --qlog-file-base --connect-to --rps --groups --no-udp-gso --max-udp-payload-size --ktls --sni --verbose --version --help ' -- "$cur" ) ) ;; *) _filedir return 0 esac return 0 } complete -F _h2load h2load nghttp2-1.68.0/doc/bash_completion/PaxHeaders/nghttp0000644000000000000000000000013215077107270017422 xustar0030 mtime=1761382072.964444273 30 atime=1761382106.806307775 30 ctime=1761382109.751298499 nghttp2-1.68.0/doc/bash_completion/nghttp0000644000175100017510000000141515077107270020013 0ustar00runnerrunner_nghttp() { local cur prev split=false COMPREPLY=() COMP_WORDBREAKS=${COMP_WORDBREAKS//=} cmd=${COMP_WORDS[0]} _get_comp_words_by_ref cur prev case $cur in -*) COMPREPLY=( $( compgen -W '--verbose --null-out --remote-name --timeout --window-bits --connection-window-bits --get-assets --stat --header --trailer --cert --key --data --multiply --upgrade --extpri --peer-max-concurrent-streams --header-table-size --encoder-header-table-size --padding --har --color --continuation --no-content-length --hexdump --no-push --max-concurrent-streams --expect-continue --no-verify-peer --ktls --version --help ' -- "$cur" ) ) ;; *) _filedir return 0 esac return 0 } complete -F _nghttp nghttp nghttp2-1.68.0/doc/bash_completion/PaxHeaders/nghttpx0000644000000000000000000000013115077107270017611 xustar0030 mtime=1761382072.964444273 30 atime=1761382106.808307766 29 ctime=1761382109.75429849 nghttp2-1.68.0/doc/bash_completion/nghttpx0000644000175100017510000001010515077107270020177 0ustar00runnerrunner_nghttpx() { local cur prev split=false COMPREPLY=() COMP_WORDBREAKS=${COMP_WORDBREAKS//=} cmd=${COMP_WORDS[0]} _get_comp_words_by_ref cur prev case $cur in -*) COMPREPLY=( $( compgen -W '--backend --frontend --backlog --backend-address-family --backend-http-proxy-uri --workers --single-thread --read-rate --read-burst --write-rate --write-burst --worker-read-rate --worker-read-burst --worker-write-rate --worker-write-burst --worker-frontend-connections --backend-connections-per-host --backend-connections-per-frontend --rlimit-nofile --rlimit-memlock --backend-request-buffer --backend-response-buffer --fastopen --no-kqueue --frontend-http2-idle-timeout --frontend-http3-idle-timeout --frontend-write-timeout --frontend-keep-alive-timeout --frontend-header-timeout --stream-read-timeout --stream-write-timeout --backend-read-timeout --backend-write-timeout --backend-connect-timeout --backend-keep-alive-timeout --listener-disable-timeout --frontend-http2-setting-timeout --backend-http2-settings-timeout --backend-max-backoff --ciphers --tls13-ciphers --client-ciphers --tls13-client-ciphers --groups --insecure --cacert --private-key-passwd-file --subcert --dh-param-file --alpn-list --verify-client --verify-client-cacert --verify-client-tolerate-expired --client-private-key-file --client-cert-file --tls-min-proto-version --tls-max-proto-version --tls-ticket-key-file --tls-ticket-key-memcached --tls-ticket-key-memcached-address-family --tls-ticket-key-memcached-interval --tls-ticket-key-memcached-max-retry --tls-ticket-key-memcached-max-fail --tls-ticket-key-cipher --tls-ticket-key-memcached-cert-file --tls-ticket-key-memcached-private-key-file --tls-dyn-rec-warmup-threshold --tls-dyn-rec-idle-timeout --no-http2-cipher-block-list --client-no-http2-cipher-block-list --tls-sct-dir --psk-secrets --client-psk-secrets --tls-no-postpone-early-data --tls-max-early-data --tls-ktls --frontend-http2-max-concurrent-streams --backend-http2-max-concurrent-streams --frontend-http2-window-size --frontend-http2-connection-window-size --backend-http2-window-size --backend-http2-connection-window-size --http2-no-cookie-crumbling --padding --no-server-push --frontend-http2-optimize-write-buffer-size --frontend-http2-optimize-window-size --frontend-http2-encoder-dynamic-table-size --frontend-http2-decoder-dynamic-table-size --backend-http2-encoder-dynamic-table-size --backend-http2-decoder-dynamic-table-size --http2-proxy --log-level --accesslog-file --accesslog-syslog --accesslog-format --accesslog-write-early --errorlog-file --errorlog-syslog --syslog-facility --add-x-forwarded-for --strip-incoming-x-forwarded-for --no-add-x-forwarded-proto --no-strip-incoming-x-forwarded-proto --add-forwarded --strip-incoming-forwarded --forwarded-by --forwarded-for --no-via --no-strip-incoming-early-data --no-location-rewrite --host-rewrite --altsvc --http2-altsvc --add-request-header --add-response-header --request-header-field-buffer --max-request-header-fields --response-header-field-buffer --max-response-header-fields --error-page --server-name --no-server-rewrite --redirect-https-port --require-http-scheme --api-max-request-body --dns-cache-timeout --dns-lookup-timeout --dns-max-try --frontend-max-requests --frontend-http2-dump-request-header --frontend-http2-dump-response-header --frontend-frame-debug --daemon --pid-file --user --single-process --max-worker-processes --worker-process-grace-shutdown-period --mruby-file --ignore-per-pattern-mruby-error --frontend-quic-idle-timeout --frontend-quic-debug-log --quic-bpf-program-file --frontend-quic-early-data --frontend-quic-qlog-dir --frontend-quic-require-token --frontend-quic-congestion-controller --frontend-quic-secret-file --quic-server-id --frontend-quic-initial-rtt --no-quic-bpf --frontend-http3-window-size --frontend-http3-connection-window-size --frontend-http3-max-window-size --frontend-http3-max-connection-window-size --frontend-http3-max-concurrent-streams --conf --include --version --help ' -- "$cur" ) ) ;; *) _filedir return 0 esac return 0 } complete -F _nghttpx nghttpx nghttp2-1.68.0/doc/PaxHeaders/nghttp2_check_authority.rst0000644000000000000000000000013215077107332020411 xustar0030 mtime=1761382106.497309136 30 atime=1761382106.556308876 30 ctime=1761382109.500299225 nghttp2-1.68.0/doc/nghttp2_check_authority.rst0000644000175100017510000000145315077107332021004 0ustar00runnerrunner nghttp2_check_authority ======================= Synopsis -------- *#include * .. function:: int nghttp2_check_authority(const uint8_t *value, size_t len) Returns nonzero if the *value* which is supposed to be the value of the :authority or host header field is valid according to https://tools.ietf.org/html/rfc3986#section-3.2 Note that :authority and host field values are not authority. They do not include userinfo in RFC 3986, see https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2, that is, it does not include '@'. This function treats '@' as a valid character. *value* is valid if it merely consists of the allowed characters. In particular, it does not check whether *value* follows the syntax of authority. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_get_stream_remote_window_size.rst0000644000000000000000000000013115077107332025114 xustar0029 mtime=1761382106.50330911 30 atime=1761382106.720308154 30 ctime=1761382109.665298748 nghttp2-1.68.0/doc/nghttp2_session_get_stream_remote_window_size.rst0000644000175100017510000000137015077107332025506 0ustar00runnerrunner nghttp2_session_get_stream_remote_window_size ============================================= Synopsis -------- *#include * .. function:: int32_t nghttp2_session_get_stream_remote_window_size( nghttp2_session *session, int32_t stream_id) Returns the remote window size for a given stream *stream_id*. This is the amount of flow-controlled payload (e.g., DATA) that the local endpoint can send without stream level WINDOW_UPDATE. There is also connection level flow control, so the effective size of payload that the local endpoint can actually send is min(`nghttp2_session_get_stream_remote_window_size()`, `nghttp2_session_get_remote_window_size()`). This function returns -1 if it fails. nghttp2-1.68.0/doc/PaxHeaders/building-android-binary.rst.in0000644000000000000000000000013015077107270020657 xustar0030 mtime=1761382072.964444273 28 atime=1761382104.0993197 30 ctime=1761382109.470299312 nghttp2-1.68.0/doc/building-android-binary.rst.in0000644000175100017510000000010215077107270021242 0ustar00runnerrunner.. include:: @top_srcdir@/doc/sources/building-android-binary.rst nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_deflate_hd.rst0000644000000000000000000000013215077107332017756 xustar0030 mtime=1761382106.498309132 30 atime=1761382106.574308797 30 ctime=1761382109.518299173 nghttp2-1.68.0/doc/nghttp2_hd_deflate_hd.rst0000644000175100017510000000251715077107332020353 0ustar00runnerrunner nghttp2_hd_deflate_hd ===================== Synopsis -------- *#include * .. function:: ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, size_t buflen, const nghttp2_nv *nva, size_t nvlen) .. warning:: Deprecated. Use `nghttp2_hd_deflate_hd2()` instead. Deflates the *nva*, which has the *nvlen* name/value pairs, into the *buf* of length *buflen*. If *buf* is not large enough to store the deflated header block, this function fails with :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller should use `nghttp2_hd_deflate_bound()` to know the upper bound of buffer size required to deflate given header name/value pairs. Once this function fails, subsequent call of this function always returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. After this function returns, it is safe to delete the *nva*. This function returns the number of bytes written to *buf* if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` Deflation process has failed. :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` The provided *buflen* size is too small to hold the output. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_recv_callback2.rst0000644000000000000000000000013115077107332024475 xustar0030 mtime=1761382106.501309119 29 atime=1761382106.67130837 30 ctime=1761382109.617298887 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_recv_callback2.rst0000644000175100017510000000100115077107332025056 0ustar00runnerrunner nghttp2_session_callbacks_set_recv_callback2 ============================================ Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_recv_callback2( nghttp2_session_callbacks *cbs, nghttp2_recv_callback2 recv_callback) Sets callback function invoked when the a session wants to receive data from the remote peer. This callback is not necessary if the application uses solely `nghttp2_session_mem_recv2()` to process received data. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_inflate_new.rst0000644000000000000000000000013215077107332020172 xustar0030 mtime=1761382106.498309132 30 atime=1761382106.594308709 30 ctime=1761382109.538299115 nghttp2-1.68.0/doc/nghttp2_hd_inflate_new.rst0000644000175100017510000000075015077107332020564 0ustar00runnerrunner nghttp2_hd_inflate_new ====================== Synopsis -------- *#include * .. function:: int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr) Initializes *\*inflater_ptr* for inflating name/values pairs. If this function fails, *\*inflater_ptr* is left untouched. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_set_local_window_size.rst0000644000000000000000000000013215077107332023355 xustar0030 mtime=1761382106.504309105 30 atime=1761382106.735308088 30 ctime=1761382109.681298702 nghttp2-1.68.0/doc/nghttp2_session_set_local_window_size.rst0000644000175100017510000000342015077107332023744 0ustar00runnerrunner nghttp2_session_set_local_window_size ===================================== Synopsis -------- *#include * .. function:: int nghttp2_session_set_local_window_size(nghttp2_session *session, uint8_t flags, int32_t stream_id, int32_t window_size) Set local window size (local endpoints's window size) to the given *window_size* for the given stream denoted by *stream_id*. To change connection level window size, specify 0 to *stream_id*. To increase window size, this function may submit WINDOW_UPDATE frame to transmission queue. The *flags* is currently ignored and should be :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. This sounds similar to `nghttp2_submit_window_update()`, but there are 2 differences. The first difference is that this function takes the absolute value of window size to set, rather than the delta. To change the window size, this may be easier to use since the application just declares the intended window size, rather than calculating delta. The second difference is that `nghttp2_submit_window_update()` affects the received bytes count which has not acked yet. By the specification of `nghttp2_submit_window_update()`, to strictly increase the local window size, we have to submit delta including all received bytes count, which might not be desirable in some cases. On the other hand, this function does not affect the received bytes count. It just sets the local window size to the given value. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The *stream_id* is negative. :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_on_begin_headers_callback.rst0000644000000000000000000000013215077107332026730 xustar0030 mtime=1761382106.501309119 30 atime=1761382106.651308458 30 ctime=1761382109.596298947 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_on_begin_headers_callback.rst0000644000175100017510000000070515077107332027322 0ustar00runnerrunner nghttp2_session_callbacks_set_on_begin_headers_callback ======================================================= Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_on_begin_headers_callback( nghttp2_session_callbacks *cbs, nghttp2_on_begin_headers_callback on_begin_headers_callback) Sets callback function invoked when the reception of header block in HEADERS or PUSH_PROMISE is started. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_consume_stream.rst0000644000000000000000000000013215077107332022013 xustar0030 mtime=1761382106.502309114 30 atime=1761382106.692308277 30 ctime=1761382109.637298829 nghttp2-1.68.0/doc/nghttp2_session_consume_stream.rst0000644000175100017510000000147215077107332022407 0ustar00runnerrunner nghttp2_session_consume_stream ============================== Synopsis -------- *#include * .. function:: int nghttp2_session_consume_stream(nghttp2_session *session, int32_t stream_id, size_t size) Like `nghttp2_session_consume()`, but this only tells library that *size* bytes were consumed only for stream denoted by *stream_id*. Note that HTTP/2 maintains connection and stream level flow control windows independently. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The *stream_id* is 0. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` Automatic WINDOW_UPDATE is not disabled. nghttp2-1.68.0/doc/PaxHeaders/nghttp.10000644000000000000000000000013215077107270014413 xustar0030 mtime=1761382072.964444273 30 atime=1761382106.801307797 30 ctime=1761382109.746298514 nghttp2-1.68.0/doc/nghttp.10000644000175100017510000001533615077107270015013 0ustar00runnerrunner.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "NGHTTP" "1" "Oct 25, 2025" "1.68.0" "nghttp2" .SH NAME nghttp \- HTTP/2 client .SH SYNOPSIS .sp \fBnghttp\fP [OPTIONS]... ... .SH DESCRIPTION .sp HTTP/2 client .INDENT 0.0 .TP .B Specify URI to access. .UNINDENT .SH OPTIONS .INDENT 0.0 .TP .B \-v, \-\-verbose Print debug information such as reception and transmission of frames and name/value pairs. Specifying this option multiple times increases verbosity. .UNINDENT .INDENT 0.0 .TP .B \-n, \-\-null\-out Discard downloaded data. .UNINDENT .INDENT 0.0 .TP .B \-O, \-\-remote\-name Save download data in the current directory. The filename is derived from URI. If URI ends with \(aq\fI/\fP\(aq, \(aqindex.html\(aq is used as a filename. Not implemented yet. .UNINDENT .INDENT 0.0 .TP .B \-t, \-\-timeout= Timeout each request after . Set 0 to disable timeout. .UNINDENT .INDENT 0.0 .TP .B \-w, \-\-window\-bits= Sets the stream level initial window size to 2**\-1. .UNINDENT .INDENT 0.0 .TP .B \-W, \-\-connection\-window\-bits= Sets the connection level initial window size to 2**\-1. .UNINDENT .INDENT 0.0 .TP .B \-a, \-\-get\-assets Download assets such as stylesheets, images and script files linked from the downloaded resource. Only links whose origins are the same with the linking resource will be downloaded. nghttp prioritizes resources using HTTP/2 dependency based priority. The priority order, from highest to lowest, is html itself, css, javascript and images. .UNINDENT .INDENT 0.0 .TP .B \-s, \-\-stat Print statistics. .UNINDENT .INDENT 0.0 .TP .B \-H, \-\-header=
Add a header to the requests. Example: \fI\%\-H\fP\(aq:method: PUT\(aq .UNINDENT .INDENT 0.0 .TP .B \-\-trailer=
Add a trailer header to the requests.
must not include pseudo header field (header field name starting with \(aq:\(aq). To send trailer, one must use \fI\%\-d\fP option to send request body. Example: \fI\%\-\-trailer\fP \(aqfoo: bar\(aq. .UNINDENT .INDENT 0.0 .TP .B \-\-cert= Use the specified client certificate file. The file must be in PEM format. .UNINDENT .INDENT 0.0 .TP .B \-\-key= Use the client private key file. The file must be in PEM format. .UNINDENT .INDENT 0.0 .TP .B \-d, \-\-data= Post FILE to server. If \(aq\-\(aq is given, data will be read from stdin. .UNINDENT .INDENT 0.0 .TP .B \-m, \-\-multiply= Request each URI times. By default, same URI is not requested twice. This option disables it too. .UNINDENT .INDENT 0.0 .TP .B \-u, \-\-upgrade Perform HTTP Upgrade for HTTP/2. This option is ignored if the request URI has https scheme. If \fI\%\-d\fP is used, the HTTP upgrade request is performed with OPTIONS method. .UNINDENT .INDENT 0.0 .TP .B \-\-extpri= Sets RFC 9218 priority of given URI. must be the wire format of priority header field (e.g., \(dqu=3,i\(dq). This option can be used multiple times, and N\-th \fI\%\-\-extpri\fP option sets priority of N\-th URI in the command line. If the number of this option is less than the number of URI, the last option value is repeated. If there is no \fI\%\-\-extpri\fP option, urgency is 3, and incremental is false. .UNINDENT .INDENT 0.0 .TP .B \-M, \-\-peer\-max\-concurrent\-streams= Use as SETTINGS_MAX_CONCURRENT_STREAMS value of remote endpoint as if it is received in SETTINGS frame. .sp Default: \fB100\fP .UNINDENT .INDENT 0.0 .TP .B \-c, \-\-header\-table\-size= Specify decoder header table size. If this option is used multiple times, and the minimum value among the given values except for last one is strictly less than the last value, that minimum value is set in SETTINGS frame payload before the last value, to simulate multiple header table size change. .UNINDENT .INDENT 0.0 .TP .B \-\-encoder\-header\-table\-size= Specify encoder header table size. The decoder (server) specifies the maximum dynamic table size it accepts. Then the negotiated dynamic table size is the minimum of this option value and the value which server specified. .UNINDENT .INDENT 0.0 .TP .B \-b, \-\-padding= Add at most bytes to a frame payload as padding. Specify 0 to disable padding. .UNINDENT .INDENT 0.0 .TP .B \-r, \-\-har= Output HTTP transactions in HAR format. If \(aq\-\(aq is given, data is written to stdout. .UNINDENT .INDENT 0.0 .TP .B \-\-color Force colored log output. .UNINDENT .INDENT 0.0 .TP .B \-\-continuation Send large header to test CONTINUATION. .UNINDENT .INDENT 0.0 .TP .B \-\-no\-content\-length Don\(aqt send content\-length header field. .UNINDENT .INDENT 0.0 .TP .B \-\-hexdump Display the incoming traffic in hexadecimal (Canonical hex+ASCII display). If SSL/TLS is used, decrypted data are used. .UNINDENT .INDENT 0.0 .TP .B \-\-no\-push Disable server push. .UNINDENT .INDENT 0.0 .TP .B \-\-max\-concurrent\-streams= The number of concurrent pushed streams this client accepts. .UNINDENT .INDENT 0.0 .TP .B \-\-expect\-continue Perform an Expect/Continue handshake: wait to send DATA (up to a short timeout) until the server sends a 100 Continue interim response. This option is ignored unless combined with the \fI\%\-d\fP option. .UNINDENT .INDENT 0.0 .TP .B \-y, \-\-no\-verify\-peer Suppress warning on server certificate verification failure. .UNINDENT .INDENT 0.0 .TP .B \-\-ktls Enable ktls. .UNINDENT .INDENT 0.0 .TP .B \-\-version Display version information and exit. .UNINDENT .INDENT 0.0 .TP .B \-h, \-\-help Display this help and exit. .UNINDENT .sp The argument is an integer and an optional unit (e.g., 10K is 10 * 1024). Units are K, M and G (powers of 1024). .sp The argument is an integer and an optional unit (e.g., 1s is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms (hours, minutes, seconds and milliseconds, respectively). If a unit is omitted, a second is used as unit. .SH SEE ALSO .sp \fBnghttpd(1)\fP, \fBnghttpx(1)\fP, \fBh2load(1)\fP .SH AUTHOR Tatsuhiro Tsujikawa .SH COPYRIGHT 2012, 2015, 2016, Tatsuhiro Tsujikawa .\" Generated by docutils manpage writer. . nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_get_remote_settings.rst0000644000000000000000000000013115077107332023040 xustar0029 mtime=1761382106.50330911 30 atime=1761382106.710308198 30 ctime=1761382109.655298777 nghttp2-1.68.0/doc/nghttp2_session_get_remote_settings.rst0000644000175100017510000000060615077107332023433 0ustar00runnerrunner nghttp2_session_get_remote_settings =================================== Synopsis -------- *#include * .. function:: uint32_t nghttp2_session_get_remote_settings( nghttp2_session *session, nghttp2_settings_id id) Returns the value of SETTINGS *id* notified by a remote endpoint. The *id* must be one of values defined in :enum:`nghttp2_settings_id`. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_unpack_extension_callback.rst0000644000000000000000000000013215077107332027032 xustar0030 mtime=1761382106.502309114 30 atime=1761382106.679308334 30 ctime=1761382109.625298863 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_unpack_extension_callback.rst0000644000175100017510000000072515077107332027426 0ustar00runnerrunner nghttp2_session_callbacks_set_unpack_extension_callback ======================================================= Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_unpack_extension_callback( nghttp2_session_callbacks *cbs, nghttp2_unpack_extension_callback unpack_extension_callback) Sets callback function invoked when the library asks the application to unpack extension frame payload from wire format. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_pack_settings_payload2.rst0000644000000000000000000000013115077107332021654 xustar0030 mtime=1761382106.500309123 30 atime=1761382106.628308559 29 ctime=1761382109.57129902 nghttp2-1.68.0/doc/nghttp2_pack_settings_payload2.rst0000644000175100017510000000224715077107332022252 0ustar00runnerrunner nghttp2_pack_settings_payload2 ============================== Synopsis -------- *#include * .. function:: nghttp2_ssize nghttp2_pack_settings_payload2( uint8_t *buf, size_t buflen, const nghttp2_settings_entry *iv, size_t niv) Serializes the SETTINGS values *iv* in the *buf*. The size of the *buf* is specified by *buflen*. The number of entries in the *iv* array is given by *niv*. The required space in *buf* for the *niv* entries is ``6*niv`` bytes and if the given buffer is too small, an error is returned. This function is used mainly for creating a SETTINGS payload to be sent with the ``HTTP2-Settings`` header field in an HTTP Upgrade request. The data written in *buf* is NOT base64url encoded and the application is responsible for encoding. This function returns the number of bytes written in *buf*, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The *iv* contains duplicate settings ID or invalid value. :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` The provided *buflen* size is too small to hold the output. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_option_set_no_recv_client_magic.rst0000644000000000000000000000013115077107332023617 xustar0030 mtime=1761382106.499309127 30 atime=1761382106.614308621 29 ctime=1761382109.55729906 nghttp2-1.68.0/doc/nghttp2_option_set_no_recv_client_magic.rst0000644000175100017510000000202415077107332024206 0ustar00runnerrunner nghttp2_option_set_no_recv_client_magic ======================================= Synopsis -------- *#include * .. function:: void nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val) By default, nghttp2 library, if configured as server, requires first 24 bytes of client magic byte string (MAGIC). In most cases, this will simplify the implementation of server. But sometimes server may want to detect the application protocol based on first few bytes on clear text communication. If this option is used with nonzero *val*, nghttp2 library does not handle MAGIC. It still checks following SETTINGS frame. This means that applications should deal with MAGIC by themselves. If this option is not used or used with zero value, if MAGIC does not match :macro:`NGHTTP2_CLIENT_MAGIC`, `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` will return error :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC`, which is fatal error. nghttp2-1.68.0/doc/PaxHeaders/macros.rst0000644000000000000000000000013215077107332015042 xustar0030 mtime=1761382106.507309092 30 atime=1761382106.552308894 30 ctime=1761382109.496299236 nghttp2-1.68.0/doc/macros.rst0000644000175100017510000000776615077107332015452 0ustar00runnerrunner Macros ====== .. macro:: NGHTTP2_VERSION Version number of the nghttp2 library release .. macro:: NGHTTP2_VERSION_NUM Numerical representation of the version number of the nghttp2 library release. This is a 24 bit number with 8 bits for major number, 8 bits for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. .. macro:: NGHTTP2_PROTO_VERSION_ID The protocol version identification string of this library supports. This identifier is used if HTTP/2 is used over TLS. .. macro:: NGHTTP2_PROTO_VERSION_ID_LEN The length of :macro:`NGHTTP2_PROTO_VERSION_ID`. .. macro:: NGHTTP2_PROTO_ALPN The serialized form of ALPN protocol identifier this library supports. Notice that first byte is the length of following protocol identifier. This is the same wire format of `TLS ALPN extension `_. This is useful to process incoming ALPN tokens in wire format. .. macro:: NGHTTP2_PROTO_ALPN_LEN The length of :macro:`NGHTTP2_PROTO_ALPN`. .. macro:: NGHTTP2_CLEARTEXT_PROTO_VERSION_ID The protocol version identification string of this library supports. This identifier is used if HTTP/2 is used over cleartext TCP. .. macro:: NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN The length of :macro:`NGHTTP2_CLEARTEXT_PROTO_VERSION_ID`. .. macro:: NGHTTP2_VERSION_AGE The age of :type:`nghttp2_info` .. macro:: NGHTTP2_DEFAULT_WEIGHT .. warning:: Deprecated. :rfc:`7540` priorities are deprecated by :rfc:`9113`. Consider migrating to :rfc:`9218` extensible prioritization scheme. The default weight of stream dependency. .. macro:: NGHTTP2_MAX_WEIGHT .. warning:: Deprecated. :rfc:`7540` priorities are deprecated by :rfc:`9113`. Consider migrating to :rfc:`9218` extensible prioritization scheme. The maximum weight of stream dependency. .. macro:: NGHTTP2_MIN_WEIGHT .. warning:: Deprecated. :rfc:`7540` priorities are deprecated by :rfc:`9113`. Consider migrating to :rfc:`9218` extensible prioritization scheme. The minimum weight of stream dependency. .. macro:: NGHTTP2_MAX_WINDOW_SIZE The maximum window size .. macro:: NGHTTP2_INITIAL_WINDOW_SIZE The initial window size for stream level flow control. .. macro:: NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE The initial window size for connection level flow control. .. macro:: NGHTTP2_DEFAULT_HEADER_TABLE_SIZE The default header table size. .. macro:: NGHTTP2_CLIENT_MAGIC The client magic string, which is the first 24 bytes byte string of client connection preface. .. macro:: NGHTTP2_CLIENT_MAGIC_LEN The length of :macro:`NGHTTP2_CLIENT_MAGIC`. .. macro:: NGHTTP2_DEFAULT_MAX_SETTINGS The default max number of settings per SETTINGS frame .. macro:: NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS .. warning:: Deprecated. The initial max concurrent streams is 0xffffffffu. Default maximum number of incoming concurrent streams. Use `nghttp2_submit_settings()` with :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS` to change the maximum number of incoming concurrent streams. .. note:: The maximum number of outgoing concurrent streams is 100 by default. .. macro:: NGHTTP2_EXTPRI_DEFAULT_URGENCY :macro:`NGHTTP2_EXTPRI_DEFAULT_URGENCY` is the default urgency level for :rfc:`9218` extensible priorities. .. macro:: NGHTTP2_EXTPRI_URGENCY_HIGH :macro:`NGHTTP2_EXTPRI_URGENCY_HIGH` is the highest urgency level for :rfc:`9218` extensible priorities. .. macro:: NGHTTP2_EXTPRI_URGENCY_LOW :macro:`NGHTTP2_EXTPRI_URGENCY_LOW` is the lowest urgency level for :rfc:`9218` extensible priorities. .. macro:: NGHTTP2_EXTPRI_URGENCY_LEVELS :macro:`NGHTTP2_EXTPRI_URGENCY_LEVELS` is the number of urgency levels for :rfc:`9218` extensible priorities. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_submit_settings.rst0000644000000000000000000000013115077107332020446 xustar0030 mtime=1761382106.505309101 29 atime=1761382106.78030789 30 ctime=1761382109.726298572 nghttp2-1.68.0/doc/nghttp2_submit_settings.rst0000644000175100017510000000247215077107332021044 0ustar00runnerrunner nghttp2_submit_settings ======================= Synopsis -------- *#include * .. function:: int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags, const nghttp2_settings_entry *iv, size_t niv) Stores local settings and submits SETTINGS frame. The *iv* is the pointer to the array of :type:`nghttp2_settings_entry`. The *niv* indicates the number of :type:`nghttp2_settings_entry`. The *flags* is currently ignored and should be :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. This function does not take ownership of the *iv*. This function copies all the elements in the *iv*. While updating individual stream's local window size, if the window size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, RST_STREAM is issued against such a stream. SETTINGS with :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` is automatically submitted by the library and application could not send it at its will. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The *iv* contains invalid value (e.g., initial window size strictly greater than (1 << 31) - 1. :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_rcbuf_incref.rst0000644000000000000000000000013215077107332017653 xustar0030 mtime=1761382106.500309123 30 atime=1761382106.635308528 30 ctime=1761382109.581298991 nghttp2-1.68.0/doc/nghttp2_rcbuf_incref.rst0000644000175100017510000000032715077107332020245 0ustar00runnerrunner nghttp2_rcbuf_incref ==================== Synopsis -------- *#include * .. function:: void nghttp2_rcbuf_incref(nghttp2_rcbuf *rcbuf) Increments the reference count of *rcbuf* by 1. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_priority_spec_init.rst0000644000000000000000000000013215077107332021142 xustar0030 mtime=1761382106.500309123 30 atime=1761382106.631308546 30 ctime=1761382109.576299005 nghttp2-1.68.0/doc/nghttp2_priority_spec_init.rst0000644000175100017510000000127715077107332021541 0ustar00runnerrunner nghttp2_priority_spec_init ========================== Synopsis -------- *#include * .. function:: void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, int32_t stream_id, int32_t weight, int exclusive) .. warning:: Deprecated. :rfc:`7540` priorities are deprecated by :rfc:`9113`. Consider migrating to :rfc:`9218` extensible prioritization scheme. Initializes *pri_spec* with the *stream_id* of the stream to depend on with *weight* and its exclusive flag. If *exclusive* is nonzero, exclusive flag is set. The *weight* must be in [:macro:`NGHTTP2_MIN_WEIGHT`, :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_get_hd_deflate_dynamic_table_size.rst0000644000000000000000000000013215077107332025612 xustar0030 mtime=1761382106.502309114 30 atime=1761382106.701308237 30 ctime=1761382109.646298803 nghttp2-1.68.0/doc/nghttp2_session_get_hd_deflate_dynamic_table_size.rst0000644000175100017510000000060215077107332026200 0ustar00runnerrunner nghttp2_session_get_hd_deflate_dynamic_table_size ================================================= Synopsis -------- *#include * .. function:: size_t nghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session) Returns the current dynamic table size of HPACK deflater including the overhead 32 bytes per entry described in RFC 7541. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_pack_extension_callback.rst0000644000000000000000000000013215077107332026467 xustar0030 mtime=1761382106.501309119 30 atime=1761382106.666308392 30 ctime=1761382109.612298901 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_pack_extension_callback.rst0000644000175100017510000000116715077107332027064 0ustar00runnerrunner nghttp2_session_callbacks_set_pack_extension_callback ===================================================== Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_pack_extension_callback( nghttp2_session_callbacks *cbs, nghttp2_pack_extension_callback pack_extension_callback) .. warning:: Deprecated. Use `nghttp2_session_callbacks_set_pack_extension_callback2()` with :type:`nghttp2_pack_extension_callback2` instead. Sets callback function invoked when the library asks the application to pack extension frame payload in wire format. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_option_set_max_send_header_block_length.rst0000644000000000000000000000013215077107332025310 xustar0030 mtime=1761382106.499309127 30 atime=1761382106.607308652 30 ctime=1761382109.551299077 nghttp2-1.68.0/doc/nghttp2_option_set_max_send_header_block_length.rst0000644000175100017510000000126415077107332025703 0ustar00runnerrunner nghttp2_option_set_max_send_header_block_length =============================================== Synopsis -------- *#include * .. function:: void nghttp2_option_set_max_send_header_block_length(nghttp2_option *option, size_t val) This option sets the maximum length of header block (a set of header fields per one HEADERS frame) to send. The length of a given set of header fields is calculated using `nghttp2_hd_deflate_bound()`. The default value is 64KiB. If application attempts to send header fields larger than this limit, the transmission of the frame fails with error code :enum:`nghttp2_error.NGHTTP2_ERR_FRAME_SIZE_ERROR`. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_inflate_del.rst0000644000000000000000000000013115077107332020144 xustar0030 mtime=1761382106.498309132 30 atime=1761382106.583308757 29 ctime=1761382109.52629915 nghttp2-1.68.0/doc/nghttp2_hd_inflate_del.rst0000644000175100017510000000035215077107332020535 0ustar00runnerrunner nghttp2_hd_inflate_del ====================== Synopsis -------- *#include * .. function:: void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater) Deallocates any resources allocated for *inflater*. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_option_set_max_continuations.rst0000644000000000000000000000013115077107332023230 xustar0030 mtime=1761382106.499309127 30 atime=1761382106.620308594 29 ctime=1761382109.56429904 nghttp2-1.68.0/doc/nghttp2_option_set_max_continuations.rst0000644000175100017510000000074415077107332023626 0ustar00runnerrunner nghttp2_option_set_max_continuations ==================================== Synopsis -------- *#include * .. function:: void nghttp2_option_set_max_continuations(nghttp2_option *option, size_t val) This function sets the maximum number of CONTINUATION frames following an incoming HEADER frame. If more than those frames are received, the remote endpoint is considered to be misbehaving and session will be closed. The default value is 8. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_submit_headers.rst0000644000000000000000000000013215077107332020222 xustar0030 mtime=1761382106.505309101 30 atime=1761382106.766307951 30 ctime=1761382109.712298612 nghttp2-1.68.0/doc/nghttp2_submit_headers.rst0000644000175100017510000000704315077107332020616 0ustar00runnerrunner nghttp2_submit_headers ====================== Synopsis -------- *#include * .. function:: int32_t nghttp2_submit_headers( nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen, void *stream_user_data) Submits HEADERS frame. The *flags* is bitwise OR of the following values: * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` If *flags* includes :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM`, this frame has END_STREAM flag set. The library handles the CONTINUATION frame internally and it correctly sets END_HEADERS to the last sequence of the PUSH_PROMISE or CONTINUATION frame. If the *stream_id* is -1, this frame is assumed as request (i.e., request HEADERS frame which opens new stream). In this case, the assigned stream ID will be returned. Otherwise, specify stream ID in *stream_id*. The *pri_spec* is ignored. The *nva* is an array of name/value pair :type:`nghttp2_nv` with *nvlen* elements. The application is responsible to include required pseudo-header fields (header field whose name starts with ":") in *nva* and must place pseudo-headers before regular header fields. This function creates copies of all name/value pairs in *nva*. It also lower-cases all names in *nva*. The order of elements in *nva* is preserved. For header fields with :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name and value are not copied respectively. With :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to pass header field name in lowercase. The application should maintain the references to them until :type:`nghttp2_on_frame_send_callback` or :type:`nghttp2_on_frame_not_send_callback` is called. The *stream_user_data* is a pointer to an arbitrary data which is associated to the stream this frame will open. Therefore it is only used if this frame opens streams, in other words, it changes stream state from idle or reserved to open. This function is low-level in a sense that the application code can specify flags directly. For usual HTTP request, `nghttp2_submit_request2()` is useful. Likewise, for HTTP response, prefer `nghttp2_submit_response2()`. This function returns newly assigned stream ID if it succeeds and *stream_id* is -1. Otherwise, this function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` No stream ID is available because maximum stream ID was reached. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The *stream_id* is 0. :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` DATA or HEADERS has been already submitted and not fully processed yet. This happens if stream denoted by *stream_id* is in reserved state. :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` The *stream_id* is -1, and *session* is server session. .. warning:: This function returns assigned stream ID if it succeeds and *stream_id* is -1. But that stream is not opened yet. The application must not submit frame to that stream ID before :type:`nghttp2_before_frame_send_callback` is called for this frame. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_version.rst0000644000000000000000000000013215077107332016711 xustar0030 mtime=1761382106.506309097 30 atime=1761382106.785307868 30 ctime=1761382109.731298557 nghttp2-1.68.0/doc/nghttp2_version.rst0000644000175100017510000000076015077107332017304 0ustar00runnerrunner nghttp2_version =============== Synopsis -------- *#include * .. function:: nghttp2_info *nghttp2_version(int least_version) Returns a pointer to a nghttp2_info struct with version information about the run-time library in use. The *least_version* argument can be set to a 24 bit numerical value for the least accepted version number and if the condition is not met, this function will return a ``NULL``. Pass in 0 to skip the version checking. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_new.rst0000644000000000000000000000013215077107332021557 xustar0030 mtime=1761382106.500309123 30 atime=1761382106.642308497 30 ctime=1761382109.587298973 nghttp2-1.68.0/doc/nghttp2_session_callbacks_new.rst0000644000175100017510000000123415077107332022147 0ustar00runnerrunner nghttp2_session_callbacks_new ============================= Synopsis -------- *#include * .. function:: int nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr) Initializes *\*callbacks_ptr* with NULL values. The initialized object can be used when initializing multiple :type:`nghttp2_session` objects. When the application finished using this object, it can use `nghttp2_session_callbacks_del()` to free its memory. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_deflate_change_table_size.rst0000644000000000000000000000013215077107332023011 xustar0030 mtime=1761382106.497309136 30 atime=1761382106.566308832 30 ctime=1761382109.510299196 nghttp2-1.68.0/doc/nghttp2_hd_deflate_change_table_size.rst0000644000175100017510000000175215077107332023406 0ustar00runnerrunner nghttp2_hd_deflate_change_table_size ==================================== Synopsis -------- *#include * .. function:: int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, size_t settings_max_dynamic_table_size) Changes header table size of the *deflater* to *settings_max_dynamic_table_size* bytes. This may trigger eviction in the dynamic table. The *settings_max_dynamic_table_size* should be the value received in SETTINGS_HEADER_TABLE_SIZE. The deflater never uses more memory than ``max_deflate_dynamic_table_size`` bytes specified in `nghttp2_hd_deflate_new()`. Therefore, if *settings_max_dynamic_table_size* > ``max_deflate_dynamic_table_size``, resulting maximum table size becomes ``max_deflate_dynamic_table_size``. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_deflate_hd_vec.rst0000644000000000000000000000013215077107332020613 xustar0030 mtime=1761382106.498309132 30 atime=1761382106.576308788 30 ctime=1761382109.520299167 nghttp2-1.68.0/doc/nghttp2_hd_deflate_hd_vec.rst0000644000175100017510000000301415077107332021201 0ustar00runnerrunner nghttp2_hd_deflate_hd_vec ========================= Synopsis -------- *#include * .. function:: ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, const nghttp2_vec *vec, size_t veclen, const nghttp2_nv *nva, size_t nvlen) .. warning:: Deprecated. Use `nghttp2_hd_deflate_hd_vec2()` instead. Deflates the *nva*, which has the *nvlen* name/value pairs, into the *veclen* size of buf vector *vec*. The each size of buffer must be set in len field of :type:`nghttp2_vec`. If and only if one chunk is filled up completely, next chunk will be used. If *vec* is not large enough to store the deflated header block, this function fails with :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller should use `nghttp2_hd_deflate_bound()` to know the upper bound of buffer size required to deflate given header name/value pairs. Once this function fails, subsequent call of this function always returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. After this function returns, it is safe to delete the *nva*. This function returns the number of bytes written to *vec* if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` Deflation process has failed. :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` The provided *buflen* size is too small to hold the output. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_submit_shutdown_notice.rst0000644000000000000000000000013215077107332022023 xustar0030 mtime=1761382106.506309097 30 atime=1761382106.781307885 30 ctime=1761382109.727298569 nghttp2-1.68.0/doc/nghttp2_submit_shutdown_notice.rst0000644000175100017510000000302215077107332022410 0ustar00runnerrunner nghttp2_submit_shutdown_notice ============================== Synopsis -------- *#include * .. function:: int nghttp2_submit_shutdown_notice(nghttp2_session *session) Signals to the client that the server started graceful shutdown procedure. This function is only usable for server. If this function is called with client side session, this function returns :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. To gracefully shutdown HTTP/2 session, server should call this function to send GOAWAY with last_stream_id (1u << 31) - 1. And after some delay (e.g., 1 RTT), send another GOAWAY with the stream ID that the server has some processing using `nghttp2_submit_goaway()`. See also `nghttp2_session_get_last_proc_stream_id()`. Unlike `nghttp2_submit_goaway()`, this function just sends GOAWAY and does nothing more. This is a mere indication to the client that session shutdown is imminent. The application should call `nghttp2_submit_goaway()` with appropriate last_stream_id after this call. If one or more GOAWAY frame have been already sent by either `nghttp2_submit_goaway()` or `nghttp2_session_terminate_session()`, this function has no effect. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` The *session* is initialized as client. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_upgrade2.rst0000644000000000000000000000013215077107332020500 xustar0030 mtime=1761382106.504309105 30 atime=1761382106.744308048 30 ctime=1761382109.690298676 nghttp2-1.68.0/doc/nghttp2_session_upgrade2.rst0000644000175100017510000000404115077107332021067 0ustar00runnerrunner nghttp2_session_upgrade2 ======================== Synopsis -------- *#include * .. function:: int nghttp2_session_upgrade2(nghttp2_session *session, const uint8_t *settings_payload, size_t settings_payloadlen, int head_request, void *stream_user_data) Performs post-process of HTTP Upgrade request. This function can be called from both client and server, but the behavior is very different in each other. If called from client side, the *settings_payload* must be the value sent in ``HTTP2-Settings`` header field and must be decoded by base64url decoder. The *settings_payloadlen* is the length of *settings_payload*. The *settings_payload* is unpacked and its setting values will be submitted using `nghttp2_submit_settings()`. This means that the client application code does not need to submit SETTINGS by itself. The stream with stream ID=1 is opened and the *stream_user_data* is used for its stream_user_data. The opened stream becomes half-closed (local) state. If called from server side, the *settings_payload* must be the value received in ``HTTP2-Settings`` header field and must be decoded by base64url decoder. The *settings_payloadlen* is the length of *settings_payload*. It is treated as if the SETTINGS frame with that payload is received. Thus, callback functions for the reception of SETTINGS frame will be invoked. The stream with stream ID=1 is opened. The *stream_user_data* is ignored. The opened stream becomes half-closed (remote). If the request method is HEAD, pass nonzero value to *head_request*. Otherwise, pass 0. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The *settings_payload* is badly formed. :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` The stream ID 1 is already used or closed; or is not available. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_is_fatal.rst0000644000000000000000000000013215077107332017006 xustar0030 mtime=1761382106.499309127 30 atime=1761382106.598308691 30 ctime=1761382109.542299103 nghttp2-1.68.0/doc/nghttp2_is_fatal.rst0000644000175100017510000000036215077107332017377 0ustar00runnerrunner nghttp2_is_fatal ================ Synopsis -------- *#include * .. function:: int nghttp2_is_fatal(int lib_error_code) Returns nonzero if the :type:`nghttp2_error` library error code *lib_error* is fatal. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_submit_extension.rst0000644000000000000000000000013215077107332020623 xustar0030 mtime=1761382106.505309101 30 atime=1761382106.763307964 30 ctime=1761382109.709298621 nghttp2-1.68.0/doc/nghttp2_submit_extension.rst0000644000175100017510000000365415077107332021223 0ustar00runnerrunner nghttp2_submit_extension ======================== Synopsis -------- *#include * .. function:: int nghttp2_submit_extension(nghttp2_session *session, uint8_t type, uint8_t flags, int32_t stream_id, void *payload) Submits extension frame. Application can pass arbitrary frame flags and stream ID in *flags* and *stream_id* respectively. The *payload* is opaque pointer, and it can be accessible though ``frame->ext.payload`` in :type:`nghttp2_pack_extension_callback2`. The library will not own passed *payload* pointer. The application must set :type:`nghttp2_pack_extension_callback2` using `nghttp2_session_callbacks_set_pack_extension_callback2()`. The application should retain the memory pointed by *payload* until the transmission of extension frame is done (which is indicated by :type:`nghttp2_on_frame_send_callback`), or transmission fails (which is indicated by :type:`nghttp2_on_frame_not_send_callback`). If application does not touch this memory region after packing it into a wire format, application can free it inside :type:`nghttp2_pack_extension_callback2`. The standard HTTP/2 frame cannot be sent with this function, so *type* must be strictly grater than 0x9. Otherwise, this function will fail with error code :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` If :type:`nghttp2_pack_extension_callback2` is not set. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` If *type* specifies standard HTTP/2 frame type. The frame types in the rage [0x0, 0x9], both inclusive, are standard HTTP/2 frame type, and cannot be sent using this function. :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory nghttp2-1.68.0/doc/PaxHeaders/h2load.1.rst0000644000000000000000000000013215077107270015067 xustar0030 mtime=1761382072.964444273 30 atime=1761382106.551308898 30 ctime=1761382109.494299242 nghttp2-1.68.0/doc/h2load.1.rst0000644000175100017510000003463315077107270015470 0ustar00runnerrunner .. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY. .. program:: h2load h2load(1) ========= SYNOPSIS -------- **h2load** [OPTIONS]... [URI]... DESCRIPTION ----------- benchmarking tool for HTTP/2 server .. describe:: Specify URI to access. Multiple URIs can be specified. URIs are used in this order for each client. All URIs are used, then first URI is used and then 2nd URI, and so on. The scheme, host and port in the subsequent URIs, if present, are ignored. Those in the first URI are used solely. Definition of a base URI overrides all scheme, host or port values. OPTIONS ------- .. option:: -n, --requests= Number of requests across all clients. If it is used with :option:`--timing-script-file` option, this option specifies the number of requests each client performs rather than the number of requests across all clients. This option is ignored if timing-based benchmarking is enabled (see :option:`--duration` option). Default: ``1`` .. option:: -c, --clients= Number of concurrent clients. With :option:`-r` option, this specifies the maximum number of connections to be made. Default: ``1`` .. option:: -t, --threads= Number of native threads. Default: ``1`` .. option:: -i, --input-file= Path of a file with multiple URIs are separated by EOLs. This option will disable URIs getting from command-line. If '-' is given as , URIs will be read from stdin. URIs are used in this order for each client. All URIs are used, then first URI is used and then 2nd URI, and so on. The scheme, host and port in the subsequent URIs, if present, are ignored. Those in the first URI are used solely. Definition of a base URI overrides all scheme, host or port values. .. option:: -m, --max-concurrent-streams= Max concurrent streams to issue per session. When http/1.1 is used, this specifies the number of HTTP pipelining requests in-flight. Default: ``1`` .. option:: -f, --max-frame-size= Maximum frame size that the local endpoint is willing to receive. Default: ``16K`` .. option:: -w, --window-bits= Sets the stream level initial window size to (2\*\*)-1. For QUIC, is capped to 26 (roughly 64MiB). It defaults to 24 (16MiB) for QUIC, and 30 for other protocols. .. option:: -W, --connection-window-bits= Sets the connection level initial window size to (2\*\*)-1. Default: ``30`` .. option:: -H, --header=
Add/Override a header to the requests. .. option:: --ciphers= Set allowed cipher list for TLSv1.2 or earlier. The format of the string is described in OpenSSL ciphers(1). Default: ``ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384`` .. option:: --tls13-ciphers= Set allowed cipher list for TLSv1.3. The format of the string is described in OpenSSL ciphers(1). Default: ``TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256`` .. option:: -p, --no-tls-proto= Specify ALPN identifier of the protocol to be used when accessing http URI without SSL/TLS. Available protocols: h2c and http/1.1 Default: ``h2c`` .. option:: -d, --data= Post FILE to server. The request method is changed to POST. For http/1.1 connection, if :option:`-d` is used, the maximum number of in-flight pipelined requests is set to 1. .. option:: -r, --rate= Specifies the fixed rate at which connections are created. The rate must be a positive integer, representing the number of connections to be made per rate period. The maximum number of connections to be made is given in :option:`-c` option. This rate will be distributed among threads as evenly as possible. For example, with :option:`-t`\2 and :option:`-r`\4, each thread gets 2 connections per period. When the rate is 0, the program will run as it normally does, creating connections at whatever variable rate it wants. The default value for this option is 0. :option:`-r` and :option:`\-D` are mutually exclusive. .. option:: --rate-period= Specifies the time period between creating connections. The period must be a positive number, representing the length of the period in time. This option is ignored if the rate option is not used. The default value for this option is 1s. .. option:: -D, --duration= Specifies the main duration for the measurements in case of timing-based benchmarking. :option:`-D` and :option:`\-r` are mutually exclusive. .. option:: --warm-up-time= Specifies the time period before starting the actual measurements, in case of timing-based benchmarking. Needs to provided along with :option:`-D` option. .. option:: -T, --connection-active-timeout= Specifies the maximum time that h2load is willing to keep a connection open, regardless of the activity on said connection. must be a positive integer, specifying the amount of time to wait. When no timeout value is set (either active or inactive), h2load will keep a connection open indefinitely, waiting for a response. .. option:: -N, --connection-inactivity-timeout= Specifies the amount of time that h2load is willing to wait to see activity on a given connection. must be a positive integer, specifying the amount of time to wait. When no timeout value is set (either active or inactive), h2load will keep a connection open indefinitely, waiting for a response. .. option:: --timing-script-file= Path of a file containing one or more lines separated by EOLs. Each script line is composed of two tab-separated fields. The first field represents the time offset from the start of execution, expressed as a positive value of milliseconds with microsecond resolution. The second field represents the URI. This option will disable URIs getting from command-line. If '-' is given as , script lines will be read from stdin. Script lines are used in order for each client. If :option:`-n` is given, it must be less than or equal to the number of script lines, larger values are clamped to the number of script lines. If :option:`-n` is not given, the number of requests will default to the number of script lines. The scheme, host and port defined in the first URI are used solely. Values contained in other URIs, if present, are ignored. Definition of a base URI overrides all scheme, host or port values. :option:`--timing-script-file` and :option:`\--rps` are mutually exclusive. .. option:: -B, --base-uri=(|unix:) Specify URI from which the scheme, host and port will be used for all requests. The base URI overrides all values defined either at the command line or inside input files. If argument starts with "unix:", then the rest of the argument will be treated as UNIX domain socket path. The connection is made through that path instead of TCP. In this case, scheme is inferred from the first URI appeared in the command line or inside input files as usual. .. option:: --alpn-list= Comma delimited list of ALPN protocol identifier sorted in the order of preference. That means most desirable protocol comes first. The parameter must be delimited by a single comma only and any white spaces are treated as a part of protocol string. Default: ``h2,http/1.1`` .. option:: --h1 Short hand for :option:`--alpn-list`\=http/1.1 :option:`--no-tls-proto`\=http/1.1, which effectively force http/1.1 for both http and https URI. .. option:: --header-table-size= Specify decoder header table size. Default: ``4K`` .. option:: --encoder-header-table-size= Specify encoder header table size. The decoder (server) specifies the maximum dynamic table size it accepts. Then the negotiated dynamic table size is the minimum of this option value and the value which server specified. Default: ``4K`` .. option:: --log-file= Write per-request information to a file as tab-separated columns: start time as microseconds since epoch; HTTP status code; microseconds until end of response. More columns may be added later. Rows are ordered by end-of- response time when using one worker thread, but may appear slightly out of order with multiple threads due to buffering. Status code is -1 for failed streams. .. option:: --qlog-file-base= Enable qlog output and specify base file name for qlogs. Qlog is emitted for each connection. For a given base name "base", each output file name becomes "base.M.N.sqlog" where M is worker ID and N is client ID (e.g. "base.0.3.sqlog"). Only effective in QUIC runs. .. option:: --connect-to=[:] Host and port to connect instead of using the authority in . .. option:: --rps= Specify request per second for each client. :option:`--rps` and :option:`--timing-script-file` are mutually exclusive. .. option:: --groups= Specify the supported groups. Default: ``X25519:P-256:P-384:P-521`` .. option:: --no-udp-gso Disable UDP GSO. .. option:: --max-udp-payload-size= Specify the maximum outgoing UDP datagram payload size. .. option:: --ktls Enable ktls. .. option:: --sni= Send in TLS SNI, overriding the host name specified in URI. .. option:: -v, --verbose Output debug information. .. option:: --version Display version information and exit. .. option:: -h, --help Display this help and exit. The argument is an integer and an optional unit (e.g., 10K is 10 * 1024). Units are K, M and G (powers of 1024). The argument is an integer and an optional unit (e.g., 1s is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms (hours, minutes, seconds and milliseconds, respectively). If a unit is omitted, a second is used as unit. .. _h2load-1-output: OUTPUT ------ requests total The number of requests h2load was instructed to make. started The number of requests h2load has started. done The number of requests completed. succeeded The number of requests completed successfully. Only HTTP status code 2xx or 3xx are considered as success. failed The number of requests failed, including HTTP level failures (non-successful HTTP status code). errored The number of requests failed, except for HTTP level failures. This is the subset of the number reported in ``failed`` and most likely the network level failures or stream was reset by RST_STREAM. timeout The number of requests whose connection timed out before they were completed. This is the subset of the number reported in ``errored``. status codes The number of status code h2load received. traffic total The number of bytes received from the server "on the wire". If requests were made via TLS, this value is the number of decrypted bytes. headers The number of response header bytes from the server without decompression. The ``space savings`` shows efficiency of header compression. Let ``decompressed(headers)`` to the number of bytes used for header fields after decompression. The ``space savings`` is calculated by (1 - ``headers`` / ``decompressed(headers)``) * 100. For HTTP/1.1, this is usually 0.00%, since it does not have header compression. For HTTP/2, it shows some insightful numbers. data The number of response body bytes received from the server. time for request min The minimum time taken for request and response. max The maximum time taken for request and response. mean The mean time taken for request and response. sd The standard deviation of the time taken for request and response. +/- sd The fraction of the number of requests within standard deviation range (mean +/- sd) against total number of successful requests. time for connect min The minimum time taken to connect to a server including TLS handshake. max The maximum time taken to connect to a server including TLS handshake. mean The mean time taken to connect to a server including TLS handshake. sd The standard deviation of the time taken to connect to a server. +/- sd The fraction of the number of connections within standard deviation range (mean +/- sd) against total number of successful connections. time for 1st byte (of (decrypted in case of TLS) application data) min The minimum time taken to get 1st byte from a server. max The maximum time taken to get 1st byte from a server. mean The mean time taken to get 1st byte from a server. sd The standard deviation of the time taken to get 1st byte from a server. +/- sd The fraction of the number of connections within standard deviation range (mean +/- sd) against total number of successful connections. req/s min The minimum request per second among all clients. max The maximum request per second among all clients. mean The mean request per second among all clients. sd The standard deviation of request per second among all clients. server. +/- sd The fraction of the number of connections within standard deviation range (mean +/- sd) against total number of successful connections. FLOW CONTROL ------------ h2load sets large flow control window by default, and effectively disables flow control to avoid under utilization of server performance. To set smaller flow control window, use :option:`-w` and :option:`-W` options. For example, use ``-w16 -W16`` to set default window size described in HTTP/2 protocol specification. SEE ALSO -------- :manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`nghttpx(1)` nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst0000644000000000000000000000013215077107332027271 xustar0030 mtime=1761382106.501309119 30 atime=1761382106.652308453 30 ctime=1761382109.597298944 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst0000644000175100017510000000066515077107332027670 0ustar00runnerrunner nghttp2_session_callbacks_set_on_data_chunk_recv_callback ========================================================= Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback) Sets callback function invoked when a chunk of data in DATA frame is received. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_submit_data2.rst0000644000000000000000000000013215077107332017602 xustar0030 mtime=1761382106.505309101 30 atime=1761382106.762307969 30 ctime=1761382109.708298623 nghttp2-1.68.0/doc/nghttp2_submit_data2.rst0000644000175100017510000000372615077107332020202 0ustar00runnerrunner nghttp2_submit_data2 ==================== Synopsis -------- *#include * .. function:: int nghttp2_submit_data2(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_data_provider2 *data_prd) Submits one or more DATA frames to the stream *stream_id*. The data to be sent are provided by *data_prd*. If *flags* contains :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM`, the last DATA frame has END_STREAM flag set. This function does not take ownership of the *data_prd*. The function copies the members of the *data_prd*. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` DATA or HEADERS has been already submitted and not fully processed yet. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The *stream_id* is 0. :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_CLOSED` The stream was already closed; or the *stream_id* is invalid. .. note:: Currently, only one DATA or HEADERS is allowed for a stream at a time. Submitting these frames more than once before first DATA or HEADERS is finished results in :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` error code. The earliest callback which tells that previous frame is done is :type:`nghttp2_on_frame_send_callback`. In side that callback, new data can be submitted using `nghttp2_submit_data2()`. Of course, all data except for last one must not have :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` flag set in *flags*. This sounds a bit complicated, and we recommend to use `nghttp2_submit_request2()` and `nghttp2_submit_response2()` to avoid this cascading issue. The experience shows that for HTTP use, these two functions are enough to implement both client and server. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_check_request_allowed.rst0000644000000000000000000000013215077107332023323 xustar0030 mtime=1761382106.502309114 30 atime=1761382106.683308317 30 ctime=1761382109.628298855 nghttp2-1.68.0/doc/nghttp2_session_check_request_allowed.rst0000644000175100017510000000143115077107332023712 0ustar00runnerrunner nghttp2_session_check_request_allowed ===================================== Synopsis -------- *#include * .. function:: int nghttp2_session_check_request_allowed(nghttp2_session *session) Returns nonzero if new request can be sent from local endpoint. This function return 0 if request is not allowed for this session. There are several reasons why request is not allowed. Some of the reasons are: session is server; stream ID has been spent; GOAWAY has been sent or received. The application can call `nghttp2_submit_request2()` without consulting this function. In that case, `nghttp2_submit_request2()` may return error. Or, request is failed to sent, and :type:`nghttp2_on_stream_close_callback` is called. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_send_callback2.rst0000644000000000000000000000013215077107332024470 xustar0030 mtime=1761382106.502309114 30 atime=1761382106.676308348 30 ctime=1761382109.622298872 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_send_callback2.rst0000644000175100017510000000077515077107332025071 0ustar00runnerrunner nghttp2_session_callbacks_set_send_callback2 ============================================ Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_send_callback2( nghttp2_session_callbacks *cbs, nghttp2_send_callback2 send_callback) Sets callback function invoked when a session wants to send data to the remote peer. This callback is not necessary if the application uses solely `nghttp2_session_mem_send2()` to serialize data to transmit. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_deflate_del.rst0000644000000000000000000000013215077107332020127 xustar0030 mtime=1761382106.497309136 30 atime=1761382106.568308823 30 ctime=1761382109.511299193 nghttp2-1.68.0/doc/nghttp2_hd_deflate_del.rst0000644000175100017510000000035215077107332020517 0ustar00runnerrunner nghttp2_hd_deflate_del ====================== Synopsis -------- *#include * .. function:: void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater) Deallocates any resources allocated for *deflater*. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_deflate_get_table_entry.rst0000644000000000000000000000013215077107332022532 xustar0030 mtime=1761382106.498309132 30 atime=1761382106.573308802 30 ctime=1761382109.516299179 nghttp2-1.68.0/doc/nghttp2_hd_deflate_get_table_entry.rst0000644000175100017510000000116715077107332023127 0ustar00runnerrunner nghttp2_hd_deflate_get_table_entry ================================== Synopsis -------- *#include * .. function:: const nghttp2_nv * nghttp2_hd_deflate_get_table_entry(nghttp2_hd_deflater *deflater, size_t idx) Returns the table entry denoted by *idx* from header table of *deflater*. The *idx* is 1-based, and idx=1 returns first entry of static table. idx=62 returns first entry of dynamic table if it exists. Specifying idx=0 is error, and this function returns NULL. If *idx* is strictly greater than the number of entries the tables contain, this function returns NULL. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_stream_get_weight.rst0000644000000000000000000000013215077107332020725 xustar0030 mtime=1761382106.505309101 30 atime=1761382106.757307991 30 ctime=1761382109.703298638 nghttp2-1.68.0/doc/nghttp2_stream_get_weight.rst0000644000175100017510000000065615077107332021324 0ustar00runnerrunner nghttp2_stream_get_weight ========================= Synopsis -------- *#include * .. function:: int32_t nghttp2_stream_get_weight(nghttp2_stream *stream) .. warning:: Deprecated. :rfc:`7540` priorities are deprecated by :rfc:`9113`. Consider migrating to :rfc:`9218` extensible prioritization scheme. This function always returns :macro:`NGHTTP2_DEFAULT_WEIGHT`. nghttp2-1.68.0/doc/PaxHeaders/package_README.rst.in0000644000000000000000000000013215077107270016574 xustar0030 mtime=1761382072.966444264 30 atime=1761382104.016320066 30 ctime=1761382109.480299283 nghttp2-1.68.0/doc/package_README.rst.in0000644000175100017510000000004515077107270017163 0ustar00runnerrunner.. include:: @top_srcdir@/README.rst nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_error_callback.rst0000644000000000000000000000013215077107332024606 xustar0030 mtime=1761382106.501309119 30 atime=1761382106.647308476 30 ctime=1761382109.592298959 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_error_callback.rst0000644000175100017510000000123115077107332025173 0ustar00runnerrunner nghttp2_session_callbacks_set_error_callback ============================================ Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_error_callback( nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback) .. warning:: Deprecated. Use `nghttp2_session_callbacks_set_error_callback2()` with :type:`nghttp2_error_callback2` instead. Sets callback function invoked when library tells error message to the application. If both :type:`nghttp2_error_callback` and :type:`nghttp2_error_callback2` are set, the latter takes precedence. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_error_callback2.rst0000644000000000000000000000013215077107332024670 xustar0030 mtime=1761382106.501309119 30 atime=1761382106.648308471 30 ctime=1761382109.594298953 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_error_callback2.rst0000644000175100017510000000101215077107332025252 0ustar00runnerrunner nghttp2_session_callbacks_set_error_callback2 ============================================= Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_error_callback2( nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2) Sets callback function invoked when library tells error code, and message to the application. If both :type:`nghttp2_error_callback` and :type:`nghttp2_error_callback2` are set, the latter takes precedence. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst0000644000000000000000000000013215077107332027770 xustar0030 mtime=1761382106.501309119 30 atime=1761382106.661308414 30 ctime=1761382109.606298919 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst0000644000175100017510000000100215077107332030351 0ustar00runnerrunner nghttp2_session_callbacks_set_on_invalid_frame_recv_callback ============================================================ Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback) Sets callback function invoked by `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` when an invalid non-DATA frame is received. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_on_frame_not_send_callback.rst0000644000000000000000000000013115077107332027133 xustar0030 mtime=1761382106.501309119 29 atime=1761382106.65530844 30 ctime=1761382109.600298936 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_on_frame_not_send_callback.rst0000644000175100017510000000066715077107332027535 0ustar00runnerrunner nghttp2_session_callbacks_set_on_frame_not_send_callback ======================================================== Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_on_frame_not_send_callback( nghttp2_session_callbacks *cbs, nghttp2_on_frame_not_send_callback on_frame_not_send_callback) Sets callback function invoked when a non-DATA frame is not sent because of an error. nghttp2-1.68.0/doc/PaxHeaders/mkapiref.py0000644000000000000000000000013215077107270015175 xustar0030 mtime=1761382072.964444273 30 atime=1761382106.472309246 30 ctime=1761382109.487299262 nghttp2-1.68.0/doc/mkapiref.py0000755000175100017510000002572715077107270015605 0ustar00runnerrunner#!/usr/bin/env python3 # -*- coding: utf-8 -*- # nghttp2 - HTTP/2 C Library # # Copyright (c) 2020 nghttp2 contributors # Copyright (c) 2020 ngtcp2 contributors # Copyright (c) 2012 Tatsuhiro Tsujikawa # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # Generates API reference from C source code. import re, sys, argparse, os.path class FunctionDoc: def __init__(self, name, content, domain, filename): self.name = name self.content = content self.domain = domain if self.domain == 'function': self.funcname = re.search(r'(nghttp2_[^ )]+)\(', self.name).group(1) self.filename = filename def write(self, out): out.write('.. {}:: {}\n'.format(self.domain, self.name)) out.write('\n') for line in self.content: out.write(' {}\n'.format(line)) class StructDoc: def __init__(self, name, content, members, member_domain): self.name = name self.content = content self.members = members self.member_domain = member_domain def write(self, out): if self.name: out.write('.. type:: {}\n'.format(self.name)) out.write('\n') for line in self.content: out.write(' {}\n'.format(line)) out.write('\n') for name, content in self.members: out.write(' .. {}:: {}\n'.format(self.member_domain, name)) out.write('\n') for line in content: out.write(' {}\n'.format(line)) out.write('\n') class EnumDoc: def __init__(self, name, content, members): self.name = name self.content = content self.members = members def write(self, out): if self.name: out.write('.. type:: {}\n'.format(self.name)) out.write('\n') for line in self.content: out.write(' {}\n'.format(line)) out.write('\n') for name, content in self.members: out.write(' .. enum:: {}\n'.format(name)) out.write('\n') for line in content: out.write(' {}\n'.format(line)) out.write('\n') class MacroDoc: def __init__(self, name, content): self.name = name self.content = content def write(self, out): out.write('''.. macro:: {}\n'''.format(self.name)) out.write('\n') for line in self.content: out.write(' {}\n'.format(line)) class MacroSectionDoc: def __init__(self, content): self.content = content def write(self, out): out.write('\n') c = ' '.join(self.content).strip() out.write(c) out.write('\n') out.write('-' * len(c)) out.write('\n\n') class TypedefDoc: def __init__(self, name, content): self.name = name self.content = content def write(self, out): out.write('''.. type:: {}\n'''.format(self.name)) out.write('\n') for line in self.content: out.write(' {}\n'.format(line)) def make_api_ref(infile): macros = [] enums = [] types = [] functions = [] while True: line = infile.readline() if not line: break elif line == '/**\n': line = infile.readline() doctype = line.split()[1] if doctype == '@function': functions.append(process_function('function', infile)) elif doctype == '@functypedef': types.append(process_function('type', infile)) elif doctype == '@struct' or doctype == '@union': types.append(process_struct(infile)) elif doctype == '@enum': enums.append(process_enum(infile)) elif doctype == '@macro': macros.append(process_macro(infile)) elif doctype == '@macrosection': macros.append(process_macrosection(infile)) elif doctype == '@typedef': types.append(process_typedef(infile)) return macros, enums, types, functions def output( title, indexfile, macrosfile, enumsfile, typesfile, funcsdir, macros, enums, types, functions): indexfile.write(''' {title} {titledecoration} .. toctree:: :maxdepth: 1 {macros} {enums} {types} '''.format( title=title, titledecoration='='*len(title), macros=os.path.splitext(os.path.basename(macrosfile.name))[0], enums=os.path.splitext(os.path.basename(enumsfile.name))[0], types=os.path.splitext(os.path.basename(typesfile.name))[0], )) for doc in functions: indexfile.write(' {}\n'.format(doc.funcname)) macrosfile.write(''' Macros ====== ''') for doc in macros: doc.write(macrosfile) enumsfile.write(''' Enums ===== ''') for doc in enums: doc.write(enumsfile) typesfile.write(''' Types (structs, unions and typedefs) ==================================== ''') for doc in types: doc.write(typesfile) for doc in functions: with open(os.path.join(funcsdir, doc.funcname + '.rst'), 'w') as f: f.write(''' {funcname} {secul} Synopsis -------- *#include * '''.format(funcname=doc.funcname, secul='='*len(doc.funcname), filename=doc.filename)) doc.write(f) def process_macro(infile): content = read_content(infile) line = infile.readline() macro_name = line.split()[1] return MacroDoc(macro_name, content) def process_macrosection(infile): content = read_content(infile) return MacroSectionDoc(content) def process_typedef(infile): content = read_content(infile) typedef = infile.readline() typedef = re.sub(r';\n$', '', typedef) typedef = re.sub(r'typedef ', '', typedef) return TypedefDoc(typedef, content) def process_enum(infile): members = [] enum_name = None content = read_content(infile) while True: line = infile.readline() if not line: break elif re.match(r'\s*/\*\*\n', line): member_content = read_content(infile) line = infile.readline() items = line.split() member_name = items[0].rstrip(',') if len(items) >= 3: member_content.insert(0, '(``{}``) '\ .format(' '.join(items[2:]).rstrip(','))) members.append((member_name, member_content)) elif line.startswith('}'): enum_name = line.rstrip().split()[1] enum_name = re.sub(r';$', '', enum_name) break return EnumDoc(enum_name, content, members) def process_struct(infile): members = [] struct_name = None content = read_content(infile) while True: line = infile.readline() if not line: break elif re.match(r'\s*/\*\*\n', line): member_content = read_content(infile) line = infile.readline() member_name = line.rstrip().rstrip(';') members.append((member_name, member_content)) elif line.startswith('}') or\ (line.startswith('typedef ') and line.endswith(';\n')): if line.startswith('}'): index = 1 else: index = 3 struct_name = line.rstrip().split()[index] struct_name = re.sub(r';$', '', struct_name) break return StructDoc(struct_name, content, members, 'member') def process_function(domain, infile): content = read_content(infile) func_proto = [] while True: line = infile.readline() if not line: break elif line == '\n': break else: func_proto.append(line) func_proto = ''.join(func_proto) func_proto = re.sub(r';\n$', '', func_proto) func_proto = re.sub(r'\s+', ' ', func_proto) func_proto = re.sub(r'NGHTTP2_EXTERN ', '', func_proto) func_proto = re.sub(r'typedef ', '', func_proto) filename = os.path.basename(infile.name) return FunctionDoc(func_proto, content, domain, filename) def read_content(infile): content = [] while True: line = infile.readline() if not line: break if re.match(r'\s*\*/\n', line): break else: content.append(transform_content(line.rstrip())) return content def arg_repl(matchobj): return '*{}*'.format(matchobj.group(1).replace('*', '\\*')) def transform_content(content): content = re.sub(r'^\s+\* ?', '', content) content = re.sub(r'\|([^\s|]+)\|', arg_repl, content) return content if __name__ == '__main__': parser = argparse.ArgumentParser(description="Generate API reference") parser.add_argument('--title', default='API Reference', help='title of index page') parser.add_argument('index', type=argparse.FileType('w'), help='index output file') parser.add_argument('macros', type=argparse.FileType('w'), help='macros section output file. The filename should be macros.rst') parser.add_argument('enums', type=argparse.FileType('w'), help='enums section output file. The filename should be enums.rst') parser.add_argument('types', type=argparse.FileType('w'), help='types section output file. The filename should be types.rst') parser.add_argument('funcsdir', help='functions doc output dir') parser.add_argument('files', nargs='+', type=argparse.FileType('r'), help='source file') args = parser.parse_args() macros = [] enums = [] types = [] funcs = [] for infile in args.files: m, e, t, f = make_api_ref(infile) macros.extend(m) enums.extend(e) types.extend(t) funcs.extend(f) funcs.sort(key=lambda x: x.funcname) output( args.title, args.index, args.macros, args.enums, args.types, args.funcsdir, macros, enums, types, funcs) nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_deflate_hd_vec2.rst0000644000000000000000000000013215077107332020675 xustar0030 mtime=1761382106.498309132 30 atime=1761382106.578308779 30 ctime=1761382109.521299164 nghttp2-1.68.0/doc/nghttp2_hd_deflate_hd_vec2.rst0000644000175100017510000000267415077107332021276 0ustar00runnerrunner nghttp2_hd_deflate_hd_vec2 ========================== Synopsis -------- *#include * .. function:: nghttp2_ssize nghttp2_hd_deflate_hd_vec2( nghttp2_hd_deflater *deflater, const nghttp2_vec *vec, size_t veclen, const nghttp2_nv *nva, size_t nvlen) Deflates the *nva*, which has the *nvlen* name/value pairs, into the *veclen* size of buf vector *vec*. The each size of buffer must be set in len field of :type:`nghttp2_vec`. If and only if one chunk is filled up completely, next chunk will be used. If *vec* is not large enough to store the deflated header block, this function fails with :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller should use `nghttp2_hd_deflate_bound()` to know the upper bound of buffer size required to deflate given header name/value pairs. Once this function fails, subsequent call of this function always returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. After this function returns, it is safe to delete the *nva*. This function returns the number of bytes written to *vec* if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` Deflation process has failed. :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` The provided *buflen* size is too small to hold the output. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_select_padding_callback2.rst0000644000000000000000000000013215077107332026504 xustar0030 mtime=1761382106.501309119 30 atime=1761382106.674308356 30 ctime=1761382109.619298881 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_select_padding_callback2.rst0000644000175100017510000000075015077107332027076 0ustar00runnerrunner nghttp2_session_callbacks_set_select_padding_callback2 ====================================================== Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_select_padding_callback2( nghttp2_session_callbacks *cbs, nghttp2_select_padding_callback2 select_padding_callback) Sets callback function invoked when the library asks application how many padding bytes are required for the transmission of the given frame. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_inflate_change_table_size.rst0000644000000000000000000000013215077107332023027 xustar0030 mtime=1761382106.498309132 30 atime=1761382106.582308762 30 ctime=1761382109.525299153 nghttp2-1.68.0/doc/nghttp2_hd_inflate_change_table_size.rst0000644000175100017510000000224015077107332023415 0ustar00runnerrunner nghttp2_hd_inflate_change_table_size ==================================== Synopsis -------- *#include * .. function:: int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, size_t settings_max_dynamic_table_size) Changes header table size in the *inflater*. This may trigger eviction in the dynamic table. The *settings_max_dynamic_table_size* should be the value transmitted in SETTINGS_HEADER_TABLE_SIZE. This function must not be called while header block is being inflated. In other words, this function must be called after initialization of *inflater*, but before calling `nghttp2_hd_inflate_hd3()`, or after `nghttp2_hd_inflate_end_headers()`. Otherwise, `NGHTTP2_ERR_INVALID_STATE` was returned. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` The function is called while header block is being inflated. Probably, application missed to call `nghttp2_hd_inflate_end_headers()`. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_consume.rst0000644000000000000000000000013115077107332020437 xustar0030 mtime=1761382106.502309114 29 atime=1761382106.68930829 30 ctime=1761382109.635298835 nghttp2-1.68.0/doc/nghttp2_session_consume.rst0000644000175100017510000000205115077107332021026 0ustar00runnerrunner nghttp2_session_consume ======================= Synopsis -------- *#include * .. function:: int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id, size_t size) Tells the *session* that *size* bytes for a stream denoted by *stream_id* were consumed by application and are ready to WINDOW_UPDATE. The consumed bytes are counted towards both connection and stream level WINDOW_UPDATE (see `nghttp2_session_consume_connection()` and `nghttp2_session_consume_stream()` to update consumption independently). This function is intended to be used without automatic window update (see `nghttp2_option_set_no_auto_window_update()`). This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The *stream_id* is 0. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` Automatic WINDOW_UPDATE is not disabled. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_resume_data.rst0000644000000000000000000000013115077107332021257 xustar0029 mtime=1761382106.50330911 30 atime=1761382106.729308114 30 ctime=1761382109.674298722 nghttp2-1.68.0/doc/nghttp2_session_resume_data.rst0000644000175100017510000000112015077107332021642 0ustar00runnerrunner nghttp2_session_resume_data =========================== Synopsis -------- *#include * .. function:: int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) Puts back previously deferred DATA frame in the stream *stream_id* to the outbound queue. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The stream does not exist; or no deferred data exist. :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_select_alpn.rst0000644000000000000000000000013215077107332017515 xustar0030 mtime=1761382106.500309123 30 atime=1761382106.639308511 30 ctime=1761382109.585298979 nghttp2-1.68.0/doc/nghttp2_select_alpn.rst0000644000175100017510000000411015077107332020101 0ustar00runnerrunner nghttp2_select_alpn =================== Synopsis -------- *#include * .. function:: int nghttp2_select_alpn(const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen) A helper function for dealing with ALPN in server side. The *in* contains peer's protocol list in preferable order. The format of *in* is length-prefixed and not null-terminated. For example, ``h2`` and ``http/1.1`` stored in *in* like this:: in[0] = 2 in[1..2] = "h2" in[3] = 8 in[4..11] = "http/1.1" inlen = 12 The selection algorithm is as follows: 1. If peer's list contains HTTP/2 protocol the library supports, it is selected and returns 1. The following step is not taken. 2. If peer's list contains ``http/1.1``, this function selects ``http/1.1`` and returns 0. The following step is not taken. 3. This function selects nothing and returns -1 (So called non-overlap case). In this case, *out* and *outlen* are left untouched. Selecting ``h2`` means that ``h2`` is written into *\*out* and its length (which is 2) is assigned to *\*outlen*. For ALPN, refer to https://tools.ietf.org/html/rfc7301 To use this method you should do something like:: static int alpn_select_proto_cb(SSL* ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { int rv; rv = nghttp2_select_alpn(out, outlen, in, inlen); if (rv == -1) { return SSL_TLSEXT_ERR_NOACK; } if (rv == 1) { ((MyType*)arg)->http2_selected = 1; } return SSL_TLSEXT_ERR_OK; } ... SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, my_obj); nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_get_stream_remote_close.rst0000644000000000000000000000013115077107332023660 xustar0029 mtime=1761382106.50330911 30 atime=1761382106.718308163 30 ctime=1761382109.664298751 nghttp2-1.68.0/doc/nghttp2_session_get_stream_remote_close.rst0000644000175100017510000000057415077107332024257 0ustar00runnerrunner nghttp2_session_get_stream_remote_close ======================================= Synopsis -------- *#include * .. function:: int nghttp2_session_get_stream_remote_close(nghttp2_session *session, int32_t stream_id) Returns 1 if remote peer half closed the given stream *stream_id*. Returns 0 if it did not. Returns -1 if no such stream exists. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_get_hd_inflate_dynamic_table_size.rst0000644000000000000000000000013215077107332025630 xustar0030 mtime=1761382106.502309114 30 atime=1761382106.702308233 30 ctime=1761382109.648298797 nghttp2-1.68.0/doc/nghttp2_session_get_hd_inflate_dynamic_table_size.rst0000644000175100017510000000060315077107332026217 0ustar00runnerrunner nghttp2_session_get_hd_inflate_dynamic_table_size ================================================= Synopsis -------- *#include * .. function:: size_t nghttp2_session_get_hd_inflate_dynamic_table_size(nghttp2_session *session) Returns the current dynamic table size of HPACK inflater, including the overhead 32 bytes per entry described in RFC 7541. nghttp2-1.68.0/doc/PaxHeaders/CMakeLists.txt0000644000000000000000000000013215077107270015565 xustar0030 mtime=1761382072.963444278 30 atime=1761382106.542308938 30 ctime=1761382109.486299265 nghttp2-1.68.0/doc/CMakeLists.txt0000644000175100017510000003063215077107270016161 0ustar00runnerrunner# Generated documents set(APIDOCS macros.rst enums.rst types.rst nghttp2_check_header_name.rst nghttp2_check_header_value.rst nghttp2_hd_deflate_bound.rst nghttp2_hd_deflate_change_table_size.rst nghttp2_hd_deflate_del.rst nghttp2_hd_deflate_get_dynamic_table_size.rst nghttp2_hd_deflate_get_max_dynamic_table_size.rst nghttp2_hd_deflate_get_num_table_entries.rst nghttp2_hd_deflate_get_table_entry.rst nghttp2_hd_deflate_hd.rst nghttp2_hd_deflate_hd_vec.rst nghttp2_hd_deflate_new.rst nghttp2_hd_deflate_new2.rst nghttp2_hd_inflate_change_table_size.rst nghttp2_hd_inflate_del.rst nghttp2_hd_inflate_end_headers.rst nghttp2_hd_inflate_get_dynamic_table_size.rst nghttp2_hd_inflate_get_max_dynamic_table_size.rst nghttp2_hd_inflate_get_num_table_entries.rst nghttp2_hd_inflate_get_table_entry.rst nghttp2_hd_inflate_hd.rst nghttp2_hd_inflate_hd2.rst nghttp2_hd_inflate_new.rst nghttp2_hd_inflate_new2.rst nghttp2_http2_strerror.rst nghttp2_is_fatal.rst nghttp2_nv_compare_name.rst nghttp2_option_del.rst nghttp2_option_new.rst nghttp2_option_set_builtin_recv_extension_type.rst nghttp2_option_set_max_deflate_dynamic_table_size.rst nghttp2_option_set_max_reserved_remote_streams.rst nghttp2_option_set_max_send_header_block_length.rst nghttp2_option_set_no_auto_ping_ack.rst nghttp2_option_set_no_auto_window_update.rst nghttp2_option_set_no_http_messaging.rst nghttp2_option_set_no_recv_client_magic.rst nghttp2_option_set_peer_max_concurrent_streams.rst nghttp2_option_set_user_recv_extension_type.rst nghttp2_option_set_max_settings.rst nghttp2_pack_settings_payload.rst nghttp2_priority_spec_check_default.rst nghttp2_priority_spec_default_init.rst nghttp2_priority_spec_init.rst nghttp2_rcbuf_decref.rst nghttp2_rcbuf_get_buf.rst nghttp2_rcbuf_incref.rst nghttp2_rcbuf_is_static.rst nghttp2_select_next_protocol.rst nghttp2_session_callbacks_del.rst nghttp2_session_callbacks_new.rst nghttp2_session_callbacks_set_before_frame_send_callback.rst nghttp2_session_callbacks_set_data_source_read_length_callback.rst nghttp2_session_callbacks_set_error_callback.rst nghttp2_session_callbacks_set_on_begin_frame_callback.rst nghttp2_session_callbacks_set_on_begin_headers_callback.rst nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst nghttp2_session_callbacks_set_on_extension_chunk_recv_callback.rst nghttp2_session_callbacks_set_on_frame_not_send_callback.rst nghttp2_session_callbacks_set_on_frame_recv_callback.rst nghttp2_session_callbacks_set_on_frame_send_callback.rst nghttp2_session_callbacks_set_on_header_callback.rst nghttp2_session_callbacks_set_on_header_callback2.rst nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst nghttp2_session_callbacks_set_on_invalid_header_callback.rst nghttp2_session_callbacks_set_on_invalid_header_callback2.rst nghttp2_session_callbacks_set_on_stream_close_callback.rst nghttp2_session_callbacks_set_pack_extension_callback.rst nghttp2_session_callbacks_set_recv_callback.rst nghttp2_session_callbacks_set_select_padding_callback.rst nghttp2_session_callbacks_set_send_callback.rst nghttp2_session_callbacks_set_send_data_callback.rst nghttp2_session_callbacks_set_unpack_extension_callback.rst nghttp2_session_change_stream_priority.rst nghttp2_session_check_request_allowed.rst nghttp2_session_check_server_session.rst nghttp2_session_client_new.rst nghttp2_session_client_new2.rst nghttp2_session_client_new3.rst nghttp2_session_consume.rst nghttp2_session_consume_connection.rst nghttp2_session_consume_stream.rst nghttp2_session_create_idle_stream.rst nghttp2_session_del.rst nghttp2_session_find_stream.rst nghttp2_session_get_effective_local_window_size.rst nghttp2_session_get_effective_recv_data_length.rst nghttp2_session_get_hd_deflate_dynamic_table_size.rst nghttp2_session_get_hd_inflate_dynamic_table_size.rst nghttp2_session_get_last_proc_stream_id.rst nghttp2_session_get_local_settings.rst nghttp2_session_get_local_window_size.rst nghttp2_session_get_next_stream_id.rst nghttp2_session_get_outbound_queue_size.rst nghttp2_session_get_remote_settings.rst nghttp2_session_get_remote_window_size.rst nghttp2_session_get_root_stream.rst nghttp2_session_get_stream_effective_local_window_size.rst nghttp2_session_get_stream_effective_recv_data_length.rst nghttp2_session_get_stream_local_close.rst nghttp2_session_get_stream_local_window_size.rst nghttp2_session_get_stream_remote_close.rst nghttp2_session_get_stream_remote_window_size.rst nghttp2_session_get_stream_user_data.rst nghttp2_session_mem_recv.rst nghttp2_session_mem_send.rst nghttp2_session_recv.rst nghttp2_session_resume_data.rst nghttp2_session_send.rst nghttp2_session_server_new.rst nghttp2_session_server_new2.rst nghttp2_session_server_new3.rst nghttp2_session_set_local_window_size.rst nghttp2_session_set_next_stream_id.rst nghttp2_session_set_stream_user_data.rst nghttp2_session_terminate_session.rst nghttp2_session_terminate_session2.rst nghttp2_session_upgrade.rst nghttp2_session_upgrade2.rst nghttp2_session_want_read.rst nghttp2_session_want_write.rst nghttp2_set_debug_vprintf_callback.rst nghttp2_stream_get_first_child.rst nghttp2_stream_get_next_sibling.rst nghttp2_stream_get_parent.rst nghttp2_stream_get_previous_sibling.rst nghttp2_stream_get_state.rst nghttp2_stream_get_sum_dependency_weight.rst nghttp2_stream_get_weight.rst nghttp2_strerror.rst nghttp2_submit_altsvc.rst nghttp2_submit_data.rst nghttp2_submit_extension.rst nghttp2_submit_goaway.rst nghttp2_submit_headers.rst nghttp2_submit_ping.rst nghttp2_submit_priority.rst nghttp2_submit_push_promise.rst nghttp2_submit_request.rst nghttp2_submit_response.rst nghttp2_submit_rst_stream.rst nghttp2_submit_settings.rst nghttp2_submit_shutdown_notice.rst nghttp2_submit_trailer.rst nghttp2_submit_window_update.rst nghttp2_version.rst ) set(MAN_PAGES nghttp.1 nghttpd.1 nghttpx.1 h2load.1 ) # Other .rst files from the source tree that need to be copied # XXX move them to sources/ and create .in files? set(RST_FILES README.rst programmers-guide.rst nghttp.1.rst nghttpd.1.rst nghttpx.1.rst h2load.1.rst ) # XXX unused for now set(EXTRA_DIST mkapiref.py ${RST_FILES} ${APIDOCS} sources/index.rst sources/tutorial-client.rst sources/tutorial-server.rst sources/tutorial-hpack.rst sources/nghttpx-howto.rst sources/h2load-howto.rst sources/building-android-binary.rst sources/contribute.rst _exts/rubydomain/LICENSE.rubydomain _exts/rubydomain/__init__.py _exts/rubydomain/rubydomain.py _themes/sphinx_rtd_theme/__init__.py _themes/sphinx_rtd_theme/breadcrumbs.html _themes/sphinx_rtd_theme/footer.html _themes/sphinx_rtd_theme/layout.html _themes/sphinx_rtd_theme/layout_old.html _themes/sphinx_rtd_theme/search.html _themes/sphinx_rtd_theme/searchbox.html _themes/sphinx_rtd_theme/static/css/badge_only.css _themes/sphinx_rtd_theme/static/css/theme.css _themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff _themes/sphinx_rtd_theme/static/js/theme.js _themes/sphinx_rtd_theme/theme.conf _themes/sphinx_rtd_theme/versions.html ${MAN_PAGES} bash_completion/nghttp bash_completion/nghttpd bash_completion/nghttpx bash_completion/h2load ) # Based on Makefile for Sphinx documentation # You can set these variables from the command line. set(SPHINXOPTS) set(SPHINXBUILD sphinx-build) set(PAPER) set(BUILDDIR manual) # Internal variables. set(PAPEROPT_a4 -D latex_paper_size=a4) set(PAPEROPT_letter -D latex_paper_size=letter) set(ALLSPHINXOPTS -d ${BUILDDIR}/doctrees ${PAPEROPT_${PAPER}} ${SPHINXOPTS} .) # "Please use `make ' where is one of" # " html to make standalone HTML files" # " dirhtml to make HTML files named index.html in directories" # " singlehtml to make a single large HTML file" # " pickle to make pickle files" # " json to make JSON files" # " htmlhelp to make HTML files and a HTML help project" # " qthelp to make HTML files and a qthelp project" # " devhelp to make HTML files and a Devhelp project" # " epub to make an epub" # " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" # " latexpdf to make LaTeX files and run them through pdflatex" # " text to make text files" # " man to make manual pages" # " changes to make an overview of all changed/added/deprecated items" # " linkcheck to check all external links for integrity" # " doctest to run all doctests embedded in the documentation (if enabled)" # Copy files for out-of-tree builds if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) set(RST_BUILD_FILES) foreach(rstfile IN LISTS RST_FILES) set(outfile "${CMAKE_CURRENT_BINARY_DIR}/${rstfile}") add_custom_command(OUTPUT "${outfile}" COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/${rstfile}" "${outfile}" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${rstfile}" ) list(APPEND RST_BUILD_FILES "${outfile}") endforeach() else() set(RST_BUILD_FILES "${RST_FILES}") endif() set(apiref_SOURCES ${CMAKE_BINARY_DIR}/lib/includes/nghttp2/nghttp2ver.h ${CMAKE_SOURCE_DIR}/lib/includes/nghttp2/nghttp2.h ) # Generates apiref.rst and other files add_custom_command( OUTPUT apiref.rst ${APIDOCS} COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/mkapiref.py" apiref.rst macros.rst enums.rst types.rst . ${apiref_SOURCES} DEPENDS ${RST_BUILD_FILES} ${apiref_SOURCES} ) set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${BUILDDIR}") # Invokes sphinx-build and prints the given messages when completed function(sphinxbuild builder) set(echo_commands) foreach(message IN LISTS ARGN) list(APPEND echo_commands COMMAND ${CMAKE_COMMAND} -E echo "${message}") endforeach() add_custom_target(${builder} COMMAND "${SPHINXBUILD}" -b ${builder} ${ALLSPHINXOPTS} "${BUILDDIR}/${builder}" COMMAND ${CMAKE_COMMAND} -E echo ${echo_commands} VERBATIM DEPENDS apiref.rst ) endfunction() foreach(builder html dirhtml singlehtml) sphinxbuild(${builder} "Build finished. The HTML pages are in ${BUILDDIR}/${builder}.") endforeach() sphinxbuild(pickle "Build finished; now you can process the pickle files.") sphinxbuild(json "Build finished; now you can process the JSON files.") sphinxbuild(htmlhelp "Build finished; now you can run HTML Help Workshop with the" ".hhp project file in ${BUILDDIR}/htmlhelp." ) sphinxbuild(qthelp "Build finished; now you can run \"qcollectiongenerator\" with the" ".qhcp project file in ${BUILDDIR}/qthelp, like this:" "# qcollectiongenerator ${BUILDDIR}/qthelp/nghttp2.qhcp" "To view the help file:" "# assistant -collectionFile ${BUILDDIR}/qthelp/nghttp2.qhc" ) sphinxbuild(devhelp "Build finished." "To view the help file:" "# mkdir -p ~/.local/share/devhelp/nghttp2" "# ln -s ${BUILDDIR}/devhelp ~/.local/share/devhelp/nghttp2" "# devhelp" ) sphinxbuild(epub "Build finished. The epub file is in ${BUILDDIR}/epub.") sphinxbuild(latex "Build finished; the LaTeX files are in ${BUILDDIR}/latex." "Run `make' in that directory to run these through (pdf)latex" "(use `make latexpdf' here to do that automatically)." ) # Invoke the Makefile generated by sphinx add_custom_target(latexpdf COMMAND ${CMAKE_COMMAND} -E echo "Running LaTeX files through pdflatex..." COMMAND make -C "${BUILDDIR}/latex" all-pdf COMMAND ${CMAKE_COMMAND} -E echo "pdflatex finished; the PDF files are in ${BUILDDIR}/latex." DEPENDS latex ) sphinxbuild(text "Build finished. The text files are in ${BUILDDIR}/text.") sphinxbuild(man "Build finished. The manual pages are in ${BUILDDIR}/man.") sphinxbuild(changes "The overview file is in ${BUILDDIR}/changes.") sphinxbuild(linkcheck "Link check complete; look for any errors in the above output" "or in ${BUILDDIR}/linkcheck/output.txt." ) sphinxbuild(doctest "Testing of doctests in the sources finished, look at the" "results in ${BUILDDIR}/doctest/output.txt." ) foreach(_man_page IN LISTS MAN_PAGES) install(FILES ${_man_page} DESTINATION "${CMAKE_INSTALL_MANDIR}/man1" ) endforeach() nghttp2-1.68.0/doc/PaxHeaders/Makefile.in0000644000000000000000000000013215077107305015071 xustar0030 mtime=1761382085.497387465 30 atime=1761382103.974320251 30 ctime=1761382109.469299314 nghttp2-1.68.0/doc/Makefile.in0000644000175100017510000010412315077107305015462 0ustar00runnerrunner# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # nghttp2 - HTTP/2 C Library # Copyright (c) 2012 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = doc ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = conf.py index.rst package_README.rst \ tutorial-client.rst tutorial-server.rst tutorial-hpack.rst \ nghttpx-howto.rst h2load-howto.rst building-android-binary.rst \ nghttp2.h.rst nghttp2ver.h.rst contribute.rst CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } man1dir = $(mandir)/man1 am__installdirs = "$(DESTDIR)$(man1dir)" NROFF = nroff MANS = $(man_MANS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in \ $(srcdir)/building-android-binary.rst.in $(srcdir)/conf.py.in \ $(srcdir)/contribute.rst.in $(srcdir)/h2load-howto.rst.in \ $(srcdir)/index.rst.in $(srcdir)/nghttp2.h.rst.in \ $(srcdir)/nghttp2ver.h.rst.in $(srcdir)/nghttpx-howto.rst.in \ $(srcdir)/package_README.rst.in \ $(srcdir)/tutorial-client.rst.in \ $(srcdir)/tutorial-hpack.rst.in \ $(srcdir)/tutorial-server.rst.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPLDFLAGS = @APPLDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BPFCFLAGS = @BPFCFLAGS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CXX = @CXX@ CXX1XCXXFLAGS = @CXX1XCXXFLAGS@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXTRABPFCFLAGS = @EXTRABPFCFLAGS@ EXTRACFLAG = @EXTRACFLAG@ EXTRA_DEFS = @EXTRA_DEFS@ FGREP = @FGREP@ FILECMD = @FILECMD@ GREP = @GREP@ HAVE_CXX20 = @HAVE_CXX20@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ JANSSON_CFLAGS = @JANSSON_CFLAGS@ JANSSON_LIBS = @JANSSON_LIBS@ JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ JEMALLOC_LIBS = @JEMALLOC_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBBPF_CFLAGS = @LIBBPF_CFLAGS@ LIBBPF_LIBS = @LIBBPF_LIBS@ LIBBROTLIDEC_CFLAGS = @LIBBROTLIDEC_CFLAGS@ LIBBROTLIDEC_LIBS = @LIBBROTLIDEC_LIBS@ LIBBROTLIENC_CFLAGS = @LIBBROTLIENC_CFLAGS@ LIBBROTLIENC_LIBS = @LIBBROTLIENC_LIBS@ LIBCARES_CFLAGS = @LIBCARES_CFLAGS@ LIBCARES_LIBS = @LIBCARES_LIBS@ LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ LIBEV_CFLAGS = @LIBEV_CFLAGS@ LIBEV_LIBS = @LIBEV_LIBS@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@ LIBMRUBY_LIBS = @LIBMRUBY_LIBS@ LIBNGHTTP3_CFLAGS = @LIBNGHTTP3_CFLAGS@ LIBNGHTTP3_LIBS = @LIBNGHTTP3_LIBS@ LIBNGTCP2_CFLAGS = @LIBNGTCP2_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS = @LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_LIBS = @LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS = @LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS@ LIBNGTCP2_CRYPTO_LIBRESSL_LIBS = @LIBNGTCP2_CRYPTO_LIBRESSL_LIBS@ LIBNGTCP2_CRYPTO_OSSL_CFLAGS = @LIBNGTCP2_CRYPTO_OSSL_CFLAGS@ LIBNGTCP2_CRYPTO_OSSL_LIBS = @LIBNGTCP2_CRYPTO_OSSL_LIBS@ LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS = @LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ LIBNGTCP2_CRYPTO_QUICTLS_LIBS = @LIBNGTCP2_CRYPTO_QUICTLS_LIBS@ LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS = @LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS@ LIBNGTCP2_CRYPTO_WOLFSSL_LIBS = @LIBNGTCP2_CRYPTO_WOLFSSL_LIBS@ LIBNGTCP2_LIBS = @LIBNGTCP2_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBTOOL_LDFLAGS = @LIBTOOL_LDFLAGS@ LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ LIBXML2_LIBS = @LIBXML2_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_AGE = @LT_AGE@ LT_CURRENT = @LT_CURRENT@ LT_REVISION = @LT_REVISION@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ SYSTEMD_LIBS = @SYSTEMD_LIBS@ TESTLDADD = @TESTLDADD@ VERSION = @VERSION@ WARNCFLAGS = @WARNCFLAGS@ WARNCXXFLAGS = @WARNCXXFLAGS@ WOLFSSL_CFLAGS = @WOLFSSL_CFLAGS@ WOLFSSL_LIBS = @WOLFSSL_LIBS@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ man_MANS = nghttp.1 nghttpd.1 nghttpx.1 h2load.1 APIDOCS = \ macros.rst \ enums.rst \ types.rst \ nghttp2_check_authority.rst \ nghttp2_check_header_name.rst \ nghttp2_check_header_value.rst \ nghttp2_check_header_value_rfc9113.rst \ nghttp2_check_method.rst \ nghttp2_check_path.rst \ nghttp2_extpri_parse_priority.rst \ nghttp2_hd_deflate_bound.rst \ nghttp2_hd_deflate_change_table_size.rst \ nghttp2_hd_deflate_del.rst \ nghttp2_hd_deflate_get_dynamic_table_size.rst \ nghttp2_hd_deflate_get_max_dynamic_table_size.rst \ nghttp2_hd_deflate_get_num_table_entries.rst \ nghttp2_hd_deflate_get_table_entry.rst \ nghttp2_hd_deflate_hd.rst \ nghttp2_hd_deflate_hd2.rst \ nghttp2_hd_deflate_hd_vec.rst \ nghttp2_hd_deflate_hd_vec2.rst \ nghttp2_hd_deflate_new.rst \ nghttp2_hd_deflate_new2.rst \ nghttp2_hd_inflate_change_table_size.rst \ nghttp2_hd_inflate_del.rst \ nghttp2_hd_inflate_end_headers.rst \ nghttp2_hd_inflate_get_dynamic_table_size.rst \ nghttp2_hd_inflate_get_max_dynamic_table_size.rst \ nghttp2_hd_inflate_get_num_table_entries.rst \ nghttp2_hd_inflate_get_table_entry.rst \ nghttp2_hd_inflate_hd.rst \ nghttp2_hd_inflate_hd2.rst \ nghttp2_hd_inflate_hd3.rst \ nghttp2_hd_inflate_new.rst \ nghttp2_hd_inflate_new2.rst \ nghttp2_http2_strerror.rst \ nghttp2_is_fatal.rst \ nghttp2_nv_compare_name.rst \ nghttp2_option_del.rst \ nghttp2_option_new.rst \ nghttp2_option_set_builtin_recv_extension_type.rst \ nghttp2_option_set_max_deflate_dynamic_table_size.rst \ nghttp2_option_set_max_reserved_remote_streams.rst \ nghttp2_option_set_max_send_header_block_length.rst \ nghttp2_option_set_no_auto_ping_ack.rst \ nghttp2_option_set_no_auto_window_update.rst \ nghttp2_option_set_no_closed_streams.rst \ nghttp2_option_set_no_http_messaging.rst \ nghttp2_option_set_no_recv_client_magic.rst \ nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation.rst \ nghttp2_option_set_peer_max_concurrent_streams.rst \ nghttp2_option_set_server_fallback_rfc7540_priorities.rst \ nghttp2_option_set_user_recv_extension_type.rst \ nghttp2_option_set_max_continuations.rst \ nghttp2_option_set_max_outbound_ack.rst \ nghttp2_option_set_max_settings.rst \ nghttp2_option_set_stream_reset_rate_limit.rst \ nghttp2_option_set_glitch_rate_limit.rst \ nghttp2_pack_settings_payload.rst \ nghttp2_pack_settings_payload2.rst \ nghttp2_priority_spec_check_default.rst \ nghttp2_priority_spec_default_init.rst \ nghttp2_priority_spec_init.rst \ nghttp2_rcbuf_decref.rst \ nghttp2_rcbuf_get_buf.rst \ nghttp2_rcbuf_incref.rst \ nghttp2_rcbuf_is_static.rst \ nghttp2_select_next_protocol.rst \ nghttp2_select_alpn.rst \ nghttp2_session_callbacks_del.rst \ nghttp2_session_callbacks_new.rst \ nghttp2_session_callbacks_set_before_frame_send_callback.rst \ nghttp2_session_callbacks_set_data_source_read_length_callback.rst \ nghttp2_session_callbacks_set_data_source_read_length_callback2.rst \ nghttp2_session_callbacks_set_error_callback.rst \ nghttp2_session_callbacks_set_error_callback2.rst \ nghttp2_session_callbacks_set_on_begin_frame_callback.rst \ nghttp2_session_callbacks_set_on_begin_headers_callback.rst \ nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst \ nghttp2_session_callbacks_set_on_extension_chunk_recv_callback.rst \ nghttp2_session_callbacks_set_on_frame_not_send_callback.rst \ nghttp2_session_callbacks_set_on_frame_recv_callback.rst \ nghttp2_session_callbacks_set_on_frame_send_callback.rst \ nghttp2_session_callbacks_set_on_header_callback.rst \ nghttp2_session_callbacks_set_on_header_callback2.rst \ nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst \ nghttp2_session_callbacks_set_on_invalid_header_callback.rst \ nghttp2_session_callbacks_set_on_invalid_header_callback2.rst \ nghttp2_session_callbacks_set_on_stream_close_callback.rst \ nghttp2_session_callbacks_set_pack_extension_callback.rst \ nghttp2_session_callbacks_set_pack_extension_callback2.rst \ nghttp2_session_callbacks_set_rand_callback.rst \ nghttp2_session_callbacks_set_recv_callback.rst \ nghttp2_session_callbacks_set_recv_callback2.rst \ nghttp2_session_callbacks_set_select_padding_callback.rst \ nghttp2_session_callbacks_set_select_padding_callback2.rst \ nghttp2_session_callbacks_set_send_callback.rst \ nghttp2_session_callbacks_set_send_callback2.rst \ nghttp2_session_callbacks_set_send_data_callback.rst \ nghttp2_session_callbacks_set_unpack_extension_callback.rst \ nghttp2_session_change_extpri_stream_priority.rst \ nghttp2_session_change_stream_priority.rst \ nghttp2_session_check_request_allowed.rst \ nghttp2_session_check_server_session.rst \ nghttp2_session_client_new.rst \ nghttp2_session_client_new2.rst \ nghttp2_session_client_new3.rst \ nghttp2_session_consume.rst \ nghttp2_session_consume_connection.rst \ nghttp2_session_consume_stream.rst \ nghttp2_session_create_idle_stream.rst \ nghttp2_session_del.rst \ nghttp2_session_find_stream.rst \ nghttp2_session_get_effective_local_window_size.rst \ nghttp2_session_get_effective_recv_data_length.rst \ nghttp2_session_get_extpri_stream_priority.rst \ nghttp2_session_get_hd_deflate_dynamic_table_size.rst \ nghttp2_session_get_hd_inflate_dynamic_table_size.rst \ nghttp2_session_get_last_proc_stream_id.rst \ nghttp2_session_get_local_settings.rst \ nghttp2_session_get_local_window_size.rst \ nghttp2_session_get_next_stream_id.rst \ nghttp2_session_get_outbound_queue_size.rst \ nghttp2_session_get_remote_settings.rst \ nghttp2_session_get_remote_window_size.rst \ nghttp2_session_get_root_stream.rst \ nghttp2_session_get_stream_effective_local_window_size.rst \ nghttp2_session_get_stream_effective_recv_data_length.rst \ nghttp2_session_get_stream_local_close.rst \ nghttp2_session_get_stream_local_window_size.rst \ nghttp2_session_get_stream_remote_close.rst \ nghttp2_session_get_stream_remote_window_size.rst \ nghttp2_session_get_stream_user_data.rst \ nghttp2_session_mem_recv.rst \ nghttp2_session_mem_recv2.rst \ nghttp2_session_mem_send.rst \ nghttp2_session_mem_send2.rst \ nghttp2_session_recv.rst \ nghttp2_session_resume_data.rst \ nghttp2_session_send.rst \ nghttp2_session_server_new.rst \ nghttp2_session_server_new2.rst \ nghttp2_session_server_new3.rst \ nghttp2_session_set_local_window_size.rst \ nghttp2_session_set_next_stream_id.rst \ nghttp2_session_set_stream_user_data.rst \ nghttp2_session_set_user_data.rst \ nghttp2_session_terminate_session.rst \ nghttp2_session_terminate_session2.rst \ nghttp2_session_upgrade.rst \ nghttp2_session_upgrade2.rst \ nghttp2_session_want_read.rst \ nghttp2_session_want_write.rst \ nghttp2_set_debug_vprintf_callback.rst \ nghttp2_stream_get_first_child.rst \ nghttp2_stream_get_next_sibling.rst \ nghttp2_stream_get_parent.rst \ nghttp2_stream_get_previous_sibling.rst \ nghttp2_stream_get_state.rst \ nghttp2_stream_get_sum_dependency_weight.rst \ nghttp2_stream_get_weight.rst \ nghttp2_strerror.rst \ nghttp2_submit_altsvc.rst \ nghttp2_submit_data.rst \ nghttp2_submit_data2.rst \ nghttp2_submit_extension.rst \ nghttp2_submit_goaway.rst \ nghttp2_submit_headers.rst \ nghttp2_submit_origin.rst \ nghttp2_submit_ping.rst \ nghttp2_submit_priority.rst \ nghttp2_submit_priority_update.rst \ nghttp2_submit_push_promise.rst \ nghttp2_submit_request.rst \ nghttp2_submit_request2.rst \ nghttp2_submit_response.rst \ nghttp2_submit_response2.rst \ nghttp2_submit_rst_stream.rst \ nghttp2_submit_settings.rst \ nghttp2_submit_shutdown_notice.rst \ nghttp2_submit_trailer.rst \ nghttp2_submit_window_update.rst \ nghttp2_version.rst RST_FILES = \ README.rst \ programmers-guide.rst \ nghttp.1.rst \ nghttpd.1.rst \ nghttpx.1.rst \ h2load.1.rst EXTRA_DIST = \ CMakeLists.txt \ mkapiref.py \ $(RST_FILES) \ $(APIDOCS) \ sources/index.rst \ sources/tutorial-client.rst \ sources/tutorial-server.rst \ sources/tutorial-hpack.rst \ sources/nghttpx-howto.rst \ sources/h2load-howto.rst \ sources/building-android-binary.rst \ sources/contribute.rst \ _exts/rubydomain/LICENSE.rubydomain \ _exts/rubydomain/__init__.py \ _exts/rubydomain/rubydomain.py \ $(man_MANS) \ bash_completion/nghttp \ bash_completion/nghttpd \ bash_completion/nghttpx \ bash_completion/h2load # Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = PAPER = BUILDDIR = manual # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu doc/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu doc/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): conf.py: $(top_builddir)/config.status $(srcdir)/conf.py.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ index.rst: $(top_builddir)/config.status $(srcdir)/index.rst.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ package_README.rst: $(top_builddir)/config.status $(srcdir)/package_README.rst.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ tutorial-client.rst: $(top_builddir)/config.status $(srcdir)/tutorial-client.rst.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ tutorial-server.rst: $(top_builddir)/config.status $(srcdir)/tutorial-server.rst.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ tutorial-hpack.rst: $(top_builddir)/config.status $(srcdir)/tutorial-hpack.rst.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ nghttpx-howto.rst: $(top_builddir)/config.status $(srcdir)/nghttpx-howto.rst.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ h2load-howto.rst: $(top_builddir)/config.status $(srcdir)/h2load-howto.rst.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ building-android-binary.rst: $(top_builddir)/config.status $(srcdir)/building-android-binary.rst.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ nghttp2.h.rst: $(top_builddir)/config.status $(srcdir)/nghttp2.h.rst.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ nghttp2ver.h.rst: $(top_builddir)/config.status $(srcdir)/nghttp2ver.h.rst.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ contribute.rst: $(top_builddir)/config.status $(srcdir)/contribute.rst.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-man1: $(man_MANS) @$(NORMAL_INSTALL) @list1=''; \ list2='$(man_MANS)'; \ test -n "$(man1dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.1[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ done; } uninstall-man1: @$(NORMAL_UNINSTALL) @list=''; test -n "$(man1dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ sed -n '/\.1[a-z]*$$/p'; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir) tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(MANS) installdirs: for dir in "$(DESTDIR)$(man1dir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-local mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: html-local info: info-am info-am: install-data-am: install-man install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-man1 install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-man uninstall-man: uninstall-man1 .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ clean-local cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am html-local \ info info-am install install-am install-data install-data-am \ install-dvi install-dvi-am install-exec install-exec-am \ install-html install-html-am install-info install-info-am \ install-man install-man1 install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ ps ps-am tags-am uninstall uninstall-am uninstall-man \ uninstall-man1 .PRECIOUS: Makefile SPHINXBUILD ?= sphinx-build .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" apiref.rst: \ $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \ $(top_srcdir)/lib/includes/nghttp2/nghttp2.h for i in $(RST_FILES); do [ -e $(builddir)/$$i ] || cp $(srcdir)/$$i $(builddir); done $(PYTHON) $(top_srcdir)/doc/mkapiref.py \ apiref.rst macros.rst enums.rst types.rst . $^ $(APIDOCS): apiref.rst clean-local: if [ $(srcdir) != $(builddir) ]; then for i in $(RST_FILES); do rm -f $(builddir)/$$i; done fi -rm -f apiref.rst -rm -f $(APIDOCS) -rm -rf $(BUILDDIR)/* html-local: apiref.rst $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/nghttp2.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/nghttp2.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/nghttp2" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/nghttp2" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: apiref.rst $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nghttp2-1.68.0/doc/PaxHeaders/nghttp2_option_set_no_auto_ping_ack.rst0000644000000000000000000000013215077107332022766 xustar0030 mtime=1761382106.499309127 30 atime=1761382106.608308647 30 ctime=1761382109.552299075 nghttp2-1.68.0/doc/nghttp2_option_set_no_auto_ping_ack.rst0000644000175100017510000000122615077107332023357 0ustar00runnerrunner nghttp2_option_set_no_auto_ping_ack =================================== Synopsis -------- *#include * .. function:: void nghttp2_option_set_no_auto_ping_ack(nghttp2_option *option, int val) This option prevents the library from sending PING frame with ACK flag set automatically when PING frame without ACK flag set is received. If this option is set to nonzero, the library won't send PING frame with ACK flag set in the response for incoming PING frame. The application can send PING frame with ACK flag set using `nghttp2_submit_ping()` with :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` as flags parameter. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_check_header_name.rst0000644000000000000000000000013215077107332020611 xustar0030 mtime=1761382106.497309136 30 atime=1761382106.557308872 30 ctime=1761382109.501299222 nghttp2-1.68.0/doc/nghttp2_check_header_name.rst0000644000175100017510000000066315077107332021206 0ustar00runnerrunner nghttp2_check_header_name ========================= Synopsis -------- *#include * .. function:: int nghttp2_check_header_name(const uint8_t *name, size_t len) Returns nonzero if HTTP header field name *name* of length *len* is valid according to http://tools.ietf.org/html/rfc7230#section-3.2 Because this is a header field name in HTTP2, the upper cased alphabet is treated as error. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_recv.rst0000644000000000000000000000013115077107332017725 xustar0029 mtime=1761382106.50330911 30 atime=1761382106.727308123 30 ctime=1761382109.673298725 nghttp2-1.68.0/doc/nghttp2_session_recv.rst0000644000175100017510000000606215077107332020322 0ustar00runnerrunner nghttp2_session_recv ==================== Synopsis -------- *#include * .. function:: int nghttp2_session_recv(nghttp2_session *session) Receives frames from the remote peer. This function receives as many frames as possible until the user callback :type:`nghttp2_recv_callback` returns :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. This function calls several callback functions which are passed when initializing the *session*. Here is the simple time chart which tells when each callback is invoked: 1. :type:`nghttp2_recv_callback` is invoked one or more times to receive frame header. 2. When frame header is received, :type:`nghttp2_on_begin_frame_callback` is invoked. 3. If the frame is DATA frame: 1. :type:`nghttp2_recv_callback` is invoked to receive DATA payload. For each chunk of data, :type:`nghttp2_on_data_chunk_recv_callback` is invoked. 2. If one DATA frame is completely received, :type:`nghttp2_on_frame_recv_callback` is invoked. If the reception of the frame triggers the closure of the stream, :type:`nghttp2_on_stream_close_callback` is invoked. 4. If the frame is the control frame: 1. :type:`nghttp2_recv_callback` is invoked one or more times to receive whole frame. 2. If the received frame is valid, then following actions are taken. If the frame is either HEADERS or PUSH_PROMISE, :type:`nghttp2_on_begin_headers_callback` is invoked. Then :type:`nghttp2_on_header_callback` is invoked for each header name/value pair. For invalid header field, :type:`nghttp2_on_invalid_header_callback` is called. After all name/value pairs are emitted successfully, :type:`nghttp2_on_frame_recv_callback` is invoked. For other frames, :type:`nghttp2_on_frame_recv_callback` is invoked. If the reception of the frame triggers the closure of the stream, :type:`nghttp2_on_stream_close_callback` is invoked. 3. If the received frame is unpacked but is interpreted as invalid, :type:`nghttp2_on_invalid_frame_recv_callback` is invoked. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_EOF` The remote peer did shutdown on the connection. :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` The callback function failed. :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC` Invalid client magic was detected. This error only returns when *session* was configured as server and `nghttp2_option_set_no_recv_client_magic()` is not used with nonzero value. :enum:`nghttp2_error.NGHTTP2_ERR_FLOODED` Flooding was detected in this HTTP/2 session, and it must be closed. This is most likely caused by misbehaviour of peer. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation.rst0000644000000000000000000000013215077107332030374 xustar0030 mtime=1761382106.499309127 30 atime=1761382106.615308616 30 ctime=1761382109.559299054 nghttp2-1.68.0/doc/nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation.rst0000644000175100017510000000107615077107332030770 0ustar00runnerrunner nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation ================================================================ Synopsis -------- *#include * .. function:: void nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation( nghttp2_option *option, int val) This option, if set to nonzero, turns off RFC 9113 leading and trailing white spaces validation against HTTP field value. Some important fields, such as HTTP/2 pseudo header fields, are validated more strictly and this option does not apply to them. nghttp2-1.68.0/doc/PaxHeaders/tutorial-hpack.rst.in0000644000000000000000000000013215077107270017113 xustar0030 mtime=1761382072.967444259 30 atime=1761382104.058319881 30 ctime=1761382109.483299274 nghttp2-1.68.0/doc/tutorial-hpack.rst.in0000644000175100017510000000020315077107270017476 0ustar00runnerrunner.. include:: @top_srcdir@/doc/sources/tutorial-hpack.rst deflate.c --------- .. literalinclude:: @top_srcdir@/examples/deflate.c nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_deflate_get_max_dynamic_table_size.rst0000644000000000000000000000013215077107332024714 xustar0030 mtime=1761382106.498309132 30 atime=1761382106.570308815 30 ctime=1761382109.514299184 nghttp2-1.68.0/doc/nghttp2_hd_deflate_get_max_dynamic_table_size.rst0000644000175100017510000000044515077107332025307 0ustar00runnerrunner nghttp2_hd_deflate_get_max_dynamic_table_size ============================================= Synopsis -------- *#include * .. function:: size_t nghttp2_hd_deflate_get_max_dynamic_table_size(nghttp2_hd_deflater *deflater) Returns the maximum dynamic table size. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_terminate_session.rst0000644000000000000000000000013215077107332022522 xustar0030 mtime=1761382106.504309105 30 atime=1761382106.740308066 30 ctime=1761382109.686298687 nghttp2-1.68.0/doc/nghttp2_session_terminate_session.rst0000644000175100017510000000220615077107332023112 0ustar00runnerrunner nghttp2_session_terminate_session ================================= Synopsis -------- *#include * .. function:: int nghttp2_session_terminate_session(nghttp2_session *session, uint32_t error_code) Signals the session so that the connection should be terminated. The last stream ID is the minimum value between the stream ID of a stream for which :type:`nghttp2_on_frame_recv_callback` was called most recently and the last stream ID we have sent to the peer previously. The *error_code* is the error code of this GOAWAY frame. The pre-defined error code is one of :enum:`nghttp2_error_code`. After the transmission, both `nghttp2_session_want_read()` and `nghttp2_session_want_write()` return 0. This function should be called when the connection should be terminated after sending GOAWAY. If the remaining streams should be processed after GOAWAY, use `nghttp2_submit_goaway()` instead. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_client_new3.rst0000644000000000000000000000013215077107332021201 xustar0030 mtime=1761382106.502309114 30 atime=1761382106.688308295 30 ctime=1761382109.633298841 nghttp2-1.68.0/doc/nghttp2_session_client_new3.rst0000644000175100017510000000164015077107332021572 0ustar00runnerrunner nghttp2_session_client_new3 =========================== Synopsis -------- *#include * .. function:: int nghttp2_session_client_new3( nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option, nghttp2_mem *mem) Like `nghttp2_session_client_new2()`, but with additional custom memory allocator specified in the *mem*. The *mem* can be ``NULL`` and the call is equivalent to `nghttp2_session_client_new2()`. This function does not take ownership *mem*. The application is responsible for freeing *mem*. The library code does not refer to *mem* pointer after this function returns, so the application can safely free it. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_mem_send2.rst0000644000000000000000000000013115077107332020637 xustar0029 mtime=1761382106.50330911 30 atime=1761382106.726308127 30 ctime=1761382109.672298728 nghttp2-1.68.0/doc/nghttp2_session_mem_send2.rst0000644000175100017510000000321215077107332021226 0ustar00runnerrunner nghttp2_session_mem_send2 ========================= Synopsis -------- *#include * .. function:: nghttp2_ssize nghttp2_session_mem_send2(nghttp2_session *session, const uint8_t **data_ptr) Returns the serialized data to send. This function behaves like `nghttp2_session_send()` except that it does not use :type:`nghttp2_send_callback2` to transmit data. Instead, it assigns the pointer to the serialized data to the *\*data_ptr* and returns its length. The other callbacks are called in the same way as they are in `nghttp2_session_send()`. If no data is available to send, this function returns 0. This function may not return all serialized data in one invocation. To get all data, call this function repeatedly until it returns 0 or one of negative error codes. The assigned *\*data_ptr* is valid until the next call of `nghttp2_session_mem_send2()` or `nghttp2_session_send()`. The caller must send all data before sending the next chunk of data. This function returns the length of the data pointed by the *\*data_ptr* if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. .. note:: This function may produce very small byte string. If that is the case, and application disables Nagle algorithm (``TCP_NODELAY``), then writing this small chunk leads to very small packet, and it is very inefficient. An application should be responsible to buffer up small chunks of data as necessary to avoid this situation. nghttp2-1.68.0/doc/PaxHeaders/enums.rst0000644000000000000000000000013115077107332014704 xustar0030 mtime=1761382106.507309092 29 atime=1761382106.55330889 30 ctime=1761382109.497299233 nghttp2-1.68.0/doc/enums.rst0000644000175100017510000003542415077107332015305 0ustar00runnerrunner Enums ===== .. type:: nghttp2_error Error codes used in this library. The code range is [-999, -500], inclusive. The following values are defined: .. enum:: NGHTTP2_ERR_INVALID_ARGUMENT (``-501``) Invalid argument passed. .. enum:: NGHTTP2_ERR_BUFFER_ERROR (``-502``) Out of buffer space. .. enum:: NGHTTP2_ERR_UNSUPPORTED_VERSION (``-503``) The specified protocol version is not supported. .. enum:: NGHTTP2_ERR_WOULDBLOCK (``-504``) Used as a return value from :type:`nghttp2_send_callback2`, :type:`nghttp2_recv_callback` and :type:`nghttp2_send_data_callback` to indicate that the operation would block. .. enum:: NGHTTP2_ERR_PROTO (``-505``) General protocol error .. enum:: NGHTTP2_ERR_INVALID_FRAME (``-506``) The frame is invalid. .. enum:: NGHTTP2_ERR_EOF (``-507``) The peer performed a shutdown on the connection. .. enum:: NGHTTP2_ERR_DEFERRED (``-508``) Used as a return value from :func:`nghttp2_data_source_read_callback2` to indicate that data transfer is postponed. See :func:`nghttp2_data_source_read_callback2` for details. .. enum:: NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE (``-509``) Stream ID has reached the maximum value. Therefore no stream ID is available. .. enum:: NGHTTP2_ERR_STREAM_CLOSED (``-510``) The stream is already closed; or the stream ID is invalid. .. enum:: NGHTTP2_ERR_STREAM_CLOSING (``-511``) RST_STREAM has been added to the outbound queue. The stream is in closing state. .. enum:: NGHTTP2_ERR_STREAM_SHUT_WR (``-512``) The transmission is not allowed for this stream (e.g., a frame with END_STREAM flag set has already sent). .. enum:: NGHTTP2_ERR_INVALID_STREAM_ID (``-513``) The stream ID is invalid. .. enum:: NGHTTP2_ERR_INVALID_STREAM_STATE (``-514``) The state of the stream is not valid (e.g., DATA cannot be sent to the stream if response HEADERS has not been sent). .. enum:: NGHTTP2_ERR_DEFERRED_DATA_EXIST (``-515``) Another DATA frame has already been deferred. .. enum:: NGHTTP2_ERR_START_STREAM_NOT_ALLOWED (``-516``) Starting new stream is not allowed (e.g., GOAWAY has been sent and/or received). .. enum:: NGHTTP2_ERR_GOAWAY_ALREADY_SENT (``-517``) GOAWAY has already been sent. .. enum:: NGHTTP2_ERR_INVALID_HEADER_BLOCK (``-518``) The received frame contains the invalid header block (e.g., There are duplicate header names; or the header names are not encoded in US-ASCII character set and not lower cased; or the header name is zero-length string; or the header value contains multiple in-sequence NUL bytes). .. enum:: NGHTTP2_ERR_INVALID_STATE (``-519``) Indicates that the context is not suitable to perform the requested operation. .. enum:: NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE (``-521``) The user callback function failed due to the temporal error. .. enum:: NGHTTP2_ERR_FRAME_SIZE_ERROR (``-522``) The length of the frame is invalid, either too large or too small. .. enum:: NGHTTP2_ERR_HEADER_COMP (``-523``) Header block inflate/deflate error. .. enum:: NGHTTP2_ERR_FLOW_CONTROL (``-524``) Flow control error .. enum:: NGHTTP2_ERR_INSUFF_BUFSIZE (``-525``) Insufficient buffer size given to function. .. enum:: NGHTTP2_ERR_PAUSE (``-526``) Callback was paused by the application .. enum:: NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS (``-527``) There are too many in-flight SETTING frame and no more transmission of SETTINGS is allowed. .. enum:: NGHTTP2_ERR_PUSH_DISABLED (``-528``) The server push is disabled. .. enum:: NGHTTP2_ERR_DATA_EXIST (``-529``) DATA or HEADERS frame for a given stream has been already submitted and has not been fully processed yet. Application should wait for the transmission of the previously submitted frame before submitting another. .. enum:: NGHTTP2_ERR_SESSION_CLOSING (``-530``) The current session is closing due to a connection error or `nghttp2_session_terminate_session()` is called. .. enum:: NGHTTP2_ERR_HTTP_HEADER (``-531``) Invalid HTTP header field was received and stream is going to be closed. .. enum:: NGHTTP2_ERR_HTTP_MESSAGING (``-532``) Violation in HTTP messaging rule. .. enum:: NGHTTP2_ERR_REFUSED_STREAM (``-533``) Stream was refused. .. enum:: NGHTTP2_ERR_INTERNAL (``-534``) Unexpected internal error, but recovered. .. enum:: NGHTTP2_ERR_CANCEL (``-535``) Indicates that a processing was canceled. .. enum:: NGHTTP2_ERR_SETTINGS_EXPECTED (``-536``) When a local endpoint expects to receive SETTINGS frame, it receives an other type of frame. .. enum:: NGHTTP2_ERR_TOO_MANY_SETTINGS (``-537``) When a local endpoint receives too many settings entries in a single SETTINGS frame. .. enum:: NGHTTP2_ERR_FATAL (``-900``) The errors < :enum:`nghttp2_error.NGHTTP2_ERR_FATAL` mean that the library is under unexpected condition and processing was terminated (e.g., out of memory). If application receives this error code, it must stop using that :type:`nghttp2_session` object and only allowed operation for that object is deallocate it using `nghttp2_session_del()`. .. enum:: NGHTTP2_ERR_NOMEM (``-901``) Out of memory. This is a fatal error. .. enum:: NGHTTP2_ERR_CALLBACK_FAILURE (``-902``) The user callback function failed. This is a fatal error. .. enum:: NGHTTP2_ERR_BAD_CLIENT_MAGIC (``-903``) Invalid client magic (see :macro:`NGHTTP2_CLIENT_MAGIC`) was received and further processing is not possible. .. enum:: NGHTTP2_ERR_FLOODED (``-904``) Possible flooding by peer was detected in this HTTP/2 session. Flooding is measured by how many PING and SETTINGS frames with ACK flag set are queued for transmission. These frames are response for the peer initiated frames, and peer can cause memory exhaustion on server side to send these frames forever and does not read network. .. enum:: NGHTTP2_ERR_TOO_MANY_CONTINUATIONS (``-905``) When a local endpoint receives too many CONTINUATION frames following a HEADER frame. .. type:: nghttp2_nv_flag The flags for header field name/value pair. .. enum:: NGHTTP2_NV_FLAG_NONE (``0``) No flag set. .. enum:: NGHTTP2_NV_FLAG_NO_INDEX (``0x01``) Indicates that this name/value pair must not be indexed ("Literal Header Field never Indexed" representation must be used in HPACK encoding). Other implementation calls this bit as "sensitive". .. enum:: NGHTTP2_NV_FLAG_NO_COPY_NAME (``0x02``) This flag is set solely by application. If this flag is set, the library does not make a copy of header field name. This could improve performance. .. enum:: NGHTTP2_NV_FLAG_NO_COPY_VALUE (``0x04``) This flag is set solely by application. If this flag is set, the library does not make a copy of header field value. This could improve performance. .. type:: nghttp2_frame_type The frame types in HTTP/2 specification. .. enum:: NGHTTP2_DATA (``0``) The DATA frame. .. enum:: NGHTTP2_HEADERS (``0x01``) The HEADERS frame. .. enum:: NGHTTP2_PRIORITY (``0x02``) The PRIORITY frame. .. enum:: NGHTTP2_RST_STREAM (``0x03``) The RST_STREAM frame. .. enum:: NGHTTP2_SETTINGS (``0x04``) The SETTINGS frame. .. enum:: NGHTTP2_PUSH_PROMISE (``0x05``) The PUSH_PROMISE frame. .. enum:: NGHTTP2_PING (``0x06``) The PING frame. .. enum:: NGHTTP2_GOAWAY (``0x07``) The GOAWAY frame. .. enum:: NGHTTP2_WINDOW_UPDATE (``0x08``) The WINDOW_UPDATE frame. .. enum:: NGHTTP2_CONTINUATION (``0x09``) The CONTINUATION frame. This frame type won't be passed to any callbacks because the library processes this frame type and its preceding HEADERS/PUSH_PROMISE as a single frame. .. enum:: NGHTTP2_ALTSVC (``0x0a``) The ALTSVC frame, which is defined in `RFC 7383 `_. .. enum:: NGHTTP2_ORIGIN (``0x0c``) The ORIGIN frame, which is defined by `RFC 8336 `_. .. enum:: NGHTTP2_PRIORITY_UPDATE (``0x10``) The PRIORITY_UPDATE frame, which is defined by :rfc:`9218`. .. type:: nghttp2_flag The flags for HTTP/2 frames. This enum defines all flags for all frames. .. enum:: NGHTTP2_FLAG_NONE (``0``) No flag set. .. enum:: NGHTTP2_FLAG_END_STREAM (``0x01``) The END_STREAM flag. .. enum:: NGHTTP2_FLAG_END_HEADERS (``0x04``) The END_HEADERS flag. .. enum:: NGHTTP2_FLAG_ACK (``0x01``) The ACK flag. .. enum:: NGHTTP2_FLAG_PADDED (``0x08``) The PADDED flag. .. enum:: NGHTTP2_FLAG_PRIORITY (``0x20``) The PRIORITY flag. .. type:: nghttp2_settings_id The SETTINGS ID. .. enum:: NGHTTP2_SETTINGS_HEADER_TABLE_SIZE (``0x01``) SETTINGS_HEADER_TABLE_SIZE .. enum:: NGHTTP2_SETTINGS_ENABLE_PUSH (``0x02``) SETTINGS_ENABLE_PUSH .. enum:: NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS (``0x03``) SETTINGS_MAX_CONCURRENT_STREAMS .. enum:: NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE (``0x04``) SETTINGS_INITIAL_WINDOW_SIZE .. enum:: NGHTTP2_SETTINGS_MAX_FRAME_SIZE (``0x05``) SETTINGS_MAX_FRAME_SIZE .. enum:: NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE (``0x06``) SETTINGS_MAX_HEADER_LIST_SIZE .. enum:: NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL (``0x08``) SETTINGS_ENABLE_CONNECT_PROTOCOL (`RFC 8441 `_) .. enum:: NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES (``0x09``) SETTINGS_NO_RFC7540_PRIORITIES (:rfc:`9218`) .. type:: nghttp2_error_code The status codes for the RST_STREAM and GOAWAY frames. .. enum:: NGHTTP2_NO_ERROR (``0x00``) No errors. .. enum:: NGHTTP2_PROTOCOL_ERROR (``0x01``) PROTOCOL_ERROR .. enum:: NGHTTP2_INTERNAL_ERROR (``0x02``) INTERNAL_ERROR .. enum:: NGHTTP2_FLOW_CONTROL_ERROR (``0x03``) FLOW_CONTROL_ERROR .. enum:: NGHTTP2_SETTINGS_TIMEOUT (``0x04``) SETTINGS_TIMEOUT .. enum:: NGHTTP2_STREAM_CLOSED (``0x05``) STREAM_CLOSED .. enum:: NGHTTP2_FRAME_SIZE_ERROR (``0x06``) FRAME_SIZE_ERROR .. enum:: NGHTTP2_REFUSED_STREAM (``0x07``) REFUSED_STREAM .. enum:: NGHTTP2_CANCEL (``0x08``) CANCEL .. enum:: NGHTTP2_COMPRESSION_ERROR (``0x09``) COMPRESSION_ERROR .. enum:: NGHTTP2_CONNECT_ERROR (``0x0a``) CONNECT_ERROR .. enum:: NGHTTP2_ENHANCE_YOUR_CALM (``0x0b``) ENHANCE_YOUR_CALM .. enum:: NGHTTP2_INADEQUATE_SECURITY (``0x0c``) INADEQUATE_SECURITY .. enum:: NGHTTP2_HTTP_1_1_REQUIRED (``0x0d``) HTTP_1_1_REQUIRED .. type:: nghttp2_data_flag The flags used to set in *data_flags* output parameter in :type:`nghttp2_data_source_read_callback2`. .. enum:: NGHTTP2_DATA_FLAG_NONE (``0``) No flag set. .. enum:: NGHTTP2_DATA_FLAG_EOF (``0x01``) Indicates EOF was sensed. .. enum:: NGHTTP2_DATA_FLAG_NO_END_STREAM (``0x02``) Indicates that END_STREAM flag must not be set even if NGHTTP2_DATA_FLAG_EOF is set. Usually this flag is used to send trailer fields with `nghttp2_submit_request2()` or `nghttp2_submit_response2()`. .. enum:: NGHTTP2_DATA_FLAG_NO_COPY (``0x04``) Indicates that application will send complete DATA frame in :type:`nghttp2_send_data_callback`. .. type:: nghttp2_headers_category The category of HEADERS, which indicates the role of the frame. In HTTP/2 spec, request, response, push response and other arbitrary headers (e.g., trailer fields) are all called just HEADERS. To give the application the role of incoming HEADERS frame, we define several categories. .. enum:: NGHTTP2_HCAT_REQUEST (``0``) The HEADERS frame is opening new stream, which is analogous to SYN_STREAM in SPDY. .. enum:: NGHTTP2_HCAT_RESPONSE (``1``) The HEADERS frame is the first response headers, which is analogous to SYN_REPLY in SPDY. .. enum:: NGHTTP2_HCAT_PUSH_RESPONSE (``2``) The HEADERS frame is the first headers sent against reserved stream. .. enum:: NGHTTP2_HCAT_HEADERS (``3``) The HEADERS frame which does not apply for the above categories, which is analogous to HEADERS in SPDY. If non-final response (e.g., status 1xx) is used, final response HEADERS frame will be categorized here. .. type:: nghttp2_hd_inflate_flag The flags for header inflation. .. enum:: NGHTTP2_HD_INFLATE_NONE (``0``) No flag set. .. enum:: NGHTTP2_HD_INFLATE_FINAL (``0x01``) Indicates all headers were inflated. .. enum:: NGHTTP2_HD_INFLATE_EMIT (``0x02``) Indicates a header was emitted. .. type:: nghttp2_stream_proto_state State of stream as described in RFC 7540. .. enum:: NGHTTP2_STREAM_STATE_IDLE (``1``) idle state. .. enum:: NGHTTP2_STREAM_STATE_OPEN open state. .. enum:: NGHTTP2_STREAM_STATE_RESERVED_LOCAL reserved (local) state. .. enum:: NGHTTP2_STREAM_STATE_RESERVED_REMOTE reserved (remote) state. .. enum:: NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL half closed (local) state. .. enum:: NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE half closed (remote) state. .. enum:: NGHTTP2_STREAM_STATE_CLOSED closed state. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_set_user_data.rst0000644000000000000000000000013015077107332021607 xustar0030 mtime=1761382106.504309105 29 atime=1761382106.73930807 29 ctime=1761382109.68529869 nghttp2-1.68.0/doc/nghttp2_session_set_user_data.rst0000644000175100017510000000056115077107332022203 0ustar00runnerrunner nghttp2_session_set_user_data ============================= Synopsis -------- *#include * .. function:: void nghttp2_session_set_user_data(nghttp2_session *session, void *user_data) Sets *user_data* to *session*, overwriting the existing user data specified in `nghttp2_session_client_new()`, or `nghttp2_session_server_new()`. nghttp2-1.68.0/doc/PaxHeaders/tutorial-server.rst.in0000644000000000000000000000013215077107270017333 xustar0030 mtime=1761382072.967444259 30 atime=1761382104.044319942 30 ctime=1761382109.484299271 nghttp2-1.68.0/doc/tutorial-server.rst.in0000644000175100017510000000023415077107270017722 0ustar00runnerrunner.. include:: @top_srcdir@/doc/sources/tutorial-server.rst libevent-server.c ----------------- .. literalinclude:: @top_srcdir@/examples/libevent-server.c nghttp2-1.68.0/doc/PaxHeaders/nghttp2_stream_get_state.rst0000644000000000000000000000013215077107332020556 xustar0030 mtime=1761382106.505309101 30 atime=1761382106.754308004 30 ctime=1761382109.700298647 nghttp2-1.68.0/doc/nghttp2_stream_get_state.rst0000644000175100017510000000060515077107332021147 0ustar00runnerrunner nghttp2_stream_get_state ======================== Synopsis -------- *#include * .. function:: nghttp2_stream_proto_state nghttp2_stream_get_state(nghttp2_stream *stream) Returns state of *stream*. The root stream retrieved by `nghttp2_session_get_root_stream()` will have stream state :enum:`nghttp2_stream_proto_state.NGHTTP2_STREAM_STATE_IDLE`. nghttp2-1.68.0/doc/PaxHeaders/programmers-guide.rst0000644000000000000000000000013115077107270017207 xustar0030 mtime=1761382072.966444264 29 atime=1761382106.54630892 30 ctime=1761382109.489299257 nghttp2-1.68.0/doc/programmers-guide.rst0000644000175100017510000004760615077107270017615 0ustar00runnerrunnerProgrammers' Guide ================== Architecture ------------ The most notable point in nghttp2 library architecture is it does not perform any I/O. nghttp2 only performs HTTP/2 protocol stuff based on input byte strings. It will call callback functions set by applications while processing input. The output of nghttp2 is just byte string. An application is responsible to send these output to the remote peer. The callback functions may be called while producing output. Not doing I/O makes embedding nghttp2 library in the existing code base very easy. Usually, the existing applications have its own I/O event loops. It is very hard to use nghttp2 in that situation if nghttp2 does its own I/O. It also makes light weight language wrapper for nghttp2 easy with the same reason. The down side is that an application author has to write more code to write complete application using nghttp2. This is especially true for simple "toy" application. For the real applications, however, this is not the case. This is because you probably want to support HTTP/1 which nghttp2 does not provide, and to do that, you will need to write your own HTTP/1 stack or use existing third-party library, and bind them together with nghttp2 and I/O event loop. In this point, not performing I/O in nghttp2 has more point than doing it. The primary object that an application uses is :type:`nghttp2_session` object, which is opaque struct and its details are hidden in order to ensure the upgrading its internal architecture without breaking the backward compatibility. An application can set callbacks to :type:`nghttp2_session` object through the dedicated object and functions, and it also interacts with it via many API function calls. An application can create as many :type:`nghttp2_session` object as it wants. But single :type:`nghttp2_session` object must be used by a single thread at the same time. This is not so hard to enforce since most event-based architecture applications use is single thread per core, and handling one connection I/O is done by single thread. To feed input to :type:`nghttp2_session` object, one can use `nghttp2_session_recv()` or `nghttp2_session_mem_recv2()` functions. They behave similarly, and the difference is that `nghttp2_session_recv()` will use :type:`nghttp2_read_callback` to get input. On the other hand, `nghttp2_session_mem_recv2()` will take input as its parameter. If in doubt, use `nghttp2_session_mem_recv2()` since it is simpler, and could be faster since it avoids calling callback function. To get output from :type:`nghttp2_session` object, one can use `nghttp2_session_send()` or `nghttp2_session_mem_send2()`. The difference between them is that the former uses :type:`nghttp2_send_callback` to pass output to an application. On the other hand, the latter returns the output to the caller. If in doubt, use `nghttp2_session_mem_send2()` since it is simpler. But `nghttp2_session_send()` might be easier to use if the output buffer an application has is fixed sized. In general, an application should call `nghttp2_session_mem_send2()` when it gets input from underlying connection. Since there is great chance to get something pushed into transmission queue while the call of `nghttp2_session_mem_send2()`, it is recommended to call `nghttp2_session_mem_recv2()` after `nghttp2_session_mem_send2()`. There is a question when we are safe to close HTTP/2 session without waiting for the closure of underlying connection. We offer 2 API calls for this: `nghttp2_session_want_read()` and `nghttp2_session_want_write()`. If they both return 0, application can destroy :type:`nghttp2_session`, and then close the underlying connection. But make sure that the buffered output has been transmitted to the peer before closing the connection when `nghttp2_session_mem_send2()` is used, since `nghttp2_session_want_write()` does not take into account the transmission of the buffered data outside of :type:`nghttp2_session`. Includes -------- To use the public APIs, include ``nghttp2/nghttp2.h``:: #include The header files are also available online: :doc:`nghttp2.h` and :doc:`nghttp2ver.h`. Remarks ------- Do not call `nghttp2_session_send()`, `nghttp2_session_mem_send2()`, `nghttp2_session_recv()` or `nghttp2_session_mem_recv2()` from the nghttp2 callback functions directly or indirectly. It will lead to the crash. You can submit requests or frames in the callbacks then call these functions outside the callbacks. `nghttp2_session_send()` and `nghttp2_session_mem_send2()` send first 24 bytes of client magic string (MAGIC) (:macro:`NGHTTP2_CLIENT_MAGIC`) on client configuration. The applications are responsible to send SETTINGS frame as part of connection preface using `nghttp2_submit_settings()`. Similarly, `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` consume MAGIC on server configuration unless `nghttp2_option_set_no_recv_client_magic()` is used with nonzero option value. .. _http-messaging: HTTP Messaging -------------- By default, nghttp2 library checks HTTP messaging rules described in `HTTP/2 specification, section 8 `_. Everything described in that section is not validated however. We briefly describe what the library does in this area. In the following description, without loss of generality we omit CONTINUATION frame since they must follow HEADERS frame and are processed atomically. In other words, they are just one big HEADERS frame. To disable these validations, use `nghttp2_option_set_no_http_messaging()`. Please note that disabling this feature does not change the fundamental client and server model of HTTP. That is, even if the validation is disabled, only client can send requests. For HTTP request, including those carried by PUSH_PROMISE, HTTP message starts with one HEADERS frame containing request headers. It is followed by zero or more DATA frames containing request body, which is followed by zero or one HEADERS containing trailer headers. The request headers must include ":scheme", ":method" and ":path" pseudo header fields unless ":method" is not "CONNECT". ":authority" is optional, but nghttp2 requires either ":authority" or "Host" header field must be present. If ":method" is "CONNECT", the request headers must include ":method" and ":authority" and must omit ":scheme" and ":path". For HTTP response, HTTP message starts with zero or more HEADERS frames containing non-final response (status code 1xx). They are followed by one HEADERS frame containing final response headers (non-1xx). It is followed by zero or more DATA frames containing response body, which is followed by zero or one HEADERS containing trailer headers. The non-final and final response headers must contain ":status" pseudo header field containing 3 digits only. All request and response headers must include exactly one valid value for each pseudo header field. Additionally nghttp2 requires all request headers must not include more than one "Host" header field. HTTP/2 prohibits connection-specific header fields. The following header fields must not appear: "Connection", "Keep-Alive", "Proxy-Connection", "Transfer-Encoding" and "Upgrade". Additionally, "TE" header field must not include any value other than "trailers". Each header field name and value must obey the field-name and field-value production rules described in `RFC 7230, section 3.2. `_. Additionally, all field name must be lower cased. The invalid header fields are treated as stream error, and that stream is reset. If application wants to treat these headers in their own way, use `nghttp2_on_invalid_header_callback `_. For "http" or "https" URIs, ":path" pseudo header fields must start with "/". The only exception is OPTIONS request, in that case, "*" is allowed in ":path" pseudo header field to represent system-wide OPTIONS request. With the above validations, nghttp2 library guarantees that header field name passed to `nghttp2_on_header_callback()` is not empty. Also required pseudo headers are all present and not empty. nghttp2 enforces "Content-Length" validation as well. All request or response headers must not contain more than one "Content-Length" header field. If "Content-Length" header field is present, it must be parsed as 64 bit signed integer. The sum of data length in the following DATA frames must match with the number in "Content-Length" header field if it is present (this does not include padding bytes). RFC 7230 says that server must not send "Content-Length" in any response with 1xx, and 204 status code. It also says that "Content-Length" is not allowed in any response with 200 status code to a CONNECT request. nghttp2 enforces them as well. Any deviation results in stream error of type PROTOCOL_ERROR. If error is found in PUSH_PROMISE frame, stream error is raised against promised stream. The order of transmission of the HTTP/2 frames ---------------------------------------------- This section describes the internals of libnghttp2 about the scheduling of transmission of HTTP/2 frames. This is pretty much internal stuff, so the details could change in the future versions of the library. libnghttp2 categorizes HTTP/2 frames into 4 categories: urgent, regular, syn_stream, and data in the order of higher priority. The urgent category includes PING and SETTINGS. They are sent with highest priority. The order inside the category is FIFO. The regular category includes frames other than PING, SETTINGS, DATA, and HEADERS which does not create stream (which counts toward concurrent stream limit). The order inside the category is FIFO. The syn_stream category includes HEADERS frame which creates stream, that counts toward the concurrent stream limit. The data category includes DATA frame, and the scheduling among DATA frames are determined by HTTP/2 dependency tree. If the application wants to send frames in the specific order, and the default transmission order does not fit, it has to schedule frames by itself using the callbacks (e.g., :type:`nghttp2_on_frame_send_callback`). RST_STREAM has special side effect when it is submitted by `nghttp2_submit_rst_stream()`. It cancels all pending HEADERS and DATA frames whose stream ID matches the one in the RST_STREAM frame. This may cause unexpected behaviour for the application in some cases. For example, suppose that application wants to send RST_STREAM after sending response HEADERS and DATA. Because of the reason we mentioned above, the following code does not work: .. code-block:: c nghttp2_submit_response2(...) nghttp2_submit_rst_stream(...) RST_STREAM cancels HEADERS (and DATA), and just RST_STREAM is sent. The correct way is use :type:`nghttp2_on_frame_send_callback`, and after HEADERS and DATA frames are sent, issue `nghttp2_submit_rst_stream()`. FYI, :type:`nghttp2_on_frame_not_send_callback` tells you why frames are not sent. Implement user defined HTTP/2 non-critical extensions ----------------------------------------------------- As of nghttp2 v1.8.0, we have added HTTP/2 non-critical extension framework, which lets application send and receive user defined custom HTTP/2 non-critical extension frames. nghttp2 also offers built-in functionality to send and receive official HTTP/2 extension frames (e.g., ALTSVC frame). For these built-in handler, refer to the next section. To send extension frame, use `nghttp2_submit_extension()`, and implement :type:`nghttp2_pack_extension_callback`. The callback implements how to encode data into wire format. The callback must be set to :type:`nghttp2_session_callbacks` using `nghttp2_session_callbacks_set_pack_extension_callback()`. For example, we will illustrate how to send `ALTSVC `_ frame. .. code-block:: c typedef struct { const char *origin; const char *field; } alt_svc; nghttp2_ssize pack_extension_callback(nghttp2_session *session, uint8_t *buf, size_t len, const nghttp2_frame *frame, void *user_data) { const alt_svc *altsvc = (const alt_svc *)frame->ext.payload; size_t originlen = strlen(altsvc->origin); size_t fieldlen = strlen(altsvc->field); uint8_t *p; if (len < 2 + originlen + fieldlen || originlen > 0xffff) { return NGHTTP2_ERR_CANCEL; } p = buf; *p++ = originlen >> 8; *p++ = originlen & 0xff; memcpy(p, altsvc->origin, originlen); p += originlen; memcpy(p, altsvc->field, fieldlen); p += fieldlen; return p - buf; } This implements :type:`nghttp2_pack_extension_callback`. We have to set this callback to :type:`nghttp2_session_callbacks`: .. code-block:: c nghttp2_session_callbacks_set_pack_extension_callback( callbacks, pack_extension_callback); To send ALTSVC frame, call `nghttp2_submit_extension()`: .. code-block:: c static const alt_svc altsvc = {"example.com", "h2=\":8000\""}; nghttp2_submit_extension(session, 0xa, NGHTTP2_FLAG_NONE, 0, (void *)&altsvc); Notice that ALTSVC is use frame type ``0xa``. To receive extension frames, implement 2 callbacks: :type:`nghttp2_unpack_extension_callback` and :type:`nghttp2_on_extension_chunk_recv_callback`. :type:`nghttp2_unpack_extension_callback` implements the way how to decode wire format. :type:`nghttp2_on_extension_chunk_recv_callback` implements how to buffer the incoming extension payload. These callbacks must be set using `nghttp2_session_callbacks_set_unpack_extension_callback()` and `nghttp2_session_callbacks_set_on_extension_chunk_recv_callback()` respectively. The application also must tell the library which extension frame type it is willing to receive using `nghttp2_option_set_user_recv_extension_type()`. Note that the application has to create :type:`nghttp2_option` object for that purpose, and initialize session with it. We use ALTSVC again to illustrate how to receive extension frames. We use different ``alt_svc`` struct than the previous one. First implement 2 callbacks. We store incoming ALTSVC payload to global variable ``altsvc_buffer``. Don't do this in production code since this is not thread safe: .. code-block:: c typedef struct { const uint8_t *origin; size_t originlen; const uint8_t *field; size_t fieldlen; } alt_svc; /* buffers incoming ALTSVC payload */ uint8_t altsvc_buffer[4096]; /* The length of byte written to altsvc_buffer */ size_t altsvc_bufferlen = 0; int on_extension_chunk_recv_callback(nghttp2_session *session, const nghttp2_frame_hd *hd, const uint8_t *data, size_t len, void *user_data) { if (sizeof(altsvc_buffer) < altsvc_bufferlen + len) { altsvc_bufferlen = 0; return NGHTTP2_ERR_CANCEL; } memcpy(altsvc_buffer + altsvc_bufferlen, data, len); altsvc_bufferlen += len; return 0; } int unpack_extension_callback(nghttp2_session *session, void **payload, const nghttp2_frame_hd *hd, void *user_data) { uint8_t *origin, *field; size_t originlen, fieldlen; uint8_t *p, *end; alt_svc *altsvc; if (altsvc_bufferlen < 2) { altsvc_bufferlen = 0; return NGHTTP2_ERR_CANCEL; } p = altsvc_buffer; end = altsvc_buffer + altsvc_bufferlen; originlen = ((*p) << 8) + *(p + 1); p += 2; if (p + originlen > end) { altsvc_bufferlen = 0; return NGHTTP2_ERR_CANCEL; } origin = p; field = p + originlen; fieldlen = end - field; altsvc = (alt_svc *)malloc(sizeof(alt_svc)); altsvc->origin = origin; altsvc->originlen = originlen; altsvc->field = field; altsvc->fieldlen = fieldlen; *payload = altsvc; altsvc_bufferlen = 0; return 0; } Set these callbacks to :type:`nghttp2_session_callbacks`: .. code-block:: c nghttp2_session_callbacks_set_on_extension_chunk_recv_callback( callbacks, on_extension_chunk_recv_callback); nghttp2_session_callbacks_set_unpack_extension_callback( callbacks, unpack_extension_callback); In ``unpack_extension_callback`` above, we set unpacked ``alt_svc`` object to ``*payload``. nghttp2 library then, calls :type:`nghttp2_on_frame_recv_callback`, and ``*payload`` will be available as ``frame->ext.payload``: .. code-block:: c int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { switch (frame->hd.type) { ... case 0xa: { alt_svc *altsvc = (alt_svc *)frame->ext.payload; fprintf(stderr, "ALTSVC frame received\n"); fprintf(stderr, " origin: %.*s\n", (int)altsvc->originlen, altsvc->origin); fprintf(stderr, " field : %.*s\n", (int)altsvc->fieldlen, altsvc->field); free(altsvc); break; } } return 0; } Finally, application should set the extension frame types it is willing to receive: .. code-block:: c nghttp2_option_set_user_recv_extension_type(option, 0xa); The :type:`nghttp2_option` must be set to :type:`nghttp2_session` on its creation: .. code-block:: c nghttp2_session_client_new2(&session, callbacks, user_data, option); How to use built-in HTTP/2 extension frame handlers --------------------------------------------------- In the previous section, we talked about the user defined HTTP/2 extension frames. In this section, we talk about HTTP/2 extension frame support built into nghttp2 library. As of this writing, nghttp2 supports ALTSVC extension frame. To send ALTSVC frame, use `nghttp2_submit_altsvc()` function. To receive ALTSVC frame through built-in functionality, application has to use `nghttp2_option_set_builtin_recv_extension_type()` to indicate the willingness of receiving ALTSVC frame: .. code-block:: c nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_ALTSVC); This is very similar to the case when we used to receive user defined frames. If the same frame type is set using `nghttp2_option_set_builtin_recv_extension_type()` and `nghttp2_option_set_user_recv_extension_type()`, the latter takes precedence. Application can implement its own frame handler rather than using built-in handler. The :type:`nghttp2_option` must be set to :type:`nghttp2_session` on its creation, like so: .. code-block:: c nghttp2_session_client_new2(&session, callbacks, user_data, option); When ALTSVC is received, :type:`nghttp2_on_frame_recv_callback` will be called as usual. Stream priorities ----------------- The stream prioritization scheme described in :rfc:`7540`, which has been formally deprecated by :rfc:`9113`, has been removed. An application is advised to send :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` of value of 1 via `nghttp2_submit_settings()`, and migrate to :rfc:`9218`. The sender of this settings value disables :rfc:`7540` priorities, and instead it enables :rfc:`9218` Extensible Prioritization Scheme. This new prioritization scheme has 2 methods to convey the stream priorities to a remote endpoint: Priority header field and PRIORITY_UPDATE frame. nghttp2 supports both methods. In order to receive and process PRIORITY_UPDATE frame, server has to call `nghttp2_option_set_builtin_recv_extension_type()` with NGHTTP2_PRIORITY_UPDATE as type argument (see the above section), and pass the option to `nghttp2_session_server_new2()` or `nghttp2_session_server_new3()` to create a server session. Client can send Priority header field via `nghttp2_submit_request2()`. It can also send PRIORITY_UPDATE frame via `nghttp2_submit_priority_update()`. Server processes Priority header field in a request header field and updates the stream priority unless HTTP messaging rule enforcement is disabled (see `nghttp2_option_set_no_http_messaging()`). nghttp2-1.68.0/doc/PaxHeaders/nghttp2_option_set_max_deflate_dynamic_table_size.rst0000644000000000000000000000013115077107332025644 xustar0030 mtime=1761382106.499309127 29 atime=1761382106.60530866 30 ctime=1761382109.548299086 nghttp2-1.68.0/doc/nghttp2_option_set_max_deflate_dynamic_table_size.rst0000644000175100017510000000107115077107332026234 0ustar00runnerrunner nghttp2_option_set_max_deflate_dynamic_table_size ================================================= Synopsis -------- *#include * .. function:: void nghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option, size_t val) This option sets the maximum dynamic table size for deflating header fields. The default value is 4KiB. In HTTP/2, receiver of deflated header block can specify maximum dynamic table size. The actual maximum size is the minimum of the size receiver specified and this option value. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_on_invalid_header_callback.rst0000644000000000000000000000013215077107332027107 xustar0030 mtime=1761382106.501309119 30 atime=1761382106.662308409 30 ctime=1761382109.608298913 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_on_invalid_header_callback.rst0000644000175100017510000000120415077107332027474 0ustar00runnerrunner nghttp2_session_callbacks_set_on_invalid_header_callback ======================================================== Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_on_invalid_header_callback( nghttp2_session_callbacks *cbs, nghttp2_on_invalid_header_callback on_invalid_header_callback) Sets callback function invoked when an invalid header name/value pair is received. If both `nghttp2_session_callbacks_set_on_invalid_header_callback()` and `nghttp2_session_callbacks_set_on_invalid_header_callback2()` are used to set callbacks, the latter takes the precedence. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_mem_recv2.rst0000644000000000000000000000013115077107332020645 xustar0029 mtime=1761382106.50330911 30 atime=1761382106.724308136 30 ctime=1761382109.669298736 nghttp2-1.68.0/doc/nghttp2_session_mem_recv2.rst0000644000175100017510000000356715077107332021251 0ustar00runnerrunner nghttp2_session_mem_recv2 ========================= Synopsis -------- *#include * .. function:: nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, const uint8_t *in, size_t inlen) Processes data *in* as an input from the remote endpoint. The *inlen* indicates the number of bytes to receive in the *in*. This function behaves like `nghttp2_session_recv()` except that it does not use :type:`nghttp2_recv_callback` to receive data; the *in* is the only data for the invocation of this function. If all bytes are processed, this function returns. The other callbacks are called in the same way as they are in `nghttp2_session_recv()`. In the current implementation, this function always tries to processes *inlen* bytes of input data unless either an error occurs or :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is returned from :type:`nghttp2_on_header_callback` or :type:`nghttp2_on_data_chunk_recv_callback`. If :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is used, the return value includes the number of bytes which was used to produce the data or frame for the callback. This function returns the number of processed bytes, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` The callback function failed. :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC` Invalid client magic was detected. This error only returns when *session* was configured as server and `nghttp2_option_set_no_recv_client_magic()` is not used with nonzero value. :enum:`nghttp2_error.NGHTTP2_ERR_FLOODED` Flooding was detected in this HTTP/2 session, and it must be closed. This is most likely caused by misbehaviour of peer. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_submit_trailer.rst0000644000000000000000000000013215077107332020251 xustar0030 mtime=1761382106.506309097 30 atime=1761382106.783307876 30 ctime=1761382109.728298566 nghttp2-1.68.0/doc/nghttp2_submit_trailer.rst0000644000175100017510000000522715077107332020647 0ustar00runnerrunner nghttp2_submit_trailer ====================== Synopsis -------- *#include * .. function:: int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen) Submits trailer fields HEADERS against the stream *stream_id*. The *nva* is an array of name/value pair :type:`nghttp2_nv` with *nvlen* elements. The application must not include pseudo-header fields (headers whose names starts with ":") in *nva*. This function creates copies of all name/value pairs in *nva*. It also lower-cases all names in *nva*. The order of elements in *nva* is preserved. For header fields with :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name and value are not copied respectively. With :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to pass header field name in lowercase. The application should maintain the references to them until :type:`nghttp2_on_frame_send_callback` or :type:`nghttp2_on_frame_not_send_callback` is called. For server, trailer fields must follow response HEADERS or response DATA without END_STREAM flat set. The library does not enforce this requirement, and applications should do this for themselves. If `nghttp2_submit_trailer()` is called before any response HEADERS submission (usually by `nghttp2_submit_response2()`), the content of *nva* will be sent as response headers, which will result in error. This function has the same effect with `nghttp2_submit_headers()`, with flags = :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` and both pri_spec and stream_user_data to NULL. To submit trailer fields after `nghttp2_submit_response2()` is called, the application has to specify :type:`nghttp2_data_provider2` to `nghttp2_submit_response2()`. Inside of :type:`nghttp2_data_source_read_callback2`, when setting :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF`, also set :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_END_STREAM`. After that, the application can send trailer fields using `nghttp2_submit_trailer()`. `nghttp2_submit_trailer()` can be used inside :type:`nghttp2_data_source_read_callback2`. This function returns 0 if it succeeds and *stream_id* is -1. Otherwise, this function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The *stream_id* is 0. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_stream_get_previous_sibling.rst0000644000000000000000000000013115077107332023020 xustar0030 mtime=1761382106.504309105 30 atime=1761382106.753308008 29 ctime=1761382109.69929865 nghttp2-1.68.0/doc/nghttp2_stream_get_previous_sibling.rst0000644000175100017510000000067215077107332023416 0ustar00runnerrunner nghttp2_stream_get_previous_sibling =================================== Synopsis -------- *#include * .. function:: nghttp2_stream * nghttp2_stream_get_previous_sibling(nghttp2_stream *stream) .. warning:: Deprecated. :rfc:`7540` priorities are deprecated by :rfc:`9113`. Consider migrating to :rfc:`9218` extensible prioritization scheme. This function always returns NULL. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_send.rst0000644000000000000000000000013115077107332017717 xustar0030 mtime=1761382106.504309105 29 atime=1761382106.73030811 30 ctime=1761382109.676298716 nghttp2-1.68.0/doc/nghttp2_session_send.rst0000644000175100017510000000442115077107332020311 0ustar00runnerrunner nghttp2_session_send ==================== Synopsis -------- *#include * .. function:: int nghttp2_session_send(nghttp2_session *session) Sends pending frames to the remote peer. This function retrieves the highest prioritized frame from the outbound queue and sends it to the remote peer. It does this as many times as possible until the user callback :type:`nghttp2_send_callback2` returns :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`, the outbound queue becomes empty or flow control is triggered (remote window size becomes depleted or maximum number of concurrent streams is reached). This function calls several callback functions which are passed when initializing the *session*. Here is the simple time chart which tells when each callback is invoked: 1. Get the next frame to send from outbound queue. 2. Prepare transmission of the frame. 3. If the control frame cannot be sent because some preconditions are not met (e.g., request HEADERS cannot be sent after GOAWAY), :type:`nghttp2_on_frame_not_send_callback` is invoked. Abort the following steps. 4. If the frame is HEADERS, PUSH_PROMISE or DATA, :type:`nghttp2_select_padding_callback` is invoked. 5. If the frame is request HEADERS, the stream is opened here. 6. :type:`nghttp2_before_frame_send_callback` is invoked. 7. If :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL` is returned from :type:`nghttp2_before_frame_send_callback`, the current frame transmission is canceled, and :type:`nghttp2_on_frame_not_send_callback` is invoked. Abort the following steps. 8. :type:`nghttp2_send_callback2` is invoked one or more times to send the frame. 9. :type:`nghttp2_on_frame_send_callback` is invoked. 10. If the transmission of the frame triggers closure of the stream, the stream is closed and :type:`nghttp2_on_stream_close_callback` is invoked. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` The callback function failed. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_option_new.rst0000644000000000000000000000013215077107332017405 xustar0030 mtime=1761382106.499309127 30 atime=1761382106.602308674 30 ctime=1761382109.546299092 nghttp2-1.68.0/doc/nghttp2_option_new.rst0000644000175100017510000000076615077107332020006 0ustar00runnerrunner nghttp2_option_new ================== Synopsis -------- *#include * .. function:: int nghttp2_option_new(nghttp2_option **option_ptr) Initializes *\*option_ptr* with default values. When the application finished using this object, it can use `nghttp2_option_del()` to free its memory. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_client_new2.rst0000644000000000000000000000013215077107332021200 xustar0030 mtime=1761382106.502309114 30 atime=1761382106.687308299 30 ctime=1761382109.632298843 nghttp2-1.68.0/doc/nghttp2_session_client_new2.rst0000644000175100017510000000157315077107332021576 0ustar00runnerrunner nghttp2_session_client_new2 =========================== Synopsis -------- *#include * .. function:: int nghttp2_session_client_new2(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option) Like `nghttp2_session_client_new()`, but with additional options specified in the *option*. The *option* can be ``NULL`` and the call is equivalent to `nghttp2_session_client_new()`. This function does not take ownership *option*. The application is responsible for freeing *option* if it finishes using the object. The library code does not refer to *option* after this function returns. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_get_stream_effective_local_window_size.rst0000644000000000000000000000013115077107332026733 xustar0029 mtime=1761382106.50330911 30 atime=1761382106.713308185 30 ctime=1761382109.659298765 nghttp2-1.68.0/doc/nghttp2_session_get_stream_effective_local_window_size.rst0000644000175100017510000000165015077107332027326 0ustar00runnerrunner nghttp2_session_get_stream_effective_local_window_size ====================================================== Synopsis -------- *#include * .. function:: int32_t nghttp2_session_get_stream_effective_local_window_size( nghttp2_session *session, int32_t stream_id) Returns the local (receive) window size for the stream *stream_id*. The local window size can be adjusted by `nghttp2_submit_window_update()`. This function takes into account that and returns effective window size. This function does not take into account the amount of received data from the remote endpoint. Use `nghttp2_session_get_stream_local_window_size()` to know the amount of data the remote endpoint can send without receiving stream level WINDOW_UPDATE frame. Note that each stream is still subject to the connection level flow control. This function returns -1 if it fails. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_on_header_callback2.rst0000644000000000000000000000013215077107332025463 xustar0030 mtime=1761382106.501309119 30 atime=1761382106.660308418 30 ctime=1761382109.605298921 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_on_header_callback2.rst0000644000175100017510000000061015077107332026050 0ustar00runnerrunner nghttp2_session_callbacks_set_on_header_callback2 ================================================= Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_on_header_callback2( nghttp2_session_callbacks *cbs, nghttp2_on_header_callback2 on_header_callback2) Sets callback function invoked when a header name/value pair is received. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_inflate_hd3.rst0000644000000000000000000000013215077107332020057 xustar0030 mtime=1761382106.498309132 30 atime=1761382106.593308713 30 ctime=1761382109.537299118 nghttp2-1.68.0/doc/nghttp2_hd_inflate_hd3.rst0000644000175100017510000000642615077107332020457 0ustar00runnerrunner nghttp2_hd_inflate_hd3 ====================== Synopsis -------- *#include * .. function:: nghttp2_ssize nghttp2_hd_inflate_hd3( nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags, const uint8_t *in, size_t inlen, int in_final) Inflates name/value block stored in *in* with length *inlen*. This function performs decompression. For each successful emission of header name/value pair, :enum:`nghttp2_hd_inflate_flag.NGHTTP2_HD_INFLATE_EMIT` is set in *\*inflate_flags* and name/value pair is assigned to the *nv_out* and the function returns. The caller must not free the members of *nv_out*. The *nv_out* may include pointers to the memory region in the *in*. The caller must retain the *in* while the *nv_out* is used. The application should call this function repeatedly until the ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and return value is non-negative. If that happens, all given input data (*inlen* bytes) are processed successfully. Then the application must call `nghttp2_hd_inflate_end_headers()` to prepare for the next header block input. In other words, if *in_final* is nonzero, and this function returns *inlen*, you can assert that :enum:`nghttp2_hd_inflate_final.NGHTTP2_HD_INFLATE_FINAL` is set in *\*inflate_flags*. The caller can feed complete compressed header block. It also can feed it in several chunks. The caller must set *in_final* to nonzero if the given input is the last block of the compressed header. This function returns the number of bytes processed if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` Inflation process has failed. :enum:`nghttp2_error.NGHTTP2_ERR_BUFFER_ERROR` The header field name or value is too large. Example follows:: int inflate_header_block(nghttp2_hd_inflater *hd_inflater, uint8_t *in, size_t inlen, int final) { nghttp2_ssize rv; for(;;) { nghttp2_nv nv; int inflate_flags = 0; rv = nghttp2_hd_inflate_hd3(hd_inflater, &nv, &inflate_flags, in, inlen, final); if(rv < 0) { fprintf(stderr, "inflate failed with error code %td", rv); return -1; } in += rv; inlen -= rv; if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { fwrite(nv.name, nv.namelen, 1, stderr); fprintf(stderr, ": "); fwrite(nv.value, nv.valuelen, 1, stderr); fprintf(stderr, "\n"); } if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { nghttp2_hd_inflate_end_headers(hd_inflater); break; } if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) { break; } } return 0; } nghttp2-1.68.0/doc/PaxHeaders/nghttp2_option_set_max_outbound_ack.rst0000644000000000000000000000013115077107332023010 xustar0030 mtime=1761382106.499309127 29 atime=1761382106.62130859 30 ctime=1761382109.565299037 nghttp2-1.68.0/doc/nghttp2_option_set_max_outbound_ack.rst0000644000175100017510000000076715077107332023413 0ustar00runnerrunner nghttp2_option_set_max_outbound_ack =================================== Synopsis -------- *#include * .. function:: void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, size_t val) This function sets the maximum number of outgoing SETTINGS ACK and PING ACK frames retained in :type:`nghttp2_session` object. If more than those frames are retained, the peer is considered to be misbehaving and session will be closed. The default value is 1000. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_inflate_get_dynamic_table_size.rst0000644000000000000000000000013215077107332024065 xustar0030 mtime=1761382106.498309132 30 atime=1761382106.585308748 30 ctime=1761382109.529299141 nghttp2-1.68.0/doc/nghttp2_hd_inflate_get_dynamic_table_size.rst0000644000175100017510000000053315077107332024456 0ustar00runnerrunner nghttp2_hd_inflate_get_dynamic_table_size ========================================= Synopsis -------- *#include * .. function:: size_t nghttp2_hd_inflate_get_dynamic_table_size(nghttp2_hd_inflater *inflater) Returns the used dynamic table size, including the overhead 32 bytes per entry described in RFC 7541. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_get_extpri_stream_priority.rst0000644000000000000000000000013215077107332024455 xustar0030 mtime=1761382106.502309114 30 atime=1761382106.699308246 30 ctime=1761382109.645298806 nghttp2-1.68.0/doc/nghttp2_session_get_extpri_stream_priority.rst0000644000175100017510000000221015077107332025040 0ustar00runnerrunner nghttp2_session_get_extpri_stream_priority ========================================== Synopsis -------- *#include * .. function:: int nghttp2_session_get_extpri_stream_priority( nghttp2_session *session, nghttp2_extpri *extpri, int32_t stream_id) Stores the stream priority of the existing stream denoted by *stream_id* in the object pointed by *extpri*. This function is meant to be used by server for :rfc:`9218` extensible prioritization scheme. If *session* is initialized as client, this function returns :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. If :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` of value of 1 is not submitted via `nghttp2_submit_settings()`, this function does nothing and returns 0. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` The *session* is initialized as client. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` *stream_id* is zero; or a stream denoted by *stream_id* is not found. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_data_source_read_length_callback.rst0000644000000000000000000000013215077107332030302 xustar0030 mtime=1761382106.500309123 30 atime=1761382106.644308489 30 ctime=1761382109.590298965 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_data_source_read_length_callback.rst0000644000175100017510000000124315077107332030672 0ustar00runnerrunner nghttp2_session_callbacks_set_data_source_read_length_callback ============================================================== Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_data_source_read_length_callback( nghttp2_session_callbacks *cbs, nghttp2_data_source_read_length_callback data_source_read_length_callback) .. warning:: Deprecated. Use `nghttp2_session_callbacks_set_data_source_read_length_callback2()` with :type:`nghttp2_data_source_read_length_callback2` instead. Sets callback function determine the length allowed in :type:`nghttp2_data_source_read_callback`. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_terminate_session2.rst0000644000000000000000000000013215077107332022604 xustar0030 mtime=1761382106.504309105 30 atime=1761382106.741308061 30 ctime=1761382109.687298684 nghttp2-1.68.0/doc/nghttp2_session_terminate_session2.rst0000644000175100017510000000232615077107332023177 0ustar00runnerrunner nghttp2_session_terminate_session2 ================================== Synopsis -------- *#include * .. function:: int nghttp2_session_terminate_session2(nghttp2_session *session, int32_t last_stream_id, uint32_t error_code) Signals the session so that the connection should be terminated. This function behaves like `nghttp2_session_terminate_session()`, but the last stream ID can be specified by the application for fine grained control of stream. The HTTP/2 specification does not allow last_stream_id to be increased. So the actual value sent as last_stream_id is the minimum value between the given *last_stream_id* and the last_stream_id we have previously sent to the peer. The *last_stream_id* is peer's stream ID or 0. So if *session* is initialized as client, *last_stream_id* must be even or 0. If *session* is initialized as server, *last_stream_id* must be odd or 0. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The *last_stream_id* is invalid. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_stream_get_next_sibling.rst0000644000000000000000000000013215077107332022123 xustar0030 mtime=1761382106.504309105 30 atime=1761382106.750308022 30 ctime=1761382109.696298658 nghttp2-1.68.0/doc/nghttp2_stream_get_next_sibling.rst0000644000175100017510000000065615077107332022522 0ustar00runnerrunner nghttp2_stream_get_next_sibling =============================== Synopsis -------- *#include * .. function:: nghttp2_stream * nghttp2_stream_get_next_sibling(nghttp2_stream *stream) .. warning:: Deprecated. :rfc:`7540` priorities are deprecated by :rfc:`9113`. Consider migrating to :rfc:`9218` extensible prioritization scheme. This function always returns NULL. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_deflate_hd2.rst0000644000000000000000000000013115077107332020037 xustar0030 mtime=1761382106.498309132 30 atime=1761382106.575308793 29 ctime=1761382109.51929917 nghttp2-1.68.0/doc/nghttp2_hd_deflate_hd2.rst0000644000175100017510000000240215077107332020426 0ustar00runnerrunner nghttp2_hd_deflate_hd2 ====================== Synopsis -------- *#include * .. function:: nghttp2_ssize nghttp2_hd_deflate_hd2(nghttp2_hd_deflater *deflater, uint8_t *buf, size_t buflen, const nghttp2_nv *nva, size_t nvlen) Deflates the *nva*, which has the *nvlen* name/value pairs, into the *buf* of length *buflen*. If *buf* is not large enough to store the deflated header block, this function fails with :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller should use `nghttp2_hd_deflate_bound()` to know the upper bound of buffer size required to deflate given header name/value pairs. Once this function fails, subsequent call of this function always returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. After this function returns, it is safe to delete the *nva*. This function returns the number of bytes written to *buf* if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` Deflation process has failed. :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` The provided *buflen* size is too small to hold the output. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_get_remote_window_size.rst0000644000000000000000000000013115077107332023541 xustar0029 mtime=1761382106.50330911 30 atime=1761382106.711308193 30 ctime=1761382109.657298771 nghttp2-1.68.0/doc/nghttp2_session_get_remote_window_size.rst0000644000175100017510000000047515077107332024140 0ustar00runnerrunner nghttp2_session_get_remote_window_size ====================================== Synopsis -------- *#include * .. function:: int32_t nghttp2_session_get_remote_window_size(nghttp2_session *session) Returns the remote window size for a connection. This function always succeeds. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_deflate_new.rst0000644000000000000000000000013215077107332020154 xustar0030 mtime=1761382106.498309132 30 atime=1761382106.579308775 30 ctime=1761382109.523299158 nghttp2-1.68.0/doc/nghttp2_hd_deflate_new.rst0000644000175100017510000000120015077107332020535 0ustar00runnerrunner nghttp2_hd_deflate_new ====================== Synopsis -------- *#include * .. function:: int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, size_t max_deflate_dynamic_table_size) Initializes *\*deflater_ptr* for deflating name/values pairs. The *max_deflate_dynamic_table_size* is the upper bound of header table size the deflater will use. If this function fails, *\*deflater_ptr* is left untouched. This function returns 0 if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_inflate_end_headers.rst0000644000000000000000000000013215077107332021642 xustar0030 mtime=1761382106.498309132 30 atime=1761382106.584308753 30 ctime=1761382109.528299144 nghttp2-1.68.0/doc/nghttp2_hd_inflate_end_headers.rst0000644000175100017510000000054215077107332022233 0ustar00runnerrunner nghttp2_hd_inflate_end_headers ============================== Synopsis -------- *#include * .. function:: int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater) Signals the end of decompression for one header block. This function returns 0 if it succeeds. Currently this function always succeeds. nghttp2-1.68.0/doc/PaxHeaders/Makefile.am0000644000000000000000000000013215077107270015061 xustar0030 mtime=1761382072.963444278 30 atime=1761382085.473387568 30 ctime=1761382109.468299317 nghttp2-1.68.0/doc/Makefile.am0000644000175100017510000003433415077107270015460 0ustar00runnerrunner# nghttp2 - HTTP/2 C Library # Copyright (c) 2012 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. man_MANS = nghttp.1 nghttpd.1 nghttpx.1 h2load.1 APIDOCS= \ macros.rst \ enums.rst \ types.rst \ nghttp2_check_authority.rst \ nghttp2_check_header_name.rst \ nghttp2_check_header_value.rst \ nghttp2_check_header_value_rfc9113.rst \ nghttp2_check_method.rst \ nghttp2_check_path.rst \ nghttp2_extpri_parse_priority.rst \ nghttp2_hd_deflate_bound.rst \ nghttp2_hd_deflate_change_table_size.rst \ nghttp2_hd_deflate_del.rst \ nghttp2_hd_deflate_get_dynamic_table_size.rst \ nghttp2_hd_deflate_get_max_dynamic_table_size.rst \ nghttp2_hd_deflate_get_num_table_entries.rst \ nghttp2_hd_deflate_get_table_entry.rst \ nghttp2_hd_deflate_hd.rst \ nghttp2_hd_deflate_hd2.rst \ nghttp2_hd_deflate_hd_vec.rst \ nghttp2_hd_deflate_hd_vec2.rst \ nghttp2_hd_deflate_new.rst \ nghttp2_hd_deflate_new2.rst \ nghttp2_hd_inflate_change_table_size.rst \ nghttp2_hd_inflate_del.rst \ nghttp2_hd_inflate_end_headers.rst \ nghttp2_hd_inflate_get_dynamic_table_size.rst \ nghttp2_hd_inflate_get_max_dynamic_table_size.rst \ nghttp2_hd_inflate_get_num_table_entries.rst \ nghttp2_hd_inflate_get_table_entry.rst \ nghttp2_hd_inflate_hd.rst \ nghttp2_hd_inflate_hd2.rst \ nghttp2_hd_inflate_hd3.rst \ nghttp2_hd_inflate_new.rst \ nghttp2_hd_inflate_new2.rst \ nghttp2_http2_strerror.rst \ nghttp2_is_fatal.rst \ nghttp2_nv_compare_name.rst \ nghttp2_option_del.rst \ nghttp2_option_new.rst \ nghttp2_option_set_builtin_recv_extension_type.rst \ nghttp2_option_set_max_deflate_dynamic_table_size.rst \ nghttp2_option_set_max_reserved_remote_streams.rst \ nghttp2_option_set_max_send_header_block_length.rst \ nghttp2_option_set_no_auto_ping_ack.rst \ nghttp2_option_set_no_auto_window_update.rst \ nghttp2_option_set_no_closed_streams.rst \ nghttp2_option_set_no_http_messaging.rst \ nghttp2_option_set_no_recv_client_magic.rst \ nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation.rst \ nghttp2_option_set_peer_max_concurrent_streams.rst \ nghttp2_option_set_server_fallback_rfc7540_priorities.rst \ nghttp2_option_set_user_recv_extension_type.rst \ nghttp2_option_set_max_continuations.rst \ nghttp2_option_set_max_outbound_ack.rst \ nghttp2_option_set_max_settings.rst \ nghttp2_option_set_stream_reset_rate_limit.rst \ nghttp2_option_set_glitch_rate_limit.rst \ nghttp2_pack_settings_payload.rst \ nghttp2_pack_settings_payload2.rst \ nghttp2_priority_spec_check_default.rst \ nghttp2_priority_spec_default_init.rst \ nghttp2_priority_spec_init.rst \ nghttp2_rcbuf_decref.rst \ nghttp2_rcbuf_get_buf.rst \ nghttp2_rcbuf_incref.rst \ nghttp2_rcbuf_is_static.rst \ nghttp2_select_next_protocol.rst \ nghttp2_select_alpn.rst \ nghttp2_session_callbacks_del.rst \ nghttp2_session_callbacks_new.rst \ nghttp2_session_callbacks_set_before_frame_send_callback.rst \ nghttp2_session_callbacks_set_data_source_read_length_callback.rst \ nghttp2_session_callbacks_set_data_source_read_length_callback2.rst \ nghttp2_session_callbacks_set_error_callback.rst \ nghttp2_session_callbacks_set_error_callback2.rst \ nghttp2_session_callbacks_set_on_begin_frame_callback.rst \ nghttp2_session_callbacks_set_on_begin_headers_callback.rst \ nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst \ nghttp2_session_callbacks_set_on_extension_chunk_recv_callback.rst \ nghttp2_session_callbacks_set_on_frame_not_send_callback.rst \ nghttp2_session_callbacks_set_on_frame_recv_callback.rst \ nghttp2_session_callbacks_set_on_frame_send_callback.rst \ nghttp2_session_callbacks_set_on_header_callback.rst \ nghttp2_session_callbacks_set_on_header_callback2.rst \ nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst \ nghttp2_session_callbacks_set_on_invalid_header_callback.rst \ nghttp2_session_callbacks_set_on_invalid_header_callback2.rst \ nghttp2_session_callbacks_set_on_stream_close_callback.rst \ nghttp2_session_callbacks_set_pack_extension_callback.rst \ nghttp2_session_callbacks_set_pack_extension_callback2.rst \ nghttp2_session_callbacks_set_rand_callback.rst \ nghttp2_session_callbacks_set_recv_callback.rst \ nghttp2_session_callbacks_set_recv_callback2.rst \ nghttp2_session_callbacks_set_select_padding_callback.rst \ nghttp2_session_callbacks_set_select_padding_callback2.rst \ nghttp2_session_callbacks_set_send_callback.rst \ nghttp2_session_callbacks_set_send_callback2.rst \ nghttp2_session_callbacks_set_send_data_callback.rst \ nghttp2_session_callbacks_set_unpack_extension_callback.rst \ nghttp2_session_change_extpri_stream_priority.rst \ nghttp2_session_change_stream_priority.rst \ nghttp2_session_check_request_allowed.rst \ nghttp2_session_check_server_session.rst \ nghttp2_session_client_new.rst \ nghttp2_session_client_new2.rst \ nghttp2_session_client_new3.rst \ nghttp2_session_consume.rst \ nghttp2_session_consume_connection.rst \ nghttp2_session_consume_stream.rst \ nghttp2_session_create_idle_stream.rst \ nghttp2_session_del.rst \ nghttp2_session_find_stream.rst \ nghttp2_session_get_effective_local_window_size.rst \ nghttp2_session_get_effective_recv_data_length.rst \ nghttp2_session_get_extpri_stream_priority.rst \ nghttp2_session_get_hd_deflate_dynamic_table_size.rst \ nghttp2_session_get_hd_inflate_dynamic_table_size.rst \ nghttp2_session_get_last_proc_stream_id.rst \ nghttp2_session_get_local_settings.rst \ nghttp2_session_get_local_window_size.rst \ nghttp2_session_get_next_stream_id.rst \ nghttp2_session_get_outbound_queue_size.rst \ nghttp2_session_get_remote_settings.rst \ nghttp2_session_get_remote_window_size.rst \ nghttp2_session_get_root_stream.rst \ nghttp2_session_get_stream_effective_local_window_size.rst \ nghttp2_session_get_stream_effective_recv_data_length.rst \ nghttp2_session_get_stream_local_close.rst \ nghttp2_session_get_stream_local_window_size.rst \ nghttp2_session_get_stream_remote_close.rst \ nghttp2_session_get_stream_remote_window_size.rst \ nghttp2_session_get_stream_user_data.rst \ nghttp2_session_mem_recv.rst \ nghttp2_session_mem_recv2.rst \ nghttp2_session_mem_send.rst \ nghttp2_session_mem_send2.rst \ nghttp2_session_recv.rst \ nghttp2_session_resume_data.rst \ nghttp2_session_send.rst \ nghttp2_session_server_new.rst \ nghttp2_session_server_new2.rst \ nghttp2_session_server_new3.rst \ nghttp2_session_set_local_window_size.rst \ nghttp2_session_set_next_stream_id.rst \ nghttp2_session_set_stream_user_data.rst \ nghttp2_session_set_user_data.rst \ nghttp2_session_terminate_session.rst \ nghttp2_session_terminate_session2.rst \ nghttp2_session_upgrade.rst \ nghttp2_session_upgrade2.rst \ nghttp2_session_want_read.rst \ nghttp2_session_want_write.rst \ nghttp2_set_debug_vprintf_callback.rst \ nghttp2_stream_get_first_child.rst \ nghttp2_stream_get_next_sibling.rst \ nghttp2_stream_get_parent.rst \ nghttp2_stream_get_previous_sibling.rst \ nghttp2_stream_get_state.rst \ nghttp2_stream_get_sum_dependency_weight.rst \ nghttp2_stream_get_weight.rst \ nghttp2_strerror.rst \ nghttp2_submit_altsvc.rst \ nghttp2_submit_data.rst \ nghttp2_submit_data2.rst \ nghttp2_submit_extension.rst \ nghttp2_submit_goaway.rst \ nghttp2_submit_headers.rst \ nghttp2_submit_origin.rst \ nghttp2_submit_ping.rst \ nghttp2_submit_priority.rst \ nghttp2_submit_priority_update.rst \ nghttp2_submit_push_promise.rst \ nghttp2_submit_request.rst \ nghttp2_submit_request2.rst \ nghttp2_submit_response.rst \ nghttp2_submit_response2.rst \ nghttp2_submit_rst_stream.rst \ nghttp2_submit_settings.rst \ nghttp2_submit_shutdown_notice.rst \ nghttp2_submit_trailer.rst \ nghttp2_submit_window_update.rst \ nghttp2_version.rst RST_FILES = \ README.rst \ programmers-guide.rst \ nghttp.1.rst \ nghttpd.1.rst \ nghttpx.1.rst \ h2load.1.rst EXTRA_DIST = \ CMakeLists.txt \ mkapiref.py \ $(RST_FILES) \ $(APIDOCS) \ sources/index.rst \ sources/tutorial-client.rst \ sources/tutorial-server.rst \ sources/tutorial-hpack.rst \ sources/nghttpx-howto.rst \ sources/h2load-howto.rst \ sources/building-android-binary.rst \ sources/contribute.rst \ _exts/rubydomain/LICENSE.rubydomain \ _exts/rubydomain/__init__.py \ _exts/rubydomain/rubydomain.py \ $(man_MANS) \ bash_completion/nghttp \ bash_completion/nghttpd \ bash_completion/nghttpx \ bash_completion/h2load # Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD ?= sphinx-build PAPER = BUILDDIR = manual # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" apiref.rst: \ $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \ $(top_srcdir)/lib/includes/nghttp2/nghttp2.h for i in $(RST_FILES); do [ -e $(builddir)/$$i ] || cp $(srcdir)/$$i $(builddir); done $(PYTHON) $(top_srcdir)/doc/mkapiref.py \ apiref.rst macros.rst enums.rst types.rst . $^ $(APIDOCS): apiref.rst clean-local: if [ $(srcdir) != $(builddir) ]; then for i in $(RST_FILES); do rm -f $(builddir)/$$i; done fi -rm -f apiref.rst -rm -f $(APIDOCS) -rm -rf $(BUILDDIR)/* html-local: apiref.rst $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/nghttp2.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/nghttp2.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/nghttp2" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/nghttp2" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: apiref.rst $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." nghttp2-1.68.0/doc/PaxHeaders/nghttp2_pack_settings_payload.rst0000644000000000000000000000013215077107332021573 xustar0030 mtime=1761382106.500309123 30 atime=1761382106.626308568 30 ctime=1761382109.570299022 nghttp2-1.68.0/doc/nghttp2_pack_settings_payload.rst0000644000175100017510000000237415077107332022171 0ustar00runnerrunner nghttp2_pack_settings_payload ============================= Synopsis -------- *#include * .. function:: ssize_t nghttp2_pack_settings_payload( uint8_t *buf, size_t buflen, const nghttp2_settings_entry *iv, size_t niv) .. warning:: Deprecated. Use `nghttp2_pack_settings_payload2()` instead. Serializes the SETTINGS values *iv* in the *buf*. The size of the *buf* is specified by *buflen*. The number of entries in the *iv* array is given by *niv*. The required space in *buf* for the *niv* entries is ``6*niv`` bytes and if the given buffer is too small, an error is returned. This function is used mainly for creating a SETTINGS payload to be sent with the ``HTTP2-Settings`` header field in an HTTP Upgrade request. The data written in *buf* is NOT base64url encoded and the application is responsible for encoding. This function returns the number of bytes written in *buf*, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The *iv* contains duplicate settings ID or invalid value. :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` The provided *buflen* size is too small to hold the output. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_inflate_hd2.rst0000644000000000000000000000013215077107332020056 xustar0030 mtime=1761382106.498309132 30 atime=1761382106.592308718 30 ctime=1761382109.536299121 nghttp2-1.68.0/doc/nghttp2_hd_inflate_hd2.rst0000644000175100017510000000653715077107332020461 0ustar00runnerrunner nghttp2_hd_inflate_hd2 ====================== Synopsis -------- *#include * .. function:: ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags, const uint8_t *in, size_t inlen, int in_final) .. warning:: Deprecated. Use `nghttp2_hd_inflate_hd3()` instead. Inflates name/value block stored in *in* with length *inlen*. This function performs decompression. For each successful emission of header name/value pair, :enum:`nghttp2_hd_inflate_flag.NGHTTP2_HD_INFLATE_EMIT` is set in *\*inflate_flags* and name/value pair is assigned to the *nv_out* and the function returns. The caller must not free the members of *nv_out*. The *nv_out* may include pointers to the memory region in the *in*. The caller must retain the *in* while the *nv_out* is used. The application should call this function repeatedly until the ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and return value is non-negative. If that happens, all given input data (*inlen* bytes) are processed successfully. Then the application must call `nghttp2_hd_inflate_end_headers()` to prepare for the next header block input. In other words, if *in_final* is nonzero, and this function returns *inlen*, you can assert that :enum:`nghttp2_hd_inflate_final.NGHTTP2_HD_INFLATE_FINAL` is set in *\*inflate_flags*. The caller can feed complete compressed header block. It also can feed it in several chunks. The caller must set *in_final* to nonzero if the given input is the last block of the compressed header. This function returns the number of bytes processed if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` Inflation process has failed. :enum:`nghttp2_error.NGHTTP2_ERR_BUFFER_ERROR` The header field name or value is too large. Example follows:: int inflate_header_block(nghttp2_hd_inflater *hd_inflater, uint8_t *in, size_t inlen, int final) { ssize_t rv; for(;;) { nghttp2_nv nv; int inflate_flags = 0; rv = nghttp2_hd_inflate_hd2(hd_inflater, &nv, &inflate_flags, in, inlen, final); if(rv < 0) { fprintf(stderr, "inflate failed with error code %zd", rv); return -1; } in += rv; inlen -= rv; if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { fwrite(nv.name, nv.namelen, 1, stderr); fprintf(stderr, ": "); fwrite(nv.value, nv.valuelen, 1, stderr); fprintf(stderr, "\n"); } if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { nghttp2_hd_inflate_end_headers(hd_inflater); break; } if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) { break; } } return 0; } nghttp2-1.68.0/doc/PaxHeaders/nghttpx.1.rst0000644000000000000000000000013215077107270015412 xustar0030 mtime=1761382072.965444268 30 atime=1761382106.549308907 30 ctime=1761382109.493299245 nghttp2-1.68.0/doc/nghttpx.1.rst0000644000175100017510000025356315077107270016020 0ustar00runnerrunner .. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY. .. program:: nghttpx nghttpx(1) ========== SYNOPSIS -------- **nghttpx** [OPTIONS]... [ ] DESCRIPTION ----------- A reverse proxy for HTTP/3, HTTP/2, and HTTP/1. .. describe:: Set path to server's private key. Required unless "no-tls" parameter is used in :option:`--frontend` option. .. describe:: Set path to server's certificate. Required unless "no-tls" parameter is used in :option:`--frontend` option. OPTIONS ------- The options are categorized into several groups. Connections ~~~~~~~~~~~ .. option:: -b, --backend=(,|unix:)[;[[:...]][[;]...] Set backend host and port. The multiple backend addresses are accepted by repeating this option. UNIX domain socket can be specified by prefixing path name with "unix:" (e.g., unix:/var/run/backend.sock). Optionally, if s are given, the backend address is only used if request matches the pattern. The pattern matching is closely designed to ServeMux in net/http package of Go programming language. consists of path, host + path or just host. The path must start with "*/*". If it ends with "*/*", it matches all request path in its subtree. To deal with the request to the directory without trailing slash, the path which ends with "*/*" also matches the request path which only lacks trailing '*/*' (e.g., path "*/foo/*" matches request path "*/foo*"). If it does not end with "*/*", it performs exact match against the request path. If host is given, it performs a match against the request host. For a request received on the frontend listener with "sni-fwd" parameter enabled, SNI host is used instead of a request host. If host alone is given, "*/*" is appended to it, so that it matches all request paths under the host (e.g., specifying "nghttp2.org" equals to "nghttp2.org/"). CONNECT method is treated specially. It does not have path, and we don't allow empty path. To workaround this, we assume that CONNECT method has "*/*" as path. Patterns with host take precedence over patterns with just path. Then, longer patterns take precedence over shorter ones. Host can include "\*" in the left most position to indicate wildcard match (only suffix match is done). The "\*" must match at least one character. For example, host pattern "\*.nghttp2.org" matches against "www.nghttp2.org" and "git.ngttp2.org", but does not match against "nghttp2.org". The exact hosts match takes precedence over the wildcard hosts match. If path part ends with "\*", it is treated as wildcard path. The wildcard path behaves differently from the normal path. For normal path, match is made around the boundary of path component separator,"*/*". On the other hand, the wildcard path does not take into account the path component separator. All paths which include the wildcard path without last "\*" as prefix, and are strictly longer than wildcard path without last "\*" are matched. "\*" must match at least one character. For example, the pattern "*/foo\**" matches "*/foo/*" and "*/foobar*". But it does not match "*/foo*", or "*/fo*". If is omitted or empty string, "*/*" is used as pattern, which matches all request paths (catch-all pattern). The catch-all backend must be given. When doing a match, nghttpx made some normalization to pattern, request host and path. For host part, they are converted to lower case. For path part, percent-encoded unreserved characters defined in RFC 3986 are decoded, and any dot-segments (".." and ".") are resolved and removed. For example, :option:`-b`\'127.0.0.1,8080;nghttp2.org/httpbin/' matches the request host "nghttp2.org" and the request path "*/httpbin/get*", but does not match the request host "nghttp2.org" and the request path "*/index.html*". The multiple s can be specified, delimiting them by ":". Specifying :option:`-b`\'127.0.0.1,8080;nghttp2.org:www.nghttp2.org' has the same effect to specify :option:`-b`\'127.0.0.1,8080;nghttp2.org' and :option:`-b`\'127.0.0.1,8080;www.nghttp2.org'. The backend addresses sharing same are grouped together forming load balancing group. Several parameters are accepted after . The parameters are delimited by ";". The available parameters are: "proto=", "tls", "sni=", "fall=", "rise=", "affinity=", "dns", "redirect-if-not-tls", "upgrade-scheme", "mruby=", "read-timeout=", "write-timeout=", "group=", "group-weight=", "weight=", and "dnf". The parameter consists of keyword, and optionally followed by "=" and value. For example, the parameter "proto=h2" consists of the keyword "proto" and value "h2". The parameter "tls" consists of the keyword "tls" without value. Each parameter is described as follows. The backend application protocol can be specified using optional "proto" parameter, and in the form of "proto=". should be one of the following list without quotes: "h2", "http/1.1". The default value of is "http/1.1". Note that usually "h2" refers to HTTP/2 over TLS. But in this option, it may mean HTTP/2 over cleartext TCP unless "tls" keyword is used (see below). TLS can be enabled by specifying optional "tls" parameter. TLS is not enabled by default. With "sni=" parameter, it can override the TLS SNI field value with given . This will default to the backend name The feature to detect whether backend is online or offline can be enabled using optional "fall" and "rise" parameters. Using "fall=" parameter, if nghttpx cannot connect to a this backend times in a row, this backend is assumed to be offline, and it is excluded from load balancing. If is 0, this backend never be excluded from load balancing whatever times nghttpx cannot connect to it, and this is the default. There is also "rise=" parameter. After backend was excluded from load balancing group, nghttpx periodically attempts to make a connection to the failed backend, and if the connection is made successfully times in a row, the backend is assumed to be online, and it is now eligible for load balancing target. If is 0, a backend is permanently offline, once it goes in that state, and this is the default behaviour. The session affinity is enabled using "affinity=" parameter. If "ip" is given in , client IP based session affinity is enabled. If "cookie" is given in , cookie based session affinity is enabled. If "none" is given in , session affinity is disabled, and this is the default. The session affinity is enabled per . If at least one backend has "affinity" parameter, and its is not "none", session affinity is enabled for all backend servers sharing the same . It is advised to set "affinity" parameter to all backend explicitly if session affinity is desired. The session affinity may break if one of the backend gets unreachable, or backend settings are reloaded or replaced by API. If "affinity=cookie" is used, the additional configuration is required. "affinity-cookie-name=" must be used to specify a name of cookie to use. Optionally, "affinity-cookie-path=" can be used to specify a path which cookie is applied. The optional "affinity-cookie-secure=" controls the Secure attribute of a cookie. The default value is "auto", and the Secure attribute is determined by a request scheme. If a request scheme is "https", then Secure attribute is set. Otherwise, it is not set. If is "yes", the Secure attribute is always set. If is "no", the Secure attribute is always omitted. "affinity-cookie-stickiness=" controls stickiness of this affinity. If is "loose", removing or adding a backend server might break the affinity and the request might be forwarded to a different backend server. If is "strict", removing the designated backend server breaks affinity, but adding new backend server does not cause breakage. If the designated backend server becomes unavailable, new backend server is chosen as if the request does not have an affinity cookie. defaults to "loose". By default, name resolution of backend host name is done at start up, or reloading configuration. If "dns" parameter is given, name resolution takes place dynamically. This is useful if backend address changes frequently. If "dns" is given, name resolution of backend host name at start up, or reloading configuration is skipped. If "redirect-if-not-tls" parameter is used, the matched backend requires that frontend connection is TLS encrypted. If it isn't, nghttpx responds to the request with 308 status code, and https URI the client should use instead is included in Location header field. The port number in redirect URI is 443 by default, and can be changed using :option:`--redirect-https-port` option. If at least one backend has "redirect-if-not-tls" parameter, this feature is enabled for all backend servers sharing the same . It is advised to set "redirect-if-no-tls" parameter to all backends explicitly if this feature is desired. If "upgrade-scheme" parameter is used along with "tls" parameter, HTTP/2 :scheme pseudo header field is changed to "https" from "http" when forwarding a request to this particular backend. This is a workaround for a backend server which requires "https" :scheme pseudo header field on TLS encrypted connection. "mruby=" parameter specifies a path to mruby script file which is invoked when this pattern is matched. All backends which share the same pattern must have the same mruby path. "read-timeout=" and "write-timeout=" parameters specify the read and write timeout of the backend connection when this pattern is matched. All backends which share the same pattern must have the same timeouts. If these timeouts are entirely omitted for a pattern, :option:`--backend-read-timeout` and :option:`--backend-write-timeout` are used. "group=" parameter specifies the name of group this backend address belongs to. By default, it belongs to the unnamed default group. The name of group is unique per pattern. "group-weight=" parameter specifies the weight of the group. The higher weight gets more frequently selected by the load balancing algorithm. must be [1, 256] inclusive. The weight 8 has 4 times more weight than 2. must be the same for all addresses which share the same . If "group-weight" is omitted in an address, but the other address which belongs to the same group specifies "group-weight", its weight is used. If no "group-weight" is specified for all addresses, the weight of a group becomes 1. "group" and "group-weight" are ignored if session affinity is enabled. "weight=" parameter specifies the weight of the backend address inside a group which this address belongs to. The higher weight gets more frequently selected by the load balancing algorithm. must be [1, 256] inclusive. The weight 8 has 4 times more weight than weight 2. If this parameter is omitted, weight becomes 1. "weight" is ignored if session affinity is enabled. If "dnf" parameter is specified, an incoming request is not forwarded to a backend and just consumed along with the request body (actually a backend server never be contacted). It is expected that the HTTP response is generated by mruby script (see "mruby=" parameter above). "dnf" is an abbreviation of "do not forward". Since ";" and ":" are used as delimiter, must not contain these characters. In order to include ":" in , one has to specify "%3A" (which is percent-encoded from of ":") instead. Since ";" has special meaning in shell, the option value must be quoted. Default: ``127.0.0.1,80`` .. option:: -f, --frontend=(,|unix:)[[;]...] Set frontend host and port. If is '\*', it assumes all addresses including both IPv4 and IPv6. UNIX domain socket can be specified by prefixing path name with "unix:" (e.g., unix:/var/run/nghttpx.sock). This option can be used multiple times to listen to multiple addresses. This option can take 0 or more parameters, which are described below. Note that "api" and "healthmon" parameters are mutually exclusive. Optionally, TLS can be disabled by specifying "no-tls" parameter. TLS is enabled by default. If "sni-fwd" parameter is used, when performing a match to select a backend server, SNI host name received from the client is used instead of the request host. See :option:`--backend` option about the pattern match. To make this frontend as API endpoint, specify "api" parameter. This is disabled by default. It is important to limit the access to the API frontend. Otherwise, someone may change the backend server, and break your services, or expose confidential information to the outside the world. To make this frontend as health monitor endpoint, specify "healthmon" parameter. This is disabled by default. Any requests which come through this address are replied with 200 HTTP status, without no body. To accept PROXY protocol version 1 and 2 on frontend connection, specify "proxyproto" parameter. This is disabled by default. To receive HTTP/3 (QUIC) traffic, specify "quic" parameter. It makes nghttpx listen on UDP port rather than TCP port. UNIX domain socket, "api", and "healthmon" parameters cannot be used with "quic" parameter. Default: ``*,3000`` .. option:: --backlog= Set listen backlog size. Default: ``65536`` .. option:: --backend-address-family=(auto|IPv4|IPv6) Specify address family of backend connections. If "auto" is given, both IPv4 and IPv6 are considered. If "IPv4" is given, only IPv4 address is considered. If "IPv6" is given, only IPv6 address is considered. Default: ``auto`` .. option:: --backend-http-proxy-uri= Specify proxy URI in the form http://[:@]:. If a proxy requires authentication, specify and . Note that they must be properly percent-encoded. This proxy is used when the backend connection is HTTP/2. First, make a CONNECT request to the proxy and it connects to the backend on behalf of nghttpx. This forms tunnel. After that, nghttpx performs SSL/TLS handshake with the downstream through the tunnel. The timeouts when connecting and making CONNECT request can be specified by :option:`--backend-read-timeout` and :option:`--backend-write-timeout` options. Performance ~~~~~~~~~~~ .. option:: -n, --workers= Set the number of worker threads. Default: ``1`` .. option:: --single-thread Run everything in one thread inside the worker process. This feature is provided for better debugging experience, or for the platforms which lack thread support. If threading is disabled, this option is always enabled. .. option:: --read-rate= Set maximum average read rate on frontend connection. Setting 0 to this option means read rate is unlimited. Default: ``0`` .. option:: --read-burst= Set maximum read burst size on frontend connection. Setting 0 to this option means read burst size is unlimited. Default: ``0`` .. option:: --write-rate= Set maximum average write rate on frontend connection. Setting 0 to this option means write rate is unlimited. Default: ``0`` .. option:: --write-burst= Set maximum write burst size on frontend connection. Setting 0 to this option means write burst size is unlimited. Default: ``0`` .. option:: --worker-read-rate= Set maximum average read rate on frontend connection per worker. Setting 0 to this option means read rate is unlimited. Not implemented yet. Default: ``0`` .. option:: --worker-read-burst= Set maximum read burst size on frontend connection per worker. Setting 0 to this option means read burst size is unlimited. Not implemented yet. Default: ``0`` .. option:: --worker-write-rate= Set maximum average write rate on frontend connection per worker. Setting 0 to this option means write rate is unlimited. Not implemented yet. Default: ``0`` .. option:: --worker-write-burst= Set maximum write burst size on frontend connection per worker. Setting 0 to this option means write burst size is unlimited. Not implemented yet. Default: ``0`` .. option:: --worker-frontend-connections= Set maximum number of simultaneous connections frontend accepts. Setting 0 means unlimited. Default: ``0`` .. option:: --backend-connections-per-host= Set maximum number of backend concurrent connections (and/or streams in case of HTTP/2) per origin host. This option is meaningful when :option:`--http2-proxy` option is used. The origin host is determined by authority portion of request URI (or :authority header field for HTTP/2). To limit the number of connections per frontend for default mode, use :option:`--backend-connections-per-frontend`\. Default: ``8`` .. option:: --backend-connections-per-frontend= Set maximum number of backend concurrent connections (and/or streams in case of HTTP/2) per frontend. This option is only used for default mode. 0 means unlimited. To limit the number of connections per host with :option:`--http2-proxy` option, use :option:`--backend-connections-per-host`\. Default: ``0`` .. option:: --rlimit-nofile= Set maximum number of open files (RLIMIT_NOFILE) to . If 0 is given, nghttpx does not set the limit. Default: ``0`` .. option:: --rlimit-memlock= Set maximum number of bytes of memory that may be locked into RAM. If 0 is given, nghttpx does not set the limit. Default: ``0`` .. option:: --backend-request-buffer= Set buffer size used to store backend request. Default: ``16K`` .. option:: --backend-response-buffer= Set buffer size used to store backend response. Default: ``128K`` .. option:: --fastopen= Enables "TCP Fast Open" for the listening socket and limits the maximum length for the queue of connections that have not yet completed the three-way handshake. If value is 0 then fast open is disabled. Default: ``0`` .. option:: --no-kqueue Don't use kqueue. This option is only applicable for the platforms which have kqueue. For other platforms, this option will be simply ignored. Timeout ~~~~~~~ .. option:: --frontend-http2-idle-timeout= Specify idle timeout for HTTP/2 frontend connection. If no active streams exist for this duration, connection is closed. Default: ``3m`` .. option:: --frontend-http3-idle-timeout= Specify idle timeout for HTTP/3 frontend connection. If no active streams exist for this duration, connection is closed. Default: ``3m`` .. option:: --frontend-write-timeout= Specify write timeout for all frontend connections. Default: ``30s`` .. option:: --frontend-keep-alive-timeout= Specify keep-alive timeout for frontend HTTP/1 connection. Default: ``1m`` .. option:: --frontend-header-timeout= Specify duration that the server waits for an HTTP request header fields to be received completely. On timeout, HTTP/1 and HTTP/2 connections are closed. For HTTP/3, the stream is shutdown, and the connection itself is left intact. Default: ``1m`` .. option:: --stream-read-timeout= Specify read timeout for HTTP/2 streams. 0 means no timeout. Default: ``0`` .. option:: --stream-write-timeout= Specify write timeout for HTTP/2 streams. 0 means no timeout. Default: ``1m`` .. option:: --backend-read-timeout= Specify read timeout for backend connection. Default: ``1m`` .. option:: --backend-write-timeout= Specify write timeout for backend connection. Default: ``30s`` .. option:: --backend-connect-timeout= Specify timeout before establishing TCP connection to backend. Default: ``30s`` .. option:: --backend-keep-alive-timeout= Specify keep-alive timeout for backend HTTP/1 connection. Default: ``2s`` .. option:: --listener-disable-timeout= After accepting connection failed, connection listener is disabled for a given amount of time. Specifying 0 disables this feature. Default: ``30s`` .. option:: --frontend-http2-setting-timeout= Specify timeout before SETTINGS ACK is received from client. Default: ``10s`` .. option:: --backend-http2-settings-timeout= Specify timeout before SETTINGS ACK is received from backend server. Default: ``10s`` .. option:: --backend-max-backoff= Specify maximum backoff interval. This is used when doing health check against offline backend (see "fail" parameter in :option:`--backend` option). It is also used to limit the maximum interval to temporarily disable backend when nghttpx failed to connect to it. These intervals are calculated using exponential backoff, and consecutive failed attempts increase the interval. This option caps its maximum value. Default: ``2m`` SSL/TLS ~~~~~~~ .. option:: --ciphers= Set allowed cipher list for frontend connection. The format of the string is described in OpenSSL ciphers(1). This option sets cipher suites for TLSv1.2. Use :option:`--tls13-ciphers` for TLSv1.3. Default: ``ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384`` .. option:: --tls13-ciphers= Set allowed cipher list for frontend connection. The format of the string is described in OpenSSL ciphers(1). This option sets cipher suites for TLSv1.3. Use :option:`--ciphers` for TLSv1.2. Default: ``TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256`` .. option:: --client-ciphers= Set allowed cipher list for backend connection. The format of the string is described in OpenSSL ciphers(1). This option sets cipher suites for TLSv1.2. Use :option:`--tls13-client-ciphers` for TLSv1.3. Default: ``ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384`` .. option:: --tls13-client-ciphers= Set allowed cipher list for backend connection. The format of the string is described in OpenSSL ciphers(1). This option sets cipher suites for TLSv1.3. Use :option:`--client-ciphers` for TLSv1.2. Default: ``TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256`` .. option:: --groups= Set the supported group list for frontend connections. is a colon separated list of group NID or names in the preference order. The supported curves depend on the linked OpenSSL library. This function requires OpenSSL >= 1.0.2. Default: ``X25519:P-256:P-384:P-521`` .. option:: -k, --insecure Don't verify backend server's certificate if TLS is enabled for backend connections. .. option:: --cacert= Set path to trusted CA certificate file. It is used in backend TLS connections to verify peer's certificate. The file must be in PEM format. It can contain multiple certificates. If the linked OpenSSL is configured to load system wide certificates, they are loaded at startup regardless of this option. .. option:: --private-key-passwd-file= Path to file that contains password for the server's private key. If none is given and the private key is password protected it'll be requested interactively. .. option:: --subcert=:[[;]...] Specify additional certificate and private key file. nghttpx will choose certificates based on the hostname indicated by client using TLS SNI extension. If nghttpx is built with OpenSSL >= 1.0.2, the signature algorithms (e.g., ECDSA+SHA256) presented by client are also taken into consideration. This allows nghttpx to send ML-DSA or ECDSA certificate to modern clients, while sending RSA based certificate to older clients. This option can be used multiple times. Additional parameter can be specified in . The available is "sct-dir=". "sct-dir=" specifies the path to directory which contains \*.sct files for TLS signed_certificate_timestamp extension (RFC 6962). This feature requires OpenSSL >= 1.0.2. See also :option:`--tls-sct-dir` option. .. option:: --dh-param-file= Path to file that contains DH parameters in PEM format. Without this option, DHE cipher suites are not available. .. option:: --alpn-list= Comma delimited list of ALPN protocol identifier sorted in the order of preference. That means most desirable protocol comes first. The parameter must be delimited by a single comma only and any white spaces are treated as a part of protocol string. Default: ``h2,http/1.1`` .. option:: --verify-client Require and verify client certificate. .. option:: --verify-client-cacert= Path to file that contains CA certificates to verify client certificate. The file must be in PEM format. It can contain multiple certificates. .. option:: --verify-client-tolerate-expired Accept expired client certificate. Operator should handle the expired client certificate by some means (e.g., mruby script). Otherwise, this option might cause a security risk. .. option:: --client-private-key-file= Path to file that contains client private key used in backend client authentication. .. option:: --client-cert-file= Path to file that contains client certificate used in backend client authentication. .. option:: --tls-min-proto-version= Specify minimum SSL/TLS protocol. The name matching is done in case-insensitive manner. The versions between :option:`--tls-min-proto-version` and :option:`\--tls-max-proto-version` are enabled. If the protocol list advertised by client does not overlap this range, you will receive the error message "unknown protocol". The available versions are: TLSv1.3 and TLSv1.2 Default: ``TLSv1.2`` .. option:: --tls-max-proto-version= Specify maximum SSL/TLS protocol. The name matching is done in case-insensitive manner. The versions between :option:`--tls-min-proto-version` and :option:`\--tls-max-proto-version` are enabled. If the protocol list advertised by client does not overlap this range, you will receive the error message "unknown protocol". The available versions are: TLSv1.3 and TLSv1.2 Default: ``TLSv1.3`` .. option:: --tls-ticket-key-file= Path to file that contains random data to construct TLS session ticket parameters. If aes-128-cbc is given in :option:`--tls-ticket-key-cipher`\, the file must contain exactly 48 bytes. If aes-256-cbc is given in :option:`--tls-ticket-key-cipher`\, the file must contain exactly 80 bytes. This options can be used repeatedly to specify multiple ticket parameters. If several files are given, only the first key is used to encrypt TLS session tickets. Other keys are accepted but server will issue new session ticket with first key. This allows session key rotation. Please note that key rotation does not occur automatically. User should rearrange files or change options values and restart nghttpx gracefully. If opening or reading given file fails, all loaded keys are discarded and it is treated as if none of this option is given. If this option is not given or an error occurred while opening or reading a file, key is generated every 1 hour internally and they are valid for 12 hours. This is recommended if ticket key sharing between nghttpx instances is not required. .. option:: --tls-ticket-key-memcached=,[;tls] Specify address of memcached server to get TLS ticket keys for session resumption. This enables shared TLS ticket key between multiple nghttpx instances. nghttpx does not set TLS ticket key to memcached. The external ticket key generator is required. nghttpx just gets TLS ticket keys from memcached, and use them, possibly replacing current set of keys. It is up to extern TLS ticket key generator to rotate keys frequently. See "TLS SESSION TICKET RESUMPTION" section in manual page to know the data format in memcached entry. Optionally, memcached connection can be encrypted with TLS by specifying "tls" parameter. .. option:: --tls-ticket-key-memcached-address-family=(auto|IPv4|IPv6) Specify address family of memcached connections to get TLS ticket keys. If "auto" is given, both IPv4 and IPv6 are considered. If "IPv4" is given, only IPv4 address is considered. If "IPv6" is given, only IPv6 address is considered. Default: ``auto`` .. option:: --tls-ticket-key-memcached-interval= Set interval to get TLS ticket keys from memcached. Default: ``10m`` .. option:: --tls-ticket-key-memcached-max-retry= Set maximum number of consecutive retries before abandoning TLS ticket key retrieval. If this number is reached, the attempt is considered as failure, and "failure" count is incremented by 1, which contributed to the value controlled :option:`--tls-ticket-key-memcached-max-fail` option. Default: ``3`` .. option:: --tls-ticket-key-memcached-max-fail= Set maximum number of consecutive failure before disabling TLS ticket until next scheduled key retrieval. Default: ``2`` .. option:: --tls-ticket-key-cipher= Specify cipher to encrypt TLS session ticket. Specify either aes-128-cbc or aes-256-cbc. By default, aes-128-cbc is used. .. option:: --tls-ticket-key-memcached-cert-file= Path to client certificate for memcached connections to get TLS ticket keys. .. option:: --tls-ticket-key-memcached-private-key-file= Path to client private key for memcached connections to get TLS ticket keys. .. option:: --tls-dyn-rec-warmup-threshold= Specify the threshold size for TLS dynamic record size behaviour. During a TLS session, after the threshold number of bytes have been written, the TLS record size will be increased to the maximum allowed (16K). The max record size will continue to be used on the active TLS session. After :option:`--tls-dyn-rec-idle-timeout` has elapsed, the record size is reduced to 1300 bytes. Specify 0 to always use the maximum record size, regardless of idle period. This behaviour applies to all TLS based frontends, and TLS HTTP/2 backends. Default: ``1M`` .. option:: --tls-dyn-rec-idle-timeout= Specify TLS dynamic record size behaviour timeout. See :option:`--tls-dyn-rec-warmup-threshold` for more information. This behaviour applies to all TLS based frontends, and TLS HTTP/2 backends. Default: ``1s`` .. option:: --no-http2-cipher-block-list Allow block listed cipher suite on frontend HTTP/2 connection. See https://tools.ietf.org/html/rfc7540#appendix-A for the complete HTTP/2 cipher suites block list. .. option:: --client-no-http2-cipher-block-list Allow block listed cipher suite on backend HTTP/2 connection. See https://tools.ietf.org/html/rfc7540#appendix-A for the complete HTTP/2 cipher suites block list. .. option:: --tls-sct-dir= Specifies the directory where \*.sct files exist. All \*.sct files in are read, and sent as extension_data of TLS signed_certificate_timestamp (RFC 6962) to client. These \*.sct files are for the certificate specified in positional command-line argument , or certificate option in configuration file. For additional certificates, use :option:`--subcert` option. This option requires OpenSSL >= 1.0.2. .. option:: --psk-secrets= Read list of PSK identity and secrets from . This is used for frontend connection. The each line of input file is formatted as :, where is PSK identity, and is secret in hex. An empty line, and line which starts with '#' are skipped. The default enabled cipher list might not contain any PSK cipher suite. In that case, desired PSK cipher suites must be enabled using :option:`--ciphers` option. The desired PSK cipher suite may be block listed by HTTP/2. To use those cipher suites with HTTP/2, consider to use :option:`--no-http2-cipher-block-list` option. But be aware its implications. .. option:: --client-psk-secrets= Read PSK identity and secrets from . This is used for backend connection. The each line of input file is formatted as :, where is PSK identity, and is secret in hex. An empty line, and line which starts with '#' are skipped. The first identity and secret pair encountered is used. The default enabled cipher list might not contain any PSK cipher suite. In that case, desired PSK cipher suites must be enabled using :option:`--client-ciphers` option. The desired PSK cipher suite may be block listed by HTTP/2. To use those cipher suites with HTTP/2, consider to use :option:`--client-no-http2-cipher-block-list` option. But be aware its implications. .. option:: --tls-no-postpone-early-data By default, except for QUIC connections, nghttpx postpones forwarding HTTP requests sent in early data, including those sent in partially in it, until TLS handshake finishes. If all backend server recognizes "Early-Data" header field, using this option makes nghttpx not postpone forwarding request and get full potential of 0-RTT data. .. option:: --tls-max-early-data= Sets the maximum amount of 0-RTT data that server accepts. Default: ``16K`` .. option:: --tls-ktls Enable ktls. HTTP/2 ~~~~~~ .. option:: -c, --frontend-http2-max-concurrent-streams= Set the maximum number of the concurrent streams in one frontend HTTP/2 session. Default: ``100`` .. option:: --backend-http2-max-concurrent-streams= Set the maximum number of the concurrent streams in one backend HTTP/2 session. This sets maximum number of concurrent opened pushed streams. The maximum number of concurrent requests are set by a remote server. Default: ``100`` .. option:: --frontend-http2-window-size= Sets the per-stream initial window size of HTTP/2 frontend connection. Default: ``65535`` .. option:: --frontend-http2-connection-window-size= Sets the per-connection window size of HTTP/2 frontend connection. Default: ``65535`` .. option:: --backend-http2-window-size= Sets the initial window size of HTTP/2 backend connection. Default: ``65535`` .. option:: --backend-http2-connection-window-size= Sets the per-connection window size of HTTP/2 backend connection. Default: ``2147483647`` .. option:: --http2-no-cookie-crumbling Don't crumble cookie header field. .. option:: --padding= Add at most bytes to a HTTP/2 frame payload as padding. Specify 0 to disable padding. This option is meant for debugging purpose and not intended to enhance protocol security. .. option:: --no-server-push Disable HTTP/2 server push. Server push is supported by default mode and HTTP/2 frontend via Link header field. It is also supported if both frontend and backend are HTTP/2 in default mode. In this case, server push from backend session is relayed to frontend, and server push via Link header field is also supported. .. option:: --frontend-http2-optimize-write-buffer-size (Experimental) Enable write buffer size optimization in frontend HTTP/2 TLS connection. This optimization aims to reduce write buffer size so that it only contains bytes which can send immediately. This makes server more responsive to prioritized HTTP/2 stream because the buffering of lower priority stream is reduced. This option is only effective on recent Linux platform. .. option:: --frontend-http2-optimize-window-size (Experimental) Automatically tune connection level window size of frontend HTTP/2 TLS connection. If this feature is enabled, connection window size starts with the default window size, 65535 bytes. nghttpx automatically adjusts connection window size based on TCP receiving window size. The maximum window size is capped by the value specified by :option:`--frontend-http2-connection-window-size`\. Since the stream is subject to stream level window size, it should be adjusted using :option:`--frontend-http2-window-size` option as well. This option is only effective on recent Linux platform. .. option:: --frontend-http2-encoder-dynamic-table-size= Specify the maximum dynamic table size of HPACK encoder in the frontend HTTP/2 connection. The decoder (client) specifies the maximum dynamic table size it accepts. Then the negotiated dynamic table size is the minimum of this option value and the value which client specified. Default: ``4K`` .. option:: --frontend-http2-decoder-dynamic-table-size= Specify the maximum dynamic table size of HPACK decoder in the frontend HTTP/2 connection. Default: ``4K`` .. option:: --backend-http2-encoder-dynamic-table-size= Specify the maximum dynamic table size of HPACK encoder in the backend HTTP/2 connection. The decoder (backend) specifies the maximum dynamic table size it accepts. Then the negotiated dynamic table size is the minimum of this option value and the value which backend specified. Default: ``4K`` .. option:: --backend-http2-decoder-dynamic-table-size= Specify the maximum dynamic table size of HPACK decoder in the backend HTTP/2 connection. Default: ``4K`` Mode ~~~~ .. describe:: (default mode) Accept HTTP/2, and HTTP/1.1 over SSL/TLS. "no-tls" parameter is used in :option:`--frontend` option, accept HTTP/2 and HTTP/1.1 over cleartext TCP. The incoming HTTP/1.1 connection can be upgraded to HTTP/2 through HTTP Upgrade. .. option:: -s, --http2-proxy Like default mode, but enable forward proxy. This is so called HTTP/2 proxy mode. Logging ~~~~~~~ .. option:: -L, --log-level= Set the severity level of log output. must be one of INFO, NOTICE, WARN, ERROR and FATAL. Default: ``NOTICE`` .. option:: --accesslog-file= Set path to write access log. To reopen file, send USR1 signal to nghttpx. .. option:: --accesslog-syslog Send access log to syslog. If this option is used, :option:`--accesslog-file` option is ignored. .. option:: --accesslog-format= Specify format string for access log. The default format is combined format. The following variables are available: * $remote_addr: client IP address. * $time_local: local time in Common Log format. * $time_iso8601: local time in ISO 8601 format. * $request: HTTP request line. * $status: HTTP response status code. * $body_bytes_sent: the number of bytes sent to client as response body. * $http_: value of HTTP request header where '_' in is replaced with '-'. * $remote_port: client port. * $server_port: server port. * $request_time: request processing time in seconds with milliseconds resolution. * $pid: PID of the running process. * $alpn: ALPN identifier of the protocol which generates the response. For HTTP/1, ALPN is always http/1.1, regardless of minor version. * $tls_cipher: cipher used for SSL/TLS connection. * $tls_client_fingerprint_sha256: SHA-256 fingerprint of client certificate. * $tls_client_fingerprint_sha1: SHA-1 fingerprint of client certificate. * $tls_client_subject_name: subject name in client certificate. * $tls_client_issuer_name: issuer name in client certificate. * $tls_client_serial: serial number in client certificate. * $tls_protocol: protocol for SSL/TLS connection. * $tls_session_id: session ID for SSL/TLS connection. * $tls_session_reused: "r" if SSL/TLS session was reused. Otherwise, "." * $tls_sni: SNI server name for SSL/TLS connection. * $backend_host: backend host used to fulfill the request. "-" if backend host is not available. * $backend_port: backend port used to fulfill the request. "-" if backend host is not available. * $method: HTTP method * $path: Request path including query. For CONNECT request, authority is recorded. * $path_without_query: $path up to the first '?' character. For CONNECT request, authority is recorded. * $protocol_version: HTTP version (e.g., HTTP/1.1, HTTP/2) The variable can be enclosed by "{" and "}" for disambiguation (e.g., ${remote_addr}). Default: ``$remote_addr - - [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"`` .. option:: --accesslog-write-early Write access log when response header fields are received from backend rather than when request transaction finishes. .. option:: --errorlog-file= Set path to write error log. To reopen file, send USR1 signal to nghttpx. stderr will be redirected to the error log file unless :option:`--errorlog-syslog` is used. Default: ``/dev/stderr`` .. option:: --errorlog-syslog Send error log to syslog. If this option is used, :option:`--errorlog-file` option is ignored. .. option:: --syslog-facility= Set syslog facility to . Default: ``daemon`` HTTP ~~~~ .. option:: --add-x-forwarded-for Append X-Forwarded-For header field to the downstream request. .. option:: --strip-incoming-x-forwarded-for Strip X-Forwarded-For header field from inbound client requests. .. option:: --no-add-x-forwarded-proto Don't append additional X-Forwarded-Proto header field to the backend request. If inbound client sets X-Forwarded-Proto, and :option:`--no-strip-incoming-x-forwarded-proto` option is used, they are passed to the backend. .. option:: --no-strip-incoming-x-forwarded-proto Don't strip X-Forwarded-Proto header field from inbound client requests. .. option:: --add-forwarded= Append RFC 7239 Forwarded header field with parameters specified in comma delimited list . The supported parameters are "by", "for", "host", and "proto". By default, the value of "by" and "for" parameters are obfuscated string. See :option:`--forwarded-by` and :option:`--forwarded-for` options respectively. Note that nghttpx does not translate non-standard X-Forwarded-\* header fields into Forwarded header field, and vice versa. .. option:: --strip-incoming-forwarded Strip Forwarded header field from inbound client requests. .. option:: --forwarded-by=(obfuscated|ip|) Specify the parameter value sent out with "by" parameter of Forwarded header field. If "obfuscated" is given, the string is randomly generated at startup. If "ip" is given, the interface address of the connection, including port number, is sent with "by" parameter. In case of UNIX domain socket, "localhost" is used instead of address and port. User can also specify the static obfuscated string. The limitation is that it must start with "_", and only consists of character set [A-Za-z0-9._-], as described in RFC 7239. Default: ``obfuscated`` .. option:: --forwarded-for=(obfuscated|ip) Specify the parameter value sent out with "for" parameter of Forwarded header field. If "obfuscated" is given, the string is randomly generated for each client connection. If "ip" is given, the remote client address of the connection, without port number, is sent with "for" parameter. In case of UNIX domain socket, "localhost" is used instead of address. Default: ``obfuscated`` .. option:: --no-via Don't append to Via header field. If Via header field is received, it is left unaltered. .. option:: --no-strip-incoming-early-data Don't strip Early-Data header field from inbound client requests. .. option:: --no-location-rewrite Don't rewrite location header field in default mode. When :option:`--http2-proxy` is used, location header field will not be altered regardless of this option. .. option:: --host-rewrite Rewrite host and :authority header fields in default mode. When :option:`--http2-proxy` is used, these headers will not be altered regardless of this option. .. option:: --altsvc= Specify protocol ID, port, host and origin of alternative service. , and are optional. Empty and are allowed and they are treated as nothing is specified. They are advertised in alt-svc header field only in HTTP/1.1 frontend. This option can be used multiple times to specify multiple alternative services. Example: :option:`--altsvc`\="h2,443,,,ma=3600; persist=1" .. option:: --http2-altsvc= Just like :option:`--altsvc` option, but this altsvc is only sent in HTTP/2 frontend. .. option:: --add-request-header=
Specify additional header field to add to request header set. The field name must be lowercase. This option just appends header field and won't replace anything already set. This option can be used several times to specify multiple header fields. Example: :option:`--add-request-header`\="foo: bar" .. option:: --add-response-header=
Specify additional header field to add to response header set. The field name must be lowercase. This option just appends header field and won't replace anything already set. This option can be used several times to specify multiple header fields. Example: :option:`--add-response-header`\="foo: bar" .. option:: --request-header-field-buffer= Set maximum buffer size for incoming HTTP request header field list. This is the sum of header name and value in bytes. If trailer fields exist, they are counted towards this number. Default: ``64K`` .. option:: --max-request-header-fields= Set maximum number of incoming HTTP request header fields. If trailer fields exist, they are counted towards this number. Default: ``100`` .. option:: --response-header-field-buffer= Set maximum buffer size for incoming HTTP response header field list. This is the sum of header name and value in bytes. If trailer fields exist, they are counted towards this number. Default: ``64K`` .. option:: --max-response-header-fields= Set maximum number of incoming HTTP response header fields. If trailer fields exist, they are counted towards this number. Default: ``500`` .. option:: --error-page=(|*)= Set file path to custom error page served when nghttpx originally generates HTTP error status code . must be greater than or equal to 400, and at most 599. If "\*" is used instead of , it matches all HTTP status code. If error status code comes from backend server, the custom error pages are not used. .. option:: --server-name= Change server response header field value to . Default: ``nghttpx`` .. option:: --no-server-rewrite Don't rewrite server header field in default mode. When :option:`--http2-proxy` is used, these headers will not be altered regardless of this option. .. option:: --redirect-https-port= Specify the port number which appears in Location header field when redirect to HTTPS URI is made due to "redirect-if-not-tls" parameter in :option:`--backend` option. Default: ``443`` .. option:: --require-http-scheme Always require http or https scheme in HTTP request. It also requires that https scheme must be used for an encrypted connection. Otherwise, http scheme must be used. This option is recommended for a server deployment which directly faces clients and the services it provides only require http or https scheme. API ~~~ .. option:: --api-max-request-body= Set the maximum size of request body for API request. Default: ``32M`` DNS ~~~ .. option:: --dns-cache-timeout= Set duration that cached DNS results remain valid. Note that nghttpx caches the unsuccessful results as well. Default: ``10s`` .. option:: --dns-lookup-timeout= Set timeout that DNS server is given to respond to the initial DNS query. For the 2nd and later queries, server is given time based on this timeout, and it is scaled linearly. Default: ``250ms`` .. option:: --dns-max-try= Set the number of DNS query before nghttpx gives up name lookup. Default: ``3`` .. option:: --frontend-max-requests= The number of requests that single frontend connection can process. For HTTP/2, this is the number of streams in one HTTP/2 connection. For HTTP/1, this is the number of keep alive requests. This is hint to nghttpx, and it may allow additional few requests. The default value is unlimited. Debug ~~~~~ .. option:: --frontend-http2-dump-request-header= Dumps request headers received by HTTP/2 frontend to the file denoted in . The output is done in HTTP/1 header field format and each header block is followed by an empty line. This option is not thread safe and MUST NOT be used with option :option:`-n`\, where >= 2. .. option:: --frontend-http2-dump-response-header= Dumps response headers sent from HTTP/2 frontend to the file denoted in . The output is done in HTTP/1 header field format and each header block is followed by an empty line. This option is not thread safe and MUST NOT be used with option :option:`-n`\, where >= 2. .. option:: -o, --frontend-frame-debug Print HTTP/2 frames in frontend to stderr. This option is not thread safe and MUST NOT be used with option :option:`-n`\=N, where N >= 2. Process ~~~~~~~ .. option:: -D, --daemon Run in a background. If :option:`-D` is used, the current working directory is changed to '*/*'. .. option:: --pid-file= Set path to save PID of this program. .. option:: --user= Run this program as . This option is intended to be used to drop root privileges. .. option:: --single-process Run this program in a single process mode for debugging purpose. Without this option, nghttpx creates at least 2 processes: main and worker processes. If this option is used, main and worker are unified into a single process. nghttpx still spawns additional process if neverbleed is used. In the single process mode, the signal handling feature is disabled. .. option:: --max-worker-processes= The maximum number of worker processes. nghttpx spawns new worker process when it reloads its configuration. The previous worker process enters graceful termination period and will terminate when it finishes handling the existing connections. However, if reloading configurations happen very frequently, the worker processes might be piled up if they take a bit long time to finish the existing connections. With this option, if the number of worker processes exceeds the given value, the oldest worker process is terminated immediately. Specifying 0 means no limit and it is the default behaviour. .. option:: --worker-process-grace-shutdown-period= Maximum period for a worker process to terminate gracefully. When a worker process enters in graceful shutdown period (e.g., when nghttpx reloads its configuration) and it does not finish handling the existing connections in the given period of time, it is immediately terminated. Specifying 0 means no limit and it is the default behaviour. Scripting ~~~~~~~~~ .. option:: --mruby-file= Set mruby script file .. option:: --ignore-per-pattern-mruby-error Ignore mruby compile error for per-pattern mruby script file. If error occurred, it is treated as if no mruby file were specified for the pattern. HTTP/3 and QUIC ~~~~~~~~~~~~~~~ .. option:: --frontend-quic-idle-timeout= Specify an idle timeout for QUIC connection. Default: ``30s`` .. option:: --frontend-quic-debug-log Output QUIC debug log to */dev/stderr.* .. option:: --quic-bpf-program-file= Specify a path to eBPF program file reuseport_kern.o to direct an incoming QUIC UDP datagram to a correct socket. Default: ``/usr/local/lib/nghttp2/reuseport_kern.o`` .. option:: --frontend-quic-early-data Enable early data on frontend QUIC connections. nghttpx sends "Early-Data" header field to a backend server if a request is received in early data and handshake has not finished. All backend servers should deal with possibly replayed requests. .. option:: --frontend-quic-qlog-dir= Specify a directory where a qlog file is written for frontend QUIC connections. A qlog file is created per each QUIC connection. The file name is ISO8601 basic format, followed by "-", server Source Connection ID and ".sqlog". .. option:: --frontend-quic-require-token Require an address validation token for a frontend QUIC connection. Server sends a token in Retry packet or NEW_TOKEN frame in the previous connection. .. option:: --frontend-quic-congestion-controller= Specify a congestion controller algorithm for a frontend QUIC connection. should be either "cubic" or "bbr". Default: ``cubic`` .. option:: --frontend-quic-secret-file= Path to file that contains secure random data to be used as QUIC keying materials. It is used to derive keys for encrypting tokens and Connection IDs. It is not used to encrypt QUIC packets. Each line of this file must contain exactly 136 bytes hex-encoded string (when decoded the byte string is 68 bytes long). The first 3 bits of decoded byte string are used to identify the keying material. An empty line or a line which starts '#' is ignored. The file can contain more than one keying materials. Because the identifier is 3 bits, at most 8 keying materials are read and the remaining data is discarded. The first keying material in the file is primarily used for encryption and decryption for new connection. The other ones are used to decrypt data for the existing connections. Specifying multiple keying materials enables key rotation. Please note that key rotation does not occur automatically. User should update files or change options values and restart nghttpx gracefully. If opening or reading given file fails, all loaded keying materials are discarded and it is treated as if none of this option is given. If this option is not given or an error occurred while opening or reading a file, a keying material is generated internally on startup and reload. .. option:: --quic-server-id= Specify server ID encoded in Connection ID to identify this particular server instance. Connection ID is encrypted and this part is not visible in public. It must be 4 bytes long and must be encoded in hex string (which is 8 bytes long). If this option is omitted, a random server ID is generated on startup and configuration reload. .. option:: --frontend-quic-initial-rtt= Specify the initial RTT of the frontend QUIC connection. Default: ``333ms`` .. option:: --no-quic-bpf Disable eBPF. .. option:: --frontend-http3-window-size= Sets the per-stream initial window size of HTTP/3 frontend connection. Default: ``256K`` .. option:: --frontend-http3-connection-window-size= Sets the per-connection window size of HTTP/3 frontend connection. Default: ``1M`` .. option:: --frontend-http3-max-window-size= Sets the maximum per-stream window size of HTTP/3 frontend connection. The window size is adjusted based on the receiving rate of stream data. The initial value is the value specified by :option:`--frontend-http3-window-size` and the window size grows up to bytes. Default: ``6M`` .. option:: --frontend-http3-max-connection-window-size= Sets the maximum per-connection window size of HTTP/3 frontend connection. The window size is adjusted based on the receiving rate of stream data. The initial value is the value specified by :option:`--frontend-http3-connection-window-size` and the window size grows up to bytes. Default: ``8M`` .. option:: --frontend-http3-max-concurrent-streams= Set the maximum number of the concurrent streams in one frontend HTTP/3 connection. Default: ``100`` Misc ~~~~ .. option:: --conf= Load configuration from . Please note that nghttpx always tries to read the default configuration file if :option:`--conf` is not given. Default: ``/etc/nghttpx/nghttpx.conf`` .. option:: --include= Load additional configurations from . File is read when configuration parser encountered this option. This option can be used multiple times, or even recursively. .. option:: -v, --version Print version and exit. .. option:: -h, --help Print this help and exit. The argument is an integer and an optional unit (e.g., 10K is 10 * 1024). Units are K, M and G (powers of 1024). The argument is an integer and an optional unit (e.g., 1s is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms (hours, minutes, seconds and milliseconds, respectively). If a unit is omitted, a second is used as unit. FILES ----- */etc/nghttpx/nghttpx.conf* The default configuration file path nghttpx searches at startup. The configuration file path can be changed using :option:`--conf` option. Those lines which are staring ``#`` are treated as comment. The option name in the configuration file is the long command-line option name with leading ``--`` stripped (e.g., ``frontend``). Put ``=`` between option name and value. Don't put extra leading or trailing spaces. When specifying arguments including characters which have special meaning to a shell, we usually use quotes so that shell does not interpret them. When writing this configuration file, quotes for this purpose must not be used. For example, specify additional request header field, do this: .. code-block:: text add-request-header=foo: bar instead of: .. code-block:: text add-request-header="foo: bar" The options which do not take argument in the command-line *take* argument in the configuration file. Specify ``yes`` as an argument (e.g., ``http2-proxy=yes``). If other string is given, it is ignored. To specify private key and certificate file which are given as positional arguments in command-line, use ``private-key-file`` and ``certificate-file``. :option:`--conf` option cannot be used in the configuration file and will be ignored if specified. Error log Error log is written to stderr by default. It can be configured using :option:`--errorlog-file`. The format of log message is as follows: (:) It is a combination of date and time when the log is written. It is in ISO 8601 format. It is a main process ID. It is a process ID which writes this log. It is a thread ID which writes this log. It would be unique within . and They are source file name, and line number which produce this log. It is a log message body. SIGNALS ------- SIGQUIT Shutdown gracefully. First accept pending connections and stop accepting connection. After all connections are handled, nghttpx exits. SIGHUP Reload configuration file given in :option:`--conf`. SIGUSR1 Reopen log files. SIGUSR2 Fork and execute nghttpx. It will execute the binary in the same path with same command-line arguments and environment variables. As of nghttpx version 1.20.0, the new main process sends SIGQUIT to the original main process when it is ready to serve requests. For the earlier versions of nghttpx, user has to send SIGQUIT to the original main process. The difference between SIGUSR2 (+ SIGQUIT) and SIGHUP is that former is usually used to execute new binary, and the main process is newly spawned. On the other hand, the latter just reloads configuration file, and the same main process continues to exist. .. note:: nghttpx consists of multiple processes: one process for processing these signals, and another one for processing requests. The former spawns the latter. The former is called main process, and the latter is called worker process. If neverbleed is enabled, the worker process spawns neverbleed daemon process which does RSA key processing. The above signal must be sent to the main process. If the other processes received one of them, it is ignored. This behaviour of these processes may change in the future release. In other words, in the future release, the processes other than main process may terminate upon the reception of these signals. Therefore these signals should not be sent to the processes other than main process. SERVER PUSH ----------- nghttpx supports HTTP/2 server push in default mode with Link header field. nghttpx looks for Link header field (`RFC 5988 `_) in response headers from backend server and extracts URI-reference with parameter ``rel=preload`` (see `preload `_) and pushes those URIs to the frontend client. Here is a sample Link header field to initiate server push: .. code-block:: text Link: ; rel=preload Link: ; rel=preload Currently, the following restriction is applied for server push: 1. The associated stream must have method "GET" or "POST". The associated stream's status code must be 200. This limitation may be loosened in the future release. nghttpx also supports server push if both frontend and backend are HTTP/2 in default mode. In this case, in addition to server push via Link header field, server push from backend is forwarded to frontend HTTP/2 session. HTTP/2 server push will be disabled if :option:`--http2-proxy` is used. UNIX DOMAIN SOCKET ------------------ nghttpx supports UNIX domain socket with a filename for both frontend and backend connections. Please note that current nghttpx implementation does not delete a socket with a filename. And on start up, if nghttpx detects that the specified socket already exists in the file system, nghttpx first deletes it. However, if SIGUSR2 is used to execute new binary and both old and new configurations use same filename, new binary does not delete the socket and continues to use it. TLS SESSION RESUMPTION ---------------------- nghttpx supports TLS session resumption through both session ID and session ticket. SESSION ID RESUMPTION ~~~~~~~~~~~~~~~~~~~~~ By default, session ID is shared by all worker threads. TLS SESSION TICKET RESUMPTION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ By default, session ticket is shared by all worker threads. The automatic key rotation is also enabled by default. Every an hour, new encryption key is generated, and previous encryption key becomes decryption only key. We set session timeout to 12 hours, and thus we keep at most 12 keys. If :option:`--tls-ticket-key-memcached` is given, encryption keys are retrieved from memcached. nghttpx just reads keys from memcached; one has to deploy key generator program to update keys frequently (e.g., every 1 hour). The example key generator tlsticketupdate.go is available under contrib directory in nghttp2 archive. The memcached entry key is ``nghttpx:tls-ticket-key``. The data format stored in memcached is the binary format described below: .. code-block:: text +--------------+-------+----------------+ | VERSION (4) |LEN (2)|KEY(48 or 80) ... +--------------+-------+----------------+ ^ | | | +------------------------+ (LEN, KEY) pair can be repeated All numbers in the above figure is bytes. All integer fields are network byte order. First 4 bytes integer VERSION field, which must be 1. The 2 bytes integer LEN field gives the length of following KEY field, which contains key. If :option:`--tls-ticket-key-cipher`\=aes-128-cbc is used, LEN must be 48. If :option:`--tls-ticket-key-cipher`\=aes-256-cbc is used, LEN must be 80. LEN and KEY pair can be repeated multiple times to store multiple keys. The key appeared first is used as encryption key. All the remaining keys are used as decryption only. By default, connections to memcached server are not encrypted. To enable encryption, use ``tls`` keyword in :option:`--tls-ticket-key-memcached` option. If :option:`--tls-ticket-key-file` is given, encryption key is read from the given file. In this case, nghttpx does not rotate key automatically. To rotate key, one has to restart nghttpx (see SIGNALS). CERTIFICATE TRANSPARENCY ------------------------ nghttpx supports TLS ``signed_certificate_timestamp`` extension (`RFC 6962 `_). The relevant options are :option:`--tls-sct-dir` and ``sct-dir`` parameter in :option:`--subcert`. They takes a directory, and nghttpx reads all files whose extension is ``.sct`` under the directory. The ``*.sct`` files are encoded as ``SignedCertificateTimestamp`` struct described in `section 3.2 of RFC 69662 `_. This format is the same one used by `nginx-ct `_ and `mod_ssl_ct `_. `ct-submit `_ can be used to submit certificates to log servers, and obtain the ``SignedCertificateTimestamp`` struct which can be used with nghttpx. MRUBY SCRIPTING --------------- .. warning:: The current mruby extension API is experimental and not frozen. The API is subject to change in the future release. .. warning:: Almost all string value returned from method, or attribute is a fresh new mruby string, which involves memory allocation, and copies. Therefore, it is strongly recommended to store a return value in a local variable, and use it, instead of calling method or accessing attribute repeatedly. nghttpx allows users to extend its capability using mruby scripts. nghttpx has 2 hook points to execute mruby script: request phase and response phase. The request phase hook is invoked after all request header fields are received from client. The response phase hook is invoked after all response header fields are received from backend server. These hooks allows users to modify header fields, or common HTTP variables, like authority or request path, and even return custom response without forwarding request to backend servers. There are 2 levels of mruby script invocations: global and per-pattern. The global mruby script is set by :option:`--mruby-file` option and is called for all requests. The per-pattern mruby script is set by "mruby" parameter in :option:`-b` option. It is invoked for a request which matches the particular pattern. The order of hook invocation is: global request phase hook, per-pattern request phase hook, per-pattern response phase hook, and finally global response phase hook. If a hook returns a response, any later hooks are not invoked. The global request hook is invoked before the pattern matching is made and changing request path may affect the pattern matching. Please note that request and response hooks of per-pattern mruby script for a single request might not come from the same script. This might happen after a request hook is executed, backend failed for some reason, and at the same time, backend configuration is replaced by API request, and then the request uses new configuration on retry. The response hook from new configuration, if it is specified, will be invoked. The all mruby script will be evaluated once per thread on startup, and it must instantiate object and evaluate it as the return value (e.g., ``App.new``). This object is called app object. If app object defines ``on_req`` method, it is called with :rb:class:`Nghttpx::Env` object on request hook. Similarly, if app object defines ``on_resp`` method, it is called with :rb:class:`Nghttpx::Env` object on response hook. For each method invocation, user can can access :rb:class:`Nghttpx::Request` and :rb:class:`Nghttpx::Response` objects via :rb:attr:`Nghttpx::Env#req` and :rb:attr:`Nghttpx::Env#resp` respectively. .. rb:module:: Nghttpx .. rb:const:: REQUEST_PHASE Constant to represent request phase. .. rb:const:: RESPONSE_PHASE Constant to represent response phase. .. rb:class:: Env Object to represent current request specific context. .. rb:attr_reader:: req Return :rb:class:`Request` object. .. rb:attr_reader:: resp Return :rb:class:`Response` object. .. rb:attr_reader:: ctx Return Ruby hash object. It persists until request finishes. So values set in request phase hook can be retrieved in response phase hook. .. rb:attr_reader:: phase Return the current phase. .. rb:attr_reader:: remote_addr Return IP address of a remote client. If connection is made via UNIX domain socket, this returns the string "localhost". .. rb:attr_reader:: server_addr Return address of server that accepted the connection. This is a string which specified in :option:`--frontend` option, excluding port number, and not a resolved IP address. For UNIX domain socket, this is a path to UNIX domain socket. .. rb:attr_reader:: server_port Return port number of the server frontend which accepted the connection from client. .. rb:attr_reader:: tls_used Return true if TLS is used on the connection. .. rb:attr_reader:: tls_sni Return the TLS SNI value which client sent in this connection. .. rb:attr_reader:: tls_client_fingerprint_sha256 Return the SHA-256 fingerprint of a client certificate. .. rb:attr_reader:: tls_client_fingerprint_sha1 Return the SHA-1 fingerprint of a client certificate. .. rb:attr_reader:: tls_client_issuer_name Return the issuer name of a client certificate. .. rb:attr_reader:: tls_client_subject_name Return the subject name of a client certificate. .. rb:attr_reader:: tls_client_serial Return the serial number of a client certificate. .. rb:attr_reader:: tls_client_not_before Return the start date of a client certificate in seconds since the epoch. .. rb:attr_reader:: tls_client_not_after Return the end date of a client certificate in seconds since the epoch. .. rb:attr_reader:: tls_cipher Return a TLS cipher negotiated in this connection. .. rb:attr_reader:: tls_protocol Return a TLS protocol version negotiated in this connection. .. rb:attr_reader:: tls_session_id Return a session ID for this connection in hex string. .. rb:attr_reader:: tls_session_reused Return true if, and only if a SSL/TLS session is reused. .. rb:attr_reader:: alpn Return ALPN identifier negotiated in this connection. .. rb:attr_reader:: tls_handshake_finished Return true if SSL/TLS handshake has finished. If it returns false in the request phase hook, the request is received in TLSv1.3 early data (0-RTT) and might be vulnerable to the replay attack. nghttpx will send Early-Data header field to backend servers to indicate this. .. rb:class:: Request Object to represent request from client. The modification to Request object is allowed only in request phase hook. .. rb:attr_reader:: http_version_major Return HTTP major version. .. rb:attr_reader:: http_version_minor Return HTTP minor version. .. rb:attr_accessor:: method HTTP method. On assignment, copy of given value is assigned. We don't accept arbitrary method name. We will document them later, but well known methods, like GET, PUT and POST, are all supported. .. rb:attr_accessor:: authority Authority (i.e., example.org), including optional port component . On assignment, copy of given value is assigned. .. rb:attr_accessor:: scheme Scheme (i.e., http, https). On assignment, copy of given value is assigned. .. rb:attr_accessor:: path Request path, including query component (i.e., /index.html). On assignment, copy of given value is assigned. The path does not include authority component of URI. This may include query component. nghttpx makes certain normalization for path. It decodes percent-encoding for unreserved characters (see https://tools.ietf.org/html/rfc3986#section-2.3), and resolves ".." and ".". But it may leave characters which should be percent-encoded as is. So be careful when comparing path against desired string. .. rb:attr_reader:: headers Return Ruby hash containing copy of request header fields. Changing values in returned hash does not change request header fields actually used in request processing. Use :rb:meth:`Nghttpx::Request#add_header` or :rb:meth:`Nghttpx::Request#set_header` to change request header fields. .. rb:method:: add_header(key, value) Add header entry associated with key. The value can be single string or array of string. It does not replace any existing values associated with key. .. rb:method:: set_header(key, value) Set header entry associated with key. The value can be single string or array of string. It replaces any existing values associated with key. .. rb:method:: clear_headers Clear all existing request header fields. .. rb:method:: push(uri) Initiate to push resource identified by *uri*. Only HTTP/2 protocol supports this feature. For the other protocols, this method is noop. *uri* can be absolute URI, absolute path or relative path to the current request. For absolute or relative path, scheme and authority are inherited from the current request. Currently, method is always GET. nghttpx will issue request to backend servers to fulfill this request. The request and response phase hooks will be called for pushed resource as well. .. rb:class:: Response Object to represent response from backend server. .. rb:attr_reader:: http_version_major Return HTTP major version. .. rb:attr_reader:: http_version_minor Return HTTP minor version. .. rb:attr_accessor:: status HTTP status code. It must be in the range [200, 999], inclusive. The non-final status code is not supported in mruby scripting at the moment. .. rb:attr_reader:: headers Return Ruby hash containing copy of response header fields. Changing values in returned hash does not change response header fields actually used in response processing. Use :rb:meth:`Nghttpx::Response#add_header` or :rb:meth:`Nghttpx::Response#set_header` to change response header fields. .. rb:method:: add_header(key, value) Add header entry associated with key. The value can be single string or array of string. It does not replace any existing values associated with key. .. rb:method:: set_header(key, value) Set header entry associated with key. The value can be single string or array of string. It replaces any existing values associated with key. .. rb:method:: clear_headers Clear all existing response header fields. .. rb:method:: return(body) Return custom response *body* to a client. When this method is called in request phase hook, the request is not forwarded to the backend, and response phase hook for this request will not be invoked. When this method is called in response phase hook, response from backend server is canceled and discarded. The status code and response header fields should be set before using this method. To set status code, use :rb:attr:`Nghttpx::Response#status`. If status code is not set, 200 is used. To set response header fields, :rb:meth:`Nghttpx::Response#add_header` and :rb:meth:`Nghttpx::Response#set_header`. When this method is invoked in response phase hook, the response headers are filled with the ones received from backend server. To send completely custom header fields, first call :rb:meth:`Nghttpx::Response#clear_headers` to erase all existing header fields, and then add required header fields. It is an error to call this method twice for a given request. .. rb:method:: send_info(status, headers) Send non-final (informational) response to a client. *status* must be in the range [100, 199], inclusive. *headers* is a hash containing response header fields. Its key must be a string, and the associated value must be either string or array of strings. Since this is not a final response, even if this method is invoked, request is still forwarded to a backend unless :rb:meth:`Nghttpx::Response#return` is called. This method can be called multiple times. It cannot be called after :rb:meth:`Nghttpx::Response#return` is called. MRUBY EXAMPLES ~~~~~~~~~~~~~~ Modify request path: .. code-block:: ruby class App def on_req(env) env.req.path = "/apps#{env.req.path}" end end App.new Don't forget to instantiate and evaluate object at the last line. Restrict permission of viewing a content to a specific client addresses: .. code-block:: ruby class App def on_req(env) allowed_clients = ["127.0.0.1", "::1"] if env.req.path.start_with?("/log/") && !allowed_clients.include?(env.remote_addr) then env.resp.status = 404 env.resp.return "permission denied" end end end App.new API ENDPOINTS ------------- nghttpx exposes API endpoints to manipulate it via HTTP based API. By default, API endpoint is disabled. To enable it, add a dedicated frontend for API using :option:`--frontend` option with "api" parameter. All requests which come from this frontend address, will be treated as API request. The response is normally JSON dictionary, and at least includes the following keys: status The status of the request processing. The following values are defined: Success The request was successful. Failure The request was failed. No change has been made. code HTTP status code Additionally, depending on the API endpoint, ``data`` key may be present, and its value contains the API endpoint specific data. We wrote "normally", since nghttpx may return ordinal HTML response in some cases where the error has occurred before reaching API endpoint (e.g., header field is too large). The following section describes available API endpoints. POST /api/v1beta1/backendconfig ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This API replaces the current backend server settings with the requested ones. The request method should be POST, but PUT is also acceptable. The request body must be nghttpx configuration file format. For configuration file format, see `FILES`_ section. The line separator inside the request body must be single LF (0x0A). Currently, only :option:`backend <--backend>` option is parsed, the others are simply ignored. The semantics of this API is replace the current backend with the backend options in request body. Describe the desired set of backend severs, and nghttpx makes it happen. If there is no :option:`backend <--backend>` option is found in request body, the current set of backend is replaced with the :option:`backend <--backend>` option's default value, which is ``127.0.0.1,80``. The replacement is done instantly without breaking existing connections or requests. It also avoids any process creation as is the case with hot swapping with signals. The one limitation is that only numeric IP address is allowed in :option:`backend <--backend>` in request body unless "dns" parameter is used while non numeric hostname is allowed in command-line or configuration file is read using :option:`--conf`. GET /api/v1beta1/configrevision ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This API returns configuration revision of the current nghttpx. The configuration revision is opaque string, and it changes after each reloading by SIGHUP. With this API, an external application knows that whether nghttpx has finished reloading its configuration by comparing the configuration revisions between before and after reloading. It is recommended to disable persistent (keep-alive) connection for this purpose in order to avoid to send a request using the reused connection which may bound to an old process. This API returns response including ``data`` key. Its value is JSON object, and it contains at least the following key: configRevision The configuration revision of the current nghttpx SEE ALSO -------- :manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`h2load(1)` nghttp2-1.68.0/doc/PaxHeaders/nghttp2_option_set_server_fallback_rfc7540_priorities.rst0000644000000000000000000000013215077107332026237 xustar0030 mtime=1761382106.500309123 30 atime=1761382106.617308608 30 ctime=1761382109.561299048 nghttp2-1.68.0/doc/nghttp2_option_set_server_fallback_rfc7540_priorities.rst0000644000175100017510000000071515077107332026632 0ustar00runnerrunner nghttp2_option_set_server_fallback_rfc7540_priorities ===================================================== Synopsis -------- *#include * .. function:: void nghttp2_option_set_server_fallback_rfc7540_priorities(nghttp2_option *option, int val) .. warning:: Deprecated. :rfc:`7540` priorities have been removed. This function works as before, but it does not take any effect against :type:`nghttp2_session`. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_check_server_session.rst0000644000000000000000000000013215077107332023175 xustar0030 mtime=1761382106.502309114 30 atime=1761382106.684308313 30 ctime=1761382109.630298849 nghttp2-1.68.0/doc/nghttp2_session_check_server_session.rst0000644000175100017510000000043615077107332023570 0ustar00runnerrunner nghttp2_session_check_server_session ==================================== Synopsis -------- *#include * .. function:: int nghttp2_session_check_server_session(nghttp2_session *session) Returns nonzero if *session* is initialized as server side session. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_submit_push_promise.rst0000644000000000000000000000013215077107332021324 xustar0030 mtime=1761382106.505309101 30 atime=1761382106.772307925 30 ctime=1761382109.718298595 nghttp2-1.68.0/doc/nghttp2_submit_push_promise.rst0000644000175100017510000000650115077107332021716 0ustar00runnerrunner nghttp2_submit_push_promise =========================== Synopsis -------- *#include * .. function:: int32_t nghttp2_submit_push_promise( nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen, void *promised_stream_user_data) Submits PUSH_PROMISE frame. The *flags* is currently ignored. The library handles the CONTINUATION frame internally and it correctly sets END_HEADERS to the last sequence of the PUSH_PROMISE or CONTINUATION frame. The *stream_id* must be client initiated stream ID. The *nva* is an array of name/value pair :type:`nghttp2_nv` with *nvlen* elements. The application is responsible to include required pseudo-header fields (header field whose name starts with ":") in *nva* and must place pseudo-headers before regular header fields. This function creates copies of all name/value pairs in *nva*. It also lower-cases all names in *nva*. The order of elements in *nva* is preserved. For header fields with :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name and value are not copied respectively. With :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to pass header field name in lowercase. The application should maintain the references to them until :type:`nghttp2_on_frame_send_callback` or :type:`nghttp2_on_frame_not_send_callback` is called. The *promised_stream_user_data* is a pointer to an arbitrary data which is associated to the promised stream this frame will open and make it in reserved state. It is available using `nghttp2_session_get_stream_user_data()`. The application can access it in :type:`nghttp2_before_frame_send_callback` and :type:`nghttp2_on_frame_send_callback` of this frame. The client side is not allowed to use this function. To submit response headers and data, use `nghttp2_submit_response2()`. This function returns assigned promised stream ID if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` This function was invoked when *session* is initialized as client. :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` No stream ID is available because maximum stream ID was reached. :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` The *stream_id* is 0; The *stream_id* does not designate stream that peer initiated. :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_CLOSED` The stream was already closed; or the *stream_id* is invalid. .. warning:: This function returns assigned promised stream ID if it succeeds. As of 1.16.0, stream object for pushed resource is created when this function succeeds. In that case, the application can submit push response for the promised frame. In 1.15.0 or prior versions, pushed stream is not opened yet when this function succeeds. The application must not submit frame to that stream ID before :type:`nghttp2_before_frame_send_callback` is called for this frame. nghttp2-1.68.0/doc/PaxHeaders/index.rst.in0000644000000000000000000000013215077107270015273 xustar0030 mtime=1761382072.964444273 30 atime=1761382104.002320127 30 ctime=1761382109.475299297 nghttp2-1.68.0/doc/index.rst.in0000644000175100017510000000006015077107270015657 0ustar00runnerrunner.. include:: @top_srcdir@/doc/sources/index.rst nghttp2-1.68.0/doc/PaxHeaders/nghttp2_rcbuf_get_buf.rst0000644000000000000000000000013215077107332020020 xustar0030 mtime=1761382106.500309123 30 atime=1761382106.634308533 30 ctime=1761382109.579298996 nghttp2-1.68.0/doc/nghttp2_rcbuf_get_buf.rst0000644000175100017510000000034315077107332020410 0ustar00runnerrunner nghttp2_rcbuf_get_buf ===================== Synopsis -------- *#include * .. function:: nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf) Returns the underlying buffer managed by *rcbuf*. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_nv_compare_name.rst0000644000000000000000000000013015077107332020353 xustar0030 mtime=1761382106.499309127 30 atime=1761382106.599308687 28 ctime=1761382109.5432991 nghttp2-1.68.0/doc/nghttp2_nv_compare_name.rst0000644000175100017510000000100615077107332020742 0ustar00runnerrunner nghttp2_nv_compare_name ======================= Synopsis -------- *#include * .. function:: int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs) Compares ``lhs->name`` of length ``lhs->namelen`` bytes and ``rhs->name`` of length ``rhs->namelen`` bytes. Returns negative integer if ``lhs->name`` is found to be less than ``rhs->name``; or returns positive integer if ``lhs->name`` is found to be greater than ``rhs->name``; or returns 0 otherwise. nghttp2-1.68.0/doc/PaxHeaders/nghttpd.10000644000000000000000000000013215077107270014557 xustar0030 mtime=1761382072.964444273 30 atime=1761382106.802307793 30 ctime=1761382109.747298511 nghttp2-1.68.0/doc/nghttpd.10000644000175100017510000001270715077107270015156 0ustar00runnerrunner.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "NGHTTPD" "1" "Oct 25, 2025" "1.68.0" "nghttp2" .SH NAME nghttpd \- HTTP/2 server .SH SYNOPSIS .sp \fBnghttpd\fP [OPTION]... [ ] .SH DESCRIPTION .sp HTTP/2 server .INDENT 0.0 .TP .B Specify listening port number. .UNINDENT .INDENT 0.0 .TP .B Set path to server\(aqs private key. Required unless \fI\%\-\-no\-tls\fP is specified. .UNINDENT .INDENT 0.0 .TP .B Set path to server\(aqs certificate. Required unless \fI\%\-\-no\-tls\fP is specified. .UNINDENT .SH OPTIONS .INDENT 0.0 .TP .B \-a, \-\-address= The address to bind to. If not specified the default IP address determined by getaddrinfo is used. .UNINDENT .INDENT 0.0 .TP .B \-D, \-\-daemon Run in a background. If \fI\%\-D\fP is used, the current working directory is changed to \(aq\fI/\fP\(aq. Therefore if this option is used, \fI\%\-d\fP option must be specified. .UNINDENT .INDENT 0.0 .TP .B \-V, \-\-verify\-client The server sends a client certificate request. If the client did not return a certificate, the handshake is terminated. Currently, this option just requests a client certificate and does not verify it. .UNINDENT .INDENT 0.0 .TP .B \-d, \-\-htdocs= Specify document root. If this option is not specified, the document root is the current working directory. .UNINDENT .INDENT 0.0 .TP .B \-v, \-\-verbose Print debug information such as reception/ transmission of frames and name/value pairs. .UNINDENT .INDENT 0.0 .TP .B \-\-no\-tls Disable SSL/TLS. .UNINDENT .INDENT 0.0 .TP .B \-c, \-\-header\-table\-size= Specify decoder header table size. .UNINDENT .INDENT 0.0 .TP .B \-\-encoder\-header\-table\-size= Specify encoder header table size. The decoder (client) specifies the maximum dynamic table size it accepts. Then the negotiated dynamic table size is the minimum of this option value and the value which client specified. .UNINDENT .INDENT 0.0 .TP .B \-\-color Force colored log output. .UNINDENT .INDENT 0.0 .TP .B \-p, \-\-push== Push resources s when is requested. This option can be used repeatedly to specify multiple push configurations. and s are relative to document root. See \fI\%\-\-htdocs\fP option. Example: \fI\%\-p\fP/=/foo.png \fI\%\-p\fP/doc=/bar.css .UNINDENT .INDENT 0.0 .TP .B \-b, \-\-padding= Add at most bytes to a frame payload as padding. Specify 0 to disable padding. .UNINDENT .INDENT 0.0 .TP .B \-m, \-\-max\-concurrent\-streams= Set the maximum number of the concurrent streams in one HTTP/2 session. .sp Default: \fB100\fP .UNINDENT .INDENT 0.0 .TP .B \-n, \-\-workers= Set the number of worker threads. .sp Default: \fB1\fP .UNINDENT .INDENT 0.0 .TP .B \-e, \-\-error\-gzip Make error response gzipped. .UNINDENT .INDENT 0.0 .TP .B \-w, \-\-window\-bits= Sets the stream level initial window size to 2**\-1. .UNINDENT .INDENT 0.0 .TP .B \-W, \-\-connection\-window\-bits= Sets the connection level initial window size to 2**\-1. .UNINDENT .INDENT 0.0 .TP .B \-\-dh\-param\-file= Path to file that contains DH parameters in PEM format. Without this option, DHE cipher suites are not available. .UNINDENT .INDENT 0.0 .TP .B \-\-early\-response Start sending response when request HEADERS is received, rather than complete request is received. .UNINDENT .INDENT 0.0 .TP .B \-\-trailer=
Add a trailer header to a response.
must not include pseudo header field (header field name starting with \(aq:\(aq). The trailer is sent only if a response has body part. Example: \fI\%\-\-trailer\fP \(aqfoo: bar\(aq. .UNINDENT .INDENT 0.0 .TP .B \-\-hexdump Display the incoming traffic in hexadecimal (Canonical hex+ASCII display). If SSL/TLS is used, decrypted data are used. .UNINDENT .INDENT 0.0 .TP .B \-\-echo\-upload Send back uploaded content if method is POST or PUT. .UNINDENT .INDENT 0.0 .TP .B \-\-mime\-types\-file= Path to file that contains MIME media types and the extensions that represent them. .sp Default: \fB/etc/mime.types\fP .UNINDENT .INDENT 0.0 .TP .B \-\-no\-content\-length Don\(aqt send content\-length header field. .UNINDENT .INDENT 0.0 .TP .B \-\-groups= Specify the supported groups. .sp Default: \fBX25519:P\-256:P\-384:P\-521\fP .UNINDENT .INDENT 0.0 .TP .B \-\-ktls Enable ktls. .UNINDENT .INDENT 0.0 .TP .B \-\-version Display version information and exit. .UNINDENT .INDENT 0.0 .TP .B \-h, \-\-help Display this help and exit. .UNINDENT .sp The argument is an integer and an optional unit (e.g., 10K is 10 * 1024). Units are K, M and G (powers of 1024). .SH SEE ALSO .sp \fBnghttp(1)\fP, \fBnghttpx(1)\fP, \fBh2load(1)\fP .SH AUTHOR Tatsuhiro Tsujikawa .SH COPYRIGHT 2012, 2015, 2016, Tatsuhiro Tsujikawa .\" Generated by docutils manpage writer. . nghttp2-1.68.0/doc/PaxHeaders/tutorial-client.rst.in0000644000000000000000000000013215077107270017303 xustar0030 mtime=1761382072.967444259 30 atime=1761382104.030320004 30 ctime=1761382109.482299277 nghttp2-1.68.0/doc/tutorial-client.rst.in0000644000175100017510000000023415077107270017672 0ustar00runnerrunner.. include:: @top_srcdir@/doc/sources/tutorial-client.rst libevent-client.c ----------------- .. literalinclude:: @top_srcdir@/examples/libevent-client.c nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_mem_send.rst0000644000000000000000000000013115077107332020555 xustar0029 mtime=1761382106.50330911 30 atime=1761382106.725308132 30 ctime=1761382109.671298731 nghttp2-1.68.0/doc/nghttp2_session_mem_send.rst0000644000175100017510000000333015077107332021145 0ustar00runnerrunner nghttp2_session_mem_send ======================== Synopsis -------- *#include * .. function:: ssize_t nghttp2_session_mem_send(nghttp2_session *session, const uint8_t **data_ptr) .. warning:: Deprecated. Use `nghttp2_session_mem_send2()` instead. Returns the serialized data to send. This function behaves like `nghttp2_session_send()` except that it does not use :type:`nghttp2_send_callback` to transmit data. Instead, it assigns the pointer to the serialized data to the *\*data_ptr* and returns its length. The other callbacks are called in the same way as they are in `nghttp2_session_send()`. If no data is available to send, this function returns 0. This function may not return all serialized data in one invocation. To get all data, call this function repeatedly until it returns 0 or one of negative error codes. The assigned *\*data_ptr* is valid until the next call of `nghttp2_session_mem_send()` or `nghttp2_session_send()`. The caller must send all data before sending the next chunk of data. This function returns the length of the data pointed by the *\*data_ptr* if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. .. note:: This function may produce very small byte string. If that is the case, and application disables Nagle algorithm (``TCP_NODELAY``), then writing this small chunk leads to very small packet, and it is very inefficient. An application should be responsible to buffer up small chunks of data as necessary to avoid this situation. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_on_frame_recv_callback.rst0000644000000000000000000000013215077107332026262 xustar0030 mtime=1761382106.501309119 30 atime=1761382106.656308436 30 ctime=1761382109.601298933 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_on_frame_recv_callback.rst0000644000175100017510000000070415077107332026653 0ustar00runnerrunner nghttp2_session_callbacks_set_on_frame_recv_callback ==================================================== Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_on_frame_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_frame_recv_callback on_frame_recv_callback) Sets callback function invoked by `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` when a frame is received. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_option_set_user_recv_extension_type.rst0000644000000000000000000000013215077107332024621 xustar0030 mtime=1761382106.500309123 30 atime=1761382106.619308599 30 ctime=1761382109.562299046 nghttp2-1.68.0/doc/nghttp2_option_set_user_recv_extension_type.rst0000644000175100017510000000141315077107332025210 0ustar00runnerrunner nghttp2_option_set_user_recv_extension_type =========================================== Synopsis -------- *#include * .. function:: void nghttp2_option_set_user_recv_extension_type(nghttp2_option *option, uint8_t type) Sets extension frame type the application is willing to handle with user defined callbacks (see :type:`nghttp2_on_extension_chunk_recv_callback` and :type:`nghttp2_unpack_extension_callback`). The *type* is extension frame type, and must be strictly greater than 0x9. Otherwise, this function does nothing. The application can call this function multiple times to set more than one frame type to receive. The application does not have to call this function if it just sends extension frames. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_want_read.rst0000644000000000000000000000013215077107332020733 xustar0030 mtime=1761382106.504309105 30 atime=1761382106.745308044 30 ctime=1761382109.691298673 nghttp2-1.68.0/doc/nghttp2_session_want_read.rst0000644000175100017510000000063415077107332021326 0ustar00runnerrunner nghttp2_session_want_read ========================= Synopsis -------- *#include * .. function:: int nghttp2_session_want_read(nghttp2_session *session) Returns nonzero value if *session* wants to receive data from the remote peer. If both `nghttp2_session_want_read()` and `nghttp2_session_want_write()` return 0, the application should drop the connection. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_inflate_get_table_entry.rst0000644000000000000000000000013115077107332022547 xustar0030 mtime=1761382106.498309132 30 atime=1761382106.589308731 29 ctime=1761382109.53329913 nghttp2-1.68.0/doc/nghttp2_hd_inflate_get_table_entry.rst0000644000175100017510000000116715077107332023145 0ustar00runnerrunner nghttp2_hd_inflate_get_table_entry ================================== Synopsis -------- *#include * .. function:: const nghttp2_nv * nghttp2_hd_inflate_get_table_entry(nghttp2_hd_inflater *inflater, size_t idx) Returns the table entry denoted by *idx* from header table of *inflater*. The *idx* is 1-based, and idx=1 returns first entry of static table. idx=62 returns first entry of dynamic table if it exists. Specifying idx=0 is error, and this function returns NULL. If *idx* is strictly greater than the number of entries the tables contain, this function returns NULL. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_inflate_hd.rst0000644000000000000000000000013215077107332017774 xustar0030 mtime=1761382106.498309132 30 atime=1761382106.591308722 30 ctime=1761382109.534299126 nghttp2-1.68.0/doc/nghttp2_hd_inflate_hd.rst0000644000175100017510000000616115077107332020370 0ustar00runnerrunner nghttp2_hd_inflate_hd ===================== Synopsis -------- *#include * .. function:: ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags, uint8_t *in, size_t inlen, int in_final) .. warning:: Deprecated. Use `nghttp2_hd_inflate_hd2()` instead. Inflates name/value block stored in *in* with length *inlen*. This function performs decompression. For each successful emission of header name/value pair, :enum:`nghttp2_hd_inflate_flag.NGHTTP2_HD_INFLATE_EMIT` is set in *\*inflate_flags* and name/value pair is assigned to the *nv_out* and the function returns. The caller must not free the members of *nv_out*. The *nv_out* may include pointers to the memory region in the *in*. The caller must retain the *in* while the *nv_out* is used. The application should call this function repeatedly until the ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and return value is non-negative. This means the all input values are processed successfully. Then the application must call `nghttp2_hd_inflate_end_headers()` to prepare for the next header block input. The caller can feed complete compressed header block. It also can feed it in several chunks. The caller must set *in_final* to nonzero if the given input is the last block of the compressed header. This function returns the number of bytes processed if it succeeds, or one of the following negative error codes: :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` Out of memory. :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` Inflation process has failed. :enum:`nghttp2_error.NGHTTP2_ERR_BUFFER_ERROR` The header field name or value is too large. Example follows:: int inflate_header_block(nghttp2_hd_inflater *hd_inflater, uint8_t *in, size_t inlen, int final) { ssize_t rv; for(;;) { nghttp2_nv nv; int inflate_flags = 0; rv = nghttp2_hd_inflate_hd(hd_inflater, &nv, &inflate_flags, in, inlen, final); if(rv < 0) { fprintf(stderr, "inflate failed with error code %zd", rv); return -1; } in += rv; inlen -= rv; if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { fwrite(nv.name, nv.namelen, 1, stderr); fprintf(stderr, ": "); fwrite(nv.value, nv.valuelen, 1, stderr); fprintf(stderr, "\n"); } if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { nghttp2_hd_inflate_end_headers(hd_inflater); break; } if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) { break; } } return 0; } nghttp2-1.68.0/doc/PaxHeaders/nghttp2_stream_get_sum_dependency_weight.rst0000644000000000000000000000013215077107332024007 xustar0030 mtime=1761382106.505309101 30 atime=1761382106.756307995 30 ctime=1761382109.701298644 nghttp2-1.68.0/doc/nghttp2_stream_get_sum_dependency_weight.rst0000644000175100017510000000067515077107332024407 0ustar00runnerrunner nghttp2_stream_get_sum_dependency_weight ======================================== Synopsis -------- *#include * .. function:: int32_t nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream) .. warning:: Deprecated. :rfc:`7540` priorities are deprecated by :rfc:`9113`. Consider migrating to :rfc:`9218` extensible prioritization scheme. This function always returns 0. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_stream_get_first_child.rst0000644000000000000000000000013215077107332021730 xustar0030 mtime=1761382106.504309105 30 atime=1761382106.749308026 30 ctime=1761382109.695298661 nghttp2-1.68.0/doc/nghttp2_stream_get_first_child.rst0000644000175100017510000000065315077107332022324 0ustar00runnerrunner nghttp2_stream_get_first_child ============================== Synopsis -------- *#include * .. function:: nghttp2_stream * nghttp2_stream_get_first_child(nghttp2_stream *stream) .. warning:: Deprecated. :rfc:`7540` priorities are deprecated by :rfc:`9113`. Consider migrating to :rfc:`9218` extensible prioritization scheme. This function always returns NULL. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_want_write.rst0000644000000000000000000000013115077107332021151 xustar0030 mtime=1761382106.504309105 30 atime=1761382106.747308035 29 ctime=1761382109.69229867 nghttp2-1.68.0/doc/nghttp2_session_want_write.rst0000644000175100017510000000063215077107332021543 0ustar00runnerrunner nghttp2_session_want_write ========================== Synopsis -------- *#include * .. function:: int nghttp2_session_want_write(nghttp2_session *session) Returns nonzero value if *session* wants to send data to the remote peer. If both `nghttp2_session_want_read()` and `nghttp2_session_want_write()` return 0, the application should drop the connection. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_on_invalid_header_callback2.rst0000644000000000000000000000013115077107332027170 xustar0030 mtime=1761382106.501309119 30 atime=1761382106.663308405 29 ctime=1761382109.60929891 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_on_invalid_header_callback2.rst0000644000175100017510000000067115077107332027565 0ustar00runnerrunner nghttp2_session_callbacks_set_on_invalid_header_callback2 ========================================================= Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_on_invalid_header_callback2( nghttp2_session_callbacks *cbs, nghttp2_on_invalid_header_callback2 on_invalid_header_callback2) Sets callback function invoked when an invalid header name/value pair is received. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_strerror.rst0000644000000000000000000000013215077107332017106 xustar0030 mtime=1761382106.505309101 30 atime=1761382106.758307987 30 ctime=1761382109.704298635 nghttp2-1.68.0/doc/nghttp2_strerror.rst0000644000175100017510000000042415077107332017476 0ustar00runnerrunner nghttp2_strerror ================ Synopsis -------- *#include * .. function:: const char *nghttp2_strerror(int lib_error_code) Returns string describing the *lib_error_code*. The *lib_error_code* must be one of the :enum:`nghttp2_error`. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_get_stream_user_data.rst0000644000000000000000000000013015077107332023146 xustar0029 mtime=1761382106.50330911 29 atime=1761382106.72130815 30 ctime=1761382109.667298742 nghttp2-1.68.0/doc/nghttp2_session_get_stream_user_data.rst0000644000175100017510000000124415077107332023541 0ustar00runnerrunner nghttp2_session_get_stream_user_data ==================================== Synopsis -------- *#include * .. function:: void * nghttp2_session_get_stream_user_data(nghttp2_session *session, int32_t stream_id) Returns stream_user_data for the stream *stream_id*. The stream_user_data is provided by `nghttp2_submit_request2()`, `nghttp2_submit_headers()` or `nghttp2_session_set_stream_user_data()`. Unless it is set using `nghttp2_session_set_stream_user_data()`, if the stream is initiated by the remote endpoint, stream_user_data is always ``NULL``. If the stream does not exist, this function returns ``NULL``. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_get_local_settings.rst0000644000000000000000000000013115077107332022637 xustar0029 mtime=1761382106.50330911 30 atime=1761382106.704308224 30 ctime=1761382109.650298791 nghttp2-1.68.0/doc/nghttp2_session_get_local_settings.rst0000644000175100017510000000064015077107332023230 0ustar00runnerrunner nghttp2_session_get_local_settings ================================== Synopsis -------- *#include * .. function:: uint32_t nghttp2_session_get_local_settings( nghttp2_session *session, nghttp2_settings_id id) Returns the value of SETTINGS *id* of local endpoint acknowledged by the remote endpoint. The *id* must be one of the values defined in :enum:`nghttp2_settings_id`. nghttp2-1.68.0/doc/PaxHeaders/nghttp2.h.rst.in0000644000000000000000000000013215077107270016000 xustar0030 mtime=1761382072.964444273 30 atime=1761382104.113319638 30 ctime=1761382109.477299291 nghttp2-1.68.0/doc/nghttp2.h.rst.in0000644000175100017510000000012515077107270016366 0ustar00runnerrunnernghttp2.h ========= .. literalinclude:: @top_srcdir@/lib/includes/nghttp2/nghttp2.h nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_on_extension_chunk_recv_callback.rst0000644000000000000000000000013215077107332030374 xustar0030 mtime=1761382106.501309119 30 atime=1761382106.653308449 30 ctime=1761382109.599298939 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_on_extension_chunk_recv_callback.rst0000644000175100017510000000072115077107332030764 0ustar00runnerrunner nghttp2_session_callbacks_set_on_extension_chunk_recv_callback ============================================================== Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_on_extension_chunk_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback) Sets callback function invoked when chunk of extension frame payload is received. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_inflate_get_max_dynamic_table_size.rst0000644000000000000000000000013115077107332024731 xustar0030 mtime=1761382106.498309132 29 atime=1761382106.58730874 30 ctime=1761382109.530299138 nghttp2-1.68.0/doc/nghttp2_hd_inflate_get_max_dynamic_table_size.rst0000644000175100017510000000044515077107332025325 0ustar00runnerrunner nghttp2_hd_inflate_get_max_dynamic_table_size ============================================= Synopsis -------- *#include * .. function:: size_t nghttp2_hd_inflate_get_max_dynamic_table_size(nghttp2_hd_inflater *inflater) Returns the maximum dynamic table size. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_hd_deflate_new2.rst0000644000000000000000000000013215077107332020236 xustar0030 mtime=1761382106.498309132 30 atime=1761382106.580308771 30 ctime=1761382109.524299155 nghttp2-1.68.0/doc/nghttp2_hd_deflate_new2.rst0000644000175100017510000000125715077107332020633 0ustar00runnerrunner nghttp2_hd_deflate_new2 ======================= Synopsis -------- *#include * .. function:: int nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, size_t max_deflate_dynamic_table_size, nghttp2_mem *mem) Like `nghttp2_hd_deflate_new()`, but with additional custom memory allocator specified in the *mem*. The *mem* can be ``NULL`` and the call is equivalent to `nghttp2_hd_deflate_new()`. This function does not take ownership *mem*. The application is responsible for freeing *mem*. The library code does not refer to *mem* pointer after this function returns, so the application can safely free it. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_rcbuf_decref.rst0000644000000000000000000000013215077107332017635 xustar0030 mtime=1761382106.500309123 30 atime=1761382106.633308537 30 ctime=1761382109.578298999 nghttp2-1.68.0/doc/nghttp2_rcbuf_decref.rst0000644000175100017510000000055015077107332020225 0ustar00runnerrunner nghttp2_rcbuf_decref ==================== Synopsis -------- *#include * .. function:: void nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf) Decrements the reference count of *rcbuf* by 1. If the reference count becomes zero, the object pointed by *rcbuf* will be freed. In this case, application must not use *rcbuf* again. nghttp2-1.68.0/doc/PaxHeaders/nghttp2_session_callbacks_set_send_callback.rst0000644000000000000000000000013215077107332024406 xustar0030 mtime=1761382106.502309114 30 atime=1761382106.675308352 30 ctime=1761382109.621298875 nghttp2-1.68.0/doc/nghttp2_session_callbacks_set_send_callback.rst0000644000175100017510000000121615077107332024776 0ustar00runnerrunner nghttp2_session_callbacks_set_send_callback =========================================== Synopsis -------- *#include * .. function:: void nghttp2_session_callbacks_set_send_callback( nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback) .. warning:: Deprecated. Use `nghttp2_session_callbacks_set_send_callback2()` with :type:`nghttp2_send_callback2` instead. Sets callback function invoked when a session wants to send data to the remote peer. This callback is not necessary if the application uses solely `nghttp2_session_mem_send()` to serialize data to transmit. nghttp2-1.68.0/doc/PaxHeaders/conf.py.in0000644000000000000000000000013215077107270014731 xustar0030 mtime=1761382072.964444273 30 atime=1761382103.988320189 30 ctime=1761382109.472299306 nghttp2-1.68.0/doc/conf.py.in0000644000175100017510000002117215077107270015324 0ustar00runnerrunner# -*- coding: utf-8 -*- # nghttp2 - HTTP/2 C Library # Copyright (c) 2012 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # nghttp2 documentation build configuration file, created by # sphinx-quickstart on Sun Mar 11 22:57:49 2012. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) sys.path.insert(0, os.path.abspath('@top_srcdir@/doc/_exts')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['rubydomain.rubydomain'] # Add any paths that contain templates here, relative to this directory. templates_path = ['@top_srcdir@/_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'nghttp2' copyright = u'2012, 2015, 2016, Tatsuhiro Tsujikawa' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '@PACKAGE_VERSION@' # The full version, including alpha/beta/rc tags. release = '@PACKAGE_VERSION@' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['manual', 'README.rst', '*-header.rst', 'sources'] # The reST default role (used for this markup: `text`) to use for all documents. default_role = 'c:func' primary_domain = 'c' # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The default language to highlight source code in. The default is 'python'. highlight_language = 'c' # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = ['@top_srcdir@/doc/_themes'] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". #html_static_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = False # Custom sidebar templates, maps document names to template names. html_sidebars = { '**': ['menu.html', 'localtoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html'] } # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. html_show_sourcelink = False html_copy_source = False # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'nghttp2doc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'nghttp2.tex', u'nghttp2 Documentation', u'Tatsuhiro Tsujikawa', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('nghttp.1', 'nghttp', u'HTTP/2 client', [u'Tatsuhiro Tsujikawa'], 1), ('nghttpd.1', 'nghttpd', u'HTTP/2 server', [u'Tatsuhiro Tsujikawa'], 1), ('nghttpx.1', 'nghttpx', u'HTTP/2 proxy', [u'Tatsuhiro Tsujikawa'], 1), ('h2load.1', 'h2load', u'HTTP/2 benchmarking tool', [u'Tatsuhiro Tsujikawa'], 1) ] nghttp2-1.68.0/PaxHeaders/android-config0000644000000000000000000000012615077107270015071 xustar0030 mtime=1761382072.961444287 29 atime=1761382104.86431633 27 ctime=1761382107.848304 nghttp2-1.68.0/android-config0000755000175100017510000000270315077107270015463 0ustar00runnerrunner#!/bin/sh # # nghttp2 - HTTP/2 C Library # # Copyright (c) 2013 Tatsuhiro Tsujikawa # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. . ./android-env ./configure \ --disable-shared \ --host=$TARGET \ --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ --without-libxml2 \ --disable-examples \ --disable-threads \ CPPFLAGS="-fPIE -I$PREFIX/include" \ PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \ LDFLAGS="-fPIE -pie -L$PREFIX/lib" nghttp2-1.68.0/PaxHeaders/ltmain.sh0000644000000000000000000000013215077107303014075 xustar0030 mtime=1761382083.042398049 30 atime=1761382104.175319365 30 ctime=1761382107.843304015 nghttp2-1.68.0/ltmain.sh0000755000175100017510000121237515077107303014503 0ustar00runnerrunner#! /usr/bin/env sh ## DO NOT EDIT - This file generated from ./build-aux/ltmain.in ## by inline-source v2019-02-19.15 # libtool (GNU libtool) 2.4.7 # Provide generalized library-building support services. # Written by Gordon Matzigkeit , 1996 # Copyright (C) 1996-2019, 2021-2022 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . PROGRAM=libtool PACKAGE=libtool VERSION="2.4.7 Debian-2.4.7-7build1" package_revision=2.4.7 ## ------ ## ## Usage. ## ## ------ ## # Run './libtool --help' for help with using this script from the # command line. ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # After configure completes, it has a better idea of some of the # shell tools we need than the defaults used by the functions shared # with bootstrap, so set those here where they can still be over- # ridden by the user, but otherwise take precedence. : ${AUTOCONF="autoconf"} : ${AUTOMAKE="automake"} ## -------------------------- ## ## Source external libraries. ## ## -------------------------- ## # Much of our low-level functionality needs to be sourced from external # libraries, which are installed to $pkgauxdir. # Set a version string for this script. scriptversion=2019-02-19.15; # UTC # General shell script boiler plate, and helper functions. # Written by Gary V. Vaughan, 2004 # This is free software. There is NO warranty; not even for # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Copyright (C) 2004-2019, 2021 Bootstrap Authors # # This file is dual licensed under the terms of the MIT license # , and GPL version 2 or later # . You must apply one of # these licenses when using or redistributing this software or any of # the files within it. See the URLs above, or the file `LICENSE` # included in the Bootstrap distribution for the full license texts. # Please report bugs or propose patches to: # ## ------ ## ## Usage. ## ## ------ ## # Evaluate this file near the top of your script to gain access to # the functions and variables defined here: # # . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh # # If you need to override any of the default environment variable # settings, do that before evaluating this file. ## -------------------- ## ## Shell normalisation. ## ## -------------------- ## # Some shells need a little help to be as Bourne compatible as possible. # Before doing anything else, make sure all that help has been provided! DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi # NLS nuisances: We save the old values in case they are required later. _G_user_locale= _G_safe_locale= for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test set = \"\${$_G_var+set}\"; then save_$_G_var=\$$_G_var $_G_var=C export $_G_var _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\" _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\" fi" done # These NLS vars are set unconditionally (bootstrap issue #24). Unset those # in case the environment reset is needed later and the $save_* variant is not # defined (see the code above). LC_ALL=C LANGUAGE=C export LANGUAGE LC_ALL # Make sure IFS has a sensible default sp=' ' nl=' ' IFS="$sp $nl" # There are apparently some retarded systems that use ';' as a PATH separator! if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # func_unset VAR # -------------- # Portably unset VAR. # In some shells, an 'unset VAR' statement leaves a non-zero return # status if VAR is already unset, which might be problematic if the # statement is used at the end of a function (thus poisoning its return # value) or when 'set -e' is active (causing even a spurious abort of # the script in this case). func_unset () { { eval $1=; (eval unset $1) >/dev/null 2>&1 && eval unset $1 || : ; } } # Make sure CDPATH doesn't cause `cd` commands to output the target dir. func_unset CDPATH # Make sure ${,E,F}GREP behave sanely. func_unset GREP_OPTIONS ## ------------------------- ## ## Locate command utilities. ## ## ------------------------- ## # func_executable_p FILE # ---------------------- # Check that FILE is an executable regular file. func_executable_p () { test -f "$1" && test -x "$1" } # func_path_progs PROGS_LIST CHECK_FUNC [PATH] # -------------------------------------------- # Search for either a program that responds to --version with output # containing "GNU", or else returned by CHECK_FUNC otherwise, by # trying all the directories in PATH with each of the elements of # PROGS_LIST. # # CHECK_FUNC should accept the path to a candidate program, and # set $func_check_prog_result if it truncates its output less than # $_G_path_prog_max characters. func_path_progs () { _G_progs_list=$1 _G_check_func=$2 _G_PATH=${3-"$PATH"} _G_path_prog_max=0 _G_path_prog_found=false _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:} for _G_dir in $_G_PATH; do IFS=$_G_save_IFS test -z "$_G_dir" && _G_dir=. for _G_prog_name in $_G_progs_list; do for _exeext in '' .EXE; do _G_path_prog=$_G_dir/$_G_prog_name$_exeext func_executable_p "$_G_path_prog" || continue case `"$_G_path_prog" --version 2>&1` in *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;; *) $_G_check_func $_G_path_prog func_path_progs_result=$func_check_prog_result ;; esac $_G_path_prog_found && break 3 done done done IFS=$_G_save_IFS test -z "$func_path_progs_result" && { echo "no acceptable sed could be found in \$PATH" >&2 exit 1 } } # We want to be able to use the functions in this file before configure # has figured out where the best binaries are kept, which means we have # to search for them ourselves - except when the results are already set # where we skip the searches. # Unless the user overrides by setting SED, search the path for either GNU # sed, or the sed that truncates its output the least. test -z "$SED" && { _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for _G_i in 1 2 3 4 5 6 7; do _G_sed_script=$_G_sed_script$nl$_G_sed_script done echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed _G_sed_script= func_check_prog_sed () { _G_path_prog=$1 _G_count=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo '' >> conftest.nl "$_G_path_prog" -f conftest.sed conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "sed gsed" func_check_prog_sed "$PATH:/usr/xpg4/bin" rm -f conftest.sed SED=$func_path_progs_result } # Unless the user overrides by setting GREP, search the path for either GNU # grep, or the grep that truncates its output the least. test -z "$GREP" && { func_check_prog_grep () { _G_path_prog=$1 _G_count=0 _G_path_prog_max=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo 'GREP' >> conftest.nl "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "grep ggrep" func_check_prog_grep "$PATH:/usr/xpg4/bin" GREP=$func_path_progs_result } ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # All uppercase variable names are used for environment variables. These # variables can be overridden by the user before calling a script that # uses them if a suitable command of that name is not already available # in the command search PATH. : ${CP="cp -f"} : ${ECHO="printf %s\n"} : ${EGREP="$GREP -E"} : ${FGREP="$GREP -F"} : ${LN_S="ln -s"} : ${MAKE="make"} : ${MKDIR="mkdir"} : ${MV="mv -f"} : ${RM="rm -f"} : ${SHELL="${CONFIG_SHELL-/bin/sh}"} ## -------------------- ## ## Useful sed snippets. ## ## -------------------- ## sed_dirname='s|/[^/]*$||' sed_basename='s|^.*/||' # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='s|\([`"$\\]\)|\\\1|g' # Same as above, but do not quote variable references. sed_double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution that turns a string into a regex matching for the # string literally. sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g' # Sed substitution that converts a w32 file name or path # that contains forward slashes, into one that contains # (escaped) backslashes. A very naive implementation. sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' # Re-'\' parameter expansions in output of sed_double_quote_subst that # were '\'-ed in input to the same. If an odd number of '\' preceded a # '$' in input to sed_double_quote_subst, that '$' was protected from # expansion. Since each input '\' is now two '\'s, look for any number # of runs of four '\'s followed by two '\'s and then a '$'. '\' that '$'. _G_bs='\\' _G_bs2='\\\\' _G_bs4='\\\\\\\\' _G_dollar='\$' sed_double_backslash="\ s/$_G_bs4/&\\ /g s/^$_G_bs2$_G_dollar/$_G_bs&/ s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g s/\n//g" # require_check_ifs_backslash # --------------------------- # Check if we can use backslash as IFS='\' separator, and set # $check_ifs_backshlash_broken to ':' or 'false'. require_check_ifs_backslash=func_require_check_ifs_backslash func_require_check_ifs_backslash () { _G_save_IFS=$IFS IFS='\' _G_check_ifs_backshlash='a\\b' for _G_i in $_G_check_ifs_backshlash do case $_G_i in a) check_ifs_backshlash_broken=false ;; '') break ;; *) check_ifs_backshlash_broken=: break ;; esac done IFS=$_G_save_IFS require_check_ifs_backslash=: } ## ----------------- ## ## Global variables. ## ## ----------------- ## # Except for the global variables explicitly listed below, the following # functions in the '^func_' namespace, and the '^require_' namespace # variables initialised in the 'Resource management' section, sourcing # this file will not pollute your global namespace with anything # else. There's no portable way to scope variables in Bourne shell # though, so actually running these functions will sometimes place # results into a variable named after the function, and often use # temporary variables in the '^_G_' namespace. If you are careful to # avoid using those namespaces casually in your sourcing script, things # should continue to work as you expect. And, of course, you can freely # overwrite any of the functions or variables defined here before # calling anything to customize them. EXIT_SUCCESS=0 EXIT_FAILURE=1 EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. # Allow overriding, eg assuming that you follow the convention of # putting '$debug_cmd' at the start of all your functions, you can get # bash to show function call trace with: # # debug_cmd='echo "${FUNCNAME[0]} $*" >&2' bash your-script-name debug_cmd=${debug_cmd-":"} exit_cmd=: # By convention, finish your script with: # # exit $exit_status # # so that you can set exit_status to non-zero if you want to indicate # something went wrong during execution without actually bailing out at # the point of failure. exit_status=$EXIT_SUCCESS # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh # is ksh but when the shell is invoked as "sh" and the current value of # the _XPG environment variable is not equal to 1 (one), the special # positional parameter $0, within a function call, is the name of the # function. progpath=$0 # The name of this program. progname=`$ECHO "$progpath" |$SED "$sed_basename"` # Make sure we have an absolute progpath for reexecution: case $progpath in [\\/]*|[A-Za-z]:\\*) ;; *[\\/]*) progdir=`$ECHO "$progpath" |$SED "$sed_dirname"` progdir=`cd "$progdir" && pwd` progpath=$progdir/$progname ;; *) _G_IFS=$IFS IFS=${PATH_SEPARATOR-:} for progdir in $PATH; do IFS=$_G_IFS test -x "$progdir/$progname" && break done IFS=$_G_IFS test -n "$progdir" || progdir=`pwd` progpath=$progdir/$progname ;; esac ## ----------------- ## ## Standard options. ## ## ----------------- ## # The following options affect the operation of the functions defined # below, and should be set appropriately depending on run-time para- # meters passed on the command line. opt_dry_run=false opt_quiet=false opt_verbose=false # Categories 'all' and 'none' are always available. Append any others # you will pass as the first argument to func_warning from your own # code. warning_categories= # By default, display warnings according to 'opt_warning_types'. Set # 'warning_func' to ':' to elide all warnings, or func_fatal_error to # treat the next displayed warning as a fatal error. warning_func=func_warn_and_continue # Set to 'all' to display all warnings, 'none' to suppress all # warnings, or a space delimited list of some subset of # 'warning_categories' to display only the listed warnings. opt_warning_types=all ## -------------------- ## ## Resource management. ## ## -------------------- ## # This section contains definitions for functions that each ensure a # particular resource (a file, or a non-empty configuration variable for # example) is available, and if appropriate to extract default values # from pertinent package files. Call them using their associated # 'require_*' variable to ensure that they are executed, at most, once. # # It's entirely deliberate that calling these functions can set # variables that don't obey the namespace limitations obeyed by the rest # of this file, in order that that they be as useful as possible to # callers. # require_term_colors # ------------------- # Allow display of bold text on terminals that support it. require_term_colors=func_require_term_colors func_require_term_colors () { $debug_cmd test -t 1 && { # COLORTERM and USE_ANSI_COLORS environment variables take # precedence, because most terminfo databases neglect to describe # whether color sequences are supported. test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"} if test 1 = "$USE_ANSI_COLORS"; then # Standard ANSI escape sequences tc_reset='' tc_bold=''; tc_standout='' tc_red=''; tc_green='' tc_blue=''; tc_cyan='' else # Otherwise trust the terminfo database after all. test -n "`tput sgr0 2>/dev/null`" && { tc_reset=`tput sgr0` test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold` tc_standout=$tc_bold test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso` test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1` test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2` test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4` test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5` } fi } require_term_colors=: } ## ----------------- ## ## Function library. ## ## ----------------- ## # This section contains a variety of useful functions to call in your # scripts. Take note of the portable wrappers for features provided by # some modern shells, which will fall back to slower equivalents on # less featureful shells. # func_append VAR VALUE # --------------------- # Append VALUE onto the existing contents of VAR. # _G_HAVE_PLUSEQ_OP # Can be empty, in which case the shell is probed, "yes" if += is # useable or anything else if it does not work. if test -z "$_G_HAVE_PLUSEQ_OP" && \ __PLUSEQ_TEST="a" && \ __PLUSEQ_TEST+=" b" 2>/dev/null && \ test "a b" = "$__PLUSEQ_TEST"; then _G_HAVE_PLUSEQ_OP=yes fi if test yes = "$_G_HAVE_PLUSEQ_OP" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_append () { $debug_cmd eval "$1+=\$2" }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_append () { $debug_cmd eval "$1=\$$1\$2" } fi # func_append_quoted VAR VALUE # ---------------------------- # Quote VALUE and append to the end of shell variable VAR, separated # by a space. if test yes = "$_G_HAVE_PLUSEQ_OP"; then eval 'func_append_quoted () { $debug_cmd func_quote_arg pretty "$2" eval "$1+=\\ \$func_quote_arg_result" }' else func_append_quoted () { $debug_cmd func_quote_arg pretty "$2" eval "$1=\$$1\\ \$func_quote_arg_result" } fi # func_append_uniq VAR VALUE # -------------------------- # Append unique VALUE onto the existing contents of VAR, assuming # entries are delimited by the first character of VALUE. For example: # # func_append_uniq options " --another-option option-argument" # # will only append to $options if " --another-option option-argument " # is not already present somewhere in $options already (note spaces at # each end implied by leading space in second argument). func_append_uniq () { $debug_cmd eval _G_current_value='`$ECHO $'$1'`' _G_delim=`expr "$2" : '\(.\)'` case $_G_delim$_G_current_value$_G_delim in *"$2$_G_delim"*) ;; *) func_append "$@" ;; esac } # func_arith TERM... # ------------------ # Set func_arith_result to the result of evaluating TERMs. test -z "$_G_HAVE_ARITH_OP" \ && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \ && _G_HAVE_ARITH_OP=yes if test yes = "$_G_HAVE_ARITH_OP"; then eval 'func_arith () { $debug_cmd func_arith_result=$(( $* )) }' else func_arith () { $debug_cmd func_arith_result=`expr "$@"` } fi # func_basename FILE # ------------------ # Set func_basename_result to FILE with everything up to and including # the last / stripped. if test yes = "$_G_HAVE_XSI_OPS"; then # If this shell supports suffix pattern removal, then use it to avoid # forking. Hide the definitions single quotes in case the shell chokes # on unsupported syntax... _b='func_basename_result=${1##*/}' _d='case $1 in */*) func_dirname_result=${1%/*}$2 ;; * ) func_dirname_result=$3 ;; esac' else # ...otherwise fall back to using sed. _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`' _d='func_dirname_result=`$ECHO "$1" |$SED "$sed_dirname"` if test "X$func_dirname_result" = "X$1"; then func_dirname_result=$3 else func_append func_dirname_result "$2" fi' fi eval 'func_basename () { $debug_cmd '"$_b"' }' # func_dirname FILE APPEND NONDIR_REPLACEMENT # ------------------------------------------- # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. eval 'func_dirname () { $debug_cmd '"$_d"' }' # func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT # -------------------------------------------------------- # Perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value retuned in "$func_basename_result" # For efficiency, we do not delegate to the functions above but instead # duplicate the functionality here. eval 'func_dirname_and_basename () { $debug_cmd '"$_b"' '"$_d"' }' # func_echo ARG... # ---------------- # Echo program name prefixed message. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname: $_G_line" done IFS=$func_echo_IFS } # func_echo_all ARG... # -------------------- # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } # func_echo_infix_1 INFIX ARG... # ------------------------------ # Echo program name, followed by INFIX on the first line, with any # additional lines not showing INFIX. func_echo_infix_1 () { $debug_cmd $require_term_colors _G_infix=$1; shift _G_indent=$_G_infix _G_prefix="$progname: $_G_infix: " _G_message=$* # Strip color escape sequences before counting printable length for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan" do test -n "$_G_tc" && { _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"` _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"` } done _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`" " ## exclude from sc_prohibit_nested_quotes func_echo_infix_1_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_infix_1_IFS $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2 _G_prefix=$_G_indent done IFS=$func_echo_infix_1_IFS } # func_error ARG... # ----------------- # Echo program name prefixed message to standard error. func_error () { $debug_cmd $require_term_colors func_echo_infix_1 " $tc_standout${tc_red}error$tc_reset" "$*" >&2 } # func_fatal_error ARG... # ----------------------- # Echo program name prefixed message to standard error, and exit. func_fatal_error () { $debug_cmd func_error "$*" exit $EXIT_FAILURE } # func_grep EXPRESSION FILENAME # ----------------------------- # Check whether EXPRESSION matches any line of FILENAME, without output. func_grep () { $debug_cmd $GREP "$1" "$2" >/dev/null 2>&1 } # func_len STRING # --------------- # Set func_len_result to the length of STRING. STRING may not # start with a hyphen. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_len () { $debug_cmd func_len_result=${#1} }' else func_len () { $debug_cmd func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` } fi # func_mkdir_p DIRECTORY-PATH # --------------------------- # Make sure the entire path to DIRECTORY-PATH is available. func_mkdir_p () { $debug_cmd _G_directory_path=$1 _G_dir_list= if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then # Protect directory names starting with '-' case $_G_directory_path in -*) _G_directory_path=./$_G_directory_path ;; esac # While some portion of DIR does not yet exist... while test ! -d "$_G_directory_path"; do # ...make a list in topmost first order. Use a colon delimited # list incase some portion of path contains whitespace. _G_dir_list=$_G_directory_path:$_G_dir_list # If the last portion added has no slash in it, the list is done case $_G_directory_path in */*) ;; *) break ;; esac # ...otherwise throw away the child directory and loop _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"` done _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'` func_mkdir_p_IFS=$IFS; IFS=: for _G_dir in $_G_dir_list; do IFS=$func_mkdir_p_IFS # mkdir can fail with a 'File exist' error if two processes # try to create one of the directories concurrently. Don't # stop in that case! $MKDIR "$_G_dir" 2>/dev/null || : done IFS=$func_mkdir_p_IFS # Bail out if we (or some other process) failed to create a directory. test -d "$_G_directory_path" || \ func_fatal_error "Failed to create '$1'" fi } # func_mktempdir [BASENAME] # ------------------------- # Make a temporary directory that won't clash with other running # libtool processes, and avoids race conditions if possible. If # given, BASENAME is the basename for that directory. func_mktempdir () { $debug_cmd _G_template=${TMPDIR-/tmp}/${1-$progname} if test : = "$opt_dry_run"; then # Return a directory name, but don't create it in dry-run mode _G_tmpdir=$_G_template-$$ else # If mktemp works, use that first and foremost _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null` if test ! -d "$_G_tmpdir"; then # Failing that, at least try and use $RANDOM to avoid a race _G_tmpdir=$_G_template-${RANDOM-0}$$ func_mktempdir_umask=`umask` umask 0077 $MKDIR "$_G_tmpdir" umask $func_mktempdir_umask fi # If we're not in dry-run mode, bomb out on failure test -d "$_G_tmpdir" || \ func_fatal_error "cannot create temporary directory '$_G_tmpdir'" fi $ECHO "$_G_tmpdir" } # func_normal_abspath PATH # ------------------------ # Remove doubled-up and trailing slashes, "." path components, # and cancel out any ".." path components in PATH after making # it an absolute path. func_normal_abspath () { $debug_cmd # These SED scripts presuppose an absolute path with a trailing slash. _G_pathcar='s|^/\([^/]*\).*$|\1|' _G_pathcdr='s|^/[^/]*||' _G_removedotparts=':dotsl s|/\./|/|g t dotsl s|/\.$|/|' _G_collapseslashes='s|/\{1,\}|/|g' _G_finalslash='s|/*$|/|' # Start from root dir and reassemble the path. func_normal_abspath_result= func_normal_abspath_tpath=$1 func_normal_abspath_altnamespace= case $func_normal_abspath_tpath in "") # Empty path, that just means $cwd. func_stripname '' '/' "`pwd`" func_normal_abspath_result=$func_stripname_result return ;; # The next three entries are used to spot a run of precisely # two leading slashes without using negated character classes; # we take advantage of case's first-match behaviour. ///*) # Unusual form of absolute path, do nothing. ;; //*) # Not necessarily an ordinary path; POSIX reserves leading '//' # and for example Cygwin uses it to access remote file shares # over CIFS/SMB, so we conserve a leading double slash if found. func_normal_abspath_altnamespace=/ ;; /*) # Absolute path, do nothing. ;; *) # Relative path, prepend $cwd. func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath ;; esac # Cancel out all the simple stuff to save iterations. We also want # the path to end with a slash for ease of parsing, so make sure # there is one (and only one) here. func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"` while :; do # Processed it all yet? if test / = "$func_normal_abspath_tpath"; then # If we ascended to the root using ".." the result may be empty now. if test -z "$func_normal_abspath_result"; then func_normal_abspath_result=/ fi break fi func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcar"` func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcdr"` # Figure out what to do with it case $func_normal_abspath_tcomponent in "") # Trailing empty path component, ignore it. ;; ..) # Parent dir; strip last assembled component from result. func_dirname "$func_normal_abspath_result" func_normal_abspath_result=$func_dirname_result ;; *) # Actual path component, append it. func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent" ;; esac done # Restore leading double-slash if one was found on entry. func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result } # func_notquiet ARG... # -------------------- # Echo program name prefixed message only when not in quiet mode. func_notquiet () { $debug_cmd $opt_quiet || func_echo ${1+"$@"} # A bug in bash halts the script if the last line of a function # fails when set -e is in force, so we need another command to # work around that: : } # func_relative_path SRCDIR DSTDIR # -------------------------------- # Set func_relative_path_result to the relative path from SRCDIR to DSTDIR. func_relative_path () { $debug_cmd func_relative_path_result= func_normal_abspath "$1" func_relative_path_tlibdir=$func_normal_abspath_result func_normal_abspath "$2" func_relative_path_tbindir=$func_normal_abspath_result # Ascend the tree starting from libdir while :; do # check if we have found a prefix of bindir case $func_relative_path_tbindir in $func_relative_path_tlibdir) # found an exact match func_relative_path_tcancelled= break ;; $func_relative_path_tlibdir*) # found a matching prefix func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" func_relative_path_tcancelled=$func_stripname_result if test -z "$func_relative_path_result"; then func_relative_path_result=. fi break ;; *) func_dirname $func_relative_path_tlibdir func_relative_path_tlibdir=$func_dirname_result if test -z "$func_relative_path_tlibdir"; then # Have to descend all the way to the root! func_relative_path_result=../$func_relative_path_result func_relative_path_tcancelled=$func_relative_path_tbindir break fi func_relative_path_result=../$func_relative_path_result ;; esac done # Now calculate path; take care to avoid doubling-up slashes. func_stripname '' '/' "$func_relative_path_result" func_relative_path_result=$func_stripname_result func_stripname '/' '/' "$func_relative_path_tcancelled" if test -n "$func_stripname_result"; then func_append func_relative_path_result "/$func_stripname_result" fi # Normalisation. If bindir is libdir, return '.' else relative path. if test -n "$func_relative_path_result"; then func_stripname './' '' "$func_relative_path_result" func_relative_path_result=$func_stripname_result fi test -n "$func_relative_path_result" || func_relative_path_result=. : } # func_quote_portable EVAL ARG # ---------------------------- # Internal function to portably implement func_quote_arg. Note that we still # keep attention to performance here so we as much as possible try to avoid # calling sed binary (so far O(N) complexity as long as func_append is O(1)). func_quote_portable () { $debug_cmd $require_check_ifs_backslash func_quote_portable_result=$2 # one-time-loop (easy break) while true do if $1; then func_quote_portable_result=`$ECHO "$2" | $SED \ -e "$sed_double_quote_subst" -e "$sed_double_backslash"` break fi # Quote for eval. case $func_quote_portable_result in *[\\\`\"\$]*) # Fallback to sed for $func_check_bs_ifs_broken=:, or when the string # contains the shell wildcard characters. case $check_ifs_backshlash_broken$func_quote_portable_result in :*|*[\[\*\?]*) func_quote_portable_result=`$ECHO "$func_quote_portable_result" \ | $SED "$sed_quote_subst"` break ;; esac func_quote_portable_old_IFS=$IFS for _G_char in '\' '`' '"' '$' do # STATE($1) PREV($2) SEPARATOR($3) set start "" "" func_quote_portable_result=dummy"$_G_char$func_quote_portable_result$_G_char"dummy IFS=$_G_char for _G_part in $func_quote_portable_result do case $1 in quote) func_append func_quote_portable_result "$3$2" set quote "$_G_part" "\\$_G_char" ;; start) set first "" "" func_quote_portable_result= ;; first) set quote "$_G_part" "" ;; esac done done IFS=$func_quote_portable_old_IFS ;; *) ;; esac break done func_quote_portable_unquoted_result=$func_quote_portable_result case $func_quote_portable_result in # double-quote args containing shell metacharacters to delay # word splitting, command substitution and variable expansion # for a subsequent eval. # many bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") func_quote_portable_result=\"$func_quote_portable_result\" ;; esac } # func_quotefast_eval ARG # ----------------------- # Quote one ARG (internal). This is equivalent to 'func_quote_arg eval ARG', # but optimized for speed. Result is stored in $func_quotefast_eval. if test xyes = `(x=; printf -v x %q yes; echo x"$x") 2>/dev/null`; then printf -v _GL_test_printf_tilde %q '~' if test '\~' = "$_GL_test_printf_tilde"; then func_quotefast_eval () { printf -v func_quotefast_eval_result %q "$1" } else # Broken older Bash implementations. Make those faster too if possible. func_quotefast_eval () { case $1 in '~'*) func_quote_portable false "$1" func_quotefast_eval_result=$func_quote_portable_result ;; *) printf -v func_quotefast_eval_result %q "$1" ;; esac } fi else func_quotefast_eval () { func_quote_portable false "$1" func_quotefast_eval_result=$func_quote_portable_result } fi # func_quote_arg MODEs ARG # ------------------------ # Quote one ARG to be evaled later. MODEs argument may contain zero or more # specifiers listed below separated by ',' character. This function returns two # values: # i) func_quote_arg_result # double-quoted (when needed), suitable for a subsequent eval # ii) func_quote_arg_unquoted_result # has all characters that are still active within double # quotes backslashified. Available only if 'unquoted' is specified. # # Available modes: # ---------------- # 'eval' (default) # - escape shell special characters # 'expand' # - the same as 'eval'; but do not quote variable references # 'pretty' # - request aesthetic output, i.e. '"a b"' instead of 'a\ b'. This might # be used later in func_quote to get output like: 'echo "a b"' instead # of 'echo a\ b'. This is slower than default on some shells. # 'unquoted' # - produce also $func_quote_arg_unquoted_result which does not contain # wrapping double-quotes. # # Examples for 'func_quote_arg pretty,unquoted string': # # string | *_result | *_unquoted_result # ------------+-----------------------+------------------- # " | \" | \" # a b | "a b" | a b # "a b" | "\"a b\"" | \"a b\" # * | "*" | * # z="${x-$y}" | "z=\"\${x-\$y}\"" | z=\"\${x-\$y}\" # # Examples for 'func_quote_arg pretty,unquoted,expand string': # # string | *_result | *_unquoted_result # --------------+---------------------+-------------------- # z="${x-$y}" | "z=\"${x-$y}\"" | z=\"${x-$y}\" func_quote_arg () { _G_quote_expand=false case ,$1, in *,expand,*) _G_quote_expand=: ;; esac case ,$1, in *,pretty,*|*,expand,*|*,unquoted,*) func_quote_portable $_G_quote_expand "$2" func_quote_arg_result=$func_quote_portable_result func_quote_arg_unquoted_result=$func_quote_portable_unquoted_result ;; *) # Faster quote-for-eval for some shells. func_quotefast_eval "$2" func_quote_arg_result=$func_quotefast_eval_result ;; esac } # func_quote MODEs ARGs... # ------------------------ # Quote all ARGs to be evaled later and join them into single command. See # func_quote_arg's description for more info. func_quote () { $debug_cmd _G_func_quote_mode=$1 ; shift func_quote_result= while test 0 -lt $#; do func_quote_arg "$_G_func_quote_mode" "$1" if test -n "$func_quote_result"; then func_append func_quote_result " $func_quote_arg_result" else func_append func_quote_result "$func_quote_arg_result" fi shift done } # func_stripname PREFIX SUFFIX NAME # --------------------------------- # strip PREFIX and SUFFIX from NAME, and store in func_stripname_result. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_stripname () { $debug_cmd # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are # positional parameters, so assign one to ordinary variable first. func_stripname_result=$3 func_stripname_result=${func_stripname_result#"$1"} func_stripname_result=${func_stripname_result%"$2"} }' else func_stripname () { $debug_cmd case $2 in .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;; *) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;; esac } fi # func_show_eval CMD [FAIL_EXP] # ----------------------------- # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. func_show_eval () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} func_quote_arg pretty,expand "$_G_cmd" eval "func_notquiet $func_quote_arg_result" $opt_dry_run || { eval "$_G_cmd" _G_status=$? if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_show_eval_locale CMD [FAIL_EXP] # ------------------------------------ # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. Use the saved locale for evaluation. func_show_eval_locale () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} $opt_quiet || { func_quote_arg expand,pretty "$_G_cmd" eval "func_echo $func_quote_arg_result" } $opt_dry_run || { eval "$_G_user_locale $_G_cmd" _G_status=$? eval "$_G_safe_locale" if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_tr_sh # ---------- # Turn $1 into a string suitable for a shell variable name. # Result is stored in $func_tr_sh_result. All characters # not in the set a-zA-Z0-9_ are replaced with '_'. Further, # if $1 begins with a digit, a '_' is prepended as well. func_tr_sh () { $debug_cmd case $1 in [0-9]* | *[!a-zA-Z0-9_]*) func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'` ;; * ) func_tr_sh_result=$1 ;; esac } # func_verbose ARG... # ------------------- # Echo program name prefixed message in verbose mode only. func_verbose () { $debug_cmd $opt_verbose && func_echo "$*" : } # func_warn_and_continue ARG... # ----------------------------- # Echo program name prefixed warning message to standard error. func_warn_and_continue () { $debug_cmd $require_term_colors func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2 } # func_warning CATEGORY ARG... # ---------------------------- # Echo program name prefixed warning message to standard error. Warning # messages can be filtered according to CATEGORY, where this function # elides messages where CATEGORY is not listed in the global variable # 'opt_warning_types'. func_warning () { $debug_cmd # CATEGORY must be in the warning_categories list! case " $warning_categories " in *" $1 "*) ;; *) func_internal_error "invalid warning category '$1'" ;; esac _G_category=$1 shift case " $opt_warning_types " in *" $_G_category "*) $warning_func ${1+"$@"} ;; esac } # func_sort_ver VER1 VER2 # ----------------------- # 'sort -V' is not generally available. # Note this deviates from the version comparison in automake # in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a # but this should suffice as we won't be specifying old # version formats or redundant trailing .0 in bootstrap.conf. # If we did want full compatibility then we should probably # use m4_version_compare from autoconf. func_sort_ver () { $debug_cmd printf '%s\n%s\n' "$1" "$2" \ | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n } # func_lt_ver PREV CURR # --------------------- # Return true if PREV and CURR are in the correct order according to # func_sort_ver, otherwise false. Use it like this: # # func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..." func_lt_ver () { $debug_cmd test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q` } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: #! /bin/sh # A portable, pluggable option parser for Bourne shell. # Written by Gary V. Vaughan, 2010 # This is free software. There is NO warranty; not even for # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Copyright (C) 2010-2019, 2021 Bootstrap Authors # # This file is dual licensed under the terms of the MIT license # , and GPL version 2 or later # . You must apply one of # these licenses when using or redistributing this software or any of # the files within it. See the URLs above, or the file `LICENSE` # included in the Bootstrap distribution for the full license texts. # Please report bugs or propose patches to: # # Set a version string for this script. scriptversion=2019-02-19.15; # UTC ## ------ ## ## Usage. ## ## ------ ## # This file is a library for parsing options in your shell scripts along # with assorted other useful supporting features that you can make use # of too. # # For the simplest scripts you might need only: # # #!/bin/sh # . relative/path/to/funclib.sh # . relative/path/to/options-parser # scriptversion=1.0 # func_options ${1+"$@"} # eval set dummy "$func_options_result"; shift # ...rest of your script... # # In order for the '--version' option to work, you will need to have a # suitably formatted comment like the one at the top of this file # starting with '# Written by ' and ending with '# Copyright'. # # For '-h' and '--help' to work, you will also need a one line # description of your script's purpose in a comment directly above the # '# Written by ' line, like the one at the top of this file. # # The default options also support '--debug', which will turn on shell # execution tracing (see the comment above debug_cmd below for another # use), and '--verbose' and the func_verbose function to allow your script # to display verbose messages only when your user has specified # '--verbose'. # # After sourcing this file, you can plug in processing for additional # options by amending the variables from the 'Configuration' section # below, and following the instructions in the 'Option parsing' # section further down. ## -------------- ## ## Configuration. ## ## -------------- ## # You should override these variables in your script after sourcing this # file so that they reflect the customisations you have added to the # option parser. # The usage line for option parsing errors and the start of '-h' and # '--help' output messages. You can embed shell variables for delayed # expansion at the time the message is displayed, but you will need to # quote other shell meta-characters carefully to prevent them being # expanded when the contents are evaled. usage='$progpath [OPTION]...' # Short help message in response to '-h' and '--help'. Add to this or # override it after sourcing this library to reflect the full set of # options your script accepts. usage_message="\ --debug enable verbose shell tracing -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -v, --verbose verbosely report processing --version print version information and exit -h, --help print short or long help message and exit " # Additional text appended to 'usage_message' in response to '--help'. long_help_message=" Warning categories include: 'all' show all warnings 'none' turn off all the warnings 'error' warnings are treated as fatal errors" # Help message printed before fatal option parsing errors. fatal_help="Try '\$progname --help' for more information." ## ------------------------- ## ## Hook function management. ## ## ------------------------- ## # This section contains functions for adding, removing, and running hooks # in the main code. A hook is just a list of function names that can be # run in order later on. # func_hookable FUNC_NAME # ----------------------- # Declare that FUNC_NAME will run hooks added with # 'func_add_hook FUNC_NAME ...'. func_hookable () { $debug_cmd func_append hookable_fns " $1" } # func_add_hook FUNC_NAME HOOK_FUNC # --------------------------------- # Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must # first have been declared "hookable" by a call to 'func_hookable'. func_add_hook () { $debug_cmd case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not accept hook functions." ;; esac eval func_append ${1}_hooks '" $2"' } # func_remove_hook FUNC_NAME HOOK_FUNC # ------------------------------------ # Remove HOOK_FUNC from the list of hook functions to be called by # FUNC_NAME. func_remove_hook () { $debug_cmd eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`' } # func_propagate_result FUNC_NAME_A FUNC_NAME_B # --------------------------------------------- # If the *_result variable of FUNC_NAME_A _is set_, assign its value to # *_result variable of FUNC_NAME_B. func_propagate_result () { $debug_cmd func_propagate_result_result=: if eval "test \"\${${1}_result+set}\" = set" then eval "${2}_result=\$${1}_result" else func_propagate_result_result=false fi } # func_run_hooks FUNC_NAME [ARG]... # --------------------------------- # Run all hook functions registered to FUNC_NAME. # It's assumed that the list of hook functions contains nothing more # than a whitespace-delimited list of legal shell function names, and # no effort is wasted trying to catch shell meta-characters or preserve # whitespace. func_run_hooks () { $debug_cmd _G_rc_run_hooks=false case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not support hook functions." ;; esac eval _G_hook_fns=\$$1_hooks; shift for _G_hook in $_G_hook_fns; do func_unset "${_G_hook}_result" eval $_G_hook '${1+"$@"}' func_propagate_result $_G_hook func_run_hooks if $func_propagate_result_result; then eval set dummy "$func_run_hooks_result"; shift fi done } ## --------------- ## ## Option parsing. ## ## --------------- ## # In order to add your own option parsing hooks, you must accept the # full positional parameter list from your hook function. You may remove # or edit any options that you action, and then pass back the remaining # unprocessed options in '_result', escaped # suitably for 'eval'. # # The '_result' variable is automatically unset # before your hook gets called; for best performance, only set the # *_result variable when necessary (i.e. don't call the 'func_quote' # function unnecessarily because it can be an expensive operation on some # machines). # # Like this: # # my_options_prep () # { # $debug_cmd # # # Extend the existing usage message. # usage_message=$usage_message' # -s, --silent don'\''t print informational messages # ' # # No change in '$@' (ignored completely by this hook). Leave # # my_options_prep_result variable intact. # } # func_add_hook func_options_prep my_options_prep # # # my_silent_option () # { # $debug_cmd # # args_changed=false # # # Note that, for efficiency, we parse as many options as we can # # recognise in a loop before passing the remainder back to the # # caller on the first unrecognised argument we encounter. # while test $# -gt 0; do # opt=$1; shift # case $opt in # --silent|-s) opt_silent=: # args_changed=: # ;; # # Separate non-argument short options: # -s*) func_split_short_opt "$_G_opt" # set dummy "$func_split_short_opt_name" \ # "-$func_split_short_opt_arg" ${1+"$@"} # shift # args_changed=: # ;; # *) # Make sure the first unrecognised option "$_G_opt" # # is added back to "$@" in case we need it later, # # if $args_changed was set to 'true'. # set dummy "$_G_opt" ${1+"$@"}; shift; break ;; # esac # done # # # Only call 'func_quote' here if we processed at least one argument. # if $args_changed; then # func_quote eval ${1+"$@"} # my_silent_option_result=$func_quote_result # fi # } # func_add_hook func_parse_options my_silent_option # # # my_option_validation () # { # $debug_cmd # # $opt_silent && $opt_verbose && func_fatal_help "\ # '--silent' and '--verbose' options are mutually exclusive." # } # func_add_hook func_validate_options my_option_validation # # You'll also need to manually amend $usage_message to reflect the extra # options you parse. It's preferable to append if you can, so that # multiple option parsing hooks can be added safely. # func_options_finish [ARG]... # ---------------------------- # Finishing the option parse loop (call 'func_options' hooks ATM). func_options_finish () { $debug_cmd func_run_hooks func_options ${1+"$@"} func_propagate_result func_run_hooks func_options_finish } # func_options [ARG]... # --------------------- # All the functions called inside func_options are hookable. See the # individual implementations for details. func_hookable func_options func_options () { $debug_cmd _G_options_quoted=false for my_func in options_prep parse_options validate_options options_finish do func_unset func_${my_func}_result func_unset func_run_hooks_result eval func_$my_func '${1+"$@"}' func_propagate_result func_$my_func func_options if $func_propagate_result_result; then eval set dummy "$func_options_result"; shift _G_options_quoted=: fi done $_G_options_quoted || { # As we (func_options) are top-level options-parser function and # nobody quoted "$@" for us yet, we need to do it explicitly for # caller. func_quote eval ${1+"$@"} func_options_result=$func_quote_result } } # func_options_prep [ARG]... # -------------------------- # All initialisations required before starting the option parse loop. # Note that when calling hook functions, we pass through the list of # positional parameters. If a hook function modifies that list, and # needs to propagate that back to rest of this script, then the complete # modified list must be put in 'func_run_hooks_result' before returning. func_hookable func_options_prep func_options_prep () { $debug_cmd # Option defaults: opt_verbose=false opt_warning_types= func_run_hooks func_options_prep ${1+"$@"} func_propagate_result func_run_hooks func_options_prep } # func_parse_options [ARG]... # --------------------------- # The main option parsing loop. func_hookable func_parse_options func_parse_options () { $debug_cmd _G_parse_options_requote=false # this just eases exit handling while test $# -gt 0; do # Defer to hook functions for initial option parsing, so they # get priority in the event of reusing an option name. func_run_hooks func_parse_options ${1+"$@"} func_propagate_result func_run_hooks func_parse_options if $func_propagate_result_result; then eval set dummy "$func_parse_options_result"; shift # Even though we may have changed "$@", we passed the "$@" array # down into the hook and it quoted it for us (because we are in # this if-branch). No need to quote it again. _G_parse_options_requote=false fi # Break out of the loop if we already parsed every option. test $# -gt 0 || break # We expect that one of the options parsed in this function matches # and thus we remove _G_opt from "$@" and need to re-quote. _G_match_parse_options=: _G_opt=$1 shift case $_G_opt in --debug|-x) debug_cmd='set -x' func_echo "enabling shell trace mode" >&2 $debug_cmd ;; --no-warnings|--no-warning|--no-warn) set dummy --warnings none ${1+"$@"} shift ;; --warnings|--warning|-W) if test $# = 0 && func_missing_arg $_G_opt; then _G_parse_options_requote=: break fi case " $warning_categories $1" in *" $1 "*) # trailing space prevents matching last $1 above func_append_uniq opt_warning_types " $1" ;; *all) opt_warning_types=$warning_categories ;; *none) opt_warning_types=none warning_func=: ;; *error) opt_warning_types=$warning_categories warning_func=func_fatal_error ;; *) func_fatal_error \ "unsupported warning category: '$1'" ;; esac shift ;; --verbose|-v) opt_verbose=: ;; --version) func_version ;; -\?|-h) func_usage ;; --help) func_help ;; # Separate optargs to long options (plugins may need this): --*=*) func_split_equals "$_G_opt" set dummy "$func_split_equals_lhs" \ "$func_split_equals_rhs" ${1+"$@"} shift ;; # Separate optargs to short options: -W*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "$func_split_short_opt_arg" ${1+"$@"} shift ;; # Separate non-argument short options: -\?*|-h*|-v*|-x*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "-$func_split_short_opt_arg" ${1+"$@"} shift ;; --) _G_parse_options_requote=: ; break ;; -*) func_fatal_help "unrecognised option: '$_G_opt'" ;; *) set dummy "$_G_opt" ${1+"$@"}; shift _G_match_parse_options=false break ;; esac if $_G_match_parse_options; then _G_parse_options_requote=: fi done if $_G_parse_options_requote; then # save modified positional parameters for caller func_quote eval ${1+"$@"} func_parse_options_result=$func_quote_result fi } # func_validate_options [ARG]... # ------------------------------ # Perform any sanity checks on option settings and/or unconsumed # arguments. func_hookable func_validate_options func_validate_options () { $debug_cmd # Display all warnings if -W was not given. test -n "$opt_warning_types" || opt_warning_types=" $warning_categories" func_run_hooks func_validate_options ${1+"$@"} func_propagate_result func_run_hooks func_validate_options # Bail if the options were screwed! $exit_cmd $EXIT_FAILURE } ## ----------------- ## ## Helper functions. ## ## ----------------- ## # This section contains the helper functions used by the rest of the # hookable option parser framework in ascii-betical order. # func_fatal_help ARG... # ---------------------- # Echo program name prefixed message to standard error, followed by # a help hint, and exit. func_fatal_help () { $debug_cmd eval \$ECHO \""Usage: $usage"\" eval \$ECHO \""$fatal_help"\" func_error ${1+"$@"} exit $EXIT_FAILURE } # func_help # --------- # Echo long help message to standard output and exit. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message" exit 0 } # func_missing_arg ARGNAME # ------------------------ # Echo program name prefixed message to standard error and set global # exit_cmd. func_missing_arg () { $debug_cmd func_error "Missing argument for '$1'." exit_cmd=exit } # func_split_equals STRING # ------------------------ # Set func_split_equals_lhs and func_split_equals_rhs shell variables # after splitting STRING at the '=' sign. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_equals () { $debug_cmd func_split_equals_lhs=${1%%=*} func_split_equals_rhs=${1#*=} if test "x$func_split_equals_lhs" = "x$1"; then func_split_equals_rhs= fi }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_equals () { $debug_cmd func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'` func_split_equals_rhs= test "x$func_split_equals_lhs=" = "x$1" \ || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'` } fi #func_split_equals # func_split_short_opt SHORTOPT # ----------------------------- # Set func_split_short_opt_name and func_split_short_opt_arg shell # variables after splitting SHORTOPT after the 2nd character. if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_short_opt () { $debug_cmd func_split_short_opt_arg=${1#??} func_split_short_opt_name=${1%"$func_split_short_opt_arg"} }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_short_opt () { $debug_cmd func_split_short_opt_name=`expr "x$1" : 'x\(-.\)'` func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'` } fi #func_split_short_opt # func_usage # ---------- # Echo short help message to standard output and exit. func_usage () { $debug_cmd func_usage_message $ECHO "Run '$progname --help |${PAGER-more}' for full usage" exit 0 } # func_usage_message # ------------------ # Echo short help message to standard output. func_usage_message () { $debug_cmd eval \$ECHO \""Usage: $usage"\" echo $SED -n 's|^# || /^Written by/{ x;p;x } h /^Written by/q' < "$progpath" echo eval \$ECHO \""$usage_message"\" } # func_version # ------------ # Echo version message to standard output and exit. # The version message is extracted from the calling file's header # comments, with leading '# ' stripped: # 1. First display the progname and version # 2. Followed by the header comment line matching /^# Written by / # 3. Then a blank line followed by the first following line matching # /^# Copyright / # 4. Immediately followed by any lines between the previous matches, # except lines preceding the intervening completely blank line. # For example, see the header comments of this file. func_version () { $debug_cmd printf '%s\n' "$progname $scriptversion" $SED -n ' /^# Written by /!b s|^# ||; p; n :fwd2blnk /./ { n b fwd2blnk } p; n :holdwrnt s|^# || s|^# *$|| /^Copyright /!{ /./H n b holdwrnt } s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2| G s|\(\n\)\n*|\1|g p; q' < "$progpath" exit $? } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "30/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: # Set a version string. scriptversion='(GNU libtool) 2.4.7' # func_echo ARG... # ---------------- # Libtool also displays the current mode in messages, so override # funclib.sh func_echo with this custom definition. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname${opt_mode+: $opt_mode}: $_G_line" done IFS=$func_echo_IFS } # func_warning ARG... # ------------------- # Libtool warnings are not categorized, so override funclib.sh # func_warning with this simpler definition. func_warning () { $debug_cmd $warning_func ${1+"$@"} } ## ---------------- ## ## Options parsing. ## ## ---------------- ## # Hook in the functions to make sure our own options are parsed during # the option parsing loop. usage='$progpath [OPTION]... [MODE-ARG]...' # Short help message in response to '-h'. usage_message="Options: --config show all configuration variables --debug enable verbose shell tracing -n, --dry-run display commands without modifying any files --features display basic configuration information and exit --mode=MODE use operation mode MODE --no-warnings equivalent to '-Wnone' --preserve-dup-deps don't remove duplicate dependency libraries --quiet, --silent don't print informational messages --tag=TAG use configuration variables from tag TAG -v, --verbose print more informational messages than default --version print version information -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -h, --help, --help-all print short, long, or detailed help message " # Additional text appended to 'usage_message' in response to '--help'. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message MODE must be one of the following: clean remove files from the build directory compile compile a source file into a libtool object execute automatically set library path, then run a program finish complete the installation of libtool libraries install install libraries or executables link create a library or an executable uninstall remove libraries from an installed directory MODE-ARGS vary depending on the MODE. When passed as first option, '--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that. Try '$progname --help --mode=MODE' for a more detailed description of MODE. When reporting a bug, please describe a test case to reproduce it and include the following information: host-triplet: $host shell: $SHELL compiler: $LTCC compiler flags: $LTCFLAGS linker: $LD (gnu? $with_gnu_ld) version: $progname $scriptversion Debian-2.4.7-7build1 automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q` autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q` Report bugs to . GNU libtool home page: . General help using GNU software: ." exit 0 } # func_lo2o OBJECT-NAME # --------------------- # Transform OBJECT-NAME from a '.lo' suffix to the platform specific # object suffix. lo2o=s/\\.lo\$/.$objext/ o2lo=s/\\.$objext\$/.lo/ if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_lo2o () { case $1 in *.lo) func_lo2o_result=${1%.lo}.$objext ;; * ) func_lo2o_result=$1 ;; esac }' # func_xform LIBOBJ-OR-SOURCE # --------------------------- # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise) # suffix to a '.lo' libtool-object suffix. eval 'func_xform () { func_xform_result=${1%.*}.lo }' else # ...otherwise fall back to using sed. func_lo2o () { func_lo2o_result=`$ECHO "$1" | $SED "$lo2o"` } func_xform () { func_xform_result=`$ECHO "$1" | $SED 's|\.[^.]*$|.lo|'` } fi # func_fatal_configuration ARG... # ------------------------------- # Echo program name prefixed message to standard error, followed by # a configuration failure hint, and exit. func_fatal_configuration () { func_fatal_error ${1+"$@"} \ "See the $PACKAGE documentation for more information." \ "Fatal configuration error." } # func_config # ----------- # Display the configuration for all the tags in this script. func_config () { re_begincf='^# ### BEGIN LIBTOOL' re_endcf='^# ### END LIBTOOL' # Default configuration. $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" # Now print the configurations for the tags. for tagname in $taglist; do $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" done exit $? } # func_features # ------------- # Display the features supported by this script. func_features () { echo "host: $host" if test yes = "$build_libtool_libs"; then echo "enable shared libraries" else echo "disable shared libraries" fi if test yes = "$build_old_libs"; then echo "enable static libraries" else echo "disable static libraries" fi exit $? } # func_enable_tag TAGNAME # ----------------------- # Verify that TAGNAME is valid, and either flag an error and exit, or # enable the TAGNAME tag. We also add TAGNAME to the global $taglist # variable here. func_enable_tag () { # Global variable: tagname=$1 re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" sed_extractcf=/$re_begincf/,/$re_endcf/p # Validate tagname. case $tagname in *[!-_A-Za-z0-9,/]*) func_fatal_error "invalid tag name: $tagname" ;; esac # Don't test for the "default" C tag, as we know it's # there but not specially marked. case $tagname in CC) ;; *) if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then taglist="$taglist $tagname" # Evaluate the configuration. Be careful to quote the path # and the sed script, to avoid splitting on whitespace, but # also don't use non-portable quotes within backquotes within # quotes we have to do it in 2 steps: extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` eval "$extractedcf" else func_error "ignoring unknown tag $tagname" fi ;; esac } # func_check_version_match # ------------------------ # Ensure that we are using m4 macros, and libtool script from the same # release of libtool. func_check_version_match () { if test "$package_revision" != "$macro_revision"; then if test "$VERSION" != "$macro_version"; then if test -z "$macro_version"; then cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from an older release. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from $PACKAGE $macro_version. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF fi else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, $progname: but the definition of this LT_INIT comes from revision $macro_revision. $progname: You should recreate aclocal.m4 with macros from revision $package_revision $progname: of $PACKAGE $VERSION and run autoconf again. _LT_EOF fi exit $EXIT_MISMATCH fi } # libtool_options_prep [ARG]... # ----------------------------- # Preparation for options parsed by libtool. libtool_options_prep () { $debug_mode # Option defaults: opt_config=false opt_dlopen= opt_dry_run=false opt_help=false opt_mode= opt_preserve_dup_deps=false opt_quiet=false nonopt= preserve_args= _G_rc_lt_options_prep=: _G_rc_lt_options_prep=: # Shorthand for --mode=foo, only valid as the first argument case $1 in clean|clea|cle|cl) shift; set dummy --mode clean ${1+"$@"}; shift ;; compile|compil|compi|comp|com|co|c) shift; set dummy --mode compile ${1+"$@"}; shift ;; execute|execut|execu|exec|exe|ex|e) shift; set dummy --mode execute ${1+"$@"}; shift ;; finish|finis|fini|fin|fi|f) shift; set dummy --mode finish ${1+"$@"}; shift ;; install|instal|insta|inst|ins|in|i) shift; set dummy --mode install ${1+"$@"}; shift ;; link|lin|li|l) shift; set dummy --mode link ${1+"$@"}; shift ;; uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) shift; set dummy --mode uninstall ${1+"$@"}; shift ;; *) _G_rc_lt_options_prep=false ;; esac if $_G_rc_lt_options_prep; then # Pass back the list of options. func_quote eval ${1+"$@"} libtool_options_prep_result=$func_quote_result fi } func_add_hook func_options_prep libtool_options_prep # libtool_parse_options [ARG]... # --------------------------------- # Provide handling for libtool specific options. libtool_parse_options () { $debug_cmd _G_rc_lt_parse_options=false # Perform our own loop to consume as many options as possible in # each iteration. while test $# -gt 0; do _G_match_lt_parse_options=: _G_opt=$1 shift case $_G_opt in --dry-run|--dryrun|-n) opt_dry_run=: ;; --config) func_config ;; --dlopen|-dlopen) opt_dlopen="${opt_dlopen+$opt_dlopen }$1" shift ;; --preserve-dup-deps) opt_preserve_dup_deps=: ;; --features) func_features ;; --finish) set dummy --mode finish ${1+"$@"}; shift ;; --help) opt_help=: ;; --help-all) opt_help=': help-all' ;; --mode) test $# = 0 && func_missing_arg $_G_opt && break opt_mode=$1 case $1 in # Valid mode arguments: clean|compile|execute|finish|install|link|relink|uninstall) ;; # Catch anything else as an error *) func_error "invalid argument for $_G_opt" exit_cmd=exit break ;; esac shift ;; --no-silent|--no-quiet) opt_quiet=false func_append preserve_args " $_G_opt" ;; --no-warnings|--no-warning|--no-warn) opt_warning=false func_append preserve_args " $_G_opt" ;; --no-verbose) opt_verbose=false func_append preserve_args " $_G_opt" ;; --silent|--quiet) opt_quiet=: opt_verbose=false func_append preserve_args " $_G_opt" ;; --tag) test $# = 0 && func_missing_arg $_G_opt && break opt_tag=$1 func_append preserve_args " $_G_opt $1" func_enable_tag "$1" shift ;; --verbose|-v) opt_quiet=false opt_verbose=: func_append preserve_args " $_G_opt" ;; # An option not handled by this hook function: *) set dummy "$_G_opt" ${1+"$@"} ; shift _G_match_lt_parse_options=false break ;; esac $_G_match_lt_parse_options && _G_rc_lt_parse_options=: done if $_G_rc_lt_parse_options; then # save modified positional parameters for caller func_quote eval ${1+"$@"} libtool_parse_options_result=$func_quote_result fi } func_add_hook func_parse_options libtool_parse_options # libtool_validate_options [ARG]... # --------------------------------- # Perform any sanity checks on option settings and/or unconsumed # arguments. libtool_validate_options () { # save first non-option argument if test 0 -lt $#; then nonopt=$1 shift fi # preserve --debug test : = "$debug_cmd" || func_append preserve_args " --debug" case $host in # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452 # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788 *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*) # don't eliminate duplications in $postdeps and $predeps opt_duplicate_compiler_generated_deps=: ;; *) opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps ;; esac $opt_help || { # Sanity checks first: func_check_version_match test yes != "$build_libtool_libs" \ && test yes != "$build_old_libs" \ && func_fatal_configuration "not configured to build any kind of library" # Darwin sucks eval std_shrext=\"$shrext_cmds\" # Only execute mode is allowed to have -dlopen flags. if test -n "$opt_dlopen" && test execute != "$opt_mode"; then func_error "unrecognized option '-dlopen'" $ECHO "$help" 1>&2 exit $EXIT_FAILURE fi # Change the help message to a mode-specific one. generic_help=$help help="Try '$progname --help --mode=$opt_mode' for more information." } # Pass back the unparsed argument list func_quote eval ${1+"$@"} libtool_validate_options_result=$func_quote_result } func_add_hook func_validate_options libtool_validate_options # Process options as early as possible so that --help and --version # can return quickly. func_options ${1+"$@"} eval set dummy "$func_options_result"; shift ## ----------- ## ## Main. ## ## ----------- ## magic='%%%MAGIC variable%%%' magic_exe='%%%MAGIC EXE variable%%%' # Global variables. extracted_archives= extracted_serial=0 # If this variable is set in any of the actions, the command in it # will be execed at the end. This prevents here-documents from being # left over by shells. exec_cmd= # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } # func_generated_by_libtool # True iff stdin has been generated by Libtool. This function is only # a basic sanity check; it will hardly flush out determined imposters. func_generated_by_libtool_p () { $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 } # func_lalib_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_lalib_p () { test -f "$1" && $SED -e 4q "$1" 2>/dev/null | func_generated_by_libtool_p } # func_lalib_unsafe_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function implements the same check as func_lalib_p without # resorting to external programs. To this end, it redirects stdin and # closes it afterwards, without saving the original file descriptor. # As a safety measure, use it only where a negative result would be # fatal anyway. Works if 'file' does not exist. func_lalib_unsafe_p () { lalib_p=no if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then for lalib_p_l in 1 2 3 4 do read lalib_p_line case $lalib_p_line in \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; esac done exec 0<&5 5<&- fi test yes = "$lalib_p" } # func_ltwrapper_script_p file # True iff FILE is a libtool wrapper script # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_script_p () { test -f "$1" && $lt_truncate_bin < "$1" 2>/dev/null | func_generated_by_libtool_p } # func_ltwrapper_executable_p file # True iff FILE is a libtool wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_executable_p () { func_ltwrapper_exec_suffix= case $1 in *.exe) ;; *) func_ltwrapper_exec_suffix=.exe ;; esac $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 } # func_ltwrapper_scriptname file # Assumes file is an ltwrapper_executable # uses $file to determine the appropriate filename for a # temporary ltwrapper_script. func_ltwrapper_scriptname () { func_dirname_and_basename "$1" "" "." func_stripname '' '.exe' "$func_basename_result" func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper } # func_ltwrapper_p file # True iff FILE is a libtool wrapper script or wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_p () { func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" } # func_execute_cmds commands fail_cmd # Execute tilde-delimited COMMANDS. # If FAIL_CMD is given, eval that upon failure. # FAIL_CMD may read-access the current command in variable CMD! func_execute_cmds () { $debug_cmd save_ifs=$IFS; IFS='~' for cmd in $1; do IFS=$sp$nl eval cmd=\"$cmd\" IFS=$save_ifs func_show_eval "$cmd" "${2-:}" done IFS=$save_ifs } # func_source file # Source FILE, adding directory component if necessary. # Note that it is not necessary on cygwin/mingw to append a dot to # FILE even if both FILE and FILE.exe exist: automatic-append-.exe # behavior happens only for exec(3), not for open(2)! Also, sourcing # 'FILE.' does not work on cygwin managed mounts. func_source () { $debug_cmd case $1 in */* | *\\*) . "$1" ;; *) . "./$1" ;; esac } # func_resolve_sysroot PATH # Replace a leading = in PATH with a sysroot. Store the result into # func_resolve_sysroot_result func_resolve_sysroot () { func_resolve_sysroot_result=$1 case $func_resolve_sysroot_result in =*) func_stripname '=' '' "$func_resolve_sysroot_result" func_resolve_sysroot_result=$lt_sysroot$func_stripname_result ;; esac } # func_replace_sysroot PATH # If PATH begins with the sysroot, replace it with = and # store the result into func_replace_sysroot_result. func_replace_sysroot () { case $lt_sysroot:$1 in ?*:"$lt_sysroot"*) func_stripname "$lt_sysroot" '' "$1" func_replace_sysroot_result='='$func_stripname_result ;; *) # Including no sysroot. func_replace_sysroot_result=$1 ;; esac } # func_infer_tag arg # Infer tagged configuration to use if any are available and # if one wasn't chosen via the "--tag" command line option. # Only attempt this if the compiler in the base compile # command doesn't match the default compiler. # arg is usually of the form 'gcc ...' func_infer_tag () { $debug_cmd if test -n "$available_tags" && test -z "$tagname"; then CC_quoted= for arg in $CC; do func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case $@ in # Blanks in the command may have been stripped by the calling shell, # but not from the CC environment variable when configure was run. " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; # Blanks at the start of $base_compile will cause this to fail # if we don't check for them as well. *) for z in $available_tags; do if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then # Evaluate the configuration. eval "`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" CC_quoted= for arg in $CC; do # Double-quote args containing other shell metacharacters. func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case "$@ " in " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) # The compiler in the base compile command matches # the one in the tagged configuration. # Assume this is the tagged configuration we want. tagname=$z break ;; esac fi done # If $tagname still isn't set, then no tagged configuration # was found and let the user know that the "--tag" command # line option must be used. if test -z "$tagname"; then func_echo "unable to infer tagged configuration" func_fatal_error "specify a tag with '--tag'" # else # func_verbose "using $tagname tagged configuration" fi ;; esac fi } # func_write_libtool_object output_name pic_name nonpic_name # Create a libtool object file (analogous to a ".la" file), # but don't create it if we're doing a dry run. func_write_libtool_object () { write_libobj=$1 if test yes = "$build_libtool_libs"; then write_lobj=\'$2\' else write_lobj=none fi if test yes = "$build_old_libs"; then write_oldobj=\'$3\' else write_oldobj=none fi $opt_dry_run || { cat >${write_libobj}T </dev/null` if test "$?" -eq 0 && test -n "$func_convert_core_file_wine_to_w32_tmp"; then func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | $SED -e "$sed_naive_backslashify"` else func_convert_core_file_wine_to_w32_result= fi fi } # end: func_convert_core_file_wine_to_w32 # func_convert_core_path_wine_to_w32 ARG # Helper function used by path conversion functions when $build is *nix, and # $host is mingw, cygwin, or some other w32 environment. Relies on a correctly # configured wine environment available, with the winepath program in $build's # $PATH. Assumes ARG has no leading or trailing path separator characters. # # ARG is path to be converted from $build format to win32. # Result is available in $func_convert_core_path_wine_to_w32_result. # Unconvertible file (directory) names in ARG are skipped; if no directory names # are convertible, then the result may be empty. func_convert_core_path_wine_to_w32 () { $debug_cmd # unfortunately, winepath doesn't convert paths, only file names func_convert_core_path_wine_to_w32_result= if test -n "$1"; then oldIFS=$IFS IFS=: for func_convert_core_path_wine_to_w32_f in $1; do IFS=$oldIFS func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" if test -n "$func_convert_core_file_wine_to_w32_result"; then if test -z "$func_convert_core_path_wine_to_w32_result"; then func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result else func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" fi fi done IFS=$oldIFS fi } # end: func_convert_core_path_wine_to_w32 # func_cygpath ARGS... # Wrapper around calling the cygpath program via LT_CYGPATH. This is used when # when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) # $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or # (2), returns the Cygwin file name or path in func_cygpath_result (input # file name or path is assumed to be in w32 format, as previously converted # from $build's *nix or MSYS format). In case (3), returns the w32 file name # or path in func_cygpath_result (input file name or path is assumed to be in # Cygwin format). Returns an empty string on error. # # ARGS are passed to cygpath, with the last one being the file name or path to # be converted. # # Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH # environment variable; do not put it in $PATH. func_cygpath () { $debug_cmd if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` if test "$?" -ne 0; then # on failure, ensure result is empty func_cygpath_result= fi else func_cygpath_result= func_error "LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'" fi } #end: func_cygpath # func_convert_core_msys_to_w32 ARG # Convert file name or path ARG from MSYS format to w32 format. Return # result in func_convert_core_msys_to_w32_result. func_convert_core_msys_to_w32 () { $debug_cmd # awkward: cmd appends spaces to result func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | $SED -e 's/[ ]*$//' -e "$sed_naive_backslashify"` } #end: func_convert_core_msys_to_w32 # func_convert_file_check ARG1 ARG2 # Verify that ARG1 (a file name in $build format) was converted to $host # format in ARG2. Otherwise, emit an error message, but continue (resetting # func_to_host_file_result to ARG1). func_convert_file_check () { $debug_cmd if test -z "$2" && test -n "$1"; then func_error "Could not determine host file name corresponding to" func_error " '$1'" func_error "Continuing, but uninstalled executables may not work." # Fallback: func_to_host_file_result=$1 fi } # end func_convert_file_check # func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH # Verify that FROM_PATH (a path in $build format) was converted to $host # format in TO_PATH. Otherwise, emit an error message, but continue, resetting # func_to_host_file_result to a simplistic fallback value (see below). func_convert_path_check () { $debug_cmd if test -z "$4" && test -n "$3"; then func_error "Could not determine the host path corresponding to" func_error " '$3'" func_error "Continuing, but uninstalled executables may not work." # Fallback. This is a deliberately simplistic "conversion" and # should not be "improved". See libtool.info. if test "x$1" != "x$2"; then lt_replace_pathsep_chars="s|$1|$2|g" func_to_host_path_result=`echo "$3" | $SED -e "$lt_replace_pathsep_chars"` else func_to_host_path_result=$3 fi fi } # end func_convert_path_check # func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG # Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT # and appending REPL if ORIG matches BACKPAT. func_convert_path_front_back_pathsep () { $debug_cmd case $4 in $1 ) func_to_host_path_result=$3$func_to_host_path_result ;; esac case $4 in $2 ) func_append func_to_host_path_result "$3" ;; esac } # end func_convert_path_front_back_pathsep ################################################## # $build to $host FILE NAME CONVERSION FUNCTIONS # ################################################## # invoked via '$to_host_file_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # Result will be available in $func_to_host_file_result. # func_to_host_file ARG # Converts the file name ARG from $build format to $host format. Return result # in func_to_host_file_result. func_to_host_file () { $debug_cmd $to_host_file_cmd "$1" } # end func_to_host_file # func_to_tool_file ARG LAZY # converts the file name ARG from $build format to toolchain format. Return # result in func_to_tool_file_result. If the conversion in use is listed # in (the comma separated) LAZY, no conversion takes place. func_to_tool_file () { $debug_cmd case ,$2, in *,"$to_tool_file_cmd",*) func_to_tool_file_result=$1 ;; *) $to_tool_file_cmd "$1" func_to_tool_file_result=$func_to_host_file_result ;; esac } # end func_to_tool_file # func_convert_file_noop ARG # Copy ARG to func_to_host_file_result. func_convert_file_noop () { func_to_host_file_result=$1 } # end func_convert_file_noop # func_convert_file_msys_to_w32 ARG # Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_file_result. func_convert_file_msys_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_to_host_file_result=$func_convert_core_msys_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_w32 # func_convert_file_cygwin_to_w32 ARG # Convert file name ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_file_cygwin_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # because $build is cygwin, we call "the" cygpath in $PATH; no need to use # LT_CYGPATH in this case. func_to_host_file_result=`cygpath -m "$1"` fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_cygwin_to_w32 # func_convert_file_nix_to_w32 ARG # Convert file name ARG from *nix to w32 format. Requires a wine environment # and a working winepath. Returns result in func_to_host_file_result. func_convert_file_nix_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_file_wine_to_w32 "$1" func_to_host_file_result=$func_convert_core_file_wine_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_w32 # func_convert_file_msys_to_cygwin ARG # Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_file_msys_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_cygpath -u "$func_convert_core_msys_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_cygwin # func_convert_file_nix_to_cygwin ARG # Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed # in a wine environment, working winepath, and LT_CYGPATH set. Returns result # in func_to_host_file_result. func_convert_file_nix_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. func_convert_core_file_wine_to_w32 "$1" func_cygpath -u "$func_convert_core_file_wine_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_cygwin ############################################# # $build to $host PATH CONVERSION FUNCTIONS # ############################################# # invoked via '$to_host_path_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # The result will be available in $func_to_host_path_result. # # Path separators are also converted from $build format to $host format. If # ARG begins or ends with a path separator character, it is preserved (but # converted to $host format) on output. # # All path conversion functions are named using the following convention: # file name conversion function : func_convert_file_X_to_Y () # path conversion function : func_convert_path_X_to_Y () # where, for any given $build/$host combination the 'X_to_Y' value is the # same. If conversion functions are added for new $build/$host combinations, # the two new functions must follow this pattern, or func_init_to_host_path_cmd # will break. # func_init_to_host_path_cmd # Ensures that function "pointer" variable $to_host_path_cmd is set to the # appropriate value, based on the value of $to_host_file_cmd. to_host_path_cmd= func_init_to_host_path_cmd () { $debug_cmd if test -z "$to_host_path_cmd"; then func_stripname 'func_convert_file_' '' "$to_host_file_cmd" to_host_path_cmd=func_convert_path_$func_stripname_result fi } # func_to_host_path ARG # Converts the path ARG from $build format to $host format. Return result # in func_to_host_path_result. func_to_host_path () { $debug_cmd func_init_to_host_path_cmd $to_host_path_cmd "$1" } # end func_to_host_path # func_convert_path_noop ARG # Copy ARG to func_to_host_path_result. func_convert_path_noop () { func_to_host_path_result=$1 } # end func_convert_path_noop # func_convert_path_msys_to_w32 ARG # Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_path_result. func_convert_path_msys_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from ARG. MSYS # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; # and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_msys_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_msys_to_w32 # func_convert_path_cygwin_to_w32 ARG # Convert path ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_path_cygwin_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_cygwin_to_w32 # func_convert_path_nix_to_w32 ARG # Convert path ARG from *nix to w32 format. Requires a wine environment and # a working winepath. Returns result in func_to_host_file_result. func_convert_path_nix_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_path_wine_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_nix_to_w32 # func_convert_path_msys_to_cygwin ARG # Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_path_msys_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_msys_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_msys_to_cygwin # func_convert_path_nix_to_cygwin ARG # Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a # a wine environment, working winepath, and LT_CYGPATH set. Returns result in # func_to_host_file_result. func_convert_path_nix_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from # ARG. msys behavior is inconsistent here, cygpath turns them # into '.;' and ';.', and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_nix_to_cygwin # func_dll_def_p FILE # True iff FILE is a Windows DLL '.def' file. # Keep in sync with _LT_DLL_DEF_P in libtool.m4 func_dll_def_p () { $debug_cmd func_dll_def_p_tmp=`$SED -n \ -e 's/^[ ]*//' \ -e '/^\(;.*\)*$/d' \ -e 's/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p' \ -e q \ "$1"` test DEF = "$func_dll_def_p_tmp" } # func_mode_compile arg... func_mode_compile () { $debug_cmd # Get the compilation command and the source file. base_compile= srcfile=$nonopt # always keep a non-empty value in "srcfile" suppress_opt=yes suppress_output= arg_mode=normal libobj= later= pie_flag= for arg do case $arg_mode in arg ) # do not "continue". Instead, add this to base_compile lastarg=$arg arg_mode=normal ;; target ) libobj=$arg arg_mode=normal continue ;; normal ) # Accept any command-line options. case $arg in -o) test -n "$libobj" && \ func_fatal_error "you cannot specify '-o' more than once" arg_mode=target continue ;; -pie | -fpie | -fPIE) func_append pie_flag " $arg" continue ;; -shared | -static | -prefer-pic | -prefer-non-pic) func_append later " $arg" continue ;; -no-suppress) suppress_opt=no continue ;; -Xcompiler) arg_mode=arg # the next one goes into the "base_compile" arg list continue # The current "srcfile" will either be retained or ;; # replaced later. I would guess that would be a bug. -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result lastarg= save_ifs=$IFS; IFS=, for arg in $args; do IFS=$save_ifs func_append_quoted lastarg "$arg" done IFS=$save_ifs func_stripname ' ' '' "$lastarg" lastarg=$func_stripname_result # Add the arguments to base_compile. func_append base_compile " $lastarg" continue ;; *) # Accept the current argument as the source file. # The previous "srcfile" becomes the current argument. # lastarg=$srcfile srcfile=$arg ;; esac # case $arg ;; esac # case $arg_mode # Aesthetically quote the previous argument. func_append_quoted base_compile "$lastarg" done # for arg case $arg_mode in arg) func_fatal_error "you must specify an argument for -Xcompile" ;; target) func_fatal_error "you must specify a target with '-o'" ;; *) # Get the name of the library object. test -z "$libobj" && { func_basename "$srcfile" libobj=$func_basename_result } ;; esac # Recognize several different file suffixes. # If the user specifies -o file.o, it is replaced with file.lo case $libobj in *.[cCFSifmso] | \ *.ada | *.adb | *.ads | *.asm | \ *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) func_xform "$libobj" libobj=$func_xform_result ;; esac case $libobj in *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; *) func_fatal_error "cannot determine name of library object from '$libobj'" ;; esac func_infer_tag $base_compile for arg in $later; do case $arg in -shared) test yes = "$build_libtool_libs" \ || func_fatal_configuration "cannot build a shared library" build_old_libs=no continue ;; -static) build_libtool_libs=no build_old_libs=yes continue ;; -prefer-pic) pic_mode=yes continue ;; -prefer-non-pic) pic_mode=no continue ;; esac done func_quote_arg pretty "$libobj" test "X$libobj" != "X$func_quote_arg_result" \ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ && func_warning "libobj name '$libobj' may not contain shell special characters." func_dirname_and_basename "$obj" "/" "" objname=$func_basename_result xdir=$func_dirname_result lobj=$xdir$objdir/$objname test -z "$base_compile" && \ func_fatal_help "you must specify a compilation command" # Delete any leftover library objects. if test yes = "$build_old_libs"; then removelist="$obj $lobj $libobj ${libobj}T" else removelist="$lobj $libobj ${libobj}T" fi # On Cygwin there's no "real" PIC flag so we must build both object types case $host_os in cygwin* | mingw* | pw32* | os2* | cegcc*) pic_mode=default ;; esac if test no = "$pic_mode" && test pass_all != "$deplibs_check_method"; then # non-PIC code in shared libraries is not supported pic_mode=default fi # Calculate the filename of the output object if compiler does # not support -o with -c if test no = "$compiler_c_o"; then output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.$objext lockfile=$output_obj.lock else output_obj= need_locks=no lockfile= fi # Lock this critical section if it is needed # We use this script file to make the link, it avoids creating a new file if test yes = "$need_locks"; then until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done elif test warn = "$need_locks"; then if test -f "$lockfile"; then $ECHO "\ *** ERROR, $lockfile exists and contains: `cat $lockfile 2>/dev/null` This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi func_append removelist " $output_obj" $ECHO "$srcfile" > "$lockfile" fi $opt_dry_run || $RM $removelist func_append removelist " $lockfile" trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 srcfile=$func_to_tool_file_result func_quote_arg pretty "$srcfile" qsrcfile=$func_quote_arg_result # Only build a PIC object if we are building libtool libraries. if test yes = "$build_libtool_libs"; then # Without this assignment, base_compile gets emptied. fbsd_hideous_sh_bug=$base_compile if test no != "$pic_mode"; then command="$base_compile $qsrcfile $pic_flag" else # Don't build PIC code command="$base_compile $qsrcfile" fi func_mkdir_p "$xdir$objdir" if test -z "$output_obj"; then # Place PIC objects in $objdir func_append command " -o $lobj" fi func_show_eval_locale "$command" \ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed, then go on to compile the next one if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then func_show_eval '$MV "$output_obj" "$lobj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi # Allow error messages only from the first compilation. if test yes = "$suppress_opt"; then suppress_output=' >/dev/null 2>&1' fi fi # Only build a position-dependent object if we build old libraries. if test yes = "$build_old_libs"; then if test yes != "$pic_mode"; then # Don't build PIC code command="$base_compile $qsrcfile$pie_flag" else command="$base_compile $qsrcfile $pic_flag" fi if test yes = "$compiler_c_o"; then func_append command " -o $obj" fi # Suppress compiler output if we already did a PIC compilation. func_append command "$suppress_output" func_show_eval_locale "$command" \ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then func_show_eval '$MV "$output_obj" "$obj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi fi $opt_dry_run || { func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" # Unlock the critical section if it was locked if test no != "$need_locks"; then removelist=$lockfile $RM "$lockfile" fi } exit $EXIT_SUCCESS } $opt_help || { test compile = "$opt_mode" && func_mode_compile ${1+"$@"} } func_mode_help () { # We need to display help for each of the modes. case $opt_mode in "") # Generic help is extracted from the usage comments # at the start of this file. func_help ;; clean) $ECHO \ "Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... Remove files from the build directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, object or program, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; compile) $ECHO \ "Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE Compile a source file into a libtool library object. This mode accepts the following additional options: -o OUTPUT-FILE set the output file name to OUTPUT-FILE -no-suppress do not suppress compiler output for multiple passes -prefer-pic try to build PIC objects only -prefer-non-pic try to build non-PIC objects only -shared do not build a '.o' file suitable for static linking -static only build a '.o' file suitable for static linking -Wc,FLAG -Xcompiler FLAG pass FLAG directly to the compiler COMPILE-COMMAND is a command to be used in creating a 'standard' object file from the given SOURCEFILE. The output file name is determined by removing the directory component from SOURCEFILE, then substituting the C source code suffix '.c' with the library object suffix, '.lo'." ;; execute) $ECHO \ "Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... Automatically set library path, then run a program. This mode accepts the following additional options: -dlopen FILE add the directory containing FILE to the library path This mode sets the library path environment variable according to '-dlopen' flags. If any of the ARGS are libtool executable wrappers, then they are translated into their corresponding uninstalled binary, and any of their required library directories are added to the library path. Then, COMMAND is executed, with ARGS as arguments." ;; finish) $ECHO \ "Usage: $progname [OPTION]... --mode=finish [LIBDIR]... Complete the installation of libtool libraries. Each LIBDIR is a directory that contains libtool libraries. The commands that this mode executes may require superuser privileges. Use the '--dry-run' option if you just want to see what would be executed." ;; install) $ECHO \ "Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... Install executables or libraries. INSTALL-COMMAND is the installation command. The first component should be either the 'install' or 'cp' program. The following components of INSTALL-COMMAND are treated specially: -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation The rest of the components are interpreted as arguments to that command (only BSD-compatible install options are recognized)." ;; link) $ECHO \ "Usage: $progname [OPTION]... --mode=link LINK-COMMAND... Link object files or libraries together to form another library, or to create an executable program. LINK-COMMAND is a command using the C compiler that you would use to create a program from several object files. The following components of LINK-COMMAND are treated specially: -all-static do not do any dynamic linking at all -avoid-version do not add a version suffix if possible -bindir BINDIR specify path to binaries directory (for systems where libraries must be found in the PATH setting at runtime) -dlopen FILE '-dlpreopen' FILE if it cannot be dlopened at runtime -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) -export-symbols SYMFILE try to export only the symbols listed in SYMFILE -export-symbols-regex REGEX try to export only the symbols matching REGEX -LLIBDIR search LIBDIR for required installed libraries -lNAME OUTPUT-FILE requires the installed library libNAME -module build a library that can dlopened -no-fast-install disable the fast-install mode -no-install link a not-installable executable -no-undefined declare that a library does not refer to external symbols -o OUTPUT-FILE create OUTPUT-FILE from the specified objects -objectlist FILE use a list of object files found in FILE to specify objects -os2dllname NAME force a short DLL name on OS/2 (no effect on other OSes) -precious-files-regex REGEX don't remove output files matching REGEX -release RELEASE specify package release information -rpath LIBDIR the created library will eventually be installed in LIBDIR -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries -shared only do dynamic linking of libtool libraries -shrext SUFFIX override the standard shared library file extension -static do not do any dynamic linking of uninstalled libtool libraries -static-libtool-libs do not do any dynamic linking of libtool libraries -version-info CURRENT[:REVISION[:AGE]] specify library version info [each variable defaults to 0] -weak LIBNAME declare that the target provides the LIBNAME interface -Wc,FLAG -Xcompiler FLAG pass linker-specific FLAG directly to the compiler -Wa,FLAG -Xassembler FLAG pass linker-specific FLAG directly to the assembler -Wl,FLAG -Xlinker FLAG pass linker-specific FLAG directly to the linker -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) All other options (arguments beginning with '-') are ignored. Every other argument is treated as a filename. Files ending in '.la' are treated as uninstalled libtool libraries, other files are standard or library object files. If the OUTPUT-FILE ends in '.la', then a libtool library is created, only library objects ('.lo' files) may be specified, and '-rpath' is required, except when creating a convenience library. If OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created using 'ar' and 'ranlib', or on Windows using 'lib'. If OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file is created, otherwise an executable program is created." ;; uninstall) $ECHO \ "Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... Remove libraries from an installation directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; *) func_fatal_help "invalid operation mode '$opt_mode'" ;; esac echo $ECHO "Try '$progname --help' for more information about other modes." } # Now that we've collected a possible --mode arg, show help if necessary if $opt_help; then if test : = "$opt_help"; then func_mode_help else { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do func_mode_help done } | $SED -n '1p; 2,$s/^Usage:/ or: /p' { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do echo func_mode_help done } | $SED '1d /^When reporting/,/^Report/{ H d } $x /information about other modes/d /more detailed .*MODE/d s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' fi exit $? fi # func_mode_execute arg... func_mode_execute () { $debug_cmd # The first argument is the command name. cmd=$nonopt test -z "$cmd" && \ func_fatal_help "you must specify a COMMAND" # Handle -dlopen flags immediately. for file in $opt_dlopen; do test -f "$file" \ || func_fatal_help "'$file' is not a file" dir= case $file in *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$lib' is not a valid libtool archive" # Read the libtool library. dlname= library_names= func_source "$file" # Skip this library if it cannot be dlopened. if test -z "$dlname"; then # Warn if it was a shared library. test -n "$library_names" && \ func_warning "'$file' was not linked with '-export-dynamic'" continue fi func_dirname "$file" "" "." dir=$func_dirname_result if test -f "$dir/$objdir/$dlname"; then func_append dir "/$objdir" else if test ! -f "$dir/$dlname"; then func_fatal_error "cannot find '$dlname' in '$dir' or '$dir/$objdir'" fi fi ;; *.lo) # Just add the directory containing the .lo file. func_dirname "$file" "" "." dir=$func_dirname_result ;; *) func_warning "'-dlopen' is ignored for non-libtool libraries and objects" continue ;; esac # Get the absolute pathname. absdir=`cd "$dir" && pwd` test -n "$absdir" && dir=$absdir # Now add the directory to shlibpath_var. if eval "test -z \"\$$shlibpath_var\""; then eval "$shlibpath_var=\"\$dir\"" else eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" fi done # This variable tells wrapper scripts just to set shlibpath_var # rather than running their programs. libtool_execute_magic=$magic # Check if any of the arguments is a wrapper script. args= for file do case $file in -* | *.la | *.lo ) ;; *) # Do a test to see if this is really a libtool program. if func_ltwrapper_script_p "$file"; then func_source "$file" # Transform arg to wrapped name. file=$progdir/$program elif func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" func_source "$func_ltwrapper_scriptname_result" # Transform arg to wrapped name. file=$progdir/$program fi ;; esac # Quote arguments (to preserve shell metacharacters). func_append_quoted args "$file" done if $opt_dry_run; then # Display what would be done. if test -n "$shlibpath_var"; then eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" echo "export $shlibpath_var" fi $ECHO "$cmd$args" exit $EXIT_SUCCESS else if test -n "$shlibpath_var"; then # Export the shlibpath_var. eval "export $shlibpath_var" fi # Restore saved environment variables for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${save_$lt_var+set}\" = set; then $lt_var=\$save_$lt_var; export $lt_var else $lt_unset $lt_var fi" done # Now prepare to actually exec the command. exec_cmd=\$cmd$args fi } test execute = "$opt_mode" && func_mode_execute ${1+"$@"} # func_mode_finish arg... func_mode_finish () { $debug_cmd libs= libdirs= admincmds= for opt in "$nonopt" ${1+"$@"} do if test -d "$opt"; then func_append libdirs " $opt" elif test -f "$opt"; then if func_lalib_unsafe_p "$opt"; then func_append libs " $opt" else func_warning "'$opt' is not a valid libtool archive" fi else func_fatal_error "invalid argument '$opt'" fi done if test -n "$libs"; then if test -n "$lt_sysroot"; then sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" else sysroot_cmd= fi # Remove sysroot references if $opt_dry_run; then for lib in $libs; do echo "removing references to $lt_sysroot and '=' prefixes from $lib" done else tmpdir=`func_mktempdir` for lib in $libs; do $SED -e "$sysroot_cmd s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ > $tmpdir/tmp-la mv -f $tmpdir/tmp-la $lib done ${RM}r "$tmpdir" fi fi if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then for libdir in $libdirs; do if test -n "$finish_cmds"; then # Do each command in the finish commands. func_execute_cmds "$finish_cmds" 'admincmds="$admincmds '"$cmd"'"' fi if test -n "$finish_eval"; then # Do the single finish_eval. eval cmds=\"$finish_eval\" $opt_dry_run || eval "$cmds" || func_append admincmds " $cmds" fi done fi # Exit here if they wanted silent mode. $opt_quiet && exit $EXIT_SUCCESS if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then echo "----------------------------------------------------------------------" echo "Libraries have been installed in:" for libdir in $libdirs; do $ECHO " $libdir" done echo echo "If you ever happen to want to link against installed libraries" echo "in a given directory, LIBDIR, you must either use libtool, and" echo "specify the full pathname of the library, or use the '-LLIBDIR'" echo "flag during linking and do at least one of the following:" if test -n "$shlibpath_var"; then echo " - add LIBDIR to the '$shlibpath_var' environment variable" echo " during execution" fi if test -n "$runpath_var"; then echo " - add LIBDIR to the '$runpath_var' environment variable" echo " during linking" fi if test -n "$hardcode_libdir_flag_spec"; then libdir=LIBDIR eval flag=\"$hardcode_libdir_flag_spec\" $ECHO " - use the '$flag' linker flag" fi if test -n "$admincmds"; then $ECHO " - have your system administrator run these commands:$admincmds" fi if test -f /etc/ld.so.conf; then echo " - have your system administrator add LIBDIR to '/etc/ld.so.conf'" fi echo echo "See any operating system documentation about shared libraries for" case $host in solaris2.[6789]|solaris2.1[0-9]) echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" echo "pages." ;; *) echo "more information, such as the ld(1) and ld.so(8) manual pages." ;; esac echo "----------------------------------------------------------------------" fi exit $EXIT_SUCCESS } test finish = "$opt_mode" && func_mode_finish ${1+"$@"} # func_mode_install arg... func_mode_install () { $debug_cmd # There may be an optional sh(1) argument at the beginning of # install_prog (especially on Windows NT). if test "$SHELL" = "$nonopt" || test /bin/sh = "$nonopt" || # Allow the use of GNU shtool's install command. case $nonopt in *shtool*) :;; *) false;; esac then # Aesthetically quote it. func_quote_arg pretty "$nonopt" install_prog="$func_quote_arg_result " arg=$1 shift else install_prog= arg=$nonopt fi # The real first argument should be the name of the installation program. # Aesthetically quote it. func_quote_arg pretty "$arg" func_append install_prog "$func_quote_arg_result" install_shared_prog=$install_prog case " $install_prog " in *[\\\ /]cp\ *) install_cp=: ;; *) install_cp=false ;; esac # We need to accept at least all the BSD install flags. dest= files= opts= prev= install_type= isdir=false stripme= no_mode=: for arg do arg2= if test -n "$dest"; then func_append files " $dest" dest=$arg continue fi case $arg in -d) isdir=: ;; -f) if $install_cp; then :; else prev=$arg fi ;; -g | -m | -o) prev=$arg ;; -s) stripme=" -s" continue ;; -*) ;; *) # If the previous option needed an argument, then skip it. if test -n "$prev"; then if test X-m = "X$prev" && test -n "$install_override_mode"; then arg2=$install_override_mode no_mode=false fi prev= else dest=$arg continue fi ;; esac # Aesthetically quote the argument. func_quote_arg pretty "$arg" func_append install_prog " $func_quote_arg_result" if test -n "$arg2"; then func_quote_arg pretty "$arg2" fi func_append install_shared_prog " $func_quote_arg_result" done test -z "$install_prog" && \ func_fatal_help "you must specify an install program" test -n "$prev" && \ func_fatal_help "the '$prev' option requires an argument" if test -n "$install_override_mode" && $no_mode; then if $install_cp; then :; else func_quote_arg pretty "$install_override_mode" func_append install_shared_prog " -m $func_quote_arg_result" fi fi if test -z "$files"; then if test -z "$dest"; then func_fatal_help "no file or destination specified" else func_fatal_help "you must specify a destination" fi fi # Strip any trailing slash from the destination. func_stripname '' '/' "$dest" dest=$func_stripname_result # Check to see that the destination is a directory. test -d "$dest" && isdir=: if $isdir; then destdir=$dest destname= else func_dirname_and_basename "$dest" "" "." destdir=$func_dirname_result destname=$func_basename_result # Not a directory, so check to see that there is only one file specified. set dummy $files; shift test "$#" -gt 1 && \ func_fatal_help "'$dest' is not a directory" fi case $destdir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) for file in $files; do case $file in *.lo) ;; *) func_fatal_help "'$destdir' must be an absolute directory name" ;; esac done ;; esac # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic=$magic staticlibs= future_libdirs= current_libdirs= for file in $files; do # Do each installation. case $file in *.$libext) # Do the static libraries later. func_append staticlibs " $file" ;; *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$file' is not a valid libtool archive" library_names= old_library= relink_command= func_source "$file" # Add the libdir to current_libdirs if it is the destination. if test "X$destdir" = "X$libdir"; then case "$current_libdirs " in *" $libdir "*) ;; *) func_append current_libdirs " $libdir" ;; esac else # Note the libdir as a future libdir. case "$future_libdirs " in *" $libdir "*) ;; *) func_append future_libdirs " $libdir" ;; esac fi func_dirname "$file" "/" "" dir=$func_dirname_result func_append dir "$objdir" if test -n "$relink_command"; then # Determine the prefix the user has applied to our future dir. inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"` # Don't allow the user to place us outside of our expected # location b/c this prevents finding dependent libraries that # are installed to the same prefix. # At present, this check doesn't affect windows .dll's that # are installed into $libdir/../bin (currently, that works fine) # but it's something to keep an eye on. test "$inst_prefix_dir" = "$destdir" && \ func_fatal_error "error: cannot install '$file' to a directory not ending in $libdir" if test -n "$inst_prefix_dir"; then # Stick the inst_prefix_dir data into the link command. relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` else relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` fi func_warning "relinking '$file'" func_show_eval "$relink_command" \ 'func_fatal_error "error: relink '\''$file'\'' with the above command before installing it"' fi # See the names of the shared library. set dummy $library_names; shift if test -n "$1"; then realname=$1 shift srcname=$realname test -n "$relink_command" && srcname=${realname}T # Install the shared library and build the symlinks. func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ 'exit $?' tstripme=$stripme case $host_os in cygwin* | mingw* | pw32* | cegcc*) case $realname in *.dll.a) tstripme= ;; esac ;; os2*) case $realname in *_dll.a) tstripme= ;; esac ;; esac if test -n "$tstripme" && test -n "$striplib"; then func_show_eval "$striplib $destdir/$realname" 'exit $?' fi if test "$#" -gt 0; then # Delete the old symlinks, and create new ones. # Try 'ln -sf' first, because the 'ln' binary might depend on # the symlink we replace! Solaris /bin/ln does not understand -f, # so we also need to try rm && ln -s. for linkname do test "$linkname" != "$realname" \ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" done fi # Do each command in the postinstall commands. lib=$destdir/$realname func_execute_cmds "$postinstall_cmds" 'exit $?' fi # Install the pseudo-library for information purposes. func_basename "$file" name=$func_basename_result instname=$dir/${name}i func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' # Maybe install the static library, too. test -n "$old_library" && func_append staticlibs " $dir/$old_library" ;; *.lo) # Install (i.e. copy) a libtool object. # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # Deduce the name of the destination old-style object file. case $destfile in *.lo) func_lo2o "$destfile" staticdest=$func_lo2o_result ;; *.$objext) staticdest=$destfile destfile= ;; *) func_fatal_help "cannot copy a libtool object to '$destfile'" ;; esac # Install the libtool object if requested. test -n "$destfile" && \ func_show_eval "$install_prog $file $destfile" 'exit $?' # Install the old object if enabled. if test yes = "$build_old_libs"; then # Deduce the name of the old-style object file. func_lo2o "$file" staticobj=$func_lo2o_result func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' fi exit $EXIT_SUCCESS ;; *) # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # If the file is missing, and there is a .exe on the end, strip it # because it is most likely a libtool script we actually want to # install stripped_ext= case $file in *.exe) if test ! -f "$file"; then func_stripname '' '.exe' "$file" file=$func_stripname_result stripped_ext=.exe fi ;; esac # Do a test to see if this is really a libtool program. case $host in *cygwin* | *mingw*) if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" wrapper=$func_ltwrapper_scriptname_result else func_stripname '' '.exe' "$file" wrapper=$func_stripname_result fi ;; *) wrapper=$file ;; esac if func_ltwrapper_script_p "$wrapper"; then notinst_deplibs= relink_command= func_source "$wrapper" # Check the variables that should have been set. test -z "$generated_by_libtool_version" && \ func_fatal_error "invalid libtool wrapper script '$wrapper'" finalize=: for lib in $notinst_deplibs; do # Check to see that each library is installed. libdir= if test -f "$lib"; then func_source "$lib" fi libfile=$libdir/`$ECHO "$lib" | $SED 's%^.*/%%g'` if test -n "$libdir" && test ! -f "$libfile"; then func_warning "'$lib' has not been installed in '$libdir'" finalize=false fi done relink_command= func_source "$wrapper" outputname= if test no = "$fast_install" && test -n "$relink_command"; then $opt_dry_run || { if $finalize; then tmpdir=`func_mktempdir` func_basename "$file$stripped_ext" file=$func_basename_result outputname=$tmpdir/$file # Replace the output file specification. relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` $opt_quiet || { func_quote_arg expand,pretty "$relink_command" eval "func_echo $func_quote_arg_result" } if eval "$relink_command"; then : else func_error "error: relink '$file' with the above command before installing it" $opt_dry_run || ${RM}r "$tmpdir" continue fi file=$outputname else func_warning "cannot relink '$file'" fi } else # Install the binary that we compiled earlier. file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` fi fi # remove .exe since cygwin /usr/bin/install will append another # one anyway case $install_prog,$host in */usr/bin/install*,*cygwin*) case $file:$destfile in *.exe:*.exe) # this is ok ;; *.exe:*) destfile=$destfile.exe ;; *:*.exe) func_stripname '' '.exe' "$destfile" destfile=$func_stripname_result ;; esac ;; esac func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' $opt_dry_run || if test -n "$outputname"; then ${RM}r "$tmpdir" fi ;; esac done for file in $staticlibs; do func_basename "$file" name=$func_basename_result # Set up the ranlib parameters. oldlib=$destdir/$name func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result func_show_eval "$install_prog \$file \$oldlib" 'exit $?' if test -n "$stripme" && test -n "$old_striplib"; then func_show_eval "$old_striplib $tool_oldlib" 'exit $?' fi # Do each command in the postinstall commands. func_execute_cmds "$old_postinstall_cmds" 'exit $?' done test -n "$future_libdirs" && \ func_warning "remember to run '$progname --finish$future_libdirs'" if test -n "$current_libdirs"; then # Maybe just do a dry run. $opt_dry_run && current_libdirs=" -n$current_libdirs" exec_cmd='$SHELL "$progpath" $preserve_args --finish$current_libdirs' else exit $EXIT_SUCCESS fi } test install = "$opt_mode" && func_mode_install ${1+"$@"} # func_generate_dlsyms outputname originator pic_p # Extract symbols from dlprefiles and create ${outputname}S.o with # a dlpreopen symbol table. func_generate_dlsyms () { $debug_cmd my_outputname=$1 my_originator=$2 my_pic_p=${3-false} my_prefix=`$ECHO "$my_originator" | $SED 's%[^a-zA-Z0-9]%_%g'` my_dlsyms= if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then if test -n "$NM" && test -n "$global_symbol_pipe"; then my_dlsyms=${my_outputname}S.c else func_error "not configured to extract global symbols from dlpreopened files" fi fi if test -n "$my_dlsyms"; then case $my_dlsyms in "") ;; *.c) # Discover the nlist of each of the dlfiles. nlist=$output_objdir/$my_outputname.nm func_show_eval "$RM $nlist ${nlist}S ${nlist}T" # Parse the name list into a source file. func_verbose "creating $output_objdir/$my_dlsyms" $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ /* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */ /* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */ #ifdef __cplusplus extern \"C\" { #endif #if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) #pragma GCC diagnostic ignored \"-Wstrict-prototypes\" #endif /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* External symbol declarations for the compiler. */\ " if test yes = "$dlself"; then func_verbose "generating symbol list for '$output'" $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" # Add our own program objects to the symbol list. progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` for progfile in $progfiles; do func_to_tool_file "$progfile" func_convert_file_msys_to_w32 func_verbose "extracting global C symbols from '$func_to_tool_file_result'" $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" done if test -n "$exclude_expsyms"; then $opt_dry_run || { eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi if test -n "$export_symbols_regex"; then $opt_dry_run || { eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi # Prepare the list of exported symbols if test -z "$export_symbols"; then export_symbols=$output_objdir/$outputname.exp $opt_dry_run || { $RM $export_symbols eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' ;; esac } else $opt_dry_run || { eval "$SED -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' ;; esac } fi fi for dlprefile in $dlprefiles; do func_verbose "extracting global C symbols from '$dlprefile'" func_basename "$dlprefile" name=$func_basename_result case $host in *cygwin* | *mingw* | *cegcc* ) # if an import library, we need to obtain dlname if func_win32_import_lib_p "$dlprefile"; then func_tr_sh "$dlprefile" eval "curr_lafile=\$libfile_$func_tr_sh_result" dlprefile_dlbasename= if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then # Use subshell, to avoid clobbering current variable values dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` if test -n "$dlprefile_dlname"; then func_basename "$dlprefile_dlname" dlprefile_dlbasename=$func_basename_result else # no lafile. user explicitly requested -dlpreopen . $sharedlib_from_linklib_cmd "$dlprefile" dlprefile_dlbasename=$sharedlib_from_linklib_result fi fi $opt_dry_run || { if test -n "$dlprefile_dlbasename"; then eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' else func_warning "Could not compute DLL name from $name" eval '$ECHO ": $name " >> "$nlist"' fi func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" } else # not an import lib $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } fi ;; *) $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } ;; esac done $opt_dry_run || { # Make sure we have at least an empty file. test -f "$nlist" || : > "$nlist" if test -n "$exclude_expsyms"; then $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T $MV "$nlist"T "$nlist" fi # Try sorting and uniquifying the output. if $GREP -v "^: " < "$nlist" | if sort -k 3 /dev/null 2>&1; then sort -k 3 else sort +2 fi | uniq > "$nlist"S; then : else $GREP -v "^: " < "$nlist" > "$nlist"S fi if test -f "$nlist"S; then eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' else echo '/* NONE */' >> "$output_objdir/$my_dlsyms" fi func_show_eval '$RM "${nlist}I"' if test -n "$global_symbol_to_import"; then eval "$global_symbol_to_import"' < "$nlist"S > "$nlist"I' fi echo >> "$output_objdir/$my_dlsyms" "\ /* The mapping between symbol names and symbols. */ typedef struct { const char *name; void *address; } lt_dlsymlist; extern LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[];\ " if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ static void lt_syminit(void) { LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols; for (; symbol->name; ++symbol) {" $SED 's/.*/ if (STREQ (symbol->name, \"&\")) symbol->address = (void *) \&&;/' < "$nlist"I >> "$output_objdir/$my_dlsyms" echo >> "$output_objdir/$my_dlsyms" "\ } }" fi echo >> "$output_objdir/$my_dlsyms" "\ LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[] = { {\"$my_originator\", (void *) 0}," if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ {\"@INIT@\", (void *) <_syminit}," fi case $need_lib_prefix in no) eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; *) eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; esac echo >> "$output_objdir/$my_dlsyms" "\ {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt_${my_prefix}_LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif\ " } # !$opt_dry_run pic_flag_for_symtable= case "$compile_command " in *" -static "*) ;; *) case $host in # compiling the symbol table file with pic_flag works around # a FreeBSD bug that causes programs to crash when -lm is # linked before any other PIC object. But we must not use # pic_flag when linking with -static. The problem exists in # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; *-*-hpux*) pic_flag_for_symtable=" $pic_flag" ;; *) $my_pic_p && pic_flag_for_symtable=" $pic_flag" ;; esac ;; esac symtab_cflags= for arg in $LTCFLAGS; do case $arg in -pie | -fpie | -fPIE) ;; *) func_append symtab_cflags " $arg" ;; esac done # Now compile the dynamic symbol file. func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' # Clean up the generated files. func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T" "${nlist}I"' # Transform the symbol file into the correct name. symfileobj=$output_objdir/${my_outputname}S.$objext case $host in *cygwin* | *mingw* | *cegcc* ) if test -f "$output_objdir/$my_outputname.def"; then compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` else compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` fi ;; *) compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` ;; esac ;; *) func_fatal_error "unknown suffix for '$my_dlsyms'" ;; esac else # We keep going just in case the user didn't refer to # lt_preloaded_symbols. The linker will fail if global_symbol_pipe # really was required. # Nullify the symbol file. compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` fi } # func_cygming_gnu_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is a GNU/binutils-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_gnu_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` test -n "$func_cygming_gnu_implib_tmp" } # func_cygming_ms_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is an MS-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_ms_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` test -n "$func_cygming_ms_implib_tmp" } # func_win32_libid arg # return the library type of file 'arg' # # Need a lot of goo to handle *both* DLLs and import libs # Has to be a shell function in order to 'eat' the argument # that is supplied when $file_magic_command is called. # Despite the name, also deal with 64 bit binaries. func_win32_libid () { $debug_cmd win32_libid_type=unknown win32_fileres=`file -L $1 2>/dev/null` case $win32_fileres in *ar\ archive\ import\ library*) # definitely import win32_libid_type="x86 archive import" ;; *ar\ archive*) # could be an import, or static # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then case $nm_interface in "MS dumpbin") if func_cygming_ms_implib_p "$1" || func_cygming_gnu_implib_p "$1" then win32_nmres=import else win32_nmres= fi ;; *) func_to_tool_file "$1" func_convert_file_msys_to_w32 win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | $SED -n -e ' 1,100{ / I /{ s|.*|import| p q } }'` ;; esac case $win32_nmres in import*) win32_libid_type="x86 archive import";; *) win32_libid_type="x86 archive static";; esac fi ;; *DLL*) win32_libid_type="x86 DLL" ;; *executable*) # but shell scripts are "executable" too... case $win32_fileres in *MS\ Windows\ PE\ Intel*) win32_libid_type="x86 DLL" ;; esac ;; esac $ECHO "$win32_libid_type" } # func_cygming_dll_for_implib ARG # # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib () { $debug_cmd sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` } # func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs # # The is the core of a fallback implementation of a # platform-specific function to extract the name of the # DLL associated with the specified import library LIBNAME. # # SECTION_NAME is either .idata$6 or .idata$7, depending # on the platform and compiler that created the implib. # # Echos the name of the DLL associated with the # specified import library. func_cygming_dll_for_implib_fallback_core () { $debug_cmd match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` $OBJDUMP -s --section "$1" "$2" 2>/dev/null | $SED '/^Contents of section '"$match_literal"':/{ # Place marker at beginning of archive member dllname section s/.*/====MARK====/ p d } # These lines can sometimes be longer than 43 characters, but # are always uninteresting /:[ ]*file format pe[i]\{,1\}-/d /^In archive [^:]*:/d # Ensure marker is printed /^====MARK====/p # Remove all lines with less than 43 characters /^.\{43\}/!d # From remaining lines, remove first 43 characters s/^.\{43\}//' | $SED -n ' # Join marker and all lines until next marker into a single line /^====MARK====/ b para H $ b para b :para x s/\n//g # Remove the marker s/^====MARK====// # Remove trailing dots and whitespace s/[\. \t]*$// # Print /./p' | # we now have a list, one entry per line, of the stringified # contents of the appropriate section of all members of the # archive that possess that section. Heuristic: eliminate # all those that have a first or second character that is # a '.' (that is, objdump's representation of an unprintable # character.) This should work for all archives with less than # 0x302f exports -- but will fail for DLLs whose name actually # begins with a literal '.' or a single character followed by # a '.'. # # Of those that remain, print the first one. $SED -e '/^\./d;/^.\./d;q' } # func_cygming_dll_for_implib_fallback ARG # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # # This fallback implementation is for use when $DLLTOOL # does not support the --identify-strict option. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib_fallback () { $debug_cmd if func_cygming_gnu_implib_p "$1"; then # binutils import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` elif func_cygming_ms_implib_p "$1"; then # ms-generated import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` else # unknown sharedlib_from_linklib_result= fi } # func_extract_an_archive dir oldlib func_extract_an_archive () { $debug_cmd f_ex_an_ar_dir=$1; shift f_ex_an_ar_oldlib=$1 if test yes = "$lock_old_archive_extraction"; then lockfile=$f_ex_an_ar_oldlib.lock until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done fi func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ 'stat=$?; rm -f "$lockfile"; exit $stat' if test yes = "$lock_old_archive_extraction"; then $opt_dry_run || rm -f "$lockfile" fi if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then : else func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" fi } # func_extract_archives gentop oldlib ... func_extract_archives () { $debug_cmd my_gentop=$1; shift my_oldlibs=${1+"$@"} my_oldobjs= my_xlib= my_xabs= my_xdir= for my_xlib in $my_oldlibs; do # Extract the objects. case $my_xlib in [\\/]* | [A-Za-z]:[\\/]*) my_xabs=$my_xlib ;; *) my_xabs=`pwd`"/$my_xlib" ;; esac func_basename "$my_xlib" my_xlib=$func_basename_result my_xlib_u=$my_xlib while :; do case " $extracted_archives " in *" $my_xlib_u "*) func_arith $extracted_serial + 1 extracted_serial=$func_arith_result my_xlib_u=lt$extracted_serial-$my_xlib ;; *) break ;; esac done extracted_archives="$extracted_archives $my_xlib_u" my_xdir=$my_gentop/$my_xlib_u func_mkdir_p "$my_xdir" case $host in *-darwin*) func_verbose "Extracting $my_xabs" # Do not bother doing anything if just a dry run $opt_dry_run || { darwin_orig_dir=`pwd` cd $my_xdir || exit $? darwin_archive=$my_xabs darwin_curdir=`pwd` func_basename "$darwin_archive" darwin_base_archive=$func_basename_result darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` if test -n "$darwin_arches"; then darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` darwin_arch= func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" for darwin_arch in $darwin_arches; do func_mkdir_p "unfat-$$/$darwin_base_archive-$darwin_arch" $LIPO -thin $darwin_arch -output "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" "$darwin_archive" cd "unfat-$$/$darwin_base_archive-$darwin_arch" func_extract_an_archive "`pwd`" "$darwin_base_archive" cd "$darwin_curdir" $RM "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" done # $darwin_arches ## Okay now we've a bunch of thin objects, gotta fatten them up :) darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$sed_basename" | sort -u` darwin_file= darwin_files= for darwin_file in $darwin_filelist; do darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` $LIPO -create -output "$darwin_file" $darwin_files done # $darwin_filelist $RM -rf unfat-$$ cd "$darwin_orig_dir" else cd $darwin_orig_dir func_extract_an_archive "$my_xdir" "$my_xabs" fi # $darwin_arches } # !$opt_dry_run ;; *) func_extract_an_archive "$my_xdir" "$my_xabs" ;; esac my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` done func_extract_archives_result=$my_oldobjs } # func_emit_wrapper [arg=no] # # Emit a libtool wrapper script on stdout. # Don't directly open a file because we may want to # incorporate the script contents within a cygwin/mingw # wrapper executable. Must ONLY be called from within # func_mode_link because it depends on a number of variables # set therein. # # ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR # variable will take. If 'yes', then the emitted script # will assume that the directory where it is stored is # the $objdir directory. This is a cygwin/mingw-specific # behavior. func_emit_wrapper () { func_emit_wrapper_arg1=${1-no} $ECHO "\ #! $SHELL # $output - temporary wrapper script for $objdir/$outputname # Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # The $output program cannot be directly executed until all the libtool # libraries that it depends on are installed. # # This wrapper script should never be moved out of the build directory. # If it is, it will not operate correctly. # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='$sed_quote_subst' # Be Bourne compatible if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH relink_command=\"$relink_command\" # This environment variable determines our operation mode. if test \"\$libtool_install_magic\" = \"$magic\"; then # install mode needs the following variables: generated_by_libtool_version='$macro_version' notinst_deplibs='$notinst_deplibs' else # When we are sourced in execute mode, \$file and \$ECHO are already set. if test \"\$libtool_execute_magic\" != \"$magic\"; then file=\"\$0\"" func_quote_arg pretty "$ECHO" qECHO=$func_quote_arg_result $ECHO "\ # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } ECHO=$qECHO fi # Very basic option parsing. These options are (a) specific to # the libtool wrapper, (b) are identical between the wrapper # /script/ and the wrapper /executable/ that is used only on # windows platforms, and (c) all begin with the string "--lt-" # (application programs are unlikely to have options that match # this pattern). # # There are only two supported options: --lt-debug and # --lt-dump-script. There is, deliberately, no --lt-help. # # The first argument to this parsing function should be the # script's $0 value, followed by "$@". lt_option_debug= func_parse_lt_options () { lt_script_arg0=\$0 shift for lt_opt do case \"\$lt_opt\" in --lt-debug) lt_option_debug=1 ;; --lt-dump-script) lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` cat \"\$lt_dump_D/\$lt_dump_F\" exit 0 ;; --lt-*) \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 exit 1 ;; esac done # Print the debug banner immediately: if test -n \"\$lt_option_debug\"; then echo \"$outputname:$output:\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\" 1>&2 fi } # Used when --lt-debug. Prints its arguments to stdout # (redirection is the responsibility of the caller) func_lt_dump_args () { lt_dump_args_N=1; for lt_arg do \$ECHO \"$outputname:$output:\$LINENO: newargv[\$lt_dump_args_N]: \$lt_arg\" lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` done } # Core function for launching the target application func_exec_program_core () { " case $host in # Backslashes separate directories on plain windows *-*-mingw | *-*-os2* | *-cegcc*) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} " ;; *) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir/\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir/\$program\" \${1+\"\$@\"} " ;; esac $ECHO "\ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 exit 1 } # A function to encapsulate launching the target application # Strips options in the --lt-* namespace from \$@ and # launches target application with the remaining arguments. func_exec_program () { case \" \$* \" in *\\ --lt-*) for lt_wr_arg do case \$lt_wr_arg in --lt-*) ;; *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; esac shift done ;; esac func_exec_program_core \${1+\"\$@\"} } # Parse options func_parse_lt_options \"\$0\" \${1+\"\$@\"} # Find the directory that this script lives in. thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` test \"x\$thisdir\" = \"x\$file\" && thisdir=. # Follow symbolic links until we get to the real thisdir. file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` while test -n \"\$file\"; do destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` # If there was a directory component, then change thisdir. if test \"x\$destdir\" != \"x\$file\"; then case \"\$destdir\" in [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; *) thisdir=\"\$thisdir/\$destdir\" ;; esac fi file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` done # Usually 'no', except on cygwin/mingw when embedded into # the cwrapper. WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then # special case for '.' if test \"\$thisdir\" = \".\"; then thisdir=\`pwd\` fi # remove .libs from thisdir case \"\$thisdir\" in *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; $objdir ) thisdir=. ;; esac fi # Try to get the absolute directory name. absdir=\`cd \"\$thisdir\" && pwd\` test -n \"\$absdir\" && thisdir=\"\$absdir\" " if test yes = "$fast_install"; then $ECHO "\ program=lt-'$outputname'$exeext progdir=\"\$thisdir/$objdir\" if test ! -f \"\$progdir/\$program\" || { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | $SED 1q\`; \\ test \"X\$file\" != \"X\$progdir/\$program\"; }; then file=\"\$\$-\$program\" if test ! -d \"\$progdir\"; then $MKDIR \"\$progdir\" else $RM \"\$progdir/\$file\" fi" $ECHO "\ # relink executable if necessary if test -n \"\$relink_command\"; then if relink_command_output=\`eval \$relink_command 2>&1\`; then : else \$ECHO \"\$relink_command_output\" >&2 $RM \"\$progdir/\$file\" exit 1 fi fi $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || { $RM \"\$progdir/\$program\"; $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } $RM \"\$progdir/\$file\" fi" else $ECHO "\ program='$outputname' progdir=\"\$thisdir/$objdir\" " fi $ECHO "\ if test -f \"\$progdir/\$program\"; then" # fixup the dll searchpath if we need to. # # Fix the DLL searchpath if we need to. Do this before prepending # to shlibpath, because on Windows, both are PATH and uninstalled # libraries must come first. if test -n "$dllsearchpath"; then $ECHO "\ # Add the dll search path components to the executable PATH PATH=$dllsearchpath:\$PATH " fi # Export our shlibpath_var if we have one. if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then $ECHO "\ # Add our own library path to $shlibpath_var $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" # Some systems cannot cope with colon-terminated $shlibpath_var # The second colon is a workaround for a bug in BeOS R4 sed $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` export $shlibpath_var " fi $ECHO "\ if test \"\$libtool_execute_magic\" != \"$magic\"; then # Run the actual program with our arguments. func_exec_program \${1+\"\$@\"} fi else # The program doesn't exist. \$ECHO \"\$0: error: '\$progdir/\$program' does not exist\" 1>&2 \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 exit 1 fi fi\ " } # func_emit_cwrapperexe_src # emit the source code for a wrapper executable on stdout # Must ONLY be called from within func_mode_link because # it depends on a number of variable set therein. func_emit_cwrapperexe_src () { cat < #include #ifdef _MSC_VER # include # include # include #else # include # include # ifdef __CYGWIN__ # include # endif #endif #include #include #include #include #include #include #include #include #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* declarations of non-ANSI functions */ #if defined __MINGW32__ # ifdef __STRICT_ANSI__ int _putenv (const char *); # endif #elif defined __CYGWIN__ # ifdef __STRICT_ANSI__ char *realpath (const char *, char *); int putenv (char *); int setenv (const char *, const char *, int); # endif /* #elif defined other_platform || defined ... */ #endif /* portability defines, excluding path handling macros */ #if defined _MSC_VER # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv # define S_IXUSR _S_IEXEC #elif defined __MINGW32__ # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv #elif defined __CYGWIN__ # define HAVE_SETENV # define FOPEN_WB "wb" /* #elif defined other platforms ... */ #endif #if defined PATH_MAX # define LT_PATHMAX PATH_MAX #elif defined MAXPATHLEN # define LT_PATHMAX MAXPATHLEN #else # define LT_PATHMAX 1024 #endif #ifndef S_IXOTH # define S_IXOTH 0 #endif #ifndef S_IXGRP # define S_IXGRP 0 #endif /* path handling portability macros */ #ifndef DIR_SEPARATOR # define DIR_SEPARATOR '/' # define PATH_SEPARATOR ':' #endif #if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \ defined __OS2__ # define HAVE_DOS_BASED_FILE_SYSTEM # define FOPEN_WB "wb" # ifndef DIR_SEPARATOR_2 # define DIR_SEPARATOR_2 '\\' # endif # ifndef PATH_SEPARATOR_2 # define PATH_SEPARATOR_2 ';' # endif #endif #ifndef DIR_SEPARATOR_2 # define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) #else /* DIR_SEPARATOR_2 */ # define IS_DIR_SEPARATOR(ch) \ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) #endif /* DIR_SEPARATOR_2 */ #ifndef PATH_SEPARATOR_2 # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) #else /* PATH_SEPARATOR_2 */ # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) #endif /* PATH_SEPARATOR_2 */ #ifndef FOPEN_WB # define FOPEN_WB "w" #endif #ifndef _O_BINARY # define _O_BINARY 0 #endif #define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) #define XFREE(stale) do { \ if (stale) { free (stale); stale = 0; } \ } while (0) #if defined LT_DEBUGWRAPPER static int lt_debug = 1; #else static int lt_debug = 0; #endif const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ void *xmalloc (size_t num); char *xstrdup (const char *string); const char *base_name (const char *name); char *find_executable (const char *wrapper); char *chase_symlinks (const char *pathspec); int make_executable (const char *path); int check_executable (const char *path); char *strendzap (char *str, const char *pat); void lt_debugprintf (const char *file, int line, const char *fmt, ...); void lt_fatal (const char *file, int line, const char *message, ...); static const char *nonnull (const char *s); static const char *nonempty (const char *s); void lt_setenv (const char *name, const char *value); char *lt_extend_str (const char *orig_value, const char *add, int to_end); void lt_update_exe_path (const char *name, const char *value); void lt_update_lib_path (const char *name, const char *value); char **prepare_spawn (char **argv); void lt_dump_script (FILE *f); EOF cat <= 0) && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return 1; else return 0; } int make_executable (const char *path) { int rval = 0; struct stat st; lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", nonempty (path)); if ((!path) || (!*path)) return 0; if (stat (path, &st) >= 0) { rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); } return rval; } /* Searches for the full path of the wrapper. Returns newly allocated full path name if found, NULL otherwise Does not chase symlinks, even on platforms that support them. */ char * find_executable (const char *wrapper) { int has_slash = 0; const char *p; const char *p_next; /* static buffer for getcwd */ char tmp[LT_PATHMAX + 1]; size_t tmp_len; char *concat_name; lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", nonempty (wrapper)); if ((wrapper == NULL) || (*wrapper == '\0')) return NULL; /* Absolute path? */ #if defined HAVE_DOS_BASED_FILE_SYSTEM if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } else { #endif if (IS_DIR_SEPARATOR (wrapper[0])) { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } #if defined HAVE_DOS_BASED_FILE_SYSTEM } #endif for (p = wrapper; *p; p++) if (*p == '/') { has_slash = 1; break; } if (!has_slash) { /* no slashes; search PATH */ const char *path = getenv ("PATH"); if (path != NULL) { for (p = path; *p; p = p_next) { const char *q; size_t p_len; for (q = p; *q; q++) if (IS_PATH_SEPARATOR (*q)) break; p_len = (size_t) (q - p); p_next = (*q == '\0' ? q : q + 1); if (p_len == 0) { /* empty path: current directory */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); } else { concat_name = XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, p, p_len); concat_name[p_len] = '/'; strcpy (concat_name + p_len + 1, wrapper); } if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } } /* not found in PATH; assume curdir */ } /* Relative path | not found in path: prepend cwd */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); return NULL; } char * chase_symlinks (const char *pathspec) { #ifndef S_ISLNK return xstrdup (pathspec); #else char buf[LT_PATHMAX]; struct stat s; char *tmp_pathspec = xstrdup (pathspec); char *p; int has_symlinks = 0; while (strlen (tmp_pathspec) && !has_symlinks) { lt_debugprintf (__FILE__, __LINE__, "checking path component for symlinks: %s\n", tmp_pathspec); if (lstat (tmp_pathspec, &s) == 0) { if (S_ISLNK (s.st_mode) != 0) { has_symlinks = 1; break; } /* search backwards for last DIR_SEPARATOR */ p = tmp_pathspec + strlen (tmp_pathspec) - 1; while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) p--; if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) { /* no more DIR_SEPARATORS left */ break; } *p = '\0'; } else { lt_fatal (__FILE__, __LINE__, "error accessing file \"%s\": %s", tmp_pathspec, nonnull (strerror (errno))); } } XFREE (tmp_pathspec); if (!has_symlinks) { return xstrdup (pathspec); } tmp_pathspec = realpath (pathspec, buf); if (tmp_pathspec == 0) { lt_fatal (__FILE__, __LINE__, "could not follow symlinks for %s", pathspec); } return xstrdup (tmp_pathspec); #endif } char * strendzap (char *str, const char *pat) { size_t len, patlen; assert (str != NULL); assert (pat != NULL); len = strlen (str); patlen = strlen (pat); if (patlen <= len) { str += len - patlen; if (STREQ (str, pat)) *str = '\0'; } return str; } void lt_debugprintf (const char *file, int line, const char *fmt, ...) { va_list args; if (lt_debug) { (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); va_start (args, fmt); (void) vfprintf (stderr, fmt, args); va_end (args); } } static void lt_error_core (int exit_status, const char *file, int line, const char *mode, const char *message, va_list ap) { fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); vfprintf (stderr, message, ap); fprintf (stderr, ".\n"); if (exit_status >= 0) exit (exit_status); } void lt_fatal (const char *file, int line, const char *message, ...) { va_list ap; va_start (ap, message); lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); va_end (ap); } static const char * nonnull (const char *s) { return s ? s : "(null)"; } static const char * nonempty (const char *s) { return (s && !*s) ? "(empty)" : nonnull (s); } void lt_setenv (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_setenv) setting '%s' to '%s'\n", nonnull (name), nonnull (value)); { #ifdef HAVE_SETENV /* always make a copy, for consistency with !HAVE_SETENV */ char *str = xstrdup (value); setenv (name, str, 1); #else size_t len = strlen (name) + 1 + strlen (value) + 1; char *str = XMALLOC (char, len); sprintf (str, "%s=%s", name, value); if (putenv (str) != EXIT_SUCCESS) { XFREE (str); } #endif } } char * lt_extend_str (const char *orig_value, const char *add, int to_end) { char *new_value; if (orig_value && *orig_value) { size_t orig_value_len = strlen (orig_value); size_t add_len = strlen (add); new_value = XMALLOC (char, add_len + orig_value_len + 1); if (to_end) { strcpy (new_value, orig_value); strcpy (new_value + orig_value_len, add); } else { strcpy (new_value, add); strcpy (new_value + add_len, orig_value); } } else { new_value = xstrdup (add); } return new_value; } void lt_update_exe_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); /* some systems can't cope with a ':'-terminated path #' */ size_t len = strlen (new_value); while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1])) { new_value[--len] = '\0'; } lt_setenv (name, new_value); XFREE (new_value); } } void lt_update_lib_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); lt_setenv (name, new_value); XFREE (new_value); } } EOF case $host_os in mingw*) cat <<"EOF" /* Prepares an argument vector before calling spawn(). Note that spawn() does not by itself call the command interpreter (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&v); v.dwPlatformId == VER_PLATFORM_WIN32_NT; }) ? "cmd.exe" : "command.com"). Instead it simply concatenates the arguments, separated by ' ', and calls CreateProcess(). We must quote the arguments since Win32 CreateProcess() interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a special way: - Space and tab are interpreted as delimiters. They are not treated as delimiters if they are surrounded by double quotes: "...". - Unescaped double quotes are removed from the input. Their only effect is that within double quotes, space and tab are treated like normal characters. - Backslashes not followed by double quotes are not special. - But 2*n+1 backslashes followed by a double quote become n backslashes followed by a double quote (n >= 0): \" -> " \\\" -> \" \\\\\" -> \\" */ #define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" #define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" char ** prepare_spawn (char **argv) { size_t argc; char **new_argv; size_t i; /* Count number of arguments. */ for (argc = 0; argv[argc] != NULL; argc++) ; /* Allocate new argument vector. */ new_argv = XMALLOC (char *, argc + 1); /* Put quoted arguments into the new argument vector. */ for (i = 0; i < argc; i++) { const char *string = argv[i]; if (string[0] == '\0') new_argv[i] = xstrdup ("\"\""); else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) { int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); size_t length; unsigned int backslashes; const char *s; char *quoted_string; char *p; length = 0; backslashes = 0; if (quote_around) length++; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') length += backslashes + 1; length++; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) length += backslashes + 1; quoted_string = XMALLOC (char, length + 1); p = quoted_string; backslashes = 0; if (quote_around) *p++ = '"'; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') { unsigned int j; for (j = backslashes + 1; j > 0; j--) *p++ = '\\'; } *p++ = c; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) { unsigned int j; for (j = backslashes; j > 0; j--) *p++ = '\\'; *p++ = '"'; } *p = '\0'; new_argv[i] = quoted_string; } else new_argv[i] = (char *) string; } new_argv[argc] = NULL; return new_argv; } EOF ;; esac cat <<"EOF" void lt_dump_script (FILE* f) { EOF func_emit_wrapper yes | $SED -n -e ' s/^\(.\{79\}\)\(..*\)/\1\ \2/ h s/\([\\"]\)/\\\1/g s/$/\\n/ s/\([^\n]*\).*/ fputs ("\1", f);/p g D' cat <<"EOF" } EOF } # end: func_emit_cwrapperexe_src # func_win32_import_lib_p ARG # True if ARG is an import lib, as indicated by $file_magic_cmd func_win32_import_lib_p () { $debug_cmd case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in *import*) : ;; *) false ;; esac } # func_suncc_cstd_abi # !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!! # Several compiler flags select an ABI that is incompatible with the # Cstd library. Avoid specifying it if any are in CXXFLAGS. func_suncc_cstd_abi () { $debug_cmd case " $compile_command " in *" -compat=g "*|*\ -std=c++[0-9][0-9]\ *|*" -library=stdcxx4 "*|*" -library=stlport4 "*) suncc_use_cstd_abi=no ;; *) suncc_use_cstd_abi=yes ;; esac } # func_mode_link arg... func_mode_link () { $debug_cmd case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) # It is impossible to link a dll without this setting, and # we shouldn't force the makefile maintainer to figure out # what system we are compiling for in order to pass an extra # flag for every libtool invocation. # allow_undefined=no # FIXME: Unfortunately, there are problems with the above when trying # to make a dll that has undefined symbols, in which case not # even a static library is built. For now, we need to specify # -no-undefined on the libtool link line when we can be certain # that all symbols are satisfied, otherwise we get a static library. allow_undefined=yes ;; *) allow_undefined=yes ;; esac libtool_args=$nonopt base_compile="$nonopt $@" compile_command=$nonopt finalize_command=$nonopt compile_rpath= finalize_rpath= compile_shlibpath= finalize_shlibpath= convenience= old_convenience= deplibs= old_deplibs= compiler_flags= linker_flags= dllsearchpath= lib_search_path=`pwd` inst_prefix_dir= new_inherited_linker_flags= avoid_version=no bindir= dlfiles= dlprefiles= dlself=no export_dynamic=no export_symbols= export_symbols_regex= generated= libobjs= ltlibs= module=no no_install=no objs= os2dllname= non_pic_objects= precious_files_regex= prefer_static_libs=no preload=false prev= prevarg= release= rpath= xrpath= perm_rpath= temp_rpath= thread_safe=no vinfo= vinfo_number=no weak_libs= single_module=$wl-single_module func_infer_tag $base_compile # We need to know -static, to get the right output filenames. for arg do case $arg in -shared) test yes != "$build_libtool_libs" \ && func_fatal_configuration "cannot build a shared library" build_old_libs=no break ;; -all-static | -static | -static-libtool-libs) case $arg in -all-static) if test yes = "$build_libtool_libs" && test -z "$link_static_flag"; then func_warning "complete static linking is impossible in this configuration" fi if test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; -static) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=built ;; -static-libtool-libs) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; esac build_libtool_libs=no build_old_libs=yes break ;; esac done # See if our shared archives depend on static archives. test -n "$old_archive_from_new_cmds" && build_old_libs=yes # Go through the arguments, transforming them on the way. while test "$#" -gt 0; do arg=$1 shift func_quote_arg pretty,unquoted "$arg" qarg=$func_quote_arg_unquoted_result func_append libtool_args " $func_quote_arg_result" # If the previous option needs an argument, assign it. if test -n "$prev"; then case $prev in output) func_append compile_command " @OUTPUT@" func_append finalize_command " @OUTPUT@" ;; esac case $prev in bindir) bindir=$arg prev= continue ;; dlfiles|dlprefiles) $preload || { # Add the symbol object into the linking commands. func_append compile_command " @SYMFILE@" func_append finalize_command " @SYMFILE@" preload=: } case $arg in *.la | *.lo) ;; # We handle these cases below. force) if test no = "$dlself"; then dlself=needless export_dynamic=yes fi prev= continue ;; self) if test dlprefiles = "$prev"; then dlself=yes elif test dlfiles = "$prev" && test yes != "$dlopen_self"; then dlself=yes else dlself=needless export_dynamic=yes fi prev= continue ;; *) if test dlfiles = "$prev"; then func_append dlfiles " $arg" else func_append dlprefiles " $arg" fi prev= continue ;; esac ;; expsyms) export_symbols=$arg test -f "$arg" \ || func_fatal_error "symbol file '$arg' does not exist" prev= continue ;; expsyms_regex) export_symbols_regex=$arg prev= continue ;; framework) case $host in *-*-darwin*) case "$deplibs " in *" $qarg.ltframework "*) ;; *) func_append deplibs " $qarg.ltframework" # this is fixed later ;; esac ;; esac prev= continue ;; inst_prefix) inst_prefix_dir=$arg prev= continue ;; mllvm) # Clang does not use LLVM to link, so we can simply discard any # '-mllvm $arg' options when doing the link step. prev= continue ;; objectlist) if test -f "$arg"; then save_arg=$arg moreargs= for fil in `cat "$save_arg"` do # func_append moreargs " $fil" arg=$fil # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test none = "$pic_object" && test none = "$non_pic_object"; then func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result if test none != "$pic_object"; then # Prepend the subdirectory the object is found in. pic_object=$xdir$pic_object if test dlfiles = "$prev"; then if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg=$pic_object fi # Non-PIC object. if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test none = "$pic_object"; then arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "'$arg' is not a valid libtool object" fi fi done else func_fatal_error "link input file '$arg' does not exist" fi arg=$save_arg prev= continue ;; os2dllname) os2dllname=$arg prev= continue ;; precious_regex) precious_files_regex=$arg prev= continue ;; release) release=-$arg prev= continue ;; rpath | xrpath) # We need an absolute path. case $arg in [\\/]* | [A-Za-z]:[\\/]*) ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac if test rpath = "$prev"; then case "$rpath " in *" $arg "*) ;; *) func_append rpath " $arg" ;; esac else case "$xrpath " in *" $arg "*) ;; *) func_append xrpath " $arg" ;; esac fi prev= continue ;; shrext) shrext_cmds=$arg prev= continue ;; weak) func_append weak_libs " $arg" prev= continue ;; xassembler) func_append compiler_flags " -Xassembler $qarg" prev= func_append compile_command " -Xassembler $qarg" func_append finalize_command " -Xassembler $qarg" continue ;; xcclinker) func_append linker_flags " $qarg" func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xcompiler) func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xlinker) func_append linker_flags " $qarg" func_append compiler_flags " $wl$qarg" prev= func_append compile_command " $wl$qarg" func_append finalize_command " $wl$qarg" continue ;; *) eval "$prev=\"\$arg\"" prev= continue ;; esac fi # test -n "$prev" prevarg=$arg case $arg in -all-static) if test -n "$link_static_flag"; then # See comment for -static flag below, for more details. func_append compile_command " $link_static_flag" func_append finalize_command " $link_static_flag" fi continue ;; -allow-undefined) # FIXME: remove this flag sometime in the future. func_fatal_error "'-allow-undefined' must not be used because it is the default" ;; -avoid-version) avoid_version=yes continue ;; -bindir) prev=bindir continue ;; -dlopen) prev=dlfiles continue ;; -dlpreopen) prev=dlprefiles continue ;; -export-dynamic) export_dynamic=yes continue ;; -export-symbols | -export-symbols-regex) if test -n "$export_symbols" || test -n "$export_symbols_regex"; then func_fatal_error "more than one -exported-symbols argument is not allowed" fi if test X-export-symbols = "X$arg"; then prev=expsyms else prev=expsyms_regex fi continue ;; -framework) prev=framework continue ;; -inst-prefix-dir) prev=inst_prefix continue ;; # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* # so, if we see these flags be careful not to treat them like -L -L[A-Z][A-Z]*:*) case $with_gcc/$host in no/*-*-irix* | /*-*-irix*) func_append compile_command " $arg" func_append finalize_command " $arg" ;; esac continue ;; -L*) func_stripname "-L" '' "$arg" if test -z "$func_stripname_result"; then if test "$#" -gt 0; then func_fatal_error "require no space between '-L' and '$1'" else func_fatal_error "need path for '-L' option" fi fi func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) absdir=`cd "$dir" && pwd` test -z "$absdir" && \ func_fatal_error "cannot determine absolute directory name of '$dir'" dir=$absdir ;; esac case "$deplibs " in *" -L$dir "* | *" $arg "*) # Will only happen for absolute or sysroot arguments ;; *) # Preserve sysroot, but never include relative directories case $dir in [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; *) func_append deplibs " -L$dir" ;; esac func_append lib_search_path " $dir" ;; esac case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` case :$dllsearchpath: in *":$dir:"*) ;; ::) dllsearchpath=$dir;; *) func_append dllsearchpath ":$dir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac continue ;; -l*) if test X-lc = "X$arg" || test X-lm = "X$arg"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) # These systems don't actually have a C or math library (as such) continue ;; *-*-os2*) # These systems don't actually have a C library (as such) test X-lc = "X$arg" && continue ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig* | *-*-midnightbsd*) # Do not include libc due to us having libc/libc_r. test X-lc = "X$arg" && continue ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C and math libraries are in the System framework func_append deplibs " System.ltframework" continue ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype test X-lc = "X$arg" && continue ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work test X-lc = "X$arg" && continue ;; esac elif test X-lc_r = "X$arg"; then case $host in *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig* | *-*-midnightbsd*) # Do not include libc_r directly, use -pthread flag. continue ;; esac fi func_append deplibs " $arg" continue ;; -mllvm) prev=mllvm continue ;; -module) module=yes continue ;; # Tru64 UNIX uses -model [arg] to determine the layout of C++ # classes, name mangling, and exception handling. # Darwin uses the -arch flag to determine output architecture. -model|-arch|-isysroot|--sysroot) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" prev=xcompiler continue ;; # Solaris ld rejects as of 11.4. Refer to Oracle bug 22985199. -pthread) case $host in *solaris2*) ;; *) case "$new_inherited_linker_flags " in *" $arg "*) ;; * ) func_append new_inherited_linker_flags " $arg" ;; esac ;; esac continue ;; -mt|-mthreads|-kthread|-Kthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case "$new_inherited_linker_flags " in *" $arg "*) ;; * ) func_append new_inherited_linker_flags " $arg" ;; esac continue ;; -multi_module) single_module=$wl-multi_module continue ;; -no-fast-install) fast_install=no continue ;; -no-install) case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) # The PATH hackery in wrapper scripts is required on Windows # and Darwin in order for the loader to find any dlls it needs. func_warning "'-no-install' is ignored for $host" func_warning "assuming '-no-fast-install' instead" fast_install=no ;; *) no_install=yes ;; esac continue ;; -no-undefined) allow_undefined=no continue ;; -objectlist) prev=objectlist continue ;; -os2dllname) prev=os2dllname continue ;; -o) prev=output ;; -precious-files-regex) prev=precious_regex continue ;; -release) prev=release continue ;; -rpath) prev=rpath continue ;; -R) prev=xrpath continue ;; -R*) func_stripname '-R' '' "$arg" dir=$func_stripname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; =*) func_stripname '=' '' "$dir" dir=$lt_sysroot$func_stripname_result ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac continue ;; -shared) # The effects of -shared are defined in a previous loop. continue ;; -shrext) prev=shrext continue ;; -static | -static-libtool-libs) # The effects of -static are defined in a previous loop. # We used to do the same as -all-static on platforms that # didn't have a PIC flag, but the assumption that the effects # would be equivalent was wrong. It would break on at least # Digital Unix and AIX. continue ;; -thread-safe) thread_safe=yes continue ;; -version-info) prev=vinfo continue ;; -version-number) prev=vinfo vinfo_number=yes continue ;; -weak) prev=weak continue ;; -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result arg= save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs func_quote_arg pretty "$flag" func_append arg " $func_quote_arg_result" func_append compiler_flags " $func_quote_arg_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Wl,*) func_stripname '-Wl,' '' "$arg" args=$func_stripname_result arg= save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs func_quote_arg pretty "$flag" func_append arg " $wl$func_quote_arg_result" func_append compiler_flags " $wl$func_quote_arg_result" func_append linker_flags " $func_quote_arg_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Xassembler) prev=xassembler continue ;; -Xcompiler) prev=xcompiler continue ;; -Xlinker) prev=xlinker continue ;; -XCClinker) prev=xcclinker continue ;; # -msg_* for osf cc -msg_*) func_quote_arg pretty "$arg" arg=$func_quote_arg_result ;; # Flags to be passed through unchanged, with rationale: # -64, -mips[0-9] enable 64-bit mode for the SGI compiler # -r[0-9][0-9]* specify processor for the SGI compiler # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler # +DA*, +DD* enable 64-bit mode for the HP compiler # -q* compiler args for the IBM compiler # -m*, -t[45]*, -txscale* architecture-specific flags for GCC # -F/path path to uninstalled frameworks, gcc on darwin # -p, -pg, --coverage, -fprofile-* profiling flags for GCC # -fstack-protector* stack protector flags for GCC # @file GCC response files # -tp=* Portland pgcc target processor selection # --sysroot=* for sysroot support # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization # -specs=* GCC specs files # -stdlib=* select c++ std lib with clang # -fsanitize=* Clang/GCC memory and address sanitizer # -fuse-ld=* Linker select flags for GCC # -static-* direct GCC to link specific libraries statically # -fcilkplus Cilk Plus language extension features for C/C++ # -Wa,* Pass flags directly to the assembler -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \ -specs=*|-fsanitize=*|-fuse-ld=*|-static-*|-fcilkplus|-Wa,*) func_quote_arg pretty "$arg" arg=$func_quote_arg_result func_append compile_command " $arg" func_append finalize_command " $arg" func_append compiler_flags " $arg" continue ;; -Z*) if test os2 = "`expr $host : '.*\(os2\)'`"; then # OS/2 uses -Zxxx to specify OS/2-specific options compiler_flags="$compiler_flags $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case $arg in -Zlinker | -Zstack) prev=xcompiler ;; esac continue else # Otherwise treat like 'Some other compiler flag' below func_quote_arg pretty "$arg" arg=$func_quote_arg_result fi ;; # Some other compiler flag. -* | +*) func_quote_arg pretty "$arg" arg=$func_quote_arg_result ;; *.$objext) # A standard object. func_append objs " $arg" ;; *.lo) # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test none = "$pic_object" && test none = "$non_pic_object"; then func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result test none = "$pic_object" || { # Prepend the subdirectory the object is found in. pic_object=$xdir$pic_object if test dlfiles = "$prev"; then if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg=$pic_object } # Non-PIC object. if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test none = "$pic_object"; then arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "'$arg' is not a valid libtool object" fi fi ;; *.$libext) # An archive. func_append deplibs " $arg" func_append old_deplibs " $arg" continue ;; *.la) # A libtool-controlled library. func_resolve_sysroot "$arg" if test dlfiles = "$prev"; then # This library was specified with -dlopen. func_append dlfiles " $func_resolve_sysroot_result" prev= elif test dlprefiles = "$prev"; then # The library was specified with -dlpreopen. func_append dlprefiles " $func_resolve_sysroot_result" prev= else func_append deplibs " $func_resolve_sysroot_result" fi continue ;; # Some other compiler argument. *) # Unknown arguments in both finalize_command and compile_command need # to be aesthetically quoted because they are evaled later. func_quote_arg pretty "$arg" arg=$func_quote_arg_result ;; esac # arg # Now actually substitute the argument into the commands. if test -n "$arg"; then func_append compile_command " $arg" func_append finalize_command " $arg" fi done # argument parsing loop test -n "$prev" && \ func_fatal_help "the '$prevarg' option requires an argument" if test yes = "$export_dynamic" && test -n "$export_dynamic_flag_spec"; then eval arg=\"$export_dynamic_flag_spec\" func_append compile_command " $arg" func_append finalize_command " $arg" fi oldlibs= # calculate the name of the file, without its directory func_basename "$output" outputname=$func_basename_result libobjs_save=$libobjs if test -n "$shlibpath_var"; then # get the directories listed in $shlibpath_var eval shlib_search_path=\`\$ECHO \"\$$shlibpath_var\" \| \$SED \'s/:/ /g\'\` else shlib_search_path= fi eval sys_lib_search_path=\"$sys_lib_search_path_spec\" eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" # Definition is injected by LT_CONFIG during libtool generation. func_munge_path_list sys_lib_dlsearch_path "$LT_SYS_LIBRARY_PATH" func_dirname "$output" "/" "" output_objdir=$func_dirname_result$objdir func_to_tool_file "$output_objdir/" tool_output_objdir=$func_to_tool_file_result # Create the object directory. func_mkdir_p "$output_objdir" # Determine the type of output case $output in "") func_fatal_help "you must specify an output file" ;; *.$libext) linkmode=oldlib ;; *.lo | *.$objext) linkmode=obj ;; *.la) linkmode=lib ;; *) linkmode=prog ;; # Anything else should be a program. esac specialdeplibs= libs= # Find all interdependent deplibs by searching for libraries # that are linked more than once (e.g. -la -lb -la) for deplib in $deplibs; do if $opt_preserve_dup_deps; then case "$libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append libs " $deplib" done if test lib = "$linkmode"; then libs="$predeps $libs $compiler_lib_search_path $postdeps" # Compute libraries that are listed more than once in $predeps # $postdeps and mark them as special (i.e., whose duplicates are # not to be eliminated). pre_post_deps= if $opt_duplicate_compiler_generated_deps; then for pre_post_dep in $predeps $postdeps; do case "$pre_post_deps " in *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; esac func_append pre_post_deps " $pre_post_dep" done fi pre_post_deps= fi deplibs= newdependency_libs= newlib_search_path= need_relink=no # whether we're linking any uninstalled libtool libraries notinst_deplibs= # not-installed libtool libraries notinst_path= # paths that contain not-installed libtool libraries case $linkmode in lib) passes="conv dlpreopen link" for file in $dlfiles $dlprefiles; do case $file in *.la) ;; *) func_fatal_help "libraries can '-dlopen' only libtool libraries: $file" ;; esac done ;; prog) compile_deplibs= finalize_deplibs= alldeplibs=false newdlfiles= newdlprefiles= passes="conv scan dlopen dlpreopen link" ;; *) passes="conv" ;; esac for pass in $passes; do # The preopen pass in lib mode reverses $deplibs; put it back here # so that -L comes before libs that need it for instance... if test lib,link = "$linkmode,$pass"; then ## FIXME: Find the place where the list is rebuilt in the wrong ## order, and fix it there properly tmp_deplibs= for deplib in $deplibs; do tmp_deplibs="$deplib $tmp_deplibs" done deplibs=$tmp_deplibs fi if test lib,link = "$linkmode,$pass" || test prog,scan = "$linkmode,$pass"; then libs=$deplibs deplibs= fi if test prog = "$linkmode"; then case $pass in dlopen) libs=$dlfiles ;; dlpreopen) libs=$dlprefiles ;; link) libs="$deplibs %DEPLIBS%" test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" ;; esac fi if test lib,dlpreopen = "$linkmode,$pass"; then # Collect and forward deplibs of preopened libtool libs for lib in $dlprefiles; do # Ignore non-libtool-libs dependency_libs= func_resolve_sysroot "$lib" case $lib in *.la) func_source "$func_resolve_sysroot_result" ;; esac # Collect preopened libtool deplibs, except any this library # has declared as weak libs for deplib in $dependency_libs; do func_basename "$deplib" deplib_base=$func_basename_result case " $weak_libs " in *" $deplib_base "*) ;; *) func_append deplibs " $deplib" ;; esac done done libs=$dlprefiles fi if test dlopen = "$pass"; then # Collect dlpreopened libraries save_deplibs=$deplibs deplibs= fi for deplib in $libs; do lib= found=false case $deplib in -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append compiler_flags " $deplib" if test lib = "$linkmode"; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -l*) if test lib != "$linkmode" && test prog != "$linkmode"; then func_warning "'-l' is ignored for archives/objects" continue fi func_stripname '-l' '' "$deplib" name=$func_stripname_result if test lib = "$linkmode"; then searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" else searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" fi for searchdir in $searchdirs; do for search_ext in .la $std_shrext .so .a; do # Search the libtool library lib=$searchdir/lib$name$search_ext if test -f "$lib"; then if test .la = "$search_ext"; then found=: else found=false fi break 2 fi done done if $found; then # deplib is a libtool library # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, # We need to do some special things here, and not later. if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $deplib "*) if func_lalib_p "$lib"; then library_names= old_library= func_source "$lib" for l in $old_library $library_names; do ll=$l done if test "X$ll" = "X$old_library"; then # only static version available found=false func_dirname "$lib" "" "." ladir=$func_dirname_result lib=$ladir/$old_library if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" fi continue fi fi ;; *) ;; esac fi else # deplib doesn't seem to be a libtool library if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" fi continue fi ;; # -l *.ltframework) if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" if test lib = "$linkmode"; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -L*) case $linkmode in lib) deplibs="$deplib $deplibs" test conv = "$pass" && continue newdependency_libs="$deplib $newdependency_libs" func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; prog) if test conv = "$pass"; then deplibs="$deplib $deplibs" continue fi if test scan = "$pass"; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; *) func_warning "'-L' is ignored for archives/objects" ;; esac # linkmode continue ;; # -L -R*) if test link = "$pass"; then func_stripname '-R' '' "$deplib" func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # Make sure the xrpath contains only unique directories. case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac fi deplibs="$deplib $deplibs" continue ;; *.la) func_resolve_sysroot "$deplib" lib=$func_resolve_sysroot_result ;; *.$libext) if test conv = "$pass"; then deplibs="$deplib $deplibs" continue fi case $linkmode in lib) # Linking convenience modules into shared libraries is allowed, # but linking other static libraries is non-portable. case " $dlpreconveniencelibs " in *" $deplib "*) ;; *) valid_a_lib=false case $deplibs_check_method in match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ | $EGREP "$match_pattern_regex" > /dev/null; then valid_a_lib=: fi ;; pass_all) valid_a_lib=: ;; esac if $valid_a_lib; then echo $ECHO "*** Warning: Linking the shared library $output against the" $ECHO "*** static library $deplib is not portable!" deplibs="$deplib $deplibs" else echo $ECHO "*** Warning: Trying to link with static lib archive $deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because the file extensions .$libext of this argument makes me believe" echo "*** that it is just a static archive that I should not use here." fi ;; esac continue ;; prog) if test link != "$pass"; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi continue ;; esac # linkmode ;; # *.$libext *.lo | *.$objext) if test conv = "$pass"; then deplibs="$deplib $deplibs" elif test prog = "$linkmode"; then if test dlpreopen = "$pass" || test yes != "$dlopen_support" || test no = "$build_libtool_libs"; then # If there is no dlopen support or we're linking statically, # we need to preload. func_append newdlprefiles " $deplib" compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append newdlfiles " $deplib" fi fi continue ;; %DEPLIBS%) alldeplibs=: continue ;; esac # case $deplib $found || test -f "$lib" \ || func_fatal_error "cannot find the library '$lib' or unhandled argument '$deplib'" # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$lib" \ || func_fatal_error "'$lib' is not a valid libtool archive" func_dirname "$lib" "" "." ladir=$func_dirname_result dlname= dlopen= dlpreopen= libdir= library_names= old_library= inherited_linker_flags= # If the library was installed with an old release of libtool, # it will not redefine variables installed, or shouldnotlink installed=yes shouldnotlink=no avoidtemprpath= # Read the .la file func_source "$lib" # Convert "-framework foo" to "foo.ltframework" if test -n "$inherited_linker_flags"; then tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do case " $new_inherited_linker_flags " in *" $tmp_inherited_linker_flag "*) ;; *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; esac done fi dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` if test lib,link = "$linkmode,$pass" || test prog,scan = "$linkmode,$pass" || { test prog != "$linkmode" && test lib != "$linkmode"; }; then test -n "$dlopen" && func_append dlfiles " $dlopen" test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" fi if test conv = "$pass"; then # Only check for convenience libraries deplibs="$lib $deplibs" if test -z "$libdir"; then if test -z "$old_library"; then func_fatal_error "cannot find name of link library for '$lib'" fi # It is a libtool convenience library, so add in its objects. func_append convenience " $ladir/$objdir/$old_library" func_append old_convenience " $ladir/$objdir/$old_library" tmp_libs= for deplib in $dependency_libs; do deplibs="$deplib $deplibs" if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done elif test prog != "$linkmode" && test lib != "$linkmode"; then func_fatal_error "'$lib' is not a convenience library" fi continue fi # $pass = conv # Get the name of the library we link against. linklib= if test -n "$old_library" && { test yes = "$prefer_static_libs" || test built,no = "$prefer_static_libs,$installed"; }; then linklib=$old_library else for l in $old_library $library_names; do linklib=$l done fi if test -z "$linklib"; then func_fatal_error "cannot find name of link library for '$lib'" fi # This library was specified with -dlopen. if test dlopen = "$pass"; then test -z "$libdir" \ && func_fatal_error "cannot -dlopen a convenience library: '$lib'" if test -z "$dlname" || test yes != "$dlopen_support" || test no = "$build_libtool_libs" then # If there is no dlname, no dlopen support or we're linking # statically, we need to preload. We also need to preload any # dependent libraries so libltdl's deplib preloader doesn't # bomb out in the load deplibs phase. func_append dlprefiles " $lib $dependency_libs" else func_append newdlfiles " $lib" fi continue fi # $pass = dlopen # We need an absolute path. case $ladir in [\\/]* | [A-Za-z]:[\\/]*) abs_ladir=$ladir ;; *) abs_ladir=`cd "$ladir" && pwd` if test -z "$abs_ladir"; then func_warning "cannot determine absolute directory name of '$ladir'" func_warning "passing it literally to the linker, although it might fail" abs_ladir=$ladir fi ;; esac func_basename "$lib" laname=$func_basename_result # Find the relevant object directory and library name. if test yes = "$installed"; then if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then func_warning "library '$lib' was moved." dir=$ladir absdir=$abs_ladir libdir=$abs_ladir else dir=$lt_sysroot$libdir absdir=$lt_sysroot$libdir fi test yes = "$hardcode_automatic" && avoidtemprpath=yes else if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then dir=$ladir absdir=$abs_ladir # Remove this search path later func_append notinst_path " $abs_ladir" else dir=$ladir/$objdir absdir=$abs_ladir/$objdir # Remove this search path later func_append notinst_path " $abs_ladir" fi fi # $installed = yes func_stripname 'lib' '.la' "$laname" name=$func_stripname_result # This library was specified with -dlpreopen. if test dlpreopen = "$pass"; then if test -z "$libdir" && test prog = "$linkmode"; then func_fatal_error "only libraries may -dlpreopen a convenience library: '$lib'" fi case $host in # special handling for platforms with PE-DLLs. *cygwin* | *mingw* | *cegcc* ) # Linker will automatically link against shared library if both # static and shared are present. Therefore, ensure we extract # symbols from the import library if a shared library is present # (otherwise, the dlopen module name will be incorrect). We do # this by putting the import library name into $newdlprefiles. # We recover the dlopen module name by 'saving' the la file # name in a special purpose variable, and (later) extracting the # dlname from the la file. if test -n "$dlname"; then func_tr_sh "$dir/$linklib" eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" func_append newdlprefiles " $dir/$linklib" else func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" fi ;; * ) # Prefer using a static library (so that no silly _DYNAMIC symbols # are required to link). if test -n "$old_library"; then func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" # Otherwise, use the dlname, so that lt_dlopen finds it. elif test -n "$dlname"; then func_append newdlprefiles " $dir/$dlname" else func_append newdlprefiles " $dir/$linklib" fi ;; esac fi # $pass = dlpreopen if test -z "$libdir"; then # Link the convenience library if test lib = "$linkmode"; then deplibs="$dir/$old_library $deplibs" elif test prog,link = "$linkmode,$pass"; then compile_deplibs="$dir/$old_library $compile_deplibs" finalize_deplibs="$dir/$old_library $finalize_deplibs" else deplibs="$lib $deplibs" # used for prog,scan pass fi continue fi if test prog = "$linkmode" && test link != "$pass"; then func_append newlib_search_path " $ladir" deplibs="$lib $deplibs" linkalldeplibs=false if test no != "$link_all_deplibs" || test -z "$library_names" || test no = "$build_libtool_libs"; then linkalldeplibs=: fi tmp_libs= for deplib in $dependency_libs; do case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; esac # Need to link against all dependency_libs? if $linkalldeplibs; then deplibs="$deplib $deplibs" else # Need to hardcode shared library paths # or/and link against static libraries newdependency_libs="$deplib $newdependency_libs" fi if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done # for deplib continue fi # $linkmode = prog... if test prog,link = "$linkmode,$pass"; then if test -n "$library_names" && { { test no = "$prefer_static_libs" || test built,yes = "$prefer_static_libs,$installed"; } || test -z "$old_library"; }; then # We need to hardcode the library path if test -n "$shlibpath_var" && test -z "$avoidtemprpath"; then # Make sure the rpath contains only unique directories. case $temp_rpath: in *"$absdir:"*) ;; *) func_append temp_rpath "$absdir:" ;; esac fi # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi # $linkmode,$pass = prog,link... if $alldeplibs && { test pass_all = "$deplibs_check_method" || { test yes = "$build_libtool_libs" && test -n "$library_names"; }; }; then # We only need to search for static libraries continue fi fi link_static=no # Whether the deplib will be linked statically use_static_libs=$prefer_static_libs if test built = "$use_static_libs" && test yes = "$installed"; then use_static_libs=no fi if test -n "$library_names" && { test no = "$use_static_libs" || test -z "$old_library"; }; then case $host in *cygwin* | *mingw* | *cegcc* | *os2*) # No point in relinking DLLs because paths are not encoded func_append notinst_deplibs " $lib" need_relink=no ;; *) if test no = "$installed"; then func_append notinst_deplibs " $lib" need_relink=yes fi ;; esac # This is a shared library # Warn about portability, can't link against -module's on some # systems (darwin). Don't bleat about dlopened modules though! dlopenmodule= for dlpremoduletest in $dlprefiles; do if test "X$dlpremoduletest" = "X$lib"; then dlopenmodule=$dlpremoduletest break fi done if test -z "$dlopenmodule" && test yes = "$shouldnotlink" && test link = "$pass"; then echo if test prog = "$linkmode"; then $ECHO "*** Warning: Linking the executable $output against the loadable module" else $ECHO "*** Warning: Linking the shared library $output against the loadable module" fi $ECHO "*** $linklib is not portable!" fi if test lib = "$linkmode" && test yes = "$hardcode_into_libs"; then # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi if test -n "$old_archive_from_expsyms_cmds"; then # figure out the soname set dummy $library_names shift realname=$1 shift libname=`eval "\\$ECHO \"$libname_spec\""` # use dlname if we got it. it's perfectly good, no? if test -n "$dlname"; then soname=$dlname elif test -n "$soname_spec"; then # bleh windows case $host in *cygwin* | mingw* | *cegcc* | *os2*) func_arith $current - $age major=$func_arith_result versuffix=-$major ;; esac eval soname=\"$soname_spec\" else soname=$realname fi # Make a new name for the extract_expsyms_cmds to use soroot=$soname func_basename "$soroot" soname=$func_basename_result func_stripname 'lib' '.dll' "$soname" newlib=libimp-$func_stripname_result.a # If the library has no export list, then create one now if test -f "$output_objdir/$soname-def"; then : else func_verbose "extracting exported symbol list from '$soname'" func_execute_cmds "$extract_expsyms_cmds" 'exit $?' fi # Create $newlib if test -f "$output_objdir/$newlib"; then :; else func_verbose "generating import library for '$soname'" func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' fi # make sure the library variables are pointing to the new library dir=$output_objdir linklib=$newlib fi # test -n "$old_archive_from_expsyms_cmds" if test prog = "$linkmode" || test relink != "$opt_mode"; then add_shlibpath= add_dir= add= lib_linked=yes case $hardcode_action in immediate | unsupported) if test no = "$hardcode_direct"; then add=$dir/$linklib case $host in *-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;; *-*-sysv4*uw2*) add_dir=-L$dir ;; *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ *-*-unixware7*) add_dir=-L$dir ;; *-*-darwin* ) # if the lib is a (non-dlopened) module then we cannot # link against it, someone is ignoring the earlier warnings if /usr/bin/file -L $add 2> /dev/null | $GREP ": [^:]* bundle" >/dev/null; then if test "X$dlopenmodule" != "X$lib"; then $ECHO "*** Warning: lib $linklib is a module, not a shared library" if test -z "$old_library"; then echo echo "*** And there doesn't seem to be a static archive available" echo "*** The link will probably fail, sorry" else add=$dir/$old_library fi elif test -n "$old_library"; then add=$dir/$old_library fi fi esac elif test no = "$hardcode_minus_L"; then case $host in *-*-sunos*) add_shlibpath=$dir ;; esac add_dir=-L$dir add=-l$name elif test no = "$hardcode_shlibpath_var"; then add_shlibpath=$dir add=-l$name else lib_linked=no fi ;; relink) if test yes = "$hardcode_direct" && test no = "$hardcode_direct_absolute"; then add=$dir/$linklib elif test yes = "$hardcode_minus_L"; then add_dir=-L$absdir # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add=-l$name elif test yes = "$hardcode_shlibpath_var"; then add_shlibpath=$dir add=-l$name else lib_linked=no fi ;; *) lib_linked=no ;; esac if test yes != "$lib_linked"; then func_fatal_configuration "unsupported hardcode properties" fi if test -n "$add_shlibpath"; then case :$compile_shlibpath: in *":$add_shlibpath:"*) ;; *) func_append compile_shlibpath "$add_shlibpath:" ;; esac fi if test prog = "$linkmode"; then test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" test -n "$add" && compile_deplibs="$add $compile_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" if test yes != "$hardcode_direct" && test yes != "$hardcode_minus_L" && test yes = "$hardcode_shlibpath_var"; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac fi fi fi if test prog = "$linkmode" || test relink = "$opt_mode"; then add_shlibpath= add_dir= add= # Finalize command for both is simple: just hardcode it. if test yes = "$hardcode_direct" && test no = "$hardcode_direct_absolute"; then add=$libdir/$linklib elif test yes = "$hardcode_minus_L"; then add_dir=-L$libdir add=-l$name elif test yes = "$hardcode_shlibpath_var"; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac add=-l$name elif test yes = "$hardcode_automatic"; then if test -n "$inst_prefix_dir" && test -f "$inst_prefix_dir$libdir/$linklib"; then add=$inst_prefix_dir$libdir/$linklib else add=$libdir/$linklib fi else # We cannot seem to hardcode it, guess we'll fake it. add_dir=-L$libdir # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add=-l$name fi if test prog = "$linkmode"; then test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" test -n "$add" && finalize_deplibs="$add $finalize_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" fi fi elif test prog = "$linkmode"; then # Here we assume that one of hardcode_direct or hardcode_minus_L # is not unsupported. This is valid on all known static and # shared platforms. if test unsupported != "$hardcode_direct"; then test -n "$old_library" && linklib=$old_library compile_deplibs="$dir/$linklib $compile_deplibs" finalize_deplibs="$dir/$linklib $finalize_deplibs" else compile_deplibs="-l$name -L$dir $compile_deplibs" finalize_deplibs="-l$name -L$dir $finalize_deplibs" fi elif test yes = "$build_libtool_libs"; then # Not a shared library if test pass_all != "$deplibs_check_method"; then # We're trying link a shared library against a static one # but the system doesn't support it. # Just print a warning and add the library to dependency_libs so # that the program can be linked against the static library. echo $ECHO "*** Warning: This system cannot link to static lib archive $lib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have." if test yes = "$module"; then echo "*** But as you try to build a module library, libtool will still create " echo "*** a static module, that should work as long as the dlopening application" echo "*** is linked with the -dlopen flag to resolve symbols at runtime." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using 'nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** 'nm' from GNU binutils and a full rebuild may help." fi if test no = "$build_old_libs"; then build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi else deplibs="$dir/$old_library $deplibs" link_static=yes fi fi # link shared/static library? if test lib = "$linkmode"; then if test -n "$dependency_libs" && { test yes != "$hardcode_into_libs" || test yes = "$build_old_libs" || test yes = "$link_static"; }; then # Extract -R from dependency_libs temp_deplibs= for libdir in $dependency_libs; do case $libdir in -R*) func_stripname '-R' '' "$libdir" temp_xrpath=$func_stripname_result case " $xrpath " in *" $temp_xrpath "*) ;; *) func_append xrpath " $temp_xrpath";; esac;; *) func_append temp_deplibs " $libdir";; esac done dependency_libs=$temp_deplibs fi func_append newlib_search_path " $absdir" # Link against this library test no = "$link_static" && newdependency_libs="$abs_ladir/$laname $newdependency_libs" # ... and its dependency_libs tmp_libs= for deplib in $dependency_libs; do newdependency_libs="$deplib $newdependency_libs" case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result";; *) func_resolve_sysroot "$deplib" ;; esac if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $func_resolve_sysroot_result "*) func_append specialdeplibs " $func_resolve_sysroot_result" ;; esac fi func_append tmp_libs " $func_resolve_sysroot_result" done if test no != "$link_all_deplibs"; then # Add the search paths of all dependency libraries for deplib in $dependency_libs; do path= case $deplib in -L*) path=$deplib ;; *.la) func_resolve_sysroot "$deplib" deplib=$func_resolve_sysroot_result func_dirname "$deplib" "" "." dir=$func_dirname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) absdir=$dir ;; *) absdir=`cd "$dir" && pwd` if test -z "$absdir"; then func_warning "cannot determine absolute directory name of '$dir'" absdir=$dir fi ;; esac if $GREP "^installed=no" $deplib > /dev/null; then case $host in *-*-darwin*) depdepl= eval deplibrary_names=`$SED -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` if test -n "$deplibrary_names"; then for tmp in $deplibrary_names; do depdepl=$tmp done if test -f "$absdir/$objdir/$depdepl"; then depdepl=$absdir/$objdir/$depdepl darwin_install_name=`$OTOOL -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` if test -z "$darwin_install_name"; then darwin_install_name=`$OTOOL64 -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` fi func_append compiler_flags " $wl-dylib_file $wl$darwin_install_name:$depdepl" func_append linker_flags " -dylib_file $darwin_install_name:$depdepl" path= fi fi ;; *) path=-L$absdir/$objdir ;; esac else eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` test -z "$libdir" && \ func_fatal_error "'$deplib' is not a valid libtool archive" test "$absdir" != "$libdir" && \ func_warning "'$deplib' seems to be moved" path=-L$absdir fi ;; esac case " $deplibs " in *" $path "*) ;; *) deplibs="$path $deplibs" ;; esac done fi # link_all_deplibs != no fi # linkmode = lib done # for deplib in $libs if test link = "$pass"; then if test prog = "$linkmode"; then compile_deplibs="$new_inherited_linker_flags $compile_deplibs" finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" else compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` fi fi dependency_libs=$newdependency_libs if test dlpreopen = "$pass"; then # Link the dlpreopened libraries before other libraries for deplib in $save_deplibs; do deplibs="$deplib $deplibs" done fi if test dlopen != "$pass"; then test conv = "$pass" || { # Make sure lib_search_path contains only unique directories. lib_search_path= for dir in $newlib_search_path; do case "$lib_search_path " in *" $dir "*) ;; *) func_append lib_search_path " $dir" ;; esac done newlib_search_path= } if test prog,link = "$linkmode,$pass"; then vars="compile_deplibs finalize_deplibs" else vars=deplibs fi for var in $vars dependency_libs; do # Add libraries to $var in reverse order eval tmp_libs=\"\$$var\" new_libs= for deplib in $tmp_libs; do # FIXME: Pedantically, this is the right thing to do, so # that some nasty dependency loop isn't accidentally # broken: #new_libs="$deplib $new_libs" # Pragmatically, this seems to cause very few problems in # practice: case $deplib in -L*) new_libs="$deplib $new_libs" ;; -R*) ;; *) # And here is the reason: when a library appears more # than once as an explicit dependence of a library, or # is implicitly linked in more than once by the # compiler, it is considered special, and multiple # occurrences thereof are not removed. Compare this # with having the same library being listed as a # dependency of multiple other libraries: in this case, # we know (pedantically, we assume) the library does not # need to be listed more than once, so we keep only the # last copy. This is not always right, but it is rare # enough that we require users that really mean to play # such unportable linking tricks to link the library # using -Wl,-lname, so that libtool does not consider it # for duplicate removal. case " $specialdeplibs " in *" $deplib "*) new_libs="$deplib $new_libs" ;; *) case " $new_libs " in *" $deplib "*) ;; *) new_libs="$deplib $new_libs" ;; esac ;; esac ;; esac done tmp_libs= for deplib in $new_libs; do case $deplib in -L*) case " $tmp_libs " in *" $deplib "*) ;; *) func_append tmp_libs " $deplib" ;; esac ;; *) func_append tmp_libs " $deplib" ;; esac done eval $var=\"$tmp_libs\" done # for var fi # Add Sun CC postdeps if required: test CXX = "$tagname" && { case $host_os in linux*) case `$CC -V 2>&1 | $SED 5q` in *Sun\ C*) # Sun C++ 5.9 func_suncc_cstd_abi if test no != "$suncc_use_cstd_abi"; then func_append postdeps ' -library=Cstd -library=Crun' fi ;; esac ;; solaris*) func_cc_basename "$CC" case $func_cc_basename_result in CC* | sunCC*) func_suncc_cstd_abi if test no != "$suncc_use_cstd_abi"; then func_append postdeps ' -library=Cstd -library=Crun' fi ;; esac ;; esac } # Last step: remove runtime libs from dependency_libs # (they stay in deplibs) tmp_libs= for i in $dependency_libs; do case " $predeps $postdeps $compiler_lib_search_path " in *" $i "*) i= ;; esac if test -n "$i"; then func_append tmp_libs " $i" fi done dependency_libs=$tmp_libs done # for pass if test prog = "$linkmode"; then dlfiles=$newdlfiles fi if test prog = "$linkmode" || test lib = "$linkmode"; then dlprefiles=$newdlprefiles fi case $linkmode in oldlib) if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then func_warning "'-dlopen' is ignored for archives" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "'-l' and '-L' are ignored for archives" ;; esac test -n "$rpath" && \ func_warning "'-rpath' is ignored for archives" test -n "$xrpath" && \ func_warning "'-R' is ignored for archives" test -n "$vinfo" && \ func_warning "'-version-info/-version-number' is ignored for archives" test -n "$release" && \ func_warning "'-release' is ignored for archives" test -n "$export_symbols$export_symbols_regex" && \ func_warning "'-export-symbols' is ignored for archives" # Now set the variables for building old libraries. build_libtool_libs=no oldlibs=$output func_append objs "$old_deplibs" ;; lib) # Make sure we only generate libraries of the form 'libNAME.la'. case $outputname in lib*) func_stripname 'lib' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" ;; *) test no = "$module" \ && func_fatal_help "libtool library '$output' must begin with 'lib'" if test no != "$need_lib_prefix"; then # Add the "lib" prefix for modules if required func_stripname '' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" else func_stripname '' '.la' "$outputname" libname=$func_stripname_result fi ;; esac if test -n "$objs"; then if test pass_all != "$deplibs_check_method"; then func_fatal_error "cannot build libtool library '$output' from non-libtool objects on this host:$objs" else echo $ECHO "*** Warning: Linking the shared library $output against the non-libtool" $ECHO "*** objects $objs is not portable!" func_append libobjs " $objs" fi fi test no = "$dlself" \ || func_warning "'-dlopen self' is ignored for libtool libraries" set dummy $rpath shift test 1 -lt "$#" \ && func_warning "ignoring multiple '-rpath's for a libtool library" install_libdir=$1 oldlibs= if test -z "$rpath"; then if test yes = "$build_libtool_libs"; then # Building a libtool convenience library. # Some compilers have problems with a '.al' extension so # convenience libraries should have the same extension an # archive normally would. oldlibs="$output_objdir/$libname.$libext $oldlibs" build_libtool_libs=convenience build_old_libs=yes fi test -n "$vinfo" && \ func_warning "'-version-info/-version-number' is ignored for convenience libraries" test -n "$release" && \ func_warning "'-release' is ignored for convenience libraries" else # Parse the version information argument. save_ifs=$IFS; IFS=: set dummy $vinfo 0 0 0 shift IFS=$save_ifs test -n "$7" && \ func_fatal_help "too many parameters to '-version-info'" # convert absolute version numbers to libtool ages # this retains compatibility with .la files and attempts # to make the code below a bit more comprehensible case $vinfo_number in yes) number_major=$1 number_minor=$2 number_revision=$3 # # There are really only two kinds -- those that # use the current revision as the major version # and those that subtract age and use age as # a minor version. But, then there is irix # that has an extra 1 added just for fun # case $version_type in # correct linux to gnu/linux during the next big refactor darwin|freebsd-elf|linux|midnightbsd-elf|osf|windows|none) func_arith $number_major + $number_minor current=$func_arith_result age=$number_minor revision=$number_revision ;; freebsd-aout|qnx|sunos) current=$number_major revision=$number_minor age=0 ;; irix|nonstopux) func_arith $number_major + $number_minor current=$func_arith_result age=$number_minor revision=$number_minor lt_irix_increment=no ;; *) func_fatal_configuration "$modename: unknown library version type '$version_type'" ;; esac ;; no) current=$1 revision=$2 age=$3 ;; esac # Check that each of the things are valid numbers. case $current in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "CURRENT '$current' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac case $revision in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "REVISION '$revision' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac case $age in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "AGE '$age' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac if test "$age" -gt "$current"; then func_error "AGE '$age' is greater than the current interface number '$current'" func_fatal_error "'$vinfo' is not valid version information" fi # Calculate the version variables. major= versuffix= verstring= case $version_type in none) ;; darwin) # Like Linux, but with the current version available in # verstring for coding it into the library header func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision # Darwin ld doesn't like 0 for these options... func_arith $current + 1 minor_current=$func_arith_result xlcverstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" # On Darwin other compilers case $CC in nagfor*) verstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" ;; *) verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" ;; esac ;; freebsd-aout) major=.$current versuffix=.$current.$revision ;; freebsd-elf | midnightbsd-elf) func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision ;; irix | nonstopux) if test no = "$lt_irix_increment"; then func_arith $current - $age else func_arith $current - $age + 1 fi major=$func_arith_result case $version_type in nonstopux) verstring_prefix=nonstopux ;; *) verstring_prefix=sgi ;; esac verstring=$verstring_prefix$major.$revision # Add in all the interfaces that we are compatible with. loop=$revision while test 0 -ne "$loop"; do func_arith $revision - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring=$verstring_prefix$major.$iface:$verstring done # Before this point, $major must not contain '.'. major=.$major versuffix=$major.$revision ;; linux) # correct to gnu/linux during the next big refactor func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision ;; osf) func_arith $current - $age major=.$func_arith_result versuffix=.$current.$age.$revision verstring=$current.$age.$revision # Add in all the interfaces that we are compatible with. loop=$age while test 0 -ne "$loop"; do func_arith $current - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring=$verstring:$iface.0 done # Make executables depend on our current version. func_append verstring ":$current.0" ;; qnx) major=.$current versuffix=.$current ;; sco) major=.$current versuffix=.$current ;; sunos) major=.$current versuffix=.$current.$revision ;; windows) # Use '-' rather than '.', since we only want one # extension on DOS 8.3 file systems. func_arith $current - $age major=$func_arith_result versuffix=-$major ;; *) func_fatal_configuration "unknown library version type '$version_type'" ;; esac # Clear the version info if we defaulted, and they specified a release. if test -z "$vinfo" && test -n "$release"; then major= case $version_type in darwin) # we can't check for "0.0" in archive_cmds due to quoting # problems, so we reset it completely verstring= ;; *) verstring=0.0 ;; esac if test no = "$need_version"; then versuffix= else versuffix=.0.0 fi fi # Remove version info from name if versioning should be avoided if test yes,no = "$avoid_version,$need_version"; then major= versuffix= verstring= fi # Check to see if the archive will have undefined symbols. if test yes = "$allow_undefined"; then if test unsupported = "$allow_undefined_flag"; then if test yes = "$build_old_libs"; then func_warning "undefined symbols not allowed in $host shared libraries; building static only" build_libtool_libs=no else func_fatal_error "can't build $host shared library unless -no-undefined is specified" fi fi else # Don't allow undefined symbols. allow_undefined_flag=$no_undefined_flag fi fi func_generate_dlsyms "$libname" "$libname" : func_append libobjs " $symfileobj" test " " = "$libobjs" && libobjs= if test relink != "$opt_mode"; then # Remove our outputs, but don't remove object files since they # may have been created when compiling PIC objects. removelist= tempremovelist=`$ECHO "$output_objdir/*"` for p in $tempremovelist; do case $p in *.$objext | *.gcno) ;; $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/$libname$release.*) if test -n "$precious_files_regex"; then if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 then continue fi fi func_append removelist " $p" ;; *) ;; esac done test -n "$removelist" && \ func_show_eval "${RM}r \$removelist" fi # Now set the variables for building old libraries. if test yes = "$build_old_libs" && test convenience != "$build_libtool_libs"; then func_append oldlibs " $output_objdir/$libname.$libext" # Transform .lo files to .o files. oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; $lo2o" | $NL2SP` fi # Eliminate all temporary directories. #for path in $notinst_path; do # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"` #done if test -n "$xrpath"; then # If the user specified any rpath flags, then add them. temp_xrpath= for libdir in $xrpath; do func_replace_sysroot "$libdir" func_append temp_xrpath " -R$func_replace_sysroot_result" case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done if test yes != "$hardcode_into_libs" || test yes = "$build_old_libs"; then dependency_libs="$temp_xrpath $dependency_libs" fi fi # Make sure dlfiles contains only unique files that won't be dlpreopened old_dlfiles=$dlfiles dlfiles= for lib in $old_dlfiles; do case " $dlprefiles $dlfiles " in *" $lib "*) ;; *) func_append dlfiles " $lib" ;; esac done # Make sure dlprefiles contains only unique files old_dlprefiles=$dlprefiles dlprefiles= for lib in $old_dlprefiles; do case "$dlprefiles " in *" $lib "*) ;; *) func_append dlprefiles " $lib" ;; esac done if test yes = "$build_libtool_libs"; then if test -n "$rpath"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) # these systems don't actually have a c library (as such)! ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C library is in the System framework func_append deplibs " System.ltframework" ;; *-*-netbsd*) # Don't link with libc until the a.out ld.so is fixed. ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-midnightbsd*) # Do not include libc due to us having libc/libc_r. ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work ;; *) # Add libc to deplibs on all other systems if necessary. if test yes = "$build_libtool_need_lc"; then func_append deplibs " -lc" fi ;; esac fi # Transform deplibs into only deplibs that can be linked in shared. name_save=$name libname_save=$libname release_save=$release versuffix_save=$versuffix major_save=$major # I'm not sure if I'm treating the release correctly. I think # release should show up in the -l (ie -lgmp5) so we don't want to # add it in twice. Is that correct? release= versuffix= major= newdeplibs= droppeddeps=no case $deplibs_check_method in pass_all) # Don't check for shared/static. Everything works. # This might be a little naive. We might want to check # whether the library exists or not. But this is on # osf3 & osf4 and I'm not really sure... Just # implementing what was already the behavior. newdeplibs=$deplibs ;; test_compile) # This code stresses the "libraries are programs" paradigm to its # limits. Maybe even breaks it. We compile a program, linking it # against the deplibs as a proxy for the library. Then we can check # whether they linked in statically or dynamically with ldd. $opt_dry_run || $RM conftest.c cat > conftest.c </dev/null` $nocaseglob else potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` fi for potent_lib in $potential_libs; do # Follow soft links. if ls -lLd "$potent_lib" 2>/dev/null | $GREP " -> " >/dev/null; then continue fi # The statement above tries to avoid entering an # endless loop below, in case of cyclic links. # We might still enter an endless loop, since a link # loop can be closed while we follow links, # but so what? potlib=$potent_lib while test -h "$potlib" 2>/dev/null; do potliblink=`ls -ld $potlib | $SED 's/.* -> //'` case $potliblink in [\\/]* | [A-Za-z]:[\\/]*) potlib=$potliblink;; *) potlib=`$ECHO "$potlib" | $SED 's|[^/]*$||'`"$potliblink";; esac done if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | $SED -e 10q | $EGREP "$file_magic_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib= break 2 fi done done fi if test -n "$a_deplib"; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib"; then $ECHO "*** with $libname but no candidates were found. (...for file magic test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a file magic. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` for a_deplib in $deplibs; do case $a_deplib in -l*) func_stripname -l '' "$a_deplib" name=$func_stripname_result if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $a_deplib "*) func_append newdeplibs " $a_deplib" a_deplib= ;; esac fi if test -n "$a_deplib"; then libname=`eval "\\$ECHO \"$libname_spec\""` for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do potential_libs=`ls $i/$libname[.-]* 2>/dev/null` for potent_lib in $potential_libs; do potlib=$potent_lib # see symlink-check above in file_magic test if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ $EGREP "$match_pattern_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib= break 2 fi done done fi if test -n "$a_deplib"; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib"; then $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a regex pattern. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; none | unknown | *) newdeplibs= tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` if test yes = "$allow_libtool_libs_with_static_runtimes"; then for i in $predeps $postdeps; do # can't use Xsed below, because $i might contain '/' tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s|$i||"` done fi case $tmp_deplibs in *[!\ \ ]*) echo if test none = "$deplibs_check_method"; then echo "*** Warning: inter-library dependencies are not supported in this platform." else echo "*** Warning: inter-library dependencies are not known to be supported." fi echo "*** All declared inter-library dependencies are being dropped." droppeddeps=yes ;; esac ;; esac versuffix=$versuffix_save major=$major_save release=$release_save libname=$libname_save name=$name_save case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library with the System framework newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac if test yes = "$droppeddeps"; then if test yes = "$module"; then echo echo "*** Warning: libtool could not satisfy all declared inter-library" $ECHO "*** dependencies of module $libname. Therefore, libtool will create" echo "*** a static module, that should work as long as the dlopening" echo "*** application is linked with the -dlopen flag." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using 'nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** 'nm' from GNU binutils and a full rebuild may help." fi if test no = "$build_old_libs"; then oldlibs=$output_objdir/$libname.$libext build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi else echo "*** The inter-library dependencies that have been dropped here will be" echo "*** automatically added whenever a program is linked with this library" echo "*** or is declared to -dlopen it." if test no = "$allow_undefined"; then echo echo "*** Since this library must not contain undefined symbols," echo "*** because either the platform does not support them or" echo "*** it was explicitly requested with -no-undefined," echo "*** libtool will only create a static version of it." if test no = "$build_old_libs"; then oldlibs=$output_objdir/$libname.$libext build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi fi fi # Done checking deplibs! deplibs=$newdeplibs fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" case $host in *-*-darwin*) newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done deplibs=$new_libs # All the library-specific variables (install_libdir is set above). library_names= old_library= dlname= # Test again, we may have decided not to build it any more if test yes = "$build_libtool_libs"; then # Remove $wl instances when linking with ld. # FIXME: should test the right _cmds variable. case $archive_cmds in *\$LD\ *) wl= ;; esac if test yes = "$hardcode_into_libs"; then # Hardcode the library paths hardcode_libdirs= dep_rpath= rpath=$finalize_rpath test relink = "$opt_mode" || rpath=$compile_rpath$rpath for libdir in $rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then func_replace_sysroot "$libdir" libdir=$func_replace_sysroot_result if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append dep_rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" fi if test -n "$runpath_var" && test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" fi test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" fi shlibpath=$finalize_shlibpath test relink = "$opt_mode" || shlibpath=$compile_shlibpath$shlibpath if test -n "$shlibpath"; then eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" fi # Get the real and link names of the library. eval shared_ext=\"$shrext_cmds\" eval library_names=\"$library_names_spec\" set dummy $library_names shift realname=$1 shift if test -n "$soname_spec"; then eval soname=\"$soname_spec\" else soname=$realname fi if test -z "$dlname"; then dlname=$soname fi lib=$output_objdir/$realname linknames= for link do func_append linknames " $link" done # Use standard objects if they are pic test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` test "X$libobjs" = "X " && libobjs= delfiles= if test -n "$export_symbols" && test -n "$include_expsyms"; then $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" export_symbols=$output_objdir/$libname.uexp func_append delfiles " $export_symbols" fi orig_export_symbols= case $host_os in cygwin* | mingw* | cegcc*) if test -n "$export_symbols" && test -z "$export_symbols_regex"; then # exporting using user supplied symfile func_dll_def_p "$export_symbols" || { # and it's NOT already a .def file. Must figure out # which of the given symbols are data symbols and tag # them as such. So, trigger use of export_symbols_cmds. # export_symbols gets reassigned inside the "prepare # the list of exported symbols" if statement, so the # include_expsyms logic still works. orig_export_symbols=$export_symbols export_symbols= always_export_symbols=yes } fi ;; esac # Prepare the list of exported symbols if test -z "$export_symbols"; then if test yes = "$always_export_symbols" || test -n "$export_symbols_regex"; then func_verbose "generating symbol list for '$libname.la'" export_symbols=$output_objdir/$libname.exp $opt_dry_run || $RM $export_symbols cmds=$export_symbols_cmds save_ifs=$IFS; IFS='~' for cmd1 in $cmds; do IFS=$save_ifs # Take the normal branch if the nm_file_list_spec branch # doesn't work or if tool conversion is not needed. case $nm_file_list_spec~$to_tool_file_cmd in *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) try_normal_branch=yes eval cmd=\"$cmd1\" func_len " $cmd" len=$func_len_result ;; *) try_normal_branch=no ;; esac if test yes = "$try_normal_branch" \ && { test "$len" -lt "$max_cmd_len" \ || test "$max_cmd_len" -le -1; } then func_show_eval "$cmd" 'exit $?' skipped_export=false elif test -n "$nm_file_list_spec"; then func_basename "$output" output_la=$func_basename_result save_libobjs=$libobjs save_output=$output output=$output_objdir/$output_la.nm func_to_tool_file "$output" libobjs=$nm_file_list_spec$func_to_tool_file_result func_append delfiles " $output" func_verbose "creating $NM input file list: $output" for obj in $save_libobjs; do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > "$output" eval cmd=\"$cmd1\" func_show_eval "$cmd" 'exit $?' output=$save_output libobjs=$save_libobjs skipped_export=false else # The command line is too long to execute in one step. func_verbose "using reloadable object file for export list..." skipped_export=: # Break out early, otherwise skipped_export may be # set to false by a later but shorter cmd. break fi done IFS=$save_ifs if test -n "$export_symbols_regex" && test : != "$skipped_export"; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi fi if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols=$export_symbols test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test : != "$skipped_export" && test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for '$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands, which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi tmp_deplibs= for test_deplib in $deplibs; do case " $convenience " in *" $test_deplib "*) ;; *) func_append tmp_deplibs " $test_deplib" ;; esac done deplibs=$tmp_deplibs if test -n "$convenience"; then if test -n "$whole_archive_flag_spec" && test yes = "$compiler_needs_object" && test -z "$libobjs"; then # extract the archives, so we have objects to list. # TODO: could optimize this to just extract one archive. whole_archive_flag_spec= fi if test -n "$whole_archive_flag_spec"; then save_libobjs=$libobjs eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= else gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $convenience func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi fi if test yes = "$thread_safe" && test -n "$thread_safe_flag_spec"; then eval flag=\"$thread_safe_flag_spec\" func_append linker_flags " $flag" fi # Make a backup of the uninstalled library when relinking if test relink = "$opt_mode"; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? fi # Do each of the archive commands. if test yes = "$module" && test -n "$module_cmds"; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then eval test_cmds=\"$module_expsym_cmds\" cmds=$module_expsym_cmds else eval test_cmds=\"$module_cmds\" cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then eval test_cmds=\"$archive_expsym_cmds\" cmds=$archive_expsym_cmds else eval test_cmds=\"$archive_cmds\" cmds=$archive_cmds fi fi if test : != "$skipped_export" && func_len " $test_cmds" && len=$func_len_result && test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then : else # The command line is too long to link in one step, link piecewise # or, if using GNU ld and skipped_export is not :, use a linker # script. # Save the value of $output and $libobjs because we want to # use them later. If we have whole_archive_flag_spec, we # want to use save_libobjs as it was before # whole_archive_flag_spec was expanded, because we can't # assume the linker understands whole_archive_flag_spec. # This may have to be revisited, in case too many # convenience libraries get linked in and end up exceeding # the spec. if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then save_libobjs=$libobjs fi save_output=$output func_basename "$output" output_la=$func_basename_result # Clear the reloadable object creation command queue and # initialize k to one. test_cmds= concat_cmds= objlist= last_robj= k=1 if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then output=$output_objdir/$output_la.lnkscript func_verbose "creating GNU ld script: $output" echo 'INPUT (' > $output for obj in $save_libobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done echo ')' >> $output func_append delfiles " $output" func_to_tool_file "$output" output=$func_to_tool_file_result elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then output=$output_objdir/$output_la.lnk func_verbose "creating linker input file list: $output" : > $output set x $save_libobjs shift firstobj= if test yes = "$compiler_needs_object"; then firstobj="$1 " shift fi for obj do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done func_append delfiles " $output" func_to_tool_file "$output" output=$firstobj\"$file_list_spec$func_to_tool_file_result\" else if test -n "$save_libobjs"; then func_verbose "creating reloadable object files..." output=$output_objdir/$output_la-$k.$objext eval test_cmds=\"$reload_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 # Loop over the list of objects to be linked. for obj in $save_libobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result if test -z "$objlist" || test "$len" -lt "$max_cmd_len"; then func_append objlist " $obj" else # The command $test_cmds is almost too long, add a # command to the queue. if test 1 -eq "$k"; then # The first file doesn't have a previous command to add. reload_objs=$objlist eval concat_cmds=\"$reload_cmds\" else # All subsequent reloadable object files will link in # the last one created. reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" fi last_robj=$output_objdir/$output_la-$k.$objext func_arith $k + 1 k=$func_arith_result output=$output_objdir/$output_la-$k.$objext objlist=" $obj" func_len " $last_robj" func_arith $len0 + $func_len_result len=$func_arith_result fi done # Handle the remaining objects by creating one last # reloadable object file. All subsequent reloadable object # files will link in the last one created. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds$reload_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi func_append delfiles " $output" else output= fi ${skipped_export-false} && { func_verbose "generating symbol list for '$libname.la'" export_symbols=$output_objdir/$libname.exp $opt_dry_run || $RM $export_symbols libobjs=$output # Append the command to create the export file. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi } test -n "$save_libobjs" && func_verbose "creating a temporary reloadable object file: $output" # Loop through the commands generated above and execute them. save_ifs=$IFS; IFS='~' for cmd in $concat_cmds; do IFS=$save_ifs $opt_quiet || { func_quote_arg expand,pretty "$cmd" eval "func_echo $func_quote_arg_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test relink = "$opt_mode"; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS=$save_ifs if test -n "$export_symbols_regex" && ${skipped_export-false}; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi ${skipped_export-false} && { if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols=$export_symbols test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for '$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands, which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi } libobjs=$output # Restore the value of output. output=$save_output if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= fi # Expand the library linking commands again to reset the # value of $libobjs for piecewise linking. # Do each of the archive commands. if test yes = "$module" && test -n "$module_cmds"; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then cmds=$module_expsym_cmds else cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then cmds=$archive_expsym_cmds else cmds=$archive_cmds fi fi fi if test -n "$delfiles"; then # Append the command to remove temporary files to $cmds. eval cmds=\"\$cmds~\$RM $delfiles\" fi # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi save_ifs=$IFS; IFS='~' for cmd in $cmds; do IFS=$sp$nl eval cmd=\"$cmd\" IFS=$save_ifs $opt_quiet || { func_quote_arg expand,pretty "$cmd" eval "func_echo $func_quote_arg_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test relink = "$opt_mode"; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS=$save_ifs # Restore the uninstalled library and exit if test relink = "$opt_mode"; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? if test -n "$convenience"; then if test -z "$whole_archive_flag_spec"; then func_show_eval '${RM}r "$gentop"' fi fi exit $EXIT_SUCCESS fi # Create links to the real library. for linkname in $linknames; do if test "$realname" != "$linkname"; then func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' fi done # If -module or -export-dynamic was specified, set the dlname. if test yes = "$module" || test yes = "$export_dynamic"; then # On all known operating systems, these are identical. dlname=$soname fi fi ;; obj) if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then func_warning "'-dlopen' is ignored for objects" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "'-l' and '-L' are ignored for objects" ;; esac test -n "$rpath" && \ func_warning "'-rpath' is ignored for objects" test -n "$xrpath" && \ func_warning "'-R' is ignored for objects" test -n "$vinfo" && \ func_warning "'-version-info' is ignored for objects" test -n "$release" && \ func_warning "'-release' is ignored for objects" case $output in *.lo) test -n "$objs$old_deplibs" && \ func_fatal_error "cannot build library object '$output' from non-libtool objects" libobj=$output func_lo2o "$libobj" obj=$func_lo2o_result ;; *) libobj= obj=$output ;; esac # Delete the old objects. $opt_dry_run || $RM $obj $libobj # Objects from convenience libraries. This assumes # single-version convenience libraries. Whenever we create # different ones for PIC/non-PIC, this we'll have to duplicate # the extraction. reload_conv_objs= gentop= # if reload_cmds runs $LD directly, get rid of -Wl from # whole_archive_flag_spec and hope we can get by with turning comma # into space. case $reload_cmds in *\$LD[\ \$]*) wl= ;; esac if test -n "$convenience"; then if test -n "$whole_archive_flag_spec"; then eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" test -n "$wl" || tmp_whole_archive_flags=`$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` reload_conv_objs=$reload_objs\ $tmp_whole_archive_flags else gentop=$output_objdir/${obj}x func_append generated " $gentop" func_extract_archives $gentop $convenience reload_conv_objs="$reload_objs $func_extract_archives_result" fi fi # If we're not building shared, we need to use non_pic_objs test yes = "$build_libtool_libs" || libobjs=$non_pic_objects # Create the old-style object. reload_objs=$objs$old_deplibs' '`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; /\.lib$/d; $lo2o" | $NL2SP`' '$reload_conv_objs output=$obj func_execute_cmds "$reload_cmds" 'exit $?' # Exit if we aren't doing a library object file. if test -z "$libobj"; then if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS fi test yes = "$build_libtool_libs" || { if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi # Create an invalid libtool object if no PIC, so that we don't # accidentally link it into a program. # $show "echo timestamp > $libobj" # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? exit $EXIT_SUCCESS } if test -n "$pic_flag" || test default != "$pic_mode"; then # Only do commands if we really have different PIC objects. reload_objs="$libobjs $reload_conv_objs" output=$libobj func_execute_cmds "$reload_cmds" 'exit $?' fi if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS ;; prog) case $host in *cygwin*) func_stripname '' '.exe' "$output" output=$func_stripname_result.exe;; esac test -n "$vinfo" && \ func_warning "'-version-info' is ignored for programs" test -n "$release" && \ func_warning "'-release' is ignored for programs" $preload \ && test unknown,unknown,unknown = "$dlopen_support,$dlopen_self,$dlopen_self_static" \ && func_warning "'LT_INIT([dlopen])' not used. Assuming no dlopen support." case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library is the System framework compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac case $host in *-*-darwin*) # Don't allow lazy linking, it breaks C++ global constructors # But is supposedly fixed on 10.4 or later (yay!). if test CXX = "$tagname"; then case ${MACOSX_DEPLOYMENT_TARGET-10.0} in 10.[0123]) func_append compile_command " $wl-bind_at_load" func_append finalize_command " $wl-bind_at_load" ;; esac fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $compile_deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $compile_deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done compile_deplibs=$new_libs func_append compile_command " $compile_deplibs" func_append finalize_command " $finalize_deplibs" if test -n "$rpath$xrpath"; then # If the user specified any rpath flags, then add them. for libdir in $rpath $xrpath; do # This is the magic to use -rpath. case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done fi # Now hardcode the library paths rpath= hardcode_libdirs= for libdir in $compile_rpath $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$libdir" | $SED -e 's*/lib$*/bin*'` case :$dllsearchpath: in *":$libdir:"*) ;; ::) dllsearchpath=$libdir;; *) func_append dllsearchpath ":$libdir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval rpath=\" $hardcode_libdir_flag_spec\" fi compile_rpath=$rpath rpath= hardcode_libdirs= for libdir in $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$finalize_perm_rpath " in *" $libdir "*) ;; *) func_append finalize_perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval rpath=\" $hardcode_libdir_flag_spec\" fi finalize_rpath=$rpath if test -n "$libobjs" && test yes = "$build_old_libs"; then # Transform all the library objects into standard objects. compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` fi func_generate_dlsyms "$outputname" "@PROGRAM@" false # template prelinking step if test -n "$prelink_cmds"; then func_execute_cmds "$prelink_cmds" 'exit $?' fi wrappers_required=: case $host in *cegcc* | *mingw32ce*) # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. wrappers_required=false ;; *cygwin* | *mingw* ) test yes = "$build_libtool_libs" || wrappers_required=false ;; *) if test no = "$need_relink" || test yes != "$build_libtool_libs"; then wrappers_required=false fi ;; esac $wrappers_required || { # Replace the output file specification. compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` link_command=$compile_command$compile_rpath # We have no uninstalled library dependencies, so finalize right now. exit_status=0 func_show_eval "$link_command" 'exit_status=$?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Delete the generated files. if test -f "$output_objdir/${outputname}S.$objext"; then func_show_eval '$RM "$output_objdir/${outputname}S.$objext"' fi exit $exit_status } if test -n "$compile_shlibpath$finalize_shlibpath"; then compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" fi if test -n "$finalize_shlibpath"; then finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" fi compile_var= finalize_var= if test -n "$runpath_var"; then if test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done compile_var="$runpath_var=\"$rpath\$$runpath_var\" " fi if test -n "$finalize_perm_rpath"; then # We should set the runpath_var. rpath= for dir in $finalize_perm_rpath; do func_append rpath "$dir:" done finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " fi fi if test yes = "$no_install"; then # We don't need to create a wrapper script. link_command=$compile_var$compile_command$compile_rpath # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` # Delete the old output file. $opt_dry_run || $RM $output # Link the executable and exit func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi exit $EXIT_SUCCESS fi case $hardcode_action,$fast_install in relink,*) # Fast installation is not supported link_command=$compile_var$compile_command$compile_rpath relink_command=$finalize_var$finalize_command$finalize_rpath func_warning "this platform does not like uninstalled shared libraries" func_warning "'$output' will be relinked during installation" ;; *,yes) link_command=$finalize_var$compile_command$finalize_rpath relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` ;; *,no) link_command=$compile_var$compile_command$compile_rpath relink_command=$finalize_var$finalize_command$finalize_rpath ;; *,needless) link_command=$finalize_var$compile_command$finalize_rpath relink_command= ;; esac # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` # Delete the old output files. $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output_objdir/$outputname" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Now create the wrapper script. func_verbose "creating $output" # Quote the relink command for shipping. if test -n "$relink_command"; then # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_arg pretty "$var_value" relink_command="$var=$func_quote_arg_result; export $var; $relink_command" fi done func_quote eval cd "`pwd`" func_quote_arg pretty,unquoted "($func_quote_result; $relink_command)" relink_command=$func_quote_arg_unquoted_result fi # Only actually do things if not in dry run mode. $opt_dry_run || { # win32 will think the script is a binary if it has # a .exe suffix, so we strip it off here. case $output in *.exe) func_stripname '' '.exe' "$output" output=$func_stripname_result ;; esac # test for cygwin because mv fails w/o .exe extensions case $host in *cygwin*) exeext=.exe func_stripname '' '.exe' "$outputname" outputname=$func_stripname_result ;; *) exeext= ;; esac case $host in *cygwin* | *mingw* ) func_dirname_and_basename "$output" "" "." output_name=$func_basename_result output_path=$func_dirname_result cwrappersource=$output_path/$objdir/lt-$output_name.c cwrapper=$output_path/$output_name.exe $RM $cwrappersource $cwrapper trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 func_emit_cwrapperexe_src > $cwrappersource # The wrapper executable is built using the $host compiler, # because it contains $host paths and files. If cross- # compiling, it, like the target executable, must be # executed on the $host or under an emulation environment. $opt_dry_run || { $LTCC $LTCFLAGS -o $cwrapper $cwrappersource $STRIP $cwrapper } # Now, create the wrapper script for func_source use: func_ltwrapper_scriptname $cwrapper $RM $func_ltwrapper_scriptname_result trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 $opt_dry_run || { # note: this script will not be executed, so do not chmod. if test "x$build" = "x$host"; then $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result else func_emit_wrapper no > $func_ltwrapper_scriptname_result fi } ;; * ) $RM $output trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 func_emit_wrapper no > $output chmod +x $output ;; esac } exit $EXIT_SUCCESS ;; esac # See if we need to build an old-fashioned archive. for oldlib in $oldlibs; do case $build_libtool_libs in convenience) oldobjs="$libobjs_save $symfileobj" addlibs=$convenience build_libtool_libs=no ;; module) oldobjs=$libobjs_save addlibs=$old_convenience build_libtool_libs=no ;; *) oldobjs="$old_deplibs $non_pic_objects" $preload && test -f "$symfileobj" \ && func_append oldobjs " $symfileobj" addlibs=$old_convenience ;; esac if test -n "$addlibs"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $addlibs func_append oldobjs " $func_extract_archives_result" fi # Do each command in the archive commands. if test -n "$old_archive_from_new_cmds" && test yes = "$build_libtool_libs"; then cmds=$old_archive_from_new_cmds else # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append oldobjs " $func_extract_archives_result" fi # POSIX demands no paths to be encoded in archives. We have # to avoid creating archives with duplicate basenames if we # might have to extract them afterwards, e.g., when creating a # static archive out of a convenience library, or when linking # the entirety of a libtool archive into another (currently # not supported by libtool). if (for obj in $oldobjs do func_basename "$obj" $ECHO "$func_basename_result" done | sort | sort -uc >/dev/null 2>&1); then : else echo "copying selected object files to avoid basename conflicts..." gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_mkdir_p "$gentop" save_oldobjs=$oldobjs oldobjs= counter=1 for obj in $save_oldobjs do func_basename "$obj" objbase=$func_basename_result case " $oldobjs " in " ") oldobjs=$obj ;; *[\ /]"$objbase "*) while :; do # Make sure we don't pick an alternate name that also # overlaps. newobj=lt$counter-$objbase func_arith $counter + 1 counter=$func_arith_result case " $oldobjs " in *[\ /]"$newobj "*) ;; *) if test ! -f "$gentop/$newobj"; then break; fi ;; esac done func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" func_append oldobjs " $gentop/$newobj" ;; *) func_append oldobjs " $obj" ;; esac done fi func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result eval cmds=\"$old_archive_cmds\" func_len " $cmds" len=$func_len_result if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then cmds=$old_archive_cmds elif test -n "$archiver_list_spec"; then func_verbose "using command file archive linking..." for obj in $oldobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > $output_objdir/$libname.libcmd func_to_tool_file "$output_objdir/$libname.libcmd" oldobjs=" $archiver_list_spec$func_to_tool_file_result" cmds=$old_archive_cmds else # the command line is too long to link in one step, link in parts func_verbose "using piecewise archive linking..." save_RANLIB=$RANLIB RANLIB=: objlist= concat_cmds= save_oldobjs=$oldobjs oldobjs= # Is there a better way of finding the last object in the list? for obj in $save_oldobjs do last_oldobj=$obj done eval test_cmds=\"$old_archive_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 for obj in $save_oldobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result func_append objlist " $obj" if test "$len" -lt "$max_cmd_len"; then : else # the above command should be used before it gets too long oldobjs=$objlist if test "$obj" = "$last_oldobj"; then RANLIB=$save_RANLIB fi test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$old_archive_cmds\" objlist= len=$len0 fi done RANLIB=$save_RANLIB oldobjs=$objlist if test -z "$oldobjs"; then eval cmds=\"\$concat_cmds\" else eval cmds=\"\$concat_cmds~\$old_archive_cmds\" fi fi fi func_execute_cmds "$cmds" 'exit $?' done test -n "$generated" && \ func_show_eval "${RM}r$generated" # Now create the libtool archive. case $output in *.la) old_library= test yes = "$build_old_libs" && old_library=$libname.$libext func_verbose "creating $output" # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_arg pretty,unquoted "$var_value" relink_command="$var=$func_quote_arg_unquoted_result; export $var; $relink_command" fi done # Quote the link command for shipping. func_quote eval cd "`pwd`" relink_command="($func_quote_result; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" func_quote_arg pretty,unquoted "$relink_command" relink_command=$func_quote_arg_unquoted_result if test yes = "$hardcode_automatic"; then relink_command= fi # Only create the output if not a dry run. $opt_dry_run || { for installed in no yes; do if test yes = "$installed"; then if test -z "$install_libdir"; then break fi output=$output_objdir/${outputname}i # Replace all uninstalled libtool libraries with the installed ones newdependency_libs= for deplib in $dependency_libs; do case $deplib in *.la) func_basename "$deplib" name=$func_basename_result func_resolve_sysroot "$deplib" eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` test -z "$libdir" && \ func_fatal_error "'$deplib' is not a valid libtool archive" func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" ;; -L*) func_stripname -L '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -L$func_replace_sysroot_result" ;; -R*) func_stripname -R '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -R$func_replace_sysroot_result" ;; *) func_append newdependency_libs " $deplib" ;; esac done dependency_libs=$newdependency_libs newdlfiles= for lib in $dlfiles; do case $lib in *.la) func_basename "$lib" name=$func_basename_result eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "'$lib' is not a valid libtool archive" func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" ;; *) func_append newdlfiles " $lib" ;; esac done dlfiles=$newdlfiles newdlprefiles= for lib in $dlprefiles; do case $lib in *.la) # Only pass preopened files to the pseudo-archive (for # eventual linking with the app. that links it) if we # didn't already link the preopened objects directly into # the library: func_basename "$lib" name=$func_basename_result eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "'$lib' is not a valid libtool archive" func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" ;; esac done dlprefiles=$newdlprefiles else newdlfiles= for lib in $dlfiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlfiles " $abs" done dlfiles=$newdlfiles newdlprefiles= for lib in $dlprefiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlprefiles " $abs" done dlprefiles=$newdlprefiles fi $RM $output # place dlname in correct position for cygwin # In fact, it would be nice if we could use this code for all target # systems that can't hard-code library paths into their executables # and that have no shared library path variable independent of PATH, # but it turns out we can't easily determine that from inspecting # libtool variables, so we have to hard-code the OSs to which it # applies here; at the moment, that means platforms that use the PE # object format with DLL files. See the long comment at the top of # tests/bindir.at for full details. tdlname=$dlname case $host,$output,$installed,$module,$dlname in *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) # If a -bindir argument was supplied, place the dll there. if test -n "$bindir"; then func_relative_path "$install_libdir" "$bindir" tdlname=$func_relative_path_result/$dlname else # Otherwise fall back on heuristic. tdlname=../bin/$dlname fi ;; esac $ECHO > $output "\ # $outputname - a libtool library file # Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # Please DO NOT delete this file! # It is necessary for linking the library. # The name that we can dlopen(3). dlname='$tdlname' # Names of this library. library_names='$library_names' # The name of the static archive. old_library='$old_library' # Linker flags that cannot go in dependency_libs. inherited_linker_flags='$new_inherited_linker_flags' # Libraries that this one depends upon. dependency_libs='$dependency_libs' # Names of additional weak libraries provided by this library weak_library_names='$weak_libs' # Version information for $libname. current=$current age=$age revision=$revision # Is this an already installed library? installed=$installed # Should we warn about portability when linking against -modules? shouldnotlink=$module # Files to dlopen/dlpreopen dlopen='$dlfiles' dlpreopen='$dlprefiles' # Directory that this library needs to be installed in: libdir='$install_libdir'" if test no,yes = "$installed,$need_relink"; then $ECHO >> $output "\ relink_command=\"$relink_command\"" fi done } # Do a symbolic link so that the libtool archive can be found in # LD_LIBRARY_PATH before the program is installed. func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' ;; esac exit $EXIT_SUCCESS } if test link = "$opt_mode" || test relink = "$opt_mode"; then func_mode_link ${1+"$@"} fi # func_mode_uninstall arg... func_mode_uninstall () { $debug_cmd RM=$nonopt files= rmforce=false exit_status=0 # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic=$magic for arg do case $arg in -f) func_append RM " $arg"; rmforce=: ;; -*) func_append RM " $arg" ;; *) func_append files " $arg" ;; esac done test -z "$RM" && \ func_fatal_help "you must specify an RM program" rmdirs= for file in $files; do func_dirname "$file" "" "." dir=$func_dirname_result if test . = "$dir"; then odir=$objdir else odir=$dir/$objdir fi func_basename "$file" name=$func_basename_result test uninstall = "$opt_mode" && odir=$dir # Remember odir for removal later, being careful to avoid duplicates if test clean = "$opt_mode"; then case " $rmdirs " in *" $odir "*) ;; *) func_append rmdirs " $odir" ;; esac fi # Don't error if the file doesn't exist and rm -f was used. if { test -L "$file"; } >/dev/null 2>&1 || { test -h "$file"; } >/dev/null 2>&1 || test -f "$file"; then : elif test -d "$file"; then exit_status=1 continue elif $rmforce; then continue fi rmfiles=$file case $name in *.la) # Possibly a libtool archive, so verify it. if func_lalib_p "$file"; then func_source $dir/$name # Delete the libtool libraries and symlinks. for n in $library_names; do func_append rmfiles " $odir/$n" done test -n "$old_library" && func_append rmfiles " $odir/$old_library" case $opt_mode in clean) case " $library_names " in *" $dlname "*) ;; *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; esac test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" ;; uninstall) if test -n "$library_names"; then # Do each command in the postuninstall commands. func_execute_cmds "$postuninstall_cmds" '$rmforce || exit_status=1' fi if test -n "$old_library"; then # Do each command in the old_postuninstall commands. func_execute_cmds "$old_postuninstall_cmds" '$rmforce || exit_status=1' fi # FIXME: should reinstall the best remaining shared library. ;; esac fi ;; *.lo) # Possibly a libtool object, so verify it. if func_lalib_p "$file"; then # Read the .lo file func_source $dir/$name # Add PIC object to the list of files to remove. if test -n "$pic_object" && test none != "$pic_object"; then func_append rmfiles " $dir/$pic_object" fi # Add non-PIC object to the list of files to remove. if test -n "$non_pic_object" && test none != "$non_pic_object"; then func_append rmfiles " $dir/$non_pic_object" fi fi ;; *) if test clean = "$opt_mode"; then noexename=$name case $file in *.exe) func_stripname '' '.exe' "$file" file=$func_stripname_result func_stripname '' '.exe' "$name" noexename=$func_stripname_result # $file with .exe has already been added to rmfiles, # add $file without .exe func_append rmfiles " $file" ;; esac # Do a test to see if this is a libtool program. if func_ltwrapper_p "$file"; then if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" relink_command= func_source $func_ltwrapper_scriptname_result func_append rmfiles " $func_ltwrapper_scriptname_result" else relink_command= func_source $dir/$noexename fi # note $name still contains .exe if it was in $file originally # as does the version of $file that was added into $rmfiles func_append rmfiles " $odir/$name $odir/${name}S.$objext" if test yes = "$fast_install" && test -n "$relink_command"; then func_append rmfiles " $odir/lt-$name" fi if test "X$noexename" != "X$name"; then func_append rmfiles " $odir/lt-$noexename.c" fi fi fi ;; esac func_show_eval "$RM $rmfiles" 'exit_status=1' done # Try to remove the $objdir's in the directories where we deleted files for dir in $rmdirs; do if test -d "$dir"; then func_show_eval "rmdir $dir >/dev/null 2>&1" fi done exit $exit_status } if test uninstall = "$opt_mode" || test clean = "$opt_mode"; then func_mode_uninstall ${1+"$@"} fi test -z "$opt_mode" && { help=$generic_help func_fatal_help "you must specify a MODE" } test -z "$exec_cmd" && \ func_fatal_help "invalid operation mode '$opt_mode'" if test -n "$exec_cmd"; then eval exec "$exec_cmd" exit $EXIT_FAILURE fi exit $exit_status # The TAGs below are defined such that we never get into a situation # where we disable both kinds of libraries. Given conflicting # choices, we go for a static library, that is the most portable, # since we can't tell whether shared libraries were disabled because # the user asked for that or because the platform doesn't support # them. This is particularly important on AIX, because we don't # support having both static and shared libraries enabled at the same # time on that platform, so we default to a shared-only configuration. # If a disable-shared tag is given, we'll fallback to a static-only # configuration. But we'll never go from static-only to shared-only. # ### BEGIN LIBTOOL TAG CONFIG: disable-shared build_libtool_libs=no build_old_libs=yes # ### END LIBTOOL TAG CONFIG: disable-shared # ### BEGIN LIBTOOL TAG CONFIG: disable-static build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` # ### END LIBTOOL TAG CONFIG: disable-static # Local Variables: # mode:shell-script # sh-indentation:2 # End: nghttp2-1.68.0/PaxHeaders/contrib0000644000000000000000000000013215077107335013645 xustar0030 mtime=1761382109.790298386 30 atime=1761382109.794298375 30 ctime=1761382109.790298386 nghttp2-1.68.0/contrib/0000755000175100017510000000000015077107335014312 5ustar00runnerrunnernghttp2-1.68.0/contrib/PaxHeaders/nghttpx-upstart.conf.in0000644000000000000000000000013215077107270020370 xustar0030 mtime=1761382072.963444278 30 atime=1761382106.843307612 30 ctime=1761382109.788298392 nghttp2-1.68.0/contrib/nghttpx-upstart.conf.in0000644000175100017510000000017415077107270020762 0ustar00runnerrunner# vim: ft=upstart: description "HTTP/2 reverse proxy" start on runlevel [2] stop on runlevel [016] exec @bindir@/nghttpx nghttp2-1.68.0/contrib/PaxHeaders/nghttpx-logrotate0000644000000000000000000000013215077107270017335 xustar0030 mtime=1761382072.963444278 30 atime=1761382106.844307608 30 ctime=1761382109.789298389 nghttp2-1.68.0/contrib/nghttpx-logrotate0000644000175100017510000000033315077107270017724 0ustar00runnerrunner/var/log/nghttpx/*.log { weekly rotate 52 missingok compress delaycompress notifempty postrotate [ -s /var/run/nghttpx.pid ] && kill -USR1 `cat /var/run/nghttpx.pid` 2> /dev/null || true endscript } nghttp2-1.68.0/contrib/PaxHeaders/nghttpx.service.in0000644000000000000000000000013215077107270017403 xustar0030 mtime=1761382072.963444278 30 atime=1761382106.841307621 30 ctime=1761382109.787298395 nghttp2-1.68.0/contrib/nghttpx.service.in0000644000175100017510000000050715077107270017775 0ustar00runnerrunner[Unit] Description=HTTP/2 proxy Documentation=man:nghttpx After=network.target [Service] Type=notify ExecStart=@bindir@/nghttpx --conf=/etc/nghttpx/nghttpx.conf ExecReload=/bin/kill --signal HUP $MAINPID KillSignal=SIGQUIT PrivateTmp=yes ProtectHome=yes ProtectSystem=full Restart=always [Install] WantedBy=multi-user.target nghttp2-1.68.0/contrib/PaxHeaders/tlsticketupdate.go0000644000000000000000000000013215077107270017460 xustar0030 mtime=1761382072.963444278 30 atime=1761382106.845307603 30 ctime=1761382109.790298386 nghttp2-1.68.0/contrib/tlsticketupdate.go0000644000175100017510000000621515077107270020054 0ustar00runnerrunner// // nghttp2 - HTTP/2 C Library // // Copyright (c) 2015 Tatsuhiro Tsujikawa // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // package main import ( "bytes" "crypto/rand" "encoding/binary" "flag" "fmt" "log" "time" "github.com/bradfitz/gomemcache/memcache" ) func makeKey(len int) []byte { b := make([]byte, len) if _, err := rand.Read(b); err != nil { log.Fatalf("rand.Read: %v", err) } return b } func main() { var host = flag.String("host", "127.0.0.1", "memcached host") var port = flag.Int("port", 11211, "memcached port") var cipher = flag.String("cipher", "aes-128-cbc", "cipher for TLS ticket encryption") var interval = flag.Int("interval", 3600, "interval to update TLS ticket keys") flag.Parse() var keylen int switch *cipher { case "aes-128-cbc": keylen = 48 case "aes-256-cbc": keylen = 80 default: log.Fatalf("cipher: unknown cipher %v", cipher) } mc := memcache.New(fmt.Sprintf("%v:%v", *host, *port)) keys := [][]byte{ makeKey(keylen), // current encryption key makeKey(keylen), // next encryption key; now decryption only } for { buf := new(bytes.Buffer) if err := binary.Write(buf, binary.BigEndian, uint32(1)); err != nil { log.Fatalf("failed to write version: %v", err) } for _, key := range keys { if err := binary.Write(buf, binary.BigEndian, uint16(keylen)); err != nil { log.Fatalf("failed to write length: %v", err) } if _, err := buf.Write(key); err != nil { log.Fatalf("buf.Write: %v", err) } } mc.Set(&memcache.Item{ Key: "nghttpx:tls-ticket-key", Value: buf.Bytes(), Expiration: int32((*interval) + 300), }) <-time.After(time.Duration(*interval) * time.Second) // rotate keys. the last key is now encryption key. // generate new key and append it to the last, so that // we can at least decrypt TLS ticket encrypted by new // key on the host which does not get new key yet. // keep at most past 11 keys as decryption only key n := len(keys) + 1 if n > 13 { n = 13 } newKeys := make([][]byte, n) newKeys[0] = keys[len(keys)-1] copy(newKeys[1:], keys[0:n-2]) newKeys[n-1] = makeKey(keylen) keys = newKeys } } nghttp2-1.68.0/contrib/PaxHeaders/CMakeLists.txt0000644000000000000000000000013115077107270016457 xustar0030 mtime=1761382072.963444278 29 atime=1761382106.83930763 30 ctime=1761382109.784298404 nghttp2-1.68.0/contrib/CMakeLists.txt0000644000175100017510000000046215077107270017052 0ustar00runnerrunnerset(CONFIGFILES nghttpx-init nghttpx.service nghttpx-upstart.conf ) # Note that the execute permissions of nghttpx-init is preserved foreach(name IN LISTS CONFIGFILES) configure_file("${name}.in" "${name}" @ONLY) endforeach() # set(EXTRA_DIST ${CONFIGFILES} nghttpx-logrotate tlsticketupdate.go) nghttp2-1.68.0/contrib/PaxHeaders/Makefile.in0000644000000000000000000000013215077107305015764 xustar0030 mtime=1761382085.470387581 30 atime=1761382104.154319458 30 ctime=1761382109.783298407 nghttp2-1.68.0/contrib/Makefile.in0000644000175100017510000004045115077107305016360 0ustar00runnerrunner# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # nghttp2 - HTTP/2 C Library # Copyright (c) 2014 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = contrib ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPLDFLAGS = @APPLDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BPFCFLAGS = @BPFCFLAGS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CXX = @CXX@ CXX1XCXXFLAGS = @CXX1XCXXFLAGS@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXTRABPFCFLAGS = @EXTRABPFCFLAGS@ EXTRACFLAG = @EXTRACFLAG@ EXTRA_DEFS = @EXTRA_DEFS@ FGREP = @FGREP@ FILECMD = @FILECMD@ GREP = @GREP@ HAVE_CXX20 = @HAVE_CXX20@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ JANSSON_CFLAGS = @JANSSON_CFLAGS@ JANSSON_LIBS = @JANSSON_LIBS@ JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ JEMALLOC_LIBS = @JEMALLOC_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBBPF_CFLAGS = @LIBBPF_CFLAGS@ LIBBPF_LIBS = @LIBBPF_LIBS@ LIBBROTLIDEC_CFLAGS = @LIBBROTLIDEC_CFLAGS@ LIBBROTLIDEC_LIBS = @LIBBROTLIDEC_LIBS@ LIBBROTLIENC_CFLAGS = @LIBBROTLIENC_CFLAGS@ LIBBROTLIENC_LIBS = @LIBBROTLIENC_LIBS@ LIBCARES_CFLAGS = @LIBCARES_CFLAGS@ LIBCARES_LIBS = @LIBCARES_LIBS@ LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ LIBEV_CFLAGS = @LIBEV_CFLAGS@ LIBEV_LIBS = @LIBEV_LIBS@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@ LIBMRUBY_LIBS = @LIBMRUBY_LIBS@ LIBNGHTTP3_CFLAGS = @LIBNGHTTP3_CFLAGS@ LIBNGHTTP3_LIBS = @LIBNGHTTP3_LIBS@ LIBNGTCP2_CFLAGS = @LIBNGTCP2_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS = @LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_LIBS = @LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS = @LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS@ LIBNGTCP2_CRYPTO_LIBRESSL_LIBS = @LIBNGTCP2_CRYPTO_LIBRESSL_LIBS@ LIBNGTCP2_CRYPTO_OSSL_CFLAGS = @LIBNGTCP2_CRYPTO_OSSL_CFLAGS@ LIBNGTCP2_CRYPTO_OSSL_LIBS = @LIBNGTCP2_CRYPTO_OSSL_LIBS@ LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS = @LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ LIBNGTCP2_CRYPTO_QUICTLS_LIBS = @LIBNGTCP2_CRYPTO_QUICTLS_LIBS@ LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS = @LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS@ LIBNGTCP2_CRYPTO_WOLFSSL_LIBS = @LIBNGTCP2_CRYPTO_WOLFSSL_LIBS@ LIBNGTCP2_LIBS = @LIBNGTCP2_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBTOOL_LDFLAGS = @LIBTOOL_LDFLAGS@ LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ LIBXML2_LIBS = @LIBXML2_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_AGE = @LT_AGE@ LT_CURRENT = @LT_CURRENT@ LT_REVISION = @LT_REVISION@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ SYSTEMD_LIBS = @SYSTEMD_LIBS@ TESTLDADD = @TESTLDADD@ VERSION = @VERSION@ WARNCFLAGS = @WARNCFLAGS@ WARNCXXFLAGS = @WARNCXXFLAGS@ WOLFSSL_CFLAGS = @WOLFSSL_CFLAGS@ WOLFSSL_LIBS = @WOLFSSL_LIBS@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ configfiles = nghttpx-init nghttpx.service nghttpx-upstart.conf EXTRA_DIST = \ CMakeLists.txt \ $(configfiles:%=%.in) \ nghttpx-logrotate \ tlsticketupdate.go edit = sed -e 's|@bindir[@]|$(bindir)|g' all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu contrib/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu contrib/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile all-local installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-local mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: all all-am all-local check check-am clean clean-generic \ clean-libtool clean-local cscopelist-am ctags-am distclean \ distclean-generic distclean-libtool distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ uninstall-am .PRECIOUS: Makefile nghttpx-init: $(srcdir)/nghttpx-init.in rm -f $@ $@.tmp $(edit) $< > $@.tmp chmod +x $@.tmp mv $@.tmp $@ nghttpx.service: $(srcdir)/nghttpx.service.in $(edit) $< > $@ nghttpx-upstart.conf: $(srcdir)/nghttpx-upstart.conf.in $(edit) $< > $@ $(configfiles): Makefile all-local: $(configfiles) clean-local: -rm -f nghttpx-init.tmp $(configfiles) # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nghttp2-1.68.0/contrib/PaxHeaders/nghttpx-init.in0000644000000000000000000000013215077107270016705 xustar0030 mtime=1761382072.963444278 30 atime=1761382106.840307625 30 ctime=1761382109.785298401 nghttp2-1.68.0/contrib/nghttpx-init.in0000755000175100017510000001031515077107270017300 0ustar00runnerrunner#! /bin/sh ### BEGIN INIT INFO # Provides: nghttpx # Required-Start: $remote_fs $syslog # Required-Stop: $remote_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: nghttpx initscript # Description: nghttpx initscript ### END INIT INFO # Author: Tatsuhiro Tsujikawa # # Do NOT "set -e" # PATH should only include /usr/* if it runs after the mountnfs.sh script PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin DESC="HTTP/2 reverse proxy" NAME=nghttpx # Depending on the configuration, binary may be located under @sbindir@ DAEMON=@bindir@/$NAME PIDFILE=/var/run/$NAME.pid DAEMON_ARGS="--conf /etc/nghttpx/nghttpx.conf --pid-file=$PIDFILE --daemon" SCRIPTNAME=/etc/init.d/$NAME # Exit if the package is not installed [ -x "$DAEMON" ] || exit 0 # Read configuration variable file if it is present [ -r /etc/default/$NAME ] && . /etc/default/$NAME # Load the VERBOSE setting and other rcS variables . /lib/init/vars.sh # Define LSB log_* functions. # Depend on lsb-base (>= 3.2-14) to ensure that this file is present # and status_of_proc is working. . /lib/lsb/init-functions # # Function that starts the daemon/service # do_start() { # Return # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ || return 1 start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ $DAEMON_ARGS \ || return 2 # Add code here, if necessary, that waits for the process to be ready # to handle requests from services started subsequently which depend # on this one. As a last resort, sleep for some time. } # # Function that stops the daemon/service # do_stop() { # Return # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # other if a failure occurred start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE RETVAL="$?" [ "$RETVAL" = 2 ] && return 2 # Wait for children to finish too if this is a daemon that forks # and if the daemon is only ever run from this initscript. # If the above conditions are not satisfied then add some other code # that waits for the process to drop all resources that could be # needed by services started subsequently. A last resort is to # sleep for some time. #start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON #[ "$?" = 2 ] && return 2 # Many daemons don't delete their pidfiles when they exit. rm -f $PIDFILE return "$RETVAL" } case "$1" in start) [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" do_start case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; stop) [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" do_stop case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; status) status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? ;; upgrade) log_daemon_msg "Upgrading $DESC" "$NAME" oldpid=`pidofproc -p $PIDFILE $NAME` case "$?" in 0) log_progress_msg "Sending SIGUSR2 to $oldpid..." kill -USR2 $oldpid log_progress_msg "Waiting for new binary..." for i in 1 2 3 4 5 ; do sleep 1 newpid=`pidofproc -p $PIDFILE $NAME` if [ "$newpid" != "$oldpid" ] ; then break fi done if [ "$newpid" != "$oldpid" ] ; then log_progress_msg "Sending SIGQUIT to $oldpid..." kill -QUIT $oldpid log_end_msg 0 else log_progress_msg "New binary failed to start" log_end_msg 1 fi ;; *) log_progress_msg "pidofproc() failed" log_end_msg 1 ;; esac ;; restart|force-reload) # # If the "reload" option is implemented then remove the # 'force-reload' alias # log_daemon_msg "Restarting $DESC" "$NAME" do_stop case "$?" in 0|1) do_start case "$?" in 0) log_end_msg 0 ;; 1) log_end_msg 1 ;; # Old process is still running *) log_end_msg 1 ;; # Failed to start esac ;; *) # Failed to stop log_end_msg 1 ;; esac ;; *) echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload|upgrade}" >&2 exit 3 ;; esac : nghttp2-1.68.0/contrib/PaxHeaders/Makefile.am0000644000000000000000000000013215077107270015754 xustar0030 mtime=1761382072.963444278 30 atime=1761382085.460387624 30 ctime=1761382109.781298412 nghttp2-1.68.0/contrib/Makefile.am0000644000175100017510000000323115077107270016343 0ustar00runnerrunner# nghttp2 - HTTP/2 C Library # Copyright (c) 2014 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. configfiles = nghttpx-init nghttpx.service nghttpx-upstart.conf EXTRA_DIST = \ CMakeLists.txt \ $(configfiles:%=%.in) \ nghttpx-logrotate \ tlsticketupdate.go edit = sed -e 's|@bindir[@]|$(bindir)|g' nghttpx-init: $(srcdir)/nghttpx-init.in rm -f $@ $@.tmp $(edit) $< > $@.tmp chmod +x $@.tmp mv $@.tmp $@ nghttpx.service: $(srcdir)/nghttpx.service.in $(edit) $< > $@ nghttpx-upstart.conf: $(srcdir)/nghttpx-upstart.conf.in $(edit) $< > $@ $(configfiles): Makefile all-local: $(configfiles) clean-local: -rm -f nghttpx-init.tmp $(configfiles) nghttp2-1.68.0/PaxHeaders/examples0000644000000000000000000000013215077107335014023 xustar0030 mtime=1761382109.386299554 30 atime=1761382109.794298375 30 ctime=1761382109.386299554 nghttp2-1.68.0/examples/0000755000175100017510000000000015077107335014470 5ustar00runnerrunnernghttp2-1.68.0/examples/PaxHeaders/deflate.c0000644000000000000000000000013215077107270015646 xustar0030 mtime=1761382072.967444259 30 atime=1761382106.384309634 30 ctime=1761382109.382299566 nghttp2-1.68.0/examples/deflate.c0000644000175100017510000001365215077107270016245 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include #include #define NGHTTP2_NO_SSIZE_T #include #define MAKE_NV(K, V) \ { \ (uint8_t *)K, (uint8_t *)V, sizeof(K) - 1, \ sizeof(V) - 1, NGHTTP2_NV_FLAG_NONE, \ } static void deflate(nghttp2_hd_deflater *deflater, nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva, size_t nvlen); static int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in, size_t inlen, int final); int main(void) { int rv; nghttp2_hd_deflater *deflater; nghttp2_hd_inflater *inflater; /* Define 1st header set. This is looks like a HTTP request. */ nghttp2_nv nva1[] = { MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"), MAKE_NV(":path", "/"), MAKE_NV("user-agent", "libnghttp2"), MAKE_NV("accept-encoding", "gzip, deflate")}; /* Define 2nd header set */ nghttp2_nv nva2[] = {MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"), MAKE_NV(":path", "/stylesheet/style.css"), MAKE_NV("user-agent", "libnghttp2"), MAKE_NV("accept-encoding", "gzip, deflate"), MAKE_NV("referer", "https://example.org")}; rv = nghttp2_hd_deflate_new(&deflater, 4096); if (rv != 0) { fprintf(stderr, "nghttp2_hd_deflate_init failed with error: %s\n", nghttp2_strerror(rv)); exit(EXIT_FAILURE); } rv = nghttp2_hd_inflate_new(&inflater); if (rv != 0) { fprintf(stderr, "nghttp2_hd_inflate_init failed with error: %s\n", nghttp2_strerror(rv)); exit(EXIT_FAILURE); } /* Encode and decode 1st header set */ deflate(deflater, inflater, nva1, sizeof(nva1) / sizeof(nva1[0])); /* Encode and decode 2nd header set, using differential encoding using state after encoding 1st header set. */ deflate(deflater, inflater, nva2, sizeof(nva2) / sizeof(nva2[0])); nghttp2_hd_inflate_del(inflater); nghttp2_hd_deflate_del(deflater); return 0; } static void deflate(nghttp2_hd_deflater *deflater, nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva, size_t nvlen) { nghttp2_ssize rv; uint8_t *buf; size_t buflen; size_t outlen; size_t i; size_t sum; sum = 0; for (i = 0; i < nvlen; ++i) { sum += nva[i].namelen + nva[i].valuelen; } printf("Input (%zu byte(s)):\n\n", sum); for (i = 0; i < nvlen; ++i) { fwrite(nva[i].name, 1, nva[i].namelen, stdout); printf(": "); fwrite(nva[i].value, 1, nva[i].valuelen, stdout); printf("\n"); } buflen = nghttp2_hd_deflate_bound(deflater, nva, nvlen); buf = malloc(buflen); rv = nghttp2_hd_deflate_hd2(deflater, buf, buflen, nva, nvlen); if (rv < 0) { fprintf(stderr, "nghttp2_hd_deflate_hd2() failed with error: %s\n", nghttp2_strerror((int)rv)); free(buf); exit(EXIT_FAILURE); } outlen = (size_t)rv; printf("\nDeflate (%zu byte(s), ratio %.02f):\n\n", outlen, sum == 0 ? 0 : (double)outlen / (double)sum); for (i = 0; i < outlen; ++i) { if ((i & 0x0fu) == 0) { printf("%08zX: ", i); } printf("%02X ", buf[i]); if (((i + 1) & 0x0fu) == 0) { printf("\n"); } } printf("\n\nInflate:\n\n"); /* We pass 1 to final parameter, because buf contains whole deflated header data. */ rv = inflate_header_block(inflater, buf, outlen, 1); if (rv != 0) { free(buf); exit(EXIT_FAILURE); } printf("\n-----------------------------------------------------------" "--------------------\n"); free(buf); } int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in, size_t inlen, int final) { nghttp2_ssize rv; for (;;) { nghttp2_nv nv; int inflate_flags = 0; size_t proclen; rv = nghttp2_hd_inflate_hd3(inflater, &nv, &inflate_flags, in, inlen, final); if (rv < 0) { fprintf(stderr, "inflate failed with error code %td", rv); return -1; } proclen = (size_t)rv; in += proclen; inlen -= proclen; if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { fwrite(nv.name, 1, nv.namelen, stderr); fprintf(stderr, ": "); fwrite(nv.value, 1, nv.valuelen, stderr); fprintf(stderr, "\n"); } if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { nghttp2_hd_inflate_end_headers(inflater); break; } if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) { break; } } return 0; } nghttp2-1.68.0/examples/PaxHeaders/libevent-server.c0000644000000000000000000000013215077107270017356 xustar0030 mtime=1761382072.968444255 30 atime=1761382106.387309621 30 ctime=1761382109.385299557 nghttp2-1.68.0/examples/libevent-server.c0000644000175100017510000006004015077107270017746 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifdef __sgi # define errx(exitcode, format, args...) \ { \ warnx(format, ##args); \ exit(exitcode); \ } # define warn(format, args...) warnx(format ": %s", ##args, strerror(errno)) # define warnx(format, args...) fprintf(stderr, format "\n", ##args) #endif /* defined(__sgi) */ #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include #ifdef HAVE_SYS_SOCKET_H # include #endif /* defined(HAVE_SYS_SOCKET_H) */ #ifdef HAVE_NETDB_H # include #endif /* defined(HAVE_NETDB_H) */ #include #ifdef HAVE_UNISTD_H # include #endif /* defined(HAVE_UNISTD_H) */ #include #ifdef HAVE_FCNTL_H # include #endif /* defined(HAVE_FCNTL_H) */ #include #ifdef HAVE_NETINET_IN_H # include #endif /* defined(HAVE_NETINET_IN_H) */ #include #ifndef __sgi # include #endif /* !defined(__sgi) */ #include #include #include #include #include #include #include #include #include #define NGHTTP2_NO_SSIZE_T #include #define OUTPUT_WOULDBLOCK_THRESHOLD (1 << 16) #define ARRLEN(x) (sizeof(x) / sizeof(x[0])) #define MAKE_NV(NAME, VALUE) \ { \ (uint8_t *)NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, \ sizeof(VALUE) - 1, NGHTTP2_NV_FLAG_NONE, \ } struct app_context; typedef struct app_context app_context; typedef struct http2_stream_data { struct http2_stream_data *prev, *next; char *request_path; int32_t stream_id; int fd; } http2_stream_data; typedef struct http2_session_data { struct http2_stream_data root; struct bufferevent *bev; app_context *app_ctx; nghttp2_session *session; char *client_addr; } http2_session_data; struct app_context { SSL_CTX *ssl_ctx; struct event_base *evbase; }; static int alpn_select_proto_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { int rv; (void)ssl; (void)arg; rv = nghttp2_select_alpn(out, outlen, in, inlen); if (rv != 1) { return SSL_TLSEXT_ERR_NOACK; } return SSL_TLSEXT_ERR_OK; } /* Create SSL_CTX. */ static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) { SSL_CTX *ssl_ctx; ssl_ctx = SSL_CTX_new(TLS_server_method()); if (!ssl_ctx) { errx(1, "Could not create SSL/TLS context: %s", ERR_error_string(ERR_get_error(), NULL)); } SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); #if OPENSSL_VERSION_NUMBER >= 0x30000000L if (SSL_CTX_set1_groups_list(ssl_ctx, "P-256") != 1) { errx(1, "SSL_CTX_set1_groups_list failed: %s", ERR_error_string(ERR_get_error(), NULL)); } #else /* OPENSSL_VERSION_NUMBER < 0x30000000L */ { EC_KEY *ecdh; ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); if (!ecdh) { errx(1, "EC_KEY_new_by_curv_name failed: %s", ERR_error_string(ERR_get_error(), NULL)); } SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh); EC_KEY_free(ecdh); } #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */ if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) { errx(1, "Could not read private key file %s", key_file); } if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) { errx(1, "Could not read certificate file %s", cert_file); } SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, NULL); return ssl_ctx; } /* Create SSL object */ static SSL *create_ssl(SSL_CTX *ssl_ctx) { SSL *ssl; ssl = SSL_new(ssl_ctx); if (!ssl) { errx(1, "Could not create SSL/TLS session object: %s", ERR_error_string(ERR_get_error(), NULL)); } return ssl; } static void add_stream(http2_session_data *session_data, http2_stream_data *stream_data) { stream_data->next = session_data->root.next; session_data->root.next = stream_data; stream_data->prev = &session_data->root; if (stream_data->next) { stream_data->next->prev = stream_data; } } static void remove_stream(http2_session_data *session_data, http2_stream_data *stream_data) { (void)session_data; stream_data->prev->next = stream_data->next; if (stream_data->next) { stream_data->next->prev = stream_data->prev; } } static http2_stream_data * create_http2_stream_data(http2_session_data *session_data, int32_t stream_id) { http2_stream_data *stream_data; stream_data = malloc(sizeof(http2_stream_data)); memset(stream_data, 0, sizeof(http2_stream_data)); stream_data->stream_id = stream_id; stream_data->fd = -1; add_stream(session_data, stream_data); return stream_data; } static void delete_http2_stream_data(http2_stream_data *stream_data) { if (stream_data->fd != -1) { close(stream_data->fd); } free(stream_data->request_path); free(stream_data); } static http2_session_data *create_http2_session_data(app_context *app_ctx, int fd, struct sockaddr *addr, int addrlen) { int rv; http2_session_data *session_data; SSL *ssl; char host[NI_MAXHOST]; int val = 1; ssl = create_ssl(app_ctx->ssl_ctx); session_data = malloc(sizeof(http2_session_data)); memset(session_data, 0, sizeof(http2_session_data)); session_data->app_ctx = app_ctx; setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); session_data->bev = bufferevent_openssl_socket_new( app_ctx->evbase, fd, ssl, BUFFEREVENT_SSL_ACCEPTING, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS); bufferevent_enable(session_data->bev, EV_READ | EV_WRITE); rv = getnameinfo(addr, (socklen_t)addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST); if (rv != 0) { session_data->client_addr = strdup("(unknown)"); } else { session_data->client_addr = strdup(host); } return session_data; } static void delete_http2_session_data(http2_session_data *session_data) { http2_stream_data *stream_data; SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev); fprintf(stderr, "%s disconnected\n", session_data->client_addr); if (ssl) { SSL_shutdown(ssl); } bufferevent_free(session_data->bev); nghttp2_session_del(session_data->session); for (stream_data = session_data->root.next; stream_data;) { http2_stream_data *next = stream_data->next; delete_http2_stream_data(stream_data); stream_data = next; } free(session_data->client_addr); free(session_data); } /* Serialize the frame and send (or buffer) the data to bufferevent. */ static int session_send(http2_session_data *session_data) { int rv; rv = nghttp2_session_send(session_data->session); if (rv != 0) { warnx("Fatal error: %s", nghttp2_strerror(rv)); return -1; } return 0; } /* Read the data in the bufferevent and feed them into nghttp2 library function. Invocation of nghttp2_session_mem_recv2() may make additional pending frames, so call session_send() at the end of the function. */ static int session_recv(http2_session_data *session_data) { nghttp2_ssize readlen; struct evbuffer *input = bufferevent_get_input(session_data->bev); size_t datalen = evbuffer_get_length(input); unsigned char *data = evbuffer_pullup(input, -1); readlen = nghttp2_session_mem_recv2(session_data->session, data, datalen); if (readlen < 0) { warnx("Fatal error: %s", nghttp2_strerror((int)readlen)); return -1; } if (evbuffer_drain(input, (size_t)readlen) != 0) { warnx("Fatal error: evbuffer_drain failed"); return -1; } if (session_send(session_data) != 0) { return -1; } return 0; } static nghttp2_ssize send_callback(nghttp2_session *session, const uint8_t *data, size_t length, int flags, void *user_data) { http2_session_data *session_data = (http2_session_data *)user_data; struct bufferevent *bev = session_data->bev; (void)session; (void)flags; /* Avoid excessive buffering in server side. */ if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >= OUTPUT_WOULDBLOCK_THRESHOLD) { return NGHTTP2_ERR_WOULDBLOCK; } bufferevent_write(bev, data, length); return (nghttp2_ssize)length; } /* Returns nonzero if the string |s| ends with the substring |sub| */ static int ends_with(const char *s, const char *sub) { size_t slen = strlen(s); size_t sublen = strlen(sub); if (slen < sublen) { return 0; } return memcmp(s + slen - sublen, sub, sublen) == 0; } /* Returns int value of hex string character |c| */ static uint8_t hex_to_uint(uint8_t c) { if ('0' <= c && c <= '9') { return (uint8_t)(c - '0'); } if ('A' <= c && c <= 'F') { return (uint8_t)(c - 'A' + 10); } if ('a' <= c && c <= 'f') { return (uint8_t)(c - 'a' + 10); } return 0; } /* Decodes percent-encoded byte string |value| with length |valuelen| and returns the decoded byte string in allocated buffer. The return value is NULL terminated. The caller must free the returned string. */ static char *percent_decode(const uint8_t *value, size_t valuelen) { char *res; res = malloc(valuelen + 1); if (valuelen > 3) { size_t i, j; for (i = 0, j = 0; i < valuelen - 2;) { if (value[i] != '%' || !isxdigit(value[i + 1]) || !isxdigit(value[i + 2])) { res[j++] = (char)value[i++]; continue; } res[j++] = (char)((hex_to_uint(value[i + 1]) << 4) + hex_to_uint(value[i + 2])); i += 3; } memcpy(&res[j], &value[i], 2); res[j + 2] = '\0'; } else { memcpy(res, value, valuelen); res[valuelen] = '\0'; } return res; } static nghttp2_ssize file_read_callback(nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *user_data) { int fd = source->fd; ssize_t r; (void)session; (void)stream_id; (void)user_data; while ((r = read(fd, buf, length)) == -1 && errno == EINTR) ; if (r == -1) { return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } if (r == 0) { *data_flags |= NGHTTP2_DATA_FLAG_EOF; } return (nghttp2_ssize)r; } static int send_response(nghttp2_session *session, int32_t stream_id, nghttp2_nv *nva, size_t nvlen, int fd) { int rv; nghttp2_data_provider2 data_prd; data_prd.source.fd = fd; data_prd.read_callback = file_read_callback; rv = nghttp2_submit_response2(session, stream_id, nva, nvlen, &data_prd); if (rv != 0) { warnx("Fatal error: %s", nghttp2_strerror(rv)); return -1; } return 0; } static const char ERROR_HTML[] = "404" "

404 Not Found

"; static int error_reply(nghttp2_session *session, http2_stream_data *stream_data) { int rv; ssize_t writelen; int pipefd[2]; nghttp2_nv hdrs[] = {MAKE_NV(":status", "404")}; rv = pipe(pipefd); if (rv != 0) { warn("Could not create pipe"); rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_data->stream_id, NGHTTP2_INTERNAL_ERROR); if (rv != 0) { warnx("Fatal error: %s", nghttp2_strerror(rv)); return -1; } return 0; } writelen = write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1); close(pipefd[1]); if (writelen != sizeof(ERROR_HTML) - 1) { close(pipefd[0]); return -1; } stream_data->fd = pipefd[0]; if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), pipefd[0]) != 0) { close(pipefd[0]); return -1; } return 0; } /* nghttp2_on_header_callback: Called when nghttp2 library emits single header name/value pair. */ static int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *user_data) { http2_stream_data *stream_data; const char PATH[] = ":path"; (void)flags; (void)user_data; switch (frame->hd.type) { case NGHTTP2_HEADERS: if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) { break; } stream_data = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); if (!stream_data || stream_data->request_path) { break; } if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) { size_t j; for (j = 0; j < valuelen && value[j] != '?'; ++j) ; stream_data->request_path = percent_decode(value, j); } break; } return 0; } static int on_begin_headers_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { http2_session_data *session_data = (http2_session_data *)user_data; http2_stream_data *stream_data; if (frame->hd.type != NGHTTP2_HEADERS || frame->headers.cat != NGHTTP2_HCAT_REQUEST) { return 0; } stream_data = create_http2_stream_data(session_data, frame->hd.stream_id); nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, stream_data); return 0; } /* Minimum check for directory traversal. Returns nonzero if it is safe. */ static int check_path(const char *path) { /* We don't like '\' in url. */ return path[0] && path[0] == '/' && strchr(path, '\\') == NULL && strstr(path, "/../") == NULL && strstr(path, "/./") == NULL && !ends_with(path, "/..") && !ends_with(path, "/."); } static int on_request_recv(nghttp2_session *session, http2_session_data *session_data, http2_stream_data *stream_data) { int fd; nghttp2_nv hdrs[] = {MAKE_NV(":status", "200")}; char *rel_path; if (!stream_data->request_path) { if (error_reply(session, stream_data) != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } return 0; } fprintf(stderr, "%s GET %s\n", session_data->client_addr, stream_data->request_path); if (!check_path(stream_data->request_path)) { if (error_reply(session, stream_data) != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } return 0; } for (rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path) ; fd = open(rel_path, O_RDONLY); if (fd == -1) { if (error_reply(session, stream_data) != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } return 0; } stream_data->fd = fd; if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), fd) != 0) { close(fd); return NGHTTP2_ERR_CALLBACK_FAILURE; } return 0; } static int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { http2_session_data *session_data = (http2_session_data *)user_data; http2_stream_data *stream_data; switch (frame->hd.type) { case NGHTTP2_DATA: case NGHTTP2_HEADERS: /* Check that the client request has finished */ if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { stream_data = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); /* For DATA and HEADERS frame, this callback may be called after on_stream_close_callback. Check that stream still alive. */ if (!stream_data) { return 0; } return on_request_recv(session, session_data, stream_data); } break; default: break; } return 0; } static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *user_data) { http2_session_data *session_data = (http2_session_data *)user_data; http2_stream_data *stream_data; (void)error_code; stream_data = nghttp2_session_get_stream_user_data(session, stream_id); if (!stream_data) { return 0; } remove_stream(session_data, stream_data); delete_http2_stream_data(stream_data); return 0; } static void initialize_nghttp2_session(http2_session_data *session_data) { nghttp2_session_callbacks *callbacks; nghttp2_session_callbacks_new(&callbacks); nghttp2_session_callbacks_set_send_callback2(callbacks, send_callback); nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, on_frame_recv_callback); nghttp2_session_callbacks_set_on_stream_close_callback( callbacks, on_stream_close_callback); nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header_callback); nghttp2_session_callbacks_set_on_begin_headers_callback( callbacks, on_begin_headers_callback); nghttp2_session_server_new(&session_data->session, callbacks, session_data); nghttp2_session_callbacks_del(callbacks); } /* Send HTTP/2 client connection header, which includes 24 bytes magic octets and SETTINGS frame */ static int send_server_connection_header(http2_session_data *session_data) { nghttp2_settings_entry iv[1] = { {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; int rv; rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv, ARRLEN(iv)); if (rv != 0) { warnx("Fatal error: %s", nghttp2_strerror(rv)); return -1; } return 0; } /* readcb for bufferevent after client connection header was checked. */ static void readcb(struct bufferevent *bev, void *ptr) { http2_session_data *session_data = (http2_session_data *)ptr; (void)bev; if (session_recv(session_data) != 0) { delete_http2_session_data(session_data); return; } } /* writecb for bufferevent. To greaceful shutdown after sending or receiving GOAWAY, we check the some conditions on the nghttp2 library and output buffer of bufferevent. If it indicates we have no business to this session, tear down the connection. If the connection is not going to shutdown, we call session_send() to process pending data in the output buffer. This is necessary because we have a threshold on the buffer size to avoid too much buffering. See send_callback(). */ static void writecb(struct bufferevent *bev, void *ptr) { http2_session_data *session_data = (http2_session_data *)ptr; if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) { return; } if (nghttp2_session_want_read(session_data->session) == 0 && nghttp2_session_want_write(session_data->session) == 0) { delete_http2_session_data(session_data); return; } if (session_send(session_data) != 0) { delete_http2_session_data(session_data); return; } } /* eventcb for bufferevent */ static void eventcb(struct bufferevent *bev, short events, void *ptr) { http2_session_data *session_data = (http2_session_data *)ptr; if (events & BEV_EVENT_CONNECTED) { const unsigned char *alpn = NULL; unsigned int alpnlen = 0; SSL *ssl; (void)bev; fprintf(stderr, "%s connected\n", session_data->client_addr); ssl = bufferevent_openssl_get_ssl(session_data->bev); SSL_get0_alpn_selected(ssl, &alpn, &alpnlen); if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) { fprintf(stderr, "%s h2 is not negotiated\n", session_data->client_addr); delete_http2_session_data(session_data); return; } initialize_nghttp2_session(session_data); if (send_server_connection_header(session_data) != 0 || session_send(session_data) != 0) { delete_http2_session_data(session_data); return; } return; } if (events & BEV_EVENT_EOF) { fprintf(stderr, "%s EOF\n", session_data->client_addr); } else if (events & BEV_EVENT_ERROR) { fprintf(stderr, "%s network error\n", session_data->client_addr); } else if (events & BEV_EVENT_TIMEOUT) { fprintf(stderr, "%s timeout\n", session_data->client_addr); } delete_http2_session_data(session_data); } /* callback for evconnlistener */ static void acceptcb(struct evconnlistener *listener, int fd, struct sockaddr *addr, int addrlen, void *arg) { app_context *app_ctx = (app_context *)arg; http2_session_data *session_data; (void)listener; session_data = create_http2_session_data(app_ctx, fd, addr, addrlen); bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data); } static void start_listen(struct event_base *evbase, const char *service, app_context *app_ctx) { int rv; struct addrinfo hints; struct addrinfo *res, *rp; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; #ifdef AI_ADDRCONFIG hints.ai_flags |= AI_ADDRCONFIG; #endif /* defined(AI_ADDRCONFIG) */ rv = getaddrinfo(NULL, service, &hints, &res); if (rv != 0) { errx(1, "Could not resolve server address"); } for (rp = res; rp; rp = rp->ai_next) { struct evconnlistener *listener; listener = evconnlistener_new_bind( evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 16, rp->ai_addr, (int)rp->ai_addrlen); if (listener) { freeaddrinfo(res); return; } } errx(1, "Could not start listener"); } static void initialize_app_context(app_context *app_ctx, SSL_CTX *ssl_ctx, struct event_base *evbase) { memset(app_ctx, 0, sizeof(app_context)); app_ctx->ssl_ctx = ssl_ctx; app_ctx->evbase = evbase; } static void run(const char *service, const char *key_file, const char *cert_file) { SSL_CTX *ssl_ctx; app_context app_ctx; struct event_base *evbase; ssl_ctx = create_ssl_ctx(key_file, cert_file); evbase = event_base_new(); initialize_app_context(&app_ctx, ssl_ctx, evbase); start_listen(evbase, service, &app_ctx); event_base_loop(evbase, 0); event_base_free(evbase); SSL_CTX_free(ssl_ctx); } int main(int argc, char **argv) { struct sigaction act; if (argc < 4) { fprintf(stderr, "Usage: libevent-server PORT KEY_FILE CERT_FILE\n"); exit(EXIT_FAILURE); } memset(&act, 0, sizeof(struct sigaction)); act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, NULL); run(argv[1], argv[2], argv[3]); return 0; } nghttp2-1.68.0/examples/PaxHeaders/client.c0000644000000000000000000000013215077107270015520 xustar0030 mtime=1761382072.967444259 30 atime=1761382106.383309638 30 ctime=1761382109.381299569 nghttp2-1.68.0/examples/client.c0000644000175100017510000004671715077107270016127 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * This program is written to show how to use nghttp2 API in C and * intentionally made simple. */ #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include #include #ifdef HAVE_UNISTD_H # include #endif /* defined(HAVE_UNISTD_H) */ #ifdef HAVE_FCNTL_H # include #endif /* defined(HAVE_FCNTL_H) */ #include #ifdef HAVE_SYS_SOCKET_H # include #endif /* defined(HAVE_SYS_SOCKET_H) */ #ifdef HAVE_NETDB_H # include #endif /* defined(HAVE_NETDB_H) */ #ifdef HAVE_NETINET_IN_H # include #endif /* defined(HAVE_NETINET_IN_H) */ #include #include #include #include #include #include #include #define NGHTTP2_NO_SSIZE_T #include #include #include #include enum { IO_NONE, WANT_READ, WANT_WRITE }; #define MAKE_NV(NAME, VALUE) \ { \ (uint8_t *)NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, \ sizeof(VALUE) - 1, NGHTTP2_NV_FLAG_NONE, \ } #define MAKE_NV_CS(NAME, VALUE) \ { \ (uint8_t *)NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, \ strlen(VALUE), NGHTTP2_NV_FLAG_NONE, \ } struct Connection { SSL *ssl; nghttp2_session *session; /* WANT_READ if SSL/TLS connection needs more input; or WANT_WRITE if it needs more output; or IO_NONE. This is necessary because SSL/TLS re-negotiation is possible at any time. nghttp2 API offers similar functions like nghttp2_session_want_read() and nghttp2_session_want_write() but they do not take into account SSL/TSL connection. */ int want_io; }; struct Request { char *host; /* In this program, path contains query component as well. */ char *path; /* This is the concatenation of host and port with ":" in between. */ char *hostport; /* Stream ID for this request. */ int32_t stream_id; uint16_t port; }; struct URI { const char *host; /* In this program, path contains query component as well. */ const char *path; size_t pathlen; const char *hostport; size_t hostlen; size_t hostportlen; uint16_t port; }; /* * Returns copy of string |s| with the length |len|. The returned * string is NULL-terminated. */ static char *strcopy(const char *s, size_t len) { char *dst; dst = malloc(len + 1); memcpy(dst, s, len); dst[len] = '\0'; return dst; } /* * Prints error message |msg| and exit. */ NGHTTP2_NORETURN static void die(const char *msg) { fprintf(stderr, "FATAL: %s\n", msg); exit(EXIT_FAILURE); } /* * Prints error containing the function name |func| and message |msg| * and exit. */ NGHTTP2_NORETURN static void dief(const char *func, const char *msg) { fprintf(stderr, "FATAL: %s: %s\n", func, msg); exit(EXIT_FAILURE); } /* * Prints error containing the function name |func| and error code * |error_code| and exit. */ NGHTTP2_NORETURN static void diec(const char *func, int error_code) { fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code, nghttp2_strerror(error_code)); exit(EXIT_FAILURE); } /* * The implementation of nghttp2_send_callback2 type. Here we write * |data| with size |length| to the network and return the number of * bytes actually written. See the documentation of * nghttp2_send_callback for the details. */ static nghttp2_ssize send_callback(nghttp2_session *session, const uint8_t *data, size_t length, int flags, void *user_data) { struct Connection *connection; int rv; (void)session; (void)flags; connection = (struct Connection *)user_data; connection->want_io = IO_NONE; ERR_clear_error(); rv = SSL_write(connection->ssl, data, (int)length); if (rv <= 0) { int err = SSL_get_error(connection->ssl, rv); if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { connection->want_io = (err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE); rv = NGHTTP2_ERR_WOULDBLOCK; } else { rv = NGHTTP2_ERR_CALLBACK_FAILURE; } } return rv; } /* * The implementation of nghttp2_recv_callback2 type. Here we read * data from the network and write them in |buf|. The capacity of * |buf| is |length| bytes. Returns the number of bytes stored in * |buf|. See the documentation of nghttp2_recv_callback for the * details. */ static nghttp2_ssize recv_callback(nghttp2_session *session, uint8_t *buf, size_t length, int flags, void *user_data) { struct Connection *connection; int rv; (void)session; (void)flags; connection = (struct Connection *)user_data; connection->want_io = IO_NONE; ERR_clear_error(); rv = SSL_read(connection->ssl, buf, (int)length); if (rv < 0) { int err = SSL_get_error(connection->ssl, rv); if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { connection->want_io = (err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE); rv = NGHTTP2_ERR_WOULDBLOCK; } else { rv = NGHTTP2_ERR_CALLBACK_FAILURE; } } else if (rv == 0) { rv = NGHTTP2_ERR_EOF; } return rv; } static int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { size_t i; (void)user_data; switch (frame->hd.type) { case NGHTTP2_HEADERS: if (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) { const nghttp2_nv *nva = frame->headers.nva; printf("[INFO] C ----------------------------> S (HEADERS)\n"); for (i = 0; i < frame->headers.nvlen; ++i) { fwrite(nva[i].name, 1, nva[i].namelen, stdout); printf(": "); fwrite(nva[i].value, 1, nva[i].valuelen, stdout); printf("\n"); } } break; case NGHTTP2_RST_STREAM: printf("[INFO] C ----------------------------> S (RST_STREAM)\n"); break; case NGHTTP2_GOAWAY: printf("[INFO] C ----------------------------> S (GOAWAY)\n"); break; } return 0; } static int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { size_t i; (void)user_data; switch (frame->hd.type) { case NGHTTP2_HEADERS: if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE) { const nghttp2_nv *nva = frame->headers.nva; struct Request *req; req = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); if (req) { printf("[INFO] C <---------------------------- S (HEADERS)\n"); for (i = 0; i < frame->headers.nvlen; ++i) { fwrite(nva[i].name, 1, nva[i].namelen, stdout); printf(": "); fwrite(nva[i].value, 1, nva[i].valuelen, stdout); printf("\n"); } } } break; case NGHTTP2_RST_STREAM: printf("[INFO] C <---------------------------- S (RST_STREAM)\n"); break; case NGHTTP2_GOAWAY: printf("[INFO] C <---------------------------- S (GOAWAY)\n"); break; } return 0; } /* * The implementation of nghttp2_on_stream_close_callback type. We use * this function to know the response is fully received. Since we just * fetch 1 resource in this program, after reception of the response, * we submit GOAWAY and close the session. */ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *user_data) { struct Request *req; (void)error_code; (void)user_data; req = nghttp2_session_get_stream_user_data(session, stream_id); if (req) { int rv; rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); if (rv != 0) { diec("nghttp2_session_terminate_session", rv); } } return 0; } /* * The implementation of nghttp2_on_data_chunk_recv_callback type. We * use this function to print the received response body. */ static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data) { struct Request *req; (void)flags; (void)user_data; req = nghttp2_session_get_stream_user_data(session, stream_id); if (req) { printf("[INFO] C <---------------------------- S (DATA chunk)\n" "%lu bytes\n", (unsigned long int)len); fwrite(data, 1, len, stdout); printf("\n"); } return 0; } /* * Setup callback functions. nghttp2 API offers many callback * functions, but most of them are optional. The send_callback is * always required. Since we use nghttp2_session_recv(), the * recv_callback is also required. */ static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks) { nghttp2_session_callbacks_set_send_callback2(callbacks, send_callback); nghttp2_session_callbacks_set_recv_callback2(callbacks, recv_callback); nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, on_frame_send_callback); nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, on_frame_recv_callback); nghttp2_session_callbacks_set_on_stream_close_callback( callbacks, on_stream_close_callback); nghttp2_session_callbacks_set_on_data_chunk_recv_callback( callbacks, on_data_chunk_recv_callback); } /* * Setup SSL/TLS context. */ static void init_ssl_ctx(SSL_CTX *ssl_ctx) { /* Disable SSLv2 and enable all workarounds for buggy servers */ SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); SSL_CTX_set_alpn_protos(ssl_ctx, (const unsigned char *)"\x02h2", 3); } static void ssl_handshake(SSL *ssl, int fd) { int rv; if (SSL_set_fd(ssl, fd) == 0) { dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL)); } ERR_clear_error(); rv = SSL_connect(ssl); if (rv <= 0) { dief("SSL_connect", ERR_error_string(ERR_get_error(), NULL)); } } /* * Connects to the host |host| and port |port|. This function returns * the file descriptor of the client socket. */ static int connect_to(const char *host, uint16_t port) { struct addrinfo hints; int fd = -1; int rv; char service[NI_MAXSERV]; struct addrinfo *res, *rp; snprintf(service, sizeof(service), "%u", port); memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; rv = getaddrinfo(host, service, &hints, &res); if (rv != 0) { dief("getaddrinfo", gai_strerror(rv)); } for (rp = res; rp; rp = rp->ai_next) { fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (fd == -1) { continue; } while ((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 && errno == EINTR) ; if (rv == 0) { break; } close(fd); fd = -1; } freeaddrinfo(res); return fd; } static void make_non_block(int fd) { int flags, rv; while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR) ; if (flags == -1) { dief("fcntl", strerror(errno)); } while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR) ; if (rv == -1) { dief("fcntl", strerror(errno)); } } static void set_tcp_nodelay(int fd) { int val = 1; int rv; rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)); if (rv == -1) { dief("setsockopt", strerror(errno)); } } /* * Update |pollfd| based on the state of |connection|. */ static void ctl_poll(struct pollfd *pollfd, struct Connection *connection) { pollfd->events = 0; if (nghttp2_session_want_read(connection->session) || connection->want_io == WANT_READ) { pollfd->events |= POLLIN; } if (nghttp2_session_want_write(connection->session) || connection->want_io == WANT_WRITE) { pollfd->events |= POLLOUT; } } /* * Submits the request |req| to the connection |connection|. This * function does not send packets; just append the request to the * internal queue in |connection->session|. */ static void submit_request(struct Connection *connection, struct Request *req) { int32_t stream_id; /* Make sure that the last item is NULL */ const nghttp2_nv nva[] = {MAKE_NV(":method", "GET"), MAKE_NV_CS(":path", req->path), MAKE_NV(":scheme", "https"), MAKE_NV_CS(":authority", req->hostport), MAKE_NV("accept", "*/*"), MAKE_NV("user-agent", "nghttp2/" NGHTTP2_VERSION)}; stream_id = nghttp2_submit_request2(connection->session, NULL, nva, sizeof(nva) / sizeof(nva[0]), NULL, req); if (stream_id < 0) { diec("nghttp2_submit_request", stream_id); } req->stream_id = stream_id; printf("[INFO] Stream ID = %d\n", stream_id); } /* * Performs the network I/O. */ static void exec_io(struct Connection *connection) { int rv; rv = nghttp2_session_recv(connection->session); if (rv != 0) { diec("nghttp2_session_recv", rv); } rv = nghttp2_session_send(connection->session); if (rv != 0) { diec("nghttp2_session_send", rv); } } static void request_init(struct Request *req, const struct URI *uri) { req->host = strcopy(uri->host, uri->hostlen); req->port = uri->port; req->path = strcopy(uri->path, uri->pathlen); req->hostport = strcopy(uri->hostport, uri->hostportlen); req->stream_id = -1; } static void request_free(struct Request *req) { free(req->host); free(req->path); free(req->hostport); } /* * Fetches the resource denoted by |uri|. */ static void fetch_uri(const struct URI *uri) { nghttp2_session_callbacks *callbacks; int fd; SSL_CTX *ssl_ctx; SSL *ssl; struct Request req; struct Connection connection; int rv; nfds_t npollfds = 1; struct pollfd pollfds[1]; request_init(&req, uri); /* Establish connection and setup SSL */ fd = connect_to(req.host, req.port); if (fd == -1) { die("Could not open file descriptor"); } ssl_ctx = SSL_CTX_new(TLS_client_method()); if (ssl_ctx == NULL) { dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL)); } init_ssl_ctx(ssl_ctx); ssl = SSL_new(ssl_ctx); if (ssl == NULL) { dief("SSL_new", ERR_error_string(ERR_get_error(), NULL)); } /* To simplify the program, we perform SSL/TLS handshake in blocking I/O. */ ssl_handshake(ssl, fd); connection.ssl = ssl; connection.want_io = IO_NONE; /* Here make file descriptor non-block */ make_non_block(fd); set_tcp_nodelay(fd); printf("[INFO] SSL/TLS handshake completed\n"); rv = nghttp2_session_callbacks_new(&callbacks); if (rv != 0) { diec("nghttp2_session_callbacks_new", rv); } setup_nghttp2_callbacks(callbacks); rv = nghttp2_session_client_new(&connection.session, callbacks, &connection); nghttp2_session_callbacks_del(callbacks); if (rv != 0) { diec("nghttp2_session_client_new", rv); } rv = nghttp2_submit_settings(connection.session, NGHTTP2_FLAG_NONE, NULL, 0); if (rv != 0) { diec("nghttp2_submit_settings", rv); } /* Submit the HTTP request to the outbound queue. */ submit_request(&connection, &req); pollfds[0].fd = fd; ctl_poll(pollfds, &connection); /* Event loop */ while (nghttp2_session_want_read(connection.session) || nghttp2_session_want_write(connection.session)) { int nfds = poll(pollfds, npollfds, -1); if (nfds == -1) { dief("poll", strerror(errno)); } if (pollfds[0].revents & (POLLIN | POLLOUT)) { exec_io(&connection); } if ((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) { die("Connection error"); } ctl_poll(pollfds, &connection); } /* Resource cleanup */ nghttp2_session_del(connection.session); SSL_shutdown(ssl); SSL_free(ssl); SSL_CTX_free(ssl_ctx); shutdown(fd, SHUT_WR); close(fd); request_free(&req); } static int parse_uri(struct URI *res, const char *uri) { /* We only interested in https */ size_t len, i, offset; int ipv6addr = 0; memset(res, 0, sizeof(struct URI)); len = strlen(uri); if (len < 9 || memcmp("https://", uri, 8) != 0) { return -1; } offset = 8; res->host = res->hostport = &uri[offset]; res->hostlen = 0; if (uri[offset] == '[') { /* IPv6 literal address */ ++offset; ++res->host; ipv6addr = 1; for (i = offset; i < len; ++i) { if (uri[i] == ']') { res->hostlen = i - offset; offset = i + 1; break; } } } else { const char delims[] = ":/?#"; for (i = offset; i < len; ++i) { if (strchr(delims, uri[i]) != NULL) { break; } } res->hostlen = i - offset; offset = i; } if (res->hostlen == 0) { return -1; } /* Assuming https */ res->port = 443; if (offset < len) { if (uri[offset] == ':') { /* port */ const char delims[] = "/?#"; int port = 0; ++offset; for (i = offset; i < len; ++i) { if (strchr(delims, uri[i]) != NULL) { break; } if ('0' <= uri[i] && uri[i] <= '9') { port *= 10; port += uri[i] - '0'; if (port > 65535) { return -1; } } else { return -1; } } if (port == 0) { return -1; } offset = i; res->port = (uint16_t)port; } } res->hostportlen = (size_t)(uri + offset + ipv6addr - res->host); for (i = offset; i < len; ++i) { if (uri[i] == '#') { break; } } if (i - offset == 0) { res->path = "/"; res->pathlen = 1; } else { res->path = &uri[offset]; res->pathlen = i - offset; } return 0; } int main(int argc, char **argv) { struct URI uri; struct sigaction act; int rv; if (argc < 2) { die("Specify a https URI"); } memset(&act, 0, sizeof(struct sigaction)); act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, 0); rv = parse_uri(&uri, argv[1]); if (rv != 0) { die("parse_uri failed"); } fetch_uri(&uri); return EXIT_SUCCESS; } nghttp2-1.68.0/examples/PaxHeaders/CMakeLists.txt0000644000000000000000000000013215077107270016636 xustar0030 mtime=1761382072.967444259 30 atime=1761382106.388309616 30 ctime=1761382109.386299554 nghttp2-1.68.0/examples/CMakeLists.txt0000644000175100017510000000205715077107270017232 0ustar00runnerrunnerif(ENABLE_EXAMPLES) file(GLOB c_sources *.c) set_source_files_properties(${c_sources} PROPERTIES COMPILE_FLAGS "${WARNCFLAGS}") file(GLOB cxx_sources *.cc) set_source_files_properties(${cxx_sources} PROPERTIES COMPILE_FLAGS "${WARNCXXFLAGS} ${CXX1XCXXFLAGS}") include_directories( ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/../third-party/urlparse" "${CMAKE_CURRENT_SOURCE_DIR}/../third-party/llhttp/include" ${LIBEVENT_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS} ) link_libraries( nghttp2 ${LIBEVENT_OPENSSL_LIBRARIES} ${OPENSSL_LIBRARIES} ${APP_LIBRARIES} ) add_executable(client client.c $ $ ) add_executable(libevent-client libevent-client.c $ $ ) add_executable(libevent-server libevent-server.c $ $ ) add_executable(deflate deflate.c $ $ ) endif() nghttp2-1.68.0/examples/PaxHeaders/Makefile.in0000644000000000000000000000013215077107305016142 xustar0030 mtime=1761382085.550387238 30 atime=1761382103.919320493 30 ctime=1761382109.379299575 nghttp2-1.68.0/examples/Makefile.in0000644000175100017510000006246415077107305016546 0ustar00runnerrunner# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # nghttp2 - HTTP/2 C Library # Copyright (c) 2012 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ @ENABLE_EXAMPLES_TRUE@noinst_PROGRAMS = client$(EXEEXT) \ @ENABLE_EXAMPLES_TRUE@ libevent-client$(EXEEXT) \ @ENABLE_EXAMPLES_TRUE@ libevent-server$(EXEEXT) \ @ENABLE_EXAMPLES_TRUE@ deflate$(EXEEXT) subdir = examples ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = PROGRAMS = $(noinst_PROGRAMS) am__client_SOURCES_DIST = client.c @ENABLE_EXAMPLES_TRUE@am_client_OBJECTS = client.$(OBJEXT) client_OBJECTS = $(am_client_OBJECTS) client_LDADD = $(LDADD) @ENABLE_EXAMPLES_TRUE@client_DEPENDENCIES = \ @ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \ @ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/liburlparse.la AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am__deflate_SOURCES_DIST = deflate.c @ENABLE_EXAMPLES_TRUE@am_deflate_OBJECTS = deflate.$(OBJEXT) deflate_OBJECTS = $(am_deflate_OBJECTS) deflate_LDADD = $(LDADD) @ENABLE_EXAMPLES_TRUE@deflate_DEPENDENCIES = \ @ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \ @ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/liburlparse.la am__libevent_client_SOURCES_DIST = libevent-client.c @ENABLE_EXAMPLES_TRUE@am_libevent_client_OBJECTS = \ @ENABLE_EXAMPLES_TRUE@ libevent-client.$(OBJEXT) libevent_client_OBJECTS = $(am_libevent_client_OBJECTS) libevent_client_LDADD = $(LDADD) @ENABLE_EXAMPLES_TRUE@libevent_client_DEPENDENCIES = \ @ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \ @ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/liburlparse.la am__libevent_server_SOURCES_DIST = libevent-server.c @ENABLE_EXAMPLES_TRUE@am_libevent_server_OBJECTS = \ @ENABLE_EXAMPLES_TRUE@ libevent-server.$(OBJEXT) libevent_server_OBJECTS = $(am_libevent_server_OBJECTS) libevent_server_LDADD = $(LDADD) @ENABLE_EXAMPLES_TRUE@libevent_server_DEPENDENCIES = \ @ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \ @ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/liburlparse.la AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/client.Po ./$(DEPDIR)/deflate.Po \ ./$(DEPDIR)/libevent-client.Po ./$(DEPDIR)/libevent-server.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(client_SOURCES) $(deflate_SOURCES) \ $(libevent_client_SOURCES) $(libevent_server_SOURCES) DIST_SOURCES = $(am__client_SOURCES_DIST) $(am__deflate_SOURCES_DIST) \ $(am__libevent_client_SOURCES_DIST) \ $(am__libevent_server_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPLDFLAGS = @APPLDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BPFCFLAGS = @BPFCFLAGS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CXX = @CXX@ CXX1XCXXFLAGS = @CXX1XCXXFLAGS@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXTRABPFCFLAGS = @EXTRABPFCFLAGS@ EXTRACFLAG = @EXTRACFLAG@ EXTRA_DEFS = @EXTRA_DEFS@ FGREP = @FGREP@ FILECMD = @FILECMD@ GREP = @GREP@ HAVE_CXX20 = @HAVE_CXX20@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ JANSSON_CFLAGS = @JANSSON_CFLAGS@ JANSSON_LIBS = @JANSSON_LIBS@ JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ JEMALLOC_LIBS = @JEMALLOC_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBBPF_CFLAGS = @LIBBPF_CFLAGS@ LIBBPF_LIBS = @LIBBPF_LIBS@ LIBBROTLIDEC_CFLAGS = @LIBBROTLIDEC_CFLAGS@ LIBBROTLIDEC_LIBS = @LIBBROTLIDEC_LIBS@ LIBBROTLIENC_CFLAGS = @LIBBROTLIENC_CFLAGS@ LIBBROTLIENC_LIBS = @LIBBROTLIENC_LIBS@ LIBCARES_CFLAGS = @LIBCARES_CFLAGS@ LIBCARES_LIBS = @LIBCARES_LIBS@ LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ LIBEV_CFLAGS = @LIBEV_CFLAGS@ LIBEV_LIBS = @LIBEV_LIBS@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@ LIBMRUBY_LIBS = @LIBMRUBY_LIBS@ LIBNGHTTP3_CFLAGS = @LIBNGHTTP3_CFLAGS@ LIBNGHTTP3_LIBS = @LIBNGHTTP3_LIBS@ LIBNGTCP2_CFLAGS = @LIBNGTCP2_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS = @LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_LIBS = @LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS = @LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS@ LIBNGTCP2_CRYPTO_LIBRESSL_LIBS = @LIBNGTCP2_CRYPTO_LIBRESSL_LIBS@ LIBNGTCP2_CRYPTO_OSSL_CFLAGS = @LIBNGTCP2_CRYPTO_OSSL_CFLAGS@ LIBNGTCP2_CRYPTO_OSSL_LIBS = @LIBNGTCP2_CRYPTO_OSSL_LIBS@ LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS = @LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ LIBNGTCP2_CRYPTO_QUICTLS_LIBS = @LIBNGTCP2_CRYPTO_QUICTLS_LIBS@ LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS = @LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS@ LIBNGTCP2_CRYPTO_WOLFSSL_LIBS = @LIBNGTCP2_CRYPTO_WOLFSSL_LIBS@ LIBNGTCP2_LIBS = @LIBNGTCP2_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBTOOL_LDFLAGS = @LIBTOOL_LDFLAGS@ LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ LIBXML2_LIBS = @LIBXML2_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_AGE = @LT_AGE@ LT_CURRENT = @LT_CURRENT@ LT_REVISION = @LT_REVISION@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ SYSTEMD_LIBS = @SYSTEMD_LIBS@ TESTLDADD = @TESTLDADD@ VERSION = @VERSION@ WARNCFLAGS = @WARNCFLAGS@ WARNCXXFLAGS = @WARNCXXFLAGS@ WOLFSSL_CFLAGS = @WOLFSSL_CFLAGS@ WOLFSSL_LIBS = @WOLFSSL_LIBS@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ EXTRA_DIST = CMakeLists.txt @ENABLE_EXAMPLES_TRUE@AM_CFLAGS = $(WARNCFLAGS) @ENABLE_EXAMPLES_TRUE@AM_CXXFLAGS = $(WARNCXXFLAGS) $(CXX1XCXXFLAGS) @ENABLE_EXAMPLES_TRUE@AM_CPPFLAGS = \ @ENABLE_EXAMPLES_TRUE@ -I$(top_srcdir)/lib/includes \ @ENABLE_EXAMPLES_TRUE@ -I$(top_builddir)/lib/includes \ @ENABLE_EXAMPLES_TRUE@ -I$(top_srcdir)/third-party/urlparse \ @ENABLE_EXAMPLES_TRUE@ @LIBEVENT_OPENSSL_CFLAGS@ \ @ENABLE_EXAMPLES_TRUE@ @OPENSSL_CFLAGS@ \ @ENABLE_EXAMPLES_TRUE@ @DEFS@ @ENABLE_EXAMPLES_TRUE@AM_LDFLAGS = @LIBTOOL_LDFLAGS@ @ENABLE_EXAMPLES_TRUE@LDADD = $(top_builddir)/lib/libnghttp2.la \ @ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/liburlparse.la \ @ENABLE_EXAMPLES_TRUE@ @LIBEVENT_OPENSSL_LIBS@ \ @ENABLE_EXAMPLES_TRUE@ @OPENSSL_LIBS@ \ @ENABLE_EXAMPLES_TRUE@ @APPLDFLAGS@ @ENABLE_EXAMPLES_TRUE@client_SOURCES = client.c @ENABLE_EXAMPLES_TRUE@libevent_client_SOURCES = libevent-client.c @ENABLE_EXAMPLES_TRUE@libevent_server_SOURCES = libevent-server.c @ENABLE_EXAMPLES_TRUE@deflate_SOURCES = deflate.c all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu examples/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu examples/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list client$(EXEEXT): $(client_OBJECTS) $(client_DEPENDENCIES) $(EXTRA_client_DEPENDENCIES) @rm -f client$(EXEEXT) $(AM_V_CCLD)$(LINK) $(client_OBJECTS) $(client_LDADD) $(LIBS) deflate$(EXEEXT): $(deflate_OBJECTS) $(deflate_DEPENDENCIES) $(EXTRA_deflate_DEPENDENCIES) @rm -f deflate$(EXEEXT) $(AM_V_CCLD)$(LINK) $(deflate_OBJECTS) $(deflate_LDADD) $(LIBS) libevent-client$(EXEEXT): $(libevent_client_OBJECTS) $(libevent_client_DEPENDENCIES) $(EXTRA_libevent_client_DEPENDENCIES) @rm -f libevent-client$(EXEEXT) $(AM_V_CCLD)$(LINK) $(libevent_client_OBJECTS) $(libevent_client_LDADD) $(LIBS) libevent-server$(EXEEXT): $(libevent_server_OBJECTS) $(libevent_server_DEPENDENCIES) $(EXTRA_libevent_server_DEPENDENCIES) @rm -f libevent-server$(EXEEXT) $(AM_V_CCLD)$(LINK) $(libevent_server_OBJECTS) $(libevent_server_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/deflate.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libevent-client.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libevent-server.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -f ./$(DEPDIR)/client.Po -rm -f ./$(DEPDIR)/deflate.Po -rm -f ./$(DEPDIR)/libevent-client.Po -rm -f ./$(DEPDIR)/libevent-server.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/client.Po -rm -f ./$(DEPDIR)/deflate.Po -rm -f ./$(DEPDIR)/libevent-client.Po -rm -f ./$(DEPDIR)/libevent-server.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ clean-generic clean-libtool clean-noinstPROGRAMS cscopelist-am \ ctags ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nghttp2-1.68.0/examples/PaxHeaders/libevent-client.c0000644000000000000000000000013215077107270017326 xustar0030 mtime=1761382072.968444255 30 atime=1761382106.385309629 30 ctime=1761382109.383299563 nghttp2-1.68.0/examples/libevent-client.c0000644000175100017510000005005415077107270017722 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifdef __sgi # include # define errx(exitcode, format, args...) \ { \ warnx(format, ##args); \ exit(exitcode); \ } # define warnx(format, args...) fprintf(stderr, format "\n", ##args) char *strndup(const char *s, size_t size); #endif /* defined(__sgi) */ #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include #ifdef HAVE_UNISTD_H # include #endif /* defined(HAVE_UNISTD_H) */ #ifdef HAVE_SYS_SOCKET_H # include #endif /* defined(HAVE_SYS_SOCKET_H) */ #ifdef HAVE_NETINET_IN_H # include #endif /* defined(HAVE_NETINET_IN_H) */ #include #ifndef __sgi # include #endif /* !defined(__sgi) */ #include #include #include #include #include #include #include #include #include #define NGHTTP2_NO_SSIZE_T #include #include "urlparse.h" #define ARRLEN(x) (sizeof(x) / sizeof(x[0])) typedef struct { /* The NULL-terminated URI string to retrieve. */ const char *uri; /* Parsed result of the |uri| */ urlparse_url *u; /* The authority portion of the |uri|, not NULL-terminated */ char *authority; /* The path portion of the |uri|, including query, not NULL-terminated */ char *path; /* The length of the |authority| */ size_t authoritylen; /* The length of the |path| */ size_t pathlen; /* The stream ID of this stream */ int32_t stream_id; } http2_stream_data; typedef struct { nghttp2_session *session; struct evdns_base *dnsbase; struct bufferevent *bev; http2_stream_data *stream_data; } http2_session_data; static http2_stream_data *create_http2_stream_data(const char *uri, urlparse_url *u) { /* MAX 5 digits (max 65535) + 1 ':' + 1 NULL (because of snprintf) */ size_t extra = 7; http2_stream_data *stream_data = malloc(sizeof(http2_stream_data)); stream_data->uri = uri; stream_data->u = u; stream_data->stream_id = -1; stream_data->authoritylen = u->field_data[URLPARSE_HOST].len; stream_data->authority = malloc(stream_data->authoritylen + extra); memcpy(stream_data->authority, &uri[u->field_data[URLPARSE_HOST].off], u->field_data[URLPARSE_HOST].len); if (u->field_set & (1 << URLPARSE_PORT)) { stream_data->authoritylen += (size_t)snprintf( stream_data->authority + u->field_data[URLPARSE_HOST].len, extra, ":%u", u->port); } /* If we don't have path in URI, we use "/" as path. */ stream_data->pathlen = 1; if (u->field_set & (1 << URLPARSE_PATH)) { stream_data->pathlen = u->field_data[URLPARSE_PATH].len; } if (u->field_set & (1 << URLPARSE_QUERY)) { /* +1 for '?' character */ stream_data->pathlen += (size_t)(u->field_data[URLPARSE_QUERY].len + 1); } stream_data->path = malloc(stream_data->pathlen); if (u->field_set & (1 << URLPARSE_PATH)) { memcpy(stream_data->path, &uri[u->field_data[URLPARSE_PATH].off], u->field_data[URLPARSE_PATH].len); } else { stream_data->path[0] = '/'; } if (u->field_set & (1 << URLPARSE_QUERY)) { stream_data ->path[stream_data->pathlen - u->field_data[URLPARSE_QUERY].len - 1] = '?'; memcpy(stream_data->path + stream_data->pathlen - u->field_data[URLPARSE_QUERY].len, &uri[u->field_data[URLPARSE_QUERY].off], u->field_data[URLPARSE_QUERY].len); } return stream_data; } static void delete_http2_stream_data(http2_stream_data *stream_data) { free(stream_data->path); free(stream_data->authority); free(stream_data); } /* Initializes |session_data| */ static http2_session_data * create_http2_session_data(struct event_base *evbase) { http2_session_data *session_data = malloc(sizeof(http2_session_data)); memset(session_data, 0, sizeof(http2_session_data)); session_data->dnsbase = evdns_base_new(evbase, 1); return session_data; } static void delete_http2_session_data(http2_session_data *session_data) { SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev); if (ssl) { SSL_shutdown(ssl); } bufferevent_free(session_data->bev); session_data->bev = NULL; evdns_base_free(session_data->dnsbase, 1); session_data->dnsbase = NULL; nghttp2_session_del(session_data->session); session_data->session = NULL; if (session_data->stream_data) { delete_http2_stream_data(session_data->stream_data); session_data->stream_data = NULL; } free(session_data); } static void print_header(FILE *f, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen) { fwrite(name, 1, namelen, f); fprintf(f, ": "); fwrite(value, 1, valuelen, f); fprintf(f, "\n"); } /* Print HTTP headers to |f|. Please note that this function does not take into account that header name and value are sequence of octets, therefore they may contain non-printable characters. */ static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen) { size_t i; for (i = 0; i < nvlen; ++i) { print_header(f, nva[i].name, nva[i].namelen, nva[i].value, nva[i].valuelen); } fprintf(f, "\n"); } /* nghttp2_send_callback2. Here we transmit the |data|, |length| bytes, to the network. Because we are using libevent bufferevent, we just write those bytes into bufferevent buffer. */ static nghttp2_ssize send_callback(nghttp2_session *session, const uint8_t *data, size_t length, int flags, void *user_data) { http2_session_data *session_data = (http2_session_data *)user_data; struct bufferevent *bev = session_data->bev; (void)session; (void)flags; bufferevent_write(bev, data, length); return (nghttp2_ssize)length; } /* nghttp2_on_header_callback: Called when nghttp2 library emits single header name/value pair. */ static int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *user_data) { http2_session_data *session_data = (http2_session_data *)user_data; (void)session; (void)flags; switch (frame->hd.type) { case NGHTTP2_HEADERS: if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && session_data->stream_data->stream_id == frame->hd.stream_id) { /* Print response headers for the initiated request. */ print_header(stderr, name, namelen, value, valuelen); break; } } return 0; } /* nghttp2_on_begin_headers_callback: Called when nghttp2 library gets started to receive header block. */ static int on_begin_headers_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { http2_session_data *session_data = (http2_session_data *)user_data; (void)session; switch (frame->hd.type) { case NGHTTP2_HEADERS: if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && session_data->stream_data->stream_id == frame->hd.stream_id) { fprintf(stderr, "Response headers for stream ID=%d:\n", frame->hd.stream_id); } break; } return 0; } /* nghttp2_on_frame_recv_callback: Called when nghttp2 library received a complete frame from the remote peer. */ static int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { http2_session_data *session_data = (http2_session_data *)user_data; (void)session; switch (frame->hd.type) { case NGHTTP2_HEADERS: if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && session_data->stream_data->stream_id == frame->hd.stream_id) { fprintf(stderr, "All headers received\n"); } break; } return 0; } /* nghttp2_on_data_chunk_recv_callback: Called when DATA frame is received from the remote peer. In this implementation, if the frame is meant to the stream we initiated, print the received data in stdout, so that the user can redirect its output to the file easily. */ static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data) { http2_session_data *session_data = (http2_session_data *)user_data; (void)session; (void)flags; if (session_data->stream_data->stream_id == stream_id) { fwrite(data, 1, len, stdout); } return 0; } /* nghttp2_on_stream_close_callback: Called when a stream is about to closed. This example program only deals with 1 HTTP request (1 stream), if it is closed, we send GOAWAY and tear down the session */ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *user_data) { http2_session_data *session_data = (http2_session_data *)user_data; int rv; if (session_data->stream_data->stream_id == stream_id) { fprintf(stderr, "Stream %d closed with error_code=%u\n", stream_id, error_code); rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); if (rv != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } } return 0; } /* Create SSL_CTX. */ static SSL_CTX *create_ssl_ctx(void) { SSL_CTX *ssl_ctx; ssl_ctx = SSL_CTX_new(TLS_client_method()); if (!ssl_ctx) { errx(1, "Could not create SSL/TLS context: %s", ERR_error_string(ERR_get_error(), NULL)); } SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); SSL_CTX_set_alpn_protos(ssl_ctx, (const unsigned char *)"\x02h2", 3); return ssl_ctx; } /* Create SSL object */ static SSL *create_ssl(SSL_CTX *ssl_ctx) { SSL *ssl; ssl = SSL_new(ssl_ctx); if (!ssl) { errx(1, "Could not create SSL/TLS session object: %s", ERR_error_string(ERR_get_error(), NULL)); } return ssl; } static void initialize_nghttp2_session(http2_session_data *session_data) { nghttp2_session_callbacks *callbacks; nghttp2_session_callbacks_new(&callbacks); nghttp2_session_callbacks_set_send_callback2(callbacks, send_callback); nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, on_frame_recv_callback); nghttp2_session_callbacks_set_on_data_chunk_recv_callback( callbacks, on_data_chunk_recv_callback); nghttp2_session_callbacks_set_on_stream_close_callback( callbacks, on_stream_close_callback); nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header_callback); nghttp2_session_callbacks_set_on_begin_headers_callback( callbacks, on_begin_headers_callback); nghttp2_session_client_new(&session_data->session, callbacks, session_data); nghttp2_session_callbacks_del(callbacks); } static void send_client_connection_header(http2_session_data *session_data) { nghttp2_settings_entry iv[1] = { {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; int rv; /* client 24 bytes magic string will be sent by nghttp2 library */ rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv, ARRLEN(iv)); if (rv != 0) { errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv)); } } #define MAKE_NV(NAME, VALUE, VALUELEN) \ { \ (uint8_t *)NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, \ VALUELEN, NGHTTP2_NV_FLAG_NONE, \ } #define MAKE_NV2(NAME, VALUE) \ { \ (uint8_t *)NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, \ sizeof(VALUE) - 1, NGHTTP2_NV_FLAG_NONE, \ } /* Send HTTP request to the remote peer */ static void submit_request(http2_session_data *session_data) { int32_t stream_id; http2_stream_data *stream_data = session_data->stream_data; const char *uri = stream_data->uri; const urlparse_url *u = stream_data->u; nghttp2_nv hdrs[] = { MAKE_NV2(":method", "GET"), MAKE_NV(":scheme", &uri[u->field_data[URLPARSE_SCHEMA].off], u->field_data[URLPARSE_SCHEMA].len), MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen), MAKE_NV(":path", stream_data->path, stream_data->pathlen)}; fprintf(stderr, "Request headers:\n"); print_headers(stderr, hdrs, ARRLEN(hdrs)); stream_id = nghttp2_submit_request2(session_data->session, NULL, hdrs, ARRLEN(hdrs), NULL, stream_data); if (stream_id < 0) { errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id)); } stream_data->stream_id = stream_id; } /* Serialize the frame and send (or buffer) the data to bufferevent. */ static int session_send(http2_session_data *session_data) { int rv; rv = nghttp2_session_send(session_data->session); if (rv != 0) { warnx("Fatal error: %s", nghttp2_strerror(rv)); return -1; } return 0; } /* readcb for bufferevent. Here we get the data from the input buffer of bufferevent and feed them to nghttp2 library. This may invoke nghttp2 callbacks. It may also queues the frame in nghttp2 session context. To send them, we call session_send() in the end. */ static void readcb(struct bufferevent *bev, void *ptr) { http2_session_data *session_data = (http2_session_data *)ptr; nghttp2_ssize readlen; struct evbuffer *input = bufferevent_get_input(bev); size_t datalen = evbuffer_get_length(input); unsigned char *data = evbuffer_pullup(input, -1); readlen = nghttp2_session_mem_recv2(session_data->session, data, datalen); if (readlen < 0) { warnx("Fatal error: %s", nghttp2_strerror((int)readlen)); delete_http2_session_data(session_data); return; } if (evbuffer_drain(input, (size_t)readlen) != 0) { warnx("Fatal error: evbuffer_drain failed"); delete_http2_session_data(session_data); return; } if (session_send(session_data) != 0) { delete_http2_session_data(session_data); return; } } /* writecb for bufferevent. To greaceful shutdown after sending or receiving GOAWAY, we check the some conditions on the nghttp2 library and output buffer of bufferevent. If it indicates we have no business to this session, tear down the connection. */ static void writecb(struct bufferevent *bev, void *ptr) { http2_session_data *session_data = (http2_session_data *)ptr; (void)bev; if (nghttp2_session_want_read(session_data->session) == 0 && nghttp2_session_want_write(session_data->session) == 0 && evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) { delete_http2_session_data(session_data); } } /* eventcb for bufferevent. For the purpose of simplicity and readability of the example program, we omitted the certificate and peer verification. After SSL/TLS handshake is over, initialize nghttp2 library session, and send client connection header. Then send HTTP request. */ static void eventcb(struct bufferevent *bev, short events, void *ptr) { http2_session_data *session_data = (http2_session_data *)ptr; if (events & BEV_EVENT_CONNECTED) { int fd = bufferevent_getfd(bev); int val = 1; const unsigned char *alpn = NULL; unsigned int alpnlen = 0; SSL *ssl; fprintf(stderr, "Connected\n"); ssl = bufferevent_openssl_get_ssl(session_data->bev); if (alpn == NULL) { SSL_get0_alpn_selected(ssl, &alpn, &alpnlen); } if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) { fprintf(stderr, "h2 is not negotiated\n"); delete_http2_session_data(session_data); return; } setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); initialize_nghttp2_session(session_data); send_client_connection_header(session_data); submit_request(session_data); if (session_send(session_data) != 0) { delete_http2_session_data(session_data); } return; } if (events & BEV_EVENT_EOF) { warnx("Disconnected from the remote host"); } else if (events & BEV_EVENT_ERROR) { warnx("Network error"); } else if (events & BEV_EVENT_TIMEOUT) { warnx("Timeout"); } delete_http2_session_data(session_data); } /* Start connecting to the remote peer |host:port| */ static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx, const char *host, uint16_t port, http2_session_data *session_data) { int rv; struct bufferevent *bev; SSL *ssl; ssl = create_ssl(ssl_ctx); bev = bufferevent_openssl_socket_new( evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING, BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE); bufferevent_enable(bev, EV_READ | EV_WRITE); bufferevent_setcb(bev, readcb, writecb, eventcb, session_data); rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase, AF_UNSPEC, host, port); if (rv != 0) { errx(1, "Could not connect to the remote host %s", host); } session_data->bev = bev; } /* Get resource denoted by the |uri|. The debug and error messages are printed in stderr, while the response body is printed in stdout. */ static void run(const char *uri) { urlparse_url u; char *host; uint16_t port; int rv; SSL_CTX *ssl_ctx; struct event_base *evbase; http2_session_data *session_data; /* Parse the |uri| and stores its components in |u| */ rv = urlparse_parse_url(uri, strlen(uri), 0, &u); if (rv != 0) { errx(1, "Could not parse URI %s", uri); } host = strndup(&uri[u.field_data[URLPARSE_HOST].off], u.field_data[URLPARSE_HOST].len); if (!(u.field_set & (1 << URLPARSE_PORT))) { port = 443; } else { port = u.port; } ssl_ctx = create_ssl_ctx(); evbase = event_base_new(); session_data = create_http2_session_data(evbase); session_data->stream_data = create_http2_stream_data(uri, &u); initiate_connection(evbase, ssl_ctx, host, port, session_data); free(host); host = NULL; event_base_loop(evbase, 0); event_base_free(evbase); SSL_CTX_free(ssl_ctx); } int main(int argc, char **argv) { struct sigaction act; if (argc < 2) { fprintf(stderr, "Usage: libevent-client HTTPS_URI\n"); exit(EXIT_FAILURE); } memset(&act, 0, sizeof(struct sigaction)); act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, NULL); run(argv[1]); return 0; } nghttp2-1.68.0/examples/PaxHeaders/Makefile.am0000644000000000000000000000013215077107270016132 xustar0030 mtime=1761382072.967444259 30 atime=1761382085.501387448 30 ctime=1761382109.378299577 nghttp2-1.68.0/examples/Makefile.am0000644000175100017510000000346615077107270016533 0ustar00runnerrunner# nghttp2 - HTTP/2 C Library # Copyright (c) 2012 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. EXTRA_DIST = CMakeLists.txt if ENABLE_EXAMPLES AM_CFLAGS = $(WARNCFLAGS) AM_CXXFLAGS = $(WARNCXXFLAGS) $(CXX1XCXXFLAGS) AM_CPPFLAGS = \ -I$(top_srcdir)/lib/includes \ -I$(top_builddir)/lib/includes \ -I$(top_srcdir)/third-party/urlparse \ @LIBEVENT_OPENSSL_CFLAGS@ \ @OPENSSL_CFLAGS@ \ @DEFS@ AM_LDFLAGS = @LIBTOOL_LDFLAGS@ LDADD = $(top_builddir)/lib/libnghttp2.la \ $(top_builddir)/third-party/liburlparse.la \ @LIBEVENT_OPENSSL_LIBS@ \ @OPENSSL_LIBS@ \ @APPLDFLAGS@ noinst_PROGRAMS = client libevent-client libevent-server deflate client_SOURCES = client.c libevent_client_SOURCES = libevent-client.c libevent_server_SOURCES = libevent-server.c deflate_SOURCES = deflate.c endif # ENABLE_EXAMPLES nghttp2-1.68.0/PaxHeaders/integration-tests0000644000000000000000000000013215077107335015670 xustar0030 mtime=1761382109.435299413 30 atime=1761382109.795298372 30 ctime=1761382109.435299413 nghttp2-1.68.0/integration-tests/0000755000175100017510000000000015077107335016335 5ustar00runnerrunnernghttp2-1.68.0/integration-tests/PaxHeaders/server.crt0000644000000000000000000000013115077107270017762 xustar0030 mtime=1761382072.974444227 29 atime=1761382106.42830944 30 ctime=1761382109.426299439 nghttp2-1.68.0/integration-tests/server.crt0000644000175100017510000000240115077107270020350 0ustar00runnerrunner-----BEGIN CERTIFICATE----- MIIDhTCCAm2gAwIBAgIJAOvIx8xIxgyOMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCTEyNy4wLjAuMTAeFw0xNTAxMjMxMjI0 MjdaFw0yNTAxMjAxMjI0MjdaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV BAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMuI QZRI/iBaxPTjTWGemt8tCEfzZWxuIW3hY/gIhwJDfH2SbourBh1s9vqcqhBq5vmo kdfVQXAnNLjIG1uhWmcHuNnKrE5hU82N6i9RsmuM5TQRvhsamHri4G+EXJMu9GqF Mso8g7MWpRSGKf+8gfjAVNwfCHFiu8oBcMmy3l54MFHgRLSveAMhiPB0e3Xlnpr5 2bS/oGTx5ynwPgBpEn2FrpT4Z/aLCLzJ/ysgNH8BXEh7n/v7xM3vd5grqB039rd5 JoxlWvp+4XpzKp5upaqmOcVUq4pDSFUQ3w6C+v33Z3OK6Qaon7GMxLv3Us3b7PZ3 1CLoWJR2o3OSnUfO/gUCAwEAAaNQME4wHQYDVR0OBBYEFLc5JWPUUVx4GJesogMV w2Rz0L3yMB8GA1UdIwQYMBaAFLc5JWPUUVx4GJesogMVw2Rz0L3yMAwGA1UdEwQF MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAP/cJWpM+GEjmVYHFacKTdbXBMox2Xn QY2NLm00WPOGvKnO7czMFfX/pEmiq71kD45rLLfbaJP205QpxqiAIvhFhuq50Co7 sTDtwcDTPLX9H7Ugjt4sTMPiwC14uVXFfoT/J46zMjXwP00qKyfszc2tkIgHfrTl h4M1hkdfmMximir/Ii7TdYYJ3oGS8tdcYb6D4DZwAljKmxF6iUOwFCUgpTmqDBT5 irXY8D27DzuNN5Pg07rwAlwXLCzrJE10UtO4MmRVXwpzmoaRQD4/tna6bZzdetvs gPdGP6W1o0q85gullieMJWeKyQA/wasoE7fypn4pHAdTZm/vH+v7GHg= -----END CERTIFICATE----- nghttp2-1.68.0/integration-tests/PaxHeaders/resp-return.rb0000644000000000000000000000013015077107270020554 xustar0030 mtime=1761382072.974444227 28 atime=1761382106.4373094 30 ctime=1761382109.435299413 nghttp2-1.68.0/integration-tests/resp-return.rb0000644000175100017510000000027615077107270021153 0ustar00runnerrunnerclass App def on_resp(env) resp = env.resp resp.clear_headers resp.status = 404 resp.add_header "from", "mruby" resp.return "Hello World from resp" end end App.new nghttp2-1.68.0/integration-tests/PaxHeaders/resp-set-header.rb0000644000000000000000000000013215077107270021260 xustar0030 mtime=1761382072.974444227 30 atime=1761382106.434309414 30 ctime=1761382109.432299421 nghttp2-1.68.0/integration-tests/resp-set-header.rb0000644000175100017510000000013115077107270021643 0ustar00runnerrunnerclass App def on_resp(env) env.resp.set_header "Alpha", "bravo" end end App.new nghttp2-1.68.0/integration-tests/PaxHeaders/setenv0000644000000000000000000000013215077107327017175 xustar0030 mtime=1761382103.963320299 30 atime=1761382104.723316951 30 ctime=1761382109.430299427 nghttp2-1.68.0/integration-tests/setenv0000644000175100017510000000062315077107327017566 0ustar00runnerrunner#!/bin/sh -e libdir="/home/runner/work/nghttp2/nghttp2/lib" if [ -d "$libdir/.libs" ]; then libdir="$libdir/.libs" fi export CGO_CFLAGS="-I/home/runner/work/nghttp2/nghttp2/lib/includes -I/home/runner/work/nghttp2/nghttp2/lib/includes -g -O2" export CGO_CPPFLAGS="" export CGO_LDFLAGS="-L$libdir " export LD_LIBRARY_PATH="$libdir" export DYLD_LIBRARY_PATH="$libdir" export GODEBUG=cgocheck=0 "$@" nghttp2-1.68.0/integration-tests/PaxHeaders/alt-server.crt0000644000000000000000000000013215077107270020541 xustar0030 mtime=1761382072.973444231 30 atime=1761382106.431309427 30 ctime=1761382109.428299433 nghttp2-1.68.0/integration-tests/alt-server.crt0000644000175100017510000000240515077107270021132 0ustar00runnerrunner-----BEGIN CERTIFICATE----- MIIDhzCCAm+gAwIBAgIJANfuEldiquMNMA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNV BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX aWRnaXRzIFB0eSBMdGQxEzARBgNVBAMMCmFsdC1kb21haW4wHhcNMTUwMTI1MDYy NTQxWhcNMjUwMTIyMDYyNTQxWjBaMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29t ZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYD VQQDDAphbHQtZG9tYWluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA 0IwhDOGDipGrJQ9IoRSzPdkU/Ii4aJgGKHlXminym42X0VI3IW61RLvOHRlHVmVH JQjFuDo2x+y81t9NlDg3HGUbSpzOzpm6StiutB7c4hreT5G4r0YKya1ugiemN0+p qjIPJWm2jVnf448eZvUKRKEQ9W0MLZjiNjVGKrKlwo7fIlXg4N3+YixLYffAT1NV d1T6V5jzlbruj15gK2nGjMQ9D1h1t9vTbTxY+mtk72aX0Y64IE6pPBWLFSSH8ozU idDoL3AZwz2Jker+ALKK8CM4uho/RPpyW1C06HH+HLdH2MqEjDOROde/Nzxm668O gK/JWGIEyUqYiUXx0yhFxwIDAQABo1AwTjAdBgNVHQ4EFgQU/Y0GDN2uPjbyePcu 95ZvYEK/gHIwHwYDVR0jBBgwFoAU/Y0GDN2uPjbyePcu95ZvYEK/gHIwDAYDVR0T BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAodD6LVCzL3wfsZ6TxTzf9TfgIdbj ilL3SEMT/xnfTXT3SLYScTRqQIAI29Y7dOLMq89p4hY2wmeUEhBUAz+y9G2JVr8o 6EbxXrQpWgNJogELqoNnMdrDxB5RsmDDKEJ/rLjDfSkjWbK7B2PZsqVTDgjekCFw u6FqTIjn/O1O/L5tjwxwxjHmQod/maFCvXoDOVBuwdHnkp298tqlvsHfHO8m++Wj +XYB8plMIjpeTh9v4w9Jc4QZ59lK/3Tt4qaENeQrMEubKSY/Zen7L2bzhk+cChWT GSGz9uNXieoZaH79D0wnyZaSZ5Ds4ActMevnGg3iYXuzuFqx8Pungn74Vg== -----END CERTIFICATE----- nghttp2-1.68.0/integration-tests/PaxHeaders/server_tester_http3.go0000644000000000000000000000013215077107270022310 xustar0030 mtime=1761382072.974444227 30 atime=1761382106.426309449 30 ctime=1761382109.424299444 nghttp2-1.68.0/integration-tests/server_tester_http3.go0000644000175100017510000000266615077107270022712 0ustar00runnerrunner//go:build quic package nghttp2 import ( "bytes" "context" "crypto/tls" "io" "net/http" "net/url" "time" "github.com/quic-go/quic-go/http3" ) func (st *serverTester) http3(rp requestParam) (*serverResponse, error) { rt := &http3.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, } defer rt.Close() c := &http.Client{ Transport: rt, } method := "GET" if rp.method != "" { method = rp.method } var body io.Reader if rp.body != nil { body = bytes.NewBuffer(rp.body) } reqURL := st.url if rp.path != "" { u, err := url.Parse(st.url) if err != nil { st.t.Fatalf("Error parsing URL from st.url %v: %v", st.url, err) } u.Path = "" u.RawQuery = "" reqURL = u.String() + rp.path } ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() req, err := http.NewRequestWithContext(ctx, method, reqURL, body) if err != nil { return nil, err } for _, h := range rp.header { req.Header.Add(h.Name, h.Value) } req.Header.Add("Test-Case", rp.name) // TODO http3 package does not support trailer at the time of // this writing. resp, err := c.Do(req) if err != nil { return nil, err } defer resp.Body.Close() respBody, err := io.ReadAll(resp.Body) if err != nil { return nil, err } res := &serverResponse{ status: resp.StatusCode, header: resp.Header, body: respBody, connClose: resp.Close, } return res, nil } nghttp2-1.68.0/integration-tests/PaxHeaders/req-return.rb0000644000000000000000000000013215077107270020374 xustar0030 mtime=1761382072.974444227 30 atime=1761382106.436309405 30 ctime=1761382109.434299416 nghttp2-1.68.0/integration-tests/req-return.rb0000644000175100017510000000027415077107270020767 0ustar00runnerrunnerclass App def on_req(env) resp = env.resp resp.clear_headers resp.status = 404 resp.add_header "from", "mruby" resp.return "Hello World from req" end end App.new nghttp2-1.68.0/integration-tests/PaxHeaders/nghttpx_http3_test.go0000644000000000000000000000013215077107270022147 xustar0030 mtime=1761382072.974444227 30 atime=1761382106.423309462 30 ctime=1761382109.421299453 nghttp2-1.68.0/integration-tests/nghttpx_http3_test.go0000644000175100017510000002226615077107270022547 0ustar00runnerrunner//go:build quic package nghttp2 import ( "bytes" "crypto/rand" "errors" "io" "net/http" "regexp" "testing" "golang.org/x/net/http2/hpack" ) // TestH3H1PlainGET tests whether simple HTTP/3 GET request works. func TestH3H1PlainGET(t *testing.T) { st := newServerTester(t, options{ quic: true, }) defer st.Close() res, err := st.http3(requestParam{ name: "TestH3H1PlainGET", }) if err != nil { t.Fatalf("Error st.http3() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH3H1RequestBody tests HTTP/3 request with body works. func TestH3H1RequestBody(t *testing.T) { body := make([]byte, 3333) _, err := rand.Read(body) if err != nil { t.Fatalf("Unable to create request body: %v", err) } opts := options{ handler: func(_ http.ResponseWriter, r *http.Request) { buf := make([]byte, 4096) buflen := 0 p := buf for { if len(p) == 0 { t.Fatal("Request body is too large") } n, err := r.Body.Read(p) p = p[n:] buflen += n if err != nil { if errors.Is(err, io.EOF) { break } t.Fatalf("r.Body.Read() = %v", err) } } buf = buf[:buflen] if got, want := buf, body; !bytes.Equal(got, want) { t.Fatalf("buf = %v; want %v", got, want) } }, quic: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http3(requestParam{ name: "TestH3H1RequestBody", body: body, }) if err != nil { t.Fatalf("Error st.http3() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH3H1GenerateVia tests that server generates Via header field to // and from backend server. func TestH3H1GenerateVia(t *testing.T) { opts := options{ handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("Via"), "3 nghttpx"; got != want { t.Errorf("Via: %v; want %v", got, want) } }, quic: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http3(requestParam{ name: "TestH3H1GenerateVia", }) if err != nil { t.Fatalf("Error st.http3() = %v", err) } if got, want := res.header.Get("Via"), "1.1 nghttpx"; got != want { t.Errorf("Via: %v; want %v", got, want) } } // TestH3H1AppendVia tests that server adds value to existing Via // header field to and from backend server. func TestH3H1AppendVia(t *testing.T) { opts := options{ handler: func(w http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("Via"), "foo, 3 nghttpx"; got != want { t.Errorf("Via: %v; want %v", got, want) } w.Header().Add("Via", "bar") }, quic: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http3(requestParam{ name: "TestH3H1AppendVia", header: []hpack.HeaderField{ pair("via", "foo"), }, }) if err != nil { t.Fatalf("Error st.http3() = %v", err) } if got, want := res.header.Get("Via"), "bar, 1.1 nghttpx"; got != want { t.Errorf("Via: %v; want %v", got, want) } } // TestH3H1NoVia tests that server does not add value to existing Via // header field to and from backend server. func TestH3H1NoVia(t *testing.T) { opts := options{ args: []string{"--no-via"}, handler: func(w http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("Via"), "foo"; got != want { t.Errorf("Via: %v; want %v", got, want) } w.Header().Add("Via", "bar") }, quic: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http3(requestParam{ name: "TestH3H1NoVia", header: []hpack.HeaderField{ pair("via", "foo"), }, }) if err != nil { t.Fatalf("Error st.http3() = %v", err) } if got, want := res.header.Get("Via"), "bar"; got != want { t.Errorf("Via: %v; want %v", got, want) } } // TestH3H1BadResponseCL tests that server returns error when // content-length response header field value does not match its // response body size. func TestH3H1BadResponseCL(t *testing.T) { opts := options{ handler: func(w http.ResponseWriter, _ *http.Request) { // we set content-length: 1024, but only send 3 bytes. w.Header().Add("Content-Length", "1024") if _, err := w.Write([]byte("foo")); err != nil { t.Fatalf("Error w.Write() = %v", err) } }, quic: true, } st := newServerTester(t, opts) defer st.Close() _, err := st.http3(requestParam{ name: "TestH3H1BadResponseCL", }) if err == nil { t.Fatal("st.http3() should fail") } } // TestH3H1HTTPSRedirect tests that HTTPS redirect should not happen // with HTTP/3. func TestH3H1HTTPSRedirect(t *testing.T) { opts := options{ args: []string{"--redirect-if-not-tls"}, quic: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http3(requestParam{ name: "TestH3H1HTTPSRedirect", }) if err != nil { t.Fatalf("Error st.http3() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH3H1AffinityCookieTLS tests that affinity cookie is sent back // in https. func TestH3H1AffinityCookieTLS(t *testing.T) { opts := options{ args: []string{"--affinity-cookie"}, quic: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http3(requestParam{ name: "TestH3H1AffinityCookieTLS", scheme: "https", }) if err != nil { t.Fatalf("Error st.http3() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar; Secure` validCookie := regexp.MustCompile(pattern) if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) { t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern) } } // TestH3H2ReqPhaseReturn tests mruby request phase hook returns // custom response. func TestH3H2ReqPhaseReturn(t *testing.T) { opts := options{ args: []string{ "--http2-bridge", "--mruby-file=" + testDir + "/req-return.rb", }, handler: func(http.ResponseWriter, *http.Request) { t.Fatalf("request should not be forwarded") }, quic: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http3(requestParam{ name: "TestH3H2ReqPhaseReturn", }) if err != nil { t.Fatalf("Error st.http3() = %v", err) } if got, want := res.status, http.StatusNotFound; got != want { t.Errorf("status = %v; want %v", got, want) } hdtests := []struct { k, v string }{ {"content-length", "20"}, {"from", "mruby"}, } for _, tt := range hdtests { if got, want := res.header.Get(tt.k), tt.v; got != want { t.Errorf("%v = %v; want %v", tt.k, got, want) } } if got, want := string(res.body), "Hello World from req"; got != want { t.Errorf("body = %v; want %v", got, want) } } // TestH3H2RespPhaseReturn tests mruby response phase hook returns // custom response. func TestH3H2RespPhaseReturn(t *testing.T) { opts := options{ args: []string{ "--http2-bridge", "--mruby-file=" + testDir + "/resp-return.rb", }, quic: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http3(requestParam{ name: "TestH3H2RespPhaseReturn", }) if err != nil { t.Fatalf("Error st.http3() = %v", err) } if got, want := res.status, http.StatusNotFound; got != want { t.Errorf("status = %v; want %v", got, want) } hdtests := []struct { k, v string }{ {"content-length", "21"}, {"from", "mruby"}, } for _, tt := range hdtests { if got, want := res.header.Get(tt.k), tt.v; got != want { t.Errorf("%v = %v; want %v", tt.k, got, want) } } if got, want := string(res.body), "Hello World from resp"; got != want { t.Errorf("body = %v; want %v", got, want) } } // TestH3ResponseBeforeRequestEnd tests the situation where response // ends before request body finishes. func TestH3ResponseBeforeRequestEnd(t *testing.T) { opts := options{ args: []string{"--mruby-file=" + testDir + "/req-return.rb"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatal("request should not be forwarded") }, quic: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http3(requestParam{ name: "TestH3ResponseBeforeRequestEnd", noEndStream: true, }) if err != nil { t.Fatalf("Error st.http3() = %v", err) } if got, want := res.status, http.StatusNotFound; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH3H1ChunkedEndsPrematurely tests that a stream is reset if the // backend chunked encoded response ends prematurely. func TestH3H1ChunkedEndsPrematurely(t *testing.T) { opts := options{ handler: func(w http.ResponseWriter, _ *http.Request) { hj, ok := w.(http.Hijacker) if !ok { http.Error(w, "Could not hijack the connection", http.StatusInternalServerError) return } conn, bufrw, err := hj.Hijack() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer conn.Close() if _, err := bufrw.WriteString("HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\n\r\n"); err != nil { t.Fatalf("Error bufrw.WriteString() = %v", err) } bufrw.Flush() }, quic: true, } st := newServerTester(t, opts) defer st.Close() _, err := st.http3(requestParam{ name: "TestH3H1ChunkedEndsPrematurely", }) if err == nil { t.Fatal("st.http3() should fail") } } nghttp2-1.68.0/integration-tests/PaxHeaders/CMakeLists.txt0000644000000000000000000000013115077107270020502 xustar0030 mtime=1761382072.973444231 29 atime=1761382106.41930948 30 ctime=1761382109.417299465 nghttp2-1.68.0/integration-tests/CMakeLists.txt0000644000175100017510000000215515077107270021076 0ustar00runnerrunnerset(GO_FILES nghttpx_http1_test.go nghttpx_http2_test.go server_tester.go server_tester_http3.go ) # XXX unused set(EXTRA_DIST ${GO_FILES} server.key server.crt alt-server.key alt-server.crt setenv req-set-header.rb resp-set-header.rb req-return.rb resp-return.rb ) # 'go test' requires both config.go and the test files in the same directory. # For out-of-tree builds, config.go is normally not placed next to the source # files, so copy the tests to the build directory as a workaround. set(GO_BUILD_FILES) if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) foreach(gofile IN LISTS GO_FILES) set(outfile "${CMAKE_CURRENT_BINARY_DIR}/${gofile}") add_custom_command(OUTPUT "${outfile}" COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/${gofile}" "${outfile}" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${gofile}" ) list(APPEND GO_BUILD_FILES "${outfile}") endforeach() endif() if(ENABLE_HTTP3) set(GO_TEST_TAGS quic) endif() add_custom_target(it COMMAND sh setenv go test -v --tags=${GO_TEST_TAGS} DEPENDS ${GO_BUILD_FILES} ) nghttp2-1.68.0/integration-tests/PaxHeaders/Makefile.in0000644000000000000000000000013215077107305020007 xustar0030 mtime=1761382085.565387174 30 atime=1761382103.933320431 30 ctime=1761382109.413299476 nghttp2-1.68.0/integration-tests/Makefile.in0000644000175100017510000004106615077107305020406 0ustar00runnerrunner# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # nghttp2 - HTTP/2 C Library # Copyright (c) 2015 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ @ENABLE_HTTP3_TRUE@am__append_1 = quic subdir = integration-tests ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = config.go setenv CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.go.in \ $(srcdir)/setenv.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPLDFLAGS = @APPLDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BPFCFLAGS = @BPFCFLAGS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CXX = @CXX@ CXX1XCXXFLAGS = @CXX1XCXXFLAGS@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXTRABPFCFLAGS = @EXTRABPFCFLAGS@ EXTRACFLAG = @EXTRACFLAG@ EXTRA_DEFS = @EXTRA_DEFS@ FGREP = @FGREP@ FILECMD = @FILECMD@ GREP = @GREP@ HAVE_CXX20 = @HAVE_CXX20@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ JANSSON_CFLAGS = @JANSSON_CFLAGS@ JANSSON_LIBS = @JANSSON_LIBS@ JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ JEMALLOC_LIBS = @JEMALLOC_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBBPF_CFLAGS = @LIBBPF_CFLAGS@ LIBBPF_LIBS = @LIBBPF_LIBS@ LIBBROTLIDEC_CFLAGS = @LIBBROTLIDEC_CFLAGS@ LIBBROTLIDEC_LIBS = @LIBBROTLIDEC_LIBS@ LIBBROTLIENC_CFLAGS = @LIBBROTLIENC_CFLAGS@ LIBBROTLIENC_LIBS = @LIBBROTLIENC_LIBS@ LIBCARES_CFLAGS = @LIBCARES_CFLAGS@ LIBCARES_LIBS = @LIBCARES_LIBS@ LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ LIBEV_CFLAGS = @LIBEV_CFLAGS@ LIBEV_LIBS = @LIBEV_LIBS@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@ LIBMRUBY_LIBS = @LIBMRUBY_LIBS@ LIBNGHTTP3_CFLAGS = @LIBNGHTTP3_CFLAGS@ LIBNGHTTP3_LIBS = @LIBNGHTTP3_LIBS@ LIBNGTCP2_CFLAGS = @LIBNGTCP2_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS = @LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_LIBS = @LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS = @LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS@ LIBNGTCP2_CRYPTO_LIBRESSL_LIBS = @LIBNGTCP2_CRYPTO_LIBRESSL_LIBS@ LIBNGTCP2_CRYPTO_OSSL_CFLAGS = @LIBNGTCP2_CRYPTO_OSSL_CFLAGS@ LIBNGTCP2_CRYPTO_OSSL_LIBS = @LIBNGTCP2_CRYPTO_OSSL_LIBS@ LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS = @LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ LIBNGTCP2_CRYPTO_QUICTLS_LIBS = @LIBNGTCP2_CRYPTO_QUICTLS_LIBS@ LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS = @LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS@ LIBNGTCP2_CRYPTO_WOLFSSL_LIBS = @LIBNGTCP2_CRYPTO_WOLFSSL_LIBS@ LIBNGTCP2_LIBS = @LIBNGTCP2_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBTOOL_LDFLAGS = @LIBTOOL_LDFLAGS@ LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ LIBXML2_LIBS = @LIBXML2_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_AGE = @LT_AGE@ LT_CURRENT = @LT_CURRENT@ LT_REVISION = @LT_REVISION@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ SYSTEMD_LIBS = @SYSTEMD_LIBS@ TESTLDADD = @TESTLDADD@ VERSION = @VERSION@ WARNCFLAGS = @WARNCFLAGS@ WARNCXXFLAGS = @WARNCXXFLAGS@ WOLFSSL_CFLAGS = @WOLFSSL_CFLAGS@ WOLFSSL_LIBS = @WOLFSSL_LIBS@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ GO_FILES = \ nghttpx_http1_test.go \ nghttpx_http2_test.go \ nghttpx_http3_test.go \ server_tester.go \ server_tester_http3.go EXTRA_DIST = \ CMakeLists.txt \ $(GO_FILES) \ server.key \ server.crt \ alt-server.key \ alt-server.crt \ setenv \ req-set-header.rb \ resp-set-header.rb \ req-return.rb \ resp-return.rb GO_TEST_TAGS = $(am__append_1) all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu integration-tests/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu integration-tests/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): config.go: $(top_builddir)/config.status $(srcdir)/config.go.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ setenv: $(top_builddir)/config.status $(srcdir)/setenv.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags-am uninstall uninstall-am .PRECIOUS: Makefile it: for i in $(GO_FILES); do [ -e $(builddir)/$$i ] || cp $(srcdir)/$$i $(builddir); done sh setenv go test -v --tags=${GO_TEST_TAGS} # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nghttp2-1.68.0/integration-tests/PaxHeaders/req-set-header.rb0000644000000000000000000000013215077107270021076 xustar0030 mtime=1761382072.974444227 30 atime=1761382106.433309418 30 ctime=1761382109.431299424 nghttp2-1.68.0/integration-tests/req-set-header.rb0000644000175100017510000000013415077107270021464 0ustar00runnerrunnerclass App def on_req(env) env.req.set_header "User-Agent", "mruby" end end App.new nghttp2-1.68.0/integration-tests/PaxHeaders/server.key0000644000000000000000000000013215077107270017763 xustar0030 mtime=1761382072.974444227 30 atime=1761382106.427309445 30 ctime=1761382109.425299442 nghttp2-1.68.0/integration-tests/server.key0000644000175100017510000000325015077107270020353 0ustar00runnerrunner-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDLiEGUSP4gWsT0 401hnprfLQhH82VsbiFt4WP4CIcCQ3x9km6LqwYdbPb6nKoQaub5qJHX1UFwJzS4 yBtboVpnB7jZyqxOYVPNjeovUbJrjOU0Eb4bGph64uBvhFyTLvRqhTLKPIOzFqUU hin/vIH4wFTcHwhxYrvKAXDJst5eeDBR4ES0r3gDIYjwdHt15Z6a+dm0v6Bk8ecp 8D4AaRJ9ha6U+Gf2iwi8yf8rIDR/AVxIe5/7+8TN73eYK6gdN/a3eSaMZVr6fuF6 cyqebqWqpjnFVKuKQ0hVEN8Ogvr992dziukGqJ+xjMS791LN2+z2d9Qi6FiUdqNz kp1Hzv4FAgMBAAECggEACG26GYP0Ui6wHVwUZkiFLVzWDPS9bIIbDEvbMfhYbvWQ gDrCLTKF7E4I5FP8jvV+XzRl5cRFE3nsKwLObzr9XWrqcsp73DsXl1mbKx58/ws0 qrVZZBHz4pLmrHeUxduZ75dYhRuAcLgtWe48awTJdR2x5fO7C8cE89afbxrjLpJE tVyiw6vVB0GfWTZodxtAFMTX1KVm4bTngXfg0NF1FBNHAX3Cm6t4YCE41hKSc0IQ Jr3C4e9uj8poze1B17k79bGB8HNMbbc8Ws0sdbxi5xnY+HUA/mYQrmGXo8sdqiYC EYCMqPm3iJrCmmpHukGf2Vt9k1aLlJ+lxOclSwFO+QKBgQDoRmoprfdmU20LyxYH eVeVqggqmhNohwnuhIvOAyrWGUkbDsssqx2Vv82z0WHAAkwEvQ984UzaYWCCL3m3 +JzpF2dz6aKhXIaYnXBlk3STMGUCDT5ysPvsin9z/unzkffh3vrbDBARGFYWG18x eUyTDOVVeTZNHUJXGjRyiftCkwKBgQDgUkR6dHU4ciSt7Y0UkyAgtZ7POR41T05L bcxbjJeqm6qlj+oP9WUk7JxeSEFUbrMiROABLPPqTwmGo4xrDRx/e7WrqN6QBKC+ Y8CfalrKRb0np60x7Mxx0kbmHp5cwv9QDKznKViOYSgKxFrOFZyMAEXQdZ3FvjXF OQWrw86kBwKBgQDXuxa9MWO3uUJtkqkaNfw/+FVvY/0kt09lJdxHci+l/IQmyl2w Vhm7TRK7sXvtfvSl7gblgMgFiC2/nGKbmR/7ag5e3R98aVhlhMywuvyp/GfEORLI KVNChfwMezVFUUx+j8BEFHcTuZuzGqcWZ0fUyER0V4k0pDlKdv9BZqBkWwKBgCdP o3qGQCilMDJex/OMGPxCd9M+4kFbZZAobMC6cbXPU+dxwgYL7i67XGfVZ8WBJNlj kpICK7irIzM6JBh6krzwlBTCIkbA2N6kopQNUl3SPOTfKKXwJp/nxs77HKuK7K09 m2tjPoatFhRU9sjY1rdeMN3oTr7hp5CpfonsZaEvAoGAEPsZcDd4N9ap5bgaeDy9 NOfLsIyaxT5k6moRIiy83QPihvCuECP16+r6M5tiSfgt/PtCimdjhRiqXzIHNRhh Nfsv13vUtZgt8cYXuTdI4a8feKI7Q4876ME8Qp3WM5/UNZWq6/sWCuZFqbXUhqM0 mwNEi5Zddzf8VsSL2gCraQg= -----END PRIVATE KEY----- nghttp2-1.68.0/integration-tests/PaxHeaders/server_tester.go0000644000000000000000000000013115077107270021165 xustar0030 mtime=1761382072.974444227 30 atime=1761382106.424309458 29 ctime=1761382109.42229945 nghttp2-1.68.0/integration-tests/server_tester.go0000644000175100017510000005060715077107270021566 0ustar00runnerrunnerpackage nghttp2 import ( "bufio" "bytes" "cmp" "context" "crypto/tls" "encoding/binary" "errors" "fmt" "io" "net" "net/http" "net/http/httptest" "net/url" "os" "os/exec" "slices" "strconv" "strings" "syscall" "testing" "time" "github.com/tatsuhiro-t/go-nghttp2" "golang.org/x/net/http2" "golang.org/x/net/http2/hpack" "golang.org/x/net/websocket" ) const ( serverBin = buildDir + "/src/nghttpx" serverPort = 3009 testDir = sourceDir + "/integration-tests" logDir = buildDir + "/integration-tests" ) func pair(name, value string) hpack.HeaderField { return hpack.HeaderField{ Name: name, Value: value, } } type serverTester struct { cmd *exec.Cmd // test frontend server process, which is test subject url string // test frontend server URL t *testing.T ts *httptest.Server // backend server frontendHost string // frontend server host backendHost string // backend server host conn net.Conn // connection to frontend server h2PrefaceSent bool // HTTP/2 preface was sent in conn nextStreamID uint32 // next stream ID fr *http2.Framer // HTTP/2 framer headerBlkBuf bytes.Buffer // buffer to store encoded header block enc *hpack.Encoder // HTTP/2 HPACK encoder header http.Header // received header fields dec *hpack.Decoder // HTTP/2 HPACK decoder authority string // server's host:port frCh chan http2.Frame // used for incoming HTTP/2 frame errCh chan error } type options struct { // args is the additional arguments to nghttpx. args []string // handler is the handler to handle the request. It defaults // to noopHandler. handler http.HandlerFunc // connectPort is the server side port where client connection // is made. It defaults to serverPort. connectPort int // tls, if set to true, sets up TLS frontend connection. tls bool // tlsConfig is the client side TLS configuration that is used // when tls is true. tlsConfig *tls.Config // tcpData is additional data that are written to connection // before TLS handshake starts. This field is ignored if tls // is false. tcpData []byte // quic, if set to true, sets up QUIC frontend connection. // quic implies tls = true. quic bool } // newServerTester creates test context. func newServerTester(t *testing.T, opts options) *serverTester { if opts.quic { opts.tls = true } if opts.handler == nil { opts.handler = noopHandler } if opts.connectPort == 0 { opts.connectPort = serverPort } ts := httptest.NewUnstartedServer(opts.handler) var ( args []string backendTLS, dns, externalDNS, acceptProxyProtocol, redirectIfNotTLS, affinityCookie, alpnH1 bool ) for _, k := range opts.args { switch k { case "--http2-bridge": backendTLS = true case "--dns": dns = true case "--external-dns": dns = true externalDNS = true case "--accept-proxy-protocol": acceptProxyProtocol = true case "--redirect-if-not-tls": redirectIfNotTLS = true case "--affinity-cookie": affinityCookie = true case "--alpn-h1": alpnH1 = true default: args = append(args, k) } } if backendTLS { nghttp2.ConfigureServer(ts.Config, &nghttp2.Server{}) // According to httptest/server.go, we have to set // NextProtos separately for ts.TLS. NextProtos set // in nghttp2.ConfigureServer is effectively ignored. ts.TLS = new(tls.Config) ts.TLS.NextProtos = append(ts.TLS.NextProtos, "h2") ts.StartTLS() args = append(args, "-k") } else { ts.Start() } scheme := "http" if opts.tls { scheme = "https" args = append(args, testDir+"/server.key", testDir+"/server.crt") } backendURL, err := url.Parse(ts.URL) if err != nil { t.Fatalf("Error parsing URL from httptest.Server: %v", err) } // URL.Host looks like "127.0.0.1:8080", but we want // "127.0.0.1,8080" b := "-b" if !externalDNS { b += fmt.Sprintf("%v;", strings.ReplaceAll(backendURL.Host, ":", ",")) } else { sep := strings.LastIndex(backendURL.Host, ":") if sep == -1 { t.Fatalf("backendURL.Host %v does not contain separator ':'", backendURL.Host) } // We use awesome service nip.io. b += fmt.Sprintf("%v.nip.io,%v;", backendURL.Host[:sep], backendURL.Host[sep+1:]) // Make external DNS tests less flaky. args = append(args, "--backend-address-family=IPv4") } if backendTLS { b += ";proto=h2;tls" } if dns { b += ";dns" } if redirectIfNotTLS { b += ";redirect-if-not-tls" } if affinityCookie { b += ";affinity=cookie;affinity-cookie-name=affinity;affinity-cookie-path=/foo/bar" } noTLS := ";no-tls" if opts.tls { noTLS = "" } var proxyProto string if acceptProxyProtocol { proxyProto = ";proxyproto" } args = append(args, fmt.Sprintf("-f127.0.0.1,%v%v%v", serverPort, noTLS, proxyProto), b, "--errorlog-file="+logDir+"/log.txt", "-LINFO") if opts.quic { args = append(args, fmt.Sprintf("-f127.0.0.1,%v;quic", serverPort), "--no-quic-bpf", // quic-go client just closes connection after // receiving the first GOAWAY without any // indication like sending CONNECTION_CLOSE if // there is no active stream. If that // happens, server keeps resending 2nd GOAWAY // until idle timeout passes. If that happens // during Close(), the process will be killed // because the default idle timeout is longer // than the close timeout. Shorten the idle // timeout to prevent it from happening. "--frontend-quic-idle-timeout=5", ) } authority := fmt.Sprintf("127.0.0.1:%v", opts.connectPort) st := &serverTester{ cmd: exec.Command(serverBin, args...), t: t, ts: ts, url: fmt.Sprintf("%v://%v", scheme, authority), frontendHost: fmt.Sprintf("127.0.0.1:%v", serverPort), backendHost: backendURL.Host, nextStreamID: 1, authority: authority, frCh: make(chan http2.Frame), errCh: make(chan error), } st.cmd.Stdout = os.Stdout st.cmd.Stderr = os.Stderr if err := st.cmd.Start(); err != nil { st.t.Fatalf("Error starting %v: %v", serverBin, err) } retry := 0 for { time.Sleep(50 * time.Millisecond) conn, err := net.Dial("tcp", authority) if err == nil && opts.tls { if len(opts.tcpData) > 0 { if _, err := conn.Write(opts.tcpData); err != nil { st.Close() st.t.Fatal("Error writing TCP data") } } var tlsConfig *tls.Config if opts.tlsConfig == nil { tlsConfig = new(tls.Config) } else { tlsConfig = opts.tlsConfig.Clone() } tlsConfig.InsecureSkipVerify = true if alpnH1 { tlsConfig.NextProtos = []string{"http/1.1"} } else { tlsConfig.NextProtos = []string{"h2"} } tlsConn := tls.Client(conn, tlsConfig) err = tlsConn.Handshake() if err == nil { conn = tlsConn } } if err != nil { retry++ if retry >= 100 { st.Close() st.t.Fatalf("Error server is not responding too long; server command-line arguments may be invalid") } continue } st.conn = conn break } st.fr = http2.NewFramer(st.conn, st.conn) st.enc = hpack.NewEncoder(&st.headerBlkBuf) st.dec = hpack.NewDecoder(4096, func(f hpack.HeaderField) { st.header.Add(f.Name, f.Value) }) return st } func (st *serverTester) Close() { if st.conn != nil { st.conn.Close() } if st.cmd != nil { done := make(chan struct{}) go func() { if err := st.cmd.Wait(); err != nil { st.t.Errorf("Error st.cmd.Wait() = %v", err) } close(done) }() if err := st.cmd.Process.Signal(syscall.SIGQUIT); err != nil { st.t.Errorf("Error st.cmd.Process.Signal() = %v", err) } select { case <-done: case <-time.After(10 * time.Second): if err := st.cmd.Process.Kill(); err != nil { st.t.Errorf("Error st.cmd.Process.Kill() = %v", err) } <-done } } if st.ts != nil { st.ts.Close() } } func (st *serverTester) readFrame() (http2.Frame, error) { go func() { f, err := st.fr.ReadFrame() if err != nil { st.errCh <- err return } st.frCh <- f }() select { case f := <-st.frCh: return f, nil case err := <-st.errCh: return nil, err case <-time.After(5 * time.Second): return nil, errors.New("timeout waiting for frame") } } type requestParam struct { name string // name for this request to identify the request in log easily streamID uint32 // stream ID, automatically assigned if 0 method string // method, defaults to GET scheme string // scheme, defaults to http authority string // authority, defaults to backend server address path string // path, defaults to / header []hpack.HeaderField // additional request header fields body []byte // request body trailer []hpack.HeaderField // trailer part httpUpgrade bool // true if upgraded to HTTP/2 through HTTP Upgrade noEndStream bool // true if END_STREAM should not be sent } // wrapper for request body to set trailer part type chunkedBodyReader struct { trailer []hpack.HeaderField trailerWritten bool body io.Reader req *http.Request } func (cbr *chunkedBodyReader) Read(p []byte) (n int, err error) { // document says that we have to set http.Request.Trailer // after request was sent and before body returns EOF. if !cbr.trailerWritten { cbr.trailerWritten = true for _, h := range cbr.trailer { cbr.req.Trailer.Set(h.Name, h.Value) } } return cbr.body.Read(p) } func (st *serverTester) websocket(rp requestParam) *serverResponse { urlstring := st.url + "/echo" config, err := websocket.NewConfig(urlstring, st.url) if err != nil { st.t.Fatalf("websocket.NewConfig(%q, %q) returned error: %v", urlstring, st.url, err) } config.Header.Add("Test-Case", rp.name) for _, h := range rp.header { config.Header.Add(h.Name, h.Value) } ws, err := websocket.NewClient(config, st.conn) if err != nil { st.t.Fatalf("Error creating websocket client: %v", err) } if _, err := ws.Write(rp.body); err != nil { st.t.Fatalf("ws.Write() returned error: %v", err) } msg := make([]byte, 1024) var n int if n, err = ws.Read(msg); err != nil { st.t.Fatalf("ws.Read() returned error: %v", err) } res := &serverResponse{ body: msg[:n], } return res } func (st *serverTester) http1(rp requestParam) (*serverResponse, error) { method := "GET" if rp.method != "" { method = rp.method } var ( body io.Reader cbr *chunkedBodyReader ) if rp.body != nil { body = bytes.NewBuffer(rp.body) if len(rp.trailer) != 0 { cbr = &chunkedBodyReader{ trailer: rp.trailer, body: body, } body = cbr } } reqURL := st.url if rp.path != "" { u, err := url.Parse(st.url) if err != nil { st.t.Fatalf("Error parsing URL from st.url %v: %v", st.url, err) } u.Path = "" u.RawQuery = "" reqURL = u.String() + rp.path } ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() req, err := http.NewRequestWithContext(ctx, method, reqURL, body) if err != nil { return nil, err } for _, h := range rp.header { req.Header.Add(h.Name, h.Value) } req.Header.Add("Test-Case", rp.name) if cbr != nil { cbr.req = req // this makes request use chunked encoding req.ContentLength = -1 req.Trailer = make(http.Header) for _, h := range cbr.trailer { req.Trailer.Set(h.Name, "") } } if err := req.Write(st.conn); err != nil { return nil, err } resp, err := http.ReadResponse(bufio.NewReader(st.conn), req) if err != nil { return nil, err } respBody, err := io.ReadAll(resp.Body) if err != nil { return nil, err } resp.Body.Close() res := &serverResponse{ status: resp.StatusCode, header: resp.Header, body: respBody, connClose: resp.Close, } return res, nil } func (st *serverTester) http2(rp requestParam) (*serverResponse, error) { st.headerBlkBuf.Reset() st.header = make(http.Header) var id uint32 if rp.streamID != 0 { id = rp.streamID if id >= st.nextStreamID && id%2 == 1 { st.nextStreamID = id + 2 } } else { id = st.nextStreamID st.nextStreamID += 2 } if !st.h2PrefaceSent { st.h2PrefaceSent = true fmt.Fprint(st.conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") if err := st.fr.WriteSettings(); err != nil { return nil, err } } res := &serverResponse{ streamID: id, } streams := make(map[uint32]*serverResponse) streams[id] = res if !rp.httpUpgrade { method := "GET" if rp.method != "" { method = rp.method } _ = st.enc.WriteField(pair(":method", method)) scheme := "http" if rp.scheme != "" { scheme = rp.scheme } _ = st.enc.WriteField(pair(":scheme", scheme)) authority := st.authority if rp.authority != "" { authority = rp.authority } _ = st.enc.WriteField(pair(":authority", authority)) path := "/" if rp.path != "" { path = rp.path } _ = st.enc.WriteField(pair(":path", path)) _ = st.enc.WriteField(pair("test-case", rp.name)) for _, h := range rp.header { _ = st.enc.WriteField(h) } err := st.fr.WriteHeaders(http2.HeadersFrameParam{ StreamID: id, EndStream: len(rp.body) == 0 && len(rp.trailer) == 0 && !rp.noEndStream, EndHeaders: true, BlockFragment: st.headerBlkBuf.Bytes(), }) if err != nil { return nil, err } if len(rp.body) != 0 { // TODO we assume rp.body fits in 1 frame if err := st.fr.WriteData(id, len(rp.trailer) == 0 && !rp.noEndStream, rp.body); err != nil { return nil, err } } if len(rp.trailer) != 0 { st.headerBlkBuf.Reset() for _, h := range rp.trailer { _ = st.enc.WriteField(h) } err := st.fr.WriteHeaders(http2.HeadersFrameParam{ StreamID: id, EndStream: true, EndHeaders: true, BlockFragment: st.headerBlkBuf.Bytes(), }) if err != nil { return nil, err } } } loop: for { fr, err := st.readFrame() if err != nil { return res, err } switch f := fr.(type) { case *http2.HeadersFrame: _, err := st.dec.Write(f.HeaderBlockFragment()) if err != nil { return res, err } sr, ok := streams[f.StreamID] if !ok { st.header = make(http.Header) break } sr.header = cloneHeader(st.header) var status int status, err = strconv.Atoi(sr.header.Get(":status")) if err != nil { return res, fmt.Errorf("could not parse :status: %w", err) } sr.status = status if f.StreamEnded() && streamEnded(res, streams, sr) { break loop } case *http2.PushPromiseFrame: _, err := st.dec.Write(f.HeaderBlockFragment()) if err != nil { return res, err } sr := &serverResponse{ streamID: f.PromiseID, reqHeader: cloneHeader(st.header), } streams[sr.streamID] = sr case *http2.DataFrame: sr, ok := streams[f.StreamID] if !ok { break } sr.body = append(sr.body, f.Data()...) if f.StreamEnded() && streamEnded(res, streams, sr) { break loop } case *http2.RSTStreamFrame: sr, ok := streams[f.StreamID] if !ok { break } sr.errCode = f.ErrCode if streamEnded(res, streams, sr) { break loop } case *http2.GoAwayFrame: if f.ErrCode == http2.ErrCodeNo { break } res.errCode = f.ErrCode res.connErr = true break loop case *http2.SettingsFrame: if f.IsAck() { break } if err := st.fr.WriteSettingsAck(); err != nil { return res, err } } } slices.SortFunc(res.pushResponse, func(a, b *serverResponse) int { return cmp.Compare(a.streamID, b.streamID) }) return res, nil } func streamEnded(mainSr *serverResponse, streams map[uint32]*serverResponse, sr *serverResponse) bool { delete(streams, sr.streamID) if mainSr.streamID != sr.streamID { mainSr.pushResponse = append(mainSr.pushResponse, sr) } return len(streams) == 0 } type serverResponse struct { status int // HTTP status code header http.Header // response header fields body []byte // response body streamID uint32 // stream ID in HTTP/2 errCode http2.ErrCode // error code received in HTTP/2 RST_STREAM or GOAWAY connErr bool // true if HTTP/2 connection error connClose bool // Connection: close is included in response header in HTTP/1 test reqHeader http.Header // http request header, currently only stores pushed request header pushResponse []*serverResponse // pushed response } func cloneHeader(h http.Header) http.Header { h2 := make(http.Header, len(h)) for k, vv := range h { vv2 := make([]string, len(vv)) copy(vv2, vv) h2[k] = vv2 } return h2 } func noopHandler(w http.ResponseWriter, r *http.Request) { if _, err := io.ReadAll(r.Body); err != nil { http.Error(w, fmt.Sprintf("Error io.ReadAll() = %v", err), http.StatusInternalServerError) } } type APIResponse struct { Status string `json:"status,omitempty"` Code int `json:"code,omitempty"` Data map[string]interface{} `json:"data,omitempty"` } type proxyProtocolV2 struct { command proxyProtocolV2Command sourceAddress net.Addr destinationAddress net.Addr additionalData []byte } type proxyProtocolV2Command int const ( proxyProtocolV2CommandLocal proxyProtocolV2Command = 0x0 proxyProtocolV2CommandProxy proxyProtocolV2Command = 0x1 ) type proxyProtocolV2Family int const ( proxyProtocolV2FamilyUnspec proxyProtocolV2Family = 0x0 proxyProtocolV2FamilyInet proxyProtocolV2Family = 0x1 proxyProtocolV2FamilyInet6 proxyProtocolV2Family = 0x2 proxyProtocolV2FamilyUnix proxyProtocolV2Family = 0x3 ) type proxyProtocolV2Protocol int const ( proxyProtocolV2ProtocolUnspec proxyProtocolV2Protocol = 0x0 proxyProtocolV2ProtocolStream proxyProtocolV2Protocol = 0x1 proxyProtocolV2ProtocolDgram proxyProtocolV2Protocol = 0x2 ) func writeProxyProtocolV2(w io.Writer, hdr proxyProtocolV2) error { if _, err := w.Write([]byte{0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A}); err != nil { return err } if _, err := w.Write([]byte{byte(0x20 | hdr.command)}); err != nil { return err } switch srcAddr := hdr.sourceAddress.(type) { case *net.TCPAddr: dstAddr := hdr.destinationAddress.(*net.TCPAddr) if len(srcAddr.IP) != len(dstAddr.IP) { panic("len(srcAddr.IP) != len(dstAddr.IP)") } var fam byte if len(srcAddr.IP) == 4 { fam = byte(proxyProtocolV2FamilyInet << 4) } else { fam = byte(proxyProtocolV2FamilyInet6 << 4) } fam |= byte(proxyProtocolV2ProtocolStream) if _, err := w.Write([]byte{fam}); err != nil { return err } length := uint16(len(srcAddr.IP)*2 + 4 + len(hdr.additionalData)) if err := binary.Write(w, binary.BigEndian, length); err != nil { return err } if _, err := w.Write(srcAddr.IP); err != nil { return err } if _, err := w.Write(dstAddr.IP); err != nil { return err } if err := binary.Write(w, binary.BigEndian, uint16(srcAddr.Port)); err != nil { return err } if err := binary.Write(w, binary.BigEndian, uint16(dstAddr.Port)); err != nil { return err } case *net.UnixAddr: dstAddr := hdr.destinationAddress.(*net.UnixAddr) if len(srcAddr.Name) > 108 { panic("too long Unix source address") } if len(dstAddr.Name) > 108 { panic("too long Unix destination address") } fam := byte(proxyProtocolV2FamilyUnix << 4) switch srcAddr.Net { case "unix": fam |= byte(proxyProtocolV2ProtocolStream) case "unixdgram": fam |= byte(proxyProtocolV2ProtocolDgram) default: fam |= byte(proxyProtocolV2ProtocolUnspec) } if _, err := w.Write([]byte{fam}); err != nil { return err } length := uint16(216 + len(hdr.additionalData)) if err := binary.Write(w, binary.BigEndian, length); err != nil { return err } zeros := make([]byte, 108) if _, err := w.Write([]byte(srcAddr.Name)); err != nil { return err } if _, err := w.Write(zeros[:108-len(srcAddr.Name)]); err != nil { return err } if _, err := w.Write([]byte(dstAddr.Name)); err != nil { return err } if _, err := w.Write(zeros[:108-len(dstAddr.Name)]); err != nil { return err } default: fam := byte(proxyProtocolV2FamilyUnspec<<4) | byte(proxyProtocolV2ProtocolUnspec) if _, err := w.Write([]byte{fam}); err != nil { return err } length := uint16(len(hdr.additionalData)) if err := binary.Write(w, binary.BigEndian, length); err != nil { return err } } if _, err := w.Write(hdr.additionalData); err != nil { return err } return nil } nghttp2-1.68.0/integration-tests/PaxHeaders/nghttpx_http1_test.go0000644000000000000000000000013215077107270022145 xustar0030 mtime=1761382072.974444227 30 atime=1761382106.420309475 30 ctime=1761382109.418299462 nghttp2-1.68.0/integration-tests/nghttpx_http1_test.go0000644000175100017510000013126515077107270022545 0ustar00runnerrunnerpackage nghttp2 import ( "bufio" "bytes" "encoding/json" "errors" "fmt" "io" "net/http" "regexp" "syscall" "testing" "time" "golang.org/x/net/http2/hpack" "golang.org/x/net/websocket" ) // TestH1H1PlainGET tests whether simple HTTP/1 GET request works. func TestH1H1PlainGET(t *testing.T) { st := newServerTester(t, options{}) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H1PlainGET", }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH1H1PlainGETClose tests whether simple HTTP/1 GET request with // Connection: close request header field works. func TestH1H1PlainGETClose(t *testing.T) { st := newServerTester(t, options{}) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H1PlainGETClose", header: []hpack.HeaderField{ pair("Connection", "close"), }, }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH1H1InvalidMethod tests that server rejects invalid method with // 501 status code func TestH1H1InvalidMethod(t *testing.T) { opts := options{ handler: func(http.ResponseWriter, *http.Request) { t.Errorf("server should not forward this request") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H1InvalidMethod", method: "get", }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusNotImplemented; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH1H1MultipleRequestCL tests that server rejects request which // contains multiple Content-Length header fields. func TestH1H1MultipleRequestCL(t *testing.T) { opts := options{ handler: func(http.ResponseWriter, *http.Request) { t.Errorf("server should not forward bad request") }, } st := newServerTester(t, opts) defer st.Close() if _, err := io.WriteString(st.conn, fmt.Sprintf("GET / HTTP/1.1\r\nHost: %v\r\nTest-Case: TestH1H1MultipleRequestCL\r\nContent-Length: 0\r\nContent-Length: 0\r\n\r\n", st.authority)); err != nil { t.Fatalf("Error io.WriteString() = %v", err) } resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) if err != nil { t.Fatalf("Error http.ReadResponse() = %v", err) } defer resp.Body.Close() if got, want := resp.StatusCode, http.StatusBadRequest; got != want { t.Errorf("status: %v; want %v", got, want) } } // // TestH1H1ConnectFailure tests that server handles the situation that // // connection attempt to HTTP/1 backend failed. // func TestH1H1ConnectFailure(t *testing.T) { // st := newServerTester(t, options{}) // defer st.Close() // // shutdown backend server to simulate backend connect failure // st.ts.Close() // res, err := st.http1(requestParam{ // name: "TestH1H1ConnectFailure", // }) // if err != nil { // t.Fatalf("Error st.http1() = %v", err) // } // want := 503 // if got := res.status; got != want { // t.Errorf("status: %v; want %v", got, want) // } // } // TestH1H1AffinityCookie tests that affinity cookie is sent back in // cleartext http. func TestH1H1AffinityCookie(t *testing.T) { opts := options{ args: []string{"--affinity-cookie"}, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H1AffinityCookie", }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar` validCookie := regexp.MustCompile(pattern) if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) { t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern) } } // TestH1H1AffinityCookieTLS tests that affinity cookie is sent back // in https. func TestH1H1AffinityCookieTLS(t *testing.T) { opts := options{ args: []string{"--alpn-h1", "--affinity-cookie"}, tls: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H1AffinityCookieTLS", }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar; Secure` validCookie := regexp.MustCompile(pattern) if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) { t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern) } } // TestH1H1GracefulShutdown tests graceful shutdown. func TestH1H1GracefulShutdown(t *testing.T) { st := newServerTester(t, options{}) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H1GracefulShutdown-1", }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } if err := st.cmd.Process.Signal(syscall.SIGQUIT); err != nil { t.Fatalf("Error st.cmd.Process.Signal() = %v", err) } time.Sleep(150 * time.Millisecond) res, err = st.http1(requestParam{ name: "TestH1H1GracefulShutdown-2", }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } if got, want := res.connClose, true; got != want { t.Errorf("res.connClose: %v; want %v", got, want) } want := io.EOF b := make([]byte, 256) if _, err := st.conn.Read(b); !errors.Is(err, want) { t.Errorf("st.conn.Read(): %v; want %v", err, want) } } // TestH1H1HostRewrite tests that server rewrites Host header field func TestH1H1HostRewrite(t *testing.T) { opts := options{ args: []string{"--host-rewrite"}, handler: func(w http.ResponseWriter, r *http.Request) { w.Header().Add("request-host", r.Host) }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H1HostRewrite", }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } if got, want := res.header.Get("request-host"), st.backendHost; got != want { t.Errorf("request-host: %v; want %v", got, want) } } // TestH1H1BadHost tests that server rejects request including bad // characters in host header field. func TestH1H1BadHost(t *testing.T) { opts := options{ handler: func(http.ResponseWriter, *http.Request) { t.Errorf("server should not forward this request") }, } st := newServerTester(t, opts) defer st.Close() if _, err := io.WriteString(st.conn, "GET / HTTP/1.1\r\nTest-Case: TestH1H1HBadHost\r\nHost: foo\"bar\r\n\r\n"); err != nil { t.Fatalf("Error io.WriteString() = %v", err) } resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) if err != nil { t.Fatalf("Error http.ReadResponse() = %v", err) } defer resp.Body.Close() if got, want := resp.StatusCode, http.StatusBadRequest; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH1H1BadAuthority tests that server rejects request including // bad characters in authority component of requset URI. func TestH1H1BadAuthority(t *testing.T) { opts := options{ handler: func(http.ResponseWriter, *http.Request) { t.Errorf("server should not forward this request") }, } st := newServerTester(t, opts) defer st.Close() if _, err := io.WriteString(st.conn, "GET http://foo\"bar/ HTTP/1.1\r\nTest-Case: TestH1H1HBadAuthority\r\nHost: foobar\r\n\r\n"); err != nil { t.Fatalf("Error io.WriteString() = %v", err) } resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) if err != nil { t.Fatalf("Error http.ReadResponse() = %v", err) } defer resp.Body.Close() if got, want := resp.StatusCode, http.StatusBadRequest; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH1H1BadScheme tests that server rejects request including // bad characters in scheme component of requset URI. func TestH1H1BadScheme(t *testing.T) { opts := options{ handler: func(http.ResponseWriter, *http.Request) { t.Errorf("server should not forward this request") }, } st := newServerTester(t, opts) defer st.Close() if _, err := io.WriteString(st.conn, "GET http*://example.com/ HTTP/1.1\r\nTest-Case: TestH1H1HBadScheme\r\nHost: example.com\r\n\r\n"); err != nil { t.Fatalf("Error io.WriteString() = %v", err) } resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) if err != nil { t.Fatalf("Error http.ReadResponse() = %v", err) } defer resp.Body.Close() if got, want := resp.StatusCode, http.StatusBadRequest; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH1H1HTTP10 tests that server can accept HTTP/1.0 request // without Host header field func TestH1H1HTTP10(t *testing.T) { opts := options{ handler: func(w http.ResponseWriter, r *http.Request) { w.Header().Add("request-host", r.Host) }, } st := newServerTester(t, opts) defer st.Close() if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1HTTP10\r\n\r\n"); err != nil { t.Fatalf("Error io.WriteString() = %v", err) } resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) if err != nil { t.Fatalf("Error http.ReadResponse() = %v", err) } defer resp.Body.Close() if got, want := resp.StatusCode, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { t.Errorf("request-host: %v; want %v", got, want) } } // TestH1H1HTTP10NoHostRewrite tests that server generates host header // field using actual backend server even if --no-http-rewrite is // used. func TestH1H1HTTP10NoHostRewrite(t *testing.T) { opts := options{ handler: func(w http.ResponseWriter, r *http.Request) { w.Header().Add("request-host", r.Host) }, } st := newServerTester(t, opts) defer st.Close() if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1HTTP10NoHostRewrite\r\n\r\n"); err != nil { t.Fatalf("Error io.WriteString() = %v", err) } resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) if err != nil { t.Fatalf("Error http.ReadResponse() = %v", err) } defer resp.Body.Close() if got, want := resp.StatusCode, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { t.Errorf("request-host: %v; want %v", got, want) } } // TestH1H1RequestTrailer tests request trailer part is forwarded to // backend. func TestH1H1RequestTrailer(t *testing.T) { opts := options{ handler: func(_ http.ResponseWriter, r *http.Request) { buf := make([]byte, 4096) for { _, err := r.Body.Read(buf) if err != nil { if errors.Is(err, io.EOF) { break } t.Fatalf("r.Body.Read() = %v", err) } } if got, want := r.Trailer.Get("foo"), "bar"; got != want { t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H1RequestTrailer", body: []byte("1"), trailer: []hpack.HeaderField{ pair("foo", "bar"), }, }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH1H1HeaderFieldBufferPath tests that request with request path // larger than configured buffer size is rejected. func TestH1H1HeaderFieldBufferPath(t *testing.T) { // The value 100 is chosen so that sum of header fields bytes // does not exceed it. We use > 100 bytes URI to exceed this // limit. opts := options{ args: []string{"--request-header-field-buffer=100"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatal("execution path should not be here") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H1HeaderFieldBufferPath", path: "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusRequestHeaderFieldsTooLarge; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH1H1HeaderFieldBuffer tests that request with header fields // larger than configured buffer size is rejected. func TestH1H1HeaderFieldBuffer(t *testing.T) { opts := options{ args: []string{"--request-header-field-buffer=10"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatal("execution path should not be here") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H1HeaderFieldBuffer", }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusRequestHeaderFieldsTooLarge; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH1H1HeaderFields tests that request with header fields more // than configured number is rejected. func TestH1H1HeaderFields(t *testing.T) { opts := options{ args: []string{"--max-request-header-fields=1"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatal("execution path should not be here") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H1HeaderFields", header: []hpack.HeaderField{ // Add extra header field to ensure that // header field limit exceeds pair("Connection", "close"), }, }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusRequestHeaderFieldsTooLarge; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH1H1Websocket tests that HTTP Upgrade to WebSocket works. func TestH1H1Websocket(t *testing.T) { opts := options{ handler: websocket.Handler(func(ws *websocket.Conn) { if _, err := io.Copy(ws, ws); err != nil { t.Fatalf("Error io.Copy() = %v", err) } }).ServeHTTP, } st := newServerTester(t, opts) defer st.Close() content := []byte("hello world") res := st.websocket(requestParam{ name: "TestH1H1Websocket", body: content, }) if got, want := res.body, content; !bytes.Equal(got, want) { t.Errorf("echo: %q; want %q", got, want) } } // TestH1H1ReqPhaseSetHeader tests mruby request phase hook // modifies request header fields. func TestH1H1ReqPhaseSetHeader(t *testing.T) { opts := options{ args: []string{"--mruby-file=" + testDir + "/req-set-header.rb"}, handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("User-Agent"), "mruby"; got != want { t.Errorf("User-Agent = %v; want %v", got, want) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H1ReqPhaseSetHeader", }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH1H1ReqPhaseReturn tests mruby request phase hook returns // custom response. func TestH1H1ReqPhaseReturn(t *testing.T) { opts := options{ args: []string{"--mruby-file=" + testDir + "/req-return.rb"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatalf("request should not be forwarded") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H1ReqPhaseReturn", }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusNotFound; got != want { t.Errorf("status = %v; want %v", got, want) } hdtests := []struct { k, v string }{ {"content-length", "20"}, {"from", "mruby"}, } for _, tt := range hdtests { if got, want := res.header.Get(tt.k), tt.v; got != want { t.Errorf("%v = %v; want %v", tt.k, got, want) } } if got, want := string(res.body), "Hello World from req"; got != want { t.Errorf("body = %v; want %v", got, want) } } // TestH1H1ReqPhaseReturnCONNECTMethod tests that mruby request phase // hook resets llhttp HPE_PAUSED_UPGRADE. func TestH1H1ReqPhaseReturnCONNECTMethod(t *testing.T) { opts := options{ args: []string{"--mruby-file=" + testDir + "/req-return.rb"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatalf("request should not be forwarded") }, } st := newServerTester(t, opts) defer st.Close() if _, err := io.WriteString(st.conn, "CONNECT 127.0.0.1:443 HTTP/1.1\r\nTest-Case: TestH1H1ReqPhaseReturnCONNECTMethod\r\nHost: 127.0.0.1:443\r\n\r\n"); err != nil { t.Fatalf("Error io.WriteString() = %v", err) } resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) if err != nil { t.Fatalf("Error http.ReadResponse() = %v", err) } defer resp.Body.Close() if got, want := resp.StatusCode, http.StatusNotFound; got != want { t.Errorf("status: %v; want %v", got, want) } hdCheck := func() { hdtests := []struct { k, v string }{ {"content-length", "20"}, {"from", "mruby"}, } for _, tt := range hdtests { if got, want := resp.Header.Get(tt.k), tt.v; got != want { t.Errorf("%v = %v; want %v", tt.k, got, want) } } if _, err := io.ReadAll(resp.Body); err != nil { t.Fatalf("Error io.ReadAll() = %v", err) } } hdCheck() if _, err := io.WriteString(st.conn, "CONNECT 127.0.0.1:443 HTTP/1.1\r\nTest-Case: TestH1H1ReqPhaseReturnCONNECTMethod\r\nHost: 127.0.0.1:443\r\n\r\n"); err != nil { t.Fatalf("Error io.WriteString() = %v", err) } resp, err = http.ReadResponse(bufio.NewReader(st.conn), nil) if err != nil { t.Fatalf("Error http.ReadResponse() = %v", err) } defer resp.Body.Close() if got, want := resp.StatusCode, http.StatusNotFound; got != want { t.Errorf("status: %v; want %v", got, want) } hdCheck() if _, err := io.ReadAll(resp.Body); err != nil { t.Fatalf("Error io.ReadAll() = %v", err) } } // TestH1H1RespPhaseSetHeader tests mruby response phase hook modifies // response header fields. func TestH1H1RespPhaseSetHeader(t *testing.T) { opts := options{ args: []string{"--mruby-file=" + testDir + "/resp-set-header.rb"}, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H1RespPhaseSetHeader", }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } if got, want := res.header.Get("alpha"), "bravo"; got != want { t.Errorf("alpha = %v; want %v", got, want) } } // TestH1H1RespPhaseReturn tests mruby response phase hook returns // custom response. func TestH1H1RespPhaseReturn(t *testing.T) { opts := options{ args: []string{"--mruby-file=" + testDir + "/resp-return.rb"}, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H1RespPhaseReturn", }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusNotFound; got != want { t.Errorf("status = %v; want %v", got, want) } hdtests := []struct { k, v string }{ {"content-length", "21"}, {"from", "mruby"}, } for _, tt := range hdtests { if got, want := res.header.Get(tt.k), tt.v; got != want { t.Errorf("%v = %v; want %v", tt.k, got, want) } } if got, want := string(res.body), "Hello World from resp"; got != want { t.Errorf("body = %v; want %v", got, want) } } // TestH1H1HTTPSRedirect tests that the request to the backend which // requires TLS is redirected to https URI. func TestH1H1HTTPSRedirect(t *testing.T) { opts := options{ args: []string{"--redirect-if-not-tls"}, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H1HTTPSRedirect", }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusPermanentRedirect; got != want { t.Errorf("status = %v; want %v", got, want) } if got, want := res.header.Get("location"), "https://127.0.0.1/"; got != want { t.Errorf("location: %v; want %v", got, want) } } // TestH1H1HTTPSRedirectPort tests that the request to the backend // which requires TLS is redirected to https URI with given port. func TestH1H1HTTPSRedirectPort(t *testing.T) { opts := options{ args: []string{ "--redirect-if-not-tls", "--redirect-https-port=8443", }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ path: "/foo?bar", name: "TestH1H1HTTPSRedirectPort", }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusPermanentRedirect; got != want { t.Errorf("status = %v; want %v", got, want) } if got, want := res.header.Get("location"), "https://127.0.0.1:8443/foo?bar"; got != want { t.Errorf("location: %v; want %v", got, want) } } // TestH1H1POSTRequests tests that server can handle 2 requests with // request body. func TestH1H1POSTRequests(t *testing.T) { st := newServerTester(t, options{}) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H1POSTRequestsNo1", body: make([]byte, 1), }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } res, err = st.http1(requestParam{ name: "TestH1H1POSTRequestsNo2", body: make([]byte, 65536), }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH1H1CONNECTMethodFailure tests that CONNECT method failure // resets llhttp HPE_PAUSED_UPGRADE. func TestH1H1CONNECTMethodFailure(t *testing.T) { opts := options{ handler: func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("required-header") == "" { w.WriteHeader(http.StatusNotFound) } }, } st := newServerTester(t, opts) defer st.Close() if _, err := io.WriteString(st.conn, "CONNECT 127.0.0.1:443 HTTP/1.1\r\nTest-Case: TestH1H1CONNECTMethodFailure\r\nHost: 127.0.0.1:443\r\n\r\n"); err != nil { t.Fatalf("Error io.WriteString() = %v", err) } resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) if err != nil { t.Fatalf("Error http.ReadResponse() = %v", err) } defer resp.Body.Close() if got, want := resp.StatusCode, http.StatusNotFound; got != want { t.Errorf("status: %v; want %v", got, want) } if _, err := io.ReadAll(resp.Body); err != nil { t.Fatalf("Error io.ReadAll() = %v", err) } } // TestH1H1CONNECTMethod tests that CONNECT request succeeds. func TestH1H1CONNECTMethod(t *testing.T) { opts := options{ handler: func(w http.ResponseWriter, r *http.Request) { hj, ok := w.(http.Hijacker) if !ok { http.Error(w, "Could not hijack the connection", http.StatusInternalServerError) return } _, bufrw, err := hj.Hijack() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if _, err := bufrw.WriteString("HTTP/1.1 200\r\n\r\n"); err != nil { t.Fatalf("Error bufrw.WriteString() = %v", err) } bufrw.Flush() }, } st := newServerTester(t, opts) defer st.Close() if _, err := io.WriteString(st.conn, "CONNECT 127.0.0.1:443 HTTP/1.1\r\nTest-Case: TestH1H1CONNECTMethod\r\nHost: 127.0.0.1:443\r\n\r\n"); err != nil { t.Fatalf("Error io.WriteString() = %v", err) } resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) if err != nil { t.Fatalf("Error http.ReadResponse() = %v", err) } defer resp.Body.Close() if got, want := resp.StatusCode, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } } // // TestH1H2ConnectFailure tests that server handles the situation that // // connection attempt to HTTP/2 backend failed. // func TestH1H2ConnectFailure(t *testing.T) { // opts := options{ // args: []string{"--http2-bridge"}, // } // st := newServerTester(t, opts) // defer st.Close() // // simulate backend connect attempt failure // st.ts.Close() // res, err := st.http1(requestParam{ // name: "TestH1H2ConnectFailure", // }) // if err != nil { // t.Fatalf("Error st.http1() = %v", err) // } // want := 503 // if got := res.status; got != want { // t.Errorf("status: %v; want %v", got, want) // } // } // TestH1H2NoHost tests that server rejects request without Host // header field for HTTP/2 backend. func TestH1H2NoHost(t *testing.T) { opts := options{ args: []string{"--http2-bridge"}, handler: func(http.ResponseWriter, *http.Request) { t.Errorf("server should not forward bad request") }, } st := newServerTester(t, opts) defer st.Close() // without Host header field, we expect 400 response if _, err := io.WriteString(st.conn, "GET / HTTP/1.1\r\nTest-Case: TestH1H2NoHost\r\n\r\n"); err != nil { t.Fatalf("Error io.WriteString() = %v", err) } resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) if err != nil { t.Fatalf("Error http.ReadResponse() = %v", err) } defer resp.Body.Close() if got, want := resp.StatusCode, http.StatusBadRequest; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH1H2HTTP10 tests that server can accept HTTP/1.0 request // without Host header field func TestH1H2HTTP10(t *testing.T) { opts := options{ args: []string{"--http2-bridge"}, handler: func(w http.ResponseWriter, r *http.Request) { w.Header().Add("request-host", r.Host) }, } st := newServerTester(t, opts) defer st.Close() if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10\r\n\r\n"); err != nil { t.Fatalf("Error io.WriteString() = %v", err) } resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) if err != nil { t.Fatalf("Error http.ReadResponse() = %v", err) } defer resp.Body.Close() if got, want := resp.StatusCode, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { t.Errorf("request-host: %v; want %v", got, want) } } // TestH1H2HTTP10NoHostRewrite tests that server generates host header // field using actual backend server even if --no-http-rewrite is // used. func TestH1H2HTTP10NoHostRewrite(t *testing.T) { opts := options{ args: []string{"--http2-bridge"}, handler: func(w http.ResponseWriter, r *http.Request) { w.Header().Add("request-host", r.Host) }, } st := newServerTester(t, opts) defer st.Close() if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10NoHostRewrite\r\n\r\n"); err != nil { t.Fatalf("Error io.WriteString() = %v", err) } resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) if err != nil { t.Fatalf("Error http.ReadResponse() = %v", err) } defer resp.Body.Close() if got, want := resp.StatusCode, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { t.Errorf("request-host: %v; want %v", got, want) } } // TestH1H2CrumbleCookie tests that Cookies are crumbled and assembled // when forwarding to HTTP/2 backend link. go-nghttp2 server // concatenates crumbled Cookies automatically, so this test is not // much effective now. func TestH1H2CrumbleCookie(t *testing.T) { opts := options{ args: []string{"--http2-bridge"}, handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("Cookie"), "alpha; bravo; charlie"; got != want { t.Errorf("Cookie: %v; want %v", got, want) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H2CrumbleCookie", header: []hpack.HeaderField{ pair("Cookie", "alpha; bravo; charlie"), }, }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH1H2GenerateVia tests that server generates Via header field to and // from backend server. func TestH1H2GenerateVia(t *testing.T) { opts := options{ args: []string{"--http2-bridge"}, handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("Via"), "1.1 nghttpx"; got != want { t.Errorf("Via: %v; want %v", got, want) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H2GenerateVia", }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.header.Get("Via"), "2 nghttpx"; got != want { t.Errorf("Via: %v; want %v", got, want) } } // TestH1H2AppendVia tests that server adds value to existing Via // header field to and from backend server. func TestH1H2AppendVia(t *testing.T) { opts := options{ args: []string{"--http2-bridge"}, handler: func(w http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("Via"), "foo, 1.1 nghttpx"; got != want { t.Errorf("Via: %v; want %v", got, want) } w.Header().Add("Via", "bar") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H2AppendVia", header: []hpack.HeaderField{ pair("via", "foo"), }, }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.header.Get("Via"), "bar, 2 nghttpx"; got != want { t.Errorf("Via: %v; want %v", got, want) } } // TestH1H2NoVia tests that server does not add value to existing Via // header field to and from backend server. func TestH1H2NoVia(t *testing.T) { opts := options{ args: []string{"--http2-bridge", "--no-via"}, handler: func(w http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("Via"), "foo"; got != want { t.Errorf("Via: %v; want %v", got, want) } w.Header().Add("Via", "bar") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H2NoVia", header: []hpack.HeaderField{ pair("via", "foo"), }, }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.header.Get("Via"), "bar"; got != want { t.Errorf("Via: %v; want %v", got, want) } } // TestH1H2ReqPhaseReturn tests mruby request phase hook returns // custom response. func TestH1H2ReqPhaseReturn(t *testing.T) { opts := options{ args: []string{ "--http2-bridge", "--mruby-file=" + testDir + "/req-return.rb", }, handler: func(http.ResponseWriter, *http.Request) { t.Fatalf("request should not be forwarded") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H2ReqPhaseReturn", }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusNotFound; got != want { t.Errorf("status = %v; want %v", got, want) } hdtests := []struct { k, v string }{ {"content-length", "20"}, {"from", "mruby"}, } for _, tt := range hdtests { if got, want := res.header.Get(tt.k), tt.v; got != want { t.Errorf("%v = %v; want %v", tt.k, got, want) } } if got, want := string(res.body), "Hello World from req"; got != want { t.Errorf("body = %v; want %v", got, want) } } // TestH1H2RespPhaseReturn tests mruby response phase hook returns // custom response. func TestH1H2RespPhaseReturn(t *testing.T) { opts := options{ args: []string{ "--http2-bridge", "--mruby-file=" + testDir + "/resp-return.rb", }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H2RespPhaseReturn", }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusNotFound; got != want { t.Errorf("status = %v; want %v", got, want) } hdtests := []struct { k, v string }{ {"content-length", "21"}, {"from", "mruby"}, } for _, tt := range hdtests { if got, want := res.header.Get(tt.k), tt.v; got != want { t.Errorf("%v = %v; want %v", tt.k, got, want) } } if got, want := string(res.body), "Hello World from resp"; got != want { t.Errorf("body = %v; want %v", got, want) } } // TestH1H2TE tests that "te: trailers" header is forwarded to HTTP/2 // backend server by stripping other encodings. func TestH1H2TE(t *testing.T) { opts := options{ args: []string{"--http2-bridge"}, handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("te"), "trailers"; got != want { t.Errorf("te: %v; want %v", got, want) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H2TE", header: []hpack.HeaderField{ pair("te", "foo,trailers,bar"), }, }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH1APIBackendconfig exercise backendconfig API endpoint routine // for successful case. func TestH1APIBackendconfig(t *testing.T) { opts := options{ args: []string{"-f127.0.0.1,3010;api;no-tls"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatalf("request should not be forwarded") }, connectPort: 3010, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1APIBackendconfig", path: "/api/v1beta1/backendconfig", method: "PUT", body: []byte(`# comment backend=127.0.0.1,3011 `), }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } var apiResp APIResponse if err := json.Unmarshal(res.body, &apiResp); err != nil { t.Fatalf("Error unmarshaling API response: %v", err) } if got, want := apiResp.Status, "Success"; got != want { t.Errorf("apiResp.Status: %v; want %v", got, want) } if got, want := apiResp.Code, 200; got != want { t.Errorf("apiResp.Status: %v; want %v", got, want) } } // TestH1APIBackendconfigQuery exercise backendconfig API endpoint // routine with query. func TestH1APIBackendconfigQuery(t *testing.T) { opts := options{ args: []string{"-f127.0.0.1,3010;api;no-tls"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatalf("request should not be forwarded") }, connectPort: 3010, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1APIBackendconfigQuery", path: "/api/v1beta1/backendconfig?foo=bar", method: "PUT", body: []byte(`# comment backend=127.0.0.1,3011 `), }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } var apiResp APIResponse if err := json.Unmarshal(res.body, &apiResp); err != nil { t.Fatalf("Error unmarshaling API response: %v", err) } if got, want := apiResp.Status, "Success"; got != want { t.Errorf("apiResp.Status: %v; want %v", got, want) } if got, want := apiResp.Code, 200; got != want { t.Errorf("apiResp.Status: %v; want %v", got, want) } } // TestH1APIBackendconfigBadMethod exercise backendconfig API endpoint // routine with bad method. func TestH1APIBackendconfigBadMethod(t *testing.T) { opts := options{ args: []string{"-f127.0.0.1,3010;api;no-tls"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatalf("request should not be forwarded") }, connectPort: 3010, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1APIBackendconfigBadMethod", path: "/api/v1beta1/backendconfig", method: "GET", body: []byte(`# comment backend=127.0.0.1,3011 `), }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusMethodNotAllowed; got != want { t.Errorf("res.status: %v; want %v", got, want) } var apiResp APIResponse if err := json.Unmarshal(res.body, &apiResp); err != nil { t.Fatalf("Error unmarshaling API response: %v", err) } if got, want := apiResp.Status, "Failure"; got != want { t.Errorf("apiResp.Status: %v; want %v", got, want) } if got, want := apiResp.Code, 405; got != want { t.Errorf("apiResp.Status: %v; want %v", got, want) } } // TestH1APIConfigrevision tests configrevision API. func TestH1APIConfigrevision(t *testing.T) { opts := options{ args: []string{"-f127.0.0.1,3010;api;no-tls"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatalf("request should not be forwarded") }, connectPort: 3010, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1APIConfigrevision", path: "/api/v1beta1/configrevision", method: "GET", }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want = %v", got, want) } var apiResp APIResponse d := json.NewDecoder(bytes.NewBuffer(res.body)) d.UseNumber() if err := d.Decode(&apiResp); err != nil { t.Fatalf("Error unmarshalling API response: %v", err) } if got, want := apiResp.Status, "Success"; got != want { t.Errorf("apiResp.Status: %v; want %v", got, want) } if got, want := apiResp.Code, 200; got != want { t.Errorf("apiResp.Status: %v; want %v", got, want) } if got, want := apiResp.Data["configRevision"], json.Number("0"); got != want { t.Errorf(`apiResp.Data["configRevision"]: %v %t; want %v`, got, got, want) } } // TestH1APINotFound exercise backendconfig API endpoint routine when // API endpoint is not found. func TestH1APINotFound(t *testing.T) { opts := options{ args: []string{"-f127.0.0.1,3010;api;no-tls"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatalf("request should not be forwarded") }, connectPort: 3010, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1APINotFound", path: "/api/notfound", method: "GET", body: []byte(`# comment backend=127.0.0.1,3011 `), }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusNotFound; got != want { t.Errorf("res.status: %v; want %v", got, want) } var apiResp APIResponse if err := json.Unmarshal(res.body, &apiResp); err != nil { t.Fatalf("Error unmarshaling API response: %v", err) } if got, want := apiResp.Status, "Failure"; got != want { t.Errorf("apiResp.Status: %v; want %v", got, want) } if got, want := apiResp.Code, 404; got != want { t.Errorf("apiResp.Status: %v; want %v", got, want) } } // TestH1Healthmon tests health monitor endpoint. func TestH1Healthmon(t *testing.T) { opts := options{ args: []string{"-f127.0.0.1,3011;healthmon;no-tls"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatalf("request should not be forwarded") }, connectPort: 3011, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1Healthmon", path: "/alpha/bravo", }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH1ResponseBeforeRequestEnd tests the situation where response // ends before request body finishes. func TestH1ResponseBeforeRequestEnd(t *testing.T) { opts := options{ args: []string{"--mruby-file=" + testDir + "/req-return.rb"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatal("request should not be forwarded") }, } st := newServerTester(t, opts) defer st.Close() if _, err := io.WriteString(st.conn, fmt.Sprintf("POST / HTTP/1.1\r\nHost: %v\r\nTest-Case: TestH1ResponseBeforeRequestEnd\r\nContent-Length: 1000000\r\n\r\n", st.authority)); err != nil { t.Fatalf("Error io.WriteString() = %v", err) } resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) if err != nil { t.Fatalf("Error http.ReadResponse() = %v", err) } defer resp.Body.Close() if got, want := resp.StatusCode, http.StatusNotFound; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH1H1ChunkedEndsPrematurely tests that an HTTP/1.1 request fails // if the backend chunked encoded response ends prematurely. func TestH1H1ChunkedEndsPrematurely(t *testing.T) { opts := options{ handler: func(w http.ResponseWriter, _ *http.Request) { hj, ok := w.(http.Hijacker) if !ok { http.Error(w, "Could not hijack the connection", http.StatusInternalServerError) return } conn, bufrw, err := hj.Hijack() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer conn.Close() if _, err := bufrw.WriteString("HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\n\r\n"); err != nil { t.Fatalf("Error bufrw.WriteString() = %v", err) } bufrw.Flush() }, } st := newServerTester(t, opts) defer st.Close() _, err := st.http1(requestParam{ name: "TestH1H1ChunkedEndsPrematurely", }) if err == nil { t.Fatal("st.http1() should fail") } } // TestH1H1RequestMalformedTransferEncoding tests that server rejects // request which contains malformed transfer-encoding. func TestH1H1RequestMalformedTransferEncoding(t *testing.T) { opts := options{ handler: func(http.ResponseWriter, *http.Request) { t.Errorf("server should not forward bad request") }, } st := newServerTester(t, opts) defer st.Close() if _, err := io.WriteString(st.conn, fmt.Sprintf("GET / HTTP/1.1\r\nHost: %v\r\nTest-Case: TestH1H1RequestMalformedTransferEncoding\r\nTransfer-Encoding: ,chunked\r\n\r\n", st.authority)); err != nil { t.Fatalf("Error io.WriteString() = %v", err) } resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) if err != nil { t.Fatalf("Error http.ReadResponse() = %v", err) } defer resp.Body.Close() if got, want := resp.StatusCode, http.StatusBadRequest; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH1H1ResponseMalformedTransferEncoding tests a request fails if // its response contains malformed transfer-encoding. func TestH1H1ResponseMalformedTransferEncoding(t *testing.T) { opts := options{ handler: func(w http.ResponseWriter, _ *http.Request) { hj, ok := w.(http.Hijacker) if !ok { http.Error(w, "Could not hijack the connection", http.StatusInternalServerError) return } conn, bufrw, err := hj.Hijack() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer conn.Close() if _, err := bufrw.WriteString("HTTP/1.1 200\r\nTransfer-Encoding: ,chunked\r\n\r\n"); err != nil { t.Fatalf("Error bufrw.WriteString() = %v", err) } bufrw.Flush() }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http1(requestParam{ name: "TestH1H1ResponseMalformedTransferEncoding", }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusBadGateway; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH1H1ResponseUnknownTransferEncoding tests a request succeeds if // its response contains unknown transfer-encoding. func TestH1H1ResponseUnknownTransferEncoding(t *testing.T) { opts := options{ handler: func(w http.ResponseWriter, _ *http.Request) { hj, ok := w.(http.Hijacker) if !ok { http.Error(w, "Could not hijack the connection", http.StatusInternalServerError) return } conn, bufrw, err := hj.Hijack() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer conn.Close() if _, err := bufrw.WriteString("HTTP/1.1 200\r\nTransfer-Encoding: foo\r\n\r\n"); err != nil { t.Fatalf("Error bufrw.WriteString() = %v", err) } bufrw.Flush() }, } st := newServerTester(t, opts) defer st.Close() if _, err := io.WriteString(st.conn, fmt.Sprintf("GET / HTTP/1.1\r\nHost: %v\r\nTest-Case: TestH1H1ResponseUnknownTransferEncoding\r\n\r\n", st.authority)); err != nil { t.Fatalf("Error: io.WriteString() = %v", err) } r := bufio.NewReader(st.conn) resp := make([]byte, 4096) resplen, err := r.Read(resp) if err != nil { t.Fatalf("Error: r.Read() = %v", err) } resp = resp[:resplen] const expect = "HTTP/1.1 200 OK\r\nTransfer-Encoding: foo\r\nConnection: close\r\nServer: nghttpx\r\nVia: 1.1 nghttpx\r\n\r\n" if got, want := string(resp), expect; got != want { t.Errorf("resp = %v, want %v", got, want) } } // TestH1H1RequestHTTP10TransferEncoding tests that server rejects // HTTP/1.0 request which contains transfer-encoding. func TestH1H1RequestHTTP10TransferEncoding(t *testing.T) { opts := options{ handler: func(http.ResponseWriter, *http.Request) { t.Errorf("server should not forward bad request") }, } st := newServerTester(t, opts) defer st.Close() if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1RequestHTTP10TransferEncoding\r\nTransfer-Encoding: chunked\r\n\r\n"); err != nil { t.Fatalf("Error io.WriteString() = %v", err) } resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) if err != nil { t.Fatalf("Error http.ReadResponse() = %v", err) } defer resp.Body.Close() if got, want := resp.StatusCode, http.StatusBadRequest; got != want { t.Errorf("status: %v; want %v", got, want) } } nghttp2-1.68.0/integration-tests/PaxHeaders/alt-server.key0000644000000000000000000000013215077107270020541 xustar0030 mtime=1761382072.973444231 30 atime=1761382106.429309436 30 ctime=1761382109.427299436 nghttp2-1.68.0/integration-tests/alt-server.key0000644000175100017510000000325415077107270021135 0ustar00runnerrunner-----BEGIN PRIVATE KEY----- MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDQjCEM4YOKkasl D0ihFLM92RT8iLhomAYoeVeaKfKbjZfRUjchbrVEu84dGUdWZUclCMW4OjbH7LzW 302UODccZRtKnM7OmbpK2K60HtziGt5PkbivRgrJrW6CJ6Y3T6mqMg8labaNWd/j jx5m9QpEoRD1bQwtmOI2NUYqsqXCjt8iVeDg3f5iLEth98BPU1V3VPpXmPOVuu6P XmAracaMxD0PWHW329NtPFj6a2TvZpfRjrggTqk8FYsVJIfyjNSJ0OgvcBnDPYmR 6v4AsorwIzi6Gj9E+nJbULTocf4ct0fYyoSMM5E51783PGbrrw6Ar8lYYgTJSpiJ RfHTKEXHAgMBAAECggEBALTrjFSXY72YB+h7rN+JjMIwDIPUvF6I3HbKZhQpJf6K xNVkRM2tNHavku0tm/S4ohLf3F+pqRKiL2Udjjjy1+S7VgTRqpwTQ0lhV5aNW8SP 2KMg4R61XfB+k+s4KHu9kYxEJ12mqydPe+r3o0FgfYryTDsOYk1AX6b1aqzqFOGF 7GaqLALSbKU59tcJJ1SZNBbpIKFUrAT9nZt9dW02/foqP5bzUk43Yjw48xmLwegc bMXXcpZhNZSktltvwRw7Q4Foc9kuRlMdTAnAD9PnMCcZwicS/YeVVF6Rz4fGviKv 7/kPHQ7g4YpFktVDzuZ5xw6GDVFeJ6uGMVUX8+EePvkCgYEA+/nrcn82nFHCxm8Q 0iiUhi/AoXjZg+O5Ytaje9O/YNoX+c4ywe13h0+TXKH79O0KfTwXeJyDgPZbAIFV 9oURellRYUzKDafnBHis2f+Ywn6GqHL5e2X30ZxIp1GK46pcvne1YuvJhgGmiVay vd7sRx09OKU124dG22rIFCis6asCgYEA0+CsA6LrEwQ/aPJYASY3VHNO/WoAOnPg Cwsg+02XWsPEwP//lNmpanz8TUm2URS063ZK8bx7t3ejvDgBdsRwwjiMlDp7XTUU 3Zk+mhCV2qkMi02aKemvz29bDhmh5JoH7W3IwsXtJYO0yZDYrDR3ioiKRccioPoE b/Nq781sEFUCgYEA4xqx9xRpaCLY5nicNI6WrwrDF8YQZisNn+PMnYKP7v8itOgA H4GkRbSXINpueKZc2dsbXH3UmJtyEdaAYBw3UIrIKmZHhl9afFE3mZQhXssjGxfl fC6/WZD+eq+n+uJFjPXf6jSSAdHjA828dB1D4CSeVTuyexZF6uUnR+QRVNkCgYEA i+pb7XLSpZYygY03zFp+Q0h6KyKqz+7hTqmkuA8/GfMZpRHop1UtaWLsAeXhfZ2c 87kEOKptUHSzLYIWhWWnyLorK1+LQ7vf8Y5XJso5C1KDNCKk4XSuYt94U9FddWa6 QXI0F1s5BYL6Cfma++0R2+va08Vy+rbf40XtojoXWJkCgYEA0hMQSCvok7is27nQ G80KXfmghU2eEB7zif3T00/fwJycxEbmnNeof+SKmhdY4ZgqTscfOxlQPflV/eqB xs4GnFDDeM0F8KH0BimOXxr7sJPFCg22PCCQQcRtM/KoU+ip/kNmTfwrsC0xMFPU HD8M1JCZF2eLMekXXP3cB0U4sUs= -----END PRIVATE KEY----- nghttp2-1.68.0/integration-tests/PaxHeaders/nghttpx_http2_test.go0000644000000000000000000000013215077107270022146 xustar0030 mtime=1761382072.974444227 30 atime=1761382106.422309466 30 ctime=1761382109.420299456 nghttp2-1.68.0/integration-tests/nghttpx_http2_test.go0000644000175100017510000030467315077107270022553 0ustar00runnerrunnerpackage nghttp2 import ( "bytes" "crypto/tls" "encoding/json" "errors" "fmt" "io" "net" "net/http" "regexp" "strings" "syscall" "testing" "time" "golang.org/x/net/http2" "golang.org/x/net/http2/hpack" ) // TestH2H1PlainGET tests whether simple HTTP/2 GET request works. func TestH2H1PlainGET(t *testing.T) { st := newServerTester(t, options{}) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1PlainGET", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H1AddXfp tests that server appends :scheme to the existing // x-forwarded-proto header field. func TestH2H1AddXfp(t *testing.T) { opts := options{ args: []string{"--no-strip-incoming-x-forwarded-proto"}, handler: func(_ http.ResponseWriter, r *http.Request) { xfp := r.Header.Get("X-Forwarded-Proto") if got, want := xfp, "foo, http"; got != want { t.Errorf("X-Forwarded-Proto = %q; want %q", got, want) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1AddXfp", header: []hpack.HeaderField{ pair("x-forwarded-proto", "foo"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H1NoAddXfp tests that server does not append :scheme to the // existing x-forwarded-proto header field. func TestH2H1NoAddXfp(t *testing.T) { opts := options{ args: []string{ "--no-add-x-forwarded-proto", "--no-strip-incoming-x-forwarded-proto", }, handler: func(_ http.ResponseWriter, r *http.Request) { xfp := r.Header.Get("X-Forwarded-Proto") if got, want := xfp, "foo"; got != want { t.Errorf("X-Forwarded-Proto = %q; want %q", got, want) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1NoAddXfp", header: []hpack.HeaderField{ pair("x-forwarded-proto", "foo"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H1StripXfp tests that server strips incoming // x-forwarded-proto header field. func TestH2H1StripXfp(t *testing.T) { opts := options{ handler: func(_ http.ResponseWriter, r *http.Request) { xfp := r.Header.Get("X-Forwarded-Proto") if got, want := xfp, "http"; got != want { t.Errorf("X-Forwarded-Proto = %q; want %q", got, want) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1StripXfp", header: []hpack.HeaderField{ pair("x-forwarded-proto", "foo"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H1StripNoAddXfp tests that server strips incoming // x-forwarded-proto header field, and does not add another. func TestH2H1StripNoAddXfp(t *testing.T) { opts := options{ args: []string{"--no-add-x-forwarded-proto"}, handler: func(_ http.ResponseWriter, r *http.Request) { if got, found := r.Header["X-Forwarded-Proto"]; found { t.Errorf("X-Forwarded-Proto = %q; want nothing", got) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1StripNoAddXfp", header: []hpack.HeaderField{ pair("x-forwarded-proto", "foo"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H1AddXff tests that server generates X-Forwarded-For header // field when forwarding request to backend. func TestH2H1AddXff(t *testing.T) { opts := options{ args: []string{"--add-x-forwarded-for"}, handler: func(_ http.ResponseWriter, r *http.Request) { xff := r.Header.Get("X-Forwarded-For") want := "127.0.0.1" if xff != want { t.Errorf("X-Forwarded-For = %v; want %v", xff, want) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1AddXff", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H1AddXff2 tests that server appends X-Forwarded-For header // field to existing one when forwarding request to backend. func TestH2H1AddXff2(t *testing.T) { opts := options{ args: []string{"--add-x-forwarded-for"}, handler: func(_ http.ResponseWriter, r *http.Request) { xff := r.Header.Get("X-Forwarded-For") want := "host, 127.0.0.1" if xff != want { t.Errorf("X-Forwarded-For = %v; want %v", xff, want) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1AddXff2", header: []hpack.HeaderField{ pair("x-forwarded-for", "host"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H1StripXff tests that --strip-incoming-x-forwarded-for // option. func TestH2H1StripXff(t *testing.T) { opts := options{ args: []string{"--strip-incoming-x-forwarded-for"}, handler: func(_ http.ResponseWriter, r *http.Request) { if xff, found := r.Header["X-Forwarded-For"]; found { t.Errorf("X-Forwarded-For = %v; want nothing", xff) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1StripXff", header: []hpack.HeaderField{ pair("x-forwarded-for", "host"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H1StripAddXff tests that --strip-incoming-x-forwarded-for and // --add-x-forwarded-for options. func TestH2H1StripAddXff(t *testing.T) { opts := options{ args: []string{ "--strip-incoming-x-forwarded-for", "--add-x-forwarded-for", }, handler: func(_ http.ResponseWriter, r *http.Request) { xff := r.Header.Get("X-Forwarded-For") want := "127.0.0.1" if xff != want { t.Errorf("X-Forwarded-For = %v; want %v", xff, want) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1StripAddXff", header: []hpack.HeaderField{ pair("x-forwarded-for", "host"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H1AddForwardedObfuscated tests that server generates // Forwarded header field with obfuscated "by" and "for" parameters. func TestH2H1AddForwardedObfuscated(t *testing.T) { opts := options{ args: []string{"--add-forwarded=by,for,host,proto"}, handler: func(_ http.ResponseWriter, r *http.Request) { pattern := fmt.Sprintf(`by=_[^;]+;for=_[^;]+;host="127\.0\.0\.1:%v";proto=http`, serverPort) validFwd := regexp.MustCompile(pattern) got := r.Header.Get("Forwarded") if !validFwd.MatchString(got) { t.Errorf("Forwarded = %v; want pattern %v", got, pattern) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1AddForwardedObfuscated", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH2H1AddForwardedByIP tests that server generates Forwarded header // field with IP address in "by" parameter. func TestH2H1AddForwardedByIP(t *testing.T) { opts := options{ args: []string{"--add-forwarded=by,for", "--forwarded-by=ip"}, handler: func(_ http.ResponseWriter, r *http.Request) { pattern := fmt.Sprintf(`by="127\.0\.0\.1:%v";for=_[^;]+`, serverPort) validFwd := regexp.MustCompile(pattern) if got := r.Header.Get("Forwarded"); !validFwd.MatchString(got) { t.Errorf("Forwarded = %v; want pattern %v", got, pattern) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1AddForwardedByIP", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH2H1AddForwardedForIP tests that server generates Forwarded header // field with IP address in "for" parameters. func TestH2H1AddForwardedForIP(t *testing.T) { opts := options{ args: []string{ "--add-forwarded=by,for,host,proto", "--forwarded-by=_alpha", "--forwarded-for=ip", }, handler: func(_ http.ResponseWriter, r *http.Request) { want := fmt.Sprintf(`by=_alpha;for=127.0.0.1;host="127.0.0.1:%v";proto=http`, serverPort) if got := r.Header.Get("Forwarded"); got != want { t.Errorf("Forwarded = %v; want %v", got, want) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1AddForwardedForIP", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH2H1AddForwardedMerge tests that server generates Forwarded // header field with IP address in "by" and "for" parameters. The // generated values must be appended to the existing value. func TestH2H1AddForwardedMerge(t *testing.T) { opts := options{ args: []string{"--add-forwarded=proto"}, handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("Forwarded"), `host=foo, proto=http`; got != want { t.Errorf("Forwarded = %v; want %v", got, want) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1AddForwardedMerge", header: []hpack.HeaderField{ pair("forwarded", "host=foo"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH2H1AddForwardedStrip tests that server generates Forwarded // header field with IP address in "by" and "for" parameters. The // generated values must not include the existing value. func TestH2H1AddForwardedStrip(t *testing.T) { opts := options{ args: []string{ "--strip-incoming-forwarded", "--add-forwarded=proto", }, handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("Forwarded"), `proto=http`; got != want { t.Errorf("Forwarded = %v; want %v", got, want) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1AddForwardedStrip", header: []hpack.HeaderField{ pair("forwarded", "host=foo"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH2H1StripForwarded tests that server strips incoming Forwarded // header field. func TestH2H1StripForwarded(t *testing.T) { opts := options{ args: []string{"--strip-incoming-forwarded"}, handler: func(_ http.ResponseWriter, r *http.Request) { if got, found := r.Header["Forwarded"]; found { t.Errorf("Forwarded = %v; want nothing", got) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1StripForwarded", header: []hpack.HeaderField{ pair("forwarded", "host=foo"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH2H1AddForwardedStatic tests that server generates Forwarded // header field with the given static obfuscated string for "by" // parameter. func TestH2H1AddForwardedStatic(t *testing.T) { opts := options{ args: []string{ "--add-forwarded=by,for", "--forwarded-by=_alpha", }, handler: func(_ http.ResponseWriter, r *http.Request) { pattern := `by=_alpha;for=_[^;]+` validFwd := regexp.MustCompile(pattern) if got := r.Header.Get("Forwarded"); !validFwd.MatchString(got) { t.Errorf("Forwarded = %v; want pattern %v", got, pattern) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1AddForwardedStatic", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH2H1GenerateVia tests that server generates Via header field to and // from backend server. func TestH2H1GenerateVia(t *testing.T) { opts := options{ handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("Via"), "2 nghttpx"; got != want { t.Errorf("Via: %v; want %v", got, want) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1GenerateVia", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.header.Get("Via"), "1.1 nghttpx"; got != want { t.Errorf("Via: %v; want %v", got, want) } } // TestH2H1AppendVia tests that server adds value to existing Via // header field to and from backend server. func TestH2H1AppendVia(t *testing.T) { opts := options{ handler: func(w http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("Via"), "foo, 2 nghttpx"; got != want { t.Errorf("Via: %v; want %v", got, want) } w.Header().Add("Via", "bar") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1AppendVia", header: []hpack.HeaderField{ pair("via", "foo"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.header.Get("Via"), "bar, 1.1 nghttpx"; got != want { t.Errorf("Via: %v; want %v", got, want) } } // TestH2H1NoVia tests that server does not add value to existing Via // header field to and from backend server. func TestH2H1NoVia(t *testing.T) { opts := options{ args: []string{"--no-via"}, handler: func(w http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("Via"), "foo"; got != want { t.Errorf("Via: %v; want %v", got, want) } w.Header().Add("Via", "bar") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1NoVia", header: []hpack.HeaderField{ pair("via", "foo"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.header.Get("Via"), "bar"; got != want { t.Errorf("Via: %v; want %v", got, want) } } // TestH2H1HostRewrite tests that server rewrites host header field func TestH2H1HostRewrite(t *testing.T) { opts := options{ args: []string{"--host-rewrite"}, handler: func(w http.ResponseWriter, r *http.Request) { w.Header().Add("request-host", r.Host) }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1HostRewrite", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } if got, want := res.header.Get("request-host"), st.backendHost; got != want { t.Errorf("request-host: %v; want %v", got, want) } } // TestH2H1NoHostRewrite tests that server does not rewrite host // header field func TestH2H1NoHostRewrite(t *testing.T) { opts := options{ handler: func(w http.ResponseWriter, r *http.Request) { w.Header().Add("request-host", r.Host) }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1NoHostRewrite", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } if got, want := res.header.Get("request-host"), st.frontendHost; got != want { t.Errorf("request-host: %v; want %v", got, want) } } // TestH2H1BadRequestCL tests that server rejects request whose // content-length header field value does not match its request body // size. func TestH2H1BadRequestCL(t *testing.T) { st := newServerTester(t, options{}) defer st.Close() // we set content-length: 1024, but the actual request body is // 3 bytes. res, err := st.http2(requestParam{ name: "TestH2H1BadRequestCL", method: "POST", header: []hpack.HeaderField{ pair("content-length", "1024"), }, body: []byte("foo"), }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } want := http2.ErrCodeProtocol if res.errCode != want { t.Errorf("res.errCode = %v; want %v", res.errCode, want) } } // TestH2H1BadResponseCL tests that server returns error when // content-length response header field value does not match its // response body size. func TestH2H1BadResponseCL(t *testing.T) { opts := options{ handler: func(w http.ResponseWriter, _ *http.Request) { // we set content-length: 1024, but only send 3 bytes. w.Header().Add("Content-Length", "1024") if _, err := w.Write([]byte("foo")); err != nil { t.Fatalf("Error w.Write() = %v", err) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1BadResponseCL", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } want := http2.ErrCodeInternal if res.errCode != want { t.Errorf("res.errCode = %v; want %v", res.errCode, want) } } // TestH2H1LocationRewrite tests location header field rewriting // works. func TestH2H1LocationRewrite(t *testing.T) { opts := options{ handler: func(w http.ResponseWriter, _ *http.Request) { // TODO we cannot get st.ts's port number // here.. 8443 is just a place holder. We // ignore it on rewrite. w.Header().Add("Location", "http://127.0.0.1:8443/p/q?a=b#fragment") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1LocationRewrite", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } want := fmt.Sprintf("http://127.0.0.1:%v/p/q?a=b#fragment", serverPort) if got := res.header.Get("Location"); got != want { t.Errorf("Location: %v; want %v", got, want) } } // TestH2H1ChunkedRequestBody tests that chunked request body works. func TestH2H1ChunkedRequestBody(t *testing.T) { opts := options{ handler: func(_ http.ResponseWriter, r *http.Request) { want := "[chunked]" if got := fmt.Sprint(r.TransferEncoding); got != want { t.Errorf("Transfer-Encoding: %v; want %v", got, want) } body, err := io.ReadAll(r.Body) if err != nil { t.Fatalf("Error reading r.body: %v", err) } want = "foo" if got := string(body); got != want { t.Errorf("body: %v; want %v", got, want) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1ChunkedRequestBody", method: "POST", body: []byte("foo"), }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H1MultipleRequestCL tests that server rejects request with // multiple Content-Length request header fields. func TestH2H1MultipleRequestCL(t *testing.T) { opts := options{ handler: func(http.ResponseWriter, *http.Request) { t.Errorf("server should not forward bad request") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1MultipleRequestCL", header: []hpack.HeaderField{ pair("content-length", "1"), pair("content-length", "1"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.errCode, http2.ErrCodeProtocol; got != want { t.Errorf("res.errCode: %v; want %v", got, want) } } // TestH2H1InvalidRequestCL tests that server rejects request with // Content-Length which cannot be parsed as a number. func TestH2H1InvalidRequestCL(t *testing.T) { opts := options{ handler: func(http.ResponseWriter, *http.Request) { t.Errorf("server should not forward bad request") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1InvalidRequestCL", header: []hpack.HeaderField{ pair("content-length", ""), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.errCode, http2.ErrCodeProtocol; got != want { t.Errorf("res.errCode: %v; want %v", got, want) } } // // TestH2H1ConnectFailure tests that server handles the situation that // // connection attempt to HTTP/1 backend failed. // func TestH2H1ConnectFailure(t *testing.T) { // st := newServerTester(t, options{}) // defer st.Close() // // shutdown backend server to simulate backend connect failure // st.ts.Close() // res, err := st.http2(requestParam{ // name: "TestH2H1ConnectFailure", // }) // if err != nil { // t.Fatalf("Error st.http2() = %v", err) // } // want := 503 // if got := res.status; got != want { // t.Errorf("status: %v; want %v", got, want) // } // } // TestH2H1InvalidMethod tests that server rejects invalid method with // 501. func TestH2H1InvalidMethod(t *testing.T) { opts := options{ handler: func(http.ResponseWriter, *http.Request) { t.Errorf("server should not forward this request") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1InvalidMethod", method: "get", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusNotImplemented; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH2H1BadAuthority tests that server rejects request including // bad characters in :authority header field. func TestH2H1BadAuthority(t *testing.T) { opts := options{ handler: func(http.ResponseWriter, *http.Request) { t.Errorf("server should not forward this request") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1BadAuthority", authority: `foo\bar`, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.errCode, http2.ErrCodeProtocol; got != want { t.Errorf("res.errCode: %v; want %v", got, want) } } // TestH2H1BadScheme tests that server rejects request including // bad characters in :scheme header field. func TestH2H1BadScheme(t *testing.T) { opts := options{ handler: func(http.ResponseWriter, *http.Request) { t.Errorf("server should not forward this request") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1BadScheme", scheme: "http*", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.errCode, http2.ErrCodeProtocol; got != want { t.Errorf("res.errCode: %v; want %v", got, want) } } // TestH2H1AssembleCookies tests that crumbled cookies in HTTP/2 // request is assembled into 1 when forwarding to HTTP/1 backend link. func TestH2H1AssembleCookies(t *testing.T) { opts := options{ handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("Cookie"), "alpha; bravo; charlie"; got != want { t.Errorf("Cookie: %v; want %v", got, want) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1AssembleCookies", header: []hpack.HeaderField{ pair("cookie", "alpha"), pair("cookie", "bravo"), pair("cookie", "charlie"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH2H1TETrailers tests that server accepts TE request header // field if it has trailers only. func TestH2H1TETrailers(t *testing.T) { st := newServerTester(t, options{}) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1TETrailers", header: []hpack.HeaderField{ pair("te", "trailers"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH2H1TEGzip tests that server resets stream if TE request header // field contains gzip. func TestH2H1TEGzip(t *testing.T) { opts := options{ handler: func(http.ResponseWriter, *http.Request) { t.Error("server should not forward bad request") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1TEGzip", header: []hpack.HeaderField{ pair("te", "gzip"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.errCode, http2.ErrCodeProtocol; got != want { t.Errorf("res.errCode = %v; want %v", res.errCode, want) } } // TestH2H1SNI tests server's TLS SNI extension feature. It must // choose appropriate certificate depending on the indicated // server_name from client. func TestH2H1SNI(t *testing.T) { opts := options{ args: []string{"--subcert=" + testDir + "/alt-server.key:" + testDir + "/alt-server.crt"}, tls: true, tlsConfig: &tls.Config{ ServerName: "alt-domain", }, } st := newServerTester(t, opts) defer st.Close() tlsConn := st.conn.(*tls.Conn) connState := tlsConn.ConnectionState() cert := connState.PeerCertificates[0] if got, want := cert.Subject.CommonName, "alt-domain"; got != want { t.Errorf("CommonName: %v; want %v", got, want) } } // TestH2H1TLSXfp tests nghttpx sends x-forwarded-proto header field // with http value since :scheme is http, even if the frontend // connection is encrypted. func TestH2H1TLSXfp(t *testing.T) { opts := options{ handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("x-forwarded-proto"), "http"; got != want { t.Errorf("x-forwarded-proto: want %v; got %v", want, got) } }, tls: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1TLSXfp", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH2H1ServerPush tests server push using Link header field from // backend server. func TestH2H1ServerPush(t *testing.T) { opts := options{ handler: func(w http.ResponseWriter, r *http.Request) { // only resources marked as rel=preload are pushed if !strings.HasPrefix(r.URL.Path, "/css/") { w.Header().Add("Link", "; rel=preload, , ; rel=preload") } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1ServerPush", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } if got, want := len(res.pushResponse), 2; got != want { t.Fatalf("len(res.pushResponse): %v; want %v", got, want) } mainCSS := res.pushResponse[0] if got, want := mainCSS.status, http.StatusOK; got != want { t.Errorf("mainCSS.status: %v; want %v", got, want) } themeCSS := res.pushResponse[1] if got, want := themeCSS.status, http.StatusOK; got != want { t.Errorf("themeCSS.status: %v; want %v", got, want) } } // TestH2H1RequestTrailer tests request trailer part is forwarded to // backend. func TestH2H1RequestTrailer(t *testing.T) { opts := options{ handler: func(_ http.ResponseWriter, r *http.Request) { buf := make([]byte, 4096) for { _, err := r.Body.Read(buf) if err != nil { if errors.Is(err, io.EOF) { break } t.Fatalf("r.Body.Read() = %v", err) } } if got, want := r.Trailer.Get("foo"), "bar"; got != want { t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1RequestTrailer", body: []byte("1"), trailer: []hpack.HeaderField{ pair("foo", "bar"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH2H1HeaderFieldBuffer tests that request with header fields // larger than configured buffer size is rejected. func TestH2H1HeaderFieldBuffer(t *testing.T) { opts := options{ args: []string{"--request-header-field-buffer=10"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatal("execution path should not be here") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1HeaderFieldBuffer", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusRequestHeaderFieldsTooLarge; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH2H1HeaderFields tests that request with header fields more // than configured number is rejected. func TestH2H1HeaderFields(t *testing.T) { opts := options{ args: []string{"--max-request-header-fields=1"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatal("execution path should not be here") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1HeaderFields", // we have at least 4 pseudo-header fields sent, and // that ensures that buffer limit exceeds. }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusRequestHeaderFieldsTooLarge; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH2H1ReqPhaseSetHeader tests mruby request phase hook // modifies request header fields. func TestH2H1ReqPhaseSetHeader(t *testing.T) { opts := options{ args: []string{"--mruby-file=" + testDir + "/req-set-header.rb"}, handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("User-Agent"), "mruby"; got != want { t.Errorf("User-Agent = %v; want %v", got, want) } }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1ReqPhaseSetHeader", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H1ReqPhaseReturn tests mruby request phase hook returns // custom response. func TestH2H1ReqPhaseReturn(t *testing.T) { opts := options{ args: []string{"--mruby-file=" + testDir + "/req-return.rb"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatalf("request should not be forwarded") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1ReqPhaseReturn", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusNotFound; got != want { t.Errorf("status = %v; want %v", got, want) } hdtests := []struct { k, v string }{ {"content-length", "20"}, {"from", "mruby"}, } for _, tt := range hdtests { if got, want := res.header.Get(tt.k), tt.v; got != want { t.Errorf("%v = %v; want %v", tt.k, got, want) } } if got, want := string(res.body), "Hello World from req"; got != want { t.Errorf("body = %v; want %v", got, want) } } // TestH2H1RespPhaseSetHeader tests mruby response phase hook modifies // response header fields. func TestH2H1RespPhaseSetHeader(t *testing.T) { opts := options{ args: []string{"--mruby-file=" + testDir + "/resp-set-header.rb"}, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1RespPhaseSetHeader", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } if got, want := res.header.Get("alpha"), "bravo"; got != want { t.Errorf("alpha = %v; want %v", got, want) } } // TestH2H1RespPhaseReturn tests mruby response phase hook returns // custom response. func TestH2H1RespPhaseReturn(t *testing.T) { opts := options{ args: []string{"--mruby-file=" + testDir + "/resp-return.rb"}, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1RespPhaseReturn", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusNotFound; got != want { t.Errorf("status = %v; want %v", got, want) } hdtests := []struct { k, v string }{ {"content-length", "21"}, {"from", "mruby"}, } for _, tt := range hdtests { if got, want := res.header.Get(tt.k), tt.v; got != want { t.Errorf("%v = %v; want %v", tt.k, got, want) } } if got, want := string(res.body), "Hello World from resp"; got != want { t.Errorf("body = %v; want %v", got, want) } } // TestH2H1Upgrade tests HTTP Upgrade to HTTP/2 func TestH2H1Upgrade(t *testing.T) { st := newServerTester(t, options{}) defer st.Close() res, err := st.http1(requestParam{ name: "TestH2H1Upgrade", header: []hpack.HeaderField{ pair("Connection", "Upgrade, HTTP2-Settings"), pair("Upgrade", "h2c"), pair("HTTP2-Settings", "AAMAAABkAAQAAP__"), }, }) if err != nil { t.Fatalf("Error st.http1() = %v", err) } if got, want := res.status, http.StatusSwitchingProtocols; got != want { t.Errorf("res.status: %v; want %v", got, want) } res, err = st.http2(requestParam{ httpUpgrade: true, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH2H1ProxyProtocolV1ForwardedForObfuscated tests that Forwarded // header field includes obfuscated address even if PROXY protocol // version 1 containing TCP4 entry is accepted. func TestH2H1ProxyProtocolV1ForwardedForObfuscated(t *testing.T) { pattern := `^for=_[^;]+$` validFwd := regexp.MustCompile(pattern) opts := options{ args: []string{ "--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=obfuscated", }, handler: func(_ http.ResponseWriter, r *http.Request) { if got := r.Header.Get("Forwarded"); !validFwd.MatchString(got) { t.Errorf("Forwarded: %v; want pattern %v", got, pattern) } }, } st := newServerTester(t, opts) defer st.Close() if _, err := st.conn.Write([]byte("PROXY TCP4 192.168.0.2 192.168.0.100 12345 8080\r\n")); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } res, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1ForwardedForObfuscated", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH2H1ProxyProtocolV1TCP4 tests PROXY protocol version 1 // containing TCP4 entry is accepted and X-Forwarded-For contains // advertised src address. func TestH2H1ProxyProtocolV1TCP4(t *testing.T) { opts := options{ args: []string{ "--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip", }, handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("X-Forwarded-For"), "192.168.0.2"; got != want { t.Errorf("X-Forwarded-For: %v; want %v", got, want) } if got, want := r.Header.Get("Forwarded"), "for=192.168.0.2"; got != want { t.Errorf("Forwarded: %v; want %v", got, want) } }, } st := newServerTester(t, opts) defer st.Close() if _, err := st.conn.Write([]byte("PROXY TCP4 192.168.0.2 192.168.0.100 12345 8080\r\n")); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } res, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1TCP4", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH2H1ProxyProtocolV1TCP6 tests PROXY protocol version 1 // containing TCP6 entry is accepted and X-Forwarded-For contains // advertised src address. func TestH2H1ProxyProtocolV1TCP6(t *testing.T) { opts := options{ args: []string{ "--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip", }, handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("X-Forwarded-For"), "2001:0db8:85a3:0000:0000:8a2e:0370:7334"; got != want { t.Errorf("X-Forwarded-For: %v; want %v", got, want) } if got, want := r.Header.Get("Forwarded"), `for="[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"`; got != want { t.Errorf("Forwarded: %v; want %v", got, want) } }, } st := newServerTester(t, opts) defer st.Close() if _, err := st.conn.Write([]byte("PROXY TCP6 2001:0db8:85a3:0000:0000:8a2e:0370:7334 ::1 12345 8080\r\n")); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } res, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1TCP6", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH2H1ProxyProtocolV1TCP4TLS tests PROXY protocol version 1 over // TLS containing TCP4 entry is accepted and X-Forwarded-For contains // advertised src address. func TestH2H1ProxyProtocolV1TCP4TLS(t *testing.T) { opts := options{ args: []string{ "--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip", }, handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("X-Forwarded-For"), "192.168.0.2"; got != want { t.Errorf("X-Forwarded-For: %v; want %v", got, want) } if got, want := r.Header.Get("Forwarded"), "for=192.168.0.2"; got != want { t.Errorf("Forwarded: %v; want %v", got, want) } }, tls: true, tcpData: []byte("PROXY TCP4 192.168.0.2 192.168.0.100 12345 8080\r\n"), } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1TCP4TLS", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH2H1ProxyProtocolV1TCP6TLS tests PROXY protocol version 1 over // TLS containing TCP6 entry is accepted and X-Forwarded-For contains // advertised src address. func TestH2H1ProxyProtocolV1TCP6TLS(t *testing.T) { opts := options{ args: []string{ "--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip", }, handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("X-Forwarded-For"), "2001:0db8:85a3:0000:0000:8a2e:0370:7334"; got != want { t.Errorf("X-Forwarded-For: %v; want %v", got, want) } if got, want := r.Header.Get("Forwarded"), `for="[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"`; got != want { t.Errorf("Forwarded: %v; want %v", got, want) } }, tls: true, tcpData: []byte("PROXY TCP6 2001:0db8:85a3:0000:0000:8a2e:0370:7334 ::1 12345 8080\r\n"), } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1TCP6TLS", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH2H1ProxyProtocolV1Unknown tests PROXY protocol version 1 // containing UNKNOWN entry is accepted. func TestH2H1ProxyProtocolV1Unknown(t *testing.T) { opts := options{ args: []string{ "--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip", }, handler: func(_ http.ResponseWriter, r *http.Request) { if got, notWant := r.Header.Get("X-Forwarded-For"), "192.168.0.2"; got == notWant { t.Errorf("X-Forwarded-For: %v; want something else", got) } if got, notWant := r.Header.Get("Forwarded"), "for=192.168.0.2"; got == notWant { t.Errorf("Forwarded: %v; want something else", got) } }, } st := newServerTester(t, opts) defer st.Close() if _, err := st.conn.Write([]byte("PROXY UNKNOWN 192.168.0.2 192.168.0.100 12345 8080\r\n")); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } res, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1Unknown", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH2H1ProxyProtocolV1JustUnknown tests PROXY protocol version 1 // containing only "PROXY UNKNOWN" is accepted. func TestH2H1ProxyProtocolV1JustUnknown(t *testing.T) { opts := options{ args: []string{ "--accept-proxy-protocol", "--add-x-forwarded-for", }, } st := newServerTester(t, opts) defer st.Close() if _, err := st.conn.Write([]byte("PROXY UNKNOWN\r\n")); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } res, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1JustUnknown", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH2H1ProxyProtocolV1TooLongLine tests PROXY protocol version 1 // line longer than 107 bytes must be rejected func TestH2H1ProxyProtocolV1TooLongLine(t *testing.T) { opts := options{ args: []string{ "--accept-proxy-protocol", "--add-x-forwarded-for", }, } st := newServerTester(t, opts) defer st.Close() if _, err := st.conn.Write([]byte("PROXY UNKNOWN ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 655350\r\n")); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } _, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1TooLongLine", }) if err == nil { t.Fatalf("connection was not terminated") } } // TestH2H1ProxyProtocolV1BadLineEnd tests that PROXY protocol version // 1 line ending without \r\n should be rejected. func TestH2H1ProxyProtocolV1BadLineEnd(t *testing.T) { opts := options{ args: []string{"--accept-proxy-protocol"}, } st := newServerTester(t, opts) defer st.Close() if _, err := st.conn.Write([]byte("PROXY TCP6 ::1 ::1 12345 8080\r \n")); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } _, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1BadLineEnd", }) if err == nil { t.Fatalf("connection was not terminated") } } // TestH2H1ProxyProtocolV1NoEnd tests that PROXY protocol version 1 // line containing no \r\n should be rejected. func TestH2H1ProxyProtocolV1NoEnd(t *testing.T) { opts := options{ args: []string{"--accept-proxy-protocol"}, } st := newServerTester(t, opts) defer st.Close() if _, err := st.conn.Write([]byte("PROXY TCP6 ::1 ::1 12345 8080")); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } _, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1NoEnd", }) if err == nil { t.Fatalf("connection was not terminated") } } // TestH2H1ProxyProtocolV1EmbeddedNULL tests that PROXY protocol // version 1 line containing NULL character should be rejected. func TestH2H1ProxyProtocolV1EmbeddedNULL(t *testing.T) { opts := options{ args: []string{"--accept-proxy-protocol"}, } st := newServerTester(t, opts) defer st.Close() b := []byte("PROXY TCP6 ::1*foo ::1 12345 8080\r\n") b[14] = 0 if _, err := st.conn.Write(b); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } _, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1EmbeddedNULL", }) if err == nil { t.Fatalf("connection was not terminated") } } // TestH2H1ProxyProtocolV1MissingSrcPort tests that PROXY protocol // version 1 line without src port should be rejected. func TestH2H1ProxyProtocolV1MissingSrcPort(t *testing.T) { opts := options{ args: []string{"--accept-proxy-protocol"}, } st := newServerTester(t, opts) defer st.Close() if _, err := st.conn.Write([]byte("PROXY TCP6 ::1 ::1 8080\r\n")); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } _, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1MissingSrcPort", }) if err == nil { t.Fatalf("connection was not terminated") } } // TestH2H1ProxyProtocolV1MissingDstPort tests that PROXY protocol // version 1 line without dst port should be rejected. func TestH2H1ProxyProtocolV1MissingDstPort(t *testing.T) { opts := options{ args: []string{"--accept-proxy-protocol"}, } st := newServerTester(t, opts) defer st.Close() if _, err := st.conn.Write([]byte("PROXY TCP6 ::1 ::1 12345 \r\n")); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } _, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1MissingDstPort", }) if err == nil { t.Fatalf("connection was not terminated") } } // TestH2H1ProxyProtocolV1InvalidSrcPort tests that PROXY protocol // containing invalid src port should be rejected. func TestH2H1ProxyProtocolV1InvalidSrcPort(t *testing.T) { opts := options{ args: []string{"--accept-proxy-protocol"}, } st := newServerTester(t, opts) defer st.Close() if _, err := st.conn.Write([]byte("PROXY TCP6 ::1 ::1 123x 8080\r\n")); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } _, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1InvalidSrcPort", }) if err == nil { t.Fatalf("connection was not terminated") } } // TestH2H1ProxyProtocolV1InvalidDstPort tests that PROXY protocol // containing invalid dst port should be rejected. func TestH2H1ProxyProtocolV1InvalidDstPort(t *testing.T) { opts := options{ args: []string{"--accept-proxy-protocol"}, } st := newServerTester(t, opts) defer st.Close() if _, err := st.conn.Write([]byte("PROXY TCP6 ::1 ::1 123456 80x\r\n")); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } _, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1InvalidDstPort", }) if err == nil { t.Fatalf("connection was not terminated") } } // TestH2H1ProxyProtocolV1LeadingZeroPort tests that PROXY protocol // version 1 line with non zero port with leading zero should be // rejected. func TestH2H1ProxyProtocolV1LeadingZeroPort(t *testing.T) { opts := options{ args: []string{"--accept-proxy-protocol"}, } st := newServerTester(t, opts) defer st.Close() if _, err := st.conn.Write([]byte("PROXY TCP6 ::1 ::1 03000 8080\r\n")); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } _, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1LeadingZeroPort", }) if err == nil { t.Fatalf("connection was not terminated") } } // TestH2H1ProxyProtocolV1TooLargeSrcPort tests that PROXY protocol // containing too large src port should be rejected. func TestH2H1ProxyProtocolV1TooLargeSrcPort(t *testing.T) { opts := options{ args: []string{"--accept-proxy-protocol"}, } st := newServerTester(t, opts) defer st.Close() if _, err := st.conn.Write([]byte("PROXY TCP6 ::1 ::1 65536 8080\r\n")); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } _, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1TooLargeSrcPort", }) if err == nil { t.Fatalf("connection was not terminated") } } // TestH2H1ProxyProtocolV1TooLargeDstPort tests that PROXY protocol // containing too large dst port should be rejected. func TestH2H1ProxyProtocolV1TooLargeDstPort(t *testing.T) { opts := options{ args: []string{"--accept-proxy-protocol"}, } st := newServerTester(t, opts) defer st.Close() if _, err := st.conn.Write([]byte("PROXY TCP6 ::1 ::1 12345 65536\r\n")); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } _, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1TooLargeDstPort", }) if err == nil { t.Fatalf("connection was not terminated") } } // TestH2H1ProxyProtocolV1InvalidSrcAddr tests that PROXY protocol // containing invalid src addr should be rejected. func TestH2H1ProxyProtocolV1InvalidSrcAddr(t *testing.T) { opts := options{ args: []string{"--accept-proxy-protocol"}, } st := newServerTester(t, opts) defer st.Close() if _, err := st.conn.Write([]byte("PROXY TCP6 192.168.0.1 ::1 12345 8080\r\n")); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } _, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1InvalidSrcAddr", }) if err == nil { t.Fatalf("connection was not terminated") } } // TestH2H1ProxyProtocolV1InvalidDstAddr tests that PROXY protocol // containing invalid dst addr should be rejected. func TestH2H1ProxyProtocolV1InvalidDstAddr(t *testing.T) { opts := options{ args: []string{"--accept-proxy-protocol"}, } st := newServerTester(t, opts) defer st.Close() if _, err := st.conn.Write([]byte("PROXY TCP6 ::1 192.168.0.1 12345 8080\r\n")); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } _, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1InvalidDstAddr", }) if err == nil { t.Fatalf("connection was not terminated") } } // TestH2H1ProxyProtocolV1InvalidProtoFamily tests that PROXY protocol // containing invalid protocol family should be rejected. func TestH2H1ProxyProtocolV1InvalidProtoFamily(t *testing.T) { opts := options{ args: []string{"--accept-proxy-protocol"}, } st := newServerTester(t, opts) defer st.Close() if _, err := st.conn.Write([]byte("PROXY UNIX ::1 ::1 12345 8080\r\n")); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } _, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1InvalidProtoFamily", }) if err == nil { t.Fatalf("connection was not terminated") } } // TestH2H1ProxyProtocolV1InvalidID tests that PROXY protocol // containing invalid PROXY protocol version 1 ID should be rejected. func TestH2H1ProxyProtocolV1InvalidID(t *testing.T) { opts := options{ args: []string{"--accept-proxy-protocol"}, } st := newServerTester(t, opts) defer st.Close() if _, err := st.conn.Write([]byte("PR0XY TCP6 ::1 ::1 12345 8080\r\n")); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } _, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV1InvalidID", }) if err == nil { t.Fatalf("connection was not terminated") } } // TestH2H1ProxyProtocolV2TCP4 tests PROXY protocol version 2 // containing AF_INET family is accepted and X-Forwarded-For contains // advertised src address. func TestH2H1ProxyProtocolV2TCP4(t *testing.T) { opts := options{ args: []string{ "--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip", }, handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("X-Forwarded-For"), "192.168.0.2"; got != want { t.Errorf("X-Forwarded-For: %v; want %v", got, want) } if got, want := r.Header.Get("Forwarded"), "for=192.168.0.2"; got != want { t.Errorf("Forwarded: %v; want %v", got, want) } }, } st := newServerTester(t, opts) defer st.Close() var b bytes.Buffer if err := writeProxyProtocolV2(&b, proxyProtocolV2{ command: proxyProtocolV2CommandProxy, sourceAddress: &net.TCPAddr{ IP: net.ParseIP("192.168.0.2").To4(), Port: 12345, }, destinationAddress: &net.TCPAddr{ IP: net.ParseIP("192.168.0.100").To4(), Port: 8080, }, additionalData: []byte("foobar"), }); err != nil { t.Fatalf("Error writeProxyProtocolV2() = %v", err) } if _, err := st.conn.Write(b.Bytes()); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } res, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV2TCP4", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH2H1ProxyProtocolV2TCP6 tests PROXY protocol version 2 // containing AF_INET6 family is accepted and X-Forwarded-For contains // advertised src address. func TestH2H1ProxyProtocolV2TCP6(t *testing.T) { opts := options{ args: []string{ "--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip", }, handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("X-Forwarded-For"), "2001:db8:85a3::8a2e:370:7334"; got != want { t.Errorf("X-Forwarded-For: %v; want %v", got, want) } if got, want := r.Header.Get("Forwarded"), `for="[2001:db8:85a3::8a2e:370:7334]"`; got != want { t.Errorf("Forwarded: %v; want %v", got, want) } }, } st := newServerTester(t, opts) defer st.Close() var b bytes.Buffer if err := writeProxyProtocolV2(&b, proxyProtocolV2{ command: proxyProtocolV2CommandProxy, sourceAddress: &net.TCPAddr{ IP: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), Port: 12345, }, destinationAddress: &net.TCPAddr{ IP: net.ParseIP("::1"), Port: 8080, }, additionalData: []byte("foobar"), }); err != nil { t.Fatalf("Error writeProxyProtocolV2() = %v", err) } if _, err := st.conn.Write(b.Bytes()); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } res, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV2TCP6", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH2H1ProxyProtocolV2TCP4TLS tests PROXY protocol version 2 over // TLS containing AF_INET family is accepted and X-Forwarded-For // contains advertised src address. func TestH2H1ProxyProtocolV2TCP4TLS(t *testing.T) { var v2Hdr bytes.Buffer if err := writeProxyProtocolV2(&v2Hdr, proxyProtocolV2{ command: proxyProtocolV2CommandProxy, sourceAddress: &net.TCPAddr{ IP: net.ParseIP("192.168.0.2").To4(), Port: 12345, }, destinationAddress: &net.TCPAddr{ IP: net.ParseIP("192.168.0.100").To4(), Port: 8080, }, additionalData: []byte("foobar"), }); err != nil { t.Fatalf("Error writeProxyProtocolV2() = %v", err) } opts := options{ args: []string{ "--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip", }, handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("X-Forwarded-For"), "192.168.0.2"; got != want { t.Errorf("X-Forwarded-For: %v; want %v", got, want) } if got, want := r.Header.Get("Forwarded"), "for=192.168.0.2"; got != want { t.Errorf("Forwarded: %v; want %v", got, want) } }, tls: true, tcpData: v2Hdr.Bytes(), } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV2TCP4TLS", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH2H1ProxyProtocolV2TCP6TLS tests PROXY protocol version 2 over // TLS containing AF_INET6 family is accepted and X-Forwarded-For // contains advertised src address. func TestH2H1ProxyProtocolV2TCP6TLS(t *testing.T) { var v2Hdr bytes.Buffer if err := writeProxyProtocolV2(&v2Hdr, proxyProtocolV2{ command: proxyProtocolV2CommandProxy, sourceAddress: &net.TCPAddr{ IP: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), Port: 12345, }, destinationAddress: &net.TCPAddr{ IP: net.ParseIP("::1"), Port: 8080, }, additionalData: []byte("foobar"), }); err != nil { t.Fatalf("Error writeProxyProtocolV2() = %v", err) } opts := options{ args: []string{ "--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip", }, handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("X-Forwarded-For"), "2001:db8:85a3::8a2e:370:7334"; got != want { t.Errorf("X-Forwarded-For: %v; want %v", got, want) } if got, want := r.Header.Get("Forwarded"), `for="[2001:db8:85a3::8a2e:370:7334]"`; got != want { t.Errorf("Forwarded: %v; want %v", got, want) } }, tls: true, tcpData: v2Hdr.Bytes(), } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV2TCP6TLS", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH2H1ProxyProtocolV2Local tests PROXY protocol version 2 // containing cmd == Local is ignored. func TestH2H1ProxyProtocolV2Local(t *testing.T) { opts := options{ args: []string{ "--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip", }, handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("X-Forwarded-For"), "127.0.0.1"; got != want { t.Errorf("X-Forwarded-For: %v; want %v", got, want) } if got, want := r.Header.Get("Forwarded"), "for=127.0.0.1"; got != want { t.Errorf("Forwarded: %v; want %v", got, want) } }, } st := newServerTester(t, opts) defer st.Close() var b bytes.Buffer if err := writeProxyProtocolV2(&b, proxyProtocolV2{ command: proxyProtocolV2CommandLocal, sourceAddress: &net.TCPAddr{ IP: net.ParseIP("192.168.0.2").To4(), Port: 12345, }, destinationAddress: &net.TCPAddr{ IP: net.ParseIP("192.168.0.100").To4(), Port: 8080, }, additionalData: []byte("foobar"), }); err != nil { t.Fatalf("Error writeProxyProtocolV2() = %v", err) } if _, err := st.conn.Write(b.Bytes()); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } res, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV2Local", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH2H1ProxyProtocolV2UnknownCmd tests PROXY protocol version 2 // containing unknown cmd should be rejected. func TestH2H1ProxyProtocolV2UnknownCmd(t *testing.T) { opts := options{ args: []string{"--accept-proxy-protocol"}, } st := newServerTester(t, opts) defer st.Close() var b bytes.Buffer if err := writeProxyProtocolV2(&b, proxyProtocolV2{ command: 0xf, sourceAddress: &net.TCPAddr{ IP: net.ParseIP("192.168.0.2").To4(), Port: 12345, }, destinationAddress: &net.TCPAddr{ IP: net.ParseIP("192.168.0.100").To4(), Port: 8080, }, additionalData: []byte("foobar"), }); err != nil { t.Fatalf("Error writeProxyProtocolV2() = %v", err) } if _, err := st.conn.Write(b.Bytes()); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } _, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV2UnknownCmd", }) if err == nil { t.Fatalf("connection was not terminated") } } // TestH2H1ProxyProtocolV2Unix tests PROXY protocol version 2 // containing AF_UNIX family is ignored. func TestH2H1ProxyProtocolV2Unix(t *testing.T) { opts := options{ args: []string{ "--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip", }, handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("X-Forwarded-For"), "127.0.0.1"; got != want { t.Errorf("X-Forwarded-For: %v; want %v", got, want) } if got, want := r.Header.Get("Forwarded"), "for=127.0.0.1"; got != want { t.Errorf("Forwarded: %v; want %v", got, want) } }, } st := newServerTester(t, opts) defer st.Close() var b bytes.Buffer if err := writeProxyProtocolV2(&b, proxyProtocolV2{ command: proxyProtocolV2CommandProxy, sourceAddress: &net.UnixAddr{ Name: "/foo", Net: "unix", }, destinationAddress: &net.UnixAddr{ Name: "/bar", Net: "unix", }, additionalData: []byte("foobar"), }); err != nil { t.Fatalf("Error writeProxyProtocolV2() = %v", err) } if _, err := st.conn.Write(b.Bytes()); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } res, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV2Unix", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH2H1ProxyProtocolV2Unspec tests PROXY protocol version 2 // containing AF_UNSPEC family is ignored. func TestH2H1ProxyProtocolV2Unspec(t *testing.T) { opts := options{ args: []string{ "--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip", }, handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("X-Forwarded-For"), "127.0.0.1"; got != want { t.Errorf("X-Forwarded-For: %v; want %v", got, want) } if got, want := r.Header.Get("Forwarded"), "for=127.0.0.1"; got != want { t.Errorf("Forwarded: %v; want %v", got, want) } }, } st := newServerTester(t, opts) defer st.Close() var b bytes.Buffer if err := writeProxyProtocolV2(&b, proxyProtocolV2{ command: proxyProtocolV2CommandProxy, additionalData: []byte("foobar"), }); err != nil { t.Fatalf("Error writeProxyProtocolV2() = %v", err) } if _, err := st.conn.Write(b.Bytes()); err != nil { t.Fatalf("Error st.conn.Write() = %v", err) } res, err := st.http2(requestParam{ name: "TestH2H1ProxyProtocolV2Unspec", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH2H1ExternalDNS tests that DNS resolution using external DNS // with HTTP/1 backend works. func TestH2H1ExternalDNS(t *testing.T) { opts := options{ args: []string{"--external-dns"}, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1ExternalDNS", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H1DNS tests that DNS resolution without external DNS with // HTTP/1 backend works. func TestH2H1DNS(t *testing.T) { opts := options{ args: []string{"--dns"}, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1DNS", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H1HTTPSRedirect tests that the request to the backend which // requires TLS is redirected to https URI. func TestH2H1HTTPSRedirect(t *testing.T) { opts := options{ args: []string{"--redirect-if-not-tls"}, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1HTTPSRedirect", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusPermanentRedirect; got != want { t.Errorf("status = %v; want %v", got, want) } if got, want := res.header.Get("location"), "https://127.0.0.1/"; got != want { t.Errorf("location: %v; want %v", got, want) } } // TestH2H1HTTPSRedirectPort tests that the request to the backend // which requires TLS is redirected to https URI with given port. func TestH2H1HTTPSRedirectPort(t *testing.T) { opts := options{ args: []string{ "--redirect-if-not-tls", "--redirect-https-port=8443", }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ path: "/foo?bar", name: "TestH2H1HTTPSRedirectPort", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusPermanentRedirect; got != want { t.Errorf("status = %v; want %v", got, want) } if got, want := res.header.Get("location"), "https://127.0.0.1:8443/foo?bar"; got != want { t.Errorf("location: %v; want %v", got, want) } } // TestH2H1Code204 tests that 204 response without content-length, and // transfer-encoding is valid. func TestH2H1Code204(t *testing.T) { opts := options{ handler: func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusNoContent) }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1Code204", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusNoContent; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H1Code204CL0 tests that 204 response with content-length: 0 // is allowed. func TestH2H1Code204CL0(t *testing.T) { opts := options{ handler: func(w http.ResponseWriter, _ *http.Request) { hj, ok := w.(http.Hijacker) if !ok { http.Error(w, "Could not hijack the connection", http.StatusInternalServerError) return } conn, bufrw, err := hj.Hijack() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer conn.Close() if _, err := bufrw.WriteString("HTTP/1.1 204\r\nContent-Length: 0\r\n\r\n"); err != nil { t.Fatalf("Error bufrw.WriteString() = %v", err) } bufrw.Flush() }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1Code204CL0", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusNoContent; got != want { t.Errorf("status = %v; want %v", got, want) } if got, found := res.header["Content-Length"]; found { t.Errorf("Content-Length = %v, want nothing", got) } } // TestH2H1Code204CLNonzero tests that 204 response with nonzero // content-length is not allowed. func TestH2H1Code204CLNonzero(t *testing.T) { opts := options{ handler: func(w http.ResponseWriter, _ *http.Request) { hj, ok := w.(http.Hijacker) if !ok { http.Error(w, "Could not hijack the connection", http.StatusInternalServerError) return } conn, bufrw, err := hj.Hijack() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer conn.Close() if _, err := bufrw.WriteString("HTTP/1.1 204\r\nContent-Length: 1\r\n\r\n"); err != nil { t.Fatalf("Error bufrw.WriteString() = %v", err) } bufrw.Flush() }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1Code204CLNonzero", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusBadGateway; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H1Code204TE tests that 204 response with transfer-encoding is // not allowed. func TestH2H1Code204TE(t *testing.T) { opts := options{ handler: func(w http.ResponseWriter, _ *http.Request) { hj, ok := w.(http.Hijacker) if !ok { http.Error(w, "Could not hijack the connection", http.StatusInternalServerError) return } conn, bufrw, err := hj.Hijack() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer conn.Close() if _, err := bufrw.WriteString("HTTP/1.1 204\r\nTransfer-Encoding: chunked\r\n\r\n"); err != nil { t.Fatalf("Error bufrw.WriteString() = %v", err) } bufrw.Flush() }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1Code204TE", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusBadGateway; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H1AffinityCookie tests that affinity cookie is sent back in // cleartext http. func TestH2H1AffinityCookie(t *testing.T) { opts := options{ args: []string{"--affinity-cookie"}, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1AffinityCookie", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar` validCookie := regexp.MustCompile(pattern) if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) { t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern) } } // TestH2H1AffinityCookieTLS tests that affinity cookie is sent back // in https. func TestH2H1AffinityCookieTLS(t *testing.T) { opts := options{ args: []string{"--affinity-cookie"}, tls: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1AffinityCookieTLS", scheme: "https", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar; Secure` validCookie := regexp.MustCompile(pattern) if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) { t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern) } } // TestH2H1GracefulShutdown tests graceful shutdown. func TestH2H1GracefulShutdown(t *testing.T) { st := newServerTester(t, options{}) defer st.Close() fmt.Fprint(st.conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") if err := st.fr.WriteSettings(); err != nil { t.Fatalf("st.fr.WriteSettings(): %v", err) } header := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":authority", st.authority), pair(":path", "/"), } for _, h := range header { _ = st.enc.WriteField(h) } if err := st.fr.WriteHeaders(http2.HeadersFrameParam{ StreamID: 1, EndStream: false, EndHeaders: true, BlockFragment: st.headerBlkBuf.Bytes(), }); err != nil { t.Fatalf("st.fr.WriteHeaders(): %v", err) } // send SIGQUIT signal to nghttpx to perform graceful shutdown if err := st.cmd.Process.Signal(syscall.SIGQUIT); err != nil { t.Fatalf("Error st.cmd.Process.Signal() = %v", err) } time.Sleep(150 * time.Millisecond) // after signal, finish request body if err := st.fr.WriteData(1, true, nil); err != nil { t.Fatalf("st.fr.WriteData(): %v", err) } numGoAway := 0 for { fr, err := st.readFrame() if err != nil { if errors.Is(err, io.EOF) { want := 2 if got := numGoAway; got != want { t.Fatalf("numGoAway: %v; want %v", got, want) } return } t.Fatalf("st.readFrame(): %v", err) } switch f := fr.(type) { case *http2.GoAwayFrame: numGoAway++ want := http2.ErrCodeNo if got := f.ErrCode; got != want { t.Fatalf("f.ErrCode(%v): %v; want %v", numGoAway, got, want) } switch numGoAway { case 1: want := (uint32(1) << 31) - 1 if got := f.LastStreamID; got != want { t.Fatalf("f.LastStreamID(%v): %v; want %v", numGoAway, got, want) } case 2: want := uint32(1) if got := f.LastStreamID; got != want { t.Fatalf("f.LastStreamID(%v): %v; want %v", numGoAway, got, want) } case 3: t.Fatalf("too many GOAWAYs received") } } } } // TestH2H2MultipleResponseCL tests that server returns error if // multiple Content-Length response header fields are received. func TestH2H2MultipleResponseCL(t *testing.T) { opts := options{ args: []string{"--http2-bridge"}, handler: func(w http.ResponseWriter, _ *http.Request) { w.Header().Add("content-length", "1") w.Header().Add("content-length", "1") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2MultipleResponseCL", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.errCode, http2.ErrCodeInternal; got != want { t.Errorf("res.errCode: %v; want %v", got, want) } } // TestH2H2InvalidResponseCL tests that server returns error if // Content-Length response header field value cannot be parsed as a // number. func TestH2H2InvalidResponseCL(t *testing.T) { opts := options{ args: []string{"--http2-bridge"}, handler: func(w http.ResponseWriter, _ *http.Request) { w.Header().Add("content-length", "") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2InvalidResponseCL", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.errCode, http2.ErrCodeInternal; got != want { t.Errorf("res.errCode: %v; want %v", got, want) } } // // TestH2H2ConnectFailure tests that server handles the situation that // // connection attempt to HTTP/2 backend failed. // func TestH2H2ConnectFailure(t *testing.T) { // opts := options{ // args: []string{"--http2-bridge"}, // } // st := newServerTester(t, opts) // defer st.Close() // // simulate backend connect attempt failure // st.ts.Close() // res, err := st.http2(requestParam{ // name: "TestH2H2ConnectFailure", // }) // if err != nil { // t.Fatalf("Error st.http2() = %v", err) // } // want := 503 // if got := res.status; got != want { // t.Errorf("status: %v; want %v", got, want) // } // } // TestH2H2HostRewrite tests that server rewrites host header field func TestH2H2HostRewrite(t *testing.T) { opts := options{ args: []string{"--http2-bridge", "--host-rewrite"}, handler: func(w http.ResponseWriter, r *http.Request) { w.Header().Add("request-host", r.Host) }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2HostRewrite", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } if got, want := res.header.Get("request-host"), st.backendHost; got != want { t.Errorf("request-host: %v; want %v", got, want) } } // TestH2H2NoHostRewrite tests that server does not rewrite host // header field func TestH2H2NoHostRewrite(t *testing.T) { opts := options{ args: []string{"--http2-bridge"}, handler: func(w http.ResponseWriter, r *http.Request) { w.Header().Add("request-host", r.Host) }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2NoHostRewrite", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } if got, want := res.header.Get("request-host"), st.frontendHost; got != want { t.Errorf("request-host: %v; want %v", got, want) } } // TestH2H2TLSXfp tests nghttpx sends x-forwarded-proto header field // with http value since :scheme is http, even if the frontend // connection is encrypted. func TestH2H2TLSXfp(t *testing.T) { opts := options{ args: []string{"--http2-bridge"}, handler: func(_ http.ResponseWriter, r *http.Request) { if got, want := r.Header.Get("x-forwarded-proto"), "http"; got != want { t.Errorf("x-forwarded-proto: want %v; got %v", want, got) } }, tls: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2TLSXfp", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH2H2AddXfp tests that server appends :scheme to the existing // x-forwarded-proto header field. func TestH2H2AddXfp(t *testing.T) { opts := options{ args: []string{ "--http2-bridge", "--no-strip-incoming-x-forwarded-proto", }, handler: func(_ http.ResponseWriter, r *http.Request) { xfp := r.Header.Get("X-Forwarded-Proto") if got, want := xfp, "foo, http"; got != want { t.Errorf("X-Forwarded-Proto = %q; want %q", got, want) } }, tls: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2AddXfp", header: []hpack.HeaderField{ pair("x-forwarded-proto", "foo"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H2NoAddXfp tests that server does not append :scheme to the // existing x-forwarded-proto header field. func TestH2H2NoAddXfp(t *testing.T) { opts := options{ args: []string{ "--http2-bridge", "--no-add-x-forwarded-proto", "--no-strip-incoming-x-forwarded-proto", }, handler: func(_ http.ResponseWriter, r *http.Request) { xfp := r.Header.Get("X-Forwarded-Proto") if got, want := xfp, "foo"; got != want { t.Errorf("X-Forwarded-Proto = %q; want %q", got, want) } }, tls: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2NoAddXfp", header: []hpack.HeaderField{ pair("x-forwarded-proto", "foo"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H2StripXfp tests that server strips incoming // x-forwarded-proto header field. func TestH2H2StripXfp(t *testing.T) { opts := options{ args: []string{"--http2-bridge"}, handler: func(_ http.ResponseWriter, r *http.Request) { xfp := r.Header.Get("X-Forwarded-Proto") if got, want := xfp, "http"; got != want { t.Errorf("X-Forwarded-Proto = %q; want %q", got, want) } }, tls: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2StripXfp", header: []hpack.HeaderField{ pair("x-forwarded-proto", "foo"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H2StripNoAddXfp tests that server strips incoming // x-forwarded-proto header field, and does not add another. func TestH2H2StripNoAddXfp(t *testing.T) { opts := options{ args: []string{"--http2-bridge", "--no-add-x-forwarded-proto"}, handler: func(_ http.ResponseWriter, r *http.Request) { if got, found := r.Header["X-Forwarded-Proto"]; found { t.Errorf("X-Forwarded-Proto = %q; want nothing", got) } }, tls: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2StripNoAddXfp", header: []hpack.HeaderField{ pair("x-forwarded-proto", "foo"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H2AddXff tests that server generates X-Forwarded-For header // field when forwarding request to backend. func TestH2H2AddXff(t *testing.T) { opts := options{ args: []string{"--http2-bridge", "--add-x-forwarded-for"}, handler: func(_ http.ResponseWriter, r *http.Request) { xff := r.Header.Get("X-Forwarded-For") want := "127.0.0.1" if xff != want { t.Errorf("X-Forwarded-For = %v; want %v", xff, want) } }, tls: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2AddXff", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H2AddXff2 tests that server appends X-Forwarded-For header // field to existing one when forwarding request to backend. func TestH2H2AddXff2(t *testing.T) { opts := options{ args: []string{"--http2-bridge", "--add-x-forwarded-for"}, handler: func(_ http.ResponseWriter, r *http.Request) { xff := r.Header.Get("X-Forwarded-For") want := "host, 127.0.0.1" if xff != want { t.Errorf("X-Forwarded-For = %v; want %v", xff, want) } }, tls: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2AddXff2", header: []hpack.HeaderField{ pair("x-forwarded-for", "host"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H2StripXff tests that --strip-incoming-x-forwarded-for // option. func TestH2H2StripXff(t *testing.T) { opts := options{ args: []string{ "--http2-bridge", "--strip-incoming-x-forwarded-for", }, handler: func(_ http.ResponseWriter, r *http.Request) { if xff, found := r.Header["X-Forwarded-For"]; found { t.Errorf("X-Forwarded-For = %v; want nothing", xff) } }, tls: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2StripXff", header: []hpack.HeaderField{ pair("x-forwarded-for", "host"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H2StripAddXff tests that --strip-incoming-x-forwarded-for and // --add-x-forwarded-for options. func TestH2H2StripAddXff(t *testing.T) { opts := options{ args: []string{ "--http2-bridge", "--strip-incoming-x-forwarded-for", "--add-x-forwarded-for", }, handler: func(_ http.ResponseWriter, r *http.Request) { xff := r.Header.Get("X-Forwarded-For") want := "127.0.0.1" if xff != want { t.Errorf("X-Forwarded-For = %v; want %v", xff, want) } }, tls: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2StripAddXff", header: []hpack.HeaderField{ pair("x-forwarded-for", "host"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H2AddForwarded tests that server generates Forwarded header // field using static obfuscated "by" parameter. func TestH2H2AddForwarded(t *testing.T) { opts := options{ args: []string{ "--http2-bridge", "--add-forwarded=by,for,host,proto", "--forwarded-by=_alpha", }, handler: func(_ http.ResponseWriter, r *http.Request) { pattern := fmt.Sprintf(`by=_alpha;for=_[^;]+;host="127\.0\.0\.1:%v";proto=https`, serverPort) validFwd := regexp.MustCompile(pattern) if got := r.Header.Get("Forwarded"); !validFwd.MatchString(got) { t.Errorf("Forwarded = %v; want pattern %v", got, pattern) } }, tls: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2AddForwarded", scheme: "https", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH2H2AddForwardedMerge tests that server generates Forwarded // header field using static obfuscated "by" parameter, and // existing Forwarded header field. func TestH2H2AddForwardedMerge(t *testing.T) { opts := options{ args: []string{ "--http2-bridge", "--add-forwarded=by,host,proto", "--forwarded-by=_alpha", }, handler: func(_ http.ResponseWriter, r *http.Request) { want := fmt.Sprintf(`host=foo, by=_alpha;host="127.0.0.1:%v";proto=https`, serverPort) if got := r.Header.Get("Forwarded"); got != want { t.Errorf("Forwarded = %v; want %v", got, want) } }, tls: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2AddForwardedMerge", scheme: "https", header: []hpack.HeaderField{ pair("forwarded", "host=foo"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH2H2AddForwardedStrip tests that server generates Forwarded // header field using static obfuscated "by" parameter, and // existing Forwarded header field stripped. func TestH2H2AddForwardedStrip(t *testing.T) { opts := options{ args: []string{ "--http2-bridge", "--strip-incoming-forwarded", "--add-forwarded=by,host,proto", "--forwarded-by=_alpha", }, handler: func(_ http.ResponseWriter, r *http.Request) { want := fmt.Sprintf(`by=_alpha;host="127.0.0.1:%v";proto=https`, serverPort) if got := r.Header.Get("Forwarded"); got != want { t.Errorf("Forwarded = %v; want %v", got, want) } }, tls: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2AddForwardedStrip", scheme: "https", header: []hpack.HeaderField{ pair("forwarded", "host=foo"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH2H2StripForwarded tests that server strips incoming Forwarded // header field. func TestH2H2StripForwarded(t *testing.T) { opts := options{ args: []string{"--http2-bridge", "--strip-incoming-forwarded"}, handler: func(_ http.ResponseWriter, r *http.Request) { if got, found := r.Header["Forwarded"]; found { t.Errorf("Forwarded = %v; want nothing", got) } }, tls: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2StripForwarded", scheme: "https", header: []hpack.HeaderField{ pair("forwarded", "host=foo"), }, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status: %v; want %v", got, want) } } // TestH2H2ReqPhaseReturn tests mruby request phase hook returns // custom response. func TestH2H2ReqPhaseReturn(t *testing.T) { opts := options{ args: []string{ "--http2-bridge", "--mruby-file=" + testDir + "/req-return.rb", }, handler: func(http.ResponseWriter, *http.Request) { t.Fatalf("request should not be forwarded") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2ReqPhaseReturn", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusNotFound; got != want { t.Errorf("status = %v; want %v", got, want) } hdtests := []struct { k, v string }{ {"content-length", "20"}, {"from", "mruby"}, } for _, tt := range hdtests { if got, want := res.header.Get(tt.k), tt.v; got != want { t.Errorf("%v = %v; want %v", tt.k, got, want) } } if got, want := string(res.body), "Hello World from req"; got != want { t.Errorf("body = %v; want %v", got, want) } } // TestH2H2RespPhaseReturn tests mruby response phase hook returns // custom response. func TestH2H2RespPhaseReturn(t *testing.T) { opts := options{ args: []string{ "--http2-bridge", "--mruby-file=" + testDir + "/resp-return.rb", }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2RespPhaseReturn", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusNotFound; got != want { t.Errorf("status = %v; want %v", got, want) } hdtests := []struct { k, v string }{ {"content-length", "21"}, {"from", "mruby"}, } for _, tt := range hdtests { if got, want := res.header.Get(tt.k), tt.v; got != want { t.Errorf("%v = %v; want %v", tt.k, got, want) } } if got, want := string(res.body), "Hello World from resp"; got != want { t.Errorf("body = %v; want %v", got, want) } } // TestH2H2ExternalDNS tests that DNS resolution using external DNS // with HTTP/2 backend works. func TestH2H2ExternalDNS(t *testing.T) { opts := options{ args: []string{"--http2-bridge", "--external-dns"}, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2ExternalDNS", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H2DNS tests that DNS resolution without external DNS with // HTTP/2 backend works. func TestH2H2DNS(t *testing.T) { opts := options{ args: []string{"--http2-bridge", "--dns"}, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2DNS", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H2Code204 tests that 204 response without content-length, and // transfer-encoding is valid. func TestH2H2Code204(t *testing.T) { opts := options{ args: []string{"--http2-bridge"}, handler: func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusNoContent) }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H2Code204", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusNoContent; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2APIBackendconfig exercise backendconfig API endpoint routine // for successful case. func TestH2APIBackendconfig(t *testing.T) { opts := options{ args: []string{"-f127.0.0.1,3010;api;no-tls"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatalf("request should not be forwarded") }, connectPort: 3010, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2APIBackendconfig", path: "/api/v1beta1/backendconfig", method: "PUT", body: []byte(`# comment backend=127.0.0.1,3011 `), }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } var apiResp APIResponse if err := json.Unmarshal(res.body, &apiResp); err != nil { t.Fatalf("Error unmarshaling API response: %v", err) } if got, want := apiResp.Status, "Success"; got != want { t.Errorf("apiResp.Status: %v; want %v", got, want) } if got, want := apiResp.Code, 200; got != want { t.Errorf("apiResp.Status: %v; want %v", got, want) } } // TestH2APIBackendconfigQuery exercise backendconfig API endpoint // routine with query. func TestH2APIBackendconfigQuery(t *testing.T) { opts := options{ args: []string{"-f127.0.0.1,3010;api;no-tls"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatalf("request should not be forwarded") }, connectPort: 3010, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2APIBackendconfigQuery", path: "/api/v1beta1/backendconfig?foo=bar", method: "PUT", body: []byte(`# comment backend=127.0.0.1,3011 `), }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } var apiResp APIResponse if err := json.Unmarshal(res.body, &apiResp); err != nil { t.Fatalf("Error unmarshaling API response: %v", err) } if got, want := apiResp.Status, "Success"; got != want { t.Errorf("apiResp.Status: %v; want %v", got, want) } if got, want := apiResp.Code, 200; got != want { t.Errorf("apiResp.Status: %v; want %v", got, want) } } // TestH2APIBackendconfigBadMethod exercise backendconfig API endpoint // routine with bad method. func TestH2APIBackendconfigBadMethod(t *testing.T) { opts := options{ args: []string{"-f127.0.0.1,3010;api;no-tls"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatalf("request should not be forwarded") }, connectPort: 3010, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2APIBackendconfigBadMethod", path: "/api/v1beta1/backendconfig", method: "GET", body: []byte(`# comment backend=127.0.0.1,3011 `), }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusMethodNotAllowed; got != want { t.Errorf("res.status: %v; want %v", got, want) } var apiResp APIResponse if err := json.Unmarshal(res.body, &apiResp); err != nil { t.Fatalf("Error unmarshaling API response: %v", err) } if got, want := apiResp.Status, "Failure"; got != want { t.Errorf("apiResp.Status: %v; want %v", got, want) } if got, want := apiResp.Code, 405; got != want { t.Errorf("apiResp.Status: %v; want %v", got, want) } } // TestH2APIConfigrevision tests configrevision API. func TestH2APIConfigrevision(t *testing.T) { opts := options{ args: []string{"-f127.0.0.1,3010;api;no-tls"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatalf("request should not be forwarded") }, connectPort: 3010, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2APIConfigrevision", path: "/api/v1beta1/configrevision", method: "GET", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want = %v", got, want) } var apiResp APIResponse d := json.NewDecoder(bytes.NewBuffer(res.body)) d.UseNumber() if err := d.Decode(&apiResp); err != nil { t.Fatalf("Error unmarshalling API response: %v", err) } if got, want := apiResp.Status, "Success"; got != want { t.Errorf("apiResp.Status: %v; want %v", got, want) } if got, want := apiResp.Code, 200; got != want { t.Errorf("apiResp.Status: %v; want %v", got, want) } if got, want := apiResp.Data["configRevision"], json.Number("0"); got != want { t.Errorf(`apiResp.Data["configRevision"]: %v %t; want %v`, got, got, want) } } // TestH2APINotFound exercise backendconfig API endpoint routine when // API endpoint is not found. func TestH2APINotFound(t *testing.T) { opts := options{ args: []string{"-f127.0.0.1,3010;api;no-tls"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatalf("request should not be forwarded") }, connectPort: 3010, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2APINotFound", path: "/api/notfound", method: "GET", body: []byte(`# comment backend=127.0.0.1,3011 `), }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusNotFound; got != want { t.Errorf("res.status: %v; want %v", got, want) } var apiResp APIResponse if err := json.Unmarshal(res.body, &apiResp); err != nil { t.Fatalf("Error unmarshaling API response: %v", err) } if got, want := apiResp.Status, "Failure"; got != want { t.Errorf("apiResp.Status: %v; want %v", got, want) } if got, want := apiResp.Code, 404; got != want { t.Errorf("apiResp.Status: %v; want %v", got, want) } } // TestH2Healthmon tests health monitor endpoint. func TestH2Healthmon(t *testing.T) { opts := options{ args: []string{"-f127.0.0.1,3011;healthmon;no-tls"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatalf("request should not be forwarded") }, connectPort: 3011, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2Healthmon", path: "/alpha/bravo", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusOK; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH2ResponseBeforeRequestEnd tests the situation where response // ends before request body finishes. func TestH2ResponseBeforeRequestEnd(t *testing.T) { opts := options{ args: []string{"--mruby-file=" + testDir + "/req-return.rb"}, handler: func(http.ResponseWriter, *http.Request) { t.Fatal("request should not be forwarded") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2ResponseBeforeRequestEnd", noEndStream: true, }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusNotFound; got != want { t.Errorf("res.status: %v; want %v", got, want) } } // TestH2H1ChunkedEndsPrematurely tests that a stream is reset if the // backend chunked encoded response ends prematurely. func TestH2H1ChunkedEndsPrematurely(t *testing.T) { opts := options{ handler: func(w http.ResponseWriter, _ *http.Request) { hj, ok := w.(http.Hijacker) if !ok { http.Error(w, "Could not hijack the connection", http.StatusInternalServerError) return } conn, bufrw, err := hj.Hijack() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer conn.Close() if _, err := bufrw.WriteString("HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\n\r\n"); err != nil { t.Fatalf("Error bufrw.WriteString() = %v", err) } bufrw.Flush() }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1ChunkedEndsPrematurely", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.errCode, http2.ErrCodeInternal; got != want { t.Errorf("res.errCode = %v; want %v", got, want) } } // TestH2H1RequireHTTPSchemeHTTPSWithoutEncryption verifies that https // scheme in non-encrypted connection is treated as error. func TestH2H1RequireHTTPSchemeHTTPSWithoutEncryption(t *testing.T) { opts := options{ args: []string{"--require-http-scheme"}, handler: func(http.ResponseWriter, *http.Request) { t.Errorf("server should not forward this request") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1RequireHTTPSchemeHTTPSWithoutEncryption", scheme: "https", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusBadRequest; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H1RequireHTTPSchemeHTTPWithEncryption verifies that http // scheme in encrypted connection is treated as error. func TestH2H1RequireHTTPSchemeHTTPWithEncryption(t *testing.T) { opts := options{ args: []string{"--require-http-scheme"}, handler: func(http.ResponseWriter, *http.Request) { t.Errorf("server should not forward this request") }, tls: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1RequireHTTPSchemeHTTPWithEncryption", scheme: "http", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusBadRequest; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H1RequireHTTPSchemeUnknownSchemeWithoutEncryption verifies // that unknown scheme in non-encrypted connection is treated as // error. func TestH2H1RequireHTTPSchemeUnknownSchemeWithoutEncryption(t *testing.T) { opts := options{ args: []string{"--require-http-scheme"}, handler: func(http.ResponseWriter, *http.Request) { t.Errorf("server should not forward this request") }, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1RequireHTTPSchemeUnknownSchemeWithoutEncryption", scheme: "unknown", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusBadRequest; got != want { t.Errorf("status = %v; want %v", got, want) } } // TestH2H1RequireHTTPSchemeUnknownSchemeWithEncryption verifies that // unknown scheme in encrypted connection is treated as error. func TestH2H1RequireHTTPSchemeUnknownSchemeWithEncryption(t *testing.T) { opts := options{ args: []string{"--require-http-scheme"}, handler: func(http.ResponseWriter, *http.Request) { t.Errorf("server should not forward this request") }, tls: true, } st := newServerTester(t, opts) defer st.Close() res, err := st.http2(requestParam{ name: "TestH2H1RequireHTTPSchemeUnknownSchemeWithEncryption", scheme: "unknown", }) if err != nil { t.Fatalf("Error st.http2() = %v", err) } if got, want := res.status, http.StatusBadRequest; got != want { t.Errorf("status = %v; want %v", got, want) } } nghttp2-1.68.0/integration-tests/PaxHeaders/config.go.in0000644000000000000000000000013015077107270020142 xustar0030 mtime=1761382072.973444231 29 atime=1761382103.94732037 29 ctime=1761382109.41529947 nghttp2-1.68.0/integration-tests/config.go.in0000644000175100017510000000012515077107270020532 0ustar00runnerrunnerpackage nghttp2 const ( buildDir = "@top_builddir@" sourceDir = "@top_srcdir@" ) nghttp2-1.68.0/integration-tests/PaxHeaders/Makefile.am0000644000000000000000000000013215077107270017777 xustar0030 mtime=1761382072.973444231 30 atime=1761382085.555387217 30 ctime=1761382109.412299479 nghttp2-1.68.0/integration-tests/Makefile.am0000644000175100017510000000320715077107270020371 0ustar00runnerrunner# nghttp2 - HTTP/2 C Library # Copyright (c) 2015 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. GO_FILES = \ nghttpx_http1_test.go \ nghttpx_http2_test.go \ nghttpx_http3_test.go \ server_tester.go \ server_tester_http3.go EXTRA_DIST = \ CMakeLists.txt \ $(GO_FILES) \ server.key \ server.crt \ alt-server.key \ alt-server.crt \ setenv \ req-set-header.rb \ resp-set-header.rb \ req-return.rb \ resp-return.rb GO_TEST_TAGS = if ENABLE_HTTP3 GO_TEST_TAGS += quic endif # ENABLE_HTTP3 it: for i in $(GO_FILES); do [ -e $(builddir)/$$i ] || cp $(srcdir)/$$i $(builddir); done sh setenv go test -v --tags=${GO_TEST_TAGS} nghttp2-1.68.0/integration-tests/PaxHeaders/setenv.in0000644000000000000000000000013215077107270017577 xustar0030 mtime=1761382072.974444227 30 atime=1761382103.960320312 30 ctime=1761382109.416299468 nghttp2-1.68.0/integration-tests/setenv.in0000644000175100017510000000057115077107270020172 0ustar00runnerrunner#!/bin/sh -e libdir="@abs_top_builddir@/lib" if [ -d "$libdir/.libs" ]; then libdir="$libdir/.libs" fi export CGO_CFLAGS="-I@abs_top_srcdir@/lib/includes -I@abs_top_builddir@/lib/includes @CFLAGS@" export CGO_CPPFLAGS="@CPPFLAGS@" export CGO_LDFLAGS="-L$libdir @LDFLAGS@" export LD_LIBRARY_PATH="$libdir" export DYLD_LIBRARY_PATH="$libdir" export GODEBUG=cgocheck=0 "$@" nghttp2-1.68.0/PaxHeaders/README.rst0000644000000000000000000000013215077107270013747 xustar0030 mtime=1761382072.961444287 30 atime=1761382104.841316431 30 ctime=1761382107.825304067 nghttp2-1.68.0/README.rst0000644000175100017510000014061315077107270014344 0ustar00runnerrunnernghttp2 - HTTP/2 C Library ========================== This is an implementation of the Hypertext Transfer Protocol version 2 in C. The framing layer of HTTP/2 is implemented as a reusable C library. On top of that, we have implemented an HTTP/2 client, server and proxy. We have also developed load test and benchmarking tools for HTTP/2. An HPACK encoder and decoder are available as a public API. Development Status ------------------ nghttp2 was originally developed based on `RFC 7540 `_ HTTP/2 and `RFC 7541 `_ HPACK - Header Compression for HTTP/2. Now we are updating our code to implement `RFC 9113 `_. The nghttp2 code base was forked from the spdylay (https://github.com/tatsuhiro-t/spdylay) project. Public Test Server ------------------ The following endpoints are available to try out our nghttp2 implementation. * https://nghttp2.org/ (TLS + ALPN and HTTP/3) This endpoint supports ``h2`` and ``http/1.1`` via ALPN and requires TLSv1.2 for HTTP/2 connection. It also supports HTTP/3. * http://nghttp2.org/ (HTTP Upgrade and HTTP/2 Direct) ``h2c`` and ``http/1.1``. Requirements ------------ The following package is required to build the libnghttp2 library: * pkg-config >= 0.20 To build the documentation, you need to install: * sphinx (http://sphinx-doc.org/) If you need libnghttp2 (C library) only, then the above packages are all you need. Use ``--enable-lib-only`` to ensure that only libnghttp2 is built. This avoids potential build error related to building bundled applications. To build and run the application programs (``nghttp``, ``nghttpd``, ``nghttpx`` and ``h2load``) in the ``src`` directory, the following packages are required: * OpenSSL >= 1.1.1; or wolfSSL >= 5.7.0; or LibreSSL >= 3.8.1; or aws-lc >= 1.19.0; or BoringSSL * libev >= 4.11 * zlib >= 1.2.3 * libc-ares >= 1.7.5 To enable ``-a`` option (getting linked assets from the downloaded resource) in ``nghttp``, the following package is required: * libxml2 >= 2.6.26 To enable systemd support in nghttpx, the following package is required: * libsystemd-dev >= 209 The HPACK tools require the following package: * jansson >= 2.5 To build sources under the examples directory, libevent is required: * libevent-openssl >= 2.0.8 To mitigate heap fragmentation in long running server programs (``nghttpd`` and ``nghttpx``), jemalloc is recommended: * jemalloc .. note:: Alpine Linux currently does not support malloc replacement due to musl limitations. See details in issue `#762 `_. For BoringSSL or aws-lc build, to enable :rfc:`8879` TLS Certificate Compression in applications, the following library is required: * libbrotli-dev >= 1.0.9 To enable mruby support for nghttpx, `mruby `_ is required. We need to build mruby with C++ ABI explicitly turned on, and probably need other mrgems, mruby is managed by git submodule under third-party/mruby directory. Currently, mruby support for nghttpx is disabled by default. To enable mruby support, use ``--with-mruby`` configure option. Note that at the time of this writing, libmruby-dev and mruby packages in Debian/Ubuntu are not usable for nghttp2, since they do not enable C++ ABI. To build mruby, the following packages are required: * ruby * bison nghttpx supports `neverbleed `_, privilege separation engine for OpenSSL. In short, it minimizes the risk of private key leakage when serious bug like Heartbleed is exploited. The neverbleed is disabled by default. To enable it, use ``--with-neverbleed`` configure option. To enable the experimental HTTP/3 support for h2load and nghttpx, the following libraries are required: * `quictls `_; or wolfSSL; or LibreSSL (does not support 0RTT); or aws-lc; or `BoringSSL `_ (commit db1a8456167249f95b854a1cd24c6b553d0f1567); or OpenSSL >= 3.5.0 * `ngtcp2 `_ >= 1.16.0 * `nghttp3 `_ >= 1.12.0 Use ``--enable-http3`` configure option to enable HTTP/3 feature for h2load and nghttpx. In order to build optional eBPF program to direct an incoming QUIC UDP datagram to a correct socket for nghttpx, the following libraries are required: * libbpf-dev >= 0.7.0 Use ``--with-libbpf`` configure option to build eBPF program. libelf-dev is needed to build libbpf. For Ubuntu 20.04, you can build libbpf from `the source code `_. nghttpx requires eBPF program for reloading its configuration and hot swapping its executable. Compiling libnghttp2 C source code requires a C99 compiler. gcc 4.8 is known to be adequate. In order to compile the C++ source code, C++20 compliant compiler is required. At least g++ >= 12 and clang++ >= 18 are known to work. .. note:: To enable mruby support in nghttpx, and use ``--with-mruby`` configure option. .. note:: Mac OS X users may need the ``--disable-threads`` configure option to disable multi-threading in nghttpd, nghttpx and h2load to prevent them from crashing. A patch is welcome to make multi threading work on Mac OS X platform. .. note:: To compile the associated applications (nghttp, nghttpd, nghttpx and h2load), you must use the ``--enable-app`` configure option and ensure that the specified requirements above are met. Normally, configure script checks required dependencies to build these applications, and enable ``--enable-app`` automatically, so you don't have to use it explicitly. But if you found that applications were not built, then using ``--enable-app`` may find that cause, such as the missing dependency. .. note:: In order to detect third party libraries, pkg-config is used (however we don't use pkg-config for some libraries (e.g., libev)). By default, pkg-config searches ``*.pc`` file in the standard locations (e.g., /usr/lib/pkgconfig). If it is necessary to use ``*.pc`` file in the custom location, specify paths to ``PKG_CONFIG_PATH`` environment variable, and pass it to configure script, like so: .. code-block:: text $ ./configure PKG_CONFIG_PATH=/path/to/pkgconfig For pkg-config managed libraries, ``*_CFLAG`` and ``*_LIBS`` environment variables are defined (e.g., ``OPENSSL_CFLAGS``, ``OPENSSL_LIBS``). Specifying non-empty string to these variables completely overrides pkg-config. In other words, if they are specified, pkg-config is not used for detection, and user is responsible to specify the correct values to these variables. For complete list of these variables, run ``./configure -h``. If you are using Ubuntu 22.04 LTS, run the following to install the required packages: .. code-block:: text sudo apt-get install g++ clang make binutils autoconf automake \ autotools-dev libtool pkg-config \ zlib1g-dev libssl-dev libxml2-dev libev-dev \ libevent-dev libjansson-dev \ libc-ares-dev libjemalloc-dev libsystemd-dev \ ruby-dev bison libelf-dev Building nghttp2 from release tar archive ----------------------------------------- The nghttp2 project regularly releases tar archives which includes nghttp2 source code, and generated build files. They can be downloaded from `Releases `_ page. Building nghttp2 from git requires autotools development packages. Building from tar archives does not require them, and thus it is much easier. The usual build step is as follows: .. code-block:: text $ tar xf nghttp2-X.Y.Z.tar.bz2 $ cd nghttp2-X.Y.Z $ ./configure $ make Building from git ----------------- Building from git is easy, but please be sure that at least autoconf 2.68 is used: .. code-block:: text $ git submodule update --init $ autoreconf -i $ automake $ autoconf $ ./configure $ make Notes for building on Windows (MSVC) ------------------------------------ The easiest way to build native Windows nghttp2 dll is use `cmake `_. The free version of `Visual C++ Build Tools `_ works fine. 1. Install cmake for windows 2. Open "Visual C++ ... Native Build Tool Command Prompt", and inside nghttp2 directly, run ``cmake``. 3. Then run ``cmake --build`` to build library. 4. nghttp2.dll, nghttp2.lib, nghttp2.exp are placed under lib directory. Note that the above steps most likely produce nghttp2 library only. No bundled applications are compiled. Notes for building on Windows (Mingw/Cygwin) -------------------------------------------- Under Mingw environment, you can only compile the library, it's ``libnghttp2-X.dll`` and ``libnghttp2.a``. If you want to compile the applications(``h2load``, ``nghttp``, ``nghttpx``, ``nghttpd``), you need to use the Cygwin environment. Under Cygwin environment, to compile the applications you need to compile and install the libev first. Secondly, you need to undefine the macro ``__STRICT_ANSI__``, if you not, the functions ``fdopen``, ``fileno`` and ``strptime`` will not available. the sample command like this: .. code-block:: text $ export CFLAGS="-U__STRICT_ANSI__ -I$libev_PREFIX/include -L$libev_PREFIX/lib" $ export CXXFLAGS=$CFLAGS $ ./configure $ make If you want to compile the applications under ``examples/``, you need to remove or rename the ``event.h`` from libev's installation, because it conflicts with libevent's installation. Notes for installation on Linux systems -------------------------------------------- After installing nghttp2 tool suite with ``make install`` one might experience a similar error: .. code-block:: text nghttpx: error while loading shared libraries: libnghttp2.so.14: cannot open shared object file: No such file or directory This means that the tool is unable to locate the ``libnghttp2.so`` shared library. To update the shared library cache run ``sudo ldconfig``. Building the documentation -------------------------- .. note:: Documentation is still incomplete. To build the documentation, run: .. code-block:: text $ make html The documents will be generated under ``doc/manual/html/``. The generated documents will not be installed with ``make install``. The online documentation is available at https://nghttp2.org/documentation/ Build HTTP/3 enabled h2load and nghttpx --------------------------------------- To build h2load and nghttpx with HTTP/3 feature enabled, run the configure script with ``--enable-http3``. For nghttpx to reload configurations and swapping its executable while gracefully terminating old worker processes, eBPF is required. Run the configure script with ``--enable-http3 --with-libbpf`` to build eBPF program. The QUIC keying material must be set with ``--frontend-quic-secret-file`` in order to keep the existing connections alive during reload. The detailed steps to build HTTP/3 enabled h2load and nghttpx follow. Build aws-lc: .. code-block:: text $ git clone --depth 1 -b v1.62.0 https://github.com/aws/aws-lc $ cd aws-lc $ cmake -B build -DDISABLE_GO=ON --install-prefix=$PWD/opt $ make -j$(nproc) -C build $ cmake --install build $ cd .. Build nghttp3: .. code-block:: text $ git clone --depth 1 -b v1.12.0 https://github.com/ngtcp2/nghttp3 $ cd nghttp3 $ git submodule update --init --depth 1 $ autoreconf -i $ ./configure --prefix=$PWD/build --enable-lib-only $ make -j$(nproc) $ make install $ cd .. Build ngtcp2: .. code-block:: text $ git clone --depth 1 -b v1.17.0 https://github.com/ngtcp2/ngtcp2 $ cd ngtcp2 $ git submodule update --init --depth 1 $ autoreconf -i $ ./configure --prefix=$PWD/build --enable-lib-only --with-boringssl \ BORINGSSL_CFLAGS="-I$PWD/../aws-lc/opt/include" \ BORINGSSL_LIBS="-L$PWD/../aws-lc/opt/lib -lssl -lcrypto" $ make -j$(nproc) $ make install $ cd .. If your Linux distribution does not have libbpf-dev >= 0.7.0, build from source: .. code-block:: text $ git clone --depth 1 -b v1.6.2 https://github.com/libbpf/libbpf $ cd libbpf $ PREFIX=$PWD/build make -C src install $ cd .. Build nghttp2: .. code-block:: text $ git clone https://github.com/nghttp2/nghttp2 $ cd nghttp2 $ git submodule update --init $ autoreconf -i $ ./configure --with-mruby --enable-http3 --with-libbpf \ CC=clang-19 CXX=clang++-19 \ PKG_CONFIG_PATH="$PWD/../aws-lc/opt/lib/pkgconfig:$PWD/../nghttp3/build/lib/pkgconfig:$PWD/../ngtcp2/build/lib/pkgconfig:$PWD/../libbpf/build/lib64/pkgconfig" \ LDFLAGS="$LDFLAGS -Wl,-rpath,$PWD/../aws-lc/opt/lib -Wl,-rpath,$PWD/../libbpf/build/lib64" $ make -j$(nproc) The eBPF program ``reuseport_kern.o`` should be found under bpf directory. Pass ``--quic-bpf-program-file=bpf/reuseport_kern.o`` option to nghttpx to load it. See also `HTTP/3 section in nghttpx - HTTP/2 proxy - HOW-TO `_. Unit tests ---------- Unit tests are done by simply running ``make check``. Integration tests ----------------- We have the integration tests for the nghttpx proxy server. The tests are written in the `Go programming language `_ and uses its testing framework. We depend on the following libraries: * golang.org/x/net/http2 * golang.org/x/net/websocket * https://github.com/tatsuhiro-t/go-nghttp2 Go modules will download these dependencies automatically. To run the tests, run the following command under ``integration-tests`` directory: .. code-block:: text $ make it Inside the tests, we use port 3009 to run the test subject server. Migration from v0.7.15 or earlier --------------------------------- nghttp2 v1.0.0 introduced several backward incompatible changes. In this section, we describe these changes and how to migrate to v1.0.0. ALPN protocol ID is now ``h2`` and ``h2c`` ++++++++++++++++++++++++++++++++++++++++++ Previously we announced ``h2-14`` and ``h2c-14``. v1.0.0 implements final protocol version, and we changed ALPN ID to ``h2`` and ``h2c``. The macros ``NGHTTP2_PROTO_VERSION_ID``, ``NGHTTP2_PROTO_VERSION_ID_LEN``, ``NGHTTP2_CLEARTEXT_PROTO_VERSION_ID``, and ``NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN`` have been updated to reflect this change. Basically, existing applications do not have to do anything, just recompiling is enough for this change. Use word "client magic" where we use "client connection preface" ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ We use "client connection preface" to mean first 24 bytes of client connection preface. This is technically not correct, since client connection preface is composed of 24 bytes client magic byte string followed by SETTINGS frame. For clarification, we call "client magic" for this 24 bytes byte string and updated API. * ``NGHTTP2_CLIENT_CONNECTION_PREFACE`` was replaced with ``NGHTTP2_CLIENT_MAGIC``. * ``NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN`` was replaced with ``NGHTTP2_CLIENT_MAGIC_LEN``. * ``NGHTTP2_BAD_PREFACE`` was renamed as ``NGHTTP2_BAD_CLIENT_MAGIC`` The already deprecated ``NGHTTP2_CLIENT_CONNECTION_HEADER`` and ``NGHTTP2_CLIENT_CONNECTION_HEADER_LEN`` were removed. If application uses these macros, just replace old ones with new ones. Since v1.0.0, client magic is sent by library (see next subsection), so client application may just remove these macro use. Client magic is sent by library +++++++++++++++++++++++++++++++ Previously nghttp2 library did not send client magic, which is first 24 bytes byte string of client connection preface, and client applications have to send it by themselves. Since v1.0.0, client magic is sent by library via first call of ``nghttp2_session_send()`` or ``nghttp2_session_mem_send2()``. The client applications which send client magic must remove the relevant code. Remove HTTP Alternative Services (Alt-Svc) related code +++++++++++++++++++++++++++++++++++++++++++++++++++++++ Alt-Svc specification is not finalized yet. To make our API stable, we have decided to remove all Alt-Svc related API from nghttp2. * ``NGHTTP2_EXT_ALTSVC`` was removed. * ``nghttp2_ext_altsvc`` was removed. We have already removed the functionality of Alt-Svc in v0.7 series and they have been essentially noop. The application using these macro and struct, remove those lines. Use nghttp2_error in nghttp2_on_invalid_frame_recv_callback +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Previously ``nghttp2_on_invalid_frame_recv_cb_called`` took the ``error_code``, defined in ``nghttp2_error_code``, as parameter. But they are not detailed enough to debug. Therefore, we decided to use more detailed ``nghttp2_error`` values instead. The application using this callback should update the callback signature. If it treats ``error_code`` as HTTP/2 error code, update the code so that it is treated as ``nghttp2_error``. Receive client magic by default +++++++++++++++++++++++++++++++ Previously nghttp2 did not process client magic (24 bytes byte string). To make it deal with it, we had to use ``nghttp2_option_set_recv_client_preface()``. Since v1.0.0, nghttp2 processes client magic by default and ``nghttp2_option_set_recv_client_preface()`` was removed. Some application may want to disable this behaviour, so we added ``nghttp2_option_set_no_recv_client_magic()`` to achieve this. The application using ``nghttp2_option_set_recv_client_preface()`` with nonzero value, just remove it. The application using ``nghttp2_option_set_recv_client_preface()`` with zero value or not using it must use ``nghttp2_option_set_no_recv_client_magic()`` with nonzero value. Client, Server and Proxy programs --------------------------------- The ``src`` directory contains the HTTP/2 client, server and proxy programs. nghttp - client +++++++++++++++ ``nghttp`` is a HTTP/2 client. It can connect to the HTTP/2 server with prior knowledge, HTTP Upgrade and ALPN TLS extension. It has verbose output mode for framing information. Here is sample output from ``nghttp`` client: .. code-block:: text $ nghttp -nv https://nghttp2.org [ 0.190] Connected The negotiated protocol: h2 [ 0.212] recv SETTINGS frame (niv=2) [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] [ 0.212] send SETTINGS frame (niv=2) [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] [ 0.212] send SETTINGS frame ; ACK (niv=0) [ 0.212] send PRIORITY frame (dep_stream_id=0, weight=201, exclusive=0) [ 0.212] send PRIORITY frame (dep_stream_id=0, weight=101, exclusive=0) [ 0.212] send PRIORITY frame (dep_stream_id=0, weight=1, exclusive=0) [ 0.212] send PRIORITY frame (dep_stream_id=7, weight=1, exclusive=0) [ 0.212] send PRIORITY frame (dep_stream_id=3, weight=1, exclusive=0) [ 0.212] send HEADERS frame ; END_STREAM | END_HEADERS | PRIORITY (padlen=0, dep_stream_id=11, weight=16, exclusive=0) ; Open new stream :method: GET :path: / :scheme: https :authority: nghttp2.org accept: */* accept-encoding: gzip, deflate user-agent: nghttp2/1.0.1-DEV [ 0.221] recv SETTINGS frame ; ACK (niv=0) [ 0.221] recv (stream_id=13) :method: GET [ 0.221] recv (stream_id=13) :scheme: https [ 0.221] recv (stream_id=13) :path: /stylesheets/screen.css [ 0.221] recv (stream_id=13) :authority: nghttp2.org [ 0.221] recv (stream_id=13) accept-encoding: gzip, deflate [ 0.222] recv (stream_id=13) user-agent: nghttp2/1.0.1-DEV [ 0.222] recv PUSH_PROMISE frame ; END_HEADERS (padlen=0, promised_stream_id=2) [ 0.222] recv (stream_id=13) :status: 200 [ 0.222] recv (stream_id=13) date: Thu, 21 May 2015 16:38:14 GMT [ 0.222] recv (stream_id=13) content-type: text/html [ 0.222] recv (stream_id=13) last-modified: Fri, 15 May 2015 15:38:06 GMT [ 0.222] recv (stream_id=13) etag: W/"555612de-19f6" [ 0.222] recv (stream_id=13) link: ; rel=preload; as=stylesheet [ 0.222] recv (stream_id=13) content-encoding: gzip [ 0.222] recv (stream_id=13) server: nghttpx nghttp2/1.0.1-DEV [ 0.222] recv (stream_id=13) via: 1.1 nghttpx [ 0.222] recv (stream_id=13) strict-transport-security: max-age=31536000 [ 0.222] recv HEADERS frame ; END_HEADERS (padlen=0) ; First response header [ 0.222] recv DATA frame ; END_STREAM [ 0.222] recv (stream_id=2) :status: 200 [ 0.222] recv (stream_id=2) date: Thu, 21 May 2015 16:38:14 GMT [ 0.222] recv (stream_id=2) content-type: text/css [ 0.222] recv (stream_id=2) last-modified: Fri, 15 May 2015 15:38:06 GMT [ 0.222] recv (stream_id=2) etag: W/"555612de-9845" [ 0.222] recv (stream_id=2) content-encoding: gzip [ 0.222] recv (stream_id=2) server: nghttpx nghttp2/1.0.1-DEV [ 0.222] recv (stream_id=2) via: 1.1 nghttpx [ 0.222] recv (stream_id=2) strict-transport-security: max-age=31536000 [ 0.222] recv HEADERS frame ; END_HEADERS (padlen=0) ; First push response header [ 0.228] recv DATA frame ; END_STREAM [ 0.228] send GOAWAY frame (last_stream_id=2, error_code=NO_ERROR(0x00), opaque_data(0)=[]) The HTTP Upgrade is performed like so: .. code-block:: text $ nghttp -nvu http://nghttp2.org [ 0.011] Connected [ 0.011] HTTP Upgrade request GET / HTTP/1.1 Host: nghttp2.org Connection: Upgrade, HTTP2-Settings Upgrade: h2c HTTP2-Settings: AAMAAABkAAQAAP__ Accept: */* User-Agent: nghttp2/1.0.1-DEV [ 0.018] HTTP Upgrade response HTTP/1.1 101 Switching Protocols Connection: Upgrade Upgrade: h2c [ 0.018] HTTP Upgrade success [ 0.018] recv SETTINGS frame (niv=2) [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] [ 0.018] send SETTINGS frame (niv=2) [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] [ 0.018] send SETTINGS frame ; ACK (niv=0) [ 0.018] send PRIORITY frame (dep_stream_id=0, weight=201, exclusive=0) [ 0.018] send PRIORITY frame (dep_stream_id=0, weight=101, exclusive=0) [ 0.018] send PRIORITY frame (dep_stream_id=0, weight=1, exclusive=0) [ 0.018] send PRIORITY frame (dep_stream_id=7, weight=1, exclusive=0) [ 0.018] send PRIORITY frame (dep_stream_id=3, weight=1, exclusive=0) [ 0.018] send PRIORITY frame (dep_stream_id=11, weight=16, exclusive=0) [ 0.019] recv (stream_id=1) :method: GET [ 0.019] recv (stream_id=1) :scheme: http [ 0.019] recv (stream_id=1) :path: /stylesheets/screen.css [ 0.019] recv (stream_id=1) host: nghttp2.org [ 0.019] recv (stream_id=1) user-agent: nghttp2/1.0.1-DEV [ 0.019] recv PUSH_PROMISE frame ; END_HEADERS (padlen=0, promised_stream_id=2) [ 0.019] recv (stream_id=1) :status: 200 [ 0.019] recv (stream_id=1) date: Thu, 21 May 2015 16:39:16 GMT [ 0.019] recv (stream_id=1) content-type: text/html [ 0.019] recv (stream_id=1) content-length: 6646 [ 0.019] recv (stream_id=1) last-modified: Fri, 15 May 2015 15:38:06 GMT [ 0.019] recv (stream_id=1) etag: "555612de-19f6" [ 0.019] recv (stream_id=1) link: ; rel=preload; as=stylesheet [ 0.019] recv (stream_id=1) accept-ranges: bytes [ 0.019] recv (stream_id=1) server: nghttpx nghttp2/1.0.1-DEV [ 0.019] recv (stream_id=1) via: 1.1 nghttpx [ 0.019] recv HEADERS frame ; END_HEADERS (padlen=0) ; First response header [ 0.019] recv DATA frame ; END_STREAM [ 0.019] recv (stream_id=2) :status: 200 [ 0.019] recv (stream_id=2) date: Thu, 21 May 2015 16:39:16 GMT [ 0.019] recv (stream_id=2) content-type: text/css [ 0.019] recv (stream_id=2) content-length: 38981 [ 0.019] recv (stream_id=2) last-modified: Fri, 15 May 2015 15:38:06 GMT [ 0.019] recv (stream_id=2) etag: "555612de-9845" [ 0.019] recv (stream_id=2) accept-ranges: bytes [ 0.019] recv (stream_id=2) server: nghttpx nghttp2/1.0.1-DEV [ 0.019] recv (stream_id=2) via: 1.1 nghttpx [ 0.019] recv HEADERS frame ; END_HEADERS (padlen=0) ; First push response header [ 0.026] recv DATA frame [ 0.027] recv DATA frame [ 0.027] send WINDOW_UPDATE frame (window_size_increment=33343) [ 0.032] send WINDOW_UPDATE frame (window_size_increment=33707) [ 0.032] recv DATA frame ; END_STREAM [ 0.032] recv SETTINGS frame ; ACK (niv=0) [ 0.032] send GOAWAY frame (last_stream_id=2, error_code=NO_ERROR(0x00), opaque_data(0)=[]) Using the ``-s`` option, ``nghttp`` prints out some timing information for requests, sorted by completion time: .. code-block:: text $ nghttp -nas https://nghttp2.org/ ***** Statistics ***** Request timing: responseEnd: the time when last byte of response was received relative to connectEnd requestStart: the time just before first byte of request was sent relative to connectEnd. If '*' is shown, this was pushed by server. process: responseEnd - requestStart code: HTTP status code size: number of bytes received as response body without inflation. URI: request URI see http://www.w3.org/TR/resource-timing/#processing-model sorted by 'complete' id responseEnd requestStart process code size request path 13 +37.19ms +280us 36.91ms 200 2K / 2 +72.65ms * +36.38ms 36.26ms 200 8K /stylesheets/screen.css 17 +77.43ms +38.67ms 38.75ms 200 3K /javascripts/octopress.js 15 +78.12ms +38.66ms 39.46ms 200 3K /javascripts/modernizr-2.0.js Using the ``-r`` option, ``nghttp`` writes more detailed timing data to the given file in HAR format. nghttpd - server ++++++++++++++++ ``nghttpd`` is a multi-threaded static web server. By default, it uses SSL/TLS connection. Use ``--no-tls`` option to disable it. ``nghttpd`` only accepts HTTP/2 connections via ALPN or direct HTTP/2 connections. No HTTP Upgrade is supported. The ``-p`` option allows users to configure server push. Just like ``nghttp``, it has a verbose output mode for framing information. Here is sample output from ``nghttpd``: .. code-block:: text $ nghttpd --no-tls -v 8080 IPv4: listen 0.0.0.0:8080 IPv6: listen :::8080 [id=1] [ 1.521] send SETTINGS frame (niv=1) [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] [id=1] [ 1.521] recv SETTINGS frame (niv=2) [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] [id=1] [ 1.521] recv SETTINGS frame ; ACK (niv=0) [id=1] [ 1.521] recv PRIORITY frame (dep_stream_id=0, weight=201, exclusive=0) [id=1] [ 1.521] recv PRIORITY frame (dep_stream_id=0, weight=101, exclusive=0) [id=1] [ 1.521] recv PRIORITY frame (dep_stream_id=0, weight=1, exclusive=0) [id=1] [ 1.521] recv PRIORITY frame (dep_stream_id=7, weight=1, exclusive=0) [id=1] [ 1.521] recv PRIORITY frame (dep_stream_id=3, weight=1, exclusive=0) [id=1] [ 1.521] recv (stream_id=13) :method: GET [id=1] [ 1.521] recv (stream_id=13) :path: / [id=1] [ 1.521] recv (stream_id=13) :scheme: http [id=1] [ 1.521] recv (stream_id=13) :authority: localhost:8080 [id=1] [ 1.521] recv (stream_id=13) accept: */* [id=1] [ 1.521] recv (stream_id=13) accept-encoding: gzip, deflate [id=1] [ 1.521] recv (stream_id=13) user-agent: nghttp2/1.0.0-DEV [id=1] [ 1.521] recv HEADERS frame ; END_STREAM | END_HEADERS | PRIORITY (padlen=0, dep_stream_id=11, weight=16, exclusive=0) ; Open new stream [id=1] [ 1.521] send SETTINGS frame ; ACK (niv=0) [id=1] [ 1.521] send HEADERS frame ; END_HEADERS (padlen=0) ; First response header :status: 200 server: nghttpd nghttp2/1.0.0-DEV content-length: 10 cache-control: max-age=3600 date: Fri, 15 May 2015 14:49:04 GMT last-modified: Tue, 30 Sep 2014 12:40:52 GMT [id=1] [ 1.522] send DATA frame ; END_STREAM [id=1] [ 1.522] stream_id=13 closed [id=1] [ 1.522] recv GOAWAY frame (last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[]) [id=1] [ 1.522] closed nghttpx - proxy +++++++++++++++ ``nghttpx`` is a multi-threaded reverse proxy for HTTP/3, HTTP/2, and HTTP/1.1, and powers http://nghttp2.org and supports HTTP/2 server push. We reworked ``nghttpx`` command-line interface, and as a result, there are several incompatibles from 1.8.0 or earlier. This is necessary to extend its capability, and secure the further feature enhancements in the future release. Please read `Migration from nghttpx v1.8.0 or earlier `_ to know how to migrate from earlier releases. ``nghttpx`` implements `important performance-oriented features `_ in TLS, such as session IDs, session tickets (with automatic key rotation), dynamic record sizing, ALPN, forward secrecy and HTTP/2. ``nghttpx`` also offers the functionality to share ticket keys among multiple ``nghttpx`` instances via memcached. ``nghttpx`` has 2 operation modes: ================== ======================== ================ ============= Mode option Frontend Backend Note ================== ======================== ================ ============= default mode HTTP/3, HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Reverse proxy ``--http2-proxy`` HTTP/3, HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Forward proxy ================== ======================== ================ ============= The interesting mode at the moment is the default mode. It works like a reverse proxy and listens for HTTP/3, HTTP/2, and HTTP/1.1 and can be deployed as a SSL/TLS terminator for existing web server. In all modes, the frontend connections are encrypted by SSL/TLS by default. To disable encryption, use the ``no-tls`` keyword in ``--frontend`` option. If encryption is disabled, incoming HTTP/1.1 connections can be upgraded to HTTP/2 through HTTP Upgrade. On the other hard, backend connections are not encrypted by default. To encrypt backend connections, use ``tls`` keyword in ``--backend`` option. ``nghttpx`` supports a configuration file. See the ``--conf`` option and sample configuration file ``nghttpx.conf.sample``. In the default mode, ``nghttpx`` works as reverse proxy to the backend server: .. code-block:: text Client <-- (HTTP/3, HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1, HTTP/2) --> Web Server [reverse proxy] With the ``--http2-proxy`` option, it works as forward proxy, and it is so called secure HTTP/2 proxy: .. code-block:: text Client <-- (HTTP/3, HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy [secure proxy] (e.g., Squid, ATS) The ``Client`` in the above example needs to be configured to use ``nghttpx`` as secure proxy. At the time of this writing, both Chrome and Firefox support secure HTTP/2 proxy. One way to configure Chrome to use a secure proxy is to create a proxy.pac script like this: .. code-block:: javascript function FindProxyForURL(url, host) { return "HTTPS SERVERADDR:PORT"; } ``SERVERADDR`` and ``PORT`` is the hostname/address and port of the machine nghttpx is running on. Please note that Chrome requires a valid certificate for secure proxy. Then run Chrome with the following arguments: .. code-block:: text $ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn The backend HTTP/2 connections can be tunneled through an HTTP proxy. The proxy is specified using ``--backend-http-proxy-uri``. The following figure illustrates how nghttpx talks to the outside HTTP/2 proxy through an HTTP proxy: .. code-block:: text Client <-- (HTTP/3, HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) -- --===================---> HTTP/2 Proxy (HTTP proxy tunnel) (e.g., nghttpx -s) Benchmarking tool ----------------- The ``h2load`` program is a benchmarking tool for HTTP/3, HTTP/2, and HTTP/1.1. The UI of ``h2load`` is heavily inspired by ``weighttp`` (https://github.com/lighttpd/weighttp). The typical usage is as follows: .. code-block:: text $ h2load -n100000 -c100 -m100 https://localhost:8443/ starting benchmark... spawning thread #0: 100 concurrent clients, 100000 total requests Protocol: TLSv1.2 Cipher: ECDHE-RSA-AES128-GCM-SHA256 Server Temp Key: ECDH P-256 256 bits progress: 10% done progress: 20% done progress: 30% done progress: 40% done progress: 50% done progress: 60% done progress: 70% done progress: 80% done progress: 90% done progress: 100% done finished in 771.26ms, 129658 req/s, 4.71MB/s requests: 100000 total, 100000 started, 100000 done, 100000 succeeded, 0 failed, 0 errored status codes: 100000 2xx, 0 3xx, 0 4xx, 0 5xx traffic: 3812300 bytes total, 1009900 bytes headers, 1000000 bytes data min max mean sd +/- sd time for request: 25.12ms 124.55ms 51.07ms 15.36ms 84.87% time for connect: 208.94ms 254.67ms 241.38ms 7.95ms 63.00% time to 1st byte: 209.11ms 254.80ms 241.51ms 7.94ms 63.00% The above example issued total 100,000 requests, using 100 concurrent clients (in other words, 100 HTTP/2 sessions), and a maximum of 100 streams per client. With the ``-t`` option, ``h2load`` will use multiple native threads to avoid saturating a single core on client side. .. warning:: **Don't use this tool against publicly available servers.** That is considered a DOS attack. Please only use it against your private servers. If the experimental HTTP/3 is enabled, h2load can send requests to HTTP/3 server. To do this, specify ``h3`` to ``--alpn-list`` option like so: .. code-block:: text $ h2load --alpn-list h3 https://127.0.0.1:4433 For nghttp2 v1.58 or earlier, use ``--npn-list`` instead of ``--alpn-list``. HPACK tools ----------- The ``src`` directory contains the HPACK tools. The ``deflatehd`` program is a command-line header compression tool. The ``inflatehd`` program is a command-line header decompression tool. Both tools read input from stdin and write output to stdout. Errors are written to stderr. They take JSON as input and output. We (mostly) use the same JSON data format described at https://github.com/http2jp/hpack-test-case. deflatehd - header compressor +++++++++++++++++++++++++++++ The ``deflatehd`` program reads JSON data or HTTP/1-style header fields from stdin and outputs compressed header block in JSON. For the JSON input, the root JSON object must include a ``cases`` key. Its value has to include the sequence of input header set. They share the same compression context and are processed in the order they appear. Each item in the sequence is a JSON object and it must include a ``headers`` key. Its value is an array of JSON objects, which includes exactly one name/value pair. Example: .. code-block:: json { "cases": [ { "headers": [ { ":method": "GET" }, { ":path": "/" } ] }, { "headers": [ { ":method": "POST" }, { ":path": "/" } ] } ] } With the ``-t`` option, the program can accept more familiar HTTP/1 style header field blocks. Each header set is delimited by an empty line: Example: .. code-block:: text :method: GET :scheme: https :path: / :method: POST user-agent: nghttp2 The output is in JSON object. It should include a ``cases`` key and its value is an array of JSON objects, which has at least the following keys: seq The index of header set in the input. input_length The sum of the length of the name/value pairs in the input. output_length The length of the compressed header block. percentage_of_original_size ``output_length`` / ``input_length`` * 100 wire The compressed header block as a hex string. headers The input header set. header_table_size The header table size adjusted before deflating the header set. Examples: .. code-block:: json { "cases": [ { "seq": 0, "input_length": 66, "output_length": 20, "percentage_of_original_size": 30.303030303030305, "wire": "01881f3468e5891afcbf83868a3d856659c62e3f", "headers": [ { ":authority": "example.org" }, { ":method": "GET" }, { ":path": "/" }, { ":scheme": "https" }, { "user-agent": "nghttp2" } ], "header_table_size": 4096 } , { "seq": 1, "input_length": 74, "output_length": 10, "percentage_of_original_size": 13.513513513513514, "wire": "88448504252dd5918485", "headers": [ { ":authority": "example.org" }, { ":method": "POST" }, { ":path": "/account" }, { ":scheme": "https" }, { "user-agent": "nghttp2" } ], "header_table_size": 4096 } ] } The output can be used as the input for ``inflatehd`` and ``deflatehd``. With the ``-d`` option, the extra ``header_table`` key is added and its associated value includes the state of dynamic header table after the corresponding header set was processed. The value includes at least the following keys: entries The entry in the header table. If ``referenced`` is ``true``, it is in the reference set. The ``size`` includes the overhead (32 bytes). The ``index`` corresponds to the index of header table. The ``name`` is the header field name and the ``value`` is the header field value. size The sum of the spaces entries occupied, this includes the entry overhead. max_size The maximum header table size. deflate_size The sum of the spaces entries occupied within ``max_deflate_size``. max_deflate_size The maximum header table size the encoder uses. This can be smaller than ``max_size``. In this case, the encoder only uses up to first ``max_deflate_size`` buffer. Since the header table size is still ``max_size``, the encoder has to keep track of entries outside the ``max_deflate_size`` but inside the ``max_size`` and make sure that they are no longer referenced. Example: .. code-block:: json { "cases": [ { "seq": 0, "input_length": 66, "output_length": 20, "percentage_of_original_size": 30.303030303030305, "wire": "01881f3468e5891afcbf83868a3d856659c62e3f", "headers": [ { ":authority": "example.org" }, { ":method": "GET" }, { ":path": "/" }, { ":scheme": "https" }, { "user-agent": "nghttp2" } ], "header_table_size": 4096, "header_table": { "entries": [ { "index": 1, "name": "user-agent", "value": "nghttp2", "referenced": true, "size": 49 }, { "index": 2, "name": ":scheme", "value": "https", "referenced": true, "size": 44 }, { "index": 3, "name": ":path", "value": "/", "referenced": true, "size": 38 }, { "index": 4, "name": ":method", "value": "GET", "referenced": true, "size": 42 }, { "index": 5, "name": ":authority", "value": "example.org", "referenced": true, "size": 53 } ], "size": 226, "max_size": 4096, "deflate_size": 226, "max_deflate_size": 4096 } } , { "seq": 1, "input_length": 74, "output_length": 10, "percentage_of_original_size": 13.513513513513514, "wire": "88448504252dd5918485", "headers": [ { ":authority": "example.org" }, { ":method": "POST" }, { ":path": "/account" }, { ":scheme": "https" }, { "user-agent": "nghttp2" } ], "header_table_size": 4096, "header_table": { "entries": [ { "index": 1, "name": ":method", "value": "POST", "referenced": true, "size": 43 }, { "index": 2, "name": "user-agent", "value": "nghttp2", "referenced": true, "size": 49 }, { "index": 3, "name": ":scheme", "value": "https", "referenced": true, "size": 44 }, { "index": 4, "name": ":path", "value": "/", "referenced": false, "size": 38 }, { "index": 5, "name": ":method", "value": "GET", "referenced": false, "size": 42 }, { "index": 6, "name": ":authority", "value": "example.org", "referenced": true, "size": 53 } ], "size": 269, "max_size": 4096, "deflate_size": 269, "max_deflate_size": 4096 } } ] } inflatehd - header decompressor +++++++++++++++++++++++++++++++ The ``inflatehd`` program reads JSON data from stdin and outputs decompressed name/value pairs in JSON. The root JSON object must include the ``cases`` key. Its value has to include the sequence of compressed header blocks. They share the same compression context and are processed in the order they appear. Each item in the sequence is a JSON object and it must have at least a ``wire`` key. Its value is a compressed header block as a hex string. Example: .. code-block:: json { "cases": [ { "wire": "8285" }, { "wire": "8583" } ] } The output is a JSON object. It should include a ``cases`` key and its value is an array of JSON objects, which has at least following keys: seq The index of the header set in the input. headers A JSON array that includes decompressed name/value pairs. wire The compressed header block as a hex string. header_table_size The header table size adjusted before inflating compressed header block. Example: .. code-block:: json { "cases": [ { "seq": 0, "wire": "01881f3468e5891afcbf83868a3d856659c62e3f", "headers": [ { ":authority": "example.org" }, { ":method": "GET" }, { ":path": "/" }, { ":scheme": "https" }, { "user-agent": "nghttp2" } ], "header_table_size": 4096 } , { "seq": 1, "wire": "88448504252dd5918485", "headers": [ { ":method": "POST" }, { ":path": "/account" }, { "user-agent": "nghttp2" }, { ":scheme": "https" }, { ":authority": "example.org" } ], "header_table_size": 4096 } ] } The output can be used as the input for ``deflatehd`` and ``inflatehd``. With the ``-d`` option, the extra ``header_table`` key is added and its associated value includes the state of the dynamic header table after the corresponding header set was processed. The format is the same as ``deflatehd``. Contribution ------------ [This text was composed based on 1.2. License section of curl/libcurl project.] When contributing with code, you agree to put your changes and new code under the same license nghttp2 is already using unless stated and agreed otherwise. When changing existing source code, do not alter the copyright of the original file(s). The copyright will still be owned by the original creator(s) or those who have been assigned copyright by the original author(s). By submitting a patch to the nghttp2 project, you (or your employer, as the case may be) agree to assign the copyright of your submission to us. .. the above really needs to be reworded to pass legal muster. We will credit you for your changes as far as possible, to give credit but also to keep a trace back to who made what changes. Please always provide us with your full real name when contributing! See `Contribution Guidelines `_ for more details. Versioning ---------- In general, we follow `Semantic Versioning `_. We may release PATCH releases between the regular releases, mainly for severe security bug fixes. We have no plan to break API compatibility changes involving soname bump, so MAJOR version will stay 1 for the foreseeable future. License ------- The MIT License nghttp2-1.68.0/PaxHeaders/proxy.pac.sample0000644000000000000000000000013215077107270015406 xustar0030 mtime=1761382072.983444185 30 atime=1761382104.863316334 30 ctime=1761382107.847304003 nghttp2-1.68.0/proxy.pac.sample0000644000175100017510000000024215077107270015774 0ustar00runnerrunnerfunction FindProxyForURL(url, host) { // For SPDY proxy return "HTTPS localhost:3000"; // For conventional HTTP proxy // return "PROXY localhost:3000"; } nghttp2-1.68.0/PaxHeaders/android-env0000644000000000000000000000013215077107270014411 xustar0030 mtime=1761382072.961444287 30 atime=1761382104.865316326 30 ctime=1761382107.850303994 nghttp2-1.68.0/android-env0000755000175100017510000000305715077107270015011 0ustar00runnerrunner#!/bin/sh # # nghttp2 - HTTP/2 C Library # # Copyright (c) 2022 nghttp2 contributors # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. if [ -z "$NDK" ]; then echo 'No $NDK specified.' exit 1 fi export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64 export TARGET=aarch64-linux-android export API=33 export AR=$TOOLCHAIN/bin/llvm-ar export CC=$TOOLCHAIN/bin/$TARGET$API-clang export CXX=$TOOLCHAIN/bin/$TARGET$API-clang++ export LD=$TOOLCHAIN/bin/ld export RANDLIB=$TOOLCHAIN/bin/llvm-ranlib export STRIP=$TOOLCHAIN/bin/llvm-strip export PREFIX=$NDK/usr/local nghttp2-1.68.0/PaxHeaders/configure.ac0000644000000000000000000000013215077107270014546 xustar0030 mtime=1761382072.962444282 30 atime=1761382080.397410149 30 ctime=1761382107.823304072 nghttp2-1.68.0/configure.ac0000644000175100017510000012666415077107270015155 0ustar00runnerrunnerdnl nghttp2 - HTTP/2 C Library dnl Copyright (c) 2012, 2013, 2014, 2015 Tatsuhiro Tsujikawa dnl Permission is hereby granted, free of charge, to any person obtaining dnl a copy of this software and associated documentation files (the dnl "Software"), to deal in the Software without restriction, including dnl without limitation the rights to use, copy, modify, merge, publish, dnl distribute, sublicense, and/or sell copies of the Software, and to dnl permit persons to whom the Software is furnished to do so, subject to dnl the following conditions: dnl The above copyright notice and this permission notice shall be dnl included in all copies or substantial portions of the Software. dnl THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, dnl EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF dnl MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND dnl NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE dnl LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION dnl OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION dnl WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. dnl Do not change user variables! dnl https://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html AC_PREREQ(2.61) AC_INIT([nghttp2], [1.68.0], [t-tujikawa@users.sourceforge.net]) AC_CONFIG_AUX_DIR([.]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADERS([config.h]) AC_USE_SYSTEM_EXTENSIONS LT_PREREQ([2.2.6]) LT_INIT() AC_CANONICAL_BUILD AC_CANONICAL_HOST AC_CANONICAL_TARGET AM_INIT_AUTOMAKE([subdir-objects tar-pax]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) dnl See versioning rule: dnl https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html AC_SUBST(LT_CURRENT, 43) AC_SUBST(LT_REVISION, 2) AC_SUBST(LT_AGE, 29) major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"` minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"` patch=`echo $PACKAGE_VERSION |cut -d. -f3 | cut -d- -f1 | sed -e "s/[^0-9]//g"` PACKAGE_VERSION_NUM=`printf "0x%02x%02x%02x" "$major" "$minor" "$patch"` AC_SUBST(PACKAGE_VERSION_NUM) dnl Checks for command-line options AC_ARG_ENABLE([werror], [AS_HELP_STRING([--enable-werror], [Turn on compile time warnings])], [werror=$enableval], [werror=no]) AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [Turn on debug output])], [debug=$enableval], [debug=no]) AC_ARG_ENABLE([threads], [AS_HELP_STRING([--disable-threads], [Turn off threading in apps])], [threads=$enableval], [threads=yes]) AC_ARG_ENABLE([app], [AS_HELP_STRING([--enable-app], [Build applications (nghttp, nghttpd, nghttpx and h2load) [default=check]])], [request_app=$enableval], [request_app=check]) AC_ARG_ENABLE([hpack-tools], [AS_HELP_STRING([--enable-hpack-tools], [Build HPACK tools [default=check]])], [request_hpack_tools=$enableval], [request_hpack_tools=check]) AC_ARG_ENABLE([examples], [AS_HELP_STRING([--enable-examples], [Build examples [default=check]])], [request_examples=$enableval], [request_examples=check]) AC_ARG_ENABLE([failmalloc], [AS_HELP_STRING([--disable-failmalloc], [Do not build failmalloc test program])], [request_failmalloc=$enableval], [request_failmalloc=yes]) AC_ARG_ENABLE([lib-only], [AS_HELP_STRING([--enable-lib-only], [Build libnghttp2 only. This is a short hand for --disable-app --disable-examples --disable-hpack-tools])], [request_lib_only=$enableval], [request_lib_only=no]) AC_ARG_ENABLE([http3], [AS_HELP_STRING([--enable-http3], [(EXPERIMENTAL) Enable HTTP/3. This requires ngtcp2, nghttp3, and a custom OpenSSL.])], [request_http3=$enableval], [request_http3=no]) AC_ARG_WITH([libxml2], [AS_HELP_STRING([--with-libxml2], [Use libxml2 [default=check]])], [request_libxml2=$withval], [request_libxml2=check]) AC_ARG_WITH([jansson], [AS_HELP_STRING([--with-jansson], [Use jansson [default=check]])], [request_jansson=$withval], [request_jansson=check]) AC_ARG_WITH([zlib], [AS_HELP_STRING([--with-zlib], [Use zlib [default=check]])], [request_zlib=$withval], [request_zlib=check]) AC_ARG_WITH([libevent-openssl], [AS_HELP_STRING([--with-libevent-openssl], [Use libevent_openssl [default=check]])], [request_libevent_openssl=$withval], [request_libevent_openssl=check]) AC_ARG_WITH([libcares], [AS_HELP_STRING([--with-libcares], [Use libc-ares [default=check]])], [request_libcares=$withval], [request_libcares=check]) AC_ARG_WITH([wolfssl], [AS_HELP_STRING([--with-wolfssl], [Use wolfSSL [default=check]])], [request_wolfssl=$withval], [request_wolfssl=check]) AC_ARG_WITH([openssl], [AS_HELP_STRING([--with-openssl], [Use openssl [default=check]])], [request_openssl=$withval], [request_openssl=check]) AC_ARG_WITH([libev], [AS_HELP_STRING([--with-libev], [Use libev [default=check]])], [request_libev=$withval], [request_libev=check]) AC_ARG_WITH([jemalloc], [AS_HELP_STRING([--with-jemalloc], [Use jemalloc [default=check]])], [request_jemalloc=$withval], [request_jemalloc=check]) AC_ARG_WITH([systemd], [AS_HELP_STRING([--with-systemd], [Enable systemd support in nghttpx [default=check]])], [request_systemd=$withval], [request_systemd=check]) AC_ARG_WITH([mruby], [AS_HELP_STRING([--with-mruby], [Use mruby [default=no]])], [request_mruby=$withval], [request_mruby=no]) AC_ARG_WITH([neverbleed], [AS_HELP_STRING([--with-neverbleed], [Use neverbleed [default=no]])], [request_neverbleed=$withval], [request_neverbleed=no]) AC_ARG_WITH([libngtcp2], [AS_HELP_STRING([--with-libngtcp2], [Use libngtcp2 [default=check]])], [request_libngtcp2=$withval], [request_libngtcp2=check]) AC_ARG_WITH([libnghttp3], [AS_HELP_STRING([--with-libnghttp3], [Use libnghttp3 [default=check]])], [request_libnghttp3=$withval], [request_libnghttp3=check]) AC_ARG_WITH([libbpf], [AS_HELP_STRING([--with-libbpf], [Use libbpf [default=no]])], [request_libbpf=$withval], [request_libbpf=no]) AC_ARG_WITH([libbrotlienc], [AS_HELP_STRING([--with-libbrotlienc], [Use libbrotlienc [default=no]])], [request_libbrotlienc=$withval], [request_libbrotlienc=no]) AC_ARG_WITH([libbrotlidec], [AS_HELP_STRING([--with-libbrotlidec], [Use libbrotlidec [default=no]])], [request_libbrotlidec=$withval], [request_libbrotlidec=no]) dnl Define variables AC_ARG_VAR([LIBEV_CFLAGS], [C compiler flags for libev, skipping any checks]) AC_ARG_VAR([LIBEV_LIBS], [linker flags for libev, skipping any checks]) AC_ARG_VAR([JEMALLOC_CFLAGS], [C compiler flags for jemalloc, skipping any checks]) AC_ARG_VAR([JEMALLOC_LIBS], [linker flags for jemalloc, skipping any checks]) AC_ARG_VAR([LIBTOOL_LDFLAGS], [libtool specific flags (e.g., -static-libtool-libs)]) AC_ARG_VAR([BPFCFLAGS], [C compiler flags for bpf program]) dnl Checks for programs AC_PROG_CC AC_PROG_CXX AC_PROG_CPP AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET AC_PROG_MKDIR_P PKG_PROG_PKG_CONFIG([0.20]) AM_PATH_PYTHON([3.8],, [:]) if [test "x$request_lib_only" = "xyes"]; then request_app=no request_hpack_tools=no request_examples=no request_http3=no request_libxml2=no request_jansson=no request_zlib=no request_libevent_openssl=no request_libcares=no request_openssl=no request_libev=no request_jemalloc=no request_systemd=no request_mruby=no request_neverbleed=no request_libngtcp2=no request_libnghttp3=no request_libbpf=no fi if test "x$GCC" = "xyes" -o "x$CC" = "xclang" ; then AC_DEFINE([NGHTTP2_NORETURN], [__attribute__((noreturn))], [Hint to the compiler that a function never return]) else AC_DEFINE([NGHTTP2_NORETURN], , [Hint to the compiler that a function never return]) fi save_CXXFLAGS="$CXXFLAGS" CXXFLAGS= AX_CXX_COMPILE_STDCXX([20], [], [optional]) CXX1XCXXFLAGS="$CXXFLAGS" CXXFLAGS="$save_CXXFLAGS" AC_SUBST([CXX1XCXXFLAGS]) AC_LANG_PUSH(C++) save_CXXFLAGS="$CXXFLAGS" CXXFLAGS="$CXXFLAGS $CXX1XCXXFLAGS" # Check that std::future is available. AC_MSG_CHECKING([whether std::future is available]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[ #include #include ]], [[ std::vector> v; (void)v; ]])], [AC_DEFINE([HAVE_STD_FUTURE], [1], [Define to 1 if you have the `std::future`.]) have_std_future=yes AC_MSG_RESULT([yes])], [have_std_future=no AC_MSG_RESULT([no])]) # Check that std::atomic> is supported. AC_MSG_CHECKING([whether std::atomic> is supported]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[ #include ]], [[ auto a = std::atomic>(std::make_shared(1000000007)); auto p = a.load(); ++*p; a.store(p); ]])], [AC_DEFINE([HAVE_ATOMIC_STD_SHARED_PTR], [1], [Define to 1 if you have the std::atomic> is supported.]) have_atomic_std_shared_ptr=yes AC_MSG_RESULT([yes])], [have_atomic_std_shared_ptr=no AC_MSG_RESULT([no])]) # Check that std::chrono::time_zone is available. AC_MSG_CHECKING([whether std::chrono::time_zone is available]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[ #include ]], [[ auto tz = std::chrono::current_zone(); (void)tz; ]])], [AC_DEFINE([HAVE_STD_CHRONO_TIME_ZONE], [1], [Define to 1 if you have the `std::chrono::time_zone`.]) have_std_chrono_time_zone=yes AC_MSG_RESULT([yes])], [have_std_chrono_time_zone=no AC_MSG_RESULT([no])]) CXXFLAGS=$save_CXXFLAGS AC_LANG_POP() # Checks for libraries. # Additional libraries required for tests. TESTLDADD= # Additional libraries required for programs under src directory. APPLDFLAGS= case "$host_os" in *android*) android_build=yes # android does not need -pthread, but needs following 2 libs for C++ APPLDFLAGS="$APPLDFLAGS -latomic" ;; *) PTHREAD_LDFLAGS="-pthread" APPLDFLAGS="$APPLDFLAGS $PTHREAD_LDFLAGS" ;; esac case "$host_os" in *solaris*) APPLDFLAGS="$APPLDFLAGS -lsocket -lnsl" ;; esac case "${build}" in *-apple-darwin*) EXTRA_DEFS="-D__APPLE_USE_RFC_3542" AC_SUBST([EXTRA_DEFS]) ;; esac # zlib have_zlib=no if test "x${request_zlib}" != "xno"; then PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3], [have_zlib=yes], [have_zlib=no]) if test "x${have_zlib}" = "xno"; then AC_MSG_NOTICE($ZLIB_PKG_ERRORS) fi fi if test "x${request_zlib}" = "xyes" && test "x${have_zlib}" != "xyes"; then AC_MSG_ERROR([zlib was requested (--with-zlib) but not found]) fi # dl: openssl requires libdl when it is statically linked. case "${host_os}" in *bsd*) # dlopen is in libc on *BSD ;; *) save_LIBS=$LIBS AC_SEARCH_LIBS([dlopen], [dl], [APPLDFLAGS="-ldl $APPLDFLAGS"], [], []) LIBS=$save_LIBS ;; esac # libev (for src) have_libev=no if test "x${request_libev}" != "xno"; then if test "x${LIBEV_LIBS}" = "x" && test "x${LIBEV_CFLAGS}" = "x"; then # libev does not have pkg-config file. Check it in an old way. save_LIBS=$LIBS # android requires -lm for floor AC_CHECK_LIB([ev], [ev_time], [have_libev=yes], [have_libev=no], [-lm]) if test "x${have_libev}" = "xyes"; then AC_CHECK_HEADER([ev.h], [have_libev=yes], [have_libev=no]) if test "x${have_libev}" = "xyes"; then LIBEV_LIBS=-lev LIBEV_CFLAGS= fi fi LIBS=$save_LIBS else have_libev=yes fi if test "x${have_libev}" = "xyes"; then AC_DEFINE([HAVE_LIBEV], [1], [Define to 1 if you have `libev` library.]) fi fi if test "x${request_libev}" = "xyes" && test "x${have_libev}" != "xyes"; then AC_MSG_ERROR([libev was requested (--with-libev) but not found]) fi if test "x${request_openssl}" = "xyes" && test "x${request_wolfssl}" = "xyes"; then AC_MSG_ERROR([Requesting both OpenSSL and wolfSSL is not allowed]) fi # openssl (for src) have_openssl=no if test "x${request_openssl}" != "xno" && test "x${request_wolfssl}" != "xyes"; then PKG_CHECK_MODULES([OPENSSL], [openssl >= 1.1.1], [have_openssl=yes], [have_openssl=no]) if test "x${have_openssl}" = "xno"; then AC_MSG_NOTICE($OPENSSL_PKG_ERRORS) else # Use C++ compiler because boringssl needs C++ runtime. AC_LANG_PUSH(C++) save_CXXFLAGS="$CXXFLAGS" save_LIBS="$LIBS" CXXFLAGS="$OPENSSL_CFLAGS $CXXFLAGS" LIBS="$OPENSSL_LIBS $LIBS" # quictls/openssl has SSL_provide_quic_data. boringssl also has # it. We will deal with it later. have_ssl_provide_quic_data=no AC_MSG_CHECKING([for SSL_provide_quic_data]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ SSL_provide_quic_data(NULL, (ssl_encryption_level_t)0, NULL, 0); ]])], [AC_MSG_RESULT([yes]); have_ssl_provide_quic_data=yes], [AC_MSG_RESULT([no]); have_ssl_provide_quic_data=no]) # Check whether this is libressl or not AC_CHECK_DECLS([LIBRESSL_VERSION_NUMBER], [have_libressl=yes], [have_libressl=no], [[ #include ]]) AC_MSG_CHECKING([for SSL_set_quic_tls_cbs]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ SSL_set_quic_tls_cbs(NULL, NULL, NULL); ]])], [AC_MSG_RESULT([yes]); have_ossl_quic=yes], [AC_MSG_RESULT([no]); have_ossl_quic=no]) # boringssl has SSL_set_quic_early_data_context. AC_MSG_CHECKING([for SSL_set_quic_early_data_context]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ SSL *ssl = NULL; SSL_set_quic_early_data_context(ssl, NULL, 0); ]])], [AC_MSG_RESULT([yes]); have_boringssl_quic=yes], [AC_MSG_RESULT([no]); have_boringssl_quic=no]) CXXFLAGS="$save_CXXFLAGS" LIBS="$save_LIBS" AC_LANG_POP() fi fi if test "x${request_openssl}" = "xyes" && test "x${have_openssl}" != "xyes"; then AC_MSG_ERROR([openssl was requested (--with-openssl) but not found]) fi # wolfSSL (for src) have_wolfssl=no if test "x${request_wolfssl}" != "xno" && test "x${request_openssl}" != "xyes" && test "x${have_openssl}" != "xyes"; then PKG_CHECK_MODULES([WOLFSSL], [wolfssl >= 5.7.0], [have_wolfssl=yes], [have_wolfssl=no]) if test "x${have_wolfssl}" = "xno"; then AC_MSG_NOTICE($WOLFSSL_PKG_ERRORS) else AC_DEFINE([HAVE_WOLFSSL], [1], [Define to 1 if you have 'wolfssl' library.]) save_CFLAGS="$CFLAGS" save_LIBS="$LIBS" CFLAGS="$WOLFSSL_CFLAGS $CFLAGS" LIBS="$WOLFSSL_LIBS $LIBS" have_wolfssl_quic=no AC_MSG_CHECKING([for wolfSSL QUIC]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ SSL_provide_quic_data(NULL, 0, NULL, 0); ]])], [AC_MSG_RESULT([yes]); have_wolfssl_quic=yes], [AC_MSG_RESULT([no]); have_wolfssl_quic=no]) CFLAGS="$save_CFLAGS" LIBS="$save_LIBS" fi fi if test "x${request_wolfssl}" = "xyes" && test "x${have_wolfssl}" != "xyes"; then AC_MSG_ERROR([wolfSSL was requested (--with-wolfssl) but not found]) fi # c-ares (for src) have_libcares=no if test "x${request_libcares}" != "xno"; then PKG_CHECK_MODULES([LIBCARES], [libcares >= 1.16.0], [have_libcares=yes], [have_libcares=no]) if test "x${have_libcares}" = "xno"; then AC_MSG_NOTICE($LIBCARES_PKG_ERRORS) fi fi if test "x${request_libcares}" = "xyes" && test "x${have_libcares}" != "xyes"; then AC_MSG_ERROR([libcares was requested (--with-libcares) but not found]) fi # ngtcp2 (for src) have_libngtcp2=no if test "x${request_libngtcp2}" != "xno"; then PKG_CHECK_MODULES([LIBNGTCP2], [libngtcp2 >= 1.16.0], [have_libngtcp2=yes], [have_libngtcp2=no]) if test "x${have_libngtcp2}" = "xno"; then AC_MSG_NOTICE($LIBNGTCP2_PKG_ERRORS) fi fi if test "x${request_libngtcp2}" = "xyes" && test "x${have_libngtcp2}" != "xyes"; then AC_MSG_ERROR([libngtcp2 was requested (--with-libngtcp2) but not found]) fi # ngtcp2_crypto_wolfssl (for src) have_libngtcp2_crypto_wolfssl=no if test "x${have_wolfssl_quic}" = "xyes" && test "x${request_libngtcp2}" != "xno"; then PKG_CHECK_MODULES([LIBNGTCP2_CRYPTO_WOLFSSL], [libngtcp2_crypto_wolfssl >= 1.16.0], [have_libngtcp2_crypto_wolfssl=yes], [have_libngtcp2_crypto_wolfssl=no]) if test "x${have_libngtcp2_crypto_wolfssl}" = "xno"; then AC_MSG_NOTICE($LIBNGTCP2_CRYPTO_WOLFSSL_PKG_ERRORS) else AC_DEFINE([HAVE_LIBNGTCP2_CRYPTO_WOLFSSL], [1], [Define to 1 if you have `libngtcp2_crypto_wolfssl` library.]) fi fi if test "x${have_wolfssl_quic}" = "xyes" && test "x${request_libngtcp2}" = "xyes" && test "x${have_libngtcp2_crypto_wolfssl}" != "xyes"; then AC_MSG_ERROR([libngtcp2_crypto_wolfssl was requested (--with-libngtcp2) but not found]) fi # ngtcp2_crypto_quictls (for src) have_libngtcp2_crypto_quictls=no if test "x${have_ssl_provide_quic_data}" = "xyes" && test "x${have_libressl}" != "xyes" && test "x${have_boringssl_quic}" != "xyes" && test "x${request_libngtcp2}" != "xno"; then PKG_CHECK_MODULES([LIBNGTCP2_CRYPTO_QUICTLS], [libngtcp2_crypto_quictls >= 1.16.0], [have_libngtcp2_crypto_quictls=yes], [have_libngtcp2_crypto_quictls=no]) if test "x${have_libngtcp2_crypto_quictls}" = "xno"; then AC_MSG_NOTICE($LIBNGTCP2_CRYPTO_QUICTLS_PKG_ERRORS) else AC_DEFINE([HAVE_LIBNGTCP2_CRYPTO_QUICTLS], [1], [Define to 1 if you have `libngtcp2_crypto_quictls` library.]) fi fi if test "x${have_ssl_provide_quic_data}" = "xyes" && test "x${have_libressl}" != "xyes" && test "x${have_boringssl_quic}" != "xyes" && test "x${request_libngtcp2}" = "xyes" && test "x${have_libngtcp2_crypto_quictls}" != "xyes"; then AC_MSG_ERROR([libngtcp2_crypto_quictls was requested (--with-libngtcp2) but not found]) fi # ngtcp2_crypto_libressl (for src) have_libngtcp2_crypto_libressl=no if test "x${have_ssl_provide_quic_data}" = "xyes" && test "x${have_libressl}" = "xyes" && test "x${request_libngtcp2}" != "xno"; then PKG_CHECK_MODULES([LIBNGTCP2_CRYPTO_LIBRESSL], [libngtcp2_crypto_libressl >= 1.16.0], [have_libngtcp2_crypto_libressl=yes], [have_libngtcp2_crypto_libressl=no]) if test "x${have_libngtcp2_crypto_libressl}" = "xno"; then AC_MSG_NOTICE($LIBNGTCP2_CRYPTO_LIBRESSL_PKG_ERRORS) else AC_DEFINE([HAVE_LIBNGTCP2_CRYPTO_LIBRESSL], [1], [Define to 1 if you have `libngtcp2_crypto_libressl` library.]) fi fi if test "x${have_ssl_provide_quic_data}" = "xyes" && test "x${have_libressl}" = "xyes" && test "x${request_libngtcp2}" = "xyes" && test "x${have_libngtcp2_crypto_libressl}" != "xyes"; then AC_MSG_ERROR([libngtcp2_crypto_libressl was requested (--with-libngtcp2) but not found]) fi # ngtcp2_crypto_boringssl (for src) have_libngtcp2_crypto_boringssl=no if test "x${have_boringssl_quic}" = "xyes" && test "x${request_libngtcp2}" != "xno"; then PKG_CHECK_MODULES([LIBNGTCP2_CRYPTO_BORINGSSL], [libngtcp2_crypto_boringssl >= 0.0.0], [have_libngtcp2_crypto_boringssl=yes], [have_libngtcp2_crypto_boringssl=no]) if test "x${have_libngtcp2_crypto_boringssl}" = "xno"; then AC_MSG_NOTICE($LIBNGTCP2_CRYPTO_BORINGSSL_PKG_ERRORS) else AC_DEFINE([HAVE_LIBNGTCP2_CRYPTO_BORINGSSL], [1], [Define to 1 if you have `libngtcp2_crypto_boringssl` library.]) fi fi if test "x${have_boringssl_quic}" = "xyes" && test "x${request_libngtcp2}" = "xyes" && test "x${have_libngtcp2_crypto_boringssl}" != "xyes"; then AC_MSG_ERROR([libngtcp2_crypto_boringssl was requested (--with-libngtcp2) but not found]) fi # ngtcp2_crypto_ossl (for src) have_libngtcp2_crypto_ossl=no if test "x${have_ossl_quic}" = "xyes" && test "x${request_libngtcp2}" != "xno"; then PKG_CHECK_MODULES([LIBNGTCP2_CRYPTO_OSSL], [libngtcp2_crypto_ossl >= 1.16.0], [have_libngtcp2_crypto_ossl=yes], [have_libngtcp2_crypto_ossl=no]) if test "x${have_libngtcp2_crypto_ossl}" = "xno"; then AC_MSG_NOTICE($LIBNGTCP2_CRYPTO_OSSL_PKG_ERRORS) else AC_DEFINE([HAVE_LIBNGTCP2_CRYPTO_OSSL], [1], [Define to 1 if you have `libngtcp2_crypto_ossl` library.]) fi fi if test "x${have_ossl_quic}" = "xyes" && test "x${request_libngtcp2}" = "xyes" && test "x${have_libngtcp2_crypto_ossl}" != "xyes"; then AC_MSG_ERROR([libngtcp2_crypto_ossl was requested (--with-libngtcp2) but not found]) fi # nghttp3 (for src) have_libnghttp3=no if test "x${request_libnghttp3}" != "xno"; then PKG_CHECK_MODULES([LIBNGHTTP3], [libnghttp3 >= 1.12.0], [have_libnghttp3=yes], [have_libnghttp3=no]) if test "x${have_libnghttp3}" = "xno"; then AC_MSG_NOTICE($LIBNGHTTP3_PKG_ERRORS) fi fi if test "x${request_libnghttp3}" = "xyes" && test "x${have_libnghttp3}" != "xyes"; then AC_MSG_ERROR([libnghttp3 was requested (--with-libnghttp3) but not found]) fi # libbpf (for src) have_libbpf=no if test "x${request_libbpf}" != "xno"; then PKG_CHECK_MODULES([LIBBPF], [libbpf >= 0.7.0], [have_libbpf=yes], [have_libbpf=no]) if test "x${have_libbpf}" = "xyes"; then AC_DEFINE([HAVE_LIBBPF], [1], [Define to 1 if you have `libbpf` library.]) if test "x${BPFCFLAGS}" = "x"; then BPFCFLAGS="-Wall -O2 -g" fi # Add the include path for Debian EXTRABPFCFLAGS="-I/usr/include/$host_cpu-$host_os" AC_SUBST([EXTRABPFCFLAGS]) AC_MSG_CHECKING([whether enum bpf_stats_type is defined in linux/bpf.h]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[ #include ]], [[ enum bpf_stats_type foo; (void)foo; ]])], [have_bpf_stats_type=yes], [have_bpf_stats_type=no]) if test "x${have_bpf_stats_type}" = "xyes"; then AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_BPF_STATS_TYPE], [1], [Define to 1 if you have enum bpf_stats_type in linux/bpf.h.]) else AC_MSG_RESULT([no]) fi else AC_MSG_NOTICE($LIBBPF_PKG_ERRORS) fi fi if test "x${request_libbpf}" = "xyes" && test "x${have_libbpf}" != "xyes"; then AC_MSG_ERROR([libbpf was requested (--with-libbpf) but not found]) fi AM_CONDITIONAL([HAVE_LIBBPF], [ test "x${have_libbpf}" = "xyes" ]) # libbrotlienc (for src) have_libbrotlienc=no if test "x${request_libbrotlienc}" != "xno"; then PKG_CHECK_MODULES([LIBBROTLIENC], [libbrotlienc >= 1.0.9], [have_libbrotlienc=yes], [have_libbrotlienc=no]) if test "x${have_libbrotlienc}" = "xno"; then AC_MSG_NOTICE($LIBBROTLIENC_PKG_ERRORS) fi fi if test "x${request_libbrotlienc}" = "xyes" && test "x${have_libbrotlienc}" != "xyes"; then AC_MSG_ERROR([libbrotlienc was requested (--with-libbrotlienc) but not found]) fi # libbrotlidec (for src) have_libbrotlidec=no if test "x${request_libbrotlidec}" != "xno"; then PKG_CHECK_MODULES([LIBBROTLIDEC], [libbrotlidec >= 1.0.9], [have_libbrotlidec=yes], [have_libbrotlidec=no]) if test "x${have_libbrotlidec}" = "xno"; then AC_MSG_NOTICE($LIBBROTLIDEC_PKG_ERRORS) fi fi if test "x${request_libbrotlidec}" = "xyes" && test "x${have_libbrotlidec}" != "xyes"; then AC_MSG_ERROR([libbrotlidec was requested (--with-libbrotlidec) but not found]) fi have_libbrotli=no if test "x${have_libbrotlienc}" = "xyes" && test "x${have_libbrotlidec}" = "xyes"; then have_libbrotli=yes AC_DEFINE([HAVE_LIBBROTLI], [1], [Define to 1 if you have `libbrotlienc` and `libbrotlidec` libraries.]) fi # libevent_openssl (for examples) # 2.0.8 is required because we use evconnlistener_set_error_cb() have_libevent_openssl=no if test "x${request_libevent_openssl}" != "xno"; then PKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.8], [have_libevent_openssl=yes], [have_libevent_openssl=no]) if test "x${have_libevent_openssl}" = "xno"; then AC_MSG_NOTICE($LIBEVENT_OPENSSL_PKG_ERRORS) fi fi if test "x${request_libevent_openssl}" = "xyes" && test "x${have_libevent_openssl}" != "xyes"; then AC_MSG_ERROR([libevent_openssl was requested (--with-libevent) but not found]) fi # jansson (for src/nghttp, src/deflatehd and src/inflatehd) have_jansson=no if test "x${request_jansson}" != "xno"; then PKG_CHECK_MODULES([JANSSON], [jansson >= 2.5], [have_jansson=yes], [have_jansson=no]) if test "x${have_jansson}" = "xyes"; then AC_DEFINE([HAVE_JANSSON], [1], [Define to 1 if you have `libjansson` library.]) else AC_MSG_NOTICE($JANSSON_PKG_ERRORS) fi fi if test "x${request_jansson}" = "xyes" && test "x${have_jansson}" != "xyes"; then AC_MSG_ERROR([jansson was requested (--with-jansson) but not found]) fi # libsystemd (for src/nghttpx) have_libsystemd=no if test "x${request_systemd}" != "xno"; then PKG_CHECK_MODULES([SYSTEMD], [libsystemd >= 209], [have_libsystemd=yes], [have_libsystemd=no]) if test "x${have_libsystemd}" = "xyes"; then AC_DEFINE([HAVE_LIBSYSTEMD], [1], [Define to 1 if you have `libsystemd` library.]) else AC_MSG_NOTICE($SYSTEMD_PKG_ERRORS) fi fi if test "x${request_systemd}" = "xyes" && test "x${have_libsystemd}" != "xyes"; then AC_MSG_ERROR([systemd was requested (--with-systemd) but not found]) fi # libxml2 (for src/nghttp) have_libxml2=no if test "x${request_libxml2}" != "xno"; then PKG_CHECK_MODULES([LIBXML2], [libxml-2.0 >= 2.6.26], [have_libxml2=yes], [have_libxml2=no]) if test "x${have_libxml2}" = "xyes"; then AC_DEFINE([HAVE_LIBXML2], [1], [Define to 1 if you have `libxml2` library.]) else AC_MSG_NOTICE($LIBXML2_PKG_ERRORS) fi fi if test "x${request_libxml2}" = "xyes" && test "x${have_libxml2}" != "xyes"; then AC_MSG_ERROR([libxml2 was requested (--with-libxml2) but not found]) fi AM_CONDITIONAL([HAVE_LIBXML2], [ test "x${have_libxml2}" = "xyes" ]) # jemalloc have_jemalloc=no if test "x${request_jemalloc}" != "xno"; then if test "x${JEMALLOC_LIBS}" = "x" && test "x${JEMALLOC_CFLAGS}" = "x"; then save_LIBS=$LIBS AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes], [], [$PTHREAD_LDFLAGS]) if test "x${have_jemalloc}" = "xyes"; then jemalloc_libs=${ac_cv_search_malloc_stats_print} else # On Darwin, malloc_stats_print is je_malloc_stats_print AC_SEARCH_LIBS([je_malloc_stats_print], [jemalloc], [have_jemalloc=yes], [], [$PTHREAD_LDFLAGS]) if test "x${have_jemalloc}" = "xyes"; then jemalloc_libs=${ac_cv_search_je_malloc_stats_print} fi fi LIBS=$save_LIBS if test "x${have_jemalloc}" = "xyes" && test "x${jemalloc_libs}" != "xnone required"; then JEMALLOC_LIBS=${jemalloc_libs} fi else have_jemalloc=yes fi fi if test "x${request_jemalloc}" = "xyes" && test "x${have_jemalloc}" != "xyes"; then AC_MSG_ERROR([jemalloc was requested (--with-jemalloc) but not found]) fi # The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL, # libev, and libc-ares. enable_app=no if test "x${request_app}" != "xno" && test "x${have_zlib}" = "xyes" && (test "x${have_openssl}" = "xyes" || test "x${have_wolfssl}" = "xyes") && test "x${have_libev}" = "xyes" && test "x${have_libcares}" = "xyes"; then enable_app=yes fi if test "x${request_app}" = "xyes" && test "x${enable_app}" != "xyes"; then AC_MSG_ERROR([applications were requested (--enable-app) but dependencies are not met.]) fi AM_CONDITIONAL([ENABLE_APP], [ test "x${enable_app}" = "xyes" ]) # Check HTTP/3 support enable_http3=no if test "x${request_http3}" != "xno" && test "x${have_libngtcp2}" = "xyes" && (test "x${have_libngtcp2_crypto_wolfssl}" = "xyes" || test "x${have_libngtcp2_crypto_quictls}" = "xyes" || test "x${have_libngtcp2_crypto_libressl}" = "xyes" || test "x${have_libngtcp2_crypto_boringssl}" = "xyes" || test "x${have_libngtcp2_crypto_ossl}" = "xyes") && test "x${have_libnghttp3}" = "xyes"; then enable_http3=yes AC_DEFINE([ENABLE_HTTP3], [1], [Define to 1 if HTTP/3 is enabled.]) fi if test "x${request_http3}" = "xyes" && test "x${enable_http3}" != "xyes"; then AC_MSG_ERROR([HTTP/3 was requested (--enable-http3) but dependencies are not met.]) fi AM_CONDITIONAL([ENABLE_HTTP3], [ test "x${enable_http3}" = "xyes" ]) enable_hpack_tools=no # HPACK tools requires jansson if test "x${request_hpack_tools}" != "xno" && test "x${have_jansson}" = "xyes"; then enable_hpack_tools=yes fi if test "x${request_hpack_tools}" = "xyes" && test "x${enable_hpack_tools}" != "xyes"; then AC_MSG_ERROR([HPACK tools were requested (--enable-hpack-tools) but dependencies are not met.]) fi AM_CONDITIONAL([ENABLE_HPACK_TOOLS], [ test "x${enable_hpack_tools}" = "xyes" ]) # The example programs depend on OpenSSL and libevent_openssl enable_examples=no if test "x${request_examples}" != "xno" && test "x${have_openssl}" = "xyes" && test "x${have_libevent_openssl}" = "xyes"; then enable_examples=yes fi if test "x${request_examples}" = "xyes" && test "x${enable_examples}" != "xyes"; then AC_MSG_ERROR([examples were requested (--enable-examples) but dependencies are not met.]) fi AM_CONDITIONAL([ENABLE_EXAMPLES], [ test "x${enable_examples}" = "xyes" ]) # third-party only be built when needed enable_third_party=no have_mruby=no have_neverbleed=no if test "x${enable_examples}" = "xyes" || test "x${enable_app}" = "xyes" || test "x${enable_hpack_tools}" = "xyes"; then enable_third_party=yes # mruby (for src/nghttpx) if test "x${request_mruby}" = "xyes"; then # We are going to build mruby have_mruby=yes AC_DEFINE([HAVE_MRUBY], [1], [Define to 1 if you have `mruby` library.]) LIBMRUBY_LIBS="-lmruby -lm" LIBMRUBY_CFLAGS= AC_SUBST([LIBMRUBY_LIBS]) AC_SUBST([LIBMRUBY_CFLAGS]) fi # neverbleed (for src/nghttpx) if test "x${request_neverbleed}" = "xyes"; then have_neverbleed=yes AC_DEFINE([HAVE_NEVERBLEED], [1], [Define to 1 if you have `neverbleed` library.]) fi fi AM_CONDITIONAL([ENABLE_THIRD_PARTY], [ test "x${enable_third_party}" = "xyes" ]) AM_CONDITIONAL([HAVE_MRUBY], [test "x${have_mruby}" = "xyes"]) AM_CONDITIONAL([HAVE_NEVERBLEED], [test "x${have_neverbleed}" = "xyes"]) # failmalloc tests enable_failmalloc=no if test "x${request_failmalloc}" = "xyes"; then enable_failmalloc=yes fi AM_CONDITIONAL([ENABLE_FAILMALLOC], [ test "x${enable_failmalloc}" = "xyes" ]) # Checks for header files. AC_HEADER_ASSERT AC_CHECK_HEADERS([ \ arpa/inet.h \ fcntl.h \ inttypes.h \ limits.h \ netdb.h \ netinet/in.h \ netinet/ip.h \ pwd.h \ stddef.h \ stdint.h \ stdlib.h \ string.h \ sys/socket.h \ sys/time.h \ syslog.h \ unistd.h \ windows.h \ ]) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_SIZE_T AC_TYPE_SSIZE_T AC_TYPE_UINT8_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT64_T AC_TYPE_INT8_T AC_TYPE_INT16_T AC_TYPE_INT32_T AC_TYPE_INT64_T AC_TYPE_OFF_T AC_TYPE_PID_T AC_TYPE_UID_T AC_CHECK_TYPES([ptrdiff_t]) AC_C_BIGENDIAN AC_C_INLINE AC_SYS_LARGEFILE AC_CHECK_MEMBER([struct tm.tm_gmtoff], [have_struct_tm_tm_gmtoff=yes], [have_struct_tm_tm_gmtoff=no], [[#include ]]) AC_CHECK_MEMBER([struct sockaddr_in.sin_len], [AC_DEFINE([HAVE_SOCKADDR_IN_SIN_LEN],[1], [Define to 1 if struct sockaddr_in has sin_len member.])], [], [[ #include #include #include ]]) AC_CHECK_MEMBER([struct sockaddr_in6.sin6_len], [AC_DEFINE([HAVE_SOCKADDR_IN6_SIN6_LEN],[1], [Define to 1 if struct sockaddr_in6 has sin6_len member.])], [], [[ #include #include #include ]]) if test "x$have_struct_tm_tm_gmtoff" = "xyes"; then AC_DEFINE([HAVE_STRUCT_TM_TM_GMTOFF], [1], [Define to 1 if you have `struct tm.tm_gmtoff` member.]) fi # Checks for library functions. # Don't check malloc, since it does not play nicely with C++ stdlib # AC_FUNC_MALLOC AC_FUNC_CHOWN AC_FUNC_ERROR_AT_LINE AC_FUNC_FORK # Don't check realloc, since LeakSanitizer detects memory leak during check # AC_FUNC_REALLOC AC_FUNC_STRERROR_R AC_FUNC_STRNLEN AC_CHECK_FUNCS([ \ _Exit \ accept4 \ clock_gettime \ dup2 \ getcwd \ getpwnam \ localtime_r \ memchr \ memmove \ memset \ mkostemp \ pipe2 \ socket \ sqrt \ strchr \ strdup \ strerror \ strndup \ strstr \ strtol \ strtoul \ timegm \ ]) # timerfd_create was added in linux kernel 2.6.25 AC_CHECK_FUNC([timerfd_create], [have_timerfd_create=yes], [have_timerfd_create=no]) AC_MSG_CHECKING([checking for GetTickCount64]) AC_LINK_IFELSE([AC_LANG_PROGRAM( [[ #include ]], [[ GetTickCount64(); ]])], [have_gettickcount64=yes], [have_gettickcount64=no]) if test "x${have_gettickcount64}" = "xyes"; then AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_GETTICKCOUNT64], [1], [Define to 1 if you have `GetTickCount64` function.]) else AC_MSG_RESULT([no]) fi # For cygwin: we can link initgroups, so AC_CHECK_FUNCS succeeds, but # cygwin disables initgroups due to feature test macro magic with our # configuration. FreeBSD declares initgroups() in unistd.h. AC_CHECK_DECLS([initgroups], [], [], [[ #ifdef HAVE_UNISTD_H # include #endif #include ]]) AC_CHECK_DECLS([CLOCK_MONOTONIC], [], [], [[ #include ]]) save_CFLAGS=$CFLAGS save_CXXFLAGS=$CXXFLAGS CFLAGS= CXXFLAGS= if test "x$werror" != "xno"; then # For C compiler AX_CHECK_COMPILE_FLAG([-Wall], [CFLAGS="$CFLAGS -Wall"]) AX_CHECK_COMPILE_FLAG([-Wextra], [CFLAGS="$CFLAGS -Wextra"]) AX_CHECK_COMPILE_FLAG([-Werror], [CFLAGS="$CFLAGS -Werror"]) AX_CHECK_COMPILE_FLAG([-Wmissing-prototypes], [CFLAGS="$CFLAGS -Wmissing-prototypes"]) AX_CHECK_COMPILE_FLAG([-Wstrict-prototypes], [CFLAGS="$CFLAGS -Wstrict-prototypes"]) AX_CHECK_COMPILE_FLAG([-Wmissing-declarations], [CFLAGS="$CFLAGS -Wmissing-declarations"]) AX_CHECK_COMPILE_FLAG([-Wpointer-arith], [CFLAGS="$CFLAGS -Wpointer-arith"]) AX_CHECK_COMPILE_FLAG([-Wdeclaration-after-statement], [CFLAGS="$CFLAGS -Wdeclaration-after-statement"]) AX_CHECK_COMPILE_FLAG([-Wformat-security], [CFLAGS="$CFLAGS -Wformat-security"]) AX_CHECK_COMPILE_FLAG([-Wwrite-strings], [CFLAGS="$CFLAGS -Wwrite-strings"]) AX_CHECK_COMPILE_FLAG([-Wshadow], [CFLAGS="$CFLAGS -Wshadow"]) AX_CHECK_COMPILE_FLAG([-Winline], [CFLAGS="$CFLAGS -Winline"]) AX_CHECK_COMPILE_FLAG([-Wnested-externs], [CFLAGS="$CFLAGS -Wnested-externs"]) AX_CHECK_COMPILE_FLAG([-Wfloat-equal], [CFLAGS="$CFLAGS -Wfloat-equal"]) AX_CHECK_COMPILE_FLAG([-Wundef], [CFLAGS="$CFLAGS -Wundef"]) AX_CHECK_COMPILE_FLAG([-Wendif-labels], [CFLAGS="$CFLAGS -Wendif-labels"]) AX_CHECK_COMPILE_FLAG([-Wempty-body], [CFLAGS="$CFLAGS -Wempty-body"]) AX_CHECK_COMPILE_FLAG([-Wcast-align], [CFLAGS="$CFLAGS -Wcast-align"]) AX_CHECK_COMPILE_FLAG([-Wclobbered], [CFLAGS="$CFLAGS -Wclobbered"]) AX_CHECK_COMPILE_FLAG([-Wvla], [CFLAGS="$CFLAGS -Wvla"]) AX_CHECK_COMPILE_FLAG([-Wpragmas], [CFLAGS="$CFLAGS -Wpragmas"]) AX_CHECK_COMPILE_FLAG([-Wunreachable-code], [CFLAGS="$CFLAGS -Wunreachable-code"]) AX_CHECK_COMPILE_FLAG([-Waddress], [CFLAGS="$CFLAGS -Waddress"]) AX_CHECK_COMPILE_FLAG([-Wattributes], [CFLAGS="$CFLAGS -Wattributes"]) AX_CHECK_COMPILE_FLAG([-Wdiv-by-zero], [CFLAGS="$CFLAGS -Wdiv-by-zero"]) AX_CHECK_COMPILE_FLAG([-Wshorten-64-to-32], [CFLAGS="$CFLAGS -Wshorten-64-to-32"]) AX_CHECK_COMPILE_FLAG([-Wconversion], [CFLAGS="$CFLAGS -Wconversion"]) AX_CHECK_COMPILE_FLAG([-Wextended-offsetof], [CFLAGS="$CFLAGS -Wextended-offsetof"]) AX_CHECK_COMPILE_FLAG([-Wformat-nonliteral], [CFLAGS="$CFLAGS -Wformat-nonliteral"]) AX_CHECK_COMPILE_FLAG([-Wlanguage-extension-token], [CFLAGS="$CFLAGS -Wlanguage-extension-token"]) AX_CHECK_COMPILE_FLAG([-Wmissing-field-initializers], [CFLAGS="$CFLAGS -Wmissing-field-initializers"]) AX_CHECK_COMPILE_FLAG([-Wmissing-noreturn], [CFLAGS="$CFLAGS -Wmissing-noreturn"]) AX_CHECK_COMPILE_FLAG([-Wmissing-variable-declarations], [CFLAGS="$CFLAGS -Wmissing-variable-declarations"]) # Not used because we cannot change public structs # AX_CHECK_COMPILE_FLAG([-Wpadded], [CFLAGS="$CFLAGS -Wpadded"]) AX_CHECK_COMPILE_FLAG([-Wsign-conversion], [CFLAGS="$CFLAGS -Wsign-conversion"]) # Not used because this basically disallows default case # AX_CHECK_COMPILE_FLAG([-Wswitch-enum], [CFLAGS="$CFLAGS -Wswitch-enum"]) AX_CHECK_COMPILE_FLAG([-Wunreachable-code-break], [CFLAGS="$CFLAGS -Wunreachable-code-break"]) AX_CHECK_COMPILE_FLAG([-Wunused-macros], [CFLAGS="$CFLAGS -Wunused-macros"]) AX_CHECK_COMPILE_FLAG([-Wunused-parameter], [CFLAGS="$CFLAGS -Wunused-parameter"]) AX_CHECK_COMPILE_FLAG([-Wredundant-decls], [CFLAGS="$CFLAGS -Wredundant-decls"]) # Only work with Clang for the moment AX_CHECK_COMPILE_FLAG([-Wheader-guard], [CFLAGS="$CFLAGS -Wheader-guard"]) AX_CHECK_COMPILE_FLAG([-Wsometimes-uninitialized], [CFLAGS="$CFLAGS -Wsometimes-uninitialized"]) AX_CHECK_COMPILE_FLAG([-Wextra-semi], [CFLAGS="$CFLAGS -Wextra-semi"]) # This is required because we pass format string as "const char*. AX_CHECK_COMPILE_FLAG([-Wno-format-nonliteral], [CFLAGS="$CFLAGS -Wno-format-nonliteral"]) # For C++ compiler AC_LANG_PUSH(C++) AX_CHECK_COMPILE_FLAG([-Wall], [CXXFLAGS="$CXXFLAGS -Wall"]) AX_CHECK_COMPILE_FLAG([-Werror], [CXXFLAGS="$CXXFLAGS -Werror"]) AX_CHECK_COMPILE_FLAG([-Wformat-security], [CXXFLAGS="$CXXFLAGS -Wformat-security"]) AX_CHECK_COMPILE_FLAG([-Wsometimes-uninitialized], [CXXFLAGS="$CXXFLAGS -Wsometimes-uninitialized"]) AX_CHECK_COMPILE_FLAG([-Wextra-semi], [CXXFLAGS="$CXXFLAGS -Wextra-semi"]) AX_CHECK_COMPILE_FLAG([-Wconversion], [CXXFLAGS="$CXXFLAGS -Wconversion"]) # Disable noexcept-type warning of g++-7. This is not harmful as # long as all source files are compiled with the same compiler. AX_CHECK_COMPILE_FLAG([-Wno-noexcept-type], [CXXFLAGS="$CXXFLAGS -Wno-noexcept-type"]) # clang++-18 warns this when building with wolfSSL >= v5.7.6-stable. AX_CHECK_COMPILE_FLAG([-Wno-extern-c-compat], [CXXFLAGS="$CXXFLAGS -Wno-extern-c-compat"]) AC_LANG_POP() fi WARNCFLAGS=$CFLAGS WARNCXXFLAGS=$CXXFLAGS CFLAGS=$save_CFLAGS CXXFLAGS=$save_CXXFLAGS AC_SUBST([WARNCFLAGS]) AC_SUBST([WARNCXXFLAGS]) EXTRACFLAG= AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [EXTRACFLAG="-fvisibility=hidden"]) AC_SUBST([EXTRACFLAG]) if test "x$debug" != "xno"; then AC_DEFINE([DEBUGBUILD], [1], [Define to 1 to enable debug output.]) fi enable_threads=yes # Some platform does not have working std::future. We disable # threading for those platforms. if test "x$threads" != "xyes" || test "x$have_std_future" != "xyes"; then enable_threads=no AC_DEFINE([NOTHREADS], [1], [Define to 1 if you want to disable threads.]) fi # propagate $enable_static to tests/Makefile.am AM_CONDITIONAL([ENABLE_STATIC], [test "x$enable_static" = "xyes"]) AC_SUBST([TESTLDADD]) AC_SUBST([APPLDFLAGS]) AC_CONFIG_FILES([ Makefile lib/Makefile lib/libnghttp2.pc lib/includes/Makefile lib/includes/nghttp2/nghttp2ver.h tests/Makefile tests/testdata/Makefile third-party/Makefile src/Makefile src/testdata/Makefile bpf/Makefile examples/Makefile integration-tests/Makefile integration-tests/config.go integration-tests/setenv doc/Makefile doc/conf.py doc/index.rst doc/package_README.rst doc/tutorial-client.rst doc/tutorial-server.rst doc/tutorial-hpack.rst doc/nghttpx-howto.rst doc/h2load-howto.rst doc/building-android-binary.rst doc/nghttp2.h.rst doc/nghttp2ver.h.rst doc/contribute.rst contrib/Makefile ]) AC_OUTPUT AC_MSG_NOTICE([summary of build options: Package version: ${VERSION} Library version: $LT_CURRENT:$LT_REVISION:$LT_AGE Install prefix: ${prefix} System types: Build: ${build} Host: ${host} Target: ${target} Compiler: C compiler: ${CC} CFLAGS: ${CFLAGS} LDFLAGS: ${LDFLAGS} C++ compiler: ${CXX} CXXFLAGS: ${CXXFLAGS} CXXCPP: ${CXXCPP} C preprocessor: ${CPP} CPPFLAGS: ${CPPFLAGS} WARNCFLAGS: ${WARNCFLAGS} WARNCXXFLAGS: ${WARNCXXFLAGS} CXX1XCXXFLAGS: ${CXX1XCXXFLAGS} EXTRACFLAG: ${EXTRACFLAG} BPFCFLAGS: ${BPFCFLAGS} EXTRABPFCFLAGS: ${EXTRABPFCFLAGS} LIBS: ${LIBS} DEFS: ${DEFS} EXTRA_DEFS: ${EXTRA_DEFS} Library: Shared: ${enable_shared} Static: ${enable_static} Libtool: LIBTOOL_LDFLAGS: ${LIBTOOL_LDFLAGS} Python: Python: ${PYTHON} PYTHON_VERSION: ${PYTHON_VERSION} Test: Failmalloc: ${enable_failmalloc} Libs: wolfSSL: ${have_wolfssl} (CFLAGS='${WOLFSSL_CFLAGS}' LIBS='${WOLFSSL_LIBS}') OpenSSL: ${have_openssl} (CFLAGS='${OPENSSL_CFLAGS}' LIBS='${OPENSSL_LIBS}') Libxml2: ${have_libxml2} (CFLAGS='${LIBXML2_CFLAGS}' LIBS='${LIBXML2_LIBS}') Libev: ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}') Libc-ares: ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_LIBS}') libngtcp2: ${have_libngtcp2} (CFLAGS='${LIBNGTCP2_CFLAGS}' LIBS='${LIBNGTCP2_LIBS}') libngtcp2_crypto_quictls: ${have_libngtcp2_crypto_quictls} (CFLAGS='${LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_QUICTLS_LIBS}') libngtcp2_crypto_libressl: ${have_libngtcp2_crypto_libressl} (CFLAGS='${LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_LIBRESSL_LIBS}') libngtcp2_crypto_boringssl: ${have_libngtcp2_crypto_boringssl} (CFLAGS='${LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_BORINGSSL_LIBS}') libngtcp2_crypto_ossl: ${have_libngtcp2_crypto_ossl} (CFLAGS='${LIBNGTCP2_CRYPTO_OSSL_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_OSSL_LIBS}') libnghttp3: ${have_libnghttp3} (CFLAGS='${LIBNGHTTP3_CFLAGS}' LIBS='${LIBNGHTTP3_LIBS}') libbpf: ${have_libbpf} (CFLAGS='${LIBBPF_CFLAGS}' LIBS='${LIBBPF_LIBS}') Libevent(SSL): ${have_libevent_openssl} (CFLAGS='${LIBEVENT_OPENSSL_CFLAGS}' LIBS='${LIBEVENT_OPENSSL_LIBS}') Jansson: ${have_jansson} (CFLAGS='${JANSSON_CFLAGS}' LIBS='${JANSSON_LIBS}') Jemalloc: ${have_jemalloc} (CFLAGS='${JEMALLOC_CFLAGS}' LIBS='${JEMALLOC_LIBS}') Zlib: ${have_zlib} (CFLAGS='${ZLIB_CFLAGS}' LIBS='${ZLIB_LIBS}') Systemd: ${have_libsystemd} (CFLAGS='${SYSTEMD_CFLAGS}' LIBS='${SYSTEMD_LIBS}') Libbrotlienc: ${have_libbrotlienc} (CFLAGS="${LIBBROTLIENC_CFLAGS}' LIBS='${LIBBROTLIENC_LIBS}') Libbrotlidec: ${have_libbrotlidec} (CFLAGS="${LIBBROTLIDEC_CFLAGS}' LIBS='${LIBBROTLIDEC_LIBS}') Third-party: http-parser: ${enable_third_party} MRuby: ${have_mruby} (CFLAGS='${LIBMRUBY_CFLAGS}' LIBS='${LIBMRUBY_LIBS}') Neverbleed: ${have_neverbleed} Features: Applications: ${enable_app} HPACK tools: ${enable_hpack_tools} Examples: ${enable_examples} Threading: ${enable_threads} HTTP/3 (EXPERIMENTAL): ${enable_http3} ]) nghttp2-1.68.0/PaxHeaders/ChangeLog0000644000000000000000000000013015077107300014022 xustar0028 mtime=1761382080.1894111 30 atime=1761382104.848316401 30 ctime=1761382107.833304044 nghttp2-1.68.0/ChangeLog0000644000175100017510000007736015077107300014431 0ustar00runnerrunnercommit 534b74b72524e962c18c7146470914632ca7eb2d Author: Tatsuhiro Tsujikawa AuthorDate: 2025-10-25 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-10-25 Update bash_completion commit 090c7fe26cc3db91de6fcab47b112ba1cfa440b8 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-10-25 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-10-25 Update manual pages commit 527cdebfee9bcf2d2489a9895602b05a1ab221f6 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-10-25 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-10-25 Bump package and library versions commit a2667a66924372e656727fedaac6ef0f4ab1b1f6 Merge: 19fbcf52 aedc3487 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-10-22 Commit: GitHub CommitDate: 2025-10-22 Merge pull request #2544 from nghttp2/bump-ngtcp2 Bump ngtcp2 and its dependencies commit aedc348754e57e4f40880215108ab6baeec4da31 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-10-22 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-10-22 Bump ngtcp2 and its dependencies commit 19fbcf5238ec3a047fea1eda6723b0c79fd80f99 Merge: 0139746d 6fe99003 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-10-14 Commit: GitHub CommitDate: 2025-10-14 Merge pull request #2543 from nghttp2/remove-ticket_keys-from-WorkerEvent nghttpx: Remove unused ticket_keys from WorkerEvent commit 6fe99003dfc26ccaeec9cc162ac9014b309e81f8 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-10-14 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-10-14 nghttpx: Remove unused ticket_keys from WorkerEvent commit 0139746d53f86667d3954fd6df525c0fad3cc463 Merge: 26e2d535 8dd0c86b Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-10-14 Commit: GitHub CommitDate: 2025-10-14 Merge pull request #2542 from nghttp2/optimize-quic-io Optimize quic io commit 8dd0c86bdec889a2b9e44e49bd3dc2945ab3e043 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-10-14 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-10-14 h2load: Prioritize QUIC UDP read event over the other events commit 5d4df477e896817774794dbd73dacd0875df3c6c Author: Tatsuhiro Tsujikawa AuthorDate: 2025-10-14 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-10-14 h2load: Defer write to the next event loop for QUIC commit 2b355a338cc6b1e3db322e3e081a0543fa431c9b Author: Tatsuhiro Tsujikawa AuthorDate: 2025-10-14 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-10-14 nghttpx: Prioritize QUIC UDP read event over the other events commit cfeec12a520147e241cc0ee3fa43fcefe5b8167a Author: Tatsuhiro Tsujikawa AuthorDate: 2025-10-14 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-10-14 nghttpx: Defer write to the next event loop for QUIC commit 26e2d53536334ec2d2f8c241a17ef79bda0528d1 Merge: 8f729331 d921c542 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-10-14 Commit: GitHub CommitDate: 2025-10-14 Merge pull request #2541 from nghttp2/dependabot/go_modules/golang.org/x/net-0.46.0 build(deps): bump golang.org/x/net from 0.44.0 to 0.46.0 commit d921c542099a17261b8039d15bf5f9230f6c9fe6 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> AuthorDate: 2025-10-13 Commit: GitHub CommitDate: 2025-10-13 build(deps): bump golang.org/x/net from 0.44.0 to 0.46.0 Bumps [golang.org/x/net](https://github.com/golang/net) from 0.44.0 to 0.46.0. - [Commits](https://github.com/golang/net/compare/v0.44.0...v0.46.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-version: 0.46.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] commit 8f729331c1e4acc97e67bafd923d2a2eab1e8d61 Merge: 2f1565b0 a25dd128 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-10-13 Commit: GitHub CommitDate: 2025-10-13 Merge pull request #2540 from nghttp2/nghttpx-quic-recv-pktcnt nghttpx: Increase number of UDP packets to read commit a25dd12811884a13a842e80db7875f3f82cfbbbf Author: Tatsuhiro Tsujikawa AuthorDate: 2025-10-13 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-10-13 nghttpx: Increase number of UDP packets to read It turns out that the limit of 10 packets per event loop is too small, that prevents an endpoint from consuming ACKs and other control frames (e.g., MAX_STREAM_DATA, MAX_STREAMS), resulting in the loss of throughput. This change increases maximum number of packets to read to 64. commit 2f1565b0e2385094ad90596960e99a7c573e40bd Merge: a60e00c6 389ae66d Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-10-07 Commit: GitHub CommitDate: 2025-10-07 Merge pull request #2538 from nghttp2/dependabot/go_modules/github.com/quic-go/quic-go-0.55.0 build(deps): bump github.com/quic-go/quic-go from 0.54.1 to 0.55.0 commit 389ae66d126bde8f9ea06a22e70daafb98838734 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> AuthorDate: 2025-10-06 Commit: GitHub CommitDate: 2025-10-06 build(deps): bump github.com/quic-go/quic-go from 0.54.1 to 0.55.0 Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.54.1 to 0.55.0. - [Release notes](https://github.com/quic-go/quic-go/releases) - [Commits](https://github.com/quic-go/quic-go/compare/v0.54.1...v0.55.0) --- updated-dependencies: - dependency-name: github.com/quic-go/quic-go dependency-version: 0.55.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] commit a60e00c6285b5dd6a1aee80f70bce77d0c198288 Merge: e802ccc0 53ce0886 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-30 Commit: GitHub CommitDate: 2025-09-30 Merge pull request #2537 from nghttp2/dependabot/go_modules/github.com/quic-go/quic-go-0.54.1 build(deps): bump github.com/quic-go/quic-go from 0.54.0 to 0.54.1 commit 53ce08869476ed4ed3c2ffe59a2fa9b13e32a87b Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> AuthorDate: 2025-09-29 Commit: GitHub CommitDate: 2025-09-29 build(deps): bump github.com/quic-go/quic-go from 0.54.0 to 0.54.1 Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.54.0 to 0.54.1. - [Release notes](https://github.com/quic-go/quic-go/releases) - [Commits](https://github.com/quic-go/quic-go/compare/v0.54.0...v0.54.1) --- updated-dependencies: - dependency-name: github.com/quic-go/quic-go dependency-version: 0.54.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] commit e802ccc02ad647ef48346b330c614583615d7118 Author: Copilot <198982749+Copilot@users.noreply.github.com> AuthorDate: 2025-09-29 Commit: GitHub CommitDate: 2025-09-29 Fix typos in documentation: "or3xx" → "or 3xx" and missing space after period (#2536) Fix typos in documentation: "or3xx" → "or 3xx" and "itself.To" → "itself. To" Co-authored-by: tatsuhiro-t <404610+tatsuhiro-t@users.noreply.github.com> commit 17428a5d09c7d79b92638e5a8e98847b18d63c6c Merge: fa585e91 83683742 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-29 Commit: GitHub CommitDate: 2025-09-29 Merge pull request #2535 from nghttp2/h2load-quic-window-bits-default h2load: Set QUIC window-bits to 24 by default commit 83683742f1bbc35797e14743e10799f66b8d6462 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-29 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-29 h2load: Set QUIC window-bits to 24 by default Reduce the default windows-bits for QUIC to 24 (16MiB). The previous default (1 << 30) is too large and causes too many packet losses on very fast connection with super low RTT. commit fa585e91821b7ccc7b329aa3fede300033e7c5e6 Merge: a2c47748 7434a370 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-28 Commit: GitHub CommitDate: 2025-09-28 Merge pull request #2534 from nghttp2/remove-redundant-semicolon Remove redundant semicolon commit 7434a370169a2ca6324265fec61cbea8a8f79ebb Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-28 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-28 Remove redundant semicolon commit a2c47748f07df9bdbe075ed7bdc5027f70566dd7 Merge: 59a9534a 1784c1c0 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-25 Commit: GitHub CommitDate: 2025-09-25 Merge pull request #2533 from nghttp2/iterators src: Use std::ranges::begin and std::ranges::end consistently commit 1784c1c0d105b046a22ffac47882223d30694cd0 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-25 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-25 src: Use std::ranges::begin and std::ranges::end consistently commit 59a9534a2da2d332e64156b7fd9bc310236953cf Merge: 4e0738d2 7fcbcd78 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-25 Commit: GitHub CommitDate: 2025-09-25 Merge pull request #2532 from nghttp2/adopt-nghttp3_conn_read_stream2 src: Adopt nghttp3_conn_read_stream2 commit 7fcbcd786e438d8019fbb9ce593e15bf26c1f03c Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-25 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-25 src: Adopt nghttp3_conn_read_stream2 Adopt nghttp3_conn_read_stream2 which requires nghttp3 v1.12.0. To pass the current timestamp, ngtcp2_conn_get_timestamp is used, which requires ngtcp2 v1.16.0. commit 4e0738d24ad615b0af6227c37164b86a5a206f5a Merge: 06e7219d 1e0413f4 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-25 Commit: GitHub CommitDate: 2025-09-25 Merge pull request #2531 from nghttp2/bump-ngtcp2 Bump ngtcp2 and its dependencies commit 1e0413f4a6a1c3befa5b623c381ef640d3c11cf1 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-25 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-25 Bump ngtcp2 and its dependencies commit 06e7219d108ea88f5a950440613eba5d85e25176 Merge: d8ed2559 c06c0691 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-24 Commit: GitHub CommitDate: 2025-09-24 Merge pull request #2530 from nghttp2/examples-consistent-cond-macro-comments examples: Consistent conditional macro comments commit c06c069126d85eff176401ab392b4985b7be791e Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-24 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-24 examples: Consistent conditional macro comments commit d8ed2559f68df53c258c2b7b9d6dfdb5b66f56f0 Merge: 6aa9f6c7 d829be35 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-24 Commit: GitHub CommitDate: 2025-09-24 Merge pull request #2529 from nghttp2/sgi-daemonize src: Move sgi _daemonize to util::daemonize commit d829be3517303c50c3d07be72282bf58ba2f5b20 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-24 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-24 src: Move sgi _daemonize to util::daemonize Move sgi _daemonize to util::daemonize so that we do not need to handle sgi case in the multiple places. Because we have no test environment for sgi machine, the flags adjustment is omitted. This is not a problem now because we only call util::daemonize with zeros. commit 6aa9f6c72e98985586ca3c76e48fa1c2626a294f Merge: 8f8eef40 4181fffc Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-24 Commit: GitHub CommitDate: 2025-09-24 Merge pull request #2528 from nghttp2/src-consistent-cond-macro-comments src: Consistent conditional macro comments commit 4181fffc02d9d290c23eb79bd3a0313caf381997 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-23 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-24 src: Consistent conditional macro comments commit 8f8eef40e8e47d98c595fe3a6be3690aa3c784f1 Merge: c218d441 3a95bf47 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-23 Commit: GitHub CommitDate: 2025-09-23 Merge pull request #2527 from nghttp2/lib-consistent-cond-macro-comments lib: Consistent conditional macro comments commit 3a95bf47f38d49e83a365c06771e31cfe668fb81 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-23 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-23 lib: Consistent conditional macro comments Make conditional macro comments consistent. - Repeat condition in closing #endif. - Use #ifdef for a single macro. Do not use #if defined(...) in this case. Use defined(...) form when repeating condition in #endif. - Apply De Morgan when negating conditions in #else. commit c218d441ea861e8cb6da0983dcdd81023754148e Merge: bcfb5d83 1952b166 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-23 Commit: GitHub CommitDate: 2025-09-23 Merge pull request #2526 from nghttp2/nullptr src: Use nullptr in C++ code commit 1952b166e9ac78e1ae4b386fb9c7ccce65203c35 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-22 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-22 src: Use nullptr in C++ code commit bcfb5d8305275c90f25becccd3f7b1d55accd6a6 Merge: 00bd05ed 37fb8262 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-22 Commit: GitHub CommitDate: 2025-09-22 Merge pull request #2525 from nghttp2/nghttpx-cert-type-constexpr nghttpx: Define NGHTTP2_CERT_TYPE as constexpr commit 37fb82621c9ad558c120203abadb1dffd078e9a7 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-22 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-22 nghttpx: Define NGHTTP2_CERT_TYPE as constexpr commit 00bd05edccf99e0cb8e36369fb9276a4ea8913ee Merge: d94ce2a5 45c67616 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-21 Commit: GitHub CommitDate: 2025-09-21 Merge pull request #2524 from nghttp2/nghttpx-drop-tlsv1.1 nghttpx: Drop TLSv1.0 and TLSv1.1 support commit 45c67616b9cc96262fb8506d9009af9bbf73e629 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-20 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-21 nghttpx: Drop TLSv1.0 and TLSv1.1 support Nowadays, people always use TLSv1.3. TLSv1.2 may be used for a particular situation where TLSv1.3 is not available due to TLS stack limitation. The large companies started to drop TLSv1.1 and earlier versions. I do not feel keeping their support without a strong reason, and I could not find any. commit d94ce2a5577155959e510281f4b1a2d5d3079a0c Merge: 6dfb3bdb 8a5c7315 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-21 Commit: GitHub CommitDate: 2025-09-21 Merge pull request #2523 from nghttp2/nghttpx-consistent-servername-cb-behavior nghttpx: Make servername_callback behavior consistent commit 8a5c731533c7f6c0fb205cfb7872a85b4af831d2 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-21 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-21 nghttpx: Make servername_callback behavior consistent Make servername_callback behavior consistent across all supported TLS stacks. RFC 6066 does not provide any guidance or requirement when a server must not acknowledge Server Name Indication. commit 6dfb3bdb8feee9b0dda1e6775c3f6257a7f7dd0e Merge: b35fa94b 43649c80 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-21 Commit: GitHub CommitDate: 2025-09-21 Merge pull request #2522 from nghttp2/nghttpx-wolfssl-support-mldsa-cert-select nghttpx: Support ML-DSA certificate selection with wolfSSL commit 43649c8004aeae7654b8440f4e4cb954f5f00121 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-21 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-21 nghttpx: Support ML-DSA certificate selection with wolfSSL commit b35fa94ba5e4138366f3b21c7c992cb91682cfc0 Merge: 7caa11f0 ee6565fe Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-21 Commit: GitHub CommitDate: 2025-09-21 Merge pull request #2521 from nghttp2/nghttpx-refactor-cert-type-detection nghttpx: Select a certificate in a single pass commit ee6565feb7de90275468d77fbed4b09a27c4dbec Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-20 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-21 nghttpx: Select a certificate in a single pass Refactored the certificate selection to select the certificate in a single pass. Cache the type of certificate to reduce the overhead. commit 7caa11f09e7595d6bc94b5dcaa9cca497763ed6e Merge: 4da70b34 cb73b18a Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-20 Commit: GitHub CommitDate: 2025-09-20 Merge pull request #2520 from nghttp2/nghttpx-cert-select-fast-path nghttpx: Add the fast path when selecting a certificate commit cb73b18a53fdfb26a87466d8e909d5c6f35c1c91 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-20 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-20 nghttpx: Add the fast path when selecting a certificate commit 4da70b34d18c8bc466d08e2d1b70867c49782ba8 Merge: 9fc31cfd cd868f00 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-20 Commit: GitHub CommitDate: 2025-09-20 Merge pull request #2519 from nghttp2/nghttpx-wolfssl-cert-select nghttpx: Select certificate with wolfSSL commit cd868f00b99eb20aa4633a01145bad74de8b7ba6 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-20 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-20 nghttpx: Select certificate with wolfSSL This change adds the certificate selection with the supported signature algorithms for wolfSSL in a way similar to BoringSSL. wolfSSL does not support ML-DSA certificate as of this writing. commit 9fc31cfd16522067448833dad57908c5a6c7a344 Merge: 5e75f0ac e9f04ae0 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-20 Commit: GitHub CommitDate: 2025-09-20 Merge pull request #2518 from nghttp2/nghttpx-boringssl-cert-select nghttpx: Select certificate with BoringSSL commit e9f04ae0ad1dce0d029d07dcba7d3e5de0f56fa8 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-20 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-20 nghttpx: Select certificate with BoringSSL Previously, the certificate selection in nghttpx depending on the supported signature algorithm is dedicated to OpenSSL. This change brings the same capability to the BoringSSL build. BoringSSL does not support ML-DSA certificate as of this writing. commit 5e75f0ac81f37d116feafbea1c2c7b2b9c5cc691 Merge: 9cbe936a 8c3f077c Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-20 Commit: GitHub CommitDate: 2025-09-20 Merge pull request #2517 from nghttp2/nghttpx-cert-select-pkey-base-id nghttpx: Select ECDSA cert based on EVP_PKEY_base_id commit 8c3f077c5e25f562a9c663f7d8726f1421be03db Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-20 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-20 nghttpx: Select ECDSA cert based on EVP_PKEY_base_id We once refactored this with the shared curves, but it seems that it is not entirely correct for this. Perhaps, the usage of X509_get_signature_nid was incorrect. commit 9cbe936a25e825b4a35a4d3fd52db18fa5ea7862 Merge: 59b6d0d1 b815972b Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-19 Commit: GitHub CommitDate: 2025-09-19 Merge pull request #2516 from nghttp2/nghttpx-ml-dsa-cert-select nghttpx: Prefer ML-DSA certificate over ECDSA commit b815972b0323b67f618790779c9e127af93b2c3d Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-19 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-19 nghttpx: Prefer ML-DSA certificate over ECDSA commit 59b6d0d1d9e910aaf8b80c1db764994aae3528b5 Merge: 1feb3679 028eeeef Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-19 Commit: GitHub CommitDate: 2025-09-19 Merge pull request #2515 from nghttp2/nghttpx-supported-groups nghttpx: Add groups option commit 028eeeefeb713154e8d1a53326c0c391f873b609 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-19 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-19 nghttpx: Add groups option The groups option takes the list of the supported groups. This deprecates ecdh-curves option. If ecdh-curves option is used, it is treated as if groups option is specified. commit 1feb3679fe781d950bdc4dcd9de2794755073bd3 Merge: 9df3962d b00d8da2 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-19 Commit: GitHub CommitDate: 2025-09-19 Merge pull request #2514 from nghttp2/groups-list Use SSL_CTX_set1_groups_list commit b00d8da2e2a03003615bcb56e6501ae02d063eb7 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-19 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-19 Use SSL_CTX_set1_groups_list Replace SSL_CTX_set1_curves_list with SSL_CTX_set1_groups_list. Remove the workaround for wolfSSL because the bug has been fixed. commit 9df3962d08058698c230c98d4d196a38df7bbe13 Merge: 280845e5 304bfcbb Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-19 Commit: GitHub CommitDate: 2025-09-19 Merge pull request #2513 from nghttp2/nghttpd-supported-groups nghttpd: Make the supported groups configurable commit 304bfcbb70c77a94f0c60e5b6b692d2457099031 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-19 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-19 nghttpd: Make the supported groups configurable Use the same default list of groups as h2load. commit 280845e52e29cc6a8acf7d17dfd54d32041fd7f2 Merge: c9ff3599 bdc5d5a6 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-16 Commit: GitHub CommitDate: 2025-09-16 Merge pull request #2511 from nghttp2/dependabot/go_modules/golang.org/x/net-0.44.0 build(deps): bump golang.org/x/net from 0.43.0 to 0.44.0 commit bdc5d5a6d10844b24b770ee904cef7ef749ba553 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> AuthorDate: 2025-09-15 Commit: GitHub CommitDate: 2025-09-15 build(deps): bump golang.org/x/net from 0.43.0 to 0.44.0 Bumps [golang.org/x/net](https://github.com/golang/net) from 0.43.0 to 0.44.0. - [Commits](https://github.com/golang/net/compare/v0.43.0...v0.44.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-version: 0.44.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] commit c9ff3599de36ffe0e4aadee7917e9e25a11a6b2f Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-15 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-15 Bump library version due to the patch release commit 15912bf810ef64e17e2177ce5ee2187dd8107c30 Merge: 80ecefeb 9e65104b Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-15 Commit: GitHub CommitDate: 2025-09-15 Merge pull request #2510 from nghttp2/remove-glitch-from-invalid-stream2 Remove session_update_glitch_ratelim called from deep inside the chain commit 9e65104b000cc7b7494ce0aa0424393acb5855d3 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-15 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-15 Remove session_update_glitch_ratelim called from deep inside the chain Calling session_update_glitch_ratelim from session_handle_invalid_stream2 makes handling error quite difficult because it might be called in nested function calls. It seems to me that adding that is accidental. commit 80ecefebb54c4d5ad173304e0e78fee1b4b0decf Merge: 89b30903 43b4369f Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-15 Commit: GitHub CommitDate: 2025-09-15 Merge pull request #2509 from nghttp2/fix-assertion-failure Fix assertion failure commit 43b4369fba1039b0e13176c8f089c6c9b9f8497a Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-15 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-15 Fix assertion failure Fix assertion failure due to the missing check for NGHTTP2_IB_IGN_ALL state. Add tests. commit 89b30903cc4d6db77f3e4a8faa4a02e20b4190e7 Merge: 3b45a194 4904c736 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-13 Commit: GitHub CommitDate: 2025-09-13 Merge pull request #2508 from nghttp2/more-builtin-ext-glitch Increase glitch counter for unexpected builtin extension frames commit 4904c736e1e6b71d8f1dd9c9948b4a68431883c1 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-13 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-13 Increase glitch counter for unexpected builtin extension frames commit 3b45a19423f6d627828d70e847533c07cc0b0b5e Merge: fbf4a7b7 877a7818 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-09 Commit: GitHub CommitDate: 2025-09-09 Merge pull request #2507 from nghttp2/dependabot/github_actions/actions/stale-10 build(deps): bump actions/stale from 9 to 10 commit fbf4a7b750a08764d19e375fcc09fe32ce6cc63e Merge: 2a190bf5 3d363ae4 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-09 Commit: GitHub CommitDate: 2025-09-09 Merge pull request #2506 from nghttp2/dependabot/github_actions/actions/setup-go-6 build(deps): bump actions/setup-go from 5 to 6 commit 2a190bf5ee67593348e464126cc3a2f158821bcd Merge: eacf3484 73141a76 Author: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> AuthorDate: 2025-09-09 Commit: GitHub CommitDate: 2025-09-09 Merge pull request #2505 from nghttp2/dependabot/github_actions/actions/github-script-8 build(deps): bump actions/github-script from 7 to 8 commit 877a78186c07e9cfbdd38e5df189e3f5de53af16 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> AuthorDate: 2025-09-08 Commit: GitHub CommitDate: 2025-09-08 build(deps): bump actions/stale from 9 to 10 Bumps [actions/stale](https://github.com/actions/stale) from 9 to 10. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/v9...v10) --- updated-dependencies: - dependency-name: actions/stale dependency-version: '10' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] commit 3d363ae478e30cb3806fba247477404b83a0c0af Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> AuthorDate: 2025-09-08 Commit: GitHub CommitDate: 2025-09-08 build(deps): bump actions/setup-go from 5 to 6 Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5 to 6. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/setup-go dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] commit 73141a7698e63d38751a183e72d9cc996268d56b Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> AuthorDate: 2025-09-08 Commit: GitHub CommitDate: 2025-09-08 build(deps): bump actions/github-script from 7 to 8 Bumps [actions/github-script](https://github.com/actions/github-script) from 7 to 8. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](https://github.com/actions/github-script/compare/v7...v8) --- updated-dependencies: - dependency-name: actions/github-script dependency-version: '8' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] commit eacf3484ca418486f8de10044f479adb3aa98229 Author: Tatsuhiro Tsujikawa AuthorDate: 2025-09-02 Commit: Tatsuhiro Tsujikawa CommitDate: 2025-09-02 Bump package version nghttp2-1.68.0/PaxHeaders/test-driver0000644000000000000000000000013215077107305014452 xustar0030 mtime=1761382085.739386429 30 atime=1761382105.071315418 30 ctime=1761382108.057303396 nghttp2-1.68.0/test-driver0000755000175100017510000001141715077107305015051 0ustar00runnerrunner#! /bin/sh # test-driver - basic testsuite driver script. scriptversion=2018-03-07.03; # UTC # Copyright (C) 2011-2021 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . # Make unconditional expansion of undefined variables an error. This # helps a lot in preventing typo-related bugs. set -u usage_error () { echo "$0: $*" >&2 print_usage >&2 exit 2 } print_usage () { cat <"$log_file" "$@" >>"$log_file" 2>&1 estatus=$? if test $enable_hard_errors = no && test $estatus -eq 99; then tweaked_estatus=1 else tweaked_estatus=$estatus fi case $tweaked_estatus:$expect_failure in 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; 0:*) col=$grn res=PASS recheck=no gcopy=no;; 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; *:*) col=$red res=FAIL recheck=yes gcopy=yes;; esac # Report the test outcome and exit status in the logs, so that one can # know whether the test passed or failed simply by looking at the '.log' # file, without the need of also peaking into the corresponding '.trs' # file (automake bug#11814). echo "$res $test_name (exit status: $estatus)" >>"$log_file" # Report outcome to console. echo "${col}${res}${std}: $test_name" # Register the test result, and other relevant metadata. echo ":test-result: $res" > $trs_file echo ":global-test-result: $res" >> $trs_file echo ":recheck: $recheck" >> $trs_file echo ":copy-in-global-log: $gcopy" >> $trs_file # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: nghttp2-1.68.0/PaxHeaders/config.guess0000644000000000000000000000013215077107305014574 xustar0030 mtime=1761382085.324388206 30 atime=1761382087.704378013 30 ctime=1761382107.839304026 nghttp2-1.68.0/config.guess0000755000175100017510000014051215077107305015172 0ustar00runnerrunner#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2022 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268 # see below for rationale timestamp='2022-01-09' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # https://git.savannah.gnu.org/cgit/config.git/plain/config.guess # # Please send patches to . # The "shellcheck disable" line above the timestamp inhibits complaints # about features and limitations of the classic Bourne shell that were # superseded or lifted in POSIX. However, this script identifies a wide # variety of pre-POSIX systems that do not have POSIX shells at all, and # even some reasonably current systems (Solaris 10 as case-in-point) still # have a pre-POSIX /bin/sh. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2022 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi # Just in case it came from the environment. GUESS= # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. tmp= # shellcheck disable=SC2172 trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 set_cc_for_build() { # prevent multiple calls if $tmp is already set test "$tmp" && return 0 : "${TMPDIR=/tmp}" # shellcheck disable=SC2039,SC3028 { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } dummy=$tmp/dummy case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in ,,) echo "int x;" > "$dummy.c" for driver in cc gcc c89 c99 ; do if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then CC_FOR_BUILD=$driver break fi done if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac } # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if test -f /.attbin/uname ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case $UNAME_SYSTEM in Linux|GNU|GNU/*) LIBC=unknown set_cc_for_build cat <<-EOF > "$dummy.c" #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #elif defined(__GLIBC__) LIBC=gnu #else #include /* First heuristic to detect musl libc. */ #ifdef __DEFINED_va_list LIBC=musl #endif #endif EOF cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` eval "$cc_set_libc" # Second heuristic to detect musl libc. if [ "$LIBC" = unknown ] && command -v ldd >/dev/null && ldd --version 2>&1 | grep -q ^musl; then LIBC=musl fi # If the system lacks a compiler, then just pick glibc. # We could probably try harder. if [ "$LIBC" = unknown ]; then LIBC=gnu fi ;; esac # Note: order is significant - the case branches are not exclusive. case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ /sbin/sysctl -n hw.machine_arch 2>/dev/null || \ /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \ echo unknown)` case $UNAME_MACHINE_ARCH in aarch64eb) machine=aarch64_be-unknown ;; armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` machine=${arch}${endian}-unknown ;; *) machine=$UNAME_MACHINE_ARCH-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently (or will in the future) and ABI. case $UNAME_MACHINE_ARCH in earm*) os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # Determine ABI tags. case $UNAME_MACHINE_ARCH in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case $UNAME_VERSION in Debian*) release='-gnu' ;; *) release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. GUESS=$machine-${os}${release}${abi-} ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE ;; *:SecBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE ;; *:LibertyBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE ;; *:MidnightBSD:*:*) GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE ;; *:ekkoBSD:*:*) GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE ;; *:SolidBSD:*:*) GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE ;; *:OS108:*:*) GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE ;; macppc:MirBSD:*:*) GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE ;; *:MirBSD:*:*) GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE ;; *:Sortix:*:*) GUESS=$UNAME_MACHINE-unknown-sortix ;; *:Twizzler:*:*) GUESS=$UNAME_MACHINE-unknown-twizzler ;; *:Redox:*:*) GUESS=$UNAME_MACHINE-unknown-redox ;; mips:OSF1:*.*) GUESS=mips-dec-osf1 ;; alpha:OSF1:*:*) # Reset EXIT trap before exiting to avoid spurious non-zero exit code. trap '' 0 case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case $ALPHA_CPU_TYPE in "EV4 (21064)") UNAME_MACHINE=alpha ;; "EV4.5 (21064)") UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") UNAME_MACHINE=alpha ;; "EV5 (21164)") UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` GUESS=$UNAME_MACHINE-dec-osf$OSF_REL ;; Amiga*:UNIX_System_V:4.0:*) GUESS=m68k-unknown-sysv4 ;; *:[Aa]miga[Oo][Ss]:*:*) GUESS=$UNAME_MACHINE-unknown-amigaos ;; *:[Mm]orph[Oo][Ss]:*:*) GUESS=$UNAME_MACHINE-unknown-morphos ;; *:OS/390:*:*) GUESS=i370-ibm-openedition ;; *:z/VM:*:*) GUESS=s390-ibm-zvmoe ;; *:OS400:*:*) GUESS=powerpc-ibm-os400 ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) GUESS=arm-acorn-riscix$UNAME_RELEASE ;; arm*:riscos:*:*|arm*:RISCOS:*:*) GUESS=arm-unknown-riscos ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) GUESS=hppa1.1-hitachi-hiuxmpp ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. case `(/bin/universe) 2>/dev/null` in att) GUESS=pyramid-pyramid-sysv3 ;; *) GUESS=pyramid-pyramid-bsd ;; esac ;; NILE*:*:*:dcosx) GUESS=pyramid-pyramid-svr4 ;; DRS?6000:unix:4.0:6*) GUESS=sparc-icl-nx6 ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) GUESS=sparc-icl-nx7 ;; esac ;; s390x:SunOS:*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL ;; sun4H:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-hal-solaris2$SUN_REL ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-sun-solaris2$SUN_REL ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) GUESS=i386-pc-auroraux$UNAME_RELEASE ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) set_cc_for_build SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 fi fi SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=$SUN_ARCH-pc-solaris2$SUN_REL ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-sun-solaris3$SUN_REL ;; sun4*:SunOS:*:*) case `/usr/bin/arch -k` in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` GUESS=sparc-sun-sunos$SUN_REL ;; sun3*:SunOS:*:*) GUESS=m68k-sun-sunos$UNAME_RELEASE ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 case `/bin/arch` in sun3) GUESS=m68k-sun-sunos$UNAME_RELEASE ;; sun4) GUESS=sparc-sun-sunos$UNAME_RELEASE ;; esac ;; aushp:SunOS:*:*) GUESS=sparc-auspex-sunos$UNAME_RELEASE ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) GUESS=m68k-milan-mint$UNAME_RELEASE ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) GUESS=m68k-hades-mint$UNAME_RELEASE ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) GUESS=m68k-unknown-mint$UNAME_RELEASE ;; m68k:machten:*:*) GUESS=m68k-apple-machten$UNAME_RELEASE ;; powerpc:machten:*:*) GUESS=powerpc-apple-machten$UNAME_RELEASE ;; RISC*:Mach:*:*) GUESS=mips-dec-mach_bsd4.3 ;; RISC*:ULTRIX:*:*) GUESS=mips-dec-ultrix$UNAME_RELEASE ;; VAX*:ULTRIX*:*:*) GUESS=vax-dec-ultrix$UNAME_RELEASE ;; 2020:CLIX:*:* | 2430:CLIX:*:*) GUESS=clipper-intergraph-clix$UNAME_RELEASE ;; mips:*:*:UMIPS | mips:*:*:RISCos) set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } GUESS=mips-mips-riscos$UNAME_RELEASE ;; Motorola:PowerMAX_OS:*:*) GUESS=powerpc-motorola-powermax ;; Motorola:*:4.3:PL8-*) GUESS=powerpc-harris-powermax ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) GUESS=powerpc-harris-powermax ;; Night_Hawk:Power_UNIX:*:*) GUESS=powerpc-harris-powerunix ;; m88k:CX/UX:7*:*) GUESS=m88k-harris-cxux7 ;; m88k:*:4*:R4*) GUESS=m88k-motorola-sysv4 ;; m88k:*:3*:R3*) GUESS=m88k-motorola-sysv3 ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 then if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ test "$TARGET_BINARY_INTERFACE"x = x then GUESS=m88k-dg-dgux$UNAME_RELEASE else GUESS=m88k-dg-dguxbcs$UNAME_RELEASE fi else GUESS=i586-dg-dgux$UNAME_RELEASE fi ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) GUESS=m88k-dolphin-sysv3 ;; M88*:*:R3*:*) # Delta 88k system running SVR3 GUESS=m88k-motorola-sysv3 ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) GUESS=m88k-tektronix-sysv3 ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) GUESS=m68k-tektronix-bsd ;; *:IRIX*:*:*) IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'` GUESS=mips-sgi-irix$IRIX_REL ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. GUESS=romp-ibm-aix # uname -m gives an 8 hex-code CPU id ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) GUESS=i386-ibm-aix ;; ia64:AIX:*:*) if test -x /usr/bin/oslevel ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then GUESS=$SYSTEM_NAME else GUESS=rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then GUESS=rs6000-ibm-aix3.2.4 else GUESS=rs6000-ibm-aix3.2 fi ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if test -x /usr/bin/lslpp ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \ awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi GUESS=$IBM_ARCH-ibm-aix$IBM_REV ;; *:AIX:*:*) GUESS=rs6000-ibm-aix ;; ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) GUESS=romp-ibm-bsd4.4 ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and GUESS=romp-ibm-bsd$UNAME_RELEASE # 4.3 with uname added to ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) GUESS=rs6000-bull-bosx ;; DPX/2?00:B.O.S.:*:*) GUESS=m68k-bull-sysv3 ;; 9000/[34]??:4.3bsd:1.*:*) GUESS=m68k-hp-bsd ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) GUESS=m68k-hp-bsd4.4 ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` case $UNAME_MACHINE in 9000/31?) HP_ARCH=m68000 ;; 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if test -x /usr/bin/getconf; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case $sc_cpu_version in 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case $sc_kernel_bits in 32) HP_ARCH=hppa2.0n ;; 64) HP_ARCH=hppa2.0w ;; '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi if test "$HP_ARCH" = ""; then set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if test "$HP_ARCH" = hppa2.0w then set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH=hppa2.0w else HP_ARCH=hppa64 fi fi GUESS=$HP_ARCH-hp-hpux$HPUX_REV ;; ia64:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` GUESS=ia64-hp-hpux$HPUX_REV ;; 3050*:HI-UX:*:*) set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } GUESS=unknown-hitachi-hiuxwe2 ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) GUESS=hppa1.1-hp-bsd ;; 9000/8??:4.3bsd:*:*) GUESS=hppa1.0-hp-bsd ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) GUESS=hppa1.0-hp-mpeix ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) GUESS=hppa1.1-hp-osf ;; hp8??:OSF1:*:*) GUESS=hppa1.0-hp-osf ;; i*86:OSF1:*:*) if test -x /usr/sbin/sysversion ; then GUESS=$UNAME_MACHINE-unknown-osf1mk else GUESS=$UNAME_MACHINE-unknown-osf1 fi ;; parisc*:Lites*:*:*) GUESS=hppa1.1-hp-lites ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) GUESS=c1-convex-bsd ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) GUESS=c34-convex-bsd ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) GUESS=c38-convex-bsd ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) GUESS=c4-convex-bsd ;; CRAY*Y-MP:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=ymp-cray-unicos$CRAY_REL ;; CRAY*[A-Z]90:*:*:*) echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=t90-cray-unicos$CRAY_REL ;; CRAY*T3E:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=alphaev5-cray-unicosmk$CRAY_REL ;; CRAY*SV1:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=sv1-cray-unicos$CRAY_REL ;; *:UNICOS/mp:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=craynv-cray-unicosmp$CRAY_REL ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE ;; sparc*:BSD/OS:*:*) GUESS=sparc-unknown-bsdi$UNAME_RELEASE ;; *:BSD/OS:*:*) GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE ;; arm:FreeBSD:*:*) UNAME_PROCESSOR=`uname -p` set_cc_for_build if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi else FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf fi ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case $UNAME_PROCESSOR in amd64) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL ;; i*:CYGWIN*:*) GUESS=$UNAME_MACHINE-pc-cygwin ;; *:MINGW64*:*) GUESS=$UNAME_MACHINE-pc-mingw64 ;; *:MINGW*:*) GUESS=$UNAME_MACHINE-pc-mingw32 ;; *:MSYS*:*) GUESS=$UNAME_MACHINE-pc-msys ;; i*:PW*:*) GUESS=$UNAME_MACHINE-pc-pw32 ;; *:SerenityOS:*:*) GUESS=$UNAME_MACHINE-pc-serenity ;; *:Interix*:*) case $UNAME_MACHINE in x86) GUESS=i586-pc-interix$UNAME_RELEASE ;; authenticamd | genuineintel | EM64T) GUESS=x86_64-unknown-interix$UNAME_RELEASE ;; IA64) GUESS=ia64-unknown-interix$UNAME_RELEASE ;; esac ;; i*:UWIN*:*) GUESS=$UNAME_MACHINE-pc-uwin ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) GUESS=x86_64-pc-cygwin ;; prep*:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=powerpcle-unknown-solaris2$SUN_REL ;; *:GNU:*:*) # the GNU system GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'` GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'` GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL ;; *:GNU/*:*:*) # other systems with GNU libc and userland GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"` GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC ;; *:Minix:*:*) GUESS=$UNAME_MACHINE-unknown-minix ;; aarch64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC=gnulibc1 ; fi GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; arm*:Linux:*:*) set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then GUESS=$UNAME_MACHINE-unknown-linux-$LIBC else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi else GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf fi fi ;; avr32*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; cris:Linux:*:*) GUESS=$UNAME_MACHINE-axis-linux-$LIBC ;; crisv32:Linux:*:*) GUESS=$UNAME_MACHINE-axis-linux-$LIBC ;; e2k:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; frv:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; hexagon:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; i*86:Linux:*:*) GUESS=$UNAME_MACHINE-pc-linux-$LIBC ;; ia64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; k1om:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; m32r*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; m68*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; mips:Linux:*:* | mips64:Linux:*:*) set_cc_for_build IS_GLIBC=0 test x"${LIBC}" = xgnu && IS_GLIBC=1 sed 's/^ //' << EOF > "$dummy.c" #undef CPU #undef mips #undef mipsel #undef mips64 #undef mips64el #if ${IS_GLIBC} && defined(_ABI64) LIBCABI=gnuabi64 #else #if ${IS_GLIBC} && defined(_ABIN32) LIBCABI=gnuabin32 #else LIBCABI=${LIBC} #endif #endif #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 CPU=mipsisa64r6 #else #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 CPU=mipsisa32r6 #else #if defined(__mips64) CPU=mips64 #else CPU=mips #endif #endif #endif #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) MIPS_ENDIAN=el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) MIPS_ENDIAN= #else MIPS_ENDIAN= #endif #endif EOF cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'` eval "$cc_set_vars" test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } ;; mips64el:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; openrisc*:Linux:*:*) GUESS=or1k-unknown-linux-$LIBC ;; or32:Linux:*:* | or1k*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; padre:Linux:*:*) GUESS=sparc-unknown-linux-$LIBC ;; parisc64:Linux:*:* | hppa64:Linux:*:*) GUESS=hppa64-unknown-linux-$LIBC ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;; PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;; *) GUESS=hppa-unknown-linux-$LIBC ;; esac ;; ppc64:Linux:*:*) GUESS=powerpc64-unknown-linux-$LIBC ;; ppc:Linux:*:*) GUESS=powerpc-unknown-linux-$LIBC ;; ppc64le:Linux:*:*) GUESS=powerpc64le-unknown-linux-$LIBC ;; ppcle:Linux:*:*) GUESS=powerpcle-unknown-linux-$LIBC ;; riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; s390:Linux:*:* | s390x:Linux:*:*) GUESS=$UNAME_MACHINE-ibm-linux-$LIBC ;; sh64*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; sh*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; sparc:Linux:*:* | sparc64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; tile*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; vax:Linux:*:*) GUESS=$UNAME_MACHINE-dec-linux-$LIBC ;; x86_64:Linux:*:*) set_cc_for_build LIBCABI=$LIBC if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_X32 >/dev/null then LIBCABI=${LIBC}x32 fi fi GUESS=$UNAME_MACHINE-pc-linux-$LIBCABI ;; xtensa*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. GUESS=i386-sequent-sysv4 ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. GUESS=$UNAME_MACHINE-pc-os2-emx ;; i*86:XTS-300:*:STOP) GUESS=$UNAME_MACHINE-unknown-stop ;; i*86:atheos:*:*) GUESS=$UNAME_MACHINE-unknown-atheos ;; i*86:syllable:*:*) GUESS=$UNAME_MACHINE-pc-syllable ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) GUESS=i386-unknown-lynxos$UNAME_RELEASE ;; i*86:*DOS:*:*) GUESS=$UNAME_MACHINE-pc-msdosdjgpp ;; i*86:*:4.*:*) UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL else GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL fi ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL else GUESS=$UNAME_MACHINE-pc-sysv32 fi ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. GUESS=i586-pc-msdosdjgpp ;; Intel:Mach:3*:*) GUESS=i386-pc-mach3 ;; paragon:*:*:*) GUESS=i860-intel-osf1 ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then GUESS=i860-stardent-sysv$UNAME_RELEASE # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. GUESS=i860-unknown-sysv$UNAME_RELEASE # Unknown i860-SVR4 fi ;; mini*:CTIX:SYS*5:*) # "miniframe" GUESS=m68010-convergent-sysv ;; mc68k:UNIX:SYSTEM5:3.51m) GUESS=m68k-convergent-sysv ;; M680?0:D-NIX:5.3:*) GUESS=m68k-diab-dnix ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) GUESS=m68k-unknown-lynxos$UNAME_RELEASE ;; mc68030:UNIX_System_V:4.*:*) GUESS=m68k-atari-sysv4 ;; TSUNAMI:LynxOS:2.*:*) GUESS=sparc-unknown-lynxos$UNAME_RELEASE ;; rs6000:LynxOS:2.*:*) GUESS=rs6000-unknown-lynxos$UNAME_RELEASE ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) GUESS=powerpc-unknown-lynxos$UNAME_RELEASE ;; SM[BE]S:UNIX_SV:*:*) GUESS=mips-dde-sysv$UNAME_RELEASE ;; RM*:ReliantUNIX-*:*:*) GUESS=mips-sni-sysv4 ;; RM*:SINIX-*:*:*) GUESS=mips-sni-sysv4 ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` GUESS=$UNAME_MACHINE-sni-sysv4 else GUESS=ns32k-sni-sysv fi ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says GUESS=i586-unisys-sysv4 ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm GUESS=hppa1.1-stratus-sysv4 ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. GUESS=i860-stratus-sysv4 ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. GUESS=$UNAME_MACHINE-stratus-vos ;; *:VOS:*:*) # From Paul.Green@stratus.com. GUESS=hppa1.1-stratus-vos ;; mc68*:A/UX:*:*) GUESS=m68k-apple-aux$UNAME_RELEASE ;; news*:NEWS-OS:6*:*) GUESS=mips-sony-newsos6 ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if test -d /usr/nec; then GUESS=mips-nec-sysv$UNAME_RELEASE else GUESS=mips-unknown-sysv$UNAME_RELEASE fi ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. GUESS=powerpc-be-beos ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. GUESS=powerpc-apple-beos ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. GUESS=i586-pc-beos ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. GUESS=i586-pc-haiku ;; x86_64:Haiku:*:*) GUESS=x86_64-unknown-haiku ;; SX-4:SUPER-UX:*:*) GUESS=sx4-nec-superux$UNAME_RELEASE ;; SX-5:SUPER-UX:*:*) GUESS=sx5-nec-superux$UNAME_RELEASE ;; SX-6:SUPER-UX:*:*) GUESS=sx6-nec-superux$UNAME_RELEASE ;; SX-7:SUPER-UX:*:*) GUESS=sx7-nec-superux$UNAME_RELEASE ;; SX-8:SUPER-UX:*:*) GUESS=sx8-nec-superux$UNAME_RELEASE ;; SX-8R:SUPER-UX:*:*) GUESS=sx8r-nec-superux$UNAME_RELEASE ;; SX-ACE:SUPER-UX:*:*) GUESS=sxace-nec-superux$UNAME_RELEASE ;; Power*:Rhapsody:*:*) GUESS=powerpc-apple-rhapsody$UNAME_RELEASE ;; *:Rhapsody:*:*) GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE ;; arm64:Darwin:*:*) GUESS=aarch64-apple-darwin$UNAME_RELEASE ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` case $UNAME_PROCESSOR in unknown) UNAME_PROCESSOR=powerpc ;; esac if command -v xcode-select > /dev/null 2> /dev/null && \ ! xcode-select --print-path > /dev/null 2> /dev/null ; then # Avoid executing cc if there is no toolchain installed as # cc will be a stub that puts up a graphical alert # prompting the user to install developer tools. CC_FOR_BUILD=no_compiler_found else set_cc_for_build fi if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_PPC >/dev/null then UNAME_PROCESSOR=powerpc fi elif test "$UNAME_PROCESSOR" = i386 ; then # uname -m returns i386 or x86_64 UNAME_PROCESSOR=$UNAME_MACHINE fi GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE ;; *:QNX:*:4*) GUESS=i386-pc-qnx ;; NEO-*:NONSTOP_KERNEL:*:*) GUESS=neo-tandem-nsk$UNAME_RELEASE ;; NSE-*:NONSTOP_KERNEL:*:*) GUESS=nse-tandem-nsk$UNAME_RELEASE ;; NSR-*:NONSTOP_KERNEL:*:*) GUESS=nsr-tandem-nsk$UNAME_RELEASE ;; NSV-*:NONSTOP_KERNEL:*:*) GUESS=nsv-tandem-nsk$UNAME_RELEASE ;; NSX-*:NONSTOP_KERNEL:*:*) GUESS=nsx-tandem-nsk$UNAME_RELEASE ;; *:NonStop-UX:*:*) GUESS=mips-compaq-nonstopux ;; BS2000:POSIX*:*:*) GUESS=bs2000-siemens-sysv ;; DS/*:UNIX_System_V:*:*) GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "${cputype-}" = 386; then UNAME_MACHINE=i386 elif test "x${cputype-}" != x; then UNAME_MACHINE=$cputype fi GUESS=$UNAME_MACHINE-unknown-plan9 ;; *:TOPS-10:*:*) GUESS=pdp10-unknown-tops10 ;; *:TENEX:*:*) GUESS=pdp10-unknown-tenex ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) GUESS=pdp10-dec-tops20 ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) GUESS=pdp10-xkl-tops20 ;; *:TOPS-20:*:*) GUESS=pdp10-unknown-tops20 ;; *:ITS:*:*) GUESS=pdp10-unknown-its ;; SEI:*:*:SEIUX) GUESS=mips-sei-seiux$UNAME_RELEASE ;; *:DragonFly:*:*) DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case $UNAME_MACHINE in A*) GUESS=alpha-dec-vms ;; I*) GUESS=ia64-dec-vms ;; V*) GUESS=vax-dec-vms ;; esac ;; *:XENIX:*:SysV) GUESS=i386-pc-xenix ;; i*86:skyos:*:*) SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'` GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL ;; i*86:rdos:*:*) GUESS=$UNAME_MACHINE-pc-rdos ;; i*86:Fiwix:*:*) GUESS=$UNAME_MACHINE-pc-fiwix ;; *:AROS:*:*) GUESS=$UNAME_MACHINE-unknown-aros ;; x86_64:VMkernel:*:*) GUESS=$UNAME_MACHINE-unknown-esx ;; amd64:Isilon\ OneFS:*:*) GUESS=x86_64-unknown-onefs ;; *:Unleashed:*:*) GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE ;; esac # Do we have a guess based on uname results? if test "x$GUESS" != x; then echo "$GUESS" exit fi # No uname command or uname output not recognized. set_cc_for_build cat > "$dummy.c" < #include #endif #if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) #if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) #include #if defined(_SIZE_T_) || defined(SIGLOST) #include #endif #endif #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) #if !defined (ultrix) #include #if defined (BSD) #if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); #else #if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); #else printf ("vax-dec-bsd\n"); exit (0); #endif #endif #else printf ("vax-dec-bsd\n"); exit (0); #endif #else #if defined(_SIZE_T_) || defined(SIGLOST) struct utsname un; uname (&un); printf ("vax-dec-ultrix%s\n", un.release); exit (0); #else printf ("vax-dec-ultrix\n"); exit (0); #endif #endif #endif #if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) #if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) #if defined(_SIZE_T_) || defined(SIGLOST) struct utsname *un; uname (&un); printf ("mips-dec-ultrix%s\n", un.release); exit (0); #else printf ("mips-dec-ultrix\n"); exit (0); #endif #endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } echo "$0: unable to guess system type" >&2 case $UNAME_MACHINE:$UNAME_SYSTEM in mips:Linux | mips64:Linux) # If we got here on MIPS GNU/Linux, output extra information. cat >&2 <&2 <&2 </dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = "$UNAME_MACHINE" UNAME_RELEASE = "$UNAME_RELEASE" UNAME_SYSTEM = "$UNAME_SYSTEM" UNAME_VERSION = "$UNAME_VERSION" EOF fi exit 1 # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: nghttp2-1.68.0/PaxHeaders/NEWS0000644000000000000000000000013215077107270012757 xustar0030 mtime=1761382072.961444287 30 atime=1761382104.851316387 30 ctime=1761382107.835304038 nghttp2-1.68.0/NEWS0000644000175100017510000000000015077107270013335 0ustar00runnerrunnernghttp2-1.68.0/PaxHeaders/nghttpx.conf.sample0000644000000000000000000000013215077107270016103 xustar0030 mtime=1761382072.983444185 30 atime=1761382104.861316343 30 ctime=1761382107.846304006 nghttp2-1.68.0/nghttpx.conf.sample0000644000175100017510000000161715077107270016500 0ustar00runnerrunner# # Sample configuration file for nghttpx. # # * Line staring '#' is treated as comment. # # * The option name in the configuration file is the long command-line # option name with leading '--' stripped (e.g., frontend). Put '=' # between option name and value. Don't put extra leading or trailing # spaces. # # * The options which do not take argument in the command-line *take* # argument in the configuration file. Specify 'yes' as argument # (e.g., http2-proxy=yes). If other string is given, it disables the # option. # # * To specify private key and certificate file, use private-key-file # and certificate-file. See the examples below. # # * conf option cannot be used in the configuration file. It will be # ignored. # # Examples: # # frontend=0.0.0.0,3000 # backend=127.0.0.1,80 # private-key-file=/path/to/server.key # certificate-file=/path/to/server.crt # http2-proxy=no # workers=1 nghttp2-1.68.0/PaxHeaders/m40000644000000000000000000000013215077107333012523 xustar0030 mtime=1761382107.821304078 30 atime=1761382109.795298372 30 ctime=1761382107.821304078 nghttp2-1.68.0/m4/0000755000175100017510000000000015077107333013170 5ustar00runnerrunnernghttp2-1.68.0/m4/PaxHeaders/ax_check_compile_flag.m40000644000000000000000000000013115077107270017307 xustar0029 mtime=1761382072.98244419 30 atime=1761382081.539404925 30 ctime=1761382107.813304102 nghttp2-1.68.0/m4/ax_check_compile_flag.m40000644000175100017510000000640215077107270017702 0ustar00runnerrunner# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) # # DESCRIPTION # # Check whether the given FLAG works with the current language's compiler # or gives an error. (Warnings, however, are ignored) # # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on # success/failure. # # If EXTRA-FLAGS is defined, it is added to the current language's default # flags (e.g. CFLAGS) when the check is done. The check is thus made with # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to # force the compiler to issue an error when a bad flag is given. # # INPUT gives an alternative input source to AC_COMPILE_IFELSE. # # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 4 AC_DEFUN([AX_CHECK_COMPILE_FLAG], [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) AS_VAR_IF(CACHEVAR,yes, [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl ])dnl AX_CHECK_COMPILE_FLAGS nghttp2-1.68.0/m4/PaxHeaders/ltsugar.m40000644000000000000000000000013215077107303014520 xustar0030 mtime=1761382083.104397766 30 atime=1761382083.215397258 30 ctime=1761382107.819304084 nghttp2-1.68.0/m4/ltsugar.m40000755000175100017510000001045315077107303015116 0ustar00runnerrunner# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- # # Copyright (C) 2004-2005, 2007-2008, 2011-2019, 2021-2022 Free Software # Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 6 ltsugar.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) # lt_join(SEP, ARG1, [ARG2...]) # ----------------------------- # Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their # associated separator. # Needed until we can rely on m4_join from Autoconf 2.62, since all earlier # versions in m4sugar had bugs. m4_define([lt_join], [m4_if([$#], [1], [], [$#], [2], [[$2]], [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) m4_define([_lt_join], [m4_if([$#$2], [2], [], [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) # lt_car(LIST) # lt_cdr(LIST) # ------------ # Manipulate m4 lists. # These macros are necessary as long as will still need to support # Autoconf-2.59, which quotes differently. m4_define([lt_car], [[$1]]) m4_define([lt_cdr], [m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], [$#], 1, [], [m4_dquote(m4_shift($@))])]) m4_define([lt_unquote], $1) # lt_append(MACRO-NAME, STRING, [SEPARATOR]) # ------------------------------------------ # Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'. # Note that neither SEPARATOR nor STRING are expanded; they are appended # to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). # No SEPARATOR is output if MACRO-NAME was previously undefined (different # than defined and empty). # # This macro is needed until we can rely on Autoconf 2.62, since earlier # versions of m4sugar mistakenly expanded SEPARATOR but not STRING. m4_define([lt_append], [m4_define([$1], m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) # lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) # ---------------------------------------------------------- # Produce a SEP delimited list of all paired combinations of elements of # PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list # has the form PREFIXmINFIXSUFFIXn. # Needed until we can rely on m4_combine added in Autoconf 2.62. m4_define([lt_combine], [m4_if(m4_eval([$# > 3]), [1], [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl [[m4_foreach([_Lt_prefix], [$2], [m4_foreach([_Lt_suffix], ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) # lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) # ----------------------------------------------------------------------- # Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited # by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. m4_define([lt_if_append_uniq], [m4_ifdef([$1], [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], [lt_append([$1], [$2], [$3])$4], [$5])], [lt_append([$1], [$2], [$3])$4])]) # lt_dict_add(DICT, KEY, VALUE) # ----------------------------- m4_define([lt_dict_add], [m4_define([$1($2)], [$3])]) # lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) # -------------------------------------------- m4_define([lt_dict_add_subkey], [m4_define([$1($2:$3)], [$4])]) # lt_dict_fetch(DICT, KEY, [SUBKEY]) # ---------------------------------- m4_define([lt_dict_fetch], [m4_ifval([$3], m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) # lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) # ----------------------------------------------------------------- m4_define([lt_if_dict_fetch], [m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], [$5], [$6])]) # lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) # -------------------------------------------------------------- m4_define([lt_dict_filter], [m4_if([$5], [], [], [lt_join(m4_quote(m4_default([$4], [[, ]])), lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl ]) nghttp2-1.68.0/m4/PaxHeaders/libtool.m40000644000000000000000000000013215077107303014503 xustar0030 mtime=1761382083.060397967 30 atime=1761382083.216397253 30 ctime=1761382107.816304093 nghttp2-1.68.0/m4/libtool.m40000755000175100017510000113165215077107303015107 0ustar00runnerrunner# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- # # Copyright (C) 1996-2001, 2003-2019, 2021-2022 Free Software # Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. m4_define([_LT_COPYING], [dnl # Copyright (C) 2014 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program or library that is built # using GNU Libtool, you may include this file under the same # distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . ]) # serial 59 LT_INIT # LT_PREREQ(VERSION) # ------------------ # Complain and exit if this libtool version is less that VERSION. m4_defun([LT_PREREQ], [m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, [m4_default([$3], [m4_fatal([Libtool version $1 or higher is required], 63)])], [$2])]) # _LT_CHECK_BUILDDIR # ------------------ # Complain if the absolute build directory name contains unusual characters m4_defun([_LT_CHECK_BUILDDIR], [case `pwd` in *\ * | *\ *) AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; esac ]) # LT_INIT([OPTIONS]) # ------------------ AC_DEFUN([LT_INIT], [AC_PREREQ([2.62])dnl We use AC_PATH_PROGS_FEATURE_CHECK AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl AC_BEFORE([$0], [LT_LANG])dnl AC_BEFORE([$0], [LT_OUTPUT])dnl AC_BEFORE([$0], [LTDL_INIT])dnl m4_require([_LT_CHECK_BUILDDIR])dnl dnl Autoconf doesn't catch unexpanded LT_ macros by default: m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 dnl unless we require an AC_DEFUNed macro: AC_REQUIRE([LTOPTIONS_VERSION])dnl AC_REQUIRE([LTSUGAR_VERSION])dnl AC_REQUIRE([LTVERSION_VERSION])dnl AC_REQUIRE([LTOBSOLETE_VERSION])dnl m4_require([_LT_PROG_LTMAIN])dnl _LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) dnl Parse OPTIONS _LT_SET_OPTIONS([$0], [$1]) # This can be used to rebuild libtool when needed LIBTOOL_DEPS=$ltmain # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' AC_SUBST(LIBTOOL)dnl _LT_SETUP # Only expand once: m4_define([LT_INIT]) ])# LT_INIT # Old names: AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PROG_LIBTOOL], []) dnl AC_DEFUN([AM_PROG_LIBTOOL], []) # _LT_PREPARE_CC_BASENAME # ----------------------- m4_defun([_LT_PREPARE_CC_BASENAME], [ # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in @S|@*""; do case $cc_temp in compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } ])# _LT_PREPARE_CC_BASENAME # _LT_CC_BASENAME(CC) # ------------------- # It would be clearer to call AC_REQUIREs from _LT_PREPARE_CC_BASENAME, # but that macro is also expanded into generated libtool script, which # arranges for $SED and $ECHO to be set by different means. m4_defun([_LT_CC_BASENAME], [m4_require([_LT_PREPARE_CC_BASENAME])dnl AC_REQUIRE([_LT_DECL_SED])dnl AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl func_cc_basename $1 cc_basename=$func_cc_basename_result ]) # _LT_FILEUTILS_DEFAULTS # ---------------------- # It is okay to use these file commands and assume they have been set # sensibly after 'm4_require([_LT_FILEUTILS_DEFAULTS])'. m4_defun([_LT_FILEUTILS_DEFAULTS], [: ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} ])# _LT_FILEUTILS_DEFAULTS # _LT_SETUP # --------- m4_defun([_LT_SETUP], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl _LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl dnl _LT_DECL([], [host_alias], [0], [The host system])dnl _LT_DECL([], [host], [0])dnl _LT_DECL([], [host_os], [0])dnl dnl _LT_DECL([], [build_alias], [0], [The build system])dnl _LT_DECL([], [build], [0])dnl _LT_DECL([], [build_os], [0])dnl dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl dnl AC_REQUIRE([AC_PROG_LN_S])dnl test -z "$LN_S" && LN_S="ln -s" _LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl dnl AC_REQUIRE([LT_CMD_MAX_LEN])dnl _LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl _LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl m4_require([_LT_CMD_RELOAD])dnl m4_require([_LT_DECL_FILECMD])dnl m4_require([_LT_CHECK_MAGIC_METHOD])dnl m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl m4_require([_LT_CMD_OLD_ARCHIVE])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_WITH_SYSROOT])dnl m4_require([_LT_CMD_TRUNCATE])dnl _LT_CONFIG_LIBTOOL_INIT([ # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi ]) if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi _LT_CHECK_OBJDIR m4_require([_LT_TAG_COMPILER])dnl case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a '.a' archive for static linking (except MSVC and # ICC, which need '.lib'). libext=a with_gnu_ld=$lt_cv_prog_gnu_ld old_CC=$CC old_CFLAGS=$CFLAGS # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o _LT_CC_BASENAME([$compiler]) # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then _LT_PATH_MAGIC fi ;; esac # Use C for the default configuration in the libtool script LT_SUPPORTED_TAG([CC]) _LT_LANG_C_CONFIG _LT_LANG_DEFAULT_CONFIG _LT_CONFIG_COMMANDS ])# _LT_SETUP # _LT_PREPARE_SED_QUOTE_VARS # -------------------------- # Define a few sed substitution that help us do robust quoting. m4_defun([_LT_PREPARE_SED_QUOTE_VARS], [# Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\([["`\\]]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ]) # _LT_PROG_LTMAIN # --------------- # Note that this code is called both from 'configure', and 'config.status' # now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, # 'config.status' has no value for ac_aux_dir unless we are using Automake, # so we pass a copy along to make sure it has a sensible value anyway. m4_defun([_LT_PROG_LTMAIN], [m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl _LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) ltmain=$ac_aux_dir/ltmain.sh ])# _LT_PROG_LTMAIN ## ------------------------------------- ## ## Accumulate code for creating libtool. ## ## ------------------------------------- ## # So that we can recreate a full libtool script including additional # tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS # in macros and then make a single call at the end using the 'libtool' # label. # _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) # ---------------------------------------- # Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL_INIT], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_INIT], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_INIT]) # _LT_CONFIG_LIBTOOL([COMMANDS]) # ------------------------------ # Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) # _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) # ----------------------------------------------------- m4_defun([_LT_CONFIG_SAVE_COMMANDS], [_LT_CONFIG_LIBTOOL([$1]) _LT_CONFIG_LIBTOOL_INIT([$2]) ]) # _LT_FORMAT_COMMENT([COMMENT]) # ----------------------------- # Add leading comment marks to the start of each line, and a trailing # full-stop to the whole comment if one is not present already. m4_define([_LT_FORMAT_COMMENT], [m4_ifval([$1], [ m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) )]) ## ------------------------ ## ## FIXME: Eliminate VARNAME ## ## ------------------------ ## # _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) # ------------------------------------------------------------------- # CONFIGNAME is the name given to the value in the libtool script. # VARNAME is the (base) name used in the configure script. # VALUE may be 0, 1 or 2 for a computed quote escaped value based on # VARNAME. Any other value will be used directly. m4_define([_LT_DECL], [lt_if_append_uniq([lt_decl_varnames], [$2], [, ], [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], [m4_ifval([$1], [$1], [$2])]) lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) m4_ifval([$4], [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) lt_dict_add_subkey([lt_decl_dict], [$2], [tagged?], [m4_ifval([$5], [yes], [no])])]) ]) # _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) # -------------------------------------------------------- m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) # lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_tag_varnames], [_lt_decl_filter([tagged?], [yes], $@)]) # _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) # --------------------------------------------------------- m4_define([_lt_decl_filter], [m4_case([$#], [0], [m4_fatal([$0: too few arguments: $#])], [1], [m4_fatal([$0: too few arguments: $#: $1])], [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], [lt_dict_filter([lt_decl_dict], $@)])[]dnl ]) # lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) # -------------------------------------------------- m4_define([lt_decl_quote_varnames], [_lt_decl_filter([value], [1], $@)]) # lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_dquote_varnames], [_lt_decl_filter([value], [2], $@)]) # lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_varnames_tagged], [m4_assert([$# <= 2])dnl _$0(m4_quote(m4_default([$1], [[, ]])), m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) m4_define([_lt_decl_varnames_tagged], [m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) # lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_all_varnames], [_$0(m4_quote(m4_default([$1], [[, ]])), m4_if([$2], [], m4_quote(lt_decl_varnames), m4_quote(m4_shift($@))))[]dnl ]) m4_define([_lt_decl_all_varnames], [lt_join($@, lt_decl_varnames_tagged([$1], lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl ]) # _LT_CONFIG_STATUS_DECLARE([VARNAME]) # ------------------------------------ # Quote a variable value, and forward it to 'config.status' so that its # declaration there will have the same value as in 'configure'. VARNAME # must have a single quote delimited value for this to work. m4_define([_LT_CONFIG_STATUS_DECLARE], [$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) # _LT_CONFIG_STATUS_DECLARATIONS # ------------------------------ # We delimit libtool config variables with single quotes, so when # we write them to config.status, we have to be sure to quote all # embedded single quotes properly. In configure, this macro expands # each variable declared with _LT_DECL (and _LT_TAGDECL) into: # # ='`$ECHO "$" | $SED "$delay_single_quote_subst"`' m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], [m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAGS # ---------------- # Output comment and list of tags supported by the script m4_defun([_LT_LIBTOOL_TAGS], [_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl available_tags='_LT_TAGS'dnl ]) # _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) # ----------------------------------- # Extract the dictionary values for VARNAME (optionally with TAG) and # expand to a commented shell variable setting: # # # Some comment about what VAR is for. # visible_name=$lt_internal_name m4_define([_LT_LIBTOOL_DECLARE], [_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [description])))[]dnl m4_pushdef([_libtool_name], m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), [0], [_libtool_name=[$]$1], [1], [_libtool_name=$lt_[]$1], [2], [_libtool_name=$lt_[]$1], [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl ]) # _LT_LIBTOOL_CONFIG_VARS # ----------------------- # Produce commented declarations of non-tagged libtool config variables # suitable for insertion in the LIBTOOL CONFIG section of the 'libtool' # script. Tagged libtool config variables (even for the LIBTOOL CONFIG # section) are produced by _LT_LIBTOOL_TAG_VARS. m4_defun([_LT_LIBTOOL_CONFIG_VARS], [m4_foreach([_lt_var], m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAG_VARS(TAG) # ------------------------- m4_define([_LT_LIBTOOL_TAG_VARS], [m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) # _LT_TAGVAR(VARNAME, [TAGNAME]) # ------------------------------ m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) # _LT_CONFIG_COMMANDS # ------------------- # Send accumulated output to $CONFIG_STATUS. Thanks to the lists of # variables for single and double quote escaping we saved from calls # to _LT_DECL, we can put quote escaped variables declarations # into 'config.status', and then the shell code to quote escape them in # for loops in 'config.status'. Finally, any additional code accumulated # from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. m4_defun([_LT_CONFIG_COMMANDS], [AC_PROVIDE_IFELSE([LT_OUTPUT], dnl If the libtool generation code has been placed in $CONFIG_LT, dnl instead of duplicating it all over again into config.status, dnl then we will have config.status run $CONFIG_LT later, so it dnl needs to know what name is stored there: [AC_CONFIG_COMMANDS([libtool], [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], dnl If the libtool generation code is destined for config.status, dnl expand the accumulated commands and init code now: [AC_CONFIG_COMMANDS([libtool], [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) ])#_LT_CONFIG_COMMANDS # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], [ # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' _LT_CONFIG_STATUS_DECLARATIONS LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$[]1 _LTECHO_EOF' } # Quote evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_quote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_dquote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done _LT_OUTPUT_LIBTOOL_INIT ]) # _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) # ------------------------------------ # Generate a child script FILE with all initialization necessary to # reuse the environment learned by the parent script, and make the # file executable. If COMMENT is supplied, it is inserted after the # '#!' sequence but before initialization text begins. After this # macro, additional text can be appended to FILE to form the body of # the child script. The macro ends with non-zero status if the # file could not be fully written (such as if the disk is full). m4_ifdef([AS_INIT_GENERATED], [m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], [m4_defun([_LT_GENERATED_FILE_INIT], [m4_require([AS_PREPARE])]dnl [m4_pushdef([AS_MESSAGE_LOG_FD])]dnl [lt_write_fail=0 cat >$1 <<_ASEOF || lt_write_fail=1 #! $SHELL # Generated by $as_me. $2 SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$1 <<\_ASEOF || lt_write_fail=1 AS_SHELL_SANITIZE _AS_PREPARE exec AS_MESSAGE_FD>&1 _ASEOF test 0 = "$lt_write_fail" && chmod +x $1[]dnl m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT # LT_OUTPUT # --------- # This macro allows early generation of the libtool script (before # AC_OUTPUT is called), incase it is used in configure for compilation # tests. AC_DEFUN([LT_OUTPUT], [: ${CONFIG_LT=./config.lt} AC_MSG_NOTICE([creating $CONFIG_LT]) _LT_GENERATED_FILE_INIT(["$CONFIG_LT"], [# Run this file to recreate a libtool stub with the current configuration.]) cat >>"$CONFIG_LT" <<\_LTEOF lt_cl_silent=false exec AS_MESSAGE_LOG_FD>>config.log { echo AS_BOX([Running $as_me.]) } >&AS_MESSAGE_LOG_FD lt_cl_help="\ '$as_me' creates a local libtool stub from the current configuration, for use in further configure time tests before the real libtool is generated. Usage: $[0] [[OPTIONS]] -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files Report bugs to ." lt_cl_version="\ m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) configured by $[0], generated by m4_PACKAGE_STRING. Copyright (C) 2011 Free Software Foundation, Inc. This config.lt script is free software; the Free Software Foundation gives unlimited permision to copy, distribute and modify it." while test 0 != $[#] do case $[1] in --version | --v* | -V ) echo "$lt_cl_version"; exit 0 ;; --help | --h* | -h ) echo "$lt_cl_help"; exit 0 ;; --debug | --d* | -d ) debug=: ;; --quiet | --q* | --silent | --s* | -q ) lt_cl_silent=: ;; -*) AC_MSG_ERROR([unrecognized option: $[1] Try '$[0] --help' for more information.]) ;; *) AC_MSG_ERROR([unrecognized argument: $[1] Try '$[0] --help' for more information.]) ;; esac shift done if $lt_cl_silent; then exec AS_MESSAGE_FD>/dev/null fi _LTEOF cat >>"$CONFIG_LT" <<_LTEOF _LT_OUTPUT_LIBTOOL_COMMANDS_INIT _LTEOF cat >>"$CONFIG_LT" <<\_LTEOF AC_MSG_NOTICE([creating $ofile]) _LT_OUTPUT_LIBTOOL_COMMANDS AS_EXIT(0) _LTEOF chmod +x "$CONFIG_LT" # configure is writing to config.log, but config.lt does its own redirection, # appending to config.log, which fails on DOS, as config.log is still kept # open by configure. Here we exec the FD to /dev/null, effectively closing # config.log, so it can be properly (re)opened and appended to by config.lt. lt_cl_success=: test yes = "$silent" && lt_config_lt_args="$lt_config_lt_args --quiet" exec AS_MESSAGE_LOG_FD>/dev/null $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false exec AS_MESSAGE_LOG_FD>>config.log $lt_cl_success || AS_EXIT(1) ])# LT_OUTPUT # _LT_CONFIG(TAG) # --------------- # If TAG is the built-in tag, create an initial libtool script with a # default configuration from the untagged config vars. Otherwise add code # to config.status for appending the configuration named by TAG from the # matching tagged config vars. m4_defun([_LT_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_CONFIG_SAVE_COMMANDS([ m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl m4_if(_LT_TAG, [C], [ # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi cfgfile=${ofile}T trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # Generated automatically by $as_me ($PACKAGE) $VERSION # NOTE: Changes made to this file will be lost: look at ltmain.sh. # Provide generalized library-building support services. # Written by Gordon Matzigkeit, 1996 _LT_COPYING _LT_LIBTOOL_TAGS # Configured defaults for sys_lib_dlsearch_path munging. : \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} # ### BEGIN LIBTOOL CONFIG _LT_LIBTOOL_CONFIG_VARS _LT_LIBTOOL_TAG_VARS # ### END LIBTOOL CONFIG _LT_EOF cat <<'_LT_EOF' >> "$cfgfile" # ### BEGIN FUNCTIONS SHARED WITH CONFIGURE _LT_PREPARE_MUNGE_PATH_LIST _LT_PREPARE_CC_BASENAME # ### END FUNCTIONS SHARED WITH CONFIGURE _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac _LT_PROG_LTMAIN # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? $SED '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" ], [cat <<_LT_EOF >> "$ofile" dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded dnl in a comment (ie after a #). # ### BEGIN LIBTOOL TAG CONFIG: $1 _LT_LIBTOOL_TAG_VARS(_LT_TAG) # ### END LIBTOOL TAG CONFIG: $1 _LT_EOF ])dnl /m4_if ], [m4_if([$1], [], [ PACKAGE='$PACKAGE' VERSION='$VERSION' RM='$RM' ofile='$ofile'], []) ])dnl /_LT_CONFIG_SAVE_COMMANDS ])# _LT_CONFIG # LT_SUPPORTED_TAG(TAG) # --------------------- # Trace this macro to discover what tags are supported by the libtool # --tag option, using: # autoconf --trace 'LT_SUPPORTED_TAG:$1' AC_DEFUN([LT_SUPPORTED_TAG], []) # C support is built-in for now m4_define([_LT_LANG_C_enabled], []) m4_define([_LT_TAGS], []) # LT_LANG(LANG) # ------------- # Enable libtool support for the given language if not already enabled. AC_DEFUN([LT_LANG], [AC_BEFORE([$0], [LT_OUTPUT])dnl m4_case([$1], [C], [_LT_LANG(C)], [C++], [_LT_LANG(CXX)], [Go], [_LT_LANG(GO)], [Java], [_LT_LANG(GCJ)], [Fortran 77], [_LT_LANG(F77)], [Fortran], [_LT_LANG(FC)], [Windows Resource], [_LT_LANG(RC)], [m4_ifdef([_LT_LANG_]$1[_CONFIG], [_LT_LANG($1)], [m4_fatal([$0: unsupported language: "$1"])])])dnl ])# LT_LANG # _LT_LANG(LANGNAME) # ------------------ m4_defun([_LT_LANG], [m4_ifdef([_LT_LANG_]$1[_enabled], [], [LT_SUPPORTED_TAG([$1])dnl m4_append([_LT_TAGS], [$1 ])dnl m4_define([_LT_LANG_]$1[_enabled], [])dnl _LT_LANG_$1_CONFIG($1)])dnl ])# _LT_LANG m4_ifndef([AC_PROG_GO], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_GO. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_GO], [AC_LANG_PUSH(Go)dnl AC_ARG_VAR([GOC], [Go compiler command])dnl AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl _AC_ARG_VAR_LDFLAGS()dnl AC_CHECK_TOOL(GOC, gccgo) if test -z "$GOC"; then if test -n "$ac_tool_prefix"; then AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) fi fi if test -z "$GOC"; then AC_CHECK_PROG(GOC, gccgo, gccgo, false) fi ])#m4_defun ])#m4_ifndef # _LT_LANG_DEFAULT_CONFIG # ----------------------- m4_defun([_LT_LANG_DEFAULT_CONFIG], [AC_PROVIDE_IFELSE([AC_PROG_CXX], [LT_LANG(CXX)], [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) AC_PROVIDE_IFELSE([AC_PROG_F77], [LT_LANG(F77)], [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) AC_PROVIDE_IFELSE([AC_PROG_FC], [LT_LANG(FC)], [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal dnl pulling things in needlessly. AC_PROVIDE_IFELSE([AC_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([LT_PROG_GCJ], [LT_LANG(GCJ)], [m4_ifdef([AC_PROG_GCJ], [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([A][M_PROG_GCJ], [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([LT_PROG_GCJ], [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) AC_PROVIDE_IFELSE([AC_PROG_GO], [LT_LANG(GO)], [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) AC_PROVIDE_IFELSE([LT_PROG_RC], [LT_LANG(RC)], [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) ])# _LT_LANG_DEFAULT_CONFIG # Obsolete macros: AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_CXX], []) dnl AC_DEFUN([AC_LIBTOOL_F77], []) dnl AC_DEFUN([AC_LIBTOOL_FC], []) dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) dnl AC_DEFUN([AC_LIBTOOL_RC], []) # _LT_TAG_COMPILER # ---------------- m4_defun([_LT_TAG_COMPILER], [AC_REQUIRE([AC_PROG_CC])dnl _LT_DECL([LTCC], [CC], [1], [A C compiler])dnl _LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl _LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl _LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC ])# _LT_TAG_COMPILER # _LT_COMPILER_BOILERPLATE # ------------------------ # Check for compiler boilerplate output or warnings with # the simple compiler test code. m4_defun([_LT_COMPILER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ])# _LT_COMPILER_BOILERPLATE # _LT_LINKER_BOILERPLATE # ---------------------- # Check for linker boilerplate output or warnings with # the simple link test code. m4_defun([_LT_LINKER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ])# _LT_LINKER_BOILERPLATE # _LT_REQUIRED_DARWIN_CHECKS # ------------------------- m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ case $host_os in rhapsody* | darwin*) AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) AC_CHECK_TOOL([LIPO], [lipo], [:]) AC_CHECK_TOOL([OTOOL], [otool], [:]) AC_CHECK_TOOL([OTOOL64], [otool64], [:]) _LT_DECL([], [DSYMUTIL], [1], [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) _LT_DECL([], [NMEDIT], [1], [Tool to change global to local symbols on Mac OS X]) _LT_DECL([], [LIPO], [1], [Tool to manipulate fat objects and archives on Mac OS X]) _LT_DECL([], [OTOOL], [1], [ldd/readelf like tool for Mach-O binaries on Mac OS X]) _LT_DECL([], [OTOOL64], [1], [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], [lt_cv_apple_cc_single_mod=no if test -z "$LT_MULTI_MODULE"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test 0 = "$_lt_result"; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -rf libconftest.dylib* rm -f conftest.* fi]) AC_CACHE_CHECK([for -exported_symbols_list linker flag], [lt_cv_ld_exported_symbols_list], [lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [lt_cv_ld_exported_symbols_list=yes], [lt_cv_ld_exported_symbols_list=no]) LDFLAGS=$save_LDFLAGS ]) AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], [lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD echo "$AR $AR_FLAGS libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD $AR $AR_FLAGS libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then lt_cv_ld_force_load=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM ]) case $host_os in rhapsody* | darwin1.[[012]]) _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; darwin*) case $MACOSX_DEPLOYMENT_TARGET,$host in 10.[[012]],*|,*powerpc*-darwin[[5-8]]*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; *) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test yes = "$lt_cv_apple_cc_single_mod"; then _lt_dar_single_mod='$single_module' fi if test yes = "$lt_cv_ld_exported_symbols_list"; then _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' fi if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac ]) # _LT_DARWIN_LINKER_FEATURES([TAG]) # --------------------------------- # Checks for linker and compiler features on darwin m4_defun([_LT_DARWIN_LINKER_FEATURES], [ m4_require([_LT_REQUIRED_DARWIN_CHECKS]) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported if test yes = "$lt_cv_ld_force_load"; then _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) else _LT_TAGVAR(whole_archive_flag_spec, $1)='' fi _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=$_lt_dar_allow_undefined case $cc_basename in ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" _LT_TAGVAR(archive_expsym_cmds, $1)="$SED 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" _LT_TAGVAR(module_expsym_cmds, $1)="$SED -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" m4_if([$1], [CXX], [ if test yes != "$lt_cv_apple_cc_single_mod"; then _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" _LT_TAGVAR(archive_expsym_cmds, $1)="$SED 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" fi ],[]) else _LT_TAGVAR(ld_shlibs, $1)=no fi ]) # _LT_SYS_MODULE_PATH_AIX([TAGNAME]) # ---------------------------------- # Links a minimal program and checks the executable # for the system default hardcoded library path. In most cases, # this is /usr/lib:/lib, but when the MPI compilers are used # the location of the communication and MPI libs are included too. # If we don't find anything, use the default library path according # to the aix ld manual. # Store the results from the different compilers for each TAGNAME. # Allow to override them for all tags through lt_cv_aix_libpath. m4_defun([_LT_SYS_MODULE_PATH_AIX], [m4_require([_LT_DECL_SED])dnl if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ lt_aix_libpath_sed='[ /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }]' _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi],[]) if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=/usr/lib:/lib fi ]) aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) fi ])# _LT_SYS_MODULE_PATH_AIX # _LT_SHELL_INIT(ARG) # ------------------- m4_define([_LT_SHELL_INIT], [m4_divert_text([M4SH-INIT], [$1 ])])# _LT_SHELL_INIT # _LT_PROG_ECHO_BACKSLASH # ----------------------- # Find how we can fake an echo command that does not interpret backslash. # In particular, with Autoconf 2.60 or later we add some code to the start # of the generated configure script that will find a shell with a builtin # printf (that we can use as an echo command). m4_defun([_LT_PROG_ECHO_BACKSLASH], [ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO AC_MSG_CHECKING([how to print strings]) # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $[]1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } case $ECHO in printf*) AC_MSG_RESULT([printf]) ;; print*) AC_MSG_RESULT([print -r]) ;; *) AC_MSG_RESULT([cat]) ;; esac m4_ifdef([_AS_DETECT_SUGGESTED], [_AS_DETECT_SUGGESTED([ test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test "X`printf %s $ECHO`" = "X$ECHO" \ || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) _LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) _LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) ])# _LT_PROG_ECHO_BACKSLASH # _LT_WITH_SYSROOT # ---------------- AC_DEFUN([_LT_WITH_SYSROOT], [m4_require([_LT_DECL_SED])dnl AC_MSG_CHECKING([for sysroot]) AC_ARG_WITH([sysroot], [AS_HELP_STRING([--with-sysroot@<:@=DIR@:>@], [Search for dependent libraries within DIR (or the compiler's sysroot if not specified).])], [], [with_sysroot=no]) dnl lt_sysroot will always be passed unquoted. We quote it here dnl in case the user passed a directory name. lt_sysroot= case $with_sysroot in #( yes) if test yes = "$GCC"; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | $SED -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) AC_MSG_RESULT([$with_sysroot]) AC_MSG_ERROR([The sysroot must be an absolute path.]) ;; esac AC_MSG_RESULT([${lt_sysroot:-no}]) _LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl [dependent libraries, and where our libraries should be installed.])]) # _LT_ENABLE_LOCK # --------------- m4_defun([_LT_ENABLE_LOCK], [AC_ARG_ENABLE([libtool-lock], [AS_HELP_STRING([--disable-libtool-lock], [avoid locking (might break parallel builds)])]) test no = "$enable_libtool_lock" || enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out what ABI is being produced by ac_compile, and set mode # options accordingly. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `$FILECMD conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE=32 ;; *ELF-64*) HPUX_IA64_MODE=64 ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then if test yes = "$lt_cv_prog_gnu_ld"; then case `$FILECMD conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `$FILECMD conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; mips64*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then emul=elf case `$FILECMD conftest.$ac_objext` in *32-bit*) emul="${emul}32" ;; *64-bit*) emul="${emul}64" ;; esac case `$FILECMD conftest.$ac_objext` in *MSB*) emul="${emul}btsmip" ;; *LSB*) emul="${emul}ltsmip" ;; esac case `$FILECMD conftest.$ac_objext` in *N32*) emul="${emul}n32" ;; esac LD="${LD-ld} -m $emul" fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. Note that the listed cases only cover the # situations where additional linker options are needed (such as when # doing 32-bit compilation for a host where ld defaults to 64-bit, or # vice versa); the common cases where no linker options are needed do # not appear in the list. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `$FILECMD conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) case `$FILECMD conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; *) LD="${LD-ld} -m elf_i386" ;; esac ;; powerpc64le-*linux*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*linux*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -belf" AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, [AC_LANG_PUSH(C) AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) AC_LANG_POP]) if test yes != "$lt_cv_cc_needs_belf"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS=$SAVE_CFLAGS fi ;; *-*solaris*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `$FILECMD conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*|x86_64-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD=${LD-ld}_sol2 fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks=$enable_libtool_lock ])# _LT_ENABLE_LOCK # _LT_PROG_AR # ----------- m4_defun([_LT_PROG_AR], [AC_CHECK_TOOLS(AR, [ar], false) : ${AR=ar} _LT_DECL([], [AR], [1], [The archiver]) # Use ARFLAGS variable as AR's operation code to sync the variable naming with # Automake. If both AR_FLAGS and ARFLAGS are specified, AR_FLAGS should have # higher priority because thats what people were doing historically (setting # ARFLAGS for automake and AR_FLAGS for libtool). FIXME: Make the AR_FLAGS # variable obsoleted/removed. test ${AR_FLAGS+y} || AR_FLAGS=${ARFLAGS-cr} lt_ar_flags=$AR_FLAGS _LT_DECL([], [lt_ar_flags], [0], [Flags to create an archive (by configure)]) # Make AR_FLAGS overridable by 'make ARFLAGS='. Don't try to run-time override # by AR_FLAGS because that was never working and AR_FLAGS is about to die. _LT_DECL([], [AR_FLAGS], [\@S|@{ARFLAGS-"\@S|@lt_ar_flags"}], [Flags to create an archive]) AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], [lt_cv_ar_at_file=no AC_COMPILE_IFELSE([AC_LANG_PROGRAM], [echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' AC_TRY_EVAL([lt_ar_try]) if test 0 -eq "$ac_status"; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a AC_TRY_EVAL([lt_ar_try]) if test 0 -ne "$ac_status"; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a ]) ]) if test no = "$lt_cv_ar_at_file"; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi _LT_DECL([], [archiver_list_spec], [1], [How to feed a file listing to the archiver]) ])# _LT_PROG_AR # _LT_CMD_OLD_ARCHIVE # ------------------- m4_defun([_LT_CMD_OLD_ARCHIVE], [_LT_PROG_AR AC_CHECK_TOOL(STRIP, strip, :) test -z "$STRIP" && STRIP=: _LT_DECL([], [STRIP], [1], [A symbol stripping program]) AC_CHECK_TOOL(RANLIB, ranlib, :) test -z "$RANLIB" && RANLIB=: _LT_DECL([], [RANLIB], [1], [Commands used to install an old-style archive]) # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in bitrig* | openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac _LT_DECL([], [old_postinstall_cmds], [2]) _LT_DECL([], [old_postuninstall_cmds], [2]) _LT_TAGDECL([], [old_archive_cmds], [2], [Commands used to build an old-style archive]) _LT_DECL([], [lock_old_archive_extraction], [0], [Whether to use a lock for old archive extraction]) ])# _LT_CMD_OLD_ARCHIVE # _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------------------- # Check whether the given compiler option works AC_DEFUN([_LT_COMPILER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$3" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi fi $RM conftest* ]) if test yes = "[$]$2"; then m4_if([$5], , :, [$5]) else m4_if([$6], , :, [$6]) fi ])# _LT_COMPILER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) # _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------- # Check whether the given linker option works AC_DEFUN([_LT_LINKER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $3" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&AS_MESSAGE_LOG_FD $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi else $2=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS ]) if test yes = "[$]$2"; then m4_if([$4], , :, [$4]) else m4_if([$5], , :, [$5]) fi ])# _LT_LINKER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) # LT_CMD_MAX_LEN #--------------- AC_DEFUN([LT_CMD_MAX_LEN], [AC_REQUIRE([AC_CANONICAL_HOST])dnl # find the maximum length of command line arguments AC_MSG_CHECKING([the maximum length of command line arguments]) AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl i=0 teststring=ABCD case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; bitrig* | darwin* | dragonfly* | freebsd* | midnightbsd* | netbsd* | openbsd*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | $SED 's/.*[[ ]]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len" && \ test undefined != "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test X`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test 17 != "$i" # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac ]) if test -n "$lt_cv_sys_max_cmd_len"; then AC_MSG_RESULT($lt_cv_sys_max_cmd_len) else AC_MSG_RESULT(none) fi max_cmd_len=$lt_cv_sys_max_cmd_len _LT_DECL([], [max_cmd_len], [0], [What is the maximum length of a command?]) ])# LT_CMD_MAX_LEN # Old name: AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) # _LT_HEADER_DLFCN # ---------------- m4_defun([_LT_HEADER_DLFCN], [AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl ])# _LT_HEADER_DLFCN # _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, # ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) # ---------------------------------------------------------------- m4_defun([_LT_TRY_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test yes = "$cross_compiling"; then : [$4] else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF [#line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; }] _LT_EOF if AC_TRY_EVAL(ac_link) && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) $1 ;; x$lt_dlneed_uscore) $2 ;; x$lt_dlunknown|x*) $3 ;; esac else : # compilation failed $3 fi fi rm -fr conftest* ])# _LT_TRY_DLOPEN_SELF # LT_SYS_DLOPEN_SELF # ------------------ AC_DEFUN([LT_SYS_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test yes != "$enable_dlopen"; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen=load_add_on lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen=LoadLibrary lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen=dlopen lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],[ lt_cv_dlopen=dyld lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ]) ;; tpf*) # Don't try to run any link tests for TPF. We know it's impossible # because TPF is a cross-compiler, and we know how we open DSOs. lt_cv_dlopen=dlopen lt_cv_dlopen_libs= lt_cv_dlopen_self=no ;; *) AC_CHECK_FUNC([shl_load], [lt_cv_dlopen=shl_load], [AC_CHECK_LIB([dld], [shl_load], [lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld], [AC_CHECK_FUNC([dlopen], [lt_cv_dlopen=dlopen], [AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl], [AC_CHECK_LIB([svld], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld], [AC_CHECK_LIB([dld], [dld_link], [lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld]) ]) ]) ]) ]) ]) ;; esac if test no = "$lt_cv_dlopen"; then enable_dlopen=no else enable_dlopen=yes fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS=$CPPFLAGS test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS=$LDFLAGS wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS=$LIBS LIBS="$lt_cv_dlopen_libs $LIBS" AC_CACHE_CHECK([whether a program can dlopen itself], lt_cv_dlopen_self, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) ]) if test yes = "$lt_cv_dlopen_self"; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" AC_CACHE_CHECK([whether a statically linked program can dlopen itself], lt_cv_dlopen_self_static, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) ]) fi CPPFLAGS=$save_CPPFLAGS LDFLAGS=$save_LDFLAGS LIBS=$save_LIBS ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi _LT_DECL([dlopen_support], [enable_dlopen], [0], [Whether dlopen is supported]) _LT_DECL([dlopen_self], [enable_dlopen_self], [0], [Whether dlopen of programs is supported]) _LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], [Whether dlopen of statically linked programs is supported]) ])# LT_SYS_DLOPEN_SELF # Old name: AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) # _LT_COMPILER_C_O([TAGNAME]) # --------------------------- # Check to see if options -c and -o are simultaneously supported by compiler. # This macro does not hard code the compiler like AC_PROG_CC_C_O. m4_defun([_LT_COMPILER_C_O], [m4_require([_LT_DECL_SED])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes fi fi chmod u+w . 2>&AS_MESSAGE_LOG_FD $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* ]) _LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], [Does compiler simultaneously support -c and -o options?]) ])# _LT_COMPILER_C_O # _LT_COMPILER_FILE_LOCKS([TAGNAME]) # ---------------------------------- # Check to see if we can do hard links to lock some files if needed m4_defun([_LT_COMPILER_FILE_LOCKS], [m4_require([_LT_ENABLE_LOCK])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_COMPILER_C_O([$1]) hard_links=nottested if test no = "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user AC_MSG_CHECKING([if we can lock with hard links]) hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no AC_MSG_RESULT([$hard_links]) if test no = "$hard_links"; then AC_MSG_WARN(['$CC' does not support '-c -o', so 'make -j' may be unsafe]) need_locks=warn fi else need_locks=no fi _LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) ])# _LT_COMPILER_FILE_LOCKS # _LT_CHECK_OBJDIR # ---------------- m4_defun([_LT_CHECK_OBJDIR], [AC_CACHE_CHECK([for objdir], [lt_cv_objdir], [rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null]) objdir=$lt_cv_objdir _LT_DECL([], [objdir], [0], [The name of the directory that contains temporary libtool files])dnl m4_pattern_allow([LT_OBJDIR])dnl AC_DEFINE_UNQUOTED([LT_OBJDIR], "$lt_cv_objdir/", [Define to the sub-directory where libtool stores uninstalled libraries.]) ])# _LT_CHECK_OBJDIR # _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) # -------------------------------------- # Check hardcoding attributes. m4_defun([_LT_LINKER_HARDCODE_LIBPATH], [AC_MSG_CHECKING([how to hardcode library paths into programs]) _LT_TAGVAR(hardcode_action, $1)= if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || test -n "$_LT_TAGVAR(runpath_var, $1)" || test yes = "$_LT_TAGVAR(hardcode_automatic, $1)"; then # We can hardcode non-existent directories. if test no != "$_LT_TAGVAR(hardcode_direct, $1)" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" && test no != "$_LT_TAGVAR(hardcode_minus_L, $1)"; then # Linking always hardcodes the temporary library directory. _LT_TAGVAR(hardcode_action, $1)=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. _LT_TAGVAR(hardcode_action, $1)=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. _LT_TAGVAR(hardcode_action, $1)=unsupported fi AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) if test relink = "$_LT_TAGVAR(hardcode_action, $1)" || test yes = "$_LT_TAGVAR(inherit_rpath, $1)"; then # Fast installation is not supported enable_fast_install=no elif test yes = "$shlibpath_overrides_runpath" || test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi _LT_TAGDECL([], [hardcode_action], [0], [How to hardcode a shared library path into an executable]) ])# _LT_LINKER_HARDCODE_LIBPATH # _LT_CMD_STRIPLIB # ---------------- m4_defun([_LT_CMD_STRIPLIB], [m4_require([_LT_DECL_EGREP]) striplib= old_striplib= AC_MSG_CHECKING([whether stripping libraries is possible]) if test -z "$STRIP"; then AC_MSG_RESULT([no]) else if $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then old_striplib="$STRIP --strip-debug" striplib="$STRIP --strip-unneeded" AC_MSG_RESULT([yes]) else case $host_os in darwin*) # FIXME - insert some real tests, host_os isn't really good enough striplib="$STRIP -x" old_striplib="$STRIP -S" AC_MSG_RESULT([yes]) ;; freebsd*) if $STRIP -V 2>&1 | $GREP "elftoolchain" >/dev/null; then old_striplib="$STRIP --strip-debug" striplib="$STRIP --strip-unneeded" AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi ;; *) AC_MSG_RESULT([no]) ;; esac fi fi _LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) _LT_DECL([], [striplib], [1]) ])# _LT_CMD_STRIPLIB # _LT_PREPARE_MUNGE_PATH_LIST # --------------------------- # Make sure func_munge_path_list() is defined correctly. m4_defun([_LT_PREPARE_MUNGE_PATH_LIST], [[# func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x@S|@2 in x) ;; *:) eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'` \@S|@@S|@1\" ;; x:*) eval @S|@1=\"\@S|@@S|@1 `$ECHO @S|@2 | $SED 's/:/ /g'`\" ;; *::*) eval @S|@1=\"\@S|@@S|@1\ `$ECHO @S|@2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval @S|@1=\"`$ECHO @S|@2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \@S|@@S|@1\" ;; *) eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'`\" ;; esac } ]])# _LT_PREPARE_PATH_LIST # _LT_SYS_DYNAMIC_LINKER([TAG]) # ----------------------------- # PORTME Fill in your ld.so characteristics m4_defun([_LT_SYS_DYNAMIC_LINKER], [AC_REQUIRE([AC_CANONICAL_HOST])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_OBJDUMP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_PREPARE_MUNGE_PATH_LIST])dnl AC_MSG_CHECKING([dynamic linker characteristics]) m4_if([$1], [], [ if test yes = "$GCC"; then case $host_os in darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; *) lt_awk_arg='/^libraries:/' ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq='s|=\([[A-Za-z]]:\)|\1|g' ;; *) lt_sed_strip_eq='s|=/|/|g' ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary... lt_tmp_lt_search_path_spec= lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` # ...but if some path component already ends with the multilib dir we assume # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). case "$lt_multi_os_dir; $lt_search_path_spec " in "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) lt_multi_os_dir= ;; esac for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" elif test -n "$lt_multi_os_dir"; then test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS = " "; FS = "/|\n";} { lt_foo = ""; lt_count = 0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo = "/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[[lt_foo]]++; } if (lt_freq[[lt_foo]] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's|/\([[A-Za-z]]:\)|\1|g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi]) library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown AC_ARG_VAR([LT_SYS_LIBRARY_PATH], [User-defined run-time library search path.]) case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='$libname$release$shared_ext$major' ;; aix[[4-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line '#! .'. This would cause the generated library to # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[[01]] | aix4.[[01]].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # Using Import Files as archive members, it is possible to support # filename-based versioning of shared library archives on AIX. While # this would work for both with and without runtime linking, it will # prevent static linking of such archives. So we do filename-based # shared library versioning with .so extension only, which is used # when both runtime linking and shared linking is enabled. # Unfortunately, runtime linking may impact performance, so we do # not want this to be the default eventually. Also, we use the # versioned .so libs for executables only if there is the -brtl # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. # To allow for filename-based versioning support, we need to create # libNAME.so.V as an archive file, containing: # *) an Import File, referring to the versioned filename of the # archive as well as the shared archive member, telling the # bitwidth (32 or 64) of that shared object, and providing the # list of exported symbols of that shared object, eventually # decorated with the 'weak' keyword # *) the shared object with the F_LOADONLY flag set, to really avoid # it being seen by the linker. # At run time we better use the real file rather than another symlink, # but for link time we create the symlink libNAME.so -> libNAME.so.V case $with_aix_soname,$aix_use_runtimelinking in # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. aix,yes) # traditional libtool dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; aix,no) # traditional AIX only dynamic_linker='AIX lib.a[(]lib.so.V[)]' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' ;; svr4,*) # full svr4 only dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)]" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,yes) # both, prefer svr4 dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)], lib.a[(]lib.so.V[)]" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # unpreferred sharedlib libNAME.a needs extra handling postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,no) # both, prefer aix dynamic_linker="AIX lib.a[(]lib.so.V[)], lib.so.V[(]$shared_archive_member_spec.o[)]" library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' ;; esac shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[[45]]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo $libname | $SED -e 's/^lib/cyg/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo $libname | $SED -e 's/^lib/pw/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl* | *,icl*) # Native MSVC or ICC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC and ICC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly* | midnightbsd*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[[23]].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[[01]]* | freebsdelf3.[[01]]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[[3-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test yes = "$lt_cv_prog_gnu_ld"; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; linux*android*) version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext' soname_spec='$libname$release$shared_ext' finish_cmds= shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes dynamic_linker='Android linker' # Don't embed -rpath directories since the linker doesn't support them. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], [lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], [lt_cv_shlibpath_overrides_runpath=yes])]) LDFLAGS=$save_LDFLAGS libdir=$save_libdir ]) shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, # even though it is searched at run-time. Try to do the best guess by # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd* | bitrig*) version_type=sunos sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then need_version=no else need_version=yes fi library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no # OS/2 can only load a DLL with a base name of 8 characters or less. soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; v=$($ECHO $release$versuffix | tr -d .-); n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); $ECHO $n$v`$shared_ext' library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' shlibpath_var=BEGINLIBPATH sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec; then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac AC_MSG_RESULT([$dynamic_linker]) test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi # remember unaugmented sys_lib_dlsearch_path content for libtool script decls... configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec # ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" # to be used as default LT_SYS_LIBRARY_PATH value in generated libtool configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH _LT_DECL([], [variables_saved_for_relink], [1], [Variables whose values should be saved in libtool wrapper scripts and restored at link time]) _LT_DECL([], [need_lib_prefix], [0], [Do we need the "lib" prefix for modules?]) _LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) _LT_DECL([], [version_type], [0], [Library versioning type]) _LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) _LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) _LT_DECL([], [shlibpath_overrides_runpath], [0], [Is shlibpath searched before the hard-coded library search path?]) _LT_DECL([], [libname_spec], [1], [Format of library name prefix]) _LT_DECL([], [library_names_spec], [1], [[List of archive names. First name is the real one, the rest are links. The last name is the one that the linker finds with -lNAME]]) _LT_DECL([], [soname_spec], [1], [[The coded name of the library, if different from the real name]]) _LT_DECL([], [install_override_mode], [1], [Permission mode override for installation of shared libraries]) _LT_DECL([], [postinstall_cmds], [2], [Command to use after installation of a shared archive]) _LT_DECL([], [postuninstall_cmds], [2], [Command to use after uninstallation of a shared archive]) _LT_DECL([], [finish_cmds], [2], [Commands used to finish a libtool library installation in a directory]) _LT_DECL([], [finish_eval], [1], [[As "finish_cmds", except a single script fragment to be evaled but not shown]]) _LT_DECL([], [hardcode_into_libs], [0], [Whether we should hardcode library paths into libraries]) _LT_DECL([], [sys_lib_search_path_spec], [2], [Compile-time system search path for libraries]) _LT_DECL([sys_lib_dlsearch_path_spec], [configure_time_dlsearch_path], [2], [Detected run-time system search path for libraries]) _LT_DECL([], [configure_time_lt_sys_library_path], [2], [Explicit LT_SYS_LIBRARY_PATH set during ./configure time]) ])# _LT_SYS_DYNAMIC_LINKER # _LT_PATH_TOOL_PREFIX(TOOL) # -------------------------- # find a file program that can recognize shared library AC_DEFUN([_LT_PATH_TOOL_PREFIX], [m4_require([_LT_DECL_EGREP])dnl AC_MSG_CHECKING([for $1]) AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, [case $MAGIC_CMD in [[\\/*] | ?:[\\/]*]) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR dnl $ac_dummy forces splitting on constant user-supplied paths. dnl POSIX.2 word splitting is done only on the output of word expansions, dnl not every word. This closes a longstanding sh security hole. ac_dummy="m4_if([$2], , $PATH, [$2])" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$1"; then lt_cv_path_MAGIC_CMD=$ac_dir/"$1" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac]) MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then AC_MSG_RESULT($MAGIC_CMD) else AC_MSG_RESULT(no) fi _LT_DECL([], [MAGIC_CMD], [0], [Used to examine libraries when file_magic_cmd begins with "file"])dnl ])# _LT_PATH_TOOL_PREFIX # Old name: AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) # _LT_PATH_MAGIC # -------------- # find a file program that can recognize a shared library m4_defun([_LT_PATH_MAGIC], [_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) else MAGIC_CMD=: fi fi ])# _LT_PATH_MAGIC # LT_PATH_LD # ---------- # find the pathname to the GNU or non-GNU linker AC_DEFUN([LT_PATH_LD], [AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PROG_ECHO_BACKSLASH])dnl AC_ARG_WITH([gnu-ld], [AS_HELP_STRING([--with-gnu-ld], [assume the C compiler uses GNU ld @<:@default=no@:>@])], [test no = "$withval" || with_gnu_ld=yes], [with_gnu_ld=no])dnl ac_prog=ld if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. AC_MSG_CHECKING([for ld used by $CC]) case $host in *-*-mingw*) # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [[\\/]]* | ?:[[\\/]]*) re_direlt='/[[^/]][[^/]]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test yes = "$with_gnu_ld"; then AC_MSG_CHECKING([for GNU ld]) else AC_MSG_CHECKING([for non-GNU ld]) fi AC_CACHE_VAL(lt_cv_path_LD, [if test -z "$LD"; then lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &1 conftest.i cat conftest.i conftest.i >conftest2.i : ${lt_DD:=$DD} AC_PATH_PROGS_FEATURE_CHECK([lt_DD], [dd], [if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: fi]) rm -f conftest.i conftest2.i conftest.out]) ])# _LT_PATH_DD # _LT_CMD_TRUNCATE # ---------------- # find command to truncate a binary pipe m4_defun([_LT_CMD_TRUNCATE], [m4_require([_LT_PATH_DD]) AC_CACHE_CHECK([how to truncate binary pipes], [lt_cv_truncate_bin], [printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i lt_cv_truncate_bin= if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" fi rm -f conftest.i conftest2.i conftest.out test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"]) _LT_DECL([lt_truncate_bin], [lt_cv_truncate_bin], [1], [Command to truncate a binary pipe]) ])# _LT_CMD_TRUNCATE # _LT_CHECK_MAGIC_METHOD # ---------------------- # how to check for library dependencies # -- PORTME fill in with the dynamic library characteristics m4_defun([_LT_CHECK_MAGIC_METHOD], [m4_require([_LT_DECL_EGREP]) m4_require([_LT_DECL_OBJDUMP]) AC_CACHE_CHECK([how to recognize dependent libraries], lt_cv_deplibs_check_method, [lt_cv_file_magic_cmd='$MAGIC_CMD' lt_cv_file_magic_test_file= lt_cv_deplibs_check_method='unknown' # Need to set the preceding variable on all platforms that support # interlibrary dependencies. # 'none' -- dependencies not supported. # 'unknown' -- same as none, but documents that we really don't know. # 'pass_all' -- all dependencies passed with no checks. # 'test_compile' -- check by making test program. # 'file_magic [[regex]]' -- check by looking for files in library path # that responds to the $file_magic_cmd with a given extended regex. # If you have 'file' or equivalent on your system and you're not sure # whether 'pass_all' will *always* work, you probably want this one. case $host_os in aix[[4-9]]*) lt_cv_deplibs_check_method=pass_all ;; beos*) lt_cv_deplibs_check_method=pass_all ;; bsdi[[45]]*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)' lt_cv_file_magic_cmd='$FILECMD -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; cygwin*) # func_win32_libid is a shell function defined in ltmain.sh lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' ;; mingw* | pw32*) # Base MSYS/MinGW do not provide the 'file' command needed by # func_win32_libid shell function, so use a weaker test based on 'objdump', # unless we find 'file', for example because we are cross-compiling. if ( file / ) >/dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly* | midnightbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=$FILECMD lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=$FILECMD case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[[3-9]]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=$FILECMD lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd* | bitrig*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; os2*) lt_cv_deplibs_check_method=pass_all ;; esac ]) file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown _LT_DECL([], [deplibs_check_method], [1], [Method to check whether dependent libraries are shared objects]) _LT_DECL([], [file_magic_cmd], [1], [Command to use when deplibs_check_method = "file_magic"]) _LT_DECL([], [file_magic_glob], [1], [How to find potential files when deplibs_check_method = "file_magic"]) _LT_DECL([], [want_nocaseglob], [1], [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) ])# _LT_CHECK_MAGIC_METHOD # LT_PATH_NM # ---------- # find the pathname to a BSD- or MS-compatible name lister AC_DEFUN([LT_PATH_NM], [AC_REQUIRE([AC_PROG_CC])dnl AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, [if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM=$NM else lt_nm_to_check=${ac_tool_prefix}nm if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. tmp_nm=$ac_dir/$lt_tmp_nm if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then # Check to see if the nm accepts a BSD-compat flag. # Adding the 'sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty case $build_os in mingw*) lt_bad_file=conftest.nm/nofile ;; *) lt_bad_file=/dev/null ;; esac case `"$tmp_nm" -B $lt_bad_file 2>&1 | $SED '1q'` in *$lt_bad_file* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break 2 ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | $SED '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break 2 ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS=$lt_save_ifs done : ${lt_cv_path_NM=no} fi]) if test no != "$lt_cv_path_NM"; then NM=$lt_cv_path_NM else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) case `$DUMPBIN -symbols -headers /dev/null 2>&1 | $SED '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols -headers" ;; *) DUMPBIN=: ;; esac fi AC_SUBST([DUMPBIN]) if test : != "$DUMPBIN"; then NM=$DUMPBIN fi fi test -z "$NM" && NM=nm AC_SUBST([NM]) _LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], [lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) cat conftest.out >&AS_MESSAGE_LOG_FD if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest*]) ])# LT_PATH_NM # Old names: AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_PROG_NM], []) dnl AC_DEFUN([AC_PROG_NM], []) # _LT_CHECK_SHAREDLIB_FROM_LINKLIB # -------------------------------- # how to determine the name of the shared library # associated with a specific link library. # -- PORTME fill in with the dynamic library characteristics m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], [m4_require([_LT_DECL_EGREP]) m4_require([_LT_DECL_OBJDUMP]) m4_require([_LT_DECL_DLLTOOL]) AC_CACHE_CHECK([how to associate runtime and link libraries], lt_cv_sharedlib_from_linklib_cmd, [lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh; # decide which one to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd=$ECHO ;; esac ]) sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO _LT_DECL([], [sharedlib_from_linklib_cmd], [1], [Command to associate shared and link libraries]) ])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB # _LT_PATH_MANIFEST_TOOL # ---------------------- # locate the manifest tool m4_defun([_LT_PATH_MANIFEST_TOOL], [AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], [lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&AS_MESSAGE_LOG_FD if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest*]) if test yes != "$lt_cv_path_mainfest_tool"; then MANIFEST_TOOL=: fi _LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl ])# _LT_PATH_MANIFEST_TOOL # _LT_DLL_DEF_P([FILE]) # --------------------- # True iff FILE is a Windows DLL '.def' file. # Keep in sync with func_dll_def_p in the libtool script AC_DEFUN([_LT_DLL_DEF_P], [dnl test DEF = "`$SED -n dnl -e '\''s/^[[ ]]*//'\'' dnl Strip leading whitespace -e '\''/^\(;.*\)*$/d'\'' dnl Delete empty lines and comments -e '\''s/^\(EXPORTS\|LIBRARY\)\([[ ]].*\)*$/DEF/p'\'' dnl -e q dnl Only consider the first "real" line $1`" dnl ])# _LT_DLL_DEF_P # LT_LIB_M # -------- # check for math library AC_DEFUN([LT_LIB_M], [AC_REQUIRE([AC_CANONICAL_HOST])dnl LIBM= case $host in *-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) # These system don't have libm, or don't need it ;; *-ncr-sysv4.3*) AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM=-lmw) AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") ;; *) AC_CHECK_LIB(m, cos, LIBM=-lm) ;; esac AC_SUBST([LIBM]) ])# LT_LIB_M # Old name: AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_CHECK_LIBM], []) # _LT_COMPILER_NO_RTTI([TAGNAME]) # ------------------------------- m4_defun([_LT_COMPILER_NO_RTTI], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= if test yes = "$GCC"; then case $cc_basename in nvcc*) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; *) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; esac _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], lt_cv_prog_compiler_rtti_exceptions, [-fno-rtti -fno-exceptions], [], [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) fi _LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], [Compiler flag to turn off builtin functions]) ])# _LT_COMPILER_NO_RTTI # _LT_CMD_GLOBAL_SYMBOLS # ---------------------- m4_defun([_LT_CMD_GLOBAL_SYMBOLS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([LT_PATH_NM])dnl AC_REQUIRE([LT_PATH_LD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_TAG_COMPILER])dnl # Check for command to grab the raw symbol name followed by C symbol from nm. AC_MSG_CHECKING([command to parse $NM output from $compiler object]) AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], [ # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[[BCDEGRST]]' # Regexp to match symbols that can be accessed directly from C. sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[[BCDT]]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[[ABCDGISTW]]' ;; hpux*) if test ia64 = "$host_cpu"; then symcode='[[ABCDEGRST]]' fi ;; irix* | nonstopux*) symcode='[[BCDEGRST]]' ;; osf*) symcode='[[BCDEGQRST]]' ;; solaris*) symcode='[[BDRT]]' ;; sco3.2v5*) symcode='[[DT]]' ;; sysv4.2uw2*) symcode='[[DT]]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[[ABDT]]' ;; sysv4) symcode='[[DFNSTU]]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[[ABCDGIRSTW]]' ;; esac if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Gets list of data symbols to import. lt_cv_sys_global_symbol_to_import="$SED -n -e 's/^I .* \(.*\)$/\1/p'" # Adjust the below global symbol transforms to fixup imported variables. lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" lt_c_name_lib_hook="\ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" else # Disable hooks by default. lt_cv_sys_global_symbol_to_import= lt_cdecl_hook= lt_c_name_hook= lt_c_name_lib_hook= fi # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="$SED -n"\ $lt_cdecl_hook\ " -e 's/^T .* \(.*\)$/extern int \1();/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="$SED -n"\ $lt_c_name_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" # Transform an extracted symbol line into symbol name with lib prefix and # symbol address. lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="$SED -n"\ $lt_c_name_lib_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function, # D for any global variable and I for any imported variable. # Also find C++ and __fastcall symbols from MSVC++ or ICC, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK ['"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ " /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ " /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ " {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ " s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx]" else lt_cv_sys_global_symbol_pipe="$SED -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | $SED '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if AC_TRY_EVAL(ac_compile); then # Now try to grab the symbols. nlist=conftest.nm $ECHO "$as_me:$LINENO: $NM conftest.$ac_objext | $lt_cv_sys_global_symbol_pipe > $nlist" >&AS_MESSAGE_LOG_FD if eval "$NM" conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist 2>&AS_MESSAGE_LOG_FD && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT@&t@_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT@&t@_DLSYM_CONST #else # define LT@&t@_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT@&t@_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[[]] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS=conftstm.$ac_objext CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" if AC_TRY_EVAL(ac_link) && test -s conftest$ac_exeext; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD fi else echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test yes = "$pipe_works"; then break else lt_cv_sys_global_symbol_pipe= fi done ]) if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then AC_MSG_RESULT(failed) else AC_MSG_RESULT(ok) fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then nm_file_list_spec='@' fi _LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], [Take the output of nm and produce a listing of raw symbols and C names]) _LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], [Transform the output of nm in a proper C declaration]) _LT_DECL([global_symbol_to_import], [lt_cv_sys_global_symbol_to_import], [1], [Transform the output of nm into a list of symbols to manually relocate]) _LT_DECL([global_symbol_to_c_name_address], [lt_cv_sys_global_symbol_to_c_name_address], [1], [Transform the output of nm in a C name address pair]) _LT_DECL([global_symbol_to_c_name_address_lib_prefix], [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], [Transform the output of nm in a C name address pair when lib prefix is needed]) _LT_DECL([nm_interface], [lt_cv_nm_interface], [1], [The name lister interface]) _LT_DECL([], [nm_file_list_spec], [1], [Specify filename containing input files for $NM]) ]) # _LT_CMD_GLOBAL_SYMBOLS # _LT_COMPILER_PIC([TAGNAME]) # --------------------------- m4_defun([_LT_COMPILER_PIC], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_wl, $1)= _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)= m4_if([$1], [CXX], [ # C++ specific cases for pic, static, wl, etc. if test yes = "$GXX"; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; *djgpp*) # DJGPP does not support shared libraries at all _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac else case $host_os in aix[[4-9]]*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; chorus*) case $cc_basename in cxch68*) # Green Hills C++ Compiler # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; dgux*) case $cc_basename in ec++*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; ghcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; freebsd* | dragonfly* | midnightbsd*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' if test ia64 != "$host_cpu"; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' fi ;; aCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac ;; *) ;; esac ;; interix*) # This is c89, which is MS Visual C++ (no shared libs) # Anyone wants to do a port? ;; irix5* | irix6* | nonstopux*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' # CC pic flag -KPIC is the default. ;; *) ;; esac ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # KAI C++ Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; ecpc* ) # old Intel C++ for x86_64, which still supported -KPIC. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; icpc* ) # Intel C++, used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgCC* | pgcpp*) # Portland Group C++ compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; cxx*) # Compaq C++ # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL 8.0, 9.0 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | $SED 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; esac ;; esac ;; lynxos*) ;; m88k*) ;; mvs*) case $cc_basename in cxx*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' ;; *) ;; esac ;; netbsd* | netbsdelf*-gnu) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' ;; RCC*) # Rational C++ 2.4.1 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; cxx*) # Digital/Compaq C++ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; *) ;; esac ;; psos*) ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' ;; *) ;; esac ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; lcc*) # Lucid _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; *) ;; esac ;; vxworks*) ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ], [ if test yes = "$GCC"; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' case $cc_basename in nagfor*) # NAG Fortran compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; hpux9* | hpux10* | hpux11*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC (with -KPIC) is the default. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in # old Intel for x86_64, which still supported -KPIC. ecc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # flang / f18. f95 an alias for gfortran or flang on Debian flang* | f18* | f95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # Lahey Fortran 8.1. lf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' ;; nagfor*) # NAG Fortran compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; tcc*) # Fabrice Bellard et al's Tiny C Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; ccc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All Alpha code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | $SED 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='' ;; *Sun\ F* | *Sun*Fortran*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' ;; *Intel*\ [[CF]]*Compiler*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; *Portland\ Group*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; esac ;; newsos6) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All OSF/1 code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; rdos*) _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; solaris*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; *) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; esac ;; sunos4*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; unicos*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; uts4*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ]) case $host_os in # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" ;; esac AC_CACHE_CHECK([for $compiler option to produce PIC], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) _LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) # # Check to make sure the PIC flag actually works. # if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in "" | " "*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; esac], [_LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) fi _LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], [Additional compiler flags for building library objects]) _LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], [How to pass a linker flag through the compiler]) # # Check to make sure the static flag actually works. # wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" _LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), $lt_tmp_static_flag, [], [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) _LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], [Compiler flag to prevent dynamic linking]) ])# _LT_COMPILER_PIC # _LT_LINKER_SHLIBS([TAGNAME]) # ---------------------------- # See if the linker supports building shared libraries. m4_defun([_LT_LINKER_SHLIBS], [AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) m4_if([$1], [CXX], [ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] case $host_os in aix[[4-9]]*) # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi ;; pw32*) _LT_TAGVAR(export_symbols_cmds, $1)=$ltdll_cmds ;; cygwin* | mingw* | cegcc*) case $cc_basename in cl* | icl*) _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] ;; esac ;; linux* | k*bsd*-gnu | gnu*) _LT_TAGVAR(link_all_deplibs, $1)=no ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac ], [ runpath_var= _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_cmds, $1)= _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(old_archive_from_new_cmds, $1)= _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= _LT_TAGVAR(thread_safe_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list _LT_TAGVAR(include_expsyms, $1)= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ' (' and ')$', so one must not match beginning or # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', # as well as any symbol that contains 'd'. _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. dnl Note also adjust exclude_expsyms for C++ above. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ and ICC port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++ or Intel C++ Compiler. if test yes != "$GCC"; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++ or ICC) with_gnu_ld=yes ;; openbsd* | bitrig*) with_gnu_ld=no ;; linux* | k*bsd*-gnu | gnu*) _LT_TAGVAR(link_all_deplibs, $1)=no ;; esac _LT_TAGVAR(ld_shlibs, $1)=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test yes = "$with_gnu_ld"; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test yes = "$lt_use_gnu_ld_interface"; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='$wl' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi supports_anon_versioning=no case `$LD -v | $SED -e 's/([[^)]]\+)\s\+//' 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[[3-9]]*) # On AIX/PPC, the GNU linker is very broken if test ia64 != "$host_cpu"; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$SED "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test linux-dietlibc = "$host_os"; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test no = "$tmp_diet" then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 _LT_TAGVAR(whole_archive_flag_spec, $1)= tmp_sharedflag='--shared' ;; nagfor*) # NAGFOR 5.3 tmp_sharedflag='-Wl,-shared' ;; xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes ;; esac case `$CC -V 2>&1 | $SED 5q` in *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi case $cc_basename in tcc*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='-rdynamic' ;; xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; sunos4*) _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac if test no = "$_LT_TAGVAR(ld_shlibs, $1)"; then runpath_var= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. _LT_TAGVAR(hardcode_minus_L, $1)=yes if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. _LT_TAGVAR(hardcode_direct, $1)=unsupported fi ;; aix[[4-9]]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then aix_use_runtimelinking=yes break fi done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # traditional, no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no ;; esac if test yes = "$GCC"; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi ;; esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag="$shared_flag "'$wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. _LT_TAGVAR(always_export_symbols, $1)=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. _LT_TAGVAR(allow_undefined_flag, $1)='-berok' # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared libraries. _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; bsdi[[45]]*) _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++ or Intel C++ Compiler. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl* | icl*) # Native MSVC or ICC _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC and ICC wrapper _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' # FIXME: Should let the user specify the lib program. _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; dgux*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly* | midnightbsd*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; hpux9*) if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' ;; hpux10*) if test yes,no = "$GCC,$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes fi ;; hpux11*) if test yes,no = "$GCC,$with_gnu_ld"; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) m4_if($1, [], [ # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) _LT_LINKER_OPTION([if $CC understands -b], _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) ;; esac fi if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], [lt_cv_irix_exported_symbol], [save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" AC_LINK_IFELSE( [AC_LANG_SOURCE( [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], [C++], [[int foo (void) { return 0; }]], [Fortran 77], [[ subroutine foo end]], [Fortran], [[ subroutine foo end]])])], [lt_cv_irix_exported_symbol=yes], [lt_cv_irix_exported_symbol=no]) LDFLAGS=$save_LDFLAGS]) if test yes = "$lt_cv_irix_exported_symbol"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' fi _LT_TAGVAR(link_all_deplibs, $1)=no else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes _LT_TAGVAR(link_all_deplibs, $1)=yes ;; linux*) case $cc_basename in tcc*) # Fabrice Bellard et al's Tiny C Compiler _LT_TAGVAR(ld_shlibs, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' ;; esac ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; newsos6) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *nto* | *qnx*) ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' fi else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' ;; osf3*) if test yes = "$GCC"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test yes = "$GCC"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; solaris*) _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' if test yes = "$GCC"; then wlarc='$wl' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' _LT_TAGVAR(archive_cmds, $1)='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='$wl' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. GCC discards it without '$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test yes = "$GCC"; then _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' else _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' fi ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes ;; sunos4*) if test sequent = "$host_vendor"; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4) case $host_vendor in sni) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' _LT_TAGVAR(hardcode_direct, $1)=no ;; motorola) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4.3*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes _LT_TAGVAR(ld_shlibs, $1)=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(ld_shlibs, $1)=no ;; esac if test sni = "$host_vendor"; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Blargedynsym' ;; esac fi fi ]) AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no _LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld _LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl _LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl _LT_DECL([], [extract_expsyms_cmds], [2], [The commands to extract the exported symbol list from a shared archive]) # # Do we need to explicitly link libc? # case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in x|xyes) # Assume -lc should be added _LT_TAGVAR(archive_cmds_need_lc, $1)=yes if test yes,yes = "$GCC,$enable_shared"; then case $_LT_TAGVAR(archive_cmds, $1) in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. AC_CACHE_CHECK([whether -lc should be explicitly linked in], [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), [$RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if AC_TRY_EVAL(ac_compile) 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) _LT_TAGVAR(allow_undefined_flag, $1)= if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) then lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no else lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes fi _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* ]) _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) ;; esac fi ;; esac _LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], [Whether or not to add -lc for building shared libraries]) _LT_TAGDECL([allow_libtool_libs_with_static_runtimes], [enable_shared_with_static_runtimes], [0], [Whether or not to disallow shared libs when runtime libs are static]) _LT_TAGDECL([], [export_dynamic_flag_spec], [1], [Compiler flag to allow reflexive dlopens]) _LT_TAGDECL([], [whole_archive_flag_spec], [1], [Compiler flag to generate shared objects directly from archives]) _LT_TAGDECL([], [compiler_needs_object], [1], [Whether the compiler copes with passing no objects directly]) _LT_TAGDECL([], [old_archive_from_new_cmds], [2], [Create an old-style archive from a shared archive]) _LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], [Create a temporary old-style archive to link instead of a shared archive]) _LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) _LT_TAGDECL([], [archive_expsym_cmds], [2]) _LT_TAGDECL([], [module_cmds], [2], [Commands used to build a loadable module if different from building a shared archive.]) _LT_TAGDECL([], [module_expsym_cmds], [2]) _LT_TAGDECL([], [with_gnu_ld], [1], [Whether we are building with GNU ld or not]) _LT_TAGDECL([], [allow_undefined_flag], [1], [Flag that allows shared libraries with undefined symbols to be built]) _LT_TAGDECL([], [no_undefined_flag], [1], [Flag that enforces no undefined symbols]) _LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], [Flag to hardcode $libdir into a binary during linking. This must work even if $libdir does not exist]) _LT_TAGDECL([], [hardcode_libdir_separator], [1], [Whether we need a single "-rpath" flag with a separated argument]) _LT_TAGDECL([], [hardcode_direct], [0], [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_direct_absolute], [0], [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes DIR into the resulting binary and the resulting library dependency is "absolute", i.e impossible to change by setting $shlibpath_var if the library is relocated]) _LT_TAGDECL([], [hardcode_minus_L], [0], [Set to "yes" if using the -LDIR flag during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_shlibpath_var], [0], [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_automatic], [0], [Set to "yes" if building a shared library automatically hardcodes DIR into the library and all subsequent libraries and executables linked against it]) _LT_TAGDECL([], [inherit_rpath], [0], [Set to yes if linker adds runtime paths of dependent libraries to runtime path list]) _LT_TAGDECL([], [link_all_deplibs], [0], [Whether libtool must link a program against all its dependency libraries]) _LT_TAGDECL([], [always_export_symbols], [0], [Set to "yes" if exported symbols are required]) _LT_TAGDECL([], [export_symbols_cmds], [2], [The commands to list exported symbols]) _LT_TAGDECL([], [exclude_expsyms], [1], [Symbols that should not be listed in the preloaded symbols]) _LT_TAGDECL([], [include_expsyms], [1], [Symbols that must always be exported]) _LT_TAGDECL([], [prelink_cmds], [2], [Commands necessary for linking programs (against libraries) with templates]) _LT_TAGDECL([], [postlink_cmds], [2], [Commands necessary for finishing linking programs]) _LT_TAGDECL([], [file_list_spec], [1], [Specify filename containing input files]) dnl FIXME: Not yet implemented dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], dnl [Compiler flag to generate thread safe objects]) ])# _LT_LINKER_SHLIBS # _LT_LANG_C_CONFIG([TAG]) # ------------------------ # Ensure that the configuration variables for a C compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to 'libtool'. m4_defun([_LT_LANG_C_CONFIG], [m4_require([_LT_DECL_EGREP])dnl lt_save_CC=$CC AC_LANG_PUSH(C) # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' _LT_TAG_COMPILER # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) LT_SYS_DLOPEN_SELF _LT_CMD_STRIPLIB # Report what library types will actually be built AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_CONFIG($1) fi AC_LANG_POP CC=$lt_save_CC ])# _LT_LANG_C_CONFIG # _LT_LANG_CXX_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a C++ compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to 'libtool'. m4_defun([_LT_LANG_CXX_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl if test -n "$CXX" && ( test no != "$CXX" && ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || (test g++ != "$CXX"))); then AC_PROG_CXXCPP else _lt_caught_CXX_error=yes fi AC_LANG_PUSH(C++) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for C++ test sources. ac_ext=cpp # Object file extension for compiled C++ test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_caught_CXX_error"; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_LD=$LD lt_save_GCC=$GCC GCC=$GXX lt_save_with_gnu_ld=$with_gnu_ld lt_save_path_LD=$lt_cv_path_LD if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx else $as_unset lt_cv_prog_gnu_ld fi if test -n "${lt_cv_path_LDCXX+set}"; then lt_cv_path_LD=$lt_cv_path_LDCXX else $as_unset lt_cv_path_LD fi test -z "${LDCXX+set}" || LD=$LDCXX CC=${CXX-"c++"} CFLAGS=$CXXFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately if test yes = "$GXX"; then _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' else _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= fi if test yes = "$GXX"; then # Set up default GNU C++ configuration LT_PATH_LD # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. if test yes = "$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) wlarc='$wl' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else with_gnu_ld=no wlarc= # A generic and very simple default shared library creation # command for GNU C++ for the case where it uses the native # linker, instead of GNU ld. If possible, this setting should # overridden to take advantage of the native linker features on # the platform it is being used on. _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' fi # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' else GXX=no with_gnu_ld=no wlarc= fi # PORTME: fill in a description of your system's C++ link characteristics AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) _LT_TAGVAR(ld_shlibs, $1)=yes case $host_os in aix3*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aix[[4-9]]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do case $ld_flag in *-brtl*) aix_use_runtimelinking=yes break ;; esac done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no ;; esac if test yes = "$GXX"; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag=$shared_flag' $wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. _LT_TAGVAR(always_export_symbols, $1)=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. # The "-G" linker flag allows undefined symbols. _LT_TAGVAR(no_undefined_flag, $1)='-bernotok' # Determine the default libpath from the value encoded in an empty # executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' fi fi ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; chorus*) case $cc_basename in *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; cygwin* | mingw* | pw32* | cegcc*) case $GXX,$cc_basename in ,cl* | no,cl* | ,icl* | no,icl*) # Native MSVC or ICC # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ func_to_tool_file "$lt_outputfile"~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # g++ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' ;; dgux*) case $cc_basename in ec++*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; ghcx*) # Green Hills C++ Compiler # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; freebsd2.*) # C++ shared libraries reported to be fairly broken before # switch to ELF _LT_TAGVAR(ld_shlibs, $1)=no ;; freebsd-elf*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; freebsd* | dragonfly* | midnightbsd*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions _LT_TAGVAR(ld_shlibs, $1)=yes ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; hpux9*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; hpux10*|hpux11*) if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) ;; *) _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' ;; esac fi case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. ;; esac case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$SED "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' fi fi _LT_TAGVAR(link_all_deplibs, $1)=yes ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; icpc* | ecpc* ) # Intel C++ with_gnu_ld=yes # version 8.0 and above of icpc choke on multiply defined symbols # if we add $predep_objects and $postdep_objects, however 7.1 and # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ $RANLIB $oldlib' _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 6 and above use weak symbols _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl--rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' ;; cxx*) # Compaq C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' ;; xl* | mpixl* | bgxl*) # IBM XL 8.0 on PPC, with GNU ld _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi ;; *) case `$CC -V 2>&1 | $SED 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes # Not sure whether something based on # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 # would be better. output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; esac ;; esac ;; lynxos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; m88k*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; mvs*) case $cc_basename in cxx*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' wlarc= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no fi # Workaround some broken pre-1.5 toolchains output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' ;; *nto* | *qnx*) _LT_TAGVAR(ld_shlibs, $1)=yes ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' fi output_verbose_link_cmd=func_echo_all else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Archives containing C++ object files must be created using # the KAI C++ compiler. case $host in osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; esac ;; RCC*) # Rational C++ 2.4.1 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; cxx*) case $host in osf3*) _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' ;; *) _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ echo "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ $RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' ;; esac _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes,no = "$GXX,$with_gnu_ld"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' case $host in osf3*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; psos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; lcc*) # Lucid # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(archive_cmds_need_lc,$1)=yes _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker if test yes,no = "$GXX,$with_gnu_ld"; then _LT_TAGVAR(no_undefined_flag, $1)=' $wl-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' else # g++ 2.7 appears to require '-G' NOT '-shared' on this # platform. _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir' case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' ;; esac fi ;; esac ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ '"$_LT_TAGVAR(old_archive_cmds, $1)" _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ '"$_LT_TAGVAR(reload_cmds, $1)" ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; vxworks*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no _LT_TAGVAR(GCC, $1)=$GXX _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS LDCXX=$LD LD=$lt_save_LD GCC=$lt_save_GCC with_gnu_ld=$lt_save_with_gnu_ld lt_cv_path_LDCXX=$lt_cv_path_LD lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld fi # test yes != "$_lt_caught_CXX_error" AC_LANG_POP ])# _LT_LANG_CXX_CONFIG # _LT_FUNC_STRIPNAME_CNF # ---------------------- # func_stripname_cnf prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). # # This function is identical to the (non-XSI) version of func_stripname, # except this one can be used by m4 code that may be executed by configure, # rather than the libtool script. m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl AC_REQUIRE([_LT_DECL_SED]) AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) func_stripname_cnf () { case @S|@2 in .*) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%\\\\@S|@2\$%%"`;; *) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%@S|@2\$%%"`;; esac } # func_stripname_cnf ])# _LT_FUNC_STRIPNAME_CNF # _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) # --------------------------------- # Figure out "hidden" library dependencies from verbose # compiler output when linking a shared library. # Parse the compiler output and extract the necessary # objects, libraries and library flags. m4_defun([_LT_SYS_HIDDEN_LIBDEPS], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl # Dependencies to place before and after the object being linked: _LT_TAGVAR(predep_objects, $1)= _LT_TAGVAR(postdep_objects, $1)= _LT_TAGVAR(predeps, $1)= _LT_TAGVAR(postdeps, $1)= _LT_TAGVAR(compiler_lib_search_path, $1)= dnl we can't use the lt_simple_compile_test_code here, dnl because it contains code intended for an executable, dnl not a library. It's possible we should let each dnl tag define a new lt_????_link_test_code variable, dnl but it's only used here... m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF int a; void foo (void) { a = 0; } _LT_EOF ], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF class Foo { public: Foo (void) { a = 0; } private: int a; }; _LT_EOF ], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer*4 a a=0 return end _LT_EOF ], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer a a=0 return end _LT_EOF ], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF public class foo { private int a; public void bar (void) { a = 0; } }; _LT_EOF ], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF package foo func foo() { } _LT_EOF ]) _lt_libdeps_save_CFLAGS=$CFLAGS case "$CC $CFLAGS " in #( *\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; *\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; *\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; esac dnl Parse the compiler output and extract the necessary dnl objects, libraries and library flags. if AC_TRY_EVAL(ac_compile); then # Parse the compiler output and extract the necessary # objects, libraries and library flags. # Sentinel used to keep track of whether or not we are before # the conftest object file. pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do case $prev$p in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. if test x-L = "$p" || test x-R = "$p"; then prev=$p continue fi # Expand the sysroot to ease extracting the directories later. if test -z "$prev"; then case $p in -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; esac fi case $p in =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; esac if test no = "$pre_test_object_deps_done"; then case $prev in -L | -R) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then _LT_TAGVAR(compiler_lib_search_path, $1)=$prev$p else _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} $prev$p" fi ;; # The "-l" case would never come before the object being # linked, so don't bother handling this case. esac else if test -z "$_LT_TAGVAR(postdeps, $1)"; then _LT_TAGVAR(postdeps, $1)=$prev$p else _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} $prev$p" fi fi prev= ;; *.lto.$objext) ;; # Ignore GCC LTO objects *.$objext) # This assumes that the test object file only shows up # once in the compiler output. if test "$p" = "conftest.$objext"; then pre_test_object_deps_done=yes continue fi if test no = "$pre_test_object_deps_done"; then if test -z "$_LT_TAGVAR(predep_objects, $1)"; then _LT_TAGVAR(predep_objects, $1)=$p else _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" fi else if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then _LT_TAGVAR(postdep_objects, $1)=$p else _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" fi fi ;; *) ;; # Ignore the rest. esac done # Clean up. rm -f a.out a.exe else echo "libtool.m4: error: problem compiling $1 test program" fi $RM -f confest.$objext CFLAGS=$_lt_libdeps_save_CFLAGS # PORTME: override above test on systems where it is broken m4_if([$1], [CXX], [case $host_os in interix[[3-9]]*) # Interix 3.5 installs completely hosed .la files for C++, so rather than # hack all around it, let's just trust "g++" to DTRT. _LT_TAGVAR(predep_objects,$1)= _LT_TAGVAR(postdep_objects,$1)= _LT_TAGVAR(postdeps,$1)= ;; esac ]) case " $_LT_TAGVAR(postdeps, $1) " in *" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; esac _LT_TAGVAR(compiler_lib_search_dirs, $1)= if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | $SED -e 's! -L! !g' -e 's!^ !!'` fi _LT_TAGDECL([], [compiler_lib_search_dirs], [1], [The directories searched by this compiler when creating a shared library]) _LT_TAGDECL([], [predep_objects], [1], [Dependencies to place before and after the objects being linked to create a shared library]) _LT_TAGDECL([], [postdep_objects], [1]) _LT_TAGDECL([], [predeps], [1]) _LT_TAGDECL([], [postdeps], [1]) _LT_TAGDECL([], [compiler_lib_search_path], [1], [The library search path used internally by the compiler when linking a shared library]) ])# _LT_SYS_HIDDEN_LIBDEPS # _LT_LANG_F77_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a Fortran 77 compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_F77_CONFIG], [AC_LANG_PUSH(Fortran 77) if test -z "$F77" || test no = "$F77"; then _lt_disable_F77=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for f77 test sources. ac_ext=f # Object file extension for compiled f77 test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the F77 compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_disable_F77"; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${F77-"f77"} CFLAGS=$FFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) GCC=$G77 if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)=$G77 _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS fi # test yes != "$_lt_disable_F77" AC_LANG_POP ])# _LT_LANG_F77_CONFIG # _LT_LANG_FC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for a Fortran compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_FC_CONFIG], [AC_LANG_PUSH(Fortran) if test -z "$FC" || test no = "$FC"; then _lt_disable_FC=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for fc test sources. ac_ext=${ac_fc_srcext-f} # Object file extension for compiled fc test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the FC compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_disable_FC"; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${FC-"f95"} CFLAGS=$FCFLAGS compiler=$CC GCC=$ac_cv_fc_compiler_gnu _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)=$ac_cv_fc_compiler_gnu _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS fi # test yes != "$_lt_disable_FC" AC_LANG_POP ])# _LT_LANG_FC_CONFIG # _LT_LANG_GCJ_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Java Compiler compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_GCJ_CONFIG], [AC_REQUIRE([LT_PROG_GCJ])dnl AC_LANG_SAVE # Source file extension for Java test sources. ac_ext=java # Object file extension for compiled Java test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="class foo {}" # Code to be used in simple link tests lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GCJ-"gcj"} CFLAGS=$GCJFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)=$LD _LT_CC_BASENAME([$compiler]) # GCJ did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GCJ_CONFIG # _LT_LANG_GO_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Go compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_GO_CONFIG], [AC_REQUIRE([LT_PROG_GO])dnl AC_LANG_SAVE # Source file extension for Go test sources. ac_ext=go # Object file extension for compiled Go test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="package main; func main() { }" # Code to be used in simple link tests lt_simple_link_test_code='package main; func main() { }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GOC-"gccgo"} CFLAGS=$GOFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)=$LD _LT_CC_BASENAME([$compiler]) # Go did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GO_CONFIG # _LT_LANG_RC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for the Windows resource compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_RC_CONFIG], [AC_REQUIRE([LT_PROG_RC])dnl AC_LANG_SAVE # Source file extension for RC test sources. ac_ext=rc # Object file extension for compiled RC test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' # Code to be used in simple link tests lt_simple_link_test_code=$lt_simple_compile_test_code # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC= CC=${RC-"windres"} CFLAGS= compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes if test -n "$compiler"; then : _LT_CONFIG($1) fi GCC=$lt_save_GCC AC_LANG_RESTORE CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_RC_CONFIG # LT_PROG_GCJ # ----------- AC_DEFUN([LT_PROG_GCJ], [m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], [AC_CHECK_TOOL(GCJ, gcj,) test set = "${GCJFLAGS+set}" || GCJFLAGS="-g -O2" AC_SUBST(GCJFLAGS)])])[]dnl ]) # Old name: AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_GCJ], []) # LT_PROG_GO # ---------- AC_DEFUN([LT_PROG_GO], [AC_CHECK_TOOL(GOC, gccgo,) ]) # LT_PROG_RC # ---------- AC_DEFUN([LT_PROG_RC], [AC_CHECK_TOOL(RC, windres,) ]) # Old name: AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_RC], []) # _LT_DECL_EGREP # -------------- # If we don't have a new enough Autoconf to choose the best grep # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_EGREP], [AC_REQUIRE([AC_PROG_EGREP])dnl AC_REQUIRE([AC_PROG_FGREP])dnl test -z "$GREP" && GREP=grep _LT_DECL([], [GREP], [1], [A grep program that handles long lines]) _LT_DECL([], [EGREP], [1], [An ERE matcher]) _LT_DECL([], [FGREP], [1], [A literal string matcher]) dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too AC_SUBST([GREP]) ]) # _LT_DECL_OBJDUMP # -------------- # If we don't have a new enough Autoconf to choose the best objdump # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_OBJDUMP], [AC_CHECK_TOOL(OBJDUMP, objdump, false) test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) AC_SUBST([OBJDUMP]) ]) # _LT_DECL_DLLTOOL # ---------------- # Ensure DLLTOOL variable is set. m4_defun([_LT_DECL_DLLTOOL], [AC_CHECK_TOOL(DLLTOOL, dlltool, false) test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program]) AC_SUBST([DLLTOOL]) ]) # _LT_DECL_FILECMD # ---------------- # Check for a file(cmd) program that can be used to detect file type and magic m4_defun([_LT_DECL_FILECMD], [AC_CHECK_TOOL([FILECMD], [file], [:]) _LT_DECL([], [FILECMD], [1], [A file(cmd) program that detects file types]) ])# _LD_DECL_FILECMD # _LT_DECL_SED # ------------ # Check for a fully-functional sed program, that truncates # as few characters as possible. Prefer GNU sed if found. m4_defun([_LT_DECL_SED], [AC_PROG_SED test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" _LT_DECL([], [SED], [1], [A sed program that does not truncate output]) _LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], [Sed that helps us avoid accidentally triggering echo(1) options like -n]) ])# _LT_DECL_SED m4_ifndef([AC_PROG_SED], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_SED. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_SED], [AC_MSG_CHECKING([for a sed that does not truncate output]) AC_CACHE_VAL(lt_cv_path_SED, [# Loop through the user's path and test for sed and gsed. # Then use that list of sed's as ones to test for truncation. as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for lt_ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" fi done done done IFS=$as_save_IFS lt_ac_max=0 lt_ac_count=0 # Add /usr/xpg4/bin/sed as it is typically found on Solaris # along with /bin/sed that truncates output. for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do test ! -f "$lt_ac_sed" && continue cat /dev/null > conftest.in lt_ac_count=0 echo $ECHO_N "0123456789$ECHO_C" >conftest.in # Check for GNU sed and select it if it is found. if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then lt_cv_path_SED=$lt_ac_sed break fi while true; do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo >>conftest.nl $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break cmp -s conftest.out conftest.nl || break # 10000 chars as input seems more than enough test 10 -lt "$lt_ac_count" && break lt_ac_count=`expr $lt_ac_count + 1` if test "$lt_ac_count" -gt "$lt_ac_max"; then lt_ac_max=$lt_ac_count lt_cv_path_SED=$lt_ac_sed fi done done ]) SED=$lt_cv_path_SED AC_SUBST([SED]) AC_MSG_RESULT([$SED]) ])#AC_PROG_SED ])#m4_ifndef # Old name: AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_SED], []) # _LT_CHECK_SHELL_FEATURES # ------------------------ # Find out whether the shell is Bourne or XSI compatible, # or has some other useful features. m4_defun([_LT_CHECK_SHELL_FEATURES], [if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi _LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac _LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl _LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl ])# _LT_CHECK_SHELL_FEATURES # _LT_PATH_CONVERSION_FUNCTIONS # ----------------------------- # Determine what file name conversion functions should be used by # func_to_host_file (and, implicitly, by func_to_host_path). These are needed # for certain cross-compile configurations and native mingw. m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_MSG_CHECKING([how to convert $build file names to $host format]) AC_CACHE_VAL(lt_cv_to_host_file_cmd, [case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac ]) to_host_file_cmd=$lt_cv_to_host_file_cmd AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) _LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], [0], [convert $build file names to $host format])dnl AC_MSG_CHECKING([how to convert $build file names to toolchain format]) AC_CACHE_VAL(lt_cv_to_tool_file_cmd, [#assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac ]) to_tool_file_cmd=$lt_cv_to_tool_file_cmd AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) _LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], [0], [convert $build files to toolchain format])dnl ])# _LT_PATH_CONVERSION_FUNCTIONS nghttp2-1.68.0/m4/PaxHeaders/ltoptions.m40000644000000000000000000000013215077107303015072 xustar0030 mtime=1761382083.082397866 30 atime=1761382083.215397258 30 ctime=1761382107.818304087 nghttp2-1.68.0/m4/ltoptions.m40000755000175100017510000003427515077107303015500 0ustar00runnerrunner# Helper functions for option handling. -*- Autoconf -*- # # Copyright (C) 2004-2005, 2007-2009, 2011-2019, 2021-2022 Free # Software Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 8 ltoptions.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) # _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) # ------------------------------------------ m4_define([_LT_MANGLE_OPTION], [[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) # _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) # --------------------------------------- # Set option OPTION-NAME for macro MACRO-NAME, and if there is a # matching handler defined, dispatch to it. Other OPTION-NAMEs are # saved as a flag. m4_define([_LT_SET_OPTION], [m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), _LT_MANGLE_DEFUN([$1], [$2]), [m4_warning([Unknown $1 option '$2'])])[]dnl ]) # _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) # ------------------------------------------------------------ # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. m4_define([_LT_IF_OPTION], [m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) # _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) # ------------------------------------------------------- # Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME # are set. m4_define([_LT_UNLESS_OPTIONS], [m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), [m4_define([$0_found])])])[]dnl m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 ])[]dnl ]) # _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) # ---------------------------------------- # OPTION-LIST is a space-separated list of Libtool options associated # with MACRO-NAME. If any OPTION has a matching handler declared with # LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about # the unknown option and exit. m4_defun([_LT_SET_OPTIONS], [# Set options m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [_LT_SET_OPTION([$1], _LT_Option)]) m4_if([$1],[LT_INIT],[ dnl dnl Simply set some default values (i.e off) if boolean options were not dnl specified: _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no ]) _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no ]) dnl dnl If no reference was made to various pairs of opposing options, then dnl we run the default mode handler for the pair. For example, if neither dnl 'shared' nor 'disable-shared' was passed, we enable building of shared dnl archives by default: _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], [_LT_ENABLE_FAST_INSTALL]) _LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4], [_LT_WITH_AIX_SONAME([aix])]) ]) ])# _LT_SET_OPTIONS ## --------------------------------- ## ## Macros to handle LT_INIT options. ## ## --------------------------------- ## # _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) # ----------------------------------------- m4_define([_LT_MANGLE_DEFUN], [[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) # LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) # ----------------------------------------------- m4_define([LT_OPTION_DEFINE], [m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl ])# LT_OPTION_DEFINE # dlopen # ------ LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes ]) AU_DEFUN([AC_LIBTOOL_DLOPEN], [_LT_SET_OPTION([LT_INIT], [dlopen]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'dlopen' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) # win32-dll # --------- # Declare package support for building win32 dll's. LT_OPTION_DEFINE([LT_INIT], [win32-dll], [enable_win32_dll=yes case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) AC_CHECK_TOOL(AS, as, false) AC_CHECK_TOOL(DLLTOOL, dlltool, false) AC_CHECK_TOOL(OBJDUMP, objdump, false) ;; esac test -z "$AS" && AS=as _LT_DECL([], [AS], [1], [Assembler program])dnl test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl ])# win32-dll AU_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_REQUIRE([AC_CANONICAL_HOST])dnl _LT_SET_OPTION([LT_INIT], [win32-dll]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'win32-dll' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) # _LT_ENABLE_SHARED([DEFAULT]) # ---------------------------- # implement the --enable-shared flag, and supports the 'shared' and # 'disable-shared' LT_INIT options. # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_SHARED], [m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([shared], [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS=$lt_save_ifs ;; esac], [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) _LT_DECL([build_libtool_libs], [enable_shared], [0], [Whether or not to build shared libraries]) ])# _LT_ENABLE_SHARED LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) # Old names: AC_DEFUN([AC_ENABLE_SHARED], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) ]) AC_DEFUN([AC_DISABLE_SHARED], [_LT_SET_OPTION([LT_INIT], [disable-shared]) ]) AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_SHARED], []) dnl AC_DEFUN([AM_DISABLE_SHARED], []) # _LT_ENABLE_STATIC([DEFAULT]) # ---------------------------- # implement the --enable-static flag, and support the 'static' and # 'disable-static' LT_INIT options. # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_STATIC], [m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([static], [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS=$lt_save_ifs ;; esac], [enable_static=]_LT_ENABLE_STATIC_DEFAULT) _LT_DECL([build_old_libs], [enable_static], [0], [Whether or not to build static libraries]) ])# _LT_ENABLE_STATIC LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) # Old names: AC_DEFUN([AC_ENABLE_STATIC], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) ]) AC_DEFUN([AC_DISABLE_STATIC], [_LT_SET_OPTION([LT_INIT], [disable-static]) ]) AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_STATIC], []) dnl AC_DEFUN([AM_DISABLE_STATIC], []) # _LT_ENABLE_FAST_INSTALL([DEFAULT]) # ---------------------------------- # implement the --enable-fast-install flag, and support the 'fast-install' # and 'disable-fast-install' LT_INIT options. # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_FAST_INSTALL], [m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([fast-install], [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS=$lt_save_ifs ;; esac], [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) _LT_DECL([fast_install], [enable_fast_install], [0], [Whether or not to optimize for fast installation])dnl ])# _LT_ENABLE_FAST_INSTALL LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) # Old names: AU_DEFUN([AC_ENABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'fast-install' option into LT_INIT's first parameter.]) ]) AU_DEFUN([AC_DISABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], [disable-fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'disable-fast-install' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) # _LT_WITH_AIX_SONAME([DEFAULT]) # ---------------------------------- # implement the --with-aix-soname flag, and support the `aix-soname=aix' # and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT # is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'. m4_define([_LT_WITH_AIX_SONAME], [m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl shared_archive_member_spec= case $host,$enable_shared in power*-*-aix[[5-9]]*,yes) AC_MSG_CHECKING([which variant of shared library versioning to provide]) AC_ARG_WITH([aix-soname], [AS_HELP_STRING([--with-aix-soname=aix|svr4|both], [shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])], [case $withval in aix|svr4|both) ;; *) AC_MSG_ERROR([Unknown argument to --with-aix-soname]) ;; esac lt_cv_with_aix_soname=$with_aix_soname], [AC_CACHE_VAL([lt_cv_with_aix_soname], [lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT) with_aix_soname=$lt_cv_with_aix_soname]) AC_MSG_RESULT([$with_aix_soname]) if test aix != "$with_aix_soname"; then # For the AIX way of multilib, we name the shared archive member # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, # the AIX toolchain works better with OBJECT_MODE set (default 32). if test 64 = "${OBJECT_MODE-32}"; then shared_archive_member_spec=shr_64 else shared_archive_member_spec=shr fi fi ;; *) with_aix_soname=aix ;; esac _LT_DECL([], [shared_archive_member_spec], [0], [Shared archive member basename, for filename based shared library versioning on AIX])dnl ])# _LT_WITH_AIX_SONAME LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])]) LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])]) LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])]) # _LT_WITH_PIC([MODE]) # -------------------- # implement the --with-pic flag, and support the 'pic-only' and 'no-pic' # LT_INIT options. # MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'. m4_define([_LT_WITH_PIC], [AC_ARG_WITH([pic], [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], [lt_p=${PACKAGE-default} case $withval in yes|no) pic_mode=$withval ;; *) pic_mode=default # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for lt_pkg in $withval; do IFS=$lt_save_ifs if test "X$lt_pkg" = "X$lt_p"; then pic_mode=yes fi done IFS=$lt_save_ifs ;; esac], [pic_mode=m4_default([$1], [default])]) _LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl ])# _LT_WITH_PIC LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) # Old name: AU_DEFUN([AC_LIBTOOL_PICMODE], [_LT_SET_OPTION([LT_INIT], [pic-only]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'pic-only' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) ## ----------------- ## ## LTDL_INIT Options ## ## ----------------- ## m4_define([_LTDL_MODE], []) LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], [m4_define([_LTDL_MODE], [nonrecursive])]) LT_OPTION_DEFINE([LTDL_INIT], [recursive], [m4_define([_LTDL_MODE], [recursive])]) LT_OPTION_DEFINE([LTDL_INIT], [subproject], [m4_define([_LTDL_MODE], [subproject])]) m4_define([_LTDL_TYPE], []) LT_OPTION_DEFINE([LTDL_INIT], [installable], [m4_define([_LTDL_TYPE], [installable])]) LT_OPTION_DEFINE([LTDL_INIT], [convenience], [m4_define([_LTDL_TYPE], [convenience])]) nghttp2-1.68.0/m4/PaxHeaders/ax_cxx_compile_stdcxx.m40000644000000000000000000000013115077107270017440 xustar0029 mtime=1761382072.98244419 30 atime=1761382081.538404929 30 ctime=1761382107.815304096 nghttp2-1.68.0/m4/ax_cxx_compile_stdcxx.m40000644000175100017510000005207315077107270020040 0ustar00runnerrunner# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html # =========================================================================== # # SYNOPSIS # # AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) # # DESCRIPTION # # Check for baseline language coverage in the compiler for the specified # version of the C++ standard. If necessary, add switches to CXX and # CXXCPP to enable support. VERSION may be '11', '14', '17', or '20' for # the respective C++ standard version. # # The second argument, if specified, indicates whether you insist on an # extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. # -std=c++11). If neither is specified, you get whatever works, with # preference for no added switch, and then for an extended mode. # # The third argument, if specified 'mandatory' or if left unspecified, # indicates that baseline support for the specified C++ standard is # required and that the macro should error out if no mode with that # support is found. If specified 'optional', then configuration proceeds # regardless, after defining HAVE_CXX${VERSION} if and only if a # supporting mode is found. # # LICENSE # # Copyright (c) 2008 Benjamin Kosnik # Copyright (c) 2012 Zack Weinberg # Copyright (c) 2013 Roy Stogner # Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov # Copyright (c) 2015 Paul Norman # Copyright (c) 2015 Moritz Klammler # Copyright (c) 2016, 2018 Krzesimir Nowak # Copyright (c) 2019 Enji Cooper # Copyright (c) 2020 Jason Merrill # Copyright (c) 2021 Jörn Heusipp # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 18 dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro dnl (serial version number 13). AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], [$1], [14], [ax_cxx_compile_alternatives="14 1y"], [$1], [17], [ax_cxx_compile_alternatives="17 1z"], [$1], [20], [ax_cxx_compile_alternatives="20"], [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$2], [], [], [$2], [ext], [], [$2], [noext], [], [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], [$3], [optional], [ax_cxx_compile_cxx$1_required=false], [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) AC_LANG_PUSH([C++])dnl ac_success=no m4_if([$2], [], [dnl AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, ax_cv_cxx_compile_cxx$1, [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [ax_cv_cxx_compile_cxx$1=yes], [ax_cv_cxx_compile_cxx$1=no])]) if test x$ax_cv_cxx_compile_cxx$1 = xyes; then ac_success=yes fi]) m4_if([$2], [noext], [], [dnl if test x$ac_success = xno; then for alternative in ${ax_cxx_compile_alternatives}; do switch="-std=gnu++${alternative}" cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, [ac_save_CXX="$CXX" CXX="$CXX $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [eval $cachevar=yes], [eval $cachevar=no]) CXX="$ac_save_CXX"]) if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done fi]) m4_if([$2], [ext], [], [dnl if test x$ac_success = xno; then dnl HP's aCC needs +std=c++11 according to: dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf dnl Cray's crayCC needs "-h std=c++11" dnl MSVC needs -std:c++NN for C++17 and later (default is C++14) for alternative in ${ax_cxx_compile_alternatives}; do for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}" MSVC; do if test x"$switch" = xMSVC; then dnl AS_TR_SH maps both `:` and `=` to `_` so -std:c++17 would collide dnl with -std=c++17. We suffix the cache variable name with _MSVC to dnl avoid this. switch=-std:c++${alternative} cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_${switch}_MSVC]) else cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) fi AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, [ac_save_CXX="$CXX" CXX="$CXX $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [eval $cachevar=yes], [eval $cachevar=no]) CXX="$ac_save_CXX"]) if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done if test x$ac_success = xyes; then break fi done fi]) AC_LANG_POP([C++]) if test x$ax_cxx_compile_cxx$1_required = xtrue; then if test x$ac_success = xno; then AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) fi fi if test x$ac_success = xno; then HAVE_CXX$1=0 AC_MSG_NOTICE([No compiler with C++$1 support was found]) else HAVE_CXX$1=1 AC_DEFINE(HAVE_CXX$1,1, [define if the compiler supports basic C++$1 syntax]) fi AC_SUBST(HAVE_CXX$1) ]) dnl Test body for checking C++11 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 ) dnl Test body for checking C++14 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 ) dnl Test body for checking C++17 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 ) dnl Test body for checking C++20 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_20], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 _AX_CXX_COMPILE_STDCXX_testbody_new_in_20 ) dnl Tests for new features in C++11 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" // MSVC always sets __cplusplus to 199711L in older versions; newer versions // only set it correctly if /Zc:__cplusplus is specified as well as a // /std:c++NN switch: // https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/ #elif __cplusplus < 201103L && !defined _MSC_VER #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual ~Base() {} virtual void f() {} }; struct Derived : public Base { virtual ~Derived() override {} virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = [](){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = [](int i, int j){ return i + j; }(1, 2); auto b = []() -> int { return '0'; }(); auto c = [=](){ return a + b; }(); auto d = [&](){ return c; }(); auto e = [a, &b](int x) mutable { const auto identity = [](int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = [](){ return 0; }; const auto unary = [](int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = [](nullary_t f){ return f(); }; const auto higher2nd = [unary](nullary_t f1){ return [unary, f1](unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L ]]) dnl Tests for new features in C++14 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ // If the compiler admits that it is not ready for C++14, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201402L && !defined _MSC_VER #error "This is not a C++14 compiler" #else namespace cxx14 { namespace test_polymorphic_lambdas { int test() { const auto lambda = [](auto&&... args){ const auto istiny = [](auto x){ return (sizeof(x) == 1UL) ? 1 : 0; }; const int aretiny[] = { istiny(args)... }; return aretiny[0]; }; return lambda(1, 1L, 1.0f, '1'); } } namespace test_binary_literals { constexpr auto ivii = 0b0000000000101010; static_assert(ivii == 42, "wrong value"); } namespace test_generalized_constexpr { template < typename CharT > constexpr unsigned long strlen_c(const CharT *const s) noexcept { auto length = 0UL; for (auto p = s; *p; ++p) ++length; return length; } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("x") == 1UL, ""); static_assert(strlen_c("test") == 4UL, ""); static_assert(strlen_c("another\0test") == 7UL, ""); } namespace test_lambda_init_capture { int test() { auto x = 0; const auto lambda1 = [a = x](int b){ return a + b; }; const auto lambda2 = [a = lambda1(x)](){ return a; }; return lambda2(); } } namespace test_digit_separators { constexpr auto ten_million = 100'000'000; static_assert(ten_million == 100000000, ""); } namespace test_return_type_deduction { auto f(int& x) { return x; } decltype(auto) g(int& x) { return x; } template < typename T1, typename T2 > struct is_same { static constexpr auto value = false; }; template < typename T > struct is_same { static constexpr auto value = true; }; int test() { auto x = 0; static_assert(is_same::value, ""); static_assert(is_same::value, ""); return x; } } } // namespace cxx14 #endif // __cplusplus >= 201402L ]]) dnl Tests for new features in C++17 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ // If the compiler admits that it is not ready for C++17, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201703L && !defined _MSC_VER #error "This is not a C++17 compiler" #else #include #include #include namespace cxx17 { namespace test_constexpr_lambdas { constexpr int foo = [](){return 42;}(); } namespace test::nested_namespace::definitions { } namespace test_fold_expression { template int multiply(Args... args) { return (args * ... * 1); } template bool all(Args... args) { return (args && ...); } } namespace test_extended_static_assert { static_assert (true); } namespace test_auto_brace_init_list { auto foo = {5}; auto bar {5}; static_assert(std::is_same, decltype(foo)>::value); static_assert(std::is_same::value); } namespace test_typename_in_template_template_parameter { template typename X> struct D; } namespace test_fallthrough_nodiscard_maybe_unused_attributes { int f1() { return 42; } [[nodiscard]] int f2() { [[maybe_unused]] auto unused = f1(); switch (f1()) { case 17: f1(); [[fallthrough]]; case 42: f1(); } return f1(); } } namespace test_extended_aggregate_initialization { struct base1 { int b1, b2 = 42; }; struct base2 { base2() { b3 = 42; } int b3; }; struct derived : base1, base2 { int d; }; derived d1 {{1, 2}, {}, 4}; // full initialization derived d2 {{}, {}, 4}; // value-initialized bases } namespace test_general_range_based_for_loop { struct iter { int i; int& operator* () { return i; } const int& operator* () const { return i; } iter& operator++() { ++i; return *this; } }; struct sentinel { int i; }; bool operator== (const iter& i, const sentinel& s) { return i.i == s.i; } bool operator!= (const iter& i, const sentinel& s) { return !(i == s); } struct range { iter begin() const { return {0}; } sentinel end() const { return {5}; } }; void f() { range r {}; for (auto i : r) { [[maybe_unused]] auto v = i; } } } namespace test_lambda_capture_asterisk_this_by_value { struct t { int i; int foo() { return [*this]() { return i; }(); } }; } namespace test_enum_class_construction { enum class byte : unsigned char {}; byte foo {42}; } namespace test_constexpr_if { template int f () { if constexpr(cond) { return 13; } else { return 42; } } } namespace test_selection_statement_with_initializer { int f() { return 13; } int f2() { if (auto i = f(); i > 0) { return 3; } switch (auto i = f(); i + 4) { case 17: return 2; default: return 1; } } } namespace test_template_argument_deduction_for_class_templates { template struct pair { pair (T1 p1, T2 p2) : m1 {p1}, m2 {p2} {} T1 m1; T2 m2; }; void f() { [[maybe_unused]] auto p = pair{13, 42u}; } } namespace test_non_type_auto_template_parameters { template struct B {}; B<5> b1; B<'a'> b2; } namespace test_structured_bindings { int arr[2] = { 1, 2 }; std::pair pr = { 1, 2 }; auto f1() -> int(&)[2] { return arr; } auto f2() -> std::pair& { return pr; } struct S { int x1 : 2; volatile double y1; }; S f3() { return {}; } auto [ x1, y1 ] = f1(); auto& [ xr1, yr1 ] = f1(); auto [ x2, y2 ] = f2(); auto& [ xr2, yr2 ] = f2(); const auto [ x3, y3 ] = f3(); } namespace test_exception_spec_type_system { struct Good {}; struct Bad {}; void g1() noexcept; void g2(); template Bad f(T*, T*); template Good f(T1*, T2*); static_assert (std::is_same_v); } namespace test_inline_variables { template void f(T) {} template inline T g(T) { return T{}; } template<> inline void f<>(int) {} template<> int g<>(int) { return 5; } } } // namespace cxx17 #endif // __cplusplus < 201703L && !defined _MSC_VER ]]) dnl Tests for new features in C++20 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_20], [[ #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 202002L && !defined _MSC_VER #error "This is not a C++20 compiler" #else #include namespace cxx20 { // As C++20 supports feature test macros in the standard, there is no // immediate need to actually test for feature availability on the // Autoconf side. } // namespace cxx20 #endif // __cplusplus < 202002L && !defined _MSC_VER ]]) nghttp2-1.68.0/m4/PaxHeaders/ltversion.m40000644000000000000000000000013215077107303015064 xustar0030 mtime=1761382083.126397665 30 atime=1761382083.215397258 30 ctime=1761382107.820304081 nghttp2-1.68.0/m4/ltversion.m40000755000175100017510000000131215077107303015454 0ustar00runnerrunner# ltversion.m4 -- version numbers -*- Autoconf -*- # # Copyright (C) 2004, 2011-2019, 2021-2022 Free Software Foundation, # Inc. # Written by Scott James Remnant, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # @configure_input@ # serial 4245 ltversion.m4 # This file is part of GNU Libtool m4_define([LT_PACKAGE_VERSION], [2.4.7]) m4_define([LT_PACKAGE_REVISION], [2.4.7]) AC_DEFUN([LTVERSION_VERSION], [macro_version='2.4.7' macro_revision='2.4.7' _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) _LT_DECL(, macro_revision, 0) ]) nghttp2-1.68.0/m4/PaxHeaders/lt~obsolete.m40000644000000000000000000000013215077107303015411 xustar0030 mtime=1761382083.148397564 30 atime=1761382083.214397262 30 ctime=1761382107.821304078 nghttp2-1.68.0/m4/lt~obsolete.m40000755000175100017510000001400715077107303016006 0ustar00runnerrunner# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- # # Copyright (C) 2004-2005, 2007, 2009, 2011-2019, 2021-2022 Free # Software Foundation, Inc. # Written by Scott James Remnant, 2004. # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 5 lt~obsolete.m4 # These exist entirely to fool aclocal when bootstrapping libtool. # # In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN), # which have later been changed to m4_define as they aren't part of the # exported API, or moved to Autoconf or Automake where they belong. # # The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN # in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us # using a macro with the same name in our local m4/libtool.m4 it'll # pull the old libtool.m4 in (it doesn't see our shiny new m4_define # and doesn't know about Autoconf macros at all.) # # So we provide this file, which has a silly filename so it's always # included after everything else. This provides aclocal with the # AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything # because those macros already exist, or will be overwritten later. # We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. # # Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. # Yes, that means every name once taken will need to remain here until # we give up compatibility with versions before 1.7, at which point # we need to keep only those names which we still refer to. # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) nghttp2-1.68.0/PaxHeaders/cmakeconfig.h.in0000644000000000000000000000013215077107270015304 xustar0030 mtime=1761382072.962444282 30 atime=1761382104.868316312 30 ctime=1761382107.852303989 nghttp2-1.68.0/cmakeconfig.h.in0000644000175100017510000000662615077107270015706 0ustar00runnerrunner/* Hint to the compiler that a function never returns */ #define NGHTTP2_NORETURN @HINT_NORETURN@ /* Define to `int' if does not define. */ #cmakedefine ssize_t @ssize_t@ /* Define to 1 if you have the `std::chrono::time_zone`. */ #cmakedefine HAVE_STD_CHRONO_TIME_ZONE 1 /* Define to 1 if you have `libjansson` library. */ #cmakedefine HAVE_JANSSON 1 /* Define to 1 if you have `libxml2` library. */ #cmakedefine HAVE_LIBXML2 1 /* Define to 1 if you have `mruby` library. */ #cmakedefine HAVE_MRUBY 1 /* Define to 1 if you have `neverbleed` library. */ #cmakedefine HAVE_NEVERBLEED 1 /* Define to 1 if you have the `_Exit` function. */ #cmakedefine HAVE__EXIT 1 /* Define to 1 if you have the `accept4` function. */ #cmakedefine HAVE_ACCEPT4 1 /* Define to 1 if you have the `clock_gettime` function. */ #cmakedefine HAVE_CLOCK_GETTIME 1 /* Define to 1 if you have the `mkostemp` function. */ #cmakedefine HAVE_MKOSTEMP 1 /* Define to 1 if you have the `pipe2` function. */ #cmakedefine HAVE_PIPE2 1 /* Define to 1 if you have the `GetTickCount64` function. */ #cmakedefine HAVE_GETTICKCOUNT64 1 /* Define to 1 if you have the `initgroups` function. */ #cmakedefine01 HAVE_DECL_INITGROUPS /* Define to 1 if you have the `CLOCK_MONOTONIC` defined. */ #cmakedefine01 HAVE_DECL_CLOCK_MONOTONIC /* Define to 1 to enable debug output. */ #cmakedefine DEBUGBUILD 1 /* Define to 1 if you want to disable threads. */ #cmakedefine NOTHREADS 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ARPA_INET_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_FCNTL_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_INTTYPES_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LIMITS_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NETDB_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NETINET_IN_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NETINET_IP_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_PWD_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_SOCKET_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_TIME_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYSLOG_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UNISTD_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_WINDOWS_H 1 /* Define to 1 if HTTP/3 is enabled. */ #cmakedefine ENABLE_HTTP3 1 /* Define to 1 if you have `libbpf` library. */ #cmakedefine HAVE_LIBBPF 1 /* Define to 1 if you have enum bpf_stats_type in linux/bpf.h. */ #cmakedefine HAVE_BPF_STATS_TYPE 1 /* Define to 1 if you have `libngtcp2_crypto_quictls` library. */ #cmakedefine HAVE_LIBNGTCP2_CRYPTO_QUICTLS /* Define to 1 if you have `libngtcp2_crypto_libressl` library. */ #cmakedefine HAVE_LIBNGTCP2_CRYPTO_LIBRESSL /* Define to 1 if you have `libngtcp2_crypto_wolfssl` library. */ #cmakedefine HAVE_LIBNGTCP2_CRYPTO_WOLFSSL 1 /* Define to 1 if you have `libev` library. */ #cmakedefine HAVE_LIBEV 1 /* Define to 1 if you have `libbrotlienc` and `libbrotlidec` libraries. */ #cmakedefine HAVE_LIBBROTLI 1 /* Define to 1 if you have `wolfssl` library. */ #cmakedefine HAVE_WOLFSSL 1 nghttp2-1.68.0/PaxHeaders/config.h.in0000644000000000000000000000013215077107304014301 xustar0030 mtime=1761382084.928389902 30 atime=1761382104.165319409 30 ctime=1761382107.828304058 nghttp2-1.68.0/config.h.in0000644000175100017510000003326715077107304014704 0ustar00runnerrunner/* config.h.in. Generated from configure.ac by autoheader. */ /* Define if building universal (internal helper macro) */ #undef AC_APPLE_UNIVERSAL_BUILD /* Define to 1 to enable debug output. */ #undef DEBUGBUILD /* Define to 1 if HTTP/3 is enabled. */ #undef ENABLE_HTTP3 /* Define to 1 if you have the `accept4' function. */ #undef HAVE_ACCEPT4 /* Define to 1 if you have the header file. */ #undef HAVE_ARPA_INET_H /* Define to 1 if you have the std::atomic> is supported. */ #undef HAVE_ATOMIC_STD_SHARED_PTR /* Define to 1 if you have enum bpf_stats_type in linux/bpf.h. */ #undef HAVE_BPF_STATS_TYPE /* Define to 1 if your system has a working `chown' function. */ #undef HAVE_CHOWN /* Define to 1 if you have the `clock_gettime' function. */ #undef HAVE_CLOCK_GETTIME /* define if the compiler supports basic C++20 syntax */ #undef HAVE_CXX20 /* Define to 1 if you have the declaration of `CLOCK_MONOTONIC', and to 0 if you don't. */ #undef HAVE_DECL_CLOCK_MONOTONIC /* Define to 1 if you have the declaration of `initgroups', and to 0 if you don't. */ #undef HAVE_DECL_INITGROUPS /* Define to 1 if you have the declaration of `LIBRESSL_VERSION_NUMBER', and to 0 if you don't. */ #undef HAVE_DECL_LIBRESSL_VERSION_NUMBER /* Define to 1 if you have the declaration of `strerror_r', and to 0 if you don't. */ #undef HAVE_DECL_STRERROR_R /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H /* Define to 1 if you have the `dup2' function. */ #undef HAVE_DUP2 /* Define to 1 if you have the header file. */ #undef HAVE_FCNTL_H /* Define to 1 if you have the `fork' function. */ #undef HAVE_FORK /* Define to 1 if you have the `getcwd' function. */ #undef HAVE_GETCWD /* Define to 1 if you have the `getpwnam' function. */ #undef HAVE_GETPWNAM /* Define to 1 if you have `GetTickCount64` function. */ #undef HAVE_GETTICKCOUNT64 /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have `libjansson` library. */ #undef HAVE_JANSSON /* Define to 1 if you have `libbpf` library. */ #undef HAVE_LIBBPF /* Define to 1 if you have `libbrotlienc` and `libbrotlidec` libraries. */ #undef HAVE_LIBBROTLI /* Define to 1 if you have `libev` library. */ #undef HAVE_LIBEV /* Define to 1 if you have `libngtcp2_crypto_boringssl` library. */ #undef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL /* Define to 1 if you have `libngtcp2_crypto_libressl` library. */ #undef HAVE_LIBNGTCP2_CRYPTO_LIBRESSL /* Define to 1 if you have `libngtcp2_crypto_ossl` library. */ #undef HAVE_LIBNGTCP2_CRYPTO_OSSL /* Define to 1 if you have `libngtcp2_crypto_quictls` library. */ #undef HAVE_LIBNGTCP2_CRYPTO_QUICTLS /* Define to 1 if you have `libngtcp2_crypto_wolfssl` library. */ #undef HAVE_LIBNGTCP2_CRYPTO_WOLFSSL /* Define to 1 if you have `libsystemd` library. */ #undef HAVE_LIBSYSTEMD /* Define to 1 if you have `libxml2` library. */ #undef HAVE_LIBXML2 /* Define to 1 if you have the header file. */ #undef HAVE_LIMITS_H /* Define to 1 if you have the `localtime_r' function. */ #undef HAVE_LOCALTIME_R /* Define to 1 if you have the `memchr' function. */ #undef HAVE_MEMCHR /* Define to 1 if you have the `memmove' function. */ #undef HAVE_MEMMOVE /* Define to 1 if you have the `memset' function. */ #undef HAVE_MEMSET /* Define to 1 if you have the header file. */ #undef HAVE_MINIX_CONFIG_H /* Define to 1 if you have the `mkostemp' function. */ #undef HAVE_MKOSTEMP /* Define to 1 if you have `mruby` library. */ #undef HAVE_MRUBY /* Define to 1 if you have the header file. */ #undef HAVE_NETDB_H /* Define to 1 if you have the header file. */ #undef HAVE_NETINET_IN_H /* Define to 1 if you have the header file. */ #undef HAVE_NETINET_IP_H /* Define to 1 if you have `neverbleed` library. */ #undef HAVE_NEVERBLEED /* Define to 1 if you have the `pipe2' function. */ #undef HAVE_PIPE2 /* Define to 1 if the system has the type `ptrdiff_t'. */ #undef HAVE_PTRDIFF_T /* Define to 1 if you have the header file. */ #undef HAVE_PWD_H /* Define to 1 if struct sockaddr_in6 has sin6_len member. */ #undef HAVE_SOCKADDR_IN6_SIN6_LEN /* Define to 1 if struct sockaddr_in has sin_len member. */ #undef HAVE_SOCKADDR_IN_SIN_LEN /* Define to 1 if you have the `socket' function. */ #undef HAVE_SOCKET /* Define to 1 if you have the `sqrt' function. */ #undef HAVE_SQRT /* Define to 1 if you have the header file. */ #undef HAVE_STDDEF_H /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDIO_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the `std::chrono::time_zone`. */ #undef HAVE_STD_CHRONO_TIME_ZONE /* Define to 1 if you have the `std::future`. */ #undef HAVE_STD_FUTURE /* Define to 1 if you have the `strchr' function. */ #undef HAVE_STRCHR /* Define to 1 if you have the `strdup' function. */ #undef HAVE_STRDUP /* Define to 1 if you have the `strerror' function. */ #undef HAVE_STRERROR /* Define if you have `strerror_r'. */ #undef HAVE_STRERROR_R /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the `strndup' function. */ #undef HAVE_STRNDUP /* Define to 1 if you have the `strstr' function. */ #undef HAVE_STRSTR /* Define to 1 if you have the `strtol' function. */ #undef HAVE_STRTOL /* Define to 1 if you have the `strtoul' function. */ #undef HAVE_STRTOUL /* Define to 1 if you have `struct tm.tm_gmtoff` member. */ #undef HAVE_STRUCT_TM_TM_GMTOFF /* Define to 1 if you have the header file. */ #undef HAVE_SYSLOG_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SOCKET_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TIME_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the `timegm' function. */ #undef HAVE_TIMEGM /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the `vfork' function. */ #undef HAVE_VFORK /* Define to 1 if you have the header file. */ #undef HAVE_VFORK_H /* Define to 1 if you have the header file. */ #undef HAVE_WCHAR_H /* Define to 1 if you have the header file. */ #undef HAVE_WINDOWS_H /* Define to 1 if you have 'wolfssl' library. */ #undef HAVE_WOLFSSL /* Define to 1 if `fork' works. */ #undef HAVE_WORKING_FORK /* Define to 1 if `vfork' works. */ #undef HAVE_WORKING_VFORK /* Define to 1 if you have the `_Exit' function. */ #undef HAVE__EXIT /* Define to the sub-directory where libtool stores uninstalled libraries. */ #undef LT_OBJDIR /* Define to 1 if assertions should be disabled. */ #undef NDEBUG /* Hint to the compiler that a function never return */ #undef NGHTTP2_NORETURN /* Define to 1 if you want to disable threads. */ #undef NOTHREADS /* Name of package */ #undef PACKAGE /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to 1 if all of the C90 standard headers exist (not just the ones required in a freestanding environment). This macro is provided for backward compatibility; new code need not use it. */ #undef STDC_HEADERS /* Define to 1 if strerror_r returns char *. */ #undef STRERROR_R_CHAR_P /* Enable extensions on AIX 3, Interix. */ #ifndef _ALL_SOURCE # undef _ALL_SOURCE #endif /* Enable general extensions on macOS. */ #ifndef _DARWIN_C_SOURCE # undef _DARWIN_C_SOURCE #endif /* Enable general extensions on Solaris. */ #ifndef __EXTENSIONS__ # undef __EXTENSIONS__ #endif /* Enable GNU extensions on systems that have them. */ #ifndef _GNU_SOURCE # undef _GNU_SOURCE #endif /* Enable X/Open compliant socket functions that do not require linking with -lxnet on HP-UX 11.11. */ #ifndef _HPUX_ALT_XOPEN_SOCKET_API # undef _HPUX_ALT_XOPEN_SOCKET_API #endif /* Identify the host operating system as Minix. This macro does not affect the system headers' behavior. A future release of Autoconf may stop defining this macro. */ #ifndef _MINIX # undef _MINIX #endif /* Enable general extensions on NetBSD. Enable NetBSD compatibility extensions on Minix. */ #ifndef _NETBSD_SOURCE # undef _NETBSD_SOURCE #endif /* Enable OpenBSD compatibility extensions on NetBSD. Oddly enough, this does nothing on OpenBSD. */ #ifndef _OPENBSD_SOURCE # undef _OPENBSD_SOURCE #endif /* Define to 1 if needed for POSIX-compatible behavior. */ #ifndef _POSIX_SOURCE # undef _POSIX_SOURCE #endif /* Define to 2 if needed for POSIX-compatible behavior. */ #ifndef _POSIX_1_SOURCE # undef _POSIX_1_SOURCE #endif /* Enable POSIX-compatible threading on Solaris. */ #ifndef _POSIX_PTHREAD_SEMANTICS # undef _POSIX_PTHREAD_SEMANTICS #endif /* Enable extensions specified by ISO/IEC TS 18661-5:2014. */ #ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__ # undef __STDC_WANT_IEC_60559_ATTRIBS_EXT__ #endif /* Enable extensions specified by ISO/IEC TS 18661-1:2014. */ #ifndef __STDC_WANT_IEC_60559_BFP_EXT__ # undef __STDC_WANT_IEC_60559_BFP_EXT__ #endif /* Enable extensions specified by ISO/IEC TS 18661-2:2015. */ #ifndef __STDC_WANT_IEC_60559_DFP_EXT__ # undef __STDC_WANT_IEC_60559_DFP_EXT__ #endif /* Enable extensions specified by ISO/IEC TS 18661-4:2015. */ #ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__ # undef __STDC_WANT_IEC_60559_FUNCS_EXT__ #endif /* Enable extensions specified by ISO/IEC TS 18661-3:2015. */ #ifndef __STDC_WANT_IEC_60559_TYPES_EXT__ # undef __STDC_WANT_IEC_60559_TYPES_EXT__ #endif /* Enable extensions specified by ISO/IEC TR 24731-2:2010. */ #ifndef __STDC_WANT_LIB_EXT2__ # undef __STDC_WANT_LIB_EXT2__ #endif /* Enable extensions specified by ISO/IEC 24747:2009. */ #ifndef __STDC_WANT_MATH_SPEC_FUNCS__ # undef __STDC_WANT_MATH_SPEC_FUNCS__ #endif /* Enable extensions on HP NonStop. */ #ifndef _TANDEM_SOURCE # undef _TANDEM_SOURCE #endif /* Enable X/Open extensions. Define to 500 only if necessary to make mbstate_t available. */ #ifndef _XOPEN_SOURCE # undef _XOPEN_SOURCE #endif /* Version number of package */ #undef VERSION /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD # if defined __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # endif #else # ifndef WORDS_BIGENDIAN # undef WORDS_BIGENDIAN # endif #endif /* Number of bits in a file offset, on hosts where this is settable. */ #undef _FILE_OFFSET_BITS /* Define for large files, on AIX-style hosts. */ #undef _LARGE_FILES /* Define for Solaris 2.5.1 so the uint32_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #undef _UINT32_T /* Define for Solaris 2.5.1 so the uint64_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #undef _UINT64_T /* Define for Solaris 2.5.1 so the uint8_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #undef _UINT8_T /* Define to `int' if doesn't define. */ #undef gid_t /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #undef inline #endif /* Define to the type of a signed integer type of width exactly 16 bits if such a type exists and the standard includes do not define it. */ #undef int16_t /* Define to the type of a signed integer type of width exactly 32 bits if such a type exists and the standard includes do not define it. */ #undef int32_t /* Define to the type of a signed integer type of width exactly 64 bits if such a type exists and the standard includes do not define it. */ #undef int64_t /* Define to the type of a signed integer type of width exactly 8 bits if such a type exists and the standard includes do not define it. */ #undef int8_t /* Define to `long int' if does not define. */ #undef off_t /* Define as a signed integer type capable of holding a process identifier. */ #undef pid_t /* Define to `unsigned int' if does not define. */ #undef size_t /* Define to `int' if does not define. */ #undef ssize_t /* Define to `int' if doesn't define. */ #undef uid_t /* Define to the type of an unsigned integer type of width exactly 16 bits if such a type exists and the standard includes do not define it. */ #undef uint16_t /* Define to the type of an unsigned integer type of width exactly 32 bits if such a type exists and the standard includes do not define it. */ #undef uint32_t /* Define to the type of an unsigned integer type of width exactly 64 bits if such a type exists and the standard includes do not define it. */ #undef uint64_t /* Define to the type of an unsigned integer type of width exactly 8 bits if such a type exists and the standard includes do not define it. */ #undef uint8_t /* Define as `fork' if `vfork' does not work. */ #undef vfork nghttp2-1.68.0/PaxHeaders/missing0000644000000000000000000000013215077107305013653 xustar0030 mtime=1761382085.340388138 30 atime=1761382094.935346461 30 ctime=1761382107.845304009 nghttp2-1.68.0/missing0000755000175100017510000001533615077107305014256 0ustar00runnerrunner#! /bin/sh # Common wrapper for a few potentially missing GNU programs. scriptversion=2018-03-07.03; # UTC # Copyright (C) 1996-2021 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try '$0 --help' for more information" exit 1 fi case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autoheader autom4te automake makeinfo bison yacc flex lex help2man Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=https://www.perl.org/ flex_URL=https://github.com/westes/flex gnu_software_URL=https://www.gnu.org/software program_details () { case $1 in aclocal|automake) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" case $normalized_program in autoconf*) echo "You should only need it if you modified 'configure.ac'," echo "or m4 files included by it." program_details 'autoconf' ;; autoheader*) echo "You should only need it if you modified 'acconfig.h' or" echo "$configure_deps." program_details 'autoheader' ;; automake*) echo "You should only need it if you modified 'Makefile.am' or" echo "$configure_deps." program_details 'automake' ;; aclocal*) echo "You should only need it if you modified 'acinclude.m4' or" echo "$configure_deps." program_details 'aclocal' ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." program_details 'autom4te' ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: nghttp2-1.68.0/PaxHeaders/Dockerfile.android0000644000000000000000000000013215077107270015671 xustar0030 mtime=1761382072.961444287 30 atime=1761382104.866316321 30 ctime=1761382107.851303992 nghttp2-1.68.0/Dockerfile.android0000644000175100017510000000767215077107270016275 0ustar00runnerrunner# vim: ft=dockerfile: # Dockerfile to build nghttp2 android binary # # $ sudo docker build -t nghttp2-android - < Dockerfile.android # # After successful build, android binaries are located under # /root/build/nghttp2. You can copy the binary using docker cp. For # example, to copy nghttpx binary to host file system location # /path/to/dest, do this: # # $ sudo docker run -v /path/to/dest:/out nghttp2-android cp /root/build/nghttp2/src/nghttpx /out # Only use standalone-toolchain for reduce size FROM ubuntu:24.04 LABEL org.opencontainers.image.authors="Tatsuhiro Tsujikawa" ARG NDK_VERSION=r27c ARG NDK=/root/android-ndk-$NDK_VERSION ARG TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64 ARG TARGET=aarch64-linux-android ARG API=33 ARG AR=$TOOLCHAIN/bin/llvm-ar ARG CC=$TOOLCHAIN/bin/$TARGET$API-clang ARG CXX=$TOOLCHAIN/bin/$TARGET$API-clang++ ARG LD=$TOOLCHAIN/bin/ld ARG RANDLIB=$TOOLCHAIN/bin/llvm-ranlib ARG STRIP=$TOOLCHAIN/bin/llvm-strip ARG PREFIX=/root/usr/local WORKDIR /root RUN apt-get update && \ apt-get install -y unzip make binutils autoconf \ automake autotools-dev libtool pkg-config git \ curl dpkg-dev libxml2-dev genisoimage libc6-i386 \ lib32stdc++6 && \ rm -rf /var/cache/apt/* # Download NDK RUN curl -L -O https://dl.google.com/android/repository/android-ndk-$NDK_VERSION-linux.zip && \ unzip -q android-ndk-$NDK_VERSION-linux.zip && \ rm android-ndk-$NDK_VERSION-linux.zip # Setup version of libraries ARG OPENSSL_VERSION=1.1.1w ARG LIBEV_VERSION=4.33 ARG ZLIB_VERSION=1.3.1 ARG CARES_VERSION=1.18.1 ARG NGHTTP2_VERSION=master WORKDIR /root/build RUN curl -L -O https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz && \ tar xf openssl-$OPENSSL_VERSION.tar.gz && \ rm openssl-$OPENSSL_VERSION.tar.gz WORKDIR /root/build/openssl-$OPENSSL_VERSION RUN export ANDROID_NDK_HOME=$NDK PATH=$TOOLCHAIN/bin:$PATH && \ ./Configure no-shared --prefix=$PREFIX android-arm64 && \ make && make install_sw WORKDIR /root/build RUN curl -L -O http://dist.schmorp.de/libev/Attic/libev-$LIBEV_VERSION.tar.gz && \ tar xf libev-$LIBEV_VERSION.tar.gz && \ rm libev-$LIBEV_VERSION.tar.gz WORKDIR /root/build/libev-$LIBEV_VERSION RUN ./configure \ --disable-dependency-tracking \ --host=$TARGET \ --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ --prefix=$PREFIX \ --disable-shared \ --enable-static \ CPPFLAGS=-I$PREFIX/include \ LDFLAGS=-L$PREFIX/lib && \ make install WORKDIR /root/build RUN curl -L -O https://github.com/madler/zlib/releases/download/v$ZLIB_VERSION/zlib-$ZLIB_VERSION.tar.gz && \ tar xf zlib-$ZLIB_VERSION.tar.gz && \ rm zlib-$ZLIB_VERSION.tar.gz WORKDIR /root/build/zlib-$ZLIB_VERSION RUN HOST=$TARGET \ ./configure \ --prefix=$PREFIX \ --libdir=$PREFIX/lib \ --includedir=$PREFIX/include \ --static && \ make install WORKDIR /root/build RUN curl -L -O https://github.com/c-ares/c-ares/releases/download/cares-1_18_1/c-ares-$CARES_VERSION.tar.gz && \ tar xf c-ares-$CARES_VERSION.tar.gz && \ rm c-ares-$CARES_VERSION.tar.gz WORKDIR /root/build/c-ares-$CARES_VERSION RUN ./configure \ --disable-dependency-tracking \ --host=$TARGET \ --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ --prefix=$PREFIX \ --disable-shared && \ make install WORKDIR /root/build RUN git clone --recursive --shallow-submodules https://github.com/nghttp2/nghttp2 -b $NGHTTP2_VERSION --depth 1 WORKDIR /root/build/nghttp2 RUN autoreconf -i && \ ./configure \ --disable-dependency-tracking \ --enable-app \ --disable-shared \ --host=$TARGET \ --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ --without-libxml2 \ --disable-examples \ --disable-threads \ CPPFLAGS="-fPIE -I$PREFIX/include" \ PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \ LDFLAGS="-static-libstdc++ -static-libgcc -fPIE -pie -L$PREFIX/lib" && \ make && \ $STRIP src/nghttpx src/nghttpd src/nghttp nghttp2-1.68.0/PaxHeaders/INSTALL0000644000000000000000000000013215077107305013310 xustar0030 mtime=1761382085.362388044 30 atime=1761382104.850316392 30 ctime=1761382107.834304041 nghttp2-1.68.0/INSTALL0000755000175100017510000003662615077107305013720 0ustar00runnerrunnerInstallation Instructions ************************* Copyright (C) 1994-1996, 1999-2002, 2004-2017, 2020-2021 Free Software Foundation, Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without warranty of any kind. Basic Installation ================== Briefly, the shell command './configure && make && make install' should configure, build, and install this package. The following more-detailed instructions are generic; see the 'README' file for instructions specific to this package. Some packages provide this 'INSTALL' file but do not implement all of the features documented below. The lack of an optional feature in a given package is not necessarily a bug. More recommendations for GNU packages can be found in *note Makefile Conventions: (standards)Makefile Conventions. The 'configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a 'Makefile' in each directory of the package. It may also create one or more '.h' files containing system-dependent definitions. Finally, it creates a shell script 'config.status' that you can run in the future to recreate the current configuration, and a file 'config.log' containing compiler output (useful mainly for debugging 'configure'). It can also use an optional file (typically called 'config.cache' and enabled with '--cache-file=config.cache' or simply '-C') that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how 'configure' could check whether to do them, and mail diffs or instructions to the address given in the 'README' so they can be considered for the next release. If you are using the cache, and at some point 'config.cache' contains results you don't want to keep, you may remove or edit it. The file 'configure.ac' (or 'configure.in') is used to create 'configure' by a program called 'autoconf'. You need 'configure.ac' if you want to change it or regenerate 'configure' using a newer version of 'autoconf'. The simplest way to compile this package is: 1. 'cd' to the directory containing the package's source code and type './configure' to configure the package for your system. Running 'configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type 'make' to compile the package. 3. Optionally, type 'make check' to run any self-tests that come with the package, generally using the just-built uninstalled binaries. 4. Type 'make install' to install the programs and any data files and documentation. When installing into a prefix owned by root, it is recommended that the package be configured and built as a regular user, and only the 'make install' phase executed with root privileges. 5. Optionally, type 'make installcheck' to repeat any self-tests, but this time using the binaries in their final installed location. This target does not install anything. Running this target as a regular user, particularly if the prior 'make install' required root privileges, verifies that the installation completed correctly. 6. You can remove the program binaries and object files from the source code directory by typing 'make clean'. To also remove the files that 'configure' created (so you can compile the package for a different kind of computer), type 'make distclean'. There is also a 'make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. 7. Often, you can also type 'make uninstall' to remove the installed files again. In practice, not all packages have tested that uninstallation works correctly, even though it is required by the GNU Coding Standards. 8. Some packages, particularly those that use Automake, provide 'make distcheck', which can by used by developers to test that all other targets like 'make install' and 'make uninstall' work correctly. This target is generally not run by end users. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the 'configure' script does not know about. Run './configure --help' for details on some of the pertinent environment variables. You can give 'configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c99 CFLAGS=-g LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU 'make'. 'cd' to the directory where you want the object files and executables to go and run the 'configure' script. 'configure' automatically checks for the source code in the directory that 'configure' is in and in '..'. This is known as a "VPATH" build. With a non-GNU 'make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use 'make distclean' before reconfiguring for another architecture. On MacOS X 10.5 and later systems, you can create libraries and executables that work on multiple system types--known as "fat" or "universal" binaries--by specifying multiple '-arch' options to the compiler but only a single '-arch' option to the preprocessor. Like this: ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CPP="gcc -E" CXXCPP="g++ -E" This is not guaranteed to produce working output in all cases, you may have to build one architecture at a time and combine the results using the 'lipo' tool if you have problems. Installation Names ================== By default, 'make install' installs the package's commands under '/usr/local/bin', include files under '/usr/local/include', etc. You can specify an installation prefix other than '/usr/local' by giving 'configure' the option '--prefix=PREFIX', where PREFIX must be an absolute file name. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option '--exec-prefix=PREFIX' to 'configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like '--bindir=DIR' to specify different values for particular kinds of files. Run 'configure --help' for a list of the directories you can set and what kinds of files go in them. In general, the default for these options is expressed in terms of '${prefix}', so that specifying just '--prefix' will affect all of the other directory specifications that were not explicitly provided. The most portable way to affect installation locations is to pass the correct locations to 'configure'; however, many packages provide one or both of the following shortcuts of passing variable assignments to the 'make install' command line to change installation locations without having to reconfigure or recompile. The first method involves providing an override variable for each affected directory. For example, 'make install prefix=/alternate/directory' will choose an alternate location for all directory configuration variables that were expressed in terms of '${prefix}'. Any directories that were specified during 'configure', but not in terms of '${prefix}', must each be overridden at install time for the entire installation to be relocated. The approach of makefile variable overrides for each directory variable is required by the GNU Coding Standards, and ideally causes no recompilation. However, some platforms have known limitations with the semantics of shared libraries that end up requiring recompilation when using this method, particularly noticeable in packages that use GNU Libtool. The second method involves providing the 'DESTDIR' variable. For example, 'make install DESTDIR=/alternate/directory' will prepend '/alternate/directory' before all installation names. The approach of 'DESTDIR' overrides is not required by the GNU Coding Standards, and does not work on platforms that have drive letters. On the other hand, it does better at avoiding recompilation issues, and works well even when some directory options were not specified in terms of '${prefix}' at 'configure' time. Optional Features ================= If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving 'configure' the option '--program-prefix=PREFIX' or '--program-suffix=SUFFIX'. Some packages pay attention to '--enable-FEATURE' options to 'configure', where FEATURE indicates an optional part of the package. They may also pay attention to '--with-PACKAGE' options, where PACKAGE is something like 'gnu-as' or 'x' (for the X Window System). The 'README' should mention any '--enable-' and '--with-' options that the package recognizes. For packages that use the X Window System, 'configure' can usually find the X include and library files automatically, but if it doesn't, you can use the 'configure' options '--x-includes=DIR' and '--x-libraries=DIR' to specify their locations. Some packages offer the ability to configure how verbose the execution of 'make' will be. For these packages, running './configure --enable-silent-rules' sets the default to minimal output, which can be overridden with 'make V=1'; while running './configure --disable-silent-rules' sets the default to verbose, which can be overridden with 'make V=0'. Particular systems ================== On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC is not installed, it is recommended to use the following options in order to use an ANSI C compiler: ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" and if that doesn't work, install pre-built binaries of GCC for HP-UX. HP-UX 'make' updates targets which have the same timestamps as their prerequisites, which makes it generally unusable when shipped generated files such as 'configure' are involved. Use GNU 'make' instead. On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot parse its '' header file. The option '-nodtk' can be used as a workaround. If GNU CC is not installed, it is therefore recommended to try ./configure CC="cc" and if that doesn't work, try ./configure CC="cc -nodtk" On Solaris, don't put '/usr/ucb' early in your 'PATH'. This directory contains several dysfunctional programs; working variants of these programs are available in '/usr/bin'. So, if you need '/usr/ucb' in your 'PATH', put it _after_ '/usr/bin'. On Haiku, software installed for all users goes in '/boot/common', not '/usr/local'. It is recommended to use the following options: ./configure --prefix=/boot/common Specifying the System Type ========================== There may be some features 'configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, 'configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the '--build=TYPE' option. TYPE can either be a short name for the system type, such as 'sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file 'config.sub' for the possible values of each field. If 'config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option '--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with '--host=TYPE'. Sharing Defaults ================ If you want to set default values for 'configure' scripts to share, you can create a site shell script called 'config.site' that gives default values for variables like 'CC', 'cache_file', and 'prefix'. 'configure' looks for 'PREFIX/share/config.site' if it exists, then 'PREFIX/etc/config.site' if it exists. Or, you can set the 'CONFIG_SITE' environment variable to the location of the site script. A warning: not all 'configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to 'configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the 'configure' command line, using 'VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified 'gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for 'CONFIG_SHELL' due to an Autoconf limitation. Until the limitation is lifted, you can use this workaround: CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash 'configure' Invocation ====================== 'configure' recognizes the following options to control how it operates. '--help' '-h' Print a summary of all of the options to 'configure', and exit. '--help=short' '--help=recursive' Print a summary of the options unique to this package's 'configure', and exit. The 'short' variant lists options used only in the top level, while the 'recursive' variant lists options also present in any nested packages. '--version' '-V' Print the version of Autoconf used to generate the 'configure' script, and exit. '--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally 'config.cache'. FILE defaults to '/dev/null' to disable caching. '--config-cache' '-C' Alias for '--cache-file=config.cache'. '--quiet' '--silent' '-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to '/dev/null' (any error messages will still be shown). '--srcdir=DIR' Look for the package's source code in directory DIR. Usually 'configure' can determine that directory automatically. '--prefix=DIR' Use DIR as the installation prefix. *note Installation Names:: for more details, including other options available for fine-tuning the installation locations. '--no-create' '-n' Run the configure checks, but stop before creating any output files. 'configure' also accepts some other, not widely useful, options. Run 'configure --help' for more details. nghttp2-1.68.0/PaxHeaders/lib0000644000000000000000000000013215077107333012751 xustar0030 mtime=1761382107.987303599 30 atime=1761382109.795298372 30 ctime=1761382107.987303599 nghttp2-1.68.0/lib/0000755000175100017510000000000015077107333013416 5ustar00runnerrunnernghttp2-1.68.0/lib/PaxHeaders/nghttp2_stream.c0000644000000000000000000000013215077107270016133 xustar0030 mtime=1761382072.981444194 30 atime=1761382104.970315863 30 ctime=1761382107.956303688 nghttp2-1.68.0/lib/nghttp2_stream.c0000644000175100017510000001564315077107270016534 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_stream.h" #include #include "nghttp2_session.h" #include "nghttp2_helper.h" #include "nghttp2_debug.h" #include "nghttp2_frame.h" void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, uint8_t flags, nghttp2_stream_state initial_state, int32_t remote_initial_window_size, int32_t local_initial_window_size, void *stream_user_data) { stream->stream_id = stream_id; stream->flags = flags; stream->state = initial_state; stream->shut_flags = NGHTTP2_SHUT_NONE; stream->stream_user_data = stream_user_data; stream->item = NULL; stream->remote_window_size = remote_initial_window_size; stream->local_window_size = local_initial_window_size; stream->recv_window_size = 0; stream->consumed_size = 0; stream->recv_reduction = 0; stream->window_update_queued = 0; stream->closed_next = NULL; stream->http_flags = NGHTTP2_HTTP_FLAG_NONE; stream->content_length = -1; stream->recv_content_length = 0; stream->status_code = -1; stream->queued = 0; stream->cycle = 0; stream->pending_penalty = 0; stream->seq = 0; stream->last_writelen = 0; stream->extpri = stream->http_extpri = NGHTTP2_EXTPRI_DEFAULT_URGENCY; } void nghttp2_stream_free(nghttp2_stream *stream) { (void)stream; } void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag) { stream->shut_flags = (uint8_t)(stream->shut_flags | flag); } void nghttp2_stream_attach_item(nghttp2_stream *stream, nghttp2_outbound_item *item) { assert((stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0); assert(stream->item == NULL); DEBUGF("stream: stream=%d attach item=%p\n", stream->stream_id, item); stream->item = item; } void nghttp2_stream_detach_item(nghttp2_stream *stream) { DEBUGF("stream: stream=%d detach item=%p\n", stream->stream_id, stream->item); stream->item = NULL; stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL); } void nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) { assert(stream->item); DEBUGF("stream: stream=%d defer item=%p cause=%02x\n", stream->stream_id, stream->item, flags); stream->flags |= flags; } void nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags) { assert(stream->item); DEBUGF("stream: stream=%d resume item=%p flags=%02x\n", stream->stream_id, stream->item, flags); stream->flags = (uint8_t)(stream->flags & ~flags); } int nghttp2_stream_check_deferred_item(nghttp2_stream *stream) { return stream->item && (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL); } int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream) { return stream->item && (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); } static int update_initial_window_size(int32_t *window_size_ptr, int32_t new_initial_window_size, int32_t old_initial_window_size) { int64_t new_window_size = (int64_t)(*window_size_ptr) + new_initial_window_size - old_initial_window_size; if (INT32_MIN > new_window_size || new_window_size > NGHTTP2_MAX_WINDOW_SIZE) { return -1; } *window_size_ptr = (int32_t)new_window_size; return 0; } int nghttp2_stream_update_remote_initial_window_size( nghttp2_stream *stream, int32_t new_initial_window_size, int32_t old_initial_window_size) { return update_initial_window_size(&stream->remote_window_size, new_initial_window_size, old_initial_window_size); } int nghttp2_stream_update_local_initial_window_size( nghttp2_stream *stream, int32_t new_initial_window_size, int32_t old_initial_window_size) { return update_initial_window_size(&stream->local_window_size, new_initial_window_size, old_initial_window_size); } void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream) { stream->state = NGHTTP2_STREAM_OPENED; stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_PUSH); } nghttp2_stream_proto_state nghttp2_stream_get_state(nghttp2_stream *stream) { if (stream == &nghttp2_stream_root) { return NGHTTP2_STREAM_STATE_IDLE; } if (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) { return NGHTTP2_STREAM_STATE_CLOSED; } if (stream->flags & NGHTTP2_STREAM_FLAG_PUSH) { if (stream->shut_flags & NGHTTP2_SHUT_RD) { return NGHTTP2_STREAM_STATE_RESERVED_LOCAL; } if (stream->shut_flags & NGHTTP2_SHUT_WR) { return NGHTTP2_STREAM_STATE_RESERVED_REMOTE; } } if (stream->shut_flags & NGHTTP2_SHUT_RD) { return NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE; } if (stream->shut_flags & NGHTTP2_SHUT_WR) { return NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL; } if (stream->state == NGHTTP2_STREAM_IDLE) { return NGHTTP2_STREAM_STATE_IDLE; } return NGHTTP2_STREAM_STATE_OPEN; } nghttp2_stream *nghttp2_stream_get_parent(nghttp2_stream *stream) { (void)stream; return NULL; } nghttp2_stream *nghttp2_stream_get_next_sibling(nghttp2_stream *stream) { (void)stream; return NULL; } nghttp2_stream *nghttp2_stream_get_previous_sibling(nghttp2_stream *stream) { (void)stream; return NULL; } nghttp2_stream *nghttp2_stream_get_first_child(nghttp2_stream *stream) { (void)stream; return NULL; } int32_t nghttp2_stream_get_weight(nghttp2_stream *stream) { (void)stream; return NGHTTP2_DEFAULT_WEIGHT; } int32_t nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream) { (void)stream; return 0; } int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream) { return stream->stream_id; } nghttp2-1.68.0/lib/PaxHeaders/libnghttp2.pc.in0000644000000000000000000000013215077107270016034 xustar0030 mtime=1761382072.976444217 30 atime=1761382103.792321052 30 ctime=1761382107.914303809 nghttp2-1.68.0/lib/libnghttp2.pc.in0000644000175100017510000000254215077107270016427 0ustar00runnerrunner# nghttp2 - HTTP/2 C Library # Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libnghttp2 Description: HTTP/2 C library URL: https://github.com/tatsuhiro-t/nghttp2 Version: @VERSION@ Libs: -L${libdir} -lnghttp2 Cflags: -I${includedir} nghttp2-1.68.0/lib/PaxHeaders/nghttp2_hd.c0000644000000000000000000000013215077107270015233 xustar0030 mtime=1761382072.978444208 30 atime=1761382104.978315828 30 ctime=1761382107.964303665 nghttp2-1.68.0/lib/nghttp2_hd.c0000644000175100017510000017561015077107270015635 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_hd.h" #include #include #include #include "nghttp2_helper.h" #include "nghttp2_int.h" #include "nghttp2_debug.h" /* Make scalar initialization form of nghttp2_hd_entry */ #define MAKE_STATIC_ENT(N, V, T, H) \ { \ {NULL, NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \ {NULL, NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, \ {(uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0}, \ T, \ H, \ } /* Generated by mkstatictbl.py */ /* 3rd parameter is nghttp2_token value for header field name. We use first enum value if same header names are repeated (e.g., :status). */ static const nghttp2_hd_static_entry static_table[] = { MAKE_STATIC_ENT(":authority", "", 0, 3153725150u), MAKE_STATIC_ENT(":method", "GET", 1, 695666056u), MAKE_STATIC_ENT(":method", "POST", 1, 695666056u), MAKE_STATIC_ENT(":path", "/", 3, 3292848686u), MAKE_STATIC_ENT(":path", "/index.html", 3, 3292848686u), MAKE_STATIC_ENT(":scheme", "http", 5, 2510477674u), MAKE_STATIC_ENT(":scheme", "https", 5, 2510477674u), MAKE_STATIC_ENT(":status", "200", 7, 4000288983u), MAKE_STATIC_ENT(":status", "204", 7, 4000288983u), MAKE_STATIC_ENT(":status", "206", 7, 4000288983u), MAKE_STATIC_ENT(":status", "304", 7, 4000288983u), MAKE_STATIC_ENT(":status", "400", 7, 4000288983u), MAKE_STATIC_ENT(":status", "404", 7, 4000288983u), MAKE_STATIC_ENT(":status", "500", 7, 4000288983u), MAKE_STATIC_ENT("accept-charset", "", 14, 3664010344u), MAKE_STATIC_ENT("accept-encoding", "gzip, deflate", 15, 3379649177u), MAKE_STATIC_ENT("accept-language", "", 16, 1979086614u), MAKE_STATIC_ENT("accept-ranges", "", 17, 1713753958u), MAKE_STATIC_ENT("accept", "", 18, 136609321u), MAKE_STATIC_ENT("access-control-allow-origin", "", 19, 2710797292u), MAKE_STATIC_ENT("age", "", 20, 742476188u), MAKE_STATIC_ENT("allow", "", 21, 2930878514u), MAKE_STATIC_ENT("authorization", "", 22, 2436257726u), MAKE_STATIC_ENT("cache-control", "", 23, 1355326669u), MAKE_STATIC_ENT("content-disposition", "", 24, 3889184348u), MAKE_STATIC_ENT("content-encoding", "", 25, 65203592u), MAKE_STATIC_ENT("content-language", "", 26, 24973587u), MAKE_STATIC_ENT("content-length", "", 27, 1308181789u), MAKE_STATIC_ENT("content-location", "", 28, 2302364718u), MAKE_STATIC_ENT("content-range", "", 29, 3555523146u), MAKE_STATIC_ENT("content-type", "", 30, 4244048277u), MAKE_STATIC_ENT("cookie", "", 31, 2007449791u), MAKE_STATIC_ENT("date", "", 32, 3564297305u), MAKE_STATIC_ENT("etag", "", 33, 113792960u), MAKE_STATIC_ENT("expect", "", 34, 2530896728u), MAKE_STATIC_ENT("expires", "", 35, 1049544579u), MAKE_STATIC_ENT("from", "", 36, 2513272949u), MAKE_STATIC_ENT("host", "", 37, 2952701295u), MAKE_STATIC_ENT("if-match", "", 38, 3597694698u), MAKE_STATIC_ENT("if-modified-since", "", 39, 2213050793u), MAKE_STATIC_ENT("if-none-match", "", 40, 2536202615u), MAKE_STATIC_ENT("if-range", "", 41, 2340978238u), MAKE_STATIC_ENT("if-unmodified-since", "", 42, 3794814858u), MAKE_STATIC_ENT("last-modified", "", 43, 3226950251u), MAKE_STATIC_ENT("link", "", 44, 232457833u), MAKE_STATIC_ENT("location", "", 45, 200649126u), MAKE_STATIC_ENT("max-forwards", "", 46, 1826162134u), MAKE_STATIC_ENT("proxy-authenticate", "", 47, 2709445359u), MAKE_STATIC_ENT("proxy-authorization", "", 48, 2686392507u), MAKE_STATIC_ENT("range", "", 49, 4208725202u), MAKE_STATIC_ENT("referer", "", 50, 3969579366u), MAKE_STATIC_ENT("refresh", "", 51, 3572655668u), MAKE_STATIC_ENT("retry-after", "", 52, 3336180598u), MAKE_STATIC_ENT("server", "", 53, 1085029842u), MAKE_STATIC_ENT("set-cookie", "", 54, 1848371000u), MAKE_STATIC_ENT("strict-transport-security", "", 55, 4138147361u), MAKE_STATIC_ENT("transfer-encoding", "", 56, 3719590988u), MAKE_STATIC_ENT("user-agent", "", 57, 606444526u), MAKE_STATIC_ENT("vary", "", 58, 1085005381u), MAKE_STATIC_ENT("via", "", 59, 1762798611u), MAKE_STATIC_ENT("www-authenticate", "", 60, 779865858u), }; static int memeq(const void *s1, const void *s2, size_t n) { return memcmp(s1, s2, n) == 0; } /* * This function was generated by genlibtokenlookup.py. Inspired by * h2o header lookup. https://github.com/h2o/h2o */ static int32_t lookup_token(const uint8_t *name, size_t namelen) { switch (namelen) { case 2: switch (name[1]) { case 'e': if (memeq("t", name, 1)) { return NGHTTP2_TOKEN_TE; } break; } break; case 3: switch (name[2]) { case 'a': if (memeq("vi", name, 2)) { return NGHTTP2_TOKEN_VIA; } break; case 'e': if (memeq("ag", name, 2)) { return NGHTTP2_TOKEN_AGE; } break; } break; case 4: switch (name[3]) { case 'e': if (memeq("dat", name, 3)) { return NGHTTP2_TOKEN_DATE; } break; case 'g': if (memeq("eta", name, 3)) { return NGHTTP2_TOKEN_ETAG; } break; case 'k': if (memeq("lin", name, 3)) { return NGHTTP2_TOKEN_LINK; } break; case 'm': if (memeq("fro", name, 3)) { return NGHTTP2_TOKEN_FROM; } break; case 't': if (memeq("hos", name, 3)) { return NGHTTP2_TOKEN_HOST; } break; case 'y': if (memeq("var", name, 3)) { return NGHTTP2_TOKEN_VARY; } break; } break; case 5: switch (name[4]) { case 'e': if (memeq("rang", name, 4)) { return NGHTTP2_TOKEN_RANGE; } break; case 'h': if (memeq(":pat", name, 4)) { return NGHTTP2_TOKEN__PATH; } break; case 'w': if (memeq("allo", name, 4)) { return NGHTTP2_TOKEN_ALLOW; } break; } break; case 6: switch (name[5]) { case 'e': if (memeq("cooki", name, 5)) { return NGHTTP2_TOKEN_COOKIE; } break; case 'r': if (memeq("serve", name, 5)) { return NGHTTP2_TOKEN_SERVER; } break; case 't': if (memeq("accep", name, 5)) { return NGHTTP2_TOKEN_ACCEPT; } if (memeq("expec", name, 5)) { return NGHTTP2_TOKEN_EXPECT; } break; } break; case 7: switch (name[6]) { case 'd': if (memeq(":metho", name, 6)) { return NGHTTP2_TOKEN__METHOD; } break; case 'e': if (memeq(":schem", name, 6)) { return NGHTTP2_TOKEN__SCHEME; } if (memeq("upgrad", name, 6)) { return NGHTTP2_TOKEN_UPGRADE; } break; case 'h': if (memeq("refres", name, 6)) { return NGHTTP2_TOKEN_REFRESH; } break; case 'r': if (memeq("refere", name, 6)) { return NGHTTP2_TOKEN_REFERER; } break; case 's': if (memeq(":statu", name, 6)) { return NGHTTP2_TOKEN__STATUS; } if (memeq("expire", name, 6)) { return NGHTTP2_TOKEN_EXPIRES; } break; } break; case 8: switch (name[7]) { case 'e': if (memeq("if-rang", name, 7)) { return NGHTTP2_TOKEN_IF_RANGE; } break; case 'h': if (memeq("if-matc", name, 7)) { return NGHTTP2_TOKEN_IF_MATCH; } break; case 'n': if (memeq("locatio", name, 7)) { return NGHTTP2_TOKEN_LOCATION; } break; case 'y': if (memeq("priorit", name, 7)) { return NGHTTP2_TOKEN_PRIORITY; } break; } break; case 9: switch (name[8]) { case 'l': if (memeq(":protoco", name, 8)) { return NGHTTP2_TOKEN__PROTOCOL; } break; } break; case 10: switch (name[9]) { case 'e': if (memeq("keep-aliv", name, 9)) { return NGHTTP2_TOKEN_KEEP_ALIVE; } if (memeq("set-cooki", name, 9)) { return NGHTTP2_TOKEN_SET_COOKIE; } break; case 'n': if (memeq("connectio", name, 9)) { return NGHTTP2_TOKEN_CONNECTION; } break; case 't': if (memeq("user-agen", name, 9)) { return NGHTTP2_TOKEN_USER_AGENT; } break; case 'y': if (memeq(":authorit", name, 9)) { return NGHTTP2_TOKEN__AUTHORITY; } break; } break; case 11: switch (name[10]) { case 'r': if (memeq("retry-afte", name, 10)) { return NGHTTP2_TOKEN_RETRY_AFTER; } break; } break; case 12: switch (name[11]) { case 'e': if (memeq("content-typ", name, 11)) { return NGHTTP2_TOKEN_CONTENT_TYPE; } break; case 's': if (memeq("max-forward", name, 11)) { return NGHTTP2_TOKEN_MAX_FORWARDS; } break; } break; case 13: switch (name[12]) { case 'd': if (memeq("last-modifie", name, 12)) { return NGHTTP2_TOKEN_LAST_MODIFIED; } break; case 'e': if (memeq("content-rang", name, 12)) { return NGHTTP2_TOKEN_CONTENT_RANGE; } break; case 'h': if (memeq("if-none-matc", name, 12)) { return NGHTTP2_TOKEN_IF_NONE_MATCH; } break; case 'l': if (memeq("cache-contro", name, 12)) { return NGHTTP2_TOKEN_CACHE_CONTROL; } break; case 'n': if (memeq("authorizatio", name, 12)) { return NGHTTP2_TOKEN_AUTHORIZATION; } break; case 's': if (memeq("accept-range", name, 12)) { return NGHTTP2_TOKEN_ACCEPT_RANGES; } break; } break; case 14: switch (name[13]) { case 'h': if (memeq("content-lengt", name, 13)) { return NGHTTP2_TOKEN_CONTENT_LENGTH; } break; case 't': if (memeq("accept-charse", name, 13)) { return NGHTTP2_TOKEN_ACCEPT_CHARSET; } break; } break; case 15: switch (name[14]) { case 'e': if (memeq("accept-languag", name, 14)) { return NGHTTP2_TOKEN_ACCEPT_LANGUAGE; } break; case 'g': if (memeq("accept-encodin", name, 14)) { return NGHTTP2_TOKEN_ACCEPT_ENCODING; } break; } break; case 16: switch (name[15]) { case 'e': if (memeq("content-languag", name, 15)) { return NGHTTP2_TOKEN_CONTENT_LANGUAGE; } if (memeq("www-authenticat", name, 15)) { return NGHTTP2_TOKEN_WWW_AUTHENTICATE; } break; case 'g': if (memeq("content-encodin", name, 15)) { return NGHTTP2_TOKEN_CONTENT_ENCODING; } break; case 'n': if (memeq("content-locatio", name, 15)) { return NGHTTP2_TOKEN_CONTENT_LOCATION; } if (memeq("proxy-connectio", name, 15)) { return NGHTTP2_TOKEN_PROXY_CONNECTION; } break; } break; case 17: switch (name[16]) { case 'e': if (memeq("if-modified-sinc", name, 16)) { return NGHTTP2_TOKEN_IF_MODIFIED_SINCE; } break; case 'g': if (memeq("transfer-encodin", name, 16)) { return NGHTTP2_TOKEN_TRANSFER_ENCODING; } break; } break; case 18: switch (name[17]) { case 'e': if (memeq("proxy-authenticat", name, 17)) { return NGHTTP2_TOKEN_PROXY_AUTHENTICATE; } break; } break; case 19: switch (name[18]) { case 'e': if (memeq("if-unmodified-sinc", name, 18)) { return NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE; } break; case 'n': if (memeq("content-dispositio", name, 18)) { return NGHTTP2_TOKEN_CONTENT_DISPOSITION; } if (memeq("proxy-authorizatio", name, 18)) { return NGHTTP2_TOKEN_PROXY_AUTHORIZATION; } break; } break; case 25: switch (name[24]) { case 'y': if (memeq("strict-transport-securit", name, 24)) { return NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY; } break; } break; case 27: switch (name[26]) { case 'n': if (memeq("access-control-allow-origi", name, 26)) { return NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN; } break; } break; } return -1; } void nghttp2_hd_entry_init(nghttp2_hd_entry *ent, nghttp2_hd_nv *nv) { ent->nv = *nv; ent->cnv.name = nv->name->base; ent->cnv.namelen = nv->name->len; ent->cnv.value = nv->value->base; ent->cnv.valuelen = nv->value->len; ent->cnv.flags = nv->flags; ent->next = NULL; ent->hash = 0; nghttp2_rcbuf_incref(ent->nv.name); nghttp2_rcbuf_incref(ent->nv.value); } void nghttp2_hd_entry_free(nghttp2_hd_entry *ent) { nghttp2_rcbuf_decref(ent->nv.value); nghttp2_rcbuf_decref(ent->nv.name); } static int name_eq(const nghttp2_hd_nv *a, const nghttp2_nv *b) { return a->name->len == b->namelen && memeq(a->name->base, b->name, b->namelen); } static int value_eq(const nghttp2_hd_nv *a, const nghttp2_nv *b) { return a->value->len == b->valuelen && memeq(a->value->base, b->value, b->valuelen); } static uint32_t name_hash(const nghttp2_nv *nv) { /* 32 bit FNV-1a: http://isthe.com/chongo/tech/comp/fnv/ */ uint32_t h = 2166136261u; size_t i; for (i = 0; i < nv->namelen; ++i) { h ^= nv->name[i]; h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24); } return h; } static void hd_map_init(nghttp2_hd_map *map) { memset(map, 0, sizeof(nghttp2_hd_map)); } static void hd_map_insert(nghttp2_hd_map *map, nghttp2_hd_entry *ent) { nghttp2_hd_entry **bucket; bucket = &map->table[ent->hash & (HD_MAP_SIZE - 1)]; if (*bucket == NULL) { *bucket = ent; return; } /* lower index is linked near the root */ ent->next = *bucket; *bucket = ent; } static nghttp2_hd_entry *hd_map_find(nghttp2_hd_map *map, int *exact_match, const nghttp2_nv *nv, int32_t token, uint32_t hash, int name_only) { nghttp2_hd_entry *p; nghttp2_hd_entry *res = NULL; *exact_match = 0; for (p = map->table[hash & (HD_MAP_SIZE - 1)]; p; p = p->next) { if (token != p->nv.token || (token == -1 && (hash != p->hash || !name_eq(&p->nv, nv)))) { continue; } if (!res) { res = p; if (name_only) { break; } } if (value_eq(&p->nv, nv)) { res = p; *exact_match = 1; break; } } return res; } static void hd_map_remove(nghttp2_hd_map *map, nghttp2_hd_entry *ent) { nghttp2_hd_entry **dst; dst = &map->table[ent->hash & (HD_MAP_SIZE - 1)]; for (; *dst; dst = &(*dst)->next) { if (*dst != ent) { continue; } *dst = ent->next; ent->next = NULL; return; } } static int hd_ringbuf_init(nghttp2_hd_ringbuf *ringbuf, size_t bufsize, nghttp2_mem *mem) { size_t size; const size_t max_size = SIZE_MAX / sizeof(nghttp2_hd_entry *); if (bufsize > max_size) { return NGHTTP2_ERR_NOMEM; } for (size = 1; size < bufsize; size <<= 1) ; if (size > max_size) { return NGHTTP2_ERR_NOMEM; } ringbuf->buffer = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry *) * size); if (ringbuf->buffer == NULL) { return NGHTTP2_ERR_NOMEM; } ringbuf->mask = size - 1; ringbuf->first = 0; ringbuf->len = 0; return 0; } static nghttp2_hd_entry *hd_ringbuf_get(nghttp2_hd_ringbuf *ringbuf, size_t idx) { assert(idx < ringbuf->len); return ringbuf->buffer[(ringbuf->first + idx) & ringbuf->mask]; } static int hd_ringbuf_reserve(nghttp2_hd_ringbuf *ringbuf, size_t bufsize, nghttp2_mem *mem) { size_t i; size_t size; nghttp2_hd_entry **buffer; if (ringbuf->mask + 1 >= bufsize) { return 0; } for (size = 1; size < bufsize; size <<= 1) ; buffer = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry *) * size); if (buffer == NULL) { return NGHTTP2_ERR_NOMEM; } for (i = 0; i < ringbuf->len; ++i) { buffer[i] = hd_ringbuf_get(ringbuf, i); } nghttp2_mem_free(mem, ringbuf->buffer); ringbuf->buffer = buffer; ringbuf->mask = size - 1; ringbuf->first = 0; return 0; } static void hd_ringbuf_free(nghttp2_hd_ringbuf *ringbuf, nghttp2_mem *mem) { size_t i; if (ringbuf == NULL) { return; } for (i = 0; i < ringbuf->len; ++i) { nghttp2_hd_entry *ent = hd_ringbuf_get(ringbuf, i); nghttp2_hd_entry_free(ent); nghttp2_mem_free(mem, ent); } nghttp2_mem_free(mem, ringbuf->buffer); } static int hd_ringbuf_push_front(nghttp2_hd_ringbuf *ringbuf, nghttp2_hd_entry *ent, nghttp2_mem *mem) { int rv; rv = hd_ringbuf_reserve(ringbuf, ringbuf->len + 1, mem); if (rv != 0) { return rv; } ringbuf->buffer[--ringbuf->first & ringbuf->mask] = ent; ++ringbuf->len; return 0; } static void hd_ringbuf_pop_back(nghttp2_hd_ringbuf *ringbuf) { assert(ringbuf->len > 0); --ringbuf->len; } static int hd_context_init(nghttp2_hd_context *context, nghttp2_mem *mem) { int rv; context->mem = mem; context->bad = 0; context->hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; rv = hd_ringbuf_init( &context->hd_table, context->hd_table_bufsize_max / NGHTTP2_HD_ENTRY_OVERHEAD, mem); if (rv != 0) { return rv; } context->hd_table_bufsize = 0; context->next_seq = 0; return 0; } static void hd_context_free(nghttp2_hd_context *context) { hd_ringbuf_free(&context->hd_table, context->mem); } int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_mem *mem) { return nghttp2_hd_deflate_init2( deflater, NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE, mem); } int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater, size_t max_deflate_dynamic_table_size, nghttp2_mem *mem) { int rv; rv = hd_context_init(&deflater->ctx, mem); if (rv != 0) { return rv; } hd_map_init(&deflater->map); if (max_deflate_dynamic_table_size < NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE) { deflater->notify_table_size_change = 1; deflater->ctx.hd_table_bufsize_max = max_deflate_dynamic_table_size; } else { deflater->notify_table_size_change = 0; } deflater->deflate_hd_table_bufsize_max = max_deflate_dynamic_table_size; deflater->min_hd_table_bufsize_max = UINT32_MAX; return 0; } int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem) { int rv; rv = hd_context_init(&inflater->ctx, mem); if (rv != 0) { goto fail; } inflater->settings_hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; inflater->min_hd_table_bufsize_max = UINT32_MAX; inflater->nv_name_keep = NULL; inflater->nv_value_keep = NULL; inflater->opcode = NGHTTP2_HD_OPCODE_NONE; inflater->state = NGHTTP2_HD_STATE_INFLATE_START; nghttp2_buf_init(&inflater->namebuf); nghttp2_buf_init(&inflater->valuebuf); inflater->namercbuf = NULL; inflater->valuercbuf = NULL; inflater->huffman_encoded = 0; inflater->index = 0; inflater->left = 0; inflater->shift = 0; inflater->index_required = 0; inflater->no_index = 0; return 0; fail: return rv; } static void hd_inflate_keep_free(nghttp2_hd_inflater *inflater) { nghttp2_rcbuf_decref(inflater->nv_value_keep); nghttp2_rcbuf_decref(inflater->nv_name_keep); inflater->nv_value_keep = NULL; inflater->nv_name_keep = NULL; } void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater) { hd_context_free(&deflater->ctx); } void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater) { hd_inflate_keep_free(inflater); nghttp2_rcbuf_decref(inflater->valuercbuf); nghttp2_rcbuf_decref(inflater->namercbuf); hd_context_free(&inflater->ctx); } static size_t entry_room(size_t namelen, size_t valuelen) { return NGHTTP2_HD_ENTRY_OVERHEAD + namelen + valuelen; } static void emit_header(nghttp2_hd_nv *nv_out, nghttp2_hd_nv *nv) { DEBUGF("inflatehd: header emission: %s: %s\n", nv->name->base, nv->value->base); /* ent->ref may be 0. This happens if the encoder emits literal block larger than header table capacity with indexing. */ *nv_out = *nv; } static size_t count_encoded_length(size_t n, size_t prefix) { size_t k = (size_t)((1 << prefix) - 1); size_t len = 0; if (n < k) { return 1; } n -= k; ++len; for (; n >= 128; n >>= 7, ++len) ; return len + 1; } static size_t encode_length(uint8_t *buf, size_t n, size_t prefix) { size_t k = (size_t)((1 << prefix) - 1); uint8_t *begin = buf; *buf = (uint8_t)(*buf & ~k); if (n < k) { *buf = (uint8_t)(*buf | n); return 1; } *buf = (uint8_t)(*buf | k); ++buf; n -= k; for (; n >= 128; n >>= 7) { *buf++ = (uint8_t)((1 << 7) | (n & 0x7f)); } *buf++ = (uint8_t)n; return (size_t)(buf - begin); } /* * Decodes |prefix| prefixed integer stored from |in|. The |last| * represents the 1 beyond the last of the valid contiguous memory * region from |in|. The decoded integer must be less than or equal * to UINT32_MAX. * * If the |initial| is nonzero, it is used as a initial value, this * function assumes the |in| starts with intermediate data. * * An entire integer is decoded successfully, decoded, the |*fin| is * set to nonzero. * * This function stores the decoded integer in |*res| if it succeed, * including partial decoding (in this case, number of shift to make * in the next call will be stored in |*shift_ptr|) and returns number * of bytes processed, or returns -1, indicating decoding error. */ static nghttp2_ssize decode_length(uint32_t *res, size_t *shift_ptr, int *fin, uint32_t initial, size_t shift, const uint8_t *in, const uint8_t *last, size_t prefix) { uint32_t k = (uint8_t)((1 << prefix) - 1); uint32_t n = initial; const uint8_t *start = in; *shift_ptr = 0; *fin = 0; if (n == 0) { if ((*in & k) != k) { *res = (*in) & k; *fin = 1; return 1; } n = k; if (++in == last) { *res = n; return (nghttp2_ssize)(in - start); } } for (; in != last; ++in, shift += 7) { uint32_t add = *in & 0x7f; if (shift >= 32) { DEBUGF("inflate: shift exponent overflow\n"); return -1; } if ((UINT32_MAX >> shift) < add) { DEBUGF("inflate: integer overflow on shift\n"); return -1; } add <<= shift; if (UINT32_MAX - add < n) { DEBUGF("inflate: integer overflow on addition\n"); return -1; } n += add; if ((*in & (1 << 7)) == 0) { break; } } *shift_ptr = shift; if (in == last) { *res = n; return (nghttp2_ssize)(in - start); } *res = n; *fin = 1; return (nghttp2_ssize)(in + 1 - start); } static int emit_table_size(nghttp2_bufs *bufs, size_t table_size) { int rv; uint8_t *bufp; size_t blocklen; uint8_t sb[16]; DEBUGF("deflatehd: emit table_size=%zu\n", table_size); blocklen = count_encoded_length(table_size, 5); if (sizeof(sb) < blocklen) { return NGHTTP2_ERR_HEADER_COMP; } bufp = sb; *bufp = 0x20u; encode_length(bufp, table_size, 5); rv = nghttp2_bufs_add(bufs, sb, blocklen); if (rv != 0) { return rv; } return 0; } static int emit_indexed_block(nghttp2_bufs *bufs, size_t idx) { int rv; size_t blocklen; uint8_t sb[16]; uint8_t *bufp; blocklen = count_encoded_length(idx + 1, 7); DEBUGF("deflatehd: emit indexed index=%zu, %zu bytes\n", idx, blocklen); if (sizeof(sb) < blocklen) { return NGHTTP2_ERR_HEADER_COMP; } bufp = sb; *bufp = 0x80u; encode_length(bufp, idx + 1, 7); rv = nghttp2_bufs_add(bufs, sb, blocklen); if (rv != 0) { return rv; } return 0; } static int emit_string(nghttp2_bufs *bufs, const uint8_t *str, size_t len) { int rv; uint8_t sb[16]; uint8_t *bufp; size_t blocklen; size_t enclen; int huffman = 0; enclen = nghttp2_hd_huff_encode_count(str, len); if (enclen < len) { huffman = 1; } else { enclen = len; } blocklen = count_encoded_length(enclen, 7); DEBUGF("deflatehd: emit string str=%.*s, length=%zu, huffman=%d, " "encoded_length=%zu\n", (int)len, (const char *)str, len, huffman, enclen); if (sizeof(sb) < blocklen) { return NGHTTP2_ERR_HEADER_COMP; } bufp = sb; *bufp = huffman ? 1 << 7 : 0; encode_length(bufp, enclen, 7); rv = nghttp2_bufs_add(bufs, sb, blocklen); if (rv != 0) { return rv; } if (huffman) { rv = nghttp2_hd_huff_encode(bufs, str, len); } else { assert(enclen == len); rv = nghttp2_bufs_add(bufs, str, len); } return rv; } static uint8_t pack_first_byte(int indexing_mode) { switch (indexing_mode) { case NGHTTP2_HD_WITH_INDEXING: return 0x40u; case NGHTTP2_HD_WITHOUT_INDEXING: return 0; case NGHTTP2_HD_NEVER_INDEXING: return 0x10u; default: assert(0); } /* This is required to compile with android NDK r10d + --enable-werror */ return 0; } static int emit_indname_block(nghttp2_bufs *bufs, size_t idx, const nghttp2_nv *nv, int indexing_mode) { int rv; uint8_t *bufp; size_t blocklen; uint8_t sb[16]; size_t prefixlen; if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) { prefixlen = 6; } else { prefixlen = 4; } DEBUGF("deflatehd: emit indname index=%zu, valuelen=%zu, indexing_mode=%d\n", idx, nv->valuelen, indexing_mode); blocklen = count_encoded_length(idx + 1, prefixlen); if (sizeof(sb) < blocklen) { return NGHTTP2_ERR_HEADER_COMP; } bufp = sb; *bufp = pack_first_byte(indexing_mode); encode_length(bufp, idx + 1, prefixlen); rv = nghttp2_bufs_add(bufs, sb, blocklen); if (rv != 0) { return rv; } rv = emit_string(bufs, nv->value, nv->valuelen); if (rv != 0) { return rv; } return 0; } static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv, int indexing_mode) { int rv; DEBUGF( "deflatehd: emit newname namelen=%zu, valuelen=%zu, indexing_mode=%d\n", nv->namelen, nv->valuelen, indexing_mode); rv = nghttp2_bufs_addb(bufs, pack_first_byte(indexing_mode)); if (rv != 0) { return rv; } rv = emit_string(bufs, nv->name, nv->namelen); if (rv != 0) { return rv; } rv = emit_string(bufs, nv->value, nv->valuelen); if (rv != 0) { return rv; } return 0; } static int add_hd_table_incremental(nghttp2_hd_context *context, nghttp2_hd_nv *nv, nghttp2_hd_map *map, uint32_t hash) { int rv; nghttp2_hd_entry *new_ent; size_t room; nghttp2_mem *mem; mem = context->mem; room = entry_room(nv->name->len, nv->value->len); while (context->hd_table_bufsize + room > context->hd_table_bufsize_max && context->hd_table.len > 0) { size_t idx = context->hd_table.len - 1; nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx); context->hd_table_bufsize -= entry_room(ent->nv.name->len, ent->nv.value->len); DEBUGF("hpack: remove item from header table: %s: %s\n", (char *)ent->nv.name->base, (char *)ent->nv.value->base); hd_ringbuf_pop_back(&context->hd_table); if (map) { hd_map_remove(map, ent); } nghttp2_hd_entry_free(ent); nghttp2_mem_free(mem, ent); } if (room > context->hd_table_bufsize_max) { /* The entry taking more than NGHTTP2_HD_MAX_BUFFER_SIZE is immediately evicted. So we don't allocate memory for it. */ return 0; } new_ent = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry)); if (new_ent == NULL) { return NGHTTP2_ERR_NOMEM; } nghttp2_hd_entry_init(new_ent, nv); rv = hd_ringbuf_push_front(&context->hd_table, new_ent, mem); if (rv != 0) { nghttp2_hd_entry_free(new_ent); nghttp2_mem_free(mem, new_ent); return rv; } new_ent->seq = context->next_seq++; new_ent->hash = hash; if (map) { hd_map_insert(map, new_ent); } context->hd_table_bufsize += room; return 0; } typedef struct { nghttp2_ssize index; /* Nonzero if both name and value are matched. */ int name_value_match; } search_result; static search_result search_static_table(const nghttp2_nv *nv, int32_t token, int name_only) { search_result res = {token, 0}; int i; const nghttp2_hd_static_entry *ent; if (name_only) { return res; } for (i = token; i <= NGHTTP2_TOKEN_WWW_AUTHENTICATE && static_table[i].token == token; ++i) { ent = &static_table[i]; if (ent->value.len == nv->valuelen && memcmp(ent->value.base, nv->value, nv->valuelen) == 0) { res.index = i; res.name_value_match = 1; return res; } } return res; } static search_result search_hd_table(nghttp2_hd_context *context, const nghttp2_nv *nv, int32_t token, int indexing_mode, nghttp2_hd_map *map, uint32_t hash) { search_result res = {-1, 0}; const nghttp2_hd_entry *ent; int exact_match; int name_only = indexing_mode == NGHTTP2_HD_NEVER_INDEXING; exact_match = 0; ent = hd_map_find(map, &exact_match, nv, token, hash, name_only); if (!exact_match && token >= 0 && token <= NGHTTP2_TOKEN_WWW_AUTHENTICATE) { return search_static_table(nv, token, name_only); } if (ent == NULL) { return res; } res.index = (nghttp2_ssize)(context->next_seq - 1 - ent->seq + NGHTTP2_STATIC_TABLE_LENGTH); res.name_value_match = exact_match; return res; } static void hd_context_shrink_table_size(nghttp2_hd_context *context, nghttp2_hd_map *map) { nghttp2_mem *mem; mem = context->mem; while (context->hd_table_bufsize > context->hd_table_bufsize_max && context->hd_table.len > 0) { size_t idx = context->hd_table.len - 1; nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx); context->hd_table_bufsize -= entry_room(ent->nv.name->len, ent->nv.value->len); hd_ringbuf_pop_back(&context->hd_table); if (map) { hd_map_remove(map, ent); } nghttp2_hd_entry_free(ent); nghttp2_mem_free(mem, ent); } } int nghttp2_hd_deflate_change_table_size( nghttp2_hd_deflater *deflater, size_t settings_max_dynamic_table_size) { size_t next_bufsize = nghttp2_min_size( settings_max_dynamic_table_size, deflater->deflate_hd_table_bufsize_max); deflater->ctx.hd_table_bufsize_max = next_bufsize; deflater->min_hd_table_bufsize_max = nghttp2_min_size(deflater->min_hd_table_bufsize_max, next_bufsize); deflater->notify_table_size_change = 1; hd_context_shrink_table_size(&deflater->ctx, &deflater->map); return 0; } int nghttp2_hd_inflate_change_table_size( nghttp2_hd_inflater *inflater, size_t settings_max_dynamic_table_size) { switch (inflater->state) { case NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE: case NGHTTP2_HD_STATE_INFLATE_START: break; default: return NGHTTP2_ERR_INVALID_STATE; } inflater->settings_hd_table_bufsize_max = settings_max_dynamic_table_size; /* It seems that encoder is not required to send dynamic table size update if the table size is not changed after applying SETTINGS_HEADER_TABLE_SIZE. RFC 7541 is ambiguous here, but this is the intention of the editor. If new maximum table size is strictly smaller than the current negotiated maximum size, encoder must send dynamic table size update. In other cases, we cannot expect it to do so. */ if (inflater->ctx.hd_table_bufsize_max > settings_max_dynamic_table_size) { inflater->state = NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE; /* Remember minimum value, and validate that encoder sends the value less than or equal to this. */ inflater->min_hd_table_bufsize_max = settings_max_dynamic_table_size; inflater->ctx.hd_table_bufsize_max = settings_max_dynamic_table_size; hd_context_shrink_table_size(&inflater->ctx, NULL); } return 0; } #define INDEX_RANGE_VALID(context, idx) \ ((idx) < (context)->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH) static size_t get_max_index(nghttp2_hd_context *context) { return context->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH; } nghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t idx) { assert(INDEX_RANGE_VALID(context, idx)); if (idx >= NGHTTP2_STATIC_TABLE_LENGTH) { return hd_ringbuf_get(&context->hd_table, idx - NGHTTP2_STATIC_TABLE_LENGTH) ->nv; } else { const nghttp2_hd_static_entry *ent = &static_table[idx]; nghttp2_hd_nv nv = {(nghttp2_rcbuf *)&ent->name, (nghttp2_rcbuf *)&ent->value, ent->token, NGHTTP2_NV_FLAG_NONE}; return nv; } } static const nghttp2_nv *nghttp2_hd_table_get2(nghttp2_hd_context *context, size_t idx) { assert(INDEX_RANGE_VALID(context, idx)); if (idx >= NGHTTP2_STATIC_TABLE_LENGTH) { return &hd_ringbuf_get(&context->hd_table, idx - NGHTTP2_STATIC_TABLE_LENGTH) ->cnv; } return &static_table[idx].cnv; } static int hd_deflate_decide_indexing(nghttp2_hd_deflater *deflater, const nghttp2_nv *nv, int32_t token) { if (token == NGHTTP2_TOKEN__PATH || token == NGHTTP2_TOKEN_AGE || token == NGHTTP2_TOKEN_CONTENT_LENGTH || token == NGHTTP2_TOKEN_ETAG || token == NGHTTP2_TOKEN_IF_MODIFIED_SINCE || token == NGHTTP2_TOKEN_IF_NONE_MATCH || token == NGHTTP2_TOKEN_LOCATION || token == NGHTTP2_TOKEN_SET_COOKIE || entry_room(nv->namelen, nv->valuelen) > deflater->ctx.hd_table_bufsize_max * 3 / 4) { return NGHTTP2_HD_WITHOUT_INDEXING; } return NGHTTP2_HD_WITH_INDEXING; } static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, const nghttp2_nv *nv) { int rv; search_result res; nghttp2_ssize idx; int indexing_mode; int32_t token; nghttp2_mem *mem; uint32_t hash = 0; DEBUGF("deflatehd: deflating %.*s: %.*s\n", (int)nv->namelen, nv->name, (int)nv->valuelen, nv->value); mem = deflater->ctx.mem; token = lookup_token(nv->name, nv->namelen); if (token == -1) { hash = name_hash(nv); } else if (token <= NGHTTP2_TOKEN_WWW_AUTHENTICATE) { hash = static_table[token].hash; } /* Don't index authorization header field since it may contain low entropy secret data (e.g., id/password). Also cookie header field with less than 20 bytes value is also never indexed. This is the same criteria used in Firefox codebase. */ indexing_mode = token == NGHTTP2_TOKEN_AUTHORIZATION || (token == NGHTTP2_TOKEN_COOKIE && nv->valuelen < 20) || (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) ? NGHTTP2_HD_NEVER_INDEXING : hd_deflate_decide_indexing(deflater, nv, token); res = search_hd_table(&deflater->ctx, nv, token, indexing_mode, &deflater->map, hash); idx = res.index; if (res.name_value_match) { DEBUGF("deflatehd: name/value match index=%td\n", idx); rv = emit_indexed_block(bufs, (size_t)idx); if (rv != 0) { return rv; } return 0; } if (res.index != -1) { DEBUGF("deflatehd: name match index=%td\n", res.index); } if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) { nghttp2_hd_nv hd_nv; if (idx != -1) { hd_nv.name = nghttp2_hd_table_get(&deflater->ctx, (size_t)idx).name; nghttp2_rcbuf_incref(hd_nv.name); } else { rv = nghttp2_rcbuf_new2(&hd_nv.name, nv->name, nv->namelen, mem); if (rv != 0) { return rv; } } rv = nghttp2_rcbuf_new2(&hd_nv.value, nv->value, nv->valuelen, mem); if (rv != 0) { nghttp2_rcbuf_decref(hd_nv.name); return rv; } hd_nv.token = token; hd_nv.flags = NGHTTP2_NV_FLAG_NONE; rv = add_hd_table_incremental(&deflater->ctx, &hd_nv, &deflater->map, hash); nghttp2_rcbuf_decref(hd_nv.value); nghttp2_rcbuf_decref(hd_nv.name); if (rv != 0) { return NGHTTP2_ERR_HEADER_COMP; } } if (idx == -1) { rv = emit_newname_block(bufs, nv, indexing_mode); } else { rv = emit_indname_block(bufs, (size_t)idx, nv, indexing_mode); } if (rv != 0) { return rv; } return 0; } int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, const nghttp2_nv *nv, size_t nvlen) { size_t i; int rv = 0; if (deflater->ctx.bad) { return NGHTTP2_ERR_HEADER_COMP; } if (deflater->notify_table_size_change) { size_t min_hd_table_bufsize_max; min_hd_table_bufsize_max = deflater->min_hd_table_bufsize_max; deflater->notify_table_size_change = 0; deflater->min_hd_table_bufsize_max = UINT32_MAX; if (deflater->ctx.hd_table_bufsize_max > min_hd_table_bufsize_max) { rv = emit_table_size(bufs, min_hd_table_bufsize_max); if (rv != 0) { goto fail; } } rv = emit_table_size(bufs, deflater->ctx.hd_table_bufsize_max); if (rv != 0) { goto fail; } } for (i = 0; i < nvlen; ++i) { rv = deflate_nv(deflater, bufs, &nv[i]); if (rv != 0) { goto fail; } } DEBUGF("deflatehd: all input name/value pairs were deflated\n"); return 0; fail: DEBUGF("deflatehd: error return %d\n", rv); deflater->ctx.bad = 1; return rv; } ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, size_t buflen, const nghttp2_nv *nv, size_t nvlen) { return (ssize_t)nghttp2_hd_deflate_hd2(deflater, buf, buflen, nv, nvlen); } nghttp2_ssize nghttp2_hd_deflate_hd2(nghttp2_hd_deflater *deflater, uint8_t *buf, size_t buflen, const nghttp2_nv *nv, size_t nvlen) { nghttp2_bufs bufs; int rv; nghttp2_mem *mem; mem = deflater->ctx.mem; rv = nghttp2_bufs_wrap_init(&bufs, buf, buflen, mem); if (rv != 0) { return rv; } rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, nv, nvlen); buflen = nghttp2_bufs_len(&bufs); nghttp2_bufs_wrap_free(&bufs); if (rv == NGHTTP2_ERR_BUFFER_ERROR) { return NGHTTP2_ERR_INSUFF_BUFSIZE; } if (rv != 0) { return rv; } return (nghttp2_ssize)buflen; } ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, const nghttp2_vec *vec, size_t veclen, const nghttp2_nv *nv, size_t nvlen) { return (ssize_t)nghttp2_hd_deflate_hd_vec2(deflater, vec, veclen, nv, nvlen); } nghttp2_ssize nghttp2_hd_deflate_hd_vec2(nghttp2_hd_deflater *deflater, const nghttp2_vec *vec, size_t veclen, const nghttp2_nv *nv, size_t nvlen) { nghttp2_bufs bufs; int rv; nghttp2_mem *mem; size_t buflen; mem = deflater->ctx.mem; rv = nghttp2_bufs_wrap_init2(&bufs, vec, veclen, mem); if (rv != 0) { return rv; } rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, nv, nvlen); buflen = nghttp2_bufs_len(&bufs); nghttp2_bufs_wrap_free(&bufs); if (rv == NGHTTP2_ERR_BUFFER_ERROR) { return NGHTTP2_ERR_INSUFF_BUFSIZE; } if (rv != 0) { return rv; } return (nghttp2_ssize)buflen; } size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, const nghttp2_nv *nva, size_t nvlen) { size_t n = 0; size_t i; (void)deflater; /* Possible Maximum Header Table Size Change. Encoding (1u << 31) - 1 using 4 bit prefix requires 6 bytes. We may emit this at most twice. */ n += 12; /* Use Literal Header Field without indexing - New Name, since it is most space consuming format. Also we choose the less one between non-huffman and huffman, so using literal byte count is sufficient for upper bound. Encoding (1u << 31) - 1 using 7 bit prefix requires 6 bytes. We need 2 of this for |nvlen| header fields. */ n += 6 * 2 * nvlen; for (i = 0; i < nvlen; ++i) { n += nva[i].namelen + nva[i].valuelen; } return n; } int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, size_t deflate_hd_table_bufsize_max) { return nghttp2_hd_deflate_new2(deflater_ptr, deflate_hd_table_bufsize_max, NULL); } int nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, size_t deflate_hd_table_bufsize_max, nghttp2_mem *mem) { int rv; nghttp2_hd_deflater *deflater; if (mem == NULL) { mem = nghttp2_mem_default(); } deflater = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_deflater)); if (deflater == NULL) { return NGHTTP2_ERR_NOMEM; } rv = nghttp2_hd_deflate_init2(deflater, deflate_hd_table_bufsize_max, mem); if (rv != 0) { nghttp2_mem_free(mem, deflater); return rv; } *deflater_ptr = deflater; return 0; } void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater) { nghttp2_mem *mem; mem = deflater->ctx.mem; nghttp2_hd_deflate_free(deflater); nghttp2_mem_free(mem, deflater); } static void hd_inflate_set_huffman_encoded(nghttp2_hd_inflater *inflater, const uint8_t *in) { inflater->huffman_encoded = (*in & (1 << 7)) != 0; } /* * Decodes the integer from the range [in, last). The result is * assigned to |inflater->left|. If the |inflater->left| is 0, then * it performs variable integer decoding from scratch. Otherwise, it * uses the |inflater->left| as the initial value and continues to * decode assuming that [in, last) begins with intermediary sequence. * * This function returns the number of bytes read if it succeeds, or * one of the following negative error codes: * * NGHTTP2_ERR_HEADER_COMP * Integer decoding failed */ static nghttp2_ssize hd_inflate_read_len(nghttp2_hd_inflater *inflater, int *rfin, const uint8_t *in, const uint8_t *last, size_t prefix, size_t maxlen) { nghttp2_ssize rv; uint32_t out; *rfin = 0; rv = decode_length(&out, &inflater->shift, rfin, (uint32_t)inflater->left, inflater->shift, in, last, prefix); if (rv == -1) { DEBUGF("inflatehd: integer decoding failed\n"); return NGHTTP2_ERR_HEADER_COMP; } if (out > maxlen) { DEBUGF("inflatehd: integer exceeded the maximum value %zu\n", maxlen); return NGHTTP2_ERR_HEADER_COMP; } inflater->left = out; DEBUGF("inflatehd: decoded integer is %u\n", out); return rv; } /* * Reads |inflater->left| bytes from the range [in, last) and performs * huffman decoding against them and pushes the result into the * |buffer|. * * This function returns the number of bytes read if it succeeds, or * one of the following negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory * NGHTTP2_ERR_HEADER_COMP * Huffman decoding failed */ static nghttp2_ssize hd_inflate_read_huff(nghttp2_hd_inflater *inflater, nghttp2_buf *buf, const uint8_t *in, const uint8_t *last) { nghttp2_ssize readlen; int fin = 0; if ((size_t)(last - in) >= inflater->left) { last = in + inflater->left; fin = 1; } readlen = nghttp2_hd_huff_decode(&inflater->huff_decode_ctx, buf, in, (size_t)(last - in), fin); if (readlen < 0) { DEBUGF("inflatehd: huffman decoding failed\n"); return readlen; } if (nghttp2_hd_huff_decode_failure_state(&inflater->huff_decode_ctx)) { DEBUGF("inflatehd: huffman decoding failed\n"); return NGHTTP2_ERR_HEADER_COMP; } inflater->left -= (size_t)readlen; return readlen; } /* * Reads |inflater->left| bytes from the range [in, last) and copies * them into the |buffer|. * * This function returns the number of bytes read if it succeeds, or * one of the following negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory * NGHTTP2_ERR_HEADER_COMP * Header decompression failed */ static nghttp2_ssize hd_inflate_read(nghttp2_hd_inflater *inflater, nghttp2_buf *buf, const uint8_t *in, const uint8_t *last) { size_t len = nghttp2_min_size((size_t)(last - in), inflater->left); buf->last = nghttp2_cpymem(buf->last, in, len); inflater->left -= len; return (nghttp2_ssize)len; } /* * Finalize indexed header representation reception. The referenced * header is always emitted, and |*nv_out| is filled with that value. */ static void hd_inflate_commit_indexed(nghttp2_hd_inflater *inflater, nghttp2_hd_nv *nv_out) { nghttp2_hd_nv nv = nghttp2_hd_table_get(&inflater->ctx, inflater->index); emit_header(nv_out, &nv); } /* * Finalize literal header representation - new name- reception. If * header is emitted, |*nv_out| is filled with that value and 0 is * returned. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory */ static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater, nghttp2_hd_nv *nv_out) { nghttp2_hd_nv nv; int rv; if (inflater->no_index) { nv.flags = NGHTTP2_NV_FLAG_NO_INDEX; } else { nv.flags = NGHTTP2_NV_FLAG_NONE; } nv.name = inflater->namercbuf; nv.value = inflater->valuercbuf; nv.token = lookup_token(inflater->namercbuf->base, inflater->namercbuf->len); if (inflater->index_required) { rv = add_hd_table_incremental(&inflater->ctx, &nv, NULL, 0); if (rv != 0) { return rv; } } emit_header(nv_out, &nv); inflater->nv_name_keep = nv.name; inflater->nv_value_keep = nv.value; inflater->namercbuf = NULL; inflater->valuercbuf = NULL; return 0; } /* * Finalize literal header representation - indexed name- * reception. If header is emitted, |*nv_out| is filled with that * value and 0 is returned. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory */ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, nghttp2_hd_nv *nv_out) { nghttp2_hd_nv nv; int rv; nv = nghttp2_hd_table_get(&inflater->ctx, inflater->index); if (inflater->no_index) { nv.flags = NGHTTP2_NV_FLAG_NO_INDEX; } else { nv.flags = NGHTTP2_NV_FLAG_NONE; } nghttp2_rcbuf_incref(nv.name); nv.value = inflater->valuercbuf; if (inflater->index_required) { rv = add_hd_table_incremental(&inflater->ctx, &nv, NULL, 0); if (rv != 0) { nghttp2_rcbuf_decref(nv.name); return NGHTTP2_ERR_NOMEM; } } emit_header(nv_out, &nv); inflater->nv_name_keep = nv.name; inflater->nv_value_keep = nv.value; inflater->valuercbuf = NULL; return 0; } ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags, uint8_t *in, size_t inlen, int in_final) { return nghttp2_hd_inflate_hd2(inflater, nv_out, inflate_flags, in, inlen, in_final); } ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags, const uint8_t *in, size_t inlen, int in_final) { return (nghttp2_ssize)nghttp2_hd_inflate_hd3(inflater, nv_out, inflate_flags, in, inlen, in_final); } nghttp2_ssize nghttp2_hd_inflate_hd3(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags, const uint8_t *in, size_t inlen, int in_final) { nghttp2_ssize rv; nghttp2_hd_nv hd_nv; rv = nghttp2_hd_inflate_hd_nv(inflater, &hd_nv, inflate_flags, in, inlen, in_final); if (rv < 0) { return rv; } if (*inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { nv_out->name = hd_nv.name->base; nv_out->namelen = hd_nv.name->len; nv_out->value = hd_nv.value->base; nv_out->valuelen = hd_nv.value->len; nv_out->flags = hd_nv.flags; } return rv; } nghttp2_ssize nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, nghttp2_hd_nv *nv_out, int *inflate_flags, const uint8_t *in, size_t inlen, int in_final) { nghttp2_ssize rv = 0; const uint8_t *first = in; const uint8_t *last = in + inlen; int rfin = 0; int busy = 0; nghttp2_mem *mem; mem = inflater->ctx.mem; if (inflater->ctx.bad) { return NGHTTP2_ERR_HEADER_COMP; } DEBUGF("inflatehd: start state=%d\n", inflater->state); hd_inflate_keep_free(inflater); *inflate_flags = NGHTTP2_HD_INFLATE_NONE; for (; in != last || busy;) { busy = 0; switch (inflater->state) { case NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE: if ((*in & 0xe0u) != 0x20u) { DEBUGF("inflatehd: header table size change was expected, but saw " "0x%02x as first byte", *in); rv = NGHTTP2_ERR_HEADER_COMP; goto fail; } /* fall through */ case NGHTTP2_HD_STATE_INFLATE_START: case NGHTTP2_HD_STATE_OPCODE: if ((*in & 0xe0u) == 0x20u) { DEBUGF("inflatehd: header table size change\n"); if (inflater->state == NGHTTP2_HD_STATE_OPCODE) { DEBUGF("inflatehd: header table size change must appear at the head " "of header block\n"); rv = NGHTTP2_ERR_HEADER_COMP; goto fail; } inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED; inflater->state = NGHTTP2_HD_STATE_READ_TABLE_SIZE; } else if (*in & 0x80u) { DEBUGF("inflatehd: indexed repr\n"); inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED; inflater->state = NGHTTP2_HD_STATE_READ_INDEX; } else { if (*in == 0x40u || *in == 0 || *in == 0x10u) { DEBUGF("inflatehd: literal header repr - new name\n"); inflater->opcode = NGHTTP2_HD_OPCODE_NEWNAME; inflater->state = NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN; } else { DEBUGF("inflatehd: literal header repr - indexed name\n"); inflater->opcode = NGHTTP2_HD_OPCODE_INDNAME; inflater->state = NGHTTP2_HD_STATE_READ_INDEX; } inflater->index_required = (*in & 0x40) != 0; inflater->no_index = (*in & 0xf0u) == 0x10u; DEBUGF("inflatehd: indexing required=%d, no_index=%d\n", inflater->index_required, inflater->no_index); if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { ++in; } } inflater->left = 0; inflater->shift = 0; break; case NGHTTP2_HD_STATE_READ_TABLE_SIZE: rfin = 0; rv = hd_inflate_read_len( inflater, &rfin, in, last, 5, nghttp2_min_size(inflater->min_hd_table_bufsize_max, inflater->settings_hd_table_bufsize_max)); if (rv < 0) { goto fail; } in += rv; if (!rfin) { goto almost_ok; } DEBUGF("inflatehd: table_size=%zu\n", inflater->left); inflater->min_hd_table_bufsize_max = UINT32_MAX; inflater->ctx.hd_table_bufsize_max = inflater->left; hd_context_shrink_table_size(&inflater->ctx, NULL); inflater->state = NGHTTP2_HD_STATE_INFLATE_START; break; case NGHTTP2_HD_STATE_READ_INDEX: { size_t prefixlen; if (inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) { prefixlen = 7; } else if (inflater->index_required) { prefixlen = 6; } else { prefixlen = 4; } rfin = 0; rv = hd_inflate_read_len(inflater, &rfin, in, last, prefixlen, get_max_index(&inflater->ctx)); if (rv < 0) { goto fail; } in += rv; if (!rfin) { goto almost_ok; } if (inflater->left == 0) { rv = NGHTTP2_ERR_HEADER_COMP; goto fail; } DEBUGF("inflatehd: index=%zu\n", inflater->left); if (inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) { inflater->index = inflater->left; --inflater->index; hd_inflate_commit_indexed(inflater, nv_out); inflater->state = NGHTTP2_HD_STATE_OPCODE; *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; return (nghttp2_ssize)(in - first); } else { inflater->index = inflater->left; --inflater->index; inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; } break; } case NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN: hd_inflate_set_huffman_encoded(inflater, in); inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN; inflater->left = 0; inflater->shift = 0; DEBUGF("inflatehd: huffman encoded=%d\n", inflater->huffman_encoded != 0); /* Fall through */ case NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN: rfin = 0; rv = hd_inflate_read_len(inflater, &rfin, in, last, 7, NGHTTP2_HD_MAX_NV); if (rv < 0) { goto fail; } in += rv; if (!rfin) { DEBUGF("inflatehd: integer not fully decoded. current=%zu\n", inflater->left); goto almost_ok; } if (inflater->huffman_encoded) { nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx); inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF; rv = nghttp2_rcbuf_new(&inflater->namercbuf, inflater->left * 2 + 1, mem); } else { inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAME; rv = nghttp2_rcbuf_new(&inflater->namercbuf, inflater->left + 1, mem); } if (rv != 0) { goto fail; } nghttp2_buf_wrap_init(&inflater->namebuf, inflater->namercbuf->base, inflater->namercbuf->len); break; case NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF: rv = hd_inflate_read_huff(inflater, &inflater->namebuf, in, last); if (rv < 0) { goto fail; } in += rv; DEBUGF("inflatehd: %td bytes read\n", rv); if (inflater->left) { DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); goto almost_ok; } *inflater->namebuf.last = '\0'; inflater->namercbuf->len = nghttp2_buf_len(&inflater->namebuf); inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; break; case NGHTTP2_HD_STATE_NEWNAME_READ_NAME: rv = hd_inflate_read(inflater, &inflater->namebuf, in, last); if (rv < 0) { goto fail; } in += rv; DEBUGF("inflatehd: %td bytes read\n", rv); if (inflater->left) { DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); goto almost_ok; } *inflater->namebuf.last = '\0'; inflater->namercbuf->len = nghttp2_buf_len(&inflater->namebuf); inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; break; case NGHTTP2_HD_STATE_CHECK_VALUELEN: hd_inflate_set_huffman_encoded(inflater, in); inflater->state = NGHTTP2_HD_STATE_READ_VALUELEN; inflater->left = 0; inflater->shift = 0; DEBUGF("inflatehd: huffman encoded=%d\n", inflater->huffman_encoded != 0); /* Fall through */ case NGHTTP2_HD_STATE_READ_VALUELEN: rfin = 0; rv = hd_inflate_read_len(inflater, &rfin, in, last, 7, NGHTTP2_HD_MAX_NV); if (rv < 0) { goto fail; } in += rv; if (!rfin) { goto almost_ok; } DEBUGF("inflatehd: valuelen=%zu\n", inflater->left); if (inflater->huffman_encoded) { nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx); inflater->state = NGHTTP2_HD_STATE_READ_VALUEHUFF; rv = nghttp2_rcbuf_new(&inflater->valuercbuf, inflater->left * 2 + 1, mem); } else { inflater->state = NGHTTP2_HD_STATE_READ_VALUE; rv = nghttp2_rcbuf_new(&inflater->valuercbuf, inflater->left + 1, mem); } if (rv != 0) { goto fail; } nghttp2_buf_wrap_init(&inflater->valuebuf, inflater->valuercbuf->base, inflater->valuercbuf->len); busy = 1; break; case NGHTTP2_HD_STATE_READ_VALUEHUFF: rv = hd_inflate_read_huff(inflater, &inflater->valuebuf, in, last); if (rv < 0) { goto fail; } in += rv; DEBUGF("inflatehd: %td bytes read\n", rv); if (inflater->left) { DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); goto almost_ok; } *inflater->valuebuf.last = '\0'; inflater->valuercbuf->len = nghttp2_buf_len(&inflater->valuebuf); if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { rv = hd_inflate_commit_newname(inflater, nv_out); } else { rv = hd_inflate_commit_indname(inflater, nv_out); } if (rv != 0) { goto fail; } inflater->state = NGHTTP2_HD_STATE_OPCODE; *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; return (nghttp2_ssize)(in - first); case NGHTTP2_HD_STATE_READ_VALUE: rv = hd_inflate_read(inflater, &inflater->valuebuf, in, last); if (rv < 0) { DEBUGF("inflatehd: value read failure %td: %s\n", rv, nghttp2_strerror((int)rv)); goto fail; } in += rv; DEBUGF("inflatehd: %td bytes read\n", rv); if (inflater->left) { DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); goto almost_ok; } *inflater->valuebuf.last = '\0'; inflater->valuercbuf->len = nghttp2_buf_len(&inflater->valuebuf); if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { rv = hd_inflate_commit_newname(inflater, nv_out); } else { rv = hd_inflate_commit_indname(inflater, nv_out); } if (rv != 0) { goto fail; } inflater->state = NGHTTP2_HD_STATE_OPCODE; *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; return (nghttp2_ssize)(in - first); } } assert(in == last); DEBUGF("inflatehd: all input bytes were processed\n"); if (in_final) { DEBUGF("inflatehd: in_final set\n"); if (inflater->state != NGHTTP2_HD_STATE_OPCODE && inflater->state != NGHTTP2_HD_STATE_INFLATE_START) { DEBUGF("inflatehd: unacceptable state=%d\n", inflater->state); rv = NGHTTP2_ERR_HEADER_COMP; goto fail; } *inflate_flags |= NGHTTP2_HD_INFLATE_FINAL; } return (nghttp2_ssize)(in - first); almost_ok: if (in_final) { DEBUGF("inflatehd: input ended prematurely\n"); rv = NGHTTP2_ERR_HEADER_COMP; goto fail; } return (nghttp2_ssize)(in - first); fail: DEBUGF("inflatehd: error return %td\n", rv); inflater->ctx.bad = 1; return rv; } int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater) { hd_inflate_keep_free(inflater); inflater->state = NGHTTP2_HD_STATE_INFLATE_START; return 0; } int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr) { return nghttp2_hd_inflate_new2(inflater_ptr, NULL); } int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr, nghttp2_mem *mem) { int rv; nghttp2_hd_inflater *inflater; if (mem == NULL) { mem = nghttp2_mem_default(); } inflater = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_inflater)); if (inflater == NULL) { return NGHTTP2_ERR_NOMEM; } rv = nghttp2_hd_inflate_init(inflater, mem); if (rv != 0) { nghttp2_mem_free(mem, inflater); return rv; } *inflater_ptr = inflater; return 0; } void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater) { nghttp2_mem *mem; mem = inflater->ctx.mem; nghttp2_hd_inflate_free(inflater); nghttp2_mem_free(mem, inflater); } int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t idx, nghttp2_nv *nv, int indexing_mode) { return emit_indname_block(bufs, idx, nv, indexing_mode); } int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv, int indexing_mode) { return emit_newname_block(bufs, nv, indexing_mode); } int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size) { return emit_table_size(bufs, table_size); } nghttp2_ssize nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *fin, uint32_t initial, size_t shift, uint8_t *in, uint8_t *last, size_t prefix) { return decode_length(res, shift_ptr, fin, initial, shift, in, last, prefix); } static const nghttp2_nv *hd_get_table_entry(nghttp2_hd_context *context, size_t idx) { if (idx == 0) { return NULL; } --idx; if (!INDEX_RANGE_VALID(context, idx)) { return NULL; } return nghttp2_hd_table_get2(context, idx); } size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater) { return get_max_index(&deflater->ctx); } const nghttp2_nv * nghttp2_hd_deflate_get_table_entry(nghttp2_hd_deflater *deflater, size_t idx) { return hd_get_table_entry(&deflater->ctx, idx); } size_t nghttp2_hd_deflate_get_dynamic_table_size(nghttp2_hd_deflater *deflater) { return deflater->ctx.hd_table_bufsize; } size_t nghttp2_hd_deflate_get_max_dynamic_table_size(nghttp2_hd_deflater *deflater) { return deflater->ctx.hd_table_bufsize_max; } size_t nghttp2_hd_inflate_get_num_table_entries(nghttp2_hd_inflater *inflater) { return get_max_index(&inflater->ctx); } const nghttp2_nv * nghttp2_hd_inflate_get_table_entry(nghttp2_hd_inflater *inflater, size_t idx) { return hd_get_table_entry(&inflater->ctx, idx); } size_t nghttp2_hd_inflate_get_dynamic_table_size(nghttp2_hd_inflater *inflater) { return inflater->ctx.hd_table_bufsize; } size_t nghttp2_hd_inflate_get_max_dynamic_table_size(nghttp2_hd_inflater *inflater) { return inflater->ctx.hd_table_bufsize_max; } nghttp2-1.68.0/lib/PaxHeaders/sfparse.h0000644000000000000000000000013115077107270014641 xustar0029 mtime=1761382072.98244419 30 atime=1761382104.962315898 30 ctime=1761382107.948303711 nghttp2-1.68.0/lib/sfparse.h0000644000175100017510000003334615077107270015243 0ustar00runnerrunner/* * sfparse * * Copyright (c) 2023 sfparse contributors * Copyright (c) 2019 nghttp3 contributors * Copyright (c) 2015 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SFPARSE_H #define SFPARSE_H /* Define WIN32 when build target is Win32 API (borrowed from libcurl) */ #if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) # define WIN32 #endif /* (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) */ #ifdef __cplusplus extern "C" { #endif /* defined(__cplusplus) */ #if defined(_MSC_VER) && (_MSC_VER < 1800) /* MSVC < 2013 does not have inttypes.h because it is not C99 compliant. See compiler macros and version number in https://sourceforge.net/p/predef/wiki/Compilers/ */ # include #else /* !(defined(_MSC_VER) && (_MSC_VER < 1800)) */ # include #endif /* !(defined(_MSC_VER) && (_MSC_VER < 1800)) */ #include #include /** * @enum * * :type:`sfparse_type` defines value type. */ typedef enum sfparse_type { /** * :enum:`SFPARSE_TYPE_BOOLEAN` indicates boolean type. */ SFPARSE_TYPE_BOOLEAN, /** * :enum:`SFPARSE_TYPE_INTEGER` indicates integer type. */ SFPARSE_TYPE_INTEGER, /** * :enum:`SFPARSE_TYPE_DECIMAL` indicates decimal type. */ SFPARSE_TYPE_DECIMAL, /** * :enum:`SFPARSE_TYPE_STRING` indicates string type. */ SFPARSE_TYPE_STRING, /** * :enum:`SFPARSE_TYPE_TOKEN` indicates token type. */ SFPARSE_TYPE_TOKEN, /** * :enum:`SFPARSE_TYPE_BYTESEQ` indicates byte sequence type. */ SFPARSE_TYPE_BYTESEQ, /** * :enum:`SFPARSE_TYPE_INNER_LIST` indicates inner list type. */ SFPARSE_TYPE_INNER_LIST, /** * :enum:`SFPARSE_TYPE_DATE` indicates date type. */ SFPARSE_TYPE_DATE, /** * :enum:`SFPARSE_TYPE_DISPSTRING` indicates display string type. */ SFPARSE_TYPE_DISPSTRING } sfparse_type; /** * @macro * * :macro:`SFPARSE_ERR_PARSE` indicates fatal parse error has * occurred, and it is not possible to continue the processing. */ #define SFPARSE_ERR_PARSE -1 /** * @macro * * :macro:`SFPARSE_ERR_EOF` indicates that there is nothing left to * read. The context of this error varies depending on the function * that returns this error code. */ #define SFPARSE_ERR_EOF -2 /** * @struct * * :type:`sfparse_vec` stores sequence of bytes. */ typedef struct sfparse_vec { /** * :member:`base` points to the beginning of the sequence of bytes. */ uint8_t *base; /** * :member:`len` is the number of bytes contained in this sequence. */ size_t len; } sfparse_vec; /** * @macro * * :macro:`SFPARSE_VALUE_FLAG_NONE` indicates no flag set. */ #define SFPARSE_VALUE_FLAG_NONE 0x0u /** * @macro * * :macro:`SFPARSE_VALUE_FLAG_ESCAPED_STRING` indicates that a string * contains escaped character(s). */ #define SFPARSE_VALUE_FLAG_ESCAPED_STRING 0x1u /** * @struct * * :type:`sfparse_decimal` contains decimal value. */ typedef struct sfparse_decimal { /** * :member:`numer` contains numerator of the decimal value. */ int64_t numer; /** * :member:`denom` contains denominator of the decimal value. */ int64_t denom; } sfparse_decimal; /** * @struct * * :type:`sfparse_value` stores a Structured Field item. For Inner * List, only type is set to * :enum:`sfparse_type.SFPARSE_TYPE_INNER_LIST`. In order to read the * items contained in an inner list, call `sfparse_parser_inner_list`. */ typedef struct sfparse_value { /** * :member:`type` is the type of the value contained in this * particular object. */ sfparse_type type; /** * :member:`flags` is bitwise OR of one or more of * :macro:`SFPARSE_VALUE_FLAG_* `. */ uint32_t flags; /** * @anonunion_start * * @sfparse_value_value */ union { /** * :member:`boolean` contains boolean value if :member:`type` == * :enum:`sfparse_type.SFPARSE_TYPE_BOOLEAN`. 1 indicates true, * and 0 indicates false. */ int boolean; /** * :member:`integer` contains integer value if :member:`type` is * either :enum:`sfparse_type.SFPARSE_TYPE_INTEGER` or * :enum:`sfparse_type.SFPARSE_TYPE_DATE`. */ int64_t integer; /** * :member:`decimal` contains decimal value if :member:`type` == * :enum:`sfparse_type.SFPARSE_TYPE_DECIMAL`. */ sfparse_decimal decimal; /** * :member:`vec` contains sequence of bytes if :member:`type` is * either :enum:`sfparse_type.SFPARSE_TYPE_STRING`, * :enum:`sfparse_type.SFPARSE_TYPE_TOKEN`, * :enum:`sfparse_type.SFPARSE_TYPE_BYTESEQ`, or * :enum:`sfparse_type.SFPARSE_TYPE_DISPSTRING`. * * For :enum:`sfparse_type.SFPARSE_TYPE_STRING`, this field * contains one or more escaped characters if :member:`flags` has * :macro:`SFPARSE_VALUE_FLAG_ESCAPED_STRING` set. To unescape * the string, use `sfparse_unescape`. * * For :enum:`sfparse_type.SFPARSE_TYPE_BYTESEQ`, this field * contains base64 encoded string. To decode this byte string, * use `sfparse_base64decode`. * * For :enum:`sfparse_type.SFPARSE_TYPE_DISPSTRING`, this field * may contain percent-encoded UTF-8 byte sequences. To decode * it, use `sfparse_pctdecode`. * * If :member:`vec.len ` == 0, :member:`vec.base * ` is guaranteed to be NULL. */ sfparse_vec vec; /** * @anonunion_end */ }; } sfparse_value; /** * @struct * * :type:`sfparse_parser` is the Structured Field Values parser. Use * `sfparse_parser_init` to initialize it. */ typedef struct sfparse_parser { /* all fields are private */ const uint8_t *pos; const uint8_t *end; uint32_t state; } sfparse_parser; /** * @function * * `sfparse_parser_init` initializes |sfp| with the given data encoded * in Structured Field Values pointed by |data| of length |datalen|. */ void sfparse_parser_init(sfparse_parser *sfp, const uint8_t *data, size_t datalen); /** * @function * * `sfparse_parser_param` reads a parameter. If this function returns * 0, it stores parameter key and value in |dest_key| and |dest_value| * respectively, if they are not NULL. * * This function does no effort to find duplicated keys. Same key may * be reported more than once. * * Caller should keep calling this function until it returns negative * error code. If it returns :macro:`SFPARSE_ERR_EOF`, all parameters * have read, and caller can continue to read rest of the values. If * it returns :macro:`SFPARSE_ERR_PARSE`, it encountered fatal error * while parsing field value. */ int sfparse_parser_param(sfparse_parser *sfp, sfparse_vec *dest_key, sfparse_value *dest_value); /** * @function * * `sfparse_parser_dict` reads the next dictionary key and value pair. * If this function returns 0, it stores the key and value in * |dest_key| and |dest_value| respectively, if they are not NULL. * * Caller can optionally read parameters attached to the pair by * calling `sfparse_parser_param`. * * This function does no effort to find duplicated keys. Same key may * be reported more than once. * * Caller should keep calling this function until it returns negative * error code. If it returns :macro:`SFPARSE_ERR_EOF`, all key and * value pairs have been read, and there is nothing left to read. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :macro:`SFPARSE_ERR_EOF` * All values in the dictionary have read. * :macro:`SFPARSE_ERR_PARSE` * It encountered fatal error while parsing field value. */ int sfparse_parser_dict(sfparse_parser *sfp, sfparse_vec *dest_key, sfparse_value *dest_value); /** * @function * * `sfparse_parser_list` reads the next list item. If this function * returns 0, it stores the item in |dest| if it is not NULL. * * Caller can optionally read parameters attached to the item by * calling `sfparse_parser_param`. * * Caller should keep calling this function until it returns negative * error code. If it returns :macro:`SFPARSE_ERR_EOF`, all values in * the list have been read, and there is nothing left to read. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :macro:`SFPARSE_ERR_EOF` * All values in the list have read. * :macro:`SFPARSE_ERR_PARSE` * It encountered fatal error while parsing field value. */ int sfparse_parser_list(sfparse_parser *sfp, sfparse_value *dest); /** * @function * * `sfparse_parser_item` reads a single item. If this function * returns 0, it stores the item in |dest| if it is not NULL. * * This function is only used for the field value that consists of a * single item. * * Caller can optionally read parameters attached to the item by * calling `sfparse_parser_param`. * * Caller should call this function again to make sure that there is * nothing left to read. If this 2nd function call returns * :macro:`SFPARSE_ERR_EOF`, all data have been processed * successfully. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :macro:`SFPARSE_ERR_EOF` * There is nothing left to read. * :macro:`SFPARSE_ERR_PARSE` * It encountered fatal error while parsing field value. */ int sfparse_parser_item(sfparse_parser *sfp, sfparse_value *dest); /** * @function * * `sfparse_parser_inner_list` reads the next inner list item. If * this function returns 0, it stores the item in |dest| if it is not * NULL. * * Caller can optionally read parameters attached to the item by * calling `sfparse_parser_param`. * * Caller should keep calling this function until it returns negative * error code. If it returns :macro:`SFPARSE_ERR_EOF`, all values in * this inner list have been read, and caller can optionally read * parameters attached to this inner list by calling * `sfparse_parser_param`. Then caller can continue to read rest of * the values. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :macro:`SFPARSE_ERR_EOF` * All values in the inner list have read. * :macro:`SFPARSE_ERR_PARSE` * It encountered fatal error while parsing field value. */ int sfparse_parser_inner_list(sfparse_parser *sfp, sfparse_value *dest); /** * @function * * `sfparse_unescape` copies |src| to |dest| by removing escapes * (``\``). |src| should be the pointer to * :member:`sfparse_value.vec` of type * :enum:`sfparse_type.SFPARSE_TYPE_STRING` produced by either * `sfparse_parser_dict`, `sfparse_parser_list`, * `sfparse_parser_inner_list`, `sfparse_parser_item`, or * `sfparse_parser_param`, otherwise the behavior is undefined. * * :member:`dest->base ` must point to the buffer * that has sufficient space to store the unescaped string. The * memory areas pointed by :member:`dest->base ` and * :member:`src->base ` must not overlap. * * This function sets the length of unescaped string to * :member:`dest->len `. */ void sfparse_unescape(sfparse_vec *dest, const sfparse_vec *src); /** * @function * * `sfparse_base64decode` decodes Base64 encoded string |src| and * writes the result into |dest|. |src| should be the pointer to * :member:`sfparse_value.vec` of type * :enum:`sfparse_type.SFPARSE_TYPE_BYTESEQ` produced by either * `sfparse_parser_dict`, `sfparse_parser_list`, * `sfparse_parser_inner_list`, `sfparse_parser_item`, or * `sfparse_parser_param`, otherwise the behavior is undefined. * * :member:`dest->base ` must point to the buffer * that has sufficient space to store the decoded byte string. * * This function sets the length of decoded byte string to * :member:`dest->len `. */ void sfparse_base64decode(sfparse_vec *dest, const sfparse_vec *src); /** * @function * * `sfparse_pctdecode` decodes percent-encoded string |src| and writes * the result into |dest|. |src| should be the pointer to * :member:`sfparse_value.vec` of type * :enum:`sfparse_type.SFPARSE_TYPE_DISPSTRING` produced by either * `sfparse_parser_dict`, `sfparse_parser_list`, * `sfparse_parser_inner_list`, `sfparse_parser_item`, or * `sfparse_parser_param`, otherwise the behavior is undefined. * * :member:`dest->base ` must point to the buffer * that has sufficient space to store the decoded byte string. The * memory areas pointed by :member:`dest->base ` and * :member:`src->base ` must not overlap. * * This function sets the length of decoded byte string to * :member:`dest->len `. */ void sfparse_pctdecode(sfparse_vec *dest, const sfparse_vec *src); #ifdef __cplusplus } #endif /* defined(__cplusplus) */ #endif /* !defined(SFPARSE_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_helper.h0000644000000000000000000000013215077107270016124 xustar0030 mtime=1761382072.979444204 30 atime=1761382104.939315999 30 ctime=1761382107.925303778 nghttp2-1.68.0/lib/nghttp2_helper.h0000644000175100017510000001211715077107270016516 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_HELPER_H #define NGHTTP2_HELPER_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include #include #include #include "nghttp2_mem.h" #define nghttp2_max_def(SUFFIX, T) \ static inline T nghttp2_max_##SUFFIX(T a, T b) { return a < b ? b : a; } nghttp2_max_def(int8, int8_t) nghttp2_max_def(int16, int16_t) nghttp2_max_def(int32, int32_t) nghttp2_max_def(int64, int64_t) nghttp2_max_def(uint8, uint8_t) nghttp2_max_def(uint16, uint16_t) nghttp2_max_def(uint32, uint32_t) nghttp2_max_def(uint64, uint64_t) nghttp2_max_def(size, size_t) #define nghttp2_min_def(SUFFIX, T) \ static inline T nghttp2_min_##SUFFIX(T a, T b) { return a < b ? a : b; } nghttp2_min_def(int8, int8_t) nghttp2_min_def(int16, int16_t) nghttp2_min_def(int32, int32_t) nghttp2_min_def(int64, int64_t) nghttp2_min_def(uint8, uint8_t) nghttp2_min_def(uint16, uint16_t) nghttp2_min_def(uint32, uint32_t) nghttp2_min_def(uint64, uint64_t) nghttp2_min_def(size, size_t) #define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0) #define nghttp2_struct_of(ptr, type, member) \ ((type *)(void *)((char *)(ptr) - offsetof(type, member))) /* * Copies 2 byte unsigned integer |n| in host byte order to |buf| in * network byte order. */ void nghttp2_put_uint16be(uint8_t *buf, uint16_t n); /* * Copies 4 byte unsigned integer |n| in host byte order to |buf| in * network byte order. */ void nghttp2_put_uint32be(uint8_t *buf, uint32_t n); /* * Retrieves 2 byte unsigned integer stored in |data| in network byte * order and returns it in host byte order. */ uint16_t nghttp2_get_uint16(const uint8_t *data); /* * Retrieves 4 byte unsigned integer stored in |data| in network byte * order and returns it in host byte order. */ uint32_t nghttp2_get_uint32(const uint8_t *data); void nghttp2_downcase(uint8_t *s, size_t len); /* * Adjusts |*local_window_size_ptr|, |*recv_window_size_ptr|, * |*recv_reduction_ptr| with |*delta_ptr| which is the * WINDOW_UPDATE's window_size_increment sent from local side. If * |delta| is strictly larger than |*recv_window_size_ptr|, * |*local_window_size_ptr| is increased by delta - * *recv_window_size_ptr. If |delta| is negative, * |*local_window_size_ptr| is decreased by delta. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_FLOW_CONTROL * local_window_size overflow or gets negative. */ int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr, int32_t *recv_window_size_ptr, int32_t *recv_reduction_ptr, int32_t *delta_ptr); /* * This function works like nghttp2_adjust_local_window_size(). The * difference is that this function assumes *delta_ptr >= 0, and * *recv_window_size_ptr is not decreased by *delta_ptr. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_FLOW_CONTROL * local_window_size overflow or gets negative. */ int nghttp2_increase_local_window_size(int32_t *local_window_size_ptr, int32_t *recv_window_size_ptr, int32_t *recv_reduction_ptr, int32_t *delta_ptr); /* * Returns non-zero if the function decided that WINDOW_UPDATE should * be sent. */ int nghttp2_should_send_window_update(int32_t local_window_size, int32_t recv_window_size); /* * Copies the buffer |src| of length |len| to the destination pointed * by the |dest|, assuming that the |dest| is at lest |len| bytes long * . Returns dest + len. */ uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len); #endif /* !defined(NGHTTP2_HELPER_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_http.h0000644000000000000000000000013215077107270015624 xustar0030 mtime=1761382072.979444204 30 atime=1761382104.954315933 30 ctime=1761382107.941303731 nghttp2-1.68.0/lib/nghttp2_http.h0000644000175100017510000000727415077107270016226 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_HTTP_H #define NGHTTP2_HTTP_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include #include "nghttp2_session.h" #include "nghttp2_stream.h" /* * This function is called when HTTP header field |nv| in |frame| is * received for |stream|. This function will validate |nv| against * the current state of stream. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_HTTP_HEADER * Invalid HTTP header field was received. * NGHTTP2_ERR_IGN_HTTP_HEADER * Invalid HTTP header field was received but it can be treated as * if it was not received because of compatibility reasons. */ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, nghttp2_frame *frame, nghttp2_hd_nv *nv, int trailer); /* * This function is called when request header is received. This * function performs validation and returns 0 if it succeeds, or -1. */ int nghttp2_http_on_request_headers(nghttp2_stream *stream, nghttp2_frame *frame); /* * This function is called when response header is received. This * function performs validation and returns 0 if it succeeds, or -1. */ int nghttp2_http_on_response_headers(nghttp2_stream *stream); /* * This function is called trailer header (for both request and * response) is received. This function performs validation and * returns 0 if it succeeds, or -1. */ int nghttp2_http_on_trailer_headers(nghttp2_stream *stream, nghttp2_frame *frame); /* * This function is called when END_STREAM flag is seen in incoming * frame. This function performs validation and returns 0 if it * succeeds, or -1. */ int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream); /* * This function is called when chunk of data is received. This * function performs validation and returns 0 if it succeeds, or -1. */ int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n); /* * This function inspects header field in |frame| and records its * method in stream->http_flags. If frame->hd.type is neither * NGHTTP2_HEADERS nor NGHTTP2_PUSH_PROMISE, this function does * nothing. */ void nghttp2_http_record_request_method(nghttp2_stream *stream, nghttp2_frame *frame); int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value, size_t valuelen); #endif /* !defined(NGHTTP2_HTTP_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_session.h0000644000000000000000000000013215077107270016330 xustar0030 mtime=1761382072.981444194 30 atime=1761382104.938316004 30 ctime=1761382107.924303781 nghttp2-1.68.0/lib/nghttp2_session.h0000644000175100017510000010045015077107270016720 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_SESSION_H #define NGHTTP2_SESSION_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include #include "nghttp2_map.h" #include "nghttp2_frame.h" #include "nghttp2_hd.h" #include "nghttp2_stream.h" #include "nghttp2_outbound_item.h" #include "nghttp2_int.h" #include "nghttp2_buf.h" #include "nghttp2_callbacks.h" #include "nghttp2_mem.h" #include "nghttp2_ratelim.h" /* The global variable for tests where we want to disable strict preface handling. */ extern int nghttp2_enable_strict_preface; extern nghttp2_stream nghttp2_stream_root; /* * Option flags. */ typedef enum { NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE = 1 << 0, NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC = 1 << 1, NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2, NGHTTP2_OPTMASK_NO_AUTO_PING_ACK = 1 << 3, NGHTTP2_OPTMASK_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION = 1 << 6, } nghttp2_optmask; /* * bitmask for built-in type to enable the default handling for that * type of the frame. */ typedef enum { NGHTTP2_TYPEMASK_NONE = 0, NGHTTP2_TYPEMASK_ALTSVC = 1 << 0, NGHTTP2_TYPEMASK_ORIGIN = 1 << 1, NGHTTP2_TYPEMASK_PRIORITY_UPDATE = 1 << 2 } nghttp2_typemask; typedef enum { NGHTTP2_OB_POP_ITEM, NGHTTP2_OB_SEND_DATA, NGHTTP2_OB_SEND_NO_COPY, NGHTTP2_OB_SEND_CLIENT_MAGIC } nghttp2_outbound_state; typedef struct { nghttp2_outbound_item *item; nghttp2_bufs framebufs; nghttp2_outbound_state state; } nghttp2_active_outbound_item; /* Buffer length for inbound raw byte stream used in nghttp2_session_recv(). */ #define NGHTTP2_INBOUND_BUFFER_LENGTH 16384 /* The default maximum number of incoming reserved streams */ #define NGHTTP2_MAX_INCOMING_RESERVED_STREAMS 200 /* The maximum number of items in outbound queue, which is considered as flooding caused by peer. All frames are not considered here. We only consider PING + ACK and SETTINGS + ACK. This is because they both are response to the frame initiated by peer and peer can send as many of them as they want. If peer does not read network, response frames are stacked up, which leads to memory exhaustion. The value selected here is arbitrary, but safe value and if we have these frames in this number, it is considered suspicious. */ #define NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM 1000 /* The default value of maximum number of concurrent streams. */ #define NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS 0xffffffffu /* The default values for stream reset rate limiter. */ #define NGHTTP2_DEFAULT_STREAM_RESET_BURST 1000 #define NGHTTP2_DEFAULT_STREAM_RESET_RATE 33 /* The default values for glitch rate limiter. */ #define NGHTTP2_DEFAULT_GLITCH_BURST 1000 #define NGHTTP2_DEFAULT_GLITCH_RATE 33 /* The default max number of CONTINUATION frames following an incoming HEADER frame. */ #define NGHTTP2_DEFAULT_MAX_CONTINUATIONS 8 /* Internal state when receiving incoming frame */ typedef enum { /* Receiving frame header */ NGHTTP2_IB_READ_CLIENT_MAGIC, NGHTTP2_IB_READ_FIRST_SETTINGS, NGHTTP2_IB_READ_HEAD, NGHTTP2_IB_READ_NBYTE, NGHTTP2_IB_READ_HEADER_BLOCK, NGHTTP2_IB_IGN_HEADER_BLOCK, NGHTTP2_IB_IGN_PAYLOAD, NGHTTP2_IB_FRAME_SIZE_ERROR, NGHTTP2_IB_READ_SETTINGS, NGHTTP2_IB_READ_GOAWAY_DEBUG, NGHTTP2_IB_EXPECT_CONTINUATION, NGHTTP2_IB_IGN_CONTINUATION, NGHTTP2_IB_READ_PAD_DATA, NGHTTP2_IB_READ_DATA, NGHTTP2_IB_IGN_DATA, NGHTTP2_IB_IGN_ALL, NGHTTP2_IB_READ_ALTSVC_PAYLOAD, NGHTTP2_IB_READ_ORIGIN_PAYLOAD, NGHTTP2_IB_READ_EXTENSION_PAYLOAD } nghttp2_inbound_state; typedef struct { nghttp2_frame frame; /* Storage for extension frame payload. frame->ext.payload points to this structure to avoid frequent memory allocation. */ nghttp2_ext_frame_payload ext_frame_payload; /* The received SETTINGS entry. For the standard settings entries, we only keep the last seen value. For SETTINGS_HEADER_TABLE_SIZE, we also keep minimum value in the last index. */ nghttp2_settings_entry *iv; /* buffer pointers to small buffer, raw_sbuf */ nghttp2_buf sbuf; /* buffer pointers to large buffer, raw_lbuf */ nghttp2_buf lbuf; /* Large buffer, malloced on demand */ uint8_t *raw_lbuf; /* The number of entry filled in |iv| */ size_t niv; /* The number of entries |iv| can store. */ size_t max_niv; /* How many bytes we still need to receive for current frame */ size_t payloadleft; /* padding length for the current frame */ size_t padlen; nghttp2_inbound_state state; /* Small fixed sized buffer. */ uint8_t raw_sbuf[32]; } nghttp2_inbound_frame; typedef struct { uint32_t header_table_size; uint32_t enable_push; uint32_t max_concurrent_streams; uint32_t initial_window_size; uint32_t max_frame_size; uint32_t max_header_list_size; uint32_t enable_connect_protocol; uint32_t no_rfc7540_priorities; } nghttp2_settings_storage; typedef enum { NGHTTP2_GOAWAY_NONE = 0, /* Flag means that connection should be terminated after sending GOAWAY. */ NGHTTP2_GOAWAY_TERM_ON_SEND = 0x1, /* Flag means GOAWAY to terminate session has been sent */ NGHTTP2_GOAWAY_TERM_SENT = 0x2, /* Flag means GOAWAY was sent */ NGHTTP2_GOAWAY_SENT = 0x4, /* Flag means GOAWAY was received */ NGHTTP2_GOAWAY_RECV = 0x8, /* Flag means GOAWAY has been submitted at least once */ NGHTTP2_GOAWAY_SUBMITTED = 0x10 } nghttp2_goaway_flag; /* nghttp2_inflight_settings stores the SETTINGS entries which local endpoint has sent to the remote endpoint, and has not received ACK yet. */ struct nghttp2_inflight_settings { struct nghttp2_inflight_settings *next; nghttp2_settings_entry *iv; size_t niv; }; typedef struct nghttp2_inflight_settings nghttp2_inflight_settings; struct nghttp2_session { nghttp2_map /* */ streams; /* Queue for outbound urgent frames (PING and SETTINGS) */ nghttp2_outbound_queue ob_urgent; /* Queue for non-DATA frames */ nghttp2_outbound_queue ob_reg; /* Queue for outbound stream-creating HEADERS (request or push response) frame, which are subject to SETTINGS_MAX_CONCURRENT_STREAMS limit. */ nghttp2_outbound_queue ob_syn; /* Queues for DATA frames which is used when SETTINGS_NO_RFC7540_PRIORITIES is enabled. This implements RFC 9218 extensible prioritization scheme. */ struct { nghttp2_pq ob_data; } sched[NGHTTP2_EXTPRI_URGENCY_LEVELS]; nghttp2_active_outbound_item aob; nghttp2_inbound_frame iframe; nghttp2_hd_deflater hd_deflater; nghttp2_hd_inflater hd_inflater; nghttp2_session_callbacks callbacks; /* Memory allocator */ nghttp2_mem mem; void *user_data; /* Queue of In-flight SETTINGS values. SETTINGS bearing ACK is not considered as in-flight. */ nghttp2_inflight_settings *inflight_settings_head; /* Stream reset rate limiter. If receiving excessive amount of stream resets, GOAWAY will be sent. */ nghttp2_ratelim stream_reset_ratelim; /* Rate limiter for all kinds of glitches. */ nghttp2_ratelim glitch_ratelim; /* Sequential number across all streams to process streams in FIFO. */ uint64_t stream_seq; /* The number of outgoing streams. This will be capped by remote_settings.max_concurrent_streams. */ size_t num_outgoing_streams; /* The number of incoming streams. This will be capped by local_settings.max_concurrent_streams. */ size_t num_incoming_streams; /* The number of incoming reserved streams. This is the number of streams in reserved (remote) state. RFC 7540 does not limit this number. nghttp2 offers nghttp2_option_set_max_reserved_remote_streams() to achieve this. If it is used, num_incoming_streams is capped by max_incoming_reserved_streams. Client application should consider to set this because without that server can send arbitrary number of PUSH_PROMISE, and exhaust client's memory. */ size_t num_incoming_reserved_streams; /* The maximum number of incoming reserved streams (reserved (remote) state). RST_STREAM will be sent for the pushed stream which exceeds this limit. */ size_t max_incoming_reserved_streams; /* The number of closed streams still kept in |streams| hash. The closed streams can be accessed through single linked list |closed_stream_head|. The current implementation only keeps incoming streams and session is initialized as server. */ size_t num_closed_streams; /* The number of idle streams kept in |streams| hash. The current implementation only keeps idle streams if session is initialized as server. */ size_t num_idle_streams; /* The number of bytes allocated for nvbuf */ size_t nvbuflen; /* Counter for detecting flooding in outbound queue. If it exceeds max_outbound_ack, session will be closed. */ size_t obq_flood_counter_; /* The maximum number of outgoing SETTINGS ACK and PING ACK in outbound queue. */ size_t max_outbound_ack; /* The maximum length of header block to send. Calculated by the same way as nghttp2_hd_deflate_bound() does. */ size_t max_send_header_block_length; /* The maximum number of settings accepted per SETTINGS frame. */ size_t max_settings; /* The maximum number of CONTINUATION frames following an incoming HEADER frame. */ size_t max_continuations; /* The number of CONTINUATION frames following an incoming HEADER frame. This variable is reset when END_HEADERS flag is seen. */ size_t num_continuations; /* Next Stream ID. Made unsigned int to detect >= (1 << 31). */ uint32_t next_stream_id; /* The last stream ID this session initiated. For client session, this is the last stream ID it has sent. For server session, it is the last promised stream ID sent in PUSH_PROMISE. */ int32_t last_sent_stream_id; /* The largest stream ID received so far */ int32_t last_recv_stream_id; /* The largest stream ID which has been processed in some way. This value will be used as last-stream-id when sending GOAWAY frame. */ int32_t last_proc_stream_id; /* Counter of unique ID of PING. Wraps when it exceeds NGHTTP2_MAX_UNIQUE_ID */ uint32_t next_unique_id; /* This is the last-stream-ID we have sent in GOAWAY */ int32_t local_last_stream_id; /* This is the value in GOAWAY frame received from remote endpoint. */ int32_t remote_last_stream_id; /* Current sender window size. This value is computed against the current initial window size of remote endpoint. */ int32_t remote_window_size; /* Keep track of the number of bytes received without WINDOW_UPDATE. This could be negative after submitting negative value to WINDOW_UPDATE. */ int32_t recv_window_size; /* The number of bytes consumed by the application and now is subject to WINDOW_UPDATE. This is only used when auto WINDOW_UPDATE is turned off. */ int32_t consumed_size; /* The amount of recv_window_size cut using submitting negative value to WINDOW_UPDATE */ int32_t recv_reduction; /* window size for local flow control. It is initially set to NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE and could be increased/decreased by submitting WINDOW_UPDATE. See nghttp2_submit_window_update(). */ int32_t local_window_size; /* This flag is used to indicate that the local endpoint received initial SETTINGS frame from the remote endpoint. */ uint8_t remote_settings_received; /* Settings value received from the remote endpoint. */ nghttp2_settings_storage remote_settings; /* Settings value of the local endpoint. */ nghttp2_settings_storage local_settings; /* Option flags. This is bitwise-OR of 0 or more of nghttp2_optmask. */ uint32_t opt_flags; /* Unacked local SETTINGS_MAX_CONCURRENT_STREAMS value. We use this to refuse the incoming stream if it exceeds this value. */ uint32_t pending_local_max_concurrent_stream; /* The bitwise OR of zero or more of nghttp2_typemask to indicate that the default handling of extension frame is enabled. */ uint32_t builtin_recv_ext_types; /* Unacked local ENABLE_PUSH value. We use this to refuse PUSH_PROMISE before SETTINGS ACK is received. */ uint8_t pending_enable_push; /* Unacked local ENABLE_CONNECT_PROTOCOL value. We use this to accept :protocol header field before SETTINGS_ACK is received. */ uint8_t pending_enable_connect_protocol; /* Unacked local SETTINGS_NO_RFC7540_PRIORITIES value, which is effective before it is acknowledged. */ uint8_t pending_no_rfc7540_priorities; /* Nonzero if the session is server side. */ uint8_t server; /* Flags indicating GOAWAY is sent and/or received. The flags are composed by bitwise OR-ing nghttp2_goaway_flag. */ uint8_t goaway_flags; /* This flag is used to reduce excessive queuing of WINDOW_UPDATE to this session. The nonzero does not necessarily mean WINDOW_UPDATE is not queued. */ uint8_t window_update_queued; /* Bitfield of extension frame types that application is willing to receive. To designate the bit of given frame type i, use user_recv_ext_types[i / 8] & (1 << (i & 0x7)). First 10 frame types are standard frame types and not used in this bitfield. If bit is set, it indicates that incoming frame with that type is passed to user defined callbacks, otherwise they are ignored. */ uint8_t user_recv_ext_types[32]; }; /* Struct used when updating initial window size of each active stream. */ typedef struct { nghttp2_session *session; int32_t new_window_size, old_window_size; } nghttp2_update_window_size_arg; typedef struct { nghttp2_session *session; /* linked list of streams to close */ nghttp2_stream *head; int32_t last_stream_id; /* nonzero if GOAWAY is sent to peer, which means we are going to close incoming streams. zero if GOAWAY is received from peer and we are going to close outgoing streams. */ int incoming; } nghttp2_close_stream_on_goaway_arg; /* TODO stream timeout etc */ /* * Returns nonzero value if |stream_id| is initiated by local * endpoint. */ int nghttp2_session_is_my_stream_id(nghttp2_session *session, int32_t stream_id); /* * Adds |item| to the outbound queue in |session|. When this function * succeeds, it takes ownership of |item|. So caller must not free it * on success. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_STREAM_CLOSED * Stream already closed (DATA and PUSH_PROMISE frame only) */ int nghttp2_session_add_item(nghttp2_session *session, nghttp2_outbound_item *item); /* * This function wraps around nghttp2_session_add_rst_stream_continue * with continue_without_stream = 1. */ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, uint32_t error_code); /* * Adds RST_STREAM frame for the stream |stream_id| with the error * code |error_code|. This is a convenient function built on top of * nghttp2_session_add_frame() to add RST_STREAM easily. * * This function simply returns 0 without adding RST_STREAM frame if * given stream is in NGHTTP2_STREAM_CLOSING state, because multiple * RST_STREAM for a stream is redundant. It also returns 0 without * adding the frame if |continue_without_stream| is nonzero, and * stream was already gone. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. */ int nghttp2_session_add_rst_stream_continue(nghttp2_session *session, int32_t stream_id, uint32_t error_code, int continue_without_stream); /* * Adds PING frame. This is a convenient function built on top of * nghttp2_session_add_frame() to add PING easily. * * If the |opaque_data| is not NULL, it must point to 8 bytes memory * region of data. The data pointed by |opaque_data| is copied. It can * be NULL. In this case, 8 bytes NULL is used. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_FLOODED * There are too many items in outbound queue; this only happens * if NGHTTP2_FLAG_ACK is set in |flags| */ int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags, const uint8_t *opaque_data); /* * Adds GOAWAY frame with the last-stream-ID |last_stream_id| and the * error code |error_code|. This is a convenient function built on top * of nghttp2_session_add_frame() to add GOAWAY easily. The * |aux_flags| are bitwise-OR of one or more of * nghttp2_goaway_aux_flag. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_INVALID_ARGUMENT * The |opaque_data_len| is too large. */ int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id, uint32_t error_code, const uint8_t *opaque_data, size_t opaque_data_len, uint8_t aux_flags); /* * Adds WINDOW_UPDATE frame with stream ID |stream_id| and * window-size-increment |window_size_increment|. This is a convenient * function built on top of nghttp2_session_add_frame() to add * WINDOW_UPDATE easily. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. */ int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags, int32_t stream_id, int32_t window_size_increment); /* * Adds SETTINGS frame. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_FLOODED * There are too many items in outbound queue; this only happens * if NGHTTP2_FLAG_ACK is set in |flags| */ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, const nghttp2_settings_entry *iv, size_t niv); /* * Creates new stream in |session| with stream ID |stream_id|, * priority |pri_spec| and flags |flags|. The |flags| is bitwise OR * of nghttp2_stream_flag. Since this function is called when initial * HEADERS is sent or received, these flags are taken from it. The * state of stream is set to |initial_state|. The |stream_user_data| * is a pointer to the arbitrary user supplied data to be associated * to this stream. * * If |initial_state| is NGHTTP2_STREAM_RESERVED, this function sets * NGHTTP2_STREAM_FLAG_PUSH flag set. * * This function returns a pointer to created new stream object, or * NULL. */ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, int32_t stream_id, uint8_t flags, nghttp2_stream_state initial_state, void *stream_user_data); /* * Closes stream whose stream ID is |stream_id|. The reason of closure * is indicated by the |error_code|. When closing the stream, * on_stream_close_callback will be called. * * This function returns 0 if it succeeds, or one the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory * NGHTTP2_ERR_INVALID_ARGUMENT * The specified stream does not exist. * NGHTTP2_ERR_CALLBACK_FAILURE * The callback function failed. */ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, uint32_t error_code); /* * Deletes |stream| from memory. After this function returns, stream * cannot be accessed. */ void nghttp2_session_destroy_stream(nghttp2_session *session, nghttp2_stream *stream); /* * If further receptions and transmissions over the stream |stream_id| * are disallowed, close the stream with error code NGHTTP2_NO_ERROR. * * This function returns 0 if it * succeeds, or one of the following negative error codes: * * NGHTTP2_ERR_INVALID_ARGUMENT * The specified stream does not exist. */ int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session, nghttp2_stream *stream); int nghttp2_session_on_request_headers_received(nghttp2_session *session, nghttp2_frame *frame); int nghttp2_session_on_response_headers_received(nghttp2_session *session, nghttp2_frame *frame, nghttp2_stream *stream); int nghttp2_session_on_push_response_headers_received(nghttp2_session *session, nghttp2_frame *frame, nghttp2_stream *stream); /* * Called when HEADERS is received, assuming |frame| is properly * initialized. This function does first validate received frame and * then open stream and call callback functions. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_IGN_HEADER_BLOCK * Frame was rejected and header block must be decoded but * result must be ignored. * NGHTTP2_ERR_CALLBACK_FAILURE * The read_callback failed */ int nghttp2_session_on_headers_received(nghttp2_session *session, nghttp2_frame *frame, nghttp2_stream *stream); /* * Called when PRIORITY is received, assuming |frame| is properly * initialized. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_CALLBACK_FAILURE * The read_callback failed */ int nghttp2_session_on_priority_received(nghttp2_session *session, nghttp2_frame *frame); /* * Called when RST_STREAM is received, assuming |frame| is properly * initialized. * * This function returns 0 if it succeeds, or one the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory * NGHTTP2_ERR_CALLBACK_FAILURE * The read_callback failed */ int nghttp2_session_on_rst_stream_received(nghttp2_session *session, nghttp2_frame *frame); /* * Called when SETTINGS is received, assuming |frame| is properly * initialized. If |noack| is non-zero, SETTINGS with ACK will not be * submitted. If |frame| has NGHTTP2_FLAG_ACK flag set, no SETTINGS * with ACK will not be submitted regardless of |noack|. * * This function returns 0 if it succeeds, or one the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory * NGHTTP2_ERR_CALLBACK_FAILURE * The read_callback failed * NGHTTP2_ERR_FLOODED * There are too many items in outbound queue, and this is most * likely caused by misbehaviour of peer. */ int nghttp2_session_on_settings_received(nghttp2_session *session, nghttp2_frame *frame, int noack); /* * Called when PUSH_PROMISE is received, assuming |frame| is properly * initialized. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_IGN_HEADER_BLOCK * Frame was rejected and header block must be decoded but * result must be ignored. * NGHTTP2_ERR_CALLBACK_FAILURE * The read_callback failed */ int nghttp2_session_on_push_promise_received(nghttp2_session *session, nghttp2_frame *frame); /* * Called when PING is received, assuming |frame| is properly * initialized. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_CALLBACK_FAILURE * The callback function failed. * NGHTTP2_ERR_FLOODED * There are too many items in outbound queue, and this is most * likely caused by misbehaviour of peer. */ int nghttp2_session_on_ping_received(nghttp2_session *session, nghttp2_frame *frame); /* * Called when GOAWAY is received, assuming |frame| is properly * initialized. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_CALLBACK_FAILURE * The callback function failed. */ int nghttp2_session_on_goaway_received(nghttp2_session *session, nghttp2_frame *frame); /* * Called when WINDOW_UPDATE is received, assuming |frame| is properly * initialized. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_CALLBACK_FAILURE * The callback function failed. */ int nghttp2_session_on_window_update_received(nghttp2_session *session, nghttp2_frame *frame); /* * Called when ALTSVC is received, assuming |frame| is properly * initialized. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_CALLBACK_FAILURE * The callback function failed. */ int nghttp2_session_on_altsvc_received(nghttp2_session *session, nghttp2_frame *frame); /* * Called when ORIGIN is received, assuming |frame| is properly * initialized. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_CALLBACK_FAILURE * The callback function failed. */ int nghttp2_session_on_origin_received(nghttp2_session *session, nghttp2_frame *frame); /* * Called when PRIORITY_UPDATE is received, assuming |frame| is * properly initialized. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_CALLBACK_FAILURE * The callback function failed. */ int nghttp2_session_on_priority_update_received(nghttp2_session *session, nghttp2_frame *frame); /* * Called when DATA is received, assuming |frame| is properly * initialized. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_CALLBACK_FAILURE * The callback function failed. */ int nghttp2_session_on_data_received(nghttp2_session *session, nghttp2_frame *frame); /* * Returns nghttp2_stream* object whose stream ID is |stream_id|. It * could be NULL if such stream does not exist. This function returns * NULL if stream is marked as closed. */ nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session, int32_t stream_id); /* * This function behaves like nghttp2_session_get_stream(), but it * returns stream object even if it is marked as closed or in * NGHTTP2_STREAM_IDLE state. */ nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session, int32_t stream_id); /* * Packs DATA frame |frame| in wire frame format and stores it in * |bufs|. Payload will be read using |aux_data->data_prd|. The * length of payload is at most |datamax| bytes. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_DEFERRED * The DATA frame is postponed. * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE * The read_callback failed (stream error). * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_CALLBACK_FAILURE * The read_callback failed (session error). */ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs, size_t datamax, nghttp2_frame *frame, nghttp2_data_aux_data *aux_data, nghttp2_stream *stream); /* * Pops and returns next item to send. If there is no such item, * returns NULL. This function takes into account max concurrent * streams. That means if session->ob_syn has item and max concurrent * streams is reached, the even if other queues contain items, then * this function returns NULL. */ nghttp2_outbound_item * nghttp2_session_pop_next_ob_item(nghttp2_session *session); /* * Returns next item to send. If there is no such item, this function * returns NULL. This function takes into account max concurrent * streams. That means if session->ob_syn has item and max concurrent * streams is reached, the even if other queues contain items, then * this function returns NULL. */ nghttp2_outbound_item * nghttp2_session_get_next_ob_item(nghttp2_session *session); /* * Updates local settings with the |iv|. The number of elements in the * array pointed by the |iv| is given by the |niv|. This function * assumes that the all settings_id member in |iv| are in range 1 to * NGHTTP2_SETTINGS_MAX, inclusive. * * While updating individual stream's local window size, if the window * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, * RST_STREAM is issued against such a stream. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory */ int nghttp2_session_update_local_settings(nghttp2_session *session, nghttp2_settings_entry *iv, size_t niv); /* * Terminates current |session| with the |error_code|. The |reason| * is NULL-terminated debug string. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_INVALID_ARGUMENT * The |reason| is too long. */ int nghttp2_session_terminate_session_with_reason(nghttp2_session *session, uint32_t error_code, const char *reason); /* * Accumulates received bytes |delta_size| for connection-level flow * control and decides whether to send WINDOW_UPDATE to the * connection. If NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE is set, * WINDOW_UPDATE will not be sent. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. */ int nghttp2_session_update_recv_connection_window_size(nghttp2_session *session, size_t delta_size); /* * Accumulates received bytes |delta_size| for stream-level flow * control and decides whether to send WINDOW_UPDATE to that stream. * If NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE is set, WINDOW_UPDATE will not * be sent. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. */ int nghttp2_session_update_recv_stream_window_size(nghttp2_session *session, nghttp2_stream *stream, size_t delta_size, int send_window_update); #endif /* !defined(NGHTTP2_SESSION_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_debug.h0000644000000000000000000000013215077107270015733 xustar0030 mtime=1761382072.977444213 30 atime=1761382104.961315903 30 ctime=1761382107.947303714 nghttp2-1.68.0/lib/nghttp2_debug.h0000644000175100017510000000330415077107270016323 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_DEBUG_H #define NGHTTP2_DEBUG_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include #ifdef DEBUGBUILD # define DEBUGF(...) nghttp2_debug_vprintf(__VA_ARGS__) void nghttp2_debug_vprintf(const char *format, ...); #else /* !defined(DEBUGBUILD) */ # define DEBUGF(...) \ do { \ } while (0) #endif /* !defined(DEBUGBUILD) */ #endif /* !defined(NGHTTP2_DEBUG_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_hd_huffman_data.c0000644000000000000000000000013215077107270017730 xustar0030 mtime=1761382072.978444208 30 atime=1761382104.980315819 30 ctime=1761382107.966303659 nghttp2-1.68.0/lib/nghttp2_hd_huffman_data.c0000644000175100017510000025200115077107270020320 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_hd_huffman.h" /* Generated by mkhufftbl.py */ const nghttp2_huff_sym huff_sym_table[] = { {13, 0xffc00000u}, {23, 0xffffb000u}, {28, 0xfffffe20u}, {28, 0xfffffe30u}, {28, 0xfffffe40u}, {28, 0xfffffe50u}, {28, 0xfffffe60u}, {28, 0xfffffe70u}, {28, 0xfffffe80u}, {24, 0xffffea00u}, {30, 0xfffffff0u}, {28, 0xfffffe90u}, {28, 0xfffffea0u}, {30, 0xfffffff4u}, {28, 0xfffffeb0u}, {28, 0xfffffec0u}, {28, 0xfffffed0u}, {28, 0xfffffee0u}, {28, 0xfffffef0u}, {28, 0xffffff00u}, {28, 0xffffff10u}, {28, 0xffffff20u}, {30, 0xfffffff8u}, {28, 0xffffff30u}, {28, 0xffffff40u}, {28, 0xffffff50u}, {28, 0xffffff60u}, {28, 0xffffff70u}, {28, 0xffffff80u}, {28, 0xffffff90u}, {28, 0xffffffa0u}, {28, 0xffffffb0u}, {6, 0x50000000u}, {10, 0xfe000000u}, {10, 0xfe400000u}, {12, 0xffa00000u}, {13, 0xffc80000u}, {6, 0x54000000u}, {8, 0xf8000000u}, {11, 0xff400000u}, {10, 0xfe800000u}, {10, 0xfec00000u}, {8, 0xf9000000u}, {11, 0xff600000u}, {8, 0xfa000000u}, {6, 0x58000000u}, {6, 0x5c000000u}, {6, 0x60000000u}, {5, 0x0u}, {5, 0x8000000u}, {5, 0x10000000u}, {6, 0x64000000u}, {6, 0x68000000u}, {6, 0x6c000000u}, {6, 0x70000000u}, {6, 0x74000000u}, {6, 0x78000000u}, {6, 0x7c000000u}, {7, 0xb8000000u}, {8, 0xfb000000u}, {15, 0xfff80000u}, {6, 0x80000000u}, {12, 0xffb00000u}, {10, 0xff000000u}, {13, 0xffd00000u}, {6, 0x84000000u}, {7, 0xba000000u}, {7, 0xbc000000u}, {7, 0xbe000000u}, {7, 0xc0000000u}, {7, 0xc2000000u}, {7, 0xc4000000u}, {7, 0xc6000000u}, {7, 0xc8000000u}, {7, 0xca000000u}, {7, 0xcc000000u}, {7, 0xce000000u}, {7, 0xd0000000u}, {7, 0xd2000000u}, {7, 0xd4000000u}, {7, 0xd6000000u}, {7, 0xd8000000u}, {7, 0xda000000u}, {7, 0xdc000000u}, {7, 0xde000000u}, {7, 0xe0000000u}, {7, 0xe2000000u}, {7, 0xe4000000u}, {8, 0xfc000000u}, {7, 0xe6000000u}, {8, 0xfd000000u}, {13, 0xffd80000u}, {19, 0xfffe0000u}, {13, 0xffe00000u}, {14, 0xfff00000u}, {6, 0x88000000u}, {15, 0xfffa0000u}, {5, 0x18000000u}, {6, 0x8c000000u}, {5, 0x20000000u}, {6, 0x90000000u}, {5, 0x28000000u}, {6, 0x94000000u}, {6, 0x98000000u}, {6, 0x9c000000u}, {5, 0x30000000u}, {7, 0xe8000000u}, {7, 0xea000000u}, {6, 0xa0000000u}, {6, 0xa4000000u}, {6, 0xa8000000u}, {5, 0x38000000u}, {6, 0xac000000u}, {7, 0xec000000u}, {6, 0xb0000000u}, {5, 0x40000000u}, {5, 0x48000000u}, {6, 0xb4000000u}, {7, 0xee000000u}, {7, 0xf0000000u}, {7, 0xf2000000u}, {7, 0xf4000000u}, {7, 0xf6000000u}, {15, 0xfffc0000u}, {11, 0xff800000u}, {14, 0xfff40000u}, {13, 0xffe80000u}, {28, 0xffffffc0u}, {20, 0xfffe6000u}, {22, 0xffff4800u}, {20, 0xfffe7000u}, {20, 0xfffe8000u}, {22, 0xffff4c00u}, {22, 0xffff5000u}, {22, 0xffff5400u}, {23, 0xffffb200u}, {22, 0xffff5800u}, {23, 0xffffb400u}, {23, 0xffffb600u}, {23, 0xffffb800u}, {23, 0xffffba00u}, {23, 0xffffbc00u}, {24, 0xffffeb00u}, {23, 0xffffbe00u}, {24, 0xffffec00u}, {24, 0xffffed00u}, {22, 0xffff5c00u}, {23, 0xffffc000u}, {24, 0xffffee00u}, {23, 0xffffc200u}, {23, 0xffffc400u}, {23, 0xffffc600u}, {23, 0xffffc800u}, {21, 0xfffee000u}, {22, 0xffff6000u}, {23, 0xffffca00u}, {22, 0xffff6400u}, {23, 0xffffcc00u}, {23, 0xffffce00u}, {24, 0xffffef00u}, {22, 0xffff6800u}, {21, 0xfffee800u}, {20, 0xfffe9000u}, {22, 0xffff6c00u}, {22, 0xffff7000u}, {23, 0xffffd000u}, {23, 0xffffd200u}, {21, 0xfffef000u}, {23, 0xffffd400u}, {22, 0xffff7400u}, {22, 0xffff7800u}, {24, 0xfffff000u}, {21, 0xfffef800u}, {22, 0xffff7c00u}, {23, 0xffffd600u}, {23, 0xffffd800u}, {21, 0xffff0000u}, {21, 0xffff0800u}, {22, 0xffff8000u}, {21, 0xffff1000u}, {23, 0xffffda00u}, {22, 0xffff8400u}, {23, 0xffffdc00u}, {23, 0xffffde00u}, {20, 0xfffea000u}, {22, 0xffff8800u}, {22, 0xffff8c00u}, {22, 0xffff9000u}, {23, 0xffffe000u}, {22, 0xffff9400u}, {22, 0xffff9800u}, {23, 0xffffe200u}, {26, 0xfffff800u}, {26, 0xfffff840u}, {20, 0xfffeb000u}, {19, 0xfffe2000u}, {22, 0xffff9c00u}, {23, 0xffffe400u}, {22, 0xffffa000u}, {25, 0xfffff600u}, {26, 0xfffff880u}, {26, 0xfffff8c0u}, {26, 0xfffff900u}, {27, 0xfffffbc0u}, {27, 0xfffffbe0u}, {26, 0xfffff940u}, {24, 0xfffff100u}, {25, 0xfffff680u}, {19, 0xfffe4000u}, {21, 0xffff1800u}, {26, 0xfffff980u}, {27, 0xfffffc00u}, {27, 0xfffffc20u}, {26, 0xfffff9c0u}, {27, 0xfffffc40u}, {24, 0xfffff200u}, {21, 0xffff2000u}, {21, 0xffff2800u}, {26, 0xfffffa00u}, {26, 0xfffffa40u}, {28, 0xffffffd0u}, {27, 0xfffffc60u}, {27, 0xfffffc80u}, {27, 0xfffffca0u}, {20, 0xfffec000u}, {24, 0xfffff300u}, {20, 0xfffed000u}, {21, 0xffff3000u}, {22, 0xffffa400u}, {21, 0xffff3800u}, {21, 0xffff4000u}, {23, 0xffffe600u}, {22, 0xffffa800u}, {22, 0xffffac00u}, {25, 0xfffff700u}, {25, 0xfffff780u}, {24, 0xfffff400u}, {24, 0xfffff500u}, {26, 0xfffffa80u}, {23, 0xffffe800u}, {26, 0xfffffac0u}, {27, 0xfffffcc0u}, {26, 0xfffffb00u}, {26, 0xfffffb40u}, {27, 0xfffffce0u}, {27, 0xfffffd00u}, {27, 0xfffffd20u}, {27, 0xfffffd40u}, {27, 0xfffffd60u}, {28, 0xffffffe0u}, {27, 0xfffffd80u}, {27, 0xfffffda0u}, {27, 0xfffffdc0u}, {27, 0xfffffde0u}, {27, 0xfffffe00u}, {26, 0xfffffb80u}, {30, 0xfffffffcu}}; const nghttp2_huff_decode huff_decode_table[][16] = { /* 0 */ { {0x04, 0}, {0x05, 0}, {0x07, 0}, {0x08, 0}, {0x0b, 0}, {0x0c, 0}, {0x10, 0}, {0x13, 0}, {0x19, 0}, {0x1c, 0}, {0x20, 0}, {0x23, 0}, {0x2a, 0}, {0x31, 0}, {0x39, 0}, {0x4040, 0}, }, /* 1 */ { {0xc000, 48}, {0xc000, 49}, {0xc000, 50}, {0xc000, 97}, {0xc000, 99}, {0xc000, 101}, {0xc000, 105}, {0xc000, 111}, {0xc000, 115}, {0xc000, 116}, {0x0d, 0}, {0x0e, 0}, {0x11, 0}, {0x12, 0}, {0x14, 0}, {0x15, 0}, }, /* 2 */ { {0x8001, 48}, {0xc016, 48}, {0x8001, 49}, {0xc016, 49}, {0x8001, 50}, {0xc016, 50}, {0x8001, 97}, {0xc016, 97}, {0x8001, 99}, {0xc016, 99}, {0x8001, 101}, {0xc016, 101}, {0x8001, 105}, {0xc016, 105}, {0x8001, 111}, {0xc016, 111}, }, /* 3 */ { {0x8002, 48}, {0x8009, 48}, {0x8017, 48}, {0xc028, 48}, {0x8002, 49}, {0x8009, 49}, {0x8017, 49}, {0xc028, 49}, {0x8002, 50}, {0x8009, 50}, {0x8017, 50}, {0xc028, 50}, {0x8002, 97}, {0x8009, 97}, {0x8017, 97}, {0xc028, 97}, }, /* 4 */ { {0x8003, 48}, {0x8006, 48}, {0x800a, 48}, {0x800f, 48}, {0x8018, 48}, {0x801f, 48}, {0x8029, 48}, {0xc038, 48}, {0x8003, 49}, {0x8006, 49}, {0x800a, 49}, {0x800f, 49}, {0x8018, 49}, {0x801f, 49}, {0x8029, 49}, {0xc038, 49}, }, /* 5 */ { {0x8003, 50}, {0x8006, 50}, {0x800a, 50}, {0x800f, 50}, {0x8018, 50}, {0x801f, 50}, {0x8029, 50}, {0xc038, 50}, {0x8003, 97}, {0x8006, 97}, {0x800a, 97}, {0x800f, 97}, {0x8018, 97}, {0x801f, 97}, {0x8029, 97}, {0xc038, 97}, }, /* 6 */ { {0x8002, 99}, {0x8009, 99}, {0x8017, 99}, {0xc028, 99}, {0x8002, 101}, {0x8009, 101}, {0x8017, 101}, {0xc028, 101}, {0x8002, 105}, {0x8009, 105}, {0x8017, 105}, {0xc028, 105}, {0x8002, 111}, {0x8009, 111}, {0x8017, 111}, {0xc028, 111}, }, /* 7 */ { {0x8003, 99}, {0x8006, 99}, {0x800a, 99}, {0x800f, 99}, {0x8018, 99}, {0x801f, 99}, {0x8029, 99}, {0xc038, 99}, {0x8003, 101}, {0x8006, 101}, {0x800a, 101}, {0x800f, 101}, {0x8018, 101}, {0x801f, 101}, {0x8029, 101}, {0xc038, 101}, }, /* 8 */ { {0x8003, 105}, {0x8006, 105}, {0x800a, 105}, {0x800f, 105}, {0x8018, 105}, {0x801f, 105}, {0x8029, 105}, {0xc038, 105}, {0x8003, 111}, {0x8006, 111}, {0x800a, 111}, {0x800f, 111}, {0x8018, 111}, {0x801f, 111}, {0x8029, 111}, {0xc038, 111}, }, /* 9 */ { {0x8001, 115}, {0xc016, 115}, {0x8001, 116}, {0xc016, 116}, {0xc000, 32}, {0xc000, 37}, {0xc000, 45}, {0xc000, 46}, {0xc000, 47}, {0xc000, 51}, {0xc000, 52}, {0xc000, 53}, {0xc000, 54}, {0xc000, 55}, {0xc000, 56}, {0xc000, 57}, }, /* 10 */ { {0x8002, 115}, {0x8009, 115}, {0x8017, 115}, {0xc028, 115}, {0x8002, 116}, {0x8009, 116}, {0x8017, 116}, {0xc028, 116}, {0x8001, 32}, {0xc016, 32}, {0x8001, 37}, {0xc016, 37}, {0x8001, 45}, {0xc016, 45}, {0x8001, 46}, {0xc016, 46}, }, /* 11 */ { {0x8003, 115}, {0x8006, 115}, {0x800a, 115}, {0x800f, 115}, {0x8018, 115}, {0x801f, 115}, {0x8029, 115}, {0xc038, 115}, {0x8003, 116}, {0x8006, 116}, {0x800a, 116}, {0x800f, 116}, {0x8018, 116}, {0x801f, 116}, {0x8029, 116}, {0xc038, 116}, }, /* 12 */ { {0x8002, 32}, {0x8009, 32}, {0x8017, 32}, {0xc028, 32}, {0x8002, 37}, {0x8009, 37}, {0x8017, 37}, {0xc028, 37}, {0x8002, 45}, {0x8009, 45}, {0x8017, 45}, {0xc028, 45}, {0x8002, 46}, {0x8009, 46}, {0x8017, 46}, {0xc028, 46}, }, /* 13 */ { {0x8003, 32}, {0x8006, 32}, {0x800a, 32}, {0x800f, 32}, {0x8018, 32}, {0x801f, 32}, {0x8029, 32}, {0xc038, 32}, {0x8003, 37}, {0x8006, 37}, {0x800a, 37}, {0x800f, 37}, {0x8018, 37}, {0x801f, 37}, {0x8029, 37}, {0xc038, 37}, }, /* 14 */ { {0x8003, 45}, {0x8006, 45}, {0x800a, 45}, {0x800f, 45}, {0x8018, 45}, {0x801f, 45}, {0x8029, 45}, {0xc038, 45}, {0x8003, 46}, {0x8006, 46}, {0x800a, 46}, {0x800f, 46}, {0x8018, 46}, {0x801f, 46}, {0x8029, 46}, {0xc038, 46}, }, /* 15 */ { {0x8001, 47}, {0xc016, 47}, {0x8001, 51}, {0xc016, 51}, {0x8001, 52}, {0xc016, 52}, {0x8001, 53}, {0xc016, 53}, {0x8001, 54}, {0xc016, 54}, {0x8001, 55}, {0xc016, 55}, {0x8001, 56}, {0xc016, 56}, {0x8001, 57}, {0xc016, 57}, }, /* 16 */ { {0x8002, 47}, {0x8009, 47}, {0x8017, 47}, {0xc028, 47}, {0x8002, 51}, {0x8009, 51}, {0x8017, 51}, {0xc028, 51}, {0x8002, 52}, {0x8009, 52}, {0x8017, 52}, {0xc028, 52}, {0x8002, 53}, {0x8009, 53}, {0x8017, 53}, {0xc028, 53}, }, /* 17 */ { {0x8003, 47}, {0x8006, 47}, {0x800a, 47}, {0x800f, 47}, {0x8018, 47}, {0x801f, 47}, {0x8029, 47}, {0xc038, 47}, {0x8003, 51}, {0x8006, 51}, {0x800a, 51}, {0x800f, 51}, {0x8018, 51}, {0x801f, 51}, {0x8029, 51}, {0xc038, 51}, }, /* 18 */ { {0x8003, 52}, {0x8006, 52}, {0x800a, 52}, {0x800f, 52}, {0x8018, 52}, {0x801f, 52}, {0x8029, 52}, {0xc038, 52}, {0x8003, 53}, {0x8006, 53}, {0x800a, 53}, {0x800f, 53}, {0x8018, 53}, {0x801f, 53}, {0x8029, 53}, {0xc038, 53}, }, /* 19 */ { {0x8002, 54}, {0x8009, 54}, {0x8017, 54}, {0xc028, 54}, {0x8002, 55}, {0x8009, 55}, {0x8017, 55}, {0xc028, 55}, {0x8002, 56}, {0x8009, 56}, {0x8017, 56}, {0xc028, 56}, {0x8002, 57}, {0x8009, 57}, {0x8017, 57}, {0xc028, 57}, }, /* 20 */ { {0x8003, 54}, {0x8006, 54}, {0x800a, 54}, {0x800f, 54}, {0x8018, 54}, {0x801f, 54}, {0x8029, 54}, {0xc038, 54}, {0x8003, 55}, {0x8006, 55}, {0x800a, 55}, {0x800f, 55}, {0x8018, 55}, {0x801f, 55}, {0x8029, 55}, {0xc038, 55}, }, /* 21 */ { {0x8003, 56}, {0x8006, 56}, {0x800a, 56}, {0x800f, 56}, {0x8018, 56}, {0x801f, 56}, {0x8029, 56}, {0xc038, 56}, {0x8003, 57}, {0x8006, 57}, {0x800a, 57}, {0x800f, 57}, {0x8018, 57}, {0x801f, 57}, {0x8029, 57}, {0xc038, 57}, }, /* 22 */ { {0x1a, 0}, {0x1b, 0}, {0x1d, 0}, {0x1e, 0}, {0x21, 0}, {0x22, 0}, {0x24, 0}, {0x25, 0}, {0x2b, 0}, {0x2e, 0}, {0x32, 0}, {0x35, 0}, {0x3a, 0}, {0x3d, 0}, {0x41, 0}, {0x4044, 0}, }, /* 23 */ { {0xc000, 61}, {0xc000, 65}, {0xc000, 95}, {0xc000, 98}, {0xc000, 100}, {0xc000, 102}, {0xc000, 103}, {0xc000, 104}, {0xc000, 108}, {0xc000, 109}, {0xc000, 110}, {0xc000, 112}, {0xc000, 114}, {0xc000, 117}, {0x26, 0}, {0x27, 0}, }, /* 24 */ { {0x8001, 61}, {0xc016, 61}, {0x8001, 65}, {0xc016, 65}, {0x8001, 95}, {0xc016, 95}, {0x8001, 98}, {0xc016, 98}, {0x8001, 100}, {0xc016, 100}, {0x8001, 102}, {0xc016, 102}, {0x8001, 103}, {0xc016, 103}, {0x8001, 104}, {0xc016, 104}, }, /* 25 */ { {0x8002, 61}, {0x8009, 61}, {0x8017, 61}, {0xc028, 61}, {0x8002, 65}, {0x8009, 65}, {0x8017, 65}, {0xc028, 65}, {0x8002, 95}, {0x8009, 95}, {0x8017, 95}, {0xc028, 95}, {0x8002, 98}, {0x8009, 98}, {0x8017, 98}, {0xc028, 98}, }, /* 26 */ { {0x8003, 61}, {0x8006, 61}, {0x800a, 61}, {0x800f, 61}, {0x8018, 61}, {0x801f, 61}, {0x8029, 61}, {0xc038, 61}, {0x8003, 65}, {0x8006, 65}, {0x800a, 65}, {0x800f, 65}, {0x8018, 65}, {0x801f, 65}, {0x8029, 65}, {0xc038, 65}, }, /* 27 */ { {0x8003, 95}, {0x8006, 95}, {0x800a, 95}, {0x800f, 95}, {0x8018, 95}, {0x801f, 95}, {0x8029, 95}, {0xc038, 95}, {0x8003, 98}, {0x8006, 98}, {0x800a, 98}, {0x800f, 98}, {0x8018, 98}, {0x801f, 98}, {0x8029, 98}, {0xc038, 98}, }, /* 28 */ { {0x8002, 100}, {0x8009, 100}, {0x8017, 100}, {0xc028, 100}, {0x8002, 102}, {0x8009, 102}, {0x8017, 102}, {0xc028, 102}, {0x8002, 103}, {0x8009, 103}, {0x8017, 103}, {0xc028, 103}, {0x8002, 104}, {0x8009, 104}, {0x8017, 104}, {0xc028, 104}, }, /* 29 */ { {0x8003, 100}, {0x8006, 100}, {0x800a, 100}, {0x800f, 100}, {0x8018, 100}, {0x801f, 100}, {0x8029, 100}, {0xc038, 100}, {0x8003, 102}, {0x8006, 102}, {0x800a, 102}, {0x800f, 102}, {0x8018, 102}, {0x801f, 102}, {0x8029, 102}, {0xc038, 102}, }, /* 30 */ { {0x8003, 103}, {0x8006, 103}, {0x800a, 103}, {0x800f, 103}, {0x8018, 103}, {0x801f, 103}, {0x8029, 103}, {0xc038, 103}, {0x8003, 104}, {0x8006, 104}, {0x800a, 104}, {0x800f, 104}, {0x8018, 104}, {0x801f, 104}, {0x8029, 104}, {0xc038, 104}, }, /* 31 */ { {0x8001, 108}, {0xc016, 108}, {0x8001, 109}, {0xc016, 109}, {0x8001, 110}, {0xc016, 110}, {0x8001, 112}, {0xc016, 112}, {0x8001, 114}, {0xc016, 114}, {0x8001, 117}, {0xc016, 117}, {0xc000, 58}, {0xc000, 66}, {0xc000, 67}, {0xc000, 68}, }, /* 32 */ { {0x8002, 108}, {0x8009, 108}, {0x8017, 108}, {0xc028, 108}, {0x8002, 109}, {0x8009, 109}, {0x8017, 109}, {0xc028, 109}, {0x8002, 110}, {0x8009, 110}, {0x8017, 110}, {0xc028, 110}, {0x8002, 112}, {0x8009, 112}, {0x8017, 112}, {0xc028, 112}, }, /* 33 */ { {0x8003, 108}, {0x8006, 108}, {0x800a, 108}, {0x800f, 108}, {0x8018, 108}, {0x801f, 108}, {0x8029, 108}, {0xc038, 108}, {0x8003, 109}, {0x8006, 109}, {0x800a, 109}, {0x800f, 109}, {0x8018, 109}, {0x801f, 109}, {0x8029, 109}, {0xc038, 109}, }, /* 34 */ { {0x8003, 110}, {0x8006, 110}, {0x800a, 110}, {0x800f, 110}, {0x8018, 110}, {0x801f, 110}, {0x8029, 110}, {0xc038, 110}, {0x8003, 112}, {0x8006, 112}, {0x800a, 112}, {0x800f, 112}, {0x8018, 112}, {0x801f, 112}, {0x8029, 112}, {0xc038, 112}, }, /* 35 */ { {0x8002, 114}, {0x8009, 114}, {0x8017, 114}, {0xc028, 114}, {0x8002, 117}, {0x8009, 117}, {0x8017, 117}, {0xc028, 117}, {0x8001, 58}, {0xc016, 58}, {0x8001, 66}, {0xc016, 66}, {0x8001, 67}, {0xc016, 67}, {0x8001, 68}, {0xc016, 68}, }, /* 36 */ { {0x8003, 114}, {0x8006, 114}, {0x800a, 114}, {0x800f, 114}, {0x8018, 114}, {0x801f, 114}, {0x8029, 114}, {0xc038, 114}, {0x8003, 117}, {0x8006, 117}, {0x800a, 117}, {0x800f, 117}, {0x8018, 117}, {0x801f, 117}, {0x8029, 117}, {0xc038, 117}, }, /* 37 */ { {0x8002, 58}, {0x8009, 58}, {0x8017, 58}, {0xc028, 58}, {0x8002, 66}, {0x8009, 66}, {0x8017, 66}, {0xc028, 66}, {0x8002, 67}, {0x8009, 67}, {0x8017, 67}, {0xc028, 67}, {0x8002, 68}, {0x8009, 68}, {0x8017, 68}, {0xc028, 68}, }, /* 38 */ { {0x8003, 58}, {0x8006, 58}, {0x800a, 58}, {0x800f, 58}, {0x8018, 58}, {0x801f, 58}, {0x8029, 58}, {0xc038, 58}, {0x8003, 66}, {0x8006, 66}, {0x800a, 66}, {0x800f, 66}, {0x8018, 66}, {0x801f, 66}, {0x8029, 66}, {0xc038, 66}, }, /* 39 */ { {0x8003, 67}, {0x8006, 67}, {0x800a, 67}, {0x800f, 67}, {0x8018, 67}, {0x801f, 67}, {0x8029, 67}, {0xc038, 67}, {0x8003, 68}, {0x8006, 68}, {0x800a, 68}, {0x800f, 68}, {0x8018, 68}, {0x801f, 68}, {0x8029, 68}, {0xc038, 68}, }, /* 40 */ { {0x2c, 0}, {0x2d, 0}, {0x2f, 0}, {0x30, 0}, {0x33, 0}, {0x34, 0}, {0x36, 0}, {0x37, 0}, {0x3b, 0}, {0x3c, 0}, {0x3e, 0}, {0x3f, 0}, {0x42, 0}, {0x43, 0}, {0x45, 0}, {0x4048, 0}, }, /* 41 */ { {0xc000, 69}, {0xc000, 70}, {0xc000, 71}, {0xc000, 72}, {0xc000, 73}, {0xc000, 74}, {0xc000, 75}, {0xc000, 76}, {0xc000, 77}, {0xc000, 78}, {0xc000, 79}, {0xc000, 80}, {0xc000, 81}, {0xc000, 82}, {0xc000, 83}, {0xc000, 84}, }, /* 42 */ { {0x8001, 69}, {0xc016, 69}, {0x8001, 70}, {0xc016, 70}, {0x8001, 71}, {0xc016, 71}, {0x8001, 72}, {0xc016, 72}, {0x8001, 73}, {0xc016, 73}, {0x8001, 74}, {0xc016, 74}, {0x8001, 75}, {0xc016, 75}, {0x8001, 76}, {0xc016, 76}, }, /* 43 */ { {0x8002, 69}, {0x8009, 69}, {0x8017, 69}, {0xc028, 69}, {0x8002, 70}, {0x8009, 70}, {0x8017, 70}, {0xc028, 70}, {0x8002, 71}, {0x8009, 71}, {0x8017, 71}, {0xc028, 71}, {0x8002, 72}, {0x8009, 72}, {0x8017, 72}, {0xc028, 72}, }, /* 44 */ { {0x8003, 69}, {0x8006, 69}, {0x800a, 69}, {0x800f, 69}, {0x8018, 69}, {0x801f, 69}, {0x8029, 69}, {0xc038, 69}, {0x8003, 70}, {0x8006, 70}, {0x800a, 70}, {0x800f, 70}, {0x8018, 70}, {0x801f, 70}, {0x8029, 70}, {0xc038, 70}, }, /* 45 */ { {0x8003, 71}, {0x8006, 71}, {0x800a, 71}, {0x800f, 71}, {0x8018, 71}, {0x801f, 71}, {0x8029, 71}, {0xc038, 71}, {0x8003, 72}, {0x8006, 72}, {0x800a, 72}, {0x800f, 72}, {0x8018, 72}, {0x801f, 72}, {0x8029, 72}, {0xc038, 72}, }, /* 46 */ { {0x8002, 73}, {0x8009, 73}, {0x8017, 73}, {0xc028, 73}, {0x8002, 74}, {0x8009, 74}, {0x8017, 74}, {0xc028, 74}, {0x8002, 75}, {0x8009, 75}, {0x8017, 75}, {0xc028, 75}, {0x8002, 76}, {0x8009, 76}, {0x8017, 76}, {0xc028, 76}, }, /* 47 */ { {0x8003, 73}, {0x8006, 73}, {0x800a, 73}, {0x800f, 73}, {0x8018, 73}, {0x801f, 73}, {0x8029, 73}, {0xc038, 73}, {0x8003, 74}, {0x8006, 74}, {0x800a, 74}, {0x800f, 74}, {0x8018, 74}, {0x801f, 74}, {0x8029, 74}, {0xc038, 74}, }, /* 48 */ { {0x8003, 75}, {0x8006, 75}, {0x800a, 75}, {0x800f, 75}, {0x8018, 75}, {0x801f, 75}, {0x8029, 75}, {0xc038, 75}, {0x8003, 76}, {0x8006, 76}, {0x800a, 76}, {0x800f, 76}, {0x8018, 76}, {0x801f, 76}, {0x8029, 76}, {0xc038, 76}, }, /* 49 */ { {0x8001, 77}, {0xc016, 77}, {0x8001, 78}, {0xc016, 78}, {0x8001, 79}, {0xc016, 79}, {0x8001, 80}, {0xc016, 80}, {0x8001, 81}, {0xc016, 81}, {0x8001, 82}, {0xc016, 82}, {0x8001, 83}, {0xc016, 83}, {0x8001, 84}, {0xc016, 84}, }, /* 50 */ { {0x8002, 77}, {0x8009, 77}, {0x8017, 77}, {0xc028, 77}, {0x8002, 78}, {0x8009, 78}, {0x8017, 78}, {0xc028, 78}, {0x8002, 79}, {0x8009, 79}, {0x8017, 79}, {0xc028, 79}, {0x8002, 80}, {0x8009, 80}, {0x8017, 80}, {0xc028, 80}, }, /* 51 */ { {0x8003, 77}, {0x8006, 77}, {0x800a, 77}, {0x800f, 77}, {0x8018, 77}, {0x801f, 77}, {0x8029, 77}, {0xc038, 77}, {0x8003, 78}, {0x8006, 78}, {0x800a, 78}, {0x800f, 78}, {0x8018, 78}, {0x801f, 78}, {0x8029, 78}, {0xc038, 78}, }, /* 52 */ { {0x8003, 79}, {0x8006, 79}, {0x800a, 79}, {0x800f, 79}, {0x8018, 79}, {0x801f, 79}, {0x8029, 79}, {0xc038, 79}, {0x8003, 80}, {0x8006, 80}, {0x800a, 80}, {0x800f, 80}, {0x8018, 80}, {0x801f, 80}, {0x8029, 80}, {0xc038, 80}, }, /* 53 */ { {0x8002, 81}, {0x8009, 81}, {0x8017, 81}, {0xc028, 81}, {0x8002, 82}, {0x8009, 82}, {0x8017, 82}, {0xc028, 82}, {0x8002, 83}, {0x8009, 83}, {0x8017, 83}, {0xc028, 83}, {0x8002, 84}, {0x8009, 84}, {0x8017, 84}, {0xc028, 84}, }, /* 54 */ { {0x8003, 81}, {0x8006, 81}, {0x800a, 81}, {0x800f, 81}, {0x8018, 81}, {0x801f, 81}, {0x8029, 81}, {0xc038, 81}, {0x8003, 82}, {0x8006, 82}, {0x800a, 82}, {0x800f, 82}, {0x8018, 82}, {0x801f, 82}, {0x8029, 82}, {0xc038, 82}, }, /* 55 */ { {0x8003, 83}, {0x8006, 83}, {0x800a, 83}, {0x800f, 83}, {0x8018, 83}, {0x801f, 83}, {0x8029, 83}, {0xc038, 83}, {0x8003, 84}, {0x8006, 84}, {0x800a, 84}, {0x800f, 84}, {0x8018, 84}, {0x801f, 84}, {0x8029, 84}, {0xc038, 84}, }, /* 56 */ { {0xc000, 85}, {0xc000, 86}, {0xc000, 87}, {0xc000, 89}, {0xc000, 106}, {0xc000, 107}, {0xc000, 113}, {0xc000, 118}, {0xc000, 119}, {0xc000, 120}, {0xc000, 121}, {0xc000, 122}, {0x46, 0}, {0x47, 0}, {0x49, 0}, {0x404a, 0}, }, /* 57 */ { {0x8001, 85}, {0xc016, 85}, {0x8001, 86}, {0xc016, 86}, {0x8001, 87}, {0xc016, 87}, {0x8001, 89}, {0xc016, 89}, {0x8001, 106}, {0xc016, 106}, {0x8001, 107}, {0xc016, 107}, {0x8001, 113}, {0xc016, 113}, {0x8001, 118}, {0xc016, 118}, }, /* 58 */ { {0x8002, 85}, {0x8009, 85}, {0x8017, 85}, {0xc028, 85}, {0x8002, 86}, {0x8009, 86}, {0x8017, 86}, {0xc028, 86}, {0x8002, 87}, {0x8009, 87}, {0x8017, 87}, {0xc028, 87}, {0x8002, 89}, {0x8009, 89}, {0x8017, 89}, {0xc028, 89}, }, /* 59 */ { {0x8003, 85}, {0x8006, 85}, {0x800a, 85}, {0x800f, 85}, {0x8018, 85}, {0x801f, 85}, {0x8029, 85}, {0xc038, 85}, {0x8003, 86}, {0x8006, 86}, {0x800a, 86}, {0x800f, 86}, {0x8018, 86}, {0x801f, 86}, {0x8029, 86}, {0xc038, 86}, }, /* 60 */ { {0x8003, 87}, {0x8006, 87}, {0x800a, 87}, {0x800f, 87}, {0x8018, 87}, {0x801f, 87}, {0x8029, 87}, {0xc038, 87}, {0x8003, 89}, {0x8006, 89}, {0x800a, 89}, {0x800f, 89}, {0x8018, 89}, {0x801f, 89}, {0x8029, 89}, {0xc038, 89}, }, /* 61 */ { {0x8002, 106}, {0x8009, 106}, {0x8017, 106}, {0xc028, 106}, {0x8002, 107}, {0x8009, 107}, {0x8017, 107}, {0xc028, 107}, {0x8002, 113}, {0x8009, 113}, {0x8017, 113}, {0xc028, 113}, {0x8002, 118}, {0x8009, 118}, {0x8017, 118}, {0xc028, 118}, }, /* 62 */ { {0x8003, 106}, {0x8006, 106}, {0x800a, 106}, {0x800f, 106}, {0x8018, 106}, {0x801f, 106}, {0x8029, 106}, {0xc038, 106}, {0x8003, 107}, {0x8006, 107}, {0x800a, 107}, {0x800f, 107}, {0x8018, 107}, {0x801f, 107}, {0x8029, 107}, {0xc038, 107}, }, /* 63 */ { {0x8003, 113}, {0x8006, 113}, {0x800a, 113}, {0x800f, 113}, {0x8018, 113}, {0x801f, 113}, {0x8029, 113}, {0xc038, 113}, {0x8003, 118}, {0x8006, 118}, {0x800a, 118}, {0x800f, 118}, {0x8018, 118}, {0x801f, 118}, {0x8029, 118}, {0xc038, 118}, }, /* 64 */ { {0x8001, 119}, {0xc016, 119}, {0x8001, 120}, {0xc016, 120}, {0x8001, 121}, {0xc016, 121}, {0x8001, 122}, {0xc016, 122}, {0xc000, 38}, {0xc000, 42}, {0xc000, 44}, {0xc000, 59}, {0xc000, 88}, {0xc000, 90}, {0x4b, 0}, {0x4e, 0}, }, /* 65 */ { {0x8002, 119}, {0x8009, 119}, {0x8017, 119}, {0xc028, 119}, {0x8002, 120}, {0x8009, 120}, {0x8017, 120}, {0xc028, 120}, {0x8002, 121}, {0x8009, 121}, {0x8017, 121}, {0xc028, 121}, {0x8002, 122}, {0x8009, 122}, {0x8017, 122}, {0xc028, 122}, }, /* 66 */ { {0x8003, 119}, {0x8006, 119}, {0x800a, 119}, {0x800f, 119}, {0x8018, 119}, {0x801f, 119}, {0x8029, 119}, {0xc038, 119}, {0x8003, 120}, {0x8006, 120}, {0x800a, 120}, {0x800f, 120}, {0x8018, 120}, {0x801f, 120}, {0x8029, 120}, {0xc038, 120}, }, /* 67 */ { {0x8003, 121}, {0x8006, 121}, {0x800a, 121}, {0x800f, 121}, {0x8018, 121}, {0x801f, 121}, {0x8029, 121}, {0xc038, 121}, {0x8003, 122}, {0x8006, 122}, {0x800a, 122}, {0x800f, 122}, {0x8018, 122}, {0x801f, 122}, {0x8029, 122}, {0xc038, 122}, }, /* 68 */ { {0x8001, 38}, {0xc016, 38}, {0x8001, 42}, {0xc016, 42}, {0x8001, 44}, {0xc016, 44}, {0x8001, 59}, {0xc016, 59}, {0x8001, 88}, {0xc016, 88}, {0x8001, 90}, {0xc016, 90}, {0x4c, 0}, {0x4d, 0}, {0x4f, 0}, {0x51, 0}, }, /* 69 */ { {0x8002, 38}, {0x8009, 38}, {0x8017, 38}, {0xc028, 38}, {0x8002, 42}, {0x8009, 42}, {0x8017, 42}, {0xc028, 42}, {0x8002, 44}, {0x8009, 44}, {0x8017, 44}, {0xc028, 44}, {0x8002, 59}, {0x8009, 59}, {0x8017, 59}, {0xc028, 59}, }, /* 70 */ { {0x8003, 38}, {0x8006, 38}, {0x800a, 38}, {0x800f, 38}, {0x8018, 38}, {0x801f, 38}, {0x8029, 38}, {0xc038, 38}, {0x8003, 42}, {0x8006, 42}, {0x800a, 42}, {0x800f, 42}, {0x8018, 42}, {0x801f, 42}, {0x8029, 42}, {0xc038, 42}, }, /* 71 */ { {0x8003, 44}, {0x8006, 44}, {0x800a, 44}, {0x800f, 44}, {0x8018, 44}, {0x801f, 44}, {0x8029, 44}, {0xc038, 44}, {0x8003, 59}, {0x8006, 59}, {0x800a, 59}, {0x800f, 59}, {0x8018, 59}, {0x801f, 59}, {0x8029, 59}, {0xc038, 59}, }, /* 72 */ { {0x8002, 88}, {0x8009, 88}, {0x8017, 88}, {0xc028, 88}, {0x8002, 90}, {0x8009, 90}, {0x8017, 90}, {0xc028, 90}, {0xc000, 33}, {0xc000, 34}, {0xc000, 40}, {0xc000, 41}, {0xc000, 63}, {0x50, 0}, {0x52, 0}, {0x54, 0}, }, /* 73 */ { {0x8003, 88}, {0x8006, 88}, {0x800a, 88}, {0x800f, 88}, {0x8018, 88}, {0x801f, 88}, {0x8029, 88}, {0xc038, 88}, {0x8003, 90}, {0x8006, 90}, {0x800a, 90}, {0x800f, 90}, {0x8018, 90}, {0x801f, 90}, {0x8029, 90}, {0xc038, 90}, }, /* 74 */ { {0x8001, 33}, {0xc016, 33}, {0x8001, 34}, {0xc016, 34}, {0x8001, 40}, {0xc016, 40}, {0x8001, 41}, {0xc016, 41}, {0x8001, 63}, {0xc016, 63}, {0xc000, 39}, {0xc000, 43}, {0xc000, 124}, {0x53, 0}, {0x55, 0}, {0x58, 0}, }, /* 75 */ { {0x8002, 33}, {0x8009, 33}, {0x8017, 33}, {0xc028, 33}, {0x8002, 34}, {0x8009, 34}, {0x8017, 34}, {0xc028, 34}, {0x8002, 40}, {0x8009, 40}, {0x8017, 40}, {0xc028, 40}, {0x8002, 41}, {0x8009, 41}, {0x8017, 41}, {0xc028, 41}, }, /* 76 */ { {0x8003, 33}, {0x8006, 33}, {0x800a, 33}, {0x800f, 33}, {0x8018, 33}, {0x801f, 33}, {0x8029, 33}, {0xc038, 33}, {0x8003, 34}, {0x8006, 34}, {0x800a, 34}, {0x800f, 34}, {0x8018, 34}, {0x801f, 34}, {0x8029, 34}, {0xc038, 34}, }, /* 77 */ { {0x8003, 40}, {0x8006, 40}, {0x800a, 40}, {0x800f, 40}, {0x8018, 40}, {0x801f, 40}, {0x8029, 40}, {0xc038, 40}, {0x8003, 41}, {0x8006, 41}, {0x800a, 41}, {0x800f, 41}, {0x8018, 41}, {0x801f, 41}, {0x8029, 41}, {0xc038, 41}, }, /* 78 */ { {0x8002, 63}, {0x8009, 63}, {0x8017, 63}, {0xc028, 63}, {0x8001, 39}, {0xc016, 39}, {0x8001, 43}, {0xc016, 43}, {0x8001, 124}, {0xc016, 124}, {0xc000, 35}, {0xc000, 62}, {0x56, 0}, {0x57, 0}, {0x59, 0}, {0x5a, 0}, }, /* 79 */ { {0x8003, 63}, {0x8006, 63}, {0x800a, 63}, {0x800f, 63}, {0x8018, 63}, {0x801f, 63}, {0x8029, 63}, {0xc038, 63}, {0x8002, 39}, {0x8009, 39}, {0x8017, 39}, {0xc028, 39}, {0x8002, 43}, {0x8009, 43}, {0x8017, 43}, {0xc028, 43}, }, /* 80 */ { {0x8003, 39}, {0x8006, 39}, {0x800a, 39}, {0x800f, 39}, {0x8018, 39}, {0x801f, 39}, {0x8029, 39}, {0xc038, 39}, {0x8003, 43}, {0x8006, 43}, {0x800a, 43}, {0x800f, 43}, {0x8018, 43}, {0x801f, 43}, {0x8029, 43}, {0xc038, 43}, }, /* 81 */ { {0x8002, 124}, {0x8009, 124}, {0x8017, 124}, {0xc028, 124}, {0x8001, 35}, {0xc016, 35}, {0x8001, 62}, {0xc016, 62}, {0xc000, 0}, {0xc000, 36}, {0xc000, 64}, {0xc000, 91}, {0xc000, 93}, {0xc000, 126}, {0x5b, 0}, {0x5c, 0}, }, /* 82 */ { {0x8003, 124}, {0x8006, 124}, {0x800a, 124}, {0x800f, 124}, {0x8018, 124}, {0x801f, 124}, {0x8029, 124}, {0xc038, 124}, {0x8002, 35}, {0x8009, 35}, {0x8017, 35}, {0xc028, 35}, {0x8002, 62}, {0x8009, 62}, {0x8017, 62}, {0xc028, 62}, }, /* 83 */ { {0x8003, 35}, {0x8006, 35}, {0x800a, 35}, {0x800f, 35}, {0x8018, 35}, {0x801f, 35}, {0x8029, 35}, {0xc038, 35}, {0x8003, 62}, {0x8006, 62}, {0x800a, 62}, {0x800f, 62}, {0x8018, 62}, {0x801f, 62}, {0x8029, 62}, {0xc038, 62}, }, /* 84 */ { {0x8001, 0}, {0xc016, 0}, {0x8001, 36}, {0xc016, 36}, {0x8001, 64}, {0xc016, 64}, {0x8001, 91}, {0xc016, 91}, {0x8001, 93}, {0xc016, 93}, {0x8001, 126}, {0xc016, 126}, {0xc000, 94}, {0xc000, 125}, {0x5d, 0}, {0x5e, 0}, }, /* 85 */ { {0x8002, 0}, {0x8009, 0}, {0x8017, 0}, {0xc028, 0}, {0x8002, 36}, {0x8009, 36}, {0x8017, 36}, {0xc028, 36}, {0x8002, 64}, {0x8009, 64}, {0x8017, 64}, {0xc028, 64}, {0x8002, 91}, {0x8009, 91}, {0x8017, 91}, {0xc028, 91}, }, /* 86 */ { {0x8003, 0}, {0x8006, 0}, {0x800a, 0}, {0x800f, 0}, {0x8018, 0}, {0x801f, 0}, {0x8029, 0}, {0xc038, 0}, {0x8003, 36}, {0x8006, 36}, {0x800a, 36}, {0x800f, 36}, {0x8018, 36}, {0x801f, 36}, {0x8029, 36}, {0xc038, 36}, }, /* 87 */ { {0x8003, 64}, {0x8006, 64}, {0x800a, 64}, {0x800f, 64}, {0x8018, 64}, {0x801f, 64}, {0x8029, 64}, {0xc038, 64}, {0x8003, 91}, {0x8006, 91}, {0x800a, 91}, {0x800f, 91}, {0x8018, 91}, {0x801f, 91}, {0x8029, 91}, {0xc038, 91}, }, /* 88 */ { {0x8002, 93}, {0x8009, 93}, {0x8017, 93}, {0xc028, 93}, {0x8002, 126}, {0x8009, 126}, {0x8017, 126}, {0xc028, 126}, {0x8001, 94}, {0xc016, 94}, {0x8001, 125}, {0xc016, 125}, {0xc000, 60}, {0xc000, 96}, {0xc000, 123}, {0x5f, 0}, }, /* 89 */ { {0x8003, 93}, {0x8006, 93}, {0x800a, 93}, {0x800f, 93}, {0x8018, 93}, {0x801f, 93}, {0x8029, 93}, {0xc038, 93}, {0x8003, 126}, {0x8006, 126}, {0x800a, 126}, {0x800f, 126}, {0x8018, 126}, {0x801f, 126}, {0x8029, 126}, {0xc038, 126}, }, /* 90 */ { {0x8002, 94}, {0x8009, 94}, {0x8017, 94}, {0xc028, 94}, {0x8002, 125}, {0x8009, 125}, {0x8017, 125}, {0xc028, 125}, {0x8001, 60}, {0xc016, 60}, {0x8001, 96}, {0xc016, 96}, {0x8001, 123}, {0xc016, 123}, {0x60, 0}, {0x6e, 0}, }, /* 91 */ { {0x8003, 94}, {0x8006, 94}, {0x800a, 94}, {0x800f, 94}, {0x8018, 94}, {0x801f, 94}, {0x8029, 94}, {0xc038, 94}, {0x8003, 125}, {0x8006, 125}, {0x800a, 125}, {0x800f, 125}, {0x8018, 125}, {0x801f, 125}, {0x8029, 125}, {0xc038, 125}, }, /* 92 */ { {0x8002, 60}, {0x8009, 60}, {0x8017, 60}, {0xc028, 60}, {0x8002, 96}, {0x8009, 96}, {0x8017, 96}, {0xc028, 96}, {0x8002, 123}, {0x8009, 123}, {0x8017, 123}, {0xc028, 123}, {0x61, 0}, {0x65, 0}, {0x6f, 0}, {0x85, 0}, }, /* 93 */ { {0x8003, 60}, {0x8006, 60}, {0x800a, 60}, {0x800f, 60}, {0x8018, 60}, {0x801f, 60}, {0x8029, 60}, {0xc038, 60}, {0x8003, 96}, {0x8006, 96}, {0x800a, 96}, {0x800f, 96}, {0x8018, 96}, {0x801f, 96}, {0x8029, 96}, {0xc038, 96}, }, /* 94 */ { {0x8003, 123}, {0x8006, 123}, {0x800a, 123}, {0x800f, 123}, {0x8018, 123}, {0x801f, 123}, {0x8029, 123}, {0xc038, 123}, {0x62, 0}, {0x63, 0}, {0x66, 0}, {0x69, 0}, {0x70, 0}, {0x77, 0}, {0x86, 0}, {0x99, 0}, }, /* 95 */ { {0xc000, 92}, {0xc000, 195}, {0xc000, 208}, {0x64, 0}, {0x67, 0}, {0x68, 0}, {0x6a, 0}, {0x6b, 0}, {0x71, 0}, {0x74, 0}, {0x78, 0}, {0x7e, 0}, {0x87, 0}, {0x8e, 0}, {0x9a, 0}, {0xa9, 0}, }, /* 96 */ { {0x8001, 92}, {0xc016, 92}, {0x8001, 195}, {0xc016, 195}, {0x8001, 208}, {0xc016, 208}, {0xc000, 128}, {0xc000, 130}, {0xc000, 131}, {0xc000, 162}, {0xc000, 184}, {0xc000, 194}, {0xc000, 224}, {0xc000, 226}, {0x6c, 0}, {0x6d, 0}, }, /* 97 */ { {0x8002, 92}, {0x8009, 92}, {0x8017, 92}, {0xc028, 92}, {0x8002, 195}, {0x8009, 195}, {0x8017, 195}, {0xc028, 195}, {0x8002, 208}, {0x8009, 208}, {0x8017, 208}, {0xc028, 208}, {0x8001, 128}, {0xc016, 128}, {0x8001, 130}, {0xc016, 130}, }, /* 98 */ { {0x8003, 92}, {0x8006, 92}, {0x800a, 92}, {0x800f, 92}, {0x8018, 92}, {0x801f, 92}, {0x8029, 92}, {0xc038, 92}, {0x8003, 195}, {0x8006, 195}, {0x800a, 195}, {0x800f, 195}, {0x8018, 195}, {0x801f, 195}, {0x8029, 195}, {0xc038, 195}, }, /* 99 */ { {0x8003, 208}, {0x8006, 208}, {0x800a, 208}, {0x800f, 208}, {0x8018, 208}, {0x801f, 208}, {0x8029, 208}, {0xc038, 208}, {0x8002, 128}, {0x8009, 128}, {0x8017, 128}, {0xc028, 128}, {0x8002, 130}, {0x8009, 130}, {0x8017, 130}, {0xc028, 130}, }, /* 100 */ { {0x8003, 128}, {0x8006, 128}, {0x800a, 128}, {0x800f, 128}, {0x8018, 128}, {0x801f, 128}, {0x8029, 128}, {0xc038, 128}, {0x8003, 130}, {0x8006, 130}, {0x800a, 130}, {0x800f, 130}, {0x8018, 130}, {0x801f, 130}, {0x8029, 130}, {0xc038, 130}, }, /* 101 */ { {0x8001, 131}, {0xc016, 131}, {0x8001, 162}, {0xc016, 162}, {0x8001, 184}, {0xc016, 184}, {0x8001, 194}, {0xc016, 194}, {0x8001, 224}, {0xc016, 224}, {0x8001, 226}, {0xc016, 226}, {0xc000, 153}, {0xc000, 161}, {0xc000, 167}, {0xc000, 172}, }, /* 102 */ { {0x8002, 131}, {0x8009, 131}, {0x8017, 131}, {0xc028, 131}, {0x8002, 162}, {0x8009, 162}, {0x8017, 162}, {0xc028, 162}, {0x8002, 184}, {0x8009, 184}, {0x8017, 184}, {0xc028, 184}, {0x8002, 194}, {0x8009, 194}, {0x8017, 194}, {0xc028, 194}, }, /* 103 */ { {0x8003, 131}, {0x8006, 131}, {0x800a, 131}, {0x800f, 131}, {0x8018, 131}, {0x801f, 131}, {0x8029, 131}, {0xc038, 131}, {0x8003, 162}, {0x8006, 162}, {0x800a, 162}, {0x800f, 162}, {0x8018, 162}, {0x801f, 162}, {0x8029, 162}, {0xc038, 162}, }, /* 104 */ { {0x8003, 184}, {0x8006, 184}, {0x800a, 184}, {0x800f, 184}, {0x8018, 184}, {0x801f, 184}, {0x8029, 184}, {0xc038, 184}, {0x8003, 194}, {0x8006, 194}, {0x800a, 194}, {0x800f, 194}, {0x8018, 194}, {0x801f, 194}, {0x8029, 194}, {0xc038, 194}, }, /* 105 */ { {0x8002, 224}, {0x8009, 224}, {0x8017, 224}, {0xc028, 224}, {0x8002, 226}, {0x8009, 226}, {0x8017, 226}, {0xc028, 226}, {0x8001, 153}, {0xc016, 153}, {0x8001, 161}, {0xc016, 161}, {0x8001, 167}, {0xc016, 167}, {0x8001, 172}, {0xc016, 172}, }, /* 106 */ { {0x8003, 224}, {0x8006, 224}, {0x800a, 224}, {0x800f, 224}, {0x8018, 224}, {0x801f, 224}, {0x8029, 224}, {0xc038, 224}, {0x8003, 226}, {0x8006, 226}, {0x800a, 226}, {0x800f, 226}, {0x8018, 226}, {0x801f, 226}, {0x8029, 226}, {0xc038, 226}, }, /* 107 */ { {0x8002, 153}, {0x8009, 153}, {0x8017, 153}, {0xc028, 153}, {0x8002, 161}, {0x8009, 161}, {0x8017, 161}, {0xc028, 161}, {0x8002, 167}, {0x8009, 167}, {0x8017, 167}, {0xc028, 167}, {0x8002, 172}, {0x8009, 172}, {0x8017, 172}, {0xc028, 172}, }, /* 108 */ { {0x8003, 153}, {0x8006, 153}, {0x800a, 153}, {0x800f, 153}, {0x8018, 153}, {0x801f, 153}, {0x8029, 153}, {0xc038, 153}, {0x8003, 161}, {0x8006, 161}, {0x800a, 161}, {0x800f, 161}, {0x8018, 161}, {0x801f, 161}, {0x8029, 161}, {0xc038, 161}, }, /* 109 */ { {0x8003, 167}, {0x8006, 167}, {0x800a, 167}, {0x800f, 167}, {0x8018, 167}, {0x801f, 167}, {0x8029, 167}, {0xc038, 167}, {0x8003, 172}, {0x8006, 172}, {0x800a, 172}, {0x800f, 172}, {0x8018, 172}, {0x801f, 172}, {0x8029, 172}, {0xc038, 172}, }, /* 110 */ { {0x72, 0}, {0x73, 0}, {0x75, 0}, {0x76, 0}, {0x79, 0}, {0x7b, 0}, {0x7f, 0}, {0x82, 0}, {0x88, 0}, {0x8b, 0}, {0x8f, 0}, {0x92, 0}, {0x9b, 0}, {0xa2, 0}, {0xaa, 0}, {0xb4, 0}, }, /* 111 */ { {0xc000, 176}, {0xc000, 177}, {0xc000, 179}, {0xc000, 209}, {0xc000, 216}, {0xc000, 217}, {0xc000, 227}, {0xc000, 229}, {0xc000, 230}, {0x7a, 0}, {0x7c, 0}, {0x7d, 0}, {0x80, 0}, {0x81, 0}, {0x83, 0}, {0x84, 0}, }, /* 112 */ { {0x8001, 176}, {0xc016, 176}, {0x8001, 177}, {0xc016, 177}, {0x8001, 179}, {0xc016, 179}, {0x8001, 209}, {0xc016, 209}, {0x8001, 216}, {0xc016, 216}, {0x8001, 217}, {0xc016, 217}, {0x8001, 227}, {0xc016, 227}, {0x8001, 229}, {0xc016, 229}, }, /* 113 */ { {0x8002, 176}, {0x8009, 176}, {0x8017, 176}, {0xc028, 176}, {0x8002, 177}, {0x8009, 177}, {0x8017, 177}, {0xc028, 177}, {0x8002, 179}, {0x8009, 179}, {0x8017, 179}, {0xc028, 179}, {0x8002, 209}, {0x8009, 209}, {0x8017, 209}, {0xc028, 209}, }, /* 114 */ { {0x8003, 176}, {0x8006, 176}, {0x800a, 176}, {0x800f, 176}, {0x8018, 176}, {0x801f, 176}, {0x8029, 176}, {0xc038, 176}, {0x8003, 177}, {0x8006, 177}, {0x800a, 177}, {0x800f, 177}, {0x8018, 177}, {0x801f, 177}, {0x8029, 177}, {0xc038, 177}, }, /* 115 */ { {0x8003, 179}, {0x8006, 179}, {0x800a, 179}, {0x800f, 179}, {0x8018, 179}, {0x801f, 179}, {0x8029, 179}, {0xc038, 179}, {0x8003, 209}, {0x8006, 209}, {0x800a, 209}, {0x800f, 209}, {0x8018, 209}, {0x801f, 209}, {0x8029, 209}, {0xc038, 209}, }, /* 116 */ { {0x8002, 216}, {0x8009, 216}, {0x8017, 216}, {0xc028, 216}, {0x8002, 217}, {0x8009, 217}, {0x8017, 217}, {0xc028, 217}, {0x8002, 227}, {0x8009, 227}, {0x8017, 227}, {0xc028, 227}, {0x8002, 229}, {0x8009, 229}, {0x8017, 229}, {0xc028, 229}, }, /* 117 */ { {0x8003, 216}, {0x8006, 216}, {0x800a, 216}, {0x800f, 216}, {0x8018, 216}, {0x801f, 216}, {0x8029, 216}, {0xc038, 216}, {0x8003, 217}, {0x8006, 217}, {0x800a, 217}, {0x800f, 217}, {0x8018, 217}, {0x801f, 217}, {0x8029, 217}, {0xc038, 217}, }, /* 118 */ { {0x8003, 227}, {0x8006, 227}, {0x800a, 227}, {0x800f, 227}, {0x8018, 227}, {0x801f, 227}, {0x8029, 227}, {0xc038, 227}, {0x8003, 229}, {0x8006, 229}, {0x800a, 229}, {0x800f, 229}, {0x8018, 229}, {0x801f, 229}, {0x8029, 229}, {0xc038, 229}, }, /* 119 */ { {0x8001, 230}, {0xc016, 230}, {0xc000, 129}, {0xc000, 132}, {0xc000, 133}, {0xc000, 134}, {0xc000, 136}, {0xc000, 146}, {0xc000, 154}, {0xc000, 156}, {0xc000, 160}, {0xc000, 163}, {0xc000, 164}, {0xc000, 169}, {0xc000, 170}, {0xc000, 173}, }, /* 120 */ { {0x8002, 230}, {0x8009, 230}, {0x8017, 230}, {0xc028, 230}, {0x8001, 129}, {0xc016, 129}, {0x8001, 132}, {0xc016, 132}, {0x8001, 133}, {0xc016, 133}, {0x8001, 134}, {0xc016, 134}, {0x8001, 136}, {0xc016, 136}, {0x8001, 146}, {0xc016, 146}, }, /* 121 */ { {0x8003, 230}, {0x8006, 230}, {0x800a, 230}, {0x800f, 230}, {0x8018, 230}, {0x801f, 230}, {0x8029, 230}, {0xc038, 230}, {0x8002, 129}, {0x8009, 129}, {0x8017, 129}, {0xc028, 129}, {0x8002, 132}, {0x8009, 132}, {0x8017, 132}, {0xc028, 132}, }, /* 122 */ { {0x8003, 129}, {0x8006, 129}, {0x800a, 129}, {0x800f, 129}, {0x8018, 129}, {0x801f, 129}, {0x8029, 129}, {0xc038, 129}, {0x8003, 132}, {0x8006, 132}, {0x800a, 132}, {0x800f, 132}, {0x8018, 132}, {0x801f, 132}, {0x8029, 132}, {0xc038, 132}, }, /* 123 */ { {0x8002, 133}, {0x8009, 133}, {0x8017, 133}, {0xc028, 133}, {0x8002, 134}, {0x8009, 134}, {0x8017, 134}, {0xc028, 134}, {0x8002, 136}, {0x8009, 136}, {0x8017, 136}, {0xc028, 136}, {0x8002, 146}, {0x8009, 146}, {0x8017, 146}, {0xc028, 146}, }, /* 124 */ { {0x8003, 133}, {0x8006, 133}, {0x800a, 133}, {0x800f, 133}, {0x8018, 133}, {0x801f, 133}, {0x8029, 133}, {0xc038, 133}, {0x8003, 134}, {0x8006, 134}, {0x800a, 134}, {0x800f, 134}, {0x8018, 134}, {0x801f, 134}, {0x8029, 134}, {0xc038, 134}, }, /* 125 */ { {0x8003, 136}, {0x8006, 136}, {0x800a, 136}, {0x800f, 136}, {0x8018, 136}, {0x801f, 136}, {0x8029, 136}, {0xc038, 136}, {0x8003, 146}, {0x8006, 146}, {0x800a, 146}, {0x800f, 146}, {0x8018, 146}, {0x801f, 146}, {0x8029, 146}, {0xc038, 146}, }, /* 126 */ { {0x8001, 154}, {0xc016, 154}, {0x8001, 156}, {0xc016, 156}, {0x8001, 160}, {0xc016, 160}, {0x8001, 163}, {0xc016, 163}, {0x8001, 164}, {0xc016, 164}, {0x8001, 169}, {0xc016, 169}, {0x8001, 170}, {0xc016, 170}, {0x8001, 173}, {0xc016, 173}, }, /* 127 */ { {0x8002, 154}, {0x8009, 154}, {0x8017, 154}, {0xc028, 154}, {0x8002, 156}, {0x8009, 156}, {0x8017, 156}, {0xc028, 156}, {0x8002, 160}, {0x8009, 160}, {0x8017, 160}, {0xc028, 160}, {0x8002, 163}, {0x8009, 163}, {0x8017, 163}, {0xc028, 163}, }, /* 128 */ { {0x8003, 154}, {0x8006, 154}, {0x800a, 154}, {0x800f, 154}, {0x8018, 154}, {0x801f, 154}, {0x8029, 154}, {0xc038, 154}, {0x8003, 156}, {0x8006, 156}, {0x800a, 156}, {0x800f, 156}, {0x8018, 156}, {0x801f, 156}, {0x8029, 156}, {0xc038, 156}, }, /* 129 */ { {0x8003, 160}, {0x8006, 160}, {0x800a, 160}, {0x800f, 160}, {0x8018, 160}, {0x801f, 160}, {0x8029, 160}, {0xc038, 160}, {0x8003, 163}, {0x8006, 163}, {0x800a, 163}, {0x800f, 163}, {0x8018, 163}, {0x801f, 163}, {0x8029, 163}, {0xc038, 163}, }, /* 130 */ { {0x8002, 164}, {0x8009, 164}, {0x8017, 164}, {0xc028, 164}, {0x8002, 169}, {0x8009, 169}, {0x8017, 169}, {0xc028, 169}, {0x8002, 170}, {0x8009, 170}, {0x8017, 170}, {0xc028, 170}, {0x8002, 173}, {0x8009, 173}, {0x8017, 173}, {0xc028, 173}, }, /* 131 */ { {0x8003, 164}, {0x8006, 164}, {0x800a, 164}, {0x800f, 164}, {0x8018, 164}, {0x801f, 164}, {0x8029, 164}, {0xc038, 164}, {0x8003, 169}, {0x8006, 169}, {0x800a, 169}, {0x800f, 169}, {0x8018, 169}, {0x801f, 169}, {0x8029, 169}, {0xc038, 169}, }, /* 132 */ { {0x8003, 170}, {0x8006, 170}, {0x800a, 170}, {0x800f, 170}, {0x8018, 170}, {0x801f, 170}, {0x8029, 170}, {0xc038, 170}, {0x8003, 173}, {0x8006, 173}, {0x800a, 173}, {0x800f, 173}, {0x8018, 173}, {0x801f, 173}, {0x8029, 173}, {0xc038, 173}, }, /* 133 */ { {0x89, 0}, {0x8a, 0}, {0x8c, 0}, {0x8d, 0}, {0x90, 0}, {0x91, 0}, {0x93, 0}, {0x96, 0}, {0x9c, 0}, {0x9f, 0}, {0xa3, 0}, {0xa6, 0}, {0xab, 0}, {0xae, 0}, {0xb5, 0}, {0xbe, 0}, }, /* 134 */ { {0xc000, 178}, {0xc000, 181}, {0xc000, 185}, {0xc000, 186}, {0xc000, 187}, {0xc000, 189}, {0xc000, 190}, {0xc000, 196}, {0xc000, 198}, {0xc000, 228}, {0xc000, 232}, {0xc000, 233}, {0x94, 0}, {0x95, 0}, {0x97, 0}, {0x98, 0}, }, /* 135 */ { {0x8001, 178}, {0xc016, 178}, {0x8001, 181}, {0xc016, 181}, {0x8001, 185}, {0xc016, 185}, {0x8001, 186}, {0xc016, 186}, {0x8001, 187}, {0xc016, 187}, {0x8001, 189}, {0xc016, 189}, {0x8001, 190}, {0xc016, 190}, {0x8001, 196}, {0xc016, 196}, }, /* 136 */ { {0x8002, 178}, {0x8009, 178}, {0x8017, 178}, {0xc028, 178}, {0x8002, 181}, {0x8009, 181}, {0x8017, 181}, {0xc028, 181}, {0x8002, 185}, {0x8009, 185}, {0x8017, 185}, {0xc028, 185}, {0x8002, 186}, {0x8009, 186}, {0x8017, 186}, {0xc028, 186}, }, /* 137 */ { {0x8003, 178}, {0x8006, 178}, {0x800a, 178}, {0x800f, 178}, {0x8018, 178}, {0x801f, 178}, {0x8029, 178}, {0xc038, 178}, {0x8003, 181}, {0x8006, 181}, {0x800a, 181}, {0x800f, 181}, {0x8018, 181}, {0x801f, 181}, {0x8029, 181}, {0xc038, 181}, }, /* 138 */ { {0x8003, 185}, {0x8006, 185}, {0x800a, 185}, {0x800f, 185}, {0x8018, 185}, {0x801f, 185}, {0x8029, 185}, {0xc038, 185}, {0x8003, 186}, {0x8006, 186}, {0x800a, 186}, {0x800f, 186}, {0x8018, 186}, {0x801f, 186}, {0x8029, 186}, {0xc038, 186}, }, /* 139 */ { {0x8002, 187}, {0x8009, 187}, {0x8017, 187}, {0xc028, 187}, {0x8002, 189}, {0x8009, 189}, {0x8017, 189}, {0xc028, 189}, {0x8002, 190}, {0x8009, 190}, {0x8017, 190}, {0xc028, 190}, {0x8002, 196}, {0x8009, 196}, {0x8017, 196}, {0xc028, 196}, }, /* 140 */ { {0x8003, 187}, {0x8006, 187}, {0x800a, 187}, {0x800f, 187}, {0x8018, 187}, {0x801f, 187}, {0x8029, 187}, {0xc038, 187}, {0x8003, 189}, {0x8006, 189}, {0x800a, 189}, {0x800f, 189}, {0x8018, 189}, {0x801f, 189}, {0x8029, 189}, {0xc038, 189}, }, /* 141 */ { {0x8003, 190}, {0x8006, 190}, {0x800a, 190}, {0x800f, 190}, {0x8018, 190}, {0x801f, 190}, {0x8029, 190}, {0xc038, 190}, {0x8003, 196}, {0x8006, 196}, {0x800a, 196}, {0x800f, 196}, {0x8018, 196}, {0x801f, 196}, {0x8029, 196}, {0xc038, 196}, }, /* 142 */ { {0x8001, 198}, {0xc016, 198}, {0x8001, 228}, {0xc016, 228}, {0x8001, 232}, {0xc016, 232}, {0x8001, 233}, {0xc016, 233}, {0xc000, 1}, {0xc000, 135}, {0xc000, 137}, {0xc000, 138}, {0xc000, 139}, {0xc000, 140}, {0xc000, 141}, {0xc000, 143}, }, /* 143 */ { {0x8002, 198}, {0x8009, 198}, {0x8017, 198}, {0xc028, 198}, {0x8002, 228}, {0x8009, 228}, {0x8017, 228}, {0xc028, 228}, {0x8002, 232}, {0x8009, 232}, {0x8017, 232}, {0xc028, 232}, {0x8002, 233}, {0x8009, 233}, {0x8017, 233}, {0xc028, 233}, }, /* 144 */ { {0x8003, 198}, {0x8006, 198}, {0x800a, 198}, {0x800f, 198}, {0x8018, 198}, {0x801f, 198}, {0x8029, 198}, {0xc038, 198}, {0x8003, 228}, {0x8006, 228}, {0x800a, 228}, {0x800f, 228}, {0x8018, 228}, {0x801f, 228}, {0x8029, 228}, {0xc038, 228}, }, /* 145 */ { {0x8003, 232}, {0x8006, 232}, {0x800a, 232}, {0x800f, 232}, {0x8018, 232}, {0x801f, 232}, {0x8029, 232}, {0xc038, 232}, {0x8003, 233}, {0x8006, 233}, {0x800a, 233}, {0x800f, 233}, {0x8018, 233}, {0x801f, 233}, {0x8029, 233}, {0xc038, 233}, }, /* 146 */ { {0x8001, 1}, {0xc016, 1}, {0x8001, 135}, {0xc016, 135}, {0x8001, 137}, {0xc016, 137}, {0x8001, 138}, {0xc016, 138}, {0x8001, 139}, {0xc016, 139}, {0x8001, 140}, {0xc016, 140}, {0x8001, 141}, {0xc016, 141}, {0x8001, 143}, {0xc016, 143}, }, /* 147 */ { {0x8002, 1}, {0x8009, 1}, {0x8017, 1}, {0xc028, 1}, {0x8002, 135}, {0x8009, 135}, {0x8017, 135}, {0xc028, 135}, {0x8002, 137}, {0x8009, 137}, {0x8017, 137}, {0xc028, 137}, {0x8002, 138}, {0x8009, 138}, {0x8017, 138}, {0xc028, 138}, }, /* 148 */ { {0x8003, 1}, {0x8006, 1}, {0x800a, 1}, {0x800f, 1}, {0x8018, 1}, {0x801f, 1}, {0x8029, 1}, {0xc038, 1}, {0x8003, 135}, {0x8006, 135}, {0x800a, 135}, {0x800f, 135}, {0x8018, 135}, {0x801f, 135}, {0x8029, 135}, {0xc038, 135}, }, /* 149 */ { {0x8003, 137}, {0x8006, 137}, {0x800a, 137}, {0x800f, 137}, {0x8018, 137}, {0x801f, 137}, {0x8029, 137}, {0xc038, 137}, {0x8003, 138}, {0x8006, 138}, {0x800a, 138}, {0x800f, 138}, {0x8018, 138}, {0x801f, 138}, {0x8029, 138}, {0xc038, 138}, }, /* 150 */ { {0x8002, 139}, {0x8009, 139}, {0x8017, 139}, {0xc028, 139}, {0x8002, 140}, {0x8009, 140}, {0x8017, 140}, {0xc028, 140}, {0x8002, 141}, {0x8009, 141}, {0x8017, 141}, {0xc028, 141}, {0x8002, 143}, {0x8009, 143}, {0x8017, 143}, {0xc028, 143}, }, /* 151 */ { {0x8003, 139}, {0x8006, 139}, {0x800a, 139}, {0x800f, 139}, {0x8018, 139}, {0x801f, 139}, {0x8029, 139}, {0xc038, 139}, {0x8003, 140}, {0x8006, 140}, {0x800a, 140}, {0x800f, 140}, {0x8018, 140}, {0x801f, 140}, {0x8029, 140}, {0xc038, 140}, }, /* 152 */ { {0x8003, 141}, {0x8006, 141}, {0x800a, 141}, {0x800f, 141}, {0x8018, 141}, {0x801f, 141}, {0x8029, 141}, {0xc038, 141}, {0x8003, 143}, {0x8006, 143}, {0x800a, 143}, {0x800f, 143}, {0x8018, 143}, {0x801f, 143}, {0x8029, 143}, {0xc038, 143}, }, /* 153 */ { {0x9d, 0}, {0x9e, 0}, {0xa0, 0}, {0xa1, 0}, {0xa4, 0}, {0xa5, 0}, {0xa7, 0}, {0xa8, 0}, {0xac, 0}, {0xad, 0}, {0xaf, 0}, {0xb1, 0}, {0xb6, 0}, {0xb9, 0}, {0xbf, 0}, {0xcf, 0}, }, /* 154 */ { {0xc000, 147}, {0xc000, 149}, {0xc000, 150}, {0xc000, 151}, {0xc000, 152}, {0xc000, 155}, {0xc000, 157}, {0xc000, 158}, {0xc000, 165}, {0xc000, 166}, {0xc000, 168}, {0xc000, 174}, {0xc000, 175}, {0xc000, 180}, {0xc000, 182}, {0xc000, 183}, }, /* 155 */ { {0x8001, 147}, {0xc016, 147}, {0x8001, 149}, {0xc016, 149}, {0x8001, 150}, {0xc016, 150}, {0x8001, 151}, {0xc016, 151}, {0x8001, 152}, {0xc016, 152}, {0x8001, 155}, {0xc016, 155}, {0x8001, 157}, {0xc016, 157}, {0x8001, 158}, {0xc016, 158}, }, /* 156 */ { {0x8002, 147}, {0x8009, 147}, {0x8017, 147}, {0xc028, 147}, {0x8002, 149}, {0x8009, 149}, {0x8017, 149}, {0xc028, 149}, {0x8002, 150}, {0x8009, 150}, {0x8017, 150}, {0xc028, 150}, {0x8002, 151}, {0x8009, 151}, {0x8017, 151}, {0xc028, 151}, }, /* 157 */ { {0x8003, 147}, {0x8006, 147}, {0x800a, 147}, {0x800f, 147}, {0x8018, 147}, {0x801f, 147}, {0x8029, 147}, {0xc038, 147}, {0x8003, 149}, {0x8006, 149}, {0x800a, 149}, {0x800f, 149}, {0x8018, 149}, {0x801f, 149}, {0x8029, 149}, {0xc038, 149}, }, /* 158 */ { {0x8003, 150}, {0x8006, 150}, {0x800a, 150}, {0x800f, 150}, {0x8018, 150}, {0x801f, 150}, {0x8029, 150}, {0xc038, 150}, {0x8003, 151}, {0x8006, 151}, {0x800a, 151}, {0x800f, 151}, {0x8018, 151}, {0x801f, 151}, {0x8029, 151}, {0xc038, 151}, }, /* 159 */ { {0x8002, 152}, {0x8009, 152}, {0x8017, 152}, {0xc028, 152}, {0x8002, 155}, {0x8009, 155}, {0x8017, 155}, {0xc028, 155}, {0x8002, 157}, {0x8009, 157}, {0x8017, 157}, {0xc028, 157}, {0x8002, 158}, {0x8009, 158}, {0x8017, 158}, {0xc028, 158}, }, /* 160 */ { {0x8003, 152}, {0x8006, 152}, {0x800a, 152}, {0x800f, 152}, {0x8018, 152}, {0x801f, 152}, {0x8029, 152}, {0xc038, 152}, {0x8003, 155}, {0x8006, 155}, {0x800a, 155}, {0x800f, 155}, {0x8018, 155}, {0x801f, 155}, {0x8029, 155}, {0xc038, 155}, }, /* 161 */ { {0x8003, 157}, {0x8006, 157}, {0x800a, 157}, {0x800f, 157}, {0x8018, 157}, {0x801f, 157}, {0x8029, 157}, {0xc038, 157}, {0x8003, 158}, {0x8006, 158}, {0x800a, 158}, {0x800f, 158}, {0x8018, 158}, {0x801f, 158}, {0x8029, 158}, {0xc038, 158}, }, /* 162 */ { {0x8001, 165}, {0xc016, 165}, {0x8001, 166}, {0xc016, 166}, {0x8001, 168}, {0xc016, 168}, {0x8001, 174}, {0xc016, 174}, {0x8001, 175}, {0xc016, 175}, {0x8001, 180}, {0xc016, 180}, {0x8001, 182}, {0xc016, 182}, {0x8001, 183}, {0xc016, 183}, }, /* 163 */ { {0x8002, 165}, {0x8009, 165}, {0x8017, 165}, {0xc028, 165}, {0x8002, 166}, {0x8009, 166}, {0x8017, 166}, {0xc028, 166}, {0x8002, 168}, {0x8009, 168}, {0x8017, 168}, {0xc028, 168}, {0x8002, 174}, {0x8009, 174}, {0x8017, 174}, {0xc028, 174}, }, /* 164 */ { {0x8003, 165}, {0x8006, 165}, {0x800a, 165}, {0x800f, 165}, {0x8018, 165}, {0x801f, 165}, {0x8029, 165}, {0xc038, 165}, {0x8003, 166}, {0x8006, 166}, {0x800a, 166}, {0x800f, 166}, {0x8018, 166}, {0x801f, 166}, {0x8029, 166}, {0xc038, 166}, }, /* 165 */ { {0x8003, 168}, {0x8006, 168}, {0x800a, 168}, {0x800f, 168}, {0x8018, 168}, {0x801f, 168}, {0x8029, 168}, {0xc038, 168}, {0x8003, 174}, {0x8006, 174}, {0x800a, 174}, {0x800f, 174}, {0x8018, 174}, {0x801f, 174}, {0x8029, 174}, {0xc038, 174}, }, /* 166 */ { {0x8002, 175}, {0x8009, 175}, {0x8017, 175}, {0xc028, 175}, {0x8002, 180}, {0x8009, 180}, {0x8017, 180}, {0xc028, 180}, {0x8002, 182}, {0x8009, 182}, {0x8017, 182}, {0xc028, 182}, {0x8002, 183}, {0x8009, 183}, {0x8017, 183}, {0xc028, 183}, }, /* 167 */ { {0x8003, 175}, {0x8006, 175}, {0x800a, 175}, {0x800f, 175}, {0x8018, 175}, {0x801f, 175}, {0x8029, 175}, {0xc038, 175}, {0x8003, 180}, {0x8006, 180}, {0x800a, 180}, {0x800f, 180}, {0x8018, 180}, {0x801f, 180}, {0x8029, 180}, {0xc038, 180}, }, /* 168 */ { {0x8003, 182}, {0x8006, 182}, {0x800a, 182}, {0x800f, 182}, {0x8018, 182}, {0x801f, 182}, {0x8029, 182}, {0xc038, 182}, {0x8003, 183}, {0x8006, 183}, {0x800a, 183}, {0x800f, 183}, {0x8018, 183}, {0x801f, 183}, {0x8029, 183}, {0xc038, 183}, }, /* 169 */ { {0xc000, 188}, {0xc000, 191}, {0xc000, 197}, {0xc000, 231}, {0xc000, 239}, {0xb0, 0}, {0xb2, 0}, {0xb3, 0}, {0xb7, 0}, {0xb8, 0}, {0xba, 0}, {0xbb, 0}, {0xc0, 0}, {0xc7, 0}, {0xd0, 0}, {0xdf, 0}, }, /* 170 */ { {0x8001, 188}, {0xc016, 188}, {0x8001, 191}, {0xc016, 191}, {0x8001, 197}, {0xc016, 197}, {0x8001, 231}, {0xc016, 231}, {0x8001, 239}, {0xc016, 239}, {0xc000, 9}, {0xc000, 142}, {0xc000, 144}, {0xc000, 145}, {0xc000, 148}, {0xc000, 159}, }, /* 171 */ { {0x8002, 188}, {0x8009, 188}, {0x8017, 188}, {0xc028, 188}, {0x8002, 191}, {0x8009, 191}, {0x8017, 191}, {0xc028, 191}, {0x8002, 197}, {0x8009, 197}, {0x8017, 197}, {0xc028, 197}, {0x8002, 231}, {0x8009, 231}, {0x8017, 231}, {0xc028, 231}, }, /* 172 */ { {0x8003, 188}, {0x8006, 188}, {0x800a, 188}, {0x800f, 188}, {0x8018, 188}, {0x801f, 188}, {0x8029, 188}, {0xc038, 188}, {0x8003, 191}, {0x8006, 191}, {0x800a, 191}, {0x800f, 191}, {0x8018, 191}, {0x801f, 191}, {0x8029, 191}, {0xc038, 191}, }, /* 173 */ { {0x8003, 197}, {0x8006, 197}, {0x800a, 197}, {0x800f, 197}, {0x8018, 197}, {0x801f, 197}, {0x8029, 197}, {0xc038, 197}, {0x8003, 231}, {0x8006, 231}, {0x800a, 231}, {0x800f, 231}, {0x8018, 231}, {0x801f, 231}, {0x8029, 231}, {0xc038, 231}, }, /* 174 */ { {0x8002, 239}, {0x8009, 239}, {0x8017, 239}, {0xc028, 239}, {0x8001, 9}, {0xc016, 9}, {0x8001, 142}, {0xc016, 142}, {0x8001, 144}, {0xc016, 144}, {0x8001, 145}, {0xc016, 145}, {0x8001, 148}, {0xc016, 148}, {0x8001, 159}, {0xc016, 159}, }, /* 175 */ { {0x8003, 239}, {0x8006, 239}, {0x800a, 239}, {0x800f, 239}, {0x8018, 239}, {0x801f, 239}, {0x8029, 239}, {0xc038, 239}, {0x8002, 9}, {0x8009, 9}, {0x8017, 9}, {0xc028, 9}, {0x8002, 142}, {0x8009, 142}, {0x8017, 142}, {0xc028, 142}, }, /* 176 */ { {0x8003, 9}, {0x8006, 9}, {0x800a, 9}, {0x800f, 9}, {0x8018, 9}, {0x801f, 9}, {0x8029, 9}, {0xc038, 9}, {0x8003, 142}, {0x8006, 142}, {0x800a, 142}, {0x800f, 142}, {0x8018, 142}, {0x801f, 142}, {0x8029, 142}, {0xc038, 142}, }, /* 177 */ { {0x8002, 144}, {0x8009, 144}, {0x8017, 144}, {0xc028, 144}, {0x8002, 145}, {0x8009, 145}, {0x8017, 145}, {0xc028, 145}, {0x8002, 148}, {0x8009, 148}, {0x8017, 148}, {0xc028, 148}, {0x8002, 159}, {0x8009, 159}, {0x8017, 159}, {0xc028, 159}, }, /* 178 */ { {0x8003, 144}, {0x8006, 144}, {0x800a, 144}, {0x800f, 144}, {0x8018, 144}, {0x801f, 144}, {0x8029, 144}, {0xc038, 144}, {0x8003, 145}, {0x8006, 145}, {0x800a, 145}, {0x800f, 145}, {0x8018, 145}, {0x801f, 145}, {0x8029, 145}, {0xc038, 145}, }, /* 179 */ { {0x8003, 148}, {0x8006, 148}, {0x800a, 148}, {0x800f, 148}, {0x8018, 148}, {0x801f, 148}, {0x8029, 148}, {0xc038, 148}, {0x8003, 159}, {0x8006, 159}, {0x800a, 159}, {0x800f, 159}, {0x8018, 159}, {0x801f, 159}, {0x8029, 159}, {0xc038, 159}, }, /* 180 */ { {0xc000, 171}, {0xc000, 206}, {0xc000, 215}, {0xc000, 225}, {0xc000, 236}, {0xc000, 237}, {0xbc, 0}, {0xbd, 0}, {0xc1, 0}, {0xc4, 0}, {0xc8, 0}, {0xcb, 0}, {0xd1, 0}, {0xd8, 0}, {0xe0, 0}, {0xee, 0}, }, /* 181 */ { {0x8001, 171}, {0xc016, 171}, {0x8001, 206}, {0xc016, 206}, {0x8001, 215}, {0xc016, 215}, {0x8001, 225}, {0xc016, 225}, {0x8001, 236}, {0xc016, 236}, {0x8001, 237}, {0xc016, 237}, {0xc000, 199}, {0xc000, 207}, {0xc000, 234}, {0xc000, 235}, }, /* 182 */ { {0x8002, 171}, {0x8009, 171}, {0x8017, 171}, {0xc028, 171}, {0x8002, 206}, {0x8009, 206}, {0x8017, 206}, {0xc028, 206}, {0x8002, 215}, {0x8009, 215}, {0x8017, 215}, {0xc028, 215}, {0x8002, 225}, {0x8009, 225}, {0x8017, 225}, {0xc028, 225}, }, /* 183 */ { {0x8003, 171}, {0x8006, 171}, {0x800a, 171}, {0x800f, 171}, {0x8018, 171}, {0x801f, 171}, {0x8029, 171}, {0xc038, 171}, {0x8003, 206}, {0x8006, 206}, {0x800a, 206}, {0x800f, 206}, {0x8018, 206}, {0x801f, 206}, {0x8029, 206}, {0xc038, 206}, }, /* 184 */ { {0x8003, 215}, {0x8006, 215}, {0x800a, 215}, {0x800f, 215}, {0x8018, 215}, {0x801f, 215}, {0x8029, 215}, {0xc038, 215}, {0x8003, 225}, {0x8006, 225}, {0x800a, 225}, {0x800f, 225}, {0x8018, 225}, {0x801f, 225}, {0x8029, 225}, {0xc038, 225}, }, /* 185 */ { {0x8002, 236}, {0x8009, 236}, {0x8017, 236}, {0xc028, 236}, {0x8002, 237}, {0x8009, 237}, {0x8017, 237}, {0xc028, 237}, {0x8001, 199}, {0xc016, 199}, {0x8001, 207}, {0xc016, 207}, {0x8001, 234}, {0xc016, 234}, {0x8001, 235}, {0xc016, 235}, }, /* 186 */ { {0x8003, 236}, {0x8006, 236}, {0x800a, 236}, {0x800f, 236}, {0x8018, 236}, {0x801f, 236}, {0x8029, 236}, {0xc038, 236}, {0x8003, 237}, {0x8006, 237}, {0x800a, 237}, {0x800f, 237}, {0x8018, 237}, {0x801f, 237}, {0x8029, 237}, {0xc038, 237}, }, /* 187 */ { {0x8002, 199}, {0x8009, 199}, {0x8017, 199}, {0xc028, 199}, {0x8002, 207}, {0x8009, 207}, {0x8017, 207}, {0xc028, 207}, {0x8002, 234}, {0x8009, 234}, {0x8017, 234}, {0xc028, 234}, {0x8002, 235}, {0x8009, 235}, {0x8017, 235}, {0xc028, 235}, }, /* 188 */ { {0x8003, 199}, {0x8006, 199}, {0x800a, 199}, {0x800f, 199}, {0x8018, 199}, {0x801f, 199}, {0x8029, 199}, {0xc038, 199}, {0x8003, 207}, {0x8006, 207}, {0x800a, 207}, {0x800f, 207}, {0x8018, 207}, {0x801f, 207}, {0x8029, 207}, {0xc038, 207}, }, /* 189 */ { {0x8003, 234}, {0x8006, 234}, {0x800a, 234}, {0x800f, 234}, {0x8018, 234}, {0x801f, 234}, {0x8029, 234}, {0xc038, 234}, {0x8003, 235}, {0x8006, 235}, {0x800a, 235}, {0x800f, 235}, {0x8018, 235}, {0x801f, 235}, {0x8029, 235}, {0xc038, 235}, }, /* 190 */ { {0xc2, 0}, {0xc3, 0}, {0xc5, 0}, {0xc6, 0}, {0xc9, 0}, {0xca, 0}, {0xcc, 0}, {0xcd, 0}, {0xd2, 0}, {0xd5, 0}, {0xd9, 0}, {0xdc, 0}, {0xe1, 0}, {0xe7, 0}, {0xef, 0}, {0xf6, 0}, }, /* 191 */ { {0xc000, 192}, {0xc000, 193}, {0xc000, 200}, {0xc000, 201}, {0xc000, 202}, {0xc000, 205}, {0xc000, 210}, {0xc000, 213}, {0xc000, 218}, {0xc000, 219}, {0xc000, 238}, {0xc000, 240}, {0xc000, 242}, {0xc000, 243}, {0xc000, 255}, {0xce, 0}, }, /* 192 */ { {0x8001, 192}, {0xc016, 192}, {0x8001, 193}, {0xc016, 193}, {0x8001, 200}, {0xc016, 200}, {0x8001, 201}, {0xc016, 201}, {0x8001, 202}, {0xc016, 202}, {0x8001, 205}, {0xc016, 205}, {0x8001, 210}, {0xc016, 210}, {0x8001, 213}, {0xc016, 213}, }, /* 193 */ { {0x8002, 192}, {0x8009, 192}, {0x8017, 192}, {0xc028, 192}, {0x8002, 193}, {0x8009, 193}, {0x8017, 193}, {0xc028, 193}, {0x8002, 200}, {0x8009, 200}, {0x8017, 200}, {0xc028, 200}, {0x8002, 201}, {0x8009, 201}, {0x8017, 201}, {0xc028, 201}, }, /* 194 */ { {0x8003, 192}, {0x8006, 192}, {0x800a, 192}, {0x800f, 192}, {0x8018, 192}, {0x801f, 192}, {0x8029, 192}, {0xc038, 192}, {0x8003, 193}, {0x8006, 193}, {0x800a, 193}, {0x800f, 193}, {0x8018, 193}, {0x801f, 193}, {0x8029, 193}, {0xc038, 193}, }, /* 195 */ { {0x8003, 200}, {0x8006, 200}, {0x800a, 200}, {0x800f, 200}, {0x8018, 200}, {0x801f, 200}, {0x8029, 200}, {0xc038, 200}, {0x8003, 201}, {0x8006, 201}, {0x800a, 201}, {0x800f, 201}, {0x8018, 201}, {0x801f, 201}, {0x8029, 201}, {0xc038, 201}, }, /* 196 */ { {0x8002, 202}, {0x8009, 202}, {0x8017, 202}, {0xc028, 202}, {0x8002, 205}, {0x8009, 205}, {0x8017, 205}, {0xc028, 205}, {0x8002, 210}, {0x8009, 210}, {0x8017, 210}, {0xc028, 210}, {0x8002, 213}, {0x8009, 213}, {0x8017, 213}, {0xc028, 213}, }, /* 197 */ { {0x8003, 202}, {0x8006, 202}, {0x800a, 202}, {0x800f, 202}, {0x8018, 202}, {0x801f, 202}, {0x8029, 202}, {0xc038, 202}, {0x8003, 205}, {0x8006, 205}, {0x800a, 205}, {0x800f, 205}, {0x8018, 205}, {0x801f, 205}, {0x8029, 205}, {0xc038, 205}, }, /* 198 */ { {0x8003, 210}, {0x8006, 210}, {0x800a, 210}, {0x800f, 210}, {0x8018, 210}, {0x801f, 210}, {0x8029, 210}, {0xc038, 210}, {0x8003, 213}, {0x8006, 213}, {0x800a, 213}, {0x800f, 213}, {0x8018, 213}, {0x801f, 213}, {0x8029, 213}, {0xc038, 213}, }, /* 199 */ { {0x8001, 218}, {0xc016, 218}, {0x8001, 219}, {0xc016, 219}, {0x8001, 238}, {0xc016, 238}, {0x8001, 240}, {0xc016, 240}, {0x8001, 242}, {0xc016, 242}, {0x8001, 243}, {0xc016, 243}, {0x8001, 255}, {0xc016, 255}, {0xc000, 203}, {0xc000, 204}, }, /* 200 */ { {0x8002, 218}, {0x8009, 218}, {0x8017, 218}, {0xc028, 218}, {0x8002, 219}, {0x8009, 219}, {0x8017, 219}, {0xc028, 219}, {0x8002, 238}, {0x8009, 238}, {0x8017, 238}, {0xc028, 238}, {0x8002, 240}, {0x8009, 240}, {0x8017, 240}, {0xc028, 240}, }, /* 201 */ { {0x8003, 218}, {0x8006, 218}, {0x800a, 218}, {0x800f, 218}, {0x8018, 218}, {0x801f, 218}, {0x8029, 218}, {0xc038, 218}, {0x8003, 219}, {0x8006, 219}, {0x800a, 219}, {0x800f, 219}, {0x8018, 219}, {0x801f, 219}, {0x8029, 219}, {0xc038, 219}, }, /* 202 */ { {0x8003, 238}, {0x8006, 238}, {0x800a, 238}, {0x800f, 238}, {0x8018, 238}, {0x801f, 238}, {0x8029, 238}, {0xc038, 238}, {0x8003, 240}, {0x8006, 240}, {0x800a, 240}, {0x800f, 240}, {0x8018, 240}, {0x801f, 240}, {0x8029, 240}, {0xc038, 240}, }, /* 203 */ { {0x8002, 242}, {0x8009, 242}, {0x8017, 242}, {0xc028, 242}, {0x8002, 243}, {0x8009, 243}, {0x8017, 243}, {0xc028, 243}, {0x8002, 255}, {0x8009, 255}, {0x8017, 255}, {0xc028, 255}, {0x8001, 203}, {0xc016, 203}, {0x8001, 204}, {0xc016, 204}, }, /* 204 */ { {0x8003, 242}, {0x8006, 242}, {0x800a, 242}, {0x800f, 242}, {0x8018, 242}, {0x801f, 242}, {0x8029, 242}, {0xc038, 242}, {0x8003, 243}, {0x8006, 243}, {0x800a, 243}, {0x800f, 243}, {0x8018, 243}, {0x801f, 243}, {0x8029, 243}, {0xc038, 243}, }, /* 205 */ { {0x8003, 255}, {0x8006, 255}, {0x800a, 255}, {0x800f, 255}, {0x8018, 255}, {0x801f, 255}, {0x8029, 255}, {0xc038, 255}, {0x8002, 203}, {0x8009, 203}, {0x8017, 203}, {0xc028, 203}, {0x8002, 204}, {0x8009, 204}, {0x8017, 204}, {0xc028, 204}, }, /* 206 */ { {0x8003, 203}, {0x8006, 203}, {0x800a, 203}, {0x800f, 203}, {0x8018, 203}, {0x801f, 203}, {0x8029, 203}, {0xc038, 203}, {0x8003, 204}, {0x8006, 204}, {0x800a, 204}, {0x800f, 204}, {0x8018, 204}, {0x801f, 204}, {0x8029, 204}, {0xc038, 204}, }, /* 207 */ { {0xd3, 0}, {0xd4, 0}, {0xd6, 0}, {0xd7, 0}, {0xda, 0}, {0xdb, 0}, {0xdd, 0}, {0xde, 0}, {0xe2, 0}, {0xe4, 0}, {0xe8, 0}, {0xeb, 0}, {0xf0, 0}, {0xf3, 0}, {0xf7, 0}, {0xfa, 0}, }, /* 208 */ { {0xc000, 211}, {0xc000, 212}, {0xc000, 214}, {0xc000, 221}, {0xc000, 222}, {0xc000, 223}, {0xc000, 241}, {0xc000, 244}, {0xc000, 245}, {0xc000, 246}, {0xc000, 247}, {0xc000, 248}, {0xc000, 250}, {0xc000, 251}, {0xc000, 252}, {0xc000, 253}, }, /* 209 */ { {0x8001, 211}, {0xc016, 211}, {0x8001, 212}, {0xc016, 212}, {0x8001, 214}, {0xc016, 214}, {0x8001, 221}, {0xc016, 221}, {0x8001, 222}, {0xc016, 222}, {0x8001, 223}, {0xc016, 223}, {0x8001, 241}, {0xc016, 241}, {0x8001, 244}, {0xc016, 244}, }, /* 210 */ { {0x8002, 211}, {0x8009, 211}, {0x8017, 211}, {0xc028, 211}, {0x8002, 212}, {0x8009, 212}, {0x8017, 212}, {0xc028, 212}, {0x8002, 214}, {0x8009, 214}, {0x8017, 214}, {0xc028, 214}, {0x8002, 221}, {0x8009, 221}, {0x8017, 221}, {0xc028, 221}, }, /* 211 */ { {0x8003, 211}, {0x8006, 211}, {0x800a, 211}, {0x800f, 211}, {0x8018, 211}, {0x801f, 211}, {0x8029, 211}, {0xc038, 211}, {0x8003, 212}, {0x8006, 212}, {0x800a, 212}, {0x800f, 212}, {0x8018, 212}, {0x801f, 212}, {0x8029, 212}, {0xc038, 212}, }, /* 212 */ { {0x8003, 214}, {0x8006, 214}, {0x800a, 214}, {0x800f, 214}, {0x8018, 214}, {0x801f, 214}, {0x8029, 214}, {0xc038, 214}, {0x8003, 221}, {0x8006, 221}, {0x800a, 221}, {0x800f, 221}, {0x8018, 221}, {0x801f, 221}, {0x8029, 221}, {0xc038, 221}, }, /* 213 */ { {0x8002, 222}, {0x8009, 222}, {0x8017, 222}, {0xc028, 222}, {0x8002, 223}, {0x8009, 223}, {0x8017, 223}, {0xc028, 223}, {0x8002, 241}, {0x8009, 241}, {0x8017, 241}, {0xc028, 241}, {0x8002, 244}, {0x8009, 244}, {0x8017, 244}, {0xc028, 244}, }, /* 214 */ { {0x8003, 222}, {0x8006, 222}, {0x800a, 222}, {0x800f, 222}, {0x8018, 222}, {0x801f, 222}, {0x8029, 222}, {0xc038, 222}, {0x8003, 223}, {0x8006, 223}, {0x800a, 223}, {0x800f, 223}, {0x8018, 223}, {0x801f, 223}, {0x8029, 223}, {0xc038, 223}, }, /* 215 */ { {0x8003, 241}, {0x8006, 241}, {0x800a, 241}, {0x800f, 241}, {0x8018, 241}, {0x801f, 241}, {0x8029, 241}, {0xc038, 241}, {0x8003, 244}, {0x8006, 244}, {0x800a, 244}, {0x800f, 244}, {0x8018, 244}, {0x801f, 244}, {0x8029, 244}, {0xc038, 244}, }, /* 216 */ { {0x8001, 245}, {0xc016, 245}, {0x8001, 246}, {0xc016, 246}, {0x8001, 247}, {0xc016, 247}, {0x8001, 248}, {0xc016, 248}, {0x8001, 250}, {0xc016, 250}, {0x8001, 251}, {0xc016, 251}, {0x8001, 252}, {0xc016, 252}, {0x8001, 253}, {0xc016, 253}, }, /* 217 */ { {0x8002, 245}, {0x8009, 245}, {0x8017, 245}, {0xc028, 245}, {0x8002, 246}, {0x8009, 246}, {0x8017, 246}, {0xc028, 246}, {0x8002, 247}, {0x8009, 247}, {0x8017, 247}, {0xc028, 247}, {0x8002, 248}, {0x8009, 248}, {0x8017, 248}, {0xc028, 248}, }, /* 218 */ { {0x8003, 245}, {0x8006, 245}, {0x800a, 245}, {0x800f, 245}, {0x8018, 245}, {0x801f, 245}, {0x8029, 245}, {0xc038, 245}, {0x8003, 246}, {0x8006, 246}, {0x800a, 246}, {0x800f, 246}, {0x8018, 246}, {0x801f, 246}, {0x8029, 246}, {0xc038, 246}, }, /* 219 */ { {0x8003, 247}, {0x8006, 247}, {0x800a, 247}, {0x800f, 247}, {0x8018, 247}, {0x801f, 247}, {0x8029, 247}, {0xc038, 247}, {0x8003, 248}, {0x8006, 248}, {0x800a, 248}, {0x800f, 248}, {0x8018, 248}, {0x801f, 248}, {0x8029, 248}, {0xc038, 248}, }, /* 220 */ { {0x8002, 250}, {0x8009, 250}, {0x8017, 250}, {0xc028, 250}, {0x8002, 251}, {0x8009, 251}, {0x8017, 251}, {0xc028, 251}, {0x8002, 252}, {0x8009, 252}, {0x8017, 252}, {0xc028, 252}, {0x8002, 253}, {0x8009, 253}, {0x8017, 253}, {0xc028, 253}, }, /* 221 */ { {0x8003, 250}, {0x8006, 250}, {0x800a, 250}, {0x800f, 250}, {0x8018, 250}, {0x801f, 250}, {0x8029, 250}, {0xc038, 250}, {0x8003, 251}, {0x8006, 251}, {0x800a, 251}, {0x800f, 251}, {0x8018, 251}, {0x801f, 251}, {0x8029, 251}, {0xc038, 251}, }, /* 222 */ { {0x8003, 252}, {0x8006, 252}, {0x800a, 252}, {0x800f, 252}, {0x8018, 252}, {0x801f, 252}, {0x8029, 252}, {0xc038, 252}, {0x8003, 253}, {0x8006, 253}, {0x800a, 253}, {0x800f, 253}, {0x8018, 253}, {0x801f, 253}, {0x8029, 253}, {0xc038, 253}, }, /* 223 */ { {0xc000, 254}, {0xe3, 0}, {0xe5, 0}, {0xe6, 0}, {0xe9, 0}, {0xea, 0}, {0xec, 0}, {0xed, 0}, {0xf1, 0}, {0xf2, 0}, {0xf4, 0}, {0xf5, 0}, {0xf8, 0}, {0xf9, 0}, {0xfb, 0}, {0xfc, 0}, }, /* 224 */ { {0x8001, 254}, {0xc016, 254}, {0xc000, 2}, {0xc000, 3}, {0xc000, 4}, {0xc000, 5}, {0xc000, 6}, {0xc000, 7}, {0xc000, 8}, {0xc000, 11}, {0xc000, 12}, {0xc000, 14}, {0xc000, 15}, {0xc000, 16}, {0xc000, 17}, {0xc000, 18}, }, /* 225 */ { {0x8002, 254}, {0x8009, 254}, {0x8017, 254}, {0xc028, 254}, {0x8001, 2}, {0xc016, 2}, {0x8001, 3}, {0xc016, 3}, {0x8001, 4}, {0xc016, 4}, {0x8001, 5}, {0xc016, 5}, {0x8001, 6}, {0xc016, 6}, {0x8001, 7}, {0xc016, 7}, }, /* 226 */ { {0x8003, 254}, {0x8006, 254}, {0x800a, 254}, {0x800f, 254}, {0x8018, 254}, {0x801f, 254}, {0x8029, 254}, {0xc038, 254}, {0x8002, 2}, {0x8009, 2}, {0x8017, 2}, {0xc028, 2}, {0x8002, 3}, {0x8009, 3}, {0x8017, 3}, {0xc028, 3}, }, /* 227 */ { {0x8003, 2}, {0x8006, 2}, {0x800a, 2}, {0x800f, 2}, {0x8018, 2}, {0x801f, 2}, {0x8029, 2}, {0xc038, 2}, {0x8003, 3}, {0x8006, 3}, {0x800a, 3}, {0x800f, 3}, {0x8018, 3}, {0x801f, 3}, {0x8029, 3}, {0xc038, 3}, }, /* 228 */ { {0x8002, 4}, {0x8009, 4}, {0x8017, 4}, {0xc028, 4}, {0x8002, 5}, {0x8009, 5}, {0x8017, 5}, {0xc028, 5}, {0x8002, 6}, {0x8009, 6}, {0x8017, 6}, {0xc028, 6}, {0x8002, 7}, {0x8009, 7}, {0x8017, 7}, {0xc028, 7}, }, /* 229 */ { {0x8003, 4}, {0x8006, 4}, {0x800a, 4}, {0x800f, 4}, {0x8018, 4}, {0x801f, 4}, {0x8029, 4}, {0xc038, 4}, {0x8003, 5}, {0x8006, 5}, {0x800a, 5}, {0x800f, 5}, {0x8018, 5}, {0x801f, 5}, {0x8029, 5}, {0xc038, 5}, }, /* 230 */ { {0x8003, 6}, {0x8006, 6}, {0x800a, 6}, {0x800f, 6}, {0x8018, 6}, {0x801f, 6}, {0x8029, 6}, {0xc038, 6}, {0x8003, 7}, {0x8006, 7}, {0x800a, 7}, {0x800f, 7}, {0x8018, 7}, {0x801f, 7}, {0x8029, 7}, {0xc038, 7}, }, /* 231 */ { {0x8001, 8}, {0xc016, 8}, {0x8001, 11}, {0xc016, 11}, {0x8001, 12}, {0xc016, 12}, {0x8001, 14}, {0xc016, 14}, {0x8001, 15}, {0xc016, 15}, {0x8001, 16}, {0xc016, 16}, {0x8001, 17}, {0xc016, 17}, {0x8001, 18}, {0xc016, 18}, }, /* 232 */ { {0x8002, 8}, {0x8009, 8}, {0x8017, 8}, {0xc028, 8}, {0x8002, 11}, {0x8009, 11}, {0x8017, 11}, {0xc028, 11}, {0x8002, 12}, {0x8009, 12}, {0x8017, 12}, {0xc028, 12}, {0x8002, 14}, {0x8009, 14}, {0x8017, 14}, {0xc028, 14}, }, /* 233 */ { {0x8003, 8}, {0x8006, 8}, {0x800a, 8}, {0x800f, 8}, {0x8018, 8}, {0x801f, 8}, {0x8029, 8}, {0xc038, 8}, {0x8003, 11}, {0x8006, 11}, {0x800a, 11}, {0x800f, 11}, {0x8018, 11}, {0x801f, 11}, {0x8029, 11}, {0xc038, 11}, }, /* 234 */ { {0x8003, 12}, {0x8006, 12}, {0x800a, 12}, {0x800f, 12}, {0x8018, 12}, {0x801f, 12}, {0x8029, 12}, {0xc038, 12}, {0x8003, 14}, {0x8006, 14}, {0x800a, 14}, {0x800f, 14}, {0x8018, 14}, {0x801f, 14}, {0x8029, 14}, {0xc038, 14}, }, /* 235 */ { {0x8002, 15}, {0x8009, 15}, {0x8017, 15}, {0xc028, 15}, {0x8002, 16}, {0x8009, 16}, {0x8017, 16}, {0xc028, 16}, {0x8002, 17}, {0x8009, 17}, {0x8017, 17}, {0xc028, 17}, {0x8002, 18}, {0x8009, 18}, {0x8017, 18}, {0xc028, 18}, }, /* 236 */ { {0x8003, 15}, {0x8006, 15}, {0x800a, 15}, {0x800f, 15}, {0x8018, 15}, {0x801f, 15}, {0x8029, 15}, {0xc038, 15}, {0x8003, 16}, {0x8006, 16}, {0x800a, 16}, {0x800f, 16}, {0x8018, 16}, {0x801f, 16}, {0x8029, 16}, {0xc038, 16}, }, /* 237 */ { {0x8003, 17}, {0x8006, 17}, {0x800a, 17}, {0x800f, 17}, {0x8018, 17}, {0x801f, 17}, {0x8029, 17}, {0xc038, 17}, {0x8003, 18}, {0x8006, 18}, {0x800a, 18}, {0x800f, 18}, {0x8018, 18}, {0x801f, 18}, {0x8029, 18}, {0xc038, 18}, }, /* 238 */ { {0xc000, 19}, {0xc000, 20}, {0xc000, 21}, {0xc000, 23}, {0xc000, 24}, {0xc000, 25}, {0xc000, 26}, {0xc000, 27}, {0xc000, 28}, {0xc000, 29}, {0xc000, 30}, {0xc000, 31}, {0xc000, 127}, {0xc000, 220}, {0xc000, 249}, {0xfd, 0}, }, /* 239 */ { {0x8001, 19}, {0xc016, 19}, {0x8001, 20}, {0xc016, 20}, {0x8001, 21}, {0xc016, 21}, {0x8001, 23}, {0xc016, 23}, {0x8001, 24}, {0xc016, 24}, {0x8001, 25}, {0xc016, 25}, {0x8001, 26}, {0xc016, 26}, {0x8001, 27}, {0xc016, 27}, }, /* 240 */ { {0x8002, 19}, {0x8009, 19}, {0x8017, 19}, {0xc028, 19}, {0x8002, 20}, {0x8009, 20}, {0x8017, 20}, {0xc028, 20}, {0x8002, 21}, {0x8009, 21}, {0x8017, 21}, {0xc028, 21}, {0x8002, 23}, {0x8009, 23}, {0x8017, 23}, {0xc028, 23}, }, /* 241 */ { {0x8003, 19}, {0x8006, 19}, {0x800a, 19}, {0x800f, 19}, {0x8018, 19}, {0x801f, 19}, {0x8029, 19}, {0xc038, 19}, {0x8003, 20}, {0x8006, 20}, {0x800a, 20}, {0x800f, 20}, {0x8018, 20}, {0x801f, 20}, {0x8029, 20}, {0xc038, 20}, }, /* 242 */ { {0x8003, 21}, {0x8006, 21}, {0x800a, 21}, {0x800f, 21}, {0x8018, 21}, {0x801f, 21}, {0x8029, 21}, {0xc038, 21}, {0x8003, 23}, {0x8006, 23}, {0x800a, 23}, {0x800f, 23}, {0x8018, 23}, {0x801f, 23}, {0x8029, 23}, {0xc038, 23}, }, /* 243 */ { {0x8002, 24}, {0x8009, 24}, {0x8017, 24}, {0xc028, 24}, {0x8002, 25}, {0x8009, 25}, {0x8017, 25}, {0xc028, 25}, {0x8002, 26}, {0x8009, 26}, {0x8017, 26}, {0xc028, 26}, {0x8002, 27}, {0x8009, 27}, {0x8017, 27}, {0xc028, 27}, }, /* 244 */ { {0x8003, 24}, {0x8006, 24}, {0x800a, 24}, {0x800f, 24}, {0x8018, 24}, {0x801f, 24}, {0x8029, 24}, {0xc038, 24}, {0x8003, 25}, {0x8006, 25}, {0x800a, 25}, {0x800f, 25}, {0x8018, 25}, {0x801f, 25}, {0x8029, 25}, {0xc038, 25}, }, /* 245 */ { {0x8003, 26}, {0x8006, 26}, {0x800a, 26}, {0x800f, 26}, {0x8018, 26}, {0x801f, 26}, {0x8029, 26}, {0xc038, 26}, {0x8003, 27}, {0x8006, 27}, {0x800a, 27}, {0x800f, 27}, {0x8018, 27}, {0x801f, 27}, {0x8029, 27}, {0xc038, 27}, }, /* 246 */ { {0x8001, 28}, {0xc016, 28}, {0x8001, 29}, {0xc016, 29}, {0x8001, 30}, {0xc016, 30}, {0x8001, 31}, {0xc016, 31}, {0x8001, 127}, {0xc016, 127}, {0x8001, 220}, {0xc016, 220}, {0x8001, 249}, {0xc016, 249}, {0xfe, 0}, {0xff, 0}, }, /* 247 */ { {0x8002, 28}, {0x8009, 28}, {0x8017, 28}, {0xc028, 28}, {0x8002, 29}, {0x8009, 29}, {0x8017, 29}, {0xc028, 29}, {0x8002, 30}, {0x8009, 30}, {0x8017, 30}, {0xc028, 30}, {0x8002, 31}, {0x8009, 31}, {0x8017, 31}, {0xc028, 31}, }, /* 248 */ { {0x8003, 28}, {0x8006, 28}, {0x800a, 28}, {0x800f, 28}, {0x8018, 28}, {0x801f, 28}, {0x8029, 28}, {0xc038, 28}, {0x8003, 29}, {0x8006, 29}, {0x800a, 29}, {0x800f, 29}, {0x8018, 29}, {0x801f, 29}, {0x8029, 29}, {0xc038, 29}, }, /* 249 */ { {0x8003, 30}, {0x8006, 30}, {0x800a, 30}, {0x800f, 30}, {0x8018, 30}, {0x801f, 30}, {0x8029, 30}, {0xc038, 30}, {0x8003, 31}, {0x8006, 31}, {0x800a, 31}, {0x800f, 31}, {0x8018, 31}, {0x801f, 31}, {0x8029, 31}, {0xc038, 31}, }, /* 250 */ { {0x8002, 127}, {0x8009, 127}, {0x8017, 127}, {0xc028, 127}, {0x8002, 220}, {0x8009, 220}, {0x8017, 220}, {0xc028, 220}, {0x8002, 249}, {0x8009, 249}, {0x8017, 249}, {0xc028, 249}, {0xc000, 10}, {0xc000, 13}, {0xc000, 22}, {0x100, 0}, }, /* 251 */ { {0x8003, 127}, {0x8006, 127}, {0x800a, 127}, {0x800f, 127}, {0x8018, 127}, {0x801f, 127}, {0x8029, 127}, {0xc038, 127}, {0x8003, 220}, {0x8006, 220}, {0x800a, 220}, {0x800f, 220}, {0x8018, 220}, {0x801f, 220}, {0x8029, 220}, {0xc038, 220}, }, /* 252 */ { {0x8003, 249}, {0x8006, 249}, {0x800a, 249}, {0x800f, 249}, {0x8018, 249}, {0x801f, 249}, {0x8029, 249}, {0xc038, 249}, {0x8001, 10}, {0xc016, 10}, {0x8001, 13}, {0xc016, 13}, {0x8001, 22}, {0xc016, 22}, {0x100, 0}, {0x100, 0}, }, /* 253 */ { {0x8002, 10}, {0x8009, 10}, {0x8017, 10}, {0xc028, 10}, {0x8002, 13}, {0x8009, 13}, {0x8017, 13}, {0xc028, 13}, {0x8002, 22}, {0x8009, 22}, {0x8017, 22}, {0xc028, 22}, {0x100, 0}, {0x100, 0}, {0x100, 0}, {0x100, 0}, }, /* 254 */ { {0x8003, 10}, {0x8006, 10}, {0x800a, 10}, {0x800f, 10}, {0x8018, 10}, {0x801f, 10}, {0x8029, 10}, {0xc038, 10}, {0x8003, 13}, {0x8006, 13}, {0x800a, 13}, {0x800f, 13}, {0x8018, 13}, {0x801f, 13}, {0x8029, 13}, {0xc038, 13}, }, /* 255 */ { {0x8003, 22}, {0x8006, 22}, {0x800a, 22}, {0x800f, 22}, {0x8018, 22}, {0x801f, 22}, {0x8029, 22}, {0xc038, 22}, {0x100, 0}, {0x100, 0}, {0x100, 0}, {0x100, 0}, {0x100, 0}, {0x100, 0}, {0x100, 0}, {0x100, 0}, }, /* 256 */ { {0x100, 0}, {0x100, 0}, {0x100, 0}, {0x100, 0}, {0x100, 0}, {0x100, 0}, {0x100, 0}, {0x100, 0}, {0x100, 0}, {0x100, 0}, {0x100, 0}, {0x100, 0}, {0x100, 0}, {0x100, 0}, {0x100, 0}, {0x100, 0}, }, }; nghttp2-1.68.0/lib/PaxHeaders/nghttp2_stream.h0000644000000000000000000000013215077107270016140 xustar0030 mtime=1761382072.981444194 30 atime=1761382104.940315995 30 ctime=1761382107.927303772 nghttp2-1.68.0/lib/nghttp2_stream.h0000644000175100017510000002633115077107270016535 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_STREAM_H #define NGHTTP2_STREAM_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include #include "nghttp2_outbound_item.h" #include "nghttp2_map.h" #include "nghttp2_pq.h" #include "nghttp2_int.h" /* * If local peer is stream initiator: * NGHTTP2_STREAM_OPENING : upon sending request HEADERS * NGHTTP2_STREAM_OPENED : upon receiving response HEADERS * NGHTTP2_STREAM_CLOSING : upon queuing RST_STREAM * * If remote peer is stream initiator: * NGHTTP2_STREAM_OPENING : upon receiving request HEADERS * NGHTTP2_STREAM_OPENED : upon sending response HEADERS * NGHTTP2_STREAM_CLOSING : upon queuing RST_STREAM */ typedef enum { /* Initial state */ NGHTTP2_STREAM_INITIAL, /* For stream initiator: request HEADERS has been sent, but response HEADERS has not been received yet. For receiver: request HEADERS has been received, but it does not send response HEADERS yet. */ NGHTTP2_STREAM_OPENING, /* For stream initiator: response HEADERS is received. For receiver: response HEADERS is sent. */ NGHTTP2_STREAM_OPENED, /* RST_STREAM is received, but somehow we need to keep stream in memory. */ NGHTTP2_STREAM_CLOSING, /* PUSH_PROMISE is received or sent */ NGHTTP2_STREAM_RESERVED, /* Stream is created in this state if it is used as anchor in dependency tree. */ NGHTTP2_STREAM_IDLE } nghttp2_stream_state; typedef enum { NGHTTP2_SHUT_NONE = 0, /* Indicates further receptions will be disallowed. */ NGHTTP2_SHUT_RD = 0x01, /* Indicates further transmissions will be disallowed. */ NGHTTP2_SHUT_WR = 0x02, /* Indicates both further receptions and transmissions will be disallowed. */ NGHTTP2_SHUT_RDWR = NGHTTP2_SHUT_RD | NGHTTP2_SHUT_WR } nghttp2_shut_flag; typedef enum { NGHTTP2_STREAM_FLAG_NONE = 0, /* Indicates that this stream is pushed stream and not opened yet. */ NGHTTP2_STREAM_FLAG_PUSH = 0x01, /* Indicates that this stream was closed */ NGHTTP2_STREAM_FLAG_CLOSED = 0x02, /* Indicates the item is deferred due to flow control. */ NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL = 0x04, /* Indicates the item is deferred by user callback */ NGHTTP2_STREAM_FLAG_DEFERRED_USER = 0x08, /* bitwise OR of NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and NGHTTP2_STREAM_FLAG_DEFERRED_USER. */ NGHTTP2_STREAM_FLAG_DEFERRED_ALL = 0x0c, /* Ignore client RFC 9218 priority signal. */ NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES = 0x20, /* Indicates that RFC 9113 leading and trailing white spaces validation against a field value is not performed. */ NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION = 0x40, } nghttp2_stream_flag; /* HTTP related flags to enforce HTTP semantics */ typedef enum { NGHTTP2_HTTP_FLAG_NONE = 0, /* header field seen so far */ NGHTTP2_HTTP_FLAG__AUTHORITY = 1, NGHTTP2_HTTP_FLAG__PATH = 1 << 1, NGHTTP2_HTTP_FLAG__METHOD = 1 << 2, NGHTTP2_HTTP_FLAG__SCHEME = 1 << 3, /* host is not pseudo header, but we require either host or :authority */ NGHTTP2_HTTP_FLAG_HOST = 1 << 4, NGHTTP2_HTTP_FLAG__STATUS = 1 << 5, /* required header fields for HTTP request except for CONNECT method. */ NGHTTP2_HTTP_FLAG_REQ_HEADERS = NGHTTP2_HTTP_FLAG__METHOD | NGHTTP2_HTTP_FLAG__PATH | NGHTTP2_HTTP_FLAG__SCHEME, NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED = 1 << 6, /* HTTP method flags */ NGHTTP2_HTTP_FLAG_METH_CONNECT = 1 << 7, NGHTTP2_HTTP_FLAG_METH_HEAD = 1 << 8, NGHTTP2_HTTP_FLAG_METH_OPTIONS = 1 << 9, NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND = 1 << 10, NGHTTP2_HTTP_FLAG_METH_ALL = NGHTTP2_HTTP_FLAG_METH_CONNECT | NGHTTP2_HTTP_FLAG_METH_HEAD | NGHTTP2_HTTP_FLAG_METH_OPTIONS | NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND, /* :path category */ /* path starts with "/" */ NGHTTP2_HTTP_FLAG_PATH_REGULAR = 1 << 11, /* path "*" */ NGHTTP2_HTTP_FLAG_PATH_ASTERISK = 1 << 12, /* scheme */ /* "http" or "https" scheme */ NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 13, /* set if final response is expected */ NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 14, NGHTTP2_HTTP_FLAG__PROTOCOL = 1 << 15, /* set if priority header field is received */ NGHTTP2_HTTP_FLAG_PRIORITY = 1 << 16, /* set if an error is encountered while parsing priority header field */ NGHTTP2_HTTP_FLAG_BAD_PRIORITY = 1 << 17, } nghttp2_http_flag; struct nghttp2_stream { nghttp2_stream_state state; nghttp2_pq_entry pq_entry; /* Content-Length of request/response body. -1 if unknown. */ int64_t content_length; /* Received body so far */ int64_t recv_content_length; /* Next scheduled time to sent item */ uint64_t cycle; /* Secondary key for prioritization to break a tie for cycle. This value is monotonically increased for single parent stream. */ uint64_t seq; nghttp2_stream *closed_next; /* The arbitrary data provided by user for this stream. */ void *stream_user_data; /* Item to send */ nghttp2_outbound_item *item; /* Last written length of frame payload */ size_t last_writelen; /* stream ID */ int32_t stream_id; /* Current remote window size. This value is computed against the current initial window size of remote endpoint. */ int32_t remote_window_size; /* Keep track of the number of bytes received without WINDOW_UPDATE. This could be negative after submitting negative value to WINDOW_UPDATE */ int32_t recv_window_size; /* The number of bytes consumed by the application and now is subject to WINDOW_UPDATE. This is only used when auto WINDOW_UPDATE is turned off. */ int32_t consumed_size; /* The amount of recv_window_size cut using submitting negative value to WINDOW_UPDATE */ int32_t recv_reduction; /* window size for local flow control. It is initially set to NGHTTP2_INITIAL_WINDOW_SIZE and could be increased/decreased by submitting WINDOW_UPDATE. See nghttp2_submit_window_update(). */ int32_t local_window_size; /* This is unpaid penalty (offset) when calculating cycle. */ uint32_t pending_penalty; /* status code from remote server */ int16_t status_code; /* Bitwise OR of zero or more nghttp2_http_flag values */ uint32_t http_flags; /* This is bitwise-OR of 0 or more of nghttp2_stream_flag. */ uint8_t flags; /* Bitwise OR of zero or more nghttp2_shut_flag values */ uint8_t shut_flags; /* Nonzero if this stream has been queued to stream pointed by dep_prev. We maintain the invariant that if a stream is queued, then its ancestors, except for root, are also queued. This invariant may break in fatal error condition. */ uint8_t queued; /* This flag is used to reduce excessive queuing of WINDOW_UPDATE to this stream. The nonzero does not necessarily mean WINDOW_UPDATE is not queued. */ uint8_t window_update_queued; /* extpri is a stream priority produced by nghttp2_extpri_to_uint8 used by RFC 9218 extensible priorities. */ uint8_t extpri; /* http_extpri is a stream priority received in HTTP request header fields and produced by nghttp2_extpri_to_uint8. */ uint8_t http_extpri; }; void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, uint8_t flags, nghttp2_stream_state initial_state, int32_t remote_initial_window_size, int32_t local_initial_window_size, void *stream_user_data); void nghttp2_stream_free(nghttp2_stream *stream); /* * Disallow either further receptions or transmissions, or both. * |flag| is bitwise OR of one or more of nghttp2_shut_flag. */ void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag); /* * Defer |stream->item|. We won't call this function in the situation * where |stream->item| == NULL. The |flags| is bitwise OR of zero or * more of NGHTTP2_STREAM_FLAG_DEFERRED_USER and * NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL. The |flags| indicates * the reason of this action. */ void nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags); /* * Put back deferred data in this stream to active state. The |flags| * are one or more of bitwise OR of the following values: * NGHTTP2_STREAM_FLAG_DEFERRED_USER and * NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and given masks are * cleared if they are set. So even if this function is called, if * one of flag is still set, data does not become active. */ void nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags); /* * Returns nonzero if item is deferred by whatever reason. */ int nghttp2_stream_check_deferred_item(nghttp2_stream *stream); /* * Returns nonzero if item is deferred by flow control. */ int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream); /* * Updates the remote window size with the new value * |new_initial_window_size|. The |old_initial_window_size| is used to * calculate the current window size. * * This function returns 0 if it succeeds or -1. The failure is due to * overflow. */ int nghttp2_stream_update_remote_initial_window_size( nghttp2_stream *stream, int32_t new_initial_window_size, int32_t old_initial_window_size); /* * Updates the local window size with the new value * |new_initial_window_size|. The |old_initial_window_size| is used to * calculate the current window size. * * This function returns 0 if it succeeds or -1. The failure is due to * overflow. */ int nghttp2_stream_update_local_initial_window_size( nghttp2_stream *stream, int32_t new_initial_window_size, int32_t old_initial_window_size); /* * Call this function if promised stream |stream| is replied with * HEADERS. This function makes the state of the |stream| to * NGHTTP2_STREAM_OPENED. */ void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream); /* * Attaches |item| to |stream|. */ void nghttp2_stream_attach_item(nghttp2_stream *stream, nghttp2_outbound_item *item); /* * Detaches |stream->item|. This function does not free * |stream->item|. The caller must free it. */ void nghttp2_stream_detach_item(nghttp2_stream *stream); #endif /* !defined(NGHTTP2_STREAM_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_outbound_item.h0000644000000000000000000000013215077107270017522 xustar0030 mtime=1761382072.979444204 30 atime=1761382104.944315978 30 ctime=1761382107.930303763 nghttp2-1.68.0/lib/nghttp2_outbound_item.h0000644000175100017510000001430215077107270020112 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_OUTBOUND_ITEM_H #define NGHTTP2_OUTBOUND_ITEM_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include #include "nghttp2_frame.h" #include "nghttp2_mem.h" #define NGHTTP2_DATA_PROVIDER_V1 1 #define NGHTTP2_DATA_PROVIDER_V2 2 typedef struct nghttp2_data_provider_wrap { int version; union { struct { nghttp2_data_source source; void *read_callback; }; nghttp2_data_provider v1; nghttp2_data_provider2 v2; } data_prd; } nghttp2_data_provider_wrap; nghttp2_data_provider_wrap * nghttp2_data_provider_wrap_v1(nghttp2_data_provider_wrap *dpw, const nghttp2_data_provider *data_prd); nghttp2_data_provider_wrap * nghttp2_data_provider_wrap_v2(nghttp2_data_provider_wrap *dpw, const nghttp2_data_provider2 *data_prd); /* struct used for HEADERS and PUSH_PROMISE frame */ typedef struct { nghttp2_data_provider_wrap dpw; void *stream_user_data; /* error code when request HEADERS is canceled by RST_STREAM while it is in queue. */ uint32_t error_code; /* nonzero if request HEADERS is canceled. The error code is stored in |error_code|. */ uint8_t canceled; } nghttp2_headers_aux_data; /* struct used for DATA frame */ typedef struct { /** * The data to be sent for this DATA frame. */ nghttp2_data_provider_wrap dpw; /** * The flags of DATA frame. We use separate flags here and * nghttp2_data frame. The latter contains flags actually sent to * peer. This |flags| may contain NGHTTP2_FLAG_END_STREAM and only * when |eof| becomes nonzero, flags in nghttp2_data has * NGHTTP2_FLAG_END_STREAM set. */ uint8_t flags; /** * The flag to indicate whether EOF was reached or not. Initially * |eof| is 0. It becomes 1 after all data were read. */ uint8_t eof; /** * The flag to indicate that NGHTTP2_DATA_FLAG_NO_COPY is used. */ uint8_t no_copy; } nghttp2_data_aux_data; typedef enum { NGHTTP2_GOAWAY_AUX_NONE = 0x0, /* indicates that session should be terminated after the transmission of this frame. */ NGHTTP2_GOAWAY_AUX_TERM_ON_SEND = 0x1, /* indicates that this GOAWAY is just a notification for graceful shutdown. No nghttp2_session.goaway_flags should be updated on the reaction to this frame. */ NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE = 0x2 } nghttp2_goaway_aux_flag; /* struct used for GOAWAY frame */ typedef struct { /* bitwise-OR of one or more of nghttp2_goaway_aux_flag. */ uint8_t flags; } nghttp2_goaway_aux_data; /* struct used for extension frame */ typedef struct { /* nonzero if this extension frame is serialized by library function, instead of user-defined callbacks. */ uint8_t builtin; } nghttp2_ext_aux_data; /* Additional data which cannot be stored in nghttp2_frame struct */ typedef union { nghttp2_data_aux_data data; nghttp2_headers_aux_data headers; nghttp2_goaway_aux_data goaway; nghttp2_ext_aux_data ext; } nghttp2_aux_data; struct nghttp2_outbound_item; typedef struct nghttp2_outbound_item nghttp2_outbound_item; struct nghttp2_outbound_item { nghttp2_frame frame; /* Storage for extension frame payload. frame->ext.payload points to this structure to avoid frequent memory allocation. */ nghttp2_ext_frame_payload ext_frame_payload; nghttp2_aux_data aux_data; /* The priority used in priority comparison. Smaller is served earlier. For PING, SETTINGS and non-DATA frames (excluding response HEADERS frame) have dedicated cycle value defined above. For DATA frame, cycle is computed by taking into account of effective weight and frame payload length previously sent, so that the amount of transmission is distributed across streams proportional to effective weight (inside a tree). */ uint64_t cycle; nghttp2_outbound_item *qnext; /* nonzero if this object is queued, except for DATA or HEADERS which are attached to stream as item. */ uint8_t queued; }; /* * Initializes |item|. No memory allocation is done in this function. * Don't call nghttp2_outbound_item_free() until frame member is * initialized. */ void nghttp2_outbound_item_init(nghttp2_outbound_item *item); /* * Deallocates resource for |item|. If |item| is NULL, this function * does nothing. */ void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem); /* * queue for nghttp2_outbound_item. */ typedef struct { nghttp2_outbound_item *head, *tail; /* number of items in this queue. */ size_t n; } nghttp2_outbound_queue; void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q); /* Pushes |item| into |q| */ void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q, nghttp2_outbound_item *item); /* Pops |item| at the top from |q|. If |q| is empty, nothing happens. */ void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q); /* Returns the top item. */ #define nghttp2_outbound_queue_top(Q) ((Q)->head) /* Returns the size of the queue */ #define nghttp2_outbound_queue_size(Q) ((Q)->n) #endif /* !defined(NGHTTP2_OUTBOUND_ITEM_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_rcbuf.h0000644000000000000000000000013215077107270015746 xustar0030 mtime=1761382072.980444199 30 atime=1761382104.956315925 30 ctime=1761382107.942303728 nghttp2-1.68.0/lib/nghttp2_rcbuf.h0000644000175100017510000000524115077107270016340 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_RCBUF_H #define NGHTTP2_RCBUF_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include struct nghttp2_rcbuf { /* custom memory allocator belongs to the mem parameter when creating this object. */ void *mem_user_data; nghttp2_free free; /* The pointer to the underlying buffer */ uint8_t *base; /* Size of buffer pointed by |base|. */ size_t len; /* Reference count */ int32_t ref; }; /* * Allocates nghttp2_rcbuf object with |size| as initial buffer size. * When the function succeeds, the reference count becomes 1. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM: * Out of memory. */ int nghttp2_rcbuf_new(nghttp2_rcbuf **rcbuf_ptr, size_t size, nghttp2_mem *mem); /* * Like nghttp2_rcbuf_new(), but initializes the buffer with |src| of * length |srclen|. This function allocates additional byte at the * end and puts '\0' into it, so that the resulting buffer could be * used as NULL-terminated string. Still (*rcbuf_ptr)->len equals to * |srclen|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM: * Out of memory. */ int nghttp2_rcbuf_new2(nghttp2_rcbuf **rcbuf_ptr, const uint8_t *src, size_t srclen, nghttp2_mem *mem); /* * Frees |rcbuf| itself, regardless of its reference cout. */ void nghttp2_rcbuf_del(nghttp2_rcbuf *rcbuf); #endif /* !defined(NGHTTP2_RCBUF_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_priority_spec.h0000644000000000000000000000013215077107270017540 xustar0030 mtime=1761382072.980444199 30 atime=1761382104.949315955 30 ctime=1761382107.935303749 nghttp2-1.68.0/lib/nghttp2_priority_spec.h0000644000175100017510000000327015077107270020132 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_PRIORITY_SPEC_H #define NGHTTP2_PRIORITY_SPEC_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include /* * This function normalizes pri_spec->weight if it is out of range. * If pri_spec->weight is less than NGHTTP2_MIN_WEIGHT, it is set to * NGHTTP2_MIN_WEIGHT. If pri_spec->weight is larger than * NGHTTP2_MAX_WEIGHT, it is set to NGHTTP2_MAX_WEIGHT. */ void nghttp2_priority_spec_normalize_weight(nghttp2_priority_spec *pri_spec); #endif /* !defined(NGHTTP2_PRIORITY_SPEC_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_hd_huffman.c0000644000000000000000000000013215077107270016737 xustar0030 mtime=1761382072.978444208 30 atime=1761382104.979315823 30 ctime=1761382107.965303662 nghttp2-1.68.0/lib/nghttp2_hd_huffman.c0000644000175100017510000001001215077107270017321 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_hd_huffman.h" #include #include #include #include "nghttp2_hd.h" #include "nghttp2_net.h" size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len) { size_t i; size_t nbits = 0; for (i = 0; i < len; ++i) { nbits += huff_sym_table[src[i]].nbits; } /* pad the prefix of EOS (256) */ return (nbits + 7) / 8; } int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src, size_t srclen) { const nghttp2_huff_sym *sym; const uint8_t *end = src + srclen; uint64_t code = 0; uint32_t x; size_t nbits = 0; size_t avail; int rv; avail = nghttp2_bufs_cur_avail(bufs); for (; src != end;) { sym = &huff_sym_table[*src++]; code |= (uint64_t)sym->code << (32 - nbits); nbits += sym->nbits; if (nbits < 32) { continue; } if (avail >= 4) { x = htonl((uint32_t)(code >> 32)); memcpy(bufs->cur->buf.last, &x, 4); bufs->cur->buf.last += 4; avail -= 4; code <<= 32; nbits -= 32; continue; } for (; nbits >= 8;) { rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 56)); if (rv != 0) { return rv; } code <<= 8; nbits -= 8; } avail = nghttp2_bufs_cur_avail(bufs); } for (; nbits >= 8;) { rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 56)); if (rv != 0) { return rv; } code <<= 8; nbits -= 8; } if (nbits) { rv = nghttp2_bufs_addb( bufs, (uint8_t)((uint8_t)(code >> 56) | ((1 << (8 - nbits)) - 1))); if (rv != 0) { return rv; } } return 0; } void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) { ctx->fstate = NGHTTP2_HUFF_ACCEPTED; } nghttp2_ssize nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, nghttp2_buf *buf, const uint8_t *src, size_t srclen, int final) { const uint8_t *end = src + srclen; nghttp2_huff_decode node = {ctx->fstate, 0}; const nghttp2_huff_decode *t = &node; uint8_t c; /* We use the decoding algorithm described in - http://graphics.ics.uci.edu/pub/Prefix.pdf [!!! NO LONGER VALID !!!] - https://ics.uci.edu/~dan/pubs/Prefix.pdf - https://github.com/nghttp2/nghttp2/files/15141264/Prefix.pdf */ for (; src != end;) { c = *src++; t = &huff_decode_table[t->fstate & 0x1ff][c >> 4]; if (t->fstate & NGHTTP2_HUFF_SYM) { *buf->last++ = t->sym; } t = &huff_decode_table[t->fstate & 0x1ff][c & 0xf]; if (t->fstate & NGHTTP2_HUFF_SYM) { *buf->last++ = t->sym; } } ctx->fstate = t->fstate; if (final && !(ctx->fstate & NGHTTP2_HUFF_ACCEPTED)) { return NGHTTP2_ERR_HEADER_COMP; } return (nghttp2_ssize)srclen; } int nghttp2_hd_huff_decode_failure_state(nghttp2_hd_huff_decode_context *ctx) { return ctx->fstate == 0x100; } nghttp2-1.68.0/lib/PaxHeaders/nghttp2_frame.c0000644000000000000000000000013215077107270015732 xustar0030 mtime=1761382072.977444213 30 atime=1761382104.967315876 30 ctime=1761382107.953303697 nghttp2-1.68.0/lib/nghttp2_frame.c0000644000175100017510000010255415077107270016331 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_frame.h" #include #include #include #include #include "nghttp2_helper.h" #include "nghttp2_net.h" #include "nghttp2_priority_spec.h" #include "nghttp2_debug.h" void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd) { nghttp2_put_uint32be(&buf[0], (uint32_t)(hd->length << 8)); buf[3] = hd->type; buf[4] = hd->flags; nghttp2_put_uint32be(&buf[5], (uint32_t)hd->stream_id); /* ignore hd->reserved for now */ } void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t *buf) { hd->length = nghttp2_get_uint32(&buf[0]) >> 8; hd->type = buf[3]; hd->flags = buf[4]; hd->stream_id = nghttp2_get_uint32(&buf[5]) & NGHTTP2_STREAM_ID_MASK; hd->reserved = 0; } void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type, uint8_t flags, int32_t stream_id) { hd->length = length; hd->type = type; hd->flags = flags; hd->stream_id = stream_id; hd->reserved = 0; } void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags, int32_t stream_id, nghttp2_headers_category cat, const nghttp2_priority_spec *pri_spec, nghttp2_nv *nva, size_t nvlen) { nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_HEADERS, flags, stream_id); frame->padlen = 0; frame->nva = nva; frame->nvlen = nvlen; frame->cat = cat; if (pri_spec) { frame->pri_spec = *pri_spec; } else { nghttp2_priority_spec_default_init(&frame->pri_spec); } } void nghttp2_frame_headers_free(nghttp2_headers *frame, nghttp2_mem *mem) { nghttp2_nv_array_del(frame->nva, mem); } void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id, const nghttp2_priority_spec *pri_spec) { nghttp2_frame_hd_init(&frame->hd, NGHTTP2_PRIORITY_SPECLEN, NGHTTP2_PRIORITY, NGHTTP2_FLAG_NONE, stream_id); frame->pri_spec = *pri_spec; } void nghttp2_frame_priority_free(nghttp2_priority *frame) { (void)frame; } void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id, uint32_t error_code) { nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, stream_id); frame->error_code = error_code; } void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame) { (void)frame; } void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags, nghttp2_settings_entry *iv, size_t niv) { nghttp2_frame_hd_init(&frame->hd, niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH, NGHTTP2_SETTINGS, flags, 0); frame->niv = niv; frame->iv = iv; } void nghttp2_frame_settings_free(nghttp2_settings *frame, nghttp2_mem *mem) { nghttp2_mem_free(mem, frame->iv); } void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags, int32_t stream_id, int32_t promised_stream_id, nghttp2_nv *nva, size_t nvlen) { nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_PUSH_PROMISE, flags, stream_id); frame->padlen = 0; frame->nva = nva; frame->nvlen = nvlen; frame->promised_stream_id = promised_stream_id; frame->reserved = 0; } void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame, nghttp2_mem *mem) { nghttp2_nv_array_del(frame->nva, mem); } void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags, const uint8_t *opaque_data) { nghttp2_frame_hd_init(&frame->hd, 8, NGHTTP2_PING, flags, 0); if (opaque_data) { memcpy(frame->opaque_data, opaque_data, sizeof(frame->opaque_data)); } else { memset(frame->opaque_data, 0, sizeof(frame->opaque_data)); } } void nghttp2_frame_ping_free(nghttp2_ping *frame) { (void)frame; } void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id, uint32_t error_code, uint8_t *opaque_data, size_t opaque_data_len) { nghttp2_frame_hd_init(&frame->hd, 8 + opaque_data_len, NGHTTP2_GOAWAY, NGHTTP2_FLAG_NONE, 0); frame->last_stream_id = last_stream_id; frame->error_code = error_code; frame->opaque_data = opaque_data; frame->opaque_data_len = opaque_data_len; frame->reserved = 0; } void nghttp2_frame_goaway_free(nghttp2_goaway *frame, nghttp2_mem *mem) { nghttp2_mem_free(mem, frame->opaque_data); } void nghttp2_frame_window_update_init(nghttp2_window_update *frame, uint8_t flags, int32_t stream_id, int32_t window_size_increment) { nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_WINDOW_UPDATE, flags, stream_id); frame->window_size_increment = window_size_increment; frame->reserved = 0; } void nghttp2_frame_window_update_free(nghttp2_window_update *frame) { (void)frame; } size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen) { /* We have iframe->padlen == 0, but iframe->frame.hd.flags may have NGHTTP2_FLAG_PADDED set. This happens when receiving CONTINUATION frame, since we don't reset flags after HEADERS was received. */ if (padlen == 0) { return 0; } return padlen - ((frame->hd.flags & NGHTTP2_FLAG_PADDED) > 0); } void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags, int32_t stream_id) { /* At this moment, the length of DATA frame is unknown */ nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_DATA, flags, stream_id); frame->padlen = 0; } void nghttp2_frame_data_free(nghttp2_data *frame) { (void)frame; } void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type, uint8_t flags, int32_t stream_id, void *payload) { nghttp2_frame_hd_init(&frame->hd, 0, type, flags, stream_id); frame->payload = payload; } void nghttp2_frame_extension_free(nghttp2_extension *frame) { (void)frame; } void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id, uint8_t *origin, size_t origin_len, uint8_t *field_value, size_t field_value_len) { nghttp2_ext_altsvc *altsvc; nghttp2_frame_hd_init(&frame->hd, 2 + origin_len + field_value_len, NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, stream_id); altsvc = frame->payload; altsvc->origin = origin; altsvc->origin_len = origin_len; altsvc->field_value = field_value; altsvc->field_value_len = field_value_len; } void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem) { nghttp2_ext_altsvc *altsvc; altsvc = frame->payload; if (altsvc == NULL) { return; } /* We use the same buffer for altsvc->origin and altsvc->field_value. */ nghttp2_mem_free(mem, altsvc->origin); } void nghttp2_frame_origin_init(nghttp2_extension *frame, nghttp2_origin_entry *ov, size_t nov) { nghttp2_ext_origin *origin; size_t payloadlen = 0; size_t i; for (i = 0; i < nov; ++i) { payloadlen += 2 + ov[i].origin_len; } nghttp2_frame_hd_init(&frame->hd, payloadlen, NGHTTP2_ORIGIN, NGHTTP2_FLAG_NONE, 0); origin = frame->payload; origin->ov = ov; origin->nov = nov; } void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem) { nghttp2_ext_origin *origin; origin = frame->payload; if (origin == NULL) { return; } /* We use the same buffer for all resources pointed by the field of origin directly or indirectly. */ nghttp2_mem_free(mem, origin->ov); } void nghttp2_frame_priority_update_init(nghttp2_extension *frame, int32_t stream_id, uint8_t *field_value, size_t field_value_len) { nghttp2_ext_priority_update *priority_update; nghttp2_frame_hd_init(&frame->hd, 4 + field_value_len, NGHTTP2_PRIORITY_UPDATE, NGHTTP2_FLAG_NONE, 0); priority_update = frame->payload; priority_update->stream_id = stream_id; priority_update->field_value = field_value; priority_update->field_value_len = field_value_len; } void nghttp2_frame_priority_update_free(nghttp2_extension *frame, nghttp2_mem *mem) { nghttp2_ext_priority_update *priority_update; priority_update = frame->payload; if (priority_update == NULL) { return; } nghttp2_mem_free(mem, priority_update->field_value); } size_t nghttp2_frame_priority_len(uint8_t flags) { if (flags & NGHTTP2_FLAG_PRIORITY) { return NGHTTP2_PRIORITY_SPECLEN; } return 0; } size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame) { return nghttp2_frame_priority_len(frame->hd.flags); } /* * Call this function after payload was serialized, but not before * changing buf->pos and serializing frame header. * * This function assumes bufs->cur points to the last buf chain of the * frame(s). * * This function serializes frame header for HEADERS/PUSH_PROMISE and * handles their successive CONTINUATION frames. * * We don't process any padding here. */ static int frame_pack_headers_shared(nghttp2_bufs *bufs, nghttp2_frame_hd *frame_hd) { nghttp2_buf *buf; nghttp2_buf_chain *ci, *ce; nghttp2_frame_hd hd; buf = &bufs->head->buf; hd = *frame_hd; hd.length = nghttp2_buf_len(buf); DEBUGF("send: HEADERS/PUSH_PROMISE, payloadlen=%zu\n", hd.length); /* We have multiple frame buffers, which means one or more CONTINUATION frame is involved. Remove END_HEADERS flag from the first frame. */ if (bufs->head != bufs->cur) { hd.flags = (uint8_t)(hd.flags & ~NGHTTP2_FLAG_END_HEADERS); } buf->pos -= NGHTTP2_FRAME_HDLEN; nghttp2_frame_pack_frame_hd(buf->pos, &hd); if (bufs->head != bufs->cur) { /* 2nd and later frames are CONTINUATION frames. */ hd.type = NGHTTP2_CONTINUATION; /* We don't have no flags except for last CONTINUATION */ hd.flags = NGHTTP2_FLAG_NONE; ce = bufs->cur; for (ci = bufs->head->next; ci != ce; ci = ci->next) { buf = &ci->buf; hd.length = nghttp2_buf_len(buf); DEBUGF("send: int CONTINUATION, payloadlen=%zu\n", hd.length); buf->pos -= NGHTTP2_FRAME_HDLEN; nghttp2_frame_pack_frame_hd(buf->pos, &hd); } buf = &ci->buf; hd.length = nghttp2_buf_len(buf); /* Set END_HEADERS flag for last CONTINUATION */ hd.flags = NGHTTP2_FLAG_END_HEADERS; DEBUGF("send: last CONTINUATION, payloadlen=%zu\n", hd.length); buf->pos -= NGHTTP2_FRAME_HDLEN; nghttp2_frame_pack_frame_hd(buf->pos, &hd); } return 0; } int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame, nghttp2_hd_deflater *deflater) { size_t nv_offset; int rv; nghttp2_buf *buf; assert(bufs->head == bufs->cur); nv_offset = nghttp2_frame_headers_payload_nv_offset(frame); buf = &bufs->cur->buf; buf->pos += nv_offset; buf->last = buf->pos; /* This call will adjust buf->last to the correct position */ rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen); if (rv == NGHTTP2_ERR_BUFFER_ERROR) { rv = NGHTTP2_ERR_HEADER_COMP; } buf->pos -= nv_offset; if (rv != 0) { return rv; } if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { nghttp2_frame_pack_priority_spec(buf->pos, &frame->pri_spec); } frame->padlen = 0; frame->hd.length = nghttp2_bufs_len(bufs); return frame_pack_headers_shared(bufs, &frame->hd); } void nghttp2_frame_pack_priority_spec(uint8_t *buf, const nghttp2_priority_spec *pri_spec) { nghttp2_put_uint32be(buf, (uint32_t)pri_spec->stream_id); if (pri_spec->exclusive) { buf[0] |= 0x80; } buf[4] = (uint8_t)(pri_spec->weight - 1); } void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec, const uint8_t *payload) { int32_t dep_stream_id; uint8_t exclusive; int32_t weight; dep_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; exclusive = (payload[0] & 0x80) > 0; weight = payload[4] + 1; nghttp2_priority_spec_init(pri_spec, dep_stream_id, weight, exclusive); } void nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, const uint8_t *payload) { if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload); } else { nghttp2_priority_spec_default_init(&frame->pri_spec); } frame->nva = NULL; frame->nvlen = 0; } void nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame) { nghttp2_buf *buf; assert(bufs->head == bufs->cur); buf = &bufs->head->buf; assert(nghttp2_buf_avail(buf) >= NGHTTP2_PRIORITY_SPECLEN); buf->pos -= NGHTTP2_FRAME_HDLEN; nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); nghttp2_frame_pack_priority_spec(buf->last, &frame->pri_spec); buf->last += NGHTTP2_PRIORITY_SPECLEN; } void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, const uint8_t *payload) { nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload); } void nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs, nghttp2_rst_stream *frame) { nghttp2_buf *buf; assert(bufs->head == bufs->cur); buf = &bufs->head->buf; assert(nghttp2_buf_avail(buf) >= 4); buf->pos -= NGHTTP2_FRAME_HDLEN; nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); nghttp2_put_uint32be(buf->last, frame->error_code); buf->last += 4; } void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, const uint8_t *payload) { frame->error_code = nghttp2_get_uint32(payload); } int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame) { nghttp2_buf *buf; assert(bufs->head == bufs->cur); buf = &bufs->head->buf; if (nghttp2_buf_avail(buf) < frame->hd.length) { return NGHTTP2_ERR_FRAME_SIZE_ERROR; } buf->pos -= NGHTTP2_FRAME_HDLEN; nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); buf->last += nghttp2_frame_pack_settings_payload(buf->last, frame->iv, frame->niv); return 0; } size_t nghttp2_frame_pack_settings_payload(uint8_t *buf, const nghttp2_settings_entry *iv, size_t niv) { size_t i; for (i = 0; i < niv; ++i, buf += NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) { nghttp2_put_uint16be(buf, (uint16_t)iv[i].settings_id); nghttp2_put_uint32be(buf + 2, iv[i].value); } return NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH * niv; } void nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame, nghttp2_settings_entry *iv, size_t niv) { frame->iv = iv; frame->niv = niv; } void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv, const uint8_t *payload) { iv->settings_id = nghttp2_get_uint16(&payload[0]); iv->value = nghttp2_get_uint32(&payload[2]); } int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, size_t *niv_ptr, const uint8_t *payload, size_t payloadlen, nghttp2_mem *mem) { size_t i; *niv_ptr = payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH; if (*niv_ptr == 0) { *iv_ptr = NULL; return 0; } *iv_ptr = nghttp2_mem_malloc(mem, (*niv_ptr) * sizeof(nghttp2_settings_entry)); if (*iv_ptr == NULL) { return NGHTTP2_ERR_NOMEM; } for (i = 0; i < *niv_ptr; ++i) { size_t off = i * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH; nghttp2_frame_unpack_settings_entry(&(*iv_ptr)[i], &payload[off]); } return 0; } int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs, nghttp2_push_promise *frame, nghttp2_hd_deflater *deflater) { size_t nv_offset = 4; int rv; nghttp2_buf *buf; assert(bufs->head == bufs->cur); buf = &bufs->cur->buf; buf->pos += nv_offset; buf->last = buf->pos; /* This call will adjust buf->last to the correct position */ rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen); if (rv == NGHTTP2_ERR_BUFFER_ERROR) { rv = NGHTTP2_ERR_HEADER_COMP; } buf->pos -= nv_offset; if (rv != 0) { return rv; } nghttp2_put_uint32be(buf->pos, (uint32_t)frame->promised_stream_id); frame->padlen = 0; frame->hd.length = nghttp2_bufs_len(bufs); return frame_pack_headers_shared(bufs, &frame->hd); } void nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, const uint8_t *payload) { frame->promised_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; frame->nva = NULL; frame->nvlen = 0; } void nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame) { nghttp2_buf *buf; assert(bufs->head == bufs->cur); buf = &bufs->head->buf; assert(nghttp2_buf_avail(buf) >= 8); buf->pos -= NGHTTP2_FRAME_HDLEN; nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); buf->last = nghttp2_cpymem(buf->last, frame->opaque_data, sizeof(frame->opaque_data)); } void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, const uint8_t *payload) { memcpy(frame->opaque_data, payload, sizeof(frame->opaque_data)); } int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame) { int rv; nghttp2_buf *buf; assert(bufs->head == bufs->cur); buf = &bufs->head->buf; buf->pos -= NGHTTP2_FRAME_HDLEN; nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); nghttp2_put_uint32be(buf->last, (uint32_t)frame->last_stream_id); buf->last += 4; nghttp2_put_uint32be(buf->last, frame->error_code); buf->last += 4; rv = nghttp2_bufs_add(bufs, frame->opaque_data, frame->opaque_data_len); if (rv == NGHTTP2_ERR_BUFFER_ERROR) { return NGHTTP2_ERR_FRAME_SIZE_ERROR; } if (rv != 0) { return rv; } return 0; } void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, const uint8_t *payload, uint8_t *var_gift_payload, size_t var_gift_payloadlen) { frame->last_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; frame->error_code = nghttp2_get_uint32(payload + 4); frame->opaque_data = var_gift_payload; frame->opaque_data_len = var_gift_payloadlen; } int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame, const uint8_t *payload, size_t payloadlen, nghttp2_mem *mem) { uint8_t *var_gift_payload; size_t var_gift_payloadlen; if (payloadlen > 8) { var_gift_payloadlen = payloadlen - 8; } else { var_gift_payloadlen = 0; } if (!var_gift_payloadlen) { var_gift_payload = NULL; } else { var_gift_payload = nghttp2_mem_malloc(mem, var_gift_payloadlen); if (var_gift_payload == NULL) { return NGHTTP2_ERR_NOMEM; } memcpy(var_gift_payload, payload + 8, var_gift_payloadlen); } nghttp2_frame_unpack_goaway_payload(frame, payload, var_gift_payload, var_gift_payloadlen); return 0; } void nghttp2_frame_pack_window_update(nghttp2_bufs *bufs, nghttp2_window_update *frame) { nghttp2_buf *buf; assert(bufs->head == bufs->cur); buf = &bufs->head->buf; assert(nghttp2_buf_avail(buf) >= 4); buf->pos -= NGHTTP2_FRAME_HDLEN; nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); nghttp2_put_uint32be(buf->last, (uint32_t)frame->window_size_increment); buf->last += 4; } void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame, const uint8_t *payload) { frame->window_size_increment = nghttp2_get_uint32(payload) & NGHTTP2_WINDOW_SIZE_INCREMENT_MASK; } void nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame) { int rv; nghttp2_buf *buf; nghttp2_ext_altsvc *altsvc; /* This is required with --disable-assert. */ (void)rv; altsvc = frame->payload; buf = &bufs->head->buf; assert(nghttp2_buf_avail(buf) >= 2 + altsvc->origin_len + altsvc->field_value_len); buf->pos -= NGHTTP2_FRAME_HDLEN; nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); nghttp2_put_uint16be(buf->last, (uint16_t)altsvc->origin_len); buf->last += 2; rv = nghttp2_bufs_add(bufs, altsvc->origin, altsvc->origin_len); assert(rv == 0); rv = nghttp2_bufs_add(bufs, altsvc->field_value, altsvc->field_value_len); assert(rv == 0); } void nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame, size_t origin_len, uint8_t *payload, size_t payloadlen) { nghttp2_ext_altsvc *altsvc; uint8_t *p; altsvc = frame->payload; p = payload; altsvc->origin = p; p += origin_len; altsvc->origin_len = origin_len; altsvc->field_value = p; altsvc->field_value_len = (size_t)(payload + payloadlen - p); } int nghttp2_frame_unpack_altsvc_payload2(nghttp2_extension *frame, const uint8_t *payload, size_t payloadlen, nghttp2_mem *mem) { uint8_t *buf; size_t origin_len; if (payloadlen < 2) { return NGHTTP2_FRAME_SIZE_ERROR; } origin_len = nghttp2_get_uint16(payload); buf = nghttp2_mem_malloc(mem, payloadlen - 2); if (!buf) { return NGHTTP2_ERR_NOMEM; } nghttp2_cpymem(buf, payload + 2, payloadlen - 2); nghttp2_frame_unpack_altsvc_payload(frame, origin_len, buf, payloadlen - 2); return 0; } int nghttp2_frame_pack_origin(nghttp2_bufs *bufs, nghttp2_extension *frame) { nghttp2_buf *buf; nghttp2_ext_origin *origin; nghttp2_origin_entry *orig; size_t i; origin = frame->payload; buf = &bufs->head->buf; if (nghttp2_buf_avail(buf) < frame->hd.length) { return NGHTTP2_ERR_FRAME_SIZE_ERROR; } buf->pos -= NGHTTP2_FRAME_HDLEN; nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); for (i = 0; i < origin->nov; ++i) { orig = &origin->ov[i]; nghttp2_put_uint16be(buf->last, (uint16_t)orig->origin_len); buf->last += 2; buf->last = nghttp2_cpymem(buf->last, orig->origin, orig->origin_len); } assert(nghttp2_buf_len(buf) == NGHTTP2_FRAME_HDLEN + frame->hd.length); return 0; } int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame, const uint8_t *payload, size_t payloadlen, nghttp2_mem *mem) { nghttp2_ext_origin *origin; const uint8_t *p, *end; uint8_t *dst; size_t originlen; nghttp2_origin_entry *ov; size_t nov = 0; size_t len = 0; origin = frame->payload; p = end = payload; if (payloadlen) { end += payloadlen; } for (; p != end;) { if (end - p < 2) { return NGHTTP2_ERR_FRAME_SIZE_ERROR; } originlen = nghttp2_get_uint16(p); p += 2; if (originlen == 0) { continue; } if (originlen > (size_t)(end - p)) { return NGHTTP2_ERR_FRAME_SIZE_ERROR; } p += originlen; /* 1 for terminal NULL */ len += originlen + 1; ++nov; } if (nov == 0) { origin->ov = NULL; origin->nov = 0; return 0; } len += nov * sizeof(nghttp2_origin_entry); ov = nghttp2_mem_malloc(mem, len); if (ov == NULL) { return NGHTTP2_ERR_NOMEM; } origin->ov = ov; origin->nov = nov; dst = (uint8_t *)ov + nov * sizeof(nghttp2_origin_entry); p = payload; for (; p != end;) { originlen = nghttp2_get_uint16(p); p += 2; if (originlen == 0) { continue; } ov->origin = dst; ov->origin_len = originlen; dst = nghttp2_cpymem(dst, p, originlen); *dst++ = '\0'; p += originlen; ++ov; } return 0; } void nghttp2_frame_pack_priority_update(nghttp2_bufs *bufs, nghttp2_extension *frame) { int rv; nghttp2_buf *buf; nghttp2_ext_priority_update *priority_update; /* This is required with --disable-assert. */ (void)rv; priority_update = frame->payload; buf = &bufs->head->buf; assert(nghttp2_buf_avail(buf) >= 4 + priority_update->field_value_len); buf->pos -= NGHTTP2_FRAME_HDLEN; nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); nghttp2_put_uint32be(buf->last, (uint32_t)priority_update->stream_id); buf->last += 4; rv = nghttp2_bufs_add(bufs, priority_update->field_value, priority_update->field_value_len); assert(rv == 0); } void nghttp2_frame_unpack_priority_update_payload(nghttp2_extension *frame, uint8_t *payload, size_t payloadlen) { nghttp2_ext_priority_update *priority_update; assert(payloadlen >= 4); priority_update = frame->payload; priority_update->stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; if (payloadlen > 4) { priority_update->field_value = payload + 4; priority_update->field_value_len = payloadlen - 4; } else { priority_update->field_value = NULL; priority_update->field_value_len = 0; } } nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv, size_t niv, nghttp2_mem *mem) { nghttp2_settings_entry *iv_copy; size_t len = niv * sizeof(nghttp2_settings_entry); if (len == 0) { return NULL; } iv_copy = nghttp2_mem_malloc(mem, len); if (iv_copy == NULL) { return NULL; } memcpy(iv_copy, iv, len); return iv_copy; } int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b) { if (a->namelen != b->namelen || a->valuelen != b->valuelen) { return 0; } if (a->name == NULL || b->name == NULL) { assert(a->namelen == 0); assert(b->namelen == 0); } else if (memcmp(a->name, b->name, a->namelen) != 0) { return 0; } if (a->value == NULL || b->value == NULL) { assert(a->valuelen == 0); assert(b->valuelen == 0); } else if (memcmp(a->value, b->value, a->valuelen) != 0) { return 0; } return 1; } void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem) { nghttp2_mem_free(mem, nva); } static int bytes_compar(const uint8_t *a, size_t alen, const uint8_t *b, size_t blen) { int rv; if (alen == blen) { return memcmp(a, b, alen); } if (alen < blen) { rv = memcmp(a, b, alen); if (rv == 0) { return -1; } return rv; } rv = memcmp(a, b, blen); if (rv == 0) { return 1; } return rv; } int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs) { return bytes_compar(lhs->name, lhs->namelen, rhs->name, rhs->namelen); } static int nv_compar(const void *lhs, const void *rhs) { const nghttp2_nv *a = (const nghttp2_nv *)lhs; const nghttp2_nv *b = (const nghttp2_nv *)rhs; int rv; rv = bytes_compar(a->name, a->namelen, b->name, b->namelen); if (rv == 0) { return bytes_compar(a->value, a->valuelen, b->value, b->valuelen); } return rv; } void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen) { qsort(nva, nvlen, sizeof(nghttp2_nv), nv_compar); } int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem) { size_t i; uint8_t *data = NULL; size_t buflen = 0; nghttp2_nv *p; if (nvlen == 0) { *nva_ptr = NULL; return 0; } for (i = 0; i < nvlen; ++i) { /* + 1 for null-termination */ if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) == 0) { buflen += nva[i].namelen + 1; } if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE) == 0) { buflen += nva[i].valuelen + 1; } } buflen += sizeof(nghttp2_nv) * nvlen; *nva_ptr = nghttp2_mem_malloc(mem, buflen); if (*nva_ptr == NULL) { return NGHTTP2_ERR_NOMEM; } p = *nva_ptr; data = (uint8_t *)(*nva_ptr) + sizeof(nghttp2_nv) * nvlen; for (i = 0; i < nvlen; ++i) { p->flags = nva[i].flags; if (nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) { p->name = nva[i].name; p->namelen = nva[i].namelen; } else { if (nva[i].namelen) { memcpy(data, nva[i].name, nva[i].namelen); } p->name = data; p->namelen = nva[i].namelen; data[p->namelen] = '\0'; nghttp2_downcase(p->name, p->namelen); data += nva[i].namelen + 1; } if (nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE) { p->value = nva[i].value; p->valuelen = nva[i].valuelen; } else { if (nva[i].valuelen) { memcpy(data, nva[i].value, nva[i].valuelen); } p->value = data; p->valuelen = nva[i].valuelen; data[p->valuelen] = '\0'; data += nva[i].valuelen + 1; } ++p; } return 0; } int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) { size_t i; for (i = 0; i < niv; ++i) { switch (iv[i].settings_id) { case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: break; case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: break; case NGHTTP2_SETTINGS_ENABLE_PUSH: if (iv[i].value != 0 && iv[i].value != 1) { return 0; } break; case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: if (iv[i].value > (uint32_t)NGHTTP2_MAX_WINDOW_SIZE) { return 0; } break; case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: if (iv[i].value < NGHTTP2_MAX_FRAME_SIZE_MIN || iv[i].value > NGHTTP2_MAX_FRAME_SIZE_MAX) { return 0; } break; case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: break; case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: if (iv[i].value != 0 && iv[i].value != 1) { return 0; } break; case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES: if (iv[i].value != 0 && iv[i].value != 1) { return 0; } break; } } return 1; } static void frame_set_pad(nghttp2_buf *buf, size_t padlen, int framehd_only) { size_t trail_padlen; size_t newlen; DEBUGF("send: padlen=%zu, shift left 1 bytes\n", padlen); memmove(buf->pos - 1, buf->pos, NGHTTP2_FRAME_HDLEN); --buf->pos; buf->pos[4] |= NGHTTP2_FLAG_PADDED; newlen = (nghttp2_get_uint32(buf->pos) >> 8) + padlen; nghttp2_put_uint32be(buf->pos, (uint32_t)((newlen << 8) + buf->pos[3])); if (framehd_only) { return; } trail_padlen = padlen - 1; buf->pos[NGHTTP2_FRAME_HDLEN] = (uint8_t)trail_padlen; /* zero out padding */ memset(buf->last, 0, trail_padlen); /* extend buffers trail_padlen bytes, since we ate previous padlen - trail_padlen byte(s) */ buf->last += trail_padlen; } void nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, size_t padlen, int framehd_only) { nghttp2_buf *buf; if (padlen == 0) { DEBUGF("send: padlen = 0, nothing to do\n"); return; } /* * We have arranged bufs like this: * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | |Frame header | Frame payload... : * +-+-----------------+-------------------------------------------+ * | |Frame header | Frame payload... : * +-+-----------------+-------------------------------------------+ * | |Frame header | Frame payload... : * +-+-----------------+-------------------------------------------+ * * We arranged padding so that it is included in the first frame * completely. For padded frame, we are going to adjust buf->pos of * frame which includes padding and serialize (memmove) frame header * in the correct position. Also extends buf->last to include * padding. */ buf = &bufs->head->buf; assert(nghttp2_buf_avail(buf) >= padlen - 1); frame_set_pad(buf, padlen, framehd_only); hd->length += padlen; hd->flags |= NGHTTP2_FLAG_PADDED; DEBUGF("send: final payloadlen=%zu, padlen=%zu\n", hd->length, padlen); } nghttp2-1.68.0/lib/PaxHeaders/nghttp2_buf.h0000644000000000000000000000013215077107270015421 xustar0030 mtime=1761382072.976444217 30 atime=1761382104.937316008 30 ctime=1761382107.923303783 nghttp2-1.68.0/lib/nghttp2_buf.h0000644000175100017510000003315115077107270016014 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_BUF_H #define NGHTTP2_BUF_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include #include "nghttp2_int.h" #include "nghttp2_mem.h" typedef struct { /* This points to the beginning of the buffer. The effective range of buffer is [begin, end). */ uint8_t *begin; /* This points to the memory one byte beyond the end of the buffer. */ uint8_t *end; /* The position indicator for effective start of the buffer. pos <= last must be hold. */ uint8_t *pos; /* The position indicator for effective one beyond of the end of the buffer. last <= end must be hold. */ uint8_t *last; /* Mark arbitrary position in buffer [begin, end) */ uint8_t *mark; } nghttp2_buf; #define nghttp2_buf_len(BUF) ((size_t)((BUF)->last - (BUF)->pos)) #define nghttp2_buf_avail(BUF) ((size_t)((BUF)->end - (BUF)->last)) #define nghttp2_buf_mark_avail(BUF) ((size_t)((BUF)->mark - (BUF)->last)) #define nghttp2_buf_cap(BUF) ((size_t)((BUF)->end - (BUF)->begin)) #define nghttp2_buf_pos_offset(BUF) ((size_t)((BUF)->pos - (BUF)->begin)) #define nghttp2_buf_last_offset(BUF) ((size_t)((BUF)->last - (BUF)->begin)) #define nghttp2_buf_shift_right(BUF, AMT) \ do { \ (BUF)->pos += AMT; \ (BUF)->last += AMT; \ } while (0) #define nghttp2_buf_shift_left(BUF, AMT) \ do { \ (BUF)->pos -= AMT; \ (BUF)->last -= AMT; \ } while (0) /* * Initializes the |buf|. No memory is allocated in this function. Use * nghttp2_buf_reserve() to allocate memory. */ void nghttp2_buf_init(nghttp2_buf *buf); /* * Initializes the |buf| and allocates at least |initial| bytes of * memory. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory */ int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial, nghttp2_mem *mem); /* * Frees buffer in |buf|. */ void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem); /* * Extends buffer so that nghttp2_buf_cap() returns at least * |new_cap|. If extensions took place, buffer pointers in |buf| will * change. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory */ int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem); /* * Resets pos, last, mark member of |buf| to buf->begin. */ void nghttp2_buf_reset(nghttp2_buf *buf); /* * Initializes |buf| using supplied buffer |begin| of length * |len|. Semantically, the application should not call *_reserve() or * nghttp2_free() functions for |buf|. */ void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len); struct nghttp2_buf_chain; typedef struct nghttp2_buf_chain nghttp2_buf_chain; /* Chains 2 buffers */ struct nghttp2_buf_chain { /* Points to the subsequent buffer. NULL if there is no such buffer. */ nghttp2_buf_chain *next; nghttp2_buf buf; }; typedef struct { /* Points to the first buffer */ nghttp2_buf_chain *head; /* Buffer pointer where write occurs. */ nghttp2_buf_chain *cur; /* Memory allocator */ nghttp2_mem *mem; /* The buffer capacity of each buf. This field may be 0 if nghttp2_bufs is initialized by nghttp2_bufs_wrap_init* family functions. */ size_t chunk_length; /* The maximum number of nghttp2_buf_chain */ size_t max_chunk; /* The number of nghttp2_buf_chain allocated */ size_t chunk_used; /* The number of nghttp2_buf_chain to keep on reset */ size_t chunk_keep; /* pos offset from begin in each buffers. On initialization and reset, buf->pos and buf->last are positioned at buf->begin + offset. */ size_t offset; } nghttp2_bufs; /* * This is the same as calling nghttp2_bufs_init2 with the given * arguments and offset = 0. */ int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk, nghttp2_mem *mem); /* * This is the same as calling nghttp2_bufs_init3 with the given * arguments and chunk_keep = max_chunk. */ int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk, size_t offset, nghttp2_mem *mem); /* * Initializes |bufs|. Each buffer size is given in the * |chunk_length|. The maximum number of buffers is given in the * |max_chunk|. On reset, first |chunk_keep| buffers are kept and * remaining buffers are deleted. Each buffer will have bufs->pos and * bufs->last shifted to left by |offset| bytes on creation and reset. * * This function allocates first buffer. bufs->head and bufs->cur * will point to the first buffer after this call. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_INVALID_ARGUMENT * chunk_keep is 0; or max_chunk < chunk_keep; or offset is too * long. */ int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk, size_t chunk_keep, size_t offset, nghttp2_mem *mem); /* * Frees any related resources to the |bufs|. */ void nghttp2_bufs_free(nghttp2_bufs *bufs); /* * Initializes |bufs| using supplied buffer |begin| of length |len|. * The first buffer bufs->head uses buffer |begin|. The buffer size * is fixed and no extra chunk buffer is allocated. In other * words, max_chunk = chunk_keep = 1. To free the resource allocated * for |bufs|, use nghttp2_bufs_wrap_free(). * * Don't use the function which performs allocation, such as * nghttp2_bufs_realloc(). * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. */ int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len, nghttp2_mem *mem); /* * Initializes |bufs| using supplied |veclen| size of buf vector * |vec|. The number of buffers is fixed and no extra chunk buffer is * allocated. In other words, max_chunk = chunk_keep = |in_len|. To * free the resource allocated for |bufs|, use * nghttp2_bufs_wrap_free(). * * Don't use the function which performs allocation, such as * nghttp2_bufs_realloc(). * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. */ int nghttp2_bufs_wrap_init2(nghttp2_bufs *bufs, const nghttp2_vec *vec, size_t veclen, nghttp2_mem *mem); /* * Frees any related resource to the |bufs|. This function does not * free supplied buffer provided in nghttp2_bufs_wrap_init(). */ void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs); /* * Reallocates internal buffer using |chunk_length|. The max_chunk, * chunk_keep and offset do not change. After successful allocation * of new buffer, previous buffers are deallocated without copying * anything into new buffers. chunk_used is reset to 1. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_INVALID_ARGUMENT * chunk_length < offset */ int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length); /* * Appends the |data| of length |len| to the |bufs|. The write starts * at bufs->cur->buf.last. A new buffers will be allocated to store * all data. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_BUFFER_ERROR * Out of buffer space. */ int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len); /* * Appends a single byte |b| to the |bufs|. The write starts at * bufs->cur->buf.last. A new buffers will be allocated to store all * data. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_BUFFER_ERROR * Out of buffer space. */ int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b); /* * Behaves like nghttp2_bufs_addb(), but this does not update * buf->last pointer. */ int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b); #define nghttp2_bufs_fast_addb(BUFS, B) \ do { \ *(BUFS)->cur->buf.last++ = B; \ } while (0) #define nghttp2_bufs_fast_addb_hold(BUFS, B) \ do { \ *(BUFS)->cur->buf.last = B; \ } while (0) /* * Performs bitwise-OR of |b| at bufs->cur->buf.last. A new buffers * will be allocated if necessary. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_BUFFER_ERROR * Out of buffer space. */ int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b); /* * Behaves like nghttp2_bufs_orb(), but does not update buf->last * pointer. */ int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b); #define nghttp2_bufs_fast_orb(BUFS, B) \ do { \ uint8_t **p = &(BUFS)->cur->buf.last; \ **p = (uint8_t)(**p | (B)); \ ++(*p); \ } while (0) #define nghttp2_bufs_fast_orb_hold(BUFS, B) \ do { \ uint8_t *p = (BUFS)->cur->buf.last; \ *p = (uint8_t)(*p | (B)); \ } while (0) /* * Copies all data stored in |bufs| to the contiguous buffer. This * function allocates the contiguous memory to store all data in * |bufs| and assigns it to |*out|. * * The contents of |bufs| is left unchanged. * * This function returns the length of copied data and assigns the * pointer to copied data to |*out| if it succeeds, or one of the * following negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory */ nghttp2_ssize nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out); /* * Copies all data stored in |bufs| to |out|. This function assumes * that the buffer space pointed by |out| has at least * nghttp2_bufs(bufs) bytes. * * The contents of |bufs| is left unchanged. * * This function returns the length of copied data. */ size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out); /* * Resets |bufs| and makes the buffers empty. */ void nghttp2_bufs_reset(nghttp2_bufs *bufs); /* * Moves bufs->cur to bufs->cur->next. If resulting bufs->cur is * NULL, this function allocates new buffers and bufs->cur points to * it. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory * NGHTTP2_ERR_BUFFER_ERROR * Out of buffer space. */ int nghttp2_bufs_advance(nghttp2_bufs *bufs); /* Sets bufs->cur to bufs->head */ #define nghttp2_bufs_rewind(BUFS) \ do { \ (BUFS)->cur = (BUFS)->head; \ } while (0) /* * Move bufs->cur, from the current position, using next member, to * the last buf which has nghttp2_buf_len(buf) > 0 without seeing buf * which satisfies nghttp2_buf_len(buf) == 0. If * nghttp2_buf_len(&bufs->cur->buf) == 0 or bufs->cur->next is NULL, * bufs->cur is unchanged. */ void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs); /* * Returns nonzero if bufs->cur->next is not empty. */ int nghttp2_bufs_next_present(nghttp2_bufs *bufs); #define nghttp2_bufs_cur_avail(BUFS) nghttp2_buf_avail(&(BUFS)->cur->buf) /* * Returns the total buffer length of |bufs|. */ size_t nghttp2_bufs_len(nghttp2_bufs *bufs); #endif /* !defined(NGHTTP2_BUF_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_pq.h0000644000000000000000000000013215077107270015265 xustar0030 mtime=1761382072.980444199 30 atime=1761382104.930316039 30 ctime=1761382107.916303804 nghttp2-1.68.0/lib/nghttp2_pq.h0000644000175100017510000000742115077107270015661 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_PQ_H #define NGHTTP2_PQ_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include #include "nghttp2_int.h" #include "nghttp2_mem.h" /* Implementation of priority queue */ typedef struct { size_t index; } nghttp2_pq_entry; typedef struct { /* The pointer to the pointer to the item stored */ nghttp2_pq_entry **q; /* Memory allocator */ nghttp2_mem *mem; /* The number of items stored */ size_t length; /* The maximum number of items this pq can store. This is automatically extended when length is reached to this value. */ size_t capacity; /* The less function between items */ nghttp2_less less; } nghttp2_pq; /* * Initializes priority queue |pq| with compare function |cmp|. */ void nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem); /* * Deallocates any resources allocated for |pq|. The stored items are * not freed by this function. */ void nghttp2_pq_free(nghttp2_pq *pq); /* * Adds |item| to the priority queue |pq|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. */ int nghttp2_pq_push(nghttp2_pq *pq, nghttp2_pq_entry *item); /* * Returns item at the top of the queue |pq|. If the queue is empty, * this function returns NULL. */ nghttp2_pq_entry *nghttp2_pq_top(nghttp2_pq *pq); /* * Pops item at the top of the queue |pq|. The popped item is not * freed by this function. */ void nghttp2_pq_pop(nghttp2_pq *pq); /* * Returns nonzero if the queue |pq| is empty. */ int nghttp2_pq_empty(nghttp2_pq *pq); /* * Returns the number of items in the queue |pq|. */ size_t nghttp2_pq_size(nghttp2_pq *pq); typedef int (*nghttp2_pq_item_cb)(nghttp2_pq_entry *item, void *arg); /* * Updates each item in |pq| using function |fun| and re-construct * priority queue. The |fun| must return non-zero if it modifies the * item in a way that it affects ordering in the priority queue. The * |arg| is passed to the 2nd parameter of |fun|. */ void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg); /* * Applies |fun| to each item in |pq|. The |arg| is passed as arg * parameter to callback function. This function must not change the * ordering key. If the return value from callback is nonzero, this * function returns 1 immediately without iterating remaining items. * Otherwise this function returns 0. */ int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg); /* * Removes |item| from priority queue. */ void nghttp2_pq_remove(nghttp2_pq *pq, nghttp2_pq_entry *item); #endif /* !defined(NGHTTP2_PQ_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_hd_huffman.h0000644000000000000000000000013115077107270016743 xustar0030 mtime=1761382072.978444208 29 atime=1761382104.94831596 30 ctime=1761382107.934303752 nghttp2-1.68.0/lib/nghttp2_hd_huffman.h0000644000175100017510000000477415077107270017350 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_HD_HUFFMAN_H #define NGHTTP2_HD_HUFFMAN_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include typedef enum { /* FSA accepts this state as the end of huffman encoding sequence. */ NGHTTP2_HUFF_ACCEPTED = 1 << 14, /* This state emits symbol */ NGHTTP2_HUFF_SYM = 1 << 15, } nghttp2_huff_decode_flag; typedef struct { /* fstate is the current huffman decoding state, which is actually the node ID of internal huffman tree with nghttp2_huff_decode_flag OR-ed. We have 257 leaf nodes, but they are identical to root node other than emitting a symbol, so we have 256 internal nodes [1..255], inclusive. The node ID 256 is a special node and it is a terminal state that means decoding failed. */ uint16_t fstate; /* symbol if NGHTTP2_HUFF_SYM flag set */ uint8_t sym; } nghttp2_huff_decode; typedef nghttp2_huff_decode huff_decode_table_type[16]; typedef struct { /* fstate is the current huffman decoding state. */ uint16_t fstate; } nghttp2_hd_huff_decode_context; typedef struct { /* The number of bits in this code */ uint32_t nbits; /* Huffman code aligned to LSB */ uint32_t code; } nghttp2_huff_sym; extern const nghttp2_huff_sym huff_sym_table[]; extern const nghttp2_huff_decode huff_decode_table[][16]; #endif /* !defined(NGHTTP2_HD_HUFFMAN_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_net.h0000644000000000000000000000013215077107270015433 xustar0030 mtime=1761382072.979444204 30 atime=1761382104.945315973 30 ctime=1761382107.932303757 nghttp2-1.68.0/lib/nghttp2_net.h0000644000175100017510000000525215077107270016027 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_NET_H #define NGHTTP2_NET_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #ifdef HAVE_ARPA_INET_H # include #endif /* defined(HAVE_ARPA_INET_H) */ #ifdef HAVE_NETINET_IN_H # include #endif /* defined(HAVE_NETINET_IN_H) */ #include #ifdef WIN32 /* Windows requires ws2_32 library for ntonl family functions. We define inline functions for those function so that we don't have dependency on that lib. */ # ifdef _MSC_VER # define STIN static __inline # else /* !defined(_MSC_VER) */ # define STIN static inline # endif /* !defined(_MSC_VER) */ STIN uint32_t htonl(uint32_t hostlong) { uint32_t res; unsigned char *p = (unsigned char *)&res; *p++ = (unsigned char)(hostlong >> 24); *p++ = (hostlong >> 16) & 0xffu; *p++ = (hostlong >> 8) & 0xffu; *p = hostlong & 0xffu; return res; } STIN uint16_t htons(uint16_t hostshort) { uint16_t res; unsigned char *p = (unsigned char *)&res; *p++ = (unsigned char)(hostshort >> 8); *p = hostshort & 0xffu; return res; } STIN uint32_t ntohl(uint32_t netlong) { uint32_t res; unsigned char *p = (unsigned char *)&netlong; res = (uint32_t)(*p++ << 24); res += (uint32_t)(*p++ << 16); res += (uint32_t)(*p++ << 8); res += *p; return res; } STIN uint16_t ntohs(uint16_t netshort) { uint16_t res; unsigned char *p = (unsigned char *)&netshort; res = (uint16_t)(*p++ << 8); res += *p; return res; } #endif /* defined(WIN32) */ #endif /* !defined(NGHTTP2_NET_H) */ nghttp2-1.68.0/lib/PaxHeaders/version.rc.in0000644000000000000000000000013115077107270015445 xustar0029 mtime=1761382072.98244419 30 atime=1761382104.999315735 30 ctime=1761382107.986303601 nghttp2-1.68.0/lib/version.rc.in0000644000175100017510000000214315077107270016036 0ustar00runnerrunner#include VS_VERSION_INFO VERSIONINFO FILEVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0 PRODUCTVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0 FILEFLAGSMASK 0x3fL FILEOS 0x40004L FILETYPE 0x2L FILESUBTYPE 0x0L #ifdef _DEBUG #define VER_STR "@PROJECT_VERSION@.0 (MSVC debug)" #define DBG "d" FILEFLAGS 0x1L #else #define VER_STR "@PROJECT_VERSION@.0 (MSVC release)" #define DBG "" FILEFLAGS 0x0L #endif BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "https://nghttp2.org/" VALUE "FileDescription", "nghttp2; HTTP/2 C library" VALUE "FileVersion", VER_STR VALUE "InternalName", "nghttp2" DBG VALUE "LegalCopyright", "The MIT License" VALUE "LegalTrademarks", "" VALUE "OriginalFilename", "nghttp2" DBG ".dll" VALUE "ProductName", "NGHTTP2." VALUE "ProductVersion", VER_STR END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END nghttp2-1.68.0/lib/PaxHeaders/includes0000644000000000000000000000013215077107334014560 xustar0030 mtime=1761382108.022303497 30 atime=1761382109.795298372 30 ctime=1761382108.022303497 nghttp2-1.68.0/lib/includes/0000755000175100017510000000000015077107334015225 5ustar00runnerrunnernghttp2-1.68.0/lib/includes/PaxHeaders/CMakeLists.txt0000644000000000000000000000013215077107270017374 xustar0030 mtime=1761382072.975444222 30 atime=1761382105.036315572 30 ctime=1761382108.022303497 nghttp2-1.68.0/lib/includes/CMakeLists.txt0000644000175100017510000000022015077107270017756 0ustar00runnerrunnerinstall(FILES nghttp2/nghttp2.h "${CMAKE_CURRENT_BINARY_DIR}/nghttp2/nghttp2ver.h" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/nghttp2") nghttp2-1.68.0/lib/includes/PaxHeaders/Makefile.in0000644000000000000000000000013115077107305016677 xustar0029 mtime=1761382085.61538696 30 atime=1761382103.805320995 30 ctime=1761382108.020303503 nghttp2-1.68.0/lib/includes/Makefile.in0000644000175100017510000005053415077107305017277 0ustar00runnerrunner# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # nghttp2 - HTTP/2 C Library # Copyright (c) 2012 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = lib/includes ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(nobase_include_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(includedir)" HEADERS = $(nobase_include_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPLDFLAGS = @APPLDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BPFCFLAGS = @BPFCFLAGS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CXX = @CXX@ CXX1XCXXFLAGS = @CXX1XCXXFLAGS@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXTRABPFCFLAGS = @EXTRABPFCFLAGS@ EXTRACFLAG = @EXTRACFLAG@ EXTRA_DEFS = @EXTRA_DEFS@ FGREP = @FGREP@ FILECMD = @FILECMD@ GREP = @GREP@ HAVE_CXX20 = @HAVE_CXX20@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ JANSSON_CFLAGS = @JANSSON_CFLAGS@ JANSSON_LIBS = @JANSSON_LIBS@ JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ JEMALLOC_LIBS = @JEMALLOC_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBBPF_CFLAGS = @LIBBPF_CFLAGS@ LIBBPF_LIBS = @LIBBPF_LIBS@ LIBBROTLIDEC_CFLAGS = @LIBBROTLIDEC_CFLAGS@ LIBBROTLIDEC_LIBS = @LIBBROTLIDEC_LIBS@ LIBBROTLIENC_CFLAGS = @LIBBROTLIENC_CFLAGS@ LIBBROTLIENC_LIBS = @LIBBROTLIENC_LIBS@ LIBCARES_CFLAGS = @LIBCARES_CFLAGS@ LIBCARES_LIBS = @LIBCARES_LIBS@ LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ LIBEV_CFLAGS = @LIBEV_CFLAGS@ LIBEV_LIBS = @LIBEV_LIBS@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@ LIBMRUBY_LIBS = @LIBMRUBY_LIBS@ LIBNGHTTP3_CFLAGS = @LIBNGHTTP3_CFLAGS@ LIBNGHTTP3_LIBS = @LIBNGHTTP3_LIBS@ LIBNGTCP2_CFLAGS = @LIBNGTCP2_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS = @LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_LIBS = @LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS = @LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS@ LIBNGTCP2_CRYPTO_LIBRESSL_LIBS = @LIBNGTCP2_CRYPTO_LIBRESSL_LIBS@ LIBNGTCP2_CRYPTO_OSSL_CFLAGS = @LIBNGTCP2_CRYPTO_OSSL_CFLAGS@ LIBNGTCP2_CRYPTO_OSSL_LIBS = @LIBNGTCP2_CRYPTO_OSSL_LIBS@ LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS = @LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ LIBNGTCP2_CRYPTO_QUICTLS_LIBS = @LIBNGTCP2_CRYPTO_QUICTLS_LIBS@ LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS = @LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS@ LIBNGTCP2_CRYPTO_WOLFSSL_LIBS = @LIBNGTCP2_CRYPTO_WOLFSSL_LIBS@ LIBNGTCP2_LIBS = @LIBNGTCP2_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBTOOL_LDFLAGS = @LIBTOOL_LDFLAGS@ LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ LIBXML2_LIBS = @LIBXML2_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_AGE = @LT_AGE@ LT_CURRENT = @LT_CURRENT@ LT_REVISION = @LT_REVISION@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ SYSTEMD_LIBS = @SYSTEMD_LIBS@ TESTLDADD = @TESTLDADD@ VERSION = @VERSION@ WARNCFLAGS = @WARNCFLAGS@ WARNCXXFLAGS = @WARNCXXFLAGS@ WOLFSSL_CFLAGS = @WOLFSSL_CFLAGS@ WOLFSSL_LIBS = @WOLFSSL_LIBS@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ EXTRA_DIST = CMakeLists.txt nobase_include_HEADERS = nghttp2/nghttp2.h nghttp2/nghttp2ver.h all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lib/includes/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu lib/includes/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-nobase_includeHEADERS: $(nobase_include_HEADERS) @$(NORMAL_INSTALL) @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ fi; \ $(am__nobase_list) | while read dir files; do \ xfiles=; for file in $$files; do \ if test -f "$$file"; then xfiles="$$xfiles $$file"; \ else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \ test -z "$$xfiles" || { \ test "x$$dir" = x. || { \ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)/$$dir'"; \ $(MKDIR_P) "$(DESTDIR)$(includedir)/$$dir"; }; \ echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(includedir)/$$dir'"; \ $(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(includedir)/$$dir" || exit $$?; }; \ done uninstall-nobase_includeHEADERS: @$(NORMAL_UNINSTALL) @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(HEADERS) installdirs: for dir in "$(DESTDIR)$(includedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-nobase_includeHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-nobase_includeHEADERS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool cscopelist-am ctags ctags-am distclean \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man \ install-nobase_includeHEADERS install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-nobase_includeHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nghttp2-1.68.0/lib/includes/PaxHeaders/nghttp20000644000000000000000000000013215077107334016146 xustar0030 mtime=1761382108.019303506 30 atime=1761382109.795298372 30 ctime=1761382108.019303506 nghttp2-1.68.0/lib/includes/nghttp2/0000755000175100017510000000000015077107334016613 5ustar00runnerrunnernghttp2-1.68.0/lib/includes/nghttp2/PaxHeaders/nghttp2.h0000644000000000000000000000013115077107270017760 xustar0030 mtime=1761382072.976444217 29 atime=1761382105.03231559 30 ctime=1761382108.018303509 nghttp2-1.68.0/lib/includes/nghttp2/nghttp2.h0000644000175100017510000073126215077107270020364 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013, 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_H #define NGHTTP2_H /* Define WIN32 when build target is Win32 API (borrowed from libcurl) */ #if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) # define WIN32 #endif /* Compatibility for non-Clang compilers */ #ifndef __has_declspec_attribute # define __has_declspec_attribute(x) 0 #endif #ifdef __cplusplus extern "C" { #endif #include #if defined(_MSC_VER) && (_MSC_VER < 1800) /* MSVC < 2013 does not have inttypes.h because it is not C99 compliant. See compiler macros and version number in https://sourceforge.net/p/predef/wiki/Compilers/ */ # include #else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ # include #endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ #include #include #include #include #ifdef NGHTTP2_STATICLIB # define NGHTTP2_EXTERN #elif defined(WIN32) || \ (__has_declspec_attribute(dllexport) && __has_declspec_attribute(dllimport)) # ifdef BUILDING_NGHTTP2 # define NGHTTP2_EXTERN __declspec(dllexport) # else /* !BUILDING_NGHTTP2 */ # define NGHTTP2_EXTERN __declspec(dllimport) # endif /* !BUILDING_NGHTTP2 */ #else /* !defined(WIN32) */ # ifdef BUILDING_NGHTTP2 # define NGHTTP2_EXTERN __attribute__((visibility("default"))) # else /* !BUILDING_NGHTTP2 */ # define NGHTTP2_EXTERN # endif /* !BUILDING_NGHTTP2 */ #endif /* !defined(WIN32) */ #ifdef BUILDING_NGHTTP2 # undef NGHTTP2_NO_SSIZE_T #endif /* BUILDING_NGHTTP2 */ /** * @typedef * * :type:`nghttp2_ssize` is a signed counterpart of size_t. */ typedef ptrdiff_t nghttp2_ssize; /** * @macro * * The protocol version identification string of this library * supports. This identifier is used if HTTP/2 is used over TLS. */ #define NGHTTP2_PROTO_VERSION_ID "h2" /** * @macro * * The length of :macro:`NGHTTP2_PROTO_VERSION_ID`. */ #define NGHTTP2_PROTO_VERSION_ID_LEN 2 /** * @macro * * The serialized form of ALPN protocol identifier this library * supports. Notice that first byte is the length of following * protocol identifier. This is the same wire format of `TLS ALPN * extension `_. This is useful * to process incoming ALPN tokens in wire format. */ #define NGHTTP2_PROTO_ALPN "\x2h2" /** * @macro * * The length of :macro:`NGHTTP2_PROTO_ALPN`. */ #define NGHTTP2_PROTO_ALPN_LEN (sizeof(NGHTTP2_PROTO_ALPN) - 1) /** * @macro * * The protocol version identification string of this library * supports. This identifier is used if HTTP/2 is used over cleartext * TCP. */ #define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "h2c" /** * @macro * * The length of :macro:`NGHTTP2_CLEARTEXT_PROTO_VERSION_ID`. */ #define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN 3 struct nghttp2_session; /** * @struct * * The primary structure to hold the resources needed for a HTTP/2 * session. The details of this structure are intentionally hidden * from the public API. */ typedef struct nghttp2_session nghttp2_session; /** * @macro * * The age of :type:`nghttp2_info` */ #define NGHTTP2_VERSION_AGE 1 /** * @struct * * This struct is what `nghttp2_version()` returns. It holds * information about the particular nghttp2 version. */ typedef struct { /** * Age of this struct. This instance of nghttp2 sets it to * :macro:`NGHTTP2_VERSION_AGE` but a future version may bump it and * add more struct fields at the bottom */ int age; /** * the :macro:`NGHTTP2_VERSION_NUM` number (since age ==1) */ int version_num; /** * points to the :macro:`NGHTTP2_VERSION` string (since age ==1) */ const char *version_str; /** * points to the :macro:`NGHTTP2_PROTO_VERSION_ID` string this * instance implements (since age ==1) */ const char *proto_str; /* -------- the above fields all exist when age == 1 */ } nghttp2_info; /** * @macro * * .. warning:: * * Deprecated. :rfc:`7540` priorities are deprecated by * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible * prioritization scheme. * * The default weight of stream dependency. */ #define NGHTTP2_DEFAULT_WEIGHT 16 /** * @macro * * .. warning:: * * Deprecated. :rfc:`7540` priorities are deprecated by * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible * prioritization scheme. * * The maximum weight of stream dependency. */ #define NGHTTP2_MAX_WEIGHT 256 /** * @macro * * .. warning:: * * Deprecated. :rfc:`7540` priorities are deprecated by * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible * prioritization scheme. * * The minimum weight of stream dependency. */ #define NGHTTP2_MIN_WEIGHT 1 /** * @macro * * The maximum window size */ #define NGHTTP2_MAX_WINDOW_SIZE ((int32_t)((1U << 31) - 1)) /** * @macro * * The initial window size for stream level flow control. */ #define NGHTTP2_INITIAL_WINDOW_SIZE ((1 << 16) - 1) /** * @macro * * The initial window size for connection level flow control. */ #define NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE ((1 << 16) - 1) /** * @macro * * The default header table size. */ #define NGHTTP2_DEFAULT_HEADER_TABLE_SIZE (1 << 12) /** * @macro * * The client magic string, which is the first 24 bytes byte string of * client connection preface. */ #define NGHTTP2_CLIENT_MAGIC "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" /** * @macro * * The length of :macro:`NGHTTP2_CLIENT_MAGIC`. */ #define NGHTTP2_CLIENT_MAGIC_LEN 24 /** * @macro * * The default max number of settings per SETTINGS frame */ #define NGHTTP2_DEFAULT_MAX_SETTINGS 32 /** * @enum * * Error codes used in this library. The code range is [-999, -500], * inclusive. The following values are defined: */ typedef enum { /** * Invalid argument passed. */ NGHTTP2_ERR_INVALID_ARGUMENT = -501, /** * Out of buffer space. */ NGHTTP2_ERR_BUFFER_ERROR = -502, /** * The specified protocol version is not supported. */ NGHTTP2_ERR_UNSUPPORTED_VERSION = -503, /** * Used as a return value from :type:`nghttp2_send_callback2`, * :type:`nghttp2_recv_callback` and * :type:`nghttp2_send_data_callback` to indicate that the operation * would block. */ NGHTTP2_ERR_WOULDBLOCK = -504, /** * General protocol error */ NGHTTP2_ERR_PROTO = -505, /** * The frame is invalid. */ NGHTTP2_ERR_INVALID_FRAME = -506, /** * The peer performed a shutdown on the connection. */ NGHTTP2_ERR_EOF = -507, /** * Used as a return value from * :func:`nghttp2_data_source_read_callback2` to indicate that data * transfer is postponed. See * :func:`nghttp2_data_source_read_callback2` for details. */ NGHTTP2_ERR_DEFERRED = -508, /** * Stream ID has reached the maximum value. Therefore no stream ID * is available. */ NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE = -509, /** * The stream is already closed; or the stream ID is invalid. */ NGHTTP2_ERR_STREAM_CLOSED = -510, /** * RST_STREAM has been added to the outbound queue. The stream is * in closing state. */ NGHTTP2_ERR_STREAM_CLOSING = -511, /** * The transmission is not allowed for this stream (e.g., a frame * with END_STREAM flag set has already sent). */ NGHTTP2_ERR_STREAM_SHUT_WR = -512, /** * The stream ID is invalid. */ NGHTTP2_ERR_INVALID_STREAM_ID = -513, /** * The state of the stream is not valid (e.g., DATA cannot be sent * to the stream if response HEADERS has not been sent). */ NGHTTP2_ERR_INVALID_STREAM_STATE = -514, /** * Another DATA frame has already been deferred. */ NGHTTP2_ERR_DEFERRED_DATA_EXIST = -515, /** * Starting new stream is not allowed (e.g., GOAWAY has been sent * and/or received). */ NGHTTP2_ERR_START_STREAM_NOT_ALLOWED = -516, /** * GOAWAY has already been sent. */ NGHTTP2_ERR_GOAWAY_ALREADY_SENT = -517, /** * The received frame contains the invalid header block (e.g., There * are duplicate header names; or the header names are not encoded * in US-ASCII character set and not lower cased; or the header name * is zero-length string; or the header value contains multiple * in-sequence NUL bytes). */ NGHTTP2_ERR_INVALID_HEADER_BLOCK = -518, /** * Indicates that the context is not suitable to perform the * requested operation. */ NGHTTP2_ERR_INVALID_STATE = -519, /** * The user callback function failed due to the temporal error. */ NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE = -521, /** * The length of the frame is invalid, either too large or too small. */ NGHTTP2_ERR_FRAME_SIZE_ERROR = -522, /** * Header block inflate/deflate error. */ NGHTTP2_ERR_HEADER_COMP = -523, /** * Flow control error */ NGHTTP2_ERR_FLOW_CONTROL = -524, /** * Insufficient buffer size given to function. */ NGHTTP2_ERR_INSUFF_BUFSIZE = -525, /** * Callback was paused by the application */ NGHTTP2_ERR_PAUSE = -526, /** * There are too many in-flight SETTING frame and no more * transmission of SETTINGS is allowed. */ NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS = -527, /** * The server push is disabled. */ NGHTTP2_ERR_PUSH_DISABLED = -528, /** * DATA or HEADERS frame for a given stream has been already * submitted and has not been fully processed yet. Application * should wait for the transmission of the previously submitted * frame before submitting another. */ NGHTTP2_ERR_DATA_EXIST = -529, /** * The current session is closing due to a connection error or * `nghttp2_session_terminate_session()` is called. */ NGHTTP2_ERR_SESSION_CLOSING = -530, /** * Invalid HTTP header field was received and stream is going to be * closed. */ NGHTTP2_ERR_HTTP_HEADER = -531, /** * Violation in HTTP messaging rule. */ NGHTTP2_ERR_HTTP_MESSAGING = -532, /** * Stream was refused. */ NGHTTP2_ERR_REFUSED_STREAM = -533, /** * Unexpected internal error, but recovered. */ NGHTTP2_ERR_INTERNAL = -534, /** * Indicates that a processing was canceled. */ NGHTTP2_ERR_CANCEL = -535, /** * When a local endpoint expects to receive SETTINGS frame, it * receives an other type of frame. */ NGHTTP2_ERR_SETTINGS_EXPECTED = -536, /** * When a local endpoint receives too many settings entries * in a single SETTINGS frame. */ NGHTTP2_ERR_TOO_MANY_SETTINGS = -537, /** * The errors < :enum:`nghttp2_error.NGHTTP2_ERR_FATAL` mean that * the library is under unexpected condition and processing was * terminated (e.g., out of memory). If application receives this * error code, it must stop using that :type:`nghttp2_session` * object and only allowed operation for that object is deallocate * it using `nghttp2_session_del()`. */ NGHTTP2_ERR_FATAL = -900, /** * Out of memory. This is a fatal error. */ NGHTTP2_ERR_NOMEM = -901, /** * The user callback function failed. This is a fatal error. */ NGHTTP2_ERR_CALLBACK_FAILURE = -902, /** * Invalid client magic (see :macro:`NGHTTP2_CLIENT_MAGIC`) was * received and further processing is not possible. */ NGHTTP2_ERR_BAD_CLIENT_MAGIC = -903, /** * Possible flooding by peer was detected in this HTTP/2 session. * Flooding is measured by how many PING and SETTINGS frames with * ACK flag set are queued for transmission. These frames are * response for the peer initiated frames, and peer can cause memory * exhaustion on server side to send these frames forever and does * not read network. */ NGHTTP2_ERR_FLOODED = -904, /** * When a local endpoint receives too many CONTINUATION frames * following a HEADER frame. */ NGHTTP2_ERR_TOO_MANY_CONTINUATIONS = -905, } nghttp2_error; /** * @struct * * The object representing single contiguous buffer. */ typedef struct { /** * The pointer to the buffer. */ uint8_t *base; /** * The length of the buffer. */ size_t len; } nghttp2_vec; struct nghttp2_rcbuf; /** * @struct * * The object representing reference counted buffer. The details of * this structure are intentionally hidden from the public API. */ typedef struct nghttp2_rcbuf nghttp2_rcbuf; /** * @function * * Increments the reference count of |rcbuf| by 1. */ NGHTTP2_EXTERN void nghttp2_rcbuf_incref(nghttp2_rcbuf *rcbuf); /** * @function * * Decrements the reference count of |rcbuf| by 1. If the reference * count becomes zero, the object pointed by |rcbuf| will be freed. * In this case, application must not use |rcbuf| again. */ NGHTTP2_EXTERN void nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf); /** * @function * * Returns the underlying buffer managed by |rcbuf|. */ NGHTTP2_EXTERN nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf); /** * @function * * Returns nonzero if the underlying buffer is statically allocated, * and 0 otherwise. This can be useful for language bindings that wish * to avoid creating duplicate strings for these buffers. */ NGHTTP2_EXTERN int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf); /** * @enum * * The flags for header field name/value pair. */ typedef enum { /** * No flag set. */ NGHTTP2_NV_FLAG_NONE = 0, /** * Indicates that this name/value pair must not be indexed ("Literal * Header Field never Indexed" representation must be used in HPACK * encoding). Other implementation calls this bit as "sensitive". */ NGHTTP2_NV_FLAG_NO_INDEX = 0x01, /** * This flag is set solely by application. If this flag is set, the * library does not make a copy of header field name. This could * improve performance. */ NGHTTP2_NV_FLAG_NO_COPY_NAME = 0x02, /** * This flag is set solely by application. If this flag is set, the * library does not make a copy of header field value. This could * improve performance. */ NGHTTP2_NV_FLAG_NO_COPY_VALUE = 0x04 } nghttp2_nv_flag; /** * @struct * * The name/value pair, which mainly used to represent header fields. */ typedef struct { /** * The |name| byte string. If this struct is presented from library * (e.g., :type:`nghttp2_on_frame_recv_callback`), |name| is * guaranteed to be NULL-terminated. For some callbacks * (:type:`nghttp2_before_frame_send_callback`, * :type:`nghttp2_on_frame_send_callback`, and * :type:`nghttp2_on_frame_not_send_callback`), it may not be * NULL-terminated if header field is passed from application with * the flag :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`). * When application is constructing this struct, |name| is not * required to be NULL-terminated. */ uint8_t *name; /** * The |value| byte string. If this struct is presented from * library (e.g., :type:`nghttp2_on_frame_recv_callback`), |value| * is guaranteed to be NULL-terminated. For some callbacks * (:type:`nghttp2_before_frame_send_callback`, * :type:`nghttp2_on_frame_send_callback`, and * :type:`nghttp2_on_frame_not_send_callback`), it may not be * NULL-terminated if header field is passed from application with * the flag :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE`). * When application is constructing this struct, |value| is not * required to be NULL-terminated. */ uint8_t *value; /** * The length of the |name|, excluding terminating NULL. */ size_t namelen; /** * The length of the |value|, excluding terminating NULL. */ size_t valuelen; /** * Bitwise OR of one or more of :type:`nghttp2_nv_flag`. */ uint8_t flags; } nghttp2_nv; /** * @enum * * The frame types in HTTP/2 specification. */ typedef enum { /** * The DATA frame. */ NGHTTP2_DATA = 0, /** * The HEADERS frame. */ NGHTTP2_HEADERS = 0x01, /** * The PRIORITY frame. */ NGHTTP2_PRIORITY = 0x02, /** * The RST_STREAM frame. */ NGHTTP2_RST_STREAM = 0x03, /** * The SETTINGS frame. */ NGHTTP2_SETTINGS = 0x04, /** * The PUSH_PROMISE frame. */ NGHTTP2_PUSH_PROMISE = 0x05, /** * The PING frame. */ NGHTTP2_PING = 0x06, /** * The GOAWAY frame. */ NGHTTP2_GOAWAY = 0x07, /** * The WINDOW_UPDATE frame. */ NGHTTP2_WINDOW_UPDATE = 0x08, /** * The CONTINUATION frame. This frame type won't be passed to any * callbacks because the library processes this frame type and its * preceding HEADERS/PUSH_PROMISE as a single frame. */ NGHTTP2_CONTINUATION = 0x09, /** * The ALTSVC frame, which is defined in `RFC 7383 * `_. */ NGHTTP2_ALTSVC = 0x0a, /** * The ORIGIN frame, which is defined by `RFC 8336 * `_. */ NGHTTP2_ORIGIN = 0x0c, /** * The PRIORITY_UPDATE frame, which is defined by :rfc:`9218`. */ NGHTTP2_PRIORITY_UPDATE = 0x10 } nghttp2_frame_type; /** * @enum * * The flags for HTTP/2 frames. This enum defines all flags for all * frames. */ typedef enum { /** * No flag set. */ NGHTTP2_FLAG_NONE = 0, /** * The END_STREAM flag. */ NGHTTP2_FLAG_END_STREAM = 0x01, /** * The END_HEADERS flag. */ NGHTTP2_FLAG_END_HEADERS = 0x04, /** * The ACK flag. */ NGHTTP2_FLAG_ACK = 0x01, /** * The PADDED flag. */ NGHTTP2_FLAG_PADDED = 0x08, /** * The PRIORITY flag. */ NGHTTP2_FLAG_PRIORITY = 0x20 } nghttp2_flag; /** * @enum * The SETTINGS ID. */ typedef enum { /** * SETTINGS_HEADER_TABLE_SIZE */ NGHTTP2_SETTINGS_HEADER_TABLE_SIZE = 0x01, /** * SETTINGS_ENABLE_PUSH */ NGHTTP2_SETTINGS_ENABLE_PUSH = 0x02, /** * SETTINGS_MAX_CONCURRENT_STREAMS */ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 0x03, /** * SETTINGS_INITIAL_WINDOW_SIZE */ NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 0x04, /** * SETTINGS_MAX_FRAME_SIZE */ NGHTTP2_SETTINGS_MAX_FRAME_SIZE = 0x05, /** * SETTINGS_MAX_HEADER_LIST_SIZE */ NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06, /** * SETTINGS_ENABLE_CONNECT_PROTOCOL * (`RFC 8441 `_) */ NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08, /** * SETTINGS_NO_RFC7540_PRIORITIES (:rfc:`9218`) */ NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES = 0x09 } nghttp2_settings_id; /* Note: If we add SETTINGS, update the capacity of NGHTTP2_INBOUND_NUM_IV as well */ /** * @macro * * .. warning:: * * Deprecated. The initial max concurrent streams is 0xffffffffu. * * Default maximum number of incoming concurrent streams. Use * `nghttp2_submit_settings()` with * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS` * to change the maximum number of incoming concurrent streams. * * .. note:: * * The maximum number of outgoing concurrent streams is 100 by * default. */ #define NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS ((1U << 31) - 1) /** * @enum * The status codes for the RST_STREAM and GOAWAY frames. */ typedef enum { /** * No errors. */ NGHTTP2_NO_ERROR = 0x00, /** * PROTOCOL_ERROR */ NGHTTP2_PROTOCOL_ERROR = 0x01, /** * INTERNAL_ERROR */ NGHTTP2_INTERNAL_ERROR = 0x02, /** * FLOW_CONTROL_ERROR */ NGHTTP2_FLOW_CONTROL_ERROR = 0x03, /** * SETTINGS_TIMEOUT */ NGHTTP2_SETTINGS_TIMEOUT = 0x04, /** * STREAM_CLOSED */ NGHTTP2_STREAM_CLOSED = 0x05, /** * FRAME_SIZE_ERROR */ NGHTTP2_FRAME_SIZE_ERROR = 0x06, /** * REFUSED_STREAM */ NGHTTP2_REFUSED_STREAM = 0x07, /** * CANCEL */ NGHTTP2_CANCEL = 0x08, /** * COMPRESSION_ERROR */ NGHTTP2_COMPRESSION_ERROR = 0x09, /** * CONNECT_ERROR */ NGHTTP2_CONNECT_ERROR = 0x0a, /** * ENHANCE_YOUR_CALM */ NGHTTP2_ENHANCE_YOUR_CALM = 0x0b, /** * INADEQUATE_SECURITY */ NGHTTP2_INADEQUATE_SECURITY = 0x0c, /** * HTTP_1_1_REQUIRED */ NGHTTP2_HTTP_1_1_REQUIRED = 0x0d } nghttp2_error_code; /** * @struct * The frame header. */ typedef struct { /** * The length field of this frame, excluding frame header. */ size_t length; /** * The stream identifier (aka, stream ID) */ int32_t stream_id; /** * The type of this frame. See `nghttp2_frame_type`. */ uint8_t type; /** * The flags. */ uint8_t flags; /** * Reserved bit in frame header. Currently, this is always set to 0 * and application should not expect something useful in here. */ uint8_t reserved; } nghttp2_frame_hd; /** * @union * * This union represents the some kind of data source passed to * :type:`nghttp2_data_source_read_callback2`. */ typedef union { /** * The integer field, suitable for a file descriptor. */ int fd; /** * The pointer to an arbitrary object. */ void *ptr; } nghttp2_data_source; /** * @enum * * The flags used to set in |data_flags| output parameter in * :type:`nghttp2_data_source_read_callback2`. */ typedef enum { /** * No flag set. */ NGHTTP2_DATA_FLAG_NONE = 0, /** * Indicates EOF was sensed. */ NGHTTP2_DATA_FLAG_EOF = 0x01, /** * Indicates that END_STREAM flag must not be set even if * NGHTTP2_DATA_FLAG_EOF is set. Usually this flag is used to send * trailer fields with `nghttp2_submit_request2()` or * `nghttp2_submit_response2()`. */ NGHTTP2_DATA_FLAG_NO_END_STREAM = 0x02, /** * Indicates that application will send complete DATA frame in * :type:`nghttp2_send_data_callback`. */ NGHTTP2_DATA_FLAG_NO_COPY = 0x04 } nghttp2_data_flag; #ifndef NGHTTP2_NO_SSIZE_T /** * @functypedef * * .. warning:: * * Deprecated. Use :type:`nghttp2_data_source_read_callback2` * instead. * * Callback function invoked when the library wants to read data from * the |source|. The read data is sent in the stream |stream_id|. * The implementation of this function must read at most |length| * bytes of data from |source| (or possibly other places) and store * them in |buf| and return number of data stored in |buf|. If EOF is * reached, set :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag * in |*data_flags|. * * Sometime it is desirable to avoid copying data into |buf| and let * application to send data directly. To achieve this, set * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` to * |*data_flags| (and possibly other flags, just like when we do * copy), and return the number of bytes to send without copying data * into |buf|. The library, seeing * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY`, will invoke * :type:`nghttp2_send_data_callback`. The application must send * complete DATA frame in that callback. * * If this callback is set by `nghttp2_submit_request()`, * `nghttp2_submit_response()` or `nghttp2_submit_headers()` and * `nghttp2_submit_data()` with flag parameter * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` set, and * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag is set to * |*data_flags|, DATA frame will have END_STREAM flag set. Usually, * this is expected behaviour and all are fine. One exception is send * trailer fields. You cannot send trailer fields after sending frame * with END_STREAM set. To avoid this problem, one can set * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_END_STREAM` along * with :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` to signal the * library not to set END_STREAM in DATA frame. Then application can * use `nghttp2_submit_trailer()` to send trailer fields. * `nghttp2_submit_trailer()` can be called inside this callback. * * If the application wants to postpone DATA frames (e.g., * asynchronous I/O, or reading data blocks for long time), it is * achieved by returning :enum:`nghttp2_error.NGHTTP2_ERR_DEFERRED` * without reading any data in this invocation. The library removes * DATA frame from the outgoing queue temporarily. To move back * deferred DATA frame to outgoing queue, call * `nghttp2_session_resume_data()`. * * By default, |length| is limited to 16KiB at maximum. If peer * allows larger frames, application can enlarge transmission buffer * size. See :type:`nghttp2_data_source_read_length_callback` for * more details. * * If the application just wants to return from * `nghttp2_session_send()` or `nghttp2_session_mem_send()` without * sending anything, return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE`. * * In case of error, there are 2 choices. Returning * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will * close the stream by issuing RST_STREAM with * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. If a different * error code is desirable, use `nghttp2_submit_rst_stream()` with a * desired error code and then return * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. * Returning :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will * signal the entire session failure. */ typedef ssize_t (*nghttp2_data_source_read_callback)( nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *user_data); #endif /* NGHTTP2_NO_SSIZE_T */ /** * @functypedef * * Callback function invoked when the library wants to read data from * the |source|. The read data is sent in the stream |stream_id|. * The implementation of this function must read at most |length| * bytes of data from |source| (or possibly other places) and store * them in |buf| and return number of data stored in |buf|. If EOF is * reached, set :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag * in |*data_flags|. * * Sometime it is desirable to avoid copying data into |buf| and let * application to send data directly. To achieve this, set * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` to * |*data_flags| (and possibly other flags, just like when we do * copy), and return the number of bytes to send without copying data * into |buf|. The library, seeing * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY`, will invoke * :type:`nghttp2_send_data_callback`. The application must send * complete DATA frame in that callback. * * If this callback is set by `nghttp2_submit_request2()`, * `nghttp2_submit_response2()` or `nghttp2_submit_headers()` and * `nghttp2_submit_data2()` with flag parameter * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` set, and * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag is set to * |*data_flags|, DATA frame will have END_STREAM flag set. Usually, * this is expected behaviour and all are fine. One exception is send * trailer fields. You cannot send trailer fields after sending frame * with END_STREAM set. To avoid this problem, one can set * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_END_STREAM` along * with :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` to signal the * library not to set END_STREAM in DATA frame. Then application can * use `nghttp2_submit_trailer()` to send trailer fields. * `nghttp2_submit_trailer()` can be called inside this callback. * * If the application wants to postpone DATA frames (e.g., * asynchronous I/O, or reading data blocks for long time), it is * achieved by returning :enum:`nghttp2_error.NGHTTP2_ERR_DEFERRED` * without reading any data in this invocation. The library removes * DATA frame from the outgoing queue temporarily. To move back * deferred DATA frame to outgoing queue, call * `nghttp2_session_resume_data()`. * * By default, |length| is limited to 16KiB at maximum. If peer * allows larger frames, application can enlarge transmission buffer * size. See :type:`nghttp2_data_source_read_length_callback` for * more details. * * If the application just wants to return from * `nghttp2_session_send()` or `nghttp2_session_mem_send2()` without * sending anything, return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE`. * * In case of error, there are 2 choices. Returning * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will * close the stream by issuing RST_STREAM with * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. If a different * error code is desirable, use `nghttp2_submit_rst_stream()` with a * desired error code and then return * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. * Returning :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will * signal the entire session failure. */ typedef nghttp2_ssize (*nghttp2_data_source_read_callback2)( nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *user_data); #ifndef NGHTTP2_NO_SSIZE_T /** * @struct * * .. warning:: * * Deprecated. Use :type:`nghttp2_data_provider2` instead. * * This struct represents the data source and the way to read a chunk * of data from it. */ typedef struct { /** * The data source. */ nghttp2_data_source source; /** * The callback function to read a chunk of data from the |source|. */ nghttp2_data_source_read_callback read_callback; } nghttp2_data_provider; #endif /* NGHTTP2_NO_SSIZE_T */ /** * @struct * * This struct represents the data source and the way to read a chunk * of data from it. */ typedef struct { /** * The data source. */ nghttp2_data_source source; /** * The callback function to read a chunk of data from the |source|. */ nghttp2_data_source_read_callback2 read_callback; } nghttp2_data_provider2; /** * @struct * * The DATA frame. The received data is delivered via * :type:`nghttp2_on_data_chunk_recv_callback`. */ typedef struct { nghttp2_frame_hd hd; /** * The length of the padding in this frame. This includes PAD_HIGH * and PAD_LOW. */ size_t padlen; } nghttp2_data; /** * @enum * * The category of HEADERS, which indicates the role of the frame. In * HTTP/2 spec, request, response, push response and other arbitrary * headers (e.g., trailer fields) are all called just HEADERS. To * give the application the role of incoming HEADERS frame, we define * several categories. */ typedef enum { /** * The HEADERS frame is opening new stream, which is analogous to * SYN_STREAM in SPDY. */ NGHTTP2_HCAT_REQUEST = 0, /** * The HEADERS frame is the first response headers, which is * analogous to SYN_REPLY in SPDY. */ NGHTTP2_HCAT_RESPONSE = 1, /** * The HEADERS frame is the first headers sent against reserved * stream. */ NGHTTP2_HCAT_PUSH_RESPONSE = 2, /** * The HEADERS frame which does not apply for the above categories, * which is analogous to HEADERS in SPDY. If non-final response * (e.g., status 1xx) is used, final response HEADERS frame will be * categorized here. */ NGHTTP2_HCAT_HEADERS = 3 } nghttp2_headers_category; /** * @struct * * .. warning:: * * Deprecated. :rfc:`7540` priorities are deprecated by * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible * prioritization scheme. * * The structure to specify stream dependency. */ typedef struct { /** * The stream ID of the stream to depend on. Specifying 0 makes * stream not depend any other stream. */ int32_t stream_id; /** * The weight of this dependency. */ int32_t weight; /** * nonzero means exclusive dependency */ uint8_t exclusive; } nghttp2_priority_spec; /** * @struct * * The HEADERS frame. It has the following members: */ typedef struct { /** * The frame header. */ nghttp2_frame_hd hd; /** * The length of the padding in this frame. This includes PAD_HIGH * and PAD_LOW. */ size_t padlen; /** * .. warning:: * * Deprecated. :rfc:`7540` priorities are deprecated by * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible * prioritization scheme. * * The priority specification */ nghttp2_priority_spec pri_spec; /** * The name/value pairs. */ nghttp2_nv *nva; /** * The number of name/value pairs in |nva|. */ size_t nvlen; /** * The category of this HEADERS frame. */ nghttp2_headers_category cat; } nghttp2_headers; /** * @struct * * .. warning:: * * Deprecated. :rfc:`7540` priorities are deprecated by * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible * prioritization scheme. * * The PRIORITY frame. It has the following members: */ typedef struct { /** * The frame header. */ nghttp2_frame_hd hd; /** * The priority specification. */ nghttp2_priority_spec pri_spec; } nghttp2_priority; /** * @struct * * The RST_STREAM frame. It has the following members: */ typedef struct { /** * The frame header. */ nghttp2_frame_hd hd; /** * The error code. See :type:`nghttp2_error_code`. */ uint32_t error_code; } nghttp2_rst_stream; /** * @struct * * The SETTINGS ID/Value pair. It has the following members: */ typedef struct { /** * The SETTINGS ID. See :type:`nghttp2_settings_id`. */ int32_t settings_id; /** * The value of this entry. */ uint32_t value; } nghttp2_settings_entry; /** * @struct * * The SETTINGS frame. It has the following members: */ typedef struct { /** * The frame header. */ nghttp2_frame_hd hd; /** * The number of SETTINGS ID/Value pairs in |iv|. */ size_t niv; /** * The pointer to the array of SETTINGS ID/Value pair. */ nghttp2_settings_entry *iv; } nghttp2_settings; /** * @struct * * The PUSH_PROMISE frame. It has the following members: */ typedef struct { /** * The frame header. */ nghttp2_frame_hd hd; /** * The length of the padding in this frame. This includes PAD_HIGH * and PAD_LOW. */ size_t padlen; /** * The name/value pairs. */ nghttp2_nv *nva; /** * The number of name/value pairs in |nva|. */ size_t nvlen; /** * The promised stream ID */ int32_t promised_stream_id; /** * Reserved bit. Currently this is always set to 0 and application * should not expect something useful in here. */ uint8_t reserved; } nghttp2_push_promise; /** * @struct * * The PING frame. It has the following members: */ typedef struct { /** * The frame header. */ nghttp2_frame_hd hd; /** * The opaque data */ uint8_t opaque_data[8]; } nghttp2_ping; /** * @struct * * The GOAWAY frame. It has the following members: */ typedef struct { /** * The frame header. */ nghttp2_frame_hd hd; /** * The last stream stream ID. */ int32_t last_stream_id; /** * The error code. See :type:`nghttp2_error_code`. */ uint32_t error_code; /** * The additional debug data */ uint8_t *opaque_data; /** * The length of |opaque_data| member. */ size_t opaque_data_len; /** * Reserved bit. Currently this is always set to 0 and application * should not expect something useful in here. */ uint8_t reserved; } nghttp2_goaway; /** * @struct * * The WINDOW_UPDATE frame. It has the following members: */ typedef struct { /** * The frame header. */ nghttp2_frame_hd hd; /** * The window size increment. */ int32_t window_size_increment; /** * Reserved bit. Currently this is always set to 0 and application * should not expect something useful in here. */ uint8_t reserved; } nghttp2_window_update; /** * @struct * * The extension frame. It has following members: */ typedef struct { /** * The frame header. */ nghttp2_frame_hd hd; /** * The pointer to extension payload. The exact pointer type is * determined by hd.type. * * Currently, no extension is supported. This is a place holder for * the future extensions. */ void *payload; } nghttp2_extension; /** * @union * * This union includes all frames to pass them to various function * calls as nghttp2_frame type. The CONTINUATION frame is omitted * from here because the library deals with it internally. */ typedef union { /** * The frame header, which is convenient to inspect frame header. */ nghttp2_frame_hd hd; /** * The DATA frame. */ nghttp2_data data; /** * The HEADERS frame. */ nghttp2_headers headers; /** * The PRIORITY frame. */ nghttp2_priority priority; /** * The RST_STREAM frame. */ nghttp2_rst_stream rst_stream; /** * The SETTINGS frame. */ nghttp2_settings settings; /** * The PUSH_PROMISE frame. */ nghttp2_push_promise push_promise; /** * The PING frame. */ nghttp2_ping ping; /** * The GOAWAY frame. */ nghttp2_goaway goaway; /** * The WINDOW_UPDATE frame. */ nghttp2_window_update window_update; /** * The extension frame. */ nghttp2_extension ext; } nghttp2_frame; #ifndef NGHTTP2_NO_SSIZE_T /** * @functypedef * * .. warning:: * * Deprecated. Use :type:`nghttp2_send_callback2` instead. * * Callback function invoked when |session| wants to send data to the * remote peer. The implementation of this function must send at most * |length| bytes of data stored in |data|. The |flags| is currently * not used and always 0. It must return the number of bytes sent if * it succeeds. If it cannot send any single byte without blocking, * it must return :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. For * other errors, it must return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. The * |user_data| pointer is the third argument passed in to the call to * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. * * This callback is required if the application uses * `nghttp2_session_send()` to send data to the remote endpoint. If * the application uses solely `nghttp2_session_mem_send()` instead, * this callback function is unnecessary. * * To set this callback to :type:`nghttp2_session_callbacks`, use * `nghttp2_session_callbacks_set_send_callback()`. * * .. note:: * * The |length| may be very small. If that is the case, and * application disables Nagle algorithm (``TCP_NODELAY``), then just * writing |data| to the network stack leads to very small packet, * and it is very inefficient. An application should be responsible * to buffer up small chunks of data as necessary to avoid this * situation. */ typedef ssize_t (*nghttp2_send_callback)(nghttp2_session *session, const uint8_t *data, size_t length, int flags, void *user_data); #endif /* NGHTTP2_NO_SSIZE_T */ /** * @functypedef * * Callback function invoked when |session| wants to send data to the * remote peer. The implementation of this function must send at most * |length| bytes of data stored in |data|. The |flags| is currently * not used and always 0. It must return the number of bytes sent if * it succeeds. If it cannot send any single byte without blocking, * it must return :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. For * other errors, it must return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. The * |user_data| pointer is the third argument passed in to the call to * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. * * This callback is required if the application uses * `nghttp2_session_send()` to send data to the remote endpoint. If * the application uses solely `nghttp2_session_mem_send2()` instead, * this callback function is unnecessary. * * To set this callback to :type:`nghttp2_session_callbacks`, use * `nghttp2_session_callbacks_set_send_callback2()`. * * .. note:: * * The |length| may be very small. If that is the case, and * application disables Nagle algorithm (``TCP_NODELAY``), then just * writing |data| to the network stack leads to very small packet, * and it is very inefficient. An application should be responsible * to buffer up small chunks of data as necessary to avoid this * situation. */ typedef nghttp2_ssize (*nghttp2_send_callback2)(nghttp2_session *session, const uint8_t *data, size_t length, int flags, void *user_data); /** * @functypedef * * Callback function invoked when * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` is used in * :type:`nghttp2_data_source_read_callback` to send complete DATA * frame. * * The |frame| is a DATA frame to send. The |framehd| is the * serialized frame header (9 bytes). The |length| is the length of * application data to send (this does not include padding). The * |source| is the same pointer passed to * :type:`nghttp2_data_source_read_callback`. * * The application first must send frame header |framehd| of length 9 * bytes. If ``frame->data.padlen > 0``, send 1 byte of value * ``frame->data.padlen - 1``. Then send exactly |length| bytes of * application data. Finally, if ``frame->data.padlen > 1``, send * ``frame->data.padlen - 1`` bytes of zero as padding. * * The application has to send complete DATA frame in this callback. * If all data were written successfully, return 0. * * If it cannot send any data at all, just return * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`; the library will call * this callback with the same parameters later (It is recommended to * send complete DATA frame at once in this function to deal with * error; if partial frame data has already sent, it is impossible to * send another data in that state, and all we can do is tear down * connection). When data is fully processed, but application wants * to make `nghttp2_session_mem_send2()` or `nghttp2_session_send()` * return immediately without processing next frames, return * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE`. If application decided to * reset this stream, return * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`, then * the library will send RST_STREAM with INTERNAL_ERROR as error code. * The application can also return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, which will * result in connection closure. Returning any other value is treated * as :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned. */ typedef int (*nghttp2_send_data_callback)(nghttp2_session *session, nghttp2_frame *frame, const uint8_t *framehd, size_t length, nghttp2_data_source *source, void *user_data); #ifndef NGHTTP2_NO_SSIZE_T /** * @functypedef * * .. warning:: * * Deprecated. Use :type:`nghttp2_recv_callback2` instead. * * Callback function invoked when |session| wants to receive data from * the remote peer. The implementation of this function must read at * most |length| bytes of data and store it in |buf|. The |flags| is * currently not used and always 0. It must return the number of * bytes written in |buf| if it succeeds. If it cannot read any * single byte without blocking, it must return * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. If it gets EOF * before it reads any single byte, it must return * :enum:`nghttp2_error.NGHTTP2_ERR_EOF`. For other errors, it must * return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * Returning 0 is treated as * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. The |user_data| * pointer is the third argument passed in to the call to * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. * * This callback is required if the application uses * `nghttp2_session_recv()` to receive data from the remote endpoint. * If the application uses solely `nghttp2_session_mem_recv()` * instead, this callback function is unnecessary. * * To set this callback to :type:`nghttp2_session_callbacks`, use * `nghttp2_session_callbacks_set_recv_callback()`. */ typedef ssize_t (*nghttp2_recv_callback)(nghttp2_session *session, uint8_t *buf, size_t length, int flags, void *user_data); #endif /* NGHTTP2_NO_SSIZE_T */ /** * @functypedef * * Callback function invoked when |session| wants to receive data from * the remote peer. The implementation of this function must read at * most |length| bytes of data and store it in |buf|. The |flags| is * currently not used and always 0. It must return the number of * bytes written in |buf| if it succeeds. If it cannot read any * single byte without blocking, it must return * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. If it gets EOF * before it reads any single byte, it must return * :enum:`nghttp2_error.NGHTTP2_ERR_EOF`. For other errors, it must * return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * Returning 0 is treated as * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. The |user_data| * pointer is the third argument passed in to the call to * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. * * This callback is required if the application uses * `nghttp2_session_recv()` to receive data from the remote endpoint. * If the application uses solely `nghttp2_session_mem_recv2()` * instead, this callback function is unnecessary. * * To set this callback to :type:`nghttp2_session_callbacks`, use * `nghttp2_session_callbacks_set_recv_callback2()`. */ typedef nghttp2_ssize (*nghttp2_recv_callback2)(nghttp2_session *session, uint8_t *buf, size_t length, int flags, void *user_data); /** * @functypedef * * Callback function invoked by `nghttp2_session_recv()` and * `nghttp2_session_mem_recv2()` when a frame is received. The * |user_data| pointer is the third argument passed in to the call to * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. * * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` * member of their data structure are always ``NULL`` and 0 * respectively. The header name/value pairs are emitted via * :type:`nghttp2_on_header_callback`. * * Only HEADERS and DATA frame can signal the end of incoming data. * If ``frame->hd.flags & NGHTTP2_FLAG_END_STREAM`` is nonzero, the * |frame| is the last frame from the remote peer in this stream. * * This callback won't be called for CONTINUATION frames. * HEADERS/PUSH_PROMISE + CONTINUATIONs are treated as single frame. * * The implementation of this function must return 0 if it succeeds. * If nonzero value is returned, it is treated as fatal error and * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` * functions immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * * To set this callback to :type:`nghttp2_session_callbacks`, use * `nghttp2_session_callbacks_set_on_frame_recv_callback()`. */ typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session, const nghttp2_frame *frame, void *user_data); /** * @functypedef * * Callback function invoked by `nghttp2_session_recv()` and * `nghttp2_session_mem_recv2()` when an invalid non-DATA frame is * received. The error is indicated by the |lib_error_code|, which is * one of the values defined in :type:`nghttp2_error`. When this * callback function is invoked, the library automatically submits * either RST_STREAM or GOAWAY frame. The |user_data| pointer is the * third argument passed in to the call to * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. * * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` * member of their data structure are always ``NULL`` and 0 * respectively. * * The implementation of this function must return 0 if it succeeds. * If nonzero is returned, it is treated as fatal error and * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` * functions immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * * To set this callback to :type:`nghttp2_session_callbacks`, use * `nghttp2_session_callbacks_set_on_invalid_frame_recv_callback()`. */ typedef int (*nghttp2_on_invalid_frame_recv_callback)( nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, void *user_data); /** * @functypedef * * Callback function invoked when a chunk of data in DATA frame is * received. The |stream_id| is the stream ID this DATA frame belongs * to. The |flags| is the flags of DATA frame which this data chunk * is contained. ``(flags & NGHTTP2_FLAG_END_STREAM) != 0`` does not * necessarily mean this chunk of data is the last one in the stream. * You should use :type:`nghttp2_on_frame_recv_callback` to know all * data frames are received. The |user_data| pointer is the third * argument passed in to the call to `nghttp2_session_client_new()` or * `nghttp2_session_server_new()`. * * If the application uses `nghttp2_session_mem_recv2()`, it can * return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` to make * `nghttp2_session_mem_recv2()` return without processing further * input bytes. The memory by pointed by the |data| is retained until * `nghttp2_session_mem_recv2()` or `nghttp2_session_recv()` is * called. The application must retain the input bytes which was used * to produce the |data| parameter, because it may refer to the memory * region included in the input bytes. * * The implementation of this function must return 0 if it succeeds. * If nonzero is returned, it is treated as fatal error, and * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` * functions immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * * To set this callback to :type:`nghttp2_session_callbacks`, use * `nghttp2_session_callbacks_set_on_data_chunk_recv_callback()`. */ typedef int (*nghttp2_on_data_chunk_recv_callback)(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data); /** * @functypedef * * Callback function invoked just before the non-DATA frame |frame| is * sent. The |user_data| pointer is the third argument passed in to * the call to `nghttp2_session_client_new()` or * `nghttp2_session_server_new()`. * * The implementation of this function must return 0 if it succeeds. * It can also return :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL` to * cancel the transmission of the given frame. * * If there is a fatal error while executing this callback, the * implementation should return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, which makes * `nghttp2_session_send()` and `nghttp2_session_mem_send2()` * functions immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * * If the other value is returned, it is treated as if * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned. * But the implementation should not rely on this since the library * may define new return value to extend its capability. * * To set this callback to :type:`nghttp2_session_callbacks`, use * `nghttp2_session_callbacks_set_before_frame_send_callback()`. */ typedef int (*nghttp2_before_frame_send_callback)(nghttp2_session *session, const nghttp2_frame *frame, void *user_data); /** * @functypedef * * Callback function invoked after the frame |frame| is sent. The * |user_data| pointer is the third argument passed in to the call to * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. * * The implementation of this function must return 0 if it succeeds. * If nonzero is returned, it is treated as fatal error and * `nghttp2_session_send()` and `nghttp2_session_mem_send2()` * functions immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * * To set this callback to :type:`nghttp2_session_callbacks`, use * `nghttp2_session_callbacks_set_on_frame_send_callback()`. */ typedef int (*nghttp2_on_frame_send_callback)(nghttp2_session *session, const nghttp2_frame *frame, void *user_data); /** * @functypedef * * Callback function invoked after the non-DATA frame |frame| is not * sent because of the error. The error is indicated by the * |lib_error_code|, which is one of the values defined in * :type:`nghttp2_error`. The |user_data| pointer is the third * argument passed in to the call to `nghttp2_session_client_new()` or * `nghttp2_session_server_new()`. * * The implementation of this function must return 0 if it succeeds. * If nonzero is returned, it is treated as fatal error and * `nghttp2_session_send()` and `nghttp2_session_mem_send2()` * functions immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * * `nghttp2_session_get_stream_user_data()` can be used to get * associated data. * * To set this callback to :type:`nghttp2_session_callbacks`, use * `nghttp2_session_callbacks_set_on_frame_not_send_callback()`. */ typedef int (*nghttp2_on_frame_not_send_callback)(nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, void *user_data); /** * @functypedef * * Callback function invoked when the stream |stream_id| is closed. * The reason of closure is indicated by the |error_code|. The * |error_code| is usually one of :enum:`nghttp2_error_code`, but that * is not guaranteed. The stream_user_data, which was specified in * `nghttp2_submit_request2()` or `nghttp2_submit_headers()`, is still * available in this function. The |user_data| pointer is the third * argument passed in to the call to `nghttp2_session_client_new()` or * `nghttp2_session_server_new()`. * * This function is also called for a stream in reserved state. * * The implementation of this function must return 0 if it succeeds. * If nonzero is returned, it is treated as fatal error and * `nghttp2_session_recv()`, `nghttp2_session_mem_recv2()`, * `nghttp2_session_send()`, and `nghttp2_session_mem_send2()` * functions immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * * To set this callback to :type:`nghttp2_session_callbacks`, use * `nghttp2_session_callbacks_set_on_stream_close_callback()`. */ typedef int (*nghttp2_on_stream_close_callback)(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *user_data); /** * @functypedef * * Callback function invoked when the reception of header block in * HEADERS or PUSH_PROMISE is started. Each header name/value pair * will be emitted by :type:`nghttp2_on_header_callback`. * * The ``frame->hd.flags`` may not have * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_HEADERS` flag set, which * indicates that one or more CONTINUATION frames are involved. But * the application does not need to care about that because the header * name/value pairs are emitted transparently regardless of * CONTINUATION frames. * * The server applications probably create an object to store * information about new stream if ``frame->hd.type == * NGHTTP2_HEADERS`` and ``frame->headers.cat == * NGHTTP2_HCAT_REQUEST``. If |session| is configured as server side, * ``frame->headers.cat`` is either ``NGHTTP2_HCAT_REQUEST`` * containing request headers or ``NGHTTP2_HCAT_HEADERS`` containing * trailer fields and never get PUSH_PROMISE in this callback. * * For the client applications, ``frame->hd.type`` is either * ``NGHTTP2_HEADERS`` or ``NGHTTP2_PUSH_PROMISE``. In case of * ``NGHTTP2_HEADERS``, ``frame->headers.cat == * NGHTTP2_HCAT_RESPONSE`` means that it is the first response * headers, but it may be non-final response which is indicated by 1xx * status code. In this case, there may be zero or more HEADERS frame * with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` which has * non-final response code and finally client gets exactly one HEADERS * frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` * containing final response headers (non-1xx status code). The * trailer fields also has ``frame->headers.cat == * NGHTTP2_HCAT_HEADERS`` which does not contain any status code. * * Returning * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will * close the stream (promised stream if frame is PUSH_PROMISE) by * issuing RST_STREAM with * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. In this case, * :type:`nghttp2_on_header_callback` and * :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a * different error code is desirable, use * `nghttp2_submit_rst_stream()` with a desired error code and then * return :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. * Again, use ``frame->push_promise.promised_stream_id`` as stream_id * parameter in `nghttp2_submit_rst_stream()` if frame is * PUSH_PROMISE. * * The implementation of this function must return 0 if it succeeds. * It can return * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` to * reset the stream (promised stream if frame is PUSH_PROMISE). For * critical errors, it must return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other * value is returned, it is treated as if * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned. If * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned, * `nghttp2_session_mem_recv2()` function will immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * * To set this callback to :type:`nghttp2_session_callbacks`, use * `nghttp2_session_callbacks_set_on_begin_headers_callback()`. */ typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session, const nghttp2_frame *frame, void *user_data); /** * @functypedef * * Callback function invoked when a header name/value pair is received * for the |frame|. The |name| of length |namelen| is header name. * The |value| of length |valuelen| is header value. The |flags| is * bitwise OR of one or more of :type:`nghttp2_nv_flag`. * * If :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_INDEX` is set in * |flags|, the receiver must not index this name/value pair when * forwarding it to the next hop. More specifically, "Literal Header * Field never Indexed" representation must be used in HPACK encoding. * * When this callback is invoked, ``frame->hd.type`` is either * :enum:`nghttp2_frame_type.NGHTTP2_HEADERS` or * :enum:`nghttp2_frame_type.NGHTTP2_PUSH_PROMISE`. After all header * name/value pairs are processed with this callback, and no error has * been detected, :type:`nghttp2_on_frame_recv_callback` will be * invoked. If there is an error in decompression, * :type:`nghttp2_on_frame_recv_callback` for the |frame| will not be * invoked. * * Both |name| and |value| are guaranteed to be NULL-terminated. The * |namelen| and |valuelen| do not include terminal NULL. If * `nghttp2_option_set_no_http_messaging()` is used with nonzero * value, NULL character may be included in |name| or |value| before * terminating NULL. * * Please note that unless `nghttp2_option_set_no_http_messaging()` is * used, nghttp2 library does perform validation against the |name| * and the |value| using `nghttp2_check_header_name()` and * `nghttp2_check_header_value()`. In addition to this, nghttp2 * performs validation based on HTTP Messaging rule, which is briefly * explained in :ref:`http-messaging` section. * * If the application uses `nghttp2_session_mem_recv2()`, it can * return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` to make * `nghttp2_session_mem_recv2()` return without processing further * input bytes. The memory pointed by |frame|, |name| and |value| * parameters are retained until `nghttp2_session_mem_recv2()` or * `nghttp2_session_recv()` is called. The application must retain * the input bytes which was used to produce these parameters, because * it may refer to the memory region included in the input bytes. * * Returning * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will * close the stream (promised stream if frame is PUSH_PROMISE) by * issuing RST_STREAM with * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. In this case, * :type:`nghttp2_on_header_callback` and * :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a * different error code is desirable, use * `nghttp2_submit_rst_stream()` with a desired error code and then * return :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. * Again, use ``frame->push_promise.promised_stream_id`` as stream_id * parameter in `nghttp2_submit_rst_stream()` if frame is * PUSH_PROMISE. * * The implementation of this function must return 0 if it succeeds. * It may return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` or * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. For * other critical failures, it must return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other * nonzero value is returned, it is treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned, * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` * functions immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * * To set this callback to :type:`nghttp2_session_callbacks`, use * `nghttp2_session_callbacks_set_on_header_callback()`. * * .. warning:: * * Application should properly limit the total buffer size to store * incoming header fields. Without it, peer may send large number * of header fields or large header fields to cause out of memory in * local endpoint. Due to how HPACK works, peer can do this * effectively without using much memory on their own. */ typedef int (*nghttp2_on_header_callback)(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *user_data); /** * @functypedef * * Callback function invoked when a header name/value pair is received * for the |frame|. The |name| is header name. The |value| is header * value. The |flags| is bitwise OR of one or more of * :type:`nghttp2_nv_flag`. * * This callback behaves like :type:`nghttp2_on_header_callback`, * except that |name| and |value| are stored in reference counted * buffer. If application wishes to keep these references without * copying them, use `nghttp2_rcbuf_incref()` to increment their * reference count. It is the application's responsibility to call * `nghttp2_rcbuf_decref()` if they called `nghttp2_rcbuf_incref()` so * as not to leak memory. If the |session| is created by * `nghttp2_session_server_new3()` or `nghttp2_session_client_new3()`, * the function to free memory is the one belongs to the mem * parameter. As long as this free function alives, |name| and * |value| can live after |session| was destroyed. */ typedef int (*nghttp2_on_header_callback2)(nghttp2_session *session, const nghttp2_frame *frame, nghttp2_rcbuf *name, nghttp2_rcbuf *value, uint8_t flags, void *user_data); /** * @functypedef * * Callback function invoked when an invalid header name/value pair is * received for the |frame|. * * The parameter and behaviour are similar to * :type:`nghttp2_on_header_callback`. The difference is that this * callback is only invoked when an invalid header name/value pair is * received which is treated as stream error if this callback returns * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` and * :type:`nghttp2_on_invalid_header_callback2` is not set. Only * invalid regular header field are passed to this callback. In other * words, invalid pseudo header field is not passed to this callback. * Also header fields which includes upper cased latter are also * treated as error without passing them to this callback. * * This callback is only considered if HTTP messaging validation is * turned on (which is on by default, see * `nghttp2_option_set_no_http_messaging()`). * * With this callback, application inspects the incoming invalid * field, and it also can reset stream from this callback by returning * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. By * default, the error code is * :enum:`nghttp2_error_code.NGHTTP2_PROTOCOL_ERROR`. To change the * error code, call `nghttp2_submit_rst_stream()` with the error code * of choice in addition to returning * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. * * If 0 is returned, the header field is ignored, and the stream is * not reset. */ typedef int (*nghttp2_on_invalid_header_callback)( nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *user_data); /** * @functypedef * * Callback function invoked when an invalid header name/value pair is * received for the |frame|. * * The parameter and behaviour are similar to * :type:`nghttp2_on_header_callback2`. The difference is that this * callback is only invoked when an invalid header name/value pair is * received which is silently ignored if neither this callback nor * :type:`nghttp2_on_invalid_header_callback` is set. Only invalid * regular header field are passed to this callback. In other words, * invalid pseudo header field is not passed to this callback. Also * header fields which includes upper cased latter are also treated as * error without passing them to this callback. * * This callback is only considered if HTTP messaging validation is * turned on (which is on by default, see * `nghttp2_option_set_no_http_messaging()`). * * With this callback, application inspects the incoming invalid * field, and it also can reset stream from this callback by returning * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. By * default, the error code is * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. To change the * error code, call `nghttp2_submit_rst_stream()` with the error code * of choice in addition to returning * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. */ typedef int (*nghttp2_on_invalid_header_callback2)( nghttp2_session *session, const nghttp2_frame *frame, nghttp2_rcbuf *name, nghttp2_rcbuf *value, uint8_t flags, void *user_data); #ifndef NGHTTP2_NO_SSIZE_T /** * @functypedef * * .. warning:: * * Deprecated. Use :type:`nghttp2_select_padding_callback2` * instead. * * Callback function invoked when the library asks application how * many padding bytes are required for the transmission of the * |frame|. The application must choose the total length of payload * including padded bytes in range [frame->hd.length, max_payloadlen], * inclusive. Choosing number not in this range will be treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. Returning * ``frame->hd.length`` means no padding is added. Returning * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will make * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions * immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * * To set this callback to :type:`nghttp2_session_callbacks`, use * `nghttp2_session_callbacks_set_select_padding_callback()`. */ typedef ssize_t (*nghttp2_select_padding_callback)(nghttp2_session *session, const nghttp2_frame *frame, size_t max_payloadlen, void *user_data); #endif /* NGHTTP2_NO_SSIZE_T */ /** * @functypedef * * Callback function invoked when the library asks application how * many padding bytes are required for the transmission of the * |frame|. The application must choose the total length of payload * including padded bytes in range [frame->hd.length, max_payloadlen], * inclusive. Choosing number not in this range will be treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. Returning * ``frame->hd.length`` means no padding is added. Returning * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will make * `nghttp2_session_send()` and `nghttp2_session_mem_send2()` * functions immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * * To set this callback to :type:`nghttp2_session_callbacks`, use * `nghttp2_session_callbacks_set_select_padding_callback2()`. */ typedef nghttp2_ssize (*nghttp2_select_padding_callback2)( nghttp2_session *session, const nghttp2_frame *frame, size_t max_payloadlen, void *user_data); #ifndef NGHTTP2_NO_SSIZE_T /** * @functypedef * * .. warning:: * * Deprecated. Use * :type:`nghttp2_data_source_read_length_callback2` instead. * * Callback function invoked when library wants to get max length of * data to send data to the remote peer. The implementation of this * function should return a value in the following range. [1, * min(|session_remote_window_size|, |stream_remote_window_size|, * |remote_max_frame_size|)]. If a value greater than this range is * returned than the max allow value will be used. Returning a value * smaller than this range is treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. The * |frame_type| is provided for future extensibility and identifies * the type of frame (see :type:`nghttp2_frame_type`) for which to get * the length for. Currently supported frame types are: * :enum:`nghttp2_frame_type.NGHTTP2_DATA`. * * This callback can be used to control the length in bytes for which * :type:`nghttp2_data_source_read_callback` is allowed to send to the * remote endpoint. This callback is optional. Returning * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will signal the * entire session failure. * * To set this callback to :type:`nghttp2_session_callbacks`, use * `nghttp2_session_callbacks_set_data_source_read_length_callback()`. */ typedef ssize_t (*nghttp2_data_source_read_length_callback)( nghttp2_session *session, uint8_t frame_type, int32_t stream_id, int32_t session_remote_window_size, int32_t stream_remote_window_size, uint32_t remote_max_frame_size, void *user_data); #endif /* NGHTTP2_NO_SSIZE_T */ /** * @functypedef * * Callback function invoked when library wants to get max length of * data to send data to the remote peer. The implementation of this * function should return a value in the following range. [1, * min(|session_remote_window_size|, |stream_remote_window_size|, * |remote_max_frame_size|)]. If a value greater than this range is * returned than the max allow value will be used. Returning a value * smaller than this range is treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. The * |frame_type| is provided for future extensibility and identifies * the type of frame (see :type:`nghttp2_frame_type`) for which to get * the length for. Currently supported frame types are: * :enum:`nghttp2_frame_type.NGHTTP2_DATA`. * * This callback can be used to control the length in bytes for which * :type:`nghttp2_data_source_read_callback` is allowed to send to the * remote endpoint. This callback is optional. Returning * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will signal the * entire session failure. * * To set this callback to :type:`nghttp2_session_callbacks`, use * `nghttp2_session_callbacks_set_data_source_read_length_callback2()`. */ typedef nghttp2_ssize (*nghttp2_data_source_read_length_callback2)( nghttp2_session *session, uint8_t frame_type, int32_t stream_id, int32_t session_remote_window_size, int32_t stream_remote_window_size, uint32_t remote_max_frame_size, void *user_data); /** * @functypedef * * Callback function invoked when a frame header is received. The * |hd| points to received frame header. * * Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will * also be called when frame header of CONTINUATION frame is received. * * If both :type:`nghttp2_on_begin_frame_callback` and * :type:`nghttp2_on_begin_headers_callback` are set and HEADERS or * PUSH_PROMISE is received, :type:`nghttp2_on_begin_frame_callback` * will be called first. * * The implementation of this function must return 0 if it succeeds. * If nonzero value is returned, it is treated as fatal error and * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` * functions immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * * To set this callback to :type:`nghttp2_session_callbacks`, use * `nghttp2_session_callbacks_set_on_begin_frame_callback()`. */ typedef int (*nghttp2_on_begin_frame_callback)(nghttp2_session *session, const nghttp2_frame_hd *hd, void *user_data); /** * @functypedef * * Callback function invoked when chunk of extension frame payload is * received. The |hd| points to frame header. The received * chunk is |data| of length |len|. * * The implementation of this function must return 0 if it succeeds. * * To abort processing this extension frame, return * :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`. * * If fatal error occurred, application should return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` * functions immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other * values are returned, currently they are treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. */ typedef int (*nghttp2_on_extension_chunk_recv_callback)( nghttp2_session *session, const nghttp2_frame_hd *hd, const uint8_t *data, size_t len, void *user_data); /** * @functypedef * * Callback function invoked when library asks the application to * unpack extension payload from its wire format. The extension * payload has been passed to the application using * :type:`nghttp2_on_extension_chunk_recv_callback`. The frame header * is already unpacked by the library and provided as |hd|. * * To receive extension frames, the application must tell desired * extension frame type to the library using * `nghttp2_option_set_user_recv_extension_type()`. * * The implementation of this function may store the pointer to the * created object as a result of unpacking in |*payload|, and returns * 0. The pointer stored in |*payload| is opaque to the library, and * the library does not own its pointer. |*payload| is initialized as * ``NULL``. The |*payload| is available as ``frame->ext.payload`` in * :type:`nghttp2_on_frame_recv_callback`. Therefore if application * can free that memory inside :type:`nghttp2_on_frame_recv_callback` * callback. Of course, application has a liberty not to use * |*payload|, and do its own mechanism to process extension frames. * * To abort processing this extension frame, return * :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`. * * If fatal error occurred, application should return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` * functions immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other * values are returned, currently they are treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. */ typedef int (*nghttp2_unpack_extension_callback)(nghttp2_session *session, void **payload, const nghttp2_frame_hd *hd, void *user_data); #ifndef NGHTTP2_NO_SSIZE_T /** * @functypedef * * .. warning:: * * Deprecated. Use :type:`nghttp2_pack_extension_callback2` * instead. * * Callback function invoked when library asks the application to pack * extension payload in its wire format. The frame header will be * packed by library. Application must pack payload only. * ``frame->ext.payload`` is the object passed to * `nghttp2_submit_extension()` as payload parameter. Application * must pack extension payload to the |buf| of its capacity |len| * bytes. The |len| is at least 16KiB. * * The implementation of this function should return the number of * bytes written into |buf| when it succeeds. * * To abort processing this extension frame, return * :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`, and * :type:`nghttp2_on_frame_not_send_callback` will be invoked. * * If fatal error occurred, application should return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions * immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other * values are returned, currently they are treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the return * value is strictly larger than |len|, it is treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. */ typedef ssize_t (*nghttp2_pack_extension_callback)(nghttp2_session *session, uint8_t *buf, size_t len, const nghttp2_frame *frame, void *user_data); #endif /* NGHTTP2_NO_SSIZE_T */ /** * @functypedef * * Callback function invoked when library asks the application to pack * extension payload in its wire format. The frame header will be * packed by library. Application must pack payload only. * ``frame->ext.payload`` is the object passed to * `nghttp2_submit_extension()` as payload parameter. Application * must pack extension payload to the |buf| of its capacity |len| * bytes. The |len| is at least 16KiB. * * The implementation of this function should return the number of * bytes written into |buf| when it succeeds. * * To abort processing this extension frame, return * :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`, and * :type:`nghttp2_on_frame_not_send_callback` will be invoked. * * If fatal error occurred, application should return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, * `nghttp2_session_send()` and `nghttp2_session_mem_send2()` * functions immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other * values are returned, currently they are treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the return * value is strictly larger than |len|, it is treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. */ typedef nghttp2_ssize (*nghttp2_pack_extension_callback2)( nghttp2_session *session, uint8_t *buf, size_t len, const nghttp2_frame *frame, void *user_data); /** * @functypedef * * .. warning:: * * Deprecated. Use :type:`nghttp2_error_callback2` instead. * * Callback function invoked when library provides the error message * intended for human consumption. This callback is solely for * debugging purpose. The |msg| is typically NULL-terminated string * of length |len|. |len| does not include the sentinel NULL * character. * * The format of error message may change between nghttp2 library * versions. The application should not depend on the particular * format. * * Normally, application should return 0 from this callback. If fatal * error occurred while doing something in this callback, application * should return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * In this case, library will return immediately with return value * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. Currently, if * nonzero value is returned from this callback, they are treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, but application * should not rely on this details. */ typedef int (*nghttp2_error_callback)(nghttp2_session *session, const char *msg, size_t len, void *user_data); /** * @functypedef * * Callback function invoked when library provides the error code, and * message. This callback is solely for debugging purpose. * |lib_error_code| is one of error code defined in * :enum:`nghttp2_error`. The |msg| is typically NULL-terminated * string of length |len|, and intended for human consumption. |len| * does not include the sentinel NULL character. * * The format of error message may change between nghttp2 library * versions. The application should not depend on the particular * format. * * Normally, application should return 0 from this callback. If fatal * error occurred while doing something in this callback, application * should return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * In this case, library will return immediately with return value * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. Currently, if * nonzero value is returned from this callback, they are treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, but application * should not rely on this details. */ typedef int (*nghttp2_error_callback2)(nghttp2_session *session, int lib_error_code, const char *msg, size_t len, void *user_data); /** * @functypedef * * Callback function invoked when unpredictable data of |destlen| * bytes are needed. The implementation must write unpredictable data * of |destlen| bytes into the buffer pointed by |dest|. */ typedef void (*nghttp2_rand_callback)(uint8_t *dest, size_t destlen); struct nghttp2_session_callbacks; /** * @struct * * Callback functions for :type:`nghttp2_session`. The details of * this structure are intentionally hidden from the public API. */ typedef struct nghttp2_session_callbacks nghttp2_session_callbacks; /** * @function * * Initializes |*callbacks_ptr| with NULL values. * * The initialized object can be used when initializing multiple * :type:`nghttp2_session` objects. * * When the application finished using this object, it can use * `nghttp2_session_callbacks_del()` to free its memory. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ NGHTTP2_EXTERN int nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr); /** * @function * * Frees any resources allocated for |callbacks|. If |callbacks| is * ``NULL``, this function does nothing. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks); #ifndef NGHTTP2_NO_SSIZE_T /** * @function * * .. warning:: * * Deprecated. Use `nghttp2_session_callbacks_set_send_callback2()` * with :type:`nghttp2_send_callback2` instead. * * Sets callback function invoked when a session wants to send data to * the remote peer. This callback is not necessary if the application * uses solely `nghttp2_session_mem_send()` to serialize data to * transmit. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_callback( nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback); #endif /* NGHTTP2_NO_SSIZE_T */ /** * @function * * Sets callback function invoked when a session wants to send data to * the remote peer. This callback is not necessary if the application * uses solely `nghttp2_session_mem_send2()` to serialize data to * transmit. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_callback2( nghttp2_session_callbacks *cbs, nghttp2_send_callback2 send_callback); #ifndef NGHTTP2_NO_SSIZE_T /** * @function * * .. warning:: * * Deprecated. Use `nghttp2_session_callbacks_set_recv_callback2()` * with :type:`nghttp2_recv_callback2` instead. * * Sets callback function invoked when the a session wants to receive * data from the remote peer. This callback is not necessary if the * application uses solely `nghttp2_session_mem_recv()` to process * received data. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback); #endif /* NGHTTP2_NO_SSIZE_T */ /** * @function * * Sets callback function invoked when the a session wants to receive * data from the remote peer. This callback is not necessary if the * application uses solely `nghttp2_session_mem_recv2()` to process * received data. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_recv_callback2( nghttp2_session_callbacks *cbs, nghttp2_recv_callback2 recv_callback); /** * @function * * Sets callback function invoked by `nghttp2_session_recv()` and * `nghttp2_session_mem_recv2()` when a frame is received. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_frame_recv_callback on_frame_recv_callback); /** * @function * * Sets callback function invoked by `nghttp2_session_recv()` and * `nghttp2_session_mem_recv2()` when an invalid non-DATA frame is * received. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback); /** * @function * * Sets callback function invoked when a chunk of data in DATA frame * is received. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback); /** * @function * * Sets callback function invoked before a non-DATA frame is sent. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_before_frame_send_callback( nghttp2_session_callbacks *cbs, nghttp2_before_frame_send_callback before_frame_send_callback); /** * @function * * Sets callback function invoked after a frame is sent. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_send_callback( nghttp2_session_callbacks *cbs, nghttp2_on_frame_send_callback on_frame_send_callback); /** * @function * * Sets callback function invoked when a non-DATA frame is not sent * because of an error. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_not_send_callback( nghttp2_session_callbacks *cbs, nghttp2_on_frame_not_send_callback on_frame_not_send_callback); /** * @function * * Sets callback function invoked when the stream is closed. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_stream_close_callback( nghttp2_session_callbacks *cbs, nghttp2_on_stream_close_callback on_stream_close_callback); /** * @function * * Sets callback function invoked when the reception of header block * in HEADERS or PUSH_PROMISE is started. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_headers_callback( nghttp2_session_callbacks *cbs, nghttp2_on_begin_headers_callback on_begin_headers_callback); /** * @function * * Sets callback function invoked when a header name/value pair is * received. If both * `nghttp2_session_callbacks_set_on_header_callback()` and * `nghttp2_session_callbacks_set_on_header_callback2()` are used to * set callbacks, the latter has the precedence. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback( nghttp2_session_callbacks *cbs, nghttp2_on_header_callback on_header_callback); /** * @function * * Sets callback function invoked when a header name/value pair is * received. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback2( nghttp2_session_callbacks *cbs, nghttp2_on_header_callback2 on_header_callback2); /** * @function * * Sets callback function invoked when an invalid header name/value * pair is received. If both * `nghttp2_session_callbacks_set_on_invalid_header_callback()` and * `nghttp2_session_callbacks_set_on_invalid_header_callback2()` are * used to set callbacks, the latter takes the precedence. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_invalid_header_callback( nghttp2_session_callbacks *cbs, nghttp2_on_invalid_header_callback on_invalid_header_callback); /** * @function * * Sets callback function invoked when an invalid header name/value * pair is received. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_invalid_header_callback2( nghttp2_session_callbacks *cbs, nghttp2_on_invalid_header_callback2 on_invalid_header_callback2); #ifndef NGHTTP2_NO_SSIZE_T /** * @function * * .. warning:: * * Deprecated. Use * `nghttp2_session_callbacks_set_select_padding_callback2()` with * :type:`nghttp2_select_padding_callback2` instead. * * Sets callback function invoked when the library asks application * how many padding bytes are required for the transmission of the * given frame. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_select_padding_callback( nghttp2_session_callbacks *cbs, nghttp2_select_padding_callback select_padding_callback); #endif /* NGHTTP2_NO_SSIZE_T */ /** * @function * * Sets callback function invoked when the library asks application * how many padding bytes are required for the transmission of the * given frame. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_select_padding_callback2( nghttp2_session_callbacks *cbs, nghttp2_select_padding_callback2 select_padding_callback); #ifndef NGHTTP2_NO_SSIZE_T /** * @function * * .. warning:: * * Deprecated. Use * `nghttp2_session_callbacks_set_data_source_read_length_callback2()` * with :type:`nghttp2_data_source_read_length_callback2` instead. * * Sets callback function determine the length allowed in * :type:`nghttp2_data_source_read_callback`. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_data_source_read_length_callback( nghttp2_session_callbacks *cbs, nghttp2_data_source_read_length_callback data_source_read_length_callback); #endif /* NGHTTP2_NO_SSIZE_T */ /** * @function * * Sets callback function determine the length allowed in * :type:`nghttp2_data_source_read_callback2`. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_data_source_read_length_callback2( nghttp2_session_callbacks *cbs, nghttp2_data_source_read_length_callback2 data_source_read_length_callback); /** * @function * * Sets callback function invoked when a frame header is received. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_frame_callback( nghttp2_session_callbacks *cbs, nghttp2_on_begin_frame_callback on_begin_frame_callback); /** * @function * * Sets callback function invoked when * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` is used in * :type:`nghttp2_data_source_read_callback2` to avoid data copy. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_data_callback( nghttp2_session_callbacks *cbs, nghttp2_send_data_callback send_data_callback); #ifndef NGHTTP2_NO_SSIZE_T /** * @function * * .. warning:: * * Deprecated. Use * `nghttp2_session_callbacks_set_pack_extension_callback2()` with * :type:`nghttp2_pack_extension_callback2` instead. * * Sets callback function invoked when the library asks the * application to pack extension frame payload in wire format. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_pack_extension_callback( nghttp2_session_callbacks *cbs, nghttp2_pack_extension_callback pack_extension_callback); #endif /* NGHTTP2_NO_SSIZE_T */ /** * @function * * Sets callback function invoked when the library asks the * application to pack extension frame payload in wire format. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_pack_extension_callback2( nghttp2_session_callbacks *cbs, nghttp2_pack_extension_callback2 pack_extension_callback); /** * @function * * Sets callback function invoked when the library asks the * application to unpack extension frame payload from wire format. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_unpack_extension_callback( nghttp2_session_callbacks *cbs, nghttp2_unpack_extension_callback unpack_extension_callback); /** * @function * * Sets callback function invoked when chunk of extension frame * payload is received. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_extension_chunk_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback); /** * @function * * .. warning:: * * Deprecated. Use * `nghttp2_session_callbacks_set_error_callback2()` with * :type:`nghttp2_error_callback2` instead. * * Sets callback function invoked when library tells error message to * the application. * * If both :type:`nghttp2_error_callback` and * :type:`nghttp2_error_callback2` are set, the latter takes * precedence. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback( nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback); /** * @function * * Sets callback function invoked when library tells error code, and * message to the application. * * If both :type:`nghttp2_error_callback` and * :type:`nghttp2_error_callback2` are set, the latter takes * precedence. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback2( nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2); /** * @function * * Sets callback function invoked when unpredictable data is needed. * Although this callback is optional due to the backward * compatibility, it is recommended to specify it to harden the * runtime behavior against suspicious activities of a remote * endpoint. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_rand_callback( nghttp2_session_callbacks *cbs, nghttp2_rand_callback rand_callback); /** * @functypedef * * Custom memory allocator to replace malloc(). The |mem_user_data| * is the mem_user_data member of :type:`nghttp2_mem` structure. */ typedef void *(*nghttp2_malloc)(size_t size, void *mem_user_data); /** * @functypedef * * Custom memory allocator to replace free(). The |mem_user_data| is * the mem_user_data member of :type:`nghttp2_mem` structure. */ typedef void (*nghttp2_free)(void *ptr, void *mem_user_data); /** * @functypedef * * Custom memory allocator to replace calloc(). The |mem_user_data| * is the mem_user_data member of :type:`nghttp2_mem` structure. */ typedef void *(*nghttp2_calloc)(size_t nmemb, size_t size, void *mem_user_data); /** * @functypedef * * Custom memory allocator to replace realloc(). The |mem_user_data| * is the mem_user_data member of :type:`nghttp2_mem` structure. */ typedef void *(*nghttp2_realloc)(void *ptr, size_t size, void *mem_user_data); /** * @struct * * Custom memory allocator functions and user defined pointer. The * |mem_user_data| member is passed to each allocator function. This * can be used, for example, to achieve per-session memory pool. * * In the following example code, ``my_malloc``, ``my_free``, * ``my_calloc`` and ``my_realloc`` are the replacement of the * standard allocators ``malloc``, ``free``, ``calloc`` and * ``realloc`` respectively:: * * void *my_malloc_cb(size_t size, void *mem_user_data) { * return my_malloc(size); * } * * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } * * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { * return my_calloc(nmemb, size); * } * * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { * return my_realloc(ptr, size); * } * * void session_new() { * nghttp2_session *session; * nghttp2_session_callbacks *callbacks; * nghttp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb, * my_realloc_cb}; * * ... * * nghttp2_session_client_new3(&session, callbacks, NULL, NULL, &mem); * * ... * } */ typedef struct { /** * An arbitrary user supplied data. This is passed to each * allocator function. */ void *mem_user_data; /** * Custom allocator function to replace malloc(). */ nghttp2_malloc malloc; /** * Custom allocator function to replace free(). */ nghttp2_free free; /** * Custom allocator function to replace calloc(). */ nghttp2_calloc calloc; /** * Custom allocator function to replace realloc(). */ nghttp2_realloc realloc; } nghttp2_mem; struct nghttp2_option; /** * @struct * * Configuration options for :type:`nghttp2_session`. The details of * this structure are intentionally hidden from the public API. */ typedef struct nghttp2_option nghttp2_option; /** * @function * * Initializes |*option_ptr| with default values. * * When the application finished using this object, it can use * `nghttp2_option_del()` to free its memory. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ NGHTTP2_EXTERN int nghttp2_option_new(nghttp2_option **option_ptr); /** * @function * * Frees any resources allocated for |option|. If |option| is * ``NULL``, this function does nothing. */ NGHTTP2_EXTERN void nghttp2_option_del(nghttp2_option *option); /** * @function * * This option prevents the library from sending WINDOW_UPDATE for a * connection automatically. If this option is set to nonzero, the * library won't send WINDOW_UPDATE for DATA until application calls * `nghttp2_session_consume()` to indicate the consumed amount of * data. Don't use `nghttp2_submit_window_update()` for this purpose. * By default, this option is set to zero. */ NGHTTP2_EXTERN void nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val); /** * @function * * This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of * remote endpoint as if it is received in SETTINGS frame. Without * specifying this option, the maximum number of outgoing concurrent * streams is initially limited to 100 to avoid issues when the local * endpoint submits lots of requests before receiving initial SETTINGS * frame from the remote endpoint, since sending them at once to the * remote endpoint could lead to rejection of some of the requests. * This value will be overwritten when the local endpoint receives * initial SETTINGS frame from the remote endpoint, either to the * value advertised in SETTINGS_MAX_CONCURRENT_STREAMS or to the * default value (unlimited) if none was advertised. */ NGHTTP2_EXTERN void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, uint32_t val); /** * @function * * By default, nghttp2 library, if configured as server, requires * first 24 bytes of client magic byte string (MAGIC). In most cases, * this will simplify the implementation of server. But sometimes * server may want to detect the application protocol based on first * few bytes on clear text communication. * * If this option is used with nonzero |val|, nghttp2 library does not * handle MAGIC. It still checks following SETTINGS frame. This * means that applications should deal with MAGIC by themselves. * * If this option is not used or used with zero value, if MAGIC does * not match :macro:`NGHTTP2_CLIENT_MAGIC`, `nghttp2_session_recv()` * and `nghttp2_session_mem_recv2()` will return error * :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC`, which is fatal * error. */ NGHTTP2_EXTERN void nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val); /** * @function * * By default, nghttp2 library enforces subset of HTTP Messaging rules * described in `HTTP/2 specification, section 8 * `_. See * :ref:`http-messaging` section for details. For those applications * who use nghttp2 library as non-HTTP use, give nonzero to |val| to * disable this enforcement. Please note that disabling this feature * does not change the fundamental client and server model of HTTP. * That is, even if the validation is disabled, only client can send * requests. */ NGHTTP2_EXTERN void nghttp2_option_set_no_http_messaging(nghttp2_option *option, int val); /** * @function * * RFC 7540 does not enforce any limit on the number of incoming * reserved streams (in RFC 7540 terms, streams in reserved (remote) * state). This only affects client side, since only server can push * streams. Malicious server can push arbitrary number of streams, * and make client's memory exhausted. This option can set the * maximum number of such incoming streams to avoid possible memory * exhaustion. If this option is set, and pushed streams are * automatically closed on reception, without calling user provided * callback, if they exceed the given limit. The default value is * 200. If session is configured as server side, this option has no * effect. Server can control the number of streams to push. */ NGHTTP2_EXTERN void nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option, uint32_t val); /** * @function * * Sets extension frame type the application is willing to handle with * user defined callbacks (see * :type:`nghttp2_on_extension_chunk_recv_callback` and * :type:`nghttp2_unpack_extension_callback`). The |type| is * extension frame type, and must be strictly greater than 0x9. * Otherwise, this function does nothing. The application can call * this function multiple times to set more than one frame type to * receive. The application does not have to call this function if it * just sends extension frames. */ NGHTTP2_EXTERN void nghttp2_option_set_user_recv_extension_type(nghttp2_option *option, uint8_t type); /** * @function * * Sets extension frame type the application is willing to receive * using builtin handler. The |type| is the extension frame type to * receive, and must be strictly greater than 0x9. Otherwise, this * function does nothing. The application can call this function * multiple times to set more than one frame type to receive. The * application does not have to call this function if it just sends * extension frames. * * If same frame type is passed to both * `nghttp2_option_set_builtin_recv_extension_type()` and * `nghttp2_option_set_user_recv_extension_type()`, the latter takes * precedence. */ NGHTTP2_EXTERN void nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option, uint8_t type); /** * @function * * This option prevents the library from sending PING frame with ACK * flag set automatically when PING frame without ACK flag set is * received. If this option is set to nonzero, the library won't send * PING frame with ACK flag set in the response for incoming PING * frame. The application can send PING frame with ACK flag set using * `nghttp2_submit_ping()` with :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` * as flags parameter. */ NGHTTP2_EXTERN void nghttp2_option_set_no_auto_ping_ack(nghttp2_option *option, int val); /** * @function * * This option sets the maximum length of header block (a set of * header fields per one HEADERS frame) to send. The length of a * given set of header fields is calculated using * `nghttp2_hd_deflate_bound()`. The default value is 64KiB. If * application attempts to send header fields larger than this limit, * the transmission of the frame fails with error code * :enum:`nghttp2_error.NGHTTP2_ERR_FRAME_SIZE_ERROR`. */ NGHTTP2_EXTERN void nghttp2_option_set_max_send_header_block_length(nghttp2_option *option, size_t val); /** * @function * * This option sets the maximum dynamic table size for deflating * header fields. The default value is 4KiB. In HTTP/2, receiver of * deflated header block can specify maximum dynamic table size. The * actual maximum size is the minimum of the size receiver specified * and this option value. */ NGHTTP2_EXTERN void nghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option, size_t val); /** * @function * * .. warning:: * * Deprecated. Closed streams are not retained anymore. * * This function works as before, but it does not take any effect * against :type:`nghttp2_session`. */ NGHTTP2_EXTERN void nghttp2_option_set_no_closed_streams(nghttp2_option *option, int val); /** * @function * * This function sets the maximum number of outgoing SETTINGS ACK and * PING ACK frames retained in :type:`nghttp2_session` object. If * more than those frames are retained, the peer is considered to be * misbehaving and session will be closed. The default value is 1000. */ NGHTTP2_EXTERN void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, size_t val); /** * @function * * This function sets the maximum number of SETTINGS entries per * SETTINGS frame that will be accepted. If more than those entries * are received, the peer is considered to be misbehaving and session * will be closed. The default value is 32. */ NGHTTP2_EXTERN void nghttp2_option_set_max_settings(nghttp2_option *option, size_t val); /** * @function * * .. warning:: * Deprecated. :rfc:`7540` priorities have been removed. * * This function works as before, but it does not take any effect * against :type:`nghttp2_session`. */ NGHTTP2_EXTERN void nghttp2_option_set_server_fallback_rfc7540_priorities(nghttp2_option *option, int val); /** * @function * * This option, if set to nonzero, turns off RFC 9113 leading and * trailing white spaces validation against HTTP field value. Some * important fields, such as HTTP/2 pseudo header fields, are * validated more strictly and this option does not apply to them. */ NGHTTP2_EXTERN void nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation( nghttp2_option *option, int val); /** * @function * * This function sets the rate limit for the incoming stream reset * (RST_STREAM frame). It is server use only. It is a token-bucket * based rate limiter. |burst| specifies the number of tokens that is * initially available. The maximum number of tokens is capped to * this value. |rate| specifies the number of tokens that are * regenerated per second. An incoming RST_STREAM consumes one token. * If there is no token available, GOAWAY is sent to tear down the * connection. |burst| and |rate| default to 1000 and 33 * respectively. */ NGHTTP2_EXTERN void nghttp2_option_set_stream_reset_rate_limit(nghttp2_option *option, uint64_t burst, uint64_t rate); /** * @function * * This function sets the maximum number of CONTINUATION frames * following an incoming HEADER frame. If more than those frames are * received, the remote endpoint is considered to be misbehaving and * session will be closed. The default value is 8. */ NGHTTP2_EXTERN void nghttp2_option_set_max_continuations(nghttp2_option *option, size_t val); /** * @function * * This function sets the rate limit for the "glitches", the * suspicious activities from a remote endpoint. It is a token-bucket * based rate limiter. |burst| specifies the number of tokens that is * initially available. The maximum number of tokens is capped to * this value. |rate| specifies the number of tokens that are * regenerated per second. When a suspicious activity is detected, * some amount of tokens are consumed. If there is no token * available, GOAWAY is sent to tear down the connection. |burst| and * |rate| default to 1000 and 33 respectively. */ NGHTTP2_EXTERN void nghttp2_option_set_glitch_rate_limit(nghttp2_option *option, uint64_t burst, uint64_t rate); /** * @function * * Initializes |*session_ptr| for client use. The all members of * |callbacks| are copied to |*session_ptr|. Therefore |*session_ptr| * does not store |callbacks|. The |user_data| is an arbitrary user * supplied data, which will be passed to the callback functions. * * The :type:`nghttp2_send_callback2` must be specified. If the * application code uses `nghttp2_session_recv()`, the * :type:`nghttp2_recv_callback` must be specified. The other members * of |callbacks| can be ``NULL``. * * If this function fails, |*session_ptr| is left untouched. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ NGHTTP2_EXTERN int nghttp2_session_client_new(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data); /** * @function * * Initializes |*session_ptr| for server use. The all members of * |callbacks| are copied to |*session_ptr|. Therefore |*session_ptr| * does not store |callbacks|. The |user_data| is an arbitrary user * supplied data, which will be passed to the callback functions. * * The :type:`nghttp2_send_callback2` must be specified. If the * application code uses `nghttp2_session_recv()`, the * :type:`nghttp2_recv_callback` must be specified. The other members * of |callbacks| can be ``NULL``. * * If this function fails, |*session_ptr| is left untouched. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ NGHTTP2_EXTERN int nghttp2_session_server_new(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data); /** * @function * * Like `nghttp2_session_client_new()`, but with additional options * specified in the |option|. * * The |option| can be ``NULL`` and the call is equivalent to * `nghttp2_session_client_new()`. * * This function does not take ownership |option|. The application is * responsible for freeing |option| if it finishes using the object. * * The library code does not refer to |option| after this function * returns. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ NGHTTP2_EXTERN int nghttp2_session_client_new2(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option); /** * @function * * Like `nghttp2_session_server_new()`, but with additional options * specified in the |option|. * * The |option| can be ``NULL`` and the call is equivalent to * `nghttp2_session_server_new()`. * * This function does not take ownership |option|. The application is * responsible for freeing |option| if it finishes using the object. * * The library code does not refer to |option| after this function * returns. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ NGHTTP2_EXTERN int nghttp2_session_server_new2(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option); /** * @function * * Like `nghttp2_session_client_new2()`, but with additional custom * memory allocator specified in the |mem|. * * The |mem| can be ``NULL`` and the call is equivalent to * `nghttp2_session_client_new2()`. * * This function does not take ownership |mem|. The application is * responsible for freeing |mem|. * * The library code does not refer to |mem| pointer after this * function returns, so the application can safely free it. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ NGHTTP2_EXTERN int nghttp2_session_client_new3( nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option, nghttp2_mem *mem); /** * @function * * Like `nghttp2_session_server_new2()`, but with additional custom * memory allocator specified in the |mem|. * * The |mem| can be ``NULL`` and the call is equivalent to * `nghttp2_session_server_new2()`. * * This function does not take ownership |mem|. The application is * responsible for freeing |mem|. * * The library code does not refer to |mem| pointer after this * function returns, so the application can safely free it. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ NGHTTP2_EXTERN int nghttp2_session_server_new3( nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option, nghttp2_mem *mem); /** * @function * * Frees any resources allocated for |session|. If |session| is * ``NULL``, this function does nothing. */ NGHTTP2_EXTERN void nghttp2_session_del(nghttp2_session *session); /** * @function * * Sends pending frames to the remote peer. * * This function retrieves the highest prioritized frame from the * outbound queue and sends it to the remote peer. It does this as * many times as possible until the user callback * :type:`nghttp2_send_callback2` returns * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`, the outbound queue * becomes empty or flow control is triggered (remote window size * becomes depleted or maximum number of concurrent streams is * reached). This function calls several callback functions which are * passed when initializing the |session|. Here is the simple time * chart which tells when each callback is invoked: * * 1. Get the next frame to send from outbound queue. * * 2. Prepare transmission of the frame. * * 3. If the control frame cannot be sent because some preconditions * are not met (e.g., request HEADERS cannot be sent after GOAWAY), * :type:`nghttp2_on_frame_not_send_callback` is invoked. Abort * the following steps. * * 4. If the frame is HEADERS, PUSH_PROMISE or DATA, * :type:`nghttp2_select_padding_callback` is invoked. * * 5. If the frame is request HEADERS, the stream is opened here. * * 6. :type:`nghttp2_before_frame_send_callback` is invoked. * * 7. If :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL` is returned from * :type:`nghttp2_before_frame_send_callback`, the current frame * transmission is canceled, and * :type:`nghttp2_on_frame_not_send_callback` is invoked. Abort * the following steps. * * 8. :type:`nghttp2_send_callback2` is invoked one or more times to * send the frame. * * 9. :type:`nghttp2_on_frame_send_callback` is invoked. * * 10. If the transmission of the frame triggers closure of the * stream, the stream is closed and * :type:`nghttp2_on_stream_close_callback` is invoked. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` * The callback function failed. */ NGHTTP2_EXTERN int nghttp2_session_send(nghttp2_session *session); #ifndef NGHTTP2_NO_SSIZE_T /** * @function * * .. warning:: * * Deprecated. Use `nghttp2_session_mem_send2()` instead. * * Returns the serialized data to send. * * This function behaves like `nghttp2_session_send()` except that it * does not use :type:`nghttp2_send_callback` to transmit data. * Instead, it assigns the pointer to the serialized data to the * |*data_ptr| and returns its length. The other callbacks are called * in the same way as they are in `nghttp2_session_send()`. * * If no data is available to send, this function returns 0. * * This function may not return all serialized data in one invocation. * To get all data, call this function repeatedly until it returns 0 * or one of negative error codes. * * The assigned |*data_ptr| is valid until the next call of * `nghttp2_session_mem_send()` or `nghttp2_session_send()`. * * The caller must send all data before sending the next chunk of * data. * * This function returns the length of the data pointed by the * |*data_ptr| if it succeeds, or one of the following negative error * codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * * .. note:: * * This function may produce very small byte string. If that is the * case, and application disables Nagle algorithm (``TCP_NODELAY``), * then writing this small chunk leads to very small packet, and it * is very inefficient. An application should be responsible to * buffer up small chunks of data as necessary to avoid this * situation. */ NGHTTP2_EXTERN ssize_t nghttp2_session_mem_send(nghttp2_session *session, const uint8_t **data_ptr); #endif /* NGHTTP2_NO_SSIZE_T */ /** * @function * * Returns the serialized data to send. * * This function behaves like `nghttp2_session_send()` except that it * does not use :type:`nghttp2_send_callback2` to transmit data. * Instead, it assigns the pointer to the serialized data to the * |*data_ptr| and returns its length. The other callbacks are called * in the same way as they are in `nghttp2_session_send()`. * * If no data is available to send, this function returns 0. * * This function may not return all serialized data in one invocation. * To get all data, call this function repeatedly until it returns 0 * or one of negative error codes. * * The assigned |*data_ptr| is valid until the next call of * `nghttp2_session_mem_send2()` or `nghttp2_session_send()`. * * The caller must send all data before sending the next chunk of * data. * * This function returns the length of the data pointed by the * |*data_ptr| if it succeeds, or one of the following negative error * codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * * .. note:: * * This function may produce very small byte string. If that is the * case, and application disables Nagle algorithm (``TCP_NODELAY``), * then writing this small chunk leads to very small packet, and it * is very inefficient. An application should be responsible to * buffer up small chunks of data as necessary to avoid this * situation. */ NGHTTP2_EXTERN nghttp2_ssize nghttp2_session_mem_send2(nghttp2_session *session, const uint8_t **data_ptr); /** * @function * * Receives frames from the remote peer. * * This function receives as many frames as possible until the user * callback :type:`nghttp2_recv_callback` returns * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. This function calls * several callback functions which are passed when initializing the * |session|. Here is the simple time chart which tells when each * callback is invoked: * * 1. :type:`nghttp2_recv_callback` is invoked one or more times to * receive frame header. * * 2. When frame header is received, * :type:`nghttp2_on_begin_frame_callback` is invoked. * * 3. If the frame is DATA frame: * * 1. :type:`nghttp2_recv_callback` is invoked to receive DATA * payload. For each chunk of data, * :type:`nghttp2_on_data_chunk_recv_callback` is invoked. * * 2. If one DATA frame is completely received, * :type:`nghttp2_on_frame_recv_callback` is invoked. If the * reception of the frame triggers the closure of the stream, * :type:`nghttp2_on_stream_close_callback` is invoked. * * 4. If the frame is the control frame: * * 1. :type:`nghttp2_recv_callback` is invoked one or more times to * receive whole frame. * * 2. If the received frame is valid, then following actions are * taken. If the frame is either HEADERS or PUSH_PROMISE, * :type:`nghttp2_on_begin_headers_callback` is invoked. Then * :type:`nghttp2_on_header_callback` is invoked for each header * name/value pair. For invalid header field, * :type:`nghttp2_on_invalid_header_callback` is called. After * all name/value pairs are emitted successfully, * :type:`nghttp2_on_frame_recv_callback` is invoked. For other * frames, :type:`nghttp2_on_frame_recv_callback` is invoked. * If the reception of the frame triggers the closure of the * stream, :type:`nghttp2_on_stream_close_callback` is invoked. * * 3. If the received frame is unpacked but is interpreted as * invalid, :type:`nghttp2_on_invalid_frame_recv_callback` is * invoked. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_EOF` * The remote peer did shutdown on the connection. * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` * The callback function failed. * :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC` * Invalid client magic was detected. This error only returns * when |session| was configured as server and * `nghttp2_option_set_no_recv_client_magic()` is not used with * nonzero value. * :enum:`nghttp2_error.NGHTTP2_ERR_FLOODED` * Flooding was detected in this HTTP/2 session, and it must be * closed. This is most likely caused by misbehaviour of peer. */ NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session); #ifndef NGHTTP2_NO_SSIZE_T /** * @function * * .. warning:: * * Deprecated. Use `nghttp2_session_mem_recv2()` instead. * * Processes data |in| as an input from the remote endpoint. The * |inlen| indicates the number of bytes to receive in the |in|. * * This function behaves like `nghttp2_session_recv()` except that it * does not use :type:`nghttp2_recv_callback` to receive data; the * |in| is the only data for the invocation of this function. If all * bytes are processed, this function returns. The other callbacks * are called in the same way as they are in `nghttp2_session_recv()`. * * In the current implementation, this function always tries to * processes |inlen| bytes of input data unless either an error occurs or * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is returned from * :type:`nghttp2_on_header_callback` or * :type:`nghttp2_on_data_chunk_recv_callback`. If * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is used, the return value * includes the number of bytes which was used to produce the data or * frame for the callback. * * This function returns the number of processed bytes, or one of the * following negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` * The callback function failed. * :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC` * Invalid client magic was detected. This error only returns * when |session| was configured as server and * `nghttp2_option_set_no_recv_client_magic()` is not used with * nonzero value. * :enum:`nghttp2_error.NGHTTP2_ERR_FLOODED` * Flooding was detected in this HTTP/2 session, and it must be * closed. This is most likely caused by misbehaviour of peer. */ NGHTTP2_EXTERN ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, size_t inlen); #endif /* NGHTTP2_NO_SSIZE_T */ /** * @function * * Processes data |in| as an input from the remote endpoint. The * |inlen| indicates the number of bytes to receive in the |in|. * * This function behaves like `nghttp2_session_recv()` except that it * does not use :type:`nghttp2_recv_callback` to receive data; the * |in| is the only data for the invocation of this function. If all * bytes are processed, this function returns. The other callbacks * are called in the same way as they are in `nghttp2_session_recv()`. * * In the current implementation, this function always tries to * processes |inlen| bytes of input data unless either an error occurs or * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is returned from * :type:`nghttp2_on_header_callback` or * :type:`nghttp2_on_data_chunk_recv_callback`. If * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is used, the return value * includes the number of bytes which was used to produce the data or * frame for the callback. * * This function returns the number of processed bytes, or one of the * following negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` * The callback function failed. * :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC` * Invalid client magic was detected. This error only returns * when |session| was configured as server and * `nghttp2_option_set_no_recv_client_magic()` is not used with * nonzero value. * :enum:`nghttp2_error.NGHTTP2_ERR_FLOODED` * Flooding was detected in this HTTP/2 session, and it must be * closed. This is most likely caused by misbehaviour of peer. */ NGHTTP2_EXTERN nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, const uint8_t *in, size_t inlen); /** * @function * * Puts back previously deferred DATA frame in the stream |stream_id| * to the outbound queue. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The stream does not exist; or no deferred data exist. * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ NGHTTP2_EXTERN int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id); /** * @function * * Returns nonzero value if |session| wants to receive data from the * remote peer. * * If both `nghttp2_session_want_read()` and * `nghttp2_session_want_write()` return 0, the application should * drop the connection. */ NGHTTP2_EXTERN int nghttp2_session_want_read(nghttp2_session *session); /** * @function * * Returns nonzero value if |session| wants to send data to the remote * peer. * * If both `nghttp2_session_want_read()` and * `nghttp2_session_want_write()` return 0, the application should * drop the connection. */ NGHTTP2_EXTERN int nghttp2_session_want_write(nghttp2_session *session); /** * @function * * Returns stream_user_data for the stream |stream_id|. The * stream_user_data is provided by `nghttp2_submit_request2()`, * `nghttp2_submit_headers()` or * `nghttp2_session_set_stream_user_data()`. Unless it is set using * `nghttp2_session_set_stream_user_data()`, if the stream is * initiated by the remote endpoint, stream_user_data is always * ``NULL``. If the stream does not exist, this function returns * ``NULL``. */ NGHTTP2_EXTERN void * nghttp2_session_get_stream_user_data(nghttp2_session *session, int32_t stream_id); /** * @function * * Sets the |stream_user_data| to the stream denoted by the * |stream_id|. If a stream user data is already set to the stream, * it is replaced with the |stream_user_data|. It is valid to specify * ``NULL`` in the |stream_user_data|, which nullifies the associated * data pointer. * * It is valid to set the |stream_user_data| to the stream reserved by * PUSH_PROMISE frame. * * This function returns 0 if it succeeds, or one of following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The stream does not exist */ NGHTTP2_EXTERN int nghttp2_session_set_stream_user_data(nghttp2_session *session, int32_t stream_id, void *stream_user_data); /** * @function * * Sets |user_data| to |session|, overwriting the existing user data * specified in `nghttp2_session_client_new()`, or * `nghttp2_session_server_new()`. */ NGHTTP2_EXTERN void nghttp2_session_set_user_data(nghttp2_session *session, void *user_data); /** * @function * * Returns the number of frames in the outbound queue. This does not * include the deferred DATA frames. */ NGHTTP2_EXTERN size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session); /** * @function * * Returns the number of DATA payload in bytes received without * WINDOW_UPDATE transmission for the stream |stream_id|. The local * (receive) window size can be adjusted by * `nghttp2_submit_window_update()`. This function takes into account * that and returns effective data length. In particular, if the * local window size is reduced by submitting negative * window_size_increment with `nghttp2_submit_window_update()`, this * function returns the number of bytes less than actually received. * * This function returns -1 if it fails. */ NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_recv_data_length( nghttp2_session *session, int32_t stream_id); /** * @function * * Returns the local (receive) window size for the stream |stream_id|. * The local window size can be adjusted by * `nghttp2_submit_window_update()`. This function takes into account * that and returns effective window size. * * This function does not take into account the amount of received * data from the remote endpoint. Use * `nghttp2_session_get_stream_local_window_size()` to know the amount * of data the remote endpoint can send without receiving stream level * WINDOW_UPDATE frame. Note that each stream is still subject to the * connection level flow control. * * This function returns -1 if it fails. */ NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_local_window_size( nghttp2_session *session, int32_t stream_id); /** * @function * * Returns the amount of flow-controlled payload (e.g., DATA) that the * remote endpoint can send without receiving stream level * WINDOW_UPDATE frame. It is also subject to the connection level * flow control. So the actual amount of data to send is * min(`nghttp2_session_get_stream_local_window_size()`, * `nghttp2_session_get_local_window_size()`). * * This function returns -1 if it fails. */ NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_local_window_size( nghttp2_session *session, int32_t stream_id); /** * @function * * Returns the number of DATA payload in bytes received without * WINDOW_UPDATE transmission for a connection. The local (receive) * window size can be adjusted by `nghttp2_submit_window_update()`. * This function takes into account that and returns effective data * length. In particular, if the local window size is reduced by * submitting negative window_size_increment with * `nghttp2_submit_window_update()`, this function returns the number * of bytes less than actually received. * * This function returns -1 if it fails. */ NGHTTP2_EXTERN int32_t nghttp2_session_get_effective_recv_data_length(nghttp2_session *session); /** * @function * * Returns the local (receive) window size for a connection. The * local window size can be adjusted by * `nghttp2_submit_window_update()`. This function takes into account * that and returns effective window size. * * This function does not take into account the amount of received * data from the remote endpoint. Use * `nghttp2_session_get_local_window_size()` to know the amount of * data the remote endpoint can send without receiving * connection-level WINDOW_UPDATE frame. Note that each stream is * still subject to the stream level flow control. * * This function returns -1 if it fails. */ NGHTTP2_EXTERN int32_t nghttp2_session_get_effective_local_window_size(nghttp2_session *session); /** * @function * * Returns the amount of flow-controlled payload (e.g., DATA) that the * remote endpoint can send without receiving connection level * WINDOW_UPDATE frame. Note that each stream is still subject to the * stream level flow control (see * `nghttp2_session_get_stream_local_window_size()`). * * This function returns -1 if it fails. */ NGHTTP2_EXTERN int32_t nghttp2_session_get_local_window_size(nghttp2_session *session); /** * @function * * Returns the remote window size for a given stream |stream_id|. * * This is the amount of flow-controlled payload (e.g., DATA) that the * local endpoint can send without stream level WINDOW_UPDATE. There * is also connection level flow control, so the effective size of * payload that the local endpoint can actually send is * min(`nghttp2_session_get_stream_remote_window_size()`, * `nghttp2_session_get_remote_window_size()`). * * This function returns -1 if it fails. */ NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_remote_window_size( nghttp2_session *session, int32_t stream_id); /** * @function * * Returns the remote window size for a connection. * * This function always succeeds. */ NGHTTP2_EXTERN int32_t nghttp2_session_get_remote_window_size(nghttp2_session *session); /** * @function * * Returns 1 if local peer half closed the given stream |stream_id|. * Returns 0 if it did not. Returns -1 if no such stream exists. */ NGHTTP2_EXTERN int nghttp2_session_get_stream_local_close(nghttp2_session *session, int32_t stream_id); /** * @function * * Returns 1 if remote peer half closed the given stream |stream_id|. * Returns 0 if it did not. Returns -1 if no such stream exists. */ NGHTTP2_EXTERN int nghttp2_session_get_stream_remote_close(nghttp2_session *session, int32_t stream_id); /** * @function * * Returns the current dynamic table size of HPACK inflater, including * the overhead 32 bytes per entry described in RFC 7541. */ NGHTTP2_EXTERN size_t nghttp2_session_get_hd_inflate_dynamic_table_size(nghttp2_session *session); /** * @function * * Returns the current dynamic table size of HPACK deflater including * the overhead 32 bytes per entry described in RFC 7541. */ NGHTTP2_EXTERN size_t nghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session); /** * @function * * Signals the session so that the connection should be terminated. * * The last stream ID is the minimum value between the stream ID of a * stream for which :type:`nghttp2_on_frame_recv_callback` was called * most recently and the last stream ID we have sent to the peer * previously. * * The |error_code| is the error code of this GOAWAY frame. The * pre-defined error code is one of :enum:`nghttp2_error_code`. * * After the transmission, both `nghttp2_session_want_read()` and * `nghttp2_session_want_write()` return 0. * * This function should be called when the connection should be * terminated after sending GOAWAY. If the remaining streams should * be processed after GOAWAY, use `nghttp2_submit_goaway()` instead. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ NGHTTP2_EXTERN int nghttp2_session_terminate_session(nghttp2_session *session, uint32_t error_code); /** * @function * * Signals the session so that the connection should be terminated. * * This function behaves like `nghttp2_session_terminate_session()`, * but the last stream ID can be specified by the application for fine * grained control of stream. The HTTP/2 specification does not allow * last_stream_id to be increased. So the actual value sent as * last_stream_id is the minimum value between the given * |last_stream_id| and the last_stream_id we have previously sent to * the peer. * * The |last_stream_id| is peer's stream ID or 0. So if |session| is * initialized as client, |last_stream_id| must be even or 0. If * |session| is initialized as server, |last_stream_id| must be odd or * 0. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |last_stream_id| is invalid. */ NGHTTP2_EXTERN int nghttp2_session_terminate_session2(nghttp2_session *session, int32_t last_stream_id, uint32_t error_code); /** * @function * * Signals to the client that the server started graceful shutdown * procedure. * * This function is only usable for server. If this function is * called with client side session, this function returns * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. * * To gracefully shutdown HTTP/2 session, server should call this * function to send GOAWAY with last_stream_id (1u << 31) - 1. And * after some delay (e.g., 1 RTT), send another GOAWAY with the stream * ID that the server has some processing using * `nghttp2_submit_goaway()`. See also * `nghttp2_session_get_last_proc_stream_id()`. * * Unlike `nghttp2_submit_goaway()`, this function just sends GOAWAY * and does nothing more. This is a mere indication to the client * that session shutdown is imminent. The application should call * `nghttp2_submit_goaway()` with appropriate last_stream_id after * this call. * * If one or more GOAWAY frame have been already sent by either * `nghttp2_submit_goaway()` or `nghttp2_session_terminate_session()`, * this function has no effect. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` * The |session| is initialized as client. */ NGHTTP2_EXTERN int nghttp2_submit_shutdown_notice(nghttp2_session *session); /** * @function * * Returns the value of SETTINGS |id| notified by a remote endpoint. * The |id| must be one of values defined in * :enum:`nghttp2_settings_id`. */ NGHTTP2_EXTERN uint32_t nghttp2_session_get_remote_settings( nghttp2_session *session, nghttp2_settings_id id); /** * @function * * Returns the value of SETTINGS |id| of local endpoint acknowledged * by the remote endpoint. The |id| must be one of the values defined * in :enum:`nghttp2_settings_id`. */ NGHTTP2_EXTERN uint32_t nghttp2_session_get_local_settings( nghttp2_session *session, nghttp2_settings_id id); /** * @function * * Tells the |session| that next stream ID is |next_stream_id|. The * |next_stream_id| must be equal or greater than the value returned * by `nghttp2_session_get_next_stream_id()`. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |next_stream_id| is strictly less than the value * `nghttp2_session_get_next_stream_id()` returns; or * |next_stream_id| is invalid (e.g., even integer for client, or * odd integer for server). */ NGHTTP2_EXTERN int nghttp2_session_set_next_stream_id(nghttp2_session *session, int32_t next_stream_id); /** * @function * * Returns the next outgoing stream ID. Notice that return type is * uint32_t. If we run out of stream ID for this session, this * function returns 1 << 31. */ NGHTTP2_EXTERN uint32_t nghttp2_session_get_next_stream_id(nghttp2_session *session); /** * @function * * Tells the |session| that |size| bytes for a stream denoted by * |stream_id| were consumed by application and are ready to * WINDOW_UPDATE. The consumed bytes are counted towards both * connection and stream level WINDOW_UPDATE (see * `nghttp2_session_consume_connection()` and * `nghttp2_session_consume_stream()` to update consumption * independently). This function is intended to be used without * automatic window update (see * `nghttp2_option_set_no_auto_window_update()`). * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |stream_id| is 0. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` * Automatic WINDOW_UPDATE is not disabled. */ NGHTTP2_EXTERN int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id, size_t size); /** * @function * * Like `nghttp2_session_consume()`, but this only tells library that * |size| bytes were consumed only for connection level. Note that * HTTP/2 maintains connection and stream level flow control windows * independently. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` * Automatic WINDOW_UPDATE is not disabled. */ NGHTTP2_EXTERN int nghttp2_session_consume_connection(nghttp2_session *session, size_t size); /** * @function * * Like `nghttp2_session_consume()`, but this only tells library that * |size| bytes were consumed only for stream denoted by |stream_id|. * Note that HTTP/2 maintains connection and stream level flow control * windows independently. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |stream_id| is 0. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` * Automatic WINDOW_UPDATE is not disabled. */ NGHTTP2_EXTERN int nghttp2_session_consume_stream(nghttp2_session *session, int32_t stream_id, size_t size); /** * @function * * .. warning:: * * Deprecated. :rfc:`7540` priorities are deprecated by * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible * prioritization scheme. * * This function is noop. It always returns 0. */ NGHTTP2_EXTERN int nghttp2_session_change_stream_priority(nghttp2_session *session, int32_t stream_id, const nghttp2_priority_spec *pri_spec); /** * @function * * .. warning:: * * Deprecated. :rfc:`7540` priorities are deprecated by * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible * prioritization scheme. * * This function is noop. It always returns 0. */ NGHTTP2_EXTERN int nghttp2_session_create_idle_stream(nghttp2_session *session, int32_t stream_id, const nghttp2_priority_spec *pri_spec); /** * @function * * .. warning:: * * This function is deprecated in favor of * `nghttp2_session_upgrade2()`, because this function lacks the * parameter to tell the library the request method used in the * original HTTP request. This information is required for client * to validate actual response body length against content-length * header field (see `nghttp2_option_set_no_http_messaging()`). If * HEAD is used in request, the length of response body must be 0 * regardless of value included in content-length header field. * * Performs post-process of HTTP Upgrade request. This function can * be called from both client and server, but the behavior is very * different in each other. * * If called from client side, the |settings_payload| must be the * value sent in ``HTTP2-Settings`` header field and must be decoded * by base64url decoder. The |settings_payloadlen| is the length of * |settings_payload|. The |settings_payload| is unpacked and its * setting values will be submitted using `nghttp2_submit_settings()`. * This means that the client application code does not need to submit * SETTINGS by itself. The stream with stream ID=1 is opened and the * |stream_user_data| is used for its stream_user_data. The opened * stream becomes half-closed (local) state. * * If called from server side, the |settings_payload| must be the * value received in ``HTTP2-Settings`` header field and must be * decoded by base64url decoder. The |settings_payloadlen| is the * length of |settings_payload|. It is treated as if the SETTINGS * frame with that payload is received. Thus, callback functions for * the reception of SETTINGS frame will be invoked. The stream with * stream ID=1 is opened. The |stream_user_data| is ignored. The * opened stream becomes half-closed (remote). * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |settings_payload| is badly formed. * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` * The stream ID 1 is already used or closed; or is not available. */ NGHTTP2_EXTERN int nghttp2_session_upgrade(nghttp2_session *session, const uint8_t *settings_payload, size_t settings_payloadlen, void *stream_user_data); /** * @function * * Performs post-process of HTTP Upgrade request. This function can * be called from both client and server, but the behavior is very * different in each other. * * If called from client side, the |settings_payload| must be the * value sent in ``HTTP2-Settings`` header field and must be decoded * by base64url decoder. The |settings_payloadlen| is the length of * |settings_payload|. The |settings_payload| is unpacked and its * setting values will be submitted using `nghttp2_submit_settings()`. * This means that the client application code does not need to submit * SETTINGS by itself. The stream with stream ID=1 is opened and the * |stream_user_data| is used for its stream_user_data. The opened * stream becomes half-closed (local) state. * * If called from server side, the |settings_payload| must be the * value received in ``HTTP2-Settings`` header field and must be * decoded by base64url decoder. The |settings_payloadlen| is the * length of |settings_payload|. It is treated as if the SETTINGS * frame with that payload is received. Thus, callback functions for * the reception of SETTINGS frame will be invoked. The stream with * stream ID=1 is opened. The |stream_user_data| is ignored. The * opened stream becomes half-closed (remote). * * If the request method is HEAD, pass nonzero value to * |head_request|. Otherwise, pass 0. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |settings_payload| is badly formed. * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` * The stream ID 1 is already used or closed; or is not available. */ NGHTTP2_EXTERN int nghttp2_session_upgrade2(nghttp2_session *session, const uint8_t *settings_payload, size_t settings_payloadlen, int head_request, void *stream_user_data); #ifndef NGHTTP2_NO_SSIZE_T /** * @function * * .. warning:: * * Deprecated. Use `nghttp2_pack_settings_payload2()` instead. * * Serializes the SETTINGS values |iv| in the |buf|. The size of the * |buf| is specified by |buflen|. The number of entries in the |iv| * array is given by |niv|. The required space in |buf| for the |niv| * entries is ``6*niv`` bytes and if the given buffer is too small, an * error is returned. This function is used mainly for creating a * SETTINGS payload to be sent with the ``HTTP2-Settings`` header * field in an HTTP Upgrade request. The data written in |buf| is NOT * base64url encoded and the application is responsible for encoding. * * This function returns the number of bytes written in |buf|, or one * of the following negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |iv| contains duplicate settings ID or invalid value. * * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` * The provided |buflen| size is too small to hold the output. */ NGHTTP2_EXTERN ssize_t nghttp2_pack_settings_payload( uint8_t *buf, size_t buflen, const nghttp2_settings_entry *iv, size_t niv); #endif /* NGHTTP2_NO_SSIZE_T */ /** * @function * * Serializes the SETTINGS values |iv| in the |buf|. The size of the * |buf| is specified by |buflen|. The number of entries in the |iv| * array is given by |niv|. The required space in |buf| for the |niv| * entries is ``6*niv`` bytes and if the given buffer is too small, an * error is returned. This function is used mainly for creating a * SETTINGS payload to be sent with the ``HTTP2-Settings`` header * field in an HTTP Upgrade request. The data written in |buf| is NOT * base64url encoded and the application is responsible for encoding. * * This function returns the number of bytes written in |buf|, or one * of the following negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |iv| contains duplicate settings ID or invalid value. * * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` * The provided |buflen| size is too small to hold the output. */ NGHTTP2_EXTERN nghttp2_ssize nghttp2_pack_settings_payload2( uint8_t *buf, size_t buflen, const nghttp2_settings_entry *iv, size_t niv); /** * @function * * Returns string describing the |lib_error_code|. The * |lib_error_code| must be one of the :enum:`nghttp2_error`. */ NGHTTP2_EXTERN const char *nghttp2_strerror(int lib_error_code); /** * @function * * Returns string representation of HTTP/2 error code |error_code| * (e.g., ``PROTOCOL_ERROR`` is returned if ``error_code == * NGHTTP2_PROTOCOL_ERROR``). If string representation is unknown for * given |error_code|, this function returns string ``unknown``. */ NGHTTP2_EXTERN const char *nghttp2_http2_strerror(uint32_t error_code); /** * @function * * .. warning:: * * Deprecated. :rfc:`7540` priorities are deprecated by * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible * prioritization scheme. * * Initializes |pri_spec| with the |stream_id| of the stream to depend * on with |weight| and its exclusive flag. If |exclusive| is * nonzero, exclusive flag is set. * * The |weight| must be in [:macro:`NGHTTP2_MIN_WEIGHT`, * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. */ NGHTTP2_EXTERN void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, int32_t stream_id, int32_t weight, int exclusive); /** * @function * * .. warning:: * * Deprecated. :rfc:`7540` priorities are deprecated by * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible * prioritization scheme. * * Initializes |pri_spec| with the default values. The default values * are: stream_id = 0, weight = :macro:`NGHTTP2_DEFAULT_WEIGHT` and * exclusive = 0. */ NGHTTP2_EXTERN void nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec); /** * @function * * .. warning:: * * Deprecated. :rfc:`7540` priorities are deprecated by * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible * prioritization scheme. * * Returns nonzero if the |pri_spec| is filled with default values. */ NGHTTP2_EXTERN int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec); #ifndef NGHTTP2_NO_SSIZE_T /** * @function * * .. warning:: * * Deprecated. Use `nghttp2_submit_request2()` instead. * * Submits HEADERS frame and optionally one or more DATA frames. * * The |pri_spec| is ignored. * * The |nva| is an array of name/value pair :type:`nghttp2_nv` with * |nvlen| elements. The application is responsible to include * required pseudo-header fields (header field whose name starts with * ":") in |nva| and must place pseudo-headers before regular header * fields. * * This function creates copies of all name/value pairs in |nva|. It * also lower-cases all names in |nva|. The order of elements in * |nva| is preserved. For header fields with * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, * header field name and value are not copied respectively. With * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application * is responsible to pass header field name in lowercase. The * application should maintain the references to them until * :type:`nghttp2_on_frame_send_callback` or * :type:`nghttp2_on_frame_not_send_callback` is called. * * HTTP/2 specification has requirement about header fields in the * request HEADERS. See the specification for more details. * * If |data_prd| is not ``NULL``, it provides data which will be sent * in subsequent DATA frames. In this case, a method that allows * request message bodies * (https://tools.ietf.org/html/rfc7231#section-4) must be specified * with ``:method`` key in |nva| (e.g. ``POST``). This function does * not take ownership of the |data_prd|. The function copies the * members of the |data_prd|. If |data_prd| is ``NULL``, HEADERS have * END_STREAM set. The |stream_user_data| is data associated to the * stream opened by this request and can be an arbitrary pointer, * which can be retrieved later by * `nghttp2_session_get_stream_user_data()`. * * This function returns assigned stream ID if it succeeds, or one of * the following negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` * No stream ID is available because maximum stream ID was * reached. * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` * The |session| is server session. * * .. warning:: * * This function returns assigned stream ID if it succeeds. But * that stream is not created yet. The application must not submit * frame to that stream ID before * :type:`nghttp2_before_frame_send_callback` is called for this * frame. This means `nghttp2_session_get_stream_user_data()` does * not work before the callback. But * `nghttp2_session_set_stream_user_data()` handles this situation * specially, and it can set data to a stream during this period. * */ NGHTTP2_EXTERN int32_t nghttp2_submit_request( nghttp2_session *session, const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider *data_prd, void *stream_user_data); #endif /* NGHTTP2_NO_SSIZE_T */ /** * @function * * Submits HEADERS frame and optionally one or more DATA frames. * * The |pri_spec| is ignored. * * The |nva| is an array of name/value pair :type:`nghttp2_nv` with * |nvlen| elements. The application is responsible to include * required pseudo-header fields (header field whose name starts with * ":") in |nva| and must place pseudo-headers before regular header * fields. * * This function creates copies of all name/value pairs in |nva|. It * also lower-cases all names in |nva|. The order of elements in * |nva| is preserved. For header fields with * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, * header field name and value are not copied respectively. With * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application * is responsible to pass header field name in lowercase. The * application should maintain the references to them until * :type:`nghttp2_on_frame_send_callback` or * :type:`nghttp2_on_frame_not_send_callback` is called. * * HTTP/2 specification has requirement about header fields in the * request HEADERS. See the specification for more details. * * If |data_prd| is not ``NULL``, it provides data which will be sent * in subsequent DATA frames. In this case, a method that allows * request message bodies * (https://tools.ietf.org/html/rfc7231#section-4) must be specified * with ``:method`` key in |nva| (e.g. ``POST``). This function does * not take ownership of the |data_prd|. The function copies the * members of the |data_prd|. If |data_prd| is ``NULL``, HEADERS have * END_STREAM set. The |stream_user_data| is data associated to the * stream opened by this request and can be an arbitrary pointer, * which can be retrieved later by * `nghttp2_session_get_stream_user_data()`. * * This function returns assigned stream ID if it succeeds, or one of * the following negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` * No stream ID is available because maximum stream ID was * reached. * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` * The |session| is server session. * * .. warning:: * * This function returns assigned stream ID if it succeeds. But * that stream is not created yet. The application must not submit * frame to that stream ID before * :type:`nghttp2_before_frame_send_callback` is called for this * frame. This means `nghttp2_session_get_stream_user_data()` does * not work before the callback. But * `nghttp2_session_set_stream_user_data()` handles this situation * specially, and it can set data to a stream during this period. * */ NGHTTP2_EXTERN int32_t nghttp2_submit_request2( nghttp2_session *session, const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider2 *data_prd, void *stream_user_data); #ifndef NGHTTP2_NO_SSIZE_T /** * @function * * .. warning:: * * Deprecated. Use `nghttp2_submit_response2()` instead. * * Submits response HEADERS frame and optionally one or more DATA * frames against the stream |stream_id|. * * The |nva| is an array of name/value pair :type:`nghttp2_nv` with * |nvlen| elements. The application is responsible to include * required pseudo-header fields (header field whose name starts with * ":") in |nva| and must place pseudo-headers before regular header * fields. * * This function creates copies of all name/value pairs in |nva|. It * also lower-cases all names in |nva|. The order of elements in * |nva| is preserved. For header fields with * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, * header field name and value are not copied respectively. With * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application * is responsible to pass header field name in lowercase. The * application should maintain the references to them until * :type:`nghttp2_on_frame_send_callback` or * :type:`nghttp2_on_frame_not_send_callback` is called. * * HTTP/2 specification has requirement about header fields in the * response HEADERS. See the specification for more details. * * If |data_prd| is not ``NULL``, it provides data which will be sent * in subsequent DATA frames. This function does not take ownership * of the |data_prd|. The function copies the members of the * |data_prd|. If |data_prd| is ``NULL``, HEADERS will have * END_STREAM flag set. * * This method can be used as normal HTTP response and push response. * When pushing a resource using this function, the |session| must be * configured using `nghttp2_session_server_new()` or its variants and * the target stream denoted by the |stream_id| must be reserved using * `nghttp2_submit_push_promise()`. * * To send non-final response headers (e.g., HTTP status 101), don't * use this function because this function half-closes the outbound * stream. Instead, use `nghttp2_submit_headers()` for this purpose. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |stream_id| is 0. * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` * DATA or HEADERS has been already submitted and not fully * processed yet. Normally, this does not happen, but when * application wrongly calls `nghttp2_submit_response()` twice, * this may happen. * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` * The |session| is client session. * * .. warning:: * * Calling this function twice for the same stream ID may lead to * program crash. It is generally considered to a programming error * to commit response twice. */ NGHTTP2_EXTERN int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider *data_prd); #endif /* NGHTTP2_NO_SSIZE_T */ /** * @function * * Submits response HEADERS frame and optionally one or more DATA * frames against the stream |stream_id|. * * The |nva| is an array of name/value pair :type:`nghttp2_nv` with * |nvlen| elements. The application is responsible to include * required pseudo-header fields (header field whose name starts with * ":") in |nva| and must place pseudo-headers before regular header * fields. * * This function creates copies of all name/value pairs in |nva|. It * also lower-cases all names in |nva|. The order of elements in * |nva| is preserved. For header fields with * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, * header field name and value are not copied respectively. With * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application * is responsible to pass header field name in lowercase. The * application should maintain the references to them until * :type:`nghttp2_on_frame_send_callback` or * :type:`nghttp2_on_frame_not_send_callback` is called. * * HTTP/2 specification has requirement about header fields in the * response HEADERS. See the specification for more details. * * If |data_prd| is not ``NULL``, it provides data which will be sent * in subsequent DATA frames. This function does not take ownership * of the |data_prd|. The function copies the members of the * |data_prd|. If |data_prd| is ``NULL``, HEADERS will have * END_STREAM flag set. * * This method can be used as normal HTTP response and push response. * When pushing a resource using this function, the |session| must be * configured using `nghttp2_session_server_new()` or its variants and * the target stream denoted by the |stream_id| must be reserved using * `nghttp2_submit_push_promise()`. * * To send non-final response headers (e.g., HTTP status 101), don't * use this function because this function half-closes the outbound * stream. Instead, use `nghttp2_submit_headers()` for this purpose. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |stream_id| is 0. * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` * DATA or HEADERS has been already submitted and not fully * processed yet. Normally, this does not happen, but when * application wrongly calls `nghttp2_submit_response2()` twice, * this may happen. * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` * The |session| is client session. * * .. warning:: * * Calling this function twice for the same stream ID may lead to * program crash. It is generally considered to a programming error * to commit response twice. */ NGHTTP2_EXTERN int nghttp2_submit_response2(nghttp2_session *session, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider2 *data_prd); /** * @function * * Submits trailer fields HEADERS against the stream |stream_id|. * * The |nva| is an array of name/value pair :type:`nghttp2_nv` with * |nvlen| elements. The application must not include pseudo-header * fields (headers whose names starts with ":") in |nva|. * * This function creates copies of all name/value pairs in |nva|. It * also lower-cases all names in |nva|. The order of elements in * |nva| is preserved. For header fields with * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, * header field name and value are not copied respectively. With * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application * is responsible to pass header field name in lowercase. The * application should maintain the references to them until * :type:`nghttp2_on_frame_send_callback` or * :type:`nghttp2_on_frame_not_send_callback` is called. * * For server, trailer fields must follow response HEADERS or response * DATA without END_STREAM flat set. The library does not enforce * this requirement, and applications should do this for themselves. * If `nghttp2_submit_trailer()` is called before any response HEADERS * submission (usually by `nghttp2_submit_response2()`), the content * of |nva| will be sent as response headers, which will result in * error. * * This function has the same effect with `nghttp2_submit_headers()`, * with flags = :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` and both * pri_spec and stream_user_data to NULL. * * To submit trailer fields after `nghttp2_submit_response2()` is * called, the application has to specify * :type:`nghttp2_data_provider2` to `nghttp2_submit_response2()`. * Inside of :type:`nghttp2_data_source_read_callback2`, when setting * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF`, also set * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_END_STREAM`. After * that, the application can send trailer fields using * `nghttp2_submit_trailer()`. `nghttp2_submit_trailer()` can be used * inside :type:`nghttp2_data_source_read_callback2`. * * This function returns 0 if it succeeds and |stream_id| is -1. * Otherwise, this function returns 0 if it succeeds, or one of the * following negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |stream_id| is 0. */ NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen); /** * @function * * Submits HEADERS frame. The |flags| is bitwise OR of the * following values: * * * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` * * If |flags| includes :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM`, * this frame has END_STREAM flag set. * * The library handles the CONTINUATION frame internally and it * correctly sets END_HEADERS to the last sequence of the PUSH_PROMISE * or CONTINUATION frame. * * If the |stream_id| is -1, this frame is assumed as request (i.e., * request HEADERS frame which opens new stream). In this case, the * assigned stream ID will be returned. Otherwise, specify stream ID * in |stream_id|. * * The |pri_spec| is ignored. * * The |nva| is an array of name/value pair :type:`nghttp2_nv` with * |nvlen| elements. The application is responsible to include * required pseudo-header fields (header field whose name starts with * ":") in |nva| and must place pseudo-headers before regular header * fields. * * This function creates copies of all name/value pairs in |nva|. It * also lower-cases all names in |nva|. The order of elements in * |nva| is preserved. For header fields with * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, * header field name and value are not copied respectively. With * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application * is responsible to pass header field name in lowercase. The * application should maintain the references to them until * :type:`nghttp2_on_frame_send_callback` or * :type:`nghttp2_on_frame_not_send_callback` is called. * * The |stream_user_data| is a pointer to an arbitrary data which is * associated to the stream this frame will open. Therefore it is * only used if this frame opens streams, in other words, it changes * stream state from idle or reserved to open. * * This function is low-level in a sense that the application code can * specify flags directly. For usual HTTP request, * `nghttp2_submit_request2()` is useful. Likewise, for HTTP * response, prefer `nghttp2_submit_response2()`. * * This function returns newly assigned stream ID if it succeeds and * |stream_id| is -1. Otherwise, this function returns 0 if it * succeeds, or one of the following negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` * No stream ID is available because maximum stream ID was * reached. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |stream_id| is 0. * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` * DATA or HEADERS has been already submitted and not fully * processed yet. This happens if stream denoted by |stream_id| * is in reserved state. * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` * The |stream_id| is -1, and |session| is server session. * * .. warning:: * * This function returns assigned stream ID if it succeeds and * |stream_id| is -1. But that stream is not opened yet. The * application must not submit frame to that stream ID before * :type:`nghttp2_before_frame_send_callback` is called for this * frame. * */ NGHTTP2_EXTERN int32_t nghttp2_submit_headers( nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen, void *stream_user_data); #ifndef NGHTTP2_NO_SSIZE_T /** * @function * * .. warning:: * * Deprecated. Use `nghttp2_submit_data2()` instead. * * Submits one or more DATA frames to the stream |stream_id|. The * data to be sent are provided by |data_prd|. If |flags| contains * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM`, the last DATA frame * has END_STREAM flag set. * * This function does not take ownership of the |data_prd|. The * function copies the members of the |data_prd|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` * DATA or HEADERS has been already submitted and not fully * processed yet. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |stream_id| is 0. * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_CLOSED` * The stream was already closed; or the |stream_id| is invalid. * * .. note:: * * Currently, only one DATA or HEADERS is allowed for a stream at a * time. Submitting these frames more than once before first DATA * or HEADERS is finished results in * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` error code. The * earliest callback which tells that previous frame is done is * :type:`nghttp2_on_frame_send_callback`. In side that callback, * new data can be submitted using `nghttp2_submit_data()`. Of * course, all data except for last one must not have * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` flag set in |flags|. * This sounds a bit complicated, and we recommend to use * `nghttp2_submit_request()` and `nghttp2_submit_response()` to * avoid this cascading issue. The experience shows that for HTTP * use, these two functions are enough to implement both client and * server. */ NGHTTP2_EXTERN int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_data_provider *data_prd); #endif /* NGHTTP2_NO_SSIZE_T */ /** * @function * * Submits one or more DATA frames to the stream |stream_id|. The * data to be sent are provided by |data_prd|. If |flags| contains * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM`, the last DATA frame * has END_STREAM flag set. * * This function does not take ownership of the |data_prd|. The * function copies the members of the |data_prd|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` * DATA or HEADERS has been already submitted and not fully * processed yet. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |stream_id| is 0. * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_CLOSED` * The stream was already closed; or the |stream_id| is invalid. * * .. note:: * * Currently, only one DATA or HEADERS is allowed for a stream at a * time. Submitting these frames more than once before first DATA * or HEADERS is finished results in * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` error code. The * earliest callback which tells that previous frame is done is * :type:`nghttp2_on_frame_send_callback`. In side that callback, * new data can be submitted using `nghttp2_submit_data2()`. Of * course, all data except for last one must not have * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` flag set in |flags|. * This sounds a bit complicated, and we recommend to use * `nghttp2_submit_request2()` and `nghttp2_submit_response2()` to * avoid this cascading issue. The experience shows that for HTTP * use, these two functions are enough to implement both client and * server. */ NGHTTP2_EXTERN int nghttp2_submit_data2(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_data_provider2 *data_prd); /** * @function * * .. warning:: * * Deprecated. :rfc:`7540` priorities are deprecated by * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible * prioritization scheme. * * This function is noop. It always returns 0. */ NGHTTP2_EXTERN int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_priority_spec *pri_spec); /** * @macro * * :macro:`NGHTTP2_EXTPRI_DEFAULT_URGENCY` is the default urgency * level for :rfc:`9218` extensible priorities. */ #define NGHTTP2_EXTPRI_DEFAULT_URGENCY 3 /** * @macro * * :macro:`NGHTTP2_EXTPRI_URGENCY_HIGH` is the highest urgency level * for :rfc:`9218` extensible priorities. */ #define NGHTTP2_EXTPRI_URGENCY_HIGH 0 /** * @macro * * :macro:`NGHTTP2_EXTPRI_URGENCY_LOW` is the lowest urgency level for * :rfc:`9218` extensible priorities. */ #define NGHTTP2_EXTPRI_URGENCY_LOW 7 /** * @macro * * :macro:`NGHTTP2_EXTPRI_URGENCY_LEVELS` is the number of urgency * levels for :rfc:`9218` extensible priorities. */ #define NGHTTP2_EXTPRI_URGENCY_LEVELS (NGHTTP2_EXTPRI_URGENCY_LOW + 1) /** * @struct * * :type:`nghttp2_extpri` is :rfc:`9218` extensible priorities * specification for a stream. */ typedef struct nghttp2_extpri { /** * :member:`urgency` is the urgency of a stream, it must be in * [:macro:`NGHTTP2_EXTPRI_URGENCY_HIGH`, * :macro:`NGHTTP2_EXTPRI_URGENCY_LOW`], inclusive, and 0 is the * highest urgency. */ uint32_t urgency; /** * :member:`inc` indicates that a content can be processed * incrementally or not. If inc is 0, it cannot be processed * incrementally. If inc is 1, it can be processed incrementally. * Other value is not permitted. */ int inc; } nghttp2_extpri; /** * @function * * Submits RST_STREAM frame to cancel/reject the stream |stream_id| * with the error code |error_code|. * * The pre-defined error code is one of :enum:`nghttp2_error_code`. * * The |flags| is currently ignored and should be * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |stream_id| is 0. */ NGHTTP2_EXTERN int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags, int32_t stream_id, uint32_t error_code); /** * @function * * Stores local settings and submits SETTINGS frame. The |iv| is the * pointer to the array of :type:`nghttp2_settings_entry`. The |niv| * indicates the number of :type:`nghttp2_settings_entry`. * * The |flags| is currently ignored and should be * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. * * This function does not take ownership of the |iv|. This function * copies all the elements in the |iv|. * * While updating individual stream's local window size, if the window * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, * RST_STREAM is issued against such a stream. * * SETTINGS with :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` is * automatically submitted by the library and application could not * send it at its will. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |iv| contains invalid value (e.g., initial window size * strictly greater than (1 << 31) - 1. * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ NGHTTP2_EXTERN int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags, const nghttp2_settings_entry *iv, size_t niv); /** * @function * * Submits PUSH_PROMISE frame. * * The |flags| is currently ignored. The library handles the * CONTINUATION frame internally and it correctly sets END_HEADERS to * the last sequence of the PUSH_PROMISE or CONTINUATION frame. * * The |stream_id| must be client initiated stream ID. * * The |nva| is an array of name/value pair :type:`nghttp2_nv` with * |nvlen| elements. The application is responsible to include * required pseudo-header fields (header field whose name starts with * ":") in |nva| and must place pseudo-headers before regular header * fields. * * This function creates copies of all name/value pairs in |nva|. It * also lower-cases all names in |nva|. The order of elements in * |nva| is preserved. For header fields with * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, * header field name and value are not copied respectively. With * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application * is responsible to pass header field name in lowercase. The * application should maintain the references to them until * :type:`nghttp2_on_frame_send_callback` or * :type:`nghttp2_on_frame_not_send_callback` is called. * * The |promised_stream_user_data| is a pointer to an arbitrary data * which is associated to the promised stream this frame will open and * make it in reserved state. It is available using * `nghttp2_session_get_stream_user_data()`. The application can * access it in :type:`nghttp2_before_frame_send_callback` and * :type:`nghttp2_on_frame_send_callback` of this frame. * * The client side is not allowed to use this function. * * To submit response headers and data, use * `nghttp2_submit_response2()`. * * This function returns assigned promised stream ID if it succeeds, * or one of the following negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` * This function was invoked when |session| is initialized as * client. * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` * No stream ID is available because maximum stream ID was * reached. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |stream_id| is 0; The |stream_id| does not designate stream * that peer initiated. * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_CLOSED` * The stream was already closed; or the |stream_id| is invalid. * * .. warning:: * * This function returns assigned promised stream ID if it succeeds. * As of 1.16.0, stream object for pushed resource is created when * this function succeeds. In that case, the application can submit * push response for the promised frame. * * In 1.15.0 or prior versions, pushed stream is not opened yet when * this function succeeds. The application must not submit frame to * that stream ID before :type:`nghttp2_before_frame_send_callback` * is called for this frame. * */ NGHTTP2_EXTERN int32_t nghttp2_submit_push_promise( nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen, void *promised_stream_user_data); /** * @function * * Submits PING frame. You don't have to send PING back when you * received PING frame. The library automatically submits PING frame * in this case. * * The |flags| is bitwise OR of 0 or more of the following value. * * * :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` * * Unless `nghttp2_option_set_no_auto_ping_ack()` is used, the |flags| * should be :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. * * If the |opaque_data| is non ``NULL``, then it should point to the 8 * bytes array of memory to specify opaque data to send with PING * frame. If the |opaque_data| is ``NULL``, zero-cleared 8 bytes will * be sent as opaque data. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ NGHTTP2_EXTERN int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, const uint8_t *opaque_data); /** * @function * * Submits GOAWAY frame with the last stream ID |last_stream_id| and * the error code |error_code|. * * The pre-defined error code is one of :enum:`nghttp2_error_code`. * * The |flags| is currently ignored and should be * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. * * The |last_stream_id| is peer's stream ID or 0. So if |session| is * initialized as client, |last_stream_id| must be even or 0. If * |session| is initialized as server, |last_stream_id| must be odd or * 0. * * The HTTP/2 specification says last_stream_id must not be increased * from the value previously sent. So the actual value sent as * last_stream_id is the minimum value between the given * |last_stream_id| and the last_stream_id previously sent to the * peer. * * If the |opaque_data| is not ``NULL`` and |opaque_data_len| is not * zero, those data will be sent as additional debug data. The * library makes a copy of the memory region pointed by |opaque_data| * with the length |opaque_data_len|, so the caller does not need to * keep this memory after the return of this function. If the * |opaque_data_len| is 0, the |opaque_data| could be ``NULL``. * * After successful transmission of GOAWAY, following things happen. * All incoming streams having strictly more than |last_stream_id| are * closed. All incoming HEADERS which starts new stream are simply * ignored. After all active streams are handled, both * `nghttp2_session_want_read()` and `nghttp2_session_want_write()` * return 0 and the application can close session. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |opaque_data_len| is too large; the |last_stream_id| is * invalid. */ NGHTTP2_EXTERN int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags, int32_t last_stream_id, uint32_t error_code, const uint8_t *opaque_data, size_t opaque_data_len); /** * @function * * Returns the last stream ID of a stream for which * :type:`nghttp2_on_frame_recv_callback` was invoked most recently. * The returned value can be used as last_stream_id parameter for * `nghttp2_submit_goaway()` and * `nghttp2_session_terminate_session2()`. * * This function always succeeds. */ NGHTTP2_EXTERN int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session); /** * @function * * Returns nonzero if new request can be sent from local endpoint. * * This function return 0 if request is not allowed for this session. * There are several reasons why request is not allowed. Some of the * reasons are: session is server; stream ID has been spent; GOAWAY * has been sent or received. * * The application can call `nghttp2_submit_request2()` without * consulting this function. In that case, * `nghttp2_submit_request2()` may return error. Or, request is * failed to sent, and :type:`nghttp2_on_stream_close_callback` is * called. */ NGHTTP2_EXTERN int nghttp2_session_check_request_allowed(nghttp2_session *session); /** * @function * * Returns nonzero if |session| is initialized as server side session. */ NGHTTP2_EXTERN int nghttp2_session_check_server_session(nghttp2_session *session); /** * @function * * Submits WINDOW_UPDATE frame. * * The |flags| is currently ignored and should be * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. * * The |stream_id| is the stream ID to send this WINDOW_UPDATE. To * send connection level WINDOW_UPDATE, specify 0 to |stream_id|. * * If the |window_size_increment| is positive, the WINDOW_UPDATE with * that value as window_size_increment is queued. If the * |window_size_increment| is larger than the received bytes from the * remote endpoint, the local window size is increased by that * difference. If the sole purpose is to increase the local window * size, consider to use `nghttp2_session_set_local_window_size()`. * * If the |window_size_increment| is negative, the local window size * is decreased by -|window_size_increment|. If automatic * WINDOW_UPDATE is enabled * (`nghttp2_option_set_no_auto_window_update()`), and the library * decided that the WINDOW_UPDATE should be submitted, then * WINDOW_UPDATE is queued with the current received bytes count. If * the sole purpose is to decrease the local window size, consider to * use `nghttp2_session_set_local_window_size()`. * * If the |window_size_increment| is 0, the function does nothing and * returns 0. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_FLOW_CONTROL` * The local window size overflow or gets negative. * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ NGHTTP2_EXTERN int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags, int32_t stream_id, int32_t window_size_increment); /** * @function * * Set local window size (local endpoints's window size) to the given * |window_size| for the given stream denoted by |stream_id|. To * change connection level window size, specify 0 to |stream_id|. To * increase window size, this function may submit WINDOW_UPDATE frame * to transmission queue. * * The |flags| is currently ignored and should be * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. * * This sounds similar to `nghttp2_submit_window_update()`, but there * are 2 differences. The first difference is that this function * takes the absolute value of window size to set, rather than the * delta. To change the window size, this may be easier to use since * the application just declares the intended window size, rather than * calculating delta. The second difference is that * `nghttp2_submit_window_update()` affects the received bytes count * which has not acked yet. By the specification of * `nghttp2_submit_window_update()`, to strictly increase the local * window size, we have to submit delta including all received bytes * count, which might not be desirable in some cases. On the other * hand, this function does not affect the received bytes count. It * just sets the local window size to the given value. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |stream_id| is negative. * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ NGHTTP2_EXTERN int nghttp2_session_set_local_window_size(nghttp2_session *session, uint8_t flags, int32_t stream_id, int32_t window_size); /** * @function * * Submits extension frame. * * Application can pass arbitrary frame flags and stream ID in |flags| * and |stream_id| respectively. The |payload| is opaque pointer, and * it can be accessible though ``frame->ext.payload`` in * :type:`nghttp2_pack_extension_callback2`. The library will not own * passed |payload| pointer. * * The application must set :type:`nghttp2_pack_extension_callback2` * using `nghttp2_session_callbacks_set_pack_extension_callback2()`. * * The application should retain the memory pointed by |payload| until * the transmission of extension frame is done (which is indicated by * :type:`nghttp2_on_frame_send_callback`), or transmission fails * (which is indicated by :type:`nghttp2_on_frame_not_send_callback`). * If application does not touch this memory region after packing it * into a wire format, application can free it inside * :type:`nghttp2_pack_extension_callback2`. * * The standard HTTP/2 frame cannot be sent with this function, so * |type| must be strictly grater than 0x9. Otherwise, this function * will fail with error code * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` * If :type:`nghttp2_pack_extension_callback2` is not set. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * If |type| specifies standard HTTP/2 frame type. The frame * types in the rage [0x0, 0x9], both inclusive, are standard * HTTP/2 frame type, and cannot be sent using this function. * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory */ NGHTTP2_EXTERN int nghttp2_submit_extension(nghttp2_session *session, uint8_t type, uint8_t flags, int32_t stream_id, void *payload); /** * @struct * * The payload of ALTSVC frame. ALTSVC frame is a non-critical * extension to HTTP/2. If this frame is received, and * `nghttp2_option_set_user_recv_extension_type()` is not set, and * `nghttp2_option_set_builtin_recv_extension_type()` is set for * :enum:`nghttp2_frame_type.NGHTTP2_ALTSVC`, * ``nghttp2_extension.payload`` will point to this struct. * * It has the following members: */ typedef struct { /** * The pointer to origin which this alternative service is * associated with. This is not necessarily NULL-terminated. */ uint8_t *origin; /** * The length of the |origin|. */ size_t origin_len; /** * The pointer to Alt-Svc field value contained in ALTSVC frame. * This is not necessarily NULL-terminated. */ uint8_t *field_value; /** * The length of the |field_value|. */ size_t field_value_len; } nghttp2_ext_altsvc; /** * @function * * Submits ALTSVC frame. * * ALTSVC frame is a non-critical extension to HTTP/2, and defined in * `RFC 7383 `_. * * The |flags| is currently ignored and should be * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. * * The |origin| points to the origin this alternative service is * associated with. The |origin_len| is the length of the origin. If * |stream_id| is 0, the origin must be specified. If |stream_id| is * not zero, the origin must be empty (in other words, |origin_len| * must be 0). * * The ALTSVC frame is only usable from server side. If this function * is invoked with client side session, this function returns * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` * The function is called from client side session * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The sum of |origin_len| and |field_value_len| is larger than * 16382; or |origin_len| is 0 while |stream_id| is 0; or * |origin_len| is not 0 while |stream_id| is not 0. */ NGHTTP2_EXTERN int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *origin, size_t origin_len, const uint8_t *field_value, size_t field_value_len); /** * @struct * * The single entry of an origin. */ typedef struct { /** * The pointer to origin. No validation is made against this field * by the library. This is not necessarily NULL-terminated. */ uint8_t *origin; /** * The length of the |origin|. */ size_t origin_len; } nghttp2_origin_entry; /** * @struct * * The payload of ORIGIN frame. ORIGIN frame is a non-critical * extension to HTTP/2 and defined by `RFC 8336 * `_. * * If this frame is received, and * `nghttp2_option_set_user_recv_extension_type()` is not set, and * `nghttp2_option_set_builtin_recv_extension_type()` is set for * :enum:`nghttp2_frame_type.NGHTTP2_ORIGIN`, * ``nghttp2_extension.payload`` will point to this struct. * * It has the following members: */ typedef struct { /** * The number of origins contained in |ov|. */ size_t nov; /** * The pointer to the array of origins contained in ORIGIN frame. */ nghttp2_origin_entry *ov; } nghttp2_ext_origin; /** * @function * * Submits ORIGIN frame. * * ORIGIN frame is a non-critical extension to HTTP/2 and defined by * `RFC 8336 `_. * * The |flags| is currently ignored and should be * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. * * The |ov| points to the array of origins. The |nov| specifies the * number of origins included in |ov|. This function creates copies * of all elements in |ov|. * * The ORIGIN frame is only usable by a server. If this function is * invoked with client side session, this function returns * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` * The function is called from client side session. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * There are too many origins, or an origin is too large to fit * into a default frame payload. */ NGHTTP2_EXTERN int nghttp2_submit_origin(nghttp2_session *session, uint8_t flags, const nghttp2_origin_entry *ov, size_t nov); /** * @struct * * The payload of PRIORITY_UPDATE frame. PRIORITY_UPDATE frame is a * non-critical extension to HTTP/2. If this frame is received, and * `nghttp2_option_set_user_recv_extension_type()` is not set, and * `nghttp2_option_set_builtin_recv_extension_type()` is set for * :enum:`nghttp2_frame_type.NGHTTP2_PRIORITY_UPDATE`, * ``nghttp2_extension.payload`` will point to this struct. * * It has the following members: */ typedef struct { /** * The stream ID of the stream whose priority is updated. */ int32_t stream_id; /** * The pointer to Priority field value. It is not necessarily * NULL-terminated. */ uint8_t *field_value; /** * The length of the :member:`field_value`. */ size_t field_value_len; } nghttp2_ext_priority_update; /** * @function * * Submits PRIORITY_UPDATE frame. * * PRIORITY_UPDATE frame is a non-critical extension to HTTP/2, and * defined in :rfc:`9218#section-7.1`. * * The |flags| is currently ignored and should be * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. * * The |stream_id| is the ID of stream which is prioritized. The * |field_value| points to the Priority field value. The * |field_value_len| is the length of the Priority field value. * * If this function is called by server, * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` is returned. * * If * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` * of value of 0 is received by a remote endpoint (or it is omitted), * this function does nothing and returns 0. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` * The function is called from server side session * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |field_value_len| is larger than 16380; or |stream_id| is * 0. */ NGHTTP2_EXTERN int nghttp2_submit_priority_update(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *field_value, size_t field_value_len); /** * @function * * Changes the priority of the existing stream denoted by |stream_id|. * The new priority is |extpri|. This function is meant to be used by * server for :rfc:`9218` extensible prioritization scheme. * * If |session| is initialized as client, this function returns * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. For client, use * `nghttp2_submit_priority_update()` instead. * * If :member:`extpri->urgency ` is out of * bound, it is set to :macro:`NGHTTP2_EXTPRI_URGENCY_LOW`. * * If |ignore_client_signal| is nonzero, server starts to ignore * client priority signals for this stream. * * If * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` * of value of 1 is not submitted via `nghttp2_submit_settings()`, * this function does nothing and returns 0. * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` * The |session| is initialized as client. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * |stream_id| is zero; or a stream denoted by |stream_id| is not * found. */ NGHTTP2_EXTERN int nghttp2_session_change_extpri_stream_priority( nghttp2_session *session, int32_t stream_id, const nghttp2_extpri *extpri, int ignore_client_signal); /** * @function * * Stores the stream priority of the existing stream denoted by * |stream_id| in the object pointed by |extpri|. This function is * meant to be used by server for :rfc:`9218` extensible * prioritization scheme. * * If |session| is initialized as client, this function returns * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. * * If * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` * of value of 1 is not submitted via `nghttp2_submit_settings()`, * this function does nothing and returns 0. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` * The |session| is initialized as client. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * |stream_id| is zero; or a stream denoted by |stream_id| is not * found. */ NGHTTP2_EXTERN int nghttp2_session_get_extpri_stream_priority( nghttp2_session *session, nghttp2_extpri *extpri, int32_t stream_id); /** * @function * * Parses Priority header field value pointed by |value| of length * |len|, and stores the result in the object pointed by |extpri|. * Priority header field is defined in :rfc:`9218`. * * This function does not initialize the object pointed by |extpri| * before storing the result. It only assigns the values that the * parser correctly extracted to fields. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * Failed to parse the header field value. */ NGHTTP2_EXTERN int nghttp2_extpri_parse_priority(nghttp2_extpri *extpri, const uint8_t *value, size_t len); /** * @function * * Compares ``lhs->name`` of length ``lhs->namelen`` bytes and * ``rhs->name`` of length ``rhs->namelen`` bytes. Returns negative * integer if ``lhs->name`` is found to be less than ``rhs->name``; or * returns positive integer if ``lhs->name`` is found to be greater * than ``rhs->name``; or returns 0 otherwise. */ NGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs); /** * @function * * .. warning:: * * Deprecated. Use `nghttp2_select_alpn` instead. * * A helper function for dealing with ALPN in server side. The |in| * contains peer's protocol list in preferable order. The format of * |in| is length-prefixed and not null-terminated. For example, * ``h2`` and ``http/1.1`` stored in |in| like this:: * * in[0] = 2 * in[1..2] = "h2" * in[3] = 8 * in[4..11] = "http/1.1" * inlen = 12 * * The selection algorithm is as follows: * * 1. If peer's list contains HTTP/2 protocol the library supports, * it is selected and returns 1. The following step is not taken. * * 2. If peer's list contains ``http/1.1``, this function selects * ``http/1.1`` and returns 0. The following step is not taken. * * 3. This function selects nothing and returns -1 (So called * non-overlap case). In this case, |out| and |outlen| are left * untouched. * * Selecting ``h2`` means that ``h2`` is written into |*out| and its * length (which is 2) is assigned to |*outlen|. * * For ALPN, refer to https://tools.ietf.org/html/rfc7301 * * To use this method you should do something like:: * * static int alpn_select_proto_cb(SSL* ssl, * const unsigned char **out, * unsigned char *outlen, * const unsigned char *in, * unsigned int inlen, * void *arg) * { * int rv; * rv = nghttp2_select_next_protocol((unsigned char**)out, outlen, * in, inlen); * if (rv == -1) { * return SSL_TLSEXT_ERR_NOACK; * } * if (rv == 1) { * ((MyType*)arg)->http2_selected = 1; * } * return SSL_TLSEXT_ERR_OK; * } * ... * SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, my_obj); * */ NGHTTP2_EXTERN int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen); /** * @function * * A helper function for dealing with ALPN in server side. The |in| * contains peer's protocol list in preferable order. The format of * |in| is length-prefixed and not null-terminated. For example, * ``h2`` and ``http/1.1`` stored in |in| like this:: * * in[0] = 2 * in[1..2] = "h2" * in[3] = 8 * in[4..11] = "http/1.1" * inlen = 12 * * The selection algorithm is as follows: * * 1. If peer's list contains HTTP/2 protocol the library supports, * it is selected and returns 1. The following step is not taken. * * 2. If peer's list contains ``http/1.1``, this function selects * ``http/1.1`` and returns 0. The following step is not taken. * * 3. This function selects nothing and returns -1 (So called * non-overlap case). In this case, |out| and |outlen| are left * untouched. * * Selecting ``h2`` means that ``h2`` is written into |*out| and its * length (which is 2) is assigned to |*outlen|. * * For ALPN, refer to https://tools.ietf.org/html/rfc7301 * * To use this method you should do something like:: * * static int alpn_select_proto_cb(SSL* ssl, * const unsigned char **out, * unsigned char *outlen, * const unsigned char *in, * unsigned int inlen, * void *arg) * { * int rv; * rv = nghttp2_select_alpn(out, outlen, in, inlen); * if (rv == -1) { * return SSL_TLSEXT_ERR_NOACK; * } * if (rv == 1) { * ((MyType*)arg)->http2_selected = 1; * } * return SSL_TLSEXT_ERR_OK; * } * ... * SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, my_obj); * */ NGHTTP2_EXTERN int nghttp2_select_alpn(const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen); /** * @function * * Returns a pointer to a nghttp2_info struct with version information * about the run-time library in use. The |least_version| argument * can be set to a 24 bit numerical value for the least accepted * version number and if the condition is not met, this function will * return a ``NULL``. Pass in 0 to skip the version checking. */ NGHTTP2_EXTERN nghttp2_info *nghttp2_version(int least_version); /** * @function * * Returns nonzero if the :type:`nghttp2_error` library error code * |lib_error| is fatal. */ NGHTTP2_EXTERN int nghttp2_is_fatal(int lib_error_code); /** * @function * * Returns nonzero if HTTP header field name |name| of length |len| is * valid according to http://tools.ietf.org/html/rfc7230#section-3.2 * * Because this is a header field name in HTTP2, the upper cased alphabet * is treated as error. */ NGHTTP2_EXTERN int nghttp2_check_header_name(const uint8_t *name, size_t len); /** * @function * * Returns nonzero if HTTP header field value |value| of length |len| * is valid according to * http://tools.ietf.org/html/rfc7230#section-3.2 * * This function is considered obsolete, and application should * consider to use `nghttp2_check_header_value_rfc9113()` instead. */ NGHTTP2_EXTERN int nghttp2_check_header_value(const uint8_t *value, size_t len); /** * @function * * Returns nonzero if HTTP header field value |value| of length |len| * is valid according to * http://tools.ietf.org/html/rfc7230#section-3.2, plus * https://datatracker.ietf.org/doc/html/rfc9113#section-8.2.1 */ NGHTTP2_EXTERN int nghttp2_check_header_value_rfc9113(const uint8_t *value, size_t len); /** * @function * * Returns nonzero if the |value| which is supposed to be the value of * the :method header field is valid according to * https://datatracker.ietf.org/doc/html/rfc7231#section-4 and * https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6 */ NGHTTP2_EXTERN int nghttp2_check_method(const uint8_t *value, size_t len); /** * @function * * Returns nonzero if the |value| which is supposed to be the value of * the :path header field is valid according to * https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.3 * * |value| is valid if it merely consists of the allowed characters. * In particular, it does not check whether |value| follows the syntax * of path. The allowed characters are all characters valid by * `nghttp2_check_header_value` minus SPC and HT. */ NGHTTP2_EXTERN int nghttp2_check_path(const uint8_t *value, size_t len); /** * @function * * Returns nonzero if the |value| which is supposed to be the value of the * :authority or host header field is valid according to * https://tools.ietf.org/html/rfc3986#section-3.2 * * Note that :authority and host field values are not authority. They * do not include userinfo in RFC 3986, see * https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2, that * is, it does not include '@'. This function treats '@' as a valid * character. * * |value| is valid if it merely consists of the allowed characters. * In particular, it does not check whether |value| follows the syntax * of authority. */ NGHTTP2_EXTERN int nghttp2_check_authority(const uint8_t *value, size_t len); /* HPACK API */ struct nghttp2_hd_deflater; /** * @struct * * HPACK deflater object. */ typedef struct nghttp2_hd_deflater nghttp2_hd_deflater; /** * @function * * Initializes |*deflater_ptr| for deflating name/values pairs. * * The |max_deflate_dynamic_table_size| is the upper bound of header * table size the deflater will use. * * If this function fails, |*deflater_ptr| is left untouched. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ NGHTTP2_EXTERN int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, size_t max_deflate_dynamic_table_size); /** * @function * * Like `nghttp2_hd_deflate_new()`, but with additional custom memory * allocator specified in the |mem|. * * The |mem| can be ``NULL`` and the call is equivalent to * `nghttp2_hd_deflate_new()`. * * This function does not take ownership |mem|. The application is * responsible for freeing |mem|. * * The library code does not refer to |mem| pointer after this * function returns, so the application can safely free it. */ NGHTTP2_EXTERN int nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, size_t max_deflate_dynamic_table_size, nghttp2_mem *mem); /** * @function * * Deallocates any resources allocated for |deflater|. */ NGHTTP2_EXTERN void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater); /** * @function * * Changes header table size of the |deflater| to * |settings_max_dynamic_table_size| bytes. This may trigger eviction * in the dynamic table. * * The |settings_max_dynamic_table_size| should be the value received * in SETTINGS_HEADER_TABLE_SIZE. * * The deflater never uses more memory than * ``max_deflate_dynamic_table_size`` bytes specified in * `nghttp2_hd_deflate_new()`. Therefore, if * |settings_max_dynamic_table_size| > * ``max_deflate_dynamic_table_size``, resulting maximum table size * becomes ``max_deflate_dynamic_table_size``. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ NGHTTP2_EXTERN int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, size_t settings_max_dynamic_table_size); #ifndef NGHTTP2_NO_SSIZE_T /** * @function * * .. warning:: * * Deprecated. Use `nghttp2_hd_deflate_hd2()` instead. * * Deflates the |nva|, which has the |nvlen| name/value pairs, into * the |buf| of length |buflen|. * * If |buf| is not large enough to store the deflated header block, * this function fails with * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller * should use `nghttp2_hd_deflate_bound()` to know the upper bound of * buffer size required to deflate given header name/value pairs. * * Once this function fails, subsequent call of this function always * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. * * After this function returns, it is safe to delete the |nva|. * * This function returns the number of bytes written to |buf| if it * succeeds, or one of the following negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` * Deflation process has failed. * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` * The provided |buflen| size is too small to hold the output. */ NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, size_t buflen, const nghttp2_nv *nva, size_t nvlen); #endif /* NGHTTP2_NO_SSIZE_T */ /** * @function * * Deflates the |nva|, which has the |nvlen| name/value pairs, into * the |buf| of length |buflen|. * * If |buf| is not large enough to store the deflated header block, * this function fails with * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller * should use `nghttp2_hd_deflate_bound()` to know the upper bound of * buffer size required to deflate given header name/value pairs. * * Once this function fails, subsequent call of this function always * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. * * After this function returns, it is safe to delete the |nva|. * * This function returns the number of bytes written to |buf| if it * succeeds, or one of the following negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` * Deflation process has failed. * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` * The provided |buflen| size is too small to hold the output. */ NGHTTP2_EXTERN nghttp2_ssize nghttp2_hd_deflate_hd2(nghttp2_hd_deflater *deflater, uint8_t *buf, size_t buflen, const nghttp2_nv *nva, size_t nvlen); #ifndef NGHTTP2_NO_SSIZE_T /** * @function * * .. warning:: * * Deprecated. Use `nghttp2_hd_deflate_hd_vec2()` instead. * * Deflates the |nva|, which has the |nvlen| name/value pairs, into * the |veclen| size of buf vector |vec|. The each size of buffer * must be set in len field of :type:`nghttp2_vec`. If and only if * one chunk is filled up completely, next chunk will be used. If * |vec| is not large enough to store the deflated header block, this * function fails with * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller * should use `nghttp2_hd_deflate_bound()` to know the upper bound of * buffer size required to deflate given header name/value pairs. * * Once this function fails, subsequent call of this function always * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. * * After this function returns, it is safe to delete the |nva|. * * This function returns the number of bytes written to |vec| if it * succeeds, or one of the following negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` * Deflation process has failed. * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` * The provided |buflen| size is too small to hold the output. */ NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, const nghttp2_vec *vec, size_t veclen, const nghttp2_nv *nva, size_t nvlen); #endif /* NGHTTP2_NO_SSIZE_T */ /** * @function * * Deflates the |nva|, which has the |nvlen| name/value pairs, into * the |veclen| size of buf vector |vec|. The each size of buffer * must be set in len field of :type:`nghttp2_vec`. If and only if * one chunk is filled up completely, next chunk will be used. If * |vec| is not large enough to store the deflated header block, this * function fails with * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller * should use `nghttp2_hd_deflate_bound()` to know the upper bound of * buffer size required to deflate given header name/value pairs. * * Once this function fails, subsequent call of this function always * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. * * After this function returns, it is safe to delete the |nva|. * * This function returns the number of bytes written to |vec| if it * succeeds, or one of the following negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` * Deflation process has failed. * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` * The provided |buflen| size is too small to hold the output. */ NGHTTP2_EXTERN nghttp2_ssize nghttp2_hd_deflate_hd_vec2( nghttp2_hd_deflater *deflater, const nghttp2_vec *vec, size_t veclen, const nghttp2_nv *nva, size_t nvlen); /** * @function * * Returns an upper bound on the compressed size after deflation of * |nva| of length |nvlen|. */ NGHTTP2_EXTERN size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, const nghttp2_nv *nva, size_t nvlen); /** * @function * * Returns the number of entries that header table of |deflater| * contains. This is the sum of the number of static table and * dynamic table, so the return value is at least 61. */ NGHTTP2_EXTERN size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater); /** * @function * * Returns the table entry denoted by |idx| from header table of * |deflater|. The |idx| is 1-based, and idx=1 returns first entry of * static table. idx=62 returns first entry of dynamic table if it * exists. Specifying idx=0 is error, and this function returns NULL. * If |idx| is strictly greater than the number of entries the tables * contain, this function returns NULL. */ NGHTTP2_EXTERN const nghttp2_nv * nghttp2_hd_deflate_get_table_entry(nghttp2_hd_deflater *deflater, size_t idx); /** * @function * * Returns the used dynamic table size, including the overhead 32 * bytes per entry described in RFC 7541. */ NGHTTP2_EXTERN size_t nghttp2_hd_deflate_get_dynamic_table_size(nghttp2_hd_deflater *deflater); /** * @function * * Returns the maximum dynamic table size. */ NGHTTP2_EXTERN size_t nghttp2_hd_deflate_get_max_dynamic_table_size(nghttp2_hd_deflater *deflater); struct nghttp2_hd_inflater; /** * @struct * * HPACK inflater object. */ typedef struct nghttp2_hd_inflater nghttp2_hd_inflater; /** * @function * * Initializes |*inflater_ptr| for inflating name/values pairs. * * If this function fails, |*inflater_ptr| is left untouched. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ NGHTTP2_EXTERN int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr); /** * @function * * Like `nghttp2_hd_inflate_new()`, but with additional custom memory * allocator specified in the |mem|. * * The |mem| can be ``NULL`` and the call is equivalent to * `nghttp2_hd_inflate_new()`. * * This function does not take ownership |mem|. The application is * responsible for freeing |mem|. * * The library code does not refer to |mem| pointer after this * function returns, so the application can safely free it. */ NGHTTP2_EXTERN int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr, nghttp2_mem *mem); /** * @function * * Deallocates any resources allocated for |inflater|. */ NGHTTP2_EXTERN void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater); /** * @function * * Changes header table size in the |inflater|. This may trigger * eviction in the dynamic table. * * The |settings_max_dynamic_table_size| should be the value * transmitted in SETTINGS_HEADER_TABLE_SIZE. * * This function must not be called while header block is being * inflated. In other words, this function must be called after * initialization of |inflater|, but before calling * `nghttp2_hd_inflate_hd3()`, or after * `nghttp2_hd_inflate_end_headers()`. Otherwise, * `NGHTTP2_ERR_INVALID_STATE` was returned. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` * The function is called while header block is being inflated. * Probably, application missed to call * `nghttp2_hd_inflate_end_headers()`. */ NGHTTP2_EXTERN int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, size_t settings_max_dynamic_table_size); /** * @enum * * The flags for header inflation. */ typedef enum { /** * No flag set. */ NGHTTP2_HD_INFLATE_NONE = 0, /** * Indicates all headers were inflated. */ NGHTTP2_HD_INFLATE_FINAL = 0x01, /** * Indicates a header was emitted. */ NGHTTP2_HD_INFLATE_EMIT = 0x02 } nghttp2_hd_inflate_flag; #ifndef NGHTTP2_NO_SSIZE_T /** * @function * * .. warning:: * * Deprecated. Use `nghttp2_hd_inflate_hd2()` instead. * * Inflates name/value block stored in |in| with length |inlen|. This * function performs decompression. For each successful emission of * header name/value pair, * :enum:`nghttp2_hd_inflate_flag.NGHTTP2_HD_INFLATE_EMIT` is set in * |*inflate_flags| and name/value pair is assigned to the |nv_out| * and the function returns. The caller must not free the members of * |nv_out|. * * The |nv_out| may include pointers to the memory region in the |in|. * The caller must retain the |in| while the |nv_out| is used. * * The application should call this function repeatedly until the * ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and * return value is non-negative. This means the all input values are * processed successfully. Then the application must call * `nghttp2_hd_inflate_end_headers()` to prepare for the next header * block input. * * The caller can feed complete compressed header block. It also can * feed it in several chunks. The caller must set |in_final| to * nonzero if the given input is the last block of the compressed * header. * * This function returns the number of bytes processed if it succeeds, * or one of the following negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` * Inflation process has failed. * :enum:`nghttp2_error.NGHTTP2_ERR_BUFFER_ERROR` * The header field name or value is too large. * * Example follows:: * * int inflate_header_block(nghttp2_hd_inflater *hd_inflater, * uint8_t *in, size_t inlen, int final) * { * ssize_t rv; * * for(;;) { * nghttp2_nv nv; * int inflate_flags = 0; * * rv = nghttp2_hd_inflate_hd(hd_inflater, &nv, &inflate_flags, * in, inlen, final); * * if(rv < 0) { * fprintf(stderr, "inflate failed with error code %zd", rv); * return -1; * } * * in += rv; * inlen -= rv; * * if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { * fwrite(nv.name, nv.namelen, 1, stderr); * fprintf(stderr, ": "); * fwrite(nv.value, nv.valuelen, 1, stderr); * fprintf(stderr, "\n"); * } * if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { * nghttp2_hd_inflate_end_headers(hd_inflater); * break; * } * if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && * inlen == 0) { * break; * } * } * * return 0; * } * */ NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags, uint8_t *in, size_t inlen, int in_final); #endif /* NGHTTP2_NO_SSIZE_T */ #ifndef NGHTTP2_NO_SSIZE_T /** * @function * * .. warning:: * * Deprecated. Use `nghttp2_hd_inflate_hd3()` instead. * * Inflates name/value block stored in |in| with length |inlen|. This * function performs decompression. For each successful emission of * header name/value pair, * :enum:`nghttp2_hd_inflate_flag.NGHTTP2_HD_INFLATE_EMIT` is set in * |*inflate_flags| and name/value pair is assigned to the |nv_out| * and the function returns. The caller must not free the members of * |nv_out|. * * The |nv_out| may include pointers to the memory region in the |in|. * The caller must retain the |in| while the |nv_out| is used. * * The application should call this function repeatedly until the * ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and * return value is non-negative. If that happens, all given input * data (|inlen| bytes) are processed successfully. Then the * application must call `nghttp2_hd_inflate_end_headers()` to prepare * for the next header block input. * * In other words, if |in_final| is nonzero, and this function returns * |inlen|, you can assert that * :enum:`nghttp2_hd_inflate_final.NGHTTP2_HD_INFLATE_FINAL` is set in * |*inflate_flags|. * * The caller can feed complete compressed header block. It also can * feed it in several chunks. The caller must set |in_final| to * nonzero if the given input is the last block of the compressed * header. * * This function returns the number of bytes processed if it succeeds, * or one of the following negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` * Inflation process has failed. * :enum:`nghttp2_error.NGHTTP2_ERR_BUFFER_ERROR` * The header field name or value is too large. * * Example follows:: * * int inflate_header_block(nghttp2_hd_inflater *hd_inflater, * uint8_t *in, size_t inlen, int final) * { * ssize_t rv; * * for(;;) { * nghttp2_nv nv; * int inflate_flags = 0; * * rv = nghttp2_hd_inflate_hd2(hd_inflater, &nv, &inflate_flags, * in, inlen, final); * * if(rv < 0) { * fprintf(stderr, "inflate failed with error code %zd", rv); * return -1; * } * * in += rv; * inlen -= rv; * * if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { * fwrite(nv.name, nv.namelen, 1, stderr); * fprintf(stderr, ": "); * fwrite(nv.value, nv.valuelen, 1, stderr); * fprintf(stderr, "\n"); * } * if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { * nghttp2_hd_inflate_end_headers(hd_inflater); * break; * } * if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && * inlen == 0) { * break; * } * } * * return 0; * } * */ NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags, const uint8_t *in, size_t inlen, int in_final); #endif /* NGHTTP2_NO_SSIZE_T */ /** * @function * * Inflates name/value block stored in |in| with length |inlen|. This * function performs decompression. For each successful emission of * header name/value pair, * :enum:`nghttp2_hd_inflate_flag.NGHTTP2_HD_INFLATE_EMIT` is set in * |*inflate_flags| and name/value pair is assigned to the |nv_out| * and the function returns. The caller must not free the members of * |nv_out|. * * The |nv_out| may include pointers to the memory region in the |in|. * The caller must retain the |in| while the |nv_out| is used. * * The application should call this function repeatedly until the * ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and * return value is non-negative. If that happens, all given input * data (|inlen| bytes) are processed successfully. Then the * application must call `nghttp2_hd_inflate_end_headers()` to prepare * for the next header block input. * * In other words, if |in_final| is nonzero, and this function returns * |inlen|, you can assert that * :enum:`nghttp2_hd_inflate_final.NGHTTP2_HD_INFLATE_FINAL` is set in * |*inflate_flags|. * * The caller can feed complete compressed header block. It also can * feed it in several chunks. The caller must set |in_final| to * nonzero if the given input is the last block of the compressed * header. * * This function returns the number of bytes processed if it succeeds, * or one of the following negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` * Inflation process has failed. * :enum:`nghttp2_error.NGHTTP2_ERR_BUFFER_ERROR` * The header field name or value is too large. * * Example follows:: * * int inflate_header_block(nghttp2_hd_inflater *hd_inflater, * uint8_t *in, size_t inlen, int final) * { * nghttp2_ssize rv; * * for(;;) { * nghttp2_nv nv; * int inflate_flags = 0; * * rv = nghttp2_hd_inflate_hd3(hd_inflater, &nv, &inflate_flags, * in, inlen, final); * * if(rv < 0) { * fprintf(stderr, "inflate failed with error code %td", rv); * return -1; * } * * in += rv; * inlen -= rv; * * if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { * fwrite(nv.name, nv.namelen, 1, stderr); * fprintf(stderr, ": "); * fwrite(nv.value, nv.valuelen, 1, stderr); * fprintf(stderr, "\n"); * } * if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { * nghttp2_hd_inflate_end_headers(hd_inflater); * break; * } * if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && * inlen == 0) { * break; * } * } * * return 0; * } * */ NGHTTP2_EXTERN nghttp2_ssize nghttp2_hd_inflate_hd3( nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags, const uint8_t *in, size_t inlen, int in_final); /** * @function * * Signals the end of decompression for one header block. * * This function returns 0 if it succeeds. Currently this function * always succeeds. */ NGHTTP2_EXTERN int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater); /** * @function * * Returns the number of entries that header table of |inflater| * contains. This is the sum of the number of static table and * dynamic table, so the return value is at least 61. */ NGHTTP2_EXTERN size_t nghttp2_hd_inflate_get_num_table_entries(nghttp2_hd_inflater *inflater); /** * @function * * Returns the table entry denoted by |idx| from header table of * |inflater|. The |idx| is 1-based, and idx=1 returns first entry of * static table. idx=62 returns first entry of dynamic table if it * exists. Specifying idx=0 is error, and this function returns NULL. * If |idx| is strictly greater than the number of entries the tables * contain, this function returns NULL. */ NGHTTP2_EXTERN const nghttp2_nv * nghttp2_hd_inflate_get_table_entry(nghttp2_hd_inflater *inflater, size_t idx); /** * @function * * Returns the used dynamic table size, including the overhead 32 * bytes per entry described in RFC 7541. */ NGHTTP2_EXTERN size_t nghttp2_hd_inflate_get_dynamic_table_size(nghttp2_hd_inflater *inflater); /** * @function * * Returns the maximum dynamic table size. */ NGHTTP2_EXTERN size_t nghttp2_hd_inflate_get_max_dynamic_table_size(nghttp2_hd_inflater *inflater); struct nghttp2_stream; /** * @struct * * The structure to represent HTTP/2 stream. The details of this * structure are intentionally hidden from the public API. */ typedef struct nghttp2_stream nghttp2_stream; /** * @function * * Returns pointer to :type:`nghttp2_stream` object denoted by * |stream_id|. If stream was not found, returns NULL. * * Returns imaginary root stream (see * `nghttp2_session_get_root_stream()`) if 0 is given in |stream_id|. * * Unless |stream_id| == 0, the returned pointer is valid until next * call of `nghttp2_session_send()`, `nghttp2_session_mem_send2()`, * `nghttp2_session_recv()`, and `nghttp2_session_mem_recv2()`. */ NGHTTP2_EXTERN nghttp2_stream * nghttp2_session_find_stream(nghttp2_session *session, int32_t stream_id); /** * @enum * * State of stream as described in RFC 7540. */ typedef enum { /** * idle state. */ NGHTTP2_STREAM_STATE_IDLE = 1, /** * open state. */ NGHTTP2_STREAM_STATE_OPEN, /** * reserved (local) state. */ NGHTTP2_STREAM_STATE_RESERVED_LOCAL, /** * reserved (remote) state. */ NGHTTP2_STREAM_STATE_RESERVED_REMOTE, /** * half closed (local) state. */ NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL, /** * half closed (remote) state. */ NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE, /** * closed state. */ NGHTTP2_STREAM_STATE_CLOSED } nghttp2_stream_proto_state; /** * @function * * Returns state of |stream|. The root stream retrieved by * `nghttp2_session_get_root_stream()` will have stream state * :enum:`nghttp2_stream_proto_state.NGHTTP2_STREAM_STATE_IDLE`. */ NGHTTP2_EXTERN nghttp2_stream_proto_state nghttp2_stream_get_state(nghttp2_stream *stream); /** * @function * * .. warning:: * * Deprecated. :rfc:`7540` priorities are deprecated by * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible * prioritization scheme. * * Returns root of dependency tree, which is imaginary stream with * stream ID 0. The returned pointer is valid until |session| is * freed by `nghttp2_session_del()`. */ NGHTTP2_EXTERN nghttp2_stream * nghttp2_session_get_root_stream(nghttp2_session *session); /** * @function * * .. warning:: * * Deprecated. :rfc:`7540` priorities are deprecated by * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible * prioritization scheme. * * This function always returns NULL. */ NGHTTP2_EXTERN nghttp2_stream * nghttp2_stream_get_parent(nghttp2_stream *stream); NGHTTP2_EXTERN int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream); /** * @function * * .. warning:: * * Deprecated. :rfc:`7540` priorities are deprecated by * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible * prioritization scheme. * * This function always returns NULL. */ NGHTTP2_EXTERN nghttp2_stream * nghttp2_stream_get_next_sibling(nghttp2_stream *stream); /** * @function * * .. warning:: * * Deprecated. :rfc:`7540` priorities are deprecated by * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible * prioritization scheme. * * This function always returns NULL. */ NGHTTP2_EXTERN nghttp2_stream * nghttp2_stream_get_previous_sibling(nghttp2_stream *stream); /** * @function * * .. warning:: * * Deprecated. :rfc:`7540` priorities are deprecated by * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible * prioritization scheme. * * This function always returns NULL. */ NGHTTP2_EXTERN nghttp2_stream * nghttp2_stream_get_first_child(nghttp2_stream *stream); /** * @function * * .. warning:: * * Deprecated. :rfc:`7540` priorities are deprecated by * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible * prioritization scheme. * * This function always returns :macro:`NGHTTP2_DEFAULT_WEIGHT`. */ NGHTTP2_EXTERN int32_t nghttp2_stream_get_weight(nghttp2_stream *stream); /** * @function * * .. warning:: * * Deprecated. :rfc:`7540` priorities are deprecated by * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible * prioritization scheme. * * This function always returns 0. */ NGHTTP2_EXTERN int32_t nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream); /** * @functypedef * * Callback function invoked when the library outputs debug logging. * The function is called with arguments suitable for ``vfprintf(3)`` * * The debug output is only enabled if the library is built with * ``DEBUGBUILD`` macro defined. */ typedef void (*nghttp2_debug_vprintf_callback)(const char *format, va_list args); /** * @function * * Sets a debug output callback called by the library when built with * ``DEBUGBUILD`` macro defined. If this option is not used, debug * log is written into standard error output. * * For builds without ``DEBUGBUILD`` macro defined, this function is * noop. * * Note that building with ``DEBUGBUILD`` may cause significant * performance penalty to libnghttp2 because of extra processing. It * should be used for debugging purpose only. * * .. Warning:: * * Building with ``DEBUGBUILD`` may cause significant performance * penalty to libnghttp2 because of extra processing. It should be * used for debugging purpose only. We write this two times because * this is important. */ NGHTTP2_EXTERN void nghttp2_set_debug_vprintf_callback( nghttp2_debug_vprintf_callback debug_vprintf_callback); #ifdef __cplusplus } #endif #endif /* NGHTTP2_H */ nghttp2-1.68.0/lib/includes/nghttp2/PaxHeaders/nghttp2ver.h.in0000644000000000000000000000013215077107270021103 xustar0030 mtime=1761382072.976444217 30 atime=1761382103.819320933 30 ctime=1761382107.829304055 nghttp2-1.68.0/lib/includes/nghttp2/nghttp2ver.h.in0000644000175100017510000000313515077107270021475 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2VER_H #define NGHTTP2VER_H /** * @macro * Version number of the nghttp2 library release */ #define NGHTTP2_VERSION "@PACKAGE_VERSION@" /** * @macro * Numerical representation of the version number of the nghttp2 library * release. This is a 24 bit number with 8 bits for major number, 8 bits * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. */ #define NGHTTP2_VERSION_NUM @PACKAGE_VERSION_NUM@ #endif /* NGHTTP2VER_H */ nghttp2-1.68.0/lib/includes/nghttp2/PaxHeaders/nghttp2ver.h0000644000000000000000000000013215077107327020501 xustar0030 mtime=1761382103.821320925 30 atime=1761382104.287318872 30 ctime=1761382108.019303506 nghttp2-1.68.0/lib/includes/nghttp2/nghttp2ver.h0000644000175100017510000000310515077107327021070 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2VER_H #define NGHTTP2VER_H /** * @macro * Version number of the nghttp2 library release */ #define NGHTTP2_VERSION "1.68.0" /** * @macro * Numerical representation of the version number of the nghttp2 library * release. This is a 24 bit number with 8 bits for major number, 8 bits * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. */ #define NGHTTP2_VERSION_NUM 0x014400 #endif /* NGHTTP2VER_H */ nghttp2-1.68.0/lib/includes/PaxHeaders/Makefile.am0000644000000000000000000000013215077107270016670 xustar0030 mtime=1761382072.975444222 30 atime=1761382085.599387029 30 ctime=1761382108.016303515 nghttp2-1.68.0/lib/includes/Makefile.am0000644000175100017510000000230515077107270017260 0ustar00runnerrunner# nghttp2 - HTTP/2 C Library # Copyright (c) 2012 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. EXTRA_DIST = CMakeLists.txt nobase_include_HEADERS = nghttp2/nghttp2.h nghttp2/nghttp2ver.h nghttp2-1.68.0/lib/PaxHeaders/nghttp2_time.c0000644000000000000000000000013215077107270015576 xustar0030 mtime=1761382072.981444194 30 atime=1761382104.993315762 30 ctime=1761382107.979303622 nghttp2-1.68.0/lib/nghttp2_time.c0000644000175100017510000000436515077107270016176 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2023 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_time.h" #ifdef HAVE_WINDOWS_H # include #endif /* defined(HAVE_WINDOWS_H) */ #include #if !defined(HAVE_GETTICKCOUNT64) || defined(__CYGWIN__) static uint64_t time_now_sec(void) { time_t t = time(NULL); if (t == -1) { return 0; } return (uint64_t)t; } #endif /* !defined(HAVE_GETTICKCOUNT64) || defined(__CYGWIN__) */ #if defined(HAVE_GETTICKCOUNT64) && !defined(__CYGWIN__) uint64_t nghttp2_time_now_sec(void) { return GetTickCount64() / 1000; } #elif defined(HAVE_CLOCK_GETTIME) && HAVE_DECL_CLOCK_MONOTONIC uint64_t nghttp2_time_now_sec(void) { struct timespec tp; int rv = clock_gettime(CLOCK_MONOTONIC, &tp); if (rv == -1) { return time_now_sec(); } return (uint64_t)tp.tv_sec; } #else /* (!defined(HAVE_GETTICKCOUNT64) || !defined(__CYGWIN__)) && \ (!defined(HAVE_CLOCK_GETTIME) || !HAVE_DECL_CLOCK_MONOTONIC) */ uint64_t nghttp2_time_now_sec(void) { return time_now_sec(); } #endif /* (!defined(HAVE_GETTICKCOUNT64) || !defined(__CYGWIN__)) && \ (!defined(HAVE_CLOCK_GETTIME) || !HAVE_DECL_CLOCK_MONOTONIC) */ nghttp2-1.68.0/lib/PaxHeaders/Makefile.msvc0000644000000000000000000000013115077107270015434 xustar0030 mtime=1761382072.975444222 30 atime=1761382104.997315744 29 ctime=1761382107.98330361 nghttp2-1.68.0/lib/Makefile.msvc0000644000175100017510000001651715077107270016037 0ustar00runnerrunner# # GNU Makefile for nghttp2 / MSVC. # # By G. Vanem 2013 # Updated 3/2015 by Remo Eichenberger @remoe # The MIT License apply. # THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST)) _VERSION := $(shell grep AC_INIT ../configure.ac | cut -d'[' -f3 | sed -e 's/-DEV//g' -e 's/], //g') _VERSION := $(subst ., ,$(_VERSION)) VER_MAJOR := $(word 1,$(_VERSION)) VER_MINOR := $(word 2,$(_VERSION)) VER_MICRO := $(word 3,$(_VERSION)) VERSION := $(VER_MAJOR).$(VER_MINOR).$(VER_MICRO) VERSION_NUM := (($(VER_MAJOR) << 16) + ($(VER_MINOR) << 8) + $(VER_MICRO)) GENERATED := 'Generated by $(realpath Makefile.MSVC)' OBJ_DIR := MSVC_obj #SUFFIX :=-vc90-mt-x86 # # Where to copy nghttp2.dll + lib + headers to. # Note: 'make install' is not in default targets. Do it explicitly. # TARGET_DIR ?= ../_VC_ROOT VC_ROOT := $(abspath $(TARGET_DIR)) INSTALL_BIN := $(VC_ROOT)/bin INSTALL_LIB := $(VC_ROOT)/lib INSTALL_HDR := $(VC_ROOT)/include DLL_R := $(OBJ_DIR)/nghttp2$(SUFFIX).dll DLL_D := $(OBJ_DIR)/nghttp2d$(SUFFIX).dll LIB_R := $(OBJ_DIR)/nghttp2-static.lib LIB_D := $(OBJ_DIR)/nghttp2d-static.lib IMP_R := $(OBJ_DIR)/nghttp2.lib IMP_D := $(OBJ_DIR)/nghttp2d.lib # # Build for DEBUG-model and RELEASE at the same time. # TARGETS := $(LIB_R) $(DLL_R) $(IMP_R) \ $(LIB_D) $(DLL_D) $(IMP_D) EXT_LIBS = NGHTTP2_PDB_R := $(OBJ_DIR)/nghttp2.pdb NGHTTP2_PDB_D := $(OBJ_DIR)/nghttp2d.pdb CC = cl LD := link AR := lib #CC := icl #LD := xilink #AR := xilib RC := rc CFLAGS := -I./includes -Dssize_t=long CFLAGS_R := -nologo -MD -W3 -Z7 -DBUILDING_NGHTTP2 CFLAGS_D := -nologo -MDd -W3 -Z7 -DBUILDING_NGHTTP2 \ -Ot -D_DEBUG -GF -RTCs -RTCu # -RTCc -GS LDFLAGS := -nologo -MAP -debug -incremental:no -opt:ref,icf -MANIFEST # -verbose NGHTTP2_SRC := nghttp2_pq.c \ nghttp2_map.c \ nghttp2_queue.c \ nghttp2_frame.c \ nghttp2_buf.c \ nghttp2_stream.c \ nghttp2_outbound_item.c \ nghttp2_session.c \ nghttp2_submit.c \ nghttp2_helper.c \ nghttp2_alpn.c \ nghttp2_hd.c \ nghttp2_hd_huffman.c \ nghttp2_hd_huffman_data.c \ nghttp2_version.c \ nghttp2_priority_spec.c \ nghttp2_option.c \ nghttp2_callbacks.c \ nghttp2_mem.c \ nghttp2_http.c \ nghttp2_rcbuf.c NGHTTP2_OBJ_R := $(addprefix $(OBJ_DIR)/r_, $(notdir $(NGHTTP2_SRC:.c=.obj))) NGHTTP2_OBJ_D := $(addprefix $(OBJ_DIR)/d_, $(notdir $(NGHTTP2_SRC:.c=.obj))) .PHONY: all intro test_ver install copy_headers_and_libs \ install_nghttp2_pyd_0 install_nghttp2_pyd_1 \ build_nghttp2_pyd_0 build_nghttp2_pyd_1 \ clean_nghttp2_pyd_0 clean_nghttp2_pyd_1 all: intro includes/nghttp2/nghttp2ver.h $(OBJ_DIR) $(TARGETS) @echo 'Welcome to NgHTTP2 (release + debug).' @echo 'Do a "make -f Makefile.MSVC install" at own risk!' intro: @echo 'Building NgHTTP (MSVC) ver. "$(VERSION)".' test_ver: @echo '$$(VERSION): "$(VERSION)".' @echo '$$(_VERSION): "$(_VERSION)".' @echo '$$(VER_MAJOR): "$(VER_MAJOR)".' @echo '$$(VER_MINOR): "$(VER_MINOR)".' @echo '$$(VER_MICRO): "$(VER_MICRO)".' $(OBJ_DIR): - mkdir $(OBJ_DIR) install: includes/nghttp2/nghttp2.h includes/nghttp2/nghttp2ver.h \ $(TARGETS) \ copy_headers_and_libs # # This MUST be done before using the 'install_nghttp2_pyd_1' rule. # copy_headers_and_libs: - mkdir -p $(INSTALL_HDR)/nghttp2 $(INSTALL_BIN) $(INSTALL_LIB) cp --update $(addprefix includes/nghttp2/, nghttp2.h nghttp2ver.h) $(INSTALL_HDR)/nghttp2 cp --update $(DLL_R) $(DLL_D) $(NGHTTP2_PDB_R) $(NGHTTP2_PDB_D) $(INSTALL_BIN) cp --update $(IMP_R) $(IMP_D) $(LIB_R) $(LIB_D) $(INSTALL_LIB) @echo $(LIB_R): $(NGHTTP2_OBJ_R) $(AR) -nologo -out:$@ $^ @echo $(LIB_D): $(NGHTTP2_OBJ_D) $(AR) -nologo -out:$@ $^ @echo $(IMP_R): $(DLL_R) $(DLL_R): $(NGHTTP2_OBJ_R) $(OBJ_DIR)/r_nghttp2.res $(LD) $(LDFLAGS) -dll -out:$@ -implib:$(IMP_R) $(NGHTTP2_OBJ_R) -PDB:$(NGHTTP2_PDB_R) $(OBJ_DIR)/r_nghttp2.res $(EXT_LIBS) mt -nologo -manifest $@.manifest -outputresource:$@\;2 @echo $(IMP_D): $(DLL_D) $(DLL_D): $(NGHTTP2_OBJ_D) $(OBJ_DIR)/d_nghttp2.res $(LD) $(LDFLAGS) -dll -out:$@ -implib:$(IMP_D) $(NGHTTP2_OBJ_D) -PDB:$(NGHTTP2_PDB_D) $(OBJ_DIR)/d_nghttp2.res $(EXT_LIBS) mt -nologo -manifest $@.manifest -outputresource:$@\;2 @echo WIN_OBJDIR:=$(shell cygpath -w $(abspath $(OBJ_DIR))) WIN_OBJDIR:=$(subst \,/,$(WIN_OBJDIR)) $(OBJ_DIR)/r_%.obj: %.c $(THIS_MAKEFILE) $(CC) $(CFLAGS_R) $(CFLAGS) -Fo$@ -c $< @echo $(OBJ_DIR)/d_%.obj: %.c $(THIS_MAKEFILE) $(CC) $(CFLAGS_D) $(CFLAGS) -Fo$@ -c $< @echo $(OBJ_DIR)/r_nghttp2.res: $(OBJ_DIR)/nghttp2.rc $(THIS_MAKEFILE) $(RC) -D_RELEASE -Fo $@ $< @echo $(OBJ_DIR)/d_nghttp2.res: $(OBJ_DIR)/nghttp2.rc $(THIS_MAKEFILE) $(RC) -D_DEBUG -Fo $@ $< @echo includes/nghttp2/nghttp2ver.h: includes/nghttp2/nghttp2ver.h.in $(THIS_MAKEFILE) sed < includes/nghttp2/nghttp2ver.h.in \ -e 's/@PACKAGE_VERSION@/$(VERSION)/g' \ -e 's/@PACKAGE_VERSION_NUM@/$(VERSION_NUM)/g' > $@ touch --reference=includes/nghttp2/nghttp2ver.h.in $@ define RES_FILE #include VS_VERSION_INFO VERSIONINFO FILEVERSION $(VER_MAJOR), $(VER_MINOR), $(VER_MICRO), 0 PRODUCTVERSION $(VER_MAJOR), $(VER_MINOR), $(VER_MICRO), 0 FILEFLAGSMASK 0x3fL FILEOS 0x40004L FILETYPE 0x2L FILESUBTYPE 0x0L #ifdef _DEBUG #define VER_STR "$(VERSION).0 (MSVC debug)" #define DBG "d" FILEFLAGS 0x1L #else #define VER_STR "$(VERSION).0 (MSVC release)" #define DBG "" FILEFLAGS 0x0L #endif BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "http://tatsuhiro-t.github.io/nghttp2/" VALUE "FileDescription", "nghttp2; HTTP/2 C library" VALUE "FileVersion", VER_STR VALUE "InternalName", "nghttp2" DBG VALUE "LegalCopyright", "The MIT License" VALUE "LegalTrademarks", "" VALUE "OriginalFilename", "nghttp2" DBG ".dll" VALUE "ProductName", "NGHTTP2." VALUE "ProductVersion", VER_STR END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END endef export RES_FILE $(OBJ_DIR)/nghttp2.rc: Makefile.MSVC @echo 'Generating $@...' @echo ' /* $(GENERATED). DO NOT EDIT.' > $@ @echo ' */' >> $@ @echo "$$RES_FILE" >> $@ clean: rm -f $(OBJ_DIR)/* includes/nghttp2/nghttp2ver.h @echo vclean realclean: clean - rm -rf $(OBJ_DIR) - rm -f .depend.MSVC # # Use gcc to generated the dependencies. No MSVC specific args please! # REPLACE_R = 's/\(.*\)\.o: /\n$$(OBJ_DIR)\/r_\1.obj: /' REPLACE_D = 's/\(.*\)\.o: /\n$$(OBJ_DIR)\/d_\1.obj: /' depend: includes/nghttp2/nghttp2ver.h @echo '# $(GENERATED). DO NOT EDIT.' > .depend.MSVC gcc -MM $(CFLAGS) $(NGHTTP2_SRC) >> .depend.tmp @echo '#' >> .depend.MSVC @echo '# Release lib objects:' >> .depend.MSVC sed -e $(REPLACE_R) .depend.tmp >> .depend.MSVC @echo '#' >> .depend.MSVC @echo '# Debug lib objects:' >> .depend.MSVC sed -e $(REPLACE_D) .depend.tmp >> .depend.MSVC rm -f .depend.tmp -include .depend.MSVC nghttp2-1.68.0/lib/PaxHeaders/nghttp2_queue.c0000644000000000000000000000013015077107270015762 xustar0030 mtime=1761382072.980444199 30 atime=1761382104.966315881 28 ctime=1761382107.9523037 nghttp2-1.68.0/lib/nghttp2_queue.c0000644000175100017510000000464015077107270016360 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_queue.h" #include #include void nghttp2_queue_init(nghttp2_queue *queue) { queue->front = queue->back = NULL; } void nghttp2_queue_free(nghttp2_queue *queue) { if (!queue) { return; } else { nghttp2_queue_cell *p = queue->front; while (p) { nghttp2_queue_cell *next = p->next; free(p); p = next; } } } int nghttp2_queue_push(nghttp2_queue *queue, void *data) { nghttp2_queue_cell *new_cell = (nghttp2_queue_cell *)malloc(sizeof(nghttp2_queue_cell)); if (!new_cell) { return NGHTTP2_ERR_NOMEM; } new_cell->data = data; new_cell->next = NULL; if (queue->back) { queue->back->next = new_cell; queue->back = new_cell; } else { queue->front = queue->back = new_cell; } return 0; } void nghttp2_queue_pop(nghttp2_queue *queue) { nghttp2_queue_cell *front = queue->front; assert(front); queue->front = front->next; if (front == queue->back) { queue->back = NULL; } free(front); } void *nghttp2_queue_front(nghttp2_queue *queue) { assert(queue->front); return queue->front->data; } void *nghttp2_queue_back(nghttp2_queue *queue) { assert(queue->back); return queue->back->data; } int nghttp2_queue_empty(nghttp2_queue *queue) { return queue->front == NULL; } nghttp2-1.68.0/lib/PaxHeaders/nghttp2_submit.c0000644000000000000000000000013215077107270016143 xustar0030 mtime=1761382072.981444194 30 atime=1761382104.974315846 30 ctime=1761382107.960303677 nghttp2-1.68.0/lib/nghttp2_submit.c0000644000175100017510000006006415077107270016541 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_submit.h" #include #include #include "nghttp2_session.h" #include "nghttp2_frame.h" #include "nghttp2_helper.h" #include "nghttp2_priority_spec.h" /* This function takes ownership of |nva_copy|. Regardless of the return value, the caller must not free |nva_copy| after this function returns. */ static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags, int32_t stream_id, nghttp2_nv *nva_copy, size_t nvlen, const nghttp2_data_provider_wrap *dpw, void *stream_user_data) { int rv; uint8_t flags_copy; nghttp2_outbound_item *item = NULL; nghttp2_frame *frame = NULL; nghttp2_headers_category hcat; nghttp2_mem *mem; mem = &session->mem; item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); if (item == NULL) { rv = NGHTTP2_ERR_NOMEM; goto fail; } nghttp2_outbound_item_init(item); if (dpw != NULL && dpw->data_prd.read_callback != NULL) { item->aux_data.headers.dpw = *dpw; } item->aux_data.headers.stream_user_data = stream_user_data; flags_copy = (uint8_t)((flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY)) | NGHTTP2_FLAG_END_HEADERS); if (stream_id == -1) { if (session->next_stream_id > INT32_MAX) { rv = NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE; goto fail; } stream_id = (int32_t)session->next_stream_id; session->next_stream_id += 2; hcat = NGHTTP2_HCAT_REQUEST; } else { /* More specific categorization will be done later. */ hcat = NGHTTP2_HCAT_HEADERS; } frame = &item->frame; nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, hcat, NULL, nva_copy, nvlen); rv = nghttp2_session_add_item(session, item); if (rv != 0) { nghttp2_frame_headers_free(&frame->headers, mem); goto fail2; } if (hcat == NGHTTP2_HCAT_REQUEST) { return stream_id; } return 0; fail: /* nghttp2_frame_headers_init() takes ownership of nva_copy. */ nghttp2_nv_array_del(nva_copy, mem); fail2: nghttp2_mem_free(mem, item); return rv; } static int32_t submit_headers_shared_nva(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider_wrap *dpw, void *stream_user_data) { int rv; nghttp2_nv *nva_copy; nghttp2_mem *mem; mem = &session->mem; rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem); if (rv < 0) { return rv; } return submit_headers_shared(session, flags, stream_id, nva_copy, nvlen, dpw, stream_user_data); } int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen) { if (stream_id <= 0) { return NGHTTP2_ERR_INVALID_ARGUMENT; } return (int)submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM, stream_id, nva, nvlen, NULL, NULL); } int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen, void *stream_user_data) { (void)pri_spec; if (stream_id == -1) { if (session->server) { return NGHTTP2_ERR_PROTO; } } else if (stream_id <= 0) { return NGHTTP2_ERR_INVALID_ARGUMENT; } flags &= NGHTTP2_FLAG_END_STREAM; return submit_headers_shared_nva(session, flags, stream_id, nva, nvlen, NULL, stream_user_data); } int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, const uint8_t *opaque_data) { flags &= NGHTTP2_FLAG_ACK; return nghttp2_session_add_ping(session, flags, opaque_data); } int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_priority_spec *pri_spec) { (void)session; (void)flags; (void)stream_id; (void)pri_spec; return 0; } int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags, int32_t stream_id, uint32_t error_code) { (void)flags; if (stream_id == 0) { return NGHTTP2_ERR_INVALID_ARGUMENT; } return nghttp2_session_add_rst_stream_continue( session, stream_id, error_code, /* continue_without_stream = */ 0); } int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags, int32_t last_stream_id, uint32_t error_code, const uint8_t *opaque_data, size_t opaque_data_len) { (void)flags; if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { return 0; } return nghttp2_session_add_goaway(session, last_stream_id, error_code, opaque_data, opaque_data_len, NGHTTP2_GOAWAY_AUX_NONE); } int nghttp2_submit_shutdown_notice(nghttp2_session *session) { if (!session->server) { return NGHTTP2_ERR_INVALID_STATE; } if (session->goaway_flags) { return 0; } return nghttp2_session_add_goaway(session, (1u << 31) - 1, NGHTTP2_NO_ERROR, NULL, 0, NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE); } int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags, const nghttp2_settings_entry *iv, size_t niv) { (void)flags; return nghttp2_session_add_settings(session, NGHTTP2_FLAG_NONE, iv, niv); } int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen, void *promised_stream_user_data) { nghttp2_outbound_item *item; nghttp2_frame *frame; nghttp2_nv *nva_copy; uint8_t flags_copy; int32_t promised_stream_id; int rv; nghttp2_mem *mem; (void)flags; mem = &session->mem; if (stream_id <= 0 || nghttp2_session_is_my_stream_id(session, stream_id)) { return NGHTTP2_ERR_INVALID_ARGUMENT; } if (!session->server) { return NGHTTP2_ERR_PROTO; } /* All 32bit signed stream IDs are spent. */ if (session->next_stream_id > INT32_MAX) { return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE; } item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); if (item == NULL) { return NGHTTP2_ERR_NOMEM; } nghttp2_outbound_item_init(item); item->aux_data.headers.stream_user_data = promised_stream_user_data; frame = &item->frame; rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem); if (rv < 0) { nghttp2_mem_free(mem, item); return rv; } flags_copy = NGHTTP2_FLAG_END_HEADERS; promised_stream_id = (int32_t)session->next_stream_id; session->next_stream_id += 2; nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy, stream_id, promised_stream_id, nva_copy, nvlen); rv = nghttp2_session_add_item(session, item); if (rv != 0) { nghttp2_frame_push_promise_free(&frame->push_promise, mem); nghttp2_mem_free(mem, item); return rv; } return promised_stream_id; } int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags, int32_t stream_id, int32_t window_size_increment) { int rv; nghttp2_stream *stream = 0; (void)flags; if (window_size_increment == 0) { return 0; } if (stream_id == 0) { rv = nghttp2_adjust_local_window_size( &session->local_window_size, &session->recv_window_size, &session->recv_reduction, &window_size_increment); if (rv != 0) { return rv; } } else { stream = nghttp2_session_get_stream(session, stream_id); if (!stream) { return 0; } rv = nghttp2_adjust_local_window_size( &stream->local_window_size, &stream->recv_window_size, &stream->recv_reduction, &window_size_increment); if (rv != 0) { return rv; } } if (window_size_increment > 0) { if (stream_id == 0) { session->consumed_size = nghttp2_max_int32(0, session->consumed_size - window_size_increment); } else { stream->consumed_size = nghttp2_max_int32(0, stream->consumed_size - window_size_increment); } return nghttp2_session_add_window_update(session, 0, stream_id, window_size_increment); } return 0; } int nghttp2_session_set_local_window_size(nghttp2_session *session, uint8_t flags, int32_t stream_id, int32_t window_size) { int32_t window_size_increment; nghttp2_stream *stream; int rv; (void)flags; if (window_size < 0) { return NGHTTP2_ERR_INVALID_ARGUMENT; } if (stream_id == 0) { window_size_increment = window_size - session->local_window_size; if (window_size_increment == 0) { return 0; } if (window_size_increment < 0) { return nghttp2_adjust_local_window_size( &session->local_window_size, &session->recv_window_size, &session->recv_reduction, &window_size_increment); } rv = nghttp2_increase_local_window_size( &session->local_window_size, &session->recv_window_size, &session->recv_reduction, &window_size_increment); if (rv != 0) { return rv; } if (window_size_increment > 0) { return nghttp2_session_add_window_update(session, 0, stream_id, window_size_increment); } return nghttp2_session_update_recv_connection_window_size(session, 0); } else { stream = nghttp2_session_get_stream(session, stream_id); if (stream == NULL) { return 0; } window_size_increment = window_size - stream->local_window_size; if (window_size_increment == 0) { return 0; } if (window_size_increment < 0) { return nghttp2_adjust_local_window_size( &stream->local_window_size, &stream->recv_window_size, &stream->recv_reduction, &window_size_increment); } rv = nghttp2_increase_local_window_size( &stream->local_window_size, &stream->recv_window_size, &stream->recv_reduction, &window_size_increment); if (rv != 0) { return rv; } if (window_size_increment > 0) { return nghttp2_session_add_window_update(session, 0, stream_id, window_size_increment); } return nghttp2_session_update_recv_stream_window_size(session, stream, 0, 1); } } int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *origin, size_t origin_len, const uint8_t *field_value, size_t field_value_len) { nghttp2_mem *mem; uint8_t *buf, *p; uint8_t *origin_copy; uint8_t *field_value_copy; nghttp2_outbound_item *item; nghttp2_frame *frame; nghttp2_ext_altsvc *altsvc; int rv; (void)flags; mem = &session->mem; if (!session->server) { return NGHTTP2_ERR_INVALID_STATE; } if (2 + origin_len + field_value_len > NGHTTP2_MAX_PAYLOADLEN) { return NGHTTP2_ERR_INVALID_ARGUMENT; } if (stream_id == 0) { if (origin_len == 0) { return NGHTTP2_ERR_INVALID_ARGUMENT; } } else if (origin_len != 0) { return NGHTTP2_ERR_INVALID_ARGUMENT; } buf = nghttp2_mem_malloc(mem, origin_len + field_value_len + 2); if (buf == NULL) { return NGHTTP2_ERR_NOMEM; } p = buf; origin_copy = p; if (origin_len) { p = nghttp2_cpymem(p, origin, origin_len); } *p++ = '\0'; field_value_copy = p; if (field_value_len) { p = nghttp2_cpymem(p, field_value, field_value_len); } *p++ = '\0'; item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); if (item == NULL) { rv = NGHTTP2_ERR_NOMEM; goto fail_item_malloc; } nghttp2_outbound_item_init(item); item->aux_data.ext.builtin = 1; altsvc = &item->ext_frame_payload.altsvc; frame = &item->frame; frame->ext.payload = altsvc; nghttp2_frame_altsvc_init(&frame->ext, stream_id, origin_copy, origin_len, field_value_copy, field_value_len); rv = nghttp2_session_add_item(session, item); if (rv != 0) { nghttp2_frame_altsvc_free(&frame->ext, mem); nghttp2_mem_free(mem, item); return rv; } return 0; fail_item_malloc: nghttp2_mem_free(mem, buf); return rv; } int nghttp2_submit_origin(nghttp2_session *session, uint8_t flags, const nghttp2_origin_entry *ov, size_t nov) { nghttp2_mem *mem; uint8_t *p; nghttp2_outbound_item *item; nghttp2_frame *frame; nghttp2_ext_origin *origin; nghttp2_origin_entry *ov_copy; size_t len = 0; size_t i; int rv; (void)flags; mem = &session->mem; if (!session->server) { return NGHTTP2_ERR_INVALID_STATE; } if (nov) { for (i = 0; i < nov; ++i) { len += ov[i].origin_len; } if (2 * nov + len > NGHTTP2_MAX_PAYLOADLEN) { return NGHTTP2_ERR_INVALID_ARGUMENT; } /* The last nov is added for terminal NULL character. */ ov_copy = nghttp2_mem_malloc(mem, nov * sizeof(nghttp2_origin_entry) + len + nov); if (ov_copy == NULL) { return NGHTTP2_ERR_NOMEM; } p = (uint8_t *)ov_copy + nov * sizeof(nghttp2_origin_entry); for (i = 0; i < nov; ++i) { ov_copy[i].origin = p; ov_copy[i].origin_len = ov[i].origin_len; p = nghttp2_cpymem(p, ov[i].origin, ov[i].origin_len); *p++ = '\0'; } assert((size_t)(p - (uint8_t *)ov_copy) == nov * sizeof(nghttp2_origin_entry) + len + nov); } else { ov_copy = NULL; } item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); if (item == NULL) { rv = NGHTTP2_ERR_NOMEM; goto fail_item_malloc; } nghttp2_outbound_item_init(item); item->aux_data.ext.builtin = 1; origin = &item->ext_frame_payload.origin; frame = &item->frame; frame->ext.payload = origin; nghttp2_frame_origin_init(&frame->ext, ov_copy, nov); rv = nghttp2_session_add_item(session, item); if (rv != 0) { nghttp2_frame_origin_free(&frame->ext, mem); nghttp2_mem_free(mem, item); return rv; } return 0; fail_item_malloc: nghttp2_mem_free(mem, ov_copy); return rv; } int nghttp2_submit_priority_update(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *field_value, size_t field_value_len) { nghttp2_mem *mem; uint8_t *buf, *p; nghttp2_outbound_item *item; nghttp2_frame *frame; nghttp2_ext_priority_update *priority_update; int rv; (void)flags; mem = &session->mem; if (session->server) { return NGHTTP2_ERR_INVALID_STATE; } if (session->remote_settings.no_rfc7540_priorities == 0) { return 0; } if (stream_id == 0 || 4 + field_value_len > NGHTTP2_MAX_PAYLOADLEN) { return NGHTTP2_ERR_INVALID_ARGUMENT; } if (field_value_len) { buf = nghttp2_mem_malloc(mem, field_value_len + 1); if (buf == NULL) { return NGHTTP2_ERR_NOMEM; } p = nghttp2_cpymem(buf, field_value, field_value_len); *p = '\0'; } else { buf = NULL; } item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); if (item == NULL) { rv = NGHTTP2_ERR_NOMEM; goto fail_item_malloc; } nghttp2_outbound_item_init(item); item->aux_data.ext.builtin = 1; priority_update = &item->ext_frame_payload.priority_update; frame = &item->frame; frame->ext.payload = priority_update; nghttp2_frame_priority_update_init(&frame->ext, stream_id, buf, field_value_len); rv = nghttp2_session_add_item(session, item); if (rv != 0) { nghttp2_frame_priority_update_free(&frame->ext, mem); nghttp2_mem_free(mem, item); return rv; } return 0; fail_item_malloc: nghttp2_mem_free(mem, buf); return rv; } static uint8_t set_request_flags(const nghttp2_data_provider_wrap *dpw) { uint8_t flags = NGHTTP2_FLAG_NONE; if (dpw == NULL || dpw->data_prd.read_callback == NULL) { flags |= NGHTTP2_FLAG_END_STREAM; } return flags; } static int32_t submit_request_shared(nghttp2_session *session, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider_wrap *dpw, void *stream_user_data) { uint8_t flags; if (session->server) { return NGHTTP2_ERR_PROTO; } flags = set_request_flags(dpw); return submit_headers_shared_nva(session, flags, -1, nva, nvlen, dpw, stream_user_data); } int32_t nghttp2_submit_request(nghttp2_session *session, const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider *data_prd, void *stream_user_data) { nghttp2_data_provider_wrap dpw; (void)pri_spec; return submit_request_shared(session, nva, nvlen, nghttp2_data_provider_wrap_v1(&dpw, data_prd), stream_user_data); } int32_t nghttp2_submit_request2(nghttp2_session *session, const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider2 *data_prd, void *stream_user_data) { nghttp2_data_provider_wrap dpw; (void)pri_spec; return submit_request_shared(session, nva, nvlen, nghttp2_data_provider_wrap_v2(&dpw, data_prd), stream_user_data); } static uint8_t set_response_flags(const nghttp2_data_provider_wrap *dpw) { uint8_t flags = NGHTTP2_FLAG_NONE; if (dpw == NULL || dpw->data_prd.read_callback == NULL) { flags |= NGHTTP2_FLAG_END_STREAM; } return flags; } static int submit_response_shared(nghttp2_session *session, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider_wrap *dpw) { uint8_t flags; if (stream_id <= 0) { return NGHTTP2_ERR_INVALID_ARGUMENT; } if (!session->server) { return NGHTTP2_ERR_PROTO; } flags = set_response_flags(dpw); return submit_headers_shared_nva(session, flags, stream_id, nva, nvlen, dpw, NULL); } int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider *data_prd) { nghttp2_data_provider_wrap dpw; return submit_response_shared(session, stream_id, nva, nvlen, nghttp2_data_provider_wrap_v1(&dpw, data_prd)); } int nghttp2_submit_response2(nghttp2_session *session, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider2 *data_prd) { nghttp2_data_provider_wrap dpw; return submit_response_shared(session, stream_id, nva, nvlen, nghttp2_data_provider_wrap_v2(&dpw, data_prd)); } int nghttp2_submit_data_shared(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_data_provider_wrap *dpw) { int rv; nghttp2_outbound_item *item; nghttp2_frame *frame; nghttp2_data_aux_data *aux_data; uint8_t nflags = flags & NGHTTP2_FLAG_END_STREAM; nghttp2_mem *mem; mem = &session->mem; if (stream_id == 0) { return NGHTTP2_ERR_INVALID_ARGUMENT; } item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); if (item == NULL) { return NGHTTP2_ERR_NOMEM; } nghttp2_outbound_item_init(item); frame = &item->frame; aux_data = &item->aux_data.data; aux_data->dpw = *dpw; aux_data->eof = 0; aux_data->flags = nflags; /* flags are sent on transmission */ nghttp2_frame_data_init(&frame->data, NGHTTP2_FLAG_NONE, stream_id); rv = nghttp2_session_add_item(session, item); if (rv != 0) { nghttp2_frame_data_free(&frame->data); nghttp2_mem_free(mem, item); return rv; } return 0; } int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_data_provider *data_prd) { nghttp2_data_provider_wrap dpw; assert(data_prd); return nghttp2_submit_data_shared( session, flags, stream_id, nghttp2_data_provider_wrap_v1(&dpw, data_prd)); } int nghttp2_submit_data2(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_data_provider2 *data_prd) { nghttp2_data_provider_wrap dpw; assert(data_prd); return nghttp2_submit_data_shared( session, flags, stream_id, nghttp2_data_provider_wrap_v2(&dpw, data_prd)); } ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen, const nghttp2_settings_entry *iv, size_t niv) { return (ssize_t)nghttp2_pack_settings_payload2(buf, buflen, iv, niv); } nghttp2_ssize nghttp2_pack_settings_payload2(uint8_t *buf, size_t buflen, const nghttp2_settings_entry *iv, size_t niv) { if (!nghttp2_iv_check(iv, niv)) { return NGHTTP2_ERR_INVALID_ARGUMENT; } if (buflen < (niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH)) { return NGHTTP2_ERR_INSUFF_BUFSIZE; } return (nghttp2_ssize)nghttp2_frame_pack_settings_payload(buf, iv, niv); } int nghttp2_submit_extension(nghttp2_session *session, uint8_t type, uint8_t flags, int32_t stream_id, void *payload) { int rv; nghttp2_outbound_item *item; nghttp2_frame *frame; nghttp2_mem *mem; mem = &session->mem; if (type <= NGHTTP2_CONTINUATION) { return NGHTTP2_ERR_INVALID_ARGUMENT; } if (!session->callbacks.pack_extension_callback2 && !session->callbacks.pack_extension_callback) { return NGHTTP2_ERR_INVALID_STATE; } item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); if (item == NULL) { return NGHTTP2_ERR_NOMEM; } nghttp2_outbound_item_init(item); frame = &item->frame; nghttp2_frame_extension_init(&frame->ext, type, flags, stream_id, payload); rv = nghttp2_session_add_item(session, item); if (rv != 0) { nghttp2_frame_extension_free(&frame->ext); nghttp2_mem_free(mem, item); return rv; } return 0; } nghttp2-1.68.0/lib/PaxHeaders/nghttp2_submit.h0000644000000000000000000000013215077107270016150 xustar0030 mtime=1761382072.981444194 30 atime=1761382104.943315982 30 ctime=1761382107.929303766 nghttp2-1.68.0/lib/nghttp2_submit.h0000644000175100017510000000306715077107270016546 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_SUBMIT_H #define NGHTTP2_SUBMIT_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include #include "nghttp2_outbound_item.h" int nghttp2_submit_data_shared(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_data_provider_wrap *dpw); #endif /* !defined(NGHTTP2_SUBMIT_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_option.h0000644000000000000000000000013215077107270016155 xustar0030 mtime=1761382072.979444204 30 atime=1761382104.951315947 30 ctime=1761382107.937303743 nghttp2-1.68.0/lib/nghttp2_option.h0000644000175100017510000001210015077107270016537 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_OPTION_H #define NGHTTP2_OPTION_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include /** * Configuration options */ typedef enum { /** * This option prevents the library from sending WINDOW_UPDATE for a * connection automatically. If this option is set to nonzero, the * library won't send WINDOW_UPDATE for DATA until application calls * nghttp2_session_consume() to indicate the amount of consumed * DATA. By default, this option is set to zero. */ NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE = 1, /** * This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of * remote endpoint as if it is received in SETTINGS frame. Without * specifying this option, before the local endpoint receives * SETTINGS_MAX_CONCURRENT_STREAMS in SETTINGS frame from remote * endpoint, SETTINGS_MAX_CONCURRENT_STREAMS is unlimited. This may * cause problem if local endpoint submits lots of requests * initially and sending them at once to the remote peer may lead to * the rejection of some requests. Specifying this option to the * sensible value, say 100, may avoid this kind of issue. This value * will be overwritten if the local endpoint receives * SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint. */ NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1, NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC = 1 << 2, NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3, NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS = 1 << 4, NGHTTP2_OPT_USER_RECV_EXT_TYPES = 1 << 5, NGHTTP2_OPT_NO_AUTO_PING_ACK = 1 << 6, NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES = 1 << 7, NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH = 1 << 8, NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE = 1 << 9, NGHTTP2_OPT_NO_CLOSED_STREAMS = 1 << 10, NGHTTP2_OPT_MAX_OUTBOUND_ACK = 1 << 11, NGHTTP2_OPT_MAX_SETTINGS = 1 << 12, NGHTTP2_OPT_SERVER_FALLBACK_RFC7540_PRIORITIES = 1 << 13, NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION = 1 << 14, NGHTTP2_OPT_STREAM_RESET_RATE_LIMIT = 1 << 15, NGHTTP2_OPT_MAX_CONTINUATIONS = 1 << 16, NGHTTP2_OPT_GLITCH_RATE_LIMIT = 1 << 17, } nghttp2_option_flag; /** * Struct to store option values for nghttp2_session. */ struct nghttp2_option { /** * NGHTTP2_OPT_STREAM_RESET_RATE_LIMIT */ uint64_t stream_reset_burst; uint64_t stream_reset_rate; /** * NGHTTP2_OPT_GLITCH_RATE_LIMIT */ uint64_t glitch_burst; uint64_t glitch_rate; /** * NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH */ size_t max_send_header_block_length; /** * NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE */ size_t max_deflate_dynamic_table_size; /** * NGHTTP2_OPT_MAX_OUTBOUND_ACK */ size_t max_outbound_ack; /** * NGHTTP2_OPT_MAX_SETTINGS */ size_t max_settings; /** * NGHTTP2_OPT_MAX_CONTINUATIONS */ size_t max_continuations; /** * Bitwise OR of nghttp2_option_flag to determine that which fields * are specified. */ uint32_t opt_set_mask; /** * NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS */ uint32_t peer_max_concurrent_streams; /** * NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS */ uint32_t max_reserved_remote_streams; /** * NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES */ uint32_t builtin_recv_ext_types; /** * NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE */ int no_auto_window_update; /** * NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC */ int no_recv_client_magic; /** * NGHTTP2_OPT_NO_HTTP_MESSAGING */ int no_http_messaging; /** * NGHTTP2_OPT_NO_AUTO_PING_ACK */ int no_auto_ping_ack; /** * NGHTTP2_OPT_NO_CLOSED_STREAMS */ int no_closed_streams; /** * NGHTTP2_OPT_SERVER_FALLBACK_RFC7540_PRIORITIES */ int server_fallback_rfc7540_priorities; /** * NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION */ int no_rfc9113_leading_and_trailing_ws_validation; /** * NGHTTP2_OPT_USER_RECV_EXT_TYPES */ uint8_t user_recv_ext_types[32]; }; #endif /* !defined(NGHTTP2_OPTION_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_hd.h0000644000000000000000000000013215077107270015240 xustar0030 mtime=1761382072.978444208 30 atime=1761382104.947315964 30 ctime=1761382107.933303755 nghttp2-1.68.0/lib/nghttp2_hd.h0000644000175100017510000003443415077107270015640 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_HD_H #define NGHTTP2_HD_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include #include "nghttp2_hd_huffman.h" #include "nghttp2_buf.h" #include "nghttp2_mem.h" #include "nghttp2_rcbuf.h" #define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE NGHTTP2_DEFAULT_HEADER_TABLE_SIZE #define NGHTTP2_HD_ENTRY_OVERHEAD 32 /* The maximum length of one name/value pair. This is the sum of the length of name and value. This is not specified by the spec. We just chose the arbitrary size */ #define NGHTTP2_HD_MAX_NV 65536 /* Default size of maximum table buffer size for encoder. Even if remote decoder notifies larger buffer size for its decoding, encoder only uses the memory up to this value. */ #define NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE (1 << 12) /* Exported for unit test */ #define NGHTTP2_STATIC_TABLE_LENGTH 61 /* Generated by genlibtokenlookup.py */ typedef enum { NGHTTP2_TOKEN__AUTHORITY = 0, NGHTTP2_TOKEN__METHOD = 1, NGHTTP2_TOKEN__PATH = 3, NGHTTP2_TOKEN__SCHEME = 5, NGHTTP2_TOKEN__STATUS = 7, NGHTTP2_TOKEN_ACCEPT_CHARSET = 14, NGHTTP2_TOKEN_ACCEPT_ENCODING = 15, NGHTTP2_TOKEN_ACCEPT_LANGUAGE = 16, NGHTTP2_TOKEN_ACCEPT_RANGES = 17, NGHTTP2_TOKEN_ACCEPT = 18, NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN = 19, NGHTTP2_TOKEN_AGE = 20, NGHTTP2_TOKEN_ALLOW = 21, NGHTTP2_TOKEN_AUTHORIZATION = 22, NGHTTP2_TOKEN_CACHE_CONTROL = 23, NGHTTP2_TOKEN_CONTENT_DISPOSITION = 24, NGHTTP2_TOKEN_CONTENT_ENCODING = 25, NGHTTP2_TOKEN_CONTENT_LANGUAGE = 26, NGHTTP2_TOKEN_CONTENT_LENGTH = 27, NGHTTP2_TOKEN_CONTENT_LOCATION = 28, NGHTTP2_TOKEN_CONTENT_RANGE = 29, NGHTTP2_TOKEN_CONTENT_TYPE = 30, NGHTTP2_TOKEN_COOKIE = 31, NGHTTP2_TOKEN_DATE = 32, NGHTTP2_TOKEN_ETAG = 33, NGHTTP2_TOKEN_EXPECT = 34, NGHTTP2_TOKEN_EXPIRES = 35, NGHTTP2_TOKEN_FROM = 36, NGHTTP2_TOKEN_HOST = 37, NGHTTP2_TOKEN_IF_MATCH = 38, NGHTTP2_TOKEN_IF_MODIFIED_SINCE = 39, NGHTTP2_TOKEN_IF_NONE_MATCH = 40, NGHTTP2_TOKEN_IF_RANGE = 41, NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE = 42, NGHTTP2_TOKEN_LAST_MODIFIED = 43, NGHTTP2_TOKEN_LINK = 44, NGHTTP2_TOKEN_LOCATION = 45, NGHTTP2_TOKEN_MAX_FORWARDS = 46, NGHTTP2_TOKEN_PROXY_AUTHENTICATE = 47, NGHTTP2_TOKEN_PROXY_AUTHORIZATION = 48, NGHTTP2_TOKEN_RANGE = 49, NGHTTP2_TOKEN_REFERER = 50, NGHTTP2_TOKEN_REFRESH = 51, NGHTTP2_TOKEN_RETRY_AFTER = 52, NGHTTP2_TOKEN_SERVER = 53, NGHTTP2_TOKEN_SET_COOKIE = 54, NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY = 55, NGHTTP2_TOKEN_TRANSFER_ENCODING = 56, NGHTTP2_TOKEN_USER_AGENT = 57, NGHTTP2_TOKEN_VARY = 58, NGHTTP2_TOKEN_VIA = 59, NGHTTP2_TOKEN_WWW_AUTHENTICATE = 60, NGHTTP2_TOKEN_TE, NGHTTP2_TOKEN_CONNECTION, NGHTTP2_TOKEN_KEEP_ALIVE, NGHTTP2_TOKEN_PROXY_CONNECTION, NGHTTP2_TOKEN_UPGRADE, NGHTTP2_TOKEN__PROTOCOL, NGHTTP2_TOKEN_PRIORITY, } nghttp2_token; struct nghttp2_hd_entry; typedef struct nghttp2_hd_entry nghttp2_hd_entry; typedef struct { /* The buffer containing header field name. NULL-termination is guaranteed. */ nghttp2_rcbuf *name; /* The buffer containing header field value. NULL-termination is guaranteed. */ nghttp2_rcbuf *value; /* nghttp2_token value for name. It could be -1 if we have no token for that header field name. */ int32_t token; /* Bitwise OR of one or more of nghttp2_nv_flag. */ uint8_t flags; } nghttp2_hd_nv; struct nghttp2_hd_entry { /* The header field name/value pair */ nghttp2_hd_nv nv; /* This is solely for nghttp2_hd_{deflate,inflate}_get_table_entry APIs to keep backward compatibility. */ nghttp2_nv cnv; /* The next entry which shares same bucket in hash table. */ nghttp2_hd_entry *next; /* The sequence number. We will increment it by one whenever we store nghttp2_hd_entry to dynamic header table. */ uint32_t seq; /* The hash value for header name (nv.name). */ uint32_t hash; }; /* The entry used for static header table. */ typedef struct { nghttp2_rcbuf name; nghttp2_rcbuf value; nghttp2_nv cnv; int32_t token; uint32_t hash; } nghttp2_hd_static_entry; typedef struct { nghttp2_hd_entry **buffer; size_t mask; size_t first; size_t len; } nghttp2_hd_ringbuf; typedef enum { NGHTTP2_HD_OPCODE_NONE, NGHTTP2_HD_OPCODE_INDEXED, NGHTTP2_HD_OPCODE_NEWNAME, NGHTTP2_HD_OPCODE_INDNAME } nghttp2_hd_opcode; typedef enum { NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE, NGHTTP2_HD_STATE_INFLATE_START, NGHTTP2_HD_STATE_OPCODE, NGHTTP2_HD_STATE_READ_TABLE_SIZE, NGHTTP2_HD_STATE_READ_INDEX, NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN, NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN, NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF, NGHTTP2_HD_STATE_NEWNAME_READ_NAME, NGHTTP2_HD_STATE_CHECK_VALUELEN, NGHTTP2_HD_STATE_READ_VALUELEN, NGHTTP2_HD_STATE_READ_VALUEHUFF, NGHTTP2_HD_STATE_READ_VALUE } nghttp2_hd_inflate_state; typedef enum { NGHTTP2_HD_WITH_INDEXING, NGHTTP2_HD_WITHOUT_INDEXING, NGHTTP2_HD_NEVER_INDEXING } nghttp2_hd_indexing_mode; typedef struct { /* dynamic header table */ nghttp2_hd_ringbuf hd_table; /* Memory allocator */ nghttp2_mem *mem; /* Abstract buffer size of hd_table as described in the spec. This is the sum of length of name/value in hd_table + NGHTTP2_HD_ENTRY_OVERHEAD bytes overhead per each entry. */ size_t hd_table_bufsize; /* The effective header table size. */ size_t hd_table_bufsize_max; /* Next sequence number for nghttp2_hd_entry */ uint32_t next_seq; /* If inflate/deflate error occurred, this value is set to 1 and further invocation of inflate/deflate will fail with NGHTTP2_ERR_HEADER_COMP. */ uint8_t bad; } nghttp2_hd_context; #define HD_MAP_SIZE 128 typedef struct { nghttp2_hd_entry *table[HD_MAP_SIZE]; } nghttp2_hd_map; struct nghttp2_hd_deflater { nghttp2_hd_context ctx; nghttp2_hd_map map; /* The upper limit of the header table size the deflater accepts. */ size_t deflate_hd_table_bufsize_max; /* Minimum header table size notified in the next context update */ size_t min_hd_table_bufsize_max; /* If nonzero, send header table size using encoding context update in the next deflate process */ uint8_t notify_table_size_change; }; struct nghttp2_hd_inflater { nghttp2_hd_context ctx; /* Stores current state of huffman decoding */ nghttp2_hd_huff_decode_context huff_decode_ctx; /* header buffer */ nghttp2_buf namebuf, valuebuf; nghttp2_rcbuf *namercbuf, *valuercbuf; /* Pointer to the name/value pair which are used in the current header emission. */ nghttp2_rcbuf *nv_name_keep, *nv_value_keep; /* The number of bytes to read */ size_t left; /* The index in indexed repr or indexed name */ size_t index; /* The maximum header table size the inflater supports. This is the same value transmitted in SETTINGS_HEADER_TABLE_SIZE */ size_t settings_hd_table_bufsize_max; /* Minimum header table size set by nghttp2_hd_inflate_change_table_size */ size_t min_hd_table_bufsize_max; /* The number of next shift to decode integer */ size_t shift; nghttp2_hd_opcode opcode; nghttp2_hd_inflate_state state; /* nonzero if string is huffman encoded */ uint8_t huffman_encoded; /* nonzero if deflater requires that current entry is indexed */ uint8_t index_required; /* nonzero if deflater requires that current entry must not be indexed */ uint8_t no_index; }; /* * Initializes the |ent| members. The reference counts of nv->name * and nv->value are increased by one for each. */ void nghttp2_hd_entry_init(nghttp2_hd_entry *ent, nghttp2_hd_nv *nv); /* * This function decreases the reference counts of nv->name and * nv->value. */ void nghttp2_hd_entry_free(nghttp2_hd_entry *ent); /* * Initializes |deflater| for deflating name/values pairs. * * The encoder only uses up to * NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE bytes for header table * even if the larger value is specified later in * nghttp2_hd_change_table_size(). * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. */ int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_mem *mem); /* * Initializes |deflater| for deflating name/values pairs. * * The encoder only uses up to |max_deflate_dynamic_table_size| bytes * for header table even if the larger value is specified later in * nghttp2_hd_change_table_size(). * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. */ int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater, size_t max_deflate_dynamic_table_size, nghttp2_mem *mem); /* * Deallocates any resources allocated for |deflater|. */ void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater); /* * Deflates the |nva|, which has the |nvlen| name/value pairs, into * the |bufs|. * * This function expands |bufs| as necessary to store the result. If * buffers is full and the process still requires more space, this * function fails and returns NGHTTP2_ERR_HEADER_COMP. * * After this function returns, it is safe to delete the |nva|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_HEADER_COMP * Deflation process has failed. * NGHTTP2_ERR_BUFFER_ERROR * Out of buffer space. */ int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, const nghttp2_nv *nva, size_t nvlen); /* * Initializes |inflater| for inflating name/values pairs. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem); /* * Deallocates any resources allocated for |inflater|. */ void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater); /* * Similar to nghttp2_hd_inflate_hd(), but this takes nghttp2_hd_nv * instead of nghttp2_nv as output parameter |nv_out|. Other than * that return values and semantics are the same as * nghttp2_hd_inflate_hd(). */ nghttp2_ssize nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, nghttp2_hd_nv *nv_out, int *inflate_flags, const uint8_t *in, size_t inlen, int in_final); /* For unittesting purpose */ int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index, nghttp2_nv *nv, int indexing_mode); /* For unittesting purpose */ int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv, int indexing_mode); /* For unittesting purpose */ int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size); /* For unittesting purpose */ nghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t index); /* For unittesting purpose */ nghttp2_ssize nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *fin, uint32_t initial, size_t shift, uint8_t *in, uint8_t *last, size_t prefix); /* Huffman encoding/decoding functions */ /* * Counts the required bytes to encode |src| with length |len|. * * This function returns the number of required bytes to encode given * data, including padding of prefix of terminal symbol code. This * function always succeeds. */ size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len); /* * Encodes the given data |src| with length |srclen| to the |bufs|. * This function expands extra buffers in |bufs| if necessary. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_BUFFER_ERROR * Out of buffer space. */ int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src, size_t srclen); void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx); /* * Decodes the given data |src| with length |srclen|. The |ctx| must * be initialized by nghttp2_hd_huff_decode_context_init(). The result * will be written to |buf|. This function assumes that |buf| has the * enough room to store the decoded byte string. * * The caller must set the |fin| to nonzero if the given input is the * final block. * * This function returns the number of read bytes from the |in|. * * If this function fails, it returns one of the following negative * return codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_HEADER_COMP * Decoding process has failed. */ nghttp2_ssize nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, nghttp2_buf *buf, const uint8_t *src, size_t srclen, int fin); /* * nghttp2_hd_huff_decode_failure_state returns nonzero if |ctx| * indicates that huffman decoding context is in failure state. */ int nghttp2_hd_huff_decode_failure_state(nghttp2_hd_huff_decode_context *ctx); #endif /* !defined(NGHTTP2_HD_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_buf.c0000644000000000000000000000013215077107270015414 xustar0030 mtime=1761382072.976444217 30 atime=1761382104.968315872 30 ctime=1761382107.955303691 nghttp2-1.68.0/lib/nghttp2_buf.c0000644000175100017510000002567115077107270016017 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_buf.h" #include #include "nghttp2_helper.h" #include "nghttp2_debug.h" void nghttp2_buf_init(nghttp2_buf *buf) { buf->begin = NULL; buf->end = NULL; buf->pos = NULL; buf->last = NULL; buf->mark = NULL; } int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial, nghttp2_mem *mem) { nghttp2_buf_init(buf); return nghttp2_buf_reserve(buf, initial, mem); } void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem) { if (buf == NULL) { return; } nghttp2_mem_free(mem, buf->begin); buf->begin = NULL; } int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem) { uint8_t *ptr; size_t cap; cap = nghttp2_buf_cap(buf); if (cap >= new_cap) { return 0; } new_cap = nghttp2_max_size(new_cap, cap * 2); ptr = nghttp2_mem_realloc(mem, buf->begin, new_cap); if (ptr == NULL) { return NGHTTP2_ERR_NOMEM; } buf->pos = ptr + (buf->pos - buf->begin); buf->last = ptr + (buf->last - buf->begin); buf->mark = ptr + (buf->mark - buf->begin); buf->begin = ptr; buf->end = ptr + new_cap; return 0; } void nghttp2_buf_reset(nghttp2_buf *buf) { buf->pos = buf->last = buf->mark = buf->begin; } void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len) { buf->begin = buf->pos = buf->last = buf->mark = buf->end = begin; if (len) { buf->end += len; } } static int buf_chain_new(nghttp2_buf_chain **chain, size_t chunk_length, nghttp2_mem *mem) { int rv; *chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain)); if (*chain == NULL) { return NGHTTP2_ERR_NOMEM; } (*chain)->next = NULL; rv = nghttp2_buf_init2(&(*chain)->buf, chunk_length, mem); if (rv != 0) { nghttp2_mem_free(mem, *chain); return NGHTTP2_ERR_NOMEM; } return 0; } static void buf_chain_del(nghttp2_buf_chain *chain, nghttp2_mem *mem) { nghttp2_buf_free(&chain->buf, mem); nghttp2_mem_free(mem, chain); } int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk, nghttp2_mem *mem) { return nghttp2_bufs_init2(bufs, chunk_length, max_chunk, 0, mem); } int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk, size_t offset, nghttp2_mem *mem) { return nghttp2_bufs_init3(bufs, chunk_length, max_chunk, max_chunk, offset, mem); } int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk, size_t chunk_keep, size_t offset, nghttp2_mem *mem) { int rv; nghttp2_buf_chain *chain; if (chunk_keep == 0 || max_chunk < chunk_keep || chunk_length < offset) { return NGHTTP2_ERR_INVALID_ARGUMENT; } rv = buf_chain_new(&chain, chunk_length, mem); if (rv != 0) { return rv; } bufs->mem = mem; bufs->offset = offset; bufs->head = chain; bufs->cur = bufs->head; nghttp2_buf_shift_right(&bufs->cur->buf, offset); bufs->chunk_length = chunk_length; bufs->chunk_used = 1; bufs->max_chunk = max_chunk; bufs->chunk_keep = chunk_keep; return 0; } int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length) { int rv; nghttp2_buf_chain *chain; if (chunk_length < bufs->offset) { return NGHTTP2_ERR_INVALID_ARGUMENT; } rv = buf_chain_new(&chain, chunk_length, bufs->mem); if (rv != 0) { return rv; } nghttp2_bufs_free(bufs); bufs->head = chain; bufs->cur = bufs->head; nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset); bufs->chunk_length = chunk_length; bufs->chunk_used = 1; return 0; } void nghttp2_bufs_free(nghttp2_bufs *bufs) { nghttp2_buf_chain *chain, *next_chain; if (bufs == NULL) { return; } for (chain = bufs->head; chain;) { next_chain = chain->next; buf_chain_del(chain, bufs->mem); chain = next_chain; } bufs->head = NULL; } int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len, nghttp2_mem *mem) { nghttp2_buf_chain *chain; chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain)); if (chain == NULL) { return NGHTTP2_ERR_NOMEM; } chain->next = NULL; nghttp2_buf_wrap_init(&chain->buf, begin, len); bufs->mem = mem; bufs->offset = 0; bufs->head = chain; bufs->cur = bufs->head; bufs->chunk_length = len; bufs->chunk_used = 1; bufs->max_chunk = 1; bufs->chunk_keep = 1; return 0; } int nghttp2_bufs_wrap_init2(nghttp2_bufs *bufs, const nghttp2_vec *vec, size_t veclen, nghttp2_mem *mem) { size_t i = 0; nghttp2_buf_chain *cur_chain; nghttp2_buf_chain *head_chain; nghttp2_buf_chain **dst_chain = &head_chain; if (veclen == 0) { return nghttp2_bufs_wrap_init(bufs, NULL, 0, mem); } head_chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain) * veclen); if (head_chain == NULL) { return NGHTTP2_ERR_NOMEM; } for (i = 0; i < veclen; ++i) { cur_chain = &head_chain[i]; cur_chain->next = NULL; nghttp2_buf_wrap_init(&cur_chain->buf, vec[i].base, vec[i].len); *dst_chain = cur_chain; dst_chain = &cur_chain->next; } bufs->mem = mem; bufs->offset = 0; bufs->head = head_chain; bufs->cur = bufs->head; /* We don't use chunk_length since no allocation is expected. */ bufs->chunk_length = 0; bufs->chunk_used = veclen; bufs->max_chunk = veclen; bufs->chunk_keep = veclen; return 0; } void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs) { if (bufs == NULL) { return; } if (bufs->head) { nghttp2_mem_free(bufs->mem, bufs->head); } } void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs) { nghttp2_buf_chain *ci; for (ci = bufs->cur; ci; ci = ci->next) { if (nghttp2_buf_len(&ci->buf) == 0) { return; } else { bufs->cur = ci; } } } size_t nghttp2_bufs_len(nghttp2_bufs *bufs) { nghttp2_buf_chain *ci; size_t len; len = 0; for (ci = bufs->head; ci; ci = ci->next) { len += nghttp2_buf_len(&ci->buf); } return len; } static int bufs_alloc_chain(nghttp2_bufs *bufs) { int rv; nghttp2_buf_chain *chain; if (bufs->cur->next) { bufs->cur = bufs->cur->next; return 0; } if (bufs->max_chunk == bufs->chunk_used) { return NGHTTP2_ERR_BUFFER_ERROR; } rv = buf_chain_new(&chain, bufs->chunk_length, bufs->mem); if (rv != 0) { return rv; } DEBUGF("new buffer %zu bytes allocated for bufs %p, used %zu\n", bufs->chunk_length, bufs, bufs->chunk_used); ++bufs->chunk_used; bufs->cur->next = chain; bufs->cur = chain; nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset); return 0; } int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len) { int rv; size_t nwrite; nghttp2_buf *buf; const uint8_t *p; p = data; while (len) { buf = &bufs->cur->buf; nwrite = nghttp2_min_size(nghttp2_buf_avail(buf), len); if (nwrite == 0) { rv = bufs_alloc_chain(bufs); if (rv != 0) { return rv; } continue; } buf->last = nghttp2_cpymem(buf->last, p, nwrite); p += nwrite; len -= nwrite; } return 0; } static int bufs_ensure_addb(nghttp2_bufs *bufs) { int rv; nghttp2_buf *buf; buf = &bufs->cur->buf; if (nghttp2_buf_avail(buf) > 0) { return 0; } rv = bufs_alloc_chain(bufs); if (rv != 0) { return rv; } return 0; } int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b) { int rv; rv = bufs_ensure_addb(bufs); if (rv != 0) { return rv; } *bufs->cur->buf.last++ = b; return 0; } int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b) { int rv; rv = bufs_ensure_addb(bufs); if (rv != 0) { return rv; } *bufs->cur->buf.last = b; return 0; } int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b) { int rv; rv = bufs_ensure_addb(bufs); if (rv != 0) { return rv; } *bufs->cur->buf.last++ |= b; return 0; } int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b) { int rv; rv = bufs_ensure_addb(bufs); if (rv != 0) { return rv; } *bufs->cur->buf.last |= b; return 0; } nghttp2_ssize nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) { size_t len; nghttp2_buf_chain *chain; nghttp2_buf *buf; uint8_t *res; nghttp2_buf resbuf; len = 0; for (chain = bufs->head; chain; chain = chain->next) { len += nghttp2_buf_len(&chain->buf); } if (len == 0) { res = NULL; return 0; } res = nghttp2_mem_malloc(bufs->mem, len); if (res == NULL) { return NGHTTP2_ERR_NOMEM; } nghttp2_buf_wrap_init(&resbuf, res, len); for (chain = bufs->head; chain; chain = chain->next) { buf = &chain->buf; resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf)); } *out = res; return (nghttp2_ssize)len; } size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out) { size_t len; nghttp2_buf_chain *chain; nghttp2_buf *buf; nghttp2_buf resbuf; len = nghttp2_bufs_len(bufs); nghttp2_buf_wrap_init(&resbuf, out, len); for (chain = bufs->head; chain; chain = chain->next) { buf = &chain->buf; resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf)); } return len; } void nghttp2_bufs_reset(nghttp2_bufs *bufs) { nghttp2_buf_chain *chain, *ci; size_t k; k = bufs->chunk_keep; for (ci = bufs->head; ci; ci = ci->next) { nghttp2_buf_reset(&ci->buf); nghttp2_buf_shift_right(&ci->buf, bufs->offset); if (--k == 0) { break; } } if (ci) { chain = ci->next; ci->next = NULL; for (ci = chain; ci;) { chain = ci->next; buf_chain_del(ci, bufs->mem); ci = chain; } bufs->chunk_used = bufs->chunk_keep; } bufs->cur = bufs->head; } int nghttp2_bufs_advance(nghttp2_bufs *bufs) { return bufs_alloc_chain(bufs); } int nghttp2_bufs_next_present(nghttp2_bufs *bufs) { nghttp2_buf_chain *chain; chain = bufs->cur->next; return chain && nghttp2_buf_len(&chain->buf); } nghttp2-1.68.0/lib/PaxHeaders/nghttp2_helper.c0000644000000000000000000000013215077107270016117 xustar0030 mtime=1761382072.979444204 30 atime=1761382104.975315841 30 ctime=1761382107.961303673 nghttp2-1.68.0/lib/nghttp2_helper.c0000644000175100017510000010531715077107270016516 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_helper.h" #include #include #include "nghttp2_net.h" void nghttp2_put_uint16be(uint8_t *buf, uint16_t n) { uint16_t x = htons(n); memcpy(buf, &x, sizeof(uint16_t)); } void nghttp2_put_uint32be(uint8_t *buf, uint32_t n) { uint32_t x = htonl(n); memcpy(buf, &x, sizeof(uint32_t)); } uint16_t nghttp2_get_uint16(const uint8_t *data) { uint16_t n; memcpy(&n, data, sizeof(uint16_t)); return ntohs(n); } uint32_t nghttp2_get_uint32(const uint8_t *data) { uint32_t n; memcpy(&n, data, sizeof(uint32_t)); return ntohl(n); } /* Generated by gendowncasetbl.py */ static const uint8_t DOWNCASE_TBL[] = { 0 /* NUL */, 1 /* SOH */, 2 /* STX */, 3 /* ETX */, 4 /* EOT */, 5 /* ENQ */, 6 /* ACK */, 7 /* BEL */, 8 /* BS */, 9 /* HT */, 10 /* LF */, 11 /* VT */, 12 /* FF */, 13 /* CR */, 14 /* SO */, 15 /* SI */, 16 /* DLE */, 17 /* DC1 */, 18 /* DC2 */, 19 /* DC3 */, 20 /* DC4 */, 21 /* NAK */, 22 /* SYN */, 23 /* ETB */, 24 /* CAN */, 25 /* EM */, 26 /* SUB */, 27 /* ESC */, 28 /* FS */, 29 /* GS */, 30 /* RS */, 31 /* US */, 32 /* SPC */, 33 /* ! */, 34 /* " */, 35 /* # */, 36 /* $ */, 37 /* % */, 38 /* & */, 39 /* ' */, 40 /* ( */, 41 /* ) */, 42 /* * */, 43 /* + */, 44 /* , */, 45 /* - */, 46 /* . */, 47 /* / */, 48 /* 0 */, 49 /* 1 */, 50 /* 2 */, 51 /* 3 */, 52 /* 4 */, 53 /* 5 */, 54 /* 6 */, 55 /* 7 */, 56 /* 8 */, 57 /* 9 */, 58 /* : */, 59 /* ; */, 60 /* < */, 61 /* = */, 62 /* > */, 63 /* ? */, 64 /* @ */, 97 /* A */, 98 /* B */, 99 /* C */, 100 /* D */, 101 /* E */, 102 /* F */, 103 /* G */, 104 /* H */, 105 /* I */, 106 /* J */, 107 /* K */, 108 /* L */, 109 /* M */, 110 /* N */, 111 /* O */, 112 /* P */, 113 /* Q */, 114 /* R */, 115 /* S */, 116 /* T */, 117 /* U */, 118 /* V */, 119 /* W */, 120 /* X */, 121 /* Y */, 122 /* Z */, 91 /* [ */, 92 /* \ */, 93 /* ] */, 94 /* ^ */, 95 /* _ */, 96 /* ` */, 97 /* a */, 98 /* b */, 99 /* c */, 100 /* d */, 101 /* e */, 102 /* f */, 103 /* g */, 104 /* h */, 105 /* i */, 106 /* j */, 107 /* k */, 108 /* l */, 109 /* m */, 110 /* n */, 111 /* o */, 112 /* p */, 113 /* q */, 114 /* r */, 115 /* s */, 116 /* t */, 117 /* u */, 118 /* v */, 119 /* w */, 120 /* x */, 121 /* y */, 122 /* z */, 123 /* { */, 124 /* | */, 125 /* } */, 126 /* ~ */, 127 /* DEL */, 128 /* 0x80 */, 129 /* 0x81 */, 130 /* 0x82 */, 131 /* 0x83 */, 132 /* 0x84 */, 133 /* 0x85 */, 134 /* 0x86 */, 135 /* 0x87 */, 136 /* 0x88 */, 137 /* 0x89 */, 138 /* 0x8a */, 139 /* 0x8b */, 140 /* 0x8c */, 141 /* 0x8d */, 142 /* 0x8e */, 143 /* 0x8f */, 144 /* 0x90 */, 145 /* 0x91 */, 146 /* 0x92 */, 147 /* 0x93 */, 148 /* 0x94 */, 149 /* 0x95 */, 150 /* 0x96 */, 151 /* 0x97 */, 152 /* 0x98 */, 153 /* 0x99 */, 154 /* 0x9a */, 155 /* 0x9b */, 156 /* 0x9c */, 157 /* 0x9d */, 158 /* 0x9e */, 159 /* 0x9f */, 160 /* 0xa0 */, 161 /* 0xa1 */, 162 /* 0xa2 */, 163 /* 0xa3 */, 164 /* 0xa4 */, 165 /* 0xa5 */, 166 /* 0xa6 */, 167 /* 0xa7 */, 168 /* 0xa8 */, 169 /* 0xa9 */, 170 /* 0xaa */, 171 /* 0xab */, 172 /* 0xac */, 173 /* 0xad */, 174 /* 0xae */, 175 /* 0xaf */, 176 /* 0xb0 */, 177 /* 0xb1 */, 178 /* 0xb2 */, 179 /* 0xb3 */, 180 /* 0xb4 */, 181 /* 0xb5 */, 182 /* 0xb6 */, 183 /* 0xb7 */, 184 /* 0xb8 */, 185 /* 0xb9 */, 186 /* 0xba */, 187 /* 0xbb */, 188 /* 0xbc */, 189 /* 0xbd */, 190 /* 0xbe */, 191 /* 0xbf */, 192 /* 0xc0 */, 193 /* 0xc1 */, 194 /* 0xc2 */, 195 /* 0xc3 */, 196 /* 0xc4 */, 197 /* 0xc5 */, 198 /* 0xc6 */, 199 /* 0xc7 */, 200 /* 0xc8 */, 201 /* 0xc9 */, 202 /* 0xca */, 203 /* 0xcb */, 204 /* 0xcc */, 205 /* 0xcd */, 206 /* 0xce */, 207 /* 0xcf */, 208 /* 0xd0 */, 209 /* 0xd1 */, 210 /* 0xd2 */, 211 /* 0xd3 */, 212 /* 0xd4 */, 213 /* 0xd5 */, 214 /* 0xd6 */, 215 /* 0xd7 */, 216 /* 0xd8 */, 217 /* 0xd9 */, 218 /* 0xda */, 219 /* 0xdb */, 220 /* 0xdc */, 221 /* 0xdd */, 222 /* 0xde */, 223 /* 0xdf */, 224 /* 0xe0 */, 225 /* 0xe1 */, 226 /* 0xe2 */, 227 /* 0xe3 */, 228 /* 0xe4 */, 229 /* 0xe5 */, 230 /* 0xe6 */, 231 /* 0xe7 */, 232 /* 0xe8 */, 233 /* 0xe9 */, 234 /* 0xea */, 235 /* 0xeb */, 236 /* 0xec */, 237 /* 0xed */, 238 /* 0xee */, 239 /* 0xef */, 240 /* 0xf0 */, 241 /* 0xf1 */, 242 /* 0xf2 */, 243 /* 0xf3 */, 244 /* 0xf4 */, 245 /* 0xf5 */, 246 /* 0xf6 */, 247 /* 0xf7 */, 248 /* 0xf8 */, 249 /* 0xf9 */, 250 /* 0xfa */, 251 /* 0xfb */, 252 /* 0xfc */, 253 /* 0xfd */, 254 /* 0xfe */, 255 /* 0xff */, }; void nghttp2_downcase(uint8_t *s, size_t len) { size_t i; for (i = 0; i < len; ++i) { s[i] = DOWNCASE_TBL[s[i]]; } } /* * local_window_size * ^ * * | * recv_window_size * | * * ^ * | * * | * 0+++++++++ * | * * \ * | * * | This rage is hidden in flow control. But it must be * v * * / kept in order to restore it when window size is enlarged. * recv_reduction * (+ for negative direction) * * recv_window_size could be negative if we decrease * local_window_size more than recv_window_size: * * local_window_size * ^ * * | * * | * * 0++++++++ * | * ^ recv_window_size (negative) * | * | * v * * * recv_reduction */ int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr, int32_t *recv_window_size_ptr, int32_t *recv_reduction_ptr, int32_t *delta_ptr) { if (*delta_ptr > 0) { int32_t recv_reduction_delta; int32_t delta; int32_t new_recv_window_size = nghttp2_max_int32(0, *recv_window_size_ptr) - *delta_ptr; if (new_recv_window_size >= 0) { *recv_window_size_ptr = new_recv_window_size; return 0; } delta = -new_recv_window_size; /* The delta size is strictly more than received bytes. Increase local_window_size by that difference |delta|. */ if (*local_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta) { return NGHTTP2_ERR_FLOW_CONTROL; } *local_window_size_ptr += delta; /* If there is recv_reduction due to earlier window_size reduction, we have to adjust it too. */ recv_reduction_delta = nghttp2_min_int32(*recv_reduction_ptr, delta); *recv_reduction_ptr -= recv_reduction_delta; if (*recv_window_size_ptr < 0) { *recv_window_size_ptr += recv_reduction_delta; } else { /* If *recv_window_size_ptr > 0, then those bytes are going to be returned to the remote peer (by WINDOW_UPDATE with the adjusted *delta_ptr), so it is effectively 0 now. We set to *recv_reduction_delta, because caller does not take into account it in *delta_ptr. */ *recv_window_size_ptr = recv_reduction_delta; } /* recv_reduction_delta must be paid from *delta_ptr, since it was added in window size reduction (see below). */ *delta_ptr -= recv_reduction_delta; return 0; } if (*local_window_size_ptr + *delta_ptr < 0 || *recv_window_size_ptr < INT32_MIN - *delta_ptr || *recv_reduction_ptr > INT32_MAX + *delta_ptr) { return NGHTTP2_ERR_FLOW_CONTROL; } /* Decreasing local window size. Note that we achieve this without noticing to the remote peer. To do this, we cut recv_window_size by -delta. This means that we don't send WINDOW_UPDATE for -delta bytes. */ *local_window_size_ptr += *delta_ptr; *recv_window_size_ptr += *delta_ptr; *recv_reduction_ptr -= *delta_ptr; *delta_ptr = 0; return 0; } int nghttp2_increase_local_window_size(int32_t *local_window_size_ptr, int32_t *recv_window_size_ptr, int32_t *recv_reduction_ptr, int32_t *delta_ptr) { int32_t recv_reduction_delta; int32_t delta; delta = *delta_ptr; assert(delta >= 0); /* The delta size is strictly more than received bytes. Increase local_window_size by that difference |delta|. */ if (*local_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta) { return NGHTTP2_ERR_FLOW_CONTROL; } *local_window_size_ptr += delta; /* If there is recv_reduction due to earlier window_size reduction, we have to adjust it too. */ recv_reduction_delta = nghttp2_min_int32(*recv_reduction_ptr, delta); *recv_reduction_ptr -= recv_reduction_delta; *recv_window_size_ptr += recv_reduction_delta; /* recv_reduction_delta must be paid from *delta_ptr, since it was added in window size reduction (see below). */ *delta_ptr -= recv_reduction_delta; return 0; } int nghttp2_should_send_window_update(int32_t local_window_size, int32_t recv_window_size) { return recv_window_size > 0 && recv_window_size >= local_window_size / 2; } const char *nghttp2_strerror(int error_code) { switch (error_code) { case 0: return "Success"; case NGHTTP2_ERR_INVALID_ARGUMENT: return "Invalid argument"; case NGHTTP2_ERR_BUFFER_ERROR: return "Out of buffer space"; case NGHTTP2_ERR_UNSUPPORTED_VERSION: return "Unsupported SPDY version"; case NGHTTP2_ERR_WOULDBLOCK: return "Operation would block"; case NGHTTP2_ERR_PROTO: return "Protocol error"; case NGHTTP2_ERR_INVALID_FRAME: return "Invalid frame octets"; case NGHTTP2_ERR_EOF: return "EOF"; case NGHTTP2_ERR_DEFERRED: return "Data transfer deferred"; case NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE: return "No more Stream ID available"; case NGHTTP2_ERR_STREAM_CLOSED: return "Stream was already closed or invalid"; case NGHTTP2_ERR_STREAM_CLOSING: return "Stream is closing"; case NGHTTP2_ERR_STREAM_SHUT_WR: return "The transmission is not allowed for this stream"; case NGHTTP2_ERR_INVALID_STREAM_ID: return "Stream ID is invalid"; case NGHTTP2_ERR_INVALID_STREAM_STATE: return "Invalid stream state"; case NGHTTP2_ERR_DEFERRED_DATA_EXIST: return "Another DATA frame has already been deferred"; case NGHTTP2_ERR_START_STREAM_NOT_ALLOWED: return "request HEADERS is not allowed"; case NGHTTP2_ERR_GOAWAY_ALREADY_SENT: return "GOAWAY has already been sent"; case NGHTTP2_ERR_INVALID_HEADER_BLOCK: return "Invalid header block"; case NGHTTP2_ERR_INVALID_STATE: return "Invalid state"; case NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE: return "The user callback function failed due to the temporal error"; case NGHTTP2_ERR_FRAME_SIZE_ERROR: return "The length of the frame is invalid"; case NGHTTP2_ERR_HEADER_COMP: return "Header compression/decompression error"; case NGHTTP2_ERR_FLOW_CONTROL: return "Flow control error"; case NGHTTP2_ERR_INSUFF_BUFSIZE: return "Insufficient buffer size given to function"; case NGHTTP2_ERR_PAUSE: return "Callback was paused by the application"; case NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS: return "Too many inflight SETTINGS"; case NGHTTP2_ERR_PUSH_DISABLED: return "Server push is disabled by peer"; case NGHTTP2_ERR_DATA_EXIST: return "DATA or HEADERS frame has already been submitted for the stream"; case NGHTTP2_ERR_SESSION_CLOSING: return "The current session is closing"; case NGHTTP2_ERR_HTTP_HEADER: return "Invalid HTTP header field was received"; case NGHTTP2_ERR_HTTP_MESSAGING: return "Violation in HTTP messaging rule"; case NGHTTP2_ERR_REFUSED_STREAM: return "Stream was refused"; case NGHTTP2_ERR_INTERNAL: return "Internal error"; case NGHTTP2_ERR_CANCEL: return "Cancel"; case NGHTTP2_ERR_SETTINGS_EXPECTED: return "When a local endpoint expects to receive SETTINGS frame, it " "receives an other type of frame"; case NGHTTP2_ERR_NOMEM: return "Out of memory"; case NGHTTP2_ERR_CALLBACK_FAILURE: return "The user callback function failed"; case NGHTTP2_ERR_BAD_CLIENT_MAGIC: return "Received bad client magic byte string"; case NGHTTP2_ERR_FLOODED: return "Flooding was detected in this HTTP/2 session, and it must be " "closed"; case NGHTTP2_ERR_TOO_MANY_SETTINGS: return "SETTINGS frame contained more than the maximum allowed entries"; case NGHTTP2_ERR_TOO_MANY_CONTINUATIONS: return "Too many CONTINUATION frames following a HEADER frame"; default: return "Unknown error code"; } } /* Generated by gennmchartbl.py */ static const int VALID_HD_NAME_CHARS[] = { 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, 0 /* SPC */, 1 /* ! */, 0 /* " */, 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */, 0 /* , */, 1 /* - */, 1 /* . */, 0 /* / */, 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */, 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */, 0 /* A */, 0 /* B */, 0 /* C */, 0 /* D */, 0 /* E */, 0 /* F */, 0 /* G */, 0 /* H */, 0 /* I */, 0 /* J */, 0 /* K */, 0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */, 0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */, 0 /* T */, 0 /* U */, 0 /* V */, 0 /* W */, 0 /* X */, 0 /* Y */, 0 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 1 /* ^ */, 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 1 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */ }; int nghttp2_check_header_name(const uint8_t *name, size_t len) { const uint8_t *last; if (len == 0) { return 0; } if (*name == ':') { if (len == 1) { return 0; } ++name; --len; } for (last = name + len; name != last; ++name) { if (!VALID_HD_NAME_CHARS[*name]) { return 0; } } return 1; } /* Generated by genvchartbl.py */ static const int VALID_HD_VALUE_CHARS[] = { 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 1 /* HT */, 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, 1 /* SPC */, 1 /* ! */, 1 /* " */, 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, 1 /* , */, 1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, 1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */, 1 /* \ */, 1 /* ] */, 1 /* ^ */, 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, 1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */, 1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */, 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */, 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */, 1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */, 1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */, 1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */, 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */, 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */, 1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */, 1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */, 1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */, 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */, 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */, 1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */, 1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */, 1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */, 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */, 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */, 1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */, 1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */, 1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */, 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */, 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */, 1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */, 1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */, 1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */, 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */, 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */, 1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */, 1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */, 1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */, 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */ }; int nghttp2_check_header_value(const uint8_t *value, size_t len) { const uint8_t *last; for (last = value + len; value != last; ++value) { if (!VALID_HD_VALUE_CHARS[*value]) { return 0; } } return 1; } int nghttp2_check_header_value_rfc9113(const uint8_t *value, size_t len) { if (len == 0) { return 1; } if (*value == ' ' || *value == '\t' || *(value + len - 1) == ' ' || *(value + len - 1) == '\t') { return 0; } return nghttp2_check_header_value(value, len); } /* Generated by genmethodchartbl.py */ static char VALID_METHOD_CHARS[] = { 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, 0 /* SPC */, 1 /* ! */, 0 /* " */, 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */, 0 /* , */, 1 /* - */, 1 /* . */, 0 /* / */, 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */, 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */, 1 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 1 /* ^ */, 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 1 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */ }; int nghttp2_check_method(const uint8_t *value, size_t len) { const uint8_t *last; if (len == 0) { return 0; } for (last = value + len; value != last; ++value) { if (!VALID_METHOD_CHARS[*value]) { return 0; } } return 1; } /* Generated by genpathchartbl.py */ static char VALID_PATH_CHARS[] = { 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, 0 /* SPC */, 1 /* ! */, 1 /* " */, 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, 1 /* , */, 1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, 1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */, 1 /* \ */, 1 /* ] */, 1 /* ^ */, 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, 1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */, 1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */, 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */, 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */, 1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */, 1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */, 1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */, 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */, 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */, 1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */, 1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */, 1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */, 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */, 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */, 1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */, 1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */, 1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */, 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */, 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */, 1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */, 1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */, 1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */, 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */, 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */, 1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */, 1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */, 1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */, 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */, 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */, 1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */, 1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */, 1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */, 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */ }; int nghttp2_check_path(const uint8_t *value, size_t len) { const uint8_t *last; for (last = value + len; value != last; ++value) { if (!VALID_PATH_CHARS[*value]) { return 0; } } return 1; } /* Generated by genauthoritychartbl.py */ static char VALID_AUTHORITY_CHARS[] = { 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, 0 /* SPC */, 1 /* ! */, 0 /* " */, 0 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, 1 /* , */, 1 /* - */, 1 /* . */, 0 /* / */, 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, 0 /* < */, 1 /* = */, 0 /* > */, 0 /* ? */, 1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */, 0 /* \ */, 1 /* ] */, 0 /* ^ */, 1 /* _ */, 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 0 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */ }; int nghttp2_check_authority(const uint8_t *value, size_t len) { const uint8_t *last; for (last = value + len; value != last; ++value) { if (!VALID_AUTHORITY_CHARS[*value]) { return 0; } } return 1; } uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len) { if (len == 0) { return dest; } memcpy(dest, src, len); return dest + len; } const char *nghttp2_http2_strerror(uint32_t error_code) { switch (error_code) { case NGHTTP2_NO_ERROR: return "NO_ERROR"; case NGHTTP2_PROTOCOL_ERROR: return "PROTOCOL_ERROR"; case NGHTTP2_INTERNAL_ERROR: return "INTERNAL_ERROR"; case NGHTTP2_FLOW_CONTROL_ERROR: return "FLOW_CONTROL_ERROR"; case NGHTTP2_SETTINGS_TIMEOUT: return "SETTINGS_TIMEOUT"; case NGHTTP2_STREAM_CLOSED: return "STREAM_CLOSED"; case NGHTTP2_FRAME_SIZE_ERROR: return "FRAME_SIZE_ERROR"; case NGHTTP2_REFUSED_STREAM: return "REFUSED_STREAM"; case NGHTTP2_CANCEL: return "CANCEL"; case NGHTTP2_COMPRESSION_ERROR: return "COMPRESSION_ERROR"; case NGHTTP2_CONNECT_ERROR: return "CONNECT_ERROR"; case NGHTTP2_ENHANCE_YOUR_CALM: return "ENHANCE_YOUR_CALM"; case NGHTTP2_INADEQUATE_SECURITY: return "INADEQUATE_SECURITY"; case NGHTTP2_HTTP_1_1_REQUIRED: return "HTTP_1_1_REQUIRED"; default: return "unknown"; } } nghttp2-1.68.0/lib/PaxHeaders/nghttp2_mem.h0000644000000000000000000000013215077107270015423 xustar0030 mtime=1761382072.979444204 30 atime=1761382104.953315938 30 ctime=1761382107.939303737 nghttp2-1.68.0/lib/nghttp2_mem.h0000644000175100017510000000347215077107270016021 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_MEM_H #define NGHTTP2_MEM_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include /* The default, system standard memory allocator */ nghttp2_mem *nghttp2_mem_default(void); /* Convenient wrapper functions to call allocator function in |mem|. */ void *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size); void nghttp2_mem_free(nghttp2_mem *mem, void *ptr); void nghttp2_mem_free2(nghttp2_free free_func, void *ptr, void *mem_user_data); void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size); void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size); #endif /* !defined(NGHTTP2_MEM_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_ratelim.h0000644000000000000000000000013215077107270016302 xustar0030 mtime=1761382072.980444199 30 atime=1761382104.958315916 30 ctime=1761382107.944303723 nghttp2-1.68.0/lib/nghttp2_ratelim.h0000644000175100017510000000426015077107270016674 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2023 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_RATELIM_H #define NGHTTP2_RATELIM_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include typedef struct nghttp2_ratelim { /* burst is the maximum value of val. */ uint64_t burst; /* rate is the amount of value that is regenerated per 1 tstamp. */ uint64_t rate; /* val is the amount of value available to drain. */ uint64_t val; /* tstamp is the last timestamp in second resolution that is known to this object. */ uint64_t tstamp; } nghttp2_ratelim; /* nghttp2_ratelim_init initializes |rl| with the given parameters. */ void nghttp2_ratelim_init(nghttp2_ratelim *rl, uint64_t burst, uint64_t rate); /* nghttp2_ratelim_update updates rl->val with the current |tstamp| given in second resolution. */ void nghttp2_ratelim_update(nghttp2_ratelim *rl, uint64_t tstamp); /* nghttp2_ratelim_drain drains |n| from rl->val. It returns 0 if it succeeds, or -1. */ int nghttp2_ratelim_drain(nghttp2_ratelim *rl, uint64_t n); #endif /* !defined(NGHTTP2_RATELIM_H) */ nghttp2-1.68.0/lib/PaxHeaders/sfparse.c0000644000000000000000000000013115077107270014634 xustar0029 mtime=1761382072.98244419 30 atime=1761382104.996315749 30 ctime=1761382107.982303613 nghttp2-1.68.0/lib/sfparse.c0000644000175100017510000015511315077107270015233 0ustar00runnerrunner/* * sfparse * * Copyright (c) 2023 sfparse contributors * Copyright (c) 2019 nghttp3 contributors * Copyright (c) 2015 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "sfparse.h" #include #include #include #ifdef __AVX2__ # include #endif /* __AVX2__ */ #define SFPARSE_STATE_DICT 0x08u #define SFPARSE_STATE_LIST 0x10u #define SFPARSE_STATE_ITEM 0x18u #define SFPARSE_STATE_INNER_LIST 0x04u #define SFPARSE_STATE_BEFORE 0x00u #define SFPARSE_STATE_BEFORE_PARAMS 0x01u #define SFPARSE_STATE_PARAMS 0x02u #define SFPARSE_STATE_AFTER 0x03u #define SFPARSE_STATE_OP_MASK 0x03u #define SFPARSE_SET_STATE_AFTER(NAME) \ (SFPARSE_STATE_##NAME | SFPARSE_STATE_AFTER) #define SFPARSE_SET_STATE_BEFORE_PARAMS(NAME) \ (SFPARSE_STATE_##NAME | SFPARSE_STATE_BEFORE_PARAMS) #define SFPARSE_SET_STATE_INNER_LIST_BEFORE(NAME) \ (SFPARSE_STATE_##NAME | SFPARSE_STATE_INNER_LIST | SFPARSE_STATE_BEFORE) #define SFPARSE_STATE_DICT_AFTER SFPARSE_SET_STATE_AFTER(DICT) #define SFPARSE_STATE_DICT_BEFORE_PARAMS SFPARSE_SET_STATE_BEFORE_PARAMS(DICT) #define SFPARSE_STATE_DICT_INNER_LIST_BEFORE \ SFPARSE_SET_STATE_INNER_LIST_BEFORE(DICT) #define SFPARSE_STATE_LIST_AFTER SFPARSE_SET_STATE_AFTER(LIST) #define SFPARSE_STATE_LIST_BEFORE_PARAMS SFPARSE_SET_STATE_BEFORE_PARAMS(LIST) #define SFPARSE_STATE_LIST_INNER_LIST_BEFORE \ SFPARSE_SET_STATE_INNER_LIST_BEFORE(LIST) #define SFPARSE_STATE_ITEM_AFTER SFPARSE_SET_STATE_AFTER(ITEM) #define SFPARSE_STATE_ITEM_BEFORE_PARAMS SFPARSE_SET_STATE_BEFORE_PARAMS(ITEM) #define SFPARSE_STATE_ITEM_INNER_LIST_BEFORE \ SFPARSE_SET_STATE_INNER_LIST_BEFORE(ITEM) #define SFPARSE_STATE_INITIAL 0x00u #define DIGIT_CASES \ case '0': \ case '1': \ case '2': \ case '3': \ case '4': \ case '5': \ case '6': \ case '7': \ case '8': \ case '9' #define LCALPHA_CASES \ case 'a': \ case 'b': \ case 'c': \ case 'd': \ case 'e': \ case 'f': \ case 'g': \ case 'h': \ case 'i': \ case 'j': \ case 'k': \ case 'l': \ case 'm': \ case 'n': \ case 'o': \ case 'p': \ case 'q': \ case 'r': \ case 's': \ case 't': \ case 'u': \ case 'v': \ case 'w': \ case 'x': \ case 'y': \ case 'z' #define UCALPHA_CASES \ case 'A': \ case 'B': \ case 'C': \ case 'D': \ case 'E': \ case 'F': \ case 'G': \ case 'H': \ case 'I': \ case 'J': \ case 'K': \ case 'L': \ case 'M': \ case 'N': \ case 'O': \ case 'P': \ case 'Q': \ case 'R': \ case 'S': \ case 'T': \ case 'U': \ case 'V': \ case 'W': \ case 'X': \ case 'Y': \ case 'Z' #define ALPHA_CASES \ UCALPHA_CASES: \ LCALPHA_CASES #define TOKEN_CASES \ case '!': \ case '#': \ case '$': \ case '%': \ case '&': \ case '\'': \ case '*': \ case '+': \ case '-': \ case '.': \ case '/': \ DIGIT_CASES: \ case ':': \ UCALPHA_CASES: \ case '^': \ case '_': \ case '`': \ LCALPHA_CASES: \ case '|': \ case '~' #define LCHEXALPHA_CASES \ case 'a': \ case 'b': \ case 'c': \ case 'd': \ case 'e': \ case 'f' #define X00_1F_CASES \ case 0x00: \ case 0x01: \ case 0x02: \ case 0x03: \ case 0x04: \ case 0x05: \ case 0x06: \ case 0x07: \ case 0x08: \ case 0x09: \ case 0x0a: \ case 0x0b: \ case 0x0c: \ case 0x0d: \ case 0x0e: \ case 0x0f: \ case 0x10: \ case 0x11: \ case 0x12: \ case 0x13: \ case 0x14: \ case 0x15: \ case 0x16: \ case 0x17: \ case 0x18: \ case 0x19: \ case 0x1a: \ case 0x1b: \ case 0x1c: \ case 0x1d: \ case 0x1e: \ case 0x1f #define X20_21_CASES \ case ' ': \ case '!' #define X23_5B_CASES \ case '#': \ case '$': \ case '%': \ case '&': \ case '\'': \ case '(': \ case ')': \ case '*': \ case '+': \ case ',': \ case '-': \ case '.': \ case '/': \ DIGIT_CASES: \ case ':': \ case ';': \ case '<': \ case '=': \ case '>': \ case '?': \ case '@': \ UCALPHA_CASES: \ case '[' #define X5D_7E_CASES \ case ']': \ case '^': \ case '_': \ case '`': \ LCALPHA_CASES: \ case '{': \ case '|': \ case '}': \ case '~' #define X7F_FF_CASES \ case 0x7f: \ case 0x80: \ case 0x81: \ case 0x82: \ case 0x83: \ case 0x84: \ case 0x85: \ case 0x86: \ case 0x87: \ case 0x88: \ case 0x89: \ case 0x8a: \ case 0x8b: \ case 0x8c: \ case 0x8d: \ case 0x8e: \ case 0x8f: \ case 0x90: \ case 0x91: \ case 0x92: \ case 0x93: \ case 0x94: \ case 0x95: \ case 0x96: \ case 0x97: \ case 0x98: \ case 0x99: \ case 0x9a: \ case 0x9b: \ case 0x9c: \ case 0x9d: \ case 0x9e: \ case 0x9f: \ case 0xa0: \ case 0xa1: \ case 0xa2: \ case 0xa3: \ case 0xa4: \ case 0xa5: \ case 0xa6: \ case 0xa7: \ case 0xa8: \ case 0xa9: \ case 0xaa: \ case 0xab: \ case 0xac: \ case 0xad: \ case 0xae: \ case 0xaf: \ case 0xb0: \ case 0xb1: \ case 0xb2: \ case 0xb3: \ case 0xb4: \ case 0xb5: \ case 0xb6: \ case 0xb7: \ case 0xb8: \ case 0xb9: \ case 0xba: \ case 0xbb: \ case 0xbc: \ case 0xbd: \ case 0xbe: \ case 0xbf: \ case 0xc0: \ case 0xc1: \ case 0xc2: \ case 0xc3: \ case 0xc4: \ case 0xc5: \ case 0xc6: \ case 0xc7: \ case 0xc8: \ case 0xc9: \ case 0xca: \ case 0xcb: \ case 0xcc: \ case 0xcd: \ case 0xce: \ case 0xcf: \ case 0xd0: \ case 0xd1: \ case 0xd2: \ case 0xd3: \ case 0xd4: \ case 0xd5: \ case 0xd6: \ case 0xd7: \ case 0xd8: \ case 0xd9: \ case 0xda: \ case 0xdb: \ case 0xdc: \ case 0xdd: \ case 0xde: \ case 0xdf: \ case 0xe0: \ case 0xe1: \ case 0xe2: \ case 0xe3: \ case 0xe4: \ case 0xe5: \ case 0xe6: \ case 0xe7: \ case 0xe8: \ case 0xe9: \ case 0xea: \ case 0xeb: \ case 0xec: \ case 0xed: \ case 0xee: \ case 0xef: \ case 0xf0: \ case 0xf1: \ case 0xf2: \ case 0xf3: \ case 0xf4: \ case 0xf5: \ case 0xf6: \ case 0xf7: \ case 0xf8: \ case 0xf9: \ case 0xfa: \ case 0xfb: \ case 0xfc: \ case 0xfd: \ case 0xfe: \ case 0xff static int is_ws(uint8_t c) { switch (c) { case ' ': case '\t': return 1; default: return 0; } } #ifdef __AVX2__ # ifdef _MSC_VER # include static int ctz(unsigned int v) { unsigned long n; /* Assume that v is not 0. */ _BitScanForward(&n, v); return (int)n; } # else /* !_MSC_VER */ # define ctz __builtin_ctz # endif /* !_MSC_VER */ #endif /* __AVX2__ */ static int parser_eof(sfparse_parser *sfp) { return sfp->pos == sfp->end; } static void parser_discard_ows(sfparse_parser *sfp) { for (; !parser_eof(sfp) && is_ws(*sfp->pos); ++sfp->pos) ; } static void parser_discard_sp(sfparse_parser *sfp) { for (; !parser_eof(sfp) && *sfp->pos == ' '; ++sfp->pos) ; } static void parser_set_op_state(sfparse_parser *sfp, uint32_t op) { sfp->state &= ~SFPARSE_STATE_OP_MASK; sfp->state |= op; } static void parser_unset_inner_list_state(sfparse_parser *sfp) { sfp->state &= ~SFPARSE_STATE_INNER_LIST; } #ifdef __AVX2__ static const uint8_t *find_char_key(const uint8_t *first, const uint8_t *last) { const __m256i us = _mm256_set1_epi8('_'); const __m256i ds = _mm256_set1_epi8('-'); const __m256i dot = _mm256_set1_epi8('.'); const __m256i ast = _mm256_set1_epi8('*'); const __m256i r0l = _mm256_set1_epi8('0' - 1); const __m256i r0r = _mm256_set1_epi8('9' + 1); const __m256i r1l = _mm256_set1_epi8('a' - 1); const __m256i r1r = _mm256_set1_epi8('z' + 1); __m256i s, x; uint32_t m; for (; first != last; first += 32) { s = _mm256_loadu_si256((void *)first); x = _mm256_cmpeq_epi8(s, us); x = _mm256_or_si256(_mm256_cmpeq_epi8(s, ds), x); x = _mm256_or_si256(_mm256_cmpeq_epi8(s, dot), x); x = _mm256_or_si256(_mm256_cmpeq_epi8(s, ast), x); x = _mm256_or_si256( _mm256_and_si256(_mm256_cmpgt_epi8(s, r0l), _mm256_cmpgt_epi8(r0r, s)), x); x = _mm256_or_si256( _mm256_and_si256(_mm256_cmpgt_epi8(s, r1l), _mm256_cmpgt_epi8(r1r, s)), x); m = ~(uint32_t)_mm256_movemask_epi8(x); if (m) { return first + ctz(m); } } return last; } #endif /* __AVX2__ */ static int parser_key(sfparse_parser *sfp, sfparse_vec *dest) { const uint8_t *base; #ifdef __AVX2__ const uint8_t *last; #endif /* __AVX2__ */ switch (*sfp->pos) { case '*': LCALPHA_CASES: break; default: return SFPARSE_ERR_PARSE; } base = sfp->pos++; #ifdef __AVX2__ if (sfp->end - sfp->pos >= 32) { last = sfp->pos + ((sfp->end - sfp->pos) & ~0x1fu); sfp->pos = find_char_key(sfp->pos, last); if (sfp->pos != last) { goto fin; } } #endif /* __AVX2__ */ for (; !parser_eof(sfp); ++sfp->pos) { switch (*sfp->pos) { case '_': case '-': case '.': case '*': DIGIT_CASES: LCALPHA_CASES: continue; } break; } #ifdef __AVX2__ fin: #endif /* __AVX2__ */ if (dest) { dest->base = (uint8_t *)base; dest->len = (size_t)(sfp->pos - dest->base); } return 0; } static int parser_number(sfparse_parser *sfp, sfparse_value *dest) { int sign = 1; int64_t value = 0; size_t len = 0; size_t fpos = 0; if (*sfp->pos == '-') { ++sfp->pos; if (parser_eof(sfp)) { return SFPARSE_ERR_PARSE; } sign = -1; } assert(!parser_eof(sfp)); for (; !parser_eof(sfp); ++sfp->pos) { switch (*sfp->pos) { DIGIT_CASES: if (++len > 15) { return SFPARSE_ERR_PARSE; } value *= 10; value += *sfp->pos - '0'; continue; } break; } if (len == 0) { return SFPARSE_ERR_PARSE; } if (parser_eof(sfp) || *sfp->pos != '.') { if (dest) { dest->type = SFPARSE_TYPE_INTEGER; dest->flags = SFPARSE_VALUE_FLAG_NONE; dest->integer = value * sign; } return 0; } /* decimal */ if (len > 12) { return SFPARSE_ERR_PARSE; } fpos = len; ++sfp->pos; for (; !parser_eof(sfp); ++sfp->pos) { switch (*sfp->pos) { DIGIT_CASES: if (++len > 15) { return SFPARSE_ERR_PARSE; } value *= 10; value += *sfp->pos - '0'; continue; } break; } if (fpos == len || len - fpos > 3) { return SFPARSE_ERR_PARSE; } if (dest) { dest->type = SFPARSE_TYPE_DECIMAL; dest->flags = SFPARSE_VALUE_FLAG_NONE; dest->decimal.numer = value * sign; switch (len - fpos) { case 1: dest->decimal.denom = 10; break; case 2: dest->decimal.denom = 100; break; case 3: dest->decimal.denom = 1000; break; } } return 0; } static int parser_date(sfparse_parser *sfp, sfparse_value *dest) { int rv; sfparse_value val; /* The first byte has already been validated by the caller. */ assert('@' == *sfp->pos); ++sfp->pos; if (parser_eof(sfp)) { return SFPARSE_ERR_PARSE; } rv = parser_number(sfp, &val); if (rv != 0) { return rv; } if (val.type != SFPARSE_TYPE_INTEGER) { return SFPARSE_ERR_PARSE; } if (dest) { *dest = val; dest->type = SFPARSE_TYPE_DATE; } return 0; } #ifdef __AVX2__ static const uint8_t *find_char_string(const uint8_t *first, const uint8_t *last) { const __m256i bs = _mm256_set1_epi8('\\'); const __m256i dq = _mm256_set1_epi8('"'); const __m256i del = _mm256_set1_epi8(0x7f); const __m256i sp = _mm256_set1_epi8(' '); __m256i s, x; uint32_t m; for (; first != last; first += 32) { s = _mm256_loadu_si256((void *)first); x = _mm256_cmpgt_epi8(sp, s); x = _mm256_or_si256(_mm256_cmpeq_epi8(s, bs), x); x = _mm256_or_si256(_mm256_cmpeq_epi8(s, dq), x); x = _mm256_or_si256(_mm256_cmpeq_epi8(s, del), x); m = (uint32_t)_mm256_movemask_epi8(x); if (m) { return first + ctz(m); } } return last; } #endif /* __AVX2__ */ static int parser_string(sfparse_parser *sfp, sfparse_value *dest) { const uint8_t *base; #ifdef __AVX2__ const uint8_t *last; #endif /* __AVX2__ */ uint32_t flags = SFPARSE_VALUE_FLAG_NONE; /* The first byte has already been validated by the caller. */ assert('"' == *sfp->pos); base = ++sfp->pos; #ifdef __AVX2__ for (; sfp->end - sfp->pos >= 32; ++sfp->pos) { last = sfp->pos + ((sfp->end - sfp->pos) & ~0x1fu); sfp->pos = find_char_string(sfp->pos, last); if (sfp->pos == last) { break; } switch (*sfp->pos) { case '\\': ++sfp->pos; if (parser_eof(sfp)) { return SFPARSE_ERR_PARSE; } switch (*sfp->pos) { case '"': case '\\': flags = SFPARSE_VALUE_FLAG_ESCAPED_STRING; break; default: return SFPARSE_ERR_PARSE; } break; case '"': goto fin; default: return SFPARSE_ERR_PARSE; } } #endif /* __AVX2__ */ for (; !parser_eof(sfp); ++sfp->pos) { switch (*sfp->pos) { X20_21_CASES: X23_5B_CASES: X5D_7E_CASES: break; case '\\': ++sfp->pos; if (parser_eof(sfp)) { return SFPARSE_ERR_PARSE; } switch (*sfp->pos) { case '"': case '\\': flags = SFPARSE_VALUE_FLAG_ESCAPED_STRING; break; default: return SFPARSE_ERR_PARSE; } break; case '"': goto fin; default: return SFPARSE_ERR_PARSE; } } return SFPARSE_ERR_PARSE; fin: if (dest) { dest->type = SFPARSE_TYPE_STRING; dest->flags = flags; dest->vec.len = (size_t)(sfp->pos - base); dest->vec.base = dest->vec.len == 0 ? NULL : (uint8_t *)base; } ++sfp->pos; return 0; } #ifdef __AVX2__ static const uint8_t *find_char_token(const uint8_t *first, const uint8_t *last) { /* r0: !..:, excluding "(), r1: A..Z r2: ^..~, excluding {} */ const __m256i r0l = _mm256_set1_epi8('!' - 1); const __m256i r0r = _mm256_set1_epi8(':' + 1); const __m256i dq = _mm256_set1_epi8('"'); const __m256i prl = _mm256_set1_epi8('('); const __m256i prr = _mm256_set1_epi8(')'); const __m256i comma = _mm256_set1_epi8(','); const __m256i r1l = _mm256_set1_epi8('A' - 1); const __m256i r1r = _mm256_set1_epi8('Z' + 1); const __m256i r2l = _mm256_set1_epi8('^' - 1); const __m256i r2r = _mm256_set1_epi8('~' + 1); const __m256i cbl = _mm256_set1_epi8('{'); const __m256i cbr = _mm256_set1_epi8('}'); __m256i s, x; uint32_t m; for (; first != last; first += 32) { s = _mm256_loadu_si256((void *)first); x = _mm256_andnot_si256( _mm256_cmpeq_epi8(s, comma), _mm256_andnot_si256( _mm256_cmpeq_epi8(s, prr), _mm256_andnot_si256( _mm256_cmpeq_epi8(s, prl), _mm256_andnot_si256(_mm256_cmpeq_epi8(s, dq), _mm256_and_si256(_mm256_cmpgt_epi8(s, r0l), _mm256_cmpgt_epi8(r0r, s)))))); x = _mm256_or_si256( _mm256_and_si256(_mm256_cmpgt_epi8(s, r1l), _mm256_cmpgt_epi8(r1r, s)), x); x = _mm256_or_si256( _mm256_andnot_si256( _mm256_cmpeq_epi8(s, cbr), _mm256_andnot_si256(_mm256_cmpeq_epi8(s, cbl), _mm256_and_si256(_mm256_cmpgt_epi8(s, r2l), _mm256_cmpgt_epi8(r2r, s)))), x); m = ~(uint32_t)_mm256_movemask_epi8(x); if (m) { return first + ctz(m); } } return last; } #endif /* __AVX2__ */ static int parser_token(sfparse_parser *sfp, sfparse_value *dest) { const uint8_t *base; #ifdef __AVX2__ const uint8_t *last; #endif /* __AVX2__ */ /* The first byte has already been validated by the caller. */ base = sfp->pos++; #ifdef __AVX2__ if (sfp->end - sfp->pos >= 32) { last = sfp->pos + ((sfp->end - sfp->pos) & ~0x1fu); sfp->pos = find_char_token(sfp->pos, last); if (sfp->pos != last) { goto fin; } } #endif /* __AVX2__ */ for (; !parser_eof(sfp); ++sfp->pos) { switch (*sfp->pos) { TOKEN_CASES: continue; } break; } #ifdef __AVX2__ fin: #endif /* __AVX2__ */ if (dest) { dest->type = SFPARSE_TYPE_TOKEN; dest->flags = SFPARSE_VALUE_FLAG_NONE; dest->vec.base = (uint8_t *)base; dest->vec.len = (size_t)(sfp->pos - base); } return 0; } #ifdef __AVX2__ static const uint8_t *find_char_byteseq(const uint8_t *first, const uint8_t *last) { const __m256i pls = _mm256_set1_epi8('+'); const __m256i fs = _mm256_set1_epi8('/'); const __m256i r0l = _mm256_set1_epi8('0' - 1); const __m256i r0r = _mm256_set1_epi8('9' + 1); const __m256i r1l = _mm256_set1_epi8('A' - 1); const __m256i r1r = _mm256_set1_epi8('Z' + 1); const __m256i r2l = _mm256_set1_epi8('a' - 1); const __m256i r2r = _mm256_set1_epi8('z' + 1); __m256i s, x; uint32_t m; for (; first != last; first += 32) { s = _mm256_loadu_si256((void *)first); x = _mm256_cmpeq_epi8(s, pls); x = _mm256_or_si256(_mm256_cmpeq_epi8(s, fs), x); x = _mm256_or_si256( _mm256_and_si256(_mm256_cmpgt_epi8(s, r0l), _mm256_cmpgt_epi8(r0r, s)), x); x = _mm256_or_si256( _mm256_and_si256(_mm256_cmpgt_epi8(s, r1l), _mm256_cmpgt_epi8(r1r, s)), x); x = _mm256_or_si256( _mm256_and_si256(_mm256_cmpgt_epi8(s, r2l), _mm256_cmpgt_epi8(r2r, s)), x); m = ~(uint32_t)_mm256_movemask_epi8(x); if (m) { return first + ctz(m); } } return last; } #endif /* __AVX2__ */ static int parser_byteseq(sfparse_parser *sfp, sfparse_value *dest) { const uint8_t *base; #ifdef __AVX2__ const uint8_t *last; #endif /* __AVX2__ */ /* The first byte has already been validated by the caller. */ assert(':' == *sfp->pos); base = ++sfp->pos; #ifdef __AVX2__ if (sfp->end - sfp->pos >= 32) { last = sfp->pos + ((sfp->end - sfp->pos) & ~0x1fu); sfp->pos = find_char_byteseq(sfp->pos, last); } #endif /* __AVX2__ */ for (; !parser_eof(sfp); ++sfp->pos) { switch (*sfp->pos) { case '+': case '/': DIGIT_CASES: ALPHA_CASES: continue; case '=': switch ((sfp->pos - base) & 0x3) { case 0: case 1: return SFPARSE_ERR_PARSE; case 2: ++sfp->pos; if (parser_eof(sfp)) { return SFPARSE_ERR_PARSE; } if (*sfp->pos == '=') { ++sfp->pos; } break; case 3: ++sfp->pos; break; } if (parser_eof(sfp) || *sfp->pos != ':') { return SFPARSE_ERR_PARSE; } goto fin; case ':': if (((sfp->pos - base) & 0x3) == 1) { return SFPARSE_ERR_PARSE; } goto fin; default: return SFPARSE_ERR_PARSE; } } return SFPARSE_ERR_PARSE; fin: if (dest) { dest->type = SFPARSE_TYPE_BYTESEQ; dest->flags = SFPARSE_VALUE_FLAG_NONE; dest->vec.len = (size_t)(sfp->pos - base); dest->vec.base = dest->vec.len == 0 ? NULL : (uint8_t *)base; } ++sfp->pos; return 0; } static int parser_boolean(sfparse_parser *sfp, sfparse_value *dest) { int b; /* The first byte has already been validated by the caller. */ assert('?' == *sfp->pos); ++sfp->pos; if (parser_eof(sfp)) { return SFPARSE_ERR_PARSE; } switch (*sfp->pos) { case '0': b = 0; break; case '1': b = 1; break; default: return SFPARSE_ERR_PARSE; } ++sfp->pos; if (dest) { dest->type = SFPARSE_TYPE_BOOLEAN; dest->flags = SFPARSE_VALUE_FLAG_NONE; dest->boolean = b; } return 0; } static int pctdecode(uint8_t *pc, const uint8_t **ppos) { uint8_t c, b = **ppos; switch (b) { DIGIT_CASES: c = (uint8_t)((b - '0') << 4); break; LCHEXALPHA_CASES: c = (uint8_t)((b - 'a' + 10) << 4); break; default: return -1; } b = *++*ppos; switch (b) { DIGIT_CASES: c |= (uint8_t)(b - '0'); break; LCHEXALPHA_CASES: c |= (uint8_t)(b - 'a' + 10); break; default: return -1; } *pc = c; ++*ppos; return 0; } /* Start of utf8 dfa */ /* Copyright (c) 2008-2010 Bjoern Hoehrmann * See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. * * Copyright (c) 2008-2009 Bjoern Hoehrmann * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #define UTF8_ACCEPT 0 #define UTF8_REJECT 12 /* clang-format off */ static const uint8_t utf8d[] = { /* * The first part of the table maps bytes to character classes that * to reduce the size of the transition table and create bitmasks. */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, /* * The second part is a transition table that maps a combination * of a state of the automaton and a character class to a state. */ 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,12,12,12,12,12, }; /* clang-format on */ static void utf8_decode(uint32_t *state, uint8_t byte) { *state = utf8d[256 + *state + utf8d[byte]]; } /* End of utf8 dfa */ static int parser_dispstring(sfparse_parser *sfp, sfparse_value *dest) { const uint8_t *base; uint8_t c; uint32_t utf8state = UTF8_ACCEPT; assert('%' == *sfp->pos); ++sfp->pos; if (parser_eof(sfp) || *sfp->pos != '"') { return SFPARSE_ERR_PARSE; } base = ++sfp->pos; for (; !parser_eof(sfp);) { switch (*sfp->pos) { X00_1F_CASES: X7F_FF_CASES: return SFPARSE_ERR_PARSE; case '%': ++sfp->pos; if (sfp->pos + 2 > sfp->end) { return SFPARSE_ERR_PARSE; } if (pctdecode(&c, &sfp->pos) != 0) { return SFPARSE_ERR_PARSE; } utf8_decode(&utf8state, c); if (utf8state == UTF8_REJECT) { return SFPARSE_ERR_PARSE; } break; case '"': if (utf8state != UTF8_ACCEPT) { return SFPARSE_ERR_PARSE; } if (dest) { dest->type = SFPARSE_TYPE_DISPSTRING; dest->flags = SFPARSE_VALUE_FLAG_NONE; dest->vec.len = (size_t)(sfp->pos - base); dest->vec.base = dest->vec.len == 0 ? NULL : (uint8_t *)base; } ++sfp->pos; return 0; default: if (utf8state != UTF8_ACCEPT) { return SFPARSE_ERR_PARSE; } ++sfp->pos; } } return SFPARSE_ERR_PARSE; } static int parser_bare_item(sfparse_parser *sfp, sfparse_value *dest) { switch (*sfp->pos) { case '"': return parser_string(sfp, dest); case '-': DIGIT_CASES: return parser_number(sfp, dest); case '@': return parser_date(sfp, dest); case ':': return parser_byteseq(sfp, dest); case '?': return parser_boolean(sfp, dest); case '*': ALPHA_CASES: return parser_token(sfp, dest); case '%': return parser_dispstring(sfp, dest); default: return SFPARSE_ERR_PARSE; } } static int parser_skip_inner_list(sfparse_parser *sfp); int sfparse_parser_param(sfparse_parser *sfp, sfparse_vec *dest_key, sfparse_value *dest_value) { int rv; switch (sfp->state & SFPARSE_STATE_OP_MASK) { case SFPARSE_STATE_BEFORE: rv = parser_skip_inner_list(sfp); if (rv != 0) { return rv; } /* fall through */ case SFPARSE_STATE_BEFORE_PARAMS: parser_set_op_state(sfp, SFPARSE_STATE_PARAMS); break; case SFPARSE_STATE_PARAMS: break; default: assert(0); abort(); } if (parser_eof(sfp) || *sfp->pos != ';') { parser_set_op_state(sfp, SFPARSE_STATE_AFTER); return SFPARSE_ERR_EOF; } ++sfp->pos; parser_discard_sp(sfp); if (parser_eof(sfp)) { return SFPARSE_ERR_PARSE; } rv = parser_key(sfp, dest_key); if (rv != 0) { return rv; } if (parser_eof(sfp) || *sfp->pos != '=') { if (dest_value) { dest_value->type = SFPARSE_TYPE_BOOLEAN; dest_value->flags = SFPARSE_VALUE_FLAG_NONE; dest_value->boolean = 1; } return 0; } ++sfp->pos; if (parser_eof(sfp)) { return SFPARSE_ERR_PARSE; } return parser_bare_item(sfp, dest_value); } static int parser_skip_params(sfparse_parser *sfp) { int rv; for (;;) { rv = sfparse_parser_param(sfp, NULL, NULL); switch (rv) { case 0: break; case SFPARSE_ERR_EOF: return 0; case SFPARSE_ERR_PARSE: return rv; default: assert(0); abort(); } } } int sfparse_parser_inner_list(sfparse_parser *sfp, sfparse_value *dest) { int rv; switch (sfp->state & SFPARSE_STATE_OP_MASK) { case SFPARSE_STATE_BEFORE: parser_discard_sp(sfp); if (parser_eof(sfp)) { return SFPARSE_ERR_PARSE; } break; case SFPARSE_STATE_BEFORE_PARAMS: rv = parser_skip_params(sfp); if (rv != 0) { return rv; } /* Technically, we are entering SFPARSE_STATE_AFTER, but we will set another state without reading the state. */ /* parser_set_op_state(sfp, SFPARSE_STATE_AFTER); */ /* fall through */ case SFPARSE_STATE_AFTER: if (parser_eof(sfp)) { return SFPARSE_ERR_PARSE; } switch (*sfp->pos) { case ' ': parser_discard_sp(sfp); if (parser_eof(sfp)) { return SFPARSE_ERR_PARSE; } break; case ')': break; default: return SFPARSE_ERR_PARSE; } break; default: assert(0); abort(); } if (*sfp->pos == ')') { ++sfp->pos; parser_unset_inner_list_state(sfp); parser_set_op_state(sfp, SFPARSE_STATE_BEFORE_PARAMS); return SFPARSE_ERR_EOF; } rv = parser_bare_item(sfp, dest); if (rv != 0) { return rv; } parser_set_op_state(sfp, SFPARSE_STATE_BEFORE_PARAMS); return 0; } static int parser_skip_inner_list(sfparse_parser *sfp) { int rv; for (;;) { rv = sfparse_parser_inner_list(sfp, NULL); switch (rv) { case 0: break; case SFPARSE_ERR_EOF: return 0; case SFPARSE_ERR_PARSE: return rv; default: assert(0); abort(); } } } static int parser_next_key_or_item(sfparse_parser *sfp) { parser_discard_ows(sfp); if (parser_eof(sfp)) { return SFPARSE_ERR_EOF; } if (*sfp->pos != ',') { return SFPARSE_ERR_PARSE; } ++sfp->pos; parser_discard_ows(sfp); if (parser_eof(sfp)) { return SFPARSE_ERR_PARSE; } return 0; } static int parser_dict_value(sfparse_parser *sfp, sfparse_value *dest) { int rv; if (parser_eof(sfp) || *(sfp->pos) != '=') { /* Boolean true */ if (dest) { dest->type = SFPARSE_TYPE_BOOLEAN; dest->flags = SFPARSE_VALUE_FLAG_NONE; dest->boolean = 1; } sfp->state = SFPARSE_STATE_DICT_BEFORE_PARAMS; return 0; } ++sfp->pos; if (parser_eof(sfp)) { return SFPARSE_ERR_PARSE; } if (*sfp->pos == '(') { if (dest) { dest->type = SFPARSE_TYPE_INNER_LIST; dest->flags = SFPARSE_VALUE_FLAG_NONE; } ++sfp->pos; sfp->state = SFPARSE_STATE_DICT_INNER_LIST_BEFORE; return 0; } rv = parser_bare_item(sfp, dest); if (rv != 0) { return rv; } sfp->state = SFPARSE_STATE_DICT_BEFORE_PARAMS; return 0; } int sfparse_parser_dict(sfparse_parser *sfp, sfparse_vec *dest_key, sfparse_value *dest_value) { int rv; switch (sfp->state) { case SFPARSE_STATE_DICT_INNER_LIST_BEFORE: rv = parser_skip_inner_list(sfp); if (rv != 0) { return rv; } /* fall through */ case SFPARSE_STATE_DICT_BEFORE_PARAMS: rv = parser_skip_params(sfp); if (rv != 0) { return rv; } /* fall through */ case SFPARSE_STATE_DICT_AFTER: rv = parser_next_key_or_item(sfp); if (rv != 0) { return rv; } break; case SFPARSE_STATE_INITIAL: parser_discard_sp(sfp); if (parser_eof(sfp)) { return SFPARSE_ERR_EOF; } break; default: assert(0); abort(); } rv = parser_key(sfp, dest_key); if (rv != 0) { return rv; } return parser_dict_value(sfp, dest_value); } int sfparse_parser_list(sfparse_parser *sfp, sfparse_value *dest) { int rv; switch (sfp->state) { case SFPARSE_STATE_LIST_INNER_LIST_BEFORE: rv = parser_skip_inner_list(sfp); if (rv != 0) { return rv; } /* fall through */ case SFPARSE_STATE_LIST_BEFORE_PARAMS: rv = parser_skip_params(sfp); if (rv != 0) { return rv; } /* fall through */ case SFPARSE_STATE_LIST_AFTER: rv = parser_next_key_or_item(sfp); if (rv != 0) { return rv; } break; case SFPARSE_STATE_INITIAL: parser_discard_sp(sfp); if (parser_eof(sfp)) { return SFPARSE_ERR_EOF; } break; default: assert(0); abort(); } if (*sfp->pos == '(') { if (dest) { dest->type = SFPARSE_TYPE_INNER_LIST; dest->flags = SFPARSE_VALUE_FLAG_NONE; } ++sfp->pos; sfp->state = SFPARSE_STATE_LIST_INNER_LIST_BEFORE; return 0; } rv = parser_bare_item(sfp, dest); if (rv != 0) { return rv; } sfp->state = SFPARSE_STATE_LIST_BEFORE_PARAMS; return 0; } int sfparse_parser_item(sfparse_parser *sfp, sfparse_value *dest) { int rv; switch (sfp->state) { case SFPARSE_STATE_INITIAL: parser_discard_sp(sfp); if (parser_eof(sfp)) { return SFPARSE_ERR_PARSE; } break; case SFPARSE_STATE_ITEM_INNER_LIST_BEFORE: rv = parser_skip_inner_list(sfp); if (rv != 0) { return rv; } /* fall through */ case SFPARSE_STATE_ITEM_BEFORE_PARAMS: rv = parser_skip_params(sfp); if (rv != 0) { return rv; } /* fall through */ case SFPARSE_STATE_ITEM_AFTER: parser_discard_sp(sfp); if (!parser_eof(sfp)) { return SFPARSE_ERR_PARSE; } return SFPARSE_ERR_EOF; default: assert(0); abort(); } if (*sfp->pos == '(') { if (dest) { dest->type = SFPARSE_TYPE_INNER_LIST; dest->flags = SFPARSE_VALUE_FLAG_NONE; } ++sfp->pos; sfp->state = SFPARSE_STATE_ITEM_INNER_LIST_BEFORE; return 0; } rv = parser_bare_item(sfp, dest); if (rv != 0) { return rv; } sfp->state = SFPARSE_STATE_ITEM_BEFORE_PARAMS; return 0; } void sfparse_parser_init(sfparse_parser *sfp, const uint8_t *data, size_t datalen) { if (datalen == 0) { sfp->pos = sfp->end = NULL; } else { sfp->pos = data; sfp->end = data + datalen; } sfp->state = SFPARSE_STATE_INITIAL; } void sfparse_unescape(sfparse_vec *dest, const sfparse_vec *src) { const uint8_t *p, *q; uint8_t *o; size_t len, slen; if (src->len == 0) { dest->len = 0; return; } o = dest->base; p = src->base; len = src->len; for (;;) { q = memchr(p, '\\', len); if (q == NULL) { memcpy(o, p, len); o += len; dest->len = (size_t)(o - dest->base); return; } slen = (size_t)(q - p); memcpy(o, p, slen); o += slen; p = q + 1; *o++ = *p++; len -= slen + 2; } } void sfparse_base64decode(sfparse_vec *dest, const sfparse_vec *src) { static const int index_tbl[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; uint8_t *o; const uint8_t *p, *end; uint32_t n; size_t i, left; int idx; if (src->len == 0) { dest->len = 0; return; } o = dest->base; p = src->base; left = src->len & 0x3; if (left == 0 && src->base[src->len - 1] == '=') { left = 4; } end = src->base + src->len - left; for (; p != end;) { n = 0; for (i = 1; i <= 4; ++i, ++p) { idx = index_tbl[*p]; assert(idx != -1); n += (uint32_t)(idx << (24 - i * 6)); } *o++ = (uint8_t)(n >> 16); *o++ = (n >> 8) & 0xffu; *o++ = n & 0xffu; } switch (left) { case 0: goto fin; case 1: assert(0); abort(); case 3: if (src->base[src->len - 1] == '=') { left = 2; } break; case 4: assert('=' == src->base[src->len - 1]); if (src->base[src->len - 2] == '=') { left = 2; } else { left = 3; } break; } switch (left) { case 2: *o = (uint8_t)(index_tbl[*p++] << 2); *o++ |= (uint8_t)(index_tbl[*p++] >> 4); break; case 3: n = (uint32_t)(index_tbl[*p++] << 10); n += (uint32_t)(index_tbl[*p++] << 4); n += (uint32_t)(index_tbl[*p++] >> 2); *o++ = (n >> 8) & 0xffu; *o++ = n & 0xffu; break; } fin: dest->len = (size_t)(o - dest->base); } void sfparse_pctdecode(sfparse_vec *dest, const sfparse_vec *src) { const uint8_t *p, *q; uint8_t *o; size_t len, slen; if (src->len == 0) { dest->len = 0; return; } o = dest->base; p = src->base; len = src->len; for (;;) { q = memchr(p, '%', len); if (q == NULL) { memcpy(o, p, len); o += len; dest->len = (size_t)(o - dest->base); return; } slen = (size_t)(q - p); memcpy(o, p, slen); o += slen; p = q + 1; pctdecode(o++, &p); len -= slen + 3; } } nghttp2-1.68.0/lib/PaxHeaders/nghttp2_time.h0000644000000000000000000000013215077107270015603 xustar0030 mtime=1761382072.981444194 30 atime=1761382104.960315907 30 ctime=1761382107.946303717 nghttp2-1.68.0/lib/nghttp2_time.h0000644000175100017510000000276715077107270016207 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2023 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_TIME_H #define NGHTTP2_TIME_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include /* nghttp2_time_now_sec returns seconds from implementation-specific timepoint. If it is unable to get seconds, it returns 0. */ uint64_t nghttp2_time_now_sec(void); #endif /* !defined(NGHTTP2_TIME_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_session.c0000644000000000000000000000013215077107270016323 xustar0030 mtime=1761382072.981444194 30 atime=1761382104.972315854 30 ctime=1761382107.959303679 nghttp2-1.68.0/lib/nghttp2_session.c0000644000175100017510000073621015077107270016724 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_session.h" #include #include #include #include #include #include "nghttp2_helper.h" #include "nghttp2_net.h" #include "nghttp2_priority_spec.h" #include "nghttp2_option.h" #include "nghttp2_http.h" #include "nghttp2_pq.h" #include "nghttp2_extpri.h" #include "nghttp2_time.h" #include "nghttp2_debug.h" #include "nghttp2_submit.h" nghttp2_stream nghttp2_stream_root; /* * Returns non-zero if the number of outgoing opened streams is larger * than or equal to * remote_settings.max_concurrent_streams. */ static int session_is_outgoing_concurrent_streams_max(nghttp2_session *session) { return session->remote_settings.max_concurrent_streams <= session->num_outgoing_streams; } /* * Returns non-zero if the number of incoming opened streams is larger * than or equal to * local_settings.max_concurrent_streams. */ static int session_is_incoming_concurrent_streams_max(nghttp2_session *session) { return session->local_settings.max_concurrent_streams <= session->num_incoming_streams; } /* * Returns non-zero if the number of incoming opened streams is larger * than or equal to * session->pending_local_max_concurrent_stream. */ static int session_is_incoming_concurrent_streams_pending_max(nghttp2_session *session) { return session->pending_local_max_concurrent_stream <= session->num_incoming_streams; } /* * Returns non-zero if |lib_error| is non-fatal error. */ static int is_non_fatal(int lib_error_code) { return lib_error_code < 0 && lib_error_code > NGHTTP2_ERR_FATAL; } int nghttp2_is_fatal(int lib_error_code) { return lib_error_code < NGHTTP2_ERR_FATAL; } static int session_enforce_http_messaging(nghttp2_session *session) { return (session->opt_flags & NGHTTP2_OPTMASK_NO_HTTP_MESSAGING) == 0; } /* * Returns nonzero if |frame| is trailer headers. */ static int session_trailer_headers(nghttp2_session *session, nghttp2_stream *stream, nghttp2_frame *frame) { if (!stream || frame->hd.type != NGHTTP2_HEADERS) { return 0; } if (session->server) { return frame->headers.cat == NGHTTP2_HCAT_HEADERS; } return frame->headers.cat == NGHTTP2_HCAT_HEADERS && (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) == 0; } /* Returns nonzero if the |stream| is in reserved(remote) state */ static int state_reserved_remote(nghttp2_session *session, nghttp2_stream *stream) { return stream->state == NGHTTP2_STREAM_RESERVED && !nghttp2_session_is_my_stream_id(session, stream->stream_id); } /* Returns nonzero if the |stream| is in reserved(local) state */ static int state_reserved_local(nghttp2_session *session, nghttp2_stream *stream) { return stream->state == NGHTTP2_STREAM_RESERVED && nghttp2_session_is_my_stream_id(session, stream->stream_id); } /* * Checks whether received stream_id is valid. This function returns * 1 if it succeeds, or 0. */ static int session_is_new_peer_stream_id(nghttp2_session *session, int32_t stream_id) { return stream_id != 0 && !nghttp2_session_is_my_stream_id(session, stream_id) && session->last_recv_stream_id < stream_id; } static int session_detect_idle_stream(nghttp2_session *session, int32_t stream_id) { /* Assume that stream object with stream_id does not exist */ if (nghttp2_session_is_my_stream_id(session, stream_id)) { if (session->last_sent_stream_id < stream_id) { return 1; } return 0; } if (session_is_new_peer_stream_id(session, stream_id)) { return 1; } return 0; } static int check_ext_type_set(const uint8_t *ext_types, uint8_t type) { return (ext_types[type / 8] & (1 << (type & 0x7))) > 0; } static int session_call_error_callback(nghttp2_session *session, int lib_error_code, const char *fmt, ...) { size_t bufsize; va_list ap; char *buf; int rv; nghttp2_mem *mem; if (!session->callbacks.error_callback && !session->callbacks.error_callback2) { return 0; } mem = &session->mem; va_start(ap, fmt); rv = vsnprintf(NULL, 0, fmt, ap); va_end(ap); if (rv < 0) { return NGHTTP2_ERR_NOMEM; } bufsize = (size_t)(rv + 1); buf = nghttp2_mem_malloc(mem, bufsize); if (buf == NULL) { return NGHTTP2_ERR_NOMEM; } va_start(ap, fmt); rv = vsnprintf(buf, bufsize, fmt, ap); va_end(ap); if (rv < 0) { nghttp2_mem_free(mem, buf); /* vsnprintf may return error because of various things we can imagine, but typically we don't want to drop session just for debug callback. */ DEBUGF("error_callback: vsnprintf failed. The template was %s\n", fmt); return 0; } if (session->callbacks.error_callback2) { rv = session->callbacks.error_callback2(session, lib_error_code, buf, (size_t)rv, session->user_data); } else { rv = session->callbacks.error_callback(session, buf, (size_t)rv, session->user_data); } nghttp2_mem_free(mem, buf); if (rv != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } return 0; } static int session_terminate_session(nghttp2_session *session, int32_t last_stream_id, uint32_t error_code, const char *reason) { int rv; const uint8_t *debug_data; size_t debug_datalen; if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { return 0; } /* Ignore all incoming frames because we are going to tear down the session. */ session->iframe.state = NGHTTP2_IB_IGN_ALL; if (reason == NULL) { debug_data = NULL; debug_datalen = 0; } else { debug_data = (const uint8_t *)reason; debug_datalen = strlen(reason); } rv = nghttp2_session_add_goaway(session, last_stream_id, error_code, debug_data, debug_datalen, NGHTTP2_GOAWAY_AUX_TERM_ON_SEND); if (rv != 0) { return rv; } session->goaway_flags |= NGHTTP2_GOAWAY_TERM_ON_SEND; return 0; } int nghttp2_session_terminate_session(nghttp2_session *session, uint32_t error_code) { return session_terminate_session(session, session->last_proc_stream_id, error_code, NULL); } int nghttp2_session_terminate_session2(nghttp2_session *session, int32_t last_stream_id, uint32_t error_code) { return session_terminate_session(session, last_stream_id, error_code, NULL); } int nghttp2_session_terminate_session_with_reason(nghttp2_session *session, uint32_t error_code, const char *reason) { return session_terminate_session(session, session->last_proc_stream_id, error_code, reason); } int nghttp2_session_is_my_stream_id(nghttp2_session *session, int32_t stream_id) { int rem; if (stream_id == 0) { return 0; } rem = stream_id & 0x1; if (session->server) { return rem == 0; } return rem == 1; } nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session, int32_t stream_id) { nghttp2_stream *stream; stream = (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id); if (stream == NULL || (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) || stream->state == NGHTTP2_STREAM_IDLE) { return NULL; } return stream; } nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session, int32_t stream_id) { return (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id); } static void session_inbound_frame_reset(nghttp2_session *session) { nghttp2_inbound_frame *iframe = &session->iframe; nghttp2_mem *mem = &session->mem; /* A bit risky code, since if this function is called from nghttp2_session_new(), we rely on the fact that iframe->frame.hd.type is 0, so that no free is performed. */ switch (iframe->frame.hd.type) { case NGHTTP2_DATA: break; case NGHTTP2_HEADERS: nghttp2_frame_headers_free(&iframe->frame.headers, mem); break; case NGHTTP2_PRIORITY: nghttp2_frame_priority_free(&iframe->frame.priority); break; case NGHTTP2_RST_STREAM: nghttp2_frame_rst_stream_free(&iframe->frame.rst_stream); break; case NGHTTP2_SETTINGS: nghttp2_frame_settings_free(&iframe->frame.settings, mem); nghttp2_mem_free(mem, iframe->iv); iframe->iv = NULL; iframe->niv = 0; iframe->max_niv = 0; break; case NGHTTP2_PUSH_PROMISE: nghttp2_frame_push_promise_free(&iframe->frame.push_promise, mem); break; case NGHTTP2_PING: nghttp2_frame_ping_free(&iframe->frame.ping); break; case NGHTTP2_GOAWAY: nghttp2_frame_goaway_free(&iframe->frame.goaway, mem); break; case NGHTTP2_WINDOW_UPDATE: nghttp2_frame_window_update_free(&iframe->frame.window_update); break; default: /* extension frame */ if (check_ext_type_set(session->user_recv_ext_types, iframe->frame.hd.type)) { nghttp2_frame_extension_free(&iframe->frame.ext); } else { switch (iframe->frame.hd.type) { case NGHTTP2_ALTSVC: if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ALTSVC) == 0) { break; } nghttp2_frame_altsvc_free(&iframe->frame.ext, mem); break; case NGHTTP2_ORIGIN: if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN) == 0) { break; } nghttp2_frame_origin_free(&iframe->frame.ext, mem); break; case NGHTTP2_PRIORITY_UPDATE: if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_PRIORITY_UPDATE) == 0) { break; } /* Do not call nghttp2_frame_priority_update_free, because all fields point to sbuf. */ break; } } break; } memset(&iframe->frame, 0, sizeof(nghttp2_frame)); memset(&iframe->ext_frame_payload, 0, sizeof(nghttp2_ext_frame_payload)); iframe->state = NGHTTP2_IB_READ_HEAD; nghttp2_buf_wrap_init(&iframe->sbuf, iframe->raw_sbuf, sizeof(iframe->raw_sbuf)); iframe->sbuf.mark += NGHTTP2_FRAME_HDLEN; nghttp2_buf_free(&iframe->lbuf, mem); nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0); iframe->raw_lbuf = NULL; iframe->payloadleft = 0; iframe->padlen = 0; } static void init_settings(nghttp2_settings_storage *settings) { settings->header_table_size = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; settings->enable_push = 1; settings->max_concurrent_streams = NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS; settings->initial_window_size = NGHTTP2_INITIAL_WINDOW_SIZE; settings->max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN; settings->max_header_list_size = UINT32_MAX; settings->no_rfc7540_priorities = UINT32_MAX; } static void active_outbound_item_reset(nghttp2_active_outbound_item *aob, nghttp2_mem *mem) { DEBUGF("send: reset nghttp2_active_outbound_item\n"); DEBUGF("send: aob->item = %p\n", aob->item); nghttp2_outbound_item_free(aob->item, mem); nghttp2_mem_free(mem, aob->item); aob->item = NULL; nghttp2_bufs_reset(&aob->framebufs); aob->state = NGHTTP2_OB_POP_ITEM; } #define NGHTTP2_STREAM_MAX_CYCLE_GAP ((uint64_t)NGHTTP2_MAX_FRAME_SIZE_MAX) static int stream_less(const void *lhsx, const void *rhsx) { const nghttp2_stream *lhs, *rhs; lhs = nghttp2_struct_of(lhsx, nghttp2_stream, pq_entry); rhs = nghttp2_struct_of(rhsx, nghttp2_stream, pq_entry); if (lhs->cycle == rhs->cycle) { return lhs->seq < rhs->seq; } return rhs->cycle - lhs->cycle <= NGHTTP2_STREAM_MAX_CYCLE_GAP; } int nghttp2_enable_strict_preface = 1; static int session_new(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, int server, const nghttp2_option *option, nghttp2_mem *mem) { int rv; size_t nbuffer; size_t max_deflate_dynamic_table_size = NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE; size_t i; uint32_t map_seed; if (mem == NULL) { mem = nghttp2_mem_default(); } *session_ptr = nghttp2_mem_calloc(mem, 1, sizeof(nghttp2_session)); if (*session_ptr == NULL) { rv = NGHTTP2_ERR_NOMEM; goto fail_session; } (*session_ptr)->mem = *mem; mem = &(*session_ptr)->mem; /* next_stream_id is initialized in either nghttp2_session_client_new2 or nghttp2_session_server_new2 */ (*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; (*session_ptr)->recv_window_size = 0; (*session_ptr)->consumed_size = 0; (*session_ptr)->recv_reduction = 0; (*session_ptr)->local_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; (*session_ptr)->goaway_flags = NGHTTP2_GOAWAY_NONE; (*session_ptr)->local_last_stream_id = (1u << 31) - 1; (*session_ptr)->remote_last_stream_id = (1u << 31) - 1; (*session_ptr)->pending_local_max_concurrent_stream = NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS; (*session_ptr)->pending_enable_push = 1; (*session_ptr)->pending_no_rfc7540_priorities = UINT8_MAX; nghttp2_ratelim_init(&(*session_ptr)->stream_reset_ratelim, NGHTTP2_DEFAULT_STREAM_RESET_BURST, NGHTTP2_DEFAULT_STREAM_RESET_RATE); nghttp2_ratelim_init(&(*session_ptr)->glitch_ratelim, NGHTTP2_DEFAULT_GLITCH_BURST, NGHTTP2_DEFAULT_GLITCH_RATE); if (server) { (*session_ptr)->server = 1; } init_settings(&(*session_ptr)->remote_settings); init_settings(&(*session_ptr)->local_settings); (*session_ptr)->max_incoming_reserved_streams = NGHTTP2_MAX_INCOMING_RESERVED_STREAMS; /* Limit max outgoing concurrent streams to sensible value */ (*session_ptr)->remote_settings.max_concurrent_streams = 100; (*session_ptr)->max_send_header_block_length = NGHTTP2_MAX_HEADERSLEN; (*session_ptr)->max_outbound_ack = NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM; (*session_ptr)->max_settings = NGHTTP2_DEFAULT_MAX_SETTINGS; (*session_ptr)->max_continuations = NGHTTP2_DEFAULT_MAX_CONTINUATIONS; if (option) { if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) && option->no_auto_window_update) { (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE; } if (option->opt_set_mask & NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS) { (*session_ptr)->remote_settings.max_concurrent_streams = option->peer_max_concurrent_streams; } if (option->opt_set_mask & NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS) { (*session_ptr)->max_incoming_reserved_streams = option->max_reserved_remote_streams; } if ((option->opt_set_mask & NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC) && option->no_recv_client_magic) { (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC; } if ((option->opt_set_mask & NGHTTP2_OPT_NO_HTTP_MESSAGING) && option->no_http_messaging) { (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING; } if (option->opt_set_mask & NGHTTP2_OPT_USER_RECV_EXT_TYPES) { memcpy((*session_ptr)->user_recv_ext_types, option->user_recv_ext_types, sizeof((*session_ptr)->user_recv_ext_types)); } if (option->opt_set_mask & NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES) { (*session_ptr)->builtin_recv_ext_types = option->builtin_recv_ext_types; } if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_PING_ACK) && option->no_auto_ping_ack) { (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_PING_ACK; } if (option->opt_set_mask & NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH) { (*session_ptr)->max_send_header_block_length = option->max_send_header_block_length; } if (option->opt_set_mask & NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE) { max_deflate_dynamic_table_size = option->max_deflate_dynamic_table_size; } if (option->opt_set_mask & NGHTTP2_OPT_MAX_OUTBOUND_ACK) { (*session_ptr)->max_outbound_ack = option->max_outbound_ack; } if ((option->opt_set_mask & NGHTTP2_OPT_MAX_SETTINGS) && option->max_settings) { (*session_ptr)->max_settings = option->max_settings; } if ((option->opt_set_mask & NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) && option->no_rfc9113_leading_and_trailing_ws_validation) { (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION; } if (option->opt_set_mask & NGHTTP2_OPT_STREAM_RESET_RATE_LIMIT) { nghttp2_ratelim_init(&(*session_ptr)->stream_reset_ratelim, option->stream_reset_burst, option->stream_reset_rate); } if (option->opt_set_mask & NGHTTP2_OPT_MAX_CONTINUATIONS) { (*session_ptr)->max_continuations = option->max_continuations; } if (option->opt_set_mask & NGHTTP2_OPT_GLITCH_RATE_LIMIT) { nghttp2_ratelim_init(&(*session_ptr)->glitch_ratelim, option->glitch_burst, option->glitch_rate); } } rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater, max_deflate_dynamic_table_size, mem); if (rv != 0) { goto fail_hd_deflater; } rv = nghttp2_hd_inflate_init(&(*session_ptr)->hd_inflater, mem); if (rv != 0) { goto fail_hd_inflater; } nbuffer = ((*session_ptr)->max_send_header_block_length + NGHTTP2_FRAMEBUF_CHUNKLEN - 1) / NGHTTP2_FRAMEBUF_CHUNKLEN; if (nbuffer == 0) { nbuffer = 1; } /* 1 for Pad Field. */ rv = nghttp2_bufs_init3(&(*session_ptr)->aob.framebufs, NGHTTP2_FRAMEBUF_CHUNKLEN, nbuffer, 1, NGHTTP2_FRAME_HDLEN + 1, mem); if (rv != 0) { goto fail_aob_framebuf; } if (callbacks->rand_callback) { callbacks->rand_callback((uint8_t *)&map_seed, sizeof(map_seed)); } else { map_seed = 0; } nghttp2_map_init(&(*session_ptr)->streams, map_seed, mem); active_outbound_item_reset(&(*session_ptr)->aob, mem); (*session_ptr)->callbacks = *callbacks; (*session_ptr)->user_data = user_data; session_inbound_frame_reset(*session_ptr); if (nghttp2_enable_strict_preface) { nghttp2_inbound_frame *iframe = &(*session_ptr)->iframe; if (server && ((*session_ptr)->opt_flags & NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC) == 0) { iframe->state = NGHTTP2_IB_READ_CLIENT_MAGIC; iframe->payloadleft = NGHTTP2_CLIENT_MAGIC_LEN; } else { iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS; } if (!server) { (*session_ptr)->aob.state = NGHTTP2_OB_SEND_CLIENT_MAGIC; nghttp2_bufs_add(&(*session_ptr)->aob.framebufs, NGHTTP2_CLIENT_MAGIC, NGHTTP2_CLIENT_MAGIC_LEN); } } for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) { nghttp2_pq_init(&(*session_ptr)->sched[i].ob_data, stream_less, mem); } return 0; fail_aob_framebuf: nghttp2_hd_inflate_free(&(*session_ptr)->hd_inflater); fail_hd_inflater: nghttp2_hd_deflate_free(&(*session_ptr)->hd_deflater); fail_hd_deflater: nghttp2_mem_free(mem, *session_ptr); fail_session: return rv; } int nghttp2_session_client_new(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data) { return nghttp2_session_client_new3(session_ptr, callbacks, user_data, NULL, NULL); } int nghttp2_session_client_new2(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option) { return nghttp2_session_client_new3(session_ptr, callbacks, user_data, option, NULL); } int nghttp2_session_client_new3(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option, nghttp2_mem *mem) { int rv; nghttp2_session *session; rv = session_new(&session, callbacks, user_data, 0, option, mem); if (rv != 0) { return rv; } /* IDs for use in client */ session->next_stream_id = 1; *session_ptr = session; return 0; } int nghttp2_session_server_new(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data) { return nghttp2_session_server_new3(session_ptr, callbacks, user_data, NULL, NULL); } int nghttp2_session_server_new2(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option) { return nghttp2_session_server_new3(session_ptr, callbacks, user_data, option, NULL); } int nghttp2_session_server_new3(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option, nghttp2_mem *mem) { int rv; nghttp2_session *session; rv = session_new(&session, callbacks, user_data, 1, option, mem); if (rv != 0) { return rv; } /* IDs for use in client */ session->next_stream_id = 2; *session_ptr = session; return 0; } static int free_streams(void *entry, void *ptr) { nghttp2_session *session; nghttp2_stream *stream; nghttp2_outbound_item *item; nghttp2_mem *mem; session = (nghttp2_session *)ptr; mem = &session->mem; stream = (nghttp2_stream *)entry; item = stream->item; if (item && !item->queued && item != session->aob.item) { nghttp2_outbound_item_free(item, mem); nghttp2_mem_free(mem, item); } nghttp2_stream_free(stream); nghttp2_mem_free(mem, stream); return 0; } static void ob_q_free(nghttp2_outbound_queue *q, nghttp2_mem *mem) { nghttp2_outbound_item *item, *next; for (item = q->head; item;) { next = item->qnext; nghttp2_outbound_item_free(item, mem); nghttp2_mem_free(mem, item); item = next; } } static int inflight_settings_new(nghttp2_inflight_settings **settings_ptr, const nghttp2_settings_entry *iv, size_t niv, nghttp2_mem *mem) { *settings_ptr = nghttp2_mem_malloc(mem, sizeof(nghttp2_inflight_settings)); if (!*settings_ptr) { return NGHTTP2_ERR_NOMEM; } if (niv > 0) { (*settings_ptr)->iv = nghttp2_frame_iv_copy(iv, niv, mem); if (!(*settings_ptr)->iv) { nghttp2_mem_free(mem, *settings_ptr); return NGHTTP2_ERR_NOMEM; } } else { (*settings_ptr)->iv = NULL; } (*settings_ptr)->niv = niv; (*settings_ptr)->next = NULL; return 0; } static void inflight_settings_del(nghttp2_inflight_settings *settings, nghttp2_mem *mem) { if (!settings) { return; } nghttp2_mem_free(mem, settings->iv); nghttp2_mem_free(mem, settings); } void nghttp2_session_del(nghttp2_session *session) { nghttp2_mem *mem; nghttp2_inflight_settings *settings; size_t i; if (session == NULL) { return; } mem = &session->mem; for (settings = session->inflight_settings_head; settings;) { nghttp2_inflight_settings *next = settings->next; inflight_settings_del(settings, mem); settings = next; } for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) { nghttp2_pq_free(&session->sched[i].ob_data); } /* Have to free streams first, so that we can check stream->item->queued */ nghttp2_map_each(&session->streams, free_streams, session); nghttp2_map_free(&session->streams); ob_q_free(&session->ob_urgent, mem); ob_q_free(&session->ob_reg, mem); ob_q_free(&session->ob_syn, mem); active_outbound_item_reset(&session->aob, mem); session_inbound_frame_reset(session); nghttp2_hd_deflate_free(&session->hd_deflater); nghttp2_hd_inflate_free(&session->hd_inflater); nghttp2_bufs_free(&session->aob.framebufs); nghttp2_mem_free(mem, session); } static uint64_t pq_get_first_cycle(nghttp2_pq *pq) { nghttp2_stream *stream; if (nghttp2_pq_empty(pq)) { return 0; } stream = nghttp2_struct_of(nghttp2_pq_top(pq), nghttp2_stream, pq_entry); return stream->cycle; } static int session_ob_data_push(nghttp2_session *session, nghttp2_stream *stream) { int rv; uint32_t urgency; int inc; nghttp2_pq *pq; assert(stream->queued == 0); urgency = nghttp2_extpri_uint8_urgency(stream->extpri); inc = nghttp2_extpri_uint8_inc(stream->extpri); assert(urgency < NGHTTP2_EXTPRI_URGENCY_LEVELS); pq = &session->sched[urgency].ob_data; stream->cycle = pq_get_first_cycle(pq); if (inc) { stream->cycle += stream->last_writelen; } rv = nghttp2_pq_push(pq, &stream->pq_entry); if (rv != 0) { return rv; } stream->queued = 1; return 0; } static void session_ob_data_remove(nghttp2_session *session, nghttp2_stream *stream) { uint32_t urgency; assert(stream->queued == 1); urgency = nghttp2_extpri_uint8_urgency(stream->extpri); assert(urgency < NGHTTP2_EXTPRI_URGENCY_LEVELS); nghttp2_pq_remove(&session->sched[urgency].ob_data, &stream->pq_entry); stream->queued = 0; } static int session_attach_stream_item(nghttp2_session *session, nghttp2_stream *stream, nghttp2_outbound_item *item) { int rv; nghttp2_stream_attach_item(stream, item); rv = session_ob_data_push(session, stream); if (rv != 0) { nghttp2_stream_detach_item(stream); return rv; } return 0; } static void session_detach_stream_item(nghttp2_session *session, nghttp2_stream *stream) { nghttp2_stream_detach_item(stream); if (!stream->queued) { return; } session_ob_data_remove(session, stream); } static void session_defer_stream_item(nghttp2_session *session, nghttp2_stream *stream, uint8_t flags) { nghttp2_stream_defer_item(stream, flags); if (!stream->queued) { return; } session_ob_data_remove(session, stream); } static int session_resume_deferred_stream_item(nghttp2_session *session, nghttp2_stream *stream, uint8_t flags) { nghttp2_stream_resume_deferred_item(stream, flags); if (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) { return 0; } return session_ob_data_push(session, stream); } static nghttp2_outbound_item * session_sched_get_next_outbound_item(nghttp2_session *session) { size_t i; nghttp2_pq_entry *ent; nghttp2_stream *stream; for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) { ent = nghttp2_pq_top(&session->sched[i].ob_data); if (!ent) { continue; } stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry); return stream->item; } return NULL; } static int session_sched_empty(nghttp2_session *session) { size_t i; for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) { if (!nghttp2_pq_empty(&session->sched[i].ob_data)) { return 0; } } return 1; } static void session_sched_reschedule_stream(nghttp2_session *session, nghttp2_stream *stream) { nghttp2_pq *pq; uint32_t urgency = nghttp2_extpri_uint8_urgency(stream->extpri); int inc = nghttp2_extpri_uint8_inc(stream->extpri); uint64_t penalty = (uint64_t)stream->last_writelen; int rv; (void)rv; assert(urgency < NGHTTP2_EXTPRI_URGENCY_LEVELS); pq = &session->sched[urgency].ob_data; if (!inc || nghttp2_pq_size(pq) == 1) { return; } nghttp2_pq_remove(pq, &stream->pq_entry); stream->cycle += penalty; rv = nghttp2_pq_push(pq, &stream->pq_entry); assert(0 == rv); } static int session_update_stream_priority(nghttp2_session *session, nghttp2_stream *stream, uint8_t u8extpri) { if (stream->extpri == u8extpri) { return 0; } if (stream->queued) { session_ob_data_remove(session, stream); stream->extpri = u8extpri; return session_ob_data_push(session, stream); } stream->extpri = u8extpri; return 0; } int nghttp2_session_add_item(nghttp2_session *session, nghttp2_outbound_item *item) { /* TODO Return error if stream is not found for the frame requiring stream presence. */ int rv = 0; nghttp2_stream *stream; nghttp2_frame *frame; frame = &item->frame; stream = nghttp2_session_get_stream(session, frame->hd.stream_id); switch (frame->hd.type) { case NGHTTP2_DATA: if (!stream) { return NGHTTP2_ERR_STREAM_CLOSED; } if (stream->item) { return NGHTTP2_ERR_DATA_EXIST; } rv = session_attach_stream_item(session, stream, item); if (rv != 0) { return rv; } return 0; case NGHTTP2_HEADERS: /* We push request HEADERS and push response HEADERS to dedicated queue because their transmission is affected by SETTINGS_MAX_CONCURRENT_STREAMS */ /* TODO If 2 HEADERS are submitted for reserved stream, then both of them are queued into ob_syn, which is not desirable. */ if (frame->headers.cat == NGHTTP2_HCAT_REQUEST || (stream && stream->state == NGHTTP2_STREAM_RESERVED)) { nghttp2_outbound_queue_push(&session->ob_syn, item); item->queued = 1; return 0; ; } nghttp2_outbound_queue_push(&session->ob_reg, item); item->queued = 1; return 0; case NGHTTP2_SETTINGS: case NGHTTP2_PING: nghttp2_outbound_queue_push(&session->ob_urgent, item); item->queued = 1; return 0; case NGHTTP2_RST_STREAM: if (stream) { stream->state = NGHTTP2_STREAM_CLOSING; } nghttp2_outbound_queue_push(&session->ob_reg, item); item->queued = 1; return 0; case NGHTTP2_PUSH_PROMISE: { nghttp2_headers_aux_data *aux_data; aux_data = &item->aux_data.headers; if (!stream) { return NGHTTP2_ERR_STREAM_CLOSED; } if (!nghttp2_session_open_stream( session, frame->push_promise.promised_stream_id, NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_STREAM_RESERVED, aux_data->stream_user_data)) { return NGHTTP2_ERR_NOMEM; } nghttp2_outbound_queue_push(&session->ob_reg, item); item->queued = 1; return 0; } case NGHTTP2_WINDOW_UPDATE: if (stream) { stream->window_update_queued = 1; } else if (frame->hd.stream_id == 0) { session->window_update_queued = 1; } nghttp2_outbound_queue_push(&session->ob_reg, item); item->queued = 1; return 0; default: nghttp2_outbound_queue_push(&session->ob_reg, item); item->queued = 1; return 0; } } int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, uint32_t error_code) { return nghttp2_session_add_rst_stream_continue( session, stream_id, error_code, /* continue_without_stream = */ 1); } int nghttp2_session_add_rst_stream_continue(nghttp2_session *session, int32_t stream_id, uint32_t error_code, int continue_without_stream) { int rv; nghttp2_outbound_item *item; nghttp2_frame *frame; nghttp2_stream *stream; nghttp2_mem *mem; mem = &session->mem; stream = nghttp2_session_get_stream(session, stream_id); if (stream && stream->state == NGHTTP2_STREAM_CLOSING) { return 0; } /* Sending RST_STREAM to an idle stream is subject to protocol violation. Historically, nghttp2 allows this. In order not to disrupt the existing applications, we don't error out this case and simply ignore it. */ if (nghttp2_session_is_my_stream_id(session, stream_id)) { if ((uint32_t)stream_id >= session->next_stream_id) { return 0; } } else if (session->last_recv_stream_id < stream_id) { return 0; } /* Cancel pending request HEADERS in ob_syn if this RST_STREAM refers to that stream. */ if (!session->server && nghttp2_session_is_my_stream_id(session, stream_id) && nghttp2_outbound_queue_top(&session->ob_syn)) { nghttp2_headers_aux_data *aux_data; nghttp2_frame *headers_frame; headers_frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame; assert(headers_frame->hd.type == NGHTTP2_HEADERS); if (headers_frame->hd.stream_id <= stream_id) { for (item = session->ob_syn.head; item; item = item->qnext) { aux_data = &item->aux_data.headers; if (item->frame.hd.stream_id < stream_id) { continue; } /* stream_id in ob_syn queue must be strictly increasing. If we found larger ID, then we can break here. */ if (item->frame.hd.stream_id > stream_id || aux_data->canceled) { break; } aux_data->error_code = error_code; aux_data->canceled = 1; return 0; } } } /* To keep the old behaviour, do not fail if stream was not found. */ if (!continue_without_stream && !stream) { return 0; } item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); if (item == NULL) { return NGHTTP2_ERR_NOMEM; } nghttp2_outbound_item_init(item); frame = &item->frame; nghttp2_frame_rst_stream_init(&frame->rst_stream, stream_id, error_code); rv = nghttp2_session_add_item(session, item); if (rv != 0) { nghttp2_frame_rst_stream_free(&frame->rst_stream); nghttp2_mem_free(mem, item); return rv; } return 0; } nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, int32_t stream_id, uint8_t flags, nghttp2_stream_state initial_state, void *stream_user_data) { int rv; nghttp2_stream *stream; int stream_alloc = 0; nghttp2_mem *mem; mem = &session->mem; stream = nghttp2_session_get_stream_raw(session, stream_id); if (session->opt_flags & NGHTTP2_OPTMASK_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) { flags |= NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION; } if (stream) { assert(stream->state == NGHTTP2_STREAM_IDLE); assert(initial_state != NGHTTP2_STREAM_IDLE); --session->num_idle_streams; } else { stream = nghttp2_mem_malloc(mem, sizeof(nghttp2_stream)); if (stream == NULL) { return NULL; } stream_alloc = 1; } if (initial_state == NGHTTP2_STREAM_RESERVED) { flags |= NGHTTP2_STREAM_FLAG_PUSH; } if (stream_alloc) { nghttp2_stream_init(stream, stream_id, flags, initial_state, (int32_t)session->remote_settings.initial_window_size, (int32_t)session->local_settings.initial_window_size, stream_user_data); stream->seq = session->stream_seq++; rv = nghttp2_map_insert(&session->streams, stream_id, stream); if (rv != 0) { nghttp2_stream_free(stream); nghttp2_mem_free(mem, stream); return NULL; } } else { stream->flags = flags; stream->state = initial_state; stream->stream_user_data = stream_user_data; } switch (initial_state) { case NGHTTP2_STREAM_RESERVED: if (nghttp2_session_is_my_stream_id(session, stream_id)) { /* reserved (local) */ nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); } else { /* reserved (remote) */ nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); ++session->num_incoming_reserved_streams; } /* Reserved stream does not count in the concurrent streams limit. That is one of the DOS vector. */ break; case NGHTTP2_STREAM_IDLE: ++session->num_idle_streams; break; default: if (nghttp2_session_is_my_stream_id(session, stream_id)) { ++session->num_outgoing_streams; } else { ++session->num_incoming_streams; } } return stream; } int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, uint32_t error_code) { nghttp2_stream *stream; nghttp2_mem *mem; int is_my_stream_id; mem = &session->mem; stream = nghttp2_session_get_stream(session, stream_id); if (!stream) { return NGHTTP2_ERR_INVALID_ARGUMENT; } DEBUGF("stream: stream(%p)=%d close\n", stream, stream->stream_id); /* We call on_stream_close_callback even if stream->state is NGHTTP2_STREAM_INITIAL. This will happen while sending request HEADERS, a local endpoint receives RST_STREAM for that stream. It may be PROTOCOL_ERROR, but without notifying stream closure will hang the stream in a local endpoint. */ if (session->callbacks.on_stream_close_callback) { if (session->callbacks.on_stream_close_callback( session, stream_id, error_code, session->user_data) != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } } if (stream->item) { nghttp2_outbound_item *item; item = stream->item; session_detach_stream_item(session, stream); /* If item is queued, it will be deleted when it is popped (nghttp2_session_prep_frame() will fail). If session->aob.item points to this item, let active_outbound_item_reset() free the item. */ if (!item->queued && item != session->aob.item) { nghttp2_outbound_item_free(item, mem); nghttp2_mem_free(mem, item); } } is_my_stream_id = nghttp2_session_is_my_stream_id(session, stream_id); /* pushed streams which is not opened yet is not counted toward max concurrent limits */ if ((stream->flags & NGHTTP2_STREAM_FLAG_PUSH)) { if (!is_my_stream_id) { --session->num_incoming_reserved_streams; } } else { if (is_my_stream_id) { --session->num_outgoing_streams; } else { --session->num_incoming_streams; } } /* Closes both directions just in case they are not closed yet */ stream->flags |= NGHTTP2_STREAM_FLAG_CLOSED; nghttp2_session_destroy_stream(session, stream); return 0; } void nghttp2_session_destroy_stream(nghttp2_session *session, nghttp2_stream *stream) { nghttp2_mem *mem; DEBUGF("stream: destroy closed stream(%p)=%d\n", stream, stream->stream_id); mem = &session->mem; if (stream->queued) { session_ob_data_remove(session, stream); } nghttp2_map_remove(&session->streams, stream->stream_id); nghttp2_stream_free(stream); nghttp2_mem_free(mem, stream); } /* * Closes stream with stream ID |stream_id| if both transmission and * reception of the stream were disallowed. The |error_code| indicates * the reason of the closure. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_INVALID_ARGUMENT * The stream is not found. * NGHTTP2_ERR_CALLBACK_FAILURE * The callback function failed. */ int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session, nghttp2_stream *stream) { if ((stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR) { return nghttp2_session_close_stream(session, stream->stream_id, NGHTTP2_NO_ERROR); } return 0; } /* * Returns nonzero if local endpoint allows reception of new stream * from remote. */ static int session_allow_incoming_new_stream(nghttp2_session *session) { return (session->goaway_flags & (NGHTTP2_GOAWAY_TERM_ON_SEND | NGHTTP2_GOAWAY_SENT)) == 0; } /* * This function returns nonzero if session is closing. */ static int session_is_closing(nghttp2_session *session) { return (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) != 0 || (nghttp2_session_want_read(session) == 0 && nghttp2_session_want_write(session) == 0); } /* * Check that we can send a frame to the |stream|. This function * returns 0 if we can send a frame to the |frame|, or one of the * following negative error codes: * * NGHTTP2_ERR_STREAM_CLOSED * The stream is already closed. * NGHTTP2_ERR_STREAM_SHUT_WR * The stream is half-closed for transmission. * NGHTTP2_ERR_SESSION_CLOSING * This session is closing. */ static int session_predicate_for_stream_send(nghttp2_session *session, nghttp2_stream *stream) { if (stream == NULL) { return NGHTTP2_ERR_STREAM_CLOSED; } if (session_is_closing(session)) { return NGHTTP2_ERR_SESSION_CLOSING; } if (stream->shut_flags & NGHTTP2_SHUT_WR) { return NGHTTP2_ERR_STREAM_SHUT_WR; } return 0; } int nghttp2_session_check_request_allowed(nghttp2_session *session) { return !session->server && session->next_stream_id <= INT32_MAX && (session->goaway_flags & NGHTTP2_GOAWAY_RECV) == 0 && !session_is_closing(session); } /* * This function checks request HEADERS frame, which opens stream, can * be sent at this time. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED * New stream cannot be created because of GOAWAY: session is * going down or received last_stream_id is strictly less than * frame->hd.stream_id. * NGHTTP2_ERR_STREAM_CLOSING * request HEADERS was canceled by RST_STREAM while it is in queue. */ static int session_predicate_request_headers_send(nghttp2_session *session, nghttp2_outbound_item *item) { if (item->aux_data.headers.canceled) { return NGHTTP2_ERR_STREAM_CLOSING; } /* If we are terminating session (NGHTTP2_GOAWAY_TERM_ON_SEND), GOAWAY was received from peer, or session is about to close, new request is not allowed. */ if ((session->goaway_flags & NGHTTP2_GOAWAY_RECV) || session_is_closing(session)) { return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED; } return 0; } /* * This function checks HEADERS, which is the first frame from the * server, with the |stream| can be sent at this time. The |stream| * can be NULL. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_STREAM_CLOSED * The stream is already closed or does not exist. * NGHTTP2_ERR_STREAM_SHUT_WR * The transmission is not allowed for this stream (e.g., a frame * with END_STREAM flag set has already sent) * NGHTTP2_ERR_INVALID_STREAM_ID * The stream ID is invalid. * NGHTTP2_ERR_STREAM_CLOSING * RST_STREAM was queued for this stream. * NGHTTP2_ERR_INVALID_STREAM_STATE * The state of the stream is not valid. * NGHTTP2_ERR_SESSION_CLOSING * This session is closing. * NGHTTP2_ERR_PROTO * Client side attempted to send response. */ static int session_predicate_response_headers_send(nghttp2_session *session, nghttp2_stream *stream) { int rv; rv = session_predicate_for_stream_send(session, stream); if (rv != 0) { return rv; } assert(stream); if (!session->server) { return NGHTTP2_ERR_PROTO; } if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) { return NGHTTP2_ERR_INVALID_STREAM_ID; } switch (stream->state) { case NGHTTP2_STREAM_OPENING: return 0; case NGHTTP2_STREAM_CLOSING: return NGHTTP2_ERR_STREAM_CLOSING; default: return NGHTTP2_ERR_INVALID_STREAM_STATE; } } /* * This function checks HEADERS for reserved stream can be sent. The * |stream| must be reserved state and the |session| is server side. * The |stream| can be NULL. * * This function returns 0 if it succeeds, or one of the following * error codes: * * NGHTTP2_ERR_STREAM_CLOSED * The stream is already closed. * NGHTTP2_ERR_STREAM_SHUT_WR * The stream is half-closed for transmission. * NGHTTP2_ERR_PROTO * The stream is not reserved state * NGHTTP2_ERR_STREAM_CLOSED * RST_STREAM was queued for this stream. * NGHTTP2_ERR_SESSION_CLOSING * This session is closing. * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED * New stream cannot be created because GOAWAY is already sent or * received. * NGHTTP2_ERR_PROTO * Client side attempted to send push response. */ static int session_predicate_push_response_headers_send(nghttp2_session *session, nghttp2_stream *stream) { int rv; /* TODO Should disallow HEADERS if GOAWAY has already been issued? */ rv = session_predicate_for_stream_send(session, stream); if (rv != 0) { return rv; } assert(stream); if (!session->server) { return NGHTTP2_ERR_PROTO; } if (stream->state != NGHTTP2_STREAM_RESERVED) { return NGHTTP2_ERR_PROTO; } if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) { return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED; } return 0; } /* * This function checks HEADERS, which is neither stream-opening nor * first response header, with the |stream| can be sent at this time. * The |stream| can be NULL. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_STREAM_CLOSED * The stream is already closed or does not exist. * NGHTTP2_ERR_STREAM_SHUT_WR * The transmission is not allowed for this stream (e.g., a frame * with END_STREAM flag set has already sent) * NGHTTP2_ERR_STREAM_CLOSING * RST_STREAM was queued for this stream. * NGHTTP2_ERR_INVALID_STREAM_STATE * The state of the stream is not valid. * NGHTTP2_ERR_SESSION_CLOSING * This session is closing. */ static int session_predicate_headers_send(nghttp2_session *session, nghttp2_stream *stream) { int rv; rv = session_predicate_for_stream_send(session, stream); if (rv != 0) { return rv; } assert(stream); switch (stream->state) { case NGHTTP2_STREAM_OPENED: return 0; case NGHTTP2_STREAM_CLOSING: return NGHTTP2_ERR_STREAM_CLOSING; default: if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) { return 0; } return NGHTTP2_ERR_INVALID_STREAM_STATE; } } /* * This function checks PUSH_PROMISE frame |frame| with the |stream| * can be sent at this time. The |stream| can be NULL. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED * New stream cannot be created because GOAWAY is already sent or * received. * NGHTTP2_ERR_PROTO * The client side attempts to send PUSH_PROMISE, or the server * sends PUSH_PROMISE for the stream not initiated by the client. * NGHTTP2_ERR_STREAM_CLOSED * The stream is already closed or does not exist. * NGHTTP2_ERR_STREAM_CLOSING * RST_STREAM was queued for this stream. * NGHTTP2_ERR_STREAM_SHUT_WR * The transmission is not allowed for this stream (e.g., a frame * with END_STREAM flag set has already sent) * NGHTTP2_ERR_PUSH_DISABLED * The remote peer disabled reception of PUSH_PROMISE. * NGHTTP2_ERR_SESSION_CLOSING * This session is closing. */ static int session_predicate_push_promise_send(nghttp2_session *session, nghttp2_stream *stream) { int rv; if (!session->server) { return NGHTTP2_ERR_PROTO; } rv = session_predicate_for_stream_send(session, stream); if (rv != 0) { return rv; } assert(stream); if (session->remote_settings.enable_push == 0) { return NGHTTP2_ERR_PUSH_DISABLED; } if (stream->state == NGHTTP2_STREAM_CLOSING) { return NGHTTP2_ERR_STREAM_CLOSING; } if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) { return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED; } return 0; } /* * This function checks WINDOW_UPDATE with the stream ID |stream_id| * can be sent at this time. Note that END_STREAM flag of the previous * frame does not affect the transmission of the WINDOW_UPDATE frame. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_STREAM_CLOSED * The stream is already closed or does not exist. * NGHTTP2_ERR_STREAM_CLOSING * RST_STREAM was queued for this stream. * NGHTTP2_ERR_INVALID_STREAM_STATE * The state of the stream is not valid. * NGHTTP2_ERR_SESSION_CLOSING * This session is closing. */ static int session_predicate_window_update_send(nghttp2_session *session, int32_t stream_id) { nghttp2_stream *stream; if (session_is_closing(session)) { return NGHTTP2_ERR_SESSION_CLOSING; } if (stream_id == 0) { /* Connection-level window update */ return 0; } stream = nghttp2_session_get_stream(session, stream_id); if (stream == NULL) { return NGHTTP2_ERR_STREAM_CLOSED; } if (stream->state == NGHTTP2_STREAM_CLOSING) { return NGHTTP2_ERR_STREAM_CLOSING; } if (state_reserved_local(session, stream)) { return NGHTTP2_ERR_INVALID_STREAM_STATE; } return 0; } static int session_predicate_altsvc_send(nghttp2_session *session, int32_t stream_id) { nghttp2_stream *stream; if (session_is_closing(session)) { return NGHTTP2_ERR_SESSION_CLOSING; } if (stream_id == 0) { return 0; } stream = nghttp2_session_get_stream(session, stream_id); if (stream == NULL) { return NGHTTP2_ERR_STREAM_CLOSED; } if (stream->state == NGHTTP2_STREAM_CLOSING) { return NGHTTP2_ERR_STREAM_CLOSING; } return 0; } static int session_predicate_origin_send(nghttp2_session *session) { if (session_is_closing(session)) { return NGHTTP2_ERR_SESSION_CLOSING; } return 0; } static int session_predicate_priority_update_send(nghttp2_session *session, int32_t stream_id) { nghttp2_stream *stream; if (session_is_closing(session)) { return NGHTTP2_ERR_SESSION_CLOSING; } stream = nghttp2_session_get_stream(session, stream_id); if (stream == NULL) { return 0; } if (stream->state == NGHTTP2_STREAM_CLOSING) { return NGHTTP2_ERR_STREAM_CLOSING; } if (stream->shut_flags & NGHTTP2_SHUT_RD) { return NGHTTP2_ERR_INVALID_STREAM_STATE; } return 0; } /* Take into account settings max frame size and both connection-level flow control here */ static nghttp2_ssize nghttp2_session_enforce_flow_control_limits( nghttp2_session *session, nghttp2_stream *stream, nghttp2_ssize requested_window_size) { DEBUGF("send: remote windowsize connection=%d, remote maxframsize=%u, " "stream(id %d)=%d\n", session->remote_window_size, session->remote_settings.max_frame_size, stream->stream_id, stream->remote_window_size); return nghttp2_min_int32( nghttp2_min_int32(nghttp2_min_int32((int32_t)requested_window_size, stream->remote_window_size), session->remote_window_size), (int32_t)session->remote_settings.max_frame_size); } /* * Returns the maximum length of next data read. If the * connection-level and/or stream-wise flow control are enabled, the * return value takes into account those current window sizes. The remote * settings for max frame size is also taken into account. */ static size_t nghttp2_session_next_data_read(nghttp2_session *session, nghttp2_stream *stream) { nghttp2_ssize window_size; window_size = nghttp2_session_enforce_flow_control_limits( session, stream, NGHTTP2_DATA_PAYLOADLEN); DEBUGF("send: available window=%td\n", window_size); return window_size > 0 ? (size_t)window_size : 0; } /* * This function checks DATA with the |stream| can be sent at this * time. The |stream| can be NULL. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_STREAM_CLOSED * The stream is already closed or does not exist. * NGHTTP2_ERR_STREAM_SHUT_WR * The transmission is not allowed for this stream (e.g., a frame * with END_STREAM flag set has already sent) * NGHTTP2_ERR_STREAM_CLOSING * RST_STREAM was queued for this stream. * NGHTTP2_ERR_INVALID_STREAM_STATE * The state of the stream is not valid. * NGHTTP2_ERR_SESSION_CLOSING * This session is closing. */ static int nghttp2_session_predicate_data_send(nghttp2_session *session, nghttp2_stream *stream) { int rv; rv = session_predicate_for_stream_send(session, stream); if (rv != 0) { return rv; } assert(stream); if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) { /* Request body data */ /* If stream->state is NGHTTP2_STREAM_CLOSING, RST_STREAM was queued but not yet sent. In this case, we won't send DATA frames. */ if (stream->state == NGHTTP2_STREAM_CLOSING) { return NGHTTP2_ERR_STREAM_CLOSING; } if (stream->state == NGHTTP2_STREAM_RESERVED) { return NGHTTP2_ERR_INVALID_STREAM_STATE; } return 0; } /* Response body data */ if (stream->state == NGHTTP2_STREAM_OPENED) { return 0; } if (stream->state == NGHTTP2_STREAM_CLOSING) { return NGHTTP2_ERR_STREAM_CLOSING; } return NGHTTP2_ERR_INVALID_STREAM_STATE; } static nghttp2_ssize session_call_select_padding(nghttp2_session *session, const nghttp2_frame *frame, size_t max_payloadlen) { nghttp2_ssize rv; size_t max_paddedlen; if (frame->hd.length >= max_payloadlen || (!session->callbacks.select_padding_callback2 && !session->callbacks.select_padding_callback)) { return (nghttp2_ssize)frame->hd.length; } max_paddedlen = nghttp2_min_size(frame->hd.length + NGHTTP2_MAX_PADLEN, max_payloadlen); if (session->callbacks.select_padding_callback2) { rv = session->callbacks.select_padding_callback2( session, frame, max_paddedlen, session->user_data); } else { rv = (nghttp2_ssize)session->callbacks.select_padding_callback( session, frame, max_paddedlen, session->user_data); } if (rv < (nghttp2_ssize)frame->hd.length || rv > (nghttp2_ssize)max_paddedlen) { return NGHTTP2_ERR_CALLBACK_FAILURE; } return rv; } /* Add padding to HEADERS or PUSH_PROMISE. We use frame->headers.padlen in this function to use the fact that frame->push_promise has also padlen in the same position. */ static int session_headers_add_pad(nghttp2_session *session, nghttp2_frame *frame) { nghttp2_ssize padded_payloadlen; nghttp2_active_outbound_item *aob; nghttp2_bufs *framebufs; size_t padlen; size_t max_payloadlen; aob = &session->aob; framebufs = &aob->framebufs; max_payloadlen = nghttp2_min_size(NGHTTP2_MAX_PAYLOADLEN, frame->hd.length + NGHTTP2_MAX_PADLEN); padded_payloadlen = session_call_select_padding(session, frame, max_payloadlen); if (nghttp2_is_fatal((int)padded_payloadlen)) { return (int)padded_payloadlen; } padlen = (size_t)padded_payloadlen - frame->hd.length; DEBUGF("send: padding selected: payloadlen=%td, padlen=%zu\n", padded_payloadlen, padlen); nghttp2_frame_add_pad(framebufs, &frame->hd, padlen, 0); frame->headers.padlen = padlen; return 0; } static size_t session_estimate_headers_payload(nghttp2_session *session, const nghttp2_nv *nva, size_t nvlen, size_t additional) { return nghttp2_hd_deflate_bound(&session->hd_deflater, nva, nvlen) + additional; } static int session_pack_extension(nghttp2_session *session, nghttp2_bufs *bufs, nghttp2_frame *frame) { nghttp2_ssize rv; nghttp2_buf *buf; size_t buflen; size_t framelen; assert(session->callbacks.pack_extension_callback2 || session->callbacks.pack_extension_callback); buf = &bufs->head->buf; buflen = nghttp2_min_size(nghttp2_buf_avail(buf), NGHTTP2_MAX_PAYLOADLEN); if (session->callbacks.pack_extension_callback2) { rv = session->callbacks.pack_extension_callback2(session, buf->last, buflen, frame, session->user_data); } else { rv = (nghttp2_ssize)session->callbacks.pack_extension_callback( session, buf->last, buflen, frame, session->user_data); } if (rv == NGHTTP2_ERR_CANCEL) { return (int)rv; } if (rv < 0 || (size_t)rv > buflen) { return NGHTTP2_ERR_CALLBACK_FAILURE; } framelen = (size_t)rv; frame->hd.length = framelen; assert(buf->pos == buf->last); buf->last += framelen; buf->pos -= NGHTTP2_FRAME_HDLEN; nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); return 0; } /* * This function serializes frame for transmission. * * This function returns 0 if it succeeds, or one of negative error * codes, including both fatal and non-fatal ones. */ static int session_prep_frame(nghttp2_session *session, nghttp2_outbound_item *item) { int rv; nghttp2_frame *frame; nghttp2_mem *mem; mem = &session->mem; frame = &item->frame; switch (frame->hd.type) { case NGHTTP2_DATA: { size_t next_readmax; nghttp2_stream *stream; stream = nghttp2_session_get_stream(session, frame->hd.stream_id); if (stream) { assert(stream->item == item); } rv = nghttp2_session_predicate_data_send(session, stream); if (rv != 0) { // If stream was already closed, nghttp2_session_get_stream() // returns NULL, but item is still attached to the stream. // Search stream including closed again. stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); if (stream) { session_detach_stream_item(session, stream); } return rv; } /* Assuming stream is not NULL */ assert(stream); next_readmax = nghttp2_session_next_data_read(session, stream); if (next_readmax == 0) { /* This must be true since we only pop DATA frame item from queue when session->remote_window_size > 0 */ assert(session->remote_window_size > 0); session_defer_stream_item(session, stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); session->aob.item = NULL; active_outbound_item_reset(&session->aob, mem); return NGHTTP2_ERR_DEFERRED; } rv = nghttp2_session_pack_data(session, &session->aob.framebufs, next_readmax, frame, &item->aux_data.data, stream); if (rv == NGHTTP2_ERR_PAUSE) { return rv; } if (rv == NGHTTP2_ERR_DEFERRED) { session_defer_stream_item(session, stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER); session->aob.item = NULL; active_outbound_item_reset(&session->aob, mem); return NGHTTP2_ERR_DEFERRED; } if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { session_detach_stream_item(session, stream); rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR); if (nghttp2_is_fatal(rv)) { return rv; } return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } if (rv != 0) { session_detach_stream_item(session, stream); return rv; } return 0; } case NGHTTP2_HEADERS: { nghttp2_headers_aux_data *aux_data; size_t estimated_payloadlen; aux_data = &item->aux_data.headers; if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { /* initial HEADERS, which opens stream */ nghttp2_stream *stream; stream = nghttp2_session_open_stream( session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_STREAM_INITIAL, aux_data->stream_user_data); if (stream == NULL) { return NGHTTP2_ERR_NOMEM; } rv = session_predicate_request_headers_send(session, item); if (rv != 0) { return rv; } if (session_enforce_http_messaging(session)) { nghttp2_http_record_request_method(stream, frame); } } else { nghttp2_stream *stream; stream = nghttp2_session_get_stream(session, frame->hd.stream_id); if (stream && stream->state == NGHTTP2_STREAM_RESERVED) { rv = session_predicate_push_response_headers_send(session, stream); if (rv == 0) { frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE; if (aux_data->stream_user_data) { stream->stream_user_data = aux_data->stream_user_data; } } } else if (session_predicate_response_headers_send(session, stream) == 0) { frame->headers.cat = NGHTTP2_HCAT_RESPONSE; rv = 0; } else { frame->headers.cat = NGHTTP2_HCAT_HEADERS; rv = session_predicate_headers_send(session, stream); } if (rv != 0) { return rv; } } estimated_payloadlen = session_estimate_headers_payload( session, frame->headers.nva, frame->headers.nvlen, NGHTTP2_PRIORITY_SPECLEN); if (estimated_payloadlen > session->max_send_header_block_length) { return NGHTTP2_ERR_FRAME_SIZE_ERROR; } rv = nghttp2_frame_pack_headers(&session->aob.framebufs, &frame->headers, &session->hd_deflater); if (rv != 0) { return rv; } DEBUGF("send: before padding, HEADERS serialized in %zu bytes\n", nghttp2_bufs_len(&session->aob.framebufs)); rv = session_headers_add_pad(session, frame); if (rv != 0) { return rv; } DEBUGF("send: HEADERS finally serialized in %zu bytes\n", nghttp2_bufs_len(&session->aob.framebufs)); if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { assert(session->last_sent_stream_id < frame->hd.stream_id); session->last_sent_stream_id = frame->hd.stream_id; } return 0; } case NGHTTP2_PRIORITY: { if (session_is_closing(session)) { return NGHTTP2_ERR_SESSION_CLOSING; } /* PRIORITY frame can be sent at any time and to any stream ID. */ nghttp2_frame_pack_priority(&session->aob.framebufs, &frame->priority); /* Peer can send PRIORITY frame against idle stream to create "anchor" in dependency tree. Only client can do this in nghttp2. In nghttp2, only server retains non-active (closed or idle) streams in memory, so we don't open stream here. */ return 0; } case NGHTTP2_RST_STREAM: if (session_is_closing(session)) { return NGHTTP2_ERR_SESSION_CLOSING; } nghttp2_frame_pack_rst_stream(&session->aob.framebufs, &frame->rst_stream); return 0; case NGHTTP2_SETTINGS: { if (frame->hd.flags & NGHTTP2_FLAG_ACK) { assert(session->obq_flood_counter_ > 0); --session->obq_flood_counter_; /* When session is about to close, don't send SETTINGS ACK. We are required to send SETTINGS without ACK though; for example, we have to send SETTINGS as a part of connection preface. */ if (session_is_closing(session)) { return NGHTTP2_ERR_SESSION_CLOSING; } } rv = nghttp2_frame_pack_settings(&session->aob.framebufs, &frame->settings); if (rv != 0) { return rv; } return 0; } case NGHTTP2_PUSH_PROMISE: { nghttp2_stream *stream; size_t estimated_payloadlen; /* stream could be NULL if associated stream was already closed. */ stream = nghttp2_session_get_stream(session, frame->hd.stream_id); /* predicate should fail if stream is NULL. */ rv = session_predicate_push_promise_send(session, stream); if (rv != 0) { return rv; } assert(stream); estimated_payloadlen = session_estimate_headers_payload( session, frame->push_promise.nva, frame->push_promise.nvlen, 0); if (estimated_payloadlen > session->max_send_header_block_length) { return NGHTTP2_ERR_FRAME_SIZE_ERROR; } rv = nghttp2_frame_pack_push_promise( &session->aob.framebufs, &frame->push_promise, &session->hd_deflater); if (rv != 0) { return rv; } rv = session_headers_add_pad(session, frame); if (rv != 0) { return rv; } assert(session->last_sent_stream_id + 2 <= frame->push_promise.promised_stream_id); session->last_sent_stream_id = frame->push_promise.promised_stream_id; return 0; } case NGHTTP2_PING: if (frame->hd.flags & NGHTTP2_FLAG_ACK) { assert(session->obq_flood_counter_ > 0); --session->obq_flood_counter_; } /* PING frame is allowed to be sent unless termination GOAWAY is sent */ if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { return NGHTTP2_ERR_SESSION_CLOSING; } nghttp2_frame_pack_ping(&session->aob.framebufs, &frame->ping); return 0; case NGHTTP2_GOAWAY: rv = nghttp2_frame_pack_goaway(&session->aob.framebufs, &frame->goaway); if (rv != 0) { return rv; } session->local_last_stream_id = frame->goaway.last_stream_id; return 0; case NGHTTP2_WINDOW_UPDATE: rv = session_predicate_window_update_send(session, frame->hd.stream_id); if (rv != 0) { return rv; } nghttp2_frame_pack_window_update(&session->aob.framebufs, &frame->window_update); return 0; case NGHTTP2_CONTINUATION: /* We never handle CONTINUATION here. */ assert(0); return 0; default: { nghttp2_ext_aux_data *aux_data; /* extension frame */ aux_data = &item->aux_data.ext; if (aux_data->builtin == 0) { if (session_is_closing(session)) { return NGHTTP2_ERR_SESSION_CLOSING; } return session_pack_extension(session, &session->aob.framebufs, frame); } switch (frame->hd.type) { case NGHTTP2_ALTSVC: rv = session_predicate_altsvc_send(session, frame->hd.stream_id); if (rv != 0) { return rv; } nghttp2_frame_pack_altsvc(&session->aob.framebufs, &frame->ext); return 0; case NGHTTP2_ORIGIN: rv = session_predicate_origin_send(session); if (rv != 0) { return rv; } rv = nghttp2_frame_pack_origin(&session->aob.framebufs, &frame->ext); if (rv != 0) { return rv; } return 0; case NGHTTP2_PRIORITY_UPDATE: { nghttp2_ext_priority_update *priority_update = frame->ext.payload; rv = session_predicate_priority_update_send(session, priority_update->stream_id); if (rv != 0) { return rv; } nghttp2_frame_pack_priority_update(&session->aob.framebufs, &frame->ext); return 0; } default: /* Unreachable here */ assert(0); return 0; } } } } nghttp2_outbound_item * nghttp2_session_get_next_ob_item(nghttp2_session *session) { if (nghttp2_outbound_queue_top(&session->ob_urgent)) { return nghttp2_outbound_queue_top(&session->ob_urgent); } if (nghttp2_outbound_queue_top(&session->ob_reg)) { return nghttp2_outbound_queue_top(&session->ob_reg); } if (!session_is_outgoing_concurrent_streams_max(session)) { if (nghttp2_outbound_queue_top(&session->ob_syn)) { return nghttp2_outbound_queue_top(&session->ob_syn); } } if (session->remote_window_size > 0) { return session_sched_get_next_outbound_item(session); } return NULL; } nghttp2_outbound_item * nghttp2_session_pop_next_ob_item(nghttp2_session *session) { nghttp2_outbound_item *item; item = nghttp2_outbound_queue_top(&session->ob_urgent); if (item) { nghttp2_outbound_queue_pop(&session->ob_urgent); item->queued = 0; return item; } item = nghttp2_outbound_queue_top(&session->ob_reg); if (item) { nghttp2_outbound_queue_pop(&session->ob_reg); item->queued = 0; return item; } if (!session_is_outgoing_concurrent_streams_max(session)) { item = nghttp2_outbound_queue_top(&session->ob_syn); if (item) { nghttp2_outbound_queue_pop(&session->ob_syn); item->queued = 0; return item; } } if (session->remote_window_size > 0) { return session_sched_get_next_outbound_item(session); } return NULL; } static int session_call_before_frame_send(nghttp2_session *session, nghttp2_frame *frame) { int rv; if (session->callbacks.before_frame_send_callback) { rv = session->callbacks.before_frame_send_callback(session, frame, session->user_data); if (rv == NGHTTP2_ERR_CANCEL) { return rv; } if (rv != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } } return 0; } static int session_call_on_frame_send(nghttp2_session *session, nghttp2_frame *frame) { int rv; if (session->callbacks.on_frame_send_callback) { rv = session->callbacks.on_frame_send_callback(session, frame, session->user_data); if (rv != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } } return 0; } static int find_stream_on_goaway_func(void *entry, void *ptr) { nghttp2_close_stream_on_goaway_arg *arg; nghttp2_stream *stream; arg = (nghttp2_close_stream_on_goaway_arg *)ptr; stream = (nghttp2_stream *)entry; if (nghttp2_session_is_my_stream_id(arg->session, stream->stream_id)) { if (arg->incoming) { return 0; } } else if (!arg->incoming) { return 0; } if (stream->state != NGHTTP2_STREAM_IDLE && (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) == 0 && stream->stream_id > arg->last_stream_id) { /* We are collecting streams to close because we cannot call nghttp2_session_close_stream() inside nghttp2_map_each(). Reuse closed_next member.. bad choice? */ assert(stream->closed_next == NULL); if (arg->head) { stream->closed_next = arg->head; arg->head = stream; } else { arg->head = stream; } } return 0; } /* Closes non-idle and non-closed streams whose stream ID > last_stream_id. If incoming is nonzero, we are going to close incoming streams. Otherwise, close outgoing streams. */ static int session_close_stream_on_goaway(nghttp2_session *session, int32_t last_stream_id, int incoming) { int rv; nghttp2_stream *stream, *next_stream; nghttp2_close_stream_on_goaway_arg arg = {session, NULL, last_stream_id, incoming}; rv = nghttp2_map_each(&session->streams, find_stream_on_goaway_func, &arg); assert(rv == 0); stream = arg.head; while (stream) { next_stream = stream->closed_next; stream->closed_next = NULL; rv = nghttp2_session_close_stream(session, stream->stream_id, NGHTTP2_REFUSED_STREAM); /* stream may be deleted here */ stream = next_stream; if (nghttp2_is_fatal(rv)) { /* Clean up closed_next member just in case */ while (stream) { next_stream = stream->closed_next; stream->closed_next = NULL; stream = next_stream; } return rv; } } return 0; } static void session_reschedule_stream(nghttp2_session *session, nghttp2_stream *stream) { stream->last_writelen = stream->item->frame.hd.length; if (!session->server) { return; } session_sched_reschedule_stream(session, stream); } static int session_update_stream_consumed_size(nghttp2_session *session, nghttp2_stream *stream, size_t delta_size); static int session_update_connection_consumed_size(nghttp2_session *session, size_t delta_size); /* * Called after a frame is sent. This function runs * on_frame_send_callback and handles stream closure upon END_STREAM * or RST_STREAM. This function does not reset session->aob. It is a * responsibility of session_after_frame_sent2. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_CALLBACK_FAILURE * The callback function failed. */ static int session_after_frame_sent1(nghttp2_session *session) { int rv; nghttp2_active_outbound_item *aob = &session->aob; nghttp2_outbound_item *item = aob->item; nghttp2_bufs *framebufs = &aob->framebufs; nghttp2_frame *frame; nghttp2_stream *stream; frame = &item->frame; if (frame->hd.type == NGHTTP2_DATA) { nghttp2_data_aux_data *aux_data; aux_data = &item->aux_data.data; stream = nghttp2_session_get_stream(session, frame->hd.stream_id); /* We update flow control window after a frame was completely sent. This is possible because we choose payload length not to exceed the window */ session->remote_window_size -= (int32_t)frame->hd.length; if (stream) { stream->remote_window_size -= (int32_t)frame->hd.length; } if (stream && aux_data->eof) { session_detach_stream_item(session, stream); /* Call on_frame_send_callback after nghttp2_stream_detach_item(), so that application can issue nghttp2_submit_data2() in the callback. */ if (session->callbacks.on_frame_send_callback) { rv = session_call_on_frame_send(session, frame); if (nghttp2_is_fatal(rv)) { return rv; } } if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { int stream_closed; stream_closed = (stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR; nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); if (nghttp2_is_fatal(rv)) { return rv; } /* stream may be NULL if it was closed */ if (stream_closed) { stream = NULL; } } return 0; } if (session->callbacks.on_frame_send_callback) { rv = session_call_on_frame_send(session, frame); if (nghttp2_is_fatal(rv)) { return rv; } } return 0; } /* non-DATA frame */ if (frame->hd.type == NGHTTP2_HEADERS || frame->hd.type == NGHTTP2_PUSH_PROMISE) { if (nghttp2_bufs_next_present(framebufs)) { DEBUGF("send: CONTINUATION exists, just return\n"); return 0; } } rv = session_call_on_frame_send(session, frame); if (nghttp2_is_fatal(rv)) { return rv; } switch (frame->hd.type) { case NGHTTP2_HEADERS: { nghttp2_headers_aux_data *aux_data; stream = nghttp2_session_get_stream(session, frame->hd.stream_id); if (!stream) { return 0; } switch (frame->headers.cat) { case NGHTTP2_HCAT_REQUEST: { stream->state = NGHTTP2_STREAM_OPENING; if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); } rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); if (nghttp2_is_fatal(rv)) { return rv; } /* We assume aux_data is a pointer to nghttp2_headers_aux_data */ aux_data = &item->aux_data.headers; if (aux_data->dpw.data_prd.read_callback) { /* nghttp2_submit_data_shared() makes a copy of aux_data->dpw */ rv = nghttp2_submit_data_shared(session, NGHTTP2_FLAG_END_STREAM, frame->hd.stream_id, &aux_data->dpw); if (nghttp2_is_fatal(rv)) { return rv; } /* TODO nghttp2_submit_data_shared() may fail if stream has already DATA frame item. We might have to handle it here. */ } return 0; } case NGHTTP2_HCAT_PUSH_RESPONSE: stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_PUSH); ++session->num_outgoing_streams; /* Fall through */ case NGHTTP2_HCAT_RESPONSE: stream->state = NGHTTP2_STREAM_OPENED; /* Fall through */ case NGHTTP2_HCAT_HEADERS: if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); } rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); if (nghttp2_is_fatal(rv)) { return rv; } /* We assume aux_data is a pointer to nghttp2_headers_aux_data */ aux_data = &item->aux_data.headers; if (aux_data->dpw.data_prd.read_callback) { rv = nghttp2_submit_data_shared(session, NGHTTP2_FLAG_END_STREAM, frame->hd.stream_id, &aux_data->dpw); if (nghttp2_is_fatal(rv)) { return rv; } /* TODO nghttp2_submit_data_shared() may fail if stream has already DATA frame item. We might have to handle it here. */ } return 0; default: /* Unreachable */ assert(0); return 0; } } case NGHTTP2_PRIORITY: return 0; case NGHTTP2_RST_STREAM: rv = nghttp2_session_close_stream(session, frame->hd.stream_id, frame->rst_stream.error_code); if (nghttp2_is_fatal(rv)) { return rv; } return 0; case NGHTTP2_GOAWAY: { nghttp2_goaway_aux_data *aux_data; aux_data = &item->aux_data.goaway; if ((aux_data->flags & NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE) == 0) { if (aux_data->flags & NGHTTP2_GOAWAY_AUX_TERM_ON_SEND) { session->goaway_flags |= NGHTTP2_GOAWAY_TERM_SENT; } session->goaway_flags |= NGHTTP2_GOAWAY_SENT; rv = session_close_stream_on_goaway(session, frame->goaway.last_stream_id, 1); if (nghttp2_is_fatal(rv)) { return rv; } } return 0; } case NGHTTP2_WINDOW_UPDATE: if (frame->hd.stream_id == 0) { session->window_update_queued = 0; if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { rv = session_update_connection_consumed_size(session, 0); } else { rv = nghttp2_session_update_recv_connection_window_size(session, 0); } if (nghttp2_is_fatal(rv)) { return rv; } return 0; } stream = nghttp2_session_get_stream(session, frame->hd.stream_id); if (!stream) { return 0; } stream->window_update_queued = 0; /* We don't have to send WINDOW_UPDATE if END_STREAM from peer is seen. */ if (stream->shut_flags & NGHTTP2_SHUT_RD) { return 0; } if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { rv = session_update_stream_consumed_size(session, stream, 0); } else { rv = nghttp2_session_update_recv_stream_window_size(session, stream, 0, 1); } if (nghttp2_is_fatal(rv)) { return rv; } return 0; default: return 0; } } /* * Called after a frame is sent and session_after_frame_sent1. This * function is responsible to reset session->aob. */ static void session_after_frame_sent2(nghttp2_session *session) { nghttp2_active_outbound_item *aob = &session->aob; nghttp2_outbound_item *item = aob->item; nghttp2_bufs *framebufs = &aob->framebufs; nghttp2_frame *frame; nghttp2_mem *mem; nghttp2_stream *stream; nghttp2_data_aux_data *aux_data; mem = &session->mem; frame = &item->frame; if (frame->hd.type != NGHTTP2_DATA) { if (frame->hd.type == NGHTTP2_HEADERS || frame->hd.type == NGHTTP2_PUSH_PROMISE) { if (nghttp2_bufs_next_present(framebufs)) { framebufs->cur = framebufs->cur->next; DEBUGF("send: next CONTINUATION frame, %zu bytes\n", nghttp2_buf_len(&framebufs->cur->buf)); return; } } active_outbound_item_reset(&session->aob, mem); return; } /* DATA frame */ aux_data = &item->aux_data.data; /* On EOF, we have already detached data. Please note that application may issue nghttp2_submit_data2() in on_frame_send_callback (call from session_after_frame_sent1), which attach data to stream. We don't want to detach it. */ if (aux_data->eof) { active_outbound_item_reset(aob, mem); return; } /* Reset no_copy here because next write may not use this. */ aux_data->no_copy = 0; stream = nghttp2_session_get_stream(session, frame->hd.stream_id); /* If session is closed or RST_STREAM was queued, we won't send further data. */ if (nghttp2_session_predicate_data_send(session, stream) != 0) { if (stream) { session_detach_stream_item(session, stream); } active_outbound_item_reset(aob, mem); return; } aob->item = NULL; active_outbound_item_reset(&session->aob, mem); return; } static int session_call_send_data(nghttp2_session *session, nghttp2_outbound_item *item, nghttp2_bufs *framebufs) { int rv; nghttp2_buf *buf; size_t length; nghttp2_frame *frame; nghttp2_data_aux_data *aux_data; buf = &framebufs->cur->buf; frame = &item->frame; length = frame->hd.length - frame->data.padlen; aux_data = &item->aux_data.data; rv = session->callbacks.send_data_callback(session, frame, buf->pos, length, &aux_data->dpw.data_prd.source, session->user_data); switch (rv) { case 0: case NGHTTP2_ERR_WOULDBLOCK: case NGHTTP2_ERR_PAUSE: case NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE: return rv; default: return NGHTTP2_ERR_CALLBACK_FAILURE; } } static nghttp2_ssize nghttp2_session_mem_send_internal(nghttp2_session *session, const uint8_t **data_ptr, int fast_cb) { int rv; nghttp2_active_outbound_item *aob; nghttp2_bufs *framebufs; nghttp2_mem *mem; mem = &session->mem; aob = &session->aob; framebufs = &aob->framebufs; for (;;) { switch (aob->state) { case NGHTTP2_OB_POP_ITEM: { nghttp2_outbound_item *item; item = nghttp2_session_pop_next_ob_item(session); if (item == NULL) { return 0; } rv = session_prep_frame(session, item); if (rv == NGHTTP2_ERR_PAUSE) { return 0; } if (rv == NGHTTP2_ERR_DEFERRED) { DEBUGF("send: frame transmission deferred\n"); break; } if (rv < 0) { int32_t opened_stream_id = 0; uint32_t error_code = NGHTTP2_INTERNAL_ERROR; int rv2 = 0; DEBUGF("send: frame preparation failed with %s\n", nghttp2_strerror(rv)); /* TODO If the error comes from compressor, the connection must be closed. */ if (item->frame.hd.type != NGHTTP2_DATA && session->callbacks.on_frame_not_send_callback && is_non_fatal(rv)) { nghttp2_frame *frame = &item->frame; /* The library is responsible for the transmission of WINDOW_UPDATE frame, so we don't call error callback for it. */ if (frame->hd.type != NGHTTP2_WINDOW_UPDATE && session->callbacks.on_frame_not_send_callback( session, frame, rv, session->user_data) != 0) { nghttp2_outbound_item_free(item, mem); nghttp2_mem_free(mem, item); return NGHTTP2_ERR_CALLBACK_FAILURE; } } /* We have to close stream opened by failed request HEADERS or PUSH_PROMISE. */ switch (item->frame.hd.type) { case NGHTTP2_HEADERS: if (item->frame.headers.cat == NGHTTP2_HCAT_REQUEST) { opened_stream_id = item->frame.hd.stream_id; if (item->aux_data.headers.canceled) { error_code = item->aux_data.headers.error_code; } else { /* Set error_code to REFUSED_STREAM so that application can send request again. */ error_code = NGHTTP2_REFUSED_STREAM; } } break; case NGHTTP2_PUSH_PROMISE: opened_stream_id = item->frame.push_promise.promised_stream_id; break; } if (opened_stream_id) { /* careful not to override rv */ rv2 = nghttp2_session_close_stream(session, opened_stream_id, error_code); } nghttp2_outbound_item_free(item, mem); nghttp2_mem_free(mem, item); active_outbound_item_reset(aob, mem); if (nghttp2_is_fatal(rv2)) { return rv2; } if (rv == NGHTTP2_ERR_HEADER_COMP) { /* If header compression error occurred, should terminate connection. */ rv = nghttp2_session_terminate_session(session, NGHTTP2_INTERNAL_ERROR); } if (nghttp2_is_fatal(rv)) { return rv; } break; } aob->item = item; nghttp2_bufs_rewind(framebufs); if (item->frame.hd.type != NGHTTP2_DATA) { nghttp2_frame *frame; frame = &item->frame; DEBUGF("send: next frame: payloadlen=%zu, type=%u, flags=0x%02x, " "stream_id=%d\n", frame->hd.length, frame->hd.type, frame->hd.flags, frame->hd.stream_id); rv = session_call_before_frame_send(session, frame); if (nghttp2_is_fatal(rv)) { return rv; } if (rv == NGHTTP2_ERR_CANCEL) { int32_t opened_stream_id = 0; uint32_t error_code = NGHTTP2_INTERNAL_ERROR; if (session->callbacks.on_frame_not_send_callback) { if (session->callbacks.on_frame_not_send_callback( session, frame, rv, session->user_data) != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } } /* We have to close stream opened by canceled request HEADERS or PUSH_PROMISE. */ switch (item->frame.hd.type) { case NGHTTP2_HEADERS: if (item->frame.headers.cat == NGHTTP2_HCAT_REQUEST) { opened_stream_id = item->frame.hd.stream_id; /* We don't have to check item->aux_data.headers.canceled since it has already been checked. */ /* Set error_code to REFUSED_STREAM so that application can send request again. */ error_code = NGHTTP2_REFUSED_STREAM; } break; case NGHTTP2_PUSH_PROMISE: opened_stream_id = item->frame.push_promise.promised_stream_id; break; } if (opened_stream_id) { /* careful not to override rv */ int rv2; rv2 = nghttp2_session_close_stream(session, opened_stream_id, error_code); if (nghttp2_is_fatal(rv2)) { return rv2; } } active_outbound_item_reset(aob, mem); break; } } else { DEBUGF("send: next frame: DATA\n"); if (item->aux_data.data.no_copy) { aob->state = NGHTTP2_OB_SEND_NO_COPY; break; } } DEBUGF("send: start transmitting frame type=%u, length=%td\n", framebufs->cur->buf.pos[3], framebufs->cur->buf.last - framebufs->cur->buf.pos); aob->state = NGHTTP2_OB_SEND_DATA; break; } case NGHTTP2_OB_SEND_DATA: { size_t datalen; nghttp2_buf *buf; buf = &framebufs->cur->buf; if (buf->pos == buf->last) { DEBUGF("send: end transmission of a frame\n"); /* Frame has completely sent */ if (fast_cb) { session_after_frame_sent2(session); } else { rv = session_after_frame_sent1(session); if (rv < 0) { /* FATAL */ assert(nghttp2_is_fatal(rv)); return rv; } session_after_frame_sent2(session); } /* We have already adjusted the next state */ break; } *data_ptr = buf->pos; datalen = nghttp2_buf_len(buf); /* We increment the offset here. If send_callback does not send everything, we will adjust it. */ buf->pos += datalen; return (nghttp2_ssize)datalen; } case NGHTTP2_OB_SEND_NO_COPY: { nghttp2_stream *stream; nghttp2_frame *frame; int pause; DEBUGF("send: no copy DATA\n"); frame = &aob->item->frame; stream = nghttp2_session_get_stream(session, frame->hd.stream_id); if (stream == NULL) { DEBUGF("send: no copy DATA cancelled because stream was closed\n"); active_outbound_item_reset(aob, mem); break; } rv = session_call_send_data(session, aob->item, framebufs); if (nghttp2_is_fatal(rv)) { return rv; } if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { session_detach_stream_item(session, stream); rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR); if (nghttp2_is_fatal(rv)) { return rv; } active_outbound_item_reset(aob, mem); break; } if (rv == NGHTTP2_ERR_WOULDBLOCK) { return 0; } pause = (rv == NGHTTP2_ERR_PAUSE); rv = session_after_frame_sent1(session); if (rv < 0) { assert(nghttp2_is_fatal(rv)); return rv; } session_after_frame_sent2(session); /* We have already adjusted the next state */ if (pause) { return 0; } break; } case NGHTTP2_OB_SEND_CLIENT_MAGIC: { size_t datalen; nghttp2_buf *buf; buf = &framebufs->cur->buf; if (buf->pos == buf->last) { DEBUGF("send: end transmission of client magic\n"); active_outbound_item_reset(aob, mem); break; } *data_ptr = buf->pos; datalen = nghttp2_buf_len(buf); buf->pos += datalen; return (nghttp2_ssize)datalen; } } } } ssize_t nghttp2_session_mem_send(nghttp2_session *session, const uint8_t **data_ptr) { return (ssize_t)nghttp2_session_mem_send2(session, data_ptr); } nghttp2_ssize nghttp2_session_mem_send2(nghttp2_session *session, const uint8_t **data_ptr) { int rv; nghttp2_ssize len; *data_ptr = NULL; len = nghttp2_session_mem_send_internal(session, data_ptr, 1); if (len <= 0) { return len; } if (session->aob.item) { /* We have to call session_after_frame_sent1 here to handle stream closure upon transmission of frames. Otherwise, END_STREAM may be reached to client before we call nghttp2_session_mem_send again and we may get exceeding number of incoming streams. */ rv = session_after_frame_sent1(session); if (rv < 0) { assert(nghttp2_is_fatal(rv)); return (nghttp2_ssize)rv; } } return len; } int nghttp2_session_send(nghttp2_session *session) { const uint8_t *data = NULL; nghttp2_ssize datalen; nghttp2_ssize sentlen; nghttp2_bufs *framebufs; framebufs = &session->aob.framebufs; for (;;) { datalen = nghttp2_session_mem_send_internal(session, &data, 0); if (datalen <= 0) { return (int)datalen; } if (session->callbacks.send_callback2) { sentlen = session->callbacks.send_callback2( session, data, (size_t)datalen, 0, session->user_data); } else { sentlen = (nghttp2_ssize)session->callbacks.send_callback( session, data, (size_t)datalen, 0, session->user_data); } if (sentlen < 0) { if (sentlen == NGHTTP2_ERR_WOULDBLOCK) { /* Transmission canceled. Rewind the offset */ framebufs->cur->buf.pos -= datalen; return 0; } return NGHTTP2_ERR_CALLBACK_FAILURE; } /* Rewind the offset to the amount of unsent bytes */ framebufs->cur->buf.pos -= datalen - sentlen; } } static nghttp2_ssize session_recv(nghttp2_session *session, uint8_t *buf, size_t len) { nghttp2_ssize rv; if (session->callbacks.recv_callback2) { rv = session->callbacks.recv_callback2(session, buf, len, 0, session->user_data); } else { rv = (nghttp2_ssize)session->callbacks.recv_callback(session, buf, len, 0, session->user_data); } if (rv > 0) { if ((size_t)rv > len) { return NGHTTP2_ERR_CALLBACK_FAILURE; } } else if (rv < 0 && rv != NGHTTP2_ERR_WOULDBLOCK && rv != NGHTTP2_ERR_EOF) { return NGHTTP2_ERR_CALLBACK_FAILURE; } return rv; } static int session_call_on_begin_frame(nghttp2_session *session, const nghttp2_frame_hd *hd) { int rv; if (session->callbacks.on_begin_frame_callback) { rv = session->callbacks.on_begin_frame_callback(session, hd, session->user_data); if (rv != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } } return 0; } static int session_call_on_frame_received(nghttp2_session *session, nghttp2_frame *frame) { int rv; if (session->callbacks.on_frame_recv_callback) { rv = session->callbacks.on_frame_recv_callback(session, frame, session->user_data); if (rv != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } } return 0; } static int session_call_on_begin_headers(nghttp2_session *session, nghttp2_frame *frame) { int rv; DEBUGF("recv: call on_begin_headers callback stream_id=%d\n", frame->hd.stream_id); if (session->callbacks.on_begin_headers_callback) { rv = session->callbacks.on_begin_headers_callback(session, frame, session->user_data); if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { return rv; } if (rv != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } } return 0; } static int session_call_on_header(nghttp2_session *session, const nghttp2_frame *frame, const nghttp2_hd_nv *nv) { int rv = 0; if (session->callbacks.on_header_callback2) { rv = session->callbacks.on_header_callback2( session, frame, nv->name, nv->value, nv->flags, session->user_data); } else if (session->callbacks.on_header_callback) { rv = session->callbacks.on_header_callback( session, frame, nv->name->base, nv->name->len, nv->value->base, nv->value->len, nv->flags, session->user_data); } if (rv == NGHTTP2_ERR_PAUSE || rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { return rv; } if (rv != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } return 0; } static int session_call_on_invalid_header(nghttp2_session *session, const nghttp2_frame *frame, const nghttp2_hd_nv *nv) { int rv; if (session->callbacks.on_invalid_header_callback2) { rv = session->callbacks.on_invalid_header_callback2( session, frame, nv->name, nv->value, nv->flags, session->user_data); } else if (session->callbacks.on_invalid_header_callback) { rv = session->callbacks.on_invalid_header_callback( session, frame, nv->name->base, nv->name->len, nv->value->base, nv->value->len, nv->flags, session->user_data); } else { /* If both callbacks are not set, the invalid field nv is ignored. */ return 0; } if (rv == NGHTTP2_ERR_PAUSE || rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { return rv; } if (rv != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } return 0; } static int session_call_on_extension_chunk_recv_callback(nghttp2_session *session, const uint8_t *data, size_t len) { int rv; nghttp2_inbound_frame *iframe = &session->iframe; nghttp2_frame *frame = &iframe->frame; if (session->callbacks.on_extension_chunk_recv_callback) { rv = session->callbacks.on_extension_chunk_recv_callback( session, &frame->hd, data, len, session->user_data); if (rv == NGHTTP2_ERR_CANCEL) { return rv; } if (rv != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } } return 0; } static int session_call_unpack_extension_callback(nghttp2_session *session) { int rv; nghttp2_inbound_frame *iframe = &session->iframe; nghttp2_frame *frame = &iframe->frame; void *payload = NULL; rv = session->callbacks.unpack_extension_callback( session, &payload, &frame->hd, session->user_data); if (rv == NGHTTP2_ERR_CANCEL) { return rv; } if (rv != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } frame->ext.payload = payload; return 0; } /* * Handles frame size error. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. */ static int session_handle_frame_size_error(nghttp2_session *session) { /* TODO Currently no callback is called for this error, because we call this callback before reading any payload */ return nghttp2_session_terminate_session(session, NGHTTP2_FRAME_SIZE_ERROR); } static uint32_t get_error_code_from_lib_error_code(int lib_error_code) { switch (lib_error_code) { case NGHTTP2_ERR_STREAM_CLOSED: return NGHTTP2_STREAM_CLOSED; case NGHTTP2_ERR_HEADER_COMP: return NGHTTP2_COMPRESSION_ERROR; case NGHTTP2_ERR_FRAME_SIZE_ERROR: return NGHTTP2_FRAME_SIZE_ERROR; case NGHTTP2_ERR_FLOW_CONTROL: return NGHTTP2_FLOW_CONTROL_ERROR; case NGHTTP2_ERR_REFUSED_STREAM: return NGHTTP2_REFUSED_STREAM; case NGHTTP2_ERR_PROTO: case NGHTTP2_ERR_HTTP_HEADER: case NGHTTP2_ERR_HTTP_MESSAGING: return NGHTTP2_PROTOCOL_ERROR; case NGHTTP2_ERR_INTERNAL: return NGHTTP2_INTERNAL_ERROR; case NGHTTP2_ERR_PUSH_CANCEL: return NGHTTP2_CANCEL; default: return NGHTTP2_INTERNAL_ERROR; } } /* * Calls on_invalid_frame_recv_callback if it is set to |session|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_CALLBACK_FAILURE * User defined callback function fails. */ static int session_call_on_invalid_frame_recv_callback(nghttp2_session *session, nghttp2_frame *frame, int lib_error_code) { if (session->callbacks.on_invalid_frame_recv_callback) { if (session->callbacks.on_invalid_frame_recv_callback( session, frame, lib_error_code, session->user_data) != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } } return 0; } static int session_update_glitch_ratelim(nghttp2_session *session) { if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { return 0; } nghttp2_ratelim_update(&session->glitch_ratelim, nghttp2_time_now_sec()); if (nghttp2_ratelim_drain(&session->glitch_ratelim, 1) == 0) { return 0; } return nghttp2_session_terminate_session(session, NGHTTP2_ENHANCE_YOUR_CALM); } static int session_handle_invalid_stream2(nghttp2_session *session, int32_t stream_id, nghttp2_frame *frame, int lib_error_code) { int rv; rv = nghttp2_session_add_rst_stream( session, stream_id, get_error_code_from_lib_error_code(lib_error_code)); if (rv != 0) { return rv; } if (frame && session->callbacks.on_invalid_frame_recv_callback) { if (session->callbacks.on_invalid_frame_recv_callback( session, frame, lib_error_code, session->user_data) != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } } return 0; } static int session_handle_invalid_stream(nghttp2_session *session, nghttp2_frame *frame, int lib_error_code) { return session_handle_invalid_stream2(session, frame->hd.stream_id, frame, lib_error_code); } static int session_inflate_handle_invalid_stream(nghttp2_session *session, nghttp2_frame *frame, int lib_error_code) { int rv; rv = session_handle_invalid_stream(session, frame, lib_error_code); if (nghttp2_is_fatal(rv)) { return rv; } return NGHTTP2_ERR_IGN_HEADER_BLOCK; } /* * Handles invalid frame which causes connection error. */ static int session_handle_invalid_connection(nghttp2_session *session, nghttp2_frame *frame, int lib_error_code, const char *reason) { if (session->callbacks.on_invalid_frame_recv_callback) { if (session->callbacks.on_invalid_frame_recv_callback( session, frame, lib_error_code, session->user_data) != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } } return nghttp2_session_terminate_session_with_reason( session, get_error_code_from_lib_error_code(lib_error_code), reason); } static int session_inflate_handle_invalid_connection(nghttp2_session *session, nghttp2_frame *frame, int lib_error_code, const char *reason) { int rv; rv = session_handle_invalid_connection(session, frame, lib_error_code, reason); if (nghttp2_is_fatal(rv)) { return rv; } return NGHTTP2_ERR_IGN_HEADER_BLOCK; } /* * Inflates header block in the memory pointed by |in| with |inlen| * bytes. If this function returns NGHTTP2_ERR_PAUSE, the caller must * call this function again, until it returns 0 or one of negative * error code. If |call_header_cb| is zero, the on_header_callback * are not invoked and the function never return NGHTTP2_ERR_PAUSE. If * the given |in| is the last chunk of header block, the |final| must * be nonzero. If header block is successfully processed (which is * indicated by the return value 0, NGHTTP2_ERR_PAUSE or * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE), the number of processed * input bytes is assigned to the |*readlen_ptr|. * * This function return 0 if it succeeds, or one of the negative error * codes: * * NGHTTP2_ERR_CALLBACK_FAILURE * The callback function failed. * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE * The callback returns this error code, indicating that this * stream should be RST_STREAMed. * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_PAUSE * The callback function returned NGHTTP2_ERR_PAUSE * NGHTTP2_ERR_HEADER_COMP * Header decompression failed */ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame, size_t *readlen_ptr, uint8_t *in, size_t inlen, int final, int call_header_cb) { nghttp2_ssize proclen; int rv; int inflate_flags; nghttp2_hd_nv nv; nghttp2_stream *stream; nghttp2_stream *subject_stream; int trailer = 0; *readlen_ptr = 0; stream = nghttp2_session_get_stream(session, frame->hd.stream_id); if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { subject_stream = nghttp2_session_get_stream( session, frame->push_promise.promised_stream_id); } else { subject_stream = stream; trailer = session_trailer_headers(session, stream, frame); } DEBUGF("recv: decoding header block %zu bytes\n", inlen); for (;;) { inflate_flags = 0; proclen = nghttp2_hd_inflate_hd_nv(&session->hd_inflater, &nv, &inflate_flags, in, inlen, final); if (nghttp2_is_fatal((int)proclen)) { return (int)proclen; } if (proclen < 0) { if (session->iframe.state == NGHTTP2_IB_READ_HEADER_BLOCK) { if (subject_stream && subject_stream->state != NGHTTP2_STREAM_CLOSING) { /* Adding RST_STREAM here is very important. It prevents from invoking subsequent callbacks for the same stream ID. */ rv = nghttp2_session_add_rst_stream( session, subject_stream->stream_id, NGHTTP2_COMPRESSION_ERROR); if (nghttp2_is_fatal(rv)) { return rv; } } } rv = nghttp2_session_terminate_session(session, NGHTTP2_COMPRESSION_ERROR); if (nghttp2_is_fatal(rv)) { return rv; } return NGHTTP2_ERR_HEADER_COMP; } in += proclen; inlen -= (size_t)proclen; *readlen_ptr += (size_t)proclen; DEBUGF("recv: proclen=%td\n", proclen); if (call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) { rv = 0; if (subject_stream) { if (session_enforce_http_messaging(session)) { rv = nghttp2_http_on_header(session, subject_stream, frame, &nv, trailer); if (rv == NGHTTP2_ERR_IGN_HTTP_HEADER) { /* Don't overwrite rv here */ int rv2; rv2 = session_call_on_invalid_header(session, frame, &nv); if (rv2 == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { DEBUGF("recv: HTTP error: type=%u, id=%d, header %.*s: %.*s\n", frame->hd.type, frame->hd.stream_id, (int)nv.name->len, nv.name->base, (int)nv.value->len, nv.value->base); rv = session_call_error_callback( session, NGHTTP2_ERR_HTTP_HEADER, "Invalid HTTP header field was received: frame type: " "%u, stream: %d, name: [%.*s], value: [%.*s]", frame->hd.type, frame->hd.stream_id, (int)nv.name->len, nv.name->base, (int)nv.value->len, nv.value->base); if (nghttp2_is_fatal(rv)) { return rv; } rv = session_handle_invalid_stream2( session, subject_stream->stream_id, frame, NGHTTP2_ERR_HTTP_HEADER); if (nghttp2_is_fatal(rv)) { return rv; } return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } else { if (rv2 != 0) { return rv2; } /* header is ignored */ DEBUGF("recv: HTTP ignored: type=%u, id=%d, header %.*s: %.*s\n", frame->hd.type, frame->hd.stream_id, (int)nv.name->len, nv.name->base, (int)nv.value->len, nv.value->base); rv2 = session_call_error_callback( session, NGHTTP2_ERR_HTTP_HEADER, "Ignoring received invalid HTTP header field: frame type: " "%u, stream: %d, name: [%.*s], value: [%.*s]", frame->hd.type, frame->hd.stream_id, (int)nv.name->len, nv.name->base, (int)nv.value->len, nv.value->base); if (nghttp2_is_fatal(rv2)) { return rv2; } } } if (rv == NGHTTP2_ERR_HTTP_HEADER) { DEBUGF("recv: HTTP error: type=%u, id=%d, header %.*s: %.*s\n", frame->hd.type, frame->hd.stream_id, (int)nv.name->len, nv.name->base, (int)nv.value->len, nv.value->base); rv = session_call_error_callback( session, NGHTTP2_ERR_HTTP_HEADER, "Invalid HTTP header field was received: frame type: " "%u, stream: %d, name: [%.*s], value: [%.*s]", frame->hd.type, frame->hd.stream_id, (int)nv.name->len, nv.name->base, (int)nv.value->len, nv.value->base); if (nghttp2_is_fatal(rv)) { return rv; } return nghttp2_session_terminate_session(session, NGHTTP2_PROTOCOL_ERROR); } } if (rv == 0) { rv = session_call_on_header(session, frame, &nv); /* This handles NGHTTP2_ERR_PAUSE and NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE as well */ if (rv != 0) { return rv; } } } } if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { nghttp2_hd_inflate_end_headers(&session->hd_inflater); break; } if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) { break; } } return 0; } /* * Call this function when HEADERS frame was completely received. * * This function returns 0 if it succeeds, or one of negative error * codes: * * NGHTTP2_ERR_CALLBACK_FAILURE * The callback function failed. * NGHTTP2_ERR_NOMEM * Out of memory. */ static int session_end_stream_headers_received(nghttp2_session *session, nghttp2_frame *frame, nghttp2_stream *stream) { int rv; assert(frame->hd.type == NGHTTP2_HEADERS); if (session->server && session_enforce_http_messaging(session) && frame->headers.cat == NGHTTP2_HCAT_REQUEST && !(stream->flags & NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES) && (stream->http_flags & NGHTTP2_HTTP_FLAG_PRIORITY)) { rv = session_update_stream_priority(session, stream, stream->http_extpri); if (rv != 0) { assert(nghttp2_is_fatal(rv)); return rv; } } if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { return 0; } nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); if (nghttp2_is_fatal(rv)) { return rv; } return 0; } static int session_after_header_block_received(nghttp2_session *session) { int rv = 0; nghttp2_frame *frame = &session->iframe.frame; nghttp2_stream *stream; /* We don't call on_frame_recv_callback if stream has been closed already or being closed. */ stream = nghttp2_session_get_stream(session, frame->hd.stream_id); if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) { return 0; } if (session_enforce_http_messaging(session)) { if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { nghttp2_stream *subject_stream; subject_stream = nghttp2_session_get_stream( session, frame->push_promise.promised_stream_id); if (subject_stream) { rv = nghttp2_http_on_request_headers(subject_stream, frame); } } else { assert(frame->hd.type == NGHTTP2_HEADERS); switch (frame->headers.cat) { case NGHTTP2_HCAT_REQUEST: rv = nghttp2_http_on_request_headers(stream, frame); break; case NGHTTP2_HCAT_RESPONSE: case NGHTTP2_HCAT_PUSH_RESPONSE: rv = nghttp2_http_on_response_headers(stream); break; case NGHTTP2_HCAT_HEADERS: if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) { assert(!session->server); rv = nghttp2_http_on_response_headers(stream); } else { rv = nghttp2_http_on_trailer_headers(stream, frame); } break; default: assert(0); } if (rv == 0 && (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { rv = nghttp2_http_on_remote_end_stream(stream); } } if (rv != 0) { return nghttp2_session_terminate_session(session, NGHTTP2_PROTOCOL_ERROR); } } rv = session_call_on_frame_received(session, frame); if (nghttp2_is_fatal(rv)) { return rv; } if (frame->hd.type != NGHTTP2_HEADERS) { return 0; } return session_end_stream_headers_received(session, frame, stream); } int nghttp2_session_on_request_headers_received(nghttp2_session *session, nghttp2_frame *frame) { int rv = 0; nghttp2_stream *stream; if (frame->hd.stream_id == 0) { return session_inflate_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: stream_id == 0"); } /* If client receives idle stream from server, it is invalid regardless stream ID is even or odd. This is because client is not expected to receive request from server. */ if (!session->server) { if (session_detect_idle_stream(session, frame->hd.stream_id)) { return session_inflate_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: client received request"); } return NGHTTP2_ERR_IGN_HEADER_BLOCK; } assert(session->server); if (!session_is_new_peer_stream_id(session, frame->hd.stream_id)) { if (frame->hd.stream_id == 0 || nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { return session_inflate_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: invalid stream_id"); } /* RFC 7540 says if an endpoint receives a HEADERS with invalid * stream ID (e.g, numerically smaller than previous), it MUST * issue connection error with error code PROTOCOL_ERROR. It is a * bit hard to detect this, since we cannot remember all streams * we observed so far. * * You might imagine this is really easy. But no. HTTP/2 is * asynchronous protocol, and usually client and server do not * share the complete picture of open/closed stream status. For * example, after server sends RST_STREAM for a stream, client may * send trailer HEADERS for that stream. If naive server detects * that, and issued connection error, then it is a bug of server * implementation since client is not wrong if it did not get * RST_STREAM when it issued trailer HEADERS. * * At the moment, we are very conservative here. We only use * connection error if stream ID refers idle stream, or we are * sure that stream is half-closed(remote) or closed. Otherwise * we just ignore HEADERS for now. */ stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); if (stream && (stream->shut_flags & NGHTTP2_SHUT_RD)) { return session_inflate_handle_invalid_connection( session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed"); } return NGHTTP2_ERR_IGN_HEADER_BLOCK; } session->last_recv_stream_id = frame->hd.stream_id; if (session_is_incoming_concurrent_streams_max(session)) { return session_inflate_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: max concurrent streams exceeded"); } if (!session_allow_incoming_new_stream(session)) { /* We just ignore stream after GOAWAY was sent */ return NGHTTP2_ERR_IGN_HEADER_BLOCK; } if (frame->headers.pri_spec.stream_id == frame->hd.stream_id) { return session_inflate_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: depend on itself"); } if (session_is_incoming_concurrent_streams_pending_max(session)) { return session_inflate_handle_invalid_stream(session, frame, NGHTTP2_ERR_REFUSED_STREAM); } stream = nghttp2_session_open_stream(session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_STREAM_OPENING, NULL); if (!stream) { return NGHTTP2_ERR_NOMEM; } session->last_proc_stream_id = session->last_recv_stream_id; rv = session_call_on_begin_headers(session, frame); if (rv != 0) { return rv; } return 0; } int nghttp2_session_on_response_headers_received(nghttp2_session *session, nghttp2_frame *frame, nghttp2_stream *stream) { int rv; /* This function is only called if stream->state == NGHTTP2_STREAM_OPENING and stream_id is local side initiated. */ assert(stream->state == NGHTTP2_STREAM_OPENING && nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)); if (frame->hd.stream_id == 0) { return session_inflate_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "response HEADERS: stream_id == 0"); } if (stream->shut_flags & NGHTTP2_SHUT_RD) { /* half closed (remote): from the spec: If an endpoint receives additional frames for a stream that is in this state it MUST respond with a stream error (Section 5.4.2) of type STREAM_CLOSED. We go further, and make it connection error. */ return session_inflate_handle_invalid_connection( session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed"); } stream->state = NGHTTP2_STREAM_OPENED; rv = session_call_on_begin_headers(session, frame); if (rv != 0) { return rv; } return 0; } int nghttp2_session_on_push_response_headers_received(nghttp2_session *session, nghttp2_frame *frame, nghttp2_stream *stream) { int rv = 0; assert(stream->state == NGHTTP2_STREAM_RESERVED); if (frame->hd.stream_id == 0) { return session_inflate_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "push response HEADERS: stream_id == 0"); } if (session->server) { return session_inflate_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "HEADERS: no HEADERS allowed from client in reserved state"); } if (session_is_incoming_concurrent_streams_max(session)) { return session_inflate_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "push response HEADERS: max concurrent streams exceeded"); } if (!session_allow_incoming_new_stream(session)) { /* We don't accept new stream after GOAWAY was sent. */ return NGHTTP2_ERR_IGN_HEADER_BLOCK; } if (session_is_incoming_concurrent_streams_pending_max(session)) { return session_inflate_handle_invalid_stream(session, frame, NGHTTP2_ERR_REFUSED_STREAM); } nghttp2_stream_promise_fulfilled(stream); if (!nghttp2_session_is_my_stream_id(session, stream->stream_id)) { --session->num_incoming_reserved_streams; } ++session->num_incoming_streams; rv = session_call_on_begin_headers(session, frame); if (rv != 0) { return rv; } return 0; } int nghttp2_session_on_headers_received(nghttp2_session *session, nghttp2_frame *frame, nghttp2_stream *stream) { int rv = 0; if (frame->hd.stream_id == 0) { return session_inflate_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "HEADERS: stream_id == 0"); } if ((stream->shut_flags & NGHTTP2_SHUT_RD)) { /* half closed (remote): from the spec: If an endpoint receives additional frames for a stream that is in this state it MUST respond with a stream error (Section 5.4.2) of type STREAM_CLOSED. we go further, and make it connection error. */ return session_inflate_handle_invalid_connection( session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed"); } if (nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { if (stream->state == NGHTTP2_STREAM_OPENED) { rv = session_call_on_begin_headers(session, frame); if (rv != 0) { return rv; } return 0; } return NGHTTP2_ERR_IGN_HEADER_BLOCK; } /* If this is remote peer initiated stream, it is OK unless it has sent END_STREAM frame already. But if stream is in NGHTTP2_STREAM_CLOSING, we discard the frame. This is a race condition. */ if (stream->state != NGHTTP2_STREAM_CLOSING) { rv = session_call_on_begin_headers(session, frame); if (rv != 0) { return rv; } return 0; } return NGHTTP2_ERR_IGN_HEADER_BLOCK; } static int session_process_headers_frame(nghttp2_session *session) { nghttp2_inbound_frame *iframe = &session->iframe; nghttp2_frame *frame = &iframe->frame; nghttp2_stream *stream; nghttp2_frame_unpack_headers_payload(&frame->headers, iframe->sbuf.pos); stream = nghttp2_session_get_stream(session, frame->hd.stream_id); if (!stream) { frame->headers.cat = NGHTTP2_HCAT_REQUEST; return nghttp2_session_on_request_headers_received(session, frame); } if (stream->state == NGHTTP2_STREAM_RESERVED) { frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE; return nghttp2_session_on_push_response_headers_received(session, frame, stream); } if (stream->state == NGHTTP2_STREAM_OPENING && nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { frame->headers.cat = NGHTTP2_HCAT_RESPONSE; return nghttp2_session_on_response_headers_received(session, frame, stream); } frame->headers.cat = NGHTTP2_HCAT_HEADERS; return nghttp2_session_on_headers_received(session, frame, stream); } static int session_update_stream_reset_ratelim(nghttp2_session *session) { if (!session->server || (session->goaway_flags & NGHTTP2_GOAWAY_SUBMITTED)) { return 0; } nghttp2_ratelim_update(&session->stream_reset_ratelim, nghttp2_time_now_sec()); if (nghttp2_ratelim_drain(&session->stream_reset_ratelim, 1) == 0) { return 0; } return nghttp2_session_add_goaway(session, session->last_recv_stream_id, NGHTTP2_INTERNAL_ERROR, NULL, 0, NGHTTP2_GOAWAY_AUX_NONE); } int nghttp2_session_on_rst_stream_received(nghttp2_session *session, nghttp2_frame *frame) { int rv; nghttp2_stream *stream; if (frame->hd.stream_id == 0) { return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, "RST_STREAM: stream_id == 0"); } if (session_detect_idle_stream(session, frame->hd.stream_id)) { return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, "RST_STREAM: stream in idle"); } stream = nghttp2_session_get_stream(session, frame->hd.stream_id); if (stream) { /* We may use stream->shut_flags for strict error checking. */ nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); } rv = session_call_on_frame_received(session, frame); if (rv != 0) { return rv; } rv = nghttp2_session_close_stream(session, frame->hd.stream_id, frame->rst_stream.error_code); if (nghttp2_is_fatal(rv)) { return rv; } return session_update_stream_reset_ratelim(session); } static int session_process_rst_stream_frame(nghttp2_session *session) { nghttp2_inbound_frame *iframe = &session->iframe; nghttp2_frame *frame = &iframe->frame; nghttp2_frame_unpack_rst_stream_payload(&frame->rst_stream, iframe->sbuf.pos); return nghttp2_session_on_rst_stream_received(session, frame); } static int update_remote_initial_window_size_func(void *entry, void *ptr) { int rv; nghttp2_update_window_size_arg *arg; nghttp2_stream *stream; arg = (nghttp2_update_window_size_arg *)ptr; stream = (nghttp2_stream *)entry; rv = nghttp2_stream_update_remote_initial_window_size( stream, arg->new_window_size, arg->old_window_size); if (rv != 0) { return NGHTTP2_ERR_FLOW_CONTROL; } /* If window size gets positive, push deferred DATA frame to outbound queue. */ if (stream->remote_window_size > 0 && nghttp2_stream_check_deferred_by_flow_control(stream)) { rv = session_resume_deferred_stream_item( arg->session, stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); if (nghttp2_is_fatal(rv)) { return rv; } } return 0; } /* * Updates the remote initial window size of all active streams. If * error occurs, all streams may not be updated. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_FLOW_CONTROL * Window size gets out of range. */ static int session_update_remote_initial_window_size(nghttp2_session *session, int32_t new_initial_window_size) { nghttp2_update_window_size_arg arg; arg.session = session; arg.new_window_size = new_initial_window_size; arg.old_window_size = (int32_t)session->remote_settings.initial_window_size; return nghttp2_map_each(&session->streams, update_remote_initial_window_size_func, &arg); } static int update_local_initial_window_size_func(void *entry, void *ptr) { int rv; nghttp2_update_window_size_arg *arg; nghttp2_stream *stream; arg = (nghttp2_update_window_size_arg *)ptr; stream = (nghttp2_stream *)entry; rv = nghttp2_stream_update_local_initial_window_size( stream, arg->new_window_size, arg->old_window_size); if (rv != 0) { return NGHTTP2_ERR_FLOW_CONTROL; } if (stream->window_update_queued) { return 0; } if (arg->session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { return session_update_stream_consumed_size(arg->session, stream, 0); } if (nghttp2_should_send_window_update(stream->local_window_size, stream->recv_window_size)) { rv = nghttp2_session_add_window_update(arg->session, NGHTTP2_FLAG_NONE, stream->stream_id, stream->recv_window_size); if (rv != 0) { return rv; } stream->recv_window_size = 0; } return 0; } /* * Updates the local initial window size of all active streams. If * error occurs, all streams may not be updated. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_FLOW_CONTROL * Window size gets out of range. */ static int session_update_local_initial_window_size(nghttp2_session *session, int32_t new_initial_window_size, int32_t old_initial_window_size) { nghttp2_update_window_size_arg arg; arg.session = session; arg.new_window_size = new_initial_window_size; arg.old_window_size = old_initial_window_size; return nghttp2_map_each(&session->streams, update_local_initial_window_size_func, &arg); } /* * Apply SETTINGS values |iv| having |niv| elements to the local * settings. We assumes that all values in |iv| is correct, since we * validated them in nghttp2_session_add_settings() already. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_HEADER_COMP * The header table size is out of range * NGHTTP2_ERR_NOMEM * Out of memory */ int nghttp2_session_update_local_settings(nghttp2_session *session, nghttp2_settings_entry *iv, size_t niv) { int rv; size_t i; int32_t new_initial_window_size = -1; uint32_t header_table_size = 0; uint32_t min_header_table_size = UINT32_MAX; uint8_t header_table_size_seen = 0; /* For NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, use the value last seen. For NGHTTP2_SETTINGS_HEADER_TABLE_SIZE, use both minimum value and last seen value. */ for (i = 0; i < niv; ++i) { switch (iv[i].settings_id) { case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: header_table_size_seen = 1; header_table_size = iv[i].value; min_header_table_size = nghttp2_min_uint32(min_header_table_size, iv[i].value); break; case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: new_initial_window_size = (int32_t)iv[i].value; break; } } if (header_table_size_seen) { if (min_header_table_size < header_table_size) { rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater, min_header_table_size); if (rv != 0) { return rv; } } rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater, header_table_size); if (rv != 0) { return rv; } } if (new_initial_window_size != -1) { rv = session_update_local_initial_window_size( session, new_initial_window_size, (int32_t)session->local_settings.initial_window_size); if (rv != 0) { return rv; } } for (i = 0; i < niv; ++i) { switch (iv[i].settings_id) { case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: session->local_settings.header_table_size = iv[i].value; break; case NGHTTP2_SETTINGS_ENABLE_PUSH: session->local_settings.enable_push = iv[i].value; break; case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: session->local_settings.max_concurrent_streams = iv[i].value; break; case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: session->local_settings.initial_window_size = iv[i].value; break; case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: session->local_settings.max_frame_size = iv[i].value; break; case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: session->local_settings.max_header_list_size = iv[i].value; break; case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: session->local_settings.enable_connect_protocol = iv[i].value; break; case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES: session->local_settings.no_rfc7540_priorities = iv[i].value; break; } } return 0; } int nghttp2_session_on_settings_received(nghttp2_session *session, nghttp2_frame *frame, int noack) { int rv; size_t i; nghttp2_mem *mem; nghttp2_inflight_settings *settings; mem = &session->mem; if (frame->hd.stream_id != 0) { return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: stream_id != 0"); } if (frame->hd.flags & NGHTTP2_FLAG_ACK) { if (frame->settings.niv != 0) { return session_handle_invalid_connection( session, frame, NGHTTP2_ERR_FRAME_SIZE_ERROR, "SETTINGS: ACK and payload != 0"); } settings = session->inflight_settings_head; if (!settings) { return session_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: unexpected ACK"); } rv = nghttp2_session_update_local_settings(session, settings->iv, settings->niv); session->inflight_settings_head = settings->next; inflight_settings_del(settings, mem); if (rv != 0) { if (nghttp2_is_fatal(rv)) { return rv; } return session_handle_invalid_connection(session, frame, rv, NULL); } return session_call_on_frame_received(session, frame); } if (!session->remote_settings_received) { session->remote_settings.max_concurrent_streams = NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS; session->remote_settings_received = 1; } for (i = 0; i < frame->settings.niv; ++i) { nghttp2_settings_entry *entry = &frame->settings.iv[i]; switch (entry->settings_id) { case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: rv = nghttp2_hd_deflate_change_table_size(&session->hd_deflater, entry->value); if (rv != 0) { if (nghttp2_is_fatal(rv)) { return rv; } else { return session_handle_invalid_connection( session, frame, NGHTTP2_ERR_HEADER_COMP, NULL); } } session->remote_settings.header_table_size = entry->value; break; case NGHTTP2_SETTINGS_ENABLE_PUSH: if (entry->value != 0 && entry->value != 1) { return session_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: invalid SETTINGS_ENBLE_PUSH"); } if (!session->server && entry->value != 0) { return session_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: server attempted to enable push"); } session->remote_settings.enable_push = entry->value; break; case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: session->remote_settings.max_concurrent_streams = entry->value; break; case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: /* Update the initial window size of the all active streams */ /* Check that initial_window_size < (1u << 31) */ if (entry->value > NGHTTP2_MAX_WINDOW_SIZE) { return session_handle_invalid_connection( session, frame, NGHTTP2_ERR_FLOW_CONTROL, "SETTINGS: too large SETTINGS_INITIAL_WINDOW_SIZE"); } rv = session_update_remote_initial_window_size(session, (int32_t)entry->value); if (nghttp2_is_fatal(rv)) { return rv; } if (rv != 0) { return session_handle_invalid_connection( session, frame, NGHTTP2_ERR_FLOW_CONTROL, NULL); } session->remote_settings.initial_window_size = entry->value; break; case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: if (entry->value < NGHTTP2_MAX_FRAME_SIZE_MIN || entry->value > NGHTTP2_MAX_FRAME_SIZE_MAX) { return session_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: invalid SETTINGS_MAX_FRAME_SIZE"); } session->remote_settings.max_frame_size = entry->value; break; case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: session->remote_settings.max_header_list_size = entry->value; break; case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: if (entry->value != 0 && entry->value != 1) { return session_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: invalid SETTINGS_ENABLE_CONNECT_PROTOCOL"); } if (!session->server && session->remote_settings.enable_connect_protocol && entry->value == 0) { return session_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: server attempted to disable " "SETTINGS_ENABLE_CONNECT_PROTOCOL"); } session->remote_settings.enable_connect_protocol = entry->value; break; case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES: if (entry->value != 0 && entry->value != 1) { return session_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: invalid SETTINGS_NO_RFC7540_PRIORITIES"); } if (session->remote_settings.no_rfc7540_priorities != UINT32_MAX && session->remote_settings.no_rfc7540_priorities != entry->value) { return session_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: SETTINGS_NO_RFC7540_PRIORITIES cannot be changed"); } session->remote_settings.no_rfc7540_priorities = entry->value; break; } } if (session->remote_settings.no_rfc7540_priorities == UINT32_MAX) { session->remote_settings.no_rfc7540_priorities = 0; } if (!noack && !session_is_closing(session)) { rv = nghttp2_session_add_settings(session, NGHTTP2_FLAG_ACK, NULL, 0); if (rv != 0) { if (nghttp2_is_fatal(rv)) { return rv; } return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_INTERNAL, NULL); } } return session_call_on_frame_received(session, frame); } static int session_process_settings_frame(nghttp2_session *session) { nghttp2_inbound_frame *iframe = &session->iframe; nghttp2_frame *frame = &iframe->frame; size_t i; nghttp2_settings_entry min_header_size_entry; if (iframe->max_niv) { min_header_size_entry = iframe->iv[iframe->max_niv - 1]; if (min_header_size_entry.value < UINT32_MAX) { /* If we have less value, then we must have SETTINGS_HEADER_TABLE_SIZE in i < iframe->niv */ for (i = 0; i < iframe->niv; ++i) { if (iframe->iv[i].settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) { break; } } assert(i < iframe->niv); if (min_header_size_entry.value != iframe->iv[i].value) { iframe->iv[iframe->niv++] = iframe->iv[i]; iframe->iv[i] = min_header_size_entry; } } } nghttp2_frame_unpack_settings_payload(&frame->settings, iframe->iv, iframe->niv); iframe->iv = NULL; iframe->niv = 0; iframe->max_niv = 0; return nghttp2_session_on_settings_received(session, frame, 0 /* ACK */); } int nghttp2_session_on_push_promise_received(nghttp2_session *session, nghttp2_frame *frame) { int rv; nghttp2_stream *stream; nghttp2_stream *promised_stream; if (frame->hd.stream_id == 0) { return session_inflate_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream_id == 0"); } if (session->server || session->local_settings.enable_push == 0) { return session_inflate_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: push disabled"); } if (!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { return session_inflate_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: invalid stream_id"); } if (!session_allow_incoming_new_stream(session)) { /* We just discard PUSH_PROMISE after GOAWAY was sent */ return NGHTTP2_ERR_IGN_HEADER_BLOCK; } if (!session_is_new_peer_stream_id(session, frame->push_promise.promised_stream_id)) { /* The spec says if an endpoint receives a PUSH_PROMISE with illegal stream ID is subject to a connection error of type PROTOCOL_ERROR. */ return session_inflate_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: invalid promised_stream_id"); } if (session_detect_idle_stream(session, frame->hd.stream_id)) { return session_inflate_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream in idle"); } session->last_recv_stream_id = frame->push_promise.promised_stream_id; stream = nghttp2_session_get_stream(session, frame->hd.stream_id); if (!stream || stream->state == NGHTTP2_STREAM_CLOSING || !session->pending_enable_push || session->num_incoming_reserved_streams >= session->max_incoming_reserved_streams) { /* Currently, client does not retain closed stream, so we don't check NGHTTP2_SHUT_RD condition here. */ rv = session_handle_invalid_stream2(session, frame->push_promise.promised_stream_id, NULL, NGHTTP2_ERR_PUSH_CANCEL); if (rv != 0) { return rv; } return NGHTTP2_ERR_IGN_HEADER_BLOCK; } if (stream->shut_flags & NGHTTP2_SHUT_RD) { return session_inflate_handle_invalid_connection( session, frame, NGHTTP2_ERR_STREAM_CLOSED, "PUSH_PROMISE: stream closed"); } promised_stream = nghttp2_session_open_stream( session, frame->push_promise.promised_stream_id, NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_STREAM_RESERVED, NULL); if (!promised_stream) { return NGHTTP2_ERR_NOMEM; } session->last_proc_stream_id = session->last_recv_stream_id; rv = session_call_on_begin_headers(session, frame); if (rv != 0) { return rv; } return 0; } static int session_process_push_promise_frame(nghttp2_session *session) { nghttp2_inbound_frame *iframe = &session->iframe; nghttp2_frame *frame = &iframe->frame; nghttp2_frame_unpack_push_promise_payload(&frame->push_promise, iframe->sbuf.pos); return nghttp2_session_on_push_promise_received(session, frame); } int nghttp2_session_on_ping_received(nghttp2_session *session, nghttp2_frame *frame) { int rv = 0; if (frame->hd.stream_id != 0) { return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, "PING: stream_id != 0"); } if ((session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_PING_ACK) == 0 && (frame->hd.flags & NGHTTP2_FLAG_ACK) == 0 && !session_is_closing(session)) { /* Peer sent ping, so ping it back */ rv = nghttp2_session_add_ping(session, NGHTTP2_FLAG_ACK, frame->ping.opaque_data); if (rv != 0) { return rv; } } return session_call_on_frame_received(session, frame); } static int session_process_ping_frame(nghttp2_session *session) { nghttp2_inbound_frame *iframe = &session->iframe; nghttp2_frame *frame = &iframe->frame; nghttp2_frame_unpack_ping_payload(&frame->ping, iframe->sbuf.pos); return nghttp2_session_on_ping_received(session, frame); } int nghttp2_session_on_goaway_received(nghttp2_session *session, nghttp2_frame *frame) { int rv; if (frame->hd.stream_id != 0) { return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, "GOAWAY: stream_id != 0"); } /* Spec says Endpoints MUST NOT increase the value they send in the last stream identifier. */ if ((frame->goaway.last_stream_id > 0 && !nghttp2_session_is_my_stream_id(session, frame->goaway.last_stream_id)) || session->remote_last_stream_id < frame->goaway.last_stream_id) { return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, "GOAWAY: invalid last_stream_id"); } session->goaway_flags |= NGHTTP2_GOAWAY_RECV; session->remote_last_stream_id = frame->goaway.last_stream_id; rv = session_call_on_frame_received(session, frame); if (nghttp2_is_fatal(rv)) { return rv; } return session_close_stream_on_goaway(session, frame->goaway.last_stream_id, 0); } static int session_process_goaway_frame(nghttp2_session *session) { nghttp2_inbound_frame *iframe = &session->iframe; nghttp2_frame *frame = &iframe->frame; nghttp2_frame_unpack_goaway_payload(&frame->goaway, iframe->sbuf.pos, iframe->lbuf.pos, nghttp2_buf_len(&iframe->lbuf)); nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0); return nghttp2_session_on_goaway_received(session, frame); } static int session_on_connection_window_update_received(nghttp2_session *session, nghttp2_frame *frame) { /* Handle connection-level flow control */ if (frame->window_update.window_size_increment == 0) { return session_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPDATE: window_size_increment == 0"); } if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment < session->remote_window_size) { return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_FLOW_CONTROL, NULL); } session->remote_window_size += frame->window_update.window_size_increment; return session_call_on_frame_received(session, frame); } static int session_on_stream_window_update_received(nghttp2_session *session, nghttp2_frame *frame) { int rv; nghttp2_stream *stream; if (session_detect_idle_stream(session, frame->hd.stream_id)) { return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPDATE to idle stream"); } stream = nghttp2_session_get_stream(session, frame->hd.stream_id); if (!stream) { return 0; } if (state_reserved_remote(session, stream)) { return session_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPADATE to reserved stream"); } if (frame->window_update.window_size_increment == 0) { return session_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPDATE: window_size_increment == 0"); } if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment < stream->remote_window_size) { return session_handle_invalid_connection( session, frame, NGHTTP2_ERR_FLOW_CONTROL, "WINDOW_UPDATE: window size overflow"); } stream->remote_window_size += frame->window_update.window_size_increment; if (stream->remote_window_size > 0 && nghttp2_stream_check_deferred_by_flow_control(stream)) { rv = session_resume_deferred_stream_item( session, stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); if (nghttp2_is_fatal(rv)) { return rv; } } return session_call_on_frame_received(session, frame); } int nghttp2_session_on_window_update_received(nghttp2_session *session, nghttp2_frame *frame) { if (frame->hd.stream_id == 0) { return session_on_connection_window_update_received(session, frame); } else { return session_on_stream_window_update_received(session, frame); } } static int session_process_window_update_frame(nghttp2_session *session) { nghttp2_inbound_frame *iframe = &session->iframe; nghttp2_frame *frame = &iframe->frame; nghttp2_frame_unpack_window_update_payload(&frame->window_update, iframe->sbuf.pos); return nghttp2_session_on_window_update_received(session, frame); } int nghttp2_session_on_altsvc_received(nghttp2_session *session, nghttp2_frame *frame) { nghttp2_ext_altsvc *altsvc; nghttp2_stream *stream; altsvc = frame->ext.payload; /* session->server case has been excluded */ if (frame->hd.stream_id == 0) { if (altsvc->origin_len == 0) { return session_call_on_invalid_frame_recv_callback(session, frame, NGHTTP2_ERR_PROTO); } } else { if (altsvc->origin_len > 0) { return session_call_on_invalid_frame_recv_callback(session, frame, NGHTTP2_ERR_PROTO); } stream = nghttp2_session_get_stream(session, frame->hd.stream_id); if (!stream) { return 0; } if (stream->state == NGHTTP2_STREAM_CLOSING) { return 0; } } if (altsvc->field_value_len == 0) { return session_call_on_invalid_frame_recv_callback(session, frame, NGHTTP2_ERR_PROTO); } return session_call_on_frame_received(session, frame); } int nghttp2_session_on_origin_received(nghttp2_session *session, nghttp2_frame *frame) { return session_call_on_frame_received(session, frame); } int nghttp2_session_on_priority_update_received(nghttp2_session *session, nghttp2_frame *frame) { nghttp2_ext_priority_update *priority_update; nghttp2_stream *stream; nghttp2_extpri extpri; int rv; assert(session->server); priority_update = frame->ext.payload; if (frame->hd.stream_id != 0) { return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, "PRIORITY_UPDATE: stream_id == 0"); } if (nghttp2_session_is_my_stream_id(session, priority_update->stream_id)) { if (session_detect_idle_stream(session, priority_update->stream_id)) { return session_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "PRIORITY_UPDATE: prioritizing idle push is not allowed"); } /* TODO Ignore priority signal to a push stream for now */ return session_call_on_frame_received(session, frame); } stream = nghttp2_session_get_stream_raw(session, priority_update->stream_id); if (stream) { /* Stream already exists. */ if (stream->flags & NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES) { return session_call_on_frame_received(session, frame); } } else if (session_detect_idle_stream(session, priority_update->stream_id)) { if (session->num_idle_streams + session->num_incoming_streams >= session->local_settings.max_concurrent_streams) { return session_handle_invalid_connection( session, frame, NGHTTP2_ERR_PROTO, "PRIORITY_UPDATE: max concurrent streams exceeded"); } stream = nghttp2_session_open_stream(session, priority_update->stream_id, NGHTTP2_FLAG_NONE, NGHTTP2_STREAM_IDLE, NULL); if (!stream) { return NGHTTP2_ERR_NOMEM; } } else { return session_call_on_frame_received(session, frame); } extpri.urgency = NGHTTP2_EXTPRI_DEFAULT_URGENCY; extpri.inc = 0; rv = nghttp2_http_parse_priority(&extpri, priority_update->field_value, priority_update->field_value_len); if (rv != 0) { /* Just ignore field_value if it cannot be parsed. */ return session_call_on_frame_received(session, frame); } rv = session_update_stream_priority(session, stream, nghttp2_extpri_to_uint8(&extpri)); if (rv != 0) { if (nghttp2_is_fatal(rv)) { return rv; } } return session_call_on_frame_received(session, frame); } static int session_process_altsvc_frame(nghttp2_session *session) { nghttp2_inbound_frame *iframe = &session->iframe; nghttp2_frame *frame = &iframe->frame; nghttp2_frame_unpack_altsvc_payload( &frame->ext, nghttp2_get_uint16(iframe->sbuf.pos), iframe->lbuf.pos, nghttp2_buf_len(&iframe->lbuf)); /* nghttp2_frame_unpack_altsvc_payload steals buffer from iframe->lbuf */ nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0); return nghttp2_session_on_altsvc_received(session, frame); } static int session_process_origin_frame(nghttp2_session *session) { nghttp2_inbound_frame *iframe = &session->iframe; nghttp2_frame *frame = &iframe->frame; nghttp2_mem *mem = &session->mem; int rv; rv = nghttp2_frame_unpack_origin_payload(&frame->ext, iframe->lbuf.pos, nghttp2_buf_len(&iframe->lbuf), mem); if (rv != 0) { if (nghttp2_is_fatal(rv)) { return rv; } /* Ignore ORIGIN frame which cannot be parsed. */ return 0; } return nghttp2_session_on_origin_received(session, frame); } static int session_process_priority_update_frame(nghttp2_session *session) { nghttp2_inbound_frame *iframe = &session->iframe; nghttp2_frame *frame = &iframe->frame; nghttp2_frame_unpack_priority_update_payload(&frame->ext, iframe->sbuf.pos, nghttp2_buf_len(&iframe->sbuf)); return nghttp2_session_on_priority_update_received(session, frame); } static int session_process_extension_frame(nghttp2_session *session) { int rv; nghttp2_inbound_frame *iframe = &session->iframe; nghttp2_frame *frame = &iframe->frame; rv = session_call_unpack_extension_callback(session); if (nghttp2_is_fatal(rv)) { return rv; } /* This handles the case where rv == NGHTTP2_ERR_CANCEL as well */ if (rv != 0) { return 0; } return session_call_on_frame_received(session, frame); } int nghttp2_session_on_data_received(nghttp2_session *session, nghttp2_frame *frame) { int rv = 0; nghttp2_stream *stream; /* We don't call on_frame_recv_callback if stream has been closed already or being closed. */ stream = nghttp2_session_get_stream(session, frame->hd.stream_id); if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) { /* This should be treated as stream error, but it results in lots of RST_STREAM. So just ignore frame against nonexistent stream for now. */ return 0; } if (session_enforce_http_messaging(session) && (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { if (nghttp2_http_on_remote_end_stream(stream) != 0) { return nghttp2_session_terminate_session(session, NGHTTP2_PROTOCOL_ERROR); } } rv = session_call_on_frame_received(session, frame); if (nghttp2_is_fatal(rv)) { return rv; } if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); if (nghttp2_is_fatal(rv)) { return rv; } } return 0; } /* For errors, this function only returns FATAL error. */ static int session_process_data_frame(nghttp2_session *session) { int rv; nghttp2_frame *public_data_frame = &session->iframe.frame; rv = nghttp2_session_on_data_received(session, public_data_frame); if (nghttp2_is_fatal(rv)) { return rv; } return 0; } /* * Now we have SETTINGS synchronization, flow control error can be * detected strictly. If DATA frame is received with length > 0 and * current received window size + delta length is strictly larger than * local window size, it is subject to FLOW_CONTROL_ERROR, so return * -1. Note that local_window_size is calculated after SETTINGS ACK is * received from peer, so peer must honor this limit. If the resulting * recv_window_size is strictly larger than NGHTTP2_MAX_WINDOW_SIZE, * return -1 too. */ static int adjust_recv_window_size(int32_t *recv_window_size_ptr, size_t delta, int32_t local_window_size) { if (*recv_window_size_ptr > local_window_size - (int32_t)delta || *recv_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - (int32_t)delta) { return -1; } *recv_window_size_ptr += (int32_t)delta; return 0; } int nghttp2_session_update_recv_stream_window_size(nghttp2_session *session, nghttp2_stream *stream, size_t delta_size, int send_window_update) { int rv; rv = adjust_recv_window_size(&stream->recv_window_size, delta_size, stream->local_window_size); if (rv != 0) { return nghttp2_session_terminate_session(session, NGHTTP2_FLOW_CONTROL_ERROR); } /* We don't have to send WINDOW_UPDATE if the data received is the last chunk in the incoming stream. */ /* We have to use local_settings here because it is the constraint the remote endpoint should honor. */ if (send_window_update && !(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) && stream->window_update_queued == 0 && nghttp2_should_send_window_update(stream->local_window_size, stream->recv_window_size)) { rv = nghttp2_session_add_window_update( session, NGHTTP2_FLAG_NONE, stream->stream_id, stream->recv_window_size); if (rv != 0) { return rv; } stream->recv_window_size = 0; } return 0; } int nghttp2_session_update_recv_connection_window_size(nghttp2_session *session, size_t delta_size) { int rv; rv = adjust_recv_window_size(&session->recv_window_size, delta_size, session->local_window_size); if (rv != 0) { return nghttp2_session_terminate_session(session, NGHTTP2_FLOW_CONTROL_ERROR); } if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) && session->window_update_queued == 0 && nghttp2_should_send_window_update(session->local_window_size, session->recv_window_size)) { /* Use stream ID 0 to update connection-level flow control window */ rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, 0, session->recv_window_size); if (rv != 0) { return rv; } session->recv_window_size = 0; } return 0; } static int session_update_consumed_size(nghttp2_session *session, int32_t *consumed_size_ptr, int32_t *recv_window_size_ptr, uint8_t window_update_queued, int32_t stream_id, size_t delta_size, int32_t local_window_size) { int32_t recv_size; int rv; if ((size_t)*consumed_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta_size) { return nghttp2_session_terminate_session(session, NGHTTP2_FLOW_CONTROL_ERROR); } *consumed_size_ptr += (int32_t)delta_size; if (window_update_queued == 0) { /* recv_window_size may be smaller than consumed_size, because it may be decreased by negative value with nghttp2_submit_window_update(). */ recv_size = nghttp2_min_int32(*consumed_size_ptr, *recv_window_size_ptr); if (nghttp2_should_send_window_update(local_window_size, recv_size)) { rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, stream_id, recv_size); if (rv != 0) { return rv; } *recv_window_size_ptr -= recv_size; *consumed_size_ptr -= recv_size; } } return 0; } static int session_update_stream_consumed_size(nghttp2_session *session, nghttp2_stream *stream, size_t delta_size) { return session_update_consumed_size( session, &stream->consumed_size, &stream->recv_window_size, stream->window_update_queued, stream->stream_id, delta_size, stream->local_window_size); } static int session_update_connection_consumed_size(nghttp2_session *session, size_t delta_size) { return session_update_consumed_size( session, &session->consumed_size, &session->recv_window_size, session->window_update_queued, 0, delta_size, session->local_window_size); } /* * Checks that we can receive the DATA frame for stream, which is * indicated by |session->iframe.frame.hd.stream_id|. If it is a * connection error situation, GOAWAY frame will be issued by this * function. * * If the DATA frame is allowed, returns 0. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_IGN_PAYLOAD * The reception of DATA frame is connection error; or should be * ignored. * NGHTTP2_ERR_NOMEM * Out of memory. */ static int session_on_data_received_fail_fast(nghttp2_session *session) { int rv; nghttp2_stream *stream; nghttp2_inbound_frame *iframe; int32_t stream_id; const char *failure_reason; uint32_t error_code = NGHTTP2_PROTOCOL_ERROR; iframe = &session->iframe; stream_id = iframe->frame.hd.stream_id; if (stream_id == 0) { /* The spec says that if a DATA frame is received whose stream ID is 0, the recipient MUST respond with a connection error of type PROTOCOL_ERROR. */ failure_reason = "DATA: stream_id == 0"; goto fail; } if (session_detect_idle_stream(session, stream_id)) { failure_reason = "DATA: stream in idle"; error_code = NGHTTP2_PROTOCOL_ERROR; goto fail; } stream = nghttp2_session_get_stream(session, stream_id); if (!stream) { stream = nghttp2_session_get_stream_raw(session, stream_id); if (stream && (stream->shut_flags & NGHTTP2_SHUT_RD)) { failure_reason = "DATA: stream closed"; error_code = NGHTTP2_STREAM_CLOSED; goto fail; } return NGHTTP2_ERR_IGN_PAYLOAD; } if (stream->shut_flags & NGHTTP2_SHUT_RD) { failure_reason = "DATA: stream in half-closed(remote)"; error_code = NGHTTP2_STREAM_CLOSED; goto fail; } if (nghttp2_session_is_my_stream_id(session, stream_id)) { if (stream->state == NGHTTP2_STREAM_CLOSING) { return NGHTTP2_ERR_IGN_PAYLOAD; } if (stream->state != NGHTTP2_STREAM_OPENED) { failure_reason = "DATA: stream not opened"; goto fail; } return 0; } if (stream->state == NGHTTP2_STREAM_RESERVED) { failure_reason = "DATA: stream in reserved"; goto fail; } if (stream->state == NGHTTP2_STREAM_CLOSING) { return NGHTTP2_ERR_IGN_PAYLOAD; } return 0; fail: rv = nghttp2_session_terminate_session_with_reason(session, error_code, failure_reason); if (nghttp2_is_fatal(rv)) { return rv; } return NGHTTP2_ERR_IGN_PAYLOAD; } static size_t inbound_frame_payload_readlen(nghttp2_inbound_frame *iframe, const uint8_t *in, const uint8_t *last) { return nghttp2_min_size((size_t)(last - in), iframe->payloadleft); } /* * Resets iframe->sbuf and advance its mark pointer by |left| bytes. */ static void inbound_frame_set_mark(nghttp2_inbound_frame *iframe, size_t left) { nghttp2_buf_reset(&iframe->sbuf); iframe->sbuf.mark += left; } static size_t inbound_frame_buf_read(nghttp2_inbound_frame *iframe, const uint8_t *in, const uint8_t *last) { size_t readlen; readlen = nghttp2_min_size((size_t)(last - in), nghttp2_buf_mark_avail(&iframe->sbuf)); iframe->sbuf.last = nghttp2_cpymem(iframe->sbuf.last, in, readlen); return readlen; } /* * Unpacks SETTINGS entry in iframe->sbuf. */ static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) { nghttp2_settings_entry iv; nghttp2_settings_entry *min_header_table_size_entry; size_t i; nghttp2_frame_unpack_settings_entry(&iv, iframe->sbuf.pos); switch (iv.settings_id) { case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: case NGHTTP2_SETTINGS_ENABLE_PUSH: case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES: break; default: DEBUGF("recv: unknown settings id=0x%02x\n", iv.settings_id); iframe->iv[iframe->niv++] = iv; return; } for (i = 0; i < iframe->niv; ++i) { if (iframe->iv[i].settings_id == iv.settings_id) { iframe->iv[i] = iv; break; } } if (i == iframe->niv) { iframe->iv[iframe->niv++] = iv; } if (iv.settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) { /* Keep track of minimum value of SETTINGS_HEADER_TABLE_SIZE */ min_header_table_size_entry = &iframe->iv[iframe->max_niv - 1]; if (iv.value < min_header_table_size_entry->value) { min_header_table_size_entry->value = iv.value; } } } /* * Checks PADDED flags and set iframe->sbuf to read them accordingly. * If padding is set, this function returns 1. If no padding is set, * this function returns 0. On error, returns -1. */ static int inbound_frame_handle_pad(nghttp2_inbound_frame *iframe, nghttp2_frame_hd *hd) { if (hd->flags & NGHTTP2_FLAG_PADDED) { if (hd->length < 1) { return -1; } inbound_frame_set_mark(iframe, 1); return 1; } DEBUGF("recv: no padding in payload\n"); return 0; } /* * Computes number of padding based on flags. This function returns * the calculated length if it succeeds, or -1. */ static nghttp2_ssize inbound_frame_compute_pad(nghttp2_inbound_frame *iframe) { size_t padlen; /* 1 for Pad Length field */ padlen = (size_t)(iframe->sbuf.pos[0] + 1); DEBUGF("recv: padlen=%zu\n", padlen); /* We cannot use iframe->frame.hd.length because of CONTINUATION */ if (padlen - 1 > iframe->payloadleft) { return -1; } iframe->padlen = padlen; return (nghttp2_ssize)padlen; } /* * This function returns the effective payload length in the data of * length |readlen| when the remaining payload is |payloadleft|. The * |payloadleft| does not include |readlen|. If padding was started * strictly before this data chunk, this function returns -1. */ static nghttp2_ssize inbound_frame_effective_readlen(nghttp2_inbound_frame *iframe, size_t payloadleft, size_t readlen) { size_t trail_padlen = nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen); if (trail_padlen > payloadleft) { size_t padlen; padlen = trail_padlen - payloadleft; if (readlen < padlen) { return -1; } return (nghttp2_ssize)(readlen - padlen); } return (nghttp2_ssize)(readlen); } static const uint8_t static_in[] = {0}; ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, size_t inlen) { return (ssize_t)nghttp2_session_mem_recv2(session, in, inlen); } nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, const uint8_t *in, size_t inlen) { const uint8_t *first, *last; nghttp2_inbound_frame *iframe = &session->iframe; size_t readlen; nghttp2_ssize padlen; int rv; int busy = 0; nghttp2_frame_hd cont_hd; nghttp2_stream *stream; size_t pri_fieldlen; nghttp2_mem *mem; if (in == NULL) { assert(inlen == 0); in = static_in; } first = in; last = in + inlen; DEBUGF("recv: connection recv_window_size=%d, local_window=%d\n", session->recv_window_size, session->local_window_size); mem = &session->mem; if (!nghttp2_session_want_read(session)) { return (nghttp2_ssize)inlen; } for (;;) { switch (iframe->state) { case NGHTTP2_IB_READ_CLIENT_MAGIC: readlen = nghttp2_min_size(inlen, iframe->payloadleft); if (memcmp(&NGHTTP2_CLIENT_MAGIC[NGHTTP2_CLIENT_MAGIC_LEN - iframe->payloadleft], in, readlen) != 0) { return NGHTTP2_ERR_BAD_CLIENT_MAGIC; } iframe->payloadleft -= readlen; in += readlen; if (iframe->payloadleft == 0) { session_inbound_frame_reset(session); iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS; } break; case NGHTTP2_IB_READ_FIRST_SETTINGS: DEBUGF("recv: [IB_READ_FIRST_SETTINGS]\n"); readlen = inbound_frame_buf_read(iframe, in, last); in += readlen; if (nghttp2_buf_mark_avail(&iframe->sbuf)) { return (nghttp2_ssize)(in - first); } if (iframe->sbuf.pos[3] != NGHTTP2_SETTINGS || (iframe->sbuf.pos[4] & NGHTTP2_FLAG_ACK)) { rv = session_call_error_callback( session, NGHTTP2_ERR_SETTINGS_EXPECTED, "Remote peer returned unexpected data while we expected " "SETTINGS frame. Perhaps, peer does not support HTTP/2 " "properly."); if (nghttp2_is_fatal(rv)) { return rv; } rv = nghttp2_session_terminate_session_with_reason( session, NGHTTP2_PROTOCOL_ERROR, "SETTINGS expected"); if (nghttp2_is_fatal(rv)) { return rv; } return (nghttp2_ssize)inlen; } iframe->state = NGHTTP2_IB_READ_HEAD; /* Fall through */ case NGHTTP2_IB_READ_HEAD: { int on_begin_frame_called = 0; DEBUGF("recv: [IB_READ_HEAD]\n"); readlen = inbound_frame_buf_read(iframe, in, last); in += readlen; if (nghttp2_buf_mark_avail(&iframe->sbuf)) { return (nghttp2_ssize)(in - first); } nghttp2_frame_unpack_frame_hd(&iframe->frame.hd, iframe->sbuf.pos); iframe->payloadleft = iframe->frame.hd.length; DEBUGF("recv: payloadlen=%zu, type=%u, flags=0x%02x, stream_id=%d\n", iframe->frame.hd.length, iframe->frame.hd.type, iframe->frame.hd.flags, iframe->frame.hd.stream_id); if (iframe->frame.hd.length > session->local_settings.max_frame_size) { DEBUGF("recv: length is too large %zu > %u\n", iframe->frame.hd.length, session->local_settings.max_frame_size); rv = nghttp2_session_terminate_session_with_reason( session, NGHTTP2_FRAME_SIZE_ERROR, "too large frame size"); if (nghttp2_is_fatal(rv)) { return rv; } return (nghttp2_ssize)inlen; } switch (iframe->frame.hd.type) { case NGHTTP2_DATA: { DEBUGF("recv: DATA\n"); iframe->frame.hd.flags &= (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PADDED); /* Check stream is open. If it is not open or closing, ignore payload. */ busy = 1; rv = session_on_data_received_fail_fast(session); if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } if (rv == NGHTTP2_ERR_IGN_PAYLOAD) { DEBUGF("recv: DATA not allowed stream_id=%d\n", iframe->frame.hd.stream_id); rv = session_update_glitch_ratelim(session); if (rv != 0) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } iframe->state = NGHTTP2_IB_IGN_DATA; break; } if (nghttp2_is_fatal(rv)) { return rv; } rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); if (rv < 0) { rv = nghttp2_session_terminate_session_with_reason( session, NGHTTP2_PROTOCOL_ERROR, "DATA: insufficient padding space"); if (nghttp2_is_fatal(rv)) { return rv; } return (nghttp2_ssize)inlen; } if (rv == 1) { iframe->state = NGHTTP2_IB_READ_PAD_DATA; break; } /* Empty DATA frame without END_STREAM flag set is suspicious. */ if (iframe->payloadleft == 0 && (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { rv = session_update_glitch_ratelim(session); if (rv != 0) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } } iframe->state = NGHTTP2_IB_READ_DATA; break; } case NGHTTP2_HEADERS: DEBUGF("recv: HEADERS\n"); iframe->frame.hd.flags &= (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PADDED | NGHTTP2_FLAG_PRIORITY); rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); if (rv < 0) { rv = nghttp2_session_terminate_session_with_reason( session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: insufficient padding space"); if (nghttp2_is_fatal(rv)) { return rv; } return (nghttp2_ssize)inlen; } if (rv == 1) { iframe->state = NGHTTP2_IB_READ_NBYTE; break; } pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags); if (pri_fieldlen > 0) { if (iframe->payloadleft < pri_fieldlen) { busy = 1; iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; break; } iframe->state = NGHTTP2_IB_READ_NBYTE; inbound_frame_set_mark(iframe, pri_fieldlen); break; } /* Call on_begin_frame_callback here because session_process_headers_frame() may call on_begin_headers_callback */ rv = session_call_on_begin_frame(session, &iframe->frame.hd); if (nghttp2_is_fatal(rv)) { return rv; } on_begin_frame_called = 1; rv = session_process_headers_frame(session); if (nghttp2_is_fatal(rv)) { return rv; } busy = 1; if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { rv = session_handle_invalid_stream2( session, iframe->frame.hd.stream_id, NULL, NGHTTP2_ERR_INTERNAL); if (nghttp2_is_fatal(rv)) { return rv; } iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; break; } if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { rv = session_update_glitch_ratelim(session); if (rv != 0) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; break; } iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; break; case NGHTTP2_PRIORITY: DEBUGF("recv: PRIORITY\n"); iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; if (iframe->payloadleft != NGHTTP2_PRIORITY_SPECLEN) { busy = 1; iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; break; } /* This is deprecated RFC 7540 priorities mechanism which is very unpopular. We do not expect it is received so frequently. */ rv = session_update_glitch_ratelim(session); if (rv != 0) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } iframe->state = NGHTTP2_IB_READ_NBYTE; inbound_frame_set_mark(iframe, NGHTTP2_PRIORITY_SPECLEN); break; case NGHTTP2_RST_STREAM: case NGHTTP2_WINDOW_UPDATE: #ifdef DEBUGBUILD switch (iframe->frame.hd.type) { case NGHTTP2_RST_STREAM: DEBUGF("recv: RST_STREAM\n"); break; case NGHTTP2_WINDOW_UPDATE: DEBUGF("recv: WINDOW_UPDATE\n"); break; } #endif /* defined(DEBUGBUILD) */ iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; if (iframe->payloadleft != 4) { busy = 1; iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; break; } iframe->state = NGHTTP2_IB_READ_NBYTE; inbound_frame_set_mark(iframe, 4); break; case NGHTTP2_SETTINGS: DEBUGF("recv: SETTINGS\n"); iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK; if ((iframe->frame.hd.length % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) || ((iframe->frame.hd.flags & NGHTTP2_FLAG_ACK) && iframe->payloadleft > 0)) { busy = 1; iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; break; } /* Check the settings flood counter early to be safe */ if (session->obq_flood_counter_ >= session->max_outbound_ack && !(iframe->frame.hd.flags & NGHTTP2_FLAG_ACK)) { return NGHTTP2_ERR_FLOODED; } iframe->state = NGHTTP2_IB_READ_SETTINGS; if (iframe->payloadleft) { nghttp2_settings_entry *min_header_table_size_entry; /* We allocate iv with additional one entry, to store the minimum header table size. */ iframe->max_niv = iframe->frame.hd.length / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH + 1; if (iframe->max_niv - 1 > session->max_settings) { rv = nghttp2_session_terminate_session_with_reason( session, NGHTTP2_ENHANCE_YOUR_CALM, "SETTINGS: too many setting entries"); if (nghttp2_is_fatal(rv)) { return rv; } return (nghttp2_ssize)inlen; } iframe->iv = nghttp2_mem_malloc(mem, sizeof(nghttp2_settings_entry) * iframe->max_niv); if (!iframe->iv) { return NGHTTP2_ERR_NOMEM; } min_header_table_size_entry = &iframe->iv[iframe->max_niv - 1]; min_header_table_size_entry->settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; min_header_table_size_entry->value = UINT32_MAX; inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH); break; } busy = 1; inbound_frame_set_mark(iframe, 0); break; case NGHTTP2_PUSH_PROMISE: DEBUGF("recv: PUSH_PROMISE\n"); iframe->frame.hd.flags &= (NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PADDED); rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); if (rv < 0) { rv = nghttp2_session_terminate_session_with_reason( session, NGHTTP2_PROTOCOL_ERROR, "PUSH_PROMISE: insufficient padding space"); if (nghttp2_is_fatal(rv)) { return rv; } return (nghttp2_ssize)inlen; } if (rv == 1) { iframe->state = NGHTTP2_IB_READ_NBYTE; break; } if (iframe->payloadleft < 4) { busy = 1; iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; break; } iframe->state = NGHTTP2_IB_READ_NBYTE; inbound_frame_set_mark(iframe, 4); break; case NGHTTP2_PING: DEBUGF("recv: PING\n"); iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK; if (iframe->payloadleft != 8) { busy = 1; iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; break; } iframe->state = NGHTTP2_IB_READ_NBYTE; inbound_frame_set_mark(iframe, 8); break; case NGHTTP2_GOAWAY: DEBUGF("recv: GOAWAY\n"); iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; if (iframe->payloadleft < 8) { busy = 1; iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; break; } iframe->state = NGHTTP2_IB_READ_NBYTE; inbound_frame_set_mark(iframe, 8); break; case NGHTTP2_CONTINUATION: DEBUGF("recv: unexpected CONTINUATION\n"); /* Receiving CONTINUATION in this state are subject to connection error of type PROTOCOL_ERROR */ rv = nghttp2_session_terminate_session_with_reason( session, NGHTTP2_PROTOCOL_ERROR, "CONTINUATION: unexpected"); if (nghttp2_is_fatal(rv)) { return rv; } return (nghttp2_ssize)inlen; default: DEBUGF("recv: extension frame\n"); if (check_ext_type_set(session->user_recv_ext_types, iframe->frame.hd.type)) { if (!session->callbacks.unpack_extension_callback) { /* Receiving too frequent unknown frames is suspicious. */ rv = session_update_glitch_ratelim(session); if (rv != 0) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } /* Silently ignore unknown frame type. */ busy = 1; iframe->state = NGHTTP2_IB_IGN_PAYLOAD; break; } busy = 1; iframe->state = NGHTTP2_IB_READ_EXTENSION_PAYLOAD; break; } else { switch (iframe->frame.hd.type) { case NGHTTP2_ALTSVC: if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ALTSVC) == 0) { /* Receiving too frequent unknown frames is suspicious. */ rv = session_update_glitch_ratelim(session); if (rv != 0) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } busy = 1; iframe->state = NGHTTP2_IB_IGN_PAYLOAD; break; } DEBUGF("recv: ALTSVC\n"); iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; iframe->frame.ext.payload = &iframe->ext_frame_payload.altsvc; if (session->server) { /* Receiving too frequent ALTSVC from client is suspicious. */ rv = session_update_glitch_ratelim(session); if (rv != 0) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } busy = 1; iframe->state = NGHTTP2_IB_IGN_PAYLOAD; break; } if (iframe->payloadleft < 2) { busy = 1; iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; break; } busy = 1; iframe->state = NGHTTP2_IB_READ_NBYTE; inbound_frame_set_mark(iframe, 2); break; case NGHTTP2_ORIGIN: if (!(session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN)) { /* Receiving too frequent unknown frames is suspicious. */ rv = session_update_glitch_ratelim(session); if (rv != 0) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } busy = 1; iframe->state = NGHTTP2_IB_IGN_PAYLOAD; break; } DEBUGF("recv: ORIGIN\n"); iframe->frame.ext.payload = &iframe->ext_frame_payload.origin; if (session->server || iframe->frame.hd.stream_id || (iframe->frame.hd.flags & 0xf0)) { /* Receiving too frequent invalid frames is suspicious. */ rv = session_update_glitch_ratelim(session); if (rv != 0) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } busy = 1; iframe->state = NGHTTP2_IB_IGN_PAYLOAD; break; } iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; if (iframe->payloadleft) { iframe->raw_lbuf = nghttp2_mem_malloc(mem, iframe->payloadleft); if (iframe->raw_lbuf == NULL) { return NGHTTP2_ERR_NOMEM; } nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf, iframe->payloadleft); } else { busy = 1; } iframe->state = NGHTTP2_IB_READ_ORIGIN_PAYLOAD; break; case NGHTTP2_PRIORITY_UPDATE: if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_PRIORITY_UPDATE) == 0) { /* Receiving too frequent unknown frames is suspicious. */ rv = session_update_glitch_ratelim(session); if (rv != 0) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } busy = 1; iframe->state = NGHTTP2_IB_IGN_PAYLOAD; break; } DEBUGF("recv: PRIORITY_UPDATE\n"); iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; iframe->frame.ext.payload = &iframe->ext_frame_payload.priority_update; if (!session->server) { rv = nghttp2_session_terminate_session_with_reason( session, NGHTTP2_PROTOCOL_ERROR, "PRIORITY_UPDATE is received from server"); if (nghttp2_is_fatal(rv)) { return rv; } return (nghttp2_ssize)inlen; } if (iframe->payloadleft < 4) { busy = 1; iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; break; } /* Receiving too frequent PRIORITY_UPDATE is suspicious. */ rv = session_update_glitch_ratelim(session); if (rv != 0) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } if (iframe->payloadleft > sizeof(iframe->raw_sbuf)) { busy = 1; iframe->state = NGHTTP2_IB_IGN_PAYLOAD; break; } busy = 1; iframe->state = NGHTTP2_IB_READ_NBYTE; inbound_frame_set_mark(iframe, iframe->payloadleft); break; default: /* Receiving too frequent unknown frames is suspicious. */ rv = session_update_glitch_ratelim(session); if (rv != 0) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } busy = 1; iframe->state = NGHTTP2_IB_IGN_PAYLOAD; break; } } } if (!on_begin_frame_called) { switch (iframe->state) { case NGHTTP2_IB_IGN_HEADER_BLOCK: case NGHTTP2_IB_IGN_PAYLOAD: case NGHTTP2_IB_FRAME_SIZE_ERROR: case NGHTTP2_IB_IGN_DATA: case NGHTTP2_IB_IGN_ALL: break; default: rv = session_call_on_begin_frame(session, &iframe->frame.hd); if (nghttp2_is_fatal(rv)) { return rv; } } } break; } case NGHTTP2_IB_READ_NBYTE: DEBUGF("recv: [IB_READ_NBYTE]\n"); readlen = inbound_frame_buf_read(iframe, in, last); in += readlen; iframe->payloadleft -= readlen; DEBUGF("recv: readlen=%zu, payloadleft=%zu, left=%zu\n", readlen, iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf)); if (nghttp2_buf_mark_avail(&iframe->sbuf)) { return (nghttp2_ssize)(in - first); } switch (iframe->frame.hd.type) { case NGHTTP2_HEADERS: if (iframe->padlen == 0 && (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) { pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags); padlen = inbound_frame_compute_pad(iframe); if (padlen < 0 || (size_t)padlen + pri_fieldlen > 1 + iframe->payloadleft) { rv = nghttp2_session_terminate_session_with_reason( session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: invalid padding"); if (nghttp2_is_fatal(rv)) { return rv; } return (nghttp2_ssize)inlen; } iframe->frame.headers.padlen = (size_t)padlen; if (pri_fieldlen > 0) { if (iframe->payloadleft < pri_fieldlen) { busy = 1; iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; break; } iframe->state = NGHTTP2_IB_READ_NBYTE; inbound_frame_set_mark(iframe, pri_fieldlen); break; } else { /* Truncate buffers used for padding spec */ inbound_frame_set_mark(iframe, 0); } } rv = session_process_headers_frame(session); if (nghttp2_is_fatal(rv)) { return rv; } busy = 1; if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { rv = session_handle_invalid_stream2( session, iframe->frame.hd.stream_id, NULL, NGHTTP2_ERR_INTERNAL); if (nghttp2_is_fatal(rv)) { return rv; } iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; break; } if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { rv = session_update_glitch_ratelim(session); if (rv != 0) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; break; } iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; break; case NGHTTP2_PRIORITY: session_inbound_frame_reset(session); break; case NGHTTP2_RST_STREAM: rv = session_process_rst_stream_frame(session); if (nghttp2_is_fatal(rv)) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } session_inbound_frame_reset(session); break; case NGHTTP2_PUSH_PROMISE: if (iframe->padlen == 0 && (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) { padlen = inbound_frame_compute_pad(iframe); if (padlen < 0 || (size_t)padlen + 4 /* promised stream id */ > 1 + iframe->payloadleft) { rv = nghttp2_session_terminate_session_with_reason( session, NGHTTP2_PROTOCOL_ERROR, "PUSH_PROMISE: invalid padding"); if (nghttp2_is_fatal(rv)) { return rv; } return (nghttp2_ssize)inlen; } iframe->frame.push_promise.padlen = (size_t)padlen; if (iframe->payloadleft < 4) { busy = 1; iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; break; } iframe->state = NGHTTP2_IB_READ_NBYTE; inbound_frame_set_mark(iframe, 4); break; } rv = session_process_push_promise_frame(session); if (nghttp2_is_fatal(rv)) { return rv; } busy = 1; if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { rv = session_handle_invalid_stream2( session, iframe->frame.push_promise.promised_stream_id, NULL, NGHTTP2_ERR_INTERNAL); if (nghttp2_is_fatal(rv)) { return rv; } iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; break; } if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; break; } iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; break; case NGHTTP2_PING: rv = session_process_ping_frame(session); if (nghttp2_is_fatal(rv)) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } session_inbound_frame_reset(session); break; case NGHTTP2_GOAWAY: { size_t debuglen; /* 8 is Last-stream-ID + Error Code */ debuglen = iframe->frame.hd.length - 8; if (debuglen > 0) { iframe->raw_lbuf = nghttp2_mem_malloc(mem, debuglen); if (iframe->raw_lbuf == NULL) { return NGHTTP2_ERR_NOMEM; } nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf, debuglen); } busy = 1; iframe->state = NGHTTP2_IB_READ_GOAWAY_DEBUG; break; } case NGHTTP2_WINDOW_UPDATE: rv = session_process_window_update_frame(session); if (nghttp2_is_fatal(rv)) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } session_inbound_frame_reset(session); break; case NGHTTP2_ALTSVC: { size_t origin_len; origin_len = nghttp2_get_uint16(iframe->sbuf.pos); DEBUGF("recv: origin_len=%zu\n", origin_len); if (origin_len > iframe->payloadleft) { busy = 1; iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; break; } if (iframe->frame.hd.length > 2) { iframe->raw_lbuf = nghttp2_mem_malloc(mem, iframe->frame.hd.length - 2); if (iframe->raw_lbuf == NULL) { return NGHTTP2_ERR_NOMEM; } nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf, iframe->frame.hd.length); } busy = 1; iframe->state = NGHTTP2_IB_READ_ALTSVC_PAYLOAD; break; case NGHTTP2_PRIORITY_UPDATE: DEBUGF("recv: prioritized_stream_id=%d\n", nghttp2_get_uint32(iframe->sbuf.pos) & NGHTTP2_STREAM_ID_MASK); rv = session_process_priority_update_frame(session); if (nghttp2_is_fatal(rv)) { return rv; } session_inbound_frame_reset(session); break; } default: /* This is unknown frame */ session_inbound_frame_reset(session); break; } break; case NGHTTP2_IB_READ_HEADER_BLOCK: case NGHTTP2_IB_IGN_HEADER_BLOCK: { nghttp2_ssize data_readlen; size_t trail_padlen; int final; #ifdef DEBUGBUILD if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { DEBUGF("recv: [IB_READ_HEADER_BLOCK]\n"); } else { DEBUGF("recv: [IB_IGN_HEADER_BLOCK]\n"); } #endif /* defined(DEBUGBUILD) */ readlen = inbound_frame_payload_readlen(iframe, in, last); DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, iframe->payloadleft - readlen); data_readlen = inbound_frame_effective_readlen( iframe, iframe->payloadleft - readlen, readlen); if (data_readlen == -1) { /* everything is padding */ data_readlen = 0; } trail_padlen = nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen); final = (iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) && iframe->payloadleft - (size_t)data_readlen == trail_padlen; if (data_readlen > 0 || (data_readlen == 0 && final)) { size_t hd_proclen = 0; DEBUGF("recv: block final=%d\n", final); rv = inflate_header_block(session, &iframe->frame, &hd_proclen, (uint8_t *)in, (size_t)data_readlen, final, iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK); if (nghttp2_is_fatal(rv)) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } if (rv == NGHTTP2_ERR_PAUSE) { in += hd_proclen; iframe->payloadleft -= hd_proclen; return (nghttp2_ssize)(in - first); } if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { /* The application says no more headers. We decompress the rest of the header block but not invoke on_header_callback and on_frame_recv_callback. */ in += hd_proclen; iframe->payloadleft -= hd_proclen; /* Use promised stream ID for PUSH_PROMISE */ rv = session_handle_invalid_stream2( session, iframe->frame.hd.type == NGHTTP2_PUSH_PROMISE ? iframe->frame.push_promise.promised_stream_id : iframe->frame.hd.stream_id, NULL, NGHTTP2_ERR_INTERNAL); if (nghttp2_is_fatal(rv)) { return rv; } busy = 1; iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; break; } in += readlen; iframe->payloadleft -= readlen; if (rv == NGHTTP2_ERR_HEADER_COMP) { /* GOAWAY is already issued */ if (iframe->payloadleft == 0) { session_inbound_frame_reset(session); } else { busy = 1; iframe->state = NGHTTP2_IB_IGN_PAYLOAD; } break; } } else { in += readlen; iframe->payloadleft -= readlen; } if (iframe->payloadleft) { break; } if ((iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) == 0) { inbound_frame_set_mark(iframe, NGHTTP2_FRAME_HDLEN); iframe->padlen = 0; if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { iframe->state = NGHTTP2_IB_EXPECT_CONTINUATION; } else { iframe->state = NGHTTP2_IB_IGN_CONTINUATION; } } else { if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { rv = session_after_header_block_received(session); if (nghttp2_is_fatal(rv)) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } } session_inbound_frame_reset(session); session->num_continuations = 0; } break; } case NGHTTP2_IB_IGN_PAYLOAD: DEBUGF("recv: [IB_IGN_PAYLOAD]\n"); readlen = inbound_frame_payload_readlen(iframe, in, last); iframe->payloadleft -= readlen; in += readlen; DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, iframe->payloadleft); if (iframe->payloadleft) { break; } switch (iframe->frame.hd.type) { case NGHTTP2_HEADERS: case NGHTTP2_PUSH_PROMISE: case NGHTTP2_CONTINUATION: /* Mark inflater bad so that we won't perform further decoding */ session->hd_inflater.ctx.bad = 1; break; default: break; } session_inbound_frame_reset(session); break; case NGHTTP2_IB_FRAME_SIZE_ERROR: DEBUGF("recv: [IB_FRAME_SIZE_ERROR]\n"); rv = session_handle_frame_size_error(session); if (nghttp2_is_fatal(rv)) { return rv; } assert(iframe->state == NGHTTP2_IB_IGN_ALL); return (nghttp2_ssize)inlen; case NGHTTP2_IB_READ_SETTINGS: DEBUGF("recv: [IB_READ_SETTINGS]\n"); readlen = inbound_frame_buf_read(iframe, in, last); iframe->payloadleft -= readlen; in += readlen; DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, iframe->payloadleft); if (nghttp2_buf_mark_avail(&iframe->sbuf)) { break; } if (readlen > 0) { inbound_frame_set_settings_entry(iframe); } if (iframe->payloadleft) { inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH); break; } rv = session_process_settings_frame(session); if (nghttp2_is_fatal(rv)) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } session_inbound_frame_reset(session); break; case NGHTTP2_IB_READ_GOAWAY_DEBUG: DEBUGF("recv: [IB_READ_GOAWAY_DEBUG]\n"); readlen = inbound_frame_payload_readlen(iframe, in, last); if (readlen > 0) { iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen); iframe->payloadleft -= readlen; in += readlen; } DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, iframe->payloadleft); if (iframe->payloadleft) { assert(nghttp2_buf_avail(&iframe->lbuf) > 0); break; } rv = session_process_goaway_frame(session); if (nghttp2_is_fatal(rv)) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } session_inbound_frame_reset(session); break; case NGHTTP2_IB_EXPECT_CONTINUATION: case NGHTTP2_IB_IGN_CONTINUATION: #ifdef DEBUGBUILD if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) { fprintf(stderr, "recv: [IB_EXPECT_CONTINUATION]\n"); } else { fprintf(stderr, "recv: [IB_IGN_CONTINUATION]\n"); } #endif /* defined(DEBUGBUILD) */ if (++session->num_continuations > session->max_continuations) { return NGHTTP2_ERR_TOO_MANY_CONTINUATIONS; } readlen = inbound_frame_buf_read(iframe, in, last); in += readlen; if (nghttp2_buf_mark_avail(&iframe->sbuf)) { return (nghttp2_ssize)(in - first); } nghttp2_frame_unpack_frame_hd(&cont_hd, iframe->sbuf.pos); iframe->payloadleft = cont_hd.length; DEBUGF("recv: payloadlen=%zu, type=%u, flags=0x%02x, stream_id=%d\n", cont_hd.length, cont_hd.type, cont_hd.flags, cont_hd.stream_id); if (cont_hd.type != NGHTTP2_CONTINUATION || cont_hd.stream_id != iframe->frame.hd.stream_id) { DEBUGF("recv: expected stream_id=%d, type=%d, but got stream_id=%d, " "type=%u\n", iframe->frame.hd.stream_id, NGHTTP2_CONTINUATION, cont_hd.stream_id, cont_hd.type); rv = nghttp2_session_terminate_session_with_reason( session, NGHTTP2_PROTOCOL_ERROR, "unexpected non-CONTINUATION frame or stream_id is invalid"); if (nghttp2_is_fatal(rv)) { return rv; } return (nghttp2_ssize)inlen; } /* CONTINUATION won't bear NGHTTP2_PADDED flag */ iframe->frame.hd.flags = (uint8_t)(iframe->frame.hd.flags | (cont_hd.flags & NGHTTP2_FLAG_END_HEADERS)); iframe->frame.hd.length += cont_hd.length; busy = 1; if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) { iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; rv = session_call_on_begin_frame(session, &cont_hd); if (nghttp2_is_fatal(rv)) { return rv; } } else { iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; } break; case NGHTTP2_IB_READ_PAD_DATA: DEBUGF("recv: [IB_READ_PAD_DATA]\n"); readlen = inbound_frame_buf_read(iframe, in, last); in += readlen; iframe->payloadleft -= readlen; DEBUGF("recv: readlen=%zu, payloadleft=%zu, left=%zu\n", readlen, iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf)); if (nghttp2_buf_mark_avail(&iframe->sbuf)) { return (nghttp2_ssize)(in - first); } /* Pad Length field is subject to flow control */ rv = nghttp2_session_update_recv_connection_window_size(session, readlen); if (nghttp2_is_fatal(rv)) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } /* Pad Length field is consumed immediately */ rv = nghttp2_session_consume(session, iframe->frame.hd.stream_id, readlen); if (nghttp2_is_fatal(rv)) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id); if (stream) { rv = nghttp2_session_update_recv_stream_window_size( session, stream, readlen, iframe->payloadleft || (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0); if (nghttp2_is_fatal(rv)) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } } busy = 1; padlen = inbound_frame_compute_pad(iframe); if (padlen < 0) { rv = nghttp2_session_terminate_session_with_reason( session, NGHTTP2_PROTOCOL_ERROR, "DATA: invalid padding"); if (nghttp2_is_fatal(rv)) { return rv; } return (nghttp2_ssize)inlen; } iframe->frame.data.padlen = (size_t)padlen; /* Empty DATA frame without END_STREAM flag set is suspicious. */ if (iframe->payloadleft == 0 && (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { rv = session_update_glitch_ratelim(session); if (rv != 0) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } } iframe->state = NGHTTP2_IB_READ_DATA; break; case NGHTTP2_IB_READ_DATA: stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id); if (!stream) { busy = 1; iframe->state = NGHTTP2_IB_IGN_DATA; break; } DEBUGF("recv: [IB_READ_DATA]\n"); readlen = inbound_frame_payload_readlen(iframe, in, last); iframe->payloadleft -= readlen; in += readlen; DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, iframe->payloadleft); if (readlen > 0) { nghttp2_ssize data_readlen; rv = nghttp2_session_update_recv_connection_window_size(session, readlen); if (nghttp2_is_fatal(rv)) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } rv = nghttp2_session_update_recv_stream_window_size( session, stream, readlen, iframe->payloadleft || (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0); if (nghttp2_is_fatal(rv)) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } data_readlen = inbound_frame_effective_readlen(iframe, iframe->payloadleft, readlen); if (data_readlen == -1) { /* everything is padding */ data_readlen = 0; } padlen = (nghttp2_ssize)readlen - data_readlen; if (padlen > 0) { /* Padding is considered as "consumed" immediately */ rv = nghttp2_session_consume(session, iframe->frame.hd.stream_id, (size_t)padlen); if (nghttp2_is_fatal(rv)) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } } DEBUGF("recv: data_readlen=%td\n", data_readlen); if (data_readlen > 0) { if (session_enforce_http_messaging(session)) { if (nghttp2_http_on_data_chunk(stream, (size_t)data_readlen) != 0) { rv = nghttp2_session_terminate_session(session, NGHTTP2_PROTOCOL_ERROR); if (nghttp2_is_fatal(rv)) { return rv; } return (nghttp2_ssize)inlen; } } if (session->callbacks.on_data_chunk_recv_callback) { rv = session->callbacks.on_data_chunk_recv_callback( session, iframe->frame.hd.flags, iframe->frame.hd.stream_id, in - readlen, (size_t)data_readlen, session->user_data); if (rv == NGHTTP2_ERR_PAUSE) { return (nghttp2_ssize)(in - first); } if (nghttp2_is_fatal(rv)) { return NGHTTP2_ERR_CALLBACK_FAILURE; } } } } if (iframe->payloadleft) { break; } rv = session_process_data_frame(session); if (nghttp2_is_fatal(rv)) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } session_inbound_frame_reset(session); break; case NGHTTP2_IB_IGN_DATA: DEBUGF("recv: [IB_IGN_DATA]\n"); readlen = inbound_frame_payload_readlen(iframe, in, last); iframe->payloadleft -= readlen; in += readlen; DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, iframe->payloadleft); if (readlen > 0) { /* Update connection-level flow control window for ignored DATA frame too */ rv = nghttp2_session_update_recv_connection_window_size(session, readlen); if (nghttp2_is_fatal(rv)) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { /* Ignored DATA is considered as "consumed" immediately. */ rv = session_update_connection_consumed_size(session, readlen); if (nghttp2_is_fatal(rv)) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } } } if (iframe->payloadleft) { break; } session_inbound_frame_reset(session); break; case NGHTTP2_IB_IGN_ALL: return (nghttp2_ssize)inlen; case NGHTTP2_IB_READ_EXTENSION_PAYLOAD: DEBUGF("recv: [IB_READ_EXTENSION_PAYLOAD]\n"); readlen = inbound_frame_payload_readlen(iframe, in, last); iframe->payloadleft -= readlen; in += readlen; DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, iframe->payloadleft); if (readlen > 0) { rv = session_call_on_extension_chunk_recv_callback( session, in - readlen, readlen); if (nghttp2_is_fatal(rv)) { return rv; } if (rv != 0) { busy = 1; iframe->state = NGHTTP2_IB_IGN_PAYLOAD; break; } } if (iframe->payloadleft > 0) { break; } rv = session_process_extension_frame(session); if (nghttp2_is_fatal(rv)) { return rv; } session_inbound_frame_reset(session); break; case NGHTTP2_IB_READ_ALTSVC_PAYLOAD: DEBUGF("recv: [IB_READ_ALTSVC_PAYLOAD]\n"); readlen = inbound_frame_payload_readlen(iframe, in, last); if (readlen > 0) { iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen); iframe->payloadleft -= readlen; in += readlen; } DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, iframe->payloadleft); if (iframe->payloadleft) { assert(nghttp2_buf_avail(&iframe->lbuf) > 0); break; } rv = session_process_altsvc_frame(session); if (nghttp2_is_fatal(rv)) { return rv; } session_inbound_frame_reset(session); break; case NGHTTP2_IB_READ_ORIGIN_PAYLOAD: DEBUGF("recv: [IB_READ_ORIGIN_PAYLOAD]\n"); readlen = inbound_frame_payload_readlen(iframe, in, last); if (readlen > 0) { iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen); iframe->payloadleft -= readlen; in += readlen; } DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, iframe->payloadleft); if (iframe->payloadleft) { assert(nghttp2_buf_avail(&iframe->lbuf) > 0); break; } rv = session_process_origin_frame(session); if (nghttp2_is_fatal(rv)) { return rv; } if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (nghttp2_ssize)inlen; } session_inbound_frame_reset(session); break; } if (!busy && in == last) { break; } busy = 0; } assert(in == last); return (nghttp2_ssize)(in - first); } int nghttp2_session_recv(nghttp2_session *session) { uint8_t buf[NGHTTP2_INBOUND_BUFFER_LENGTH]; while (1) { nghttp2_ssize readlen; readlen = session_recv(session, buf, sizeof(buf)); if (readlen > 0) { nghttp2_ssize proclen = nghttp2_session_mem_recv2(session, buf, (size_t)readlen); if (proclen < 0) { return (int)proclen; } assert(proclen == readlen); } else if (readlen == 0 || readlen == NGHTTP2_ERR_WOULDBLOCK) { return 0; } else if (readlen == NGHTTP2_ERR_EOF) { return NGHTTP2_ERR_EOF; } else if (readlen < 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } } } /* * Returns the number of active streams, which includes streams in * reserved state. */ static size_t session_get_num_active_streams(nghttp2_session *session) { return nghttp2_map_size(&session->streams) - session->num_closed_streams - session->num_idle_streams; } int nghttp2_session_want_read(nghttp2_session *session) { size_t num_active_streams; /* If this flag is set, we don't want to read. The application should drop the connection. */ if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) { return 0; } num_active_streams = session_get_num_active_streams(session); /* Unless termination GOAWAY is sent or received, we always want to read incoming frames. */ if (num_active_streams > 0) { return 1; } /* If there is no active streams and GOAWAY has been sent or received, we are done with this session. */ return (session->goaway_flags & (NGHTTP2_GOAWAY_SENT | NGHTTP2_GOAWAY_RECV)) == 0; } int nghttp2_session_want_write(nghttp2_session *session) { /* If these flag is set, we don't want to write any data. The application should drop the connection. */ if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) { return 0; } /* * Unless termination GOAWAY is sent or received, we want to write * frames if there is pending ones. If pending frame is request/push * response HEADERS and concurrent stream limit is reached, we don't * want to write them. */ return session->aob.item || nghttp2_outbound_queue_top(&session->ob_urgent) || nghttp2_outbound_queue_top(&session->ob_reg) || (!session_sched_empty(session) && session->remote_window_size > 0) || (nghttp2_outbound_queue_top(&session->ob_syn) && !session_is_outgoing_concurrent_streams_max(session)); } int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags, const uint8_t *opaque_data) { int rv; nghttp2_outbound_item *item; nghttp2_frame *frame; nghttp2_mem *mem; mem = &session->mem; if ((flags & NGHTTP2_FLAG_ACK) && session->obq_flood_counter_ >= session->max_outbound_ack) { return NGHTTP2_ERR_FLOODED; } item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); if (item == NULL) { return NGHTTP2_ERR_NOMEM; } nghttp2_outbound_item_init(item); frame = &item->frame; nghttp2_frame_ping_init(&frame->ping, flags, opaque_data); rv = nghttp2_session_add_item(session, item); if (rv != 0) { nghttp2_frame_ping_free(&frame->ping); nghttp2_mem_free(mem, item); return rv; } if (flags & NGHTTP2_FLAG_ACK) { ++session->obq_flood_counter_; } return 0; } int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id, uint32_t error_code, const uint8_t *opaque_data, size_t opaque_data_len, uint8_t aux_flags) { int rv; nghttp2_outbound_item *item; nghttp2_frame *frame; uint8_t *opaque_data_copy = NULL; nghttp2_goaway_aux_data *aux_data; nghttp2_mem *mem; mem = &session->mem; if (nghttp2_session_is_my_stream_id(session, last_stream_id)) { return NGHTTP2_ERR_INVALID_ARGUMENT; } if (opaque_data_len) { if (opaque_data_len + 8 > NGHTTP2_MAX_PAYLOADLEN) { return NGHTTP2_ERR_INVALID_ARGUMENT; } opaque_data_copy = nghttp2_mem_malloc(mem, opaque_data_len); if (opaque_data_copy == NULL) { return NGHTTP2_ERR_NOMEM; } memcpy(opaque_data_copy, opaque_data, opaque_data_len); } item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); if (item == NULL) { nghttp2_mem_free(mem, opaque_data_copy); return NGHTTP2_ERR_NOMEM; } nghttp2_outbound_item_init(item); frame = &item->frame; /* last_stream_id must not be increased from the value previously sent */ last_stream_id = nghttp2_min_int32(last_stream_id, session->local_last_stream_id); nghttp2_frame_goaway_init(&frame->goaway, last_stream_id, error_code, opaque_data_copy, opaque_data_len); aux_data = &item->aux_data.goaway; aux_data->flags = aux_flags; rv = nghttp2_session_add_item(session, item); if (rv != 0) { nghttp2_frame_goaway_free(&frame->goaway, mem); nghttp2_mem_free(mem, item); return rv; } session->goaway_flags |= NGHTTP2_GOAWAY_SUBMITTED; return 0; } int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags, int32_t stream_id, int32_t window_size_increment) { int rv; nghttp2_outbound_item *item; nghttp2_frame *frame; nghttp2_mem *mem; mem = &session->mem; item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); if (item == NULL) { return NGHTTP2_ERR_NOMEM; } nghttp2_outbound_item_init(item); frame = &item->frame; nghttp2_frame_window_update_init(&frame->window_update, flags, stream_id, window_size_increment); rv = nghttp2_session_add_item(session, item); if (rv != 0) { nghttp2_frame_window_update_free(&frame->window_update); nghttp2_mem_free(mem, item); return rv; } return 0; } static void session_append_inflight_settings(nghttp2_session *session, nghttp2_inflight_settings *settings) { nghttp2_inflight_settings **i; for (i = &session->inflight_settings_head; *i; i = &(*i)->next) ; *i = settings; } int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, const nghttp2_settings_entry *iv, size_t niv) { nghttp2_outbound_item *item; nghttp2_frame *frame; nghttp2_settings_entry *iv_copy; size_t i; int rv; nghttp2_mem *mem; nghttp2_inflight_settings *inflight_settings = NULL; uint8_t no_rfc7540_pri = session->pending_no_rfc7540_priorities; mem = &session->mem; if (flags & NGHTTP2_FLAG_ACK) { if (niv != 0) { return NGHTTP2_ERR_INVALID_ARGUMENT; } if (session->obq_flood_counter_ >= session->max_outbound_ack) { return NGHTTP2_ERR_FLOODED; } } if (!nghttp2_iv_check(iv, niv)) { return NGHTTP2_ERR_INVALID_ARGUMENT; } for (i = 0; i < niv; ++i) { if (iv[i].settings_id != NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES) { continue; } if (no_rfc7540_pri == UINT8_MAX) { no_rfc7540_pri = (uint8_t)iv[i].value; continue; } if (iv[i].value != (uint32_t)no_rfc7540_pri) { return NGHTTP2_ERR_INVALID_ARGUMENT; } } item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); if (item == NULL) { return NGHTTP2_ERR_NOMEM; } if (niv > 0) { iv_copy = nghttp2_frame_iv_copy(iv, niv, mem); if (iv_copy == NULL) { nghttp2_mem_free(mem, item); return NGHTTP2_ERR_NOMEM; } } else { iv_copy = NULL; } if ((flags & NGHTTP2_FLAG_ACK) == 0) { rv = inflight_settings_new(&inflight_settings, iv, niv, mem); if (rv != 0) { assert(nghttp2_is_fatal(rv)); nghttp2_mem_free(mem, iv_copy); nghttp2_mem_free(mem, item); return rv; } } nghttp2_outbound_item_init(item); frame = &item->frame; nghttp2_frame_settings_init(&frame->settings, flags, iv_copy, niv); rv = nghttp2_session_add_item(session, item); if (rv != 0) { /* The only expected error is fatal one */ assert(nghttp2_is_fatal(rv)); inflight_settings_del(inflight_settings, mem); nghttp2_frame_settings_free(&frame->settings, mem); nghttp2_mem_free(mem, item); return rv; } if (flags & NGHTTP2_FLAG_ACK) { ++session->obq_flood_counter_; } else { session_append_inflight_settings(session, inflight_settings); } /* Extract NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS and ENABLE_PUSH here. We use it to refuse the incoming stream and PUSH_PROMISE with RST_STREAM. */ for (i = niv; i > 0; --i) { if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) { session->pending_local_max_concurrent_stream = iv[i - 1].value; break; } } for (i = niv; i > 0; --i) { if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_PUSH) { session->pending_enable_push = (uint8_t)iv[i - 1].value; break; } } for (i = niv; i > 0; --i) { if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL) { session->pending_enable_connect_protocol = (uint8_t)iv[i - 1].value; break; } } if (no_rfc7540_pri == UINT8_MAX) { session->pending_no_rfc7540_priorities = 0; } else { session->pending_no_rfc7540_priorities = no_rfc7540_pri; } return 0; } int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs, size_t datamax, nghttp2_frame *frame, nghttp2_data_aux_data *aux_data, nghttp2_stream *stream) { int rv; uint32_t data_flags; nghttp2_ssize payloadlen; nghttp2_ssize padded_payloadlen; nghttp2_buf *buf; size_t max_payloadlen; assert(bufs->head == bufs->cur); buf = &bufs->cur->buf; if (session->callbacks.read_length_callback2 || session->callbacks.read_length_callback) { if (session->callbacks.read_length_callback2) { payloadlen = session->callbacks.read_length_callback2( session, frame->hd.type, stream->stream_id, session->remote_window_size, stream->remote_window_size, session->remote_settings.max_frame_size, session->user_data); } else { payloadlen = (nghttp2_ssize)session->callbacks.read_length_callback( session, frame->hd.type, stream->stream_id, session->remote_window_size, stream->remote_window_size, session->remote_settings.max_frame_size, session->user_data); } DEBUGF("send: read_length_callback=%td\n", payloadlen); payloadlen = nghttp2_session_enforce_flow_control_limits(session, stream, payloadlen); DEBUGF("send: read_length_callback after flow control=%td\n", payloadlen); if (payloadlen <= 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } if ((size_t)payloadlen > nghttp2_buf_avail(buf)) { /* Resize the current buffer(s). The reason why we do +1 for buffer size is for possible padding field. */ rv = nghttp2_bufs_realloc(&session->aob.framebufs, (size_t)(NGHTTP2_FRAME_HDLEN + 1 + payloadlen)); if (rv != 0) { DEBUGF("send: realloc buffer failed rv=%d", rv); /* If reallocation failed, old buffers are still in tact. So use safe limit. */ payloadlen = (nghttp2_ssize)datamax; DEBUGF("send: use safe limit payloadlen=%td", payloadlen); } else { assert(&session->aob.framebufs == bufs); buf = &bufs->cur->buf; } } datamax = (size_t)payloadlen; } /* Current max DATA length is less then buffer chunk size */ assert(nghttp2_buf_avail(buf) >= datamax); data_flags = NGHTTP2_DATA_FLAG_NONE; switch (aux_data->dpw.version) { case NGHTTP2_DATA_PROVIDER_V1: payloadlen = (nghttp2_ssize)aux_data->dpw.data_prd.v1.read_callback( session, frame->hd.stream_id, buf->pos, datamax, &data_flags, &aux_data->dpw.data_prd.source, session->user_data); break; case NGHTTP2_DATA_PROVIDER_V2: payloadlen = aux_data->dpw.data_prd.v2.read_callback( session, frame->hd.stream_id, buf->pos, datamax, &data_flags, &aux_data->dpw.data_prd.source, session->user_data); break; default: assert(0); abort(); } if (payloadlen == NGHTTP2_ERR_DEFERRED || payloadlen == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE || payloadlen == NGHTTP2_ERR_PAUSE) { DEBUGF("send: DATA postponed due to %s\n", nghttp2_strerror((int)payloadlen)); return (int)payloadlen; } if (payloadlen < 0 || datamax < (size_t)payloadlen) { /* This is the error code when callback is failed. */ return NGHTTP2_ERR_CALLBACK_FAILURE; } buf->last = buf->pos + payloadlen; buf->pos -= NGHTTP2_FRAME_HDLEN; /* Clear flags, because this may contain previous flags of previous DATA */ frame->hd.flags = NGHTTP2_FLAG_NONE; if (data_flags & NGHTTP2_DATA_FLAG_EOF) { aux_data->eof = 1; /* If NGHTTP2_DATA_FLAG_NO_END_STREAM is set, don't set NGHTTP2_FLAG_END_STREAM */ if ((aux_data->flags & NGHTTP2_FLAG_END_STREAM) && (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM) == 0) { frame->hd.flags |= NGHTTP2_FLAG_END_STREAM; } } if (data_flags & NGHTTP2_DATA_FLAG_NO_COPY) { if (session->callbacks.send_data_callback == NULL) { DEBUGF("NGHTTP2_DATA_FLAG_NO_COPY requires send_data_callback set\n"); return NGHTTP2_ERR_CALLBACK_FAILURE; } aux_data->no_copy = 1; } frame->hd.length = (size_t)payloadlen; frame->data.padlen = 0; max_payloadlen = nghttp2_min_size(datamax, frame->hd.length + NGHTTP2_MAX_PADLEN); padded_payloadlen = session_call_select_padding(session, frame, max_payloadlen); if (nghttp2_is_fatal((int)padded_payloadlen)) { return (int)padded_payloadlen; } frame->data.padlen = (size_t)(padded_payloadlen - payloadlen); nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); nghttp2_frame_add_pad(bufs, &frame->hd, frame->data.padlen, aux_data->no_copy); session_reschedule_stream(session, stream); if (frame->hd.length == 0 && (data_flags & NGHTTP2_DATA_FLAG_EOF) && (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM)) { /* DATA payload length is 0, and DATA frame does not bear END_STREAM. In this case, there is no point to send 0 length DATA frame. */ return NGHTTP2_ERR_CANCEL; } return 0; } void *nghttp2_session_get_stream_user_data(nghttp2_session *session, int32_t stream_id) { nghttp2_stream *stream; stream = nghttp2_session_get_stream(session, stream_id); if (stream) { return stream->stream_user_data; } else { return NULL; } } int nghttp2_session_set_stream_user_data(nghttp2_session *session, int32_t stream_id, void *stream_user_data) { nghttp2_stream *stream; nghttp2_frame *frame; nghttp2_outbound_item *item; stream = nghttp2_session_get_stream(session, stream_id); if (stream) { stream->stream_user_data = stream_user_data; return 0; } if (session->server || !nghttp2_session_is_my_stream_id(session, stream_id) || !nghttp2_outbound_queue_top(&session->ob_syn)) { return NGHTTP2_ERR_INVALID_ARGUMENT; } frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame; assert(frame->hd.type == NGHTTP2_HEADERS); if (frame->hd.stream_id > stream_id || (uint32_t)stream_id >= session->next_stream_id) { return NGHTTP2_ERR_INVALID_ARGUMENT; } for (item = session->ob_syn.head; item; item = item->qnext) { if (item->frame.hd.stream_id < stream_id) { continue; } if (item->frame.hd.stream_id > stream_id) { break; } item->aux_data.headers.stream_user_data = stream_user_data; return 0; } return NGHTTP2_ERR_INVALID_ARGUMENT; } int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) { int rv; nghttp2_stream *stream; stream = nghttp2_session_get_stream(session, stream_id); if (stream == NULL || !nghttp2_stream_check_deferred_item(stream)) { return NGHTTP2_ERR_INVALID_ARGUMENT; } rv = session_resume_deferred_stream_item(session, stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER); if (nghttp2_is_fatal(rv)) { return rv; } return 0; } size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) { return nghttp2_outbound_queue_size(&session->ob_urgent) + nghttp2_outbound_queue_size(&session->ob_reg) + nghttp2_outbound_queue_size(&session->ob_syn); /* TODO account for item attached to stream */ } int32_t nghttp2_session_get_stream_effective_recv_data_length(nghttp2_session *session, int32_t stream_id) { nghttp2_stream *stream; stream = nghttp2_session_get_stream(session, stream_id); if (stream == NULL) { return -1; } return stream->recv_window_size < 0 ? 0 : stream->recv_window_size; } int32_t nghttp2_session_get_stream_effective_local_window_size(nghttp2_session *session, int32_t stream_id) { nghttp2_stream *stream; stream = nghttp2_session_get_stream(session, stream_id); if (stream == NULL) { return -1; } return stream->local_window_size; } int32_t nghttp2_session_get_stream_local_window_size(nghttp2_session *session, int32_t stream_id) { nghttp2_stream *stream; int32_t size; stream = nghttp2_session_get_stream(session, stream_id); if (stream == NULL) { return -1; } size = stream->local_window_size - stream->recv_window_size; /* size could be negative if local endpoint reduced SETTINGS_INITIAL_WINDOW_SIZE */ if (size < 0) { return 0; } return size; } int32_t nghttp2_session_get_effective_recv_data_length(nghttp2_session *session) { return session->recv_window_size < 0 ? 0 : session->recv_window_size; } int32_t nghttp2_session_get_effective_local_window_size(nghttp2_session *session) { return session->local_window_size; } int32_t nghttp2_session_get_local_window_size(nghttp2_session *session) { return session->local_window_size - session->recv_window_size; } int32_t nghttp2_session_get_stream_remote_window_size(nghttp2_session *session, int32_t stream_id) { nghttp2_stream *stream; stream = nghttp2_session_get_stream(session, stream_id); if (stream == NULL) { return -1; } /* stream->remote_window_size can be negative when SETTINGS_INITIAL_WINDOW_SIZE is changed. */ return nghttp2_max_int32(0, stream->remote_window_size); } int32_t nghttp2_session_get_remote_window_size(nghttp2_session *session) { return session->remote_window_size; } uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session, nghttp2_settings_id id) { switch (id) { case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: return session->remote_settings.header_table_size; case NGHTTP2_SETTINGS_ENABLE_PUSH: return session->remote_settings.enable_push; case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: return session->remote_settings.max_concurrent_streams; case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: return session->remote_settings.initial_window_size; case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: return session->remote_settings.max_frame_size; case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: return session->remote_settings.max_header_list_size; case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: return session->remote_settings.enable_connect_protocol; case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES: return session->remote_settings.no_rfc7540_priorities; } assert(0); abort(); /* if NDEBUG is set */ } uint32_t nghttp2_session_get_local_settings(nghttp2_session *session, nghttp2_settings_id id) { switch (id) { case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: return session->local_settings.header_table_size; case NGHTTP2_SETTINGS_ENABLE_PUSH: return session->local_settings.enable_push; case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: return session->local_settings.max_concurrent_streams; case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: return session->local_settings.initial_window_size; case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: return session->local_settings.max_frame_size; case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: return session->local_settings.max_header_list_size; case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: return session->local_settings.enable_connect_protocol; case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES: return session->local_settings.no_rfc7540_priorities; } assert(0); abort(); /* if NDEBUG is set */ } static int nghttp2_session_upgrade_internal(nghttp2_session *session, const uint8_t *settings_payload, size_t settings_payloadlen, void *stream_user_data) { nghttp2_stream *stream; nghttp2_frame frame; nghttp2_settings_entry *iv; size_t niv; int rv; nghttp2_mem *mem; mem = &session->mem; if ((!session->server && session->next_stream_id != 1) || (session->server && session->last_recv_stream_id >= 1)) { return NGHTTP2_ERR_PROTO; } if (settings_payloadlen % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) { return NGHTTP2_ERR_INVALID_ARGUMENT; } /* SETTINGS frame contains too many settings */ if (settings_payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH > session->max_settings) { return NGHTTP2_ERR_TOO_MANY_SETTINGS; } rv = nghttp2_frame_unpack_settings_payload2(&iv, &niv, settings_payload, settings_payloadlen, mem); if (rv != 0) { return rv; } if (session->server) { nghttp2_frame_hd_init(&frame.hd, settings_payloadlen, NGHTTP2_SETTINGS, NGHTTP2_FLAG_NONE, 0); frame.settings.iv = iv; frame.settings.niv = niv; rv = nghttp2_session_on_settings_received(session, &frame, 1 /* No ACK */); } else { rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, niv); } nghttp2_mem_free(mem, iv); if (rv != 0) { return rv; } stream = nghttp2_session_open_stream( session, 1, NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_STREAM_OPENING, session->server ? NULL : stream_user_data); if (stream == NULL) { return NGHTTP2_ERR_NOMEM; } if (session->server) { nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); session->last_recv_stream_id = 1; session->last_proc_stream_id = 1; } else { nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); session->last_sent_stream_id = 1; session->next_stream_id += 2; } return 0; } int nghttp2_session_upgrade(nghttp2_session *session, const uint8_t *settings_payload, size_t settings_payloadlen, void *stream_user_data) { int rv; nghttp2_stream *stream; rv = nghttp2_session_upgrade_internal(session, settings_payload, settings_payloadlen, stream_user_data); if (rv != 0) { return rv; } stream = nghttp2_session_get_stream(session, 1); assert(stream); /* We have no information about request header fields when Upgrade was happened. So we don't know the request method here. If request method is HEAD, we have a trouble because we may have nonzero content-length header field in response headers, and we will going to check it against the actual DATA frames, but we may get mismatch because HEAD response body must be empty. Because of this reason, nghttp2_session_upgrade() was deprecated in favor of nghttp2_session_upgrade2(), which has |head_request| parameter to indicate that request method is HEAD or not. */ stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND; return 0; } int nghttp2_session_upgrade2(nghttp2_session *session, const uint8_t *settings_payload, size_t settings_payloadlen, int head_request, void *stream_user_data) { int rv; nghttp2_stream *stream; rv = nghttp2_session_upgrade_internal(session, settings_payload, settings_payloadlen, stream_user_data); if (rv != 0) { return rv; } stream = nghttp2_session_get_stream(session, 1); assert(stream); if (head_request) { stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; } return 0; } int nghttp2_session_get_stream_local_close(nghttp2_session *session, int32_t stream_id) { nghttp2_stream *stream; stream = nghttp2_session_get_stream(session, stream_id); if (!stream) { return -1; } return (stream->shut_flags & NGHTTP2_SHUT_WR) != 0; } int nghttp2_session_get_stream_remote_close(nghttp2_session *session, int32_t stream_id) { nghttp2_stream *stream; stream = nghttp2_session_get_stream(session, stream_id); if (!stream) { return -1; } return (stream->shut_flags & NGHTTP2_SHUT_RD) != 0; } int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id, size_t size) { int rv; nghttp2_stream *stream; if (stream_id == 0) { return NGHTTP2_ERR_INVALID_ARGUMENT; } if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { return NGHTTP2_ERR_INVALID_STATE; } rv = session_update_connection_consumed_size(session, size); if (nghttp2_is_fatal(rv)) { return rv; } stream = nghttp2_session_get_stream(session, stream_id); if (!stream) { return 0; } rv = session_update_stream_consumed_size(session, stream, size); if (nghttp2_is_fatal(rv)) { return rv; } return 0; } int nghttp2_session_consume_connection(nghttp2_session *session, size_t size) { int rv; if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { return NGHTTP2_ERR_INVALID_STATE; } rv = session_update_connection_consumed_size(session, size); if (nghttp2_is_fatal(rv)) { return rv; } return 0; } int nghttp2_session_consume_stream(nghttp2_session *session, int32_t stream_id, size_t size) { int rv; nghttp2_stream *stream; if (stream_id == 0) { return NGHTTP2_ERR_INVALID_ARGUMENT; } if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { return NGHTTP2_ERR_INVALID_STATE; } stream = nghttp2_session_get_stream(session, stream_id); if (!stream) { return 0; } rv = session_update_stream_consumed_size(session, stream, size); if (nghttp2_is_fatal(rv)) { return rv; } return 0; } int nghttp2_session_set_next_stream_id(nghttp2_session *session, int32_t next_stream_id) { if (next_stream_id <= 0 || session->next_stream_id > (uint32_t)next_stream_id) { return NGHTTP2_ERR_INVALID_ARGUMENT; } if (session->server) { if (next_stream_id % 2) { return NGHTTP2_ERR_INVALID_ARGUMENT; } } else if (next_stream_id % 2 == 0) { return NGHTTP2_ERR_INVALID_ARGUMENT; } session->next_stream_id = (uint32_t)next_stream_id; return 0; } uint32_t nghttp2_session_get_next_stream_id(nghttp2_session *session) { return session->next_stream_id; } int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session) { return session->last_proc_stream_id; } nghttp2_stream *nghttp2_session_find_stream(nghttp2_session *session, int32_t stream_id) { if (stream_id == 0) { return &nghttp2_stream_root; } return nghttp2_session_get_stream_raw(session, stream_id); } nghttp2_stream *nghttp2_session_get_root_stream(nghttp2_session *session) { (void)session; return &nghttp2_stream_root; } int nghttp2_session_check_server_session(nghttp2_session *session) { return session->server; } int nghttp2_session_change_stream_priority( nghttp2_session *session, int32_t stream_id, const nghttp2_priority_spec *pri_spec) { (void)session; (void)stream_id; (void)pri_spec; return 0; } int nghttp2_session_create_idle_stream(nghttp2_session *session, int32_t stream_id, const nghttp2_priority_spec *pri_spec) { (void)session; (void)stream_id; (void)pri_spec; return 0; } size_t nghttp2_session_get_hd_inflate_dynamic_table_size(nghttp2_session *session) { return nghttp2_hd_inflate_get_dynamic_table_size(&session->hd_inflater); } size_t nghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session) { return nghttp2_hd_deflate_get_dynamic_table_size(&session->hd_deflater); } void nghttp2_session_set_user_data(nghttp2_session *session, void *user_data) { session->user_data = user_data; } int nghttp2_session_change_extpri_stream_priority( nghttp2_session *session, int32_t stream_id, const nghttp2_extpri *extpri_in, int ignore_client_signal) { nghttp2_stream *stream; nghttp2_extpri extpri = *extpri_in; if (!session->server) { return NGHTTP2_ERR_INVALID_STATE; } if (session->pending_no_rfc7540_priorities != 1) { return 0; } if (stream_id == 0) { return NGHTTP2_ERR_INVALID_ARGUMENT; } stream = nghttp2_session_get_stream_raw(session, stream_id); if (!stream) { return NGHTTP2_ERR_INVALID_ARGUMENT; } if (extpri.urgency > NGHTTP2_EXTPRI_URGENCY_LOW) { extpri.urgency = NGHTTP2_EXTPRI_URGENCY_LOW; } if (ignore_client_signal) { stream->flags |= NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES; } return session_update_stream_priority(session, stream, nghttp2_extpri_to_uint8(&extpri)); } int nghttp2_session_get_extpri_stream_priority(nghttp2_session *session, nghttp2_extpri *extpri, int32_t stream_id) { nghttp2_stream *stream; if (!session->server) { return NGHTTP2_ERR_INVALID_STATE; } if (session->pending_no_rfc7540_priorities != 1) { return 0; } if (stream_id == 0) { return NGHTTP2_ERR_INVALID_ARGUMENT; } stream = nghttp2_session_get_stream_raw(session, stream_id); if (!stream) { return NGHTTP2_ERR_INVALID_ARGUMENT; } nghttp2_extpri_from_uint8(extpri, stream->extpri); return 0; } nghttp2-1.68.0/lib/PaxHeaders/nghttp2_callbacks.h0000644000000000000000000000013115077107270016563 xustar0030 mtime=1761382072.977444213 30 atime=1761382104.952315942 29 ctime=1761382107.93830374 nghttp2-1.68.0/lib/nghttp2_callbacks.h0000644000175100017510000001350715077107270017162 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_CALLBACKS_H #define NGHTTP2_CALLBACKS_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include /* * Callback functions. */ struct nghttp2_session_callbacks { /** * Deprecated. Use send_callback2 instead. Callback function * invoked when the session wants to send data to the remote peer. * This callback is not necessary if the application uses solely * `nghttp2_session_mem_send()` to serialize data to transmit. */ nghttp2_send_callback send_callback; /** * Callback function invoked when the session wants to send data to * the remote peer. This callback is not necessary if the * application uses solely `nghttp2_session_mem_send2()` to * serialize data to transmit. */ nghttp2_send_callback2 send_callback2; /** * Deprecated. Use recv_callback2 instead. Callback function * invoked when the session wants to receive data from the remote * peer. This callback is not necessary if the application uses * solely `nghttp2_session_mem_recv()` to process received data. */ nghttp2_recv_callback recv_callback; /** * Callback function invoked when the session wants to receive data * from the remote peer. This callback is not necessary if the * application uses solely `nghttp2_session_mem_recv2()` to process * received data. */ nghttp2_recv_callback2 recv_callback2; /** * Callback function invoked by `nghttp2_session_recv()` when a * frame is received. */ nghttp2_on_frame_recv_callback on_frame_recv_callback; /** * Callback function invoked by `nghttp2_session_recv()` when an * invalid non-DATA frame is received. */ nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback; /** * Callback function invoked when a chunk of data in DATA frame is * received. */ nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback; /** * Callback function invoked before a non-DATA frame is sent. */ nghttp2_before_frame_send_callback before_frame_send_callback; /** * Callback function invoked after a frame is sent. */ nghttp2_on_frame_send_callback on_frame_send_callback; /** * The callback function invoked when a non-DATA frame is not sent * because of an error. */ nghttp2_on_frame_not_send_callback on_frame_not_send_callback; /** * Callback function invoked when the stream is closed. */ nghttp2_on_stream_close_callback on_stream_close_callback; /** * Callback function invoked when the reception of header block in * HEADERS or PUSH_PROMISE is started. */ nghttp2_on_begin_headers_callback on_begin_headers_callback; /** * Callback function invoked when a header name/value pair is * received. */ nghttp2_on_header_callback on_header_callback; nghttp2_on_header_callback2 on_header_callback2; /** * Callback function invoked when a invalid header name/value pair * is received which is silently ignored if these callbacks are not * set. */ nghttp2_on_invalid_header_callback on_invalid_header_callback; nghttp2_on_invalid_header_callback2 on_invalid_header_callback2; /** * Deprecated. Use select_padding_callback2 instead. Callback * function invoked when the library asks application how many * padding bytes are required for the transmission of the given * frame. */ nghttp2_select_padding_callback select_padding_callback; /** * Callback function invoked when the library asks application how * many padding bytes are required for the transmission of the given * frame. */ nghttp2_select_padding_callback2 select_padding_callback2; /** * Deprecated. Use read_length_callback2 instead. The callback * function used to determine the length allowed in * `nghttp2_data_source_read_callback()` */ nghttp2_data_source_read_length_callback read_length_callback; /** * The callback function used to determine the length allowed in * `nghttp2_data_source_read_callback2()` */ nghttp2_data_source_read_length_callback2 read_length_callback2; /** * Sets callback function invoked when a frame header is received. */ nghttp2_on_begin_frame_callback on_begin_frame_callback; nghttp2_send_data_callback send_data_callback; /** * Deprecated. Use pack_extension_callback2 instead. */ nghttp2_pack_extension_callback pack_extension_callback; nghttp2_pack_extension_callback2 pack_extension_callback2; nghttp2_unpack_extension_callback unpack_extension_callback; nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback; nghttp2_error_callback error_callback; nghttp2_error_callback2 error_callback2; nghttp2_rand_callback rand_callback; }; #endif /* !defined(NGHTTP2_CALLBACKS_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_rcbuf.c0000644000000000000000000000013215077107270015741 xustar0030 mtime=1761382072.980444199 30 atime=1761382104.989315779 30 ctime=1761382107.975303633 nghttp2-1.68.0/lib/nghttp2_rcbuf.c0000644000175100017510000000534115077107270016334 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_rcbuf.h" #include #include #include "nghttp2_mem.h" #include "nghttp2_helper.h" int nghttp2_rcbuf_new(nghttp2_rcbuf **rcbuf_ptr, size_t size, nghttp2_mem *mem) { uint8_t *p; p = nghttp2_mem_malloc(mem, sizeof(nghttp2_rcbuf) + size); if (p == NULL) { return NGHTTP2_ERR_NOMEM; } *rcbuf_ptr = (void *)p; (*rcbuf_ptr)->mem_user_data = mem->mem_user_data; (*rcbuf_ptr)->free = mem->free; (*rcbuf_ptr)->base = p + sizeof(nghttp2_rcbuf); (*rcbuf_ptr)->len = size; (*rcbuf_ptr)->ref = 1; return 0; } int nghttp2_rcbuf_new2(nghttp2_rcbuf **rcbuf_ptr, const uint8_t *src, size_t srclen, nghttp2_mem *mem) { int rv; rv = nghttp2_rcbuf_new(rcbuf_ptr, srclen + 1, mem); if (rv != 0) { return rv; } (*rcbuf_ptr)->len = srclen; *nghttp2_cpymem((*rcbuf_ptr)->base, src, srclen) = '\0'; return 0; } /* * Frees |rcbuf| itself, regardless of its reference cout. */ void nghttp2_rcbuf_del(nghttp2_rcbuf *rcbuf) { nghttp2_mem_free2(rcbuf->free, rcbuf, rcbuf->mem_user_data); } void nghttp2_rcbuf_incref(nghttp2_rcbuf *rcbuf) { if (rcbuf->ref == -1) { return; } ++rcbuf->ref; } void nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf) { if (rcbuf == NULL || rcbuf->ref == -1) { return; } assert(rcbuf->ref > 0); if (--rcbuf->ref == 0) { nghttp2_rcbuf_del(rcbuf); } } nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf) { nghttp2_vec res = {rcbuf->base, rcbuf->len}; return res; } int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf) { return rcbuf->ref == -1; } nghttp2-1.68.0/lib/PaxHeaders/nghttp2_map.c0000644000000000000000000000013215077107270015415 xustar0030 mtime=1761382072.979444204 30 atime=1761382104.965315885 30 ctime=1761382107.951303702 nghttp2-1.68.0/lib/nghttp2_map.c0000644000175100017510000001561615077107270016016 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2017 ngtcp2 contributors * Copyright (c) 2012 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_map.h" #include #include #include #include "nghttp2_helper.h" #define NGHTTP2_INITIAL_HASHBITS 4 void nghttp2_map_init(nghttp2_map *map, uint32_t seed, nghttp2_mem *mem) { map->mem = mem; map->hashbits = 0; map->table = NULL; map->seed = seed; map->size = 0; } void nghttp2_map_free(nghttp2_map *map) { if (!map) { return; } nghttp2_mem_free(map->mem, map->table); } int nghttp2_map_each(const nghttp2_map *map, int (*func)(void *data, void *ptr), void *ptr) { int rv; size_t i; nghttp2_map_bucket *bkt; size_t tablelen; if (map->size == 0) { return 0; } tablelen = 1u << map->hashbits; for (i = 0; i < tablelen; ++i) { bkt = &map->table[i]; if (bkt->data == NULL) { continue; } rv = func(bkt->data, ptr); if (rv != 0) { return rv; } } return 0; } static size_t map_hash(const nghttp2_map *map, nghttp2_map_key_type key) { /* hasher from https://github.com/rust-lang/rustc-hash/blob/dc5c33f1283de2da64d8d7a06401d91aded03ad4/src/lib.rs We do not perform finalization here because we use top bits anyway. */ uint32_t h = ((uint32_t)key + map->seed) * 0x93d765dd; return (size_t)((h * 2654435769u) >> (32 - map->hashbits)); } static void map_bucket_swap(nghttp2_map_bucket *a, nghttp2_map_bucket *b) { nghttp2_map_bucket c = *a; *a = *b; *b = c; } #ifndef WIN32 void nghttp2_map_print_distance(const nghttp2_map *map) { size_t i; size_t idx; nghttp2_map_bucket *bkt; size_t tablelen; if (map->size == 0) { return; } tablelen = 1u << map->hashbits; for (i = 0; i < tablelen; ++i) { bkt = &map->table[i]; if (bkt->data == NULL) { fprintf(stderr, "@%zu \n", i); continue; } idx = map_hash(map, bkt->key); fprintf(stderr, "@%zu hash=%zu key=%d base=%zu distance=%u\n", i, map_hash(map, bkt->key), bkt->key, idx, bkt->psl); } } #endif /* !defined(WIN32) */ static int map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) { size_t idx = map_hash(map, key); nghttp2_map_bucket b = { .key = key, .data = data, }; nghttp2_map_bucket *bkt; size_t mask = (1u << map->hashbits) - 1; for (;;) { bkt = &map->table[idx]; if (bkt->data == NULL) { *bkt = b; ++map->size; return 0; } if (b.psl > bkt->psl) { map_bucket_swap(bkt, &b); } else if (bkt->key == key) { /* TODO This check is just a waste after first swap or if this function is called from map_resize. That said, there is no difference with or without this conditional in performance wise. */ return NGHTTP2_ERR_INVALID_ARGUMENT; } ++b.psl; idx = (idx + 1) & mask; } } static int map_resize(nghttp2_map *map, size_t new_hashbits) { size_t i; nghttp2_map_bucket *bkt; size_t tablelen; int rv; nghttp2_map new_map = { .table = nghttp2_mem_calloc(map->mem, 1u << new_hashbits, sizeof(nghttp2_map_bucket)), .mem = map->mem, .seed = map->seed, .hashbits = new_hashbits, }; (void)rv; if (new_map.table == NULL) { return NGHTTP2_ERR_NOMEM; } if (map->size) { tablelen = 1u << map->hashbits; for (i = 0; i < tablelen; ++i) { bkt = &map->table[i]; if (bkt->data == NULL) { continue; } rv = map_insert(&new_map, bkt->key, bkt->data); assert(0 == rv); } } nghttp2_mem_free(map->mem, map->table); map->table = new_map.table; map->hashbits = new_hashbits; return 0; } int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) { int rv; assert(data); /* Load factor is 7/8 */ /* Under the very initial condition, that is map->size == 0 and map->hashbits == 0, 8 > 7 still holds nicely. */ if ((map->size + 1) * 8 > (1u << map->hashbits) * 7) { if (map->hashbits) { rv = map_resize(map, map->hashbits + 1); if (rv != 0) { return rv; } } else { rv = map_resize(map, NGHTTP2_INITIAL_HASHBITS); if (rv != 0) { return rv; } } } rv = map_insert(map, key, data); if (rv != 0) { return rv; } return 0; } void *nghttp2_map_find(const nghttp2_map *map, nghttp2_map_key_type key) { size_t idx; nghttp2_map_bucket *bkt; size_t psl = 0; size_t mask; if (map->size == 0) { return NULL; } idx = map_hash(map, key); mask = (1u << map->hashbits) - 1; for (;;) { bkt = &map->table[idx]; if (bkt->data == NULL || psl > bkt->psl) { return NULL; } if (bkt->key == key) { return bkt->data; } ++psl; idx = (idx + 1) & mask; } } int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key) { size_t idx; nghttp2_map_bucket *b, *bkt; size_t psl = 0; size_t mask; if (map->size == 0) { return NGHTTP2_ERR_INVALID_ARGUMENT; } idx = map_hash(map, key); mask = (1u << map->hashbits) - 1; for (;;) { bkt = &map->table[idx]; if (bkt->data == NULL || psl > bkt->psl) { return NGHTTP2_ERR_INVALID_ARGUMENT; } if (bkt->key == key) { b = bkt; idx = (idx + 1) & mask; for (;;) { bkt = &map->table[idx]; if (bkt->data == NULL || bkt->psl == 0) { b->data = NULL; break; } --bkt->psl; *b = *bkt; b = bkt; idx = (idx + 1) & mask; } --map->size; return 0; } ++psl; idx = (idx + 1) & mask; } } void nghttp2_map_clear(nghttp2_map *map) { if (map->size == 0) { return; } memset(map->table, 0, sizeof(*map->table) * (1u << map->hashbits)); map->size = 0; } size_t nghttp2_map_size(const nghttp2_map *map) { return map->size; } nghttp2-1.68.0/lib/PaxHeaders/CMakeLists.txt0000644000000000000000000000013115077107270015565 xustar0030 mtime=1761382072.974444227 29 atime=1761382104.99831574 30 ctime=1761382107.984303607 nghttp2-1.68.0/lib/CMakeLists.txt0000644000175100017510000000747515077107270016173 0ustar00runnerrunneradd_subdirectory(includes) include_directories( "${CMAKE_CURRENT_SOURCE_DIR}/includes" "${CMAKE_CURRENT_BINARY_DIR}/includes" ) add_definitions(-DBUILDING_NGHTTP2) set(NGHTTP2_SOURCES nghttp2_pq.c nghttp2_map.c nghttp2_queue.c nghttp2_frame.c nghttp2_buf.c nghttp2_stream.c nghttp2_outbound_item.c nghttp2_session.c nghttp2_submit.c nghttp2_helper.c nghttp2_alpn.c nghttp2_hd.c nghttp2_hd_huffman.c nghttp2_hd_huffman_data.c nghttp2_version.c nghttp2_priority_spec.c nghttp2_option.c nghttp2_callbacks.c nghttp2_mem.c nghttp2_http.c nghttp2_rcbuf.c nghttp2_extpri.c nghttp2_ratelim.c nghttp2_time.c nghttp2_debug.c sfparse.c ) set(NGHTTP2_RES "") set(STATIC_LIB "nghttp2_static") set(SHARED_LIB "nghttp2") if(BUILD_SHARED_LIBS AND BUILD_STATIC_LIBS AND MSVC AND NOT STATIC_LIB_SUFFIX) set(STATIC_LIB_SUFFIX "_static") endif() if(WIN32) configure_file( version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc @ONLY) set(NGHTTP2_RES ${CMAKE_CURRENT_BINARY_DIR}/version.rc) endif() set(NGHTTP2_GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") set(NGHTTP2_VERSION_CONFIG "${NGHTTP2_GENERATED_DIR}/${PROJECT_NAME}ConfigVersion.cmake") set(NGHTTP2_PROJECT_CONFIG "${NGHTTP2_GENERATED_DIR}/${PROJECT_NAME}Config.cmake") set(NGHTTP2_TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") set(NGHTTP2_CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}") set(NGHTTP2_NAMESPACE "${PROJECT_NAME}::") set(NGHTTP2_VERSION ${PROJECT_VERSION}) include(CMakePackageConfigHelpers) write_basic_package_version_file( "${NGHTTP2_VERSION_CONFIG}" VERSION ${NGHTTP2_VERSION} COMPATIBILITY SameMajorVersion ) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.cmake.in" "${NGHTTP2_PROJECT_CONFIG}" @ONLY) # Install cmake config files install( FILES "${NGHTTP2_PROJECT_CONFIG}" "${NGHTTP2_VERSION_CONFIG}" DESTINATION "${NGHTTP2_CONFIG_INSTALL_DIR}") install( EXPORT "${NGHTTP2_TARGETS_EXPORT_NAME}" NAMESPACE "${NGHTTP2_NAMESPACE}" DESTINATION "${NGHTTP2_CONFIG_INSTALL_DIR}") # Public shared library if(BUILD_SHARED_LIBS) add_library(${SHARED_LIB} SHARED ${NGHTTP2_SOURCES} ${NGHTTP2_RES}) set_target_properties(${SHARED_LIB} PROPERTIES COMPILE_FLAGS "${WARNCFLAGS}" VERSION ${LT_VERSION} SOVERSION ${LT_SOVERSION} C_VISIBILITY_PRESET hidden ) target_include_directories(${SHARED_LIB} INTERFACE $ $ $ ) install(TARGETS ${SHARED_LIB} EXPORT ${NGHTTP2_TARGETS_EXPORT_NAME} ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") list(APPEND nghttp2_exports ${SHARED_LIB}) endif() # Static library (for unittests because of symbol visibility) if(BUILD_STATIC_LIBS) add_library(${STATIC_LIB} STATIC ${NGHTTP2_SOURCES}) set_target_properties(${STATIC_LIB} PROPERTIES COMPILE_FLAGS "${WARNCFLAGS}" VERSION ${LT_VERSION} SOVERSION ${LT_SOVERSION} ARCHIVE_OUTPUT_NAME nghttp2${STATIC_LIB_SUFFIX} ) target_include_directories(${STATIC_LIB} INTERFACE $ $ $ ) target_compile_definitions(${STATIC_LIB} PUBLIC "-DNGHTTP2_STATICLIB") install(TARGETS ${STATIC_LIB} EXPORT ${NGHTTP2_TARGETS_EXPORT_NAME} ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") list(APPEND nghttp2_exports ${STATIC_LIB}) endif() if(BUILD_SHARED_LIBS) set(LIB_SELECTED ${SHARED_LIB}) else() set(LIB_SELECTED ${STATIC_LIB}) endif() add_library(nghttp2 ALIAS ${LIB_SELECTED}) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libnghttp2.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") nghttp2-1.68.0/lib/PaxHeaders/Makefile.in0000644000000000000000000000013215077107305015072 xustar0030 mtime=1761382085.595387046 30 atime=1761382103.778321114 30 ctime=1761382107.912303815 nghttp2-1.68.0/lib/Makefile.in0000644000175100017510000011117315077107305015466 0ustar00runnerrunner# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # nghttp2 - HTTP/2 C Library # Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = lib ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = libnghttp2.pc CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" LTLIBRARIES = $(lib_LTLIBRARIES) libnghttp2_la_LIBADD = am__objects_1 = am__objects_2 = nghttp2_pq.lo nghttp2_map.lo nghttp2_queue.lo \ nghttp2_frame.lo nghttp2_buf.lo nghttp2_stream.lo \ nghttp2_outbound_item.lo nghttp2_session.lo nghttp2_submit.lo \ nghttp2_helper.lo nghttp2_alpn.lo nghttp2_hd.lo \ nghttp2_hd_huffman.lo nghttp2_hd_huffman_data.lo \ nghttp2_version.lo nghttp2_priority_spec.lo nghttp2_option.lo \ nghttp2_callbacks.lo nghttp2_mem.lo nghttp2_http.lo \ nghttp2_rcbuf.lo nghttp2_extpri.lo nghttp2_ratelim.lo \ nghttp2_time.lo nghttp2_debug.lo sfparse.lo am_libnghttp2_la_OBJECTS = $(am__objects_1) $(am__objects_2) libnghttp2_la_OBJECTS = $(am_libnghttp2_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libnghttp2_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(libnghttp2_la_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/nghttp2_alpn.Plo \ ./$(DEPDIR)/nghttp2_buf.Plo ./$(DEPDIR)/nghttp2_callbacks.Plo \ ./$(DEPDIR)/nghttp2_debug.Plo ./$(DEPDIR)/nghttp2_extpri.Plo \ ./$(DEPDIR)/nghttp2_frame.Plo ./$(DEPDIR)/nghttp2_hd.Plo \ ./$(DEPDIR)/nghttp2_hd_huffman.Plo \ ./$(DEPDIR)/nghttp2_hd_huffman_data.Plo \ ./$(DEPDIR)/nghttp2_helper.Plo ./$(DEPDIR)/nghttp2_http.Plo \ ./$(DEPDIR)/nghttp2_map.Plo ./$(DEPDIR)/nghttp2_mem.Plo \ ./$(DEPDIR)/nghttp2_option.Plo \ ./$(DEPDIR)/nghttp2_outbound_item.Plo \ ./$(DEPDIR)/nghttp2_pq.Plo \ ./$(DEPDIR)/nghttp2_priority_spec.Plo \ ./$(DEPDIR)/nghttp2_queue.Plo ./$(DEPDIR)/nghttp2_ratelim.Plo \ ./$(DEPDIR)/nghttp2_rcbuf.Plo ./$(DEPDIR)/nghttp2_session.Plo \ ./$(DEPDIR)/nghttp2_stream.Plo ./$(DEPDIR)/nghttp2_submit.Plo \ ./$(DEPDIR)/nghttp2_time.Plo ./$(DEPDIR)/nghttp2_version.Plo \ ./$(DEPDIR)/sfparse.Plo am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libnghttp2_la_SOURCES) DIST_SOURCES = $(libnghttp2_la_SOURCES) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac DATA = $(pkgconfig_DATA) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir distdir-am am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/libnghttp2.pc.in \ $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPLDFLAGS = @APPLDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BPFCFLAGS = @BPFCFLAGS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CXX = @CXX@ CXX1XCXXFLAGS = @CXX1XCXXFLAGS@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXTRABPFCFLAGS = @EXTRABPFCFLAGS@ EXTRACFLAG = @EXTRACFLAG@ EXTRA_DEFS = @EXTRA_DEFS@ FGREP = @FGREP@ FILECMD = @FILECMD@ GREP = @GREP@ HAVE_CXX20 = @HAVE_CXX20@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ JANSSON_CFLAGS = @JANSSON_CFLAGS@ JANSSON_LIBS = @JANSSON_LIBS@ JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ JEMALLOC_LIBS = @JEMALLOC_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBBPF_CFLAGS = @LIBBPF_CFLAGS@ LIBBPF_LIBS = @LIBBPF_LIBS@ LIBBROTLIDEC_CFLAGS = @LIBBROTLIDEC_CFLAGS@ LIBBROTLIDEC_LIBS = @LIBBROTLIDEC_LIBS@ LIBBROTLIENC_CFLAGS = @LIBBROTLIENC_CFLAGS@ LIBBROTLIENC_LIBS = @LIBBROTLIENC_LIBS@ LIBCARES_CFLAGS = @LIBCARES_CFLAGS@ LIBCARES_LIBS = @LIBCARES_LIBS@ LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ LIBEV_CFLAGS = @LIBEV_CFLAGS@ LIBEV_LIBS = @LIBEV_LIBS@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@ LIBMRUBY_LIBS = @LIBMRUBY_LIBS@ LIBNGHTTP3_CFLAGS = @LIBNGHTTP3_CFLAGS@ LIBNGHTTP3_LIBS = @LIBNGHTTP3_LIBS@ LIBNGTCP2_CFLAGS = @LIBNGTCP2_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS = @LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_LIBS = @LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS = @LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS@ LIBNGTCP2_CRYPTO_LIBRESSL_LIBS = @LIBNGTCP2_CRYPTO_LIBRESSL_LIBS@ LIBNGTCP2_CRYPTO_OSSL_CFLAGS = @LIBNGTCP2_CRYPTO_OSSL_CFLAGS@ LIBNGTCP2_CRYPTO_OSSL_LIBS = @LIBNGTCP2_CRYPTO_OSSL_LIBS@ LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS = @LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ LIBNGTCP2_CRYPTO_QUICTLS_LIBS = @LIBNGTCP2_CRYPTO_QUICTLS_LIBS@ LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS = @LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS@ LIBNGTCP2_CRYPTO_WOLFSSL_LIBS = @LIBNGTCP2_CRYPTO_WOLFSSL_LIBS@ LIBNGTCP2_LIBS = @LIBNGTCP2_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBTOOL_LDFLAGS = @LIBTOOL_LDFLAGS@ LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ LIBXML2_LIBS = @LIBXML2_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_AGE = @LT_AGE@ LT_CURRENT = @LT_CURRENT@ LT_REVISION = @LT_REVISION@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ SYSTEMD_LIBS = @SYSTEMD_LIBS@ TESTLDADD = @TESTLDADD@ VERSION = @VERSION@ WARNCFLAGS = @WARNCFLAGS@ WARNCXXFLAGS = @WARNCXXFLAGS@ WOLFSSL_CFLAGS = @WOLFSSL_CFLAGS@ WOLFSSL_LIBS = @WOLFSSL_LIBS@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SUBDIRS = includes EXTRA_DIST = Makefile.msvc CMakeLists.txt version.rc.in config.cmake.in AM_CFLAGS = $(WARNCFLAGS) $(EXTRACFLAG) AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes -DBUILDING_NGHTTP2 \ @DEFS@ AM_LDFLAGS = @LIBTOOL_LDFLAGS@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libnghttp2.pc DISTCLEANFILES = $(pkgconfig_DATA) lib_LTLIBRARIES = libnghttp2.la OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \ nghttp2_frame.c \ nghttp2_buf.c \ nghttp2_stream.c nghttp2_outbound_item.c \ nghttp2_session.c nghttp2_submit.c \ nghttp2_helper.c \ nghttp2_alpn.c \ nghttp2_hd.c nghttp2_hd_huffman.c nghttp2_hd_huffman_data.c \ nghttp2_version.c \ nghttp2_priority_spec.c \ nghttp2_option.c \ nghttp2_callbacks.c \ nghttp2_mem.c \ nghttp2_http.c \ nghttp2_rcbuf.c \ nghttp2_extpri.c \ nghttp2_ratelim.c \ nghttp2_time.c \ nghttp2_debug.c \ sfparse.c HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ nghttp2_frame.h \ nghttp2_buf.h \ nghttp2_session.h nghttp2_helper.h nghttp2_stream.h nghttp2_int.h \ nghttp2_alpn.h \ nghttp2_submit.h nghttp2_outbound_item.h \ nghttp2_net.h \ nghttp2_hd.h nghttp2_hd_huffman.h \ nghttp2_priority_spec.h \ nghttp2_option.h \ nghttp2_callbacks.h \ nghttp2_mem.h \ nghttp2_http.h \ nghttp2_rcbuf.h \ nghttp2_extpri.h \ nghttp2_ratelim.h \ nghttp2_time.h \ nghttp2_debug.h \ sfparse.h libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS) libnghttp2_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) all: all-recursive .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lib/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu lib/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): libnghttp2.pc: $(top_builddir)/config.status $(srcdir)/libnghttp2.pc.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libnghttp2.la: $(libnghttp2_la_OBJECTS) $(libnghttp2_la_DEPENDENCIES) $(EXTRA_libnghttp2_la_DEPENDENCIES) $(AM_V_CCLD)$(libnghttp2_la_LINK) -rpath $(libdir) $(libnghttp2_la_OBJECTS) $(libnghttp2_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_alpn.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_buf.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_callbacks.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_debug.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_extpri.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_frame.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_huffman.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_huffman_data.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_helper.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_http.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_map.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_mem.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_option.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_outbound_item.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_pq.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_priority_spec.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_queue.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_ratelim.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_rcbuf.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_session.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_stream.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_submit.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_time.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_version.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sfparse.Plo@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkgconfigDATA: $(pkgconfig_DATA) @$(NORMAL_INSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ done uninstall-pkgconfigDATA: @$(NORMAL_UNINSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile $(LTLIBRARIES) $(DATA) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ mostlyclean-am distclean: distclean-recursive -rm -f ./$(DEPDIR)/nghttp2_alpn.Plo -rm -f ./$(DEPDIR)/nghttp2_buf.Plo -rm -f ./$(DEPDIR)/nghttp2_callbacks.Plo -rm -f ./$(DEPDIR)/nghttp2_debug.Plo -rm -f ./$(DEPDIR)/nghttp2_extpri.Plo -rm -f ./$(DEPDIR)/nghttp2_frame.Plo -rm -f ./$(DEPDIR)/nghttp2_hd.Plo -rm -f ./$(DEPDIR)/nghttp2_hd_huffman.Plo -rm -f ./$(DEPDIR)/nghttp2_hd_huffman_data.Plo -rm -f ./$(DEPDIR)/nghttp2_helper.Plo -rm -f ./$(DEPDIR)/nghttp2_http.Plo -rm -f ./$(DEPDIR)/nghttp2_map.Plo -rm -f ./$(DEPDIR)/nghttp2_mem.Plo -rm -f ./$(DEPDIR)/nghttp2_option.Plo -rm -f ./$(DEPDIR)/nghttp2_outbound_item.Plo -rm -f ./$(DEPDIR)/nghttp2_pq.Plo -rm -f ./$(DEPDIR)/nghttp2_priority_spec.Plo -rm -f ./$(DEPDIR)/nghttp2_queue.Plo -rm -f ./$(DEPDIR)/nghttp2_ratelim.Plo -rm -f ./$(DEPDIR)/nghttp2_rcbuf.Plo -rm -f ./$(DEPDIR)/nghttp2_session.Plo -rm -f ./$(DEPDIR)/nghttp2_stream.Plo -rm -f ./$(DEPDIR)/nghttp2_submit.Plo -rm -f ./$(DEPDIR)/nghttp2_time.Plo -rm -f ./$(DEPDIR)/nghttp2_version.Plo -rm -f ./$(DEPDIR)/sfparse.Plo -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-pkgconfigDATA install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-libLTLIBRARIES install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f ./$(DEPDIR)/nghttp2_alpn.Plo -rm -f ./$(DEPDIR)/nghttp2_buf.Plo -rm -f ./$(DEPDIR)/nghttp2_callbacks.Plo -rm -f ./$(DEPDIR)/nghttp2_debug.Plo -rm -f ./$(DEPDIR)/nghttp2_extpri.Plo -rm -f ./$(DEPDIR)/nghttp2_frame.Plo -rm -f ./$(DEPDIR)/nghttp2_hd.Plo -rm -f ./$(DEPDIR)/nghttp2_hd_huffman.Plo -rm -f ./$(DEPDIR)/nghttp2_hd_huffman_data.Plo -rm -f ./$(DEPDIR)/nghttp2_helper.Plo -rm -f ./$(DEPDIR)/nghttp2_http.Plo -rm -f ./$(DEPDIR)/nghttp2_map.Plo -rm -f ./$(DEPDIR)/nghttp2_mem.Plo -rm -f ./$(DEPDIR)/nghttp2_option.Plo -rm -f ./$(DEPDIR)/nghttp2_outbound_item.Plo -rm -f ./$(DEPDIR)/nghttp2_pq.Plo -rm -f ./$(DEPDIR)/nghttp2_priority_spec.Plo -rm -f ./$(DEPDIR)/nghttp2_queue.Plo -rm -f ./$(DEPDIR)/nghttp2_ratelim.Plo -rm -f ./$(DEPDIR)/nghttp2_rcbuf.Plo -rm -f ./$(DEPDIR)/nghttp2_session.Plo -rm -f ./$(DEPDIR)/nghttp2_stream.Plo -rm -f ./$(DEPDIR)/nghttp2_submit.Plo -rm -f ./$(DEPDIR)/nghttp2_time.Plo -rm -f ./$(DEPDIR)/nghttp2_version.Plo -rm -f ./$(DEPDIR)/sfparse.Plo -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-libLTLIBRARIES uninstall-pkgconfigDATA .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ am--depfiles check check-am clean clean-generic \ clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-libLTLIBRARIES install-man install-pdf \ install-pdf-am install-pkgconfigDATA install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-libLTLIBRARIES uninstall-pkgconfigDATA .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nghttp2-1.68.0/lib/PaxHeaders/nghttp2_map.h0000644000000000000000000000013215077107270015422 xustar0030 mtime=1761382072.979444204 30 atime=1761382104.933316026 30 ctime=1761382107.919303795 nghttp2-1.68.0/lib/nghttp2_map.h0000644000175100017510000001002215077107270016005 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2017 ngtcp2 contributors * Copyright (c) 2012 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_MAP_H #define NGHTTP2_MAP_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include #include "nghttp2_mem.h" /* Implementation of unordered map */ typedef int32_t nghttp2_map_key_type; typedef struct nghttp2_map_bucket { uint32_t psl; nghttp2_map_key_type key; void *data; } nghttp2_map_bucket; typedef struct nghttp2_map { nghttp2_map_bucket *table; nghttp2_mem *mem; uint32_t seed; size_t size; size_t hashbits; } nghttp2_map; /* * nghttp2_map_init initializes the map |map|. */ void nghttp2_map_init(nghttp2_map *map, uint32_t seed, nghttp2_mem *mem); /* * nghttp2_map_free deallocates any resources allocated for |map|. * The stored entries are not freed by this function. Use * nghttp2_map_each() to free each entry. */ void nghttp2_map_free(nghttp2_map *map); /* * nghttp2_map_insert inserts the new |data| with the |key| to the map * |map|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_INVALID_ARGUMENT * The item associated by |key| already exists. * NGHTTP2_ERR_NOMEM * Out of memory */ int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data); /* * nghttp2_map_find returns the entry associated by the key |key|. If * there is no such entry, this function returns NULL. */ void *nghttp2_map_find(const nghttp2_map *map, nghttp2_map_key_type key); /* * nghttp2_map_remove removes the entry associated by the key |key| * from the |map|. The removed entry is not freed by this function. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_INVALID_ARGUMENT * The entry associated by |key| does not exist. */ int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key); /* * nghttp2_map_clear removes all entries from |map|. The removed * entry is not freed by this function. */ void nghttp2_map_clear(nghttp2_map *map); /* * nghttp2_map_size returns the number of items stored in the map * |map|. */ size_t nghttp2_map_size(const nghttp2_map *map); /* * nghttp2_map_each applies the function |func| to each entry in the * |map| with the optional user supplied pointer |ptr|. * * If the |func| returns 0, this function calls the |func| with the * next entry. If the |func| returns nonzero, it will not call the * |func| for further entries and return the return value of the * |func| immediately. Thus, this function returns 0 if all the * invocations of the |func| return 0, or nonzero value which the last * invocation of |func| returns. */ int nghttp2_map_each(const nghttp2_map *map, int (*func)(void *data, void *ptr), void *ptr); #ifndef WIN32 void nghttp2_map_print_distance(const nghttp2_map *map); #endif /* !defined(WIN32) */ #endif /* !defined(NGHTTP2_MAP_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_queue.h0000644000000000000000000000013215077107270015771 xustar0030 mtime=1761382072.980444199 30 atime=1761382104.934316022 30 ctime=1761382107.920303792 nghttp2-1.68.0/lib/nghttp2_queue.h0000644000175100017510000000353015077107270016362 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_QUEUE_H #define NGHTTP2_QUEUE_H #ifdef HAVE_CONFIG_H # include "config.h" #endif /* defined(HAVE_CONFIG_H) */ #include typedef struct nghttp2_queue_cell { void *data; struct nghttp2_queue_cell *next; } nghttp2_queue_cell; typedef struct { nghttp2_queue_cell *front, *back; } nghttp2_queue; void nghttp2_queue_init(nghttp2_queue *queue); void nghttp2_queue_free(nghttp2_queue *queue); int nghttp2_queue_push(nghttp2_queue *queue, void *data); void nghttp2_queue_pop(nghttp2_queue *queue); void *nghttp2_queue_front(nghttp2_queue *queue); void *nghttp2_queue_back(nghttp2_queue *queue); int nghttp2_queue_empty(nghttp2_queue *queue); #endif /* !defined(NGHTTP2_QUEUE_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_int.h0000644000000000000000000000013215077107270015437 xustar0030 mtime=1761382072.979444204 30 atime=1761382104.931316035 30 ctime=1761382107.918303798 nghttp2-1.68.0/lib/nghttp2_int.h0000644000175100017510000000430315077107270016027 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_INT_H #define NGHTTP2_INT_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include /* Macros, types and constants for internal use */ /* "less" function, return nonzero if |lhs| is less than |rhs|. */ typedef int (*nghttp2_less)(const void *lhs, const void *rhs); /* Internal error code. They must be in the range [-499, -100], inclusive. */ typedef enum { NGHTTP2_ERR_CREDENTIAL_PENDING = -101, NGHTTP2_ERR_IGN_HEADER_BLOCK = -103, NGHTTP2_ERR_IGN_PAYLOAD = -104, /* * Invalid HTTP header field was received but it can be treated as * if it was not received because of compatibility reasons. */ NGHTTP2_ERR_IGN_HTTP_HEADER = -105, /* * Invalid HTTP header field was received, and it is ignored. * Unlike NGHTTP2_ERR_IGN_HTTP_HEADER, this does not invoke * nghttp2_on_invalid_header_callback. */ NGHTTP2_ERR_REMOVE_HTTP_HEADER = -106, /* * Cancel pushed stream. */ NGHTTP2_ERR_PUSH_CANCEL = -107, } nghttp2_internal_error; #endif /* !defined(NGHTTP2_INT_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_extpri.c0000644000000000000000000000013215077107270016153 xustar0030 mtime=1761382072.977444213 30 atime=1761382104.990315775 30 ctime=1761382107.977303627 nghttp2-1.68.0/lib/nghttp2_extpri.c0000644000175100017510000000335215077107270016546 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2022 nghttp3 contributors * Copyright (c) 2022 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_extpri.h" #include "nghttp2_http.h" uint8_t nghttp2_extpri_to_uint8(const nghttp2_extpri *extpri) { return (uint8_t)((uint32_t)extpri->inc << 7 | extpri->urgency); } void nghttp2_extpri_from_uint8(nghttp2_extpri *extpri, uint8_t u8extpri) { extpri->urgency = nghttp2_extpri_uint8_urgency(u8extpri); extpri->inc = nghttp2_extpri_uint8_inc(u8extpri); } int nghttp2_extpri_parse_priority(nghttp2_extpri *extpri, const uint8_t *value, size_t len) { return nghttp2_http_parse_priority(extpri, value, len); } nghttp2-1.68.0/lib/PaxHeaders/nghttp2_version.c0000644000000000000000000000013015077107270016323 xustar0029 mtime=1761382072.98244419 29 atime=1761382104.98231581 30 ctime=1761382107.968303653 nghttp2-1.68.0/lib/nghttp2_version.c0000644000175100017510000000302515077107270016715 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include static nghttp2_info version = {NGHTTP2_VERSION_AGE, NGHTTP2_VERSION_NUM, NGHTTP2_VERSION, NGHTTP2_PROTO_VERSION_ID}; nghttp2_info *nghttp2_version(int least_version) { if (least_version > NGHTTP2_VERSION_NUM) return NULL; return &version; } nghttp2-1.68.0/lib/PaxHeaders/config.cmake.in0000644000000000000000000000013215077107270015702 xustar0030 mtime=1761382072.975444222 30 atime=1761382105.001315727 30 ctime=1761382107.987303599 nghttp2-1.68.0/lib/config.cmake.in0000644000175100017510000000015415077107270016272 0ustar00runnerrunnerinclude(CMakeFindDependencyMacro) include("${CMAKE_CURRENT_LIST_DIR}/@NGHTTP2_TARGETS_EXPORT_NAME@.cmake") nghttp2-1.68.0/lib/PaxHeaders/nghttp2_extpri.h0000644000000000000000000000013115077107270016157 xustar0030 mtime=1761382072.977444213 29 atime=1761382104.95731592 30 ctime=1761382107.943303726 nghttp2-1.68.0/lib/nghttp2_extpri.h0000644000175100017510000000451215077107270016552 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2022 nghttp3 contributors * Copyright (c) 2022 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_EXTPRI_H #define NGHTTP2_EXTPRI_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include /* * NGHTTP2_EXTPRI_INC_MASK is a bit mask to retrieve incremental bit * from a value produced by nghttp2_extpri_to_uint8. */ #define NGHTTP2_EXTPRI_INC_MASK (1 << 7) /* * nghttp2_extpri_to_uint8 encodes |pri| into uint8_t variable. */ uint8_t nghttp2_extpri_to_uint8(const nghttp2_extpri *extpri); /* * nghttp2_extpri_from_uint8 decodes |u8extpri|, which is produced by * nghttp2_extpri_to_uint8, intto |extpri|. */ void nghttp2_extpri_from_uint8(nghttp2_extpri *extpri, uint8_t u8extpri); /* * nghttp2_extpri_uint8_urgency extracts urgency from |PRI| which is * supposed to be constructed by nghttp2_extpri_to_uint8. */ #define nghttp2_extpri_uint8_urgency(PRI) \ ((uint32_t)((PRI) & ~NGHTTP2_EXTPRI_INC_MASK)) /* * nghttp2_extpri_uint8_inc extracts inc from |PRI| which is supposed to * be constructed by nghttp2_extpri_to_uint8. */ #define nghttp2_extpri_uint8_inc(PRI) (((PRI) & NGHTTP2_EXTPRI_INC_MASK) != 0) #endif /* !defined(NGHTTP2_EXTPRI_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_priority_spec.c0000644000000000000000000000013115077107270017532 xustar0030 mtime=1761382072.980444199 30 atime=1761382104.983315806 29 ctime=1761382107.96930365 nghttp2-1.68.0/lib/nghttp2_priority_spec.c0000644000175100017510000000412715077107270020127 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_priority_spec.h" void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, int32_t stream_id, int32_t weight, int exclusive) { pri_spec->stream_id = stream_id; pri_spec->weight = weight; pri_spec->exclusive = exclusive != 0; } void nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec) { pri_spec->stream_id = 0; pri_spec->weight = NGHTTP2_DEFAULT_WEIGHT; pri_spec->exclusive = 0; } int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec) { return pri_spec->stream_id == 0 && pri_spec->weight == NGHTTP2_DEFAULT_WEIGHT && pri_spec->exclusive == 0; } void nghttp2_priority_spec_normalize_weight(nghttp2_priority_spec *pri_spec) { if (pri_spec->weight < NGHTTP2_MIN_WEIGHT) { pri_spec->weight = NGHTTP2_MIN_WEIGHT; } else if (pri_spec->weight > NGHTTP2_MAX_WEIGHT) { pri_spec->weight = NGHTTP2_MAX_WEIGHT; } } nghttp2-1.68.0/lib/PaxHeaders/Makefile.am0000644000000000000000000000013215077107270015062 xustar0030 mtime=1761382072.974444227 30 atime=1761382085.569387157 30 ctime=1761382107.911303818 nghttp2-1.68.0/lib/Makefile.am0000644000175100017510000000517515077107270015462 0ustar00runnerrunner# nghttp2 - HTTP/2 C Library # Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SUBDIRS = includes EXTRA_DIST = Makefile.msvc CMakeLists.txt version.rc.in config.cmake.in AM_CFLAGS = $(WARNCFLAGS) $(EXTRACFLAG) AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes -DBUILDING_NGHTTP2 \ @DEFS@ AM_LDFLAGS = @LIBTOOL_LDFLAGS@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libnghttp2.pc DISTCLEANFILES = $(pkgconfig_DATA) lib_LTLIBRARIES = libnghttp2.la OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \ nghttp2_frame.c \ nghttp2_buf.c \ nghttp2_stream.c nghttp2_outbound_item.c \ nghttp2_session.c nghttp2_submit.c \ nghttp2_helper.c \ nghttp2_alpn.c \ nghttp2_hd.c nghttp2_hd_huffman.c nghttp2_hd_huffman_data.c \ nghttp2_version.c \ nghttp2_priority_spec.c \ nghttp2_option.c \ nghttp2_callbacks.c \ nghttp2_mem.c \ nghttp2_http.c \ nghttp2_rcbuf.c \ nghttp2_extpri.c \ nghttp2_ratelim.c \ nghttp2_time.c \ nghttp2_debug.c \ sfparse.c HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ nghttp2_frame.h \ nghttp2_buf.h \ nghttp2_session.h nghttp2_helper.h nghttp2_stream.h nghttp2_int.h \ nghttp2_alpn.h \ nghttp2_submit.h nghttp2_outbound_item.h \ nghttp2_net.h \ nghttp2_hd.h nghttp2_hd_huffman.h \ nghttp2_priority_spec.h \ nghttp2_option.h \ nghttp2_callbacks.h \ nghttp2_mem.h \ nghttp2_http.h \ nghttp2_rcbuf.h \ nghttp2_extpri.h \ nghttp2_ratelim.h \ nghttp2_time.h \ nghttp2_debug.h \ sfparse.h libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS) libnghttp2_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) nghttp2-1.68.0/lib/PaxHeaders/nghttp2_callbacks.c0000644000000000000000000000013215077107270016557 xustar0030 mtime=1761382072.977444213 30 atime=1761382104.985315797 30 ctime=1761382107.972303642 nghttp2-1.68.0/lib/nghttp2_callbacks.c0000644000175100017510000001673215077107270017160 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_callbacks.h" #include int nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr) { *callbacks_ptr = calloc(1, sizeof(nghttp2_session_callbacks)); if (*callbacks_ptr == NULL) { return NGHTTP2_ERR_NOMEM; } return 0; } void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks) { free(callbacks); } void nghttp2_session_callbacks_set_send_callback( nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback) { cbs->send_callback = send_callback; } void nghttp2_session_callbacks_set_send_callback2( nghttp2_session_callbacks *cbs, nghttp2_send_callback2 send_callback) { cbs->send_callback2 = send_callback; } void nghttp2_session_callbacks_set_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback) { cbs->recv_callback = recv_callback; } void nghttp2_session_callbacks_set_recv_callback2( nghttp2_session_callbacks *cbs, nghttp2_recv_callback2 recv_callback) { cbs->recv_callback2 = recv_callback; } void nghttp2_session_callbacks_set_on_frame_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_frame_recv_callback on_frame_recv_callback) { cbs->on_frame_recv_callback = on_frame_recv_callback; } void nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback) { cbs->on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; } void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback) { cbs->on_data_chunk_recv_callback = on_data_chunk_recv_callback; } void nghttp2_session_callbacks_set_before_frame_send_callback( nghttp2_session_callbacks *cbs, nghttp2_before_frame_send_callback before_frame_send_callback) { cbs->before_frame_send_callback = before_frame_send_callback; } void nghttp2_session_callbacks_set_on_frame_send_callback( nghttp2_session_callbacks *cbs, nghttp2_on_frame_send_callback on_frame_send_callback) { cbs->on_frame_send_callback = on_frame_send_callback; } void nghttp2_session_callbacks_set_on_frame_not_send_callback( nghttp2_session_callbacks *cbs, nghttp2_on_frame_not_send_callback on_frame_not_send_callback) { cbs->on_frame_not_send_callback = on_frame_not_send_callback; } void nghttp2_session_callbacks_set_on_stream_close_callback( nghttp2_session_callbacks *cbs, nghttp2_on_stream_close_callback on_stream_close_callback) { cbs->on_stream_close_callback = on_stream_close_callback; } void nghttp2_session_callbacks_set_on_begin_headers_callback( nghttp2_session_callbacks *cbs, nghttp2_on_begin_headers_callback on_begin_headers_callback) { cbs->on_begin_headers_callback = on_begin_headers_callback; } void nghttp2_session_callbacks_set_on_header_callback( nghttp2_session_callbacks *cbs, nghttp2_on_header_callback on_header_callback) { cbs->on_header_callback = on_header_callback; } void nghttp2_session_callbacks_set_on_header_callback2( nghttp2_session_callbacks *cbs, nghttp2_on_header_callback2 on_header_callback2) { cbs->on_header_callback2 = on_header_callback2; } void nghttp2_session_callbacks_set_on_invalid_header_callback( nghttp2_session_callbacks *cbs, nghttp2_on_invalid_header_callback on_invalid_header_callback) { cbs->on_invalid_header_callback = on_invalid_header_callback; } void nghttp2_session_callbacks_set_on_invalid_header_callback2( nghttp2_session_callbacks *cbs, nghttp2_on_invalid_header_callback2 on_invalid_header_callback2) { cbs->on_invalid_header_callback2 = on_invalid_header_callback2; } void nghttp2_session_callbacks_set_select_padding_callback( nghttp2_session_callbacks *cbs, nghttp2_select_padding_callback select_padding_callback) { cbs->select_padding_callback = select_padding_callback; } void nghttp2_session_callbacks_set_select_padding_callback2( nghttp2_session_callbacks *cbs, nghttp2_select_padding_callback2 select_padding_callback) { cbs->select_padding_callback2 = select_padding_callback; } void nghttp2_session_callbacks_set_data_source_read_length_callback( nghttp2_session_callbacks *cbs, nghttp2_data_source_read_length_callback data_source_read_length_callback) { cbs->read_length_callback = data_source_read_length_callback; } void nghttp2_session_callbacks_set_data_source_read_length_callback2( nghttp2_session_callbacks *cbs, nghttp2_data_source_read_length_callback2 data_source_read_length_callback) { cbs->read_length_callback2 = data_source_read_length_callback; } void nghttp2_session_callbacks_set_on_begin_frame_callback( nghttp2_session_callbacks *cbs, nghttp2_on_begin_frame_callback on_begin_frame_callback) { cbs->on_begin_frame_callback = on_begin_frame_callback; } void nghttp2_session_callbacks_set_send_data_callback( nghttp2_session_callbacks *cbs, nghttp2_send_data_callback send_data_callback) { cbs->send_data_callback = send_data_callback; } void nghttp2_session_callbacks_set_pack_extension_callback( nghttp2_session_callbacks *cbs, nghttp2_pack_extension_callback pack_extension_callback) { cbs->pack_extension_callback = pack_extension_callback; } void nghttp2_session_callbacks_set_pack_extension_callback2( nghttp2_session_callbacks *cbs, nghttp2_pack_extension_callback2 pack_extension_callback) { cbs->pack_extension_callback2 = pack_extension_callback; } void nghttp2_session_callbacks_set_unpack_extension_callback( nghttp2_session_callbacks *cbs, nghttp2_unpack_extension_callback unpack_extension_callback) { cbs->unpack_extension_callback = unpack_extension_callback; } void nghttp2_session_callbacks_set_on_extension_chunk_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback) { cbs->on_extension_chunk_recv_callback = on_extension_chunk_recv_callback; } void nghttp2_session_callbacks_set_error_callback( nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback) { cbs->error_callback = error_callback; } void nghttp2_session_callbacks_set_error_callback2( nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2) { cbs->error_callback2 = error_callback2; } void nghttp2_session_callbacks_set_rand_callback( nghttp2_session_callbacks *cbs, nghttp2_rand_callback rand_callback) { cbs->rand_callback = rand_callback; } nghttp2-1.68.0/lib/PaxHeaders/nghttp2_debug.c0000644000000000000000000000013215077107270015726 xustar0030 mtime=1761382072.977444213 30 atime=1761382104.994315757 30 ctime=1761382107.980303619 nghttp2-1.68.0/lib/nghttp2_debug.c0000644000175100017510000000405115077107270016316 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_debug.h" #include #ifdef DEBUGBUILD static void nghttp2_default_debug_vfprintf_callback(const char *fmt, va_list args) { vfprintf(stderr, fmt, args); } static nghttp2_debug_vprintf_callback static_debug_vprintf_callback = nghttp2_default_debug_vfprintf_callback; void nghttp2_debug_vprintf(const char *format, ...) { if (static_debug_vprintf_callback) { va_list args; va_start(args, format); static_debug_vprintf_callback(format, args); va_end(args); } } void nghttp2_set_debug_vprintf_callback( nghttp2_debug_vprintf_callback debug_vprintf_callback) { static_debug_vprintf_callback = debug_vprintf_callback; } #else /* !defined(DEBUGBUILD) */ void nghttp2_set_debug_vprintf_callback( nghttp2_debug_vprintf_callback debug_vprintf_callback) { (void)debug_vprintf_callback; } #endif /* !defined(DEBUGBUILD) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_pq.c0000644000000000000000000000013215077107270015260 xustar0030 mtime=1761382072.979444204 30 atime=1761382104.963315894 30 ctime=1761382107.949303708 nghttp2-1.68.0/lib/nghttp2_pq.c0000644000175100017510000001043415077107270015652 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_pq.h" #include #include #include "nghttp2_helper.h" void nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem) { pq->mem = mem; pq->capacity = 0; pq->q = NULL; pq->length = 0; pq->less = less; } void nghttp2_pq_free(nghttp2_pq *pq) { nghttp2_mem_free(pq->mem, pq->q); pq->q = NULL; } static void swap(nghttp2_pq *pq, size_t i, size_t j) { nghttp2_pq_entry *a = pq->q[i]; nghttp2_pq_entry *b = pq->q[j]; pq->q[i] = b; b->index = i; pq->q[j] = a; a->index = j; } static void bubble_up(nghttp2_pq *pq, size_t index) { size_t parent; while (index != 0) { parent = (index - 1) / 2; if (!pq->less(pq->q[index], pq->q[parent])) { return; } swap(pq, parent, index); index = parent; } } int nghttp2_pq_push(nghttp2_pq *pq, nghttp2_pq_entry *item) { if (pq->capacity <= pq->length) { void *nq; size_t ncapacity; ncapacity = nghttp2_max_size(4, (pq->capacity * 2)); nq = nghttp2_mem_realloc(pq->mem, pq->q, ncapacity * sizeof(nghttp2_pq_entry *)); if (nq == NULL) { return NGHTTP2_ERR_NOMEM; } pq->capacity = ncapacity; pq->q = nq; } pq->q[pq->length] = item; item->index = pq->length; ++pq->length; bubble_up(pq, pq->length - 1); return 0; } nghttp2_pq_entry *nghttp2_pq_top(nghttp2_pq *pq) { if (pq->length == 0) { return NULL; } else { return pq->q[0]; } } static void bubble_down(nghttp2_pq *pq, size_t index) { size_t i, j, minindex; for (;;) { j = index * 2 + 1; minindex = index; for (i = 0; i < 2; ++i, ++j) { if (j >= pq->length) { break; } if (pq->less(pq->q[j], pq->q[minindex])) { minindex = j; } } if (minindex == index) { return; } swap(pq, index, minindex); index = minindex; } } void nghttp2_pq_pop(nghttp2_pq *pq) { if (pq->length > 0) { pq->q[0] = pq->q[pq->length - 1]; pq->q[0]->index = 0; --pq->length; bubble_down(pq, 0); } } void nghttp2_pq_remove(nghttp2_pq *pq, nghttp2_pq_entry *item) { assert(pq->q[item->index] == item); if (item->index == 0) { nghttp2_pq_pop(pq); return; } if (item->index == pq->length - 1) { --pq->length; return; } pq->q[item->index] = pq->q[pq->length - 1]; pq->q[item->index]->index = item->index; --pq->length; if (pq->less(item, pq->q[item->index])) { bubble_down(pq, item->index); } else { bubble_up(pq, item->index); } } int nghttp2_pq_empty(nghttp2_pq *pq) { return pq->length == 0; } size_t nghttp2_pq_size(nghttp2_pq *pq) { return pq->length; } void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg) { size_t i; int rv = 0; if (pq->length == 0) { return; } for (i = 0; i < pq->length; ++i) { rv |= (*fun)(pq->q[i], arg); } if (rv) { for (i = pq->length; i > 0; --i) { bubble_down(pq, i - 1); } } } int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg) { size_t i; if (pq->length == 0) { return 0; } for (i = 0; i < pq->length; ++i) { if ((*fun)(pq->q[i], arg)) { return 1; } } return 0; } nghttp2-1.68.0/lib/PaxHeaders/nghttp2_mem.c0000644000000000000000000000013215077107270015416 xustar0030 mtime=1761382072.979444204 30 atime=1761382104.987315788 30 ctime=1761382107.973303639 nghttp2-1.68.0/lib/nghttp2_mem.c0000644000175100017510000000461615077107270016015 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_mem.h" static void *default_malloc(size_t size, void *mem_user_data) { (void)mem_user_data; return malloc(size); } static void default_free(void *ptr, void *mem_user_data) { (void)mem_user_data; free(ptr); } static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) { (void)mem_user_data; return calloc(nmemb, size); } static void *default_realloc(void *ptr, size_t size, void *mem_user_data) { (void)mem_user_data; return realloc(ptr, size); } static nghttp2_mem mem_default = {NULL, default_malloc, default_free, default_calloc, default_realloc}; nghttp2_mem *nghttp2_mem_default(void) { return &mem_default; } void *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size) { return mem->malloc(size, mem->mem_user_data); } void nghttp2_mem_free(nghttp2_mem *mem, void *ptr) { mem->free(ptr, mem->mem_user_data); } void nghttp2_mem_free2(nghttp2_free free_func, void *ptr, void *mem_user_data) { free_func(ptr, mem_user_data); } void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size) { return mem->calloc(nmemb, size, mem->mem_user_data); } void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size) { return mem->realloc(ptr, size, mem->mem_user_data); } nghttp2-1.68.0/lib/PaxHeaders/nghttp2_ratelim.c0000644000000000000000000000013215077107270016275 xustar0030 mtime=1761382072.980444199 30 atime=1761382104.992315766 30 ctime=1761382107.978303624 nghttp2-1.68.0/lib/nghttp2_ratelim.c0000644000175100017510000000372415077107270016673 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2023 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_ratelim.h" #include "nghttp2_helper.h" void nghttp2_ratelim_init(nghttp2_ratelim *rl, uint64_t burst, uint64_t rate) { rl->val = rl->burst = burst; rl->rate = rate; rl->tstamp = 0; } void nghttp2_ratelim_update(nghttp2_ratelim *rl, uint64_t tstamp) { uint64_t d, gain; if (tstamp == rl->tstamp) { return; } if (tstamp > rl->tstamp) { d = tstamp - rl->tstamp; } else { d = 1; } rl->tstamp = tstamp; if (UINT64_MAX / d < rl->rate) { rl->val = rl->burst; return; } gain = rl->rate * d; if (UINT64_MAX - gain < rl->val) { rl->val = rl->burst; return; } rl->val += gain; rl->val = nghttp2_min_uint64(rl->val, rl->burst); } int nghttp2_ratelim_drain(nghttp2_ratelim *rl, uint64_t n) { if (rl->val < n) { return -1; } rl->val -= n; return 0; } nghttp2-1.68.0/lib/PaxHeaders/nghttp2_outbound_item.c0000644000000000000000000000013215077107270017515 xustar0030 mtime=1761382072.979444204 30 atime=1761382104.971315859 30 ctime=1761382107.957303685 nghttp2-1.68.0/lib/nghttp2_outbound_item.c0000644000175100017510000001011315077107270020101 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_outbound_item.h" #include #include nghttp2_data_provider_wrap * nghttp2_data_provider_wrap_v1(nghttp2_data_provider_wrap *dpw, const nghttp2_data_provider *data_prd) { if (!data_prd) { return NULL; } dpw->version = NGHTTP2_DATA_PROVIDER_V1; dpw->data_prd.v1 = *data_prd; return dpw; } nghttp2_data_provider_wrap * nghttp2_data_provider_wrap_v2(nghttp2_data_provider_wrap *dpw, const nghttp2_data_provider2 *data_prd) { if (!data_prd) { return NULL; } dpw->version = NGHTTP2_DATA_PROVIDER_V2; dpw->data_prd.v2 = *data_prd; return dpw; } void nghttp2_outbound_item_init(nghttp2_outbound_item *item) { item->cycle = 0; item->qnext = NULL; item->queued = 0; memset(&item->aux_data, 0, sizeof(nghttp2_aux_data)); } void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) { nghttp2_frame *frame; if (item == NULL) { return; } frame = &item->frame; switch (frame->hd.type) { case NGHTTP2_DATA: nghttp2_frame_data_free(&frame->data); break; case NGHTTP2_HEADERS: nghttp2_frame_headers_free(&frame->headers, mem); break; case NGHTTP2_PRIORITY: nghttp2_frame_priority_free(&frame->priority); break; case NGHTTP2_RST_STREAM: nghttp2_frame_rst_stream_free(&frame->rst_stream); break; case NGHTTP2_SETTINGS: nghttp2_frame_settings_free(&frame->settings, mem); break; case NGHTTP2_PUSH_PROMISE: nghttp2_frame_push_promise_free(&frame->push_promise, mem); break; case NGHTTP2_PING: nghttp2_frame_ping_free(&frame->ping); break; case NGHTTP2_GOAWAY: nghttp2_frame_goaway_free(&frame->goaway, mem); break; case NGHTTP2_WINDOW_UPDATE: nghttp2_frame_window_update_free(&frame->window_update); break; default: { nghttp2_ext_aux_data *aux_data; aux_data = &item->aux_data.ext; if (aux_data->builtin == 0) { nghttp2_frame_extension_free(&frame->ext); break; } switch (frame->hd.type) { case NGHTTP2_ALTSVC: nghttp2_frame_altsvc_free(&frame->ext, mem); break; case NGHTTP2_ORIGIN: nghttp2_frame_origin_free(&frame->ext, mem); break; case NGHTTP2_PRIORITY_UPDATE: nghttp2_frame_priority_update_free(&frame->ext, mem); break; default: assert(0); break; } } } } void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q) { q->head = q->tail = NULL; q->n = 0; } void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q, nghttp2_outbound_item *item) { if (q->tail) { q->tail = q->tail->qnext = item; } else { q->head = q->tail = item; } ++q->n; } void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q) { nghttp2_outbound_item *item; if (!q->head) { return; } item = q->head; q->head = q->head->qnext; item->qnext = NULL; if (!q->head) { q->tail = NULL; } --q->n; } nghttp2-1.68.0/lib/PaxHeaders/nghttp2_frame.h0000644000000000000000000000013215077107270015737 xustar0030 mtime=1761382072.978444208 30 atime=1761382104.935316017 30 ctime=1761382107.922303786 nghttp2-1.68.0/lib/nghttp2_frame.h0000644000175100017510000005664415077107270016346 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_FRAME_H #define NGHTTP2_FRAME_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include #include "nghttp2_hd.h" #include "nghttp2_buf.h" #define NGHTTP2_STREAM_ID_MASK ((1u << 31) - 1) #define NGHTTP2_PRI_GROUP_ID_MASK ((1u << 31) - 1) #define NGHTTP2_PRIORITY_MASK ((1u << 31) - 1) #define NGHTTP2_WINDOW_SIZE_INCREMENT_MASK ((1u << 31) - 1) #define NGHTTP2_SETTINGS_ID_MASK ((1 << 24) - 1) /* The number of bytes of frame header. */ #define NGHTTP2_FRAME_HDLEN 9 #define NGHTTP2_MAX_FRAME_SIZE_MAX ((1 << 24) - 1) #define NGHTTP2_MAX_FRAME_SIZE_MIN (1 << 14) #define NGHTTP2_MAX_PAYLOADLEN 16384 /* The one frame buffer length for transmission. We may use several of them to support CONTINUATION. To account for Pad Length field, we allocate extra 1 byte, which saves extra large memcopying. */ #define NGHTTP2_FRAMEBUF_CHUNKLEN \ (NGHTTP2_FRAME_HDLEN + 1 + NGHTTP2_MAX_PAYLOADLEN) /* The default length of DATA frame payload. */ #define NGHTTP2_DATA_PAYLOADLEN NGHTTP2_MAX_FRAME_SIZE_MIN /* Maximum headers block size to send, calculated using nghttp2_hd_deflate_bound(). This is the default value, and can be overridden by nghttp2_option_set_max_send_header_block_length(). */ #define NGHTTP2_MAX_HEADERSLEN 65536 /* The number of bytes for each SETTINGS entry */ #define NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH 6 /* Length of priority related fields in HEADERS/PRIORITY frames */ #define NGHTTP2_PRIORITY_SPECLEN 5 /* Maximum length of padding in bytes. */ #define NGHTTP2_MAX_PADLEN 256 /* Union of extension frame payload */ typedef union { nghttp2_ext_altsvc altsvc; nghttp2_ext_origin origin; nghttp2_ext_priority_update priority_update; } nghttp2_ext_frame_payload; void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd); void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t *buf); /** * Initializes frame header |hd| with given parameters. Reserved bit * is set to 0. */ void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type, uint8_t flags, int32_t stream_id); /** * Returns the number of priority field depending on the |flags|. If * |flags| has neither NGHTTP2_FLAG_PRIORITY_GROUP nor * NGHTTP2_FLAG_PRIORITY_DEPENDENCY set, return 0. */ size_t nghttp2_frame_priority_len(uint8_t flags); /** * Packs the |pri_spec| in |buf|. This function assumes |buf| has * enough space for serialization. */ void nghttp2_frame_pack_priority_spec(uint8_t *buf, const nghttp2_priority_spec *pri_spec); /** * Unpacks the priority specification from payload |payload| of length * |payloadlen| to |pri_spec|. The |flags| is used to determine what * kind of priority specification is in |payload|. This function * assumes the |payload| contains whole priority specification. */ void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec, const uint8_t *payload); /* * Returns the offset from the HEADERS frame payload where the * compressed header block starts. The frame payload does not include * frame header. */ size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame); /* * Packs HEADERS frame |frame| in wire format and store it in |bufs|. * This function expands |bufs| as necessary to store frame. * * The caller must make sure that nghttp2_bufs_reset(bufs) is called * before calling this function. * * frame->hd.length is assigned after length is determined during * packing process. CONTINUATION frames are also serialized in this * function. This function does not handle padding. * * This function returns 0 if it succeeds, or returns one of the * following negative error codes: * * NGHTTP2_ERR_HEADER_COMP * The deflate operation failed. * NGHTTP2_ERR_NOMEM * Out of memory. */ int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame, nghttp2_hd_deflater *deflater); /* * Unpacks HEADERS frame byte sequence into |frame|. This function * only unapcks bytes that come before name/value header block and * after possible Pad Length field. */ void nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, const uint8_t *payload); /* * Packs PRIORITY frame |frame| in wire format and store it in * |bufs|. * * The caller must make sure that nghttp2_bufs_reset(bufs) is called * before calling this function. */ void nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame); /* * Unpacks PRIORITY wire format into |frame|. */ void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, const uint8_t *payload); /* * Packs RST_STREAM frame |frame| in wire frame format and store it in * |bufs|. * * The caller must make sure that nghttp2_bufs_reset(bufs) is called * before calling this function. */ void nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs, nghttp2_rst_stream *frame); /* * Unpacks RST_STREAM frame byte sequence into |frame|. */ void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, const uint8_t *payload); /* * Packs SETTINGS frame |frame| in wire format and store it in * |bufs|. * * The caller must make sure that nghttp2_bufs_reset(bufs) is called * before calling this function. * * This function returns 0 if it succeeds, or returns one of the * following negative error codes: * * NGHTTP2_ERR_FRAME_SIZE_ERROR * The length of the frame is too large. */ int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame); /* * Packs the |iv|, which includes |niv| entries, in the |buf|, * assuming the |buf| has at least 8 * |niv| bytes. * * Returns the number of bytes written into the |buf|. */ size_t nghttp2_frame_pack_settings_payload(uint8_t *buf, const nghttp2_settings_entry *iv, size_t niv); void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv, const uint8_t *payload); /* * Initializes payload of frame->settings. The |frame| takes * ownership of |iv|. */ void nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame, nghttp2_settings_entry *iv, size_t niv); /* * Unpacks SETTINGS payload into |*iv_ptr|. The number of entries are * assigned to the |*niv_ptr|. This function allocates enough memory * to store the result in |*iv_ptr|. The caller is responsible to free * |*iv_ptr| after its use. * * This function returns 0 if it succeeds or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. */ int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, size_t *niv_ptr, const uint8_t *payload, size_t payloadlen, nghttp2_mem *mem); /* * Packs PUSH_PROMISE frame |frame| in wire format and store it in * |bufs|. This function expands |bufs| as necessary to store * frame. * * The caller must make sure that nghttp2_bufs_reset(bufs) is called * before calling this function. * * frame->hd.length is assigned after length is determined during * packing process. CONTINUATION frames are also serialized in this * function. This function does not handle padding. * * This function returns 0 if it succeeds, or returns one of the * following negative error codes: * * NGHTTP2_ERR_HEADER_COMP * The deflate operation failed. * NGHTTP2_ERR_NOMEM * Out of memory. */ int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs, nghttp2_push_promise *frame, nghttp2_hd_deflater *deflater); /* * Unpacks PUSH_PROMISE frame byte sequence into |frame|. This * function only unapcks bytes that come before name/value header * block and after possible Pad Length field. */ void nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, const uint8_t *payload); /* * Packs PING frame |frame| in wire format and store it in * |bufs|. * * The caller must make sure that nghttp2_bufs_reset(bufs) is called * before calling this function. */ void nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame); /* * Unpacks PING wire format into |frame|. */ void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, const uint8_t *payload); /* * Packs GOAWAY frame |frame| in wire format and store it in |bufs|. * This function expands |bufs| as necessary to store frame. * * The caller must make sure that nghttp2_bufs_reset(bufs) is called * before calling this function. * * This function returns 0 if it succeeds or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_FRAME_SIZE_ERROR * The length of the frame is too large. */ int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame); /* * Unpacks GOAWAY wire format into |frame|. The |payload| of length * |payloadlen| contains first 8 bytes of payload. The * |var_gift_payload| of length |var_gift_payloadlen| contains * remaining payload and its buffer is gifted to the function and then * |frame|. The |var_gift_payloadlen| must be freed by * nghttp2_frame_goaway_free(). */ void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, const uint8_t *payload, uint8_t *var_gift_payload, size_t var_gift_payloadlen); /* * Unpacks GOAWAY wire format into |frame|. This function only exists * for unit test. After allocating buffer for debug data, this * function internally calls nghttp2_frame_unpack_goaway_payload(). * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. */ int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame, const uint8_t *payload, size_t payloadlen, nghttp2_mem *mem); /* * Packs WINDOW_UPDATE frame |frame| in wire frame format and store it * in |bufs|. * * The caller must make sure that nghttp2_bufs_reset(bufs) is called * before calling this function. */ void nghttp2_frame_pack_window_update(nghttp2_bufs *bufs, nghttp2_window_update *frame); /* * Unpacks WINDOW_UPDATE frame byte sequence into |frame|. */ void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame, const uint8_t *payload); /* * Packs ALTSVC frame |frame| in wire frame format and store it in * |bufs|. * * The caller must make sure that nghttp2_bufs_reset(bufs) is called * before calling this function. */ void nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *ext); /* * Unpacks ALTSVC wire format into |frame|. The |payload| of * |payloadlen| bytes contains frame payload. This function assumes * that frame->payload points to the nghttp2_ext_altsvc object. */ void nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame, size_t origin_len, uint8_t *payload, size_t payloadlen); /* * Unpacks ALTSVC wire format into |frame|. This function only exists * for unit test. After allocating buffer for fields, this function * internally calls nghttp2_frame_unpack_altsvc_payload(). * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_FRAME_SIZE_ERROR * The payload is too small. */ int nghttp2_frame_unpack_altsvc_payload2(nghttp2_extension *frame, const uint8_t *payload, size_t payloadlen, nghttp2_mem *mem); /* * Packs ORIGIN frame |frame| in wire frame format and store it in * |bufs|. * * The caller must make sure that nghttp2_bufs_reset(bufs) is called * before calling this function. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_FRAME_SIZE_ERROR * The length of the frame is too large. */ int nghttp2_frame_pack_origin(nghttp2_bufs *bufs, nghttp2_extension *ext); /* * Unpacks ORIGIN wire format into |frame|. The |payload| of length * |payloadlen| contains the frame payload. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_FRAME_SIZE_ERROR * The payload is too small. */ int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame, const uint8_t *payload, size_t payloadlen, nghttp2_mem *mem); /* * Packs PRIORITY_UPDATE frame |frame| in wire frame format and store * it in |bufs|. * * The caller must make sure that nghttp2_bufs_reset(bufs) is called * before calling this function. */ void nghttp2_frame_pack_priority_update(nghttp2_bufs *bufs, nghttp2_extension *ext); /* * Unpacks PRIORITY_UPDATE wire format into |frame|. The |payload| of * |payloadlen| bytes contains frame payload. This function assumes * that frame->payload points to the nghttp2_ext_priority_update * object. */ void nghttp2_frame_unpack_priority_update_payload(nghttp2_extension *frame, uint8_t *payload, size_t payloadlen); /* * Initializes HEADERS frame |frame| with given values. |frame| takes * ownership of |nva|, so caller must not free it. If |stream_id| is * not assigned yet, it must be -1. */ void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags, int32_t stream_id, nghttp2_headers_category cat, const nghttp2_priority_spec *pri_spec, nghttp2_nv *nva, size_t nvlen); void nghttp2_frame_headers_free(nghttp2_headers *frame, nghttp2_mem *mem); void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id, const nghttp2_priority_spec *pri_spec); void nghttp2_frame_priority_free(nghttp2_priority *frame); void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id, uint32_t error_code); void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame); /* * Initializes PUSH_PROMISE frame |frame| with given values. |frame| * takes ownership of |nva|, so caller must not free it. */ void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags, int32_t stream_id, int32_t promised_stream_id, nghttp2_nv *nva, size_t nvlen); void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame, nghttp2_mem *mem); /* * Initializes SETTINGS frame |frame| with given values. |frame| takes * ownership of |iv|, so caller must not free it. The |flags| are * bitwise-OR of one or more of nghttp2_settings_flag. */ void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags, nghttp2_settings_entry *iv, size_t niv); void nghttp2_frame_settings_free(nghttp2_settings *frame, nghttp2_mem *mem); /* * Initializes PING frame |frame| with given values. If the * |opqeue_data| is not NULL, it must point to 8 bytes memory region * of data. The data pointed by |opaque_data| is copied. It can be * NULL. In this case, 8 bytes NULL is used. */ void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags, const uint8_t *opque_data); void nghttp2_frame_ping_free(nghttp2_ping *frame); /* * Initializes GOAWAY frame |frame| with given values. On success, * this function takes ownership of |opaque_data|, so caller must not * free it. If the |opaque_data_len| is 0, opaque_data could be NULL. */ void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id, uint32_t error_code, uint8_t *opaque_data, size_t opaque_data_len); void nghttp2_frame_goaway_free(nghttp2_goaway *frame, nghttp2_mem *mem); void nghttp2_frame_window_update_init(nghttp2_window_update *frame, uint8_t flags, int32_t stream_id, int32_t window_size_increment); void nghttp2_frame_window_update_free(nghttp2_window_update *frame); void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type, uint8_t flags, int32_t stream_id, void *payload); void nghttp2_frame_extension_free(nghttp2_extension *frame); /* * Initializes ALTSVC frame |frame| with given values. This function * assumes that frame->payload points to nghttp2_ext_altsvc object. * Also |origin| and |field_value| are allocated in single buffer, * starting |origin|. On success, this function takes ownership of * |origin|, so caller must not free it. */ void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id, uint8_t *origin, size_t origin_len, uint8_t *field_value, size_t field_value_len); /* * Frees up resources under |frame|. This function does not free * nghttp2_ext_altsvc object pointed by frame->payload. This function * only frees origin pointed by nghttp2_ext_altsvc.origin. Therefore, * other fields must be allocated in the same buffer with origin. */ void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem); /* * Initializes ORIGIN frame |frame| with given values. This function * assumes that frame->payload points to nghttp2_ext_origin object. * Also |ov| and the memory pointed by the field of its elements are * allocated in single buffer, starting with |ov|. On success, this * function takes ownership of |ov|, so caller must not free it. */ void nghttp2_frame_origin_init(nghttp2_extension *frame, nghttp2_origin_entry *ov, size_t nov); /* * Frees up resources under |frame|. This function does not free * nghttp2_ext_origin object pointed by frame->payload. This function * only frees nghttp2_ext_origin.ov. Therefore, other fields must be * allocated in the same buffer with ov. */ void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem); /* * Initializes PRIORITY_UPDATE frame |frame| with given values. This * function assumes that frame->payload points to * nghttp2_ext_priority_update object. On success, this function * takes ownership of |field_value|, so caller must not free it. */ void nghttp2_frame_priority_update_init(nghttp2_extension *frame, int32_t stream_id, uint8_t *field_value, size_t field_value_len); /* * Frees up resources under |frame|. This function does not free * nghttp2_ext_priority_update object pointed by frame->payload. This * function only frees field_value pointed by * nghttp2_ext_priority_update.field_value. */ void nghttp2_frame_priority_update_free(nghttp2_extension *frame, nghttp2_mem *mem); /* * Returns the number of padding bytes after payload. The total * padding length is given in the |padlen|. The returned value does * not include the Pad Length field. If |padlen| is 0, this function * returns 0, regardless of frame->hd.flags. */ size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen); void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags, int32_t stream_id); void nghttp2_frame_data_free(nghttp2_data *frame); /* * Makes copy of |iv| and return the copy. The |niv| is the number of * entries in |iv|. This function returns the pointer to the copy if * it succeeds, or NULL. */ nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv, size_t niv, nghttp2_mem *mem); /* * Sorts the |nva| in ascending order of name and value. If names are * equivalent, sort them by value. */ void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen); /* * Copies name/value pairs from |nva|, which contains |nvlen| pairs, * to |*nva_ptr|, which is dynamically allocated so that all items can * be stored. The resultant name and value in nghttp2_nv are * guaranteed to be NULL-terminated even if the input is not * null-terminated. * * The |*nva_ptr| must be freed using nghttp2_nv_array_del(). * * This function returns 0 if it succeeds or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. */ int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem); /* * Returns nonzero if the name/value pair |a| equals to |b|. The name * is compared in case-sensitive, because we ensure that this function * is called after the name is lower-cased. */ int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b); /* * Frees |nva|. */ void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem); /* * Checks that the |iv|, which includes |niv| entries, does not have * invalid values. * * This function returns nonzero if it succeeds, or 0. */ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv); /* * Sets Pad Length field and flags and adjusts frame header position * of each buffers in |bufs|. The number of padding is given in the * |padlen| including Pad Length field. The |hd| is the frame header * for the serialized data. This function fills zeros padding region * unless framehd_only is nonzero. */ void nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, size_t padlen, int framehd_only); #endif /* !defined(NGHTTP2_FRAME_H) */ nghttp2-1.68.0/lib/PaxHeaders/nghttp2_http.c0000644000000000000000000000013215077107270015617 xustar0030 mtime=1761382072.979444204 30 atime=1761382104.988315784 30 ctime=1761382107.974303636 nghttp2-1.68.0/lib/nghttp2_http.c0000644000175100017510000005523715077107270016223 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_http.h" #include #include #include #include "nghttp2_hd.h" #include "nghttp2_helper.h" #include "nghttp2_extpri.h" #include "sfparse.h" static uint8_t downcase(uint8_t c) { return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c; } static int memieq(const void *a, const void *b, size_t n) { size_t i; const uint8_t *aa = a, *bb = b; for (i = 0; i < n; ++i) { if (downcase(aa[i]) != downcase(bb[i])) { return 0; } } return 1; } #define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N))) static int64_t parse_uint(const uint8_t *s, size_t len) { int64_t n = 0; size_t i; if (len == 0) { return -1; } for (i = 0; i < len; ++i) { if ('0' <= s[i] && s[i] <= '9') { if (n > INT64_MAX / 10) { return -1; } n *= 10; if (n > INT64_MAX - (s[i] - '0')) { return -1; } n += s[i] - '0'; continue; } return -1; } return n; } static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv, uint32_t flag) { if ((stream->http_flags & flag) || nv->value->len == 0) { return 0; } stream->http_flags = stream->http_flags | flag; return 1; } static int expect_response_body(nghttp2_stream *stream) { return (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_HEAD) == 0 && stream->status_code / 100 != 1 && stream->status_code != 304 && stream->status_code != 204; } /* For "http" or "https" URIs, OPTIONS request may have "*" in :path header field to represent system-wide OPTIONS request. Otherwise, :path header field value must start with "/". This function must be called after ":method" header field was received. This function returns nonzero if path is valid.*/ static int check_path(nghttp2_stream *stream) { return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 || ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) || ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) && (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK))); } static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv, int trailer, int connect_protocol) { nghttp2_extpri extpri; if (nv->name->base[0] == ':') { if (trailer || (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { return NGHTTP2_ERR_HTTP_HEADER; } } switch (nv->token) { case NGHTTP2_TOKEN__AUTHORITY: if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) { return NGHTTP2_ERR_HTTP_HEADER; } break; case NGHTTP2_TOKEN__METHOD: if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) { return NGHTTP2_ERR_HTTP_HEADER; } switch (nv->value->len) { case 4: if (lstreq("HEAD", nv->value->base, nv->value->len)) { stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; } break; case 7: switch (nv->value->base[6]) { case 'T': if (lstreq("CONNECT", nv->value->base, nv->value->len)) { if (stream->stream_id % 2 == 0) { /* we won't allow CONNECT for push */ return NGHTTP2_ERR_HTTP_HEADER; } stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT; } break; case 'S': if (lstreq("OPTIONS", nv->value->base, nv->value->len)) { stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS; } break; } break; } break; case NGHTTP2_TOKEN__PATH: if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) { return NGHTTP2_ERR_HTTP_HEADER; } if (nv->value->base[0] == '/') { stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR; } else if (nv->value->len == 1 && nv->value->base[0] == '*') { stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK; } break; case NGHTTP2_TOKEN__SCHEME: if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) { return NGHTTP2_ERR_HTTP_HEADER; } if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) || (nv->value->len == 5 && memieq("https", nv->value->base, 5))) { stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP; } break; case NGHTTP2_TOKEN__PROTOCOL: if (!connect_protocol) { return NGHTTP2_ERR_HTTP_HEADER; } if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) { return NGHTTP2_ERR_HTTP_HEADER; } break; case NGHTTP2_TOKEN_HOST: if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) { return NGHTTP2_ERR_HTTP_HEADER; } break; case NGHTTP2_TOKEN_CONTENT_LENGTH: { if (stream->content_length != -1) { return NGHTTP2_ERR_HTTP_HEADER; } stream->content_length = parse_uint(nv->value->base, nv->value->len); if (stream->content_length == -1) { return NGHTTP2_ERR_HTTP_HEADER; } break; } /* disallowed header fields */ case NGHTTP2_TOKEN_CONNECTION: case NGHTTP2_TOKEN_KEEP_ALIVE: case NGHTTP2_TOKEN_PROXY_CONNECTION: case NGHTTP2_TOKEN_TRANSFER_ENCODING: case NGHTTP2_TOKEN_UPGRADE: return NGHTTP2_ERR_HTTP_HEADER; case NGHTTP2_TOKEN_TE: if (!lstrieq("trailers", nv->value->base, nv->value->len)) { return NGHTTP2_ERR_HTTP_HEADER; } break; case NGHTTP2_TOKEN_PRIORITY: if (!trailer && /* Do not parse the header field in PUSH_PROMISE. */ (stream->stream_id & 1) && !(stream->http_flags & NGHTTP2_HTTP_FLAG_BAD_PRIORITY)) { nghttp2_extpri_from_uint8(&extpri, stream->http_extpri); if (nghttp2_http_parse_priority(&extpri, nv->value->base, nv->value->len) == 0) { stream->http_extpri = nghttp2_extpri_to_uint8(&extpri); stream->http_flags |= NGHTTP2_HTTP_FLAG_PRIORITY; } else { stream->http_flags &= (uint32_t)~NGHTTP2_HTTP_FLAG_PRIORITY; stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY; } } break; default: if (nv->name->base[0] == ':') { return NGHTTP2_ERR_HTTP_HEADER; } } if (nv->name->base[0] != ':') { stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; } return 0; } static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv, int trailer) { if (nv->name->base[0] == ':') { if (trailer || (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { return NGHTTP2_ERR_HTTP_HEADER; } } switch (nv->token) { case NGHTTP2_TOKEN__STATUS: { if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) { return NGHTTP2_ERR_HTTP_HEADER; } if (nv->value->len != 3) { return NGHTTP2_ERR_HTTP_HEADER; } stream->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len); if (stream->status_code == -1 || stream->status_code == 101) { return NGHTTP2_ERR_HTTP_HEADER; } break; } case NGHTTP2_TOKEN_CONTENT_LENGTH: { if (stream->status_code == 204) { /* content-length header field in 204 response is prohibited by RFC 7230. But some widely used servers send content-length: 0. Until they get fixed, we ignore it. */ if (stream->content_length != -1) { /* Found multiple content-length field */ return NGHTTP2_ERR_HTTP_HEADER; } if (!lstrieq("0", nv->value->base, nv->value->len)) { return NGHTTP2_ERR_HTTP_HEADER; } stream->content_length = 0; return NGHTTP2_ERR_REMOVE_HTTP_HEADER; } if (stream->status_code / 100 == 1) { return NGHTTP2_ERR_HTTP_HEADER; } /* https://tools.ietf.org/html/rfc7230#section-3.3.3 */ if (stream->status_code / 100 == 2 && (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) { return NGHTTP2_ERR_REMOVE_HTTP_HEADER; } if (stream->content_length != -1) { return NGHTTP2_ERR_HTTP_HEADER; } stream->content_length = parse_uint(nv->value->base, nv->value->len); if (stream->content_length == -1) { return NGHTTP2_ERR_HTTP_HEADER; } break; } /* disallowed header fields */ case NGHTTP2_TOKEN_CONNECTION: case NGHTTP2_TOKEN_KEEP_ALIVE: case NGHTTP2_TOKEN_PROXY_CONNECTION: case NGHTTP2_TOKEN_TRANSFER_ENCODING: case NGHTTP2_TOKEN_UPGRADE: return NGHTTP2_ERR_HTTP_HEADER; case NGHTTP2_TOKEN_TE: if (!lstrieq("trailers", nv->value->base, nv->value->len)) { return NGHTTP2_ERR_HTTP_HEADER; } break; default: if (nv->name->base[0] == ':') { return NGHTTP2_ERR_HTTP_HEADER; } } if (nv->name->base[0] != ':') { stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; } return 0; } static int check_scheme(const uint8_t *value, size_t len) { const uint8_t *last; if (len == 0) { return 0; } if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) { return 0; } last = value + len; ++value; for (; value != last; ++value) { if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z') || ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' || *value == '.')) { return 0; } } return 1; } static int lws(const uint8_t *s, size_t n) { size_t i; for (i = 0; i < n; ++i) { if (s[i] != ' ' && s[i] != '\t') { return 0; } } return 1; } /* Generated by genauthoritychartbl.py, but '@' is not allowed */ static char VALID_AUTHORITY_CHARS[] = { 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, 0 /* SPC */, 1 /* ! */, 0 /* " */, 0 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, 1 /* , */, 1 /* - */, 1 /* . */, 0 /* / */, 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, 0 /* < */, 1 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */, 0 /* \ */, 1 /* ] */, 0 /* ^ */, 1 /* _ */, 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 0 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */ }; static int check_authority(const uint8_t *value, size_t len) { const uint8_t *last; for (last = value + len; value != last; ++value) { if (!VALID_AUTHORITY_CHARS[*value]) { return 0; } } return 1; } int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, nghttp2_frame *frame, nghttp2_hd_nv *nv, int trailer) { int rv; /* We are strict for pseudo header field. One bad character should lead to fail. OTOH, we should be a bit forgiving for regular headers, since existing public internet has so much illegal headers floating around and if we kill the stream because of this, we may disrupt many web sites and/or libraries. So we become conservative here, and just ignore those illegal regular headers. */ if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) { size_t i; if (nv->name->len > 0 && nv->name->base[0] == ':') { return NGHTTP2_ERR_HTTP_HEADER; } /* header field name must be lower-cased without exception */ for (i = 0; i < nv->name->len; ++i) { uint8_t c = nv->name->base[i]; if ('A' <= c && c <= 'Z') { return NGHTTP2_ERR_HTTP_HEADER; } } /* When ignoring regular headers, we set this flag so that we still enforce header field ordering rule for pseudo header fields. */ stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; return NGHTTP2_ERR_IGN_HTTP_HEADER; } switch (nv->token) { case NGHTTP2_TOKEN__METHOD: rv = nghttp2_check_method(nv->value->base, nv->value->len); break; case NGHTTP2_TOKEN__PATH: rv = nghttp2_check_path(nv->value->base, nv->value->len); break; case NGHTTP2_TOKEN__AUTHORITY: case NGHTTP2_TOKEN_HOST: if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) { rv = check_authority(nv->value->base, nv->value->len); } else if ( stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) { rv = nghttp2_check_header_value(nv->value->base, nv->value->len); } else { rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len); } break; case NGHTTP2_TOKEN__SCHEME: rv = check_scheme(nv->value->base, nv->value->len); break; case NGHTTP2_TOKEN__PROTOCOL: /* Check the value consists of just white spaces, which was done in check_pseudo_header before nghttp2_check_header_value_rfc9113 has been introduced. */ if ((stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) && lws(nv->value->base, nv->value->len)) { rv = 0; break; } /* fall through */ default: if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) { rv = nghttp2_check_header_value(nv->value->base, nv->value->len); } else { rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len); } } if (rv == 0) { assert(nv->name->len > 0); if (nv->name->base[0] == ':') { return NGHTTP2_ERR_HTTP_HEADER; } /* When ignoring regular headers, we set this flag so that we still enforce header field ordering rule for pseudo header fields. */ stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; return NGHTTP2_ERR_IGN_HTTP_HEADER; } if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) { return http_request_on_header(stream, nv, trailer, session->server && session->pending_enable_connect_protocol); } return http_response_on_header(stream, nv, trailer); } int nghttp2_http_on_request_headers(nghttp2_stream *stream, nghttp2_frame *frame) { if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) && (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) { if ((stream->http_flags & (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) || (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) { return -1; } stream->content_length = -1; } else { if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) != NGHTTP2_HTTP_FLAG_REQ_HEADERS || (stream->http_flags & (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) { return -1; } if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) && ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) == 0 || (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0)) { return -1; } if (!check_path(stream)) { return -1; } } if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { /* we are going to reuse data fields for upcoming response. Clear them now, except for method flags. */ stream->http_flags &= NGHTTP2_HTTP_FLAG_METH_ALL; stream->content_length = -1; } return 0; } int nghttp2_http_on_response_headers(nghttp2_stream *stream) { if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) { return -1; } if (stream->status_code / 100 == 1) { /* non-final response */ stream->http_flags = (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) | NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE; stream->content_length = -1; stream->status_code = -1; return 0; } stream->http_flags = stream->http_flags & (uint32_t)~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE; if (!expect_response_body(stream)) { stream->content_length = 0; } else if (stream->http_flags & (NGHTTP2_HTTP_FLAG_METH_CONNECT | NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND)) { stream->content_length = -1; } return 0; } int nghttp2_http_on_trailer_headers(nghttp2_stream *stream, nghttp2_frame *frame) { (void)stream; if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { return -1; } return 0; } int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) { if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) { return -1; } if (stream->content_length != -1 && stream->content_length != stream->recv_content_length) { return -1; } return 0; } int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) { stream->recv_content_length += (int64_t)n; if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) || (stream->content_length != -1 && stream->recv_content_length > stream->content_length)) { return -1; } return 0; } void nghttp2_http_record_request_method(nghttp2_stream *stream, nghttp2_frame *frame) { const nghttp2_nv *nva; size_t nvlen; size_t i; switch (frame->hd.type) { case NGHTTP2_HEADERS: nva = frame->headers.nva; nvlen = frame->headers.nvlen; break; case NGHTTP2_PUSH_PROMISE: nva = frame->push_promise.nva; nvlen = frame->push_promise.nvlen; break; default: return; } /* TODO we should do this strictly. */ for (i = 0; i < nvlen; ++i) { const nghttp2_nv *nv = &nva[i]; if (!(nv->namelen == 7 && nv->name[6] == 'd' && memcmp(":metho", nv->name, nv->namelen - 1) == 0)) { continue; } if (lstreq("CONNECT", nv->value, nv->valuelen)) { stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT; return; } if (lstreq("HEAD", nv->value, nv->valuelen)) { stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; return; } return; } } int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value, size_t valuelen) { nghttp2_extpri pri = *dest; sfparse_parser sfp; sfparse_vec key; sfparse_value val; int rv; sfparse_parser_init(&sfp, value, valuelen); for (;;) { rv = sfparse_parser_dict(&sfp, &key, &val); if (rv != 0) { if (rv == SFPARSE_ERR_EOF) { break; } return NGHTTP2_ERR_INVALID_ARGUMENT; } if (key.len != 1) { continue; } switch (key.base[0]) { case 'i': if (val.type != SFPARSE_TYPE_BOOLEAN) { return NGHTTP2_ERR_INVALID_ARGUMENT; } pri.inc = val.boolean; break; case 'u': if (val.type != SFPARSE_TYPE_INTEGER || val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH || NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) { return NGHTTP2_ERR_INVALID_ARGUMENT; } pri.urgency = (uint32_t)val.integer; break; } } *dest = pri; return 0; } nghttp2-1.68.0/lib/PaxHeaders/nghttp2_option.c0000644000000000000000000000013215077107270016150 xustar0030 mtime=1761382072.979444204 30 atime=1761382104.984315801 30 ctime=1761382107.970303648 nghttp2-1.68.0/lib/nghttp2_option.c0000644000175100017510000001367115077107270016550 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_option.h" #include "nghttp2_session.h" int nghttp2_option_new(nghttp2_option **option_ptr) { *option_ptr = calloc(1, sizeof(nghttp2_option)); if (*option_ptr == NULL) { return NGHTTP2_ERR_NOMEM; } return 0; } void nghttp2_option_del(nghttp2_option *option) { free(option); } void nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val) { option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE; option->no_auto_window_update = val; } void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, uint32_t val) { option->opt_set_mask |= NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS; option->peer_max_concurrent_streams = val; } void nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val) { option->opt_set_mask |= NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC; option->no_recv_client_magic = val; } void nghttp2_option_set_no_http_messaging(nghttp2_option *option, int val) { option->opt_set_mask |= NGHTTP2_OPT_NO_HTTP_MESSAGING; option->no_http_messaging = val; } void nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option, uint32_t val) { option->opt_set_mask |= NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS; option->max_reserved_remote_streams = val; } static void set_ext_type(uint8_t *ext_types, uint8_t type) { ext_types[type / 8] = (uint8_t)(ext_types[type / 8] | (1 << (type & 0x7))); } void nghttp2_option_set_user_recv_extension_type(nghttp2_option *option, uint8_t type) { if (type < 10) { return; } option->opt_set_mask |= NGHTTP2_OPT_USER_RECV_EXT_TYPES; set_ext_type(option->user_recv_ext_types, type); } void nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option, uint8_t type) { switch (type) { case NGHTTP2_ALTSVC: option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES; option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_ALTSVC; return; case NGHTTP2_ORIGIN: option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES; option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_ORIGIN; return; case NGHTTP2_PRIORITY_UPDATE: option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES; option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_PRIORITY_UPDATE; return; default: return; } } void nghttp2_option_set_no_auto_ping_ack(nghttp2_option *option, int val) { option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_PING_ACK; option->no_auto_ping_ack = val; } void nghttp2_option_set_max_send_header_block_length(nghttp2_option *option, size_t val) { option->opt_set_mask |= NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH; option->max_send_header_block_length = val; } void nghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option, size_t val) { option->opt_set_mask |= NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE; option->max_deflate_dynamic_table_size = val; } void nghttp2_option_set_no_closed_streams(nghttp2_option *option, int val) { option->opt_set_mask |= NGHTTP2_OPT_NO_CLOSED_STREAMS; option->no_closed_streams = val; } void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, size_t val) { option->opt_set_mask |= NGHTTP2_OPT_MAX_OUTBOUND_ACK; option->max_outbound_ack = val; } void nghttp2_option_set_max_settings(nghttp2_option *option, size_t val) { option->opt_set_mask |= NGHTTP2_OPT_MAX_SETTINGS; option->max_settings = val; } void nghttp2_option_set_server_fallback_rfc7540_priorities( nghttp2_option *option, int val) { option->opt_set_mask |= NGHTTP2_OPT_SERVER_FALLBACK_RFC7540_PRIORITIES; option->server_fallback_rfc7540_priorities = val; } void nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation( nghttp2_option *option, int val) { option->opt_set_mask |= NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION; option->no_rfc9113_leading_and_trailing_ws_validation = val; } void nghttp2_option_set_stream_reset_rate_limit(nghttp2_option *option, uint64_t burst, uint64_t rate) { option->opt_set_mask |= NGHTTP2_OPT_STREAM_RESET_RATE_LIMIT; option->stream_reset_burst = burst; option->stream_reset_rate = rate; } void nghttp2_option_set_max_continuations(nghttp2_option *option, size_t val) { option->opt_set_mask |= NGHTTP2_OPT_MAX_CONTINUATIONS; option->max_continuations = val; } void nghttp2_option_set_glitch_rate_limit(nghttp2_option *option, uint64_t burst, uint64_t rate) { option->opt_set_mask |= NGHTTP2_OPT_GLITCH_RATE_LIMIT; option->glitch_burst = burst; option->glitch_rate = rate; } nghttp2-1.68.0/lib/PaxHeaders/nghttp2_alpn.c0000644000000000000000000000013215077107270015572 xustar0030 mtime=1761382072.976444217 30 atime=1761382104.976315836 30 ctime=1761382107.962303671 nghttp2-1.68.0/lib/nghttp2_alpn.c0000644000175100017510000000516615077107270016172 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_alpn.h" #include static int select_alpn(const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, const char *key, unsigned int keylen) { unsigned int i; for (i = 0; i + keylen <= inlen; i += (unsigned int)(in[i] + 1)) { if (memcmp(&in[i], key, keylen) == 0) { *out = (unsigned char *)&in[i + 1]; *outlen = in[i]; return 0; } } return -1; } #define NGHTTP2_HTTP_1_1_ALPN "\x8http/1.1" #define NGHTTP2_HTTP_1_1_ALPN_LEN (sizeof(NGHTTP2_HTTP_1_1_ALPN) - 1) int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen) { if (select_alpn((const unsigned char **)out, outlen, in, inlen, NGHTTP2_PROTO_ALPN, NGHTTP2_PROTO_ALPN_LEN) == 0) { return 1; } if (select_alpn((const unsigned char **)out, outlen, in, inlen, NGHTTP2_HTTP_1_1_ALPN, NGHTTP2_HTTP_1_1_ALPN_LEN) == 0) { return 0; } return -1; } int nghttp2_select_alpn(const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen) { if (select_alpn(out, outlen, in, inlen, NGHTTP2_PROTO_ALPN, NGHTTP2_PROTO_ALPN_LEN) == 0) { return 1; } if (select_alpn(out, outlen, in, inlen, NGHTTP2_HTTP_1_1_ALPN, NGHTTP2_HTTP_1_1_ALPN_LEN) == 0) { return 0; } return -1; } nghttp2-1.68.0/lib/PaxHeaders/nghttp2_alpn.h0000644000000000000000000000013215077107270015577 xustar0030 mtime=1761382072.976444217 30 atime=1761382104.942315986 30 ctime=1761382107.928303769 nghttp2-1.68.0/lib/nghttp2_alpn.h0000644000175100017510000000251315077107270016170 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_ALPN_H #define NGHTTP2_ALPN_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include #endif /* !defined(NGHTTP2_ALPN_H) */ nghttp2-1.68.0/PaxHeaders/configure0000644000000000000000000000013215077107304014162 xustar0030 mtime=1761382084.518391658 30 atime=1761382085.851385949 30 ctime=1761382107.812304104 nghttp2-1.68.0/configure0000755000175100017510000336273715077107304014602 0ustar00runnerrunner#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.71 for nghttp2 1.68.0. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, # Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="as_nop=: if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else \$as_nop case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ) then : else \$as_nop exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 blah=\$(echo \$(echo blah)) test x\"\$blah\" = xblah || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\ || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null then : as_have_required=yes else $as_nop as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null then : else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$as_shell as_have_required=yes if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null then : break 2 fi fi done;; esac as_found=false done IFS=$as_save_IFS if $as_found then : else $as_nop if { test -f "$SHELL" || test -f "$SHELL.exe"; } && as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$SHELL as_have_required=yes fi fi if test "x$CONFIG_SHELL" != x then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno then : printf "%s\n" "$0: This script requires a shell more modern than all" printf "%s\n" "$0: the shells that I found on your system." if test ${ZSH_VERSION+y} ; then printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." else printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and $0: t-tujikawa@users.sourceforge.net about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else $as_nop as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" SHELL=${CONFIG_SHELL-/bin/sh} test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='nghttp2' PACKAGE_TARNAME='nghttp2' PACKAGE_VERSION='1.68.0' PACKAGE_STRING='nghttp2 1.68.0' PACKAGE_BUGREPORT='t-tujikawa@users.sourceforge.net' PACKAGE_URL='' # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_STDIO_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_header_c_list= ac_func_c_list= ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS APPLDFLAGS TESTLDADD ENABLE_STATIC_FALSE ENABLE_STATIC_TRUE EXTRACFLAG WARNCXXFLAGS WARNCFLAGS LIBOBJS ENABLE_FAILMALLOC_FALSE ENABLE_FAILMALLOC_TRUE HAVE_NEVERBLEED_FALSE HAVE_NEVERBLEED_TRUE HAVE_MRUBY_FALSE HAVE_MRUBY_TRUE ENABLE_THIRD_PARTY_FALSE ENABLE_THIRD_PARTY_TRUE LIBMRUBY_CFLAGS LIBMRUBY_LIBS ENABLE_EXAMPLES_FALSE ENABLE_EXAMPLES_TRUE ENABLE_HPACK_TOOLS_FALSE ENABLE_HPACK_TOOLS_TRUE ENABLE_HTTP3_FALSE ENABLE_HTTP3_TRUE ENABLE_APP_FALSE ENABLE_APP_TRUE HAVE_LIBXML2_FALSE HAVE_LIBXML2_TRUE LIBXML2_LIBS LIBXML2_CFLAGS SYSTEMD_LIBS SYSTEMD_CFLAGS JANSSON_LIBS JANSSON_CFLAGS LIBEVENT_OPENSSL_LIBS LIBEVENT_OPENSSL_CFLAGS LIBBROTLIDEC_LIBS LIBBROTLIDEC_CFLAGS LIBBROTLIENC_LIBS LIBBROTLIENC_CFLAGS HAVE_LIBBPF_FALSE HAVE_LIBBPF_TRUE EXTRABPFCFLAGS LIBBPF_LIBS LIBBPF_CFLAGS LIBNGHTTP3_LIBS LIBNGHTTP3_CFLAGS LIBNGTCP2_CRYPTO_OSSL_LIBS LIBNGTCP2_CRYPTO_OSSL_CFLAGS LIBNGTCP2_CRYPTO_BORINGSSL_LIBS LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS LIBNGTCP2_CRYPTO_LIBRESSL_LIBS LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS LIBNGTCP2_CRYPTO_QUICTLS_LIBS LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS LIBNGTCP2_CRYPTO_WOLFSSL_LIBS LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS LIBNGTCP2_LIBS LIBNGTCP2_CFLAGS LIBCARES_LIBS LIBCARES_CFLAGS WOLFSSL_LIBS WOLFSSL_CFLAGS OPENSSL_LIBS OPENSSL_CFLAGS ZLIB_LIBS ZLIB_CFLAGS EXTRA_DEFS CXX1XCXXFLAGS HAVE_CXX20 pkgpyexecdir pyexecdir pkgpythondir pythondir PYTHON_EXEC_PREFIX PYTHON_PREFIX PYTHON_PLATFORM PYTHON_VERSION PYTHON PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG CPP am__fastdepCXX_FALSE am__fastdepCXX_TRUE CXXDEPMODE CXXCPP ac_ct_CXX CXXFLAGS CXX BPFCFLAGS LIBTOOL_LDFLAGS JEMALLOC_LIBS JEMALLOC_CFLAGS LIBEV_LIBS LIBEV_CFLAGS PACKAGE_VERSION_NUM LT_AGE LT_REVISION LT_CURRENT AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V AM_V CSCOPE ETAGS CTAGS am__fastdepCC_FALSE am__fastdepCC_TRUE CCDEPMODE am__nodep AMDEPBACKSLASH AMDEP_FALSE AMDEP_TRUE am__include DEPDIR am__untar am__tar AMTAR am__leading_dot SET_MAKE mkdir_p MKDIR_P INSTALL_STRIP_PROGRAM install_sh MAKEINFO AUTOHEADER AUTOMAKE AUTOCONF ACLOCAL VERSION PACKAGE CYGPATH_W am__isrc INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM target_os target_vendor target_cpu target LT_SYS_LIBRARY_PATH OTOOL64 OTOOL LIPO NMEDIT DSYMUTIL MANIFEST_TOOL AWK RANLIB STRIP ac_ct_AR AR DLLTOOL OBJDUMP FILECMD LN_S NM ac_ct_DUMPBIN DUMPBIN LD FGREP EGREP GREP SED host_os host_vendor host_cpu host build_os build_vendor build_cpu build LIBTOOL OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL am__quote' ac_subst_files='' ac_user_opts=' enable_option_checking enable_shared enable_static with_pic enable_fast_install with_aix_soname with_gnu_ld with_sysroot enable_libtool_lock enable_dependency_tracking enable_silent_rules enable_werror enable_debug enable_threads enable_app enable_hpack_tools enable_examples enable_failmalloc enable_lib_only enable_http3 with_libxml2 with_jansson with_zlib with_libevent_openssl with_libcares with_wolfssl with_openssl with_libev with_jemalloc with_systemd with_mruby with_neverbleed with_libngtcp2 with_libnghttp3 with_libbpf with_libbrotlienc with_libbrotlidec with_python_sys_prefix with_python_prefix with_python_exec_prefix enable_assert enable_largefile ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS LT_SYS_LIBRARY_PATH LIBEV_CFLAGS LIBEV_LIBS JEMALLOC_CFLAGS JEMALLOC_LIBS LIBTOOL_LDFLAGS BPFCFLAGS CXX CXXFLAGS CCC CXXCPP CPP PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR PYTHON ZLIB_CFLAGS ZLIB_LIBS OPENSSL_CFLAGS OPENSSL_LIBS WOLFSSL_CFLAGS WOLFSSL_LIBS LIBCARES_CFLAGS LIBCARES_LIBS LIBNGTCP2_CFLAGS LIBNGTCP2_LIBS LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS LIBNGTCP2_CRYPTO_WOLFSSL_LIBS LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS LIBNGTCP2_CRYPTO_QUICTLS_LIBS LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS LIBNGTCP2_CRYPTO_LIBRESSL_LIBS LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS LIBNGTCP2_CRYPTO_BORINGSSL_LIBS LIBNGTCP2_CRYPTO_OSSL_CFLAGS LIBNGTCP2_CRYPTO_OSSL_LIBS LIBNGHTTP3_CFLAGS LIBNGHTTP3_LIBS LIBBPF_CFLAGS LIBBPF_LIBS LIBBROTLIENC_CFLAGS LIBBROTLIENC_LIBS LIBBROTLIDEC_CFLAGS LIBBROTLIDEC_LIBS LIBEVENT_OPENSSL_CFLAGS LIBEVENT_OPENSSL_LIBS JANSSON_CFLAGS JANSSON_LIBS SYSTEMD_CFLAGS SYSTEMD_LIBS LIBXML2_CFLAGS LIBXML2_LIBS' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures nghttp2 1.68.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/nghttp2] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF Program names: --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] --target=TARGET configure for building compilers for TARGET [HOST] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of nghttp2 1.68.0:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-shared[=PKGS] build shared libraries [default=yes] --enable-static[=PKGS] build static libraries [default=yes] --enable-fast-install[=PKGS] optimize for fast installation [default=yes] --disable-libtool-lock avoid locking (might break parallel builds) --enable-dependency-tracking do not reject slow dependency extractors --disable-dependency-tracking speeds up one-time build --enable-silent-rules less verbose build output (undo: "make V=1") --disable-silent-rules verbose build output (undo: "make V=0") --enable-werror Turn on compile time warnings --enable-debug Turn on debug output --disable-threads Turn off threading in apps --enable-app Build applications (nghttp, nghttpd, nghttpx and h2load) [default=check] --enable-hpack-tools Build HPACK tools [default=check] --enable-examples Build examples [default=check] --disable-failmalloc Do not build failmalloc test program --enable-lib-only Build libnghttp2 only. This is a short hand for --disable-app --disable-examples --disable-hpack-tools --enable-http3 (EXPERIMENTAL) Enable HTTP/3. This requires ngtcp2, nghttp3, and a custom OpenSSL. --disable-assert turn off assertions --disable-largefile omit support for large files Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use both] --with-aix-soname=aix|svr4|both shared library versioning (aka "SONAME") variant to provide on AIX, [default=aix]. --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-sysroot[=DIR] Search for dependent libraries within DIR (or the compiler's sysroot if not specified). --with-libxml2 Use libxml2 [default=check] --with-jansson Use jansson [default=check] --with-zlib Use zlib [default=check] --with-libevent-openssl Use libevent_openssl [default=check] --with-libcares Use libc-ares [default=check] --with-wolfssl Use wolfSSL [default=check] --with-openssl Use openssl [default=check] --with-libev Use libev [default=check] --with-jemalloc Use jemalloc [default=check] --with-systemd Enable systemd support in nghttpx [default=check] --with-mruby Use mruby [default=no] --with-neverbleed Use neverbleed [default=no] --with-libngtcp2 Use libngtcp2 [default=check] --with-libnghttp3 Use libnghttp3 [default=check] --with-libbpf Use libbpf [default=no] --with-libbrotlienc Use libbrotlienc [default=no] --with-libbrotlidec Use libbrotlidec [default=no] --with-python-sys-prefix use Python's sys.prefix and sys.exec_prefix values --with-python_prefix override the default PYTHON_PREFIX --with-python_exec_prefix override the default PYTHON_EXEC_PREFIX Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory LT_SYS_LIBRARY_PATH User-defined run-time library search path. LIBEV_CFLAGS C compiler flags for libev, skipping any checks LIBEV_LIBS linker flags for libev, skipping any checks JEMALLOC_CFLAGS C compiler flags for jemalloc, skipping any checks JEMALLOC_LIBS linker flags for jemalloc, skipping any checks LIBTOOL_LDFLAGS libtool specific flags (e.g., -static-libtool-libs) BPFCFLAGS C compiler flags for bpf program CXX C++ compiler command CXXFLAGS C++ compiler flags CXXCPP C++ preprocessor CPP C preprocessor PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path PYTHON the Python interpreter ZLIB_CFLAGS C compiler flags for ZLIB, overriding pkg-config ZLIB_LIBS linker flags for ZLIB, overriding pkg-config OPENSSL_CFLAGS C compiler flags for OPENSSL, overriding pkg-config OPENSSL_LIBS linker flags for OPENSSL, overriding pkg-config WOLFSSL_CFLAGS C compiler flags for WOLFSSL, overriding pkg-config WOLFSSL_LIBS linker flags for WOLFSSL, overriding pkg-config LIBCARES_CFLAGS C compiler flags for LIBCARES, overriding pkg-config LIBCARES_LIBS linker flags for LIBCARES, overriding pkg-config LIBNGTCP2_CFLAGS C compiler flags for LIBNGTCP2, overriding pkg-config LIBNGTCP2_LIBS linker flags for LIBNGTCP2, overriding pkg-config LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS C compiler flags for LIBNGTCP2_CRYPTO_WOLFSSL, overriding pkg-config LIBNGTCP2_CRYPTO_WOLFSSL_LIBS linker flags for LIBNGTCP2_CRYPTO_WOLFSSL, overriding pkg-config LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS C compiler flags for LIBNGTCP2_CRYPTO_QUICTLS, overriding pkg-config LIBNGTCP2_CRYPTO_QUICTLS_LIBS linker flags for LIBNGTCP2_CRYPTO_QUICTLS, overriding pkg-config LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS C compiler flags for LIBNGTCP2_CRYPTO_LIBRESSL, overriding pkg-config LIBNGTCP2_CRYPTO_LIBRESSL_LIBS linker flags for LIBNGTCP2_CRYPTO_LIBRESSL, overriding pkg-config LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS C compiler flags for LIBNGTCP2_CRYPTO_BORINGSSL, overriding pkg-config LIBNGTCP2_CRYPTO_BORINGSSL_LIBS linker flags for LIBNGTCP2_CRYPTO_BORINGSSL, overriding pkg-config LIBNGTCP2_CRYPTO_OSSL_CFLAGS C compiler flags for LIBNGTCP2_CRYPTO_OSSL, overriding pkg-config LIBNGTCP2_CRYPTO_OSSL_LIBS linker flags for LIBNGTCP2_CRYPTO_OSSL, overriding pkg-config LIBNGHTTP3_CFLAGS C compiler flags for LIBNGHTTP3, overriding pkg-config LIBNGHTTP3_LIBS linker flags for LIBNGHTTP3, overriding pkg-config LIBBPF_CFLAGS C compiler flags for LIBBPF, overriding pkg-config LIBBPF_LIBS linker flags for LIBBPF, overriding pkg-config LIBBROTLIENC_CFLAGS C compiler flags for LIBBROTLIENC, overriding pkg-config LIBBROTLIENC_LIBS linker flags for LIBBROTLIENC, overriding pkg-config LIBBROTLIDEC_CFLAGS C compiler flags for LIBBROTLIDEC, overriding pkg-config LIBBROTLIDEC_LIBS linker flags for LIBBROTLIDEC, overriding pkg-config LIBEVENT_OPENSSL_CFLAGS C compiler flags for LIBEVENT_OPENSSL, overriding pkg-config LIBEVENT_OPENSSL_LIBS linker flags for LIBEVENT_OPENSSL, overriding pkg-config JANSSON_CFLAGS C compiler flags for JANSSON, overriding pkg-config JANSSON_LIBS linker flags for JANSSON, overriding pkg-config SYSTEMD_CFLAGS C compiler flags for SYSTEMD, overriding pkg-config SYSTEMD_LIBS linker flags for SYSTEMD, overriding pkg-config LIBXML2_CFLAGS C compiler flags for LIBXML2, overriding pkg-config LIBXML2_LIBS linker flags for LIBXML2, overriding pkg-config Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for configure.gnu first; this name is used for a wrapper for # Metaconfig's "Configure" on case-insensitive file systems. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF nghttp2 configure 1.68.0 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext } then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. */ #include #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main (void) { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_cxx_try_compile LINENO # ---------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_compile # ac_fn_cxx_try_cpp LINENO # ------------------------ # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || test ! -s conftest.err } then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_cpp # ac_fn_cxx_try_link LINENO # ------------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext } then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_link # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err } then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_check_decl LINENO SYMBOL VAR INCLUDES EXTRA-OPTIONS FLAG-VAR # ------------------------------------------------------------------ # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR # accordingly. Pass EXTRA-OPTIONS to the compiler, using FLAG-VAR. ac_fn_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack as_decl_name=`echo $2|sed 's/ *(.*//'` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 printf %s "checking whether $as_decl_name is declared... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` eval ac_save_FLAGS=\$$6 as_fn_append $6 " $5" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { #ifndef $as_decl_name #ifdef __cplusplus (void) $as_decl_use; #else (void) $as_decl_name; #endif #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext eval $6=\$ac_save_FLAGS fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_check_decl # ac_fn_c_check_type LINENO TYPE VAR INCLUDES # ------------------------------------------- # Tests whether TYPE exists after having included INCLUDES, setting cache # variable VAR accordingly. ac_fn_c_check_type () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop eval "$3=no" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { if (sizeof ($2)) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { if (sizeof (($2))) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop eval "$3=yes" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_type # ac_fn_c_find_uintX_t LINENO BITS VAR # ------------------------------------ # Finds an unsigned integer type with width BITS, setting cache variable VAR # accordingly. ac_fn_c_find_uintX_t () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 printf %s "checking for uint$2_t... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop eval "$3=no" # Order is important - never check a type that is potentially smaller # than half of the expected target width. for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \ 'unsigned long long int' 'unsigned short int' 'unsigned char'; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main (void) { static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : case $ac_type in #( uint$2_t) : eval "$3=yes" ;; #( *) : eval "$3=\$ac_type" ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if eval test \"x\$"$3"\" = x"no" then : else $as_nop break fi done fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_find_uintX_t # ac_fn_c_find_intX_t LINENO BITS VAR # ----------------------------------- # Finds a signed integer type with width BITS, setting cache variable VAR # accordingly. ac_fn_c_find_intX_t () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for int$2_t" >&5 printf %s "checking for int$2_t... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop eval "$3=no" # Order is important - never check a type that is potentially smaller # than half of the expected target width. for ac_type in int$2_t 'int' 'long int' \ 'long long int' 'short int' 'signed char'; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default enum { N = $2 / 2 - 1 }; int main (void) { static int test_array [1 - 2 * !(0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default enum { N = $2 / 2 - 1 }; int main (void) { static int test_array [1 - 2 * !(($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1) < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 2))]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop case $ac_type in #( int$2_t) : eval "$3=yes" ;; #( *) : eval "$3=\$ac_type" ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if eval test \"x\$"$3"\" = x"no" then : else $as_nop break fi done fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_find_intX_t # ac_fn_c_try_run LINENO # ---------------------- # Try to run conftest.$ac_ext, and return whether this succeeded. Assumes that # executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; } then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: program exited with status $ac_status" >&5 printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES # ---------------------------------------------------- # Tries to find if the field MEMBER exists in type AGGR, after including # INCLUDES, setting cache variable VAR accordingly. ac_fn_c_check_member () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 printf %s "checking for $2.$3... " >&6; } if eval test \${$4+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int main (void) { static $2 ac_aggr; if (ac_aggr.$3) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$4=yes" else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int main (void) { static $2 ac_aggr; if (sizeof ac_aggr.$3) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$4=yes" else $as_nop eval "$4=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$4 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_member ac_configure_args_raw= for ac_arg do case $ac_arg in *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append ac_configure_args_raw " '$ac_arg'" done case $ac_configure_args_raw in *$as_nl*) ac_safe_unquote= ;; *) ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab. ac_unsafe_a="$ac_unsafe_z#~" ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g" ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;; esac cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by nghttp2 $as_me 1.68.0, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac printf "%s\n" "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Sanitize IFS. IFS=" "" $as_nl" # Save into config.log some information that might help in debugging. { echo printf "%s\n" "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo printf "%s\n" "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then printf "%s\n" "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then printf "%s\n" "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && printf "%s\n" "$as_me: caught signal $ac_signal" printf "%s\n" "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h printf "%s\n" "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. if test -n "$CONFIG_SITE"; then ac_site_files="$CONFIG_SITE" elif test "x$prefix" != xNONE; then ac_site_files="$prefix/share/config.site $prefix/etc/config.site" else ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi for ac_site_file in $ac_site_files do case $ac_site_file in #( */*) : ;; #( *) : ac_site_file=./$ac_site_file ;; esac if test -f "$ac_site_file" && test -r "$ac_site_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 printf "%s\n" "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 printf "%s\n" "$as_me: creating cache $cache_file" >&6;} >$cache_file fi as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H" # Test code for whether the C compiler supports C89 (global declarations) ac_c_conftest_c89_globals=' /* Does the compiler advertise C89 conformance? Do not test the value of __STDC__, because some compilers set it to 0 while being otherwise adequately conformant. */ #if !defined __STDC__ # error "Compiler does not advertise C89 conformance" #endif #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ struct buf { int x; }; struct buf * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not \xHH hex character constants. These do not provoke an error unfortunately, instead are silently treated as an "x". The following induces an error, until -std is added to get proper ANSI mode. Curiously \x00 != x always comes out true, for an array size at least. It is necessary to write \x00 == 0 to get something that is true only with -std. */ int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) '\''x'\'' int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int), int, int);' # Test code for whether the C compiler supports C89 (body of main). ac_c_conftest_c89_main=' ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); ' # Test code for whether the C compiler supports C99 (global declarations) ac_c_conftest_c99_globals=' // Does the compiler advertise C99 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L # error "Compiler does not advertise C99 conformance" #endif #include extern int puts (const char *); extern int printf (const char *, ...); extern int dprintf (int, const char *, ...); extern void *malloc (size_t); // Check varargs macros. These examples are taken from C99 6.10.3.5. // dprintf is used instead of fprintf to avoid needing to declare // FILE and stderr. #define debug(...) dprintf (2, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK #error "your preprocessor is broken" #endif #if BIG_OK #else #error "your preprocessor is broken" #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) continue; return 0; } // Check varargs and va_copy. static bool test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str = ""; int number = 0; float fnumber = 0; while (*format) { switch (*format++) { case '\''s'\'': // string str = va_arg (args_copy, const char *); break; case '\''d'\'': // int number = va_arg (args_copy, int); break; case '\''f'\'': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); return *str && number && fnumber; } ' # Test code for whether the C compiler supports C99 (body of main). ac_c_conftest_c99_main=' // Check bool. _Bool success = false; success |= (argc != 0); // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[0] = argv[0][0]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' || dynamic_array[ni.number - 1] != 543); ' # Test code for whether the C compiler supports C11 (global declarations) ac_c_conftest_c11_globals=' // Does the compiler advertise C11 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L # error "Compiler does not advertise C11 conformance" #endif // Check _Alignas. char _Alignas (double) aligned_as_double; char _Alignas (0) no_special_alignment; extern char aligned_as_int; char _Alignas (0) _Alignas (int) aligned_as_int; // Check _Alignof. enum { int_alignment = _Alignof (int), int_array_alignment = _Alignof (int[100]), char_alignment = _Alignof (char) }; _Static_assert (0 < -_Alignof (int), "_Alignof is signed"); // Check _Noreturn. int _Noreturn does_not_return (void) { for (;;) continue; } // Check _Static_assert. struct test_static_assert { int x; _Static_assert (sizeof (int) <= sizeof (long int), "_Static_assert does not work in struct"); long int y; }; // Check UTF-8 literals. #define u8 syntax error! char const utf8_literal[] = u8"happens to be ASCII" "another string"; // Check duplicate typedefs. typedef long *long_ptr; typedef long int *long_ptr; typedef long_ptr long_ptr; // Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. struct anonymous { union { struct { int i; int j; }; struct { int k; long int l; } w; }; int m; } v1; ' # Test code for whether the C compiler supports C11 (body of main). ac_c_conftest_c11_main=' _Static_assert ((offsetof (struct anonymous, i) == offsetof (struct anonymous, w.k)), "Anonymous union alignment botch"); v1.i = 2; v1.w.k = 5; ok |= v1.i != 5; ' # Test code for whether the C compiler supports C11 (complete). ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} ${ac_c_conftest_c11_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} ${ac_c_conftest_c11_main} return ok; } " # Test code for whether the C compiler supports C99 (complete). ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} return ok; } " # Test code for whether the C compiler supports C89 (complete). ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} return ok; } " as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H" as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H" as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H" as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H" as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H" as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" as_fn_append ac_header_c_list " wchar.h wchar_h HAVE_WCHAR_H" as_fn_append ac_header_c_list " minix/config.h minix_config_h HAVE_MINIX_CONFIG_H" # Test code for whether the C++ compiler supports C++98 (global declarations) ac_cxx_conftest_cxx98_globals=' // Does the compiler advertise C++98 conformance? #if !defined __cplusplus || __cplusplus < 199711L # error "Compiler does not advertise C++98 conformance" #endif // These inclusions are to reject old compilers that // lack the unsuffixed header files. #include #include // and are *not* freestanding headers in C++98. extern void assert (int); namespace std { extern int strcmp (const char *, const char *); } // Namespaces, exceptions, and templates were all added after "C++ 2.0". using std::exception; using std::strcmp; namespace { void test_exception_syntax() { try { throw "test"; } catch (const char *s) { // Extra parentheses suppress a warning when building autoconf itself, // due to lint rules shared with more typical C programs. assert (!(strcmp) (s, "test")); } } template struct test_template { T const val; explicit test_template(T t) : val(t) {} template T add(U u) { return static_cast(u) + val; } }; } // anonymous namespace ' # Test code for whether the C++ compiler supports C++98 (body of main) ac_cxx_conftest_cxx98_main=' assert (argc); assert (! argv[0]); { test_exception_syntax (); test_template tt (2.0); assert (tt.add (4) == 6.0); assert (true && !false); } ' # Test code for whether the C++ compiler supports C++11 (global declarations) ac_cxx_conftest_cxx11_globals=' // Does the compiler advertise C++ 2011 conformance? #if !defined __cplusplus || __cplusplus < 201103L # error "Compiler does not advertise C++11 conformance" #endif namespace cxx11test { constexpr int get_val() { return 20; } struct testinit { int i; double d; }; class delegate { public: delegate(int n) : n(n) {} delegate(): delegate(2354) {} virtual int getval() { return this->n; }; protected: int n; }; class overridden : public delegate { public: overridden(int n): delegate(n) {} virtual int getval() override final { return this->n * 2; } }; class nocopy { public: nocopy(int i): i(i) {} nocopy() = default; nocopy(const nocopy&) = delete; nocopy & operator=(const nocopy&) = delete; private: int i; }; // for testing lambda expressions template Ret eval(Fn f, Ret v) { return f(v); } // for testing variadic templates and trailing return types template auto sum(V first) -> V { return first; } template auto sum(V first, Args... rest) -> V { return first + sum(rest...); } } ' # Test code for whether the C++ compiler supports C++11 (body of main) ac_cxx_conftest_cxx11_main=' { // Test auto and decltype auto a1 = 6538; auto a2 = 48573953.4; auto a3 = "String literal"; int total = 0; for (auto i = a3; *i; ++i) { total += *i; } decltype(a2) a4 = 34895.034; } { // Test constexpr short sa[cxx11test::get_val()] = { 0 }; } { // Test initializer lists cxx11test::testinit il = { 4323, 435234.23544 }; } { // Test range-based for int array[] = {9, 7, 13, 15, 4, 18, 12, 10, 5, 3, 14, 19, 17, 8, 6, 20, 16, 2, 11, 1}; for (auto &x : array) { x += 23; } } { // Test lambda expressions using cxx11test::eval; assert (eval ([](int x) { return x*2; }, 21) == 42); double d = 2.0; assert (eval ([&](double x) { return d += x; }, 3.0) == 5.0); assert (d == 5.0); assert (eval ([=](double x) mutable { return d += x; }, 4.0) == 9.0); assert (d == 5.0); } { // Test use of variadic templates using cxx11test::sum; auto a = sum(1); auto b = sum(1, 2); auto c = sum(1.0, 2.0, 3.0); } { // Test constructor delegation cxx11test::delegate d1; cxx11test::delegate d2(); cxx11test::delegate d3(45); } { // Test override and final cxx11test::overridden o1(55464); } { // Test nullptr char *c = nullptr; } { // Test template brackets test_template<::test_template> v(test_template(12)); } { // Unicode literals char const *utf8 = u8"UTF-8 string \u2500"; char16_t const *utf16 = u"UTF-8 string \u2500"; char32_t const *utf32 = U"UTF-32 string \u2500"; } ' # Test code for whether the C compiler supports C++11 (complete). ac_cxx_conftest_cxx11_program="${ac_cxx_conftest_cxx98_globals} ${ac_cxx_conftest_cxx11_globals} int main (int argc, char **argv) { int ok = 0; ${ac_cxx_conftest_cxx98_main} ${ac_cxx_conftest_cxx11_main} return ok; } " # Test code for whether the C compiler supports C++98 (complete). ac_cxx_conftest_cxx98_program="${ac_cxx_conftest_cxx98_globals} int main (int argc, char **argv) { int ok = 0; ${ac_cxx_conftest_cxx98_main} return ok; } " as_fn_append ac_header_c_list " vfork.h vfork_h HAVE_VFORK_H" as_fn_append ac_func_c_list " fork HAVE_FORK" as_fn_append ac_func_c_list " vfork HAVE_VFORK" # Auxiliary files required by this configure script. ac_aux_files="missing install-sh config.guess config.sub ltmain.sh compile" # Locations in which to look for auxiliary files. ac_aux_dir_candidates="${srcdir}/." # Search for a directory containing all of the required auxiliary files, # $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates. # If we don't find one directory that contains all the files we need, # we report the set of missing files from the *first* directory in # $ac_aux_dir_candidates and give up. ac_missing_aux_files="" ac_first_candidate=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in $ac_aux_dir_candidates do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5 ac_aux_dir_found=yes ac_install_sh= for ac_aux in $ac_aux_files do # As a special case, if "install-sh" is required, that requirement # can be satisfied by any of "install-sh", "install.sh", or "shtool", # and $ac_install_sh is set appropriately for whichever one is found. if test x"$ac_aux" = x"install-sh" then if test -f "${as_dir}install-sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5 ac_install_sh="${as_dir}install-sh -c" elif test -f "${as_dir}install.sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5 ac_install_sh="${as_dir}install.sh -c" elif test -f "${as_dir}shtool"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5 ac_install_sh="${as_dir}shtool install -c" else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} install-sh" else break fi fi else if test -f "${as_dir}${ac_aux}"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5 else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}" else break fi fi fi done if test "$ac_aux_dir_found" = yes; then ac_aux_dir="$as_dir" break fi ac_first_candidate=false as_found=false done IFS=$as_save_IFS if $as_found then : else $as_nop as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. if test -f "${ac_aux_dir}config.guess"; then ac_config_guess="$SHELL ${ac_aux_dir}config.guess" fi if test -f "${ac_aux_dir}config.sub"; then ac_config_sub="$SHELL ${ac_aux_dir}config.sub" fi if test -f "$ac_aux_dir/configure"; then ac_configure="$SHELL ${ac_aux_dir}configure" fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers config.h" # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. set dummy ${ac_tool_prefix}clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}clang" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "clang", so it can be a program name with args. set dummy clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="clang" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi fi test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion -version; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 printf %s "checking whether the C compiler works... " >&6; } ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else $as_nop ac_file='' fi if test -z "$ac_file" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 printf %s "checking for C compiler default output file name... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 printf "%s\n" "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 printf %s "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else $as_nop { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 printf "%s\n" "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 printf %s "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 printf "%s\n" "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 printf %s "checking for suffix of object files... " >&6; } if test ${ac_cv_objext+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 printf "%s\n" "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 printf %s "checking whether the compiler supports GNU C... " >&6; } if test ${ac_cv_c_compiler_gnu+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_compiler_gnu=yes else $as_nop ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } ac_compiler_gnu=$ac_cv_c_compiler_gnu if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+y} ac_save_CFLAGS=$CFLAGS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 printf %s "checking whether $CC accepts -g... " >&6; } if test ${ac_cv_prog_cc_g+y} then : printf %s "(cached) " >&6 else $as_nop ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes else $as_nop CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 printf "%s\n" "$ac_cv_prog_cc_g" >&6; } if test $ac_test_CFLAGS; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi ac_prog_cc_stdc=no if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 printf %s "checking for $CC option to enable C11 features... " >&6; } if test ${ac_cv_prog_cc_c11+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c11=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c11_program _ACEOF for ac_arg in '' -std=gnu11 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c11=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c11" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c11" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c11" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } CC="$CC $ac_cv_prog_cc_c11" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 ac_prog_cc_stdc=c11 fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 printf %s "checking for $CC option to enable C99 features... " >&6; } if test ${ac_cv_prog_cc_c99+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c99_program _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c99" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c99" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } CC="$CC $ac_cv_prog_cc_c99" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 ac_prog_cc_stdc=c99 fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 printf %s "checking for $CC option to enable C89 features... " >&6; } if test ${ac_cv_prog_cc_c89+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c89_program _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c89" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c89" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } CC="$CC $ac_cv_prog_cc_c89" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 ac_prog_cc_stdc=c89 fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 printf %s "checking whether $CC understands -c and -o together... " >&6; } if test ${am_cv_prog_cc_c_o+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 printf "%s\n" "$am_cv_prog_cc_c_o" >&6; } if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_header= ac_cache= for ac_item in $ac_header_c_list do if test $ac_cache; then ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then printf "%s\n" "#define $ac_item 1" >> confdefs.h fi ac_header= ac_cache= elif test $ac_header; then ac_cache=$ac_item else ac_header=$ac_item fi done if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes then : printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 printf %s "checking whether it is safe to define __EXTENSIONS__... " >&6; } if test ${ac_cv_safe_to_define___extensions__+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ # define __EXTENSIONS__ 1 $ac_includes_default int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_safe_to_define___extensions__=yes else $as_nop ac_cv_safe_to_define___extensions__=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 printf "%s\n" "$ac_cv_safe_to_define___extensions__" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether _XOPEN_SOURCE should be defined" >&5 printf %s "checking whether _XOPEN_SOURCE should be defined... " >&6; } if test ${ac_cv_should_define__xopen_source+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_should_define__xopen_source=no if test $ac_cv_header_wchar_h = yes then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include mbstate_t x; int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _XOPEN_SOURCE 500 #include mbstate_t x; int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_should_define__xopen_source=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_should_define__xopen_source" >&5 printf "%s\n" "$ac_cv_should_define__xopen_source" >&6; } printf "%s\n" "#define _ALL_SOURCE 1" >>confdefs.h printf "%s\n" "#define _DARWIN_C_SOURCE 1" >>confdefs.h printf "%s\n" "#define _GNU_SOURCE 1" >>confdefs.h printf "%s\n" "#define _HPUX_ALT_XOPEN_SOCKET_API 1" >>confdefs.h printf "%s\n" "#define _NETBSD_SOURCE 1" >>confdefs.h printf "%s\n" "#define _OPENBSD_SOURCE 1" >>confdefs.h printf "%s\n" "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h printf "%s\n" "#define __STDC_WANT_IEC_60559_ATTRIBS_EXT__ 1" >>confdefs.h printf "%s\n" "#define __STDC_WANT_IEC_60559_BFP_EXT__ 1" >>confdefs.h printf "%s\n" "#define __STDC_WANT_IEC_60559_DFP_EXT__ 1" >>confdefs.h printf "%s\n" "#define __STDC_WANT_IEC_60559_FUNCS_EXT__ 1" >>confdefs.h printf "%s\n" "#define __STDC_WANT_IEC_60559_TYPES_EXT__ 1" >>confdefs.h printf "%s\n" "#define __STDC_WANT_LIB_EXT2__ 1" >>confdefs.h printf "%s\n" "#define __STDC_WANT_MATH_SPEC_FUNCS__ 1" >>confdefs.h printf "%s\n" "#define _TANDEM_SOURCE 1" >>confdefs.h if test $ac_cv_header_minix_config_h = yes then : MINIX=yes printf "%s\n" "#define _MINIX 1" >>confdefs.h printf "%s\n" "#define _POSIX_SOURCE 1" >>confdefs.h printf "%s\n" "#define _POSIX_1_SOURCE 2" >>confdefs.h else $as_nop MINIX= fi if test $ac_cv_safe_to_define___extensions__ = yes then : printf "%s\n" "#define __EXTENSIONS__ 1" >>confdefs.h fi if test $ac_cv_should_define__xopen_source = yes then : printf "%s\n" "#define _XOPEN_SOURCE 500" >>confdefs.h fi case `pwd` in *\ * | *\ *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 printf "%s\n" "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; esac macro_version='2.4.7' macro_revision='2.4.7' ltmain=$ac_aux_dir/ltmain.sh # Make sure we can run config.sub. $SHELL "${ac_aux_dir}config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL ${ac_aux_dir}config.sub" "$LINENO" 5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 printf %s "checking build system type... " >&6; } if test ${ac_cv_build+y} then : printf %s "(cached) " >&6 else $as_nop ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "${ac_aux_dir}config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $ac_build_alias failed" "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 printf "%s\n" "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 printf %s "checking host system type... " >&6; } if test ${ac_cv_host+y} then : printf %s "(cached) " >&6 else $as_nop if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "${ac_aux_dir}config.sub" $host_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $host_alias failed" "$LINENO" 5 fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 printf "%s\n" "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac # Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\(["`$\\]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 printf %s "checking how to print strings... " >&6; } # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "" } case $ECHO in printf*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: printf" >&5 printf "%s\n" "printf" >&6; } ;; print*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 printf "%s\n" "print -r" >&6; } ;; *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: cat" >&5 printf "%s\n" "cat" >&6; } ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 printf %s "checking for a sed that does not truncate output... " >&6; } if test ${ac_cv_path_SED+y} then : printf %s "(cached) " >&6 else $as_nop ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for ac_i in 1 2 3 4 5 6 7; do ac_script="$ac_script$as_nl$ac_script" done echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed { ac_script=; unset ac_script;} if test -z "$SED"; then ac_path_SED_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in sed gsed do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_SED="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_SED" || continue # Check for GNU ac_path_SED and select it if it is found. # Check for GNU $ac_path_SED case `"$ac_path_SED" --version 2>&1` in *GNU*) ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; *) ac_count=0 printf %s 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" printf "%s\n" '' >> "conftest.nl" "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_SED_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_SED="$ac_path_SED" ac_path_SED_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_SED_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_SED"; then as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 fi else ac_cv_path_SED=$SED fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 printf "%s\n" "$ac_cv_path_SED" >&6; } SED="$ac_cv_path_SED" rm -f conftest.sed test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 printf %s "checking for grep that handles long lines and -e... " >&6; } if test ${ac_cv_path_GREP+y} then : printf %s "(cached) " >&6 else $as_nop if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in grep ggrep do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 printf %s 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" printf "%s\n" 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 printf "%s\n" "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 printf %s "checking for egrep... " >&6; } if test ${ac_cv_path_EGREP+y} then : printf %s "(cached) " >&6 else $as_nop if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in egrep do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 printf %s 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" printf "%s\n" 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 printf "%s\n" "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 printf %s "checking for fgrep... " >&6; } if test ${ac_cv_path_FGREP+y} then : printf %s "(cached) " >&6 else $as_nop if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 then ac_cv_path_FGREP="$GREP -F" else if test -z "$FGREP"; then ac_path_FGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in fgrep do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_FGREP="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_FGREP" || continue # Check for GNU ac_path_FGREP and select it if it is found. # Check for GNU $ac_path_FGREP case `"$ac_path_FGREP" --version 2>&1` in *GNU*) ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; *) ac_count=0 printf %s 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" printf "%s\n" 'FGREP' >> "conftest.nl" "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_FGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_FGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_FGREP"; then as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_FGREP=$FGREP fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 printf "%s\n" "$ac_cv_path_FGREP" >&6; } FGREP="$ac_cv_path_FGREP" test -z "$GREP" && GREP=grep # Check whether --with-gnu-ld was given. if test ${with_gnu_ld+y} then : withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes else $as_nop with_gnu_ld=no fi ac_prog=ld if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 printf %s "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test yes = "$with_gnu_ld"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 printf %s "checking for GNU ld... " >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 printf %s "checking for non-GNU ld... " >&6; } fi if test ${lt_cv_path_LD+y} then : printf %s "(cached) " >&6 else $as_nop if test -z "$LD"; then lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &5 printf "%s\n" "$LD" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 printf %s "checking if the linker ($LD) is GNU ld... " >&6; } if test ${lt_cv_prog_gnu_ld+y} then : printf %s "(cached) " >&6 else $as_nop # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 printf "%s\n" "$lt_cv_prog_gnu_ld" >&6; } with_gnu_ld=$lt_cv_prog_gnu_ld { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 printf %s "checking for BSD- or MS-compatible name lister (nm)... " >&6; } if test ${lt_cv_path_NM+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM=$NM else lt_nm_to_check=${ac_tool_prefix}nm if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. tmp_nm=$ac_dir/$lt_tmp_nm if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then # Check to see if the nm accepts a BSD-compat flag. # Adding the 'sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty case $build_os in mingw*) lt_bad_file=conftest.nm/nofile ;; *) lt_bad_file=/dev/null ;; esac case `"$tmp_nm" -B $lt_bad_file 2>&1 | $SED '1q'` in *$lt_bad_file* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break 2 ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | $SED '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break 2 ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS=$lt_save_ifs done : ${lt_cv_path_NM=no} fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 printf "%s\n" "$lt_cv_path_NM" >&6; } if test no != "$lt_cv_path_NM"; then NM=$lt_cv_path_NM else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else if test -n "$ac_tool_prefix"; then for ac_prog in dumpbin "link -dump" do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_DUMPBIN+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$DUMPBIN"; then ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DUMPBIN=$ac_cv_prog_DUMPBIN if test -n "$DUMPBIN"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 printf "%s\n" "$DUMPBIN" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$DUMPBIN" && break done fi if test -z "$DUMPBIN"; then ac_ct_DUMPBIN=$DUMPBIN for ac_prog in dumpbin "link -dump" do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_DUMPBIN+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_DUMPBIN"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN if test -n "$ac_ct_DUMPBIN"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 printf "%s\n" "$ac_ct_DUMPBIN" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$ac_ct_DUMPBIN" && break done if test "x$ac_ct_DUMPBIN" = x; then DUMPBIN=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DUMPBIN=$ac_ct_DUMPBIN fi fi case `$DUMPBIN -symbols -headers /dev/null 2>&1 | $SED '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols -headers" ;; *) DUMPBIN=: ;; esac fi if test : != "$DUMPBIN"; then NM=$DUMPBIN fi fi test -z "$NM" && NM=nm { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 printf %s "checking the name lister ($NM) interface... " >&6; } if test ${lt_cv_nm_interface+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 (eval echo "\"\$as_me:$LINENO: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest* fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 printf "%s\n" "$lt_cv_nm_interface" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 printf %s "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 printf "%s\n" "no, using $LN_S" >&6; } fi # find the maximum length of command line arguments { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 printf %s "checking the maximum length of command line arguments... " >&6; } if test ${lt_cv_sys_max_cmd_len+y} then : printf %s "(cached) " >&6 else $as_nop i=0 teststring=ABCD case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; bitrig* | darwin* | dragonfly* | freebsd* | midnightbsd* | netbsd* | openbsd*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | $SED 's/.*[ ]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len" && \ test undefined != "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test X`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test 17 != "$i" # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac fi if test -n "$lt_cv_sys_max_cmd_len"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 printf "%s\n" "$lt_cv_sys_max_cmd_len" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none" >&5 printf "%s\n" "none" >&6; } fi max_cmd_len=$lt_cv_sys_max_cmd_len : ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5 printf %s "checking how to convert $build file names to $host format... " >&6; } if test ${lt_cv_to_host_file_cmd+y} then : printf %s "(cached) " >&6 else $as_nop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac fi to_host_file_cmd=$lt_cv_to_host_file_cmd { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5 printf "%s\n" "$lt_cv_to_host_file_cmd" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5 printf %s "checking how to convert $build file names to toolchain format... " >&6; } if test ${lt_cv_to_tool_file_cmd+y} then : printf %s "(cached) " >&6 else $as_nop #assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac fi to_tool_file_cmd=$lt_cv_to_tool_file_cmd { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5 printf "%s\n" "$lt_cv_to_tool_file_cmd" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 printf %s "checking for $LD option to reload object files... " >&6; } if test ${lt_cv_ld_reload_flag+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_ld_reload_flag='-r' fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 printf "%s\n" "$lt_cv_ld_reload_flag" >&6; } reload_flag=$lt_cv_ld_reload_flag case $reload_flag in "" | " "*) ;; *) reload_flag=" $reload_flag" ;; esac reload_cmds='$LD$reload_flag -o $output$reload_objs' case $host_os in cygwin* | mingw* | pw32* | cegcc*) if test yes != "$GCC"; then reload_cmds=false fi ;; darwin*) if test yes = "$GCC"; then reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs' else reload_cmds='$LD$reload_flag -o $output$reload_objs' fi ;; esac if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}file", so it can be a program name with args. set dummy ${ac_tool_prefix}file; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_FILECMD+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$FILECMD"; then ac_cv_prog_FILECMD="$FILECMD" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_FILECMD="${ac_tool_prefix}file" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi FILECMD=$ac_cv_prog_FILECMD if test -n "$FILECMD"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $FILECMD" >&5 printf "%s\n" "$FILECMD" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_FILECMD"; then ac_ct_FILECMD=$FILECMD # Extract the first word of "file", so it can be a program name with args. set dummy file; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_FILECMD+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_FILECMD"; then ac_cv_prog_ac_ct_FILECMD="$ac_ct_FILECMD" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_FILECMD="file" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_FILECMD=$ac_cv_prog_ac_ct_FILECMD if test -n "$ac_ct_FILECMD"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_FILECMD" >&5 printf "%s\n" "$ac_ct_FILECMD" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_FILECMD" = x; then FILECMD=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac FILECMD=$ac_ct_FILECMD fi else FILECMD="$ac_cv_prog_FILECMD" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. set dummy ${ac_tool_prefix}objdump; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_OBJDUMP+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$OBJDUMP"; then ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OBJDUMP=$ac_cv_prog_OBJDUMP if test -n "$OBJDUMP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 printf "%s\n" "$OBJDUMP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_OBJDUMP"; then ac_ct_OBJDUMP=$OBJDUMP # Extract the first word of "objdump", so it can be a program name with args. set dummy objdump; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_OBJDUMP+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_OBJDUMP"; then ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OBJDUMP="objdump" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP if test -n "$ac_ct_OBJDUMP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 printf "%s\n" "$ac_ct_OBJDUMP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_OBJDUMP" = x; then OBJDUMP="false" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OBJDUMP=$ac_ct_OBJDUMP fi else OBJDUMP="$ac_cv_prog_OBJDUMP" fi test -z "$OBJDUMP" && OBJDUMP=objdump { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 printf %s "checking how to recognize dependent libraries... " >&6; } if test ${lt_cv_deplibs_check_method+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_file_magic_cmd='$MAGIC_CMD' lt_cv_file_magic_test_file= lt_cv_deplibs_check_method='unknown' # Need to set the preceding variable on all platforms that support # interlibrary dependencies. # 'none' -- dependencies not supported. # 'unknown' -- same as none, but documents that we really don't know. # 'pass_all' -- all dependencies passed with no checks. # 'test_compile' -- check by making test program. # 'file_magic [[regex]]' -- check by looking for files in library path # that responds to the $file_magic_cmd with a given extended regex. # If you have 'file' or equivalent on your system and you're not sure # whether 'pass_all' will *always* work, you probably want this one. case $host_os in aix[4-9]*) lt_cv_deplibs_check_method=pass_all ;; beos*) lt_cv_deplibs_check_method=pass_all ;; bsdi[45]*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' lt_cv_file_magic_cmd='$FILECMD -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; cygwin*) # func_win32_libid is a shell function defined in ltmain.sh lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' ;; mingw* | pw32*) # Base MSYS/MinGW do not provide the 'file' command needed by # func_win32_libid shell function, so use a weaker test based on 'objdump', # unless we find 'file', for example because we are cross-compiling. if ( file / ) >/dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly* | midnightbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=$FILECMD lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=$FILECMD case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]' lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[3-9]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=$FILECMD lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd* | bitrig*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; os2*) lt_cv_deplibs_check_method=pass_all ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 printf "%s\n" "$lt_cv_deplibs_check_method" >&6; } file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. set dummy ${ac_tool_prefix}dlltool; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_DLLTOOL+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$DLLTOOL"; then ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DLLTOOL=$ac_cv_prog_DLLTOOL if test -n "$DLLTOOL"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 printf "%s\n" "$DLLTOOL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_DLLTOOL"; then ac_ct_DLLTOOL=$DLLTOOL # Extract the first word of "dlltool", so it can be a program name with args. set dummy dlltool; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_DLLTOOL+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_DLLTOOL"; then ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DLLTOOL="dlltool" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL if test -n "$ac_ct_DLLTOOL"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 printf "%s\n" "$ac_ct_DLLTOOL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_DLLTOOL" = x; then DLLTOOL="false" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DLLTOOL=$ac_ct_DLLTOOL fi else DLLTOOL="$ac_cv_prog_DLLTOOL" fi test -z "$DLLTOOL" && DLLTOOL=dlltool { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5 printf %s "checking how to associate runtime and link libraries... " >&6; } if test ${lt_cv_sharedlib_from_linklib_cmd+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh; # decide which one to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd=$ECHO ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5 printf "%s\n" "$lt_cv_sharedlib_from_linklib_cmd" >&6; } sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO if test -n "$ac_tool_prefix"; then for ac_prog in ar do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_AR+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_AR="$ac_tool_prefix$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 printf "%s\n" "$AR" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$AR" && break done fi if test -z "$AR"; then ac_ct_AR=$AR for ac_prog in ar do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_AR+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_AR="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 printf "%s\n" "$ac_ct_AR" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$ac_ct_AR" && break done if test "x$ac_ct_AR" = x; then AR="false" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi fi : ${AR=ar} # Use ARFLAGS variable as AR's operation code to sync the variable naming with # Automake. If both AR_FLAGS and ARFLAGS are specified, AR_FLAGS should have # higher priority because thats what people were doing historically (setting # ARFLAGS for automake and AR_FLAGS for libtool). FIXME: Make the AR_FLAGS # variable obsoleted/removed. test ${AR_FLAGS+y} || AR_FLAGS=${ARFLAGS-cr} lt_ar_flags=$AR_FLAGS # Make AR_FLAGS overridable by 'make ARFLAGS='. Don't try to run-time override # by AR_FLAGS because that was never working and AR_FLAGS is about to die. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5 printf %s "checking for archiver @FILE support... " >&6; } if test ${lt_cv_ar_at_file+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_ar_at_file=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5' { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 (eval $lt_ar_try) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test 0 -eq "$ac_status"; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 (eval $lt_ar_try) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test 0 -ne "$ac_status"; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5 printf "%s\n" "$lt_cv_ar_at_file" >&6; } if test no = "$lt_cv_ar_at_file"; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_STRIP+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 printf "%s\n" "$STRIP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_STRIP+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 printf "%s\n" "$ac_ct_STRIP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi test -z "$STRIP" && STRIP=: if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_RANLIB+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 printf "%s\n" "$RANLIB" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_RANLIB+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 printf "%s\n" "$ac_ct_RANLIB" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi test -z "$RANLIB" && RANLIB=: # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in bitrig* | openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_AWK+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 printf "%s\n" "$AWK" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$AWK" && break done # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Check for command to grab the raw symbol name followed by C symbol from nm. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 printf %s "checking command to parse $NM output from $compiler object... " >&6; } if test ${lt_cv_sys_global_symbol_pipe+y} then : printf %s "(cached) " >&6 else $as_nop # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[BCDEGRST]' # Regexp to match symbols that can be accessed directly from C. sympat='\([_A-Za-z][_A-Za-z0-9]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[BCDT]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[ABCDGISTW]' ;; hpux*) if test ia64 = "$host_cpu"; then symcode='[ABCDEGRST]' fi ;; irix* | nonstopux*) symcode='[BCDEGRST]' ;; osf*) symcode='[BCDEGQRST]' ;; solaris*) symcode='[BDRT]' ;; sco3.2v5*) symcode='[DT]' ;; sysv4.2uw2*) symcode='[DT]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[ABDT]' ;; sysv4) symcode='[DFNSTU]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[ABCDGIRSTW]' ;; esac if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Gets list of data symbols to import. lt_cv_sys_global_symbol_to_import="$SED -n -e 's/^I .* \(.*\)$/\1/p'" # Adjust the below global symbol transforms to fixup imported variables. lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" lt_c_name_lib_hook="\ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" else # Disable hooks by default. lt_cv_sys_global_symbol_to_import= lt_cdecl_hook= lt_c_name_hook= lt_c_name_lib_hook= fi # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="$SED -n"\ $lt_cdecl_hook\ " -e 's/^T .* \(.*\)$/extern int \1();/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="$SED -n"\ $lt_c_name_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" # Transform an extracted symbol line into symbol name with lib prefix and # symbol address. lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="$SED -n"\ $lt_c_name_lib_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function, # D for any global variable and I for any imported variable. # Also find C++ and __fastcall symbols from MSVC++ or ICC, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK '"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ " /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ " /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ " {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ " s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx" else lt_cv_sys_global_symbol_pipe="$SED -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | $SED '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then # Now try to grab the symbols. nlist=conftest.nm $ECHO "$as_me:$LINENO: $NM conftest.$ac_objext | $lt_cv_sys_global_symbol_pipe > $nlist" >&5 if eval "$NM" conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist 2>&5 && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS=conftstm.$ac_objext CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest$ac_exeext; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&5 fi else echo "cannot find nm_test_var in $nlist" >&5 fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 fi else echo "$progname: failed program was:" >&5 cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test yes = "$pipe_works"; then break else lt_cv_sys_global_symbol_pipe= fi done fi if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: failed" >&5 printf "%s\n" "failed" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ok" >&5 printf "%s\n" "ok" >&6; } fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then nm_file_list_spec='@' fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5 printf %s "checking for sysroot... " >&6; } # Check whether --with-sysroot was given. if test ${with_sysroot+y} then : withval=$with_sysroot; else $as_nop with_sysroot=no fi lt_sysroot= case $with_sysroot in #( yes) if test yes = "$GCC"; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | $SED -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_sysroot" >&5 printf "%s\n" "$with_sysroot" >&6; } as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5 ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5 printf "%s\n" "${lt_sysroot:-no}" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a working dd" >&5 printf %s "checking for a working dd... " >&6; } if test ${ac_cv_path_lt_DD+y} then : printf %s "(cached) " >&6 else $as_nop printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i : ${lt_DD:=$DD} if test -z "$lt_DD"; then ac_path_lt_DD_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in dd do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_lt_DD="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_lt_DD" || continue if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: fi $ac_path_lt_DD_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_lt_DD"; then : fi else ac_cv_path_lt_DD=$lt_DD fi rm -f conftest.i conftest2.i conftest.out fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_lt_DD" >&5 printf "%s\n" "$ac_cv_path_lt_DD" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to truncate binary pipes" >&5 printf %s "checking how to truncate binary pipes... " >&6; } if test ${lt_cv_truncate_bin+y} then : printf %s "(cached) " >&6 else $as_nop printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i lt_cv_truncate_bin= if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" fi rm -f conftest.i conftest2.i conftest.out test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_truncate_bin" >&5 printf "%s\n" "$lt_cv_truncate_bin" >&6; } # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in $*""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } # Check whether --enable-libtool-lock was given. if test ${enable_libtool_lock+y} then : enableval=$enable_libtool_lock; fi test no = "$enable_libtool_lock" || enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out what ABI is being produced by ac_compile, and set mode # options accordingly. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `$FILECMD conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE=32 ;; *ELF-64*) HPUX_IA64_MODE=64 ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '#line '$LINENO' "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then if test yes = "$lt_cv_prog_gnu_ld"; then case `$FILECMD conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `$FILECMD conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; mips64*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '#line '$LINENO' "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then emul=elf case `$FILECMD conftest.$ac_objext` in *32-bit*) emul="${emul}32" ;; *64-bit*) emul="${emul}64" ;; esac case `$FILECMD conftest.$ac_objext` in *MSB*) emul="${emul}btsmip" ;; *LSB*) emul="${emul}ltsmip" ;; esac case `$FILECMD conftest.$ac_objext` in *N32*) emul="${emul}n32" ;; esac LD="${LD-ld} -m $emul" fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. Note that the listed cases only cover the # situations where additional linker options are needed (such as when # doing 32-bit compilation for a host where ld defaults to 64-bit, or # vice versa); the common cases where no linker options are needed do # not appear in the list. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `$FILECMD conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) case `$FILECMD conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; *) LD="${LD-ld} -m elf_i386" ;; esac ;; powerpc64le-*linux*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*linux*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -belf" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 printf %s "checking whether the C compiler needs -belf... " >&6; } if test ${lt_cv_cc_needs_belf+y} then : printf %s "(cached) " >&6 else $as_nop ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : lt_cv_cc_needs_belf=yes else $as_nop lt_cv_cc_needs_belf=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 printf "%s\n" "$lt_cv_cc_needs_belf" >&6; } if test yes != "$lt_cv_cc_needs_belf"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS=$SAVE_CFLAGS fi ;; *-*solaris*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `$FILECMD conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*|x86_64-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD=${LD-ld}_sol2 fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks=$enable_libtool_lock if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args. set dummy ${ac_tool_prefix}mt; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_MANIFEST_TOOL+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$MANIFEST_TOOL"; then ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL if test -n "$MANIFEST_TOOL"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5 printf "%s\n" "$MANIFEST_TOOL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_MANIFEST_TOOL"; then ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL # Extract the first word of "mt", so it can be a program name with args. set dummy mt; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_MANIFEST_TOOL+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_MANIFEST_TOOL"; then ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_MANIFEST_TOOL="mt" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL if test -n "$ac_ct_MANIFEST_TOOL"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5 printf "%s\n" "$ac_ct_MANIFEST_TOOL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_MANIFEST_TOOL" = x; then MANIFEST_TOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL fi else MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL" fi test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5 printf %s "checking if $MANIFEST_TOOL is a manifest tool... " >&6; } if test ${lt_cv_path_mainfest_tool+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5 $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&5 if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest* fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5 printf "%s\n" "$lt_cv_path_mainfest_tool" >&6; } if test yes != "$lt_cv_path_mainfest_tool"; then MANIFEST_TOOL=: fi case $host_os in rhapsody* | darwin*) if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_DSYMUTIL+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$DSYMUTIL"; then ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DSYMUTIL=$ac_cv_prog_DSYMUTIL if test -n "$DSYMUTIL"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 printf "%s\n" "$DSYMUTIL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_DSYMUTIL"; then ac_ct_DSYMUTIL=$DSYMUTIL # Extract the first word of "dsymutil", so it can be a program name with args. set dummy dsymutil; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_DSYMUTIL+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_DSYMUTIL"; then ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL if test -n "$ac_ct_DSYMUTIL"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 printf "%s\n" "$ac_ct_DSYMUTIL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_DSYMUTIL" = x; then DSYMUTIL=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DSYMUTIL=$ac_ct_DSYMUTIL fi else DSYMUTIL="$ac_cv_prog_DSYMUTIL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. set dummy ${ac_tool_prefix}nmedit; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_NMEDIT+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$NMEDIT"; then ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi NMEDIT=$ac_cv_prog_NMEDIT if test -n "$NMEDIT"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 printf "%s\n" "$NMEDIT" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_NMEDIT"; then ac_ct_NMEDIT=$NMEDIT # Extract the first word of "nmedit", so it can be a program name with args. set dummy nmedit; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_NMEDIT+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_NMEDIT"; then ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_NMEDIT="nmedit" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT if test -n "$ac_ct_NMEDIT"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 printf "%s\n" "$ac_ct_NMEDIT" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_NMEDIT" = x; then NMEDIT=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac NMEDIT=$ac_ct_NMEDIT fi else NMEDIT="$ac_cv_prog_NMEDIT" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. set dummy ${ac_tool_prefix}lipo; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_LIPO+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$LIPO"; then ac_cv_prog_LIPO="$LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_LIPO="${ac_tool_prefix}lipo" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi LIPO=$ac_cv_prog_LIPO if test -n "$LIPO"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 printf "%s\n" "$LIPO" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_LIPO"; then ac_ct_LIPO=$LIPO # Extract the first word of "lipo", so it can be a program name with args. set dummy lipo; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_LIPO+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_LIPO"; then ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_LIPO="lipo" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO if test -n "$ac_ct_LIPO"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 printf "%s\n" "$ac_ct_LIPO" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_LIPO" = x; then LIPO=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac LIPO=$ac_ct_LIPO fi else LIPO="$ac_cv_prog_LIPO" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. set dummy ${ac_tool_prefix}otool; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_OTOOL+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$OTOOL"; then ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_OTOOL="${ac_tool_prefix}otool" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL=$ac_cv_prog_OTOOL if test -n "$OTOOL"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 printf "%s\n" "$OTOOL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL"; then ac_ct_OTOOL=$OTOOL # Extract the first word of "otool", so it can be a program name with args. set dummy otool; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_OTOOL+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_OTOOL"; then ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OTOOL="otool" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL if test -n "$ac_ct_OTOOL"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 printf "%s\n" "$ac_ct_OTOOL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_OTOOL" = x; then OTOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL=$ac_ct_OTOOL fi else OTOOL="$ac_cv_prog_OTOOL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. set dummy ${ac_tool_prefix}otool64; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_OTOOL64+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$OTOOL64"; then ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL64=$ac_cv_prog_OTOOL64 if test -n "$OTOOL64"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 printf "%s\n" "$OTOOL64" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL64"; then ac_ct_OTOOL64=$OTOOL64 # Extract the first word of "otool64", so it can be a program name with args. set dummy otool64; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_OTOOL64+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_OTOOL64"; then ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OTOOL64="otool64" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 if test -n "$ac_ct_OTOOL64"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 printf "%s\n" "$ac_ct_OTOOL64" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_OTOOL64" = x; then OTOOL64=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL64=$ac_ct_OTOOL64 fi else OTOOL64="$ac_cv_prog_OTOOL64" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 printf %s "checking for -single_module linker flag... " >&6; } if test ${lt_cv_apple_cc_single_mod+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_apple_cc_single_mod=no if test -z "$LT_MULTI_MODULE"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&5 $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&5 # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test 0 = "$_lt_result"; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&5 fi rm -rf libconftest.dylib* rm -f conftest.* fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 printf "%s\n" "$lt_cv_apple_cc_single_mod" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 printf %s "checking for -exported_symbols_list linker flag... " >&6; } if test ${lt_cv_ld_exported_symbols_list+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : lt_cv_ld_exported_symbols_list=yes else $as_nop lt_cv_ld_exported_symbols_list=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 printf "%s\n" "$lt_cv_ld_exported_symbols_list" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 printf %s "checking for -force_load linker flag... " >&6; } if test ${lt_cv_ld_force_load+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 echo "$AR $AR_FLAGS libconftest.a conftest.o" >&5 $AR $AR_FLAGS libconftest.a conftest.o 2>&5 echo "$RANLIB libconftest.a" >&5 $RANLIB libconftest.a 2>&5 cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&5 elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then lt_cv_ld_force_load=yes else cat conftest.err >&5 fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 printf "%s\n" "$lt_cv_ld_force_load" >&6; } case $host_os in rhapsody* | darwin1.[012]) _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; darwin*) case $MACOSX_DEPLOYMENT_TARGET,$host in 10.[012],*|,*powerpc*-darwin[5-8]*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; *) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test yes = "$lt_cv_apple_cc_single_mod"; then _lt_dar_single_mod='$single_module' fi if test yes = "$lt_cv_ld_exported_symbols_list"; then _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' fi if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac # func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x$2 in x) ;; *:) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" ;; x:*) eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" ;; *::*) eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" ;; *) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" ;; esac } ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default " if test "x$ac_cv_header_dlfcn_h" = xyes then : printf "%s\n" "#define HAVE_DLFCN_H 1" >>confdefs.h fi # Set options enable_dlopen=no enable_win32_dll=no # Check whether --enable-shared was given. if test ${enable_shared+y} then : enableval=$enable_shared; p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS=$lt_save_ifs ;; esac else $as_nop enable_shared=yes fi # Check whether --enable-static was given. if test ${enable_static+y} then : enableval=$enable_static; p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS=$lt_save_ifs ;; esac else $as_nop enable_static=yes fi # Check whether --with-pic was given. if test ${with_pic+y} then : withval=$with_pic; lt_p=${PACKAGE-default} case $withval in yes|no) pic_mode=$withval ;; *) pic_mode=default # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for lt_pkg in $withval; do IFS=$lt_save_ifs if test "X$lt_pkg" = "X$lt_p"; then pic_mode=yes fi done IFS=$lt_save_ifs ;; esac else $as_nop pic_mode=default fi # Check whether --enable-fast-install was given. if test ${enable_fast_install+y} then : enableval=$enable_fast_install; p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS=$lt_save_ifs ;; esac else $as_nop enable_fast_install=yes fi shared_archive_member_spec= case $host,$enable_shared in power*-*-aix[5-9]*,yes) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking which variant of shared library versioning to provide" >&5 printf %s "checking which variant of shared library versioning to provide... " >&6; } # Check whether --with-aix-soname was given. if test ${with_aix_soname+y} then : withval=$with_aix_soname; case $withval in aix|svr4|both) ;; *) as_fn_error $? "Unknown argument to --with-aix-soname" "$LINENO" 5 ;; esac lt_cv_with_aix_soname=$with_aix_soname else $as_nop if test ${lt_cv_with_aix_soname+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_with_aix_soname=aix fi with_aix_soname=$lt_cv_with_aix_soname fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_aix_soname" >&5 printf "%s\n" "$with_aix_soname" >&6; } if test aix != "$with_aix_soname"; then # For the AIX way of multilib, we name the shared archive member # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, # the AIX toolchain works better with OBJECT_MODE set (default 32). if test 64 = "${OBJECT_MODE-32}"; then shared_archive_member_spec=shr_64 else shared_archive_member_spec=shr fi fi ;; *) with_aix_soname=aix ;; esac # This can be used to rebuild libtool when needed LIBTOOL_DEPS=$ltmain # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' test -z "$LN_S" && LN_S="ln -s" if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 printf %s "checking for objdir... " >&6; } if test ${lt_cv_objdir+y} then : printf %s "(cached) " >&6 else $as_nop rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 printf "%s\n" "$lt_cv_objdir" >&6; } objdir=$lt_cv_objdir printf "%s\n" "#define LT_OBJDIR \"$lt_cv_objdir/\"" >>confdefs.h case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a '.a' archive for static linking (except MSVC and # ICC, which need '.lib'). libext=a with_gnu_ld=$lt_cv_prog_gnu_ld old_CC=$CC old_CFLAGS=$CFLAGS # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o func_cc_basename $compiler cc_basename=$func_cc_basename_result # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 printf %s "checking for ${ac_tool_prefix}file... " >&6; } if test ${lt_cv_path_MAGIC_CMD+y} then : printf %s "(cached) " >&6 else $as_nop case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/${ac_tool_prefix}file"; then lt_cv_path_MAGIC_CMD=$ac_dir/"${ac_tool_prefix}file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac fi MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 printf "%s\n" "$MAGIC_CMD" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for file" >&5 printf %s "checking for file... " >&6; } if test ${lt_cv_path_MAGIC_CMD+y} then : printf %s "(cached) " >&6 else $as_nop case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/file"; then lt_cv_path_MAGIC_CMD=$ac_dir/"file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac fi MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 printf "%s\n" "$MAGIC_CMD" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi else MAGIC_CMD=: fi fi fi ;; esac # Use C for the default configuration in the libtool script lt_save_CC=$CC ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o objext=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then lt_prog_compiler_no_builtin_flag= if test yes = "$GCC"; then case $cc_basename in nvcc*) lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; *) lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 printf %s "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } if test ${lt_cv_prog_compiler_rtti_exceptions+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_prog_compiler_rtti_exceptions=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-fno-rtti -fno-exceptions" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_rtti_exceptions=yes fi fi $RM conftest* fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 printf "%s\n" "$lt_cv_prog_compiler_rtti_exceptions" >&6; } if test yes = "$lt_cv_prog_compiler_rtti_exceptions"; then lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" else : fi fi lt_prog_compiler_wl= lt_prog_compiler_pic= lt_prog_compiler_static= if test yes = "$GCC"; then lt_prog_compiler_wl='-Wl,' lt_prog_compiler_static='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' fi lt_prog_compiler_pic='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support lt_prog_compiler_pic='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries lt_prog_compiler_pic='-DDLL_EXPORT' case $host_os in os2*) lt_prog_compiler_static='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. lt_prog_compiler_static= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) lt_prog_compiler_pic='-fPIC' ;; esac ;; interix[3-9]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. lt_prog_compiler_can_build_shared=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic=-Kconform_pic fi ;; *) lt_prog_compiler_pic='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 lt_prog_compiler_wl='-Xlinker ' if test -n "$lt_prog_compiler_pic"; then lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) lt_prog_compiler_wl='-Wl,' if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' else lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' fi ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic='-fno-common' case $cc_basename in nagfor*) # NAG Fortran compiler lt_prog_compiler_wl='-Wl,-Wl,,' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; esac ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). lt_prog_compiler_pic='-DDLL_EXPORT' case $host_os in os2*) lt_prog_compiler_static='$wl-static' ;; esac ;; hpux9* | hpux10* | hpux11*) lt_prog_compiler_wl='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) lt_prog_compiler_pic='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? lt_prog_compiler_static='$wl-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) lt_prog_compiler_wl='-Wl,' # PIC (with -KPIC) is the default. lt_prog_compiler_static='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in # old Intel for x86_64, which still supported -KPIC. ecc*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-static' ;; # flang / f18. f95 an alias for gfortran or flang on Debian flang* | f18* | f95*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; # Lahey Fortran 8.1. lf95*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='--shared' lt_prog_compiler_static='--static' ;; nagfor*) # NAG Fortran compiler lt_prog_compiler_wl='-Wl,-Wl,,' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; tcc*) # Fabrice Bellard et al's Tiny C Compiler lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; ccc*) lt_prog_compiler_wl='-Wl,' # All Alpha code is PIC. lt_prog_compiler_static='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-qpic' lt_prog_compiler_static='-qstaticlink' ;; *) case `$CC -V 2>&1 | $SED 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='' ;; *Sun\ F* | *Sun*Fortran*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Wl,' ;; *Intel*\ [CF]*Compiler*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; *Portland\ Group*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; esac ;; esac ;; newsos6) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; osf3* | osf4* | osf5*) lt_prog_compiler_wl='-Wl,' # All OSF/1 code is PIC. lt_prog_compiler_static='-non_shared' ;; rdos*) lt_prog_compiler_static='-non_shared' ;; solaris*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) lt_prog_compiler_wl='-Qoption ld ';; *) lt_prog_compiler_wl='-Wl,';; esac ;; sunos4*) lt_prog_compiler_wl='-Qoption ld ' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic='-Kconform_pic' lt_prog_compiler_static='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; unicos*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_can_build_shared=no ;; uts4*) lt_prog_compiler_pic='-pic' lt_prog_compiler_static='-Bstatic' ;; *) lt_prog_compiler_can_build_shared=no ;; esac fi case $host_os in # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) lt_prog_compiler_pic= ;; *) lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 printf %s "checking for $compiler option to produce PIC... " >&6; } if test ${lt_cv_prog_compiler_pic+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_prog_compiler_pic=$lt_prog_compiler_pic fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5 printf "%s\n" "$lt_cv_prog_compiler_pic" >&6; } lt_prog_compiler_pic=$lt_cv_prog_compiler_pic # # Check to make sure the PIC flag actually works. # if test -n "$lt_prog_compiler_pic"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 printf %s "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } if test ${lt_cv_prog_compiler_pic_works+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_prog_compiler_pic_works=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$lt_prog_compiler_pic -DPIC" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_pic_works=yes fi fi $RM conftest* fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 printf "%s\n" "$lt_cv_prog_compiler_pic_works" >&6; } if test yes = "$lt_cv_prog_compiler_pic_works"; then case $lt_prog_compiler_pic in "" | " "*) ;; *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; esac else lt_prog_compiler_pic= lt_prog_compiler_can_build_shared=no fi fi # # Check to make sure the static flag actually works. # wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 printf %s "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } if test ${lt_cv_prog_compiler_static_works+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_prog_compiler_static_works=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $lt_tmp_static_flag" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_static_works=yes fi else lt_cv_prog_compiler_static_works=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 printf "%s\n" "$lt_cv_prog_compiler_static_works" >&6; } if test yes = "$lt_cv_prog_compiler_static_works"; then : else lt_prog_compiler_static= fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 printf %s "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if test ${lt_cv_prog_compiler_c_o+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 printf "%s\n" "$lt_cv_prog_compiler_c_o" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 printf %s "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if test ${lt_cv_prog_compiler_c_o+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 printf "%s\n" "$lt_cv_prog_compiler_c_o" >&6; } hard_links=nottested if test no = "$lt_cv_prog_compiler_c_o" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 printf %s "checking if we can lock with hard links... " >&6; } hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 printf "%s\n" "$hard_links" >&6; } if test no = "$hard_links"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5 printf "%s\n" "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;} need_locks=warn fi else need_locks=no fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 printf %s "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } runpath_var= allow_undefined_flag= always_export_symbols=no archive_cmds= archive_expsym_cmds= compiler_needs_object=no enable_shared_with_static_runtimes=no export_dynamic_flag_spec= export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' hardcode_automatic=no hardcode_direct=no hardcode_direct_absolute=no hardcode_libdir_flag_spec= hardcode_libdir_separator= hardcode_minus_L=no hardcode_shlibpath_var=unsupported inherit_rpath=no link_all_deplibs=unknown module_cmds= module_expsym_cmds= old_archive_from_new_cmds= old_archive_from_expsyms_cmds= thread_safe_flag_spec= whole_archive_flag_spec= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list include_expsyms= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ' (' and ')$', so one must not match beginning or # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', # as well as any symbol that contains 'd'. exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ and ICC port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++ or Intel C++ Compiler. if test yes != "$GCC"; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++ or ICC) with_gnu_ld=yes ;; openbsd* | bitrig*) with_gnu_ld=no ;; linux* | k*bsd*-gnu | gnu*) link_all_deplibs=no ;; esac ld_shlibs=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test yes = "$with_gnu_ld"; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;; *\ \(GNU\ Binutils\)\ [3-9]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test yes = "$lt_use_gnu_ld_interface"; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='$wl' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' export_dynamic_flag_spec='$wl--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then whole_archive_flag_spec=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else whole_archive_flag_spec= fi supports_anon_versioning=no case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[3-9]*) # On AIX/PPC, the GNU linker is very broken if test ia64 != "$host_cpu"; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then allow_undefined_flag=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else ld_shlibs=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, # as there is no search path for DLLs. hardcode_libdir_flag_spec='-L$libdir' export_dynamic_flag_spec='$wl--export-all-symbols' allow_undefined_flag=unsupported always_export_symbols=no enable_shared_with_static_runtimes=yes export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else ld_shlibs=no fi ;; haiku*) archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' link_all_deplibs=yes ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes allow_undefined_flag=unsupported shrext_cmds=.dll archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes=yes file_list_spec='@' ;; interix[3-9]*) hardcode_direct=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='$wl-rpath,$libdir' export_dynamic_flag_spec='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' archive_expsym_cmds='$SED "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test linux-dietlibc = "$host_os"; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test no = "$tmp_diet" then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 whole_archive_flag_spec= tmp_sharedflag='--shared' ;; nagfor*) # NAGFOR 5.3 tmp_sharedflag='-Wl,-shared' ;; xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object=yes ;; esac case `$CC -V 2>&1 | $SED 5q` in *Sun\ C*) # Sun C 5.9 whole_archive_flag_spec='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi case $cc_basename in tcc*) hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' export_dynamic_flag_spec='-rdynamic' ;; xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else ld_shlibs=no fi ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac ;; sunos4*) archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= hardcode_direct=yes hardcode_shlibpath_var=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac if test no = "$ld_shlibs"; then runpath_var= hardcode_libdir_flag_spec= export_dynamic_flag_spec= whole_archive_flag_spec= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) allow_undefined_flag=unsupported always_export_symbols=yes archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. hardcode_minus_L=yes if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. hardcode_direct=unsupported fi ;; aix[4-9]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then aix_use_runtimelinking=yes break fi done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. archive_cmds='' hardcode_direct=yes hardcode_direct_absolute=yes hardcode_libdir_separator=':' link_all_deplibs=yes file_list_spec='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # traditional, no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. hardcode_direct=no hardcode_direct_absolute=no ;; esac if test yes = "$GCC"; then case $host_os in aix4.[012]|aix4.[012].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking hardcode_minus_L=yes hardcode_libdir_flag_spec='-L$libdir' hardcode_libdir_separator= fi ;; esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag="$shared_flag "'$wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi export_dynamic_flag_spec='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. always_export_symbols=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. allow_undefined_flag='-berok' # Determine the default libpath from the value encoded in an # empty executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if test ${lt_cv_aix_libpath_+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath_ fi hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then hardcode_libdir_flag_spec='$wl-R $libdir:/usr/lib:/lib' allow_undefined_flag="-z nodefs" archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if test ${lt_cv_aix_libpath_+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath_ fi hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. no_undefined_flag=' $wl-bernotok' allow_undefined_flag=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. whole_archive_flag_spec='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives whole_archive_flag_spec='$convenience' fi archive_cmds_need_lc=yes archive_expsym_cmds='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared libraries. archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols archive_expsym_cmds="$archive_expsym_cmds"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi archive_expsym_cmds="$archive_expsym_cmds"'~$RM -r $output_objdir/$realname.d' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; bsdi[45]*) export_dynamic_flag_spec=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++ or Intel C++ Compiler. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl* | icl*) # Native MSVC or ICC hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported always_export_symbols=yes file_list_spec='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, )='true' enable_shared_with_static_runtimes=yes exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib old_postinstall_cmds='chmod 644 $oldlib' postlink_cmds='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC and ICC wrapper hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. old_archive_from_new_cmds='true' # FIXME: Should let the user specify the lib program. old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' enable_shared_with_static_runtimes=yes ;; esac ;; darwin* | rhapsody*) archive_cmds_need_lc=no hardcode_direct=no hardcode_automatic=yes hardcode_shlibpath_var=unsupported if test yes = "$lt_cv_ld_force_load"; then whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' else whole_archive_flag_spec='' fi link_all_deplibs=yes allow_undefined_flag=$_lt_dar_allow_undefined case $cc_basename in ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" archive_expsym_cmds="$SED 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" module_expsym_cmds="$SED -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" else ld_shlibs=no fi ;; dgux*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly* | midnightbsd*) archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; hpux9*) if test yes = "$GCC"; then archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' fi hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes export_dynamic_flag_spec='$wl-E' ;; hpux10*) if test yes,no = "$GCC,$with_gnu_ld"; then archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test no = "$with_gnu_ld"; then hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes fi ;; hpux11*) if test yes,no = "$GCC,$with_gnu_ld"; then case $host_cpu in hppa*64*) archive_cmds='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) archive_cmds='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 printf %s "checking if $CC understands -b... " >&6; } if test ${lt_cv_prog_compiler__b+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_prog_compiler__b=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -b" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler__b=yes fi else lt_cv_prog_compiler__b=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 printf "%s\n" "$lt_cv_prog_compiler__b" >&6; } if test yes = "$lt_cv_prog_compiler__b"; then archive_cmds='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi ;; esac fi if test no = "$with_gnu_ld"; then hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: case $host_cpu in hppa*64*|ia64*) hardcode_direct=no hardcode_shlibpath_var=no ;; *) hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test yes = "$GCC"; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5 printf %s "checking whether the $host_os linker accepts -exported_symbol... " >&6; } if test ${lt_cv_irix_exported_symbol+y} then : printf %s "(cached) " >&6 else $as_nop save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int foo (void) { return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : lt_cv_irix_exported_symbol=yes else $as_nop lt_cv_irix_exported_symbol=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5 printf "%s\n" "$lt_cv_irix_exported_symbol" >&6; } if test yes = "$lt_cv_irix_exported_symbol"; then archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' fi link_all_deplibs=no else archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: inherit_rpath=yes link_all_deplibs=yes ;; linux*) case $cc_basename in tcc*) # Fabrice Bellard et al's Tiny C Compiler ld_shlibs=yes archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' ;; esac ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; newsos6) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: hardcode_shlibpath_var=no ;; *nto* | *qnx*) ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then hardcode_direct=yes hardcode_shlibpath_var=no hardcode_direct_absolute=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' hardcode_libdir_flag_spec='$wl-rpath,$libdir' export_dynamic_flag_spec='$wl-E' else archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='$wl-rpath,$libdir' fi else ld_shlibs=no fi ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes allow_undefined_flag=unsupported shrext_cmds=.dll archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes=yes file_list_spec='@' ;; osf3*) if test yes = "$GCC"; then allow_undefined_flag=' $wl-expect_unresolved $wl\*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test yes = "$GCC"; then allow_undefined_flag=' $wl-expect_unresolved $wl\*' archive_cmds='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly hardcode_libdir_flag_spec='-rpath $libdir' fi archive_cmds_need_lc='no' hardcode_libdir_separator=: ;; solaris*) no_undefined_flag=' -z defs' if test yes = "$GCC"; then wlarc='$wl' archive_cmds='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' archive_cmds='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='$wl' archive_cmds='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi hardcode_libdir_flag_spec='-R$libdir' hardcode_shlibpath_var=no case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. GCC discards it without '$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test yes = "$GCC"; then whole_archive_flag_spec='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' else whole_archive_flag_spec='-z allextract$convenience -z defaultextract' fi ;; esac link_all_deplibs=yes ;; sunos4*) if test sequent = "$host_vendor"; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. archive_cmds='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi hardcode_libdir_flag_spec='-L$libdir' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; sysv4) case $host_vendor in sni) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' reload_cmds='$CC -r -o $output$reload_objs' hardcode_direct=no ;; motorola) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' hardcode_shlibpath_var=no ;; sysv4.3*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no export_dynamic_flag_spec='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes ld_shlibs=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) no_undefined_flag='$wl-z,text' archive_cmds_need_lc=no hardcode_shlibpath_var=no runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. no_undefined_flag='$wl-z,text' allow_undefined_flag='$wl-z,nodefs' archive_cmds_need_lc=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='$wl-R,$libdir' hardcode_libdir_separator=':' link_all_deplibs=yes export_dynamic_flag_spec='$wl-Bexport' runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; *) ld_shlibs=no ;; esac if test sni = "$host_vendor"; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) export_dynamic_flag_spec='$wl-Blargedynsym' ;; esac fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 printf "%s\n" "$ld_shlibs" >&6; } test no = "$ld_shlibs" && can_build_shared=no with_gnu_ld=$with_gnu_ld # # Do we need to explicitly link libc? # case "x$archive_cmds_need_lc" in x|xyes) # Assume -lc should be added archive_cmds_need_lc=yes if test yes,yes = "$GCC,$enable_shared"; then case $archive_cmds in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 printf %s "checking whether -lc should be explicitly linked in... " >&6; } if test ${lt_cv_archive_cmds_need_lc+y} then : printf %s "(cached) " >&6 else $as_nop $RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$lt_prog_compiler_wl pic_flag=$lt_prog_compiler_pic compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$allow_undefined_flag allow_undefined_flag= if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then lt_cv_archive_cmds_need_lc=no else lt_cv_archive_cmds_need_lc=yes fi allow_undefined_flag=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 printf "%s\n" "$lt_cv_archive_cmds_need_lc" >&6; } archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc ;; esac fi ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 printf %s "checking dynamic linker characteristics... " >&6; } if test yes = "$GCC"; then case $host_os in darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; *) lt_awk_arg='/^libraries:/' ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq='s|=\([A-Za-z]:\)|\1|g' ;; *) lt_sed_strip_eq='s|=/|/|g' ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary... lt_tmp_lt_search_path_spec= lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` # ...but if some path component already ends with the multilib dir we assume # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). case "$lt_multi_os_dir; $lt_search_path_spec " in "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) lt_multi_os_dir= ;; esac for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" elif test -n "$lt_multi_os_dir"; then test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS = " "; FS = "/|\n";} { lt_foo = ""; lt_count = 0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo = "/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[lt_foo]++; } if (lt_freq[lt_foo] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's|/\([A-Za-z]:\)|\1|g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='$libname$release$shared_ext$major' ;; aix[4-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line '#! .'. This would cause the generated library to # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[01] | aix4.[01].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # Using Import Files as archive members, it is possible to support # filename-based versioning of shared library archives on AIX. While # this would work for both with and without runtime linking, it will # prevent static linking of such archives. So we do filename-based # shared library versioning with .so extension only, which is used # when both runtime linking and shared linking is enabled. # Unfortunately, runtime linking may impact performance, so we do # not want this to be the default eventually. Also, we use the # versioned .so libs for executables only if there is the -brtl # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. # To allow for filename-based versioning support, we need to create # libNAME.so.V as an archive file, containing: # *) an Import File, referring to the versioned filename of the # archive as well as the shared archive member, telling the # bitwidth (32 or 64) of that shared object, and providing the # list of exported symbols of that shared object, eventually # decorated with the 'weak' keyword # *) the shared object with the F_LOADONLY flag set, to really avoid # it being seen by the linker. # At run time we better use the real file rather than another symlink, # but for link time we create the symlink libNAME.so -> libNAME.so.V case $with_aix_soname,$aix_use_runtimelinking in # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. aix,yes) # traditional libtool dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; aix,no) # traditional AIX only dynamic_linker='AIX lib.a(lib.so.V)' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' ;; svr4,*) # full svr4 only dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,yes) # both, prefer svr4 dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # unpreferred sharedlib libNAME.a needs extra handling postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,no) # both, prefer aix dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' ;; esac shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[45]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo $libname | $SED -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api" ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo $libname | $SED -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl* | *,icl*) # Native MSVC or ICC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC and ICC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly* | midnightbsd*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[23].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[01]* | freebsdelf3.[01]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[3-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test yes = "$lt_cv_prog_gnu_ld"; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; linux*android*) version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext' soname_spec='$libname$release$shared_ext' finish_cmds= shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes dynamic_linker='Android linker' # Don't embed -rpath directories since the linker doesn't support them. hardcode_libdir_flag_spec='-L$libdir' ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH if test ${lt_cv_shlibpath_overrides_runpath+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null then : lt_cv_shlibpath_overrides_runpath=yes fi fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS libdir=$save_libdir fi shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, # even though it is searched at run-time. Try to do the best guess by # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd* | bitrig*) version_type=sunos sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then need_version=no else need_version=yes fi library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no # OS/2 can only load a DLL with a base name of 8 characters or less. soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; v=$($ECHO $release$versuffix | tr -d .-); n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); $ECHO $n$v`$shared_ext' library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' shlibpath_var=BEGINLIBPATH sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec; then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 printf "%s\n" "$dynamic_linker" >&6; } test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi # remember unaugmented sys_lib_dlsearch_path content for libtool script decls... configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec # ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" # to be used as default LT_SYS_LIBRARY_PATH value in generated libtool configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 printf %s "checking how to hardcode library paths into programs... " >&6; } hardcode_action= if test -n "$hardcode_libdir_flag_spec" || test -n "$runpath_var" || test yes = "$hardcode_automatic"; then # We can hardcode non-existent directories. if test no != "$hardcode_direct" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, )" && test no != "$hardcode_minus_L"; then # Linking always hardcodes the temporary library directory. hardcode_action=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. hardcode_action=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. hardcode_action=unsupported fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 printf "%s\n" "$hardcode_action" >&6; } if test relink = "$hardcode_action" || test yes = "$inherit_rpath"; then # Fast installation is not supported enable_fast_install=no elif test yes = "$shlibpath_overrides_runpath" || test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi if test yes != "$enable_dlopen"; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen=load_add_on lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen=LoadLibrary lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen=dlopen lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 printf %s "checking for dlopen in -ldl... " >&6; } if test ${ac_cv_lib_dl_dlopen+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char dlopen (); int main (void) { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_dl_dlopen=yes else $as_nop ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 printf "%s\n" "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl else $as_nop lt_cv_dlopen=dyld lt_cv_dlopen_libs= lt_cv_dlopen_self=yes fi ;; tpf*) # Don't try to run any link tests for TPF. We know it's impossible # because TPF is a cross-compiler, and we know how we open DSOs. lt_cv_dlopen=dlopen lt_cv_dlopen_libs= lt_cv_dlopen_self=no ;; *) ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" if test "x$ac_cv_func_shl_load" = xyes then : lt_cv_dlopen=shl_load else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 printf %s "checking for shl_load in -ldld... " >&6; } if test ${ac_cv_lib_dld_shl_load+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char shl_load (); int main (void) { return shl_load (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_dld_shl_load=yes else $as_nop ac_cv_lib_dld_shl_load=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 printf "%s\n" "$ac_cv_lib_dld_shl_load" >&6; } if test "x$ac_cv_lib_dld_shl_load" = xyes then : lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld else $as_nop ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" if test "x$ac_cv_func_dlopen" = xyes then : lt_cv_dlopen=dlopen else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 printf %s "checking for dlopen in -ldl... " >&6; } if test ${ac_cv_lib_dl_dlopen+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char dlopen (); int main (void) { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_dl_dlopen=yes else $as_nop ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 printf "%s\n" "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 printf %s "checking for dlopen in -lsvld... " >&6; } if test ${ac_cv_lib_svld_dlopen+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lsvld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char dlopen (); int main (void) { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_svld_dlopen=yes else $as_nop ac_cv_lib_svld_dlopen=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 printf "%s\n" "$ac_cv_lib_svld_dlopen" >&6; } if test "x$ac_cv_lib_svld_dlopen" = xyes then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 printf %s "checking for dld_link in -ldld... " >&6; } if test ${ac_cv_lib_dld_dld_link+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char dld_link (); int main (void) { return dld_link (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_dld_dld_link=yes else $as_nop ac_cv_lib_dld_dld_link=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 printf "%s\n" "$ac_cv_lib_dld_dld_link" >&6; } if test "x$ac_cv_lib_dld_dld_link" = xyes then : lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld fi fi fi fi fi fi ;; esac if test no = "$lt_cv_dlopen"; then enable_dlopen=no else enable_dlopen=yes fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS=$CPPFLAGS test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS=$LDFLAGS wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS=$LIBS LIBS="$lt_cv_dlopen_libs $LIBS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 printf %s "checking whether a program can dlopen itself... " >&6; } if test ${lt_cv_dlopen_self+y} then : printf %s "(cached) " >&6 else $as_nop if test yes = "$cross_compiling"; then : lt_cv_dlopen_self=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; esac else : # compilation failed lt_cv_dlopen_self=no fi fi rm -fr conftest* fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 printf "%s\n" "$lt_cv_dlopen_self" >&6; } if test yes = "$lt_cv_dlopen_self"; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 printf %s "checking whether a statically linked program can dlopen itself... " >&6; } if test ${lt_cv_dlopen_self_static+y} then : printf %s "(cached) " >&6 else $as_nop if test yes = "$cross_compiling"; then : lt_cv_dlopen_self_static=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; esac else : # compilation failed lt_cv_dlopen_self_static=no fi fi rm -fr conftest* fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 printf "%s\n" "$lt_cv_dlopen_self_static" >&6; } fi CPPFLAGS=$save_CPPFLAGS LDFLAGS=$save_LDFLAGS LIBS=$save_LIBS ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi striplib= old_striplib= { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 printf %s "checking whether stripping libraries is possible... " >&6; } if test -z "$STRIP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } else if $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then old_striplib="$STRIP --strip-debug" striplib="$STRIP --strip-unneeded" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else case $host_os in darwin*) # FIXME - insert some real tests, host_os isn't really good enough striplib="$STRIP -x" old_striplib="$STRIP -S" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } ;; freebsd*) if $STRIP -V 2>&1 | $GREP "elftoolchain" >/dev/null; then old_striplib="$STRIP --strip-debug" striplib="$STRIP --strip-unneeded" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi ;; *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } ;; esac fi fi # Report what library types will actually be built { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 printf %s "checking if libtool supports shared libraries... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 printf "%s\n" "$can_build_shared" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 printf %s "checking whether to build shared libraries... " >&6; } test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[4-9]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 printf "%s\n" "$enable_shared" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 printf %s "checking whether to build static libraries... " >&6; } # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 printf "%s\n" "$enable_static" >&6; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu CC=$lt_save_CC ac_config_commands="$ac_config_commands libtool" # Only expand once: { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking target system type" >&5 printf %s "checking target system type... " >&6; } if test ${ac_cv_target+y} then : printf %s "(cached) " >&6 else $as_nop if test "x$target_alias" = x; then ac_cv_target=$ac_cv_host else ac_cv_target=`$SHELL "${ac_aux_dir}config.sub" $target_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $target_alias failed" "$LINENO" 5 fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5 printf "%s\n" "$ac_cv_target" >&6; } case $ac_cv_target in *-*-*) ;; *) as_fn_error $? "invalid value of canonical target" "$LINENO" 5;; esac target=$ac_cv_target ac_save_IFS=$IFS; IFS='-' set x $ac_cv_target shift target_cpu=$1 target_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: target_os=$* IFS=$ac_save_IFS case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac # The aliases save the names the user supplied, while $host etc. # will get canonicalized. test -n "$target_alias" && test "$program_prefix$program_suffix$program_transform_name" = \ NONENONEs,x,x, && program_prefix=${target_alias}- am__api_version='1.16' # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 printf %s "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if test ${ac_cv_path_install+y} then : printf %s "(cached) " >&6 else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac # Account for fact that we put trailing slashes in our PATH walk. case $as_dir in #(( ./ | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test ${ac_cv_path_install+y}; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 printf "%s\n" "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 printf %s "checking whether build environment is sane... " >&6; } # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[\\\"\#\$\&\'\`$am_lf]*) as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; esac case $srcdir in *[\\\"\#\$\&\'\`$am_lf\ \ ]*) as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$*" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$*" != "X $srcdir/configure conftest.file" \ && test "$*" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". as_fn_error $? "ls -t appears to fail. Make sure there is not a broken alias in your environment" "$LINENO" 5 fi if test "$2" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$2" = conftest.file ) then # Ok. : else as_fn_error $? "newly created file is older than distributed files! Check your system clock" "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi rm -f conftest.file test "$program_prefix" != NONE && program_transform_name="s&^&$program_prefix&;$program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s&\$&$program_suffix&;$program_transform_name" # Double any \ or $. # By default was `s,x,x', remove it if useless. ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' program_transform_name=`printf "%s\n" "$program_transform_name" | sed "$ac_script"` if test x"${MISSING+set}" != xset; then MISSING="\${SHELL} '$am_aux_dir/missing'" fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 printf "%s\n" "$as_me: WARNING: 'missing' script is too old or missing" >&2;} fi if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. if test "$cross_compiling" != no; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_STRIP+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 printf "%s\n" "$STRIP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_STRIP+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 printf "%s\n" "$ac_ct_STRIP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a race-free mkdir -p" >&5 printf %s "checking for a race-free mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if test ${ac_cv_path_mkdir+y} then : printf %s "(cached) " >&6 else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext" || continue case `"$as_dir$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir ('*'coreutils) '* | \ 'BusyBox '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir$ac_prog$ac_exec_ext break 3;; esac done done done IFS=$as_save_IFS fi test -d ./--version && rmdir ./--version if test ${ac_cv_path_mkdir+y}; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use the slow shell script. Don't cache a # value for MKDIR_P within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. MKDIR_P="$ac_install_sh -d" fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 printf "%s\n" "$MKDIR_P" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 printf %s "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`printf "%s\n" "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval test \${ac_cv_prog_make_${ac_make}_set+y} then : printf %s "(cached) " >&6 else $as_nop cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } SET_MAKE= else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null DEPDIR="${am__leading_dot}deps" ac_config_commands="$ac_config_commands depfiles" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5 printf %s "checking whether ${MAKE-make} supports the include directive... " >&6; } cat > confinc.mk << 'END' am__doit: @echo this is the am__doit target >confinc.out .PHONY: am__doit END am__include="#" am__quote= # BSD make does it like this. echo '.include "confinc.mk" # ignored' > confmf.BSD # Other make implementations (GNU, Solaris 10, AIX) do it like this. echo 'include confinc.mk # ignored' > confmf.GNU _am_result=no for s in GNU BSD; do { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5 (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } case $?:`cat confinc.out 2>/dev/null` in #( '0:this is the am__doit target') : case $s in #( BSD) : am__include='.include' am__quote='"' ;; #( *) : am__include='include' am__quote='' ;; esac ;; #( *) : ;; esac if test "$am__include" != "#"; then _am_result="yes ($s style)" break fi done rm -f confinc.* confmf.* { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5 printf "%s\n" "${_am_result}" >&6; } # Check whether --enable-dependency-tracking was given. if test ${enable_dependency_tracking+y} then : enableval=$enable_dependency_tracking; fi if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi if test "x$enable_dependency_tracking" != xno; then AMDEP_TRUE= AMDEP_FALSE='#' else AMDEP_TRUE='#' AMDEP_FALSE= fi # Check whether --enable-silent-rules was given. if test ${enable_silent_rules+y} then : enableval=$enable_silent_rules; fi case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=1;; esac am_make=${MAKE-make} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 printf %s "checking whether $am_make supports nested variables... " >&6; } if test ${am_cv_make_support_nested_variables+y} then : printf %s "(cached) " >&6 else $as_nop if printf "%s\n" 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 printf "%s\n" "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AM_BACKSLASH='\' if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." am__isrc=' -I$(srcdir)' # test to see if srcdir already configured if test -f $srcdir/config.status; then as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi # Define the identity of the package. PACKAGE='nghttp2' VERSION='1.68.0' printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h printf "%s\n" "#define VERSION \"$VERSION\"" >>confdefs.h # Some tools Automake needs. ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # mkdir_p='$(MKDIR_P)' # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. # Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AMTAR='$${TAR-tar}' # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar pax cpio none' { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to create a pax tar archive" >&5 printf %s "checking how to create a pax tar archive... " >&6; } # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_pax-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do { echo "$as_me:$LINENO: $_am_tar --version" >&5 ($_am_tar --version) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && break done am__tar="$_am_tar --format=posix -chf - "'"$$tardir"' am__tar_="$_am_tar --format=posix -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x pax -w "$$tardir"' am__tar_='pax -L -x pax -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H pax -L' am__tar_='find "$tardir" -print | cpio -o -H pax -L' am__untar='cpio -i -H pax -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_pax}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file { echo "$as_me:$LINENO: tardir=conftest.dir && eval $am__tar_ >conftest.tar" >&5 (tardir=conftest.dir && eval $am__tar_ >conftest.tar) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } rm -rf conftest.dir if test -s conftest.tar; then { echo "$as_me:$LINENO: $am__untar &5 ($am__untar &5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { echo "$as_me:$LINENO: cat conftest.dir/file" >&5 (cat conftest.dir/file) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } grep GrepMe conftest.dir/file >/dev/null 2>&1 && break fi done rm -rf conftest.dir if test ${am_cv_prog_tar_pax+y} then : printf %s "(cached) " >&6 else $as_nop am_cv_prog_tar_pax=$_am_tool fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_tar_pax" >&5 printf "%s\n" "$am_cv_prog_tar_pax" >&6; } depcc="$CC" am_compiler_list= { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 printf %s "checking dependency style of $depcc... " >&6; } if test ${am_cv_CC_dependencies_compiler_type+y} then : printf %s "(cached) " >&6 else $as_nop if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CC_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CC_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CC_dependencies_compiler_type=none fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 printf "%s\n" "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then am__fastdepCC_TRUE= am__fastdepCC_FALSE='#' else am__fastdepCC_TRUE='#' am__fastdepCC_FALSE= fi # Variables for tags utilities; see am/tags.am if test -z "$CTAGS"; then CTAGS=ctags fi if test -z "$ETAGS"; then ETAGS=etags fi if test -z "$CSCOPE"; then CSCOPE=cscope fi # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 fi fi # Check whether --enable-silent-rules was given. if test ${enable_silent_rules+y} then : enableval=$enable_silent_rules; fi case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=0;; esac am_make=${MAKE-make} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 printf %s "checking whether $am_make supports nested variables... " >&6; } if test ${am_cv_make_support_nested_variables+y} then : printf %s "(cached) " >&6 else $as_nop if printf "%s\n" 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 printf "%s\n" "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AM_BACKSLASH='\' LT_CURRENT=43 LT_REVISION=2 LT_AGE=29 major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/^0-9//g"` minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/^0-9//g"` patch=`echo $PACKAGE_VERSION |cut -d. -f3 | cut -d- -f1 | sed -e "s/^0-9//g"` PACKAGE_VERSION_NUM=`printf "0x%02x%02x%02x" "$major" "$minor" "$patch"` # Check whether --enable-werror was given. if test ${enable_werror+y} then : enableval=$enable_werror; werror=$enableval else $as_nop werror=no fi # Check whether --enable-debug was given. if test ${enable_debug+y} then : enableval=$enable_debug; debug=$enableval else $as_nop debug=no fi # Check whether --enable-threads was given. if test ${enable_threads+y} then : enableval=$enable_threads; threads=$enableval else $as_nop threads=yes fi # Check whether --enable-app was given. if test ${enable_app+y} then : enableval=$enable_app; request_app=$enableval else $as_nop request_app=check fi # Check whether --enable-hpack-tools was given. if test ${enable_hpack_tools+y} then : enableval=$enable_hpack_tools; request_hpack_tools=$enableval else $as_nop request_hpack_tools=check fi # Check whether --enable-examples was given. if test ${enable_examples+y} then : enableval=$enable_examples; request_examples=$enableval else $as_nop request_examples=check fi # Check whether --enable-failmalloc was given. if test ${enable_failmalloc+y} then : enableval=$enable_failmalloc; request_failmalloc=$enableval else $as_nop request_failmalloc=yes fi # Check whether --enable-lib-only was given. if test ${enable_lib_only+y} then : enableval=$enable_lib_only; request_lib_only=$enableval else $as_nop request_lib_only=no fi # Check whether --enable-http3 was given. if test ${enable_http3+y} then : enableval=$enable_http3; request_http3=$enableval else $as_nop request_http3=no fi # Check whether --with-libxml2 was given. if test ${with_libxml2+y} then : withval=$with_libxml2; request_libxml2=$withval else $as_nop request_libxml2=check fi # Check whether --with-jansson was given. if test ${with_jansson+y} then : withval=$with_jansson; request_jansson=$withval else $as_nop request_jansson=check fi # Check whether --with-zlib was given. if test ${with_zlib+y} then : withval=$with_zlib; request_zlib=$withval else $as_nop request_zlib=check fi # Check whether --with-libevent-openssl was given. if test ${with_libevent_openssl+y} then : withval=$with_libevent_openssl; request_libevent_openssl=$withval else $as_nop request_libevent_openssl=check fi # Check whether --with-libcares was given. if test ${with_libcares+y} then : withval=$with_libcares; request_libcares=$withval else $as_nop request_libcares=check fi # Check whether --with-wolfssl was given. if test ${with_wolfssl+y} then : withval=$with_wolfssl; request_wolfssl=$withval else $as_nop request_wolfssl=check fi # Check whether --with-openssl was given. if test ${with_openssl+y} then : withval=$with_openssl; request_openssl=$withval else $as_nop request_openssl=check fi # Check whether --with-libev was given. if test ${with_libev+y} then : withval=$with_libev; request_libev=$withval else $as_nop request_libev=check fi # Check whether --with-jemalloc was given. if test ${with_jemalloc+y} then : withval=$with_jemalloc; request_jemalloc=$withval else $as_nop request_jemalloc=check fi # Check whether --with-systemd was given. if test ${with_systemd+y} then : withval=$with_systemd; request_systemd=$withval else $as_nop request_systemd=check fi # Check whether --with-mruby was given. if test ${with_mruby+y} then : withval=$with_mruby; request_mruby=$withval else $as_nop request_mruby=no fi # Check whether --with-neverbleed was given. if test ${with_neverbleed+y} then : withval=$with_neverbleed; request_neverbleed=$withval else $as_nop request_neverbleed=no fi # Check whether --with-libngtcp2 was given. if test ${with_libngtcp2+y} then : withval=$with_libngtcp2; request_libngtcp2=$withval else $as_nop request_libngtcp2=check fi # Check whether --with-libnghttp3 was given. if test ${with_libnghttp3+y} then : withval=$with_libnghttp3; request_libnghttp3=$withval else $as_nop request_libnghttp3=check fi # Check whether --with-libbpf was given. if test ${with_libbpf+y} then : withval=$with_libbpf; request_libbpf=$withval else $as_nop request_libbpf=no fi # Check whether --with-libbrotlienc was given. if test ${with_libbrotlienc+y} then : withval=$with_libbrotlienc; request_libbrotlienc=$withval else $as_nop request_libbrotlienc=no fi # Check whether --with-libbrotlidec was given. if test ${with_libbrotlidec+y} then : withval=$with_libbrotlidec; request_libbrotlidec=$withval else $as_nop request_libbrotlidec=no fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. set dummy ${ac_tool_prefix}clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}clang" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "clang", so it can be a program name with args. set dummy clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="clang" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi fi test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion -version; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 printf %s "checking whether the compiler supports GNU C... " >&6; } if test ${ac_cv_c_compiler_gnu+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_compiler_gnu=yes else $as_nop ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } ac_compiler_gnu=$ac_cv_c_compiler_gnu if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+y} ac_save_CFLAGS=$CFLAGS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 printf %s "checking whether $CC accepts -g... " >&6; } if test ${ac_cv_prog_cc_g+y} then : printf %s "(cached) " >&6 else $as_nop ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes else $as_nop CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 printf "%s\n" "$ac_cv_prog_cc_g" >&6; } if test $ac_test_CFLAGS; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi ac_prog_cc_stdc=no if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 printf %s "checking for $CC option to enable C11 features... " >&6; } if test ${ac_cv_prog_cc_c11+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c11=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c11_program _ACEOF for ac_arg in '' -std=gnu11 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c11=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c11" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c11" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c11" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } CC="$CC $ac_cv_prog_cc_c11" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 ac_prog_cc_stdc=c11 fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 printf %s "checking for $CC option to enable C99 features... " >&6; } if test ${ac_cv_prog_cc_c99+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c99_program _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c99" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c99" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } CC="$CC $ac_cv_prog_cc_c99" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 ac_prog_cc_stdc=c99 fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 printf %s "checking for $CC option to enable C89 features... " >&6; } if test ${ac_cv_prog_cc_c89+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c89_program _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c89" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c89" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } CC="$CC $ac_cv_prog_cc_c89" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 ac_prog_cc_stdc=c89 fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 printf %s "checking whether $CC understands -c and -o together... " >&6; } if test ${am_cv_prog_cc_c_o+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 printf "%s\n" "$am_cv_prog_cc_c_o" >&6; } if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test -z "$CXX"; then if test -n "$CCC"; then CXX=$CCC else if test -n "$ac_tool_prefix"; then for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC clang++ do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CXX+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 printf "%s\n" "$CXX" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$CXX" && break done fi if test -z "$CXX"; then ac_ct_CXX=$CXX for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC clang++ do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CXX+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CXX="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 printf "%s\n" "$ac_ct_CXX" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$ac_ct_CXX" && break done if test "x$ac_ct_CXX" = x; then CXX="g++" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CXX=$ac_ct_CXX fi fi fi fi # Provide some information about the compiler. printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C++" >&5 printf %s "checking whether the compiler supports GNU C++... " >&6; } if test ${ac_cv_cxx_compiler_gnu+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : ac_compiler_gnu=yes else $as_nop ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 printf "%s\n" "$ac_cv_cxx_compiler_gnu" >&6; } ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test $ac_compiler_gnu = yes; then GXX=yes else GXX= fi ac_test_CXXFLAGS=${CXXFLAGS+y} ac_save_CXXFLAGS=$CXXFLAGS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 printf %s "checking whether $CXX accepts -g... " >&6; } if test ${ac_cv_prog_cxx_g+y} then : printf %s "(cached) " >&6 else $as_nop ac_save_cxx_werror_flag=$ac_cxx_werror_flag ac_cxx_werror_flag=yes ac_cv_prog_cxx_g=no CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : ac_cv_prog_cxx_g=yes else $as_nop CXXFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : else $as_nop ac_cxx_werror_flag=$ac_save_cxx_werror_flag CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : ac_cv_prog_cxx_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cxx_werror_flag=$ac_save_cxx_werror_flag fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 printf "%s\n" "$ac_cv_prog_cxx_g" >&6; } if test $ac_test_CXXFLAGS; then CXXFLAGS=$ac_save_CXXFLAGS elif test $ac_cv_prog_cxx_g = yes; then if test "$GXX" = yes; then CXXFLAGS="-g -O2" else CXXFLAGS="-g" fi else if test "$GXX" = yes; then CXXFLAGS="-O2" else CXXFLAGS= fi fi ac_prog_cxx_stdcxx=no if test x$ac_prog_cxx_stdcxx = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++11 features" >&5 printf %s "checking for $CXX option to enable C++11 features... " >&6; } if test ${ac_cv_prog_cxx_cxx11+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cxx_cxx11=no ac_save_CXX=$CXX cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_cxx_conftest_cxx11_program _ACEOF for ac_arg in '' -std=gnu++11 -std=gnu++0x -std=c++11 -std=c++0x -qlanglvl=extended0x -AA do CXX="$ac_save_CXX $ac_arg" if ac_fn_cxx_try_compile "$LINENO" then : ac_cv_prog_cxx_cxx11=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cxx_cxx11" != "xno" && break done rm -f conftest.$ac_ext CXX=$ac_save_CXX fi if test "x$ac_cv_prog_cxx_cxx11" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cxx_cxx11" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx11" >&5 printf "%s\n" "$ac_cv_prog_cxx_cxx11" >&6; } CXX="$CXX $ac_cv_prog_cxx_cxx11" fi ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx11 ac_prog_cxx_stdcxx=cxx11 fi fi if test x$ac_prog_cxx_stdcxx = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++98 features" >&5 printf %s "checking for $CXX option to enable C++98 features... " >&6; } if test ${ac_cv_prog_cxx_cxx98+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cxx_cxx98=no ac_save_CXX=$CXX cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_cxx_conftest_cxx98_program _ACEOF for ac_arg in '' -std=gnu++98 -std=c++98 -qlanglvl=extended -AA do CXX="$ac_save_CXX $ac_arg" if ac_fn_cxx_try_compile "$LINENO" then : ac_cv_prog_cxx_cxx98=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cxx_cxx98" != "xno" && break done rm -f conftest.$ac_ext CXX=$ac_save_CXX fi if test "x$ac_cv_prog_cxx_cxx98" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cxx_cxx98" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx98" >&5 printf "%s\n" "$ac_cv_prog_cxx_cxx98" >&6; } CXX="$CXX $ac_cv_prog_cxx_cxx98" fi ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx98 ac_prog_cxx_stdcxx=cxx98 fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu func_stripname_cnf () { case $2 in .*) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%\\\\$2\$%%"`;; *) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%$2\$%%"`;; esac } # func_stripname_cnf if test -n "$CXX" && ( test no != "$CXX" && ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || (test g++ != "$CXX"))); then ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 printf %s "checking how to run the C++ preprocessor... " >&6; } if test -z "$CXXCPP"; then if test ${ac_cv_prog_CXXCPP+y} then : printf %s "(cached) " >&6 else $as_nop # Double quotes because $CXX needs to be expanded for CXXCPP in "$CXX -E" cpp /lib/cpp do ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO" then : else $as_nop # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO" then : # Broken: success on invalid input. continue else $as_nop # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok then : break fi done ac_cv_prog_CXXCPP=$CXXCPP fi CXXCPP=$ac_cv_prog_CXXCPP else ac_cv_prog_CXXCPP=$CXXCPP fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 printf "%s\n" "$CXXCPP" >&6; } ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO" then : else $as_nop # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO" then : # Broken: success on invalid input. continue else $as_nop # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok then : else $as_nop { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu else _lt_caught_CXX_error=yes fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu archive_cmds_need_lc_CXX=no allow_undefined_flag_CXX= always_export_symbols_CXX=no archive_expsym_cmds_CXX= compiler_needs_object_CXX=no export_dynamic_flag_spec_CXX= hardcode_direct_CXX=no hardcode_direct_absolute_CXX=no hardcode_libdir_flag_spec_CXX= hardcode_libdir_separator_CXX= hardcode_minus_L_CXX=no hardcode_shlibpath_var_CXX=unsupported hardcode_automatic_CXX=no inherit_rpath_CXX=no module_cmds_CXX= module_expsym_cmds_CXX= link_all_deplibs_CXX=unknown old_archive_cmds_CXX=$old_archive_cmds reload_flag_CXX=$reload_flag reload_cmds_CXX=$reload_cmds no_undefined_flag_CXX= whole_archive_flag_spec_CXX= enable_shared_with_static_runtimes_CXX=no # Source file extension for C++ test sources. ac_ext=cpp # Object file extension for compiled C++ test sources. objext=o objext_CXX=$objext # No sense in running all these tests if we already determined that # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_caught_CXX_error"; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(int, char *[]) { return(0); }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # save warnings/boilerplate of simple test code ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_LD=$LD lt_save_GCC=$GCC GCC=$GXX lt_save_with_gnu_ld=$with_gnu_ld lt_save_path_LD=$lt_cv_path_LD if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx else $as_unset lt_cv_prog_gnu_ld fi if test -n "${lt_cv_path_LDCXX+set}"; then lt_cv_path_LD=$lt_cv_path_LDCXX else $as_unset lt_cv_path_LD fi test -z "${LDCXX+set}" || LD=$LDCXX CC=${CXX-"c++"} CFLAGS=$CXXFLAGS compiler=$CC compiler_CXX=$CC func_cc_basename $compiler cc_basename=$func_cc_basename_result if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately if test yes = "$GXX"; then lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' else lt_prog_compiler_no_builtin_flag_CXX= fi if test yes = "$GXX"; then # Set up default GNU C++ configuration # Check whether --with-gnu-ld was given. if test ${with_gnu_ld+y} then : withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes else $as_nop with_gnu_ld=no fi ac_prog=ld if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 printf %s "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test yes = "$with_gnu_ld"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 printf %s "checking for GNU ld... " >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 printf %s "checking for non-GNU ld... " >&6; } fi if test ${lt_cv_path_LD+y} then : printf %s "(cached) " >&6 else $as_nop if test -z "$LD"; then lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &5 printf "%s\n" "$LD" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 printf %s "checking if the linker ($LD) is GNU ld... " >&6; } if test ${lt_cv_prog_gnu_ld+y} then : printf %s "(cached) " >&6 else $as_nop # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 printf "%s\n" "$lt_cv_prog_gnu_ld" >&6; } with_gnu_ld=$lt_cv_prog_gnu_ld # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. if test yes = "$with_gnu_ld"; then archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) wlarc='$wl' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else whole_archive_flag_spec_CXX= fi else with_gnu_ld=no wlarc= # A generic and very simple default shared library creation # command for GNU C++ for the case where it uses the native # linker, instead of GNU ld. If possible, this setting should # overridden to take advantage of the native linker features on # the platform it is being used on. archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' fi # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' else GXX=no with_gnu_ld=no wlarc= fi # PORTME: fill in a description of your system's C++ link characteristics { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 printf %s "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } ld_shlibs_CXX=yes case $host_os in aix3*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; aix[4-9]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do case $ld_flag in *-brtl*) aix_use_runtimelinking=yes break ;; esac done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. archive_cmds_CXX='' hardcode_direct_CXX=yes hardcode_direct_absolute_CXX=yes hardcode_libdir_separator_CXX=':' link_all_deplibs_CXX=yes file_list_spec_CXX='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. hardcode_direct_CXX=no hardcode_direct_absolute_CXX=no ;; esac if test yes = "$GXX"; then case $host_os in aix4.[012]|aix4.[012].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct_CXX=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking hardcode_minus_L_CXX=yes hardcode_libdir_flag_spec_CXX='-L$libdir' hardcode_libdir_separator_CXX= fi esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag=$shared_flag' $wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi export_dynamic_flag_spec_CXX='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. always_export_symbols_CXX=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. # The "-G" linker flag allows undefined symbols. no_undefined_flag_CXX='-bernotok' # Determine the default libpath from the value encoded in an empty # executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if test ${lt_cv_aix_libpath__CXX+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO" then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath__CXX fi hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath" archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then hardcode_libdir_flag_spec_CXX='$wl-R $libdir:/usr/lib:/lib' allow_undefined_flag_CXX="-z nodefs" archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if test ${lt_cv_aix_libpath__CXX+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO" then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath__CXX fi hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. no_undefined_flag_CXX=' $wl-bernotok' allow_undefined_flag_CXX=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives whole_archive_flag_spec_CXX='$convenience' fi archive_cmds_need_lc_CXX=yes archive_expsym_cmds_CXX='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$RM -r $output_objdir/$realname.d' fi fi ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then allow_undefined_flag_CXX=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else ld_shlibs_CXX=no fi ;; chorus*) case $cc_basename in *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; cygwin* | mingw* | pw32* | cegcc*) case $GXX,$cc_basename in ,cl* | no,cl* | ,icl* | no,icl*) # Native MSVC or ICC # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec_CXX=' ' allow_undefined_flag_CXX=unsupported always_export_symbols_CXX=yes file_list_spec_CXX='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. archive_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, CXX)='true' enable_shared_with_static_runtimes_CXX=yes # Don't use ranlib old_postinstall_cmds_CXX='chmod 644 $oldlib' postlink_cmds_CXX='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ func_to_tool_file "$lt_outputfile"~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # g++ # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, # as there is no search path for DLLs. hardcode_libdir_flag_spec_CXX='-L$libdir' export_dynamic_flag_spec_CXX='$wl--export-all-symbols' allow_undefined_flag_CXX=unsupported always_export_symbols_CXX=no enable_shared_with_static_runtimes_CXX=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else ld_shlibs_CXX=no fi ;; esac ;; darwin* | rhapsody*) archive_cmds_need_lc_CXX=no hardcode_direct_CXX=no hardcode_automatic_CXX=yes hardcode_shlibpath_var_CXX=unsupported if test yes = "$lt_cv_ld_force_load"; then whole_archive_flag_spec_CXX='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' else whole_archive_flag_spec_CXX='' fi link_all_deplibs_CXX=yes allow_undefined_flag_CXX=$_lt_dar_allow_undefined case $cc_basename in ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" archive_expsym_cmds_CXX="$SED 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" module_expsym_cmds_CXX="$SED -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" if test yes != "$lt_cv_apple_cc_single_mod"; then archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" archive_expsym_cmds_CXX="$SED 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" fi else ld_shlibs_CXX=no fi ;; os2*) hardcode_libdir_flag_spec_CXX='-L$libdir' hardcode_minus_L_CXX=yes allow_undefined_flag_CXX=unsupported shrext_cmds=.dll archive_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' archive_expsym_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds_CXX='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes_CXX=yes file_list_spec_CXX='@' ;; dgux*) case $cc_basename in ec++*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; ghcx*) # Green Hills C++ Compiler # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; freebsd2.*) # C++ shared libraries reported to be fairly broken before # switch to ELF ld_shlibs_CXX=no ;; freebsd-elf*) archive_cmds_need_lc_CXX=no ;; freebsd* | dragonfly* | midnightbsd*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions ld_shlibs_CXX=yes ;; haiku*) archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' link_all_deplibs_CXX=yes ;; hpux9*) hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir' hardcode_libdir_separator_CXX=: export_dynamic_flag_spec_CXX='$wl-E' hardcode_direct_CXX=yes hardcode_minus_L_CXX=yes # Not in the search PATH, # but as the default # location of the library. case $cc_basename in CC*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; aCC*) archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support ld_shlibs_CXX=no fi ;; esac ;; hpux10*|hpux11*) if test no = "$with_gnu_ld"; then hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir' hardcode_libdir_separator_CXX=: case $host_cpu in hppa*64*|ia64*) ;; *) export_dynamic_flag_spec_CXX='$wl-E' ;; esac fi case $host_cpu in hppa*64*|ia64*) hardcode_direct_CXX=no hardcode_shlibpath_var_CXX=no ;; *) hardcode_direct_CXX=yes hardcode_direct_absolute_CXX=yes hardcode_minus_L_CXX=yes # Not in the search PATH, # but as the default # location of the library. ;; esac case $cc_basename in CC*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; aCC*) case $host_cpu in hppa*64*) archive_cmds_CXX='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then case $host_cpu in hppa*64*) archive_cmds_CXX='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi else # FIXME: insert proper C++ library support ld_shlibs_CXX=no fi ;; esac ;; interix[3-9]*) hardcode_direct_CXX=no hardcode_shlibpath_var_CXX=no hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' export_dynamic_flag_spec_CXX='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' archive_expsym_cmds_CXX='$SED "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' fi fi link_all_deplibs_CXX=yes ;; esac hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' hardcode_libdir_separator_CXX=: inherit_rpath_CXX=yes ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; icpc* | ecpc* ) # Intel C++ with_gnu_ld=yes # version 8.0 and above of icpc choke on multiply defined symbols # if we add $predep_objects and $postdep_objects, however 7.1 and # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac archive_cmds_need_lc_CXX=no hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [1-5].* | *pgcpp\ [1-5].*) prelink_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' old_archive_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ $RANLIB $oldlib' archive_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 6 and above use weak symbols archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac hardcode_libdir_flag_spec_CXX='$wl--rpath $wl$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' whole_archive_flag_spec_CXX='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' ;; cxx*) # Compaq C++ archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH hardcode_libdir_flag_spec_CXX='-rpath $libdir' hardcode_libdir_separator_CXX=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' ;; xl* | mpixl* | bgxl*) # IBM XL 8.0 on PPC, with GNU ld hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds_CXX='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi ;; *) case `$CC -V 2>&1 | $SED 5q` in *Sun\ C*) # Sun C++ 5.9 no_undefined_flag_CXX=' -zdefs' archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' archive_expsym_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' hardcode_libdir_flag_spec_CXX='-R$libdir' whole_archive_flag_spec_CXX='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object_CXX=yes # Not sure whether something based on # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 # would be better. output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' ;; esac ;; esac ;; lynxos*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; m88k*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; mvs*) case $cc_basename in cxx*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' wlarc= hardcode_libdir_flag_spec_CXX='-R$libdir' hardcode_direct_CXX=yes hardcode_shlibpath_var_CXX=no fi # Workaround some broken pre-1.5 toolchains output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' ;; *nto* | *qnx*) ld_shlibs_CXX=yes ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then hardcode_direct_CXX=yes hardcode_shlibpath_var_CXX=no hardcode_direct_absolute_CXX=yes archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' export_dynamic_flag_spec_CXX='$wl-E' whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' fi output_verbose_link_cmd=func_echo_all else ld_shlibs_CXX=no fi ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' hardcode_libdir_separator_CXX=: # Archives containing C++ object files must be created using # the KAI C++ compiler. case $host in osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;; esac ;; RCC*) # Rational C++ 2.4.1 # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; cxx*) case $host in osf3*) allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*' archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' ;; *) allow_undefined_flag_CXX=' -expect_unresolved \*' archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ echo "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ $RM $lib.exp' hardcode_libdir_flag_spec_CXX='-rpath $libdir' ;; esac hardcode_libdir_separator_CXX=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes,no = "$GXX,$with_gnu_ld"; then allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*' case $host in osf3*) archive_cmds_CXX='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; *) archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; esac hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' hardcode_libdir_separator_CXX=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' else # FIXME: insert proper C++ library support ld_shlibs_CXX=no fi ;; esac ;; psos*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; lcc*) # Lucid # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ archive_cmds_need_lc_CXX=yes no_undefined_flag_CXX=' -zdefs' archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' hardcode_libdir_flag_spec_CXX='-R$libdir' hardcode_shlibpath_var_CXX=no case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract' ;; esac link_all_deplibs_CXX=yes output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' ;; gcx*) # Green Hills C++ Compiler archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker if test yes,no = "$GXX,$with_gnu_ld"; then no_undefined_flag_CXX=' $wl-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' else # g++ 2.7 appears to require '-G' NOT '-shared' on this # platform. archive_cmds_CXX='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' fi hardcode_libdir_flag_spec_CXX='$wl-R $wl$libdir' case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) whole_archive_flag_spec_CXX='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' ;; esac fi ;; esac ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) no_undefined_flag_CXX='$wl-z,text' archive_cmds_need_lc_CXX=no hardcode_shlibpath_var_CXX=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. no_undefined_flag_CXX='$wl-z,text' allow_undefined_flag_CXX='$wl-z,nodefs' archive_cmds_need_lc_CXX=no hardcode_shlibpath_var_CXX=no hardcode_libdir_flag_spec_CXX='$wl-R,$libdir' hardcode_libdir_separator_CXX=':' link_all_deplibs_CXX=yes export_dynamic_flag_spec_CXX='$wl-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' old_archive_cmds_CXX='$CC -Tprelink_objects $oldobjs~ '"$old_archive_cmds_CXX" reload_cmds_CXX='$CC -Tprelink_objects $reload_objs~ '"$reload_cmds_CXX" ;; *) archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; vxworks*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 printf "%s\n" "$ld_shlibs_CXX" >&6; } test no = "$ld_shlibs_CXX" && can_build_shared=no GCC_CXX=$GXX LD_CXX=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... # Dependencies to place before and after the object being linked: predep_objects_CXX= postdep_objects_CXX= predeps_CXX= postdeps_CXX= compiler_lib_search_path_CXX= cat > conftest.$ac_ext <<_LT_EOF class Foo { public: Foo (void) { a = 0; } private: int a; }; _LT_EOF _lt_libdeps_save_CFLAGS=$CFLAGS case "$CC $CFLAGS " in #( *\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; *\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; *\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; esac if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then # Parse the compiler output and extract the necessary # objects, libraries and library flags. # Sentinel used to keep track of whether or not we are before # the conftest object file. pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do case $prev$p in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. if test x-L = "$p" || test x-R = "$p"; then prev=$p continue fi # Expand the sysroot to ease extracting the directories later. if test -z "$prev"; then case $p in -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; esac fi case $p in =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; esac if test no = "$pre_test_object_deps_done"; then case $prev in -L | -R) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$compiler_lib_search_path_CXX"; then compiler_lib_search_path_CXX=$prev$p else compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} $prev$p" fi ;; # The "-l" case would never come before the object being # linked, so don't bother handling this case. esac else if test -z "$postdeps_CXX"; then postdeps_CXX=$prev$p else postdeps_CXX="${postdeps_CXX} $prev$p" fi fi prev= ;; *.lto.$objext) ;; # Ignore GCC LTO objects *.$objext) # This assumes that the test object file only shows up # once in the compiler output. if test "$p" = "conftest.$objext"; then pre_test_object_deps_done=yes continue fi if test no = "$pre_test_object_deps_done"; then if test -z "$predep_objects_CXX"; then predep_objects_CXX=$p else predep_objects_CXX="$predep_objects_CXX $p" fi else if test -z "$postdep_objects_CXX"; then postdep_objects_CXX=$p else postdep_objects_CXX="$postdep_objects_CXX $p" fi fi ;; *) ;; # Ignore the rest. esac done # Clean up. rm -f a.out a.exe else echo "libtool.m4: error: problem compiling CXX test program" fi $RM -f confest.$objext CFLAGS=$_lt_libdeps_save_CFLAGS # PORTME: override above test on systems where it is broken case $host_os in interix[3-9]*) # Interix 3.5 installs completely hosed .la files for C++, so rather than # hack all around it, let's just trust "g++" to DTRT. predep_objects_CXX= postdep_objects_CXX= postdeps_CXX= ;; esac case " $postdeps_CXX " in *" -lc "*) archive_cmds_need_lc_CXX=no ;; esac compiler_lib_search_dirs_CXX= if test -n "${compiler_lib_search_path_CXX}"; then compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | $SED -e 's! -L! !g' -e 's!^ !!'` fi lt_prog_compiler_wl_CXX= lt_prog_compiler_pic_CXX= lt_prog_compiler_static_CXX= # C++ specific cases for pic, static, wl, etc. if test yes = "$GXX"; then lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static_CXX='-Bstatic' fi lt_prog_compiler_pic_CXX='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support lt_prog_compiler_pic_CXX='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries lt_prog_compiler_pic_CXX='-DDLL_EXPORT' case $host_os in os2*) lt_prog_compiler_static_CXX='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic_CXX='-fno-common' ;; *djgpp*) # DJGPP does not support shared libraries at all lt_prog_compiler_pic_CXX= ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. lt_prog_compiler_static_CXX= ;; interix[3-9]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic_CXX=-Kconform_pic fi ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) ;; *) lt_prog_compiler_pic_CXX='-fPIC' ;; esac ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic_CXX='-fPIC -shared' ;; *) lt_prog_compiler_pic_CXX='-fPIC' ;; esac else case $host_os in aix[4-9]*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static_CXX='-Bstatic' else lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' fi ;; chorus*) case $cc_basename in cxch68*) # Green Hills C++ Compiler # _LT_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). lt_prog_compiler_pic_CXX='-DDLL_EXPORT' ;; dgux*) case $cc_basename in ec++*) lt_prog_compiler_pic_CXX='-KPIC' ;; ghcx*) # Green Hills C++ Compiler lt_prog_compiler_pic_CXX='-pic' ;; *) ;; esac ;; freebsd* | dragonfly* | midnightbsd*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) case $cc_basename in CC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='$wl-a ${wl}archive' if test ia64 != "$host_cpu"; then lt_prog_compiler_pic_CXX='+Z' fi ;; aCC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='$wl-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) lt_prog_compiler_pic_CXX='+Z' ;; esac ;; *) ;; esac ;; interix*) # This is c89, which is MS Visual C++ (no shared libs) # Anyone wants to do a port? ;; irix5* | irix6* | nonstopux*) case $cc_basename in CC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='-non_shared' # CC pic flag -KPIC is the default. ;; *) ;; esac ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # KAI C++ Compiler lt_prog_compiler_wl_CXX='--backend -Wl,' lt_prog_compiler_pic_CXX='-fPIC' ;; ecpc* ) # old Intel C++ for x86_64, which still supported -KPIC. lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-static' ;; icpc* ) # Intel C++, used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-fPIC' lt_prog_compiler_static_CXX='-static' ;; pgCC* | pgcpp*) # Portland Group C++ compiler lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-fpic' lt_prog_compiler_static_CXX='-Bstatic' ;; cxx*) # Compaq C++ # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. lt_prog_compiler_pic_CXX= lt_prog_compiler_static_CXX='-non_shared' ;; xlc* | xlC* | bgxl[cC]* | mpixl[cC]*) # IBM XL 8.0, 9.0 on PPC and BlueGene lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-qpic' lt_prog_compiler_static_CXX='-qstaticlink' ;; *) case `$CC -V 2>&1 | $SED 5q` in *Sun\ C*) # Sun C++ 5.9 lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-Bstatic' lt_prog_compiler_wl_CXX='-Qoption ld ' ;; esac ;; esac ;; lynxos*) ;; m88k*) ;; mvs*) case $cc_basename in cxx*) lt_prog_compiler_pic_CXX='-W c,exportall' ;; *) ;; esac ;; netbsd* | netbsdelf*-gnu) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic_CXX='-fPIC -shared' ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) lt_prog_compiler_wl_CXX='--backend -Wl,' ;; RCC*) # Rational C++ 2.4.1 lt_prog_compiler_pic_CXX='-pic' ;; cxx*) # Digital/Compaq C++ lt_prog_compiler_wl_CXX='-Wl,' # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. lt_prog_compiler_pic_CXX= lt_prog_compiler_static_CXX='-non_shared' ;; *) ;; esac ;; psos*) ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-Bstatic' lt_prog_compiler_wl_CXX='-Qoption ld ' ;; gcx*) # Green Hills C++ Compiler lt_prog_compiler_pic_CXX='-PIC' ;; *) ;; esac ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x lt_prog_compiler_pic_CXX='-pic' lt_prog_compiler_static_CXX='-Bstatic' ;; lcc*) # Lucid lt_prog_compiler_pic_CXX='-pic' ;; *) ;; esac ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) case $cc_basename in CC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-Bstatic' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 lt_prog_compiler_pic_CXX='-KPIC' ;; *) ;; esac ;; vxworks*) ;; *) lt_prog_compiler_can_build_shared_CXX=no ;; esac fi case $host_os in # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) lt_prog_compiler_pic_CXX= ;; *) lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 printf %s "checking for $compiler option to produce PIC... " >&6; } if test ${lt_cv_prog_compiler_pic_CXX+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_prog_compiler_pic_CXX=$lt_prog_compiler_pic_CXX fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_CXX" >&5 printf "%s\n" "$lt_cv_prog_compiler_pic_CXX" >&6; } lt_prog_compiler_pic_CXX=$lt_cv_prog_compiler_pic_CXX # # Check to make sure the PIC flag actually works. # if test -n "$lt_prog_compiler_pic_CXX"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 printf %s "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; } if test ${lt_cv_prog_compiler_pic_works_CXX+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_prog_compiler_pic_works_CXX=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_pic_works_CXX=yes fi fi $RM conftest* fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5 printf "%s\n" "$lt_cv_prog_compiler_pic_works_CXX" >&6; } if test yes = "$lt_cv_prog_compiler_pic_works_CXX"; then case $lt_prog_compiler_pic_CXX in "" | " "*) ;; *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; esac else lt_prog_compiler_pic_CXX= lt_prog_compiler_can_build_shared_CXX=no fi fi # # Check to make sure the static flag actually works. # wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 printf %s "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } if test ${lt_cv_prog_compiler_static_works_CXX+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_prog_compiler_static_works_CXX=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $lt_tmp_static_flag" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_static_works_CXX=yes fi else lt_cv_prog_compiler_static_works_CXX=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5 printf "%s\n" "$lt_cv_prog_compiler_static_works_CXX" >&6; } if test yes = "$lt_cv_prog_compiler_static_works_CXX"; then : else lt_prog_compiler_static_CXX= fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 printf %s "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if test ${lt_cv_prog_compiler_c_o_CXX+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_prog_compiler_c_o_CXX=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o_CXX=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 printf "%s\n" "$lt_cv_prog_compiler_c_o_CXX" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 printf %s "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if test ${lt_cv_prog_compiler_c_o_CXX+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_prog_compiler_c_o_CXX=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o_CXX=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 printf "%s\n" "$lt_cv_prog_compiler_c_o_CXX" >&6; } hard_links=nottested if test no = "$lt_cv_prog_compiler_c_o_CXX" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 printf %s "checking if we can lock with hard links... " >&6; } hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 printf "%s\n" "$hard_links" >&6; } if test no = "$hard_links"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5 printf "%s\n" "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;} need_locks=warn fi else need_locks=no fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 printf %s "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' case $host_os in aix[4-9]*) # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else export_symbols_cmds_CXX='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi ;; pw32*) export_symbols_cmds_CXX=$ltdll_cmds ;; cygwin* | mingw* | cegcc*) case $cc_basename in cl* | icl*) exclude_expsyms_CXX='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' ;; *) export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms_CXX='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' ;; esac ;; linux* | k*bsd*-gnu | gnu*) link_all_deplibs_CXX=no ;; *) export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 printf "%s\n" "$ld_shlibs_CXX" >&6; } test no = "$ld_shlibs_CXX" && can_build_shared=no with_gnu_ld_CXX=$with_gnu_ld # # Do we need to explicitly link libc? # case "x$archive_cmds_need_lc_CXX" in x|xyes) # Assume -lc should be added archive_cmds_need_lc_CXX=yes if test yes,yes = "$GCC,$enable_shared"; then case $archive_cmds_CXX in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 printf %s "checking whether -lc should be explicitly linked in... " >&6; } if test ${lt_cv_archive_cmds_need_lc_CXX+y} then : printf %s "(cached) " >&6 else $as_nop $RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$lt_prog_compiler_wl_CXX pic_flag=$lt_prog_compiler_pic_CXX compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$allow_undefined_flag_CXX allow_undefined_flag_CXX= if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 (eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then lt_cv_archive_cmds_need_lc_CXX=no else lt_cv_archive_cmds_need_lc_CXX=yes fi allow_undefined_flag_CXX=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc_CXX" >&5 printf "%s\n" "$lt_cv_archive_cmds_need_lc_CXX" >&6; } archive_cmds_need_lc_CXX=$lt_cv_archive_cmds_need_lc_CXX ;; esac fi ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 printf %s "checking dynamic linker characteristics... " >&6; } library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='$libname$release$shared_ext$major' ;; aix[4-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line '#! .'. This would cause the generated library to # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[01] | aix4.[01].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # Using Import Files as archive members, it is possible to support # filename-based versioning of shared library archives on AIX. While # this would work for both with and without runtime linking, it will # prevent static linking of such archives. So we do filename-based # shared library versioning with .so extension only, which is used # when both runtime linking and shared linking is enabled. # Unfortunately, runtime linking may impact performance, so we do # not want this to be the default eventually. Also, we use the # versioned .so libs for executables only if there is the -brtl # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. # To allow for filename-based versioning support, we need to create # libNAME.so.V as an archive file, containing: # *) an Import File, referring to the versioned filename of the # archive as well as the shared archive member, telling the # bitwidth (32 or 64) of that shared object, and providing the # list of exported symbols of that shared object, eventually # decorated with the 'weak' keyword # *) the shared object with the F_LOADONLY flag set, to really avoid # it being seen by the linker. # At run time we better use the real file rather than another symlink, # but for link time we create the symlink libNAME.so -> libNAME.so.V case $with_aix_soname,$aix_use_runtimelinking in # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. aix,yes) # traditional libtool dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; aix,no) # traditional AIX only dynamic_linker='AIX lib.a(lib.so.V)' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' ;; svr4,*) # full svr4 only dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,yes) # both, prefer svr4 dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # unpreferred sharedlib libNAME.a needs extra handling postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,no) # both, prefer aix dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' ;; esac shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[45]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo $libname | $SED -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo $libname | $SED -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl* | *,icl*) # Native MSVC or ICC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC and ICC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly* | midnightbsd*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[23].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[01]* | freebsdelf3.[01]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[3-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test yes = "$lt_cv_prog_gnu_ld"; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; linux*android*) version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext' soname_spec='$libname$release$shared_ext' finish_cmds= shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes dynamic_linker='Android linker' # Don't embed -rpath directories since the linker doesn't support them. hardcode_libdir_flag_spec_CXX='-L$libdir' ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH if test ${lt_cv_shlibpath_overrides_runpath+y} then : printf %s "(cached) " >&6 else $as_nop lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\"" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO" then : if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null then : lt_cv_shlibpath_overrides_runpath=yes fi fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS libdir=$save_libdir fi shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, # even though it is searched at run-time. Try to do the best guess by # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd* | bitrig*) version_type=sunos sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then need_version=no else need_version=yes fi library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no # OS/2 can only load a DLL with a base name of 8 characters or less. soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; v=$($ECHO $release$versuffix | tr -d .-); n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); $ECHO $n$v`$shared_ext' library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' shlibpath_var=BEGINLIBPATH sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec; then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 printf "%s\n" "$dynamic_linker" >&6; } test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi # remember unaugmented sys_lib_dlsearch_path content for libtool script decls... configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec # ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" # to be used as default LT_SYS_LIBRARY_PATH value in generated libtool configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 printf %s "checking how to hardcode library paths into programs... " >&6; } hardcode_action_CXX= if test -n "$hardcode_libdir_flag_spec_CXX" || test -n "$runpath_var_CXX" || test yes = "$hardcode_automatic_CXX"; then # We can hardcode non-existent directories. if test no != "$hardcode_direct_CXX" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" && test no != "$hardcode_minus_L_CXX"; then # Linking always hardcodes the temporary library directory. hardcode_action_CXX=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. hardcode_action_CXX=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. hardcode_action_CXX=unsupported fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5 printf "%s\n" "$hardcode_action_CXX" >&6; } if test relink = "$hardcode_action_CXX" || test yes = "$inherit_rpath_CXX"; then # Fast installation is not supported enable_fast_install=no elif test yes = "$shlibpath_overrides_runpath" || test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi fi # test -n "$compiler" CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS LDCXX=$LD LD=$lt_save_LD GCC=$lt_save_GCC with_gnu_ld=$lt_save_with_gnu_ld lt_cv_path_LDCXX=$lt_cv_path_LD lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld fi # test yes != "$_lt_caught_CXX_error" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu depcc="$CXX" am_compiler_list= { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 printf %s "checking dependency style of $depcc... " >&6; } if test ${am_cv_CXX_dependencies_compiler_type+y} then : printf %s "(cached) " >&6 else $as_nop if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CXX_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CXX_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CXX_dependencies_compiler_type=none fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 printf "%s\n" "$am_cv_CXX_dependencies_compiler_type" >&6; } CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then am__fastdepCXX_TRUE= am__fastdepCXX_FALSE='#' else am__fastdepCXX_TRUE='#' am__fastdepCXX_FALSE= fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 printf %s "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if test ${ac_cv_prog_CPP+y} then : printf %s "(cached) " >&6 else $as_nop # Double quotes because $CC needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" cpp /lib/cpp do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO" then : else $as_nop # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO" then : # Broken: success on invalid input. continue else $as_nop # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 printf "%s\n" "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO" then : else $as_nop # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO" then : # Broken: success on invalid input. continue else $as_nop # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok then : else $as_nop { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 printf %s "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 printf "%s\n" "no, using $LN_S" >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 printf %s "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`printf "%s\n" "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval test \${ac_cv_prog_make_${ac_make}_set+y} then : printf %s "(cached) " >&6 else $as_nop cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } SET_MAKE= else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_PKG_CONFIG+y} then : printf %s "(cached) " >&6 else $as_nop case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 printf "%s\n" "$PKG_CONFIG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_path_PKG_CONFIG"; then ac_pt_PKG_CONFIG=$PKG_CONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_ac_pt_PKG_CONFIG+y} then : printf %s "(cached) " >&6 else $as_nop case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 printf "%s\n" "$ac_pt_PKG_CONFIG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKG_CONFIG=$ac_pt_PKG_CONFIG fi else PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi fi if test -n "$PKG_CONFIG"; then _pkg_min_version=0.20 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 printf %s "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } PKG_CONFIG="" fi fi if test -n "$PYTHON"; then # If the user set $PYTHON, use it and don't search something else. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $PYTHON version is >= 3.8" >&5 printf %s "checking whether $PYTHON version is >= 3.8... " >&6; } prog="import sys # split strings by '.' and convert to numeric. Append some zeros # because we need at least 4 digits for the hex conversion. # map returns an iterator in Python 3.0 and a list in 2.x minver = list(map(int, '3.8'.split('.'))) + [0, 0, 0] minverhex = 0 # xrange is not present in Python 3.0 and range returns an iterator for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[i] sys.exit(sys.hexversion < minverhex)" if { echo "$as_me:$LINENO: $PYTHON -c "$prog"" >&5 ($PYTHON -c "$prog") >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } as_fn_error $? "Python interpreter is too old" "$LINENO" 5 fi am_display_PYTHON=$PYTHON else # Otherwise, try each interpreter until we find one that satisfies # VERSION. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a Python interpreter with version >= 3.8" >&5 printf %s "checking for a Python interpreter with version >= 3.8... " >&6; } if test ${am_cv_pathless_PYTHON+y} then : printf %s "(cached) " >&6 else $as_nop for am_cv_pathless_PYTHON in python python2 python3 python3.11 python3.10 python3.9 python3.8 python3.7 python3.6 python3.5 python3.4 python3.3 python3.2 python3.1 python3.0 python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0 none; do test "$am_cv_pathless_PYTHON" = none && break prog="import sys # split strings by '.' and convert to numeric. Append some zeros # because we need at least 4 digits for the hex conversion. # map returns an iterator in Python 3.0 and a list in 2.x minver = list(map(int, '3.8'.split('.'))) + [0, 0, 0] minverhex = 0 # xrange is not present in Python 3.0 and range returns an iterator for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[i] sys.exit(sys.hexversion < minverhex)" if { echo "$as_me:$LINENO: $am_cv_pathless_PYTHON -c "$prog"" >&5 ($am_cv_pathless_PYTHON -c "$prog") >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } then : break fi done fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_pathless_PYTHON" >&5 printf "%s\n" "$am_cv_pathless_PYTHON" >&6; } # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. if test "$am_cv_pathless_PYTHON" = none; then PYTHON=: else # Extract the first word of "$am_cv_pathless_PYTHON", so it can be a program name with args. set dummy $am_cv_pathless_PYTHON; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_PYTHON+y} then : printf %s "(cached) " >&6 else $as_nop case $PYTHON in [\\/]* | ?:[\\/]*) ac_cv_path_PYTHON="$PYTHON" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_PYTHON="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PYTHON=$ac_cv_path_PYTHON if test -n "$PYTHON"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PYTHON" >&5 printf "%s\n" "$PYTHON" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi am_display_PYTHON=$am_cv_pathless_PYTHON fi if test "$PYTHON" = :; then : else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON version" >&5 printf %s "checking for $am_display_PYTHON version... " >&6; } if test ${am_cv_python_version+y} then : printf %s "(cached) " >&6 else $as_nop am_cv_python_version=`$PYTHON -c "import sys; print ('%u.%u' % sys.version_info[:2])"` fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_version" >&5 printf "%s\n" "$am_cv_python_version" >&6; } PYTHON_VERSION=$am_cv_python_version { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON platform" >&5 printf %s "checking for $am_display_PYTHON platform... " >&6; } if test ${am_cv_python_platform+y} then : printf %s "(cached) " >&6 else $as_nop am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"` fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_platform" >&5 printf "%s\n" "$am_cv_python_platform" >&6; } PYTHON_PLATFORM=$am_cv_python_platform if test "x$prefix" = xNONE; then am__usable_prefix=$ac_default_prefix else am__usable_prefix=$prefix fi # Allow user to request using sys.* values from Python, # instead of the GNU $prefix values. # Check whether --with-python-sys-prefix was given. if test ${with_python_sys_prefix+y} then : withval=$with_python_sys_prefix; am_use_python_sys=: else $as_nop am_use_python_sys=false fi # Allow user to override whatever the default Python prefix is. # Check whether --with-python_prefix was given. if test ${with_python_prefix+y} then : withval=$with_python_prefix; am_python_prefix_subst=$withval am_cv_python_prefix=$withval { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for explicit $am_display_PYTHON prefix" >&5 printf %s "checking for explicit $am_display_PYTHON prefix... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_prefix" >&5 printf "%s\n" "$am_cv_python_prefix" >&6; } else $as_nop if $am_use_python_sys; then # using python sys.prefix value, not GNU { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for python default $am_display_PYTHON prefix" >&5 printf %s "checking for python default $am_display_PYTHON prefix... " >&6; } if test ${am_cv_python_prefix+y} then : printf %s "(cached) " >&6 else $as_nop am_cv_python_prefix=`$PYTHON -c "import sys; sys.stdout.write(sys.prefix)"` fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_prefix" >&5 printf "%s\n" "$am_cv_python_prefix" >&6; } case $am_cv_python_prefix in $am__usable_prefix*) am__strip_prefix=`echo "$am__usable_prefix" | sed 's|.|.|g'` am_python_prefix_subst=`echo "$am_cv_python_prefix" | sed "s,^$am__strip_prefix,\\${prefix},"` ;; *) am_python_prefix_subst=$am_cv_python_prefix ;; esac else # using GNU prefix value, not python sys.prefix am_python_prefix_subst='${prefix}' am_python_prefix=$am_python_prefix_subst { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for GNU default $am_display_PYTHON prefix" >&5 printf %s "checking for GNU default $am_display_PYTHON prefix... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_python_prefix" >&5 printf "%s\n" "$am_python_prefix" >&6; } fi fi # Substituting python_prefix_subst value. PYTHON_PREFIX=$am_python_prefix_subst # emacs-page Now do it all over again for Python exec_prefix, but with yet # another conditional: fall back to regular prefix if that was specified. # Check whether --with-python_exec_prefix was given. if test ${with_python_exec_prefix+y} then : withval=$with_python_exec_prefix; am_python_exec_prefix_subst=$withval am_cv_python_exec_prefix=$withval { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for explicit $am_display_PYTHON exec_prefix" >&5 printf %s "checking for explicit $am_display_PYTHON exec_prefix... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_exec_prefix" >&5 printf "%s\n" "$am_cv_python_exec_prefix" >&6; } else $as_nop # no explicit --with-python_exec_prefix, but if # --with-python_prefix was given, use its value for python_exec_prefix too. if test -n "$with_python_prefix" then : am_python_exec_prefix_subst=$with_python_prefix am_cv_python_exec_prefix=$with_python_prefix { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for python_prefix-given $am_display_PYTHON exec_prefix" >&5 printf %s "checking for python_prefix-given $am_display_PYTHON exec_prefix... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_exec_prefix" >&5 printf "%s\n" "$am_cv_python_exec_prefix" >&6; } else $as_nop # Set am__usable_exec_prefix whether using GNU or Python values, # since we use that variable for pyexecdir. if test "x$exec_prefix" = xNONE; then am__usable_exec_prefix=$am__usable_prefix else am__usable_exec_prefix=$exec_prefix fi # if $am_use_python_sys; then # using python sys.exec_prefix, not GNU { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for python default $am_display_PYTHON exec_prefix" >&5 printf %s "checking for python default $am_display_PYTHON exec_prefix... " >&6; } if test ${am_cv_python_exec_prefix+y} then : printf %s "(cached) " >&6 else $as_nop am_cv_python_exec_prefix=`$PYTHON -c "import sys; sys.stdout.write(sys.exec_prefix)"` fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_exec_prefix" >&5 printf "%s\n" "$am_cv_python_exec_prefix" >&6; } case $am_cv_python_exec_prefix in $am__usable_exec_prefix*) am__strip_prefix=`echo "$am__usable_exec_prefix" | sed 's|.|.|g'` am_python_exec_prefix_subst=`echo "$am_cv_python_exec_prefix" | sed "s,^$am__strip_prefix,\\${exec_prefix},"` ;; *) am_python_exec_prefix_subst=$am_cv_python_exec_prefix ;; esac else # using GNU $exec_prefix, not python sys.exec_prefix am_python_exec_prefix_subst='${exec_prefix}' am_python_exec_prefix=$am_python_exec_prefix_subst { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for GNU default $am_display_PYTHON exec_prefix" >&5 printf %s "checking for GNU default $am_display_PYTHON exec_prefix... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_python_exec_prefix" >&5 printf "%s\n" "$am_python_exec_prefix" >&6; } fi fi fi # Substituting python_exec_prefix_subst. PYTHON_EXEC_PREFIX=$am_python_exec_prefix_subst # Factor out some code duplication into this shell variable. am_python_setup_sysconfig="\ import sys # Prefer sysconfig over distutils.sysconfig, for better compatibility # with python 3.x. See automake bug#10227. try: import sysconfig except ImportError: can_use_sysconfig = 0 else: can_use_sysconfig = 1 # Can't use sysconfig in CPython 2.7, since it's broken in virtualenvs: # try: from platform import python_implementation if python_implementation() == 'CPython' and sys.version[:3] == '2.7': can_use_sysconfig = 0 except ImportError: pass" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON script directory (pythondir)" >&5 printf %s "checking for $am_display_PYTHON script directory (pythondir)... " >&6; } if test ${am_cv_python_pythondir+y} then : printf %s "(cached) " >&6 else $as_nop if test "x$am_cv_python_prefix" = x; then am_py_prefix=$am__usable_prefix else am_py_prefix=$am_cv_python_prefix fi am_cv_python_pythondir=`$PYTHON -c " $am_python_setup_sysconfig if can_use_sysconfig: if hasattr(sysconfig, 'get_default_scheme'): scheme = sysconfig.get_default_scheme() else: scheme = sysconfig._get_default_scheme() if scheme == 'posix_local': # Debian's default scheme installs to /usr/local/ but we want to find headers in /usr/ scheme = 'posix_prefix' sitedir = sysconfig.get_path('purelib', scheme, vars={'base':'$am_py_prefix'}) else: from distutils import sysconfig sitedir = sysconfig.get_python_lib(0, 0, prefix='$am_py_prefix') sys.stdout.write(sitedir)"` # case $am_cv_python_pythondir in $am_py_prefix*) am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'` am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,\\${PYTHON_PREFIX},"` ;; *) case $am_py_prefix in /usr|/System*) ;; *) am_cv_python_pythondir="\${PYTHON_PREFIX}/lib/python$PYTHON_VERSION/site-packages" ;; esac ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_pythondir" >&5 printf "%s\n" "$am_cv_python_pythondir" >&6; } pythondir=$am_cv_python_pythondir pkgpythondir=\${pythondir}/$PACKAGE { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON extension module directory (pyexecdir)" >&5 printf %s "checking for $am_display_PYTHON extension module directory (pyexecdir)... " >&6; } if test ${am_cv_python_pyexecdir+y} then : printf %s "(cached) " >&6 else $as_nop if test "x$am_cv_python_exec_prefix" = x; then am_py_exec_prefix=$am__usable_exec_prefix else am_py_exec_prefix=$am_cv_python_exec_prefix fi am_cv_python_pyexecdir=`$PYTHON -c " $am_python_setup_sysconfig if can_use_sysconfig: if hasattr(sysconfig, 'get_default_scheme'): scheme = sysconfig.get_default_scheme() else: scheme = sysconfig._get_default_scheme() if scheme == 'posix_local': # Debian's default scheme installs to /usr/local/ but we want to find headers in /usr/ scheme = 'posix_prefix' sitedir = sysconfig.get_path('platlib', scheme, vars={'platbase':'$am_py_exec_prefix'}) else: from distutils import sysconfig sitedir = sysconfig.get_python_lib(1, 0, prefix='$am_py_exec_prefix') sys.stdout.write(sitedir)"` # case $am_cv_python_pyexecdir in $am_py_exec_prefix*) am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'` am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,\\${PYTHON_EXEC_PREFIX},"` ;; *) case $am_py_exec_prefix in /usr|/System*) ;; *) am_cv_python_pyexecdir="\${PYTHON_EXEC_PREFIX}/lib/python$PYTHON_VERSION/site-packages" ;; esac ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_pyexecdir" >&5 printf "%s\n" "$am_cv_python_pyexecdir" >&6; } pyexecdir=$am_cv_python_pyexecdir pkgpyexecdir=\${pyexecdir}/$PACKAGE fi if test "x$request_lib_only" = "xyes"; then request_app=no request_hpack_tools=no request_examples=no request_http3=no request_libxml2=no request_jansson=no request_zlib=no request_libevent_openssl=no request_libcares=no request_openssl=no request_libev=no request_jemalloc=no request_systemd=no request_mruby=no request_neverbleed=no request_libngtcp2=no request_libnghttp3=no request_libbpf=no fi if test "x$GCC" = "xyes" -o "x$CC" = "xclang" ; then printf "%s\n" "#define NGHTTP2_NORETURN __attribute__((noreturn))" >>confdefs.h else printf "%s\n" "#define NGHTTP2_NORETURN /**/" >>confdefs.h fi save_CXXFLAGS="$CXXFLAGS" CXXFLAGS= ax_cxx_compile_alternatives="20" ax_cxx_compile_cxx20_required=false ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ac_success=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++20 features by default" >&5 printf %s "checking whether $CXX supports C++20 features by default... " >&6; } if test ${ax_cv_cxx_compile_cxx20+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" // MSVC always sets __cplusplus to 199711L in older versions; newer versions // only set it correctly if /Zc:__cplusplus is specified as well as a // /std:c++NN switch: // https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/ #elif __cplusplus < 201103L && !defined _MSC_VER #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual ~Base() {} virtual void f() {} }; struct Derived : public Base { virtual ~Derived() override {} virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = [](){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = [](int i, int j){ return i + j; }(1, 2); auto b = []() -> int { return '0'; }(); auto c = [=](){ return a + b; }(); auto d = [&](){ return c; }(); auto e = [a, &b](int x) mutable { const auto identity = [](int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = [](){ return 0; }; const auto unary = [](int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = [](nullary_t f){ return f(); }; const auto higher2nd = [unary](nullary_t f1){ return [unary, f1](unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L // If the compiler admits that it is not ready for C++14, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201402L && !defined _MSC_VER #error "This is not a C++14 compiler" #else namespace cxx14 { namespace test_polymorphic_lambdas { int test() { const auto lambda = [](auto&&... args){ const auto istiny = [](auto x){ return (sizeof(x) == 1UL) ? 1 : 0; }; const int aretiny[] = { istiny(args)... }; return aretiny[0]; }; return lambda(1, 1L, 1.0f, '1'); } } namespace test_binary_literals { constexpr auto ivii = 0b0000000000101010; static_assert(ivii == 42, "wrong value"); } namespace test_generalized_constexpr { template < typename CharT > constexpr unsigned long strlen_c(const CharT *const s) noexcept { auto length = 0UL; for (auto p = s; *p; ++p) ++length; return length; } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("x") == 1UL, ""); static_assert(strlen_c("test") == 4UL, ""); static_assert(strlen_c("another\0test") == 7UL, ""); } namespace test_lambda_init_capture { int test() { auto x = 0; const auto lambda1 = [a = x](int b){ return a + b; }; const auto lambda2 = [a = lambda1(x)](){ return a; }; return lambda2(); } } namespace test_digit_separators { constexpr auto ten_million = 100'000'000; static_assert(ten_million == 100000000, ""); } namespace test_return_type_deduction { auto f(int& x) { return x; } decltype(auto) g(int& x) { return x; } template < typename T1, typename T2 > struct is_same { static constexpr auto value = false; }; template < typename T > struct is_same { static constexpr auto value = true; }; int test() { auto x = 0; static_assert(is_same::value, ""); static_assert(is_same::value, ""); return x; } } } // namespace cxx14 #endif // __cplusplus >= 201402L // If the compiler admits that it is not ready for C++17, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201703L && !defined _MSC_VER #error "This is not a C++17 compiler" #else #include #include #include namespace cxx17 { namespace test_constexpr_lambdas { constexpr int foo = [](){return 42;}(); } namespace test::nested_namespace::definitions { } namespace test_fold_expression { template int multiply(Args... args) { return (args * ... * 1); } template bool all(Args... args) { return (args && ...); } } namespace test_extended_static_assert { static_assert (true); } namespace test_auto_brace_init_list { auto foo = {5}; auto bar {5}; static_assert(std::is_same, decltype(foo)>::value); static_assert(std::is_same::value); } namespace test_typename_in_template_template_parameter { template typename X> struct D; } namespace test_fallthrough_nodiscard_maybe_unused_attributes { int f1() { return 42; } [[nodiscard]] int f2() { [[maybe_unused]] auto unused = f1(); switch (f1()) { case 17: f1(); [[fallthrough]]; case 42: f1(); } return f1(); } } namespace test_extended_aggregate_initialization { struct base1 { int b1, b2 = 42; }; struct base2 { base2() { b3 = 42; } int b3; }; struct derived : base1, base2 { int d; }; derived d1 {{1, 2}, {}, 4}; // full initialization derived d2 {{}, {}, 4}; // value-initialized bases } namespace test_general_range_based_for_loop { struct iter { int i; int& operator* () { return i; } const int& operator* () const { return i; } iter& operator++() { ++i; return *this; } }; struct sentinel { int i; }; bool operator== (const iter& i, const sentinel& s) { return i.i == s.i; } bool operator!= (const iter& i, const sentinel& s) { return !(i == s); } struct range { iter begin() const { return {0}; } sentinel end() const { return {5}; } }; void f() { range r {}; for (auto i : r) { [[maybe_unused]] auto v = i; } } } namespace test_lambda_capture_asterisk_this_by_value { struct t { int i; int foo() { return [*this]() { return i; }(); } }; } namespace test_enum_class_construction { enum class byte : unsigned char {}; byte foo {42}; } namespace test_constexpr_if { template int f () { if constexpr(cond) { return 13; } else { return 42; } } } namespace test_selection_statement_with_initializer { int f() { return 13; } int f2() { if (auto i = f(); i > 0) { return 3; } switch (auto i = f(); i + 4) { case 17: return 2; default: return 1; } } } namespace test_template_argument_deduction_for_class_templates { template struct pair { pair (T1 p1, T2 p2) : m1 {p1}, m2 {p2} {} T1 m1; T2 m2; }; void f() { [[maybe_unused]] auto p = pair{13, 42u}; } } namespace test_non_type_auto_template_parameters { template struct B {}; B<5> b1; B<'a'> b2; } namespace test_structured_bindings { int arr[2] = { 1, 2 }; std::pair pr = { 1, 2 }; auto f1() -> int(&)[2] { return arr; } auto f2() -> std::pair& { return pr; } struct S { int x1 : 2; volatile double y1; }; S f3() { return {}; } auto [ x1, y1 ] = f1(); auto& [ xr1, yr1 ] = f1(); auto [ x2, y2 ] = f2(); auto& [ xr2, yr2 ] = f2(); const auto [ x3, y3 ] = f3(); } namespace test_exception_spec_type_system { struct Good {}; struct Bad {}; void g1() noexcept; void g2(); template Bad f(T*, T*); template Good f(T1*, T2*); static_assert (std::is_same_v); } namespace test_inline_variables { template void f(T) {} template inline T g(T) { return T{}; } template<> inline void f<>(int) {} template<> int g<>(int) { return 5; } } } // namespace cxx17 #endif // __cplusplus < 201703L && !defined _MSC_VER #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 202002L && !defined _MSC_VER #error "This is not a C++20 compiler" #else #include namespace cxx20 { // As C++20 supports feature test macros in the standard, there is no // immediate need to actually test for feature availability on the // Autoconf side. } // namespace cxx20 #endif // __cplusplus < 202002L && !defined _MSC_VER _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : ax_cv_cxx_compile_cxx20=yes else $as_nop ax_cv_cxx_compile_cxx20=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cxx_compile_cxx20" >&5 printf "%s\n" "$ax_cv_cxx_compile_cxx20" >&6; } if test x$ax_cv_cxx_compile_cxx20 = xyes; then ac_success=yes fi if test x$ac_success = xno; then for alternative in ${ax_cxx_compile_alternatives}; do switch="-std=gnu++${alternative}" cachevar=`printf "%s\n" "ax_cv_cxx_compile_cxx20_$switch" | $as_tr_sh` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++20 features with $switch" >&5 printf %s "checking whether $CXX supports C++20 features with $switch... " >&6; } if eval test \${$cachevar+y} then : printf %s "(cached) " >&6 else $as_nop ac_save_CXX="$CXX" CXX="$CXX $switch" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" // MSVC always sets __cplusplus to 199711L in older versions; newer versions // only set it correctly if /Zc:__cplusplus is specified as well as a // /std:c++NN switch: // https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/ #elif __cplusplus < 201103L && !defined _MSC_VER #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual ~Base() {} virtual void f() {} }; struct Derived : public Base { virtual ~Derived() override {} virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = [](){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = [](int i, int j){ return i + j; }(1, 2); auto b = []() -> int { return '0'; }(); auto c = [=](){ return a + b; }(); auto d = [&](){ return c; }(); auto e = [a, &b](int x) mutable { const auto identity = [](int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = [](){ return 0; }; const auto unary = [](int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = [](nullary_t f){ return f(); }; const auto higher2nd = [unary](nullary_t f1){ return [unary, f1](unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L // If the compiler admits that it is not ready for C++14, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201402L && !defined _MSC_VER #error "This is not a C++14 compiler" #else namespace cxx14 { namespace test_polymorphic_lambdas { int test() { const auto lambda = [](auto&&... args){ const auto istiny = [](auto x){ return (sizeof(x) == 1UL) ? 1 : 0; }; const int aretiny[] = { istiny(args)... }; return aretiny[0]; }; return lambda(1, 1L, 1.0f, '1'); } } namespace test_binary_literals { constexpr auto ivii = 0b0000000000101010; static_assert(ivii == 42, "wrong value"); } namespace test_generalized_constexpr { template < typename CharT > constexpr unsigned long strlen_c(const CharT *const s) noexcept { auto length = 0UL; for (auto p = s; *p; ++p) ++length; return length; } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("x") == 1UL, ""); static_assert(strlen_c("test") == 4UL, ""); static_assert(strlen_c("another\0test") == 7UL, ""); } namespace test_lambda_init_capture { int test() { auto x = 0; const auto lambda1 = [a = x](int b){ return a + b; }; const auto lambda2 = [a = lambda1(x)](){ return a; }; return lambda2(); } } namespace test_digit_separators { constexpr auto ten_million = 100'000'000; static_assert(ten_million == 100000000, ""); } namespace test_return_type_deduction { auto f(int& x) { return x; } decltype(auto) g(int& x) { return x; } template < typename T1, typename T2 > struct is_same { static constexpr auto value = false; }; template < typename T > struct is_same { static constexpr auto value = true; }; int test() { auto x = 0; static_assert(is_same::value, ""); static_assert(is_same::value, ""); return x; } } } // namespace cxx14 #endif // __cplusplus >= 201402L // If the compiler admits that it is not ready for C++17, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201703L && !defined _MSC_VER #error "This is not a C++17 compiler" #else #include #include #include namespace cxx17 { namespace test_constexpr_lambdas { constexpr int foo = [](){return 42;}(); } namespace test::nested_namespace::definitions { } namespace test_fold_expression { template int multiply(Args... args) { return (args * ... * 1); } template bool all(Args... args) { return (args && ...); } } namespace test_extended_static_assert { static_assert (true); } namespace test_auto_brace_init_list { auto foo = {5}; auto bar {5}; static_assert(std::is_same, decltype(foo)>::value); static_assert(std::is_same::value); } namespace test_typename_in_template_template_parameter { template typename X> struct D; } namespace test_fallthrough_nodiscard_maybe_unused_attributes { int f1() { return 42; } [[nodiscard]] int f2() { [[maybe_unused]] auto unused = f1(); switch (f1()) { case 17: f1(); [[fallthrough]]; case 42: f1(); } return f1(); } } namespace test_extended_aggregate_initialization { struct base1 { int b1, b2 = 42; }; struct base2 { base2() { b3 = 42; } int b3; }; struct derived : base1, base2 { int d; }; derived d1 {{1, 2}, {}, 4}; // full initialization derived d2 {{}, {}, 4}; // value-initialized bases } namespace test_general_range_based_for_loop { struct iter { int i; int& operator* () { return i; } const int& operator* () const { return i; } iter& operator++() { ++i; return *this; } }; struct sentinel { int i; }; bool operator== (const iter& i, const sentinel& s) { return i.i == s.i; } bool operator!= (const iter& i, const sentinel& s) { return !(i == s); } struct range { iter begin() const { return {0}; } sentinel end() const { return {5}; } }; void f() { range r {}; for (auto i : r) { [[maybe_unused]] auto v = i; } } } namespace test_lambda_capture_asterisk_this_by_value { struct t { int i; int foo() { return [*this]() { return i; }(); } }; } namespace test_enum_class_construction { enum class byte : unsigned char {}; byte foo {42}; } namespace test_constexpr_if { template int f () { if constexpr(cond) { return 13; } else { return 42; } } } namespace test_selection_statement_with_initializer { int f() { return 13; } int f2() { if (auto i = f(); i > 0) { return 3; } switch (auto i = f(); i + 4) { case 17: return 2; default: return 1; } } } namespace test_template_argument_deduction_for_class_templates { template struct pair { pair (T1 p1, T2 p2) : m1 {p1}, m2 {p2} {} T1 m1; T2 m2; }; void f() { [[maybe_unused]] auto p = pair{13, 42u}; } } namespace test_non_type_auto_template_parameters { template struct B {}; B<5> b1; B<'a'> b2; } namespace test_structured_bindings { int arr[2] = { 1, 2 }; std::pair pr = { 1, 2 }; auto f1() -> int(&)[2] { return arr; } auto f2() -> std::pair& { return pr; } struct S { int x1 : 2; volatile double y1; }; S f3() { return {}; } auto [ x1, y1 ] = f1(); auto& [ xr1, yr1 ] = f1(); auto [ x2, y2 ] = f2(); auto& [ xr2, yr2 ] = f2(); const auto [ x3, y3 ] = f3(); } namespace test_exception_spec_type_system { struct Good {}; struct Bad {}; void g1() noexcept; void g2(); template Bad f(T*, T*); template Good f(T1*, T2*); static_assert (std::is_same_v); } namespace test_inline_variables { template void f(T) {} template inline T g(T) { return T{}; } template<> inline void f<>(int) {} template<> int g<>(int) { return 5; } } } // namespace cxx17 #endif // __cplusplus < 201703L && !defined _MSC_VER #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 202002L && !defined _MSC_VER #error "This is not a C++20 compiler" #else #include namespace cxx20 { // As C++20 supports feature test macros in the standard, there is no // immediate need to actually test for feature availability on the // Autoconf side. } // namespace cxx20 #endif // __cplusplus < 202002L && !defined _MSC_VER _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : eval $cachevar=yes else $as_nop eval $cachevar=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CXX="$ac_save_CXX" fi eval ac_res=\$$cachevar { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done fi if test x$ac_success = xno; then for alternative in ${ax_cxx_compile_alternatives}; do for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}" MSVC; do if test x"$switch" = xMSVC; then switch=-std:c++${alternative} cachevar=`printf "%s\n" "ax_cv_cxx_compile_cxx20_${switch}_MSVC" | $as_tr_sh` else cachevar=`printf "%s\n" "ax_cv_cxx_compile_cxx20_$switch" | $as_tr_sh` fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++20 features with $switch" >&5 printf %s "checking whether $CXX supports C++20 features with $switch... " >&6; } if eval test \${$cachevar+y} then : printf %s "(cached) " >&6 else $as_nop ac_save_CXX="$CXX" CXX="$CXX $switch" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" // MSVC always sets __cplusplus to 199711L in older versions; newer versions // only set it correctly if /Zc:__cplusplus is specified as well as a // /std:c++NN switch: // https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/ #elif __cplusplus < 201103L && !defined _MSC_VER #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual ~Base() {} virtual void f() {} }; struct Derived : public Base { virtual ~Derived() override {} virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = [](){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = [](int i, int j){ return i + j; }(1, 2); auto b = []() -> int { return '0'; }(); auto c = [=](){ return a + b; }(); auto d = [&](){ return c; }(); auto e = [a, &b](int x) mutable { const auto identity = [](int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = [](){ return 0; }; const auto unary = [](int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = [](nullary_t f){ return f(); }; const auto higher2nd = [unary](nullary_t f1){ return [unary, f1](unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L // If the compiler admits that it is not ready for C++14, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201402L && !defined _MSC_VER #error "This is not a C++14 compiler" #else namespace cxx14 { namespace test_polymorphic_lambdas { int test() { const auto lambda = [](auto&&... args){ const auto istiny = [](auto x){ return (sizeof(x) == 1UL) ? 1 : 0; }; const int aretiny[] = { istiny(args)... }; return aretiny[0]; }; return lambda(1, 1L, 1.0f, '1'); } } namespace test_binary_literals { constexpr auto ivii = 0b0000000000101010; static_assert(ivii == 42, "wrong value"); } namespace test_generalized_constexpr { template < typename CharT > constexpr unsigned long strlen_c(const CharT *const s) noexcept { auto length = 0UL; for (auto p = s; *p; ++p) ++length; return length; } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("x") == 1UL, ""); static_assert(strlen_c("test") == 4UL, ""); static_assert(strlen_c("another\0test") == 7UL, ""); } namespace test_lambda_init_capture { int test() { auto x = 0; const auto lambda1 = [a = x](int b){ return a + b; }; const auto lambda2 = [a = lambda1(x)](){ return a; }; return lambda2(); } } namespace test_digit_separators { constexpr auto ten_million = 100'000'000; static_assert(ten_million == 100000000, ""); } namespace test_return_type_deduction { auto f(int& x) { return x; } decltype(auto) g(int& x) { return x; } template < typename T1, typename T2 > struct is_same { static constexpr auto value = false; }; template < typename T > struct is_same { static constexpr auto value = true; }; int test() { auto x = 0; static_assert(is_same::value, ""); static_assert(is_same::value, ""); return x; } } } // namespace cxx14 #endif // __cplusplus >= 201402L // If the compiler admits that it is not ready for C++17, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201703L && !defined _MSC_VER #error "This is not a C++17 compiler" #else #include #include #include namespace cxx17 { namespace test_constexpr_lambdas { constexpr int foo = [](){return 42;}(); } namespace test::nested_namespace::definitions { } namespace test_fold_expression { template int multiply(Args... args) { return (args * ... * 1); } template bool all(Args... args) { return (args && ...); } } namespace test_extended_static_assert { static_assert (true); } namespace test_auto_brace_init_list { auto foo = {5}; auto bar {5}; static_assert(std::is_same, decltype(foo)>::value); static_assert(std::is_same::value); } namespace test_typename_in_template_template_parameter { template typename X> struct D; } namespace test_fallthrough_nodiscard_maybe_unused_attributes { int f1() { return 42; } [[nodiscard]] int f2() { [[maybe_unused]] auto unused = f1(); switch (f1()) { case 17: f1(); [[fallthrough]]; case 42: f1(); } return f1(); } } namespace test_extended_aggregate_initialization { struct base1 { int b1, b2 = 42; }; struct base2 { base2() { b3 = 42; } int b3; }; struct derived : base1, base2 { int d; }; derived d1 {{1, 2}, {}, 4}; // full initialization derived d2 {{}, {}, 4}; // value-initialized bases } namespace test_general_range_based_for_loop { struct iter { int i; int& operator* () { return i; } const int& operator* () const { return i; } iter& operator++() { ++i; return *this; } }; struct sentinel { int i; }; bool operator== (const iter& i, const sentinel& s) { return i.i == s.i; } bool operator!= (const iter& i, const sentinel& s) { return !(i == s); } struct range { iter begin() const { return {0}; } sentinel end() const { return {5}; } }; void f() { range r {}; for (auto i : r) { [[maybe_unused]] auto v = i; } } } namespace test_lambda_capture_asterisk_this_by_value { struct t { int i; int foo() { return [*this]() { return i; }(); } }; } namespace test_enum_class_construction { enum class byte : unsigned char {}; byte foo {42}; } namespace test_constexpr_if { template int f () { if constexpr(cond) { return 13; } else { return 42; } } } namespace test_selection_statement_with_initializer { int f() { return 13; } int f2() { if (auto i = f(); i > 0) { return 3; } switch (auto i = f(); i + 4) { case 17: return 2; default: return 1; } } } namespace test_template_argument_deduction_for_class_templates { template struct pair { pair (T1 p1, T2 p2) : m1 {p1}, m2 {p2} {} T1 m1; T2 m2; }; void f() { [[maybe_unused]] auto p = pair{13, 42u}; } } namespace test_non_type_auto_template_parameters { template struct B {}; B<5> b1; B<'a'> b2; } namespace test_structured_bindings { int arr[2] = { 1, 2 }; std::pair pr = { 1, 2 }; auto f1() -> int(&)[2] { return arr; } auto f2() -> std::pair& { return pr; } struct S { int x1 : 2; volatile double y1; }; S f3() { return {}; } auto [ x1, y1 ] = f1(); auto& [ xr1, yr1 ] = f1(); auto [ x2, y2 ] = f2(); auto& [ xr2, yr2 ] = f2(); const auto [ x3, y3 ] = f3(); } namespace test_exception_spec_type_system { struct Good {}; struct Bad {}; void g1() noexcept; void g2(); template Bad f(T*, T*); template Good f(T1*, T2*); static_assert (std::is_same_v); } namespace test_inline_variables { template void f(T) {} template inline T g(T) { return T{}; } template<> inline void f<>(int) {} template<> int g<>(int) { return 5; } } } // namespace cxx17 #endif // __cplusplus < 201703L && !defined _MSC_VER #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 202002L && !defined _MSC_VER #error "This is not a C++20 compiler" #else #include namespace cxx20 { // As C++20 supports feature test macros in the standard, there is no // immediate need to actually test for feature availability on the // Autoconf side. } // namespace cxx20 #endif // __cplusplus < 202002L && !defined _MSC_VER _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : eval $cachevar=yes else $as_nop eval $cachevar=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CXX="$ac_save_CXX" fi eval ac_res=\$$cachevar { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done if test x$ac_success = xyes; then break fi done fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test x$ax_cxx_compile_cxx20_required = xtrue; then if test x$ac_success = xno; then as_fn_error $? "*** A compiler with support for C++20 language features is required." "$LINENO" 5 fi fi if test x$ac_success = xno; then HAVE_CXX20=0 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: No compiler with C++20 support was found" >&5 printf "%s\n" "$as_me: No compiler with C++20 support was found" >&6;} else HAVE_CXX20=1 printf "%s\n" "#define HAVE_CXX20 1" >>confdefs.h fi CXX1XCXXFLAGS="$CXXFLAGS" CXXFLAGS="$save_CXXFLAGS" ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu save_CXXFLAGS="$CXXFLAGS" CXXFLAGS="$CXXFLAGS $CXX1XCXXFLAGS" # Check that std::future is available. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether std::future is available" >&5 printf %s "checking whether std::future is available... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { std::vector> v; (void)v; ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : printf "%s\n" "#define HAVE_STD_FUTURE 1" >>confdefs.h have_std_future=yes { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop have_std_future=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext # Check that std::atomic> is supported. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether std::atomic> is supported" >&5 printf %s "checking whether std::atomic> is supported... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { auto a = std::atomic>(std::make_shared(1000000007)); auto p = a.load(); ++*p; a.store(p); ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : printf "%s\n" "#define HAVE_ATOMIC_STD_SHARED_PTR 1" >>confdefs.h have_atomic_std_shared_ptr=yes { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop have_atomic_std_shared_ptr=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext # Check that std::chrono::time_zone is available. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether std::chrono::time_zone is available" >&5 printf %s "checking whether std::chrono::time_zone is available... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { auto tz = std::chrono::current_zone(); (void)tz; ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : printf "%s\n" "#define HAVE_STD_CHRONO_TIME_ZONE 1" >>confdefs.h have_std_chrono_time_zone=yes { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop have_std_chrono_time_zone=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CXXFLAGS=$save_CXXFLAGS ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Checks for libraries. # Additional libraries required for tests. TESTLDADD= # Additional libraries required for programs under src directory. APPLDFLAGS= case "$host_os" in *android*) android_build=yes # android does not need -pthread, but needs following 2 libs for C++ APPLDFLAGS="$APPLDFLAGS -latomic" ;; *) PTHREAD_LDFLAGS="-pthread" APPLDFLAGS="$APPLDFLAGS $PTHREAD_LDFLAGS" ;; esac case "$host_os" in *solaris*) APPLDFLAGS="$APPLDFLAGS -lsocket -lnsl" ;; esac case "${build}" in *-apple-darwin*) EXTRA_DEFS="-D__APPLE_USE_RFC_3542" ;; esac # zlib have_zlib=no if test "x${request_zlib}" != "xno"; then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for zlib >= 1.2.3" >&5 printf %s "checking for zlib >= 1.2.3... " >&6; } if test -n "$ZLIB_CFLAGS"; then pkg_cv_ZLIB_CFLAGS="$ZLIB_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib >= 1.2.3\""; } >&5 ($PKG_CONFIG --exists --print-errors "zlib >= 1.2.3") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_ZLIB_CFLAGS=`$PKG_CONFIG --cflags "zlib >= 1.2.3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$ZLIB_LIBS"; then pkg_cv_ZLIB_LIBS="$ZLIB_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib >= 1.2.3\""; } >&5 ($PKG_CONFIG --exists --print-errors "zlib >= 1.2.3") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_ZLIB_LIBS=`$PKG_CONFIG --libs "zlib >= 1.2.3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then ZLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "zlib >= 1.2.3" 2>&1` else ZLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "zlib >= 1.2.3" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$ZLIB_PKG_ERRORS" >&5 have_zlib=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } have_zlib=no else ZLIB_CFLAGS=$pkg_cv_ZLIB_CFLAGS ZLIB_LIBS=$pkg_cv_ZLIB_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } have_zlib=yes fi if test "x${have_zlib}" = "xno"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ZLIB_PKG_ERRORS" >&5 printf "%s\n" "$as_me: $ZLIB_PKG_ERRORS" >&6;} fi fi if test "x${request_zlib}" = "xyes" && test "x${have_zlib}" != "xyes"; then as_fn_error $? "zlib was requested (--with-zlib) but not found" "$LINENO" 5 fi # dl: openssl requires libdl when it is statically linked. case "${host_os}" in *bsd*) # dlopen is in libc on *BSD ;; *) save_LIBS=$LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing dlopen" >&5 printf %s "checking for library containing dlopen... " >&6; } if test ${ac_cv_search_dlopen+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char dlopen (); int main (void) { return dlopen (); ; return 0; } _ACEOF for ac_lib in '' dl do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_dlopen=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_dlopen+y} then : break fi done if test ${ac_cv_search_dlopen+y} then : else $as_nop ac_cv_search_dlopen=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dlopen" >&5 printf "%s\n" "$ac_cv_search_dlopen" >&6; } ac_res=$ac_cv_search_dlopen if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" APPLDFLAGS="-ldl $APPLDFLAGS" fi LIBS=$save_LIBS ;; esac # libev (for src) have_libev=no if test "x${request_libev}" != "xno"; then if test "x${LIBEV_LIBS}" = "x" && test "x${LIBEV_CFLAGS}" = "x"; then # libev does not have pkg-config file. Check it in an old way. save_LIBS=$LIBS # android requires -lm for floor { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ev_time in -lev" >&5 printf %s "checking for ev_time in -lev... " >&6; } if test ${ac_cv_lib_ev_ev_time+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lev -lm $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char ev_time (); int main (void) { return ev_time (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_ev_ev_time=yes else $as_nop ac_cv_lib_ev_ev_time=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ev_ev_time" >&5 printf "%s\n" "$ac_cv_lib_ev_ev_time" >&6; } if test "x$ac_cv_lib_ev_ev_time" = xyes then : have_libev=yes else $as_nop have_libev=no fi if test "x${have_libev}" = "xyes"; then ac_fn_c_check_header_compile "$LINENO" "ev.h" "ac_cv_header_ev_h" "$ac_includes_default" if test "x$ac_cv_header_ev_h" = xyes then : have_libev=yes else $as_nop have_libev=no fi if test "x${have_libev}" = "xyes"; then LIBEV_LIBS=-lev LIBEV_CFLAGS= fi fi LIBS=$save_LIBS else have_libev=yes fi if test "x${have_libev}" = "xyes"; then printf "%s\n" "#define HAVE_LIBEV 1" >>confdefs.h fi fi if test "x${request_libev}" = "xyes" && test "x${have_libev}" != "xyes"; then as_fn_error $? "libev was requested (--with-libev) but not found" "$LINENO" 5 fi if test "x${request_openssl}" = "xyes" && test "x${request_wolfssl}" = "xyes"; then as_fn_error $? "Requesting both OpenSSL and wolfSSL is not allowed" "$LINENO" 5 fi # openssl (for src) have_openssl=no if test "x${request_openssl}" != "xno" && test "x${request_wolfssl}" != "xyes"; then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for openssl >= 1.1.1" >&5 printf %s "checking for openssl >= 1.1.1... " >&6; } if test -n "$OPENSSL_CFLAGS"; then pkg_cv_OPENSSL_CFLAGS="$OPENSSL_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl >= 1.1.1\""; } >&5 ($PKG_CONFIG --exists --print-errors "openssl >= 1.1.1") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_OPENSSL_CFLAGS=`$PKG_CONFIG --cflags "openssl >= 1.1.1" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$OPENSSL_LIBS"; then pkg_cv_OPENSSL_LIBS="$OPENSSL_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl >= 1.1.1\""; } >&5 ($PKG_CONFIG --exists --print-errors "openssl >= 1.1.1") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_OPENSSL_LIBS=`$PKG_CONFIG --libs "openssl >= 1.1.1" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then OPENSSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "openssl >= 1.1.1" 2>&1` else OPENSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "openssl >= 1.1.1" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$OPENSSL_PKG_ERRORS" >&5 have_openssl=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } have_openssl=no else OPENSSL_CFLAGS=$pkg_cv_OPENSSL_CFLAGS OPENSSL_LIBS=$pkg_cv_OPENSSL_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } have_openssl=yes fi if test "x${have_openssl}" = "xno"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $OPENSSL_PKG_ERRORS" >&5 printf "%s\n" "$as_me: $OPENSSL_PKG_ERRORS" >&6;} else # Use C++ compiler because boringssl needs C++ runtime. ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu save_CXXFLAGS="$CXXFLAGS" save_LIBS="$LIBS" CXXFLAGS="$OPENSSL_CFLAGS $CXXFLAGS" LIBS="$OPENSSL_LIBS $LIBS" # quictls/openssl has SSL_provide_quic_data. boringssl also has # it. We will deal with it later. have_ssl_provide_quic_data=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for SSL_provide_quic_data" >&5 printf %s "checking for SSL_provide_quic_data... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { SSL_provide_quic_data(NULL, (ssl_encryption_level_t)0, NULL, 0); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; }; have_ssl_provide_quic_data=yes else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; }; have_ssl_provide_quic_data=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext # Check whether this is libressl or not { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX options needed to detect all undeclared functions" >&5 printf %s "checking for $CXX options needed to detect all undeclared functions... " >&6; } if test ${ac_cv_cxx_undeclared_builtin_options+y} then : printf %s "(cached) " >&6 else $as_nop ac_save_CFLAGS=$CFLAGS ac_cv_cxx_undeclared_builtin_options='cannot detect' for ac_arg in '' -fno-builtin; do CFLAGS="$ac_save_CFLAGS $ac_arg" # This test program should *not* compile successfully. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { (void) strchr; ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : else $as_nop # This test program should compile successfully. # No library function is consistently available on # freestanding implementations, so test against a dummy # declaration. Include always-available headers on the # off chance that they somehow elicit warnings. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include extern void ac_decl (int, char *); int main (void) { (void) ac_decl (0, (char *) 0); (void) ac_decl; ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : if test x"$ac_arg" = x then : ac_cv_cxx_undeclared_builtin_options='none needed' else $as_nop ac_cv_cxx_undeclared_builtin_options=$ac_arg fi break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done CFLAGS=$ac_save_CFLAGS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_undeclared_builtin_options" >&5 printf "%s\n" "$ac_cv_cxx_undeclared_builtin_options" >&6; } case $ac_cv_cxx_undeclared_builtin_options in #( 'cannot detect') : { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot make $CXX report undeclared builtins See \`config.log' for more details" "$LINENO" 5; } ;; #( 'none needed') : ac_cxx_undeclared_builtin_options='' ;; #( *) : ac_cxx_undeclared_builtin_options=$ac_cv_cxx_undeclared_builtin_options ;; esac ac_fn_check_decl "$LINENO" "LIBRESSL_VERSION_NUMBER" "ac_cv_have_decl_LIBRESSL_VERSION_NUMBER" " #include " "$ac_cxx_undeclared_builtin_options" "CXXFLAGS" if test "x$ac_cv_have_decl_LIBRESSL_VERSION_NUMBER" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_LIBRESSL_VERSION_NUMBER $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : have_libressl=yes else $as_nop have_libressl=no fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for SSL_set_quic_tls_cbs" >&5 printf %s "checking for SSL_set_quic_tls_cbs... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { SSL_set_quic_tls_cbs(NULL, NULL, NULL); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; }; have_ossl_quic=yes else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; }; have_ossl_quic=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext # boringssl has SSL_set_quic_early_data_context. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for SSL_set_quic_early_data_context" >&5 printf %s "checking for SSL_set_quic_early_data_context... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { SSL *ssl = NULL; SSL_set_quic_early_data_context(ssl, NULL, 0); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; }; have_boringssl_quic=yes else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; }; have_boringssl_quic=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext CXXFLAGS="$save_CXXFLAGS" LIBS="$save_LIBS" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi fi if test "x${request_openssl}" = "xyes" && test "x${have_openssl}" != "xyes"; then as_fn_error $? "openssl was requested (--with-openssl) but not found" "$LINENO" 5 fi # wolfSSL (for src) have_wolfssl=no if test "x${request_wolfssl}" != "xno" && test "x${request_openssl}" != "xyes" && test "x${have_openssl}" != "xyes"; then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for wolfssl >= 5.7.0" >&5 printf %s "checking for wolfssl >= 5.7.0... " >&6; } if test -n "$WOLFSSL_CFLAGS"; then pkg_cv_WOLFSSL_CFLAGS="$WOLFSSL_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"wolfssl >= 5.7.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "wolfssl >= 5.7.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_WOLFSSL_CFLAGS=`$PKG_CONFIG --cflags "wolfssl >= 5.7.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$WOLFSSL_LIBS"; then pkg_cv_WOLFSSL_LIBS="$WOLFSSL_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"wolfssl >= 5.7.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "wolfssl >= 5.7.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_WOLFSSL_LIBS=`$PKG_CONFIG --libs "wolfssl >= 5.7.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then WOLFSSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "wolfssl >= 5.7.0" 2>&1` else WOLFSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "wolfssl >= 5.7.0" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$WOLFSSL_PKG_ERRORS" >&5 have_wolfssl=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } have_wolfssl=no else WOLFSSL_CFLAGS=$pkg_cv_WOLFSSL_CFLAGS WOLFSSL_LIBS=$pkg_cv_WOLFSSL_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } have_wolfssl=yes fi if test "x${have_wolfssl}" = "xno"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $WOLFSSL_PKG_ERRORS" >&5 printf "%s\n" "$as_me: $WOLFSSL_PKG_ERRORS" >&6;} else printf "%s\n" "#define HAVE_WOLFSSL 1" >>confdefs.h save_CFLAGS="$CFLAGS" save_LIBS="$LIBS" CFLAGS="$WOLFSSL_CFLAGS $CFLAGS" LIBS="$WOLFSSL_LIBS $LIBS" have_wolfssl_quic=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for wolfSSL QUIC" >&5 printf %s "checking for wolfSSL QUIC... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { SSL_provide_quic_data(NULL, 0, NULL, 0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; }; have_wolfssl_quic=yes else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; }; have_wolfssl_quic=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext CFLAGS="$save_CFLAGS" LIBS="$save_LIBS" fi fi if test "x${request_wolfssl}" = "xyes" && test "x${have_wolfssl}" != "xyes"; then as_fn_error $? "wolfSSL was requested (--with-wolfssl) but not found" "$LINENO" 5 fi # c-ares (for src) have_libcares=no if test "x${request_libcares}" != "xno"; then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libcares >= 1.16.0" >&5 printf %s "checking for libcares >= 1.16.0... " >&6; } if test -n "$LIBCARES_CFLAGS"; then pkg_cv_LIBCARES_CFLAGS="$LIBCARES_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcares >= 1.16.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libcares >= 1.16.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBCARES_CFLAGS=`$PKG_CONFIG --cflags "libcares >= 1.16.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBCARES_LIBS"; then pkg_cv_LIBCARES_LIBS="$LIBCARES_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcares >= 1.16.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libcares >= 1.16.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBCARES_LIBS=`$PKG_CONFIG --libs "libcares >= 1.16.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBCARES_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libcares >= 1.16.0" 2>&1` else LIBCARES_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libcares >= 1.16.0" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBCARES_PKG_ERRORS" >&5 have_libcares=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } have_libcares=no else LIBCARES_CFLAGS=$pkg_cv_LIBCARES_CFLAGS LIBCARES_LIBS=$pkg_cv_LIBCARES_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } have_libcares=yes fi if test "x${have_libcares}" = "xno"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $LIBCARES_PKG_ERRORS" >&5 printf "%s\n" "$as_me: $LIBCARES_PKG_ERRORS" >&6;} fi fi if test "x${request_libcares}" = "xyes" && test "x${have_libcares}" != "xyes"; then as_fn_error $? "libcares was requested (--with-libcares) but not found" "$LINENO" 5 fi # ngtcp2 (for src) have_libngtcp2=no if test "x${request_libngtcp2}" != "xno"; then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libngtcp2 >= 1.16.0" >&5 printf %s "checking for libngtcp2 >= 1.16.0... " >&6; } if test -n "$LIBNGTCP2_CFLAGS"; then pkg_cv_LIBNGTCP2_CFLAGS="$LIBNGTCP2_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libngtcp2 >= 1.16.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libngtcp2 >= 1.16.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNGTCP2_CFLAGS=`$PKG_CONFIG --cflags "libngtcp2 >= 1.16.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBNGTCP2_LIBS"; then pkg_cv_LIBNGTCP2_LIBS="$LIBNGTCP2_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libngtcp2 >= 1.16.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libngtcp2 >= 1.16.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNGTCP2_LIBS=`$PKG_CONFIG --libs "libngtcp2 >= 1.16.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBNGTCP2_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libngtcp2 >= 1.16.0" 2>&1` else LIBNGTCP2_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libngtcp2 >= 1.16.0" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBNGTCP2_PKG_ERRORS" >&5 have_libngtcp2=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } have_libngtcp2=no else LIBNGTCP2_CFLAGS=$pkg_cv_LIBNGTCP2_CFLAGS LIBNGTCP2_LIBS=$pkg_cv_LIBNGTCP2_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } have_libngtcp2=yes fi if test "x${have_libngtcp2}" = "xno"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $LIBNGTCP2_PKG_ERRORS" >&5 printf "%s\n" "$as_me: $LIBNGTCP2_PKG_ERRORS" >&6;} fi fi if test "x${request_libngtcp2}" = "xyes" && test "x${have_libngtcp2}" != "xyes"; then as_fn_error $? "libngtcp2 was requested (--with-libngtcp2) but not found" "$LINENO" 5 fi # ngtcp2_crypto_wolfssl (for src) have_libngtcp2_crypto_wolfssl=no if test "x${have_wolfssl_quic}" = "xyes" && test "x${request_libngtcp2}" != "xno"; then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libngtcp2_crypto_wolfssl >= 1.16.0" >&5 printf %s "checking for libngtcp2_crypto_wolfssl >= 1.16.0... " >&6; } if test -n "$LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS"; then pkg_cv_LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS="$LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libngtcp2_crypto_wolfssl >= 1.16.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libngtcp2_crypto_wolfssl >= 1.16.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS=`$PKG_CONFIG --cflags "libngtcp2_crypto_wolfssl >= 1.16.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBNGTCP2_CRYPTO_WOLFSSL_LIBS"; then pkg_cv_LIBNGTCP2_CRYPTO_WOLFSSL_LIBS="$LIBNGTCP2_CRYPTO_WOLFSSL_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libngtcp2_crypto_wolfssl >= 1.16.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libngtcp2_crypto_wolfssl >= 1.16.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNGTCP2_CRYPTO_WOLFSSL_LIBS=`$PKG_CONFIG --libs "libngtcp2_crypto_wolfssl >= 1.16.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBNGTCP2_CRYPTO_WOLFSSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libngtcp2_crypto_wolfssl >= 1.16.0" 2>&1` else LIBNGTCP2_CRYPTO_WOLFSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libngtcp2_crypto_wolfssl >= 1.16.0" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBNGTCP2_CRYPTO_WOLFSSL_PKG_ERRORS" >&5 have_libngtcp2_crypto_wolfssl=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } have_libngtcp2_crypto_wolfssl=no else LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS=$pkg_cv_LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS LIBNGTCP2_CRYPTO_WOLFSSL_LIBS=$pkg_cv_LIBNGTCP2_CRYPTO_WOLFSSL_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } have_libngtcp2_crypto_wolfssl=yes fi if test "x${have_libngtcp2_crypto_wolfssl}" = "xno"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $LIBNGTCP2_CRYPTO_WOLFSSL_PKG_ERRORS" >&5 printf "%s\n" "$as_me: $LIBNGTCP2_CRYPTO_WOLFSSL_PKG_ERRORS" >&6;} else printf "%s\n" "#define HAVE_LIBNGTCP2_CRYPTO_WOLFSSL 1" >>confdefs.h fi fi if test "x${have_wolfssl_quic}" = "xyes" && test "x${request_libngtcp2}" = "xyes" && test "x${have_libngtcp2_crypto_wolfssl}" != "xyes"; then as_fn_error $? "libngtcp2_crypto_wolfssl was requested (--with-libngtcp2) but not found" "$LINENO" 5 fi # ngtcp2_crypto_quictls (for src) have_libngtcp2_crypto_quictls=no if test "x${have_ssl_provide_quic_data}" = "xyes" && test "x${have_libressl}" != "xyes" && test "x${have_boringssl_quic}" != "xyes" && test "x${request_libngtcp2}" != "xno"; then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libngtcp2_crypto_quictls >= 1.16.0" >&5 printf %s "checking for libngtcp2_crypto_quictls >= 1.16.0... " >&6; } if test -n "$LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS"; then pkg_cv_LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS="$LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libngtcp2_crypto_quictls >= 1.16.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libngtcp2_crypto_quictls >= 1.16.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS=`$PKG_CONFIG --cflags "libngtcp2_crypto_quictls >= 1.16.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBNGTCP2_CRYPTO_QUICTLS_LIBS"; then pkg_cv_LIBNGTCP2_CRYPTO_QUICTLS_LIBS="$LIBNGTCP2_CRYPTO_QUICTLS_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libngtcp2_crypto_quictls >= 1.16.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libngtcp2_crypto_quictls >= 1.16.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNGTCP2_CRYPTO_QUICTLS_LIBS=`$PKG_CONFIG --libs "libngtcp2_crypto_quictls >= 1.16.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBNGTCP2_CRYPTO_QUICTLS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libngtcp2_crypto_quictls >= 1.16.0" 2>&1` else LIBNGTCP2_CRYPTO_QUICTLS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libngtcp2_crypto_quictls >= 1.16.0" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBNGTCP2_CRYPTO_QUICTLS_PKG_ERRORS" >&5 have_libngtcp2_crypto_quictls=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } have_libngtcp2_crypto_quictls=no else LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS=$pkg_cv_LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS LIBNGTCP2_CRYPTO_QUICTLS_LIBS=$pkg_cv_LIBNGTCP2_CRYPTO_QUICTLS_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } have_libngtcp2_crypto_quictls=yes fi if test "x${have_libngtcp2_crypto_quictls}" = "xno"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $LIBNGTCP2_CRYPTO_QUICTLS_PKG_ERRORS" >&5 printf "%s\n" "$as_me: $LIBNGTCP2_CRYPTO_QUICTLS_PKG_ERRORS" >&6;} else printf "%s\n" "#define HAVE_LIBNGTCP2_CRYPTO_QUICTLS 1" >>confdefs.h fi fi if test "x${have_ssl_provide_quic_data}" = "xyes" && test "x${have_libressl}" != "xyes" && test "x${have_boringssl_quic}" != "xyes" && test "x${request_libngtcp2}" = "xyes" && test "x${have_libngtcp2_crypto_quictls}" != "xyes"; then as_fn_error $? "libngtcp2_crypto_quictls was requested (--with-libngtcp2) but not found" "$LINENO" 5 fi # ngtcp2_crypto_libressl (for src) have_libngtcp2_crypto_libressl=no if test "x${have_ssl_provide_quic_data}" = "xyes" && test "x${have_libressl}" = "xyes" && test "x${request_libngtcp2}" != "xno"; then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libngtcp2_crypto_libressl >= 1.16.0" >&5 printf %s "checking for libngtcp2_crypto_libressl >= 1.16.0... " >&6; } if test -n "$LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS"; then pkg_cv_LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS="$LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libngtcp2_crypto_libressl >= 1.16.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libngtcp2_crypto_libressl >= 1.16.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS=`$PKG_CONFIG --cflags "libngtcp2_crypto_libressl >= 1.16.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBNGTCP2_CRYPTO_LIBRESSL_LIBS"; then pkg_cv_LIBNGTCP2_CRYPTO_LIBRESSL_LIBS="$LIBNGTCP2_CRYPTO_LIBRESSL_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libngtcp2_crypto_libressl >= 1.16.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libngtcp2_crypto_libressl >= 1.16.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNGTCP2_CRYPTO_LIBRESSL_LIBS=`$PKG_CONFIG --libs "libngtcp2_crypto_libressl >= 1.16.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBNGTCP2_CRYPTO_LIBRESSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libngtcp2_crypto_libressl >= 1.16.0" 2>&1` else LIBNGTCP2_CRYPTO_LIBRESSL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libngtcp2_crypto_libressl >= 1.16.0" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBNGTCP2_CRYPTO_LIBRESSL_PKG_ERRORS" >&5 have_libngtcp2_crypto_libressl=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } have_libngtcp2_crypto_libressl=no else LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS=$pkg_cv_LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS LIBNGTCP2_CRYPTO_LIBRESSL_LIBS=$pkg_cv_LIBNGTCP2_CRYPTO_LIBRESSL_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } have_libngtcp2_crypto_libressl=yes fi if test "x${have_libngtcp2_crypto_libressl}" = "xno"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $LIBNGTCP2_CRYPTO_LIBRESSL_PKG_ERRORS" >&5 printf "%s\n" "$as_me: $LIBNGTCP2_CRYPTO_LIBRESSL_PKG_ERRORS" >&6;} else printf "%s\n" "#define HAVE_LIBNGTCP2_CRYPTO_LIBRESSL 1" >>confdefs.h fi fi if test "x${have_ssl_provide_quic_data}" = "xyes" && test "x${have_libressl}" = "xyes" && test "x${request_libngtcp2}" = "xyes" && test "x${have_libngtcp2_crypto_libressl}" != "xyes"; then as_fn_error $? "libngtcp2_crypto_libressl was requested (--with-libngtcp2) but not found" "$LINENO" 5 fi # ngtcp2_crypto_boringssl (for src) have_libngtcp2_crypto_boringssl=no if test "x${have_boringssl_quic}" = "xyes" && test "x${request_libngtcp2}" != "xno"; then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libngtcp2_crypto_boringssl >= 0.0.0" >&5 printf %s "checking for libngtcp2_crypto_boringssl >= 0.0.0... " >&6; } if test -n "$LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS"; then pkg_cv_LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS="$LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libngtcp2_crypto_boringssl >= 0.0.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libngtcp2_crypto_boringssl >= 0.0.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS=`$PKG_CONFIG --cflags "libngtcp2_crypto_boringssl >= 0.0.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBNGTCP2_CRYPTO_BORINGSSL_LIBS"; then pkg_cv_LIBNGTCP2_CRYPTO_BORINGSSL_LIBS="$LIBNGTCP2_CRYPTO_BORINGSSL_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libngtcp2_crypto_boringssl >= 0.0.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libngtcp2_crypto_boringssl >= 0.0.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNGTCP2_CRYPTO_BORINGSSL_LIBS=`$PKG_CONFIG --libs "libngtcp2_crypto_boringssl >= 0.0.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBNGTCP2_CRYPTO_BORINGSSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libngtcp2_crypto_boringssl >= 0.0.0" 2>&1` else LIBNGTCP2_CRYPTO_BORINGSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libngtcp2_crypto_boringssl >= 0.0.0" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBNGTCP2_CRYPTO_BORINGSSL_PKG_ERRORS" >&5 have_libngtcp2_crypto_boringssl=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } have_libngtcp2_crypto_boringssl=no else LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS=$pkg_cv_LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS LIBNGTCP2_CRYPTO_BORINGSSL_LIBS=$pkg_cv_LIBNGTCP2_CRYPTO_BORINGSSL_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } have_libngtcp2_crypto_boringssl=yes fi if test "x${have_libngtcp2_crypto_boringssl}" = "xno"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $LIBNGTCP2_CRYPTO_BORINGSSL_PKG_ERRORS" >&5 printf "%s\n" "$as_me: $LIBNGTCP2_CRYPTO_BORINGSSL_PKG_ERRORS" >&6;} else printf "%s\n" "#define HAVE_LIBNGTCP2_CRYPTO_BORINGSSL 1" >>confdefs.h fi fi if test "x${have_boringssl_quic}" = "xyes" && test "x${request_libngtcp2}" = "xyes" && test "x${have_libngtcp2_crypto_boringssl}" != "xyes"; then as_fn_error $? "libngtcp2_crypto_boringssl was requested (--with-libngtcp2) but not found" "$LINENO" 5 fi # ngtcp2_crypto_ossl (for src) have_libngtcp2_crypto_ossl=no if test "x${have_ossl_quic}" = "xyes" && test "x${request_libngtcp2}" != "xno"; then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libngtcp2_crypto_ossl >= 1.16.0" >&5 printf %s "checking for libngtcp2_crypto_ossl >= 1.16.0... " >&6; } if test -n "$LIBNGTCP2_CRYPTO_OSSL_CFLAGS"; then pkg_cv_LIBNGTCP2_CRYPTO_OSSL_CFLAGS="$LIBNGTCP2_CRYPTO_OSSL_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libngtcp2_crypto_ossl >= 1.16.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libngtcp2_crypto_ossl >= 1.16.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNGTCP2_CRYPTO_OSSL_CFLAGS=`$PKG_CONFIG --cflags "libngtcp2_crypto_ossl >= 1.16.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBNGTCP2_CRYPTO_OSSL_LIBS"; then pkg_cv_LIBNGTCP2_CRYPTO_OSSL_LIBS="$LIBNGTCP2_CRYPTO_OSSL_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libngtcp2_crypto_ossl >= 1.16.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libngtcp2_crypto_ossl >= 1.16.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNGTCP2_CRYPTO_OSSL_LIBS=`$PKG_CONFIG --libs "libngtcp2_crypto_ossl >= 1.16.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBNGTCP2_CRYPTO_OSSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libngtcp2_crypto_ossl >= 1.16.0" 2>&1` else LIBNGTCP2_CRYPTO_OSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libngtcp2_crypto_ossl >= 1.16.0" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBNGTCP2_CRYPTO_OSSL_PKG_ERRORS" >&5 have_libngtcp2_crypto_ossl=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } have_libngtcp2_crypto_ossl=no else LIBNGTCP2_CRYPTO_OSSL_CFLAGS=$pkg_cv_LIBNGTCP2_CRYPTO_OSSL_CFLAGS LIBNGTCP2_CRYPTO_OSSL_LIBS=$pkg_cv_LIBNGTCP2_CRYPTO_OSSL_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } have_libngtcp2_crypto_ossl=yes fi if test "x${have_libngtcp2_crypto_ossl}" = "xno"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $LIBNGTCP2_CRYPTO_OSSL_PKG_ERRORS" >&5 printf "%s\n" "$as_me: $LIBNGTCP2_CRYPTO_OSSL_PKG_ERRORS" >&6;} else printf "%s\n" "#define HAVE_LIBNGTCP2_CRYPTO_OSSL 1" >>confdefs.h fi fi if test "x${have_ossl_quic}" = "xyes" && test "x${request_libngtcp2}" = "xyes" && test "x${have_libngtcp2_crypto_ossl}" != "xyes"; then as_fn_error $? "libngtcp2_crypto_ossl was requested (--with-libngtcp2) but not found" "$LINENO" 5 fi # nghttp3 (for src) have_libnghttp3=no if test "x${request_libnghttp3}" != "xno"; then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libnghttp3 >= 1.12.0" >&5 printf %s "checking for libnghttp3 >= 1.12.0... " >&6; } if test -n "$LIBNGHTTP3_CFLAGS"; then pkg_cv_LIBNGHTTP3_CFLAGS="$LIBNGHTTP3_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libnghttp3 >= 1.12.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libnghttp3 >= 1.12.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNGHTTP3_CFLAGS=`$PKG_CONFIG --cflags "libnghttp3 >= 1.12.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBNGHTTP3_LIBS"; then pkg_cv_LIBNGHTTP3_LIBS="$LIBNGHTTP3_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libnghttp3 >= 1.12.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libnghttp3 >= 1.12.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNGHTTP3_LIBS=`$PKG_CONFIG --libs "libnghttp3 >= 1.12.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBNGHTTP3_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libnghttp3 >= 1.12.0" 2>&1` else LIBNGHTTP3_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libnghttp3 >= 1.12.0" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBNGHTTP3_PKG_ERRORS" >&5 have_libnghttp3=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } have_libnghttp3=no else LIBNGHTTP3_CFLAGS=$pkg_cv_LIBNGHTTP3_CFLAGS LIBNGHTTP3_LIBS=$pkg_cv_LIBNGHTTP3_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } have_libnghttp3=yes fi if test "x${have_libnghttp3}" = "xno"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $LIBNGHTTP3_PKG_ERRORS" >&5 printf "%s\n" "$as_me: $LIBNGHTTP3_PKG_ERRORS" >&6;} fi fi if test "x${request_libnghttp3}" = "xyes" && test "x${have_libnghttp3}" != "xyes"; then as_fn_error $? "libnghttp3 was requested (--with-libnghttp3) but not found" "$LINENO" 5 fi # libbpf (for src) have_libbpf=no if test "x${request_libbpf}" != "xno"; then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libbpf >= 0.7.0" >&5 printf %s "checking for libbpf >= 0.7.0... " >&6; } if test -n "$LIBBPF_CFLAGS"; then pkg_cv_LIBBPF_CFLAGS="$LIBBPF_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libbpf >= 0.7.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libbpf >= 0.7.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBBPF_CFLAGS=`$PKG_CONFIG --cflags "libbpf >= 0.7.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBBPF_LIBS"; then pkg_cv_LIBBPF_LIBS="$LIBBPF_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libbpf >= 0.7.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libbpf >= 0.7.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBBPF_LIBS=`$PKG_CONFIG --libs "libbpf >= 0.7.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBBPF_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libbpf >= 0.7.0" 2>&1` else LIBBPF_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libbpf >= 0.7.0" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBBPF_PKG_ERRORS" >&5 have_libbpf=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } have_libbpf=no else LIBBPF_CFLAGS=$pkg_cv_LIBBPF_CFLAGS LIBBPF_LIBS=$pkg_cv_LIBBPF_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } have_libbpf=yes fi if test "x${have_libbpf}" = "xyes"; then printf "%s\n" "#define HAVE_LIBBPF 1" >>confdefs.h if test "x${BPFCFLAGS}" = "x"; then BPFCFLAGS="-Wall -O2 -g" fi # Add the include path for Debian EXTRABPFCFLAGS="-I/usr/include/$host_cpu-$host_os" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether enum bpf_stats_type is defined in linux/bpf.h" >&5 printf %s "checking whether enum bpf_stats_type is defined in linux/bpf.h... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { enum bpf_stats_type foo; (void)foo; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : have_bpf_stats_type=yes else $as_nop have_bpf_stats_type=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if test "x${have_bpf_stats_type}" = "xyes"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define HAVE_BPF_STATS_TYPE 1" >>confdefs.h else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $LIBBPF_PKG_ERRORS" >&5 printf "%s\n" "$as_me: $LIBBPF_PKG_ERRORS" >&6;} fi fi if test "x${request_libbpf}" = "xyes" && test "x${have_libbpf}" != "xyes"; then as_fn_error $? "libbpf was requested (--with-libbpf) but not found" "$LINENO" 5 fi if test "x${have_libbpf}" = "xyes" ; then HAVE_LIBBPF_TRUE= HAVE_LIBBPF_FALSE='#' else HAVE_LIBBPF_TRUE='#' HAVE_LIBBPF_FALSE= fi # libbrotlienc (for src) have_libbrotlienc=no if test "x${request_libbrotlienc}" != "xno"; then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libbrotlienc >= 1.0.9" >&5 printf %s "checking for libbrotlienc >= 1.0.9... " >&6; } if test -n "$LIBBROTLIENC_CFLAGS"; then pkg_cv_LIBBROTLIENC_CFLAGS="$LIBBROTLIENC_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libbrotlienc >= 1.0.9\""; } >&5 ($PKG_CONFIG --exists --print-errors "libbrotlienc >= 1.0.9") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBBROTLIENC_CFLAGS=`$PKG_CONFIG --cflags "libbrotlienc >= 1.0.9" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBBROTLIENC_LIBS"; then pkg_cv_LIBBROTLIENC_LIBS="$LIBBROTLIENC_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libbrotlienc >= 1.0.9\""; } >&5 ($PKG_CONFIG --exists --print-errors "libbrotlienc >= 1.0.9") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBBROTLIENC_LIBS=`$PKG_CONFIG --libs "libbrotlienc >= 1.0.9" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBBROTLIENC_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libbrotlienc >= 1.0.9" 2>&1` else LIBBROTLIENC_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libbrotlienc >= 1.0.9" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBBROTLIENC_PKG_ERRORS" >&5 have_libbrotlienc=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } have_libbrotlienc=no else LIBBROTLIENC_CFLAGS=$pkg_cv_LIBBROTLIENC_CFLAGS LIBBROTLIENC_LIBS=$pkg_cv_LIBBROTLIENC_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } have_libbrotlienc=yes fi if test "x${have_libbrotlienc}" = "xno"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $LIBBROTLIENC_PKG_ERRORS" >&5 printf "%s\n" "$as_me: $LIBBROTLIENC_PKG_ERRORS" >&6;} fi fi if test "x${request_libbrotlienc}" = "xyes" && test "x${have_libbrotlienc}" != "xyes"; then as_fn_error $? "libbrotlienc was requested (--with-libbrotlienc) but not found" "$LINENO" 5 fi # libbrotlidec (for src) have_libbrotlidec=no if test "x${request_libbrotlidec}" != "xno"; then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libbrotlidec >= 1.0.9" >&5 printf %s "checking for libbrotlidec >= 1.0.9... " >&6; } if test -n "$LIBBROTLIDEC_CFLAGS"; then pkg_cv_LIBBROTLIDEC_CFLAGS="$LIBBROTLIDEC_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libbrotlidec >= 1.0.9\""; } >&5 ($PKG_CONFIG --exists --print-errors "libbrotlidec >= 1.0.9") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBBROTLIDEC_CFLAGS=`$PKG_CONFIG --cflags "libbrotlidec >= 1.0.9" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBBROTLIDEC_LIBS"; then pkg_cv_LIBBROTLIDEC_LIBS="$LIBBROTLIDEC_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libbrotlidec >= 1.0.9\""; } >&5 ($PKG_CONFIG --exists --print-errors "libbrotlidec >= 1.0.9") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBBROTLIDEC_LIBS=`$PKG_CONFIG --libs "libbrotlidec >= 1.0.9" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBBROTLIDEC_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libbrotlidec >= 1.0.9" 2>&1` else LIBBROTLIDEC_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libbrotlidec >= 1.0.9" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBBROTLIDEC_PKG_ERRORS" >&5 have_libbrotlidec=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } have_libbrotlidec=no else LIBBROTLIDEC_CFLAGS=$pkg_cv_LIBBROTLIDEC_CFLAGS LIBBROTLIDEC_LIBS=$pkg_cv_LIBBROTLIDEC_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } have_libbrotlidec=yes fi if test "x${have_libbrotlidec}" = "xno"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $LIBBROTLIDEC_PKG_ERRORS" >&5 printf "%s\n" "$as_me: $LIBBROTLIDEC_PKG_ERRORS" >&6;} fi fi if test "x${request_libbrotlidec}" = "xyes" && test "x${have_libbrotlidec}" != "xyes"; then as_fn_error $? "libbrotlidec was requested (--with-libbrotlidec) but not found" "$LINENO" 5 fi have_libbrotli=no if test "x${have_libbrotlienc}" = "xyes" && test "x${have_libbrotlidec}" = "xyes"; then have_libbrotli=yes printf "%s\n" "#define HAVE_LIBBROTLI 1" >>confdefs.h fi # libevent_openssl (for examples) # 2.0.8 is required because we use evconnlistener_set_error_cb() have_libevent_openssl=no if test "x${request_libevent_openssl}" != "xno"; then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libevent_openssl >= 2.0.8" >&5 printf %s "checking for libevent_openssl >= 2.0.8... " >&6; } if test -n "$LIBEVENT_OPENSSL_CFLAGS"; then pkg_cv_LIBEVENT_OPENSSL_CFLAGS="$LIBEVENT_OPENSSL_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libevent_openssl >= 2.0.8\""; } >&5 ($PKG_CONFIG --exists --print-errors "libevent_openssl >= 2.0.8") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBEVENT_OPENSSL_CFLAGS=`$PKG_CONFIG --cflags "libevent_openssl >= 2.0.8" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBEVENT_OPENSSL_LIBS"; then pkg_cv_LIBEVENT_OPENSSL_LIBS="$LIBEVENT_OPENSSL_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libevent_openssl >= 2.0.8\""; } >&5 ($PKG_CONFIG --exists --print-errors "libevent_openssl >= 2.0.8") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBEVENT_OPENSSL_LIBS=`$PKG_CONFIG --libs "libevent_openssl >= 2.0.8" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBEVENT_OPENSSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libevent_openssl >= 2.0.8" 2>&1` else LIBEVENT_OPENSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libevent_openssl >= 2.0.8" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBEVENT_OPENSSL_PKG_ERRORS" >&5 have_libevent_openssl=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } have_libevent_openssl=no else LIBEVENT_OPENSSL_CFLAGS=$pkg_cv_LIBEVENT_OPENSSL_CFLAGS LIBEVENT_OPENSSL_LIBS=$pkg_cv_LIBEVENT_OPENSSL_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } have_libevent_openssl=yes fi if test "x${have_libevent_openssl}" = "xno"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $LIBEVENT_OPENSSL_PKG_ERRORS" >&5 printf "%s\n" "$as_me: $LIBEVENT_OPENSSL_PKG_ERRORS" >&6;} fi fi if test "x${request_libevent_openssl}" = "xyes" && test "x${have_libevent_openssl}" != "xyes"; then as_fn_error $? "libevent_openssl was requested (--with-libevent) but not found" "$LINENO" 5 fi # jansson (for src/nghttp, src/deflatehd and src/inflatehd) have_jansson=no if test "x${request_jansson}" != "xno"; then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for jansson >= 2.5" >&5 printf %s "checking for jansson >= 2.5... " >&6; } if test -n "$JANSSON_CFLAGS"; then pkg_cv_JANSSON_CFLAGS="$JANSSON_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"jansson >= 2.5\""; } >&5 ($PKG_CONFIG --exists --print-errors "jansson >= 2.5") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_JANSSON_CFLAGS=`$PKG_CONFIG --cflags "jansson >= 2.5" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$JANSSON_LIBS"; then pkg_cv_JANSSON_LIBS="$JANSSON_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"jansson >= 2.5\""; } >&5 ($PKG_CONFIG --exists --print-errors "jansson >= 2.5") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_JANSSON_LIBS=`$PKG_CONFIG --libs "jansson >= 2.5" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then JANSSON_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "jansson >= 2.5" 2>&1` else JANSSON_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "jansson >= 2.5" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$JANSSON_PKG_ERRORS" >&5 have_jansson=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } have_jansson=no else JANSSON_CFLAGS=$pkg_cv_JANSSON_CFLAGS JANSSON_LIBS=$pkg_cv_JANSSON_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } have_jansson=yes fi if test "x${have_jansson}" = "xyes"; then printf "%s\n" "#define HAVE_JANSSON 1" >>confdefs.h else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $JANSSON_PKG_ERRORS" >&5 printf "%s\n" "$as_me: $JANSSON_PKG_ERRORS" >&6;} fi fi if test "x${request_jansson}" = "xyes" && test "x${have_jansson}" != "xyes"; then as_fn_error $? "jansson was requested (--with-jansson) but not found" "$LINENO" 5 fi # libsystemd (for src/nghttpx) have_libsystemd=no if test "x${request_systemd}" != "xno"; then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libsystemd >= 209" >&5 printf %s "checking for libsystemd >= 209... " >&6; } if test -n "$SYSTEMD_CFLAGS"; then pkg_cv_SYSTEMD_CFLAGS="$SYSTEMD_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd >= 209\""; } >&5 ($PKG_CONFIG --exists --print-errors "libsystemd >= 209") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SYSTEMD_CFLAGS=`$PKG_CONFIG --cflags "libsystemd >= 209" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$SYSTEMD_LIBS"; then pkg_cv_SYSTEMD_LIBS="$SYSTEMD_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd >= 209\""; } >&5 ($PKG_CONFIG --exists --print-errors "libsystemd >= 209") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SYSTEMD_LIBS=`$PKG_CONFIG --libs "libsystemd >= 209" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsystemd >= 209" 2>&1` else SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsystemd >= 209" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$SYSTEMD_PKG_ERRORS" >&5 have_libsystemd=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } have_libsystemd=no else SYSTEMD_CFLAGS=$pkg_cv_SYSTEMD_CFLAGS SYSTEMD_LIBS=$pkg_cv_SYSTEMD_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } have_libsystemd=yes fi if test "x${have_libsystemd}" = "xyes"; then printf "%s\n" "#define HAVE_LIBSYSTEMD 1" >>confdefs.h else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $SYSTEMD_PKG_ERRORS" >&5 printf "%s\n" "$as_me: $SYSTEMD_PKG_ERRORS" >&6;} fi fi if test "x${request_systemd}" = "xyes" && test "x${have_libsystemd}" != "xyes"; then as_fn_error $? "systemd was requested (--with-systemd) but not found" "$LINENO" 5 fi # libxml2 (for src/nghttp) have_libxml2=no if test "x${request_libxml2}" != "xno"; then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libxml-2.0 >= 2.6.26" >&5 printf %s "checking for libxml-2.0 >= 2.6.26... " >&6; } if test -n "$LIBXML2_CFLAGS"; then pkg_cv_LIBXML2_CFLAGS="$LIBXML2_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libxml-2.0 >= 2.6.26\""; } >&5 ($PKG_CONFIG --exists --print-errors "libxml-2.0 >= 2.6.26") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBXML2_CFLAGS=`$PKG_CONFIG --cflags "libxml-2.0 >= 2.6.26" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBXML2_LIBS"; then pkg_cv_LIBXML2_LIBS="$LIBXML2_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libxml-2.0 >= 2.6.26\""; } >&5 ($PKG_CONFIG --exists --print-errors "libxml-2.0 >= 2.6.26") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBXML2_LIBS=`$PKG_CONFIG --libs "libxml-2.0 >= 2.6.26" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBXML2_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libxml-2.0 >= 2.6.26" 2>&1` else LIBXML2_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libxml-2.0 >= 2.6.26" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBXML2_PKG_ERRORS" >&5 have_libxml2=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } have_libxml2=no else LIBXML2_CFLAGS=$pkg_cv_LIBXML2_CFLAGS LIBXML2_LIBS=$pkg_cv_LIBXML2_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } have_libxml2=yes fi if test "x${have_libxml2}" = "xyes"; then printf "%s\n" "#define HAVE_LIBXML2 1" >>confdefs.h else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $LIBXML2_PKG_ERRORS" >&5 printf "%s\n" "$as_me: $LIBXML2_PKG_ERRORS" >&6;} fi fi if test "x${request_libxml2}" = "xyes" && test "x${have_libxml2}" != "xyes"; then as_fn_error $? "libxml2 was requested (--with-libxml2) but not found" "$LINENO" 5 fi if test "x${have_libxml2}" = "xyes" ; then HAVE_LIBXML2_TRUE= HAVE_LIBXML2_FALSE='#' else HAVE_LIBXML2_TRUE='#' HAVE_LIBXML2_FALSE= fi # jemalloc have_jemalloc=no if test "x${request_jemalloc}" != "xno"; then if test "x${JEMALLOC_LIBS}" = "x" && test "x${JEMALLOC_CFLAGS}" = "x"; then save_LIBS=$LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing malloc_stats_print" >&5 printf %s "checking for library containing malloc_stats_print... " >&6; } if test ${ac_cv_search_malloc_stats_print+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char malloc_stats_print (); int main (void) { return malloc_stats_print (); ; return 0; } _ACEOF for ac_lib in '' jemalloc do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $PTHREAD_LDFLAGS $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_malloc_stats_print=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_malloc_stats_print+y} then : break fi done if test ${ac_cv_search_malloc_stats_print+y} then : else $as_nop ac_cv_search_malloc_stats_print=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_malloc_stats_print" >&5 printf "%s\n" "$ac_cv_search_malloc_stats_print" >&6; } ac_res=$ac_cv_search_malloc_stats_print if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" have_jemalloc=yes fi if test "x${have_jemalloc}" = "xyes"; then jemalloc_libs=${ac_cv_search_malloc_stats_print} else # On Darwin, malloc_stats_print is je_malloc_stats_print { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing je_malloc_stats_print" >&5 printf %s "checking for library containing je_malloc_stats_print... " >&6; } if test ${ac_cv_search_je_malloc_stats_print+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char je_malloc_stats_print (); int main (void) { return je_malloc_stats_print (); ; return 0; } _ACEOF for ac_lib in '' jemalloc do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $PTHREAD_LDFLAGS $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_je_malloc_stats_print=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_je_malloc_stats_print+y} then : break fi done if test ${ac_cv_search_je_malloc_stats_print+y} then : else $as_nop ac_cv_search_je_malloc_stats_print=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_je_malloc_stats_print" >&5 printf "%s\n" "$ac_cv_search_je_malloc_stats_print" >&6; } ac_res=$ac_cv_search_je_malloc_stats_print if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" have_jemalloc=yes fi if test "x${have_jemalloc}" = "xyes"; then jemalloc_libs=${ac_cv_search_je_malloc_stats_print} fi fi LIBS=$save_LIBS if test "x${have_jemalloc}" = "xyes" && test "x${jemalloc_libs}" != "xnone required"; then JEMALLOC_LIBS=${jemalloc_libs} fi else have_jemalloc=yes fi fi if test "x${request_jemalloc}" = "xyes" && test "x${have_jemalloc}" != "xyes"; then as_fn_error $? "jemalloc was requested (--with-jemalloc) but not found" "$LINENO" 5 fi # The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL, # libev, and libc-ares. enable_app=no if test "x${request_app}" != "xno" && test "x${have_zlib}" = "xyes" && (test "x${have_openssl}" = "xyes" || test "x${have_wolfssl}" = "xyes") && test "x${have_libev}" = "xyes" && test "x${have_libcares}" = "xyes"; then enable_app=yes fi if test "x${request_app}" = "xyes" && test "x${enable_app}" != "xyes"; then as_fn_error $? "applications were requested (--enable-app) but dependencies are not met." "$LINENO" 5 fi if test "x${enable_app}" = "xyes" ; then ENABLE_APP_TRUE= ENABLE_APP_FALSE='#' else ENABLE_APP_TRUE='#' ENABLE_APP_FALSE= fi # Check HTTP/3 support enable_http3=no if test "x${request_http3}" != "xno" && test "x${have_libngtcp2}" = "xyes" && (test "x${have_libngtcp2_crypto_wolfssl}" = "xyes" || test "x${have_libngtcp2_crypto_quictls}" = "xyes" || test "x${have_libngtcp2_crypto_libressl}" = "xyes" || test "x${have_libngtcp2_crypto_boringssl}" = "xyes" || test "x${have_libngtcp2_crypto_ossl}" = "xyes") && test "x${have_libnghttp3}" = "xyes"; then enable_http3=yes printf "%s\n" "#define ENABLE_HTTP3 1" >>confdefs.h fi if test "x${request_http3}" = "xyes" && test "x${enable_http3}" != "xyes"; then as_fn_error $? "HTTP/3 was requested (--enable-http3) but dependencies are not met." "$LINENO" 5 fi if test "x${enable_http3}" = "xyes" ; then ENABLE_HTTP3_TRUE= ENABLE_HTTP3_FALSE='#' else ENABLE_HTTP3_TRUE='#' ENABLE_HTTP3_FALSE= fi enable_hpack_tools=no # HPACK tools requires jansson if test "x${request_hpack_tools}" != "xno" && test "x${have_jansson}" = "xyes"; then enable_hpack_tools=yes fi if test "x${request_hpack_tools}" = "xyes" && test "x${enable_hpack_tools}" != "xyes"; then as_fn_error $? "HPACK tools were requested (--enable-hpack-tools) but dependencies are not met." "$LINENO" 5 fi if test "x${enable_hpack_tools}" = "xyes" ; then ENABLE_HPACK_TOOLS_TRUE= ENABLE_HPACK_TOOLS_FALSE='#' else ENABLE_HPACK_TOOLS_TRUE='#' ENABLE_HPACK_TOOLS_FALSE= fi # The example programs depend on OpenSSL and libevent_openssl enable_examples=no if test "x${request_examples}" != "xno" && test "x${have_openssl}" = "xyes" && test "x${have_libevent_openssl}" = "xyes"; then enable_examples=yes fi if test "x${request_examples}" = "xyes" && test "x${enable_examples}" != "xyes"; then as_fn_error $? "examples were requested (--enable-examples) but dependencies are not met." "$LINENO" 5 fi if test "x${enable_examples}" = "xyes" ; then ENABLE_EXAMPLES_TRUE= ENABLE_EXAMPLES_FALSE='#' else ENABLE_EXAMPLES_TRUE='#' ENABLE_EXAMPLES_FALSE= fi # third-party only be built when needed enable_third_party=no have_mruby=no have_neverbleed=no if test "x${enable_examples}" = "xyes" || test "x${enable_app}" = "xyes" || test "x${enable_hpack_tools}" = "xyes"; then enable_third_party=yes # mruby (for src/nghttpx) if test "x${request_mruby}" = "xyes"; then # We are going to build mruby have_mruby=yes printf "%s\n" "#define HAVE_MRUBY 1" >>confdefs.h LIBMRUBY_LIBS="-lmruby -lm" LIBMRUBY_CFLAGS= fi # neverbleed (for src/nghttpx) if test "x${request_neverbleed}" = "xyes"; then have_neverbleed=yes printf "%s\n" "#define HAVE_NEVERBLEED 1" >>confdefs.h fi fi if test "x${enable_third_party}" = "xyes" ; then ENABLE_THIRD_PARTY_TRUE= ENABLE_THIRD_PARTY_FALSE='#' else ENABLE_THIRD_PARTY_TRUE='#' ENABLE_THIRD_PARTY_FALSE= fi if test "x${have_mruby}" = "xyes"; then HAVE_MRUBY_TRUE= HAVE_MRUBY_FALSE='#' else HAVE_MRUBY_TRUE='#' HAVE_MRUBY_FALSE= fi if test "x${have_neverbleed}" = "xyes"; then HAVE_NEVERBLEED_TRUE= HAVE_NEVERBLEED_FALSE='#' else HAVE_NEVERBLEED_TRUE='#' HAVE_NEVERBLEED_FALSE= fi # failmalloc tests enable_failmalloc=no if test "x${request_failmalloc}" = "xyes"; then enable_failmalloc=yes fi if test "x${enable_failmalloc}" = "xyes" ; then ENABLE_FAILMALLOC_TRUE= ENABLE_FAILMALLOC_FALSE='#' else ENABLE_FAILMALLOC_TRUE='#' ENABLE_FAILMALLOC_FALSE= fi # Checks for header files. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to enable assertions" >&5 printf %s "checking whether to enable assertions... " >&6; } # Check whether --enable-assert was given. if test ${enable_assert+y} then : enableval=$enable_assert; ac_enable_assert=$enableval if test "x$enableval" = xno then : printf "%s\n" "#define NDEBUG 1" >>confdefs.h elif test "x$enableval" != xyes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: invalid argument supplied to --enable-assert" >&5 printf "%s\n" "$as_me: WARNING: invalid argument supplied to --enable-assert" >&2;} ac_enable_assert=yes fi else $as_nop ac_enable_assert=yes fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_enable_assert" >&5 printf "%s\n" "$ac_enable_assert" >&6; } ac_fn_c_check_header_compile "$LINENO" "arpa/inet.h" "ac_cv_header_arpa_inet_h" "$ac_includes_default" if test "x$ac_cv_header_arpa_inet_h" = xyes then : printf "%s\n" "#define HAVE_ARPA_INET_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "fcntl.h" "ac_cv_header_fcntl_h" "$ac_includes_default" if test "x$ac_cv_header_fcntl_h" = xyes then : printf "%s\n" "#define HAVE_FCNTL_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "inttypes.h" "ac_cv_header_inttypes_h" "$ac_includes_default" if test "x$ac_cv_header_inttypes_h" = xyes then : printf "%s\n" "#define HAVE_INTTYPES_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "limits.h" "ac_cv_header_limits_h" "$ac_includes_default" if test "x$ac_cv_header_limits_h" = xyes then : printf "%s\n" "#define HAVE_LIMITS_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "netdb.h" "ac_cv_header_netdb_h" "$ac_includes_default" if test "x$ac_cv_header_netdb_h" = xyes then : printf "%s\n" "#define HAVE_NETDB_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "netinet/in.h" "ac_cv_header_netinet_in_h" "$ac_includes_default" if test "x$ac_cv_header_netinet_in_h" = xyes then : printf "%s\n" "#define HAVE_NETINET_IN_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "netinet/ip.h" "ac_cv_header_netinet_ip_h" "$ac_includes_default" if test "x$ac_cv_header_netinet_ip_h" = xyes then : printf "%s\n" "#define HAVE_NETINET_IP_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "pwd.h" "ac_cv_header_pwd_h" "$ac_includes_default" if test "x$ac_cv_header_pwd_h" = xyes then : printf "%s\n" "#define HAVE_PWD_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "stddef.h" "ac_cv_header_stddef_h" "$ac_includes_default" if test "x$ac_cv_header_stddef_h" = xyes then : printf "%s\n" "#define HAVE_STDDEF_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "stdint.h" "ac_cv_header_stdint_h" "$ac_includes_default" if test "x$ac_cv_header_stdint_h" = xyes then : printf "%s\n" "#define HAVE_STDINT_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default" if test "x$ac_cv_header_stdlib_h" = xyes then : printf "%s\n" "#define HAVE_STDLIB_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "string.h" "ac_cv_header_string_h" "$ac_includes_default" if test "x$ac_cv_header_string_h" = xyes then : printf "%s\n" "#define HAVE_STRING_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/socket.h" "ac_cv_header_sys_socket_h" "$ac_includes_default" if test "x$ac_cv_header_sys_socket_h" = xyes then : printf "%s\n" "#define HAVE_SYS_SOCKET_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/time.h" "ac_cv_header_sys_time_h" "$ac_includes_default" if test "x$ac_cv_header_sys_time_h" = xyes then : printf "%s\n" "#define HAVE_SYS_TIME_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "syslog.h" "ac_cv_header_syslog_h" "$ac_includes_default" if test "x$ac_cv_header_syslog_h" = xyes then : printf "%s\n" "#define HAVE_SYSLOG_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "unistd.h" "ac_cv_header_unistd_h" "$ac_includes_default" if test "x$ac_cv_header_unistd_h" = xyes then : printf "%s\n" "#define HAVE_UNISTD_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "windows.h" "ac_cv_header_windows_h" "$ac_includes_default" if test "x$ac_cv_header_windows_h" = xyes then : printf "%s\n" "#define HAVE_WINDOWS_H 1" >>confdefs.h fi # Checks for typedefs, structures, and compiler characteristics. ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" if test "x$ac_cv_type_size_t" = xyes then : else $as_nop printf "%s\n" "#define size_t unsigned int" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "ssize_t" "ac_cv_type_ssize_t" "$ac_includes_default" if test "x$ac_cv_type_ssize_t" = xyes then : else $as_nop printf "%s\n" "#define ssize_t int" >>confdefs.h fi ac_fn_c_find_uintX_t "$LINENO" "8" "ac_cv_c_uint8_t" case $ac_cv_c_uint8_t in #( no|yes) ;; #( *) printf "%s\n" "#define _UINT8_T 1" >>confdefs.h printf "%s\n" "#define uint8_t $ac_cv_c_uint8_t" >>confdefs.h ;; esac ac_fn_c_find_uintX_t "$LINENO" "16" "ac_cv_c_uint16_t" case $ac_cv_c_uint16_t in #( no|yes) ;; #( *) printf "%s\n" "#define uint16_t $ac_cv_c_uint16_t" >>confdefs.h ;; esac ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t" case $ac_cv_c_uint32_t in #( no|yes) ;; #( *) printf "%s\n" "#define _UINT32_T 1" >>confdefs.h printf "%s\n" "#define uint32_t $ac_cv_c_uint32_t" >>confdefs.h ;; esac ac_fn_c_find_uintX_t "$LINENO" "64" "ac_cv_c_uint64_t" case $ac_cv_c_uint64_t in #( no|yes) ;; #( *) printf "%s\n" "#define _UINT64_T 1" >>confdefs.h printf "%s\n" "#define uint64_t $ac_cv_c_uint64_t" >>confdefs.h ;; esac ac_fn_c_find_intX_t "$LINENO" "8" "ac_cv_c_int8_t" case $ac_cv_c_int8_t in #( no|yes) ;; #( *) printf "%s\n" "#define int8_t $ac_cv_c_int8_t" >>confdefs.h ;; esac ac_fn_c_find_intX_t "$LINENO" "16" "ac_cv_c_int16_t" case $ac_cv_c_int16_t in #( no|yes) ;; #( *) printf "%s\n" "#define int16_t $ac_cv_c_int16_t" >>confdefs.h ;; esac ac_fn_c_find_intX_t "$LINENO" "32" "ac_cv_c_int32_t" case $ac_cv_c_int32_t in #( no|yes) ;; #( *) printf "%s\n" "#define int32_t $ac_cv_c_int32_t" >>confdefs.h ;; esac ac_fn_c_find_intX_t "$LINENO" "64" "ac_cv_c_int64_t" case $ac_cv_c_int64_t in #( no|yes) ;; #( *) printf "%s\n" "#define int64_t $ac_cv_c_int64_t" >>confdefs.h ;; esac ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default" if test "x$ac_cv_type_off_t" = xyes then : else $as_nop printf "%s\n" "#define off_t long int" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default " if test "x$ac_cv_type_pid_t" = xyes then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if defined _WIN64 && !defined __CYGWIN__ LLP64 #endif int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_pid_type='int' else $as_nop ac_pid_type='__int64' fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext printf "%s\n" "#define pid_t $ac_pid_type" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5 printf %s "checking for uid_t in sys/types.h... " >&6; } if test ${ac_cv_type_uid_t+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "uid_t" >/dev/null 2>&1 then : ac_cv_type_uid_t=yes else $as_nop ac_cv_type_uid_t=no fi rm -rf conftest* fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uid_t" >&5 printf "%s\n" "$ac_cv_type_uid_t" >&6; } if test $ac_cv_type_uid_t = no; then printf "%s\n" "#define uid_t int" >>confdefs.h printf "%s\n" "#define gid_t int" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "ptrdiff_t" "ac_cv_type_ptrdiff_t" "$ac_includes_default" if test "x$ac_cv_type_ptrdiff_t" = xyes then : printf "%s\n" "#define HAVE_PTRDIFF_T 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 printf %s "checking whether byte ordering is bigendian... " >&6; } if test ${ac_cv_c_bigendian+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_c_bigendian=unknown # See if we're dealing with a universal compiler. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __APPLE_CC__ not a universal capable compiler #endif typedef int dummy; _ACEOF if ac_fn_c_try_compile "$LINENO" then : # Check for potential -arch flags. It is not universal unless # there are at least two -arch flags with different values. ac_arch= ac_prev= for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do if test -n "$ac_prev"; then case $ac_word in i?86 | x86_64 | ppc | ppc64) if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then ac_arch=$ac_word else ac_cv_c_bigendian=universal break fi ;; esac ac_prev= elif test "x$ac_word" = "x-arch"; then ac_prev=arch fi done fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if test $ac_cv_c_bigendian = unknown; then # See if sys/param.h defines the BYTE_ORDER macro. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { #if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ && LITTLE_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : # It does; now see whether it defined to BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_bigendian=yes else $as_nop ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { #if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : # It does; now see whether it defined to _BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { #ifndef _BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_bigendian=yes else $as_nop ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # Compile a test program. if test "$cross_compiling" = yes then : # Try to guess by grepping values from an object file. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ unsigned short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; unsigned short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; int use_ascii (int i) { return ascii_mm[i] + ascii_ii[i]; } unsigned short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; unsigned short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } extern int foo; int main (void) { return use_ascii (foo) == use_ebcdic (foo); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then ac_cv_c_bigendian=yes fi if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else # finding both strings is unlikely to happen, but who knows? ac_cv_c_bigendian=unknown fi fi fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main (void) { /* Are we little or big endian? From Harbison&Steele. */ union { long int l; char c[sizeof (long int)]; } u; u.l = 1; return u.c[sizeof (long int) - 1] == 1; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_c_bigendian=no else $as_nop ac_cv_c_bigendian=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 printf "%s\n" "$ac_cv_c_bigendian" >&6; } case $ac_cv_c_bigendian in #( yes) printf "%s\n" "#define WORDS_BIGENDIAN 1" >>confdefs.h ;; #( no) ;; #( universal) printf "%s\n" "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h ;; #( *) as_fn_error $? "unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 printf %s "checking for inline... " >&6; } if test ${ac_cv_c_inline+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_c_inline=no for ac_kw in inline __inline__ __inline; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __cplusplus typedef int foo_t; static $ac_kw foo_t static_foo (void) {return 0; } $ac_kw foo_t foo (void) {return 0; } #endif _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_inline=$ac_kw fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext test "$ac_cv_c_inline" != no && break done fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 printf "%s\n" "$ac_cv_c_inline" >&6; } case $ac_cv_c_inline in inline | yes) ;; *) case $ac_cv_c_inline in no) ac_val=;; *) ac_val=$ac_cv_c_inline;; esac cat >>confdefs.h <<_ACEOF #ifndef __cplusplus #define inline $ac_val #endif _ACEOF ;; esac # Check whether --enable-largefile was given. if test ${enable_largefile+y} then : enableval=$enable_largefile; fi if test "$enable_largefile" != no; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 printf %s "checking for special C compiler options needed for large files... " >&6; } if test ${ac_cv_sys_largefile_CC+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_sys_largefile_CC=no if test "$GCC" != yes; then ac_save_CC=$CC while :; do # IRIX 6.2 and later do not support large files by default, # so use the C compiler's -n32 option if that helps. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : break fi rm -f core conftest.err conftest.$ac_objext conftest.beam CC="$CC -n32" if ac_fn_c_try_compile "$LINENO" then : ac_cv_sys_largefile_CC=' -n32'; break fi rm -f core conftest.err conftest.$ac_objext conftest.beam break done CC=$ac_save_CC rm -f conftest.$ac_ext fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 printf "%s\n" "$ac_cv_sys_largefile_CC" >&6; } if test "$ac_cv_sys_largefile_CC" != no; then CC=$CC$ac_cv_sys_largefile_CC fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 printf %s "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } if test ${ac_cv_sys_file_offset_bits+y} then : printf %s "(cached) " >&6 else $as_nop while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_sys_file_offset_bits=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _FILE_OFFSET_BITS 64 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_sys_file_offset_bits=64; break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_sys_file_offset_bits=unknown break done fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 printf "%s\n" "$ac_cv_sys_file_offset_bits" >&6; } case $ac_cv_sys_file_offset_bits in #( no | unknown) ;; *) printf "%s\n" "#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits" >>confdefs.h ;; esac rm -rf conftest* if test $ac_cv_sys_file_offset_bits = unknown; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 printf %s "checking for _LARGE_FILES value needed for large files... " >&6; } if test ${ac_cv_sys_large_files+y} then : printf %s "(cached) " >&6 else $as_nop while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_sys_large_files=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _LARGE_FILES 1 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_sys_large_files=1; break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_sys_large_files=unknown break done fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 printf "%s\n" "$ac_cv_sys_large_files" >&6; } case $ac_cv_sys_large_files in #( no | unknown) ;; *) printf "%s\n" "#define _LARGE_FILES $ac_cv_sys_large_files" >>confdefs.h ;; esac rm -rf conftest* fi fi ac_fn_c_check_member "$LINENO" "struct tm" "tm_gmtoff" "ac_cv_member_struct_tm_tm_gmtoff" "#include " if test "x$ac_cv_member_struct_tm_tm_gmtoff" = xyes then : have_struct_tm_tm_gmtoff=yes else $as_nop have_struct_tm_tm_gmtoff=no fi ac_fn_c_check_member "$LINENO" "struct sockaddr_in" "sin_len" "ac_cv_member_struct_sockaddr_in_sin_len" " #include #include #include " if test "x$ac_cv_member_struct_sockaddr_in_sin_len" = xyes then : printf "%s\n" "#define HAVE_SOCKADDR_IN_SIN_LEN 1" >>confdefs.h fi ac_fn_c_check_member "$LINENO" "struct sockaddr_in6" "sin6_len" "ac_cv_member_struct_sockaddr_in6_sin6_len" " #include #include #include " if test "x$ac_cv_member_struct_sockaddr_in6_sin6_len" = xyes then : printf "%s\n" "#define HAVE_SOCKADDR_IN6_SIN6_LEN 1" >>confdefs.h fi if test "x$have_struct_tm_tm_gmtoff" = "xyes"; then printf "%s\n" "#define HAVE_STRUCT_TM_TM_GMTOFF 1" >>confdefs.h fi # Checks for library functions. # Don't check malloc, since it does not play nicely with C++ stdlib # AC_FUNC_MALLOC { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for working chown" >&5 printf %s "checking for working chown... " >&6; } if test ${ac_cv_func_chown_works+y} then : printf %s "(cached) " >&6 else $as_nop if test "$cross_compiling" = yes then : case "$host_os" in # (( # Guess yes on glibc systems. *-gnu*) ac_cv_func_chown_works=yes ;; # If we don't know, assume the worst. *) ac_cv_func_chown_works=no ;; esac else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default #include int main (void) { char *f = "conftest.chown"; struct stat before, after; if (creat (f, 0600) < 0) return 1; if (stat (f, &before) < 0) return 1; if (chown (f, (uid_t) -1, (gid_t) -1) == -1) return 1; if (stat (f, &after) < 0) return 1; return ! (before.st_uid == after.st_uid && before.st_gid == after.st_gid); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_func_chown_works=yes else $as_nop ac_cv_func_chown_works=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f conftest.chown fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_chown_works" >&5 printf "%s\n" "$ac_cv_func_chown_works" >&6; } if test $ac_cv_func_chown_works = yes; then printf "%s\n" "#define HAVE_CHOWN 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for error_at_line" >&5 printf %s "checking for error_at_line... " >&6; } if test ${ac_cv_lib_error_at_line+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { error_at_line (0, 0, "", 0, "an error occurred"); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_error_at_line=yes else $as_nop ac_cv_lib_error_at_line=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_error_at_line" >&5 printf "%s\n" "$ac_cv_lib_error_at_line" >&6; } if test $ac_cv_lib_error_at_line = no; then case " $LIBOBJS " in *" error.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS error.$ac_objext" ;; esac fi ac_func= for ac_item in $ac_func_c_list do if test $ac_func; then ac_fn_c_check_func "$LINENO" $ac_func ac_cv_func_$ac_func if eval test \"x\$ac_cv_func_$ac_func\" = xyes; then echo "#define $ac_item 1" >> confdefs.h fi ac_func= else ac_func=$ac_item fi done if test "x$ac_cv_func_fork" = xyes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5 printf %s "checking for working fork... " >&6; } if test ${ac_cv_func_fork_works+y} then : printf %s "(cached) " >&6 else $as_nop if test "$cross_compiling" = yes then : ac_cv_func_fork_works=cross else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main (void) { /* By Ruediger Kuhlmann. */ return fork () < 0; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_func_fork_works=yes else $as_nop ac_cv_func_fork_works=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fork_works" >&5 printf "%s\n" "$ac_cv_func_fork_works" >&6; } else ac_cv_func_fork_works=$ac_cv_func_fork fi if test "x$ac_cv_func_fork_works" = xcross; then case $host in *-*-amigaos* | *-*-msdosdjgpp*) # Override, as these systems have only a dummy fork() stub ac_cv_func_fork_works=no ;; *) ac_cv_func_fork_works=yes ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 printf "%s\n" "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} fi ac_cv_func_vfork_works=$ac_cv_func_vfork if test "x$ac_cv_func_vfork" = xyes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5 printf %s "checking for working vfork... " >&6; } if test ${ac_cv_func_vfork_works+y} then : printf %s "(cached) " >&6 else $as_nop if test "$cross_compiling" = yes then : ac_cv_func_vfork_works=cross else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Thanks to Paul Eggert for this test. */ $ac_includes_default #include #include #ifdef HAVE_VFORK_H # include #endif static void do_nothing (int sig) { (void) sig; } /* On some sparc systems, changes by the child to local and incoming argument registers are propagated back to the parent. The compiler is told about this with #include , but some compilers (e.g. gcc -O) don't grok . Test for this by using a static variable whose address is put into a register that is clobbered by the vfork. */ static void sparc_address_test (int arg) { static pid_t child; if (!child) { child = vfork (); if (child < 0) { perror ("vfork"); _exit(2); } if (!child) { arg = getpid(); write(-1, "", 0); _exit (arg); } } } int main (void) { pid_t parent = getpid (); pid_t child; sparc_address_test (0); /* On Solaris 2.4, changes by the child to the signal handler also munge signal handlers in the parent. To detect this, start by putting the parent's handler in a known state. */ signal (SIGTERM, SIG_DFL); child = vfork (); if (child == 0) { /* Here is another test for sparc vfork register problems. This test uses lots of local variables, at least as many local variables as main has allocated so far including compiler temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should reuse the register of parent for one of the local variables, since it will think that parent can't possibly be used any more in this routine. Assigning to the local variable will thus munge parent in the parent process. */ pid_t p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); /* Convince the compiler that p..p7 are live; otherwise, it might use the same hardware register for all 8 local variables. */ if (p != p1 || p != p2 || p != p3 || p != p4 || p != p5 || p != p6 || p != p7) _exit(1); /* Alter the child's signal handler. */ if (signal (SIGTERM, do_nothing) != SIG_DFL) _exit(1); /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent from child file descriptors. If the child closes a descriptor before it execs or exits, this munges the parent's descriptor as well. Test for this by closing stdout in the child. */ _exit(close(fileno(stdout)) != 0); } else { int status; struct stat st; while (wait(&status) != child) ; return ( /* Was there some problem with vforking? */ child < 0 /* Did the child munge the parent's signal handler? */ || signal (SIGTERM, SIG_DFL) != SIG_DFL /* Did the child fail? (This shouldn't happen.) */ || status /* Did the vfork/compiler bug occur? */ || parent != getpid() /* Did the file descriptor bug occur? */ || fstat(fileno(stdout), &st) != 0 ); } } _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_func_vfork_works=yes else $as_nop ac_cv_func_vfork_works=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_vfork_works" >&5 printf "%s\n" "$ac_cv_func_vfork_works" >&6; } fi; if test "x$ac_cv_func_fork_works" = xcross; then ac_cv_func_vfork_works=$ac_cv_func_vfork { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 printf "%s\n" "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} fi if test "x$ac_cv_func_vfork_works" = xyes; then printf "%s\n" "#define HAVE_WORKING_VFORK 1" >>confdefs.h else printf "%s\n" "#define vfork fork" >>confdefs.h fi if test "x$ac_cv_func_fork_works" = xyes; then printf "%s\n" "#define HAVE_WORKING_FORK 1" >>confdefs.h fi # Don't check realloc, since LeakSanitizer detects memory leak during check # AC_FUNC_REALLOC { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5 printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; } if test ${ac_cv_c_undeclared_builtin_options+y} then : printf %s "(cached) " >&6 else $as_nop ac_save_CFLAGS=$CFLAGS ac_cv_c_undeclared_builtin_options='cannot detect' for ac_arg in '' -fno-builtin; do CFLAGS="$ac_save_CFLAGS $ac_arg" # This test program should *not* compile successfully. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { (void) strchr; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop # This test program should compile successfully. # No library function is consistently available on # freestanding implementations, so test against a dummy # declaration. Include always-available headers on the # off chance that they somehow elicit warnings. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include extern void ac_decl (int, char *); int main (void) { (void) ac_decl (0, (char *) 0); (void) ac_decl; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if test x"$ac_arg" = x then : ac_cv_c_undeclared_builtin_options='none needed' else $as_nop ac_cv_c_undeclared_builtin_options=$ac_arg fi break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done CFLAGS=$ac_save_CFLAGS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_undeclared_builtin_options" >&5 printf "%s\n" "$ac_cv_c_undeclared_builtin_options" >&6; } case $ac_cv_c_undeclared_builtin_options in #( 'cannot detect') : { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot make $CC report undeclared builtins See \`config.log' for more details" "$LINENO" 5; } ;; #( 'none needed') : ac_c_undeclared_builtin_options='' ;; #( *) : ac_c_undeclared_builtin_options=$ac_cv_c_undeclared_builtin_options ;; esac ac_fn_check_decl "$LINENO" "strerror_r" "ac_cv_have_decl_strerror_r" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_strerror_r" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_STRERROR_R $ac_have_decl" >>confdefs.h if test $ac_cv_have_decl_strerror_r = yes; then # For backward compatibility's sake, define HAVE_STRERROR_R. # (We used to run AC_CHECK_FUNCS_ONCE for strerror_r, as well # as AC_CHECK_DECLS_ONCE.) printf "%s\n" "#define HAVE_STRERROR_R 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether strerror_r returns char *" >&5 printf %s "checking whether strerror_r returns char *... " >&6; } if test ${ac_cv_func_strerror_r_char_p+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_func_strerror_r_char_p=no if test $ac_cv_have_decl_strerror_r = yes; then cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { char buf[100]; char x = *strerror_r (0, buf, sizeof buf); char *p = strerror_r (0, buf, sizeof buf); return !p || x; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_strerror_r_char_p=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_strerror_r_char_p" >&5 printf "%s\n" "$ac_cv_func_strerror_r_char_p" >&6; } if test $ac_cv_func_strerror_r_char_p = yes; then printf "%s\n" "#define STRERROR_R_CHAR_P 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for working strnlen" >&5 printf %s "checking for working strnlen... " >&6; } if test ${ac_cv_func_strnlen_working+y} then : printf %s "(cached) " >&6 else $as_nop if test "$cross_compiling" = yes then : # Guess no on AIX systems, yes otherwise. case "$host_os" in aix*) ac_cv_func_strnlen_working=no;; *) ac_cv_func_strnlen_working=yes;; esac else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main (void) { #define S "foobar" #define S_LEN (sizeof S - 1) /* At least one implementation is buggy: that of AIX 4.3 would give strnlen (S, 1) == 3. */ int i; for (i = 0; i < S_LEN + 1; ++i) { int expected = i <= S_LEN ? i : S_LEN; if (strnlen (S, i) != expected) return 1; } return 0; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_func_strnlen_working=yes else $as_nop ac_cv_func_strnlen_working=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_strnlen_working" >&5 printf "%s\n" "$ac_cv_func_strnlen_working" >&6; } test $ac_cv_func_strnlen_working = no && case " $LIBOBJS " in *" strnlen.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS strnlen.$ac_objext" ;; esac ac_fn_c_check_func "$LINENO" "_Exit" "ac_cv_func__Exit" if test "x$ac_cv_func__Exit" = xyes then : printf "%s\n" "#define HAVE__EXIT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "accept4" "ac_cv_func_accept4" if test "x$ac_cv_func_accept4" = xyes then : printf "%s\n" "#define HAVE_ACCEPT4 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "clock_gettime" "ac_cv_func_clock_gettime" if test "x$ac_cv_func_clock_gettime" = xyes then : printf "%s\n" "#define HAVE_CLOCK_GETTIME 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "dup2" "ac_cv_func_dup2" if test "x$ac_cv_func_dup2" = xyes then : printf "%s\n" "#define HAVE_DUP2 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getcwd" "ac_cv_func_getcwd" if test "x$ac_cv_func_getcwd" = xyes then : printf "%s\n" "#define HAVE_GETCWD 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getpwnam" "ac_cv_func_getpwnam" if test "x$ac_cv_func_getpwnam" = xyes then : printf "%s\n" "#define HAVE_GETPWNAM 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "localtime_r" "ac_cv_func_localtime_r" if test "x$ac_cv_func_localtime_r" = xyes then : printf "%s\n" "#define HAVE_LOCALTIME_R 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "memchr" "ac_cv_func_memchr" if test "x$ac_cv_func_memchr" = xyes then : printf "%s\n" "#define HAVE_MEMCHR 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "memmove" "ac_cv_func_memmove" if test "x$ac_cv_func_memmove" = xyes then : printf "%s\n" "#define HAVE_MEMMOVE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "memset" "ac_cv_func_memset" if test "x$ac_cv_func_memset" = xyes then : printf "%s\n" "#define HAVE_MEMSET 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "mkostemp" "ac_cv_func_mkostemp" if test "x$ac_cv_func_mkostemp" = xyes then : printf "%s\n" "#define HAVE_MKOSTEMP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "pipe2" "ac_cv_func_pipe2" if test "x$ac_cv_func_pipe2" = xyes then : printf "%s\n" "#define HAVE_PIPE2 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "socket" "ac_cv_func_socket" if test "x$ac_cv_func_socket" = xyes then : printf "%s\n" "#define HAVE_SOCKET 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "sqrt" "ac_cv_func_sqrt" if test "x$ac_cv_func_sqrt" = xyes then : printf "%s\n" "#define HAVE_SQRT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strchr" "ac_cv_func_strchr" if test "x$ac_cv_func_strchr" = xyes then : printf "%s\n" "#define HAVE_STRCHR 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strdup" "ac_cv_func_strdup" if test "x$ac_cv_func_strdup" = xyes then : printf "%s\n" "#define HAVE_STRDUP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strerror" "ac_cv_func_strerror" if test "x$ac_cv_func_strerror" = xyes then : printf "%s\n" "#define HAVE_STRERROR 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strndup" "ac_cv_func_strndup" if test "x$ac_cv_func_strndup" = xyes then : printf "%s\n" "#define HAVE_STRNDUP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strstr" "ac_cv_func_strstr" if test "x$ac_cv_func_strstr" = xyes then : printf "%s\n" "#define HAVE_STRSTR 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strtol" "ac_cv_func_strtol" if test "x$ac_cv_func_strtol" = xyes then : printf "%s\n" "#define HAVE_STRTOL 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strtoul" "ac_cv_func_strtoul" if test "x$ac_cv_func_strtoul" = xyes then : printf "%s\n" "#define HAVE_STRTOUL 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "timegm" "ac_cv_func_timegm" if test "x$ac_cv_func_timegm" = xyes then : printf "%s\n" "#define HAVE_TIMEGM 1" >>confdefs.h fi # timerfd_create was added in linux kernel 2.6.25 ac_fn_c_check_func "$LINENO" "timerfd_create" "ac_cv_func_timerfd_create" if test "x$ac_cv_func_timerfd_create" = xyes then : have_timerfd_create=yes else $as_nop have_timerfd_create=no fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking checking for GetTickCount64" >&5 printf %s "checking checking for GetTickCount64... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { GetTickCount64(); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : have_gettickcount64=yes else $as_nop have_gettickcount64=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext if test "x${have_gettickcount64}" = "xyes"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define HAVE_GETTICKCOUNT64 1" >>confdefs.h else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # For cygwin: we can link initgroups, so AC_CHECK_FUNCS succeeds, but # cygwin disables initgroups due to feature test macro magic with our # configuration. FreeBSD declares initgroups() in unistd.h. ac_fn_check_decl "$LINENO" "initgroups" "ac_cv_have_decl_initgroups" " #ifdef HAVE_UNISTD_H # include #endif #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_initgroups" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_INITGROUPS $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "CLOCK_MONOTONIC" "ac_cv_have_decl_CLOCK_MONOTONIC" " #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_CLOCK_MONOTONIC" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_CLOCK_MONOTONIC $ac_have_decl" >>confdefs.h save_CFLAGS=$CFLAGS save_CXXFLAGS=$CXXFLAGS CFLAGS= CXXFLAGS= if test "x$werror" != "xno"; then # For C compiler { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wall" >&5 printf %s "checking whether C compiler accepts -Wall... " >&6; } if test ${ax_cv_check_cflags___Wall+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wall" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wall=yes else $as_nop ax_cv_check_cflags___Wall=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wall" >&5 printf "%s\n" "$ax_cv_check_cflags___Wall" >&6; } if test "x$ax_cv_check_cflags___Wall" = xyes then : CFLAGS="$CFLAGS -Wall" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wextra" >&5 printf %s "checking whether C compiler accepts -Wextra... " >&6; } if test ${ax_cv_check_cflags___Wextra+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wextra" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wextra=yes else $as_nop ax_cv_check_cflags___Wextra=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wextra" >&5 printf "%s\n" "$ax_cv_check_cflags___Wextra" >&6; } if test "x$ax_cv_check_cflags___Wextra" = xyes then : CFLAGS="$CFLAGS -Wextra" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Werror" >&5 printf %s "checking whether C compiler accepts -Werror... " >&6; } if test ${ax_cv_check_cflags___Werror+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Werror=yes else $as_nop ax_cv_check_cflags___Werror=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Werror" >&5 printf "%s\n" "$ax_cv_check_cflags___Werror" >&6; } if test "x$ax_cv_check_cflags___Werror" = xyes then : CFLAGS="$CFLAGS -Werror" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wmissing-prototypes" >&5 printf %s "checking whether C compiler accepts -Wmissing-prototypes... " >&6; } if test ${ax_cv_check_cflags___Wmissing_prototypes+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wmissing-prototypes" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wmissing_prototypes=yes else $as_nop ax_cv_check_cflags___Wmissing_prototypes=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wmissing_prototypes" >&5 printf "%s\n" "$ax_cv_check_cflags___Wmissing_prototypes" >&6; } if test "x$ax_cv_check_cflags___Wmissing_prototypes" = xyes then : CFLAGS="$CFLAGS -Wmissing-prototypes" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wstrict-prototypes" >&5 printf %s "checking whether C compiler accepts -Wstrict-prototypes... " >&6; } if test ${ax_cv_check_cflags___Wstrict_prototypes+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wstrict-prototypes" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wstrict_prototypes=yes else $as_nop ax_cv_check_cflags___Wstrict_prototypes=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wstrict_prototypes" >&5 printf "%s\n" "$ax_cv_check_cflags___Wstrict_prototypes" >&6; } if test "x$ax_cv_check_cflags___Wstrict_prototypes" = xyes then : CFLAGS="$CFLAGS -Wstrict-prototypes" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wmissing-declarations" >&5 printf %s "checking whether C compiler accepts -Wmissing-declarations... " >&6; } if test ${ax_cv_check_cflags___Wmissing_declarations+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wmissing-declarations" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wmissing_declarations=yes else $as_nop ax_cv_check_cflags___Wmissing_declarations=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wmissing_declarations" >&5 printf "%s\n" "$ax_cv_check_cflags___Wmissing_declarations" >&6; } if test "x$ax_cv_check_cflags___Wmissing_declarations" = xyes then : CFLAGS="$CFLAGS -Wmissing-declarations" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wpointer-arith" >&5 printf %s "checking whether C compiler accepts -Wpointer-arith... " >&6; } if test ${ax_cv_check_cflags___Wpointer_arith+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wpointer-arith" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wpointer_arith=yes else $as_nop ax_cv_check_cflags___Wpointer_arith=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wpointer_arith" >&5 printf "%s\n" "$ax_cv_check_cflags___Wpointer_arith" >&6; } if test "x$ax_cv_check_cflags___Wpointer_arith" = xyes then : CFLAGS="$CFLAGS -Wpointer-arith" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wdeclaration-after-statement" >&5 printf %s "checking whether C compiler accepts -Wdeclaration-after-statement... " >&6; } if test ${ax_cv_check_cflags___Wdeclaration_after_statement+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wdeclaration-after-statement" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wdeclaration_after_statement=yes else $as_nop ax_cv_check_cflags___Wdeclaration_after_statement=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wdeclaration_after_statement" >&5 printf "%s\n" "$ax_cv_check_cflags___Wdeclaration_after_statement" >&6; } if test "x$ax_cv_check_cflags___Wdeclaration_after_statement" = xyes then : CFLAGS="$CFLAGS -Wdeclaration-after-statement" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wformat-security" >&5 printf %s "checking whether C compiler accepts -Wformat-security... " >&6; } if test ${ax_cv_check_cflags___Wformat_security+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wformat-security" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wformat_security=yes else $as_nop ax_cv_check_cflags___Wformat_security=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wformat_security" >&5 printf "%s\n" "$ax_cv_check_cflags___Wformat_security" >&6; } if test "x$ax_cv_check_cflags___Wformat_security" = xyes then : CFLAGS="$CFLAGS -Wformat-security" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wwrite-strings" >&5 printf %s "checking whether C compiler accepts -Wwrite-strings... " >&6; } if test ${ax_cv_check_cflags___Wwrite_strings+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wwrite-strings" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wwrite_strings=yes else $as_nop ax_cv_check_cflags___Wwrite_strings=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wwrite_strings" >&5 printf "%s\n" "$ax_cv_check_cflags___Wwrite_strings" >&6; } if test "x$ax_cv_check_cflags___Wwrite_strings" = xyes then : CFLAGS="$CFLAGS -Wwrite-strings" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wshadow" >&5 printf %s "checking whether C compiler accepts -Wshadow... " >&6; } if test ${ax_cv_check_cflags___Wshadow+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wshadow" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wshadow=yes else $as_nop ax_cv_check_cflags___Wshadow=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wshadow" >&5 printf "%s\n" "$ax_cv_check_cflags___Wshadow" >&6; } if test "x$ax_cv_check_cflags___Wshadow" = xyes then : CFLAGS="$CFLAGS -Wshadow" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Winline" >&5 printf %s "checking whether C compiler accepts -Winline... " >&6; } if test ${ax_cv_check_cflags___Winline+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Winline" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Winline=yes else $as_nop ax_cv_check_cflags___Winline=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Winline" >&5 printf "%s\n" "$ax_cv_check_cflags___Winline" >&6; } if test "x$ax_cv_check_cflags___Winline" = xyes then : CFLAGS="$CFLAGS -Winline" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wnested-externs" >&5 printf %s "checking whether C compiler accepts -Wnested-externs... " >&6; } if test ${ax_cv_check_cflags___Wnested_externs+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wnested-externs" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wnested_externs=yes else $as_nop ax_cv_check_cflags___Wnested_externs=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wnested_externs" >&5 printf "%s\n" "$ax_cv_check_cflags___Wnested_externs" >&6; } if test "x$ax_cv_check_cflags___Wnested_externs" = xyes then : CFLAGS="$CFLAGS -Wnested-externs" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wfloat-equal" >&5 printf %s "checking whether C compiler accepts -Wfloat-equal... " >&6; } if test ${ax_cv_check_cflags___Wfloat_equal+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wfloat-equal" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wfloat_equal=yes else $as_nop ax_cv_check_cflags___Wfloat_equal=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wfloat_equal" >&5 printf "%s\n" "$ax_cv_check_cflags___Wfloat_equal" >&6; } if test "x$ax_cv_check_cflags___Wfloat_equal" = xyes then : CFLAGS="$CFLAGS -Wfloat-equal" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wundef" >&5 printf %s "checking whether C compiler accepts -Wundef... " >&6; } if test ${ax_cv_check_cflags___Wundef+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wundef" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wundef=yes else $as_nop ax_cv_check_cflags___Wundef=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wundef" >&5 printf "%s\n" "$ax_cv_check_cflags___Wundef" >&6; } if test "x$ax_cv_check_cflags___Wundef" = xyes then : CFLAGS="$CFLAGS -Wundef" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wendif-labels" >&5 printf %s "checking whether C compiler accepts -Wendif-labels... " >&6; } if test ${ax_cv_check_cflags___Wendif_labels+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wendif-labels" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wendif_labels=yes else $as_nop ax_cv_check_cflags___Wendif_labels=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wendif_labels" >&5 printf "%s\n" "$ax_cv_check_cflags___Wendif_labels" >&6; } if test "x$ax_cv_check_cflags___Wendif_labels" = xyes then : CFLAGS="$CFLAGS -Wendif-labels" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wempty-body" >&5 printf %s "checking whether C compiler accepts -Wempty-body... " >&6; } if test ${ax_cv_check_cflags___Wempty_body+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wempty-body" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wempty_body=yes else $as_nop ax_cv_check_cflags___Wempty_body=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wempty_body" >&5 printf "%s\n" "$ax_cv_check_cflags___Wempty_body" >&6; } if test "x$ax_cv_check_cflags___Wempty_body" = xyes then : CFLAGS="$CFLAGS -Wempty-body" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wcast-align" >&5 printf %s "checking whether C compiler accepts -Wcast-align... " >&6; } if test ${ax_cv_check_cflags___Wcast_align+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wcast-align" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wcast_align=yes else $as_nop ax_cv_check_cflags___Wcast_align=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wcast_align" >&5 printf "%s\n" "$ax_cv_check_cflags___Wcast_align" >&6; } if test "x$ax_cv_check_cflags___Wcast_align" = xyes then : CFLAGS="$CFLAGS -Wcast-align" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wclobbered" >&5 printf %s "checking whether C compiler accepts -Wclobbered... " >&6; } if test ${ax_cv_check_cflags___Wclobbered+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wclobbered" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wclobbered=yes else $as_nop ax_cv_check_cflags___Wclobbered=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wclobbered" >&5 printf "%s\n" "$ax_cv_check_cflags___Wclobbered" >&6; } if test "x$ax_cv_check_cflags___Wclobbered" = xyes then : CFLAGS="$CFLAGS -Wclobbered" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wvla" >&5 printf %s "checking whether C compiler accepts -Wvla... " >&6; } if test ${ax_cv_check_cflags___Wvla+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wvla" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wvla=yes else $as_nop ax_cv_check_cflags___Wvla=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wvla" >&5 printf "%s\n" "$ax_cv_check_cflags___Wvla" >&6; } if test "x$ax_cv_check_cflags___Wvla" = xyes then : CFLAGS="$CFLAGS -Wvla" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wpragmas" >&5 printf %s "checking whether C compiler accepts -Wpragmas... " >&6; } if test ${ax_cv_check_cflags___Wpragmas+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wpragmas" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wpragmas=yes else $as_nop ax_cv_check_cflags___Wpragmas=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wpragmas" >&5 printf "%s\n" "$ax_cv_check_cflags___Wpragmas" >&6; } if test "x$ax_cv_check_cflags___Wpragmas" = xyes then : CFLAGS="$CFLAGS -Wpragmas" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wunreachable-code" >&5 printf %s "checking whether C compiler accepts -Wunreachable-code... " >&6; } if test ${ax_cv_check_cflags___Wunreachable_code+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wunreachable-code" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wunreachable_code=yes else $as_nop ax_cv_check_cflags___Wunreachable_code=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wunreachable_code" >&5 printf "%s\n" "$ax_cv_check_cflags___Wunreachable_code" >&6; } if test "x$ax_cv_check_cflags___Wunreachable_code" = xyes then : CFLAGS="$CFLAGS -Wunreachable-code" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Waddress" >&5 printf %s "checking whether C compiler accepts -Waddress... " >&6; } if test ${ax_cv_check_cflags___Waddress+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Waddress" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Waddress=yes else $as_nop ax_cv_check_cflags___Waddress=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Waddress" >&5 printf "%s\n" "$ax_cv_check_cflags___Waddress" >&6; } if test "x$ax_cv_check_cflags___Waddress" = xyes then : CFLAGS="$CFLAGS -Waddress" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wattributes" >&5 printf %s "checking whether C compiler accepts -Wattributes... " >&6; } if test ${ax_cv_check_cflags___Wattributes+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wattributes" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wattributes=yes else $as_nop ax_cv_check_cflags___Wattributes=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wattributes" >&5 printf "%s\n" "$ax_cv_check_cflags___Wattributes" >&6; } if test "x$ax_cv_check_cflags___Wattributes" = xyes then : CFLAGS="$CFLAGS -Wattributes" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wdiv-by-zero" >&5 printf %s "checking whether C compiler accepts -Wdiv-by-zero... " >&6; } if test ${ax_cv_check_cflags___Wdiv_by_zero+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wdiv-by-zero" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wdiv_by_zero=yes else $as_nop ax_cv_check_cflags___Wdiv_by_zero=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wdiv_by_zero" >&5 printf "%s\n" "$ax_cv_check_cflags___Wdiv_by_zero" >&6; } if test "x$ax_cv_check_cflags___Wdiv_by_zero" = xyes then : CFLAGS="$CFLAGS -Wdiv-by-zero" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wshorten-64-to-32" >&5 printf %s "checking whether C compiler accepts -Wshorten-64-to-32... " >&6; } if test ${ax_cv_check_cflags___Wshorten_64_to_32+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wshorten-64-to-32" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wshorten_64_to_32=yes else $as_nop ax_cv_check_cflags___Wshorten_64_to_32=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wshorten_64_to_32" >&5 printf "%s\n" "$ax_cv_check_cflags___Wshorten_64_to_32" >&6; } if test "x$ax_cv_check_cflags___Wshorten_64_to_32" = xyes then : CFLAGS="$CFLAGS -Wshorten-64-to-32" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wconversion" >&5 printf %s "checking whether C compiler accepts -Wconversion... " >&6; } if test ${ax_cv_check_cflags___Wconversion+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wconversion" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wconversion=yes else $as_nop ax_cv_check_cflags___Wconversion=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wconversion" >&5 printf "%s\n" "$ax_cv_check_cflags___Wconversion" >&6; } if test "x$ax_cv_check_cflags___Wconversion" = xyes then : CFLAGS="$CFLAGS -Wconversion" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wextended-offsetof" >&5 printf %s "checking whether C compiler accepts -Wextended-offsetof... " >&6; } if test ${ax_cv_check_cflags___Wextended_offsetof+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wextended-offsetof" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wextended_offsetof=yes else $as_nop ax_cv_check_cflags___Wextended_offsetof=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wextended_offsetof" >&5 printf "%s\n" "$ax_cv_check_cflags___Wextended_offsetof" >&6; } if test "x$ax_cv_check_cflags___Wextended_offsetof" = xyes then : CFLAGS="$CFLAGS -Wextended-offsetof" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wformat-nonliteral" >&5 printf %s "checking whether C compiler accepts -Wformat-nonliteral... " >&6; } if test ${ax_cv_check_cflags___Wformat_nonliteral+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wformat-nonliteral" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wformat_nonliteral=yes else $as_nop ax_cv_check_cflags___Wformat_nonliteral=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wformat_nonliteral" >&5 printf "%s\n" "$ax_cv_check_cflags___Wformat_nonliteral" >&6; } if test "x$ax_cv_check_cflags___Wformat_nonliteral" = xyes then : CFLAGS="$CFLAGS -Wformat-nonliteral" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wlanguage-extension-token" >&5 printf %s "checking whether C compiler accepts -Wlanguage-extension-token... " >&6; } if test ${ax_cv_check_cflags___Wlanguage_extension_token+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wlanguage-extension-token" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wlanguage_extension_token=yes else $as_nop ax_cv_check_cflags___Wlanguage_extension_token=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wlanguage_extension_token" >&5 printf "%s\n" "$ax_cv_check_cflags___Wlanguage_extension_token" >&6; } if test "x$ax_cv_check_cflags___Wlanguage_extension_token" = xyes then : CFLAGS="$CFLAGS -Wlanguage-extension-token" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wmissing-field-initializers" >&5 printf %s "checking whether C compiler accepts -Wmissing-field-initializers... " >&6; } if test ${ax_cv_check_cflags___Wmissing_field_initializers+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wmissing-field-initializers" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wmissing_field_initializers=yes else $as_nop ax_cv_check_cflags___Wmissing_field_initializers=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wmissing_field_initializers" >&5 printf "%s\n" "$ax_cv_check_cflags___Wmissing_field_initializers" >&6; } if test "x$ax_cv_check_cflags___Wmissing_field_initializers" = xyes then : CFLAGS="$CFLAGS -Wmissing-field-initializers" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wmissing-noreturn" >&5 printf %s "checking whether C compiler accepts -Wmissing-noreturn... " >&6; } if test ${ax_cv_check_cflags___Wmissing_noreturn+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wmissing-noreturn" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wmissing_noreturn=yes else $as_nop ax_cv_check_cflags___Wmissing_noreturn=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wmissing_noreturn" >&5 printf "%s\n" "$ax_cv_check_cflags___Wmissing_noreturn" >&6; } if test "x$ax_cv_check_cflags___Wmissing_noreturn" = xyes then : CFLAGS="$CFLAGS -Wmissing-noreturn" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wmissing-variable-declarations" >&5 printf %s "checking whether C compiler accepts -Wmissing-variable-declarations... " >&6; } if test ${ax_cv_check_cflags___Wmissing_variable_declarations+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wmissing-variable-declarations" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wmissing_variable_declarations=yes else $as_nop ax_cv_check_cflags___Wmissing_variable_declarations=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wmissing_variable_declarations" >&5 printf "%s\n" "$ax_cv_check_cflags___Wmissing_variable_declarations" >&6; } if test "x$ax_cv_check_cflags___Wmissing_variable_declarations" = xyes then : CFLAGS="$CFLAGS -Wmissing-variable-declarations" else $as_nop : fi # Not used because we cannot change public structs # AX_CHECK_COMPILE_FLAG([-Wpadded], [CFLAGS="$CFLAGS -Wpadded"]) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wsign-conversion" >&5 printf %s "checking whether C compiler accepts -Wsign-conversion... " >&6; } if test ${ax_cv_check_cflags___Wsign_conversion+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wsign-conversion" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wsign_conversion=yes else $as_nop ax_cv_check_cflags___Wsign_conversion=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wsign_conversion" >&5 printf "%s\n" "$ax_cv_check_cflags___Wsign_conversion" >&6; } if test "x$ax_cv_check_cflags___Wsign_conversion" = xyes then : CFLAGS="$CFLAGS -Wsign-conversion" else $as_nop : fi # Not used because this basically disallows default case # AX_CHECK_COMPILE_FLAG([-Wswitch-enum], [CFLAGS="$CFLAGS -Wswitch-enum"]) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wunreachable-code-break" >&5 printf %s "checking whether C compiler accepts -Wunreachable-code-break... " >&6; } if test ${ax_cv_check_cflags___Wunreachable_code_break+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wunreachable-code-break" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wunreachable_code_break=yes else $as_nop ax_cv_check_cflags___Wunreachable_code_break=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wunreachable_code_break" >&5 printf "%s\n" "$ax_cv_check_cflags___Wunreachable_code_break" >&6; } if test "x$ax_cv_check_cflags___Wunreachable_code_break" = xyes then : CFLAGS="$CFLAGS -Wunreachable-code-break" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wunused-macros" >&5 printf %s "checking whether C compiler accepts -Wunused-macros... " >&6; } if test ${ax_cv_check_cflags___Wunused_macros+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wunused-macros" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wunused_macros=yes else $as_nop ax_cv_check_cflags___Wunused_macros=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wunused_macros" >&5 printf "%s\n" "$ax_cv_check_cflags___Wunused_macros" >&6; } if test "x$ax_cv_check_cflags___Wunused_macros" = xyes then : CFLAGS="$CFLAGS -Wunused-macros" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wunused-parameter" >&5 printf %s "checking whether C compiler accepts -Wunused-parameter... " >&6; } if test ${ax_cv_check_cflags___Wunused_parameter+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wunused-parameter" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wunused_parameter=yes else $as_nop ax_cv_check_cflags___Wunused_parameter=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wunused_parameter" >&5 printf "%s\n" "$ax_cv_check_cflags___Wunused_parameter" >&6; } if test "x$ax_cv_check_cflags___Wunused_parameter" = xyes then : CFLAGS="$CFLAGS -Wunused-parameter" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wredundant-decls" >&5 printf %s "checking whether C compiler accepts -Wredundant-decls... " >&6; } if test ${ax_cv_check_cflags___Wredundant_decls+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wredundant-decls" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wredundant_decls=yes else $as_nop ax_cv_check_cflags___Wredundant_decls=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wredundant_decls" >&5 printf "%s\n" "$ax_cv_check_cflags___Wredundant_decls" >&6; } if test "x$ax_cv_check_cflags___Wredundant_decls" = xyes then : CFLAGS="$CFLAGS -Wredundant-decls" else $as_nop : fi # Only work with Clang for the moment { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wheader-guard" >&5 printf %s "checking whether C compiler accepts -Wheader-guard... " >&6; } if test ${ax_cv_check_cflags___Wheader_guard+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wheader-guard" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wheader_guard=yes else $as_nop ax_cv_check_cflags___Wheader_guard=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wheader_guard" >&5 printf "%s\n" "$ax_cv_check_cflags___Wheader_guard" >&6; } if test "x$ax_cv_check_cflags___Wheader_guard" = xyes then : CFLAGS="$CFLAGS -Wheader-guard" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wsometimes-uninitialized" >&5 printf %s "checking whether C compiler accepts -Wsometimes-uninitialized... " >&6; } if test ${ax_cv_check_cflags___Wsometimes_uninitialized+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wsometimes-uninitialized" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wsometimes_uninitialized=yes else $as_nop ax_cv_check_cflags___Wsometimes_uninitialized=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wsometimes_uninitialized" >&5 printf "%s\n" "$ax_cv_check_cflags___Wsometimes_uninitialized" >&6; } if test "x$ax_cv_check_cflags___Wsometimes_uninitialized" = xyes then : CFLAGS="$CFLAGS -Wsometimes-uninitialized" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wextra-semi" >&5 printf %s "checking whether C compiler accepts -Wextra-semi... " >&6; } if test ${ax_cv_check_cflags___Wextra_semi+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wextra-semi" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wextra_semi=yes else $as_nop ax_cv_check_cflags___Wextra_semi=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wextra_semi" >&5 printf "%s\n" "$ax_cv_check_cflags___Wextra_semi" >&6; } if test "x$ax_cv_check_cflags___Wextra_semi" = xyes then : CFLAGS="$CFLAGS -Wextra-semi" else $as_nop : fi # This is required because we pass format string as "const char*. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wno-format-nonliteral" >&5 printf %s "checking whether C compiler accepts -Wno-format-nonliteral... " >&6; } if test ${ax_cv_check_cflags___Wno_format_nonliteral+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Wno-format-nonliteral" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Wno_format_nonliteral=yes else $as_nop ax_cv_check_cflags___Wno_format_nonliteral=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wno_format_nonliteral" >&5 printf "%s\n" "$ax_cv_check_cflags___Wno_format_nonliteral" >&6; } if test "x$ax_cv_check_cflags___Wno_format_nonliteral" = xyes then : CFLAGS="$CFLAGS -Wno-format-nonliteral" else $as_nop : fi # For C++ compiler ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Wall" >&5 printf %s "checking whether C++ compiler accepts -Wall... " >&6; } if test ${ax_cv_check_cxxflags___Wall+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS -Wall" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : ax_cv_check_cxxflags___Wall=yes else $as_nop ax_cv_check_cxxflags___Wall=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cxxflags___Wall" >&5 printf "%s\n" "$ax_cv_check_cxxflags___Wall" >&6; } if test "x$ax_cv_check_cxxflags___Wall" = xyes then : CXXFLAGS="$CXXFLAGS -Wall" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Werror" >&5 printf %s "checking whether C++ compiler accepts -Werror... " >&6; } if test ${ax_cv_check_cxxflags___Werror+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : ax_cv_check_cxxflags___Werror=yes else $as_nop ax_cv_check_cxxflags___Werror=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cxxflags___Werror" >&5 printf "%s\n" "$ax_cv_check_cxxflags___Werror" >&6; } if test "x$ax_cv_check_cxxflags___Werror" = xyes then : CXXFLAGS="$CXXFLAGS -Werror" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Wformat-security" >&5 printf %s "checking whether C++ compiler accepts -Wformat-security... " >&6; } if test ${ax_cv_check_cxxflags___Wformat_security+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS -Wformat-security" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : ax_cv_check_cxxflags___Wformat_security=yes else $as_nop ax_cv_check_cxxflags___Wformat_security=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cxxflags___Wformat_security" >&5 printf "%s\n" "$ax_cv_check_cxxflags___Wformat_security" >&6; } if test "x$ax_cv_check_cxxflags___Wformat_security" = xyes then : CXXFLAGS="$CXXFLAGS -Wformat-security" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Wsometimes-uninitialized" >&5 printf %s "checking whether C++ compiler accepts -Wsometimes-uninitialized... " >&6; } if test ${ax_cv_check_cxxflags___Wsometimes_uninitialized+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS -Wsometimes-uninitialized" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : ax_cv_check_cxxflags___Wsometimes_uninitialized=yes else $as_nop ax_cv_check_cxxflags___Wsometimes_uninitialized=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cxxflags___Wsometimes_uninitialized" >&5 printf "%s\n" "$ax_cv_check_cxxflags___Wsometimes_uninitialized" >&6; } if test "x$ax_cv_check_cxxflags___Wsometimes_uninitialized" = xyes then : CXXFLAGS="$CXXFLAGS -Wsometimes-uninitialized" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Wextra-semi" >&5 printf %s "checking whether C++ compiler accepts -Wextra-semi... " >&6; } if test ${ax_cv_check_cxxflags___Wextra_semi+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS -Wextra-semi" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : ax_cv_check_cxxflags___Wextra_semi=yes else $as_nop ax_cv_check_cxxflags___Wextra_semi=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cxxflags___Wextra_semi" >&5 printf "%s\n" "$ax_cv_check_cxxflags___Wextra_semi" >&6; } if test "x$ax_cv_check_cxxflags___Wextra_semi" = xyes then : CXXFLAGS="$CXXFLAGS -Wextra-semi" else $as_nop : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Wconversion" >&5 printf %s "checking whether C++ compiler accepts -Wconversion... " >&6; } if test ${ax_cv_check_cxxflags___Wconversion+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS -Wconversion" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : ax_cv_check_cxxflags___Wconversion=yes else $as_nop ax_cv_check_cxxflags___Wconversion=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cxxflags___Wconversion" >&5 printf "%s\n" "$ax_cv_check_cxxflags___Wconversion" >&6; } if test "x$ax_cv_check_cxxflags___Wconversion" = xyes then : CXXFLAGS="$CXXFLAGS -Wconversion" else $as_nop : fi # Disable noexcept-type warning of g++-7. This is not harmful as # long as all source files are compiled with the same compiler. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Wno-noexcept-type" >&5 printf %s "checking whether C++ compiler accepts -Wno-noexcept-type... " >&6; } if test ${ax_cv_check_cxxflags___Wno_noexcept_type+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS -Wno-noexcept-type" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : ax_cv_check_cxxflags___Wno_noexcept_type=yes else $as_nop ax_cv_check_cxxflags___Wno_noexcept_type=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cxxflags___Wno_noexcept_type" >&5 printf "%s\n" "$ax_cv_check_cxxflags___Wno_noexcept_type" >&6; } if test "x$ax_cv_check_cxxflags___Wno_noexcept_type" = xyes then : CXXFLAGS="$CXXFLAGS -Wno-noexcept-type" else $as_nop : fi # clang++-18 warns this when building with wolfSSL >= v5.7.6-stable. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Wno-extern-c-compat" >&5 printf %s "checking whether C++ compiler accepts -Wno-extern-c-compat... " >&6; } if test ${ax_cv_check_cxxflags___Wno_extern_c_compat+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS -Wno-extern-c-compat" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : ax_cv_check_cxxflags___Wno_extern_c_compat=yes else $as_nop ax_cv_check_cxxflags___Wno_extern_c_compat=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cxxflags___Wno_extern_c_compat" >&5 printf "%s\n" "$ax_cv_check_cxxflags___Wno_extern_c_compat" >&6; } if test "x$ax_cv_check_cxxflags___Wno_extern_c_compat" = xyes then : CXXFLAGS="$CXXFLAGS -Wno-extern-c-compat" else $as_nop : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi WARNCFLAGS=$CFLAGS WARNCXXFLAGS=$CXXFLAGS CFLAGS=$save_CFLAGS CXXFLAGS=$save_CXXFLAGS EXTRACFLAG= { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -fvisibility=hidden" >&5 printf %s "checking whether C compiler accepts -fvisibility=hidden... " >&6; } if test ${ax_cv_check_cflags___fvisibility_hidden+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -fvisibility=hidden" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___fvisibility_hidden=yes else $as_nop ax_cv_check_cflags___fvisibility_hidden=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___fvisibility_hidden" >&5 printf "%s\n" "$ax_cv_check_cflags___fvisibility_hidden" >&6; } if test "x$ax_cv_check_cflags___fvisibility_hidden" = xyes then : EXTRACFLAG="-fvisibility=hidden" else $as_nop : fi if test "x$debug" != "xno"; then printf "%s\n" "#define DEBUGBUILD 1" >>confdefs.h fi enable_threads=yes # Some platform does not have working std::future. We disable # threading for those platforms. if test "x$threads" != "xyes" || test "x$have_std_future" != "xyes"; then enable_threads=no printf "%s\n" "#define NOTHREADS 1" >>confdefs.h fi # propagate $enable_static to tests/Makefile.am if test "x$enable_static" = "xyes"; then ENABLE_STATIC_TRUE= ENABLE_STATIC_FALSE='#' else ENABLE_STATIC_TRUE='#' ENABLE_STATIC_FALSE= fi ac_config_files="$ac_config_files Makefile lib/Makefile lib/libnghttp2.pc lib/includes/Makefile lib/includes/nghttp2/nghttp2ver.h tests/Makefile tests/testdata/Makefile third-party/Makefile src/Makefile src/testdata/Makefile bpf/Makefile examples/Makefile integration-tests/Makefile integration-tests/config.go integration-tests/setenv doc/Makefile doc/conf.py doc/index.rst doc/package_README.rst doc/tutorial-client.rst doc/tutorial-server.rst doc/tutorial-hpack.rst doc/nghttpx-howto.rst doc/h2load-howto.rst doc/building-android-binary.rst doc/nghttp2.h.rst doc/nghttp2ver.h.rst doc/contribute.rst contrib/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 printf "%s\n" "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 printf %s "checking that generated files are newer than configure... " >&6; } if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: done" >&5 printf "%s\n" "done" >&6; } if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then as_fn_error $? "conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -n "$EXEEXT"; then am__EXEEXT_TRUE= am__EXEEXT_FALSE='#' else am__EXEEXT_TRUE='#' am__EXEEXT_FALSE= fi if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_LIBBPF_TRUE}" && test -z "${HAVE_LIBBPF_FALSE}"; then as_fn_error $? "conditional \"HAVE_LIBBPF\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_LIBXML2_TRUE}" && test -z "${HAVE_LIBXML2_FALSE}"; then as_fn_error $? "conditional \"HAVE_LIBXML2\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_APP_TRUE}" && test -z "${ENABLE_APP_FALSE}"; then as_fn_error $? "conditional \"ENABLE_APP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_HTTP3_TRUE}" && test -z "${ENABLE_HTTP3_FALSE}"; then as_fn_error $? "conditional \"ENABLE_HTTP3\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_HPACK_TOOLS_TRUE}" && test -z "${ENABLE_HPACK_TOOLS_FALSE}"; then as_fn_error $? "conditional \"ENABLE_HPACK_TOOLS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_EXAMPLES_TRUE}" && test -z "${ENABLE_EXAMPLES_FALSE}"; then as_fn_error $? "conditional \"ENABLE_EXAMPLES\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_THIRD_PARTY_TRUE}" && test -z "${ENABLE_THIRD_PARTY_FALSE}"; then as_fn_error $? "conditional \"ENABLE_THIRD_PARTY\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_MRUBY_TRUE}" && test -z "${HAVE_MRUBY_FALSE}"; then as_fn_error $? "conditional \"HAVE_MRUBY\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_NEVERBLEED_TRUE}" && test -z "${HAVE_NEVERBLEED_FALSE}"; then as_fn_error $? "conditional \"HAVE_NEVERBLEED\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_FAILMALLOC_TRUE}" && test -z "${ENABLE_FAILMALLOC_FALSE}"; then as_fn_error $? "conditional \"ENABLE_FAILMALLOC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_STATIC_TRUE}" && test -z "${ENABLE_STATIC_FALSE}"; then as_fn_error $? "conditional \"ENABLE_STATIC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else $as_nop as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by nghttp2 $as_me 1.68.0, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" config_commands="$ac_config_commands" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Configuration commands: $config_commands Report bugs to ." _ACEOF ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ nghttp2 config.status 1.68.0 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" Copyright (C) 2021 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' MKDIR_P='$MKDIR_P' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) printf "%s\n" "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) printf "%s\n" "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) printf "%s\n" "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX printf "%s\n" "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # # INIT-COMMANDS # # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' shared_archive_member_spec='`$ECHO "$shared_archive_member_spec" | $SED "$delay_single_quote_subst"`' SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`' host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`' lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`' reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' FILECMD='`$ECHO "$FILECMD" | $SED "$delay_single_quote_subst"`' OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`' want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`' DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`' sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' lt_ar_flags='`$ECHO "$lt_ar_flags" | $SED "$delay_single_quote_subst"`' AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_import='`$ECHO "$lt_cv_sys_global_symbol_to_import" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' lt_cv_nm_interface='`$ECHO "$lt_cv_nm_interface" | $SED "$delay_single_quote_subst"`' nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`' lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`' lt_cv_truncate_bin='`$ECHO "$lt_cv_truncate_bin" | $SED "$delay_single_quote_subst"`' objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`' DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`' file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' configure_time_dlsearch_path='`$ECHO "$configure_time_dlsearch_path" | $SED "$delay_single_quote_subst"`' configure_time_lt_sys_library_path='`$ECHO "$configure_time_lt_sys_library_path" | $SED "$delay_single_quote_subst"`' hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' compiler_lib_search_dirs='`$ECHO "$compiler_lib_search_dirs" | $SED "$delay_single_quote_subst"`' predep_objects='`$ECHO "$predep_objects" | $SED "$delay_single_quote_subst"`' postdep_objects='`$ECHO "$postdep_objects" | $SED "$delay_single_quote_subst"`' predeps='`$ECHO "$predeps" | $SED "$delay_single_quote_subst"`' postdeps='`$ECHO "$postdeps" | $SED "$delay_single_quote_subst"`' compiler_lib_search_path='`$ECHO "$compiler_lib_search_path" | $SED "$delay_single_quote_subst"`' LD_CXX='`$ECHO "$LD_CXX" | $SED "$delay_single_quote_subst"`' reload_flag_CXX='`$ECHO "$reload_flag_CXX" | $SED "$delay_single_quote_subst"`' reload_cmds_CXX='`$ECHO "$reload_cmds_CXX" | $SED "$delay_single_quote_subst"`' old_archive_cmds_CXX='`$ECHO "$old_archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' compiler_CXX='`$ECHO "$compiler_CXX" | $SED "$delay_single_quote_subst"`' GCC_CXX='`$ECHO "$GCC_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "$lt_prog_compiler_no_builtin_flag_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_pic_CXX='`$ECHO "$lt_prog_compiler_pic_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_wl_CXX='`$ECHO "$lt_prog_compiler_wl_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_static_CXX='`$ECHO "$lt_prog_compiler_static_CXX" | $SED "$delay_single_quote_subst"`' lt_cv_prog_compiler_c_o_CXX='`$ECHO "$lt_cv_prog_compiler_c_o_CXX" | $SED "$delay_single_quote_subst"`' archive_cmds_need_lc_CXX='`$ECHO "$archive_cmds_need_lc_CXX" | $SED "$delay_single_quote_subst"`' enable_shared_with_static_runtimes_CXX='`$ECHO "$enable_shared_with_static_runtimes_CXX" | $SED "$delay_single_quote_subst"`' export_dynamic_flag_spec_CXX='`$ECHO "$export_dynamic_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' whole_archive_flag_spec_CXX='`$ECHO "$whole_archive_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' compiler_needs_object_CXX='`$ECHO "$compiler_needs_object_CXX" | $SED "$delay_single_quote_subst"`' old_archive_from_new_cmds_CXX='`$ECHO "$old_archive_from_new_cmds_CXX" | $SED "$delay_single_quote_subst"`' old_archive_from_expsyms_cmds_CXX='`$ECHO "$old_archive_from_expsyms_cmds_CXX" | $SED "$delay_single_quote_subst"`' archive_cmds_CXX='`$ECHO "$archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' archive_expsym_cmds_CXX='`$ECHO "$archive_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' module_cmds_CXX='`$ECHO "$module_cmds_CXX" | $SED "$delay_single_quote_subst"`' module_expsym_cmds_CXX='`$ECHO "$module_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' with_gnu_ld_CXX='`$ECHO "$with_gnu_ld_CXX" | $SED "$delay_single_quote_subst"`' allow_undefined_flag_CXX='`$ECHO "$allow_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' no_undefined_flag_CXX='`$ECHO "$no_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' hardcode_libdir_flag_spec_CXX='`$ECHO "$hardcode_libdir_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' hardcode_libdir_separator_CXX='`$ECHO "$hardcode_libdir_separator_CXX" | $SED "$delay_single_quote_subst"`' hardcode_direct_CXX='`$ECHO "$hardcode_direct_CXX" | $SED "$delay_single_quote_subst"`' hardcode_direct_absolute_CXX='`$ECHO "$hardcode_direct_absolute_CXX" | $SED "$delay_single_quote_subst"`' hardcode_minus_L_CXX='`$ECHO "$hardcode_minus_L_CXX" | $SED "$delay_single_quote_subst"`' hardcode_shlibpath_var_CXX='`$ECHO "$hardcode_shlibpath_var_CXX" | $SED "$delay_single_quote_subst"`' hardcode_automatic_CXX='`$ECHO "$hardcode_automatic_CXX" | $SED "$delay_single_quote_subst"`' inherit_rpath_CXX='`$ECHO "$inherit_rpath_CXX" | $SED "$delay_single_quote_subst"`' link_all_deplibs_CXX='`$ECHO "$link_all_deplibs_CXX" | $SED "$delay_single_quote_subst"`' always_export_symbols_CXX='`$ECHO "$always_export_symbols_CXX" | $SED "$delay_single_quote_subst"`' export_symbols_cmds_CXX='`$ECHO "$export_symbols_cmds_CXX" | $SED "$delay_single_quote_subst"`' exclude_expsyms_CXX='`$ECHO "$exclude_expsyms_CXX" | $SED "$delay_single_quote_subst"`' include_expsyms_CXX='`$ECHO "$include_expsyms_CXX" | $SED "$delay_single_quote_subst"`' prelink_cmds_CXX='`$ECHO "$prelink_cmds_CXX" | $SED "$delay_single_quote_subst"`' postlink_cmds_CXX='`$ECHO "$postlink_cmds_CXX" | $SED "$delay_single_quote_subst"`' file_list_spec_CXX='`$ECHO "$file_list_spec_CXX" | $SED "$delay_single_quote_subst"`' hardcode_action_CXX='`$ECHO "$hardcode_action_CXX" | $SED "$delay_single_quote_subst"`' compiler_lib_search_dirs_CXX='`$ECHO "$compiler_lib_search_dirs_CXX" | $SED "$delay_single_quote_subst"`' predep_objects_CXX='`$ECHO "$predep_objects_CXX" | $SED "$delay_single_quote_subst"`' postdep_objects_CXX='`$ECHO "$postdep_objects_CXX" | $SED "$delay_single_quote_subst"`' predeps_CXX='`$ECHO "$predeps_CXX" | $SED "$delay_single_quote_subst"`' postdeps_CXX='`$ECHO "$postdeps_CXX" | $SED "$delay_single_quote_subst"`' compiler_lib_search_path_CXX='`$ECHO "$compiler_lib_search_path_CXX" | $SED "$delay_single_quote_subst"`' LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } # Quote evaled strings. for var in SHELL \ ECHO \ PATH_SEPARATOR \ SED \ GREP \ EGREP \ FGREP \ LD \ NM \ LN_S \ lt_SP2NL \ lt_NL2SP \ reload_flag \ FILECMD \ OBJDUMP \ deplibs_check_method \ file_magic_cmd \ file_magic_glob \ want_nocaseglob \ DLLTOOL \ sharedlib_from_linklib_cmd \ AR \ archiver_list_spec \ STRIP \ RANLIB \ CC \ CFLAGS \ compiler \ lt_cv_sys_global_symbol_pipe \ lt_cv_sys_global_symbol_to_cdecl \ lt_cv_sys_global_symbol_to_import \ lt_cv_sys_global_symbol_to_c_name_address \ lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ lt_cv_nm_interface \ nm_file_list_spec \ lt_cv_truncate_bin \ lt_prog_compiler_no_builtin_flag \ lt_prog_compiler_pic \ lt_prog_compiler_wl \ lt_prog_compiler_static \ lt_cv_prog_compiler_c_o \ need_locks \ MANIFEST_TOOL \ DSYMUTIL \ NMEDIT \ LIPO \ OTOOL \ OTOOL64 \ shrext_cmds \ export_dynamic_flag_spec \ whole_archive_flag_spec \ compiler_needs_object \ with_gnu_ld \ allow_undefined_flag \ no_undefined_flag \ hardcode_libdir_flag_spec \ hardcode_libdir_separator \ exclude_expsyms \ include_expsyms \ file_list_spec \ variables_saved_for_relink \ libname_spec \ library_names_spec \ soname_spec \ install_override_mode \ finish_eval \ old_striplib \ striplib \ compiler_lib_search_dirs \ predep_objects \ postdep_objects \ predeps \ postdeps \ compiler_lib_search_path \ LD_CXX \ reload_flag_CXX \ compiler_CXX \ lt_prog_compiler_no_builtin_flag_CXX \ lt_prog_compiler_pic_CXX \ lt_prog_compiler_wl_CXX \ lt_prog_compiler_static_CXX \ lt_cv_prog_compiler_c_o_CXX \ export_dynamic_flag_spec_CXX \ whole_archive_flag_spec_CXX \ compiler_needs_object_CXX \ with_gnu_ld_CXX \ allow_undefined_flag_CXX \ no_undefined_flag_CXX \ hardcode_libdir_flag_spec_CXX \ hardcode_libdir_separator_CXX \ exclude_expsyms_CXX \ include_expsyms_CXX \ file_list_spec_CXX \ compiler_lib_search_dirs_CXX \ predep_objects_CXX \ postdep_objects_CXX \ predeps_CXX \ postdeps_CXX \ compiler_lib_search_path_CXX; do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in reload_cmds \ old_postinstall_cmds \ old_postuninstall_cmds \ old_archive_cmds \ extract_expsyms_cmds \ old_archive_from_new_cmds \ old_archive_from_expsyms_cmds \ archive_cmds \ archive_expsym_cmds \ module_cmds \ module_expsym_cmds \ export_symbols_cmds \ prelink_cmds \ postlink_cmds \ postinstall_cmds \ postuninstall_cmds \ finish_cmds \ sys_lib_search_path_spec \ configure_time_dlsearch_path \ configure_time_lt_sys_library_path \ reload_cmds_CXX \ old_archive_cmds_CXX \ old_archive_from_new_cmds_CXX \ old_archive_from_expsyms_cmds_CXX \ archive_cmds_CXX \ archive_expsym_cmds_CXX \ module_cmds_CXX \ module_expsym_cmds_CXX \ export_symbols_cmds_CXX \ prelink_cmds_CXX \ postlink_cmds_CXX; do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done ac_aux_dir='$ac_aux_dir' # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi PACKAGE='$PACKAGE' VERSION='$VERSION' RM='$RM' ofile='$ofile' AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "lib/Makefile") CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;; "lib/libnghttp2.pc") CONFIG_FILES="$CONFIG_FILES lib/libnghttp2.pc" ;; "lib/includes/Makefile") CONFIG_FILES="$CONFIG_FILES lib/includes/Makefile" ;; "lib/includes/nghttp2/nghttp2ver.h") CONFIG_FILES="$CONFIG_FILES lib/includes/nghttp2/nghttp2ver.h" ;; "tests/Makefile") CONFIG_FILES="$CONFIG_FILES tests/Makefile" ;; "tests/testdata/Makefile") CONFIG_FILES="$CONFIG_FILES tests/testdata/Makefile" ;; "third-party/Makefile") CONFIG_FILES="$CONFIG_FILES third-party/Makefile" ;; "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; "src/testdata/Makefile") CONFIG_FILES="$CONFIG_FILES src/testdata/Makefile" ;; "bpf/Makefile") CONFIG_FILES="$CONFIG_FILES bpf/Makefile" ;; "examples/Makefile") CONFIG_FILES="$CONFIG_FILES examples/Makefile" ;; "integration-tests/Makefile") CONFIG_FILES="$CONFIG_FILES integration-tests/Makefile" ;; "integration-tests/config.go") CONFIG_FILES="$CONFIG_FILES integration-tests/config.go" ;; "integration-tests/setenv") CONFIG_FILES="$CONFIG_FILES integration-tests/setenv" ;; "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; "doc/conf.py") CONFIG_FILES="$CONFIG_FILES doc/conf.py" ;; "doc/index.rst") CONFIG_FILES="$CONFIG_FILES doc/index.rst" ;; "doc/package_README.rst") CONFIG_FILES="$CONFIG_FILES doc/package_README.rst" ;; "doc/tutorial-client.rst") CONFIG_FILES="$CONFIG_FILES doc/tutorial-client.rst" ;; "doc/tutorial-server.rst") CONFIG_FILES="$CONFIG_FILES doc/tutorial-server.rst" ;; "doc/tutorial-hpack.rst") CONFIG_FILES="$CONFIG_FILES doc/tutorial-hpack.rst" ;; "doc/nghttpx-howto.rst") CONFIG_FILES="$CONFIG_FILES doc/nghttpx-howto.rst" ;; "doc/h2load-howto.rst") CONFIG_FILES="$CONFIG_FILES doc/h2load-howto.rst" ;; "doc/building-android-binary.rst") CONFIG_FILES="$CONFIG_FILES doc/building-android-binary.rst" ;; "doc/nghttp2.h.rst") CONFIG_FILES="$CONFIG_FILES doc/nghttp2.h.rst" ;; "doc/nghttp2ver.h.rst") CONFIG_FILES="$CONFIG_FILES doc/nghttp2ver.h.rst" ;; "doc/contribute.rst") CONFIG_FILES="$CONFIG_FILES doc/contribute.rst" ;; "contrib/Makefile") CONFIG_FILES="$CONFIG_FILES contrib/Makefile" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers test ${CONFIG_COMMANDS+y} || CONFIG_COMMANDS=$config_commands fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 printf "%s\n" "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`printf "%s\n" "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac ac_MKDIR_P=$MKDIR_P case $MKDIR_P in [\\/$]* | ?:[\\/]* ) ;; */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 printf "%s\n" "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi # Compute "$ac_file"'s index in $config_headers. _am_arg="$ac_file" _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || $as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$_am_arg" : 'X\(//\)[^/]' \| \ X"$_am_arg" : 'X\(//\)$' \| \ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$_am_arg" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'`/stamp-h$_am_stamp_count ;; :C) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 printf "%s\n" "$as_me: executing $ac_file commands" >&6;} ;; esac case $ac_file$ac_mode in "libtool":C) # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi cfgfile=${ofile}T trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # Generated automatically by $as_me ($PACKAGE) $VERSION # NOTE: Changes made to this file will be lost: look at ltmain.sh. # Provide generalized library-building support services. # Written by Gordon Matzigkeit, 1996 # Copyright (C) 2014 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program or library that is built # using GNU Libtool, you may include this file under the same # distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # The names of the tagged configurations supported by this script. available_tags='CXX ' # Configured defaults for sys_lib_dlsearch_path munging. : \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} # ### BEGIN LIBTOOL CONFIG # Which release of libtool.m4 was used? macro_version=$macro_version macro_revision=$macro_revision # Whether or not to build shared libraries. build_libtool_libs=$enable_shared # Whether or not to build static libraries. build_old_libs=$enable_static # What type of objects to build. pic_mode=$pic_mode # Whether or not to optimize for fast installation. fast_install=$enable_fast_install # Shared archive member basename,for filename based shared library versioning on AIX. shared_archive_member_spec=$shared_archive_member_spec # Shell to use when invoking shell scripts. SHELL=$lt_SHELL # An echo program that protects backslashes. ECHO=$lt_ECHO # The PATH separator for the build system. PATH_SEPARATOR=$lt_PATH_SEPARATOR # The host system. host_alias=$host_alias host=$host host_os=$host_os # The build system. build_alias=$build_alias build=$build build_os=$build_os # A sed program that does not truncate output. SED=$lt_SED # Sed that helps us avoid accidentally triggering echo(1) options like -n. Xsed="\$SED -e 1s/^X//" # A grep program that handles long lines. GREP=$lt_GREP # An ERE matcher. EGREP=$lt_EGREP # A literal string matcher. FGREP=$lt_FGREP # A BSD- or MS-compatible name lister. NM=$lt_NM # Whether we need soft or hard links. LN_S=$lt_LN_S # What is the maximum length of a command? max_cmd_len=$max_cmd_len # Object file suffix (normally "o"). objext=$ac_objext # Executable file suffix (normally ""). exeext=$exeext # whether the shell understands "unset". lt_unset=$lt_unset # turn spaces into newlines. SP2NL=$lt_lt_SP2NL # turn newlines into spaces. NL2SP=$lt_lt_NL2SP # convert \$build file names to \$host format. to_host_file_cmd=$lt_cv_to_host_file_cmd # convert \$build files to toolchain format. to_tool_file_cmd=$lt_cv_to_tool_file_cmd # A file(cmd) program that detects file types. FILECMD=$lt_FILECMD # An object symbol dumper. OBJDUMP=$lt_OBJDUMP # Method to check whether dependent libraries are shared objects. deplibs_check_method=$lt_deplibs_check_method # Command to use when deplibs_check_method = "file_magic". file_magic_cmd=$lt_file_magic_cmd # How to find potential files when deplibs_check_method = "file_magic". file_magic_glob=$lt_file_magic_glob # Find potential files using nocaseglob when deplibs_check_method = "file_magic". want_nocaseglob=$lt_want_nocaseglob # DLL creation program. DLLTOOL=$lt_DLLTOOL # Command to associate shared and link libraries. sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd # The archiver. AR=$lt_AR # Flags to create an archive (by configure). lt_ar_flags=$lt_ar_flags # Flags to create an archive. AR_FLAGS=\${ARFLAGS-"\$lt_ar_flags"} # How to feed a file listing to the archiver. archiver_list_spec=$lt_archiver_list_spec # A symbol stripping program. STRIP=$lt_STRIP # Commands used to install an old-style archive. RANLIB=$lt_RANLIB old_postinstall_cmds=$lt_old_postinstall_cmds old_postuninstall_cmds=$lt_old_postuninstall_cmds # Whether to use a lock for old archive extraction. lock_old_archive_extraction=$lock_old_archive_extraction # A C compiler. LTCC=$lt_CC # LTCC compiler flags. LTCFLAGS=$lt_CFLAGS # Take the output of nm and produce a listing of raw symbols and C names. global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe # Transform the output of nm in a proper C declaration. global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl # Transform the output of nm into a list of symbols to manually relocate. global_symbol_to_import=$lt_lt_cv_sys_global_symbol_to_import # Transform the output of nm in a C name address pair. global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address # Transform the output of nm in a C name address pair when lib prefix is needed. global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix # The name lister interface. nm_interface=$lt_lt_cv_nm_interface # Specify filename containing input files for \$NM. nm_file_list_spec=$lt_nm_file_list_spec # The root where to search for dependent libraries,and where our libraries should be installed. lt_sysroot=$lt_sysroot # Command to truncate a binary pipe. lt_truncate_bin=$lt_lt_cv_truncate_bin # The name of the directory that contains temporary libtool files. objdir=$objdir # Used to examine libraries when file_magic_cmd begins with "file". MAGIC_CMD=$MAGIC_CMD # Must we lock files when doing compilation? need_locks=$lt_need_locks # Manifest tool. MANIFEST_TOOL=$lt_MANIFEST_TOOL # Tool to manipulate archived DWARF debug symbol files on Mac OS X. DSYMUTIL=$lt_DSYMUTIL # Tool to change global to local symbols on Mac OS X. NMEDIT=$lt_NMEDIT # Tool to manipulate fat objects and archives on Mac OS X. LIPO=$lt_LIPO # ldd/readelf like tool for Mach-O binaries on Mac OS X. OTOOL=$lt_OTOOL # ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. OTOOL64=$lt_OTOOL64 # Old archive suffix (normally "a"). libext=$libext # Shared library suffix (normally ".so"). shrext_cmds=$lt_shrext_cmds # The commands to extract the exported symbol list from a shared archive. extract_expsyms_cmds=$lt_extract_expsyms_cmds # Variables whose values should be saved in libtool wrapper scripts and # restored at link time. variables_saved_for_relink=$lt_variables_saved_for_relink # Do we need the "lib" prefix for modules? need_lib_prefix=$need_lib_prefix # Do we need a version for libraries? need_version=$need_version # Library versioning type. version_type=$version_type # Shared library runtime path variable. runpath_var=$runpath_var # Shared library path variable. shlibpath_var=$shlibpath_var # Is shlibpath searched before the hard-coded library search path? shlibpath_overrides_runpath=$shlibpath_overrides_runpath # Format of library name prefix. libname_spec=$lt_libname_spec # List of archive names. First name is the real one, the rest are links. # The last name is the one that the linker finds with -lNAME library_names_spec=$lt_library_names_spec # The coded name of the library, if different from the real name. soname_spec=$lt_soname_spec # Permission mode override for installation of shared libraries. install_override_mode=$lt_install_override_mode # Command to use after installation of a shared archive. postinstall_cmds=$lt_postinstall_cmds # Command to use after uninstallation of a shared archive. postuninstall_cmds=$lt_postuninstall_cmds # Commands used to finish a libtool library installation in a directory. finish_cmds=$lt_finish_cmds # As "finish_cmds", except a single script fragment to be evaled but # not shown. finish_eval=$lt_finish_eval # Whether we should hardcode library paths into libraries. hardcode_into_libs=$hardcode_into_libs # Compile-time system search path for libraries. sys_lib_search_path_spec=$lt_sys_lib_search_path_spec # Detected run-time system search path for libraries. sys_lib_dlsearch_path_spec=$lt_configure_time_dlsearch_path # Explicit LT_SYS_LIBRARY_PATH set during ./configure time. configure_time_lt_sys_library_path=$lt_configure_time_lt_sys_library_path # Whether dlopen is supported. dlopen_support=$enable_dlopen # Whether dlopen of programs is supported. dlopen_self=$enable_dlopen_self # Whether dlopen of statically linked programs is supported. dlopen_self_static=$enable_dlopen_self_static # Commands to strip libraries. old_striplib=$lt_old_striplib striplib=$lt_striplib # The linker used to build libraries. LD=$lt_LD # How to create reloadable object files. reload_flag=$lt_reload_flag reload_cmds=$lt_reload_cmds # Commands used to build an old-style archive. old_archive_cmds=$lt_old_archive_cmds # A language specific compiler. CC=$lt_compiler # Is the compiler the GNU compiler? with_gcc=$GCC # Compiler flag to turn off builtin functions. no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag # Additional compiler flags for building library objects. pic_flag=$lt_lt_prog_compiler_pic # How to pass a linker flag through the compiler. wl=$lt_lt_prog_compiler_wl # Compiler flag to prevent dynamic linking. link_static_flag=$lt_lt_prog_compiler_static # Does compiler simultaneously support -c and -o options? compiler_c_o=$lt_lt_cv_prog_compiler_c_o # Whether or not to add -lc for building shared libraries. build_libtool_need_lc=$archive_cmds_need_lc # Whether or not to disallow shared libs when runtime libs are static. allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes # Compiler flag to allow reflexive dlopens. export_dynamic_flag_spec=$lt_export_dynamic_flag_spec # Compiler flag to generate shared objects directly from archives. whole_archive_flag_spec=$lt_whole_archive_flag_spec # Whether the compiler copes with passing no objects directly. compiler_needs_object=$lt_compiler_needs_object # Create an old-style archive from a shared archive. old_archive_from_new_cmds=$lt_old_archive_from_new_cmds # Create a temporary old-style archive to link instead of a shared archive. old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds # Commands used to build a shared archive. archive_cmds=$lt_archive_cmds archive_expsym_cmds=$lt_archive_expsym_cmds # Commands used to build a loadable module if different from building # a shared archive. module_cmds=$lt_module_cmds module_expsym_cmds=$lt_module_expsym_cmds # Whether we are building with GNU ld or not. with_gnu_ld=$lt_with_gnu_ld # Flag that allows shared libraries with undefined symbols to be built. allow_undefined_flag=$lt_allow_undefined_flag # Flag that enforces no undefined symbols. no_undefined_flag=$lt_no_undefined_flag # Flag to hardcode \$libdir into a binary during linking. # This must work even if \$libdir does not exist hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec # Whether we need a single "-rpath" flag with a separated argument. hardcode_libdir_separator=$lt_hardcode_libdir_separator # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary. hardcode_direct=$hardcode_direct # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary and the resulting library dependency is # "absolute",i.e impossible to change by setting \$shlibpath_var if the # library is relocated. hardcode_direct_absolute=$hardcode_direct_absolute # Set to "yes" if using the -LDIR flag during linking hardcodes DIR # into the resulting binary. hardcode_minus_L=$hardcode_minus_L # Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR # into the resulting binary. hardcode_shlibpath_var=$hardcode_shlibpath_var # Set to "yes" if building a shared library automatically hardcodes DIR # into the library and all subsequent libraries and executables linked # against it. hardcode_automatic=$hardcode_automatic # Set to yes if linker adds runtime paths of dependent libraries # to runtime path list. inherit_rpath=$inherit_rpath # Whether libtool must link a program against all its dependency libraries. link_all_deplibs=$link_all_deplibs # Set to "yes" if exported symbols are required. always_export_symbols=$always_export_symbols # The commands to list exported symbols. export_symbols_cmds=$lt_export_symbols_cmds # Symbols that should not be listed in the preloaded symbols. exclude_expsyms=$lt_exclude_expsyms # Symbols that must always be exported. include_expsyms=$lt_include_expsyms # Commands necessary for linking programs (against libraries) with templates. prelink_cmds=$lt_prelink_cmds # Commands necessary for finishing linking programs. postlink_cmds=$lt_postlink_cmds # Specify filename containing input files. file_list_spec=$lt_file_list_spec # How to hardcode a shared library path into an executable. hardcode_action=$hardcode_action # The directories searched by this compiler when creating a shared library. compiler_lib_search_dirs=$lt_compiler_lib_search_dirs # Dependencies to place before and after the objects being linked to # create a shared library. predep_objects=$lt_predep_objects postdep_objects=$lt_postdep_objects predeps=$lt_predeps postdeps=$lt_postdeps # The library search path used internally by the compiler when linking # a shared library. compiler_lib_search_path=$lt_compiler_lib_search_path # ### END LIBTOOL CONFIG _LT_EOF cat <<'_LT_EOF' >> "$cfgfile" # ### BEGIN FUNCTIONS SHARED WITH CONFIGURE # func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x$2 in x) ;; *:) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" ;; x:*) eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" ;; *::*) eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" ;; *) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" ;; esac } # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in $*""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } # ### END FUNCTIONS SHARED WITH CONFIGURE _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac ltmain=$ac_aux_dir/ltmain.sh # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? $SED '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" cat <<_LT_EOF >> "$ofile" # ### BEGIN LIBTOOL TAG CONFIG: CXX # The linker used to build libraries. LD=$lt_LD_CXX # How to create reloadable object files. reload_flag=$lt_reload_flag_CXX reload_cmds=$lt_reload_cmds_CXX # Commands used to build an old-style archive. old_archive_cmds=$lt_old_archive_cmds_CXX # A language specific compiler. CC=$lt_compiler_CXX # Is the compiler the GNU compiler? with_gcc=$GCC_CXX # Compiler flag to turn off builtin functions. no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX # Additional compiler flags for building library objects. pic_flag=$lt_lt_prog_compiler_pic_CXX # How to pass a linker flag through the compiler. wl=$lt_lt_prog_compiler_wl_CXX # Compiler flag to prevent dynamic linking. link_static_flag=$lt_lt_prog_compiler_static_CXX # Does compiler simultaneously support -c and -o options? compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX # Whether or not to add -lc for building shared libraries. build_libtool_need_lc=$archive_cmds_need_lc_CXX # Whether or not to disallow shared libs when runtime libs are static. allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX # Compiler flag to allow reflexive dlopens. export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX # Compiler flag to generate shared objects directly from archives. whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX # Whether the compiler copes with passing no objects directly. compiler_needs_object=$lt_compiler_needs_object_CXX # Create an old-style archive from a shared archive. old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX # Create a temporary old-style archive to link instead of a shared archive. old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX # Commands used to build a shared archive. archive_cmds=$lt_archive_cmds_CXX archive_expsym_cmds=$lt_archive_expsym_cmds_CXX # Commands used to build a loadable module if different from building # a shared archive. module_cmds=$lt_module_cmds_CXX module_expsym_cmds=$lt_module_expsym_cmds_CXX # Whether we are building with GNU ld or not. with_gnu_ld=$lt_with_gnu_ld_CXX # Flag that allows shared libraries with undefined symbols to be built. allow_undefined_flag=$lt_allow_undefined_flag_CXX # Flag that enforces no undefined symbols. no_undefined_flag=$lt_no_undefined_flag_CXX # Flag to hardcode \$libdir into a binary during linking. # This must work even if \$libdir does not exist hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX # Whether we need a single "-rpath" flag with a separated argument. hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary. hardcode_direct=$hardcode_direct_CXX # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary and the resulting library dependency is # "absolute",i.e impossible to change by setting \$shlibpath_var if the # library is relocated. hardcode_direct_absolute=$hardcode_direct_absolute_CXX # Set to "yes" if using the -LDIR flag during linking hardcodes DIR # into the resulting binary. hardcode_minus_L=$hardcode_minus_L_CXX # Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR # into the resulting binary. hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX # Set to "yes" if building a shared library automatically hardcodes DIR # into the library and all subsequent libraries and executables linked # against it. hardcode_automatic=$hardcode_automatic_CXX # Set to yes if linker adds runtime paths of dependent libraries # to runtime path list. inherit_rpath=$inherit_rpath_CXX # Whether libtool must link a program against all its dependency libraries. link_all_deplibs=$link_all_deplibs_CXX # Set to "yes" if exported symbols are required. always_export_symbols=$always_export_symbols_CXX # The commands to list exported symbols. export_symbols_cmds=$lt_export_symbols_cmds_CXX # Symbols that should not be listed in the preloaded symbols. exclude_expsyms=$lt_exclude_expsyms_CXX # Symbols that must always be exported. include_expsyms=$lt_include_expsyms_CXX # Commands necessary for linking programs (against libraries) with templates. prelink_cmds=$lt_prelink_cmds_CXX # Commands necessary for finishing linking programs. postlink_cmds=$lt_postlink_cmds_CXX # Specify filename containing input files. file_list_spec=$lt_file_list_spec_CXX # How to hardcode a shared library path into an executable. hardcode_action=$hardcode_action_CXX # The directories searched by this compiler when creating a shared library. compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX # Dependencies to place before and after the objects being linked to # create a shared library. predep_objects=$lt_predep_objects_CXX postdep_objects=$lt_postdep_objects_CXX predeps=$lt_predeps_CXX postdeps=$lt_postdeps_CXX # The library search path used internally by the compiler when linking # a shared library. compiler_lib_search_path=$lt_compiler_lib_search_path_CXX # ### END LIBTOOL TAG CONFIG: CXX _LT_EOF ;; "depfiles":C) test x"$AMDEP_TRUE" != x"" || { # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. # TODO: see whether this extra hack can be removed once we start # requiring Autoconf 2.70 or later. case $CONFIG_FILES in #( *\'*) : eval set x "$CONFIG_FILES" ;; #( *) : set x $CONFIG_FILES ;; #( *) : ;; esac shift # Used to flag and report bootstrapping failures. am_rc=0 for am_mf do # Strip MF so we end up with the name of the file. am_mf=`printf "%s\n" "$am_mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile which includes # dependency-tracking related rules and includes. # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ || continue am_dirpart=`$as_dirname -- "$am_mf" || $as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$am_mf" : 'X\(//\)[^/]' \| \ X"$am_mf" : 'X\(//\)$' \| \ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$am_mf" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` am_filepart=`$as_basename -- "$am_mf" || $as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \ X"$am_mf" : 'X\(//\)$' \| \ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$am_mf" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` { echo "$as_me:$LINENO: cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles" >&5 (cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } || am_rc=$? done if test $am_rc -ne 0; then { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "Something went wrong bootstrapping makefile fragments for automatic dependency tracking. If GNU make was not used, consider re-running the configure script with MAKE=\"gmake\" (or whatever is necessary). You can also try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking). See \`config.log' for more details" "$LINENO" 5; } fi { am_dirpart=; unset am_dirpart;} { am_filepart=; unset am_filepart;} { am_mf=; unset am_mf;} { am_rc=; unset am_rc;} rm -f conftest-deps.mk } ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: summary of build options: Package version: ${VERSION} Library version: $LT_CURRENT:$LT_REVISION:$LT_AGE Install prefix: ${prefix} System types: Build: ${build} Host: ${host} Target: ${target} Compiler: C compiler: ${CC} CFLAGS: ${CFLAGS} LDFLAGS: ${LDFLAGS} C++ compiler: ${CXX} CXXFLAGS: ${CXXFLAGS} CXXCPP: ${CXXCPP} C preprocessor: ${CPP} CPPFLAGS: ${CPPFLAGS} WARNCFLAGS: ${WARNCFLAGS} WARNCXXFLAGS: ${WARNCXXFLAGS} CXX1XCXXFLAGS: ${CXX1XCXXFLAGS} EXTRACFLAG: ${EXTRACFLAG} BPFCFLAGS: ${BPFCFLAGS} EXTRABPFCFLAGS: ${EXTRABPFCFLAGS} LIBS: ${LIBS} DEFS: ${DEFS} EXTRA_DEFS: ${EXTRA_DEFS} Library: Shared: ${enable_shared} Static: ${enable_static} Libtool: LIBTOOL_LDFLAGS: ${LIBTOOL_LDFLAGS} Python: Python: ${PYTHON} PYTHON_VERSION: ${PYTHON_VERSION} Test: Failmalloc: ${enable_failmalloc} Libs: wolfSSL: ${have_wolfssl} (CFLAGS='${WOLFSSL_CFLAGS}' LIBS='${WOLFSSL_LIBS}') OpenSSL: ${have_openssl} (CFLAGS='${OPENSSL_CFLAGS}' LIBS='${OPENSSL_LIBS}') Libxml2: ${have_libxml2} (CFLAGS='${LIBXML2_CFLAGS}' LIBS='${LIBXML2_LIBS}') Libev: ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}') Libc-ares: ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_LIBS}') libngtcp2: ${have_libngtcp2} (CFLAGS='${LIBNGTCP2_CFLAGS}' LIBS='${LIBNGTCP2_LIBS}') libngtcp2_crypto_quictls: ${have_libngtcp2_crypto_quictls} (CFLAGS='${LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_QUICTLS_LIBS}') libngtcp2_crypto_libressl: ${have_libngtcp2_crypto_libressl} (CFLAGS='${LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_LIBRESSL_LIBS}') libngtcp2_crypto_boringssl: ${have_libngtcp2_crypto_boringssl} (CFLAGS='${LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_BORINGSSL_LIBS}') libngtcp2_crypto_ossl: ${have_libngtcp2_crypto_ossl} (CFLAGS='${LIBNGTCP2_CRYPTO_OSSL_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_OSSL_LIBS}') libnghttp3: ${have_libnghttp3} (CFLAGS='${LIBNGHTTP3_CFLAGS}' LIBS='${LIBNGHTTP3_LIBS}') libbpf: ${have_libbpf} (CFLAGS='${LIBBPF_CFLAGS}' LIBS='${LIBBPF_LIBS}') Libevent(SSL): ${have_libevent_openssl} (CFLAGS='${LIBEVENT_OPENSSL_CFLAGS}' LIBS='${LIBEVENT_OPENSSL_LIBS}') Jansson: ${have_jansson} (CFLAGS='${JANSSON_CFLAGS}' LIBS='${JANSSON_LIBS}') Jemalloc: ${have_jemalloc} (CFLAGS='${JEMALLOC_CFLAGS}' LIBS='${JEMALLOC_LIBS}') Zlib: ${have_zlib} (CFLAGS='${ZLIB_CFLAGS}' LIBS='${ZLIB_LIBS}') Systemd: ${have_libsystemd} (CFLAGS='${SYSTEMD_CFLAGS}' LIBS='${SYSTEMD_LIBS}') Libbrotlienc: ${have_libbrotlienc} (CFLAGS=\"${LIBBROTLIENC_CFLAGS}' LIBS='${LIBBROTLIENC_LIBS}') Libbrotlidec: ${have_libbrotlidec} (CFLAGS=\"${LIBBROTLIDEC_CFLAGS}' LIBS='${LIBBROTLIDEC_LIBS}') Third-party: http-parser: ${enable_third_party} MRuby: ${have_mruby} (CFLAGS='${LIBMRUBY_CFLAGS}' LIBS='${LIBMRUBY_LIBS}') Neverbleed: ${have_neverbleed} Features: Applications: ${enable_app} HPACK tools: ${enable_hpack_tools} Examples: ${enable_examples} Threading: ${enable_threads} HTTP/3 (EXPERIMENTAL): ${enable_http3} " >&5 printf "%s\n" "$as_me: summary of build options: Package version: ${VERSION} Library version: $LT_CURRENT:$LT_REVISION:$LT_AGE Install prefix: ${prefix} System types: Build: ${build} Host: ${host} Target: ${target} Compiler: C compiler: ${CC} CFLAGS: ${CFLAGS} LDFLAGS: ${LDFLAGS} C++ compiler: ${CXX} CXXFLAGS: ${CXXFLAGS} CXXCPP: ${CXXCPP} C preprocessor: ${CPP} CPPFLAGS: ${CPPFLAGS} WARNCFLAGS: ${WARNCFLAGS} WARNCXXFLAGS: ${WARNCXXFLAGS} CXX1XCXXFLAGS: ${CXX1XCXXFLAGS} EXTRACFLAG: ${EXTRACFLAG} BPFCFLAGS: ${BPFCFLAGS} EXTRABPFCFLAGS: ${EXTRABPFCFLAGS} LIBS: ${LIBS} DEFS: ${DEFS} EXTRA_DEFS: ${EXTRA_DEFS} Library: Shared: ${enable_shared} Static: ${enable_static} Libtool: LIBTOOL_LDFLAGS: ${LIBTOOL_LDFLAGS} Python: Python: ${PYTHON} PYTHON_VERSION: ${PYTHON_VERSION} Test: Failmalloc: ${enable_failmalloc} Libs: wolfSSL: ${have_wolfssl} (CFLAGS='${WOLFSSL_CFLAGS}' LIBS='${WOLFSSL_LIBS}') OpenSSL: ${have_openssl} (CFLAGS='${OPENSSL_CFLAGS}' LIBS='${OPENSSL_LIBS}') Libxml2: ${have_libxml2} (CFLAGS='${LIBXML2_CFLAGS}' LIBS='${LIBXML2_LIBS}') Libev: ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}') Libc-ares: ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_LIBS}') libngtcp2: ${have_libngtcp2} (CFLAGS='${LIBNGTCP2_CFLAGS}' LIBS='${LIBNGTCP2_LIBS}') libngtcp2_crypto_quictls: ${have_libngtcp2_crypto_quictls} (CFLAGS='${LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_QUICTLS_LIBS}') libngtcp2_crypto_libressl: ${have_libngtcp2_crypto_libressl} (CFLAGS='${LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_LIBRESSL_LIBS}') libngtcp2_crypto_boringssl: ${have_libngtcp2_crypto_boringssl} (CFLAGS='${LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_BORINGSSL_LIBS}') libngtcp2_crypto_ossl: ${have_libngtcp2_crypto_ossl} (CFLAGS='${LIBNGTCP2_CRYPTO_OSSL_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_OSSL_LIBS}') libnghttp3: ${have_libnghttp3} (CFLAGS='${LIBNGHTTP3_CFLAGS}' LIBS='${LIBNGHTTP3_LIBS}') libbpf: ${have_libbpf} (CFLAGS='${LIBBPF_CFLAGS}' LIBS='${LIBBPF_LIBS}') Libevent(SSL): ${have_libevent_openssl} (CFLAGS='${LIBEVENT_OPENSSL_CFLAGS}' LIBS='${LIBEVENT_OPENSSL_LIBS}') Jansson: ${have_jansson} (CFLAGS='${JANSSON_CFLAGS}' LIBS='${JANSSON_LIBS}') Jemalloc: ${have_jemalloc} (CFLAGS='${JEMALLOC_CFLAGS}' LIBS='${JEMALLOC_LIBS}') Zlib: ${have_zlib} (CFLAGS='${ZLIB_CFLAGS}' LIBS='${ZLIB_LIBS}') Systemd: ${have_libsystemd} (CFLAGS='${SYSTEMD_CFLAGS}' LIBS='${SYSTEMD_LIBS}') Libbrotlienc: ${have_libbrotlienc} (CFLAGS=\"${LIBBROTLIENC_CFLAGS}' LIBS='${LIBBROTLIENC_LIBS}') Libbrotlidec: ${have_libbrotlidec} (CFLAGS=\"${LIBBROTLIDEC_CFLAGS}' LIBS='${LIBBROTLIDEC_LIBS}') Third-party: http-parser: ${enable_third_party} MRuby: ${have_mruby} (CFLAGS='${LIBMRUBY_CFLAGS}' LIBS='${LIBMRUBY_LIBS}') Neverbleed: ${have_neverbleed} Features: Applications: ${enable_app} HPACK tools: ${enable_hpack_tools} Examples: ${enable_examples} Threading: ${enable_threads} HTTP/3 (EXPERIMENTAL): ${enable_http3} " >&6;} nghttp2-1.68.0/PaxHeaders/config.sub0000644000000000000000000000013015077107305014235 xustar0030 mtime=1761382085.329388185 29 atime=1761382087.70037803 29 ctime=1761382107.84130402 nghttp2-1.68.0/config.sub0000755000175100017510000010511615077107305014636 0ustar00runnerrunner#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2022 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268 # see below for rationale timestamp='2022-01-03' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # https://git.savannah.gnu.org/cgit/config.git/plain/config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. # The "shellcheck disable" line above the timestamp inhibits complaints # about features and limitations of the classic Bourne shell that were # superseded or lifted in POSIX. However, this script identifies a wide # variety of pre-POSIX systems that do not have POSIX shells at all, and # even some reasonably current systems (Solaris 10 as case-in-point) still # have a pre-POSIX /bin/sh. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2022 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; *local*) # First pass through any local machine types. echo "$1" exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Split fields of configuration type # shellcheck disable=SC2162 saved_IFS=$IFS IFS="-" read field1 field2 field3 field4 <&2 exit 1 ;; *-*-*-*) basic_machine=$field1-$field2 basic_os=$field3-$field4 ;; *-*-*) # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two # parts maybe_os=$field2-$field3 case $maybe_os in nto-qnx* | linux-* | uclinux-uclibc* \ | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ | storm-chaos* | os2-emx* | rtmk-nova*) basic_machine=$field1 basic_os=$maybe_os ;; android-linux) basic_machine=$field1-unknown basic_os=linux-android ;; *) basic_machine=$field1-$field2 basic_os=$field3 ;; esac ;; *-*) # A lone config we happen to match not fitting any pattern case $field1-$field2 in decstation-3100) basic_machine=mips-dec basic_os= ;; *-*) # Second component is usually, but not always the OS case $field2 in # Prevent following clause from handling this valid os sun*os*) basic_machine=$field1 basic_os=$field2 ;; zephyr*) basic_machine=$field1-unknown basic_os=$field2 ;; # Manufacturers dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ | unicom* | ibm* | next | hp | isi* | apollo | altos* \ | convergent* | ncr* | news | 32* | 3600* | 3100* \ | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ | ultra | tti* | harris | dolphin | highlevel | gould \ | cbm | ns | masscomp | apple | axis | knuth | cray \ | microblaze* | sim | cisco \ | oki | wec | wrs | winbond) basic_machine=$field1-$field2 basic_os= ;; *) basic_machine=$field1 basic_os=$field2 ;; esac ;; esac ;; *) # Convert single-component short-hands not valid as part of # multi-component configurations. case $field1 in 386bsd) basic_machine=i386-pc basic_os=bsd ;; a29khif) basic_machine=a29k-amd basic_os=udi ;; adobe68k) basic_machine=m68010-adobe basic_os=scout ;; alliant) basic_machine=fx80-alliant basic_os= ;; altos | altos3068) basic_machine=m68k-altos basic_os= ;; am29k) basic_machine=a29k-none basic_os=bsd ;; amdahl) basic_machine=580-amdahl basic_os=sysv ;; amiga) basic_machine=m68k-unknown basic_os= ;; amigaos | amigados) basic_machine=m68k-unknown basic_os=amigaos ;; amigaunix | amix) basic_machine=m68k-unknown basic_os=sysv4 ;; apollo68) basic_machine=m68k-apollo basic_os=sysv ;; apollo68bsd) basic_machine=m68k-apollo basic_os=bsd ;; aros) basic_machine=i386-pc basic_os=aros ;; aux) basic_machine=m68k-apple basic_os=aux ;; balance) basic_machine=ns32k-sequent basic_os=dynix ;; blackfin) basic_machine=bfin-unknown basic_os=linux ;; cegcc) basic_machine=arm-unknown basic_os=cegcc ;; convex-c1) basic_machine=c1-convex basic_os=bsd ;; convex-c2) basic_machine=c2-convex basic_os=bsd ;; convex-c32) basic_machine=c32-convex basic_os=bsd ;; convex-c34) basic_machine=c34-convex basic_os=bsd ;; convex-c38) basic_machine=c38-convex basic_os=bsd ;; cray) basic_machine=j90-cray basic_os=unicos ;; crds | unos) basic_machine=m68k-crds basic_os= ;; da30) basic_machine=m68k-da30 basic_os= ;; decstation | pmax | pmin | dec3100 | decstatn) basic_machine=mips-dec basic_os= ;; delta88) basic_machine=m88k-motorola basic_os=sysv3 ;; dicos) basic_machine=i686-pc basic_os=dicos ;; djgpp) basic_machine=i586-pc basic_os=msdosdjgpp ;; ebmon29k) basic_machine=a29k-amd basic_os=ebmon ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson basic_os=ose ;; gmicro) basic_machine=tron-gmicro basic_os=sysv ;; go32) basic_machine=i386-pc basic_os=go32 ;; h8300hms) basic_machine=h8300-hitachi basic_os=hms ;; h8300xray) basic_machine=h8300-hitachi basic_os=xray ;; h8500hms) basic_machine=h8500-hitachi basic_os=hms ;; harris) basic_machine=m88k-harris basic_os=sysv3 ;; hp300 | hp300hpux) basic_machine=m68k-hp basic_os=hpux ;; hp300bsd) basic_machine=m68k-hp basic_os=bsd ;; hppaosf) basic_machine=hppa1.1-hp basic_os=osf ;; hppro) basic_machine=hppa1.1-hp basic_os=proelf ;; i386mach) basic_machine=i386-mach basic_os=mach ;; isi68 | isi) basic_machine=m68k-isi basic_os=sysv ;; m68knommu) basic_machine=m68k-unknown basic_os=linux ;; magnum | m3230) basic_machine=mips-mips basic_os=sysv ;; merlin) basic_machine=ns32k-utek basic_os=sysv ;; mingw64) basic_machine=x86_64-pc basic_os=mingw64 ;; mingw32) basic_machine=i686-pc basic_os=mingw32 ;; mingw32ce) basic_machine=arm-unknown basic_os=mingw32ce ;; monitor) basic_machine=m68k-rom68k basic_os=coff ;; morphos) basic_machine=powerpc-unknown basic_os=morphos ;; moxiebox) basic_machine=moxie-unknown basic_os=moxiebox ;; msdos) basic_machine=i386-pc basic_os=msdos ;; msys) basic_machine=i686-pc basic_os=msys ;; mvs) basic_machine=i370-ibm basic_os=mvs ;; nacl) basic_machine=le32-unknown basic_os=nacl ;; ncr3000) basic_machine=i486-ncr basic_os=sysv4 ;; netbsd386) basic_machine=i386-pc basic_os=netbsd ;; netwinder) basic_machine=armv4l-rebel basic_os=linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony basic_os=newsos ;; news1000) basic_machine=m68030-sony basic_os=newsos ;; necv70) basic_machine=v70-nec basic_os=sysv ;; nh3000) basic_machine=m68k-harris basic_os=cxux ;; nh[45]000) basic_machine=m88k-harris basic_os=cxux ;; nindy960) basic_machine=i960-intel basic_os=nindy ;; mon960) basic_machine=i960-intel basic_os=mon960 ;; nonstopux) basic_machine=mips-compaq basic_os=nonstopux ;; os400) basic_machine=powerpc-ibm basic_os=os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson basic_os=ose ;; os68k) basic_machine=m68k-none basic_os=os68k ;; paragon) basic_machine=i860-intel basic_os=osf ;; parisc) basic_machine=hppa-unknown basic_os=linux ;; psp) basic_machine=mipsallegrexel-sony basic_os=psp ;; pw32) basic_machine=i586-unknown basic_os=pw32 ;; rdos | rdos64) basic_machine=x86_64-pc basic_os=rdos ;; rdos32) basic_machine=i386-pc basic_os=rdos ;; rom68k) basic_machine=m68k-rom68k basic_os=coff ;; sa29200) basic_machine=a29k-amd basic_os=udi ;; sei) basic_machine=mips-sei basic_os=seiux ;; sequent) basic_machine=i386-sequent basic_os= ;; sps7) basic_machine=m68k-bull basic_os=sysv2 ;; st2000) basic_machine=m68k-tandem basic_os= ;; stratus) basic_machine=i860-stratus basic_os=sysv4 ;; sun2) basic_machine=m68000-sun basic_os= ;; sun2os3) basic_machine=m68000-sun basic_os=sunos3 ;; sun2os4) basic_machine=m68000-sun basic_os=sunos4 ;; sun3) basic_machine=m68k-sun basic_os= ;; sun3os3) basic_machine=m68k-sun basic_os=sunos3 ;; sun3os4) basic_machine=m68k-sun basic_os=sunos4 ;; sun4) basic_machine=sparc-sun basic_os= ;; sun4os3) basic_machine=sparc-sun basic_os=sunos3 ;; sun4os4) basic_machine=sparc-sun basic_os=sunos4 ;; sun4sol2) basic_machine=sparc-sun basic_os=solaris2 ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun basic_os= ;; sv1) basic_machine=sv1-cray basic_os=unicos ;; symmetry) basic_machine=i386-sequent basic_os=dynix ;; t3e) basic_machine=alphaev5-cray basic_os=unicos ;; t90) basic_machine=t90-cray basic_os=unicos ;; toad1) basic_machine=pdp10-xkl basic_os=tops20 ;; tpf) basic_machine=s390x-ibm basic_os=tpf ;; udi29k) basic_machine=a29k-amd basic_os=udi ;; ultra3) basic_machine=a29k-nyu basic_os=sym1 ;; v810 | necv810) basic_machine=v810-nec basic_os=none ;; vaxv) basic_machine=vax-dec basic_os=sysv ;; vms) basic_machine=vax-dec basic_os=vms ;; vsta) basic_machine=i386-pc basic_os=vsta ;; vxworks960) basic_machine=i960-wrs basic_os=vxworks ;; vxworks68) basic_machine=m68k-wrs basic_os=vxworks ;; vxworks29k) basic_machine=a29k-wrs basic_os=vxworks ;; xbox) basic_machine=i686-pc basic_os=mingw32 ;; ymp) basic_machine=ymp-cray basic_os=unicos ;; *) basic_machine=$1 basic_os= ;; esac ;; esac # Decode 1-component or ad-hoc basic machines case $basic_machine in # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) cpu=hppa1.1 vendor=winbond ;; op50n) cpu=hppa1.1 vendor=oki ;; op60c) cpu=hppa1.1 vendor=oki ;; ibm*) cpu=i370 vendor=ibm ;; orion105) cpu=clipper vendor=highlevel ;; mac | mpw | mac-mpw) cpu=m68k vendor=apple ;; pmac | pmac-mpw) cpu=powerpc vendor=apple ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) cpu=m68000 vendor=att ;; 3b*) cpu=we32k vendor=att ;; bluegene*) cpu=powerpc vendor=ibm basic_os=cnk ;; decsystem10* | dec10*) cpu=pdp10 vendor=dec basic_os=tops10 ;; decsystem20* | dec20*) cpu=pdp10 vendor=dec basic_os=tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) cpu=m68k vendor=motorola ;; dpx2*) cpu=m68k vendor=bull basic_os=sysv3 ;; encore | umax | mmax) cpu=ns32k vendor=encore ;; elxsi) cpu=elxsi vendor=elxsi basic_os=${basic_os:-bsd} ;; fx2800) cpu=i860 vendor=alliant ;; genix) cpu=ns32k vendor=ns ;; h3050r* | hiux*) cpu=hppa1.1 vendor=hitachi basic_os=hiuxwe2 ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) cpu=m68000 vendor=hp ;; hp9k3[2-9][0-9]) cpu=m68k vendor=hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) cpu=hppa1.1 vendor=hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp cpu=hppa1.1 vendor=hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp cpu=hppa1.1 vendor=hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) cpu=hppa1.1 vendor=hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; i*86v32) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv32 ;; i*86v4*) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv4 ;; i*86v) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv ;; i*86sol2) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=solaris2 ;; j90 | j90-cray) cpu=j90 vendor=cray basic_os=${basic_os:-unicos} ;; iris | iris4d) cpu=mips vendor=sgi case $basic_os in irix*) ;; *) basic_os=irix4 ;; esac ;; miniframe) cpu=m68000 vendor=convergent ;; *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) cpu=m68k vendor=atari basic_os=mint ;; news-3600 | risc-news) cpu=mips vendor=sony basic_os=newsos ;; next | m*-next) cpu=m68k vendor=next case $basic_os in openstep*) ;; nextstep*) ;; ns2*) basic_os=nextstep2 ;; *) basic_os=nextstep3 ;; esac ;; np1) cpu=np1 vendor=gould ;; op50n-* | op60c-*) cpu=hppa1.1 vendor=oki basic_os=proelf ;; pa-hitachi) cpu=hppa1.1 vendor=hitachi basic_os=hiuxwe2 ;; pbd) cpu=sparc vendor=tti ;; pbb) cpu=m68k vendor=tti ;; pc532) cpu=ns32k vendor=pc532 ;; pn) cpu=pn vendor=gould ;; power) cpu=power vendor=ibm ;; ps2) cpu=i386 vendor=ibm ;; rm[46]00) cpu=mips vendor=siemens ;; rtpc | rtpc-*) cpu=romp vendor=ibm ;; sde) cpu=mipsisa32 vendor=sde basic_os=${basic_os:-elf} ;; simso-wrs) cpu=sparclite vendor=wrs basic_os=vxworks ;; tower | tower-32) cpu=m68k vendor=ncr ;; vpp*|vx|vx-*) cpu=f301 vendor=fujitsu ;; w65) cpu=w65 vendor=wdc ;; w89k-*) cpu=hppa1.1 vendor=winbond basic_os=proelf ;; none) cpu=none vendor=none ;; leon|leon[3-9]) cpu=sparc vendor=$basic_machine ;; leon-*|leon[3-9]-*) cpu=sparc vendor=`echo "$basic_machine" | sed 's/-.*//'` ;; *-*) # shellcheck disable=SC2162 saved_IFS=$IFS IFS="-" read cpu vendor <&2 exit 1 ;; esac ;; esac # Here we canonicalize certain aliases for manufacturers. case $vendor in digital*) vendor=dec ;; commodore*) vendor=cbm ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if test x$basic_os != x then # First recognize some ad-hoc cases, or perhaps split kernel-os, or else just # set os. case $basic_os in gnu/linux*) kernel=linux os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'` ;; os2-emx) kernel=os2 os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'` ;; nto-qnx*) kernel=nto os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'` ;; *-*) # shellcheck disable=SC2162 saved_IFS=$IFS IFS="-" read kernel os <&2 exit 1 ;; esac # As a final step for OS-related things, validate the OS-kernel combination # (given a valid OS), if there is a kernel. case $kernel-$os in linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* \ | linux-musl* | linux-relibc* | linux-uclibc* ) ;; uclinux-uclibc* ) ;; -dietlibc* | -newlib* | -musl* | -relibc* | -uclibc* ) # These are just libc implementations, not actual OSes, and thus # require a kernel. echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2 exit 1 ;; kfreebsd*-gnu* | kopensolaris*-gnu*) ;; vxworks-simlinux | vxworks-simwindows | vxworks-spe) ;; nto-qnx*) ;; os2-emx) ;; *-eabi* | *-gnueabi*) ;; -*) # Blank kernel with real OS is always fine. ;; *-*) echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2 exit 1 ;; esac # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. case $vendor in unknown) case $cpu-$os in *-riscix*) vendor=acorn ;; *-sunos*) vendor=sun ;; *-cnk* | *-aix*) vendor=ibm ;; *-beos*) vendor=be ;; *-hpux*) vendor=hp ;; *-mpeix*) vendor=hp ;; *-hiux*) vendor=hitachi ;; *-unos*) vendor=crds ;; *-dgux*) vendor=dg ;; *-luna*) vendor=omron ;; *-genix*) vendor=ns ;; *-clix*) vendor=intergraph ;; *-mvs* | *-opened*) vendor=ibm ;; *-os400*) vendor=ibm ;; s390-* | s390x-*) vendor=ibm ;; *-ptx*) vendor=sequent ;; *-tpf*) vendor=ibm ;; *-vxsim* | *-vxworks* | *-windiss*) vendor=wrs ;; *-aux*) vendor=apple ;; *-hms*) vendor=hitachi ;; *-mpw* | *-macos*) vendor=apple ;; *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) vendor=atari ;; *-vos*) vendor=stratus ;; esac ;; esac echo "$cpu-$vendor-${kernel:+$kernel-}$os" exit # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: nghttp2-1.68.0/PaxHeaders/COPYING0000644000000000000000000000013215077107270013313 xustar0030 mtime=1761382072.960444291 30 atime=1761382104.847316405 30 ctime=1761382107.832304046 nghttp2-1.68.0/COPYING0000644000175100017510000000220415077107270013701 0ustar00runnerrunnerThe MIT License Copyright (c) 2012, 2014, 2015, 2016 Tatsuhiro Tsujikawa Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. nghttp2-1.68.0/PaxHeaders/AUTHORS0000644000000000000000000000013215077107270013330 xustar0030 mtime=1761382072.960444291 30 atime=1761382104.846316409 30 ctime=1761382107.830304052 nghttp2-1.68.0/AUTHORS0000644000175100017510000000535115077107270013724 0ustar00runnerrunnernghttp2 project was started as a fork of spdylay project [1]. Both projects were started by Tatsuhiro Tsujikawa, who is still the main author of these projects. Meanwhile, we have many contributions, and we are not here without them. We sincerely thank you to all who made a contribution. Here is the all individuals/organizations who contributed to nghttp2 and spdylay project at which we forked. These names are retrieved from git commit log. If you have made a contribution, but you are missing in the list, please let us know via github issues [2]. [1] https://github.com/tatsuhiro-t/spdylay [2] https://github.com/nghttp2/nghttp2/issues -------- 187j3x1 Adam Gołębiowski Alek Storm Alex Nalivko Alexandr Vlasov Alexandros Konstantinakis-Karmis Alexis La Goutte Alyssa Ross Amir Livneh Amir Pakdel Anders Bakken Andreas Pohl Andrew Penkrat Andy Davies Angus Gratton Anna Henningsen Ant Bryan Anthony Alayo Asra Ali Benedikt Christoph Wolters Benjamin Peterson Bernard Spil Bernhard Walle Brendan Heinonen Brian Card Brian Suh Daniel Bevenius Daniel Evers Daniel Stenberg Dave Reisner David Beitey David Korczynski David Weekly Deel Deep Chordia Dimitris Apostolou Dmitri Tikhonov Dmitriy Vetutnev Don Dylan Plecki Etienne Cimon Fabian Möller Fabian Wiesel Fred Sundvik Gabi Davar Gaël PORTAY Geoff Hill George Liu Gitai Google Inc. Hajime Fujita Jacky Tian Jacky_Yin Jacob Champion James M Snell Jan Kundrát Jan-E Janusz Dziemidowicz Jay Satiro Jeff 'Raid' Baitis Jianqing Wang Jim Morrison Jiwoo Park Jonas Kvinge Josh Braegger José F. Calcerrada Kamil Dudka Karthik Dasari Kazuho Oku Kenny (kang-yen) Peng Kenny Peng Kit Chan Kyle Schomp LazyHamster Leo Neat Lorenz Nickel Lucas Pardue Lukas Märdian MATSUMOTO Ryosuke Marc Bachmann Marcelo Trylesinski Mark Boddington Matt Rudary Matt Way Michael Kaufmann Mike Conlen Mike Frysinger Mike Lothian Nicholas Hurley Nora Shoemaker Paweł Wegner Pedro Santos Peeyush Aggarwal Peng-Yu Chen Peter Wu Piotr Sikora PufferOverflow Raul Gutierrez Segales Remo E Renaud Reza Tavakoli Richard Wolfert Rick Lei Ross Smith II Rudi Heitbaum Ryan Carsten Schmidt Ryo Ota Scott Mitchell Sebastiaan Deckers Sergei Trofimovich Sergey Fedorov Shelley Vohr Simon Frankenberger Simone Basso Soham Sinha Stefan Eissing Stephen Ludin Sunpoet Po-Chuan Hsieh Svante Signell Syohei YOSHIDA Tapanito Tatsuhiko Kubo Tatsuhiro Tsujikawa Thomas Devoogdt Tobias Geerinckx-Rice Tom Harwood Tomas Krizek Tomasz Buchert Tomasz Torcz Vernon Tang Viacheslav Biriukov Viktor Szakats Viktor Szépe Ville Vesilehto Wenfeng Liu William A Rowe Jr Xiaoguang Sun Zachary Turner Zhuoyun Wei acesso ayanamist bmarques1995 bxshi clemahieu dalf dawg es fangdingjun feicong hrxi jwchoi kumagi lhuang04 lstefani makovich mod-h2-dev moparisthebest robaho snnn yuuki-kodama nghttp2-1.68.0/PaxHeaders/third-party0000644000000000000000000000013215077107334014453 xustar0030 mtime=1761382108.224302913 30 atime=1761382109.795298372 30 ctime=1761382108.224302913 nghttp2-1.68.0/third-party/0000755000175100017510000000000015077107334015120 5ustar00runnerrunnernghttp2-1.68.0/third-party/PaxHeaders/build_config.rb0000644000000000000000000000013115077107271017500 xustar0030 mtime=1761382073.009444065 30 atime=1761382080.109411467 29 ctime=1761382108.22530291 nghttp2-1.68.0/third-party/build_config.rb0000644000175100017510000000250115077107271020067 0ustar00runnerrunnerdef config(conf) toolchain :clang if ENV['MRUBY_CC'].include? "clang" toolchain :gcc if ENV['MRUBY_CC'].include? "gcc" conf.cc.command = ENV['MRUBY_CC'] conf.cxx.command = ENV['MRUBY_CXX'] if ENV['MRUBY_LD'] conf.linker.command = ENV['MRUBY_LD'] end if ENV['MRUBY_AR'] conf.archiver.command = ENV['MRUBY_AR'] end # C++ project needs this. Without this, mruby exception does not # properly destroy C++ object allocated on stack. conf.enable_cxx_exception conf.build_dir = ENV['BUILD_DIR'] # Here is the mruby gems included in default.gembox minus # mruby-bin-debugger which causes the application to crash. conf.gembox "stdlib" conf.gembox "stdlib-ext" conf.gembox "stdlib-io" conf.gembox "math" conf.gembox "metaprog" # Generate mrbc command conf.gem :core => "mruby-bin-mrbc" # Generate mirb command conf.gem :core => "mruby-bin-mirb" # Generate mruby command conf.gem :core => "mruby-bin-mruby" # Generate mruby-strip command conf.gem :core => "mruby-bin-strip" # Generate mruby-config command conf.gem :core => "mruby-bin-config" # Added by nghttp2 project conf.gem :core => 'mruby-eval' end if ENV['BUILD'] == ENV['HOST'] then MRuby::Build.new do |conf| config(conf) end else MRuby::CrossBuild.new(ENV['HOST']) do |conf| config(conf) end end nghttp2-1.68.0/third-party/PaxHeaders/neverbleed0000644000000000000000000000013215077107334016566 xustar0030 mtime=1761382108.219302928 30 atime=1761382109.795298372 30 ctime=1761382108.219302928 nghttp2-1.68.0/third-party/neverbleed/0000755000175100017510000000000015077107334017233 5ustar00runnerrunnernghttp2-1.68.0/third-party/neverbleed/PaxHeaders/neverbleed.h0000644000000000000000000000013215077107276021134 xustar0030 mtime=1761382078.156420401 30 atime=1761382080.175411164 30 ctime=1761382108.219302928 nghttp2-1.68.0/third-party/neverbleed/neverbleed.h0000644000175100017510000001126115077107276021525 0ustar00runnerrunner/* * Copyright (c) 2015 Kazuho Oku, DeNA Co., Ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef NEVERBLEED_H #define NEVERBLEED_H #include #include #include #ifdef __FreeBSD__ #include #endif #ifdef __cplusplus extern "C" { #endif #if (defined(__linux__) && !defined(__ANDROID__)) || defined(__FreeBSD__) || defined(__NetBSD__) #define NEVERBLEED_HAS_PTHREAD_SETAFFINITY_NP 1 #if defined(__linux__) #define NEVERBLEED_CPU_SET_T cpu_set_t #else #define NEVERBLEED_CPU_SET_T cpuset_t #endif #endif #define NEVERBLEED_ERRBUF_SIZE (256) #define NEVERBLEED_AUTH_TOKEN_SIZE 32 typedef struct st_neverbleed_t { ENGINE *engine; pid_t daemon_pid; struct sockaddr_un sun_; pthread_key_t thread_key; unsigned char auth_token[NEVERBLEED_AUTH_TOKEN_SIZE]; } neverbleed_t; typedef struct st_neverbleed_iobuf_t { char *buf; char *start; char *end; size_t capacity; struct st_neverbleed_iobuf_t *next; unsigned processing : 1; } neverbleed_iobuf_t; /** * initializes the privilege separation engine (returns 0 if successful) */ int neverbleed_init(neverbleed_t *nb, char *errbuf); /** * loads a private key file (returns 1 if successful) */ int neverbleed_load_private_key_file(neverbleed_t *nb, SSL_CTX *ctx, const char *fn, char *errbuf); /** * setuidgid (also changes the file permissions so that `user` can connect to the daemon, if change_socket_ownership is non-zero) */ int neverbleed_setuidgid(neverbleed_t *nb, const char *user, int change_socket_ownership); /** * builds a digestsign request */ void neverbleed_start_digestsign(neverbleed_iobuf_t *buf, EVP_PKEY *pkey, const EVP_MD *md, const void *input, size_t len, int rsa_pss); /** * parses a digestsign response */ void neverbleed_finish_digestsign(neverbleed_iobuf_t *buf, void **digest, size_t *digest_len); /** * builds a RSA decrypt request */ void neverbleed_start_decrypt(neverbleed_iobuf_t *buf, EVP_PKEY *pkey, const void *input, size_t len); /** * parses a decrypt response */ void neverbleed_finish_decrypt(neverbleed_iobuf_t *buf, void **digest, size_t *digest_len); #if NEVERBLEED_HAS_PTHREAD_SETAFFINITY_NP /** * set the cpu affinity for the neverbleed thread (returns 0 if successful) */ int neverbleed_setaffinity(neverbleed_t *nb, NEVERBLEED_CPU_SET_T *cpuset); #endif /** * an optional callback that can be registered by the application for doing stuff immediately after the neverbleed process is being * spawned */ extern void (*neverbleed_post_fork_cb)(void); /** * An optional callback used for replacing `iobuf_transaction`; i.e., the logic that sends the request and receives the response. * * If `responseless` equals `1`, the ownership of stack-allocated `req` is given to the callback. In this case, `req` must be free'd using `neverbleed_iobuf_dispose` */ extern void (*neverbleed_transaction_cb)(neverbleed_iobuf_t *req, int responseless); typedef void (*neverbleed_cb)(int); int neverbleed_get_fd(neverbleed_t *nb); static size_t neverbleed_iobuf_size(neverbleed_iobuf_t *buf); void neverbleed_iobuf_dispose(neverbleed_iobuf_t *buf); void neverbleed_transaction_read(neverbleed_t *nb, neverbleed_iobuf_t *buf); void neverbleed_transaction_write(neverbleed_t *nb, neverbleed_iobuf_t *buf); /** * if set to a non-zero value, RSA operations are offloaded */ extern enum neverbleed_offload_type { NEVERBLEED_OFFLOAD_OFF = 0, NEVERBLEED_OFFLOAD_QAT_ON, NEVERBLEED_OFFLOAD_QAT_AUTO, } neverbleed_offload; /* inline function definitions */ inline size_t neverbleed_iobuf_size(neverbleed_iobuf_t *buf) { return (size_t)(buf->end - buf->start); } #ifdef __cplusplus } #endif #endif nghttp2-1.68.0/third-party/neverbleed/PaxHeaders/neverbleed.c0000644000000000000000000000013215077107276021127 xustar0030 mtime=1761382078.156420401 30 atime=1761382080.175411164 30 ctime=1761382108.218302931 nghttp2-1.68.0/third-party/neverbleed/neverbleed.c0000644000175100017510000020603215077107276021522 0ustar00runnerrunner/* * Copyright (c) 2015 Kazuho Oku, DeNA Co., Ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__linux__) #include #include #include #elif defined(__APPLE__) #include #elif defined(__FreeBSD__) #include #elif defined(__sun) #include #endif /* to maximize code-reuse between different stacks, we intentionally use API declared by OpenSSL as legacy */ #define OPENSSL_SUPPRESS_DEPRECATED #include #include #if defined(LIBRESSL_VERSION_NUMBER) ? LIBRESSL_VERSION_NUMBER >= 0x3050000fL : OPENSSL_VERSION_NUMBER >= 0x1010000fL /* RSA_METHOD is opaque, so RSA_meth* are used. */ #define NEVERBLEED_OPAQUE_RSA_METHOD #endif #if OPENSSL_VERSION_NUMBER >= 0x1010000fL && !defined(OPENSSL_NO_EC) && \ (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x2090100fL) /* EC_KEY_METHOD and related APIs are avaliable, so ECDSA is enabled. */ #define NEVERBLEED_ECDSA #endif #include #ifdef NEVERBLEED_ECDSA #include #endif #include #include #include #ifdef __linux #if OPENSSL_VERSION_NUMBER >= 0x1010000fL && !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL) #define USE_OFFLOAD 1 #endif #if defined(OPENSSL_IS_BORINGSSL) && defined(NEVERBLEED_BORINGSSL_USE_QAT) #include "qat_bssl.h" /* the mapping seems to be missing */ #ifndef ASYNC_WAIT_CTX_get_all_fds extern int bssl_async_wait_ctx_get_all_fds(ASYNC_WAIT_CTX *ctx, OSSL_ASYNC_FD *fd, size_t *numfds); #define ASYNC_WAIT_CTX_get_all_fds bssl_async_wait_ctx_get_all_fds #endif #define USE_OFFLOAD 1 #endif #endif #if OPENSSL_VERSION_NUMBER < 0x1010000fL || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) static void RSA_get0_key(const RSA *rsa, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) { if (n) { *n = rsa->n; } if (e) { *e = rsa->e; } if (d) { *d = rsa->d; } } static int RSA_set0_key(RSA *rsa, BIGNUM *n, BIGNUM *e, BIGNUM *d) { if (n == NULL || e == NULL) { return 0; } BN_free(rsa->n); BN_free(rsa->e); BN_free(rsa->d); rsa->n = n; rsa->e = e; rsa->d = d; return 1; } static void RSA_set_flags(RSA *r, int flags) { r->flags |= flags; } #define EVP_PKEY_up_ref(p) CRYPTO_add(&(p)->references, 1, CRYPTO_LOCK_EVP_PKEY) #endif #include "neverbleed.h" enum neverbleed_type { NEVERBLEED_TYPE_ERROR, NEVERBLEED_TYPE_RSA, NEVERBLEED_TYPE_ECDSA }; struct st_neverbleed_rsa_exdata_t { neverbleed_t *nb; size_t key_index; }; struct st_neverbleed_thread_data_t { pid_t self_pid; int fd; }; /** * a variant of pthread_once, that does not require you to declare a callback, nor have a global variable */ #define NEVERBLEED_MULTITHREAD_ONCE(block) \ do { \ static volatile int lock = 0; \ int lock_loaded = lock; \ __sync_synchronize(); \ if (!lock_loaded) { \ static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; \ pthread_mutex_lock(&mutex); \ if (!lock) { \ do { \ block \ } while (0); \ __sync_synchronize(); \ lock = 1; \ } \ pthread_mutex_unlock(&mutex); \ } \ } while (0) static void warnvf(const char *fmt, va_list args) { char errbuf[256]; if (errno != 0) { strerror_r(errno, errbuf, sizeof(errbuf)); } else { errbuf[0] = '\0'; } fprintf(stderr, "[openssl-privsep] "); vfprintf(stderr, fmt, args); if (errbuf[0] != '\0') fputs(errbuf, stderr); fputc('\n', stderr); } __attribute__((format(printf, 1, 2))) static void warnf(const char *fmt, ...) { va_list args; va_start(args, fmt); warnvf(fmt, args); va_end(args); } __attribute__((format(printf, 1, 2), noreturn)) static void dief(const char *fmt, ...) { va_list args; va_start(args, fmt); warnvf(fmt, args); va_end(args); abort(); } static char *dirname(const char *path) { const char *last_slash = strrchr(path, '/'); char *ret; if (last_slash == NULL) { errno = 0; dief("dirname: no slash in given path:%s", path); } if ((ret = malloc(last_slash + 1 - path)) == NULL) dief("no memory"); memcpy(ret, path, last_slash - path); ret[last_slash - path] = '\0'; return ret; } static void set_cloexec(int fd) { if (fcntl(fd, F_SETFD, O_CLOEXEC) == -1) dief("failed to set O_CLOEXEC to fd %d", fd); } static int read_nbytes(int fd, void *p, size_t sz) { while (sz != 0) { ssize_t r; while ((r = read(fd, p, sz)) == -1 && errno == EINTR) ; if (r == -1) { return -1; } else if (r == 0) { errno = 0; return -1; } p = (char *)p + r; sz -= r; } return 0; } /** * This function disposes of the memory allocated for `neverbleed_iobuf_t`, but retains the value of `next` and `processing` so that * the buffer can be "cleared" while in use by worker threads. */ static void iobuf_dispose(neverbleed_iobuf_t *buf) { if (buf->capacity != 0) OPENSSL_cleanse(buf->buf, buf->capacity); free(buf->buf); buf->buf = NULL; buf->start = NULL; buf->end = NULL; buf->capacity = 0; } static void iobuf_reserve(neverbleed_iobuf_t *buf, size_t extra) { size_t start_off, end_off; if (extra <= buf->buf - buf->end + buf->capacity) return; if (buf->capacity == 0) buf->capacity = 4096; while (buf->buf - buf->end + buf->capacity < extra) buf->capacity *= 2; if (buf->buf != NULL) { start_off = buf->start - buf->buf; end_off = buf->end - buf->buf; } else { /* C99 forbids us doing `buf->start - buf->buf` when both are NULL (undefined behavior) */ start_off = 0; end_off = 0; } if ((buf->buf = realloc(buf->buf, buf->capacity)) == NULL) dief("realloc failed"); buf->start = buf->buf + start_off; buf->end = buf->buf + end_off; } static void iobuf_push_num(neverbleed_iobuf_t *buf, size_t v) { iobuf_reserve(buf, sizeof(v)); memcpy(buf->end, &v, sizeof(v)); buf->end += sizeof(v); } static void iobuf_push_str(neverbleed_iobuf_t *buf, const char *s) { size_t l = strlen(s) + 1; iobuf_reserve(buf, l); memcpy(buf->end, s, l); buf->end += l; } static void iobuf_push_bytes(neverbleed_iobuf_t *buf, const void *p, size_t l) { iobuf_push_num(buf, l); iobuf_reserve(buf, l); memcpy(buf->end, p, l); buf->end += l; } static int iobuf_shift_num(neverbleed_iobuf_t *buf, size_t *v) { if (neverbleed_iobuf_size(buf) < sizeof(*v)) return -1; memcpy(v, buf->start, sizeof(*v)); buf->start += sizeof(*v); return 0; } static char *iobuf_shift_str(neverbleed_iobuf_t *buf) { char *nul = memchr(buf->start, '\0', neverbleed_iobuf_size(buf)), *ret; if (nul == NULL) return NULL; ret = buf->start; buf->start = nul + 1; return ret; } static void *iobuf_shift_bytes(neverbleed_iobuf_t *buf, size_t *l) { void *ret; if (iobuf_shift_num(buf, l) != 0) return NULL; if (neverbleed_iobuf_size(buf) < *l) return NULL; ret = buf->start; buf->start += *l; return ret; } static int iobuf_write(neverbleed_iobuf_t *buf, int fd) { struct iovec vecs[2] = {{NULL}}; size_t bufsz = neverbleed_iobuf_size(buf); int vecindex; ssize_t r; vecs[0].iov_base = &bufsz; vecs[0].iov_len = sizeof(bufsz); vecs[1].iov_base = buf->start; vecs[1].iov_len = bufsz; for (vecindex = 0; vecindex != sizeof(vecs) / sizeof(vecs[0]);) { while ((r = writev(fd, vecs + vecindex, sizeof(vecs) / sizeof(vecs[0]) - vecindex)) == -1 && errno == EINTR) ; if (r == -1) return -1; assert(r != 0); while (r != 0 && r >= vecs[vecindex].iov_len) { r -= vecs[vecindex].iov_len; ++vecindex; } if (r != 0) { vecs[vecindex].iov_base = (char *)vecs[vecindex].iov_base + r; vecs[vecindex].iov_len -= r; } } return 0; } static int iobuf_read(neverbleed_iobuf_t *buf, int fd) { size_t sz; if (read_nbytes(fd, &sz, sizeof(sz)) != 0) return -1; iobuf_reserve(buf, sz); if (read_nbytes(fd, buf->end, sz) != 0) return -1; buf->end += sz; return 0; } void neverbleed_iobuf_dispose(neverbleed_iobuf_t *buf) { iobuf_dispose(buf); } static void iobuf_transaction_write(neverbleed_iobuf_t *buf, struct st_neverbleed_thread_data_t *thdata) { if (iobuf_write(buf, thdata->fd) == -1) { if (errno != 0) { dief("write error (%d) %s", errno, strerror(errno)); } else { dief("connection closed by daemon"); } } } static void iobuf_transaction_read(neverbleed_iobuf_t *buf, struct st_neverbleed_thread_data_t *thdata) { iobuf_dispose(buf); if (iobuf_read(buf, thdata->fd) == -1) { if (errno != 0) { dief("read error (%d) %s", errno, strerror(errno)); } else { dief("connection closed by daemon"); } } } /** * Only sends a request, does not read a response */ static void iobuf_transaction_no_response(neverbleed_iobuf_t *buf, struct st_neverbleed_thread_data_t *thdata) { if (neverbleed_transaction_cb != NULL) { neverbleed_transaction_cb(buf, 1); } else { iobuf_transaction_write(buf, thdata); iobuf_dispose(buf); } } /** * Sends a request and reads a response. */ static void iobuf_transaction(neverbleed_iobuf_t *buf, struct st_neverbleed_thread_data_t *thdata) { if (neverbleed_transaction_cb != NULL) { neverbleed_transaction_cb(buf, 0); } else { iobuf_transaction_write(buf, thdata); iobuf_transaction_read(buf, thdata); } } #if !defined(NAME_MAX) || defined(__linux__) /* readdir(3) is known to be thread-safe on Linux and should be thread-safe on a platform that does not have a predefined value for NAME_MAX */ #define FOREACH_DIRENT(dp, dent) \ struct dirent *dent; \ while ((dent = readdir(dp)) != NULL) #else #define FOREACH_DIRENT(dp, dent) \ struct { \ struct dirent d; \ char s[NAME_MAX + 1]; \ } dent_; \ struct dirent *dentp, *dent = &dent_.d; \ int ret; \ while ((ret = readdir_r(dp, dent, &dentp)) == 0 && dentp != NULL) #endif /* FOREACH_DIRENT */ static void unlink_dir(const char *path) { DIR *dp; char buf[PATH_MAX]; if ((dp = opendir(path)) != NULL) { FOREACH_DIRENT(dp, entp) { if (strcmp(entp->d_name, ".") == 0 || strcmp(entp->d_name, "..") == 0) continue; snprintf(buf, sizeof(buf), "%s/%s", path, entp->d_name); unlink_dir(buf); } closedir(dp); } unlink(path); rmdir(path); } static void dispose_thread_data(void *_thdata) { struct st_neverbleed_thread_data_t *thdata = _thdata; assert(thdata->fd >= 0); close(thdata->fd); thdata->fd = -1; free(thdata); } static struct st_neverbleed_thread_data_t *get_thread_data(neverbleed_t *nb) { struct st_neverbleed_thread_data_t *thdata; pid_t self_pid = getpid(); ssize_t r; if ((thdata = pthread_getspecific(nb->thread_key)) != NULL) { if (thdata->self_pid == self_pid) return thdata; /* we have been forked! */ close(thdata->fd); } else { if ((thdata = malloc(sizeof(*thdata))) == NULL) dief("malloc failed"); } thdata->self_pid = self_pid; #ifdef SOCK_CLOEXEC if ((thdata->fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1) dief("socket(2) failed"); #else if ((thdata->fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) dief("socket(2) failed"); set_cloexec(thdata->fd); #endif while (connect(thdata->fd, (void *)&nb->sun_, sizeof(nb->sun_)) != 0) if (errno != EINTR) dief("failed to connect to privsep daemon"); while ((r = write(thdata->fd, nb->auth_token, sizeof(nb->auth_token))) == -1 && errno == EINTR) ; if (r != sizeof(nb->auth_token)) dief("failed to send authentication token"); pthread_setspecific(nb->thread_key, thdata); return thdata; } int neverbleed_get_fd(neverbleed_t *nb) { struct st_neverbleed_thread_data_t *thdata = get_thread_data(nb); return thdata->fd; } void neverbleed_transaction_read(neverbleed_t *nb, neverbleed_iobuf_t *buf) { struct st_neverbleed_thread_data_t *thdata = get_thread_data(nb); iobuf_transaction_read(buf, thdata); } void neverbleed_transaction_write(neverbleed_t *nb, neverbleed_iobuf_t *buf) { struct st_neverbleed_thread_data_t *thdata = get_thread_data(nb); iobuf_transaction_write(buf, thdata); } static void do_exdata_free_callback(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp) { /* when other engines are used, this callback gets called without neverbleed data */ if (ptr == NULL) return; struct st_neverbleed_rsa_exdata_t *exdata = ptr; struct st_neverbleed_thread_data_t *thdata = get_thread_data(exdata->nb); neverbleed_iobuf_t buf = {NULL}; iobuf_push_str(&buf, "del_pkey"); iobuf_push_num(&buf, exdata->key_index); // "del_pkey" command is fire-and-forget, it cannot fail, so doesn't have a response iobuf_transaction_no_response(&buf, thdata); free(exdata); } static int get_rsa_exdata_idx(void); static void rsa_exdata_free_callback(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp) { assert(idx == get_rsa_exdata_idx()); do_exdata_free_callback(parent, ptr, ad, idx, argl, argp); } static int get_rsa_exdata_idx(void) { static volatile int index; NEVERBLEED_MULTITHREAD_ONCE({ index = RSA_get_ex_new_index(0, NULL, NULL, NULL, rsa_exdata_free_callback); }); return index; } static void get_privsep_data(const RSA *rsa, struct st_neverbleed_rsa_exdata_t **exdata, struct st_neverbleed_thread_data_t **thdata) { *exdata = RSA_get_ex_data(rsa, get_rsa_exdata_idx()); if (*exdata == NULL) { errno = 0; dief("invalid internal ref"); } *thdata = get_thread_data((*exdata)->nb); } static struct { struct { pthread_mutex_t lock; /** * if the slot is use contains a non-NULL key; if not in use, contains the index of the next empty slot or SIZE_MAX if there * are no more empty slots */ union { EVP_PKEY *pkey; size_t next_empty; } *slots; size_t num_slots; size_t first_empty; } keys; neverbleed_t *nb; } daemon_vars = {{.lock = PTHREAD_MUTEX_INITIALIZER, .first_empty = SIZE_MAX}}; static __thread struct { int sockfd; #ifdef __linux int epollfd; #endif struct { neverbleed_iobuf_t *first, **next; } responses; } conn_ctx; static int use_offload = 0; #if USE_OFFLOAD struct engine_request { neverbleed_iobuf_t *buf; int async_fd; #ifdef OPENSSL_IS_BORINGSSL struct { RSA *rsa; uint8_t output[512]; union { struct { uint8_t padded[512]; } digestsign; }; } data; async_ctx *async_ctx; #else int (*stub)(neverbleed_iobuf_t *); struct { ASYNC_WAIT_CTX *ctx; ASYNC_JOB *job; } async; #endif }; static void offload_free_request(struct engine_request *req) { #ifdef OPENSSL_IS_BORINGSSL bssl_qat_async_finish_job(req->async_ctx); RSA_free(req->data.rsa); #else ASYNC_WAIT_CTX_free(req->async.ctx); #endif OPENSSL_cleanse(req, sizeof(*req)); free(req); } static int do_epoll_ctl(int epollfd, int op, int fd, struct epoll_event *event) { int ret; while ((ret = epoll_ctl(epollfd, op, fd, event) != 0) && errno == EINTR) ; return ret; } static void register_wait_fd(struct engine_request *req) { #ifdef OPENSSL_IS_BORINGSSL ASYNC_WAIT_CTX *ctx = req->async_ctx->currjob->waitctx; #else ASYNC_WAIT_CTX *ctx = req->async.ctx; #endif size_t numfds; if (!ASYNC_WAIT_CTX_get_all_fds(ctx, NULL, &numfds) || numfds != 1) dief("unexpected number of fds (%zu) requested in async mode\n", numfds); if (!ASYNC_WAIT_CTX_get_all_fds(ctx, &req->async_fd, &numfds)) dief("ASYNC_WAIT_CTX_get_all_fds failed\n"); struct epoll_event ev = {.events = EPOLLIN, .data.ptr = req}; if (do_epoll_ctl(conn_ctx.epollfd, EPOLL_CTL_ADD, req->async_fd, &ev) != 0) dief("epoll_ctl failed:%d\n", errno); } #endif static int send_responses(int cleanup) { neverbleed_iobuf_t *buf; int result = 0; /* Send all buffers that have data being filled. The lock is held until everything is being done, as this function can be called * from multiple threads simultaneously. */ while ((buf = conn_ctx.responses.first) != NULL && !buf->processing) { if ((conn_ctx.responses.first = buf->next) == NULL) conn_ctx.responses.next = &conn_ctx.responses.first; if (!cleanup && iobuf_write(buf, conn_ctx.sockfd) != 0) { warnf(errno != 0 ? "write error" : "connection closed by client"); result = -1; } iobuf_dispose(buf); free(buf); if (result != 0) break; } return result; } static RSA *daemon_get_rsa(size_t key_index) { RSA *rsa = NULL; pthread_mutex_lock(&daemon_vars.keys.lock); if (key_index < daemon_vars.keys.num_slots) rsa = EVP_PKEY_get1_RSA(daemon_vars.keys.slots[key_index].pkey); pthread_mutex_unlock(&daemon_vars.keys.lock); return rsa; } size_t allocate_slot(void) { /* expand if all slots are in use */ if (daemon_vars.keys.first_empty == SIZE_MAX) { size_t new_capacity = (daemon_vars.keys.num_slots < 4 ? 4 : daemon_vars.keys.num_slots) * 2; if ((daemon_vars.keys.slots = realloc(daemon_vars.keys.slots, sizeof(daemon_vars.keys.slots[0]) * new_capacity)) == NULL) dief("no memory"); daemon_vars.keys.first_empty = daemon_vars.keys.num_slots; for (size_t i = daemon_vars.keys.num_slots; i < new_capacity - 1; ++i) daemon_vars.keys.slots[i].next_empty = i + 1; daemon_vars.keys.slots[new_capacity - 1].next_empty = SIZE_MAX; daemon_vars.keys.num_slots = new_capacity; } /* detach the first empty slot from the empty list */ size_t slot_index = daemon_vars.keys.first_empty; daemon_vars.keys.first_empty = daemon_vars.keys.slots[slot_index].next_empty; /* set bogus value in the allocated slot to help figure out what happened upon crash */ daemon_vars.keys.slots[slot_index].next_empty = SIZE_MAX - 1; return slot_index; } static size_t daemon_set_pkey(EVP_PKEY *pkey) { assert(pkey != NULL); pthread_mutex_lock(&daemon_vars.keys.lock); size_t index = allocate_slot(); daemon_vars.keys.slots[index].pkey = pkey; EVP_PKEY_up_ref(pkey); pthread_mutex_unlock(&daemon_vars.keys.lock); return index; } static int priv_encdec_proxy(const char *cmd, int flen, const unsigned char *from, unsigned char *_to, RSA *rsa, int padding) { struct st_neverbleed_rsa_exdata_t *exdata; struct st_neverbleed_thread_data_t *thdata; neverbleed_iobuf_t buf = {NULL}; size_t ret; unsigned char *to; size_t tolen; get_privsep_data(rsa, &exdata, &thdata); iobuf_push_str(&buf, cmd); iobuf_push_bytes(&buf, from, flen); iobuf_push_num(&buf, exdata->key_index); iobuf_push_num(&buf, padding); iobuf_transaction(&buf, thdata); if (iobuf_shift_num(&buf, &ret) != 0 || (to = iobuf_shift_bytes(&buf, &tolen)) == NULL) { errno = 0; dief("failed to parse response"); } memcpy(_to, to, tolen); iobuf_dispose(&buf); return (int)ret; } static int priv_encdec_stub(const char *name, int (*func)(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding), neverbleed_iobuf_t *buf) { unsigned char *from, to[4096]; size_t flen; size_t key_index, padding; RSA *rsa; int ret; if ((from = iobuf_shift_bytes(buf, &flen)) == NULL || iobuf_shift_num(buf, &key_index) != 0 || iobuf_shift_num(buf, &padding) != 0) { errno = 0; warnf("%s: failed to parse request", name); return -1; } if ((rsa = daemon_get_rsa(key_index)) == NULL) { errno = 0; warnf("%s: invalid key index:%zu\n", name, key_index); return -1; } ret = func((int)flen, from, to, rsa, (int)padding); iobuf_dispose(buf); RSA_free(rsa); iobuf_push_num(buf, ret); iobuf_push_bytes(buf, to, ret > 0 ? ret : 0); return 0; } #if !defined(OPENSSL_IS_BORINGSSL) static int priv_enc_proxy(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) { return priv_encdec_proxy("priv_enc", flen, from, to, rsa, padding); } static int priv_enc_stub(neverbleed_iobuf_t *buf) { return priv_encdec_stub(__FUNCTION__, RSA_private_encrypt, buf); } static int priv_dec_proxy(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) { return priv_encdec_proxy("priv_dec", flen, from, to, rsa, padding); } static int priv_dec_stub(neverbleed_iobuf_t *buf) { return priv_encdec_stub(__FUNCTION__, RSA_private_decrypt, buf); } static int sign_proxy(int type, const unsigned char *m, unsigned int m_len, unsigned char *_sigret, unsigned *_siglen, const RSA *rsa) { struct st_neverbleed_rsa_exdata_t *exdata; struct st_neverbleed_thread_data_t *thdata; neverbleed_iobuf_t buf = {NULL}; size_t ret, siglen; unsigned char *sigret; get_privsep_data(rsa, &exdata, &thdata); iobuf_push_str(&buf, "sign"); iobuf_push_num(&buf, type); iobuf_push_bytes(&buf, m, m_len); iobuf_push_num(&buf, exdata->key_index); iobuf_transaction(&buf, thdata); if (iobuf_shift_num(&buf, &ret) != 0 || (sigret = iobuf_shift_bytes(&buf, &siglen)) == NULL) { errno = 0; dief("failed to parse response"); } memcpy(_sigret, sigret, siglen); *_siglen = (unsigned)siglen; iobuf_dispose(&buf); return (int)ret; } static int sign_stub(neverbleed_iobuf_t *buf) { unsigned char *m, sigret[4096]; size_t type, m_len, key_index; RSA *rsa; unsigned siglen = 0; int ret; if (iobuf_shift_num(buf, &type) != 0 || (m = iobuf_shift_bytes(buf, &m_len)) == NULL || iobuf_shift_num(buf, &key_index) != 0) { errno = 0; warnf("%s: failed to parse request", __FUNCTION__); return -1; } if ((rsa = daemon_get_rsa(key_index)) == NULL) { errno = 0; warnf("%s: invalid key index:%zu", __FUNCTION__, key_index); return -1; } ret = RSA_sign((int)type, m, (unsigned)m_len, sigret, &siglen, rsa); iobuf_dispose(buf); RSA_free(rsa); iobuf_push_num(buf, ret); iobuf_push_bytes(buf, sigret, ret == 1 ? siglen : 0); return 0; } #endif static EVP_PKEY *create_pkey(neverbleed_t *nb, size_t key_index, const char *ebuf, const char *nbuf) { struct st_neverbleed_rsa_exdata_t *exdata; RSA *rsa; EVP_PKEY *pkey; BIGNUM *e = NULL, *n = NULL; if ((exdata = malloc(sizeof(*exdata))) == NULL) { fprintf(stderr, "no memory\n"); abort(); } exdata->nb = nb; exdata->key_index = key_index; rsa = RSA_new_method(nb->engine); RSA_set_ex_data(rsa, get_rsa_exdata_idx(), exdata); if (BN_hex2bn(&e, ebuf) == 0) { fprintf(stderr, "failed to parse e:%s\n", ebuf); abort(); } if (BN_hex2bn(&n, nbuf) == 0) { fprintf(stderr, "failed to parse n:%s\n", nbuf); abort(); } RSA_set0_key(rsa, n, e, NULL); #if !defined(OPENSSL_IS_BORINGSSL) RSA_set_flags(rsa, RSA_FLAG_EXT_PKEY); #endif pkey = EVP_PKEY_new(); EVP_PKEY_set1_RSA(pkey, rsa); RSA_free(rsa); return pkey; } #ifdef NEVERBLEED_ECDSA static EC_KEY *daemon_get_ecdsa(size_t key_index) { EC_KEY *ec_key = NULL; pthread_mutex_lock(&daemon_vars.keys.lock); if (key_index < daemon_vars.keys.num_slots) ec_key = EVP_PKEY_get1_EC_KEY(daemon_vars.keys.slots[key_index].pkey); pthread_mutex_unlock(&daemon_vars.keys.lock); return ec_key; } static int ecdsa_sign_stub(neverbleed_iobuf_t *buf) { unsigned char *m, sigret[4096]; size_t type, m_len, key_index; EC_KEY *ec_key; unsigned siglen = 0; int ret; if (iobuf_shift_num(buf, &type) != 0 || (m = iobuf_shift_bytes(buf, &m_len)) == NULL || iobuf_shift_num(buf, &key_index) != 0) { errno = 0; warnf("%s: failed to parse request", __FUNCTION__); return -1; } if ((ec_key = daemon_get_ecdsa(key_index)) == NULL) { errno = 0; warnf("%s: invalid key index:%zu", __FUNCTION__, key_index); return -1; } ret = ECDSA_sign((int)type, m, (unsigned)m_len, sigret, &siglen, ec_key); iobuf_dispose(buf); EC_KEY_free(ec_key); iobuf_push_num(buf, ret); iobuf_push_bytes(buf, sigret, ret == 1 ? siglen : 0); return 0; } static int get_ecdsa_exdata_idx(void); static void ecdsa_exdata_free_callback(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp) { assert(idx == get_ecdsa_exdata_idx()); do_exdata_free_callback(parent, ptr, ad, idx, argl, argp); } static int get_ecdsa_exdata_idx(void) { static volatile int index; NEVERBLEED_MULTITHREAD_ONCE({ index = EC_KEY_get_ex_new_index(0, NULL, NULL, NULL, ecdsa_exdata_free_callback); }); return index; } static void ecdsa_get_privsep_data(const EC_KEY *ec_key, struct st_neverbleed_rsa_exdata_t **exdata, struct st_neverbleed_thread_data_t **thdata) { *exdata = EC_KEY_get_ex_data(ec_key, get_ecdsa_exdata_idx()); if (*exdata == NULL) { errno = 0; dief("invalid internal ref"); } *thdata = get_thread_data((*exdata)->nb); } static int ecdsa_sign_proxy(int type, const unsigned char *m, int m_len, unsigned char *_sigret, unsigned int *_siglen, const BIGNUM *kinv, const BIGNUM *rp, EC_KEY *ec_key) { struct st_neverbleed_rsa_exdata_t *exdata; struct st_neverbleed_thread_data_t *thdata; neverbleed_iobuf_t buf = {NULL}; size_t ret, siglen; unsigned char *sigret; ecdsa_get_privsep_data(ec_key, &exdata, &thdata); /* as far as I've tested so far, kinv and rp are always NULL. Looks like setup_sign will precompute this, but it is only called sign_sig, and it seems to be not used in TLS ECDSA */ if (kinv != NULL || rp != NULL) { errno = 0; dief("unexpected non-NULL kinv and rp"); } iobuf_push_str(&buf, "ecdsa_sign"); iobuf_push_num(&buf, type); iobuf_push_bytes(&buf, m, m_len); iobuf_push_num(&buf, exdata->key_index); iobuf_transaction(&buf, thdata); if (iobuf_shift_num(&buf, &ret) != 0 || (sigret = iobuf_shift_bytes(&buf, &siglen)) == NULL) { errno = 0; dief("failed to parse response"); } memcpy(_sigret, sigret, siglen); *_siglen = (unsigned)siglen; iobuf_dispose(&buf); return (int)ret; } static EVP_PKEY *ecdsa_create_pkey(neverbleed_t *nb, size_t key_index, int curve_name, const void *pubkey, size_t pubkey_len) { struct st_neverbleed_rsa_exdata_t *exdata; EC_KEY *ec_key; EC_GROUP *ec_group; EC_POINT *ec_pubkey; EVP_PKEY *pkey; if ((exdata = malloc(sizeof(*exdata))) == NULL) { fprintf(stderr, "no memory\n"); abort(); } exdata->nb = nb; exdata->key_index = key_index; ec_key = EC_KEY_new_method(nb->engine); EC_KEY_set_ex_data(ec_key, get_ecdsa_exdata_idx(), exdata); ec_group = EC_GROUP_new_by_curve_name(curve_name); if (!ec_group) { fprintf(stderr, "could not create EC_GROUP\n"); abort(); } EC_KEY_set_group(ec_key, ec_group); ec_pubkey = EC_POINT_new(ec_group); assert(ec_pubkey != NULL); if (!EC_POINT_oct2point(ec_group, ec_pubkey, pubkey, pubkey_len, NULL)) { fprintf(stderr, "failed to get ECDSA ephemeral public key from BIGNUM\n"); abort(); } EC_KEY_set_public_key(ec_key, ec_pubkey); pkey = EVP_PKEY_new(); EVP_PKEY_set1_EC_KEY(pkey, ec_key); EC_POINT_free(ec_pubkey); EC_GROUP_free(ec_group); EC_KEY_free(ec_key); return pkey; } #endif static EVP_PKEY *daemon_get_pkey(size_t key_index) { EVP_PKEY *pkey = NULL; pthread_mutex_lock(&daemon_vars.keys.lock); if (key_index < daemon_vars.keys.num_slots) { pkey = daemon_vars.keys.slots[key_index].pkey; EVP_PKEY_up_ref(pkey); } pthread_mutex_unlock(&daemon_vars.keys.lock); return pkey; } #if USE_OFFLOAD && defined(OPENSSL_IS_BORINGSSL) static struct engine_request *bssl_offload_create_request(neverbleed_iobuf_t *buf, EVP_PKEY *pkey) { RSA *_rsa = EVP_PKEY_get1_RSA(pkey); struct engine_request *req = malloc(sizeof(*req)); if (req == NULL) dief("no memory\n"); *req = (struct engine_request){.buf = buf, .async_fd = -1, .async_ctx = bssl_qat_async_start_job(), .data.rsa = _rsa}; if (req->async_ctx == NULL) dief("failed to initialize async job\n"); if (RSA_size(req->data.rsa) > sizeof(req->data.output)) dief("RSA key too large\n"); return req; } static void bssl_offload_digestsign(neverbleed_iobuf_t *buf, EVP_PKEY *pkey, const EVP_MD *md, const void *signdata, size_t signlen, int rsa_pss) { uint8_t digest[EVP_MAX_MD_SIZE]; unsigned digestlen; { /* generate digest of signdata */ EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); if (mdctx == NULL) dief("no memory\n"); if (!EVP_DigestInit_ex(mdctx, md, NULL) || !EVP_DigestUpdate(mdctx, signdata, signlen) || !EVP_DigestFinal_ex(mdctx, digest, &digestlen)) dief("digest calculation failed\n"); EVP_MD_CTX_free(mdctx); } struct engine_request *req = bssl_offload_create_request(buf, pkey); size_t rsa_size = RSA_size(req->data.rsa), padded_len; int padding; /* generate padded octets to be signed */ if (rsa_pss) { if (!RSA_padding_add_PKCS1_PSS_mgf1(req->data.rsa, req->data.digestsign.padded, digest, md, md, -1)) dief("RSA_paddding_add_PKCS1_PSS_mgf1 failed\n"); padded_len = rsa_size; padding = RSA_NO_PADDING; } else { /* PKCS1 padding */ int hash_nid = EVP_MD_type(md), is_alloced; uint8_t *tbs; if (!RSA_add_pkcs1_prefix(&tbs, &padded_len, &is_alloced, hash_nid, digest, digestlen)) dief("RSA_add_pkcs1_prefix failed\n"); if (padded_len > rsa_size) dief("output of RSA_add_pkcs1_prefix is unexpectedly large\n"); memcpy(req->data.digestsign.padded, tbs, padded_len); if (is_alloced) OPENSSL_free(tbs); padding = RSA_PKCS1_PADDING; } OPENSSL_cleanse(digest, sizeof(digest)); /* dispatch RSA calculation */ RSA_METHOD *meth = bssl_engine_get_rsa_method(); if (meth == NULL) dief("failed to obtain QAT RSA method table\n"); size_t siglen; if (!meth->sign_raw(req->data.rsa, &siglen, req->data.output, rsa_size, req->data.digestsign.padded, padded_len, padding)) dief("sign_raw failure\n"); if (siglen != 0) dief("sign_raw completed synchronously unexpectedly\n"); buf->processing = 1; register_wait_fd(req); } static int bssl_offload_decrypt(neverbleed_iobuf_t *buf, EVP_PKEY *pkey, const void *src, size_t len) { struct engine_request *req = bssl_offload_create_request(buf, pkey); /* dispatch RSA calculation */ RSA_METHOD *meth = bssl_engine_get_rsa_method(); if (meth == NULL) dief("failed to obtain QAT RSA method table\n"); size_t outlen; if (!meth->decrypt(req->data.rsa, &outlen, req->data.output, sizeof(req->data.output), src, len, RSA_NO_PADDING)) { warnf("RSA decrypt failure\n"); goto Exit; } if (outlen != 0) dief("RSA decrypt completed synchronously unexpectedly\n"); buf->processing = 1; register_wait_fd(req); return 1; Exit: offload_free_request(req); return 0; } #endif static int digestsign_stub(neverbleed_iobuf_t *buf) { size_t key_index, md_nid, signlen; void *signdata; size_t rsa_pss; EVP_PKEY *pkey; const EVP_MD *md; /* parse input */ if (iobuf_shift_num(buf, &key_index) != 0 || iobuf_shift_num(buf, &md_nid) != 0 || (signdata = iobuf_shift_bytes(buf, &signlen)) == NULL || iobuf_shift_num(buf, &rsa_pss) != 0) { errno = 0; warnf("%s: failed to parse request", __FUNCTION__); return -1; } if ((pkey = daemon_get_pkey(key_index)) == NULL) { errno = 0; warnf("%s: invalid key index:%zu", __FUNCTION__, key_index); return -1; } if (md_nid != SIZE_MAX) { if ((md = EVP_get_digestbynid((int)md_nid)) == NULL) { errno = 0; warnf("%s: invalid EVP_MD nid", __FUNCTION__); return -1; } } else { md = NULL; } #if USE_OFFLOAD && defined(OPENSSL_IS_BORINGSSL) if (use_offload && EVP_PKEY_id(pkey) == EVP_PKEY_RSA) { bssl_offload_digestsign(buf, pkey, md, signdata, signlen, rsa_pss); goto Exit; } #endif /* generate signature */ EVP_MD_CTX *mdctx = NULL; EVP_PKEY_CTX *pkey_ctx = NULL; unsigned char digestbuf[4096]; size_t digestlen; if ((mdctx = EVP_MD_CTX_create()) == NULL) goto Softfail; if (EVP_DigestSignInit(mdctx, &pkey_ctx, md, NULL, pkey) != 1) goto Softfail; if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA && rsa_pss) { if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1 || EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1) != 1) goto Softfail; if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, md) != 1) goto Softfail; } /* ED25519 keys can never be loaded, so use the Update -> Final call chain without worrying about backward compatibility */ if (EVP_DigestSignUpdate(mdctx, signdata, signlen) != 1) goto Softfail; if (EVP_DigestSignFinal(mdctx, NULL, &digestlen) != 1) goto Softfail; if (sizeof(digestbuf) < digestlen) { warnf("%s: digest unexpectedly long as %zu bytes", __FUNCTION__, digestlen); goto Softfail; } if (EVP_DigestSignFinal(mdctx, digestbuf, &digestlen) != 1) goto Softfail; Respond: /* build response */ iobuf_dispose(buf); iobuf_push_bytes(buf, digestbuf, digestlen); if (mdctx != NULL) EVP_MD_CTX_destroy(mdctx); Exit: if (pkey != NULL) EVP_PKEY_free(pkey); return 0; Softfail: digestlen = 0; goto Respond; } void neverbleed_start_digestsign(neverbleed_iobuf_t *buf, EVP_PKEY *pkey, const EVP_MD *md, const void *input, size_t len, int rsa_pss) { struct st_neverbleed_rsa_exdata_t *exdata; struct st_neverbleed_thread_data_t *thdata; const char *cmd = "digestsign"; /* obtain reference */ switch (EVP_PKEY_base_id(pkey)) { case EVP_PKEY_RSA: { RSA *rsa = EVP_PKEY_get1_RSA(pkey); /* get0 is available not available in OpenSSL 1.0.2 */ get_privsep_data(rsa, &exdata, &thdata); RSA_free(rsa); cmd = "digestsign-rsa"; } break; #ifdef NEVERBLEED_ECDSA case EVP_PKEY_EC: ecdsa_get_privsep_data(EVP_PKEY_get0_EC_KEY(pkey), &exdata, &thdata); break; #endif default: dief("unexpected private key"); break; } *buf = (neverbleed_iobuf_t){NULL}; iobuf_push_str(buf, cmd); iobuf_push_num(buf, exdata->key_index); iobuf_push_num(buf, md != NULL ? (size_t)EVP_MD_nid(md) : SIZE_MAX); iobuf_push_bytes(buf, input, len); iobuf_push_num(buf, rsa_pss); } void neverbleed_finish_digestsign(neverbleed_iobuf_t *buf, void **digest, size_t *digest_len) { const void *src; if ((src = iobuf_shift_bytes(buf, digest_len)) == NULL) { errno = 0; dief("failed to parse response"); } if ((*digest = malloc(*digest_len)) == NULL) dief("no memory"); memcpy(*digest, src, *digest_len); iobuf_dispose(buf); } static int decrypt_stub(neverbleed_iobuf_t *buf) { size_t key_index, srclen; void *src; EVP_PKEY *pkey; RSA *rsa; uint8_t decryptbuf[1024]; int decryptlen; /* parse input */ if (iobuf_shift_num(buf, &key_index) != 0 || (src = iobuf_shift_bytes(buf, &srclen)) == NULL) { errno = 0; warnf("%s: failed to parse request", __FUNCTION__); return -1; } if ((pkey = daemon_get_pkey(key_index)) == NULL) { errno = 0; warnf("%s: invalid key index:%zu", __FUNCTION__, key_index); return -1; } rsa = EVP_PKEY_get1_RSA(pkey); /* get0 is available not available in OpenSSL 1.0.2 */ assert(rsa != NULL); assert(sizeof(decryptbuf) >= RSA_size(rsa)); #if USE_OFFLOAD && defined(OPENSSL_IS_BORINGSSL) if (use_offload) { if (!bssl_offload_decrypt(buf, pkey, src, srclen)) goto Softfail; goto Exit; } #endif if ((decryptlen = RSA_private_decrypt(srclen, src, decryptbuf, rsa, RSA_NO_PADDING)) == -1) { errno = 0; warnf("RSA decryption error"); goto Softfail; } Respond: iobuf_dispose(buf); iobuf_push_bytes(buf, decryptbuf, decryptlen); Exit: RSA_free(rsa); EVP_PKEY_free(pkey); return 0; Softfail: decryptlen = 0; goto Respond; } void neverbleed_start_decrypt(neverbleed_iobuf_t *buf, EVP_PKEY *pkey, const void *input, size_t len) { struct st_neverbleed_rsa_exdata_t *exdata; struct st_neverbleed_thread_data_t *thdata; { RSA *rsa = EVP_PKEY_get1_RSA(pkey); /* get0 is available not available in OpenSSL 1.0.2 */ assert(rsa != NULL); get_privsep_data(rsa, &exdata, &thdata); RSA_free(rsa); } *buf = (neverbleed_iobuf_t){NULL}; iobuf_push_str(buf, "decrypt"); iobuf_push_num(buf, exdata->key_index); iobuf_push_bytes(buf, input, len); } void neverbleed_finish_decrypt(neverbleed_iobuf_t *buf, void **digest, size_t *digest_len) { neverbleed_finish_digestsign(buf, digest, digest_len); } int neverbleed_load_private_key_file(neverbleed_t *nb, SSL_CTX *ctx, const char *fn, char *errbuf) { struct st_neverbleed_thread_data_t *thdata = get_thread_data(nb); neverbleed_iobuf_t buf = {NULL}; int ret = 1; size_t index, type; EVP_PKEY *pkey; iobuf_push_str(&buf, "load_key"); iobuf_push_str(&buf, fn); iobuf_transaction(&buf, thdata); if (iobuf_shift_num(&buf, &type) != 0 || iobuf_shift_num(&buf, &index) != 0) { errno = 0; dief("failed to parse response"); } switch (type) { case NEVERBLEED_TYPE_RSA: { char *estr, *nstr; if ((estr = iobuf_shift_str(&buf)) == NULL || (nstr = iobuf_shift_str(&buf)) == NULL) { errno = 0; dief("failed to parse response"); } pkey = create_pkey(nb, index, estr, nstr); break; } #ifdef NEVERBLEED_ECDSA case NEVERBLEED_TYPE_ECDSA: { size_t curve_name, pubkey_len; void *pubkey_bytes; if (iobuf_shift_num(&buf, &curve_name) != 0 || (pubkey_bytes = iobuf_shift_bytes(&buf, &pubkey_len)) == NULL) { errno = 0; dief("failed to parse response"); } pkey = ecdsa_create_pkey(nb, index, (int)curve_name, pubkey_bytes, pubkey_len); break; } #endif default: { char *errstr; if ((errstr = iobuf_shift_str(&buf)) == NULL) { errno = 0; dief("failed to parse response"); } snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "%s", errstr); return -1; } } iobuf_dispose(&buf); /* success */ if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1) { snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "SSL_CTX_use_PrivateKey failed"); ret = 0; } EVP_PKEY_free(pkey); return ret; } static int load_key_stub(neverbleed_iobuf_t *buf) { char *fn; FILE *fp = NULL; RSA *rsa = NULL; size_t key_index = SIZE_MAX; char *estr = NULL, *nstr = NULL, errbuf[NEVERBLEED_ERRBUF_SIZE] = ""; size_t type = NEVERBLEED_TYPE_ERROR; EVP_PKEY *pkey = NULL; #ifdef NEVERBLEED_ECDSA const EC_GROUP *ec_group; void *ec_pubkeybytes = NULL; size_t ec_pubkeylen; #endif if ((fn = iobuf_shift_str(buf)) == NULL) { warnf("%s: failed to parse request", __FUNCTION__); return -1; } if ((fp = fopen(fn, "rt")) == NULL) { strerror_r(errno, errbuf, sizeof(errbuf)); goto Respond; } if ((pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL)) == NULL) { snprintf(errbuf, sizeof(errbuf), "failed to parse the private key"); goto Respond; } switch (EVP_PKEY_base_id(pkey)) { case EVP_PKEY_RSA: { const BIGNUM *e, *n; rsa = EVP_PKEY_get1_RSA(pkey); type = NEVERBLEED_TYPE_RSA; RSA_get0_key(rsa, &n, &e, NULL); estr = BN_bn2hex(e); nstr = BN_bn2hex(n); break; } case EVP_PKEY_EC: { #ifdef NEVERBLEED_ECDSA const EC_POINT *ec_pubkey; EC_KEY *ec_key; ec_key = (EC_KEY *)EVP_PKEY_get0_EC_KEY(pkey); type = NEVERBLEED_TYPE_ECDSA; ec_group = EC_KEY_get0_group(ec_key); ec_pubkey = EC_KEY_get0_public_key(ec_key); ec_pubkeylen = EC_POINT_point2oct(ec_group, ec_pubkey, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); if (!(ec_pubkeylen > 0 && (ec_pubkeybytes = malloc(ec_pubkeylen)) != NULL && EC_POINT_point2oct(ec_group, ec_pubkey, POINT_CONVERSION_UNCOMPRESSED, ec_pubkeybytes, ec_pubkeylen, NULL) == ec_pubkeylen)) dief("failed to serialize EC public key"); break; #else snprintf(errbuf, sizeof(errbuf), "ECDSA support requires OpenSSL >= 1.1.0, LibreSSL >= 2.9.1, or BoringSSL"); goto Respond; #endif } default: snprintf(errbuf, sizeof(errbuf), "unsupported private key: %d", EVP_PKEY_base_id(pkey)); goto Respond; } /* store the key */ key_index = daemon_set_pkey(pkey); Respond: iobuf_dispose(buf); iobuf_push_num(buf, type); iobuf_push_num(buf, key_index); switch (type) { case NEVERBLEED_TYPE_RSA: iobuf_push_str(buf, estr != NULL ? estr : ""); iobuf_push_str(buf, nstr != NULL ? nstr : ""); break; #ifdef NEVERBLEED_ECDSA case NEVERBLEED_TYPE_ECDSA: iobuf_push_num(buf, EC_GROUP_get_curve_name(ec_group)); iobuf_push_bytes(buf, ec_pubkeybytes, ec_pubkeylen); break; #endif default: iobuf_push_str(buf, errbuf); } if (rsa != NULL) RSA_free(rsa); if (pkey != NULL) EVP_PKEY_free(pkey); if (estr != NULL) OPENSSL_free(estr); if (nstr != NULL) OPENSSL_free(nstr); #ifdef NEVERBLEED_ECDSA if (ec_pubkeybytes != NULL) free(ec_pubkeybytes); #endif if (fp != NULL) fclose(fp); return 0; } int neverbleed_setuidgid(neverbleed_t *nb, const char *user, int change_socket_ownership) { struct st_neverbleed_thread_data_t *thdata = get_thread_data(nb); neverbleed_iobuf_t buf = {NULL}; size_t ret; iobuf_push_str(&buf, "setuidgid"); iobuf_push_str(&buf, user); iobuf_push_num(&buf, change_socket_ownership); iobuf_transaction(&buf, thdata); if (iobuf_shift_num(&buf, &ret) != 0) { errno = 0; dief("failed to parse response"); } iobuf_dispose(&buf); return (int)ret; } static int setuidgid_stub(neverbleed_iobuf_t *buf) { const char *user; size_t change_socket_ownership; struct passwd pwbuf, *pw; char pwstrbuf[65536]; /* should be large enough */ int ret = -1; if ((user = iobuf_shift_str(buf)) == NULL || iobuf_shift_num(buf, &change_socket_ownership) != 0) { errno = 0; warnf("%s: failed to parse request", __FUNCTION__); return -1; } errno = 0; if (getpwnam_r(user, &pwbuf, pwstrbuf, sizeof(pwstrbuf), &pw) != 0) { warnf("%s: getpwnam_r failed", __FUNCTION__); goto Respond; } if (pw == NULL) { warnf("%s: failed to obtain information of user:%s", __FUNCTION__, user); goto Respond; } if (change_socket_ownership) { char *dir; if (chown(daemon_vars.nb->sun_.sun_path, pw->pw_uid, pw->pw_gid) != 0) dief("chown failed for:%s", daemon_vars.nb->sun_.sun_path); dir = dirname(daemon_vars.nb->sun_.sun_path); if (chown(dir, pw->pw_uid, pw->pw_gid) != 0) dief("chown failed for:%s", dir); free(dir); } /* setuidgid */ if (setgid(pw->pw_gid) != 0) { warnf("%s: setgid(%d) failed", __FUNCTION__, (int)pw->pw_gid); goto Respond; } if (initgroups(pw->pw_name, pw->pw_gid) != 0) { warnf("%s: initgroups(%s, %d) failed", __FUNCTION__, pw->pw_name, (int)pw->pw_gid); goto Respond; } if (setuid(pw->pw_uid) != 0) { warnf("%s: setuid(%d) failed\n", __FUNCTION__, (int)pw->pw_uid); goto Respond; } ret = 0; Respond: iobuf_dispose(buf); iobuf_push_num(buf, ret); return 0; } #if NEVERBLEED_HAS_PTHREAD_SETAFFINITY_NP int neverbleed_setaffinity(neverbleed_t *nb, NEVERBLEED_CPU_SET_T *cpuset) { struct st_neverbleed_thread_data_t *thdata = get_thread_data(nb); neverbleed_iobuf_t buf = {NULL}; size_t ret; iobuf_push_str(&buf, "setaffinity"); iobuf_push_bytes(&buf, cpuset, sizeof(*cpuset)); iobuf_transaction(&buf, thdata); if (iobuf_shift_num(&buf, &ret) != 0) { errno = 0; dief("failed to parse response"); } iobuf_dispose(&buf); return (int)ret; } static int setaffinity_stub(neverbleed_iobuf_t *buf) { char *cpuset_bytes; size_t cpuset_len; NEVERBLEED_CPU_SET_T cpuset; int ret = 1; if ((cpuset_bytes = iobuf_shift_bytes(buf, &cpuset_len)) == NULL) { errno = 0; warnf("%s: failed to parse request", __FUNCTION__); return -1; } assert(cpuset_len == sizeof(NEVERBLEED_CPU_SET_T)); memcpy(&cpuset, cpuset_bytes, cpuset_len); #ifdef __NetBSD__ ret = pthread_setaffinity_np(pthread_self(), cpuset_size(cpuset), cpuset); #else ret = pthread_setaffinity_np(pthread_self(), sizeof(NEVERBLEED_CPU_SET_T), &cpuset); #endif if (ret != 0) { ret = 1; goto Respond; } ret = 0; Respond: iobuf_dispose(buf); iobuf_push_num(buf, ret); return 0; } #endif __attribute__((noreturn)) static void *daemon_close_notify_thread(void *_close_notify_fd) { int close_notify_fd = (int)((char *)_close_notify_fd - (char *)NULL); char b; ssize_t r; Redo: r = read(close_notify_fd, &b, 1); if (r == -1 && errno == EINTR) goto Redo; if (r > 0) goto Redo; /* close or error */ /* unlink the temporary directory and socket file */ unlink_dir(dirname(daemon_vars.nb->sun_.sun_path)); _exit(0); } static int del_pkey_stub(neverbleed_iobuf_t *buf) { size_t key_index; if (iobuf_shift_num(buf, &key_index) != 0) { errno = 0; warnf("%s: failed to parse request", __FUNCTION__); return -1; } pthread_mutex_lock(&daemon_vars.keys.lock); /* set slot as available */ if (key_index < daemon_vars.keys.num_slots) { EVP_PKEY_free(daemon_vars.keys.slots[key_index].pkey); daemon_vars.keys.slots[key_index].next_empty = daemon_vars.keys.first_empty; daemon_vars.keys.first_empty = key_index; } else { warnf("%s: invalid key index %zu", __FUNCTION__, key_index); } pthread_mutex_unlock(&daemon_vars.keys.lock); return 0; } #define offload_start(stub, buf) ((stub)(buf)) #if USE_OFFLOAD #ifdef OPENSSL_IS_BORINGSSL static int offload_resume(struct engine_request *req) { size_t outlen; if (do_epoll_ctl(conn_ctx.epollfd, EPOLL_CTL_DEL, req->async_fd, NULL) != 0) dief("epoll_ctl failed:%d\n", errno); /* get result */ if (bssl_qat_async_ctx_copy_result(req->async_ctx, req->data.output, &outlen, sizeof(req->data.output)) != 0) dief("failed to obtain offload result\n"); if (outlen > sizeof(req->data.output)) dief("RSA output is unexpectedly large\n"); /* save the result */ iobuf_dispose(req->buf); iobuf_push_bytes(req->buf, req->data.output, outlen); req->buf->processing = 0; offload_free_request(req); return 0; } #else static int offload_jobfunc(void *_req) { struct engine_request *req = *(void **)_req; return req->stub(req->buf); } #undef offload_start static int offload_start(int (*stub)(neverbleed_iobuf_t *), neverbleed_iobuf_t *buf) { /* if engine is not used, run the stub synchronously */ if (!use_offload) return stub(buf); buf->processing = 1; struct engine_request *req = malloc(sizeof(*req)); if (req == NULL) dief("no memory"); *req = (struct engine_request){.buf = buf, .async_fd = -1, .stub = stub}; if ((req->async.ctx = ASYNC_WAIT_CTX_new()) == NULL) dief("failed to create ASYNC_WAIT_CTX\n"); int ret; switch (ASYNC_start_job(&req->async.job, req->async.ctx, &ret, offload_jobfunc, &req, sizeof(req))) { case ASYNC_PAUSE: /* operation running async; register fd and bail out */ register_wait_fd(req); return 0; case ASYNC_FINISH: /* completed synchronously */ buf->processing = 0; break; default: dief("ASYNC_start_job errored\n"); break; } offload_free_request(req); return ret; } static int offload_resume(struct engine_request *req) { int ret; switch (ASYNC_start_job(&req->async.job, req->async.ctx, &ret, offload_jobfunc, &req, sizeof(req))) { case ASYNC_PAUSE: /* assume that wait fd is unchanged */ return 0; case ASYNC_FINISH: if (do_epoll_ctl(conn_ctx.epollfd, EPOLL_CTL_DEL, req->async_fd, NULL) != 0) dief("epoll_ctl failed:%d\n", errno); break; default: dief("ASYNC_start_job failed\n"); break; } /* job done */ req->buf->processing = 0; offload_free_request(req); return ret; } #endif #endif /** * This function waits for the provided socket to become readable, then calls `nanosleep(1)` before returning. * The intention behind sleep is to provide the application to complete its event loop before the neverbleed process starts * spending CPU cycles on the time-consuming RSA operation. * In addition, when QAT is used, this function processes completion notifications from QAT and sends the responses. */ static int wait_for_data(int cleanup) { #if USE_OFFLOAD struct epoll_event events[20]; int has_read = 0, num_events; do { while ((num_events = epoll_wait(conn_ctx.epollfd, events, sizeof(events) / sizeof(events[0]), -1)) == -1 && (errno == EAGAIN || errno == EINTR)) ; if (num_events == -1) dief("epoll_wait(2):%d\n", errno); for (int i = 0; i < num_events; ++i) { if (events[i].data.ptr == NULL) { has_read = 1; } else { struct engine_request *req = events[i].data.ptr; int ret; if ((ret = offload_resume(req)) != 0) return ret; if ((ret = send_responses(0)) != 0) return ret; } } } while (!has_read); #else fd_set rfds; int ret; FD_ZERO(&rfds); if (!cleanup) FD_SET(conn_ctx.sockfd, &rfds); while ((ret = select(conn_ctx.sockfd + 1, &rfds, NULL, NULL, NULL)) == -1 && (errno == EAGAIN || errno == EINTR)) ; if (ret == -1) dief("select(2):%d\n", errno); #endif // yield when data is available struct timespec tv = {.tv_nsec = 1}; (void)nanosleep(&tv, NULL); return 0; } static void *daemon_conn_thread(void *_sock_fd) { conn_ctx.sockfd = (int)((char *)_sock_fd - (char *)NULL); conn_ctx.responses.next = &conn_ctx.responses.first; neverbleed_iobuf_t *buf = NULL; #if USE_OFFLOAD if ((conn_ctx.epollfd = epoll_create1(EPOLL_CLOEXEC)) == -1) dief("epoll_create1 failed:%d\n", errno); { struct epoll_event ev = {.events = EPOLLIN}; if (do_epoll_ctl(conn_ctx.epollfd, EPOLL_CTL_ADD, conn_ctx.sockfd, &ev) != 0) dief("epoll_ctl failed:%d\n", errno); } #endif { /* authenticate */ unsigned char auth_token[NEVERBLEED_AUTH_TOKEN_SIZE]; if (read_nbytes(conn_ctx.sockfd, &auth_token, sizeof(auth_token)) != 0) { warnf("failed to receive authencication token from client"); goto Exit; } if (memcmp(auth_token, daemon_vars.nb->auth_token, NEVERBLEED_AUTH_TOKEN_SIZE) != 0) { warnf("client authentication failed"); goto Exit; } } while (1) { if (wait_for_data(0) != 0) break; free(buf); buf = malloc(sizeof(*buf)); if (buf == NULL) dief("no memory"); *buf = (neverbleed_iobuf_t){}; char *cmd; if (iobuf_read(buf, conn_ctx.sockfd) != 0) { if (errno != 0) warnf("read error"); break; } if ((cmd = iobuf_shift_str(buf)) == NULL) { errno = 0; warnf("failed to parse request"); break; } #if !defined(OPENSSL_IS_BORINGSSL) if (strcmp(cmd, "priv_enc") == 0) { if (offload_start(priv_enc_stub, buf) != 0) break; } else if (strcmp(cmd, "priv_dec") == 0) { if (offload_start(priv_dec_stub, buf) != 0) break; } else if (strcmp(cmd, "sign") == 0) { if (offload_start(sign_stub, buf) != 0) break; #ifdef NEVERBLEED_ECDSA } else if (strcmp(cmd, "ecdsa_sign") == 0) { if (ecdsa_sign_stub(buf) != 0) break; #endif } else #endif if (strcmp(cmd, "digestsign") == 0) { if (digestsign_stub(buf) != 0) break; } else if (strcmp(cmd, "digestsign-rsa") == 0) { if (offload_start(digestsign_stub, buf) != 0) break; } else if (strcmp(cmd, "decrypt") == 0) { if (offload_start(decrypt_stub, buf) != 0) break; } else if (strcmp(cmd, "load_key") == 0) { if (load_key_stub(buf) != 0) break; } else if (strcmp(cmd, "del_pkey") == 0) { if (del_pkey_stub(buf) != 0) break; iobuf_dispose(buf); // "del_pkey" command is fire-and-forget, it cannot fail, so doesn't have a response continue; } else if (strcmp(cmd, "setuidgid") == 0) { if (setuidgid_stub(buf) != 0) break; #if NEVERBLEED_HAS_PTHREAD_SETAFFINITY_NP } else if (strcmp(cmd, "setaffinity") == 0) { if (setaffinity_stub(buf) != 0) break; #endif } else { warnf("unknown command:%s", cmd); break; } /* add response to chain */ *conn_ctx.responses.next = buf; conn_ctx.responses.next = &buf->next; buf = NULL; /* do not free */ /* send responses if possible */ if (send_responses(0) != 0) break; } Exit: free(buf); /* run the loop while async ops are running */ while (conn_ctx.responses.first != NULL) wait_for_data(1); close(conn_ctx.sockfd); #ifdef __linux close(conn_ctx.epollfd); #endif return NULL; } #if !(defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)) #define closefrom my_closefrom static void my_closefrom(int lowfd) { /* On linux, try close_range (2), then fall back to the slow loop if it fails. */ #if defined(__linux__) && defined(__NR_close_range) if (syscall(__NR_close_range, lowfd, ~0, 0) == 0) return; #endif for (int fd = (int)sysconf(_SC_OPEN_MAX) - 1; fd >= lowfd; --fd) (void)close(fd); } #endif static void cleanup_fds(int listen_fd, int close_notify_fd) { int maxfd, k; maxfd = 0; if (listen_fd > maxfd) { maxfd = listen_fd; } if (close_notify_fd > maxfd) { maxfd = close_notify_fd; } for (k = 0; k < maxfd; k++) { if (k == listen_fd || k == close_notify_fd) continue; switch (k) { case STDOUT_FILENO: case STDERR_FILENO: case STDIN_FILENO: break; default: (void)close(k); } } closefrom(maxfd + 1); } __attribute__((noreturn)) static void daemon_main(int listen_fd, int close_notify_fd, const char *tempdir) { pthread_t tid; pthread_attr_t thattr; int sock_fd; cleanup_fds(listen_fd, close_notify_fd); pthread_attr_init(&thattr); pthread_attr_setdetachstate(&thattr, 1); switch (neverbleed_offload) { case NEVERBLEED_OFFLOAD_QAT_ON: case NEVERBLEED_OFFLOAD_QAT_AUTO: { #if USE_OFFLOAD && defined(OPENSSL_IS_BORINGSSL) ENGINE_load_qat(); bssl_qat_set_default_string("RSA"); use_offload = ENGINE_QAT_PTR_GET() != NULL; #elif USE_OFFLOAD && !defined(OPENSSL_IS_BORINGSSL) ENGINE *qat = ENGINE_by_id("qatengine"); if (qat != NULL && ENGINE_init(qat)) { if (!ENGINE_set_default_RSA(qat)) dief("failed to assign RSA operations to QAT\n"); use_offload = 1; } #endif if (!use_offload && neverbleed_offload == NEVERBLEED_OFFLOAD_QAT_ON) dief("use of QAT is forced but unavailable\n"); } break; default: break; } if (pthread_create(&tid, &thattr, daemon_close_notify_thread, (char *)NULL + close_notify_fd) != 0) dief("pthread_create failed"); while (1) { while ((sock_fd = accept(listen_fd, NULL, NULL)) == -1) ; if (pthread_create(&tid, &thattr, daemon_conn_thread, (char *)NULL + sock_fd) != 0) dief("pthread_create failed"); } } #ifndef NEVERBLEED_OPAQUE_RSA_METHOD static RSA_METHOD static_rsa_method = { "privsep RSA method", /* name */ NULL, /* rsa_pub_enc */ NULL, /* rsa_pub_dec */ priv_enc_proxy, /* rsa_priv_enc */ priv_dec_proxy, /* rsa_priv_dec */ NULL, /* rsa_mod_exp */ NULL, /* bn_mod_exp */ NULL, /* init */ NULL, /* finish */ RSA_FLAG_SIGN_VER, /* flags */ NULL, /* app data */ sign_proxy, /* rsa_sign */ NULL, /* rsa_verify */ NULL /* rsa_keygen */ }; #endif int neverbleed_init(neverbleed_t *nb, char *errbuf) { int pipe_fds[2] = {-1, -1}, listen_fd = -1; char *tempdir = NULL; /* setup the daemon */ if (pipe(pipe_fds) != 0) { snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "pipe(2) failed:%s", strerror(errno)); goto Fail; } set_cloexec(pipe_fds[1]); if ((tempdir = strdup("/tmp/openssl-privsep.XXXXXX")) == NULL) { snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "no memory"); goto Fail; } if (mkdtemp(tempdir) == NULL) { snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "failed to create temporary directory under /tmp:%s", strerror(errno)); goto Fail; } memset(&nb->sun_, 0, sizeof(nb->sun_)); nb->sun_.sun_family = AF_UNIX; snprintf(nb->sun_.sun_path, sizeof(nb->sun_.sun_path), "%s/_", tempdir); RAND_bytes(nb->auth_token, sizeof(nb->auth_token)); if ((listen_fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) { snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "socket(2) failed:%s", strerror(errno)); goto Fail; } if (bind(listen_fd, (void *)&nb->sun_, sizeof(nb->sun_)) != 0) { snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "failed to bind to %s:%s", nb->sun_.sun_path, strerror(errno)); goto Fail; } if (listen(listen_fd, SOMAXCONN) != 0) { snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "listen(2) failed:%s", strerror(errno)); goto Fail; } nb->daemon_pid = fork(); switch (nb->daemon_pid) { case -1: snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "fork(2) failed:%s", strerror(errno)); goto Fail; case 0: close(pipe_fds[1]); #if defined(__linux__) prctl(PR_SET_DUMPABLE, 0, 0, 0, 0); #elif defined(__FreeBSD__) int dumpable = PROC_TRACE_CTL_DISABLE; procctl(P_PID, 0, PROC_TRACE_CTL, &dumpable); #elif defined(__sun) setpflags(__PROC_PROTECT, 1); #elif defined(__APPLE__) ptrace(PT_DENY_ATTACH, 0, 0, 0); #endif if (neverbleed_post_fork_cb != NULL) neverbleed_post_fork_cb(); daemon_vars.nb = nb; daemon_main(listen_fd, pipe_fds[0], tempdir); break; default: break; } close(listen_fd); listen_fd = -1; close(pipe_fds[0]); pipe_fds[0] = -1; #if defined(OPENSSL_IS_BORINGSSL) nb->engine = NULL; #else { /* setup engine */ const RSA_METHOD *rsa_default_method; RSA_METHOD *rsa_method; #ifdef NEVERBLEED_ECDSA const EC_KEY_METHOD *ecdsa_default_method; EC_KEY_METHOD *ecdsa_method; #endif #ifdef NEVERBLEED_OPAQUE_RSA_METHOD rsa_default_method = RSA_PKCS1_OpenSSL(); rsa_method = RSA_meth_dup(rsa_default_method); RSA_meth_set1_name(rsa_method, "privsep RSA method"); RSA_meth_set_priv_enc(rsa_method, priv_enc_proxy); RSA_meth_set_priv_dec(rsa_method, priv_dec_proxy); RSA_meth_set_sign(rsa_method, sign_proxy); #else rsa_default_method = RSA_PKCS1_SSLeay(); rsa_method = &static_rsa_method; rsa_method->rsa_pub_enc = rsa_default_method->rsa_pub_enc; rsa_method->rsa_pub_dec = rsa_default_method->rsa_pub_dec; rsa_method->rsa_verify = rsa_default_method->rsa_verify; rsa_method->bn_mod_exp = rsa_default_method->bn_mod_exp; #endif #ifdef NEVERBLEED_ECDSA ecdsa_default_method = EC_KEY_get_default_method(); ecdsa_method = EC_KEY_METHOD_new(ecdsa_default_method); /* it seems sign_sig and sign_setup is not used in TLS ECDSA. */ EC_KEY_METHOD_set_sign(ecdsa_method, ecdsa_sign_proxy, NULL, NULL); #endif if ((nb->engine = ENGINE_new()) == NULL || !ENGINE_set_id(nb->engine, "neverbleed") || !ENGINE_set_name(nb->engine, "privilege separation software engine") || !ENGINE_set_RSA(nb->engine, rsa_method) #ifdef NEVERBLEED_ECDSA || !ENGINE_set_EC(nb->engine, ecdsa_method) #endif ) { snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "failed to initialize the OpenSSL engine"); goto Fail; } ENGINE_add(nb->engine); } #endif /* setup thread key */ pthread_key_create(&nb->thread_key, dispose_thread_data); free(tempdir); return 0; Fail: if (pipe_fds[0] != -1) close(pipe_fds[0]); if (pipe_fds[1] != -1) close(pipe_fds[1]); if (tempdir != NULL) { unlink_dir(tempdir); free(tempdir); } if (listen_fd != -1) close(listen_fd); if (nb->engine != NULL) { ENGINE_free(nb->engine); nb->engine = NULL; } return -1; } void (*neverbleed_post_fork_cb)(void) = NULL; void (*neverbleed_transaction_cb)(neverbleed_iobuf_t *, int) = NULL; enum neverbleed_offload_type neverbleed_offload = NEVERBLEED_OFFLOAD_OFF; nghttp2-1.68.0/third-party/PaxHeaders/urlparse0000644000000000000000000000013215077107334016310 xustar0030 mtime=1761382108.222302919 30 atime=1761382109.795298372 30 ctime=1761382108.222302919 nghttp2-1.68.0/third-party/urlparse/0000755000175100017510000000000015077107334016755 5ustar00runnerrunnernghttp2-1.68.0/third-party/urlparse/PaxHeaders/urlparse.h0000644000000000000000000000013215077107276020400 xustar0030 mtime=1761382078.166420355 30 atime=1761382080.179411146 30 ctime=1761382108.222302919 nghttp2-1.68.0/third-party/urlparse/urlparse.h0000644000175100017510000001146115077107276020773 0ustar00runnerrunner/* * urlparse * * Copyright (c) 2024 urlparse contributors * Copyright (c) 2023 sfparse contributors * Copyright (c) 2019 nghttp3 contributors * Copyright (c) 2015 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef URLPARSE_H #define URLPARSE_H /* Define WIN32 when build target is Win32 API (borrowed from libcurl) */ #if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) # define WIN32 #endif /* (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) */ #ifdef __cplusplus extern "C" { #endif /* defined(__cplusplus) */ #if defined(_MSC_VER) && (_MSC_VER < 1800) /* MSVC < 2013 does not have inttypes.h because it is not C99 compliant. See compiler macros and version number in https://sourceforge.net/p/predef/wiki/Compilers/ */ # include #else /* !(defined(_MSC_VER) && (_MSC_VER < 1800)) */ # include #endif /* !(defined(_MSC_VER) && (_MSC_VER < 1800)) */ #include #include /** * @macro * * :macro:`URLPARSE_ERR_PARSE` indicates that an error occurred while * the parser is processing a URL. */ #define URLPARSE_ERR_PARSE -1 /** * @enum * * :type:`urlparse_url_fields` defines URL component fields. */ typedef enum urlparse_url_fields { /** * :enum:`URLPARSE_SCHEMA` is a URL scheme. */ URLPARSE_SCHEMA = 0, /** * :enum:`URLPARSE_HOST` is a host. */ URLPARSE_HOST = 1, /** * :enum:`URLPARSE_PORT` is a port. */ URLPARSE_PORT = 2, /** * :enum:`URLPARSE_PATH` is a path. */ URLPARSE_PATH = 3, /** * :enum:`URLPARSE_QUERY` is a query. */ URLPARSE_QUERY = 4, /** * :enum:`URLPARSE_FRAGMENT` is a fragment. */ URLPARSE_FRAGMENT = 5, /** * :enum:`URLPARSE_USERINFO` is a userinfo. */ URLPARSE_USERINFO = 6, /** * :enum:`URLPARSE_MAX` is the number of fields. */ URLPARSE_MAX = 7 } urlparse_url_fields; /** * @struct * * :type:`urlparse_url` is a struct to store the result of parsing a * URL. */ typedef struct urlparse_url { /** * :member:`field_set` is a bitmask of (1 << :type:`URLPARSE_* * `) values. */ uint16_t field_set; /** * :member:`port` is the integer representation of * :enum:`URLPARSE_PORT ` string. * It is assigned only when (:member:`field_set` & (1 << * :enum:`URLPARSE_PORT `)) is * nonzero. */ uint16_t port; /** * @anonstruct_start * * @struct_urlparse_field_data * * :member:`field_data` stores the position and its length of each * URL component if the corresponding bit is set in * :member:`field_set`. For example, * field_data[:enum:`URLPARSE_HOST * `] is assigned if * (:member:`field_set` & (1 << :enum:`URLPARSE_HOST * `)) is nonzero. */ struct { /** * :member:`off` is an offset into buffer in which field starts. */ uint16_t off; /** * :member:`len` is a length of run in buffer. */ uint16_t len; /** * @anonstruct_end */ } field_data[URLPARSE_MAX]; } urlparse_url; /** * @function * * `urlparse_parse_url` parses |url| of length |urllen| bytes, and * stores the result in |u|. If |is_connect| is nonzero, it parses * the URL as a request target that appears in CONNECT request, that * is, consisting of only the host and port number. * * This function initializes |u| before its use. If this function * returns nonzero, |u| might not be initialized. * * This function returns 0 if it succeeds, or * :macro:`URLPARSE_ERR_PARSE`. */ int urlparse_parse_url(const char *url, size_t urllen, int is_connect, urlparse_url *u); #ifdef __cplusplus } #endif /* defined(__cplusplus) */ #endif /* !defined(URLPARSE_H) */ nghttp2-1.68.0/third-party/urlparse/PaxHeaders/urlparse.c0000644000000000000000000000013215077107276020373 xustar0030 mtime=1761382078.166420355 30 atime=1761382080.178411151 30 ctime=1761382108.221302922 nghttp2-1.68.0/third-party/urlparse/urlparse.c0000644000175100017510000004226315077107276020772 0ustar00runnerrunner/* * urlparse * * Copyright (c) 2024 urlparse contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "urlparse.h" #include #include #include #define DIGIT_CASES \ case '0': \ case '1': \ case '2': \ case '3': \ case '4': \ case '5': \ case '6': \ case '7': \ case '8': \ case '9' #define LCALPHA_CASES \ case 'a': \ case 'b': \ case 'c': \ case 'd': \ case 'e': \ case 'f': \ case 'g': \ case 'h': \ case 'i': \ case 'j': \ case 'k': \ case 'l': \ case 'm': \ case 'n': \ case 'o': \ case 'p': \ case 'q': \ case 'r': \ case 's': \ case 't': \ case 'u': \ case 'v': \ case 'w': \ case 'x': \ case 'y': \ case 'z' #define UCALPHA_CASES \ case 'A': \ case 'B': \ case 'C': \ case 'D': \ case 'E': \ case 'F': \ case 'G': \ case 'H': \ case 'I': \ case 'J': \ case 'K': \ case 'L': \ case 'M': \ case 'N': \ case 'O': \ case 'P': \ case 'Q': \ case 'R': \ case 'S': \ case 'T': \ case 'U': \ case 'V': \ case 'W': \ case 'X': \ case 'Y': \ case 'Z' #define ALPHA_CASES \ UCALPHA_CASES: \ LCALPHA_CASES #define HEX_CASES \ DIGIT_CASES: \ case 'A': \ case 'B': \ case 'C': \ case 'D': \ case 'E': \ case 'F': \ case 'a': \ case 'b': \ case 'c': \ case 'd': \ case 'e': \ case 'f' typedef struct urlparse_parser { const char *begin; const char *pos; const char *end; } urlparse_parser; static int urlparse_parser_eof(urlparse_parser *up) { return up->pos == up->end; } static void urlparse_url_set_field_data(urlparse_url *dest, int field, const char *data, const char *start, const char *end) { dest->field_set |= (uint16_t)(1 << field); dest->field_data[field].off = (uint16_t)(start - data); dest->field_data[field].len = (uint16_t)(end - start); } static int parse_scheme(urlparse_parser *up, urlparse_url *dest) { const char *start; if (urlparse_parser_eof(up)) { return URLPARSE_ERR_PARSE; } switch (*up->pos) { ALPHA_CASES: break; case '/': case '*': return 0; default: return URLPARSE_ERR_PARSE; } start = up->pos; ++up->pos; for (; !urlparse_parser_eof(up); ++up->pos) { switch (*up->pos) { ALPHA_CASES: continue; case ':': goto fin; default: return URLPARSE_ERR_PARSE; } } return URLPARSE_ERR_PARSE; fin: urlparse_url_set_field_data(dest, URLPARSE_SCHEMA, up->begin, start, up->pos); return 0; } static int parse_path_abempty(urlparse_parser *up, urlparse_url *dest) { const char *start; if (urlparse_parser_eof(up)) { return 0; } switch (*up->pos) { case '?': return 0; case '/': break; case '*': if (dest->field_set & (1 << URLPARSE_HOST)) { return URLPARSE_ERR_PARSE; } break; default: return URLPARSE_ERR_PARSE; } start = up->pos++; for (; !urlparse_parser_eof(up); ++up->pos) { switch (*up->pos) { /* unreserved */ ALPHA_CASES: DIGIT_CASES: case '-': case '.': case '_': case '~': /* pct-encoded */ case '%': /* sub-delims */ case '!': case '$': case '&': case '\'': case '(': case ')': case '*': case '+': case ',': case ';': case '=': /* extra */ case ':': case '@': case '/': /* http-parser allows the following characters as well. */ case '"': case '<': case '>': case '[': case '\\': case ']': case '^': case '`': case '{': case '|': case '}': continue; case '?': case '#': goto fin; default: return URLPARSE_ERR_PARSE; } } fin: urlparse_url_set_field_data(dest, URLPARSE_PATH, up->begin, start, up->pos); return 0; } static int parse_userinfo(urlparse_parser *up, urlparse_url *dest) { const char *start; assert(!urlparse_parser_eof(up)); start = up->pos; for (; !urlparse_parser_eof(up); ++up->pos) { switch (*up->pos) { /* unreserved */ ALPHA_CASES: DIGIT_CASES: case '-': case '.': case '_': case '~': /* pct-encoded */ case '%': /* sub-delims */ case '!': case '$': case '&': case '\'': case '(': case ')': case '*': case '+': case ',': case ';': case '=': case ':': continue; case '@': goto fin; default: return URLPARSE_ERR_PARSE; } } return URLPARSE_ERR_PARSE; fin: if (start != up->pos) { urlparse_url_set_field_data(dest, URLPARSE_USERINFO, up->begin, start, up->pos); } ++up->pos; return 0; } static int parse_host(urlparse_parser *up, urlparse_url *dest) { const char *start; assert(!urlparse_parser_eof(up)); start = up->pos; for (; !urlparse_parser_eof(up); ++up->pos) { switch (*up->pos) { ALPHA_CASES: DIGIT_CASES: case '.': case '-': continue; case ':': case '/': case '?': break; default: return URLPARSE_ERR_PARSE; } break; } if (start == up->pos) { return URLPARSE_ERR_PARSE; } urlparse_url_set_field_data(dest, URLPARSE_HOST, up->begin, start, up->pos); return 0; } static int parse_ipv6_host(urlparse_parser *up, urlparse_url *dest) { const char *start, *zone_start; if (urlparse_parser_eof(up)) { return URLPARSE_ERR_PARSE; } start = up->pos; for (; !urlparse_parser_eof(up); ++up->pos) { switch (*up->pos) { HEX_CASES: case ':': case '.': continue; case '%': if (start == up->pos) { return URLPARSE_ERR_PARSE; } zone_start = up->pos; ++up->pos; if (urlparse_parser_eof(up)) { return URLPARSE_ERR_PARSE; } for (; !urlparse_parser_eof(up); ++up->pos) { switch (*up->pos) { ALPHA_CASES: DIGIT_CASES: case '%': case '.': case '-': case '_': case '~': continue; case ']': if (zone_start + 1 == up->pos) { return URLPARSE_ERR_PARSE; } goto fin; default: return URLPARSE_ERR_PARSE; } } return URLPARSE_ERR_PARSE; case ']': goto fin; default: return URLPARSE_ERR_PARSE; } } return URLPARSE_ERR_PARSE; fin: if (start == up->pos) { return URLPARSE_ERR_PARSE; } urlparse_url_set_field_data(dest, URLPARSE_HOST, up->begin, start, up->pos); ++up->pos; return 0; } static int parse_port(urlparse_parser *up, urlparse_url *dest) { const char *start; uint16_t port, d; if (urlparse_parser_eof(up)) { /* http_parser disallows empty port. */ return URLPARSE_ERR_PARSE; } start = up->pos; port = 0; for (; !urlparse_parser_eof(up); ++up->pos) { switch (*up->pos) { DIGIT_CASES: if (port > UINT16_MAX / 10) { return URLPARSE_ERR_PARSE; } port *= 10; d = (uint16_t)(*up->pos - '0'); if (port > UINT16_MAX - d) { return URLPARSE_ERR_PARSE; } port += d; break; case '/': case '?': /* http_parser disallows empty port. */ if (start == up->pos) { return URLPARSE_ERR_PARSE; } goto fin; default: return URLPARSE_ERR_PARSE; } } fin: urlparse_url_set_field_data(dest, URLPARSE_PORT, up->begin, start, up->pos); dest->port = port; return 0; } static int parse_authority(urlparse_parser *up, urlparse_url *dest) { const char *start; int rv; if (urlparse_parser_eof(up)) { return URLPARSE_ERR_PARSE; } if (*up->pos == '[') { ++up->pos; rv = parse_ipv6_host(up, dest); if (rv != 0) { return rv; } } else { start = up->pos; rv = parse_host(up, dest); if (rv == 0) { if (urlparse_parser_eof(up) || *up->pos != ':') { return 0; } ++up->pos; rv = parse_port(up, dest); if (rv == 0) { return 0; } } /* Rewind, and try parsing userinfo, and then host. */ up->pos = start; rv = parse_userinfo(up, dest); if (rv != 0) { return rv; } if (urlparse_parser_eof(up)) { return URLPARSE_ERR_PARSE; } if (*up->pos == '[') { ++up->pos; rv = parse_ipv6_host(up, dest); if (rv != 0) { return rv; } } else { rv = parse_host(up, dest); if (rv != 0) { return rv; } } } if (urlparse_parser_eof(up) || *up->pos != ':') { return 0; } ++up->pos; return parse_port(up, dest); } static int parse_extra(urlparse_parser *up, urlparse_url *dest, int field) { const char *start; start = up->pos; for (; !urlparse_parser_eof(up); ++up->pos) { switch (*up->pos) { /* unreserved */ ALPHA_CASES: DIGIT_CASES: case '-': case '.': case '_': case '~': /* pct-encoded */ case '%': /* sub-delims */ case '!': case '$': case '&': case '\'': case '(': case ')': case '*': case '+': case ',': case ';': case '=': /* extra */ case ':': case '@': /* query/fragment specific */ case '/': case '?': /* http-parser allows the following characters as well. */ case '"': case '<': case '>': case '[': case '\\': case ']': case '^': case '`': case '{': case '|': case '}': continue; case '#': if (field == URLPARSE_QUERY) { goto fin; } continue; default: return URLPARSE_ERR_PARSE; } } fin: if (start != up->pos) { urlparse_url_set_field_data(dest, field, up->begin, start, up->pos); } return 0; } int urlparse_parse_url(const char *url, size_t urllen, int is_connect, urlparse_url *dest) { urlparse_parser up; int rv; if (urllen == 0 || urllen > UINT16_MAX) { return URLPARSE_ERR_PARSE; } up.begin = url; up.pos = url; up.end = url + urllen; memset(dest, 0, sizeof(*dest)); if (is_connect) { /* http_parser requires host:port when is_connect is nonzero. */ rv = parse_authority(&up, dest); if (rv != 0) { return rv; } } else { rv = parse_scheme(&up, dest); if (rv != 0) { return rv; } if (dest->field_set & (1 << URLPARSE_SCHEMA)) { if (up.end - up.pos < 3 || *up.pos != ':' || *(up.pos + 1) != '/' || *(up.pos + 2) != '/') { return URLPARSE_ERR_PARSE; } up.pos += 3; rv = parse_authority(&up, dest); if (rv != 0) { return rv; } } } rv = parse_path_abempty(&up, dest); if (rv != 0) { return rv; } if (urlparse_parser_eof(&up)) { goto fin; } if (*up.pos == '?') { ++up.pos; rv = parse_extra(&up, dest, URLPARSE_QUERY); if (rv != 0) { return rv; } } if (urlparse_parser_eof(&up)) { goto fin; } assert(*up.pos == '#'); ++up.pos; for (; !urlparse_parser_eof(&up) && *up.pos == '#'; ++up.pos) ; rv = parse_extra(&up, dest, URLPARSE_FRAGMENT); if (rv != 0) { return rv; } fin: if (is_connect && dest->field_set != ((1 << URLPARSE_HOST) | (1 << URLPARSE_PORT))) { return URLPARSE_ERR_PARSE; } return 0; } nghttp2-1.68.0/third-party/PaxHeaders/mruby0000644000000000000000000000013215077107334015611 xustar0030 mtime=1761382108.968300763 30 atime=1761382109.795298372 30 ctime=1761382108.968300763 nghttp2-1.68.0/third-party/mruby/0000755000175100017510000000000015077107334016256 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/PaxHeaders/build_config.rb0000644000000000000000000000013215077107276020644 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.118411425 30 ctime=1761382108.551301968 nghttp2-1.68.0/third-party/mruby/build_config.rb0000644000175100017510000000107215077107276021234 0ustar00runnerrunner# The default build configuration file was moved to `build_config/default.rb`. # # Recommended way to customize the build configuration is: # * copy `default.rb` (or any config file) to a new file (e.g. `myconfig.rb`) # * edit `myconfig.rb`. # * `rake MRUBY_CONFIG=/path/to/myconfig.rb` to compile and test. # * or `rake MRUBY_CONFIG=myconfig` if your configuration file is in the `build_config` directory. # * (optional) submit your configuration as a pull-request if it's useful for others raise "The default configuration was moved to `build_config/default.rb`" nghttp2-1.68.0/third-party/mruby/PaxHeaders/Dockerfile0000644000000000000000000000013215077107276017665 xustar0030 mtime=1761382078.094420684 30 atime=1761382080.116411435 30 ctime=1761382108.346302561 nghttp2-1.68.0/third-party/mruby/Dockerfile0000644000175100017510000000054415077107276020260 0ustar00runnerrunnerFROM ruby:3.2.2-bullseye RUN apt-get update && apt-get install --no-install-recommends -y python3-pip shellcheck \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* WORKDIR /app COPY Gemfile Gemfile.lock .pre-commit-config.yaml ./ RUN bundle install && pip3 install --no-cache-dir pre-commit && git init . && pre-commit install-hooks COPY . . nghttp2-1.68.0/third-party/mruby/PaxHeaders/doc0000644000000000000000000000012615077107334016361 xustar0028 mtime=1761382108.9553008 30 atime=1761382109.795298372 28 ctime=1761382108.9553008 nghttp2-1.68.0/third-party/mruby/doc/0000755000175100017510000000000015077107334017023 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/doc/PaxHeaders/mruby3.3.md0000644000000000000000000000013015077107276020342 xustar0030 mtime=1761382078.099420661 30 atime=1761382080.120411416 28 ctime=1761382108.9553008 nghttp2-1.68.0/third-party/mruby/doc/mruby3.3.md0000644000175100017510000004052315077107276020740 0ustar00runnerrunner# User visible changes in `mruby3.3` from `mruby3.2` "**_NOTE_**:" are changes to be aware of. # The language - aliases work properly with `super` ([2ad3f0e](https://github.com/mruby/mruby/commit/2ad3f0e)) - `callee` method work differently with aliases in mruby ([f2dc76e](https://github.com/mruby/mruby/commit/f2dc76e)) - define `Kernel#respond_to_missing?` method ([347586e](https://github.com/mruby/mruby/commit/347586e)) - `_inspect` method (`inspect` with recursive check) is removed ([e2bbf75](https://github.com/mruby/mruby/commit/e2bbf75), [5cb0c74](https://github.com/mruby/mruby/commit/5cb0c74), [113565a](https://github.com/mruby/mruby/commit/113565a), [0713f2a](https://github.com/mruby/mruby/commit/0713f2a), [6ae6b63](https://github.com/mruby/mruby/commit/6ae6b63), [fc9fffc](https://github.com/mruby/mruby/commit/fc9fffc)) - `__printstr__` method is removed; use `print` instead ([acecee0](https://github.com/mruby/mruby/commit/acecee0), [192e6e3](https://github.com/mruby/mruby/commit/192e6e3)) - New method `String#bytesplice` ([5274647](https://github.com/mruby/mruby/commit/5274647), [a2e2e83](https://github.com/mruby/mruby/commit/a2e2e83)) - Allow `return` in blocks to cross C boundaries ([#6125](https://github.com/mruby/mruby/pull/6125)) # Configuration - mruby can be built using Docker now. Try `docker-compose build` for example. ([#5961](https://github.com/mruby/mruby/pull/5961)) - New Platform: DJGPP (MS-DOS) ([#6022](https://github.com/mruby/mruby/pull/6022)) - New Platform: Nintendo Wii ([#6086](https://github.com/mruby/mruby/pull/6086)) - Improved Platform: Android ([#6013](https://github.com/mruby/mruby/pull/6013)) - Improved Platform: Dreamcast ([#6130](https://github.com/mruby/mruby/pull/6130)) - Allow tests to be disabled for specific gems; warn about disabled tests ([#6012](https://github.com/mruby/mruby/pull/6012)) - Replace `MRB_NO_DIRECT_THREADING` with `MRB_USE_VM_SWITCH_DISPATCH` ([#5902](https://github.com/mruby/mruby/pull/5902)) # mruby memory API - `mrb_default_allocf` can be overridden by the application ([34c5d96](https://github.com/mruby/mruby/commit/34c5d96)) - `mrb_open_allocf` will be deprecated ([cfee5c2](https://github.com/mruby/mruby/commit/cfee5c2)) # Changes in C API - add new error handling API functions ([8c8bbd9](https://github.com/mruby/mruby/commit/8c8bbd9)) - Add `mrb_vm_ci_env_clear()` function with `MRB_API` ([#5945](https://github.com/mruby/mruby/pull/5945)) - a new function `mrb_check_frozen_value()` ([ccdf75c](https://github.com/mruby/mruby/commit/ccdf75c)) - avoid formatting in `mrb_bug()` ([82a48bd](https://github.com/mruby/mruby/commit/82a48bd))
**_NOTE_**: If you are using it, you must give a simple string or replace it with a call to `mrb_raise()` series. - stop using `mrbc_` prefix for compiler context ([c5e3cbe](https://github.com/mruby/mruby/commit/c5e3cbe))
The same names are provided as before, but we recommend replacing them. - Allow `Class#allocate` to be prohibited ([#5979](https://github.com/mruby/mruby/pull/5979), [#6122](https://github.com/mruby/mruby/pull/6122), [#6123](https://github.com/mruby/mruby/pull/6123))
To disable `#allocate`, use `MRB_UNDEF_ALLOCATOR()`. This is also automatically applied when the subclass is created, but to explicitly allow it, use `MRB_DEFINE_ALLOCATOR()`. # Changes in mrbgems - **default.gembox**: Add mruby debugger mrdb (`mruby-bin-debugger`) ([#5966](https://github.com/mruby/mruby/pull/5966)) - **mruby-bin-config**: new options `--cxx`, `--cxxflags`, `--as`, `--asflags`, `--objc`, `--objcflags` ([#6054](https://github.com/mruby/mruby/pull/6054)) - **mruby-binding**: renamed from `mruby-binding-core` of mruby3.2 ([11af5db](https://github.com/mruby/mruby/commit/11af5db))
**_NOTE_**: If using `mruby-binding-core` of mruby 3.2, replace it with `mruby-binding`. - **mruby-binding**: implemented `Binding#initialize_copy` method ([#5517](https://github.com/mruby/mruby/pull/5517)) - **mruby-binding**: `Kernel#binding` responds only to calls from Ruby ([#5981](https://github.com/mruby/mruby/pull/5981)) - **mruby-compar-ext**: Comparable#clamp to accept nil as arguments ([836bebc](https://github.com/mruby/mruby/commit/836bebc)) - **mruby-compiler**: add print name for identifier tokens ([d7b2e3a](https://github.com/mruby/mruby/commit/d7b2e3a)) - **mruby-data**: allow empty Data ([927a9df](https://github.com/mruby/mruby/commit/927a9df)) - **mruby-enumerator**: remove internal attribute methods `obj`, `args`, `kwd`, `meth`, `fib`. ([735fa24](https://github.com/mruby/mruby/commit/735fa24)) - **mruby-enumerator**: add Enumerator#size ([861f8bd](https://github.com/mruby/mruby/commit/861f8bd)) - **mruby-eval**: merged `mruby-binding` of mruby3.2 ([501b22a](https://github.com/mruby/mruby/commit/501b22a), [#5989](https://github.com/mruby/mruby/pull/5989))
**_NOTE_**: If using `mruby-binding` of mruby 3.2, replace it with `mruby-eval`. - **mruby-fiber**: Add a new `mrb_fiber_new()` with `MRB_API` ([#6097](https://github.com/mruby/mruby/pull/6097)) - **mruby-fiber**: Allows calling `Fiber#resume` from C ([#6106](https://github.com/mruby/mruby/pull/6106)) - **mruby-fiber**: `Fiber#to_s` format changed ([#6105](https://github.com/mruby/mruby/pull/6105)) - **mruby-io**: add File#atime and File#ctime ([321cfe9](https://github.com/mruby/mruby/commit/321cfe9)) - **mruby-io**: Add "x" mode option for `IO.open` ([#6081](https://github.com/mruby/mruby/pull/6081)) - **mruby-io**: File.new should not take blocks ([53de964](https://github.com/mruby/mruby/commit/53de964)) - **mruby-method**: `Method#to_s` format changed ([f5bc82f](https://github.com/mruby/mruby/commit/f5bc82f), [02f189c](https://github.com/mruby/mruby/commit/02f189c)) - **mruby-numeric-ext**: `int.pow(n,m)` to take bigint as exponential ([d482eab](https://github.com/mruby/mruby/commit/d482eab)) - **mruby-pack**: support new directives `j`, `J`, `b`, `B`, `#` ([2a1e3a5](https://github.com/mruby/mruby/commit/2a1e3a5), [e7021f1](https://github.com/mruby/mruby/commit/e7021f1), [e17f325](https://github.com/mruby/mruby/commit/e17f325)) - **mruby-range-ext**: new method `Range#overlap?` ([384d0e2](https://github.com/mruby/mruby/commit/384d0e2)) - **mruby-string-ext**: Add `String#valid_encoding?` method ([eabe2d9](https://github.com/mruby/mruby/commit/eabe2d9)) - **mruby-struct**: allow empty Struct when a name is not given ([c212ede](https://github.com/mruby/mruby/commit/c212ede)) - **mruby-time**: should allow year before 1900 ([e5de08b](https://github.com/mruby/mruby/commit/e5de08b)) - **mruby-time**: support bigint to time_t if necessary ([7096d27](https://github.com/mruby/mruby/commit/7096d27)) - **mruby-time**: need to handle negative time_t ([b064d7e](https://github.com/mruby/mruby/commit/b064d7e)) # Changes in build system - Extended `rake install` task ([#5928](https://github.com/mruby/mruby/pull/5928))
**_NOTE_**: Due to this impact, executable files in the `mruby/bin/` directory by default are now symbolic links (batch files on Windows). If previously relied on those executables, should be replaced with direct references to the entity created under the build directory (e.g. `mruby/build/host/bin/`). - Encode and decode escape characters for presym ([#6011](https://github.com/mruby/mruby/pull/6011)) - Rakefile: remove default build target directories in `deep_clean` ([#6032](https://github.com/mruby/mruby/pull/6032), [1e38569](https://github.com/mruby/mruby/commit/1e38569)) # Other breaking changes - `mrb_f_raise()` is now an internal function ([#5923](https://github.com/mruby/mruby/pull/5923), [#6070](https://github.com/mruby/mruby/pull/6070)) - `mrb_make_exception()` is now an internal function with different parameters ([431f83e](https://github.com/mruby/mruby/commit/431f83e), [78137f3](https://github.com/mruby/mruby/commit/78137f3)) - The `File#path` method no longer uses the `#to_path` method for implicit conversion ([d86c4a7](https://github.com/mruby/mruby/commit/d86c4a7)) - stop mrb isolation for each test file ([a20fbe5](https://github.com/mruby/mruby/commit/a20fbe5)) - RBreak remembers the CI location ([#6103](https://github.com/mruby/mruby/pull/6103)) # Bugs Fixed - [#5724](https://github.com/mruby/mruby/issues/5724) Rational#\*\* is missing - [#5725](https://github.com/mruby/mruby/issues/5725) weird const_missing exceptions in mrblib code - [#5789](https://github.com/mruby/mruby/issues/5789) No memory release of backtrace information due to stack error - [#5932](https://github.com/mruby/mruby/issues/5932) How to create a block using the C API? mrb_yield keeps crashing! - [#5943](https://github.com/mruby/mruby/issues/5943) TCPSocket#write is failed - [#5944](https://github.com/mruby/mruby/issues/5944) Behavior of calling method with a hash variable - [#5946](https://github.com/mruby/mruby/pull/5946) Don't switch constant search path from modules to Object - [#5949](https://github.com/mruby/mruby/issues/5949) Caller appears to report wrong line when block passed and brackets omitted - [0906cd7](https://github.com/mruby/mruby/commit/0906cd7) numeric.c: fix rounding function issues with big numbers - [#5974](https://github.com/mruby/mruby/issues/5974) Invalid escape sequences in gem_init.c on windows - [#5975](https://github.com/mruby/mruby/issues/5975) Equals comparison fails on extreme ends of 64-bit integers - [#5985](https://github.com/mruby/mruby/issues/5985) Sign extension with OP_LOADI32 in get_int_operand() - [#5986](https://github.com/mruby/mruby/issues/5986) Fix bugs in String#bytesplice - [#5987](https://github.com/mruby/mruby/issues/5987) ~(-1 << 64) is incorrect - [#5991](https://github.com/mruby/mruby/issues/5991) 'gets' method not working in mruby-3.2.0 - [#5994](https://github.com/mruby/mruby/pull/5994) fix typo in mrbgems/mruby-io/src/io.c - [#5995](https://github.com/mruby/mruby/issues/5995) One seemingly unnecessary parameter is passed in the block parameters - [#6008](https://github.com/mruby/mruby/pull/6008) Make "bintest" independent of directory - [b47c8b7](https://github.com/mruby/mruby/commit/b47c8b7) gc.c (clear_all_old): fix a generational GC bug - [#6029](https://github.com/mruby/mruby/issues/6029) mruby build fails under mrbgems directory - [a264965](https://github.com/mruby/mruby/commit/a264965) mruby-os-memsize/memsize.c: fix irep size calculation - [3310e10](https://github.com/mruby/mruby/commit/3310e10) mruby-test/mrbgem.rake: fix mrb_state handling bug - [#6041](https://github.com/mruby/mruby/issues/6041) GC Performance may have degraded - [#6044](https://github.com/mruby/mruby/issues/6044) Generated presym/table.h contains invalid characters - [#6051](https://github.com/mruby/mruby/issues/6051) Null pointer dereference in mrb_addrinfo_unix_path - [#6052](https://github.com/mruby/mruby/issues/6052) Null pointer dereference while handling the Proc class - [#6055](https://github.com/mruby/mruby/pull/6055) Fix libmruby name for VisualC++ - [#6060](https://github.com/mruby/mruby/issues/6060) SEGFAULT Issue Related to Fiber Usage in ngx_mruby Development - [#6061](https://github.com/mruby/mruby/issues/6061) Performance issue in String#codepoints - [#6064](https://github.com/mruby/mruby/issues/6064) MRUBY_PACKAGE_DIR does not always have a value. - [#6065](https://github.com/mruby/mruby/issues/6065) Null pointer dereference while handling the Proc class - [#6066](https://github.com/mruby/mruby/issues/6066) Null pointer dereference involving Struct.new() - [#6067](https://github.com/mruby/mruby/issues/6067) Null pointer dereference in mrb_string_value_cstr - [#6068](https://github.com/mruby/mruby/issues/6068) Stack overflow in mrb_vm_exec - [#6076](https://github.com/mruby/mruby/pull/6076) Fixed unwinding block that could point to invalid PC - [#6084](https://github.com/mruby/mruby/issues/6084) Incorrect symbolic sinks in binary built on Linux - [#6087](https://github.com/mruby/mruby/issues/6087) 'Remote branch HEAD not found in upstream origin' error on build - [#6089](https://github.com/mruby/mruby/issues/6089) binding.eval() handles def expressions differently from CRuby - [#6098](https://github.com/mruby/mruby/issues/6098) Fails to call superclass of wrapped method - [#6099](https://github.com/mruby/mruby/issues/6099) `ensure` section is not executed if the function exits via a return in a proc - [#6108](https://github.com/mruby/mruby/issues/6108) VM crashes with break - [#6118](https://github.com/mruby/mruby/pull/6118) Fixed IO#read with buf - [#6120](https://github.com/mruby/mruby/pull/6120) Set EBADF if check_file_descriptor() fails - [#6126](https://github.com/mruby/mruby/pull/6126) Fixed return value of `OP_RETURN_BLK` called directly under C function - [#6134](https://github.com/mruby/mruby/issues/6134) String#unpack1 returns an array instead of a single string - [#6136](https://github.com/mruby/mruby/pull/6136) Fixed when combined `mrb_fiber_resume()` and `Fiber#transfer` # Pull Requests (User Visible Ones) - [#5517](https://github.com/mruby/mruby/pull/5517) Fixed local variables not separated between copied binding objects - [#5902](https://github.com/mruby/mruby/pull/5902) Replace `MRB_NO_DIRECT_THREADING` with `MRB_USE_VM_SWITCH_DISPATCH` - [#5923](https://github.com/mruby/mruby/pull/5923) Demotion `mrb_f_raise()` from `MRB_API` - [#5928](https://github.com/mruby/mruby/pull/5928) Improved `rake install` - [#5945](https://github.com/mruby/mruby/pull/5945) Avoid exposure for `REnv` objects - [#5946](https://github.com/mruby/mruby/pull/5946) Don't switch constant search path from modules to Object - [#5966](https://github.com/mruby/mruby/pull/5966) Update default.gembox add mruby debugger mrdb - [#5979](https://github.com/mruby/mruby/pull/5979) Allow Class#allocate to be prohibited - [#5981](https://github.com/mruby/mruby/pull/5981) `Kernel#binding` responds only to calls from Ruby - [#5989](https://github.com/mruby/mruby/pull/5989) Integrate mruby-binding-eval into mruby-eval - [#5961](https://github.com/mruby/mruby/pull/5961) Add Docker to build and run all mruby tests. Run pre-commit and generate YARD docs with Docker - [#5994](https://github.com/mruby/mruby/pull/5994) fix typo in mrbgems/mruby-io/src/io.c - [#6008](https://github.com/mruby/mruby/pull/6008) Make "bintest" independent of directory - [#6009](https://github.com/mruby/mruby/pull/6009) Avoid adding /bintest which does not exist - [#6011](https://github.com/mruby/mruby/pull/6011) Encode and decode escape characters for presym - [#6012](https://github.com/mruby/mruby/pull/6012) Allow tests to be disabled for specific gems; warn about disabled tests - [#6013](https://github.com/mruby/mruby/pull/6013) Fix Android toolchain - [#6022](https://github.com/mruby/mruby/pull/6022) Build configuration for MS-DOS and DJGPP - [#6032](https://github.com/mruby/mruby/pull/6032) Rake: update task clean to remove bin and build folders - [#6045](https://github.com/mruby/mruby/pull/6045) Fixes escape sequence bug and enhancements in Presym scanning - [#6054](https://github.com/mruby/mruby/pull/6054) Extends `bin/mruby-config` - [#6055](https://github.com/mruby/mruby/pull/6055) Fix libmruby name for VisualC++ - [#6070](https://github.com/mruby/mruby/pull/6070) Demotion mrb_f_raise() in kernel.c from MRB_API too - [#6076](https://github.com/mruby/mruby/pull/6076) Fixed unwinding block that could point to invalid PC - [#6081](https://github.com/mruby/mruby/pull/6081) Add "x" mode option for IO.open - [#6086](https://github.com/mruby/mruby/pull/6086) Add build config for Nintendo Wii - [#6097](https://github.com/mruby/mruby/pull/6097) Add a new mrb_fiber_new() with MRB_API - [#6103](https://github.com/mruby/mruby/pull/6103) RBreak remembers the CI location - [#6105](https://github.com/mruby/mruby/pull/6105) Implement `Fiber#to_s` method - [#6106](https://github.com/mruby/mruby/pull/6106) Ease fiber limitations - [#6118](https://github.com/mruby/mruby/pull/6118) Fixed IO#read with buf - [#6120](https://github.com/mruby/mruby/pull/6120) Set EBADF if check_file_descriptor() fails - [#6122](https://github.com/mruby/mruby/pull/6122) Prohibit `Class#allocate` in a different way - [#6123](https://github.com/mruby/mruby/pull/6123) Inherit `MRB_FL_UNDEF_ALLOCATE` in subclasses - [#6125](https://github.com/mruby/mruby/pull/6125) Allow `OP_RETURN_BLK` to cross C boundaries - [#6126](https://github.com/mruby/mruby/pull/6126) Fixed return value of `OP_RETURN_BLK` called directly under C function - [#6130](https://github.com/mruby/mruby/pull/6130) `dreamcast_shelf build config`: complete overhaul - [#6136](https://github.com/mruby/mruby/pull/6136) Fixed when combined `mrb_fiber_resume()` and `Fiber#transfer` nghttp2-1.68.0/third-party/mruby/doc/PaxHeaders/mruby3.0.md0000644000000000000000000000013215077107276020341 xustar0030 mtime=1761382078.098420666 30 atime=1761382080.120411416 30 ctime=1761382108.946300826 nghttp2-1.68.0/third-party/mruby/doc/mruby3.0.md0000644000175100017510000001125415077107276020734 0ustar00runnerrunner# User visible changes in `mruby3.0` # Build System ## `build_config` directory Typical build configuration files are located in `build_config` directory. For examples: - `default`: the default configuration - `host-gprof`: compiles with `gprof` for performance tuning - `host-m32`: compiles in gcc 32-bit mode on 64-bit platforms - `boxing`: compiles all three boxing options - `clang-asan`: compiles with `clang`'s Address Sanitizer You can specify the build configuration file with the `MRUBY_CONFIG` environment variable (or `CONFIG` in short). If the value specified by `MRUBY_CONFIG` is not the path to the configuration file, `build_config/${MRUBY_CONFIG}.rb` is used. So you can specify it as `rake MRUBY_CONFIG=boxing`, for example. # Build Configuration Contribution When you write a new build configuration description, please contribute. We welcome your contribution as a GitHub pull-request. # Language Changes ## New Syntax We have ported some new syntax from CRuby. - Single line pattern matching (`12 => x`); mruby matches only with local variables at the moment - Numbered block parameter (`x.map{_1 * 2}`) - End-less `def` (`def double(x) = x*2`) # Configuration Options Changed ## Renamed for consistency Some configuration macro names are changed for consistency (use `MRB_USE_XXX` or `MRB_NO_XXX`). | mruby2 | mruby3 | | ------------------------------ | ------------------------- | | `MRB_ENABLE_ALL_SYMBOLS` | `MRB_USE_ALL_SYMBOLS` | | `MRB_ENABLE_CXX_ABI` | `MRB_USE_CXX_ABI` | | `MRB_ENABLE_CXX_EXCEPTION` | `MRB_USE_CXX_EXCEPTION` | | `MRB_ENABLE_DEBUG_HOOK` | `MRB_USE_DEBUG_HOOK` | | `MRB_DISABLE_DIRECT_THREADING` | `MRB_NO_DIRECT_THREADING` | | `MRB_DISABLE_STDIO` | `MRB_NO_STDIO` | | `MRB_METHOD_T_STRUCT` | `MRB_USE_METHOD_T_STRUCT` | | `MRB_USE_FLOAT` | `MRB_USE_FLOAT32` | | `MRB_WITHOUT_FLOAT` | `MRB_NO_FLOAT` | | `ENABLE_LINENOISE` | `MRB_USE_LINENOISE` | | `ENABLE_READLINE` | `MRB_USE_READLINE` | | `DISABLE_MIRB_UNDERSCORE` | `MRB_NO_MIRB_UNDERSCORE` | - `MRB_USE_FLOAT32` is changed from `MRB_USE_FLOAT` to make sure `float` here means using single-precision float, and not the opposite of `MRB_NO_FLOAT`. - `MRB_USE_METHOD_T_STRUCT` uses `struct` version of `mrb_method_t`. More portable but consumes more memory. Turned on by default on 32-bit platforms. - `MRB_` prefix is added to those without. ## `MRB_NO_BOXING` Uses `struct` to represent `mrb_value`. Consumes more memory but easier to investigate the internal and to debug. It used to be default `mrb_value` representation. Now the default is `MRB_WORD_BOXING`. ## `MRB_WORD_BOXING` Pack `mrb_value` in an `intptr_t` integer. Consumes less memory compared to `MRB_NO_BOXING` especially on 32-bit platforms. `Fixnum` size is 31 bits so some integer values does not fit in `Fixnum` integers. ## `MRB_NAN_BOXING` Pack `mrb_value` in a floating-point number. Nothing changed from previous versions. ## `MRB_USE_MALLOC_TRIM` Call `malloc_trim(0)` from mrb_full_gc() if this macro is defined. If you are using glibc malloc, this macro could reduce memory consumption. # Command Line Program ## `bin/mruby` (by mrbgems/mruby-bin-mruby) The mruby3 now automatically detects `*.mrb` files without the `-b` switch. Therefore, it can be mixed with the `*.rb` file in combination with the `-r` switch and specified at the same time. Here's an example that works fine: ```console $ bin/mruby app.mrb $ bin/mruby -r lib1.mrb -r lib2.rb app.rb $ bin/mruby -r lib1.rb -r lib2.rb < app.mrb ``` # Internal Changes ## New Instructions `mruby3` introduces a few new instructions. Instructions that access pool[i]/syms[i] where i>255. - `OP_LOADL16` - `OP_STRING16` - `OP_LOADSYM16` Instructions that load a 32-bit integer. - `OP_LOADI32` Instruction that unwinds jump table for rescue/ensure. - `OP_JMPUW` Renamed from `OP_RAISE` - `OP_RAISEIF` Instruction that is reserved for the future keyword argument support. - OP_SENDVK ## Removed Instructions Instructions for old exception handling - `OP_ONERR` - `OP_POPERR` - `OP_EPUSH` - `OP_EPOP` No more operand extension - `OP_EXT1` - `OP_EXT2` - `OP_EXT3` ## Changed Instructions Jump addresses used to be specified by absolute offset from the start of `iseq`. Now they are relative offset from the address of the next instruction. ## `Random` now use `xoshiro128++`. For better and faster random number generation. ## Preallocated Symbol Preallocated symbols are interned at compile-time. They can be accessed via symbols macros (e.g. `MRB_SYM()`). See [Symbols](guides/symbol.md). nghttp2-1.68.0/third-party/mruby/doc/PaxHeaders/mruby3.4.md0000644000000000000000000000013215077107276020345 xustar0030 mtime=1761382078.099420661 30 atime=1761382080.121411411 30 ctime=1761382108.954300803 nghttp2-1.68.0/third-party/mruby/doc/mruby3.4.md0000644000175100017510000005614415077107276020747 0ustar00runnerrunner# User visible changes in `mruby3.4` from `mruby3.3` "**_NOTE_**:" are changes to be aware of. # The language - mruby now supports `private` and `protected` visibility ([b0db0bd](https://github.com/mruby/mruby/commit/b0db0bd)) - Maximum length of inlined symbols reduced from 5 to 4 characters to provide space for visibility flags ([6442a01](https://github.com/mruby/mruby/commit/6442a01)) - Many methods are made private according to CRuby visibility ([4a0e806](https://github.com/mruby/mruby/commit/4a0e806)) - Generate OP_SSEND for `self.method` type calls ([111fe4b](https://github.com/mruby/mruby/commit/111fe4b)) - `initialize` method will be always private ([eb8b412](https://github.com/mruby/mruby/commit/eb8b412)) - Add new hooks `method_removed`, `method_undefined` ([9c74f6e](https://github.com/mruby/mruby/commit/9c74f6e)) - Add new hooks `singleton_method_removed`, `singleton_method_undefined` ([0863c08](https://github.com/mruby/mruby/commit/0863c08)) - Updated `OP_DEF` output from codedump ([3a3e877](https://github.com/mruby/mruby/commit/3a3e877)) - Better handling of binary strings, e.g. String#b ([b0127f0](https://github.com/mruby/mruby/commit/b0127f0)) - Hash `to_s` format has changed ([baeeb5e](https://github.com/mruby/mruby/commit/baeeb5e)) - Some encoding related method such as `#force_encoding` ([e47b4ca](https://github.com/mruby/mruby/commit/e47b4ca)), `#b` ([b0127f0](https://github.com/mruby/mruby/commit/b0127f0)) - Constant folding for `String#+` ([6687bdd](https://github.com/mruby/mruby/commit/6687bdd)) - Remove Float bit-operation ([db8368f](https://github.com/mruby/mruby/commit/db8368f)) - use SWAR technique for strlen performance ([cbb31e6](https://github.com/mruby/mruby/commit/cbb31e6)) - use merge sort for `Array#sort` ([5bd63d6](https://github.com/mruby/mruby/commit/5bd63d6)) # Changes in C API - pool.c renamed to mempool.c (and mrb_pool to mrb_mempool) ([49525fa](https://github.com/mruby/mruby/commit/49525fa)) - mrb_pool_value renamed to mrb_irep_pool to reduce confusion ([62ef5db](https://github.com/mruby/mruby/commit/62ef5db)) - rename BOXNIX_SET_VALUE to BOXNO_SET_VALUE ([#6397](https://github.com/mruby/mruby/pull/6397)) - `MRB_FROZEN_P()` is replaced by `mrb_frozen_p()` ([c11d18e](https://github.com/mruby/mruby/commit/c11d18e)) - rename `color` to `gc_color` ([0e79f6b](https://github.com/mruby/mruby/commit/0e79f6b), [1e36d76](https://github.com/mruby/mruby/commit/1e36d76)) - add `obj->frozen` instead of flags `MRB_SET_FROZEN_FLAG`/`MRB_UNSET_FROZEN_FLAG` ([8276143](https://github.com/mruby/mruby/commit/8276143)) # Build & Configuration - New Build Target: `test:run:serial`, `test:run:serial:lib`, `test:run:serial:bin` ([#6423](https://github.com/mruby/mruby/pull/6423)) - New Platform: PlayStation Portable ([#6022](https://github.com/mruby/mruby/pull/6465)) - New Platform: emscripten ([#6487](https://github.com/mruby/mruby/pull/6487)) - New Config: no-float (with MRB_NO_FLOAT) ([32200f1](https://github.com/mruby/mruby/commit/32200f1)) # Changes in mrbgems - **mruby-print**: removed; if you do not use `mruby-io`, mruby use `#print` etc. in the core ([8c8bbd9](https://github.com/mruby/mruby/commit/8c8bbd9)) - **mruby-enum-lazy**: Add Enumerable::Lazy#grep_v to mruby-enum-lazy ([#6171](https://github.com/mruby/mruby/pull/6171)) - **mruby-io**: Add `level` argument to `File.dirname` ([#6463](https://github.com/mruby/mruby/pull/6463)) - **mruby-io**: File.absolute_path? ([#6482](https://github.com/mruby/mruby/pull/6482)) - **mruby-io**: File.absolute_path ([96113a2](https://github.com/mruby/mruby/commit/96113a2)) - **mruby-toplevel-ext**: top-level public/private/protected moved to the core ([2a876d2](https://github.com/mruby/mruby/commit/2a876d2)) - **mruby-metaprog**: method list methods now works according to the visibility ([9229da1](https://github.com/mruby/mruby/commit/9229da1)) - **mruby-metaprog**: `public_instance_methods`, `private_instance_methods`, `protected_instance_methods` ([9e3e7b2](https://github.com/mruby/mruby/commit/9e3e7b2)) - **mruby-encoding**: MRB_UTF8_STRING turned on automatically with this gem ([74bdae9](https://github.com/mruby/mruby/commit/74bdae9)) # Fixed GitHub Issues - [#6173](https://github.com/mruby/mruby/issues/6173) Fails to build with tcc(Tiny C Compiler) - [#6156](https://github.com/mruby/mruby/issues/6156) '/LIBPATH' issue - [#6183](https://github.com/mruby/mruby/issues/6183) ".e".to_f returns NAN - [#6182](https://github.com/mruby/mruby/issues/6182) mrb_read_float() converts "0.3" with a small error compared to strtod() - [#6210](https://github.com/mruby/mruby/issues/6210) Unary minus seems broken - [#6255](https://github.com/mruby/mruby/issues/6255) Wrong number of characters in broken UTF-8 string - [#4038](https://github.com/mruby/mruby/issues/4038) Heap buffer overflow in OP_ENTER - [#6262](https://github.com/mruby/mruby/issues/6262) Unable to define == for objects when using Array#delete - [#6267](https://github.com/mruby/mruby/issues/6267) When MRB_UTF8_STRING is enabled, giving byte characters for String#index and String#split gives wrong results - [#6277](https://github.com/mruby/mruby/issues/6277) MSVC: can't use malloc() in string.c with WIN32_LEAN_AND_MEAN - [#6240](https://github.com/mruby/mruby/issues/6240) Differentiate between lib and lib64 in the build settings. - [#6304](https://github.com/mruby/mruby/issues/6304) Calling method_missing with only Kwargs passes arguments incorrectly - [#6317](https://github.com/mruby/mruby/issues/6317) mrb_gc_register() may cause GC and collect the object being protected - [#6307](https://github.com/mruby/mruby/issues/6307) Planned change patch for mrb_vm_exec() - [#6298](https://github.com/mruby/mruby/issues/6298) foo :bar {} is legal in mruby? - [#6326](https://github.com/mruby/mruby/issues/6326) Detect “Use-after-free†with address sanitizer - [#5358](https://github.com/mruby/mruby/issues/5358) static warning from getpwnam - [#6339](https://github.com/mruby/mruby/issues/6339) mrb_ary_delete() may refer to an invalid address (use-after-free) - [#6346](https://github.com/mruby/mruby/issues/6346) Block kwargs are passed as last positional arg when using yield - [#6365](https://github.com/mruby/mruby/issues/6365) powl() not available when compiling for Dreamcast - [#6369](https://github.com/mruby/mruby/issues/6369) 100x Performance Regression from 3.1 - [#6270](https://github.com/mruby/mruby/issues/6270) NODE_ZSUPER from deeply nested blocks will result in a truncated digits for block index in OP_ARGARY - [#6297](https://github.com/mruby/mruby/issues/6297) Assigning to a block variable changes the actual block (thus affecting block_given? and yield) - [#6389](https://github.com/mruby/mruby/issues/6389) instance_exec named block args don't work properly - [#6388](https://github.com/mruby/mruby/issues/6388) Recent commit broke my windows build - [#6411](https://github.com/mruby/mruby/issues/6411) Wrong function unwinding when using return in a block - [#6439](https://github.com/mruby/mruby/issues/6439) OP_JMPUW does not call the ensure block when it jumps to the beginning of the begin block - [#6441](https://github.com/mruby/mruby/issues/6441) break inside while loop will execute the ensure block outside of the while loop - [#6453](https://github.com/mruby/mruby/issues/6453) Bigint: incorrect behavior of ^ operator - [#6452](https://github.com/mruby/mruby/issues/6452) Bigint: weird mod behavior - [#6451](https://github.com/mruby/mruby/issues/6451) Bigint: incorrect division behavior - [#6456](https://github.com/mruby/mruby/issues/6456) bigint: bug with division of a small number by a bigint - [#6466](https://github.com/mruby/mruby/issues/6466) mruby-bin-mruby using Kernel#p and Kernel#print in bintest fails test - [#6467](https://github.com/mruby/mruby/issues/6467) Heap-Use-After-Free due to Recursive group_by Calls - [#6471](https://github.com/mruby/mruby/issues/6471) Discrepancy in codegen for binary operations between master branch and 3.3.0 - [#6477](https://github.com/mruby/mruby/issues/6477) heap-buffer-overflow in mrb_vm_exec - [#6485](https://github.com/mruby/mruby/issues/6485) Hash#rehash does not check if the hash is frozen - [#6483](https://github.com/mruby/mruby/issues/6483) Hash#default_proc= accepts arbitrary objects that are not callable - [#6491](https://github.com/mruby/mruby/issues/6491) Destroy existing string literals when composing string literals # Merged Pull Requests (User Visible Ones) - [#6171](https://github.com/mruby/mruby/pull/6171) Add Enumerable::Lazy#grep_v to mruby-enum-lazy - [#6174](https://github.com/mruby/mruby/pull/6174) Fix MRUBY_PACKAGE_DIR in mruby-config.bat - [#6175](https://github.com/mruby/mruby/pull/6175) Corrected strange conditional in mrb_vm_run() - [#6176](https://github.com/mruby/mruby/pull/6176) Stop assuming alias proc in CI_PROC_SET() - [#6177](https://github.com/mruby/mruby/pull/6177) gha: add macOS 14 to the build - [#6184](https://github.com/mruby/mruby/pull/6184) Remove the L_STOP label - [#6185](https://github.com/mruby/mruby/pull/6185) Added mrb_callinfo::u.keep_context for clarity - [#6186](https://github.com/mruby/mruby/pull/6186) Omit NULL check of e->cxt in OP_RETURN_BLK - [#6191](https://github.com/mruby/mruby/pull/6191) Speed up symbol equality comparison - [#6192](https://github.com/mruby/mruby/pull/6192) Fix `OP_STOP` with exception - [#6193](https://github.com/mruby/mruby/pull/6193) Fix wrong assertion in `OP_SENDB`. - [#6194](https://github.com/mruby/mruby/pull/6194) Simplify the calculation of the number of closures in `MRB_TT_FIBER` - [#6197](https://github.com/mruby/mruby/pull/6197) Fix int_xor to call flo_xor. - [#6201](https://github.com/mruby/mruby/pull/6201) tasks/doc.rake: standardize the `rake doc` error messages - [#6202](https://github.com/mruby/mruby/pull/6202) Remove the `.yardoc` folder with `rake doc:clean:api` - [#6204](https://github.com/mruby/mruby/pull/6204) Clean up the `.editorconfig` file - [#6209](https://github.com/mruby/mruby/pull/6209) Minor `.gitignore` clean up - [#6211](https://github.com/mruby/mruby/pull/6211) Minor `.gitignore` clean up; order entries - [#6216](https://github.com/mruby/mruby/pull/6216) Shared empty `iv_tbl` of module - [#6217](https://github.com/mruby/mruby/pull/6217) Strict declaration for `mrb_istruct_size()` - [#6219](https://github.com/mruby/mruby/pull/6219) Avoid assigning a fixed value in the loop - [#6220](https://github.com/mruby/mruby/pull/6220) Reorganize `mrb_cache_entry` and `mrb_method_t` types - [#6221](https://github.com/mruby/mruby/pull/6221) Arranging `each_backtrace()` - [#6222](https://github.com/mruby/mruby/pull/6222) Need to synchronize `dbg->regs` after VM call in `mrdb` - [#6224](https://github.com/mruby/mruby/pull/6224) `mrb_env_unshare()` to break the link to fiber - [#6225](https://github.com/mruby/mruby/pull/6225) Revert "Adjust environment when `mrb_exec_irep` happened." - [#6227](https://github.com/mruby/mruby/pull/6227) fix: `Array#shuffle(!)` result distribution - [#6228](https://github.com/mruby/mruby/pull/6228) Revert "`env` referred from top-level callinfo should not be unshared; fix #4019" - [#6230](https://github.com/mruby/mruby/pull/6230) Fix status of fiber after switched by exception raised - [#6231](https://github.com/mruby/mruby/pull/6231) Add a way to let other gems handle closing of fds in mruby-io - [#6232](https://github.com/mruby/mruby/pull/6232) Fold the code for freeing `env` - [#6233](https://github.com/mruby/mruby/pull/6233) Free stack memory at end of fiber - [#6235](https://github.com/mruby/mruby/pull/6235) fix `Array#delete` always firing the block when deleting `nil` - [#6236](https://github.com/mruby/mruby/pull/6236) unify the code for filter methods (and speed up `#reject!`) - [#6237](https://github.com/mruby/mruby/pull/6237) Stricter env objects to attach to ci - [#6238](https://github.com/mruby/mruby/pull/6238) Minimize zero initialization of the stack - [#6243](https://github.com/mruby/mruby/pull/6243) Fixed base64 decoding in `mruby-pack` - [#6244](https://github.com/mruby/mruby/pull/6244) Revise scope of role of `mrb_vm_run()` - [#6246](https://github.com/mruby/mruby/pull/6246) Fix typo in `test/t/hash.rb` - [#6249](https://github.com/mruby/mruby/pull/6249) Fix spelling in `src/vm.c` - [#6250](https://github.com/mruby/mruby/pull/6250) Fix spelling - [#6251](https://github.com/mruby/mruby/pull/6251) Clean up root move `CODEOWNERS` to `.github` directory - [#6253](https://github.com/mruby/mruby/pull/6253) Allow recycling fibers by GC if not referenced directly - [#6256](https://github.com/mruby/mruby/pull/6256) Update documentation for `mrb_top_run()` - [#6257](https://github.com/mruby/mruby/pull/6257) fix some mrbconf.md typos - [#6260](https://github.com/mruby/mruby/pull/6260) Remove `exc_caught` from `mrb_vm_exec()` - [#6261](https://github.com/mruby/mruby/pull/6261) fix: `to_a` integer ranges with `begin > end` failing - [#6263](https://github.com/mruby/mruby/pull/6263) fix: `Array#delete` mistakenly calling block even if not passed - [#6264](https://github.com/mruby/mruby/pull/6264) Must pass keyword arguments for `Kernel#to_enum` - [#6265](https://github.com/mruby/mruby/pull/6265) Fixes `Dir.children` and `Dir.each_child` - [#6266](https://github.com/mruby/mruby/pull/6266) Passes the nonexistent key as a block argument in `Array#delete` - [#6273](https://github.com/mruby/mruby/pull/6273) Improvements to `mrb_protect_atexit()` - [#6275](https://github.com/mruby/mruby/pull/6275) Fixed `Binding#eval` that failed to assign to the same variable - [#6276](https://github.com/mruby/mruby/pull/6276) Always run `atexit` on the top-level call frame - [#6279](https://github.com/mruby/mruby/pull/6279) Include headers for malloc() explicitly; fix #6277 - [#6280](https://github.com/mruby/mruby/pull/6280) Remove `MRB_ENV_CLOSED` flag - [#6281](https://github.com/mruby/mruby/pull/6281) Fixes local variables in `mruby-binding`. - [#6283](https://github.com/mruby/mruby/pull/6283) Simplify `uvenv()` - [#6288](https://github.com/mruby/mruby/pull/6288) Detach `env` of ci explicitly on atexit - [#6289](https://github.com/mruby/mruby/pull/6289) Simplify `OP_RETURN_BLK` and `OP_BREAK` - [#6290](https://github.com/mruby/mruby/pull/6290) Allow to change the output directory name of the `libmruby` file - [#6293](https://github.com/mruby/mruby/pull/6293) Changed the instruction table in `opcode.md` - [#6294](https://github.com/mruby/mruby/pull/6294) Optimise `mrb_iv_get` - [#6302](https://github.com/mruby/mruby/pull/6302) Minor cleanup in mrb_str_init - [#6303](https://github.com/mruby/mruby/pull/6303) mrb_str_aset_m() should return replace instead of str - [#6305](https://github.com/mruby/mruby/pull/6305) Protect keyword arguments in `prepare_missing()` - [#6308](https://github.com/mruby/mruby/pull/6308) Assume that `MRB_CATCH()` has `mrb->exc` set - [#6310](https://github.com/mruby/mruby/pull/6310) Doubling the call stack when extending it - [#6311](https://github.com/mruby/mruby/pull/6311) Added fast-path for positional arguments less than 15 in `OP_SEND` - [#6312](https://github.com/mruby/mruby/pull/6312) Omit error checking at `OP_RETURN`, `OP_RETURN_BLK` and `OP_BREAK` - [#6313](https://github.com/mruby/mruby/pull/6313) Fix wrong column number in opcode.md - [#6314](https://github.com/mruby/mruby/pull/6314) Optimize even?/odd? for big integers - [#6318](https://github.com/mruby/mruby/pull/6318) Shrink variables in `mrb_vm_exec()` - [#6320](https://github.com/mruby/mruby/pull/6320) Shrinking the code in `OP_BREAK` and `OP_RETURN_BLK` - [#6321](https://github.com/mruby/mruby/pull/6321) Avoid warnings in `lib/**/*.rb` - [#6322](https://github.com/mruby/mruby/pull/6322) Fix mrb_ro_data_p on Intel Mac - [#6324](https://github.com/mruby/mruby/pull/6324) Remove `localjump_error()` - [#6327](https://github.com/mruby/mruby/pull/6327) fix ncurses linking issues - [#6328](https://github.com/mruby/mruby/pull/6328) Fix use-after-free in `obj_free()` for env objects - [#6329](https://github.com/mruby/mruby/pull/6329) Fix use-after-free in `mrb_obj_alloc()` - [#6330](https://github.com/mruby/mruby/pull/6330) Add a precondition to call `mrb_env_unshare()`. - [#6331](https://github.com/mruby/mruby/pull/6331) Restore the GC arena with tests - [#6332](https://github.com/mruby/mruby/pull/6332) Must not depend on the “host†build to generate `mruby-compiler/core/y.tab.c` - [#6333](https://github.com/mruby/mruby/pull/6333) Reduce the number of branch instructions in the `heap_p()` - [#6335](https://github.com/mruby/mruby/pull/6335) Add `return_ci` in `CHECKPOINT_MAIN()` of `OP_RETURN` - [#6338](https://github.com/mruby/mruby/pull/6338) Need to place static proc objects into 8-byte alignments - [#6340](https://github.com/mruby/mruby/pull/6340) Fix use-after-free for `Array#<=>` - [#6341](https://github.com/mruby/mruby/pull/6341) Need to restore the GC arena after some function calls - [#6344](https://github.com/mruby/mruby/pull/6344) prefer using `mrb_yield` to call block arguments - [#6347](https://github.com/mruby/mruby/pull/6347) codegen.c,parse.y: remove flattening of `yield` arguments; fix #6346 - [#6348](https://github.com/mruby/mruby/pull/6348) Cancel the warning disablement - [#6349](https://github.com/mruby/mruby/pull/6349) Perform GC before deleting directories - [#6350](https://github.com/mruby/mruby/pull/6350) Fixed character encoding conversion function mismatch - [#6351](https://github.com/mruby/mruby/pull/6351) Remove unnecessary `mrb_gc_arena_restore()` - [#6353](https://github.com/mruby/mruby/pull/6353) Fix use-after-free in `mrb_ary_delete()` - [#6356](https://github.com/mruby/mruby/pull/6356) Making splat argument objects invisible from Ruby side - [#6373](https://github.com/mruby/mruby/pull/6373) Add build config for Milk-V Duo (RISC-V Linux) board - [#6382](https://github.com/mruby/mruby/pull/6382) Make array objects invisible in `mrb_gc_register()` - [#6385](https://github.com/mruby/mruby/pull/6385) Small improvements for `mrb_gc_register()` - [#6386](https://github.com/mruby/mruby/pull/6386) Avoid calling `mrb_gv_set()` from `mrb_gc_unregister()` - [#6387](https://github.com/mruby/mruby/pull/6387) Small improvements for `mrb_gc_unregister()` - [#6390](https://github.com/mruby/mruby/pull/6390) Fix use-after-free by `mrb_gc_unregistor()` - [#6391](https://github.com/mruby/mruby/pull/6391) Fixed argument forwarding in `instance_exec` - [#6392](https://github.com/mruby/mruby/pull/6392) Fix argument forwarding in `mrb_exec_irep()` - [#6393](https://github.com/mruby/mruby/pull/6393) Follow-up to #6391 - [#6395](https://github.com/mruby/mruby/pull/6395) Storing method-id inside Symbol#to_proc - [#6396](https://github.com/mruby/mruby/pull/6396) Milk-V Build Config: update GPIO gem URL - [#6397](https://github.com/mruby/mruby/pull/6397) boxing_no.h: rename BOXNIX_SET_VALUE -> BOXNO_SET_VALUE - [#6399](https://github.com/mruby/mruby/pull/6399) Add macOS 15 to the build - [#6405](https://github.com/mruby/mruby/pull/6405) `io_read`: use `%i` instead of `%d` in call to `mrb_raisef` - [#6407](https://github.com/mruby/mruby/pull/6407) Allow to exclude specific files in `rake install` - [#6408](https://github.com/mruby/mruby/pull/6408) Improve compliance with C++ standards - [#6410](https://github.com/mruby/mruby/pull/6410) Put `#include ` in `parse.y` - [#6412](https://github.com/mruby/mruby/pull/6412) Distinguish the call frame of the generator with `OP_RETURN_BLK` - [#6413](https://github.com/mruby/mruby/pull/6413) Add links to documentation in `README.md` - [#6415](https://github.com/mruby/mruby/pull/6415) Fix numbered parameters when used as a singleton - [#6416](https://github.com/mruby/mruby/pull/6416) Optimize the "new" method's iseq - [#6419](https://github.com/mruby/mruby/pull/6419) Follow Ruby's behavior for numbered parameters in -> {} - [#6420](https://github.com/mruby/mruby/pull/6420) Update `labeler.yml`: add label for the `tools` directory - [#6422](https://github.com/mruby/mruby/pull/6422) Add annotations for function names defined in the preprocessor - [#6423](https://github.com/mruby/mruby/pull/6423) Adding a serialized test task - [#6427](https://github.com/mruby/mruby/pull/6427) Need to update `ci` variable after re-entry to VM - [#6428](https://github.com/mruby/mruby/pull/6428) Change the limits of OP_ADDI and OP_SUBI from 0-127 to 0-255. - [#6429](https://github.com/mruby/mruby/pull/6429) Fix numbered parameters when used as hash keys - [#6432](https://github.com/mruby/mruby/pull/6432) Moving code in macro arguments out of macros - [#6434](https://github.com/mruby/mruby/pull/6434) Added document "Layout of the mruby filesystem" - [#6436](https://github.com/mruby/mruby/pull/6436) Make `rake doc:update-index` prettier friendly - [#6437](https://github.com/mruby/mruby/pull/6437) Add more details to the pre-commit config - [#6438](https://github.com/mruby/mruby/pull/6438) Remove unused `MRuby::Build#list_install_excludes` method - [#6440](https://github.com/mruby/mruby/pull/6440) Fix `redo` keyword - [#6442](https://github.com/mruby/mruby/pull/6442) Fixed wrong range condition in `OP_JMPUW` - [#6443](https://github.com/mruby/mruby/pull/6443) Fix NODE_NEGATE for bigints - [#6444](https://github.com/mruby/mruby/pull/6444) Add test cases for bigints - [#6446](https://github.com/mruby/mruby/pull/6446) Omit the `_WIN64` definition check - [#6447](https://github.com/mruby/mruby/pull/6447) Fixed `File.expand_path` - [#6448](https://github.com/mruby/mruby/pull/6448) Suppress presym in `mruby/ext/io.h` file - [#6449](https://github.com/mruby/mruby/pull/6449) Using presym in the `mruby-io/src/file_test.c` file - [#6450](https://github.com/mruby/mruby/pull/6450) Change `MRB_WITH_IO_PREAD_PWRITE` configuration name - [#6454](https://github.com/mruby/mruby/pull/6454) mruby-bigint: handle rhs bigint in int_mod and int_divmod functions - [#6455](https://github.com/mruby/mruby/pull/6455) mruby-bigint: fix rounding behavior in mpz_mdiv and mpz_mdivmod functions - [#6457](https://github.com/mruby/mruby/pull/6457) bigint: fix bug with division of a small number by a bigint - [#6459](https://github.com/mruby/mruby/pull/6459) `FileTest` is a module - [#6461](https://github.com/mruby/mruby/pull/6461) To create a release package file in draft - [#6462](https://github.com/mruby/mruby/pull/6462) Properly cast the return value of `memchr()` - [#6463](https://github.com/mruby/mruby/pull/6463) Add `level` argument to `File.dirname` - [#6465](https://github.com/mruby/mruby/pull/6465) Add initial PlayStation portable crossbuild support - [#6468](https://github.com/mruby/mruby/pull/6468) Fixed missing changes to `IB_FIND_BY_KEY()` parameter names - [#6469](https://github.com/mruby/mruby/pull/6469) Add more `const` qualifier for `RProc` - [#6472](https://github.com/mruby/mruby/pull/6472) Moved tests for `Integer#quo` - [#6473](https://github.com/mruby/mruby/pull/6473) Hide `mpz_and()` symbol - [#6473](https://github.com/mruby/mruby/pull/6473) Hide mpz_and() symbol - [#6474](https://github.com/mruby/mruby/pull/6474) Avoid array object creation with “unknown keyword†error - [#6475](https://github.com/mruby/mruby/pull/6475) Don't include deleted mruby-print - [#6478](https://github.com/mruby/mruby/pull/6478) Fixed buffer overrun in function `chars2bytes()` - [#6479](https://github.com/mruby/mruby/pull/6479) Reimplementation of `File.expand_path` method - [#6482](https://github.com/mruby/mruby/pull/6482) Add `File.absolute_path?` method - [#6487](https://github.com/mruby/mruby/pull/6487) Add Emscripten toolchain & build_config nghttp2-1.68.0/third-party/mruby/doc/PaxHeaders/mruby3.1.md0000644000000000000000000000013115077107276020341 xustar0030 mtime=1761382078.098420666 30 atime=1761382080.120411416 29 ctime=1761382108.94830082 nghttp2-1.68.0/third-party/mruby/doc/mruby3.1.md0000644000175100017510000002343715077107276020743 0ustar00runnerrunner# User visible changes in `mruby3.1` from `mruby3.0` # New Features ## Core Language Features ### Keyword Arguments CRuby3.0 compatible keyword arguments are introduced. Keyword arguments are basically separated from ordinal arguments. ### Other Language Enhancement - Implement endless-def [Ruby:Feature#16746](https://bugs.ruby-lang.org/issues/16746) - Replace `R-assignment` by `single-line pattern matching` [Ruby:Feature#15921](https://bugs.ruby-lang.org/issues/15921) - Support squiggly heredocs. [#5246](https://github.com/mruby/mruby/pull/5246) - Hash value omission [Ruby:Feature#14579](https://bugs.ruby-lang.org/issues/14579) ## Configuration Options Changed Some configuration macros are available: - `MRB_WORDBOX_NO_FLOAT_TRUNCATE`: by default, float values are packed in the word if possible, but define this macro to allocate float values in the heap. - `MRB_USE_RO_DATA_P_ETEXT`: define this macro if `_etext` is available on your platform. - `MRB_NO_DEFAULT_RO_DATA_P`: define this macro to avoid using predefined `mrb_ro_data_p()` function --- # Updated Features ## New build configurations We have added several new build configurations in the `build_config` directory. - `cross-mingw-winetest.rb` - `cross-mingw.rb` - `nintendo_switch.rb` - `serenity.rb` - `minimal`: minimal configuration - `host-f32`: compiles with `mrb_float` as 32-bit `float` - `host-nofloat`: compiles with no float configuration - `android_arm64_v8a.rb`: renamed from `android_arm64-v8a.rb` ## Core Libraries ### New Methods - `Array#product` - `Array#repeated_combination` - `Array#repeated_permutation` - `Kernel#__ENCODING__` - `Random.bytes` - `Random#bytes` - `String#center` ### New Gem Enhancement - `mrbgems/mruby-pack` now supports `M` directive (Q encoding) - `mrbgems/mruby-pack` now supports `X` directive (back-up by bytes) - `mrbgems/mruby-pack` now supports `@` directive (absolute position) - `mrbgems/mruby-pack` now supports `w` directive (BER compression) ## Tools - `mruby-config` now supports `--cc` and `--ld` options. - Remove `OP_` prefix from `mruby -v` code dump output. - Prohibit use of `OP_EXT{1,2,3}` by `mrbc` with `--no-ext-ops` option. ## Features for mruby Developer - Add new specifier `c` to `mrb_get_args()` for receive Class/Module. --- # Breaking Changes ## Incompatibly Changed Methods - `Kernel#printf` (`mruby-sprintf`) Format specifiers `%a` and `%A` are removed. - `Kernel#puts` (`mruby-print`) Now expand Array arguments. ## mruby VM and bytecode Due to improvements in the binary format, mruby binaries are no longer backward compatible. To run the mruby binaries on mruby 3.1, recompile with the mruby 3.1 `mrbc`. - Upgrade mruby VM version `RITE_VM_VER` to `0300` (means mruby 3.0 or after). - Upgrade mruby binary version `RITE_BINARY_FORMAT_VER` to `0300`. ## Reintroduced Instructions `mruby3.0` removed `OP_EXT1`, `OP_EXT2`, `OP_EXT3` for operand extension. But the operand size limitations was too tight for real-world application. `mruby3.1` reintroduces those extension instructions. ## Removed Instructions `mruby3.1` removed following instructions. - `OP_LOADL16` - `OP_LOADSYM16` - `OP_STRING16` - `OP_LAMBDA16` - `OP_BLOCK16` - `OP_METHOD16` - `OP_EXEC16` Those instructions are no longer needed by reintroduction of extension instructions. - `OP_SENDV` - `OP_SENDVB` Those instructions for method calls with variable number of arguments are no longer needed. They are covered by `OP_SEND` instruction with `n=15`. ## New Instructions `mruby3.1` introduces following new instructions. - `OP_GETIDX`: takes 1 operands `R[a][a+1]` - `OP_SETIDX`: takes 1 operands `R[a][a+1]=R[a+2]` - `OP_SSEND`: takes 3 operands `a=self.b(c...)`; see `OP_SEND` - `OP_SSENDB`: takes 3 operands `a=self.b(c...){...}`; see `OP_SEND` - `OP_SYMBOL`: takes 2 operands `R[a] = intern(Pool[b])` ### `OP_GETIDX` and `OP_SETIDX` Execute `obj[int]` and `obj[int] = value` respectively, where `obj` is `string|array|hash`. ### `OP_SSEND` and `OP_SSENDB` They are similar to `OP_SEND` and `OP_SENDB` respectively. They initialize the `R[a]` by `self` first so that we can skip one `OP_LOADSELF` instruction for each call. ### `OP_SYMBOL` Extracts the character string placed in the pool as a symbol. ## Changed Instructions ### `OP_SEND` and `OP_SENDB` Method calling instructions are unified. Now `OP_SEND` and `OP_SENDB` (method call with a block) can support both splat arguments and keyword arguments as well. The brief description of the instructions: |`OP_SEND` | BBB | `R[a] = R[a].call(Syms[b],R[a+1..n],R[a+n+1],R[a+n+2]..nk) c=n|nk<<4` | |`OP_SENDB` | BBB | `R[a] = R[a].call(Syms[b],R[a+1..n],R[a+n+1..nk],R[a+n+2..nk],&R[a+n+2*nk+2]) c=n|nk<<4` | Operand C specifies the number of arguments. Lower 4 bits (`n`) represents the number of ordinal arguments, and higher 4 bits (`nk`) represents the number of keyword arguments. When `n == 15`, the method takes arguments packed in an array. When `nk == 15`, the method takes keyword arguments are packed in a hash. ### `OP_ARYPUSH` Now takes 2 operands and pushes multiple entries to an array. ## Boxing Updated ### Word Boxing `MRB_WORD_BOXING` now packs floating-point numbers in the word, if the size of `mrb_float` is equal or smaller than the size of `mrb_int` by default. If the size of `mrb_float` and `mrb_int` are same, the last 2 bits in the `mrb_float` are trimmed and used as flags. If you need full precision, you need to define `MRB_WORDBOX_NO_FLOAT_TRUNCATE` as described above. ### NaN Boxing Previous NaN boxing packs values in NaN representation, but pointer retrievals are far more frequent than floating-point number references. So we add constant offset to NaN representation to clear higher bits of pointer representation. This representation is called "Favor Pointer" NaN Boxing. Also, previous NaN boxing limit the size of `mrb_int` to 4 bytes (32 bits) to fit in NaN values. Now we allocate integer values in the heap, if the value does not fit in the 32 bit range, just like we did in Word Boxing. ## Constant Folding The code generator was updated to reduce the number of instructions, e.g. ``` a = 2 * 5 ``` will be interpreted as ``` a = 10 ``` In addition, we have improved peephole optimizations, for example: ``` GETIV R4 :@foo MOVE R1 R4 ``` to ``` GETIV R1 :@foo ``` ## `String#hash` now use `FNV1a` algorithm For better and faster hash values. --- # Major bugfixes - Fix infinite recursive call bugs in integer division [98799aa6](https://github.com/mruby/mruby/commit/98799aa6) - Fix to raise TypeError with super inside instance_eval / class_eval [#5476](https://github.com/mruby/mruby/pull/5476) - Fix to call `method_added` hooks on method definitions; [#2339](https://github.com/mruby/mruby/pull/2339) - Fix a potential buffer overflow in `time_zonename` [26340a88](https://github.com/mruby/mruby/commit/26340a88) - Fix `Module.instance_eval` bug [#5528](https://github.com/mruby/mruby/pull/5528) - Fix fix `M` packing bug [bfe2bd49](https://github.com/mruby/mruby/commit/bfe2bd49) - Fix a bug regarding attribute assignment with kargs [de2b4bd0](https://github.com/mruby/mruby/commit/de2b4bd0) - Fix SIGSEGV with mrbgems/mruby-method [#5580](https://github.com/mruby/mruby/pull/5580) - Fix print error before cleanup in `codegen_error()` [#5603](https://github.com/mruby/mruby/pull/5603) - Fix a bug in unpacking BER [#5611](https://github.com/mruby/mruby/pull/5611) - Fix a bug with numbered parameters as arguments [#5605](https://github.com/mruby/mruby/pull/5605) - Fix `mrb_ary_shift_m` initialization bug [27d1e013](https://github.com/mruby/mruby/commit/27d1e013) - Fix keyword argument with `super` [#5628](https://github.com/mruby/mruby/pull/5628) - Fix a bug with numbered parameters on toplevel [7e7f1b2f](https://github.com/mruby/mruby/commit/7e7f1b2f) - Fix keyword argument bug [#5632](https://github.com/mruby/mruby/issues/5632) - Fix multiple assignments in parameters [#5647](https://github.com/mruby/mruby/issues/5647) - Fix keyword parameters not passing through super [#5660](https://github.com/mruby/mruby/issues/5660) - Fix infinite loop from unclosed here-doc [#5676](https://github.com/mruby/mruby/issues/5676) - Fix negative integer division bug [#5678](https://github.com/mruby/mruby/issues/5678) # CVEs ## Fixed CVEs Following CVEs are fixed in this release. - [CVE-2021-4110](https://www.cve.org/CVERecord?id=CVE-2021-4110) - [CVE-2021-4188](https://www.cve.org/CVERecord?id=CVE-2021-4188) - [CVE-2022-0080](https://www.cve.org/CVERecord?id=CVE-2022-0080) - [CVE-2022-0240](https://www.cve.org/CVERecord?id=CVE-2022-0240) - [CVE-2022-0326](https://www.cve.org/CVERecord?id=CVE-2022-0326) - [CVE-2022-0481](https://www.cve.org/CVERecord?id=CVE-2022-0481) - [CVE-2022-0631](https://www.cve.org/CVERecord?id=CVE-2022-0631) - [CVE-2022-0632](https://www.cve.org/CVERecord?id=CVE-2022-0632) - [CVE-2022-0890](https://www.cve.org/CVERecord?id=CVE-2022-0890) - [CVE-2022-1071](https://www.cve.org/CVERecord?id=CVE-2022-1071) - [CVE-2022-1106](https://www.cve.org/CVERecord?id=CVE-2022-1106) - [CVE-2022-1201](https://www.cve.org/CVERecord?id=CVE-2022-1201) - [CVE-2022-1427](https://www.cve.org/CVERecord?id=CVE-2022-1427) ## Unaffected CVEs Following CVEs do not cause problems in this release. They are fixed in the later release. - [CVE-2022-0481](https://www.cve.org/CVERecord?id=CVE-2022-0481) - [CVE-2022-0525](https://www.cve.org/CVERecord?id=CVE-2022-0525) - [CVE-2022-0570](https://www.cve.org/CVERecord?id=CVE-2022-0570) - [CVE-2022-0614](https://www.cve.org/CVERecord?id=CVE-2022-0614) - [CVE-2022-0623](https://www.cve.org/CVERecord?id=CVE-2022-0623) - [CVE-2022-0630](https://www.cve.org/CVERecord?id=CVE-2022-0630) - [CVE-2022-0717](https://www.cve.org/CVERecord?id=CVE-2022-0817) - [CVE-2022-1212](https://www.cve.org/CVERecord?id=CVE-2022-1212) - [CVE-2022-1276](https://www.cve.org/CVERecord?id=CVE-2022-1276) - [CVE-2022-1286](https://www.cve.org/CVERecord?id=CVE-2022-1286) nghttp2-1.68.0/third-party/mruby/doc/PaxHeaders/internal0000644000000000000000000000013215077107334020172 xustar0030 mtime=1761382108.950300815 30 atime=1761382109.795298372 30 ctime=1761382108.950300815 nghttp2-1.68.0/third-party/mruby/doc/internal/0000755000175100017510000000000015077107334020637 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/doc/internal/PaxHeaders/boxing.md0000644000000000000000000000013215077107276022064 xustar0030 mtime=1761382078.098420666 30 atime=1761382080.120411416 30 ctime=1761382108.950300815 nghttp2-1.68.0/third-party/mruby/doc/internal/boxing.md0000644000175100017510000000650215077107276022457 0ustar00runnerrunner # Boxing The mruby objects and data are represented by C data type `mrb_value`. There are three options how to pack the data values in the `mrb_value`. - Word Boxing - NaN Boxing - No Boxing ## Word Boxing Word boxing packs the Ruby data in a word, which is a natural integer size that equals to the size of pointers (`intptr_t`). Word boxing can be specified by `MRB_WORD_BOXING`, and it's default configuration for most platforms. Some values (called immediate values, e.g. integers, booleans, symbols, etc.) are directly packed in the word. The other data types are represented by pointers to the heap allocated structures. The Word boxing packing bit patterns are like following: | Types | Bit Pattern | | ------ | ------------------------------------- | | object | `xxxxxxxx xxxxxxxx xxxxxxxx xxxxx000` | | fixnum | `xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxx1` | | nil | `00000000 00000000 00000000 00000000` | | true | `00000000 00000000 00000000 00001100` | | false | `00000000 00000000 00000000 00000100` | | undef | `00000000 00000000 00000000 00010100` | | symbol | `xxxxxxxx xxxxxxxx xxxxxxxx xxxxxx10` | On 64-bit platforms (unless `MRB_WORDBOX_NO_FLOAT_TRUNCATE`), float values are also packed in the `mrb_value`. In that case, we drop least significant 2 bits from mantissa. If you need full precision for floating-point numbers, define `MRB_WORDBOX_NO_FLOAT_TRUNCATE`. ## NaN Boxing NaN boxing packs the Ruby data in a floating-point numbers, which represent NaN (Not a Number) values. Under IEEE753 definitions every value that exponent is all set are considered as NaN. That means NaN can represent `2^51` values. NaN boxing is a teaching to pack the values in those NaN representation. In theory, 64-bit pointers are too big to fit in NaN, but practically most OS use only 48 bits at most for pointers (except for some OS e.g. Solaris). The NaN boxing packing bit patterns are like following: | Types | Bit Pattern | | ------ | ------------------------------------------------------------------------- | | float | `SEEEEEEE EEEEFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF` | | +/-inf | `S1111111 11110000 00000000 00000000 00000000 00000000 00000000 00000000` | | nan | `01111111 11111000 00000000 00000000 00000000 00000000 00000000 00000000` | | fixnum | `01111111 11111001 00000000 00000000 IIIIIIII IIIIIIII IIIIIIII IIIIIIII` | | symbol | `01111111 11111110 00000000 00000000 SSSSSSSS SSSSSSSS SSSSSSSS SSSSSSSS` | | misc | `01111111 11111111 00000000 00000000 00000000 00000000 00TTTTTT 0000MMMM` | | object | `01111111 11111100 PPPPPPPP PPPPPPPP PPPPPPPP PPPPPPPP PPPPPPPP PPPPPP00` | | ptr | `01111111 11111100 PPPPPPPP PPPPPPPP PPPPPPPP PPPPPPPP PPPPPPPP PPPPPP01` | | nil | `00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000` | The object values appear far more frequently than floating-point numbers, so we offset the value so that object pointers are unchanged. This technique is called "favor pointer". ## No Boxing No boxing represents `mrb_value` by the C struct with `type` and the value union. This is the most portable (but inefficient) representation. No boxing can be specified by `MRB_NO_BOXING`, and it's default for debugging configuration (e.g. `host-debug`). nghttp2-1.68.0/third-party/mruby/doc/internal/PaxHeaders/opcode.md0000644000000000000000000000013215077107276022047 xustar0030 mtime=1761382078.098420666 30 atime=1761382080.120411416 30 ctime=1761382108.949300818 nghttp2-1.68.0/third-party/mruby/doc/internal/opcode.md0000644000175100017510000002013215077107276022435 0ustar00runnerrunner # The new bytecode We will reimplement the VM to use 8bit instruction code. By bytecode, we mean real byte code. The whole purpose is reducing the memory consumption of mruby VM. # Instructions Instructions are bytes. There can be 256 instructions. Currently, we have 106 instructions. Instructions can take 0 to 3 operands. ## operands The size of operands can be either 8bits, 16bits or 24bits. In the table.1 below, the third field describes the size of operands. - B: 8bit - S: 16bit - W: 24bit If the first and second operands are of type `B` (8bits), they may be extended to 16bits by the operand extension instruction immediately preceding them. See also `OP_EXT1`, `OP_EXT2` and `OP_EXT3`. ## table.1 Instruction Table | No. | Instruction Name | Operand type | Semantics | --: | ---------------- | ------------ | --------------- | 0 | `OP_NOP` | `-` | `no operation` | 1 | `OP_MOVE` | `BB` | `R(a) = R(b)` | 2 | `OP_LOADL` | `BB` | `R(a) = Pool(b)` | 3 | `OP_LOADI8` | `BB` | `R(a) = mrb_int(b)` | 4 | `OP_LOADINEG` | `BB` | `R(a) = mrb_int(-b)` | 5 | `OP_LOADI__1` | `B` | `R(a) = mrb_int(-1)` | 6 | `OP_LOADI_0` | `B` | `R(a) = mrb_int(0)` | 7 | `OP_LOADI_1` | `B` | `R(a) = mrb_int(1)` | 8 | `OP_LOADI_2` | `B` | `R(a) = mrb_int(2)` | 9 | `OP_LOADI_3` | `B` | `R(a) = mrb_int(3)` | 10 | `OP_LOADI_4` | `B` | `R(a) = mrb_int(4)` | 11 | `OP_LOADI_5` | `B` | `R(a) = mrb_int(5)` | 12 | `OP_LOADI_6` | `B` | `R(a) = mrb_int(6)` | 13 | `OP_LOADI_7` | `B` | `R(a) = mrb_int(7)` | 14 | `OP_LOADI16` | `BS` | `R(a) = mrb_int(b)` | 15 | `OP_LOADI32` | `BSS` | `R(a) = mrb_int((b<<16)+c)` | 16 | `OP_LOADSYM` | `BB` | `R(a) = Syms(b)` | 17 | `OP_LOADNIL` | `B` | `R(a) = nil` | 18 | `OP_LOADSELF` | `B` | `R(a) = self` | 19 | `OP_LOADT` | `B` | `R(a) = true` | 20 | `OP_LOADF` | `B` | `R(a) = false` | 21 | `OP_GETGV` | `BB` | `R(a) = getglobal(Syms(b))` | 22 | `OP_SETGV` | `BB` | `setglobal(Syms(b), R(a))` | 23 | `OP_GETSV` | `BB` | `R(a) = Special[Syms(b)]` | 24 | `OP_SETSV` | `BB` | `Special[Syms(b)] = R(a)` | 25 | `OP_GETIV` | `BB` | `R(a) = ivget(Syms(b))` | 26 | `OP_SETIV` | `BB` | `ivset(Syms(b),R(a))` | 27 | `OP_GETCV` | `BB` | `R(a) = cvget(Syms(b))` | 28 | `OP_SETCV` | `BB` | `cvset(Syms(b),R(a))` | 29 | `OP_GETCONST` | `BB` | `R(a) = constget(Syms(b))` | 30 | `OP_SETCONST` | `BB` | `constset(Syms(b),R(a))` | 31 | `OP_GETMCNST` | `BB` | `R(a) = R(a)::Syms(b)` | 32 | `OP_SETMCNST` | `BB` | `R(a+1)::Syms(b) = R(a)` | 33 | `OP_GETUPVAR` | `BBB` | `R(a) = uvget(b,c)` | 34 | `OP_SETUPVAR` | `BBB` | `uvset(b,c,R(a))` | 35 | `OP_GETIDX` | `B` | `R(a) = R(a)[R(a+1)]` | 36 | `OP_SETIDX` | `B` | `R(a)[R(a+1)] = R(a+2)` | 37 | `OP_JMP` | `S` | `pc+=a` | 38 | `OP_JMPIF` | `BS` | `if R(a) pc+=b` | 39 | `OP_JMPNOT` | `BS` | `if !R(a) pc+=b` | 40 | `OP_JMPNIL` | `BS` | `if R(a)==nil pc+=b` | 41 | `OP_JMPUW` | `S` | `unwind_and_jump_to(a)` | 42 | `OP_EXCEPT` | `B` | `R(a) = exc` | 43 | `OP_RESCUE` | `BB` | `R(b) = R(a).isa?(R(b))` | 44 | `OP_RAISEIF` | `B` | `raise(R(a)) if R(a)` | 45 | `OP_SSEND` | `BBB` | `R(a) = self.send(Syms(b),R(a+1)..,R(a+n+1):R(a+n+2)..) (c=n\|k<<4)` | 46 | `OP_SSENDB` | `BBB` | `R(a) = self.send(Syms(b),R(a+1)..,R(a+n+1):R(a+n+2)..,&R(a+n+2k+1))` | 47 | `OP_SEND` | `BBB` | `R(a) = R(a).send(Syms(b),R(a+1)..,R(a+n+1):R(a+n+2)..) (c=n\|k<<4)` | 48 | `OP_SENDB` | `BBB` | `R(a) = R(a).send(Syms(b),R(a+1)..,R(a+n+1):R(a+n+2)..,&R(a+n+2k+1))` | 49 | `OP_CALL` | `-` | `self.call(*, **, &) (But overlay the current call frame; tailcall)` | 50 | `OP_SUPER` | `BB` | `R(a) = super(R(a+1),... ,R(a+b+1))` | 51 | `OP_ARGARY` | `BS` | `R(a) = argument array (16=m5:r1:m5:d1:lv4)` | 52 | `OP_ENTER` | `W` | `arg setup according to flags (23=m5:o5:r1:m5:k5:d1:b1)` | 53 | `OP_KEY_P` | `BB` | `R(a) = kdict.key?(Syms(b))` | 54 | `OP_KEYEND` | `-` | `raise unless kdict.empty?` | 55 | `OP_KARG` | `BB` | `R(a) = kdict[Syms(b)]; kdict.delete(Syms(b))` | 56 | `OP_RETURN` | `B` | `return R(a) (normal)` | 57 | `OP_RETURN_BLK` | `B` | `return R(a) (in-block return)` | 58 | `OP_BREAK` | `B` | `break R(a)` | 59 | `OP_BLKPUSH` | `BS` | `R(a) = block (16=m5:r1:m5:d1:lv4)` | 60 | `OP_ADD` | `B` | `R(a) = R(a)+R(a+1)` | 61 | `OP_ADDI` | `BB` | `R(a) = R(a)+mrb_int(b)` | 62 | `OP_SUB` | `B` | `R(a) = R(a)-R(a+1)` | 63 | `OP_SUBI` | `BB` | `R(a) = R(a)-mrb_int(b)` | 64 | `OP_MUL` | `B` | `R(a) = R(a)*R(a+1)` | 65 | `OP_DIV` | `B` | `R(a) = R(a)/R(a+1)` | 66 | `OP_EQ` | `B` | `R(a) = R(a)==R(a+1)` | 67 | `OP_LT` | `B` | `R(a) = R(a)R(a+1)` | 70 | `OP_GE` | `B` | `R(a) = R(a)>=R(a+1)` | 71 | `OP_ARRAY` | `BB` | `R(a) = ary_new(R(a),R(a+1)..R(a+b))` | 72 | `OP_ARRAY2` | `BBB` | `R(a) = ary_new(R(b),R(b+1)..R(b+c))` | 73 | `OP_ARYCAT` | `B` | `ary_cat(R(a),R(a+1))` | 74 | `OP_ARYPUSH` | `BB` | `ary_push(R(a),R(a+1)..R(a+b))` | 75 | `OP_ARYSPLAT` | `B` | `R(a) = ary_splat(R(a))` | 76 | `OP_AREF` | `BBB` | `R(a) = R(b)[c]` | 77 | `OP_ASET` | `BBB` | `R(b)[c] = R(a)` | 78 | `OP_APOST` | `BBB` | `*R(a),R(a+1)..R(a+c) = R(a)[b..]` | 79 | `OP_INTERN` | `B` | `R(a) = intern(R(a))` | 80 | `OP_SYMBOL` | `BB` | `R(a) = intern(Pool(b))` | 81 | `OP_STRING` | `BB` | `R(a) = str_dup(Pool(b))` | 82 | `OP_STRCAT` | `B` | `str_cat(R(a),R(a+1))` | 83 | `OP_HASH` | `BB` | `R(a) = hash_new(R(a),R(a+1)..R(a+b*2-1))` | 84 | `OP_HASHADD` | `BB` | `hash_push(R(a),R(a+1)..R(a+b*2))` | 85 | `OP_HASHCAT` | `B` | `R(a) = hash_cat(R(a),R(a+1))` | 86 | `OP_LAMBDA` | `BB` | `R(a) = lambda(Irep(b),OP_L_LAMBDA)` | 87 | `OP_BLOCK` | `BB` | `R(a) = lambda(Irep(b),OP_L_BLOCK)` | 88 | `OP_METHOD` | `BB` | `R(a) = lambda(Irep(b),OP_L_METHOD)` | 89 | `OP_RANGE_INC` | `B` | `R(a) = range_new(R(a),R(a+1),FALSE)` | 90 | `OP_RANGE_EXC` | `B` | `R(a) = range_new(R(a),R(a+1),TRUE)` | 91 | `OP_OCLASS` | `B` | `R(a) = ::Object` | 92 | `OP_CLASS` | `BB` | `R(a) = newclass(R(a),Syms(b),R(a+1))` | 93 | `OP_MODULE` | `BB` | `R(a) = newmodule(R(a),Syms(b))` | 94 | `OP_EXEC` | `BB` | `R(a) = blockexec(R(a),Irep(b))` | 95 | `OP_DEF` | `BB` | `R(a).newmethod(Syms(b),R(a+1)); R(a) = Syms(b)` | 96 | `OP_ALIAS` | `BB` | `alias_method(target_class,Syms(a),Syms(b))` | 97 | `OP_UNDEF` | `B` | `undef_method(target_class,Syms(a))` | 98 | `OP_SCLASS` | `B` | `R(a) = R(a).singleton_class` | 99 | `OP_TCLASS` | `B` | `R(a) = target_class` | 100 | `OP_DEBUG` | `BBB` | `print a,b,c` | 101 | `OP_ERR` | `B` | `raise(LocalJumpError, Pool(a))` | 102 | `OP_EXT1` | `-` | `make 1st operand (a) 16bit` | 103 | `OP_EXT2` | `-` | `make 2nd operand (b) 16bit` | 104 | `OP_EXT3` | `-` | `make 1st and 2nd operands 16bit` | 105 | `OP_STOP` | `-` | `stop VM` nghttp2-1.68.0/third-party/mruby/doc/PaxHeaders/mruby_logo_red_icon.png0000644000000000000000000000013215077107276023166 xustar0030 mtime=1761382078.099420661 30 atime=1761382080.121411411 30 ctime=1761382108.945300829 nghttp2-1.68.0/third-party/mruby/doc/mruby_logo_red_icon.png0000644000175100017510000001233315077107276023560 0ustar00runnerrunner‰PNG  IHDRddpâ•T pHYs  šœsRGBÙÉ,€IDATxÚí] xŒwŸ&TP"â¨vId›¢DDZm¥‡«EiQê¨Rª¶î–§ÛC—’Ö.=ô «µ[Ôö¢u&H!B$$G9äÄ‘Cîx÷ç}¿trÍdf>sÐyŸç÷„ÉÌ—ï{ÿ÷ü£±‹]ìb»ØÅ.v±‹] —]5]€qÀÛÀó€g@+`0xðíš»¹$4IðÒÐ¾Ž€'p¿†ö?€×º2®UÃEåóNvmª'ãµޢؽ÷jhW# ù7ÓP ;ˆèB:(¯7ýUCÁÝäýÁ¾øÝ_4´»5þ×p¼înת¢¸ÇÉÀÖàî¢ü âðã-èÜ’itqÛzÊ=q˜òÏœ üغr$Ò~üŠbç‡å8ÒΆlAŒ˜yÃ)üùäï¬!…Øãv-ëV¾;0XüD%€Œp±çå¾ÿ˜ ‘’+Ù»` ù»ÀbšÞ d¿5u8ílŒkúðµ—þ”{'Ðx xè¸)¿s¼€eÀ6 Q/1 ÐC\ËN'ül%Š òtåð2V¿X,¤À².·ä¿¡¸´·# M€iÀ. [®øÿuàtE0XA÷i( 9”…ëßpÏ kp¤cÃ;ѹ§Ó™÷^a’vÝ¥¡¸f‘©rüÅî´E£Å¼LÉø}_SIÂn72fÃ'ÅhEÁÎ@3 ©†]D@ öå⿽$r?œòþûxº°þºº‡Š/e"ˆ {io[!äâïÿ!S%ö­Ñô+‰™3š*$êµhgƒ?\×¢[Uù€'0Xæ`ëÆ.VŸNLîMq~3)å»)uç”ôõtöS1J½ä} %E݃ŒèÌ;IŸ\>€¸ÁÃbf©1ö!úM£ab*¤8;÷ëB{ÚüẺÝ*Å×ËÀ?  ÊÁ6Àõ†ë©OÉkޣҜˤO ÏÀÆÐîâž\4ðñ‹ê$$ÐÁÿ7V’Ö| ë”"„h%ý—uLöþÎü<Á¶LÄH Nb’ã·Ó 8òç {ð! ¾!=ÛPQZ<#Ù[(°=>ß–r·†òbÃõ"÷‹’,ë?~dˆ”äÓÙ÷'!Fq¬‚ëBªËÉÉCٺ׵ÐÉø*ØGÜ |7bĆÃ}œ)rb/Œèwéø(&Je¿1aä.fëÚQ{éÂÚ ñ|«Ô!}\ñ™×)sçR‡œŽdäD„PƯÿ¦Óo¿å:)u+›Ÿ%öÍš„e$!žÝÅiup7N>:Û3ñà°Ü z?lyq!UÈ©ÙÏÑVøä#ÝézY™"×ÎE³¢w8j(zúpªM.íßÎ$.¨\©7‹ jßwºqÍåó`‚µŸÁûqߣ©6IÝ´†‰VþÎ^[!£'kà¸zl?é’“SŸ¢í©˜ËòsÈÁh>ˆšƒÅ™Vm’6ŠY½/U½—€ï1ȨÌ'ë\ÙuͱBÆy]¨j_ }5íiNi9(¯zŸL‘è™Ïc„ó5øzº$⥴ãNvYP”)‚ÑÏ-]Rx!nÚ .’-«ôFqkmBƹ¸!:2 E=„H°ô”Àœøå»d°”—ÁuŒB•î_!ùçŽsjàj2)leGžnKÖ-'¤áÜïÊ9LåE…TIЖùBÒ÷.ü¹Ö&d(ŽÑÁ7unÉlýâRÑÖ@6ÀÐr(0šÊKKªrPX@¹Qatþó…ÈΚIªÙ…] ,D·ËÒ÷­(%Ø×©¢n0ø[b%»œpzcØõr¦3(JóÏžÔº®ñ9P\×ëÖ$¤pA¾ƒºN?1é &D­}'<ˆ‡’Q(fnßH…©‰d¬ÄÌ×'V™ºa™"QÓ‡±W@öˆ˜‹€ŸO¨k¶ì<Üo;Kâ üRÝà°…µT¸ Ð-iûìôûØŸsbz¬ ¶šˆ1ÝéâÖÿ6ÂÏp­Ân*fÎ2UJ.g¡М~× ž,šIdd~´Së ¶XcNcCe¥q€w¥]‹?Uó!.]D‘ØY‹(ïÕVÕ^ú-F^»ƒú:òÏ@©²·;wH6Ø $}réÀvŽS°4 ¿!ˆê6}ôëÏQ…„¿Ð‹ŸOñ-IH=à+ ª"}DÙ'^¢§+ë“ïO¬¯Ìöqfƒ@ËD0Ix(dDtjÖ`Jûi5Ç c°´üLà´8ã·utzá(y'’±]\Aôõ»ØÝ$}µˆT§ß¿iª¤áÜø”Ž3ÐËÐÉ=–$å£Zóø¤É˜¹ã'Ò'¥9W0MºïÛHg?x•‚xy[‚åÚLÃ:µ×ò0[8ms!wŸgC*J×ÛÞ}7â]Ÿ6T‚ o‚ óÚ!“akªç?YÄnUɲ$!ï 5Ý *eÔž„oØ\öªw„ȸeóÈIùn%,޳pÓCˆ¶êØ®4•Œ‘+a»‘–;¡ãËÏY[ç™ØCìj×õ’¥™£3+þüÊ÷¨g%ÈÎÚr<86‹T+„ ມ³\ª‹I,|81@*íH ÿš…®A”î{¼^Žê>Š™;Œ-‘g»+ƒWkvàb™°@¶”L褥%™¢“o.ðà†êSAÒÙ:ªêdkõi»ÃŒe ©‘äo>äXrÀÛ ñ&½¦« ÞE !JÌ“¬Ë ?Ñm8úŒÜå3p¡S(þãÙ„ic(üI:ÜÏi²¸(™3‘ÏâÑ9Lµ>¿°²ëÚh BFë"DV ô’‡›¾ƒo>zæPR#q~oÀÒøïãºçjº›Ð} !5‘/Ï.G©5/$Ù®Ú=àb0fÞhÒ%aƒ¼ùz £ÌMȳu6ê:ÉelÖÓÊN@ÜqìÄMƒ@¿‹LÌÁC± x$ãz|ÝZ¦j‘ݵfRdäš ©»h`•+H—ä?’%…ÇßKÅç\ÌÚ`4ä¦ágÑÎîDðÄz A†ÅŠ º¿>ê…-d„ þ§‡ïán®£ƒ‘„þãŒX†‰døpß VÕIAš~«]2—­_)†×™»ÁHuÂ[|Ò—Ëtr Ä(R–²U!@÷£ì -˜eÌ¥Z„ÛöW¡5š§nlùó¸ŽÖeéðçaR¤­o<°fv[ê..q£(Z;²å*àasÒÙPBОG›Ã‰‹»êR B¼@È}Z«âˆM85EËÅ í_´:†ó"9ÌP¢™×SÂÍ$Ð6«?­Ò­j‘ º³¦Ò"ë¾H$8uÅ߯ÛZ?ð 2¸ðwQ°~F Ò]¢ƒN²ZÅ\„¸¥†*i[G¿1†ª :°ðëw¡º­N¤Œ`YÄAùüJd’A\õ@+¯‡<â"–¥_0¿¥Ð'ZÁ"% h.<ȸ+nî"ø;K¼ÀïP™÷¦kqÑF® Á5…PÜãbsnˆÉ3¦A·ÂUnu‰÷4íäöµÚ@+­›ÈIƒÈPAÁ=®s‹§ÁDŠÛš‚}ï¤`\ï`Fˆ ý°¤é=&ÂX)JK¤£ÏvªœHÌ0!w— V–bð§5‹¿ì}[1úeTŠO7Þ캘جݛÉD;+ÅÜI:\é9n­\//7ü³åe„Æ*·ƒ>YÀq0ÐÃ^eŽŒ9Œ‰Æ)M\WÒšåµÔ³hÙEi)Úùz¬ÿB_k Y@@\ [ +Å›¨·úSè“­1($³ÄÀ…ƒž5¦†š3ÓŠ6’i]tnBÅS¨º$}ý9ûk ÔÞXEG)à`a¨Žg˜Iù¥¼fó.˜ï™ËÞо®¢üæP>¯àçgã×d:A'z›“Pãsx±’˜¹©6É‹9† ½”ÕïÒŠGV#ÁÖƒ6Ò\é ã÷ÜNÛSà›!åÅE¸‡pÊØ¼–g'O¾ÊÊgwªÝ>!÷£U¾Qð2'!&γ_½|0@wå}þ4÷¦N͆º¡ îÛšƒì¡Þ®è¦väFbâªwyäªWþ·Üv91©¯Kò`¥WU~­òU hgNB¶šZé¢ÇÅ>hŒòd³©¬ð™ ü¹Ü“G(ýç5XGöVÉ<ƵLå7Sj¦Ž:”¯9€³9 Ù¤âæØå¤¬ûŒn¶` 1·À1M åOçBë½à+)ßÅå«GÐÀœ„|˜š¦JnÞµg§©P~.åD†PÚÿ¾ä™Çˆ—zPÈ£j*ßMQ~W«"ÚÜßjnPZôà ‘ÒÜ+¼Ò>uÃg˜;™ŒÅk³ò÷¶Ó¶Í\¥ÂåÛBÌMÈb•7ˆ˜+xéA”‹‘^Š_^RÄ‹×rƒ)uãg¨-&ñîØ da5åKè–Àvsò ºÝ±¿‹¸Ìðˆ?ÔÇít'ù2QĤqÑ(“K·,6™ýL‘›v³Þ<¡%k»ÚZ`ä{éƒÙýjs2 [²Àšƒ¼ì‰tšià4¯ô{g$ÚVŽzø™›·>\Ø!Í–"ïÄ+=)yíRÔ$ßPê«6|Ê‹ï²~æB1v¢¦?%VÝ´ÍA•X`nBúÛ4ÞLF½ fÞ0º–CFodÅœˆt¢½UßÓdKlþ$›' Ü7CQصÊÒ'Éq¼{«69ÿÉÛp_7å¾F››o›ÊtkôŒ!„UsT‡ uÞõP­K–تd«›ê{hnB²z%u½~2V¼… 0Õ÷ºÞÒ[¥'Yƒ OîÈ´ú$e§ÅÜ,ôÑV;ÿEÒ'8‚$ª¾×O­qÂÃ1‹"ÛêÜ:3w$ý*‡ÇÒ(ä =ô)7½§ÝÅÌy3ù‡5yβ„È,âAßX y*KY^§´XìÆYÒ…ïW¢Fé†)cY€×TÈÜßQ&Ãr"Žè=ON4R…¹Ö:˜&À¢„¸ßØ~à¬0“†-ÑèÄ˽ñZCÂŽ*ÞÜŸ¸ƒáÃR‘DM{’÷¥`ä³å$®ZN:„WÅïn©ú^Ç[‹Ç-GˆL8îÛŠ p[qØÙ$íÅJ<Òß“tŠÈLÞ#öŒ† ]‚SµáêTßëóÖ<àìGË"1 lP{R›‚FWì5áŸgߟnð2XÑ^ëIX¾Š¬Lõ½ö³&!>–!Db‡ìViûX ^ãì²²·’)ÉÎįFˆUªïÕ×Úç2®¶!ÞÕ6uöàS‚BzºR)‚»)Hˆ½Y“SžÖ&Ä(6{ q»1EëRåLàÔ_#PËAj%÷d(ÈPÝéÍ\má|_?ó7¥’þë·•Vºçó„ÄUJÑž¢nXÐØV¾*Ûܳ„|®ŠeüHŠðÊwl?бÑó:oO(ÎLÅžÀ,a=„½*»P¯üÏçJ^뇃s †™M‘ãŵU÷±ÎÚÒñ ,09…,HšŒc»Sò¿—2!I«c›Û"37þœxåqNÂypætз>„äÁ;±¸Å.ÇDM•#f[ñõÕ. µµï™Š·À$•¸/WY·»5ÿ› ?9LFËÑI턌~9§÷³.#ýÜæ¾WÐmyQ¬Õ‘g“߃XqðòŸ YÀƒíùZ·?Š€£À|ÀÉÖ¿§jÿmH@°xè¯bS§Uyú6 Ø ¼ ôRoÖ'eË-FÀy`0ð¾½¾Ö[yȆ•ˆV-ײ>)ßÙÅ@(° ÜûgýÆç@¹ÈöK€Á€«ýû·µ¤¬´P°xè4¶k^7!m̰r>ؼøŽvMGÊ •ÄkÉ@'»Fo‚(£™ @¬Rªþ¶ví™”{¥À (Ò}À2`ÐÒ®)»ØÅ.v±‹]ìb»¨—ÿ@þ¦ÓžÝªIEND®B`‚nghttp2-1.68.0/third-party/mruby/doc/PaxHeaders/guides0000644000000000000000000000013215077107334017636 xustar0030 mtime=1761382108.967300765 30 atime=1761382109.795298372 30 ctime=1761382108.967300765 nghttp2-1.68.0/third-party/mruby/doc/guides/0000755000175100017510000000000015077107334020303 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/doc/guides/PaxHeaders/link.md0000644000000000000000000000013215077107276021177 xustar0030 mtime=1761382078.098420666 30 atime=1761382080.120411416 30 ctime=1761382108.963300777 nghttp2-1.68.0/third-party/mruby/doc/guides/link.md0000644000175100017510000000470415077107276021574 0ustar00runnerrunner # Linking `libmruby` to your application You have two ways to link `libmruby` to your application. - using executable gem. - using normal compilation process ## Executable Gems If your application is relatively small, `mrbgem` is an easier way to create the executable. By tradition, the gem name start with `mruby-bin-`, e.g. `mruby-bin-debugger`. ### `mrbgem.rake` file The executable name is specified in `mrbgem.rake` file at the top of your `mrbgem` directory. ```ruby MRuby::Gem::Specification.new('mruby-bin-example') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Example for executable command gem' spec.bins = %w(mruby-example) # <- this is binary name end ``` ### Source tree structure The source file for the gem executable should be in `/tools/`. Currently, we support C or C++ source code (`.c`, `.cpp`, `.cxx`, `.cc`) for the executable. Ruby source files are not supported. Put the functionality in the different gem and specify dependency to it in `mrbgem.rake`. ## Normal compilation process The `libmruby` is a normal library so that you can just link it to your application. Specify proper compiler options (`-I` etc.) and linker options (`-lmruby` etc.) to compile and link your application. Specify those options in your build script (e.g. `Makefile`). ### Compiler options You need to specify compiler options that are compatible to mruby configuration, for example: - `-I` to specify the place for mruby header files - `-D` to specify mruby configuration macros To retrieve compiler options used to build `mruby`, you can use `mruby-config` command with following options: - `--cc` compiler name - `--cflags` options passed to compiler ```console $ mruby-config --cflags -std=gnu99 -g -O3 -Wall -DMRB_GC_FIXED_ARENA -I/home/matz/work/mruby/include -I/home/matz/work/mruby/build/host/include ``` ### Linker options Just like compiler options, you need to specify linker options that are compatible to mruby configuration. To retrieve linker options, you can use `mruby-config` with following options: - `--ld` linker name - `--ldflags` options passed to linker - `--ldflags-before-libs` options passed to linker before linked libraries - `--libs` linked libraries ```console $ mruby-config --ldflags -L/home/matz/work/mruby/build/host/lib $ mruby-config --ldflags-before-libs # $ mruby-config --libs -lmruby -lm ``` nghttp2-1.68.0/third-party/mruby/doc/guides/PaxHeaders/debugger.md0000644000000000000000000000013215077107276022026 xustar0030 mtime=1761382078.098420666 30 atime=1761382080.119411421 30 ctime=1761382108.961300783 nghttp2-1.68.0/third-party/mruby/doc/guides/debugger.md0000644000175100017510000001717215077107276022426 0ustar00runnerrunner # How to Use the mruby Debugger copyright (c) 2014 Specified Non-Profit Corporation mruby Forum ## 1. Summary This file documents the mruby debugger ('mrdb') methods. ## 2 Debugging with mrdb ## 2.1 Building mrdb The trunk of the mruby source tree, with the most recent mrdb, can be checked out with the following command: ```bash $ git clone https://github.com/mruby/mruby.git ``` To run the `make` command: ```bash $ cd mruby $ make ``` By default, the `make` command will install the debugger files into mruby/bin. You can add the path for mrdb on your host environment with the following command: ```bash $ echo "export PATH=\$PATH:MRUBY_ROOT/bin" >> ~/.bashrc $ source ~/.bashrc ``` `*MRUBY_ROOT` is the directory in which mruby source code will be installed. To confirm mrdb was installed properly, run mrdb with the `--version` option: ```bash $ mrdb --version mruby 3.4.0 (2025-04-20) ``` ## 2.2 Basic Operation ### 2.2.1 Debugging mruby Script Files (rb file) with mrdb To invoke the mruby debugger, just type `mrdb`. To specify the script file: ```bash $ mrdb [option] filename ``` For example: Debugging sample.rb ```bash $ mrdb sample.rb ``` You can execute the shell commands listed below: | command | description | | :--------------: | :------------------------------------------------------------------------ | | run | execute programs | | step | execute stepping | | continue | execute continuing program | | break | configure the breaking point | | delete | deleting the breaking points | | disable | disabling the breaking points | | enable | enabling the breaking points | | info breakpoints | showing list of the breaking points | | print | evaluating and printing the values of the mruby expressions in the script | | list | displaying the source cords | | help | showing help | | quit | terminating the mruby debugger | ### 2.2.2 Debugging mruby Binary Files (mrb file) with mrdb You can debug the mruby binary files. #### 2.2.2.1 Debugging the binary files - notice To debug mruby binary files, you need to compile mruby files with option `-g`. ```bash $ mrbc -g sample.rb ``` You can debug the mruby binary files with following command and the option `-b`. ```bash $ mrdb -b sample.mrb ``` Then you can execute all debugger shell commands. #### Break Command You can use any breakpoint to stop the program by specifying the line number and method name. The breakpoint list will be displayed after you have set the breakpoint successfully. Usage: ``` break [file:]linenum b [file:]linenum break [class:]method b [class:]method ``` The breakpoint will be ordered in serial from 1. The number, which was given to the deleted breakpoint, will never be given to another breakpoint again. You can give multiple breakpoints to the specified the line number and method. Be aware that the breakpoint command will not check the validity of the class name and method name. You can get the current breakpoint information by the following options. breakpoint breakpoint number : filename. line number breakpoint breakpoint number : [class name,] method name #### Continue Command Usage: ``` continue [N] c [N] ``` N: the next breakpoint number When resuming the program, it will stop at breakpoint N (N-1 breakpoint will be ignored). When you run the `continue` command without specifying N, the program will be stopped at the next breakpoint. Example: ``` (foo.rb:1) continue 3 ``` This will resume the program and stop it at the third breakpoint. #### Delete Command This will delete the specified breakpoint. Usage: ``` delete [breakpoint-no] d [breakpoint-no] ``` breakpoint-no: breakpoint number Example: ``` (foo.rb:1) delete ``` This will delete all the breakpoints. ``` (foo.rb:1) delete 1 3 ``` This will delete the breakpoint at 1 and 3. #### Disable Command This will disable the specified breakpoint. Usage: ``` disable [breakpoint-no] dis [breakpoint-no] ``` reappointing: breakpoint number Example: ``` (foo.rb:1) disable ``` Use `disable` if you would like to disable all the breakpoints. ``` (foo.rb:1) disable 1 3 ``` This will disable the breakpoints at 1 and 3. #### Enable Command This will enable the specified breakpoints. Usage: ``` enable [breakpoint-no] e [breakpoint-no] ``` breakpoint-no: breakpoint number Example: ``` (foo.rb:1) enable ``` Enabling all breakpoints ``` (foo.rb:1) enable 1 3 ``` Enabling the breakpoint 1 and 3 #### eval command Evaluating the string as source code and printing the value. Same as print command, please see print command. #### help command Displaying the help message. Usage: ``` help [command] h [command] ``` Typing `help` without any options will display the command list. #### Info Breakpoints Command Displaying the specified breakpoint information. Usage: ``` info breakpoints [breakpoint-no] i b [breakpoint-no] ``` breakpoint-no: breakpoint number Typing "info breakpoints" without ant option will display all breakpoint information. Example: ``` (sample.rb:1) info breakpoints Num Type Enb What 1 breakpoint y at sample.rb:3 -> filename,line number 2 breakpoint n in Sample_class:sample_class_method -> [class:]method name 3 breakpoint y in sample_global_method ``` Displaying the specified breakpoint number: ``` (foo.rb:1) info breakpoints 1 3 Num Type Enb What 1 breakpoint y at sample.rb:3 3 breakpoint y in sample_global_method ``` #### List Command To display the code of the source file. Usage: ``` list [filename:]first[,last] l [filename]:first[,last] ``` first: the opening row number last : the closing row number When you specify the `first`, but not the `last` option, you will receive 10 rows. When you do not specify both the `first` and `last` options, you will receive the next 10 rows. Example: ``` Specifying filename and first row number sample.rb:1) list sample2.rb:5 ``` Specifying the filename and the first and last row number: ``` (sample.rb:1) list sample2.rb:6,7 ``` #### Print Command Evaluating the string as source code and printing the value. Usage: ``` print [expr] p [expr] ``` expr: expression The expression is mandatory. The displayed expressions will be serially ordered from 1. If an exception occurs, the exception information will be displayed, and the debugging will be continued. Example: ``` (sample.rb:1) print 1+2 $1 = 3 (sample.rb:1) print self $2 = main ``` Below is the case of the exception: ``` (sample.rb:1) print (1+2 $1 = SyntaxError: line 1: syntax error, unexpected $end, expecting ')' ``` #### Quit Command Quitting the debugger. Usage: ``` quit q ``` #### Run Command Running the program and stopping at the first breakpoint. Usage: ``` run r ``` #### Step Command This will run the program step by step. When the method and the block are invoked, the program will stop at the first row. The program, which is developed in C, will be ignored. nghttp2-1.68.0/third-party/mruby/doc/guides/PaxHeaders/compile.md0000644000000000000000000000013215077107276021672 xustar0030 mtime=1761382078.097420671 30 atime=1761382080.119411421 30 ctime=1761382108.957300794 nghttp2-1.68.0/third-party/mruby/doc/guides/compile.md0000644000175100017510000004741715077107276022277 0ustar00runnerrunner # Compile mruby uses Rake to compile and cross-compile all libraries and binaries. ## Prerequisites To compile mruby out of the source code you need the following tools: - C Compiler (e.g. `gcc` or `clang`) - Linker (e.g. `gcc` or `clang`) - Archive utility (e.g. `ar`) - Ruby 2.5 or later (e.g. `ruby` or `jruby`) Optional: - Git (to update mruby source and integrate mrbgems easier) - C++ compiler (to use mrbgems which include `*.cpp`, `*.cxx`, `*.cc`) - Bison (to compile `mrbgems/mruby-compiler/core/parse.y`) - gperf (to compile `mrbgems/mruby-compiler/core/keywords`) Note that `bison` bundled with macOS is too old to compile `mruby`. Try `brew install bison` and follow the instruction shown to update the `$PATH` to compile `mruby`. We also encourage you to upgrade `ruby` on macOS in similar manner. ## Build To compile `mruby` with the default build configuration, just invoke `rake` inside of the mruby source root. To generate and execute the test tools call `rake test`. To clean all build files call `rake clean`. To see full command line on build, call `rake -v`. You can specify your own configuration file by the `MRUBY_CONFIG` environment variable (you can use `CONFIG` for shorthand for `MRUBY_CONFIG`). If the path doesn't exist, `build_config/${MRUBY_CONFIG}.rb` is used. The default configuration is defined in the `build_config/default.rb` file. Those build configuration files contain the build configuration of mruby, for example: ```ruby MRuby::Build.new do |conf| conf.toolchain :gcc end ``` All tools necessary to compile mruby can be set or modified here. ## Build Configuration We wish you submit a pull-request to `build_config/PLATFORM.rb`, once you created a new configuration for a new platform. Inside the configuration file, the following options can be configured based on your environment. ### Toolchains The mruby build system already contains a set of toolchain templates which configure the build environment for specific compiler infrastructures. #### GCC Toolchain configuration for the GNU C Compiler. ```ruby conf.toolchain :gcc ``` #### clang Toolchain configuration for the LLVM C Compiler clang. Mainly equal to the GCC toolchain. ```ruby conf.toolchain :clang ``` #### Visual Studio 2010, 2012 and 2013 Toolchain configuration for Visual Studio on Windows. If you use the [Visual Studio Command Prompt](), you normally do not have to specify this manually, since it gets automatically detected by our build process. ```ruby conf.toolchain :visualcpp ``` #### Android Toolchain configuration for Android. ```ruby conf.toolchain :android ``` Requires the custom standalone Android NDK and the toolchain path in `ANDROID_STANDALONE_TOOLCHAIN`. ### Binaries It is possible to select which tools should be compiled during the compilation process. For example, - `mruby` - `mirb` The configuration are done via `mrbgems`. See `Mrbgems` section. ### File Separator Some environments require a different file separator character. It is possible to set the character via `conf.file_separator`. ```ruby conf.file_separator = '/' ``` ### Name of library directory In some environments, the `libmruby.a` file requires a different directory name than `lib`. You can be changed to any name by the `conf.libdir_name` accessor. ```ruby conf.libdir_name = 'lib64' ``` Alternatively, it can be changed via the environment variable `MRUBY_SYSTEM_LIBDIR_NAME` when the `rake` command is run. ```console $ export MRUBY_SYSTEM_LIBDIR_NAME=lib64 $ rake clean all ``` NOTES: - This environment variable `MRUBY_SYSTEM_LIBDIR_NAME` does not affect `MRuby::CrossBuild`. In other words, if you want to change it for `MRuby::CrossBuild`, you must set it with `MRuby::CrossBuild#libdir_name=`. - If you want to switch this environment variable `MRUBY_SYSTEM_LIBDIR_NAME`, you must do `rake clean`. A bad usage example is shown below. ```console $ rake clean all $ rake MRUBY_SYSTEM_LIBDIR_NAME=lib64 install ``` ### C Compiler Configuration of the C compiler binary, flags and include paths. ```ruby conf.cc do |cc| cc.command = ... cc.flags = ... cc.include_paths = ... cc.defines = ... cc.option_include_path = ... cc.option_define = ... cc.compile_options = ... end ``` C Compiler has header searcher to detect installed library. If you need an include path of header file use `search_header_path`: ```ruby # Searches `iconv.h`. # If found it will return include path of the header file. # Otherwise it will return nil. fail 'iconv.h not found' unless conf.cc.search_header_path 'iconv.h' ``` If you need a full filename of header file use `search_header`: ```ruby # Searches `iconv.h`. # If found it will return full path of the header file. # Otherwise it will return nil. iconv_h = conf.cc.search_header 'iconv.h' print "iconv.h found: #{iconv_h}\n" ``` Header searcher uses compiler's `include_paths` by default. When you are using GCC toolchain (including clang toolchain since its base is gcc toolchain) it will use compiler specific include paths too. (For example `/usr/local/include`, `/usr/include`) If you need a special header search paths define a singleton method `header_search_paths` to C compiler: ```ruby def conf.cc.header_search_paths ['/opt/local/include'] + include_paths end ``` ### Linker Configuration of the Linker binary, flags and library paths. ```ruby conf.linker do |linker| linker.command = ... linker.flags = ... linker.flags_before_libraries = ... linker.libraries = ... linker.flags_after_libraries = ... linker.library_paths = ... linker.option_library = ... linker.option_library_path = ... linker.link_options = ... end ``` ### Archiver Configuration of the Archiver binary and flags. ```ruby conf.archiver do |archiver| archiver.command = ... archiver.archive_options = ... end ``` ### Parser Generator Configuration of the Parser Generator binary and flags. ```ruby conf.yacc do |yacc| yacc.command = ... yacc.compile_options = ... end ``` ### GPerf Configuration of the GPerf binary and flags. ```ruby conf.gperf do |gperf| gperf.command = ... gperf.compile_options = ... end ``` ### File Extensions ```ruby conf.exts do |exts| exts.object = ... exts.executable = ... exts.library = ... end ``` ### Preallocated Symbols By far, preallocated symbols are highly compatible with the previous versions, so we expect you won't see any problem with them. But just in case you face any issue, you can disable preallocated symbols by specifying `conf.disable_presym`. In the build process, `mrbc` under cross compiling environment will be compiled with this configuration. ### Mrbgems `mruby` comes with the (sort of) packaging system named `mrbgems`. To specify `gem`, you can use `conf.gem` in the configuration file. ```ruby # Integrate a bundled Gem you see in `mrbgems` directory conf.gem :core => 'mruby-something' # Integrate a Gem from GitHub conf.gem :github => 'someone/mruby-another' # Integrate a mruby binary Gem conf.gem :core => 'mruby-bin-mruby' # Integrate a interactive mruby binary Gem conf.gem :core => 'mruby-bin-mirb' # Integrate GemBox (set of Gems) conf.gembox "default" ``` A GemBox is a set of Gems defined in `mrbgems/default.gembox` for example. It's just a set of `mrbgem` configurations. There is a `RubyGem` (gem for CRuby) named `mgem` that help you to manage `mrbgems`. Try `gem install mgem`. `mgem` can show you the list of registered `mrbgems`. See [doc/guides/mrbgems.md](mrbgems.md) for more option about mrbgems. ### Mrbtest Configuration Mrbtest build process. If you want `mrbtest.a` only, You should set `conf.build_mrbtest_lib_only` ```ruby conf.build_mrbtest_lib_only ``` ### Bintest Tests for mrbgem tools using CRuby. To have bintests place `*.rb` scripts to `bintest/` directory of mrbgems. See `mruby-bin-*/bintest/*.rb` if you need examples. If you want a temporary files use `tempfile` module of CRuby instead of `/tmp/`. You can enable it with following: ```ruby conf.enable_bintest ``` ### C++ ABI By default, mruby uses setjmp/longjmp to implement its exceptions. But it doesn't release C++ stack object correctly. To support mrbgems written in C++, mruby can be configured to use C++ exception. There are two levels of C++ exception handling. The one is `enable_cxx_exception` that enables C++ exception, but uses C ABI. The other is `enable_cxx_abi` where all files are compiled by C++ compiler. When you mix C++ code, C++ exception would be enabled automatically. If you need to enable C++ exception explicitly add the following: ```ruby conf.enable_cxx_exception ``` #### C++ exception disabling If your compiler does not support C++, and you want to ensure you don't use mrbgem written in C++, you can explicitly disable C++ exception, add following: ```ruby conf.disable_cxx_exception ``` and you will get an error when you try to use C++ gem. Note that it must be called before `enable_cxx_exception` or `gem` method. ### Debugging mode To enable debugging mode add the following: ```ruby conf.enable_debug ``` When debugging mode is enabled - Macro `MRB_DEBUG` would be defined. - Which means `mrb_assert()` macro is enabled. - Debug information of irep would be generated by `mrbc`. - Because `-g` flag would be added to `mrbc` runner. - You can have better backtrace of mruby scripts with this. ## Cross-Compilation mruby can also be cross-compiled from one platform to another. To achieve cross-compilation, the build configuration needs to contain an instance of `MRuby::CrossBuild`. This instance defines the compilation tools and flags for the target platform. An example could look like this: ```ruby MRuby::CrossBuild.new('32bit') do |conf| conf.toolchain :gcc conf.cc.flags << "-m32" conf.linker.flags << "-m32" end ``` All configuration options of `MRuby::Build` can also be used in `MRuby::CrossBuild`. You can find examples under the `build_config` directory. ### Mrbtest in Cross-Compilation In cross compilation, you can run `mrbtest` on an emulator if you have it by changing configuration of test runner. ```ruby conf.test_runner do |t| t.command = ... # set emulator. this value must be non nil or false t.flags = ... # set flags of emulator def t.run(bin) # override `run` if you need to change the behavior of it ... # `bin` is the full path of mrbtest end end ``` ## Build process During the build process the `build` directory will be created in the root directory. The structure of this directory will look like this: ``` +- build | +- host | +- LEGAL <- License description | +- bin <- Binaries (mirb, mrbc and mruby) | +- lib <- Libraries (libmruby.a and libmruby_core.a) | +- mrbc <- Minimal mrbc place | +- mrbgems <- Compilation result from mrbgems | +- mrblib <- Compilation result from mrblib | +- src <- Compilation result from C sources ``` The compilation workflow will look like this: - compile minimal `mrbc` from `src` and `mrblib` sources - compile all files under `src` (object files will be stored in `build/host/mrbc/src`) - compile `mruby-compiler` gem - create `build/host/mrbc/lib/libmruby_core.a` out of all object files (C only) - create `build/host/mrbc/bin/mrbc` via `mruby-bin-mrbc` gem - compile all files under `src` and store result in `build/host/src` - create `build/host/mrblib/mrblib.c` by compiling all `*.rb` files under `mrblib` with `build/host/mrbc/bin/mrbc` - compile `build/host/mrblib/mrblib.c` to `build/host/mrblib/mrblib.o` - create `build/host/lib/libmruby.a` out of all object files (C and Ruby) - compile (normal) mrbgems specified in the configuration file - create `build/host/lib/libmruby.a` from object files from gems and `libmruby_core.a` - create binary commands according to binary gems (e.g. `mirb` and `mruby`) - copy binaries under `build/host/bin` to `bin` directory ``` _____ _____ ______ ____ ____ _____ _____ ____ | CC |->|GEN |->|AR |->|CC |->|CC |->|AR |->|CC |->|CC | | *.c | |y.tab| |core.a| |mrbc| |*.rb| |lib.a| |mruby| |mirb| ----- ----- ------ ---- ---- ----- ----- ---- ``` ### Cross-Compilation In case of a cross-compilation to `i386` the `build` directory structure looks like this: ``` +- build | +- host | | | +- bin <- Native Binaries | | | +- lib <- Native Libraries | | | +- mrbgems | | | +- src | +- i386 | +- bin <- Cross-compiled Binaries | +- include <- Header Directory | +- lib <- Cross-compiled Libraries | +- mrbgems | +- mrblib | +- src ``` An extra directory is created for the target platform. In case you compile for `i386` a directory called `i386` is created under the build directory. The cross compilation workflow starts in the same way as the normal compilation by compiling all _native_ libraries and binaries, except for we don't have `host/mrbc` directory (`host` directory itself works as placeholder for `mrbc`). Afterwards the cross compilation process proceeds like this: - cross-compile all files under `src` and store result in `build/i386/src` - create `build/i386/lib/libmruby_core.a` out of C object files - create `build/i386/mrblib/mrblib.c` by compiling all `*.rb` files under `mrblib` with native `build/host/bin/mrbc` - cross-compile `build/i386/mrblib/mrblib.c` to `build/i386/mrblib/mrblib.o` - create `build/i386/lib/libmruby.a` from object files from gems and `libmruby_core.a` - create binary commands according to binary gems (e.g. `mirb` and `mruby`) - copy binaries under `build/host/bin` to `bin` directory ``` _______________________________________________________________ | Native Compilation for Host System | | _____ ______ _____ ____ ____ _____ | | | CC | -> |AR | -> |GEN | -> |CC | -> |CC | -> |AR | | | | *.c | |core.a| |y.tab| |mrbc| |*.rb| |lib.a| | | ----- ------ ----- ---- ---- ----- | --------------------------------------------------------------- || \||/ \/ ________________________________________________________________ | Cross Compilation for Target System | | _____ _____ _____ ____ ______ _____ | | | CC | -> |AR | -> |CC | -> |CC | -> |AR | -> |CC | | | | *.c | |lib.a| |mruby| |mirb| |core.a| |mrbc | | | ----- ----- ----- ---- ------ ----- | ---------------------------------------------------------------- ``` ## Build Configuration Examples ### Minimal Library To build a minimal mruby library you need to use the Cross Compiling feature due to the reason that there are functions (e.g. stdio) which can't be disabled for the main build. ```ruby MRuby::CrossBuild.new('minimal') do |conf| conf.toolchain :gcc conf.cc.defines << 'MRB_NO_STDIO' end ``` This configuration defines a cross compile build called 'minimal' which is using the GCC and compiles for the host machine. It also disables all usages of stdio and doesn't compile any binaries (e.g. `mrbc`). ## Test Environment mruby's build process includes a test environment. In case you start the testing of mruby, a native binary called `mrbtest` will be generated and executed. This binary contains all test cases which are defined under `test/t`. In case of a cross-compilation an additional cross-compiled `mrbtest` binary is generated. You can copy this binary and run on your target system. ## Embedding `mruby` in Your Application After the build, you will get `libmruby.a`. You can link it to your application. For compiler options and library path, you can use `mruby-config` command for convenience. `mruby-config` command prints the configuration used for `libmruby.a`. ```console $ mruby-config --help Usage: mruby-config [switches] switches: --cc print compiler name --cflags print flags passed to compiler --ld print linker name --ldflags print flags passed to linker --ldflags-before-libs print flags passed to linker before linked libraries --libs print linked libraries --libmruby-path print libmruby path --help print this help ``` For example, when you have a C source file (`c.c`) and try to compile and link it with `libmruby.a`, you can run the following command, ``` `mruby-config --cc --cflags` c.c `mruby-config --ldflags --libs` ``` When you use `make`, add following lines in `Makefile` ``` MRB_CONFIG = CFLAGS = `$(MRB_CONFIG) --cflags` LDFLAGS = `$(MRB_CONFIG) --ldflags` LIBS = `$(MRB_CONFIG) --libs` ``` ## Install To install the files in the `bin`, `include` and `lib` directories generated by the "host" build target into a system directory, do the following: ```console $ rake install ``` If there are multiple build targets in the build configuration file, to install the products of all build targets, do the following: ```console $ rake install:full ``` To install only one of several build targets, e.g., the "its-mine" build target, do the following: ```console $ rake install:full:its-mine ``` To install only the executable files, do the following: ```console $ rake install_bin # only "host" build target $ rake install:bin # all build targets $ rake install:bin:its-mine # only "its-mine" build target ``` ### Installation Directory The installation directory is `/usr/local` for the "host" build target and `/usr/local/mruby/` for the others. To change them, you can set the environment variable `PREFIX` or use `MRuby::Build#install_prefix = dir` in your build configuration file. The `PREFIX` environment variable affects all build targets and changes the `/usr/local` part. The `MRuby::Build#install_prefix` can be set for each individual build target. In this case, the environment variable `PREFIX` is ignored. Also, if the environment variable `DESTDIR` is set, it will prepend to the path obtained by `install_prefix` to determine the final write directory. This is intended for temporary file expansion by the user's package work. --- To summarize: - The default value of the environment variable `PREFIX` is `/usr/local`. - For the "host" build target, the default value of `MRuby::Build#install_prefix` is ``. - For a build target other than "host", the default value of `MRuby::Build#install_prefix` is `/mruby/`. - If the environment variable `DESTDIR` is set, the actual write directory is `/`. ### Excluded files In some cases there are files that you do not want to install. In such cases, add a file path filter to the array object `MRuby::Build#install_excludes` to exclude them. The following is an object that can be defined as a file path filter. The `path` variable that appears is a relative path based on `MRuby::Build#build_dir`. - string objects: files matched by `string.match?(path)` are excluded. - regexp object: files matched by `regexp.match?(path)` are excluded. - proc object: files which return true with `proc.call(path)` are excluded. ```ruby # exclude bin/mrbc conf.install_excludes << exefile("bin/mrbc") # exclude all files under lib/ directory conf.install_excludes << %r(^lib/) # exclude bin/mrbtest, but in this case it is recommended to use string instead of proc conf.install_excludes << proc { |path| path == exefile("bin/mrbtest") } ``` By default, it contains only a proc object to exclude `libmruby_core`. ## Tips - If you see compilation troubles, try `rake clean` first. nghttp2-1.68.0/third-party/mruby/doc/guides/PaxHeaders/memory.md0000644000000000000000000000013215077107276021552 xustar0030 mtime=1761382078.098420666 30 atime=1761382080.120411416 30 ctime=1761382108.958300792 nghttp2-1.68.0/third-party/mruby/doc/guides/memory.md0000644000175100017510000000713515077107276022150 0ustar00runnerrunner # Memory Allocation In mruby, you can customize how memory is allocated in two ways: 1. **Provide your own `malloc()`/`realloc()`/`free()`** 2. **Override `mrb_basic_alloc_func()`** --- ## 1. Provide your own `malloc()`/`realloc()`/`free()` On platforms without a full C standard library —such as many microcontrollers— you may need to supply your own implementations of `malloc()`, `realloc()`, and `free()`. mruby’s allocator calls directly into these functions, so replacing them lets you control **every** allocation and deallocation performed by your entire program, including any third‑party libraries you link against. Keep in mind: - Calling `realloc(NULL, size)` must behave like `malloc(size)`. - Calling `free(NULL)` must be a no‑op. Simply define these three functions in your code (or link against a library that provides them), and mruby — along with all other code in your process — will use your versions automatically. ## 2. Override `mrb_basic_alloc_func()` Inside mruby, all of its own memory allocations go through a single function called mrb_basic_alloc_func() (formerly mrb_default_allocf()). By defining this function in your application before linking, you can intercept and handle **only** the memory operations initiated by mruby itself without affecting other libraries or parts of your program. ```c // Example signature: // void* mrb_basic_alloc_func(void* ptr, size_t size); ``` Implement mrb_basic_alloc_func() in your code, and mruby will invoke it for every internal allocation, reallocation, and free request. ### Expected behavior - `mrb_basic_alloc_func(NULL, size)` should allocate `size` bytes, just like `malloc(size)`. - `mrb_basic_alloc_func(ptr, size)` should resize the existing block at `ptr` to `size` bytes, just like `realloc(ptr, size)`. - `mrb_basic_alloc_func(ptr, 0)` should free the block at `ptr`, just like `free(ptr)`. --- ## Summary of effects: - **Custom `malloc`/`realloc`/`free`**: replaces allocation behavior globally (mruby + all other code and third‑party libraries). - **Custom `mrb_basic_alloc_func()`**: replaces allocation behavior only for mruby’s internal use, leaving other libraries’ allocations untouched. ## Migration note If you are moving from the old API: 1. **Removal of `mrb_open_allocf()`** - \_Old: ```c mrb_state *mrb = mrb_open_allocf(my_allocf, ud); ``` - \_New: ```c // No allocf parameter; set up your hook via mrb_basic_alloc_func definition. mrb_state *mrb = mrb_open_core(); ``` 2. **`mrb_open_core()` takes no arguments** - Simply drop any allocf or user-data arguments, and redefine `mrb_basic_alloc_func` as you need. 3. **No more `mrb_allocf` type** - Definitions using the `mrb_allocf` typedef can be removed; implement `mrb_basic_alloc_func()` with the signature below: ```c void* mrb_basic_alloc_func(void *ptr, size_t size); ``` 4. **`mrb_basic_alloc_func` signature change** - _Old:_ ```c void* mrb_default_allocf(mrb_state *mrb, void *ptr, size_t size, void *ud); ``` - _New:_ ```c void* mrb_basic_alloc_func(void *ptr, size_t size); ``` --- ### Code examples - **Old style**: ```c static void* my_allocf(mrb_state *mrb, void *ud, void *ptr, size_t size) { // ...custom logic... } mrb_state *mrb = mrb_open_allocf(my_allocf, some_ud); ``` - **New style**: ```c // Define your hook before creating the state: void* mrb_basic_alloc_func(void *ptr, size_t size) { // ...custom logic... } mrb_state *mrb = mrb_open_core(); ``` nghttp2-1.68.0/third-party/mruby/doc/guides/PaxHeaders/symbol.md0000644000000000000000000000013215077107276021547 xustar0030 mtime=1761382078.098420666 30 atime=1761382080.120411416 30 ctime=1761382108.964300774 nghttp2-1.68.0/third-party/mruby/doc/guides/symbol.md0000644000175100017510000000550015077107276022137 0ustar00runnerrunner # Symbols Symbols in `mruby` C source code is represented by `mrb_sym` which is alias of `uint32_t`. Lower 30 bits are used for symbols so that higher 2 bits can be used as flags, e.g. `struct mt_elem` in `class.c`. ```c struct mt_elem { union mt_ptr ptr; size_t func_p:1; size_t noarg_p:1; mrb_sym key:sizeof(mrb_sym)*8-2; }; ``` ## C API We provide following C API for symbols. ### Generate Symbols #### `mrb_sym mrb_intern(mrb_state*,const char*,size_t)` Get a symbol from a string. #### `mrb_sym mrb_intern_check_cstr(mrb_state*,const char*)` Get a symbol from a NULL terminated (C) string. #### `mrb_sym mrb_intern_str(mrb_state*,mrb_value)` Get a symbol from a Ruby string object. #### `mrb_intern_lit(mrb_state*,const char*)` Get a symbol from a C string literal. The second argument should be a C string literal, otherwise you will get a compilation error. It does not copy C string given the fact it's a literal. #### `mrb_sym mrb_intern_check(mrb_state*,const char*,size_t)` Get a symbol from a string if the string has been already registered as a symbol, otherwise return `0`. We also provide variants `mrb_intern_check_str()` (from Ruby string) and `mrb_intern_check_cstr()` (from C string). #### `const char *mrb_sym_name(mrb_state*,mrb_sym)` Get a string representation of a symbol as a C string. #### `const char *mrb_sym_name_len(mrb_state*,mrb_sym,mrb_int*)` Get a string representation of a symbol, and its length. ## Preallocate Symbols To save RAM, `mruby` can use compile-time allocation of some symbols. You can use following macros to get preallocated symbols by including `mruby/presym.h` header. - `MRB_SYM(xor)` //=> xor (Word characters) - `MRB_SYM_B(xor)` //=> xor! (Method with Bang) - `MRB_SYM_Q(xor)` //=> xor? (Method with Question mark) - `MRB_SYM_E(xor)` //=> xor= (Method with Equal) - `MRB_GVSYM(xor)` //=> $xor (Global Variable) - `MRB_CVSYM(xor)` //=> @@xor (Class Variable) - `MRB_IVSYM(xor)` //=> @xor (Instance Variable) - `MRB_OPSYM(xor)` //=> ^ (Operator) For `MRB_OPSYM()`, specify the names corresponding to operators (see `MRuby::Presym::OPERATORS` in `lib/mruby/presym.rb` for the names that can be specified for it). Other than that, describe only word characters excluding leading and ending punctuation. These macros are converted to static symbol IDs at compile time, unless preallocate symbols are disabled by `conf.disable_presym`. In that case, these macros are expanded to `mrb_intern_lit` calls, therefore the mruby state variable is required. The above macros assume the variable name is `mrb`. If its name is not `mrb`, you need to use macros with `_2` suffix, such as `MRB_SYM_2` to specify `mrb_state*` variable. ### Disabling Preallocated Symbols You can disable preallocated symbols by specifying `conf.disable_presym` in the configuration file. nghttp2-1.68.0/third-party/mruby/doc/guides/PaxHeaders/mrbconf.md0000644000000000000000000000013215077107276021670 xustar0030 mtime=1761382078.098420666 30 atime=1761382080.120411416 30 ctime=1761382108.967300765 nghttp2-1.68.0/third-party/mruby/doc/guides/mrbconf.md0000644000175100017510000001631215077107276022263 0ustar00runnerrunner # mruby configuration macros ## The configuration file You can do the build configuration in the build configuration file. The default configuration file is `build_config/default.rb`. You can specify your own configuration file by the `MRUBY_CONFIG` environment variable (you can use `CONFIG` for shorthand for `MRUBY_CONFIG`). If the path doesn't exist, `build_config/${MRUBY_CONFIG}.rb` is used. ## How to use these macros Just add the configuration value to the `MRuby::Build#defines` attribute. This is the same for `MRuby::CrossBuild`. ```ruby # build_config.rb MRuby::Build.new do |conf| ... conf.defines << 'MRB_GC_FIXED_ARENA' conf.defines << 'MRB_NO_METHOD_CACHE' ... end ``` **_NOTE_** - Use common definitions (`conf.defines`) instead of per-compiler definitions (e.g., `conf.cc.defines`) unless there is a special reason not to. - It is now deprecated to edit the `include/mruby/mrbconf.h` file or give it directly as a compiler flag, as was the case before. ## stdio setting `MRB_NO_STDIO` - When defined `` functions won't be used. - Some features will be disabled when this is enabled: - `mrb_irep` load/dump from/to file. - Compiling mruby script from a file. - Printing features in **src/print.c**. ## Debug macros `MRB_USE_DEBUG_HOOK` - When defined code fetch hook and debug OP hook will be enabled. - When using any of the hook set function pointer `code_fetch_hook` and/or `debug_op_hook` of `mrb_state`. - Fetch hook will be called before any OP. - Debug OP hook will be called when dispatching `OP_DEBUG`. `MRB_DEBUG` - When defined `mrb_assert*` macro will be defined with macros from ``. - Could be enabled via `enable_debug` method of `MRuby::Build`. ## Stack configuration `MRB_STACK_EXTEND_DOUBLING` - If defined doubles the stack size when extending it. - Otherwise extends stack with `MRB_STACK_GROWTH`. `MRB_STACK_GROWTH` - Default value is `128`. - Used in stack extending. - Ignored when `MRB_STACK_EXTEND_DOUBLING` is defined. `MRB_STACK_MAX` - Default value is `0x40000 - MRB_STACK_GROWTH`. - Raises `RuntimeError` when stack size exceeds this value. ## Primitive type configuration `MRB_USE_FLOAT32` - When defined single-precision floating-point type(C type `float`) is used as `mrb_float`. - Otherwise, double-precision floating-point type(C type `double`) is used as `mrb_float`. `MRB_NO_FLOAT` - When defined removes floating-point numbers from mruby. - It makes mruby easier to handle in "Micro-controller without FPU" and "Kernel Space". `MRB_INT32` - When defined, or `MRB_INT64` are not defined on 32-bit CPU mode, `mrb_int` will be defined as `int32_t`. - Conflicts with `MRB_INT64`. `MRB_INT64` - When defined, or `MRB_INT32` are not defined on 64-bit CPU mode, `mrb_int` will be defined as `int64_t`. - Conflicts with `MRB_INT32`. ## Garbage collector configuration `MRB_GC_STRESS` - When defined full GC is emitted per each `RBasic` allocation. - Mainly used in memory manager debugging. - If defined at the same time as `MRB_DEBUG`, full GC is emitted also per each heap allocation (`mrb_malloc()` or etc.). This configuration slows down mruby execution by a factor of 2 to 3 or even more. `MRB_GC_TURN_OFF_GENERATIONAL` - When defined turns generational GC off by default. `MRB_GC_FIXED_ARENA` - When defined used fixed size GC arena. - Raises `RuntimeError` when this is defined and GC arena size exceeds `MRB_GC_ARENA_SIZE`. - Useful tracking unnecessary mruby object allocation. `MRB_GC_ARENA_SIZE` - Default value is `100`. - Ignored when `MRB_GC_FIXED_ARENA` isn't defined. - Defines fixed GC arena size. `MRB_HEAP_PAGE_SIZE` - Default value is `1024`. - Specifies number of `RBasic` per each heap page. - To calculate the number of bytes per heap page, it is "(size of management data per heap page) + (size per object) \* `MRB_HEAP_PAGE_SIZE`". In mruby 3.1.0, the "size of management data per heap page" is 6 words, also "size per object" is 6 words. For a 32-bit CPU, `(6 * 4) + (6 * 4) * MRB_HEAP_PAGE_SIZE` gives the bytes of size per heap page. Conversely, for example, to keep the size per heap page to 4 Ki bytes, calculate `(4096 - (6 * 4)) / (6 * 4)` to specify `MRB_HEAP_PAGE_SIZE=169`. ## Memory pool configuration `POOL_ALIGNMENT` - Default value is `4`. - If you're allocating data types that requires alignment more than default value define the largest value of required alignment. `POOL_PAGE_SIZE` - Default value is `16000`. - Specifies page size of pool page. - Smaller the value is increases memory overhead. ## State atexit configuration `MRB_FIXED_STATE_ATEXIT_STACK` - If defined enables fixed size `mrb_state` atexit stack. - Raises `RuntimeError` when `mrb_state_atexit` call count to same `mrb_state` exceeds `MRB_FIXED_STATE_ATEXIT_STACK_SIZE`'s value. `MRB_FIXED_STATE_ATEXIT_STACK_SIZE` - Default value is `5`. - If `MRB_FIXED_STATE_ATEXIT_STACK` isn't defined this macro is ignored. ## `mrb_value` configuration `MRB_ENDIAN_BIG` - If defined compiles mruby for big endian machines. - Used in `MRB_NAN_BOXING`. - Some mrbgem use this mrbconf. `MRB_NAN_BOXING` - If defined represent `mrb_value` in boxed `double`. - Conflicts with `MRB_USE_FLOAT32` and `MRB_NO_FLOAT`. `MRB_WORD_BOXING` - If defined represent `mrb_value` as a word. - If defined `Float` will be a mruby object with `RBasic`. ## Reduce heap memory configuration `MRB_USE_ETEXT_RO_DATA_P` - Use `etext` and `edata` section addresses defined by the linker to detect read-only data. - Those addresses are widely available, but not portable, nor standardized. - This macro is defined by default on User-mode Linux. `MRB_NO_DEFAULT_RO_DATA_P` - Define this macro when the default `mrb_ro_data_p()` does not work for any reason. `MRB_USE_CUSTOM_RO_DATA_P` - Please try if `MRB_USE_LINK_TIME_RO_DATA_P` is not available. - The `mrb_ro_data_p()` function is implemented by the user in an arbitrary file. - The prototype declaration is `mrb_bool mrb_ro_data_p(const char *ptr)`. - Return `TRUE` if `ptr` is in the read-only section, otherwise return `FALSE`. ## Other configuration `MRB_MALLOC_TRIM` - call `malloc_trim(0)` for each `mrb_full_gc()` call `MRB_UTF8_STRING` - Adds UTF-8 encoding support to character-oriented String instance methods. - If it isn't defined, they only support the US-ASCII encoding. `MRB_STR_LENGTH_MAX` - The maximum length of strings (default 1MB) - set this value to zero to skip the check `MRB_ARY_LENGTH_MAX` - The maximum length of arrays (default 1MB) - set this value to zero to skip the check `MRB_FUNCALL_ARGC_MAX` - Default value is `16`. - Specifies 4th argument(`argc`) max value of `mrb_funcall`. - Raises `ArgumentError` when the `argc` argument is bigger then this value `mrb_funcall`. `KHASH_DEFAULT_SIZE` - Default value is `32`. - Specifies default size of khash table bucket. - Used in `kh_init_ ## name` function. `MRB_NO_METHOD_CACHE` - Disable method cache to save memory. `MRB_METHOD_CACHE_SIZE` - Default value is `256`. - Ignored if `MRB_NO_METHOD_CACHE` is defined. - Need to be the power of 2. `MRB_USE_ALL_SYMBOLS` - Make it available `Symbol.all_symbols` in `mrbgems/mruby-symbol-ext` - Increase heap memory usage. `MRB_USE_VM_SWITCH_DISPATCH` - Turn on switch dispatch in VM loop nghttp2-1.68.0/third-party/mruby/doc/guides/PaxHeaders/gc-arena-howto.md0000644000000000000000000000013215077107276023055 xustar0030 mtime=1761382078.098420666 30 atime=1761382080.119411421 30 ctime=1761382108.966300769 nghttp2-1.68.0/third-party/mruby/doc/guides/gc-arena-howto.md0000644000175100017510000001517115077107276023452 0ustar00runnerrunner # How to use `mrb_gc_arena_save()`/`mrb_gc_arena_restore()`/`mrb_gc_protect()` _This is an English translation of [Matz's blog post][matz blog post] written in Japanese._ _Some parts are updated to reflect recent changes._ [matz blog post]: https://www.rubyist.net/~matz/20130731.html When you are extending mruby using C language, you may encounter mysterious "arena overflow error" or memory leak or very slow execution speed. This is an error indicating overflow of "GC arena" implementing "conservative GC". GC (garbage collector) must ensure that object is "alive", in other words, that it is referenced by somewhere from the program. This can be determined by checking if the object can be directly or indirectly referenced by root. The local variables, global variables and constants etc. are root. If program execution is performed inside mruby VM, there is nothing to worry about because GC can access all roots owned by the VM. The problem arises when executing C functions. The object referenced by C variable is also "alive", but mruby GC cannot aware of this, so it might mistakenly recognize the objects referenced by only C variables as dead. This can be a fatal bug if the GC tries to collect a live object. In CRuby, we scan C stack area, and use C variable as root to check whether object is alive or not. Of course, because we are accessing C stack just as memory region, we never know it is an integer or a pointer. We work around this by assuming that if it looks like a pointer, then assume it as a pointer. We call it "conservative". By the way, CRuby's "conservative GC" has some problems. The biggest problem is we have no way to access to the stack area in portable way. Therefore, we cannot use this method if we'd like to implement highly portable runtime, like mruby. So we came up with another plan to implement "conservative GC" in mruby. Again, the problem is when an object which was created in C function, becomes no longer referenced in the Ruby world, and cannot be treated as garbage. In mruby, we recognize all objects created in C function are alive. Then we have no problem such as confusing a live object as dead. This means that because we cannot collect a truly dead object, we may lose efficiency, but as a trade-off the GC itself is highly portable. We can say goodbye to the problem that GC deletes live objects due to optimization which sometimes occurs in CRuby. According to this idea, we have a table, called "GC arena", which remembers objects created in C function. The arena is stack structure, when C function execution is returned to mruby VM, all objects registered in the arena are popped. This works very well, but can cause another problem: "arena overflow error" or memory leak. As of this writing, mruby automatically extend arena to remember objects (See `MRB_GC_FIXED_ARENA` and `MRB_GC_ARENA_SIZE` in [doc/guides/mrbconf.md](mrbconf.md)). If you create many objects in C functions, memory usage will increase, since GC never kicks in. This memory usage may look like memory leaks, but will also make execution slower as more memory will need to be allocated. With the build time configuration, you can limit the maximum size of arena (e.g., 100). Then if you create many objects, arena overflows, thus you will get an "arena overflow error". To work around these problems, we have `mrb_gc_arena_save()` and `mrb_gc_arena_restore()` functions. `int mrb_gc_arena_save(mrb)` returns the current position of the stack top of GC arena, and `void mrb_gc_arena_restore(mrb, idx)` sets the stack top position to back to given `idx`. We can use them like this: ```c int arena_idx = mrb_gc_arena_save(mrb); // ...create objects... mrb_gc_arena_restore(mrb, arena_idx); ``` In mruby, C function calls are surrounded by this save/restore, but we can further optimize memory usage by surrounding save/restore, and can avoid creating arena overflow bugs. Let's take a real example. Here is the source code of `Array#inspect`: ```c static mrb_value inspect_ary(mrb_state *mrb, mrb_value ary, mrb_value list) { mrb_int i; mrb_value s, arystr; char head[] = { '[' }; char sep[] = { ',', ' ' }; char tail[] = { ']' }; /* check recursive */ for (i=0; i 0) { mrb_str_cat(mrb, arystr, sep, sizeof(sep)); } if (mrb_array_p(RARRAY_PTR(ary)[i])) { s = inspect_ary(mrb, RARRAY_PTR(ary)[i], list); } else { s = mrb_inspect(mrb, RARRAY_PTR(ary)[i]); } mrb_str_cat(mrb, arystr, RSTRING_PTR(s), RSTRING_LEN(s)); mrb_gc_arena_restore(mrb, ai); } mrb_str_cat(mrb, arystr, tail, sizeof(tail)); mrb_ary_pop(mrb, list); return arystr; } ``` This is a real example, so slightly complicated, but bear with me. The essence of `Array#inspect` is that after stringifying each element of array using `inspect` method, we join them together so that we can get `inspect` representation of the entire array. After the `inspect` representation is created, we no longer require the individual string representation. This means that we don't have to register these temporal objects into GC arena. Therefore, in order to keep the arena size small; the `ary_inspect()` function will do the following: - save the position of the stack top using `mrb_gc_arena_save()`. - get `inspect` representation of each element. - append it to the constructing entire `inspect` representation of array. - restore stack top position using `mrb_gc_arena_restore()`. Please note that the final `inspect` representation of entire array was created before the call of `mrb_gc_arena_restore()`. Otherwise, required temporal object may be deleted by GC. We may have an usecase where after creating many temporal objects, we'd like to keep some of them. In this case, we cannot use the same idea in `ary_inspect()` like appending objects to existing one. Instead, after `mrb_gc_arena_restore()`, we must re-register the objects we want to keep in the arena using `mrb_gc_protect(mrb, obj)`. Use `mrb_gc_protect()` with caution because it could also lead to an "arena overflow error". We must also mention that when `mrb_funcall` is called in top level, the return value is also registered to GC arena, so repeated use of `mrb_funcall` may eventually lead to an "arena overflow error". Use `mrb_gc_arena_save()` and `mrb_gc_arena_restore()` or possible use of `mrb_gc_protect()` to workaround this. nghttp2-1.68.0/third-party/mruby/doc/guides/PaxHeaders/hier.md0000644000000000000000000000013115077107276021170 xustar0030 mtime=1761382078.098420666 30 atime=1761382080.120411416 29 ctime=1761382108.96230078 nghttp2-1.68.0/third-party/mruby/doc/guides/hier.md0000644000175100017510000000513015077107276021560 0ustar00runnerrunner # The mruby directory structure ```text +- 💎 mruby/ The top directory of mruby. | +- 📠.github/ GitHub configuration files for mruby project management. | +- 📠benchmark/ Benchmarking files for mruby. | +- 📠bin/ Links to temporary executables after build. Auto-created. | +- 📠build/ Default build output destination for mruby. Auto-created. | | | +- 📠repos/ The git clone destination directory for GEMs that depend on the build configuration. | | | +- 📠host/ The "host" build output directory. | +- 📠build_config/ Build configuration files for various environments. | +- 📠doc/ Documentation for mruby. | | | +- 📠guides/ Documentation for general users. | | | +- 📠internal/ Documentation for internal implementations for developers. | +- 📠examples/ Examples of mruby usages. | | | +- 📠mrbgems/ Examples for creating custom GEM for mruby. | +- 📠include/ C header files required when using mruby. | +- 📠lib/ Ruby scripts used for building mruby. | +- 📠mrbgems/ A library collection of features not provided by mruby core only. | | See doc/guides/mrbgems.md file | | | +- 📠mruby-*/ The directory of each GEMs. | | | +- 📃 *.gembox A collection of GEMs grouped by features and purposes. | +- 📠mrblib/ The core Ruby scripts that makes up the main body of mruby. | +- 📠oss-fuzz/ Source code for The fuzzing-test. | See https://github.com/google/oss-fuzz | +- 📠src/ The core C source code that makes up the main body of mruby. | +- 📠tasks/ Rake tasks at build-time. | | | +- 📠toolchains/ Definitions for the compiler, linker, archiver, etc. for each toolchain. | +- 📠test/ Ruby scripts needed for testing mruby. | | | +- 📠t/ mruby test cases. | +- 📠tools/ External programs used for the mruby project. | +- 📠lrama/ LALR parser generator as an alternative to bison. Import from https://github.com/ruby/lrama ``` nghttp2-1.68.0/third-party/mruby/doc/guides/PaxHeaders/mrbgems.md0000644000000000000000000000013215077107276021676 xustar0030 mtime=1761382078.098420666 30 atime=1761382080.120411416 30 ctime=1761382108.959300789 nghttp2-1.68.0/third-party/mruby/doc/guides/mrbgems.md0000644000175100017510000003512615077107276022275 0ustar00runnerrunner # mrbgems mrbgems is a library manager to integrate C and Ruby extensions in an easy and standardised way into mruby. Conventionally, each mrbgem name is prefixed by `mruby-`, e.g. `mruby-time` for a gem that provides `Time` class functionality. ## Usage You have to activate mrbgems explicitly in your build configuration. To add a gem, add the following line to your build configuration file, for example: ```ruby conf.gem '/path/to/your/gem/dir' ``` You can also use a relative path to specify a gem. ```ruby conf.gem 'examples/mrbgems/ruby_extension_example' ``` In that case, - if your build configuration file is in the `build_config` directory, it's relative from `MRUBY_ROOT`. - otherwise, it is relative from the directory where your build configuration is. A remote GIT repository location for a GEM is also supported: ```ruby conf.gem :git => 'https://github.com/masuidrive/mrbgems-example.git', :branch => 'master' conf.gem :github => 'masuidrive/mrbgems-example', :branch => 'master' conf.gem :bitbucket => 'mruby/mrbgems-example', :branch => 'master' ``` NOTE: `:bitbucket` option supports only git. Hg is unsupported in this version. You can specify the subdirectory of the repository with `:path` option: ```ruby conf.gem github: 'mruby/mruby', path: 'mrbgems/mruby-socket' ``` To use mrbgem from [mgem-list](https://github.com/mruby/mgem-list) use `:mgem` option: ```ruby conf.gem :mgem => 'mruby-yaml' conf.gem :mgem => 'yaml' # 'mruby-' prefix could be omitted ``` For specifying the commit hash to checkout use `:checksum_hash` option: ```ruby conf.gem mgem: 'mruby-redis', checksum_hash: '3446d19fc4a3f9697b5ddbf2a904f301c42f2f4e' ``` If there are missing dependencies, mrbgem dependencies solver will reference mrbgem from the core or mgem-list. Note that if more than one git-based gem has the same base name (i.e. the default checkout directory name), it is (now) an error **UNLESS** they have the same repository URL, branch name and commit-id (i.e. checksum hash). You can bypass this by explicitly importing your preferred version **first** and setting the `canonical:` option to `true`: ```ruby conf.gem github: 'me/mruby-yaml', branch: 'my-hacked-branch', canonical: true ``` If you do this, the system will (mostly) silently ignore other attempts to clone a gem with this name. Note that this only affects cloning the gem from git. It does not resolve version conflicts. If the version as specified in the gem's rakefile is incompatible with a dependency, your build will still fail. ### Tweak the gem in your build configuration file You can give blocks in the `conf.gem` call to make adjustments for environments where the original gem does not expect them: ```ruby conf.gem core: "mruby-bin-mirb" do |g| # For cross build to NetBSD g.linker.libraries = %w(edit termcap) end ``` However, it should be used with caution, as it may deviate from the intent of the gem's author. ### Gem Testing If you enable unit tests in your build with `enable_test`, tests will be generated for all gems and their dependencies by default. If necessary, it is possible to suppress tests for a specific gem like so: ```ruby conf.gem 'mruby-noisygem' do |g| g.skip_test = true end ``` However, it is considered best practice to leave all tests enabled whenever possible. A warning message will be generated for each gem with disabled tests. ## GemBox There are instances when you wish to add a collection of mrbgems into mruby at once, or be able to substitute mrbgems based on configuration, without having to add each gem to your build configuration file. A packaged collection of mrbgems is called a GemBox. A GemBox is a file that contains a list of mrbgems to load into mruby, in the same format as if you were adding them to the build config via `config.gem`, but wrapped in an `MRuby::GemBox` object. GemBoxes are loaded into mruby via `config.gembox 'boxname'`. Below we have created a GemBox containing `mruby-time` and `mrbgems-example`: ```ruby MRuby::GemBox.new do |conf| conf.gem "#{root}/mrbgems/mruby-time" conf.gem :github => 'masuidrive/mrbgems-example' end ``` As mentioned, the GemBox uses the same conventions as `MRuby::Build`. The GemBox must be saved with a `.gembox` extension inside the `mrbgems` directory to be picked up by mruby. To use this example GemBox, we save it as `custom.gembox` inside the `mrbgems` directory in mruby, and add the following to your build configuration file inside the build block: ```ruby conf.gembox 'custom' ``` This will cause the `custom` GemBox to be read in during the build process, adding `mruby-time` and `mrbgems-example` to the build. If you want, you can put GemBox outside the mruby directory. In that case you must specify an absolute path like below. ```ruby conf.gembox "#{ENV["HOME"]}/mygemboxes/custom" ``` There are two GemBoxes that ship with mruby: [default](../../mrbgems/default.gembox) and [full-core](../../mrbgems/full-core.gembox). The [default](../../mrbgems/default.gembox) GemBox contains several core components of mruby, and [full-core](../../mrbgems/full-core.gembox) contains every gem found in the `mrbgems` directory. ## GEM Structure The maximal GEM structure looks like this: ``` +- GEM_NAME <- Name of GEM | +- README.md <- Readme for GEM | +- mrbgem.rake <- GEM Specification | +- include/ <- Header for Ruby extension (will exported) | +- mrblib/ <- Source for Ruby extension | +- src/ <- Source for C extension | +- tools/ <- Source for Executable (in C) | +- test/ <- Test code (Ruby) ``` The `mrblib` directory contains pure Ruby files to extend mruby. The `src` directory contains C/C++ files to extend mruby. The `include` directory contains C/C++ header files. The `test` directory contains C/C++ and pure Ruby files for testing purposes which will be used by `mrbtest`. `mrbgem.rake` contains the specification to compile C and Ruby files. `README.md` is a short description of your GEM. ## Build process mrbgems expects a specification file called `mrbgem.rake` inside of your GEM directory. A typical GEM specification could look like this for example: ```ruby MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Example mrbgem using C and Ruby' end ``` The mrbgems build process will use this specification to compile Object and Ruby files. The compilation results will be added to `lib/libmruby.a`. This file exposes the GEM functionality to tools like `mruby` and `mirb`. The following properties can be set inside your `MRuby::Gem::Specification` for information purpose: - `spec.license` or `spec.licenses` (A single license or a list of them under which this GEM is licensed) - `spec.author` or `spec.authors` (Developer name or a list of them) - `spec.version` (Current version) - `spec.description` (Detailed description) - `spec.summary` - One line short description of mrbgem. - Printed in build summary of rake when set. - `spec.homepage` (Homepage) - `spec.requirements` (External requirements as information for user) The `license` and `author` properties are required in every GEM! In case your GEM is depending on other GEMs please use `spec.add_dependency(gem, *requirements[, default_get_info])` like: ```ruby MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' # Add GEM dependency mruby-parser. # The version must be between 1.0.0 and 1.5.2 . spec.add_dependency('mruby-parser', '>= 1.0.0', '<= 1.5.2') # Use any version of mruby-uv from GitHub. spec.add_dependency('mruby-uv', '>= 0.0.0', :github => 'mattn/mruby-uv') # Use latest mruby-onig-regexp from GitHub. (version requirements can be omitted) spec.add_dependency('mruby-onig-regexp', :github => 'mattn/mruby-onig-regexp') # You can add extra mgems active only on test spec.add_test_dependency('mruby-process', :github => 'iij/mruby-process') end ``` The version requirements and default gem information are optional. Version requirement supports following operators: - '=': is equal - '!=': is not equal - '>': is greater - '<': is lesser - '>=': is equal or greater - '<=': is equal or lesser - '~>': is equal or greater and is lesser than the next major version - example 1: '~> 2.2.2' means '>= 2.2.2' and '< 2.3.0' - example 2: '~> 2.2' means '>= 2.2.0' and '< 3.0.0' When more than one version requirements is passed, the dependency must satisfy all of it. You can have default gem to use as dependency when it's not defined in your build configuration. When the last argument of `add_dependency` call is `Hash`, it will be treated as default gem information. Its format is same as argument of method `MRuby::Build#gem`, expect that it can't be treated as path gem location. When a special version of dependency is required, use `MRuby::Build#gem` in the build configuration to override default gem. If you have conflicting GEMs use the following method: - `spec.add_conflict(gem, *requirements)` - The `requirements` argument is same as in `add_dependency` method. like following code: ```ruby MRuby::Gem::Specification.new 'some-regexp-binding' do |spec| spec.license = 'BSD' spec.author = 'John Doe' spec.add_conflict 'mruby-onig-regexp', '> 0.0.0' spec.add_conflict 'mruby-hs-regexp' spec.add_conflict 'mruby-pcre-regexp' spec.add_conflict 'mruby-regexp-pcre' end ``` In case your GEM has more complex build requirements you can use the following options additionally inside your GEM specification: - `spec.cc.flags` (C compiler flags) - `spec.cc.defines` (C compiler defines) - `spec.cc.include_paths` (C compiler include paths) - `spec.linker.flags` (Linker flags) - `spec.linker.libraries` (Linker libraries) - `spec.linker.library_paths` (Linker additional library path) - `spec.bins` (Generate binary file) - `spec.rbfiles` (Ruby files to compile) - `spec.objs` (Object files to compile) - `spec.test_rbfiles` (Ruby test files for integration into mrbtest) - `spec.test_objs` (Object test files for integration into mrbtest) - `spec.test_preload` (Initialization files for mrbtest) You also can use `spec.mruby.cc` and `spec.mruby.linker` to add extra global parameters for the compiler and linker. ### include_paths and dependency Your GEM can export include paths to another GEMs that depends on your GEM. By default, `/...absolute path.../{GEM_NAME}/include` will be exported. So it is recommended not to put GEM's local header files on include/. These exports are retroactive. For example: when B depends on C and A depends on B, A will get include paths exported by C. Exported include_paths are automatically appended to GEM local include_paths by rake. You can use `spec.export_include_paths` accessor if you want more complex build. ## C Extension mruby can be extended with C. This is possible by using the C API to integrate C libraries into mruby. ### Preconditions mrbgems expects that you have implemented a C method called `mrb_YOURGEMNAME_gem_init(mrb_state)`. `YOURGEMNAME` will be replaced by the name of your GEM. If you call your GEM `c_extension_example`, your initialisation method could look like this: ```c void mrb_c_extension_example_gem_init(mrb_state* mrb) { struct RClass *class_cextension = mrb_define_module(mrb, "CExtension"); mrb_define_class_method(mrb, class_cextension, "c_method", mrb_c_method, MRB_ARGS_NONE()); } ``` ### Finalize mrbgems expects that you have implemented a C method called `mrb_YOURGEMNAME_gem_final(mrb_state)`. `YOURGEMNAME` will be replaced by the name of your GEM. If you call your GEM `c_extension_example`, your finalizer method could look like this: ```c void mrb_c_extension_example_gem_final(mrb_state* mrb) { free(someone); } ``` ### Example ``` +- c_extension_example/ | +- README.md (Optional) | +- src/ | | | +- example.c <- C extension source | +- test/ | | | +- example.rb <- Test code for C extension | +- mrbgem.rake <- GEM specification ``` ## Ruby Extension mruby can be extended with pure Ruby. It is possible to override existing classes or add new ones in this way. Put all Ruby files into the `mrblib` directory. ### Preconditions none ### Example ``` +- ruby_extension_example/ | +- README.md (Optional) | +- mrblib/ | | | +- example.rb <- Ruby extension source | +- test/ | | | +- example.rb <- Test code for Ruby extension | +- mrbgem.rake <- GEM specification ``` ## C and Ruby Extension mruby can be extended with C and Ruby at the same time. It is possible to override existing classes or add new ones in this way. Put all Ruby files into the `mrblib` directory and all C files into the `src` directory. mruby codes under `mrblib` directory would be executed after gem init C function is called. Make sure _mruby script_ depends on _C code_ and _C code_ doesn't depend on _mruby script_. ### Preconditions See C and Ruby example. ### Example ``` +- c_and_ruby_extension_example/ | +- README.md (Optional) | +- mrblib/ | | | +- example.rb <- Ruby extension source | +- src/ | | | +- example.c <- C extension source | +- test/ | | | +- example.rb <- Test code for C and Ruby extension | +- mrbgem.rake <- GEM specification ``` ## Binary gems Some gems can generate executables under `bin` directory. Those gems are called binary gems. Names of binary gems are conventionally prefixed by `mruby-bin`, e.g. `mruby-bin-mirb` and `mruby-bin-strip`. To specify the name of executable, you need to specify `spec.bins` in the `mrbgem.rake`. The entry point `main()` should be in the C source file under `tools//*.c` where `` is a name of the executable. C files under the `` directory are compiled and linked to the executable, but not included in `libmruby.a`, whereas files under `mrblib` and `src` are. It is strongly recommended not to include `mrblib` and `src` directories in the binary gems, to separate normal gems and binary gems. ### Example ``` +- mruby-bin-example/ | +- README.md (Optional) | +- bintest/ | | | +- example.rb <- Test code for binary gem | +- mrbgem.rake <- Gem specification | +- mrblib/ <- Source for Ruby extension (Optional) | +- src/ <- Source for C extension (Optional) | +- tools/ | +- example/ <- Executable name directory | +- example.c <- Source for Executable (includes main) ``` nghttp2-1.68.0/third-party/mruby/doc/PaxHeaders/limitations.md0000644000000000000000000000013215077107276021316 xustar0030 mtime=1761382078.098420666 30 atime=1761382080.120411416 30 ctime=1761382108.951300812 nghttp2-1.68.0/third-party/mruby/doc/limitations.md0000644000175100017510000001054515077107276021713 0ustar00runnerrunner # Limitations and Differences The philosophy of mruby is to be a lightweight implementation of the Ruby ISO standard. These two objectives are partially contradicting. Ruby is an expressive language with complex implementation details which are difficult to implement in a lightweight manner. To cope with this, limitations to the "Ruby Compatibility" are defined. This document is collecting these limitations. ## Integrity This document does not contain a complete list of limitations. Please help to improve it by submitting your findings. ## `Kernel.raise` in rescue clause `Kernel.raise` without arguments does not raise the current exception within a rescue clause. ```ruby begin 1 / 0 rescue raise end ``` #### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)] `ZeroDivisionError` is raised. #### mruby [3.1.0 (2022-05-12)] `RuntimeError` is raised instead of `ZeroDivisionError`. To re-raise the exception, you have to do: ```ruby begin 1 / 0 rescue => e raise e end ``` ## Fiber execution can't cross C function boundary mruby's `Fiber` is implemented similarly to Lua's co-routine. This results in the consequence that you can't switch context within C functions. Only exception is `mrb_fiber_yield` at return. ## `Array` does not support instance variables To reduce memory consumption `Array` does not support instance variables. ```ruby class Liste < Array def initialize(str = nil) @field = str end end p Liste.new "foobar" ``` #### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)] `[]` #### mruby [3.1.0 (2022-05-12)] `ArgumentError` is raised. ## `defined?` The `defined?` keyword is considered too complex to be fully implemented. It is recommended to use `const_defined?` and other reflection methods instead. ```ruby defined?(Foo) ``` #### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)] ``` nil ``` #### mruby [3.1.0 (2022-05-12)] `NameError` is raised. ## `alias` on global variables Aliasing a global variable works in CRuby but is not part of the ISO standard. ```ruby alias $a $__a__ ``` #### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)] `nil` #### mruby [3.1.0 (2022-05-12)] Syntax error ## Operator modification An operator can't be overwritten by the user. ```ruby class String def + end end 'a' + 'b' ``` #### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)] `ArgumentError` is raised. The re-defined `+` operator does not accept any arguments. #### mruby [3.1.0 (2022-05-12)] `'ab'` Behavior of the operator wasn't changed. ## `Kernel#binding` is not supported until [3.0.0 (2021-03-05)] `Kernel#binding` method is not supported. #### Ruby [ruby 2.5.1p57 (2018-03-29 revision 63029)] ```shell $ ruby -e 'puts Proc.new {}.binding' # ``` ## `nil?` redefinition in conditional expressions Redefinition of `nil?` is ignored in conditional expressions. ```ruby a = "a" def a.nil? true end puts(a.nil? ? "truthy" : "falsy") ``` Ruby outputs `truthy`. mruby outputs `falsy`. ## Argument Destructuring ```ruby def m(a,(b,c),d); p [a,b,c,d]; end m(1,[2,3],4) # => [1,2,3,4] ``` Destructured arguments (`b` and `c` in above example) cannot be accessed from the default expression of optional arguments and keyword arguments, since actual assignment is done after the evaluation of those default expressions. Thus: ```ruby def f(a,(b,c),d=b) p [a,b,c,d] end f(1,[2,3]) ``` CRuby gives `[1,2,3,nil]`. mruby raises `NoMethodError` for `b`. Keyword argument expansion has similar restrictions. The following example, gives `[1, 1]` for CRuby, mruby raises `NoMethodError` for `b`. ```ruby def g(a: 1, b: a) p [a,b] end g(a:1) ``` ## No Double Dispatch in Module Loading To make implementation simpler, mruby does not use double dispatching in module loading (`include`/`prepend`/`extend`). Those method internally called corresponding actual load methods (`append_features`/`prepend_features`/`extend_object`). But they are rarely overloaded, consumes more memory, and make loading little bit slower. As a Ruby implementation for the smaller device, we decided mruby simpler. ```ruby module M def self.append_features(mod) p :append end end class C include M end ``` #### Ruby [ruby 3.5.0dev (2025-04-21 85bab61565))] Prints `:append`. #### mruby [3.5.0 (2025-04-28)] Nothing printed (since `include` does not call `append_features` internally). nghttp2-1.68.0/third-party/mruby/doc/PaxHeaders/mruby3.2.md0000644000000000000000000000013215077107276020343 xustar0030 mtime=1761382078.099420661 30 atime=1761382080.120411416 30 ctime=1761382108.953300806 nghttp2-1.68.0/third-party/mruby/doc/mruby3.2.md0000644000175100017510000000632215077107276020736 0ustar00runnerrunner# User visible changes in `mruby3.2` from `mruby3.1` # The language - Now `a::B = c` should evaluate `a` then `c`. - Anonymous arguments `*`, `**`, `&` can be passed for forwarding. - Multi-precision integer is available now via `mruby-bigint` gem. # mruby VM and bytecode - `OP_ARYDUP` was renamed to `OP_ARYSPLAT`. The instruction name was changed but instruction number and basic behavior have not changed (except that `ARYDUP nil` makes `[]`). # Tools ## `mruby` - `-b` only specifies the script is the binary. The files loaded by `-r` are not affected by the option. - `mruby` now loads complied binary if the suffix is `.mrb`. ## `mrbc` - Add `--no-optimize` option to disable optimization. # mrbgems ## mruby-class-ext - Add `Class#subclasses` method. - Add `Module#undefined_instance_methods` method. ## New bundled gems - mruby-errno from - mruby-set from - mruby-dir from - mruby-data # Breaking Changes ## `mrb_vm_run()` may detach top-level local variables referenced from blocks When the `mrb_vm_run()` function (including `mrb_top_run()`) is called, the previous top-level local variables referenced from blocks is detached under either of the following conditions. - If the `stack_keep` parameter is given as 0. - If the number of variables in `irep` to be executed is less than the number of previous top-level local variables. This change also affects API functions such as `mrb_load_string()` and `mrb_load_file()`. The conditions under which the previous top-level local variables referenced from blocks is detached in these functions are as follows: - If the function has no `mrbc_context` pointer parameter, or the `mrbc_context` pointer parameter is set to `NULL`. - If the number of variables held in the `mrbc_context` pointer is less than the number of previous top-level local variables. Intentional reliance on previous behavior may cause compatibility problems in your application. # CVEs Following CVEs are fixed. - [CVE-2022-0080](https://www.cve.org/CVERecord?id=CVE-2022-0080) - [CVE-2022-0240](https://www.cve.org/CVERecord?id=CVE-2022-0240) - [CVE-2022-0326](https://www.cve.org/CVERecord?id=CVE-2022-0326) - [CVE-2022-0481](https://www.cve.org/CVERecord?id=CVE-2022-0481) - [CVE-2022-0525](https://www.cve.org/CVERecord?id=CVE-2022-0525) - [CVE-2022-0570](https://www.cve.org/CVERecord?id=CVE-2022-0570) - [CVE-2022-0614](https://www.cve.org/CVERecord?id=CVE-2022-0614) - [CVE-2022-0623](https://www.cve.org/CVERecord?id=CVE-2022-0623) - [CVE-2022-0630](https://www.cve.org/CVERecord?id=CVE-2022-0630) - [CVE-2022-0631](https://www.cve.org/CVERecord?id=CVE-2022-0631) - [CVE-2022-0632](https://www.cve.org/CVERecord?id=CVE-2022-0632) - [CVE-2022-0717](https://www.cve.org/CVERecord?id=CVE-2022-0717) - [CVE-2022-0890](https://www.cve.org/CVERecord?id=CVE-2022-0890) - [CVE-2022-1106](https://www.cve.org/CVERecord?id=CVE-2022-1106) - [CVE-2022-1212](https://www.cve.org/CVERecord?id=CVE-2022-1212) - [CVE-2022-1276](https://www.cve.org/CVERecord?id=CVE-2022-1276) - [CVE-2022-1286](https://www.cve.org/CVERecord?id=CVE-2022-1286) - [CVE-2022-1934](https://www.cve.org/CVERecord?id=CVE-2022-1934) nghttp2-1.68.0/third-party/mruby/PaxHeaders/examples0000644000000000000000000000013215077107334017427 xustar0030 mtime=1761382108.200302983 30 atime=1761382109.795298372 30 ctime=1761382108.200302983 nghttp2-1.68.0/third-party/mruby/examples/0000755000175100017510000000000015077107334020074 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/examples/PaxHeaders/mrbgems0000644000000000000000000000013015077107334021061 xustar0029 mtime=1761382108.20130298 30 atime=1761382109.795298372 29 ctime=1761382108.20130298 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/0000755000175100017510000000000015077107334021530 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/examples/mrbgems/PaxHeaders/ruby_extension_example0000644000000000000000000000013215077107334025653 xustar0030 mtime=1761382108.453302252 30 atime=1761382109.795298372 30 ctime=1761382108.453302252 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/ruby_extension_example/0000755000175100017510000000000015077107334026320 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/examples/mrbgems/ruby_extension_example/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276030052 xustar0030 mtime=1761382078.100420657 30 atime=1761382080.121411411 30 ctime=1761382108.452302254 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/ruby_extension_example/mrbgem.rake0000644000175100017510000000150315077107276030441 0ustar00runnerrunnerMRuby::Gem::Specification.new('ruby_extension_example') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' # Add compile flags # spec.cc.flags << '' # Add cflags to all # spec.mruby.cc.flags << '-g' # Add libraries # spec.linker.libraries << 'external_lib' # Default build files # spec.rbfiles = Dir.glob("#{dir}/mrblib/*.rb") # spec.objs = Dir.glob("#{dir}/src/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) } # spec.test_rbfiles = Dir.glob("#{dir}/test/*.rb") # spec.test_objs = Dir.glob("#{dir}/test/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) } # spec.test_preload = 'test/assert.rb' # Values accessible as TEST_ARGS inside test scripts # spec.test_args = {'tmp_dir' => Dir::tmpdir} end nghttp2-1.68.0/third-party/mruby/examples/mrbgems/ruby_extension_example/PaxHeaders/mrblib0000644000000000000000000000013215077107334027122 xustar0030 mtime=1761382108.455302246 30 atime=1761382109.795298372 30 ctime=1761382108.455302246 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/ruby_extension_example/mrblib/0000755000175100017510000000000015077107334027567 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/examples/mrbgems/ruby_extension_example/mrblib/PaxHeaders/example.r0000644000000000000000000000013215077107276031022 xustar0030 mtime=1761382078.100420657 30 atime=1761382080.121411411 30 ctime=1761382108.455302246 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/ruby_extension_example/mrblib/example.rb0000644000175100017510000000014315077107276031552 0ustar00runnerrunnerclass RubyExtension def RubyExtension.ruby_method puts "#{self}: A Ruby Extension" end end nghttp2-1.68.0/third-party/mruby/examples/mrbgems/ruby_extension_example/PaxHeaders/README.md0000644000000000000000000000013215077107276027214 xustar0030 mtime=1761382078.100420657 30 atime=1761382080.121411411 30 ctime=1761382108.453302252 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/ruby_extension_example/README.md0000644000175100017510000000013615077107276027604 0ustar00runnerrunner# Pure Ruby Extension Example This is an example gem which implements a pure Ruby extension. nghttp2-1.68.0/third-party/mruby/examples/mrbgems/ruby_extension_example/PaxHeaders/test0000644000000000000000000000013215077107334026632 xustar0030 mtime=1761382108.454302248 30 atime=1761382109.795298372 30 ctime=1761382108.454302248 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/ruby_extension_example/test/0000755000175100017510000000000015077107334027277 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/examples/mrbgems/ruby_extension_example/test/PaxHeaders/example.rb0000644000000000000000000000013215077107276030674 xustar0030 mtime=1761382078.100420657 30 atime=1761382080.121411411 30 ctime=1761382108.454302248 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/ruby_extension_example/test/example.rb0000644000175100017510000000012115077107276031256 0ustar00runnerrunnerassert('Ruby Extension Example') do RubyExtension.respond_to? :ruby_method end nghttp2-1.68.0/third-party/mruby/examples/mrbgems/PaxHeaders/mruby-YOUR-bigint0000644000000000000000000000013215077107334024267 xustar0030 mtime=1761382108.477302182 30 atime=1761382109.795298372 30 ctime=1761382108.477302182 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/mruby-YOUR-bigint/0000755000175100017510000000000015077107334024734 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/examples/mrbgems/mruby-YOUR-bigint/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276026466 xustar0030 mtime=1761382078.100420657 30 atime=1761382080.121411411 30 ctime=1761382108.477302182 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/mruby-YOUR-bigint/mrbgem.rake0000644000175100017510000000102015077107276027047 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-bigint') do |spec| spec.author = 'YOUR-NAME-HERE' spec.license = 'YOUR-LICENSE-HERE' spec.summary = 'Yet another multi-precision Integer extension' spec.homepage = 'https://gem.example/for/mruby-YOUR-bigint' spec.build.defines << 'MRB_USE_BIGINT' #spec.build.linker.libraries << 'gmp' # when uses libgmp spec.build.libmruby_core_objs << Dir.glob(File.join(__dir__, 'core/**/*.c')).map { |fn| objfile(fn.relative_path_from(__dir__).pathmap("#{spec.build_dir}/%X")) } end nghttp2-1.68.0/third-party/mruby/examples/mrbgems/mruby-YOUR-bigint/PaxHeaders/core0000644000000000000000000000013215077107334025217 xustar0030 mtime=1761382108.476302185 30 atime=1761382109.795298372 30 ctime=1761382108.476302185 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/mruby-YOUR-bigint/core/0000755000175100017510000000000015077107334025664 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/examples/mrbgems/mruby-YOUR-bigint/core/PaxHeaders/bigint.c0000644000000000000000000000013215077107276026721 xustar0030 mtime=1761382078.100420657 30 atime=1761382080.121411411 30 ctime=1761382108.476302185 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/mruby-YOUR-bigint/core/bigint.c0000644000175100017510000000321715077107276027314 0ustar00runnerrunner/* * If placed under the "mruby/examples/mrbgems/mruby-YOUR-bigint" directory, * this file is available under the Creative Commons Zero License (CC0). * Note that file is incomplete. * * TODO: If this file is copied and another implementation is written, * remove this comment block from the copied file. */ #include #include /* * The "mruby/internal.h" file should be placed after the other mruby header files. */ #include /* * The "mruby/presym.h" file is placed at the end of the mruby header file. */ #include /* * Define your own struct RBigint. * * - Object type must be MRB_TT_BIGINT. * - If the structure is named RBigint, MRB_OBJ_ALLOC() can be used as is. */ struct RBigint { /* * Put MRB_OBJECT_HEADER before the first member of the structure. */ MRB_OBJECT_HEADER; /* * Up to 3 words can be freely configured. */ size_t len; size_t capa; uintptr_t *num; }; /* * Assert with mrb_static_assert_object_size() that the entire structure is within 6 words. */ mrb_static_assert_object_size(struct RBigint); /* * The lower 16 bits of the object flags (`obj->flags`) can be used freely by the GEM author. */ #define MY_BIGINT_NEGATIVE_FLAG 1 #define MY_BIGINT_NEGATIVE_P(obj) ((obj)->flags & MY_BIGINT_NEGATIVE_FLAG) /* * Implement the functions declared in `#ifdef MRUBY_USE_BIGINT ... #endif` in the "mruby/internal.h" file. */ mrb_value mrb_bint_new_int(mrb_state *mrb, mrb_int x) { struct RBigint *obj = MRB_OBJ_ALLOC(mrb, MRB_TT_BIGINT, mrb->integer_class); ... return mrb_obj_value(obj); } /* * The implementation function continues... */ nghttp2-1.68.0/third-party/mruby/examples/mrbgems/mruby-YOUR-bigint/PaxHeaders/TODO-HINT.md0000644000000000000000000000013215077107276026200 xustar0030 mtime=1761382078.100420657 30 atime=1761382080.121411411 30 ctime=1761382108.475302188 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/mruby-YOUR-bigint/TODO-HINT.md0000644000175100017510000000370015077107276026570 0ustar00runnerrunner# Hints for creating your own bigint GEM This example gem, mruby-YOUR-bigint, is available under the Creative Commons Zero License (CC0). This file is placed for the purpose of describing hints for creating a `mruby-bigint` compatible GEM to realize multiple integers. The file structure in this example is as follows: ``` +- mruby-YOUR-bigint/ <- Make this directory public if necessary. | Change the name of copied directory. | +- TODO-HINT.md <- You are currently viewing this file. | Remove this from copied directory. | +- core/ | | | +- bigint.c <- Body of the implementation. | +- mrbgem.rake <- GEM name is "mruby-bigint". May be depended on by other GEMs. ``` Implementers of their own bigints should copy below this directory to another directory and do the following: - Rewrite `spec.author`, `spec.license`, `spec.homepage` and `spec.summary` in `/mrbgem.rake` file to those of your own implementers. - Implement the respective functions in `/core/bigint.c`. - Define and use an object structure for `MRB_TT_BIGINT` type-tag. It is recommended to use `mrb_static_assert_object_size()` to ensure that the size of the object structure is within six words. - Delete this file from the destination of the copy. If you wish to use it as an alternative to the `mruby-bigint` provided by mruby, please leave the GEM name in `/mrbgem.rake` as it is. This is an important factor when it is depended from other GEMs with `spec.add_dependency 'mruby-bigint'`. The name of the top directory of the GEM can be changed arbitrarily. The name of the Git repository can also be changed arbitrarily. Note that there is no need for an initialization function as there is in a normal GEM. If you need it, create a file `/src/bigint.c` for example, and implement the `mrb_mruby_bigint_gem_init()` function. nghttp2-1.68.0/third-party/mruby/examples/mrbgems/PaxHeaders/cdata_extension_example0000644000000000000000000000013215077107334025746 xustar0030 mtime=1761382108.458302237 30 atime=1761382109.795298372 30 ctime=1761382108.458302237 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/cdata_extension_example/0000755000175100017510000000000015077107334026413 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/examples/mrbgems/cdata_extension_example/PaxHeaders/mrbgem.rake0000644000000000000000000000013115077107276030144 xustar0030 mtime=1761382078.099420661 30 atime=1761382080.121411411 29 ctime=1761382108.45730224 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/cdata_extension_example/mrbgem.rake0000644000175100017510000000150615077107276030537 0ustar00runnerrunnerMRuby::Gem::Specification.new('cdata_extension_example') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' # Add compile flags # spec.cc.flags << '-g' # Add cflags to all # spec.mruby.cc.flags << '-g' # Add libraries # spec.linker.libraries << 'external_lib' # Default build files # spec.rbfiles = Dir.glob("#{dir}/mrblib/*.rb") # spec.objs = Dir.glob("#{dir}/src/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) } # spec.test_rbfiles = Dir.glob("#{dir}/test/*.rb") # spec.test_objs = Dir.glob("#{dir}/test/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) } # spec.test_preload = 'test/assert.rb' # Values accessible as TEST_ARGS inside test scripts # spec.test_args = {'tmp_dir' => Dir::tmpdir} end nghttp2-1.68.0/third-party/mruby/examples/mrbgems/cdata_extension_example/PaxHeaders/README.md0000644000000000000000000000013215077107276027307 xustar0030 mtime=1761382078.099420661 30 atime=1761382080.121411411 30 ctime=1761382108.458302237 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/cdata_extension_example/README.md0000644000175100017510000000013515077107276027676 0ustar00runnerrunner# C Data Extension Example This is an example gem which implements a C extension with Data. nghttp2-1.68.0/third-party/mruby/examples/mrbgems/cdata_extension_example/PaxHeaders/test0000644000000000000000000000013215077107334026725 xustar0030 mtime=1761382108.459302234 30 atime=1761382109.795298372 30 ctime=1761382108.459302234 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/cdata_extension_example/test/0000755000175100017510000000000015077107334027372 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/examples/mrbgems/cdata_extension_example/test/PaxHeaders/example.c0000644000000000000000000000013215077107276030606 xustar0030 mtime=1761382078.100420657 30 atime=1761382080.121411411 30 ctime=1761382108.459302234 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/cdata_extension_example/test/example.c0000644000175100017510000000016015077107276031173 0ustar00runnerrunner#include void mrb_cdata_extension_example_gem_test(mrb_state *mrb) { /* test initializer in C */ } nghttp2-1.68.0/third-party/mruby/examples/mrbgems/cdata_extension_example/PaxHeaders/src0000644000000000000000000000013215077107334026535 xustar0030 mtime=1761382108.461302228 30 atime=1761382109.795298372 30 ctime=1761382108.461302228 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/cdata_extension_example/src/0000755000175100017510000000000015077107334027202 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/examples/mrbgems/cdata_extension_example/src/PaxHeaders/example.c0000644000000000000000000000013215077107276030416 xustar0030 mtime=1761382078.099420661 30 atime=1761382080.121411411 30 ctime=1761382108.461302228 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/cdata_extension_example/src/example.c0000644000175100017510000000322515077107276031010 0ustar00runnerrunner#include #include #include #include #include static void mrb_foo_free(mrb_state *mrb, void *ptr) { /* custom destructor */ mrb_free(mrb, ptr); } struct mrb_data_type mrb_foo_type = { "Foo", mrb_foo_free }; struct Foo { int bar; char baz[32]; }; static mrb_value mrb_foo_initialize(mrb_state *mrb, mrb_value self) { struct Foo *f; f = (struct Foo*)mrb_malloc(mrb, sizeof(struct Foo)); f->bar = 0; DATA_PTR(self) = f; DATA_TYPE(self) = &mrb_foo_type; return self; } static mrb_value mrb_foo_get_bar(mrb_state *mrb, mrb_value self) { struct Foo *f; f = (struct Foo*)mrb_data_get_ptr(mrb, self, &mrb_foo_type); if (f == NULL) { mrb_raise(mrb, E_RUNTIME_ERROR, "uninitialized data"); } return mrb_fixnum_value(f->bar); } static mrb_value mrb_foo_set_bar(mrb_state *mrb, mrb_value self) { struct Foo *f; int v; f = (struct Foo*)mrb_data_get_ptr(mrb, self, &mrb_foo_type); if (f == NULL) { mrb_raise(mrb, E_RUNTIME_ERROR, "uninitialized data"); } mrb_get_args(mrb, "i", &v); f->bar = v; return mrb_fixnum_value(f->bar); } void mrb_cdata_extension_example_gem_init(mrb_state* mrb) { struct RClass *class_foo; class_foo = mrb_define_class(mrb, "Foo", mrb->object_class); MRB_SET_INSTANCE_TT(class_foo, MRB_TT_CDATA); mrb_define_method(mrb, class_foo, "initialize", mrb_foo_initialize, MRB_ARGS_NONE()); mrb_define_method(mrb, class_foo, "bar", mrb_foo_get_bar, MRB_ARGS_NONE()); mrb_define_method(mrb, class_foo, "bar=", mrb_foo_set_bar, MRB_ARGS_REQ(1)); } void mrb_cdata_extension_example_gem_final(mrb_state* mrb) { /* gem finalizer */ } nghttp2-1.68.0/third-party/mruby/examples/mrbgems/PaxHeaders/c_extension_example0000644000000000000000000000013215077107334025114 xustar0030 mtime=1761382108.469302205 30 atime=1761382109.795298372 30 ctime=1761382108.469302205 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_extension_example/0000755000175100017510000000000015077107334025561 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_extension_example/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276027313 xustar0030 mtime=1761382078.099420661 30 atime=1761382080.121411411 30 ctime=1761382108.468302208 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_extension_example/mrbgem.rake0000644000175100017510000000150215077107276027701 0ustar00runnerrunnerMRuby::Gem::Specification.new('c_extension_example') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' # Add compile flags # spec.cc.flags << '-g' # Add cflags to all # spec.mruby.cc.flags << '-g' # Add libraries # spec.linker.libraries << 'external_lib' # Default build files # spec.rbfiles = Dir.glob("#{dir}/mrblib/*.rb") # spec.objs = Dir.glob("#{dir}/src/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) } # spec.test_rbfiles = Dir.glob("#{dir}/test/*.rb") # spec.test_objs = Dir.glob("#{dir}/test/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) } # spec.test_preload = 'test/assert.rb' # Values accessible as TEST_ARGS inside test scripts # spec.test_args = {'tmp_dir' => Dir::tmpdir} end nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_extension_example/PaxHeaders/README.md0000644000000000000000000000013215077107276026455 xustar0030 mtime=1761382078.099420661 30 atime=1761382080.121411411 30 ctime=1761382108.469302205 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_extension_example/README.md0000644000175100017510000000011615077107276027043 0ustar00runnerrunner# C Extension Example This is an example gem which implements a C extension. nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_extension_example/PaxHeaders/test0000644000000000000000000000013215077107334026073 xustar0030 mtime=1761382108.472302196 30 atime=1761382109.795298372 30 ctime=1761382108.472302196 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_extension_example/test/0000755000175100017510000000000015077107334026540 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_extension_example/test/PaxHeaders/example.rb0000644000000000000000000000013215077107276030135 xustar0030 mtime=1761382078.099420661 30 atime=1761382080.121411411 30 ctime=1761382108.471302199 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_extension_example/test/example.rb0000644000175100017510000000011015077107276030515 0ustar00runnerrunnerassert('C Extension Example') do CExtension.respond_to? :c_method end nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_extension_example/test/PaxHeaders/example.c0000644000000000000000000000013215077107276027754 xustar0030 mtime=1761382078.099420661 30 atime=1761382080.121411411 30 ctime=1761382108.472302196 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_extension_example/test/example.c0000644000175100017510000000015415077107276030344 0ustar00runnerrunner#include void mrb_c_extension_example_gem_test(mrb_state *mrb) { /* test initializer in C */ } nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_extension_example/PaxHeaders/src0000644000000000000000000000013215077107334025703 xustar0030 mtime=1761382108.473302194 30 atime=1761382109.795298372 30 ctime=1761382108.473302194 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_extension_example/src/0000755000175100017510000000000015077107334026350 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_extension_example/src/PaxHeaders/example.c0000644000000000000000000000013215077107276027564 xustar0030 mtime=1761382078.099420661 30 atime=1761382080.121411411 30 ctime=1761382108.473302194 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_extension_example/src/example.c0000644000175100017510000000104515077107276030154 0ustar00runnerrunner#include #include #include static mrb_value mrb_c_method(mrb_state *mrb, mrb_value self) { mrb_ensure_string_type(mrb, self); printf("%s: A C Extension\n", mrb_str_to_cstr(mrb, self)); return self; } void mrb_c_extension_example_gem_init(mrb_state* mrb) { struct RClass *class_cextension = mrb_define_module(mrb, "CExtension"); mrb_define_class_method(mrb, class_cextension, "c_method", mrb_c_method, MRB_ARGS_NONE()); } void mrb_c_extension_example_gem_final(mrb_state* mrb) { /* finalizer */ } nghttp2-1.68.0/third-party/mruby/examples/mrbgems/PaxHeaders/c_and_ruby_extension_example0000644000000000000000000000013215077107334026777 xustar0030 mtime=1761382108.463302222 30 atime=1761382109.795298372 30 ctime=1761382108.463302222 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/0000755000175100017510000000000015077107334027444 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/PaxHeaders/mrbgem.rak0000644000000000000000000000013215077107276031031 xustar0030 mtime=1761382078.099420661 30 atime=1761382080.121411411 30 ctime=1761382108.462302225 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/mrbgem.rake0000644000175100017510000000151115077107276031564 0ustar00runnerrunnerMRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' # Add compile flags # spec.cc.flags << '' # Add cflags to all # spec.mruby.cc.flags << '-g' # Add libraries # spec.linker.libraries << 'external_lib' # Default build files # spec.rbfiles = Dir.glob("#{dir}/mrblib/*.rb") # spec.objs = Dir.glob("#{dir}/src/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) } # spec.test_rbfiles = Dir.glob("#{dir}/test/*.rb") # spec.test_objs = Dir.glob("#{dir}/test/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) } # spec.test_preload = 'test/assert.rb' # Values accessible as TEST_ARGS inside test scripts # spec.test_args = {'tmp_dir' => Dir::tmpdir} end nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/PaxHeaders/mrblib0000644000000000000000000000013215077107334030246 xustar0030 mtime=1761382108.466302214 30 atime=1761382109.795298372 30 ctime=1761382108.466302214 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/mrblib/0000755000175100017510000000000015077107334030713 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/mrblib/PaxHeaders/exa0000644000000000000000000000013215077107276031030 xustar0030 mtime=1761382078.099420661 30 atime=1761382080.121411411 30 ctime=1761382108.466302214 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/mrblib/example.rb0000644000175100017510000000014615077107276032701 0ustar00runnerrunnermodule CRubyExtension def CRubyExtension.ruby_method puts "#{self}: A Ruby Extension" end end nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/PaxHeaders/README.md0000644000000000000000000000013215077107276030340 xustar0030 mtime=1761382078.099420661 30 atime=1761382080.121411411 30 ctime=1761382108.463302222 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/README.md0000644000175100017510000000014015077107276030723 0ustar00runnerrunner# C and Ruby Extension Example This is an example gem which implements a C and Ruby extension. nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/PaxHeaders/test0000644000000000000000000000013015077107334027754 xustar0029 mtime=1761382108.46430222 30 atime=1761382109.795298372 29 ctime=1761382108.46430222 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/test/0000755000175100017510000000000015077107334030423 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/test/PaxHeaders/examp0000644000000000000000000000013115077107276031074 xustar0030 mtime=1761382078.099420661 30 atime=1761382080.121411411 29 ctime=1761382108.46430222 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/test/example.rb0000644000175100017510000000026215077107276032410 0ustar00runnerrunnerassert('C and Ruby Extension Example 1') do CRubyExtension.respond_to? :c_method end assert('C and Ruby Extension Example 2') do CRubyExtension.respond_to? :ruby_method end nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/PaxHeaders/src0000644000000000000000000000013215077107334027566 xustar0030 mtime=1761382108.467302211 30 atime=1761382109.795298372 30 ctime=1761382108.467302211 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/src/0000755000175100017510000000000015077107334030233 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/src/PaxHeaders/exampl0000644000000000000000000000013215077107276031061 xustar0030 mtime=1761382078.099420661 30 atime=1761382080.121411411 30 ctime=1761382108.467302211 nghttp2-1.68.0/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/src/example.c0000644000175100017510000000107315077107276032040 0ustar00runnerrunner#include #include #include static mrb_value mrb_c_method(mrb_state *mrb, mrb_value self) { mrb_ensure_string_type(mrb, self); printf("%s: A C Extension\n", mrb_str_to_cstr(mrb, self)); return self; } void mrb_c_and_ruby_extension_example_gem_init(mrb_state* mrb) { struct RClass *class_cextension = mrb_define_module(mrb, "CRubyExtension"); mrb_define_class_method(mrb, class_cextension, "c_method", mrb_c_method, MRB_ARGS_NONE()); } void mrb_c_and_ruby_extension_example_gem_final(mrb_state* mrb) { /* finalizer */ } nghttp2-1.68.0/third-party/mruby/PaxHeaders/LICENSE0000644000000000000000000000013115077107276016677 xustar0029 mtime=1761382078.09542068 30 atime=1761382080.118411425 30 ctime=1761382108.553301962 nghttp2-1.68.0/third-party/mruby/LICENSE0000644000175100017510000000204515077107276017271 0ustar00runnerrunnerCopyright (c) 2010- mruby developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. nghttp2-1.68.0/third-party/mruby/PaxHeaders/benchmark0000644000000000000000000000013215077107334017543 xustar0030 mtime=1761382108.355302535 30 atime=1761382109.795298372 30 ctime=1761382108.355302535 nghttp2-1.68.0/third-party/mruby/benchmark/0000755000175100017510000000000015077107334020210 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/benchmark/PaxHeaders/bm_ao_render.rb0000644000000000000000000000013215077107276022566 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.118411425 30 ctime=1761382108.350302549 nghttp2-1.68.0/third-party/mruby/benchmark/bm_ao_render.rb0000644000175100017510000001503315077107276023160 0ustar00runnerrunner# AO render benchmark # Original program (C) Syoyo Fujita in JavaScript (and other languages) # https://code.google.com/p/aobench/ # Ruby(yarv2llvm) version by Hideki Miura # mruby version by Hideki Miura # IMAGE_WIDTH = Integer(ARGV[0] || 64) IMAGE_HEIGHT = IMAGE_WIDTH NSUBSAMPLES = 2 NAO_SAMPLES = 8 module Rand # Use xorshift @x = 123456789 @y = 362436069 @z = 521288629 @w = 88675123 BNUM = 1 << 29 BNUMF = BNUM.to_f def self.rand x = @x t = x ^ ((x & 0xfffff) << 11) w = @w @x, @y, @z = @y, @z, w w = @w = (w ^ (w >> 19) ^ (t ^ (t >> 8))) (w % BNUM) / BNUMF end end class Vec def initialize(x, y, z) @x = x @y = y @z = z end def x=(v); @x = v; end def y=(v); @y = v; end def z=(v); @z = v; end def x; @x; end def y; @y; end def z; @z; end def vadd(b) Vec.new(@x + b.x, @y + b.y, @z + b.z) end def vsub(b) Vec.new(@x - b.x, @y - b.y, @z - b.z) end def vcross(b) Vec.new(@y * b.z - @z * b.y, @z * b.x - @x * b.z, @x * b.y - @y * b.x) end def vdot(b) r = @x * b.x + @y * b.y + @z * b.z r end def vlength Math.sqrt(@x * @x + @y * @y + @z * @z) end def vnormalize len = vlength v = Vec.new(@x, @y, @z) if len > 1.0e-17 v.x = v.x / len v.y = v.y / len v.z = v.z / len end v end end class Sphere def initialize(center, radius) @center = center @radius = radius end def center; @center; end def radius; @radius; end def intersect(ray, isect) rs = ray.org.vsub(@center) b = rs.vdot(ray.dir) c = rs.vdot(rs) - (@radius * @radius) d = b * b - c if d > 0.0 t = - b - Math.sqrt(d) if t > 0.0 and t < isect.t isect.t = t isect.hit = true isect.pl = Vec.new(ray.org.x + ray.dir.x * t, ray.org.y + ray.dir.y * t, ray.org.z + ray.dir.z * t) n = isect.pl.vsub(@center) isect.n = n.vnormalize end end end end class Plane def initialize(p, n) @p = p @n = n end def intersect(ray, isect) d = -@p.vdot(@n) v = ray.dir.vdot(@n) v0 = v if v < 0.0 v0 = -v end if v0 < 1.0e-17 return end t = -(ray.org.vdot(@n) + d) / v if t > 0.0 and t < isect.t isect.hit = true isect.t = t isect.n = @n isect.pl = Vec.new(ray.org.x + t * ray.dir.x, ray.org.y + t * ray.dir.y, ray.org.z + t * ray.dir.z) end end end class Ray def initialize(org, dir) @org = org @dir = dir end def org; @org; end def org=(v); @org = v; end def dir; @dir; end def dir=(v); @dir = v; end end class Isect def initialize @t = 10000000.0 @hit = false @pl = Vec.new(0.0, 0.0, 0.0) @n = Vec.new(0.0, 0.0, 0.0) end def t; @t; end def t=(v); @t = v; end def hit; @hit; end def hit=(v); @hit = v; end def pl; @pl; end def pl=(v); @pl = v; end def n; @n; end def n=(v); @n = v; end end def clamp(f) i = f * 255.5 if i > 255.0 i = 255.0 end if i < 0.0 i = 0.0 end i.to_i end def orthoBasis(basis, n) basis[2] = Vec.new(n.x, n.y, n.z) basis[1] = Vec.new(0.0, 0.0, 0.0) if n.x < 0.6 and n.x > -0.6 basis[1].x = 1.0 elsif n.y < 0.6 and n.y > -0.6 basis[1].y = 1.0 elsif n.z < 0.6 and n.z > -0.6 basis[1].z = 1.0 else basis[1].x = 1.0 end basis[0] = basis[1].vcross(basis[2]) basis[0] = basis[0].vnormalize basis[1] = basis[2].vcross(basis[0]) basis[1] = basis[1].vnormalize end class Scene def initialize @spheres = Array.new @spheres[0] = Sphere.new(Vec.new(-2.0, 0.0, -3.5), 0.5) @spheres[1] = Sphere.new(Vec.new(-0.5, 0.0, -3.0), 0.5) @spheres[2] = Sphere.new(Vec.new(1.0, 0.0, -2.2), 0.5) @plane = Plane.new(Vec.new(0.0, -0.5, 0.0), Vec.new(0.0, 1.0, 0.0)) end def ambient_occlusion(isect) basis = Array.new(3) orthoBasis(basis, isect.n) ntheta = NAO_SAMPLES nphi = NAO_SAMPLES eps = 0.0001 occlusion = 0.0 p0 = Vec.new(isect.pl.x + eps * isect.n.x, isect.pl.y + eps * isect.n.y, isect.pl.z + eps * isect.n.z) nphi.times do ntheta.times do r = Rand::rand phi = 2.0 * 3.14159265 * Rand::rand x = Math.cos(phi) * Math.sqrt(1.0 - r) y = Math.sin(phi) * Math.sqrt(1.0 - r) z = Math.sqrt(r) rx = x * basis[0].x + y * basis[1].x + z * basis[2].x ry = x * basis[0].y + y * basis[1].y + z * basis[2].y rz = x * basis[0].z + y * basis[1].z + z * basis[2].z raydir = Vec.new(rx, ry, rz) ray = Ray.new(p0, raydir) occisect = Isect.new @spheres[0].intersect(ray, occisect) @spheres[1].intersect(ray, occisect) @spheres[2].intersect(ray, occisect) @plane.intersect(ray, occisect) if occisect.hit occlusion = occlusion + 1.0 end end end occlusion = (ntheta.to_f * nphi.to_f - occlusion) / (ntheta.to_f * nphi.to_f) Vec.new(occlusion, occlusion, occlusion) end def render(w, h, nsubsamples) nsf = nsubsamples.to_f nsfs = nsf * nsf h.times do |y| w.times do |x| rad = Vec.new(0.0, 0.0, 0.0) # Subsampling nsubsamples.times do |v| nsubsamples.times do |u| wf = w.to_f hf = h.to_f xf = x.to_f yf = y.to_f uf = u.to_f vf = v.to_f px = (xf + (uf / nsf) - (wf / 2.0)) / (wf / 2.0) py = -(yf + (vf / nsf) - (hf / 2.0)) / (hf / 2.0) eye = Vec.new(px, py, -1.0).vnormalize ray = Ray.new(Vec.new(0.0, 0.0, 0.0), eye) isect = Isect.new @spheres[0].intersect(ray, isect) @spheres[1].intersect(ray, isect) @spheres[2].intersect(ray, isect) @plane.intersect(ray, isect) if isect.hit col = ambient_occlusion(isect) rad.x = rad.x + col.x rad.y = rad.y + col.y rad.z = rad.z + col.z end end end r = rad.x / nsfs g = rad.y / nsfs b = rad.z / nsfs printf("%c", clamp(r)) printf("%c", clamp(g)) printf("%c", clamp(b)) end end end end # File.open("ao.ppm", "w") do |fp| printf("P6\n") printf("%d %d\n", IMAGE_WIDTH, IMAGE_HEIGHT) printf("255\n", IMAGE_WIDTH, IMAGE_HEIGHT) Scene.new.render(IMAGE_WIDTH, IMAGE_HEIGHT, NSUBSAMPLES) # Scene.new.render(256, 256, 2) # end nghttp2-1.68.0/third-party/mruby/benchmark/PaxHeaders/bm_so_mandelbrot.rb0000644000000000000000000000013215077107276023460 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.118411425 30 ctime=1761382108.351302546 nghttp2-1.68.0/third-party/mruby/benchmark/bm_so_mandelbrot.rb0000644000175100017510000000255015077107276024052 0ustar00runnerrunner# The Computer Language Benchmarks Game # http://shootout.alioth.debian.org/ # # contributed by Karl von Laudermann # modified by Jeremy Echols size = 600 # ARGV[0].to_i puts "P4\n#{size} #{size}" ITER = 49 # Iterations - 1 for easy for..in looping LIMIT_SQUARED = 4.0 # Presquared limit byte_acc = 0 bit_num = 0 count_size = size - 1 # Precomputed size for easy for..in looping # For..in loops are faster than .upto, .downto, .times, etc. for y in 0..count_size for x in 0..count_size zr = 0.0 zi = 0.0 cr = (2.0*x/size)-1.5 ci = (2.0*y/size)-1.0 escape = false # To make use of the for..in code, we use a dummy variable, # like one would in C for dummy in 0..ITER tr = zr*zr - zi*zi + cr ti = 2*zr*zi + ci zr, zi = tr, ti if (zr*zr+zi*zi) > LIMIT_SQUARED escape = true break end end byte_acc = (byte_acc << 1) | (escape ? 0b0 : 0b1) bit_num += 1 # Code is very similar for these cases, but using separate blocks # ensures we skip the shifting when it's unnecessary, which is most cases. if (bit_num == 8) print byte_acc.chr byte_acc = 0 bit_num = 0 elsif (x == count_size) byte_acc <<= (8 - bit_num) print byte_acc.chr byte_acc = 0 bit_num = 0 end end end nghttp2-1.68.0/third-party/mruby/benchmark/PaxHeaders/bm_app_lc_fizzbuzz.rb0000644000000000000000000000013215077107276024043 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.118411425 30 ctime=1761382108.355302535 nghttp2-1.68.0/third-party/mruby/benchmark/bm_app_lc_fizzbuzz.rb0000644000175100017510000003667315077107276024452 0ustar00runnerrunner# # FizzBuzz program using only lambda calculus # # This program is quoted from # "Understanding Computation" by Tom Stuart # http://computationbook.com/ # # You can understand why this program works fine by reading this book. # solution = -> k { -> f { -> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> l { -> x { -> g { -> b { b }[-> p { p[-> x { -> y { x } }] }[l]][x][-> y { g[f[-> l { -> p { p[-> x { -> y { y } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][x][g]][-> l { -> p { p[-> x { -> y { x } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][y] }] } } } }][k][-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> l { -> x { -> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[l][f[x]] } }] } }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[m][n]][-> x { -> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[f[-> n { -> p { -> x { p[n[p][x]] } } }[m]][n]][m][x] }][-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]] } } }][-> p { -> x { p[x] } }][-> p { -> x { p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[x]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] } }]][-> n { -> b { b }[-> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> p { -> x { p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[x]]]]]]]]]]]]]]] } }]]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]][-> b { b }[-> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> p { -> x { p[p[p[x]]] } }]]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]][-> b { b }[-> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> n { -> l { -> x { -> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> l { -> x { -> g { -> b { b }[-> p { p[-> x { -> y { x } }] }[l]][x][-> y { g[f[-> l { -> p { p[-> x { -> y { y } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][x][g]][-> l { -> p { p[-> x { -> y { x } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][y] }] } } } }][l][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][x]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }] } }[-> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]][-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> x { f[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { -> n { -> p { -> x { p[n[p][x]] } } }[f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n]][x] }][-> p { -> x { x } }] } } }][n][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][x] }]][-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]] } }][n]]]] }] FIRST = -> l { LEFT[RIGHT[l]] } IF = -> b { b } LEFT = -> p { p[-> x { -> y { x } } ] } RIGHT = -> p { p[-> x { -> y { y } } ] } IS_EMPTY = LEFT REST = -> l { RIGHT[RIGHT[l]] } def to_integer(proc) proc[-> n { n + 1 }][0] end def to_boolean(proc) IF[proc][true][false] end def to_array(proc) array = [] until to_boolean(IS_EMPTY[proc]) array.push(FIRST[proc]) proc = REST[proc] end array end def to_char(c) '0123456789BFiuz'.slice(to_integer(c)) end def to_string(s) to_array(s).map { |c| to_char(c) }.join end answer = to_array(solution).map do |p| to_string(p) end # puts answer nghttp2-1.68.0/third-party/mruby/benchmark/PaxHeaders/bm_so_lists.rb0000644000000000000000000000013215077107276022467 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.118411425 30 ctime=1761382108.354302537 nghttp2-1.68.0/third-party/mruby/benchmark/bm_so_lists.rb0000644000175100017510000000220715077107276023060 0ustar00runnerrunner# from http://www.bagley.org/~doug/shootout/bench/lists/lists.ruby # which is lost long time ago. The past content can be retrieved from # https://web.archive.org/web/20040805115204/http://www.bagley.org/~doug/shootout/bench/lists/lists.ruby NUM = 300 SIZE = 10000 def test_lists # create a list of integers (Li1) from 1 to SIZE li1 = (1..SIZE).to_a # copy the list to li2 (not by individual items) li2 = li1.dup # remove each individual item from left side of li2 and # append to right side of li3 (preserving order) li3 = Array.new while (not li2.empty?) li3.push(li2.shift) end # li2 must now be empty # remove each individual item from right side of li3 and # append to right side of li2 (reversing list) until li3.empty? li2.push(li3.pop) end # li3 must now be empty # reverse li1 in place li1.reverse! # check that first item is now SIZE if li1[0] != SIZE p "not SIZE" 0 else # compare li1 and li2 for equality if li1 != li2 return(0) else # return the length of the list li1.length end end end i = 0 while i` To update use `pre-commit autoupdate` Sometimes you might need to skip one or more hooks which can be done with the `SKIP` environment variable. `$ SKIP=yamllint git commit -m "foo"` For convenience, we have added `pre-commit run --all-files`, `pre-commit install` and `pre-commit autoupdate` to both the Makefile and the Rakefile. Run them with: - `make check` or `rake check` - `make checkinstall` or `rake checkinstall` - `make checkupdate` or `rake checkupdate` To configure `pre-commit` you can modify the config file [.pre-commit-config.yaml](.pre-commit-config.yaml). We use [GitHub Actions](.github/workflows/lint.yml) to run `pre-commit` on every pull request. ### pre-commit quick links - [Quick start](https://pre-commit.com/#quick-start) - [Usage](https://pre-commit.com/#usage) - [pre-commit autoupdate](https://pre-commit.com/#pre-commit-autoupdate) - [Temporarily disabling hooks](https://pre-commit.com/#temporarily-disabling-hooks) ## Docker We have both a `Dockerfile` and `docker-compose.yml` files in the repository root. You can run these with the command line or use [Docker Desktop](https://www.docker.com/products/docker-desktop/). The Docker image is running Debian bullseye with Ruby and Python installed. You can build the Docker image with: `$ docker-compose build test` So far we just have one service: `test`. Running the default `docker-compose` command will create the Docker image, spin up a container and then build and run all mruby tests. The default `docker-compose` command is: `$ docker-compose -p mruby run test` You can also use Make or Rake to run the default `docker-compose` command from above: - `make composetest` - `rake composetest` List your Docker images with: ```console $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE mruby-test latest ec60f9536948 29 seconds ago 1.29GB ``` You can also run any custom `docker-compose` command which will override the default. For example to run `pre-commit run --all-files` type: `$ docker-compose -p mruby run test pre-commit run --all-files` For convenience, you can also run `pre-commit` with: - `make composecheck` - `rake composecheck` The bonus of running `pre-commit` with `docker-compose` is that you won't need to install `pre-commit` and the hooks on your local machine. And that also means you won't need to install `brew`, `conda` or `pip`. Note limitation: currently running `pre-commit` with `docker-compose` we skip the `check-executables-have-shebangs` hook. Two more examples of custom `docker-compose` commands are: - `$ docker-compose -p mruby run test ls` - `$ docker-compose -p mruby run test rake doc:api` If you want to test using a different `docker-compose` YAML config file you can use the `-f` flag: `$ docker-compose -p mruby -f docker-compose.test.yml run test` - - ## Spell Checking We are using `pre-commit` to run [codespell](https://github.com/codespell-project/codespell) to check code for common misspellings. We have a small custom dictionary file [codespell.txt](.github/linters/codespell.txt). ## Coding conventions How to style your C and Ruby code which you want to submit. ### C code The core part (parser, bytecode-interpreter, core-lib, etc.) of mruby is written in the C programming language. Please note the following hints for your C code: #### Comply with C99 (ISO/IEC 9899:1999) mruby should be highly portable to other systems and compilers. For this it is recommended to keep your code as close as possible to the C99 standard (). Visual C++ is also an important target for mruby (supported version is 2013 or later). For this reason features that are not supported by Visual C++ may not be used (e.g. `%z` of `strftime()`). NOTE: Old GCC requires `-std=gnu99` option to enable C99 support. #### Reduce library dependencies to a minimum The dependencies to libraries should be kept to an absolute minimum. This increases the portability but makes it also easier to cut away parts of mruby on-demand. #### Insert a break after the function return value: ```c int main(void) { ... } ``` ### Ruby code Parts of the standard library of mruby are written in the Ruby programming language itself. Please note the following hints for your Ruby code: #### Comply with the Ruby standard (ISO/IEC 30170:2012) mruby is currently targeting to execute Ruby code which complies to ISO/IEC 30170:2012 (), unless there's a clear reason, e.g. the latest Ruby has changed behavior from ISO. ## Building documentation ### mruby API - [YARD](https://yardoc.org/) - YARD is a documentation generation tool for the Ruby programming language - [yard-mruby](https://rubygems.org/gems/yard-mruby) - Document mruby sources with YARD - [yard-coderay](https://rubygems.org/gems/yard-coderay) - Adds coderay syntax highlighting to YARD docs ### C API - [Doxygen](https://www.doxygen.nl/) - Generate documentation from source code - [Graphviz](https://graphviz.org/) - Graphviz is open source graph visualization software nghttp2-1.68.0/third-party/mruby/PaxHeaders/NEWS0000644000000000000000000000013115077107276016371 xustar0029 mtime=1761382078.09542068 30 atime=1761382080.118411425 30 ctime=1761382108.230302896 nghttp2-1.68.0/third-party/mruby/NEWS0000644000175100017510000005615715077107276017000 0ustar00runnerrunnerNEWS ---- # User visible changes in `mruby3.4` from `mruby3.3` "**_NOTE_**:" are changes to be aware of. # The language - mruby now supports `private` and `protected` visibility ([b0db0bd](https://github.com/mruby/mruby/commit/b0db0bd)) - Maximum length of inlined symbols reduced from 5 to 4 characters to provide space for visibility flags ([6442a01](https://github.com/mruby/mruby/commit/6442a01)) - Many methods are made private according to CRuby visibility ([4a0e806](https://github.com/mruby/mruby/commit/4a0e806)) - Generate OP_SSEND for `self.method` type calls ([111fe4b](https://github.com/mruby/mruby/commit/111fe4b)) - `initialize` method will be always private ([eb8b412](https://github.com/mruby/mruby/commit/eb8b412)) - Add new hooks `method_removed`, `method_undefined` ([9c74f6e](https://github.com/mruby/mruby/commit/9c74f6e)) - Add new hooks `singleton_method_removed`, `singleton_method_undefined` ([0863c08](https://github.com/mruby/mruby/commit/0863c08)) - Updated `OP_DEF` output from codedump ([3a3e877](https://github.com/mruby/mruby/commit/3a3e877)) - Better handling of binary strings, e.g. String#b ([b0127f0](https://github.com/mruby/mruby/commit/b0127f0)) - Hash `to_s` format has changed ([baeeb5e](https://github.com/mruby/mruby/commit/baeeb5e)) - Some encoding related method such as `#force_encoding` ([e47b4ca](https://github.com/mruby/mruby/commit/e47b4ca)), `#b` ([b0127f0](https://github.com/mruby/mruby/commit/b0127f0)) - Constant folding for `String#+` ([6687bdd](https://github.com/mruby/mruby/commit/6687bdd)) - Remove Float bit-operation ([db8368f](https://github.com/mruby/mruby/commit/db8368f)) - use SWAR technique for strlen performance ([cbb31e6](https://github.com/mruby/mruby/commit/cbb31e6)) - use merge sort for `Array#sort` ([5bd63d6](https://github.com/mruby/mruby/commit/5bd63d6)) # Changes in C API - pool.c renamed to mempool.c (and mrb_pool to mrb_mempool) ([49525fa](https://github.com/mruby/mruby/commit/49525fa)) - mrb_pool_value renamed to mrb_irep_pool to reduce confusion ([62ef5db](https://github.com/mruby/mruby/commit/62ef5db)) - rename BOXNIX_SET_VALUE to BOXNO_SET_VALUE ([#6397](https://github.com/mruby/mruby/pull/6397)) - `MRB_FROZEN_P()` is replaced by `mrb_frozen_p()` ([c11d18e](https://github.com/mruby/mruby/commit/c11d18e)) - rename `color` to `gc_color` ([0e79f6b](https://github.com/mruby/mruby/commit/0e79f6b), [1e36d76](https://github.com/mruby/mruby/commit/1e36d76)) - add `obj->frozen` instead of flags `MRB_SET_FROZEN_FLAG`/`MRB_UNSET_FROZEN_FLAG` ([8276143](https://github.com/mruby/mruby/commit/8276143)) # Build & Configuration - New Build Target: `test:run:serial`, `test:run:serial:lib`, `test:run:serial:bin` ([#6423](https://github.com/mruby/mruby/pull/6423)) - New Platform: PlayStation Portable ([#6022](https://github.com/mruby/mruby/pull/6465)) - New Platform: emscripten ([#6487](https://github.com/mruby/mruby/pull/6487)) - New Config: no-float (with MRB_NO_FLOAT) ([32200f1](https://github.com/mruby/mruby/commit/32200f1)) # Changes in mrbgems - **mruby-print**: removed; if you do not use `mruby-io`, mruby use `#print` etc. in the core ([8c8bbd9](https://github.com/mruby/mruby/commit/8c8bbd9)) - **mruby-enum-lazy**: Add Enumerable::Lazy#grep_v to mruby-enum-lazy ([#6171](https://github.com/mruby/mruby/pull/6171)) - **mruby-io**: Add `level` argument to `File.dirname` ([#6463](https://github.com/mruby/mruby/pull/6463)) - **mruby-io**: File.absolute_path? ([#6482](https://github.com/mruby/mruby/pull/6482)) - **mruby-io**: File.absolute_path ([96113a2](https://github.com/mruby/mruby/commit/96113a2)) - **mruby-toplevel-ext**: top-level public/private/protected moved to the core ([2a876d2](https://github.com/mruby/mruby/commit/2a876d2)) - **mruby-metaprog**: method list methods now works according to the visibility ([9229da1](https://github.com/mruby/mruby/commit/9229da1)) - **mruby-metaprog**: `public_instance_methods`, `private_instance_methods`, `protected_instance_methods` ([9e3e7b2](https://github.com/mruby/mruby/commit/9e3e7b2)) - **mruby-encoding**: MRB_UTF8_STRING turned on automatically with this gem ([74bdae9](https://github.com/mruby/mruby/commit/74bdae9)) # Fixed GitHub Issues - [#6173](https://github.com/mruby/mruby/issues/6173) Fails to build with tcc(Tiny C Compiler) - [#6156](https://github.com/mruby/mruby/issues/6156) '/LIBPATH' issue - [#6183](https://github.com/mruby/mruby/issues/6183) ".e".to_f returns NAN - [#6182](https://github.com/mruby/mruby/issues/6182) mrb_read_float() converts "0.3" with a small error compared to strtod() - [#6210](https://github.com/mruby/mruby/issues/6210) Unary minus seems broken - [#6255](https://github.com/mruby/mruby/issues/6255) Wrong number of characters in broken UTF-8 string - [#4038](https://github.com/mruby/mruby/issues/4038) Heap buffer overflow in OP_ENTER - [#6262](https://github.com/mruby/mruby/issues/6262) Unable to define == for objects when using Array#delete - [#6267](https://github.com/mruby/mruby/issues/6267) When MRB_UTF8_STRING is enabled, giving byte characters for String#index and String#split gives wrong results - [#6277](https://github.com/mruby/mruby/issues/6277) MSVC: can't use malloc() in string.c with WIN32_LEAN_AND_MEAN - [#6240](https://github.com/mruby/mruby/issues/6240) Differentiate between lib and lib64 in the build settings. - [#6304](https://github.com/mruby/mruby/issues/6304) Calling method_missing with only Kwargs passes arguments incorrectly - [#6317](https://github.com/mruby/mruby/issues/6317) mrb_gc_register() may cause GC and collect the object being protected - [#6307](https://github.com/mruby/mruby/issues/6307) Planned change patch for mrb_vm_exec() - [#6298](https://github.com/mruby/mruby/issues/6298) foo :bar {} is legal in mruby? - [#6326](https://github.com/mruby/mruby/issues/6326) Detect “Use-after-free†with address sanitizer - [#5358](https://github.com/mruby/mruby/issues/5358) static warning from getpwnam - [#6339](https://github.com/mruby/mruby/issues/6339) mrb_ary_delete() may refer to an invalid address (use-after-free) - [#6346](https://github.com/mruby/mruby/issues/6346) Block kwargs are passed as last positional arg when using yield - [#6365](https://github.com/mruby/mruby/issues/6365) powl() not available when compiling for Dreamcast - [#6369](https://github.com/mruby/mruby/issues/6369) 100x Performance Regression from 3.1 - [#6270](https://github.com/mruby/mruby/issues/6270) NODE_ZSUPER from deeply nested blocks will result in a truncated digits for block index in OP_ARGARY - [#6297](https://github.com/mruby/mruby/issues/6297) Assigning to a block variable changes the actual block (thus affecting block_given? and yield) - [#6389](https://github.com/mruby/mruby/issues/6389) instance_exec named block args don't work properly - [#6388](https://github.com/mruby/mruby/issues/6388) Recent commit broke my windows build - [#6411](https://github.com/mruby/mruby/issues/6411) Wrong function unwinding when using return in a block - [#6439](https://github.com/mruby/mruby/issues/6439) OP_JMPUW does not call the ensure block when it jumps to the beginning of the begin block - [#6441](https://github.com/mruby/mruby/issues/6441) break inside while loop will execute the ensure block outside of the while loop - [#6453](https://github.com/mruby/mruby/issues/6453) Bigint: incorrect behavior of ^ operator - [#6452](https://github.com/mruby/mruby/issues/6452) Bigint: weird mod behavior - [#6451](https://github.com/mruby/mruby/issues/6451) Bigint: incorrect division behavior - [#6456](https://github.com/mruby/mruby/issues/6456) bigint: bug with division of a small number by a bigint - [#6466](https://github.com/mruby/mruby/issues/6466) mruby-bin-mruby using Kernel#p and Kernel#print in bintest fails test - [#6467](https://github.com/mruby/mruby/issues/6467) Heap-Use-After-Free due to Recursive group_by Calls - [#6471](https://github.com/mruby/mruby/issues/6471) Discrepancy in codegen for binary operations between master branch and 3.3.0 - [#6477](https://github.com/mruby/mruby/issues/6477) heap-buffer-overflow in mrb_vm_exec - [#6485](https://github.com/mruby/mruby/issues/6485) Hash#rehash does not check if the hash is frozen - [#6483](https://github.com/mruby/mruby/issues/6483) Hash#default_proc= accepts arbitrary objects that are not callable - [#6491](https://github.com/mruby/mruby/issues/6491) Destroy existing string literals when composing string literals # Merged Pull Requests (User Visible Ones) - [#6171](https://github.com/mruby/mruby/pull/6171) Add Enumerable::Lazy#grep_v to mruby-enum-lazy - [#6174](https://github.com/mruby/mruby/pull/6174) Fix MRUBY_PACKAGE_DIR in mruby-config.bat - [#6175](https://github.com/mruby/mruby/pull/6175) Corrected strange conditional in mrb_vm_run() - [#6176](https://github.com/mruby/mruby/pull/6176) Stop assuming alias proc in CI_PROC_SET() - [#6177](https://github.com/mruby/mruby/pull/6177) gha: add macOS 14 to the build - [#6184](https://github.com/mruby/mruby/pull/6184) Remove the L_STOP label - [#6185](https://github.com/mruby/mruby/pull/6185) Added mrb_callinfo::u.keep_context for clarity - [#6186](https://github.com/mruby/mruby/pull/6186) Omit NULL check of e->cxt in OP_RETURN_BLK - [#6191](https://github.com/mruby/mruby/pull/6191) Speed up symbol equality comparison - [#6192](https://github.com/mruby/mruby/pull/6192) Fix `OP_STOP` with exception - [#6193](https://github.com/mruby/mruby/pull/6193) Fix wrong assertion in `OP_SENDB`. - [#6194](https://github.com/mruby/mruby/pull/6194) Simplify the calculation of the number of closures in `MRB_TT_FIBER` - [#6197](https://github.com/mruby/mruby/pull/6197) Fix int_xor to call flo_xor. - [#6201](https://github.com/mruby/mruby/pull/6201) tasks/doc.rake: standardize the `rake doc` error messages - [#6202](https://github.com/mruby/mruby/pull/6202) Remove the `.yardoc` folder with `rake doc:clean:api` - [#6204](https://github.com/mruby/mruby/pull/6204) Clean up the `.editorconfig` file - [#6209](https://github.com/mruby/mruby/pull/6209) Minor `.gitignore` clean up - [#6211](https://github.com/mruby/mruby/pull/6211) Minor `.gitignore` clean up; order entries - [#6216](https://github.com/mruby/mruby/pull/6216) Shared empty `iv_tbl` of module - [#6217](https://github.com/mruby/mruby/pull/6217) Strict declaration for `mrb_istruct_size()` - [#6219](https://github.com/mruby/mruby/pull/6219) Avoid assigning a fixed value in the loop - [#6220](https://github.com/mruby/mruby/pull/6220) Reorganize `mrb_cache_entry` and `mrb_method_t` types - [#6221](https://github.com/mruby/mruby/pull/6221) Arranging `each_backtrace()` - [#6222](https://github.com/mruby/mruby/pull/6222) Need to synchronize `dbg->regs` after VM call in `mrdb` - [#6224](https://github.com/mruby/mruby/pull/6224) `mrb_env_unshare()` to break the link to fiber - [#6225](https://github.com/mruby/mruby/pull/6225) Revert "Adjust environment when `mrb_exec_irep` happened." - [#6227](https://github.com/mruby/mruby/pull/6227) fix: `Array#shuffle(!)` result distribution - [#6228](https://github.com/mruby/mruby/pull/6228) Revert "`env` referred from top-level callinfo should not be unshared; fix #4019" - [#6230](https://github.com/mruby/mruby/pull/6230) Fix status of fiber after switched by exception raised - [#6231](https://github.com/mruby/mruby/pull/6231) Add a way to let other gems handle closing of fds in mruby-io - [#6232](https://github.com/mruby/mruby/pull/6232) Fold the code for freeing `env` - [#6233](https://github.com/mruby/mruby/pull/6233) Free stack memory at end of fiber - [#6235](https://github.com/mruby/mruby/pull/6235) fix `Array#delete` always firing the block when deleting `nil` - [#6236](https://github.com/mruby/mruby/pull/6236) unify the code for filter methods (and speed up `#reject!`) - [#6237](https://github.com/mruby/mruby/pull/6237) Stricter env objects to attach to ci - [#6238](https://github.com/mruby/mruby/pull/6238) Minimize zero initialization of the stack - [#6243](https://github.com/mruby/mruby/pull/6243) Fixed base64 decoding in `mruby-pack` - [#6244](https://github.com/mruby/mruby/pull/6244) Revise scope of role of `mrb_vm_run()` - [#6246](https://github.com/mruby/mruby/pull/6246) Fix typo in `test/t/hash.rb` - [#6249](https://github.com/mruby/mruby/pull/6249) Fix spelling in `src/vm.c` - [#6250](https://github.com/mruby/mruby/pull/6250) Fix spelling - [#6251](https://github.com/mruby/mruby/pull/6251) Clean up root move `CODEOWNERS` to `.github` directory - [#6253](https://github.com/mruby/mruby/pull/6253) Allow recycling fibers by GC if not referenced directly - [#6256](https://github.com/mruby/mruby/pull/6256) Update documentation for `mrb_top_run()` - [#6257](https://github.com/mruby/mruby/pull/6257) fix some mrbconf.md typos - [#6260](https://github.com/mruby/mruby/pull/6260) Remove `exc_caught` from `mrb_vm_exec()` - [#6261](https://github.com/mruby/mruby/pull/6261) fix: `to_a` integer ranges with `begin > end` failing - [#6263](https://github.com/mruby/mruby/pull/6263) fix: `Array#delete` mistakenly calling block even if not passed - [#6264](https://github.com/mruby/mruby/pull/6264) Must pass keyword arguments for `Kernel#to_enum` - [#6265](https://github.com/mruby/mruby/pull/6265) Fixes `Dir.children` and `Dir.each_child` - [#6266](https://github.com/mruby/mruby/pull/6266) Passes the nonexistent key as a block argument in `Array#delete` - [#6273](https://github.com/mruby/mruby/pull/6273) Improvements to `mrb_protect_atexit()` - [#6275](https://github.com/mruby/mruby/pull/6275) Fixed `Binding#eval` that failed to assign to the same variable - [#6276](https://github.com/mruby/mruby/pull/6276) Always run `atexit` on the top-level call frame - [#6279](https://github.com/mruby/mruby/pull/6279) Include headers for malloc() explicitly; fix #6277 - [#6280](https://github.com/mruby/mruby/pull/6280) Remove `MRB_ENV_CLOSED` flag - [#6281](https://github.com/mruby/mruby/pull/6281) Fixes local variables in `mruby-binding`. - [#6283](https://github.com/mruby/mruby/pull/6283) Simplify `uvenv()` - [#6288](https://github.com/mruby/mruby/pull/6288) Detach `env` of ci explicitly on atexit - [#6289](https://github.com/mruby/mruby/pull/6289) Simplify `OP_RETURN_BLK` and `OP_BREAK` - [#6290](https://github.com/mruby/mruby/pull/6290) Allow to change the output directory name of the `libmruby` file - [#6293](https://github.com/mruby/mruby/pull/6293) Changed the instruction table in `opcode.md` - [#6294](https://github.com/mruby/mruby/pull/6294) Optimise `mrb_iv_get` - [#6302](https://github.com/mruby/mruby/pull/6302) Minor cleanup in mrb_str_init - [#6303](https://github.com/mruby/mruby/pull/6303) mrb_str_aset_m() should return replace instead of str - [#6305](https://github.com/mruby/mruby/pull/6305) Protect keyword arguments in `prepare_missing()` - [#6308](https://github.com/mruby/mruby/pull/6308) Assume that `MRB_CATCH()` has `mrb->exc` set - [#6310](https://github.com/mruby/mruby/pull/6310) Doubling the call stack when extending it - [#6311](https://github.com/mruby/mruby/pull/6311) Added fast-path for positional arguments less than 15 in `OP_SEND` - [#6312](https://github.com/mruby/mruby/pull/6312) Omit error checking at `OP_RETURN`, `OP_RETURN_BLK` and `OP_BREAK` - [#6313](https://github.com/mruby/mruby/pull/6313) Fix wrong column number in opcode.md - [#6314](https://github.com/mruby/mruby/pull/6314) Optimize even?/odd? for big integers - [#6318](https://github.com/mruby/mruby/pull/6318) Shrink variables in `mrb_vm_exec()` - [#6320](https://github.com/mruby/mruby/pull/6320) Shrinking the code in `OP_BREAK` and `OP_RETURN_BLK` - [#6321](https://github.com/mruby/mruby/pull/6321) Avoid warnings in `lib/**/*.rb` - [#6322](https://github.com/mruby/mruby/pull/6322) Fix mrb_ro_data_p on Intel Mac - [#6324](https://github.com/mruby/mruby/pull/6324) Remove `localjump_error()` - [#6327](https://github.com/mruby/mruby/pull/6327) fix ncurses linking issues - [#6328](https://github.com/mruby/mruby/pull/6328) Fix use-after-free in `obj_free()` for env objects - [#6329](https://github.com/mruby/mruby/pull/6329) Fix use-after-free in `mrb_obj_alloc()` - [#6330](https://github.com/mruby/mruby/pull/6330) Add a precondition to call `mrb_env_unshare()`. - [#6331](https://github.com/mruby/mruby/pull/6331) Restore the GC arena with tests - [#6332](https://github.com/mruby/mruby/pull/6332) Must not depend on the “host†build to generate `mruby-compiler/core/y.tab.c` - [#6333](https://github.com/mruby/mruby/pull/6333) Reduce the number of branch instructions in the `heap_p()` - [#6335](https://github.com/mruby/mruby/pull/6335) Add `return_ci` in `CHECKPOINT_MAIN()` of `OP_RETURN` - [#6338](https://github.com/mruby/mruby/pull/6338) Need to place static proc objects into 8-byte alignments - [#6340](https://github.com/mruby/mruby/pull/6340) Fix use-after-free for `Array#<=>` - [#6341](https://github.com/mruby/mruby/pull/6341) Need to restore the GC arena after some function calls - [#6344](https://github.com/mruby/mruby/pull/6344) prefer using `mrb_yield` to call block arguments - [#6347](https://github.com/mruby/mruby/pull/6347) codegen.c,parse.y: remove flattening of `yield` arguments; fix #6346 - [#6348](https://github.com/mruby/mruby/pull/6348) Cancel the warning disablement - [#6349](https://github.com/mruby/mruby/pull/6349) Perform GC before deleting directories - [#6350](https://github.com/mruby/mruby/pull/6350) Fixed character encoding conversion function mismatch - [#6351](https://github.com/mruby/mruby/pull/6351) Remove unnecessary `mrb_gc_arena_restore()` - [#6353](https://github.com/mruby/mruby/pull/6353) Fix use-after-free in `mrb_ary_delete()` - [#6356](https://github.com/mruby/mruby/pull/6356) Making splat argument objects invisible from Ruby side - [#6373](https://github.com/mruby/mruby/pull/6373) Add build config for Milk-V Duo (RISC-V Linux) board - [#6382](https://github.com/mruby/mruby/pull/6382) Make array objects invisible in `mrb_gc_register()` - [#6385](https://github.com/mruby/mruby/pull/6385) Small improvements for `mrb_gc_register()` - [#6386](https://github.com/mruby/mruby/pull/6386) Avoid calling `mrb_gv_set()` from `mrb_gc_unregister()` - [#6387](https://github.com/mruby/mruby/pull/6387) Small improvements for `mrb_gc_unregister()` - [#6390](https://github.com/mruby/mruby/pull/6390) Fix use-after-free by `mrb_gc_unregistor()` - [#6391](https://github.com/mruby/mruby/pull/6391) Fixed argument forwarding in `instance_exec` - [#6392](https://github.com/mruby/mruby/pull/6392) Fix argument forwarding in `mrb_exec_irep()` - [#6393](https://github.com/mruby/mruby/pull/6393) Follow-up to #6391 - [#6395](https://github.com/mruby/mruby/pull/6395) Storing method-id inside Symbol#to_proc - [#6396](https://github.com/mruby/mruby/pull/6396) Milk-V Build Config: update GPIO gem URL - [#6397](https://github.com/mruby/mruby/pull/6397) boxing_no.h: rename BOXNIX_SET_VALUE -> BOXNO_SET_VALUE - [#6399](https://github.com/mruby/mruby/pull/6399) Add macOS 15 to the build - [#6405](https://github.com/mruby/mruby/pull/6405) `io_read`: use `%i` instead of `%d` in call to `mrb_raisef` - [#6407](https://github.com/mruby/mruby/pull/6407) Allow to exclude specific files in `rake install` - [#6408](https://github.com/mruby/mruby/pull/6408) Improve compliance with C++ standards - [#6410](https://github.com/mruby/mruby/pull/6410) Put `#include ` in `parse.y` - [#6412](https://github.com/mruby/mruby/pull/6412) Distinguish the call frame of the generator with `OP_RETURN_BLK` - [#6413](https://github.com/mruby/mruby/pull/6413) Add links to documentation in `README.md` - [#6415](https://github.com/mruby/mruby/pull/6415) Fix numbered parameters when used as a singleton - [#6416](https://github.com/mruby/mruby/pull/6416) Optimize the "new" method's iseq - [#6419](https://github.com/mruby/mruby/pull/6419) Follow Ruby's behavior for numbered parameters in -> {} - [#6420](https://github.com/mruby/mruby/pull/6420) Update `labeler.yml`: add label for the `tools` directory - [#6422](https://github.com/mruby/mruby/pull/6422) Add annotations for function names defined in the preprocessor - [#6423](https://github.com/mruby/mruby/pull/6423) Adding a serialized test task - [#6427](https://github.com/mruby/mruby/pull/6427) Need to update `ci` variable after re-entry to VM - [#6428](https://github.com/mruby/mruby/pull/6428) Change the limits of OP_ADDI and OP_SUBI from 0-127 to 0-255. - [#6429](https://github.com/mruby/mruby/pull/6429) Fix numbered parameters when used as hash keys - [#6432](https://github.com/mruby/mruby/pull/6432) Moving code in macro arguments out of macros - [#6434](https://github.com/mruby/mruby/pull/6434) Added document "Layout of the mruby filesystem" - [#6436](https://github.com/mruby/mruby/pull/6436) Make `rake doc:update-index` prettier friendly - [#6437](https://github.com/mruby/mruby/pull/6437) Add more details to the pre-commit config - [#6438](https://github.com/mruby/mruby/pull/6438) Remove unused `MRuby::Build#list_install_excludes` method - [#6440](https://github.com/mruby/mruby/pull/6440) Fix `redo` keyword - [#6442](https://github.com/mruby/mruby/pull/6442) Fixed wrong range condition in `OP_JMPUW` - [#6443](https://github.com/mruby/mruby/pull/6443) Fix NODE_NEGATE for bigints - [#6444](https://github.com/mruby/mruby/pull/6444) Add test cases for bigints - [#6446](https://github.com/mruby/mruby/pull/6446) Omit the `_WIN64` definition check - [#6447](https://github.com/mruby/mruby/pull/6447) Fixed `File.expand_path` - [#6448](https://github.com/mruby/mruby/pull/6448) Suppress presym in `mruby/ext/io.h` file - [#6449](https://github.com/mruby/mruby/pull/6449) Using presym in the `mruby-io/src/file_test.c` file - [#6450](https://github.com/mruby/mruby/pull/6450) Change `MRB_WITH_IO_PREAD_PWRITE` configuration name - [#6454](https://github.com/mruby/mruby/pull/6454) mruby-bigint: handle rhs bigint in int_mod and int_divmod functions - [#6455](https://github.com/mruby/mruby/pull/6455) mruby-bigint: fix rounding behavior in mpz_mdiv and mpz_mdivmod functions - [#6457](https://github.com/mruby/mruby/pull/6457) bigint: fix bug with division of a small number by a bigint - [#6459](https://github.com/mruby/mruby/pull/6459) `FileTest` is a module - [#6461](https://github.com/mruby/mruby/pull/6461) To create a release package file in draft - [#6462](https://github.com/mruby/mruby/pull/6462) Properly cast the return value of `memchr()` - [#6463](https://github.com/mruby/mruby/pull/6463) Add `level` argument to `File.dirname` - [#6465](https://github.com/mruby/mruby/pull/6465) Add initial PlayStation portable crossbuild support - [#6468](https://github.com/mruby/mruby/pull/6468) Fixed missing changes to `IB_FIND_BY_KEY()` parameter names - [#6469](https://github.com/mruby/mruby/pull/6469) Add more `const` qualifier for `RProc` - [#6472](https://github.com/mruby/mruby/pull/6472) Moved tests for `Integer#quo` - [#6473](https://github.com/mruby/mruby/pull/6473) Hide `mpz_and()` symbol - [#6473](https://github.com/mruby/mruby/pull/6473) Hide mpz_and() symbol - [#6474](https://github.com/mruby/mruby/pull/6474) Avoid array object creation with “unknown keyword†error - [#6475](https://github.com/mruby/mruby/pull/6475) Don't include deleted mruby-print - [#6478](https://github.com/mruby/mruby/pull/6478) Fixed buffer overrun in function `chars2bytes()` - [#6479](https://github.com/mruby/mruby/pull/6479) Reimplementation of `File.expand_path` method - [#6482](https://github.com/mruby/mruby/pull/6482) Add `File.absolute_path?` method - [#6487](https://github.com/mruby/mruby/pull/6487) Add Emscripten toolchain & build_config nghttp2-1.68.0/third-party/mruby/PaxHeaders/TODO.md0000644000000000000000000000013115077107276016761 xustar0029 mtime=1761382078.09542068 30 atime=1761382080.118411425 30 ctime=1761382108.536302011 nghttp2-1.68.0/third-party/mruby/TODO.md0000644000175100017510000000051515077107276017353 0ustar00runnerrunner# Things to Do in the future # After mruby 3.1 - parser and code generator independent from `mrb_state` (picoruby?) - variable sized AST node - iv/hash entry cache - more peephole optimization (if possible) # Things to do (Things we need to consider) - `begin ... end while cond` to behave as CRuby - special variables ($1,$2..) nghttp2-1.68.0/third-party/mruby/PaxHeaders/build_config0000644000000000000000000000013215077107334020235 xustar0030 mtime=1761382108.437302298 30 atime=1761382109.795298372 30 ctime=1761382108.437302298 nghttp2-1.68.0/third-party/mruby/build_config/0000755000175100017510000000000015077107334020702 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/helpers0000644000000000000000000000013215077107334021677 xustar0030 mtime=1761382108.439302292 30 atime=1761382109.795298372 30 ctime=1761382108.439302292 nghttp2-1.68.0/third-party/mruby/build_config/helpers/0000755000175100017510000000000015077107334022344 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/build_config/helpers/PaxHeaders/wine_runner.rb0000644000000000000000000000013215077107276024641 xustar0030 mtime=1761382078.097420671 30 atime=1761382080.119411421 30 ctime=1761382108.439302292 nghttp2-1.68.0/third-party/mruby/build_config/helpers/wine_runner.rb0000755000175100017510000000311215077107276025231 0ustar00runnerrunner#!/usr/bin/env ruby # Wrapper for running tests for cross-compiled Windows builds in Wine. require 'open3' DOSROOT = 'z:' # Rewrite test output to replace DOS-isms with Unix-isms. def clean(output, stderr = false) ends_with_newline = !!(output =~ /\n$/) executable = ARGV[0].gsub(/\.exe\z/i, '') # Fix line-ends output = output.gsub(/\r\n/, "\n") # Strip out Wine messages results = output.split(/\n/).map do |line| # Fix file paths if line =~ /#{DOSROOT}\\/i line.gsub!(/#{DOSROOT}([^:]*)/i) { |path| path.gsub!(/^#{DOSROOT}/i, '') path.gsub!(%r{\\}, '/') path } end # strip '.exe' off the end of the executable's name if needed line.gsub!(/(#{Regexp.escape executable})\.exe/i, '\1') line end result_text = results.join("\n") result_text += "\n" if ends_with_newline result_text end def main if ARGV.empty? || ARGV[0] =~ /^- (-?) (\?|help|h) $/x puts "#{$0} " exit 0 end # For simplicity, just read all of stdin into memory and pass that # as an argument when invoking wine. (Skipped if STDIN was not # redirected.) if !STDIN.tty? input = STDIN.read else input = "" end # Disable all Wine messages so they don't interfere with the output ENV['WINEDEBUG'] = 'err-all,warn-all,fixme-all,trace-all' # Run the program in wine and capture the output output, errormsg, status = Open3.capture3('wine', *ARGV, :stdin_data => input) # Clean and print the results. STDOUT.write clean(output) STDERR.write clean(errormsg) exit(status.exitstatus) end main() nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/chipKITMax32.rb0000644000000000000000000000013215077107276022752 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.118411425 30 ctime=1761382108.396302416 nghttp2-1.68.0/third-party/mruby/build_config/chipKITMax32.rb0000644000175100017510000000435215077107276023346 0ustar00runnerrunner# Cross Compiling configuration for Digilent chipKIT Max32 # https://www.digilentinc.com/Products/Detail.cfm?Prod=CHIPKIT-MAX32 # # Requires MPIDE (https://github.com/chipKIT32/chipKIT32-MAX) # # This configuration is based on @kyab's version # http://d.hatena.ne.jp/kyab/20130201 MRuby::CrossBuild.new("chipKITMax32") do |conf| toolchain :gcc # Mac OS X # MPIDE_PATH = '/Applications/Mpide.app/Contents/Resources/Java' # GNU Linux MPIDE_PATH = '/opt/mpide-0023-linux-20120903' PIC32_PATH = "#{MPIDE_PATH}/hardware/pic32" conf.cc do |cc| cc.command = "#{PIC32_PATH}/compiler/pic32-tools/bin/pic32-gcc" cc.include_paths << ["#{PIC32_PATH}/cores/pic32", "#{PIC32_PATH}/variants/Max32", "#{PIC32_PATH}/libraries"] cc.flags = %w(-O2 -mno-smart-io -w -ffunction-sections -fdata-sections -g -mdebugger -Wcast-align -fno-short-double -mprocessor=32MX795F512L -DF_CPU=80000000L -DARDUINO=23 -D_BOARD_MEGA_ -DMPIDEVER=0x01000202 -DMPIDE=23) cc.compile_options = %Q[%{flags} -o "%{outfile}" -c "%{infile}"] #configuration for low memory environment cc.defines << %w(MRB_HEAP_PAGE_SIZE=64) cc.defines << %w(KHASH_DEFAULT_SIZE=8) cc.defines << %w(MRB_GC_STRESS) #cc.defines << %w(MRB_NO_STDIO) #if you don't need stdio. #cc.defines << %w(POOL_PAGE_SIZE=1000) #effective only for use with mruby-eval end conf.cxx do |cxx| cxx.command = conf.cc.command.dup cxx.include_paths = conf.cc.include_paths.dup cxx.flags = conf.cc.flags.dup cxx.defines = conf.cc.defines.dup cxx.compile_options = conf.cc.compile_options.dup end conf.archiver do |archiver| archiver.command = "#{PIC32_PATH}/compiler/pic32-tools/bin/pic32-ar" archiver.archive_options = 'rcs "%{outfile}" %{objs}' end #no executables conf.bins = [] #do not build test executable conf.build_mrbtest_lib_only #disable C++ exception conf.disable_cxx_exception #gems from core conf.gem :core => "mruby-math" conf.gem :core => "mruby-enum-ext" #light-weight regular expression conf.gem :github => "masamitsu-murase/mruby-hs-regexp", :branch => "master" #Arduino API #conf.gem :github =>"kyab/mruby-arduino", :branch => "master" end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/host-f32.rb0000644000000000000000000000013215077107276022211 xustar0030 mtime=1761382078.097420671 30 atime=1761382080.119411421 30 ctime=1761382108.392302428 nghttp2-1.68.0/third-party/mruby/build_config/host-f32.rb0000644000175100017510000000043615077107276022604 0ustar00runnerrunnerMRuby::Build.new do |conf| # load specific toolchain settings toolchain :gcc # include the GEM box conf.gembox 'full-core' conf.cc.defines << 'MRB_USE_FLOAT32' # Turn on `enable_debug` for better debugging conf.enable_debug conf.enable_test conf.enable_bintest end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/cross-mingw-winetest.rb0000644000000000000000000000013215077107276024754 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.119411421 30 ctime=1761382108.391302431 nghttp2-1.68.0/third-party/mruby/build_config/cross-mingw-winetest.rb0000644000175100017510000000554115077107276025351 0ustar00runnerrunner# Cross-compile using MinGW and test using Wine. # # Steps: # # 1. Install MinGW; 64-bit target seems to work best. # # 2. Install Wine. # # 3. Run command: # # wine cmd /c echo "Hello world"' # # This will confirm that Wine works and will trigger standard # Wine setup, which is slow. # # 4. Confirm that drive 'z:' is mapped to your root filesystem. # (This is supposed to be a default but it helps to # double-check.) To confirm, run: # # wine cmd /c dir 'z:\\' # # This should give you a DOS-style equivalent of 'ls /'. If not, # you'll need to fix that with winecfg or by adding a symlink to # '~/.wine/dosdevices'. # # 5. You will likely need to tweak the settings below to work with # your configuration unless it is exactly like one of the platforms # I've tested on (Ubuntu 20.04 or macOS using brew.) # # 6. Run the build command: # # MRUBY_CONFIG=build_config/cross-mingw-winetest.rb rake test # # If all goes well, you should now have Windows executables and a # set of passing tests. # # # Caveats: # # 1. This works by using a helper script that rewrites test output # to make it look *nix-like and then handing it back to the test # cases. Some of the existing tests were (slightly) modified to # make this easier but only for the 'full-core' gembox. Other # gems' bintests may or may not work with the helper script and # may or may not be fixable by extending the script. # # 2. MinGW and Wine are both complex and not very consistent so you # will likely need to do some fiddling to get things to work. # # 3. This script assumes you are running it on a *nix-style OS. # # 4. I recommend building 64-bit targets only. Building a 32-bit # Windows binary with i686-w64-mingw32 seems to work (at least, # it did for me) but the resulting executable failed a number of # unit tests due to small errors in some floating-point # operations. It's unclear if this indicates more serious problems. # MRuby::CrossBuild.new("cross-mingw-winetest") do |conf| conf.toolchain :gcc conf.host_target = "x86_64-w64-mingw32" # Ubuntu 20 conf.cc.command = "#{conf.host_target}-gcc-posix" # macOS+Wine from brew #conf.cc.command = "#{conf.host_target}-gcc" conf.linker.command = conf.cc.command conf.archiver.command = "#{conf.host_target}-gcc-ar" conf.exts.executable = ".exe" # By default, we compile as static as possible to remove runtime # MinGW dependencies; they are probably fixable but it gets # complicated. conf.cc.flags = ['-static'] conf.linker.flags += ['-static'] conf.test_runner do |t| thisdir = File.absolute_path( File.dirname(__FILE__) ) t.command = File.join(thisdir, * %w{ helpers wine_runner.rb}) end conf.gembox "full-core" conf.enable_bintest conf.enable_test end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/cross-32bit.rb0000644000000000000000000000013215077107276022716 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.119411421 30 ctime=1761382108.435302303 nghttp2-1.68.0/third-party/mruby/build_config/cross-32bit.rb0000644000175100017510000000052515077107276023310 0ustar00runnerrunner# Define cross build settings MRuby::CrossBuild.new('cross-32bit') do |conf| conf.toolchain :gcc conf.cc.flags << "-m32" conf.linker.flags << "-m32" # conf.build_mrbtest_lib_only conf.gem :core => "mruby-bin-mruby" conf.gem "#{MRUBY_ROOT}/examples/mrbgems/c_and_ruby_extension_example" conf.test_runner.command = 'env' end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/host-gprof.rb0000644000000000000000000000013115077107276022733 xustar0030 mtime=1761382078.097420671 30 atime=1761382080.119411421 29 ctime=1761382108.42630233 nghttp2-1.68.0/third-party/mruby/build_config/host-gprof.rb0000644000175100017510000000042715077107276023327 0ustar00runnerrunnerMRuby::Build.new do |conf| # load specific toolchain settings toolchain :gcc # include the GEM box conf.gembox 'full-core' conf.cc.flags << '-pg' conf.linker.flags << '-pg' # Turn on `enable_debug` for better debugging conf.enable_debug conf.enable_test end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/host-cxx.rb0000644000000000000000000000013115077107276022420 xustar0030 mtime=1761382078.097420671 30 atime=1761382080.119411421 29 ctime=1761382108.40530239 nghttp2-1.68.0/third-party/mruby/build_config/host-cxx.rb0000644000175100017510000000034715077107276023015 0ustar00runnerrunnerMRuby::Build.new do |conf| conf.toolchain # include the default GEMs conf.gembox 'default' # C compiler settings conf.cc.defines = %w(MRB_USE_DEBUG_HOOK) conf.enable_debug conf.enable_cxx_abi conf.enable_test end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/host-debug.rb0000644000000000000000000000013215077107276022705 xustar0030 mtime=1761382078.097420671 30 atime=1761382080.119411421 30 ctime=1761382108.403302396 nghttp2-1.68.0/third-party/mruby/build_config/host-debug.rb0000644000175100017510000000063615077107276023302 0ustar00runnerrunnerMRuby::Build.new('host') do |conf| # load specific toolchain settings conf.toolchain conf.enable_debug # include the default GEMs conf.gembox 'full-core' # C compiler settings conf.cc.defines = %w(MRB_USE_DEBUG_HOOK MRB_NO_BOXING) # Generate mruby debugger command (require mruby-eval) conf.gem :core => "mruby-bin-debugger" # test conf.enable_test # bintest conf.enable_bintest end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/host-shared.rb0000644000000000000000000000013215077107276023065 xustar0030 mtime=1761382078.097420671 30 atime=1761382080.119411421 30 ctime=1761382108.402302399 nghttp2-1.68.0/third-party/mruby/build_config/host-shared.rb0000644000175100017510000000141215077107276023453 0ustar00runnerrunner# NOTE: Currently, this configuration file does not support VisualC++! # Your help is needed! MRuby::Build.new do |conf| # load specific toolchain settings conf.toolchain # include the GEM box conf.gembox 'default' # C compiler settings conf.compilers.each do |cc| cc.flags << '-fPIC' end conf.archiver do |archiver| archiver.command = cc.command archiver.archive_options = '-shared -o %{outfile} %{objs}' end # file extensions conf.exts do |exts| exts.library = '.so' end # file separator # conf.file_separator = '/' # enable this if better compatibility with C++ is desired #conf.enable_cxx_exception # Turn on `enable_debug` for better debugging conf.enable_debug conf.enable_bintest conf.enable_test end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/RX630.rb0000644000000000000000000000013215077107276021426 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.118411425 30 ctime=1761382108.429302321 nghttp2-1.68.0/third-party/mruby/build_config/RX630.rb0000644000175100017510000000332115077107276022015 0ustar00runnerrunner# Cross Compiling configuration for RX630 # http://gadget.renesas.com/ # # Requires gnurx_v14.03 MRuby::CrossBuild.new("RX630") do |conf| toolchain :gcc # Linux BIN_PATH = "/usr/share/gnurx_v14.03_elf-1/bin" conf.cc do |cc| cc.command = "#{BIN_PATH}/rx-elf-gcc" cc.flags = "-Wall -g -O2 -flto -mcpu=rx600 -m64bit-doubles" cc.compile_options = %Q[%{flags} -o "%{outfile}" -c "%{infile}"] #configuration for low memory environment cc.defines << %w(MRB_USE_FLOAT32) cc.defines << %w(MRB_HEAP_PAGE_SIZE=64) cc.defines << %w(KHASH_DEFAULT_SIZE=8) cc.defines << %w(MRB_GC_STRESS) cc.defines << %w(MRB_NO_STDIO) #if you don't need stdio. #cc.defines << %w(POOL_PAGE_SIZE=1000) #effective only for use with mruby-eval end conf.cxx do |cxx| cxx.command = conf.cc.command.dup cxx.include_paths = conf.cc.include_paths.dup cxx.flags = conf.cc.flags.dup cxx.defines = conf.cc.defines.dup cxx.compile_options = conf.cc.compile_options.dup end conf.linker do |linker| linker.command="#{BIN_PATH}/rx-elf-ld" end conf.archiver do |archiver| archiver.command = "#{BIN_PATH}/rx-elf-ar" archiver.archive_options = 'rcs "%{outfile}" %{objs}' end #no executables conf.bins = [] #do not build executable test conf.build_mrbtest_lib_only #disable C++ exception conf.disable_cxx_exception #gems from core conf.gem :core => "mruby-sprintf" conf.gem :core => "mruby-math" conf.gem :core => "mruby-enum-ext" conf.gem :core => "mruby-numeric-ext" #light-weight regular expression #conf.gem :github => "masamitsu-murase/mruby-hs-regexp", :branch => "master" #Arduino API #conf.gem :github =>"kyab/mruby-arduino", :branch => "master" end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/default.rb0000644000000000000000000000013215077107276022270 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.119411421 30 ctime=1761382108.422302341 nghttp2-1.68.0/third-party/mruby/build_config/default.rb0000644000175100017510000000471415077107276022666 0ustar00runnerrunnerMRuby::Build.new do |conf| # load specific toolchain settings conf.toolchain # Use mrbgems # conf.gem 'examples/mrbgems/ruby_extension_example' # conf.gem 'examples/mrbgems/c_extension_example' do |g| # g.cc.flags << '-g' # append cflags in this gem # end # conf.gem 'examples/mrbgems/c_and_ruby_extension_example' # conf.gem :core => 'mruby-eval' # conf.gem :mgem => 'mruby-onig-regexp' # conf.gem :github => 'mattn/mruby-onig-regexp' # conf.gem :git => 'git@github.com:mattn/mruby-onig-regexp.git', :branch => 'master', :options => '-v' # include the GEM box conf.gembox 'default' # C compiler settings # conf.cc do |cc| # cc.command = ENV['CC'] || 'gcc' # cc.flags = [ENV['CFLAGS'] || %w()] # cc.include_paths = ["#{root}/include"] # cc.defines = %w() # cc.option_include_path = %q[-I"%s"] # cc.option_define = '-D%s' # cc.compile_options = %Q[%{flags} -MMD -o "%{outfile}" -c "%{infile}"] # end # mrbc settings # conf.mrbc do |mrbc| # mrbc.compile_options = "-g -B%{funcname} -o-" # The -g option is required for line numbers # end # Linker settings # conf.linker do |linker| # linker.command = ENV['LD'] || 'gcc' # linker.flags = [ENV['LDFLAGS'] || []] # linker.flags_before_libraries = [] # linker.libraries = %w() # linker.flags_after_libraries = [] # linker.library_paths = [] # linker.option_library = '-l%s' # linker.option_library_path = '-L%s' # linker.link_options = %Q[%{flags} -o "%{outfile}" %{objs} %{libs}] # end # Archiver settings # conf.archiver do |archiver| # archiver.command = ENV['AR'] || 'ar' # archiver.archive_options = 'rs "%{outfile}" %{objs}' # end # Parser generator settings # conf.yacc do |yacc| # yacc.command = ENV['YACC'] || 'bison' # yacc.compile_options = %q[-o "%{outfile}" "%{infile}"] # end # gperf settings # conf.gperf do |gperf| # gperf.command = 'gperf' # gperf.compile_options = %q[-L ANSI-C -C -j1 -i 1 -o -t -N mrb_reserved_word -k"1,3,$" "%{infile}" > "%{outfile}"] # end # file extensions # conf.exts do |exts| # exts.object = '.o' # exts.executable = '' # '.exe' if Windows # exts.library = '.a' # end # file separator # conf.file_separator = '/' # change library directory name from the default "lib" if necessary # conf.libdir_name = 'lib64' # Turn on `enable_debug` for better debugging # conf.enable_debug conf.enable_bintest conf.enable_test end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/milkv_duo.rb0000644000000000000000000000013015077107276022633 xustar0030 mtime=1761382078.097420671 30 atime=1761382080.119411421 28 ctime=1761382108.4363023 nghttp2-1.68.0/third-party/mruby/build_config/milkv_duo.rb0000644000175100017510000000755715077107276023243 0ustar00runnerrunner# Cross Compiling configuration for Milk-V Duo. # To build (on Ubuntu 24.04): rake MRUBY_CONFIG=build_config/milkv_duo.rb # # Requires: https://github.com/milkv-duo/duo-sdk # MRuby::CrossBuild.new("milkv_duo") do |conf| # Set this string to match your board: milkv_duo, milkv_duo256m, milkv_duos MILKV_DUO_VARIANT = "milkv_duo256m" # Expect duo-sdk directory is same level as (next to) mruby top-level directory. SDK_BASE = File.expand_path("../../../", File.expand_path(__FILE__)) + "/duo-sdk" TOOLCHAIN_BASE = "#{SDK_BASE}/riscv64-linux-musl-x86_64" SYSROOT = "#{SDK_BASE}/rootfs" toolchain :gcc # C compiler settings conf.cc do |cc| cc.command = "#{TOOLCHAIN_BASE}/bin/riscv64-unknown-linux-musl-gcc" cc.include_paths << "#{TOOLCHAIN_BASE}/lib/gcc/riscv64-unknown-linux/musl/10.2.0/include-fixed" cc.include_paths << "#{TOOLCHAIN_BASE}/lib/gcc/riscv64-unknown-linux/musl/10.2.0/include" cc.include_paths << "#{TOOLCHAIN_BASE}/riscv64-unknown-linux/include" cc.include_paths << "#{TOOLCHAIN_BASE}/include" cc.include_paths << "#{SYSROOT}/usr/include" cc.flags << ["-mcpu=c906fdv", "-march=rv64imafdcv0p7xthead", "-mcmodel=medany", "-mabi=lp64d"] cc.flags << ["-D_LARGEFILE_SOURCE", "-D_LARGEFILE64_SOURCE", "-D_FILE_OFFSET_BITS=64"] cc.flags << ["-Wl,--copy-dt-needed-entries", "-Wl,-lc,-lgcc_s,-lwiringx"] cc.defines << "MILKV_DUO_VARIANT=_#{MILKV_DUO_VARIANT}" end # Linker settings conf.linker do |linker| linker.command = cc.command linker.library_paths << ["#{SYSROOT}/lib", "#{SYSROOT}/usr/lib"] linker.flags = cc.flags end # Archiver settings conf.archiver do |archiver| archiver.command = "#{TOOLCHAIN_BASE}/bin/riscv64-unknown-linux-musl-gcc-ar" end # Do not build executable test conf.build_mrbtest_lib_only # Disable C++ exception conf.disable_cxx_exception # All standard gems. conf.gem 'mrbgems/mruby-array-ext/' conf.gem 'mrbgems/mruby-bigint/' conf.gem 'mrbgems/mruby-bin-config/' conf.gem 'mrbgems/mruby-bin-debugger/' conf.gem 'mrbgems/mruby-bin-mirb/' conf.gem 'mrbgems/mruby-bin-mrbc/' conf.gem 'mrbgems/mruby-bin-mruby/' conf.gem 'mrbgems/mruby-bin-strip/' conf.gem 'mrbgems/mruby-binding/' conf.gem 'mrbgems/mruby-catch/' conf.gem 'mrbgems/mruby-class-ext/' conf.gem 'mrbgems/mruby-cmath/' conf.gem 'mrbgems/mruby-compar-ext/' conf.gem 'mrbgems/mruby-compiler/' conf.gem 'mrbgems/mruby-complex/' conf.gem 'mrbgems/mruby-data/' conf.gem 'mrbgems/mruby-dir/' conf.gem 'mrbgems/mruby-enum-chain/' conf.gem 'mrbgems/mruby-enum-ext/' conf.gem 'mrbgems/mruby-enum-lazy/' conf.gem 'mrbgems/mruby-enumerator/' conf.gem 'mrbgems/mruby-errno/' conf.gem 'mrbgems/mruby-error/' conf.gem 'mrbgems/mruby-eval/' conf.gem 'mrbgems/mruby-exit/' conf.gem 'mrbgems/mruby-fiber/' conf.gem 'mrbgems/mruby-hash-ext/' conf.gem 'mrbgems/mruby-io/' conf.gem 'mrbgems/mruby-kernel-ext/' conf.gem 'mrbgems/mruby-math/' conf.gem 'mrbgems/mruby-metaprog/' conf.gem 'mrbgems/mruby-method/' conf.gem 'mrbgems/mruby-numeric-ext/' conf.gem 'mrbgems/mruby-object-ext/' conf.gem 'mrbgems/mruby-objectspace/' conf.gem 'mrbgems/mruby-os-memsize/' conf.gem 'mrbgems/mruby-pack/' conf.gem 'mrbgems/mruby-proc-binding/' conf.gem 'mrbgems/mruby-proc-ext/' conf.gem 'mrbgems/mruby-random/' conf.gem 'mrbgems/mruby-range-ext/' conf.gem 'mrbgems/mruby-rational/' conf.gem 'mrbgems/mruby-set/' conf.gem 'mrbgems/mruby-sleep/' conf.gem 'mrbgems/mruby-socket/' conf.gem 'mrbgems/mruby-sprintf/' conf.gem 'mrbgems/mruby-string-ext/' conf.gem 'mrbgems/mruby-struct/' conf.gem 'mrbgems/mruby-symbol-ext/' # conf.gem 'mrbgems/mruby-test-inline-struct/' # conf.gem 'mrbgems/mruby-test/' conf.gem 'mrbgems/mruby-time/' conf.gem 'mrbgems/mruby-toplevel-ext/' # GPIO, ADC, PWM, I2C and SPI support for Milk-V Duo # conf.gem :github => 'denko-rb/mruby-milkv-duo' end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/gameboyadvance.rb0000644000000000000000000000013115077107276023610 xustar0030 mtime=1761382078.097420671 30 atime=1761382080.119411421 29 ctime=1761382108.39830241 nghttp2-1.68.0/third-party/mruby/build_config/gameboyadvance.rb0000644000175100017510000000406515077107276024206 0ustar00runnerrunner# Cross Compiling configuration for the Nintendo GameBoyAdvance. # This configuration requires devkitARM # https://devkitpro.org/wiki/Getting_Started/devkitARM # # Tested only on GNU/Linux # MRuby::CrossBuild.new("gameboyadvance") do |conf| toolchain :gcc DEVKITPRO_PATH = "/opt/devkitpro" BIN_PATH = "#{DEVKITPRO_PATH}/devkitARM/bin" # C compiler conf.cc do |cc| cc.command = "#{BIN_PATH}/arm-none-eabi-gcc" cc.flags << ["-mthumb-interwork", "-mthumb", "-O2"] cc.compile_options = %(%{flags} -o "%{outfile}" -c "%{infile}") end # C++ compiler conf.cxx do |cxx| cxx.command = "#{BIN_PATH}/arm-none-eabi-g++" cxx.include_paths = conf.cc.include_paths.dup cxx.flags = conf.cc.flags.dup cxx.flags << %w[-fno-rtti -fno-exceptions] cxx.defines = conf.cc.defines.dup cxx.compile_options = conf.cc.compile_options.dup end # Linker conf.linker do |linker| linker.command = "#{BIN_PATH}/arm-none-eabi-gcc" linker.flags << ["-mthumb-interwork", "-mthumb", "-specs=gba.specs"] end # No executables conf.bins = [] # Do not build executable test conf.build_mrbtest_lib_only # Disable C++ exception conf.disable_cxx_exception # Gems from core # removing mruby-io conf.gem core: "mruby-metaprog" conf.gem core: "mruby-pack" conf.gem core: "mruby-sprintf" conf.gem core: "mruby-math" conf.gem core: "mruby-time" conf.gem core: "mruby-struct" conf.gem core: "mruby-compar-ext" conf.gem core: "mruby-enum-ext" conf.gem core: "mruby-string-ext" conf.gem core: "mruby-numeric-ext" conf.gem core: "mruby-array-ext" conf.gem core: "mruby-hash-ext" conf.gem core: "mruby-range-ext" conf.gem core: "mruby-proc-ext" conf.gem core: "mruby-symbol-ext" conf.gem core: "mruby-random" conf.gem core: "mruby-object-ext" conf.gem core: "mruby-objectspace" conf.gem core: "mruby-fiber" conf.gem core: "mruby-enumerator" conf.gem core: "mruby-enum-lazy" conf.gem core: "mruby-toplevel-ext" conf.gem core: "mruby-kernel-ext" conf.gem core: "mruby-class-ext" conf.gem core: "mruby-compiler" end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/android_arm64_v8a.rb0000644000000000000000000000013215077107276024053 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.118411425 30 ctime=1761382108.434302306 nghttp2-1.68.0/third-party/mruby/build_config/android_arm64_v8a.rb0000644000175100017510000000036015077107276024442 0ustar00runnerrunner# Requires Android NDK r13 or later. MRuby::CrossBuild.new('android-arm64-v8a') do |conf| params = { :arch => 'arm64-v8a', :sdk_version => 33, :toolchain => :clang } toolchain :android, params conf.gembox 'default' end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/host-m32.rb0000644000000000000000000000013215077107276022220 xustar0030 mtime=1761382078.097420671 30 atime=1761382080.119411421 30 ctime=1761382108.400302405 nghttp2-1.68.0/third-party/mruby/build_config/host-m32.rb0000644000175100017510000000045715077107276022616 0ustar00runnerrunnerMRuby::Build.new do |conf| # load specific toolchain settings toolchain :gcc # include the GEM box conf.gembox 'full-core' conf.cc.flags << '-m32' conf.linker.flags << '-m32' # Turn on `enable_debug` for better debugging conf.enable_debug conf.enable_test conf.enable_bintest end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/emscripten-cxx.rb0000644000000000000000000000013215077107276023615 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.119411421 30 ctime=1761382108.432302312 nghttp2-1.68.0/third-party/mruby/build_config/emscripten-cxx.rb0000644000175100017510000000052015077107276024202 0ustar00runnerrunner# Make sure to add these compile options: # build/emscripten-cxx/host-bin/mruby-config --cxxflags # # Make sure to add these link options: # build/emscripten-cxx/host-bin/mruby-config --ldflags --libs MRuby::CrossBuild.new('emscripten-cxx') do |conf| conf.toolchain :emscripten conf.gembox 'default' conf.enable_cxx_abi end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/mrbc.rb0000644000000000000000000000013215077107276021567 xustar0030 mtime=1761382078.097420671 30 atime=1761382080.119411421 30 ctime=1761382108.407302384 nghttp2-1.68.0/third-party/mruby/build_config/mrbc.rb0000644000175100017510000000033715077107276022162 0ustar00runnerrunnerMRuby::Build.new do |conf| if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR'] conf.toolchain :visualcpp else conf.toolchain :gcc end conf.build_mrbc_exec conf.disable_libmruby conf.disable_presym end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/i586-pc-msdosdjgpp.rb0000644000000000000000000000013215077107276024107 xustar0030 mtime=1761382078.097420671 30 atime=1761382080.119411421 30 ctime=1761382108.395302419 nghttp2-1.68.0/third-party/mruby/build_config/i586-pc-msdosdjgpp.rb0000644000175100017510000000532315077107276024502 0ustar00runnerrunner# Cross Compiling configuration for MS-DOS # # Requires DJGPP cross-compiler, see # https://github.com/andrewwutw/build-djgpp/releases MRuby::CrossBuild.new("i586-pc-msdosdjgpp") do |conf| toolchain :gcc # If DJGPP is not in the PATH, set this to the bin directory DJGPP_PATH = nil GCC = 'i586-pc-msdosdjgpp-gcc' GXX = 'i586-pc-msdosdjgpp-g++' AR = 'i586-pc-msdosdjgpp-ar' conf.cc do |cc| cc.command = DJGPP_PATH ? File.join(DJGPP_PATH, GCC) : GCC cc.defines << 'MRB_NO_IO_PREAD_PWRITE' cc.defines << 'MRB_UTF8_STRING' end conf.cxx do |cxx| cxx.command = DJGPP_PATH ? File.join(DJGPP_PATH, GXX) : GXX cxx.defines << 'MRB_NO_IO_PREAD_PWRITE' cxx.defines << 'MRB_UTF8_STRING' end conf.linker do |linker| linker.command = DJGPP_PATH ? File.join(DJGPP_PATH, GXX) : GXX linker.libraries = %w(m) end conf.archiver do |archiver| archiver.command = DJGPP_PATH ? File.join(DJGPP_PATH, AR) : AR end # All provided gems that can be reasonably made to compile: # default.gembox, minus mruby-socket and replacing mruby-cmath with mruby-cmath-alt conf.gembox "stdlib" conf.gembox "stdlib-ext" conf.gem :core => 'mruby-io' # stdlib-io.gembox <- default.gembox # No socket support in DJGPP # conf.gem :core => 'mruby-socket' # stdlib-io.gembox <- default.gembox conf.gem :core => 'mruby-errno' # stdlib-io.gembox <- default.gembox conf.gem :core => 'mruby-dir' # stdlib-io.gembox <- default.gembox conf.gem :core => 'mruby-bigint' # math.gembox <- default.gembox conf.gem :core => 'mruby-complex' # math.gembox <- default.gembox conf.gem :core => 'mruby-math' # math.gembox <- default.gembox conf.gem :core => 'mruby-rational' # math.gembox <- default.gembox # Alternative implementation of cmath, not requiring # conf.gem :github => 'chasonr/mruby-cmath-alt' # math.gembox <- default.gembox conf.gembox "metaprog" conf.gem :core => 'mruby-bin-mrbc' # default.gembox conf.gem :core => 'mruby-bin-debugger' # default.gembox conf.gem :core => 'mruby-bin-mirb' # default.gembox conf.gem :core => 'mruby-bin-mruby' # default.gembox conf.gem :core => 'mruby-bin-strip' # default.gembox conf.gem :core => 'mruby-bin-config' # default.gembox # Other compilable gems conf.gem :core => 'mruby-binding' conf.gem :core => 'mruby-catch' conf.gem :core => 'mruby-enum-chain' conf.gem :core => 'mruby-error' conf.gem :core => 'mruby-exit' conf.gem :core => 'mruby-os-memsize' conf.gem :core => 'mruby-proc-binding' conf.gem :core => 'mruby-sleep' # For Onigmo regular expression support conf.gem :github => 'mattn/mruby-onig-regexp' end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/IntelEdison.rb0000644000000000000000000000013215077107276023061 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.118411425 30 ctime=1761382108.411302373 nghttp2-1.68.0/third-party/mruby/build_config/IntelEdison.rb0000644000175100017510000000445315077107276023457 0ustar00runnerrunner# Cross-compiling setup for Intel Edison (poky linux) platform # Get SDK from here: https://software.intel.com/en-us/iot/hardware/edison/downloads # REMEMBER to check and update the SDK root in the constant POKY_EDISON_PATH MRuby::Build.new do |conf| toolchain :gcc conf.gembox 'default' conf.cc.defines = %w(MRB_USE_READLINE) conf.gembox 'default' #lightweight regular expression conf.gem :github => "pbosetti/mruby-hs-regexp", :branch => "master" end # Define cross build settings MRuby::CrossBuild.new('core2-32-poky-linux') do |conf| toolchain :gcc # Mac OS X # POKY_EDISON_PATH = '/opt/poky-edison/1.7.2' POKY_EDISON_SYSROOT = "#{POKY_EDISON_PATH}/sysroots/core2-32-poky-linux" POKY_EDISON_X86_PATH = "#{POKY_EDISON_PATH}/sysroots/i386-pokysdk-darwin" POKY_EDISON_BIN_PATH = "#{POKY_EDISON_X86_PATH}/usr/bin/i586-poky-linux" conf.cc do |cc| cc.command = "#{POKY_EDISON_BIN_PATH}/i586-poky-linux-gcc" cc.include_paths << ["#{POKY_EDISON_SYSROOT}/usr/include", "#{POKY_EDISON_X86_PATH}/usr/include"] cc.flags = %w(-m32 -march=core2 -mtune=core2 -msse3 -mfpmath=sse -mstackrealign -fno-omit-frame-pointer) cc.flags << %w(-O2 -pipe -g -feliminate-unused-debug-types) cc.flags << "--sysroot=#{POKY_EDISON_SYSROOT}" cc.compile_options = %Q[%{flags} -o "%{outfile}" -c "%{infile}"] cc.defines = %w(MRB_USE_READLINE) end conf.cxx do |cxx| cxx.command = "#{POKY_EDISON_BIN_PATH}/i586-poky-linux-g++" cxx.include_paths = conf.cc.include_paths.dup cxx.include_paths << ["#{POKY_EDISON_SYSROOT}/usr/include/c++/4.9.1"] cxx.flags = conf.cc.flags.dup cxx.defines = conf.cc.defines.dup cxx.compile_options = conf.cc.compile_options.dup end conf.archiver do |archiver| archiver.command = "#{POKY_EDISON_BIN_PATH}/i586-poky-linux-ar" archiver.archive_options = 'rcs "%{outfile}" %{objs}' end conf.linker do |linker| linker.command = "#{POKY_EDISON_BIN_PATH}/i586-poky-linux-g++" linker.flags = %w(-m32 -march=i586) linker.flags << "--sysroot=#{POKY_EDISON_SYSROOT}" linker.flags << %w(-O1) linker.libraries = %w(m pthread) end #do not build executable test conf.build_mrbtest_lib_only conf.gembox 'default' #lightweight regular expression conf.gem :github => "pbosetti/mruby-hs-regexp", :branch => "master" end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/cross-mingw.rb0000644000000000000000000000013215077107276023114 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.119411421 30 ctime=1761382108.438302295 nghttp2-1.68.0/third-party/mruby/build_config/cross-mingw.rb0000644000175100017510000000072415077107276023507 0ustar00runnerrunner# # Ubuntu 20.04 requires at least `gcc-mingw-w64-x86-64` package as a # cross compiler. # MRuby::CrossBuild.new("cross-mingw") do |conf| conf.toolchain :gcc conf.host_target = "x86_64-w64-mingw32" # required for `for_windows?` used by `mruby-socket` gem conf.cc.command = "#{conf.host_target}-gcc-posix" conf.linker.command = conf.cc.command conf.archiver.command = "#{conf.host_target}-gcc-ar" conf.exts.executable = ".exe" conf.gembox "default" end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/dreamcast_shelf.rb0000644000000000000000000000013215077107276023770 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.119411421 30 ctime=1761382108.421302344 nghttp2-1.68.0/third-party/mruby/build_config/dreamcast_shelf.rb0000644000175100017510000000575615077107276024375 0ustar00runnerrunner# Cross Compiling configuration for the Sega Dreamcast # https://dreamcast.wiki/Using_Ruby_for_Sega_Dreamcast_development # # Requires KallistiOS (KOS) # http://gamedev.allusion.net/softprj/kos/ # # This configuration has been improved to be used as KallistiOS Port (kos-ports) # Updated: 2023-12-24 # # Tested on GNU/Linux, macOS and Windows (MinGW-w64/MSYS2, Cygwin, DreamSDK) # DreamSDK is based on MinGW/MSYS: https://dreamsdk.org/ # # Install mruby for Sega Dreamcast using the "mruby" kos-port. # MRuby::CrossBuild.new("dreamcast") do |conf| toolchain :gcc # Getting critical environment variables KOS_BASE = ENV["KOS_BASE"] KOS_CC_BASE = ENV["KOS_CC_BASE"] if (KOS_BASE.nil? || KOS_BASE.empty? || KOS_CC_BASE.nil? || KOS_CC_BASE.empty?) raise "Error: KallistiOS is required; KOS_BASE/KOS_CC_BASE needs to be declared; Stop." end # C compiler # All flags and settings below were extracted from KallistiOS environment files conf.cc do |cc| cc.command = "#{KOS_CC_BASE}/bin/sh-elf-gcc" cc.include_paths << ["#{KOS_BASE}/include", "#{KOS_BASE}/kernel/arch/dreamcast/include", "#{KOS_BASE}/addons/include", "#{KOS_BASE}/../kos-ports/include"] cc.flags << ["-O2", "-fomit-frame-pointer", "-fno-builtin", "-ml", "-m4-single-only", "-ffunction-sections", "-fdata-sections", "-matomic-model=soft-imask", "-ftls-model=local-exec", "-Wall", "-g"] cc.compile_options = %Q[%{flags} -o "%{outfile}" -c "%{infile}"] cc.defines << %w(_arch_dreamcast) cc.defines << %w(_arch_sub_pristine) end # C++ compiler conf.cxx do |cxx| cxx.command = conf.cc.command.dup cxx.include_paths = conf.cc.include_paths.dup cxx.flags = conf.cc.flags.dup cxx.flags << %w(-fno-operator-names) cxx.defines = conf.cc.defines.dup cxx.compile_options = conf.cc.compile_options.dup end # Linker conf.linker do |linker| linker.command = "#{KOS_CC_BASE}/bin/sh-elf-gcc" linker.flags << ["-Wl,-Ttext=0x8c010000", "-Wl,--gc-sections", "-T#{KOS_BASE}/utils/ldscripts/shlelf.xc", "-nodefaultlibs", "-Wl,--start-group -lkallisti -lc -lgcc -Wl,--end-group"] linker.library_paths << ["#{KOS_BASE}/lib/dreamcast", "#{KOS_BASE}/addons/lib/dreamcast", "#{KOS_BASE}/../kos-ports/lib"] end # Archiver conf.archiver do |archiver| archiver.command = "#{KOS_CC_BASE}/bin/sh-elf-ar" archiver.archive_options = 'rcs "%{outfile}" %{objs}' end # No executables needed for KallistiOS conf.bins = [] # Do not build test binaries conf.build_mrbtest_lib_only # Gemboxes conf.gembox "default-no-stdio" conf.gembox "stdlib-ext" conf.gembox "metaprog" # Additional Gems # Currently unsupported on KallistiOS: "mruby-io", "mruby-dir", "mruby-socket" conf.gem :core => "mruby-binding" conf.gem :core => "mruby-catch" conf.gem :core => "mruby-enum-chain" conf.gem :core => "mruby-errno" conf.gem :core => "mruby-error" conf.gem :core => "mruby-exit" conf.gem :core => "mruby-os-memsize" conf.gem :core => "mruby-proc-binding" conf.gem :core => "mruby-sleep" end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/boxing.rb0000644000000000000000000000013215077107276022132 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.118411425 30 ctime=1761382108.413302367 nghttp2-1.68.0/third-party/mruby/build_config/boxing.rb0000644000175100017510000000075215077107276022526 0ustar00runnerrunnerboxings = %w[no word nan] bits = [64, 32] ints = [64, 32] boxings.product(bits, ints) do |boxing, bit, int| MRuby::Build.new("boxing-#{boxing}-m#{bit}-i#{int}") do |conf| conf.toolchain :gcc conf.gembox 'full-core' conf.compilers.each do |c| c.defines << "MRB_#{boxing.upcase}_BOXING" c.defines << "MRB_INT#{int}" c.flags << "-m#{bit}" end conf.linker.flags << "-m#{bit}" conf.enable_debug conf.enable_test conf.enable_bintest end end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/bench.rb0000644000000000000000000000013215077107276021723 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.118411425 30 ctime=1761382108.404302393 nghttp2-1.68.0/third-party/mruby/build_config/bench.rb0000644000175100017510000000036415077107276022316 0ustar00runnerrunnerMRuby::Build.new('bench') do |conf| # Gets set by the VS command prompts. if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR'] toolchain :visualcpp else toolchain :gcc conf.cc.flags << '-O3' end conf.gembox 'default' end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/nintendo_switch.rb0000644000000000000000000000013215077107276024043 xustar0030 mtime=1761382078.097420671 30 atime=1761382080.119411421 30 ctime=1761382108.418302353 nghttp2-1.68.0/third-party/mruby/build_config/nintendo_switch.rb0000644000175100017510000000530415077107276024435 0ustar00runnerrunner# Cross Compiling configuration for the Nintendo Switch, it requires Nintendo SDK # Tested on windows MRuby::CrossBuild.new('nintendo_switch_32bit') do |conf| conf.toolchain :clang NINTENDO_SDK_PATH = ENV['NINTENDO_SDK_ROOT'] include_paths = [ "#{NINTENDO_SDK_PATH}/Include", "#{NINTENDO_SDK_PATH}/Common/Configs/Targets/NX-NXFP2-a32/Include" ] conf.cc do |cc| cc.command = "#{NINTENDO_SDK_PATH}/Compilers/NX/bin/nx-clang++" cc.include_paths += include_paths cc.flags += ['-fpic -fno-short-enums -ffunction-sections -fdata-sections -fno-common -fno-strict-aliasing -fomit-frame-pointer -fno-vectorize -funsigned-char -O2 -g -mno-implicit-float'] cc.defines += 'NN_SDK_BUILD_RELEASE' end conf.cxx do |cxx| cxx.command = "#{NINTENDO_SDK_PATH}/Compilers/NX/bin/nx-clang++" cxx.include_paths += include_paths cxx.flags += ['-fpic -fno-short-enums -ffunction-sections -fdata-sections -fno-common -fno-strict-aliasing -fomit-frame-pointer -fno-vectorize -funsigned-char -O2 -g -mno-implicit-float'] cxx.defines += 'NN_SDK_BUILD_RELEASE' end conf.archiver do |archiver| archiver.command = "#{NINTENDO_SDK_PATH}/Compilers/NX/nx/armv7l/bin/llvm-ar" end conf.linker do |linker| linker.command = "#{NINTENDO_SDK_PATH}/Compilers/NX/nx/armv7l/bin/clang++" linker.libraries = [] end # Add your mrbgems end MRuby::CrossBuild.new('nintendo_switch_64bit') do |conf| conf.toolchain :clang NINTENDO_SDK_PATH = ENV['NINTENDO_SDK_ROOT'] include_paths = [ "#{NINTENDO_SDK_PATH}/Include", "#{NINTENDO_SDK_PATH}/Common/Configs/Targets/NX-NXFP2-a64/Include" ] conf.cc do |cc| cc.command = "#{NINTENDO_SDK_PATH}/Compilers/NX/bin/nx-clang++" cc.include_paths += include_paths cc.flags += ['-fpic -fno-short-enums -ffunction-sections -fdata-sections -fno-common -fno-strict-aliasing -fomit-frame-pointer -fno-vectorize -funsigned-char -O2 -g -mno-implicit-float'] cc.flags << '--target=aarch64-nintendo-nx-elf' cc.defines += 'NN_SDK_BUILD_RELEASE' end conf.cxx do |cxx| cxx.command = "#{NINTENDO_SDK_PATH}/Compilers/NX/bin/nx-clang++" cxx.include_paths += include_paths cxx.flags += ['-fpic -fno-short-enums -ffunction-sections -fdata-sections -fno-common -fno-strict-aliasing -fomit-frame-pointer -fno-vectorize -funsigned-char -O2 -g -mno-implicit-float'] cxx.flags << '--target=aarch64-nintendo-nx-elf' cxx.defines += 'NN_SDK_BUILD_RELEASE' end conf.archiver do |archiver| archiver.command = "#{NINTENDO_SDK_PATH}/Compilers/NX/nx/aarch64/bin/llvm-ar" end conf.linker do |linker| linker.command = "#{NINTENDO_SDK_PATH}/Compilers/NX/nx/aarch64/bin/clang++" linker.libraries = [] end # Add your mrbgems end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/emscripten.rb0000644000000000000000000000013215077107276023015 xustar0030 mtime=1761382078.097420671 30 atime=1761382080.119411421 30 ctime=1761382108.423302338 nghttp2-1.68.0/third-party/mruby/build_config/emscripten.rb0000644000175100017510000000045315077107276023407 0ustar00runnerrunner# Make sure to add these compile options: # build/emscripten/host-bin/mruby-config --cflags # # Make sure to add these link options: # build/emscripten/host-bin/mruby-config --ldflags --libs MRuby::CrossBuild.new('emscripten') do |conf| conf.toolchain :emscripten conf.gembox 'default' end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/ci0000644000000000000000000000013215077107334020630 xustar0030 mtime=1761382108.417302355 30 atime=1761382109.795298372 30 ctime=1761382108.417302355 nghttp2-1.68.0/third-party/mruby/build_config/ci/0000755000175100017510000000000015077107334021275 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/build_config/ci/PaxHeaders/msvc.rb0000644000000000000000000000013215077107276022207 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.118411425 30 ctime=1761382108.416302358 nghttp2-1.68.0/third-party/mruby/build_config/ci/msvc.rb0000644000175100017510000000107315077107276022600 0ustar00runnerrunnerSTDOUT.sync = STDERR.sync = true unless Rake.application.options.always_multitask def setup_option(conf) conf.cc.compile_options.sub!(%r{/Zi }, "") unless ENV['CFLAGS'] conf.cxx.compile_options.sub!(%r{/Zi }, "") unless ENV['CFLAGS'] || ENV['CXXFLAGS'] conf.linker.flags << "/DEBUG:NONE" unless ENV['LDFLAGS'] end MRuby::Build.new do |conf| conf.toolchain :visualcpp # include all core GEMs conf.gembox 'full-core' conf.compilers.each do |c| c.defines += %w(MRB_GC_FIXED_ARENA) end setup_option(conf) conf.enable_bintest conf.enable_test end nghttp2-1.68.0/third-party/mruby/build_config/ci/PaxHeaders/gcc-clang.rb0000644000000000000000000000013215077107276023055 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.118411425 30 ctime=1761382108.417302355 nghttp2-1.68.0/third-party/mruby/build_config/ci/gcc-clang.rb0000644000175100017510000000150015077107276023441 0ustar00runnerrunnerSTDOUT.sync = STDERR.sync = true unless Rake.application.options.always_multitask MRuby::Build.new('full-debug') do |conf| conf.toolchain conf.enable_debug # include all core GEMs conf.gembox 'full-core' conf.cc.defines += %w(MRB_GC_STRESS MRB_USE_DEBUG_HOOK) conf.enable_test end MRuby::Build.new do |conf| conf.toolchain # include all core GEMs conf.gembox 'full-core' conf.gem :core => 'mruby-bin-debugger' conf.compilers.each do |c| c.defines += %w(MRB_GC_FIXED_ARENA) end conf.enable_bintest conf.enable_test end MRuby::Build.new('cxx_abi') do |conf| conf.toolchain conf.gembox 'full-core' conf.cc.flags += %w(-fpermissive -std=gnu++03) conf.compilers.each do |c| c.defines += %w(MRB_GC_FIXED_ARENA) end conf.enable_test conf.enable_cxx_abi conf.build_mrbc_exec end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/playstationportable.rb0000644000000000000000000000013115077107276024743 xustar0030 mtime=1761382078.097420671 30 atime=1761382080.119411421 29 ctime=1761382108.41230237 nghttp2-1.68.0/third-party/mruby/build_config/playstationportable.rb0000644000175100017510000000470315077107276025340 0ustar00runnerrunner# Cross Compiling configuration for the Sony PlayStation Portable. # This configuration requires toolchain from https://github.com/pspdev MRuby::CrossBuild.new("playstationportable") do |conf| toolchain :gcc PSPDEV_PATH = "#{ENV['PSPDEV']}" BIN_PATH = "#{PSPDEV_PATH}/bin" # C compiler conf.cc do |cc| cc.command = "#{BIN_PATH}/psp-gcc" cc.flags << ["-O2", "-D_PSP_FW_VERSION=600"] cc.include_paths << ["#{PSPDEV_PATH}/psp/include", "#{PSPDEV_PATH}/psp/sdk/include"] cc.compile_options = %(%{flags} -o "%{outfile}" -c "%{infile}") end # C++ compiler conf.cxx do |cxx| cxx.command = "#{BIN_PATH}/psp-g++" cxx.include_paths = conf.cc.include_paths.dup cxx.flags = conf.cc.flags.dup cxx.flags << %w[-fno-rtti -fno-exceptions] cxx.defines = conf.cc.defines.dup cxx.compile_options = conf.cc.compile_options.dup end # Linker conf.linker do |linker| linker.command = "#{BIN_PATH}/psp-gcc" linker.flags << ["-Wl,-zmax-page-size=128"] end # No executables conf.bins = [] # Do not build executable test conf.build_mrbtest_lib_only # Gems from core conf.gem :core => "mruby-metaprog" conf.gem :core => "mruby-pack" conf.gem :core => "mruby-sprintf" conf.gem :core => "mruby-math" conf.gem :core => "mruby-time" conf.gem :core => "mruby-struct" conf.gem :core => "mruby-compar-ext" conf.gem :core => "mruby-enum-ext" conf.gem :core => "mruby-string-ext" conf.gem :core => "mruby-numeric-ext" conf.gem :core => "mruby-array-ext" conf.gem :core => "mruby-hash-ext" conf.gem :core => "mruby-range-ext" conf.gem :core => "mruby-proc-ext" conf.gem :core => "mruby-symbol-ext" conf.gem :core => "mruby-random" conf.gem :core => "mruby-object-ext" conf.gem :core => "mruby-objectspace" conf.gem :core => "mruby-fiber" conf.gem :core => "mruby-enumerator" conf.gem :core => "mruby-enum-lazy" conf.gem :core => "mruby-toplevel-ext" conf.gem :core => "mruby-kernel-ext" conf.gem :core => "mruby-class-ext" conf.gem :core => "mruby-compiler" conf.gem :core => "mruby-binding" conf.gem :core => "mruby-catch" conf.gem :core => "mruby-enum-chain" conf.gem :core => "mruby-errno" conf.gem :core => "mruby-error" conf.gem :core => "mruby-exit" conf.gem :core => "mruby-os-memsize" conf.gem :core => "mruby-proc-binding" conf.gem :core => "mruby-sleep" conf.gem :core => "mruby-io" conf.gem :core => "mruby-dir" #conf.gem :core => "mruby-socket" unsupported end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/serenity.rb0000644000000000000000000000013215077107276022506 xustar0030 mtime=1761382078.097420671 30 atime=1761382080.119411421 30 ctime=1761382108.420302347 nghttp2-1.68.0/third-party/mruby/build_config/serenity.rb0000644000175100017510000000167215077107276023104 0ustar00runnerrunner# Cross compiling configuration for SerenityOS # Graphical Unix-like operating system for x86 computers. # https://github.com/SerenityOS/serenity # # Should be built using the SerenityOS Ports system # https://github.com/SerenityOS/serenity/tree/master/Ports # # As of 2021/08/20, SERENITY_ARCH is defined in the SerenityOS Ports # build script to always be either "i686" or "x86_64" MRuby::CrossBuild.new('serenity') do |conf| conf.toolchain :gcc conf.archiver.command = "#{ENV['SERENITY_ARCH']}-pc-serenity-ar" conf.linker.command = "#{ENV['SERENITY_ARCH']}-pc-serenity-g++" conf.cxx.command = "#{ENV['SERENITY_ARCH']}-pc-serenity-g++" conf.cxx.defines << (ENV['SERENITY_ARCH'].include?('64') ? 'MRB_64BIT' : 'MRB_32BIT') conf.cc.command = "#{ENV['SERENITY_ARCH']}-pc-serenity-gcc" conf.cc.defines << (ENV['SERENITY_ARCH'].include?('64') ? 'MRB_64BIT' : 'MRB_32BIT') conf.gembox 'full-core' conf.test_runner.command = 'env' end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/minimal.rb0000644000000000000000000000013215077107276022272 xustar0030 mtime=1761382078.097420671 30 atime=1761382080.119411421 30 ctime=1761382108.399302407 nghttp2-1.68.0/third-party/mruby/build_config/minimal.rb0000644000175100017510000000015115077107276022657 0ustar00runnerrunnerMRuby::CrossBuild.new('minimal') do |conf| conf.toolchain :gcc conf.cc.defines << 'MRB_NO_STDIO' end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/luckfox_pico.rb0000644000000000000000000000013215077107276023331 xustar0030 mtime=1761382078.097420671 30 atime=1761382080.119411421 30 ctime=1761382108.430302318 nghttp2-1.68.0/third-party/mruby/build_config/luckfox_pico.rb0000644000175100017510000001006515077107276023723 0ustar00runnerrunner# Cross Compiling configuration for Luckfox Pico embedded SBC. # # To build on Ubuntu x86_64: rake MRUBY_CONFIG=build_config/luckfox_pico.rb # Uses Buildroot SDK for this board. Binaries run on the corresponding Linux image. # Requires: https://github.com/LuckfoxTECH/luckfox-pico # # NOTE: default config includes all standard mrbgems, EXCEPT: mruby-cmath # MRuby::CrossBuild.new("luckfox_pico") do |conf| # Clone the luckfox-pico repo above next to (same directory level) as mruby. SDK_BASE = File.realpath(File.expand_path("../../../", File.expand_path(__FILE__)) + "/luckfox-pico") TOOLCHAIN_BASE = "#{SDK_BASE}/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf" SYSROOT = "#{TOOLCHAIN_BASE}/arm-rockchip830-linux-uclibcgnueabihf/sysroot" toolchain :gcc # C compiler settings conf.cc do |cc| cc.command = "#{TOOLCHAIN_BASE}/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc" cc.include_paths << "#{TOOLCHAIN_BASE}/lib/gcc/arm-rockchip830-linux-uclibcgnueabihf/8.3.0/include" cc.include_paths << "#{TOOLCHAIN_BASE}/lib/gcc/arm-rockchip830-linux-uclibcgnueabihf/8.3.0/include-fixed" cc.include_paths << "#{TOOLCHAIN_BASE}/arm-rockchip830-linux-uclibcgnueabihf/include/c++/8.3.0/arm-rockchip830-linux-uclibcgnueabihf" cc.include_paths << "#{SYSROOT}/usr/include" # Flags taken from the SDK's Makefile cc.flags << ["-march=armv7-a", "-mfpu=neon", "-mfloat-abi=hard"] cc.flags << ["-D_LARGEFILE_SOURCE", "-D_LARGEFILE64_SOURCE", "-D_FILE_OFFSET_BITS=64", "-ffunction-sections", "-fdata-sections"] cc.flags << ["-O2", "-fPIC"] cc.flags << ["-Wl,--copy-dt-needed-entries", "-Wl,-lc,-lgcc_s"] end # Linker settings conf.linker do |linker| linker.command = cc.command linker.library_paths << "#{TOOLCHAIN_BASE}/arm-rockchip830-linux-uclibcgnueabihf/lib" linker.flags = cc.flags end # Archiver settings conf.archiver do |archiver| archiver.command = "#{TOOLCHAIN_BASE}/bin/arm-rockchip830-linux-uclibcgnueabihf-ar" end # Do not build executable test conf.build_mrbtest_lib_only # Disable C++ exception conf.disable_cxx_exception # All standard gems. conf.gem 'mrbgems/mruby-array-ext/' conf.gem 'mrbgems/mruby-bigint/' conf.gem 'mrbgems/mruby-bin-config/' conf.gem 'mrbgems/mruby-bin-debugger/' conf.gem 'mrbgems/mruby-bin-mirb/' conf.gem 'mrbgems/mruby-bin-mrbc/' conf.gem 'mrbgems/mruby-bin-mruby/' conf.gem 'mrbgems/mruby-bin-strip/' conf.gem 'mrbgems/mruby-binding/' conf.gem 'mrbgems/mruby-catch/' conf.gem 'mrbgems/mruby-class-ext/' # SDK doesn't include complex math for uClibc # conf.gem 'mrbgems/mruby-cmath/' conf.gem 'mrbgems/mruby-compar-ext/' conf.gem 'mrbgems/mruby-compiler/' conf.gem 'mrbgems/mruby-complex/' conf.gem 'mrbgems/mruby-data/' conf.gem 'mrbgems/mruby-dir/' conf.gem 'mrbgems/mruby-enum-chain/' conf.gem 'mrbgems/mruby-enum-ext/' conf.gem 'mrbgems/mruby-enum-lazy/' conf.gem 'mrbgems/mruby-enumerator/' conf.gem 'mrbgems/mruby-errno/' conf.gem 'mrbgems/mruby-error/' conf.gem 'mrbgems/mruby-eval/' conf.gem 'mrbgems/mruby-exit/' conf.gem 'mrbgems/mruby-fiber/' conf.gem 'mrbgems/mruby-hash-ext/' conf.gem 'mrbgems/mruby-io/' conf.gem 'mrbgems/mruby-kernel-ext/' conf.gem 'mrbgems/mruby-math/' conf.gem 'mrbgems/mruby-metaprog/' conf.gem 'mrbgems/mruby-method/' conf.gem 'mrbgems/mruby-numeric-ext/' conf.gem 'mrbgems/mruby-object-ext/' conf.gem 'mrbgems/mruby-objectspace/' conf.gem 'mrbgems/mruby-os-memsize/' conf.gem 'mrbgems/mruby-pack/' conf.gem 'mrbgems/mruby-proc-binding/' conf.gem 'mrbgems/mruby-proc-ext/' conf.gem 'mrbgems/mruby-random/' conf.gem 'mrbgems/mruby-range-ext/' conf.gem 'mrbgems/mruby-rational/' conf.gem 'mrbgems/mruby-set/' conf.gem 'mrbgems/mruby-sleep/' conf.gem 'mrbgems/mruby-socket/' conf.gem 'mrbgems/mruby-sprintf/' conf.gem 'mrbgems/mruby-string-ext/' conf.gem 'mrbgems/mruby-struct/' conf.gem 'mrbgems/mruby-symbol-ext/' # conf.gem 'mrbgems/mruby-test-inline-struct/' # conf.gem 'mrbgems/mruby-test/' conf.gem 'mrbgems/mruby-time/' conf.gem 'mrbgems/mruby-toplevel-ext/' end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/no-float.rb0000644000000000000000000000013215077107276022363 xustar0030 mtime=1761382078.097420671 30 atime=1761382080.119411421 30 ctime=1761382108.408302381 nghttp2-1.68.0/third-party/mruby/build_config/no-float.rb0000644000175100017510000000050315077107276022751 0ustar00runnerrunner# Define cross build settings MRuby::CrossBuild.new('no-float') do |conf| conf.toolchain # Add configuration conf.compilers.each do |c| c.defines << "MRB_NO_FLOAT" end conf.gem :core => "mruby-bin-mruby" conf.test_runner.command = 'env' conf.enable_debug # conf.enable_bintest conf.enable_test end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/IntelGalileo.rb0000644000000000000000000000013215077107276023214 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.118411425 30 ctime=1761382108.414302364 nghttp2-1.68.0/third-party/mruby/build_config/IntelGalileo.rb0000644000175100017510000000633715077107276023615 0ustar00runnerrunner# Cross Compiling configuration for Intel Galileo on Arduino environment # https://arduino.cc/en/ArduinoCertified/IntelGalileo # # Requires Arduino IDE for Intel Galileo MRuby::CrossBuild.new("Galileo") do |conf| toolchain :gcc # Mac OS X # Assume you renamed Arduino.app to Arduino_Galileo.app GALILEO_ARDUINO_PATH = '/Applications/Arduino_Galileo.app/Contents/Resources/Java' # GNU Linux #ARDUINO_GALILEO_PATH = '/opt/arduino' GALILEO_BIN_PATH = "#{GALILEO_ARDUINO_PATH}/hardware/tools/x86/i386-pokysdk-darwin/usr/bin/i586-poky-linux-uclibc" GALILEO_SYSROOT = "#{GALILEO_ARDUINO_PATH}/hardware/tools/x86/i586-poky-linux-uclibc" GALILEO_X86_PATH = "#{GALILEO_ARDUINO_PATH}/hardware/arduino/x86" conf.cc do |cc| cc.command = "#{GALILEO_BIN_PATH}/i586-poky-linux-uclibc-gcc" cc.include_paths << ["#{GALILEO_X86_PATH}/cores/arduino", "#{GALILEO_X86_PATH}/variants/galileo_fab_d"] cc.flags = %w(-m32 -march=i586 -c -g -Os -w -ffunction-sections -fdata-sections -MMD -DARDUINO=153) cc.flags << "--sysroot=#{GALILEO_SYSROOT}" cc.compile_options = %Q[%{flags} -o "%{outfile}" -c "%{infile}"] end conf.cxx do |cxx| cxx.command = "#{GALILEO_BIN_PATH}/i586-poky-linux-uclibc-g++" cxx.include_paths = conf.cc.include_paths.dup cxx.include_paths << "#{GALILEO_ARDUINO_PATH}/hardware/tools/x86/i586-poky-linux-uclibc/usr/include/c++" cxx.include_paths << "#{GALILEO_ARDUINO_PATH}/hardware/tools/x86/i586-poky-linux-uclibc/usr/include/c++/i586-poky-linux-uclibc" cxx.flags = conf.cc.flags.dup cxx.defines = conf.cc.defines.dup cxx.compile_options = conf.cc.compile_options.dup end conf.archiver do |archiver| archiver.command = "#{GALILEO_BIN_PATH}/i586-poky-linux-uclibc-ar" archiver.archive_options = 'rcs "%{outfile}" %{objs}' end conf.linker do |linker| linker.command = "#{GALILEO_BIN_PATH}/i586-poky-linux-uclibc-g++" linker.flags = %w(-m32 -march=i586) linker.flags << "--sysroot=#{GALILEO_SYSROOT}" linker.flags << %w(-Os -Wl,--gc-sections) linker.libraries = %w(m pthread) end #no executables conf.bins = [] #do not build executable test conf.build_mrbtest_lib_only #official mrbgems conf.gem :core => "mruby-sprintf" conf.gem :core => "mruby-math" conf.gem :core => "mruby-time" conf.gem :core => "mruby-struct" conf.gem :core => "mruby-enum-ext" conf.gem :core => "mruby-string-ext" conf.gem :core => "mruby-numeric-ext" conf.gem :core => "mruby-array-ext" conf.gem :core => "mruby-hash-ext" conf.gem :core => "mruby-range-ext" conf.gem :core => "mruby-proc-ext" conf.gem :core => "mruby-symbol-ext" conf.gem :core => "mruby-random" conf.gem :core => "mruby-object-ext" conf.gem :core => "mruby-objectspace" conf.gem :core => "mruby-fiber" conf.gem :core => "mruby-toplevel-ext" #lightweight regular expression conf.gem :github => "masamitsu-murase/mruby-hs-regexp", :branch => "master" #Arduino API #conf.gem :github =>"kyab/mruby-arduino", :branch => "master" do |g| # g.cxx.include_paths << "#{GALILEO_X86_PATH}/libraries/Wire" # g.cxx.include_paths << "#{GALILEO_X86_PATH}/libraries/Servo" #enable unsupported Servo class # g.cxx.defines << "MRUBY_ARDUINO_GALILEO_ENABLE_SERVO" #end end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/ArduinoDue.rb0000644000000000000000000000013215077107276022703 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.118411425 30 ctime=1761382108.409302378 nghttp2-1.68.0/third-party/mruby/build_config/ArduinoDue.rb0000644000175100017510000000522115077107276023273 0ustar00runnerrunner# Cross Compiling configuration for Arduino Due # https://arduino.cc/en/Main/ArduinoBoardDue # # Requires Arduino IDE >= 1.5 MRuby::CrossBuild.new("ArduinoDue") do |conf| toolchain :gcc # Mac OS X, Arduino IDE <= 1.5.6 # ARDUINO_PATH = '/Applications/Arduino.app/Contents/Resources/Java' # Mac OS X, Arduino IDE >= 1.5.7 # ARDUINO_PATH = '/Applications/Arduino.app/Contents/Java' # GNU Linux ARDUINO_PATH = '/opt/arduino' # Arduino IDE <= 1.5.6 BIN_PATH = "#{ARDUINO_PATH}/hardware/tools/g++_arm_none_eabi/bin" # Arduino IDE >= 1.5.7 # BIN_PATH = "#{ARDUINO_PATH}/hardware/tools/gcc-arm-none-eabi-4.8.3-2014q1/bin" SAM_PATH = "#{ARDUINO_PATH}/hardware/arduino/sam" TARGET_PATH = "#{SAM_PATH}/variants/arduino_due_x" conf.cc do |cc| cc.command = "#{BIN_PATH}/arm-none-eabi-gcc" cc.include_paths << ["#{SAM_PATH}/system/libsam", "#{SAM_PATH}/system/CMSIS/CMSIS/Include/", "#{SAM_PATH}/system/CMSIS/Device/ATMEL/", "#{SAM_PATH}/cores/arduino", "#{SAM_PATH}/libraries","#{TARGET_PATH}"] cc.flags = %w(-g -Os -w -ffunction-sections -fdata-sections -nostdlib --param max-inline-insns-single=500 -Dprintf=iprintf -mcpu=cortex-m3 -DF_CPU=84000000L -DARDUINO=156 -DARDUINO_SAM_DUE -DARDUINO_ARCH_SAM -D__SAM3X8E__ -mthumb -DUSB_PID=0x003e -DUSB_VID=0x2341 -DUSBCON -DUSB_MANUFACTURER="Unknown" -DUSB_PRODUCT="Arduino Due") cc.compile_options = %Q[%{flags} -o "%{outfile}" -c "%{infile}"] #configuration for low memory environment cc.defines << %w(MRB_HEAP_PAGE_SIZE=64) cc.defines << %w(KHASH_DEFAULT_SIZE=8) cc.defines << %w(MRB_GC_STRESS) #cc.defines << %w(MRB_NO_STDIO) #if you don't need stdio. #cc.defines << %w(POOL_PAGE_SIZE=1000) #effective only for use with mruby-eval end conf.cxx do |cxx| cxx.command = conf.cc.command.dup cxx.include_paths = conf.cc.include_paths.dup cxx.flags = conf.cc.flags.dup cxx.flags << %w(-fno-rtti -fno-exceptions) cxx.defines = conf.cc.defines.dup cxx.compile_options = conf.cc.compile_options.dup end conf.archiver do |archiver| archiver.command = "#{BIN_PATH}/arm-none-eabi-ar" archiver.archive_options = 'rcs "%{outfile}" %{objs}' end #no executables conf.bins = [] #do not build executable test conf.build_mrbtest_lib_only #disable C++ exception conf.disable_cxx_exception #gems from core conf.gem :core => "mruby-math" conf.gem :core => "mruby-enum-ext" #light-weight regular expression conf.gem :github => "masamitsu-murase/mruby-hs-regexp", :branch => "master" #Arduino API #conf.gem :github =>"kyab/mruby-arduino", :branch => "master" end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/android_armeabi_v7a_neon_hard.rb0000644000000000000000000000013215077107276026536 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.118411425 30 ctime=1761382108.425302332 nghttp2-1.68.0/third-party/mruby/build_config/android_armeabi_v7a_neon_hard.rb0000644000175100017510000000045615077107276027133 0ustar00runnerrunner# Requires Android NDK r13 or later. MRuby::CrossBuild.new('android-armeabi-v7a-neon-hard') do |conf| params = { :arch => 'armeabi-v7a', :mfpu => 'neon', :mfloat_abi => 'hard', :sdk_version => 33, :toolchain => :clang } toolchain :android, params conf.gembox 'default' end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/clang-asan.rb0000644000000000000000000000013215077107276022650 xustar0030 mtime=1761382078.096420675 30 atime=1761382080.118411425 30 ctime=1761382108.427302326 nghttp2-1.68.0/third-party/mruby/build_config/clang-asan.rb0000644000175100017510000000040315077107276023235 0ustar00runnerrunnerMRuby::Build.new do |conf| conf.toolchain :clang # include the GEM box conf.gembox 'full-core' # Turn on `enable_debug` for better debugging conf.enable_sanitizer "address,undefined" conf.enable_debug conf.enable_bintest conf.enable_test end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/host-nofloat.rb0000644000000000000000000000013215077107276023261 xustar0030 mtime=1761382078.097420671 30 atime=1761382080.119411421 30 ctime=1761382108.431302315 nghttp2-1.68.0/third-party/mruby/build_config/host-nofloat.rb0000644000175100017510000000066715077107276023662 0ustar00runnerrunnerMRuby::Build.new do |conf| # load specific toolchain settings toolchain :gcc # include the GEM box conf.gembox "stdlib" conf.gembox "stdlib-ext" conf.gembox "stdlib-io" conf.gembox "metaprog" conf.gem :core => 'mruby-bin-mruby' conf.gem :core => 'mruby-bin-mirb' # Add configuration conf.compilers.each do |c| c.defines << "MRB_NO_FLOAT" end conf.enable_debug conf.enable_test conf.enable_bintest end nghttp2-1.68.0/third-party/mruby/build_config/PaxHeaders/nintendo_wii.rb0000644000000000000000000000013215077107276023332 xustar0030 mtime=1761382078.097420671 30 atime=1761382080.119411421 30 ctime=1761382108.394302422 nghttp2-1.68.0/third-party/mruby/build_config/nintendo_wii.rb0000644000175100017510000000563715077107276023735 0ustar00runnerrunner# Cross Compiling configuration for the Nintendo Wii # This configuration requires devkitPPC # https://devkitpro.org/wiki/Getting_Started # # MRuby::CrossBuild.new("wii") do |conf| toolchain :gcc DEVKITPRO_PATH = "/opt/devkitpro" BIN_PATH = "#{DEVKITPRO_PATH}/devkitPPC/bin" # C compiler conf.cc do |cc| cc.command = "#{BIN_PATH}/powerpc-eabi-gcc" cc.compile_options = %(%{flags} -o "%{outfile}" -c "%{infile}") end # C++ compiler conf.cxx do |cxx| cxx.command = "#{BIN_PATH}/powerpc-eabi-g++" cxx.include_paths = conf.cc.include_paths.dup cxx.flags = conf.cc.flags.dup cxx.defines = conf.cc.defines.dup cxx.compile_options = conf.cc.compile_options.dup end # Linker conf.linker do |linker| linker.command = "#{BIN_PATH}/powerpc-eabi-gcc" end # No executables conf.bins = [] # Do not build executable test conf.build_mrbtest_lib_only # Disable C++ exception conf.disable_cxx_exception # All current core gems with ones with build issues commented out conf.gem 'mrbgems/mruby-array-ext/' conf.gem 'mrbgems/mruby-bigint/' conf.gem 'mrbgems/mruby-bin-config/' conf.gem 'mrbgems/mruby-bin-debugger/' conf.gem 'mrbgems/mruby-bin-mirb/' conf.gem 'mrbgems/mruby-bin-mrbc/' conf.gem 'mrbgems/mruby-bin-mruby/' conf.gem 'mrbgems/mruby-bin-strip/' conf.gem 'mrbgems/mruby-binding/' conf.gem 'mrbgems/mruby-catch/' conf.gem 'mrbgems/mruby-class-ext/' conf.gem 'mrbgems/mruby-cmath/' conf.gem 'mrbgems/mruby-compar-ext/' conf.gem 'mrbgems/mruby-compiler/' conf.gem 'mrbgems/mruby-complex/' conf.gem 'mrbgems/mruby-data/' #conf.gem 'mrbgems/mruby-dir/' conf.gem 'mrbgems/mruby-enum-chain/' conf.gem 'mrbgems/mruby-enum-ext/' conf.gem 'mrbgems/mruby-enum-lazy/' conf.gem 'mrbgems/mruby-enumerator/' conf.gem 'mrbgems/mruby-errno/' conf.gem 'mrbgems/mruby-error/' conf.gem 'mrbgems/mruby-eval/' conf.gem 'mrbgems/mruby-exit/' conf.gem 'mrbgems/mruby-fiber/' conf.gem 'mrbgems/mruby-hash-ext/' #conf.gem 'mrbgems/mruby-io/' conf.gem 'mrbgems/mruby-kernel-ext/' conf.gem 'mrbgems/mruby-math/' conf.gem 'mrbgems/mruby-metaprog/' conf.gem 'mrbgems/mruby-method/' conf.gem 'mrbgems/mruby-numeric-ext/' conf.gem 'mrbgems/mruby-object-ext/' conf.gem 'mrbgems/mruby-objectspace/' conf.gem 'mrbgems/mruby-os-memsize/' conf.gem 'mrbgems/mruby-pack/' conf.gem 'mrbgems/mruby-proc-binding/' conf.gem 'mrbgems/mruby-proc-ext/' conf.gem 'mrbgems/mruby-random/' conf.gem 'mrbgems/mruby-range-ext/' conf.gem 'mrbgems/mruby-rational/' conf.gem 'mrbgems/mruby-set/' conf.gem 'mrbgems/mruby-sleep/' #conf.gem 'mrbgems/mruby-socket/' conf.gem 'mrbgems/mruby-sprintf/' conf.gem 'mrbgems/mruby-string-ext/' conf.gem 'mrbgems/mruby-struct/' conf.gem 'mrbgems/mruby-symbol-ext/' conf.gem 'mrbgems/mruby-test-inline-struct/' #conf.gem 'mrbgems/mruby-test/' conf.gem 'mrbgems/mruby-time/' conf.gem 'mrbgems/mruby-toplevel-ext/' end nghttp2-1.68.0/third-party/mruby/PaxHeaders/oss-fuzz0000644000000000000000000000013215077107334017411 xustar0030 mtime=1761382108.390302433 30 atime=1761382109.795298372 30 ctime=1761382108.390302433 nghttp2-1.68.0/third-party/mruby/oss-fuzz/0000755000175100017510000000000015077107334020056 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/oss-fuzz/PaxHeaders/mruby_fuzzer.c0000644000000000000000000000013215077107276022402 xustar0030 mtime=1761382078.131420515 30 atime=1761382080.150411279 30 ctime=1761382108.389302436 nghttp2-1.68.0/third-party/mruby/oss-fuzz/mruby_fuzzer.c0000644000175100017510000000060715077107276022775 0ustar00runnerrunner#include #include #include #include int LLVMFuzzerTestOneInput(uint8_t *Data, size_t size) { if (size < 1) { return 0; } char *code = malloc(size+1); memcpy(code, Data, size); code[size] = '\0'; mrb_state *mrb = mrb_open(); mrb_load_string(mrb, code); mrb_close(mrb); free(code); return 0; } nghttp2-1.68.0/third-party/mruby/oss-fuzz/PaxHeaders/proto_to_ruby.h0000644000000000000000000000013215077107276022552 xustar0030 mtime=1761382078.131420515 30 atime=1761382080.150411279 30 ctime=1761382108.390302433 nghttp2-1.68.0/third-party/mruby/oss-fuzz/proto_to_ruby.h0000644000175100017510000000272615077107276023151 0ustar00runnerrunner#include #include #include #include #include #include #include namespace ruby_fuzzer { class protoConverter { public: protoConverter() { m_numLiveVars = 1; m_numVarsPerScope.push(m_numLiveVars); } protoConverter(protoConverter const& x) { m_numLiveVars = x.m_numLiveVars; m_numVarsPerScope = x.m_numVarsPerScope; } ~protoConverter() {} std::string FunctionToString(Function const& _input); private: void visit(ArrType const&); void visit(Array const&); void visit(AssignmentStatement const&); void visit(BinaryOp const&); void visit(BuiltinFuncs const&); void visit(Const const&); void visit(Function const&); void visit(HashType const&); void visit(IfElse const&); void visit(KVPair const&); void visit(MathConst const&); void visit(MathOps const&); void visit(MathType const&); void visit(ObjectSpace const&); void visit(Rvalue const&); void visit(Statement const&); void visit(StatementSeq const&); void visit(StringExtNoArg const&); void visit(Ternary const&); void visit(Time const&); void visit(VarRef const&); template void visit(google::protobuf::RepeatedPtrField const& _repeated_field); std::string removeSpecial(const std::string &x); std::ostringstream m_output; std::stack m_numVarsPerScope; int32_t m_numLiveVars; }; } nghttp2-1.68.0/third-party/mruby/oss-fuzz/PaxHeaders/proto_to_ruby.cpp0000644000000000000000000000013215077107276023105 xustar0030 mtime=1761382078.131420515 30 atime=1761382080.150411279 30 ctime=1761382108.382302457 nghttp2-1.68.0/third-party/mruby/oss-fuzz/proto_to_ruby.cpp0000644000175100017510000002364615077107276023510 0ustar00runnerrunner#include "proto_to_ruby.h" using namespace ruby_fuzzer; std::string protoConverter::removeSpecial(const std::string &x) { std::string tmp(x); if (!tmp.empty()) tmp.erase(std::remove_if(tmp.begin(), tmp.end(), [](char c) { return !(std::isalpha(c) || std::isdigit(c)); } ), tmp.end()); return tmp; } void protoConverter::visit(ArrType const& x) { if (x.elements_size() > 0) { int i = x.elements_size(); m_output << "["; for (auto &e : x.elements()) { i--; if (i == 0) { visit(e); } else { visit(e); m_output << ", "; } } m_output << "]"; } else { m_output << "[1]"; } } void protoConverter::visit(Array const& x) { switch (x.arr_func()) { case Array::FLATTEN: visit(x.arr_arg()); m_output << ".flatten"; break; case Array::COMPACT: visit(x.arr_arg()); m_output << ".compact"; break; case Array::FETCH: visit(x.arr_arg()); m_output << ".fetch"; break; case Array::FILL: visit(x.arr_arg()); m_output << ".fill"; break; case Array::ROTATE: visit(x.arr_arg()); m_output << ".rotate"; break; case Array::ROTATE_E: visit(x.arr_arg()); m_output << ".rotate!"; break; case Array::DELETEIF: visit(x.arr_arg()); m_output << ".delete_if"; break; case Array::INSERT: visit(x.arr_arg()); m_output << ".insert"; break; case Array::BSEARCH: visit(x.arr_arg()); m_output << ".bsearch"; break; case Array::KEEPIF: visit(x.arr_arg()); m_output << ".keep_if"; break; case Array::SELECT: visit(x.arr_arg()); m_output << ".select"; break; case Array::VALUES_AT: visit(x.arr_arg()); m_output << ".values_at"; break; case Array::BLOCK: visit(x.arr_arg()); m_output << ".index"; break; case Array::DIG: visit(x.arr_arg()); m_output << ".dig"; break; case Array::SLICE: visit(x.arr_arg()); m_output << ".slice"; break; case Array::PERM: visit(x.arr_arg()); m_output << ".permutation"; break; case Array::COMB: visit(x.arr_arg()); m_output << ".combination"; break; case Array::ASSOC: visit(x.arr_arg()); m_output << ".assoc"; break; case Array::RASSOC: visit(x.arr_arg()); m_output << ".rassoc"; break; } m_output << "("; visit(x.val_arg()); m_output << ")"; } void protoConverter::visit(AssignmentStatement const& x) { m_output << "var_" << m_numLiveVars << " = "; visit(x.rvalue()); m_numVarsPerScope.top()++; m_numLiveVars++; m_output << "\n"; } void protoConverter::visit(BinaryOp const& x) { m_output << "("; visit(x.left()); switch (x.op()) { case BinaryOp::ADD: m_output << " + "; break; case BinaryOp::SUB: m_output << " - "; break; case BinaryOp::MUL: m_output << " * "; break; case BinaryOp::DIV: m_output << " / "; break; case BinaryOp::MOD: m_output << " % "; break; case BinaryOp::XOR: m_output << " ^ "; break; case BinaryOp::AND: m_output << " and "; break; case BinaryOp::OR: m_output << " or "; break; case BinaryOp::EQ: m_output << " == "; break; case BinaryOp::NE: m_output << " != "; break; case BinaryOp::LE: m_output << " <= "; break; case BinaryOp::GE: m_output << " >= "; break; case BinaryOp::LT: m_output << " < "; break; case BinaryOp::GT: m_output << " > "; break; case BinaryOp::RS: m_output << " >> "; break; } visit(x.right()); m_output << ")"; } void protoConverter::visit(BuiltinFuncs const& x) { switch (x.bifunc_oneof_case()) { case BuiltinFuncs::kOs: visit(x.os()); break; case BuiltinFuncs::kTime: visit(x.time()); break; case BuiltinFuncs::kArr: visit(x.arr()); break; case BuiltinFuncs::kMops: visit(x.mops()); break; case BuiltinFuncs::BIFUNC_ONEOF_NOT_SET: m_output << "1"; break; } m_output << "\n"; } void protoConverter::visit(Const const& x) { switch (x.const_oneof_case()) { case Const::kIntLit: m_output << "(" << (x.int_lit() % 13) << ")"; break; case Const::kBoolVal: m_output << "(" << x.bool_val() << ")"; break; case Const::CONST_ONEOF_NOT_SET: m_output << "1"; break; } } void protoConverter::visit(Function const& x) { m_output << "def foo()\nvar_0 = 1\n"; visit(x.statements()); m_output << "end\n"; m_output << "foo\n"; } void protoConverter::visit(HashType const& x) { if (x.keyval_size() > 0) { int i = x.keyval_size(); m_output << "{"; for (auto &e : x.keyval()) { i--; if (i == 0) { visit(e); } else { visit(e); m_output << ", "; } } m_output << "}"; } } void protoConverter::visit(IfElse const& x) { m_output << "if "; visit(x.cond()); m_output << "\n"; visit(x.if_body()); m_output << "\nelse\n"; visit(x.else_body()); m_output << "\nend\n"; } void protoConverter::visit(KVPair const& x) { m_output << "\"" << removeSpecial(x.key()) << "\""; m_output << " => "; m_output << "\"" << removeSpecial(x.val()) << "\""; } void protoConverter::visit(MathConst const& x) { switch (x.math_const()) { case MathConst::PI: m_output << "Math::PI"; break; case MathConst::E: m_output << "Math::E"; break; } } void protoConverter::visit(MathOps const& x) { switch (x.math_op()) { case MathOps::CBRT: m_output << "Math.cbrt("; visit(x.math_arg()); m_output << ")"; break; case MathOps::COS: m_output << "Math.cos("; visit(x.math_arg()); m_output << ")"; break; case MathOps::ERF: m_output << "Math.erf("; visit(x.math_arg()); m_output << ")"; break; case MathOps::ERFC: m_output << "Math.erfc("; visit(x.math_arg()); m_output << ")"; break; case MathOps::LOG: m_output << "Math.log("; visit(x.math_arg()); m_output << ")"; break; case MathOps::LOG10: m_output << "Math.log10("; visit(x.math_arg()); m_output << ")"; break; case MathOps::LOG2: m_output << "Math.log2("; visit(x.math_arg()); m_output << ")"; break; case MathOps::SIN: m_output << "Math.sin("; visit(x.math_arg()); m_output << ")"; break; case MathOps::SQRT: m_output << "Math.sqrt("; visit(x.math_arg()); m_output << ")"; break; case MathOps::TAN: m_output << "Math.tan("; visit(x.math_arg()); m_output << ")"; break; } } void protoConverter::visit(MathType const& x) { switch (x.math_arg_oneof_case()) { case MathType::kMathRval: visit(x.math_rval()); break; case MathType::kMathConst: visit(x.math_const()); break; case MathType::MATH_ARG_ONEOF_NOT_SET: m_output << "1"; break; } } void protoConverter::visit(ObjectSpace const& x) { switch (x.os_func()) { case ObjectSpace::COUNT: m_output << "ObjectSpace.count_objects"; break; } m_output << "("; visit(x.os_arg()); m_output << ")" << "\n"; } void protoConverter::visit(Rvalue const& x) { switch (x.rvalue_oneof_case()) { case Rvalue::kVarref: visit(x.varref()); break; case Rvalue::kCons: visit(x.cons()); break; case Rvalue::kBinop: visit(x.binop()); break; case Rvalue::RVALUE_ONEOF_NOT_SET: m_output << "1"; break; } } void protoConverter::visit(Statement const& x) { switch (x.stmt_oneof_case()) { case Statement::kAssignment: visit(x.assignment()); break; case Statement::kIfelse: visit(x.ifelse()); break; case Statement::kTernaryStmt: visit(x.ternary_stmt()); break; case Statement::kBuiltins: visit(x.builtins()); break; case Statement::kBlockstmt: visit(x.blockstmt()); break; case Statement::STMT_ONEOF_NOT_SET: break; } m_output << "\n"; } void protoConverter::visit(StatementSeq const& x) { if (x.statements_size() > 0) { m_numVarsPerScope.push(0); m_output << "@scope ||= begin\n"; for (auto &st : x.statements()) visit(st); m_output << "end\n"; m_numLiveVars -= m_numVarsPerScope.top(); m_numVarsPerScope.pop(); } } void protoConverter::visit(StringExtNoArg const& x) { m_output << "\"" << removeSpecial(x.str_arg()) << "\""; switch (x.str_op()) { case StringExtNoArg::DUMP: m_output << ".dump"; break; case StringExtNoArg::STRIP: m_output << ".strip"; break; case StringExtNoArg::LSTRIP: m_output << ".lstrip"; break; case StringExtNoArg::RSTRIP: m_output << ".rstrip"; break; case StringExtNoArg::STRIPE: m_output << ".strip!"; break; case StringExtNoArg::LSTRIPE: m_output << ".lstrip!"; break; case StringExtNoArg::RSTRIPE: m_output << ".rstrip!"; break; case StringExtNoArg::SWAPCASE: m_output << ".swapcase"; break; case StringExtNoArg::SWAPCASEE: m_output << ".swapcase!"; break; case StringExtNoArg::SQUEEZE: m_output << ".squeeze"; break; } } void protoConverter::visit(Ternary const& x) { m_output << "("; visit(x.tern_cond()); m_output << " ? "; visit(x.t_branch()); m_output << " : "; visit(x.f_branch()); m_output << ")\n"; } void protoConverter::visit(Time const& x) { switch (x.t_func()) { case Time::AT: m_output << "Time.at"; break; case Time::GM: m_output << "Time.gm"; break; } m_output << "(" << (x.t_arg()% 13) << ")" << "\n"; } void protoConverter::visit(VarRef const& x) { m_output << "var_" << (static_cast(x.varnum()) % m_numLiveVars); } std::string protoConverter::FunctionToString(Function const& input) { visit(input); return m_output.str(); } nghttp2-1.68.0/third-party/mruby/oss-fuzz/PaxHeaders/ruby.proto0000644000000000000000000000013215077107276021541 xustar0030 mtime=1761382078.131420515 30 atime=1761382080.150411279 30 ctime=1761382108.381302459 nghttp2-1.68.0/third-party/mruby/oss-fuzz/ruby.proto0000644000175100017510000000647415077107276022144 0ustar00runnerrunnersyntax = "proto2"; message VarRef { required int32 varnum = 1; } message ArrType { repeated Const elements = 1; } message KVPair { required string key = 1; required string val = 2; } message HashType { repeated KVPair keyval = 1; } message StringExtNoArg { enum StrExtOp { DUMP = 0; STRIP = 1; LSTRIP = 2; RSTRIP = 3; STRIPE = 4; LSTRIPE = 5; RSTRIPE = 6; SWAPCASE = 7; SWAPCASEE = 8; SQUEEZE = 9; } required StrExtOp str_op = 1; required string str_arg = 2; } message MathConst { enum MathConstLit { PI = 0; E = 1; } required MathConstLit math_const = 1; } message Const { oneof const_oneof { uint32 int_lit = 1; bool bool_val = 4; } } message BinaryOp { enum Op { ADD = 0; SUB = 1; MUL = 2; DIV = 3; MOD = 4; XOR = 5; AND = 6; OR = 7; EQ = 8; NE = 9; LE = 10; GE = 11; LT = 12; GT = 13; RS = 14; }; required Op op = 1; required Rvalue left = 2; required Rvalue right = 3; } message Rvalue { oneof rvalue_oneof { VarRef varref = 1; Const cons = 2; BinaryOp binop = 3; } } message AssignmentStatement { required Rvalue rvalue = 2; } message IfElse { required Rvalue cond = 1; required StatementSeq if_body = 2; required StatementSeq else_body = 3; } //TODO: Add Switch statement //message Switch { // required Rvalue switch_var = 1; // repeated Rvalue cond = 2; //} message Ternary { required Rvalue tern_cond = 1; required Rvalue t_branch = 2; required Rvalue f_branch = 3; } message ObjectSpace { enum OS_methods { COUNT = 1; } required OS_methods os_func = 1; required HashType os_arg = 2; } message Time { enum T_methods { AT = 1; GM = 2; } required T_methods t_func = 1; required uint32 t_arg = 2; } message Array { enum Arr_methods { FLATTEN = 1; COMPACT = 2; FETCH = 3; FILL = 4; ROTATE = 5; ROTATE_E = 6; DELETEIF = 7; INSERT = 8; BSEARCH = 9; KEEPIF = 10; SELECT = 11; VALUES_AT = 12; BLOCK = 13; DIG = 14; SLICE = 15; PERM = 16; COMB = 17; ASSOC = 18; RASSOC = 19; } required Arr_methods arr_func = 1; required ArrType arr_arg = 2; required Rvalue val_arg = 3; } message MathType { oneof math_arg_oneof { Rvalue math_rval = 2; MathConst math_const = 3; } } message MathOps { enum Mops { CBRT = 1; COS = 2; ERF = 3; ERFC = 4; LOG = 5; LOG10 = 6; LOG2 = 7; SIN = 8; SQRT = 9; TAN = 10; } required Mops math_op = 1; required MathType math_arg = 2; } message BuiltinFuncs { oneof bifunc_oneof { ObjectSpace os = 1; Time time = 2; Array arr = 3; MathOps mops = 4; } } message Statement { oneof stmt_oneof { AssignmentStatement assignment = 1; IfElse ifelse = 2; Ternary ternary_stmt = 3; BuiltinFuncs builtins = 4; StatementSeq blockstmt = 5; } } message StatementSeq { repeated Statement statements = 1; } message Function { required StatementSeq statements = 1; } package ruby_fuzzer; nghttp2-1.68.0/third-party/mruby/oss-fuzz/PaxHeaders/config0000644000000000000000000000013215077107334020656 xustar0030 mtime=1761382108.387302442 30 atime=1761382109.795298372 30 ctime=1761382108.387302442 nghttp2-1.68.0/third-party/mruby/oss-fuzz/config/0000755000175100017510000000000015077107334021323 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/oss-fuzz/config/PaxHeaders/mruby_proto_fuzzer.options0000644000000000000000000000013215077107276026343 xustar0030 mtime=1761382078.131420515 30 atime=1761382080.150411279 30 ctime=1761382108.386302445 nghttp2-1.68.0/third-party/mruby/oss-fuzz/config/mruby_proto_fuzzer.options0000644000175100017510000000007115077107276026731 0ustar00runnerrunner[libfuzzer] close_fd_mask = 3 dict = mruby.dict fork = 1 nghttp2-1.68.0/third-party/mruby/oss-fuzz/config/PaxHeaders/mruby.dict0000644000000000000000000000013215077107276022743 xustar0030 mtime=1761382078.131420515 30 atime=1761382080.149411284 30 ctime=1761382108.385302448 nghttp2-1.68.0/third-party/mruby/oss-fuzz/config/mruby.dict0000644000175100017510000000370715077107276023342 0ustar00runnerrunnerkeyword___ENCODING__="__ENCODING__" keyword___FILE__="__FILE__" keyword___LINE__="__LINE__" keyword_BEGIN="BEGIN" keyword_END="END" keyword_alias="alias" keyword_and="and" keyword_begin="begin" keyword_break="break" keyword_case="case" keyword_class="class" keyword_def="def" keyword_do="do" keyword_else="else" keyword_elsif="elsif" keyword_end="end" keyword_ensure="ensure" keyword_false="false" keyword_for="for" keyword_if="if" keyword_in="in" keyword_module="module" keyword_next="next" keyword_nil="nil" keyword_not="not" keyword_or="or" keyword_redo="redo" keyword_rescue="rescue" keyword_retry="retry" keyword_return="return" keyword_self="self" keyword_super="super" keyword_then="then" keyword_true="true" keyword_undef="undef" keyword_unless="unless" keyword_until="until" keyword_when="when" keyword_while="while" keyword_yield="yield" operator_a=" !" operator_b=" ~" operator_c=" +" operator_d=" -" operator_e=" []" operator_f=" []=" operator_g=" *" operator_h=" /" operator_i=" %" operator_j=" +-" operator_k=" >>" operator_l=" <<" operator_m=" &" operator_n=" ^" operator_o=" |" operator_p=" <=" operator_q=" <>" operator_r=" >=" operator_s=" <=>" operator_t=" ==" operator_u=" ===" operator_v=" !=" operator_w=" =~" operator_x=" !~" operator_y=" &&" operator_z=" ||" operator_aa=" .." operator_ab=" ..." operator_ac=" ?" operator_ad=" :" operator_ae=" =" operator_af=" %=" operator_ag=" /=" operator_ah=" -=" operator_ai=" +=" operator_aj=" |=" operator_ak=" &=" operator_al=" >>=" operator_am=" <<=" operator_an=" *=" operator_ao=" &&=" operator_ap=" ||=" operator_aq=" **=" operator_ar=" ^=" operator_as=" not" operator_at=" or" operator_au=" and" operator_av=" if" operator_aw=" unless" operator_ax=" while" operator_ay=" until" operator_az=" begin" operator_ba=" end" snippet_1eq1=" 1=1" snippet_dollar=" $1" snippet_at=" @a" snippet_symbol=" :a" snippet_array=" [1,2]" snippet_block=" 1.times{|x| x}" snippet_multi=" 1*1" string_single_q=" 'a'" string_dbl_q=" \"a\"" nghttp2-1.68.0/third-party/mruby/oss-fuzz/config/PaxHeaders/mruby_fuzzer.options0000644000000000000000000000013215077107276025120 xustar0030 mtime=1761382078.131420515 30 atime=1761382080.150411279 30 ctime=1761382108.387302442 nghttp2-1.68.0/third-party/mruby/oss-fuzz/config/mruby_fuzzer.options0000644000175100017510000000011015077107276025500 0ustar00runnerrunner[libfuzzer] close_fd_mask = 3 dict = mruby.dict fork = 1 only_ascii = 1 nghttp2-1.68.0/third-party/mruby/oss-fuzz/PaxHeaders/mruby_proto_fuzzer.cpp0000644000000000000000000000013215077107276024165 xustar0030 mtime=1761382078.131420515 30 atime=1761382080.150411279 30 ctime=1761382108.383302454 nghttp2-1.68.0/third-party/mruby/oss-fuzz/mruby_proto_fuzzer.cpp0000644000175100017510000000205015077107276024552 0ustar00runnerrunner#include #include #include #include #include #include #include #include "proto_to_ruby.h" using namespace ruby_fuzzer; using namespace std; int FuzzRB(const uint8_t *Data, size_t size) { mrb_value v; mrb_state *mrb = mrb_open(); if (!mrb) return 0; char *code = (char*)malloc(size+1); if (!code) return 0; memcpy(code, Data, size); code[size] = '\0'; if (const char *dump_path = getenv("PROTO_FUZZER_DUMP_PATH")) { // With libFuzzer binary run this to generate an RB file x.rb: // PROTO_FUZZER_DUMP_PATH=x.rb ./a.out proto-input std::ofstream of(dump_path); of.write(code, size); } std::cout << "\n\n############\n" << code << "\n############\n\n"; v = mrb_load_string(mrb, code); mrb_close(mrb); free(code); return 0; } DEFINE_PROTO_FUZZER(const Function &function) { protoConverter converter; auto s = converter.FunctionToString(function); (void)FuzzRB((const uint8_t*)s.data(), s.size()); } nghttp2-1.68.0/third-party/mruby/PaxHeaders/Gemfile0000644000000000000000000000013015077107276017164 xustar0029 mtime=1761382078.09542068 29 atime=1761382080.11741143 30 ctime=1761382108.347302558 nghttp2-1.68.0/third-party/mruby/Gemfile0000644000175100017510000000017015077107276017554 0ustar00runnerrunner# frozen_string_literal: true source 'https://rubygems.org' gem 'rake' gem 'yard' gem 'yard-coderay' gem 'yard-mruby' nghttp2-1.68.0/third-party/mruby/PaxHeaders/tasks0000644000000000000000000000013215077107334016736 xustar0030 mtime=1761382108.378302468 30 atime=1761382109.795298372 30 ctime=1761382108.378302468 nghttp2-1.68.0/third-party/mruby/tasks/0000755000175100017510000000000015077107334017403 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/tasks/PaxHeaders/doc.rake0000644000000000000000000000013215077107276020431 xustar0030 mtime=1761382078.138420483 30 atime=1761382080.162411224 30 ctime=1761382108.369302494 nghttp2-1.68.0/third-party/mruby/tasks/doc.rake0000644000175100017510000000701115077107276021020 0ustar00runnerrunnerMRuby.autoload :Documentation, 'mruby/doc' desc 'generate document' task :doc => %w[doc:api doc:capi] namespace :doc do desc 'generate yard docs' task :api do begin sh "mrbdoc" rescue puts "ERROR: To generate YARD documentation, you should install the yard-coderay and yard-mruby gems." puts " $ gem install yard-coderay yard-mruby" puts "https://yardoc.org/" puts "https://rubygems.org/gems/yard-mruby" puts "https://rubygems.org/gems/yard-coderay" end end desc 'generate doxygen docs' task :capi do begin sh "doxygen Doxyfile" rescue puts "ERROR: To generate C API documentation, you should install Doxygen and Graphviz." puts "On Debian-based systems:" puts " $ sudo apt-get install doxygen graphviz" puts "On RHEL-based systems:" puts " $ sudo dnf install doxygen graphviz" puts "On macOS-based systems:" puts " $ brew install doxygen graphviz" puts "https://www.doxygen.nl/" puts "https://graphviz.org/" end end desc 'clean all built docs' task :clean => %w[clean:api clean:capi] namespace :clean do desc 'clean yard docs' task :api do rm_rf %w(doc/api .yardoc) end desc 'clean doxygen docs' task :capi do rm_rf 'doc/capi' end end namespace :view do desc 'open yard docs' task :api do if RUBY_PLATFORM.include?('darwin') sh 'open doc/api/index.html' else sh 'xdg-open doc/api/index.html' end end desc 'open doxygen docs' task :capi do if RUBY_PLATFORM.include?('darwin') sh 'open doc/capi/html/index.html' else sh 'xdg-open doc/capi/html/index.html' end end end desc 'update doc/internal/opcode.md' task 'update-opcode.md' do unless system(*%W(git --git-dir #{MRUBY_ROOT}/.git --work-tree #{MRUBY_ROOT} diff --quiet @ -- doc/internal/opcode.md)) abort <<~'ERRMESG' The file "doc/internal/opcode.md" has been modified but not committed. To avoid loss of your edits, the automatic update process has been aborted. ERRMESG end MRuby::Documentation.update_opcode_md end task 'update-index' do rev_order = %w(doc/internal/ doc/guides/ doc/) cmd = %W(git --git-dir #{MRUBY_ROOT}/.git --work-tree #{MRUBY_ROOT} ls-files -- doc/*.md) doc = IO.popen(cmd, "r") { |io| io.read.split("\n") } doc.sort_by! { |e| [-rev_order.index { |o| e.start_with?(o) }, e] } readme_path = File.join(MRUBY_ROOT, "README.md") readme = File.read(readme_path) matched = false mark_begin = "\n" mark_end = "\n" readme1 = readme.sub(/^#{mark_begin}\n\K.*(?=^\n#{mark_end})/m) { matched = true doc.each_with_object("") { |d, a| summary = File.open(File.join(MRUBY_ROOT, d)) { |f| f.each_line.first.slice(/^/, 1) } if summary summary = "Internal Implementation / #{summary}" if d.start_with?("doc/internal/") a << "- [#{summary}](#{d})\n" end } } raise "missing marker for document index in README.md" unless matched File.write(readme_path, readme1, mode: "wb") unless readme == readme1 end end # deprecated task "api_doc" => "doc:api" task "capi_doc" => "doc:capi" task "clean_doc" => "doc:clean" task "clean_api_doc" => "doc:clean:api" task "clean_capi_doc" => "doc:clean:capi" task "view_api" => "doc:view:api" task "view_capi" => "doc:view:capi" nghttp2-1.68.0/third-party/mruby/tasks/PaxHeaders/install.rake0000644000000000000000000000013115077107276021331 xustar0030 mtime=1761382078.138420483 30 atime=1761382080.162411224 29 ctime=1761382108.37430248 nghttp2-1.68.0/third-party/mruby/tasks/install.rake0000644000175100017510000000266215077107276021730 0ustar00runnerrunnerdesc "install compiled products (on host)" task :install => "install:full:host" desc "install compiled executable (on host)" task :install_bin => "install:bin:host" desc "install compiled products (all build targets)" task "install:full" desc "install compiled executable (all build targets)" task "install:bin" MRuby.each_target do |build| next if build.internal? prefix = File.join(MRuby::INSTALL_DESTDIR, build.install_prefix) exclude_filter = build.install_excludes.flatten task "install:full" => "install:full:#{build.name}" task "install:full:#{build.name}" => "install:bin:#{build.name}" do Dir.glob(File.join(build.build_dir.gsub(/[\[\{\*\?]/, "\\\0"), "{include,#{libdir_name}}/**/*")) do |path| next unless File.file? path file = path.relative_path_from(build.build_dir) next if exclude_filter.any? { |filter| filter.respond_to?(:call) ? filter.call(file) : filter.match?(file) } install_D path, File.join(prefix, file) end end task "install:bin" => "install:bin:#{build.name}" task "install:bin:#{build.name}" => "all" do Dir.glob(File.join(build.build_dir.gsub(/[\[\{\*\?]/, "\\\0"), "{bin,host-bin}/**/*")) do |path| next unless File.file? path file = path.relative_path_from(build.build_dir) next if exclude_filter.any? { |filter| filter.respond_to?(:call) ? filter.call(file) : filter.match?(file) } install_D path, File.join(prefix, file) end end end nghttp2-1.68.0/third-party/mruby/tasks/PaxHeaders/bin.rake0000644000000000000000000000013215077107276020434 xustar0030 mtime=1761382078.138420483 30 atime=1761382080.161411229 30 ctime=1761382108.376302474 nghttp2-1.68.0/third-party/mruby/tasks/bin.rake0000644000175100017510000000145515077107276021031 0ustar00runnerrunnerMRuby.each_target do |build| if build.host? && build.mrbc_build && !build.gems["mruby-bin-mrbc"] exe = build.exefile("#{build.mrbc_build.build_dir}/bin/mrbc") build.products << build.define_installer(exe) end build.bins.each{|bin| build.products << define_installer_if_needed(bin)} build.gems.each do |gem| linker_attrs = build.gems.linker_attrs(gem) gem.bins.each do |bin| exe = build.exefile("#{build.build_dir}/bin/#{bin}") objs = Dir["#{gem.dir}/tools/#{bin}/*.{c,cpp,cxx,cc}"].map do |f| build.objfile(f.pathmap("#{gem.build_dir}/tools/#{bin}/%n")) end file exe => objs.concat(build.libraries) do |t| build.linker.run t.name, t.prerequisites, *linker_attrs end build.products << define_installer_if_needed(bin) end end end nghttp2-1.68.0/third-party/mruby/tasks/PaxHeaders/presym.rake0000644000000000000000000000013215077107276021203 xustar0030 mtime=1761382078.138420483 30 atime=1761382080.162411224 30 ctime=1761382108.373302483 nghttp2-1.68.0/third-party/mruby/tasks/presym.rake0000644000175100017510000000310315077107276021570 0ustar00runnerrunnerall_prerequisites = ->(task_name, prereqs) do Rake::Task[task_name].prerequisites.each do |prereq_name| next if prereqs[prereq_name] prereqs[prereq_name] = true all_prerequisites.(Rake::Task[prereq_name].name, prereqs) end end MRuby.each_target do |build| gensym_task = task(:gensym) next unless build.presym_enabled? presym = build.presym include_dir = "#{build.build_dir}/include" build.compilers.each{|c| c.include_paths << include_dir} build.gems.each{|gem| gem.compilers.each{|c| c.include_paths << include_dir}} prereqs = {} ppps = [] build_dir = "#{build.build_dir}/" mrbc_build_dir = "#{build.mrbc_build.build_dir}/" if build.mrbc_build build.products.each{|product| all_prerequisites.(product, prereqs)} prereqs.each_key do |prereq| next unless File.extname(prereq) == build.exts.object next unless prereq.start_with?(build_dir) next if mrbc_build_dir && prereq.start_with?(mrbc_build_dir) ppp = prereq.ext(build.exts.presym_preprocessed) if Rake.application.lookup(ppp) || Rake.application.enhance_with_matching_rule(ppp) ppps << ppp end end file presym.list_path => ppps do presyms = presym.scan(ppps) current_presyms = presym.read_list if File.exist?(presym.list_path) update = presyms != current_presyms presym.write_list(presyms) if update mkdir_p presym.header_dir %w[id table].each do |type| next if !update && File.exist?(presym.send("#{type}_header_path")) presym.send("write_#{type}_header", presyms) end end gensym_task.enhance([presym.list_path]) end nghttp2-1.68.0/third-party/mruby/tasks/PaxHeaders/toolchains0000644000000000000000000000013215077107334021101 xustar0030 mtime=1761382108.368302497 30 atime=1761382109.795298372 30 ctime=1761382108.368302497 nghttp2-1.68.0/third-party/mruby/tasks/toolchains/0000755000175100017510000000000015077107334021546 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/tasks/toolchains/PaxHeaders/visualcpp.rake0000644000000000000000000000013215077107276024035 xustar0030 mtime=1761382078.138420483 30 atime=1761382080.162411224 30 ctime=1761382108.361302517 nghttp2-1.68.0/third-party/mruby/tasks/toolchains/visualcpp.rake0000644000175100017510000000340715077107276024431 0ustar00runnerrunnerMRuby::Toolchain.new(:visualcpp) do |conf, _params| compiler_flags = %w(/nologo /W3 /MD /O2 /D_CRT_SECURE_NO_WARNINGS) [conf.cc, conf.cxx].each do |compiler| if compiler == conf.cc compiler.command = ENV['CC'] || 'cl.exe' # C4013: implicit function declaration compiler.flags = [*(ENV['CFLAGS'] || compiler_flags + %w(/we4013))] else compiler.command = ENV['CXX'] || 'cl.exe' compiler.flags = [*(ENV['CXXFLAGS'] || ENV['CFLAGS'] || compiler_flags + %w(/EHs))] end compiler.defines = %w(MRB_STACK_EXTEND_DOUBLING) compiler.option_include_path = %q[/I"%s"] compiler.option_define = '/D%s' compiler.compile_options = %Q[/Zi /c /Fo"%{outfile}" %{flags} "%{infile}"] compiler.preprocess_options = %Q[/EP %{flags} "%{infile}" > "%{outfile}"] compiler.cxx_compile_flag = '/TP' compiler.cxx_exception_flag = '/EHs' end conf.linker do |linker| linker.command = ENV['LD'] || 'link.exe' linker.flags = [ENV['LDFLAGS'] || %w(/NOLOGO /DEBUG /INCREMENTAL:NO /OPT:ICF /OPT:REF)] linker.libraries = %w() linker.library_paths = %w() linker.option_library = '%s.lib' linker.option_library_path = '/LIBPATH:%s' linker.link_options = %Q[%{flags} /OUT:"%{outfile}" %{objs} %{flags_before_libraries} %{libs} %{flags_after_libraries}] end conf.archiver do |archiver| archiver.command = ENV['AR'] || 'lib.exe' archiver.archive_options = '/nologo /OUT:"%{outfile}" %{objs}' end conf.gperf do |gperf| gperf.command = 'gperf.exe' gperf.compile_options = %q[-L ANSI-C -C -j1 -i 1 -o -t -N mrb_reserved_word -k"1,3,$" "%{infile}" > "%{outfile}"] end conf.exts do |exts| exts.object = '.obj' exts.executable = '.exe' exts.library = '.lib' end conf.file_separator = '\\' end nghttp2-1.68.0/third-party/mruby/tasks/toolchains/PaxHeaders/openwrt.rake0000644000000000000000000000013215077107276023525 xustar0030 mtime=1761382078.138420483 30 atime=1761382080.162411224 30 ctime=1761382108.363302511 nghttp2-1.68.0/third-party/mruby/tasks/toolchains/openwrt.rake0000644000175100017510000000210515077107276024113 0ustar00runnerrunner# usage of environmental variables to set the # cross compiling toolchain proper MRuby::Toolchain.new(:openwrt) do |conf| [conf.cc, conf.cxx, conf.objc, conf.asm].each do |cc| if cc == conf.cxx cc.command = ENV['TARGET_CXX'] cc.flags = ENV['TARGET_CXXFLAGS'] else cc.command = ENV['TARGET_CC'] cc.flags = ENV['TARGET_CFLAGS'] end cc.option_include_path = %q[-I"%s"] cc.option_define = '-D%s' cc.compile_options = '%{flags} -MMD -o "%{outfile}" -c "%{infile}"' cc.preprocess_options = '%{flags} -o "%{outfile}" -E -P "%{infile}"' end conf.linker do |linker| linker.command = ENV['TARGET_CC'] linker.flags = ENV['TARGET_LDFLAGS'] linker.libraries = %w(m) linker.library_paths = [] linker.option_library = '-l%s' linker.option_library_path = '-L%s' linker.link_options = '%{flags} -o "%{outfile}" %{objs} %{flags_before_libraries} %{libs} %{flags_after_libraries}' end conf.archiver do |archiver| archiver.command = ENV['TARGET_AR'] archiver.archive_options = 'rs "%{outfile}" %{objs}' end end nghttp2-1.68.0/third-party/mruby/tasks/toolchains/PaxHeaders/clang.rake0000644000000000000000000000013015077107276023111 xustar0030 mtime=1761382078.138420483 30 atime=1761382080.162411224 28 ctime=1761382108.3673025 nghttp2-1.68.0/third-party/mruby/tasks/toolchains/clang.rake0000644000175100017510000000044115077107276023502 0ustar00runnerrunnerMRuby::Toolchain.new(:clang) do |conf, _params| toolchain :gcc, default_command: 'clang' [conf.cc, conf.objc, conf.asm].each do |cc| cc.flags << '-Wzero-length-array' unless ENV['CFLAGS'] end conf.cxx.flags << '-Wzero-length-array' unless ENV['CXXFLAGS'] || ENV['CFLAGS'] end nghttp2-1.68.0/third-party/mruby/tasks/toolchains/PaxHeaders/android.rake0000644000000000000000000000013215077107276023447 xustar0030 mtime=1761382078.138420483 30 atime=1761382080.162411224 30 ctime=1761382108.364302509 nghttp2-1.68.0/third-party/mruby/tasks/toolchains/android.rake0000644000175100017510000001301715077107276024041 0ustar00runnerrunnerrequire "json" class MRuby::Toolchain::Android DEFAULT_ARCH = 'armeabi-v7a' # TODO : Revise if arch should have a default DEFAULT_TOOLCHAIN = :clang DEFAULT_NDK_HOMES = %w{ /usr/local/opt/android-sdk/ndk-bundle /usr/local/opt/android-ndk ~/Android/Sdk/ndk-bundle %LOCALAPPDATA%/Android/android-sdk/ndk-bundle %LOCALAPPDATA%/Android/android-ndk %LOCALAPPDATA%/Android/Sdk/ndk/* ~/Library/Android/sdk/ndk-bundle ~/Library/Android/ndk /opt/android-ndk } TOOLCHAINS = [:clang] ARCHITECTURES = %w{ armeabi-v7a arm64-v8a x86 x86_64 } class AndroidNDKHomeNotFound < StandardError def message <<-EOM Couldn't find Android NDK Home. Set ANDROID_NDK_HOME environment variable or set :ndk_home parameter EOM end end attr_reader :params def initialize(params) @params = params end def bin(command) command = command.to_s toolchain_path.join('bin', command).to_s end def home_path @home_path ||= Pathname.new( params[:ndk_home] || ENV['ANDROID_NDK_HOME'] || DEFAULT_NDK_HOMES.find { |path| path.gsub! '%LOCALAPPDATA%', ENV['LOCALAPPDATA'] || '%LOCALAPPDATA%' path.gsub! '\\', '/' path.gsub! '~', Dir.home || '~' path.gsub!('*') do next nil unless path[-1] == "*" dirs = Dir.glob(path).collect do |d| m = d.match(/(\d+)\.(\d+)\.(\d+)$/) m ? [m[1], m[2], m[3]].collect(&:to_i) : nil end dirs.compact! dirs.sort! do |before, after| f = 0 if (f = (after.first <=> before.first)) != 0 next f elsif (f = (after[1] <=> before[1])) != 0 next f else next after.last <=> before.last end end dirs.empty? ? nil.to_s : dirs.first.join(".") end File.directory?(path) } || raise(AndroidNDKHomeNotFound) ) end def toolchain @toolchain ||= params.fetch(:toolchain){ DEFAULT_TOOLCHAIN } end def toolchain_path @toolchain_path ||= home_path.join('toolchains', 'llvm' , 'prebuilt', host_platform) end def host_platform @host_platform ||= case RUBY_PLATFORM when /cygwin|mswin|mingw|bccwin|wince|emx/i path = home_path.join('toolchains', 'llvm' , 'prebuilt', 'windows*') Dir.glob(path.to_s){ |item| next if File.file?(item) path = Pathname.new(item) break } path.basename when /x86_64-darwin/i 'darwin-x86_64' when /darwin/i 'darwin-x86' when /x86_64-linux/i 'linux-x86_64' when /linux/i 'linux-x86' else raise NotImplementedError, "Unknown host platform (#{RUBY_PLATFORM})" end end def arch @arch ||= (params[:arch] || ENV['ANDROID_ARCH'] || DEFAULT_ARCH).to_s end def armeabi_v7a_mfpu @armeabi_v7a_mfpu ||= (params[:mfpu] || 'vfpv3-d16').to_s end def armeabi_v7a_mfloat_abi @armeabi_v7a_mfloat_abi ||= (params[:mfloat_abi] || 'softfp').to_s end def sdk_version @sdk_version ||= params[:sdk_version] if !@sdk_version then # Higher SDK version will be used. json = nil File.open(home_path + "meta/platforms.json") do |f| json = JSON.load(f) end @sdk_version = json["max"] end @sdk_version end def no_warn_mismatch if %W(soft softfp).include? armeabi_v7a_mfloat_abi '' else ',--no-warn-mismatch' end end def cc case toolchain when :clang then bin('clang') end end def ar case toolchain when :clang then bin('llvm-ar') end end def ctarget flags = [] v = sdk_version case toolchain when :clang case arch when /armeabi-v7a/ then flags += %W(-target armv7a-linux-androideabi#{v} -mfpu=#{armeabi_v7a_mfpu} -mfloat-abi=#{armeabi_v7a_mfloat_abi}) when /arm64-v8a/ then flags += %W(-target aarch64-linux-android#{v}) when /x86_64/ then flags += %W(-target x86_64-linux-android#{v}) when /x86/ then flags += %W(-target i686-linux-android#{v}) end end flags end def cflags flags = [] case RUBY_PLATFORM when /mswin|mingw|win32/ # Build for Android don't need window flag flags += %W(-U_WIN32 -U_WIN64) end flags += %W(-MMD -MP -D__android__ -DANDROID) flags += ctarget flags += %W(-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes) flags end def ldflags flags = [] flags end def ldflags_before_libraries flags = [] v = sdk_version case toolchain when :clang case arch when /armeabi-v7a/ then flags += %W(-target armv7-none-linux-androideabi#{v} -Wl,--fix-cortex-a8#{no_warn_mismatch}) when /arm64-v8a/ then flags += %W(-target aarch64-none-linux-android#{v}) when /x86_64/ then flags += %W(-target x86_64-none-linux-android#{v}) when /x86/ then flags += %W(-target i686-none-linux-android#{v}) end end flags += %W(-no-canonical-prefixes) flags end end MRuby::Toolchain.new(:android) do |conf, params| android = MRuby::Toolchain::Android.new(params) toolchain android.toolchain [conf.cc, conf.cxx, conf.objc, conf.asm].each do |cc| cc.command = android.cc cc.flags = android.cflags end conf.archiver.command = android.ar conf.linker.command = android.cc conf.linker.flags = android.ldflags conf.linker.flags_before_libraries = android.ldflags_before_libraries end nghttp2-1.68.0/third-party/mruby/tasks/toolchains/PaxHeaders/gcc.rake0000644000000000000000000000013215077107276022563 xustar0030 mtime=1761382078.138420483 30 atime=1761382080.162411224 30 ctime=1761382108.365302506 nghttp2-1.68.0/third-party/mruby/tasks/toolchains/gcc.rake0000644000175100017510000000526215077107276023160 0ustar00runnerrunnerMRuby::Toolchain.new(:gcc) do |conf, params| default_command = params[:default_command] || 'gcc' compiler_flags = %w(-g -O3 -Wall -Wundef) c_mandatory_flags = %w(-std=gnu99) cxx_invalid_flags = %w(-Werror-implicit-function-declaration) compile_opt = '%{flags} -o "%{outfile}" "%{infile}"' [conf.cc, conf.objc, conf.asm, conf.cxx].each do |compiler| if compiler == conf.cxx compiler.command = ENV['CXX'] || conf.cc.command.sub(/g\Kcc|$/, '++') compiler.flags = [ENV['CXXFLAGS'] || ENV['CFLAGS'] || compiler_flags] else compiler.command = ENV['CC'] || default_command compiler.flags = [c_mandatory_flags, ENV['CFLAGS'] || [compiler_flags, cxx_invalid_flags, %w(-Wwrite-strings)]] end compiler.option_include_path = %q[-I"%s"] compiler.option_define = '-D%s' compiler.compile_options = "-MMD -c #{compile_opt}" compiler.preprocess_options = "-E -P #{compile_opt}" compiler.cxx_compile_flag = '-x c++ -std=gnu++03' compiler.cxx_exception_flag = '-fexceptions' compiler.cxx_invalid_flags = c_mandatory_flags + cxx_invalid_flags def compiler.setup_debug(conf) self.flags << %w(-g3 -O0) end end conf.linker do |linker| linker.command = ENV['LD'] || ENV['CXX'] || ENV['CC'] || default_command linker.flags = [ENV['LDFLAGS'] || %w()] if ENV['OS'] == 'Windows_NT' linker.libraries = [] else linker.libraries = %w(m) end linker.library_paths = [] linker.option_library = '-l%s' linker.option_library_path = '-L%s' linker.link_options = '%{flags} -o "%{outfile}" %{objs} %{flags_before_libraries} %{libs} %{flags_after_libraries}' end [[conf.cc, 'c'], [conf.cxx, 'c++']].each do |cc, lang| cc.instance_variable_set :@header_search_language, lang def cc.header_search_paths if @header_search_command != command result = `echo | #{build.filename command} -x#{@header_search_language} -Wp,-v - -fsyntax-only 2>&1` return include_paths if $?.exitstatus != 0 @frameworks = [] @header_search_paths = result.lines.map { |v| framework = v.match(/^ (.*)(?: \(framework directory\))$/) if framework @frameworks << framework[1] next nil end v.match(/^ (.*)$/) }.compact.map { |v| v[1] }.select { |v| File.directory? v } @header_search_paths += include_paths @header_search_command = command end @header_search_paths end end def conf.enable_sanitizer(*opts) fail 'sanitizer already set' if @sanitizer_list @sanitizer_list = opts flg = "-fsanitize=#{opts.join ','}" [self.cc, self.cxx, self.linker].each{|cmd| cmd.flags << flg } end end nghttp2-1.68.0/third-party/mruby/tasks/toolchains/PaxHeaders/emscripten.rake0000644000000000000000000000013215077107276024200 xustar0030 mtime=1761382078.138420483 30 atime=1761382080.162411224 30 ctime=1761382108.368302497 nghttp2-1.68.0/third-party/mruby/tasks/toolchains/emscripten.rake0000644000175100017510000000156015077107276024572 0ustar00runnerrunnerMRuby::Toolchain.new(:emscripten) do |conf| toolchain :clang # See: # - https://emscripten.org/docs/tools_reference/emcc.html # - https://emscripten.org/docs/tools_reference/settings_reference.html # - https://github.com/emscripten-core/emscripten/blob/main/src/settings.js compile_and_link_flags = [ ] compile_flags = [ *compile_and_link_flags, '-Wno-unused-but-set-variable', ] link_flags = [ *compile_and_link_flags, ] conf.cc do |cc| cc.command = 'emcc' cc.flags.concat(compile_flags) unless ENV['CFLAGS'] end conf.cxx do |cxx| cxx.command = 'em++' cxx.flags.concat(compile_flags) unless ENV['CXXFLAGS'] || ENV['CFLAGS'] end conf.linker do |linker| linker.command = 'emcc' linker.flags.concat(link_flags) unless ENV['LDFLAGS'] end conf.archiver do |archiver| archiver.command = 'emar' end end nghttp2-1.68.0/third-party/mruby/tasks/PaxHeaders/core.rake0000644000000000000000000000013115077107276020613 xustar0030 mtime=1761382078.138420483 30 atime=1761382080.162411224 29 ctime=1761382108.36030252 nghttp2-1.68.0/third-party/mruby/tasks/core.rake0000644000175100017510000000053215077107276021204 0ustar00runnerrunneras_cxx_srcs = %w[vm error gc].map{|name| "#{MRUBY_ROOT}/src/#{name}.c"} MRuby.each_target do objs = Dir.glob("#{MRUBY_ROOT}/src/*.c").map do |src| if cxx_exception_enabled? && as_cxx_srcs.include?(src) compile_as_cxx(src) else objfile(src.pathmap("#{build_dir}/src/%n")) end end self.libmruby_core_objs << objs end nghttp2-1.68.0/third-party/mruby/tasks/PaxHeaders/mrblib.rake0000644000000000000000000000013215077107276021133 xustar0030 mtime=1761382078.138420483 30 atime=1761382080.162411224 30 ctime=1761382108.378302468 nghttp2-1.68.0/third-party/mruby/tasks/mrblib.rake0000644000175100017510000000223215077107276021522 0ustar00runnerrunnerMRuby.each_target do next unless libmruby_enabled? src = "#{build_dir}/mrblib/mrblib.c" rbfiles = Dir["#{MRUBY_ROOT}/mrblib/*.rb"].sort! self.libmruby_objs << objfile(src.ext) file src => [mrbcfile, __FILE__, *rbfiles] do |t| if presym_enabled? cdump = true suffix = "proc" else cdump = false suffix = "irep" end mkdir_p File.dirname(t.name) File.open(t.name, 'w') do |f| _pp "GEN", "mrblib/*.rb", "#{t.name.relative_path}" f.puts %Q[/*] f.puts %Q[ * This file is loading the mrblib] f.puts %Q[ *] f.puts %Q[ * IMPORTANT:] f.puts %Q[ * This file was generated!] f.puts %Q[ * All manual changes will get lost.] f.puts %Q[ */] unless presym_enabled? f.puts %Q[#include ] f.puts %Q[#include ] end mrbc.run f, rbfiles, "mrblib_#{suffix}", cdump: cdump, static: true f.puts %Q[void] f.puts %Q[mrb_init_mrblib(mrb_state *mrb)] f.puts %Q[{] f.puts %Q[ mrblib_#{suffix}_init_syms(mrb);] if cdump f.puts %Q[ mrb_load_#{suffix}(mrb, mrblib_#{suffix});] f.puts %Q[}] end end end nghttp2-1.68.0/third-party/mruby/tasks/PaxHeaders/libmruby.rake0000644000000000000000000000013215077107276021511 xustar0030 mtime=1761382078.138420483 30 atime=1761382080.162411224 30 ctime=1761382108.359302523 nghttp2-1.68.0/third-party/mruby/tasks/libmruby.rake0000644000175100017510000000636315077107276022111 0ustar00runnerrunnerMRuby.each_target do file libmruby_core_static => libmruby_core_objs.flatten do |t| archiver.run t.name, t.prerequisites end products << libmruby_core_static next unless libmruby_enabled? copy_headers_task = "expose_header_files:#{self.name}" file libmruby_static => libmruby_objs.flatten do |t| Rake::Task[copy_headers_task].invoke archiver.run t.name, t.prerequisites end task copy_headers_task do |t| # Since header files may be generated dynamically and it is hard to know all of them, # the task is executed depending on when libmruby.a is generated. gemsbasedir = File.join(build_dir, "include/mruby/gems") dirmap = { MRUBY_ROOT => build_dir } gems.each { |g| dirmap[g.dir] = File.join(gemsbasedir, g.name) dirmap[g.build_dir] = File.join(gemsbasedir, g.name) } dirs = each_header_files.to_a dirs.uniq! dirs.replace_prefix_by(dirmap).zip(dirs).each do |dest, src| if File.mtime(src).to_i > (File.mtime(dest).to_i rescue 0) mkpath File.dirname(dest) cp src, dest end end end file "#{build_dir}/#{libdir_name}/libmruby.flags.mak" => [__FILE__, libmruby_static] do |t| mkdir_p File.dirname t.name open(t.name, 'w') do |f| f.puts <<~FLAGS_MAKE # GNU make is required to use this file. MRUBY_PACKAGE_DIR_GNU := $(shell dirname "$(lastword $(MAKEFILE_LIST))") MRUBY_PACKAGE_DIR != dirname "$(MRUBY_PACKAGE_DIR_GNU)" FLAGS_MAKE [ [cc, "MRUBY_CC", "MRUBY_CFLAGS"], [cxx, "MRUBY_CXX", "MRUBY_CXXFLAGS"], [asm, "MRUBY_AS", "MRUBY_ASFLAGS"], [objc, "MRUBY_OBJC", "MRUBY_OBJCFLAGS"] ].each do |cc, cmd, flags| incpaths = cc.include_paths.dup dirmaps = { MRUBY_ROOT => "$(MRUBY_PACKAGE_DIR)", build_dir => "$(MRUBY_PACKAGE_DIR)" } gems.each do |g| incpaths.concat g.export_include_paths dirmaps[g.dir] = "$(MRUBY_PACKAGE_DIR)/include/mruby/gems/#{g.name}" dirmaps[g.build_dir] = "$(MRUBY_PACKAGE_DIR)/include/mruby/gems/#{g.name}" end modcc = cc.clone modcc.include_paths = incpaths.replace_prefix_by(dirmaps).uniq f.puts "#{cmd} = #{cc.command}" f.puts "#{flags} = #{modcc.all_flags}" end f.puts "MRUBY_LD = #{linker.command}" libgems = gems.reject{|g| g.bin?} gem_flags = libgems.map {|g| g.linker.flags } gem_library_paths = libgems.map {|g| g.linker.library_paths } f.puts "MRUBY_LDFLAGS = #{linker.all_flags(gem_library_paths, gem_flags)} #{linker.option_library_path % "$(MRUBY_PACKAGE_DIR)/#{libdir_name}"}" gem_flags_before_libraries = libgems.map {|g| g.linker.flags_before_libraries } f.puts "MRUBY_LDFLAGS_BEFORE_LIBS = #{[linker.flags_before_libraries, gem_flags_before_libraries].flatten.join(' ')}" gem_libraries = libgems.map {|g| g.linker.libraries } libmruby = (toolchains.find { |e| e == "visualcpp" }) ? "libmruby" : "mruby" f.puts "MRUBY_LIBS = #{linker.option_library % libmruby} #{linker.library_flags(gem_libraries)}" f.puts "MRUBY_LIBMRUBY_PATH = #{libmruby_static.replace_prefix_by(build_dir => "$(MRUBY_PACKAGE_DIR)")}" end end products << libmruby_static end nghttp2-1.68.0/third-party/mruby/tasks/PaxHeaders/mrbgems.rake0000644000000000000000000000013215077107276021320 xustar0030 mtime=1761382078.138420483 30 atime=1761382080.162411224 30 ctime=1761382108.372302486 nghttp2-1.68.0/third-party/mruby/tasks/mrbgems.rake0000644000175100017510000001355615077107276021722 0ustar00runnerrunnerMRuby.each_target do active_gems_txt = "#{build_dir}/mrbgems/active_gems.txt" if enable_gems? # set up all gems gems.each(&:setup) gems.check self # loader all gems self.libmruby_objs << objfile("#{build_dir}/mrbgems/gem_init") file objfile("#{build_dir}/mrbgems/gem_init") => ["#{build_dir}/mrbgems/gem_init.c", "#{build_dir}/LEGAL"] file "#{build_dir}/mrbgems/gem_init.c" => [active_gems_txt, MRUBY_CONFIG, __FILE__] do |t| mkdir_p "#{build_dir}/mrbgems" open(t.name, 'w') do |f| gem_func_gems = gems.select { |g| g.generate_functions } gem_func_decls = '' gem_funcs = '' gem_func_gems.each do |g| init = "GENERATED_TMP_mrb_#{g.funcname}_gem_init" final = "GENERATED_TMP_mrb_#{g.funcname}_gem_final" gem_func_decls << "void #{init}(mrb_state*);\n" \ "void #{final}(mrb_state*);\n" gem_funcs << " { #{init}, #{final} },\n" end f.puts %Q[/*] f.puts %Q[ * This file contains a list of all] f.puts %Q[ * initializing methods which are] f.puts %Q[ * necessary to bootstrap all gems.] f.puts %Q[ *] f.puts %Q[ * This file was generated by mruby/#{__FILE__.relative_path_from(MRUBY_ROOT)}.] f.puts %Q[ *] f.puts %Q[ * IMPORTANT:] f.puts %Q[ * This file was generated!] f.puts %Q[ * All manual changes will get lost.] f.puts %Q[ */] f.puts %Q[] f.puts %Q[#include ] f.puts %Q[#include ] f.puts %Q[#include ] f.puts %Q[] unless gem_funcs.empty? f.write gem_func_decls f.puts %Q[] f.puts %Q[static const struct {] f.puts %Q[ void (*init)(mrb_state*);] f.puts %Q[ void (*final)(mrb_state*);] f.puts %Q[} gem_funcs[] = {] f.write gem_funcs f.puts %Q[};] f.puts %Q[] f.puts %Q[#define NUM_GEMS ((int)(sizeof(gem_funcs) / sizeof(gem_funcs[0])))] f.puts %Q[] f.puts %Q[struct final_mrbgems {] f.puts %Q[ int i;] f.puts %Q[ int ai;] f.puts %Q[};] f.puts %Q[] f.puts %Q[static mrb_value] f.puts %Q[final_mrbgems_body(mrb_state *mrb, void *ud) {] f.puts %Q[ struct final_mrbgems *p = (struct final_mrbgems*)ud;] f.puts %Q[ for (; p->i >= 0; p->i--) {] f.puts %Q[ gem_funcs[p->i].final(mrb);] f.puts %Q[ mrb_gc_arena_restore(mrb, p->ai);] f.puts %Q[ }] f.puts %Q[ return mrb_nil_value();] f.puts %Q[}] f.puts %Q[] f.puts %Q[static void] f.puts %Q[mrb_final_mrbgems(mrb_state *mrb) {] f.puts %Q[ struct final_mrbgems a = { NUM_GEMS - 1, mrb_gc_arena_save(mrb) };] f.puts %Q[ for (; a.i >= 0; a.i--) {] f.puts %Q[ mrb_protect_error(mrb, final_mrbgems_body, &a, NULL);] f.puts %Q[ mrb_gc_arena_restore(mrb, a.ai);] f.puts %Q[ }] f.puts %Q[}] f.puts %Q[] end f.puts %Q[void] f.puts %Q[mrb_init_mrbgems(mrb_state *mrb) {] unless gem_funcs.empty? f.puts %Q[ int ai = mrb_gc_arena_save(mrb);] f.puts %Q[ for (int i = 0; i < NUM_GEMS; i++) {] f.puts %Q[ gem_funcs[i].init(mrb);] f.puts %Q[ mrb_gc_arena_restore(mrb, ai);] f.puts %Q[ mrb_vm_ci_env_clear(mrb, mrb->c->cibase);] f.puts %Q[ if (mrb->exc) {] f.puts %Q[ mrb_exc_raise(mrb, mrb_obj_value(mrb->exc));] f.puts %Q[ }] f.puts %Q[ }] f.puts %Q[ mrb_state_atexit(mrb, mrb_final_mrbgems);] end f.puts %Q[}] end end end file active_gems_txt => :generate_active_gems_txt desc "generate the active gems text files" task :generate_active_gems_txt do |t| def t.timestamp; Time.at(0) end active_gems = gems.sort_by(&:name).inject(""){|s, g| s << "#{g.name}\n"} if !File.exist?(active_gems_txt) || File.read(active_gems_txt) != active_gems mkdir_p File.dirname(active_gems_txt) File.write(active_gems_txt, active_gems) end end # legal documents file "#{build_dir}/LEGAL" => [MRUBY_CONFIG, __FILE__] do |t| mkdir_p File.dirname t.name open(t.name, 'w+') do |f| f.puts < [bm_file, dat_dir, mruby_bin] do |task| print bm_name puts "..." data = (0...MRuby::BENCHMARK_REPEAT).map do |n| str = %x{(time -p #{mruby_bin} #{bm_file}) 2>&1 >/dev/null} str.scan(/\d+\.\d+$/).map(&:to_f) # [real, user, sys] end File.open(task.name, "w") do |f| data = data.map {|_,r,s| (r + s) / 2.0} min = data.min max = data.max avg = data.inject(&:+) / data.size f.puts "#{bm_name.gsub('_', '-')} #{avg} #{min} #{max}" end end end end file plot_file => $dat_files do plot end desc "run benchmark tests" task :benchmark => plot_file do plot end nghttp2-1.68.0/third-party/mruby/tasks/PaxHeaders/test.rake0000644000000000000000000000013215077107276020643 xustar0030 mtime=1761382078.138420483 30 atime=1761382080.162411224 30 ctime=1761382108.377302471 nghttp2-1.68.0/third-party/mruby/tasks/test.rake0000644000175100017510000000401015077107276021226 0ustar00runnerrunnerdesc "build and run all mruby tests" task :test => "test:build" do Rake::Task["test:run"].invoke end namespace :test do |test_ns| desc "build and run library tests" task :lib => "build:lib" do test_ns["run:lib"].invoke end desc "build and run command binaries tests" task :bin => "rake:all" do test_ns["run:bin"].invoke end desc "build all mruby tests" task :build => "build:lib" namespace :build do |test_build_ns| desc "build library tests" task :lib => "rake:all" do MRuby.each_target{|build| build.gem(core: 'mruby-test')} test = test_build_ns["lib_without_loading_gem"] test.invoke if test end end desc "run all mruby tests" task :run namespace :run do desc "run library tests" task :lib desc "run command binaries tests" task :bin end desc "run all mruby tests serially" task "run:serial" => "build" do Rake::Task["test:run"].prerequisite_tasks.each(&:invoke) end desc "run library tests serially" task "run:serial:bin" => "build:bin" do Rake::Task["test:run:lib"].prerequisite_tasks.each(&:invoke) end desc "run command binaries tests serially" task "run:serial:lib" => "build:lib" do Rake::Task["test:run:bin"].prerequisite_tasks.each(&:invoke) end end MRuby.each_target do |build| if build.test_enabled? t = task "test:build:lib_without_loading_gem:#{build.name}" do gem = build.gems["mruby-test"] gem.setup gem.setup_compilers Rake::Task[build.define_installer_if_needed("mrbtest")].invoke end task "test:build:lib_without_loading_gem" => t t = task "test:run:lib:#{build.name}" do build.run_test end task "test:run" => t task "test:run:lib" => t end if build.bintest_enabled? t = task "test:run:bin:#{build.name}" do build.run_bintest end task "test:run" => t task "test:run:bin" => t end end task :clean do host = MRuby.targets["host"] rm_f host.exefile("#{host.class.install_dir}/mrbtest") if host && host.test_enabled? end nghttp2-1.68.0/third-party/mruby/PaxHeaders/mrblib0000644000000000000000000000013215077107334017060 xustar0030 mtime=1761382108.550301971 30 atime=1761382109.796298369 30 ctime=1761382108.550301971 nghttp2-1.68.0/third-party/mruby/mrblib/0000755000175100017510000000000015077107334017525 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrblib/PaxHeaders/enum.rb0000644000000000000000000000013115077107276020432 xustar0029 mtime=1761382078.13042052 30 atime=1761382080.149411284 30 ctime=1761382108.549301974 nghttp2-1.68.0/third-party/mruby/mrblib/enum.rb0000644000175100017510000001651015077107276021026 0ustar00runnerrunner## # Enumerable # # The Enumerable mixin provides collection classes with # several traversal and searching methods, and with the ability to # sort. The class must provide a method `each`, which # yields successive members of the collection. If # {Enumerable#max}, {#min}, or # {#sort} is used, the objects in the collection must also # implement a meaningful `<=>` operator, as these methods # rely on an ordering between members of the collection. # # ISO 15.3.2 module Enumerable NONE = Object.new ## # Call the given block for each element # which is yield by +each+. Return false # if one block value is false. Otherwise # return true. If no block is given and # +self+ is false return false. # # ISO 15.3.2.2.1 def all?(&block) if block self.each{|*val| return false unless block.call(*val)} else self.each{|*val| return false unless val.__svalue} end true end ## # Call the given block for each element # which is yield by +each+. Return true # if one block value is true. Otherwise # return false. If no block is given and # +self+ is true object return true. # # ISO 15.3.2.2.2 def any?(&block) if block self.each{|*val| return true if block.call(*val)} else self.each{|*val| return true if val.__svalue} end false end ## # Call the given block for each element # which is yield by +each+. Append all # values of each block together and # return this value. # # ISO 15.3.2.2.3 def collect(&block) return to_enum :collect unless block ary = [] self.each{|*val| ary.push(block.call(*val))} ary end ## # Return the first element for which # value from the block is true. If no # object matches, calls +ifnone+ and # returns its result. Otherwise returns # +nil+. # # ISO 15.3.2.2.4 def detect(ifnone=nil, &block) return to_enum :detect, ifnone unless block self.each{|*val| if block.call(*val) return val.__svalue end } ifnone.call unless ifnone.nil? end ## # Call the given block for each element # which is yield by +each+. Pass an # index to the block which starts at 0 # and increase by 1 for each element. # # ISO 15.3.2.2.5 def each_with_index(&block) return to_enum :each_with_index unless block i = 0 self.each{|*val| block.call(val.__svalue, i) i += 1 } self end ## # Return an array of all elements which # are yield by +each+. # # ISO 15.3.2.2.6 def entries ary = [] self.each{|*val| # __svalue is an internal method ary.push val.__svalue } ary end ## # Alias for find # # ISO 15.3.2.2.7 alias find detect ## # Call the given block for each element # which is yield by +each+. Return an array # which contains all elements whose block # value was true. # # ISO 15.3.2.2.8 def find_all(&block) return to_enum :find_all unless block ary = [] self.each{|*val| ary.push(val.__svalue) if block.call(*val) } ary end ## # Call the given block for each element # which is yield by +each+ and which return # value was true when invoking === with # +pattern+. Return an array with all # elements or the respective block values. # # ISO 15.3.2.2.9 def grep(pattern, &block) ary = [] self.each{|*val| sv = val.__svalue if pattern === sv ary.push((block)? block.call(*val): sv) end } ary end ## # Return true if at least one element which # is yield by +each+ returns a true value # by invoking == with +obj+. Otherwise return # false. # # ISO 15.3.2.2.10 def include?(obj) self.each{|*val| return true if val.__svalue == obj } false end ## # Call the given block for each element # which is yield by +each+. Return value # is the sum of all block values. Pass # to each block the current sum and the # current element. # # ISO 15.3.2.2.11 def inject(*args, &block) raise ArgumentError, "too many arguments" if args.size > 2 if Symbol === args[-1] sym = args[-1] block = ->(x,y){x.__send__(sym,y)} args.pop end if args.empty? flag = true # no initial argument result = nil else flag = false result = args[0] end self.each{|*val| val = val.__svalue if flag # push first element as initial flag = false result = val else result = block.call(result, val) end } result end alias reduce inject ## # Alias for collect # # ISO 15.3.2.2.12 alias map collect ## # Return the maximum value of all elements # yield by +each+. If no block is given <=> # will be invoked to define this value. If # a block is given it will be used instead. # # ISO 15.3.2.2.13 def max(&block) flag = true # 1st element? result = nil self.each{|*val| val = val.__svalue if flag # 1st element result = val flag = false else if block result = val if block.call(val, result) > 0 else result = val if (val <=> result) > 0 end end } result end ## # Return the minimum value of all elements # yield by +each+. If no block is given <=> # will be invoked to define this value. If # a block is given it will be used instead. # # ISO 15.3.2.2.14 def min(&block) flag = true # 1st element? result = nil self.each{|*val| val = val.__svalue if flag # 1st element result = val flag = false else if block result = val if block.call(val, result) < 0 else result = val if (val <=> result) < 0 end end } result end ## # Alias for include? # # ISO 15.3.2.2.15 alias member? include? ## # Call the given block for each element # which is yield by +each+. Return an # array which contains two arrays. The # first array contains all elements # whose block value was true. The second # array contains all elements whose # block value was false. # # ISO 15.3.2.2.16 def partition(&block) return to_enum :partition unless block ary_T = [] ary_F = [] self.each{|*val| if block.call(*val) ary_T.push(val.__svalue) else ary_F.push(val.__svalue) end } [ary_T, ary_F] end ## # Call the given block for each element # which is yield by +each+. Return an # array which contains only the elements # whose block value was false. # # ISO 15.3.2.2.17 def reject(&block) return to_enum :reject unless block ary = [] self.each{|*val| ary.push(val.__svalue) unless block.call(*val) } ary end ## # Alias for find_all. # # ISO 15.3.2.2.18 alias select find_all ## # Return a sorted array of all elements # which are yield by +each+. If no block # is given <=> will be invoked on each # element to define the order. Otherwise # the given block will be used for # sorting. # # ISO 15.3.2.2.19 def sort(&block) self.map{|*val| val.__svalue}.sort(&block) end ## # Alias for entries. # # ISO 15.3.2.2.20 alias to_a entries # redefine #hash 15.3.1.3.15 def hash h = 12347 i = 0 self.each do |e| h = __update_hash(h, i, e.hash) i += 1 end h end end nghttp2-1.68.0/third-party/mruby/mrblib/PaxHeaders/hash.rb0000644000000000000000000000013215077107276020412 xustar0030 mtime=1761382078.131420515 30 atime=1761382080.149411284 30 ctime=1761382108.541301997 nghttp2-1.68.0/third-party/mruby/mrblib/hash.rb0000644000175100017510000001626215077107276021011 0ustar00runnerrunner## # Hash # # ISO 15.2.13 class Hash ## # Hash is enumerable # # ISO 15.2.13.3 include Enumerable ## # call-seq: # hash == object -> true or false # # Equality---Two hashes are equal if they each contain the same number # of keys and if each key-value pair is equal to (according to # Object#==) the corresponding elements in the other # hash. # # ISO 15.2.13.4.1 def ==(hash) return true if self.equal?(hash) unless Hash === hash return false end return false if self.size != hash.size self.each do |k,v| return false unless hash.key?(k) return false unless self[k] == hash[k] end return true end ## # call-seq: # hash.eql? object -> true or false # # Returns true if hash and other are # both hashes with the same content compared by eql?. # def eql?(hash) return true if self.equal?(hash) unless Hash === hash return false end return false if self.size != hash.size self.each do |k,v| return false unless hash.key?(k) return false unless self[k].eql?(hash[k]) end return true end ## # call-seq: # hash.delete(key) -> value or nil # hash.delete(key) {|key| ... } -> object # # Delete the element with the key +key+. # Return the value of the element if +key+ # was found. Return nil if nothing was # found. If a block is given, call the # block with the value of the element. # # ISO 15.2.13.4.8 def delete(key, &block) if block && !self.has_key?(key) return block.call(key) end self.__delete(key) end ## # call-seq: # hsh.each {| key, value | block } -> hsh # hsh.each_pair {| key, value | block } -> hsh # hsh.each -> an_enumerator # hsh.each_pair -> an_enumerator # # Calls the given block for each element of +self+ # and pass the key and value of each element. # # If no block is given, an enumerator is returned instead. # # h = { "a" => 100, "b" => 200 } # h.each {|key, value| puts "#{key} is #{value}" } # # produces: # # a is 100 # b is 200 # # ISO 15.2.13.4.9 def each(&block) return to_enum :each unless block keys = self.keys vals = self.values len = self.size i = 0 while i < len block.call [keys[i], vals[i]] i += 1 end self end ## # call-seq: # hsh.each_key {| key | block } -> hsh # hsh.each_key -> an_enumerator # # Calls the given block for each element of +self+ # and pass the key of each element. # # If no block is given, an enumerator is returned instead. # # h = { "a" => 100, "b" => 200 } # h.each_key {|key| puts key } # # produces: # # a # b # # ISO 15.2.13.4.10 def each_key(&block) return to_enum :each_key unless block self.keys.each{|k| block.call(k)} self end ## # call-seq: # hsh.each_value {| value | block } -> self # hsh.each_value -> an_enumerator # # Calls the given block with each value; returns +self+: # # If no block is given, an enumerator is returned instead. # # h = { "a" => 100, "b" => 200 } # h.each_value {|value| puts value } # # produces: # # 100 # 200 # # ISO 15.2.13.4.11 def each_value(&block) return to_enum :each_value unless block self.values.each{|v| block.call(v)} self end ## # call-seq: # hsh.merge(other_hash..) -> hsh # hsh.merge(other_hash..){|key, oldval, newval| block} -> hsh # # Returns the new \Hash formed by merging each of +other_hashes+ # into a copy of +self+. # # Each argument in +other_hashes+ must be a \Hash. # Adds the contents of _other_hash_ to _hsh_. If no block is specified, # entries with duplicate keys are overwritten with the values from # _other_hash_, otherwise the value of each duplicate key is determined by # calling the block with the key, its value in _hsh_ and its value in # _other_hash_. # # Example: # h = {foo: 0, bar: 1, baz: 2} # h1 = {bat: 3, bar: 4} # h2 = {bam: 5, bat:6} # h3 = h.merge(h1, h2) { |key, old_value, new_value| old_value + new_value } # h3 # => {:foo=>0, :bar=>5, :baz=>2, :bat=>9, :bam=>5} # # ISO 15.2.13.4.22 def merge(*others, &block) h = self.dup return h.__merge(*others) unless block i=0; len=others.size while i hsh or nil # hsh.reject! -> an_enumerator # # Equivalent to Hash#delete_if, but returns # nil if no changes were made. # # 1.8/1.9 Hash#reject! returns Hash; ISO says nothing. # def reject!(&block) return to_enum :reject! unless block keys = [] self.each{|k,v| if block.call([k, v]) keys.push(k) end } return nil if keys.size == 0 keys.each{|k| self.delete(k) } self end ## # call-seq: # hsh.reject {|key, value| block} -> a_hash # hsh.reject -> an_enumerator # # Returns a new hash consisting of entries for which the block returns false. # # If no block is given, an enumerator is returned instead. # # h = { "a" => 100, "b" => 200, "c" => 300 } # h.reject {|k,v| k < "b"} #=> {"b" => 200, "c" => 300} # h.reject {|k,v| v > 100} #=> {"a" => 100} # # 1.8/1.9 Hash#reject returns Hash; ISO says nothing. # def reject(&block) return to_enum :reject unless block h = {} self.each{|k,v| unless block.call([k, v]) h[k] = v end } h end ## # call-seq: # hsh.select! {| key, value | block } -> hsh or nil # hsh.select! -> an_enumerator # # Equivalent to Hash#keep_if, but returns # nil if no changes were made. # # 1.9 Hash#select! returns Hash; ISO says nothing. # def select!(&block) return to_enum :select! unless block keys = [] self.each{|k,v| unless block.call([k, v]) keys.push(k) end } return nil if keys.size == 0 keys.each{|k| self.delete(k) } self end ## # call-seq: # hsh.select {|key, value| block} -> a_hash # hsh.select -> an_enumerator # # Returns a new hash consisting of entries for which the block returns true. # # If no block is given, an enumerator is returned instead. # # h = { "a" => 100, "b" => 200, "c" => 300 } # h.select {|k,v| k > "a"} #=> {"b" => 200, "c" => 300} # h.select {|k,v| v < 200} #=> {"a" => 100} # # 1.9 Hash#select returns Hash; ISO says nothing # def select(&block) return to_enum :select unless block h = {} self.each{|k,v| if block.call([k, v]) h[k] = v end } h end end nghttp2-1.68.0/third-party/mruby/mrblib/PaxHeaders/array.rb0000644000000000000000000000013115077107276020604 xustar0029 mtime=1761382078.13042052 30 atime=1761382080.149411284 30 ctime=1761382108.544301988 nghttp2-1.68.0/third-party/mruby/mrblib/array.rb0000644000175100017510000000353015077107276021176 0ustar00runnerrunner## # Array # # ISO 15.2.12 class Array ## # call-seq: # array.each {|element| ... } -> self # array.each -> Enumerator # # Calls the given block for each element of +self+ # and pass the respective element. # # ISO 15.2.12.5.10 def each(&block) return to_enum :each unless block idx = 0 while idx < length block.call(self[idx]) idx += 1 end self end ## # call-seq: # array.each_index {|index| ... } -> self # array.each_index -> Enumerator # # Calls the given block for each element of +self+ # and pass the index of the respective element. # # ISO 15.2.12.5.11 def each_index(&block) return to_enum :each_index unless block idx = 0 while idx < length block.call(idx) idx += 1 end self end ## # call-seq: # array.collect! {|element| ... } -> self # array.collect! -> new_enumerator # # Calls the given block for each element of +self+ # and pass the respective element. Each element will # be replaced by the resulting values. # # ISO 15.2.12.5.7 def collect!(&block) return to_enum :collect! unless block idx = 0 len = size while idx < len self[idx] = block.call(self[idx]) idx += 1 end self end ## # call-seq: # array.map! {|element| ... } -> self # array.map! -> new_enumerator # # Alias for collect! # # ISO 15.2.12.5.20 alias map! collect! ## # call-seq: # array.sort -> new_array # array.sort {|a, b| ... } -> new_array # # Returns a new Array whose elements are those from +self+, sorted. def sort(&block) self.dup.sort!(&block) end ## # call-seq: # array.to_a -> self # # Returns self, no need to convert. def to_a self end alias entries to_a ## # Array is enumerable # ISO 15.2.12.3 include Enumerable end nghttp2-1.68.0/third-party/mruby/mrblib/PaxHeaders/symbol.rb0000644000000000000000000000013215077107276020774 xustar0030 mtime=1761382078.131420515 30 atime=1761382080.149411284 30 ctime=1761382108.548301977 nghttp2-1.68.0/third-party/mruby/mrblib/symbol.rb0000644000175100017510000000021615077107276021363 0ustar00runnerrunnerclass Symbol def to_proc mid = self ->(obj,*args,**opts,&block) do obj.__send__(mid, *args, **opts, &block) end end end nghttp2-1.68.0/third-party/mruby/mrblib/PaxHeaders/string.rb0000644000000000000000000000013215077107276020775 xustar0030 mtime=1761382078.131420515 30 atime=1761382080.149411284 30 ctime=1761382108.545301985 nghttp2-1.68.0/third-party/mruby/mrblib/string.rb0000644000175100017510000001053615077107276021372 0ustar00runnerrunner## # String # # ISO 15.2.10 class String # ISO 15.2.10.3 include Comparable ## # Calls the given block for each line # and pass the respective line. # # ISO 15.2.10.5.15 def each_line(separator = "\n", &block) return to_enum(:each_line, separator) unless block if separator.nil? block.call(self) return self end raise TypeError unless separator.is_a?(String) paragraph_mode = false if separator.empty? paragraph_mode = true separator = "\n\n" end start = 0 string = dup self_len = self.bytesize sep_len = separator.bytesize while (pointer = string.byteindex(separator, start)) pointer += sep_len pointer += 1 while paragraph_mode && string.getbyte(pointer) == 10 # 10 == \n block.call(string.byteslice(start, pointer - start)) start = pointer end return self if start == self_len block.call(string.byteslice(start, self_len - start)) self end ## # Replace all matches of +pattern+ with +replacement+. # Call block (if given) for each match and replace # +pattern+ with the value of the block. Return the # final value. # # ISO 15.2.10.5.18 def gsub(*args, &block) return to_enum(:gsub, *args) if args.length == 1 && !block raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 1..2)" unless (1..2).include?(args.length) pattern, replace = *args plen = pattern.length if args.length == 2 && block block = nil end offset = 0 result = [] while found = self.byteindex(pattern, offset) result << self.byteslice(offset, found - offset) offset = found + plen result << if block block.call(pattern).to_s else self.__sub_replace(replace, pattern, found) end if plen == 0 result << self.byteslice(offset, 1) offset += 1 end end result << self.byteslice(offset..-1) if offset < length result.join end ## # Replace all matches of +pattern+ with +replacement+. # Call block (if given) for each match and replace # +pattern+ with the value of the block. Modify # +self+ with the final value. # # ISO 15.2.10.5.19 def gsub!(*args, &block) raise FrozenError, "can't modify frozen String" if frozen? return to_enum(:gsub!, *args) if args.length == 1 && !block str = self.gsub(*args, &block) return nil unless self.index(args[0]) self.replace(str) end # ## # # Calls the given block for each match of +pattern+ # # If no block is given return an array with all # # matches of +pattern+. # # # # ISO 15.2.10.5.32 # def scan(pattern, &block) # # TODO: String#scan is not implemented yet # end ## # Replace only the first match of +pattern+ with # +replacement+. Call block (if given) for each # match and replace +pattern+ with the value of the # block. Return the final value. # # ISO 15.2.10.5.36 def sub(*args, &block) unless (1..2).include?(args.length) raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 2)" end pattern, replace = *args if args.length == 2 && block block = nil end result = [] found = self.index(pattern) return self.dup unless found result << self.byteslice(0, found) offset = found + pattern.length result << if block block.call(pattern).to_s else self.__sub_replace(replace, pattern, found) end result << self.byteslice(offset..-1) if offset < length result.join end ## # Replace only the first match of +pattern+ with # +replacement+. Call block (if given) for each # match and replace +pattern+ with the value of the # block. Modify +self+ with the final value. # # ISO 15.2.10.5.37 def sub!(*args, &block) raise FrozenError, "can't modify frozen String" if frozen? str = self.sub(*args, &block) return nil unless self.index(args[0]) self.replace(str) end ## # Call the given block for each byte of +self+. def each_byte(&block) return to_enum(:each_byte, &block) unless block pos = 0 while pos < bytesize block.call(getbyte(pos)) pos += 1 end self end # those two methods requires Regexp that is optional in mruby ## # ISO 15.2.10.5.3 #def =~(re) # re =~ self #end ## # ISO 15.2.10.5.27 #def match(re, &block) # re.match(self, &block) #end end nghttp2-1.68.0/third-party/mruby/mrblib/PaxHeaders/compar.rb0000644000000000000000000000013115077107276020747 xustar0029 mtime=1761382078.13042052 30 atime=1761382080.149411284 30 ctime=1761382108.546301983 nghttp2-1.68.0/third-party/mruby/mrblib/compar.rb0000644000175100017510000000357715077107276021354 0ustar00runnerrunner## # Comparable # # ISO 15.3.3 module Comparable ## # call-seq: # obj < other -> true or false # # Return true if +self+ is less # than +other+. Otherwise return # false. # # ISO 15.3.3.2.1 def < other cmp = self <=> other if cmp.nil? raise ArgumentError, "comparison of #{self.class} with #{other.class} failed" end cmp < 0 end ## # call-seq: # obj <= other -> true or false # # Return true if +self+ is less # than or equal to +other+. # Otherwise return false. # # ISO 15.3.3.2.2 def <= other cmp = self <=> other if cmp.nil? raise ArgumentError, "comparison of #{self.class} with #{other.class} failed" end cmp <= 0 end ## # call-seq: # obj == other -> true or false # # Return true if +self+ is equal # to +other+. Otherwise return # false. # # ISO 15.3.3.2.3 def == other cmp = self <=> other cmp.equal?(0) end ## # call-seq: # obj > other -> true or false # # Return true if +self+ is greater # than +other+. Otherwise return # false. # # ISO 15.3.3.2.4 def > other cmp = self <=> other if cmp.nil? raise ArgumentError, "comparison of #{self.class} with #{other.class} failed" end cmp > 0 end ## # call-seq: # obj >= other -> true or false # # Return true if +self+ is greater # than or equal to +other+. # Otherwise return false. # # ISO 15.3.3.2.5 def >= other cmp = self <=> other if cmp.nil? raise ArgumentError, "comparison of #{self.class} with #{other.class} failed" end cmp >= 0 end ## # call-seq: # obj.between?(min,max) -> true or false # # Return true if +self+ is greater # than or equal to +min+ and # less than or equal to +max+. # Otherwise return false. # # ISO 15.3.3.2.6 def between?(min, max) self >= min and self <= max end end nghttp2-1.68.0/third-party/mruby/mrblib/PaxHeaders/numeric.rb0000644000000000000000000000013215077107276021131 xustar0030 mtime=1761382078.131420515 30 atime=1761382080.149411284 30 ctime=1761382108.550301971 nghttp2-1.68.0/third-party/mruby/mrblib/numeric.rb0000644000175100017510000000520715077107276021525 0ustar00runnerrunner## # Numeric # # ISO 15.2.7 class Numeric include Comparable ## # Returns the receiver simply. # # ISO 15.2.7.4.1 def +@ self end ## # Returns the receiver's value, negated. # # ISO 15.2.7.4.2 def -@ 0 - self end ## # Returns the absolute value of the receiver. # # ISO 15.2.7.4.3 def abs if self < 0 -self else self end end end ## # Integer # # ISO 15.2.8 ## class Integer ## # Calls the given block once for each Integer # from +self+ downto +num+. # # ISO 15.2.8.3.15 def downto(num, &block) return to_enum(:downto, num) unless block i = self.to_i while i >= num block.call(i) i -= 1 end self end ## # Returns self + 1 # # ISO 15.2.8.3.19 def next self + 1 end # ISO 15.2.8.3.21 alias succ next ## # Calls the given block +self+ times. # # ISO 15.2.8.3.22 def times(&block) return to_enum :times unless block i = 0 while i < self block.call i i += 1 end self end ## # Calls the given block once for each Integer # from +self+ upto +num+. # # ISO 15.2.8.3.27 def upto(num, &block) return to_enum(:upto, num) unless block i = self.to_i while i <= num block.call(i) i += 1 end self end ## # Calls the given block from +self+ to +num+ # incremented by +step+ (default 1). # def step(num=nil, step=1, &block) raise ArgumentError, "step can't be 0" if step == 0 return to_enum(:step, num, step) unless block i = __coerce_step_counter(step) if num == self || step.infinite? block.call(i) if step > 0 && i <= (num||i) || step < 0 && i >= (num||-i) elsif num == nil while true block.call(i) i += step end elsif step > 0 while i <= num block.call(i) i += step end else while i >= num block.call(i) i += step end end self end end class Float ## # Calls the given block from +self+ to +num+ # incremented by +step+ (default 1). # def step(num=nil, step=1, &block) raise ArgumentError, "step can't be 0" if step == 0 return to_enum(:step, num, step) unless block i = self if num == self || step.infinite? block.call(i) if step > 0 && i <= (num||i) || step < 0 && i >= (num||-i) elsif num == nil while true block.call(i) i += step end elsif step > 0 while i <= num block.call(i) i += step end else while i >= num block.call(i) i += step end end self end end if Object.const_defined?(:Float) nghttp2-1.68.0/third-party/mruby/mrblib/PaxHeaders/range.rb0000644000000000000000000000013215077107276020563 xustar0030 mtime=1761382078.131420515 30 atime=1761382080.149411284 30 ctime=1761382108.538302006 nghttp2-1.68.0/third-party/mruby/mrblib/range.rb0000644000175100017510000000361415077107276021157 0ustar00runnerrunner## # Range # # ISO 15.2.14 class Range ## # Range is enumerable # # ISO 15.2.14.3 include Enumerable ## # Calls the given block for each element of +self+ # and pass the respective element. # # ISO 15.2.14.4.4 def each(&block) return to_enum :each unless block val = self.begin last = self.end if val.kind_of?(Integer) && last.nil? i = val while true block.call(i) i += 1 end return self end if val.kind_of?(String) && last.nil? if val.respond_to? :__upto_endless return val.__upto_endless(&block) else str_each = true end end if val.kind_of?(Integer) && last.kind_of?(Integer) # integers are special lim = last lim += 1 unless exclude_end? i = val while i < lim block.call(i) i += 1 end return self end if val.kind_of?(String) && last.kind_of?(String) # strings are special if val.respond_to? :upto return val.upto(last, exclude_end?, &block) else str_each = true end end raise TypeError, "can't iterate" unless val.respond_to? :succ return self if (val <=> last) > 0 while (val <=> last) < 0 block.call(val) val = val.succ if str_each break if val.size > last.size end end block.call(val) if !exclude_end? && (val <=> last) == 0 self end # redefine #hash 15.3.1.3.15 def hash h = first.hash ^ last.hash h += 1 if self.exclude_end? h end ## # call-seq: # rng.to_a -> array # rng.entries -> array # # Returns an array containing the items in the range. # # (1..7).to_a #=> [1, 2, 3, 4, 5, 6, 7] # (1..).to_a #=> RangeError: cannot convert endless range to an array def to_a a = __num_to_a return a if a super end alias entries to_a end nghttp2-1.68.0/third-party/mruby/mrblib/PaxHeaders/kernel.rb0000644000000000000000000000012715077107276020753 xustar0030 mtime=1761382078.131420515 30 atime=1761382080.149411284 27 ctime=1761382108.540302 nghttp2-1.68.0/third-party/mruby/mrblib/kernel.rb0000644000175100017510000000133615077107276021342 0ustar00runnerrunner## # Kernel # # ISO 15.3.1 module Kernel # 15.3.1.2.1 Kernel.` # provided by Kernel#` # 15.3.1.3.3 def `(s) raise NotImplementedError.new("backquotes not implemented") end ## # 15.3.1.2.3 Kernel.eval # 15.3.1.3.12 Kernel#eval # NotImplemented by mruby core; use mruby-eval gem ## # ISO 15.3.1.2.8 Kernel.loop # not provided by mruby ## # Calls the given block repetitively. # # ISO 15.3.1.3.29 private def loop(&block) return to_enum :loop unless block while true yield end rescue StopIteration => e e.result end # 11.4.4 Step c) def !~(y) !(self =~ y) end def to_enum(*a) raise NotImplementedError.new("fiber required for enumerator") end end nghttp2-1.68.0/third-party/mruby/mrblib/PaxHeaders/10error.rb0000644000000000000000000000013115077107276020760 xustar0029 mtime=1761382078.13042052 30 atime=1761382080.149411284 30 ctime=1761382108.542301994 nghttp2-1.68.0/third-party/mruby/mrblib/10error.rb0000644000175100017510000000060215077107276021347 0ustar00runnerrunner# ISO 15.2.31 class NameError < StandardError attr_accessor :name def initialize(message=nil, name=nil) @name = name super(message) end end # ISO 15.2.32 class NoMethodError < NameError attr_reader :args def initialize(message=nil, name=nil, args=nil) @args = args super message, name end end class StopIteration < IndexError attr_accessor :result end nghttp2-1.68.0/third-party/mruby/PaxHeaders/lib0000644000000000000000000000013015077107334016355 xustar0029 mtime=1761382108.20130298 30 atime=1761382109.796298369 29 ctime=1761382108.20130298 nghttp2-1.68.0/third-party/mruby/lib/0000755000175100017510000000000015077107334017024 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/lib/PaxHeaders/mruby0000644000000000000000000000013015077107334017513 xustar0029 mtime=1761382108.45030226 30 atime=1761382109.796298369 29 ctime=1761382108.45030226 nghttp2-1.68.0/third-party/mruby/lib/mruby/0000755000175100017510000000000015077107334020162 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/lib/mruby/PaxHeaders/presym.rb0000644000000000000000000000013215077107276021443 xustar0030 mtime=1761382078.103420643 30 atime=1761382080.124411398 30 ctime=1761382108.449302263 nghttp2-1.68.0/third-party/mruby/lib/mruby/presym.rb0000644000175100017510000001100415077107276022027 0ustar00runnerrunnermodule MRuby class Presym OPERATORS = { "!" => "not", "%" => "mod", "&" => "and", "*" => "mul", "+" => "add", "-" => "sub", "/" => "div", "<" => "lt", ">" => "gt", "^" => "xor", "`" => "tick", "|" => "or", "~" => "neg", "!=" => "neq", "!~" => "nmatch", "&&" => "andand", "**" => "pow", "+@" => "plus", "-@" => "minus", "<<" => "lshift", "<=" => "le", "==" => "eq", "=~" => "match", ">=" => "ge", ">>" => "rshift", "[]" => "aref", "||" => "oror", "<=>" => "cmp", "===" => "eqq", "[]=" => "aset", }.freeze SYMBOL_TO_MACRO = { # Symbol => Macro # [prefix, suffix] => [prefix, suffix] ["$" , "" ] => ["GV" , "" ], ["@@" , "" ] => ["CV" , "" ], ["@" , "" ] => ["IV" , "" ], ["" , "!" ] => ["" , "_B" ], ["" , "?" ] => ["" , "_Q" ], ["" , "=" ] => ["" , "_E" ], ["" , "" ] => ["" , "" ], }.freeze C_STR_LITERAL_RE = /"(?:[^\\\"]|\\.)*"/ ESCAPE_SEQUENCE_MAP = { "a" => "\a", "b" => "\b", "e" => "\e", "f" => "\f", "n" => "\n", "r" => "\r", "t" => "\t", "v" => "\v", } ESCAPE_SEQUENCE_MAP.keys.each { |k| ESCAPE_SEQUENCE_MAP[ESCAPE_SEQUENCE_MAP[k]] = k } def initialize(build) @build = build end def scan(paths) presym_hash = {} paths.each {|path| read_preprocessed(presym_hash, path)} presym_hash.keys.sort_by!{|sym| [c_literal_size(sym), sym]} end def read_list File.readlines(list_path, mode: "r:binary").each(&:chomp!) end def write_list(presyms) _pp "GEN", list_path.relative_path File.binwrite(list_path, presyms.join("\n") << "\n") end def write_id_header(presyms) prefix_re = Regexp.union(*SYMBOL_TO_MACRO.keys.map(&:first).uniq) suffix_re = Regexp.union(*SYMBOL_TO_MACRO.keys.map(&:last).uniq) sym_re = /\A(#{prefix_re})?([\w&&\D]\w*)(#{suffix_re})?\z/o _pp "GEN", id_header_path.relative_path File.open(id_header_path, "w:binary") do |f| f.puts "enum mruby_presym {" presyms.each.with_index(1) do |sym, num| if sym_re =~ sym && (affixes = SYMBOL_TO_MACRO[[$1, $3]]) f.puts " MRB_#{affixes * 'SYM'}__#{$2} = #{num}," elsif name = OPERATORS[sym] f.puts " MRB_OPSYM__#{name} = #{num}," end end f.puts "};" f.puts f.puts "#define MRB_PRESYM_MAX #{presyms.size}" end end def write_table_header(presyms) _pp "GEN", table_header_path.relative_path File.open(table_header_path, "w:binary") do |f| f.puts "static const uint16_t presym_length_table[] = {" presyms.each{|sym| f.puts " #{sym.bytesize},\t/* #{sym} */"} f.puts "};" f.puts f.puts "static const char * const presym_name_table[] = {" presyms.each do |sym| sym = sym.gsub(/([\x01-\x1f\x7f-\xff])|("|\\)/n) { case when $1 e = ESCAPE_SEQUENCE_MAP[$1] e ? "\\#{e}" : '\\x%02x""' % $1.ord when $2 "\\#$2" end } f.puts %| "#{sym}",| end f.puts "};" end end def list_path @list_path ||= "#{@build.build_dir}/presym".freeze end def header_dir @header_dir ||= "#{@build.build_dir}/include/mruby/presym".freeze end def id_header_path @id_header_path ||= "#{header_dir}/id.h".freeze end def table_header_path @table_header_path ||= "#{header_dir}/table.h".freeze end private def read_preprocessed(presym_hash, path) File.binread(path).scan(/<@! (.*?) !@>/) do |part,| literals = part.scan(C_STR_LITERAL_RE) unless literals.empty? literals = literals.map{|l| l[1..-2]} literals.each do |e| e.gsub!(/\\x([0-9A-Fa-f]{1,2})|\\(0[0-7]{,3})|\\([abefnrtv])|\\(.)/) do case when $1; $1.hex.chr(Encoding::BINARY) when $2; $2.oct.chr(Encoding::BINARY) when $3; ESCAPE_SEQUENCE_MAP[$3] when $4; $4 end end end presym_hash[literals.join] = true end end end def c_literal_size(literal_without_quote) literal_without_quote.size # TODO: consider escape sequence end end end nghttp2-1.68.0/third-party/mruby/lib/mruby/PaxHeaders/doc.rb0000644000000000000000000000013215077107276020671 xustar0030 mtime=1761382078.103420643 30 atime=1761382080.124411398 30 ctime=1761382108.446302272 nghttp2-1.68.0/third-party/mruby/lib/mruby/doc.rb0000644000175100017510000000316115077107276021262 0ustar00runnerrunnerautoload :Pathname, 'pathname' module MRuby module Documentation def Documentation.update_opcode_md mrubydir = Pathname(MRUBY_ROOT) path_ops_h = mrubydir + "include/mruby/ops.h" path_opcode_md = mrubydir + "doc/internal/opcode.md" opspecs = { "Z" => { prefix: "", modified: "-" }, "B" => { prefix: "\'" }, "BB" => { prefix: "\"" }, "BBB" => { prefix: "\"" }, "BS" => { prefix: "\'" }, "BSS" => { prefix: "\'" }, "S" => { prefix: "" }, "W" => { prefix: "" }, } diff = "" spliter = <<~'SPLITER' | No. | Instruction Name | Operand type | Semantics | --: | ---------------- | ------------ | --------------- SPLITER diff = path_opcode_md.read.sub(/^#{Regexp.escape spliter}.*?(?=\z|^$\n)/m) do repl = spliter ops = path_ops_h.read pat = /^\s*OPCODE\s*\(\s*(\w+)\s*,\s*(\w+)\s*\)\s*(?:\/\*\s*(.*?)\s*\*\/\s*)?/ ops.scan(pat).each_with_index do |(ins, opr, cmt), no| if cmt cmt.sub!(/\s*#.*/, "") cmt.sub!(/\b(?=L_\w+\b)/, "OP_") cmt.gsub!(/\b(Irep|Pool|R|Syms)\[([^\[\]]+)\]/, "\\1(\\2)") cmt.gsub!(/[\\\|]/) { |m| "\\#{m}" } # Ruby-2.5 is not support "Numbered block parameter" end spec = opspecs[opr] or raise "unknown operand type: #{opr}" item = format("| %3d | %-16s | %-12s | %s\n", no, "`OP_#{ins}`", "`#{spec[:modified] || opr}`", cmt && "`#{cmt}`") repl << item end repl end path_opcode_md.binwrite diff end end end nghttp2-1.68.0/third-party/mruby/lib/mruby/PaxHeaders/lockfile.rb0000644000000000000000000000013115077107276021713 xustar0030 mtime=1761382078.103420643 30 atime=1761382080.124411398 29 ctime=1761382108.44330228 nghttp2-1.68.0/third-party/mruby/lib/mruby/lockfile.rb0000644000175100017510000000274015077107276022307 0ustar00runnerrunnerautoload :YAML, 'yaml' module MRuby autoload :Source, 'mruby/source' class Lockfile class << self def enable @enabled = true end def disable @enabled = false end def enabled? @enabled end def build(target_name) instance.build(target_name) end def write instance.write if enabled? end def instance @instance ||= new("#{MRUBY_CONFIG}.lock") end end def initialize(filename) @filename = filename end def build(target_name) read[target_name] ||= {} end def write locks = {"mruby" => mruby} locks["builds"] = @builds if @builds File.write(@filename, YAML.dump(locks)) end private def read @builds ||= if File.exist?(@filename) YAML.load_file(@filename)["builds"] || {} else {} end end def shellquote(s) if ENV['OS'] == 'Windows_NT' "\"#{s}\"" else "'#{s}'" end end def mruby mruby = { 'version' => MRuby::Source::MRUBY_VERSION, 'release_no' => MRuby::Source::MRUBY_RELEASE_NO, } git_dir = "#{MRUBY_ROOT}/.git" if File.directory?(git_dir) mruby['git_commit'] = `git --git-dir #{shellquote(git_dir)} --work-tree #{shellquote(MRUBY_ROOT)} rev-parse --verify HEAD`.strip end mruby end enable end end nghttp2-1.68.0/third-party/mruby/lib/mruby/PaxHeaders/gem.rb0000644000000000000000000000013215077107276020674 xustar0030 mtime=1761382078.103420643 30 atime=1761382080.124411398 30 ctime=1761382108.448302266 nghttp2-1.68.0/third-party/mruby/lib/mruby/gem.rb0000644000175100017510000003620515077107276021272 0ustar00runnerrunnerrequire 'forwardable' autoload :TSort, 'tsort' autoload :Shellwords, 'shellwords' module MRuby module Gem class << self attr_accessor :current end class Specification include Rake::DSL extend Forwardable def_delegators :@build, :filename, :objfile, :libfile, :exefile attr_accessor :name, :dir, :build alias mruby build attr_accessor :build_config_initializer attr_accessor :version attr_accessor :description, :summary attr_accessor :homepage attr_accessor :licenses, :authors alias :license= :licenses= alias :author= :authors= attr_accessor :rbfiles, :objs attr_writer :test_objs, :test_rbfiles attr_accessor :test_args, :test_preload attr_accessor :bins attr_accessor :requirements attr_reader :dependencies, :conflicts attr_accessor :export_include_paths attr_reader :generate_functions attr_writer :skip_test attr_block MRuby::Build::COMMANDS def initialize(name, &block) @name = name @initializer = block @version = "0.0.0" @dependencies = [] @conflicts = [] MRuby::Gem.current = self end def setup return if defined?(@bins) # return if already set up MRuby::Gem.current = self MRuby::Build::COMMANDS.each do |command| instance_variable_set("@#{command}", @build.send(command).clone) end @linker.run_attrs.each(&:clear) @rbfiles = Dir.glob("#{@dir}/mrblib/**/*.rb").sort @objs = srcs_to_objs("src") @test_preload = nil # 'test/assert.rb' @test_args = {} @skip_test = false @bins = [] @cdump = true @requirements = [] @export_include_paths = [] @export_include_paths << "#{dir}/include" if File.directory? "#{dir}/include" instance_eval(&@initializer) @generate_functions = !(@rbfiles.empty? && @objs.empty?) @objs << objfile("#{build_dir}/gem_init") if @generate_functions if !name || !licenses || !authors fail "#{name || dir} required to set name, license(s) and author(s)" end build.libmruby_objs << @objs instance_eval(&@build_config_initializer) if @build_config_initializer repo_url = build.gem_dir_to_repo_url[dir] build.locks[repo_url]['version'] = version if repo_url end def skip_test? @skip_test end def setup_compilers (core? ? [@cc, *(@cxx if build.cxx_exception_enabled?)] : compilers).each do |compiler| compiler.define_rules build_dir, @dir, @build.exts.presym_preprocessed if build.presym_enabled? compiler.define_rules build_dir, @dir, @build.exts.object compiler.defines << %Q[MRBGEM_#{funcname.upcase}_VERSION=#{version}] compiler.include_paths << "#{@dir}/include" if File.directory? "#{@dir}/include" end define_gem_init_builder if @generate_functions end def for_windows? if build.kind_of?(MRuby::CrossBuild) return %w(x86_64-w64-mingw32 i686-w64-mingw32).include?(build.host_target) elsif build.kind_of?(MRuby::Build) return ('A'..'Z').to_a.any? { |vol| Dir.exist?("#{vol}:") } end return false end def disable_cdump @cdump = false end def cdump? build.presym_enabled? && @cdump end def core? @dir.start_with?("#{MRUBY_ROOT}/mrbgems/") end def bin? @bins.size > 0 end def add_dependency(name, *requirements) default_gem = requirements.last.kind_of?(Hash) ? requirements.pop : nil requirements = ['>= 0.0.0'] if requirements.empty? requirements.flatten! @dependencies << {:gem => name, :requirements => requirements, :default => default_gem} end def add_test_dependency(*args) add_dependency(*args) if build.test_enabled? || build.bintest_enabled? end def add_conflict(name, *req) @conflicts << {:gem => name, :requirements => req.empty? ? nil : req} end def build_dir "#{build.build_dir}/mrbgems/#{name}" end def test_rbireps "#{build_dir}/gem_test.c" end def test_objs @test_objs ||= srcs_to_objs("test") end def test_rbfiles @test_rbfiles ||= Dir["#{@dir}/test/**/*.rb"].sort! end def search_package(name, version_query=nil) package_query = name package_query += " #{version_query}" if version_query _pp "PKG-CONFIG", package_query escaped_package_query = Shellwords.escape(package_query) if system("pkg-config --exists #{escaped_package_query}") cc.flags += [`pkg-config --cflags #{escaped_package_query}`.strip] cxx.flags += [`pkg-config --cflags #{escaped_package_query}`.strip] linker.flags_before_libraries += [`pkg-config --libs #{escaped_package_query}`.strip] true else false end end def funcname @funcname ||= @name.tr('-', '_') end def compilers MRuby::Build::COMPILERS.map do |c| instance_variable_get("@#{c}") end end def srcs_to_objs(src_dir_from_gem_dir) exts = compilers.flat_map{|c| c.source_exts} * "," Dir["#{@dir}/#{src_dir_from_gem_dir}/*{#{exts}}"].map do |f| objfile(f.relative_path_from(@dir).to_s.pathmap("#{build_dir}/%X")) end end def define_gem_init_builder file "#{build_dir}/gem_init.c" => [build.mrbcfile, __FILE__] + [rbfiles].flatten do |t| mkdir_p build_dir generate_gem_init("#{build_dir}/gem_init.c") end end def generate_gem_init(fname) _pp "GEN", fname.relative_path open(fname, 'w') do |f| print_gem_init_header f unless rbfiles.empty? opts = {cdump: cdump?, static: true} if cdump? build.mrbc.run f, rbfiles, "gem_mrblib_#{funcname}_proc", **opts else build.mrbc.run f, rbfiles, "gem_mrblib_irep_#{funcname}", **opts end end f.puts %Q[void mrb_#{funcname}_gem_init(mrb_state *mrb);] f.puts %Q[void mrb_#{funcname}_gem_final(mrb_state *mrb);] f.puts %Q[] f.puts %Q[void GENERATED_TMP_mrb_#{funcname}_gem_init(mrb_state *mrb) {] f.puts %Q[ gem_mrblib_#{funcname}_proc_init_syms(mrb);] if !rbfiles.empty? && cdump? f.puts %Q[ mrb_#{funcname}_gem_init(mrb);] if objs != [objfile("#{build_dir}/gem_init")] unless rbfiles.empty? if cdump? f.puts %Q[ mrb_load_proc(mrb, gem_mrblib_#{funcname}_proc);] else f.puts %Q[ mrb_load_irep(mrb, gem_mrblib_irep_#{funcname});] end end f.puts %Q[}] f.puts %Q[] f.puts %Q[void GENERATED_TMP_mrb_#{funcname}_gem_final(mrb_state *mrb) {] f.puts %Q[ mrb_#{funcname}_gem_final(mrb);] if objs != [objfile("#{build_dir}/gem_init")] f.puts %Q[}] end end # generate_gem_init def print_gem_comment(f) f.puts %Q[/*] f.puts %Q[ * This file is loading the irep] f.puts %Q[ * Ruby GEM code.] f.puts %Q[ *] f.puts %Q[ * This file was generated by mruby/#{__FILE__.relative_path_from(MRUBY_ROOT)}.] f.puts %Q[ *] f.puts %Q[ * IMPORTANT:] f.puts %Q[ * This file was generated!] f.puts %Q[ * All manual changes will get lost.] f.puts %Q[ */] end def print_gem_init_header(f) print_gem_comment(f) if rbfiles.empty? f.puts %Q[#include ] else f.puts %Q[#include ] unless cdump? f.puts %Q[#include ] f.puts %Q[#include ] end end end def print_gem_test_header(f) print_gem_comment(f) f.puts %Q[#include ] f.puts %Q[#include ] f.puts %Q[#include ] f.puts %Q[#include ] f.puts %Q[#include ] f.puts %Q[#include ] unless test_args.empty? end def custom_test_init? !test_objs.empty? end def version_ok?(req_versions) req_versions.map do |req| cmp, ver = req.split cmp_result = Version.new(version) <=> Version.new(ver) case cmp when '=' then cmp_result == 0 when '!=' then cmp_result != 0 when '>' then cmp_result == 1 when '<' then cmp_result == -1 when '>=' then cmp_result >= 0 when '<=' then cmp_result <= 0 when '~>' Version.new(version).twiddle_wakka_ok?(Version.new(ver)) else fail "Comparison not possible with '#{cmp}'" end end.all? end def each_header_files(&block) return to_enum(__method__) unless block self.export_include_paths.flatten.uniq.compact.each do |dir| Find.find(dir) do |d| next unless File.file? d yield d end end self end end # Specification class Version include Comparable include Enumerable def <=>(other) ret = 0 own = to_enum other.each do |oth| begin ret = own.next <=> oth rescue StopIteration ret = 0 <=> oth end break unless ret == 0 end ret end # ~> compare algorithm # # Example: # ~> 2 means >= 2.0.0 and < 3.0.0 # ~> 2.2 means >= 2.2.0 and < 3.0.0 # ~> 2.2.2 means >= 2.2.2 and < 2.3.0 def twiddle_wakka_ok?(other) gr_or_eql = (self <=> other) >= 0 still_major_or_minor = (self <=> other.skip_major_or_minor) < 0 gr_or_eql and still_major_or_minor end def skip_major_or_minor a = @ary.dup a << 0 if a.size == 1 # ~> 2 can also be represented as ~> 2.0 a.slice!(-1) a[-1] = a[-1].succ a end def initialize(str) @str = str @ary = @str.split('.').map(&:to_i) end def each(&block); @ary.each(&block); end def [](index); @ary[index]; end def []=(index, value) @ary[index] = value @str = @ary.join('.') end def slice!(index) @ary.slice!(index) @str = @ary.join('.') end end # Version class List include Enumerable def initialize @ary = [] end def each(&b) @ary.each(&b) self end def [](name) @ary.detect {|g| g.name == name} end def <<(gem) unless @ary.detect {|g| g.dir == gem.dir } @ary << gem else # GEM was already added to this list end self end def empty? @ary.empty? end def default_gem_params dep if dep[:default]; dep elsif File.exist? "#{MRUBY_ROOT}/mrbgems/#{dep[:gem]}" # check core { :gem => dep[:gem], :default => { :core => dep[:gem] } } else # fallback to mgem-list { :gem => dep[:gem], :default => { :mgem => dep[:gem] } } end end def generate_gem_table build gem_table = each_with_object({}) { |spec, h| h[spec.name] = spec } default_gems = {} each do |g| g.dependencies.each do |dep| default_gems[dep[:gem]] ||= default_gem_params(dep) end end until default_gems.empty? def_name, def_gem = default_gems.shift next if gem_table[def_name] spec = gem_table[def_name] = build.gem(def_gem[:default]) fail "Invalid gem name: #{spec.name} (Expected: #{def_name})" if spec.name != def_name spec.setup spec.dependencies.each do |dep| default_gems[dep[:gem]] ||= default_gem_params(dep) end end each do |g| g.dependencies.each do |dep| name = dep[:gem] req_versions = dep[:requirements] dep_g = gem_table[name] # check each GEM dependency against all available GEMs if dep_g.nil? fail "The GEM '#{g.name}' depends on the GEM '#{name}' but it could not be found" end unless dep_g.version_ok? req_versions fail "#{name} version should be #{req_versions.join(' and ')} but was '#{dep_g.version}'" end end cfls = g.conflicts.select { |c| cfl_g = gem_table[c[:gem]] cfl_g and cfl_g.version_ok?(c[:requirements] || ['>= 0.0.0']) }.map { |c| "#{c[:gem]}(#{gem_table[c[:gem]].version})" } fail "Conflicts of gem `#{g.name}` found: #{cfls.join ', '}" unless cfls.empty? end gem_table end def tsort_dependencies ary, table, all_dependency_listed = false unless all_dependency_listed left = ary.dup until left.empty? v = left.pop table[v].dependencies.each do |dep| left.push dep[:gem] ary.push dep[:gem] end end end ary.uniq! table.instance_variable_set :@root_gems, ary class << table include TSort def tsort_each_node(&b) @root_gems.each(&b) end def tsort_each_child(n, &b) fetch(n).dependencies.each do |v| b.call v[:gem] end end end begin table.tsort.map { |v| table[v] } rescue TSort::Cyclic => e fail "Circular mrbgem dependency found: #{e.message}" end end def check(build) gem_table = generate_gem_table build @ary = tsort_dependencies gem_table.keys, gem_table, true each(&:setup_compilers) each do |g| import_include_paths(g) end end def import_include_paths(g) gem_table = each_with_object({}) { |spec, h| h[spec.name] = spec } g.dependencies.each do |dep| dep_g = gem_table[dep[:gem]] # We can do recursive call safely # as circular dependency has already detected in the caller. import_include_paths(dep_g) dep_g.export_include_paths.uniq! g.compilers.each do |compiler| compiler.include_paths += dep_g.export_include_paths g.export_include_paths += dep_g.export_include_paths compiler.include_paths.uniq! g.export_include_paths.uniq! end end end def linker_attrs(gem=nil) gems = self.reject{|g| g.bin?} # library gems gems << gem unless gem.nil? gems.map{|g| g.linker.run_attrs}.transpose end end # List end # Gem GemBox = Object.new class << GemBox attr_accessor :path def new(&block); block.call(self); end def config=(obj); @config = obj; end def gem(gemdir, &block); @config.gem(gemdir, &block); end def gembox(gemfile); @config.gembox(gemfile); end end # GemBox end # MRuby nghttp2-1.68.0/third-party/mruby/lib/mruby/PaxHeaders/source.rb0000644000000000000000000000013115077107276021423 xustar0030 mtime=1761382078.103420643 30 atime=1761382080.125411393 29 ctime=1761382108.45030226 nghttp2-1.68.0/third-party/mruby/lib/mruby/source.rb0000644000175100017510000000267215077107276022023 0ustar00runnerrunnerrequire "pathname" module MRuby module Source # mruby's source root directory ROOT = Pathname.new(File.expand_path('../../../',__FILE__)) # Reads a constant defined at version.h MRUBY_READ_VERSION_CONSTANT = Proc.new do |name| ROOT.join('include','mruby','version.h').read.match(/^#define #{name} +"?([\w\. ]+)"?\r?$/)[1] end MRUBY_RUBY_VERSION = MRUBY_READ_VERSION_CONSTANT['MRUBY_RUBY_VERSION'] MRUBY_RUBY_ENGINE = MRUBY_READ_VERSION_CONSTANT['MRUBY_RUBY_ENGINE'] MRUBY_RELEASE_MAJOR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_MAJOR']) MRUBY_RELEASE_MINOR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_MINOR']) MRUBY_RELEASE_TEENY = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_TEENY']) MRUBY_VERSION = [MRUBY_RELEASE_MAJOR,MRUBY_RELEASE_MINOR,MRUBY_RELEASE_TEENY].join('.') MRUBY_RELEASE_NO = (MRUBY_RELEASE_MAJOR * 100 * 100 + MRUBY_RELEASE_MINOR * 100 + MRUBY_RELEASE_TEENY) MRUBY_RELEASE_YEAR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_YEAR']) MRUBY_RELEASE_MONTH = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_MONTH']) MRUBY_RELEASE_DAY = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_DAY']) MRUBY_RELEASE_DATE = [MRUBY_RELEASE_YEAR,MRUBY_RELEASE_MONTH,MRUBY_RELEASE_DAY].join('.') MRUBY_BIRTH_YEAR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_BIRTH_YEAR']) MRUBY_AUTHOR = MRUBY_READ_VERSION_CONSTANT['MRUBY_AUTHOR'] end end nghttp2-1.68.0/third-party/mruby/lib/mruby/PaxHeaders/build0000644000000000000000000000013215077107334020614 xustar0030 mtime=1761382108.441302286 30 atime=1761382109.796298369 30 ctime=1761382108.441302286 nghttp2-1.68.0/third-party/mruby/lib/mruby/build/0000755000175100017510000000000015077107334021261 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/lib/mruby/build/PaxHeaders/command.rb0000644000000000000000000000013215077107276022641 xustar0030 mtime=1761382078.103420643 30 atime=1761382080.124411398 30 ctime=1761382108.440302289 nghttp2-1.68.0/third-party/mruby/lib/mruby/build/command.rb0000644000175100017510000003025615077107276023237 0ustar00runnerrunnerrequire 'forwardable' module MRuby class Command include Rake::DSL extend Forwardable def_delegators :@build, :filename, :objfile, :libfile, :exefile attr_accessor :build, :command def initialize(build) @build = build end # clone is deep clone without @build def clone target = super excepts = %w(@build) instance_variables.each do |attr| unless excepts.include?(attr.to_s) val = Marshal::load(Marshal.dump(instance_variable_get(attr))) # deep clone target.instance_variable_set(attr, val) end end target end def shellquote(s) "\"#{s}\"" end private def _run(options, params={}) sh "#{build.filename(command)} #{options % params}" end end class Command::Compiler < Command attr_accessor :label, :flags, :include_paths, :defines, :source_exts attr_accessor :compile_options, :option_define, :option_include_path, :out_ext attr_accessor :cxx_compile_flag, :cxx_exception_flag, :cxx_invalid_flags attr_writer :preprocess_options def initialize(build, source_exts=[], label: "CC") super(build) @command = ENV['CC'] || 'cc' @label = label @flags = [ENV['CFLAGS'] || []] @source_exts = source_exts @include_paths = ["#{MRUBY_ROOT}/include"] @defines = [] @option_include_path = %q[-I"%s"] @option_define = %q[-D"%s"] @compile_options = %q[%{flags} -o "%{outfile}" -c "%{infile}"] @cxx_invalid_flags = [] @out_ext = build.exts.object end alias header_search_paths include_paths def preprocess_options @preprocess_options ||= @compile_options.sub(/(?:\A|\s)\K-c(?=\s)/, "-E -P") end def search_header_path(name) header_search_paths.find do |v| File.exist? build.filename("#{v}/#{name}").sub(/^"(.*)"$/, '\1') end end def search_header(name) path = search_header_path name path && build.filename("#{path}/#{name}").sub(/^"(.*)"$/, '\1') end def all_flags(_defines=[], _include_paths=[], _flags=[]) define_flags = [defines, _defines, build.defines].flatten.map{ |d| option_define % d } include_path_flags = [include_paths, _include_paths].flatten.map do |f| option_include_path % filename(f) end [flags, define_flags, include_path_flags, _flags].flatten.join(' ') end def run(outfile, infile, _defines=[], _include_paths=[], _flags=[]) mkdir_p File.dirname(outfile) flags = all_flags(_defines, _include_paths, _flags) if object_ext?(outfile) label = @label opts = compile_options else label = "CPP" opts = preprocess_options flags << " -DMRB_PRESYM_SCANNING" end _pp label, infile.relative_path, outfile.relative_path _run opts, flags: flags, infile: filename(infile), outfile: filename(outfile) end def define_rules(build_dir, source_dir='', out_ext=build.exts.object) gemrake = File.join(source_dir, "mrbgem.rake") rakedep = File.exist?(gemrake) ? [ gemrake ] : [] bd = build_dir if bd.start_with?(MRUBY_ROOT) bd = bd.sub(MRUBY_ROOT, '') end if bd.include? "mrbgems/" generated_file_matcher = Regexp.new("^#{Regexp.escape build_dir}/(?!mrbc/)(.*)#{Regexp.escape out_ext}$") else generated_file_matcher = Regexp.new("^#{Regexp.escape build_dir}/(?!mrbc/|mrbgems/.+/)(.*)#{Regexp.escape out_ext}$") end source_exts.each do |ext| rule generated_file_matcher => [ proc { |file| file.sub(generated_file_matcher, "#{source_dir}/\\1#{ext}") }, proc { |file| get_dependencies(file) + rakedep } ] do |t| run t.name, t.prerequisites.first end rule generated_file_matcher => [ proc { |file| file.sub(generated_file_matcher, "#{build_dir}/\\1#{ext}") }, proc { |file| get_dependencies(file) + rakedep } ] do |t| run t.name, t.prerequisites.first end end end # This method can be redefined as a singleton method where appropriate. # Manipulate `flags`, `include_paths` and/or more if necessary. def setup_debug(conf) nil end private # # === Example of +.d+ file # # ==== Without -MP compiler flag # # /build/host/src/array.o: /src/array.c \ # /include/mruby/common.h /include/mruby/value.h \ # /src/value_array.h # # ==== With -MP compiler flag # # /build/host/src/array.o: /src/array.c \ # /include/mruby/common.h /include/mruby/value.h \ # /src/value_array.h # # /include/mruby/common.h: # # /include/mruby/value.h: # # /src/value_array.h: # def get_dependencies(file) dep_file = file.ext(".d") return [MRUBY_CONFIG] unless object_ext?(file) && File.exist?(dep_file) deps = File.read(dep_file).gsub("\\\n ", "").split("\n").map do |dep_line| # dep_line: # - "/build/host/src/array.o: /src/array.c /include/mruby/common.h ..." # - "" # - "/include/mruby/common.h:" dep_line.scan(/^\S+:\s+(.+)$/).flatten.map { |s| s.split(' ') }.flatten # => ["/src/array.c", "/include/mruby/common.h" , ...] # [] # [] end.flatten.uniq deps << MRUBY_CONFIG end def object_ext?(path) File.extname(path) == build.exts.object end end class Command::Linker < Command attr_accessor :flags, :library_paths, :flags_before_libraries, :libraries, :flags_after_libraries attr_accessor :link_options, :option_library, :option_library_path def initialize(build) super @command = ENV['LD'] || 'ld' @flags = (ENV['LDFLAGS'] || []) @flags_before_libraries, @flags_after_libraries = [], [] @libraries = [] @library_paths = [] @option_library = %q[-l"%s"] @option_library_path = %q[-L"%s"] @link_options = %Q[%{flags} -o "%{outfile}" %{objs} %{flags_before_libraries} %{libs} %{flags_after_libraries}] end def all_flags(_library_paths=[], _flags=[]) library_path_flags = [library_paths, _library_paths].flatten.map do |f| option_library_path % filename(f) end [flags, library_path_flags, _flags].flatten.join(' ') end def library_flags(_libraries) [libraries, _libraries].flatten.map{ |d| option_library % d }.join(' ') end def run_attrs [@libraries, @library_paths, @flags, @flags_before_libraries, @flags_after_libraries] end def run(outfile, objfiles, _libraries=[], _library_paths=[], _flags=[], _flags_before_libraries=[], _flags_after_libraries=[]) mkdir_p File.dirname(outfile) library_flags = [libraries, _libraries].flatten.map { |d| option_library % d } _pp "LD", outfile.relative_path _run link_options, { :flags => all_flags(_library_paths, _flags), :outfile => filename(outfile) , :objs => filename(objfiles).map{|f| %Q["#{f}"]}.join(' '), :flags_before_libraries => [flags_before_libraries, _flags_before_libraries].flatten.join(' '), :flags_after_libraries => [flags_after_libraries, _flags_after_libraries].flatten.join(' '), :libs => library_flags.join(' ') } end end class Command::Archiver < Command attr_accessor :archive_options def initialize(build) super @command = ENV['AR'] || 'ar' @archive_options = 'rs "%{outfile}" %{objs}' end def run(outfile, objfiles) mkdir_p File.dirname(outfile) _pp "AR", outfile.relative_path _run archive_options, { :outfile => filename(outfile), :objs => filename(objfiles).map{|f| %Q["#{f}"]}.join(' ') } end end class Command::Yacc < Command attr_accessor :compile_options def initialize(build) super @command = "ruby #{MRUBY_ROOT}/tools/lrama/exe/lrama" @compile_options = %q[-o "%{outfile}" "%{infile}"] end def run(outfile, infile) mkdir_p File.dirname(outfile) _pp "YACC", infile.relative_path, outfile.relative_path _run compile_options, { :outfile => filename(outfile) , :infile => filename(infile) } end end class Command::Gperf < Command attr_accessor :compile_options def initialize(build) super @command = 'gperf' @compile_options = %q[-L ANSI-C -C -j1 -i 1 -o -t -N mrb_reserved_word -k"1,3,$" "%{infile}" > "%{outfile}"] end def run(outfile, infile) mkdir_p File.dirname(outfile) _pp "GPERF", infile.relative_path, outfile.relative_path _run compile_options, { :outfile => filename(outfile) , :infile => filename(infile) } end end class Command::Git < Command attr_accessor :flags attr_accessor :clone_options, :pull_options, :checkout_options, :checkout_detach_options, :reset_options def initialize(build) super @command = 'git' @flags = [] @clone_options = "clone %{flags} %{url} %{dir}" @pull_options = "--git-dir %{repo_dir}/.git --work-tree %{repo_dir} pull" @checkout_options = "--git-dir %{repo_dir}/.git --work-tree %{repo_dir} checkout %{checksum_hash}" @checkout_detach_options = "--git-dir %{repo_dir}/.git --work-tree %{repo_dir} checkout --detach %{checksum_hash}" @reset_options = "--git-dir %{repo_dir}/.git --work-tree %{repo_dir} reset %{checksum_hash}" end def run_clone(dir, url, _flags = []) _pp "GIT", url, dir.relative_path _run clone_options, { :flags => [flags, _flags].flatten.join(' '), :url => shellquote(url), :dir => shellquote(filename(dir)) } end def run_pull(dir, url) _pp "GIT PULL", url, dir.relative_path _run pull_options, { :repo_dir => shellquote(dir) } end def run_checkout(dir, checksum_hash) _pp "GIT CHECKOUT", dir, checksum_hash _run checkout_options, { :checksum_hash => checksum_hash, :repo_dir => shellquote(dir) } end def run_checkout_detach(dir, checksum_hash) _pp "GIT CHECKOUT DETACH", dir, checksum_hash _run checkout_detach_options, { :checksum_hash => checksum_hash, :repo_dir => shellquote(dir) } end def run_reset_hard(dir, checksum_hash) _pp "GIT RESET", dir, checksum_hash _run reset_options, { :checksum_hash => checksum_hash, :repo_dir => shellquote(dir) } end def commit_hash(dir) `#{@command} --git-dir #{shellquote(dir + '/.git')} --work-tree #{shellquote(dir)} rev-parse --verify HEAD`.strip end def current_branch(dir) `#{@command} --git-dir #{shellquote(dir + '/.git')} --work-tree #{shellquote(dir)} rev-parse --abbrev-ref HEAD`.strip end end class Command::Mrbc < Command attr_accessor :compile_options def initialize(build) super @command = nil @compile_options = "-B%{funcname} -o-" end def run(out, infiles, funcname, cdump: true, static: false) @command ||= @build.mrbcfile infiles = [infiles].flatten infiles.each_with_index do |f, i| _pp i == 0 ? "MRBC" : "", f.relative_path, indent: 2 end opt = @compile_options % {funcname: funcname} opt << " -S" if cdump opt << " -s" if static cmd = %["#{filename @command}" #{opt} #{filename(infiles).map{|f| %["#{f}"]}.join(' ')}] puts cmd if Rake.verbose IO.popen(cmd, 'r') do |io| out.puts io.read end # if mrbc execution fail, drop the file unless $?.success? rm_f out.path fail "Command failed with status (#{$?.exitstatus}): [#{cmd[0,42]}...]" end end end class Command::CrossTestRunner < Command attr_accessor :runner_options attr_accessor :verbose_flag attr_accessor :flags def initialize(build) super @command = nil @runner_options = '%{flags} %{infile}' @verbose_flag = '' @flags = [] end def emulator return "" unless @command return [@command, *@flags].map{|c| shellquote(c)}.join(' ') end def run(testbinfile) puts "TEST for " + @build.name _run runner_options, { :flags => [flags, verbose_flag].flatten.join(' '), :infile => testbinfile } end end end nghttp2-1.68.0/third-party/mruby/lib/mruby/build/PaxHeaders/load_gems.rb0000644000000000000000000000013215077107276023155 xustar0030 mtime=1761382078.103420643 30 atime=1761382080.124411398 30 ctime=1761382108.441302286 nghttp2-1.68.0/third-party/mruby/lib/mruby/build/load_gems.rb0000644000175100017510000002635015077107276023553 0ustar00runnerrunnerrequire 'yaml' module MRuby module LoadGems def gembox(gemboxfile) gembox = File.expand_path("#{gemboxfile}.gembox", "#{MRUBY_ROOT}/mrbgems") fail "Can't find gembox '#{gembox}'" unless File.exist?(gembox) GemBox.config = self GemBox.path = gembox instance_eval File.read(gembox) GemBox.path = nil end def gem(gem_src, &block) caller_dir = File.expand_path(File.dirname(caller(1,1)[0][/^(.*?):\d/,1])) gem_src = {gemdir: gem_src} if gem_src.is_a? String @gem_checkouts ||= {} checkout = GemLoader .new(self, caller_dir, @gem_checkouts, **gem_src) .fetch! return nil unless checkout @gem_checkouts[checkout.gemdir] = checkout # Load the gem's rakefile gemrake = File.join(checkout.full_gemdir, "mrbgem.rake") fail "Can't find #{gemrake}" unless File.exist?(gemrake) Gem.current = nil load gemrake return nil unless Gem.current current = Gem.current # Add it to gems current.dir = checkout.full_gemdir current.build = self.is_a?(MRuby::Build) ? self : MRuby::Build.current current.build_config_initializer = block gems << current cxx_srcs = Dir.glob("#{current.dir}/{src,test,tools}/*.{cpp,cxx,cc}") enable_cxx_exception unless cxx_srcs.empty? current end # Class to represent the relationship between a gem dependency and # its remote repository (if any). class GemCheckout attr_reader :gemdir, :repo, :branch, :commit def initialize(gemdir, repo, branch, commit, canonical, path = nil) @gemdir = gemdir # Working copy of the gem @path = path # Path to gem relative to checkout @repo = repo # Remote gem repo @branch = branch # Branch to check out @commit = commit # Commit-id to use @canonical = canonical # This is the One True checkout end def full_gemdir return @gemdir unless @path return File.join(@gemdir, @path) end def canonical?() return @canonical; end def git?() return !!@repo; end def gemname() return File.basename(@gemdir); end def hash return [@gemdir, @repo, @branch, @commit, @canonical, @path].hash end def ==(other) return @gemdir == other.gemdir && @repo == other.repo && @branch == other.branch && @commit == other.commit && @canonical == other.canonical? && full_gemdir == other.full_gemdir end alias_method :eql?, :== def to_s desc = @gemdir desc += " -> #{@repo}/#{@branch}" if git? desc += "/#{commit}" if commit return desc end end # Class to decode the argument set given to 'MRuby::Build::gem', # and git-clone+git-checkout the sources if needed. class GemLoader def initialize(build, build_config_dir, # Parent dir. of build_config gem_checkouts, # Hash of existing checkouts # Git repo: git: nil, branch: "master", checksum_hash: nil, options: [], path: nil, # path to root relative to gem checkout # Git repo on GitHub github: nil, # Git repo on BitBucket bitbucket: nil, method: nil, # mgem entry mgem: nil, # Core package core: nil, # Local file(s) gemdir: nil, # Related flags: canonical: false # Ignore subsequent checkout of this gem ) # Tolerate a single string option options = [options] unless options.is_a? Array @build = build @build_config_dir = build_config_dir @gem_checkouts = gem_checkouts @canonical = canonical @git = git @path = path @branch = branch @checksum_hash = checksum_hash @options = options @canonical = canonical @github = github @bitbucket = bitbucket @method = method @mgem = mgem @core = core @gemdir = gemdir actions = [git, github, bitbucket, mgem, core, gemdir] fail("Need to set exactly ONE of git, github, bitbucket, mgem, core, " + "or gemdir") unless actions.compact.size == 1 end # Retrieve the repo and return the details in a GemCheckout # object or nil if nothing needed to be done. def fetch! return fromGemdir! if @gemdir return fromCore! if @core return fromGitHub! if @github return fromBitBucket! if @bitbucket return fromMGem! if @mgem return fromGit!(@git, @branch) if @git # Shouldn't be reachable, but... fail "Invalid gem configuration!" end private # # Local Paths # def fromGemdir! gem_src = @gemdir # If @gemdir is a relative path, we first convert it to an # absolute path; this depends on circumstances. if MRuby::GemBox.path # If GemBox.path is set, it means that this fetch operation is # happening as part of a gembox evaluation and we use the # gembox's path as the starting point. gem_src = File.expand_path(gem_src, File.dirname(MRuby::GemBox.path)) else # Otherwise, we use the path to the build_config.rb file that # requested this gem. This path was extracted earlier and # stored in @build_config_dir via the second argument of # 'initialize'. root_dir = @build_config_dir # And we default to the repo root if the file is one of the # stock configs in build_config/. root_dir = MRUBY_ROOT if root_dir == "#{MRUBY_ROOT}/build_config" gem_src = File.expand_path(gem_src, root_dir) end return GemCheckout.new(gem_src, nil, nil, nil, @canonical) end def fromCore! return GemCheckout.new("#{@build.root}/mrbgems/#{@core}", nil, nil, nil, @canonical) end # # Git forges # def fromGitHub! url = "https://github.com/#{@github}.git" return fromGit!(url, @branch) end def fromBitBucket! if @method == "ssh" url = "git@bitbucket.org:#{@bitbucket}.git" else url = "https://bitbucket.org/#{@bitbucket}.git" end return fromGit!(url, @branch) end # # mgem file # def fromMGem! mgem = fetchMGem(@mgem) url = mgem['repository'] branch = mgem['branch'] || @branch return fromGit!(url, branch) end # Fetch the contents of the named mgem item. Will clone the # mgem-list repo if not present def fetchMGem(mgem) list_dir = "#{@build.gem_clone_dir}/mgem-list" url = 'https://github.com/mruby/mgem-list.git' git_clone_dependency(url, list_dir, nil, 'master') conf_path = "#{list_dir}/#{mgem}.gem" conf_path = "#{list_dir}/mruby-#{mgem}.gem" unless File.exist? conf_path fail "mgem not found: #{mgem}" unless File.exist? conf_path conf = YAML.load File.read conf_path fail "unknown mgem protocol: #{conf['protocol']}" if conf['protocol'] != 'git' return conf end # # Git checkouts # def fromGit!(url, branch) repo_dir = "#{@build.gem_clone_dir}/" + "#{url.match(/([-\w]+)(\.[-\w]+|)$/).to_a[1]}" commit = @checksum_hash return nil if skip_this?(url, repo_dir, branch, commit) # If there's a lockfile entry for this repo AND the user hasn't # specified a specific commit ID, we use the locked branch and # commit. lock = @build.locks[url] if @build.lock_enabled? if !commit && lock branch = lock['branch'] commit = lock['commit'] end # Clone the dependency (if needed) and checkout the expected # revision. git_clone_dependency(url, repo_dir, commit, branch) git_checkout_dependency(repo_dir, commit, branch) # Set the lockfile entry if enabled if @build.lock_enabled? @build.gem_dir_to_repo_url[repo_dir] = url @build.locks[url] = { 'url' => url, 'branch' => branch || @build.git.current_branch(repo_dir), 'commit' => @build.git.commit_hash(repo_dir), } end return GemCheckout.new(repo_dir, url, branch, commit, @canonical,@path) end # Test if this repo can be skipped. This will happen if it's # already in @gem_checkouts and EITHER it is identical (same # url, branch, commit-ID and subdirectory path) as the current # checkout OR its "canonical" flag is true. If it's in # @gem_checkouts and neither of these conditions is true, that's # a fatal error; it means there are multiple incompatible # versions of this gem to be checked out into this directory. # # Otherwise, returns false. def skip_this?(url, repo_dir, branch, commit) prev = @gem_checkouts[repo_dir] return false unless prev # Canonical declarations must precede all others. fail("Attempted to redeclare #{prev.gemname} as canonical!\n" + "('canonical' can only be used on its first declaration.)") if prev && @canonical # If prev is canonical, we can ignore this if prev.canonical? puts "Found canonical #{prev.gemname}; skipping this one." return true end # If this checkout is identical to the current one, we can skip it. candidate = GemCheckout.new(repo_dir, url, branch, commit, @canonical, @path) if prev == candidate puts "Found duplicate checkout for #{repo_dir}; ignoring." return true end # Otherwise, we have a checkout conflict. This is an error. fail "Conflicting gem definitions for '#{repo_dir}':\n" + " #{candidate}\n" + " #{prev}\n" end # Retrieve a git repo if it's not present. Return # [path_to_checkout, did_clone] def git_clone_dependency(url, repo_dir, commit, branch) return if File.exist?(repo_dir) && File.exist?(File.join(repo_dir, '.git')) FileUtils.mkdir_p repo_dir options = @options.dup options << "--recursive" options << "--branch \"#{branch}\"" options << "--depth 1" unless commit @build.git.run_clone repo_dir, url, options end def git_checkout_dependency(repo_dir, commit, branch) return unless commit @build.git.run_checkout_detach(repo_dir, commit) end end def enable_gems? !@gems.empty? end end # LoadGems end # MRuby nghttp2-1.68.0/third-party/mruby/lib/mruby/PaxHeaders/build.rb0000644000000000000000000000013215077107276021223 xustar0030 mtime=1761382078.103420643 30 atime=1761382080.124411398 30 ctime=1761382108.444302277 nghttp2-1.68.0/third-party/mruby/lib/mruby/build.rb0000644000175100017510000004204215077107276021615 0ustar00runnerrunnerrequire "mruby/core_ext" require "mruby/build/load_gems" require "mruby/build/command" autoload :Find, "find" module MRuby autoload :Gem, "mruby/gem" autoload :Lockfile, "mruby/lockfile" autoload :Presym, "mruby/presym" INSTALL_PREFIX = ENV['PREFIX'] || ENV['INSTALL_PREFIX'] || '/usr/local' INSTALL_DESTDIR = ENV['DESTDIR'] || '' class << self def targets @targets ||= {} end def each_target(&block) return to_enum(:each_target) if block.nil? @targets.each do |key, target| target.instance_eval(&block) end end end class Toolchain class << self attr_accessor :toolchains def guess if cc = ENV["CC"] || ENV["CXX"] return "clang" if cc.include?("clang") else return "clang" if RUBY_PLATFORM =~ /darwin|(?:free|open)bsd/ return "gcc" if RUBY_PLATFORM.include?("cygwin") return "visualcpp" if ENV.include?("VisualStudioVersion") return "visualcpp" if ENV.include?("VSINSTALLDIR") end "gcc" end end def initialize(name, &block) @name, @initializer = name.to_s, block MRuby::Toolchain.toolchains[@name] = self end def setup(conf, params={}) conf.instance_exec(conf, params, &@initializer) end self.toolchains = {} end class Build class << self attr_accessor :current def mruby_config_path path = ENV['MRUBY_CONFIG'] || ENV['CONFIG'] if path.nil? || path.empty? path = "#{MRUBY_ROOT}/build_config/default.rb" elsif !File.file?(path) && !Pathname.new(path).absolute? f = "#{MRUBY_ROOT}/build_config/#{path}.rb" path = File.exist?(f) ? f : File.extname(path).empty? ? f : path end path end def install_dir @install_dir ||= ENV['INSTALL_DIR'] || "#{MRUBY_ROOT}/bin" end end include Rake::DSL include LoadGems attr_accessor :name, :bins, :exts, :file_separator, :build_dir, :gem_clone_dir, :defines, :libdir_name attr_reader :products, :libmruby_core_objs, :libmruby_objs, :gems, :toolchains, :presym, :mrbc_build, :gem_dir_to_repo_url attr_reader :install_excludes alias libmruby libmruby_objs COMPILERS = %w(cc cxx objc asm) COMMANDS = COMPILERS + %w(linker archiver yacc gperf git exts mrbc) attr_block MRuby::Build::COMMANDS Exts = Struct.new(:object, :executable, :library, :presym_preprocessed) def initialize(name='host', build_dir=nil, internal: false, &block) @name = name.to_s unless current = MRuby.targets[@name] if ENV['OS'] == 'Windows_NT' @exts = Exts.new('.o', '.exe', '.a', '.pi') else @exts = Exts.new('.o', '', '.a', '.pi') end build_dir = build_dir || ENV['MRUBY_BUILD_DIR'] || "#{MRUBY_ROOT}/build" @file_separator = '/' @build_dir = "#{build_dir}/#{@name}" @gem_clone_dir = "#{build_dir}/repos/#{@name}" @libdir_name = (self.kind_of?(MRuby::CrossBuild) ? nil : ENV["MRUBY_SYSTEM_LIBDIR_NAME"]) || "lib" @install_prefix = nil @install_excludes = [] @defines = [] @cc = Command::Compiler.new(self, %w(.c), label: "CC") @cxx = Command::Compiler.new(self, %w(.cc .cxx .cpp), label: "CXX") @objc = Command::Compiler.new(self, %w(.m), label: "OBJC") @asm = Command::Compiler.new(self, %w(.S .asm .s), label: "ASM") @linker = Command::Linker.new(self) @archiver = Command::Archiver.new(self) @yacc = Command::Yacc.new(self) @gperf = Command::Gperf.new(self) @git = Command::Git.new(self) @mrbc = Command::Mrbc.new(self) @products = [] @bins = [] @gems = MRuby::Gem::List.new @libmruby_core_objs = [] @libmruby_objs = [@libmruby_core_objs] @enable_libmruby = true @build_mrbtest_lib_only = false @cxx_exception_enabled = false @cxx_exception_disabled = false @cxx_abi_enabled = false @enable_bintest = false @enable_test = false @enable_lock = true @enable_presym = true @enable_benchmark = true @mrbcfile_external = false @internal = internal @toolchains = [] @gem_dir_to_repo_url = {} # Add lambda instead of string because libdir_name or lib may be changed by user configuration libmruby_core_name = nil @install_excludes << ->(file) { libmruby_core_name ||= File.join(libdir_name, libfile("libmruby_core")) file == libmruby_core_name } MRuby.targets[@name] = current = self end MRuby::Build.current = current begin current.instance_eval(&block) ensure if current.libmruby_enabled? && !current.mrbcfile_external? if current.presym_enabled? current.create_mrbc_build if current.host? || current.gems["mruby-bin-mrbc"] elsif current.host? current.build_mrbc_exec end end current.presym = Presym.new(current) if current.presym_enabled? end end def libmruby_enabled? @enable_libmruby end def disable_libmruby @enable_libmruby = false end def debug_enabled? @enable_debug end def enable_debug compilers.each do |c| c.defines += %w(MRB_DEBUG) c.setup_debug(self) end @mrbc.compile_options += ' -g' @enable_debug = true end def presym_enabled? @enable_presym end def disable_presym if @enable_presym @enable_presym = false compilers.each{|c| c.defines << "MRB_NO_PRESYM"} end end def disable_lock @enable_lock = false end def lock_enabled? Lockfile.enabled? && @enable_lock end def disable_cxx_exception if @cxx_exception_enabled or @cxx_abi_enabled raise "cxx_exception already enabled" end @cxx_exception_disabled = true end def enable_cxx_exception return if @cxx_exception_enabled return if @cxx_abi_enabled if @cxx_exception_disabled raise "cxx_exception disabled" end @cxx_exception_enabled = true compilers.each { |c| c.defines += %w(MRB_USE_CXX_EXCEPTION) c.flags << c.cxx_exception_flag } linker.command = cxx.command if toolchains.find { |v| v == 'gcc' } end def cxx_exception_enabled? @cxx_exception_enabled end def cxx_abi_enabled? @cxx_abi_enabled end def enable_cxx_abi return if @cxx_abi_enabled if @cxx_exception_enabled raise "cxx_exception already enabled" end compilers.each { |c| c.defines += %w(MRB_USE_CXX_EXCEPTION MRB_USE_CXX_ABI) c.flags << c.cxx_compile_flag c.flags = c.flags.flatten - c.cxx_invalid_flags.flatten } linker.command = cxx.command if toolchains.find { |v| v == 'gcc' } @cxx_abi_enabled = true end def benchmark_enabled? @enable_benchmark end def disable_benchmark @enable_benchmark = false end def compile_as_cxx(src, cxx_src = nil, obj = nil, includes = []) # # If `cxx_src` is specified, this method behaves the same as before as # compatibility mode, but `.d` file is not read. # # If `cxx_src` is omitted, `.d` file is read by using mruby standard # Rake rule (C++ source name is also changed). # if cxx_src obj ||= cxx_src + @exts.object dsts = [obj] dsts << (cxx_src + @exts.presym_preprocessed) if presym_enabled? defines = [] include_paths = ["#{MRUBY_ROOT}/src", *includes] dsts.each do |dst| file dst => cxx_src do |t| cxx.run t.name, t.prerequisites.first, defines, include_paths end end else cxx_src = "#{build_dir}/#{src.relative_path.to_s.remove_leading_parents}".ext << "-cxx.cxx" obj = cxx_src.ext(@exts.object) end file cxx_src => [src, __FILE__] do |t| mkdir_p File.dirname t.name IO.write t.name, < 'mruby-bin-mrbc' unless @gems['mruby-bin-mrbc'] end def locks Lockfile.build(@name) end def mrbcfile return @mrbcfile if @mrbcfile gem_name = "mruby-bin-mrbc" if (gem = @gems[gem_name]) @mrbcfile = exefile("#{gem.build.build_dir}/bin/mrbc") elsif !host? && (host = MRuby.targets["host"]) if (gem = host.gems[gem_name]) @mrbcfile = exefile("#{gem.build.build_dir}/bin/mrbc") elsif host.mrbcfile_external? @mrbcfile = host.mrbcfile end end @mrbcfile || fail("external mrbc or mruby-bin-mrbc gem in current('#{@name}') or 'host' build is required") end def mrbcfile=(path) @mrbcfile = path @mrbcfile_external = true end def mrbcfile_external? @mrbcfile_external end def compilers COMPILERS.map do |c| instance_variable_get("@#{c}") end end def define_rules compilers.each do |compiler| compiler.defines << "MRB_NO_GEMS" unless enable_gems? && libmruby_enabled? end [@cc, *(@cxx if cxx_exception_enabled?)].each do |compiler| compiler.define_rules(@build_dir, MRUBY_ROOT, @exts.object) compiler.define_rules(@build_dir, MRUBY_ROOT, @exts.presym_preprocessed) if presym_enabled? end end def define_installer_outline(src, dst) file dst => src do _pp "GEN", src.relative_path, dst.relative_path mkdir_p(File.dirname(dst)) yield dst end dst end if ENV['OS'] == 'Windows_NT' def define_installer(src) dst = "#{self.class.install_dir}/#{File.basename(src)}".pathmap("%X.bat") define_installer_outline(src, dst) do File.write dst, <<~BATCHFILE @echo off call "#{File.expand_path(src)}" %* BATCHFILE end end else def define_installer(src) dst = "#{self.class.install_dir}/#{File.basename(src)}" define_installer_outline(src, dst) do File.unlink(dst) rescue nil File.symlink(src.relative_path_from(self.class.install_dir), dst) end end end def define_installer_if_needed(bin) exe = exefile("#{build_dir}/bin/#{bin}") host? ? define_installer(exe) : exe end def filename(name) if name.is_a?(Array) name.flatten.map { |n| filename(n) } else name.gsub('/', file_separator) end end def exefile(name) if name.is_a?(Array) name.flatten.map { |n| exefile(n) } elsif File.extname(name).empty? "#{name}#{exts.executable}" else # `name` sometimes have (non-standard) extension (e.g. `.bat`). name end end def objfile(name) if name.is_a?(Array) name.flatten.map { |n| objfile(n) } else "#{name}#{exts.object}" end end def libfile(name) if name.is_a?(Array) name.flatten.map { |n| libfile(n) } else "#{name}#{exts.library}" end end def build_mrbtest_lib_only @build_mrbtest_lib_only = true end def build_mrbtest_lib_only? @build_mrbtest_lib_only end def verbose_flag Rake.verbose ? ' -v' : '' end def run_test puts ">>> Test #{name} <<<" mrbtest = exefile("#{build_dir}/bin/mrbtest") sh "#{filename mrbtest.relative_path}#{verbose_flag}" puts end def run_bintest puts ">>> Bintest #{name} <<<" targets = @gems.select { |v| File.directory? "#{v.dir}/bintest" }.map { |v| filename v.dir } mrbc = @gems["mruby-bin-mrbc"] ? exefile("#{@build_dir}/bin/mrbc") : mrbcfile env = {"BUILD_DIR" => @build_dir, "MRBCFILE" => mrbc} bintest = File.join(MRUBY_ROOT, "test/bintest.rb") sh env, "ruby #{bintest}#{verbose_flag} #{targets.join ' '}" end def print_build_summary puts "================================================" puts " Config Name: #{@name}" puts " Output Directory: #{self.build_dir.relative_path}" puts " Binaries: #{@bins.join(', ')}" unless @bins.empty? unless @gems.empty? puts " Included Gems:" gems = @gems.sort_by { |gem| gem.name } gems.each do |gem| gem_version = " - #{gem.version}" if gem.version != '0.0.0' gem_summary = " - #{gem.summary}" if gem.summary puts " #{gem.name}#{gem_version}#{gem_summary}" puts " - Binaries: #{gem.bins.join(', ')}" unless gem.bins.empty? end end puts "================================================" puts end def libmruby_static libfile("#{build_dir}/#{libdir_name}/libmruby") end def libmruby_core_static libfile("#{build_dir}/#{libdir_name}/libmruby_core") end def libraries [libmruby_static] end def host? @name == "host" end def internal? @internal end def each_header_files(&block) return to_enum(__method__) unless block basedir = File.join(MRUBY_ROOT, "include") Find.find(basedir) do |d| next unless File.file? d yield d end @gems.each { |g| g.each_header_files(&block) } self end def install_prefix @install_prefix || (self.name == "host" ? MRuby::INSTALL_PREFIX : File.join(MRuby::INSTALL_PREFIX, "mruby/#{self.name}")) end def install_prefix=(dir) @install_prefix = dir&.to_s end protected attr_writer :presym def create_mrbc_build exclusions = %i[@name @build_dir @gems @enable_test @enable_bintest @internal @install_excludes] name = "#{@name}/mrbc" MRuby.targets.delete(name) build = self.class.new(name, internal: true){} build.build_dir = "#{@build_dir}/mrbc" instance_variables.each do |n| next if exclusions.include?(n) v = instance_variable_get(n) v = case v when nil, true, false, Numeric; v when String; v.clone when Command; v.clone.tap { |u| u.build = build } else Marshal.load(Marshal.dump(v)) # deep clone end build.instance_variable_set(n, v) end build.build_mrbc_exec build.disable_libmruby build.disable_presym @mrbc_build = build self.mrbcfile = build.mrbcfile build end end # Build class CrossBuild < Build attr_block %w(test_runner) # cross compiling targets for building native extensions. # host - arch of where the built binary will run # build - arch of the machine building the binary attr_accessor :host_target, :build_target def initialize(name, build_dir=nil, &block) @test_runner = Command::CrossTestRunner.new(self) super unless mrbcfile_external? || MRuby.targets['host'] # add minimal 'host' MRuby::Build.new('host') do |conf| conf.toolchain conf.build_mrbc_exec conf.disable_libmruby conf.disable_presym end end end def mrbcfile mrbcfile_external? ? super : MRuby::targets['host'].mrbcfile end def run_test @test_runner.runner_options << verbose_flag mrbtest = exefile("#{build_dir}/bin/mrbtest") if (@test_runner.command == nil) puts "You should run #{mrbtest} on target device." puts else @test_runner.run(mrbtest) end end def run_bintest puts ">>> Bintest #{name} <<<" targets = @gems.select { |v| File.directory? "#{v.dir}/bintest" }.map { |v| filename v.dir } mrbc = @gems["mruby-bin-mrbc"] ? exefile("#{@build_dir}/bin/mrbc") : mrbcfile emulator = @test_runner.command emulator = @test_runner.shellquote(emulator) if emulator env = { "BUILD_DIR" => @build_dir, "MRBCFILE" => mrbc, "EMULATOR" => @test_runner.emulator, } bintest = File.join(MRUBY_ROOT, "test/bintest.rb") sh env, "ruby #{bintest}#{verbose_flag} #{targets.join ' '}" end protected def create_mrbc_build; end end # CrossBuild end # MRuby nghttp2-1.68.0/third-party/mruby/lib/mruby/PaxHeaders/core_ext.rb0000644000000000000000000000013215077107276021734 xustar0030 mtime=1761382078.103420643 30 atime=1761382080.124411398 30 ctime=1761382108.445302274 nghttp2-1.68.0/third-party/mruby/lib/mruby/core_ext.rb0000644000175100017510000000310315077107276022321 0ustar00runnerrunnerautoload :Pathname, 'pathname' class Object class << self def attr_block(*syms) syms.flatten.each do |sym| class_eval "def #{sym}(&block);block.call(@#{sym}) if block_given?;@#{sym};end" end end end end class String def relative_path_from(dir) Pathname.new(File.expand_path(self)).relative_path_from(Pathname.new(File.expand_path(dir))).to_s end def relative_path relative_path_from(Dir.pwd) end def remove_leading_parents Pathname.new(".#{Pathname.new("/#{self}").cleanpath}").cleanpath.to_s end def replace_prefix_by(dirmap) [self].replace_prefix_by(dirmap)[0] end end class Array # Replace the prefix of each string that is a file path that contains in its own array. # # dirmap is a hash whose elements are `{ "path/to/old-prefix" => "path/to/new-prefix", ... }`. # If it does not match any element of dirmap, the file path is not replaced. def replace_prefix_by(dirmap) dirmap = dirmap.map { |older, newer| [File.join(older, "/"), File.join(newer, "/")] } dirmap.sort! dirmap.reverse! self.flatten.map do |e| map = dirmap.find { |older, newer| e.start_with?(older) } e = e.sub(map[0], map[1]) if map e end end end def install_D(src, dst) _pp "INSTALL", src.relative_path, dst.relative_path rm_f dst mkdir_p File.dirname(dst) cp src, dst end def _pp(cmd, src, tgt=nil, indent: nil) return if Rake.application.options.silent width = 5 template = indent ? "%#{width * indent}s %s %s" : "%-#{width}s %s %s" puts template % [cmd, src, tgt ? "-> #{tgt}" : nil] end nghttp2-1.68.0/third-party/mruby/PaxHeaders/LEGAL0000644000000000000000000000013015077107276016440 xustar0029 mtime=1761382078.09542068 29 atime=1761382080.11741143 30 ctime=1761382108.357302529 nghttp2-1.68.0/third-party/mruby/LEGAL0000644000175100017510000000504015077107276017031 0ustar00runnerrunnerLEGAL NOTICE INFORMATION ------------------------ All the files in this distribution are covered under the MIT license (see the file LICENSE) except some files mentioned below: - src/string.c (memsearch_swar): 2 clause BSD license code by Wojciech MuÅ‚a (@WojciechMula) - src/fmt_fp.c: public domain by Dave Hylands (@dhylands) - mrbgems/mruby-dir/src/Win/dirent.c: MIT-like license by Kevlin Henney [src/string.c] The implementation of mrb_memsearch_ss() is taken from https://github.com/WojciechMula/sse4-strstr.git Copyright (c) 2008-2016, Wojciech MuÅ‚a All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [src/fmt_fp.c] The code in this function was inspired from Fred Bayer's pdouble.c. Since pdouble.c was released as Public Domain, I'm releasing this code as public domain as well. Dave Hylands The original code can be found in https://github.com/dhylands/format-float [mrbgems/mruby-dir/src/Win/dirent.c] used only for Windows platform Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved. Permission to use, copy, modify, and distribute this software and its documentation for any purpose is hereby granted without fee, provided that this copyright and permissions notice appear in all copies and derivatives. This software is supplied "as is" without express or implied warranty. But that said, if there are any problems please get in touch. nghttp2-1.68.0/third-party/mruby/PaxHeaders/mrbgems0000644000000000000000000000013215077107334017245 xustar0030 mtime=1761382108.937300852 30 atime=1761382109.796298369 30 ctime=1761382108.937300852 nghttp2-1.68.0/third-party/mruby/mrbgems/0000755000175100017510000000000015077107334017712 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-eval0000644000000000000000000000013215077107334021330 xustar0030 mtime=1761382108.599301829 30 atime=1761382109.796298369 30 ctime=1761382108.599301829 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-eval/0000755000175100017510000000000015077107334021775 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-eval/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276023527 xustar0030 mtime=1761382078.118420575 30 atime=1761382080.139411329 30 ctime=1761382108.599301829 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-eval/mrbgem.rake0000644000175100017510000000065115077107276024121 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-eval') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'standard Kernel#eval method' add_dependency 'mruby-compiler', :core => 'mruby-compiler' add_dependency 'mruby-binding', :core => 'mruby-binding' spec.add_test_dependency('mruby-metaprog', :core => 'mruby-metaprog') spec.add_test_dependency('mruby-method', :core => 'mruby-method') end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-eval/PaxHeaders/test0000644000000000000000000000013215077107334022307 xustar0030 mtime=1761382108.602301821 30 atime=1761382109.796298369 30 ctime=1761382108.602301821 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-eval/test/0000755000175100017510000000000015077107334022754 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-eval/test/PaxHeaders/eval.rb0000644000000000000000000000013215077107276023645 xustar0030 mtime=1761382078.118420575 30 atime=1761382080.139411329 30 ctime=1761382108.602301821 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-eval/test/eval.rb0000644000175100017510000000762015077107276024242 0ustar00runnerrunner# Kernel.eval is not provided by mruby. '15.3.1.2.3' assert('Kernel#eval', '15.3.1.3.12') do assert_equal(10) { eval '1 * 10' } assert_equal('aaa') { eval "'a' * 3" } assert_equal(10) { a = 10 eval "a" } assert_equal(20) { a = 10 eval "a = 20" a } assert_equal(15) { c = 5 lambda { a = 10 eval "c = a + c" }.call c } assert_equal(5) { c = 5 lambda { eval 'lambda { c }.call' }.call } assert_equal(15) { c = 5 lambda { a = 10 eval 'lambda { c = a + c }.call' }.call c } assert_equal(2) { a = 10 eval 'def f(a); b=a+1; end' f(1) } end assert('rest arguments of eval') do assert_raise(TypeError) { eval('0', 0, 'test', 0) } assert_equal ['test', 'test.rb', 10] do eval('[\'test\', __FILE__, __LINE__]', nil, 'test.rb', 10) end end assert 'eval syntax error' do assert_raise(SyntaxError) do eval 'p "test' end end assert('String instance_eval') do obj = Object.new obj.instance_eval{ @test = 'test' } assert_raise(ArgumentError) { obj.instance_eval(0) { } } assert_raise(ArgumentError) { obj.instance_eval('0', 'test', 0, 'test') } assert_equal(['test.rb', 10]) { obj.instance_eval('[__FILE__, __LINE__]', 'test.rb', 10)} assert_equal('test') { obj.instance_eval('@test') } assert_equal('test') { obj.instance_eval { @test } } o = Object.new assert_equal ['', o, o], o.instance_eval("[''].each { |s| break [s, o, self] }") end assert('Kernel#eval(string) context') do class TestEvalConstScope EVAL_CONST_CLASS = 'class' def const_string eval 'EVAL_CONST_CLASS' end end obj = TestEvalConstScope.new assert_raise(NameError) { eval 'EVAL_CONST_CLASS' } assert_equal('class') { obj.const_string } end assert('BasicObject#instance_eval with begin-rescue-ensure execution order') do class HellRaiser def raise_hell order = [:enter_raise_hell] begin order.push :begin self.instance_eval("raise 'error'") rescue order.push :rescue ensure order.push :ensure end order end end hell_raiser = HellRaiser.new assert_equal([:enter_raise_hell, :begin, :rescue, :ensure], hell_raiser.raise_hell) end assert('BasicObject#instance_eval to define singleton methods Issue #3141') do foo_class = Class.new do def bar(x) instance_eval "def baz; #{x}; end" end end f1 = foo_class.new f2 = foo_class.new f1.bar 1 f2.bar 2 assert_equal(1){f1.baz} assert_equal(2){f2.baz} end assert('Kernel#eval(string) Issue #4021') do assert_equal('FOO') { (eval <<'EOS').call } foo = "FOO" Proc.new { foo } EOS assert_equal('FOO') { def do_eval(code) eval(code) end do_eval(<<'EOS').call foo = "FOO" Proc.new { foo } EOS } end assert('Calling the same method as the variable name') do hoge = Object.new def hoge.fuga "Hit!" end assert_equal("Hit!") { fuga = "Miss!"; eval "hoge.fuga" } assert_equal("Hit!") { fuga = "Miss!"; -> { eval "hoge.fuga" }.call } assert_equal("Hit!") { -> { fuga = "Miss!"; eval "hoge.fuga" }.call } assert_equal("Hit!") { fuga = "Miss!"; eval("-> { hoge.fuga }").call } end assert('Access numbered parameter from eval') do hoge = Object.new def hoge.fuga(a, &b) b.call(a) end assert_equal(6) { hoge.fuga(3) { _1 + eval("_1") } } end assert('Module#class_eval with string') do c = Class.new c.class_eval "def foo() 42; end" cc = c.new assert_true cc.respond_to?(:foo) assert_equal 42, c.new.foo b = c.class_eval("class A; def a; 55; end; end; class B; def b; A; end; end; B") assert_equal 55, b.new.b.new.a end assert 'method visibility with eval' do c = Class.new do eval <<~CODE private def bad! "BAD!" end CODE def good! "GOOD!" end end assert_raise NoMethodError do c.new.bad! end assert_equal "GOOD!" do c.new.good! end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-eval/test/PaxHeaders/binding.rb0000644000000000000000000000013215077107276024330 xustar0030 mtime=1761382078.118420575 30 atime=1761382080.139411329 30 ctime=1761382108.601301823 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-eval/test/binding.rb0000644000175100017510000000400715077107276024721 0ustar00runnerrunnerassert("Binding#eval") do b = nil 1.times { x, y, z = 1, 2, 3; [x,y,z]; b = binding } assert_equal([1, 2, 3], b.eval("[x, y, z]")) here = self assert_equal(here, b.eval("self")) end assert("Binding#local_variables") do block = Proc.new do |a| b = 1 binding end bind = block.call(0) assert_equal [:a, :b, :bind, :block], bind.local_variables.sort bind.eval("x = 2") assert_equal [:a, :b, :bind, :block, :x], bind.local_variables.sort end assert("Binding#local_variable_set") do bind = binding 1.times { assert_equal(9, bind.local_variable_set(:x, 9)) assert_equal(9, bind.eval("x")) assert_equal([:bind, :x], bind.eval("local_variables.sort")) } end assert("Binding#local_variable_get") do bind = binding x = 1 1.times { y = 2 assert_equal(1, bind.local_variable_get(:x)) x = 10 assert_equal(10, bind.local_variable_get(:x)) assert_raise(NameError) { bind.local_variable_get(:y) } bind.eval("z = 3") assert_equal(3, bind.local_variable_get(:z)) bind.eval("y = 5") assert_equal(5, bind.local_variable_get(:y)) assert_equal(2, y) } end assert "Binding#eval with Binding.new via UnboundMethod" do assert_raise(NoMethodError) { Class.instance_method(:new).bind_call(Binding) } end assert "Binding#eval with Binding.new via Method" do # The following test is OK if SIGSEGV does not occur cx = Class.new(Binding) cx.define_singleton_method(:allocate, &Object.method(:allocate)) Class.instance_method(:new).bind_call(cx).__send__(:eval,"") assert_true true end assert "access local variables into procs" do bx = binding block = bx.eval("a = 1; proc { a }") bx.eval("a = 2") assert_equal 2, block.call end assert "Binding#eval on another target class" do obj = Object.new Module.new do self::BINDING = obj.instance_eval { binding } def self.eval(code) self::BINDING.eval code end self.eval "def self.m1; :m1; end" self.eval "def m2; :m2; end" end assert_equal :m1, obj.m1 assert_equal :m2, obj.m2 end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-eval/PaxHeaders/src0000644000000000000000000000013215077107334022117 xustar0030 mtime=1761382108.603301818 30 atime=1761382109.796298369 30 ctime=1761382108.603301818 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-eval/src/0000755000175100017510000000000015077107334022564 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-eval/src/PaxHeaders/eval.c0000644000000000000000000000013215077107276023274 xustar0030 mtime=1761382078.118420575 30 atime=1761382080.139411329 30 ctime=1761382108.603301818 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-eval/src/eval.c0000644000175100017510000002355315077107276023674 0ustar00runnerrunner#include #include #include #include #include #include #include #include #include #include #include /* provided by mruby-binding */ mrb_bool mrb_binding_p(mrb_state *mrb, mrb_value binding); const struct RProc * mrb_binding_extract_proc(mrb_state *mrb, mrb_value binding); struct REnv * mrb_binding_extract_env(mrb_state *mrb, mrb_value binding); /* provided by mruby-compiler */ typedef mrb_bool mrb_parser_foreach_top_variable_func(mrb_state *mrb, mrb_sym sym, void *user); void mrb_parser_foreach_top_variable(mrb_state *mrb, struct mrb_parser_state *p, mrb_parser_foreach_top_variable_func *func, void *user); static struct RProc* create_proc_from_string(mrb_state *mrb, const char *s, mrb_int len, mrb_value binding, const char *file, mrb_int line) { mrb_ccontext *cxt; struct mrb_parser_state *p; struct RProc *proc; const struct RProc *scope; struct REnv *e; mrb_callinfo *ci; /* callinfo of eval caller */ struct RClass *target_class = NULL; struct mrb_context *c = mrb->c; if (!mrb_nil_p(binding)) { if (!mrb_binding_p(mrb, binding)) { mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %C (expected binding)", mrb_obj_class(mrb, binding)); } scope = mrb_binding_extract_proc(mrb, binding); if (MRB_PROC_CFUNC_P(scope)) { e = NULL; } else { e = mrb_binding_extract_env(mrb, binding); mrb_assert(e != NULL); } } else { ci = (c->ci > c->cibase) ? c->ci - 1 : c->cibase; scope = ci->proc; e = NULL; } if (file) { if (strlen(file) >= UINT16_MAX) { mrb_raise(mrb, E_ARGUMENT_ERROR, "filename too long"); } } else { file = "(eval)"; } cxt = mrb_ccontext_new(mrb); cxt->lineno = (uint16_t)line; mrb_ccontext_filename(mrb, cxt, file); cxt->capture_errors = TRUE; cxt->no_optimize = TRUE; cxt->upper = scope && MRB_PROC_CFUNC_P(scope) ? NULL : scope; p = mrb_parse_nstring(mrb, s, len, cxt); /* only occur when memory ran out */ if (!p) { mrb_ccontext_free(mrb, cxt); mrb_raise(mrb, E_RUNTIME_ERROR, "Failed to create parser state (out of memory)"); } if (0 < p->nerr) { /* parse error */ mrb_value str; mrb_ccontext_free(mrb, cxt); if (!p->error_buffer[0].message) { mrb_parser_free(p); mrb_raise(mrb, E_SYNTAX_ERROR, "compile error"); } if (file) { str = mrb_format(mrb, "file %s line %d: %s", file, p->error_buffer[0].lineno, p->error_buffer[0].message); } else { str = mrb_format(mrb, "line %d: %s", p->error_buffer[0].lineno, p->error_buffer[0].message); } mrb_parser_free(p); mrb_exc_raise(mrb, mrb_exc_new_str(mrb, E_SYNTAX_ERROR, str)); } proc = mrb_generate_code(mrb, p); if (proc == NULL) { /* codegen error */ mrb_parser_free(p); mrb_ccontext_free(mrb, cxt); mrb_raise(mrb, E_SCRIPT_ERROR, "codegen error"); } if (c->ci > c->cibase) { ci = &c->ci[-1]; } else { ci = c->cibase; } if (scope) { target_class = MRB_PROC_TARGET_CLASS(scope); if (!MRB_PROC_CFUNC_P(scope)) { if (e == NULL) { /* when `binding` is nil */ e = mrb_vm_ci_env(ci); if (e == NULL) { e = mrb_env_new(mrb, c, ci, ci->proc->body.irep->nlocals, ci->stack, target_class); ci->u.env = e; } } proc->e.env = e; proc->flags |= MRB_PROC_ENVSET; mrb_field_write_barrier(mrb, (struct RBasic*)proc, (struct RBasic*)e); } } proc->upper = scope; mrb_vm_ci_target_class_set(mrb->c->ci, target_class); /* mrb_codedump_all(mrb, proc); */ mrb_parser_free(p); mrb_ccontext_free(mrb, cxt); return proc; } static mrb_value exec_irep(mrb_state *mrb, mrb_value self, struct RProc *proc) { mrb_callinfo *ci = mrb->c->ci; /* no argument passed from eval() */ ci->n = 0; ci->nk = 0; /* clear visibility */ MRB_CI_SET_VISIBILITY_BREAK(ci); /* clear block */ ci->stack[1] = mrb_nil_value(); return mrb_exec_irep(mrb, self, proc); } static void binding_eval_error_check(mrb_state *mrb, struct mrb_parser_state *p, const char *file) { if (!p) { mrb_raise(mrb, E_RUNTIME_ERROR, "Failed to create parser state (out of memory)"); } if (0 < p->nerr) { mrb_value str; if (file) { str = mrb_format(mrb, "file %s line %d: %s", file, p->error_buffer[0].lineno, p->error_buffer[0].message); } else { str = mrb_format(mrb, "line %d: %s", p->error_buffer[0].lineno, p->error_buffer[0].message); } mrb_exc_raise(mrb, mrb_exc_new_str(mrb, E_SYNTAX_ERROR, str)); } } #define LV_BUFFERS 8 struct expand_lvspace { mrb_irep *irep; struct REnv *env; int numvar; mrb_sym syms[LV_BUFFERS]; }; static mrb_bool expand_lvspace(mrb_state *mrb, mrb_sym sym, void *user) { struct expand_lvspace *p = (struct expand_lvspace*)user; mrb_int symlen; const char *symname = mrb_sym_name_len(mrb, sym, &symlen); if (symname && symlen > 0) { if (symname[0] != '&' && symname[0] != '*') { p->syms[p->numvar++] = sym; if (p->numvar >= LV_BUFFERS) { mrb_proc_merge_lvar(mrb, p->irep, p->env, p->numvar, p->syms, NULL); p->numvar = 0; } } } return TRUE; } struct binding_eval_prepare_body { mrb_value binding; const char *file; mrb_ccontext *cxt; struct mrb_parser_state *pstate; }; static mrb_value binding_eval_prepare_body(mrb_state *mrb, void *opaque) { struct binding_eval_prepare_body *p = (struct binding_eval_prepare_body*)opaque; binding_eval_error_check(mrb, p->pstate, p->file); struct expand_lvspace args = { (mrb_irep*)p->cxt->upper->body.irep, mrb_binding_extract_env(mrb, p->binding), 0, { 0 } }; mrb_parser_foreach_top_variable(mrb, p->pstate, expand_lvspace, &args); if (args.numvar > 0) { mrb_proc_merge_lvar(mrb, args.irep, args.env, args.numvar, args.syms, NULL); } return mrb_nil_value(); } static void binding_eval_prepare(mrb_state *mrb, mrb_value binding, const char *expr, mrb_int exprlen, const char *file) { struct binding_eval_prepare_body d = { binding }; const struct RProc *proc = mrb_binding_extract_proc(mrb, binding); mrb_assert(!MRB_PROC_CFUNC_P(proc)); d.cxt = mrb_ccontext_new(mrb); d.file = mrb_ccontext_filename(mrb, d.cxt, file ? file : "(eval)"); d.cxt->capture_errors = TRUE; d.cxt->upper = proc; d.pstate = mrb_parse_nstring(mrb, expr, exprlen, d.cxt); mrb_bool error; mrb_value ret = mrb_protect_error(mrb, binding_eval_prepare_body, &d, &error); if (d.pstate) mrb_parser_free(d.pstate); if (d.cxt) mrb_ccontext_free(mrb, d.cxt); if (error) mrb_exc_raise(mrb, ret); } static mrb_value f_eval(mrb_state *mrb, mrb_value self) { const char *s; mrb_int len; mrb_value binding = mrb_nil_value(); const char *file = NULL; mrb_int line = 1; struct RProc *proc; mrb_get_args(mrb, "s|ozi", &s, &len, &binding, &file, &line); if (!mrb_nil_p(binding)) { binding_eval_prepare(mrb, binding, s, len, file); } proc = create_proc_from_string(mrb, s, len, binding, file, line); if (!mrb_nil_p(binding)) { self = mrb_iv_get(mrb, binding, MRB_SYM(recv)); } mrb_assert(!MRB_PROC_CFUNC_P(proc)); return exec_irep(mrb, self, proc); } static mrb_value f_instance_eval(mrb_state *mrb, mrb_value self) { if (!mrb_block_given_p(mrb)) { const char *s; mrb_int len; const char *file = NULL; mrb_int line = 1; struct RClass *c; struct RProc *proc; mrb_get_args(mrb, "s|zi", &s, &len, &file, &line); c = mrb_singleton_class_ptr(mrb, self); proc = create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line); MRB_PROC_SET_TARGET_CLASS(proc, c); mrb_assert(!MRB_PROC_CFUNC_P(proc)); mrb_vm_ci_target_class_set(mrb->c->ci, c); return exec_irep(mrb, self, proc); } else { mrb_get_args(mrb, ""); return mrb_obj_instance_eval(mrb, self); } } static mrb_value f_class_eval(mrb_state *mrb, mrb_value self) { if (!mrb_block_given_p(mrb)) { const char *s; mrb_int len; const char *file = NULL; mrb_int line = 1; struct RProc *proc; mrb_get_args(mrb, "s|zi", &s, &len, &file, &line); proc = create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line); MRB_PROC_SET_TARGET_CLASS(proc, mrb_class_ptr(self)); mrb_assert(!MRB_PROC_CFUNC_P(proc)); mrb_vm_ci_target_class_set(mrb->c->ci, mrb_class_ptr(self)); return exec_irep(mrb, self, proc); } else { mrb_get_args(mrb, ""); return mrb_mod_module_eval(mrb, self); } } static mrb_value mrb_binding_eval(mrb_state *mrb, mrb_value binding) { mrb_callinfo *ci = mrb->c->ci; int argc = ci->n; mrb_value *argv = ci->stack + 1; if (argc < 15) { argv[0] = mrb_ary_new_from_values(mrb, argc, argv); argv[1] = argv[argc]; /* copy block */ ci->n = 15; } mrb_ary_splice(mrb, argv[0], 1, 0, binding); /* insert binding as 2nd argument */ return f_eval(mrb, binding); } void mrb_mruby_eval_gem_init(mrb_state* mrb) { mrb_define_private_method_id(mrb, mrb->kernel_module, MRB_SYM(eval), f_eval, MRB_ARGS_ARG(1, 3)); mrb_define_method_id(mrb, mrb_class_get_id(mrb, MRB_SYM(BasicObject)), MRB_SYM(instance_eval), f_instance_eval, MRB_ARGS_OPT(3)|MRB_ARGS_BLOCK()); mrb_define_method_id(mrb, mrb->module_class, MRB_SYM(module_eval), f_class_eval, MRB_ARGS_OPT(3)|MRB_ARGS_BLOCK()); mrb_define_method_id(mrb, mrb->module_class, MRB_SYM(class_eval), f_class_eval, MRB_ARGS_OPT(3)|MRB_ARGS_BLOCK()); struct RClass *binding = mrb_class_get_id(mrb, MRB_SYM(Binding)); mrb_define_method_id(mrb, binding, MRB_SYM(eval), mrb_binding_eval, MRB_ARGS_ANY()); } void mrb_mruby_eval_gem_final(mrb_state* mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-pack0000644000000000000000000000013015077107334021315 xustar0029 mtime=1761382108.64730169 30 atime=1761382109.796298369 29 ctime=1761382108.64730169 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-pack/0000755000175100017510000000000015077107334021764 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-pack/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276023516 xustar0030 mtime=1761382078.124420547 30 atime=1761382080.143411311 30 ctime=1761382108.646301694 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-pack/mrbgem.rake0000644000175100017510000000032015077107276024101 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-pack') do |spec| spec.license = 'MIT' spec.authors = ['Internet Initiative Japan Inc.', 'mruby developers'] spec.summary = 'Array#pack and String#unpack method' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-pack/PaxHeaders/README.md0000644000000000000000000000013115077107276022657 xustar0030 mtime=1761382078.124420547 30 atime=1761382080.143411311 29 ctime=1761382108.64730169 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-pack/README.md0000644000175100017510000000554015077107276023254 0ustar00runnerrunner# mruby-pack (pack / unpack) mruby-pack provides `Array#pack` and `String#unpack` for mruby. ## Installation Add the line below into your build configuration: ``` conf.gem :core => 'mruby-pack' ``` There is no dependency on other mrbgems. ## Supported template string - A : arbitrary binary string (space padded, count is width) - a : arbitrary binary string (null padded, count is width) - B : bit string (descending) - b : bit string (ascending) - C : 8-bit unsigned (unsigned char) - c : 8-bit signed (signed char) - D, d: 64-bit float, native format - E : 64-bit float, little endian byte order - e : 32-bit float, little endian byte order - F, f: 32-bit float, native format - G : 64-bit float, network (big-endian) byte order - g : 32-bit float, network (big-endian) byte order - H : hex string (high nibble first) - h : hex string (low nibble first) - I : unsigned integer, native endian (`unsigned int` in C) - i : signed integer, native endian (`int` in C) - J : unsigned integer, native endian (`uintptr_t` in C) - j : signed integer, native endian (`intptr_t` in C) - L : 32-bit unsigned, native endian (`uint32_t`) - l : 32-bit signed, native endian (`int32_t`) - m : base64 encoded string (see RFC 2045, count is width) - N : 32-bit unsigned, network (big-endian) byte order - n : 16-bit unsigned, network (big-endian) byte order - Q : 64-bit unsigned, native endian (`uint64_t`) - q : 64-bit signed, native endian (`int64_t`) - S : 16-bit unsigned, native endian (`uint16_t`) - s : 16-bit signed, native endian (`int16_t`) - U : UTF-8 character - V : 32-bit unsigned, VAX (little-endian) byte order - v : 16-bit unsigned, VAX (little-endian) byte order - x : null byte - X : back up bytes - Z : same as "a", except that null is added with \* - @ : absolute position ## License Copyright (c) 2012 Internet Initiative Japan Inc. Copyright (c) 2017 mruby developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-pack/PaxHeaders/test0000644000000000000000000000013215077107334022276 xustar0030 mtime=1761382108.649301685 30 atime=1761382109.796298369 30 ctime=1761382108.649301685 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-pack/test/0000755000175100017510000000000015077107334022743 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-pack/test/PaxHeaders/pack.rb0000644000000000000000000000013215077107276023623 xustar0030 mtime=1761382078.124420547 30 atime=1761382080.144411306 30 ctime=1761382108.649301685 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-pack/test/pack.rb0000644000175100017510000001241315077107276024214 0ustar00runnerrunnerPACK_IS_LITTLE_ENDIAN = "\x01\00".unpack('S')[0] == 0x01 def assert_pack tmpl, packed, unpacked t = tmpl.inspect assert "assert_pack" do assert_equal packed, unpacked.pack(tmpl), "#{unpacked.inspect}.pack(#{t})" assert_equal unpacked, packed.unpack(tmpl), "#{packed.inspect}.unpack(#{t})" end end # pack & unpack 'm' (base64) assert('pack("m")') do assert_pack "m", "", [""] assert_pack "m", "AA==\n", ["\0"] assert_pack "m", "AAA=\n", ["\0\0"] assert_pack "m", "AAAA\n", ["\0\0\0"] assert_pack "m", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n", ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"] ary = ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"] assert_equal ary, "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n".unpack("m") assert_equal ary, "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg==\n".unpack("m") assert_equal "QQ==\n", ["A", "B"].pack("m50") assert_equal ["A"], "QQ==\n".unpack("m50") assert_equal "QQ==Qg==", ["A", "B"].pack("m0 m0") assert_equal ["A", "B"], "QQ==Qg==".unpack("m10 m10") assert_pack "m0", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg==", ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"] end # pack & unpack 'M' (Quoted-printable) assert('pack("M")') do assert_pack "M", "123=\n", ["123"] assert_pack "M", "=3D\n", ["=\n"] assert_pack "M", "=E3=81=82=\n", ["ã‚"] assert_equal ["123"], "123=\n".unpack("M") assert_equal ["=\n"], "=3D\n".unpack("M") assert_equal ["ã‚"], "=E3=81=82=\n".unpack("M") end # pack & unpack 'B'/'b' assert('pack("B/b")') do assert_pack "b*", "\xFF\x00", ["1111111100000000"] assert_pack "b*", "\x01\x02", ["1000000001000000"] assert_pack "b3", "\x01", ["100"] assert_pack "B*", "\xFF\x00", ["1111111100000000"] assert_pack "B*", "\x01\x02", ["0000000100000010"] end # pack & unpack 'H' assert('pack("H")') do assert_pack "H*", "01", ["3031"] assert_pack "H*", "\020", ["10"] end assert('pack("C")') do assert_pack "C*", "\x00\x01\x7F\x80\xFF", [0, 1, 127, 128, 255] end assert('pack("a")') do assert_equal "a", ["abc"].pack("a") assert_equal "abc", ["abc"].pack("a*") assert_equal "abc\0", ["abc"].pack("a4") assert_equal ["abc\0"], "abc\0".unpack("a4") assert_equal ["abc "], "abc ".unpack("a4") end assert('pack("A")') do assert_equal "a", ["abc"].pack("A") assert_equal "abc", ["abc"].pack("A*") assert_equal "abc ", ["abc"].pack("A4") assert_equal ["abc"], "abc\0".unpack("A4") assert_equal ["abc"], "abc ".unpack("A4") end # regression tests assert('issue #1') do assert_equal "\000\001\000\002", [1, 2].pack("nn") end assert 'pack float' do skip unless Object.const_defined?(:Float) assert_pack 'e', "\x00\x00@@", [3.0] assert_pack 'g', "@@\x00\x00", [3.0] if PACK_IS_LITTLE_ENDIAN assert_pack 'f', "\x00\x00@@", [3.0] assert_pack 'F', "\x00\x00@@", [3.0] else assert_pack 'f', "@@\x00\x00", [3.0] assert_pack 'F', "@@\x00\x00", [3.0] end end assert 'pack double' do skip unless Object.const_defined?(:Float) assert_pack 'E', "\x00\x00\x00\x00\x00\x00\b@", [3.0] assert_pack 'G', "@\b\x00\x00\x00\x00\x00\x00", [3.0] if PACK_IS_LITTLE_ENDIAN assert_pack 'd', "\x00\x00\x00\x00\x00\x00\b@", [3.0] assert_pack 'D', "\x00\x00\x00\x00\x00\x00\b@", [3.0] else assert_pack 'd', "@\b\x00\x00\x00\x00\x00\x00", [3.0] assert_pack 'D', "@\b\x00\x00\x00\x00\x00\x00", [3.0] end end assert 'pack/unpack "i"' do int_size = [0].pack('i').size raise "pack('i').size is too small (#{int_size})" if int_size < 2 if PACK_IS_LITTLE_ENDIAN str = "\xC7\xCF" + "\xFF" * (int_size-2) else str = "\xFF" * (int_size-2) + "\xCF\xC7" end assert_pack 'i', str, [-12345] end assert 'pack/unpack "I"' do uint_size = [0].pack('I').size raise "pack('I').size is too small (#{uint_size})" if uint_size < 2 if PACK_IS_LITTLE_ENDIAN str = "\x39\x30" + "\0" * (uint_size-2) else str = "\0" * (uint_size-2) + "\x30\x39" end assert_pack 'I', str, [12345] end assert 'pack/unpack "w"' do for x in [0,1,127,128,16383,16384,65535,65536] assert_equal [x], [x].pack("w").unpack("w") end end assert 'pack/unpack "U"' do assert_equal [], "".unpack("U") assert_equal [], "".unpack("U*") assert_equal [65, 66], "ABC".unpack("U2") assert_equal [12371, 12435, 12395, 12385, 12399, 19990, 30028], "ã“ã‚“ã«ã¡ã¯ä¸–界".unpack("U*") assert_equal "", [].pack("U") assert_equal "", [].pack("U*") assert_equal "AB", [65, 66, 67].pack("U2") assert_equal "ã“ã‚“ã«ã¡ã¯ä¸–界", [12371, 12435, 12395, 12385, 12399, 19990, 30028].pack("U*") assert_equal "\000", [0].pack("U") assert_raise(RangeError) { [-0x40000000].pack("U") } assert_raise(RangeError) { [-1].pack("U") } assert_raise(RangeError) { [0x40000000].pack("U") } end assert 'unpack1' do d = 1234 assert_equal(d, [d].pack("i").unpack1("i")) d = "foobar" assert_equal(d, [d].pack("a*").unpack1("a*")) assert_equal(d, [d].pack("A*").unpack1("A*")) assert_equal(d, [d].pack("Z*").unpack1("Z*")) assert_equal(d, [d].pack("m").unpack1("m")) assert_equal(d, [d].pack("M").unpack1("M")) d = "10010101" assert_equal(d, [d].pack("b*").unpack1("b*")) d = "f00b00" assert_equal(d, [d].pack("h*").unpack1("h*")) end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-pack/PaxHeaders/src0000644000000000000000000000013215077107334022106 xustar0030 mtime=1761382108.650301682 30 atime=1761382109.796298369 30 ctime=1761382108.650301682 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-pack/src/0000755000175100017510000000000015077107334022553 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-pack/src/PaxHeaders/pack.c0000644000000000000000000000013215077107276023252 xustar0030 mtime=1761382078.124420547 30 atime=1761382080.143411311 30 ctime=1761382108.650301682 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-pack/src/pack.c0000644000175100017510000012144115077107276023645 0ustar00runnerrunner/* ** pack.c - Array#pack, String#unpack */ #include #include #include #include #include #include #include #include #include #include #include #define INT_OVERFLOW_P(n) ((n) < MRB_INT_MIN || (n) > MRB_INT_MAX) #define UINT_OVERFLOW_P(n) ((n) > MRB_INT_MAX) #ifndef EOF # define EOF (-1) /* for MRB_NO_STDIO */ #endif struct tmpl { mrb_value str; int idx; }; enum pack_dir { PACK_DIR_CHAR, /* C */ PACK_DIR_SHORT, /* S */ PACK_DIR_LONG, /* L */ PACK_DIR_QUAD, /* Q */ //PACK_DIR_INT, /* i */ //PACK_DIR_VAX, PACK_DIR_BER, /* w */ PACK_DIR_UTF8, /* U */ PACK_DIR_DOUBLE, /* E */ PACK_DIR_FLOAT, /* f */ PACK_DIR_STR, /* A */ PACK_DIR_HEX, /* h */ PACK_DIR_BSTR, /* b */ PACK_DIR_BASE64, /* m */ PACK_DIR_QENC, /* M */ PACK_DIR_NUL, /* x */ PACK_DIR_BACK, /* X */ PACK_DIR_ABS, /* @ */ PACK_DIR_NONE, /* - */ }; enum pack_type { PACK_TYPE_INTEGER, PACK_TYPE_FLOAT, PACK_TYPE_STRING, PACK_TYPE_NONE }; #define PACK_FLAG_s 0x00000001 /* native size ("_" "!") */ #define PACK_FLAG_a 0x00000002 /* null padding ("a") */ #define PACK_FLAG_Z 0x00000004 /* append nul char ("z") */ #define PACK_FLAG_SIGNED 0x00000008 /* native size ("_" "!") */ #define PACK_FLAG_GT 0x00000010 /* big endian (">") */ #define PACK_FLAG_LT 0x00000020 /* little endian ("<") */ #define PACK_FLAG_WIDTH 0x00000040 /* "count" is "width" */ #define PACK_FLAG_LSB 0x00000080 /* LSB / low nibble first */ #define PACK_FLAG_COUNT2 0x00000100 /* "count" is special... */ #define PACK_FLAG_LITTLEENDIAN 0x00000200 /* little endian actually */ #define PACK_BASE64_IGNORE 0xff #define PACK_BASE64_PADDING 0xfe #define IGN PACK_BASE64_IGNORE #define PAD PACK_BASE64_PADDING static const unsigned char base64chars[64] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', }; static const unsigned char base64_dec_tab[128] = { IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, IGN, 62, IGN, IGN, IGN, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, IGN, IGN, IGN, PAD, IGN, IGN, IGN, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, IGN, IGN, IGN, IGN, IGN, IGN, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, IGN, IGN, IGN, IGN, IGN, }; static int hex2int(unsigned char ch) { if (ch >= '0' && ch <= '9') return ch - '0'; else if (ch >= 'A' && ch <= 'F') return 10 + (ch - 'A'); else if (ch >= 'a' && ch <= 'f') return 10 + (ch - 'a'); else return -1; } static mrb_value str_len_ensure(mrb_state *mrb, mrb_value str, mrb_int len) { mrb_int n = RSTRING_LEN(str); if (len < 0) { mrb_raise(mrb, E_RANGE_ERROR, "negative (or overflowed) integer"); } if (len > n) { do { n *= 2; } while (len > n); str = mrb_str_resize(mrb, str, n); } return str; } static int pack_char(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags) { str = str_len_ensure(mrb, str, sidx + 1); RSTRING_PTR(str)[sidx] = (char)mrb_integer(o); return 1; } static int unpack_char(mrb_state *mrb, const void *src, int srclen, mrb_value ary, unsigned int flags) { if (flags & PACK_FLAG_SIGNED) mrb_ary_push(mrb, ary, mrb_fixnum_value(*(signed char*)src)); else mrb_ary_push(mrb, ary, mrb_fixnum_value(*(unsigned char*)src)); return 1; } static int pack_short(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags) { uint16_t n; str = str_len_ensure(mrb, str, sidx + 2); n = (uint16_t)mrb_integer(o); if (flags & PACK_FLAG_LITTLEENDIAN) { RSTRING_PTR(str)[sidx+0] = n % 256; RSTRING_PTR(str)[sidx+1] = n / 256; } else { RSTRING_PTR(str)[sidx+0] = n / 256; RSTRING_PTR(str)[sidx+1] = n % 256; } return 2; } static int unpack_short(mrb_state *mrb, const unsigned char *src, int srclen, mrb_value ary, unsigned int flags) { int n; if (flags & PACK_FLAG_LITTLEENDIAN) { n = src[1] * 256 + src[0]; } else { n = src[0] * 256 + src[1]; } if ((flags & PACK_FLAG_SIGNED) && (n >= 0x8000)) { n -= 0x10000; } mrb_ary_push(mrb, ary, mrb_fixnum_value(n)); return 2; } static int pack_long(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags) { uint32_t n; str = str_len_ensure(mrb, str, sidx + 4); n = (uint32_t)mrb_integer(o); if (flags & PACK_FLAG_LITTLEENDIAN) { RSTRING_PTR(str)[sidx+0] = (char)(n & 0xff); RSTRING_PTR(str)[sidx+1] = (char)(n >> 8); RSTRING_PTR(str)[sidx+2] = (char)(n >> 16); RSTRING_PTR(str)[sidx+3] = (char)(n >> 24); } else { RSTRING_PTR(str)[sidx+0] = (char)(n >> 24); RSTRING_PTR(str)[sidx+1] = (char)(n >> 16); RSTRING_PTR(str)[sidx+2] = (char)(n >> 8); RSTRING_PTR(str)[sidx+3] = (char)(n & 0xff); } return 4; } #ifndef MRB_INT64 static void u32tostr(char *buf, size_t len, uint32_t n) { #ifdef MRB_NO_STDIO char *bufend = buf + len; char *p = bufend - 1; if (len < 1) { return; } *p-- = '\0'; len--; if (n > 0) { for (; len > 0 && n > 0; len --, n /= 10) { *p -- = '0' + (n % 10); } p++; } else if (len > 0) { *p = '0'; len--; } memmove(buf, p, bufend - p); #else snprintf(buf, len, "%" PRIu32, n); #endif /* MRB_NO_STDIO */ } #endif /* MRB_INT64 */ static int unpack_long(mrb_state *mrb, const unsigned char *src, int srclen, mrb_value ary, unsigned int flags) { #ifndef MRB_INT64 char msg[60]; #endif uint32_t ul; mrb_int n; if (flags & PACK_FLAG_LITTLEENDIAN) { ul = (uint32_t)src[3] * 256*256*256; ul += (uint32_t)src[2] *256*256; ul += (uint32_t)src[1] *256; ul += (uint32_t)src[0]; } else { ul = (uint32_t)src[0] * 256*256*256; ul += (uint32_t)src[1] *256*256; ul += (uint32_t)src[2] *256; ul += (uint32_t)src[3]; } if (flags & PACK_FLAG_SIGNED) { n = (int32_t)ul; } else { #ifndef MRB_INT64 if (UINT_OVERFLOW_P(ul)) { u32tostr(msg, sizeof(msg), ul); mrb_raisef(mrb, E_RANGE_ERROR, "cannot unpack to Integer: %s", msg); } #endif n = ul; } mrb_ary_push(mrb, ary, mrb_int_value(mrb, n)); return 4; } static int pack_quad(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags) { uint64_t n; str = str_len_ensure(mrb, str, sidx + 8); n = (uint64_t)mrb_integer(o); if (flags & PACK_FLAG_LITTLEENDIAN) { RSTRING_PTR(str)[sidx+0] = (char)(n & 0xff); RSTRING_PTR(str)[sidx+1] = (char)(n >> 8); RSTRING_PTR(str)[sidx+2] = (char)(n >> 16); RSTRING_PTR(str)[sidx+3] = (char)(n >> 24); RSTRING_PTR(str)[sidx+4] = (char)(n >> 32); RSTRING_PTR(str)[sidx+5] = (char)(n >> 40); RSTRING_PTR(str)[sidx+6] = (char)(n >> 48); RSTRING_PTR(str)[sidx+7] = (char)(n >> 56); } else { RSTRING_PTR(str)[sidx+0] = (char)(n >> 56); RSTRING_PTR(str)[sidx+1] = (char)(n >> 48); RSTRING_PTR(str)[sidx+2] = (char)(n >> 40); RSTRING_PTR(str)[sidx+3] = (char)(n >> 32); RSTRING_PTR(str)[sidx+4] = (char)(n >> 24); RSTRING_PTR(str)[sidx+5] = (char)(n >> 16); RSTRING_PTR(str)[sidx+6] = (char)(n >> 8); RSTRING_PTR(str)[sidx+7] = (char)(n & 0xff); } return 8; } static void u64tostr(char *buf, size_t len, uint64_t n) { #ifdef MRB_NO_STDIO mrb_assert(len > 0); if (n < 10) { buf[0] = '0' + n; buf[1] = '\0'; return; } char *bufend = buf + len; char *p = bufend - 1; *p-- = '\0'; len--; for (; len > 0 && n > 0; len--, n /= 10) { *p-- = '0' + (n % 10); } p++; memmove(buf, p, bufend - p); #else snprintf(buf, len, "%" PRIu64, n); #endif /* MRB_NO_STDIO */ } #ifndef MRB_INT64 static void i64tostr(char *buf, size_t len, int64_t n) { #ifdef MRB_NO_STDIO mrb_assert(len > 0); if (n < 0) { *buf++ = '-'; len--; n = -n; } u64tostr(buf, len, (uint64_t)n); #else snprintf(buf, len, "%" PRId64, n); #endif /* MRB_NO_STDIO */ } #endif /* MRB_INT64 */ static int unpack_quad(mrb_state *mrb, const unsigned char *src, int srclen, mrb_value ary, unsigned int flags) { char msg[60]; uint64_t ull; int i, pos, step; mrb_int n; if (flags & PACK_FLAG_LITTLEENDIAN) { pos = 7; step = -1; } else { pos = 0; step = 1; } ull = 0; for (i = 0; i < 8; i++) { ull = ull * 256 + (uint64_t)src[pos]; pos += step; } if (flags & PACK_FLAG_SIGNED) { int64_t sll = ull; #ifndef MRB_INT64 if (INT_OVERFLOW_P(sll)) { i64tostr(msg, sizeof(msg), sll); mrb_raisef(mrb, E_RANGE_ERROR, "cannot unpack to Integer: %s", msg); } #endif n = (mrb_int)sll; } else { if (UINT_OVERFLOW_P(ull)) { u64tostr(msg, sizeof(msg), ull); mrb_raisef(mrb, E_RANGE_ERROR, "cannot unpack to Integer: %s", msg); } n = (mrb_int)ull; } mrb_ary_push(mrb, ary, mrb_int_value(mrb, n)); return 8; } static int pack_BER(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags) { mrb_int n = mrb_integer(o); int i; char *p; if (n < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "can't compress negative numbers"); } for (i=1; i<(int)sizeof(mrb_int)+1; i++) { mrb_int mask = ~((1L<<(7*i))-1); if ((n & mask) == 0) break; } str = str_len_ensure(mrb, str, sidx + i); p = RSTRING_PTR(str)+sidx; for (size_t j=i; j>0; p++,j--) { mrb_int x = (n>>(7*(j-1)))&0x7f; *p = (char)x; if (j > 1) *p |= 0x80; } return i; } static int unpack_BER(mrb_state *mrb, const unsigned char *src, int srclen, mrb_value ary, unsigned int flags) { int i; mrb_int n = 0; const unsigned char *p = src; const unsigned char *e = p + srclen; if (srclen == 0) return 0; for (i=1; p (MRB_INT_MAX>>7)) { mrb_raise(mrb, E_RANGE_ERROR, "BER unpacking 'w' overflow"); } n <<= 7; n |= *p & 0x7f; if ((*p & 0x80) == 0) break; } mrb_ary_push(mrb, ary, mrb_int_value(mrb, n)); return i; } #ifndef MRB_NO_FLOAT static int pack_double(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags) { int i; double d; uint8_t *buffer = (uint8_t*)&d; str = str_len_ensure(mrb, str, sidx + 8); d = mrb_float(o); if (flags & PACK_FLAG_LITTLEENDIAN) { if (littleendian) { memcpy(RSTRING_PTR(str) + sidx, buffer, 8); } else { for (i = 0; i < 8; i++) { RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1]; } } } else { if (littleendian) { for (i = 0; i < 8; i++) { RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1]; } } else { memcpy(RSTRING_PTR(str) + sidx, buffer, 8); } } return 8; } static int unpack_double(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value ary, unsigned int flags) { int i; double d; uint8_t *buffer = (uint8_t*)&d; if (flags & PACK_FLAG_LITTLEENDIAN) { if (littleendian) { memcpy(buffer, src, 8); } else { for (i = 0; i < 8; i++) { buffer[8 - i - 1] = src[i]; } } } else { if (littleendian) { for (i = 0; i < 8; i++) { buffer[8 - i - 1] = src[i]; } } else { memcpy(buffer, src, 8); } } mrb_ary_push(mrb, ary, mrb_float_value(mrb, d)); return 8; } static int pack_float(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags) { int i; float f; uint8_t *buffer = (uint8_t*)&f; str = str_len_ensure(mrb, str, sidx + 4); f = (float)mrb_float(o); if (flags & PACK_FLAG_LITTLEENDIAN) { if (littleendian) { memcpy(RSTRING_PTR(str) + sidx, buffer, 4); } else { for (i = 0; i < 4; i++) { RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1]; } } } else { if (littleendian) { for (i = 0; i < 4; i++) { RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1]; } } else { memcpy(RSTRING_PTR(str) + sidx, buffer, 4); } } return 4; } static int unpack_float(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value ary, unsigned int flags) { int i; float f; uint8_t *buffer = (uint8_t*)&f; if (flags & PACK_FLAG_LITTLEENDIAN) { if (littleendian) { memcpy(buffer, src, 4); } else { for (i = 0; i < 4; i++) { buffer[4 - i - 1] = src[i]; } } } else { if (littleendian) { for (i = 0; i < 4; i++) { buffer[4 - i - 1] = src[i]; } } else { memcpy(buffer, src, 4); } } mrb_ary_push(mrb, ary, mrb_float_value(mrb, f)); return 4; } #endif static int pack_utf8(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, int count, unsigned int flags) { char utf8[4]; int len = 0; uint32_t c = 0; c = (uint32_t)mrb_integer(o); /* Unicode character */ /* from mruby-compiler gem */ if (c < 0x80) { utf8[0] = (char)c; len = 1; } else if (c < 0x800) { utf8[0] = (char)(0xC0 | (c >> 6)); utf8[1] = (char)(0x80 | (c & 0x3F)); len = 2; } else if (c < 0x10000) { utf8[0] = (char)(0xE0 | (c >> 12) ); utf8[1] = (char)(0x80 | ((c >> 6) & 0x3F)); utf8[2] = (char)(0x80 | ( c & 0x3F)); len = 3; } else if (c < 0x200000) { utf8[0] = (char)(0xF0 | (c >> 18) ); utf8[1] = (char)(0x80 | ((c >> 12) & 0x3F)); utf8[2] = (char)(0x80 | ((c >> 6) & 0x3F)); utf8[3] = (char)(0x80 | ( c & 0x3F)); len = 4; } else { mrb_raise(mrb, E_RANGE_ERROR, "pack(U): value out of range"); } str = str_len_ensure(mrb, str, sidx + len); memcpy(RSTRING_PTR(str) + sidx, utf8, len); return len; } static const unsigned long utf8_limits[] = { 0x0, /* 1 */ 0x80, /* 2 */ 0x800, /* 3 */ 0x10000, /* 4 */ 0x200000, /* 5 */ 0x4000000, /* 6 */ 0x80000000, /* 7 */ }; static unsigned long utf8_to_uv(mrb_state *mrb, const char *p, long *lenp) { int c = *p++ & 0xff; unsigned long uv = c; long n = 1; if (!(uv & 0x80)) { *lenp = 1; return uv; } if (!(uv & 0x40)) { *lenp = 1; malformed: mrb_raise(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character"); } if (!(uv & 0x20)) { n = 2; uv &= 0x1f; } else if (!(uv & 0x10)) { n = 3; uv &= 0x0f; } else if (!(uv & 0x08)) { n = 4; uv &= 0x07; } else if (!(uv & 0x04)) { n = 5; uv &= 0x03; } else if (!(uv & 0x02)) { n = 6; uv &= 0x01; } else { *lenp = 1; goto malformed; } if (n > *lenp) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character (expected %d bytes, given %d bytes)", n, *lenp); } *lenp = n--; if (n != 0) { while (n--) { c = *p++ & 0xff; if ((c & 0xc0) != 0x80) { *lenp -= n + 1; goto malformed; } else { c &= 0x3f; uv = uv << 6 | c; } } } n = *lenp - 1; if (uv < utf8_limits[n]) { mrb_raise(mrb, E_ARGUMENT_ERROR, "redundant UTF-8 sequence"); } return uv; } static int unpack_utf8(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value ary, unsigned int flags) { unsigned long uv; long lenp = srclen; if (srclen == 0) { return 1; } uv = utf8_to_uv(mrb, (const char*)src, &lenp); mrb_ary_push(mrb, ary, mrb_fixnum_value((mrb_int)uv)); return (int)lenp; } static int pack_str(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, int count, unsigned int flags) { mrb_int copylen, slen, padlen; char *dptr, *dptr0, pad, *sptr; sptr = RSTRING_PTR(src); slen = RSTRING_LEN(src); if ((flags & PACK_FLAG_a) || (flags & PACK_FLAG_Z)) pad = '\0'; else pad = ' '; if (count == 0) { return 0; } else if (count == -1) { copylen = slen; padlen = (flags & PACK_FLAG_Z) ? 1 : 0; } else if (count < slen) { copylen = count; padlen = 0; } else { copylen = slen; padlen = count - slen; } dst = str_len_ensure(mrb, dst, didx + copylen + padlen); dptr0 = dptr = RSTRING_PTR(dst) + didx; memcpy(dptr, sptr, copylen); dptr += copylen; while (padlen-- > 0) { *dptr++ = pad; } return (int)(dptr - dptr0); } #define CHECK_UNPACK_LEN(mrb, slen, ary) do {\ if ((slen) <= 0) {\ mrb_ary_push(mrb, ary, mrb_str_new(mrb, 0, 0));\ return 0;\ }\ } while (0) static int unpack_str(mrb_state *mrb, const void *src, int slen, mrb_value ary, int count, unsigned int flags) { CHECK_UNPACK_LEN(mrb, slen, ary); mrb_value dst; const char *sptr; int copylen; sptr = (const char*)src; if (count != -1 && count < slen) { slen = count; } copylen = slen; if (slen >= 0 && flags & PACK_FLAG_Z) { /* "Z" */ const char *cp; if ((cp = (const char*)memchr(sptr, '\0', slen)) != NULL) { copylen = (int)(cp - sptr); if (count == -1) { slen = copylen + 1; } } } else if (!(flags & PACK_FLAG_a)) { /* "A" */ while (copylen > 0 && (sptr[copylen - 1] == '\0' || ISSPACE(sptr[copylen - 1]))) { copylen--; } } if (copylen < 0) copylen = 0; dst = mrb_str_new(mrb, sptr, (mrb_int)copylen); mrb_ary_push(mrb, ary, dst); return slen; } static int pack_hex(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, int count, unsigned int flags) { unsigned int ashift, bshift; long slen; char *dptr, *dptr0, *sptr; sptr = RSTRING_PTR(src); slen = (long)RSTRING_LEN(src); if (flags & PACK_FLAG_LSB) { ashift = 0; bshift = 4; } else { ashift = 4; bshift = 0; } if (count == -1) { count = slen; } else if (slen > count) { slen = count; } dst = str_len_ensure(mrb, dst, didx + count); dptr = RSTRING_PTR(dst) + didx; dptr0 = dptr; for (; count > 0; count -= 2) { int a = 0, b = 0; if (slen > 0) { a = hex2int(*sptr++); if (a < 0) break; slen--; } if (slen > 0) { b = hex2int(*sptr++); if (b < 0) break; slen--; } *dptr++ = (a << ashift) + (b << bshift); } return (int)(dptr - dptr0); } static int unpack_hex(mrb_state *mrb, const void *src, int slen, mrb_value ary, int count, unsigned int flags) { CHECK_UNPACK_LEN(mrb, slen, ary); mrb_value dst; int a, ashift, b, bshift; const char *sptr, *sptr0; char *dptr, *dptr0; const char hexadecimal[] = "0123456789abcdef"; if (flags & PACK_FLAG_LSB) { ashift = 0; bshift = 4; } else { ashift = 4; bshift = 0; } sptr = (const char*)src; if (count == -1) count = slen * 2; dst = mrb_str_new(mrb, NULL, count); dptr = RSTRING_PTR(dst); sptr0 = sptr; dptr0 = dptr; while (slen > 0 && count > 0) { a = (*sptr >> ashift) & 0x0f; b = (*sptr >> bshift) & 0x0f; sptr++; slen--; *dptr++ = hexadecimal[a]; count--; if (count > 0) { *dptr++ = hexadecimal[b]; count--; } } dst = mrb_str_resize(mrb, dst, (mrb_int)(dptr - dptr0)); mrb_ary_push(mrb, ary, dst); return (int)(sptr - sptr0); } static int pack_bstr(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, int count, unsigned int flags) { const char *sptr = RSTRING_PTR(src); int slen = (int)RSTRING_LEN(src); if (count == -1) { count = slen; } else if (slen > count) { slen = count; } dst = str_len_ensure(mrb, dst, didx + count); char *dptr = RSTRING_PTR(dst) + didx; char *dptr0 = dptr; unsigned int byte = 0; for (int i=0; i++ < slen; sptr++) { if (flags & PACK_FLAG_LSB) { if (*sptr & 1) byte |= 128; if (i & 7) byte >>= 1; else { char c = (char)(byte&0xff); *dptr++ = c; byte = 0; } } else { byte |= *sptr & 1; if (i & 7) byte <<= 1; else { char c = (char)(byte&0xff); *dptr++ = c; byte = 0; } } } if (slen & 7) { if (flags & PACK_FLAG_LSB) { byte >>= 7 - (slen & 7); } else { byte <<= 7 - (slen & 7); } char c = (char)(byte&0xff); *dptr++ = c; } return (int)(dptr - dptr0); } static int unpack_bstr(mrb_state *mrb, const void *src, int slen, mrb_value ary, int count, unsigned int flags) { CHECK_UNPACK_LEN(mrb, slen, ary); const char *sptr0 = (const char*)src; const char *sptr = sptr0; if (count == -1 || count > slen * 8) count = slen * 8; mrb_value dst = mrb_str_new(mrb, NULL, count); char *dptr = RSTRING_PTR(dst); const char *dptr0 = dptr; int bits = 0; for (int i=0; i>= 1; else bits = (unsigned char)*sptr++; *dptr++ = (bits & 1) ? '1' : '0'; } else { if (i & 7) bits <<= 1; else bits = (unsigned char)*sptr++; *dptr++ = (bits & 128) ? '1' : '0'; } } dst = mrb_str_resize(mrb, dst, (mrb_int)(dptr - dptr0)); mrb_ary_push(mrb, ary, dst); return (int)(sptr - sptr0); } static int pack_base64(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, int count) { mrb_int dstlen; unsigned long l; mrb_int column, srclen; char *srcptr, *dstptr, *dstptr0; srcptr = RSTRING_PTR(src); srclen = RSTRING_LEN(src); if (srclen == 0) /* easy case */ return 0; if (count != 0 && count < 3) { /* -1, 1 or 2 */ count = 45; } else if (count >= 3) { count -= count % 3; } dstlen = (srclen+2) / 3 * 4; if (count > 0) { dstlen += (srclen / count) + ((srclen % count) == 0 ? 0 : 1); } dst = str_len_ensure(mrb, dst, didx + dstlen); dstptr = RSTRING_PTR(dst) + didx; dstptr0 = dstptr; for (column = 3; srclen >= 3; srclen -= 3, column += 3) { l = (unsigned char)*srcptr++ << 16; l += (unsigned char)*srcptr++ << 8; l += (unsigned char)*srcptr++; *dstptr++ = base64chars[(l >> 18) & 0x3f]; *dstptr++ = base64chars[(l >> 12) & 0x3f]; *dstptr++ = base64chars[(l >> 6) & 0x3f]; *dstptr++ = base64chars[ l & 0x3f]; if (column == count) { *dstptr++ = '\n'; column = 0; } } if (srclen == 1) { l = (unsigned char)*srcptr++ << 16; *dstptr++ = base64chars[(l >> 18) & 0x3f]; *dstptr++ = base64chars[(l >> 12) & 0x3f]; *dstptr++ = '='; *dstptr++ = '='; column += 3; } else if (srclen == 2) { l = (unsigned char)*srcptr++ << 16; l += (unsigned char)*srcptr++ << 8; *dstptr++ = base64chars[(l >> 18) & 0x3f]; *dstptr++ = base64chars[(l >> 12) & 0x3f]; *dstptr++ = base64chars[(l >> 6) & 0x3f]; *dstptr++ = '='; column += 3; } if (column > 0 && count > 0) { *dstptr++ = '\n'; } return (int)(dstptr - dstptr0); } static int unpack_base64(mrb_state *mrb, const void *src, int slen, mrb_value ary) { CHECK_UNPACK_LEN(mrb, slen, ary); mrb_value dst; int dlen; unsigned long l; int i, padding; unsigned char c, ch[4]; const char *sptr, *sptr0; char *dptr, *dptr0; sptr0 = sptr = (const char*)src; dlen = slen / 4 * 3; /* an estimated value - may be shorter */ dst = mrb_str_new(mrb, NULL, dlen); dptr0 = dptr = RSTRING_PTR(dst); padding = 0; while (slen >= 4) { for (i = 0; i < 4; i++) { do { if (slen-- == 0) goto done; c = *sptr++; if (c >= sizeof(base64_dec_tab)) continue; ch[i] = base64_dec_tab[c]; if (ch[i] == PACK_BASE64_PADDING) { ch[i] = 0; padding++; } } while (c >= sizeof(base64_dec_tab) || ch[i] == PACK_BASE64_IGNORE); } l = (ch[0] << 18) + (ch[1] << 12) + (ch[2] << 6) + ch[3]; if (padding == 0) { *dptr++ = (l >> 16) & 0xff; *dptr++ = (l >> 8) & 0xff; *dptr++ = l & 0xff; } else if (padding == 1) { *dptr++ = (l >> 16) & 0xff; *dptr++ = (l >> 8) & 0xff; break; } else { *dptr++ = (l >> 16) & 0xff; break; } } done: dst = mrb_str_resize(mrb, dst, (mrb_int)(dptr - dptr0)); mrb_ary_push(mrb, ary, dst); return (int)(sptr - sptr0); } static int pack_qenc(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, int count) { static const char hex_table[] = "0123456789ABCDEF"; char buff[1024]; char *s = RSTRING_PTR(src); char *send = s + RSTRING_LEN(src); int i = 0, n = 0, prev = EOF; int dlen = 0; if (count <= 1) count = 72; while (s < send) { if ((*s > 126) || (*s < 32 && *s != '\n' && *s != '\t') || (*s == '=')) { buff[i++] = '='; buff[i++] = hex_table[(*s & 0xf0) >> 4]; buff[i++] = hex_table[*s & 0x0f]; n += 3; prev = EOF; } else if (*s == '\n') { if (prev == ' ' || prev == '\t') { buff[i++] = '='; buff[i++] = *s; } buff[i++] = *s; n = 0; prev = *s; } else { buff[i++] = *s; n++; prev = *s; } if (n > count) { buff[i++] = '='; buff[i++] = '\n'; n = 0; prev = '\n'; } if (i > 1024 - 5) { str_len_ensure(mrb, dst, didx+dlen+i); memcpy(RSTRING_PTR(dst)+didx+dlen, buff, i); dlen += i; i = 0; } s++; } if (n > 0) { buff[i++] = '='; buff[i++] = '\n'; } if (i > 0) { str_len_ensure(mrb, dst, didx+dlen+i); memcpy(RSTRING_PTR(dst)+didx+dlen, buff, i); dlen += i; } return dlen; } static int unpack_qenc(mrb_state *mrb, const void *src, int slen, mrb_value ary) { CHECK_UNPACK_LEN(mrb, slen, ary); mrb_value buf = mrb_str_new(mrb, 0, slen); const char *s = (const char*)src, *ss = s; const char *send = s + slen; char *ptr = RSTRING_PTR(buf); int c1, c2; while (s < send) { if (*s == '=') { if (++s == send) break; if (s+1 < send && *s == '\r' && *(s+1) == '\n') s++; if (*s != '\n') { if ((c1 = hex2int(*s)) == -1) break; if (++s == send) break; if ((c2 = hex2int(*s)) == -1) break; *ptr++ = (char)(c1 << 4 | c2); } } else { *ptr++ = *s; } s++; ss = s; } buf = mrb_str_resize(mrb, buf, (mrb_int)(ptr - RSTRING_PTR(buf))); mrb_str_cat(mrb, buf, ss, send-ss); mrb_ary_push(mrb, ary, buf); return slen; } static int pack_nul(mrb_state *mrb, mrb_value dst, mrb_int didx, int count) { long i; dst = str_len_ensure(mrb, dst, didx + count); for (i = 0; i < count; i++) { RSTRING_PTR(dst)[didx + i] = '\0'; } return count; } static void check_x(mrb_state *mrb, mrb_int a, mrb_int count, char c) { if (a < count) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "%c outside of string", c); } } static void prepare_tmpl(mrb_state *mrb, struct tmpl *tmpl) { mrb_get_args(mrb, "S", &tmpl->str); tmpl->idx = 0; } static int has_tmpl(const struct tmpl *tmpl) { return (tmpl->idx < RSTRING_LEN(tmpl->str)); } static enum pack_dir read_tmpl(mrb_state *mrb, struct tmpl *tmpl, enum pack_type *typep, mrb_int *sizep, mrb_int *countp, unsigned int *flagsp) { mrb_int t, tlen; int ch, size = 0; enum pack_dir dir; enum pack_type type; int count = 1; unsigned int flags = 0; const char *tptr; tptr = RSTRING_PTR(tmpl->str); tlen = RSTRING_LEN(tmpl->str); restart: if (tmpl->idx >= tlen) return PACK_DIR_NONE; t = tptr[tmpl->idx++]; alias: switch (t) { case 'A': dir = PACK_DIR_STR; type = PACK_TYPE_STRING; flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2; break; case 'a': dir = PACK_DIR_STR; type = PACK_TYPE_STRING; flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2 | PACK_FLAG_a; break; case 'C': dir = PACK_DIR_CHAR; type = PACK_TYPE_INTEGER; size = 1; break; case 'c': dir = PACK_DIR_CHAR; type = PACK_TYPE_INTEGER; size = 1; flags |= PACK_FLAG_SIGNED; break; case 'D': case 'd': dir = PACK_DIR_DOUBLE; type = PACK_TYPE_FLOAT; size = 8; flags |= PACK_FLAG_SIGNED; break; case 'F': case 'f': dir = PACK_DIR_FLOAT; type = PACK_TYPE_FLOAT; size = 4; flags |= PACK_FLAG_SIGNED; break; case 'E': dir = PACK_DIR_DOUBLE; type = PACK_TYPE_FLOAT; size = 8; flags |= PACK_FLAG_SIGNED | PACK_FLAG_LT; break; case 'e': dir = PACK_DIR_FLOAT; type = PACK_TYPE_FLOAT; size = 4; flags |= PACK_FLAG_SIGNED | PACK_FLAG_LT; break; case 'G': dir = PACK_DIR_DOUBLE; type = PACK_TYPE_FLOAT; size = 8; flags |= PACK_FLAG_SIGNED | PACK_FLAG_GT; break; case 'g': dir = PACK_DIR_FLOAT; type = PACK_TYPE_FLOAT; size = 4; flags |= PACK_FLAG_SIGNED | PACK_FLAG_GT; break; case 'H': dir = PACK_DIR_HEX; type = PACK_TYPE_STRING; flags |= PACK_FLAG_COUNT2; break; case 'h': dir = PACK_DIR_HEX; type = PACK_TYPE_STRING; flags |= PACK_FLAG_COUNT2 | PACK_FLAG_LSB; break; case 'B': dir = PACK_DIR_BSTR; type = PACK_TYPE_STRING; flags |= PACK_FLAG_COUNT2; break; case 'b': dir = PACK_DIR_BSTR; type = PACK_TYPE_STRING; flags |= PACK_FLAG_COUNT2 | PACK_FLAG_LSB; break; case 'I': switch (sizeof(int)) { case 2: t = 'S'; goto alias; case 4: t = 'L'; goto alias; case 8: t = 'Q'; goto alias; default: mrb_raisef(mrb, E_RUNTIME_ERROR, "mruby-pack does not support sizeof(int) == %d", (int)sizeof(int)); } break; case 'i': switch (sizeof(int)) { case 2: t = 's'; goto alias; case 4: t = 'l'; goto alias; case 8: t = 'q'; goto alias; default: mrb_raisef(mrb, E_RUNTIME_ERROR, "mruby-pack does not support sizeof(int) == %d", (int)sizeof(int)); } break; case 'J': switch (sizeof(intptr_t)) { case 4: t = 'L'; goto alias; case 8: t = 'Q'; goto alias; default: mrb_raisef(mrb, E_RUNTIME_ERROR, "mruby-pack does not support sizeof(uintptr_t) == %d", (int)sizeof(uintptr_t)); } break; case 'j': switch (sizeof(intptr_t)) { case 4: t = 'l'; goto alias; case 8: t = 'q'; goto alias; default: mrb_raisef(mrb, E_RUNTIME_ERROR, "mruby-pack does not support sizeof(intptr_t) == %d", (int)sizeof(intptr_t)); } break; case 'L': dir = PACK_DIR_LONG; type = PACK_TYPE_INTEGER; size = 4; break; case 'l': dir = PACK_DIR_LONG; type = PACK_TYPE_INTEGER; size = 4; flags |= PACK_FLAG_SIGNED; break; case 'w': dir = PACK_DIR_BER; type = PACK_TYPE_INTEGER; flags |= PACK_FLAG_SIGNED; break; case 'm': dir = PACK_DIR_BASE64; type = PACK_TYPE_STRING; flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2; break; case 'M': dir = PACK_DIR_QENC; type = PACK_TYPE_STRING; flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2; break; case 'N': /* = "L>" */ dir = PACK_DIR_LONG; type = PACK_TYPE_INTEGER; size = 4; flags |= PACK_FLAG_GT; break; case 'n': /* = "S>" */ dir = PACK_DIR_SHORT; type = PACK_TYPE_INTEGER; size = 2; flags |= PACK_FLAG_GT; break; case 'Q': dir = PACK_DIR_QUAD; type = PACK_TYPE_INTEGER; size = 8; break; case 'q': dir = PACK_DIR_QUAD; type = PACK_TYPE_INTEGER; size = 8; flags |= PACK_FLAG_SIGNED; break; case 'S': dir = PACK_DIR_SHORT; type = PACK_TYPE_INTEGER; size = 2; break; case 's': dir = PACK_DIR_SHORT; type = PACK_TYPE_INTEGER; size = 2; flags |= PACK_FLAG_SIGNED; break; case 'U': dir = PACK_DIR_UTF8; type = PACK_TYPE_INTEGER; break; case 'V': /* = "L<" */ dir = PACK_DIR_LONG; type = PACK_TYPE_INTEGER; size = 4; flags |= PACK_FLAG_LT; break; case 'v': /* = "S<" */ dir = PACK_DIR_SHORT; type = PACK_TYPE_INTEGER; size = 2; flags |= PACK_FLAG_LT; break; case 'x': dir = PACK_DIR_NUL; type = PACK_TYPE_NONE; break; case 'X': dir = PACK_DIR_BACK; type = PACK_TYPE_NONE; break; case '@': dir = PACK_DIR_ABS; type = PACK_TYPE_NONE; break; case 'Z': dir = PACK_DIR_STR; type = PACK_TYPE_STRING; flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2 | PACK_FLAG_Z; break; case '#': while (++tmpl->idx < tlen && tptr[tmpl->idx] != '\n') ; goto restart; case 'p': case 'P': case '%': mrb_raisef(mrb, E_ARGUMENT_ERROR, "%c is not supported", (char)t); break; default: if (!ISSPACE((char)t)) { char c = (char)t; mrb_value s = mrb_str_new(mrb, &c, 1); mrb_raisef(mrb, E_ARGUMENT_ERROR, "unknown unpack directive %!v", s); } goto restart; } /* read suffix [0-9*_!<>] */ while (tmpl->idx < tlen) { ch = tptr[tmpl->idx]; if (ISDIGIT(ch)) { char *e; mrb_int n; if (!mrb_read_int(tptr+tmpl->idx, tptr+tlen, &e, &n) || INT_MAX < n) { mrb_raise(mrb, E_RUNTIME_ERROR, "too big template length"); } count = (int)n; tmpl->idx = (int)(e - tptr); continue; } else if (ch == '*') { if (type == PACK_TYPE_NONE) count = 0; else count = -1; } else if (ch == '_' || ch == '!' || ch == '<' || ch == '>') { if (strchr("sSiIlLqQ", (int)t) == NULL) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "'%c' allowed only after types sSiIlLqQ", ch); } if (ch == '_' || ch == '!') { flags |= PACK_FLAG_s; } else if (ch == '<') { flags |= PACK_FLAG_LT; } else if (ch == '>') { flags |= PACK_FLAG_GT; } } else { break; } tmpl->idx++; } if ((flags & PACK_FLAG_LT) || (!(flags & PACK_FLAG_GT) && littleendian)) { flags |= PACK_FLAG_LITTLEENDIAN; } *typep = type; *sizep = size; *countp = count; *flagsp = flags; return dir; } static mrb_value mrb_pack_pack(mrb_state *mrb, mrb_value ary) { mrb_value o, result; struct tmpl tmpl; enum pack_type type; mrb_int count, size; unsigned int flags; enum pack_dir dir; prepare_tmpl(mrb, &tmpl); result = mrb_str_new(mrb, NULL, 128); /* allocate initial buffer */ mrb_int aidx = 0; mrb_int ridx = 0; while (has_tmpl(&tmpl)) { dir = read_tmpl(mrb, &tmpl, &type, &size, &count, &flags); if (dir == PACK_DIR_NONE) break; if (dir == PACK_DIR_NUL) { grow: if (ridx > INT_MAX - count) goto overflow; ridx += pack_nul(mrb, result, ridx, count); continue; } else if (dir == PACK_DIR_BACK) { check_x(mrb, ridx, count, 'X'); ridx -= count; continue; } else if (dir == PACK_DIR_ABS) { count -= ridx; if (count > 0) goto grow; count = -count; check_x(mrb, ridx, count, '@'); ridx -= count; continue; } if ((flags & PACK_FLAG_WIDTH) && aidx >= RARRAY_LEN(ary)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments"); } for (; aidx < RARRAY_LEN(ary); aidx++) { if (count == 0 && !(flags & PACK_FLAG_WIDTH)) break; o = RARRAY_PTR(ary)[aidx]; if (type == PACK_TYPE_INTEGER) { o = mrb_ensure_int_type(mrb, o); } #ifndef MRB_NO_FLOAT else if (type == PACK_TYPE_FLOAT) { if (!mrb_float_p(o)) { o = mrb_ensure_float_type(mrb, o); } } #endif else if (type == PACK_TYPE_STRING) { if (!mrb_string_p(o)) { mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %T into String", o); } } switch (dir) { case PACK_DIR_CHAR: ridx += pack_char(mrb, o, result, ridx, flags); break; case PACK_DIR_SHORT: ridx += pack_short(mrb, o, result, ridx, flags); break; case PACK_DIR_LONG: ridx += pack_long(mrb, o, result, ridx, flags); break; case PACK_DIR_QUAD: ridx += pack_quad(mrb, o, result, ridx, flags); break; case PACK_DIR_BER: ridx += pack_BER(mrb, o, result, ridx, flags); break; case PACK_DIR_BASE64: ridx += pack_base64(mrb, o, result, ridx, count); break; case PACK_DIR_QENC: ridx += pack_qenc(mrb, o, result, ridx, count); break; case PACK_DIR_HEX: ridx += pack_hex(mrb, o, result, ridx, count, flags); break; case PACK_DIR_BSTR: ridx += pack_bstr(mrb, o, result, ridx, count, flags); break; case PACK_DIR_STR: ridx += pack_str(mrb, o, result, ridx, count, flags); break; #ifndef MRB_NO_FLOAT case PACK_DIR_DOUBLE: ridx += pack_double(mrb, o, result, ridx, flags); break; case PACK_DIR_FLOAT: ridx += pack_float(mrb, o, result, ridx, flags); break; #endif case PACK_DIR_UTF8: ridx += pack_utf8(mrb, o, result, ridx, count, flags); break; default: break; } if (flags & PACK_FLAG_COUNT2) { /* always consumes 1 entry */ aidx++; break; } if (count > 0) { count--; } } if (ridx < 0) { overflow: mrb_raise(mrb, E_RANGE_ERROR, "negative (or overflowed) template size"); } } mrb_str_resize(mrb, result, ridx); return result; } static mrb_value pack_unpack(mrb_state *mrb, mrb_value str, mrb_bool single) { mrb_value result; struct tmpl tmpl; mrb_int count; unsigned int flags; enum pack_dir dir; enum pack_type type; mrb_int size, srcidx, srclen; const unsigned char *sptr; prepare_tmpl(mrb, &tmpl); srcidx = 0; srclen = (int)RSTRING_LEN(str); result = mrb_ary_new(mrb); while (has_tmpl(&tmpl)) { dir = read_tmpl(mrb, &tmpl, &type, &size, &count, &flags); if (dir == PACK_DIR_NONE) break; if (dir == PACK_DIR_NUL) { check_x(mrb, srclen-srcidx, count, 'x'); srcidx += count; continue; } else if (dir == PACK_DIR_BACK) { check_x(mrb, srcidx, count, 'X'); srcidx -= count; continue; } else if (dir == PACK_DIR_ABS) { check_x(mrb, srclen, count, '@'); srcidx = count; continue; } /* PACK_FLAG_COUNT2 directions */ sptr = (const unsigned char*)RSTRING_PTR(str) + srcidx; switch (dir) { case PACK_DIR_HEX: srcidx += unpack_hex(mrb, sptr, srclen - srcidx, result, count, flags); if (single) goto single_return; continue; case PACK_DIR_BSTR: srcidx += unpack_bstr(mrb, sptr, srclen - srcidx, result, count, flags); if (single) goto single_return; continue; case PACK_DIR_STR: srcidx += unpack_str(mrb, sptr, srclen - srcidx, result, count, flags); if (single) goto single_return; continue; case PACK_DIR_BASE64: srcidx += unpack_base64(mrb, sptr, srclen - srcidx, result); if (single) goto single_return; continue; case PACK_DIR_QENC: srcidx += unpack_qenc(mrb, sptr, srclen - srcidx, result); if (single) goto single_return; continue; default: break; } while (count != 0 && srcidx < srclen) { if (srclen - srcidx < size) { while (count-- > 0) { mrb_ary_push(mrb, result, mrb_nil_value()); } break; } sptr = (const unsigned char*)RSTRING_PTR(str) + srcidx; switch (dir) { case PACK_DIR_CHAR: srcidx += unpack_char(mrb, sptr, srclen - srcidx, result, flags); break; case PACK_DIR_SHORT: srcidx += unpack_short(mrb, sptr, srclen - srcidx, result, flags); break; case PACK_DIR_LONG: srcidx += unpack_long(mrb, sptr, srclen - srcidx, result, flags); break; case PACK_DIR_QUAD: srcidx += unpack_quad(mrb, sptr, srclen - srcidx, result, flags); break; case PACK_DIR_BER: srcidx += unpack_BER(mrb, sptr, srclen - srcidx, result, flags); break; #ifndef MRB_NO_FLOAT case PACK_DIR_FLOAT: srcidx += unpack_float(mrb, sptr, srclen - srcidx, result, flags); break; case PACK_DIR_DOUBLE: srcidx += unpack_double(mrb, sptr, srclen - srcidx, result, flags); break; #endif case PACK_DIR_UTF8: srcidx += unpack_utf8(mrb, sptr, srclen - srcidx, result, flags); break; default: mrb_raise(mrb, E_RUNTIME_ERROR, "mruby-pack's bug"); } if (count > 0) { count--; } } } if (single) { single_return: if (RARRAY_LEN(result) > 0) { return RARRAY_PTR(result)[0]; } return mrb_nil_value(); } return result; } static mrb_value mrb_pack_unpack(mrb_state *mrb, mrb_value str) { return pack_unpack(mrb, str, FALSE); } static mrb_value mrb_pack_unpack1(mrb_state *mrb, mrb_value str) { return pack_unpack(mrb, str, TRUE); } void mrb_mruby_pack_gem_init(mrb_state *mrb) { mrb_define_method_id(mrb, mrb->array_class, MRB_SYM(pack), mrb_pack_pack, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, mrb->string_class, MRB_SYM(unpack), mrb_pack_unpack, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, mrb->string_class, MRB_SYM(unpack1), mrb_pack_unpack1, MRB_ARGS_REQ(1)); } void mrb_mruby_pack_gem_final(mrb_state *mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-bin-debugger0000644000000000000000000000013215077107334022733 xustar0030 mtime=1761382108.885301003 30 atime=1761382109.796298369 30 ctime=1761382108.885301003 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/0000755000175100017510000000000015077107334023400 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/PaxHeaders/bintest0000644000000000000000000000013215077107334024403 xustar0030 mtime=1761382108.887300997 30 atime=1761382109.796298369 30 ctime=1761382108.887300997 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/bintest/0000755000175100017510000000000015077107334025050 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/bintest/PaxHeaders/print.rb0000644000000000000000000000013215077107276026146 xustar0030 mtime=1761382078.105420634 30 atime=1761382080.126411389 30 ctime=1761382108.887300997 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/bintest/print.rb0000644000175100017510000004236315077107276026546 0ustar00runnerrunnerrequire 'open3' require 'tempfile' require 'strscan' class BinTest_MrubyBinDebugger # @debug1=false # @debug2=true def self.test(rubysource, testcase) script, bin = Tempfile.new(['test', '.rb']), Tempfile.new(['test', '.mrb']) # .rb script.write rubysource script.flush # compile `#{cmd("mrbc")} -g -o "#{bin.path}" "#{script.path}"` # add mrdb quit testcase << {:cmd=>"quit"} stdin_data = testcase.map{|t| t[:cmd]}.join("\n") << "\n" prompt = /^\(#{Regexp.escape(script.path)}:\d+\) / ["#{cmd('mrdb')} #{script.path}", "#{cmd('mrdb')} -b #{bin.path}"].each do |cmd| o, s = Open3.capture2(cmd, :stdin_data => stdin_data) scanner = StringScanner.new(o) scanner.skip_until(prompt) testcase.each do |tc| exp = tc[:exp] if exp act = scanner.scan_until(/\n/) break unless assert_operator act, :start_with?, exp end scanner.skip_until(prompt) end =begin if @debug1 o.split("\n").each_with_index do |i,actual| p [i,actual] end end # compare actual / expected o.split("\n").each do |actual| next if actual.empty? exp = exp_vals.shift if @debug2 a = true a = actual.include?(exp) unless exp.nil? p [actual, exp] unless a end assert_true actual.include?(exp) unless exp.nil? end =end end end end assert('mruby-bin-debugger(print) invalid arguments') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"p", :exp=>"Parameter not specified."} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) normal') do # ruby source src = <<"SRC" foo = 'foo' bar = foo baz = bar SRC # test case tc = [] tc << {:cmd=>"s"} tc << {:cmd=>"p (1+2)", :exp=>'$1 = 3'} tc << {:cmd=>"p foo", :exp=>'$2 = "foo"'} tc << {:cmd=>"p foo*=2", :exp=>'$3 = "foofoo"'} tc << {:cmd=>"s"} tc << {:cmd=>"p bar", :exp=>'$4 = "foofoo"'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) error') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"p (1+2", :exp=>'$1 = line 1: syntax error'} tc << {:cmd=>"p bar", :exp=>'$2 = undefined method'} BinTest_MrubyBinDebugger.test(src, tc) end # Kernel#instance_eval(string) doesn't work multiple statements. =begin assert('mruby-bin-debugger(print) multiple statements') do # ruby source src = <<"SRC" x = 0 y = 0 z = 0 SRC # test case tc = [] tc << {:cmd=>"s",} tc << {:cmd=>"p x=1;x+=2", :exp=>"3"} tc << {:cmd=>"s",} tc << {:cmd=>"p x", :exp=>"3"} BinTest_MrubyBinDebugger.test(src, tc) end =end assert('mruby-bin-debugger(print) scope:top') do # ruby source (bp is break point) src = "bp=nil\n" # test case tc = [] tc << {:cmd=>"p self", :exp=>'$1 = main'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) scope:class') do # ruby source (bp is break point) src = <<"SRC" class TestClassScope bp = nil end SRC # test case tc = [] tc << {:cmd=>"s"} tc << {:cmd=>"p self", :exp=>'$1 = TestClassScope'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) scope:module') do # ruby source (bp is break point) src = <<"SRC" class TestModuleScope bp = nil end SRC # test case tc = [] tc << {:cmd=>"s"} tc << {:cmd=>"p self", :exp=>'$1 = TestModuleScope'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) scope:instance method') do # ruby source (bp is break point) src = <<"SRC" class TestMethodScope def m bp = nil end end TestMethodScope.new.m SRC tc = [] tc << {:cmd=>"b 3"} tc << {:cmd=>"r"} tc << {:cmd=>"p self", :exp=>'$1 = #"b 3"} tc << {:cmd=>"r"} tc << {:cmd=>"p self", :exp=>'$1 = TestClassMethodScope'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) scope:block') do # ruby source (bp is break point) src = <<"SRC" 1.times do bp = nil end class TestBlockScope 1.times do bp = nil end def m 1.times do bp = nil end end end TestBlockScope.new.m SRC tc = [] tc << {:cmd=>"b 2"} tc << {:cmd=>"b 6"} tc << {:cmd=>"b 10"} tc << {:cmd=>"c"} tc << {:cmd=>"p self", :exp=>'$1 = main'} tc << {:cmd=>"c"} tc << {:cmd=>"p self", :exp=>'$2 = TestBlockScope'} tc << {:cmd=>"c"} tc << {:cmd=>"p self", :exp=>'$3 = #"b 6"} tc << {:cmd=>"b 8"} tc << {:cmd=>"b 11"} tc << {:cmd=>"r"} tc << {:cmd=>"p lv", :exp=>'$1 = "class"'} tc << {:cmd=>"c"} tc << {:cmd=>"p lv", :exp=>'$2 = "instance method"'} tc << {:cmd=>"c"} tc << {:cmd=>"p lv", :exp=>'$3 = "top"'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) same name:instance variable') do # ruby source (bp is break point) src = <<"SRC" @iv = 'top' class TestInstanceVariableName def initialize(v) @iv = v end def m bp = nil end end i1 = TestInstanceVariableName.new('instance1') i2 = TestInstanceVariableName.new('instance2') i1.m i2.m bp = nil SRC tc = [] tc << {:cmd=>"b 7"} tc << {:cmd=>"b 14"} tc << {:cmd=>"r"} tc << {:cmd=>"p @iv", :exp=>'$1 = "instance1"'} tc << {:cmd=>"c"} tc << {:cmd=>"p @iv", :exp=>'$2 = "instance2"'} tc << {:cmd=>"c"} tc << {:cmd=>"p @iv", :exp=>'$3 = "top"'} BinTest_MrubyBinDebugger.test(src, tc) end # Kernel#instance_eval(string) doesn't work const. =begin assert('mruby-bin-debugger(print) same name:const') do # ruby source (bp is break point) src = <<"SRC" CONST='top' class TestConstNameSuperClass CONST='super class' def m bp = nil end end class TestConstNameSubClass < TestConstNameSuperClass CONST='sub class' def m bp = nil end end TestConstNameSuperClass.new.m() TestConstNameSubClass.new.m() bp = nil SRC # todo: wait for 'break' to be implemented tc = [] 9.times { tc << {:cmd=>"s"} } tc << {:cmd=>"p CONST", :exp=>"super class"} 3.times { tc << {:cmd=>"s"} } tc << {:cmd=>"p CONST", :exp=>"sub class"} 1.times { tc << {:cmd=>"s"} } tc << {:cmd=>"p CONST", :exp=>"top"} BinTest_MrubyBinDebugger.test(src, tc) end =end assert('mruby-bin-debugger(print) Literal:Numeric') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"p 100", :exp=>'$1 = 100'} tc << {:cmd=>"p -0b100", :exp=>'$2 = -4'} tc << {:cmd=>"p +0100", :exp=>'$3 = 64'} tc << {:cmd=>"p 0x100", :exp=>'$4 = 256'} tc << {:cmd=>"p 1_234", :exp=>'$5 = 1234'} tc << {:cmd=>"p 0b1000_0000", :exp=>"$6 = #{0b1000_0000}"} tc << {:cmd=>"p 0x1000_0000", :exp=>"$7 = #{0x1000_0000}"} tc << {:cmd=>"p 3.14", :exp=>'$8 = 3.14'} tc << {:cmd=>"p -12.3", :exp=>'$9 = -12.3'} tc << {:cmd=>"p +12.000", :exp=>'$10 = 12'} tc << {:cmd=>"p 1e4", :exp=>'$11 = 10000'} tc << {:cmd=>"p -0.1e-2", :exp=>'$12 = -0.001'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Literal:String') do # ruby source src = <<"SRC" foo = 'foo' bar = "bar" baz = "baz" SRC # test case tc = [] tc << {:cmd=>"s"} tc << {:cmd=>"s"} tc << {:cmd=>'p "str"', :exp=>'$1 = "str"'} tc << {:cmd=>'p "s\tt\rr\n"', :exp=>'$2 = "s\\tt\\rr\\n"'} tc << {:cmd=>'p "\C-a\C-z"', :exp=>'$3 = "\\x01\\x1a"'} tc << {:cmd=>'p "#{foo+bar}"', :exp=>'$4 = "foobar"'} tc << {:cmd=>'p \'str\'', :exp=>'$5 = "str"'} tc << {:cmd=>'p \'s\\tt\\rr\\n\'', :exp=>'$6 = "s\\\\tt\\\\rr\\\\n"'} tc << {:cmd=>'p \'\\C-a\\C-z\'', :exp=>'$7 = "\\\\C-a\\\\C-z"'} tc << {:cmd=>'p \'#{foo+bar}\'', :exp=>'$8 = "\\#{foo+bar}"'} tc << {:cmd=>'p %!str!', :exp=>'$9 = "str"'} tc << {:cmd=>'p %!s\tt\rr\n!', :exp=>'$10 = "s\\tt\\rr\\n"'} tc << {:cmd=>'p %!\C-a\C-z!', :exp=>'$11 = "\\x01\\x1a"'} tc << {:cmd=>'p %!#{foo+bar}!', :exp=>'$12 = "foobar"'} tc << {:cmd=>'p %Q!str!', :exp=>'$13 = "str"'} tc << {:cmd=>'p %Q!s\tt\rr\n!', :exp=>'$14 = "s\\tt\\rr\\n"'} tc << {:cmd=>'p %Q!\C-a\C-z!', :exp=>'$15 = "\\x01\\x1a"'} tc << {:cmd=>'p %Q!#{foo+bar}!', :exp=>'$16 = "foobar"'} tc << {:cmd=>'p %q!str!', :exp=>'$17 = "str"'} tc << {:cmd=>'p %q!s\\tt\\rr\\n!', :exp=>'$18 = "s\\\\tt\\\\rr\\\\n"'} tc << {:cmd=>'p %q!\\C-a\\C-z!', :exp=>'$19 = "\\\\C-a\\\\C-z"'} tc << {:cmd=>'p %q!#{foo+bar}!', :exp=>'$20 = "\\#{foo+bar}"'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Literal:Array') do # ruby source src = <<"SRC" foo = 'foo' bar = "bar" baz = "baz" SRC # test case tc = [] tc << {:cmd=>"s"} tc << {:cmd=>"s"} tc << {:cmd=>'p []', :exp=>'$1 = []'} tc << {:cmd=>'p [ 5, 12, 8, 10, ]', :exp=>'$2 = [5, 12, 8, 10]'} tc << {:cmd=>'p [1,2.5,"#{foo+bar}"]', :exp=>'$3 = [1, 2.5, "foobar"]'} tc << {:cmd=>'p %w[3.14 A\ &\ B #{foo}]', :exp=>'$4 = ["3.14", "A & B", "\#{foo}"]'} tc << {:cmd=>'p %W[3.14 A\ &\ B #{foo}]', :exp=>'$5 = ["3.14", "A & B", "foo"]'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Literal:Hash') do # ruby source src = <<"SRC" foo = 'foo' bar = "bar" baz = "baz" SRC # test case tc = [] tc << {:cmd=>"s"} tc << {:cmd=>"s"} tc << {:cmd=>'p {}', :exp=>'$1 = {}'} tc << {:cmd=>'p {"one"=>1,"two"=>2}', :exp=>'$2 = {"one" => 1, "two" => 2}'} tc << {:cmd=>'p {eins: "1", zwei: "2",}', :exp=>'$3 = {eins: "1", zwei: "2"}'} tc << {:cmd=>'p {uno: "one", dos: 2}', :exp=>'$4 = {uno: "one", dos: 2}'} tc << {:cmd=>'p {"one"=>1, zwei: 2, tres: 3}', :exp=>'$5 = {"one" => 1, zwei: 2, tres: 3}'} tc << {:cmd=>'p {foo: "#{foo}",bar: "#{bar}"}', :exp=>'$6 = {foo: "foo", bar: "bar"}'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Literal:Range') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>'p 1..10', :exp=>'$1 = 1..10'} tc << {:cmd=>'p 1...10', :exp=>'$2 = 1...10'} tc << {:cmd=>'p 100..10', :exp=>'$3 = 100..10'} tc << {:cmd=>'p 1 ... 10', :exp=>'$4 = 1...10'} tc << {:cmd=>'p "1" .. "9"', :exp=>'$5 = "1".."9"'} tc << {:cmd=>'p "A" ... "Z"', :exp=>'$6 = "A"..."Z"'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Literal:Symbol') do # ruby source src = <<"SRC" foo = 'foo' bar = "bar" baz = "baz" SRC # test case tc = [] tc << {:cmd=>"s"} tc << {:cmd=>"s"} tc << {:cmd=>'p :sym', :exp=>'$1 = :sym'} tc << {:cmd=>'p :"sd"', :exp=>'$2 = :sd'} tc << {:cmd=>"p :'ss'", :exp=>'$3 = :ss'} tc << {:cmd=>'p :"123"', :exp=>'$4 = :"123"'} tc << {:cmd=>'p :"#{foo} baz"', :exp=>'$5 = :"foo baz"'} tc << {:cmd=>'p %s!symsym!', :exp=>'$6 = :symsym'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Unary operation') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>'p +10', :exp=>'$1 = 10'} tc << {:cmd=>'p -100', :exp=>'$2 = -100'} tc << {:cmd=>'p !true', :exp=>'$3 = false'} tc << {:cmd=>'p !false', :exp=>'$4 = true'} tc << {:cmd=>'p !nil', :exp=>'$5 = true'} tc << {:cmd=>'p !1', :exp=>'$6 = false'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Binary operation') do # ruby source src = <<"SRC" CONST = 100 a,b,c = 1, 5, 8 foo,bar,baz = 'foo','bar','baz' ary = [] SRC # test case tc = [] tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'p a+1', :exp=>'$1 = 2'} tc << {:cmd=>'p 2-b', :exp=>'$2 = -3'} tc << {:cmd=>'p c * 3', :exp=>'$3 = 24'} tc << {:cmd=>'p a/b', :exp=>'$4 = 0'} tc << {:cmd=>'p c%b', :exp=>'$5 = 3'} tc << {:cmd=>'p 2**10', :exp=>'$6 = 1024'} tc << {:cmd=>'p ~3', :exp=>'$7 = -4'} tc << {:cmd=>'p 1<<2', :exp=>'$8 = 4'} tc << {:cmd=>'p 64>>5', :exp=>'$9 = 2'} tc << {:cmd=>'p a|c', :exp=>'$10 = 9'} tc << {:cmd=>'p a&b', :exp=>'$11 = 1'} tc << {:cmd=>'p a^b', :exp=>'$12 = 4'} tc << {:cmd=>'p a>b', :exp=>'$13 = false'} tc << {:cmd=>'p a'$14 = true'} tc << {:cmd=>'p b>=5', :exp=>'$15 = true'} tc << {:cmd=>'p b<=5', :exp=>'$16 = true'} tc << {:cmd=>'p "A"<=>"B"', :exp=>'$17 = -1'} tc << {:cmd=>'p "A"=="B"', :exp=>'$18 = false'} tc << {:cmd=>'p "A"==="B"', :exp=>'$19 = false'} tc << {:cmd=>'p "A"!="B"', :exp=>'$20 = true'} tc << {:cmd=>'p false || true', :exp=>'$21 = true'} tc << {:cmd=>'p false && true', :exp=>'$22 = false'} tc << {:cmd=>'p not nil', :exp=>'$23 = true'} tc << {:cmd=>'p false or true', :exp=>'$24 = true'} tc << {:cmd=>'p false and true', :exp=>'$25 = false'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Ternary operation') do # ruby source src = <<"SRC" CONST = 100 a,b,c = 1, 5, -10 foo,bar,baz = 'foo','bar','baz' ary = [] SRC # test case tc = [] tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'p (a < b) ? a : b', :exp=>'$1 = 1'} tc << {:cmd=>'p (a > b) ? a : b', :exp=>'$2 = 5'} tc << {:cmd=>'p true ? "true" : "false"', :exp=>'$3 = "true"'} tc << {:cmd=>'p false ? "true" : "false"', :exp=>'$4 = "false"'} tc << {:cmd=>'p nil ? "true" : "false"', :exp=>'$5 = "false"'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Substitution:simple') do # ruby source src = <<"SRC" CONST = 100 a,b,c = 1, 5, -10 foo,bar,baz = 'foo','bar','baz' ary = [] SRC # test case tc = [] tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'p a=2', :exp=>'$1 = 2'} tc << {:cmd=>'p foo=[foo,bar,baz]', :exp=>'$2 = ["foo", "bar", "baz"]'} tc << {:cmd=>'p undefined=-1', :exp=>'$3 = -1'} tc << {:cmd=>'p "#{undefined}"', :exp=>'$4 = undefined method'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Substitution:self') do # ruby source src = <<"SRC" CONST = 100 a,b,c = 1, 5, -10 foo,bar,baz = 'foo','bar','baz' ary = [] SRC # test case tc = [] tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'p a+=9', :exp=>'$1 = 10'} tc << {:cmd=>'p b-=c', :exp=>'$2 = 15'} tc << {:cmd=>'p bar*=2', :exp=>'$3 = "barbar"'} tc << {:cmd=>'p a/=4', :exp=>'$4 = 2'} tc << {:cmd=>'p c%=4', :exp=>'$5 = 2'} tc << {:cmd=>'p b&=0b0101', :exp=>'$6 = 5'} tc << {:cmd=>'p c|=0x10', :exp=>'$7 = 18'} tc << {:cmd=>'p "#{a} #{b} #{c}"', :exp=>'$8 = "2 5 18"'} tc << {:cmd=>'p "#{foo}#{bar}#{baz}"', :exp=>'$9 = "foobarbarbaz"'} tc << {:cmd=>'p a,b,c=[10,20,30]',:exp=>'$10 = [10, 20, 30]'} tc << {:cmd=>'p [a,b,c]', :exp=>'$11 = [10, 20, 30]'} tc << {:cmd=>'p a,b=b,a', :exp=>'$12 = [20, 10]'} tc << {:cmd=>'p [a,b]', :exp=>'$13 = [20, 10]'} tc << {:cmd=>'p undefined=-1', :exp=>'$14 = -1'} tc << {:cmd=>'p "#{undefined}"', :exp=>'$15 = undefined method'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Substitution:multiple') do # ruby source src = <<"SRC" CONST = 100 a,b,c = 1, 5, -10 foo,bar,baz = 'foo','bar','baz' ary = [] SRC # test case tc = [] tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'p a,b=[10,20]', :exp=>'$1 = [10, 20]'} tc << {:cmd=>'p [a,b,c]', :exp=>'$2 = [10, 20, -10]'} tc << {:cmd=>'p foo,bar=["FOO","BAR","BAZ"]', :exp=>'$3 = ["FOO", "BAR", "BAZ"]'} tc << {:cmd=>'p [foo,bar,baz]', :exp=>'$4 = ["FOO", "BAR", "baz"]'} tc << {:cmd=>'p a,foo=foo,a', :exp=>'$5 = ["FOO", 10]'} tc << {:cmd=>'p [a,foo]', :exp=>'$6 = ["FOO", 10]'} # tc << {:cmd=>'p a,*b=[123, 456, 789]'} # tc << {:cmd=>'p [a,b]', :exp=>'[123, [456, 789]]'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Substitution:self') do # ruby source src = <<"SRC" CONST = 100 a,b,c = 1, 5, -10 foo,bar,baz = 'foo','bar','baz' ary = [] SRC # test case tc = [] tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'p a+=9', :exp=>'$1 = 10'} tc << {:cmd=>'p b-=c', :exp=>'$2 = 15'} tc << {:cmd=>'p bar*=2', :exp=>'$3 = "barbar"'} tc << {:cmd=>'p a/=4', :exp=>'$4 = 2'} tc << {:cmd=>'p c%=4', :exp=>'$5 = 2'} tc << {:cmd=>'p b&=0b0101', :exp=>'$6 = 5'} tc << {:cmd=>'p c|=0x10', :exp=>'$7 = 18'} tc << {:cmd=>'p "#{a} #{b} #{c}"', :exp=>'$8 = "2 5 18"'} tc << {:cmd=>'p "#{foo}#{bar}#{baz}"', :exp=>'$9 = "foobarbarbaz"'} tc << {:cmd=>'p a,b,c=[10,20,30]',:exp=>'$10 = [10, 20, 30]'} tc << {:cmd=>'p [a,b,c]', :exp=>'$11 = [10, 20, 30]'} tc << {:cmd=>'p a,b=b,a', :exp=>'$12 = [20, 10]'} tc << {:cmd=>'p [a,b]', :exp=>'$13 = [20, 10]'} tc << {:cmd=>'p undefined=-1', :exp=>'$14 = -1'} tc << {:cmd=>'p "#{undefined}"', :exp=>'$15 = undefined method'} BinTest_MrubyBinDebugger.test(src, tc) end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/bintest/PaxHeaders/mrdb.rb0000644000000000000000000000012715077107276025742 xustar0030 mtime=1761382078.105420634 30 atime=1761382080.126411389 27 ctime=1761382108.886301 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/bintest/mrdb.rb0000644000175100017510000002075415077107276026336 0ustar00runnerrunnerrequire 'open3' require 'tempfile' class BinTest_MrubyBinDebugger @debug1=false @debug2=true @debug3=true def self.test(rubysource, testcase) script, bin = Tempfile.new(['test', '.rb']), Tempfile.new(['test', '.mrb']) # .rb script.write rubysource script.flush # compile `#{cmd("mrbc")} -g -o "#{bin.path}" "#{script.path}"` # add mrdb quit testcase << {:cmd=>"quit"} stdin_data = testcase.map{|t| t[:cmd]}.join("\n") << "\n" ["#{cmd('mrdb')} #{script.path}", "#{cmd('mrdb')} -b #{bin.path}"].each do |cmd| o, s = Open3.capture2(cmd, :stdin_data => stdin_data) exp_vals = testcase.map{|t| t.fetch(:exp, nil)} unexp_vals = testcase.map{|t| t.fetch(:unexp, nil)} if @debug1 o.split("\n").each_with_index do |i,actual| p [i,actual] end end # compare actual / expected o.split("\n").each do |actual| next if actual.empty? exp = exp_vals.shift if @debug2 a = true a = actual.include?(exp) unless exp.nil? p [actual, exp] unless a end assert_true actual.include?(exp) unless exp.nil? end # compare actual / unexpected o.split("\n").each do |actual| next if actual.empty? unexp = unexp_vals.shift if @debug3 a = false a = actual.include?(unexp) unless unexp.nil? p [actual, unexp] if a end assert_false actual.include?(unexp) unless unexp.nil? end end end end INVCMD = "invalid command" assert('mruby-bin-debugger(mrdb) command line') do # ruby source src = "foo = 'foo'\n" str = ":#{'abcdefghij' * 103}" cmd = "p a=#{str}" # test case BinTest_MrubyBinDebugger.test(src, [{:cmd=>cmd[0...1023], :unexp=>'command line too long.'}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>cmd[0...1024], :unexp=>'command line too long.'}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>cmd[0...1025], :exp=>'command line too long.'}]) end assert('mruby-bin-debugger(mrdb) command: "break"') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"b", :unexp=>INVCMD} tc << {:cmd=>"br", :unexp=>INVCMD} tc << {:cmd=>"brea", :unexp=>INVCMD} tc << {:cmd=>"break", :unexp=>INVCMD} BinTest_MrubyBinDebugger.test(src, tc) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"bl", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"breaka", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "continue"') do # ruby source src = "foo = 'foo'\n" # test case BinTest_MrubyBinDebugger.test(src, [{:cmd=>"c", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"co", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"continu", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"continue", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"cn", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"continuee", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "delete"') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"d 1", :unexp=>INVCMD} tc << {:cmd=>"de 1", :unexp=>INVCMD} tc << {:cmd=>"delet 1", :unexp=>INVCMD} tc << {:cmd=>"delete 1", :unexp=>INVCMD} BinTest_MrubyBinDebugger.test(src, tc) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"dd 1", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"deletee 1", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "disable"') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"dis", :unexp=>INVCMD} tc << {:cmd=>"disa", :unexp=>INVCMD} tc << {:cmd=>"disabl", :unexp=>INVCMD} tc << {:cmd=>"disable", :unexp=>INVCMD} BinTest_MrubyBinDebugger.test(src, tc) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"di", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"disb", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"disablee", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "enable"') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"en", :unexp=>INVCMD} tc << {:cmd=>"ena", :unexp=>INVCMD} tc << {:cmd=>"enabl", :unexp=>INVCMD} tc << {:cmd=>"enable", :unexp=>INVCMD} BinTest_MrubyBinDebugger.test(src, tc) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"e", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"enb", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"enablee", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "eval"') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"ev", :unexp=>INVCMD} tc << {:cmd=>"eva", :unexp=>INVCMD} tc << {:cmd=>"eval", :unexp=>INVCMD} BinTest_MrubyBinDebugger.test(src, tc) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"e", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"evl", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"evall", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "help"') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"h", :unexp=>INVCMD} tc << {:cmd=>"he", :unexp=>INVCMD} tc << {:cmd=>"hel", :unexp=>INVCMD} tc << {:cmd=>"help", :unexp=>INVCMD} BinTest_MrubyBinDebugger.test(src, tc) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"hl", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"helpp", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "info breakpoints"') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"i b", :unexp=>INVCMD} tc << {:cmd=>"in b", :unexp=>INVCMD} tc << {:cmd=>"i br", :unexp=>INVCMD} tc << {:cmd=>"inf breakpoint", :unexp=>INVCMD} tc << {:cmd=>"info breakpoints", :unexp=>INVCMD} BinTest_MrubyBinDebugger.test(src, tc) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ii b", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"i bb", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"infoo breakpoints", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"info breakpointss", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "list"') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"l", :unexp=>INVCMD} tc << {:cmd=>"li", :unexp=>INVCMD} tc << {:cmd=>"lis", :unexp=>INVCMD} tc << {:cmd=>"list", :unexp=>INVCMD} BinTest_MrubyBinDebugger.test(src, tc) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ll", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"listt", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "print"') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"p", :unexp=>INVCMD} tc << {:cmd=>"pr", :unexp=>INVCMD} tc << {:cmd=>"prin", :unexp=>INVCMD} tc << {:cmd=>"print", :unexp=>INVCMD} BinTest_MrubyBinDebugger.test(src, tc) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"pp", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"printt", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "quit"') do # ruby source src = "foo = 'foo'\n" # test case BinTest_MrubyBinDebugger.test(src, [{:cmd=>"q", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"qu", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"qui", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"quit", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"qq", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"quitt", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "run"') do # ruby source src = "foo = 'foo'\n" # test case BinTest_MrubyBinDebugger.test(src, [{:cmd=>"r", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ru", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"run", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"rr", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"runn", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "step"') do # ruby source src = <<"SRC" while true foo = 'foo' end SRC # test case tc = [] tc << {:cmd=>"s", :unexp=>INVCMD} tc << {:cmd=>"st", :unexp=>INVCMD} tc << {:cmd=>"ste", :unexp=>INVCMD} tc << {:cmd=>"step", :unexp=>INVCMD} BinTest_MrubyBinDebugger.test(src, tc) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ss", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"stepp", :exp=>INVCMD}]) end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276025132 xustar0030 mtime=1761382078.105420634 30 atime=1761382080.126411389 30 ctime=1761382108.885301003 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/mrbgem.rake0000644000175100017510000000044715077107276025527 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-bin-debugger') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'mruby debugger command' spec.build.defines << "MRB_USE_DEBUG_HOOK" spec.add_dependency('mruby-eval', :core => 'mruby-eval') spec.bins = %w(mrdb) end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/PaxHeaders/tools0000644000000000000000000000013215077107334024073 xustar0030 mtime=1761382108.202302977 30 atime=1761382109.796298369 30 ctime=1761382108.202302977 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/0000755000175100017510000000000015077107334024540 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/PaxHeaders/mrdb0000644000000000000000000000013215077107334025017 xustar0030 mtime=1761382108.883301008 30 atime=1761382109.796298369 30 ctime=1761382108.883301008 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/0000755000175100017510000000000015077107334025464 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/PaxHeaders/apilist.c0000644000000000000000000000013215077107276026712 xustar0030 mtime=1761382078.106420629 30 atime=1761382080.126411389 30 ctime=1761382108.883301008 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c0000644000175100017510000001066515077107276027312 0ustar00runnerrunner/* * apilist.c */ #include #include #include "mrdb.h" #include "mrdberror.h" #include "apilist.h" #include "apistring.h" #include #include #include #define LINE_BUF_SIZE MAX_COMMAND_LINE typedef struct source_file { char *path; uint16_t lineno; FILE *fp; } source_file; static void source_file_free(mrb_state *mrb, source_file *file) { if (file != NULL) { if (file->path != NULL) { mrb_free(mrb, file->path); } if (file->fp != NULL) { fclose(file->fp); file->fp = NULL; } mrb_free(mrb, file); } } static char* build_path(mrb_state *mrb, const char *dir, const char *base) { int len; char *path = NULL; len = strlen(base) + 1; if (strcmp(dir, ".")) { len += strlen(dir) + sizeof("/") - 1; } path = (char*)mrb_malloc(mrb, len); memset(path, 0, len); if (strcmp(dir, ".")) { strcat(path, dir); strcat(path, "/"); } strcat(path, base); return path; } static char* dirname(mrb_state *mrb, const char *path) { size_t len; const char *p; if (path == NULL) { return NULL; } p = strrchr(path, '/'); len = p != NULL ? (size_t)(p - path) : strlen(path); return mrdb_strndup(mrb, path, len); } static source_file* source_file_new(mrb_state *mrb, mrb_debug_context *dbg, char *filename) { source_file *file; file = (source_file*)mrb_malloc(mrb, sizeof(source_file)); memset(file, '\0', sizeof(source_file)); file->fp = fopen(filename, "rb"); if (file->fp == NULL) { source_file_free(mrb, file); return NULL; } file->lineno = 1; file->path = mrdb_strdup(mrb, filename); if (file->path == NULL) { source_file_free(mrb, file); return NULL; } return file; } static mrb_bool remove_newlines(char *s, FILE *fp) { int c; char *p; size_t len; if ((len = strlen(s)) == 0) { return FALSE; } p = s + len - 1; if (*p != '\r' && *p != '\n') { return FALSE; } if (*p == '\r') { /* peek the next character and skip '\n' */ if ((c = fgetc(fp)) != '\n') { ungetc(c, fp); } } /* remove trailing newline characters */ while (s <= p && (*p == '\r' || *p == '\n')) { *p-- = '\0'; } return TRUE; } static void show_lines(source_file *file, uint16_t line_min, uint16_t line_max) { char buf[LINE_BUF_SIZE]; int show_lineno = 1, found_newline = 0, is_printed = 0; if (file->fp == NULL) { return; } while (fgets(buf, sizeof(buf), file->fp) != NULL) { found_newline = remove_newlines(buf, file->fp); if (line_min <= file->lineno) { if (show_lineno) { printf("%-8d", file->lineno); } show_lineno = found_newline; printf(found_newline ? "%s\n" : "%s", buf); is_printed = 1; } if (found_newline) { if (line_max < ++file->lineno) { break; } } } if (is_printed && !found_newline) { printf("\n"); } } char* mrb_debug_get_source(mrb_state *mrb, mrdb_state *mrdb, const char *srcpath, const char *filename) { int i; FILE *fp; const char *search_path[3]; char *path = NULL; const char *srcname = strrchr(filename, '/'); if (srcname) srcname++; else srcname = filename; search_path[0] = srcpath; search_path[1] = dirname(mrb, mrb_debug_get_filename(mrb, mrdb->dbg->irep, 0)); search_path[2] = "."; for (i = 0; i < 3; i++) { if (search_path[i] == NULL) { continue; } if ((path = build_path(mrb, search_path[i], srcname)) == NULL) { continue; } if ((fp = fopen(path, "rb")) == NULL) { mrb_free(mrb, path); path = NULL; continue; } fclose(fp); break; } mrb_free(mrb, (void*)search_path[1]); return path; } int32_t mrb_debug_list(mrb_state *mrb, mrb_debug_context *dbg, char *filename, uint16_t line_min, uint16_t line_max) { char *ext; source_file *file; if (mrb == NULL || dbg == NULL || filename == NULL) { return MRB_DEBUG_INVALID_ARGUMENT; } ext = strrchr(filename, '.'); if (ext == NULL || strcmp(ext, ".rb")) { printf("List command only supports .rb file.\n"); return MRB_DEBUG_INVALID_ARGUMENT; } if (line_min > line_max) { return MRB_DEBUG_INVALID_ARGUMENT; } if ((file = source_file_new(mrb, dbg, filename)) != NULL) { show_lines(file, line_min, line_max); source_file_free(mrb, file); return MRB_DEBUG_OK; } else { printf("Invalid source file named %s.\n", filename); return MRB_DEBUG_INVALID_ARGUMENT; } } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/PaxHeaders/apiprint.c0000644000000000000000000000013115077107276027072 xustar0030 mtime=1761382078.106420629 30 atime=1761382080.126411389 29 ctime=1761382108.86530106 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c0000644000175100017510000000361115077107276027464 0ustar00runnerrunner/* ** apiprint.c ** */ #include #include "mrdb.h" #include #include #include #include #include #include #include #include #include "apiprint.h" static void mrdb_check_syntax(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len) { mrb_ccontext *c; c = mrb_ccontext_new(mrb); c->no_exec = TRUE; c->capture_errors = TRUE; mrb_ccontext_filename(mrb, c, (const char*)dbg->prvfile); c->lineno = dbg->prvline; /* Load program */ mrb_load_nstring_cxt(mrb, expr, len, c); mrb_ccontext_free(mrb, c); } mrb_value mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len, mrb_bool *exc, int direct_eval) { void (*tmp)(struct mrb_state*, const struct mrb_irep*, const mrb_code*, mrb_value*); mrb_value ruby_code; mrb_value s; mrb_value v; mrb_value recv; /* disable code_fetch_hook */ tmp = mrb->code_fetch_hook; mrb->code_fetch_hook = NULL; mrdb_check_syntax(mrb, dbg, expr, len); if (mrb->exc) { v = mrb_obj_value(mrb->exc); mrb->exc = 0; } else if (direct_eval) { recv = dbg->regs[0]; v = mrb_funcall(mrb, recv, expr, 0); } else { /* * begin * expr * rescue => e * e * end */ ruby_code = mrb_str_new_lit(mrb, "begin\n"); ruby_code = mrb_str_cat(mrb, ruby_code, expr, len); ruby_code = mrb_str_cat_lit(mrb, ruby_code, "\nrescue => e\ne\nend"); recv = dbg->regs[0]; v = mrb_funcall_argv(mrb, recv, MRB_SYM(instance_eval), 1, &ruby_code); } mrb_bool is_exc = mrb_obj_is_kind_of(mrb, v, E_EXCEPTION); if (is_exc) { s = mrb_exc_get_output(mrb, mrb_obj_ptr(v)); } else { s = mrb_inspect(mrb, v); } if (exc) *exc = is_exc; /* enable code_fetch_hook */ mrb->code_fetch_hook = tmp; return s; } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/PaxHeaders/cmdbreak.c0000644000000000000000000000013215077107276027015 xustar0030 mtime=1761382078.106420629 30 atime=1761382080.127411384 30 ctime=1761382108.864301063 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c0000644000175100017510000002556115077107276027416 0ustar00runnerrunner/* ** cmdbreak.c ** */ #include #include #include #include #include #include #include "mrdb.h" #include "mrdberror.h" #include "apibreak.h" #define BREAK_SET_MSG_LINE "Breakpoint %d: file %s, line %d.\n" #define BREAK_SET_MSG_METHOD "Breakpoint %d: method %s.\n" #define BREAK_SET_MSG_CLASS_METHOD "Breakpoint %d: class %s, method %s.\n" #define BREAK_INFO_MSG_HEADER "Num Type Enb What" #define BREAK_INFO_MSG_LINEBREAK "%-8ubreakpoint %s at %s:%u\n" #define BREAK_INFO_MSG_METHODBREAK "%-8ubreakpoint %s in %s:%s\n" #define BREAK_INFO_MSG_METHODBREAK_NOCLASS "%-8ubreakpoint %s in %s\n" #define BREAK_INFO_MSG_ENABLE "y" #define BREAK_INFO_MSG_DISABLE "n" #define BREAK_ERR_MSG_INVALIDARG "Internal error." #define BREAK_ERR_MSG_BLANK "Try \'help break\' for more information." #define BREAK_ERR_MSG_RANGEOVER "The line number range is from 1 to 65535." #define BREAK_ERR_MSG_NUMOVER "Exceeded the settable number of breakpoint." #define BREAK_ERR_MSG_NOOVER "Breakno is over the available number.Please 'quit' and restart mrdb." #define BREAK_ERR_MSG_INVALIDSTR "String \'%s\' is invalid.\n" #define BREAK_ERR_MSG_INVALIDLINENO "Line %d in file \"%s\" is unavailable.\n" #define BREAK_ERR_MSG_INVALIDCLASS "Class name \'%s\' is invalid.\n" #define BREAK_ERR_MSG_INVALIDMETHOD "Method name \'%s\' is invalid.\n" #define BREAK_ERR_MSG_INVALIDFILE "Source file named \"%s\" is unavailable.\n" #define BREAK_ERR_MSG_INVALIDBPNO "warning: bad breakpoint number at or near '%s'\n" #define BREAK_ERR_MSG_INVALIDBPNO_INFO "Args must be numbers variables." #define BREAK_ERR_MSG_NOBPNO "No breakpoint number %d.\n" #define BREAK_ERR_MSG_NOBPNO_INFO "No breakpoint matching '%d'\n" #define BREAK_ERR_MSG_NOBPNO_INFOALL "No breakpoints." #define LINENO_MAX_DIGIT 6 #define BPNO_LETTER_NUM 9 typedef int32_t (*all_command_func)(mrb_state*, mrb_debug_context*); typedef int32_t (*select_command_func)(mrb_state*, mrb_debug_context*, uint32_t); static void print_api_common_error(int32_t error) { switch(error) { case MRB_DEBUG_INVALID_ARGUMENT: puts(BREAK_ERR_MSG_INVALIDARG); break; default: break; } } #undef STRTOUL #define STRTOUL(ul,s) { \ int i; \ ul = 0; \ for (i=0; ISDIGIT(s[i]); i++) ul = 10*ul + (s[i] -'0'); \ } static int32_t parse_breakpoint_no(char* args) { char* ps = args; uint32_t l; if ((*ps == '0')||(strlen(ps) >= BPNO_LETTER_NUM)) { return 0; } while (!(ISBLANK(*ps)||ISCNTRL(*ps))) { if (!ISDIGIT(*ps)) { return 0; } ps++; } STRTOUL(l, args); return l; } static mrb_bool exe_set_command_all(mrb_state *mrb, mrdb_state *mrdb, all_command_func func) { int32_t ret = MRB_DEBUG_OK; if (mrdb->wcnt == 1) { ret = func(mrb, mrdb->dbg); print_api_common_error(ret); return TRUE; } return FALSE; } static void exe_set_command_select(mrb_state *mrb, mrdb_state *mrdb, select_command_func func) { char* ps; int32_t ret = MRB_DEBUG_OK; int32_t bpno = 0; int32_t i; for (i=1; iwcnt; i++) { ps = mrdb->words[i]; bpno = parse_breakpoint_no(ps); if (bpno == 0) { printf(BREAK_ERR_MSG_INVALIDBPNO, ps); break; } ret = func(mrb, mrdb->dbg, (uint32_t)bpno); if (ret == MRB_DEBUG_BREAK_INVALID_NO) { printf(BREAK_ERR_MSG_NOBPNO, bpno); } else if (ret != MRB_DEBUG_OK) { print_api_common_error(ret); } } } mrb_debug_bptype check_bptype(char* args) { char* ps = args; if (ISBLANK(*ps)||ISCNTRL(*ps)) { puts(BREAK_ERR_MSG_BLANK); return MRB_DEBUG_BPTYPE_NONE; } if (!ISDIGIT(*ps)) { return MRB_DEBUG_BPTYPE_METHOD; } while (!(ISBLANK(*ps)||ISCNTRL(*ps))) { if (!ISDIGIT(*ps)) { printf(BREAK_ERR_MSG_INVALIDSTR, args); return MRB_DEBUG_BPTYPE_NONE; } ps++; } if ((*args == '0')||(strlen(args) >= LINENO_MAX_DIGIT)) { puts(BREAK_ERR_MSG_RANGEOVER); return MRB_DEBUG_BPTYPE_NONE; } return MRB_DEBUG_BPTYPE_LINE; } static void print_breakpoint(mrb_debug_breakpoint *bp) { const char* enable_letter[] = {BREAK_INFO_MSG_DISABLE, BREAK_INFO_MSG_ENABLE}; if (bp->type == MRB_DEBUG_BPTYPE_LINE) { printf(BREAK_INFO_MSG_LINEBREAK, bp->bpno, enable_letter[bp->enable], bp->point.linepoint.file, bp->point.linepoint.lineno); } else { if (bp->point.methodpoint.class_name == NULL) { printf(BREAK_INFO_MSG_METHODBREAK_NOCLASS, bp->bpno, enable_letter[bp->enable], bp->point.methodpoint.method_name); } else { printf(BREAK_INFO_MSG_METHODBREAK, bp->bpno, enable_letter[bp->enable], bp->point.methodpoint.class_name, bp->point.methodpoint.method_name); } } } static void info_break_all(mrb_state *mrb, mrdb_state *mrdb) { int32_t bpnum = 0; int32_t i = 0; int32_t ret = MRB_DEBUG_OK; mrb_debug_breakpoint *bp_list; bpnum = mrb_debug_get_breaknum(mrb, mrdb->dbg); if (bpnum < 0) { print_api_common_error(bpnum); return; } else if (bpnum == 0) { puts(BREAK_ERR_MSG_NOBPNO_INFOALL); return; } bp_list = (mrb_debug_breakpoint*)mrb_malloc(mrb, bpnum * sizeof(mrb_debug_breakpoint)); ret = mrb_debug_get_break_all(mrb, mrdb->dbg, (uint32_t)bpnum, bp_list); if (ret < 0) { print_api_common_error(ret); return; } puts(BREAK_INFO_MSG_HEADER); for (i = 0; i < bpnum; i++) { print_breakpoint(&bp_list[i]); } mrb_free(mrb, bp_list); } static void info_break_select(mrb_state *mrb, mrdb_state *mrdb) { int32_t ret = MRB_DEBUG_OK; int32_t bpno = 0; char* ps = mrdb->command; mrb_debug_breakpoint bp; mrb_bool isFirst = TRUE; int32_t i; for (i=2; iwcnt; i++) { ps = mrdb->words[i]; bpno = parse_breakpoint_no(ps); if (bpno == 0) { puts(BREAK_ERR_MSG_INVALIDBPNO_INFO); break; } ret = mrb_debug_get_break(mrb, mrdb->dbg, bpno, &bp); if (ret == MRB_DEBUG_BREAK_INVALID_NO) { printf(BREAK_ERR_MSG_NOBPNO_INFO, bpno); break; } else if (ret != MRB_DEBUG_OK) { print_api_common_error(ret); break; } else if (isFirst == TRUE) { isFirst = FALSE; puts(BREAK_INFO_MSG_HEADER); } print_breakpoint(&bp); } } mrb_debug_bptype parse_breakcommand(mrb_state *mrb, mrdb_state *mrdb, const char **file, uint32_t *line, char **cname, char **method) { mrb_debug_context *dbg = mrdb->dbg; char *args; char *body; mrb_debug_bptype type; uint32_t l; if (mrdb->wcnt <= 1) { puts(BREAK_ERR_MSG_BLANK); return MRB_DEBUG_BPTYPE_NONE; } args = mrdb->words[1]; if ((body = strrchr(args, ':')) == NULL) { body = args; type = check_bptype(body); } else { if (body == args) { printf(BREAK_ERR_MSG_INVALIDSTR, args); return MRB_DEBUG_BPTYPE_NONE; } *body = '\0'; type = check_bptype(++body); } switch(type) { case MRB_DEBUG_BPTYPE_LINE: STRTOUL(l, body); if (l <= 65535) { *line = l; *file = (body == args)? mrb_debug_get_filename(mrb, dbg->irep, dbg->pc - dbg->irep->iseq): args; } else { puts(BREAK_ERR_MSG_RANGEOVER); type = MRB_DEBUG_BPTYPE_NONE; } break; case MRB_DEBUG_BPTYPE_METHOD: if (body == args) { /* method only */ if (ISUPPER(*body)||ISLOWER(*body)||(*body == '_')) { *method = body; *cname = NULL; } else { printf(BREAK_ERR_MSG_INVALIDMETHOD, args); type = MRB_DEBUG_BPTYPE_NONE; } } else { if (ISUPPER(*args)) { switch(*body) { case '@': case '$': case '?': case '.': case ',': case ':': case ';': case '#': case '\\': case '\'': case '\"': printf(BREAK_ERR_MSG_INVALIDMETHOD, body); type = MRB_DEBUG_BPTYPE_NONE; break; default: *method = body; *cname = args; break; } } else { printf(BREAK_ERR_MSG_INVALIDCLASS, args); type = MRB_DEBUG_BPTYPE_NONE; } } break; case MRB_DEBUG_BPTYPE_NONE: default: break; } return type; } dbgcmd_state dbgcmd_break(mrb_state *mrb, mrdb_state *mrdb) { mrb_debug_bptype type; mrb_debug_context *dbg = mrdb->dbg; const char *file = NULL; uint32_t line = 0; char *cname = NULL; char *method = NULL; int32_t ret; type = parse_breakcommand(mrb, mrdb, &file, &line, &cname, &method); switch (type) { case MRB_DEBUG_BPTYPE_LINE: ret = mrb_debug_set_break_line(mrb, dbg, file, line); break; case MRB_DEBUG_BPTYPE_METHOD: ret = mrb_debug_set_break_method(mrb, dbg, cname, method); break; case MRB_DEBUG_BPTYPE_NONE: default: return DBGST_PROMPT; } if (ret >= 0) { if (type == MRB_DEBUG_BPTYPE_LINE) { printf(BREAK_SET_MSG_LINE, ret, file, line); } else if ((type == MRB_DEBUG_BPTYPE_METHOD)&&(cname == NULL)) { printf(BREAK_SET_MSG_METHOD, ret, method); } else { printf(BREAK_SET_MSG_CLASS_METHOD, ret, cname, method); } } else { switch (ret) { case MRB_DEBUG_BREAK_INVALID_LINENO: printf(BREAK_ERR_MSG_INVALIDLINENO, line, file); break; case MRB_DEBUG_BREAK_INVALID_FILE: printf(BREAK_ERR_MSG_INVALIDFILE, file); break; case MRB_DEBUG_BREAK_NUM_OVER: puts(BREAK_ERR_MSG_NUMOVER); break; case MRB_DEBUG_BREAK_NO_OVER: puts(BREAK_ERR_MSG_NOOVER); break; case MRB_DEBUG_INVALID_ARGUMENT: puts(BREAK_ERR_MSG_INVALIDARG); break; case MRB_DEBUG_NOBUF: puts("T.B.D."); break; default: break; } } return DBGST_PROMPT; } dbgcmd_state dbgcmd_info_break(mrb_state *mrb, mrdb_state *mrdb) { if (mrdb->wcnt == 2) { info_break_all(mrb, mrdb); } else { info_break_select(mrb, mrdb); } return DBGST_PROMPT; } dbgcmd_state dbgcmd_delete(mrb_state *mrb, mrdb_state *mrdb) { mrb_bool ret = FALSE; ret = exe_set_command_all(mrb, mrdb, mrb_debug_delete_break_all); if (ret != TRUE) { exe_set_command_select(mrb, mrdb, mrb_debug_delete_break); } return DBGST_PROMPT; } dbgcmd_state dbgcmd_enable(mrb_state *mrb, mrdb_state *mrdb) { mrb_bool ret = FALSE; ret = exe_set_command_all(mrb, mrdb, mrb_debug_enable_break_all); if (ret != TRUE) { exe_set_command_select(mrb, mrdb, mrb_debug_enable_break); } return DBGST_PROMPT; } dbgcmd_state dbgcmd_disable(mrb_state *mrb, mrdb_state *mrdb) { mrb_bool ret = FALSE; ret = exe_set_command_all(mrb, mrdb, mrb_debug_disable_break_all); if (ret != TRUE) { exe_set_command_select(mrb, mrdb, mrb_debug_disable_break); } return DBGST_PROMPT; } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/PaxHeaders/apistring.h0000644000000000000000000000013215077107276027252 xustar0030 mtime=1761382078.106420629 30 atime=1761382080.126411389 30 ctime=1761382108.868301052 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apistring.h0000644000175100017510000000043115077107276027640 0ustar00runnerrunner/* * apistring.h */ #ifndef APISTRING_H_ #define APISTRING_H_ #include /* both functions return a null pointer on failure */ char *mrdb_strndup(mrb_state *mrb, const char *s, size_t size); char *mrdb_strdup(mrb_state *mrb, const char *s); #endif /* APISTRING_H_ */ nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/PaxHeaders/mrdberror.h0000644000000000000000000000013215077107276027250 xustar0030 mtime=1761382078.106420629 30 atime=1761382080.127411384 30 ctime=1761382108.873301037 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h0000644000175100017510000000071315077107276027641 0ustar00runnerrunner/* ** mrdberror.h - mruby debugger error code ** */ #ifndef MRDBERROR_H #define MRDBERROR_H #define MRB_DEBUG_OK (0) #define MRB_DEBUG_NOBUF (-1) #define MRB_DEBUG_INVALID_ARGUMENT (-2) #define MRB_DEBUG_BREAK_INVALID_LINENO (-11) #define MRB_DEBUG_BREAK_INVALID_FILE (-12) #define MRB_DEBUG_BREAK_INVALID_NO (-13) #define MRB_DEBUG_BREAK_NUM_OVER (-14) #define MRB_DEBUG_BREAK_NO_OVER (-15) #endif nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/PaxHeaders/apiprint.h0000644000000000000000000000013215077107276027100 xustar0030 mtime=1761382078.106420629 30 atime=1761382080.126411389 30 ctime=1761382108.869301049 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h0000644000175100017510000000033615077107276027472 0ustar00runnerrunner/* * apiprint.h */ #ifndef APIPRINT_H_ #define APIPRINT_H_ #include #include "mrdb.h" mrb_value mrb_debug_eval(mrb_state*, mrb_debug_context*, const char*, size_t, mrb_bool*, int); #endif /* APIPRINT_H_ */ nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/PaxHeaders/apilist.h0000644000000000000000000000013215077107276026717 xustar0030 mtime=1761382078.106420629 30 atime=1761382080.126411389 30 ctime=1761382108.876301029 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.h0000644000175100017510000000043615077107276027312 0ustar00runnerrunner/* * apilist.h */ #ifndef APILIST_H_ #define APILIST_H_ #include #include "mrdb.h" int32_t mrb_debug_list(mrb_state*, mrb_debug_context*, char*, uint16_t, uint16_t); char* mrb_debug_get_source(mrb_state*, mrdb_state*, const char*, const char *); #endif /* APILIST_H_ */ nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/PaxHeaders/cmdmisc.c0000644000000000000000000000013215077107276026664 xustar0030 mtime=1761382078.106420629 30 atime=1761382080.127411384 30 ctime=1761382108.877301026 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c0000644000175100017510000002551115077107276027260 0ustar00runnerrunner/* ** cmdmisc.c - mruby debugger miscellaneous command functions ** */ #include #include #include #include "apilist.h" #include "apistring.h" #include typedef struct help_msg { const char *cmd1; const char *cmd2; const char *short_msg; const char *long_msg; } help_msg; static help_msg help_msg_list[] = { { "b[reak]", NULL, "Set breakpoint", "Usage: break [file:]line\n" " break [class:]method\n" "\n" "Set breakpoint at specified line or method.\n" "If \'[file:]line\' is specified, break at start of code for that line (in a file).\n" "If \'[class:]method\' is specified, break at start of code for that method (of the class).\n" }, { "c[ontinue]", NULL, "Continue program being debugged", "Usage: continue [N]\n" "\n" "Continue program stopped by a breakpoint.\n" "If N, which is non negative value, is passed,\n" "proceed program until the N-th breakpoint is coming.\n" "If N is not passed, N is assumed 1.\n" }, { "d[elete]", NULL, "Delete some breakpoints", "Usage: delete [bpno1 [bpno2 [... [bpnoN]]]]\n" "\n" "Delete some breakpoints.\n" "Arguments are breakpoint numbers with spaces in between.\n" "To delete all breakpoints, give no argument.\n" }, { "dis[able]", NULL, "Disable some breakpoints", "Usage: disable [bpno1 [bpno2 [... [bpnoN]]]]\n" "\n" "Disable some breakpoints.\n" "Arguments are breakpoint numbers with spaces in between.\n" "To disable all breakpoints, give no argument.\n" }, { "en[able]", NULL, "Enable some breakpoints", "Usage: enable [bpno1 [bpno2 [... [bpnoN]]]]\n" "\n" "Enable some breakpoints.\n" "Arguments are breakpoint numbers with spaces in between.\n" "To enable all breakpoints, give no argument.\n" }, { "ev[al]", NULL, "Evaluate expression", "Usage: eval expr\n" "\n" "It evaluates and prints the value of the mruby expression.\n" "This is equivalent to the \'print\' command.\n" }, { "h[elp]", NULL, "Print this help", "Usage: help [command]\n" "\n" "With no arguments, help displays a short list of commands.\n" "With a command name as help argument, help displays how to use that command.\n" }, { "i[nfo]", "b[reakpoints]", "Status of breakpoints", "Usage: info breakpoints [bpno1 [bpno2 [... [bpnoN]]]]\n" "\n" "Status of specified breakpoints (all user-settable breakpoints if no argument).\n" "Arguments are breakpoint numbers with spaces in between.\n" }, { "i[nfo]", "l[ocals]", "Print name of local variables", "Usage: info locals\n" "\n" "Print name of local variables.\n" }, { "l[ist]", NULL, "List specified line", "Usage: list\n" " list first[,last]\n" " list filename:first[,last]\n" "\n" "Print lines from a source file.\n" "\n" "With first and last, list prints lines from first to last.\n" "When last is empty, it stands for ten lines away from first.\n" "With filename, list prints lines in the specified source file.\n" }, { "p[rint]", NULL, "Print value of expression", "Usage: print expr\n" "\n" "It evaluates and prints the value of the mruby expression.\n" "This is equivalent to the \'eval\' command.\n" }, { "q[uit]", NULL, "Exit mrdb", "Usage: quit\n" "\n" "Exit mrdb.\n" }, { "r[un]", NULL, "Start debugged program", "Usage: run\n" "\n" "Start debugged program.\n" }, { "s[tep]", NULL, "Step program until it reaches a different source line", "Usage: step\n" "\n" "Step program until it reaches a different source line.\n" }, { NULL, NULL, NULL, NULL } }; typedef struct listcmd_parser_state { mrb_bool parse_error; mrb_bool has_line_min; mrb_bool has_line_max; char *filename; uint16_t line_min; uint16_t line_max; } listcmd_parser_state; static listcmd_parser_state* listcmd_parser_state_new(mrb_state *mrb) { listcmd_parser_state *st = (listcmd_parser_state*)mrb_malloc(mrb, sizeof(listcmd_parser_state)); static const listcmd_parser_state st_zero = {0}; *st = st_zero; return st; } static void listcmd_parser_state_free(mrb_state *mrb, listcmd_parser_state *st) { if (st != NULL) { if (st->filename != NULL) { mrb_free(mrb, st->filename); } mrb_free(mrb, st); } } static mrb_bool parse_uint(char **sp, uint16_t *n) { char *p; int i; if (*sp == NULL || **sp == '\0') { return FALSE; } for (p = *sp; *p != '\0' && ISDIGIT(*p); p++) ; if (p != *sp && (i = atoi(*sp)) >= 0) { *n = (uint16_t)i; *sp = p; return TRUE; } return FALSE; } static mrb_bool skip_char(char **sp, char c) { if (*sp != NULL && **sp == c) { ++*sp; return TRUE; } return FALSE; } static mrb_bool parse_lineno(mrb_state *mrb, char **sp, listcmd_parser_state *st) { if (*sp == NULL || **sp == '\0') { return FALSE; } st->has_line_min = FALSE; st->has_line_max = FALSE; if (parse_uint(sp, &st->line_min)) { st->has_line_min = TRUE; } else { return FALSE; } if (skip_char(sp, ',')) { if (parse_uint(sp, &st->line_max)) { st->has_line_max = TRUE; } else { st->parse_error = TRUE; return FALSE; } } return TRUE; } static mrb_bool parse_filename(mrb_state *mrb, char **sp, listcmd_parser_state *st) { char *p; int len; if (st->filename != NULL) { mrb_free(mrb, st->filename); st->filename = NULL; } if ((p = strchr(*sp, ':')) != NULL) { len = p - *sp; } else { len = strlen(*sp); } if (len > 0 && (st->filename = mrdb_strndup(mrb, *sp, len)) != NULL) { *sp += len; return TRUE; } else { return FALSE; } } char* replace_ext(mrb_state *mrb, const char *filename, const char *ext) { size_t len; const char *p; char *s; if (filename == NULL) { return NULL; } if ((p = strrchr(filename, '.')) != NULL && strchr(p, '/') == NULL) { len = p - filename; } else { len = strlen(filename); } s = (char*)mrb_malloc(mrb, len + strlen(ext) + 1); memset(s, '\0', len + strlen(ext) + 1); strncpy(s, filename, len); strcat(s, ext); return s; } static mrb_bool parse_listcmd_args(mrb_state *mrb, mrdb_state *mrdb, listcmd_parser_state *st) { char *p; switch (mrdb->wcnt) { case 2: p = mrdb->words[1]; /* mrdb->words[1] ::= | ':' | */ if (!parse_lineno(mrb, &p, st)) { if (parse_filename(mrb, &p, st)) { if (skip_char(&p, ':')) { if (!parse_lineno(mrb, &p, st)) { st->parse_error = TRUE; } } } else { st->parse_error = TRUE; } } if (*p != '\0') { st->parse_error = TRUE; } break; case 1: case 0: /* do nothing */ break; default: st->parse_error = TRUE; printf("too many arguments\n"); break; } if (!st->parse_error) { if (!st->has_line_min) { st->line_min = (!st->filename && mrdb->dbg->prvline > 0) ? mrdb->dbg->prvline : 1; } if (!st->has_line_max) { st->line_max = st->line_min + 9; } if (st->filename == NULL) { if (mrdb->dbg->prvfile && strcmp(mrdb->dbg->prvfile, "-")) { st->filename = replace_ext(mrb, mrdb->dbg->prvfile, ".rb"); } } } if (st->parse_error || st->filename == NULL) { return FALSE; } return TRUE; } static mrb_bool check_cmd_pattern(const char *pattern, const char *cmd) { const char *lbracket, *rbracket, *p, *q; if (pattern == NULL && cmd == NULL) { return TRUE; } if (pattern == NULL || cmd == NULL) { return FALSE; } if ((lbracket = strchr(pattern, '[')) == NULL) { return !strcmp(pattern, cmd); } if ((rbracket = strchr(pattern, ']')) == NULL) { return FALSE; } if (strncmp(pattern, cmd, lbracket - pattern)) { return FALSE; } p = lbracket + 1; q = (char*)cmd + (lbracket - pattern); for (; p < rbracket && *q != '\0'; p++, q++) { if (*p != *q) { break; } } return *q == '\0'; } static help_msg* get_help_msg(char *cmd1, char *cmd2) { help_msg *p; if (cmd1 == NULL) { return NULL; } for (p = help_msg_list; p->cmd1 != NULL; p++) { if (check_cmd_pattern(p->cmd1, cmd1) && check_cmd_pattern(p->cmd2, cmd2)) { return p; } } return NULL; } static mrb_bool show_short_help(void) { help_msg *p; printf("Commands\n"); for (p = help_msg_list; p->cmd1 != NULL; p++) { if (p->cmd2 == NULL) { printf(" %s -- %s\n", p->cmd1, p->short_msg); } else { printf(" %s %s -- %s\n", p->cmd1, p->cmd2, p->short_msg); } } return TRUE; } static mrb_bool show_long_help(char *cmd1, char *cmd2) { help_msg *help; if ((help = get_help_msg(cmd1, cmd2)) == NULL) { return FALSE; } printf("%s", help->long_msg); return TRUE; } dbgcmd_state dbgcmd_list(mrb_state *mrb, mrdb_state *mrdb) { char *filename; listcmd_parser_state *st = listcmd_parser_state_new(mrb); if (parse_listcmd_args(mrb, mrdb, st)) { if ((filename = mrb_debug_get_source(mrb, mrdb, mrdb->srcpath, st->filename)) == NULL) { filename = st->filename; } mrb_debug_list(mrb, mrdb->dbg, filename, st->line_min, st->line_max); if (filename != NULL && filename != st->filename) { mrb_free(mrb, filename); } listcmd_parser_state_free(mrb, st); } return DBGST_PROMPT; } dbgcmd_state dbgcmd_help(mrb_state *mrb, mrdb_state *mrdb) { mrb_bool is_valid; int i; switch (mrdb->wcnt) { case 0: case 1: is_valid = show_short_help(); break; case 2: is_valid = show_long_help(mrdb->words[1], NULL); break; case 3: is_valid = show_long_help(mrdb->words[1], mrdb->words[2]); break; default: is_valid = FALSE; break; } if (!is_valid) { printf("Invalid command \""); for (i = 1; i < mrdb->wcnt; i++) { printf("%s%s", i == 1 ? "" : " ", mrdb->words[i]); } printf("\". Try \"help\".\n"); } return DBGST_PROMPT; } dbgcmd_state dbgcmd_quit(mrb_state *mrb, mrdb_state *mrdb) { switch (mrdb->dbg->xm) { case DBG_RUN: case DBG_STEP: case DBG_NEXT: while (1) { char c; int buf; printf("The program is running. Exit anyway? (y or n) "); fflush(stdout); if ((buf = getchar()) == EOF) { mrdb->dbg->xm = DBG_QUIT; break; } c = buf; while (buf != '\n' && (buf = getchar()) != EOF) ; if (c == 'y' || c == 'Y') { mrdb->dbg->xm = DBG_QUIT; break; } else if (c == 'n' || c == 'N') { break; } else { printf("Please answer y or n.\n"); } } break; default: mrdb->dbg->xm = DBG_QUIT; break; } if (mrdb->dbg->xm == DBG_QUIT) { struct RClass *exc; exc = mrb_define_class(mrb, "DebuggerExit", E_EXCEPTION); mrb_raise(mrb, exc, "Exit mrdb"); } return DBGST_PROMPT; } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/PaxHeaders/cmdprint.c0000644000000000000000000000013215077107276027065 xustar0030 mtime=1761382078.106420629 30 atime=1761382080.127411384 30 ctime=1761382108.878301023 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c0000644000175100017510000000320315077107276027453 0ustar00runnerrunner/* ** cmdprint.c - mruby debugger print command functions ** */ #include #include "mrdb.h" #include #include #include #include #include #include #include "apiprint.h" dbgcmd_state dbgcmd_print(mrb_state *mrb, mrdb_state *mrdb) { mrb_value expr; mrb_value result; uint8_t wcnt; int ai; if (mrdb->wcnt <= 1) { puts("Parameter not specified."); return DBGST_PROMPT; } ai = mrb_gc_arena_save(mrb); /* eval expr */ expr = mrb_str_new_cstr(mrb, NULL); for (wcnt=1; wcntwcnt; wcnt++) { expr = mrb_str_cat_lit(mrb, expr, " "); expr = mrb_str_cat_cstr(mrb, expr, mrdb->words[wcnt]); } result = mrb_debug_eval(mrb, mrdb->dbg, RSTRING_PTR(expr), RSTRING_LEN(expr), NULL, 0); /* $print_no = result */ printf("$%lu = ", (unsigned long)mrdb->print_no++); fwrite(RSTRING_PTR(result), RSTRING_LEN(result), 1, stdout); putc('\n', stdout); if (mrdb->print_no == 0) { mrdb->print_no = 1; } mrb_gc_arena_restore(mrb, ai); return DBGST_PROMPT; } dbgcmd_state dbgcmd_eval(mrb_state *mrb, mrdb_state *mrdb) { return dbgcmd_print(mrb, mrdb); } dbgcmd_state dbgcmd_info_local(mrb_state *mrb, mrdb_state *mrdb) { mrb_value result; mrb_value s; int ai; ai = mrb_gc_arena_save(mrb); result = mrb_debug_eval(mrb, mrdb->dbg, "local_variables", 0, NULL, 1); s = mrb_str_cat_lit(mrb, result, "\0"); printf("$%lu = %s\n", (unsigned long)mrdb->print_no++, RSTRING_PTR(s)); if (mrdb->print_no == 0) { mrdb->print_no = 1; } mrb_gc_arena_restore(mrb, ai); return DBGST_PROMPT; } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/PaxHeaders/apibreak.c0000644000000000000000000000013115077107276027022 xustar0030 mtime=1761382078.106420629 30 atime=1761382080.126411389 29 ctime=1761382108.87230104 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c0000644000175100017510000002672315077107276027425 0ustar00runnerrunner/* ** apibreak.c ** */ #include #include #include #include "mrdb.h" #include #include #include #include #include #include #include "mrdberror.h" #include "apibreak.h" #include "apistring.h" #define MAX_BREAKPOINTNO (MAX_BREAKPOINT * 1024) #define MRB_DEBUG_BP_FILE_OK (0x0001) #define MRB_DEBUG_BP_LINENO_OK (0x0002) static uint16_t check_lineno(mrb_irep_debug_info_file *info_file, uint16_t lineno) { uint32_t count = info_file->line_entry_count; uint16_t l_idx; switch (info_file->line_type) { case mrb_debug_line_ary: for (l_idx = 0; l_idx < count; ++l_idx) { if (lineno == info_file->lines.ary[l_idx]) { return lineno; } } break; case mrb_debug_line_flat_map: for (l_idx = 0; l_idx < count; ++l_idx) { if (lineno == info_file->lines.flat_map[l_idx].line) { return lineno; } } break; case mrb_debug_line_packed_map: { const uint8_t *p = info_file->lines.packed_map; const uint8_t *pend = p + count; uint32_t line = 0; while (p < pend) { mrb_packed_int_decode(p, &p); line += mrb_packed_int_decode(p, &p); if (line == lineno) return lineno; } } break; } return 0; } static int32_t get_break_index(mrb_debug_context *dbg, uint32_t bpno) { uint32_t i; int32_t index; char hit = FALSE; for (i = 0; i < dbg->bpnum; i++) { if (dbg->bp[i].bpno == bpno) { hit = TRUE; index = i; break; } } if (hit == FALSE) { return MRB_DEBUG_BREAK_INVALID_NO; } return index; } static void free_breakpoint(mrb_state *mrb, mrb_debug_breakpoint *bp) { switch(bp->type) { case MRB_DEBUG_BPTYPE_LINE: mrb_free(mrb, (void*)bp->point.linepoint.file); break; case MRB_DEBUG_BPTYPE_METHOD: mrb_free(mrb, (void*)bp->point.methodpoint.method_name); if (bp->point.methodpoint.class_name != NULL) { mrb_free(mrb, (void*)bp->point.methodpoint.class_name); } break; default: break; } } static uint16_t check_file_lineno(mrb_state *mrb, const struct mrb_irep *irep, const char *file, uint16_t lineno) { mrb_irep_debug_info_file *info_file; uint16_t result = 0; uint16_t f_idx; uint16_t fix_lineno; uint16_t i; for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) { const char *filename; info_file = irep->debug_info->files[f_idx]; filename = mrb_sym_name_len(mrb, info_file->filename_sym, NULL); if (!strcmp(filename, file)) { result = MRB_DEBUG_BP_FILE_OK; fix_lineno = check_lineno(info_file, lineno); if (fix_lineno != 0) { return result | MRB_DEBUG_BP_LINENO_OK; } } for (i=0; i < irep->rlen; ++i) { result |= check_file_lineno(mrb, irep->reps[i], file, lineno); if (result == (MRB_DEBUG_BP_FILE_OK | MRB_DEBUG_BP_LINENO_OK)) { return result; } } } return result; } static int32_t compare_break_method(mrb_state *mrb, mrb_debug_breakpoint *bp, struct RClass *class_obj, mrb_sym method_sym, mrb_bool* isCfunc) { const char* class_name; const char* method_name; mrb_method_t m; struct RClass* sc; const char* sn; mrb_sym ssym; mrb_debug_methodpoint *method_p; mrb_bool is_defined; method_name = mrb_sym_name(mrb, method_sym); method_p = &bp->point.methodpoint; if (strcmp(method_p->method_name, method_name) == 0) { class_name = mrb_class_name(mrb, class_obj); if (class_name == NULL) { if (method_p->class_name == NULL) { return bp->bpno; } } else if (method_p->class_name != NULL) { m = mrb_method_search_vm(mrb, &class_obj, method_sym); if (MRB_METHOD_UNDEF_P(m)) { return MRB_DEBUG_OK; } if (MRB_METHOD_CFUNC_P(m)) { *isCfunc = TRUE; } is_defined = mrb_class_defined(mrb, method_p->class_name); if (is_defined == FALSE) { return MRB_DEBUG_OK; } sc = mrb_class_get(mrb, method_p->class_name); ssym = mrb_intern_check_cstr(mrb, method_p->method_name); m = mrb_method_search_vm(mrb, &sc, ssym); if (MRB_METHOD_UNDEF_P(m)) { return MRB_DEBUG_OK; } class_name = mrb_class_name(mrb, class_obj); sn = mrb_class_name(mrb, sc); if (strcmp(sn, class_name) == 0) { return bp->bpno; } } } return MRB_DEBUG_OK; } int32_t mrb_debug_set_break_line(mrb_state *mrb, mrb_debug_context *dbg, const char *file, uint16_t lineno) { int32_t index; char* set_file; uint16_t result; if ((mrb == NULL)||(dbg == NULL)||(file == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } if (dbg->bpnum >= MAX_BREAKPOINT) { return MRB_DEBUG_BREAK_NUM_OVER; } if (dbg->next_bpno > MAX_BREAKPOINTNO) { return MRB_DEBUG_BREAK_NO_OVER; } /* file and lineno check. */ result = check_file_lineno(mrb, dbg->root_irep, file, lineno); if (result == 0) { return MRB_DEBUG_BREAK_INVALID_FILE; } else if (result == MRB_DEBUG_BP_FILE_OK) { return MRB_DEBUG_BREAK_INVALID_LINENO; } set_file = mrdb_strdup(mrb, file); index = dbg->bpnum; dbg->bp[index].bpno = dbg->next_bpno; dbg->next_bpno++; dbg->bp[index].enable = TRUE; dbg->bp[index].type = MRB_DEBUG_BPTYPE_LINE; dbg->bp[index].point.linepoint.lineno = lineno; dbg->bpnum++; dbg->bp[index].point.linepoint.file = set_file; return dbg->bp[index].bpno; } int32_t mrb_debug_set_break_method(mrb_state *mrb, mrb_debug_context *dbg, const char *class_name, const char *method_name) { int32_t index; char* set_class; char* set_method; if ((mrb == NULL) || (dbg == NULL) || (method_name == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } if (dbg->bpnum >= MAX_BREAKPOINT) { return MRB_DEBUG_BREAK_NUM_OVER; } if (dbg->next_bpno > MAX_BREAKPOINTNO) { return MRB_DEBUG_BREAK_NO_OVER; } if (class_name != NULL) { set_class = mrdb_strdup(mrb, class_name); } else { set_class = NULL; } set_method = mrdb_strdup(mrb, method_name); if (set_method == NULL) { mrb_free(mrb, set_class); } index = dbg->bpnum; dbg->bp[index].bpno = dbg->next_bpno; dbg->next_bpno++; dbg->bp[index].enable = TRUE; dbg->bp[index].type = MRB_DEBUG_BPTYPE_METHOD; dbg->bp[index].point.methodpoint.method_name = set_method; dbg->bp[index].point.methodpoint.class_name = set_class; dbg->bpnum++; return dbg->bp[index].bpno; } int32_t mrb_debug_get_breaknum(mrb_state *mrb, mrb_debug_context *dbg) { if ((mrb == NULL) || (dbg == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } return dbg->bpnum; } int32_t mrb_debug_get_break_all(mrb_state *mrb, mrb_debug_context *dbg, uint32_t size, mrb_debug_breakpoint *bp) { uint32_t get_size = 0; if ((mrb == NULL) || (dbg == NULL) || (bp == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } if (dbg->bpnum >= size) { get_size = size; } else { get_size = dbg->bpnum; } memcpy(bp, dbg->bp, sizeof(mrb_debug_breakpoint) * get_size); return get_size; } int32_t mrb_debug_get_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno, mrb_debug_breakpoint *bp) { int32_t index; if ((mrb == NULL) || (dbg == NULL) || (bp == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } index = get_break_index(dbg, bpno); if (index == MRB_DEBUG_BREAK_INVALID_NO) { return MRB_DEBUG_BREAK_INVALID_NO; } bp->bpno = dbg->bp[index].bpno; bp->enable = dbg->bp[index].enable; bp->point = dbg->bp[index].point; bp->type = dbg->bp[index].type; return 0; } int32_t mrb_debug_delete_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno) { uint32_t i; int32_t index; if ((mrb == NULL) ||(dbg == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } index = get_break_index(dbg, bpno); if (index == MRB_DEBUG_BREAK_INVALID_NO) { return MRB_DEBUG_BREAK_INVALID_NO; } free_breakpoint(mrb, &dbg->bp[index]); for (i = index; i < dbg->bpnum; i++) { if ((i + 1) == dbg->bpnum) { dbg->bp[i] = (mrb_debug_breakpoint){0}; } else { dbg->bp[i] = dbg->bp[i + 1]; } } dbg->bpnum--; return MRB_DEBUG_OK; } int32_t mrb_debug_delete_break_all(mrb_state *mrb, mrb_debug_context *dbg) { uint32_t i; if ((mrb == NULL) || (dbg == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } for (i = 0; i < dbg->bpnum; i++) { free_breakpoint(mrb, &dbg->bp[i]); } dbg->bpnum = 0; return MRB_DEBUG_OK; } int32_t mrb_debug_enable_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno) { int32_t index = 0; if ((mrb == NULL) || (dbg == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } index = get_break_index(dbg, bpno); if (index == MRB_DEBUG_BREAK_INVALID_NO) { return MRB_DEBUG_BREAK_INVALID_NO; } dbg->bp[index].enable = TRUE; return MRB_DEBUG_OK; } int32_t mrb_debug_enable_break_all(mrb_state *mrb, mrb_debug_context *dbg) { uint32_t i; if ((mrb == NULL) || (dbg == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } for (i = 0; i < dbg->bpnum; i++) { dbg->bp[i].enable = TRUE; } return MRB_DEBUG_OK; } int32_t mrb_debug_disable_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno) { int32_t index = 0; if ((mrb == NULL) || (dbg == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } index = get_break_index(dbg, bpno); if (index == MRB_DEBUG_BREAK_INVALID_NO) { return MRB_DEBUG_BREAK_INVALID_NO; } dbg->bp[index].enable = FALSE; return MRB_DEBUG_OK; } int32_t mrb_debug_disable_break_all(mrb_state *mrb, mrb_debug_context *dbg) { uint32_t i; if ((mrb == NULL) || (dbg == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } for (i = 0; i < dbg->bpnum; i++) { dbg->bp[i].enable = FALSE; } return MRB_DEBUG_OK; } static mrb_bool check_start_pc_for_line(mrb_state *mrb, const mrb_irep *irep, const mrb_code *pc, uint16_t line) { if (pc > irep->iseq) { if (line == mrb_debug_get_line(mrb, irep, pc - irep->iseq - 1)) { return FALSE; } } return TRUE; } int32_t mrb_debug_check_breakpoint_line(mrb_state *mrb, mrb_debug_context *dbg, const char *file, uint16_t line) { mrb_debug_breakpoint *bp; mrb_debug_linepoint *line_p; uint32_t i; if ((mrb == NULL) || (dbg == NULL) || (file == NULL) || (line <= 0)) { return MRB_DEBUG_INVALID_ARGUMENT; } if (!check_start_pc_for_line(mrb, dbg->irep, dbg->pc, line)) { return MRB_DEBUG_OK; } bp = dbg->bp; for (i=0; ibpnum; i++) { switch (bp->type) { case MRB_DEBUG_BPTYPE_LINE: if (bp->enable == TRUE) { line_p = &bp->point.linepoint; if ((strcmp(line_p->file, file) == 0) && (line_p->lineno == line)) { return bp->bpno; } } break; case MRB_DEBUG_BPTYPE_METHOD: break; case MRB_DEBUG_BPTYPE_NONE: default: return MRB_DEBUG_OK; } bp++; } return MRB_DEBUG_OK; } int32_t mrb_debug_check_breakpoint_method(mrb_state *mrb, mrb_debug_context *dbg, struct RClass *class_obj, mrb_sym method_sym, mrb_bool* isCfunc) { mrb_debug_breakpoint *bp; int32_t bpno; uint32_t i; if ((mrb == NULL) || (dbg == NULL) || (class_obj == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } bp = dbg->bp; for (i=0; ibpnum; i++) { if (bp->type == MRB_DEBUG_BPTYPE_METHOD) { if (bp->enable == TRUE) { bpno = compare_break_method(mrb, bp, class_obj, method_sym, isCfunc); if (bpno > 0) { return bpno; } } } else if (bp->type == MRB_DEBUG_BPTYPE_NONE) { break; } bp++; } return 0; } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/PaxHeaders/mrdb.h0000644000000000000000000000013215077107276026176 xustar0030 mtime=1761382078.106420629 30 atime=1761382080.127411384 30 ctime=1761382108.881301014 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h0000644000175100017510000000636315077107276026576 0ustar00runnerrunner/* ** mrdb.h - mruby debugger ** */ #ifndef MRDB_H #define MRDB_H #include #include "mrdbconf.h" #define MAX_COMMAND_WORD (16) typedef enum debug_command_id { DBGCMD_RUN, DBGCMD_CONTINUE, DBGCMD_NEXT, DBGCMD_STEP, DBGCMD_BREAK, DBGCMD_INFO_BREAK, DBGCMD_INFO_LOCAL, DBGCMD_WATCH, DBGCMD_INFO_WATCH, DBGCMD_ENABLE, DBGCMD_DISABLE, DBGCMD_DELETE, DBGCMD_PRINT, DBGCMD_DISPLAY, DBGCMD_INFO_DISPLAY, DBGCMD_DELETE_DISPLAY, DBGCMD_EVAL, DBGCMD_BACKTRACE, DBGCMD_LIST, DBGCMD_HELP, DBGCMD_QUIT, DBGCMD_UNKNOWN } debug_command_id; typedef enum dbgcmd_state { DBGST_CONTINUE, DBGST_PROMPT, DBGST_COMMAND_ERROR, DBGST_MAX, DBGST_RESTART } dbgcmd_state; typedef enum mrdb_exemode { DBG_INIT, DBG_RUN, DBG_STEP, DBG_NEXT, DBG_QUIT, } mrdb_exemode; typedef enum mrdb_exephase { DBG_PHASE_BEFORE_RUN, DBG_PHASE_RUNNING, DBG_PHASE_AFTER_RUN, DBG_PHASE_RESTART, } mrdb_exephase; typedef enum mrdb_brkmode { BRK_INIT, BRK_BREAK, BRK_STEP, BRK_NEXT, BRK_QUIT, } mrdb_brkmode; typedef enum { MRB_DEBUG_BPTYPE_NONE, MRB_DEBUG_BPTYPE_LINE, MRB_DEBUG_BPTYPE_METHOD, } mrb_debug_bptype; typedef struct mrb_debug_linepoint { const char *file; uint16_t lineno; } mrb_debug_linepoint; typedef struct mrb_debug_methodpoint { const char *class_name; const char *method_name; } mrb_debug_methodpoint; typedef struct mrb_debug_breakpoint { uint32_t bpno; uint8_t enable; mrb_debug_bptype type; union point { mrb_debug_linepoint linepoint; mrb_debug_methodpoint methodpoint; } point; } mrb_debug_breakpoint; typedef struct mrb_debug_context { const struct mrb_irep *root_irep; const struct mrb_irep *irep; const mrb_code *pc; mrb_value *regs; const char *prvfile; int32_t prvline; mrb_callinfo *prvci; mrdb_exemode xm; mrdb_exephase xphase; mrdb_brkmode bm; int16_t bmi; uint16_t ccnt; uint16_t scnt; mrb_debug_breakpoint bp[MAX_BREAKPOINT]; uint32_t bpnum; int32_t next_bpno; int32_t method_bpno; int32_t stopped_bpno; mrb_bool isCfunc; mrdb_exemode (*break_hook)(mrb_state *mrb, struct mrb_debug_context *dbg); } mrb_debug_context; typedef struct mrdb_state { char *command; uint8_t wcnt; uint8_t pi; char *words[MAX_COMMAND_WORD]; const char *srcpath; uint32_t print_no; mrb_debug_context *dbg; } mrdb_state; typedef dbgcmd_state (*debug_command_func)(mrb_state*, mrdb_state*); /* cmdrun.c */ dbgcmd_state dbgcmd_run(mrb_state*, mrdb_state*); dbgcmd_state dbgcmd_continue(mrb_state*, mrdb_state*); dbgcmd_state dbgcmd_step(mrb_state*, mrdb_state*); dbgcmd_state dbgcmd_next(mrb_state*, mrdb_state*); /* cmdbreak.c */ dbgcmd_state dbgcmd_break(mrb_state*, mrdb_state*); dbgcmd_state dbgcmd_info_break(mrb_state*, mrdb_state*); dbgcmd_state dbgcmd_info_local(mrb_state*, mrdb_state*); dbgcmd_state dbgcmd_delete(mrb_state*, mrdb_state*); dbgcmd_state dbgcmd_enable(mrb_state*, mrdb_state*); dbgcmd_state dbgcmd_disable(mrb_state*, mrdb_state*); /* cmdprint.c */ dbgcmd_state dbgcmd_print(mrb_state*, mrdb_state*); dbgcmd_state dbgcmd_eval(mrb_state*, mrdb_state*); /* cmdmisc.c */ dbgcmd_state dbgcmd_list(mrb_state*, mrdb_state*); dbgcmd_state dbgcmd_help(mrb_state*, mrdb_state*); dbgcmd_state dbgcmd_quit(mrb_state*, mrdb_state*); #endif nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/PaxHeaders/cmdrun.c0000644000000000000000000000013215077107276026535 xustar0030 mtime=1761382078.106420629 30 atime=1761382080.127411384 30 ctime=1761382108.867301055 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c0000644000175100017510000000235015077107276027125 0ustar00runnerrunner/* ** cmdrun.c - mruby debugger run command functions ** */ #include #include "mrdb.h" dbgcmd_state dbgcmd_run(mrb_state *mrb, mrdb_state *mrdb) { mrb_debug_context *dbg = mrdb->dbg; if (dbg->xm == DBG_INIT){ dbg->xm = DBG_RUN; } else { dbg->xm = DBG_QUIT; if (dbg->xphase == DBG_PHASE_RUNNING){ struct RClass *exc; puts("Start it from the beginning"); exc = mrb_define_class(mrb, "DebuggerRestart", E_EXCEPTION); mrb_raise(mrb, exc, "Restart mrdb"); } } return DBGST_RESTART; } dbgcmd_state dbgcmd_continue(mrb_state *mrb, mrdb_state *mrdb) { mrb_debug_context *dbg = mrdb->dbg; int ccnt = 1; if (mrdb->wcnt > 1){ sscanf(mrdb->words[1], "%d", &ccnt); } dbg->ccnt = (uint16_t)(ccnt > 0 ? ccnt : 1); /* count of continue */ if (dbg->xphase == DBG_PHASE_AFTER_RUN){ puts("The program is not running."); dbg->xm = DBG_QUIT; } else { dbg->xm = DBG_RUN; } return DBGST_CONTINUE; } dbgcmd_state dbgcmd_step(mrb_state *mrb, mrdb_state *mrdb) { mrdb->dbg->xm = DBG_STEP; return DBGST_CONTINUE; } dbgcmd_state dbgcmd_next(mrb_state *mrb, mrdb_state *mrdb) { mrdb->dbg->xm = DBG_NEXT; mrdb->dbg->prvci = mrb->c->ci; return DBGST_CONTINUE; } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/PaxHeaders/mrdbconf.h0000644000000000000000000000013115077107276027043 xustar0030 mtime=1761382078.106420629 30 atime=1761382080.127411384 29 ctime=1761382108.87930102 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h0000644000175100017510000000075515077107276027443 0ustar00runnerrunner/* ** mrdbconf.h - mruby debugger configuration ** */ #ifndef MRDBCONF_H #define MRDBCONF_H #ifndef MRB_USE_DEBUG_HOOK # error mruby-bin-debugger need 'MRB_USE_DEBUG_HOOK' in your build configuration #endif #ifdef MRB_NO_STDIO # error mruby-bin-debugger conflicts 'MRB_NO_STDIO' in your build configuration #endif /* configuration options: */ /* maximum size for command buffer */ #define MAX_COMMAND_LINE 1024 /* maximum number of settable breakpoint */ #define MAX_BREAKPOINT 5 #endif nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/PaxHeaders/mrdb.c0000644000000000000000000000013215077107276026171 xustar0030 mtime=1761382078.106420629 30 atime=1761382080.127411384 30 ctime=1761382108.882301011 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c0000644000175100017510000004277615077107276026601 0ustar00runnerrunner/* ** mrdb.c - mruby debugger ** */ #include #include #include #include #include #include #include #include #include #include #include #include "mrdb.h" #include "apibreak.h" #include "apilist.h" void mrdb_state_free(mrb_state*); static mrb_debug_context *_debug_context = NULL; static mrdb_state *_mrdb_state = NULL; struct _args { FILE *rfp; char* fname; char* srcpath; int argc; char** argv; mrb_bool mrbfile : 1; }; typedef struct debug_command { const char *cmd1; const char *cmd2; uint8_t len1; uint8_t len2; uint8_t div; debug_command_id id; debug_command_func func; } debug_command; static const debug_command debug_command_list[] = { {"break", NULL, 1, 0, 0, DBGCMD_BREAK, dbgcmd_break}, /* b[reak] */ {"continue", NULL, 1, 0, 0, DBGCMD_CONTINUE, dbgcmd_continue}, /* c[ontinue] */ {"delete", NULL, 1, 0, 1, DBGCMD_DELETE, dbgcmd_delete}, /* d[elete] */ {"disable", NULL, 3, 0, 1, DBGCMD_DISABLE, dbgcmd_disable}, /* dis[able] */ {"enable", NULL, 2, 0, 1, DBGCMD_ENABLE, dbgcmd_enable}, /* en[able] */ {"eval", NULL, 2, 0, 0, DBGCMD_EVAL, dbgcmd_eval}, /* ev[al] */ {"help", NULL, 1, 0, 1, DBGCMD_HELP, dbgcmd_help}, /* h[elp] */ {"info", "breakpoints", 1, 1, 1, DBGCMD_INFO_BREAK, dbgcmd_info_break}, /* i[nfo] b[reakpoints] */ {"info", "locals", 1, 1, 0, DBGCMD_INFO_LOCAL, dbgcmd_info_local}, /* i[nfo] l[ocals] */ {"list", NULL, 1, 0, 1, DBGCMD_LIST, dbgcmd_list}, /* l[ist] */ {"print", NULL, 1, 0, 0, DBGCMD_PRINT, dbgcmd_print}, /* p[rint] */ {"quit", NULL, 1, 0, 0, DBGCMD_QUIT, dbgcmd_quit}, /* q[uit] */ {"run", NULL, 1, 0, 0, DBGCMD_RUN, dbgcmd_run}, /* r[un] */ {"step", NULL, 1, 0, 1, DBGCMD_STEP, dbgcmd_step}, /* s[tep] */ {"next", NULL, 1, 0, 1, DBGCMD_NEXT, dbgcmd_next}, /* n[ext] */ {NULL} }; static void usage(const char *name) { static const char *const usage_msg[] = { "switches:", "-b load and execute RiteBinary (mrb) file", "-d specify source directory", "--version print the version", "--copyright print the copyright", NULL }; const char *const *p = usage_msg; printf("Usage: %s [switches] programfile\n", name); while (*p) { printf(" %s\n", *p++); } } static int parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args) { char **origargv = argv; static const struct _args args_zero = { 0 }; *args = args_zero; for (argc--,argv++; argc > 0; argc--,argv++) { char *item; if (argv[0][0] != '-') break; item = argv[0] + 1; switch (*item++) { case 'b': args->mrbfile = TRUE; break; case 'd': if (item[0]) { goto append_srcpath; } else if (argc > 1) { argc--; argv++; item = argv[0]; append_srcpath: if (!args->srcpath) { size_t buflen; char *buf; buflen = strlen(item) + 1; buf = (char*)mrb_malloc(mrb, buflen); memcpy(buf, item, buflen); args->srcpath = buf; } else { size_t srcpathlen; size_t itemlen; srcpathlen = strlen(args->srcpath); itemlen = strlen(item); args->srcpath = (char*)mrb_realloc(mrb, args->srcpath, srcpathlen + itemlen + 2); args->srcpath[srcpathlen] = '\n'; memcpy(args->srcpath + srcpathlen + 1, item, itemlen + 1); } } else { printf("%s: No path specified for -d\n", *origargv); return EXIT_SUCCESS; } break; case '-': if (strcmp((*argv) + 2, "version") == 0) { mrb_show_version(mrb); exit(EXIT_SUCCESS); } else if (strcmp((*argv) + 2, "copyright") == 0) { mrb_show_copyright(mrb); exit(EXIT_SUCCESS); } default: return EXIT_FAILURE; } } if (args->rfp == NULL) { if (*argv == NULL) { printf("%s: Program file not specified.\n", *origargv); return EXIT_FAILURE; } else { args->rfp = fopen(argv[0], args->mrbfile ? "rb" : "r"); if (args->rfp == NULL) { printf("%s: Cannot open program file. (%s)\n", *origargv, *argv); return EXIT_FAILURE; } args->fname = argv[0]; argc--; argv++; } } args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1)); memcpy(args->argv, argv, (argc+1) * sizeof(char*)); args->argc = argc; return EXIT_SUCCESS; } static void cleanup(mrb_state *mrb, struct _args *args) { if (args->rfp) fclose(args->rfp); if (args->srcpath) mrb_free(mrb, args->srcpath); if (args->argv) mrb_free(mrb, args->argv); mrdb_state_free(mrb); mrb_close(mrb); } static mrb_debug_context* mrb_debug_context_new(mrb_state *mrb) { mrb_debug_context *dbg = (mrb_debug_context*)mrb_malloc(mrb, sizeof(mrb_debug_context)); static const mrb_debug_context dbg_zero = {0}; *dbg = dbg_zero; dbg->xm = DBG_INIT; dbg->xphase = DBG_PHASE_BEFORE_RUN; dbg->next_bpno = 1; return dbg; } mrb_debug_context* mrb_debug_context_get(mrb_state *mrb) { if (!_debug_context) { _debug_context = mrb_debug_context_new(mrb); } return _debug_context; } void mrb_debug_context_set(mrb_debug_context *dbg) { _debug_context = dbg; } void mrb_debug_context_free(mrb_state *mrb) { if (_debug_context) { mrb_debug_delete_break_all(mrb, _debug_context); mrb_free(mrb, _debug_context); _debug_context = NULL; } } static mrdb_state* mrdb_state_new(mrb_state *mrb) { mrdb_state *mrdb = (mrdb_state*)mrb_malloc(mrb, sizeof(mrdb_state)); static const mrdb_state mrdb_zero = {0}; *mrdb = mrdb_zero; mrdb->dbg = mrb_debug_context_get(mrb); mrdb->command = (char*)mrb_malloc(mrb, MAX_COMMAND_LINE+1); mrdb->print_no = 1; return mrdb; } mrdb_state* mrdb_state_get(mrb_state *mrb) { if (!_mrdb_state) { _mrdb_state = mrdb_state_new(mrb); } return _mrdb_state; } void mrdb_state_set(mrdb_state *mrdb) { _mrdb_state = mrdb; } void mrdb_state_free(mrb_state *mrb) { mrb_debug_context_free(mrb); if (_mrdb_state) { mrb_free(mrb, _mrdb_state->command); mrb_free(mrb, _mrdb_state); _mrdb_state = NULL; } } static char* get_command(mrb_state *mrb, mrdb_state *mrdb) { int i; int c; for (i=0; icommand[i] = c; } if (i == 0 && feof(stdin)) { clearerr(stdin); strcpy(mrdb->command, "quit"); i += sizeof("quit") - 1; } if (i == MAX_COMMAND_LINE) { for (; (c=getchar()) != EOF && c !='\n'; i++) ; } if (i > MAX_COMMAND_LINE) { printf("command line too long.\n"); i = 0; /* discard command data */ } mrdb->command[i] = '\0'; return mrdb->command; } static char* pick_out_word(mrb_state *mrb, char **pp) { char *ps; for (ps=*pp; ISBLANK(*ps); ps++) ; if (*ps == '\0') { return NULL; } if (*ps == '\"' || *ps == '\'') { *pp = strchr(ps+1, *ps); if (*pp) (*pp)++; } else { *pp = strpbrk(ps, " \t"); } if (!*pp) { *pp = ps + strlen(ps); } if (**pp != '\0') { **pp = '\0'; (*pp)++; } return ps; } static debug_command* parse_command(mrb_state *mrb, mrdb_state *mrdb, char *buf) { debug_command *cmd = NULL; char *p = buf; size_t wlen; /* get word #1 */ mrdb->words[0] = pick_out_word(mrb, &p); if (!mrdb->words[0]) { return NULL; } mrdb->wcnt = 1; /* set remain parameter */ for (; *p && ISBLANK(*p); p++) ; if (*p) { mrdb->words[mrdb->wcnt++] = p; } /* check word #1 */ for (cmd=(debug_command*)debug_command_list; cmd->cmd1; cmd++) { wlen = strlen(mrdb->words[0]); if (wlen >= cmd->len1 && strncmp(mrdb->words[0], cmd->cmd1, wlen) == 0) { break; } } if (cmd->cmd2) { if (mrdb->wcnt > 1) { /* get word #2 */ mrdb->words[1] = pick_out_word(mrb, &p); if (mrdb->words[1]) { /* update remain parameter */ for (; *p && ISBLANK(*p); p++) ; if (*p) { mrdb->words[mrdb->wcnt++] = p; } } } /* check word #1,#2 */ for (; cmd->cmd1; cmd++) { wlen = strlen(mrdb->words[0]); if (wlen < cmd->len1 || strncmp(mrdb->words[0], cmd->cmd1, wlen)) { continue; } if (!cmd->cmd2) break; /* word #1 only */ if (mrdb->wcnt == 1) continue; /* word #2 not specified */ wlen = strlen(mrdb->words[1]); if (wlen >= cmd->len2 && strncmp(mrdb->words[1], cmd->cmd2, wlen) == 0) { break; /* word #1 and #2 */ } } } /* divide remain parameters */ if (cmd->cmd1 && cmd->div) { p = mrdb->words[--mrdb->wcnt]; for (; mrdb->wcntwcnt++) { mrdb->words[mrdb->wcnt] = pick_out_word(mrb, &p); if (!mrdb->words[mrdb->wcnt]) { break; } } } return cmd->cmd1 ? cmd : NULL; } static void print_info_stopped_break(mrb_state *mrb, mrdb_state *mrdb) { mrb_debug_breakpoint bp; int32_t ret; uint16_t lineno; const char *file; const char *method_name; const char *class_name; ret = mrb_debug_get_break(mrb, mrdb->dbg, mrdb->dbg->stopped_bpno, &bp); if (ret == 0) { switch(bp.type) { case MRB_DEBUG_BPTYPE_LINE: file = bp.point.linepoint.file; lineno = bp.point.linepoint.lineno; printf("Breakpoint %d, at %s:%d\n", bp.bpno, file, lineno); break; case MRB_DEBUG_BPTYPE_METHOD: method_name = bp.point.methodpoint.method_name; class_name = bp.point.methodpoint.class_name; if (class_name == NULL) { printf("Breakpoint %d, %s\n", bp.bpno, method_name); } else { printf("Breakpoint %d, %s:%s\n", bp.bpno, class_name, method_name); } if (mrdb->dbg->isCfunc) { printf("Stopped before calling the C function.\n"); } break; default: break; } } } static void print_info_stopped_step_next(mrb_state *mrb, mrdb_state *mrdb) { const char* file = mrdb->dbg->prvfile; uint16_t lineno = mrdb->dbg->prvline; printf("%s:%d\n", file, lineno); } static void print_info_stopped_code(mrb_state *mrb, mrdb_state *mrdb) { char* file = mrb_debug_get_source(mrb, mrdb, mrdb->srcpath, mrdb->dbg->prvfile); uint16_t lineno = mrdb->dbg->prvline; if (file != NULL) { mrb_debug_list(mrb, mrdb->dbg, file, lineno, lineno); mrb_free(mrb, file); } } static void print_info_stopped(mrb_state *mrb, mrdb_state *mrdb) { switch(mrdb->dbg->bm) { case BRK_BREAK: print_info_stopped_break(mrb, mrdb); print_info_stopped_code(mrb, mrdb); break; case BRK_STEP: case BRK_NEXT: print_info_stopped_step_next(mrb, mrdb); print_info_stopped_code(mrb, mrdb); break; default: break; } } static debug_command* get_and_parse_command(mrb_state *mrb, mrdb_state *mrdb) { debug_command *cmd = NULL; char *p; int i; while (!cmd) { for (p=NULL; !p || *p=='\0'; ) { printf("(%s:%d) ", mrdb->dbg->prvfile, mrdb->dbg->prvline); fflush(stdout); p = get_command(mrb, mrdb); } cmd = parse_command(mrb, mrdb, p); #ifdef _DBG_MRDB_PARSER_ for (i=0; iwcnt; i++) { printf("%d: %s\n", i, mrdb->words[i]); } #endif if (!cmd) { printf("invalid command ("); for (i=0; iwcnt; i++) { if (i>0) { printf(" "); } printf("%s", mrdb->words[i]); } puts(")"); } } return cmd; } static int32_t check_method_breakpoint(mrb_state *mrb, const mrb_irep *irep, const mrb_code *pc, mrb_value *regs) { struct RClass* c; mrb_sym sym; int32_t bpno; mrb_bool isCfunc; struct mrb_insn_data insn; mrb_debug_context *dbg = mrb_debug_context_get(mrb); isCfunc = FALSE; bpno = dbg->method_bpno; dbg->method_bpno = 0; insn = mrb_decode_insn(pc); switch(insn.insn) { case OP_SEND: case OP_SENDB: c = mrb_class(mrb, regs[insn.a]); sym = irep->syms[insn.b]; break; case OP_SUPER: c = mrb_vm_ci_target_class(mrb->c->ci)->super; sym = mrb->c->ci->mid; break; default: sym = 0; break; } if (sym != 0) { dbg->method_bpno = mrb_debug_check_breakpoint_method(mrb, dbg, c, sym, &isCfunc); if (isCfunc) { bpno = dbg->method_bpno; dbg->method_bpno = 0; } } dbg->isCfunc = isCfunc; return bpno; } static void mrb_code_fetch_hook(mrb_state *mrb, const mrb_irep *irep, const mrb_code *pc, mrb_value *regs) { const char *file; int32_t line; int32_t bpno; mrb_debug_context *dbg = mrb_debug_context_get(mrb); mrb_assert(dbg); dbg->irep = irep; dbg->pc = pc; dbg->regs = regs; if (dbg->xphase == DBG_PHASE_RESTART) { dbg->root_irep = irep; dbg->prvfile = NULL; dbg->prvline = 0; dbg->prvci = NULL; dbg->xm = DBG_RUN; dbg->xphase = DBG_PHASE_RUNNING; } file = mrb_debug_get_filename(mrb, irep, pc - irep->iseq); line = mrb_debug_get_line(mrb, irep, pc - irep->iseq); switch (dbg->xm) { case DBG_STEP: if (*pc != OP_JMP && (!file || (dbg->prvfile == file && dbg->prvline == line))) { return; } dbg->method_bpno = 0; dbg->bm = BRK_STEP; break; case DBG_NEXT: if (!file || (dbg->prvfile == file && dbg->prvline == line)) { return; } if ((intptr_t)(dbg->prvci) < (intptr_t)(mrb->c->ci)) { return; } dbg->prvci = NULL; dbg->method_bpno = 0; dbg->bm = BRK_NEXT; break; case DBG_RUN: bpno = check_method_breakpoint(mrb, irep, pc, regs); if (bpno > 0) { dbg->stopped_bpno = bpno; dbg->bm = BRK_BREAK; break; } if (dbg->prvfile != file || dbg->prvline != line) { bpno = mrb_debug_check_breakpoint_line(mrb, dbg, file, line); if (bpno > 0) { dbg->stopped_bpno = bpno; dbg->bm = BRK_BREAK; break; } } dbg->prvfile = file; dbg->prvline = line; return; case DBG_INIT: dbg->root_irep = irep; dbg->bm = BRK_INIT; if (!file || line < 0) { puts("Cannot get debugging information."); } break; default: return; } dbg->prvfile = file; dbg->prvline = line; if (dbg->bm == BRK_BREAK && --dbg->ccnt > 0) { return; } dbg->break_hook(mrb, dbg); dbg->xphase = DBG_PHASE_RUNNING; } static mrdb_exemode mrb_debug_break_hook(mrb_state *mrb, mrb_debug_context *dbg) { ptrdiff_t regs_off = dbg->regs - mrb->c->ci->stack; debug_command *cmd; dbgcmd_state st = DBGST_CONTINUE; mrdb_state *mrdb = mrdb_state_get(mrb); print_info_stopped(mrb, mrdb); while (1) { cmd = get_and_parse_command(mrb, mrdb); mrb_assert(cmd); st = cmd->func(mrb, mrdb); dbg->regs = mrb->c->ci->stack + regs_off; if ((st == DBGST_CONTINUE) || (st == DBGST_RESTART)) break; } return dbg->xm; } int main(int argc, char **argv) { mrb_state *mrb = mrb_open(); int n = -1; struct _args args; mrb_value v; mrdb_state *mrdb; mrdb_state *mrdb_backup; mrb_debug_context* dbg_backup; debug_command *cmd; l_restart: if (mrb == NULL) { fputs("Invalid mrb_state, exiting mruby\n", stderr); return EXIT_FAILURE; } /* parse command parameters */ n = parse_args(mrb, argc, argv, &args); if (n == EXIT_FAILURE || args.rfp == NULL) { cleanup(mrb, &args); usage(argv[0]); return n; } /* initialize debugger information */ mrdb = mrdb_state_get(mrb); mrb_assert(mrdb && mrdb->dbg); mrdb->srcpath = args.srcpath; if (mrdb->dbg->xm == DBG_QUIT) { mrdb->dbg->xphase = DBG_PHASE_RESTART; } else { mrdb->dbg->xphase = DBG_PHASE_BEFORE_RUN; } mrdb->dbg->xm = DBG_INIT; mrdb->dbg->ccnt = 1; /* setup hook functions */ mrb->code_fetch_hook = mrb_code_fetch_hook; mrdb->dbg->break_hook = mrb_debug_break_hook; if (args.mrbfile) { /* .mrb */ v = mrb_load_irep_file(mrb, args.rfp); } else { /* .rb */ mrb_ccontext *cc = mrb_ccontext_new(mrb); mrb_ccontext_filename(mrb, cc, args.fname); v = mrb_load_file_cxt(mrb, args.rfp, cc); mrb_ccontext_free(mrb, cc); } if (mrdb->dbg->xm == DBG_QUIT && !mrb_undef_p(v) && mrb->exc) { const char *classname = mrb_obj_classname(mrb, mrb_obj_value(mrb->exc)); if (!strcmp(classname, "DebuggerExit")) { cleanup(mrb, &args); return 0; } if (!strcmp(classname, "DebuggerRestart")) { mrdb_backup = mrdb_state_get(mrb); dbg_backup = mrb_debug_context_get(mrb); mrdb_state_set(NULL); mrb_debug_context_set(NULL); cleanup(mrb, &args); mrb = mrb_open(); mrdb_state_set(mrdb_backup); mrb_debug_context_set(dbg_backup); goto l_restart; } } puts("mruby application exited."); mrdb->dbg->xphase = DBG_PHASE_AFTER_RUN; if (!mrb_undef_p(v)) { if (mrb->exc) { mrb_print_error(mrb); } else { printf(" => "); mrb_p(mrb, v); } } mrdb->dbg->prvfile = "-"; mrdb->dbg->prvline = 0; while (1) { cmd = get_and_parse_command(mrb, mrdb); mrb_assert(cmd); if (cmd->id == DBGCMD_QUIT) { break; } if ( cmd->func(mrb, mrdb) == DBGST_RESTART ) goto l_restart; } cleanup(mrb, &args); return 0; } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/PaxHeaders/apistring.c0000644000000000000000000000013215077107276027245 xustar0030 mtime=1761382078.106420629 30 atime=1761382080.126411389 30 ctime=1761382108.874301034 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apistring.c0000644000175100017510000000121215077107276027631 0ustar00runnerrunner/* ** apistring.c ** */ #include #include "apistring.h" static size_t mrb_debug_strnlen(const char *s, size_t maxlen) { const char *p = (const char*)memchr(s, '\0', maxlen); return p != NULL ? (size_t)(p - s) : maxlen; } char* mrdb_strndup(mrb_state *mrb, const char *s, size_t size) { size_t l = mrb_debug_strnlen(s, size); char *d = (char*)mrb_malloc_simple(mrb, l + 1); if (d != NULL) { memcpy(d, s, l); d[l] = '\0'; } return d; } char* mrdb_strdup(mrb_state *mrb, const char *s) { size_t z = strlen(s) + 1; char *d = (char*)mrb_malloc_simple(mrb, z); return d != NULL ? (char*)memcpy(d, s, z) : NULL; } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/PaxHeaders/apibreak.h0000644000000000000000000000013215077107276027030 xustar0030 mtime=1761382078.106420629 30 atime=1761382080.126411389 30 ctime=1761382108.870301046 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.h0000644000175100017510000000226715077107276027427 0ustar00runnerrunner/* ** apibreak.h ** */ #ifndef APIBREAK_H_ #define APIBREAK_H_ #include #include "mrdb.h" int32_t mrb_debug_set_break_line(mrb_state*, mrb_debug_context*, const char*, uint16_t); int32_t mrb_debug_set_break_method(mrb_state*, mrb_debug_context*, const char*, const char*); int32_t mrb_debug_get_breaknum(mrb_state*, mrb_debug_context*); int32_t mrb_debug_get_break_all(mrb_state*, mrb_debug_context*, uint32_t, mrb_debug_breakpoint bp[]); int32_t mrb_debug_get_break(mrb_state*, mrb_debug_context*, uint32_t, mrb_debug_breakpoint*); int32_t mrb_debug_delete_break(mrb_state*, mrb_debug_context*, uint32_t); int32_t mrb_debug_delete_break_all(mrb_state*, mrb_debug_context*); int32_t mrb_debug_enable_break(mrb_state*, mrb_debug_context*, uint32_t); int32_t mrb_debug_enable_break_all(mrb_state*, mrb_debug_context*); int32_t mrb_debug_disable_break(mrb_state*, mrb_debug_context*, uint32_t); int32_t mrb_debug_disable_break_all(mrb_state*, mrb_debug_context*); int32_t mrb_debug_check_breakpoint_line(mrb_state*, mrb_debug_context*, const char*, uint16_t); int32_t mrb_debug_check_breakpoint_method(mrb_state*, mrb_debug_context*, struct RClass*, mrb_sym, mrb_bool*); #endif /* APIBREAK_H_ */ nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-bin-strip0000644000000000000000000000013215077107334022310 xustar0030 mtime=1761382108.899300962 30 atime=1761382109.796298369 30 ctime=1761382108.899300962 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-strip/0000755000175100017510000000000015077107334022755 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-strip/PaxHeaders/bintest0000644000000000000000000000013215077107334023760 xustar0030 mtime=1761382108.900300959 30 atime=1761382109.796298369 30 ctime=1761382108.900300959 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-strip/bintest/0000755000175100017510000000000015077107334024425 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-strip/bintest/PaxHeaders/mruby_strip.rb0000644000000000000000000000013215077107276026746 xustar0030 mtime=1761382078.107420625 30 atime=1761382080.128411379 30 ctime=1761382108.900300959 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-strip/bintest/mruby_strip.rb0000644000175100017510000000436215077107276027343 0ustar00runnerrunnerrequire 'tempfile' assert('no files') do o = `#{cmd('mruby-strip')} 2>&1` assert_equal 1, $?.exitstatus assert_equal "no files to strip", o.split("\n")[0] end assert('file not found') do o = `#{cmd('mruby-strip')} not_found.mrb 2>&1` assert_equal 1, $?.exitstatus assert_equal "can't open file for reading not_found.mrb\n", o end assert('not irep file') do t = Tempfile.new('script.rb') t.write 'p test\n' t.flush o = `#{cmd('mruby-strip')} #{t.path} 2>&1` assert_equal 1, $?.exitstatus assert_equal "can't read irep file #{t.path}\n", o end assert('success') do script_file, compiled1, compiled2 = Tempfile.new('script.rb'), Tempfile.new('c1.mrb'), Tempfile.new('c2.mrb') script_file.write "p 'test'\n" script_file.flush `#{cmd('mrbc')} -g -o #{compiled1.path} #{script_file.path}` `#{cmd('mrbc')} -g -o #{compiled2.path} #{script_file.path}` o = `#{cmd('mruby-strip')} #{compiled1.path}` assert_equal 0, $?.exitstatus assert_equal "", o assert_equal `#{cmd('mruby')} #{script_file.path}`, `#{cmd('mruby')} #{compiled1.path}` o = `#{cmd('mruby-strip')} #{compiled1.path} #{compiled2.path}` assert_equal 0, $?.exitstatus assert_equal "", o end assert('check debug section') do script_file, with_debug, without_debug = Tempfile.new('script.rb'), Tempfile.new('c1.mrb'), Tempfile.new('c2.mrb') script_file.write "p 'test'\n" script_file.flush `#{cmd('mrbc')} -o #{without_debug.path} #{script_file.path}` `#{cmd('mrbc')} -g -o #{with_debug.path} #{script_file.path}` assert_true with_debug.size >= without_debug.size `#{cmd('mruby-strip')} #{with_debug.path}` assert_equal without_debug.size, with_debug.size end assert('check lv section') do script_file, with_lv, without_lv = Tempfile.new('script.rb'), Tempfile.new('c1.mrb'), Tempfile.new('c2.mrb') script_file.write < 'mruby-compiler' spec.bins = %w(mruby-strip) end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-strip/PaxHeaders/tools0000644000000000000000000000013215077107334023450 xustar0030 mtime=1761382108.202302977 30 atime=1761382109.796298369 30 ctime=1761382108.202302977 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-strip/tools/0000755000175100017510000000000015077107334024115 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-strip/tools/PaxHeaders/mruby-strip0000644000000000000000000000013215077107334025745 xustar0030 mtime=1761382108.897300968 30 atime=1761382109.796298369 30 ctime=1761382108.897300968 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-strip/tools/mruby-strip/0000755000175100017510000000000015077107334026412 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-strip/tools/mruby-strip/PaxHeaders/mruby_strip.c0000644000000000000000000000013115077107276030551 xustar0029 mtime=1761382078.10842062 30 atime=1761382080.128411379 30 ctime=1761382108.897300968 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby_strip.c0000644000175100017510000000540015077107276031141 0ustar00runnerrunner#include #ifdef MRB_NO_STDIO # error mruby-bin-strip conflicts 'MRB_NO_STDIO' in your build configuration #endif #include #include #include #include #include struct strip_args { int argc_start; int argc; char **argv; uint8_t flags; }; static void print_usage(const char *f) { printf("Usage: %s [switches] irepfiles\n", f); printf("switches:\n"); printf(" -l, --lvar remove LVAR section too.\n"); } static int parse_args(int argc, char **argv, struct strip_args *args) { int i; args->argc_start = 0; args->argc = argc; args->argv = argv; args->flags = 0; for (i = 1; i < argc; i++) { const size_t len = strlen(argv[i]); if (len >= 2 && argv[i][0] == '-') { switch (argv[i][1]) { case 'l': args->flags = MRB_DUMP_NO_LVAR; break; case '-': if (strncmp((*argv) + 2, "lvar", len) == 0) { args->flags = MRB_DUMP_NO_LVAR; break; } default: return -1; } } else { break; } } args->argc_start = i; return i; } static int strip(mrb_state *mrb, struct strip_args *args) { int i; for (i = args->argc_start; i < args->argc; i++) { char *filename; FILE *rfile; mrb_irep *irep; FILE *wfile; int dump_result; filename = args->argv[i]; rfile = fopen(filename, "rb"); if (rfile == NULL) { fprintf(stderr, "can't open file for reading %s\n", filename); return EXIT_FAILURE; } irep = mrb_read_irep_file(mrb, rfile); fclose(rfile); if (irep == NULL) { fprintf(stderr, "can't read irep file %s\n", filename); return EXIT_FAILURE; } wfile = fopen(filename, "wb"); if (wfile == NULL) { fprintf(stderr, "can't open file for writing %s\n", filename); mrb_irep_decref(mrb, irep); return EXIT_FAILURE; } /* debug flag must always be false */ dump_result = mrb_dump_irep_binary(mrb, irep, args->flags, wfile); fclose(wfile); mrb_irep_decref(mrb, irep); if (dump_result != MRB_DUMP_OK) { fprintf(stderr, "error occurred during dumping %s\n", filename); return EXIT_FAILURE; } } return EXIT_SUCCESS; } int main(int argc, char **argv) { struct strip_args args; int args_result; mrb_state *mrb; int ret; if (argc <= 1) { printf("no files to strip\n"); print_usage(argv[0]); return EXIT_FAILURE; } args_result = parse_args(argc, argv, &args); if (args_result < 0) { print_usage(argv[0]); return EXIT_FAILURE; } mrb = mrb_open_core(); if (mrb == NULL) { fputs("Invalid mrb_state, exiting mruby-strip\n", stderr); return EXIT_FAILURE; } ret = strip(mrb, &args); mrb_close(mrb); return ret; } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-encoding0000644000000000000000000000013215077107334022167 xustar0030 mtime=1761382108.926300884 30 atime=1761382109.796298369 30 ctime=1761382108.926300884 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-encoding/0000755000175100017510000000000015077107334022634 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-encoding/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024366 xustar0030 mtime=1761382078.116420584 30 atime=1761382080.137411338 30 ctime=1761382108.926300884 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-encoding/mrbgem.rake0000644000175100017510000000046315077107276024761 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-encoding') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = "Poorman's Encoding for mruby" spec.build.defines << "HAVE_MRUBY_ENCODING_GEM" spec.build.defines << "MRB_UTF8_STRING" spec.add_test_dependency 'mruby-string-ext' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-encoding/PaxHeaders/test0000644000000000000000000000013215077107334023146 xustar0030 mtime=1761382108.928300878 30 atime=1761382109.796298369 30 ctime=1761382108.928300878 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-encoding/test/0000755000175100017510000000000015077107334023613 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-encoding/test/PaxHeaders/string.rb0000644000000000000000000000013215077107276025063 xustar0030 mtime=1761382078.116420584 30 atime=1761382080.138411334 30 ctime=1761382108.927300881 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-encoding/test/string.rb0000644000175100017510000000137015077107276025454 0ustar00runnerrunner## # String(Ext) Test UTF8STRING = __ENCODING__ == "UTF-8" assert('String#valid_encoding?') do assert_true "hello".valid_encoding? if UTF8STRING assert_true "ã‚".valid_encoding? assert_false "\xfe".valid_encoding? assert_false "ã‚\xfe".valid_encoding? assert_true "ã‚\xfe".b.valid_encoding? else assert_true "\xfe".valid_encoding? end end assert('String#encoding') do if UTF8STRING a = "ã‚" assert_equal Encoding::UTF_8, a.encoding assert_equal Encoding::BINARY, a.b.encoding assert_equal a, a.force_encoding(Encoding::BINARY) assert_equal a, a.force_encoding(Encoding::BINARY) assert_equal Encoding::BINARY, a.encoding else a = "hello" assert_equal Encoding::BINARY, a.encoding end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-encoding/test/PaxHeaders/numeric.rb0000644000000000000000000000013215077107276025217 xustar0030 mtime=1761382078.116420584 30 atime=1761382080.138411334 30 ctime=1761382108.928300878 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-encoding/test/numeric.rb0000644000175100017510000000167115077107276025614 0ustar00runnerrunnerassert('Integer#chr') do assert_equal("A", 65.chr) assert_equal("B", 0x42.chr) assert_equal("\xab", 171.chr) assert_raise(RangeError) { -1.chr } assert_raise(RangeError) { 256.chr } assert_equal("A", 65.chr("ASCII-8BIT")) assert_equal("B", 0x42.chr("BINARY")) assert_equal("\xab", 171.chr("ascii-8bit")) assert_raise(RangeError) { -1.chr("binary") } assert_raise(RangeError) { 256.chr("Ascii-8bit") } assert_raise(ArgumentError) { 65.chr("ASCII") } assert_raise(ArgumentError) { 65.chr("ASCII-8BIT", 2) } assert_raise(TypeError) { 65.chr(:BINARY) } if __ENCODING__ == "ASCII-8BIT" assert_raise(ArgumentError) { 65.chr("UTF-8") } else assert_equal("A", 65.chr("UTF-8")) assert_equal("B", 0x42.chr("UTF-8")) assert_equal("«", 171.chr("utf-8")) assert_equal("ã‚", 12354.chr("Utf-8")) assert_raise(RangeError) { -1.chr("utf-8") } assert_raise(RangeError) { 0x110000.chr.chr("UTF-8") } end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-encoding/PaxHeaders/src0000644000000000000000000000013215077107334022756 xustar0030 mtime=1761382108.929300875 30 atime=1761382109.796298369 30 ctime=1761382108.929300875 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-encoding/src/0000755000175100017510000000000015077107334023423 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-encoding/src/PaxHeaders/encoding.c0000644000000000000000000000013215077107276024772 xustar0030 mtime=1761382078.116420584 30 atime=1761382080.137411338 30 ctime=1761382108.929300875 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-encoding/src/encoding.c0000644000175100017510000000631015077107276025362 0ustar00runnerrunner#include #include #include #include #include #define ENC_ASCII_8BIT "ASCII-8BIT" #define ENC_BINARY "BINARY" #define ENC_UTF8 "UTF-8" #define ENC_COMP_P(enc, enc_lit) \ casecmp_p(RSTRING_PTR(enc), RSTRING_LEN(enc), enc_lit, sizeof(enc_lit"")-1) static mrb_bool casecmp_p(const char *s1, mrb_int len1, const char *s2, mrb_int len2) { if (len1 != len2) return FALSE; const char *e1 = s1 + len1; const char *e2 = s2 + len2; while (s1 < e1 && s2 < e2) { if (*s1 != *s2 && TOUPPER(*s1) != TOUPPER(*s2)) return FALSE; s1++; s2++; } return TRUE; } /* * call-seq: * string.valid_encoding? -> true or false * * Returns true for a string which is encoded correctly. * */ static mrb_value str_valid_enc_p(mrb_state *mrb, mrb_value str) { #define utf8_islead(c) ((unsigned char)((c)&0xc0) != 0x80) struct RString *s = mrb_str_ptr(str); if (RSTR_SINGLE_BYTE_P(s)) return mrb_true_value(); if (RSTR_BINARY_P(s)) return mrb_true_value(); mrb_int byte_len = RSTR_LEN(s); mrb_int utf8_len = 0; const char *p = RSTR_PTR(s); const char *e = p + byte_len; while (p < e) { mrb_int len = mrb_utf8len(p, e); if (len == 1 && (*p & 0x80)) return mrb_false_value(); p += len; utf8_len++; } if (byte_len == utf8_len) RSTR_SET_SINGLE_BYTE_FLAG(s); return mrb_true_value(); } static mrb_value get_encoding(mrb_state *mrb, mrb_sym enc) { struct RClass *e = mrb_module_get_id(mrb, MRB_SYM(Encoding)); return mrb_const_get(mrb, mrb_obj_value(e), enc); } static mrb_value str_encoding(mrb_state *mrb, mrb_value self) { struct RString *s = mrb_str_ptr(self); if (RSTR_BINARY_P(s)) { return get_encoding(mrb, MRB_SYM(BINARY)); } return get_encoding(mrb, MRB_SYM(UTF_8)); } static mrb_value str_force_encoding(mrb_state *mrb, mrb_value self) { mrb_value enc; mrb_get_args(mrb, "S", &enc); struct RString *s = mrb_str_ptr(self); if (ENC_COMP_P(enc, ENC_ASCII_8BIT) || ENC_COMP_P(enc, ENC_BINARY)) { s->flags |= MRB_STR_BINARY; } else if (ENC_COMP_P(enc, ENC_UTF8)) { s->flags &= ~MRB_STR_BINARY; } else { mrb_raisef(mrb, E_ARGUMENT_ERROR, "unknown encoding name - %v", enc); } return self; } void mrb_mruby_encoding_gem_init(mrb_state* mrb) { struct RClass *s = mrb->string_class; mrb_define_method_id(mrb, s, MRB_SYM_Q(valid_encoding), str_valid_enc_p, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_SYM(encoding), str_encoding, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_SYM(force_encoding), str_force_encoding, MRB_ARGS_REQ(1)); /* Poorman's Encoding * * Encoding - module instead of class * encodings - supports only UTF-8 and ASCII-8BIT (and its alias BINARY) * each Encoding - encoding name string instead of Encoding object * */ struct RClass *e = mrb_define_module_id(mrb, MRB_SYM(Encoding)); mrb_value b = mrb_str_new_lit_frozen(mrb, ENC_ASCII_8BIT); mrb_define_const_id(mrb, e, MRB_SYM(ASCII_8BIT), b); mrb_define_const_id(mrb, e, MRB_SYM(BINARY), b); mrb_define_const_id(mrb, e, MRB_SYM(UTF_8), mrb_str_new_lit_frozen(mrb, ENC_UTF8)); } void mrb_mruby_encoding_gem_final(mrb_state* mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-data0000644000000000000000000000013215077107334021312 xustar0030 mtime=1761382108.709301511 30 atime=1761382109.796298369 30 ctime=1761382108.709301511 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-data/0000755000175100017510000000000015077107334021757 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-data/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276023511 xustar0030 mtime=1761382078.115420588 30 atime=1761382080.137411338 30 ctime=1761382108.709301511 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-data/mrbgem.rake0000644000175100017510000000023415077107276024100 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-data') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'standard Data class' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-data/PaxHeaders/test0000644000000000000000000000013215077107334022271 xustar0030 mtime=1761382108.710301508 30 atime=1761382109.796298369 30 ctime=1761382108.710301508 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-data/test/0000755000175100017510000000000015077107334022736 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-data/test/PaxHeaders/data.rb0000644000000000000000000000013215077107276023611 xustar0030 mtime=1761382078.115420588 30 atime=1761382080.137411338 30 ctime=1761382108.710301508 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-data/test/data.rb0000644000175100017510000000277715077107276024216 0ustar00runnerrunner## # Struct ISO Test assert('Data') do assert_equal Class, Data.class end assert('Data.define') do c = Data.define(:m1, :m2) assert_equal Data, c.superclass assert_equal [:m1, :m2], c.members end assert('Data#==') do c = Data.define(:m1, :m2) cc1 = c.new(1,2) cc2 = c.new(1,2) assert_true cc1 == cc2 end assert('Data#members') do c = Data.define(:m1, :m2) assert_equal [:m1, :m2], c.new(1,2).members end assert('wrong struct arg count') do c = Data.define(:m1) assert_raise ArgumentError do cc = c.new(1,2,3) end end assert('data dup') do c = Data.define(:m1, :m2, :m3, :m4, :m5) cc = c.new(1,2,3,4,5) assert_nothing_raised { assert_equal(cc, cc.dup) } end assert('Data inspect') do c = Data.define(:m1, :m2, :m3, :m4, :m5) cc = c.new(1,2,3,4,5) assert_equal "#", cc.inspect end assert('Data#to_h') do s = Data.define(:white, :red, :green).new('ruuko', 'yuzuki', 'hitoe') assert_equal({:white => 'ruuko', :red => 'yuzuki', :green => 'hitoe'}) { s.to_h } end assert("Data.define does not allow array") do assert_raise(TypeError) do Data.define("Test", [:a]) end end assert("Data.define generates subclass of Data") do begin original_struct = Data Data = String assert_equal original_struct, original_struct.define(:foo).superclass ensure Data = original_struct end end assert 'Data#freeze' do c = Data.define(:m) o = c.new(:test) assert_equal :test, o.m assert_nothing_raised { o.freeze } end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-data/PaxHeaders/src0000644000000000000000000000013215077107334022101 xustar0030 mtime=1761382108.712301503 30 atime=1761382109.796298369 30 ctime=1761382108.712301503 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-data/src/0000755000175100017510000000000015077107334022546 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-data/src/PaxHeaders/data.c0000644000000000000000000000013215077107276023240 xustar0030 mtime=1761382078.115420588 30 atime=1761382080.137411338 30 ctime=1761382108.712301503 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-data/src/data.c0000644000175100017510000003340315077107276023633 0ustar00runnerrunner/* ** data.c - Data class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include #define RDATA_LEN(st) RARRAY_LEN(st) #define RDATA_PTR(st) RARRAY_PTR(st) #define data_p(o) (mrb_type(o) == MRB_TT_STRUCT) static struct RClass * data_class(mrb_state *mrb) { return mrb_class_get_id(mrb, MRB_SYM(Data)); } static void data_corrupted(mrb_state *mrb) { mrb_raise(mrb, E_TYPE_ERROR, "corrupted data"); } static mrb_value data_s_members(mrb_state *mrb, struct RClass *c) { struct RClass* sclass = data_class(mrb); for (;;) { mrb_value mem = mrb_iv_get(mrb, mrb_obj_value(c), MRB_SYM(__members__)); if (!mrb_nil_p(mem)) { if (!mrb_array_p(mem)) { data_corrupted(mrb); } return mem; } c = c->super; if (c == sclass || c == 0) { mrb_raise(mrb, E_TYPE_ERROR, "uninitialized data"); } } } static mrb_value data_members(mrb_state *mrb, mrb_value obj) { if (!data_p(obj) || RDATA_LEN(obj) == 0) { data_corrupted(mrb); } mrb_value members = data_s_members(mrb, mrb_obj_class(mrb, obj)); if (RDATA_LEN(obj) != RARRAY_LEN(members)) { mrb_raisef(mrb, E_TYPE_ERROR, "data size differs (%i required %i given)", RARRAY_LEN(members), RDATA_LEN(obj)); } return members; } static mrb_value mrb_data_s_members(mrb_state *mrb, mrb_value klass) { mrb_value members = data_s_members(mrb, mrb_class_ptr(klass)); return mrb_ary_new_from_values(mrb, RARRAY_LEN(members), RARRAY_PTR(members)); } /* * call-seq: * data.members -> array * * Returns an array of strings representing the names of the instance * variables. * * Customer = Data.define(:name, :address, :zip) * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) * joe.members #=> [:name, :address, :zip] */ static mrb_value mrb_data_members(mrb_state *mrb, mrb_value obj) { return mrb_data_s_members(mrb, mrb_obj_value(mrb_obj_class(mrb, obj))); } static mrb_value data_ref(mrb_state *mrb, mrb_value obj, mrb_int i) { mrb_int len = RDATA_LEN(obj); mrb_value *ptr = RDATA_PTR(obj); if (!ptr || len <= i) return mrb_nil_value(); return ptr[i]; } static mrb_value mrb_data_ref(mrb_state *mrb, mrb_value obj) { mrb_int argc = mrb_get_argc(mrb); if (argc != 0) { mrb_argnum_error(mrb, argc, 0, 0); } mrb_int i = mrb_integer(mrb_proc_cfunc_env_get(mrb, 0)); return data_ref(mrb, obj, i); } static mrb_value data_ref_0(mrb_state *mrb, mrb_value obj) { return data_ref(mrb, obj, 0); } static mrb_value data_ref_1(mrb_state *mrb, mrb_value obj) { return data_ref(mrb, obj, 1); } static mrb_value data_ref_2(mrb_state *mrb, mrb_value obj) { return data_ref(mrb, obj, 2); } static mrb_value data_ref_3(mrb_state *mrb, mrb_value obj) { return data_ref(mrb, obj, 3); } #define DATA_DIRECT_REF_MAX 4 static mrb_func_t aref[DATA_DIRECT_REF_MAX] = { data_ref_0, data_ref_1, data_ref_2, data_ref_3, }; static void make_data_define_accessors(mrb_state *mrb, mrb_value members, struct RClass *c) { const mrb_value *ptr_members = RARRAY_PTR(members); mrb_int len = RARRAY_LEN(members); int ai = mrb_gc_arena_save(mrb); for (mrb_int i=0; ic->ci->nk > 0) { mrb_value tmp = mrb_str_new(mrb, NULL, sizeof(mrb_sym)*n); mrb_sym *knames = (mrb_sym*)RSTRING_PTR(tmp); mrb_value m = mrb_ary_new_capa(mrb, n); vals = RARRAY_PTR(m); for (mrb_int i=0; ibasic.c->super = c->c; */ make_data_define_accessors(mrb, members, c); mrb_value data = mrb_obj_value(c); mrb_iv_set(mrb, data, MRB_SYM(__members__), members); return data; } /* * call-seq: * DataClass.new(arg, ...) -> obj * * Data::define returns a new Class object, * which can then be used to create specific instances of the new * data structure. The number of actual parameters must be * equal to the number of attributes defined for this class. * Passing too many or too less parameters will raise an * ArgumentError. * * The remaining methods listed in this section (class and instance) * are defined for this generated class. * * # Create a structure named by its constant * Customer = Data.define(:name, :address) #=> Customer * Customer.new("Dave", "123 Main") #=> # */ static mrb_value mrb_data_s_def(mrb_state *mrb, mrb_value klass) { const mrb_value *argv; mrb_int argc; mrb_value b; mrb_get_args(mrb, "*&", &argv, &argc, &b); mrb_value rest = mrb_ary_new_from_values(mrb, argc, argv); for (mrb_int i=0; i true or false * * Equality---Returns true if other_data is * equal to this one: they must be of the same class as generated by * Data::define, and all values of must be equal * (according to Object#==). * * Customer = Data.define(:name, :address, :zip) * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) * joe2 = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) * jane = Customer.new("Jane Doe", "456 Elm, Anytown NC", 12345) * joe == joe2 #=> true * joe == jane #=> false */ static mrb_value mrb_data_equal(mrb_state *mrb, mrb_value s) { mrb_value s2 = mrb_get_arg1(mrb); if (mrb_obj_equal(mrb, s, s2)) { return mrb_true_value(); } if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) { return mrb_false_value(); } if (RDATA_LEN(s) != RDATA_LEN(s2)) { return mrb_false_value(); } mrb_value *ptr = RDATA_PTR(s); mrb_value *ptr2 = RDATA_PTR(s2); mrb_int len = RDATA_LEN(s); int ai = mrb_gc_arena_save(mrb); for (mrb_int i=0; i true or false * * Two structures are equal if they are the same object, or if all their * fields are equal (using Object#eql?). */ static mrb_value mrb_data_eql(mrb_state *mrb, mrb_value s) { mrb_value s2 = mrb_get_arg1(mrb); if (mrb_obj_equal(mrb, s, s2)) { return mrb_true_value(); } if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) { return mrb_false_value(); } if (RDATA_LEN(s) != RDATA_LEN(s2)) { return mrb_false_value(); } mrb_value *ptr = RDATA_PTR(s); mrb_value *ptr2 = RDATA_PTR(s2); mrb_int len = RDATA_LEN(s); for (mrb_int i=0; i hash * * Create a hash from member names and values. */ static mrb_value mrb_data_to_h(mrb_state *mrb, mrb_value self) { mrb_value members = data_members(mrb, self); mrb_value *mems = RARRAY_PTR(members); mrb_value ret = mrb_hash_new_capa(mrb, RARRAY_LEN(members)); mrb_int len = RARRAY_LEN(members); for (mrb_int i=0; i string * data.inspect -> string * * Returns a string representation of Data */ static mrb_value mrb_data_to_s(mrb_state *mrb, mrb_value self) { mrb_value members = data_members(mrb, self); mrb_int mlen = RARRAY_LEN(members); mrb_value *mems = RARRAY_PTR(members); mrb_value ret = mrb_str_new_lit(mrb, "#0) mrb_str_cat_lit(mrb, ret, ", "); mrb_str_cat(mrb, ret, name, len); mrb_str_cat_lit(mrb, ret, "="); mrb_str_cat_str(mrb, ret, mrb_inspect(mrb, RARRAY_PTR(self)[i])); mrb_gc_arena_restore(mrb, ai); } mrb_str_cat_lit(mrb, ret, ">"); return ret; } /* * A Data is a convenient way to bundle a number of * attributes together, using accessor methods, without having to write * an explicit class. * * The Data class is a generator of specific classes, * each one of which is defined to hold a set of variables and their * accessors. In these examples, we'll call the generated class * "CustomerClass," and we'll show an example instance of that * class as "CustomerInst." * * In the descriptions that follow, the parameter symbol refers * to a symbol (such as :name). */ void mrb_mruby_data_gem_init(mrb_state* mrb) { struct RClass *d = mrb_define_class_id(mrb, MRB_SYM(Data), mrb->object_class); MRB_SET_INSTANCE_TT(d, MRB_TT_STRUCT); MRB_UNDEF_ALLOCATOR(d); mrb_undef_class_method_id(mrb, d, MRB_SYM(new)); mrb_define_class_method_id(mrb, d, MRB_SYM(define), mrb_data_s_def, MRB_ARGS_ANY()); mrb_define_method_id(mrb, d, MRB_OPSYM(eq), mrb_data_equal, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, d, MRB_SYM(members), mrb_data_members, MRB_ARGS_NONE()); mrb_define_method_id(mrb, d, MRB_SYM(initialize), mrb_data_initialize, MRB_ARGS_ANY()); mrb_define_method_id(mrb, d, MRB_SYM(initialize_copy), mrb_data_init_copy, MRB_ARGS_ANY()); mrb_define_method_id(mrb, d, MRB_SYM_Q(eql), mrb_data_eql, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, d, MRB_SYM(to_h), mrb_data_to_h, MRB_ARGS_NONE()); mrb_define_method_id(mrb, d, MRB_SYM(to_s), mrb_data_to_s, MRB_ARGS_NONE()); mrb_define_method_id(mrb, d, MRB_SYM(inspect), mrb_data_to_s, MRB_ARGS_NONE()); } void mrb_mruby_data_gem_final(mrb_state* mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-bin-config0000644000000000000000000000013215077107334022414 xustar0030 mtime=1761382108.701301534 30 atime=1761382109.796298369 30 ctime=1761382108.701301534 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-config/0000755000175100017510000000000015077107334023061 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-config/PaxHeaders/mruby-config.bat0000644000000000000000000000013215077107276025567 xustar0030 mtime=1761382078.105420634 30 atime=1761382080.126411389 30 ctime=1761382108.701301534 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-config/mruby-config.bat0000644000175100017510000000403315077107276026157 0ustar00runnerrunner@echo off set MRUBY_PACKAGE_DIR=%~dp0.. :top shift if "%0" equ "" goto :eof if "%0" equ "--cc" goto cc if "%0" equ "--cflags" goto cflags if "%0" equ "--cxx" goto cxx if "%0" equ "--cxxflags" goto cxxflags if "%0" equ "--as" goto as if "%0" equ "--asflags" goto asflags if "%0" equ "--objc" goto objc if "%0" equ "--objcflags" goto objcflags if "%0" equ "--ld" goto ld if "%0" equ "--ldflags" goto ldflags if "%0" equ "--ldflags-before-libs" goto ldflagsbeforelibs if "%0" equ "--libs" goto libs if "%0" equ "--libmruby-path" goto libmrubypath if "%0" equ "--help" goto showhelp echo Invalid Option goto :eof :cc echo MRUBY_CC goto top :cflags echo MRUBY_CFLAGS goto top :cxx echo MRUBY_CXX goto top :cxxflags echo MRUBY_CXXFLAGS goto top :as echo MRUBY_AS goto top :asflags echo MRUBY_ASFLAGS goto top :objc echo MRUBY_OBJC goto top :objcflags echo MRUBY_OBJCFLAGS goto top :ld echo MRUBY_LD goto top :libs echo MRUBY_LIBS goto top :ldflags echo MRUBY_LDFLAGS goto top :ldflagsbeforelibs echo MRUBY_LDFLAGS_BEFORE_LIBS goto top :libmrubypath echo MRUBY_LIBMRUBY_PATH goto top :showhelp echo Usage: mruby-config [switches] echo switches: echo --cc print C compiler name echo --cflags print flags passed to C compiler echo --cxx print C++ compiler name echo --cxxflags print flags passed to C++ compiler echo --as print assembler name echo --asflags print flags passed to assembler echo --objc print Objective C compiler name echo --objcflags print flags passed to Objective C compiler echo --ld print linker name echo --ldflags print flags passed to linker echo --ldflags-before-libs print flags passed to linker before linked libraries echo --libs print linked libraries echo --libmruby-path print libmruby path nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-config/PaxHeaders/mrbgem.rake0000644000000000000000000000013115077107276024612 xustar0030 mtime=1761382078.105420634 30 atime=1761382080.126411389 29 ctime=1761382108.69930154 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-config/mrbgem.rake0000644000175100017510000000255115077107276025206 0ustar00runnerrunneriscross = MRuby::Build.current.kind_of?(MRuby::CrossBuild) MRuby::Gem::Specification.new('mruby-bin-config') do |spec| name = 'mruby-config' spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = "#{name} command" if iscross mruby_config_dir = "#{build.build_dir}/host-bin" else mruby_config_dir = "#{build.build_dir}/bin" end if ENV['OS'] == 'Windows_NT' suffix = '.bat' refvar = '%\\1%' else suffix = '' refvar = '${\\1}' end mruby_config = name + suffix mruby_config_path = "#{mruby_config_dir}/#{mruby_config}" make_cfg = "#{build.build_dir}/#{build.libdir_name}/libmruby.flags.mak" tmplt_path = "#{__dir__}/#{mruby_config}" if iscross build.products << mruby_config_path else build.bins << mruby_config end directory mruby_config_dir file mruby_config_path => [__FILE__, mruby_config_dir, make_cfg, tmplt_path] do |t| config = Hash[File.readlines(make_cfg).map!(&:chomp).map! {|l| l.gsub!(/\$\((\w+)\)/, refvar) l.gsub('\\"', '"').split(' = ', 2).map! {|s| s.sub(/^(?=.)/, 'echo ')} }] tmplt = File.read(tmplt_path) tmplt.sub!(%r((?<=\A#!/bin/sh\n\n)), <<~SETDIR) MRUBY_PACKAGE_DIR=$(dirname "$(dirname "$(readlink -f "$0")")") SETDIR File.write(t.name, tmplt.gsub(/(#{Regexp.union(*config.keys)})\b/, config)) chmod(0755, t.name) end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-config/PaxHeaders/mruby-config0000644000000000000000000000013215077107276025022 xustar0030 mtime=1761382078.105420634 30 atime=1761382080.126411389 30 ctime=1761382108.700301538 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-config/mruby-config0000755000175100017510000000312415077107276025415 0ustar00runnerrunner#!/bin/sh print_help() { echo "Usage: mruby-config [switches]" echo " switches:" echo " --cc print C compiler name" echo " --cflags print flags passed to C compiler" echo " --cxx print C++ compiler name" echo " --cxxflags print flags passed to C++ compiler" echo " --as print assembler name" echo " --asflags print flags passed to assembler" echo " --objc print Objective C compiler name" echo " --objcflags print flags passed to Objective C compiler" echo " --ld print linker name" echo " --ldflags print flags passed to linker" echo " --ldflags-before-libs print flags passed to linker before linked libraries" echo " --libs print linked libraries" echo " --libmruby-path print libmruby path" echo " --help print this help" exit 0 } if [ $# -eq 0 ]; then print_help fi while [ $# -gt 0 ]; do case $1 in --cc) echo MRUBY_CC;; --cflags) echo MRUBY_CFLAGS;; --cxx) echo MRUBY_CXX;; --cxxflags) echo MRUBY_CXXFLAGS;; --as) echo MRUBY_AS;; --asflags) echo MRUBY_ASFLAGS;; --objc) echo MRUBY_OBJC;; --objcflags) echo MRUBY_OBJCFLAGS;; --ld) echo MRUBY_LD;; --ldflags) echo MRUBY_LDFLAGS;; --ldflags-before-libs) echo MRUBY_LDFLAGS_BEFORE_LIBS;; --libs) echo MRUBY_LIBS;; --libmruby-path) echo MRUBY_LIBMRUBY_PATH;; *) print_help;; esac shift done nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-array-ext0000644000000000000000000000013215077107334022315 xustar0030 mtime=1761382108.888300994 30 atime=1761382109.796298369 30 ctime=1761382108.888300994 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-array-ext/0000755000175100017510000000000015077107334022762 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-array-ext/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024514 xustar0030 mtime=1761382078.104420639 30 atime=1761382080.125411393 30 ctime=1761382108.888300994 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-array-ext/mrbgem.rake0000644000175100017510000000024315077107276025103 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-array-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Array class extension' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-array-ext/PaxHeaders/mrblib0000644000000000000000000000013215077107334023564 xustar0030 mtime=1761382108.891300985 30 atime=1761382109.796298369 30 ctime=1761382108.891300985 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-array-ext/mrblib/0000755000175100017510000000000015077107334024231 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-array-ext/mrblib/PaxHeaders/array.rb0000644000000000000000000000013215077107276025311 xustar0030 mtime=1761382078.104420639 30 atime=1761382080.125411393 30 ctime=1761382108.891300985 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-array-ext/mrblib/array.rb0000644000175100017510000006446215077107276025715 0ustar00runnerrunnerclass Array ## # call-seq: # ary.uniq! -> ary or nil # ary.uniq! { |item| ... } -> ary or nil # # Removes duplicate elements from +self+. # Returns nil if no changes are made (that is, no # duplicates are found). # # a = [ "a", "a", "b", "b", "c" ] # a.uniq! #=> ["a", "b", "c"] # b = [ "a", "b", "c" ] # b.uniq! #=> nil # c = [["student","sam"], ["student","george"], ["teacher","matz"]] # c.uniq! { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]] # def uniq!(&block) hash = {} if block self.each do |val| key = block.call(val) hash[key] = val unless hash.key?(key) end result = hash.values else hash = {} self.each do |val| hash[val] = val end result = hash.keys end if result.size == self.size nil else self.replace(result) end end ## # call-seq: # ary.uniq -> new_ary # ary.uniq { |item| ... } -> new_ary # # Returns a new array by removing duplicate values in +self+. # # a = [ "a", "a", "b", "b", "c" ] # a.uniq #=> ["a", "b", "c"] # # b = [["student","sam"], ["student","george"], ["teacher","matz"]] # b.uniq { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]] # def uniq(&block) ary = self[0..-1] ary.uniq!(&block) ary end ## # call-seq: # ary - other_ary -> new_ary # # Array Difference---Returns a new array that is a copy of # the original array, removing any items that also appear in # other_ary. (If you need set-like behavior, see the # library class Set.) # # [ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ] #=> [ 3, 3, 5 ] # def -(elem) raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array hash = {} array = [] idx = 0 len = elem.size while idx < len hash[elem[idx]] = true idx += 1 end idx = 0 len = size while idx < len v = self[idx] array << v unless hash[v] idx += 1 end array end ## # call-seq: # ary.difference(other_ary1, other_ary2, ...) -> new_ary # # Returns a new array that is a copy of the original array, removing all # occurrences of any item that also appear in +other_ary+. The order is # preserved from the original array. # def difference(*args) ary = self args.each do |x| ary = ary - x end ary end ## # call-seq: # ary | other_ary -> new_ary # # Set Union---Returns a new array by joining this array with # other_ary, removing duplicates. # # [ "a", "b", "c" ] | [ "c", "d", "a" ] # #=> [ "a", "b", "c", "d" ] # def |(elem) raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array ary = self + elem ary.uniq! or ary end ## # call-seq: # ary.union(other_ary,...) -> new_ary # # Set Union---Returns a new array by joining this array with # other_ary, removing duplicates. # # ["a", "b", "c"].union(["c", "d", "a"], ["a", "c", "e"]) # #=> ["a", "b", "c", "d", "e"] # def union(*args) ary = self.dup args.each do |x| ary.concat(x) ary.uniq! end ary end ## # call-seq: # ary & other_ary -> new_ary # # Set Intersection---Returns a new array # containing elements common to the two arrays, with no duplicates. # # [ 1, 1, 3, 5 ] & [ 1, 2, 3 ] #=> [ 1, 3 ] # def &(elem) raise TypeError, "cannot convert #{elem.class} into Array" unless elem.class == Array hash = {} array = [] idx = 0 len = elem.size while idx < len hash[elem[idx]] = true idx += 1 end idx = 0 len = size while idx < len v = self[idx] if hash[v] array << v hash.delete v end idx += 1 end array end ## # call-seq: # ary.intersection(other_ary,...) -> new_ary # # Set Intersection---Returns a new array containing elements common to # this array and other_arys, removing duplicates. The order is # preserved from the original array. # # [1, 2, 3].intersection([3, 4, 1], [1, 3, 5]) #=> [1, 3] # def intersection(*args) ary = self args.each do |x| ary = ary & x end ary end ## # call-seq: # ary.intersect?(other_ary) -> true or false # # Returns +true+ if the array and +other_ary+ have at least one element in # common, otherwise returns +false+. # # a = [ 1, 2, 3 ] # b = [ 3, 4, 5 ] # c = [ 5, 6, 7 ] # a.intersect?(b) #=> true # a.intersect?(c) #=> false def intersect?(ary) raise TypeError, "cannot convert #{ary.class} into Array" unless ary.class == Array hash = {} if self.length > ary.length shorter = ary longer = self else shorter = self longer = ary end idx = 0 len = shorter.size while idx < len hash[shorter[idx]] = true idx += 1 end idx = 0 len = size while idx < len v = longer[idx] if hash[v] return true end idx += 1 end false end ## # call-seq: # ary.flatten -> new_ary # ary.flatten(level) -> new_ary # # Returns a new array that is a one-dimensional flattening of this # array (recursively). That is, for every element that is an array, # extract its elements into the new array. If the optional # level argument determines the level of recursion to flatten. # # s = [ 1, 2, 3 ] #=> [1, 2, 3] # t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]] # a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10] # a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # a = [ 1, 2, [3, [4, 5] ] ] # a.flatten(1) #=> [1, 2, 3, [4, 5]] # def flatten(depth=nil) res = Array.new(self) res.flatten! depth res end ## # call-seq: # ary.flatten! -> ary or nil # ary.flatten!(level) -> array or nil # # Flattens +self+ in place. # Returns nil if no modifications were made (i.e., # ary contains no subarrays.) If the optional level # argument determines the level of recursion to flatten. # # a = [ 1, 2, [3, [4, 5] ] ] # a.flatten! #=> [1, 2, 3, 4, 5] # a.flatten! #=> nil # a #=> [1, 2, 3, 4, 5] # a = [ 1, 2, [3, [4, 5] ] ] # a.flatten!(1) #=> [1, 2, 3, [4, 5]] # def flatten!(depth=nil) modified = false ar = [] idx = 0 len = size while idx < len e = self[idx] if e.is_a?(Array) && (depth.nil? || depth > 0) ar += e.flatten(depth.nil? ? nil : depth - 1) modified = true else ar << e end idx += 1 end if modified self.replace(ar) else nil end end # for efficiency def reverse_each(&block) return to_enum :reverse_each unless block i = self.size - 1 while i>=0 block.call(self[i]) i -= 1 end self end ## # call-seq: # ary.fetch(index) -> obj # ary.fetch(index, default) -> obj # ary.fetch(index) { |index| block } -> obj # # Tries to return the element at position +index+, but throws an IndexError # exception if the referenced +index+ lies outside of the array bounds. This # error can be prevented by supplying a second argument, which will act as a # +default+ value. # # Alternatively, if a block is given it will only be executed when an # invalid +index+ is referenced. # # Negative values of +index+ count from the end of the array. # # a = [ 11, 22, 33, 44 ] # a.fetch(1) #=> 22 # a.fetch(-1) #=> 44 # a.fetch(4, 'cat') #=> "cat" # a.fetch(100) { |i| puts "#{i} is out of bounds" } # #=> "100 is out of bounds" # def fetch(n, ifnone=NONE, &block) #warn "block supersedes default value argument" if !n.nil? && ifnone != NONE && block idx = n.__to_int if idx < 0 idx += size end if idx < 0 || size <= idx return block.call(n) if block if NONE.equal?(ifnone) raise IndexError, "index #{n} outside of array bounds: #{-size}...#{size}" end return ifnone end self[idx] end ## # call-seq: # ary.fill(obj) -> ary # ary.fill(obj, start [, length]) -> ary # ary.fill(obj, range ) -> ary # ary.fill { |index| block } -> ary # ary.fill(start [, length] ) { |index| block } -> ary # ary.fill(range) { |index| block } -> ary # # The first three forms set the selected elements of +self+ (which # may be the entire array) to +obj+. # # A +start+ of +nil+ is equivalent to zero. # # A +length+ of +nil+ is equivalent to the length of the array. # # The last three forms fill the array with the value of the given block, # which is passed the absolute index of each element to be filled. # # Negative values of +start+ count from the end of the array, where +-1+ is # the last element. # # a = [ "a", "b", "c", "d" ] # a.fill("x") #=> ["x", "x", "x", "x"] # a.fill("w", -1) #=> ["x", "x", "x", "w"] # a.fill("z", 2, 2) #=> ["x", "x", "z", "z"] # a.fill("y", 0..1) #=> ["y", "y", "z", "z"] # a.fill { |i| i*i } #=> [0, 1, 4, 9] # a.fill(-2) { |i| i*i*i } #=> [0, 1, 8, 27] # a.fill(1, 2) { |i| i+1 } #=> [0, 2, 3, 27] # a.fill(0..1) { |i| i+1 } #=> [1, 2, 3, 27] # def fill(arg0=nil, arg1=nil, arg2=nil, &block) if arg0.nil? && arg1.nil? && arg2.nil? && !block raise ArgumentError, "wrong number of arguments (given 0, expected 1..3)" end beg = len = 0 if block if arg0.nil? && arg1.nil? && arg2.nil? # ary.fill { |index| block } -> ary beg = 0 len = self.size elsif !arg0.nil? && arg0.kind_of?(Range) # ary.fill(range) { |index| block } -> ary beg = arg0.begin beg += self.size if beg < 0 len = arg0.end len += self.size if len < 0 len += 1 unless arg0.exclude_end? elsif !arg0.nil? # ary.fill(start [, length] ) { |index| block } -> ary beg = arg0.__to_int beg += self.size if beg < 0 if arg1.nil? len = self.size else len = beg + arg1.__to_int end end else if !arg0.nil? && arg1.nil? && arg2.nil? # ary.fill(obj) -> ary beg = 0 len = self.size elsif !arg0.nil? && !arg1.nil? && arg1.kind_of?(Range) # ary.fill(obj, range ) -> ary beg = arg1.begin beg += self.size if beg < 0 len = arg1.end len += self.size if len < 0 len += 1 unless arg1.exclude_end? elsif !arg0.nil? && !arg1.nil? # ary.fill(obj, start [, length]) -> ary beg = arg1.__to_int beg += self.size if beg < 0 if arg2.nil? len = self.size else len = beg + arg2.__to_int end end end i = beg if block while i < len self[i] = block.call(i) i += 1 end else while i < len self[i] = arg0 i += 1 end end self end ## # call-seq: # ary.delete_if { |item| block } -> ary # ary.delete_if -> Enumerator # # Deletes every element of +self+ for which block evaluates to +true+. # # The array is changed instantly every time the block is called, not after # the iteration is over. # # See also Array#reject! # # If no block is given, an Enumerator is returned instead. # # scores = [ 97, 42, 75 ] # scores.delete_if {|score| score < 80 } #=> [97] def delete_if(&block) return to_enum :delete_if unless block result = [] idx = 0 len = size while idx < len elem = self[idx] result << elem unless block.call(elem) idx += 1 end self.replace(result) end ## # call-seq: # ary.reject! { |item| block } -> ary or nil # ary.reject! -> Enumerator # # Equivalent to Array#delete_if, deleting elements from +self+ for which the # block evaluates to +true+, but returns +nil+ if no changes were made. # # The array is changed instantly every time the block is called, not after # the iteration is over. # # See also Enumerable#reject and Array#delete_if. # # If no block is given, an Enumerator is returned instead. def reject!(&block) return to_enum :reject! unless block result = [] idx = 0 len = size while idx < len elem = self[idx] result << elem unless block.call(elem) idx += 1 end return nil if len == result.size self.replace(result) end ## # call-seq: # ary.insert(index, obj...) -> ary # # Inserts the given values before the element with the given +index+. # # Negative indices count backwards from the end of the array, where +-1+ is # the last element. # # a = %w{ a b c d } # a.insert(2, 99) #=> ["a", "b", 99, "c", "d"] # a.insert(-2, 1, 2, 3) #=> ["a", "b", 99, "c", 1, 2, 3, "d"] def insert(idx, *args) idx = idx.__to_int idx += self.size + 1 if idx < 0 self[idx, 0] = args self end ## # call-seq: # ary.bsearch {|x| block } -> elem # # By using binary search, finds a value from this array which meets # the given condition in O(log n) where n is the size of the array. # # You can use this method in two use cases: a find-minimum mode and # a find-any mode. In either case, the elements of the array must be # monotone (or sorted) with respect to the block. # # In find-minimum mode (this is a good choice for typical use case), # the block must return true or false, and there must be an index i # (0 <= i <= ary.size) so that: # # - the block returns false for any element whose index is less than # i, and # - the block returns true for any element whose index is greater # than or equal to i. # # This method returns the i-th element. If i is equal to ary.size, # it returns nil. # # ary = [0, 4, 7, 10, 12] # ary.bsearch {|x| x >= 4 } #=> 4 # ary.bsearch {|x| x >= 6 } #=> 7 # ary.bsearch {|x| x >= -1 } #=> 0 # ary.bsearch {|x| x >= 100 } #=> nil # # In find-any mode (this behaves like libc's bsearch(3)), the block # must return a number, and there must be two indices i and j # (0 <= i <= j <= ary.size) so that: # # - the block returns a positive number for ary[k] if 0 <= k < i, # - the block returns zero for ary[k] if i <= k < j, and # - the block returns a negative number for ary[k] if # j <= k < ary.size. # # Under this condition, this method returns any element whose index # is within i...j. If i is equal to j (i.e., there is no element # that satisfies the block), this method returns nil. # # ary = [0, 4, 7, 10, 12] # # try to find v such that 4 <= v < 8 # ary.bsearch {|x| 1 - (x / 4).truncate } #=> 4 or 7 # # try to find v such that 8 <= v < 10 # ary.bsearch {|x| 4 - (x / 2).truncate } #=> nil # # You must not mix the two modes at a time; the block must always # return either true/false, or always return a number. It is # undefined which value is actually picked up at each iteration. def bsearch(&block) return to_enum :bsearch unless block if idx = bsearch_index(&block) self[idx] else nil end end ## # call-seq: # ary.bsearch_index {|x| block } -> int or nil # # By using binary search, finds an index of a value from this array which # meets the given condition in O(log n) where n is the size of the array. # # It supports two modes, depending on the nature of the block and they are # exactly the same as in the case of #bsearch method with the only difference # being that this method returns the index of the element instead of the # element itself. For more details consult the documentation for #bsearch. def bsearch_index(&block) return to_enum :bsearch_index unless block low = 0 high = size satisfied = false while low < high mid = ((low+high)/2).truncate res = block.call self[mid] case res when 0 # find-any mode: Found! return mid when Numeric # find-any mode: Continue... in_lower_half = res < 0 when true # find-min mode in_lower_half = true satisfied = true when false, nil # find-min mode in_lower_half = false else raise TypeError, 'invalid block result (must be numeric, true, false or nil)' end if in_lower_half high = mid else low = mid + 1 end end satisfied ? low : nil end ## # call-seq: # ary.keep_if { |item| block } -> ary # ary.keep_if -> Enumerator # # Deletes every element of +self+ for which the given block evaluates to # +false+. # # See also Array#select! # # If no block is given, an Enumerator is returned instead. # # a = [1, 2, 3, 4, 5] # a.keep_if { |val| val > 3 } #=> [4, 5] def keep_if(&block) return to_enum :keep_if unless block result = [] idx = 0 len = size while idx < len elem = self[idx] result << elem if block.call(elem) idx += 1 end self.replace(result) end ## # call-seq: # ary.select! {|item| block } -> ary or nil # ary.select! -> Enumerator # # Invokes the given block passing in successive elements from +self+, # deleting elements for which the block returns a +false+ value. # # If changes were made, it will return +self+, otherwise it returns +nil+. # # See also Array#keep_if # # If no block is given, an Enumerator is returned instead. def select!(&block) return to_enum :select! unless block result = [] idx = 0 len = size while idx < len elem = self[idx] result << elem if block.call(elem) idx += 1 end return nil if len == result.size self.replace(result) end ## # call-seq: # ary.dig(idx, ...) -> object # # Extracts the nested value specified by the sequence of idx # objects by calling +dig+ at each step, returning +nil+ if any # intermediate step is +nil+. # def dig(idx,*args) idx = idx.__to_int n = self[idx] if args.size > 0 n&.dig(*args) else n end end ## # call-seq: # ary.permutation { |p| block } -> ary # ary.permutation -> Enumerator # ary.permutation(n) { |p| block } -> ary # ary.permutation(n) -> Enumerator # # When invoked with a block, yield all permutations of length +n+ of the # elements of the array, then return the array itself. # # If +n+ is not specified, yield all permutations of all elements. # # The implementation makes no guarantees about the order in which the # permutations are yielded. # # If no block is given, an Enumerator is returned instead. # # Examples: # # a = [1, 2, 3] # a.permutation.to_a #=> [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] # a.permutation(1).to_a #=> [[1],[2],[3]] # a.permutation(2).to_a #=> [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]] # a.permutation(3).to_a #=> [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] # a.permutation(0).to_a #=> [[]] # one permutation of length 0 # a.permutation(4).to_a #=> [] # no permutations of length 4 def permutation(n=self.size, &block) n = n.__to_int return to_enum(:permutation, n) unless block size = self.size if n == 0 yield [] elsif 0 < n && n <= size i = 0 while i 0 ary = self[0...i] + self[i+1..-1] ary.permutation(n-1) do |c| yield result + c end else yield result end i += 1 end end self end ## # call-seq: # ary.combination(n) { |c| block } -> ary # ary.combination(n) -> Enumerator # # When invoked with a block, yields all combinations of length +n+ of elements # from the array and then returns the array itself. # # The implementation makes no guarantees about the order in which the # combinations are yielded. # # If no block is given, an Enumerator is returned instead. # # Examples: # # a = [1, 2, 3, 4] # a.combination(1).to_a #=> [[1],[2],[3],[4]] # a.combination(2).to_a #=> [[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]] # a.combination(3).to_a #=> [[1,2,3],[1,2,4],[1,3,4],[2,3,4]] # a.combination(4).to_a #=> [[1,2,3,4]] # a.combination(0).to_a #=> [[]] # one combination of length 0 # a.combination(5).to_a #=> [] # no combinations of length 5 def combination(n, &block) n = n.__to_int return to_enum(:combination, n) unless block size = self.size if n == 0 yield [] elsif n == 1 i = 0 while i new_ary # # Assumes that self is an array of arrays and transposes the rows and columns. # # If the length of the subarrays don't match, an IndexError is raised. # # Examples: # # a = [[1,2], [3,4], [5,6]] # a.transpose #=> [[1, 3, 5], [2, 4, 6]] def transpose return [] if empty? column_count = nil self.each do |row| raise TypeError unless row.is_a?(Array) column_count ||= row.size raise IndexError, 'element size differs' unless column_count == row.size end Array.new(column_count) do |column_index| self.map { |row| row[column_index] } end end ## # call-seq: # ary.to_h -> Hash # ary.to_h{|item| ... } -> Hash # # Returns the result of interpreting array as an array of # [key, value] pairs. If a block is given, it should # return [key, value] pairs to construct a hash. # # [[:foo, :bar], [1, 2]].to_h # # => {:foo => :bar, 1 => 2} # [1, 2].to_h{|x| [x, x*2]} # # => {1 => 2, 2 => 4} # def to_h(&blk) h = {} self.each do |v| v = blk.call(v) if blk raise TypeError, "wrong element type #{v.class}" unless Array === v raise ArgumentError, "wrong array length (expected 2, was #{v.length})" unless v.length == 2 h[v[0]] = v[1] end h end alias append push alias prepend unshift alias filter! select! ## # call-seq: # ary.fetch_values(idx, ...) -> array # ary.fetch_values(idx, ...) { |i| block } -> array # # Returns an array containing the values associated with the given indexes. # but also raises IndexError when one of indexes can't be found. # Also see Array#values_at and Array#fetch. # # a = ["cat", "dog", "cow"] # # a.fetch_values(2, 0) #=> ["cow", "cat"] # a.fetch_values(2, 5) # raises KeyError # a.fetch_values(2, 5) {|i| "BIRD" } #=> ["cow", "BIRD"] # def fetch_values(*idx, &block) idx.map do |i| self.fetch(i, &block) end end ## # call-seq: # ary.product(*arys) -> array # ary.product(*arys) { |item| ... } -> self def product(*arys, &block) size = arys.size i = size while i > 0 i -= 1 unless arys[i].kind_of?(Array) raise TypeError, "no implicit conversion into Array" end end i = size total = self.size total *= arys[i -= 1].size while i > 0 if block result = self list = ->(*, e) { block.call e } class << list; alias []= call; end else result = [nil] * total list = result end i = 0 while i < total group = [nil] * (size + 1) j = size n = i while j > 0 j -= 1 a = arys[j] b = a.size group[j + 1] = a[n % b] n /= b end group[0] = self[n] list[i] = group i += 1 end result end ## # call-seq: # ary.repeated_combination(n) { |combination| ... } -> self # ary.repeated_combination(n) -> enumerator # # A +combination+ method that contains the same elements. def repeated_combination(n, &block) raise TypeError, "no implicit conversion into Integer" unless 0 <=> n return to_enum(:repeated_combination, n) unless block __repeated_combination(n, false, &block) end ## # call-seq: # ary.repeated_permutation(n) { |permutation| ... } -> self # ary.repeated_permutation(n) -> enumerator # # A +permutation+ method that contains the same elements. def repeated_permutation(n, &block) n = n.__to_int raise TypeError, "no implicit conversion into Integer" unless 0 <=> n return to_enum(:repeated_permutation, n) unless block __repeated_combination(n, true, &block) end def __repeated_combination(n, permutation, &block) n = n.__to_int case n when 0 yield [] when 1 i = 0 while i < self.size yield [self[i]] i += 1 end else if n > 0 v = [0] * n while true tmp = [nil] * n i = 0 while i < n tmp[i] = self[v[i]] i += 1 end yield tmp tmp = self.size i = n - 1 while i >= 0 v[i] += 1 break if v[i] < tmp i -= 1 end break unless v[0] < tmp i = 1 while i < n unless v[i] < tmp if permutation v[i] = 0 else v[i] = v[i - 1] end end i += 1 end end end end self end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-array-ext/PaxHeaders/test0000644000000000000000000000013215077107334023274 xustar0030 mtime=1761382108.890300988 30 atime=1761382109.796298369 30 ctime=1761382108.890300988 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-array-ext/test/0000755000175100017510000000000015077107334023741 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-array-ext/test/PaxHeaders/array.rb0000644000000000000000000000013215077107276025021 xustar0030 mtime=1761382078.104420639 30 atime=1761382080.125411393 30 ctime=1761382108.890300988 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-array-ext/test/array.rb0000644000175100017510000004043515077107276025417 0ustar00runnerrunner## # Array(Ext) Test def assert_permutation_combination(exp, receiver, meth, *args) act = [] ret = receiver.__send__(meth, *args) { |v| act << v } assert "assert_#{meth}" do assert_equal(exp, act.sort) assert_same(receiver, ret) end end def assert_permutation(exp, receiver, *args) assert_permutation_combination(exp, receiver, :permutation, *args) end def assert_combination(exp, receiver, *args) assert_permutation_combination(exp, receiver, :combination, *args) end def assert_repeated_permutation(exp, receiver, *args) assert_permutation_combination(exp, receiver, :repeated_permutation, *args) end def assert_repeated_combination(exp, receiver, *args) assert_permutation_combination(exp, receiver, :repeated_combination, *args) end assert("Array#assoc") do s1 = [ "colors", "red", "blue", "green" ] s2 = [ "letters", "a", "b", "c" ] s3 = "foo" a = [ s1, s2, s3 ] assert_equal [ "letters", "a", "b", "c" ], a.assoc("letters") assert_nil a.assoc("foo") end assert("Array#at") do a = [ "a", "b", "c", "d", "e" ] assert_equal "a", a.at(0) assert_equal "e", a.at(-1) end assert("Array#rassoc") do a = [ [ 1, "one"], [2, "two"], [3, "three"], ["ii", "two"] ] assert_equal [2, "two"], a.rassoc("two") assert_nil a.rassoc("four") end assert("Array#uniq!") do a = [1, 2, 3, 1] a.uniq! assert_equal [1, 2, 3], a b = [ "a", "b", "c" ] assert_nil b.uniq! c = [["student","sam"], ["student","george"], ["teacher","matz"]] assert_equal [["student", "sam"], ["teacher", "matz"]], c.uniq! { |s| s.first } d = [["student","sam"], ["teacher","matz"]] assert_nil d.uniq! { |s| s.first } end assert("Array#uniq") do a = [1, 2, 3, 1] assert_equal [1, 2, 3], a.uniq assert_equal [1, 2, 3, 1], a b = [["student","sam"], ["student","george"], ["teacher","matz"]] assert_equal [["student", "sam"], ["teacher", "matz"]], b.uniq { |s| s.first } end assert("Array#-") do a = [1, 2, 3, 1] b = [1] c = 1 assert_raise(TypeError) { a - c } assert_equal [2, 3], (a - b) assert_equal [1, 2, 3, 1], a end assert("Array#|") do a = [1, 2, 3, 1] b = [1, 4] c = 1 assert_raise(TypeError) { a | c } assert_equal [1, 2, 3, 4], (a | b) assert_equal [1, 2, 3, 1], a end assert("Array#union") do a = [1, 2, 3, 1] b = [1, 4] c = [1, 5] assert_equal [1, 2, 3, 4, 5], a.union(b,c) end assert("Array#difference") do a = [1, 2, 3, 1, 6, 7] b = [1, 4, 6] c = [1, 5, 7] assert_equal [2, 3], a.difference(b,c) end assert("Array#&") do a = [1, 2, 3, 1] b = [1, 4] c = 1 assert_raise(TypeError) { a & c } assert_equal [1], (a & b) assert_equal [1, 2, 3, 1], a end assert("Array#intersection") do a = [1, 2, 3, 1, 8, 6, 7, 8] b = [1, 4, 6, 8] c = [1, 5, 7, 8] assert_equal [1, 8], a.intersection(b,c) end assert("Array#intersect?") do a = [ 1, 2, 3 ] b = [ 3, 4, 5 ] c = [ 5, 6, 7 ] assert_true(a.intersect?(b)) assert_false(a.intersect?(c)) end assert("Array#flatten") do assert_equal [1, 2, "3", {4=>5}, :'6'], [1, 2, "3", {4=>5}, :'6'].flatten assert_equal [1, 2, 3, 4, 5, 6], [1, 2, [3, 4, 5], 6].flatten assert_equal [1, 2, 3, 4, 5, 6], [1, 2, [3, [4, 5], 6]].flatten assert_equal [1, [2, [3, [4, [5, [6]]]]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(0) assert_equal [1, 2, [3, [4, [5, [6]]]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(1) assert_equal [1, 2, 3, [4, [5, [6]]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(2) assert_equal [1, 2, 3, 4, [5, [6]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(3) assert_equal [1, 2, 3, 4, 5, [6]], [1, [2, [3, [4, [5, [6]]]]]].flatten(4) assert_equal [1, 2, 3, 4, 5, 6], [1, [2, [3, [4, [5, [6]]]]]].flatten(5) end assert("Array#flatten!") do assert_equal [1, 2, 3, 4, 5, 6], [1, 2, [3, [4, 5], 6]].flatten! end assert("Array#compact") do a = [1, nil, "2", nil, :t, false, nil] assert_equal [1, "2", :t, false], a.compact assert_equal [1, nil, "2", nil, :t, false, nil], a end assert("Array#compact!") do a = [1, nil, "2", nil, :t, false, nil] a.compact! assert_equal [1, "2", :t, false], a end assert("Array#fetch") do a = [ 11, 22, 33, 44 ] assert_equal 22, a.fetch(1) assert_equal 44, a.fetch(-1) assert_equal 'cat', a.fetch(4, 'cat') ret = 0 a.fetch(100) { |i| ret = i } assert_equal 100, ret assert_raise(IndexError) { a.fetch(100) } end assert("Array#fetch_values") do a = [ 11, 22, 33, 44 ] assert_equal([33, 11], a.fetch_values(2, 0)) assert_raise(IndexError) { a.fetch_values(2, 5) } assert_equal([33, 55], a.fetch_values(2, 5) { |i| i*11 }) end assert("Array#fill") do a = [ "a", "b", "c", "d" ] assert_equal ["x", "x", "x", "x"], a.fill("x") assert_equal ["x", "x", "x", "w"], a.fill("w", -1) assert_equal ["x", "x", "z", "z"], a.fill("z", 2, 2) assert_equal ["y", "y", "z", "z"], a.fill("y", 0..1) assert_equal [0, 1, 4, 9], a.fill { |i| i*i } assert_equal [0, 1, 8, 27], a.fill(-2) { |i| i*i*i } assert_equal [0, 2, 3, 27], a.fill(1, 2) { |i| i+1 } assert_equal [1, 2, 3, 27], a.fill(0..1) { |i| i+1 } assert_raise(ArgumentError) { a.fill } assert_equal([0, 1, 2, 3, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, -2, 1)) assert_equal([0, 1, 2, 3, -1, -1, -1], [0, 1, 2, 3, 4, 5].fill(-1, -2, 3)) assert_equal([0, 1, 2, -1, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, 3..4)) assert_equal([0, 1, 2, -1, 4, 5], [0, 1, 2, 3, 4, 5].fill(-1, 3...4)) assert_equal([0, 1, -1, -1, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, 2..-2)) assert_equal([0, 1, -1, -1, 4, 5], [0, 1, 2, 3, 4, 5].fill(-1, 2...-2)) assert_equal([0, 1, 2, 13, 14, 5], [0, 1, 2, 3, 4, 5].fill(3..4){|i| i+10}) assert_equal([0, 1, 2, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(3...4){|i| i+10}) assert_equal([0, 1, 12, 13, 14, 5], [0, 1, 2, 3, 4, 5].fill(2..-2){|i| i+10}) assert_equal([0, 1, 12, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(2...-2){|i| i+10}) assert_equal [1, 2, 3, 4, 'x', 'x'], [1, 2, 3, 4, 5, 6].fill('x', -2..-1) assert_equal [1, 2, 3, 4, 'x', 6], [1, 2, 3, 4, 5, 6].fill('x', -2...-1) assert_equal [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6].fill('x', -2...-2) assert_equal [1, 2, 3, 4, 'x', 6], [1, 2, 3, 4, 5, 6].fill('x', -2..-2) assert_equal [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6].fill('x', -2..0) end assert("Array#reverse_each") do a = [ "a", "b", "c", "d" ] b = [] a.reverse_each do |i| b << i end assert_equal [ "d", "c", "b", "a" ], b end assert("Array#rotate") do a = ["a", "b", "c", "d"] assert_equal ["b", "c", "d", "a"], a.rotate assert_equal ["a", "b", "c", "d"], a assert_equal ["c", "d", "a", "b"], a.rotate(2) assert_equal ["b", "c", "d", "a"], a.rotate(-3) assert_equal ["c", "d", "a", "b"], a.rotate(10) assert_equal [], [].rotate end assert("Array#rotate!") do a = ["a", "b", "c", "d"] assert_equal ["b", "c", "d", "a"], a.rotate! assert_equal ["b", "c", "d", "a"], a assert_equal ["d", "a", "b", "c"], a.rotate!(2) assert_equal ["a", "b", "c", "d"], a.rotate!(-3) assert_equal ["c", "d", "a", "b"], a.rotate(10) assert_equal [], [].rotate! end assert("Array#delete_if") do a = [1, 2, 3, 4, 5] assert_equal [1, 2, 3, 4, 5], a.delete_if { false } assert_equal [1, 2, 3, 4, 5], a a = [1, 2, 3, 4, 5] assert_equal [], a.delete_if { true } assert_equal [], a a = [1, 2, 3, 4, 5] assert_equal [1, 2, 3], a.delete_if { |i| i > 3 } assert_equal [1, 2, 3], a end assert("Array#reject!") do a = [1, 2, 3, 4, 5] assert_nil a.reject! { false } assert_equal [1, 2, 3, 4, 5], a a = [1, 2, 3, 4, 5] assert_equal [], a.reject! { true } assert_equal [], a a = [1, 2, 3, 4, 5] assert_equal [1, 2, 3], a.reject! { |val| val > 3 } assert_equal [1, 2, 3], a end assert("Array#insert") do a = ["a", "b", "c", "d"] assert_equal ["a", "b", 99, "c", "d"], a.insert(2, 99) assert_equal ["a", "b", 99, "c", 1, 2, 3, "d"], a.insert(-2, 1, 2, 3) b = ["a", "b", "c", "d"] assert_equal ["a", "b", "c", "d", nil, nil, 99], b.insert(6, 99) end assert("Array#bsearch") do # Find minimum mode a = [0, 2, 4] assert_equal 0, a.bsearch{ |x| x >= -1 } assert_equal 0, a.bsearch{ |x| x >= 0 } assert_equal 2, a.bsearch{ |x| x >= 1 } assert_equal 2, a.bsearch{ |x| x >= 2 } assert_equal 4, a.bsearch{ |x| x >= 3 } assert_equal 4, a.bsearch{ |x| x >= 4 } assert_nil a.bsearch{ |x| x >= 5 } # Find any mode a = [0, 4, 8] def between(lo, x, hi) if x < lo 1 elsif x > hi -1 else 0 end end assert_nil a.bsearch{ |x| between(-3, x, -1) } assert_equal 0, a.bsearch{ |x| between(-1, x, 1) } assert_nil a.bsearch{ |x| between( 1, x, 3) } assert_equal 4, a.bsearch{ |x| between( 3, x, 5) } assert_nil a.bsearch{ |x| between( 5, x, 7) } assert_equal 8, a.bsearch{ |x| between( 7, x, 9) } assert_nil a.bsearch{ |x| between( 9, x, 11) } assert_equal 0, a.bsearch{ |x| between( 0, x, 3) } assert_equal 4, a.bsearch{ |x| between( 0, x, 4) } assert_equal 4, a.bsearch{ |x| between( 4, x, 8) } assert_equal 8, a.bsearch{ |x| between( 5, x, 8) } # Invalid block result assert_raise TypeError, 'invalid block result (must be numeric, true, false or nil)' do a.bsearch{ 'I like to watch the world burn' } end end # tested through Array#bsearch #assert("Array#bsearch_index") do #end assert("Array#keep_if") do a = [1, 2, 3, 4, 5] assert_equal [1, 2, 3, 4, 5], a.keep_if { true } assert_equal [1, 2, 3, 4, 5], a a = [1, 2, 3, 4, 5] assert_equal [], a.keep_if { false } assert_equal [], a a = [1, 2, 3, 4, 5] assert_equal [4, 5], a.keep_if { |val| val > 3 } assert_equal [4, 5], a end assert("Array#select!") do a = [1, 2, 3, 4, 5] assert_nil a.select! { true } assert_equal [1, 2, 3, 4, 5], a a = [1, 2, 3, 4, 5] assert_equal [], a.select! { false } assert_equal [], a a = [1, 2, 3, 4, 5] assert_equal [4, 5], a.select! { |val| val > 3 } assert_equal [4, 5], a end assert('Array#values_at') do a = %w{red green purple white none} assert_equal %w{red purple none}, a.values_at(0, 2, 4) assert_equal ['green', 'white', nil, nil], a.values_at(1, 3, 5, 7) assert_equal ['none', 'white', 'white', nil], a.values_at(-1, -2, -2, -7) assert_equal ['none', nil, nil, 'red', 'green', 'purple'], a.values_at(4..6, 0...3) assert_raise(TypeError) { a.values_at 'tt' } end assert('Array#to_h') do assert_equal({}, [].to_h) assert_equal({a: 1, b:2}, [[:a, 1], [:b, 2]].to_h) assert_raise(TypeError) { [1].to_h } assert_raise(ArgumentError) { [[1]].to_h } end assert("Array#dig") do h = [[[1]], 0] assert_equal(1, h.dig(0, 0, 0)) assert_nil(h.dig(2, 0)) assert_raise(TypeError) {h.dig(:a)} end assert("Array#slice!") do a = [1, 2, 3] b = a.slice!(0) c = [1, 2, 3, 4, 5] d = c.slice!(0, 2) e = [1, 2, 3, 4, 5] f = e.slice!(1..3) g = [1, 2, 3] h = g.slice!(-1) i = [1, 2, 3] j = i.slice!(0, -1) assert_equal(a, [2, 3]) assert_equal(b, 1) assert_equal(c, [3, 4, 5]) assert_equal(d, [1, 2]) assert_equal(e, [1, 5]) assert_equal(f, [2, 3, 4]) assert_equal(g, [1, 2]) assert_equal(h, 3) assert_equal(i, [1, 2, 3]) assert_equal(j, nil) end assert("Array#permutation") do a = [1, 2, 3] assert_permutation([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]], a) assert_permutation([[1],[2],[3]], a, 1) assert_permutation([[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]], a, 2) assert_permutation([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]], a, 3) assert_permutation([[]], a, 0) assert_permutation([], a, 4) assert_permutation([], a, -1) end assert("Array#combination") do a = [1, 2, 3, 4] assert_combination([[1],[2],[3],[4]], a, 1) assert_combination([[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]], a, 2) assert_combination([[1,2,3],[1,2,4],[1,3,4],[2,3,4]], a, 3) assert_combination([[1,2,3,4]], a, 4) assert_combination([[]], a, 0) assert_combination([], a, 5) assert_combination([], a, -1) end assert('Array#transpose') do assert_equal([].transpose, []) assert_equal([[]].transpose, []) assert_equal([[1]].transpose, [[1]]) assert_equal([[1,2,3]].transpose, [[1], [2], [3]]) assert_equal([[1], [2], [3]].transpose, [[1,2,3]]) assert_equal([[1,2], [3,4], [5,6]].transpose, [[1,3,5], [2,4,6]]) assert_raise(TypeError) { [1].transpose } assert_raise(IndexError) { [[1], [2,3,4]].transpose } end assert "Array#product" do assert_equal [[1], [2], [3]], [1, 2, 3].product assert_equal [], [1, 2, 3].product([]) assert_equal [], [1, 2, 3].product([4, 5, 6], []) expect = [[1, 5, 8], [1, 5, 9], [1, 6, 8], [1, 6, 9], [1, 7, 8], [1, 7, 9], [2, 5, 8], [2, 5, 9], [2, 6, 8], [2, 6, 9], [2, 7, 8], [2, 7, 9], [3, 5, 8], [3, 5, 9], [3, 6, 8], [3, 6, 9], [3, 7, 8], [3, 7, 9], [4, 5, 8], [4, 5, 9], [4, 6, 8], [4, 6, 9], [4, 7, 8], [4, 7, 9]] assert_equal expect, [1, 2, 3, 4].product([5, 6, 7], [8, 9]) expect = [[1, 4, 7], [1, 4, 8], [1, 4, 9], [1, 5, 7], [1, 5, 8], [1, 5, 9], [1, 6, 7], [1, 6, 8], [1, 6, 9], [2, 4, 7], [2, 4, 8], [2, 4, 9], [2, 5, 7], [2, 5, 8], [2, 5, 9], [2, 6, 7], [2, 6, 8], [2, 6, 9], [3, 4, 7], [3, 4, 8], [3, 4, 9], [3, 5, 7], [3, 5, 8], [3, 5, 9], [3, 6, 7], [3, 6, 8], [3, 6, 9]] assert_equal expect, [1, 2, 3].product([4, 5, 6], [7, 8, 9]) base = [1, 2, 3] x = [] assert_equal base, base.product([4, 5, 6], [7, 8, 9]) { |e| x << e } assert_equal expect, x end assert("Array#repeated_combination") do a = [1, 2, 3] assert_raise(ArgumentError) { a.repeated_combination } #assert_kind_of(Enumerator, a.repeated_combination(1)) assert_repeated_combination([[1],[2],[3]], a, 1) assert_repeated_combination([[1,1],[1,2],[1,3],[2,2],[2,3],[3,3]], a, 2) assert_repeated_combination([[1,1,1],[1,1,2],[1,1,3],[1,2,2],[1,2,3],[1,3,3],[2,2,2], [2,2,3],[2,3,3],[3,3,3]], a, 3) assert_repeated_combination([[1,1,1,1],[1,1,1,2],[1,1,1,3],[1,1,2,2],[1,1,2,3],[1,1,3,3], [1,2,2,2],[1,2,2,3],[1,2,3,3],[1,3,3,3],[2,2,2,2],[2,2,2,3], [2,2,3,3],[2,3,3,3],[3,3,3,3]], a, 4) assert_repeated_combination([[1,1,1,1,1],[1,1,1,1,2],[1,1,1,1,3],[1,1,1,2,2],[1,1,1,2,3], [1,1,1,3,3],[1,1,2,2,2],[1,1,2,2,3],[1,1,2,3,3],[1,1,3,3,3], [1,2,2,2,2],[1,2,2,2,3],[1,2,2,3,3],[1,2,3,3,3],[1,3,3,3,3], [2,2,2,2,2],[2,2,2,2,3],[2,2,2,3,3],[2,2,3,3,3],[2,3,3,3,3], [3,3,3,3,3]], a, 5) assert_repeated_combination([[]], a, 0) assert_repeated_combination([], a, -1) end assert("Array#repeated_permutation") do a = [1, 2, 3] assert_raise(ArgumentError) { a.repeated_permutation } #assert_kind_of(Enumerator, a.repeated_permutation(1)) assert_repeated_permutation([[1],[2],[3]], a, 1) assert_repeated_permutation([[1,1],[1,2],[1,3],[2,1],[2,2],[2,3],[3,1],[3,2],[3,3]], a, 2) assert_repeated_permutation([[1,1,1],[1,1,2],[1,1,3],[1,2,1],[1,2,2],[1,2,3],[1,3,1],[1,3,2],[1,3,3], [2,1,1],[2,1,2],[2,1,3],[2,2,1],[2,2,2],[2,2,3],[2,3,1],[2,3,2],[2,3,3], [3,1,1],[3,1,2],[3,1,3],[3,2,1],[3,2,2],[3,2,3],[3,3,1],[3,3,2],[3,3,3]], a, 3) assert_repeated_permutation([[1,1,1,1],[1,1,1,2],[1,1,1,3],[1,1,2,1],[1,1,2,2],[1,1,2,3], [1,1,3,1],[1,1,3,2],[1,1,3,3],[1,2,1,1],[1,2,1,2],[1,2,1,3], [1,2,2,1],[1,2,2,2],[1,2,2,3],[1,2,3,1],[1,2,3,2],[1,2,3,3], [1,3,1,1],[1,3,1,2],[1,3,1,3],[1,3,2,1],[1,3,2,2],[1,3,2,3], [1,3,3,1],[1,3,3,2],[1,3,3,3],[2,1,1,1],[2,1,1,2],[2,1,1,3], [2,1,2,1],[2,1,2,2],[2,1,2,3],[2,1,3,1],[2,1,3,2],[2,1,3,3], [2,2,1,1],[2,2,1,2],[2,2,1,3],[2,2,2,1],[2,2,2,2],[2,2,2,3], [2,2,3,1],[2,2,3,2],[2,2,3,3],[2,3,1,1],[2,3,1,2],[2,3,1,3], [2,3,2,1],[2,3,2,2],[2,3,2,3],[2,3,3,1],[2,3,3,2],[2,3,3,3], [3,1,1,1],[3,1,1,2],[3,1,1,3],[3,1,2,1],[3,1,2,2],[3,1,2,3], [3,1,3,1],[3,1,3,2],[3,1,3,3],[3,2,1,1],[3,2,1,2],[3,2,1,3], [3,2,2,1],[3,2,2,2],[3,2,2,3],[3,2,3,1],[3,2,3,2],[3,2,3,3], [3,3,1,1],[3,3,1,2],[3,3,1,3],[3,3,2,1],[3,3,2,2],[3,3,2,3], [3,3,3,1],[3,3,3,2],[3,3,3,3]], a, 4) assert_repeated_permutation([[]], a, 0) assert_repeated_permutation([], a, -1) end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-array-ext/PaxHeaders/src0000644000000000000000000000013215077107334023104 xustar0030 mtime=1761382108.892300982 30 atime=1761382109.796298369 30 ctime=1761382108.892300982 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-array-ext/src/0000755000175100017510000000000015077107334023551 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-array-ext/src/PaxHeaders/array.c0000644000000000000000000000013215077107276024450 xustar0030 mtime=1761382078.104420639 30 atime=1761382080.125411393 30 ctime=1761382108.892300982 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-array-ext/src/array.c0000644000175100017510000002203015077107276025035 0ustar00runnerrunner#include #include #include #include #include #include #include /* * call-seq: * ary.assoc(obj) -> new_ary or nil * * Searches through an array whose elements are also arrays * comparing _obj_ with the first element of each contained array * using obj.==. * Returns the first contained array that matches (that * is, the first associated array), * or +nil+ if no match is found. * See also Array#rassoc. * * s1 = [ "colors", "red", "blue", "green" ] * s2 = [ "letters", "a", "b", "c" ] * s3 = "foo" * a = [ s1, s2, s3 ] * a.assoc("letters") #=> [ "letters", "a", "b", "c" ] * a.assoc("foo") #=> nil */ static mrb_value ary_assoc(mrb_state *mrb, mrb_value ary) { mrb_int i; mrb_value v; mrb_value k = mrb_get_arg1(mrb); for (i = 0; i < RARRAY_LEN(ary); i++) { v = mrb_check_array_type(mrb, RARRAY_PTR(ary)[i]); if (!mrb_nil_p(v) && RARRAY_LEN(v) > 0 && mrb_equal(mrb, RARRAY_PTR(v)[0], k)) return v; } return mrb_nil_value(); } /* * call-seq: * ary.rassoc(obj) -> new_ary or nil * * Searches through the array whose elements are also arrays. Compares * _obj_ with the second element of each contained array using * ==. Returns the first contained array that matches. See * also Array#assoc. * * a = [ [ 1, "one"], [2, "two"], [3, "three"], ["ii", "two"] ] * a.rassoc("two") #=> [2, "two"] * a.rassoc("four") #=> nil */ static mrb_value ary_rassoc(mrb_state *mrb, mrb_value ary) { mrb_int i; mrb_value v; mrb_value value = mrb_get_arg1(mrb); for (i = 0; i < RARRAY_LEN(ary); i++) { v = RARRAY_PTR(ary)[i]; if (mrb_array_p(v) && RARRAY_LEN(v) > 1 && mrb_equal(mrb, RARRAY_PTR(v)[1], value)) return v; } return mrb_nil_value(); } /* * call-seq: * ary.at(index) -> obj or nil * * Returns the element at _index_. A * negative index counts from the end of +self+. Returns +nil+ * if the index is out of range. See also Array#[]. * * a = [ "a", "b", "c", "d", "e" ] * a.at(0) #=> "a" * a.at(-1) #=> "e" */ static mrb_value ary_at(mrb_state *mrb, mrb_value ary) { mrb_int pos = mrb_as_int(mrb, mrb_get_arg1(mrb)); return mrb_ary_entry(ary, pos); } static mrb_value ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n) { return mrb_ary_entry(ary, n); } static mrb_value ary_values_at(mrb_state *mrb, mrb_value self) { mrb_int argc = mrb_get_argc(mrb); const mrb_value *argv = mrb_get_argv(mrb); return mrb_get_values_at(mrb, self, RARRAY_LEN(self), argc, argv, ary_ref); } mrb_value mrb_ary_delete_at(mrb_state *mrb, mrb_value self); /* * call-seq: * ary.slice!(index) -> obj or nil * ary.slice!(start, length) -> new_ary or nil * ary.slice!(range) -> new_ary or nil * * Deletes the element(s) given by an +index+ (optionally up to +length+ * elements) or by a +range+. * * Returns the deleted object (or objects), or +nil+ if the +index+ is out of * range. * * a = [ "a", "b", "c" ] * a.slice!(1) #=> "b" * a #=> ["a", "c"] * a.slice!(-1) #=> "c" * a #=> ["a"] * a.slice!(100) #=> nil * a #=> ["a"] */ static mrb_value ary_slice_bang(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_int i, j, len, alen; mrb_value *ptr; mrb_value ary; mrb_ary_modify(mrb, a); if (mrb_get_argc(mrb) == 1) { mrb_value index = mrb_get_arg1(mrb); if (mrb_type(index) == MRB_TT_RANGE) { if (mrb_range_beg_len(mrb, index, &i, &len, ARY_LEN(a), TRUE) == MRB_RANGE_OK) { goto delete_pos_len; } return mrb_nil_value(); } return mrb_ary_delete_at(mrb, self); } mrb_get_args(mrb, "ii", &i, &len); delete_pos_len: alen = ARY_LEN(a); if (i < 0) i += alen; if (i < 0 || alen < i) return mrb_nil_value(); if (len < 0) return mrb_nil_value(); if (alen == i) return mrb_ary_new(mrb); if (len > alen - i) len = alen - i; ptr = ARY_PTR(a) + i; ary = mrb_ary_new_from_values(mrb, len, ptr); for (j = i; j < alen - len; j++) { *ptr = *(ptr+len); ptr++; } mrb_ary_resize(mrb, self, alen - len); return ary; } /* * call-seq: * ary.compact -> new_ary * * Returns a copy of +self+ with all +nil+ elements removed. * * [ "a", nil, "b", nil, "c", nil ].compact * #=> [ "a", "b", "c" ] */ static mrb_value ary_compact(mrb_state *mrb, mrb_value self) { mrb_value ary = mrb_ary_new(mrb); mrb_int len = RARRAY_LEN(self); mrb_value *p = RARRAY_PTR(self); for (mrb_int i = 0; i < len; i++) { if (!mrb_nil_p(p[i])) { mrb_ary_push(mrb, ary, p[i]); } } return ary; } /* * call-seq: * ary.compact! -> ary or nil * * Removes +nil+ elements from the array. * Returns +nil+ if no changes were made, otherwise returns * ary. * * [ "a", nil, "b", nil, "c" ].compact! #=> [ "a", "b", "c" ] * [ "a", "b", "c" ].compact! #=> nil */ static mrb_value ary_compact_bang(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_int i, j = 0; mrb_int len = ARY_LEN(a); mrb_ary_modify(mrb, a); mrb_value *p = ARY_PTR(a); for (i = 0; i < len; i++) { if (!mrb_nil_p(p[i])) { if (i != j) p[j] = p[i]; j++; } } if (i == j) return mrb_nil_value(); ARY_SET_LEN(RARRAY(self), j); return self; } /* * call-seq: * ary.rotate(count=1) -> new_ary * * Returns a new array by rotating +self+ so that the element at +count+ is * the first element of the new array. * * If +count+ is negative then it rotates in the opposite direction, starting * from the end of +self+ where +-1+ is the last element. * * a = [ "a", "b", "c", "d" ] * a.rotate #=> ["b", "c", "d", "a"] * a #=> ["a", "b", "c", "d"] * a.rotate(2) #=> ["c", "d", "a", "b"] * a.rotate(-3) #=> ["b", "c", "d", "a"] */ static mrb_value ary_rotate(mrb_state *mrb, mrb_value self) { mrb_int count=1; mrb_get_args(mrb, "|i", &count); mrb_value ary = mrb_ary_new(mrb); mrb_int len = RARRAY_LEN(self); mrb_value *p = RARRAY_PTR(self); mrb_int idx; if (len <= 0) return ary; if (count < 0) { idx = len - (~count % len) - 1; } else { idx = count % len; } for (mrb_int i = 0; i ary * * Rotates +self+ in place so that the element at +count+ comes first, and * returns +self+. * * If +count+ is negative then it rotates in the opposite direction, starting * from the end of the array where +-1+ is the last element. * * a = [ "a", "b", "c", "d" ] * a.rotate! #=> ["b", "c", "d", "a"] * a #=> ["b", "c", "d", "a"] * a.rotate!(2) #=> ["d", "a", "b", "c"] * a.rotate!(-3) #=> ["a", "b", "c", "d"] */ static mrb_value ary_rotate_bang(mrb_state *mrb, mrb_value self) { mrb_int count=1; mrb_get_args(mrb, "|i", &count); struct RArray *a = mrb_ary_ptr(self); mrb_int len = ARY_LEN(a); mrb_int idx; mrb_ary_modify(mrb, a); mrb_value *p = ARY_PTR(a); if (len == 0 || count == 0) return self; if (count == 1) { mrb_value v = p[0]; for (mrb_int i=1; i [3,4,5,1,2] */ /* first, reverse the whole array */ /* [1,2,3,4,5] -> [5,4,3,2,1] */ rev(p, 0, len); /* then, re-reverse part before idx */ /* [5,4,3,2,1] -> [3,4,5,2,1] */ /* ^idx ~~~~~ */ rev(p, 0, len-idx); /* finally, re-reverse part after idx */ /* [3,4,5,2,1] -> [3,4,5,1,2] */ /* ^idx ~~~ */ rev(p, len-idx, len); return self; } void mrb_mruby_array_ext_gem_init(mrb_state* mrb) { struct RClass * a = mrb->array_class; mrb_define_method_id(mrb, a, MRB_SYM(assoc), ary_assoc, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, a, MRB_SYM(at), ary_at, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, a, MRB_SYM(rassoc), ary_rassoc, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, a, MRB_SYM(values_at), ary_values_at, MRB_ARGS_ANY()); mrb_define_method_id(mrb, a, MRB_SYM_B(slice), ary_slice_bang, MRB_ARGS_ARG(1,1)); mrb_define_method_id(mrb, a, MRB_SYM(compact), ary_compact, MRB_ARGS_NONE()); mrb_define_method_id(mrb, a, MRB_SYM_B(compact), ary_compact_bang, MRB_ARGS_NONE()); mrb_define_method_id(mrb, a, MRB_SYM(rotate), ary_rotate, MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, a, MRB_SYM_B(rotate), ary_rotate_bang, MRB_ARGS_OPT(1)); } void mrb_mruby_array_ext_gem_final(mrb_state* mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/default.gembox0000644000000000000000000000013215077107276022156 xustar0030 mtime=1761382078.104420639 30 atime=1761382080.125411393 30 ctime=1761382108.932300867 nghttp2-1.68.0/third-party/mruby/mrbgems/default.gembox0000644000175100017510000000106215077107276022545 0ustar00runnerrunnerMRuby::GemBox.new do |conf| conf.gembox "stdlib" conf.gembox "stdlib-ext" conf.gembox "stdlib-io" conf.gembox "math" conf.gembox "metaprog" # Generate mrbc command conf.gem :core => "mruby-bin-mrbc" # Generate mrdb command conf.gem :core => "mruby-bin-debugger" # Generate mirb command conf.gem :core => "mruby-bin-mirb" # Generate mruby command conf.gem :core => "mruby-bin-mruby" # Generate mruby-strip command conf.gem :core => "mruby-bin-strip" # Generate mruby-config command conf.gem :core => "mruby-bin-config" end nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-error0000644000000000000000000000013215077107334021532 xustar0030 mtime=1761382108.850301104 30 atime=1761382109.796298369 30 ctime=1761382108.850301104 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-error/0000755000175100017510000000000015077107334022177 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-error/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276023731 xustar0030 mtime=1761382078.118420575 30 atime=1761382080.139411329 30 ctime=1761382108.850301104 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-error/mrbgem.rake0000644000175100017510000000024415077107276024321 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-error') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'extensional error handling' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-error/PaxHeaders/test0000644000000000000000000000013215077107334022511 xustar0030 mtime=1761382108.852301098 30 atime=1761382109.796298369 30 ctime=1761382108.852301098 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-error/test/0000755000175100017510000000000015077107334023156 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-error/test/PaxHeaders/exception.rb0000644000000000000000000000013215077107276025116 xustar0030 mtime=1761382078.118420575 30 atime=1761382080.139411329 30 ctime=1761382108.851301101 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-error/test/exception.rb0000644000175100017510000000264715077107276025517 0ustar00runnerrunnerassert 'mrb_protect' do # no failure in protect returns [result, false] assert_equal ['test', false] do ExceptionTest.mrb_protect { 'test' } end # failure in protect returns [exception, true] result = ExceptionTest.mrb_protect { raise 'test' } assert_kind_of RuntimeError, result[0] assert_true result[1] end assert 'mrb_ensure' do a = false assert_equal 'test' do ExceptionTest.mrb_ensure Proc.new { 'test' }, Proc.new { a = true } end assert_true a a = false assert_raise RuntimeError do ExceptionTest.mrb_ensure Proc.new { raise 'test' }, Proc.new { a = true } end assert_true a end assert 'mrb_rescue' do assert_equal 'test' do ExceptionTest.mrb_rescue Proc.new { 'test' }, Proc.new {} end class CustomExp < Exception end assert_raise CustomExp do ExceptionTest.mrb_rescue Proc.new { raise CustomExp.new 'test' }, Proc.new { 'rescue' } end assert_equal 'rescue' do ExceptionTest.mrb_rescue Proc.new { raise 'test' }, Proc.new { 'rescue' } end end assert 'mrb_rescue_exceptions' do assert_equal 'test' do ExceptionTest.mrb_rescue_exceptions Proc.new { 'test' }, Proc.new {} end assert_raise RangeError do ExceptionTest.mrb_rescue_exceptions Proc.new { raise RangeError.new 'test' }, Proc.new { 'rescue' } end assert_equal 'rescue' do ExceptionTest.mrb_rescue_exceptions Proc.new { raise TypeError.new 'test' }, Proc.new { 'rescue' } end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-error/test/PaxHeaders/exception.c0000644000000000000000000000013215077107276024735 xustar0030 mtime=1761382078.118420575 30 atime=1761382080.139411329 30 ctime=1761382108.852301098 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-error/test/exception.c0000644000175100017510000000304515077107276025327 0ustar00runnerrunner#include #include #include static mrb_value protect_cb(mrb_state *mrb, mrb_value b) { return mrb_yield_argv(mrb, b, 0, NULL); } static mrb_value run_protect(mrb_state *mrb, mrb_value self) { mrb_value b; mrb_value ret[2]; mrb_bool state; mrb_get_args(mrb, "&", &b); ret[0] = mrb_protect(mrb, protect_cb, b, &state); ret[1] = mrb_bool_value(state); return mrb_ary_new_from_values(mrb, 2, ret); } static mrb_value run_ensure(mrb_state *mrb, mrb_value self) { mrb_value b, e; mrb_get_args(mrb, "oo", &b, &e); return mrb_ensure(mrb, protect_cb, b, protect_cb, e); } static mrb_value run_rescue(mrb_state *mrb, mrb_value self) { mrb_value b, r; mrb_get_args(mrb, "oo", &b, &r); return mrb_rescue(mrb, protect_cb, b, protect_cb, r); } static mrb_value run_rescue_exceptions(mrb_state *mrb, mrb_value self) { mrb_value b, r; struct RClass *cls[1]; mrb_get_args(mrb, "oo", &b, &r); cls[0] = E_TYPE_ERROR; return mrb_rescue_exceptions(mrb, protect_cb, b, protect_cb, r, 1, cls); } void mrb_mruby_error_gem_test(mrb_state *mrb) { struct RClass *cls; cls = mrb_define_class(mrb, "ExceptionTest", mrb->object_class); mrb_define_module_function(mrb, cls, "mrb_protect", run_protect, MRB_ARGS_NONE() | MRB_ARGS_BLOCK()); mrb_define_module_function(mrb, cls, "mrb_ensure", run_ensure, MRB_ARGS_REQ(2)); mrb_define_module_function(mrb, cls, "mrb_rescue", run_rescue, MRB_ARGS_REQ(2)); mrb_define_module_function(mrb, cls, "mrb_rescue_exceptions", run_rescue_exceptions, MRB_ARGS_REQ(2)); } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-error/PaxHeaders/src0000644000000000000000000000013215077107334022321 xustar0030 mtime=1761382108.854301092 30 atime=1761382109.796298369 30 ctime=1761382108.854301092 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-error/src/0000755000175100017510000000000015077107334022766 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-error/src/PaxHeaders/exception.c0000644000000000000000000000013215077107276024545 xustar0030 mtime=1761382078.118420575 30 atime=1761382080.139411329 30 ctime=1761382108.854301092 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-error/src/exception.c0000644000175100017510000000406615077107276025143 0ustar00runnerrunner#include #include struct protect_data { mrb_func_t body; mrb_value data; }; static mrb_value protect_body(mrb_state *mrb, void *p) { struct protect_data *dp = (struct protect_data*)p; return dp->body(mrb, dp->data); } MRB_API mrb_value mrb_protect(mrb_state *mrb, mrb_func_t body, mrb_value data, mrb_bool *state) { struct protect_data protect_data = { body, data }; return mrb_protect_error(mrb, protect_body, &protect_data, state); } MRB_API mrb_value mrb_ensure(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t ensure, mrb_value e_data) { int ai = mrb_gc_arena_save(mrb); struct protect_data protect_data = { body, b_data }; mrb_bool error; mrb_value result = mrb_protect_error(mrb, protect_body, &protect_data, &error); ensure(mrb, e_data); mrb_gc_arena_restore(mrb, ai); mrb_gc_protect(mrb, result); if (error) { mrb_exc_raise(mrb, result); /* rethrow caught exceptions */ } return result; } MRB_API mrb_value mrb_rescue(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t rescue, mrb_value r_data) { return mrb_rescue_exceptions(mrb, body, b_data, rescue, r_data, 1, &mrb->eStandardError_class); } MRB_API mrb_value mrb_rescue_exceptions(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t rescue, mrb_value r_data, mrb_int len, struct RClass **classes) { int ai = mrb_gc_arena_save(mrb); struct protect_data protect_data = { body, b_data }; mrb_bool error; mrb_value result = mrb_protect_error(mrb, protect_body, &protect_data, &error); if (error) { mrb_bool error_matched = FALSE; for (mrb_int i = 0; i < len; i++) { if (mrb_obj_is_kind_of(mrb, result, classes[i])) { error_matched = TRUE; break; } } if (!error_matched) { mrb_exc_raise(mrb, result); } mrb->exc = NULL; result = rescue(mrb, r_data); mrb_gc_arena_restore(mrb, ai); mrb_gc_protect(mrb, result); } return result; } void mrb_mruby_error_gem_init(mrb_state *mrb) { } void mrb_mruby_error_gem_final(mrb_state *mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/stdlib.gembox0000644000000000000000000000013115077107276022012 xustar0029 mtime=1761382078.13042052 30 atime=1761382080.149411284 30 ctime=1761382108.605301812 nghttp2-1.68.0/third-party/mruby/mrbgems/stdlib.gembox0000644000175100017510000000301015077107276022375 0ustar00runnerrunner# It also works with MRB_NO_STDIO and MRB_NO_FLOAT. MRuby::GemBox.new do |conf| # Use Comparable module extension conf.gem :core => "mruby-compar-ext" # Use Enumerable module extension conf.gem :core => "mruby-enum-ext" # Use String class extension conf.gem :core => "mruby-string-ext" # Use Numeric class extension conf.gem :core => "mruby-numeric-ext" # Use Array class extension conf.gem :core => "mruby-array-ext" # Use Hash class extension conf.gem :core => "mruby-hash-ext" # Use Range class extension conf.gem :core => "mruby-range-ext" # Use Proc class extension conf.gem :core => "mruby-proc-ext" # Use Symbol class extension conf.gem :core => "mruby-symbol-ext" # Use Object class extension conf.gem :core => "mruby-object-ext" # Use ObjectSpace class conf.gem :core => "mruby-objectspace" # Use Set class conf.gem :core => "mruby-set" # Use Fiber class conf.gem :core => "mruby-fiber" # Use Enumerator class (require mruby-fiber) conf.gem :core => "mruby-enumerator" # Use Enumerator::Lazy class (require mruby-enumerator) conf.gem :core => "mruby-enum-lazy" # Use Enumerator::Chain class (require mruby-enumerator) conf.gem :core => "mruby-enum-chain" # Use toplevel object (main) methods extension conf.gem :core => "mruby-toplevel-ext" # Use Kernel module extension conf.gem :core => "mruby-kernel-ext" # Use class/module extension conf.gem :core => "mruby-class-ext" # Use catch/throw methods conf.gem :core => "mruby-catch" end nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-socket0000644000000000000000000000013215077107334021671 xustar0030 mtime=1761382108.802301242 30 atime=1761382109.796298369 30 ctime=1761382108.802301242 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/0000755000175100017510000000000015077107334022336 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024070 xustar0030 mtime=1761382078.126420538 30 atime=1761382080.145411302 30 ctime=1761382108.801301245 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/mrbgem.rake0000644000175100017510000000101615077107276024456 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-socket') do |spec| spec.license = 'MIT' spec.authors = ['Internet Initiative Japan Inc.', 'mruby developers'] spec.summary = 'standard socket class' #spec.cc.defines << "HAVE_SA_LEN=0" # If Windows, use winsock if spec.for_windows? spec.linker.libraries << "wsock32" spec.linker.libraries << "ws2_32" end spec.add_dependency('mruby-io', :core => 'mruby-io') spec.add_dependency('mruby-error', :core => 'mruby-error') # spec.add_dependency('mruby-mtest') end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/PaxHeaders/mrblib0000644000000000000000000000013215077107334023140 xustar0030 mtime=1761382108.814301208 30 atime=1761382109.796298369 30 ctime=1761382108.814301208 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/mrblib/0000755000175100017510000000000015077107334023605 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/mrblib/PaxHeaders/socket.rb0000644000000000000000000000013215077107276025037 xustar0030 mtime=1761382078.127420534 30 atime=1761382080.145411302 30 ctime=1761382108.814301208 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/mrblib/socket.rb0000644000175100017510000002662315077107276025440 0ustar00runnerrunnerclass Addrinfo def initialize(sockaddr, family=Socket::PF_UNSPEC, socktype=0, protocol=0) @hostname = nil if sockaddr.is_a? Array sary = sockaddr if sary[0] == 'AF_INET' || sary[0] == 'AF_INET6' @sockaddr = Socket.sockaddr_in(sary[1], sary[3]) @hostname = sary[2] elsif sary[0] == 'AF_UNIX' @sockaddr = Socket.sockaddr_un(sary[1]) end else @sockaddr = sockaddr.dup end if family == Socket::PF_UNSPEC or family == nil @family = Socket._sockaddr_family(@sockaddr) else @family = family end @socktype = socktype @protocol = protocol end def self.foreach(nodename, service, family=nil, socktype=nil, protocol=nil, flags=0, &block) a = self.getaddrinfo(nodename, service, family, socktype, protocol, flags) a.each { |ai| block.call(ai) } a end def self.ip(host) Addrinfo.new(Socket.sockaddr_in(0, host)) end def self.tcp(host, port) Addrinfo.getaddrinfo(host, port, nil, Socket::SOCK_STREAM, Socket::IPPROTO_TCP)[0] end def self.udp(host, port) Addrinfo.getaddrinfo(host, port, nil, Socket::SOCK_DGRAM, Socket::IPPROTO_UDP)[0] end def self.unix(path, socktype=Socket::SOCK_STREAM) Addrinfo.new(Socket.sockaddr_un(path), Socket::AF_UNIX, socktype) end def afamily @family end #def bind #def canonname #def connect #def connect_from #def connect_to #def family_addrinfo(host, port=nil) #def getnameinfo(flags=0) # Socket.getnameinfo #end def inspect if ipv4? or ipv6? if @protocol == Socket::IPPROTO_TCP or (@socktype == Socket::SOCK_STREAM and @protocol == 0) proto = 'TCP' elsif @protocol == Socket::IPPROTO_UDP or (@socktype == Socket::SOCK_DGRAM and @protocol == 0) proto = 'UDP' else proto = '???' end else proto = "SOCK_STREAM" end "#" end def inspect_sockaddr if ipv4? a, p = ip_unpack "#{a}:#{p}" elsif ipv6? a, p = ip_unpack "[#{a}]:#{p}" elsif unix? unix_path else '???' end end def ip? ipv4? or ipv6? end def ip_address ip_unpack[0] end def ip_port ip_unpack[1] end def ip_unpack h, p = getnameinfo(Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV) [ h, p.to_i ] end def ipv4? @family == Socket::AF_INET end #def ipv4_loopback? #def ipv4_multicast? #def ipv4_private? def ipv6? @family == Socket::AF_INET6 end #def ipv6_loopback? #def ipv6_mc_global? #def ipv6_mc_linklocal? #def ipv6_mc_nodelocal? #def ipv6_mc_orilocal? #def ipv6_mc_sitelocal? #def ipv6_multicast? #def ipv6_to_ipv4 #def ipv6_unspecified #def ipv6_v4compat? #def ipv6_v4mapped? #def listen(backlog=5) def pfamily @family end attr_reader :protocol attr_reader :socktype def _to_array case @family when Socket::AF_INET s = "AF_INET" when Socket::AF_INET6 s = "AF_INET6" when Socket::AF_UNIX s = "AF_UNIX" else s = "(unknown AF)" end addr, port = self.getnameinfo(Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV) [ s, port.to_i, addr, addr ] end def to_sockaddr @sockaddr end alias to_s to_sockaddr def unix? @family == Socket::AF_UNIX end end class BasicSocket < IO @@do_not_reverse_lookup = true def self.do_not_reverse_lookup @@do_not_reverse_lookup end def self.do_not_reverse_lookup=(val) @@do_not_reverse_lookup = val ? true : false end def initialize(*args) super(*args) self._is_socket = true @do_not_reverse_lookup = @@do_not_reverse_lookup end def self.for_fd(fd) super(fd, "r+") end #def connect_address def local_address Addrinfo.new self.getsockname end def recv_nonblock(maxlen, flags=0) begin _setnonblock(true) recv(maxlen, flags) ensure _setnonblock(false) end end def remote_address Addrinfo.new self.getpeername end attr_accessor :do_not_reverse_lookup end class IPSocket < BasicSocket def self.getaddress(host) Addrinfo.ip(host).ip_address end def addr Addrinfo.new(self.getsockname)._to_array end def peeraddr Addrinfo.new(self.getpeername)._to_array end def recvfrom(maxlen, flags=0) msg, sa = _recvfrom(maxlen, flags) [ msg, Addrinfo.new(sa)._to_array ] end end class TCPSocket < IPSocket def initialize(host, service, local_host=nil, local_service=nil) if @init_with_fd super(host, service) else s = nil e = SocketError Addrinfo.foreach(host, service) { |ai| begin s = Socket._socket(ai.afamily, Socket::SOCK_STREAM, 0) if local_host or local_service local_host ||= (ai.afamily == Socket::AF_INET) ? "0.0.0.0" : "::" local_service ||= "0" bi = Addrinfo.getaddrinfo(local_host, local_service, ai.afamily, ai.socktype)[0] Socket._bind(s, bi.to_sockaddr) end Socket._connect(s, ai.to_sockaddr) super(s, "r+") return rescue => e0 e = e0 end } raise e end end def self._new_with_prelude(*args, &pre) o = self._allocate o.instance_eval(&pre) o.initialize(*args) o end #def self.gethostbyname(host) end class TCPServer < TCPSocket def initialize(host=nil, service) ai = Addrinfo.getaddrinfo(host, service, nil, nil, nil, Socket::AI_PASSIVE)[0] @init_with_fd = true super(Socket._socket(ai.afamily, Socket::SOCK_STREAM, 0), "r+") if Socket.const_defined?(:SO_REUSEADDR) self.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true) end Socket._bind(self.fileno, ai.to_sockaddr) listen(5) self end def accept fd = self.sysaccept begin TCPSocket._new_with_prelude(fd, "r+") { @init_with_fd = true } rescue IO._sysclose(fd) rescue nil raise end end def accept_nonblock begin self._setnonblock(true) self.accept ensure self._setnonblock(false) end end def listen(backlog) Socket._listen(self.fileno, backlog) 0 end def sysaccept Socket._accept(self.fileno) end end class UDPSocket < IPSocket def initialize(af=Socket::AF_INET) super(Socket._socket(af, Socket::SOCK_DGRAM, 0), "r+") @af = af self end def bind(host, port) Socket._bind(self.fileno, _sockaddr_in(port, host)) 0 end def connect(host, port) Socket._connect(self.fileno, _sockaddr_in(port, host)) 0 end def recvfrom_nonblock(*args) s = self begin self._setnonblock(true) self.recvfrom(*args) ensure # XXX: self is a SystemcallException here! (should be bug) s._setnonblock(false) end end def send(mesg, flags, host=nil, port=nil) if port super(mesg, flags, _sockaddr_in(port, host)) elsif host super(mesg, flags, host) else super(mesg, flags) end end def _sockaddr_in(port, host) ai = Addrinfo.getaddrinfo(host, port, @af, Socket::SOCK_DGRAM)[0] ai.to_sockaddr end end class Socket < BasicSocket def initialize(domain, type, protocol=0) super(Socket._socket(domain, type, protocol), "r+") end #def self.accept_loop def self.getaddrinfo(nodename, servname, family=nil, socktype=nil, protocol=nil, flags=0) Addrinfo.getaddrinfo(nodename, servname, family, socktype, protocol, flags).map { |ai| ary = ai._to_array ary[2] = nodename ary[4] = ai.afamily ary[5] = ai.socktype ary[6] = ai.protocol ary } end #def self.getnameinfo #def self.ip_address_list def self.open(*args) new(args) end def self.sockaddr_in(port, host) ai = Addrinfo.getaddrinfo(host, port, nil, Socket::SOCK_DGRAM)[0] ai.to_sockaddr end #def self.tcp #def self.tcp_server_loop #def self.tcp_server_sockets #def self.udp_server_loop #def self.udp_server_loop_on #def self.udp_server_recv #def self.udp_server_sockets #def self.unix(path) #def self.unix_server_loop #def self.unix_server_socket def self.unpack_sockaddr_in(sa) Addrinfo.new(sa).ip_unpack.reverse end def self.unpack_sockaddr_un(sa) Addrinfo.new(sa).unix_path end class << self alias pack_sockaddr_in sockaddr_in alias pack_sockaddr_un sockaddr_un alias pair socketpair end def accept fd, addr = self.sysaccept [ Socket.for_fd(fd), addr ] end def accept_nonblock begin self._setnonblock(true) self.accept ensure self._setnonblock(false) end end def bind(sockaddr) sockaddr = sockaddr.to_sockaddr if sockaddr.is_a? Addrinfo Socket._bind(self.fileno, sockaddr) 0 end def connect(sockaddr) sockaddr = sockaddr.to_sockaddr if sockaddr.is_a? Addrinfo Socket._connect(self.fileno, sockaddr) 0 end def connect_nonblock(sockaddr) begin self._setnonblock(true) self.connect(sockaddr) ensure self._setnonblock(false) end end #def ipv6only! def listen(backlog) Socket._listen(self.fileno, backlog) 0 end def recvfrom(maxlen, flags=0) msg, sa = _recvfrom(maxlen, flags) socktype = self.getsockopt(Socket::SOL_SOCKET, Socket::SO_TYPE).int [ msg, Addrinfo.new(sa, Socket::PF_UNSPEC, socktype) ] end def recvfrom_nonblock(*args) begin self._setnonblock(true) self._recvfrom(*args) ensure self._setnonblock(false) end end def sysaccept Socket._accept2(self.fileno) end end class UNIXSocket < BasicSocket def initialize(path, &block) if self.is_a? UNIXServer super(path, "r") else super(Socket._socket(Socket::AF_UNIX, Socket::SOCK_STREAM, 0), "r+") Socket._connect(self.fileno, Socket.sockaddr_un(path)) if block_given? begin yield self ensure begin self.close unless self.closed? rescue StandardError end end end end end class << self def socketpair(type=Socket::SOCK_STREAM, protocol=0) a = Socket.socketpair(Socket::AF_UNIX, type, protocol) [ UNIXSocket.for_fd(a[0]), UNIXSocket.for_fd(a[1]) ] end alias pair socketpair end def addr [ "AF_UNIX", path ] end def path Addrinfo.new(self.getsockname).unix_path end def peeraddr [ "AF_UNIX", Addrinfo.new(self.getpeername).unix_path ] end #def recv_io def recvfrom(maxlen, flags=0) msg, sa = _recvfrom(maxlen, flags) path = (sa.size > 0) ? Addrinfo.new(sa).unix_path : "" [ msg, [ "AF_UNIX", path ] ] end #def send_io end class UNIXServer < UNIXSocket def initialize(path) fd = Socket._socket(Socket::AF_UNIX, Socket::SOCK_STREAM, 0) begin super(fd) Socket._bind(fd, Socket.pack_sockaddr_un(path)) self.listen(5) rescue => e IO._sysclose(fd) rescue nil raise e end if block_given? begin yield self ensure self.close rescue nil unless self.closed? end end end def accept fd = self.sysaccept begin sock = UNIXSocket.for_fd(fd) rescue IO._sysclose(fd) rescue nil end sock end def accept_nonblock begin self._setnonblock(true) self.accept ensure self._setnonblock(false) end end def listen(backlog) Socket._listen(self.fileno, backlog) 0 end def sysaccept Socket._accept(self.fileno) end end class SocketError < StandardError; end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/PaxHeaders/README.md0000644000000000000000000000013215077107276023232 xustar0030 mtime=1761382078.126420538 30 atime=1761382080.145411302 30 ctime=1761382108.802301242 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/README.md0000644000175100017510000000341115077107276023621 0ustar00runnerrunner# mruby-socket "mruby-socket" mrbgem provides BSD socket interface for mruby. API is compatible with CRuby's "socket" library. ## Example ```sh % vi kame.rb s = TCPSocket.open("www.kame.net", 80) s.write("GET / HTTP/1.0\r\n\r\n") puts s.read s.close % mruby kame.rb HTTP/1.1 200 OK Date: Tue, 21 May 2013 04:31:30 GMT ... ``` ## Requirement - [mruby-io](../mruby-io) mrbgem - [iij/mruby-mtest](https://github.com/iij/mruby-mtest) mrbgem to run tests - system must have RFC3493 basic socket interface - and some POSIX API... ## TODO - add missing methods - write more tests - fix possible descriptor leakage (see XXX comments) - `UNIXSocket#recv_io` `UNIXSocket#send_io` ## License Copyright (c) 2013 Internet Initiative Japan Inc. Copyright (c) 2017 mruby developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/PaxHeaders/test0000644000000000000000000000013215077107334022650 xustar0030 mtime=1761382108.812301214 30 atime=1761382109.796298369 30 ctime=1761382108.812301214 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/test/0000755000175100017510000000000015077107334023315 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/test/PaxHeaders/socket.rb0000644000000000000000000000013215077107276024547 xustar0030 mtime=1761382078.127420534 30 atime=1761382080.146411297 30 ctime=1761382108.804301237 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/test/socket.rb0000644000175100017510000000210315077107276025133 0ustar00runnerrunnerunless SocketTest.win? assert('Socket.gethostname') do assert_true(Socket.gethostname.is_a? String) end assert('Socket::getaddrinfo') do ret = Socket.getaddrinfo("localhost", 53, Socket::AF_INET, Socket::SOCK_DGRAM) assert_true ret.size >= 1 a = ret[0] assert_equal "AF_INET", a[0] assert_equal 53, a[1] # documents says it's a hostname but CRuby returns an address #assert_equal "127.0.0.1", a[2] assert_equal "127.0.0.1", a[3] assert_equal Socket::AF_INET, a[4] assert_equal Socket::SOCK_DGRAM, a[5] assert_equal Socket::IPPROTO_UDP, a[6] unless SocketTest.cygwin? end assert('Socket#recvfrom') do begin sstr = "abcdefg" s = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0) c = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0) s.bind(Socket.sockaddr_in(0, "127.0.0.1")) c.send sstr, 0, s.getsockname rstr, ai = s.recvfrom sstr.size assert_equal sstr, rstr assert_equal "127.0.0.1", ai.ip_address ensure s.close rescue nil c.close rescue nil end end end # win? nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/test/PaxHeaders/udpsocket.rb0000644000000000000000000000013215077107276025260 xustar0030 mtime=1761382078.127420534 30 atime=1761382080.146411297 30 ctime=1761382108.805301234 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/test/udpsocket.rb0000644000175100017510000000053115077107276025647 0ustar00runnerrunnerassert('UDPSocket.new') do s = UDPSocket.new assert_true(s.is_a? UDPSocket) s.close s = UDPSocket.new(Socket::AF_INET6) assert_true(s.is_a? UDPSocket) s.close true end #assert('UDPSocket#connect') do #assert('UDPSocket#send') do #assert('UDPSocket#recv') do #assert('UDPSocket#bind') do #assert('UDPSocket#recvfrom_nonblock') do nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/test/PaxHeaders/unix.rb0000644000000000000000000000013215077107276024242 xustar0030 mtime=1761382078.127420534 30 atime=1761382080.146411297 30 ctime=1761382108.811301216 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/test/unix.rb0000644000175100017510000000534315077107276024637 0ustar00runnerrunnerunless SocketTest.win? || SocketTest.cygwin? def unixserver_test_block path = SocketTest.tmppath File.unlink path rescue nil begin result = yield path ensure File.unlink path rescue nil end result end def with_unix_server unixserver_test_block do |path| UNIXServer.open(path) { |server| yield path, server } end end def with_unix_client with_unix_server do |path, server| UNIXSocket.open(path) do |csock| ssock = server.accept begin yield path, server, ssock, csock ensure ssock.close unless ssock.closed? rescue nil end end end end assert('UNIXServer.new') do unixserver_test_block do |path| server = UNIXServer.new(path) assert_true server.is_a? UNIXServer server.close File.unlink path s2 = nil result = UNIXServer.open(path) { |s1| assert_true s1.is_a? UNIXServer s2 = s1 1234 } assert_equal 1234, result assert_true s2.is_a? UNIXServer assert_true s2.closed? end end # assert('UNIXServer#accept_nonblock') - would block if fails assert('UNIXServer#addr') do with_unix_server do |path, server| assert_equal [ "AF_UNIX", path], server.addr end end assert('UNIXServer#path') do with_unix_server do |path, server| assert_equal path, server.path end end # assert('UNIXServer#peeraddr') - will raise a runtime exception assert('UNIXServer#listen') do with_unix_server do |path, server| assert_equal 0, server.listen(1) end end assert('UNIXServer#sysaccept') do with_unix_server do |path, server| UNIXSocket.open(path) do |csock| begin fd = server.sysaccept assert_true fd.kind_of? Integer ensure IO._sysclose(fd) rescue nil end end end end assert('UNIXSocket.new') do with_unix_server do |path, server| c = UNIXSocket.new(path) assert_true c.is_a? UNIXSocket c.close true end end assert('UNIXSocket#addr') do with_unix_client do |path, server, ssock, csock| assert_equal [ "AF_UNIX", path ], ssock.addr assert_equal [ "AF_UNIX", "" ], csock.addr end end assert('UNIXSocket#path') do with_unix_client do |path, server, ssock, csock| assert_equal path, ssock.path assert_equal "", csock.path end end assert('UNIXSocket#peeraddr') do with_unix_client do |path, server, ssock, csock| assert_equal [ "AF_UNIX", "" ], ssock.peeraddr assert_equal [ "AF_UNIX", path ], csock.peeraddr end end assert('UNIXSocket#recvfrom') do with_unix_client do |path, server, ssock, csock| str = "0123456789" ssock.send str, 0 a = csock.recvfrom(8) assert_equal str[0, 8], a[0] assert_equal "AF_UNIX", a[1][0] # a[1][1] would be "" or something end end end # SocketTest.win? nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/test/PaxHeaders/basicsocket.rb0000644000000000000000000000013215077107276025551 xustar0030 mtime=1761382078.127420534 30 atime=1761382080.146411297 30 ctime=1761382108.809301222 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/test/basicsocket.rb0000644000175100017510000000071715077107276026146 0ustar00runnerrunnerassert('BasicSocket') do assert_equal(Class, BasicSocket.class) end assert('super class of BasicSocket') do assert_equal(IO, BasicSocket.superclass) end assert('BasicSocket.do_not_reverse_lookup') do assert_equal(BasicSocket.do_not_reverse_lookup, true) end assert('BasicSocket.do_not_reverse_lookup=') do BasicSocket.do_not_reverse_lookup = false assert_equal(BasicSocket.do_not_reverse_lookup, false) BasicSocket.do_not_reverse_lookup = true end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/test/PaxHeaders/sockettest.c0000644000000000000000000000013215077107276025266 xustar0030 mtime=1761382078.127420534 30 atime=1761382080.146411297 30 ctime=1761382108.807301228 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/test/sockettest.c0000644000175100017510000000321715077107276025661 0ustar00runnerrunner#include #include #include #if defined(_WIN32) #include #if defined(_MSC_VER) || \ (defined(MRB_MINGW32_VERSION) && MRB_MINGW32_VERSION < 3021) || \ (defined(MRB_MINGW64_VERSION) && MRB_MINGW64_VERSION < 4000) #include #include #define open _open #define close _close #define unlink _unlink static int mkstemp(char *p) { int fd; char* fname = _mktemp(p); if (fname == NULL) return -1; fd = open(fname, O_RDWR | O_CREAT | O_EXCL, _S_IREAD | _S_IWRITE); if (fd >= 0) return fd; return -1; } #endif #else #include #endif mrb_value mrb_sockettest_tmppath(mrb_state *mrb, mrb_value klass) { char name[] = "mruby-socket.XXXXXXXX"; int fd = mkstemp(name); if (fd == -1) { mrb_sys_fail(mrb, 0); } if (close(fd) == -1) { mrb_sys_fail(mrb, 0); } if (unlink(name) == -1) { mrb_sys_fail(mrb, 0); } return mrb_str_new_cstr(mrb, name); } mrb_value mrb_sockettest_win_p(mrb_state *mrb, mrb_value klass) { #ifdef _WIN32 return mrb_true_value(); #else return mrb_false_value(); #endif } mrb_value mrb_sockettest_cygwin_p(mrb_state *mrb, mrb_value klass) { #if defined(__CYGWIN__) || defined(__CYGWIN32__) return mrb_true_value(); #else return mrb_false_value(); #endif } void mrb_mruby_socket_gem_test(mrb_state* mrb) { struct RClass *c = mrb_define_module(mrb, "SocketTest"); mrb_define_class_method(mrb, c, "tmppath", mrb_sockettest_tmppath, MRB_ARGS_NONE()); mrb_define_class_method(mrb, c, "win?", mrb_sockettest_win_p, MRB_ARGS_NONE()); mrb_define_class_method(mrb, c, "cygwin?", mrb_sockettest_cygwin_p, MRB_ARGS_NONE()); } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/test/PaxHeaders/addrinfo.rb0000644000000000000000000000013215077107276025045 xustar0030 mtime=1761382078.127420534 30 atime=1761382080.146411297 30 ctime=1761382108.810301219 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/test/addrinfo.rb0000644000175100017510000000533315077107276025441 0ustar00runnerrunnerassert('Addrinfo') do assert_equal(Class, Addrinfo.class) end assert('super class of Addrinfo') do assert_equal(Object, Addrinfo.superclass) end assert('Addrinfo.getaddrinfo') do ary = Addrinfo.getaddrinfo("localhost", 53, Socket::AF_INET, Socket::SOCK_STREAM) assert_true(ary.size >= 1) ai = ary[0] assert_equal(ai.afamily, Socket::AF_INET) assert_equal(ai.pfamily, Socket::PF_INET) assert_equal(ai.socktype, Socket::SOCK_STREAM) assert_equal(ai.ip_address, '127.0.0.1') assert_equal(ai.ip_port, 53) end assert('Addrinfo.foreach') do # assume Addrinfo.getaddrinfo works well a = Addrinfo.getaddrinfo("localhost", 80) b = [] Addrinfo.foreach("localhost", 80) { |ai| b << ai } assert_equal(a.size, b.size) end assert('Addrinfo.ip') do ai = Addrinfo.ip('127.0.0.1') assert_equal('127.0.0.1', ai.ip_address) assert_equal(Socket::AF_INET, ai.afamily) assert_equal(0, ai.ip_port) assert_equal(0, ai.socktype) assert_equal(0, ai.protocol) end assert('Addrinfo.tcp') do ai = Addrinfo.tcp('127.0.0.1', 25) assert_equal('127.0.0.1', ai.ip_address) assert_equal(Socket::AF_INET, ai.afamily) assert_equal(25, ai.ip_port) assert_equal(Socket::SOCK_STREAM, ai.socktype) assert_equal(Socket::IPPROTO_TCP, ai.protocol) end assert('Addrinfo.udp') do ai = Addrinfo.udp('127.0.0.1', 53) assert_equal('127.0.0.1', ai.ip_address) assert_equal(Socket::AF_INET, ai.afamily) assert_equal(53, ai.ip_port) assert_equal(Socket::SOCK_DGRAM, ai.socktype) assert_equal(Socket::IPPROTO_UDP, ai.protocol) end assert('Addrinfo.unix') do skip "unix is not supported on Windows" if SocketTest.win? a1 = Addrinfo.unix('/tmp/sock') assert_true(a1.unix?) assert_equal('/tmp/sock', a1.unix_path) assert_equal(Socket::SOCK_STREAM, a1.socktype) a2 = Addrinfo.unix('/tmp/sock', Socket::SOCK_DGRAM) assert_equal(Socket::SOCK_DGRAM, a2.socktype) end assert('Addrinfo#afamily') do skip "afamily is not supported on Windows" if SocketTest.win? ai4 = Addrinfo.new(Socket.sockaddr_in(1, '127.0.0.1')) ai6 = Addrinfo.new(Socket.sockaddr_in(1, '::1')) aiu = Addrinfo.new(Socket.sockaddr_un('/tmp/sock')) assert_equal(Socket::AF_INET, ai4.afamily) assert_equal(Socket::AF_INET6, ai6.afamily) assert_equal(Socket::AF_UNIX, aiu.afamily) end # assert('Addrinfo#canonname') do # #getnameinfo # assert('Addrinfo#inspect') do # assert('Addrinfo#inspect_socket') do # assert('Addrinfo#ip?') do # assert('Addrinfo#ip_address') do # assert('Addrinfo#ip_port') do # assert('Addrinfo#ip_unpack') do # assert('Addrinfo#ipv4?') do # assert('Addrinfo#ipv6?') do # assert('Addrinfo#pfamily') do # assert('Addrinfo#protocol') do # assert('Addrinfo#socktype') do # assert('Addrinfo#to_sockaddr') do # assert('Addrinfo#unix?') do # #unix_path nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/test/PaxHeaders/tcpsocket.rb0000644000000000000000000000013215077107276025256 xustar0030 mtime=1761382078.127420534 30 atime=1761382080.146411297 30 ctime=1761382108.806301231 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/test/tcpsocket.rb0000644000175100017510000000017615077107276025652 0ustar00runnerrunner#assert('TCPSocket.gethostbyname') do #assert('TCPSocket.new') do #assert('TCPSocket#close') do #assert('TCPSocket#write') do nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/test/PaxHeaders/ipsocket.rb0000644000000000000000000000013215077107276025100 xustar0030 mtime=1761382078.127420534 30 atime=1761382080.146411297 30 ctime=1761382108.812301214 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/test/ipsocket.rb0000644000175100017510000000163015077107276025470 0ustar00runnerrunnerunless SocketTest.win? # Note: most of tests below will fail if UDPSocket is broken. assert('IPSocket.getaddress') do l = IPSocket.getaddress("localhost") assert_true (l == "127.0.0.1" or l == "::1") end assert('IPSocket.addr') do localhost = "127.0.0.1" s = UDPSocket.new s.bind(localhost, 0) port = Addrinfo.new(s.getsockname).ip_port a = s.addr assert_equal "AF_INET", a[0] assert_equal port, a[1] assert_equal localhost, a[2] assert_equal localhost, a[3] s.close true end assert('IPSocket.peeraddr') do localhost = "127.0.0.1" server = UDPSocket.new server.bind(localhost, 0) port = server.local_address.ip_port client = UDPSocket.new client.connect(localhost, port) a = client.peeraddr assert_equal "AF_INET", a[0] assert_equal port, a[1] assert_equal localhost, a[2] assert_equal localhost, a[3] client.close server.close true end end # win? nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/PaxHeaders/src0000644000000000000000000000013215077107334022460 xustar0030 mtime=1761382108.819301193 30 atime=1761382109.796298369 30 ctime=1761382108.819301193 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/src/0000755000175100017510000000000015077107334023125 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/src/PaxHeaders/gen.rb0000644000000000000000000000013215077107276023640 xustar0030 mtime=1761382078.127420534 30 atime=1761382080.145411302 30 ctime=1761382108.816301202 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-socket/src/gen.rb0000755000175100017510000000051015077107276024227 0ustar00runnerrunner#!/usr/bin/env ruby Dir.chdir(File.dirname($0)) f = File.open("const.cstub", "w") IO.readlines("const.def").each { |name| name.sub(/^#.*/, "") name.strip! next if name.empty? f.write < #include #include #include #define SHUT_RDWR SD_BOTH typedef int fsize_t; #else #include #include #include #include #include #include #include #include #include #include typedef size_t fsize_t; #endif #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(HAVE_SA_LEN) #if (defined(BSD) && (BSD >= 199006)) #define HAVE_SA_LEN 1 #else #define HAVE_SA_LEN 0 #endif #endif #define E_SOCKET_ERROR mrb_class_get_id(mrb, MRB_SYM(SocketError)) #ifdef _WIN32 static const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) { if (af == AF_INET) { struct sockaddr_in in = {0}; in.sin_family = AF_INET; memcpy(&in.sin_addr, src, sizeof(struct in_addr)); getnameinfo((struct sockaddr*)&in, sizeof(struct sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST); return dst; } else if (af == AF_INET6) { struct sockaddr_in6 in = {0}; in.sin6_family = AF_INET6; memcpy(&in.sin6_addr, src, sizeof(struct in_addr6)); getnameinfo((struct sockaddr*)&in, sizeof(struct sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST); return dst; } return NULL; } static int inet_pton(int af, const char *src, void *dst) { struct addrinfo hints = {0}; hints.ai_family = af; struct addrinfo *res; if (getaddrinfo(src, NULL, &hints, &res) != 0) { printf("Couldn't resolve host %s\n", src); return -1; } for (struct addrinfo *r = res; r; r = r->ai_next) { memcpy(dst, r->ai_addr, r->ai_addrlen); } freeaddrinfo(res); return 0; } #endif struct gen_addrinfo_args { struct RClass *klass; struct addrinfo *addrinfo; }; static mrb_value gen_addrinfo(mrb_state *mrb, mrb_value args) { mrb_value ary = mrb_ary_new(mrb); int arena_idx = mrb_gc_arena_save(mrb); /* ary must be on arena! */ struct gen_addrinfo_args *a = (struct gen_addrinfo_args*)mrb_cptr(args); for (struct addrinfo *res = a->addrinfo; res != NULL; res = res->ai_next) { mrb_value sa = mrb_str_new(mrb, (char*)res->ai_addr, res->ai_addrlen); mrb_value args[4] = {sa, mrb_fixnum_value(res->ai_family), mrb_fixnum_value(res->ai_socktype), mrb_fixnum_value(res->ai_protocol)}; mrb_value ai = mrb_obj_new(mrb, a->klass, 4, args); mrb_ary_push(mrb, ary, ai); mrb_gc_arena_restore(mrb, arena_idx); } return ary; } static mrb_value free_addrinfo(mrb_state *mrb, mrb_value addrinfo) { freeaddrinfo((struct addrinfo*)mrb_cptr(addrinfo)); return mrb_nil_value(); } static mrb_value mrb_addrinfo_getaddrinfo(mrb_state *mrb, mrb_value klass) { struct addrinfo hints = {0}, *addr; mrb_value family, protocol, service, socktype; mrb_int flags = 0; const char *hostname; family = socktype = protocol = mrb_nil_value(); mrb_get_args(mrb, "z!o|oooi", &hostname, &service, &family, &socktype, &protocol, &flags); const char *servname = NULL; if (mrb_string_p(service)) { servname = RSTRING_CSTR(mrb, service); } else if (mrb_integer_p(service)) { servname = RSTRING_PTR(mrb_integer_to_str(mrb, service, 10)); } else if (mrb_nil_p(service)) { servname = NULL; } else { mrb_raise(mrb, E_TYPE_ERROR, "service must be String, Integer, or nil"); } hints.ai_flags = (int)flags; if (mrb_integer_p(family)) { hints.ai_family = (int)mrb_integer(family); } if (mrb_integer_p(socktype)) { hints.ai_socktype = (int)mrb_integer(socktype); } if (mrb_integer_p(protocol)) { hints.ai_protocol = (int)mrb_integer(protocol); } int error = getaddrinfo(hostname, servname, &hints, &addr); if (error) { mrb_raisef(mrb, E_SOCKET_ERROR, "getaddrinfo: %s", gai_strerror(error)); } struct gen_addrinfo_args args = {mrb_class_ptr(klass), addr}; return mrb_ensure(mrb, gen_addrinfo, mrb_cptr_value(mrb, &args), free_addrinfo, mrb_cptr_value(mrb, addr)); } static mrb_value mrb_addrinfo_getnameinfo(mrb_state *mrb, mrb_value self) { mrb_int flags = 0; mrb_get_args(mrb, "|i", &flags); mrb_value host = mrb_str_new_capa(mrb, NI_MAXHOST); mrb_value serv = mrb_str_new_capa(mrb, NI_MAXSERV); mrb_value sastr = mrb_iv_get(mrb, self, MRB_IVSYM(sockaddr)); if (!mrb_string_p(sastr)) { mrb_raise(mrb, E_SOCKET_ERROR, "invalid sockaddr"); } int error = getnameinfo((struct sockaddr*)RSTRING_PTR(sastr), (socklen_t)RSTRING_LEN(sastr), RSTRING_PTR(host), NI_MAXHOST, RSTRING_PTR(serv), NI_MAXSERV, (int)flags); if (error) { mrb_raisef(mrb, E_SOCKET_ERROR, "getnameinfo: %s", gai_strerror(error)); } mrb_value ary = mrb_ary_new_capa(mrb, 2); mrb_str_resize(mrb, host, strlen(RSTRING_PTR(host))); mrb_ary_push(mrb, ary, host); mrb_str_resize(mrb, serv, strlen(RSTRING_PTR(serv))); mrb_ary_push(mrb, ary, serv); return ary; } #ifndef _WIN32 static mrb_value mrb_addrinfo_unix_path(mrb_state *mrb, mrb_value self) { mrb_value sastr = mrb_iv_get(mrb, self, MRB_IVSYM(sockaddr)); if (!mrb_string_p(sastr) || ((struct sockaddr*)RSTRING_PTR(sastr))->sa_family != AF_UNIX) mrb_raise(mrb, E_SOCKET_ERROR, "need AF_UNIX address"); if (RSTRING_LEN(sastr) < (mrb_int)offsetof(struct sockaddr_un, sun_path) + 1) { return mrb_str_new(mrb, "", 0); } else { return mrb_str_new_cstr(mrb, ((struct sockaddr_un*)RSTRING_PTR(sastr))->sun_path); } } #endif static mrb_value sa2addrlist(mrb_state *mrb, const struct sockaddr *sa, socklen_t salen) { const char *afstr; unsigned short port; switch (sa->sa_family) { case AF_INET: afstr = "AF_INET"; port = ((struct sockaddr_in*)sa)->sin_port; break; case AF_INET6: afstr = "AF_INET6"; port = ((struct sockaddr_in6*)sa)->sin6_port; break; default: mrb_raise(mrb, E_ARGUMENT_ERROR, "bad af"); return mrb_nil_value(); } port = ntohs(port); mrb_value host = mrb_str_new_capa(mrb, NI_MAXHOST); if (getnameinfo(sa, salen, RSTRING_PTR(host), NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == -1) mrb_sys_fail(mrb, "getnameinfo"); mrb_str_resize(mrb, host, strlen(RSTRING_PTR(host))); mrb_value ary = mrb_ary_new_capa(mrb, 4); mrb_ary_push(mrb, ary, mrb_str_new_cstr(mrb, afstr)); mrb_ary_push(mrb, ary, mrb_fixnum_value(port)); mrb_ary_push(mrb, ary, host); mrb_ary_push(mrb, ary, host); return ary; } int mrb_io_fileno(mrb_state *mrb, mrb_value io); static int socket_fd(mrb_state *mrb, mrb_value sock) { return mrb_io_fileno(mrb, sock); } static int socket_family(int s) { struct sockaddr_storage ss; socklen_t salen = sizeof(ss); if (getsockname(s, (struct sockaddr*)&ss, &salen) == -1) return AF_UNSPEC; return ss.ss_family; } static mrb_value mrb_basicsocket_getpeereid(mrb_state *mrb, mrb_value self) { #ifdef HAVE_GETPEEREID gid_t egid; uid_t euid; int s = socket_fd(mrb, self); if (getpeereid(s, &euid, &egid) != 0) mrb_sys_fail(mrb, "getpeereid"); mrb_value ary = mrb_ary_new_capa(mrb, 2); mrb_ary_push(mrb, ary, mrb_fixnum_value((mrb_int)euid)); mrb_ary_push(mrb, ary, mrb_fixnum_value((mrb_int)egid)); return ary; #else mrb_raise(mrb, E_RUNTIME_ERROR, "getpeereid is not available on this system"); return mrb_nil_value(); #endif } static mrb_value mrb_basicsocket_getpeername(mrb_state *mrb, mrb_value self) { struct sockaddr_storage ss; socklen_t salen = sizeof(ss); if (getpeername(socket_fd(mrb, self), (struct sockaddr*)&ss, &salen) != 0) mrb_sys_fail(mrb, "getpeername"); return mrb_str_new(mrb, (char*)&ss, salen); } static mrb_value mrb_basicsocket_getsockname(mrb_state *mrb, mrb_value self) { struct sockaddr_storage ss; socklen_t salen = sizeof(ss); if (getsockname(socket_fd(mrb, self), (struct sockaddr*)&ss, &salen) != 0) mrb_sys_fail(mrb, "getsockname"); return mrb_str_new(mrb, (char*)&ss, salen); } static struct RClass * socket_option_class(mrb_state *mrb) { return mrb_class_get_under_id(mrb, mrb_class_get_id(mrb, MRB_SYM(Socket)), MRB_SYM(Option)); } static mrb_value socket_option_init(mrb_state *mrb, mrb_value self) { mrb_int family, level, optname; mrb_value data; mrb_get_args(mrb, "iiio", &family, &level, &optname, &data); mrb_iv_set(mrb, self, MRB_SYM(family), mrb_int_value(mrb, family)); mrb_iv_set(mrb, self, MRB_SYM(level), mrb_int_value(mrb, level)); mrb_iv_set(mrb, self, MRB_SYM(optname), mrb_int_value(mrb, optname)); mrb_iv_set(mrb, self, MRB_SYM(data), data); return self; } static mrb_value socket_option_s_bool(mrb_state *mrb, mrb_value klass) { mrb_value args[4]; mrb_bool data; mrb_get_args(mrb, "ooob", &args[0], &args[1], &args[2], &data); int tmp = (int)data; args[3] = mrb_str_new(mrb, (char*)&tmp, sizeof(int)); return mrb_obj_new(mrb, mrb_class_ptr(klass), 4, args); } static mrb_value socket_option_s_int(mrb_state *mrb, mrb_value klass) { mrb_value args[4]; mrb_int data; mrb_get_args(mrb, "oooi", &args[0], &args[1], &args[2], &data); int tmp = (int)data; args[3] = mrb_str_new(mrb, (char*)&tmp, sizeof(int)); return mrb_obj_new(mrb, mrb_class_ptr(klass), 4, args); } static mrb_value socket_option_family(mrb_state *mrb, mrb_value self) { return mrb_iv_get(mrb, self, MRB_SYM(family)); } static mrb_value socket_option_level(mrb_state *mrb, mrb_value self) { return mrb_iv_get(mrb, self, MRB_SYM(level)); } static mrb_value socket_option_optname(mrb_state *mrb, mrb_value self) { return mrb_iv_get(mrb, self, MRB_SYM(optname)); } static mrb_value socket_option_data(mrb_state *mrb, mrb_value self) { return mrb_iv_get(mrb, self, MRB_SYM(data)); } static int option_int(mrb_state *mrb, mrb_value self) { mrb_value data = mrb_obj_as_string(mrb, mrb_iv_get(mrb, self, MRB_SYM(data))); if (RSTRING_LEN(data) != sizeof(int)) { mrb_raisef(mrb, E_TYPE_ERROR, "size differ; expected as sizeof(int)=%i but %i", (mrb_int)sizeof(int), RSTRING_LEN(data)); } int tmp; memcpy((char*)&tmp, RSTRING_PTR(data), sizeof(int)); return tmp; } static mrb_value socket_option_int(mrb_state *mrb, mrb_value self) { int i = option_int(mrb, self); return mrb_int_value(mrb, (mrb_int)i); } static mrb_value socket_option_bool(mrb_state *mrb, mrb_value self) { int i = option_int(mrb, self); return mrb_bool_value((mrb_bool)i); } static mrb_value socket_option_notimp(mrb_state *mrb, mrb_value self) { mrb_notimplement(mrb); return mrb_nil_value(); } static mrb_value socket_option_inspect(mrb_state *mrb, mrb_value self) { mrb_value str = mrb_str_new_cstr(mrb, "#"); return str; } static mrb_value mrb_basicsocket_getsockopt(mrb_state *mrb, mrb_value self) { mrb_int level, optname; mrb_get_args(mrb, "ii", &level, &optname); int s = socket_fd(mrb, self); char opt[8]; socklen_t optlen = sizeof(opt); if (getsockopt(s, (int)level, (int)optname, opt, &optlen) == -1) mrb_sys_fail(mrb, "getsockopt"); mrb_int family = socket_family(s); mrb_value data = mrb_str_new(mrb, opt, optlen); mrb_value args[4] = {mrb_fixnum_value(family), mrb_fixnum_value(level), mrb_fixnum_value(optname), data}; return mrb_obj_new(mrb, socket_option_class(mrb), 4, args); } static mrb_value mrb_basicsocket_recv(mrb_state *mrb, mrb_value self) { mrb_int maxlen, flags = 0; mrb_get_args(mrb, "i|i", &maxlen, &flags); mrb_value buf = mrb_str_new_capa(mrb, maxlen); ssize_t n = recv(socket_fd(mrb, self), RSTRING_PTR(buf), (fsize_t)maxlen, (int)flags); if (n == -1) mrb_sys_fail(mrb, "recv"); mrb_str_resize(mrb, buf, (mrb_int)n); return buf; } static mrb_value mrb_basicsocket_recvfrom(mrb_state *mrb, mrb_value self) { mrb_int maxlen, flags = 0; mrb_get_args(mrb, "i|i", &maxlen, &flags); mrb_value buf = mrb_str_new_capa(mrb, maxlen); socklen_t socklen = sizeof(struct sockaddr_storage); mrb_value sa = mrb_str_new_capa(mrb, socklen); ssize_t n = recvfrom(socket_fd(mrb, self), RSTRING_PTR(buf), (fsize_t)maxlen, (int)flags, (struct sockaddr*)RSTRING_PTR(sa), &socklen); if (n == -1) mrb_sys_fail(mrb, "recvfrom"); mrb_str_resize(mrb, buf, (mrb_int)n); mrb_str_resize(mrb, sa, (mrb_int)socklen); mrb_value ary = mrb_ary_new_capa(mrb, 2); mrb_ary_push(mrb, ary, buf); mrb_ary_push(mrb, ary, sa); return ary; } static mrb_value mrb_basicsocket_send(mrb_state *mrb, mrb_value self) { mrb_int flags; mrb_value mesg; mrb_value dest = mrb_nil_value(); mrb_get_args(mrb, "Si|S", &mesg, &flags, &dest); ssize_t n; if (mrb_nil_p(dest)) { n = send(socket_fd(mrb, self), RSTRING_PTR(mesg), (fsize_t)RSTRING_LEN(mesg), (int)flags); } else { n = sendto(socket_fd(mrb, self), RSTRING_PTR(mesg), (fsize_t)RSTRING_LEN(mesg), (int)flags, (const struct sockaddr*)RSTRING_PTR(dest), (fsize_t)RSTRING_LEN(dest)); } if (n == -1) mrb_sys_fail(mrb, "send"); return mrb_fixnum_value((mrb_int)n); } static mrb_value mrb_basicsocket_setnonblock(mrb_state *mrb, mrb_value self) { mrb_bool nonblocking; #ifdef _WIN32 u_long mode = 1; #endif mrb_get_args(mrb, "b", &nonblocking); int fd = socket_fd(mrb, self); #ifdef _WIN32 int flags = ioctlsocket(fd, FIONBIO, &mode); if (flags != NO_ERROR) mrb_sys_fail(mrb, "ioctlsocket"); #else int flags = fcntl(fd, F_GETFL, 0); if (flags == 1) mrb_sys_fail(mrb, "fcntl"); if (nonblocking) flags |= O_NONBLOCK; else flags &= ~O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) == -1) mrb_sys_fail(mrb, "fcntl"); #endif return mrb_nil_value(); } static mrb_value mrb_basicsocket_setsockopt(mrb_state *mrb, mrb_value self) { mrb_int level = 0, optname; mrb_value so, optval; mrb_int argc = mrb_get_args(mrb, "o|io", &so, &optname, &optval); if (argc == 3) { mrb_ensure_int_type(mrb, so); level = mrb_integer(so); if (mrb_string_p(optval)) { /* that's good */ } else if (mrb_true_p(optval) || mrb_false_p(optval)) { mrb_int i = mrb_test(optval) ? 1 : 0; optval = mrb_str_new(mrb, (char*)&i, sizeof(i)); } else if (mrb_integer_p(optval)) { if (optname == IP_MULTICAST_TTL || optname == IP_MULTICAST_LOOP) { char uc = (char)mrb_integer(optval); optval = mrb_str_new(mrb, &uc, sizeof(uc)); } else { mrb_int i = mrb_integer(optval); optval = mrb_str_new(mrb, (char*)&i, sizeof(i)); } } else { mrb_raise(mrb, E_ARGUMENT_ERROR, "optval should be true, false, an integer, or a string"); } } else if (argc == 1) { if (!mrb_obj_is_instance_of(mrb, so, socket_option_class(mrb))) mrb_raise(mrb, E_ARGUMENT_ERROR, "not an instance of Socket::Option"); level = mrb_as_int(mrb, mrb_iv_get(mrb, so, MRB_SYM(level))); optname = mrb_as_int(mrb, mrb_iv_get(mrb, so, MRB_SYM(optname))); optval = mrb_iv_get(mrb, so, MRB_SYM(data)); mrb_ensure_string_type(mrb, optval); } else { mrb_argnum_error(mrb, argc, 3, 3); } int s = socket_fd(mrb, self); if (setsockopt(s, (int)level, (int)optname, RSTRING_PTR(optval), (socklen_t)RSTRING_LEN(optval)) == -1) mrb_sys_fail(mrb, "setsockopt"); return mrb_fixnum_value(0); } static mrb_value mrb_basicsocket_shutdown(mrb_state *mrb, mrb_value self) { mrb_int how = SHUT_RDWR; mrb_get_args(mrb, "|i", &how); if (shutdown(socket_fd(mrb, self), (int)how) != 0) mrb_sys_fail(mrb, "shutdown"); return mrb_fixnum_value(0); } static mrb_value mrb_basicsocket_set_is_socket(mrb_state *mrb, mrb_value self) { mrb_bool b; mrb_get_args(mrb, "b", &b); struct mrb_io *io_p = (struct mrb_io*)DATA_PTR(self); if (io_p) { io_p->is_socket = b; } return mrb_bool_value(b); } static mrb_value mrb_ipsocket_ntop(mrb_state *mrb, mrb_value klass) { mrb_int af, n; const char *addr; char buf[50]; mrb_get_args(mrb, "is", &af, &addr, &n); if ((af == AF_INET && n != 4) || (af == AF_INET6 && n != 16) || inet_ntop((int)af, addr, buf, sizeof(buf)) == NULL) mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid address"); return mrb_str_new_cstr(mrb, buf); } static mrb_value mrb_ipsocket_pton(mrb_state *mrb, mrb_value klass) { mrb_int af, n; const char *bp; char buf[50]; mrb_get_args(mrb, "is", &af, &bp, &n); if ((size_t)n > sizeof(buf) - 1) goto invalid; memcpy(buf, bp, n); buf[n] = '\0'; if (af == AF_INET) { struct in_addr in; if (inet_pton(AF_INET, buf, (void*)&in.s_addr) != 1) goto invalid; return mrb_str_new(mrb, (char*)&in.s_addr, 4); } else if (af == AF_INET6) { struct in6_addr in6; if (inet_pton(AF_INET6, buf, (void*)&in6.s6_addr) != 1) goto invalid; return mrb_str_new(mrb, (char*)&in6.s6_addr, 16); } else { mrb_raise(mrb, E_ARGUMENT_ERROR, "unsupported address family"); } invalid: mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid address"); return mrb_nil_value(); /* dummy */ } static mrb_value mrb_ipsocket_recvfrom(mrb_state *mrb, mrb_value self) { struct sockaddr_storage ss; mrb_int maxlen; mrb_int flags = 0; mrb_get_args(mrb, "i|i", &maxlen, &flags); mrb_value buf = mrb_str_new_capa(mrb, maxlen); socklen_t socklen = sizeof(ss); int fd = socket_fd(mrb, self); ssize_t n = recvfrom(fd, RSTRING_PTR(buf), (fsize_t)maxlen, (int)flags, (struct sockaddr*)&ss, &socklen); if (n == -1) { mrb_sys_fail(mrb, "recvfrom"); } mrb_str_resize(mrb, buf, (mrb_int)n); mrb_value a = sa2addrlist(mrb, (struct sockaddr*)&ss, socklen); mrb_value pair = mrb_ary_new_capa(mrb, 2); mrb_ary_push(mrb, pair, buf); mrb_ary_push(mrb, pair, a); return pair; } static mrb_value mrb_socket_gethostname(mrb_state *mrb, mrb_value cls) { #ifdef HOST_NAME_MAX size_t bufsize = HOST_NAME_MAX + 1; #else size_t bufsize = 256; #endif mrb_value buf = mrb_str_new_capa(mrb, (mrb_int)bufsize); if (gethostname(RSTRING_PTR(buf), (fsize_t)bufsize) != 0) mrb_sys_fail(mrb, "gethostname"); mrb_str_resize(mrb, buf, (mrb_int)strlen(RSTRING_PTR(buf))); return buf; } static mrb_value mrb_socket_accept(mrb_state *mrb, mrb_value klass) { mrb_int s0; mrb_get_args(mrb, "i", &s0); int s1 = (int)accept(s0, NULL, NULL); if (s1 == -1) { mrb_sys_fail(mrb, "accept"); } return mrb_fixnum_value(s1); } static mrb_value mrb_socket_accept2(mrb_state *mrb, mrb_value klass) { mrb_int s0; mrb_get_args(mrb, "i", &s0); socklen_t socklen = sizeof(struct sockaddr_storage); mrb_value sastr = mrb_str_new_capa(mrb, (mrb_int)socklen); int s1 = (int)accept(s0, (struct sockaddr*)RSTRING_PTR(sastr), &socklen); if (s1 == -1) { mrb_sys_fail(mrb, "accept"); } // XXX: possible descriptor leakage here! mrb_str_resize(mrb, sastr, socklen); mrb_value ary = mrb_ary_new_capa(mrb, 2); mrb_ary_push(mrb, ary, mrb_fixnum_value(s1)); mrb_ary_push(mrb, ary, sastr); return ary; } static mrb_value mrb_socket_bind(mrb_state *mrb, mrb_value klass) { mrb_value sastr; mrb_int s; mrb_get_args(mrb, "iS", &s, &sastr); if (bind((int)s, (struct sockaddr*)RSTRING_PTR(sastr), (socklen_t)RSTRING_LEN(sastr)) == -1) { mrb_sys_fail(mrb, "bind"); } return mrb_nil_value(); } static mrb_value mrb_socket_connect(mrb_state *mrb, mrb_value klass) { mrb_value sastr; mrb_int s; mrb_get_args(mrb, "iS", &s, &sastr); if (connect((int)s, (struct sockaddr*)RSTRING_PTR(sastr), (socklen_t)RSTRING_LEN(sastr)) == -1) { mrb_sys_fail(mrb, "connect"); } return mrb_nil_value(); } static mrb_value mrb_socket_listen(mrb_state *mrb, mrb_value klass) { mrb_int s, backlog; mrb_get_args(mrb, "ii", &s, &backlog); if (listen((int)s, (int)backlog) == -1) { mrb_sys_fail(mrb, "listen"); } return mrb_nil_value(); } static mrb_value mrb_socket_sockaddr_family(mrb_state *mrb, mrb_value klass) { mrb_value str; const struct sockaddr *sa; mrb_get_args(mrb, "S", &str); if ((size_t)RSTRING_LEN(str) < offsetof(struct sockaddr, sa_family) + sizeof(sa->sa_family)) { mrb_raise(mrb, E_SOCKET_ERROR, "invalid sockaddr (too short)"); } sa = (const struct sockaddr*)RSTRING_PTR(str); return mrb_fixnum_value(sa->sa_family); } static mrb_value mrb_socket_sockaddr_un(mrb_state *mrb, mrb_value klass) { #ifdef _WIN32 mrb_raise(mrb, E_NOTIMP_ERROR, "sockaddr_un unsupported on Windows"); return mrb_nil_value(); #else mrb_value path; struct sockaddr_un *sunp; mrb_get_args(mrb, "S", &path); if ((size_t)RSTRING_LEN(path) > sizeof(sunp->sun_path) - 1) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "too long unix socket path (max: %d bytes)", (int)sizeof(sunp->sun_path) - 1); } mrb_value s = mrb_str_new_capa(mrb, sizeof(struct sockaddr_un)); sunp = (struct sockaddr_un*)RSTRING_PTR(s); #if HAVE_SA_LEN sunp->sun_len = sizeof(struct sockaddr_un); #endif sunp->sun_family = AF_UNIX; memcpy(sunp->sun_path, RSTRING_PTR(path), RSTRING_LEN(path)); sunp->sun_path[RSTRING_LEN(path)] = '\0'; mrb_str_resize(mrb, s, sizeof(struct sockaddr_un)); return s; #endif } static mrb_value mrb_socket_socketpair(mrb_state *mrb, mrb_value klass) { #ifdef _WIN32 mrb_raise(mrb, E_NOTIMP_ERROR, "socketpair unsupported on Windows"); return mrb_nil_value(); #else mrb_int domain, type, protocol; int sv[2]; mrb_get_args(mrb, "iii", &domain, &type, &protocol); if (socketpair(domain, type, protocol, sv) == -1) { mrb_sys_fail(mrb, "socketpair"); } // XXX: possible descriptor leakage here! mrb_value ary = mrb_ary_new_capa(mrb, 2); mrb_ary_push(mrb, ary, mrb_fixnum_value(sv[0])); mrb_ary_push(mrb, ary, mrb_fixnum_value(sv[1])); return ary; #endif } static mrb_value mrb_socket_socket(mrb_state *mrb, mrb_value klass) { mrb_int domain, type, protocol; mrb_get_args(mrb, "iii", &domain, &type, &protocol); int s = (int)socket((int)domain, (int)type, (int)protocol); if (s == -1) mrb_sys_fail(mrb, "socket"); return mrb_fixnum_value(s); } static mrb_value mrb_tcpsocket_allocate(mrb_state *mrb, mrb_value klass) { struct RClass *c = mrb_class_ptr(klass); enum mrb_vtype ttype = MRB_INSTANCE_TT(c); /* copied from mrb_instance_alloc() */ if (ttype == 0) ttype = MRB_TT_OBJECT; return mrb_obj_value((struct RObject*)mrb_obj_alloc(mrb, ttype, c)); } /* Windows overrides for IO methods on BasicSocket objects. * This is because sockets on Windows are not the same as file * descriptors, and thus functions which operate on file descriptors * will break on socket descriptors. */ #ifdef _WIN32 static mrb_value mrb_win32_basicsocket_close(mrb_state *mrb, mrb_value self) { if (closesocket(socket_fd(mrb, self)) != NO_ERROR) mrb_raise(mrb, E_SOCKET_ERROR, "closesocket unsuccessful"); return mrb_nil_value(); } static mrb_value mrb_win32_basicsocket_sysread(mrb_state *mrb, mrb_value self) { mrb_value buf = mrb_nil_value(); mrb_int maxlen; mrb_get_args(mrb, "i|S", &maxlen, &buf); if (maxlen < 0) { return mrb_nil_value(); } if (mrb_nil_p(buf)) { buf = mrb_str_new(mrb, NULL, maxlen); } if (RSTRING_LEN(buf) != maxlen) { buf = mrb_str_resize(mrb, buf, maxlen); } int sd = socket_fd(mrb, self); int ret = recv(sd, RSTRING_PTR(buf), (int)maxlen, 0); switch (ret) { case 0: /* EOF */ if (maxlen == 0) { buf = mrb_str_new_cstr(mrb, ""); } else { mrb_raise(mrb, E_EOF_ERROR, "sysread failed: End of File"); } break; case SOCKET_ERROR: /* Error */ mrb_sys_fail(mrb, "recv"); break; default: if (RSTRING_LEN(buf) != ret) { buf = mrb_str_resize(mrb, buf, ret); } break; } return buf; } static mrb_value mrb_win32_basicsocket_sysseek(mrb_state *mrb, mrb_value self) { mrb_raise(mrb, E_NOTIMP_ERROR, "sysseek not implemented for windows sockets"); return mrb_nil_value(); } static mrb_value mrb_win32_basicsocket_syswrite(mrb_state *mrb, mrb_value self) { mrb_value str; SOCKET sd = socket_fd(mrb, self); mrb_get_args(mrb, "S", &str); int n = send(sd, RSTRING_PTR(str), (int)RSTRING_LEN(str), 0); if (n == SOCKET_ERROR) mrb_sys_fail(mrb, "send"); return mrb_int_value(mrb, n); } #endif void mrb_mruby_socket_gem_init(mrb_state* mrb) { #ifdef _WIN32 WSADATA wsaData; int result; result = WSAStartup(MAKEWORD(2,2), &wsaData); if (result != NO_ERROR) mrb_raise(mrb, E_RUNTIME_ERROR, "WSAStartup failed"); #endif struct RClass *ainfo = mrb_define_class_id(mrb, MRB_SYM(Addrinfo), mrb->object_class); mrb_define_class_method_id(mrb, ainfo, MRB_SYM(getaddrinfo), mrb_addrinfo_getaddrinfo, MRB_ARGS_REQ(2)|MRB_ARGS_OPT(4)); mrb_define_method_id(mrb, ainfo, MRB_SYM(getnameinfo), mrb_addrinfo_getnameinfo, MRB_ARGS_OPT(1)); #ifndef _WIN32 mrb_define_method_id(mrb, ainfo, MRB_SYM(unix_path), mrb_addrinfo_unix_path, MRB_ARGS_NONE()); #endif struct RClass *io = mrb_class_get_id(mrb, MRB_SYM(IO)); struct RClass *bsock = mrb_define_class_id(mrb, MRB_SYM(BasicSocket), io); mrb_define_method_id(mrb, bsock, MRB_SYM(_recvfrom), mrb_basicsocket_recvfrom, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, bsock, MRB_SYM(_setnonblock), mrb_basicsocket_setnonblock, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, bsock, MRB_SYM(getpeereid), mrb_basicsocket_getpeereid, MRB_ARGS_NONE()); mrb_define_method_id(mrb, bsock, MRB_SYM(getpeername), mrb_basicsocket_getpeername, MRB_ARGS_NONE()); mrb_define_method_id(mrb, bsock, MRB_SYM(getsockname), mrb_basicsocket_getsockname, MRB_ARGS_NONE()); mrb_define_method_id(mrb, bsock, MRB_SYM(getsockopt), mrb_basicsocket_getsockopt, MRB_ARGS_REQ(2)); mrb_define_method_id(mrb, bsock, MRB_SYM(recv), mrb_basicsocket_recv, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, bsock, MRB_SYM(send), mrb_basicsocket_send, MRB_ARGS_REQ(2)|MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, bsock, MRB_SYM(setsockopt), mrb_basicsocket_setsockopt, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(2)); mrb_define_method_id(mrb, bsock, MRB_SYM(shutdown), mrb_basicsocket_shutdown, MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, bsock, MRB_SYM_E(_is_socket), mrb_basicsocket_set_is_socket, MRB_ARGS_REQ(1)); struct RClass *ipsock = mrb_define_class_id(mrb, MRB_SYM(IPSocket), bsock); mrb_define_class_method_id(mrb, ipsock, MRB_SYM(ntop), mrb_ipsocket_ntop, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, ipsock, MRB_SYM(pton), mrb_ipsocket_pton, MRB_ARGS_REQ(2)); mrb_define_method_id(mrb, ipsock, MRB_SYM(recvfrom), mrb_ipsocket_recvfrom, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); struct RClass *tcpsock = mrb_define_class_id(mrb, MRB_SYM(TCPSocket), ipsock); mrb_define_class_method_id(mrb, tcpsock, MRB_SYM(_allocate), mrb_tcpsocket_allocate, MRB_ARGS_NONE()); struct RClass *sock = mrb_define_class_id(mrb, MRB_SYM(Socket), bsock); mrb_define_class_method_id(mrb, sock, MRB_SYM(_accept), mrb_socket_accept, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, sock, MRB_SYM(_accept2), mrb_socket_accept2, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, sock, MRB_SYM(_bind), mrb_socket_bind, MRB_ARGS_REQ(3)); mrb_define_class_method_id(mrb, sock, MRB_SYM(_connect), mrb_socket_connect, MRB_ARGS_REQ(3)); mrb_define_class_method_id(mrb, sock, MRB_SYM(_listen), mrb_socket_listen, MRB_ARGS_REQ(2)); mrb_define_class_method_id(mrb, sock, MRB_SYM(_sockaddr_family), mrb_socket_sockaddr_family, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, sock, MRB_SYM(_socket), mrb_socket_socket, MRB_ARGS_REQ(3)); mrb_define_class_method_id(mrb, sock, MRB_SYM(gethostname), mrb_socket_gethostname, MRB_ARGS_NONE()); mrb_define_class_method_id(mrb, sock, MRB_SYM(sockaddr_un), mrb_socket_sockaddr_un, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, sock, MRB_SYM(socketpair), mrb_socket_socketpair, MRB_ARGS_REQ(3)); /* Windows IO Methods Overridden on BasicSocket */ #ifdef _WIN32 mrb_define_method_id(mrb, bsock, MRB_SYM(close), mrb_win32_basicsocket_close, MRB_ARGS_NONE()); mrb_define_method_id(mrb, bsock, MRB_SYM(sysread), mrb_win32_basicsocket_sysread, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, bsock, MRB_SYM(sysseek), mrb_win32_basicsocket_sysseek, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, bsock, MRB_SYM(syswrite), mrb_win32_basicsocket_syswrite, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, bsock, MRB_SYM(read), mrb_win32_basicsocket_sysread, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, bsock, MRB_SYM(write), mrb_win32_basicsocket_syswrite, MRB_ARGS_REQ(1)); #endif struct RClass *option = mrb_define_class_under_id(mrb, sock, MRB_SYM(Option), mrb->object_class); mrb_define_class_method_id(mrb, option, MRB_SYM(bool), socket_option_s_bool, MRB_ARGS_REQ(4)); mrb_define_class_method_id(mrb, option, MRB_SYM(int), socket_option_s_int, MRB_ARGS_REQ(4)); mrb_define_method_id(mrb, option, MRB_SYM(initialize), socket_option_init, MRB_ARGS_REQ(4)); mrb_define_method_id(mrb, option, MRB_SYM(inspect), socket_option_inspect, MRB_ARGS_REQ(0)); mrb_define_method_id(mrb, option, MRB_SYM(family), socket_option_family, MRB_ARGS_REQ(0)); mrb_define_method_id(mrb, option, MRB_SYM(level), socket_option_level, MRB_ARGS_REQ(0)); mrb_define_method_id(mrb, option, MRB_SYM(optname), socket_option_optname, MRB_ARGS_REQ(0)); mrb_define_method_id(mrb, option, MRB_SYM(data), socket_option_data, MRB_ARGS_REQ(0)); mrb_define_method_id(mrb, option, MRB_SYM(bool), socket_option_bool, MRB_ARGS_REQ(0)); mrb_define_method_id(mrb, option, MRB_SYM(int), socket_option_int, MRB_ARGS_REQ(0)); mrb_define_method_id(mrb, option, MRB_SYM(linger), socket_option_notimp, MRB_ARGS_REQ(0)); mrb_define_method_id(mrb, option, MRB_SYM(unpack), socket_option_notimp, MRB_ARGS_REQ(1)); struct RClass *constants = mrb_define_module_under_id(mrb, sock, MRB_SYM(Constants)); #define define_const(SYM) \ do { \ mrb_define_const(mrb, constants, #SYM, mrb_int_value(mrb, SYM)); \ } while (0) #include "const.cstub" mrb_include_module(mrb, sock, constants); } void mrb_mruby_socket_gem_final(mrb_state* mrb) { #ifdef _WIN32 WSACleanup(); #endif } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-bin-mrbc0000644000000000000000000000013215077107334022072 xustar0030 mtime=1761382108.923300893 30 atime=1761382109.796298369 30 ctime=1761382108.923300893 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mrbc/0000755000175100017510000000000015077107334022537 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mrbc/PaxHeaders/bintest0000644000000000000000000000013015077107334023540 xustar0029 mtime=1761382108.92430089 30 atime=1761382109.796298369 29 ctime=1761382108.92430089 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mrbc/bintest/0000755000175100017510000000000015077107334024207 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mrbc/bintest/PaxHeaders/mrbc.rb0000644000000000000000000000013115077107276025073 xustar0030 mtime=1761382078.107420625 30 atime=1761382080.127411384 29 ctime=1761382108.92430089 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mrbc/bintest/mrbc.rb0000644000175100017510000000204515077107276025465 0ustar00runnerrunnerrequire 'tempfile' assert('Compiling multiple files without new line in last line. #2361') do a, b, out = Tempfile.new('a.rb'), Tempfile.new('b.rb'), Tempfile.new('out.mrb') a.write('module A; end') a.flush b.write('module B; end') b.flush result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} #{b.path} 2>&1` assert_equal "#{cmd_bin('mrbc')}:#{a.path}:Syntax OK", result.chomp assert_equal 0, $?.exitstatus end assert('parsing function with void argument') do a, out = Tempfile.new('a.rb'), Tempfile.new('out.mrb') a.write('f ()') a.flush result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} 2>&1` assert_equal "#{cmd_bin('mrbc')}:#{a.path}:Syntax OK", result.chomp assert_equal 0, $?.exitstatus end assert('embedded document with invalid terminator') do a, out = Tempfile.new('a.rb'), Tempfile.new('out.mrb') a.write("=begin\n=endx\n") a.flush result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} 2>&1` assert_equal "#{a.path}:3:0: embedded document meets end of file", result.chomp assert_equal 1, $?.exitstatus end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mrbc/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024271 xustar0030 mtime=1761382078.107420625 30 atime=1761382080.127411384 30 ctime=1761382108.923300893 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mrbc/mrbgem.rake0000644000175100017510000000103115077107276024654 0ustar00runnerrunnerMRuby::Gem::Specification.new 'mruby-bin-mrbc' do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'mruby compiler executable' spec.add_dependency 'mruby-compiler', :core => 'mruby-compiler' exec = exefile("#{build.build_dir}/bin/mrbc") mrbc_objs = Dir.glob("#{spec.dir}/tools/mrbc/*.c").map { |f| objfile(f.pathmap("#{spec.build_dir}/tools/mrbc/%n")) } file exec => mrbc_objs << build.libmruby_core_static do |t| build.linker.run t.name, t.prerequisites end build.bins << 'mrbc' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mrbc/PaxHeaders/tools0000644000000000000000000000013215077107334023232 xustar0030 mtime=1761382108.202302977 30 atime=1761382109.796298369 30 ctime=1761382108.202302977 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mrbc/tools/0000755000175100017510000000000015077107334023677 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mrbc/tools/PaxHeaders/mrbc0000644000000000000000000000013215077107334024155 xustar0030 mtime=1761382108.922300896 30 atime=1761382109.796298369 30 ctime=1761382108.922300896 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/0000755000175100017510000000000015077107334024622 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/PaxHeaders/stub.c0000644000000000000000000000013215077107276025360 xustar0030 mtime=1761382078.107420625 30 atime=1761382080.127411384 30 ctime=1761382108.920300902 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/stub.c0000644000175100017510000000352015077107276025750 0ustar00runnerrunner#include /* functions defined in mrbgems referenced from the core should be listed here to avoid link errors, since mrbc does not link any mrbgem ignoring configuration. */ #ifdef MRB_USE_COMPLEX mrb_value mrb_complex_new(mrb_state *mrb, mrb_float x, mrb_float y) { return mrb_nil_value(); } mrb_value mrb_complex_add(mrb_state *mrb, mrb_value x, mrb_value y) { return mrb_nil_value(); } mrb_value mrb_complex_sub(mrb_state *mrb, mrb_value x, mrb_value y) { return mrb_nil_value(); } mrb_value mrb_complex_mul(mrb_state *mrb, mrb_value x, mrb_value y) { return mrb_nil_value(); } mrb_value mrb_complex_div(mrb_state *mrb, mrb_value x, mrb_value y) { return mrb_nil_value(); } mrb_value mrb_complex_to_i(mrb_state *mrb, mrb_value x) { return mrb_nil_value(); } mrb_value mrb_complex_to_f(mrb_state *mrb, mrb_value x) { return mrb_nil_value(); } void mrb_complex_copy(mrb_state *mrb, mrb_value x, mrb_value y) { } #endif #ifdef MRB_USE_RATIONAL mrb_value mrb_rational_new(mrb_state *mrb, mrb_int x, mrb_int y) { return mrb_nil_value(); } mrb_value mrb_rational_add(mrb_state *mrb, mrb_value x, mrb_value y) { return mrb_nil_value(); } mrb_value mrb_rational_sub(mrb_state *mrb, mrb_value x, mrb_value y) { return mrb_nil_value(); } mrb_value mrb_rational_mul(mrb_state *mrb, mrb_value x, mrb_value y) { return mrb_nil_value(); } mrb_value mrb_rational_div(mrb_state *mrb, mrb_value x, mrb_value y) { return mrb_nil_value(); } mrb_value mrb_rational_to_i(mrb_state *mrb, mrb_value x) { return mrb_nil_value(); } mrb_value mrb_rational_to_f(mrb_state *mrb, mrb_value x) { return mrb_nil_value(); } mrb_value mrb_as_rational(mrb_state *mrb, mrb_value x) { return mrb_nil_value(); } void mrb_rational_copy(mrb_state *mrb, mrb_value x, mrb_value y) { } int mrb_rational_mark(mrb_state *mrb, struct RBasic *x) { return 2; } #endif nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/PaxHeaders/mrbc.c0000644000000000000000000000013215077107276025326 xustar0030 mtime=1761382078.107420625 30 atime=1761382080.127411384 30 ctime=1761382108.922300896 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c0000644000175100017510000002102615077107276025717 0ustar00runnerrunner#include #ifdef MRB_NO_STDIO # error mruby-bin-mrbc conflicts 'MRB_NO_STDIO' in your build configuration #endif #include #include #include #include #include #include #define RITEBIN_EXT ".mrb" #define C_EXT ".c" struct mrbc_args { const char *prog; const char *outfile; const char *initname; char **argv; int argc; int idx; mrb_bool dump_struct : 1; mrb_bool check_syntax : 1; mrb_bool verbose : 1; mrb_bool no_ext_ops : 1; mrb_bool no_optimize : 1; uint8_t flags : 3; }; static void usage(const char *name) { static const char *const usage_msg[] = { "switches:", "-c check syntax only", "-o place the output into ; required for multi-files", "-v print version number, then turn on verbose mode", "-g produce debugging information", "-B binary output in C language format", "-S dump C struct (requires -B)", "-s define as static variable", "--remove-lv remove local variables", "--no-ext-ops prohibit using OP_EXTs", "--no-optimize disable peephole optimization", "--verbose run at verbose mode", "--version print the version", "--copyright print the copyright", NULL }; const char *const *p = usage_msg; printf("Usage: %s [switches] programfile...\n", name); while (*p) printf(" %s\n", *p++); } static char * get_outfilename(mrb_state *mrb, char *infile, const char *ext) { size_t ilen, flen, elen; char *outfile; char *p = NULL; ilen = strlen(infile); flen = ilen; if (*ext) { elen = strlen(ext); if ((p = strrchr(infile, '.'))) { ilen = p - infile; } flen += elen; } else { flen = ilen; } outfile = (char*)mrb_malloc(mrb, flen+1); strncpy(outfile, infile, ilen+1); if (p) { strncpy(outfile+ilen, ext, elen+1); } return outfile; } static int parse_args(mrb_state *mrb, int argc, char **argv, struct mrbc_args *args) { static const struct mrbc_args args_zero = { 0 }; int i; *args = args_zero; args->argc = argc; args->argv = argv; args->prog = argv[0]; for (i=1; ioutfile) { fprintf(stderr, "%s: an output file is already specified. (%s)\n", args->prog, args->outfile); return -1; } if (argv[i][2] == '\0' && argv[i+1]) { i++; args->outfile = get_outfilename(mrb, argv[i], ""); } else { args->outfile = get_outfilename(mrb, argv[i] + 2, ""); } break; case 'S': args->dump_struct = TRUE; break; case 'B': if (argv[i][2] == '\0' && argv[i+1]) { i++; args->initname = argv[i]; } else { args->initname = argv[i]+2; } if (*args->initname == '\0') { fprintf(stderr, "%s: function name is not specified.\n", args->prog); return -1; } break; case 'c': args->check_syntax = TRUE; break; case 'v': if (!args->verbose) mrb_show_version(mrb); args->verbose = TRUE; break; case 'g': args->flags |= MRB_DUMP_DEBUG_INFO; break; case 's': args->flags |= MRB_DUMP_STATIC; break; case 'E': case 'e': fprintf(stderr, "%s: -e/-E option no longer needed.\n", args->prog); break; case 'h': return -1; case '-': if (argv[i][1] == '\n') { return i; } if (strcmp(argv[i] + 2, "version") == 0) { mrb_show_version(mrb); exit(EXIT_SUCCESS); } else if (strcmp(argv[i] + 2, "verbose") == 0) { args->verbose = TRUE; break; } else if (strcmp(argv[i] + 2, "copyright") == 0) { mrb_show_copyright(mrb); exit(EXIT_SUCCESS); } else if (strcmp(argv[i] + 2, "remove-lv") == 0) { args->flags |= MRB_DUMP_NO_LVAR; break; } else if (strcmp(argv[i] + 2, "no-ext-ops") == 0) { args->no_ext_ops = TRUE; break; } else if (strcmp(argv[i] + 2, "no-optimize") == 0) { args->no_optimize = TRUE; break; } return -1; default: return i; } } else { break; } } return i; } static void cleanup(mrb_state *mrb, struct mrbc_args *args) { mrb_free(mrb, (void*)args->outfile); mrb_close(mrb); } static int partial_hook(struct mrb_parser_state *p) { mrb_ccontext *c = p->cxt; struct mrbc_args *args = (struct mrbc_args*)c->partial_data; const char *fn; if (p->f) fclose(p->f); if (args->idx >= args->argc) { p->f = NULL; return -1; } fn = args->argv[args->idx++]; p->f = fopen(fn, "rb"); if (p->f == NULL) { fprintf(stderr, "%s: cannot open program file. (%s)\n", args->prog, fn); return -1; } mrb_parser_set_filename(p, fn); return 0; } static mrb_value load_file(mrb_state *mrb, struct mrbc_args *args) { mrb_ccontext *c; mrb_value result; char *input = args->argv[args->idx]; FILE *infile; mrb_bool need_close = FALSE; c = mrb_ccontext_new(mrb); if (args->verbose) c->dump_result = TRUE; c->no_exec = TRUE; c->no_ext_ops = args->no_ext_ops; c->no_optimize = args->no_optimize; if (input[0] == '-' && input[1] == '\0') { infile = stdin; } else { need_close = TRUE; if ((infile = fopen(input, "rb")) == NULL) { fprintf(stderr, "%s: cannot open program file. (%s)\n", args->prog, input); return mrb_nil_value(); } } mrb_ccontext_filename(mrb, c, input); args->idx++; if (args->idx < args->argc) { need_close = FALSE; mrb_ccontext_partial_hook(c, partial_hook, (void*)args); } result = mrb_load_file_cxt(mrb, infile, c); if (need_close) fclose(infile); mrb_ccontext_free(mrb, c); if (mrb_undef_p(result)) { return mrb_nil_value(); } return result; } static int dump_file(mrb_state *mrb, FILE *wfp, const char *outfile, const struct RProc *proc, struct mrbc_args *args) { int n = MRB_DUMP_OK; const mrb_irep *irep = proc->body.irep; if (args->initname) { if (args->dump_struct) { n = mrb_dump_irep_cstruct(mrb, irep, args->flags, wfp, args->initname); } else { n = mrb_dump_irep_cfunc(mrb, irep, args->flags, wfp, args->initname); } if (n == MRB_DUMP_INVALID_ARGUMENT) { fprintf(stderr, "%s: invalid C language symbol name\n", args->initname); } } else { n = mrb_dump_irep_binary(mrb, irep, args->flags, wfp); } if (n != MRB_DUMP_OK) { fprintf(stderr, "%s: error in mrb dump (%s) %d\n", args->prog, outfile, n); } return n; } int main(int argc, char **argv) { mrb_state *mrb = mrb_open_core(); int n, result; struct mrbc_args args; FILE *wfp; mrb_value load; if (mrb == NULL) { fputs("Invalid mrb_state, exiting mrbc\n", stderr); return EXIT_FAILURE; } n = parse_args(mrb, argc, argv, &args); if (n < 0) { cleanup(mrb, &args); usage(argv[0]); return EXIT_FAILURE; } if (n == argc) { fprintf(stderr, "%s: no program file given\n", args.prog); return EXIT_FAILURE; } if (args.outfile == NULL && !args.check_syntax) { if (n + 1 == argc) { args.outfile = get_outfilename(mrb, argv[n], args.initname ? C_EXT : RITEBIN_EXT); } else { fprintf(stderr, "%s: output file should be specified to compile multiple files\n", args.prog); return EXIT_FAILURE; } } args.idx = n; load = load_file(mrb, &args); if (mrb_nil_p(load)) { cleanup(mrb, &args); return EXIT_FAILURE; } if (args.check_syntax) { printf("%s:%s:Syntax OK\n", args.prog, argv[n]); } if (args.check_syntax) { cleanup(mrb, &args); return EXIT_SUCCESS; } if (args.outfile) { if (strcmp("-", args.outfile) == 0) { wfp = stdout; } else if ((wfp = fopen(args.outfile, "wb")) == NULL) { fprintf(stderr, "%s: cannot open output file:(%s)\n", args.prog, args.outfile); return EXIT_FAILURE; } } else { fputs("Output file is required\n", stderr); return EXIT_FAILURE; } result = dump_file(mrb, wfp, args.outfile, mrb_proc_ptr(load), &args); fclose(wfp); cleanup(mrb, &args); if (result != MRB_DUMP_OK) { return EXIT_FAILURE; } return EXIT_SUCCESS; } void mrb_init_mrblib(mrb_state *mrb) { } #ifndef MRB_NO_GEMS void mrb_init_mrbgems(mrb_state *mrb) { } #endif nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-dir0000644000000000000000000000013215077107334021157 xustar0030 mtime=1761382108.714301497 30 atime=1761382109.796298369 30 ctime=1761382108.714301497 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/0000755000175100017510000000000015077107334021624 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/PaxHeaders/mrbgem.rake0000644000000000000000000000013015077107276023354 xustar0030 mtime=1761382078.115420588 30 atime=1761382080.137411338 28 ctime=1761382108.7133015 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/mrbgem.rake0000644000175100017510000000025215077107276023745 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-dir') do |spec| spec.license = 'MIT and MIT-like license' spec.authors = ['Internet Initiative Japan Inc.', 'Kevlin Henney'] end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/PaxHeaders/mrblib0000644000000000000000000000013215077107334022426 xustar0030 mtime=1761382108.718301485 30 atime=1761382109.796298369 30 ctime=1761382108.718301485 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/mrblib/0000755000175100017510000000000015077107334023073 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/mrblib/PaxHeaders/dir.rb0000644000000000000000000000013215077107276023613 xustar0030 mtime=1761382078.115420588 30 atime=1761382080.137411338 30 ctime=1761382108.718301485 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/mrblib/dir.rb0000644000175100017510000000266115077107276024210 0ustar00runnerrunnerclass Dir include Enumerable def each(&block) return to_enum(:each) unless block while s = self.read block.call(s) end self end def each_child(&block) return to_enum(:each_child) unless block while s = self.read block.call(s) unless s == "." || s == ".." end self end alias pos tell alias pos= seek class << self def entries(path) a = [] self.open(path) do |d| while s = d.read a << s end end a end def children(path) a = [] self.open(path) do |d| while s = d.read a << s unless s == "." || s == ".." end end a end def foreach(path, &block) return to_enum(:foreach, path) unless block self.open(path) do |d| d.each(&block) end end def open(path, &block) if block d = self.new(path) begin block.call(d) ensure begin d.close rescue IOError end end else self.new(path) end end def chdir(path, &block) if block wd = self.getwd begin self._chdir(path) block.call(path) ensure self._chdir(wd) end else self._chdir(path) end end alias exists? exist? alias pwd getwd alias rmdir delete alias unlink delete end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/PaxHeaders/README.md0000644000000000000000000000013215077107276022520 xustar0030 mtime=1761382078.115420588 30 atime=1761382080.137411338 30 ctime=1761382108.714301497 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/README.md0000644000175100017510000000334415077107276023114 0ustar00runnerrunner# mruby-dir Dir class for mruby. Supported methods are: `.chdir` `.delete` `.entries` `.exist?` `.foreach` `.getwd` `.mkdir` `.open` `#close` `#each` `#read` `#rewind` `#seek` `#tell` ## License Copyright (c) 2012 Internet Initiative Japan Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ### On Windows platforms, you must agree on additional license too: Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved. Permission to use, copy, modify, and distribute this software and its documentation for any purpose is hereby granted without fee, provided that this copyright and permissions notice appear in all copies and derivatives. This software is supplied "as is" without express or implied warranty. But that said, if there are any problems please get in touch. nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/PaxHeaders/test0000644000000000000000000000013215077107334022136 xustar0030 mtime=1761382108.717301488 30 atime=1761382109.796298369 30 ctime=1761382108.717301488 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/test/0000755000175100017510000000000015077107334022603 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/test/PaxHeaders/dir.rb0000644000000000000000000000013215077107276023323 xustar0030 mtime=1761382078.116420584 30 atime=1761382080.137411338 30 ctime=1761382108.715301494 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/test/dir.rb0000644000175100017510000000475515077107276023726 0ustar00runnerrunnerassert('Dir') do assert_equal(Class, Dir.class) end assert('DirTest.setup') do assert_nothing_raised{DirTest.setup} end assert('Dir.chdir') do assert_equal 0, Dir.chdir(DirTest.sandbox) end assert('Dir.entries') do a = Dir.entries(DirTest.sandbox) assert_true a.include?("a") assert_true a.include?("b") end assert('Dir.exist?') do assert_true Dir.exist?(DirTest.sandbox) assert_false Dir.exist?(DirTest.sandbox + "/nosuchdir") end assert('Dir.foreach') do a = [] Dir.foreach(DirTest.sandbox) { |s| a << s } assert_true a.include?("a") assert_true a.include?("b") end assert('Dir.getwd') do s = Dir.getwd assert_true s.kind_of? String end assert('Dir.mkdir') do m1 = DirTest.sandbox + "/mkdir1" m2 = DirTest.sandbox + "/mkdir2" assert_equal 0, Dir.mkdir(m1) assert_equal 0, Dir.mkdir(m2, 0765) end assert('Dir.delete') do s = DirTest.sandbox + "/delete" Dir.mkdir(s) assert_true Dir.exist?(s) Dir.delete(s) assert_false Dir.exist?(s) end assert('Dir.open') do a = [] Dir.open(DirTest.sandbox) { |d| d.each_child { |s| a << s } } assert_true a.include?("a") assert_true a.include?("b") end assert('Dir#initialize and Dir#close') do d = Dir.new(".") assert_true d.instance_of?(Dir) assert_nil d.close end assert('Dir#close') do d = Dir.new(".") assert_nothing_raised{d.close} end assert('Dir#each_child') do a = [] d = Dir.open(DirTest.sandbox) d.each_child { |s| a << s } d.close assert_true a.include?("a") assert_true a.include?("b") end assert('Dir#read') do a = [] d = Dir.open(DirTest.sandbox) while s = d.read a << s end d.close assert_true a.include?("a") assert_true a.include?("b") end assert('Dir#rewind') do d = Dir.open(DirTest.sandbox) while d.read; end assert_equal d, d.rewind a = [] while s = d.read a << s end d.close assert_true a.include? "a" assert_true a.include? "b" end # Note: behaviors of seekdir(3) and telldir(3) are so platform-dependent # that we cannot write portable tests here. assert('Dir#tell') do n = nil begin Dir.open(DirTest.sandbox) { |d| n = d.tell } assert_true n.is_a? Integer rescue NotImplementedError => e skip e.message end end assert('Dir#seek') do d1 = Dir.open(DirTest.sandbox) d1.read begin n = d1.tell d1.read d2 = d1.seek(n) assert_equal d1, d2 rescue NotImplementedError => e skip e.message end end assert('DirTest.teardown') do GC.start assert_nothing_raised{DirTest.teardown} end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/test/PaxHeaders/dirtest.c0000644000000000000000000000013215077107276024042 xustar0030 mtime=1761382078.116420584 30 atime=1761382080.137411338 30 ctime=1761382108.717301488 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/test/dirtest.c0000644000175100017510000000677315077107276024447 0ustar00runnerrunner#include #include #include #include #include #if defined(_WIN32) #ifdef __cplusplus extern "C" { #endif typedef struct DIR DIR; struct dirent { char *d_name; }; DIR *opendir(const char *name); struct dirent *readdir(DIR *dir); int closedir(DIR *dir); #ifdef __cplusplus } #endif #include #include #define rmdir(path) _rmdir(path) #define getcwd(path,len) _getcwd(path,len) #define mkdir(path,mode) _mkdir(path) #define chdir(path) _chdir(path) #else #include #include #endif #include #include #include static void make_dir(mrb_state *mrb, const char *name, const char *up) { if (mkdir(name, 0) == -1) { if (chdir("..") == 0) { rmdir(up); } mrb_raisef(mrb, E_RUNTIME_ERROR, "mkdir(%s) failed", mrb_str_new_cstr(mrb, name)); } } mrb_value mrb_dirtest_setup(mrb_state *mrb, mrb_value klass) { char buf[1024]; const char *aname = "a"; const char *bname = "b"; /* save current working directory */ if (getcwd(buf, sizeof(buf)) == NULL) { mrb_raise(mrb, E_RUNTIME_ERROR, "getcwd() failed"); } mrb_cv_set(mrb, klass, mrb_intern_cstr(mrb, "pwd"), mrb_str_new_cstr(mrb, buf)); /* create sandbox */ #if defined(_WIN32) snprintf(buf, sizeof(buf), "%s\\mruby-dir-test.XXXXXX", _getcwd(NULL,0)); if ((_mktemp(buf) == NULL) || mkdir(buf,0) != 0) { mrb_raisef(mrb, E_RUNTIME_ERROR, "mkdtemp(%s) failed", buf); } #else snprintf(buf, sizeof(buf), "%s/mruby-dir-test.XXXXXX", P_tmpdir); if (mkdtemp(buf) == NULL) { mrb_raisef(mrb, E_RUNTIME_ERROR, "mkdtemp(%s) failed", buf); } #endif mrb_cv_set(mrb, klass, mrb_intern_cstr(mrb, "sandbox"), mrb_str_new_cstr(mrb, buf)); /* go to sandbox */ if (chdir(buf) == -1) { rmdir(buf); mrb_raisef(mrb, E_RUNTIME_ERROR, "chdir(%s) failed", buf); } /* make some directories in the sandbox */ make_dir(mrb, aname, buf); make_dir(mrb, bname, buf); return mrb_true_value(); } mrb_value mrb_dirtest_teardown(mrb_state *mrb, mrb_value klass) { mrb_value d, sandbox; DIR *dirp; struct dirent *dp; const char *path; /* cleanup sandbox */ sandbox = mrb_cv_get(mrb, klass, mrb_intern_cstr(mrb, "sandbox")); path = mrb_str_to_cstr(mrb, sandbox); dirp = opendir(path); while ((dp = readdir(dirp)) != NULL) { if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) continue; if (rmdir(dp->d_name) == -1) { mrb_raisef(mrb, E_RUNTIME_ERROR, "rmdir(%s) failed", dp->d_name); } } closedir(dirp); /* back to original pwd */ d = mrb_cv_get(mrb, klass, mrb_intern_cstr(mrb, "pwd")); path = mrb_str_to_cstr(mrb, d); if (chdir(path) == -1) { mrb_raisef(mrb, E_RUNTIME_ERROR, "chdir(%s) failed", path); } /* remove sandbox directory */ sandbox = mrb_cv_get(mrb, klass, mrb_intern_cstr(mrb, "sandbox")); path = mrb_str_to_cstr(mrb, sandbox); if (rmdir(path) == -1) { mrb_raisef(mrb, E_RUNTIME_ERROR, "rmdir(%s) failed", path); } return mrb_true_value(); } mrb_value mrb_dirtest_sandbox(mrb_state *mrb, mrb_value klass) { return mrb_cv_get(mrb, klass, mrb_intern_cstr(mrb, "sandbox")); } void mrb_mruby_dir_gem_test(mrb_state *mrb) { struct RClass *c = mrb_define_module(mrb, "DirTest"); mrb_define_class_method(mrb, c, "sandbox", mrb_dirtest_sandbox, MRB_ARGS_NONE()); mrb_define_class_method(mrb, c, "setup", mrb_dirtest_setup, MRB_ARGS_NONE()); mrb_define_class_method(mrb, c, "teardown", mrb_dirtest_teardown, MRB_ARGS_NONE()); } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/PaxHeaders/src0000644000000000000000000000013215077107334021746 xustar0030 mtime=1761382108.721301477 30 atime=1761382109.796298369 30 ctime=1761382108.721301477 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/src/0000755000175100017510000000000015077107334022413 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/src/PaxHeaders/Win0000644000000000000000000000013215077107334022503 xustar0030 mtime=1761382108.719301482 30 atime=1761382109.796298369 30 ctime=1761382108.719301482 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/src/Win/0000755000175100017510000000000015077107334023150 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/src/Win/PaxHeaders/dirent.c0000644000000000000000000000013215077107276024216 xustar0030 mtime=1761382078.115420588 30 atime=1761382080.137411338 30 ctime=1761382108.719301482 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/src/Win/dirent.c0000644000175100017510000000613215077107276024610 0ustar00runnerrunner/* Implementation of POSIX directory browsing functions and types for Win32. Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com) History: Created March 1997. Updated June 2003 and July 2012. Rights: See end of file. */ #include #include /* _findfirst and _findnext set errno iff they return -1 */ #include #include #ifdef __cplusplus extern "C" { #endif typedef ptrdiff_t handle_type; /* C99's intptr_t not sufficiently portable */ struct dirent { char *d_name; }; struct DIR { handle_type handle; /* -1 for failed rewind */ struct _finddata_t info; struct dirent result; /* d_name null iff first time */ char *name; /* null-terminated char string */ }; typedef struct DIR DIR; DIR *opendir(const char *name) { DIR *dir = 0; if (name && name[0]) { size_t base_length = strlen(name); const char *all = /* search pattern must end with suitable wildcard */ strchr("/\\", name[base_length - 1]) ? "*" : "/*"; if ((dir = (DIR *) malloc(sizeof *dir)) != 0 && (dir->name = (char *) malloc(base_length + strlen(all) + 1)) != 0) { strcat(strcpy(dir->name, name), all); if ((dir->handle = (handle_type) _findfirst(dir->name, &dir->info)) != -1) { dir->result.d_name = 0; } else { /* rollback */ free(dir->name); free(dir); dir = 0; } } else { /* rollback */ free(dir); dir = 0; errno = ENOMEM; } } else { errno = EINVAL; } return dir; } int closedir(DIR *dir) { int result = -1; if (dir) { if (dir->handle != -1) { result = _findclose(dir->handle); } free(dir->name); free(dir); } if (result == -1) { /* map all errors to EBADF */ errno = EBADF; } return result; } struct dirent *readdir(DIR *dir) { struct dirent *result = 0; if (dir && dir->handle != -1) { if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) { result = &dir->result; result->d_name = dir->info.name; } } else { errno = EBADF; } return result; } void rewinddir(DIR *dir) { if (dir && dir->handle != -1) { _findclose(dir->handle); dir->handle = (handle_type) _findfirst(dir->name, &dir->info); dir->result.d_name = 0; } else { errno = EBADF; } } #ifdef __cplusplus } #endif /* Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved. Permission to use, copy, modify, and distribute this software and its documentation for any purpose is hereby granted without fee, provided that this copyright and permissions notice appear in all copies and derivatives. This software is supplied "as is" without express or implied warranty. But that said, if there are any problems please get in touch. */ nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/src/PaxHeaders/dir.c0000644000000000000000000000013215077107276022752 xustar0030 mtime=1761382078.116420584 30 atime=1761382080.137411338 30 ctime=1761382108.721301477 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-dir/src/dir.c0000644000175100017510000001705415077107276023351 0ustar00runnerrunner/* ** dir.c - Dir ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #if defined(_WIN32) #define MAXPATHLEN 1024 #if !defined(PATH_MAX) #define PATH_MAX MAX_PATH #endif #define S_ISDIR(B) ((B)&_S_IFDIR) #include "Win/dirent.c" #include #define rmdir(path) _rmdir(path) #define getcwd(path,len) _getcwd(path,len) #define mkdir(path,mode) _mkdir(path) #define chdir(path) _chdir(path) #else #include #include #include #endif #include #include #include #include #include #include #include #define E_IO_ERROR mrb_exc_get_id(mrb, MRB_SYM(IOError)) struct mrb_dir { DIR *dir; }; static void mrb_dir_free(mrb_state *mrb, void *ptr) { struct mrb_dir *mdir = (struct mrb_dir*)ptr; if (mdir->dir) { closedir(mdir->dir); mdir->dir = NULL; } mrb_free(mrb, mdir); } static struct mrb_data_type mrb_dir_type = { "DIR", mrb_dir_free }; static mrb_value mrb_dir_close(mrb_state *mrb, mrb_value self) { struct mrb_dir *mdir; mdir = (struct mrb_dir*)mrb_get_datatype(mrb, self, &mrb_dir_type); if (!mdir) return mrb_nil_value(); if (!mdir->dir) { mrb_raise(mrb, E_IO_ERROR, "closed directory"); } if (closedir(mdir->dir) == -1) { mrb_sys_fail(mrb, "closedir"); } mdir->dir = NULL; return mrb_nil_value(); } static mrb_value mrb_dir_init(mrb_state *mrb, mrb_value self) { DIR *dir; struct mrb_dir *mdir; const char *path; mdir = (struct mrb_dir*)DATA_PTR(self); if (mdir) { mrb_dir_free(mrb, mdir); } DATA_TYPE(self) = &mrb_dir_type; DATA_PTR(self) = NULL; mdir = (struct mrb_dir*)mrb_malloc(mrb, sizeof(*mdir)); mdir->dir = NULL; DATA_PTR(self) = mdir; mrb_get_args(mrb, "z", &path); if ((dir = opendir(path)) == NULL) { mrb_sys_fail(mrb, path); } mdir->dir = dir; return self; } static mrb_value mrb_dir_delete(mrb_state *mrb, mrb_value klass) { const char *path; mrb_get_args(mrb, "z", &path); if (rmdir(path) == -1) { mrb_sys_fail(mrb, path); } return mrb_fixnum_value(0); } static mrb_value mrb_dir_existp(mrb_state *mrb, mrb_value klass) { struct stat sb; const char *path; mrb_get_args(mrb, "z", &path); if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { return mrb_true_value(); } else { return mrb_false_value(); } } static mrb_value mrb_dir_getwd(mrb_state *mrb, mrb_value klass) { mrb_value path; mrb_int size = 64; path = mrb_str_buf_new(mrb, size); while (getcwd(RSTRING_PTR(path), size) == NULL) { int e = errno; if (e != ERANGE) { mrb_sys_fail(mrb, "getcwd(2)"); } size *= 2; mrb_str_resize(mrb, path, size); } mrb_str_resize(mrb, path, strlen(RSTRING_PTR(path))); return path; } static mrb_value mrb_dir_mkdir(mrb_state *mrb, mrb_value klass) { mrb_int mode; const char *path; mode = 0777; mrb_get_args(mrb, "z|i", &path, &mode); if (mkdir(path, mode) == -1) { mrb_sys_fail(mrb, path); } return mrb_fixnum_value(0); } static mrb_value mrb_dir_chdir(mrb_state *mrb, mrb_value klass) { const char *path; mrb_get_args(mrb, "z", &path); if (chdir(path) == -1) { mrb_sys_fail(mrb, path); } return mrb_fixnum_value(0); } static mrb_value mrb_dir_chroot(mrb_state *mrb, mrb_value self) { #if defined(_WIN32) || defined(__ANDROID__) || defined(__MSDOS__) mrb_raise(mrb, E_NOTIMP_ERROR, "chroot() unreliable on your system"); return mrb_fixnum_value(0); #else const char *path; int res; mrb_get_args(mrb, "z", &path); res = chroot(path); if (res == -1) { mrb_sys_fail(mrb, path); } return mrb_fixnum_value(res); #endif } static mrb_bool skip_name_p(const char *name) { if (name[0] != '.') return FALSE; if (name[1] == '\0') return TRUE; if (name[1] != '.') return FALSE; if (name[2] == '\0') return TRUE; return FALSE; } static mrb_value mrb_dir_empty(mrb_state *mrb, mrb_value self) { DIR *dir; struct dirent *dp; const char *path; mrb_value result = mrb_true_value(); mrb_get_args(mrb, "z", &path); if ((dir = opendir(path)) == NULL) { mrb_sys_fail(mrb, path); } while ((dp = readdir(dir))) { if (!skip_name_p(dp->d_name)) { result = mrb_false_value(); break; } } closedir(dir); return result; } static mrb_value mrb_dir_read(mrb_state *mrb, mrb_value self) { struct mrb_dir *mdir; struct dirent *dp; mdir = (struct mrb_dir*)mrb_get_datatype(mrb, self, &mrb_dir_type); if (!mdir) return mrb_nil_value(); if (!mdir->dir) { mrb_raise(mrb, E_IO_ERROR, "closed directory"); } dp = readdir(mdir->dir); if (dp != NULL) { return mrb_str_new_cstr(mrb, dp->d_name); } else { return mrb_nil_value(); } } static mrb_value mrb_dir_rewind(mrb_state *mrb, mrb_value self) { struct mrb_dir *mdir; mdir = (struct mrb_dir*)mrb_get_datatype(mrb, self, &mrb_dir_type); if (!mdir) return mrb_nil_value(); if (!mdir->dir) { mrb_raise(mrb, E_IO_ERROR, "closed directory"); } rewinddir(mdir->dir); return self; } static mrb_value mrb_dir_seek(mrb_state *mrb, mrb_value self) { #if defined(_WIN32) || defined(__ANDROID__) mrb_raise(mrb, E_NOTIMP_ERROR, "dirseek() unreliable on your system"); return self; #else struct mrb_dir *mdir; mrb_int pos; mdir = (struct mrb_dir*)mrb_get_datatype(mrb, self, &mrb_dir_type); if (!mdir) return mrb_nil_value(); if (!mdir->dir) { mrb_raise(mrb, E_IO_ERROR, "closed directory"); } mrb_get_args(mrb, "i", &pos); seekdir(mdir->dir, (long)pos); return self; #endif } static mrb_value mrb_dir_tell(mrb_state *mrb, mrb_value self) { #if defined(_WIN32) || defined(__ANDROID__) mrb_raise(mrb, E_NOTIMP_ERROR, "dirtell() unreliable on your system"); return mrb_fixnum_value(0); #else struct mrb_dir *mdir; mrb_int pos; mdir = (struct mrb_dir*)mrb_get_datatype(mrb, self, &mrb_dir_type); if (!mdir) return mrb_nil_value(); if (!mdir->dir) { mrb_raise(mrb, E_IO_ERROR, "closed directory"); } pos = (mrb_int)telldir(mdir->dir); return mrb_fixnum_value(pos); #endif } void mrb_mruby_dir_gem_init(mrb_state *mrb) { struct RClass *d; d = mrb_define_class_id(mrb, MRB_SYM(Dir), mrb->object_class); MRB_SET_INSTANCE_TT(d, MRB_TT_DATA); mrb_define_class_method_id(mrb, d, MRB_SYM(delete), mrb_dir_delete, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, d, MRB_SYM_Q(exist), mrb_dir_existp, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, d, MRB_SYM(getwd), mrb_dir_getwd, MRB_ARGS_NONE()); mrb_define_class_method_id(mrb, d, MRB_SYM(mkdir), mrb_dir_mkdir, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_class_method_id(mrb, d, MRB_SYM(_chdir), mrb_dir_chdir, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, d, MRB_SYM(chroot), mrb_dir_chroot, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, d, MRB_SYM_Q(empty), mrb_dir_empty, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, d, MRB_SYM(close), mrb_dir_close, MRB_ARGS_NONE()); mrb_define_method_id(mrb, d, MRB_SYM(initialize), mrb_dir_init, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, d, MRB_SYM(read), mrb_dir_read, MRB_ARGS_NONE()); mrb_define_method_id(mrb, d, MRB_SYM(rewind), mrb_dir_rewind, MRB_ARGS_NONE()); mrb_define_method_id(mrb, d, MRB_SYM(seek), mrb_dir_seek, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, d, MRB_SYM(tell), mrb_dir_tell, MRB_ARGS_NONE()); mrb_define_class_id(mrb, MRB_SYM(IOError), E_STANDARD_ERROR); } void mrb_mruby_dir_gem_final(mrb_state *mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-hash-ext0000644000000000000000000000013215077107334022122 xustar0030 mtime=1761382108.745301407 30 atime=1761382109.796298369 30 ctime=1761382108.745301407 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-hash-ext/0000755000175100017510000000000015077107334022567 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-hash-ext/PaxHeaders/mrbgem.rake0000644000000000000000000000013115077107276024320 xustar0029 mtime=1761382078.11942057 30 atime=1761382080.140411325 30 ctime=1761382108.745301407 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-hash-ext/mrbgem.rake0000644000175100017510000000034215077107276024710 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-hash-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Hash class extension' spec.add_dependency 'mruby-array-ext', core: 'mruby-array-ext' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-hash-ext/PaxHeaders/mrblib0000644000000000000000000000013215077107334023371 xustar0030 mtime=1761382108.748301399 30 atime=1761382109.796298369 30 ctime=1761382108.748301399 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-hash-ext/mrblib/0000755000175100017510000000000015077107334024036 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-hash-ext/mrblib/PaxHeaders/hash.rb0000644000000000000000000000013115077107276024722 xustar0029 mtime=1761382078.11942057 30 atime=1761382080.140411325 30 ctime=1761382108.748301399 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-hash-ext/mrblib/hash.rb0000644000175100017510000003115715077107276025322 0ustar00runnerrunnerclass Hash # ISO does not define Hash#each_pair, so each_pair is defined in gem. alias each_pair each ## # call-seq: # Hash[ key, value, ... ] -> new_hash # Hash[ [ [key, value], ... ] ] -> new_hash # Hash[ object ] -> new_hash # # Creates a new hash populated with the given objects. # # Similar to the literal `{ _key_ => _value_, ... }`. In the first # form, keys and values occur in pairs, so there must be an even number of # arguments. # # The second and third form take a single argument which is either an array # of key-value pairs or an object convertible to a hash. # # Hash["a", 100, "b", 200] #=> {"a"=>100, "b"=>200} # Hash[ [ ["a", 100], ["b", 200] ] ] #=> {"a"=>100, "b"=>200} # Hash["a" => 100, "b" => 200] #=> {"a"=>100, "b"=>200} # def self.[](*object) length = object.length if length == 1 o = object[0] if Hash === o h = self.new o.each { |k, v| h[k] = v } return h elsif o.respond_to?(:to_a) h = self.new o.to_a.each do |i| raise ArgumentError, "wrong element type #{i.class} (expected array)" unless i.respond_to?(:to_a) k, v = nil case i.size when 2 k = i[0] v = i[1] when 1 k = i[0] else raise ArgumentError, "invalid number of elements (#{i.size} for 1..2)" end h[k] = v end return h end end unless length % 2 == 0 raise ArgumentError, 'odd number of arguments for Hash' end h = self.new 0.step(length - 2, 2) do |i| h[object[i]] = object[i + 1] end h end ## # call-seq: # hsh.merge!(other_hash..) -> hsh # hsh.merge!(other_hash..){|key, oldval, newval| block} -> hsh # # Adds the contents of _other_hash_ to _hsh_. If no block is specified, # entries with duplicate keys are overwritten with the values from # _other_hash_, otherwise the value of each duplicate key is determined by # calling the block with the key, its value in _hsh_ and its value in # _other_hash_. # # h1 = { "a" => 100, "b" => 200 } # h2 = { "b" => 254, "c" => 300 } # h1.merge!(h2) #=> {"a"=>100, "b"=>254, "c"=>300} # # h1 = { "a" => 100, "b" => 200 } # h2 = { "b" => 254, "c" => 300 } # h1.merge!(h2) { |key, v1, v2| v1 } # #=> {"a"=>100, "b"=>200, "c"=>300} # def merge!(*others, &block) i = 0; len=others.size return self.__merge(*others) unless block while i hsh # # Removes all nil values from the hash. Returns the hash. # Returns nil if the hash does not contain nil values. # # h = { a: 1, b: false, c: nil } # h.compact! #=> { a: 1, b: false } # def compact! self.__compact end ## # call-seq: # hsh.compact -> new_hsh # # Returns a new hash with the nil values/key pairs removed # # h = { a: 1, b: false, c: nil } # h.compact #=> { a: 1, b: false } # h #=> { a: 1, b: false, c: nil } # def compact h=self.dup h.__compact h end ## # call-seq: # hsh.fetch(key [, default] ) -> obj # hsh.fetch(key) {| key | block } -> obj # # Returns a value from the hash for the given key. If the key can't be # found, there are several options: With no other arguments, it will # raise an KeyError exception; if default is # given, then that will be returned; if the optional code block is # specified, then that will be run and its result returned. # # h = { "a" => 100, "b" => 200 } # h.fetch("a") #=> 100 # h.fetch("z", "go fish") #=> "go fish" # h.fetch("z") { |el| "go fish, #{el}"} #=> "go fish, z" # # The following example shows that an exception is raised if the key # is not found and a default value is not supplied. # # h = { "a" => 100, "b" => 200 } # h.fetch("z") # # produces: # # prog.rb:2:in 'fetch': key not found (KeyError) # from prog.rb:2 # def fetch(key, none=NONE, &block) unless self.key?(key) if block block.call(key) elsif !NONE.equal?(none) none else raise KeyError, "Key not found: #{key.inspect}" end else self[key] end end ## # call-seq: # hsh.delete_if {| key, value | block } -> hsh # hsh.delete_if -> an_enumerator # # Deletes every key-value pair from hsh for which block # evaluates to true. # # If no block is given, an enumerator is returned instead. # # h = { "a" => 100, "b" => 200, "c" => 300 } # h.delete_if {|key, value| key >= "b" } #=> {"a"=>100} # def delete_if(&block) return to_enum :delete_if unless block self.each do |k, v| self.delete(k) if block.call(k, v) end self end ## # call-seq: # hash.flatten -> an_array # hash.flatten(level) -> an_array # # Returns a new array that is a one-dimensional flattening of this # hash. That is, for every key or value that is an array, extract # its elements into the new array. Unlike Array#flatten, this # method does not flatten recursively by default. The optional # level argument determines the level of recursion to flatten. # # a = {1=> "one", 2 => [2,"two"], 3 => "three"} # a.flatten # => [1, "one", 2, [2, "two"], 3, "three"] # a.flatten(2) # => [1, "one", 2, 2, "two", 3, "three"] # def flatten(level=1) self.to_a.flatten(level) end ## # call-seq: # hsh.invert -> new_hash # # Returns a new hash created by using hsh's values as keys, and # the keys as values. # # h = { "n" => 100, "m" => 100, "y" => 300, "d" => 200, "a" => 0 } # h.invert #=> {0=>"a", 100=>"m", 200=>"d", 300=>"y"} # def invert h = self.class.new self.each {|k, v| h[v] = k } h end ## # call-seq: # hsh.keep_if {| key, value | block } -> hsh # hsh.keep_if -> an_enumerator # # Deletes every key-value pair from hsh for which block # evaluates to false. # # If no block is given, an enumerator is returned instead. # def keep_if(&block) return to_enum :keep_if unless block self.each do |k, v| unless block.call([k, v]) self.delete(k) end end self end ## # call-seq: # hsh.key(value) -> key # # Returns the key of an occurrence of a given value. If the value is # not found, returns nil. # # h = { "a" => 100, "b" => 200, "c" => 300, "d" => 300 } # h.key(200) #=> "b" # h.key(300) #=> "c" # h.key(999) #=> nil # def key(val) self.each do |k, v| return k if v == val end nil end ## # call-seq: # hsh.to_h -> hsh or new_hash # # Returns +self+. If called on a subclass of Hash, converts # the receiver to a Hash object. # def to_h self end ## # call-seq: # hash < other -> true or false # # Returns true if hash is subset of # other. # # h1 = {a:1, b:2} # h2 = {a:1, b:2, c:3} # h1 < h2 #=> true # h2 < h1 #=> false # h1 < h1 #=> false # def <(hash) raise TypeError, "can't convert #{hash.class} to Hash" unless Hash === hash size < hash.size and all? {|key, val| hash.key?(key) and hash[key] == val } end ## # call-seq: # hash <= other -> true or false # # Returns true if hash is subset of # other or equals to other. # # h1 = {a:1, b:2} # h2 = {a:1, b:2, c:3} # h1 <= h2 #=> true # h2 <= h1 #=> false # h1 <= h1 #=> true # def <=(hash) raise TypeError, "can't convert #{hash.class} to Hash" unless Hash === hash size <= hash.size and all? {|key, val| hash.key?(key) and hash[key] == val } end ## # call-seq: # hash > other -> true or false # # Returns true if other is subset of # hash. # # h1 = {a:1, b:2} # h2 = {a:1, b:2, c:3} # h1 > h2 #=> false # h2 > h1 #=> true # h1 > h1 #=> false # def >(hash) raise TypeError, "can't convert #{hash.class} to Hash" unless Hash === hash size > hash.size and hash.all? {|key, val| key?(key) and self[key] == val } end ## # call-seq: # hash >= other -> true or false # # Returns true if other is subset of # hash or equals to hash. # # h1 = {a:1, b:2} # h2 = {a:1, b:2, c:3} # h1 >= h2 #=> false # h2 >= h1 #=> true # h1 >= h1 #=> true # def >=(hash) raise TypeError, "can't convert #{hash.class} to Hash" unless Hash === hash size >= hash.size and hash.all? {|key, val| key?(key) and self[key] == val } end ## # call-seq: # hsh.dig(key,...) -> object # # Extracts the nested value specified by the sequence of key # objects by calling +dig+ at each step, returning +nil+ if any # intermediate step is +nil+. # def dig(idx,*args) n = self[idx] if args.size > 0 n&.dig(*args) else n end end ## # call-seq: # hsh.transform_keys {|key| block } -> new_hash # hsh.transform_keys -> an_enumerator # # Returns a new hash, with the keys computed from running the block # once for each key in the hash, and the values unchanged. # # If no block is given, an enumerator is returned instead. # def transform_keys(&block) return to_enum :transform_keys unless block hash = {} self.keys.each do |k| new_key = block.call(k) hash[new_key] = self[k] end hash end ## # call-seq: # hsh.transform_keys! {|key| block } -> hsh # hsh.transform_keys! -> an_enumerator # # Invokes the given block once for each key in hsh, replacing it # with the new key returned by the block, and then returns hsh. # # If no block is given, an enumerator is returned instead. # def transform_keys!(&block) return to_enum :transform_keys! unless block hash = self.transform_keys(&block) self.replace(hash) self end ## # call-seq: # hsh.transform_values {|value| block } -> new_hash # hsh.transform_values -> an_enumerator # # Returns a new hash with the results of running the block once for # every value. # This method does not change the keys. # # If no block is given, an enumerator is returned instead. # def transform_values(&b) return to_enum :transform_values unless block_given? hash = {} self.keys.each do |k| hash[k] = yield(self[k]) end hash end ## # call-seq: # hsh.transform_values! {|key| block } -> hsh # hsh.transform_values! -> an_enumerator # # Invokes the given block once for each value in the hash, replacing # with the new value returned by the block, and then returns hsh. # # If no block is given, an enumerator is returned instead. # def transform_values!(&b) return to_enum :transform_values! unless block_given? self.keys.each do |k| self[k] = yield(self[k]) end self end def to_proc ->x{self[x]} end ## # call-seq: # hsh.fetch_values(key, ...) -> array # hsh.fetch_values(key, ...) { |key| block } -> array # # Returns an array containing the values associated with the given keys # but also raises KeyError when one of keys can't be found. # Also see Hash#values_at and Hash#fetch. # # h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" } # # h.fetch_values("cow", "cat") #=> ["bovine", "feline"] # h.fetch_values("cow", "bird") # raises KeyError # h.fetch_values("cow", "bird") { |k| k.upcase } #=> ["bovine", "BIRD"] # def fetch_values(*keys, &block) keys.map do |k| self.fetch(k, &block) end end alias filter select alias filter! select! end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-hash-ext/PaxHeaders/test0000644000000000000000000000013215077107334023101 xustar0030 mtime=1761382108.746301405 30 atime=1761382109.796298369 30 ctime=1761382108.746301405 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-hash-ext/test/0000755000175100017510000000000015077107334023546 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-hash-ext/test/PaxHeaders/hash.rb0000644000000000000000000000013115077107276024432 xustar0029 mtime=1761382078.11942057 30 atime=1761382080.140411325 30 ctime=1761382108.746301405 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-hash-ext/test/hash.rb0000644000175100017510000001600115077107276025021 0ustar00runnerrunner## # Hash(Ext) Test assert('Hash.[] Hash') do a = Hash['a_key' => 'a_value'] assert_equal({'a_key' => 'a_value'}, a) end assert('Hash.[] [ [ ["b_key", "b_value" ] ] ]') do a = Hash[ [ ['b_key', 'b_value'] ] ] assert_equal({'b_key' => 'b_value'}, a) a = Hash[ [ ] ] assert_equal({}, a) assert_raise(ArgumentError) do Hash[ [ ['b_key', 'b_value', 'b_over'] ] ] end assert_raise(ArgumentError) do Hash[ [ [] ] ] end end assert('Hash.[] "c_key", "c_value"') do a = Hash['c_key', 'c_value', 'd_key', 1] assert_equal({'c_key' => 'c_value', 'd_key' => 1}, a) a = Hash[] assert_equal({}, a) assert_raise(ArgumentError) do Hash['d_key'] end end assert('Hash.[] for sub class') do sub_hash_class = Class.new(Hash) sub_hash = sub_hash_class[] assert_equal(sub_hash_class, sub_hash.class) end assert('Hash#merge!') do a = { 'abc_key' => 'abc_value', 'cba_key' => 'cba_value' } b = { 'cba_key' => 'XXX', 'xyz_key' => 'xyz_value' } result_1 = a.merge! b a = { 'abc_key' => 'abc_value', 'cba_key' => 'cba_value' } result_2 = a.merge!(b) do |key, original, new| original end assert_equal({'abc_key' => 'abc_value', 'cba_key' => 'XXX', 'xyz_key' => 'xyz_value' }, result_1) assert_equal({'abc_key' => 'abc_value', 'cba_key' => 'cba_value', 'xyz_key' => 'xyz_value' }, result_2) assert_raise(TypeError) do { 'abc_key' => 'abc_value' }.merge! "a" end # multiple arguments assert_equal({a:1,b:2,c:3}, {a:1}.merge!({b:2},{c:3})) end assert('Hash#values_at') do h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" } assert_equal ["bovine", "feline"], h.values_at("cow", "cat") keys = [] (0...1000).each { |v| keys.push "#{v}" } h = Hash.new { |hash,k| hash[k] = k } assert_equal keys, h.values_at(*keys) end assert('Hash#compact') do h = { "cat" => "feline", "dog" => nil, "cow" => false } assert_equal({ "cat" => "feline", "cow" => false }, h.compact) assert_equal({ "cat" => "feline", "dog" => nil, "cow" => false }, h) end assert('Hash#compact!') do h = { "cat" => "feline", "dog" => nil, "cow" => false } assert_equal({ "cat" => "feline", "cow" => false }, h.compact!) assert_nil(h.compact!) end assert('Hash#fetch') do h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" } assert_equal "feline", h.fetch("cat") assert_equal "mickey", h.fetch("mouse", "mickey") assert_equal "minny", h.fetch("mouse"){"minny"} assert_equal "mouse", h.fetch("mouse"){|k| k} assert_raise(KeyError) do h.fetch("gnu") end end assert("Hash#delete_if") do base = { 1 => 'one', 2 => false, true => 'true', 'cat' => 99 } h1 = { 1 => 'one', 2 => false, true => 'true' } h2 = { 2 => false, 'cat' => 99 } h3 = { 2 => false } h = base.dup assert_equal(h, h.delete_if { false }) assert_equal({}, h.delete_if { true }) h = base.dup assert_equal(h1, h.delete_if {|k,v| k.instance_of?(String) }) assert_equal(h1, h) h = base.dup assert_equal(h2, h.delete_if {|k,v| v.instance_of?(String) }) assert_equal(h2, h) h = base.dup assert_equal(h3, h.delete_if {|k,v| v }) assert_equal(h3, h) h = base.dup n = 0 h.delete_if {|*a| n += 1 assert_equal(2, a.size) assert_equal(base[a[0]], a[1]) h.shift true } assert_equal(base.size, n) end assert("Hash#flatten") do a = {1=> "one", 2 => [2,"two"], 3 => [3, ["three"]]} assert_equal [1, "one", 2, [2, "two"], 3, [3, ["three"]]], a.flatten assert_equal [[1, "one"], [2, [2, "two"]], [3, [3, ["three"]]]], a.flatten(0) assert_equal [1, "one", 2, [2, "two"], 3, [3, ["three"]]], a.flatten(1) assert_equal [1, "one", 2, 2, "two", 3, 3, ["three"]], a.flatten(2) assert_equal [1, "one", 2, 2, "two", 3, 3, "three"], a.flatten(3) end assert("Hash#invert") do h = { 1 => 'one', 2 => 'two', 3 => 'three', true => 'true', nil => 'nil' }.invert assert_equal 1, h['one'] assert_equal true, h['true'] assert_equal nil, h['nil'] h = { 'a' => 1, 'b' => 2, 'c' => 1 }.invert assert_equal(2, h.length) assert_include(%w[a c], h[1]) assert_equal('b', h[2]) end assert("Hash#invert with sub class") do sub_hash_class = Class.new(Hash) sub_hash = sub_hash_class.new assert_equal(sub_hash_class, sub_hash.invert.class) end assert("Hash#keep_if") do h = { 1 => 2, 3 => 4, 5 => 6 } assert_equal({3=>4,5=>6}, h.keep_if {|k, v| k + v >= 7 }) h = { 1 => 2, 3 => 4, 5 => 6 } assert_equal({ 1 => 2, 3=> 4, 5 =>6} , h.keep_if { true }) end assert("Hash#key") do h = { "a" => 100, "b" => 200, "c" => 300, "d" => 300, nil => 'nil', 'nil' => nil } assert_equal "b", h.key(200) assert_equal "c", h.key(300) assert_nil h.key(999) assert_nil h.key('nil') assert_equal 'nil', h.key(nil) end assert("Hash#to_h") do h = { "a" => 100, "b" => 200 } assert_equal Hash, h.to_h.class assert_equal h, h.to_h end assert('Hash#<') do h1 = {a:1, b:2} h2 = {a:1, b:2, c:3} assert_false(h1 < h1) assert_true(h1 < h2) assert_false(h2 < h1) assert_false(h2 < h2) h1 = {a:1} h2 = {a:2} assert_false(h1 < h1) assert_false(h1 < h2) assert_false(h2 < h1) assert_false(h2 < h2) end assert('Hash#<=') do h1 = {a:1, b:2} h2 = {a:1, b:2, c:3} assert_true(h1 <= h1) assert_true(h1 <= h2) assert_false(h2 <= h1) assert_true(h2 <= h2) h1 = {a:1} h2 = {a:2} assert_true(h1 <= h1) assert_false(h1 <= h2) assert_false(h2 <= h1) assert_true(h2 <= h2) end assert('Hash#>=') do h1 = {a:1, b:2} h2 = {a:1, b:2, c:3} assert_true(h1 >= h1) assert_false(h1 >= h2) assert_true(h2 >= h1) assert_true(h2 >= h2) h1 = {a:1} h2 = {a:2} assert_true(h1 >= h1) assert_false(h1 >= h2) assert_false(h2 >= h1) assert_true(h2 >= h2) end assert('Hash#>') do h1 = {a:1, b:2} h2 = {a:1, b:2, c:3} assert_false(h1 > h1) assert_false(h1 > h2) assert_true(h2 > h1) assert_false(h2 > h2) h1 = {a:1} h2 = {a:2} assert_false(h1 > h1) assert_false(h1 > h2) assert_false(h2 > h1) assert_false(h2 > h2) end assert("Hash#dig") do h = {a:{b:{c:1}}} assert_equal(1, h.dig(:a, :b, :c)) assert_nil(h.dig(:d)) end assert("Hash#transform_keys") do h = {"1" => 100, "2" => 200} assert_equal({"1!" => 100, "2!" => 200}, h.transform_keys{|k| k+"!"}) assert_equal({1 => 100, 2 => 200}, h.transform_keys{|k|k.to_i}) assert_same(h, h.transform_keys!{|k|k.to_i}) assert_equal({1 => 100, 2 => 200}, h) end assert("Hash#transform_values") do h = {a: 1, b: 2, c: 3} assert_equal({a: 2, b: 5, c: 10}, h.transform_values{|v| v * v + 1}) assert_equal({a: "1", b: "2", c: "3"}, h.transform_values{|v|v.to_s}) assert_same(h, h.transform_values!{|v|v.to_s}) assert_equal({a: "1", b: "2", c: "3"}, h) end assert("Hash#slice") do h = { a: 100, b: 200, c: 300 } assert_equal({:a=>100}, h.slice(:a)) assert_equal({:b=>200, :c=>300}, h.slice(:b, :c, :d)) end assert("Hash#except") do h = { a: 100, b: 200, c: 300 } assert_equal({:b=>200, :c=>300}, h.except(:a)) assert_equal({:a=>100}, h.except(:b, :c, :d)) assert_equal(h, h.except) end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-hash-ext/PaxHeaders/src0000644000000000000000000000013215077107334022711 xustar0030 mtime=1761382108.749301396 30 atime=1761382109.796298369 30 ctime=1761382108.749301396 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-hash-ext/src/0000755000175100017510000000000015077107334023356 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-hash-ext/src/PaxHeaders/hash_ext.c0000644000000000000000000000013115077107276024741 xustar0029 mtime=1761382078.11942057 30 atime=1761382080.140411325 30 ctime=1761382108.749301396 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-hash-ext/src/hash_ext.c0000644000175100017510000000512315077107276025333 0ustar00runnerrunner/* ** hash.c - Hash class ** ** See Copyright Notice in mruby.h */ #include #include #include #include /* * call-seq: * hsh.values_at(key, ...) -> array * * Return an array containing the values associated with the given keys. * Also see Hash.select. * * h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" } * h.values_at("cow", "cat") #=> ["bovine", "feline"] */ static mrb_value hash_values_at(mrb_state *mrb, mrb_value hash) { const mrb_value *argv; mrb_value result; mrb_int argc; int ai; mrb_get_args(mrb, "*", &argv, &argc); result = mrb_ary_new_capa(mrb, argc); if (argc == 0) return result; ai = mrb_gc_arena_save(mrb); for (mrb_int i = 0; i < argc; i++) { mrb_ary_push(mrb, result, mrb_hash_get(mrb, hash, argv[i])); mrb_gc_arena_restore(mrb, ai); } return result; } /* * call-seq: * hsh.slice(*keys) -> a_hash * * Returns a hash containing only the given keys and their values. * * h = { a: 100, b: 200, c: 300 } * h.slice(:a) #=> {:a=>100} * h.slice(:b, :c, :d) #=> {:b=>200, :c=>300} */ static mrb_value hash_slice(mrb_state *mrb, mrb_value hash) { const mrb_value *argv; mrb_value result; mrb_int argc; mrb_get_args(mrb, "*", &argv, &argc); result = mrb_hash_new_capa(mrb, argc); if (argc == 0) return result; /* empty hash */ for (mrb_int i = 0; i < argc; i++) { mrb_value key = argv[i]; mrb_value val; val = mrb_hash_fetch(mrb, hash, key, mrb_undef_value()); if (!mrb_undef_p(val)) { mrb_hash_set(mrb, result, key, val); } } return result; } /* * call-seq: * hsh.except(*keys) -> a_hash * * Returns a hash excluding the given keys and their values. * * h = { a: 100, b: 200, c: 300 } * h.except(:a) #=> {:b=>200, :c=>300} * h.except(:b, :c, :d) #=> {:a=>100} */ static mrb_value hash_except(mrb_state *mrb, mrb_value hash) { const mrb_value *argv; mrb_value result; mrb_int argc; mrb_get_args(mrb, "*", &argv, &argc); result = mrb_hash_dup(mrb, hash); for (mrb_int i = 0; i < argc; i++) { mrb_hash_delete_key(mrb, result, argv[i]); } return result; } void mrb_mruby_hash_ext_gem_init(mrb_state *mrb) { struct RClass *h; h = mrb->hash_class; mrb_define_method_id(mrb, h, MRB_SYM(values_at), hash_values_at, MRB_ARGS_ANY()); mrb_define_method_id(mrb, h, MRB_SYM(slice), hash_slice, MRB_ARGS_ANY()); mrb_define_method_id(mrb, h, MRB_SYM(except), hash_except, MRB_ARGS_ANY()); } void mrb_mruby_hash_ext_gem_final(mrb_state *mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-random0000644000000000000000000000013215077107334021661 xustar0030 mtime=1761382108.629301743 30 atime=1761382109.796298369 30 ctime=1761382108.629301743 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-random/0000755000175100017510000000000015077107334022326 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-random/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024060 xustar0030 mtime=1761382078.125420543 30 atime=1761382080.144411306 30 ctime=1761382108.629301743 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-random/mrbgem.rake0000644000175100017510000000022715077107276024451 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-random') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Random class' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-random/PaxHeaders/test0000644000000000000000000000013015077107334022636 xustar0029 mtime=1761382108.63030174 30 atime=1761382109.796298369 29 ctime=1761382108.63030174 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-random/test/0000755000175100017510000000000015077107334023305 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-random/test/PaxHeaders/random.rb0000644000000000000000000000013115077107276024526 xustar0030 mtime=1761382078.125420543 30 atime=1761382080.144411306 29 ctime=1761382108.63030174 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-random/test/random.rb0000644000175100017510000000735215077107276025126 0ustar00runnerrunner## # Random Test assert("Random.new") do r1 = Random.new(123) r2 = Random.new(123) r3 = Random.new(124) assert_equal(r1.rand, r2.rand) assert_not_equal(r1.rand, r3.rand) end assert("Kernel#srand") do srand(234) r1 = rand srand(234) r2 = rand srand(235) r3 = rand assert_equal(r1, r2) assert_not_equal(r1, r3) end assert("Random.srand") do Random.srand(345) r1 = rand srand(345) r2 = Random.rand Random.srand(346) r3 = rand assert_equal(r1, r2) assert_not_equal(r1, r3) end assert("Random#bytes") do r = Random.new(10) num = 11 a = r.bytes(num) assert_kind_of String, a assert_equal num, a.bytesize b = r.bytes(num) assert_kind_of String, b assert_equal num, b.bytesize assert_not_equal a, b b = r.bytes(num / 2) assert_equal num / 2, b.bytesize end assert("return class of Kernel#rand") do assert_kind_of(Integer, rand(3)) assert_kind_of(Integer, rand(1.5)) skip unless Object.const_defined?(:Float) assert_kind_of(Float, rand) assert_kind_of(Float, rand(0.5)) end assert("Array#shuffle") do orig = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ary = orig.dup shuffled = ary.shuffle assert_equal(orig, ary) assert_not_equal(ary, shuffled) assert_equal(orig, shuffled.sort) end assert('Array#shuffle!') do orig = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ary = orig.dup assert_same(ary, ary.shuffle!) assert_not_equal(orig, ary) assert_equal(orig, ary.sort) end assert("Array#shuffle(random)") do assert_raise(TypeError) do # this will cause an exception due to the wrong argument [1, 2].shuffle(random: "Not a Random instance") end # verify that the same seed causes the same results ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] shuffled1 = ary.shuffle(random: Random.new(345)) shuffled2 = ary.shuffle(random: Random.new(345)) shuffled3 = ary.shuffle(random: Random.new(346)) assert_equal(shuffled1, shuffled2) assert_not_equal(shuffled1, shuffled3) end assert('Array#shuffle!(random)') do assert_raise(TypeError) do # this will cause an exception due to the wrong argument [1, 2].shuffle!(random: "Not a Random instance") end # verify that the same seed causes the same results ary1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ary1.shuffle!(random: Random.new(345)) ary2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ary2.shuffle!(random: Random.new(345)) ary3 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ary3.shuffle!(random: Random.new(346)) assert_equal(ary1, ary2) assert_not_equal(ary1, ary3) end assert('Array#sample') do 100.times do assert_include([0, 1, 2], [2, 1, 0].sample) [2, 1, 0].sample(2).each { |sample| assert_include([0, 1, 2], sample) } h = {} (1..10).to_a.sample(7).each do |sample| assert_not_include(h, sample) h[sample] = true end end assert_nil([].sample) assert_equal([], [].sample(1)) assert_equal([], [2, 1].sample(0)) assert_raise(TypeError) { [2, 1].sample(true) } assert_raise(ArgumentError) { [2, 1].sample(-1) } end assert('Array#sample(random)') do assert_raise(TypeError) do # this will cause an exception due to the wrong argument [1, 2].sample(2, random: "Not a Random instance") end # verify that the same seed causes the same results ary = (1..10).to_a srand(15) samples1 = ary.sample(4) samples2 = ary.sample(4, random: Random.new(15)) samples3 = ary.sample(4, random: Random.new(16)) assert_equal(samples1, samples2) assert_not_equal(samples1, samples3) end assert("Kernel#rand()") do 100.times { assert_include(0.0..1.0, rand) assert_include(0...100, rand(0...100)) assert_include(0...100, rand(100)) } assert_equal(rand(0...0), nil) assert_equal(rand(0.0...0), nil) assert_equal(rand(0...0.0), nil) assert_equal(rand(0.0...0.0), nil) assert_equal(rand(1..0), nil) end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-random/PaxHeaders/src0000644000000000000000000000013215077107334022450 xustar0030 mtime=1761382108.632301734 30 atime=1761382109.796298369 30 ctime=1761382108.632301734 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-random/src/0000755000175100017510000000000015077107334023115 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-random/src/PaxHeaders/random.c0000644000000000000000000000013215077107276024156 xustar0030 mtime=1761382078.125420543 30 atime=1761382080.144411306 30 ctime=1761382108.632301734 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-random/src/random.c0000644000175100017510000002721115077107276024551 0ustar00runnerrunner/* ** random.c - Random module ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include /* Written in 2019 by David Blackman and Sebastiano Vigna (vigna@acm.org) To the extent possible under law, the author has dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty. See . */ /* This is xoshiro128++ 1.0, one of our 32-bit all-purpose, rock-solid generators. It has excellent speed, a state size (128 bits) that is large enough for mild parallelism, and it passes all tests we are aware of. For generating just single-precision (i.e., 32-bit) floating-point numbers, xoshiro128+ is even faster. The state must be seeded so that it is not everywhere zero. */ #ifdef MRB_32BIT # define XORSHIFT96 # define NSEEDS 3 # define SEEDPOS 2 #else # define NSEEDS 4 # define SEEDPOS 0 #endif #define LASTSEED (NSEEDS-1) typedef struct rand_state { uint32_t seed[NSEEDS]; } rand_state; static void rand_init(rand_state *t) { t->seed[0] = 123456789; t->seed[1] = 362436069; t->seed[2] = 521288629; #ifndef XORSHIFT96 t->seed[3] = 88675123; #endif } static uint32_t rand_uint32(rand_state *state); static uint32_t rand_seed(rand_state *t, uint32_t seed) { uint32_t old_seed = t->seed[SEEDPOS]; rand_init(t); t->seed[SEEDPOS] = seed; for (int i = 0; i < 10; i++) { rand_uint32(t); } return old_seed; } #ifndef XORSHIFT96 static inline uint32_t rotl(const uint32_t x, int k) { return (x << k) | (x >> (32 - k)); } #endif static uint32_t rand_uint32(rand_state *state) { #ifdef XORSHIFT96 uint32_t *seed = state->seed; uint32_t x = seed[0]; uint32_t y = seed[1]; uint32_t z = seed[2]; uint32_t t = (x ^ (x << 3)) ^ (y ^ (y >> 19)) ^ (z ^ (z << 6)); x = y; y = z; z = t; seed[0] = x; seed[1] = y; seed[2] = z; return z; #else uint32_t *s = state->seed; const uint32_t result = rotl(s[0] + s[3], 7) + s[0]; const uint32_t t = s[1] << 9; s[2] ^= s[0]; s[3] ^= s[1]; s[1] ^= s[2]; s[0] ^= s[3]; s[2] ^= t; s[3] = rotl(s[3], 11); return result; #endif /* XORSHIFT96 */ } #ifndef MRB_NO_FLOAT static double rand_real(rand_state *t) { uint32_t x = rand_uint32(t); return x*(1.0/4294967296.0); } #endif static mrb_value random_rand(mrb_state *mrb, rand_state *t, mrb_int max) { if (max == 0) { #ifndef MRB_NO_FLOAT return mrb_float_value(mrb, rand_real(t)); #else max = 100; #endif } return mrb_int_value(mrb, rand_uint32(t) % max); } static mrb_int rand_i(rand_state *t, mrb_int max) { return rand_uint32(t) % max; } static mrb_value rand_range_int(mrb_state *mrb, rand_state *t, mrb_int begin, mrb_int end, mrb_bool excl) { mrb_int span = end - begin + (excl ? 0 : 1); if (span <= 0) return mrb_nil_value(); return mrb_int_value(mrb, (rand_i(t, span)) + begin); } #ifndef MRB_NO_FLOAT static mrb_value rand_range_float(mrb_state *mrb, rand_state *t, mrb_float begin, mrb_float end, mrb_bool excl) { mrb_float span = end - begin + (excl ? 0.0 : 1.0); if (span <= 0.0) return mrb_nil_value(); return mrb_float_value(mrb, rand_real(t) * span + begin); } #endif static mrb_noreturn void range_error(mrb_state *mrb, mrb_value v) { mrb_raisef(mrb, E_TYPE_ERROR, "no implicit conversion of %Y into Integer", v); } static mrb_value random_range(mrb_state *mrb, rand_state *t, mrb_value rv) { struct RRange *r = mrb_range_ptr(mrb, rv); if (mrb_integer_p(RANGE_BEG(r)) && mrb_integer_p(RANGE_END(r))) { return rand_range_int(mrb, t, mrb_integer(RANGE_BEG(r)), mrb_integer(RANGE_END(r)), RANGE_EXCL(r)); } #define cast_to_float(v) \ (mrb_float_p(v) ? mrb_float(v) \ : mrb_integer_p(v) ? (mrb_float)mrb_integer(v) \ : (range_error(mrb, v), 0.0)) return rand_range_float(mrb, t, cast_to_float(RANGE_BEG(r)), cast_to_float(RANGE_END(r)), RANGE_EXCL(r)); #undef cast_to_float } static mrb_value random_rand_impl(mrb_state *mrb, rand_state *t, mrb_value self) { mrb_value arg; if (mrb_get_args(mrb, "|o", &arg) == 0) { return random_rand(mrb, t, 0); } if (mrb_float_p(arg)) { return random_rand(mrb, t, (mrb_int)mrb_float(arg)); } if (mrb_integer_p(arg)) { return random_rand(mrb, t, mrb_integer(arg)); } if (mrb_range_p(arg)) { return random_range(mrb, t, arg); } range_error(mrb, arg); } #define ID_RANDOM MRB_SYM(mruby_Random) static mrb_value random_default(mrb_state *mrb) { struct RClass *c = mrb_class_get_id(mrb, ID_RANDOM); mrb_value d = mrb_iv_get(mrb, mrb_obj_value(c), ID_RANDOM); if (!mrb_obj_is_kind_of(mrb, d, c)) { mrb_raise(mrb, E_RUNTIME_ERROR, "[BUG] default Random replaced"); } return d; } #define random_ptr(v) (rand_state*)mrb_istruct_ptr(v) #define random_default_state(mrb) random_ptr(random_default(mrb)) static mrb_value random_m_init(mrb_state *mrb, mrb_value self) { mrb_int seed; rand_state *t = random_ptr(self); if (mrb_get_args(mrb, "|i", &seed) == 0) { rand_init(t); } else { rand_seed(t, (uint32_t)seed); } return self; } static mrb_value random_m_rand(mrb_state *mrb, mrb_value self) { rand_state *t = random_ptr(self); return random_rand_impl(mrb, t, self); } static mrb_value random_m_srand(mrb_state *mrb, mrb_value self) { uint32_t seed; mrb_int i; rand_state *t = random_ptr(self); if (mrb_get_args(mrb, "|i", &i) == 0) { seed = (uint32_t)time(NULL) ^ rand_uint32(t) ^ (uint32_t)(uintptr_t)t; } else { seed = (uint32_t)i; } uint32_t old_seed = rand_seed(t, seed); return mrb_int_value(mrb, (mrb_int)old_seed); } static mrb_value random_m_bytes(mrb_state *mrb, mrb_value self) { rand_state *t = random_ptr(self); mrb_int i = mrb_as_int(mrb, mrb_get_arg1(mrb)); mrb_value bytes = mrb_str_new(mrb, NULL, i); uint8_t *p = (uint8_t*)RSTRING_PTR(bytes); for (; i > 0; i--, p++) { *p = (uint8_t)rand_uint32(t); } return bytes; } static rand_state* check_random_arg(mrb_state *mrb, mrb_value r) { struct RClass *c = mrb_class_get_id(mrb, ID_RANDOM); rand_state *random; if (mrb_undef_p(r)) { random = random_default_state(mrb); } else if (mrb_istruct_p(r) && mrb_obj_is_kind_of(mrb, r, c)){ random = (rand_state*)mrb_istruct_ptr(r); } else { mrb_raise(mrb, E_TYPE_ERROR, "Random object required"); } return random; } /* * call-seq: * ary.shuffle! -> ary * * Shuffles elements in self in place. */ static mrb_value mrb_ary_shuffle_bang(mrb_state *mrb, mrb_value ary) { if (RARRAY_LEN(ary) > 1) { mrb_sym kname = MRB_SYM(random); mrb_value r; const mrb_kwargs kw = {1, 0, &kname, &r, NULL}; mrb_get_args(mrb, ":", &kw); rand_state *random = check_random_arg(mrb, r); mrb_ary_modify(mrb, mrb_ary_ptr(ary)); for (mrb_int i = RARRAY_LEN(ary) - 1; i > 0; i--) { mrb_value *ptr = RARRAY_PTR(ary); mrb_int j = rand_i(random, i + 1); mrb_value tmp = ptr[i]; ptr[i] = ptr[j]; ptr[j] = tmp; } } return ary; } /* * call-seq: * ary.shuffle -> new_ary * * Returns a new array with elements of self shuffled. */ static mrb_value mrb_ary_shuffle(mrb_state *mrb, mrb_value ary) { mrb_value new_ary = mrb_ary_new_from_values(mrb, RARRAY_LEN(ary), RARRAY_PTR(ary)); mrb_ary_shuffle_bang(mrb, new_ary); return new_ary; } /* * call-seq: * ary.sample -> obj * ary.sample(n) -> new_ary * * Choose a random element or +n+ random elements from the array. * * The elements are chosen by using random and unique indices into the array * in order to ensure that an element doesn't repeat itself unless the array * already contained duplicate elements. * * If the array is empty the first form returns +nil+ and the second form * returns an empty array. */ static mrb_value mrb_ary_sample(mrb_state *mrb, mrb_value ary) { mrb_int n = 0; mrb_bool given; mrb_sym kname = MRB_SYM(random); mrb_value r; const mrb_kwargs kw = {1, 0, &kname, &r, NULL}; mrb_get_args(mrb, "|i?:", &n, &given, &kw); rand_state *random = check_random_arg(mrb, r); mrb_int len = RARRAY_LEN(ary); if (!given) { /* pick one element */ switch (len) { case 0: return mrb_nil_value(); case 1: return RARRAY_PTR(ary)[0]; default: return RARRAY_PTR(ary)[rand_i(random, len)]; } } else { if (n < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "negative sample number"); if (n > len) n = len; mrb_value result = mrb_ary_new_capa(mrb, n); for (mrb_int i=0; iarray_class; mrb_static_assert(sizeof(rand_state) <= ISTRUCT_DATA_SIZE); mrb_define_private_method_id(mrb, mrb->kernel_module, MRB_SYM(rand), random_f_rand, MRB_ARGS_OPT(1)); mrb_define_private_method_id(mrb, mrb->kernel_module, MRB_SYM(srand), random_f_srand, MRB_ARGS_OPT(1)); struct RClass *random = mrb_define_class_id(mrb, MRB_SYM(Random), mrb->object_class); mrb_const_set(mrb, mrb_obj_value(mrb->object_class), ID_RANDOM, mrb_obj_value(random)); // for class check MRB_SET_INSTANCE_TT(random, MRB_TT_ISTRUCT); mrb_define_class_method_id(mrb, random, MRB_SYM(rand), random_f_rand, MRB_ARGS_OPT(1)); mrb_define_class_method_id(mrb, random, MRB_SYM(srand), random_f_srand, MRB_ARGS_OPT(1)); mrb_define_class_method_id(mrb, random, MRB_SYM(bytes), random_f_bytes, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, random, MRB_SYM(initialize), random_m_init, MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, random, MRB_SYM(rand), random_m_rand, MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, random, MRB_SYM(srand), random_m_srand, MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, random, MRB_SYM(bytes), random_m_bytes, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, array, MRB_SYM(shuffle), mrb_ary_shuffle, MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, array, MRB_SYM_B(shuffle), mrb_ary_shuffle_bang, MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, array, MRB_SYM(sample), mrb_ary_sample, MRB_ARGS_OPT(2)); mrb_value d = mrb_obj_new(mrb, random, 0, NULL); rand_state *t = random_ptr(d); mrb_iv_set(mrb, mrb_obj_value(random), ID_RANDOM, d); uint32_t seed = (uint32_t)time(NULL); rand_seed(t, seed ^ (uint32_t)(uintptr_t)t); } void mrb_mruby_random_gem_final(mrb_state *mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-cmath0000644000000000000000000000013215077107334021475 xustar0030 mtime=1761382108.638301717 30 atime=1761382109.796298369 30 ctime=1761382108.638301717 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-cmath/0000755000175100017510000000000015077107334022142 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-cmath/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276023674 xustar0030 mtime=1761382078.109420616 30 atime=1761382080.128411379 30 ctime=1761382108.638301717 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-cmath/mrbgem.rake0000644000175100017510000000050715077107276024266 0ustar00runnerrunner# This `mruby-cmath` gem uses C99 _Complex features # You need C compiler that support C99+ MRuby::Gem::Specification.new('mruby-cmath') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'standard Math module with complex' spec.add_dependency 'mruby-complex', :core => 'mruby-complex' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-cmath/PaxHeaders/test0000644000000000000000000000013215077107334022454 xustar0030 mtime=1761382108.639301714 30 atime=1761382109.797298366 30 ctime=1761382108.639301714 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-cmath/test/0000755000175100017510000000000015077107334023121 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-cmath/test/PaxHeaders/cmath.rb0000644000000000000000000000013215077107276024157 xustar0030 mtime=1761382078.109420616 30 atime=1761382080.128411379 30 ctime=1761382108.640301711 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-cmath/test/cmath.rb0000644000175100017510000000243215077107276024550 0ustar00runnerrunner## # CMath Test def assert_complex(c1, c2) assert('assert_complex') do assert_float(c1.real, c2.real) assert_float(c1.imaginary, c2.imaginary) end end assert('CMath.exp') do assert_float(1.0, CMath.exp(0)) assert_complex(-1+0i, CMath.exp(Math::PI.i)) assert_complex((-1.1312043837568135+2.4717266720048188i), CMath.exp(1+2i)) end assert('CMath.log') do assert_float(0, CMath.log(1)) assert_float(3.0, CMath.log(8,2)) assert_complex((1.092840647090816-0.42078724841586035i), CMath.log(-8,-2)) end assert('CMath.sqrt') do assert_complex(Complex(0,2), CMath.sqrt(-4.0)) assert_complex(Complex(0,3), CMath.sqrt(-9.0)) end assert('CMath trigonometric_functions') do assert_complex(Math.sinh(2).i, CMath.sin(2i)) assert_complex(Math.cosh(2)+0i, CMath.cos(2i)) assert_complex(Math.tanh(2).i, CMath.tan(2i)) assert_complex(Math.sin(2).i, CMath.sinh(2i)) assert_complex(Math.cos(2)+0i, CMath.cosh(2i)) assert_complex(Math.tan(2).i, CMath.tanh(2i)) assert_complex(1+1i, CMath.sin(CMath.asin(1+1i))) assert_complex(1+1i, CMath.cos(CMath.acos(1+1i))) assert_complex(1+1i, CMath.tan(CMath.atan(1+1i))) assert_complex(1+1i, CMath.sinh(CMath.asinh(1+1i))) assert_complex(1+1i, CMath.cosh(CMath.acosh(1+1i))) assert_complex(1+1i, CMath.tanh(CMath.atanh(1+1i))) end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-cmath/PaxHeaders/src0000644000000000000000000000013215077107334022264 xustar0030 mtime=1761382108.641301708 30 atime=1761382109.797298366 30 ctime=1761382108.641301708 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-cmath/src/0000755000175100017510000000000015077107334022731 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-cmath/src/PaxHeaders/cmath.c0000644000000000000000000000013215077107276023606 xustar0030 mtime=1761382078.109420616 30 atime=1761382080.128411379 30 ctime=1761382108.641301708 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-cmath/src/cmath.c0000644000175100017510000001644715077107276024212 0ustar00runnerrunner/* ** cmath.c - Math module with complex numbers ** ** See Copyright Notice in mruby.h */ /* ** This `mruby-cmath` gem uses C99 _Complex features ** You need C compiler that support C99+ */ #include #ifdef MRB_NO_FLOAT # error CMath conflicts with 'MRB_NO_FLOAT' configuration #endif #include mrb_value mrb_complex_new(mrb_state *mrb, mrb_float real, mrb_float imag); void mrb_complex_get(mrb_state *mrb, mrb_value cpx, mrb_float*, mrb_float*); static mrb_bool cmath_get_complex(mrb_state *mrb, mrb_value c, mrb_float *r, mrb_float *i) { if (mrb_integer_p(c)) { *r = (mrb_float)mrb_integer(c); *i = 0; return FALSE; } else if (mrb_float_p(c)) { *r = mrb_float(c); *i = 0; return FALSE; } else if (mrb_type(c) == MRB_TT_COMPLEX) { mrb_complex_get(mrb, c, r, i); return TRUE; } else { mrb_raise(mrb, E_TYPE_ERROR, "Numeric required"); return FALSE; } } #ifdef MRB_USE_FLOAT32 #define F(x) x##f #else #define F(x) x #endif #if defined(_WIN32) && !defined(__MINGW32__) #ifdef MRB_USE_FLOAT32 typedef _Fcomplex mrb_complex; #define CX(r,i) _FCbuild(r,i) #else typedef _Dcomplex mrb_complex; #define CX(r,i) _Cbuild(r,i) #endif static mrb_complex CXDIVf(mrb_complex x, mrb_float y) { return CX(creal(x)/y, cimag(x)/y); } static mrb_complex CXDIVc(mrb_complex a, mrb_complex b) { mrb_float ratio, den; mrb_float abr, abi, cr, ci; if ((abr = creal(b)) < 0) abr = - abr; if ((abi = cimag(b)) < 0) abi = - abi; if (abr <= abi) { ratio = creal(b) / cimag(b); den = cimag(a) * (1 + ratio*ratio); cr = (creal(a)*ratio + cimag(a)) / den; ci = (cimag(a)*ratio - creal(a)) / den; } else { ratio = cimag(b) / creal(b); den = creal(a) * (1 + ratio*ratio); cr = (creal(a) + cimag(a)*ratio) / den; ci = (cimag(a) - creal(a)*ratio) / den; } return CX(cr, ci); } #else #if defined(__cplusplus) && \ (defined(__APPLE__) || defined(__EMSCRIPTEN__) || \ (defined(__clang__) && (defined(__FreeBSD__) || defined(__OpenBSD__)))) #ifdef MRB_USE_FLOAT32 typedef std::complex mrb_complex; #else typedef std::complex mrb_complex; #endif /* MRB_USE_FLOAT32 */ #define CX(r,i) mrb_complex(r,i) #define creal(c) c.real() #define cimag(c) c.imag() #define FC(n) F(n) #else /* cpp */ #ifdef MRB_USE_FLOAT32 typedef float _Complex mrb_complex; #else typedef double _Complex mrb_complex; #endif /* MRB_USE_FLOAT32 */ #define CX(r,i) ((r)+(i)*_Complex_I) #endif #define CXDIVf(x,y) (x)/(y) #define CXDIVc(x,y) (x)/(y) #endif #ifndef FC #define FC(n) F(c ## n) #endif #define DEF_CMATH_METHOD(name) \ static mrb_value \ cmath_ ## name(mrb_state *mrb, mrb_value self)\ {\ mrb_value z = mrb_get_arg1(mrb);\ mrb_float real, imag;\ if (cmath_get_complex(mrb, z, &real, &imag)) {\ mrb_complex c = CX(real,imag);\ c = FC(name)(c);\ return mrb_complex_new(mrb, creal(c), cimag(c));\ }\ return mrb_float_value(mrb, F(name)(real));\ } /* exp(z): return the exponential of z */ DEF_CMATH_METHOD(exp) /* log(z): return the natural logarithm of z, with branch cut along the negative real axis */ static mrb_value cmath_log(mrb_state *mrb, mrb_value self) { mrb_value z; mrb_float base; mrb_float real, imag; mrb_int n = mrb_get_args(mrb, "o|f", &z, &base); #ifndef M_E #define M_E F(exp)(1.0) #endif if (n == 1) base = M_E; if (cmath_get_complex(mrb, z, &real, &imag) || real < 0.0) { mrb_complex c = CX(real,imag); c = FC(log)(c); if (n == 2) c = CXDIVc(c, FC(log)(CX(base,0))); return mrb_complex_new(mrb, creal(c), cimag(c)); } if (n == 1) return mrb_float_value(mrb, F(log)(real)); return mrb_float_value(mrb, F(log)(real)/F(log)(base)); } /* log10(z): return the base-10 logarithm of z, with branch cut along the negative real axis */ static mrb_value cmath_log10(mrb_state *mrb, mrb_value self) { mrb_value z = mrb_get_arg1(mrb); mrb_float real, imag; if (cmath_get_complex(mrb, z, &real, &imag) || real < 0.0) { mrb_complex c = CX(real,imag); c = CXDIVf(FC(log)(c),log(10)); return mrb_complex_new(mrb, creal(c), cimag(c)); } return mrb_float_value(mrb, F(log10)(real)); } /* log2(z): return the base-2 logarithm of z, with branch cut along the negative real axis */ static mrb_value cmath_log2(mrb_state *mrb, mrb_value self) { mrb_value z = mrb_get_arg1(mrb); mrb_float real, imag; if (cmath_get_complex(mrb, z, &real, &imag) || real < 0.0) { mrb_complex c = CX(real,imag); c = CXDIVf(FC(log)(c),log(2.0)); return mrb_complex_new(mrb, creal(c), cimag(c)); } return mrb_float_value(mrb, F(log2)(real)); } /* sqrt(z): return square root of z */ static mrb_value cmath_sqrt(mrb_state *mrb, mrb_value self) { mrb_value z = mrb_get_arg1(mrb); mrb_float real, imag; if (cmath_get_complex(mrb, z, &real, &imag) || real < 0.0) { mrb_complex c = CX(real,imag); c = FC(sqrt)(c); return mrb_complex_new(mrb, creal(c), cimag(c)); } return mrb_float_value(mrb, F(sqrt)(real)); } /* sin(z): sine function */ DEF_CMATH_METHOD(sin) /* cos(z): cosine function */ DEF_CMATH_METHOD(cos) /* tan(z): tangent function */ DEF_CMATH_METHOD(tan) /* asin(z): arc sine function */ DEF_CMATH_METHOD(asin) /* acos(z): arc cosine function */ DEF_CMATH_METHOD(acos) /* atan(z): arg tangent function */ DEF_CMATH_METHOD(atan) /* sinh(z): hyperbolic sine function */ DEF_CMATH_METHOD(sinh) /* cosh(z): hyperbolic cosine function */ DEF_CMATH_METHOD(cosh) /* tanh(z): hyperbolic tangent function */ DEF_CMATH_METHOD(tanh) /* asinh(z): inverse hyperbolic sine function */ DEF_CMATH_METHOD(asinh) /* acosh(z): inverse hyperbolic cosine function */ DEF_CMATH_METHOD(acosh) /* atanh(z): inverse hyperbolic tangent function */ DEF_CMATH_METHOD(atanh) /* ------------------------------------------------------------------------*/ void mrb_mruby_cmath_gem_init(mrb_state* mrb) { struct RClass *cmath; cmath = mrb_define_module(mrb, "CMath"); mrb_include_module(mrb, cmath, mrb_module_get(mrb, "Math")); mrb_define_module_function(mrb, cmath, "sin", cmath_sin, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, cmath, "cos", cmath_cos, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, cmath, "tan", cmath_tan, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, cmath, "asin", cmath_asin, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, cmath, "acos", cmath_acos, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, cmath, "atan", cmath_atan, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, cmath, "sinh", cmath_sinh, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, cmath, "cosh", cmath_cosh, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, cmath, "tanh", cmath_tanh, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, cmath, "asinh", cmath_asinh, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, cmath, "acosh", cmath_acosh, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, cmath, "atanh", cmath_atanh, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, cmath, "exp", cmath_exp, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, cmath, "log", cmath_log, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_module_function(mrb, cmath, "log2", cmath_log2, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, cmath, "log10", cmath_log10, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, cmath, "sqrt", cmath_sqrt, MRB_ARGS_REQ(1)); } void mrb_mruby_cmath_gem_final(mrb_state* mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-complex0000644000000000000000000000013215077107334022050 xustar0030 mtime=1761382108.842301127 30 atime=1761382109.797298366 30 ctime=1761382108.842301127 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-complex/0000755000175100017510000000000015077107334022515 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-complex/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024247 xustar0030 mtime=1761382078.114420593 30 atime=1761382080.137411338 30 ctime=1761382108.842301127 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-complex/mrbgem.rake0000644000175100017510000000037215077107276024641 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-complex') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Complex class' spec.build.defines << "MRB_USE_COMPLEX" spec.add_dependency 'mruby-math', core: 'mruby-math' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-complex/PaxHeaders/mrblib0000644000000000000000000000013215077107334023317 xustar0030 mtime=1761382108.845301118 30 atime=1761382109.797298366 30 ctime=1761382108.845301118 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-complex/mrblib/0000755000175100017510000000000015077107334023764 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-complex/mrblib/PaxHeaders/complex.rb0000644000000000000000000000013215077107276025375 xustar0030 mtime=1761382078.115420588 30 atime=1761382080.137411338 30 ctime=1761382108.845301118 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-complex/mrblib/complex.rb0000644000175100017510000000245215077107276025770 0ustar00runnerrunnerclass Complex < Numeric def self.polar(abs, arg = 0) Complex(abs * Math.cos(arg), abs * Math.sin(arg)) end def inspect "(#{to_s})" end def to_s "#{real}#{'+' unless imaginary < 0}#{imaginary}#{'*' unless imaginary.finite?}i" end def +@ self end def -@ Complex(-real, -imaginary) end def <=>(other) return nil unless other.kind_of?(Numeric) self.to_f <=> other.to_f rescue nil end def abs Math.hypot imaginary, real end alias_method :magnitude, :abs def abs2 real * real + imaginary * imaginary end def arg Math.atan2 imaginary, real end alias_method :angle, :arg alias_method :phase, :arg def conjugate Complex(real, -imaginary) end alias_method :conj, :conjugate def fdiv(numeric) Complex(real / numeric, imaginary / numeric) end def polar [abs, arg] end def real? false end def rectangular [real, imaginary] end alias_method :rect, :rectangular def to_c self end def to_r raise RangeError.new "can't convert #{to_s} into Rational" unless imaginary.zero? Rational(real, 1) end alias_method :imag, :imaginary Numeric.class_eval do def i Complex(0, self) end end undef i end class Numeric def to_c Complex(self, 0) end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-complex/PaxHeaders/test0000644000000000000000000000013215077107334023027 xustar0030 mtime=1761382108.843301124 30 atime=1761382109.797298366 30 ctime=1761382108.843301124 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-complex/test/0000755000175100017510000000000015077107334023474 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-complex/test/PaxHeaders/complex.rb0000644000000000000000000000013215077107276025105 xustar0030 mtime=1761382078.115420588 30 atime=1761382080.137411338 30 ctime=1761382108.843301124 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-complex/test/complex.rb0000644000175100017510000001154415077107276025502 0ustar00runnerrunnerdef assert_complex(real, exp) assert "assert_complex" do assert_float real.real, exp.real assert_float real.imaginary, exp.imaginary end end assert 'Complex' do c = 123i assert_equal Complex, c.class assert_equal [c.real, c.imaginary], [0, 123] c = 123 + -1.23i assert_equal Complex, c.class assert_equal [c.real, c.imaginary], [123, -1.23] end assert 'Complex::polar' do assert_complex Complex.polar(3, 0), (3 + 0i) assert_complex Complex.polar(3, Math::PI/2), (0 + 3i) assert_complex Complex.polar(3, Math::PI), (-3 + 0i) assert_complex Complex.polar(3, -Math::PI/2), (0 + -3i) end assert 'Complex::rectangular' do assert_complex Complex.rectangular(1, 2), (1 + 2i) end assert 'Complex#*' do assert_complex Complex(2, 3) * Complex(2, 3), (-5 + 12i) assert_complex Complex(900) * Complex(1), (900 + 0i) assert_complex Complex(-2, 9) * Complex(-9, 2), (0 - 85i) assert_complex Complex(9, 8) * 4, (36 + 32i) assert_complex Complex(20, 9) * 9.8, (196.0 + 88.2i) assert_complex 4 * Complex(9, 8), (36 + 32i) assert_complex 9.8 * Complex(20, 9), (196.0 + 88.2i) end assert 'Complex#+' do assert_complex Complex(2, 3) + Complex(2, 3) , (4 + 6i) assert_complex Complex(900) + Complex(1) , (901 + 0i) assert_complex Complex(-2, 9) + Complex(-9, 2), (-11 + 11i) assert_complex Complex(9, 8) + 4 , (13 + 8i) assert_complex Complex(20, 9) + 9.8 , (29.8 + 9i) assert_complex 4 + Complex(9, 8) , (13 + 8i) assert_complex 9.8 + Complex(20, 9) , (29.8 + 9i) end assert 'Complex#-' do assert_complex Complex(2, 3) - Complex(2, 3) , (0 + 0i) assert_complex Complex(900) - Complex(1) , (899 + 0i) assert_complex Complex(-2, 9) - Complex(-9, 2), (7 + 7i) assert_complex Complex(9, 8) - 4 , (5 + 8i) assert_complex Complex(20, 9) - 9.8 , (10.2 + 9i) assert_complex 4 - Complex(9, 8) , (-5 - 8i) assert_complex 10.5 - Complex(20, 9) , (-9.5 - 9i) end assert 'Complex#-@' do assert_complex((-1 - 2i), -Complex(1, 2)) end assert 'Complex#/' do assert_complex Complex(2, 3) / Complex(2, 3) , (1 + 0i) assert_complex Complex(900) / Complex(1) , (900 + 0i) assert_complex Complex(-2, 9) / Complex(-9, 2), ((36.0 / 85) - (77i / 85)) assert_complex Complex(9, 8) / 4 , ((9.0 / 4) + 2i) assert_complex Complex(20, 9) / 9.8 , (2.0408163265306123 + 0.9183673469387754i) assert_complex 4 / Complex(9, 8) , (0.2482758620689655 - 0.2206896551724138i) assert_complex 9.8 / Complex(20, 9) , (0.4074844074844075 - 0.1833679833679834i) if 1e39.infinite? then # MRB_USE_FLOAT32 in effect ten = 1e21 one = 1e20 else ten = 1e201 one = 1e200 end assert_complex Complex(ten, ten) / Complex(one, one), Complex(10.0, 0.0) end assert 'Complex#==' do assert_true Complex(2, 3) == Complex(2, 3) assert_true Complex(5) == 5 assert_true Complex(0) == 0.0 assert_true 5 == Complex(5) assert_true 0.0 == Complex(0) end assert 'Complex#abs' do assert_float Complex(-1).abs, 1 assert_float Complex(3.0, -4.0).abs, 5.0 if 1e39.infinite? then # MRB_USE_FLOAT32 in effect exp = 125 else exp = 1021 end assert_true Complex(3.0*2.0**exp, 4.0*2.0**exp).abs.finite? assert_float Complex(3.0*2.0**exp, 4.0*2.0**exp).abs, 5.0*2.0**exp end assert 'Complex#abs2' do assert_float Complex(-1).abs2, 1 assert_float Complex(3.0, -4.0).abs2, 25.0 end assert 'Complex#arg' do assert_float Complex.polar(3, Math::PI/2).arg, 1.5707963267948966 end assert 'Complex#conjugate' do assert_complex Complex(1, 2).conjugate, (1 - 2i) end assert 'Complex#fdiv' do assert_complex Complex(11, 22).fdiv(3), (3.6666666666666665 + 7.333333333333333i) end assert 'Complex#imaginary' do assert_float Complex(7).imaginary , 0 assert_float Complex(9, -4).imaginary, -4 end assert 'Complex#polar' do assert_equal Complex(1, 2).polar, [2.23606797749979, 1.1071487177940904] end assert 'Complex#real' do assert_float Complex(7).real, 7 assert_float Complex(9, -4).real, 9 end assert 'Complex#real?' do assert_false Complex(1).real? end assert 'Complex::rectangular' do assert_equal Complex(1, 2).rectangular, [1, 2] end assert 'Complex::to_c' do assert_equal Complex(1, 2).to_c, Complex(1, 2) end assert 'Complex::to_f' do assert_float Complex(1, 0).to_f, 1.0 assert_raise(RangeError) do Complex(1, 2).to_f end end assert 'Complex::to_i' do assert_equal Complex(1, 0).to_i, 1 assert_raise(RangeError) do Complex(1, 2).to_i end end assert 'Complex#frozen?' do assert_predicate(1i, :frozen?) assert_predicate(Complex(2,3), :frozen?) assert_predicate(4+5i, :frozen?) end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-complex/PaxHeaders/src0000644000000000000000000000013215077107334022637 xustar0030 mtime=1761382108.846301115 30 atime=1761382109.797298366 30 ctime=1761382108.846301115 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-complex/src/0000755000175100017510000000000015077107334023304 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-complex/src/PaxHeaders/complex.c0000644000000000000000000000013215077107276024534 xustar0030 mtime=1761382078.115420588 30 atime=1761382080.137411338 30 ctime=1761382108.846301115 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-complex/src/complex.c0000644000175100017510000002442015077107276025126 0ustar00runnerrunner#include #include #include #include #include #ifdef MRB_NO_FLOAT # error Complex conflicts with 'MRB_NO_FLOAT' configuration #endif #ifdef MRB_USE_FLOAT32 #define F(x) x##f #else #define F(x) x #endif struct mrb_complex { mrb_float real; mrb_float imaginary; }; #if defined(MRB_32BIT) && !defined(MRB_USE_FLOAT32) struct RComplex { MRB_OBJECT_HEADER; struct mrb_complex *p; }; static struct mrb_complex* complex_ptr(mrb_state *mrb, mrb_value v) { struct RComplex *r = (struct RComplex*)mrb_obj_ptr(v); if (!r->p) { mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized complex"); } return r->p; } #else #define COMPLEX_INLINE struct RComplex { MRB_OBJECT_HEADER; struct mrb_complex r; }; #define complex_ptr(mrb, v) (&((struct RComplex*)mrb_obj_ptr(v))->r) #endif mrb_static_assert_object_size(struct RComplex); static struct RBasic* complex_alloc(mrb_state *mrb, struct RClass *c, struct mrb_complex **p) { struct RComplex *s; s = MRB_OBJ_ALLOC(mrb, MRB_TT_COMPLEX, c); #ifdef COMPLEX_INLINE *p = &s->r; #else *p = s->p = (struct mrb_complex*)mrb_malloc(mrb, sizeof(struct mrb_complex)); #endif return (struct RBasic*)s; } void mrb_complex_get(mrb_state *mrb, mrb_value cpx, mrb_float *r, mrb_float *i) { struct mrb_complex *c = complex_ptr(mrb, cpx); *r = c->real; *i = c->imaginary; } mrb_value mrb_complex_new(mrb_state *mrb, mrb_float real, mrb_float imaginary) { struct RClass *c = mrb_class_get_id(mrb, MRB_SYM(Complex)); struct mrb_complex *p; struct RBasic *comp = complex_alloc(mrb, c, &p); p->real = real; p->imaginary = imaginary; comp->frozen = 1; return mrb_obj_value(comp); } #define complex_new(mrb, real, imag) mrb_complex_new(mrb, real, imag) void mrb_complex_copy(mrb_state *mrb, mrb_value x, mrb_value y) { struct mrb_complex *p1 = complex_ptr(mrb, x); struct mrb_complex *p2 = complex_ptr(mrb, y); p1->real = p2->real; p1->imaginary = p2->imaginary; } static mrb_value complex_real(mrb_state *mrb, mrb_value self) { struct mrb_complex *p = complex_ptr(mrb, self); return mrb_float_value(mrb, p->real); } static mrb_value complex_imaginary(mrb_state *mrb, mrb_value self) { struct mrb_complex *p = complex_ptr(mrb, self); return mrb_float_value(mrb, p->imaginary); } static mrb_value complex_s_rect(mrb_state *mrb, mrb_value self) { mrb_float real, imaginary = 0.0; mrb_get_args(mrb, "f|f", &real, &imaginary); return complex_new(mrb, real, imaginary); } mrb_value mrb_complex_to_f(mrb_state *mrb, mrb_value self) { struct mrb_complex *p = complex_ptr(mrb, self); if (p->imaginary != 0) { mrb_raisef(mrb, E_RANGE_ERROR, "can't convert %v into Float", self); } return mrb_float_value(mrb, p->real); } mrb_value mrb_complex_to_i(mrb_state *mrb, mrb_value self) { struct mrb_complex *p = complex_ptr(mrb, self); #ifdef MRB_USE_BIGINT if (p->imaginary != 0) { mrb_raisef(mrb, E_RANGE_ERROR, "can't convert %v into Integer", self); } if (!FIXABLE_FLOAT(p->real)) { return mrb_bint_new_float(mrb, p->real); } #else if (p->imaginary != 0 || !FIXABLE_FLOAT(p->real)) { mrb_raisef(mrb, E_RANGE_ERROR, "can't convert %v into Integer", self); } #endif return mrb_int_value(mrb, (mrb_int)p->real); } mrb_bool mrb_complex_eq(mrb_state *mrb, mrb_value x, mrb_value y) { struct mrb_complex *p1 = complex_ptr(mrb, x); switch (mrb_type(y)) { case MRB_TT_COMPLEX: { struct mrb_complex *p2 = complex_ptr(mrb, y); if (p1->real == p2->real && p1->imaginary == p2->imaginary) { return TRUE; } return FALSE; } case MRB_TT_INTEGER: if (p1->imaginary != 0) return FALSE; return p1->real == mrb_integer(y); case MRB_TT_FLOAT: if (p1->imaginary != 0) return FALSE; return p1->real == mrb_float(y); default: return mrb_equal(mrb, y, x); } } static mrb_value complex_eq(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); return mrb_bool_value(mrb_complex_eq(mrb, x, y)); } mrb_value mrb_complex_add(mrb_state *mrb, mrb_value x, mrb_value y) { struct mrb_complex *p1 = complex_ptr(mrb, x); switch (mrb_type(y)) { case MRB_TT_COMPLEX: { struct mrb_complex *p2 = complex_ptr(mrb, y); return mrb_complex_new(mrb, p1->real+p2->real, p1->imaginary+p2->imaginary); } default: { mrb_float z = mrb_as_float(mrb, y); return mrb_complex_new(mrb, p1->real+z, p1->imaginary); } } } static mrb_value complex_add(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); return mrb_complex_add(mrb, x, y); } mrb_value mrb_complex_sub(mrb_state *mrb, mrb_value x, mrb_value y) { struct mrb_complex *p1 = complex_ptr(mrb, x); switch (mrb_type(y)) { case MRB_TT_COMPLEX: { struct mrb_complex *p2 = complex_ptr(mrb, y); return mrb_complex_new(mrb, p1->real-p2->real, p1->imaginary-p2->imaginary); } default: { mrb_float z = mrb_as_float(mrb, y); return mrb_complex_new(mrb, p1->real-z, p1->imaginary); } } } static mrb_value complex_sub(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); return mrb_complex_sub(mrb, x, y); } mrb_value mrb_complex_mul(mrb_state *mrb, mrb_value x, mrb_value y) { struct mrb_complex *p1 = complex_ptr(mrb, x); switch (mrb_type(y)) { case MRB_TT_COMPLEX: { struct mrb_complex *p2 = complex_ptr(mrb, y); return mrb_complex_new(mrb, p1->real*p2->real - p1->imaginary*p2->imaginary, p1->real*p2->imaginary + p2->real*p1->imaginary); } default: { mrb_float z = mrb_as_float(mrb, y); return mrb_complex_new(mrb, p1->real*z, p1->imaginary*z); } } } static mrb_value complex_mul(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); return mrb_complex_mul(mrb, x, y); } /* Arithmetic on (significand, exponent) pairs avoids premature overflow in complex division */ struct float_pair { mrb_float s; int x; }; static void add_pair(struct float_pair *s, struct float_pair const *a, struct float_pair const *b) { if (b->s == 0.0F) { *s = *a; } else if (a->s == 0.0F) { *s = *b; } else if (a->x >= b->x) { s->s = a->s + F(ldexp)(b->s, b->x - a->x); s->x = a->x; } else { s->s = F(ldexp)(a->s, a->x - b->x) + b->s; s->x = b->x; } } static void mul_pair(struct float_pair *p, struct float_pair const *a, struct float_pair const *b) { p->s = a->s * b->s; p->x = a->x + b->x; } static void div_pair(struct float_pair *q, struct float_pair const *a, struct float_pair const *b) { q->s = mrb_div_float(a->s, b->s); q->x = a->x - b->x; } mrb_value mrb_complex_div(mrb_state *mrb, mrb_value self, mrb_value rhs) { struct mrb_complex *a, *b; a = complex_ptr(mrb, self); if (mrb_type(rhs) != MRB_TT_COMPLEX) { if (mrb_integer_p(rhs) && mrb_integer(rhs) == 0) { mrb_int_zerodiv(mrb); } mrb_float f = mrb_as_float(mrb, rhs); return complex_new(mrb, mrb_div_float(a->real, f), mrb_div_float(a->imaginary, f)); } struct float_pair ar, ai, br, bi; struct float_pair br2, bi2; struct float_pair div; struct float_pair ar_br, ai_bi; struct float_pair ai_br, ar_bi; struct float_pair zr, zi; b = complex_ptr(mrb, rhs); /* Split floating-point components into significand and exponent */ ar.s = F(frexp)(a->real, &ar.x); ai.s = F(frexp)(a->imaginary, &ai.x); br.s = F(frexp)(b->real, &br.x); bi.s = F(frexp)(b->imaginary, &bi.x); /* Perform arithmetic on (significand, exponent) pairs to produce the result: */ /* the divisor */ mul_pair(&br2, &br, &br); mul_pair(&bi2, &bi, &bi); add_pair(&div, &br2, &bi2); /* real component */ mul_pair(&ar_br, &ar, &br); mul_pair(&ai_bi, &ai, &bi); add_pair(&zr, &ar_br, &ai_bi); div_pair(&zr, &zr, &div); /* imaginary component */ mul_pair(&ai_br, &ai, &br); mul_pair(&ar_bi, &ar, &bi); ar_bi.s = -ar_bi.s; add_pair(&zi, &ai_br, &ar_bi); div_pair(&zi, &zi, &div); /* assemble the result */ return complex_new(mrb, F(ldexp)(zr.s, zr.x), F(ldexp)(zi.s, zi.x)); } static mrb_value complex_div(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); return mrb_complex_div(mrb, x, y); } static mrb_value complex_hash(mrb_state *mrb, mrb_value cpx) { struct mrb_complex *c = complex_ptr(mrb, cpx); uint32_t hash = mrb_byte_hash((uint8_t*)&c->real, sizeof(mrb_float)); hash = mrb_byte_hash_step((uint8_t*)&c->imaginary, sizeof(mrb_float), hash); return mrb_int_value(mrb, hash); } static mrb_value nil_to_c(mrb_state *mrb, mrb_value self) { return complex_new(mrb, 0, 0); } void mrb_mruby_complex_gem_init(mrb_state *mrb) { struct RClass *comp; comp = mrb_define_class_id(mrb, MRB_SYM(Complex), mrb_class_get_id(mrb, MRB_SYM(Numeric))); MRB_SET_INSTANCE_TT(comp, MRB_TT_COMPLEX); MRB_UNDEF_ALLOCATOR(comp); mrb_undef_class_method_id(mrb, comp, MRB_SYM(new)); mrb_define_class_method_id(mrb, comp, MRB_SYM(rectangular), complex_s_rect, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_class_method_id(mrb, comp, MRB_SYM(rect), complex_s_rect, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_private_method_id(mrb, mrb->kernel_module, MRB_SYM(Complex), complex_s_rect, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, comp, MRB_SYM(real), complex_real, MRB_ARGS_NONE()); mrb_define_method_id(mrb, comp, MRB_SYM(imaginary), complex_imaginary, MRB_ARGS_NONE()); mrb_define_method_id(mrb, comp, MRB_SYM(to_f), mrb_complex_to_f, MRB_ARGS_NONE()); mrb_define_method_id(mrb, comp, MRB_SYM(to_i), mrb_complex_to_i, MRB_ARGS_NONE()); mrb_define_method_id(mrb, comp, MRB_SYM(to_c), mrb_obj_itself, MRB_ARGS_NONE()); mrb_define_method_id(mrb, comp, MRB_OPSYM(add), complex_add, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, comp, MRB_OPSYM(sub), complex_sub, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, comp, MRB_OPSYM(mul), complex_mul, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, comp, MRB_OPSYM(div), complex_div, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, comp, MRB_SYM(quo), complex_div, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, comp, MRB_OPSYM(eq), complex_eq, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, comp, MRB_SYM(hash), complex_hash, MRB_ARGS_NONE()); mrb_define_method_id(mrb, mrb->nil_class, MRB_SYM(to_c), nil_to_c, MRB_ARGS_NONE()); } void mrb_mruby_complex_gem_final(mrb_state* mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-metaprog0000644000000000000000000000013015077107334022215 xustar0029 mtime=1761382108.77530132 30 atime=1761382109.797298366 29 ctime=1761382108.77530132 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-metaprog/0000755000175100017510000000000015077107334022664 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-metaprog/PaxHeaders/mrbgem.rake0000644000000000000000000000013115077107276024415 xustar0030 mtime=1761382078.121420561 30 atime=1761382080.142411316 29 ctime=1761382108.77530132 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-metaprog/mrbgem.rake0000644000175100017510000000026015077107276025004 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-metaprog') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Meta-programming features for mruby' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-metaprog/PaxHeaders/test0000644000000000000000000000013215077107334023176 xustar0030 mtime=1761382108.776301318 30 atime=1761382109.797298366 30 ctime=1761382108.776301318 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-metaprog/test/0000755000175100017510000000000015077107334023643 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-metaprog/test/PaxHeaders/metaprog.rb0000644000000000000000000000013215077107276025423 xustar0030 mtime=1761382078.122420556 30 atime=1761382080.142411316 30 ctime=1761382108.776301318 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-metaprog/test/metaprog.rb0000644000175100017510000003114015077107276026012 0ustar00runnerrunnerassert('Kernel#send', '15.3.1.3.44') do # test with block l = send(:lambda) do true end assert_true l.call assert_equal l.class, Proc # test with argument assert_true send(:respond_to?, :nil?) # test without argument and without block assert_equal send(:to_s).class, String end assert('Kernel#instance_variable_defined?', '15.3.1.3.20') do o = Object.new o.instance_variable_set(:@a, 1) assert_true o.instance_variable_defined?("@a") assert_false o.instance_variable_defined?("@b") assert_true o.instance_variable_defined?("@a"[0,2]) assert_true o.instance_variable_defined?("@abc"[0,2]) assert_raise(NameError) { o.instance_variable_defined?("@0") } end assert('Kernel#instance_variable_get', '15.3.1.3.21') do o = Class.new { attr_accessor :foo, :bar }.new o.foo = "one" o.bar = 2 assert_equal("one", o.instance_variable_get(:@foo)) assert_equal(2, o.instance_variable_get("@bar")) assert_equal(nil, o.instance_variable_get(:@baz)) %w[foo @1].each do |n| assert_raise(NameError) { o.instance_variable_get(n) } end end assert('Kernel#instance_variable_set', '15.3.1.3.22') do o = Class.new { attr_reader :foo, :_bar }.new assert_equal("one", o.instance_variable_set(:@foo, "one")) assert_equal("one", o.foo) assert_equal(2, o.instance_variable_set("@_bar", 2)) assert_equal(2, o._bar) %w[@6 @% @@a @ a].each do |n| assert_raise(NameError) { o.instance_variable_set(n, 1) } end assert_raise(FrozenError) { o.freeze.instance_variable_set(:@a, 2) } assert_raise(FrozenError, ArgumentError) { nil.instance_variable_set(:@a, 2) } end assert('Kernel#instance_variables', '15.3.1.3.23') do o = Object.new o.instance_eval do @a = 11 @b = 12 end ivars = o.instance_variables assert_equal Array, ivars.class assert_equal(2, ivars.size) assert_true ivars.include?(:@a) assert_true ivars.include?(:@b) end assert('Kernel#methods', '15.3.1.3.31') do assert_equal Array, methods.class assert_equal [:foo], Class.new{def self.foo; end}.methods(false) assert_equal [], Class.new{}.methods(false) end assert('Kernel#private_methods', '15.3.1.3.36') do assert_equal Array, private_methods.class c = Class.new do private def foo end end assert_equal [:foo], c.new.private_methods(false) end assert('Kernel#protected_methods', '15.3.1.3.37') do assert_equal Array, protected_methods.class c = Class.new do protected def foo end end assert_equal [:foo], c.new.protected_methods(false) end assert('Kernel#public_methods', '15.3.1.3.38') do assert_equal Array, public_methods.class c = Class.new do def foo end end assert_equal [:foo], c.new.public_methods(false) end assert('Kernel#singleton_methods', '15.3.1.3.45') do assert_equal singleton_methods.class, Array end assert('Kernel#global_variables', '15.3.1.3.14') do variables1 = global_variables assert_equal Array, variables1.class assert_not_include(variables1, :$kernel_global_variables_test) $kernel_global_variables_test = nil variables2 = global_variables assert_include(variables2, :$kernel_global_variables_test) assert_equal(1, variables2.size - variables1.size) end # Kernel.global_variables is not provided by mruby. '15.3.1.2.4' assert('Kernel#local_variables', '15.3.1.3.28') do assert_equal Array, local_variables.class def local_var_list a = "hello" local_variables end assert_equal [:a], local_var_list end # Kernel.local_variables is not provided by mruby. '15.3.1.2.7' assert('Kernel#define_singleton_method') do o = Object.new ret = o.define_singleton_method(:test_method) do :singleton_method_ok end assert_equal :test_method, ret assert_equal :singleton_method_ok, o.test_method assert_raise(TypeError) { 2.define_singleton_method(:f){} } assert_raise(FrozenError) { [].freeze.define_singleton_method(:f){} } end assert('Kernel#singleton_class') do o1 = Object.new assert_same(o1.singleton_class, class << o1; self end) o2 = Object.new sc2 = class << o2; self end assert_same(o2.singleton_class, sc2) o3 = Object.new sc3 = o3.singleton_class o3.freeze assert_predicate(sc3, :frozen?) assert_predicate(Object.new.freeze.singleton_class, :frozen?) end def labeled_module(name, &block) Module.new do (class < #include #include #include #include #include #include #include #include #include #define MT_PUBLIC MRB_METHOD_PUBLIC_FL #define MT_PRIVATE MRB_METHOD_PRIVATE_FL #define MT_PROTECTED MRB_METHOD_PROTECTED_FL #define MT_NOPRIV (MT_PRIVATE|MT_PROTECTED) static mrb_value mrb_f_nil(mrb_state *mrb, mrb_value cv) { return mrb_nil_value(); } /* 15.3.1.3.20 */ /* * call-seq: * obj.instance_variable_defined?(symbol) -> true or false * * Returns true if the given instance variable is * defined in obj. * * class Fred * def initialize(p1, p2) * @a, @b = p1, p2 * end * end * fred = Fred.new('cat', 99) * fred.instance_variable_defined?(:@a) #=> true * fred.instance_variable_defined?("@b") #=> true * fred.instance_variable_defined?("@c") #=> false */ static mrb_value mrb_obj_ivar_defined(mrb_state *mrb, mrb_value self) { mrb_sym sym; mrb_get_args(mrb, "n", &sym); mrb_iv_name_sym_check(mrb, sym); return mrb_bool_value(mrb_iv_defined(mrb, self, sym)); } /* 15.3.1.3.21 */ /* * call-seq: * obj.instance_variable_get(symbol) -> obj * * Returns the value of the given instance variable, or nil if the * instance variable is not set. The @ part of the * variable name should be included for regular instance * variables. Throws a NameError exception if the * supplied symbol is not valid as an instance variable name. * * class Fred * def initialize(p1, p2) * @a, @b = p1, p2 * end * end * fred = Fred.new('cat', 99) * fred.instance_variable_get(:@a) #=> "cat" * fred.instance_variable_get("@b") #=> 99 */ static mrb_value mrb_obj_ivar_get(mrb_state *mrb, mrb_value self) { mrb_sym iv_name; mrb_get_args(mrb, "n", &iv_name); mrb_iv_name_sym_check(mrb, iv_name); return mrb_iv_get(mrb, self, iv_name); } /* 15.3.1.3.22 */ /* * call-seq: * obj.instance_variable_set(symbol, obj) -> obj * * Sets the instance variable names by symbol to * object, thereby frustrating the efforts of the class's * author to attempt to provide proper encapsulation. The variable * did not have to exist prior to this call. * * class Fred * def initialize(p1, p2) * @a, @b = p1, p2 * end * end * fred = Fred.new('cat', 99) * fred.instance_variable_set(:@a, 'dog') #=> "dog" * fred.instance_variable_set(:@c, 'cat') #=> "cat" * fred.inspect #=> "#" */ static mrb_value mrb_obj_ivar_set(mrb_state *mrb, mrb_value self) { mrb_sym iv_name; mrb_value val; mrb_get_args(mrb, "no", &iv_name, &val); mrb_iv_name_sym_check(mrb, iv_name); mrb_iv_set(mrb, self, iv_name, val); return val; } /* 15.3.1.2.7 */ /* 15.3.1.3.28 */ /* * call-seq: * local_variables -> array * * Returns the names of local variables in the current scope. * * [mruby limitation] * If variable symbol information was stripped out from * compiled binary files using `mruby-strip -l`, this * method always returns an empty array. */ static mrb_value mrb_local_variables(mrb_state *mrb, mrb_value self) { return mrb_proc_local_variables(mrb, mrb->c->ci[-1].proc); } KHASH_DECLARE(st, mrb_sym, char, TRUE) KHASH_DEFINE(st, mrb_sym, char, TRUE, kh_int_hash_func, kh_int_hash_equal) struct mt_set { unsigned int visibility; khash_t(st) *set; }; #define vicheck(flags, visi) (((visi)==MT_NOPRIV) ? (((flags)&0x3)!=MT_PRIVATE) : (((flags)&0x3)==(visi))) static int method_entry_i(mrb_state *mrb, mrb_sym mid, mrb_method_t m, void *p) { struct mt_set *s = (struct mt_set*)p; if (vicheck(m.flags, s->visibility) && kh_get(st, mrb, s->set, mid) == kh_end(s->set)) { khint_t k = kh_put(st, mrb, s->set, mid); kh_val(s->set, k) = !MRB_METHOD_UNDEF_P(m); } return 0; } static void method_entry_loop(mrb_state *mrb, struct RClass *klass, khash_t(st) *set, unsigned int visibility) { struct mt_set s = {visibility, set}; mrb_mt_foreach(mrb, klass, method_entry_i, (void*)&s); } static mrb_value mrb_class_instance_method_list(mrb_state *mrb, mrb_bool recur, struct RClass *klass, unsigned int flags) { mrb_value ary; khash_t(st) *set = kh_init(st, mrb); if (!recur) { if (klass->flags & MRB_FL_CLASS_IS_PREPENDED) { MRB_CLASS_ORIGIN(klass); } method_entry_loop(mrb, klass, set, flags); } else { struct RClass *oldklass = NULL; while (klass && (klass != oldklass)) { method_entry_loop(mrb, klass, set, flags); oldklass = klass; klass = klass->super; } } ary = mrb_ary_new_capa(mrb, kh_size(set)); for (khint_t i=0; i array * * Returns a list of the names of public and protected methods of * `obj`. This will include all the methods accessible in * `obj`'s ancestors. * If the optional parameter is `false`, it * returns an array of `obj`'s public and protected singleton methods, * the array will not include methods in modules included in `obj`. * * class Klass * def kMethod() * end * end * k = Klass.new * k.methods[0..9] #=> [:kMethod, :respond_to?, :nil?, :is_a?, * # :class, :instance_variable_set, * # :methods, :extend, :__send__, :instance_eval] * k.methods.length #=> 42 */ static mrb_value mrb_obj_methods_m(mrb_state *mrb, mrb_value self) { return mrb_obj_methods(mrb, self, MT_NOPRIV); } /* 15.3.1.3.36 */ /* * call-seq: * obj.private_methods(all=true) -> array * * Returns the list of private methods accessible to obj. If * the all parameter is set to false, only those methods * in the receiver will be listed. */ static mrb_value mrb_obj_private_methods(mrb_state *mrb, mrb_value self) { return mrb_obj_methods(mrb, self, MT_PRIVATE); } /* 15.3.1.3.37 */ /* * call-seq: * obj.protected_methods(all=true) -> array * * Returns the list of protected methods accessible to obj. If * the all parameter is set to false, only those methods * in the receiver will be listed. */ static mrb_value mrb_obj_protected_methods(mrb_state *mrb, mrb_value self) { return mrb_obj_methods(mrb, self, MT_PROTECTED); } /* 15.3.1.3.38 */ /* * call-seq: * obj.public_methods(all=true) -> array * * Returns the list of public methods accessible to obj. If * the all parameter is set to false, only those methods * in the receiver will be listed. */ static mrb_value mrb_obj_public_methods(mrb_state *mrb, mrb_value self) { return mrb_obj_methods(mrb, self, MT_PUBLIC); } static mrb_value mrb_obj_singleton_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj) { mrb_value ary; struct RClass *klass; khash_t(st) *set = kh_init(st, mrb); klass = mrb_class(mrb, obj); if (klass && (klass->tt == MRB_TT_SCLASS)) { method_entry_loop(mrb, klass, set, MT_PUBLIC); klass = klass->super; } if (recur) { while (klass && ((klass->tt == MRB_TT_SCLASS) || (klass->tt == MRB_TT_ICLASS))) { method_entry_loop(mrb, klass, set, MT_PUBLIC); klass = klass->super; } } ary = mrb_ary_new(mrb); for (khint_t i=0;i array * * Returns an array of the names of singleton methods for obj. * If the optional all parameter is true, the list will include * methods in modules included in obj. * Only public and protected singleton methods are returned. * * module Other * def three() end * end * * class Single * def Single.four() end * end * * a = Single.new * * def a.one() * end * * class << a * include Other * def two() * end * end * * Single.singleton_methods #=> [:four] * a.singleton_methods(false) #=> [:two, :one] * a.singleton_methods #=> [:two, :one, :three] */ static mrb_value mrb_obj_singleton_methods_m(mrb_state *mrb, mrb_value self) { mrb_bool recur = TRUE; mrb_get_args(mrb, "|b", &recur); return mrb_obj_singleton_methods(mrb, recur, self); } mrb_value mrb_mod_define_method_m(mrb_state *mrb, struct RClass *c); static mrb_value mod_define_singleton_method(mrb_state *mrb, mrb_value self) { return mrb_mod_define_method_m(mrb, mrb_class_ptr(mrb_singleton_class(mrb, self))); } static mrb_bool cv_name_p(mrb_state *mrb, const char *name, mrb_int len) { return len > 2 && name[0] == '@' && name[1] == '@' && !ISDIGIT(name[2]) && mrb_ident_p(name+2, len-2); } static void check_cv_name_sym(mrb_state *mrb, mrb_sym id) { mrb_int len; const char *name = mrb_sym_name_len(mrb, id, &len); if (!cv_name_p(mrb, name, len)) { mrb_name_error(mrb, id, "'%n' is not allowed as a class variable name", id); } } /* 15.2.2.4.39 */ /* * call-seq: * remove_class_variable(sym) -> obj * * Removes the definition of the sym, returning that * constant's value. * * class Dummy * @@var = 99 * puts @@var * p class_variables * remove_class_variable(:@@var) * p class_variables * end * * produces: * * 99 * [:@@var] * [] */ static mrb_value mrb_mod_remove_cvar(mrb_state *mrb, mrb_value mod) { mrb_value val; mrb_sym id; mrb_get_args(mrb, "n", &id); check_cv_name_sym(mrb, id); val = mrb_iv_remove(mrb, mod, id); if (!mrb_undef_p(val)) return val; if (mrb_cv_defined(mrb, mod, id)) { mrb_name_error(mrb, id, "cannot remove %n for %v", id, mod); } mrb_name_error(mrb, id, "class variable %n not defined for %v", id, mod); /* not reached */ return mrb_nil_value(); } /* 15.2.2.4.16 */ /* * call-seq: * obj.class_variable_defined?(symbol) -> true or false * * Returns true if the given class variable is defined * in obj. * * class Fred * @@foo = 99 * end * Fred.class_variable_defined?(:@@foo) #=> true * Fred.class_variable_defined?(:@@bar) #=> false */ static mrb_value mrb_mod_cvar_defined(mrb_state *mrb, mrb_value mod) { mrb_sym id; mrb_get_args(mrb, "n", &id); check_cv_name_sym(mrb, id); return mrb_bool_value(mrb_cv_defined(mrb, mod, id)); } /* 15.2.2.4.17 */ /* * call-seq: * mod.class_variable_get(symbol) -> obj * * Returns the value of the given class variable (or throws a * NameError exception). The @@ part of the * variable name should be included for regular class variables * * class Fred * @@foo = 99 * end * Fred.class_variable_get(:@@foo) #=> 99 */ static mrb_value mrb_mod_cvar_get(mrb_state *mrb, mrb_value mod) { mrb_sym id; mrb_get_args(mrb, "n", &id); check_cv_name_sym(mrb, id); return mrb_cv_get(mrb, mod, id); } /* 15.2.2.4.18 */ /* * call-seq: * obj.class_variable_set(symbol, obj) -> obj * * Sets the class variable names by symbol to * object. * * class Fred * @@foo = 99 * def foo * @@foo * end * end * Fred.class_variable_set(:@@foo, 101) #=> 101 * Fred.new.foo #=> 101 */ static mrb_value mrb_mod_cvar_set(mrb_state *mrb, mrb_value mod) { mrb_value value; mrb_sym id; mrb_get_args(mrb, "no", &id, &value); check_cv_name_sym(mrb, id); mrb_cv_set(mrb, mod, id, value); return value; } static mrb_value mrb_mod_included_modules(mrb_state *mrb, mrb_value self) { mrb_value result; struct RClass *c = mrb_class_ptr(self); struct RClass *origin = c; MRB_CLASS_ORIGIN(origin); result = mrb_ary_new(mrb); while (c) { if (c != origin && c->tt == MRB_TT_ICLASS) { if (c->c->tt == MRB_TT_MODULE) { mrb_ary_push(mrb, result, mrb_obj_value(c->c)); } } c = c->super; } return result; } static mrb_value mod_instance_methods(mrb_state *mrb, mrb_value mod, unsigned int visibility) { struct RClass *c = mrb_class_ptr(mod); mrb_bool recur = TRUE; mrb_get_args(mrb, "|b", &recur); return mrb_class_instance_method_list(mrb, recur, c, visibility); } /* 15.2.2.4.33 */ /* * call-seq: * mod.instance_methods(include_super=true) -> array * * Returns an array containing the names of the public and protected instance * methods in the receiver. For a module, these are the public and protected methods; * for a class, they are the instance (not singleton) methods. With no * argument, or with an argument that is false, the * instance methods in mod are returned, otherwise the methods * in mod and mod's superclasses are returned. * * module A * def method1() end * end * class B * def method2() end * end * class C < B * def method3() end * end * * A.instance_methods #=> [:method1] * B.instance_methods(false) #=> [:method2] * C.instance_methods(false) #=> [:method3] * C.instance_methods(true).length #=> 43 */ static mrb_value mrb_mod_instance_methods(mrb_state *mrb, mrb_value mod) { return mod_instance_methods(mrb, mod, MT_NOPRIV); } static mrb_value mrb_mod_public_instance_methods(mrb_state *mrb, mrb_value mod) { return mod_instance_methods(mrb, mod, MT_PUBLIC); } static mrb_value mrb_mod_private_instance_methods(mrb_state *mrb, mrb_value mod) { return mod_instance_methods(mrb, mod, MT_PRIVATE); } static mrb_value mrb_mod_protected_instance_methods(mrb_state *mrb, mrb_value mod) { return mod_instance_methods(mrb, mod, MT_PROTECTED); } static int undefined_method_i(mrb_state *mrb, mrb_sym mid, mrb_method_t m, void *p) { mrb_value ary = *(mrb_value*)p; if (MRB_METHOD_UNDEF_P(m)) { mrb_ary_push(mrb, ary, mrb_symbol_value(mid)); } return 0; } /* * call-seq: * mod.undefined_methods() -> array * * Returns an array containing the names of the undefined methods of the module/class. */ static mrb_value mrb_mod_undefined_methods(mrb_state *mrb, mrb_value mod) { struct RClass *m = mrb_class_ptr(mod); mrb_get_args(mrb, ""); /* no argument */ mrb_value ary = mrb_ary_new(mrb); if (m->flags & MRB_FL_CLASS_IS_PREPENDED) { MRB_CLASS_ORIGIN(m); } mrb_mt_foreach(mrb, m, undefined_method_i, (void*)&ary); return ary; } /* 15.2.2.4.41 */ /* * call-seq: * remove_method(symbol) -> self * * Removes the method identified by _symbol_ from the current * class. For an example, see Module.undef_method. */ static mrb_value mrb_mod_remove_method(mrb_state *mrb, mrb_value mod) { mrb_int argc; const mrb_value *argv; struct RClass *c = mrb_class_ptr(mod); mrb_get_args(mrb, "*", &argv, &argc); mrb_check_frozen(mrb, c); int ai = mrb_gc_arena_save(mrb); while (argc--) { mrb_remove_method(mrb, c, mrb_obj_to_sym(mrb, *argv)); mrb_gc_arena_restore(mrb, ai); argv++; } return mod; } static mrb_value mrb_mod_s_constants(mrb_state *mrb, mrb_value mod) { if (mrb_get_argc(mrb) > 0 || mrb_class_ptr(mod) != mrb->module_class) { return mrb_mod_constants(mrb, mod); } const struct RProc *proc = mrb->c->ci[-1].proc; struct RClass *c = MRB_PROC_TARGET_CLASS(proc); mrb_value ary = mrb_ary_new(mrb); if (!c) c = mrb->object_class; mrb_mod_const_at(mrb, c, ary); proc = proc->upper; while (proc) { struct RClass *c2 = MRB_PROC_TARGET_CLASS(proc); if (!c2) c2 = mrb->object_class; mrb_mod_const_at(mrb, c2, ary); proc = proc->upper; } while (c) { mrb_mod_const_at(mrb, c, ary); c = c->super; if (c == mrb->object_class) break; } return ary; } static mrb_value mrb_mod_s_nesting(mrb_state *mrb, mrb_value mod) { const struct RProc *proc; mrb_value ary; struct RClass *c = NULL; ary = mrb_ary_new(mrb); proc = mrb->c->ci[-1].proc; /* callee proc */ while (proc && !MRB_PROC_CFUNC_P(proc)) { if (MRB_PROC_SCOPE_P(proc)) { struct RClass *c2 = MRB_PROC_TARGET_CLASS(proc); if (c2 != c) { c = c2; mrb_ary_push(mrb, ary, mrb_obj_value(c)); } } proc = proc->upper; } return ary; } void mrb_mruby_metaprog_gem_init(mrb_state* mrb) { struct RClass *krn = mrb->kernel_module; struct RClass *mod = mrb->module_class; mrb_define_private_method_id(mrb, krn, MRB_SYM(global_variables), mrb_f_global_variables, MRB_ARGS_NONE()); /* 15.3.1.3.14 (15.3.1.2.4) */ mrb_define_private_method_id(mrb, krn, MRB_SYM(local_variables), mrb_local_variables, MRB_ARGS_NONE()); /* 15.3.1.3.28 (15.3.1.2.7) */ mrb_define_method_id(mrb, krn, MRB_SYM(singleton_class), mrb_singleton_class, MRB_ARGS_NONE()); mrb_define_method_id(mrb, krn, MRB_SYM_Q(instance_variable_defined), mrb_obj_ivar_defined, MRB_ARGS_REQ(1)); /* 15.3.1.3.20 */ mrb_define_method_id(mrb, krn, MRB_SYM(instance_variable_get), mrb_obj_ivar_get, MRB_ARGS_REQ(1)); /* 15.3.1.3.21 */ mrb_define_method_id(mrb, krn, MRB_SYM(instance_variable_set), mrb_obj_ivar_set, MRB_ARGS_REQ(2)); /* 15.3.1.3.22 */ mrb_define_method_id(mrb, krn, MRB_SYM(instance_variables), mrb_obj_instance_variables, MRB_ARGS_NONE()); /* 15.3.1.3.23 */ mrb_define_method_id(mrb, krn, MRB_SYM(methods), mrb_obj_methods_m, MRB_ARGS_OPT(1)); /* 15.3.1.3.31 */ mrb_define_method_id(mrb, krn, MRB_SYM(private_methods), mrb_obj_private_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.36 */ mrb_define_method_id(mrb, krn, MRB_SYM(protected_methods), mrb_obj_protected_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.37 */ mrb_define_method_id(mrb, krn, MRB_SYM(public_methods), mrb_obj_public_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.38 */ mrb_define_method_id(mrb, krn, MRB_SYM(singleton_methods), mrb_obj_singleton_methods_m, MRB_ARGS_OPT(1)); /* 15.3.1.3.45 */ mrb_define_method_id(mrb, krn, MRB_SYM(define_singleton_method), mod_define_singleton_method, MRB_ARGS_REQ(1)|MRB_ARGS_BLOCK()); mrb_define_method_id(mrb, krn, MRB_SYM(send), mrb_f_send, MRB_ARGS_REQ(1)|MRB_ARGS_REST()|MRB_ARGS_BLOCK()); /* 15.3.1.3.44 */ mrb_define_method_id(mrb, krn, MRB_SYM(public_send), mrb_f_public_send, MRB_ARGS_REQ(1)|MRB_ARGS_REST()|MRB_ARGS_BLOCK()); mrb_define_method_id(mrb, mod, MRB_SYM(class_variables), mrb_mod_class_variables, MRB_ARGS_OPT(1)); /* 15.2.2.4.19 */ mrb_define_method_id(mrb, mod, MRB_SYM(remove_class_variable), mrb_mod_remove_cvar, MRB_ARGS_REQ(1)); /* 15.2.2.4.39 */ mrb_define_method_id(mrb, mod, MRB_SYM_Q(class_variable_defined), mrb_mod_cvar_defined, MRB_ARGS_REQ(1)); /* 15.2.2.4.16 */ mrb_define_method_id(mrb, mod, MRB_SYM(class_variable_get), mrb_mod_cvar_get, MRB_ARGS_REQ(1)); /* 15.2.2.4.17 */ mrb_define_method_id(mrb, mod, MRB_SYM(class_variable_set), mrb_mod_cvar_set, MRB_ARGS_REQ(2)); /* 15.2.2.4.18 */ mrb_define_method_id(mrb, mod, MRB_SYM(included_modules), mrb_mod_included_modules, MRB_ARGS_NONE()); /* 15.2.2.4.30 */ mrb_define_method_id(mrb, mod, MRB_SYM(instance_methods), mrb_mod_instance_methods, MRB_ARGS_ANY()); /* 15.2.2.4.33 */ mrb_define_method_id(mrb, mod, MRB_SYM(public_instance_methods), mrb_mod_public_instance_methods, MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, mod, MRB_SYM(private_instance_methods), mrb_mod_private_instance_methods, MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, mod, MRB_SYM(protected_instance_methods), mrb_mod_protected_instance_methods, MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, mod, MRB_SYM(undefined_instance_methods), mrb_mod_undefined_methods, MRB_ARGS_NONE()); mrb_define_method_id(mrb, mod, MRB_SYM(remove_method), mrb_mod_remove_method, MRB_ARGS_ANY()); /* 15.2.2.4.41 */ mrb_define_method_id(mrb, mod, MRB_SYM(method_removed), mrb_f_nil, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, mod, MRB_SYM(constants), mrb_mod_constants, MRB_ARGS_OPT(1)); /* 15.2.2.4.24 */ mrb_define_class_method_id(mrb, mod, MRB_SYM(constants), mrb_mod_s_constants, MRB_ARGS_ANY()); /* 15.2.2.3.1 */ mrb_define_class_method_id(mrb, mod, MRB_SYM(nesting), mrb_mod_s_nesting, MRB_ARGS_NONE()); /* 15.2.2.3.2 */ } void mrb_mruby_metaprog_gem_final(mrb_state* mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-bigint0000644000000000000000000000013215077107334021655 xustar0030 mtime=1761382108.663301644 30 atime=1761382109.797298366 30 ctime=1761382108.663301644 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bigint/0000755000175100017510000000000015077107334022322 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bigint/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024054 xustar0030 mtime=1761382078.105420634 30 atime=1761382080.126411389 30 ctime=1761382108.659301656 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bigint/mrbgem.rake0000644000175100017510000000061415077107276024445 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-bigint') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Integer class extension to multiple-precision' spec.build.defines << "MRB_USE_BIGINT" spec.build.libmruby_core_objs << Dir.glob(File.join(__dir__, "core/**/*.c")).map { |fn| objfile(fn.relative_path_from(__dir__).pathmap("#{spec.build_dir}/%X")) } end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bigint/PaxHeaders/README-fgmp.md0000644000000000000000000000013215077107276024145 xustar0030 mtime=1761382078.104420639 30 atime=1761382080.125411393 30 ctime=1761382108.663301644 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bigint/README-fgmp.md0000644000175100017510000000707315077107276024544 0ustar00runnerrunner# fgmp FGMP is a public domain implementation of a subset of the GNU gmp library with the same API. WELCOME TO FGMP. FGMP is a public domain implementation of a subset of the GNU gmp library with the same API. For instance, you can link the following trivial program with either this code, or libgmp.a and get the same results. ```c #include #include "gmp.h" main() { MP_INT a; MP_INT b; MP_INT c; mpz_init_set_ui(&a,1); mpz_init_set_ui(&b,2); mpz_init(&c); mpz_add(&c,&a,&b); printf("\n%s\n", mpz_get_str(NULL,10,&c)); } ``` FGMP is really in the public domain. You can do whatever you want with it. I wrote FGMP so that we would all have access to a (truly free) implementation of this subset of the API of GNU libgmp. I encourage everyone to distribute this as widely as possible. If you need more documentation, I suggest you look at the file gmp.texi which is included with the GNU gmp library. You can send me bug reports, implementations of missing functions, flames and rants by Email. Any submissions of new code to be integrated into fgmp must also be placed in the public domain (For the particularly dense, you can release a new fgmp yourself under different licensing terms. This is a condition for including a submission in a release of FGMP that I personally prepare). Mark Henderson # This is the fifth BETA release. 1.0b5 I hereby place this file and all of FGMP in the public domain. Thanks to Paul Rouse for changes to get fgmp to work on a 286 MS-DOS compiler, the functions mpz_sqrt and mpz_sqrtrem, plus other general bugfixes. Thanks also to Erick Gallesio for a fix to mpz_init_set_str Define B64 if your "long" type is 64 bits. Otherwise, we assume 32 bit longs. (The 64-bit version hasn't been tested enough) ``` Platforms: Linux 0.99 (gcc) IBM RS6000/AIX 3.2 (IBM xlc compiler and gcc 2.3) Sun OS 4.1, Sun 3/4 DEC Alpha OSF/1 (only lightly tested, 64 bit longs do make a difference, thanks to DEC for providing access via axposf.pa.dec.com). Define B64 for this platform MS-DOS 286 C compiler (see credits above) ``` # Some differences between gmp and fgmp 1. fgmp is considerably slower than gmp 2. fgmp does not implement the following: - all mpq\_\* - internal mpn\_\* functions - mpz_perfect_square_p - mpz_inp_raw, mpz_out_raw - mp_set_memory_functions, mpz_out_str, mpz_inp_str 3. fgmp implements the following in addition to the routines in GNU gmp. `int mpz_jacobi(MP_INT *a, MP_INT *b)` - finds the jacobi symbol (a/b) 4. mpz_sizeinbase often overestimates the exact value 5. To convert your gmp based program to fgmp (subject to the above) - recompile your source. Make sure to include the gmp.h file included with fgmp rather than that included with gmp. (The point is to recompile all files which include gmp.h) - link with gmp.o instead of libgmp.a Here's a completely sorted list of functions implemented in fgmp: ``` _mpz_realloc mpz_abs mpz_add mpz_add_ui mpz_and mpz_clear mpz_cmp mpz_cmp_si mpz_cmp_ui mpz_div mpz_div_2exp mpz_div_ui mpz_divmod mpz_divmod_ui mpz_fac_ui mpz_gcd mpz_gcdext mpz_get_si mpz_get_str mpz_get_ui mpz_init mpz_init_set mpz_init_set_si mpz_init_set_str mpz_init_set_ui mpz_jacobi mpz_mdiv mpz_mdiv_ui mpz_mdivmod mpz_mdivmod_ui mpz_mmod mpz_mmod_ui mpz_mod mpz_mod_2exp mpz_mod_ui mpz_mul mpz_mul_2exp mpz_mul_ui mpz_neg mpz_or mpz_pow_ui mpz_powm mpz_powm_ui mpz_probab_prime_p mpz_random mpz_random2 mpz_set mpz_set_si mpz_set_str mpz_set_ui mpz_size mpz_sizeinbase mpz_sqrt mpz_sqrtrem mpz_sub mpz_sub_ui mpz_xor ``` nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bigint/PaxHeaders/README.md0000644000000000000000000000013215077107276023216 xustar0030 mtime=1761382078.104420639 30 atime=1761382080.125411393 30 ctime=1761382108.660301653 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bigint/README.md0000644000175100017510000000106415077107276023607 0ustar00runnerrunner# Multi-precision Integer extension for mruby This extension uses fgmp, which is a public domain implementation of a subset of the GNU gmp library by Mark Henderson . But it's heavily modified to fit with mruby. You can get the original source code from . You can read the original README for fgmp in [README-fgmp.md](README-fgmp.md). If you want to create your own Multi-precision Integer GEM, see [examples/mrbgems/mruby-YOUR-bigint/TODO-HINT.md](../../examples/mrbgems/mruby-YOUR-bigint/TODO-HINT.md). nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bigint/PaxHeaders/core0000644000000000000000000000013215077107334022605 xustar0030 mtime=1761382108.658301659 30 atime=1761382109.797298366 30 ctime=1761382108.658301659 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bigint/core/0000755000175100017510000000000015077107334023252 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bigint/core/PaxHeaders/bigint.c0000644000000000000000000000013215077107276024307 xustar0030 mtime=1761382078.105420634 30 atime=1761382080.125411393 30 ctime=1761382108.657301662 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bigint/core/bigint.c0000644000175100017510000012637715077107276024717 0ustar00runnerrunner/** ** @file mruby/bigint.c - Multi-precision Integer ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include "bigint.h" #define DIG_SIZE (MPZ_DIG_SIZE) #define DIG_BASE (1ULL << DIG_SIZE) #define DIG_MASK (DIG_BASE - 1) #define HIGH(x) ((x) >> DIG_SIZE) #define LOW(x) ((x) & DIG_MASK) #define iabs(x) (((x)>0)?(x):(-x)) #define imax(x,y) (((x)>(y))?(x):(y)) #define imin(x,y) (((x)<(y))?(x):(y)) #define dg(x,i) (((size_t)i < (x)->sz)?(x)->p[i]:0) static void mpz_init(mrb_state *mrb, mpz_t *s) { s->p = NULL; s->sn=0; s->sz=0; } static void mpz_realloc(mrb_state *mrb, mpz_t *x, size_t size) { if (x->sz < size) { x->p=(mp_limb*)mrb_realloc(mrb, x->p, size*sizeof(mp_limb)); for (size_t i=x->sz; ip[i] = 0; x->sz = size; } } static void mpz_set(mrb_state *mrb, mpz_t *y, mpz_t *x) { size_t i, k = x->sz; mpz_realloc(mrb, y, k); for (i=0;i < k; i++) y->p[i] = x->p[i]; y->sz = k; y->sn = x->sn; } static void mpz_init_set(mrb_state *mrb, mpz_t *s, mpz_t *t) { mpz_init(mrb, s); mpz_set(mrb, s, t); } static void mpz_set_int(mrb_state *mrb, mpz_t *y, mrb_int v) { mrb_uint u; if (v == 0) { y->sn=0; u = 0; } else if (v > 0) { y->sn = 1; u = v; } else /* if (v < 0) */ { y->sn = -1; if (v == MRB_INT_MIN) u = v; else u = -v; } #if MRB_INT_BIT > DIG_SIZE if ((u & ~DIG_MASK) != 0) { mpz_realloc(mrb, y, 2); y->p[1] = (mp_limb)HIGH(u); y->p[0] = (mp_limb)LOW(u); return; } #endif mpz_realloc(mrb, y, 1); y->p[0] = (mp_limb)u; } static void mpz_set_uint64(mrb_state *mrb, mpz_t *y, uint64_t u) { size_t len = 0; for (uint64_t u0=u; u0; u0>>=DIG_SIZE,len++) ; y->sn = (u != 0); mpz_realloc(mrb, y, len); for (size_t i=0; ip[i] = (mp_limb)LOW(u); u >>= DIG_SIZE; } } #ifdef MRB_INT32 static void mpz_set_int64(mrb_state *mrb, mpz_t *y, int64_t v) { uint64_t u; if (v < 0) { if (v == INT64_MIN) u = v; else u = -v; } else { u = v; } mpz_set_uint64(mrb, y, u); if (v < 0) { y->sn = -1; } } #endif static void mpz_init_set_int(mrb_state *mrb, mpz_t *y, mrb_int v) { mpz_init(mrb, y); mpz_set_int(mrb, y, v); } static void mpz_clear(mrb_state *mrb, mpz_t *s) { if (s->p) mrb_free(mrb, s->p); s->p = NULL; s->sn = 0; s->sz = 0; } static void mpz_move(mrb_state *mrb, mpz_t *y, mpz_t *x) { mpz_clear(mrb, y); y->sn = x->sn; y->sz = x->sz; y->p = x->p; x->p = NULL; x->sn = 0; x->sz = 0; } static size_t digits(mpz_t *x) { size_t i; if (x->sz == 0) return 0; for (i = x->sz - 1; x->p[i] == 0; i--) if (i == 0) break; return i+1; } static void trim(mpz_t *x) { while (x->sz && x->p[x->sz-1] == 0) { x->sz--; } } /* z = x + y, without regard for sign */ static void uadd(mrb_state *mrb, mpz_t *z, mpz_t *x, mpz_t *y) { if (y->sz < x->sz) { mpz_t *t; /* swap x,y */ t=x; x=y; y=t; } /* now y->sz >= x->sz */ mpz_realloc(mrb, z, y->sz+1); mp_dbl_limb c = 0; size_t i; for (i=0; isz; i++) { c += (mp_dbl_limb)y->p[i] + (mp_dbl_limb)x->p[i]; z->p[i] = LOW(c); c >>= DIG_SIZE; } for (;isz; i++) { c += y->p[i]; z->p[i] = LOW(c); c >>= DIG_SIZE; } z->p[y->sz] = (mp_limb)c; } /* z = y - x, ignoring sign */ /* precondition: abs(y) >= abs(x) */ static void usub(mrb_state *mrb, mpz_t *z, mpz_t *y, mpz_t *x) { mpz_realloc(mrb, z, (size_t)(y->sz)); mp_dbl_limb_signed b = 0; size_t i; for (i=0;isz;i++) { b += (mp_dbl_limb_signed)y->p[i]; b -= (mp_dbl_limb_signed)x->p[i]; z->p[i] = LOW(b); b = HIGH(b); } for (;isz; i++) { b += y->p[i]; z->p[i] = LOW(b); b = HIGH(b); } z->sz = digits(z); } /* compare abs(x) and abs(y) */ static int ucmp(mpz_t *y, mpz_t *x) { if (y->sz < x->sz) return -1; if (y->sz > x->sz) return 1; if (x->sz == 0) return 0; for (size_t i=x->sz-1;; i--) { mp_limb a = y->p[i]; mp_limb b = x->p[i]; if (a > b) return 1; if (a < b) return -1; if (i == 0) break; } return 0; } #define zero_p(x) ((x)->sn == 0) /* check if all digits are zero */ static int uzero_p(mpz_t *x) { if (x->sz == 0) return 1; for (size_t i=x->sz-1;; i--) { if (x->p[i] != 0) return 0; if (i == 0) break; } return 1; } static void zero(mpz_t *x) { x->sn=0; if (x->p) { x->sz=1; x->p[0]=0; } else { x->sz=0; } } /* z = x + y */ static void mpz_add(mrb_state *mrb, mpz_t *zz, mpz_t *x, mpz_t *y) { if (zero_p(x)) { mpz_set(mrb, zz, y); return; } if (zero_p(y)) { mpz_set(mrb, zz, x); return; } mpz_t z; mpz_init(mrb, &z); if (x->sn > 0 && y->sn > 0) { uadd(mrb, &z, x, y); z.sn = 1; } else if (x->sn < 0 && y->sn < 0) { uadd(mrb, &z, x, y); z.sn = -1; } else { int mg; /* signs differ */ if ((mg = ucmp(x,y)) == 0) { zero(&z); } else if (mg > 0) { /* abs(y) < abs(x) */ usub(mrb, &z, x, y); z.sn = (x->sn > 0 && y->sn < 0) ? 1 : (-1); } else { /* abs(y) > abs(x) */ usub(mrb, &z, y, x); z.sn = (x->sn < 0 && y->sn > 0) ? 1 : (-1); } } trim(&z); mpz_move(mrb, zz, &z); } /* x += n */ /* ignores sign of x */ /* assumes n is positive and small (fits in mp_limb) */ static void mpz_add_int(mrb_state *mrb, mpz_t *x, mrb_int n) { // If n is zero, no operation is needed if (n == 0) return; // Assume x is positive and n is a small positive integer mp_dbl_limb carry = n; // Initialize carry with n for (size_t i = 0; i < x->sz && carry; i++) { carry += (mp_dbl_limb)x->p[i]; // Add current limb and carry x->p[i] = LOW(carry); // Store lower 32 bits in current limb carry = HIGH(carry); // Update carry with higher bits } if (carry != 0) { mpz_realloc(mrb, x, x->sz + 1); x->p[x->sz-1] = (mp_limb)carry; x->sn = 1; } trim(x); } /* z = x - y -- just use mpz_add - I'm lazy */ static void mpz_sub(mrb_state *mrb, mpz_t *z, mpz_t *x, mpz_t *y) { mpz_t u; mpz_init(mrb, &u); u.p = y->p; u.sz = y->sz; u.sn = -(y->sn); mpz_add(mrb, z, x, &u); } /* x -= n */ /* ignores sign of x */ /* assumes n is positive and small (fits in mp_limb) */ static void mpz_sub_int(mrb_state *mrb, mpz_t *x, mrb_int n) { // If n is zero, no operation is needed if (n == 0) return; // If x is zero, set x to n if (zero_p(x) || x->sz == 0) { mpz_set_int(mrb, x, n); return; } // Initialize borrow and start decrement mp_dbl_limb_signed borrow = (mp_limb)n; size_t i = 0; // Subtract 1 from the least significant limb and propagate if necessary borrow = (mp_dbl_limb_signed)x->p[i] - borrow; x->p[i] = LOW(borrow); borrow = (borrow < 0) ? 1 : 0; // Continue through limbs while there is a borrow for (i = 1; i < x->sz && borrow; i++) { borrow = (mp_dbl_limb_signed)x->p[i] - borrow; x->p[i] = LOW(borrow); borrow = (borrow < 0) ? 1 : 0; } // Trim any unnecessary leading zeros trim(x); } /* w = u * v */ /* Simple Multiply */ static void mpz_mul(mrb_state *mrb, mpz_t *ww, mpz_t *u, mpz_t *v) { if (zero_p(u) || zero_p(v)) { zero(ww); return; } mpz_t w; mpz_init(mrb, &w); mpz_realloc(mrb, &w, u->sz + v->sz); for (size_t j = 0; j < u->sz; j++) { size_t i; mp_dbl_limb cc = (mp_limb)0; mp_limb u0 = u->p[j]; if (u0 == 0) continue; for (i = 0; i < v->sz; i++) { mp_limb v0 = v->p[i]; if (v0 == 0) continue; cc += (mp_dbl_limb)w.p[i + j] + (mp_dbl_limb)u0 * (mp_dbl_limb)v0; w.p[i + j] = LOW(cc); cc = HIGH(cc); } if (cc) { w.p[i + j] = (mp_limb)cc; } } w.sn = u->sn * v->sn; trim(&w); mpz_move(mrb, ww, &w); } /* number of leading zero bits in digit */ static int lzb(mp_limb x) { if (x == 0) return 0; #if (defined(__GNUC__) || __has_builtin(__builtin_clz)) if (sizeof(mp_limb) == sizeof(int64_t)) return __builtin_clzll(x); else if (sizeof(mp_limb) == sizeof(int32_t)) return __builtin_clz(x); #endif int j=0; for (mp_limb i = ((mp_limb)1 << (DIG_SIZE-1)); i && !(x&i); j++,i>>=1) ; return j; } /* c1 = a>>n */ /* n must be < DIG_SIZE */ static void urshift(mrb_state *mrb, mpz_t *c1, mpz_t *a, size_t n) { mrb_assert(n < DIG_SIZE); if (n == 0) mpz_set(mrb, c1, a); else if (uzero_p(a)) { zero(c1); } else { mpz_t c; mp_limb cc = 0; mp_dbl_limb rm = (((mp_dbl_limb)1<sz); for (size_t i=a->sz-1;; i--) { c.p[i] = ((a->p[i] >> n) | cc) & DIG_MASK; cc = (a->p[i] & rm) << (DIG_SIZE - n); if (i == 0) break; } trim(&c); mpz_move(mrb, c1, &c); } } /* c1 = a<sz+1); size_t i; for (i=0; isz; i++) { c.p[i] = ((a->p[i] << n) | cc) & DIG_MASK; cc = (a->p[i] & rm) >> (DIG_SIZE-n); } c.p[i] = cc; trim(&c); mpz_move(mrb, c1, &c); } } /* internal routine to compute x/y and x%y ignoring signs */ /* qq = xx/yy; rr = xx%yy */ static void udiv(mrb_state *mrb, mpz_t *qq, mpz_t *rr, mpz_t *xx, mpz_t *yy) { /* simple cases */ int cmp = ucmp(xx, yy); if (cmp == 0) { mpz_set_int(mrb, qq, 1); zero(rr); return; } else if (cmp < 0) { zero(qq); mpz_set(mrb, rr, xx); return; } mpz_t q, x, y; mrb_assert(yy->sn != 0); /* divided by zero */ mrb_assert(yy->sz > 0); /* divided by zero */ mpz_init(mrb, &q); mpz_init(mrb, &x); mpz_init(mrb, &y); mpz_realloc(mrb, &x, xx->sz+1); size_t yd = digits(yy); size_t ns = lzb(yy->p[yd-1]); ulshift(mrb, &x, xx, ns); ulshift(mrb, &y, yy, ns); size_t xd = digits(&x); mpz_realloc(mrb, &q, xd); mp_dbl_limb z = y.p[yd-1]; if (xd>=yd) { for (size_t j=xd-yd;; j--) { mp_dbl_limb_signed b=0; mp_dbl_limb qhat; if (j+yd == xd) qhat = x.p[j+yd-1] / z; else qhat = (((mp_dbl_limb)x.p[j+yd] << DIG_SIZE) + x.p[j+yd-1]) / z; if (qhat) { size_t i; for (i=0; isz; urshift(mrb, rr, &x, ns); trim(&q); mpz_move(mrb, qq, &q); mpz_clear(mrb, &x); mpz_clear(mrb, &y); } static void mpz_mdiv(mrb_state *mrb, mpz_t *q, mpz_t *x, mpz_t *y) { mpz_t r; short sn1 = x->sn, sn2 = y->sn, qsign; if (zero_p(x)) { mpz_init_set_int(mrb, q, 0); return; } mpz_init(mrb, &r); udiv(mrb, q, &r, x, y); qsign = q->sn = sn1*sn2; if (uzero_p(q)) q->sn = 0; /* now if r != 0 and q < 0 we need to round q towards -inf */ if (!uzero_p(&r) && qsign < 0) { /* add 1 to magnitude */ mpz_add_int(mrb, q, 1); /* force negative sign in case the value of q was zero before rounding */ q->sn = -1; } mpz_clear(mrb, &r); } static void mpz_mmod(mrb_state *mrb, mpz_t *r, mpz_t *x, mpz_t *y) { mpz_t q; short sn1 = x->sn, sn2 = y->sn, sn3; mpz_init(mrb, &q); if (sn1 == 0) { zero(r); return; } udiv(mrb, &q, r, x, y); mpz_clear(mrb, &q); if (uzero_p(r)) { r->sn = 0; return; } sn3 = sn1*sn2; if (sn3 > 0) r->sn = sn1; else if (sn1 < 0 && sn2 > 0) { r->sn = 1; mpz_sub(mrb, r, y, r); } else { r->sn = 1; mpz_add(mrb, r, y, r); } } static void mpz_mdivmod(mrb_state *mrb, mpz_t *q, mpz_t *r, mpz_t *x, mpz_t *y) { short sn1 = x->sn, sn2 = y->sn, qsign; if (sn1 == 0) { zero(q); zero(r); return; } udiv(mrb, q, r, x, y); qsign = q->sn = sn1*sn2; if (uzero_p(r)) { /* q != 0, since q=r=0 would mean x=0, which was tested above */ r->sn = 0; return; } if (q->sn > 0) r->sn = sn1; else if (sn1 < 0 && sn2 > 0) { r->sn = 1; mpz_sub(mrb, r, y, r); } else { r->sn = 1; mpz_add(mrb, r, y, r); } if (uzero_p(q)) q->sn = 0; /* now if r != 0 and q < 0 we need to round q towards -inf */ if (!uzero_p(r) && qsign < 0) { /* add 1 to magnitude */ mpz_add_int(mrb, q, 1); /* force negative sign in case the value of q was zero before rounding */ q->sn = -1; } } static void mpz_mod(mrb_state *mrb, mpz_t *r, mpz_t *x, mpz_t *y) { mpz_t q; short sn = x->sn; if (zero_p(x)) { zero(r); return; } mpz_init(mrb, &q); udiv(mrb, &q, r, x, y); r->sn = sn; if (uzero_p(r)) r->sn = 0; mpz_clear(mrb, &q); } static mrb_int mpz_cmp(mrb_state *mrb, mpz_t *x, mpz_t *y) { if (x->sn < 0 && y->sn > 0) return (-1); if (x->sn > 0 && y->sn < 0) return 1; int abscmp=ucmp(x, y); if (x->sn >=0 && y->sn >=0) return abscmp; return (-abscmp); // if (x->sn <=0 && y->sn <=0) } /* 2<=base<=36 - this overestimates the optimal value, which is OK */ static size_t mpz_sizeinbase(mpz_t *x, mrb_int base) { size_t i, j; size_t bits = digits(x) * DIG_SIZE; mrb_assert(2 <= base && base <= 36); if (zero_p(x) || x->sz == 0) return 0; for (j=0,i=1; i<=(size_t)base; i*=2,j++) ; return bits/(j-1)+1; } /* x = y * n (only called from mpz_init_set_str) */ /* assumes x and n are positive or zero */ /* assumes n is small (fits in mp_limb) */ static void mpz_mul_int(mrb_state *mrb, mpz_t *x, mrb_int n) { if (n == 0 || zero_p(x)) { zero(x); return; } size_t x_sz = x->sz; size_t new_sz = x_sz + 1; // Maximum possible size after multiplication // Reallocate x if necessary mpz_realloc(mrb, x, new_sz); mp_dbl_limb cc = 0; mp_limb n_limb = (mp_limb)n; for (size_t i = 0; i < x_sz; i++) { // Multiply each limb and add carry cc += (mp_dbl_limb)x->p[i] * n_limb; x->p[i] = LOW(cc); cc = HIGH(cc); } if (cc) { // If there is a remaining carry, store it x->p[x_sz] = (mp_limb)cc; } else { x->sz = x_sz; } x->sn = 1; trim(x); } static int mpz_init_set_str(mrb_state *mrb, mpz_t *x, const char *s, mrb_int len, mrb_int base) { int retval = 0; short sn; uint8_t k; mpz_init(mrb, x); if (*s == '-') { sn = -1; s++; } else if (*s == '+') { sn = 1; s++; } else sn = 1; for (mrb_int i=0; i= '0' && s[i] <= '9') k = (uint8_t)s[i] - (uint8_t)'0'; else if (s[i] >= 'A' && s[i] <= 'Z') k = (uint8_t)s[i] - (uint8_t)'A'+10; else if (s[i] >= 'a' && s[i] <= 'z') k = (uint8_t)s[i] - (uint8_t)'a'+10; else { retval = (-1); break; } if (k >= base) { retval = (-1); break; } mpz_mul_int(mrb, x, base); mpz_add_int(mrb, x, k); } x->sn = x->sz == 0 ? 0 : sn; return retval; } /* power of base no bigger than DIG_BASE */ /* power of 2 is handled differently */ static const mp_limb base_limit[34*2] = { #ifdef MRB_NO_MPZ64BIT 59049, // 3^10 0, // 4^8 (skip) 15625, // 5^6 46656, // 6^6 16807, // 7^5 0, // 8^5 (skip) 59049, // 9^5 10000, // 10^4 14641, // 11^4 20736, // 12^4 28561, // 13^4 38416, // 14^4 50625, // 15^4 0, // 16^4 (skip) 4913, // 17^3 5832, // 18^3 6859, // 19^3 8000, // 20^3 9261, // 21^3 10648, // 22^3 12167, // 23^3 13824, // 24^3 15625, // 25^3 17576, // 26^3 19683, // 27^3 21952, // 28^3 24389, // 29^3 27000, // 30^3 29791, // 31^3 0, // 32^3 (skip) 35937, // 33^3 39304, // 34^3 42875, // 35^3 46656, // 36^3 #else 3486784401UL, // 3^20 0, // 4^16 (skip) 1220703125UL, // 5^13 2176782336UL, // 6^12 1977326743UL, // 7^11 0, // 8^10 (skip) 3486784401UL, // 9^10 1000000000UL, // 10^9 2357947691UL, // 11^9 429981696UL, // 12^8 815730721UL, // 13^8 1475789056UL, // 14^8 2562890625UL, // 15^8 0, // 16^8 (skip) 410338673UL, // 17^7 612220032UL, // 18^7 893871739UL, // 19^7 1280000000UL, // 20^7 1801088541UL, // 21^7 2494357888UL, // 22^7 3404825447UL, // 23^7 191102976UL, // 24^6 244140625UL, // 25^6 308915776UL, // 26^6 387420489UL, // 27^6 481890304UL, // 28^6 594823321UL, // 29^6 729000000UL, // 30^6 887503681UL, // 31^6 0, // 32^6 (skip) 1291467969UL, // 33^6 1544804416UL, // 34^6 1838265625UL, // 35^6 2176782336UL, // 36^6 #endif }; static char* mpz_get_str(mrb_state *mrb, char *s, mrb_int sz, mrb_int base, mpz_t *x) { mrb_assert(2 <= base && base <= 36); if (zero_p(x)) { *s='0'; *(s+1)='\0'; return s; } char *ps = s; char *se = s+sz; int xlen = (int)digits(x); if ((base & (base - 1)) == 0) { // base is a power of 2 int shift = 0; while ((1 << shift) < base) shift++; mp_limb mask = (mp_limb)base - 1; mp_dbl_limb value = 0; int bits = 0; for (int i = 0; i < xlen; i++) { value |= (mp_dbl_limb)x->p[i] << bits; bits += DIG_SIZE; while (bits >= shift) { mp_limb digit = value & mask; value >>= shift; bits -= shift; if (digit < 10) *s++ = '0' + digit; else *s++ = 'a' + digit - 10; } } } else { mp_limb *t = (mp_limb*)mrb_malloc(mrb, xlen*sizeof(mp_limb)); mp_limb *tend = t + xlen; memcpy(t, x->p, xlen*sizeof(mp_limb)); mp_limb b2 = base_limit[base-3]; for (;;) { mp_limb *d = tend; mp_dbl_limb a = 0; while (--d >= t) { mp_limb d0 = *d; a = (a<=base; b/=base) { char a0 = (char)(a % base); if (a0 < 10) a0 += '0'; else a0 += 'a' - 10; if (s == se) break; *s++ = a0; a /= base; } // check if number is zero for (d = t; d < tend; d++) { if (*d != 0) break; } if (d == tend) break; } mrb_free(mrb, t); } while (pssn < 0) { *s++ = '-'; } /* reverse string */ for (char *u = ps,*v=s-1; u < v; u++,v--) { char temp = *u; *u = *v; *v = temp; } *s = '\0'; /* null termination */ return ps; } static int mpz_get_int(mpz_t *y, mrb_int *v) { if (zero_p(y)) { *v = 0; return TRUE; } mp_dbl_limb i = 0; mp_limb *d = y->p + y->sz; while (d-- > y->p) { if (HIGH(i) != 0) { /* will overflow */ return FALSE; } i = (i << DIG_SIZE) | *d; } if (i > MRB_INT_MAX) { /* overflow */ return FALSE; } if (y->sn < 0) { *v = -(mrb_int)i; } else { *v = (mrb_int)i; } return TRUE; } static void mpz_mul_2exp(mrb_state *mrb, mpz_t *z, mpz_t *x, mrb_int e) { if (e==0) mpz_set(mrb, z, x); else { short sn = x->sn; size_t digs = e / DIG_SIZE; size_t bs = e % DIG_SIZE; mpz_t y; mpz_init(mrb, &y); mpz_realloc(mrb, &y, x->sz+digs); for (size_t i=0;isz;i++) y.p[i+digs] = x->p[i]; if (bs) { ulshift(mrb, z, &y, bs); mpz_clear(mrb, &y); } else { mpz_move(mrb, z, &y); } z->sn = sn; } } static void mpz_div_2exp(mrb_state *mrb, mpz_t *z, mpz_t *x, mrb_int e) { short sn = x->sn; if (e==0) mpz_set(mrb, z, x); else { size_t digs = e / DIG_SIZE; size_t bs = e % DIG_SIZE; mpz_t y; mpz_init(mrb, &y); mpz_realloc(mrb, &y, x->sz-digs); for (size_t i=0; i < x->sz-digs; i++) y.p[i] = x->p[i+digs]; if (bs) { urshift(mrb, z, &y, bs); mpz_clear(mrb, &y); } else { mpz_move(mrb, z, &y); } if (uzero_p(z)) z->sn = 0; else { z->sn = sn; } } } static void mpz_neg(mrb_state *mrb, mpz_t *x, mpz_t *y) { mpz_set(mrb, x, y); x->sn = -(y->sn); } #define make_2comp(v,c) do { v=~(v)+(c); c=((v)==0 && (c));} while (0) static void mpz_and(mrb_state *mrb, mpz_t *z, mpz_t *x, mpz_t *y) { if (zero_p(x) || zero_p(y)) { zero(z); return; } mrb_assert(x->sz > 0 || y->sz > 0); size_t max_sz = (x->sz > y->sz) ? x->sz : y->sz; mpz_realloc(mrb, z, max_sz); z->sn = (x->sn == y->sn) ? x->sn : 1; char c1 = 1, c2 = 1, c3 = 1; for (size_t i = 0; i < max_sz; i++) { mp_limb xv = (i < x->sz) ? x->p[i] : 0; mp_limb yv = (i < y->sz) ? y->p[i] : 0; if (x->sn < 0) make_2comp(xv, c1); if (y->sn < 0) make_2comp(yv, c2); mp_limb zv = xv & yv; if (z->sn < 0) make_2comp(zv, c3); z->p[i] = zv; } } static void mpz_or(mrb_state *mrb, mpz_t *z, mpz_t *x, mpz_t *y) /* not the most efficient way to do this */ { if (zero_p(x)) { mpz_set(mrb, z, y); return; } if (zero_p(y)) { mpz_set(mrb, z, x); return; } mrb_assert(x->sz > 0 || y->sz > 0); size_t max_sz = (x->sz > y->sz) ? x->sz : y->sz; mpz_realloc(mrb, z, max_sz); z->sn = (x->sn == y->sn) ? x->sn : -1; char c1 = 1, c2 = 1, c3 = 1; for (size_t i = 0; i < max_sz; i++) { mp_limb xv = (i < x->sz) ? x->p[i] : 0; mp_limb yv = (i < y->sz) ? y->p[i] : 0; if (x->sn < 0) make_2comp(xv, c1); if (y->sn < 0) make_2comp(yv, c2); mp_limb zv = xv | yv; if (z->sn < 0) make_2comp(zv, c3); z->p[i] = zv; } } static void mpz_xor(mrb_state *mrb, mpz_t *z, mpz_t *x, mpz_t *y) /* not the most efficient way to do this */ { if (zero_p(x)) { mpz_set(mrb, z, y); return; } if (zero_p(y)) { mpz_set(mrb, z, x); return; } mrb_assert(x->sz > 0 || y->sz > 0); size_t max_sz = (x->sz > y->sz) ? x->sz : y->sz; mpz_realloc(mrb, z, max_sz); z->sn = (x->sn == y->sn) ? 1 : -1; char c1 = 1, c2 = 1, c3 = 1; for (size_t i = 0; i < max_sz; i++) { mp_limb xv = (i < x->sz) ? x->p[i] : 0; mp_limb yv = (i < y->sz) ? y->p[i] : 0; if (x->sn < 0) make_2comp(xv, c1); if (y->sn < 0) make_2comp(yv, c2); mp_limb zv = xv ^ yv; if (z->sn < 0) make_2comp(zv, c3); z->p[i] = zv; } } static void mpz_pow(mrb_state *mrb, mpz_t *zz, mpz_t *x, mrb_int e) { mpz_t t; mrb_uint mask = 1ULL<<(sizeof(mrb_int)*8-1); if (e==0) { mpz_set_int(mrb, zz, 1L); return; } mpz_init_set(mrb, &t, x); for (;!(mask &e); mask>>=1) ; mask>>=1; for (;mask!=0; mask>>=1) { mpz_mul(mrb, &t, &t, &t); if (e & mask) mpz_mul(mrb, &t, &t, x); } mpz_move(mrb, zz, &t); } static void mpz_powm(mrb_state *mrb, mpz_t *zz, mpz_t *x, mpz_t *ex, mpz_t *n) { if (zero_p(ex) || uzero_p(ex)) { mpz_set_int(mrb, zz, 1); return; } if (ex->sn < 0) { return; } mpz_t t, b; mpz_init_set_int(mrb, &t, 1); mpz_init_set(mrb, &b, x); size_t len = digits(ex); for (size_t i=0; ip[i]; for (size_t j=0; j>= 1; mpz_mul(mrb, &b, &b, &b); mpz_mod(mrb, &b, &b, n); } } mpz_move(mrb, zz, &t); mpz_clear(mrb, &b); } static void mpz_powm_i(mrb_state *mrb, mpz_t *zz, mpz_t *x, mrb_int ex, mpz_t *n) { if (ex == 0) { mpz_set_int(mrb, zz, 1); return; } if (ex < 0) { return; } mpz_t t, b; mpz_init_set_int(mrb, &t, 1); mpz_init_set(mrb, &b, x); while (ex > 0) { if ((ex & 1) == 1) { mpz_mul(mrb, &t, &t, &b); mpz_mod(mrb, &t, &t, n); } ex >>= 1; mpz_mul(mrb, &b, &b, &b); mpz_mod(mrb, &b, &b, n); } mpz_move(mrb, zz, &t); mpz_clear(mrb, &b); } #ifdef MRB_USE_RATIONAL static void mpz_abs(mrb_state *mrb, mpz_t *x, mpz_t *y) { mpz_init_set(mrb, x, y); if (zero_p(y)) x->sn = 0; else x->sn = 1; } static void mpz_gcd(mrb_state *mrb, mpz_t *gg, mpz_t *aa, mpz_t *bb) { mpz_t a, b, t; mpz_abs(mrb, &a, aa); mpz_abs(mrb, &b, bb); mpz_init(mrb, &t); while (b.sn != 0) { mpz_mod(mrb, &t, &a, &b); mpz_set(mrb, &a, &b); mpz_set(mrb, &b, &t); } trim(&a); mpz_move(mrb, gg, &a); mpz_clear(mrb, &b); mpz_clear(mrb, &t); } #endif static size_t mpz_bits(const mpz_t *x) { if (x->sz == 0 || x->sn == 0) return 0; size_t limb_bits = sizeof(mp_limb) * 8; // Get the most significant limb size_t i = x->sz - 1; mp_limb high = x->p[i]; // Number of bits = total full limbs + significant bits in top limb return i * limb_bits + (limb_bits - lzb(high)); } static void mpz_sqrt(mrb_state *mrb, mpz_t *z, mpz_t *x) { mrb_assert(x->sn >= 0); if (x->sz == 0) { // sqrt(0) = 0 z->sn = 0; z->sz = 0; return; } // Estimate initial value: 1 << (bit_length(x) / 2) size_t xbits = mpz_bits(x); size_t sbit = (xbits + 1) / 2; mpz_t s, t; mpz_init_set_int(mrb, &s, 1); mpz_mul_2exp(mrb, &s, &s, sbit); mpz_init(mrb, &t); // Iteratively refine s using Newton-Raphson method: // s = (s + x / s) / 2 for (;;) { mpz_mdiv(mrb, &t, x, &s); // t = x / s mpz_add(mrb, &t, &t, &s); // t = s + x/s mpz_div_2exp(mrb, &t, &t, 1); // t = (s + x/s) / 2 if (mpz_cmp(mrb, &t, &s) >= 0) { // Converged: t >= s break; } mpz_set(mrb, &s, &t); } mpz_move(mrb, z, &s); mpz_clear(mrb, &t); } /* --- mruby functions --- */ /* initialize mpz_t from RBigint (not need to clear) */ static void bint_as_mpz(struct RBigint *b, mpz_t *x) { x->p = RBIGINT_ARY(b); x->sz = RBIGINT_SIZE(b); x->sn = RBIGINT_SIGN(b); } static struct RBigint* bint_new(mrb_state *mrb, mpz_t *x) { struct RBigint *b = MRB_OBJ_ALLOC(mrb, MRB_TT_BIGINT, mrb->integer_class); if (x->sz <= RBIGINT_EMBED_SIZE_MAX) { RBIGINT_SET_EMBED_SIZE(b, x->sz); RBIGINT_SET_EMBED_SIGN(b, x->sn); if (x->p) memcpy(RBIGINT_EMBED_ARY(b), x->p, x->sz*sizeof(mp_limb)); mpz_clear(mrb, x); } else { RBIGINT_SET_HEAP(b); b->as.heap = *x; } return b; } static struct RBigint* bint_new_int(mrb_state *mrb, mrb_int n) { mpz_t x; mpz_init_set_int(mrb, &x, n); return bint_new(mrb, &x); } mrb_value mrb_bint_new_int(mrb_state *mrb, mrb_int x) { struct RBigint *b = bint_new_int(mrb, x); return mrb_obj_value(b); } #ifdef MRB_INT32 mrb_value mrb_bint_new_int64(mrb_state *mrb, int64_t n) { mpz_t x; mpz_set_int64(mrb, &x, n); struct RBigint *b = bint_new(mrb, &x); return mrb_obj_value(b); } #endif mrb_value mrb_bint_new_uint64(mrb_state *mrb, uint64_t x) { mpz_t z; mpz_init(mrb, &z); mpz_set_uint64(mrb, &z, x); struct RBigint *b = bint_new(mrb ,&z); return mrb_obj_value(b); } mrb_value mrb_bint_new_str(mrb_state *mrb, const char *x, mrb_int len, mrb_int base) { mpz_t z; int sn = 1; if (base < 0) { base = -base; sn = -1; } mrb_assert(2 <= base && base <= 36); mpz_init_set_str(mrb, &z, x, len, base); if (sn < 0) { z.sn = sn; } struct RBigint *b = bint_new(mrb, &z); return mrb_obj_value(b); } static mrb_value bint_norm(mrb_state *mrb, struct RBigint *b) { mrb_int i; mpz_t a; bint_as_mpz(b, &a); if (mpz_get_int(&a, &i)) { return mrb_int_value(mrb, i); } return mrb_obj_value(b); } void mrb_gc_free_bint(mrb_state *mrb, struct RBasic *x) { struct RBigint *b = (struct RBigint*)x; if (!RBIGINT_EMBED_P(b)) { mpz_clear(mrb, &b->as.heap); } } #ifndef MRB_NO_FLOAT mrb_value mrb_bint_new_float(mrb_state *mrb, mrb_float x) { /* x should not be NaN nor Infinity */ mrb_assert(x == x && x != x * 0.5); if (FIXABLE_FLOAT(x)) { return mrb_int_value(mrb, (mrb_int)x); } int sn; if (x < 0.0) { x = -x; sn = -1; } else { sn = 1; } if (x < 1.0) { return mrb_fixnum_value(0); } mpz_t r; mpz_init(mrb, &r); r.sn = sn; mrb_float b = (double)DIG_BASE; mrb_float bi = 1.0 / b; size_t rn; for (rn = 1; x >= b; rn++) x *= bi; mpz_realloc(mrb, &r, rn); mp_limb *rp = r.p; for (size_t i=rn-1;;i--) { mp_limb f = LOW((mp_limb)x); x -= f; mrb_assert(x < 1.0); rp[i] = f; if (i == 0) break; } return bint_norm(mrb, bint_new(mrb, &r)); } mrb_float mrb_bint_as_float(mrb_state *mrb, mrb_value self) { mpz_t m; bint_as_mpz(RBIGINT(self), &m); mp_limb *d = m.p + m.sz; mrb_float val = 0; while (d-- > m.p) { val = val * DIG_BASE + *d; } if (m.sn < 0) { val = -val; } return val; } #endif mrb_value mrb_as_bint(mrb_state *mrb, mrb_value x) { if (mrb_bigint_p(x)) return x; return mrb_bint_new_int(mrb, mrb_as_int(mrb, x)); } mrb_int mrb_bint_as_int(mrb_state *mrb, mrb_value x) { mpz_t m; mrb_int i; bint_as_mpz(RBIGINT(x), &m); if (!mpz_get_int(&m, &i)) { mrb_raise(mrb, E_RANGE_ERROR, "integer out of range"); } return i; } #ifdef MRB_INT32 int64_t mrb_bint_as_int64(mrb_state *mrb, mrb_value x) { mpz_t m; bint_as_mpz(RBIGINT(x), &m); uint64_t u = 0; size_t len = digits(&m); if (len*sizeof(mp_limb) > sizeof(uint64_t)) { out_of_range: mrb_raise(mrb, E_RANGE_ERROR, "integer out of range"); } for (size_t i=len-1; ; i--) { u <<= DIG_SIZE; u |= m.p[i]; if (i==0) break; } if (u > INT64_MAX) goto out_of_range; if (m.sn < 0) return -(int64_t)u; return (int64_t)u; } #endif uint64_t mrb_bint_as_uint64(mrb_state *mrb, mrb_value x) { mpz_t m; bint_as_mpz(RBIGINT(x), &m); uint64_t u = 0; size_t len = digits(&m); if (m.sn < 0 || len*sizeof(mp_limb) > sizeof(uint64_t)) { mrb_raise(mrb, E_RANGE_ERROR, "integer out of range"); } for (size_t i=len-1; ; i--) { u <<= DIG_SIZE; u |= m.p[i]; if (i==0) break; } return u; } static mrb_bool int_fit_limb_p(mrb_int i) { #if DIG_SIZE == 32 # ifdef MRB_INT64 // if mp_limb is int32_t return (i > INT32_MIN && i <= INT32_MAX); # else // if mp_limb is also int32_t, it always fits return TRUE; # endif #else /* if DIG_SIZE == 16 */ // if mp_limb is int16_t return (i > INT16_MIN && i <= INT16_MAX); #endif } /* unnormalize version of mrb_bint_add */ mrb_value mrb_bint_add_n(mrb_state *mrb, mrb_value x, mrb_value y) { mpz_t a, b, z; bint_as_mpz(RBIGINT(x), &a); if (mrb_integer_p(y)) { mrb_int n = mrb_integer(y); if (int_fit_limb_p(n)) { mpz_init_set(mrb, &z, &a); if ((n > 0) ^ (z.sn > 0)) { mpz_sub_int(mrb, &z, n<0 ? -n : n); } else { mpz_add_int(mrb, &z, n<0 ? -n : n); } struct RBigint *v = bint_new(mrb, &z); return mrb_obj_value(v); } } y = mrb_as_bint(mrb, y); bint_as_mpz(RBIGINT(y), &b); mpz_init(mrb, &z); mpz_add(mrb, &z, &a, &b); struct RBigint *v = bint_new(mrb, &z); return mrb_obj_value(v); } mrb_value mrb_bint_add(mrb_state *mrb, mrb_value x, mrb_value y) { #ifndef MRB_NO_FLOAT if (mrb_float_p(y)) { mrb_float v1 = mrb_bint_as_float(mrb, x); mrb_float v2 = mrb_float(y); return mrb_float_value(mrb,v1+v2); } #endif x = mrb_bint_add_n(mrb, x, y); return bint_norm(mrb, RBIGINT(x)); } /* unnormalize version of mrb_bint_sub */ mrb_value mrb_bint_sub_n(mrb_state *mrb, mrb_value x, mrb_value y) { mpz_t a, b, z; bint_as_mpz(RBIGINT(x), &a); if (mrb_integer_p(y)) { mrb_int n = mrb_integer(y); if (int_fit_limb_p(n)) { mpz_init_set(mrb, &z, &a); if ((n > 0) ^ (z.sn > 0)) { mpz_add_int(mrb, &z, n<0 ? -n : n); } else { mpz_sub_int(mrb, &z, n<0 ? -n : n); } struct RBigint *v = bint_new(mrb, &z); return mrb_obj_value(v); } } y = mrb_as_bint(mrb, y); bint_as_mpz(RBIGINT(y), &b); mpz_init(mrb, &z); mpz_sub(mrb, &z, &a, &b); struct RBigint *v = bint_new(mrb, &z); return mrb_obj_value(v); } mrb_value mrb_bint_sub(mrb_state *mrb, mrb_value x, mrb_value y) { #ifndef MRB_NO_FLOAT if (mrb_float_p(y)) { mrb_float v1 = mrb_bint_as_float(mrb, x); mrb_float v2 = mrb_float(y); return mrb_float_value(mrb,v1-v2); } #endif x = mrb_bint_sub_n(mrb, x, y); return bint_norm(mrb, RBIGINT(x)); } static struct RBigint* bint_mul(mrb_state *mrb, mrb_value x, mrb_value y) { mpz_t a, b, z; y = mrb_as_bint(mrb, y); bint_as_mpz(RBIGINT(x), &a); bint_as_mpz(RBIGINT(y), &b); mpz_init(mrb, &z); mpz_mul(mrb, &z, &a, &b); return bint_new(mrb, &z); } mrb_value mrb_bint_mul(mrb_state *mrb, mrb_value x, mrb_value y) { if (mrb_integer_p(y)) { if (mrb_integer(y) == 0) return mrb_fixnum_value(0); if (mrb_integer(y) == 1) return bint_norm(mrb, RBIGINT(x)); } #ifndef MRB_NO_FLOAT if (mrb_float_p(y)) { mrb_float v1 = mrb_bint_as_float(mrb, x); mrb_float v2 = mrb_float(y); return mrb_float_value(mrb,v1*v2); } #endif return bint_norm(mrb, bint_mul(mrb, x, y)); } mrb_value mrb_bint_mul_n(mrb_state *mrb, mrb_value x, mrb_value y) { struct RBigint *b = bint_mul(mrb, x, y); return mrb_obj_value(b); } mrb_value mrb_bint_div(mrb_state *mrb, mrb_value x, mrb_value y) { if (mrb_integer_p(y)) { if (mrb_integer(y) == 0) mrb_int_zerodiv(mrb); if (mrb_integer(y) == 1) return bint_norm(mrb, RBIGINT(x)); } #ifndef MRB_NO_FLOAT if (mrb_float_p(y)) { mrb_float v1 = mrb_bint_as_float(mrb, x); mrb_float v2 = mrb_float(y); return mrb_float_value(mrb,v1*v2); } #endif mpz_t a, b, z; y = mrb_as_bint(mrb, y); bint_as_mpz(RBIGINT(y), &b); if (zero_p(&b) || uzero_p(&b)) { mrb_int_zerodiv(mrb); } bint_as_mpz(RBIGINT(x), &a); mpz_init(mrb, &z); mpz_mdiv(mrb, &z, &a, &b); return bint_norm(mrb, bint_new(mrb, &z)); } mrb_value mrb_bint_add_ii(mrb_state *mrb, mrb_int x, mrb_int y) { mpz_t a, b, z; mpz_init_set_int(mrb, &a, x); mpz_init_set_int(mrb, &b, y); mpz_init(mrb, &z); mpz_add(mrb, &z, &a, &b); mpz_clear(mrb, &a); mpz_clear(mrb, &b); return bint_norm(mrb, bint_new(mrb, &z)); } mrb_value mrb_bint_sub_ii(mrb_state *mrb, mrb_int x, mrb_int y) { mpz_t a, b, z; mpz_init_set_int(mrb, &a, x); mpz_init_set_int(mrb, &b, y); mpz_init(mrb, &z); mpz_sub(mrb, &z, &a, &b); mpz_clear(mrb, &a); mpz_clear(mrb, &b); return bint_norm(mrb, bint_new(mrb, &z)); } mrb_value mrb_bint_mul_ii(mrb_state *mrb, mrb_int x, mrb_int y) { mpz_t a, b, z; mpz_init_set_int(mrb, &a, x); mpz_init_set_int(mrb, &b, y); mpz_init(mrb, &z); mpz_mul(mrb, &z, &a, &b); mpz_clear(mrb, &a); mpz_clear(mrb, &b); return bint_norm(mrb, bint_new(mrb, &z)); } mrb_value mrb_bint_mod(mrb_state *mrb, mrb_value x, mrb_value y) { #ifndef MRB_NO_FLOAT if (mrb_float_p(y)) { mrb_float v1 = mrb_bint_as_float(mrb, x); mrb_float v2 = mrb_float(y); return mrb_float_value(mrb, fmod(v1, v2)); } #endif if (mrb_integer_p(y) && mrb_integer(y) == 0) { mrb_int_zerodiv(mrb); } mpz_t a, b, z; y = mrb_as_bint(mrb, y); bint_as_mpz(RBIGINT(y), &b); if (zero_p(&b) || uzero_p(&b)) { mrb_int_zerodiv(mrb); } bint_as_mpz(RBIGINT(x), &a); mpz_init(mrb, &z); mpz_mmod(mrb, &z, &a, &b); return bint_norm(mrb, bint_new(mrb, &z)); } mrb_value mrb_bint_rem(mrb_state *mrb, mrb_value x, mrb_value y) { /* called from mrbgems/mruby-numeric-ext/src/numeric_ext.c */ /* y should not be float */ if (mrb_integer_p(y) && mrb_integer(y) == 0) { mrb_int_zerodiv(mrb); } mpz_t a, b, z; y = mrb_as_bint(mrb, y); bint_as_mpz(RBIGINT(y), &b); if (zero_p(&b) || uzero_p(&b)) { mrb_int_zerodiv(mrb); } bint_as_mpz(RBIGINT(x), &a); mpz_init(mrb, &z); mpz_mod(mrb, &z, &a, &b); return bint_norm(mrb, bint_new(mrb, &z)); } mrb_value mrb_bint_divmod(mrb_state *mrb, mrb_value x, mrb_value y) { /* called from src/numeric.c */ /* y should not be float */ if (mrb_integer_p(y) && mrb_integer(y) == 0) { mrb_int_zerodiv(mrb); } y = mrb_as_bint(mrb, y); mpz_t a, b, c, d; bint_as_mpz(RBIGINT(y), &b); if (zero_p(&b) || uzero_p(&b)) { mrb_int_zerodiv(mrb); } bint_as_mpz(RBIGINT(x), &a); mpz_init(mrb, &c); mpz_init(mrb, &d); mpz_mdivmod(mrb, &c, &d, &a, &b); return mrb_assoc_new(mrb, bint_norm(mrb, bint_new(mrb, &c)), bint_norm(mrb, bint_new(mrb, &d))); } mrb_int mrb_bint_cmp(mrb_state *mrb, mrb_value x, mrb_value y) { #ifndef MRB_NO_FLOAT if (mrb_float_p(y)) { mrb_float v1 = mrb_bint_as_float(mrb, x); mrb_float v2 = mrb_float(y); if (v1 == v2) return 0; if (v1 > v2) return 1; return -1; } #endif mpz_t a; bint_as_mpz(RBIGINT(x), &a); if (!mrb_bigint_p(y)) { if (!mrb_integer_p(y)) return -2; /* type mismatch */ mrb_int i1, i2 = mrb_integer(y); if (mpz_get_int(&a, &i1)) { if (i1 == i2) return 0; if (i1 > i2) return 1; return -1; } if (a.sn > 0) return 1; return -1; } mpz_t b; bint_as_mpz(RBIGINT(y), &b); return mpz_cmp(mrb, &a, &b); } mrb_value mrb_bint_pow(mrb_state *mrb, mrb_value x, mrb_value y) { mpz_t a; bint_as_mpz(RBIGINT(x), &a); switch (mrb_type(y)) { case MRB_TT_INTEGER: break; case MRB_TT_BIGINT: mrb_raise(mrb, E_TYPE_ERROR, "too big power"); default: mrb_raisef(mrb, E_TYPE_ERROR, "%Y cannot be convert to integer", y); } mpz_t z; mpz_init(mrb, &z); mpz_pow(mrb, &z, &a, mrb_integer(y)); struct RBigint *b = bint_new(mrb, &z); return mrb_obj_value(b); } mrb_value mrb_bint_powm(mrb_state *mrb, mrb_value x, mrb_value exp, mrb_value mod) { mpz_t a, b, c, z; bint_as_mpz(RBIGINT(x), &a); if (mrb_integer_p(mod)) { mrb_int m = mrb_integer(mod); if (m == 0) mrb_int_zerodiv(mrb); mpz_init_set_int(mrb, &c, m); } else { mod = mrb_as_bint(mrb, mod); bint_as_mpz(RBIGINT(mod), &c); if (zero_p(&c) || uzero_p(&c)) { mrb_int_zerodiv(mrb); } } mpz_init(mrb, &z); if (mrb_bigint_p(exp)) { bint_as_mpz(RBIGINT(exp), &b); if (b.sn < 0) goto raise; mpz_powm(mrb, &z, &a, &b, &c); } else { mrb_int e = mrb_integer(exp); if (e < 0) goto raise; mpz_powm_i(mrb, &z, &a, e, &c); } if (mrb_integer_p(mod)) mpz_clear(mrb, &c); return bint_norm(mrb, bint_new(mrb, &z)); raise: if (mrb_integer_p(mod)) mpz_clear(mrb, &c); mrb_raise(mrb, E_ARGUMENT_ERROR, "int.pow(n,m): n must be positive"); /* not reached */ return mrb_nil_value(); } mrb_value mrb_bint_to_s(mrb_state *mrb, mrb_value x, mrb_int base) { mpz_t a; bint_as_mpz(RBIGINT(x), &a); if (zero_p(&a) || uzero_p(&a)) { return mrb_str_new_lit(mrb, "0"); } size_t len = mpz_sizeinbase(&a, (int)base); if (sizeof(size_t) >= sizeof(mrb_int) && MRB_INT_MAX-2 < len) { mrb_raise(mrb, E_ARGUMENT_ERROR, "too long string from Integer"); } mrb_value str = mrb_str_new(mrb, NULL, len+2); mpz_get_str(mrb, RSTRING_PTR(str), len, base, &a); RSTR_SET_LEN(RSTRING(str), strlen(RSTRING_PTR(str))); return str; } mrb_value mrb_bint_and(mrb_state *mrb, mrb_value x, mrb_value y) { mpz_t a, b, c; bint_as_mpz(RBIGINT(x), &a); if (mrb_integer_p(y)) { mrb_int z = mrb_integer(y); if (z == 0) return mrb_fixnum_value(0); if (z > 0 && (mp_dbl_limb)z < DIG_BASE) { z &= a.p[0]; return mrb_int_value(mrb, z); } if (z == -1) return x; } y = mrb_as_bint(mrb, y); bint_as_mpz(RBIGINT(y), &b); if (zero_p(&a) || zero_p(&b)) return mrb_fixnum_value(0); mpz_init(mrb, &c); mpz_and(mrb, &c, &a, &b); return bint_norm(mrb, bint_new(mrb, &c)); } mrb_value mrb_bint_or(mrb_state *mrb, mrb_value x, mrb_value y) { mpz_t a, b, c; bint_as_mpz(RBIGINT(x), &a); if (mrb_integer_p(y)) { mrb_int z = mrb_integer(y); if (z == 0) return x; if (z == -1) return y; } y = mrb_as_bint(mrb, y); bint_as_mpz(RBIGINT(y), &b); if (zero_p(&a)) return y; if (zero_p(&b)) return x; mpz_init(mrb, &c); mpz_or(mrb, &c, &b, &a); return bint_norm(mrb, bint_new(mrb, &c)); } mrb_value mrb_bint_xor(mrb_state *mrb, mrb_value x, mrb_value y) { mpz_t a, b, c; bint_as_mpz(RBIGINT(x), &a); if (mrb_integer_p(y) && a.sn > 0) { mrb_int z = mrb_integer(y); if (z == 0) return x; if (0 < z && (mp_dbl_limb)z < DIG_BASE) { mpz_init_set(mrb, &c, &a); c.p[0] ^= z; return bint_norm(mrb, bint_new(mrb, &c)); } } y = mrb_as_bint(mrb, y); bint_as_mpz(RBIGINT(y), &b); if (zero_p(&a)) return y; if (zero_p(&b)) return x; mpz_init(mrb, &c); mpz_xor(mrb, &c, &a, &b); return bint_norm(mrb, bint_new(mrb, &c)); } mrb_value mrb_bint_neg(mrb_state *mrb, mrb_value x) { mpz_t a, b; bint_as_mpz(RBIGINT(x), &a); mpz_init(mrb, &b); mpz_neg(mrb, &b, &a); struct RBigint *b2 = bint_new(mrb, &b); /* no normalization */ return mrb_obj_value(b2); } mrb_value mrb_bint_rev(mrb_state *mrb, mrb_value x) { mpz_t a, b; bint_as_mpz(RBIGINT(x), &a); mpz_init(mrb, &b); mpz_neg(mrb, &b, &a); mpz_sub_int(mrb, &b, 1); return bint_norm(mrb, bint_new(mrb, &b)); } mrb_value mrb_bint_lshift(mrb_state *mrb, mrb_value x, mrb_int width) { mpz_t a, z; bint_as_mpz(RBIGINT(x), &a); mpz_init(mrb, &z); if (width < 0) { mpz_div_2exp(mrb, &z, &a, -width); } else { mpz_mul_2exp(mrb, &z, &a, width); } return bint_norm(mrb, bint_new(mrb, &z)); } mrb_value mrb_bint_rshift(mrb_state *mrb, mrb_value x, mrb_int width) { mpz_t a, z; bint_as_mpz(RBIGINT(x), &a); mpz_init(mrb, &z); if (width < 0) { mpz_mul_2exp(mrb, &z, &a, -width); } else { mpz_div_2exp(mrb, &z, &a, width); } return bint_norm(mrb, bint_new(mrb, &z)); } void mrb_bint_copy(mrb_state *mrb, mrb_value x, mrb_value y) { mpz_t a, b; bint_as_mpz(RBIGINT(x), &a); bint_as_mpz(RBIGINT(y), &b); mpz_init_set(mrb, &a, &b); } size_t mrb_bint_memsize(mrb_value x) { mpz_t z; bint_as_mpz(RBIGINT(x), &z); return z.sz * sizeof(mp_limb); } mrb_value mrb_bint_sqrt(mrb_state *mrb, mrb_value x) { mpz_t a; bint_as_mpz(RBIGINT(x), &a); if (a.sn < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "square root of negative number"); } mpz_t z; mpz_init(mrb, &z); mpz_sqrt(mrb, &z, &a); return bint_norm(mrb, bint_new(mrb, &z)); } mrb_value mrb_bint_hash(mrb_state *mrb, mrb_value x) { mpz_t z; bint_as_mpz(RBIGINT(x), &z); uint32_t hash = mrb_byte_hash((uint8_t*)z.p, z.sz*sizeof(mp_limb)); hash = mrb_byte_hash_step((uint8_t*)&z.sn, sizeof(z.sn), hash); return mrb_int_value(mrb, hash); } /* to be used only from mruby-sprintf */ mrb_value mrb_bint_2comp(mrb_state *mrb, mrb_value x) { mpz_t a, z; bint_as_mpz(RBIGINT(x), &a); mpz_init(mrb, &z); mrb_assert(a.sn < 0); size_t size = a.sz; mpz_realloc(mrb, &z, size); mp_limb *ds = a.p; mp_limb *dd = z.p; char carry = 1; for (size_t i=0; i * I'm already aware that fgmp is considerably slower than gmp * * CREDITS: * Paul Rouse - generic bugfixes, mpz_sqrt and * mpz_sqrtrem, and modifications to get fgmp to compile on a system * with int and long of different sizes (specifically MS-DOS,286 compiler) * Also see the file "notes" included with the fgmp distribution, for * more credits. * * VERSION 1.0 - beta 5 */ #include #if defined(MRB_INT32) && defined(_WIN32) && !defined(MRB_NO_MPZ64BIT) #define MRB_NO_MPZ64BIT #endif #ifdef MRB_NO_MPZ64BIT typedef uint16_t mp_limb; typedef uint32_t mp_dbl_limb; typedef int32_t mp_dbl_limb_signed; #define MPZ_DIG_SIZE 16 #else typedef uint32_t mp_limb; typedef uint64_t mp_dbl_limb; typedef int64_t mp_dbl_limb_signed; #define MPZ_DIG_SIZE 32 #endif #define RBIGINT_EMBED_SIZE_MAX ((sizeof(void*) * 3) / sizeof(mp_limb)) typedef struct _mpz_t { mp_limb *p; short sn; size_t sz; } mpz_t; struct RBigint { MRB_OBJECT_HEADER; union { mpz_t heap; mp_limb ary[RBIGINT_EMBED_SIZE_MAX]; } as; }; #define RBIGINT(v) ((struct RBigint*)mrb_ptr(v)) /* * flags of struct RBigint * * 6..: UNUSED * 4..5: sign flags * 00: negative (<--> -1) * 01: zero (<--> 0) * 10: positive (<--> +1) * 11: UNUSED * 0..3: size of embedded array; 15 means used with heap */ #define RBIGINT_EMBED_SIZE_MASK 0x0f #define RBIGINT_EMBED_SIZE_OVER RBIGINT_EMBED_SIZE_MASK #define RBIGINT_EMBED_SIZE_SHIFT 0 #define RBIGINT_EMBED_SIGN_MASK 0x03 #define RBIGINT_EMBED_SIGN_SHIFT 4 #define RBIGINT_ARY(m) (RBIGINT_EMBED_P(m) ? RBIGINT_EMBED_ARY(m) : RBIGINT_HEAP_ARY(m)) #define RBIGINT_SIGN(m) (RBIGINT_EMBED_P(m) ? RBIGINT_EMBED_SIGN(m) : RBIGINT_HEAP_SIGN(m)) #define RBIGINT_SIZE(m) (RBIGINT_EMBED_P(m) ? RBIGINT_EMBED_SIZE(m) : RBIGINT_HEAP_SIZE(m)) #define RBIGINT_HEAP_ARY(m) ((m)->as.heap.p) #define RBIGINT_HEAP_SIGN(m) ((m)->as.heap.sn) #define RBIGINT_HEAP_SIZE(m) ((m)->as.heap.sz) #define RBIGINT_SET_HEAP(m) do { \ (m)->flags |= RBIGINT_EMBED_SIZE_OVER << RBIGINT_EMBED_SIZE_SHIFT; \ } while (0) #define RBIGINT_SET_HEAP_SIGN(m, s) do { \ (m)->as.heap.sn = (s); \ } while (0) #define RBIGINT_SET_HEAP_SIZE(m, s) do { \ (m)->as.heap.sz = (s); \ } while (0) #define RBIGINT_EMBED_P(m) ((((m)->flags >> RBIGINT_EMBED_SIZE_SHIFT) & RBIGINT_EMBED_SIZE_MASK) < RBIGINT_EMBED_SIZE_OVER) #define RBIGINT_EMBED_ARY(m) ((m)->as.ary) #define RBIGINT_EMBED_SIGN(m) ((short)(((m)->flags >> RBIGINT_EMBED_SIGN_SHIFT) & RBIGINT_EMBED_SIGN_MASK) - 1) #define RBIGINT_EMBED_SIZE(m) (size_t)(((m)->flags >> RBIGINT_EMBED_SIZE_SHIFT) & RBIGINT_EMBED_SIZE_MASK) #define RBIGINT_SET_EMBED_ZERO(m) do { \ (m)->flags &= ~(RBIGINT_EMBED_SIZE_MASK << RBIGINT_EMBED_SIZE_SHIFT); \ } while (0) #define RBIGINT_SET_EMBED_SIGN(m, s) do { \ (m)->flags = ((((s) + 1) & RBIGINT_EMBED_SIGN_MASK) << RBIGINT_EMBED_SIGN_SHIFT) | \ ((m)->flags & ~(RBIGINT_EMBED_SIGN_MASK << RBIGINT_EMBED_SIGN_SHIFT)); \ } while (0) #define RBIGINT_SET_EMBED_SIZE(m, s) do { \ size_t s_tmp = (s); \ mrb_assert((s_tmp) <= RBIGINT_EMBED_SIZE_MAX); \ RBIGINT_SET_EMBED_ZERO(m); \ (m)->flags |= (s_tmp) << RBIGINT_EMBED_SIZE_SHIFT; \ } while (0) mrb_static_assert_object_size(struct RBigint); #endif /* MRUBY_BIGINT_H */ nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bigint/PaxHeaders/test0000644000000000000000000000013215077107334022634 xustar0030 mtime=1761382108.662301647 30 atime=1761382109.797298366 30 ctime=1761382108.662301647 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bigint/test/0000755000175100017510000000000015077107334023301 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bigint/test/PaxHeaders/bigint.rb0000644000000000000000000000013215077107276024517 xustar0030 mtime=1761382078.105420634 30 atime=1761382080.126411389 30 ctime=1761382108.662301647 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bigint/test/bigint.rb0000644000175100017510000001140015077107276025103 0ustar00runnerrunnerassert 'Bigint basic' do n = 1<<65 assert_equal 36893488147419103232, n end assert 'Bigint +' do n = 1<<65 assert_equal 36893488147419103232, n + 0 assert_equal 36893488147419104229, n + 997 assert_equal 36893488147419102235, n + -997 assert_equal(-36893488147419102235, -n + 997) assert_equal(-36893488147419104229, -n + -997) assert_equal 73786976294838206464, n + n assert_equal 0, n + -n assert_equal 0, -n + n assert_equal(-73786976294838206464, -n + -n) assert_equal 36893488147419104229, 997 + n assert_equal 36893488147419102235, -997 + n end assert 'Bigint -' do n = 1<<65 assert_equal 36893488147419103232, n - 0 assert_equal 36893488147419102235, n - 997 assert_equal 36893488147419104229, n - -997 assert_equal(-36893488147419104229, -n - 997) assert_equal(-36893488147419102235, -n - -997) assert_equal 0, n - n assert_equal(-36893488147419104229, -997 - n) assert_equal(-36893488147419102235, 997 - n) assert_equal(-36893488147419104229, -997 - n) end assert 'Bigint *' do n = 1<<65 assert_equal 0, n * 0 assert_equal 36782807682976845922304, n * 997 assert_equal(-36782807682976845922304, n * -997) assert_equal 36782807682976845922304, 997 * n assert_equal(-36782807682976845922304, -997 * n) assert_equal 1361129467683753853853498429727072845824, n * n assert_equal(-1361129467683753853853498429727072845824, -n * n) assert_equal(-1361129467683753853853498429727072845824, n * -n) assert_equal 1361129467683753853853498429727072845824, -n * -n end assert 'Bigint /' do n = 1<<65 assert_equal 37004501652376231, n / 997 assert_equal(-37004501652376232, n / -997) assert_equal(-37004501652376232, -n / 997) assert_equal 0, 997 / n assert_equal 2, 73786976294838206464 / n assert_equal 1, n / n assert_equal(-1, -n / n) assert_equal(-1, n / -n) assert_equal 1, -n / -n end assert 'Bigint mod' do n = 1<<65 assert_equal 925, n % 997 assert_equal(-72, n % -997) assert_equal 72, -n % 997 assert_equal(-925, -n % -997) assert_equal 0, n % n assert_equal 997, 997 % n assert_equal 36893488147419102235, -997 % n assert_equal(-36893488147419102235, 997 % -n) assert_equal(-997, -997 % -n) assert_equal 18446744073709551616, (n / 2) % n end assert 'Bigint divmod' do n = 1<<65 assert_equal [37004501652376231, 925], n.divmod(997) assert_equal [-37004501652376232, -72], n.divmod(-997) assert_equal [-37004501652376232, 72], (-n).divmod(997) assert_equal [37004501652376231, -925], (-n).divmod(-997) assert_equal [1, 0], n.divmod(n) assert_equal [0, 997], 997.divmod(n) assert_equal [-1, 36893488147419102235], (-997).divmod(n) assert_equal [-1, -36893488147419102235], 997.divmod(-n) assert_equal [0, -997], (-997).divmod(-n) assert_equal [0, 18446744073709551616], (n / 2).divmod(n) end assert 'Bigint &' do n = 1<<65 assert_equal 0, n & 0 assert_equal 0, 0 & n assert_equal 0, n & 1 assert_equal 1, (n + 3) & 1 assert_equal 2, (n + 3) & 2 assert_equal 3, (n + 3) & 3 assert_equal n, n & n assert_equal 36893488147419103232, n & -1 assert_equal 36893488147419103232, -1 & n end assert 'Bigint |' do n = 1<<65 assert_equal 36893488147419103232, n | 0 assert_equal 36893488147419103232, 0 | n assert_equal 36893488147419103233, n | 1 assert_equal 36893488147419103233, 1 | n assert_equal 36893488147419103235, n | 3 assert_equal 36893488147419103232, n | n assert_equal(-1, n | -1) end assert 'Bigint ^' do n = 1<<65 assert_equal 36893488147419103232, n ^ 0 assert_equal 36893488147419103233, n ^ 1 assert_equal 36893488147419103235, 3 ^ n assert_equal 0, n ^ n assert_equal(-36893488147419103233, n ^ -1) assert_equal(-36893488147419103231, -n ^ 1) end assert 'Bigint to_s' do n = 1197857166996989179607278372168909873645893814254642585755536286462800958278984531968 assert_equal n, "11978_571669_96989179607278372168909873645893814254642585755536286462800958278984531968".to_i assert_equal(-n, "-11978_571669_96989179607278372168909873645893814254642585755536286462800958278984531968".to_i) n = 0x1197857166996989179607278372168909873645893814254642585755536286462800958278984531968 assert_equal n, "1197857166996989179607278372168909873645893814254642585755536286462800958278984531968".to_i(16) n = 10 ** 20 assert_equal "100000000000000000000", n.to_s end assert 'Bigint pow' do n = 18446744073709551616 assert_equal n, 2 ** 64 assert_equal n, 1 << 64 assert_equal 2, n >> 63 n = 1<<65 assert_equal n, n ** 1 assert_equal 1, n ** 0 assert_equal 1361129467683753853853498429727072845824, n ** 2 # assert_equal 193128586, n.pow(n, 1234567890) # assert_equal(-1041439304, n.pow(n, -1234567890)) end assert 'Bigint abs' do n = 1<<65 assert_equal 36893488147419103232, n.abs assert_equal 36893488147419103232, (-n).abs end nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/full-core.gembox0000644000000000000000000000013215077107276022422 xustar0030 mtime=1761382078.104420639 30 atime=1761382080.125411393 30 ctime=1761382108.824301179 nghttp2-1.68.0/third-party/mruby/mrbgems/full-core.gembox0000644000175100017510000000031215077107276023006 0ustar00runnerrunnerMRuby::GemBox.new do |conf| Dir.glob("#{root}/mrbgems/mruby-*/mrbgem.rake") do |x| g = File.basename(File.dirname(x)) conf.gem :core => g unless g =~ /^mruby-(?:bin-debugger|test)$/ end end nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/default-no-stdio.gembox0000644000000000000000000000013215077107276023710 xustar0030 mtime=1761382078.104420639 30 atime=1761382080.125411393 30 ctime=1761382108.614301786 nghttp2-1.68.0/third-party/mruby/mrbgems/default-no-stdio.gembox0000644000175100017510000000011415077107276024274 0ustar00runnerrunnerMRuby::GemBox.new do |conf| conf.gembox "stdlib" conf.gembox "math" end nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-sprintf0000644000000000000000000000013215077107334022066 xustar0030 mtime=1761382108.757301373 30 atime=1761382109.797298366 30 ctime=1761382108.757301373 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sprintf/0000755000175100017510000000000015077107334022533 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sprintf/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024265 xustar0030 mtime=1761382078.127420534 30 atime=1761382080.146411297 30 ctime=1761382108.757301373 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sprintf/mrbgem.rake0000644000175100017510000000025215077107276024654 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-sprintf') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'standard Kernel#sprintf method' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sprintf/PaxHeaders/mrblib0000644000000000000000000000013215077107334023335 xustar0030 mtime=1761382108.759301367 30 atime=1761382109.797298366 30 ctime=1761382108.759301367 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sprintf/mrblib/0000755000175100017510000000000015077107334024002 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sprintf/mrblib/PaxHeaders/string.rb0000644000000000000000000000013215077107276025252 xustar0030 mtime=1761382078.127420534 30 atime=1761382080.146411297 30 ctime=1761382108.759301367 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sprintf/mrblib/string.rb0000644000175100017510000000020315077107276025635 0ustar00runnerrunnerclass String def %(args) if args.is_a? Array sprintf(self, *args) else sprintf(self, args) end end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sprintf/PaxHeaders/test0000644000000000000000000000013015077107334023043 xustar0029 mtime=1761382108.75830137 30 atime=1761382109.797298366 29 ctime=1761382108.75830137 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sprintf/test/0000755000175100017510000000000015077107334023512 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sprintf/test/PaxHeaders/sprintf.rb0000644000000000000000000000013115077107276025140 xustar0030 mtime=1761382078.128420529 30 atime=1761382080.146411297 29 ctime=1761382108.75830137 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sprintf/test/sprintf.rb0000644000175100017510000000511715077107276025535 0ustar00runnerrunnerassert('sprintf invalid') do assert_raise(ArgumentError) { sprintf('%1$*d', 3) } assert_raise(ArgumentError) { sprintf('%1$.*d', 3) } end assert('String#%') do assert_equal "one=1", "one=%d" % 1 assert_equal "1 one", "%d %s" % [ 1, "one" ] assert_equal "123 < 456", "%{num} < %s" % { num: 123, str: "456" } assert_equal 15, ("%b" % (1<<14)).size skip unless Object.const_defined?(:Float) assert_equal "1.0", "%3.1f" % 1.01 assert_equal " 12345.12", "% 4.2f" % 12345.1234 assert_equal "12345.12", "%-4.2f" % 12345.12345 assert_equal "+12345.12", "%+4.2f" % 12345.1234 assert_equal "12345.12", "%04.2f" % 12345.12345 assert_equal "0012345.12", "%010.2f" % 12345.1234 end assert('String#% with inf') do skip unless Object.const_defined?(:Float) inf = Float::INFINITY assert_equal "Inf", "%f" % inf assert_equal "Inf", "%2f" % inf assert_equal "Inf", "%3f" % inf assert_equal " Inf", "%4f" % inf assert_equal " Inf", "%5f" % inf assert_equal "+Inf", "%+f" % inf assert_equal "+Inf", "%+2f" % inf assert_equal "+Inf", "%+3f" % inf assert_equal "+Inf", "%+4f" % inf assert_equal " +Inf", "%+5f" % inf assert_equal "Inf", "%-f" % inf assert_equal "Inf", "%-2f" % inf assert_equal "Inf", "%-3f" % inf assert_equal "Inf ", "%-4f" % inf assert_equal "Inf ", "%-5f" % inf assert_equal " Inf", "% f" % inf assert_equal " Inf", "% 2f" % inf assert_equal " Inf", "% 3f" % inf assert_equal " Inf", "% 4f" % inf assert_equal " Inf", "% 5f" % inf end assert('String#% with nan') do skip unless Object.const_defined?(:Float) nan = Float::NAN assert_equal "NaN", "%f" % nan assert_equal "NaN", "%2f" % nan assert_equal "NaN", "%3f" % nan assert_equal " NaN", "%4f" % nan assert_equal " NaN", "%5f" % nan assert_equal "+NaN", "%+f" % nan assert_equal "+NaN", "%+2f" % nan assert_equal "+NaN", "%+3f" % nan assert_equal "+NaN", "%+4f" % nan assert_equal " +NaN", "%+5f" % nan assert_equal "NaN", "%-f" % nan assert_equal "NaN", "%-2f" % nan assert_equal "NaN", "%-3f" % nan assert_equal "NaN ", "%-4f" % nan assert_equal "NaN ", "%-5f" % nan assert_equal " NaN", "% f" % nan assert_equal " NaN", "% 2f" % nan assert_equal " NaN", "% 3f" % nan assert_equal " NaN", "% 4f" % nan assert_equal " NaN", "% 5f" % nan end assert("String#% %b") do assert_equal("..10115", "%0b5" % -5) end assert("String#% %d") do assert_equal(" 10", "%4d" % 10) assert_equal("1000", "%4d" % 1000) assert_equal("10000", "%4d" % 10000) end assert("String#% invalid format") do assert_raise ArgumentError do "%?" % "" end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sprintf/PaxHeaders/src0000644000000000000000000000013215077107334022655 xustar0030 mtime=1761382108.761301361 30 atime=1761382109.797298366 30 ctime=1761382108.761301361 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sprintf/src/0000755000175100017510000000000015077107334023322 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sprintf/src/PaxHeaders/sprintf.c0000644000000000000000000000013215077107276024570 xustar0030 mtime=1761382078.128420529 30 atime=1761382080.146411297 30 ctime=1761382108.761301361 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sprintf/src/sprintf.c0000644000175100017510000010213015077107276025155 0ustar00runnerrunner/* ** sprintf.c - Kernel.#sprintf ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #define BIT_DIGITS(N) (((N)*146)/485 + 1) /* log2(10) =~ 146/485 */ #define BITSPERDIG MRB_INT_BIT #define EXTENDSIGN(n, l) (((~0U << (n)) >> (((n)*(l)) % BITSPERDIG)) & ~(~0U << (n))) mrb_value mrb_bint_2comp(mrb_state *mrb, mrb_value x); static char* remove_sign_bits(char *str, int base) { char *t; t = str; if (base == 16) { while (*t == 'f') { t++; } } else if (base == 8) { *t |= EXTENDSIGN(3, strlen(t)); while (*t == '7') { t++; } } else if (base == 2) { while (*t == '1') { t++; } } return t; } static char * mrb_uint_to_cstr(char *buf, size_t len, mrb_int num, int base) { char *b = buf + len - 1; const int mask = base-1; int shift; mrb_uint val = (uint64_t)num; if (num == 0) { buf[0] = '0'; buf[1] = '\0'; return buf; } switch (base) { case 16: shift = 4; break; case 8: shift = 3; break; case 2: shift = 1; break; default: return NULL; } *--b = '\0'; do { *--b = mrb_digitmap[(int)(val & mask)]; } while (val >>= shift); if (num < 0) { b = remove_sign_bits(b, base); } return b; } #define FNONE 0 #define FSHARP 1 #define FMINUS 2 #define FPLUS 4 #define FZERO 8 #define FSPACE 16 #define FWIDTH 32 #define FPREC 64 #define FPREC0 128 #ifndef MRB_NO_FLOAT static int fmt_float(char *buf, size_t buf_size, char fmt, int flags, int width, int prec, mrb_float f) { char sign = '\0'; int left_align = 0; int zero_pad = 0; if (flags & FSHARP) fmt |= 0x80; if (flags & FPLUS) sign = '+'; if (flags & FMINUS) left_align = 1; if (flags & FZERO) zero_pad = 1; if (flags & FSPACE) sign = ' '; int len = mrb_format_float(f, buf, buf_size, fmt, prec, sign); // buf[0] < '0' returns true if the first character is space, + or - // buf[1] < '9' matches a digit, and doesn't match when we get back +nan or +inf if (buf[0] < '0' && buf[1] <= '9' && zero_pad) { buf++; width--; len--; } if (*buf < '0' || *buf >= '9') { // For inf or nan, we don't want to zero pad. zero_pad = 0; } if (len >= width) { return len; } buf[width] = '\0'; if (left_align) { memset(&buf[len], ' ', width - len); return width; } memmove(&buf[width - len], buf, len); if (zero_pad) { memset(buf, '0', width - len); } else { memset(buf, ' ', width - len); } return width; } #endif #define CHECK(l) do { \ if (blen+(l) >= bsiz) {\ while (blen+(l) >= bsiz) {\ if (bsiz > MRB_INT_MAX/2) mrb_raise(mrb, E_ARGUMENT_ERROR, "too big specifier");\ bsiz*=2;\ }\ mrb_str_resize(mrb, result, bsiz);\ }\ buf = RSTRING_PTR(result);\ } while (0) #define PUSH(s, l) do { \ CHECK(l);\ memcpy(&buf[blen], s, l);\ blen += (mrb_int)(l);\ } while (0) #define FILL(c, l) do { \ CHECK(l);\ memset(&buf[blen], c, l);\ blen += (l);\ } while (0) static void check_next_arg(mrb_state *mrb, int posarg, int nextarg) { switch (posarg) { case -1: mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%d) mixed with numbered", nextarg); break; case -2: mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%d) mixed with named", nextarg); break; default: break; } } static void check_pos_arg(mrb_state *mrb, int posarg, mrb_int n) { if (posarg > 0) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%i) after unnumbered(%d)", n, posarg); } if (posarg == -2) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%i) after named", n); } if (n < 1) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid index - %i$", n); } } static void check_name_arg(mrb_state *mrb, int posarg, const char *name, size_t len) { if (posarg > 0) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%l after unnumbered(%d)", name, len, posarg); } if (posarg == -1) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%l after numbered", name, len); } } #define GETNEXTARG() (\ check_next_arg(mrb, posarg, nextarg),\ (posarg = nextarg++, GETNTHARG(posarg))) #define GETARG() (!mrb_undef_p(nextvalue) ? nextvalue : GETNEXTARG()) #define GETPOSARG(n) (\ check_pos_arg(mrb, posarg, n),\ (posarg = -1, GETNTHARG(n))) #define GETNTHARG(nth) \ ((nth >= argc) ? (mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments"), mrb_undef_value()) : argv[nth]) #define CHECKNAMEARG(name, len) (\ check_name_arg(mrb, posarg, name, len),\ posarg = -2) #define GETNUM(n, val) do { \ if (!(p = get_num(mrb, p, end, &(n)))) \ mrb_raise(mrb, E_ARGUMENT_ERROR, #val " too big"); \ } while(0) #define GETASTER(num) do { \ mrb_value tmp_v; \ t = p++; \ GETNUM(n, val); \ if (*p == '$') { \ tmp_v = GETPOSARG(n); \ } \ else { \ tmp_v = GETNEXTARG(); \ p = t; \ } \ num = (int)mrb_as_int(mrb, tmp_v); \ } while (0) static const char* get_num(mrb_state *mrb, const char *p, const char *end, int *valp) { char *e; mrb_int n; if (!mrb_read_int(p, end, &e, &n) || INT_MAX < n) { return NULL; } *valp = (int)n; return e; } static void get_hash(mrb_state *mrb, mrb_value *hash, mrb_int argc, const mrb_value *argv) { mrb_value tmp; if (!mrb_undef_p(*hash)) return; if (argc != 2) { mrb_raise(mrb, E_ARGUMENT_ERROR, "one hash required"); } tmp = mrb_check_hash_type(mrb, argv[1]); if (mrb_nil_p(tmp)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "one hash required"); } *hash = tmp; } static mrb_value mrb_str_format(mrb_state *mrb, mrb_int argc, const mrb_value *argv, mrb_value fmt) { const char *p, *end; char *buf; mrb_int blen; mrb_int bsiz; mrb_value result; int n; int width; int prec; int nextarg = 1; int posarg = 0; mrb_value nextvalue; mrb_value str; mrb_value hash = mrb_undef_value(); #define CHECK_FOR_WIDTH(f) \ if ((f) & FWIDTH) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "width given twice"); \ } \ if ((f) & FPREC0) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "width after precision"); \ } #define CHECK_FOR_FLAGS(f) \ if ((f) & FWIDTH) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "flag after width"); \ } \ if ((f) & FPREC0) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "flag after precision"); \ } argc++; argv--; mrb_ensure_string_type(mrb, fmt); p = RSTRING_PTR(fmt); end = p + RSTRING_LEN(fmt); blen = 0; bsiz = 120; result = mrb_str_new_capa(mrb, bsiz); buf = RSTRING_PTR(result); memset(buf, 0, bsiz); int ai = mrb_gc_arena_save(mrb); for (; p < end; p++) { const char *t; mrb_sym id = 0; int flags = FNONE; for (t = p; t < end && *t != '%'; t++) ; if (t + 1 == end) { /* % at the bottom */ mrb_raise(mrb, E_ARGUMENT_ERROR, "incomplete format specifier; use %% (double %) instead"); } PUSH(p, t - p); if (t >= end) goto sprint_exit; /* end of fmt string */ p = t + 1; /* skip '%' */ width = prec = -1; nextvalue = mrb_undef_value(); retry: switch (*p) { default: mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed format string - %%%c", *p); break; case ' ': CHECK_FOR_FLAGS(flags); flags |= FSPACE; p++; goto retry; case '#': CHECK_FOR_FLAGS(flags); flags |= FSHARP; p++; goto retry; case '+': CHECK_FOR_FLAGS(flags); flags |= FPLUS; p++; goto retry; case '-': CHECK_FOR_FLAGS(flags); flags |= FMINUS; p++; goto retry; case '0': CHECK_FOR_FLAGS(flags); flags |= FZERO; p++; goto retry; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': GETNUM(n, width); if (*p == '$') { if (!mrb_undef_p(nextvalue)) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "value given twice - %i$", n); } nextvalue = GETPOSARG(n); p++; goto retry; } CHECK_FOR_WIDTH(flags); width = n; flags |= FWIDTH; goto retry; case '<': case '{': { const char *start = p; char term = (*p == '<') ? '>' : '}'; for (; p < end && *p != term; ) p++; if (id) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "name%l after <%n>", start, p - start + 1, id); } CHECKNAMEARG(start, p - start + 1); get_hash(mrb, &hash, argc, argv); id = mrb_intern_check(mrb, start + 1, p - start - 1); if (id) { nextvalue = mrb_hash_fetch(mrb, hash, mrb_symbol_value(id), mrb_undef_value()); } if (!id || mrb_undef_p(nextvalue)) { mrb_raisef(mrb, E_KEY_ERROR, "key%l not found", start, p - start + 1); } if (term == '}') goto format_s; p++; goto retry; } case '*': CHECK_FOR_WIDTH(flags); flags |= FWIDTH; GETASTER(width); if (width > INT16_MAX || INT16_MIN > width) { mrb_raise(mrb, E_ARGUMENT_ERROR, "width too big"); } if (width < 0) { flags |= FMINUS; width = -width; } p++; goto retry; case '.': if (flags & FPREC0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "precision given twice"); } flags |= FPREC|FPREC0; p++; if (*p == '*') { GETASTER(prec); if (prec < 0) { /* ignore negative precision */ flags &= ~FPREC; } p++; goto retry; } GETNUM(prec, precision); goto retry; case '\n': case '\0': p--; /* fallthrough */ case '%': if (flags != FNONE) { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format character - %"); } PUSH("%", 1); break; case 'c': { mrb_value val = GETARG(); mrb_value tmp; char *c; tmp = mrb_check_string_type(mrb, val); if (!mrb_nil_p(tmp)) { if (RSTRING_LEN(tmp) != 1) { mrb_raise(mrb, E_ARGUMENT_ERROR, "%c requires a character"); } } else if (mrb_integer_p(val)) { mrb_int n = mrb_integer(val); #ifndef MRB_UTF8_STRING char buf[1]; buf[0] = (char)n&0xff; tmp = mrb_str_new(mrb, buf, 1); #else if (n < 0x80) { char buf[1]; buf[0] = (char)n; tmp = mrb_str_new(mrb, buf, 1); } else { tmp = mrb_funcall_argv(mrb, val, MRB_SYM(chr), 0, NULL); mrb_check_type(mrb, tmp, MRB_TT_STRING); } #endif } else { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid character"); } c = RSTRING_PTR(tmp); n = (int)RSTRING_LEN(tmp); if (!(flags & FWIDTH)) { PUSH(c, n); } else if ((flags & FMINUS)) { PUSH(c, n); if (width>0) FILL(' ', width-1); } else { if (width>0) FILL(' ', width-1); PUSH(c, n); } mrb_gc_arena_restore(mrb, ai); } break; case 's': case 'p': format_s: { mrb_value arg = GETARG(); mrb_int len; mrb_int slen; if (*p == 'p') arg = mrb_inspect(mrb, arg); str = mrb_obj_as_string(mrb, arg); len = RSTRING_LEN(str); if (RSTRING(result)->flags & MRB_STR_EMBED) { mrb_int tmp_n = len; RSTRING(result)->flags &= ~MRB_STR_EMBED_LEN_MASK; RSTRING(result)->flags |= tmp_n << MRB_STR_EMBED_LEN_SHIFT; } else { RSTRING(result)->as.heap.len = blen; } if (flags&(FPREC|FWIDTH)) { slen = RSTRING_LEN(str); if (slen < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid mbstring sequence"); } if ((flags&FPREC) && (prec < slen)) { char *p = RSTRING_PTR(str) + prec; slen = prec; len = (mrb_int)(p - RSTRING_PTR(str)); } /* need to adjust multi-byte string pos */ if ((flags&FWIDTH) && (width > slen)) { width -= (int)slen; if (!(flags&FMINUS)) { FILL(' ', width); } PUSH(RSTRING_PTR(str), len); if (flags&FMINUS) { FILL(' ', width); } break; } } PUSH(RSTRING_PTR(str), len); mrb_gc_arena_restore(mrb, ai); } break; case 'd': case 'i': case 'o': case 'x': case 'X': case 'b': case 'B': case 'u': { mrb_value val = GETARG(); char nbuf[69], *s; const char *prefix = NULL; int sign = 0, dots = 0; char sc = 0; char fc = 0; mrb_int v = 0; int base; int len; if (flags & FSHARP) { switch (*p) { case 'o': prefix = "0"; break; case 'x': prefix = "0x"; break; case 'X': prefix = "0X"; break; case 'b': prefix = "0b"; break; case 'B': prefix = "0B"; break; default: break; } } switch (*p) { case 'o': base = 8; break; case 'x': case 'X': base = 16; break; case 'b': case 'B': base = 2; break; case 'u': case 'd': case 'i': sign = 1; /* fall through */ default: base = 10; break; } bin_retry: switch (mrb_type(val)) { #ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: val = mrb_float_to_integer(mrb, val); goto bin_retry; #endif #ifdef MRB_USE_BIGINT case MRB_TT_BIGINT: { mrb_int n = (mrb_bint_cmp(mrb, val, mrb_fixnum_value(0))); mrb_bool need_dots = ((flags & FPLUS) == 0) && (base == 16 || base == 8 || base == 2) && n < 0; if (need_dots) { val = mrb_bint_2comp(mrb, val); dots = 1; v = -1; } mrb_value str = mrb_bint_to_s(mrb, val, base); s = RSTRING_PTR(str); len = (int)RSTRING_LEN(str); } goto str_skip; #endif case MRB_TT_STRING: val = mrb_str_to_integer(mrb, val, 0, TRUE); goto bin_retry; case MRB_TT_INTEGER: v = mrb_integer(val); break; default: v = mrb_as_int(mrb, val); break; } if (sign) { if (v >= 0) { if (flags & FPLUS) { sc = '+'; width--; } else if (flags & FSPACE) { sc = ' '; width--; } } else { sc = '-'; width--; } s = mrb_int_to_cstr(nbuf, sizeof(nbuf), v, base); if (v < 0) s++; /* skip minus sign */ } else { /* print as unsigned */ s = mrb_uint_to_cstr(nbuf, sizeof(nbuf), v, base); if (v < 0) { dots = 1; } } { size_t size; size = strlen(s); /* PARANOID: assert(size <= MRB_INT_MAX) */ len = (int)size; } #ifdef MRB_USE_BIGINT str_skip: #endif switch (base) { case 16: fc = 'f'; break; case 8: fc = '7'; break; case 2: fc = '1'; break; } if (dots) { if (base == 8 && (*s == '1' || *s == '3')) { s++; len--; } while (*s == fc) { s++; len--; } } if (*p == 'X') { char *pp = s; int c; while ((c = (int)(unsigned char)*pp) != 0) { *pp = toupper(c); pp++; } if (base == 16) { fc = 'F'; } } if (prefix && !prefix[1]) { /* octal */ if (dots) { prefix = NULL; } else if (len == 1 && *s == '0') { len = 0; if (flags & FPREC) prec--; } else if ((flags & FPREC) && (prec > len)) { prefix = NULL; } } else if (len == 1 && *s == '0') { prefix = NULL; } if (prefix) { size_t size; size = strlen(prefix); /* PARANOID: assert(size <= MRB_INT_MAX). * this check is absolutely paranoid. */ width -= (int)size; } if ((flags & (FZERO|FMINUS|FPREC)) == FZERO) { prec = width; width = 0; } else { if (prec < len) { if (!prefix && prec == 0 && len == 1 && *s == '0') len = 0; prec = len; } width -= prec; } if (!(flags&FMINUS) && width > 0) { FILL(' ', width); width = 0; } if (sc) PUSH(&sc, 1); if (prefix) { int plen = (int)strlen(prefix); PUSH(prefix, plen); } if (dots) { prec -= 2; width -= 2; PUSH("..", 2); if (*s != fc) { FILL(fc, 1); prec--; width--; } } if (prec > len) { CHECK(prec - len); if ((flags & (FMINUS|FPREC)) != FMINUS) { char c = '0'; FILL(c, prec - len); } else if (v < 0) { FILL(fc, prec - len); } } PUSH(s, len); if (width > 0) { FILL(' ', width); } } break; case 'f': case 'g': case 'G': case 'e': case 'E': { #ifdef MRB_NO_FLOAT mrb_raisef(mrb, E_ARGUMENT_ERROR, "%%%c not supported with MRB_NO_FLOAT defined", *p); #else mrb_value val = GETARG(); double fval; mrb_int need = 6; fval = mrb_as_float(mrb, val); if (!isfinite(fval)) { const char *expr; const int elen = 3; char sign = '\0'; if (isnan(fval)) { expr = "NaN"; } else { expr = "Inf"; } need = elen; if (!isnan(fval) && fval < 0.0) sign = '-'; else if (flags & (FPLUS|FSPACE)) sign = (flags & FPLUS) ? '+' : ' '; if (sign) need++; if ((flags & FWIDTH) && need < width) need = width; if (need < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "width too big"); } FILL(' ', need); if (flags & FMINUS) { if (sign) buf[blen - need--] = sign; memcpy(&buf[blen - need], expr, elen); } else { if (sign) buf[blen - elen - 1] = sign; memcpy(&buf[blen - elen], expr, (size_t)elen); } break; } need = 0; if (*p != 'e' && *p != 'E') { int i; frexp(fval, &i); if (i > 0) need = BIT_DIGITS(i); } if (need > MRB_INT_MAX - ((flags&FPREC) ? prec : 6)) { too_big_width_prec: mrb_raise(mrb, E_ARGUMENT_ERROR, (width > prec ? "width too big" : "prec too big")); } need += (flags&FPREC) ? prec : 6; if ((flags&FWIDTH) && need < width) need = width; if ((mrb_int)need > MRB_INT_MAX - 20) { goto too_big_width_prec; } need += 20; CHECK(need); n = fmt_float(&buf[blen], need, *p, flags, width, prec, fval); if (n < 0 || n >= need) { mrb_raise(mrb, E_RUNTIME_ERROR, "formatting error"); } blen += n; #endif } break; } } sprint_exit: #if 0 /* XXX - We cannot validate the number of arguments if (digit)$ style used. */ if (posarg >= 0 && nextarg < argc) { const char *mesg = "too many arguments for format string"; if (mrb_test(ruby_debug)) mrb_raise(mrb, E_ARGUMENT_ERROR, mesg); if (mrb_test(ruby_verbose)) mrb_warn(mrb, mesg); } #endif mrb_str_resize(mrb, result, blen); return result; } /* * call-seq: * format(format_string [, arguments...] ) -> string * sprintf(format_string [, arguments...] ) -> string * * Returns the string resulting from applying format_string to * any additional arguments. Within the format string, any characters * other than format sequences are copied to the result. * * The syntax of a format sequence is follows. * * %[flags][width][.precision]type * * A format * sequence consists of a percent sign, followed by optional flags, * width, and precision indicators, then terminated with a field type * character. The field type controls how the corresponding * sprintf argument is to be interpreted, while the flags * modify that interpretation. * * The field type characters are: * * Field | Integer Format * ------+-------------------------------------------------------------- * b | Convert argument as a binary number. * | Negative numbers will be displayed as a two's complement * | prefixed with '..1'. * B | Equivalent to 'b', but uses an uppercase 0B for prefix * | in the alternative format by #. * d | Convert argument as a decimal number. * i | Identical to 'd'. * o | Convert argument as an octal number. * | Negative numbers will be displayed as a two's complement * | prefixed with '..7'. * u | Identical to 'd'. * x | Convert argument as a hexadecimal number. * | Negative numbers will be displayed as a two's complement * | prefixed with '..f' (representing an infinite string of * | leading 'ff's). * X | Equivalent to 'x', but uses uppercase letters. * * Field | Float Format * ------+-------------------------------------------------------------- * e | Convert floating-point argument into exponential notation * | with one digit before the decimal point as [-]d.dddddde[+-]dd. * | The precision specifies the number of digits after the decimal * | point (defaulting to six). * E | Equivalent to 'e', but uses an uppercase E to indicate * | the exponent. * f | Convert floating-point argument as [-]ddd.dddddd, * | where the precision specifies the number of digits after * | the decimal point. * g | Convert a floating-point number using exponential form * | if the exponent is less than -4 or greater than or * | equal to the precision, or in dd.dddd form otherwise. * | The precision specifies the number of significant digits. * G | Equivalent to 'g', but use an uppercase 'E' in exponent form. * * Field | Other Format * ------+-------------------------------------------------------------- * c | Argument is the numeric code for a single character or * | a single character string itself. * p | The valuing of argument.inspect. * s | Argument is a string to be substituted. If the format * | sequence contains a precision, at most that many characters * | will be copied. * % | A percent sign itself will be displayed. No argument taken. * * The flags modifies the behavior of the formats. * The flag characters are: * * Flag | Applies to | Meaning * ---------+---------------+----------------------------------------- * space | bBdiouxX | Leave a space at the start of * | aAeEfgG | non-negative numbers. * | (numeric fmt) | For 'o', 'x', 'X', 'b' and 'B', use * | | a minus sign with absolute value for * | | negative values. * ---------+---------------+----------------------------------------- * (digit)$ | all | Specifies the absolute argument number * | | for this field. Absolute and relative * | | argument numbers cannot be mixed in a * | | sprintf string. * ---------+---------------+----------------------------------------- * # | bBoxX | Use an alternative format. * | aAeEfgG | For the conversions 'o', increase the precision * | | until the first digit will be '0' if * | | it is not formatted as complements. * | | For the conversions 'x', 'X', 'b' and 'B' * | | on non-zero, prefix the result with "0x", * | | "0X", "0b" and "0B", respectively. * | | For 'e', 'E', 'f', 'g', and 'G', * | | force a decimal point to be added, * | | even if no digits follow. * | | For 'g' and 'G', do not remove trailing zeros. * ---------+---------------+----------------------------------------- * + | bBdiouxX | Add a leading plus sign to non-negative * | aAeEfgG | numbers. * | (numeric fmt) | For 'o', 'x', 'X', 'b' and 'B', use * | | a minus sign with absolute value for * | | negative values. * ---------+---------------+----------------------------------------- * - | all | Left-justify the result of this conversion. * ---------+---------------+----------------------------------------- * 0 (zero) | bBdiouxX | Pad with zeros, not spaces. * | aAeEfgG | For 'o', 'x', 'X', 'b' and 'B', radix-1 * | (numeric fmt) | is used for negative numbers formatted as * | | complements. * ---------+---------------+----------------------------------------- * * | all | Use the next argument as the field width. * | | If negative, left-justify the result. If the * | | asterisk is followed by a number and a dollar * | | sign, use the indicated argument as the width. * * Examples of flags: * * # '+' and space flag specifies the sign of non-negative numbers. * sprintf("%d", 123) #=> "123" * sprintf("%+d", 123) #=> "+123" * sprintf("% d", 123) #=> " 123" * * # '#' flag for 'o' increases number of digits to show '0'. * # '+' and space flag changes format of negative numbers. * sprintf("%o", 123) #=> "173" * sprintf("%#o", 123) #=> "0173" * sprintf("%+o", -123) #=> "-173" * sprintf("%o", -123) #=> "..7605" * sprintf("%#o", -123) #=> "..7605" * * # '#' flag for 'x' add a prefix '0x' for non-zero numbers. * # '+' and space flag disables complements for negative numbers. * sprintf("%x", 123) #=> "7b" * sprintf("%#x", 123) #=> "0x7b" * sprintf("%+x", -123) #=> "-7b" * sprintf("%x", -123) #=> "..f85" * sprintf("%#x", -123) #=> "0x..f85" * sprintf("%#x", 0) #=> "0" * * # '#' for 'X' uses the prefix '0X'. * sprintf("%X", 123) #=> "7B" * sprintf("%#X", 123) #=> "0X7B" * * # '#' flag for 'b' add a prefix '0b' for non-zero numbers. * # '+' and space flag disables complements for negative numbers. * sprintf("%b", 123) #=> "1111011" * sprintf("%#b", 123) #=> "0b1111011" * sprintf("%+b", -123) #=> "-1111011" * sprintf("%b", -123) #=> "..10000101" * sprintf("%#b", -123) #=> "0b..10000101" * sprintf("%#b", 0) #=> "0" * * # '#' for 'B' uses the prefix '0B'. * sprintf("%B", 123) #=> "1111011" * sprintf("%#B", 123) #=> "0B1111011" * * # '#' for 'e' forces to show the decimal point. * sprintf("%.0e", 1) #=> "1e+00" * sprintf("%#.0e", 1) #=> "1.e+00" * * # '#' for 'f' forces to show the decimal point. * sprintf("%.0f", 1234) #=> "1234" * sprintf("%#.0f", 1234) #=> "1234." * * # '#' for 'g' forces to show the decimal point. * # It also disables stripping lowest zeros. * sprintf("%g", 123.4) #=> "123.4" * sprintf("%#g", 123.4) #=> "123.400" * sprintf("%g", 123456) #=> "123456" * sprintf("%#g", 123456) #=> "123456." * * The field width is an optional integer, followed optionally by a * period and a precision. The width specifies the minimum number of * characters that will be written to the result for this field. * * Examples of width: * * # padding is done by spaces, width=20 * # 0 or radix-1. <------------------> * sprintf("%20d", 123) #=> " 123" * sprintf("%+20d", 123) #=> " +123" * sprintf("%020d", 123) #=> "00000000000000000123" * sprintf("%+020d", 123) #=> "+0000000000000000123" * sprintf("% 020d", 123) #=> " 0000000000000000123" * sprintf("%-20d", 123) #=> "123 " * sprintf("%-+20d", 123) #=> "+123 " * sprintf("%- 20d", 123) #=> " 123 " * sprintf("%020x", -123) #=> "..ffffffffffffffff85" * * For * numeric fields, the precision controls the number of decimal places * displayed. For string fields, the precision determines the maximum * number of characters to be copied from the string. (Thus, the format * sequence %10.10s will always contribute exactly ten * characters to the result.) * * Examples of precisions: * * # precision for 'd', 'o', 'x' and 'b' is * # minimum number of digits <------> * sprintf("%20.8d", 123) #=> " 00000123" * sprintf("%20.8o", 123) #=> " 00000173" * sprintf("%20.8x", 123) #=> " 0000007b" * sprintf("%20.8b", 123) #=> " 01111011" * sprintf("%20.8d", -123) #=> " -00000123" * sprintf("%20.8o", -123) #=> " ..777605" * sprintf("%20.8x", -123) #=> " ..ffff85" * sprintf("%20.8b", -11) #=> " ..110101" * * # "0x" and "0b" for '#x' and '#b' is not counted for * # precision but "0" for '#o' is counted. <------> * sprintf("%#20.8d", 123) #=> " 00000123" * sprintf("%#20.8o", 123) #=> " 00000173" * sprintf("%#20.8x", 123) #=> " 0x0000007b" * sprintf("%#20.8b", 123) #=> " 0b01111011" * sprintf("%#20.8d", -123) #=> " -00000123" * sprintf("%#20.8o", -123) #=> " ..777605" * sprintf("%#20.8x", -123) #=> " 0x..ffff85" * sprintf("%#20.8b", -11) #=> " 0b..110101" * * # precision for 'e' is number of * # digits after the decimal point <------> * sprintf("%20.8e", 1234.56789) #=> " 1.23456789e+03" * * # precision for 'f' is number of * # digits after the decimal point <------> * sprintf("%20.8f", 1234.56789) #=> " 1234.56789000" * * # precision for 'g' is number of * # significant digits <-------> * sprintf("%20.8g", 1234.56789) #=> " 1234.5679" * * # <-------> * sprintf("%20.8g", 123456789) #=> " 1.2345679e+08" * * # precision for 's' is * # maximum number of characters <------> * sprintf("%20.8s", "string test") #=> " string t" * * Examples: * * sprintf("%d %04x", 123, 123) #=> "123 007b" * sprintf("%08b '%4s'", 123, 123) #=> "01111011 ' 123'" * sprintf("%1$*2$s %2$d %1$s", "hello", 8) #=> " hello 8 hello" * sprintf("%1$*2$s %2$d", "hello", -8) #=> "hello -8" * sprintf("%+g:% g:%-g", 1.23, 1.23, 1.23) #=> "+1.23: 1.23:1.23" * sprintf("%u", -123) #=> "-123" * * For more complex formatting, Ruby supports a reference by name. * %s style uses format style, but %{name} style doesn't. * * Examples: * sprintf("%d : %f", { :foo => 1, :bar => 2 }) * #=> 1 : 2.000000 * sprintf("%{foo}f", { :foo => 1 }) * # => "1f" */ static mrb_value mrb_f_sprintf(mrb_state *mrb, mrb_value obj) { mrb_int argc; const mrb_value *argv; mrb_get_args(mrb, "*", &argv, &argc); if (argc <= 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments"); return mrb_nil_value(); } else { return mrb_str_format(mrb, argc - 1, argv + 1, argv[0]); } } void mrb_mruby_sprintf_gem_init(mrb_state *mrb) { struct RClass *krn = mrb->kernel_module; mrb_define_module_function(mrb, krn, "sprintf", mrb_f_sprintf, MRB_ARGS_ANY()); mrb_define_module_function(mrb, krn, "format", mrb_f_sprintf, MRB_ARGS_ANY()); } void mrb_mruby_sprintf_gem_final(mrb_state *mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-time0000644000000000000000000000013215077107334021337 xustar0030 mtime=1761382108.783301297 30 atime=1761382109.797298366 30 ctime=1761382108.783301297 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-time/0000755000175100017510000000000015077107334022004 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-time/PaxHeaders/mrbgem.rake0000644000000000000000000000013115077107276023535 xustar0029 mtime=1761382078.13042052 30 atime=1761382080.148411288 30 ctime=1761382108.783301297 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-time/mrbgem.rake0000644000175100017510000000023415077107276024125 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-time') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'standard Time class' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-time/PaxHeaders/test0000644000000000000000000000013215077107334022316 xustar0030 mtime=1761382108.784301295 30 atime=1761382109.797298366 30 ctime=1761382108.784301295 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-time/test/0000755000175100017510000000000015077107334022763 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-time/test/PaxHeaders/time.rb0000644000000000000000000000013115077107276023662 xustar0029 mtime=1761382078.13042052 30 atime=1761382080.148411288 30 ctime=1761382108.784301295 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-time/test/time.rb0000644000175100017510000001551315077107276024260 0ustar00runnerrunner## # Time ISO Test assert('Time.new', '15.2.3.3.3') do assert_equal(Time, Time.new.class) end assert('Time', '15.2.19') do assert_equal(Class, Time.class) end assert('Time.at', '15.2.19.6.1') do assert_kind_of(Time, Time.at(1300000000.0)) skip unless Object.const_defined?(:Float) assert_raise(FloatDomainError) { Time.at(Float::NAN) } assert_raise(FloatDomainError) { Time.at(Float::INFINITY) } assert_raise(FloatDomainError) { Time.at(-Float::INFINITY) } assert_raise(FloatDomainError) { Time.at(0, Float::NAN) } assert_raise(FloatDomainError) { Time.at(0, Float::INFINITY) } assert_raise(FloatDomainError) { Time.at(0, -Float::INFINITY) } end assert('Time.gm', '15.2.19.6.2') do t = Time.gm(2012, 9, 23) assert_operator(2012, :eql?, t.year) assert_operator( 9, :eql?, t.month) assert_operator( 23, :eql?, t.day) assert_operator( 0, :eql?, t.hour) assert_operator( 0, :eql?, t.min) assert_operator( 0, :eql?, t.sec) assert_operator( 0, :eql?, t.usec) end assert('Time.local', '15.2.19.6.3') do t = Time.local(2014, 12, 27, 18) assert_operator(2014, :eql?, t.year) assert_operator( 12, :eql?, t.month) assert_operator( 27, :eql?, t.day) assert_operator( 18, :eql?, t.hour) assert_operator( 0, :eql?, t.min) assert_operator( 0, :eql?, t.sec) assert_operator( 0, :eql?, t.usec) end assert('Time.mktime', '15.2.19.6.4') do t = Time.mktime(2013, 10, 4, 6, 15, 58, 3485) assert_operator(2013, :eql?, t.year) assert_operator( 10, :eql?, t.month) assert_operator( 4, :eql?, t.day) assert_operator( 6, :eql?, t.hour) assert_operator( 15, :eql?, t.min) assert_operator( 58, :eql?, t.sec) assert_operator(3485, :eql?, t.usec) end assert('Time.now', '15.2.19.6.5') do assert_equal(Time, Time.now.class) end assert('Time.utc', '15.2.19.6.6') do t = Time.utc(2034) assert_operator(2034, :eql?, t.year) assert_operator( 1, :eql?, t.month) assert_operator( 1, :eql?, t.day) assert_operator( 0, :eql?, t.hour) assert_operator( 0, :eql?, t.min) assert_operator( 0, :eql?, t.sec) assert_operator( 0, :eql?, t.usec) end assert('Time#+', '15.2.19.7.1') do t1 = Time.at(1300000000) t2 = t1.+(60) assert_equal("Sun Mar 13 07:07:40 2011", t2.utc.asctime) skip unless Object.const_defined?(:Float) assert_raise(FloatDomainError) { Time.at(0) + Float::NAN } assert_raise(FloatDomainError) { Time.at(0) + Float::INFINITY } assert_raise(FloatDomainError) { Time.at(0) + -Float::INFINITY } end assert('Time#-', '15.2.19.7.2') do t1 = Time.at(1300000000) t2 = t1.-(60) assert_equal("Sun Mar 13 07:05:40 2011", t2.utc.asctime) skip unless Object.const_defined?(:Float) assert_raise(FloatDomainError) { Time.at(0) - Float::NAN } assert_raise(FloatDomainError) { Time.at(0) - Float::INFINITY } assert_raise(FloatDomainError) { Time.at(0) - -Float::INFINITY } end assert('Time#<=>', '15.2.19.7.3') do t1 = Time.at(1300000000) t2 = Time.at(1400000000) t3 = Time.at(1500000000) assert_equal(1, t2 <=> t1) assert_equal(0, t2 <=> t2) assert_equal(-1, t2 <=> t3) assert_nil(t2 <=> nil) end assert('Time#asctime', '15.2.19.7.4') do assert_equal("Thu Mar 4 05:06:07 1982", Time.gm(1982,3,4,5,6,7).asctime) end assert('Time#ctime', '15.2.19.7.5') do assert_equal("Thu Oct 24 15:26:47 2013", Time.gm(2013,10,24,15,26,47).ctime) end assert('Time#day', '15.2.19.7.6') do assert_equal(23, Time.gm(2012, 12, 23).day) end assert('Time#dst?', '15.2.19.7.7') do assert_not_predicate(Time.gm(2012, 12, 23).utc, :dst?) end assert('Time#getgm', '15.2.19.7.8') do assert_equal("Sun Mar 13 07:06:40 2011", Time.at(1300000000).getgm.asctime) end assert('Time#getlocal', '15.2.19.7.9') do t1 = Time.at(1300000000.0) t2 = Time.at(1300000000.0) t3 = t1.getlocal assert_equal(t1, t3) assert_equal(t3, t2.getlocal) end assert('Time#getutc', '15.2.19.7.10') do assert_equal("Sun Mar 13 07:06:40 2011", Time.at(1300000000).getutc.asctime) end assert('Time#gmt?', '15.2.19.7.11') do assert_predicate(Time.at(1300000000).utc, :gmt?) end # ATM not implemented # assert('Time#gmt_offset', '15.2.19.7.12') do assert('Time#gmtime', '15.2.19.7.13') do t = Time.now assert_predicate(t.gmtime, :gmt?) assert_predicate(t, :gmt?) end # ATM not implemented # assert('Time#gmtoff', '15.2.19.7.14') do assert('Time#hour', '15.2.19.7.15') do assert_equal(7, Time.gm(2012, 12, 23, 7, 6).hour) end # ATM doesn't really work # assert('Time#initialize', '15.2.19.7.16') do assert('Time#initialize_copy', '15.2.19.7.17') do t = Time.at(7.0e6) assert_equal(t, t.clone) end assert('Time#localtime', '15.2.19.7.18') do t1 = Time.utc(2014, 5 ,6) t2 = Time.utc(2014, 5 ,6) t3 = t2.getlocal assert_equal(t3, t1.localtime) assert_equal(t3, t1) end assert('Time#mday', '15.2.19.7.19') do assert_equal(23, Time.gm(2012, 12, 23).mday) end assert('Time#min', '15.2.19.7.20') do assert_equal(6, Time.gm(2012, 12, 23, 7, 6).min) end assert('Time#mon', '15.2.19.7.21') do assert_equal(12, Time.gm(2012, 12, 23).mon) end assert('Time#month', '15.2.19.7.22') do assert_equal(12, Time.gm(2012, 12, 23).month) end assert('Times#sec', '15.2.19.7.23') do assert_equal(40, Time.gm(2012, 12, 23, 7, 6, 40).sec) end assert('Time#to_f', '15.2.19.7.24') do skip unless Object.const_defined?(:Float) assert_operator(2.0, :eql?, Time.at(2).to_f) end assert('Time#to_i', '15.2.19.7.25') do assert_operator(2, :eql?, Time.at(2).to_i) end assert('Time#usec', '15.2.19.7.26') do assert_equal(0, Time.at(1300000000).usec) skip unless Object.const_defined?(:Float) assert_equal(0, Time.at(1300000000.0).usec) end assert('Time#utc', '15.2.19.7.27') do t = Time.now assert_predicate(t.utc, :gmt?) assert_predicate(t, :gmt?) end assert('Time#utc?', '15.2.19.7.28') do assert_predicate(Time.at(1300000000).utc, :utc?) end # ATM not implemented # assert('Time#utc_offset', '15.2.19.7.29') do assert('Time#wday', '15.2.19.7.30') do assert_equal(0, Time.gm(2012, 12, 23).wday) end assert('Time#yday', '15.2.19.7.31') do assert_equal(358, Time.gm(2012, 12, 23).yday) end assert('Time#year', '15.2.19.7.32') do assert_equal(2012, Time.gm(2012, 12, 23).year) end assert('Time#zone', '15.2.19.7.33') do assert_equal('UTC', Time.at(1300000000).utc.zone) end # Not ISO specified assert('Time#to_s') do assert_equal("2003-04-05 06:07:08 UTC", Time.gm(2003,4,5,6,7,8,9).to_s) end assert('Time#inspect') do assert_match("2013-10-28 16:27:48 [+-][0-9][0-9][0-9][0-9]", Time.local(2013,10,28,16,27,48).inspect) end assert('day of week methods') do t = Time.gm(2012, 12, 24) assert_false t.sunday? assert_true t.monday? assert_false t.tuesday? assert_false t.wednesday? assert_false t.thursday? assert_false t.friday? assert_false t.saturday? end assert('2000 times 500us make a second') do t = Time.utc 2015 2000.times do t += 0.0005 end assert_equal(0, t.usec) end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-time/PaxHeaders/include0000644000000000000000000000013215077107334022762 xustar0030 mtime=1761382108.207302963 30 atime=1761382109.797298366 30 ctime=1761382108.207302963 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-time/include/0000755000175100017510000000000015077107334023427 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-time/include/PaxHeaders/mruby0000644000000000000000000000013215077107334024120 xustar0030 mtime=1761382108.787301286 30 atime=1761382109.797298366 30 ctime=1761382108.787301286 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-time/include/mruby/0000755000175100017510000000000015077107334024565 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-time/include/mruby/PaxHeaders/time.h0000644000000000000000000000013115077107276025310 xustar0029 mtime=1761382078.13042052 30 atime=1761382080.148411288 30 ctime=1761382108.787301286 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-time/include/mruby/time.h0000644000175100017510000000072215077107276025702 0ustar00runnerrunner/* ** mruby/time.h - Time class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_TIME_H #define MRUBY_TIME_H #include #include MRB_BEGIN_DECL typedef enum mrb_timezone { MRB_TIMEZONE_NONE = 0, MRB_TIMEZONE_UTC = 1, MRB_TIMEZONE_LOCAL = 2, MRB_TIMEZONE_LAST = 3 } mrb_timezone; MRB_API mrb_value mrb_time_at(mrb_state *mrb, time_t sec, time_t usec, mrb_timezone timezone); MRB_END_DECL #endif /* MRUBY_TIME_H */ nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-time/PaxHeaders/src0000644000000000000000000000013215077107334022126 xustar0030 mtime=1761382108.785301292 30 atime=1761382109.797298366 30 ctime=1761382108.785301292 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-time/src/0000755000175100017510000000000015077107334022573 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-time/src/PaxHeaders/time.c0000644000000000000000000000013115077107276023311 xustar0029 mtime=1761382078.13042052 30 atime=1761382080.148411288 30 ctime=1761382108.785301292 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-time/src/time.c0000644000175100017510000007721015077107276023711 0ustar00runnerrunner/* ** time.c - Time class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #ifdef MRB_NO_STDIO #include #endif #include #ifndef _WIN32 #include #endif #define NDIV(x,y) (-(-((x)+1)/(y))-1) #define TO_S_FMT "%Y-%m-%d %H:%M:%S " #if defined(_MSC_VER) && _MSC_VER < 1800 double round(double x) { return floor(x + 0.5); } #endif #ifndef MRB_NO_FLOAT # if !defined(__MINGW64__) && defined(_WIN32) # define llround(x) round(x) # endif #endif #if defined(__MINGW64__) || defined(__MINGW32__) # include #endif /** Time class configuration */ /* gettimeofday(2) */ /* C99 does not have gettimeofday that is required to retrieve microseconds */ /* uncomment following macro on platforms without gettimeofday(2) */ /* #define NO_GETTIMEOFDAY */ /* gmtime(3) */ /* C99 does not have reentrant gmtime_r() so it might cause troubles under */ /* multi-threading environment. undef following macro on platforms that */ /* does not have gmtime_r() and localtime_r(). */ /* #define NO_GMTIME_R */ #ifdef _WIN32 #ifdef _MSC_VER /* Win32 platform do not provide gmtime_r/localtime_r; emulate them using gmtime_s/localtime_s */ #define gmtime_r(tp, tm) ((gmtime_s((tm), (tp)) == 0) ? (tm) : NULL) #define localtime_r(tp, tm) ((localtime_s((tm), (tp)) == 0) ? (tm) : NULL) #else #define NO_GMTIME_R #endif #endif #ifdef __STRICT_ANSI__ /* Strict ANSI (e.g. -std=c99) do not provide gmtime_r/localtime_r */ #define NO_GMTIME_R #endif /* asctime(3) */ /* mruby usually use its own implementation of struct tm to string conversion */ /* except when MRB_NO_STDIO is set. In that case, it uses asctime() or asctime_r(). */ /* By default mruby tries to use asctime_r() which is reentrant. */ /* Undef following macro on platforms that does not have asctime_r(). */ /* #define NO_ASCTIME_R */ /* timegm(3) */ /* mktime() creates tm structure for localtime; timegm() is for UTC time */ /* define following macro to use probably faster timegm() on the platform */ /* #define USE_SYSTEM_TIMEGM */ /** end of Time class configuration */ /* protection against incorrectly defined _POSIX_TIMERS */ #if defined(_POSIX_TIMERS) && (_POSIX_TIMERS + 0) > 0 && defined(CLOCK_REALTIME) # define USE_CLOCK_GETTIME #endif #if !defined(NO_GETTIMEOFDAY) # if defined(_WIN32) && !defined(USE_CLOCK_GETTIME) # define WIN32_LEAN_AND_MEAN /* don't include winsock.h */ # include # define gettimeofday my_gettimeofday # ifdef _MSC_VER # define UI64(x) x##ui64 # else # define UI64(x) x##ull # endif typedef long suseconds_t; # if (!defined __MINGW64__) && (!defined __MINGW32__) struct timeval { time_t tv_sec; suseconds_t tv_usec; }; # endif static int gettimeofday(struct timeval *tv, void *tz) { if (tz) { mrb_assert(0); /* timezone is not supported */ } if (tv) { union { FILETIME ft; unsigned __int64 u64; } t; GetSystemTimeAsFileTime(&t.ft); /* 100 ns intervals since Windows epoch */ t.u64 -= UI64(116444736000000000); /* Unix epoch bias */ t.u64 /= 10; /* to microseconds */ tv->tv_sec = (time_t)(t.u64 / (1000 * 1000)); tv->tv_usec = t.u64 % (1000 * 1000); } return 0; } # else # include # endif #endif #ifdef NO_GMTIME_R #define gmtime_r(t,r) gmtime(t) #define localtime_r(t,r) localtime(t) #endif #ifndef USE_SYSTEM_TIMEGM #define timegm my_timgm static unsigned int is_leapyear(unsigned int y) { return (y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0); } static time_t timegm(struct tm *tm) { static const unsigned int ndays[2][12] = { {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; time_t r = 0; int i; unsigned int *nday = (unsigned int*) ndays[is_leapyear(tm->tm_year+1900)]; static const int epoch_year = 70; if (tm->tm_year >= epoch_year) { for (i = epoch_year; i < tm->tm_year; ++i) r += is_leapyear(i+1900) ? 366*24*60*60 : 365*24*60*60; } else { for (i = tm->tm_year; i < epoch_year; ++i) r -= is_leapyear(i+1900) ? 366*24*60*60 : 365*24*60*60; } for (i = 0; i < tm->tm_mon; ++i) r += nday[i] * 24 * 60 * 60; r += (tm->tm_mday - 1) * 24 * 60 * 60; r += tm->tm_hour * 60 * 60; r += tm->tm_min * 60; r += tm->tm_sec; return r; } #endif /* Since we are limited to using ISO C99, this implementation is based * on time_t. That means the resolution of time is only precise to the * second level. Also, there are only 2 timezones, namely UTC and LOCAL. */ #ifndef MRB_NO_STDIO static const char mon_names[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", }; static const char wday_names[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", }; #endif struct mrb_time { time_t sec; time_t usec; enum mrb_timezone timezone; struct tm datetime; }; static const struct mrb_data_type time_type = { "Time", mrb_free }; #define MRB_TIME_T_UINT (~(time_t)0 > 0) #define MRB_TIME_MIN ( \ MRB_TIME_T_UINT ? 0 : \ (sizeof(time_t) <= 4 ? INT32_MIN : INT64_MIN) \ ) #define MRB_TIME_MAX (time_t)( \ MRB_TIME_T_UINT ? (sizeof(time_t) <= 4 ? UINT32_MAX : UINT64_MAX) : \ (sizeof(time_t) <= 4 ? INT32_MAX : INT64_MAX) \ ) /* return true if time_t is fit in mrb_int */ static mrb_bool fixable_time_t_p(time_t v) { if (MRB_INT_MIN <= MRB_TIME_MIN && MRB_TIME_MAX <= MRB_INT_MAX) return TRUE; if (v > (time_t)MRB_INT_MAX) return FALSE; if (MRB_TIME_T_UINT) return TRUE; if (MRB_INT_MIN > (mrb_int)v) return FALSE; return TRUE; } static void time_out_of_range(mrb_state *mrb, mrb_value obj) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "%v out of Time range", obj); } static time_t mrb_to_time_t(mrb_state *mrb, mrb_value obj, time_t *usec) { time_t t; switch (mrb_type(obj)) { #ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: { mrb_float f = mrb_float(obj); mrb_check_num_exact(mrb, f); if (f >= ((mrb_float)MRB_TIME_MAX-1.0) || f < ((mrb_float)MRB_TIME_MIN+1.0)) { time_out_of_range(mrb, obj); } if (usec) { double tt = floor(f); if (!isfinite(tt)) time_out_of_range(mrb, obj); t = (time_t)tt; *usec = (time_t)trunc((f - tt) * 1.0e+6); } else { double tt = round(f); if (!isfinite(tt)) time_out_of_range(mrb, obj); t = (time_t)tt; } } break; #endif /* MRB_NO_FLOAT */ #ifdef MRB_USE_BIGINT case MRB_TT_BIGINT: { if (sizeof(time_t) > sizeof(mrb_int)) { if (MRB_TIME_T_UINT) { t = (time_t)mrb_bint_as_uint64(mrb, obj); } else { t = (time_t)mrb_bint_as_int64(mrb, obj); } if (usec) { *usec = 0; } break; } else { mrb_int i = mrb_bint_as_int(mrb, obj); obj = mrb_int_value(mrb, i); } } /* fall through */ #endif /* MRB_USE_BIGINT */ case MRB_TT_INTEGER: { mrb_int i = mrb_integer(obj); if ((MRB_INT_MAX > MRB_TIME_MAX && i > 0 && (time_t)i > MRB_TIME_MAX) || (0 > MRB_TIME_MIN && MRB_TIME_MIN > MRB_INT_MIN && MRB_TIME_MIN > i)) { time_out_of_range(mrb, obj); } t = (time_t)i; if (usec) { *usec = 0; } } break; default: mrb_raisef(mrb, E_TYPE_ERROR, "cannot convert %Y to time", obj); return 0; } return t; } static mrb_value time_value_from_time_t(mrb_state *mrb, time_t t) { if (!fixable_time_t_p(t)) { #if defined(MRB_USE_BIGINT) if (MRB_TIME_T_UINT) { return mrb_bint_new_uint64(mrb, (uint64_t)t); } else { return mrb_bint_new_int64(mrb, (int64_t)t); } #elif !defined(MRB_NO_FLOAT) return mrb_float_value(mrb, (mrb_float)t); #else mrb_raisef(mrb, E_ARGUMENT_ERROR, "Time too big"); #endif } return mrb_int_value(mrb, (mrb_int)t); } /** Updates the datetime of a mrb_time based on it's timezone and seconds setting. Returns self on success, NULL of failure. if `dealloc` is set `true`, it frees `self` on error. */ static struct mrb_time* time_update_datetime(mrb_state *mrb, struct mrb_time *self, int dealloc) { time_t t = self->sec; struct tm *aid; if (self->timezone == MRB_TIMEZONE_UTC) { aid = gmtime_r(&t, &self->datetime); } else { aid = localtime_r(&t, &self->datetime); } if (!aid) { if (dealloc) mrb_free(mrb, self); time_out_of_range(mrb, time_value_from_time_t(mrb, t)); /* not reached */ return NULL; } #ifdef NO_GMTIME_R self->datetime = *aid; /* copy data */ #endif return self; } static mrb_value time_wrap(mrb_state *mrb, struct RClass *tc, struct mrb_time *tm) { return mrb_obj_value(Data_Wrap_Struct(mrb, tc, &time_type, tm)); } /* Allocates a mrb_time object and initializes it. */ static struct mrb_time* time_alloc_time(mrb_state *mrb, time_t sec, time_t usec, enum mrb_timezone timezone) { struct mrb_time *tm = (struct mrb_time*)mrb_malloc(mrb, sizeof(struct mrb_time)); tm->sec = sec; tm->usec = usec; if (!MRB_TIME_T_UINT && tm->usec < 0) { long sec2 = (long)NDIV(tm->usec,1000000); /* negative div */ tm->usec -= sec2 * 1000000; tm->sec += sec2; } else if (tm->usec >= 1000000) { long sec2 = (long)(tm->usec / 1000000); tm->usec -= sec2 * 1000000; tm->sec += sec2; } tm->timezone = timezone; time_update_datetime(mrb, tm, TRUE); return tm; } static struct mrb_time* time_alloc(mrb_state *mrb, mrb_value sec, mrb_value usec, enum mrb_timezone timezone) { time_t tsec, tusec; tsec = mrb_to_time_t(mrb, sec, &tusec); tusec += mrb_to_time_t(mrb, usec, NULL); return time_alloc_time(mrb, tsec, tusec, timezone); } static mrb_value time_make_time(mrb_state *mrb, struct RClass *c, time_t sec, time_t usec, enum mrb_timezone timezone) { return time_wrap(mrb, c, time_alloc_time(mrb, sec, usec, timezone)); } static mrb_value time_make(mrb_state *mrb, struct RClass *c, mrb_value sec, mrb_value usec, enum mrb_timezone timezone) { return time_wrap(mrb, c, time_alloc(mrb, sec, usec, timezone)); } static struct mrb_time* current_mrb_time(mrb_state *mrb) { struct mrb_time tmzero = {0}; time_t sec, usec; #if defined(TIME_UTC) && !defined(__ANDROID__) { struct timespec ts; timespec_get(&ts, TIME_UTC); sec = ts.tv_sec; usec = ts.tv_nsec / 1000; } #elif defined(USE_CLOCK_GETTIME) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); sec = ts.tv_sec; usec = ts.tv_nsec / 1000; } #elif defined(NO_GETTIMEOFDAY) { static time_t last_sec = 0, last_usec = 0; sec = time(NULL); if (sec != last_sec) { last_sec = sec; last_usec = 0; } else { /* add 1 usec to differentiate two times */ last_usec += 1; } usec = last_usec; } #else { struct timeval tv; gettimeofday(&tv, NULL); sec = tv.tv_sec; usec = tv.tv_usec; } #endif struct mrb_time *tm = (struct mrb_time*)mrb_malloc(mrb, sizeof(*tm)); *tm = tmzero; tm->sec = sec; tm->usec = usec; tm->timezone = MRB_TIMEZONE_LOCAL; time_update_datetime(mrb, tm, TRUE); return tm; } /* Allocates a new Time object with given millis value. */ static mrb_value time_now(mrb_state *mrb, mrb_value self) { return time_wrap(mrb, mrb_class_ptr(self), current_mrb_time(mrb)); } MRB_API mrb_value mrb_time_at(mrb_state *mrb, time_t sec, time_t usec, enum mrb_timezone zone) { return time_make_time(mrb, mrb_class_get_id(mrb, MRB_SYM(Time)), sec, usec, zone); } /* 15.2.19.6.1 */ /* Creates an instance of time at the given time in seconds, etc. */ static mrb_value time_at_m(mrb_state *mrb, mrb_value self) { mrb_value sec; mrb_value usec = mrb_fixnum_value(0); mrb_get_args(mrb, "o|o", &sec, &usec); return time_make(mrb, mrb_class_ptr(self), sec, usec, MRB_TIMEZONE_LOCAL); } static struct mrb_time* time_mktime(mrb_state *mrb, mrb_int ayear, mrb_int amonth, mrb_int aday, mrb_int ahour, mrb_int amin, mrb_int asec, mrb_int ausec, enum mrb_timezone timezone) { struct tm nowtime = { 0 }; #if MRB_INT_MAX > INT_MAX #define OUTINT(x) (((MRB_TIME_T_UINT ? 0 : INT_MIN) > (x)) || (x) > INT_MAX) #else #define OUTINT(x) 0 #endif ayear -= 1900; if (OUTINT(ayear) || amonth < 1 || amonth > 12 || aday < 1 || aday > 31 || ahour < 0 || ahour > 24 || (ahour == 24 && (amin > 0 || asec > 0)) || amin < 0 || amin > 59 || asec < 0 || asec > 60) mrb_raise(mrb, E_ARGUMENT_ERROR, "argument out of range"); nowtime.tm_year = (int)ayear; nowtime.tm_mon = (int)(amonth - 1); nowtime.tm_mday = (int)aday; nowtime.tm_hour = (int)ahour; nowtime.tm_min = (int)amin; nowtime.tm_sec = (int)asec; nowtime.tm_isdst = -1; time_t (*mk)(struct tm*); if (timezone == MRB_TIMEZONE_UTC) { mk = timegm; } else { mk = mktime; } time_t nowsecs = (*mk)(&nowtime); if (nowsecs == (time_t)-1) { nowtime.tm_sec += 1; /* maybe Epoch-1 sec */ nowsecs = (*mk)(&nowtime); if (nowsecs != 0) { /* check if Epoch */ mrb_raise(mrb, E_ARGUMENT_ERROR, "Not a valid time"); } nowsecs = (time_t)-1; /* valid Epoch-1 */ } return time_alloc_time(mrb, nowsecs, ausec, timezone); } /* 15.2.19.6.2 */ /* Creates an instance of time at the given time in UTC. */ static mrb_value time_gm(mrb_state *mrb, mrb_value self) { mrb_int ayear = 0, amonth = 1, aday = 1, ahour = 0, amin = 0, asec = 0, ausec = 0; mrb_get_args(mrb, "i|iiiiii", &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec); return time_wrap(mrb, mrb_class_ptr(self), time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_UTC)); } /* 15.2.19.6.3 */ /* Creates an instance of time at the given time in local time zone. */ static mrb_value time_local(mrb_state *mrb, mrb_value self) { mrb_int ayear = 0, amonth = 1, aday = 1, ahour = 0, amin = 0, asec = 0, ausec = 0; mrb_get_args(mrb, "i|iiiiii", &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec); return time_wrap(mrb, mrb_class_ptr(self), time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_LOCAL)); } static struct mrb_time* time_get_ptr(mrb_state *mrb, mrb_value time) { struct mrb_time *tm = DATA_GET_PTR(mrb, time, &time_type, struct mrb_time); if (!tm) { mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized time"); } return tm; } static mrb_value time_eq(mrb_state *mrb, mrb_value self) { mrb_value other = mrb_get_arg1(mrb); struct mrb_time *tm1 = DATA_GET_PTR(mrb, self, &time_type, struct mrb_time); struct mrb_time *tm2 = DATA_CHECK_GET_PTR(mrb, other, &time_type, struct mrb_time); mrb_bool eq_p = tm1 && tm2 && tm1->sec == tm2->sec && tm1->usec == tm2->usec; return mrb_bool_value(eq_p); } static mrb_value time_cmp(mrb_state *mrb, mrb_value self) { mrb_value other = mrb_get_arg1(mrb); struct mrb_time *tm1 = DATA_GET_PTR(mrb, self, &time_type, struct mrb_time); struct mrb_time *tm2 = DATA_CHECK_GET_PTR(mrb, other, &time_type, struct mrb_time); if (!tm1 || !tm2) return mrb_nil_value(); if (tm1->sec > tm2->sec) { return mrb_fixnum_value(1); } else if (tm1->sec < tm2->sec) { return mrb_fixnum_value(-1); } /* tm1->sec == tm2->sec */ if (tm1->usec > tm2->usec) { return mrb_fixnum_value(1); } else if (tm1->usec < tm2->usec) { return mrb_fixnum_value(-1); } return mrb_fixnum_value(0); } static mrb_noreturn void int_overflow(mrb_state *mrb, const char *reason) { mrb_raisef(mrb, E_RANGE_ERROR, "time_t overflow in Time %s", reason); } static mrb_value time_plus(mrb_state *mrb, mrb_value self) { mrb_value o = mrb_get_arg1(mrb); time_t sec, usec; struct mrb_time *tm = time_get_ptr(mrb, self); sec = mrb_to_time_t(mrb, o, &usec); #ifdef MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS if (__builtin_add_overflow(tm->sec, sec, &sec)) { int_overflow(mrb, "addition"); } #else if (sec >= 0) { if (tm->sec > MRB_TIME_MAX - sec) { int_overflow(mrb, "addition"); } } else { if (tm->sec < MRB_TIME_MIN - sec) { int_overflow(mrb, "addition"); } } sec = tm->sec + sec; #endif return time_make_time(mrb, mrb_obj_class(mrb, self), sec, tm->usec+usec, tm->timezone); } static mrb_value time_minus(mrb_state *mrb, mrb_value self) { mrb_value other = mrb_get_arg1(mrb); struct mrb_time *tm = time_get_ptr(mrb, self); struct mrb_time *tm2 = DATA_CHECK_GET_PTR(mrb, other, &time_type, struct mrb_time); if (tm2) { #ifndef MRB_NO_FLOAT mrb_float f; f = (mrb_float)(tm->sec - tm2->sec) + (mrb_float)(tm->usec - tm2->usec) / 1.0e6; return mrb_float_value(mrb, f); #else mrb_int f; f = tm->sec - tm2->sec; if (tm->usec < tm2->usec) f--; return mrb_int_value(mrb, f); #endif } else { time_t sec, usec; sec = mrb_to_time_t(mrb, other, &usec); #ifdef MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS if (__builtin_sub_overflow(tm->sec, sec, &sec)) { int_overflow(mrb, "subtraction"); } #else if (sec >= 0) { if (tm->sec < MRB_TIME_MIN + sec) { int_overflow(mrb, "subtraction"); } } else { if (tm->sec > MRB_TIME_MAX + sec) { int_overflow(mrb, "subtraction"); } } sec = tm->sec - sec; #endif return time_make_time(mrb, mrb_obj_class(mrb, self), sec, tm->usec-usec, tm->timezone); } } /* 15.2.19.7.30 */ /* Returns week day number of time. */ static mrb_value time_wday(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_wday); } /* 15.2.19.7.31 */ /* Returns year day number of time. */ static mrb_value time_yday(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_yday + 1); } /* 15.2.19.7.32 */ /* Returns year of time. */ static mrb_value time_year(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_year + 1900); } static size_t time_zonename(mrb_state *mrb, struct mrb_time *tm, char *buf, size_t len) { #if defined(_MSC_VER) && _MSC_VER < 1900 || defined(__MINGW64__) || defined(__MINGW32__) struct tm datetime = {0}; time_t utc_sec = timegm(&tm->datetime); int offset = abs((int)(utc_sec - tm->sec) / 60); datetime.tm_year = 100; datetime.tm_hour = offset / 60; datetime.tm_min = offset % 60; buf[0] = utc_sec < tm->sec ? '-' : '+'; return strftime(buf+1, len-1, "%H%M", &datetime) + 1; #else return strftime(buf, len, "%z", &tm->datetime); #endif } /* 15.2.19.7.33 */ /* Returns name of time's timezone. */ static mrb_value time_zone(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); if (tm->timezone == MRB_TIMEZONE_UTC) { return mrb_str_new_lit(mrb, "UTC"); } char buf[64]; size_t len = time_zonename(mrb, tm, buf, sizeof(buf)); return mrb_str_new(mrb, buf, len); } /* 15.2.19.7.4 */ /* Returns a string that describes the time. */ static mrb_value time_asctime(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); struct tm *d = &tm->datetime; int len; #if defined(MRB_NO_STDIO) # ifdef NO_ASCTIME_R char *buf = asctime(d); # else char buf[32], *s; s = asctime_r(d, buf); # endif len = strlen(buf)-1; /* truncate the last newline */ #else char buf[32]; len = snprintf(buf, sizeof(buf), "%s %s %2d %02d:%02d:%02d %.4d", wday_names[d->tm_wday], mon_names[d->tm_mon], d->tm_mday, d->tm_hour, d->tm_min, d->tm_sec, d->tm_year + 1900); #endif return mrb_str_new(mrb, buf, len); } /* 15.2.19.7.6 */ /* Returns the day in the month of the time. */ static mrb_value time_day(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_mday); } /* 15.2.19.7.7 */ /* Returns true if daylight saving was applied for this time. */ static mrb_value time_dst_p(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); return mrb_bool_value(tm->datetime.tm_isdst); } /* 15.2.19.7.8 */ /* 15.2.19.7.10 */ /* Returns the Time object of the UTC(GMT) timezone. */ static mrb_value time_getutc(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); struct mrb_time *tm2 = (struct mrb_time*)mrb_malloc(mrb, sizeof(*tm)); *tm2 = *tm; tm2->timezone = MRB_TIMEZONE_UTC; time_update_datetime(mrb, tm2, TRUE); return time_wrap(mrb, mrb_obj_class(mrb, self), tm2); } /* 15.2.19.7.9 */ /* Returns the Time object of the LOCAL timezone. */ static mrb_value time_getlocal(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); struct mrb_time *tm2 = (struct mrb_time*)mrb_malloc(mrb, sizeof(*tm)); *tm2 = *tm; tm2->timezone = MRB_TIMEZONE_LOCAL; time_update_datetime(mrb, tm2, TRUE); return time_wrap(mrb, mrb_obj_class(mrb, self), tm2); } /* 15.2.19.7.15 */ /* Returns hour of time. */ static mrb_value time_hour(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_hour); } /* 15.2.19.7.16 */ /* Initializes a time by setting the amount of milliseconds since the epoch.*/ static mrb_value time_init(mrb_state *mrb, mrb_value self) { mrb_int ayear = 0, amonth = 1, aday = 1, ahour = 0, amin = 0, asec = 0, ausec = 0; mrb_int n = mrb_get_args(mrb, "|iiiiiii", &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec); struct mrb_time *tm = (struct mrb_time*)DATA_PTR(self); if (tm) { mrb_free(mrb, tm); } mrb_data_init(self, NULL, &time_type); if (n == 0) { tm = current_mrb_time(mrb); } else { tm = time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_LOCAL); } mrb_data_init(self, tm, &time_type); return self; } /* 15.2.19.7.17(x) */ /* Initializes a copy of this time object. */ static mrb_value time_init_copy(mrb_state *mrb, mrb_value copy) { mrb_value src = mrb_get_arg1(mrb); if (mrb_obj_equal(mrb, copy, src)) return copy; if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) { mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class"); } struct mrb_time *t1 = (struct mrb_time*)DATA_PTR(copy); struct mrb_time *t2 = (struct mrb_time*)DATA_PTR(src); if (!t2) { mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized time"); } if (!t1) { t1 = (struct mrb_time*)mrb_malloc(mrb, sizeof(struct mrb_time)); mrb_data_init(copy, t1, &time_type); } *t1 = *t2; return copy; } /* 15.2.19.7.18 */ /* Sets the timezone attribute of the Time object to LOCAL. */ static mrb_value time_localtime(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); tm->timezone = MRB_TIMEZONE_LOCAL; time_update_datetime(mrb, tm, FALSE); return self; } /* 15.2.19.7.19 */ /* Returns day of month of time. */ static mrb_value time_mday(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_mday); } /* 15.2.19.7.20 */ /* Returns minutes of time. */ static mrb_value time_min(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_min); } /* 15.2.19.7.21 (mon) and 15.2.19.7.22 (month) */ /* Returns month of time. */ static mrb_value time_mon(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_mon + 1); } /* 15.2.19.7.23 */ /* Returns seconds in minute of time. */ static mrb_value time_sec(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_sec); } #ifndef MRB_NO_FLOAT /* 15.2.19.7.24 */ /* Returns a Float with the time since the epoch in seconds. */ static mrb_value time_to_f(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); return mrb_float_value(mrb, (mrb_float)tm->sec + (mrb_float)tm->usec/1.0e6); } #endif /* 15.2.19.7.25 */ /* Returns an Integer with the time since the epoch in seconds. */ static mrb_value time_to_i(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); return time_value_from_time_t(mrb, tm->sec); } /* 15.2.19.7.26 */ /* Returns the number of microseconds for time. */ static mrb_value time_usec(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); return mrb_fixnum_value((mrb_int)tm->usec); } /* 15.2.19.7.27 */ /* Sets the timezone attribute of the Time object to UTC. */ static mrb_value time_utc(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); tm->timezone = MRB_TIMEZONE_UTC; time_update_datetime(mrb, tm, FALSE); return self; } /* 15.2.19.7.28 */ /* Returns true if this time is in the UTC timezone false if not. */ static mrb_value time_utc_p(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); return mrb_bool_value(tm->timezone == MRB_TIMEZONE_UTC); } static mrb_value time_to_s(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); char buf[64]; size_t len; if (tm->timezone == MRB_TIMEZONE_UTC) { len = strftime(buf, sizeof(buf), TO_S_FMT "UTC", &tm->datetime); } else { len = strftime(buf, sizeof(buf), TO_S_FMT, &tm->datetime); len += time_zonename(mrb, tm, buf+len, sizeof(buf)-len); } mrb_value str = mrb_str_new(mrb, buf, len); RSTR_SET_ASCII_FLAG(mrb_str_ptr(str)); return str; } static mrb_value time_hash(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); uint32_t hash = mrb_byte_hash((uint8_t*)&tm->sec, sizeof(time_t)); hash = mrb_byte_hash_step((uint8_t*)&tm->usec, sizeof(time_t), hash); hash = mrb_byte_hash_step((uint8_t*)&tm->timezone, sizeof(tm->timezone), hash); return mrb_int_value(mrb, hash); } #define wday_impl(num) \ struct mrb_time *tm = time_get_ptr(mrb, self);\ return mrb_bool_value(tm->datetime.tm_wday == (num)); static mrb_value time_sunday(mrb_state *mrb, mrb_value self) { wday_impl(0); } static mrb_value time_monday(mrb_state *mrb, mrb_value self) { wday_impl(1); } static mrb_value time_tuesday(mrb_state *mrb, mrb_value self) { wday_impl(2); } static mrb_value time_wednesday(mrb_state *mrb, mrb_value self) { wday_impl(3); } static mrb_value time_thursday(mrb_state *mrb, mrb_value self) { wday_impl(4); } static mrb_value time_friday(mrb_state *mrb, mrb_value self) { wday_impl(5); } static mrb_value time_saturday(mrb_state *mrb, mrb_value self) { wday_impl(6); } void mrb_mruby_time_gem_init(mrb_state* mrb) { /* ISO 15.2.19.2 */ struct RClass *tc = mrb_define_class_id(mrb, MRB_SYM(Time), mrb->object_class); MRB_SET_INSTANCE_TT(tc, MRB_TT_CDATA); mrb_include_module(mrb, tc, mrb_module_get_id(mrb, MRB_SYM(Comparable))); mrb_define_class_method_id(mrb, tc, MRB_SYM(at), time_at_m, MRB_ARGS_ARG(1, 1)); /* 15.2.19.6.1 */ mrb_define_class_method_id(mrb, tc, MRB_SYM(gm), time_gm, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.2 */ mrb_define_class_method_id(mrb, tc, MRB_SYM(local), time_local, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.3 */ mrb_define_class_method_id(mrb, tc, MRB_SYM(mktime), time_local, MRB_ARGS_ARG(1,6));/* 15.2.19.6.4 */ mrb_define_class_method_id(mrb, tc, MRB_SYM(now), time_now, MRB_ARGS_NONE()); /* 15.2.19.6.5 */ mrb_define_class_method_id(mrb, tc, MRB_SYM(utc), time_gm, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.6 */ mrb_define_method_id(mrb, tc, MRB_SYM(hash), time_hash , MRB_ARGS_NONE()); mrb_define_method_id(mrb, tc, MRB_SYM_Q(eql), time_eq , MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, tc, MRB_OPSYM(eq), time_eq , MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, tc, MRB_OPSYM(cmp), time_cmp , MRB_ARGS_REQ(1)); /* 15.2.19.7.1 */ mrb_define_method_id(mrb, tc, MRB_OPSYM(add), time_plus , MRB_ARGS_REQ(1)); /* 15.2.19.7.2 */ mrb_define_method_id(mrb, tc, MRB_OPSYM(sub), time_minus , MRB_ARGS_REQ(1)); /* 15.2.19.7.3 */ mrb_define_method_id(mrb, tc, MRB_SYM(to_s), time_to_s , MRB_ARGS_NONE()); mrb_define_method_id(mrb, tc, MRB_SYM(inspect), time_to_s , MRB_ARGS_NONE()); mrb_define_method_id(mrb, tc, MRB_SYM(asctime), time_asctime, MRB_ARGS_NONE()); /* 15.2.19.7.4 */ mrb_define_method_id(mrb, tc, MRB_SYM(ctime), time_asctime, MRB_ARGS_NONE()); /* 15.2.19.7.5 */ mrb_define_method_id(mrb, tc, MRB_SYM(day), time_day , MRB_ARGS_NONE()); /* 15.2.19.7.6 */ mrb_define_method_id(mrb, tc, MRB_SYM_Q(dst), time_dst_p , MRB_ARGS_NONE()); /* 15.2.19.7.7 */ mrb_define_method_id(mrb, tc, MRB_SYM(getgm), time_getutc , MRB_ARGS_NONE()); /* 15.2.19.7.8 */ mrb_define_method_id(mrb, tc, MRB_SYM(getlocal),time_getlocal,MRB_ARGS_NONE()); /* 15.2.19.7.9 */ mrb_define_method_id(mrb, tc, MRB_SYM(getutc), time_getutc , MRB_ARGS_NONE()); /* 15.2.19.7.10 */ mrb_define_method_id(mrb, tc, MRB_SYM_Q(gmt), time_utc_p , MRB_ARGS_NONE()); /* 15.2.19.7.11 */ mrb_define_method_id(mrb, tc, MRB_SYM(gmtime), time_utc , MRB_ARGS_NONE()); /* 15.2.19.7.13 */ mrb_define_method_id(mrb, tc, MRB_SYM(hour), time_hour, MRB_ARGS_NONE()); /* 15.2.19.7.15 */ mrb_define_method_id(mrb, tc, MRB_SYM(localtime), time_localtime, MRB_ARGS_NONE()); /* 15.2.19.7.18 */ mrb_define_method_id(mrb, tc, MRB_SYM(mday), time_mday, MRB_ARGS_NONE()); /* 15.2.19.7.19 */ mrb_define_method_id(mrb, tc, MRB_SYM(min), time_min, MRB_ARGS_NONE()); /* 15.2.19.7.20 */ mrb_define_method_id(mrb, tc, MRB_SYM(mon), time_mon, MRB_ARGS_NONE()); /* 15.2.19.7.21 */ mrb_define_method_id(mrb, tc, MRB_SYM(month), time_mon, MRB_ARGS_NONE()); /* 15.2.19.7.22 */ mrb_define_method_id(mrb, tc, MRB_SYM(sec), time_sec, MRB_ARGS_NONE()); /* 15.2.19.7.23 */ mrb_define_method_id(mrb, tc, MRB_SYM(to_i), time_to_i, MRB_ARGS_NONE()); /* 15.2.19.7.25 */ #ifndef MRB_NO_FLOAT mrb_define_method_id(mrb, tc, MRB_SYM(to_f), time_to_f, MRB_ARGS_NONE()); /* 15.2.19.7.24 */ #endif mrb_define_method_id(mrb, tc, MRB_SYM(usec), time_usec, MRB_ARGS_NONE()); /* 15.2.19.7.26 */ mrb_define_method_id(mrb, tc, MRB_SYM(utc), time_utc, MRB_ARGS_NONE()); /* 15.2.19.7.27 */ mrb_define_method_id(mrb, tc, MRB_SYM_Q(utc), time_utc_p,MRB_ARGS_NONE()); /* 15.2.19.7.28 */ mrb_define_method_id(mrb, tc, MRB_SYM(wday), time_wday, MRB_ARGS_NONE()); /* 15.2.19.7.30 */ mrb_define_method_id(mrb, tc, MRB_SYM(yday), time_yday, MRB_ARGS_NONE()); /* 15.2.19.7.31 */ mrb_define_method_id(mrb, tc, MRB_SYM(year), time_year, MRB_ARGS_NONE()); /* 15.2.19.7.32 */ mrb_define_method_id(mrb, tc, MRB_SYM(zone), time_zone, MRB_ARGS_NONE()); /* 15.2.19.7.33 */ mrb_define_method_id(mrb, tc, MRB_SYM(initialize), time_init, MRB_ARGS_REQ(1)); /* 15.2.19.7.16 */ mrb_define_method_id(mrb, tc, MRB_SYM(initialize_copy), time_init_copy, MRB_ARGS_REQ(1)); /* 15.2.19.7.17 */ mrb_define_method_id(mrb, tc, MRB_SYM_Q(sunday), time_sunday, MRB_ARGS_NONE()); mrb_define_method_id(mrb, tc, MRB_SYM_Q(monday), time_monday, MRB_ARGS_NONE()); mrb_define_method_id(mrb, tc, MRB_SYM_Q(tuesday), time_tuesday, MRB_ARGS_NONE()); mrb_define_method_id(mrb, tc, MRB_SYM_Q(wednesday), time_wednesday, MRB_ARGS_NONE()); mrb_define_method_id(mrb, tc, MRB_SYM_Q(thursday), time_thursday, MRB_ARGS_NONE()); mrb_define_method_id(mrb, tc, MRB_SYM_Q(friday), time_friday, MRB_ARGS_NONE()); mrb_define_method_id(mrb, tc, MRB_SYM_Q(saturday), time_saturday, MRB_ARGS_NONE()); /* methods not available: gmt_offset(15.2.19.7.12) gmtoff(15.2.19.7.14) utc_offset(15.2.19.7.29) */ } void mrb_mruby_time_gem_final(mrb_state* mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-method0000644000000000000000000000013215077107334021661 xustar0030 mtime=1761382108.624301757 30 atime=1761382109.797298366 30 ctime=1761382108.624301757 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-method/0000755000175100017510000000000015077107334022326 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-method/PaxHeaders/mrbgem.rake0000644000000000000000000000013115077107276024057 xustar0030 mtime=1761382078.122420556 30 atime=1761382080.142411316 29 ctime=1761382108.62330176 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-method/mrbgem.rake0000644000175100017510000000035515077107276024453 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-method') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Method and UnboundMethod class' spec.add_dependency('mruby-proc-ext', :core => 'mruby-proc-ext') end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-method/PaxHeaders/mrblib0000644000000000000000000000013215077107334023130 xustar0030 mtime=1761382108.627301748 30 atime=1761382109.797298366 30 ctime=1761382108.627301748 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-method/mrblib/0000755000175100017510000000000015077107334023575 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-method/mrblib/PaxHeaders/method.rb0000644000000000000000000000013215077107276025017 xustar0030 mtime=1761382078.122420556 30 atime=1761382080.142411316 30 ctime=1761382108.627301748 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-method/mrblib/method.rb0000644000175100017510000000047315077107276025413 0ustar00runnerrunnerclass Method def to_proc m = self lambda { |*args, **opts, &b| m.call(*args, **opts, &b) } end def <<(other) ->(*args, **opts, &block) { call(other.call(*args, **opts, &block)) } end def >>(other) ->(*args, **opts, &block) { other.call(call(*args, **opts, &block)) } end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-method/PaxHeaders/README.md0000644000000000000000000000013215077107276023222 xustar0030 mtime=1761382078.122420556 30 atime=1761382080.142411316 30 ctime=1761382108.624301757 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-method/README.md0000644000175100017510000000204515077107276023613 0ustar00runnerrunner# mruby-method An implementation of class **Method** and **UnboundMethod** for mruby ```ruby p Enumerable.instance_method(:find_all).source_location #=> ["mruby/mruby/mrblib/enum.rb", 148] ``` # Note You need to enable debug option in your build configuration to use `source_location` method in this gem, for example: ```ruby MRuby::Build.new do |conf| conf.enable_debug end ``` # Supported Methods ## Kernel - `Kernel#method` - `Kernel#singleton_method` ## Module - `Module#instance_method` ## Method class - `Method#name` - `Method#call` - `Method#super_method` - `Method#arity` - `Method#unbind` - `Method#[]` - `Method#owner` - `Method#receiver` - `Method#parameters` - `Method#source_location` - `Method#to_proc` ## UnboundMethod class - `UnboundMethod#name` - `UnboundMethod#bind` - `UnboundMethod#super_method` - `UnboundMethod#arity` - `UnboundMethod#owner` - `UnboundMethod#parameters` - `UnboundMethod#source_location` # See also - - nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-method/PaxHeaders/test0000644000000000000000000000013215077107334022640 xustar0030 mtime=1761382108.625301754 30 atime=1761382109.797298366 30 ctime=1761382108.625301754 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-method/test/0000755000175100017510000000000015077107334023305 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-method/test/PaxHeaders/method.rb0000644000000000000000000000013215077107276024527 xustar0030 mtime=1761382078.122420556 30 atime=1761382080.142411316 30 ctime=1761382108.625301754 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-method/test/method.rb0000644000175100017510000003344515077107276025130 0ustar00runnerrunnerclass Base def foo() :base end end class Derived < Base def foo() :derived end end class Interpreter attr_accessor :ret def do_a() @ret += "there, "; end def do_d() @ret += "Hello "; end def do_e() @ret += "!\n"; end def do_v() @ret += "Dave"; end Dispatcher = { "a" => instance_method(:do_a), "d" => instance_method(:do_d), "e" => instance_method(:do_e), "v" => instance_method(:do_v) } def interpret(string) @ret = "" string.split("").each {|b| Dispatcher[b].bind(self).call } end end assert 'demo' do interpreter = Interpreter.new interpreter.interpret('dave') assert_equal "Hello there, Dave!\n", interpreter.ret end assert 'Method#arity' do Class.new { attr_accessor :done def initialize; @done = false; end def m0() end def m1(a) end def m2(a, b) end def mo1(a = nil, &b) end def mo2(a, b = nil) end def mo3(*a) end def mo4(a, *b, &c) end def mo5(a, *b, c) end def mo6(a, *b, c, &d) end def mo7(a, b = nil, *c, d, &e) end def ma1((a), &b) nil && a end def run assert_equal(0, method(:m0).arity) assert_equal(1, method(:m1).arity) assert_equal(2, method(:m2).arity) assert_equal(-1, method(:mo1).arity) assert_equal(-2, method(:mo2).arity) assert_equal(-1, method(:mo3).arity) assert_equal(-2, method(:mo4).arity) assert_equal(-3, method(:mo5).arity) assert_equal(-3, method(:mo6).arity) assert_equal(-3, method(:mo7).arity) assert_equal(1, method(:ma1).arity) assert_equal(-1, method(:__send__).arity) assert_equal(-1, method(:nothing).arity) end def respond_to_missing?(m, b) m == :nothing end }.new.run end assert 'Method and UnboundMethod should not be have a `new` method' do assert_raise(NoMethodError){ Method.new } assert_raise(NoMethodError){ UnboundMethod.new } end assert 'instance' do assert_kind_of Method, 1.method(:+) assert_kind_of UnboundMethod, Integer.instance_method(:+) end assert 'Method#call' do assert_equal 3, 1.method(:+).call(2) assert_equal "ab", "a".method(:+)["b"] klass = Class.new { def foo; 42; end } klass2 = Class.new(klass) { def foo; super; end } assert_equal 42, klass2.new.method(:foo).call i = Class.new { def bar yield 3 end }.new assert_raise(LocalJumpError) { i.method(:bar).call } assert_equal 3, i.method(:bar).call { |i| i } assert_raise(ArgumentError) { nil.method(:__id__).call nil, 1 } assert_raise(ArgumentError) { nil.method(:__id__).call nil, opts: 1 } end assert 'Method#call for regression' do obj = BasicObject.new assert_equal String, Kernel.instance_method(:inspect).bind(obj).call().class, "https://github.com/ksss/mruby-method/issues/4" end assert 'Method#call with undefined method' do c = Class.new { attr_accessor :m, :argv def respond_to_missing?(m, b) m == :foo end def method_missing(m, *argv) @m = m @argv = argv super end } cc = c.new assert_raise(NameError) { cc.method(:nothing) } assert_kind_of Method, cc.method(:foo) assert_raise(NoMethodError) { cc.method(:foo).call(:arg1, :arg2) } assert_equal :foo, cc.m assert_equal [:arg1, :arg2], cc.argv cc = c.new m = cc.method(:foo) c.class_eval do def foo :ng end end assert_raise(NoMethodError) { m.call(:arg1, :arg2) } end assert 'Method#call with undefined method -- only kwargs' do c = Class.new { attr_accessor :m, :argv, :kwargs def respond_to_missing?(m, b) m == :foo end def method_missing(m, *argv, **kwargs) @m = m @argv = argv @kwargs = kwargs super end } cc = c.new assert_kind_of Method, cc.method(:foo) # Calling cc.method(:foo) works assert_raise(NoMethodError) { cc.method(:foo).call(kwarg1: :val1, kwarg2: :val2) } assert_equal :foo, cc.m assert_equal [], cc.argv assert_equal({ kwarg1: :val1, kwarg2: :val2 }, cc.kwargs) # calling cc.foo fails assert_raise(NoMethodError) { cc.foo(kwarg1: :val1, kwarg2: :val2) } assert_equal :foo, cc.m assert_equal [], cc.argv assert_equal({ kwarg1: :val1, kwarg2: :val2 }, cc.kwargs) end assert 'Method#source_location' do skip if proc{}.source_location.nil? filename = __FILE__ klass = Class.new lineno = __LINE__ + 1 klass.define_method(:find_me_if_you_can) {} assert_equal [filename, lineno], klass.new.method(:find_me_if_you_can).source_location lineno = __LINE__ + 1 class < { } values = { x: 1, y: 2, z: 3, w: [4, 5, 6], u: 7, v: 8, opts: { s: 9, t: 10 }, blk: blk } assert_equal values, o.method(:baz).to_proc.call(1, 2, 3, 4, 5, 6, u: 7, v: 8, s: 9, t: 10, &blk) assert_equal values, o.method(:baz).to_proc.call(1, 2, 3, 4, 5, 6, **{ u: 7, v: 8, s: 9, t: 10 }, &blk) assert_equal values, o.method(:baz).to_proc.call(*[1, 2, 3, 4, 5, 6], u: 7, v: 8, s: 9, t: 10, &blk) assert_equal values, o.method(:baz).to_proc.call(*[1, 2, 3, 4, 5, 6], **{ u: 7, v: 8, s: 9, t: 10 }, &blk) assert_raise(ArgumentError) { nil.method(:__id__).to_proc.call nil, 1 } assert_raise(ArgumentError) { nil.method(:__id__).to_proc.call nil, opts: 1 } end assert 'to_s' do o = Object.new def o.foo; end m = o.method(:foo) assert_match("#", m.unbind.inspect) c = Class.new c.class_eval { def foo; end; } m = c.new.method(:foo) assert_match("#", m.inspect) m = c.instance_method(:foo) assert_match("#", m.inspect) end assert 'owner' do c = Class.new do def foo; end def self.bar; end end m = Module.new do def baz; end end c.include(m) c2 = Class.new(c) assert_equal(c, c.instance_method(:foo).owner) assert_equal(c, c2.instance_method(:foo).owner) assert_equal(c, c.new.method(:foo).owner) assert_equal(c, c2.new.method(:foo).owner) assert_equal((class <>" do obj = Object.new class << obj def mul2(n); n * 2; end def add3(n); n + 3; end end f = obj.method(:mul2) g = obj.method(:add3) m1 = f << g assert_kind_of Proc, m1 assert_equal 16, m1.call(5) m2 = f >> g assert_kind_of Proc, m2 assert_equal 13, m2.call(5) end assert 'UnboundMethod#arity' do c = Class.new { def foo(a, b) end def respond_to_missing?(m, b) m == :nothing end } assert_equal 2, c.instance_method(:foo).arity assert_equal(-1, c.new.method(:nothing).unbind.arity) end assert 'UnboundMethod#==' do assert_false(Integer.instance_method(:+) == Integer.instance_method(:-)) assert_true(Integer.instance_method(:+) == Integer.instance_method(:+)) assert_true(UnboundMethod.instance_method(:==) == UnboundMethod.instance_method(:eql?)) skip unless Object.const_defined?(:Float) assert_false(Integer.instance_method(:+) == Float.instance_method(:+)) end assert 'UnboundMethod#super_method' do m = Derived.instance_method(:foo) m = m.super_method assert_equal(Base.instance_method(:foo), m) assert_nil(m.super_method) m = Object.instance_method(:object_id) assert_nil(m.super_method) end assert 'UnboundMethod#bind' do m = Module.new{ def meth() :meth end }.instance_method(:meth) assert_raise(ArgumentError) { m.bind } assert_kind_of Method, m.bind(1) assert_kind_of Method, m.bind(:sym) assert_kind_of Method, m.bind(Object.new) assert_equal(:meth, m.bind(1).call) assert_equal(:meth, m.bind(:sym).call) assert_equal(:meth, m.bind(Object.new).call) sc = nil Class.new { sc = class << self def foo end self end } assert_raise(TypeError) { sc.instance_method(:foo).bind([]) } assert_raise(TypeError) { Array.instance_method(:each).bind(1) } assert_kind_of Method, Object.instance_method(:object_id).bind(Object.new) end assert 'UnboundMethod#bind_call' do m = Array.instance_method(:size) assert_equal(:size, m.name) assert_equal(0, m.bind_call([])) assert_equal(1, m.bind_call([1])) assert_equal(2, m.bind_call([1,2])) o = Object.new def m(x, y, z, *w, u:, v:, **opts, &blk) { x:, y:, z:, w:, u:, v:, opts:, blk: } end m = o.method(:m).unbind blk = -> { } values = { x: 1, y: 2, z: 3, w: [4, 5, 6], u: 7, v: 8, opts: { s: 9, t: 10 }, blk: blk } assert_equal values, m.bind_call(o, 1, 2, 3, 4, 5, 6, u: 7, v: 8, s: 9, t: 10, &blk) assert_equal values, m.bind_call(o, 1, 2, 3, 4, 5, 6, **{ u: 7, v: 8, s: 9, t: 10 }, &blk) assert_equal values, m.bind_call(o, *[1, 2, 3, 4, 5, 6], u: 7, v: 8, s: 9, t: 10, &blk) assert_equal values, m.bind_call(o, *[1, 2, 3, 4, 5, 6], **{ u: 7, v: 8, s: 9, t: 10 }, &blk) assert_raise(ArgumentError) { m.bind_call } assert_raise(ArgumentError) { BasicObject.instance_method(:__id__).bind_call nil, 1 } assert_raise(ArgumentError) { BasicObject.instance_method(:__id__).bind_call nil, opts: 1 } end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-method/PaxHeaders/src0000644000000000000000000000013215077107334022450 xustar0030 mtime=1761382108.628301745 30 atime=1761382109.797298366 30 ctime=1761382108.628301745 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-method/src/0000755000175100017510000000000015077107334023115 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-method/src/PaxHeaders/method.c0000644000000000000000000000013215077107276024156 xustar0030 mtime=1761382078.122420556 30 atime=1761382080.142411316 30 ctime=1761382108.628301745 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-method/src/method.c0000644000175100017510000004440215077107276024552 0ustar00runnerrunner#include #include #include #include #include #include #include #include // Defined by mruby-proc-ext on which mruby-method depends mrb_value mrb_proc_parameters(mrb_state *mrb, mrb_value proc); mrb_value mrb_proc_source_location(mrb_state *mrb, const struct RProc *p); static mrb_value args_shift(mrb_state *mrb) { mrb_callinfo *ci = mrb->c->ci; mrb_value *argv = ci->stack + 1; if (ci->n < 15) { if (ci->n == 0) { goto argerr; } mrb_assert(ci->nk == 0 || ci->nk == 15); mrb_value obj = argv[0]; int count = ci->n + (ci->nk == 0 ? 0 : 1) + 1 /* block */ - 1 /* first value */; memmove(argv, argv + 1, count * sizeof(mrb_value)); ci->n--; return obj; } else if (RARRAY_LEN(*argv) > 0) { return mrb_ary_shift(mrb, *argv); } else { argerr: mrb_argnum_error(mrb, 0, 1, -1); return mrb_undef_value(); /* not reached */ } } static void args_unshift(mrb_state *mrb, mrb_value obj) { mrb_callinfo *ci = mrb->c->ci; mrb_value *argv = ci->stack + 1; if (ci->n < 15) { mrb_assert(ci->nk == 0 || ci->nk == 15); mrb_value args = mrb_ary_new_from_values(mrb, ci->n, argv); if (ci->nk == 0) { mrb_value block = argv[ci->n]; argv[0] = args; argv[1] = block; } else { mrb_value keyword = argv[ci->n]; mrb_value block = argv[ci->n + 1]; argv[0] = args; argv[1] = keyword; argv[2] = block; } ci->n = 15; } mrb_ary_unshift(mrb, *argv, obj); } static const struct RProc* method_missing_prepare(mrb_state *mrb, mrb_sym *mid, mrb_value recv, struct RClass **tc) { const mrb_sym id_method_missing = MRB_SYM(method_missing); mrb_callinfo *ci = mrb->c->ci; if (*mid == id_method_missing) { method_missing: ; int n = ci->n; mrb_value *argv = ci->stack + 1; mrb_value args = (n == 15) ? argv[0] : mrb_ary_new_from_values(mrb, n, argv); mrb_method_missing(mrb, id_method_missing, recv, args); } *tc = mrb_class(mrb, recv); mrb_method_t m = mrb_method_search_vm(mrb, tc, id_method_missing); if (MRB_METHOD_UNDEF_P(m)) { goto method_missing; } const struct RProc *proc; if (MRB_METHOD_FUNC_P(m)) { struct RProc *p = mrb_proc_new_cfunc(mrb, MRB_METHOD_FUNC(m)); MRB_PROC_SET_TARGET_CLASS(p, *tc); proc = p; } else { proc = MRB_METHOD_PROC(m); } args_unshift(mrb, mrb_symbol_value(*mid)); *mid = id_method_missing; return proc; } static struct RObject * method_object_alloc(mrb_state *mrb, struct RClass *mclass) { return MRB_OBJ_ALLOC(mrb, MRB_TT_OBJECT, mclass); } static const struct RProc* method_extract_proc(mrb_state *mrb, mrb_value self) { mrb_value obj = mrb_iv_get(mrb, self, MRB_SYM(_proc)); if (mrb_nil_p(obj)) { return NULL; } else { mrb_check_type(mrb, obj, MRB_TT_PROC); return mrb_proc_ptr(obj); } } static mrb_value method_extract_receiver(mrb_state *mrb, mrb_value self) { return mrb_iv_get(mrb, self, MRB_SYM(_recv)); } static mrb_sym method_extract_mid(mrb_state *mrb, mrb_value self) { mrb_value obj = mrb_iv_get(mrb, self, MRB_SYM(_name)); mrb_check_type(mrb, obj, MRB_TT_SYMBOL); return mrb_symbol(obj); } static struct RClass* method_extract_owner(mrb_state *mrb, mrb_value self) { mrb_value obj = mrb_iv_get(mrb, self, MRB_SYM(_owner)); switch (mrb_type(obj)) { case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_SCLASS: break; default: mrb_raise(mrb, E_TYPE_ERROR, "not class/module as owner of method object"); } return mrb_class_ptr(obj); } static void bind_check(mrb_state *mrb, mrb_value recv, mrb_value owner) { if (!mrb_module_p(owner) && mrb_class_ptr(owner) != mrb_obj_class(mrb, recv) && !mrb_obj_is_kind_of(mrb, recv, mrb_class_ptr(owner))) { if (mrb_sclass_p(owner)) { mrb_raise(mrb, E_TYPE_ERROR, "singleton method called for a different object"); } else { mrb_raisef(mrb, E_TYPE_ERROR, "bind argument must be an instance of %v", owner); } } } static mrb_value unbound_method_bind(mrb_state *mrb, mrb_value self) { mrb_value owner = mrb_iv_get(mrb, self, MRB_SYM(_owner)); mrb_value name = mrb_iv_get(mrb, self, MRB_SYM(_name)); mrb_value proc = mrb_iv_get(mrb, self, MRB_SYM(_proc)); mrb_value klass = mrb_iv_get(mrb, self, MRB_SYM(_klass)); mrb_value recv = mrb_get_arg1(mrb); bind_check(mrb, recv, owner); struct RObject *me = method_object_alloc(mrb, mrb_class_get_id(mrb, MRB_SYM(Method))); mrb_obj_iv_set(mrb, me, MRB_SYM(_owner), owner); mrb_obj_iv_set(mrb, me, MRB_SYM(_recv), recv); mrb_obj_iv_set(mrb, me, MRB_SYM(_name), name); mrb_obj_iv_set(mrb, me, MRB_SYM(_proc), proc); mrb_obj_iv_set(mrb, me, MRB_SYM(_klass), klass); return mrb_obj_value(me); } static mrb_bool method_p(mrb_state *mrb, struct RClass *c, mrb_value proc) { if (mrb_type(proc) != MRB_TT_OBJECT) return FALSE; if (!mrb_obj_is_instance_of(mrb, proc, c)) return FALSE; struct RObject *p = mrb_obj_ptr(proc); if (!mrb_obj_iv_defined(mrb, p, MRB_SYM(_owner))) return FALSE; if (!mrb_obj_iv_defined(mrb, p, MRB_SYM(_recv))) return FALSE; if (!mrb_obj_iv_defined(mrb, p, MRB_SYM(_name))) return FALSE; if (!mrb_obj_iv_defined(mrb, p, MRB_SYM(_proc))) return FALSE; if (!mrb_obj_iv_defined(mrb, p, MRB_SYM(_klass))) return FALSE; return TRUE; } #define IV_GET(value, name) mrb_iv_get(mrb, value, name) static mrb_value method_eql(mrb_state *mrb, mrb_value self) { mrb_value other = mrb_get_arg1(mrb); mrb_value orig_proc, other_proc; if (!method_p(mrb, mrb_class(mrb, self), other)) return mrb_false_value(); if (mrb_class_ptr(IV_GET(self, MRB_SYM(_owner))) != mrb_class_ptr(IV_GET(other, MRB_SYM(_owner)))) return mrb_false_value(); if (!mrb_obj_equal(mrb, IV_GET(self, MRB_SYM(_recv)), IV_GET(other, MRB_SYM(_recv)))) return mrb_false_value(); orig_proc = IV_GET(self, MRB_SYM(_proc)); other_proc = IV_GET(other, MRB_SYM(_proc)); if (mrb_nil_p(orig_proc) && mrb_nil_p(other_proc) && mrb_symbol(IV_GET(self, MRB_SYM(_name))) == mrb_symbol(IV_GET(other, MRB_SYM(_name)))) { return mrb_true_value(); } if (mrb_nil_p(orig_proc) || mrb_nil_p(other_proc)) { return mrb_false_value(); } return mrb_bool_value(mrb_proc_eql(mrb, orig_proc, other_proc)); } #undef IV_GET static mrb_value mcall(mrb_state *mrb, mrb_value self, mrb_value recv) { const struct RProc *proc = method_extract_proc(mrb, self); mrb_sym mid = method_extract_mid(mrb, self); struct RClass *tc = method_extract_owner(mrb, self); if (mrb_undef_p(recv)) { recv = method_extract_receiver(mrb, self); } else { bind_check(mrb, recv, mrb_obj_value(tc)); } if (!proc) { proc = method_missing_prepare(mrb, &mid, recv, &tc); } mrb->c->ci->mid = mid; mrb->c->ci->u.target_class = tc; return mrb_exec_irep(mrb, recv, proc); } static mrb_value method_call(mrb_state *mrb, mrb_value self) { return mcall(mrb, self, mrb_undef_value()); } static mrb_value method_bcall(mrb_state *mrb, mrb_value self) { mrb_value recv = args_shift(mrb); mrb_gc_protect(mrb, recv); return mcall(mrb, self, recv); } static mrb_value method_unbind(mrb_state *mrb, mrb_value self) { mrb_value owner = mrb_iv_get(mrb, self, MRB_SYM(_owner)); mrb_value name = mrb_iv_get(mrb, self, MRB_SYM(_name)); mrb_value proc = mrb_iv_get(mrb, self, MRB_SYM(_proc)); mrb_value klass = mrb_iv_get(mrb, self, MRB_SYM(_klass)); struct RObject *ume = method_object_alloc(mrb, mrb_class_get_id(mrb, MRB_SYM(UnboundMethod))); mrb_obj_iv_set(mrb, ume, MRB_SYM(_owner), owner); mrb_obj_iv_set(mrb, ume, MRB_SYM(_recv), mrb_nil_value()); mrb_obj_iv_set(mrb, ume, MRB_SYM(_name), name); mrb_obj_iv_set(mrb, ume, MRB_SYM(_proc), proc); mrb_obj_iv_set(mrb, ume, MRB_SYM(_klass), klass); return mrb_obj_value(ume); } static const struct RProc * method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid) { mrb_method_t m = mrb_method_search_vm(mrb, cp, mid); if (MRB_METHOD_UNDEF_P(m)) return NULL; if (MRB_METHOD_PROC_P(m)) return MRB_METHOD_PROC(m); struct RProc *proc = mrb_proc_new_cfunc(mrb, MRB_METHOD_FUNC(m)); if (MRB_METHOD_NOARG_P(m)) { proc->flags |= MRB_PROC_NOARG; } return proc; } static mrb_value method_super_method(mrb_state *mrb, mrb_value self) { mrb_value recv = mrb_iv_get(mrb, self, MRB_SYM(_recv)); mrb_value klass = mrb_iv_get(mrb, self, MRB_SYM(_klass)); mrb_value owner = mrb_iv_get(mrb, self, MRB_SYM(_owner)); mrb_value name = mrb_iv_get(mrb, self, MRB_SYM(_name)); struct RClass *super, *rklass; if (mrb_type(owner) == MRB_TT_MODULE) { struct RClass *m = mrb_class_ptr(owner); rklass = mrb_class_ptr(klass)->super; while (rklass && rklass->c != m) { rklass = rklass->super; } if (!rklass) return mrb_nil_value(); super = rklass->super; } else { super = mrb_class_ptr(owner)->super; } const struct RProc *proc = method_search_vm(mrb, &super, mrb_symbol(name)); if (!proc) return mrb_nil_value(); if (!super) return mrb_nil_value(); super = mrb_class_real(super); struct RObject *me = method_object_alloc(mrb, mrb_obj_class(mrb, self)); mrb_obj_iv_set(mrb, me, MRB_SYM(_owner), mrb_obj_value(super)); mrb_obj_iv_set(mrb, me, MRB_SYM(_recv), recv); mrb_obj_iv_set(mrb, me, MRB_SYM(_name), name); mrb_obj_iv_set(mrb, me, MRB_SYM(_proc), mrb_obj_value((void*)proc)); mrb_obj_iv_set(mrb, me, MRB_SYM(_klass), mrb_obj_value(super)); return mrb_obj_value(me); } static mrb_value method_arity(mrb_state *mrb, mrb_value self) { mrb_value proc = mrb_iv_get(mrb, self, MRB_SYM(_proc)); mrb_int arity = mrb_nil_p(proc) ? -1 : mrb_proc_arity(mrb_proc_ptr(proc)); return mrb_fixnum_value(arity); } static mrb_value method_source_location(mrb_state *mrb, mrb_value self) { mrb_value proc = mrb_iv_get(mrb, self, MRB_SYM(_proc)); if (mrb_nil_p(proc)) return mrb_nil_value(); return mrb_proc_source_location(mrb, mrb_proc_ptr(proc)); } static mrb_value method_parameters(mrb_state *mrb, mrb_value self) { mrb_value proc = mrb_iv_get(mrb, self, MRB_SYM(_proc)); if (mrb_nil_p(proc)) { mrb_value rest = mrb_symbol_value(MRB_SYM(rest)); mrb_value arest = mrb_ary_new_from_values(mrb, 1, &rest); return mrb_ary_new_from_values(mrb, 1, &arest); } return mrb_proc_parameters(mrb, proc); } static mrb_value method_to_s(mrb_state *mrb, mrb_value self) { mrb_value owner = mrb_iv_get(mrb, self, MRB_SYM(_owner)); mrb_value klass = mrb_iv_get(mrb, self, MRB_SYM(_klass)); mrb_value name = mrb_iv_get(mrb, self, MRB_SYM(_name)); mrb_value str = mrb_str_new_lit(mrb, "#<"); mrb_value proc = mrb_iv_get(mrb, self, MRB_SYM(_proc)); mrb_str_cat_cstr(mrb, str, mrb_obj_classname(mrb, self)); mrb_str_cat_lit(mrb, str, ": "); if (mrb_type(owner) == MRB_TT_SCLASS) { mrb_value recv = mrb_iv_get(mrb, self, MRB_SYM(_recv)); if (!mrb_nil_p(recv)) { mrb_str_concat(mrb, str, recv); mrb_str_cat_lit(mrb, str, "."); mrb_str_concat(mrb, str, name); goto finish; } } { struct RClass *ok = mrb_class_ptr(owner); struct RClass *rk = mrb_class_ptr(klass); struct RClass *rklass = mrb_class_real(rk); /* skip internal class */ if (ok == rk || ok == rklass) { mrb_str_concat(mrb, str, owner); mrb_str_cat_lit(mrb, str, "#"); mrb_str_concat(mrb, str, name); } else { mrb_str_concat(mrb, str, mrb_obj_value(rklass)); mrb_str_cat_lit(mrb, str, "("); mrb_str_concat(mrb, str, owner); mrb_str_cat_lit(mrb, str, ")#"); mrb_str_concat(mrb, str, name); } } finish:; if (!mrb_nil_p(proc)) { const struct RProc *p = mrb_proc_ptr(proc); if (MRB_PROC_ALIAS_P(p)) { mrb_sym mid; while (MRB_PROC_ALIAS_P(p)) { mid = p->body.mid; p = p->upper; } mrb_str_cat_lit(mrb, str, "("); mrb_str_concat(mrb, str, mrb_symbol_value(mid)); mrb_str_cat_lit(mrb, str, ")"); } } mrb_value loc = method_source_location(mrb, self); if (mrb_array_p(loc) && RARRAY_LEN(loc) == 2) { mrb_str_cat_lit(mrb, str, " "); mrb_str_concat(mrb, str, RARRAY_PTR(loc)[0]); mrb_str_cat_lit(mrb, str, ":"); mrb_str_concat(mrb, str, RARRAY_PTR(loc)[1]); } mrb_str_cat_lit(mrb, str, ">"); return str; } static mrb_bool search_method_owner(mrb_state *mrb, struct RClass *c, mrb_value obj, mrb_sym name, struct RClass **owner, const struct RProc **proc, mrb_bool unbound) { *owner = c; *proc = method_search_vm(mrb, owner, name); if (!*proc) { if (unbound) { return FALSE; } if (!mrb_respond_to(mrb, obj, MRB_SYM_Q(respond_to_missing))) { return FALSE; } mrb_value ret = mrb_funcall_id(mrb, obj, MRB_SYM_Q(respond_to_missing), 2, mrb_symbol_value(name), mrb_true_value()); if (!mrb_test(ret)) { return FALSE; } *owner = c; } return TRUE; } static mrb_noreturn void singleton_method_error(mrb_state *mrb, mrb_sym name, mrb_value obj) { mrb_raisef(mrb, E_NAME_ERROR, "undefined singleton method '%n' for '%!v'", name, obj); } static mrb_value method_alloc(mrb_state *mrb, struct RClass *c, mrb_value obj, mrb_sym name, mrb_bool unbound, mrb_bool singleton) { struct RClass *owner; const struct RProc *proc; if (!search_method_owner(mrb, c, obj, name, &owner, &proc, unbound)) { if (singleton) { singleton_method_error(mrb, name, obj); } else { mrb_raisef(mrb, E_NAME_ERROR, "undefined method '%n' for class '%C'", name, c); } } if (singleton && (owner->tt != MRB_TT_SCLASS && owner->tt != MRB_TT_ICLASS)) { singleton_method_error(mrb, name, obj); } while ((owner)->tt == MRB_TT_ICLASS) owner = (owner)->c; struct RObject *me = method_object_alloc(mrb, mrb_class_get_id(mrb, unbound ? MRB_SYM(UnboundMethod) : MRB_SYM(Method))); mrb_obj_iv_set(mrb, me, MRB_SYM(_owner), mrb_obj_value(owner)); mrb_obj_iv_set(mrb, me, MRB_SYM(_recv), unbound ? mrb_nil_value() : obj); mrb_obj_iv_set(mrb, me, MRB_SYM(_name), mrb_symbol_value(name)); mrb_obj_iv_set(mrb, me, MRB_SYM(_proc), proc ? mrb_obj_value((void*)proc) : mrb_nil_value()); mrb_obj_iv_set(mrb, me, MRB_SYM(_klass), mrb_obj_value(c)); return mrb_obj_value(me); } static mrb_value mrb_kernel_method(mrb_state *mrb, mrb_value self) { mrb_sym name; mrb_get_args(mrb, "n", &name); return method_alloc(mrb, mrb_class(mrb, self), self, name, FALSE, FALSE); } static mrb_value mrb_kernel_singleton_method(mrb_state *mrb, mrb_value self) { mrb_sym name; mrb_get_args(mrb, "n", &name); struct RClass *c = mrb_class(mrb, self); return method_alloc(mrb, c, self, name, FALSE, TRUE); } static mrb_value mrb_module_instance_method(mrb_state *mrb, mrb_value self) { mrb_sym name; mrb_get_args(mrb, "n", &name); return method_alloc(mrb, mrb_class_ptr(self), self, name, TRUE, FALSE); } static mrb_value method_owner(mrb_state *mrb, mrb_value self) { return mrb_iv_get(mrb, self, MRB_SYM(_owner)); } static mrb_value method_receiver(mrb_state *mrb, mrb_value self) { return mrb_iv_get(mrb, self, MRB_SYM(_recv)); } static mrb_value method_name(mrb_state *mrb, mrb_value self) { return mrb_iv_get(mrb, self, MRB_SYM(_name)); } void mrb_mruby_method_gem_init(mrb_state* mrb) { struct RClass *unbound_method = mrb_define_class_id(mrb, MRB_SYM(UnboundMethod), mrb->object_class); struct RClass *method = mrb_define_class_id(mrb, MRB_SYM(Method), mrb->object_class); MRB_SET_INSTANCE_TT(unbound_method, MRB_TT_OBJECT); MRB_UNDEF_ALLOCATOR(unbound_method); mrb_undef_class_method_id(mrb, unbound_method, MRB_SYM(new)); mrb_define_method_id(mrb, unbound_method, MRB_SYM(bind), unbound_method_bind, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, unbound_method, MRB_SYM(super_method), method_super_method, MRB_ARGS_NONE()); mrb_define_method_id(mrb, unbound_method, MRB_OPSYM(eq), method_eql, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, unbound_method, MRB_SYM_Q(eql), method_eql, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, unbound_method, MRB_SYM(to_s), method_to_s, MRB_ARGS_NONE()); mrb_define_method_id(mrb, unbound_method, MRB_SYM(inspect), method_to_s, MRB_ARGS_NONE()); mrb_define_method_id(mrb, unbound_method, MRB_SYM(arity), method_arity, MRB_ARGS_NONE()); mrb_define_method_id(mrb, unbound_method, MRB_SYM(source_location), method_source_location, MRB_ARGS_NONE()); mrb_define_method_id(mrb, unbound_method, MRB_SYM(parameters), method_parameters, MRB_ARGS_NONE()); mrb_define_method_id(mrb, unbound_method, MRB_SYM(bind_call), method_bcall, MRB_ARGS_REQ(1)|MRB_ARGS_ANY()); mrb_define_method_id(mrb, unbound_method, MRB_SYM(owner), method_owner, MRB_ARGS_NONE()); mrb_define_method_id(mrb, unbound_method, MRB_SYM(name), method_name, MRB_ARGS_NONE()); MRB_SET_INSTANCE_TT(method, MRB_TT_OBJECT); MRB_UNDEF_ALLOCATOR(method); mrb_undef_class_method_id(mrb, method, MRB_SYM(new)); mrb_define_method_id(mrb, method, MRB_OPSYM(eq), method_eql, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, method, MRB_SYM_Q(eql), method_eql, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, method, MRB_SYM(to_s), method_to_s, MRB_ARGS_NONE()); mrb_define_method_id(mrb, method, MRB_SYM(inspect), method_to_s, MRB_ARGS_NONE()); mrb_define_method_id(mrb, method, MRB_SYM(call), method_call, MRB_ARGS_ANY()); mrb_define_method_id(mrb, method, MRB_OPSYM(aref), method_call, MRB_ARGS_ANY()); mrb_define_method_id(mrb, method, MRB_SYM(unbind), method_unbind, MRB_ARGS_NONE()); mrb_define_method_id(mrb, method, MRB_SYM(super_method), method_super_method, MRB_ARGS_NONE()); mrb_define_method_id(mrb, method, MRB_SYM(arity), method_arity, MRB_ARGS_NONE()); mrb_define_method_id(mrb, method, MRB_SYM(source_location), method_source_location, MRB_ARGS_NONE()); mrb_define_method_id(mrb, method, MRB_SYM(parameters), method_parameters, MRB_ARGS_NONE()); mrb_define_method_id(mrb, method, MRB_SYM(owner), method_owner, MRB_ARGS_NONE()); mrb_define_method_id(mrb, method, MRB_SYM(receiver), method_receiver, MRB_ARGS_NONE()); mrb_define_method_id(mrb, method, MRB_SYM(name), method_name, MRB_ARGS_NONE()); mrb_define_method_id(mrb, mrb->kernel_module, MRB_SYM(method), mrb_kernel_method, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, mrb->kernel_module, MRB_SYM(singleton_method), mrb_kernel_singleton_method, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, mrb->module_class, MRB_SYM(instance_method), mrb_module_instance_method, MRB_ARGS_REQ(1)); } void mrb_mruby_method_gem_final(mrb_state* mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-exit0000644000000000000000000000013215077107334021352 xustar0030 mtime=1761382108.847301112 30 atime=1761382109.797298366 30 ctime=1761382108.847301112 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-exit/0000755000175100017510000000000015077107334022017 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-exit/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276023551 xustar0030 mtime=1761382078.118420575 30 atime=1761382080.139411329 30 ctime=1761382108.847301112 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-exit/mrbgem.rake0000644000175100017510000000023315077107276024137 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-exit') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Kernel#exit method' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-exit/PaxHeaders/src0000644000000000000000000000013215077107334022141 xustar0030 mtime=1761382108.848301109 30 atime=1761382109.797298366 30 ctime=1761382108.848301109 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-exit/src/0000755000175100017510000000000015077107334022606 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-exit/src/PaxHeaders/mruby_exit.c0000644000000000000000000000013115077107276024555 xustar0029 mtime=1761382078.11942057 30 atime=1761382080.139411329 30 ctime=1761382108.848301109 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-exit/src/mruby_exit.c0000644000175100017510000000374615077107276025160 0ustar00runnerrunner#include #include #include #include #include #ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 #endif #ifndef EXIT_FAILURE # define EXIT_FAILURE 1 #endif static int get_status(mrb_state *mrb) { mrb_value status = mrb_true_value(); mrb_get_args(mrb, "|o", &status); if (mrb_true_p(status)) return EXIT_SUCCESS; if (mrb_false_p(status)) return EXIT_FAILURE; return (int)mrb_as_int(mrb, status); } /* * call-seq: * exit(status=true) * * Initiates the termination of the Ruby script by raising the * SystemExit exception. This exception may be caught. The * optional parameter is used to return a status code to the invoking * environment. * * +true+ and +false+ of _status_ means success and failure * respectively. The interpretation of other integer values are * system dependent. * * exit(0) */ static mrb_value f_exit(mrb_state *mrb, mrb_value self) { int status = get_status(mrb); mrb_value exc = mrb_obj_new(mrb, mrb_exc_get_id(mrb, MRB_SYM(SystemExit)), 0, NULL); struct RException *e = mrb_exc_ptr(exc); e->flags |= MRB_EXC_EXIT; mrb_iv_set(mrb, exc, MRB_SYM(status), mrb_int_value(mrb, (mrb_int)status)); mrb_exc_raise(mrb, exc); /* not reached */ return mrb_nil_value(); } /* * call-seq: * exit!(status=true) * * Exits the process immediately. No exit handlers are run. * status is returned to the underlying system as the * exit status. * * exit!(0) */ static mrb_value f_exit_bang(mrb_state *mrb, mrb_value self) { exit(get_status(mrb)); /* not reached */ return mrb_nil_value(); } void mrb_mruby_exit_gem_init(mrb_state* mrb) { mrb_define_class_id(mrb, MRB_SYM(SystemExit), E_EXCEPTION); mrb_define_private_method_id(mrb, mrb->kernel_module, MRB_SYM(exit), f_exit, MRB_ARGS_OPT(1)); mrb_define_private_method_id(mrb, mrb->kernel_module, MRB_SYM_B(exit), f_exit_bang, MRB_ARGS_OPT(1)); } void mrb_mruby_exit_gem_final(mrb_state* mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-toplevel-ext0000644000000000000000000000013215077107334023031 xustar0030 mtime=1761382108.913300922 30 atime=1761382109.797298366 30 ctime=1761382108.913300922 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-toplevel-ext/0000755000175100017510000000000015077107334023476 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-toplevel-ext/PaxHeaders/mrbgem.rake0000644000000000000000000000013115077107276025227 xustar0029 mtime=1761382078.13042052 30 atime=1761382080.149411284 30 ctime=1761382108.913300922 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-toplevel-ext/mrbgem.rake0000644000175100017510000000027115077107276025620 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-toplevel-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'toplevel object (main) methods extension' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-toplevel-ext/PaxHeaders/mrblib0000644000000000000000000000013215077107334024300 xustar0030 mtime=1761382108.915300916 30 atime=1761382109.797298366 30 ctime=1761382108.915300916 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-toplevel-ext/mrblib/0000755000175100017510000000000015077107334024745 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-toplevel-ext/mrblib/PaxHeaders/toplevel.rb0000644000000000000000000000013115077107276026540 xustar0029 mtime=1761382078.13042052 30 atime=1761382080.149411284 30 ctime=1761382108.915300916 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-toplevel-ext/mrblib/toplevel.rb0000644000175100017510000000012715077107276027131 0ustar00runnerrunnerclass < :value}, Hash(key: :value)) assert_raise(TypeError) { Hash([1, 2, 3]) } end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-kernel-ext/PaxHeaders/src0000644000000000000000000000013215077107334023246 xustar0030 mtime=1761382108.732301445 30 atime=1761382109.797298366 30 ctime=1761382108.732301445 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-kernel-ext/src/0000755000175100017510000000000015077107334023713 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-kernel-ext/src/PaxHeaders/kernel.c0000644000000000000000000000013115077107276024753 xustar0030 mtime=1761382078.121420561 29 atime=1761382080.14141132 30 ctime=1761382108.732301445 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-kernel-ext/src/kernel.c0000644000175100017510000001640315077107276025350 0ustar00runnerrunner#include #include #include #include #include #include #include #include #include #include static mrb_value mrb_f_caller(mrb_state *mrb, mrb_value self) { mrb_value bt, v; mrb_int bt_len, argc, lev, n; argc = mrb_get_args(mrb, "|oi", &v, &n); bt = mrb_get_backtrace(mrb); bt_len = RARRAY_LEN(bt); switch (argc) { case 0: lev = 1; n = bt_len - 1; break; case 1: if (mrb_range_p(v)) { mrb_int beg, len; if (mrb_range_beg_len(mrb, v, &beg, &len, bt_len, TRUE) == MRB_RANGE_OK) { lev = beg; n = len; } else { return mrb_nil_value(); } } else { lev = mrb_as_int(mrb, v); if (lev < 0) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%v)", v); } n = bt_len - lev; } break; case 2: lev = mrb_as_int(mrb, v); break; default: /* not reached */ lev = n = 0; break; } if (lev >= bt_len) return mrb_nil_value(); if (lev < 0) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%v)", v); } if (n < 0) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative size (%d)", n); } if (n == 0) { return mrb_ary_new(mrb); } if (bt_len <= n + lev) n = bt_len - lev - 1; return mrb_ary_new_from_values(mrb, n, RARRAY_PTR(bt)+lev+1); } /* * call-seq: * __method__ -> symbol * * Returns the called name of the current method as a Symbol. * If called outside of a method, it returns nil. * */ static mrb_value mrb_f_method(mrb_state *mrb, mrb_value self) { mrb_callinfo *ci = mrb->c->ci; ci--; if (ci->proc && ci->proc->e.env && ci->proc->e.env->tt == MRB_TT_ENV && ci->proc->e.env->mid) return mrb_symbol_value(ci->proc->e.env->mid); else if (ci->mid) return mrb_symbol_value(ci->mid); else return mrb_nil_value(); } /* * call-seq: * __callee__ -> symbol * * Returns the called name of the current method as a Symbol. * If called outside of a method, it returns nil. * */ static mrb_value mrb_f_callee(mrb_state *mrb, mrb_value self) { mrb_callinfo *ci = mrb->c->ci; ci--; if (ci->mid) return mrb_symbol_value(ci->mid); else return mrb_nil_value(); } /* * call-seq: * Integer(arg,base=0) -> integer * * Converts arg to a Integer. * Numeric types are converted directly (with floating-point numbers * being truncated). base (0, or between 2 and 36) is a base for * integer string representation. If arg is a String, * when base is omitted or equals to zero, radix indicators * (0, 0b, and 0x) are honored. * In any case, strings should be strictly conformed to numeric * representation. This behavior is different from that of * String#to_i. Non string values will be treated as integers. * Passing nil raises a TypeError. * * Integer(123.999) #=> 123 * Integer("0x1a") #=> 26 * Integer(Time.new) #=> 1204973019 * Integer("0930", 10) #=> 930 * Integer("111", 2) #=> 7 * Integer(nil) #=> TypeError */ static mrb_value mrb_f_integer(mrb_state *mrb, mrb_value self) { mrb_value val, tmp; mrb_int base = 0; mrb_get_args(mrb, "o|i", &val, &base); if (mrb_nil_p(val)) { if (base != 0) goto arg_error; mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Integer"); } switch (mrb_type(val)) { #ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: if (base != 0) goto arg_error; return mrb_float_to_integer(mrb, val); #endif case MRB_TT_INTEGER: if (base != 0) goto arg_error; return val; case MRB_TT_STRING: string_conv: return mrb_str_to_integer(mrb, val, base, TRUE); default: break; } if (base != 0) { tmp = mrb_obj_as_string(mrb, val); if (mrb_string_p(tmp)) { val = tmp; goto string_conv; } arg_error: mrb_raise(mrb, E_ARGUMENT_ERROR, "base specified for non string value"); } /* to raise TypeError */ return mrb_ensure_integer_type(mrb, val); } #ifndef MRB_NO_FLOAT /* * call-seq: * Float(arg) -> float * * Returns arg converted to a float. Numeric types are converted * directly, the rest are converted using arg.to_f. * * Float(1) #=> 1.0 * Float(123.456) #=> 123.456 * Float("123.456") #=> 123.456 * Float(nil) #=> TypeError */ static mrb_value mrb_f_float(mrb_state *mrb, mrb_value self) { mrb_value arg = mrb_get_arg1(mrb); if (mrb_string_p(arg)) { return mrb_float_value(mrb, mrb_str_to_dbl(mrb, arg, TRUE)); } return mrb_ensure_float_type(mrb, arg); } #endif /* * call-seq: * String(arg) -> string * * Returns arg as an String. * converted using to_s method. * * String(self) #=> "main" * String(self.class) #=> "Object" * String(123456) #=> "123456" */ static mrb_value mrb_f_string(mrb_state *mrb, mrb_value self) { mrb_value arg = mrb_get_arg1(mrb); mrb_value tmp; tmp = mrb_type_convert(mrb, arg, MRB_TT_STRING, MRB_SYM(to_s)); return tmp; } /* * call-seq: * Array(arg) -> array * * Returns +arg+ as an Array using to_a method. * * Array(1..5) #=> [1, 2, 3, 4, 5] * */ static mrb_value mrb_f_array(mrb_state *mrb, mrb_value self) { mrb_value arg = mrb_get_arg1(mrb); mrb_value tmp; tmp = mrb_type_convert_check(mrb, arg, MRB_TT_ARRAY, MRB_SYM(to_a)); if (mrb_nil_p(tmp)) { return mrb_ary_new_from_values(mrb, 1, &arg); } return tmp; } /* * call-seq: * Hash(arg) -> hash * * Returns a Hash if arg is a Hash. * Returns an empty Hash when arg is nil * or []. * * Hash([]) #=> {} * Hash(nil) #=> {} * Hash(key: :value) #=> {:key => :value} * Hash([1, 2, 3]) #=> TypeError * */ static mrb_value mrb_f_hash(mrb_state *mrb, mrb_value self) { mrb_value arg = mrb_get_arg1(mrb); if (mrb_nil_p(arg) || (mrb_array_p(arg) && RARRAY_LEN(arg) == 0)) { return mrb_hash_new(mrb); } mrb_ensure_hash_type(mrb, arg); return arg; } void mrb_mruby_kernel_ext_gem_init(mrb_state *mrb) { struct RClass *krn = mrb->kernel_module; mrb_define_private_method_id(mrb, krn, MRB_SYM(fail), mrb_f_raise, MRB_ARGS_OPT(2)); mrb_define_private_method_id(mrb, krn, MRB_SYM(caller), mrb_f_caller, MRB_ARGS_OPT(2)); mrb_define_private_method_id(mrb, krn, MRB_SYM(__method__), mrb_f_method, MRB_ARGS_NONE()); mrb_define_private_method_id(mrb, krn, MRB_SYM(__callee__), mrb_f_callee, MRB_ARGS_NONE()); mrb_define_private_method_id(mrb, krn, MRB_SYM(Integer), mrb_f_integer, MRB_ARGS_ARG(1,1)); #ifndef MRB_NO_FLOAT mrb_define_private_method_id(mrb, krn, MRB_SYM(Float), mrb_f_float, MRB_ARGS_REQ(1)); #endif mrb_define_private_method_id(mrb, krn, MRB_SYM(String), mrb_f_string, MRB_ARGS_REQ(1)); mrb_define_private_method_id(mrb, krn, MRB_SYM(Array), mrb_f_array, MRB_ARGS_REQ(1)); mrb_define_private_method_id(mrb, krn, MRB_SYM(Hash), mrb_f_hash, MRB_ARGS_REQ(1)); } void mrb_mruby_kernel_ext_gem_final(mrb_state *mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/math.gembox0000644000000000000000000000013215077107276021463 xustar0030 mtime=1761382078.104420639 30 atime=1761382080.125411393 30 ctime=1761382108.800301248 nghttp2-1.68.0/third-party/mruby/mrbgems/math.gembox0000644000175100017510000000046015077107276022053 0ustar00runnerrunner# It also works with MRB_NO_STDIO. MRuby::GemBox.new do |conf| # Use standard Math module conf.gem :core => "mruby-math" # Use Rational/Complex numbers conf.gem :core => "mruby-rational" conf.gem :core => "mruby-complex" # Use Multi-precision Integer conf.gem :core => "mruby-bigint" end nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-symbol-ext0000644000000000000000000000013215077107334022504 xustar0030 mtime=1761382108.664301641 30 atime=1761382109.797298366 30 ctime=1761382108.664301641 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-symbol-ext/0000755000175100017510000000000015077107334023151 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-symbol-ext/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024703 xustar0030 mtime=1761382078.129420524 30 atime=1761382080.147411293 30 ctime=1761382108.664301641 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-symbol-ext/mrbgem.rake0000644000175100017510000000024515077107276025274 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-symbol-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Symbol class extension' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-symbol-ext/PaxHeaders/mrblib0000644000000000000000000000013215077107334023753 xustar0030 mtime=1761382108.667301633 30 atime=1761382109.797298366 30 ctime=1761382108.667301633 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-symbol-ext/mrblib/0000755000175100017510000000000015077107334024420 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-symbol-ext/mrblib/PaxHeaders/symbol.rb0000644000000000000000000000013215077107276025667 xustar0030 mtime=1761382078.129420524 30 atime=1761382080.147411293 30 ctime=1761382108.667301633 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-symbol-ext/mrblib/symbol.rb0000644000175100017510000000241515077107276026261 0ustar00runnerrunnerclass Symbol include Comparable alias intern to_sym ## # call-seq: # sym.capitalize -> symbol # # Same as sym.to_s.capitalize.intern. def capitalize (self.to_s.capitalize! || self).to_sym end ## # call-seq: # sym.downcase -> symbol # # Same as sym.to_s.downcase.intern. def downcase (self.to_s.downcase! || self).to_sym end ## # call-seq: # sym.upcase -> symbol # # Same as sym.to_s.upcase.intern. def upcase (self.to_s.upcase! || self).to_sym end ## # call-seq: # sym.casecmp(other) -> -1, 0, +1 or nil # # Case-insensitive version of Symbol#<=>. def casecmp(other) return nil unless other.kind_of?(Symbol) lhs = self.to_s; lhs.upcase! rhs = other.to_s.upcase lhs <=> rhs end ## # call-seq: # sym.casecmp?(other) -> true, false, or nil # # Returns true if sym and other_sym are equal after case folding, # false if they are not equal, and nil if other_sym is not a string. def casecmp?(sym) c = self.casecmp(sym) return nil if c.nil? return c == 0 end # # call-seq: # sym.empty? -> true or false # # Returns that _sym_ is :"" or not. def empty? self.length == 0 end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-symbol-ext/PaxHeaders/test0000644000000000000000000000013215077107334023463 xustar0030 mtime=1761382108.666301636 30 atime=1761382109.797298366 30 ctime=1761382108.666301636 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-symbol-ext/test/0000755000175100017510000000000015077107334024130 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-symbol-ext/test/PaxHeaders/symbol.rb0000644000000000000000000000013215077107276025377 xustar0030 mtime=1761382078.129420524 30 atime=1761382080.147411293 30 ctime=1761382108.666301636 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-symbol-ext/test/symbol.rb0000644000175100017510000000255715077107276026000 0ustar00runnerrunner## # Symbol(Ext) Test if Symbol.respond_to?(:all_symbols) assert('Symbol.all_symbols') do foo = [:__symbol_test_1, :__symbol_test_2, :__symbol_test_3].sort symbols = Symbol.all_symbols.select{|sym|sym.to_s.include? '__symbol_test'}.sort assert_equal foo, symbols end end %w[size length].each do |n| assert("Symbol##{n}") do assert_equal 5, :hello.__send__(n) assert_equal 4, :"aA\0b".__send__(n) if __ENCODING__ == "UTF-8" assert_equal 8, :"ã“ã‚“ã«ã¡ã¯ä¸–界!".__send__(n) assert_equal 4, :"aã‚\0b".__send__(n) else assert_equal 22, :"ã“ã‚“ã«ã¡ã¯ä¸–界!".__send__(n) assert_equal 6, :"aã‚\0b".__send__(n) end end end assert("Symbol#capitalize") do assert_equal :Hello, :hello.capitalize assert_equal :Hello, :HELLO.capitalize assert_equal :Hello, :Hello.capitalize end assert("Symbol#downcase") do assert_equal :hello, :hEllO.downcase assert_equal :hello, :hello.downcase end assert("Symbol#upcase") do assert_equal :HELLO, :hEllO.upcase assert_equal :HELLO, :HELLO.upcase end assert("Symbol#casecmp") do assert_equal 0, :HELLO.casecmp(:hEllO) assert_equal 1, :HELLO.casecmp(:hEllN) assert_equal(-1, :HELLO.casecmp(:hEllP)) assert_nil :HELLO.casecmp("hEllO") end assert("Symbol#empty?") do assert_false :'a'.empty? end assert('Symbol#intern') do assert_equal :test, :test.intern end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-symbol-ext/PaxHeaders/src0000644000000000000000000000013015077107334023271 xustar0029 mtime=1761382108.66830163 30 atime=1761382109.797298366 29 ctime=1761382108.66830163 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-symbol-ext/src/0000755000175100017510000000000015077107334023740 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-symbol-ext/src/PaxHeaders/symbol.c0000644000000000000000000000013115077107276025025 xustar0030 mtime=1761382078.129420524 30 atime=1761382080.147411293 29 ctime=1761382108.66830163 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-symbol-ext/src/symbol.c0000644000175100017510000000372615077107276025426 0ustar00runnerrunner#include #include #include #include #include /* * call-seq: * Symbol.all_symbols => array * * Returns an array of all the symbols currently in Ruby's symbol * table. * * Symbol.all_symbols.size #=> 903 * Symbol.all_symbols[1,20] #=> [:floor, :ARGV, :Binding, :symlink, * :chown, :EOFError, :$;, :String, * :LOCK_SH, :"setuid?", :$<, * :default_proc, :compact, :extend, * :Tms, :getwd, :$=, :ThreadGroup, * :wait2, :$>] */ #ifdef MRB_USE_ALL_SYMBOLS static mrb_value mrb_sym_all_symbols(mrb_state *mrb, mrb_value self) { mrb_value ary = mrb_ary_new_capa(mrb, mrb->symidx); for (mrb_sym i=1; i<=MRB_PRESYM_MAX; i++) { mrb_ary_push(mrb, ary, mrb_symbol_value(i)); } mrb_sym lim = mrb->symidx + 1; for (mrb_sym i=1; i integer * * Same as sym.to_s.length. */ static mrb_value mrb_sym_length(mrb_state *mrb, mrb_value self) { mrb_int len; #ifdef MRB_UTF8_STRING mrb_int byte_len; const char *name = mrb_sym_name_len(mrb, mrb_symbol(self), &byte_len); len = mrb_utf8_strlen(name, byte_len); #else mrb_sym_name_len(mrb, mrb_symbol(self), &len); #endif return mrb_fixnum_value(len); } void mrb_mruby_symbol_ext_gem_init(mrb_state* mrb) { struct RClass *s = mrb->symbol_class; #ifdef MRB_USE_ALL_SYMBOLS mrb_define_class_method_id(mrb, s, MRB_SYM(all_symbols), mrb_sym_all_symbols, MRB_ARGS_NONE()); #endif mrb_define_method_id(mrb, s, MRB_SYM(length), mrb_sym_length, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_SYM(size), mrb_sym_length, MRB_ARGS_NONE()); } void mrb_mruby_symbol_ext_gem_final(mrb_state* mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-test-inline-struct0000644000000000000000000000013215077107334024156 xustar0030 mtime=1761382108.779301309 30 atime=1761382109.797298366 30 ctime=1761382108.779301309 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-test-inline-struct/0000755000175100017510000000000015077107334024623 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-test-inline-struct/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276026355 xustar0030 mtime=1761382078.129420524 30 atime=1761382080.147411293 30 ctime=1761382108.779301309 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-test-inline-struct/mrbgem.rake0000644000175100017510000000024715077107276026750 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-test-inline-struct') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'inline structure' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-test-inline-struct/PaxHeaders/test0000644000000000000000000000013215077107334025135 xustar0030 mtime=1761382108.781301303 30 atime=1761382109.797298366 30 ctime=1761382108.781301303 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-test-inline-struct/test/0000755000175100017510000000000015077107334025602 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-test-inline-struct/test/PaxHeaders/inline.c0000644000000000000000000000013215077107276026641 xustar0030 mtime=1761382078.129420524 30 atime=1761382080.147411293 30 ctime=1761382108.780301306 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-test-inline-struct/test/inline.c0000644000175100017510000000532315077107276027234 0ustar00runnerrunner#include #include #include #include static mrb_value istruct_test_initialize(mrb_state *mrb, mrb_value self) { char *string = (char*)mrb_istruct_ptr(self); mrb_int size = mrb_istruct_size(); mrb_value object = mrb_get_arg1(mrb); if (mrb_integer_p(object)) { strncpy(string, "fixnum", size-1); } #ifndef MRB_NO_FLOAT else if (mrb_float_p(object)) { strncpy(string, "float", size-1); } #endif else if (mrb_string_p(object)) { strncpy(string, "string", size-1); } else { strncpy(string, "anything", size-1); } string[size - 1] = 0; // force NULL at the end return self; } static mrb_value istruct_test_to_s(mrb_state *mrb, mrb_value self) { return mrb_str_new_cstr(mrb, (const char*)mrb_istruct_ptr(self)); } static mrb_value istruct_test_length(mrb_state *mrb, mrb_value self) { return mrb_fixnum_value(mrb_istruct_size()); } static mrb_value istruct_test_test_receive(mrb_state *mrb, mrb_value self) { mrb_value object = mrb_get_arg1(mrb); if (mrb_obj_class(mrb, object) != mrb_class_get(mrb, "InlineStructTest")) { mrb_raise(mrb, E_TYPE_ERROR, "Expected InlineStructTest"); } return mrb_bool_value(((char*)mrb_istruct_ptr(object))[0] == 's'); } static mrb_value istruct_test_test_receive_direct(mrb_state *mrb, mrb_value self) { mrb_value is = mrb_get_arg1(mrb); struct RClass *klass = mrb_class_get(mrb, "InlineStructTest"); /* if you need to protect istruct retrieval from untrusted code, you need to care about class replacing. See mrbgem/mruby-random/src/random.c for detail */ if (mrb_istruct_p(is) && mrb_obj_is_kind_of(mrb, is, klass)) { char *ptr = (char*)mrb_istruct_ptr(is); return mrb_bool_value(ptr[0] == 's'); } mrb_raise(mrb, E_TYPE_ERROR, "InlineStructTest"); return mrb_false_value(); } static mrb_value istruct_test_mutate(mrb_state *mrb, mrb_value self) { char *ptr = (char*)mrb_istruct_ptr(self); memcpy(ptr, "mutate", 6); return mrb_nil_value(); } void mrb_mruby_test_inline_struct_gem_test(mrb_state *mrb) { struct RClass *cls; cls = mrb_define_class(mrb, "InlineStructTest", mrb->object_class); MRB_SET_INSTANCE_TT(cls, MRB_TT_ISTRUCT); mrb_define_method(mrb, cls, "initialize", istruct_test_initialize, MRB_ARGS_REQ(1)); mrb_define_method(mrb, cls, "to_s", istruct_test_to_s, MRB_ARGS_NONE()); mrb_define_method(mrb, cls, "mutate", istruct_test_mutate, MRB_ARGS_NONE()); mrb_define_class_method(mrb, cls, "length", istruct_test_length, MRB_ARGS_NONE()); mrb_define_class_method(mrb, cls, "test_receive", istruct_test_test_receive, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, cls, "test_receive_direct", istruct_test_test_receive_direct, MRB_ARGS_REQ(1)); } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-test-inline-struct/test/PaxHeaders/inline.rb0000644000000000000000000000013215077107276027022 xustar0030 mtime=1761382078.129420524 30 atime=1761382080.147411293 30 ctime=1761382108.781301303 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-test-inline-struct/test/inline.rb0000644000175100017510000000544415077107276027421 0ustar00runnerrunner## # InlineStruct Test class InlineStructTest def extra_method :ok end def test_ivar_set @var = :ivar end def test_ivar_get @vat end end assert('InlineStructTest#dup') do obj = InlineStructTest.new(1) assert_equal obj.to_s, 'fixnum' assert_equal obj.dup.to_s, 'fixnum' end assert('InlineStructTest#clone') do obj = InlineStructTest.new(1) assert_equal obj.to_s, 'fixnum' assert_equal obj.clone.to_s, 'fixnum' end assert('InlineStruct#object_id') do obj1 = InlineStructTest.new(1) obj2 = InlineStructTest.new(1) assert_not_equal obj1, obj2 assert_not_equal obj1.object_id, obj2.object_id assert_not_equal obj1.object_id, obj1.dup.object_id assert_not_equal obj1.object_id, obj1.clone.object_id end assert('InlineStructTest#mutate (dup)') do obj1 = InlineStructTest.new("foo") assert_equal obj1.to_s, "string" obj2 = obj1.dup assert_equal obj2.to_s, "string" obj1.mutate assert_equal obj1.to_s, "mutate" assert_equal obj2.to_s, "string" end assert('InlineStructTest#mutate (clone)') do obj1 = InlineStructTest.new("foo") assert_equal obj1.to_s, "string" obj2 = obj1.clone assert_equal obj2.to_s, "string" obj1.mutate assert_equal obj1.to_s, "mutate" assert_equal obj2.to_s, "string" end assert('InlineStructTest#test_receive(string)') do assert_equal InlineStructTest.test_receive(InlineStructTest.new('a')), true end assert('InlineStructTest#test_receive(float)') do assert_equal InlineStructTest.test_receive(InlineStructTest.new(1.25)), false end assert('InlineStructTest#test_receive(invalid object)') do assert_raise(TypeError) do InlineStructTest.test_receive([]) end end assert('InlineStructTest#test_receive(string)') do assert_equal InlineStructTest.test_receive_direct(InlineStructTest.new('a')), true end assert('InlineStructTest#test_receive(float)') do assert_equal InlineStructTest.test_receive_direct(InlineStructTest.new(1.25)), false end assert('InlineStructTest#test_receive(invalid object)') do assert_raise(TypeError) do InlineStructTest.test_receive_direct([]) end end assert('InlineStructTest#extra_method') do assert_equal InlineStructTest.new(1).extra_method, :ok end assert('InlineStructTest instance variable') do obj = InlineStructTest.new(1) assert_raise(ArgumentError) do obj.test_ivar_set end assert_equal obj.test_ivar_get, nil end # 64-bit mode if InlineStructTest.length == 24 assert('InlineStructTest length [64-bit]') do assert_equal InlineStructTest.length, 3 * 8 end end # 32-bit mode if InlineStructTest.length == 12 assert('InlineStructTest length [32-bit]') do assert_equal InlineStructTest.length, 3 * 4 end end # 16-bit mode if InlineStructTest.length == 6 assert('InlineStructTest length [16-bit]') do assert_equal InlineStructTest.length, 3 * 2 end end nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-errno0000644000000000000000000000013215077107334021526 xustar0030 mtime=1761382108.903300951 30 atime=1761382109.797298366 30 ctime=1761382108.903300951 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-errno/0000755000175100017510000000000015077107334022173 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-errno/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276023725 xustar0030 mtime=1761382078.117420579 30 atime=1761382080.139411329 30 ctime=1761382108.901300956 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-errno/mrbgem.rake0000644000175100017510000000027615077107276024322 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-errno') do |spec| spec.license = 'MIT' spec.authors = 'Internet Initiative Japan Inc.' spec.summary = 'Errno module and SystemCallError class' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-errno/PaxHeaders/mrblib0000644000000000000000000000013215077107334022775 xustar0030 mtime=1761382108.905300945 30 atime=1761382109.797298366 30 ctime=1761382108.905300945 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-errno/mrblib/0000755000175100017510000000000015077107334023442 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-errno/mrblib/PaxHeaders/errno.rb0000644000000000000000000000013215077107276024531 xustar0030 mtime=1761382078.117420579 30 atime=1761382080.139411329 30 ctime=1761382108.905300945 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-errno/mrblib/errno.rb0000644000175100017510000000046315077107276025124 0ustar00runnerrunnermodule Errno def Errno.const_defined?(name) __errno_defined?(name) or super end def Errno.const_missing(name) __errno_define(name) or super end # Module#constants is defined in mruby-metaprog # So, it may be raised NoMethodError def Errno.constants __errno_list(super) end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-errno/PaxHeaders/README.md0000644000000000000000000000013215077107276023067 xustar0030 mtime=1761382078.117420579 30 atime=1761382080.139411329 30 ctime=1761382108.903300951 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-errno/README.md0000644000175100017510000000214515077107276023461 0ustar00runnerrunner# mruby-errno Errno module for mruby ## License Copyright (c) 2013 Internet Initiative Japan Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-errno/PaxHeaders/test0000644000000000000000000000013215077107334022505 xustar0030 mtime=1761382108.904300948 30 atime=1761382109.797298366 30 ctime=1761382108.904300948 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-errno/test/0000755000175100017510000000000015077107334023152 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-errno/test/PaxHeaders/errno.rb0000644000000000000000000000013215077107276024241 xustar0030 mtime=1761382078.118420575 30 atime=1761382080.139411329 30 ctime=1761382108.904300948 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-errno/test/errno.rb0000644000175100017510000000313015077107276024626 0ustar00runnerrunnerassert('Errno') do assert_equal(Module, Errno.class) end assert('SystemCallError') do assert_equal(Class, SystemCallError.class) end assert('SystemCallError superclass') do assert_equal(StandardError, SystemCallError.superclass) end assert('SystemCallError#initialize') do assert_equal("unknown error - a", SystemCallError.new("a").message) assert_equal("Unknown error: 12345 - a", SystemCallError.new("a", 12345).message) assert_equal("Unknown error: 12345", SystemCallError.new(12345).message) end assert('SystemCallError#errno') do assert_equal 1, SystemCallError.new("a", 1).errno assert_equal 1, SystemCallError.new(1).errno assert_equal 12345, SystemCallError.new("a", 12345).errno assert_equal 23456, SystemCallError.new(23456).errno end assert('SystemCallError#inspect') do assert_equal("#", SystemCallError.new("a").inspect) end assert('Errno::NOERROR') do assert_equal(Class, Errno::NOERROR.class) end # Is there any platform does not have EPERM? assert('Errno::EPERM') do assert_equal(Class, Errno::EPERM.class) end assert('Errno::EPERM superclass') do assert_equal(SystemCallError, Errno::EPERM.superclass) end assert('Errno::EPERM::Errno') do assert_true(Errno::EPERM::Errno.is_a?(Fixnum)) end assert('Errno::EPERM#message') do msg = Errno::EPERM.new.message assert_equal("#{msg} - a", Errno::EPERM.new("a").message) end assert('Errno::EPERM#inspect') do e = Errno::EPERM.new msg = e.message assert_equal("#", e.inspect) assert_equal("#", Errno::EPERM.new("a").inspect) end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-errno/PaxHeaders/src0000644000000000000000000000013215077107334022315 xustar0030 mtime=1761382108.911300927 30 atime=1761382109.797298366 30 ctime=1761382108.911300927 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-errno/src/0000755000175100017510000000000015077107334022762 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-errno/src/PaxHeaders/known_errors.def0000644000000000000000000000013115077107276025606 xustar0030 mtime=1761382078.118420575 30 atime=1761382080.139411329 29 ctime=1761382108.91030093 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-errno/src/known_errors.def0000644000175100017510000000250315077107276026177 0ustar00runnerrunnerEPERM ENOENT ESRCH EINTR EIO ENXIO E2BIG ENOEXEC EBADF ECHILD EAGAIN ENOMEM EACCES EFAULT ENOTBLK EBUSY EEXIST EXDEV ENODEV ENOTDIR EISDIR EINVAL ENFILE EMFILE ENOTTY ETXTBSY EFBIG ENOSPC ESPIPE EROFS EMLINK EPIPE EDOM ERANGE EDEADLK ENAMETOOLONG ENOLCK ENOSYS ENOTEMPTY ELOOP EWOULDBLOCK ENOMSG EIDRM ECHRNG EL2NSYNC EL3HLT EL3RST ELNRNG EUNATCH ENOCSI EL2HLT EBADE EBADR EXFULL ENOANO EBADRQC EBADSLT EDEADLOCK EBFONT ENOSTR ENODATA ETIME ENOSR ENONET ENOPKG EREMOTE ENOLINK EADV ESRMNT ECOMM EPROTO ENOTCAPABLE ECAPMODE EMULTIHOP EDOTDOT EBADMSG EPWROFF EDEVERR EOVERFLOW EBADEXEC EBADARCH EBADMACHO ESHLIBVERS ENOTUNIQ EBADFD EREMCHG ELIBACC ELIBBAD ELIBSCN ELIBMAX ELIBEXEC EILSEQ ERESTART ESTRPIPE EUSERS ENOTSOCK EDESTADDRREQ EMSGSIZE EPROTOTYPE ENOPROTOOPT EPROTONOSUPPORT ESOCKTNOSUPPORT EOPNOTSUPP EPFNOSUPPORT EAFNOSUPPORT EADDRINUSE EADDRNOTAVAIL ENETDOWN ENETUNREACH ENETRESET ECONNABORTED ECONNRESET ENOBUFS EISCONN ENOTCONN ESHUTDOWN ETOOMANYREFS ETIMEDOUT ECONNREFUSED EHOSTDOWN EHOSTUNREACH EALREADY EINPROGRESS ESTALE EUCLEAN ENOTNAM ENAVAIL EISNAM EREMOTEIO EDQUOT ECANCELED EKEYEXPIRED EKEYREJECTED EKEYREVOKED EMEDIUMTYPE ENOKEY ENOMEDIUM ENOTRECOVERABLE EOWNERDEAD EINTEGRITY ERFKILL EAUTH EBADRPC EDOOFUS EFTYPE ENEEDAUTH ENOATTR ENOTSUP EPROCLIM EPROCUNAVAIL EPROGMISMATCH EPROGUNAVAIL ERPCMISMATCH EIPSEC ENOPOLICY EQFULL nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-errno/src/PaxHeaders/gen.rb0000644000000000000000000000013215077107276023475 xustar0030 mtime=1761382078.118420575 30 atime=1761382080.139411329 30 ctime=1761382108.907300939 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-errno/src/gen.rb0000755000175100017510000000047415077107276024075 0ustar00runnerrunner#!/usr/bin/env ruby Dir.chdir(File.dirname($0)) d = File.open("known_errors_def.cstub", "w") IO.readlines("known_errors.def").each { |name| next if name =~ /^#/ name.strip! d.write < #include #include #include #include #include #include #include #include #include #include #include static const struct { #ifdef MRB_NO_PRESYM #define itsdefined(name, sym) { #name, name }, const char *name; #else #define itsdefined(name, sym) { sym, name }, mrb_sym sym; #endif int eno; } e2c[] = { #define itsnotdefined(name, sym) #include "known_errors_def.cstub" #undef itsdefined #undef itsnotdefined }; static const struct { #ifdef MRB_NO_PRESYM #define itsnotdefined(name, sym) { #name }, const char *name; #else #define itsnotdefined(name, sym) { sym }, mrb_sym sym; #endif } noe2c[] = { #define itsdefined(name, sym) #include "known_errors_def.cstub" #undef itsdefined #undef itsnotdefined }; #ifdef MRB_NO_PRESYM #define ENTRY_SYM(e) mrb_intern_static(mrb, (e).name, strlen((e).name)) #else #define ENTRY_SYM(e) (e).sym #endif #define E2C_LEN (sizeof(e2c) / sizeof(e2c[0])) #define NOE2C_LEN (sizeof(noe2c) / sizeof(noe2c[0])) static struct RClass* mrb_errno_define_exxx(mrb_state *mrb, mrb_sym name, int eno) { struct RClass *errno_module = mrb_module_get_id(mrb, MRB_SYM(Errno)); if (mrb_const_defined_at(mrb, mrb_obj_value(errno_module), name)) { mrb_value v = mrb_const_get(mrb, mrb_obj_value(errno_module), name); if (mrb_class_p(v)) { return mrb_class_ptr(v); } } struct RClass *sce_class = mrb_exc_get_id(mrb, MRB_SYM(SystemCallError)); struct RClass *e = mrb_define_class_under_id(mrb, errno_module, name, sce_class); mrb_define_const_id(mrb, e, MRB_SYM(Errno), mrb_fixnum_value(eno)); return e; } #ifndef MRB_NO_PRESYM typedef mrb_sym sym_ref; #define sym_ref_init(mrb, id) (id) #define errno_name_matched_p(errentry, ref) ((errentry).sym == *(ref)) #else typedef struct { const char *name; size_t len; } sym_ref; static sym_ref sym_ref_init(mrb_state *mrb, mrb_sym id) { mrb_int len = 0; const char *name = mrb_sym_name_len(mrb, id, &len); sym_ref ename = { name, (size_t)len }; return ename; } #define errno_name_matched_p(errentry, ref) errno_name_matched_p_0((errentry).name, (ref)) static mrb_bool errno_name_matched_p_0(const char *name, const sym_ref *ref) { if (ref->len == strlen(name) && memcmp(ref->name, name, ref->len) == 0) { return TRUE; } else { return FALSE; } } #endif // MRB_NO_PRESYM static mrb_bool ary_included_in_head(mrb_state *mrb, mrb_value ary, mrb_value obj, mrb_ssize head) { const mrb_value *p = RARRAY_PTR(ary); for (; head > 0; head--, p++) { if (mrb_obj_eq(mrb, obj, *p)) { return TRUE; } } return FALSE; } static mrb_value mrb_errno_defined_p(mrb_state *mrb, mrb_value self) { mrb_sym name; mrb_get_args(mrb, "n", &name); const sym_ref ref = sym_ref_init(mrb, name); for (size_t i = 0; i < E2C_LEN; i++) { if (errno_name_matched_p(e2c[i], &ref)) { return mrb_true_value(); } } for (size_t i = 0; i < NOE2C_LEN; i++) { if (errno_name_matched_p(noe2c[i], &ref)) { return mrb_true_value(); } } return mrb_false_value(); } static mrb_value mrb_errno_define(mrb_state *mrb, mrb_value self) { mrb_sym name; mrb_get_args(mrb, "n", &name); const sym_ref ref = sym_ref_init(mrb, name); for (size_t i = 0; i < E2C_LEN; i++) { if (errno_name_matched_p(e2c[i], &ref)) { return mrb_obj_value(mrb_errno_define_exxx(mrb, name, e2c[i].eno)); } } for (size_t i = 0; i < NOE2C_LEN; i++) { if (errno_name_matched_p(noe2c[i], &ref)) { struct RClass *errno_module = mrb_module_get_id(mrb, MRB_SYM(Errno)); return mrb_obj_value(mrb_class_get_under_id(mrb, errno_module, MRB_SYM(NOERROR))); } } return mrb_nil_value(); } static mrb_value mrb_errno_list(mrb_state *mrb, mrb_value self) { mrb_value list; mrb_get_args(mrb, "A", &list); mrb_ary_modify(mrb, mrb_ary_ptr(list)); mrb_ssize head = RARRAY_LEN(list); for (size_t i = 0; i < E2C_LEN; i++) { mrb_value id = mrb_symbol_value(ENTRY_SYM(e2c[i])); if (!ary_included_in_head(mrb, list, id, head)) { mrb_ary_push(mrb, list, id); } } for (size_t i = 0; i < NOE2C_LEN; i++) { mrb_value id = mrb_symbol_value(ENTRY_SYM(noe2c[i])); if (!ary_included_in_head(mrb, list, id, head)) { mrb_ary_push(mrb, list, id); } } return list; } static void mrb_sce_init(mrb_state *mrb, mrb_value self, mrb_value m, mrb_value no) { mrb_value str; char buf[20]; if (!mrb_nil_p(no)) { size_t i; int n = (int)mrb_as_int(mrb, no); for (i=0; i < E2C_LEN; i++) { if (e2c[i].eno == n) { mrb_basic_ptr(self)->c = mrb_errno_define_exxx(mrb, ENTRY_SYM(e2c[i]), e2c[i].eno); str = mrb_str_new_cstr(mrb, strerror(n)); break; } } if (i == E2C_LEN) { mrb_iv_set(mrb, self, MRB_SYM(errno), mrb_fixnum_value(n)); str = mrb_str_new_cstr(mrb, "Unknown error: "); char *bp = mrb_int_to_cstr(buf, sizeof(buf), n, 10); mrb_str_cat2(mrb, str, bp); } } else { str = mrb_str_new_cstr(mrb, "unknown error"); } if (!mrb_nil_p(m)) { mrb_str_cat2(mrb, str, " - "); mrb_str_append(mrb, str, m); } mrb_exc_mesg_set(mrb, mrb_exc_ptr(self), str); } static mrb_value mrb_exxx_init(mrb_state *mrb, mrb_value self) { mrb_value m = mrb_nil_value(); mrb_get_args(mrb, "|S", &m); mrb_sce_init(mrb, self, m, mrb_nil_value()); return self; } static mrb_value mrb_sce_init_m(mrb_state *mrb, mrb_value self) { if (mrb_class(mrb, self) != mrb_exc_get_id(mrb, MRB_SYM(SystemCallError))) { return mrb_exxx_init(mrb, self); } mrb_value m, n; if (mrb_get_args(mrb, "o|o", &m, &n) == 1) { if (mrb_fixnum_p(m)) { n = m; m = mrb_nil_value(); } else { n = mrb_nil_value(); } } mrb_sce_init(mrb, self, m, n); return self; } static mrb_value mrb_sce_errno(mrb_state *mrb, mrb_value self) { struct RClass *c; mrb_sym sym; c = mrb_class(mrb, self); sym = MRB_SYM(Errno); if (mrb_const_defined_at(mrb, mrb_obj_value(c), sym)) { return mrb_const_get(mrb, mrb_obj_value(c), sym); } else { sym = MRB_SYM(errno); return mrb_attr_get(mrb, self, sym); } } static mrb_value mrb_sce_sys_fail(mrb_state *mrb, mrb_value cls) { struct RClass *sce; mrb_value msg, no; mrb_int argc; mrb->c->ci->mid = 0; sce = mrb_class_ptr(cls); argc = mrb_get_args(mrb, "o|S", &no, &msg); struct RBasic* e = mrb_obj_alloc(mrb, MRB_TT_EXCEPTION, sce); mrb_value exc = mrb_obj_value(e); if (argc == 1) { msg = mrb_nil_value(); } mrb_sce_init(mrb, exc, msg, no); mrb_exc_raise(mrb, exc); return mrb_nil_value(); /* NOTREACHED */ } void mrb_mruby_errno_gem_init(mrb_state *mrb) { struct RClass *sce = mrb_define_class_id(mrb, MRB_SYM(SystemCallError), E_STANDARD_ERROR); mrb_define_class_method_id(mrb, sce, MRB_SYM(_sys_fail), mrb_sce_sys_fail, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, sce, MRB_SYM(errno), mrb_sce_errno, MRB_ARGS_NONE()); mrb_define_method_id(mrb, sce, MRB_SYM(initialize), mrb_sce_init_m, MRB_ARGS_ARG(1, 1)); struct RClass *eno = mrb_define_module_id(mrb, MRB_SYM(Errno)); mrb_define_class_method_id(mrb, eno, MRB_SYM_Q(__errno_defined), mrb_errno_defined_p, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, eno, MRB_SYM(__errno_define), mrb_errno_define, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, eno, MRB_SYM(__errno_list), mrb_errno_list, MRB_ARGS_REQ(1)); struct RClass *e = mrb_define_class_under_id(mrb, eno, MRB_SYM(NOERROR), sce); mrb_define_const_id(mrb, e, MRB_SYM(Errno), mrb_fixnum_value(0)); //mrb_define_method_id(mrb, e, MRB_SYM(eqq), mrb_exxx_cmp, MRB_ARGS_REQ(1)); // Pre-allocation for Errno::ENOMEM only mrb_errno_define_exxx(mrb, MRB_SYM(ENOMEM), ENOMEM); } void mrb_mruby_errno_gem_final(mrb_state *mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-proc-ext0000644000000000000000000000013215077107334022142 xustar0030 mtime=1761382108.702301532 30 atime=1761382109.797298366 30 ctime=1761382108.702301532 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-ext/0000755000175100017510000000000015077107334022607 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-ext/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024341 xustar0030 mtime=1761382078.124420547 30 atime=1761382080.144411306 30 ctime=1761382108.702301532 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-ext/mrbgem.rake0000644000175100017510000000024115077107276024726 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-proc-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Proc class extension' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-ext/PaxHeaders/mrblib0000644000000000000000000000013015077107334023407 xustar0029 mtime=1761382108.70630152 30 atime=1761382109.797298366 29 ctime=1761382108.70630152 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-ext/mrblib/0000755000175100017510000000000015077107334024056 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-ext/mrblib/PaxHeaders/proc.rb0000644000000000000000000000013115077107276024762 xustar0030 mtime=1761382078.124420547 30 atime=1761382080.144411306 29 ctime=1761382108.70630152 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-ext/mrblib/proc.rb0000644000175100017510000000177115077107276025361 0ustar00runnerrunnerclass Proc def ===(*args) call(*args) end def yield(*args) call(*args) end def to_proc self end def curry(arity=self.arity) type = :proc abs = lambda {|a| a < 0 ? -a - 1 : a} arity = abs[arity] if lambda? type = :lambda self_arity = self.arity if (self_arity >= 0 && arity != self_arity) || (self_arity < 0 && abs[self_arity] > arity) raise ArgumentError, "wrong number of arguments (given #{arity}, expected #{abs[self_arity]})" end end pproc = self make_curry = proc do |given_args=[]| __send__(type) do |*args| new_args = given_args + args if new_args.size >= arity pproc[*new_args] else make_curry[new_args] end end end make_curry.call end def <<(other) ->(*args, **opts, &block) { call(other.call(*args, **opts, &block)) } end def >>(other) ->(*args, **opts, &block) { other.call(call(*args, **opts, &block)) } end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-ext/PaxHeaders/test0000644000000000000000000000013215077107334023121 xustar0030 mtime=1761382108.705301523 30 atime=1761382109.797298366 30 ctime=1761382108.705301523 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-ext/test/0000755000175100017510000000000015077107334023566 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-ext/test/PaxHeaders/proc.rb0000644000000000000000000000013215077107276024473 xustar0030 mtime=1761382078.125420543 30 atime=1761382080.144411306 30 ctime=1761382108.704301526 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-ext/test/proc.rb0000644000175100017510000000666315077107276025076 0ustar00runnerrunner## # Proc(Ext) Test def enable_debug_info? return @enable_debug_info unless @enable_debug_info == nil begin raise rescue => e @enable_debug_info = !e.backtrace.empty? if @enable_debug_info && e.backtrace[0].include?("(unknown)") @enable_debug_info = false end return @enable_debug_info end end assert('Proc#source_location') do skip unless enable_debug_info? file, line = Proc.new{}.source_location assert_equal __FILE__, file assert_equal __LINE__ - 2, line end assert('Proc#inspect') do ins = Proc.new{}.inspect if enable_debug_info? metas = %w(\\ * ? [ ] { }) file = __FILE__.split("").map{|c| metas.include?(c) ? "\\#{c}" : c}.join line = __LINE__ - 4 else file = line = "-" end assert_match "#", ins end assert('Proc#lambda?') do assert_true lambda{}.lambda? assert_true !Proc.new{}.lambda? end assert('Proc#===') do proc = Proc.new {|a| a * 2} assert_equal 20, (proc === 10) end assert('Proc#yield') do proc = Proc.new {|a| a * 2} assert_equal 20, proc.yield(10) end assert('Proc#curry') do b = proc {|x, y, z| (x||0) + (y||0) + (z||0) } assert_equal 6, b.curry[1][2][3] assert_equal 6, b.curry[1, 2][3, 4] assert_equal 6, b.curry(5)[1][2][3][4][5] assert_equal 6, b.curry(5)[1, 2][3, 4][5] assert_equal 1, b.curry(1)[1] b = lambda {|x, y, z| (x||0) + (y||0) + (z||0) } assert_equal 6, b.curry[1][2][3] assert_raise(ArgumentError) { b.curry[1, 2][3, 4] } assert_raise(ArgumentError) { b.curry(5) } assert_raise(ArgumentError) { b.curry(1) } assert_false(proc{}.curry.lambda?) assert_true(lambda{}.curry.lambda?) end assert('Proc#parameters') do assert_equal([], Proc.new {}.parameters) assert_equal([], Proc.new {||}.parameters) assert_equal([[:opt, :a]], Proc.new {|a|}.parameters) assert_equal([[:req, :a]], lambda {|a|}.parameters) assert_equal([[:opt, :a]], lambda {|a=nil|}.parameters) assert_equal([[:req, :a]], ->(a){}.parameters) assert_equal([[:rest, :*]], lambda { |*| }.parameters) assert_equal([[:rest, :a]], Proc.new {|*a|}.parameters) assert_equal([[:opt, :x], [:opt, :y], [:rest, :other]], Proc.new{|x,y=42,*other|}.parameters) assert_equal([[:opt, :a], [:opt, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:opt, :f], [:opt, :g], [:block, :h]], Proc.new {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters) assert_equal([[:req, :a], [:req, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:req, :f], [:req, :g], [:block, :h]], lambda {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters) end assert('Proc#to_proc') do proc = Proc.new {} assert_equal proc, proc.to_proc end assert('Kernel#proc') do assert_true !proc{|a|}.lambda? assert_raise LocalJumpError do proc{ break }.call end end assert "Proc#<< and Proc#>>" do add3 = ->(n) { n + 3 } mul2 = ->(n) { n * 2 } f1 = mul2 << add3 assert_kind_of Proc, f1 assert_equal 16, f1.call(5) f2 = mul2 >> add3 assert_kind_of Proc, f2 assert_equal 13, f2.call(5) end assert('mrb_proc_new_cfunc_with_env') do ProcExtTest.mrb_proc_new_cfunc_with_env(:test) ProcExtTest.mrb_proc_new_cfunc_with_env(:mruby) t = ProcExtTest.new assert_equal :test, t.test assert_equal :mruby, t.mruby end assert('mrb_cfunc_env_get') do ProcExtTest.mrb_cfunc_env_get :get_int, [0, 1, 2] t = ProcExtTest.new assert_raise(TypeError) { t.cfunc_without_env } assert_raise(IndexError) { t.get_int(-1) } assert_raise(IndexError) { t.get_int(3) } assert_equal 1, t.get_int(1) end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-ext/test/PaxHeaders/proc.c0000644000000000000000000000013215077107276024312 xustar0030 mtime=1761382078.124420547 30 atime=1761382080.144411306 30 ctime=1761382108.705301523 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-ext/test/proc.c0000644000175100017510000000311215077107276024677 0ustar00runnerrunner#include #include #include static mrb_value return_func_name(mrb_state *mrb, mrb_value self) { return mrb_cfunc_env_get(mrb, 0); } static mrb_value proc_new_cfunc_with_env(mrb_state *mrb, mrb_value self) { mrb_sym n; mrb_value n_val; mrb_method_t m; struct RProc *p; mrb_get_args(mrb, "n", &n); n_val = mrb_symbol_value(n); p = mrb_proc_new_cfunc_with_env(mrb, return_func_name, 1, &n_val); MRB_METHOD_FROM_PROC(m, p); mrb_define_method_raw(mrb, mrb_class_ptr(self), n, m); return self; } static mrb_value return_env(mrb_state *mrb, mrb_value self) { mrb_int idx; mrb_get_args(mrb, "i", &idx); return mrb_cfunc_env_get(mrb, idx); } static mrb_value cfunc_env_get(mrb_state *mrb, mrb_value self) { mrb_sym n; const mrb_value *argv; mrb_int argc; mrb_method_t m; struct RProc *p; mrb_get_args(mrb, "na", &n, &argv, &argc); p = mrb_proc_new_cfunc_with_env(mrb, return_env, argc, argv); MRB_METHOD_FROM_PROC(m, p); mrb_define_method_raw(mrb, mrb_class_ptr(self), n, m); return self; } static mrb_value cfunc_without_env(mrb_state *mrb, mrb_value self) { return mrb_cfunc_env_get(mrb, 0); } void mrb_mruby_proc_ext_gem_test(mrb_state *mrb) { struct RClass *cls; cls = mrb_define_class(mrb, "ProcExtTest", mrb->object_class); mrb_define_class_method(mrb, cls, "mrb_proc_new_cfunc_with_env", proc_new_cfunc_with_env, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, cls, "mrb_cfunc_env_get", cfunc_env_get, MRB_ARGS_REQ(2)); mrb_define_method(mrb, cls, "cfunc_without_env", cfunc_without_env, MRB_ARGS_NONE()); } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-ext/PaxHeaders/src0000644000000000000000000000013215077107334022731 xustar0030 mtime=1761382108.708301514 30 atime=1761382109.797298366 30 ctime=1761382108.708301514 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-ext/src/0000755000175100017510000000000015077107334023376 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-ext/src/PaxHeaders/proc.c0000644000000000000000000000013215077107276024122 xustar0030 mtime=1761382078.124420547 30 atime=1761382080.144411306 30 ctime=1761382108.708301514 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-ext/src/proc.c0000644000175100017510000001176415077107276024523 0ustar00runnerrunner#include #include #include #include #include #include #include static mrb_value proc_lambda_p(mrb_state *mrb, mrb_value self) { struct RProc *p = mrb_proc_ptr(self); return mrb_bool_value(MRB_PROC_STRICT_P(p)); } mrb_value mrb_proc_source_location(mrb_state *mrb, const struct RProc *p) { if (MRB_PROC_CFUNC_P(p)) { return mrb_nil_value(); } /* handle alias */ if (MRB_PROC_ALIAS_P(p)) { p = p->upper; } const mrb_irep *irep = p->body.irep; int32_t line; const char *filename; if (!mrb_debug_get_position(mrb, irep, 0, &line, &filename)) { return mrb_nil_value(); } return mrb_assoc_new(mrb, mrb_str_new_cstr(mrb, filename), mrb_fixnum_value(line)); } static mrb_value proc_source_location(mrb_state *mrb, mrb_value self) { return mrb_proc_source_location(mrb, mrb_proc_ptr(self)); } static mrb_value proc_inspect(mrb_state *mrb, mrb_value self) { struct RProc *p = mrb_proc_ptr(self); mrb_value str = mrb_str_new_lit(mrb, "#body.irep; const char *filename; int32_t line; mrb_str_cat_lit(mrb, str, " "); if (mrb_debug_get_position(mrb, irep, 0, &line, &filename)) { mrb_str_cat_cstr(mrb, str, filename); mrb_str_cat_lit(mrb, str, ":"); mrb_str_concat(mrb, str, mrb_fixnum_value(line)); } else { mrb_str_cat_lit(mrb, str, "-:-"); } } if (MRB_PROC_STRICT_P(p)) { mrb_str_cat_lit(mrb, str, " (lambda)"); } mrb_str_cat_lit(mrb, str, ">"); return str; } static mrb_value kernel_proc(mrb_state *mrb, mrb_value self) { mrb_value blk; mrb_get_args(mrb, "&!", &blk); return blk; } /* * call-seq: * prc.parameters -> array * * Returns the parameter information of this proc. * * prc = lambda{|x, y=42, *other|} * prc.parameters #=> [[:req, :x], [:opt, :y], [:rest, :other]] */ mrb_value mrb_proc_parameters(mrb_state *mrb, mrb_value self) { struct parameters_type { mrb_sym name; int size; } *p, parameters_list [] = { {MRB_SYM(req), 0}, {MRB_SYM(opt), 0}, {MRB_SYM(rest), 0}, {MRB_SYM(req), 0}, {MRB_SYM(keyrest),0}, {MRB_SYM(block), 0}, {MRB_SYM(key), 0}, {0, 0} }; int i; const struct RProc *proc = mrb_proc_ptr(self); if (MRB_PROC_CFUNC_P(proc)) { // TODO cfunc aspec is not implemented yet return mrb_ary_new(mrb); } const struct mrb_irep *irep = proc->body.irep; if (!irep || !irep->lv || *irep->iseq != OP_ENTER) { return mrb_ary_new(mrb); } if (!MRB_PROC_STRICT_P(proc)) { parameters_list[0].name = MRB_SYM(opt); parameters_list[3].name = MRB_SYM(opt); } mrb_aspec aspec = PEEK_W(irep->iseq+1); parameters_list[0].size = MRB_ASPEC_REQ(aspec); parameters_list[1].size = MRB_ASPEC_OPT(aspec); parameters_list[2].size = MRB_ASPEC_REST(aspec); parameters_list[3].size = MRB_ASPEC_POST(aspec); parameters_list[4].size = MRB_ASPEC_KDICT(aspec); parameters_list[5].size = MRB_ASPEC_BLOCK(aspec); parameters_list[6].size = MRB_ASPEC_KEY(aspec); int max = 0; for (i = 0; parameters_list[i].name; i++) { max += parameters_list[i].size; } mrb_value parameters = mrb_ary_new_capa(mrb, max); mrb_value krest = mrb_nil_value(); mrb_value block = mrb_nil_value(); for (i = 0, p = parameters_list; p->name; p++) { mrb_value sname = mrb_symbol_value(p->name); for (int j = 0; j < p->size; i++, j++) { mrb_value a = mrb_ary_new(mrb); mrb_ary_push(mrb, a, sname); if (i < max && irep->lv[i]) { mrb_ary_push(mrb, a, mrb_symbol_value(irep->lv[i])); } if (p->name == MRB_SYM(block)) { if (irep->lv[i+1]) { mrb_ary_push(mrb, a, mrb_symbol_value(irep->lv[i+1])); } block = a; continue; } if (p->name == MRB_SYM(keyrest)) { krest = a; continue; } mrb_ary_push(mrb, parameters, a); } /* need to skip empty block slot */ if (p->size == 0 && p->name == MRB_SYM(block)) i++; } if (!mrb_nil_p(krest)) mrb_ary_push(mrb, parameters, krest); if (!mrb_nil_p(block)) mrb_ary_push(mrb, parameters, block); return parameters; } void mrb_mruby_proc_ext_gem_init(mrb_state* mrb) { struct RClass *p = mrb->proc_class; mrb_define_method_id(mrb, p, MRB_SYM_Q(lambda), proc_lambda_p, MRB_ARGS_NONE()); mrb_define_method_id(mrb, p, MRB_SYM(source_location), proc_source_location, MRB_ARGS_NONE()); mrb_define_method_id(mrb, p, MRB_SYM(to_s), proc_inspect, MRB_ARGS_NONE()); mrb_define_method_id(mrb, p, MRB_SYM(inspect), proc_inspect, MRB_ARGS_NONE()); mrb_define_method_id(mrb, p, MRB_SYM(parameters), mrb_proc_parameters, MRB_ARGS_NONE()); mrb_define_private_method_id(mrb, mrb->kernel_module, MRB_SYM(proc), kernel_proc, MRB_ARGS_NONE()|MRB_ARGS_BLOCK()); } void mrb_mruby_proc_ext_gem_final(mrb_state* mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-class-ext0000644000000000000000000000013215077107334022304 xustar0030 mtime=1761382108.733301442 30 atime=1761382109.797298366 30 ctime=1761382108.733301442 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-class-ext/0000755000175100017510000000000015077107334022751 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-class-ext/PaxHeaders/mrbgem.rake0000644000000000000000000000013115077107276024502 xustar0029 mtime=1761382078.10842062 30 atime=1761382080.128411379 30 ctime=1761382108.734301439 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-class-ext/mrbgem.rake0000644000175100017510000000024415077107276025073 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-class-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'class/module extension' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-class-ext/PaxHeaders/mrblib0000644000000000000000000000013015077107334023551 xustar0029 mtime=1761382108.73730143 30 atime=1761382109.797298366 29 ctime=1761382108.73730143 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-class-ext/mrblib/0000755000175100017510000000000015077107334024220 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-class-ext/mrblib/PaxHeaders/module.rb0000644000000000000000000000013015077107276025445 xustar0029 mtime=1761382078.10842062 30 atime=1761382080.128411379 29 ctime=1761382108.73730143 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-class-ext/mrblib/module.rb0000644000175100017510000000455615077107276026051 0ustar00runnerrunnerclass Module ## # call-seq: # mod < other -> true, false, or nil # # Returns true if `mod` is a subclass of `other`. Returns # nil if there's no relationship between the two. # (Think of the relationship in terms of the class definition: # "class A < B" implies "A < B".) # def <(other) if self.equal?(other) false else self <= other end end ## # call-seq: # mod <= other -> true, false, or nil # # Returns true if `mod` is a subclass of `other` or # is the same as `other`. Returns # nil if there's no relationship between the two. # (Think of the relationship in terms of the class definition: # "class A < B" implies "A < B".) def <=(other) raise TypeError, 'compared with non class/module' unless other.is_a?(Module) if self.ancestors.include?(other) return true elsif other.ancestors.include?(self) return false end end ## # call-seq: # mod > other -> true, false, or nil # # Returns true if `mod` is an ancestor of `other`. Returns # nil if there's no relationship between the two. # (Think of the relationship in terms of the class definition: # "class A < B" implies "B > A".) # def >(other) if self.equal?(other) false else self >= other end end ## # call-seq: # mod >= other -> true, false, or nil # # Returns true if `mod` is an ancestor of `other`, or the # two modules are the same. Returns # nil if there's no relationship between the two. # (Think of the relationship in terms of the class definition: # "class A < B" implies "B > A".) # def >=(other) raise TypeError, 'compared with non class/module' unless other.is_a?(Module) return other < self end ## # call-seq: # module <=> other_module -> -1, 0, +1, or nil # # Comparison---Returns -1, 0, +1 or nil depending on whether `module` # includes `other_module`, they are the same, or if `module` is included by # `other_module`. # # Returns `nil` if `module` has no relationship with `other_module`, if # `other_module` is not a module, or if the two values are incomparable. # def <=>(other) return 0 if self.equal?(other) return nil unless other.is_a?(Module) cmp = self < other return -1 if cmp return 1 unless cmp.nil? return nil end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-class-ext/PaxHeaders/test0000644000000000000000000000013215077107334023263 xustar0030 mtime=1761382108.736301433 30 atime=1761382109.797298366 30 ctime=1761382108.736301433 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-class-ext/test/0000755000175100017510000000000015077107334023730 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-class-ext/test/PaxHeaders/class.rb0000644000000000000000000000013115077107276024776 xustar0029 mtime=1761382078.10842062 30 atime=1761382080.128411379 30 ctime=1761382108.735301436 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-class-ext/test/class.rb0000644000175100017510000000163615077107276025375 0ustar00runnerrunnerassert 'Class#subclasses' do a = Class.new b = Class.new(a) c = Class.new(b) d = Class.new(a) a_sub = a.subclasses assert_equal(2, a_sub.size) assert_true(a_sub.include?(b)) assert_true(a_sub.include?(d)) assert_equal([c], b.subclasses) assert_equal([], c.subclasses) end assert 'Class#attached_object' do foo = Class.new foo_s = class < #include #include #include #include #include #include #include static mrb_value mod_name(mrb_state *mrb, mrb_value self) { mrb_value name = mrb_class_path(mrb, mrb_class_ptr(self)); if (mrb_string_p(name)) { mrb_basic_ptr(name)->frozen = 1; } return name; } static mrb_value mod_singleton_class_p(mrb_state *mrb, mrb_value self) { return mrb_bool_value(mrb_sclass_p(self)); } /* * call-seq: * module_exec(arg...) {|var...| block } -> obj * class_exec(arg...) {|var...| block } -> obj * * Evaluates the given block in the context of the * class/module. The method defined in the block will belong * to the receiver. Any arguments passed to the method will be * passed to the block. This can be used if the block needs to * access instance variables. * * class Thing * end * Thing.class_exec{ * def hello() "Hello there!" end * } * puts Thing.new.hello() */ static mrb_value mod_module_exec(mrb_state *mrb, mrb_value self) { return mrb_object_exec(mrb, self, mrb_class_ptr(self)); } struct subclass_args { struct RClass *c; mrb_value ary; }; static int add_subclasses(mrb_state *mrb, struct RBasic *obj, void *data) { struct subclass_args *args = (struct subclass_args*)data; if (obj->tt == MRB_TT_CLASS) { struct RClass *c = (struct RClass*)obj; if (mrb_class_real(c->super) == args->c) { mrb_ary_push(mrb, args->ary, mrb_obj_value(obj)); } } return MRB_EACH_OBJ_OK; } /* * call-seq: * subclasses -> array * * Returns an array of classes where the receiver is the * direct superclass of the class, excluding singleton classes. * The order of the returned array is not defined. * * class A; end * class B < A; end * class C < B; end * class D < A; end * * A.subclasses #=> [D, B] * B.subclasses #=> [C] * C.subclasses #=> [] */ static mrb_value class_subclasses(mrb_state *mrb, mrb_value self) { struct RClass *c = mrb_class_ptr(self); mrb_value ary = mrb_ary_new(mrb); if (c->flags & MRB_FL_CLASS_IS_INHERITED) { struct subclass_args arg = {c, ary}; mrb_objspace_each_objects(mrb, add_subclasses, &arg); } return ary; } /* * call-seq: * attached_object -> object * * Returns the object for which the receiver is the singleton class. * Raises an TypeError if the class is not a singleton class. * * class Foo; end * * Foo.singleton_class.attached_object #=> Foo * Foo.attached_object #=> TypeError: not a singleton class * Foo.new.singleton_class.attached_object #=> # * TrueClass.attached_object #=> TypeError: not a singleton class * NilClass.attached_object #=> TypeError: not a singleton class */ static mrb_value class_attached_object(mrb_state *mrb, mrb_value self) { struct RClass *c = mrb_class_ptr(self); if (c->tt != MRB_TT_SCLASS) { mrb_raise(mrb, E_TYPE_ERROR, "not a singleton class"); } return mrb_obj_iv_get(mrb, (struct RObject*)c, MRB_SYM(__attached__)); } void mrb_mruby_class_ext_gem_init(mrb_state *mrb) { struct RClass *mod = mrb->module_class; mrb_define_method_id(mrb, mod, MRB_SYM(name), mod_name, MRB_ARGS_NONE()); mrb_define_method_id(mrb, mod, MRB_SYM_Q(singleton_class), mod_singleton_class_p, MRB_ARGS_NONE()); mrb_define_method_id(mrb, mod, MRB_SYM(module_exec), mod_module_exec, MRB_ARGS_ANY()|MRB_ARGS_BLOCK()); mrb_define_method_id(mrb, mod, MRB_SYM(class_exec), mod_module_exec, MRB_ARGS_ANY()|MRB_ARGS_BLOCK()); struct RClass *cls = mrb->class_class; mrb_define_method_id(mrb, cls, MRB_SYM(subclasses), class_subclasses, MRB_ARGS_NONE()); mrb_define_method_id(mrb, cls, MRB_SYM(attached_object), class_attached_object, MRB_ARGS_NONE()); } void mrb_mruby_class_ext_gem_final(mrb_state *mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-objectspace0000644000000000000000000000013215077107334022663 xustar0030 mtime=1761382108.820301191 30 atime=1761382109.797298366 30 ctime=1761382108.820301191 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-objectspace/0000755000175100017510000000000015077107334023330 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-objectspace/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276025062 xustar0030 mtime=1761382078.123420552 30 atime=1761382080.143411311 30 ctime=1761382108.820301191 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-objectspace/mrbgem.rake0000644000175100017510000000024115077107276025447 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-objectspace') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'ObjectSpace class' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-objectspace/PaxHeaders/test0000644000000000000000000000013215077107334023642 xustar0030 mtime=1761382108.821301188 30 atime=1761382109.797298366 30 ctime=1761382108.821301188 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-objectspace/test/0000755000175100017510000000000015077107334024307 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-objectspace/test/PaxHeaders/objectspace.rb0000644000000000000000000000013215077107276026533 xustar0030 mtime=1761382078.123420552 30 atime=1761382080.143411311 30 ctime=1761382108.821301188 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-objectspace/test/objectspace.rb0000644000175100017510000000305515077107276027126 0ustar00runnerrunnerassert('ObjectSpace.count_objects') do h = {} f = Fiber.new {} if Object.const_defined?(:Fiber) ObjectSpace.count_objects(h) assert_kind_of(Hash, h) assert_true(h.keys.all? {|x| x.is_a?(Symbol) || x.is_a?(Integer) }) assert_true(h.values.all? {|x| x.is_a?(Integer) }) assert_true(h.has_key?(:TOTAL)) assert_true(h.has_key?(:FREE)) assert_true(h.has_key?(:T_FIBER)) if Object.const_defined? :Fiber assert_equal(h[:TOTAL] * 2, h.values.reduce(:+)) h = ObjectSpace.count_objects assert_kind_of(Hash, h) assert_true(h.keys.all? {|x| x.is_a?(Symbol) || x.is_a?(Integer) }) assert_true(h.values.all? {|x| x.is_a?(Integer) }) assert_raise(TypeError) { ObjectSpace.count_objects(1) } h0 = {:T_FOO=>1000} h = ObjectSpace.count_objects(h0) assert_false(h0.has_key?(:T_FOO)) GC.start h_after = {} h_before = ObjectSpace.count_objects objs = [] 1000.times do objs << {} end ObjectSpace.count_objects(h) objs = nil GC.start ObjectSpace.count_objects(h_after) assert_equal(h[:T_HASH], h_before[:T_HASH] + 1000) assert_equal(h_after[:T_HASH], h_before[:T_HASH]) end assert('ObjectSpace.each_object') do objs = [] objs_count = ObjectSpace.each_object { |obj| objs << obj } assert_equal objs.length, objs_count arys = [] arys_count = ObjectSpace.each_object(Array) { |obj| arys << obj } assert_equal arys.length, arys_count assert_true arys.length < objs.length end assert 'Check class pointer of ObjectSpace.each_object.' do assert_nothing_raised { ObjectSpace.each_object { |obj| !obj } } end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-objectspace/PaxHeaders/src0000644000000000000000000000013215077107334023452 xustar0030 mtime=1761382108.823301182 30 atime=1761382109.797298366 30 ctime=1761382108.823301182 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-objectspace/src/0000755000175100017510000000000015077107334024117 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-objectspace/src/PaxHeaders/mruby_objectspace.c0000644000000000000000000000013215077107276027400 xustar0030 mtime=1761382078.123420552 30 atime=1761382080.143411311 30 ctime=1761382108.823301182 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-objectspace/src/mruby_objectspace.c0000644000175100017510000001100015077107276027760 0ustar00runnerrunner#include #include #include #include #include #include #include #include #include #include #include #include #include struct os_count_struct { mrb_int total; mrb_int freed; mrb_int counts[MRB_TT_MAXDEFINE+1]; }; static int os_count_object_type(mrb_state *mrb, struct RBasic *obj, void *data) { struct os_count_struct *obj_count; obj_count = (struct os_count_struct*)data; obj_count->total++; if (mrb_object_dead_p(mrb, obj)) { obj_count->freed++; } else { obj_count->counts[obj->tt]++; } return MRB_EACH_OBJ_OK; } /* * call-seq: * ObjectSpace.count_objects([result_hash]) -> hash * * Counts objects for each type. * * It returns a hash, such as: * { * :TOTAL=>10000, * :FREE=>3011, * :T_OBJECT=>6, * :T_CLASS=>404, * # ... * } * * If the optional argument +result_hash+ is given, * it is overwritten and returned. This is intended to avoid probe effect. * */ static mrb_value os_count_objects(mrb_state *mrb, mrb_value self) { struct os_count_struct obj_count = { 0 }; mrb_int i; mrb_value hash; if (mrb_get_args(mrb, "|H", &hash) == 0) { hash = mrb_hash_new(mrb); } if (!mrb_hash_empty_p(mrb, hash)) { mrb_hash_clear(mrb, hash); } mrb_objspace_each_objects(mrb, os_count_object_type, &obj_count); mrb_hash_set(mrb, hash, mrb_symbol_value(MRB_SYM(TOTAL)), mrb_fixnum_value(obj_count.total)); mrb_hash_set(mrb, hash, mrb_symbol_value(MRB_SYM(FREE)), mrb_fixnum_value(obj_count.freed)); for (i = MRB_TT_FALSE; i < MRB_TT_MAXDEFINE; i++) { mrb_value type; switch (i) { #define COUNT_TYPE(t) case (MRB_T ## t): type = mrb_symbol_value(MRB_SYM(t)); break; COUNT_TYPE(T_INTEGER); COUNT_TYPE(T_FLOAT); COUNT_TYPE(T_CPTR); COUNT_TYPE(T_OBJECT); COUNT_TYPE(T_CLASS); COUNT_TYPE(T_MODULE); COUNT_TYPE(T_ICLASS); COUNT_TYPE(T_SCLASS); COUNT_TYPE(T_PROC); COUNT_TYPE(T_ARRAY); COUNT_TYPE(T_HASH); COUNT_TYPE(T_STRING); COUNT_TYPE(T_RANGE); COUNT_TYPE(T_EXCEPTION); COUNT_TYPE(T_ENV); COUNT_TYPE(T_DATA); COUNT_TYPE(T_FIBER); COUNT_TYPE(T_STRUCT); COUNT_TYPE(T_ISTRUCT); COUNT_TYPE(T_BREAK); COUNT_TYPE(T_COMPLEX); COUNT_TYPE(T_RATIONAL); COUNT_TYPE(T_BIGINT); #undef COUNT_TYPE default: type = mrb_fixnum_value(i); break; } if (obj_count.counts[i]) mrb_hash_set(mrb, hash, type, mrb_fixnum_value(obj_count.counts[i])); } return hash; } struct os_each_object_data { struct RClass *target_module; mrb_int count; mrb_value block; }; static int os_each_object_cb(mrb_state *mrb, struct RBasic *obj, void *ud) { struct os_each_object_data *d = (struct os_each_object_data*)ud; /* filter dead objects */ if (mrb_object_dead_p(mrb, obj)) { return MRB_EACH_OBJ_OK; } /* filter internal objects */ switch (obj->tt) { case MRB_TT_FREE: case MRB_TT_ENV: case MRB_TT_BREAK: case MRB_TT_ICLASS: return MRB_EACH_OBJ_OK; default: break; } /* filter half baked (or internal) objects */ if (!obj->c) return MRB_EACH_OBJ_OK; /* filter class kind if target module defined */ if (d->target_module && !mrb_obj_is_kind_of(mrb, mrb_obj_value(obj), d->target_module)) { return MRB_EACH_OBJ_OK; } mrb_yield(mrb, d->block, mrb_obj_value(obj)); ++d->count; return MRB_EACH_OBJ_OK; } /* * call-seq: * ObjectSpace.each_object([module]) {|obj| ... } -> fixnum * * Calls the block once for each object in this Ruby process. * Returns the number of objects found. * If the optional argument +module+ is given, * calls the block for only those classes or modules * that match (or are a subclass of) +module+. * * If no block is given, ArgumentError is raised. * */ static mrb_value os_each_object(mrb_state *mrb, mrb_value self) { struct os_each_object_data d = {0}; mrb_get_args(mrb, "&!|c", &d.block, &d.target_module); mrb_objspace_each_objects(mrb, os_each_object_cb, &d); return mrb_fixnum_value(d.count); } void mrb_mruby_objectspace_gem_init(mrb_state *mrb) { struct RClass *os = mrb_define_module(mrb, "ObjectSpace"); mrb_define_class_method(mrb, os, "count_objects", os_count_objects, MRB_ARGS_OPT(1)); mrb_define_class_method(mrb, os, "each_object", os_each_object, MRB_ARGS_OPT(1)); } void mrb_mruby_objectspace_gem_final(mrb_state *mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-sleep0000644000000000000000000000013215077107334021511 xustar0030 mtime=1761382108.753301384 30 atime=1761382109.797298366 30 ctime=1761382108.753301384 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sleep/0000755000175100017510000000000015077107334022156 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sleep/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276023710 xustar0030 mtime=1761382078.126420538 30 atime=1761382080.145411302 30 ctime=1761382108.752301387 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sleep/mrbgem.rake0000644000175100017510000000027715077107276024306 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-sleep') do |spec| spec.license = 'MIT' spec.authors = ['MATSUMOTO Ryosuke', 'mruby developers'] spec.summary = 'Kernel#sleep and Kernel#usleep' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sleep/PaxHeaders/example0000644000000000000000000000013215077107334023144 xustar0030 mtime=1761382108.750301393 30 atime=1761382109.797298366 30 ctime=1761382108.750301393 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sleep/example/0000755000175100017510000000000015077107334023611 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sleep/example/PaxHeaders/sleep.rb0000644000000000000000000000013215077107276024663 xustar0030 mtime=1761382078.126420538 30 atime=1761382080.145411302 30 ctime=1761382108.750301393 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sleep/example/sleep.rb0000644000175100017510000000003015077107276025244 0ustar00runnerrunnersleep(10) usleep(10000) nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sleep/PaxHeaders/README.md0000644000000000000000000000013215077107276023052 xustar0030 mtime=1761382078.126420538 30 atime=1761382080.145411302 30 ctime=1761382108.753301384 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sleep/README.md0000644000175100017510000000053315077107276023443 0ustar00runnerrunner# Sleep Module for mruby mruby sleep module ## Install by mrbgems - add `conf.gem` line to your build configuration. ```ruby MRuby::Build.new do |conf| # ... (snip) ... conf.gem :core => 'mruby-sleep' end ``` ## Example ```ruby sleep(10) usleep(10000) ``` # License under the MIT License: - nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sleep/PaxHeaders/test0000644000000000000000000000013215077107334022470 xustar0030 mtime=1761382108.754301381 30 atime=1761382109.797298366 30 ctime=1761382108.754301381 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sleep/test/0000755000175100017510000000000015077107334023135 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sleep/test/PaxHeaders/sleep_test.rb0000644000000000000000000000013215077107276025246 xustar0030 mtime=1761382078.126420538 30 atime=1761382080.145411302 30 ctime=1761382108.754301381 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sleep/test/sleep_test.rb0000644000175100017510000000144115077107276025636 0ustar00runnerrunnerassert("sleep works") do assert_nothing_raised { sleep(1) } assert_nothing_raised { sleep(0) } end assert("sleep would accept non-negative float value") do skip unless Object.const_defined?(:Float) assert_nothing_raised { sleep(0.01) } assert_nothing_raised { sleep(0.0) } assert_nothing_raised { sleep(-0.0) } end assert("sleep would not accept negative integer value") do assert_raise(ArgumentError) { sleep(-1) } end assert("sleep would not accept negative float value") do skip unless Object.const_defined?(:Float) assert_raise(ArgumentError) { sleep(-0.1) } end assert("usleep works") do assert_nothing_raised { usleep(100) } assert_nothing_raised { usleep(0) } end assert("usleep would not accept negative value") do assert_raise(ArgumentError) { usleep(-100) } end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sleep/PaxHeaders/src0000644000000000000000000000013215077107334022300 xustar0030 mtime=1761382108.756301375 30 atime=1761382109.797298366 30 ctime=1761382108.756301375 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sleep/src/0000755000175100017510000000000015077107334022745 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sleep/src/PaxHeaders/sleep.c0000644000000000000000000000013215077107276023636 xustar0030 mtime=1761382078.126420538 30 atime=1761382080.145411302 30 ctime=1761382108.756301375 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-sleep/src/sleep.c0000644000175100017510000000723215077107276024232 0ustar00runnerrunner/* ** mrb_sleep - sleep methods for mruby ** ** Copyright (c) mod_mruby developers 2012- ** Copyright (c) mruby developers 2018 ** ** Permission is hereby granted, free of charge, to any person obtaining ** a copy of this software and associated documentation files (the ** "Software"), to deal in the Software without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Software, and to ** permit persons to whom the Software is furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be ** included in all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ** ** [ MIT license: https://www.opensource.org/licenses/mit-license.php ] */ #include #ifdef _WIN32 #include #define sleep(x) Sleep(x * 1000) #define usleep(x) Sleep((DWORD)(((x)<1000) ? 1 : ((x)/1000))) #else #include #include #endif #include #include /* not implemented forever sleep (called without an argument)*/ static mrb_value f_sleep(mrb_state *mrb, mrb_value self) { time_t beg = time(0); time_t end; #ifndef MRB_NO_FLOAT mrb_float sec; mrb_get_args(mrb, "f", &sec); if (sec >= 0) { usleep(sec * 1000000); } else { mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must not be negative"); } #else mrb_int sec; mrb_get_args(mrb, "i", &sec); if (sec >= 0) { sleep(sec); } else { mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must not be negative"); } #endif end = time(0) - beg; return mrb_fixnum_value((mrb_int)end); } /* mruby special; needed for mruby without float numbers */ static mrb_value f_usleep(mrb_state *mrb, mrb_value self) { mrb_int usec; #ifdef _WIN32 FILETIME st_ft,ed_ft; unsigned __int64 st_time = 0; unsigned __int64 ed_time = 0; #else struct timeval st_tm,ed_tm; #endif time_t slp_tm; #ifdef _WIN32 GetSystemTimeAsFileTime(&st_ft); #else gettimeofday(&st_tm, NULL); #endif /* not implemented forever sleep (called without an argument)*/ mrb_get_args(mrb, "i", &usec); if (usec >= 0) { usleep(usec); } else { mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must not be negative"); } #ifdef _WIN32 GetSystemTimeAsFileTime(&ed_ft); st_time |= st_ft.dwHighDateTime; st_time <<=32; st_time |= st_ft.dwLowDateTime; ed_time |= ed_ft.dwHighDateTime; ed_time <<=32; ed_time |= ed_ft.dwLowDateTime; slp_tm = (ed_time - st_time) / 10; #else gettimeofday(&ed_tm, NULL); if (st_tm.tv_usec > ed_tm.tv_usec) { slp_tm = 1000000 + ed_tm.tv_usec - st_tm.tv_usec; } else { slp_tm = ed_tm.tv_usec - st_tm.tv_usec; } #endif return mrb_fixnum_value((mrb_int)slp_tm); } void mrb_mruby_sleep_gem_init(mrb_state *mrb) { mrb_define_private_method_id(mrb, mrb->kernel_module, MRB_SYM(sleep), f_sleep, MRB_ARGS_REQ(1)); mrb_define_private_method_id(mrb, mrb->kernel_module, MRB_SYM(usleep), f_usleep, MRB_ARGS_REQ(1)); } void mrb_mruby_sleep_gem_final(mrb_state *mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-rational0000644000000000000000000000013215077107334022212 xustar0030 mtime=1761382108.825301176 30 atime=1761382109.797298366 30 ctime=1761382108.825301176 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-rational/0000755000175100017510000000000015077107334022657 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-rational/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024411 xustar0030 mtime=1761382078.125420543 30 atime=1761382080.144411306 30 ctime=1761382108.825301176 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-rational/mrbgem.rake0000644000175100017510000000041415077107276025000 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-rational') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Rational class' spec.build.defines << "MRB_USE_RATIONAL" spec.add_test_dependency('mruby-complex', :core => 'mruby-complex') end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-rational/PaxHeaders/mrblib0000644000000000000000000000013215077107334023461 xustar0030 mtime=1761382108.828301167 30 atime=1761382109.797298366 30 ctime=1761382108.828301167 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-rational/mrblib/0000755000175100017510000000000015077107334024126 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-rational/mrblib/PaxHeaders/rational.rb0000644000000000000000000000013215077107276025701 xustar0030 mtime=1761382078.125420543 30 atime=1761382080.144411306 30 ctime=1761382108.828301167 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-rational/mrblib/rational.rb0000644000175100017510000000044315077107276026272 0ustar00runnerrunnerclass Rational < Numeric def inspect "(#{to_s})" end def to_s "#{numerator}/#{denominator}" end def <=>(other) return nil unless other.kind_of?(Numeric) self.to_f <=> other.to_f rescue nil end end class Numeric def to_r Rational(self, 1) end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-rational/PaxHeaders/test0000644000000000000000000000013015077107334023167 xustar0029 mtime=1761382108.82730117 30 atime=1761382109.797298366 29 ctime=1761382108.82730117 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-rational/test/0000755000175100017510000000000015077107334023636 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-rational/test/PaxHeaders/rational.rb0000644000000000000000000000013115077107276025410 xustar0030 mtime=1761382078.126420538 30 atime=1761382080.145411302 29 ctime=1761382108.82730117 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-rational/test/rational.rb0000644000175100017510000002441015077107276026002 0ustar00runnerrunnerdef assert_rational(exp, real) assert "assert_rational" do assert_kind_of Rational, real assert_float exp.numerator, real.numerator assert_float exp.denominator, real.denominator end end def assert_equal_rational(exp, o1, o2) assert "assert_equal_rational" do if exp assert_operator(o1, :==, o2) assert_not_operator(o1, :!=, o2) else assert_not_operator(o1, :==, o2) assert_operator(o1, :!=, o2) end end end def assert_cmp(exp, o1, o2) if exp == (o1 <=> o2) pass else flunk "", " Expected #{o1.inspect} <=> #{o2.inspect} to be #{exp}." end end def assert_complex(real, imag) if Object.const_defined?(:Complex) assert "assert_complex" do c = yield assert_float(real, c.real) assert_float(imag, c.imaginary) end end end assert 'Rational' do r = 5r assert_equal(Rational, r.class) assert_equal([5, 1], [r.numerator, r.denominator]) end assert 'Kernel#Rational' do r = Rational(4,10) assert_equal(2, r.numerator) assert_equal(5, r.denominator) r = Rational(3) assert_equal(3, r.numerator) assert_equal(1, r.denominator) assert_raise(ArgumentError) { Rational() } assert_raise(ArgumentError) { Rational(1,2,3) } end assert 'Rational#to_f' do assert_float(2.0, Rational(2).to_f) assert_float(2.25, Rational(9, 4).to_f) assert_float(-0.75, Rational(-3, 4).to_f) assert_float(6.666666666666667, Rational(20, 3).to_f) end assert 'Rational#to_i' do assert_equal(0, Rational(2, 3).to_i) assert_equal(3, Rational(3).to_i) assert_equal(300, Rational(300.6).to_i) assert_equal(1, Rational(98, 71).to_i) assert_equal(-15, Rational(-30, 2).to_i) end assert 'Rational#*' do assert_rational(Rational(4, 9), Rational(2, 3) * Rational(2, 3)) assert_rational(Rational(900, 1), Rational(900) * Rational(1)) assert_rational(Rational(1, 1), Rational(-2, 9) * Rational(-9, 2)) assert_rational(Rational(9, 2), Rational(9, 8) * 4) assert_float( 21.77777777777778, Rational(20, 9) * 9.8) assert_float( 21.77777777777778, 9.8 * Rational(20, 9)) assert_complex(5.2, 2.6) {Rational(13,5)*(2.0+1i)} assert_complex(5.2, 2.6) {(2.0+1i)*Rational(13,5)} end assert 'Rational#+' do assert_rational(Rational(4, 3), Rational(2, 3) + Rational(2, 3)) assert_rational(Rational(901, 1), Rational(900) + Rational(1)) assert_rational(Rational(-85, 18), Rational(-2, 9) + Rational(-9, 2)) assert_rational(Rational(41, 8), Rational(9, 8) + 4) assert_rational(Rational(41, 8), 4 + Rational(9, 8)) assert_float( 12.022222222222222, Rational(20, 9) + 9.8) assert_float( 12.022222222222222, 9.8 + Rational(20, 9)) assert_complex(24.0, 0) {Rational(24,2)+(12.0+0i)} assert_complex(24.0, 0) {(12.0+0i)+Rational(24,2)} end assert 'Rational#-' do assert_rational(Rational(0, 1), Rational(2, 3) - Rational(2, 3)) assert_rational(Rational(899, 1), Rational(900) - Rational(1)) assert_rational(Rational(77, 18), Rational(-2, 9) - Rational(-9, 2)) assert_rational(Rational(23, 8), 4 - Rational(9, 8)) assert_float( -7.577777777777778, Rational(20, 9) - 9.8) assert_float( 7.577777777777778, 9.8 - Rational(20, 9)) assert_complex(2.0, 0) {Rational(24,2)-(10.0+0i)} assert_complex(2.0, 0) {(14.0+0i)-Rational(24,2)} end assert 'Rational#/' do assert_rational(Rational(1, 1), Rational(2, 3) / Rational(2, 3)) assert_rational(Rational(900, 1), Rational(900) / Rational(1)) assert_rational(Rational(4, 81), Rational(-2, 9) / Rational(-9, 2)) assert_rational(Rational(9, 32), Rational(9, 8) / 4) assert_rational(Rational(32, 9), 4 / Rational(9, 8)) assert_float( 0.22675736961451246, Rational(20, 9) / 9.8) assert_float( 4.41, 9.8 / Rational(20, 9)) assert_complex(1.92, 1.44) {Rational(24,2)/(4.0-3i)} assert_complex(0.25, 0.25) {(3.0+3i)/Rational(24,2)} end assert 'Rational#==, Rational#!=' do assert_equal_rational(true, Rational(1,1), Rational(1)) assert_equal_rational(true, Rational(-1,1), -1r) assert_equal_rational(true, Rational(13,4), 3.25) assert_equal_rational(true, Rational(13,3.25), Rational(4,1)) assert_equal_rational(true, Rational(-3,-4), Rational(3,4)) assert_equal_rational(true, Rational(-4,5), Rational(4,-5)) assert_equal_rational(true, Rational(4,2), 2) assert_equal_rational(true, Rational(-4,2), -2) assert_equal_rational(true, Rational(4,-2), -2) assert_equal_rational(true, Rational(4,2), 2.0) assert_equal_rational(true, Rational(-4,2), -2.0) assert_equal_rational(true, Rational(4,-2), -2.0) assert_equal_rational(true, Rational(8,6), Rational(4,3)) assert_equal_rational(false, Rational(13,4), 3) assert_equal_rational(false, Rational(13,4), 3.3) assert_equal_rational(false, Rational(2,1), 1r) assert_equal_rational(false, Rational(1), nil) assert_equal_rational(false, Rational(1), '') end assert 'Integer#==(Rational), Integer#!=(Rational)' do assert_equal_rational(true, 2, Rational(4,2)) assert_equal_rational(true, -2, Rational(-4,2)) assert_equal_rational(true, -2, Rational(4,-2)) assert_equal_rational(false, 3, Rational(13,4)) end assert 'Float#==(Rational), Float#!=(Rational)' do assert_equal_rational(true, 2.0, Rational(4,2)) assert_equal_rational(true, -2.0, Rational(-4,2)) assert_equal_rational(true, -2.0, Rational(4,-2)) assert_equal_rational(false, 3.3, Rational(13,4)) end assert 'Rational#<=>' do assert_cmp(-1, Rational(-1), Rational(0)) assert_cmp(0, Rational(0), Rational(0)) assert_cmp(1, Rational(1), Rational(0)) assert_cmp(-1, Rational(-1), 0) assert_cmp(0, Rational(0), 0) assert_cmp(1, Rational(1), 0) assert_cmp(-1, Rational(-1), 0.0) assert_cmp(0, Rational(0), 0.0) assert_cmp(1, Rational(1), 0.0) assert_cmp(-1, Rational(1,2), Rational(2,3)) assert_cmp(0, Rational(2,3), Rational(2,3)) assert_cmp(1, Rational(2,3), Rational(1,2)) assert_cmp(1, Rational(2,3), Rational(1,2)) assert_cmp(1, Rational(0), Rational(-1)) assert_cmp(-1, Rational(0), Rational(1)) assert_cmp(1, Rational(2,3), Rational(1,2)) assert_cmp(0, Rational(2,3), Rational(2,3)) assert_cmp(-1, Rational(1,2), Rational(2,3)) assert_cmp(-1, Rational(1,2), Rational(2,3)) assert_cmp(nil, 3r, "3") end assert 'Integer#<=>(Rational)' do assert_cmp(-1, -2, Rational(-9,5)) assert_cmp(0, 5, 5r) assert_cmp(1, 3, Rational(8,3)) end assert 'Float#<=>(Rational)' do assert_cmp(-1, -2.1, Rational(-9,5)) assert_cmp(0, 5.0, 5r) assert_cmp(1, 2.7, Rational(8,3)) end assert 'Rational#<' do assert_operator(Rational(1,2), :<, Rational(2,3)) assert_not_operator(Rational(2,3), :<, Rational(2,3)) assert_operator(Rational(2,3), :<, 1) assert_not_operator(2r, :<, 2) assert_not_operator(Rational(2,3), :<, -3) assert_operator(Rational(-4,3), :<, -0.3) assert_not_operator(Rational(13,4), :<, 3.25) assert_not_operator(Rational(2,3), :<, 0.6) assert_raise(ArgumentError) { 1r < "2" } end assert 'Integer#<(Rational)' do assert_not_operator(1, :<, Rational(2,3)) assert_not_operator(2, :<, 2r) assert_operator(-3, :<, Rational(2,3)) end assert 'Float#<(Rational)' do assert_not_operator(-0.3, :<, Rational(-4,3)) assert_not_operator(3.25, :<, Rational(13,4)) assert_operator(0.6, :<, Rational(2,3)) end assert 'Rational#<=' do assert_operator(Rational(1,2), :<=, Rational(2,3)) assert_operator(Rational(2,3), :<=, Rational(2,3)) assert_operator(Rational(2,3), :<=, 1) assert_operator(2r, :<=, 2) assert_not_operator(Rational(2,3), :<=, -3) assert_operator(Rational(-4,3), :<=, -0.3) assert_operator(Rational(13,4), :<=, 3.25) assert_not_operator(Rational(2,3), :<=, 0.6) assert_raise(ArgumentError) { 1r <= "2" } end assert 'Integer#<=(Rational)' do assert_not_operator(1, :<=, Rational(2,3)) assert_operator(2, :<=, 2r) assert_operator(-3, :<=, Rational(2,3)) end assert 'Float#<=(Rational)' do assert_not_operator(-0.3, :<=, Rational(-4,3)) assert_operator(3.25, :<=, Rational(13,4)) assert_operator(0.6, :<=, Rational(2,3)) end assert 'Rational#>' do assert_not_operator(Rational(1,2), :>, Rational(2,3)) assert_not_operator(Rational(2,3), :>, Rational(2,3)) assert_not_operator(Rational(2,3), :>, 1) assert_not_operator(2r, :>, 2) assert_operator(Rational(2,3), :>, -3) assert_not_operator(Rational(-4,3), :>, -0.3) assert_not_operator(Rational(13,4), :>, 3.25) assert_operator(Rational(2,3), :>, 0.6) assert_raise(ArgumentError) { 1r > "2" } end assert 'Integer#>(Rational)' do assert_operator(1, :>, Rational(2,3)) assert_not_operator(2, :>, 2r) assert_not_operator(-3, :>, Rational(2,3)) end assert 'Float#>(Rational)' do assert_operator(-0.3, :>, Rational(-4,3)) assert_not_operator(3.25, :>, Rational(13,4)) assert_not_operator(0.6, :>, Rational(2,3)) end assert 'Rational#>=' do assert_not_operator(Rational(1,2), :>=, Rational(2,3)) assert_operator(Rational(2,3), :>=, Rational(2,3)) assert_not_operator(Rational(2,3), :>=, 1) assert_operator(2r, :>=, 2) assert_operator(Rational(2,3), :>=, -3) assert_not_operator(Rational(-4,3), :>=, -0.3) assert_operator(Rational(13,4), :>=, 3.25) assert_operator(Rational(2,3), :>=, 0.6) assert_raise(ArgumentError) { 1r >= "2" } end assert 'Integer#>=(Rational)' do assert_operator(1, :>=, Rational(2,3)) assert_operator(2, :>=, 2r) assert_not_operator(-3, :>=, Rational(2,3)) end assert 'Float#>=(Rational)' do assert_operator(-0.3, :>=, Rational(-4,3)) assert_operator(3.25, :>=, Rational(13,4)) assert_not_operator(0.6, :>=, Rational(2,3)) end assert 'Rational#negative?' do assert_predicate(Rational(-2,3), :negative?) assert_predicate(Rational(2,-3), :negative?) assert_not_predicate(Rational(2,3), :negative?) assert_not_predicate(Rational(0), :negative?) end assert 'Rational#frozen?' do assert_predicate(1r, :frozen?) assert_predicate(Rational(2,3), :frozen?) assert_predicate(4/5r, :frozen?) end assert 'Rational#**' do assert_rational(1r, (14/2r)**0) assert_rational(14/2r, (14/2r)**1) assert_rational(49r, (14/2r)**2) assert_rational(27r, (6/2r)**3) assert_float(2.0, (4r)**(1/2r)) assert_rational(4r, (4r)**(2/2r)) assert_rational(16r, (4r)**(4/2r)) assert_float(1.0, (4r)**(0.0)) assert_float(2.0, (4r)**(0.5)) assert_float(4.0, (4r)**(1.0)) assert_float(16.0, (4r)**(2.0)) assert_float(3.5**1.5, (7/2r)**(1.5)) end assert 'Integer#quo' do a = 6.quo(5) assert_equal 6/5r, a end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-rational/PaxHeaders/src0000644000000000000000000000013215077107334023001 xustar0030 mtime=1761382108.829301164 30 atime=1761382109.797298366 30 ctime=1761382108.829301164 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-rational/src/0000755000175100017510000000000015077107334023446 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-rational/src/PaxHeaders/rational.c0000644000000000000000000000013215077107276025040 xustar0030 mtime=1761382078.125420543 30 atime=1761382080.144411306 30 ctime=1761382108.829301164 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-rational/src/rational.c0000644000175100017510000006441415077107276025441 0ustar00runnerrunner#include #include #include #include #include #ifndef MRB_NO_FLOAT #include mrb_value mrb_complex_new(mrb_state *, mrb_float, mrb_float); #endif mrb_bool mrb_complex_eq(mrb_state *mrb, mrb_value, mrb_value); mrb_value mrb_complex_add(mrb_state *mrb, mrb_value, mrb_value); mrb_value mrb_complex_sub(mrb_state *mrb, mrb_value, mrb_value); mrb_value mrb_complex_mul(mrb_state *mrb, mrb_value, mrb_value); mrb_value mrb_complex_div(mrb_state *mrb, mrb_value, mrb_value); mrb_value mrb_bint_mul_n(mrb_state *mrb, mrb_value x, mrb_value y); void mrb_bint_reduce(mrb_state *mrb, mrb_value *x, mrb_value *y); #ifdef MRB_USE_BIGINT struct mrb_rational { union { struct { mrb_int num; mrb_int den; } i; struct { struct RBasic *num; struct RBasic *den; } b; }; }; #define numerator i.num #define denominator i.den #define RAT_BIGINT 1 #define RAT_BIGINT_P(obj) (mrb_obj_ptr(obj)->flags & RAT_BIGINT) #else struct mrb_rational { mrb_int numerator; mrb_int denominator; }; #endif #define ONE mrb_fixnum_value(1) #define ZERO mrb_fixnum_value(0) #if defined(MRB_INT64) && defined(MRB_32BIT) struct RRational { MRB_OBJECT_HEADER; struct mrb_rational *p; }; static struct mrb_rational* rat_ptr(mrb_state *mrb, mrb_value v) { struct RRational *r = (struct RRational*)mrb_obj_ptr(v); if (!r->p) { mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized rational"); } return r->p; } #else #define RATIONAL_INLINE struct RRational { MRB_OBJECT_HEADER; struct mrb_rational r; }; #define rat_ptr(mrb, v) (&((struct RRational*)mrb_obj_ptr(v))->r) #endif mrb_static_assert_object_size(struct RRational); static struct mrb_rational* rat_alloc(mrb_state *mrb, struct RClass *c, struct RBasic **obj) { struct RRational *s = MRB_OBJ_ALLOC(mrb, MRB_TT_RATIONAL, c); struct mrb_rational *p; #ifdef RATIONAL_INLINE p = &s->r; #else p = s->p = (struct mrb_rational*)mrb_malloc(mrb, sizeof(struct mrb_rational)); #endif *obj = (struct RBasic*)s; return p; } #ifdef RAT_BIGINT int mrb_rational_mark(mrb_state *mrb, struct RBasic *rat) { if (!(rat->flags & RAT_BIGINT)) return 0; mrb_value self = mrb_obj_value(rat); struct mrb_rational *p = rat_ptr(mrb, self); mrb_gc_mark(mrb, p->b.num); mrb_gc_mark(mrb, p->b.den); return 2; } #endif static mrb_value rat_numerator(mrb_state *mrb, mrb_value self) { struct mrb_rational *p = rat_ptr(mrb, self); #ifdef RAT_BIGINT if (RAT_BIGINT_P(self)) { return mrb_obj_value(p->b.num); } #endif return mrb_int_value(mrb, p->numerator); } /* normalized version of rat_numerator() */ static mrb_value rational_numerator(mrb_state *mrb, mrb_value self) { mrb_value n = rat_numerator(mrb, self); if (mrb_bigint_p(n)) { /* normalize bigint */ return mrb_bint_mul(mrb, n, ONE); } return n; } static mrb_value rat_denominator(mrb_state *mrb, mrb_value self) { struct mrb_rational *p = rat_ptr(mrb, self); #ifdef RAT_BIGINT if (RAT_BIGINT_P(self)) { return mrb_obj_value(p->b.den); } #endif return mrb_int_value(mrb, p->denominator); } /* normalized version of rat_denominator() */ static mrb_value rational_denominator(mrb_state *mrb, mrb_value self) { mrb_value n = rat_denominator(mrb, self); if (mrb_bigint_p(n)) { /* normalize bigint */ return mrb_bint_mul(mrb, n, ONE); } return n; } static mrb_noreturn void rat_overflow(mrb_state *mrb) { mrb_raise(mrb, E_RANGE_ERROR, "integer overflow in rational"); } static mrb_noreturn void rat_zerodiv(mrb_state *mrb) { mrb_raise(mrb, E_ZERODIV_ERROR, "divided by 0 in rational"); } static mrb_noreturn void rat_type_error(mrb_state *mrb, mrb_value x) { mrb_raisef(mrb, E_TYPE_ERROR, "%T cannot be converted to Rational", x); } void mrb_rational_copy(mrb_state *mrb, mrb_value x, mrb_value y) { struct mrb_rational *p1 = rat_ptr(mrb, x); struct mrb_rational *p2 = rat_ptr(mrb, y); #ifdef RAT_BIGINT struct RRational *r = (struct RRational*)mrb_obj_ptr(x); if (RAT_BIGINT_P(y)) { p1->b.num = p2->b.num; p1->b.den = p2->b.den; r->flags |= RAT_BIGINT; return; } r->flags &= ~RAT_BIGINT; #endif p1->numerator = p2->numerator; p1->denominator = p2->denominator; } inline static mrb_int i_gcd(mrb_int x, mrb_int y) { mrb_uint u, v, t; int shift; if (x < 0) x = -x; if (y < 0) y = -y; if (x == 0) return y; if (y == 0) return x; u = (mrb_uint)x; v = (mrb_uint)y; for (shift = 0; ((u | v) & 1) == 0; shift++) { u >>= 1; v >>= 1; } while ((u & 1) == 0) u >>= 1; do { while ((v & 1) == 0) v >>= 1; if (u > v) { t = v; v = u; u = t; } v = v - u; } while (v != 0); return (mrb_int)(u << shift); } #ifdef RAT_BIGINT static mrb_value rational_new_b(mrb_state *mrb, mrb_value n, mrb_value d) { /* bigint check */ mrb_assert(mrb_bigint_p(n)); d = mrb_as_bint(mrb, d); mrb_int cmp = mrb_bint_cmp(mrb, d, ZERO); if (cmp == 0) { rat_zerodiv(mrb); } /* negative */ if (cmp < 0) { n = mrb_bint_neg(mrb, n); d = mrb_bint_neg(mrb, d); } /* normalize (n/gcd, d/gcd) */ mrb_bint_reduce(mrb, &n, &d); struct RClass *c = mrb_class_get_id(mrb, MRB_SYM(Rational)); struct RBasic *rat; struct mrb_rational *p = rat_alloc(mrb, c, &rat); rat->flags |= RAT_BIGINT; p->b.num = (struct RBasic*)mrb_obj_ptr(n); p->b.den = (struct RBasic*)mrb_obj_ptr(d); rat->frozen = 1; return mrb_obj_value(rat); } #endif mrb_value mrb_rational_new(mrb_state *mrb, mrb_int nume, mrb_int deno) { if (deno == 0) { rat_zerodiv(mrb); } if (nume == MRB_INT_MIN || deno == MRB_INT_MIN) { #ifdef RAT_BIGINT mrb_value num = mrb_as_bint(mrb, mrb_int_value(mrb, nume)); mrb_value den = mrb_as_bint(mrb, mrb_int_value(mrb, deno)); return rational_new_b(mrb, num, den); #else rat_overflow(mrb); #endif } if (deno < 0) { nume *= -1; deno *= -1; } mrb_int a = i_gcd(nume, deno); nume /= a; deno /= a; struct RClass *c = mrb_class_get_id(mrb, MRB_SYM(Rational)); struct RBasic *rat; struct mrb_rational *p = rat_alloc(mrb, c, &rat); p->numerator = nume; p->denominator = deno; rat->frozen = 1; return mrb_obj_value(rat); } #define rational_new_i(mrb,n,d) mrb_rational_new(mrb, n, d) #ifndef MRB_NO_FLOAT #if defined(MRB_INT32) || defined(MRB_USE_FLOAT32) #define frexp_rat(x,exp) frexpf((float)x, exp) #define ldexp_rat(x,exp) ldexpf((float)x, exp) #define RAT_MANT_DIG FLT_MANT_DIG #define RAT_INT_LIMIT 30 #define RAT_HUGE_VAL HUGE_VALF #else #define frexp_rat frexp #define ldexp_rat ldexp #define RAT_MANT_DIG DBL_MANT_DIG #define RAT_INT_LIMIT 62 #define RAT_HUGE_VAL HUGE_VAL #endif #define mrb_int_fit_p(x,t) ((t)MRB_INT_MIN <= (x) && (x) <= (t)MRB_INT_MAX) static mrb_value int_lshift(mrb_state *mrb, mrb_value v, mrb_int n) { if (mrb_integer_p(v)) { mrb_float f = (mrb_float)mrb_integer(v); f *= 1< 0) { mrb_int temp; if (mrb_int_mul_overflow(nume, ((mrb_int)1)<>= exp; } return rational_new_i(mrb, nume, deno); } static mrb_float rat_float(mrb_state *mrb, mrb_value x) { struct mrb_rational *p = rat_ptr(mrb, x); #ifdef RAT_BIGINT if (RAT_BIGINT_P(x)) { return mrb_bint_as_float(mrb, mrb_obj_value(p->b.num)) / mrb_bint_as_float(mrb, mrb_obj_value(p->b.den)); } #endif return (mrb_float)p->numerator / (mrb_float)p->denominator; } mrb_value mrb_rational_to_f(mrb_state *mrb, mrb_value self) { mrb_float f = rat_float(mrb, self); return mrb_float_value(mrb, f); } #endif mrb_value mrb_rational_to_i(mrb_state *mrb, mrb_value self) { struct mrb_rational *p = rat_ptr(mrb, self); #ifdef RAT_BIGINT if (RAT_BIGINT_P(self)) { return mrb_bint_div(mrb, mrb_obj_value(p->b.num), mrb_obj_value(p->b.den)); } #endif return mrb_int_value(mrb, p->numerator / p->denominator); } mrb_value mrb_as_rational(mrb_state *mrb, mrb_value x) { switch(mrb_type(x)) { case MRB_TT_INTEGER: return rational_new_i(mrb, mrb_integer(x), 1); #ifdef RAT_BIGINT case MRB_TT_BIGINT: return rational_new_b(mrb, x, ONE); #endif case MRB_TT_RATIONAL: return x; #ifndef MRB_NO_FLOAT #ifdef MRB_USE_COMPLEX case MRB_TT_COMPLEX: #endif case MRB_TT_FLOAT: return rational_new_f(mrb, mrb_as_float(mrb, x)); #endif default: rat_type_error(mrb, x); } } static mrb_value rational_negative_p(mrb_state *mrb, mrb_value self) { struct mrb_rational *p = rat_ptr(mrb, self); #ifdef RAT_BIGINT if (RAT_BIGINT_P(self)) { mrb_int cmp = mrb_bint_cmp(mrb, mrb_obj_value(p->b.num), ZERO); return mrb_bool_value(cmp < 0); } #endif return mrb_bool_value(p->numerator < 0); } #ifndef MRB_NO_FLOAT static mrb_value float_to_r(mrb_state *mrb, mrb_value self) { return rational_new_f(mrb, mrb_float(self)); } #endif static mrb_value int_to_r(mrb_state *mrb, mrb_value self) { #ifdef RAT_BIGINT if (mrb_bigint_p(self)) { return rational_new_b(mrb, self, ONE); } #endif return rational_new_i(mrb, mrb_integer(self), 1); } static mrb_value nil_to_r(mrb_state *mrb, mrb_value self) { return rational_new_i(mrb, 0, 1); } #if !defined(MRB_NO_FLOAT) || defined(RAT_BIGINT) static mrb_value rational_new(mrb_state *mrb, mrb_value a, mrb_value b) { #ifdef MRB_NO_FLOAT a = mrb_as_int(mrb, a); b = mrb_as_int(mrb, b); return rational_new_i(mrb, mrb_integer(a), mrb_integer(b)); #else if (mrb_integer_p(a) && mrb_integer_p(b)) { return rational_new_i(mrb, mrb_integer(a), mrb_integer(b)); } #ifdef RAT_BIGINT else if (mrb_bigint_p(a) || mrb_bigint_p(b)) { return rational_new_b(mrb, mrb_as_bint(mrb, a), b); } #endif else { mrb_float x = mrb_as_float(mrb, a); mrb_float y = mrb_as_float(mrb, b); return rational_new_f(mrb, x/y); } #endif } static mrb_value rational_m(mrb_state *mrb, mrb_value self) { mrb_value a, b = ONE; mrb_get_args(mrb, "o|o", &a, &b); return rational_new(mrb, a, b); } #else static mrb_value rational_m(mrb_state *mrb, mrb_value self) { mrb_int n, d = 1; mrb_get_args(mrb, "i|i", &n, &d); return rational_new_i(mrb, n, d); } #endif static mrb_value rational_eq_b(mrb_state *mrb, mrb_value x, mrb_value y) { struct mrb_rational *p1 = rat_ptr(mrb, x); mrb_bool result; switch (mrb_type(y)) { case MRB_TT_INTEGER: if (p1->denominator != 1) return mrb_false_value(); result = p1->numerator == mrb_integer(y); break; #ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: result = ((double)p1->numerator/p1->denominator) == mrb_float(y); break; #endif case MRB_TT_RATIONAL: { struct mrb_rational *p2 = rat_ptr(mrb, y); mrb_int a, b; if (p1->numerator == p2->numerator && p1->denominator == p2->denominator) { return mrb_true_value(); } if (mrb_int_mul_overflow(p1->numerator, p2->denominator, &a) || mrb_int_mul_overflow(p2->numerator, p1->denominator, &b)) { #ifdef MRB_NO_FLOAT rat_overflow(mrb); #else result = (double)p1->numerator*p2->denominator == (double)p2->numerator*p2->denominator; break; #endif } result = a == b; break; } #ifdef MRB_USE_COMPLEX case MRB_TT_COMPLEX: { result = mrb_complex_eq(mrb, y, mrb_rational_to_f(mrb, x)); break; } #endif default: result = mrb_equal(mrb, y, x); break; } return mrb_bool_value(result); } static mrb_value rational_eq(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); #ifdef RAT_BIGINT if (RAT_BIGINT_P(x)) return rational_eq_b(mrb, x, y); #endif struct mrb_rational *p1 = rat_ptr(mrb, x); mrb_bool result; switch (mrb_type(y)) { case MRB_TT_INTEGER: if (p1->denominator != 1) return mrb_false_value(); result = p1->numerator == mrb_integer(y); break; #ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: result = ((double)p1->numerator/p1->denominator) == mrb_float(y); break; #endif case MRB_TT_RATIONAL: { struct mrb_rational *p2 = rat_ptr(mrb, y); mrb_int a, b; if (p1->numerator == p2->numerator && p1->denominator == p2->denominator) { return mrb_true_value(); } if (mrb_int_mul_overflow(p1->numerator, p2->denominator, &a) || mrb_int_mul_overflow(p2->numerator, p1->denominator, &b)) { #ifdef MRB_NO_FLOAT rat_overflow(mrb); #else result = (double)p1->numerator*p2->denominator == (double)p2->numerator*p2->denominator; break; #endif } result = a == b; break; } #ifdef MRB_USE_COMPLEX case MRB_TT_COMPLEX: { result = mrb_complex_eq(mrb, y, mrb_rational_to_f(mrb, x)); break; } #endif default: result = mrb_equal(mrb, y, x); break; } return mrb_bool_value(result); } static mrb_value rational_minus(mrb_state *mrb, mrb_value x) { struct mrb_rational *p = rat_ptr(mrb, x); #ifdef RAT_BIGINT mrb_value num; if (RAT_BIGINT_P(x)) { num = mrb_obj_value(p->b.num); bint: return rational_new_b(mrb, mrb_bint_neg(mrb, num), mrb_obj_value(p->b.den)); } #endif mrb_int n = p->numerator; if (n == MRB_INT_MIN) { #ifdef RAT_BIGINT num = mrb_as_bint(mrb, mrb_int_value(mrb, p->numerator)); goto bint; #else rat_overflow(mrb); #endif } return rational_new_i(mrb, -n, p->denominator); } #ifdef RAT_BIGINT static mrb_value rat_add_b(mrb_state *mrb, mrb_value x, mrb_value y) { mrb_value num1 = rat_numerator(mrb, x); mrb_value den1 = rat_denominator(mrb, x); mrb_value num2, den2; switch(mrb_type(y)) { case MRB_TT_RATIONAL: num2 = rat_numerator(mrb, y); den2 = rat_denominator(mrb, y); break; case MRB_TT_INTEGER: case MRB_TT_BIGINT: num2 = y; den2 = ONE; break; default: /* should not happen */ rat_type_error(mrb, y); } mrb_value a = mrb_bint_mul_n(mrb, mrb_as_bint(mrb, num1), den2); mrb_value b = mrb_bint_mul_n(mrb, mrb_as_bint(mrb, num2), den1); a = mrb_bint_add_n(mrb, a, b); b = mrb_bint_mul_n(mrb, mrb_as_bint(mrb, den1), den2); return rational_new_b(mrb, a, b); } #endif mrb_value mrb_rational_add(mrb_state *mrb, mrb_value x, mrb_value y) { struct mrb_rational *p1 = rat_ptr(mrb, x); switch (mrb_type(y)) { case MRB_TT_INTEGER: #ifdef RAT_BIGINT if (RAT_BIGINT_P(x)) return rat_add_b(mrb, x, y); #endif { mrb_int z = mrb_integer(y); if (mrb_int_mul_overflow(z, p1->denominator, &z)) rat_overflow(mrb); if (mrb_int_add_overflow(p1->numerator, z, &z)) rat_overflow(mrb); return rational_new_i(mrb, z, p1->denominator); } case MRB_TT_RATIONAL: #ifdef RAT_BIGINT if (RAT_BIGINT_P(x) || RAT_BIGINT_P(y)) return rat_add_b(mrb, x, y); #endif { struct mrb_rational *p2 = rat_ptr(mrb, y); mrb_int a, b; if (mrb_int_mul_overflow(p1->numerator, p2->denominator, &a)) rat_overflow(mrb); if (mrb_int_mul_overflow(p2->numerator, p1->denominator, &b)) rat_overflow(mrb); if (mrb_int_add_overflow(a, b, &a)) rat_overflow(mrb); if (mrb_int_mul_overflow(p1->denominator, p2->denominator, &b)) rat_overflow(mrb); return rational_new_i(mrb, a, b); } #ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: { mrb_float z = p1->numerator + mrb_float(y) * p1->denominator; return mrb_float_value(mrb, mrb_div_float(z, (mrb_float)p1->denominator)); } #endif #ifdef RAT_BIGINT case MRB_TT_BIGINT: return rat_add_b(mrb, x, y); #endif #if defined(MRB_USE_COMPLEX) case MRB_TT_COMPLEX: return mrb_complex_add(mrb, mrb_complex_new(mrb, rat_float(mrb, x), 0), y); #endif default: return mrb_funcall_argv(mrb, y, MRB_OPSYM(add), 1, &x); } } static mrb_value rational_add(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); return mrb_rational_add(mrb, x, y); } #ifdef RAT_BIGINT static mrb_value rat_sub_b(mrb_state *mrb, mrb_value x, mrb_value y) { mrb_value num1 = rat_numerator(mrb, x); mrb_value den1 = rat_denominator(mrb, x); mrb_value num2, den2; switch(mrb_type(y)) { case MRB_TT_RATIONAL: num2 = rat_numerator(mrb, y); den2 = rat_denominator(mrb, y); break; case MRB_TT_INTEGER: case MRB_TT_BIGINT: num2 = y; den2 = ONE; break; default: /* should not happen */ rat_type_error(mrb, y); } mrb_value a = mrb_bint_mul_n(mrb, mrb_as_bint(mrb, num1), den2); mrb_value b = mrb_bint_mul_n(mrb, mrb_as_bint(mrb, num2), den1); a = mrb_bint_sub_n(mrb, a, b); b = mrb_bint_mul_n(mrb, mrb_as_bint(mrb, den1), den2); return rational_new_b(mrb, a, b); } #endif mrb_value mrb_rational_sub(mrb_state *mrb, mrb_value x, mrb_value y) { struct mrb_rational *p1 = rat_ptr(mrb, x); switch (mrb_type(y)) { case MRB_TT_INTEGER: #ifdef RAT_BIGINT if (RAT_BIGINT_P(x)) return rat_sub_b(mrb, x, y); #endif { mrb_int z = mrb_integer(y); if (mrb_int_mul_overflow(z, p1->denominator, &z)) rat_overflow(mrb); if (mrb_int_sub_overflow(p1->numerator, z, &z)) rat_overflow(mrb); return rational_new_i(mrb, z, p1->denominator); } case MRB_TT_RATIONAL: #ifdef RAT_BIGINT if (RAT_BIGINT_P(x) || RAT_BIGINT_P(y)) return rat_sub_b(mrb, x, y); #endif { struct mrb_rational *p2 = rat_ptr(mrb, y); mrb_int a, b; if (mrb_int_mul_overflow(p1->numerator, p2->denominator, &a)) rat_overflow(mrb); if (mrb_int_mul_overflow(p2->numerator, p1->denominator, &b)) rat_overflow(mrb); if (mrb_int_sub_overflow(a, b, &a)) rat_overflow(mrb); if (mrb_int_mul_overflow(p1->denominator, p2->denominator, &b)) rat_overflow(mrb); return rational_new_i(mrb, a, b); } #ifdef RAT_BIGINT case MRB_TT_BIGINT: return rat_sub_b(mrb, x, y); #endif #if defined(MRB_USE_COMPLEX) case MRB_TT_COMPLEX: return mrb_complex_sub(mrb, mrb_complex_new(mrb, rat_float(mrb, x), 0), y); #endif #ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: default: { mrb_float z = p1->numerator - mrb_as_float(mrb, y) * p1->denominator; return mrb_float_value(mrb, mrb_div_float(z, (mrb_float)p1->denominator)); } #else default: rat_type_error(mrb, y); #endif } } static mrb_value rational_sub(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); return mrb_rational_sub(mrb, x, y); } #ifdef RAT_BIGINT static mrb_value rat_mul_b(mrb_state *mrb, mrb_value x, mrb_value y) { mrb_value num, den; switch(mrb_type(y)) { case MRB_TT_RATIONAL: num = rat_numerator(mrb, y); den = rat_denominator(mrb, y); break; case MRB_TT_INTEGER: case MRB_TT_BIGINT: num = y; den = ONE; break; default: /* should not happen */ rat_type_error(mrb, y); } mrb_value a = mrb_bint_mul_n(mrb, mrb_as_bint(mrb, rat_numerator(mrb, x)), num); mrb_value b = mrb_bint_mul_n(mrb, mrb_as_bint(mrb, rat_denominator(mrb, x)), den); return rational_new_b(mrb, a, b); } #endif mrb_value mrb_rational_mul(mrb_state *mrb, mrb_value x, mrb_value y) { switch (mrb_type(y)) { case MRB_TT_INTEGER: #ifdef RAT_BIGINT if (RAT_BIGINT_P(x)) return rat_mul_b(mrb, x, y); #endif { struct mrb_rational *p1 = rat_ptr(mrb, x); mrb_int z = mrb_integer(y); if (mrb_int_mul_overflow(p1->numerator, z, &z)) rat_overflow(mrb); return rational_new_i(mrb, z, p1->denominator); } case MRB_TT_RATIONAL: #ifdef RAT_BIGINT if (RAT_BIGINT_P(x) || RAT_BIGINT_P(y)) return rat_mul_b(mrb, x, y); #endif { struct mrb_rational *p1 = rat_ptr(mrb, x); struct mrb_rational *p2 = rat_ptr(mrb, y); mrb_int a, b; if (mrb_int_mul_overflow(p1->numerator, p2->numerator, &a)) rat_overflow(mrb); if (mrb_int_mul_overflow(p1->denominator, p2->denominator, &b)) rat_overflow(mrb); return rational_new_i(mrb, a, b); } #ifdef RAT_BIGINT case MRB_TT_BIGINT: return rat_mul_b(mrb, x, y); #endif #ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: { struct mrb_rational *p1 = rat_ptr(mrb, x); mrb_float z = p1->numerator * mrb_float(y); return mrb_float_value(mrb, mrb_div_float(z, (mrb_float)p1->denominator)); } #endif #if defined(MRB_USE_COMPLEX) case MRB_TT_COMPLEX: return mrb_complex_mul(mrb, mrb_complex_new(mrb, rat_float(mrb, x), 0), y); #endif default: return mrb_funcall_argv(mrb, y, MRB_OPSYM(mul), 1, &x); } } static mrb_value rational_mul(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); return mrb_rational_mul(mrb, x, y); } #ifdef RAT_BIGINT static mrb_value rat_div_b(mrb_state *mrb, mrb_value x, mrb_value y) { mrb_value num, den; switch(mrb_type(y)) { case MRB_TT_RATIONAL: num = rat_numerator(mrb, y); den = rat_denominator(mrb, y); break; case MRB_TT_INTEGER: #ifdef MRB_USE_BIGINT case MRB_TT_BIGINT: #endif num = y; den = ONE; break; default: /* should not happen */ rat_type_error(mrb, y); } mrb_value a = mrb_bint_mul_n(mrb, mrb_as_bint(mrb, rat_numerator(mrb, x)), den); mrb_value b = mrb_bint_mul_n(mrb, mrb_as_bint(mrb, rat_denominator(mrb, x)), num); return rational_new_b(mrb, a, b); } #endif mrb_value mrb_rational_div(mrb_state *mrb, mrb_value x, mrb_value y) { switch (mrb_type(y)) { case MRB_TT_INTEGER: #ifdef RAT_BIGINT if (RAT_BIGINT_P(x)) return rat_div_b(mrb, x, y); #endif { struct mrb_rational *p1 = rat_ptr(mrb, x); mrb_int z = mrb_integer(y); if (z == 0) mrb_int_zerodiv(mrb); if (mrb_int_mul_overflow(p1->denominator, z, &z)) rat_overflow(mrb); return rational_new_i(mrb, p1->numerator, z); } case MRB_TT_RATIONAL: #ifdef RAT_BIGINT if (RAT_BIGINT_P(x) || RAT_BIGINT_P(y)) return rat_div_b(mrb, x, y); #endif { struct mrb_rational *p1 = rat_ptr(mrb, x); struct mrb_rational *p2 = rat_ptr(mrb, y); mrb_int a, b; if (mrb_int_mul_overflow(p1->numerator, p2->denominator, &a)) rat_overflow(mrb); if (mrb_int_mul_overflow(p2->numerator, p1->denominator, &b)) rat_overflow(mrb); return rational_new_i(mrb, a, b); } #ifdef RAT_BIGINT case MRB_TT_BIGINT: return rat_div_b(mrb, x, y); #endif #ifdef MRB_USE_COMPLEX case MRB_TT_COMPLEX: return mrb_complex_div(mrb, mrb_complex_new(mrb, rat_float(mrb, x), 0), y); #endif #ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: { struct mrb_rational *p1 = rat_ptr(mrb, x); mrb_float z = mrb_div_float((mrb_float)p1->numerator, mrb_as_float(mrb, y)); return mrb_float_value(mrb, mrb_div_float(z, (mrb_float)p1->denominator)); } #endif default: rat_type_error(mrb, y); /* not reached */ return mrb_nil_value(); } } static mrb_value rational_div(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); return mrb_rational_div(mrb, x, y); } mrb_value mrb_int_pow(mrb_state *mrb, mrb_value x, mrb_value y); static mrb_value rational_pow(mrb_state *mrb, mrb_value x) { #ifndef MRB_NO_FLOAT mrb_value y = mrb_get_arg1(mrb); double d1 = rat_float(mrb, x); double d2 = mrb_as_float(mrb, y); d1 = pow(d1, d2); switch (mrb_type(y)) { case MRB_TT_FLOAT: return mrb_float_value(mrb, d1); case MRB_TT_INTEGER: case MRB_TT_RATIONAL: return rational_new_f(mrb, d1); case MRB_TT_BIGINT: default: return mrb_float_value(mrb, d1); } #else mrb_raisef(mrb, E_NOTIMP_ERROR, "Rational#** not implemented with MRB_NO_FLOAT"); /* not reached */ return mrb_nil_value(); #endif } static mrb_value rational_hash(mrb_state *mrb, mrb_value rat) { struct mrb_rational *r = rat_ptr(mrb, rat); uint32_t hash; #ifdef RAT_BIGINT if (RAT_BIGINT_P(rat)) { mrb_value tmp = mrb_bint_hash(mrb, mrb_obj_value(r->b.num)); hash = (uint32_t)mrb_integer(tmp); tmp = mrb_bint_hash(mrb, mrb_obj_value(r->b.den)); hash ^= (uint32_t)mrb_integer(tmp); return mrb_int_value(mrb, hash); } #endif hash = mrb_byte_hash((uint8_t*)&r->numerator, sizeof(mrb_int)); hash = mrb_byte_hash_step((uint8_t*)&r->denominator, sizeof(mrb_int), hash); return mrb_int_value(mrb, hash); } void mrb_mruby_rational_gem_init(mrb_state *mrb) { struct RClass *rat = mrb_define_class_id(mrb, MRB_SYM(Rational), mrb_class_get_id(mrb, MRB_SYM(Numeric))); MRB_SET_INSTANCE_TT(rat, MRB_TT_RATIONAL); MRB_UNDEF_ALLOCATOR(rat); mrb_undef_class_method_id(mrb, rat, MRB_SYM(new)); mrb_define_method_id(mrb, rat, MRB_SYM(numerator), rational_numerator, MRB_ARGS_NONE()); mrb_define_method_id(mrb, rat, MRB_SYM(denominator), rational_denominator, MRB_ARGS_NONE()); #ifndef MRB_NO_FLOAT mrb_define_method_id(mrb, rat, MRB_SYM(to_f), mrb_rational_to_f, MRB_ARGS_NONE()); #endif mrb_define_method_id(mrb, rat, MRB_SYM(to_i), mrb_rational_to_i, MRB_ARGS_NONE()); mrb_define_method_id(mrb, rat, MRB_SYM(to_r), mrb_obj_itself, MRB_ARGS_NONE()); mrb_define_method_id(mrb, rat, MRB_SYM_Q(negative), rational_negative_p, MRB_ARGS_NONE()); mrb_define_method_id(mrb, rat, MRB_OPSYM(eq), rational_eq, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, rat, MRB_OPSYM(minus), rational_minus, MRB_ARGS_NONE()); mrb_define_method_id(mrb, rat, MRB_OPSYM(add), rational_add, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, rat, MRB_OPSYM(sub), rational_sub, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, rat, MRB_OPSYM(mul), rational_mul, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, rat, MRB_OPSYM(div), rational_div, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, rat, MRB_SYM(quo), rational_div, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, rat, MRB_OPSYM(pow), rational_pow, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, rat, MRB_SYM(hash), rational_hash, MRB_ARGS_NONE()); #ifndef MRB_NO_FLOAT mrb_define_method_id(mrb, mrb->float_class, MRB_SYM(to_r), float_to_r, MRB_ARGS_NONE()); #endif mrb_define_method_id(mrb, mrb->integer_class, MRB_SYM(to_r), int_to_r, MRB_ARGS_NONE()); mrb_define_method_id(mrb, mrb->nil_class, MRB_SYM(to_r), nil_to_r, MRB_ARGS_NONE()); mrb_define_private_method_id(mrb, mrb->kernel_module, MRB_SYM(Rational), rational_m, MRB_ARGS_ARG(1,1)); } void mrb_mruby_rational_gem_final(mrb_state* mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-os-memsize0000644000000000000000000000013215077107334022471 xustar0030 mtime=1761382108.933300864 30 atime=1761382109.797298366 30 ctime=1761382108.933300864 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-os-memsize/0000755000175100017510000000000015077107334023136 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-os-memsize/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024670 xustar0030 mtime=1761382078.123420552 30 atime=1761382080.143411311 30 ctime=1761382108.933300864 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-os-memsize/mrbgem.rake0000644000175100017510000000070415077107276025261 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-os-memsize') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'ObjectSpace memsize_of method' spec.add_dependency('mruby-objectspace', :core => 'mruby-objectspace') spec.add_test_dependency('mruby-metaprog', :core => 'mruby-metaprog') spec.add_test_dependency('mruby-method', :core => 'mruby-method') spec.add_test_dependency('mruby-fiber', :core => 'mruby-fiber') end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-os-memsize/PaxHeaders/test0000644000000000000000000000013215077107334023450 xustar0030 mtime=1761382108.935300858 30 atime=1761382109.797298366 30 ctime=1761382108.935300858 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-os-memsize/test/0000755000175100017510000000000015077107334024115 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-os-memsize/test/PaxHeaders/memsize.rb0000644000000000000000000000013215077107276025530 xustar0030 mtime=1761382078.123420552 30 atime=1761382080.143411311 30 ctime=1761382108.935300858 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-os-memsize/test/memsize.rb0000644000175100017510000000352715077107276026127 0ustar00runnerrunnerassert 'ObjectSpace.memsize_of' do # immediate literals int_size = ObjectSpace.memsize_of 1 assert_equal int_size, 0, 'int zero' sym_size = ObjectSpace.memsize_of :foo assert_equal sym_size, 0, 'sym zero' assert_equal ObjectSpace.memsize_of(true), int_size assert_equal ObjectSpace.memsize_of(false), int_size assert_not_equal ObjectSpace.memsize_of('a'), 0, 'memsize of str' if __ENCODING__ == "UTF-8" assert_not_equal ObjectSpace.memsize_of("ã“ã‚“ã«ã¡ã¯ä¸–界"), 0, 'memsize of utf8 str' end # class defs class_obj_size = ObjectSpace.memsize_of Class assert_not_equal class_obj_size, 0, 'Class obj not zero' empty_class_def_size = ObjectSpace.memsize_of Class.new assert_not_equal empty_class_def_size, 0, 'Class def not zero' proc_size = ObjectSpace.memsize_of Proc.new { x = 1; x } assert_not_equal proc_size, 0 class_with_methods = Class.new do def foo a = 0 a + 1 end end m_size = ObjectSpace.memsize_of class_with_methods.instance_method(:foo) assert_not_equal m_size, 0, 'method size not zero' # collections empty_array_size = ObjectSpace.memsize_of [] assert_not_equal empty_array_size, 0, 'empty array size not zero' assert_operator empty_array_size, :<, ObjectSpace.memsize_of(Array.new(16)), 'large array size greater than embed' # fiber empty_fiber_size = ObjectSpace.memsize_of(Fiber.new {}) assert_not_equal empty_fiber_size, 0, 'empty fiber not zero' #hash assert_not_equal ObjectSpace.memsize_of({}), 0, 'empty hash size not zero' end assert 'ObjectSpace.memsize_of_all' do foo_class = Class.new do def initialize @a = 'a' @b = 'b' end end foos = Array.new(10) { foo_class.new } foo_size = ObjectSpace.memsize_of(foos.first) assert_equal ObjectSpace.memsize_of_all(foo_class), foo_size * foos.size, 'Memsize of all instance' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-os-memsize/PaxHeaders/src0000644000000000000000000000013215077107334023260 xustar0030 mtime=1761382108.936300855 30 atime=1761382109.798298363 30 ctime=1761382108.936300855 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-os-memsize/src/0000755000175100017510000000000015077107334023725 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-os-memsize/src/PaxHeaders/memsize.c0000644000000000000000000000013215077107276025157 xustar0030 mtime=1761382078.123420552 30 atime=1761382080.143411311 30 ctime=1761382108.936300855 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-os-memsize/src/memsize.c0000644000175100017510000001715215077107276025555 0ustar00runnerrunner#include #include #include #include #include #include #include #include #include #include #include #include #include #include static size_t os_memsize_of_irep(mrb_state* state, const struct mrb_irep *irep) { size_t size = (irep->slen * sizeof(mrb_sym)) + (irep->plen * sizeof(mrb_irep_pool)) + (irep->ilen * sizeof(mrb_code)) + (irep->rlen * sizeof(struct mrb_irep*)); for (int i = 0; i < irep->plen; i++) { const mrb_irep_pool *p = &irep->pool[i]; if ((p->tt & IREP_TT_NFLAG) == 0) { /* string pool value */ size += (p->tt>>2); } else if (p->tt == IREP_TT_BIGINT) { /* bigint pool value */ size += p->u.str[0]; } } for (int i = 0; i < irep->rlen; i++) { size += sizeof(struct mrb_irep); /* size of irep structure */ size += os_memsize_of_irep(state, irep->reps[i]); } return size; } static size_t os_memsize_of_method(mrb_state* mrb, mrb_value method_obj) { mrb_value proc_value = mrb_obj_iv_get(mrb, mrb_obj_ptr(method_obj), mrb_intern_lit(mrb, "_proc")); if (mrb_nil_p(proc_value)) return 0; struct RProc *proc = mrb_proc_ptr(proc_value); size_t size = sizeof(struct RProc); if (!MRB_PROC_CFUNC_P(proc)) size += os_memsize_of_irep(mrb, proc->body.irep); return size; } static mrb_bool obj_is_kind_of_checked(mrb_state *mrb, mrb_value obj, const char *classname) { mrb_value objclass = mrb_obj_value(mrb->object_class); if (mrb_const_defined(mrb, objclass, mrb_intern_cstr(mrb, classname))) { struct RClass *klass = mrb_class_get(mrb, classname); return mrb_obj_is_kind_of(mrb, obj, klass); } return FALSE; } static size_t os_memsize_of_object(mrb_state* mrb, mrb_value obj) { size_t size = 0; switch(mrb_type(obj)) { case MRB_TT_STRING: size += mrb_objspace_page_slot_size(); if (!RSTR_EMBED_P(RSTRING(obj)) && !RSTR_SHARED_P(RSTRING(obj))) { size += RSTRING_CAPA(obj); size++; /* NUL terminator */ } break; case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_SCLASS: case MRB_TT_ICLASS: size += mrb_class_mt_memsize(mrb, mrb_class_ptr(obj)); /* fall through */ case MRB_TT_EXCEPTION: case MRB_TT_OBJECT: { size += mrb_objspace_page_slot_size(); size += mrb_obj_iv_tbl_memsize(obj); if (obj_is_kind_of_checked(mrb, obj, "UnboundMethod") || obj_is_kind_of_checked(mrb, obj, "Method")) { size += os_memsize_of_method(mrb, obj); } break; } case MRB_TT_HASH: { size += mrb_objspace_page_slot_size() + mrb_hash_memsize(obj); break; } case MRB_TT_STRUCT: case MRB_TT_ARRAY: { mrb_int len = RARRAY_LEN(obj); /* Arrays that do not fit within an RArray perform a heap allocation * storing an array of pointers to the original objects*/ size += mrb_objspace_page_slot_size(); if (len > MRB_ARY_EMBED_LEN_MAX) size += sizeof(mrb_value*) * len; break; } case MRB_TT_PROC: { struct RProc* proc = mrb_proc_ptr(obj); size += mrb_objspace_page_slot_size(); size += MRB_ENV_LEN(proc->e.env) * sizeof(mrb_value); if (!MRB_PROC_CFUNC_P(proc)) size += os_memsize_of_irep(mrb, proc->body.irep); break; } case MRB_TT_RANGE: #ifndef MRB_RANGE_EMBED size += mrb_objspace_page_slot_size() + sizeof(struct mrb_range_edges); #endif break; case MRB_TT_FIBER: { struct RFiber* f = (struct RFiber*)mrb_ptr(obj); ptrdiff_t stack_size = f->cxt->stend - f->cxt->stbase; ptrdiff_t ci_size = f->cxt->ciend - f->cxt->cibase; size += mrb_objspace_page_slot_size() + sizeof(struct mrb_context) + sizeof(mrb_value) * stack_size + sizeof(mrb_callinfo) * ci_size; break; } #ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: #endif case MRB_TT_INTEGER: if (mrb_immediate_p(obj)) break; case MRB_TT_RATIONAL: #if defined(MRB_USE_RATIONAL) #if defined(MRB_INT64) && defined(MRB_32BIT) size += sizeof(mrb_int)*2; #endif size += mrb_objspace_page_slot_size(); #endif break; case MRB_TT_COMPLEX: #if defined(MRB_USE_COMPLEX) #if defined(MRB_32BIT) && !defined(MRB_USE_FLOAT32) size += sizeof(mrb_float)*2; #endif size += mrb_objspace_page_slot_size(); #endif break; case MRB_TT_BIGINT: #if defined(MRB_USE_BIGINT) size += mrb_bint_memsize(obj); /* fall through */ #endif case MRB_TT_CDATA: case MRB_TT_ISTRUCT: size += mrb_objspace_page_slot_size(); break; case MRB_TT_BACKTRACE: size += ((struct RBacktrace*)mrb_obj_ptr(obj))->len * sizeof(struct mrb_backtrace_location); break; /* zero heap size types. * immediate VM stack values, contained within mrb_state, or on C stack */ case MRB_TT_TRUE: case MRB_TT_FALSE: case MRB_TT_BREAK: case MRB_TT_CPTR: case MRB_TT_SYMBOL: case MRB_TT_FREE: case MRB_TT_UNDEF: case MRB_TT_ENV: /* never used, silences compiler warning * not having a default: clause lets the compiler tell us when there is a new * TT not accounted for */ case MRB_TT_MAXDEFINE: break; } return size; } /* * call-seq: * ObjectSpace.memsize_of(obj) -> Numeric * * Returns the amount of heap memory allocated for object in size_t units. * * The return value depends on the definition of size_t on that platform, * therefore the value is not comparable across platform types. * * Immediate values such as integers, booleans, symbols and unboxed float numbers * return 0. Additionally special objects which are small enough to fit inside an * object pointer, termed embedded objects, will return the size of the object pointer. * Strings and arrays below a compile-time defined size may be embedded. */ static mrb_value os_memsize_of(mrb_state *mrb, mrb_value self) { mrb_value obj = mrb_get_arg1(mrb); size_t total = os_memsize_of_object(mrb, obj); return mrb_fixnum_value((mrb_int)total); } struct os_memsize_of_all_cb_data { size_t t; struct RClass *type; }; static int os_memsize_of_all_cb(mrb_state *mrb, struct RBasic *obj, void *d) { struct os_memsize_of_all_cb_data *data = (struct os_memsize_of_all_cb_data*)d; switch (obj->tt) { case MRB_TT_FREE: case MRB_TT_ENV: case MRB_TT_BREAK: case MRB_TT_ICLASS: /* internal objects that should not be counted */ return MRB_EACH_OBJ_OK; default: break; } /* skip Proc objects for methods */ if (obj->c == NULL) return 0; if (data->type == NULL || mrb_obj_is_kind_of(mrb, mrb_obj_value(obj), data->type)) data->t += os_memsize_of_object(mrb, mrb_obj_value(obj)); return MRB_EACH_OBJ_OK; } /* * call-seq: * ObjectSpace.memsize_of_all([klass]) -> Numeric * * Return consuming memory size of all living objects of type klass. * */ static mrb_value os_memsize_of_all(mrb_state *mrb, mrb_value self) { struct os_memsize_of_all_cb_data data = { 0 }; mrb_get_args(mrb, "|c", &data.type); mrb_objspace_each_objects(mrb, os_memsize_of_all_cb, &data); return mrb_fixnum_value((mrb_int)data.t); } void mrb_mruby_os_memsize_gem_init(mrb_state *mrb) { struct RClass *os = mrb_module_get(mrb, "ObjectSpace"); mrb_define_class_method(mrb, os, "memsize_of", os_memsize_of, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, os, "memsize_of_all", os_memsize_of_all, MRB_ARGS_OPT(1)); } void mrb_mruby_os_memsize_gem_final(mrb_state *mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-range-ext0000644000000000000000000000013215077107334022273 xustar0030 mtime=1761382108.766301347 30 atime=1761382109.798298363 30 ctime=1761382108.766301347 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-range-ext/0000755000175100017510000000000015077107334022740 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-range-ext/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024472 xustar0030 mtime=1761382078.125420543 30 atime=1761382080.144411306 30 ctime=1761382108.766301347 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-range-ext/mrbgem.rake0000644000175100017510000000024315077107276025061 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-range-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Range class extension' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-range-ext/PaxHeaders/mrblib0000644000000000000000000000013215077107334023542 xustar0030 mtime=1761382108.768301341 30 atime=1761382109.798298363 30 ctime=1761382108.768301341 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-range-ext/mrblib/0000755000175100017510000000000015077107334024207 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-range-ext/mrblib/PaxHeaders/range.rb0000644000000000000000000000013215077107276025245 xustar0030 mtime=1761382078.125420543 30 atime=1761382080.144411306 30 ctime=1761382108.768301341 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-range-ext/mrblib/range.rb0000644000175100017510000000646515077107276025650 0ustar00runnerrunnerclass Range ## # call-seq: # rng.first -> obj # rng.first(n) -> an_array # # Returns the first object in the range, or an array of the first +n+ # elements. # # (10..20).first #=> 10 # (10..20).first(3) #=> [10, 11, 12] # def first(*args) raise RangeError, "cannot get the first element of beginless range" if self.begin.nil? return self.begin if args.empty? raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 1)" unless args.length == 1 nv = args[0] n = nv.__to_int raise ArgumentError, "negative array size (or size too big)" unless 0 <= n ary = [] each do |i| break if n <= 0 ary.push(i) n -= 1 end ary end ## # call-seq: # rng.last -> obj # rng.last(n) -> an_array # # Returns the last object in the range, # or an array of the last +n+ elements. # # Note that with no arguments +last+ will return the object that defines # the end of the range even if #exclude_end? is +true+. # # (10..20).last #=> 20 # (10...20).last #=> 20 # (10..20).last(3) #=> [18, 19, 20] # (10...20).last(3) #=> [17, 18, 19] def last(*args) raise RangeError, "cannot get the last element of endless range" if self.end.nil? return self.end if args.empty? raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 1)" unless args.length == 1 nv = args[0] n = nv.__to_int raise ArgumentError, "negative array size (or size too big)" unless 0 <= n return self.to_a.last(nv) end def max(&block) val = self.begin last = self.end return super(&block) if block raise RangeError, "cannot get the maximum of endless range" if last.nil? # fast path for numerics if val.kind_of?(Numeric) && last.kind_of?(Numeric) raise TypeError if exclude_end? && !last.kind_of?(Integer) return nil if val > last return nil if val == last && exclude_end? max = last max -= 1 if exclude_end? return max end # delegate to Enumerable super() end def min(&block) val = self.begin last = self.end if block raise RangeError, "cannot get the minimum of endless range with custom comparison method" if last.nil? return super(&block) end return val if last.nil? # fast path for numerics if val.kind_of?(Numeric) && last.kind_of?(Numeric) return nil if val > last return nil if val == last && exclude_end? min = val return min end # delegate to Enumerable super() end # Compare two ranges and see if they overlap each other # (1..5).overlap?(4..6) # => true # (1..5).overlap?(7..9) # => false def overlap?(other) raise TypeError, "argument must be a range" unless other.kind_of?(Range) self_begin = self.begin other_end = other.end other_excl = other.exclude_end? return false if __empty_range?(self_begin, other_end, other_excl) other_begin = other.begin self_end = self.end self_excl = self.exclude_end? return false if __empty_range?(other_begin, self_end, self_excl) return true if self_begin == other_begin return false if __empty_range?(self_begin, self_end, self_excl) return false if __empty_range?(other_begin, other_end, other_excl) true end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-range-ext/PaxHeaders/test0000644000000000000000000000013215077107334023252 xustar0030 mtime=1761382108.767301344 30 atime=1761382109.798298363 30 ctime=1761382108.767301344 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-range-ext/test/0000755000175100017510000000000015077107334023717 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-range-ext/test/PaxHeaders/range.rb0000644000000000000000000000013215077107276024755 xustar0030 mtime=1761382078.125420543 30 atime=1761382080.144411306 30 ctime=1761382108.767301344 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-range-ext/test/range.rb0000644000175100017510000001433615077107276025354 0ustar00runnerrunner## # Range(Ext) Test assert('Range#cover?') do assert_true ("a".."z").cover?("c") assert_false ("a".."z").cover?("5") assert_true ("a".."z").cover?("cc") assert_false ("a".."z").cover?(nil) assert_true ("a"..).cover?("c") assert_false ("a"..).cover?("5") assert_true ("a"..).cover?("cc") assert_true (.."z").cover?("a") assert_false (..."z").cover?("z") assert_true (.."z").cover?("z") assert_true (nil..nil).cover?(nil) assert_true ("a".."c").cover?("b".."d") assert_true ("a"..).cover?("b"..) assert_false ("a"..).cover?(1..) assert_false ("d"..).cover?(.."b") assert_true (.."c").cover?("b".."d") assert_true (.."c").cover?(.."d") assert_false (.."c").cover?(..2) assert_false (.."c").cover?("d"..) end assert('Range#first') do assert_equal 10, (10..20).first assert_equal [10, 11, 12], (10..20).first(3) assert_equal 10, (10..).first assert_equal [10, 11, 12], (10..).first(3) assert_raise(RangeError) { (..1).first } skip unless Object.const_defined?(:Float) assert_equal [0, 1, 2], (0..Float::INFINITY).first(3) end assert('Range#last') do assert_equal 20, (10..20).last assert_equal 20, (10...20).last assert_raise(RangeError) { (10..).last } assert_raise(RangeError) { (10...).last } assert_equal [18, 19, 20], (10..20).last(3) assert_equal [17, 18, 19], (10...20).last(3) end assert('Range#size') do assert_equal 42, (1..42).size assert_equal 41, (1...42).size assert_nil ('a'..'z').size assert_nil ('a'..).size assert_nil (1..).size unless Object.const_defined?(:Float) skip unless Object.const_defined?(:Float) assert_equal 6, (1...6.3).size assert_equal 5, (1...6.0).size assert_equal Float::INFINITY, (0..Float::INFINITY).size assert_equal Float::INFINITY, (1..).size assert_equal Float::INFINITY, (1...).size end assert('Range#max') do # returns the maximum value in the range when called with no arguments assert_equal 10, (1..10).max assert_equal 9, (1...10).max assert_equal 536870911, (0...2**29).max # returns nil when the endpoint is less than the start point assert_equal nil, (100..10).max # returns nil when the endpoint equals the start point and the range is exclusive assert_equal nil, (5...5).max # returns the endpoint when the endpoint equals the start point and the range is inclusive assert_equal 5, (5..5).max # raises RangeError when called on an endless range assert_raise(RangeError) { (10..).max } assert_raise(RangeError) { (10...).max } skip unless Object.const_defined?(:Float) # returns the maximum value in the Float range when called with no arguments assert_equal 908.1111, (303.20..908.1111).max # raises TypeError when called on an exclusive range and a non Integer value assert_raise(TypeError) { (303.20...908.1111).max } # returns nil when the endpoint is less than the start point in a Float range assert_equal nil, (3003.20..908.1111).max end assert('Range#max given a block') do # passes each pair of values in the range to the block acc = [] (1..10).max do |a, b| acc << a acc << b a end (1..10).each do |value| assert_true acc.include?(value) end # passes each pair of elements to the block in reversed order acc = [] (1..5).max do |a, b| acc << [a, b] a end assert_equal [[2, 1], [3, 2], [4, 3], [5, 4]], acc # returns the element the block determines to be the maximum assert_equal 1, ((1..3).max { |_a, _b| -3 }) # returns nil when the endpoint is less than the start point assert_equal nil, ((100..10).max { |x, y| x <=> y }) assert_equal nil, ((5...5).max { |x, y| x <=> y }) end assert('Range#min') do # returns the minimum value in the range when called with no arguments assert_equal 1, (1..10).min assert_equal 1, (1...10).min assert_equal 1, (1..).min # returns nil when the start point is greater than the endpoint assert_equal nil, (100..10).min # returns nil when the endpoint equals the start point and the range is exclusive assert_equal nil, (5...5).min # returns the endpoint when the endpoint equals the start point and the range is inclusive assert_equal 5, (5..5).min skip unless Object.const_defined?(:Float) # returns the minimum value in the Float range when called with no arguments assert_equal 303.20, (303.20..908.1111).min assert_equal 1, (1.0..).min # returns nil when the start point is greater than the endpoint in a Float range assert_equal nil, (3003.20..908.1111).min end assert('Range#min given a block') do # raise when called with a block in endless range assert_raise(RangeError) { (1..).min{} } # passes each pair of values in the range to the block acc = [] (1..10).min do |a, b| acc << a acc << b a end (1..10).each do |value| assert_true acc.include?(value) end # passes each pair of elements to the block in reversed order acc = [] (1..5).min do |a, b| acc << [a, b] a end assert_equal [[2, 1], [3, 1], [4, 1], [5, 1]], acc # returns the element the block determines to be the minimum assert_equal 3, ((1..3).min { |_a, _b| -3 }) # returns nil when the start point is greater than the endpoint assert_equal nil, ((100..10).min { |x, y| x <=> y }) assert_equal nil, ((5...5).min { |x, y| x <=> y }) end assert('Range#overlap?') do assert_false((0..2).overlap?(-2..-1)) assert_false((0..2).overlap?(-2...0)) assert_true((0..2).overlap?(-1..0)) assert_true((0..2).overlap?(1..2)) assert_true((0..2).overlap?(2..3)) assert_false((0..2).overlap?(3...4)) assert_false((0...2).overlap?(2..3)) assert_true((..0).overlap?(-1..0)) assert_true((...0).overlap?(-1..0)) assert_true((..0).overlap?(0..1)) assert_true((..0).overlap?(..1)) assert_false((..0).overlap?(1..2)) assert_false((...0).overlap?(0..1)) assert_false((0..).overlap?(-2..-1)) assert_false((0..).overlap?(...0)) assert_true((0..).overlap?(..0)) assert_true((0..).overlap?(0..1)) assert_true((0..).overlap?(1..2)) assert_true((0..).overlap?(-1..0)) assert_true((0..).overlap?(1..)) assert_true((0..).overlap?(-1..0)) assert_true((0..).overlap?(..0)) assert_true((0..).overlap?(0..1)) assert_true((0..).overlap?(1..2)) assert_true((0..).overlap?(1..)) assert_raise(TypeError) { (0..).overlap?(1) } assert_raise(TypeError) { (0..).overlap?(nil) } end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-range-ext/PaxHeaders/src0000644000000000000000000000013215077107334023062 xustar0030 mtime=1761382108.770301335 30 atime=1761382109.798298363 30 ctime=1761382108.770301335 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-range-ext/src/0000755000175100017510000000000015077107334023527 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-range-ext/src/PaxHeaders/range.c0000644000000000000000000000013215077107276024404 xustar0030 mtime=1761382078.125420543 30 atime=1761382080.144411306 30 ctime=1761382108.770301335 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-range-ext/src/range.c0000644000175100017510000001360315077107276024777 0ustar00runnerrunner#include #include #include static mrb_bool r_less(mrb_state *mrb, mrb_value a, mrb_value b, mrb_bool excl) { switch (mrb_cmp(mrb, a, b)) { case -2: /* failure */ case 1: return FALSE; case 0: return !excl; case -1: default: /* just in case */ return TRUE; } } /* * call-seq: * rng.cover?(obj) -> true or false * rng.cover?(range) -> true or false * * Returns +true+ if the given argument is within +self+, +false+ otherwise. * * With non-range argument +object+, evaluates with <= and <. * * For range +self+ with included end value (#exclude_end? == false), * evaluates thus: * * self.begin <= object <= self.end * * ("a".."z").cover?("c") #=> true * ("a".."z").cover?("5") #=> false * ("a".."z").cover?("cc") #=> true */ static mrb_value range_cover(mrb_state *mrb, mrb_value range) { struct RRange *r = mrb_range_ptr(mrb, range); mrb_value val = mrb_get_arg1(mrb); mrb_value beg = RANGE_BEG(r); mrb_value end = RANGE_END(r); if (mrb_nil_p(beg) && mrb_nil_p(end)) return mrb_true_value(); if (mrb_range_p(val)) { struct RRange *r2 = mrb_range_ptr(mrb, val); mrb_value beg2 = RANGE_BEG(r2); mrb_value end2 = RANGE_END(r2); /* range.cover?(nil..nil) => true */ if (mrb_nil_p(beg2) && mrb_nil_p(end2)) return mrb_true_value(); /* (a..b).cover?(c..d) */ if (mrb_nil_p(end)) { /* a.. */ /* (a..).cover?(c..) => true */ if (mrb_nil_p(end2)) return mrb_bool_value(mrb_cmp(mrb, beg, beg2) != -2); /* (a..).cover?(c..d) where d false */ if (r_less(mrb, end2, beg, RANGE_EXCL(r2))) return mrb_false_value(); return mrb_true_value(); } else if (mrb_nil_p(beg)) { /* ..b */ /* (..b).cover?(..d) => true */ if (mrb_nil_p(beg2)) return mrb_bool_value(mrb_cmp(mrb, end, end2) != -2); /* (..b).cover?(c..d) where b false */ if (r_less(mrb, end, beg2, RANGE_EXCL(r))) return mrb_false_value(); return mrb_true_value(); } else { /* a..b */ /* (a..b).cover?(c..) => (c (a false */ if (r_less(mrb, end, beg2, RANGE_EXCL(r))) return mrb_false_value(); /* (a..b).cover?(c..d) where (d false */ if (r_less(mrb, end2, beg, RANGE_EXCL(r2))) return mrb_false_value(); return mrb_true_value(); } } if (mrb_nil_p(beg) || r_less(mrb, beg, val, FALSE)) { if (mrb_nil_p(end)) { return mrb_true_value(); } if (r_less(mrb, val, end, RANGE_EXCL(r))) return mrb_true_value(); } return mrb_false_value(); } /* * call-seq: * rng.size -> num * * Returns the number of elements in the range. Both the begin and the end of * the Range must be Numeric, otherwise nil is returned. * * (10..20).size #=> 11 * ('a'..'z').size #=> nil */ #ifndef MRB_NO_FLOAT static mrb_value range_size(mrb_state *mrb, mrb_value range) { struct RRange *r = mrb_range_ptr(mrb, range); mrb_value beg = RANGE_BEG(r); mrb_value end = RANGE_END(r); if (mrb_float_p(beg)) { mrb_raise(mrb, E_TYPE_ERROR, "can't iterate from Float"); } if (mrb_nil_p(beg)) { mrb_raise(mrb, E_TYPE_ERROR, "can't iterate from nil"); } if (mrb_integer_p(beg) && mrb_nil_p(end)) { return mrb_float_value(mrb, INFINITY); } mrb_bool excl = RANGE_EXCL(r); mrb_float beg_f, end_f; mrb_bool num_p = TRUE; if (mrb_integer_p(beg)) { beg_f = (mrb_float)mrb_integer(beg); } else if (mrb_float_p(beg)) { beg_f = mrb_float(beg); } else { num_p = FALSE; } if (mrb_integer_p(end)) { end_f = (mrb_float)mrb_integer(end); } else if (mrb_float_p(end)) { end_f = mrb_float(end); } else { num_p = FALSE; } if (num_p) { mrb_float n = end_f - beg_f; mrb_float err = (fabs(beg_f) + fabs(end_f) + fabs(end_f-beg_f)) * MRB_FLOAT_EPSILON; if (err>0.5) err=0.5; if (excl) { if (n<=0) return mrb_fixnum_value(0); if (n<1) n = 0; else n = floor(n - err); } else { if (n<0) return mrb_fixnum_value(0); n = floor(n + err); } if (isinf(n+1)) return mrb_float_value(mrb, INFINITY); return mrb_fixnum_value((mrb_int)n+1); } return mrb_nil_value(); } #else static mrb_value range_size(mrb_state *mrb, mrb_value range) { struct RRange *r = mrb_range_ptr(mrb, range); mrb_value beg = RANGE_BEG(r); mrb_value end = RANGE_END(r); if (mrb_nil_p(beg)) { mrb_raise(mrb, E_TYPE_ERROR, "can't iterate from nil"); } if (mrb_integer_p(beg) && mrb_nil_p(end)) { return mrb_nil_value(); } mrb_int excl = RANGE_EXCL(r) ? 0 : 1; if (mrb_integer_p(beg) && mrb_integer_p(end)) { mrb_int a = mrb_integer(beg); mrb_int b = mrb_integer(end); mrb_int c = b - a + excl; return mrb_int_value(mrb, c); } return mrb_nil_value(); } #endif /* MRB_NO_FLOAT */ static mrb_value range_empty_p(mrb_state *mrb, mrb_value range) { mrb_value b, e; mrb_bool excl; mrb_get_args(mrb, "oob", &b, &e, &excl); if (mrb_nil_p(b) || mrb_nil_p(e)) return mrb_false_value(); mrb_int comp = mrb_cmp(mrb, b, e); return mrb_bool_value(comp == -2 || comp > 0 || (comp == 0 && excl)); } void mrb_mruby_range_ext_gem_init(mrb_state* mrb) { struct RClass *s = mrb->range_class; mrb_define_method_id(mrb, s, MRB_SYM_Q(cover), range_cover, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, s, MRB_SYM(size), range_size, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_SYM_Q(__empty_range), range_empty_p, MRB_ARGS_REQ(3)); } void mrb_mruby_range_ext_gem_final(mrb_state* mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-numeric-ext0000644000000000000000000000013215077107334022641 xustar0030 mtime=1761382108.831301159 30 atime=1761382109.798298363 30 ctime=1761382108.831301159 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-numeric-ext/0000755000175100017510000000000015077107334023306 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-numeric-ext/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276025040 xustar0030 mtime=1761382078.122420556 30 atime=1761382080.143411311 30 ctime=1761382108.831301159 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-numeric-ext/mrbgem.rake0000644000175100017510000000024715077107276025433 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-numeric-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Numeric class extension' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-numeric-ext/PaxHeaders/mrblib0000644000000000000000000000013215077107334024110 xustar0030 mtime=1761382108.833301153 30 atime=1761382109.798298363 30 ctime=1761382108.833301153 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-numeric-ext/mrblib/0000755000175100017510000000000015077107334024555 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-numeric-ext/mrblib/PaxHeaders/numeric_ext.rb0000644000000000000000000000013215077107276027041 xustar0030 mtime=1761382078.123420552 30 atime=1761382080.143411311 30 ctime=1761382108.833301153 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb0000644000175100017510000000405215077107276027432 0ustar00runnerrunnerclass Numeric ## # call-seq: # zero? -> true or false # # Returns +true+ if +zero+ has a zero value, +false+ otherwise. # # Of the Core and Standard Library classes, # only Rational and Complex use this implementation. # def zero? self == 0 end ## # call-seq: # nonzero? -> self or nil # # Returns +self+ if +self+ is not a zero value, +nil+ otherwise; # uses method zero? for the evaluation. # def nonzero? if self == 0 nil else self end end ## # call-seq: # positive? -> true or false # # Returns +true+ if +self+ is greater than 0, +false+ otherwise. # def positive? self > 0 end ## # call-seq: # negative? -> true or false # # Returns +true+ if +self+ is less than 0, +false+ otherwise. # def negative? self < 0 end ## # call-seq: # num.integer? -> true or false # # Returns true if num is an Integer. # # 1.0.integer? #=> false # 1.integer? #=> true # def integer? false end end class Integer ## # call-seq: # int.allbits?(mask) -> true or false # # Returns +true+ if all bits of +int+ & +mask+ are 1. # def allbits?(mask) (self & mask) == mask end ## # call-seq: # int.anybits?(mask) -> true or false # # Returns +true+ if any bits of +int+ & +mask+ are 1. # def anybits?(mask) (self & mask) != 0 end ## # call-seq: # int.nobits?(mask) -> true or false # # Returns +true+ if no bits of +int+ & +mask+ are 1. # def nobits?(mask) (self & mask) == 0 end # call-seq: # ceildiv(other) -> integer # # Returns the result of division +self+ by +other+. The # result is rounded up to the nearest integer. # # 3.ceildiv(3) # => 1 # 4.ceildiv(3) # => 2 # # 4.ceildiv(-3) # => -1 # -4.ceildiv(3) # => -1 # -4.ceildiv(-3) # => 2 # # 3.ceildiv(1.2) # => 3 def ceildiv(other) -div(-other) end def integer? true end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-numeric-ext/PaxHeaders/test0000644000000000000000000000013215077107334023620 xustar0030 mtime=1761382108.832301156 30 atime=1761382109.798298363 30 ctime=1761382108.832301156 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-numeric-ext/test/0000755000175100017510000000000015077107334024265 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-numeric-ext/test/PaxHeaders/numeric.rb0000644000000000000000000000013215077107276025671 xustar0030 mtime=1761382078.123420552 30 atime=1761382080.143411311 30 ctime=1761382108.832301156 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-numeric-ext/test/numeric.rb0000644000175100017510000000376315077107276026272 0ustar00runnerrunner## # Numeric(Ext) Test assert('Integer#div') do assert_equal 52, 365.div(7) end assert('Float#div') do skip unless Object.const_defined?(:Float) assert_float 52, 365.2425.div(7) end assert('Integer#zero?') do assert_equal true, 0.zero? assert_equal false, 1.zero? end assert('Integer#nonzero?') do assert_equal nil, 0.nonzero? assert_equal 1000, 1000.nonzero? end assert('Integer#pow') do assert_equal(8, 2.pow(3)) assert_equal(-8, (-2).pow(3)) assert_equal(361, 9.pow(1024,1000)) end assert('Integer#ceildiv') do assert_equal(0, 0.ceildiv(3)) assert_equal(1, 1.ceildiv(3)) assert_equal(1, 3.ceildiv(3)) assert_equal(2, 4.ceildiv(3)) assert_equal(-1, 4.ceildiv(-3)) assert_equal(-1, -4.ceildiv(3)) assert_equal(2, -4.ceildiv(-3)) if Object.const_defined?(:Float) assert_equal(3, 3.ceildiv(1.2)) end if Object.const_defined?(:Rational) assert_equal(3, 3.ceildiv(6/5r)) end # assert_equal(10, (10**100-11).ceildiv(10**99-1)) # assert_equal(11, (10**100-9).ceildiv(10**99-1)) assert_equal(8, 2.pow(3)) assert_equal(-8, (-2).pow(3)) # assert_equal(361, 9.pow(1024,1000)) end assert('Integer#even?') do assert_true(0.even?) assert_true(2.even?) assert_true(-2.even?) assert_false(1.even?) assert_false(-1.even?) # assert_true((10**100).even?) # assert_true((-10**100).even?) # assert_false((10**100+1).even?) # assert_false((-10**100-1).even?) end assert('Integer#odd?') do assert_false(0.odd?) assert_false(2.odd?) assert_false(-2.odd?) assert_true(1.odd?) assert_true(-1.odd?) # assert_false((10**100).odd?) # assert_false((-10**100).odd?) # assert_true((10**100+1).odd?) # assert_true((-10**100-1).odd?) end assert('Integer#digits') do assert_equal([5, 4, 3, 2, 1], 12345.digits) assert_equal([4, 6, 6, 0, 5], 12345.digits(7)) assert_equal([45, 23, 1], 12345.digits(100)) end assert('Integer.sqrt') do assert_equal(4, Integer.sqrt(16)) assert_equal(10, Integer.sqrt(100)) assert_equal(85, Integer.sqrt(7244)) end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-numeric-ext/PaxHeaders/src0000644000000000000000000000013015077107334023426 xustar0029 mtime=1761382108.83430115 30 atime=1761382109.798298363 29 ctime=1761382108.83430115 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-numeric-ext/src/0000755000175100017510000000000015077107334024075 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-numeric-ext/src/PaxHeaders/numeric_ext.c0000644000000000000000000000013115077107276026177 xustar0030 mtime=1761382078.123420552 30 atime=1761382080.143411311 29 ctime=1761382108.83430115 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-numeric-ext/src/numeric_ext.c0000644000175100017510000002213015077107276026566 0ustar00runnerrunner#include #include #include #include #include #ifndef MRB_NO_FLOAT static mrb_value flo_remainder(mrb_state *mrb, mrb_value self); #endif /* * call-seq: * num.remainder(numeric) -> real * * x.remainder(y) means x-y*(x/y).truncate. * * See Numeric#divmod. */ static mrb_value int_remainder(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); mrb_int a, b; #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { if (mrb_integer_p(y) || mrb_bigint_p(y)) { return mrb_bint_rem(mrb, x, y); } return flo_remainder(mrb, mrb_float_value(mrb, mrb_as_float(mrb, x))); } #endif a = mrb_integer(x); if (mrb_integer_p(y)) { b = mrb_integer(y); if (b == 0) mrb_int_zerodiv(mrb); if (a == MRB_INT_MIN && b == -1) return mrb_fixnum_value(0); return mrb_int_value(mrb, a % b); } #ifdef MRB_NO_FLOAT mrb_raise(mrb, E_TYPE_ERROR, "non integer remainder"); #else return flo_remainder(mrb, mrb_float_value(mrb, mrb_as_float(mrb, x))); #endif } mrb_value mrb_int_pow(mrb_state *mrb, mrb_value x, mrb_value y); /* * call-seq: * integer.pow(numeric) -> numeric * integer.pow(integer, integer) -> integer * * Returns (modular) exponentiation as: * * a.pow(b) #=> same as a**b * a.pow(b, m) #=> same as (a**b) % m, but avoids huge temporary values */ static mrb_value int_powm(mrb_state *mrb, mrb_value x) { mrb_value m, e; mrb_int exp, mod, result = 1; if (mrb_get_argc(mrb) == 1) { return mrb_int_pow(mrb, x, mrb_get_arg1(mrb)); } mrb_get_args(mrb, "oo", &e, &m); if (!mrb_integer_p(e) #ifdef MRB_USE_BIGINT && !mrb_bigint_p(e) #endif ) { mrb_raise(mrb, E_TYPE_ERROR, "int.pow(n,m): 2nd argument not allowed unless 1st argument is an integer"); } #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { return mrb_bint_powm(mrb, x, e, m); } if (mrb_bigint_p(e) || mrb_bigint_p(m)) { return mrb_bint_powm(mrb, mrb_bint_new_int(mrb, mrb_integer(x)), e, m); } #endif exp = mrb_integer(e); if (exp < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "int.pow(n,m): n must be positive"); if (!mrb_integer_p(m)) mrb_raise(mrb, E_TYPE_ERROR, "int.pow(n,m): m must be integer"); mod = mrb_integer(m); if (mod < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "int.pow(n,m): m must be positive when 2nd argument specified"); if (mod == 0) mrb_int_zerodiv(mrb); if (mod == 1) return mrb_fixnum_value(0); mrb_int base = mrb_integer(x); for (;;) { mrb_int tmp; if (exp & 1) { if (mrb_int_mul_overflow(result, base, &tmp)) { result %= mod; base %= mod; if (mrb_int_mul_overflow(result, base, &tmp)) { #ifdef MRB_USE_BIGINT return mrb_bint_powm(mrb, mrb_bint_new_int(mrb, mrb_integer(x)), e, m); #else mrb_int_overflow(mrb, "pow"); #endif } } result = tmp % mod; } exp >>= 1; if (exp == 0) break; if (mrb_int_mul_overflow(base, base, &tmp)) { base %= mod; if (mrb_int_mul_overflow(base, base, &tmp)) { #ifdef MRB_USE_BIGINT return mrb_bint_powm(mrb, mrb_bint_new_int(mrb, mrb_integer(x)), e, m); #else mrb_int_overflow(mrb, "pow"); #endif } } base = tmp % mod; } return mrb_int_value(mrb, result); } /* * call-seq: * digits(base = 10) -> array_of_integers * * Returns an array of integers representing the +base+-radix * digits of +self+; * the first element of the array represents the least significant digit: * * 12345.digits # => [5, 4, 3, 2, 1] * 12345.digits(7) # => [4, 6, 6, 0, 5] * 12345.digits(100) # => [45, 23, 1] * * Raises an exception if +self+ is negative or +base+ is less than 2. * */ static mrb_value int_digits(mrb_state *mrb, mrb_value self) { mrb_int base = 10; mrb_get_args(mrb, "|i", &base); if (base < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative radix"); } else if (base < 2) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %i", base); } #ifdef MRB_USE_BIGINT if (mrb_bigint_p(self)) { mrb_value x = self; mrb_value zero = mrb_fixnum_value(0); mrb_value bv = mrb_int_value(mrb, base); if (mrb_bint_cmp(mrb, x, zero) < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "number should be positive"); } mrb_value digits = mrb_ary_new(mrb); while (mrb_bint_cmp(mrb, x, zero) == 0) { mrb_ary_push(mrb, digits, zero); return digits; } while (mrb_bint_cmp(mrb, x, zero) > 0) { mrb_ary_push(mrb, digits, mrb_bint_mod(mrb, x, bv)); x = mrb_bint_div(mrb, x, bv); if (!mrb_bigint_p(x)) { mrb_int n = mrb_integer(x); while (n > 0) { mrb_int q = n % base; mrb_ary_push(mrb, digits, mrb_int_value(mrb, q)); n /= base; } break; } } return digits; } #endif mrb_int n = mrb_integer(self); if (n < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "number should be positive"); } mrb_value digits = mrb_ary_new(mrb); if (n == 0) { mrb_ary_push(mrb, digits, mrb_fixnum_value(0)); return digits; } while (n > 0) { mrb_int q = n % base; mrb_ary_push(mrb, digits, mrb_int_value(mrb, q)); n /= base; } return digits; } /* * call-seq: * int.size -> int * * Returns the number of bytes in the machine representation of int * (machine dependent). * * 1.size #=> 8 * -1.size #=> 8 * 2147483647.size #=> 8 * (256**10 - 1).size #=> 12 * (256**20 - 1).size #=> 20 * (256**40 - 1).size #=> 40 */ static mrb_value int_size(mrb_state *mrb, mrb_value self) { size_t size = sizeof(mrb_int); #ifdef MRB_USE_BIGINT if (mrb_bigint_p(self)) { size = mrb_bint_memsize(self); } #endif return mrb_fixnum_value((mrb_int)size); } static mrb_value int_even(mrb_state *mrb, mrb_value self) { #ifdef MRB_USE_BIGINT if (mrb_bigint_p(self)) { mrb_value and1 = mrb_bint_and(mrb, self, mrb_fixnum_value(1)); if (mrb_integer(and1) == 0) return mrb_true_value(); return mrb_false_value(); } #endif return mrb_bool_value(mrb_integer(self) % 2 == 0); } static mrb_value int_odd(mrb_state *mrb, mrb_value self) { mrb_value even = int_even(mrb, self); mrb_bool odd = !mrb_test(even); return mrb_bool_value(odd); } #ifndef MRB_NO_FLOAT static mrb_value flo_remainder(mrb_state *mrb, mrb_value self) { mrb_float a, b; a = mrb_float(self); mrb_get_args(mrb, "f", &b); if (b == 0) mrb_int_zerodiv(mrb); if (isinf(b)) return mrb_float_value(mrb, a); return mrb_float_value(mrb, a-b*trunc(a/b)); } #endif static mrb_int isqrt(mrb_int n) { mrb_assert(n >= 0); if (n < 2) return n; mrb_int x = n; mrb_int y = (x + 1) / 2; // Babylonian method (integer version) while (y < x) { x = y; y = (x + n / x) / 2; } return x; } static mrb_value int_sqrt(mrb_state *mrb, mrb_value self) { mrb_value arg = mrb_get_arg1(mrb); if (mrb_integer_p(arg)) { mrb_int n = mrb_integer(arg); if (n < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "non-negative integer required"); } return mrb_int_value(mrb, isqrt(n)); } #ifdef MRB_USE_BIGINT else if (mrb_bigint_p(arg)) { return mrb_bint_sqrt(mrb, arg); } #endif else { mrb_raise(mrb, E_TYPE_ERROR, "expected Integer"); } } void mrb_mruby_numeric_ext_gem_init(mrb_state* mrb) { struct RClass *ic = mrb->integer_class; mrb_define_alias_id(mrb, ic, MRB_SYM(modulo), MRB_OPSYM(mod)); mrb_define_method_id(mrb, ic, MRB_SYM(remainder), int_remainder, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, ic, MRB_SYM(pow), int_powm, MRB_ARGS_ARG(1,1)); mrb_define_method_id(mrb, ic, MRB_SYM(digits), int_digits, MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, ic, MRB_SYM(size), int_size, MRB_ARGS_NONE()); mrb_define_method_id(mrb, ic, MRB_SYM_Q(odd), int_odd, MRB_ARGS_NONE()); mrb_define_method_id(mrb, ic, MRB_SYM_Q(even), int_even, MRB_ARGS_NONE()); mrb_define_class_method_id(mrb, ic, MRB_SYM(sqrt), int_sqrt, MRB_ARGS_REQ(1)); #ifndef MRB_NO_FLOAT struct RClass *fc = mrb->float_class; mrb_define_alias_id(mrb, fc, MRB_SYM(modulo), MRB_OPSYM(mod)); mrb_define_method_id(mrb, fc, MRB_SYM(remainder), flo_remainder, MRB_ARGS_REQ(1)); mrb_define_const_id(mrb, fc, MRB_SYM(RADIX), mrb_fixnum_value(MRB_FLT_RADIX)); mrb_define_const_id(mrb, fc, MRB_SYM(MANT_DIG), mrb_fixnum_value(MRB_FLT_MANT_DIG)); mrb_define_const_id(mrb, fc, MRB_SYM(EPSILON), mrb_float_value(mrb, MRB_FLT_EPSILON)); mrb_define_const_id(mrb, fc, MRB_SYM(DIG), mrb_fixnum_value(MRB_FLT_DIG)); mrb_define_const_id(mrb, fc, MRB_SYM(MIN_EXP), mrb_fixnum_value(MRB_FLT_MIN_EXP)); mrb_define_const_id(mrb, fc, MRB_SYM(MIN), mrb_float_value(mrb, MRB_FLT_MIN)); mrb_define_const_id(mrb, fc, MRB_SYM(MIN_10_EXP), mrb_fixnum_value(MRB_FLT_MIN_10_EXP)); mrb_define_const_id(mrb, fc, MRB_SYM(MAX_EXP), mrb_fixnum_value(MRB_FLT_MAX_EXP)); mrb_define_const_id(mrb, fc, MRB_SYM(MAX), mrb_float_value(mrb, MRB_FLT_MAX)); mrb_define_const_id(mrb, fc, MRB_SYM(MAX_10_EXP), mrb_fixnum_value(MRB_FLT_MAX_10_EXP)); #endif /* MRB_NO_FLOAT */ } void mrb_mruby_numeric_ext_gem_final(mrb_state* mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-test0000644000000000000000000000013215077107334021360 xustar0030 mtime=1761382108.863301066 30 atime=1761382109.798298363 30 ctime=1761382108.863301066 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-test/0000755000175100017510000000000015077107334022025 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-test/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276023557 xustar0030 mtime=1761382078.129420524 30 atime=1761382080.148411288 30 ctime=1761382108.859301078 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-test/mrbgem.rake0000644000175100017510000001642215077107276024154 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-test') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'mruby test' spec.test_rbfiles = Dir.glob("#{MRUBY_ROOT}/test/t/*.rb") clib = "#{build_dir}/mrbtest.c" mlib = clib.ext(exts.object) exec = exefile("#{build.build_dir}/bin/mrbtest") assert_c = "#{build_dir}/assert.c" assert_rb = "#{MRUBY_ROOT}/test/assert.rb" assert_lib = assert_c.ext(exts.object) mrbtest_lib = libfile("#{build_dir}/mrbtest") mrbtest_objs = [assert_lib] driver_objs = srcs_to_objs(".") file assert_lib => assert_c file assert_c => [assert_rb, build.mrbcfile] do |t| _pp "GEN", t.name.relative_path mkdir_p File.dirname(t.name) open(t.name, 'w') do |f| mrbc.run f, assert_rb, 'mrbtest_assert_irep', cdump: false end end gem_table = build.gems.generate_gem_table build build.gems.each do |g| test_rbobj = g.test_rbireps.ext(exts.object) mrbtest_objs.concat(g.test_objs) mrbtest_objs << test_rbobj dep_list = build.gems.tsort_dependencies([g.name], gem_table).select(&:generate_functions) file test_rbobj => g.test_rbireps file g.test_rbireps => [g.test_rbfiles, build.mrbcfile].flatten do |t| _pp "GEN", t.name.relative_path mkdir_p File.dirname(t.name) open(t.name, 'w') do |f| g.print_gem_test_header(f) test_preload = g.test_preload and [g.dir, MRUBY_ROOT].map {|dir| File.expand_path(g.test_preload, dir) }.find {|file| File.exist?(file) } f.puts %Q[/*] f.puts %Q[ * This file contains a test code for #{g.name} gem.] f.puts %Q[ *] f.puts %Q[ * IMPORTANT:] f.puts %Q[ * This file was generated!] f.puts %Q[ * All manual changes will get lost.] f.puts %Q[ */] if test_preload.nil? f.puts %Q[extern const uint8_t mrbtest_assert_irep[];] else g.build.mrbc.run f, test_preload, "gem_test_irep_#{g.funcname}_preload", cdump: false end g.test_rbfiles.flatten.each_with_index do |rbfile, i| g.build.mrbc.run f, rbfile, "gem_test_irep_#{g.funcname}_#{i}", cdump: false, static: true end f.puts %Q[void mrb_#{g.funcname}_gem_test(mrb_state *mrb);] if g.custom_test_init? dep_list.each do |d| f.puts %Q[void GENERATED_TMP_mrb_#{d.funcname}_gem_init(mrb_state *mrb);] f.puts %Q[void GENERATED_TMP_mrb_#{d.funcname}_gem_final(mrb_state *mrb);] end f.puts %Q[void mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose);] f.puts %Q[void mrb_t_pass_result(mrb_state *dst, mrb_state *src);] f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb) {] unless g.test_rbfiles.empty? unless g.test_args.empty? f.puts %Q[ mrb_value test_args_hash;] end f.puts %Q[ mrb_state *mrb2 = mrb_open_core();] f.puts %Q[ if (mrb2 == NULL) {] f.puts %Q[ fprintf(stderr, "Invalid mrb_state, exiting \%s", __func__);] f.puts %Q[ exit(EXIT_FAILURE);] f.puts %Q[ }] f.puts %Q[ int ai = mrb_gc_arena_save(mrb2);] f.puts %Q[ mrb_const_set(mrb2, mrb_obj_value(mrb2->object_class), mrb_intern_lit(mrb2, "GEMNAME"), mrb_str_new(mrb2, "#{g.name}", #{g.name.length}));] f.puts %Q[ mrb_gc_arena_restore(mrb2, ai);] if test_preload.nil? f.puts %Q[ mrb_load_irep(mrb2, mrbtest_assert_irep);] else f.puts %Q[ mrb_load_irep(mrb2, gem_test_irep_#{g.funcname}_preload);] end dep_list.each do |d| f.puts %Q[ GENERATED_TMP_mrb_#{d.funcname}_gem_init(mrb2);] f.puts %Q[ mrb_state_atexit(mrb2, GENERATED_TMP_mrb_#{d.funcname}_gem_final);] f.puts %Q[ mrb_gc_arena_restore(mrb2, ai);] end f.puts %Q[ mrb_init_test_driver(mrb2, mrb_test(mrb_gv_get(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"))));] f.puts %Q[ mrb_gc_arena_restore(mrb2, ai);] f.puts %Q[ ] g.test_rbfiles.count.times do |i| unless g.test_args.empty? f.puts %Q[ test_args_hash = mrb_hash_new_capa(mrb2, #{g.test_args.length}); ] g.test_args.each do |arg_name, arg_value| escaped_arg_name = arg_name.gsub('\\', '\\\\\\\\').gsub('"', '\"') escaped_arg_value = arg_value.gsub('\\', '\\\\\\\\').gsub('"', '\"') f.puts %Q[ mrb_hash_set(mrb2, test_args_hash, mrb_str_new(mrb2, "#{escaped_arg_name.to_s}", #{escaped_arg_name.to_s.length}), mrb_str_new(mrb2, "#{escaped_arg_value.to_s}", #{escaped_arg_value.to_s.length})); ] end f.puts %Q[ mrb_const_set(mrb2, mrb_obj_value(mrb2->object_class), mrb_intern_lit(mrb2, "TEST_ARGS"), test_args_hash); ] end f.puts %Q[ mrb_gc_arena_restore(mrb2, ai);] f.puts %Q[ mrb_#{g.funcname}_gem_test(mrb2);] if g.custom_test_init? f.puts %Q[ mrb_gc_arena_restore(mrb2, ai);] f.puts %Q[ mrb_load_irep(mrb2, gem_test_irep_#{g.funcname}_#{i});] f.puts %Q[ if (mrb2->exc) {] f.puts %Q[ mrb_print_error(mrb2);] f.puts %Q[ mrb_close(mrb2);] f.puts %Q[ exit(EXIT_FAILURE);] f.puts %Q[ }] f.puts %Q[ ] end f.puts %Q[ mrb_t_pass_result(mrb, mrb2);] f.puts %Q[ mrb_close(mrb2);] end f.puts %Q[}] end end end file mrbtest_lib => mrbtest_objs do |t| build.archiver.run t.name, t.prerequisites end unless build.build_mrbtest_lib_only? file exec => [*driver_objs, mlib, mrbtest_lib, build.libmruby_static] do |t| build.linker.run t.name, t.prerequisites, *build.gems.linker_attrs end end file mlib => clib file clib => ["#{build.build_dir}/mrbgems/active_gems.txt", build.mrbcfile, __FILE__] do |_t| _pp "GEN", clib.relative_path mkdir_p File.dirname(clib) open(clib, 'w') do |f| f.puts %Q[/*] f.puts %Q[ * This file contains a list of all] f.puts %Q[ * test functions.] f.puts %Q[ *] f.puts %Q[ * IMPORTANT:] f.puts %Q[ * This file was generated!] f.puts %Q[ * All manual changes will get lost.] f.puts %Q[ */] f.puts %Q[] f.puts %Q[#include ] f.puts %Q[#include ] f.puts %Q[#include ] f.puts %Q[] build.gems.each do |g| f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb);] end f.puts %Q[void mrbgemtest_init(mrb_state* mrb) {] f.puts %Q[ int ai = mrb_gc_arena_save(mrb);] build.gems.each do |g| if g.skip_test? f.puts %Q[ do {] f.puts %Q[ mrb_value asserts = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$asserts"));] f.puts %Q[ mrb_ary_push(mrb, asserts, mrb_str_new_lit(mrb, ] f.puts %Q[ "Warn: Skipping tests for gem (#{ g.name == 'mruby-test' ? 'core' : "mrbgems: #{g.name}" })"));] f.puts %Q[ } while (0);] else f.puts %Q[ GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb);] end f.puts %Q[ mrb_gc_arena_restore(mrb, ai);] end f.puts %Q[}] end end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-test/PaxHeaders/vformat.c0000644000000000000000000000013215077107276023264 xustar0030 mtime=1761382078.129420524 30 atime=1761382080.148411288 30 ctime=1761382108.860301075 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-test/vformat.c0000644000175100017510000000672615077107276023667 0ustar00runnerrunner#include #include #include #include #include /* no argument */ static mrb_value vf_s_format_0(mrb_state *mrb, mrb_value klass) { mrb_value fmt_str; mrb_get_args(mrb, "S", &fmt_str); const char *fmt = RSTRING_CSTR(mrb, fmt_str); return mrb_format(mrb, fmt); } /* c char */ static mrb_value vf_s_format_c(mrb_state *mrb, mrb_value klass) { mrb_value fmt_str, arg_str; mrb_get_args(mrb, "SS", &fmt_str, &arg_str); const char *fmt = RSTRING_CSTR(mrb, fmt_str); char c = RSTRING_CSTR(mrb, arg_str)[0]; return mrb_format(mrb, fmt, c); } /* d int */ static mrb_value vf_s_format_d(mrb_state *mrb, mrb_value klass) { mrb_value fmt_str; mrb_int i; mrb_get_args(mrb, "Si", &fmt_str, &i); const char *fmt = RSTRING_CSTR(mrb, fmt_str); int d = (int)i; return mrb_format(mrb, fmt, d); } #ifndef MRB_NO_FLOAT /* f float */ static mrb_value vf_s_format_f(mrb_state *mrb, mrb_value klass) { mrb_value fmt_str; mrb_float f; mrb_get_args(mrb, "Sf", &fmt_str, &f); const char *fmt = RSTRING_CSTR(mrb, fmt_str); return mrb_format(mrb, fmt, f); } #endif /* i fixnum */ static mrb_value vf_s_format_i(mrb_state *mrb, mrb_value klass) { mrb_value fmt_str; mrb_int i; mrb_get_args(mrb, "Si", &fmt_str, &i); const char *fmt = RSTRING_CSTR(mrb, fmt_str); return mrb_format(mrb, fmt, i); } /* l char*, size_t */ static mrb_value vf_s_format_l(mrb_state *mrb, mrb_value klass) { mrb_value fmt_str, arg_str; mrb_int i; mrb_get_args(mrb, "SSi", &fmt_str, &arg_str, &i); const char *fmt = RSTRING_CSTR(mrb, fmt_str); const char *s = RSTRING_PTR(arg_str); size_t len = (size_t)i; if (len > (size_t)RSTRING_LEN(arg_str)) len = (size_t)RSTRING_LEN(arg_str); return mrb_format(mrb, fmt, s, len); } /* n symbol */ static mrb_value vf_s_format_n(mrb_state *mrb, mrb_value klass) { mrb_value fmt_str; mrb_sym sym; mrb_get_args(mrb, "Sn", &fmt_str, &sym); const char *fmt = RSTRING_CSTR(mrb, fmt_str); return mrb_format(mrb, fmt, sym); } /* s char* */ static mrb_value vf_s_format_s(mrb_state *mrb, mrb_value klass) { mrb_value fmt_str, arg_str; mrb_get_args(mrb, "SS", &fmt_str, &arg_str); const char *fmt = RSTRING_CSTR(mrb, fmt_str); const char *s = RSTRING_CSTR(mrb, arg_str); return mrb_format(mrb, fmt, s); } /* C RClass */ static mrb_value vf_s_format_C(mrb_state *mrb, mrb_value klass) { mrb_value fmt_str, arg_cls; mrb_get_args(mrb, "SC", &fmt_str, &arg_cls); const char *fmt = RSTRING_CSTR(mrb, fmt_str); struct RClass *c = mrb_class_ptr(arg_cls); return mrb_format(mrb, fmt, c); } /* v value */ static mrb_value vf_s_format_v(mrb_state *mrb, mrb_value klass) { mrb_value fmt_str, arg_v; mrb_get_args(mrb, "So", &fmt_str, &arg_v); const char *fmt = RSTRING_CSTR(mrb, fmt_str); return mrb_format(mrb, fmt, arg_v); } void mrb_init_test_vformat(mrb_state *mrb) { struct RClass *vf = mrb_define_module(mrb, "TestVFormat"); mrb_define_class_method(mrb, vf, "z", vf_s_format_0, MRB_ARGS_REQ(1)); #define VF_DEFINE_FORMAT_METHOD(t) VF_DEFINE_FORMAT_METHOD_n(t,2) #define VF_DEFINE_FORMAT_METHOD_n(t,n) mrb_define_class_method(mrb, vf, #t, vf_s_format_##t, MRB_ARGS_REQ(n)); VF_DEFINE_FORMAT_METHOD(c); VF_DEFINE_FORMAT_METHOD(d); #ifndef MRB_NO_FLOAT VF_DEFINE_FORMAT_METHOD(f); #endif VF_DEFINE_FORMAT_METHOD(i); VF_DEFINE_FORMAT_METHOD_n(l,3); VF_DEFINE_FORMAT_METHOD(n); VF_DEFINE_FORMAT_METHOD(s); VF_DEFINE_FORMAT_METHOD(C); VF_DEFINE_FORMAT_METHOD(v); } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-test/PaxHeaders/README.md0000644000000000000000000000013215077107276022721 xustar0030 mtime=1761382078.129420524 30 atime=1761382080.148411288 30 ctime=1761382108.861301072 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-test/README.md0000644000175100017510000000016115077107276023307 0ustar00runnerrunner# Running Tests To run the tests, execute the following from the project's root directory. ``` $ make test ``` nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-test/PaxHeaders/driver.c0000644000000000000000000000013215077107276023101 xustar0030 mtime=1761382078.129420524 30 atime=1761382080.148411288 30 ctime=1761382108.863301066 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-test/driver.c0000644000175100017510000002017415077107276023475 0ustar00runnerrunner/* ** mrbtest - Test for Embeddable Ruby ** ** This program runs Ruby test programs in test/t directory ** against the current mruby implementation. */ #include #include #include #include #include #include #include #include #include #include extern const uint8_t mrbtest_assert_irep[]; void mrbgemtest_init(mrb_state* mrb); void mrb_init_test_vformat(mrb_state* mrb); /* Print a short remark for the user */ static void print_hint(void) { printf("mrbtest - Embeddable Ruby Test\n\n"); } static int eval_test(mrb_state *mrb) { /* evaluate the test */ mrb_value result = mrb_funcall(mrb, mrb_top_self(mrb), "report", 0); /* did an exception occur? */ if (mrb->exc) { mrb_print_error(mrb); mrb->exc = 0; return EXIT_FAILURE; } else { return mrb_bool(result) ? EXIT_SUCCESS : EXIT_FAILURE; } } /* Implementation of print due to the reason that there might be no print */ static mrb_value t_print(mrb_state *mrb, mrb_value self) { const mrb_value *argv; mrb_int argc; mrb_get_args(mrb, "*!", &argv, &argc); int ai = mrb_gc_arena_save(mrb); for (mrb_int i = 0; i < argc; i++) { mrb_value s = mrb_obj_as_string(mrb, argv[i]); fwrite(RSTRING_PTR(s), RSTRING_LEN(s), 1, stdout); mrb_gc_arena_restore(mrb, ai); } fflush(stdout); return mrb_nil_value(); } #define UNESCAPE(p, endp) ((p) != (endp) && *(p) == '\\' ? (p)+1 : (p)) #define CHAR_CMP(c1, c2) ((unsigned char)(c1) - (unsigned char)(c2)) static const char * str_match_bracket(const char *p, const char *pat_end, const char *s, const char *str_end) { mrb_bool ok = FALSE, negated = FALSE; if (p == pat_end) return NULL; if (*p == '!' || *p == '^') { negated = TRUE; p++; } while (*p != ']') { const char *t1 = p; if ((t1 = UNESCAPE(t1, pat_end)) == pat_end) return NULL; if ((p = t1 + 1) == pat_end) return NULL; if (p[0] == '-' && p[1] != ']') { const char *t2 = p + 1; if ((t2 = UNESCAPE(t2, pat_end)) == pat_end) return NULL; p = t2 + 1; if (!ok && CHAR_CMP(*t1, *s) <= 0 && CHAR_CMP(*s, *t2) <= 0) ok = TRUE; } else { if (!ok && CHAR_CMP(*t1, *s) == 0) ok = TRUE; } } return ok == negated ? NULL : p + 1; } static mrb_bool str_match_no_brace_p(const char *pat, mrb_int pat_len, const char *str, mrb_int str_len) { const char *p = pat, *s = str; const char *pat_end = pat + pat_len, *str_end = str + str_len; const char *p_tmp = NULL, *s_tmp = NULL; for (;;) { if (p == pat_end) return s == str_end; switch (*p) { case '*': do { p++; } while (p != pat_end && *p == '*'); if (UNESCAPE(p, pat_end) == pat_end) return TRUE; if (s == str_end) return FALSE; p_tmp = p; s_tmp = s; continue; case '?': if (s == str_end) return FALSE; p++; s++; continue; case '[': { const char *t; if (s == str_end) return FALSE; if ((t = str_match_bracket(p+1, pat_end, s, str_end))) { p = t; s++; continue; } goto L_failed; } } /* ordinary */ p = UNESCAPE(p, pat_end); if (s == str_end) return p == pat_end; if (p == pat_end) goto L_failed; if (*p++ != *s++) goto L_failed; continue; L_failed: if (p_tmp && s_tmp) { /* try next '*' position */ p = p_tmp; s = ++s_tmp; continue; } return FALSE; } } #define COPY_AND_INC(dst, src, len) \ do { memcpy(dst, src, len); dst += len; } while (0) static mrb_bool str_match_p(mrb_state *mrb, const char *pat, mrb_int pat_len, const char *str, mrb_int str_len) { const char *p = pat, *pat_end = pat + pat_len; const char *lbrace = NULL, *rbrace = NULL; int nest = 0; mrb_bool ret = FALSE; for (; p != pat_end; p++) { if (*p == '{' && nest++ == 0) lbrace = p; else if (*p == '}' && lbrace && --nest == 0) { rbrace = p; break; } else if (*p == '\\' && ++p == pat_end) break; } if (lbrace && rbrace) { /* expand brace */ char *ex_pat = (char*)mrb_malloc(mrb, pat_len-2); /* expanded pattern */ char *ex_p = ex_pat; COPY_AND_INC(ex_p, pat, lbrace-pat); p = lbrace; while (p < rbrace) { char *orig_ex_p = ex_p; const char *t = ++p; for (nest = 0; p < rbrace && !(*p == ',' && nest == 0); p++) { if (*p == '{') nest++; else if (*p == '}') nest--; else if (*p == '\\' && ++p == rbrace) break; } COPY_AND_INC(ex_p, t, p-t); COPY_AND_INC(ex_p, rbrace+1, pat_end-rbrace-1); if ((ret = str_match_p(mrb, ex_pat, ex_p-ex_pat, str, str_len))) break; ex_p = orig_ex_p; } mrb_free(mrb, ex_pat); } else if (!lbrace && !rbrace) { ret = str_match_no_brace_p(pat, pat_len, str, str_len); } return ret; } static mrb_value m_str_match_p(mrb_state *mrb, mrb_value self) { const char *pat, *str; mrb_int pat_len, str_len; mrb_get_args(mrb, "ss", &pat, &pat_len, &str, &str_len); return mrb_bool_value(str_match_p(mrb, pat, pat_len, str, str_len)); } void mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose) { struct RClass *krn = mrb->kernel_module; mrb_define_method(mrb, krn, "t_print", t_print, MRB_ARGS_ANY()); mrb_define_method(mrb, krn, "_str_match?", m_str_match_p, MRB_ARGS_REQ(2)); struct RClass *mrbtest = mrb_define_module(mrb, "Mrbtest"); #ifndef MRB_NO_FLOAT #ifdef MRB_USE_FLOAT32 #ifdef MRB_WORDBOX_NO_FLOAT_TRUNCATE mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-5)); #else mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-4)); #endif #else mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-10)); #endif #else (void)mrbtest; #endif mrb_init_test_vformat(mrb); if (verbose) { mrb_gv_set(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"), mrb_true_value()); } } void mrb_t_pass_result(mrb_state *mrb_dst, mrb_state *mrb_src) { if (mrb_src->exc) { mrb_print_error(mrb_src); exit(EXIT_FAILURE); } #define TEST_COUNT_PASS(name) \ do { \ mrb_value res_src = mrb_gv_get(mrb_src, mrb_intern_lit(mrb_src, "$" #name)); \ if (mrb_integer_p(res_src)) { \ mrb_value res_dst = mrb_gv_get(mrb_dst, mrb_intern_lit(mrb_dst, "$" #name)); \ mrb_gv_set(mrb_dst, mrb_intern_lit(mrb_dst, "$" #name), mrb_int_value(mrb_dst, mrb_integer(res_dst) + mrb_integer(res_src))); \ } \ } while (FALSE) \ TEST_COUNT_PASS(ok_test); TEST_COUNT_PASS(ko_test); TEST_COUNT_PASS(kill_test); TEST_COUNT_PASS(warning_test); TEST_COUNT_PASS(skip_test); #undef TEST_COUNT_PASS mrb_value res_src = mrb_gv_get(mrb_src, mrb_intern_lit(mrb_src, "$asserts")); if (mrb_array_p(res_src)) { mrb_int i; mrb_value res_dst = mrb_gv_get(mrb_dst, mrb_intern_lit(mrb_dst, "$asserts")); int ai = mrb_gc_arena_save(mrb_dst); for (i = 0; i < RARRAY_LEN(res_src); i++) { mrb_value val_src = RARRAY_PTR(res_src)[i]; mrb_ensure_string_type(mrb_dst, val_src); mrb_ary_push(mrb_dst, res_dst, mrb_str_new(mrb_dst, RSTRING_PTR(val_src), RSTRING_LEN(val_src))); mrb_gc_arena_restore(mrb_dst, ai); } } } int main(int argc, char **argv) { mrb_state *mrb; mrb_bool verbose = FALSE; print_hint(); /* new interpreter instance */ mrb = mrb_open(); if (mrb == NULL) { fputs("Invalid mrb_state, exiting test driver", stderr); return EXIT_FAILURE; } if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'v') { printf("verbose mode: enable\n\n"); verbose = TRUE; } int ai = mrb_gc_arena_save(mrb); mrb_init_test_driver(mrb, verbose); mrb_gc_arena_restore(mrb, ai); mrb_load_irep(mrb, mrbtest_assert_irep); mrb_gc_arena_restore(mrb, ai); mrbgemtest_init(mrb); int ret = eval_test(mrb); mrb_close(mrb); return ret; } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/default-no-fpu.gembox0000644000000000000000000000013215077107276023360 xustar0030 mtime=1761382078.104420639 30 atime=1761382080.125411393 30 ctime=1761382108.937300852 nghttp2-1.68.0/third-party/mruby/mrbgems/default-no-fpu.gembox0000644000175100017510000000006715077107276023753 0ustar00runnerrunnerMRuby::GemBox.new do |conf| conf.gembox "stdlib" end nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/metaprog.gembox0000644000000000000000000000013215077107276022350 xustar0030 mtime=1761382078.104420639 30 atime=1761382080.125411393 30 ctime=1761382108.795301263 nghttp2-1.68.0/third-party/mruby/mrbgems/metaprog.gembox0000644000175100017510000000073415077107276022744 0ustar00runnerrunner# It also works with MRB_NO_STDIO and MRB_NO_FLOAT. MRuby::GemBox.new do |conf| # Meta-programming features conf.gem :core => "mruby-metaprog" # Use Method/UnboundMethod class conf.gem :core => "mruby-method" # Use eval() conf.gem :core => "mruby-eval" # Use binding() conf.gem :core => "mruby-binding" # Use Proc#binding() conf.gem :core => "mruby-proc-binding" # Use mruby-compiler to build other mrbgems conf.gem :core => "mruby-compiler" end nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-math0000644000000000000000000000013015077107334021330 xustar0029 mtime=1761382108.91730091 30 atime=1761382109.798298363 29 ctime=1761382108.91730091 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-math/0000755000175100017510000000000015077107334021777 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-math/PaxHeaders/mrbgem.rake0000644000000000000000000000013015077107276023527 xustar0030 mtime=1761382078.121420561 29 atime=1761382080.14141132 29 ctime=1761382108.91730091 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-math/mrbgem.rake0000644000175100017510000000023515077107276024121 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-math') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'standard Math module' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-math/PaxHeaders/test0000644000000000000000000000013215077107334022311 xustar0030 mtime=1761382108.918300907 30 atime=1761382109.798298363 30 ctime=1761382108.918300907 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-math/test/0000755000175100017510000000000015077107334022756 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-math/test/PaxHeaders/math.rb0000644000000000000000000000013215077107276023651 xustar0030 mtime=1761382078.121420561 30 atime=1761382080.142411316 30 ctime=1761382108.918300907 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-math/test/math.rb0000644000175100017510000001346015077107276024245 0ustar00runnerrunner## # Math Test def assert_float_and_int(exp_ary, act_ary) assert('assert_float_and_int') do flo_exp, int_exp, flo_act, int_act = *exp_ary, *act_ary assert_float(flo_exp, flo_act) assert_operator(int_exp, :eql?, int_act) end end assert('Math.sin') do assert_float(0, Math.sin(0)) assert_float(1, Math.sin(Math::PI / 2)) end assert('Math.cos') do assert_float(1, Math.cos(0)) assert_float(0, Math.cos(Math::PI / 2)) end assert('Math.tan') do assert_float(0, Math.tan(0)) assert_float(1, Math.tan(Math::PI / 4)) end assert('Fundamental trig identities') do N = 13 N.times do |i| a = Math::PI / N * i ca = Math::PI / 2 - a s = Math.sin(a) c = Math.cos(a) t = Math.tan(a) assert_float(Math.cos(ca), s) assert_float(1 / Math.tan(ca), t) assert_float(1, s ** 2 + c ** 2) assert_float((1/c) ** 2, t ** 2 + 1) assert_float((1/s) ** 2, (1/t) ** 2 + 1) end end assert('Math.exp') do assert_float(1.0, Math.exp(0)) assert_float(2.718281828459045, Math.exp(1)) assert_float(4.4816890703380645, Math.exp(1.5)) end assert('Math.log') do assert_float(0, Math.log(1)) assert_float(1.0, Math.log(Math::E)) assert_float(3.0, Math.log(Math::E**3)) end assert('Math.log2') do assert_float(0.0, Math.log2(1)) assert_float(1.0, Math.log2(2)) end assert('Math.log10') do assert_float(0.0, Math.log10(1)) assert_float(1.0, Math.log10(10)) assert_float(30.0, Math.log10(10.0**30)) end assert('Math.sqrt') do num = [0.0, 1.0, 2.0, 3.0, 4.0] sqr = [0, 1, 4, 9, 16] sqr.each_with_index do |v,i| assert_float(num[i], Math.sqrt(v)) end end assert('Math.cbrt') do num = [-2.0, -1.0, 0.0, 1.0, 2.0] cub = [-8, -1, 0, 1, 8] cub.each_with_index do |v,i| assert_float(num[i], Math.cbrt(v)) end end assert('Math.hypot') do assert_float(5.0, Math.hypot(3, 4)) end assert('Math.erf') do assert_float(0, Math.erf(0)) assert_float(0.842700792949715, Math.erf(1)) assert_float(-0.8427007929497148, Math.erf(-1)) end assert('Math.erfc') do assert_float(0.157299207050285, Math.erfc(1)) assert_float(1.8427007929497148, Math.erfc(-1)) end assert('Math.acos') do assert_float(0 * Math::PI / 4, Math.acos( 1.0)) assert_float(1 * Math::PI / 4, Math.acos( 1.0 / Math.sqrt(2))) assert_float(2 * Math::PI / 4, Math.acos( 0.0)) assert_float(4 * Math::PI / 4, Math.acos(-1.0)) assert_raise(Math::DomainError) { Math.acos(+1.1) } assert_raise(Math::DomainError) { Math.acos(-1.1) } end assert('Math.asin') do assert_float( 0 * Math::PI / 4, Math.asin( 0.0)) assert_float( 1 * Math::PI / 4, Math.asin( 1.0 / Math.sqrt(2))) assert_float( 2 * Math::PI / 4, Math.asin( 1.0)) assert_float(-2 * Math::PI / 4, Math.asin(-1.0)) assert_raise(Math::DomainError) { Math.asin(+1.1) } assert_raise(Math::DomainError) { Math.asin(-1.1) } assert_raise(Math::DomainError) { Math.asin(2.0) } end assert('Math.atan') do assert_float( 0 * Math::PI / 4, Math.atan( 0.0)) assert_float( 1 * Math::PI / 4, Math.atan( 1.0)) assert_float( 2 * Math::PI / 4, Math.atan(1.0 / 0.0)) assert_float(-1 * Math::PI / 4, Math.atan(-1.0)) end assert('Math.cosh') do assert_float(1, Math.cosh(0)) assert_float((Math::E ** 1 + Math::E ** -1) / 2, Math.cosh(1)) assert_float((Math::E ** 2 + Math::E ** -2) / 2, Math.cosh(2)) end assert('Math.sinh') do assert_float(0, Math.sinh(0)) assert_float((Math::E ** 1 - Math::E ** -1) / 2, Math.sinh(1)) assert_float((Math::E ** 2 - Math::E ** -2) / 2, Math.sinh(2)) end assert('Math.tanh') do assert_float(Math.sinh(0) / Math.cosh(0), Math.tanh(0)) assert_float(Math.sinh(1) / Math.cosh(1), Math.tanh(1)) assert_float(Math.sinh(2) / Math.cosh(2), Math.tanh(2)) assert_float(+1.0, Math.tanh(+1000.0)) assert_float(-1.0, Math.tanh(-1000.0)) end assert('Math.acosh') do assert_float(0, Math.acosh(1)) assert_float(1, Math.acosh((Math::E ** 1 + Math::E ** -1) / 2)) assert_float(2, Math.acosh((Math::E ** 2 + Math::E ** -2) / 2)) assert_raise(Math::DomainError) { Math.acosh(0.9) } assert_raise(Math::DomainError) { Math.acosh(0) } end assert('Math.asinh') do assert_float(0, Math.asinh(0)) assert_float(1, Math.asinh((Math::E ** 1 - Math::E ** -1) / 2)) assert_float(2, Math.asinh((Math::E ** 2 - Math::E ** -2) / 2)) end assert('Math.atanh') do assert_float(0, Math.atanh(Math.sinh(0) / Math.cosh(0))) assert_float(1, Math.atanh(Math.sinh(1) / Math.cosh(1))) assert_float(2, Math.atanh(Math.sinh(2) / Math.cosh(2))) assert_float(Float::INFINITY, Math.atanh(1)) assert_float(-Float::INFINITY, Math.atanh(-1)) assert_raise(Math::DomainError) { Math.atanh(+1.1) } assert_raise(Math::DomainError) { Math.atanh(-1.1) } end assert('Math.atan2') do assert_float(+0.0, Math.atan2(+0.0, +0.0)) assert_float(-0.0, Math.atan2(-0.0, +0.0)) assert_float(+Math::PI, Math.atan2(+0.0, -0.0)) assert_float(-Math::PI, Math.atan2(-0.0, -0.0)) assert_float(0, Math.atan2(0, 1)) assert_float(Math::PI / 4, Math.atan2(1, 1)) assert_float(Math::PI / 2, Math.atan2(1, 0)) inf = Float::INFINITY skip "Math.atan2() return NaN" if Math.atan2(+inf, -inf).nan? expected = 3.0 * Math::PI / 4.0 assert_float(+expected, Math.atan2(+inf, -inf)) assert_float(-expected, Math.atan2(-inf, -inf)) expected = Math::PI / 4.0 assert_float(+expected, Math.atan2(+inf, +inf)) assert_float(-expected, Math.atan2(-inf, +inf)) end assert('Math.ldexp') do assert_float(0.0, Math.ldexp(0.0, 0.0)) assert_float(0.5, Math.ldexp(0.5, 0.0)) assert_float(1.0, Math.ldexp(0.5, 1.0)) assert_float(2.0, Math.ldexp(0.5, 2.0)) assert_float(3.0, Math.ldexp(0.75, 2.0)) end assert('Math.frexp') do assert_float_and_int([0.0, 0], Math.frexp(0.0)) assert_float_and_int([0.5, 0], Math.frexp(0.5)) assert_float_and_int([0.5, 1], Math.frexp(1.0)) assert_float_and_int([0.5, 2], Math.frexp(2.0)) assert_float_and_int([0.75, 2], Math.frexp(3.0)) end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-math/PaxHeaders/src0000644000000000000000000000013215077107334022121 xustar0030 mtime=1761382108.919300904 30 atime=1761382109.798298363 30 ctime=1761382108.919300904 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-math/src/0000755000175100017510000000000015077107334022566 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-math/src/PaxHeaders/math.c0000644000000000000000000000013115077107276023277 xustar0030 mtime=1761382078.121420561 29 atime=1761382080.14141132 30 ctime=1761382108.919300904 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-math/src/math.c0000644000175100017510000003647215077107276023704 0ustar00runnerrunner/* ** math.c - Math module ** ** See Copyright Notice in mruby.h */ #include #ifdef MRB_NO_FLOAT # error Math conflicts with 'MRB_NO_FLOAT' configuration #endif #include #include static void domain_error(mrb_state *mrb, const char *func) { struct RClass *math = mrb_module_get_id(mrb, MRB_SYM(Math)); struct RClass *domainerror = mrb_class_get_under_id(mrb, math, MRB_SYM(DomainError)); mrb_raisef(mrb, domainerror, "Numerical argument is out of domain - %s", func); } /* math functions not provided by Microsoft Visual C++ 2012 or older */ #if defined _MSC_VER && _MSC_VER <= 1700 double asinh(double x) { double xa, ya, y; /* Basic formula loses precision for x < 0, but asinh is an odd function */ xa = fabs(x); if (xa > 3.16227E+18) { /* Prevent x*x from overflowing; basic formula reduces to log(2*x) */ ya = log(xa) + 0.69314718055994530942; } else { /* Basic formula for asinh */ ya = log(xa + sqrt(xa*xa + 1.0)); } y = _copysign(ya, x); return y; } double acosh(double x) { double y; if (x > 3.16227E+18) { /* Prevent x*x from overflowing; basic formula reduces to log(2*x) */ y = log(x) + 0.69314718055994530942; } else { /* Basic formula for acosh */ y = log(x + sqrt(x*x - 1.0)); } return y; } double atanh(double x) { double y; if (fabs(x) < 1E-2) { /* The sums 1+x and 1-x lose precision for small x. Use the polynomial instead. */ double x2 = x * x; y = x*(1.0 + x2*(1.0/3.0 + x2*(1.0/5.0 + x2*(1.0/7.0)))); } else { /* Basic formula for atanh */ y = 0.5 * (log1p(x) - log1p(-x)); } return y; } double cbrt(double x) { double xa, ya, y; /* pow(x, y) is undefined for x < 0 and y not an integer, but cbrt is an odd function */ xa = fabs(x); ya = pow(xa, 1.0/3.0); y = _copysign(ya, x); return y; } /* Declaration of complementary Error function */ double erfc(double x); /* ** Implementations of error functions ** credits to http://www.digitalmars.com/archives/cplusplus/3634.html */ /* Implementation of Error function */ double erf(double x) { static const double two_sqrtpi = 1.128379167095512574; double sum = x; double term = x; double xsqr = x*x; int j= 1; if (fabs(x) > 2.2) { return 1.0 - erfc(x); } do { term *= xsqr/j; sum -= term/(2*j+1); j++; term *= xsqr/j; sum += term/(2*j+1); j++; if (sum == 0) break; } while (fabs(term/sum) > DBL_EPSILON); return two_sqrtpi*sum; } /* Implementation of complementary Error function */ double erfc(double x) { static const double one_sqrtpi = 0.564189583547756287; double a = 1; double b = x; double c = x; double d = x*x+0.5; double q1; double q2 = b/d; double n = 1.0; if (fabs(x) < 2.2) { return erfc(x); } if (x < 0.0) { /*signbit(x)*/ return 2.0 - erfc(-x); } do { double t = a*n+b*x; a = b; b = t; t = c*n+d*x; c = d; d = t; n += 0.5; q1 = q2; q2 = b/d; } while (fabs(q1-q2)/q2 > DBL_EPSILON); return one_sqrtpi*exp(-x*x)*q2; } #endif #if defined __FreeBSD__ && !defined __FreeBSD_version #include /* for __FreeBSD_version */ #endif #if (defined _MSC_VER && _MSC_VER < 1800) || defined __ANDROID__ || (defined __FreeBSD__ && __FreeBSD_version < 803000) double log2(double x) { return log10(x)/log10(2.0); } #endif #define get_float_arg(mrb) mrb_as_float((mrb), mrb_get_arg1(mrb)) /* TRIGONOMETRIC FUNCTIONS */ /* * call-seq: * Math.sin(x) -> float * * Computes the sine of x (expressed in radians). Returns * -1..1. */ static mrb_value math_sin(mrb_state *mrb, mrb_value obj) { mrb_float x = sin(get_float_arg(mrb)); return mrb_float_value(mrb, x); } /* * call-seq: * Math.cos(x) -> float * * Computes the cosine of x (expressed in radians). Returns * -1..1. */ static mrb_value math_cos(mrb_state *mrb, mrb_value obj) { mrb_float x = cos(get_float_arg(mrb)); return mrb_float_value(mrb, x); } /* * call-seq: * Math.tan(x) -> float * * Returns the tangent of x (expressed in radians). */ static mrb_value math_tan(mrb_state *mrb, mrb_value obj) { mrb_float x = tan(get_float_arg(mrb)); return mrb_float_value(mrb, x); } /* INVERSE TRIGONOMETRIC FUNCTIONS */ /* * call-seq: * Math.asin(x) -> float * * Computes the arc sine of x. * @return computed value between `-(PI/2)` and `(PI/2)`. */ static mrb_value math_asin(mrb_state *mrb, mrb_value obj) { mrb_float x = get_float_arg(mrb); if (x < -1.0 || x > 1.0) { domain_error(mrb, "asin"); } x = asin(x); return mrb_float_value(mrb, x); } /* * call-seq: * Math.acos(x) -> float * * Computes the arc cosine of x. Returns 0..PI. */ static mrb_value math_acos(mrb_state *mrb, mrb_value obj) { mrb_float x = get_float_arg(mrb); if (x < -1.0 || x > 1.0) { domain_error(mrb, "acos"); } x = acos(x); return mrb_float_value(mrb, x); } /* * call-seq: * Math.atan(x) -> float * * Computes the arc tangent of x. Returns `-(PI/2) .. (PI/2)`. */ static mrb_value math_atan(mrb_state *mrb, mrb_value obj) { mrb_float x = atan(get_float_arg(mrb)); return mrb_float_value(mrb, x); } /* * call-seq: * Math.atan2(y, x) -> float * * Computes the arc tangent given y and x. Returns * -PI..PI. * * Math.atan2(-0.0, -1.0) #=> -3.141592653589793 * Math.atan2(-1.0, -1.0) #=> -2.356194490192345 * Math.atan2(-1.0, 0.0) #=> -1.5707963267948966 * Math.atan2(-1.0, 1.0) #=> -0.7853981633974483 * Math.atan2(-0.0, 1.0) #=> -0.0 * Math.atan2(0.0, 1.0) #=> 0.0 * Math.atan2(1.0, 1.0) #=> 0.7853981633974483 * Math.atan2(1.0, 0.0) #=> 1.5707963267948966 * Math.atan2(1.0, -1.0) #=> 2.356194490192345 * Math.atan2(0.0, -1.0) #=> 3.141592653589793 * */ static mrb_value math_atan2(mrb_state *mrb, mrb_value obj) { mrb_float x, y; mrb_get_args(mrb, "ff", &x, &y); x = atan2(x, y); return mrb_float_value(mrb, x); } /* HYPERBOLIC TRIG FUNCTIONS */ /* * call-seq: * Math.sinh(x) -> float * * Computes the hyperbolic sine of x (expressed in * radians). */ static mrb_value math_sinh(mrb_state *mrb, mrb_value obj) { mrb_float x = sinh(get_float_arg(mrb)); return mrb_float_value(mrb, x); } /* * call-seq: * Math.cosh(x) -> float * * Computes the hyperbolic cosine of x (expressed in radians). */ static mrb_value math_cosh(mrb_state *mrb, mrb_value obj) { mrb_float x = cosh(get_float_arg(mrb)); return mrb_float_value(mrb, x); } /* * call-seq: * Math.tanh() -> float * * Computes the hyperbolic tangent of x (expressed in * radians). */ static mrb_value math_tanh(mrb_state *mrb, mrb_value obj) { mrb_float x = tanh(get_float_arg(mrb)); return mrb_float_value(mrb, x); } /* INVERSE HYPERBOLIC TRIG FUNCTIONS */ /* * call-seq: * Math.asinh(x) -> float * * Computes the inverse hyperbolic sine of x. */ static mrb_value math_asinh(mrb_state *mrb, mrb_value obj) { mrb_float x = asinh(get_float_arg(mrb)); return mrb_float_value(mrb, x); } /* * call-seq: * Math.acosh(x) -> float * * Computes the inverse hyperbolic cosine of x. */ static mrb_value math_acosh(mrb_state *mrb, mrb_value obj) { mrb_float x = get_float_arg(mrb); if (x < 1.0) { domain_error(mrb, "acosh"); } x = acosh(x); return mrb_float_value(mrb, x); } /* * call-seq: * Math.atanh(x) -> float * * Computes the inverse hyperbolic tangent of x. */ static mrb_value math_atanh(mrb_state *mrb, mrb_value obj) { mrb_float x = get_float_arg(mrb); if (x < -1.0 || x > 1.0) { domain_error(mrb, "atanh"); } x = atanh(x); return mrb_float_value(mrb, x); } /* EXPONENTIALS AND LOGARITHMS */ /* * call-seq: * Math.exp(x) -> float * * Returns e**x. * * Math.exp(0) #=> 1.0 * Math.exp(1) #=> 2.718281828459045 * Math.exp(1.5) #=> 4.4816890703380645 * */ static mrb_value math_exp(mrb_state *mrb, mrb_value obj) { mrb_float x = exp(get_float_arg(mrb)); return mrb_float_value(mrb, x); } /* * call-seq: * Math.log(numeric) -> float * Math.log(num,base) -> float * * Returns the natural logarithm of numeric. * If additional second argument is given, it will be the base * of logarithm. * * Math.log(1) #=> 0.0 * Math.log(Math::E) #=> 1.0 * Math.log(Math::E**3) #=> 3.0 * Math.log(12,3) #=> 2.2618595071429146 * */ static mrb_value math_log(mrb_state *mrb, mrb_value obj) { mrb_float x, base; mrb_int argc; argc = mrb_get_args(mrb, "f|f", &x, &base); if (x < 0.0) { domain_error(mrb, "log"); } x = log(x); if (argc == 2) { if (base < 0.0) { domain_error(mrb, "log"); } x /= log(base); } return mrb_float_value(mrb, x); } /* * call-seq: * Math.log2(numeric) -> float * * Returns the base 2 logarithm of numeric. * * Math.log2(1) #=> 0.0 * Math.log2(2) #=> 1.0 * Math.log2(32768) #=> 15.0 * Math.log2(65536) #=> 16.0 * */ static mrb_value math_log2(mrb_state *mrb, mrb_value obj) { mrb_float x = get_float_arg(mrb); if (x < 0.0) { domain_error(mrb, "log2"); } x = log2(x); return mrb_float_value(mrb, x); } /* * call-seq: * Math.log10(numeric) -> float * * Returns the base 10 logarithm of numeric. * * Math.log10(1) #=> 0.0 * Math.log10(10) #=> 1.0 * Math.log10(10**100) #=> 100.0 * */ static mrb_value math_log10(mrb_state *mrb, mrb_value obj) { mrb_float x = get_float_arg(mrb); if (x < 0.0) { domain_error(mrb, "log10"); } x = log10(x); return mrb_float_value(mrb, x); } /* * call-seq: * Math.sqrt(numeric) -> float * * Returns the square root of numeric. * */ static mrb_value math_sqrt(mrb_state *mrb, mrb_value obj) { mrb_float x = get_float_arg(mrb); if (x < 0.0) { domain_error(mrb, "sqrt"); } x = sqrt(x); return mrb_float_value(mrb, x); } /* * call-seq: * Math.cbrt(numeric) -> float * * Returns the cube root of numeric. * * -9.upto(9) {|x| * p [x, Math.cbrt(x), Math.cbrt(x)**3] * } * #=> * [-9, -2.0800838230519, -9.0] * [-8, -2.0, -8.0] * [-7, -1.91293118277239, -7.0] * [-6, -1.81712059283214, -6.0] * [-5, -1.7099759466767, -5.0] * [-4, -1.5874010519682, -4.0] * [-3, -1.44224957030741, -3.0] * [-2, -1.25992104989487, -2.0] * [-1, -1.0, -1.0] * [0, 0.0, 0.0] * [1, 1.0, 1.0] * [2, 1.25992104989487, 2.0] * [3, 1.44224957030741, 3.0] * [4, 1.5874010519682, 4.0] * [5, 1.7099759466767, 5.0] * [6, 1.81712059283214, 6.0] * [7, 1.91293118277239, 7.0] * [8, 2.0, 8.0] * [9, 2.0800838230519, 9.0] * */ static mrb_value math_cbrt(mrb_state *mrb, mrb_value obj) { mrb_float x = cbrt(get_float_arg(mrb)); return mrb_float_value(mrb, x); } /* * call-seq: * Math.frexp(numeric) -> [ fraction, exponent ] * * Returns a two-element array containing the normalized fraction (a * Float) and exponent (a Integer) of * numeric. * * fraction, exponent = Math.frexp(1234) #=> [0.6025390625, 11] * fraction * 2**exponent #=> 1234.0 */ static mrb_value math_frexp(mrb_state *mrb, mrb_value obj) { mrb_float x = get_float_arg(mrb); int exp; x = frexp(x, &exp); return mrb_assoc_new(mrb, mrb_float_value(mrb, x), mrb_fixnum_value(exp)); } /* * call-seq: * Math.ldexp(flt, int) -> float * * Returns the value of flt*(2**int). * * fraction, exponent = Math.frexp(1234) * Math.ldexp(fraction, exponent) #=> 1234.0 */ static mrb_value math_ldexp(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_int i; mrb_get_args(mrb, "fi", &x, &i); x = ldexp(x, (int)i); return mrb_float_value(mrb, x); } /* * call-seq: * Math.hypot(x, y) -> float * * Returns sqrt(x**2 + y**2), the hypotenuse of a right-angled triangle * with sides x and y. * * Math.hypot(3, 4) #=> 5.0 */ static mrb_value math_hypot(mrb_state *mrb, mrb_value obj) { mrb_float x, y; mrb_get_args(mrb, "ff", &x, &y); x = hypot(x, y); return mrb_float_value(mrb, x); } /* * call-seq: * Math.erf(x) -> float * * Calculates the error function of x. */ static mrb_value math_erf(mrb_state *mrb, mrb_value obj) { mrb_float x = erf(get_float_arg(mrb)); return mrb_float_value(mrb, x); } /* * call-seq: * Math.erfc(x) -> float * * Calculates the complementary error function of x. */ static mrb_value math_erfc(mrb_state *mrb, mrb_value obj) { mrb_float x = erfc(get_float_arg(mrb)); return mrb_float_value(mrb, x); } /* ------------------------------------------------------------------------*/ void mrb_mruby_math_gem_init(mrb_state* mrb) { struct RClass *mrb_math; mrb_math = mrb_define_module(mrb, "Math"); mrb_define_class_under_id(mrb, mrb_math, MRB_SYM(DomainError), E_STANDARD_ERROR); #ifdef M_PI mrb_define_const_id(mrb, mrb_math, MRB_SYM(PI), mrb_float_value(mrb, M_PI)); #else mrb_define_const_id(mrb, mrb_math, MRB_SYM(PI), mrb_float_value(mrb, atan(1.0)*4.0)); #endif #ifdef M_E mrb_define_const_id(mrb, mrb_math, MRB_SYM(E), mrb_float_value(mrb, M_E)); #else mrb_define_const_id(mrb, mrb_math, MRB_SYM(E), mrb_float_value(mrb, exp(1.0))); #endif mrb_define_module_function(mrb, mrb_math, "sin", math_sin, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "cos", math_cos, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "tan", math_tan, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "asin", math_asin, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "acos", math_acos, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "atan", math_atan, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "atan2", math_atan2, MRB_ARGS_REQ(2)); mrb_define_module_function(mrb, mrb_math, "sinh", math_sinh, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "cosh", math_cosh, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "tanh", math_tanh, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "asinh", math_asinh, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "acosh", math_acosh, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "atanh", math_atanh, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "exp", math_exp, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "log", math_log, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_module_function(mrb, mrb_math, "log2", math_log2, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "log10", math_log10, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "sqrt", math_sqrt, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "cbrt", math_cbrt, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "frexp", math_frexp, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "ldexp", math_ldexp, MRB_ARGS_REQ(2)); mrb_define_module_function(mrb, mrb_math, "hypot", math_hypot, MRB_ARGS_REQ(2)); mrb_define_module_function(mrb, mrb_math, "erf", math_erf, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "erfc", math_erfc, MRB_ARGS_REQ(1)); } void mrb_mruby_math_gem_final(mrb_state* mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-io0000644000000000000000000000013215077107334021010 xustar0030 mtime=1761382108.671301621 30 atime=1761382109.798298363 30 ctime=1761382108.671301621 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/0000755000175100017510000000000015077107334021455 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276023207 xustar0030 mtime=1761382078.120420566 30 atime=1761382080.140411325 30 ctime=1761382108.669301627 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/mrbgem.rake0000644000175100017510000000055015077107276023577 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-io') do |spec| spec.license = 'MIT' spec.authors = ['Internet Initiative Japan Inc.', 'mruby developers'] spec.summary = 'IO and File class' if spec.for_windows? spec.linker.libraries << "ws2_32" end spec.build.defines << "HAVE_MRUBY_IO_GEM" spec.add_test_dependency 'mruby-time', core: 'mruby-time' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/PaxHeaders/mrblib0000644000000000000000000000013215077107334022257 xustar0030 mtime=1761382108.681301592 30 atime=1761382109.798298363 30 ctime=1761382108.681301592 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/mrblib/0000755000175100017510000000000015077107334022724 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/mrblib/PaxHeaders/io.rb0000644000000000000000000000013215077107276023275 xustar0030 mtime=1761382078.120420566 30 atime=1761382080.140411325 30 ctime=1761382108.677301604 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/mrblib/io.rb0000644000175100017510000000577215077107276023700 0ustar00runnerrunner## # IO # # ISO 15.2.20 class IOError < StandardError; end class EOFError < IOError; end class IO def self.open(*args, &block) io = self.new(*args) return io unless block begin yield io ensure begin io.close unless io.closed? rescue StandardError end end end def self.popen(command, mode = 'r', **opts, &block) if !self.respond_to?(:_popen) raise NotImplementedError, "popen is not supported on this platform" end io = self._popen(command, mode, **opts) return io unless block begin yield io ensure begin io.close unless io.closed? rescue IOError # nothing end end end def self.pipe(&block) if !self.respond_to?(:_pipe) raise NotImplementedError, "pipe is not supported on this platform" end if block begin r, w = IO._pipe yield r, w ensure r.close unless r.closed? w.close unless w.closed? end else IO._pipe end end def self.read(path, length=nil, offset=0, mode: "r") str = "" fd = -1 io = nil begin fd = IO.sysopen(path, mode) io = IO.open(fd, mode) io.seek(offset) if offset > 0 str = io.read(length) ensure if io io.close elsif fd != -1 IO._sysclose(fd) end end str end def hash # We must define IO#hash here because IO includes Enumerable and # Enumerable#hash will call IO#read() otherwise self.__id__ end def <<(str) write(str) self end alias_method :eof, :eof? alias_method :tell, :pos def pos=(i) seek(i, SEEK_SET) end def rewind seek(0, SEEK_SET) end def ungetbyte(c) if c.is_a? String c = c.getbyte(0) else c &= 0xff end s = " " s.setbyte(0,c) ungetc s end # 15.2.20.5.3 def each(&block) return to_enum unless block while line = self.gets block.call(line) end self end # 15.2.20.5.4 def each_byte(&block) return to_enum(:each_byte) unless block while byte = self.getbyte block.call(byte) end self end # 15.2.20.5.5 alias each_line each def each_char(&block) return to_enum(:each_char) unless block while char = self.getc block.call(char) end self end def puts(*args) i = 0 len = args.size if len == 0 write "\n" return end while i < len s = args[i] if s.kind_of?(Array) puts(*s) if s.size > 0 else s = s.to_s write s write "\n" if (s[-1] != "\n") end i += 1 end nil end def print(*args) i = 0 len = args.size while i < len write args[i].to_s i += 1 end end def printf(*args) write sprintf(*args) nil end alias_method :to_i, :fileno alias_method :tty?, :isatty end STDIN = IO.open(0, "r") STDOUT = IO.open(1, "w") STDERR = IO.open(2, "w") $stdin = STDIN $stdout = STDOUT $stderr = STDERR nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/mrblib/PaxHeaders/file_constants.rb0000644000000000000000000000013215077107276025701 xustar0030 mtime=1761382078.120420566 30 atime=1761382080.140411325 30 ctime=1761382108.680301595 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/mrblib/file_constants.rb0000644000175100017510000000027315077107276026273 0ustar00runnerrunnerclass File module Constants FNM_SYSCASE = 0 FNM_NOESCAPE = 1 FNM_PATHNAME = 2 FNM_DOTMATCH = 4 FNM_CASEFOLD = 8 end end class File include File::Constants end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/mrblib/PaxHeaders/kernel.rb0000644000000000000000000000013215077107276024146 xustar0030 mtime=1761382078.120420566 30 atime=1761382080.140411325 30 ctime=1761382108.681301592 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/mrblib/kernel.rb0000644000175100017510000000117715077107276024544 0ustar00runnerrunnermodule Kernel private def `(cmd) #` IO.popen(cmd) { |io| io.read } end private def open(file, *rest, &block) raise ArgumentError unless file.is_a?(String) if file[0] == "|" IO.popen(file[1..-1], *rest, &block) else File.open(file, *rest, &block) end end private def print(...) $stdout.print(...) end private def puts(...) $stdout.puts(...) end private def printf(...) $stdout.printf(...) end private def gets(...) $stdin.gets(...) end private def readline(...) $stdin.readline(...) end private def readlines(...) $stdin.readlines(...) end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/mrblib/PaxHeaders/file.rb0000644000000000000000000000013215077107276023605 xustar0030 mtime=1761382078.120420566 30 atime=1761382080.140411325 30 ctime=1761382108.679301598 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/mrblib/file.rb0000644000175100017510000000477315077107276024210 0ustar00runnerrunnerclass File < IO attr_accessor :path def initialize(fd_or_path, mode = "r", perm = 0666) if fd_or_path.kind_of? Integer super(fd_or_path, mode) else @path = fd_or_path fd = IO.sysopen(@path, mode, perm) super(fd, mode) end end def atime t = self._atime t && Time.at(t) end def ctime t = self._ctime t && Time.at(t) end def mtime t = self._mtime t && Time.at(t) end def inspect "<#{self.class}:#{@path}>" end def self.join(*names) return "" if names.empty? names.map! do |name| case name when String name when Array if names == name raise ArgumentError, "recursive array" end join(*name) else raise TypeError, "no implicit conversion of #{name.class} into String" end end return names[0] if names.size == 1 if names[0][-1] == File::SEPARATOR s = names[0][0..-2] else s = names[0].dup end (1..names.size-2).each { |i| t = names[i] if t[0] == File::SEPARATOR and t[-1] == File::SEPARATOR t = t[1..-2] elsif t[0] == File::SEPARATOR t = t[1..-1] elsif t[-1] == File::SEPARATOR t = t[0..-2] end s += File::SEPARATOR + t if t != "" } if names[-1][0] == File::SEPARATOR s += File::SEPARATOR + names[-1][1..-1] else s += File::SEPARATOR + names[-1] end s end def self.foreach(file) if block_given? self.open(file) do |f| f.each {|l| yield l} end else return self.new(file) end end def self.directory?(file) FileTest.directory?(file) end def self.exist?(file) FileTest.exist?(file) end def self.exists?(file) FileTest.exists?(file) end def self.file?(file) FileTest.file?(file) end def self.pipe?(file) FileTest.pipe?(file) end def self.size(file) FileTest.size(file) end def self.size?(file) FileTest.size?(file) end def self.socket?(file) FileTest.socket?(file) end def self.symlink?(file) FileTest.symlink?(file) end def self.zero?(file) FileTest.zero?(file) end def self.extname(filename) fname = self.basename(filename) epos = fname.rindex('.') return '' if epos == 0 || epos.nil? return fname[epos..-1] end def self.path(filename) if filename.kind_of?(String) filename else raise TypeError, "no implicit conversion of #{filename.class} into String" end end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/PaxHeaders/README.md0000644000000000000000000000013115077107276022350 xustar0029 mtime=1761382078.11942057 30 atime=1761382080.140411325 30 ctime=1761382108.671301621 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/README.md0000644000175100017510000002237515077107276022752 0ustar00runnerrunner# mruby-io `IO` and `File` classes for mruby ## Installation Add the line below to your build configuration. ``` conf.gem core: 'mruby-io' ``` ## Implemented methods ### IO - | method | mruby-io | memo | | -------------------------- | -------- | -------- | | IO.binread | | | | IO.binwrite | | | | IO.copy_stream | | | | IO.new, IO.for_fd, IO.open | o | | | IO.foreach | | | | IO.pipe | o | | | IO.popen | o | | | IO.read | o | | | IO.readlines | | | | IO.select | o | | | IO.sysopen | o | | | IO.try_convert | | | | IO.write | | | | IO#<< | | | | IO#advise | | | | IO#autoclose= | | | | IO#autoclose? | | | | IO#binmode | | | | IO#binmode? | | | | IO#bytes | | obsolete | | IO#chars | | obsolete | | IO#clone, IO#dup | o | | | IO#close | o | | | IO#close_on_exec= | o | | | IO#close_on_exec? | o | | | IO#close_read | | | | IO#close_write | | | | IO#closed? | o | | | IO#codepoints | | obsolete | | IO#each_byte | o | | | IO#each_char | o | | | IO#each_codepoint | | | | IO#each_line | o | | | IO#eof, IO#eof? | o | | | IO#external_encoding | | | | IO#fcntl | | | | IO#fdatasync | | | | IO#fileno, IO#to_i | o | | | IO#flush | o | | | IO#fsync | | | | IO#getbyte | o | | | IO#getc | o | | | IO#gets | o | | | IO#internal_encoding | | | | IO#ioctl | | | | IO#isatty, IO#tty? | o | | | IO#lineno | | | | IO#lineno= | | | | IO#lines | | obsolete | | IO#pid | o | | | IO#pos, IO#tell | o | | | IO#pos= | o | | | IO#print | o | | | IO#printf | o | | | IO#putc | | | | IO#puts | o | | | IO#read | o | | | IO#read_nonblock | | | | IO#readbyte | o | | | IO#readchar | o | | | IO#readline | o | | | IO#readlines | o | | | IO#readpartial | | | | IO#reopen | | | | IO#rewind | | | | IO#seek | o | | | IO#set_encoding | | | | IO#stat | | | | IO#sync | o | | | IO#sync= | o | | | IO#sysread | o | | | IO#sysseek | o | | | IO#syswrite | o | | | IO#to_io | | | | IO#ungetbyte | o | | | IO#ungetc | o | | | IO#write | o | | | IO#write_nonblock | | | ### File - | method | mruby-io | memo | | --------------------------- | -------- | -------- | | File.absolute_path | | | | File.atime | | | | File.basename | o | | | File.blockdev? | | FileTest | | File.chardev? | | FileTest | | File.chmod | o | | | File.chown | | | | File.ctime | | | | File.delete, File.unlink | o | | | File.directory? | o | FileTest | | File.dirname | o | | | File.executable? | | FileTest | | File.executable_real? | | FileTest | | File.exist?, exists? | o | FileTest | | File.expand_path | o | | | File.extname | o | | | File.file? | o | FileTest | | File.fnmatch, File.fnmatch? | | | | File.ftype | | | | File.grpowned? | | FileTest | | File.identical? | | FileTest | | File.join | o | | | File.lchmod | | | | File.lchown | | | | File.link | | | | File.lstat | | | | File.mtime | | | | File.new, File.open | o | | | File.owned? | | FileTest | | File.path | | | | File.pipe? | o | FileTest | | File.readable? | | FileTest | | File.readable_real? | | FileTest | | File.readlink | o | | | File.realdirpath | | | | File.realpath | o | | | File.rename | o | | | File.setgid? | | FileTest | | File.setuid? | | FileTest | | File.size | o | | | File.size? | o | FileTest | | File.socket? | o | FileTest | | File.split | | | | File.stat | | | | File.sticky? | | FileTest | | File.symlink | | | | File.symlink? | o | FileTest | | File.truncate | | | | File.umask | o | | | File.utime | | | | File.world_readable? | | | | File.world_writable? | | | | File.writable? | | FileTest | | File.writable_real? | | FileTest | | File.zero? | o | FileTest | | File#atime | o | | | File#chmod | | | | File#chown | | | | File#ctime | o | | | File#flock | o | | | File#lstat | | | | File#mtime | o | | | File#path | o | | | File#size | | | | File#truncate | | | ## Porting Note If your (non Windows) platform does not support `getpwnam(3)` for some reason, define `MRB_IO_NO_PWNAM`. See [mruby#5358](https://github.com/mruby/mruby/issues/5358). ## License Copyright (c) 2013 Internet Initiative Japan Inc. Copyright (c) 2017 mruby developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/PaxHeaders/test0000644000000000000000000000013215077107334021767 xustar0030 mtime=1761382108.676301607 30 atime=1761382109.798298363 30 ctime=1761382108.676301607 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/test/0000755000175100017510000000000015077107334022434 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/test/PaxHeaders/mruby_io_test.c0000644000000000000000000000013115077107276025100 xustar0030 mtime=1761382078.121420561 29 atime=1761382080.14141132 30 ctime=1761382108.672301618 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/test/mruby_io_test.c0000644000175100017510000001714415077107276025500 0ustar00runnerrunner#include #include #include #include #if defined(_WIN32) #include #include #include #include #include #include #if (!defined __MINGW64__) && (!defined __MINGW32__) typedef int mode_t; #endif #define open _open #define close _close #if defined(_MSC_VER) || \ (defined(MRB_MINGW32_VERSION) && MRB_MINGW32_VERSION < 3021) || \ (defined(MRB_MINGW64_VERSION) && MRB_MINGW64_VERSION < 4000) #include static int mkstemp(char *p) { int fd; char* fname = _mktemp(p); if (fname == NULL) return -1; fd = open(fname, O_RDWR | O_CREAT | O_EXCL, _S_IREAD | _S_IWRITE); if (fd >= 0) return fd; return -1; } #endif static char* mkdtemp(char *temp) { char *path = _mktemp(temp); if (path[0] == 0) return NULL; if (_mkdir(path) < 0) return NULL; return path; } #define umask(mode) _umask(mode) #define rmdir(path) _rmdir(path) #else #include #include #include #include #include #endif #include #include #include #include #include #include #include #include static mrb_value mrb_io_test_io_setup(mrb_state *mrb, mrb_value self) { #define GVNAME(n) "$mrbtest_io_" #n "name" enum {IDX_READ, IDX_WRITE, IDX_LINK, IDX_SOCKET, IDX_COUNT}; const char *gvnames[] = {GVNAME(rf), GVNAME(wf), GVNAME(symlink), GVNAME(socket)}; char *fnames[IDX_COUNT]; int fds[IDX_COUNT]; char msg[] = "mruby io test\n"; mode_t mask; FILE *fp; int i; #if !defined(_WIN32) struct sockaddr_un sun0 = { 0 }; /* Initialize them all because it is environment dependent */ #endif mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_msg"), mrb_str_new_cstr(mrb, msg)); mask = umask(077); for (i = 0; i < IDX_COUNT; i++) { mrb_value fname = mrb_str_new_capa(mrb, 0); #if !defined(_WIN32) /* * Workaround for not being able to bind a socket to some file systems * (e.g. vboxsf, NFS). [#4981] */ char *tmpdir = getenv("TMPDIR"); if (tmpdir && strlen(tmpdir) > 0) { mrb_str_cat_cstr(mrb, fname, tmpdir); if (*(RSTRING_END(fname)-1) != '/') mrb_str_cat_lit(mrb, fname, "/"); } else { mrb_str_cat_lit(mrb, fname, "/tmp/"); } #endif mrb_str_cat_cstr(mrb, fname, gvnames[i]+1); mrb_str_cat_cstr(mrb, fname, ".XXXXXXXX"); fnames[i] = RSTRING_PTR(fname); fds[i] = mkstemp(fnames[i]); if (fds[i] == -1) { mrb_raise(mrb, E_RUNTIME_ERROR, "can't create temporary file"); } close(fds[i]); mrb_gv_set(mrb, mrb_intern_cstr(mrb, gvnames[i]), fname); } umask(mask); fp = fopen(fnames[IDX_READ], "wb"); if (fp == NULL) { mrb_raise(mrb, E_RUNTIME_ERROR, "can't open temporary file"); return mrb_nil_value(); } fputs(msg, fp); fclose(fp); fp = fopen(fnames[IDX_WRITE], "wb"); if (fp == NULL) { mrb_raise(mrb, E_RUNTIME_ERROR, "can't open temporary file"); return mrb_nil_value(); } fclose(fp); #if !defined(_WIN32) unlink(fnames[IDX_LINK]); if (symlink(basename(fnames[IDX_READ]), fnames[IDX_LINK]) == -1) { mrb_raise(mrb, E_RUNTIME_ERROR, "can't make a symbolic link"); } unlink(fnames[IDX_SOCKET]); fds[IDX_SOCKET] = socket(AF_UNIX, SOCK_STREAM, 0); if (fds[IDX_SOCKET] == -1) { mrb_raise(mrb, E_RUNTIME_ERROR, "can't make a socket"); } sun0.sun_family = AF_UNIX; strncpy(sun0.sun_path, fnames[IDX_SOCKET], sizeof(sun0.sun_path)-1); sun0.sun_path[sizeof(sun0.sun_path)-1] = 0; if (bind(fds[IDX_SOCKET], (struct sockaddr*)&sun0, sizeof(sun0)) == -1) { mrb_raisef(mrb, E_RUNTIME_ERROR, "can't bind AF_UNIX socket to %s: %d", sun0.sun_path, errno); } close(fds[IDX_SOCKET]); #endif return mrb_true_value(); #undef GVNAME } static mrb_value mrb_io_test_io_cleanup(mrb_state *mrb, mrb_value self) { mrb_value rfname = mrb_gv_get(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_rfname")); mrb_value wfname = mrb_gv_get(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_wfname")); mrb_value symlinkname = mrb_gv_get(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_symlinkname")); mrb_value socketname = mrb_gv_get(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_socketname")); if (mrb_string_p(rfname)) { remove(RSTRING_PTR(rfname)); } if (mrb_string_p(wfname)) { remove(RSTRING_PTR(wfname)); } if (mrb_string_p(symlinkname)) { remove(RSTRING_PTR(symlinkname)); } if (mrb_string_p(socketname)) { remove(RSTRING_PTR(socketname)); } mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_rfname"), mrb_nil_value()); mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_wfname"), mrb_nil_value()); mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_symlinkname"), mrb_nil_value()); mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_socketname"), mrb_nil_value()); mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_msg"), mrb_nil_value()); return mrb_nil_value(); } static mrb_value mrb_io_test_mkdtemp(mrb_state *mrb, mrb_value klass) { mrb_value str; char *cp; mrb_get_args(mrb, "S", &str); cp = mrb_str_to_cstr(mrb, str); if (mkdtemp(cp) == NULL) { mrb_sys_fail(mrb, "mkdtemp"); } return mrb_str_new_cstr(mrb, cp); } static mrb_value mrb_io_test_rmdir(mrb_state *mrb, mrb_value klass) { const char *cp; mrb_get_args(mrb, "z", &cp); if (rmdir(cp) == -1) { mrb_sys_fail(mrb, "rmdir"); } return mrb_true_value(); } static mrb_value mrb_io_win_p(mrb_state *mrb, mrb_value klass) { #if defined(_WIN32) # if defined(__CYGWIN__) || defined(__CYGWIN32__) return mrb_false_value(); # else return mrb_true_value(); # endif #else return mrb_false_value(); #endif } #if defined(_WIN32) #define MAXPATHLEN 1024 #define getcwd _getcwd #else #include #include #include #endif static mrb_value mrb_io_test_getwd(mrb_state *mrb, mrb_value klass) { char buf[MAXPATHLEN]; mrb->c->ci->mid = 0; if (getcwd(buf, MAXPATHLEN) == NULL) { mrb_sys_fail(mrb, "getcwd(2)"); } char *utf8 = mrb_utf8_from_locale(buf, -1); mrb_value path = mrb_str_new_cstr(mrb, utf8); mrb_utf8_free(utf8); return path; } #ifdef MRB_USE_IO_PREAD_PWRITE # define MRB_USE_IO_PREAD_PWRITE_ENABLED TRUE #else # define MRB_USE_IO_PREAD_PWRITE_ENABLED FALSE #endif void mrb_mruby_io_gem_test(mrb_state* mrb) { struct RClass *io_test = mrb_define_module(mrb, "MRubyIOTestUtil"); mrb_define_class_method(mrb, io_test, "io_test_setup", mrb_io_test_io_setup, MRB_ARGS_NONE()); mrb_define_class_method(mrb, io_test, "io_test_cleanup", mrb_io_test_io_cleanup, MRB_ARGS_NONE()); mrb_define_class_method(mrb, io_test, "getwd", mrb_io_test_getwd, MRB_ARGS_NONE()); mrb_define_class_method(mrb, io_test, "mkdtemp", mrb_io_test_mkdtemp, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, io_test, "rmdir", mrb_io_test_rmdir, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, io_test, "win?", mrb_io_win_p, MRB_ARGS_NONE()); mrb_define_const(mrb, io_test, "MRB_USE_IO_PREAD_PWRITE", mrb_bool_value(MRB_USE_IO_PREAD_PWRITE_ENABLED)); const char *env_home = getenv("HOME"); #ifdef _WIN32 if (!env_home) { env_home = getenv("USERPROFILE"); } #endif if (env_home) { char *utf8 = mrb_utf8_from_locale(env_home, -1); mrb_value path = mrb_str_new_cstr(mrb, utf8); #ifdef _WIN32 char *pathp = RSTRING_PTR(path); const char *const pathend = pathp + RSTRING_LEN(path); for (;;) { pathp = (char*)memchr(pathp, '\\', pathend - pathp); if (!pathp) break; *pathp++ = '/'; } #endif mrb_define_const(mrb, io_test, "ENV_HOME", path); mrb_utf8_free(utf8); } } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/test/PaxHeaders/io.rb0000644000000000000000000000013115077107276023004 xustar0030 mtime=1761382078.121420561 29 atime=1761382080.14141132 30 ctime=1761382108.673301616 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/test/io.rb0000644000175100017510000003705715077107276023411 0ustar00runnerrunner## # IO Test MRubyIOTestUtil.io_test_setup $cr, $crlf, $cmd = MRubyIOTestUtil.win? ? [1, "\r\n", "cmd /c "] : [0, "\n", ""] def assert_io_open(meth) assert "assert_io_open" do fd = IO.sysopen($mrbtest_io_rfname) assert_equal Integer, fd.class io1 = IO.__send__(meth, fd) begin assert_equal IO, io1.class assert_equal $mrbtest_io_msg, io1.read ensure io1.close end if meth == :open io2 = IO.__send__(meth, IO.sysopen($mrbtest_io_rfname))do |io| if meth == :open assert_equal $mrbtest_io_msg, io.read else flunk "IO.#{meth} does not take block" end end end assert_raise(RuntimeError) { IO.__send__(meth, 1023) } # For Windows assert_raise(RuntimeError) { IO.__send__(meth, 1 << 26) } end end assert('IO.class', '15.2.20') do assert_equal(Class, IO.class) end assert('IO.superclass', '15.2.20.2') do assert_equal(Object, IO.superclass) end assert('IO.ancestors', '15.2.20.3') do assert_include(IO.ancestors, Enumerable) end assert('IO.open', '15.2.20.4.1') do assert_io_open(:open) end assert('IO#close', '15.2.20.5.1') do io = IO.new(IO.sysopen($mrbtest_io_rfname)) assert_nil io.close end assert('IO#closed?', '15.2.20.5.2') do io = IO.new(IO.sysopen($mrbtest_io_rfname)) assert_false io.closed? io.close assert_true io.closed? end #assert('IO#each', '15.2.20.5.3') do #assert('IO#each_byte', '15.2.20.5.4') do #assert('IO#each_line', '15.2.20.5.5') do assert('IO#eof?', '15.2.20.5.6') do io = IO.new(IO.sysopen($mrbtest_io_wfname, 'w'), 'w') assert_raise(IOError) do io.eof? end io.close # empty file io = IO.open(IO.sysopen($mrbtest_io_wfname, 'w'), 'w') io.close io = IO.open(IO.sysopen($mrbtest_io_wfname, 'r'), 'r') assert_true io.eof? io.close # nonempty file io = IO.new(IO.sysopen($mrbtest_io_rfname)) assert_false io.eof? io.readchar assert_false io.eof? io.read assert_true io.eof? io.close end assert('IO#flush', '15.2.20.5.7') do # Note: mruby-io does not have any buffer to be flushed now. io = IO.new(IO.sysopen($mrbtest_io_wfname)) assert_equal io, io.flush io.close assert_raise(IOError) do io.flush end end assert('IO#getc', '15.2.20.5.8') do io = IO.new(IO.sysopen($mrbtest_io_rfname)) $mrbtest_io_msg.split("").each { |ch| assert_equal ch, io.getc } assert_equal nil, io.getc io.close end assert('IO#getbyte') do io = IO.new(IO.sysopen($mrbtest_io_rfname)) $mrbtest_io_msg.split("").each do |ch| assert_equal ch.getbyte(0), io.getbyte end assert_equal nil, io.getbyte io.close end #assert('IO#gets', '15.2.20.5.9') do #assert('IO#initialize_copy', '15.2.20.5.10') do #assert('IO#print', '15.2.20.5.11') do #assert('IO#putc', '15.2.20.5.12') do #assert('IO#puts', '15.2.20.5.13') do assert('IO#read', '15.2.20.5.14') do IO.open(IO.sysopen($mrbtest_io_rfname)) do |io| assert_raise(ArgumentError) { io.read(-5) } assert_raise(TypeError) { io.read("str") } len = $mrbtest_io_msg.length assert_equal '', io.read(0) assert_equal 'mruby', io.read(5) assert_equal $mrbtest_io_msg[5,len], io.read(len) assert_equal "", io.read assert_nil io.read(1) end IO.open(IO.sysopen($mrbtest_io_rfname)) do |io| assert_equal $mrbtest_io_msg, io.read end end assert "IO#read(n) with n > IO::BUF_SIZE" do buf_size = 4096 # copied from io.c skip "pipe is not supported on this platform" if MRubyIOTestUtil.win? IO.pipe do |r,w| n = buf_size+1 w.write 'a'*n assert_equal 'a'*n, r.read(n) end end assert "IO#read(n, buf)" do IO.open(IO.sysopen($mrbtest_io_rfname)) do |io| buf = "12345" assert_same buf, io.read(0, buf) assert_equal "", buf buf = "12345" assert_same buf, io.read(5, buf) assert_equal "mruby", buf buf = "12345" assert_same buf, io.read(nil, buf) assert_equal " io test\n", buf buf = "12345" assert_nil io.read(99, buf) assert_equal "", buf buf = "12345" assert_same buf, io.read(0, buf) assert_equal "", buf end end assert('IO#readchar', '15.2.20.5.15') do # almost same as IO#getc IO.open(IO.sysopen($mrbtest_io_rfname)) do |io| $mrbtest_io_msg.split("").each { |ch| assert_equal ch, io.readchar } assert_raise(EOFError) do io.readchar end end end #assert('IO#readline', '15.2.20.5.16') do #assert('IO#readlines', '15.2.20.5.17') do assert('IO#sync', '15.2.20.5.18') do io = IO.new(IO.sysopen($mrbtest_io_rfname)) s = io.sync assert_true(s == true || s == false) io.close assert_raise(IOError) do io.sync end end assert('IO#sync=', '15.2.20.5.19') do io = IO.new(IO.sysopen($mrbtest_io_rfname)) io.sync = true assert_true io.sync io.sync = false assert_false io.sync io.close assert_raise(IOError) do io.sync = true end end assert('IO#write', '15.2.20.5.20') do io = IO.open(IO.sysopen($mrbtest_io_wfname, "w"), "w") assert_equal 1, io.write("a") io.close io = IO.open(IO.sysopen($mrbtest_io_wfname, "r+"), "r+") assert_equal 7, io.write("abcdefg") io.rewind assert_equal "ab", io.read(2) assert_equal 3, io.write("123") io.rewind assert_equal "ab123fg", io.read io.close end assert('IO#<<') do io = IO.open(IO.sysopen($mrbtest_io_wfname, "w"), "w") io << "a" << "b" assert_equal 2, io.pos io.close end assert('IO#dup for readable') do io = IO.new(IO.sysopen($mrbtest_io_rfname)) dup = io.dup assert_true io != dup assert_true io.fileno != dup.fileno begin assert_true dup.close_on_exec? rescue NotImplementedError end assert_equal 'm', dup.sysread(1) assert_equal 'r', io.sysread(1) assert_equal 'u', dup.sysread(1) assert_equal 'b', io.sysread(1) assert_equal 'y', dup.sysread(1) dup.close assert_false io.closed? io.close end assert('IO#dup for writable') do io = IO.open(IO.sysopen($mrbtest_io_wfname, 'w+'), 'w+') dup = io.dup io.syswrite "mruby" assert_equal 5, dup.sysseek(0, IO::SEEK_CUR) io.sysseek 0, IO::SEEK_SET assert_equal 0, dup.sysseek(0, IO::SEEK_CUR) assert_equal "mruby", dup.sysread(5) dup.close io.close end assert('IO.for_fd') do assert_io_open(:for_fd) end assert('IO.new') do assert_io_open(:new) end assert('IO gc check') do assert_nothing_raised { 100.times { IO.new(0) } } end assert('IO.sysopen("./nonexistent")') do if Object.const_defined? :Errno eclass = Errno::ENOENT else eclass = RuntimeError end assert_raise eclass do fd = IO.sysopen "./nonexistent" IO._sysclose fd end end assert('IO.sysopen, IO#sysread') do fd = IO.sysopen $mrbtest_io_rfname io = IO.new fd str1 = " " str2 = io.sysread(5, str1) assert_equal $mrbtest_io_msg[0,5], str1 assert_equal $mrbtest_io_msg[0,5], str2 assert_raise EOFError do io.sysread(10000) io.sysread(10000) end assert_raise RuntimeError do io.sysread(5, "abcde".freeze) end io.close assert_equal "", io.sysread(0) assert_raise(IOError) { io.sysread(1) } assert_raise(ArgumentError) { io.sysread(-1) } io.closed? fd = IO.sysopen $mrbtest_io_wfname, "w" io = IO.new fd, "w" assert_raise(IOError) { io.sysread(1) } io.close end assert('IO.sysopen, IO#syswrite') do fd = IO.sysopen $mrbtest_io_wfname, "w" io = IO.new fd, "w" str = "abcdefg" len = io.syswrite(str) assert_equal str.size, len io.close io = IO.new(IO.sysopen($mrbtest_io_rfname), "r") assert_raise(IOError) { io.syswrite("a") } io.close end assert('IO#ungetc') do io = IO.new(IO.sysopen($mrbtest_io_rfname)) assert_equal 'm', io.getc assert_nothing_raised{io.ungetc("M")} assert_equal 'M', io.getc io.close end assert('IO#isatty') do skip "isatty is not supported on this platform" if MRubyIOTestUtil.win? begin f = File.open("/dev/tty") rescue RuntimeError => e skip e.message else assert_true f.isatty ensure f&.close end begin f = File.open($mrbtest_io_rfname) assert_false f.isatty ensure f&.close end end assert('IO#pos=, IO#seek') do fd = IO.sysopen $mrbtest_io_rfname io = IO.new fd def io._buf @buf end assert_equal 'm', io.getc assert_equal 1, io.pos assert_equal 0, io.seek(0) assert_equal 0, io.pos io.close end assert('IO#rewind') do fd = IO.sysopen $mrbtest_io_rfname io = IO.new fd assert_equal 'm', io.getc assert_equal 1, io.pos assert_equal 0, io.rewind assert_equal 0, io.pos io.close end assert('IO#gets') do fd = IO.sysopen $mrbtest_io_rfname io = IO.new fd # gets without arguments assert_equal $mrbtest_io_msg, io.gets, "gets without arguments" assert_equal nil, io.gets, "gets returns nil, when EOF" # gets with limit io.pos = 0 assert_equal $mrbtest_io_msg[0, 5], io.gets(5), "gets with limit" # gets with rs io.pos = 0 assert_equal $mrbtest_io_msg[0, 6], io.gets(' '), "gets with rs" assert_equal $mrbtest_io_msg[6, 3], io.gets(' '), "gets with rs(2)" # gets with rs, limit io.pos = 0 assert_equal $mrbtest_io_msg[0, 5], io.gets(' ', 5), "gets with rs, limit" io.close assert_equal true, io.closed?, "close success" # reading many-lines file. fd = IO.sysopen $mrbtest_io_wfname, "w" io = IO.new fd, "w" io.write "0123456789" * 2 + "\na" assert_equal 22 + $cr, io.pos io.close assert_equal true, io.closed? fd = IO.sysopen $mrbtest_io_wfname io = IO.new fd line = io.gets # gets first line assert_equal "0123456789" * 2 + "\n", line, "gets first line" assert_equal 21, line.size assert_equal 21 + $cr, io.pos # gets second line assert_equal "a", io.gets, "gets second line" # gets third line assert_equal nil, io.gets, "gets third line; returns nil" io.close end assert('IO#gets - paragraph mode') do fd = IO.sysopen $mrbtest_io_wfname, "w" io = IO.new fd, "w" io.write "0" * 10 + "\n" io.write "1" * 10 + "\n\n" io.write "2" * 10 + "\n" assert_equal 34 + $cr * 4, io.pos io.close fd = IO.sysopen $mrbtest_io_wfname io = IO.new fd para1 = "#{'0' * 10}\n#{'1' * 10}\n\n" text1 = io.gets("") assert_equal para1, text1 para2 = "#{'2' * 10}\n" text2 = io.gets("") assert_equal para2, text2 io.close end assert('IO.popen') do begin $? = nil io = IO.popen("#{$cmd}echo mruby-io") assert_true io.close_on_exec? assert_equal Integer, io.pid.class out = io.read assert_equal out.class, String assert_include out, 'mruby-io' io.close if Object.const_defined? :Process assert_true $?.success? else assert_equal 0, $? end assert_true io.closed? rescue NotImplementedError => e skip e.message end end assert('IO.popen with in option') do begin IO.pipe do |r, w| w.write 'hello' w.close assert_equal "hello", IO.popen("cat", "r", in: r) { |i| i.read } assert_equal "", r.read end assert_raise(ArgumentError) { IO.popen("hello", "r", in: Object.new) } rescue NotImplementedError => e skip e.message end end assert('IO.popen with out option') do begin IO.pipe do |r, w| IO.popen("echo 'hello'", "w", out: w) {} w.close assert_equal "hello\n", r.read end rescue NotImplementedError => e skip e.message end end assert('IO.popen with err option') do begin IO.pipe do |r, w| assert_equal "", IO.popen("echo 'hello' 1>&2", "r", err: w) { |i| i.read } w.close assert_equal "hello\n", r.read end rescue NotImplementedError => e skip e.message end end assert('IO.read') do # empty file fd = IO.sysopen $mrbtest_io_wfname, "w" io = IO.new fd, "w" io.close assert_equal "", IO.read($mrbtest_io_wfname) assert_equal nil, IO.read($mrbtest_io_wfname, 1) # one byte file fd = IO.sysopen $mrbtest_io_wfname, "w" io = IO.new fd, "w" io.write "123" io.close assert_equal "123", IO.read($mrbtest_io_wfname) assert_equal "", IO.read($mrbtest_io_wfname, 0) assert_equal "1", IO.read($mrbtest_io_wfname, 1) assert_equal "", IO.read($mrbtest_io_wfname, 0, 10) assert_equal "23", IO.read($mrbtest_io_wfname, 2, 1) assert_equal "23", IO.read($mrbtest_io_wfname, 10, 1) assert_equal "", IO.read($mrbtest_io_wfname, nil, 10) assert_equal nil, IO.read($mrbtest_io_wfname, 1, 10) end assert('IO#fileno') do fd = IO.sysopen $mrbtest_io_rfname io = IO.new fd assert_equal io.fileno, fd assert_equal io.to_i, fd io.close end assert('IO#close_on_exec') do fd = IO.sysopen $mrbtest_io_wfname, "w" io = IO.new fd, "w" begin # IO.sysopen opens a file descriptor with O_CLOEXEC flag. assert_true io.close_on_exec? rescue ScriptError io.close skip "IO\#close_on_exec is not implemented." end io.close_on_exec = false assert_equal(false, io.close_on_exec?) io.close_on_exec = true assert_equal(true, io.close_on_exec?) io.close_on_exec = false assert_equal(false, io.close_on_exec?) io.close begin r, w = IO.pipe assert_equal(true, r.close_on_exec?) r.close_on_exec = false assert_equal(false, r.close_on_exec?) r.close_on_exec = true assert_equal(true, r.close_on_exec?) assert_equal(true, w.close_on_exec?) w.close_on_exec = false assert_equal(false, w.close_on_exec?) w.close_on_exec = true assert_equal(true, w.close_on_exec?) ensure r.close unless r.closed? w.close unless w.closed? end end assert('IO#sysseek') do IO.open(IO.sysopen($mrbtest_io_rfname)) do |io| assert_equal 2, io.sysseek(2) assert_equal 5, io.sysseek(3, IO::SEEK_CUR) # 2 + 3 => 5 assert_equal $mrbtest_io_msg.size - 4, io.sysseek(-4, IO::SEEK_END) end end assert('IO#pread') do skip "IO#pread is not implemented on this configuration" unless MRubyIOTestUtil::MRB_USE_IO_PREAD_PWRITE IO.open(IO.sysopen($mrbtest_io_rfname, 'r'), 'r') do |io| assert_equal $mrbtest_io_msg.byteslice(5, 8), io.pread(8, 5) assert_equal 0, io.pos assert_equal $mrbtest_io_msg.byteslice(1, 5), io.pread(5, 1) assert_equal 0, io.pos assert_raise(RuntimeError) { io.pread(20, -9) } end end assert('IO#pwrite') do skip "IO#pwrite is not implemented on this configuration" unless MRubyIOTestUtil::MRB_USE_IO_PREAD_PWRITE IO.open(IO.sysopen($mrbtest_io_wfname, 'w+'), 'w+') do |io| assert_equal 6, io.pwrite("Warld!", 7) assert_equal 0, io.pos assert_equal 7, io.pwrite("Hello, ", 0) assert_equal 0, io.pos assert_equal "Hello, Warld!", io.read assert_equal 6, io.pwrite("world!", 7) assert_equal 13, io.pos io.pos = 0 assert_equal "Hello, world!", io.read end end assert('IO.pipe') do begin called = false IO.pipe do |r, w| assert_true r.kind_of?(IO) assert_true w.kind_of?(IO) assert_false r.closed? assert_false w.closed? assert_true FileTest.pipe?(r) assert_true FileTest.pipe?(w) assert_nil r.pid assert_nil w.pid assert_true 2 < r.fileno assert_true 2 < w.fileno assert_true r.fileno != w.fileno assert_false r.sync assert_true w.sync assert_equal 8, w.write('test for') assert_equal 'test', r.read(4) assert_equal ' for', r.read(4) assert_equal 5, w.write(' pipe') assert_equal nil, w.close assert_equal ' pipe', r.read called = true assert_raise(IOError) { r.write 'test' } # TODO: # This assert expect raise IOError but got RuntimeError # Because mruby-io not have flag for I/O readable # assert_raise(IOError) { w.read } end assert_true called assert_nothing_raised do IO.pipe { |r, w| r.close; w.close } end rescue NotImplementedError => e skip e.message end end assert('`cmd`') do begin assert_equal `#{$cmd}echo foo`, "foo#{$crlf}" rescue NotImplementedError => e skip e.message end end MRubyIOTestUtil.io_test_cleanup nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/test/PaxHeaders/file_test.rb0000644000000000000000000000013115077107276024353 xustar0030 mtime=1761382078.121420561 29 atime=1761382080.14141132 30 ctime=1761382108.676301607 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/test/file_test.rb0000644000175100017510000000546015077107276024751 0ustar00runnerrunner## # FileTest MRubyIOTestUtil.io_test_setup assert("FileTest.directory?") do dir = MRubyIOTestUtil.mkdtemp("mruby-io-test.XXXXXX") begin assert_true FileTest.directory?(dir) assert_false FileTest.directory?($mrbtest_io_rfname) ensure MRubyIOTestUtil.rmdir dir end end assert("FileTest.exist?") do assert_equal true, FileTest.exist?($mrbtest_io_rfname), "filename - exist" assert_equal false, FileTest.exist?($mrbtest_io_rfname + "-"), "filename - not exist" io = IO.new(IO.sysopen($mrbtest_io_rfname)) assert_equal true, FileTest.exist?(io), "io obj - exist" io.close assert_equal true, io.closed? assert_raise(IOError) { FileTest.exist?(io) } assert_raise(TypeError) { File.exist?($mrbtest_io_rfname.to_sym) } end assert("FileTest.file?") do dir = MRubyIOTestUtil.mkdtemp("mruby-io-test.XXXXXX") begin assert_true FileTest.file?($mrbtest_io_rfname) assert_false FileTest.file?(dir) ensure MRubyIOTestUtil.rmdir dir end end assert("FileTest.pipe?") do begin assert_equal false, FileTest.pipe?("/tmp") io = IO.popen("ls") assert_equal true, FileTest.pipe?(io) rescue NotImplementedError => e skip e.message end end assert('FileTest.size') do assert_equal FileTest.size($mrbtest_io_rfname), $mrbtest_io_msg.size assert_equal FileTest.size($mrbtest_io_wfname), 0 end assert("FileTest.size?") do assert_equal $mrbtest_io_msg.size, FileTest.size?($mrbtest_io_rfname) assert_equal nil, FileTest.size?($mrbtest_io_wfname) assert_equal nil, FileTest.size?("not-exist-test-target-file") fp1 = File.open($mrbtest_io_rfname) fp2 = File.open($mrbtest_io_wfname) assert_equal $mrbtest_io_msg.size, FileTest.size?(fp1) assert_equal nil, FileTest.size?(fp2) fp1.close fp2.close assert_raise IOError do FileTest.size?(fp1) end assert_true fp1.closed? assert_raise IOError do FileTest.size?(fp2) end assert_true fp2.closed? end assert("FileTest.socket?") do begin assert_true FileTest.socket?($mrbtest_io_socketname) rescue NotImplementedError => e skip e.message end end assert("FileTest.symlink?") do begin assert_true FileTest.symlink?($mrbtest_io_symlinkname) rescue NotImplementedError => e skip e.message end end assert("FileTest.zero?") do assert_equal false, FileTest.zero?($mrbtest_io_rfname) assert_equal true, FileTest.zero?($mrbtest_io_wfname) assert_equal false, FileTest.zero?("not-exist-test-target-file") fp1 = File.open($mrbtest_io_rfname) fp2 = File.open($mrbtest_io_wfname) assert_equal false, FileTest.zero?(fp1) assert_equal true, FileTest.zero?(fp2) fp1.close fp2.close assert_raise IOError do FileTest.zero?(fp1) end assert_true fp1.closed? assert_raise IOError do FileTest.zero?(fp2) end assert_true fp2.closed? end MRubyIOTestUtil.io_test_cleanup nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/test/PaxHeaders/file.rb0000644000000000000000000000013015077107276023313 xustar0030 mtime=1761382078.121420561 29 atime=1761382080.14141132 29 ctime=1761382108.67530161 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/test/file.rb0000644000175100017510000003040115077107276023703 0ustar00runnerrunner## # File Test MRubyIOTestUtil.io_test_setup def assert_dirname_with_level(path, results) assert('dirname with level') do assert_raise(ArgumentError) { File.dirname path, -1 } results.each do |level, expect| assert_equal expect, File.dirname(path, level) end end end assert('File.class', '15.2.21') do assert_equal Class, File.class end assert('File.superclass', '15.2.21.2') do assert_equal IO, File.superclass end assert('File#initialize', '15.2.21.4.1') do io = File.open($mrbtest_io_rfname, "r") assert_nil io.close assert_raise IOError do io.close end end assert('File#path', '15.2.21.4.2') do io = File.open($mrbtest_io_rfname, "r") assert_equal $mrbtest_io_msg, io.read assert_equal $mrbtest_io_rfname, io.path io.close assert_equal $mrbtest_io_rfname, io.path assert_true io.closed? end assert('File.basename') do assert_equal '/', File.basename('//') assert_equal 'a', File.basename('/a/') assert_equal 'b', File.basename('/a/b') assert_equal 'b', File.basename('../a/b') assert_raise(ArgumentError) { File.basename("/a/b\0") } end assert('File.dirname') do assert_equal '.', File.dirname('') assert_equal '.', File.dirname('a') assert_equal '/', File.dirname('/a') assert_equal '/', File.dirname('/a/') assert_equal 'a', File.dirname('a/b') assert_equal 'a', File.dirname('a/b/') assert_equal 'a/b', File.dirname('a/b/c') assert_equal '/a', File.dirname('/a/b') assert_equal '/a', File.dirname('/a/b/') assert_equal '/a/b', File.dirname('/a/b/c') assert_equal '/a/b', File.dirname('/a/b/c/') assert_equal '/', File.dirname('/a//') assert_equal '/a', File.dirname('/a//b') assert_equal '/a/b', File.dirname('/a/b//c//') end assert('File.dirname (with level)') do assert_dirname_with_level '', { 0 => '.', 1 => '.', 2 => '.' } assert_dirname_with_level 'a', { 0 => 'a', 1 => '.', 2 => '.' } assert_dirname_with_level '/a', { 0 => '/a', 1 => '/', 2 => '/' } assert_dirname_with_level '/a/', { 0 => '/a/', 1 => '/', 2 => '/' } assert_dirname_with_level 'a/b', { 0 => 'a/b', 1 => 'a', 2 => '.' } assert_dirname_with_level 'a/b/', { 0 => 'a/b/', 1 => 'a', 2 => '.' } assert_dirname_with_level 'a/b/c', { 0 => 'a/b/c', 1 => 'a/b', 2 => 'a' } assert_dirname_with_level 'a/b/c/d', { 0 => 'a/b/c/d', 1 => 'a/b/c', 2 => 'a/b' } end unless MRubyIOTestUtil.win? assert('File.dirname (not Windows)') do assert_equal '/a', File.dirname('//a//b/') end else assert('File.dirname (on Windows)') do assert_equal 'c:.', File.dirname('c:') assert_equal 'c:.', File.dirname('c:a') assert_equal 'c:.', File.dirname('c:a/') assert_equal 'c:a', File.dirname('c:a/b') assert_equal 'c:/', File.dirname('c:/') assert_equal 'c:/', File.dirname('c:/a') assert_equal 'c:/', File.dirname('c:/a/') assert_equal 'c:/a', File.dirname('c:/a/b') assert_equal '//.', File.dirname('//.') assert_equal '//.', File.dirname('//./') assert_equal '//./a', File.dirname('//./a') assert_equal '//./a', File.dirname('//./a/') assert_equal '//./a', File.dirname('//./a/b') assert_equal '//./a/b', File.dirname('//./a/b/c') assert_equal '//?', File.dirname('//?/') assert_equal '//?/a', File.dirname('//?/a') assert_equal '//?/a', File.dirname('//?/a/') assert_equal '//?/a', File.dirname('//?/a/b') assert_equal '//host1', File.dirname('//host1/') assert_equal '//host1/a', File.dirname('//host1/a') assert_equal '//host1/a', File.dirname('//host1/a/') assert_equal '//host1/a', File.dirname('//host1/a/b') end end assert('File.extname') do assert_equal '.txt', File.extname('foo/foo.txt') assert_equal '.gz', File.extname('foo/foo.tar.gz') assert_equal '', File.extname('foo/bar') assert_equal '', File.extname('foo/.bar') assert_equal '', File.extname('foo.txt/bar') assert_equal '', File.extname('.foo') assert_equal '.rb', File.extname('.a.rb') assert_equal '.', File.extname('foo.') end assert('File#flock') do f = File.open $mrbtest_io_rfname begin assert_equal(f.flock(File::LOCK_SH), 0) assert_equal(f.flock(File::LOCK_UN), 0) assert_equal(f.flock(File::LOCK_EX | File::LOCK_NB), 0) assert_equal(f.flock(File::LOCK_UN), 0) rescue NotImplementedError => e skip e.message ensure f.close end end assert('File#atime') do begin File.open("#{$mrbtest_io_wfname}.atime", 'w') do |f| assert_equal Time, f.mtime.class File.open("#{$mrbtest_io_wfname}.atime", 'r') do |f2| assert_equal true, f.atime == f2.atime end end ensure File.delete("#{$mrbtest_io_wfname}.atime") end end assert('File#ctime') do begin File.open("#{$mrbtest_io_wfname}.ctime", 'w') do |f| assert_equal Time, f.ctime.class File.open("#{$mrbtest_io_wfname}.ctime", 'r') do |f2| assert_equal true, f.ctime == f2.ctime end end ensure File.delete("#{$mrbtest_io_wfname}.ctime") end end assert('File#mtime') do begin File.open("#{$mrbtest_io_wfname}.mtime", 'w') do |f| assert_equal Time, f.mtime.class File.open("#{$mrbtest_io_wfname}.mtime", 'r') do |f2| assert_equal true, f.mtime == f2.mtime end end ensure File.delete("#{$mrbtest_io_wfname}.mtime") end end assert('File#size and File#truncate') do fname = "#{$mrbtest_io_wfname}.resize" begin File.open(fname, 'w') do |f| assert_equal 0, f.size assert_equal 0, f.truncate(100) assert_equal 100, f.size assert_equal 0, f.pos assert_equal 0, f.truncate(5) assert_equal 5, f.size end ensure File.delete(fname) end end assert('File.join') do assert_equal "", File.join() assert_equal "a", File.join("a") assert_equal "/a", File.join("/a") assert_equal "a/", File.join("a/") assert_equal "a/b/c", File.join("a", "b", "c") assert_equal "/a/b/c", File.join("/a", "b", "c") assert_equal "a/b/c/", File.join("a", "b", "c/") assert_equal "a/b/c", File.join("a/", "/b/", "/c") assert_equal "a/b/c", File.join(["a", "b", "c"]) assert_equal "a/b/c", File.join("a", ["b", ["c"]]) end assert('File.realpath') do dir = MRubyIOTestUtil.mkdtemp("mruby-io-test.XXXXXX") begin sep = File::ALT_SEPARATOR || File::SEPARATOR relative_path = "#{File.basename(dir)}#{sep}realpath_test" path = "#{MRubyIOTestUtil.getwd}#{sep}#{relative_path}" File.open(path, "w"){} assert_equal path, File.realpath(relative_path) unless MRubyIOTestUtil.win? path1 = File.realpath($mrbtest_io_rfname) path2 = File.realpath($mrbtest_io_symlinkname) assert_equal path1, path2 end ensure File.delete path rescue nil MRubyIOTestUtil.rmdir dir end assert_raise(ArgumentError) { File.realpath("TO\0DO") } end assert("File.readlink") do begin exp = File.basename($mrbtest_io_rfname) act = File.readlink($mrbtest_io_symlinkname) assert_equal exp, act rescue NotImplementedError => e skip e.message end end assert("File.readlink fails with non-symlink") do skip "readlink is not supported on this platform" if MRubyIOTestUtil.win? begin e2 = nil assert_raise(RuntimeError) { begin File.readlink($mrbtest_io_rfname) rescue => e if Object.const_defined?(:SystemCallError) and e.kind_of?(SystemCallError) raise RuntimeError, "SystemCallError converted to RuntimeError" end raise e rescue NotImplementedError => e e2 = e end } raise e2 if e2 rescue NotImplementedError => e skip e.message end end if MRubyIOTestUtil.win? assert('File.expand_path (for windows)') do drive = MRubyIOTestUtil.getwd[0, 2] alt1 = (drive.downcase != "c:") ? "c:" : "x:" alt2 = (drive.downcase != "d:") ? "d:" : "y:" assert_equal "#{drive}/", File.expand_path("..", "/tmp"), "parent path with base_dir (1)" assert_equal "#{drive}/tmp", File.expand_path("..", "/tmp/mruby"), "parent path with base_dir (2)" assert_equal "#{drive}/home", File.expand_path("/home"), "absolute" assert_equal "#{drive}/home", File.expand_path("/home", "."), "absolute with base_dir" assert_equal "#{drive}/hoge", File.expand_path("/tmp/..//hoge") assert_equal "//tmp/hoge", File.expand_path("////tmp/..///////hoge") assert_equal "#{drive}/", File.expand_path("../../../..", "/") assert_equal "#{drive}/", File.expand_path(([".."] * 100).join("/")) assert_equal "#{alt1}/x/y", File.expand_path("#{alt1}y", "/x") assert_equal "#{alt2}/x/y", File.expand_path("#{alt2}y", "/x") assert_equal "#{alt1}/x", File.expand_path("#{alt1}x", "#{alt2}y") assert_equal "#{alt1}/y/x", File.expand_path("#{alt1}x", "./y") end else assert('File.expand_path') do assert_equal "/", File.expand_path("..", "/tmp"), "parent path with base_dir (1)" assert_equal "/tmp", File.expand_path("..", "/tmp/mruby"), "parent path with base_dir (2)" assert_equal "/home", File.expand_path("/home"), "absolute" assert_equal "/home", File.expand_path("/home", "."), "absolute with base_dir" assert_equal "/hoge", File.expand_path("/tmp/..//hoge") assert_equal "/hoge", File.expand_path("////tmp/..///////hoge") assert_equal "/", File.expand_path("../../../..", "/") assert_equal "/", File.expand_path(([".."] * 100).join("/")) assert_equal "/x/c:y", File.expand_path("c:y", "/x") end end assert('File.expand_path (with getenv(3))') do skip unless MRubyIOTestUtil.const_defined?(:ENV_HOME) assert_equal MRubyIOTestUtil::ENV_HOME, File.expand_path("~/"), "home" assert_equal MRubyIOTestUtil::ENV_HOME, File.expand_path("~/", "/"), "home with base_dir" assert_equal "#{MRubyIOTestUtil::ENV_HOME}/user", File.expand_path("user", MRubyIOTestUtil::ENV_HOME), "relative with base_dir" end if MRubyIOTestUtil.win? assert('File.absolute_path? (for windows)') do assert_true File.absolute_path?("c:/") assert_true File.absolute_path?("c:/a") assert_true File.absolute_path?("//") assert_true File.absolute_path?("//?") assert_true File.absolute_path?("//?/") assert_false File.absolute_path?("") assert_false File.absolute_path?("/") assert_false File.absolute_path?("c:") end else assert('File.absolute_path?') do assert_true File.absolute_path?("/") assert_true File.absolute_path?("/a") assert_true File.absolute_path?("/a/b/") assert_false File.absolute_path?("") assert_false File.absolute_path?("a") assert_false File.absolute_path?("a/b/") assert_false File.absolute_path?("c:/") end end assert('File.path') do assert_equal "", File.path("") assert_equal "a/b/c", File.path("a/b/c") assert_equal "a/../b/./c", File.path("a/../b/./c") assert_raise(TypeError) { File.path(nil) } assert_raise(TypeError) { File.path(123) } end assert('File.symlink') do target_name = "/usr/bin" if !File.exist?(target_name) skip("target directory of File.symlink is not found") end begin tmpdir = MRubyIOTestUtil.mkdtemp("mruby-io-test.XXXXXX") rescue => e skip e.message end symlink_name = "#{tmpdir}/test-bin-dummy" begin assert_equal 0, File.symlink(target_name, symlink_name) assert_equal true, File.symlink?(symlink_name) rescue NotImplementedError => e skip e.message ensure File.delete symlink_name rescue nil MRubyIOTestUtil.rmdir tmpdir rescue nil end end assert('File.chmod') do File.open("#{$mrbtest_io_wfname}.chmod-test", 'w') {} begin assert_equal 1, File.chmod(0400, "#{$mrbtest_io_wfname}.chmod-test") ensure File.delete("#{$mrbtest_io_wfname}.chmod-test") end end assert('File.open with "x" mode') do File.unlink $mrbtest_io_wfname rescue nil assert_nothing_raised do File.open($mrbtest_io_wfname, "wx") {} end assert_raise(RuntimeError) do File.open($mrbtest_io_wfname, "wx") {} end File.unlink $mrbtest_io_wfname rescue nil assert_nothing_raised do File.open($mrbtest_io_wfname, "w+x") {} end assert_raise(RuntimeError) do File.open($mrbtest_io_wfname, "w+x") {} end assert_raise(ArgumentError) do File.open($mrbtest_io_wfname, "rx") {} end assert_raise(ArgumentError) do File.open($mrbtest_io_wfname, "ax") {} end end MRubyIOTestUtil.io_test_cleanup nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/PaxHeaders/include0000644000000000000000000000013215077107334022433 xustar0030 mtime=1761382108.204302971 30 atime=1761382109.798298363 30 ctime=1761382108.204302971 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/include/0000755000175100017510000000000015077107334023100 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/include/PaxHeaders/mruby0000644000000000000000000000013215077107334023571 xustar0030 mtime=1761382108.204302971 30 atime=1761382109.798298363 30 ctime=1761382108.204302971 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/include/mruby/0000755000175100017510000000000015077107334024236 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/include/mruby/PaxHeaders/ext0000644000000000000000000000013215077107334024371 xustar0030 mtime=1761382108.688301572 30 atime=1761382109.798298363 30 ctime=1761382108.688301572 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/include/mruby/ext/0000755000175100017510000000000015077107334025036 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/include/mruby/ext/PaxHeaders/io.h0000644000000000000000000000013215077107276025233 xustar0030 mtime=1761382078.120420566 30 atime=1761382080.140411325 30 ctime=1761382108.688301572 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/include/mruby/ext/io.h0000644000175100017510000000403515077107276025625 0ustar00runnerrunner/* ** io.h - IO class */ #ifndef MRUBY_IO_H #define MRUBY_IO_H #include #ifdef MRB_NO_STDIO # error IO and File conflicts 'MRB_NO_STDIO' in your build configuration #endif #if defined(__cplusplus) extern "C" { #endif #if defined(MRB_NO_IO_PREAD_PWRITE) || defined(MRB_WITHOUT_IO_PREAD_PWRITE) # undef MRB_USE_IO_PREAD_PWRITE #elif !defined(MRB_USE_IO_PREAD_PWRITE) # if defined(__unix__) || defined(__MACH__) || defined(MRB_WITH_IO_PREAD_PWRITE) # define MRB_USE_IO_PREAD_PWRITE # endif #endif #define MRB_IO_BUF_SIZE 4096 struct mrb_io_buf { short start; short len; char mem[MRB_IO_BUF_SIZE]; }; struct mrb_io { unsigned int readable:1, writable:1, eof:1, sync:1, is_socket:1, close_fd:1, close_fd2:1; int fd; /* file descriptor, or -1 */ int fd2; /* file descriptor to write if it's different from fd, or -1 */ int pid; /* child's pid (for pipes) */ struct mrb_io_buf *buf; }; #define MRB_O_RDONLY 0x0000 #define MRB_O_WRONLY 0x0001 #define MRB_O_RDWR 0x0002 #define MRB_O_ACCMODE (MRB_O_RDONLY | MRB_O_WRONLY | MRB_O_RDWR) #define MRB_O_NONBLOCK 0x0004 #define MRB_O_APPEND 0x0008 #define MRB_O_SYNC 0x0010 #define MRB_O_NOFOLLOW 0x0020 #define MRB_O_CREAT 0x0040 #define MRB_O_TRUNC 0x0080 #define MRB_O_EXCL 0x0100 #define MRB_O_NOCTTY 0x0200 #define MRB_O_DIRECT 0x0400 #define MRB_O_BINARY 0x0800 #define MRB_O_SHARE_DELETE 0x1000 #define MRB_O_TMPFILE 0x2000 #define MRB_O_NOATIME 0x4000 #define MRB_O_DSYNC 0x00008000 #define MRB_O_RSYNC 0x00010000 #define E_IO_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(IOError)) #define E_EOF_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(EOFError)) int mrb_io_fileno(mrb_state *mrb, mrb_value io); #if defined(__cplusplus) } /* extern "C" { */ #endif #endif /* MRUBY_IO_H */ nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/PaxHeaders/src0000644000000000000000000000013215077107334021577 xustar0030 mtime=1761382108.686301578 30 atime=1761382109.798298363 30 ctime=1761382108.686301578 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/src/0000755000175100017510000000000015077107334022244 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/src/PaxHeaders/io.c0000644000000000000000000000013215077107276022434 xustar0030 mtime=1761382078.120420566 30 atime=1761382080.140411325 30 ctime=1761382108.685301581 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/src/io.c0000644000175100017510000013544615077107276023041 0ustar00runnerrunner/* ** io.c - IO class */ #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(_WIN32) #include #include #include #define open _open #define close _close #define dup _dup #define dup2 _dup2 #define read _read #define write _write #define lseek _lseek #define isatty _isatty #define WEXITSTATUS(x) (x) typedef int fsize_t; typedef long ftime_t; typedef long fsuseconds_t; typedef int fmode_t; typedef int fssize_t; #ifndef O_TMPFILE #define O_TMPFILE O_TEMPORARY #endif #else #include #include #include typedef size_t fsize_t; typedef time_t ftime_t; #ifdef __DJGPP__ typedef long fsuseconds_t; #else typedef suseconds_t fsuseconds_t; #endif typedef mode_t fmode_t; typedef ssize_t fssize_t; #endif #ifdef _MSC_VER typedef mrb_int pid_t; #endif #include #include #include #define OPEN_ACCESS_MODE_FLAGS (O_RDONLY | O_WRONLY | O_RDWR) #define OPEN_RDONLY_P(f) ((mrb_bool)(((f) & OPEN_ACCESS_MODE_FLAGS) == O_RDONLY)) #define OPEN_WRONLY_P(f) ((mrb_bool)(((f) & OPEN_ACCESS_MODE_FLAGS) == O_WRONLY)) #define OPEN_RDWR_P(f) ((mrb_bool)(((f) & OPEN_ACCESS_MODE_FLAGS) == O_RDWR)) #define OPEN_READABLE_P(f) ((mrb_bool)(OPEN_RDONLY_P(f) || OPEN_RDWR_P(f))) #define OPEN_WRITABLE_P(f) ((mrb_bool)(OPEN_WRONLY_P(f) || OPEN_RDWR_P(f))) static void io_free(mrb_state *mrb, void *ptr); struct mrb_data_type mrb_io_type = { "IO", io_free }; static int io_modestr_to_flags(mrb_state *mrb, const char *modestr); static int io_mode_to_flags(mrb_state *mrb, mrb_value mode); static void fptr_finalize(mrb_state *mrb, struct mrb_io *fptr, int quiet); static struct mrb_io* io_get_open_fptr(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr = (struct mrb_io*)mrb_data_get_ptr(mrb, io, &mrb_io_type); if (fptr == NULL) { mrb_raise(mrb, E_IO_ERROR, "uninitialized stream"); } if (fptr->fd < 0) { mrb_raise(mrb, E_IO_ERROR, "closed stream"); } return fptr; } #if !defined(MRB_NO_IO_POPEN) && defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE # define MRB_NO_IO_POPEN 1 #endif #ifndef MRB_NO_IO_POPEN static void io_set_process_status(mrb_state *mrb, pid_t pid, int status) { struct RClass *c_status = NULL; mrb_value v; if (mrb_class_defined_id(mrb, MRB_SYM(Process))) { struct RClass *c_process = mrb_module_get_id(mrb, MRB_SYM(Process)); if (mrb_const_defined(mrb, mrb_obj_value(c_process), MRB_SYM(Status))) { c_status = mrb_class_get_under_id(mrb, c_process, MRB_SYM(Status)); } } if (c_status != NULL) { v = mrb_funcall_id(mrb, mrb_obj_value(c_status), MRB_SYM(new), 2, mrb_fixnum_value(pid), mrb_fixnum_value(status)); } else { v = mrb_fixnum_value(WEXITSTATUS(status)); } mrb_gv_set(mrb, mrb_intern_lit(mrb, "$?"), v); } #endif static int io_modestr_to_flags(mrb_state *mrb, const char *mode) { int flags; const char *m = mode; switch (*m++) { case 'r': flags = O_RDONLY; break; case 'w': flags = O_WRONLY | O_CREAT | O_TRUNC; break; case 'a': flags = O_WRONLY | O_CREAT | O_APPEND; break; default: goto modeerr; } while (*m) { switch (*m++) { case 'b': #ifdef O_BINARY flags |= O_BINARY; #endif break; case 'x': if (mode[0] != 'w') goto modeerr; flags |= O_EXCL; break; case '+': flags = (flags & ~OPEN_ACCESS_MODE_FLAGS) | O_RDWR; break; case ':': /* XXX: PASSTHROUGH*/ default: goto modeerr; } } return flags; modeerr: mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %s", mode); return 0; /* not reached */ } static int io_mode_to_flags(mrb_state *mrb, mrb_value mode) { if (mrb_nil_p(mode)) { return O_RDONLY; } else if (mrb_string_p(mode)) { return io_modestr_to_flags(mrb, RSTRING_CSTR(mrb, mode)); } else { int flags = 0; mrb_int flags0 = mrb_as_int(mrb, mode); switch (flags0 & MRB_O_ACCMODE) { case MRB_O_RDONLY: flags |= O_RDONLY; break; case MRB_O_WRONLY: flags |= O_WRONLY; break; case MRB_O_RDWR: flags |= O_RDWR; break; default: mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %v", mode); } if (flags0 & MRB_O_APPEND) flags |= O_APPEND; if (flags0 & MRB_O_CREAT) flags |= O_CREAT; if (flags0 & MRB_O_EXCL) flags |= O_EXCL; if (flags0 & MRB_O_TRUNC) flags |= O_TRUNC; #ifdef O_NONBLOCK if (flags0 & MRB_O_NONBLOCK) flags |= O_NONBLOCK; #endif #ifdef O_NOCTTY if (flags0 & MRB_O_NOCTTY) flags |= O_NOCTTY; #endif #ifdef O_BINARY if (flags0 & MRB_O_BINARY) flags |= O_BINARY; #endif #ifdef O_SHARE_DELETE if (flags0 & MRB_O_SHARE_DELETE) flags |= O_SHARE_DELETE; #endif #ifdef O_SYNC if (flags0 & MRB_O_SYNC) flags |= O_SYNC; #endif #ifdef O_DSYNC if (flags0 & MRB_O_DSYNC) flags |= O_DSYNC; #endif #ifdef O_RSYNC if (flags0 & MRB_O_RSYNC) flags |= O_RSYNC; #endif #ifdef O_NOFOLLOW if (flags0 & MRB_O_NOFOLLOW) flags |= O_NOFOLLOW; #endif #ifdef O_NOATIME if (flags0 & MRB_O_NOATIME) flags |= O_NOATIME; #endif #ifdef O_DIRECT if (flags0 & MRB_O_DIRECT) flags |= O_DIRECT; #endif #ifdef O_TMPFILE if (flags0 & MRB_O_TMPFILE) flags |= O_TMPFILE; #endif return flags; } } static void io_fd_cloexec(mrb_state *mrb, int fd) { #if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC) int flags = fcntl(fd, F_GETFD); int flags2; if (flags < 0) { mrb_sys_fail(mrb, "cloexec GETFD"); } if (fd <= 2) { flags2 = flags & ~FD_CLOEXEC; /* Clear CLOEXEC for standard file descriptors: 0, 1, 2. */ } else { flags2 = flags | FD_CLOEXEC; /* Set CLOEXEC for non-standard file descriptors: 3, 4, 5, ... */ } if (flags != flags2) { if (fcntl(fd, F_SETFD, flags2) < 0) { mrb_sys_fail(mrb, "cloexec SETFD"); } } #endif } #if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) static int io_cloexec_pipe(mrb_state *mrb, int fildes[2]) { int ret = pipe(fildes); if (ret == -1) return -1; io_fd_cloexec(mrb, fildes[0]); io_fd_cloexec(mrb, fildes[1]); return ret; } static int io_pipe(mrb_state *mrb, int pipes[2]) { int ret = io_cloexec_pipe(mrb, pipes); if (ret == -1) { if (errno == EMFILE || errno == ENFILE) { mrb_garbage_collect(mrb); ret = io_cloexec_pipe(mrb, pipes); } } return ret; } static int io_process_exec(const char *pname) { const char *s = pname; while (*s == ' ' || *s == '\t' || *s == '\n') s++; if (!*s) { errno = ENOENT; return -1; } execl("/bin/sh", "sh", "-c", pname, (char*)NULL); return -1; } #endif static void io_free(mrb_state *mrb, void *ptr) { struct mrb_io *io = (struct mrb_io*)ptr; if (io != NULL) { fptr_finalize(mrb, io, TRUE); mrb_free(mrb, io); } } static void io_init_buf(mrb_state *mrb, struct mrb_io *fptr) { if (fptr->readable) { fptr->buf = (struct mrb_io_buf*)mrb_malloc(mrb, sizeof(struct mrb_io_buf)); fptr->buf->start = 0; fptr->buf->len = 0; } } static struct mrb_io * io_alloc(mrb_state *mrb) { struct mrb_io *fptr = (struct mrb_io*)mrb_malloc(mrb, sizeof(struct mrb_io)); fptr->fd = -1; fptr->fd2 = -1; fptr->pid = 0; fptr->buf = 0; fptr->readable = 0; fptr->writable = 0; fptr->sync = 0; fptr->eof = 0; fptr->is_socket = 0; fptr->close_fd = 1; fptr->close_fd2 = 1; return fptr; } #ifndef NOFILE #define NOFILE 64 #endif #ifdef MRB_NO_IO_POPEN # define io_s_popen mrb_notimplement_m #else static int option_to_fd(mrb_state *mrb, mrb_value v) { if (mrb_undef_p(v)) return -1; if (mrb_nil_p(v)) return -1; switch (mrb_type(v)) { case MRB_TT_CDATA: /* IO */ return mrb_io_fileno(mrb, v); case MRB_TT_INTEGER: return (int)mrb_integer(v); default: mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong exec redirect action"); break; } return -1; /* never reached */ } static mrb_value io_s_popen_args(mrb_state *mrb, mrb_value klass, const char **cmd, int *flags, int *doexec, int *opt_in, int *opt_out, int *opt_err) { mrb_value mode = mrb_nil_value(); struct { mrb_value opt_in, opt_out, opt_err; } kv; mrb_sym knames[3] = {MRB_SYM(in), MRB_SYM(out), MRB_SYM(err)}; const mrb_kwargs kw = { 3, 0, knames, &kv.opt_in, NULL, }; mrb_get_args(mrb, "zo:", cmd, &mode, &kw); *flags = io_mode_to_flags(mrb, mode); *doexec = (strcmp("-", *cmd) != 0); *opt_in = option_to_fd(mrb, kv.opt_in); *opt_out = option_to_fd(mrb, kv.opt_out); *opt_err = option_to_fd(mrb, kv.opt_err); return mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type)); } #ifdef _WIN32 static mrb_value io_s_popen(mrb_state *mrb, mrb_value klass) { int doexec; int opt_in, opt_out, opt_err; const char *cmd; struct mrb_io *fptr; int pid = 0, flags; STARTUPINFO si; PROCESS_INFORMATION pi; HANDLE ifd[2]; HANDLE ofd[2]; ifd[0] = INVALID_HANDLE_VALUE; ifd[1] = INVALID_HANDLE_VALUE; ofd[0] = INVALID_HANDLE_VALUE; ofd[1] = INVALID_HANDLE_VALUE; mrb->c->ci->mid = 0; mrb_value io = io_s_popen_args(mrb, klass, &cmd, &flags, &doexec, &opt_in, &opt_out, &opt_err); SECURITY_ATTRIBUTES saAttr; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; if (OPEN_READABLE_P(flags)) { if (!CreatePipe(&ofd[0], &ofd[1], &saAttr, 0) || !SetHandleInformation(ofd[0], HANDLE_FLAG_INHERIT, 0)) { mrb_sys_fail(mrb, "pipe"); } } if (OPEN_WRITABLE_P(flags)) { if (!CreatePipe(&ifd[0], &ifd[1], &saAttr, 0) || !SetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0)) { mrb_sys_fail(mrb, "pipe"); } } if (doexec) { ZeroMemory(&pi, sizeof(pi)); ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); si.dwFlags |= STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; si.dwFlags |= STARTF_USESTDHANDLES; if (OPEN_READABLE_P(flags)) { si.hStdOutput = ofd[1]; si.hStdError = ofd[1]; } if (OPEN_WRITABLE_P(flags)) { si.hStdInput = ifd[0]; } if (!CreateProcess( NULL, (char*)cmd, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &si, &pi)) { CloseHandle(ifd[0]); CloseHandle(ifd[1]); CloseHandle(ofd[0]); CloseHandle(ofd[1]); mrb_raisef(mrb, E_IO_ERROR, "command not found: %s", cmd); } CloseHandle(pi.hThread); CloseHandle(ifd[0]); CloseHandle(ofd[1]); pid = pi.dwProcessId; } fptr = io_alloc(mrb); fptr->fd = _open_osfhandle((intptr_t)ofd[0], 0); fptr->fd2 = _open_osfhandle((intptr_t)ifd[1], 0); fptr->pid = pid; fptr->readable = OPEN_READABLE_P(flags); fptr->writable = OPEN_WRITABLE_P(flags); io_init_buf(mrb, fptr); DATA_TYPE(io) = &mrb_io_type; DATA_PTR(io) = fptr; return io; } #else static mrb_value io_s_popen(mrb_state *mrb, mrb_value klass) { int doexec; int opt_in, opt_out, opt_err; const char *cmd; int pid, flags, write_fd = -1; int pr[2] = { -1, -1 }; int pw[2] = { -1, -1 }; mrb->c->ci->mid = 0; mrb_value io = io_s_popen_args(mrb, klass, &cmd, &flags, &doexec, &opt_in, &opt_out, &opt_err); if (OPEN_READABLE_P(flags)) { if (pipe(pr) == -1) { mrb_sys_fail(mrb, "pipe"); } io_fd_cloexec(mrb, pr[0]); io_fd_cloexec(mrb, pr[1]); } if (OPEN_WRITABLE_P(flags)) { if (pipe(pw) == -1) { if (pr[0] != -1) close(pr[0]); if (pr[1] != -1) close(pr[1]); mrb_sys_fail(mrb, "pipe"); } io_fd_cloexec(mrb, pw[0]); io_fd_cloexec(mrb, pw[1]); } if (!doexec) { fflush(stdout); fflush(stderr); } mrb_value result = mrb_nil_value(); switch (pid = fork()) { case 0: /* child */ if (opt_in != -1) { dup2(opt_in, 0); } if (opt_out != -1) { dup2(opt_out, 1); } if (opt_err != -1) { dup2(opt_err, 2); } if (OPEN_READABLE_P(flags)) { close(pr[0]); if (pr[1] != 1) { dup2(pr[1], 1); close(pr[1]); } } if (OPEN_WRITABLE_P(flags)) { close(pw[1]); if (pw[0] != 0) { dup2(pw[0], 0); close(pw[0]); } } if (doexec) { for (int fd = 3; fd < NOFILE; fd++) { close(fd); } io_process_exec(cmd); mrb_raisef(mrb, E_IO_ERROR, "command not found: %s", cmd); _exit(127); } result = mrb_nil_value(); break; default: /* parent */ { int fd; if (OPEN_RDWR_P(flags)) { close(pr[1]); fd = pr[0]; close(pw[0]); write_fd = pw[1]; } else if (OPEN_RDONLY_P(flags)) { close(pr[1]); fd = pr[0]; } else { close(pw[0]); fd = pw[1]; } struct mrb_io *fptr = io_alloc(mrb); fptr->fd = fd; fptr->fd2 = write_fd; fptr->pid = pid; fptr->readable = OPEN_READABLE_P(flags); fptr->writable = OPEN_WRITABLE_P(flags); io_init_buf(mrb, fptr); DATA_TYPE(io) = &mrb_io_type; DATA_PTR(io) = fptr; result = io; } break; case -1: /* error */ { int saved_errno = errno; if (OPEN_READABLE_P(flags)) { close(pr[0]); close(pr[1]); } if (OPEN_WRITABLE_P(flags)) { close(pw[0]); close(pw[1]); } errno = saved_errno; mrb_sys_fail(mrb, "pipe_open failed"); } break; } return result; } #endif /* _WIN32 */ #endif /* TARGET_OS_IPHONE */ static int symdup(mrb_state *mrb, int fd, mrb_bool *failed) { int new_fd; *failed = TRUE; if (fd < 0) return fd; new_fd = dup(fd); if (new_fd > 0) *failed = FALSE; return new_fd; } static mrb_value io_init_copy(mrb_state *mrb, mrb_value copy) { mrb_value orig = mrb_get_arg1(mrb); struct mrb_io *fptr_copy; struct mrb_io *fptr_orig; mrb_bool failed = TRUE; fptr_orig = io_get_open_fptr(mrb, orig); fptr_copy = (struct mrb_io*)DATA_PTR(copy); if (fptr_orig == fptr_copy) return copy; if (fptr_copy != NULL) { fptr_finalize(mrb, fptr_copy, FALSE); mrb_free(mrb, fptr_copy); } fptr_copy = (struct mrb_io*)io_alloc(mrb); fptr_copy->pid = fptr_orig->pid; fptr_copy->readable = fptr_orig->readable; fptr_copy->writable = fptr_orig->writable; fptr_copy->sync = fptr_orig->sync; fptr_copy->is_socket = fptr_orig->is_socket; io_init_buf(mrb, fptr_copy); DATA_TYPE(copy) = &mrb_io_type; DATA_PTR(copy) = fptr_copy; fptr_copy->fd = symdup(mrb, fptr_orig->fd, &failed); if (failed) { mrb_sys_fail(mrb, 0); } io_fd_cloexec(mrb, fptr_copy->fd); if (fptr_orig->fd2 != -1) { fptr_copy->fd2 = symdup(mrb, fptr_orig->fd2, &failed); if (failed) { close(fptr_copy->fd); mrb_sys_fail(mrb, 0); } io_fd_cloexec(mrb, fptr_copy->fd2); } return copy; } static void check_file_descriptor(mrb_state *mrb, mrb_int fd) { struct stat sb; int fdi = (int)fd; #if MRB_INT_MIN < INT_MIN || MRB_INT_MAX > INT_MAX if (fdi != fd) { errno = EBADF; goto badfd; } #endif #ifdef _WIN32 { DWORD err; int len = sizeof(err); if (getsockopt(fdi, SOL_SOCKET, SO_ERROR, (char*)&err, &len) == 0) { return; } } if (fdi < 0 || fdi > _getmaxstdio()) { errno = EBADF; goto badfd; } #endif /* _WIN32 */ if (fstat(fdi, &sb) == 0) return; if (errno == EBADF) goto badfd; return; badfd: mrb_sys_fail(mrb, "bad file descriptor"); } static mrb_value io_init(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr; mrb_int fd; mrb_value mode, opt; /* opt (Hash) will be ignored */ int flags; mode = opt = mrb_nil_value(); if (mrb_block_given_p(mrb)) { mrb_warn(mrb, "File.new() does not take block; use File.open() instead"); } mrb_get_args(mrb, "i|oH", &fd, &mode, &opt); switch (fd) { case 0: /* STDIN_FILENO */ case 1: /* STDOUT_FILENO */ case 2: /* STDERR_FILENO */ break; default: check_file_descriptor(mrb, fd); break; } flags = io_mode_to_flags(mrb, mode); fptr = (struct mrb_io*)DATA_PTR(io); if (fptr != NULL) { fptr_finalize(mrb, fptr, TRUE); mrb_free(mrb, fptr); } fptr = io_alloc(mrb); DATA_TYPE(io) = &mrb_io_type; DATA_PTR(io) = fptr; fptr->fd = (int)fd; fptr->readable = OPEN_READABLE_P(flags); fptr->writable = OPEN_WRITABLE_P(flags); io_init_buf(mrb, fptr); return io; } static void fptr_finalize(mrb_state *mrb, struct mrb_io *fptr, int quiet) { int saved_errno = 0; int limit = quiet ? 3 : 0; if (fptr == NULL) { return; } if (fptr->fd >= limit) { #ifdef _WIN32 if (fptr->is_socket) { if (fptr->close_fd && closesocket(fptr->fd) != 0) { saved_errno = WSAGetLastError(); } fptr->fd = -1; } #endif if (fptr->fd != -1 && fptr->close_fd) { if (close(fptr->fd) == -1) { saved_errno = errno; } } fptr->fd = -1; } if (fptr->fd2 >= limit) { if (fptr->close_fd2 && close(fptr->fd2) == -1) { if (saved_errno == 0) { saved_errno = errno; } } fptr->fd2 = -1; } #ifndef MRB_NO_IO_POPEN if (fptr->pid != 0) { #if !defined(_WIN32) pid_t pid; int status; do { pid = waitpid(fptr->pid, &status, 0); } while (pid == -1 && errno == EINTR); if (!quiet && pid == fptr->pid) { io_set_process_status(mrb, pid, status); } #else HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, fptr->pid); DWORD status; if (WaitForSingleObject(h, INFINITE) && GetExitCodeProcess(h, &status)) if (!quiet) io_set_process_status(mrb, fptr->pid, (int)status); CloseHandle(h); #endif fptr->pid = 0; /* Note: we don't raise an exception when waitpid(3) fails */ } #endif if (fptr->buf) { mrb_free(mrb, fptr->buf); fptr->buf = NULL; } if (!quiet && saved_errno != 0) { errno = saved_errno; mrb_sys_fail(mrb, "fptr_finalize failed"); } } static struct mrb_io* io_get_read_fptr(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr = io_get_open_fptr(mrb, io); if (!fptr->readable) { mrb_raise(mrb, E_IO_ERROR, "not opened for reading"); } return fptr; } static struct mrb_io* io_get_write_fptr(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr = io_get_open_fptr(mrb, io); if (!fptr->writable) { mrb_raise(mrb, E_IO_ERROR, "not opened for writing"); } return fptr; } static int io_get_write_fd(struct mrb_io *fptr) { if (fptr->fd2 == -1) { return fptr->fd; } else { return fptr->fd2; } } static mrb_value io_isatty(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr = io_get_open_fptr(mrb, io); if (isatty(fptr->fd) == 0) return mrb_false_value(); return mrb_true_value(); } static mrb_value io_s_for_fd(mrb_state *mrb, mrb_value klass) { struct RClass *c = mrb_class_ptr(klass); enum mrb_vtype ttype = MRB_INSTANCE_TT(c); /* copied from mrb_instance_alloc() */ if (ttype == 0) ttype = MRB_TT_OBJECT; mrb_value obj = mrb_obj_value((struct RObject*)mrb_obj_alloc(mrb, ttype, c)); return io_init(mrb, obj); } static mrb_value io_s_sysclose(mrb_state *mrb, mrb_value klass) { mrb_int fd; mrb->c->ci->mid = 0; mrb_get_args(mrb, "i", &fd); if (close((int)fd) == -1) { mrb_sys_fail(mrb, "close"); } return mrb_fixnum_value(0); } static int io_cloexec_open(mrb_state *mrb, const char *pathname, int flags, fmode_t mode) { int retry = FALSE; char *fname = mrb_locale_from_utf8(pathname, -1); int fd; #ifdef O_CLOEXEC /* O_CLOEXEC is available since Linux 2.6.23. Linux 2.6.18 silently ignore it. */ flags |= O_CLOEXEC; #elif defined O_NOINHERIT flags |= O_NOINHERIT; #endif reopen: fd = open(fname, flags, mode); if (fd == -1) { if (!retry) { switch (errno) { case ENFILE: case EMFILE: mrb_garbage_collect(mrb); retry = TRUE; goto reopen; } } mrb_sys_fail(mrb, RSTRING_CSTR(mrb, mrb_format(mrb, "open %s", pathname))); } mrb_locale_free(fname); if (fd <= 2) { io_fd_cloexec(mrb, fd); } return fd; } static mrb_value io_s_sysopen(mrb_state *mrb, mrb_value klass) { mrb_value path = mrb_nil_value(); mrb_value mode = mrb_nil_value(); mrb_int perm = -1; mrb_get_args(mrb, "S|oi", &path, &mode, &perm); if (perm < 0) { perm = 0666; } const char *pat = RSTRING_CSTR(mrb, path); int flags = io_mode_to_flags(mrb, mode); mrb_int fd = io_cloexec_open(mrb, pat, flags, (fmode_t)perm); return mrb_fixnum_value(fd); } static void eof_error(mrb_state *mrb) { mrb_raise(mrb, E_EOF_ERROR, "end of file reached"); } static mrb_value io_read_common(mrb_state *mrb, fssize_t (*readfunc)(int, void*, fsize_t, off_t), mrb_value io, mrb_value buf, mrb_int maxlen, off_t offset) { if (maxlen < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative expanding string size"); } else if (maxlen == 0) { return mrb_str_new(mrb, NULL, maxlen); } if (mrb_nil_p(buf)) { buf = mrb_str_new(mrb, NULL, maxlen); } if (RSTRING_LEN(buf) != maxlen) { buf = mrb_str_resize(mrb, buf, maxlen); } else { mrb_str_modify(mrb, RSTRING(buf)); } struct mrb_io *fptr = io_get_read_fptr(mrb, io); int ret = readfunc(fptr->fd, RSTRING_PTR(buf), (fsize_t)maxlen, offset); if (ret < 0) { mrb_sys_fail(mrb, "sysread failed"); } if (RSTRING_LEN(buf) != ret) { buf = mrb_str_resize(mrb, buf, ret); } if (ret == 0 && maxlen > 0) { fptr->eof = 1; eof_error(mrb); } return buf; } static fssize_t sysread(int fd, void *buf, fsize_t nbytes, off_t offset) { return (fssize_t)read(fd, buf, nbytes); } static mrb_value io_sysread(mrb_state *mrb, mrb_value io) { mrb_value buf = mrb_nil_value(); mrb_int maxlen; mrb_get_args(mrb, "i|S", &maxlen, &buf); return io_read_common(mrb, sysread, io, buf, maxlen, 0); } static mrb_value io_sysseek(mrb_state *mrb, mrb_value io) { mrb_int offset, whence = -1; mrb_get_args(mrb, "i|i", &offset, &whence); if (whence < 0) { whence = 0; } struct mrb_io *fptr = io_get_open_fptr(mrb, io); off_t pos = lseek(fptr->fd, (off_t)offset, (int)whence); if (pos == -1) { mrb_sys_fail(mrb, "sysseek"); } fptr->eof = 0; if (sizeof(off_t) > sizeof(mrb_int) && pos > (off_t)MRB_INT_MAX) { mrb_raise(mrb, E_IO_ERROR, "sysseek reached too far for mrb_int"); } return mrb_int_value(mrb, (mrb_int)pos); } static mrb_value io_seek(mrb_state *mrb, mrb_value io) { mrb_value pos = io_sysseek(mrb, io); struct mrb_io *fptr = io_get_open_fptr(mrb, io); if (fptr->buf) { fptr->buf->start = 0; fptr->buf->len = 0; } return pos; } static mrb_value io_write_common(mrb_state *mrb, fssize_t (*writefunc)(int, const void*, fsize_t, off_t), struct mrb_io *fptr, const void *buf, mrb_ssize blen, off_t offset) { int fd = io_get_write_fd(fptr); fssize_t length = writefunc(fd, buf, (fsize_t)blen, offset); if (length == -1) { mrb_sys_fail(mrb, "syswrite"); } return mrb_int_value(mrb, (mrb_int)length); } static fssize_t syswrite(int fd, const void *buf, fsize_t nbytes, off_t offset) { return (fssize_t)write(fd, buf, nbytes); } static mrb_value io_syswrite(mrb_state *mrb, mrb_value io) { mrb_value buf; mrb_get_args(mrb, "S", &buf); return io_write_common(mrb, syswrite, io_get_write_fptr(mrb, io), RSTRING_PTR(buf), RSTRING_LEN(buf), 0); } /* def write(string) */ /* str = string.is_a?(String) ? string : string.to_s */ /* return 0 if str.empty? */ /* len = syswrite(str) */ /* len */ /* end */ static mrb_int fd_write(mrb_state *mrb, int fd, mrb_value str) { fssize_t n; str = mrb_obj_as_string(mrb, str); fssize_t len = (fssize_t)RSTRING_LEN(str); if (len == 0) return 0; for (fssize_t sum=0; sumbuf && fptr->buf->len > 0) { off_t n; /* get current position */ n = lseek(fd, 0, SEEK_CUR); if (n == -1) mrb_sys_fail(mrb, "lseek"); /* move cursor */ n = lseek(fd, n - fptr->buf->len, SEEK_SET); if (n == -1) mrb_sys_fail(mrb, "lseek(2)"); fptr->buf->start = fptr->buf->len = 0; } mrb_int len = 0; if (mrb_get_argc(mrb) == 1) { len = fd_write(mrb, fd, mrb_get_arg1(mrb)); } else { mrb_value *argv; mrb_int argc; mrb_get_args(mrb, "*", &argv, &argc); while (argc--) { len += fd_write(mrb, fd, *argv++); } } return mrb_int_value(mrb, len); } static mrb_value io_close(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr; fptr = io_get_open_fptr(mrb, io); fptr_finalize(mrb, fptr, FALSE); return mrb_nil_value(); } static mrb_value io_close_write(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr = io_get_open_fptr(mrb, io); if (close((int)fptr->fd2) == -1) { mrb_sys_fail(mrb, "close"); } return mrb_nil_value(); } static mrb_value io_closed(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr = (struct mrb_io*)mrb_data_get_ptr(mrb, io, &mrb_io_type); if (fptr == NULL || fptr->fd >= 0) { return mrb_false_value(); } return mrb_true_value(); } static mrb_value io_pos(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr = io_get_open_fptr(mrb, io); off_t pos = lseek(fptr->fd, 0, SEEK_CUR); if (pos == -1) mrb_sys_fail(mrb, 0); if (fptr->buf) { return mrb_int_value(mrb, pos - fptr->buf->len); } else { return mrb_int_value(mrb, pos); } } static mrb_value io_pid(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr = io_get_open_fptr(mrb, io); if (fptr->pid > 0) { return mrb_fixnum_value(fptr->pid); } return mrb_nil_value(); } static struct timeval time2timeval(mrb_state *mrb, mrb_value time) { struct timeval t = { 0, 0 }; switch (mrb_type(time)) { case MRB_TT_INTEGER: t.tv_sec = (ftime_t)mrb_integer(time); t.tv_usec = 0; break; #ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: t.tv_sec = (ftime_t)mrb_float(time); t.tv_usec = (fsuseconds_t)((mrb_float(time) - t.tv_sec) * 1000000.0); break; #endif default: mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class"); } return t; } #if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) static mrb_value io_s_pipe(mrb_state *mrb, mrb_value klass) { int pipes[2]; if (io_pipe(mrb, pipes) == -1) { mrb_sys_fail(mrb, "pipe"); } mrb_value r = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type)); struct mrb_io *fptr_r = io_alloc(mrb); fptr_r->fd = pipes[0]; fptr_r->readable = 1; DATA_TYPE(r) = &mrb_io_type; DATA_PTR(r) = fptr_r; io_init_buf(mrb, fptr_r); mrb_value w = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type)); struct mrb_io *fptr_w = io_alloc(mrb); fptr_w->fd = pipes[1]; fptr_w->writable = 1; fptr_w->sync = 1; DATA_TYPE(w) = &mrb_io_type; DATA_PTR(w) = fptr_w; return mrb_assoc_new(mrb, r, w); } #endif static int mrb_io_read_data_pending(mrb_state *mrb, struct mrb_io *fptr) { if (fptr->buf && fptr->buf->len > 0) return 1; return 0; } static mrb_value io_s_select(mrb_state *mrb, mrb_value klass) { const mrb_value *argv; mrb_int argc; mrb_value read_io, list; struct mrb_io *fptr; int pending = 0; mrb_value result; int max = 0; int interrupt_flag = 0; mrb_get_args(mrb, "*", &argv, &argc); if (argc < 1 || argc > 4) { mrb_argnum_error(mrb, argc, 1, 4); } mrb_value timeout = mrb_nil_value(); mrb_value except = mrb_nil_value(); mrb_value write = mrb_nil_value(); if (argc > 3) timeout = argv[3]; if (argc > 2) except = argv[2]; if (argc > 1) write = argv[1]; mrb_value read = argv[0]; struct timeval *tp, timerec; if (mrb_nil_p(timeout)) { tp = NULL; } else { timerec = time2timeval(mrb, timeout); tp = &timerec; } fd_set pset, rset, *rp; FD_ZERO(&pset); if (!mrb_nil_p(read)) { mrb_check_type(mrb, read, MRB_TT_ARRAY); rp = &rset; FD_ZERO(rp); for (int i = 0; i < RARRAY_LEN(read); i++) { read_io = RARRAY_PTR(read)[i]; fptr = io_get_open_fptr(mrb, read_io); if (fptr->fd >= FD_SETSIZE) continue; FD_SET(fptr->fd, rp); if (mrb_io_read_data_pending(mrb, fptr)) { pending++; FD_SET(fptr->fd, &pset); } if (max < fptr->fd) max = fptr->fd; } if (pending) { timerec.tv_sec = timerec.tv_usec = 0; tp = &timerec; } } else { rp = NULL; } fd_set wset, *wp; if (!mrb_nil_p(write)) { mrb_check_type(mrb, write, MRB_TT_ARRAY); wp = &wset; FD_ZERO(wp); for (int i = 0; i < RARRAY_LEN(write); i++) { fptr = io_get_open_fptr(mrb, RARRAY_PTR(write)[i]); if (fptr->fd >= FD_SETSIZE) continue; FD_SET(fptr->fd, wp); if (max < fptr->fd) max = fptr->fd; if (fptr->fd2 >= 0) { FD_SET(fptr->fd2, wp); if (max < fptr->fd2) max = fptr->fd2; } } } else { wp = NULL; } fd_set eset, *ep; if (!mrb_nil_p(except)) { mrb_check_type(mrb, except, MRB_TT_ARRAY); ep = &eset; FD_ZERO(ep); for (int i = 0; i < RARRAY_LEN(except); i++) { fptr = io_get_open_fptr(mrb, RARRAY_PTR(except)[i]); if (fptr->fd >= FD_SETSIZE) continue; FD_SET(fptr->fd, ep); if (max < fptr->fd) max = fptr->fd; if (fptr->fd2 >= 0) { FD_SET(fptr->fd2, ep); if (max < fptr->fd2) max = fptr->fd2; } } } else { ep = NULL; } max++; int n; retry: n = select(max, rp, wp, ep, tp); if (n < 0) { #ifdef _WIN32 errno = WSAGetLastError(); if (errno != WSAEINTR) mrb_sys_fail(mrb, "select failed"); #else if (errno != EINTR) mrb_sys_fail(mrb, "select failed"); #endif if (tp == NULL) goto retry; interrupt_flag = 1; } if (!pending && n == 0) return mrb_nil_value(); result = mrb_ary_new_capa(mrb, 3); mrb_ary_push(mrb, result, rp ? mrb_ary_new(mrb) : mrb_ary_new_capa(mrb, 0)); mrb_ary_push(mrb, result, wp ? mrb_ary_new(mrb) : mrb_ary_new_capa(mrb, 0)); mrb_ary_push(mrb, result, ep ? mrb_ary_new(mrb) : mrb_ary_new_capa(mrb, 0)); if (interrupt_flag == 0) { if (rp) { list = RARRAY_PTR(result)[0]; for (int i = 0; i < RARRAY_LEN(read); i++) { fptr = io_get_open_fptr(mrb, RARRAY_PTR(read)[i]); if (FD_ISSET(fptr->fd, rp) || FD_ISSET(fptr->fd, &pset)) { mrb_ary_push(mrb, list, RARRAY_PTR(read)[i]); } } } if (wp) { list = RARRAY_PTR(result)[1]; for (int i = 0; i < RARRAY_LEN(write); i++) { fptr = io_get_open_fptr(mrb, RARRAY_PTR(write)[i]); if (FD_ISSET(fptr->fd, wp)) { mrb_ary_push(mrb, list, RARRAY_PTR(write)[i]); } else if (fptr->fd2 >= 0 && FD_ISSET(fptr->fd2, wp)) { mrb_ary_push(mrb, list, RARRAY_PTR(write)[i]); } } } if (ep) { list = RARRAY_PTR(result)[2]; for (int i = 0; i < RARRAY_LEN(except); i++) { fptr = io_get_open_fptr(mrb, RARRAY_PTR(except)[i]); if (FD_ISSET(fptr->fd, ep)) { mrb_ary_push(mrb, list, RARRAY_PTR(except)[i]); } else if (fptr->fd2 >= 0 && FD_ISSET(fptr->fd2, ep)) { mrb_ary_push(mrb, list, RARRAY_PTR(except)[i]); } } } } return result; } int mrb_io_fileno(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr = io_get_open_fptr(mrb, io); return fptr->fd; } static mrb_value io_fileno(mrb_state *mrb, mrb_value io) { int fd = mrb_io_fileno(mrb, io); return mrb_fixnum_value(fd); } #if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC) static mrb_value io_close_on_exec_p(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr = io_get_open_fptr(mrb, io); int ret; if (fptr->fd2 >= 0) { if ((ret = fcntl(fptr->fd2, F_GETFD)) == -1) mrb_sys_fail(mrb, "F_GETFD failed"); if (!(ret & FD_CLOEXEC)) return mrb_false_value(); } if ((ret = fcntl(fptr->fd, F_GETFD)) == -1) mrb_sys_fail(mrb, "F_GETFD failed"); if (!(ret & FD_CLOEXEC)) return mrb_false_value(); return mrb_true_value(); } #else # define io_close_on_exec_p mrb_notimplement_m #endif #if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC) static mrb_value io_set_close_on_exec(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr = io_get_open_fptr(mrb, io); mrb_bool b; mrb_get_args(mrb, "b", &b); int flag = b ? FD_CLOEXEC : 0; int ret; if (fptr->fd2 >= 0) { if ((ret = fcntl(fptr->fd2, F_GETFD)) == -1) mrb_sys_fail(mrb, "F_GETFD failed"); if ((ret & FD_CLOEXEC) != flag) { ret = (ret & ~FD_CLOEXEC) | flag; ret = fcntl(fptr->fd2, F_SETFD, ret); if (ret == -1) mrb_sys_fail(mrb, "F_SETFD failed"); } } if ((ret = fcntl(fptr->fd, F_GETFD)) == -1) mrb_sys_fail(mrb, "F_GETFD failed"); if ((ret & FD_CLOEXEC) != flag) { ret = (ret & ~FD_CLOEXEC) | flag; ret = fcntl(fptr->fd, F_SETFD, ret); if (ret == -1) mrb_sys_fail(mrb, "F_SETFD failed"); } return mrb_bool_value(b); } #else # define io_set_close_on_exec mrb_notimplement_m #endif static mrb_value io_set_sync(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr = io_get_open_fptr(mrb, io); mrb_bool b; mrb_get_args(mrb, "b", &b); fptr->sync = b; return mrb_bool_value(b); } static mrb_value io_sync(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr = io_get_open_fptr(mrb, io); return mrb_bool_value(fptr->sync); } #ifndef MRB_USE_IO_PREAD_PWRITE # define io_pread mrb_notimplement_m # define io_pwrite mrb_notimplement_m #else static off_t value2off(mrb_state *mrb, mrb_value offv) { return (off_t)mrb_as_int(mrb, offv); } /* * call-seq: * pread(maxlen, offset, outbuf = "") -> outbuf */ static mrb_value io_pread(mrb_state *mrb, mrb_value io) { mrb_value buf = mrb_nil_value(); mrb_value off; mrb_int maxlen; mrb_get_args(mrb, "io|S!", &maxlen, &off, &buf); return io_read_common(mrb, pread, io, buf, maxlen, value2off(mrb, off)); } /* * call-seq: * pwrite(buffer, offset) -> wrote_bytes */ static mrb_value io_pwrite(mrb_state *mrb, mrb_value io) { mrb_value buf, off; mrb_get_args(mrb, "So", &buf, &off); return io_write_common(mrb, pwrite, io_get_write_fptr(mrb, io), RSTRING_PTR(buf), RSTRING_LEN(buf), value2off(mrb, off)); } #endif /* MRB_USE_IO_PREAD_PWRITE */ static mrb_value io_ungetc(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr = io_get_read_fptr(mrb, io); struct mrb_io_buf *buf = fptr->buf; mrb_value str; mrb_get_args(mrb, "S", &str); mrb_int len = RSTRING_LEN(str); if (len > SHRT_MAX) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string too long to ungetc"); } if (len > MRB_IO_BUF_SIZE - buf->len) { fptr->buf = (struct mrb_io_buf*)mrb_realloc(mrb, buf, sizeof(struct mrb_io_buf)+buf->len+len-MRB_IO_BUF_SIZE); buf = fptr->buf; } memmove(buf->mem+len, buf->mem+buf->start, buf->len); memcpy(buf->mem, RSTRING_PTR(str), len); buf->start = 0; buf->len += (short)len; return mrb_nil_value(); } static void io_buf_reset(struct mrb_io_buf *buf) { buf->start = 0; buf->len = 0; } static void io_buf_shift(struct mrb_io_buf *buf, mrb_int n) { mrb_assert(n <= SHRT_MAX); buf->start += (short)n; buf->len -= (short)n; } #ifdef MRB_UTF8_STRING static void io_fill_buf_comp(mrb_state *mrb, struct mrb_io *fptr) { struct mrb_io_buf *buf = fptr->buf; int keep = buf->len; memmove(buf->mem, buf->mem+buf->start, keep); int n = read(fptr->fd, buf->mem+keep, MRB_IO_BUF_SIZE-keep); if (n < 0) mrb_sys_fail(mrb, 0); if (n == 0) fptr->eof = 1; buf->start = 0; buf->len += (short)n; } #endif static void io_fill_buf(mrb_state *mrb, struct mrb_io *fptr) { struct mrb_io_buf *buf = fptr->buf; if (buf->len > 0) return; int n = read(fptr->fd, buf->mem, MRB_IO_BUF_SIZE); if (n < 0) mrb_sys_fail(mrb, 0); if (n == 0) fptr->eof = 1; buf->start = 0; buf->len = (short)n; } static mrb_value io_eof(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr = io_get_read_fptr(mrb, io); if (fptr->eof) return mrb_true_value(); if (fptr->buf->len > 0) return mrb_false_value(); io_fill_buf(mrb, fptr); return mrb_bool_value(fptr->eof); } static void io_buf_cat(mrb_state *mrb, mrb_value outbuf, struct mrb_io_buf *buf, mrb_int n) { mrb_assert(n <= buf->len); mrb_str_cat(mrb, outbuf, buf->mem+buf->start, n); io_buf_shift(buf, n); } static void io_buf_cat_all(mrb_state *mrb, mrb_value outbuf, struct mrb_io_buf *buf) { mrb_str_cat(mrb, outbuf, buf->mem+buf->start, buf->len); io_buf_reset(buf); } static mrb_value io_read_all(mrb_state *mrb, struct mrb_io *fptr, mrb_value outbuf) { for (;;) { io_fill_buf(mrb, fptr); if (fptr->eof) { return outbuf; } io_buf_cat_all(mrb, outbuf, fptr->buf); } } static mrb_value io_reset_outbuf(mrb_state *mrb, mrb_value outbuf, mrb_int len) { if (mrb_nil_p(outbuf)) { outbuf = mrb_str_new(mrb, NULL, 0); } else { mrb_str_modify(mrb, mrb_str_ptr(outbuf)); RSTR_SET_LEN(mrb_str_ptr(outbuf), 0); } return outbuf; } static mrb_value io_read(mrb_state *mrb, mrb_value io) { mrb_value outbuf = mrb_nil_value(); mrb_value len; mrb_int length = 0; mrb_bool length_given; struct mrb_io *fptr = io_get_read_fptr(mrb, io); mrb_get_args(mrb, "|o?S", &len, &length_given, &outbuf); if (length_given) { if (mrb_nil_p(len)) { length_given = FALSE; } else { length = mrb_as_int(mrb, len); if (length < 0) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative length %i given", length); } if (length == 0) { return io_reset_outbuf(mrb, outbuf, 0); } } } outbuf = io_reset_outbuf(mrb, outbuf, MRB_IO_BUF_SIZE); if (!length_given) { /* read as much as possible */ return io_read_all(mrb, fptr, outbuf); } struct mrb_io_buf *buf = fptr->buf; for (;;) { io_fill_buf(mrb, fptr); if (fptr->eof || length == 0) { if (RSTRING_LEN(outbuf) == 0) return mrb_nil_value(); return outbuf; } if (buf->len < length) { length -= buf->len; io_buf_cat_all(mrb, outbuf, buf); } else { io_buf_cat(mrb, outbuf, buf, length); return outbuf; } } } static mrb_int io_find_index(struct mrb_io *fptr, const char *rs, mrb_int rslen) { struct mrb_io_buf *buf = fptr->buf; mrb_assert(rslen > 0); const char c = rs[0]; const mrb_int limit = buf->len - rslen + 1; const char *p = buf->mem+buf->start; for (mrb_int i=0; ibuf; mrb_get_args(mrb, "|o?i?", &rs, &rs_given, &limit, &limit_given); if (limit_given == FALSE) { if (rs_given) { if (mrb_nil_p(rs)) { rs_given = FALSE; } else if (mrb_integer_p(rs)) { limit = mrb_integer(rs); limit_given = TRUE; rs = mrb_nil_value(); } else if (!mrb_string_p(rs)) { mrb_ensure_int_type(mrb, rs); } } } if (rs_given) { if (mrb_nil_p(rs)) { rs_given = FALSE; } else { mrb_ensure_string_type(mrb, rs); if (RSTRING_LEN(rs) == 0) { /* paragraph mode */ rs = mrb_str_new_lit(mrb, "\n\n"); } } } else { rs = mrb_str_new_lit(mrb, "\n"); rs_given = TRUE; } /* from now on rs_given==FALSE means no RS */ if (mrb_nil_p(rs) && !limit_given) { return io_read_all(mrb, fptr, mrb_str_new_capa(mrb, MRB_IO_BUF_SIZE)); } io_fill_buf(mrb, fptr); if (fptr->eof) return mrb_nil_value(); mrb_value outbuf; if (limit_given) { if (limit == 0) return mrb_str_new(mrb, NULL, 0); outbuf = mrb_str_new_capa(mrb, limit); } else { outbuf = mrb_str_new(mrb, NULL, 0); } for (;;) { if (rs_given) { /* with RS */ mrb_int rslen = RSTRING_LEN(rs); mrb_int idx = io_find_index(fptr, RSTRING_PTR(rs), rslen); if (idx >= 0) { /* found */ mrb_int n = idx+rslen; if (limit_given && limit < n) { n = limit; } io_buf_cat(mrb, outbuf, buf, n); return outbuf; } } if (limit_given) { if (limit <= buf->len) { io_buf_cat(mrb, outbuf, buf, limit); return outbuf; } limit -= buf->len; } io_buf_cat_all(mrb, outbuf, buf); io_fill_buf(mrb, fptr); if (fptr->eof) { if (RSTRING_LEN(outbuf) == 0) return mrb_nil_value(); return outbuf; } } } static mrb_value io_readline(mrb_state *mrb, mrb_value io) { mrb_value result = io_gets(mrb, io); if (mrb_nil_p(result)) { eof_error(mrb); } return result; } static mrb_value io_readlines(mrb_state *mrb, mrb_value io) { mrb_value ary = mrb_ary_new(mrb); for (;;) { mrb_value line = io_gets(mrb, io); if (mrb_nil_p(line)) return ary; mrb_ary_push(mrb, ary, line); } } static mrb_value io_getc(mrb_state *mrb, mrb_value io) { mrb_int len = 1; struct mrb_io *fptr = io_get_read_fptr(mrb, io); struct mrb_io_buf *buf = fptr->buf; io_fill_buf(mrb, fptr); if (fptr->eof) return mrb_nil_value(); #ifdef MRB_UTF8_STRING const char *p = &buf->mem[buf->start]; if ((*p) & 0x80) { len = mrb_utf8len(p, p+buf->len); if (len == 1 && buf->len < 4) { /* partial UTF-8 */ io_fill_buf_comp(mrb, fptr); p = &buf->mem[buf->start]; len = mrb_utf8len(p, p+buf->len); } } #endif mrb_value str = mrb_str_new(mrb, buf->mem+buf->start, len); io_buf_shift(buf, len); return str; } static mrb_value io_readchar(mrb_state *mrb, mrb_value io) { mrb_value result = io_getc(mrb, io); if (mrb_nil_p(result)) { eof_error(mrb); } return result; } static mrb_value io_getbyte(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr = io_get_read_fptr(mrb, io); struct mrb_io_buf *buf = fptr->buf; io_fill_buf(mrb, fptr); if (fptr->eof) return mrb_nil_value(); unsigned char c = buf->mem[buf->start]; io_buf_shift(buf, 1); return mrb_int_value(mrb, (mrb_int)c); } static mrb_value io_readbyte(mrb_state *mrb, mrb_value io) { mrb_value result = io_getbyte(mrb, io); if (mrb_nil_p(result)) { eof_error(mrb); } return result; } static mrb_value io_flush(mrb_state *mrb, mrb_value io) { io_get_open_fptr(mrb, io); return io; } void mrb_init_io(mrb_state *mrb) { struct RClass *io = mrb_define_class_id(mrb, MRB_SYM(IO), mrb->object_class); MRB_SET_INSTANCE_TT(io, MRB_TT_CDATA); mrb_include_module(mrb, io, mrb_module_get_id(mrb, MRB_SYM(Enumerable))); /* 15.2.20.3 */ mrb_define_class_method_id(mrb, io, MRB_SYM(_popen), io_s_popen, MRB_ARGS_ARG(1,2)); mrb_define_class_method_id(mrb, io, MRB_SYM(_sysclose), io_s_sysclose, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, io, MRB_SYM(for_fd), io_s_for_fd, MRB_ARGS_ARG(1,2)); mrb_define_class_method_id(mrb, io, MRB_SYM(select), io_s_select, MRB_ARGS_ARG(1,3)); mrb_define_class_method_id(mrb, io, MRB_SYM(sysopen), io_s_sysopen, MRB_ARGS_ARG(1,2)); #if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) mrb_define_class_method_id(mrb, io, MRB_SYM(_pipe), io_s_pipe, MRB_ARGS_NONE()); #endif mrb_define_method_id(mrb, io, MRB_SYM(initialize), io_init, MRB_ARGS_ARG(1,2)); mrb_define_method_id(mrb, io, MRB_SYM(initialize_copy), io_init_copy, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, io, MRB_SYM(isatty), io_isatty, MRB_ARGS_NONE()); mrb_define_method_id(mrb, io, MRB_SYM_Q(eof), io_eof, MRB_ARGS_NONE()); /* 15.2.20.5.6 */ mrb_define_method_id(mrb, io, MRB_SYM(getc), io_getc, MRB_ARGS_NONE()); /* 15.2.20.5.8 */ mrb_define_method_id(mrb, io, MRB_SYM(gets), io_gets, MRB_ARGS_OPT(2)); /* 15.2.20.5.9 */ mrb_define_method_id(mrb, io, MRB_SYM(read), io_read, MRB_ARGS_OPT(2)); /* 15.2.20.5.14 */ mrb_define_method_id(mrb, io, MRB_SYM(readchar), io_readchar, MRB_ARGS_NONE()); /* 15.2.20.5.15 */ mrb_define_method_id(mrb, io, MRB_SYM(readline), io_readline, MRB_ARGS_OPT(2)); /* 15.2.20.5.16 */ mrb_define_method_id(mrb, io, MRB_SYM(readlines), io_readlines, MRB_ARGS_OPT(2)); /* 15.2.20.5.17 */ mrb_define_method_id(mrb, io, MRB_SYM(sync), io_sync, MRB_ARGS_NONE()); /* 15.2.20.5.18 */ mrb_define_method_id(mrb, io, MRB_SYM_E(sync), io_set_sync, MRB_ARGS_REQ(1)); /* 15.2.20.5.19 */ mrb_define_method_id(mrb, io, MRB_SYM(sysread), io_sysread, MRB_ARGS_ARG(1,1)); mrb_define_method_id(mrb, io, MRB_SYM(sysseek), io_sysseek, MRB_ARGS_ARG(1,1)); mrb_define_method_id(mrb, io, MRB_SYM(syswrite), io_syswrite, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, io, MRB_SYM(seek), io_seek, MRB_ARGS_ARG(1,1)); mrb_define_method_id(mrb, io, MRB_SYM(close), io_close, MRB_ARGS_NONE()); /* 15.2.20.5.1 */ mrb_define_method_id(mrb, io, MRB_SYM(close_write), io_close_write, MRB_ARGS_NONE()); mrb_define_method_id(mrb, io, MRB_SYM_E(close_on_exec), io_set_close_on_exec, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, io, MRB_SYM_Q(close_on_exec), io_close_on_exec_p, MRB_ARGS_NONE()); mrb_define_method_id(mrb, io, MRB_SYM_Q(closed), io_closed, MRB_ARGS_NONE()); /* 15.2.20.5.2 */ mrb_define_method_id(mrb, io, MRB_SYM(flush), io_flush, MRB_ARGS_NONE()); /* 15.2.20.5.7 */ mrb_define_method_id(mrb, io, MRB_SYM(ungetc), io_ungetc, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, io, MRB_SYM(pos), io_pos, MRB_ARGS_NONE()); mrb_define_method_id(mrb, io, MRB_SYM(pid), io_pid, MRB_ARGS_NONE()); mrb_define_method_id(mrb, io, MRB_SYM(fileno), io_fileno, MRB_ARGS_NONE()); mrb_define_method_id(mrb, io, MRB_SYM(write), io_write, MRB_ARGS_ANY()); /* 15.2.20.5.20 */ mrb_define_method_id(mrb, io, MRB_SYM(pread), io_pread, MRB_ARGS_ANY()); /* ruby 2.5 feature */ mrb_define_method_id(mrb, io, MRB_SYM(pwrite), io_pwrite, MRB_ARGS_ANY()); /* ruby 2.5 feature */ mrb_define_method_id(mrb, io, MRB_SYM(getbyte), io_getbyte, MRB_ARGS_NONE()); mrb_define_method_id(mrb, io, MRB_SYM(readbyte), io_readbyte, MRB_ARGS_NONE()); mrb_define_const_id(mrb, io, MRB_SYM(SEEK_SET), mrb_fixnum_value(SEEK_SET)); mrb_define_const_id(mrb, io, MRB_SYM(SEEK_CUR), mrb_fixnum_value(SEEK_CUR)); mrb_define_const_id(mrb, io, MRB_SYM(SEEK_END), mrb_fixnum_value(SEEK_END)); } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/src/PaxHeaders/file.c0000644000000000000000000000013215077107276022744 xustar0030 mtime=1761382078.120420566 30 atime=1761382080.140411325 30 ctime=1761382108.686301578 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/src/file.c0000644000175100017510000005676615077107276023360 0ustar00runnerrunner/* ** file.c - File class */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(_WIN32) #include #include #define NULL_FILE "NUL" #define UNLINK _unlink #define GETCWD _getcwd #define CHMOD(a, b) 0 #define MAXPATHLEN 1024 #if !defined(PATH_MAX) #define PATH_MAX _MAX_PATH #endif #define realpath(N,R) _fullpath((R),(N),_MAX_PATH) #include #else #define NULL_FILE "/dev/null" #include #define UNLINK unlink #define GETCWD getcwd #define CHMOD(a, b) chmod(a,b) #include #ifndef __DJGPP__ #include #endif #include #include #endif #define FILE_SEPARATOR "/" #if defined(_WIN32) #define PATH_SEPARATOR ";" #define FILE_ALT_SEPARATOR "\\" #define VOLUME_SEPARATOR ":" #define DIRSEP_P(ch) (((ch) == '/') | ((ch) == '\\')) #define VOLSEP_P(ch) ((ch) == ':') #define UNC_PATH_P(path) (DIRSEP_P((path)[0]) && DIRSEP_P((path)[1])) #define DRIVE_LETTER_P(path) (((size_t)(((path)[0]) | 0x20) - 'a' <= (size_t)'z' - 'a') && (path)[1] == ':') #define DRIVE_EQUAL_P(x, y) (((x)[0] | 0x20) == ((y)[0] | 0x20)) #else #define PATH_SEPARATOR ":" #define DIRSEP_P(ch) ((ch) == '/') #endif #ifndef LOCK_SH #define LOCK_SH 1 #endif #ifndef LOCK_EX #define LOCK_EX 2 #endif #ifndef LOCK_NB #define LOCK_NB 4 #endif #ifndef LOCK_UN #define LOCK_UN 8 #endif #if !defined(_WIN32) || defined(MRB_MINGW32_LEGACY) typedef struct stat mrb_stat; # define mrb_stat(path, sb) stat(path, sb) # define mrb_fstat(fd, sb) fstat(fd, sb) #elif defined MRB_INT32 typedef struct _stat32 mrb_stat; # define mrb_stat(path, sb) _stat32(path, sb) # define mrb_fstat(fd, sb) _fstat32(fd, sb) #else typedef struct _stat64 mrb_stat; # define mrb_stat(path, sb) _stat64(path, sb) # define mrb_fstat(fd, sb) _fstat64(fd, sb) #endif #ifdef _WIN32 static int flock(int fd, int operation) { HANDLE h = (HANDLE)_get_osfhandle(fd); DWORD flags; flags = ((operation & LOCK_NB) ? LOCKFILE_FAIL_IMMEDIATELY : 0) | ((operation & LOCK_SH) ? LOCKFILE_EXCLUSIVE_LOCK : 0); static const OVERLAPPED zero_ov = {0}; OVERLAPPED ov = zero_ov; return LockFileEx(h, flags, 0, 0xffffffff, 0xffffffff, &ov) ? 0 : -1; } #endif static mrb_value mrb_file_s_umask(mrb_state *mrb, mrb_value klass) { #if defined(_WIN32) /* nothing to do on windows */ return mrb_fixnum_value(0); #else mrb_int mask, omask; if (mrb_get_args(mrb, "|i", &mask) == 0) { omask = umask(0); umask(omask); } else { omask = umask(mask); } return mrb_fixnum_value(omask); #endif } static mrb_value mrb_file_s_unlink(mrb_state *mrb, mrb_value obj) { const mrb_value *argv; mrb_int argc; mrb_get_args(mrb, "*", &argv, &argc); for (int i = 0; i < argc; i++) { mrb_value pathv = argv[i]; mrb_ensure_string_type(mrb, pathv); const char *utf8_path = RSTRING_CSTR(mrb, pathv); char *path = mrb_locale_from_utf8(utf8_path, -1); if (UNLINK(path) < 0) { mrb_locale_free(path); mrb_sys_fail(mrb, utf8_path); } mrb_locale_free(path); } return mrb_fixnum_value(argc); } static mrb_value mrb_file_s_rename(mrb_state *mrb, mrb_value obj) { mrb_value from, to; mrb_get_args(mrb, "SS", &from, &to); char *src = mrb_locale_from_utf8(RSTRING_CSTR(mrb, from), -1); char *dst = mrb_locale_from_utf8(RSTRING_CSTR(mrb, to), -1); if (rename(src, dst) < 0) { #if defined(_WIN32) if (CHMOD(dst, 0666) == 0 && UNLINK(dst) == 0 && rename(src, dst) == 0) { mrb_locale_free(src); mrb_locale_free(dst); return mrb_fixnum_value(0); } #endif mrb_locale_free(src); mrb_locale_free(dst); mrb_sys_fail(mrb, RSTRING_CSTR(mrb, mrb_format(mrb, "(%v, %v)", from, to))); return mrb_fixnum_value(-1); /* not reached */ } mrb_locale_free(src); mrb_locale_free(dst); return mrb_fixnum_value(0); } #define SKIP_DIRSEP(p) for (; DIRSEP_P(*(p)); (p)++) #define NEXT_DIRSEP(p) for (; *(p) != '\0' && !DIRSEP_P(*(p)); (p)++) static const char* scan_dirname(const char *path, mrb_int level) { const char *p = path + strlen(path); if (level < 1) return p; for (; p > path && DIRSEP_P(p[-1]); p--) ; for (; level > 0; level--) { for (; p > path && !DIRSEP_P(p[-1]); p--) ; for (; p > path && DIRSEP_P(p[-1]); p--) ; } return p > path ? p : path; } static mrb_value mrb_file_dirname(mrb_state *mrb, mrb_value klass) { const char *path; mrb_int level = 1; mrb_get_args(mrb, "z|i", &path, &level); if (level < 0) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level: %i", level); } const char *p = path; #ifdef _WIN32 if (UNC_PATH_P(p)) { p += 2; SKIP_DIRSEP(p); path = p - 2; /* if consecutive, point to the trailing slash */ NEXT_DIRSEP(p); const char *o = p; SKIP_DIRSEP(p); if (*p == '\0') { p = o; } else { NEXT_DIRSEP(p); p = scan_dirname(p, level); } return mrb_str_new(mrb, path, p - path); } else if (ISALPHA(p[0]) && p[1] == ':') { p += 2; const char *o = p; SKIP_DIRSEP(p); p = scan_dirname(p, level); mrb_value s = mrb_str_new(mrb, path, p - path); if (p == o) { mrb_str_cat_lit(mrb, s, "."); } return s; } #endif SKIP_DIRSEP(p); if (p > path) { path = p - 1; /* if consecutive, point to the trailing slash */ } p = scan_dirname(p, level); return (p == path) ? mrb_str_new_lit(mrb, ".") : mrb_str_new(mrb, path, p - path); } static mrb_value mrb_file_basename(mrb_state *mrb, mrb_value klass) { // NOTE: Do not use mrb_locale_from_utf8 here #if defined(_WIN32) char bname[_MAX_DIR]; char extname[_MAX_EXT]; char *path; mrb_get_args(mrb, "z", &path); size_t ridx = strlen(path); if (ridx > 0) { ridx--; while (ridx > 0 && (path[ridx] == '/' || path[ridx] == '\\')) { path[ridx] = '\0'; ridx--; } if (ridx == 0 && path[0] == '/') { return mrb_str_new_cstr(mrb, path); } } _splitpath((const char*)path, NULL, NULL, bname, extname); mrb_value buffer = mrb_str_new_cstr(mrb, bname); mrb_str_cat_cstr(mrb, buffer, extname); return buffer; #else char *path, *bname; mrb_get_args(mrb, "z", &path); if ((bname = basename(path)) == NULL) { mrb_sys_fail(mrb, "basename"); } if (strcmp(bname, "//") == 0) bname[1] = '\0'; /* patch for Cygwin */ return mrb_str_new_cstr(mrb, bname); #endif } static mrb_value mrb_file_realpath(mrb_state *mrb, mrb_value klass) { mrb_value pathname, dir_string; if (mrb_get_args(mrb, "S|S", &pathname, &dir_string) == 2) { mrb_value s = mrb_str_dup(mrb, dir_string); s = mrb_str_cat_cstr(mrb, s, FILE_SEPARATOR); s = mrb_str_append(mrb, s, pathname); pathname = s; } char *cpath = mrb_locale_from_utf8(RSTRING_CSTR(mrb, pathname), -1); mrb_value result = mrb_str_new_capa(mrb, PATH_MAX); if (realpath(cpath, RSTRING_PTR(result)) == NULL) { mrb_locale_free(cpath); mrb_sys_fail(mrb, RSTRING_CSTR(mrb, pathname)); return result; /* not reached */ } mrb_locale_free(cpath); mrb_str_resize(mrb, result, strlen(RSTRING_PTR(result))); return result; } static const char* path_getwd(mrb_state *mrb) { char buf[MAXPATHLEN]; if (GETCWD(buf, MAXPATHLEN) == NULL) { mrb_sys_fail(mrb, "getcwd(2)"); } char *utf8 = mrb_utf8_from_locale(buf, -1); mrb_value path = mrb_str_new_cstr(mrb, utf8); mrb_utf8_free(utf8); return RSTRING_CSTR(mrb, path); } static mrb_bool path_absolute_p(const char *path) { #ifdef _WIN32 return UNC_PATH_P(path) || (ISALPHA(path[0]) && VOLSEP_P(path[1]) && DIRSEP_P(path[2])); #else return DIRSEP_P(path[0]); #endif } static void path_parse(mrb_state *mrb, mrb_value ary, const char *path, int ai) { #ifdef _WIN32 if (DRIVE_LETTER_P(path)) { mrb_ary_set(mrb, ary, 0, mrb_str_new(mrb, path, 2)); path += 2; if (DIRSEP_P(*path)) { ARY_SET_LEN(mrb_ary_ptr(ary), 1); } mrb_gc_arena_restore(mrb, ai); } else if (UNC_PATH_P(path)) { path += 2; SKIP_DIRSEP(path); const char *path0 = path; NEXT_DIRSEP(path); mrb_value prefix = mrb_str_new_lit(mrb, "//"); mrb_str_cat(mrb, prefix, path0, path - path0); ARY_SET_LEN(mrb_ary_ptr(ary), 0); mrb_ary_push(mrb, ary, prefix); mrb_gc_arena_restore(mrb, ai); } else #endif /* _WIN32 */ { if (RARRAY_LEN(ary) == 0) { mrb_ary_set(mrb, ary, 0, mrb_nil_value()); } else if (DIRSEP_P(*path)) { ARY_SET_LEN(mrb_ary_ptr(ary), 1); } } for (;;) { SKIP_DIRSEP(path); const char *path0 = path; NEXT_DIRSEP(path); ptrdiff_t len = path - path0; if (len == 0) { break; } else if (len == 1 && path0[0] == '.') { /* do nothing */ } else if (len == 2 && path0[0] == '.' && path0[1] == '.') { if (RARRAY_LEN(ary) >= 2) { mrb_ary_pop(mrb, ary); } } else { mrb_ary_push(mrb, ary, mrb_str_new(mrb, path0, path - path0)); mrb_gc_arena_restore(mrb, ai); } } } // This function decomposes path into an array based on basedir and workdir. // The array consists of the root prefix at ary[0], zero or more directories, and finally file names. // The root prefix is nil for non-Windows, or the drive name or UNC host name for Windows. static mrb_value path_split(mrb_state *mrb, const char *path, const char *basedir, const char *workdir) { mrb_value ary = mrb_ary_new(mrb); int ai = mrb_gc_arena_save(mrb); if (workdir) { path_parse(mrb, ary, workdir, ai); } if (basedir) { path_parse(mrb, ary, basedir, ai); } path_parse(mrb, ary, path, ai); return ary; } static const char* path_gethome(mrb_state *mrb, const char **pathp) { mrb_assert(pathp && *pathp && **pathp == '~'); char *home; mrb_value path; const char *username = ++*pathp; NEXT_DIRSEP(*pathp); ptrdiff_t len = *pathp - username; if (len == 0) { home = getenv("HOME"); #ifdef _WIN32 if (home == NULL) { home = getenv("USERPROFILE"); } #endif if (home == NULL) { mrb_raise(mrb, E_ARGUMENT_ERROR, "couldn't find HOME environment -- expanding '~'"); } if (!path_absolute_p(home)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "non-absolute home"); } } else { const char *uname = RSTRING_CSTR(mrb, mrb_str_new(mrb, username, (mrb_int)len)); #if defined(_WIN32) || defined(MRB_IO_NO_PWNAM) mrb_raisef(mrb, E_ARGUMENT_ERROR, "user %s doesn't exist", uname); #else const struct passwd *pwd = getpwnam(uname); if (pwd == NULL) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "user %s doesn't exist", uname); } home = pwd->pw_dir; if (!path_absolute_p(home)) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "non-absolute home of ~%s", uname); } #endif } home = mrb_utf8_from_locale(home, -1); path = mrb_str_new_cstr(mrb, home); mrb_utf8_free(home); SKIP_DIRSEP(*pathp); return RSTRING_CSTR(mrb, path); } static mrb_value path_expand(mrb_state *mrb, const char *path, const char *base, mrb_bool tilda) { mrb_value ary; // split path components as array and normalization if (tilda && path[0] == '~') { base = path_gethome(mrb, &path); ary = path_split(mrb, path, base, NULL); } else if (path_absolute_p(path)) { ary = path_split(mrb, path, NULL, NULL); } else { const char *wd = NULL; if (tilda && base[0] == '~') { wd = path_gethome(mrb, &base); } #ifndef _WIN32 else if (!path_absolute_p(base)) { wd = path_getwd(mrb); } #else else if (DRIVE_LETTER_P(path)) { if (DRIVE_LETTER_P(base) && DRIVE_EQUAL_P(path, base) && DIRSEP_P(base[2])) { wd = NULL; } else { wd = path_getwd(mrb); if (UNC_PATH_P(base) || (DRIVE_LETTER_P(base) && !DRIVE_EQUAL_P(path, base))) { base = NULL; } if (!DRIVE_EQUAL_P(path, wd)) { wd = NULL; } } } else if (UNC_PATH_P(base)) { wd = NULL; } else { wd = path_getwd(mrb); } #endif /* _WIN32 */ ary = path_split(mrb, path, base, wd); } // join path components as string mrb_value ret; mrb_assert(RARRAY_LEN(ary) >= 1); #ifndef _WIN32 mrb_assert(mrb_nil_p(RARRAY_PTR(ary)[0])); ret = mrb_str_new(mrb, NULL, 0); #else mrb_assert(mrb_string_p(RARRAY_PTR(ary)[0])); ret = RARRAY_PTR(ary)[0]; #endif if (RARRAY_LEN(ary) == 1) { #ifdef _WIN32 mrb_assert(mrb_string_p(ret)); mrb_assert(RSTRING_LEN(ret) >= 2); // drive letter or UNC prefix if (!DIRSEP_P(RSTRING_PTR(ret)[0])) #endif { mrb_str_cat_lit(mrb, ret, "/"); } } else { for (int i = 1; i < RARRAY_LEN(ary); i++) { mrb_str_cat_lit(mrb, ret, "/"); mrb_assert(mrb_string_p(RARRAY_PTR(ary)[i])); mrb_str_cat_str(mrb, ret, RARRAY_PTR(ary)[i]); } } return ret; } static mrb_value mrb_file_expand_path(mrb_state *mrb, mrb_value self) { const char *path; const char *default_dir = "."; mrb_get_args(mrb, "z|z", &path, &default_dir); return path_expand(mrb, path, default_dir, TRUE); } static mrb_value mrb_file_absolute_path(mrb_state *mrb, mrb_value self) { const char *path; const char *default_dir = "."; mrb_get_args(mrb, "z|z", &path, &default_dir); return path_expand(mrb, path, default_dir, FALSE); } static mrb_value mrb_file_absolute_path_p(mrb_state *mrb, mrb_value klass) { mrb_value path = mrb_get_arg1(mrb); mrb_ensure_string_type(mrb, path); return mrb_bool_value(path_absolute_p(RSTRING_CSTR(mrb, path))); } #define TIME_OVERFLOW_P(a) (sizeof(time_t) >= sizeof(mrb_int) && ((a) > MRB_INT_MAX || (a) < MRB_INT_MIN)) #define TIME_T_UINT (~(time_t)0 > 0) #if defined(MRB_USE_BITINT) #define TIME_BIGTIME(mrb, a) \ return (TIME_T_UINT ? mrb_bint_new_uint64((mrb), (uint64_t)(a)) \ : mrb_bint_new_int64(mrb, (int64_t)(a))) #elif !defined(MRB_NO_FLOAT) #define TIME_BIGTIME(mrb,a) return mrb_float_value((mrb), (mrb_float)(a)) #else #define TIME_BIGTIME(mrb, a) mrb_raise(mrb, E_IO_ERROR, #a " overflow") #endif static mrb_value mrb_file_atime(mrb_state *mrb, mrb_value self) { int fd = mrb_io_fileno(mrb, self); mrb_stat st; mrb->c->ci->mid = 0; if (mrb_fstat(fd, &st) == -1) mrb_sys_fail(mrb, "atime"); if (TIME_OVERFLOW_P(st.st_atime)) { TIME_BIGTIME(mrb, st.st_atime); } return mrb_int_value(mrb, (mrb_int)st.st_atime); } static mrb_value mrb_file_ctime(mrb_state *mrb, mrb_value self) { int fd = mrb_io_fileno(mrb, self); mrb_stat st; mrb->c->ci->mid = 0; if (mrb_fstat(fd, &st) == -1) mrb_sys_fail(mrb, "ctime"); if (TIME_OVERFLOW_P(st.st_ctime)) { TIME_BIGTIME(mrb, st. st_ctime); } return mrb_int_value(mrb, (mrb_int)st.st_ctime); } static mrb_value mrb_file_mtime(mrb_state *mrb, mrb_value self) { int fd = mrb_io_fileno(mrb, self); mrb_stat st; mrb->c->ci->mid = 0; if (mrb_fstat(fd, &st) == -1) mrb_sys_fail(mrb, "mtime"); if (TIME_OVERFLOW_P(st.st_mtime)) { TIME_BIGTIME(mrb, st. st_mtime); } return mrb_int_value(mrb, (mrb_int)st.st_mtime); } static mrb_value mrb_file_flock(mrb_state *mrb, mrb_value self) { #if defined(sun) mrb_raise(mrb, E_NOTIMP_ERROR, "flock is not supported on Illumos/Solaris/Windows"); #else mrb_int operation; mrb_get_args(mrb, "i", &operation); int fd = mrb_io_fileno(mrb, self); while (flock(fd, (int)operation) == -1) { switch (errno) { case EINTR: /* retry */ break; case EAGAIN: /* NetBSD */ #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN case EWOULDBLOCK: /* FreeBSD OpenBSD Linux */ #endif if (operation & LOCK_NB) { return mrb_false_value(); } /* FALLTHRU - should not happen */ default: mrb_sys_fail(mrb, "flock"); break; } } #endif return mrb_fixnum_value(0); } static mrb_value mrb_file_size(mrb_state *mrb, mrb_value self) { mrb_stat st; int fd = mrb_io_fileno(mrb, self); if (mrb_fstat(fd, &st) == -1) { mrb_sys_fail(mrb, "fstat"); } if (sizeof(st.st_size) >= sizeof(mrb_int) && st.st_size > MRB_INT_MAX) { #ifdef MRB_NO_FLOAT mrb_raise(mrb, E_RUNTIME_ERROR, "File#size too large for MRB_NO_FLOAT"); #else return mrb_float_value(mrb, (mrb_float)st.st_size); #endif } return mrb_int_value(mrb, (mrb_int)st.st_size); } static int mrb_ftruncate(int fd, mrb_int length) { #ifndef _WIN32 return ftruncate(fd, (off_t)length); #else __int64 cur; HANDLE file = (HANDLE)_get_osfhandle(fd); if (file == INVALID_HANDLE_VALUE) { return -1; } cur = _lseeki64(fd, 0, SEEK_CUR); if (cur == -1) return -1; if (_lseeki64(fd, (__int64)length, SEEK_SET) == -1) return -1; if (!SetEndOfFile(file)) { errno = EINVAL; /* TODO: GetLastError to errno */ return -1; } if (_lseeki64(fd, cur, SEEK_SET) == -1) return -1; return 0; #endif /* _WIN32 */ } static mrb_value mrb_file_truncate(mrb_state *mrb, mrb_value self) { mrb_value lenv = mrb_get_arg1(mrb); int fd = mrb_io_fileno(mrb, self); mrb_int length = mrb_as_int(mrb, lenv); if (mrb_ftruncate(fd, length) != 0) { mrb_sys_fail(mrb, "ftruncate"); } return mrb_fixnum_value(0); } static mrb_value mrb_file_s_symlink(mrb_state *mrb, mrb_value klass) { #if defined(_WIN32) mrb_raise(mrb, E_NOTIMP_ERROR, "symlink is not supported on this platform"); #else mrb_value from, to; mrb_get_args(mrb, "SS", &from, &to); const char *src = mrb_locale_from_utf8(RSTRING_CSTR(mrb, from), -1); const char *dst = mrb_locale_from_utf8(RSTRING_CSTR(mrb, to), -1); if (symlink(src, dst) == -1) { mrb_locale_free(src); mrb_locale_free(dst); mrb_sys_fail(mrb, RSTRING_CSTR(mrb, mrb_format(mrb, "(%v, %v)", from, to))); } mrb_locale_free(src); mrb_locale_free(dst); #endif return mrb_fixnum_value(0); } static mrb_value mrb_file_s_chmod(mrb_state *mrb, mrb_value klass) { mrb_int mode; mrb_int argc; const mrb_value *filenames; int ai = mrb_gc_arena_save(mrb); mrb_get_args(mrb, "i*", &mode, &filenames, &argc); for (int i = 0; i < argc; i++) { mrb_ensure_string_type(mrb, filenames[i]); const char *utf8_path = RSTRING_CSTR(mrb, filenames[i]); char *path = mrb_locale_from_utf8(utf8_path, -1); if (CHMOD(path, mode) == -1) { mrb_locale_free(path); mrb_sys_fail(mrb, utf8_path); } mrb_locale_free(path); mrb_gc_arena_restore(mrb, ai); } return mrb_fixnum_value(argc); } static mrb_value mrb_file_s_readlink(mrb_state *mrb, mrb_value klass) { #if defined(_WIN32) mrb_raise(mrb, E_NOTIMP_ERROR, "readlink is not supported on this platform"); return mrb_nil_value(); // unreachable #else const char *path; size_t bufsize = 100; mrb_get_args(mrb, "z", &path); char *tmp = mrb_locale_from_utf8(path, -1); char *buf = (char*)mrb_malloc(mrb, bufsize); ssize_t rc; while ((rc = readlink(tmp, buf, bufsize)) == (ssize_t)bufsize) { bufsize += 100; buf = (char*)mrb_realloc(mrb, buf, bufsize); } mrb_locale_free(tmp); if (rc == -1) { mrb_free(mrb, buf); mrb_sys_fail(mrb, path); } tmp = mrb_utf8_from_locale(buf, -1); mrb_value ret = mrb_str_new(mrb, tmp, rc); mrb_utf8_free(tmp); mrb_free(mrb, buf); return ret; #endif } void mrb_init_file(mrb_state *mrb) { struct RClass *io = mrb_class_get_id(mrb, MRB_SYM(IO)); struct RClass *file = mrb_define_class_id(mrb, MRB_SYM(File), io); MRB_SET_INSTANCE_TT(file, MRB_TT_CDATA); mrb_define_class_method_id(mrb, file, MRB_SYM(umask), mrb_file_s_umask, MRB_ARGS_OPT(1)); mrb_define_class_method_id(mrb, file, MRB_SYM(delete), mrb_file_s_unlink, MRB_ARGS_ANY()); mrb_define_class_method_id(mrb, file, MRB_SYM(unlink), mrb_file_s_unlink, MRB_ARGS_ANY()); mrb_define_class_method_id(mrb, file, MRB_SYM(rename), mrb_file_s_rename, MRB_ARGS_REQ(2)); mrb_define_class_method_id(mrb, file, MRB_SYM(symlink), mrb_file_s_symlink, MRB_ARGS_REQ(2)); mrb_define_class_method_id(mrb, file, MRB_SYM(chmod), mrb_file_s_chmod, MRB_ARGS_REQ(1) | MRB_ARGS_REST()); mrb_define_class_method_id(mrb, file, MRB_SYM(readlink), mrb_file_s_readlink, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, file, MRB_SYM(dirname), mrb_file_dirname, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, file, MRB_SYM(basename), mrb_file_basename, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, file, MRB_SYM(realpath), mrb_file_realpath, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_class_method_id(mrb, file, MRB_SYM(absolute_path), mrb_file_absolute_path, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_class_method_id(mrb, file, MRB_SYM_Q(absolute_path), mrb_file_absolute_path_p, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, file, MRB_SYM(expand_path), mrb_file_expand_path, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, file, MRB_SYM(flock), mrb_file_flock, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, file, MRB_SYM(_atime), mrb_file_atime, MRB_ARGS_NONE()); mrb_define_method_id(mrb, file, MRB_SYM(_ctime), mrb_file_ctime, MRB_ARGS_NONE()); mrb_define_method_id(mrb, file, MRB_SYM(_mtime), mrb_file_mtime, MRB_ARGS_NONE()); mrb_define_method_id(mrb, file, MRB_SYM(size), mrb_file_size, MRB_ARGS_NONE()); mrb_define_method_id(mrb, file, MRB_SYM(truncate), mrb_file_truncate, MRB_ARGS_REQ(1)); struct RClass *cnst = mrb_define_module_under_id(mrb, file, MRB_SYM(Constants)); mrb_define_const_id(mrb, cnst, MRB_SYM(LOCK_SH), mrb_fixnum_value(LOCK_SH)); mrb_define_const_id(mrb, cnst, MRB_SYM(LOCK_EX), mrb_fixnum_value(LOCK_EX)); mrb_define_const_id(mrb, cnst, MRB_SYM(LOCK_UN), mrb_fixnum_value(LOCK_UN)); mrb_define_const_id(mrb, cnst, MRB_SYM(LOCK_NB), mrb_fixnum_value(LOCK_NB)); mrb_define_const_id(mrb, cnst, MRB_SYM(SEPARATOR), mrb_str_new_cstr(mrb, FILE_SEPARATOR)); mrb_define_const_id(mrb, cnst, MRB_SYM(PATH_SEPARATOR), mrb_str_new_cstr(mrb, PATH_SEPARATOR)); #if defined(_WIN32) mrb_define_const_id(mrb, cnst, MRB_SYM(ALT_SEPARATOR), mrb_str_new_cstr(mrb, FILE_ALT_SEPARATOR)); #else mrb_define_const_id(mrb, cnst, MRB_SYM(ALT_SEPARATOR), mrb_nil_value()); #endif mrb_define_const_id(mrb, cnst, MRB_SYM(NULL), mrb_str_new_cstr(mrb, NULL_FILE)); mrb_define_const_id(mrb, cnst, MRB_SYM(RDONLY), mrb_fixnum_value(MRB_O_RDONLY)); mrb_define_const_id(mrb, cnst, MRB_SYM(WRONLY), mrb_fixnum_value(MRB_O_WRONLY)); mrb_define_const_id(mrb, cnst, MRB_SYM(RDWR), mrb_fixnum_value(MRB_O_RDWR)); mrb_define_const_id(mrb, cnst, MRB_SYM(APPEND), mrb_fixnum_value(MRB_O_APPEND)); mrb_define_const_id(mrb, cnst, MRB_SYM(CREAT), mrb_fixnum_value(MRB_O_CREAT)); mrb_define_const_id(mrb, cnst, MRB_SYM(EXCL), mrb_fixnum_value(MRB_O_EXCL)); mrb_define_const_id(mrb, cnst, MRB_SYM(TRUNC), mrb_fixnum_value(MRB_O_TRUNC)); mrb_define_const_id(mrb, cnst, MRB_SYM(NONBLOCK), mrb_fixnum_value(MRB_O_NONBLOCK)); mrb_define_const_id(mrb, cnst, MRB_SYM(NOCTTY), mrb_fixnum_value(MRB_O_NOCTTY)); mrb_define_const_id(mrb, cnst, MRB_SYM(BINARY), mrb_fixnum_value(MRB_O_BINARY)); mrb_define_const_id(mrb, cnst, MRB_SYM(SHARE_DELETE), mrb_fixnum_value(MRB_O_SHARE_DELETE)); mrb_define_const_id(mrb, cnst, MRB_SYM(SYNC), mrb_fixnum_value(MRB_O_SYNC)); mrb_define_const_id(mrb, cnst, MRB_SYM(DSYNC), mrb_fixnum_value(MRB_O_DSYNC)); mrb_define_const_id(mrb, cnst, MRB_SYM(RSYNC), mrb_fixnum_value(MRB_O_RSYNC)); mrb_define_const_id(mrb, cnst, MRB_SYM(NOFOLLOW), mrb_fixnum_value(MRB_O_NOFOLLOW)); mrb_define_const_id(mrb, cnst, MRB_SYM(NOATIME), mrb_fixnum_value(MRB_O_NOATIME)); mrb_define_const_id(mrb, cnst, MRB_SYM(DIRECT), mrb_fixnum_value(MRB_O_DIRECT)); mrb_define_const_id(mrb, cnst, MRB_SYM(TMPFILE), mrb_fixnum_value(MRB_O_TMPFILE)); } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/src/PaxHeaders/mruby_io_gem.c0000644000000000000000000000013115077107276024501 xustar0030 mtime=1761382078.120420566 29 atime=1761382080.14141132 30 ctime=1761382108.683301586 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/src/mruby_io_gem.c0000644000175100017510000000054615077107276025077 0ustar00runnerrunner#include void mrb_init_io(mrb_state *mrb); void mrb_init_file(mrb_state *mrb); void mrb_init_file_test(mrb_state *mrb); #define DONE mrb_gc_arena_restore(mrb, 0) void mrb_mruby_io_gem_init(mrb_state* mrb) { mrb_init_io(mrb); DONE; mrb_init_file(mrb); DONE; mrb_init_file_test(mrb); DONE; } void mrb_mruby_io_gem_final(mrb_state* mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/src/PaxHeaders/file_test.c0000644000000000000000000000013215077107276024003 xustar0030 mtime=1761382078.120420566 30 atime=1761382080.140411325 30 ctime=1761382108.684301584 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-io/src/file_test.c0000644000175100017510000001723215077107276024400 0ustar00runnerrunner/* ** file_test.c - FileTest class */ #include #include #include #include #include #include #include #include #include #if defined(_WIN32) #define LSTAT stat #include #else #define LSTAT lstat #include #include #include #ifndef __DJGPP__ #include #endif #include #include #endif #include #include #include #include extern struct mrb_data_type mrb_io_type; static int mrb_stat0(mrb_state *mrb, mrb_value obj, struct stat *st, int do_lstat) { if (mrb_obj_is_kind_of(mrb, obj, mrb_class_get_id(mrb, MRB_SYM(IO)))) { struct mrb_io *fptr; fptr = (struct mrb_io*)mrb_data_get_ptr(mrb, obj, &mrb_io_type); if (fptr && fptr->fd >= 0) { return fstat(fptr->fd, st); } mrb_raise(mrb, E_IO_ERROR, "closed stream"); return -1; } else { char *path = mrb_locale_from_utf8(RSTRING_CSTR(mrb, obj), -1); int ret; if (do_lstat) { ret = LSTAT(path, st); } else { ret = stat(path, st); } mrb_locale_free(path); return ret; } } static int mrb_stat(mrb_state *mrb, mrb_value obj, struct stat *st) { return mrb_stat0(mrb, obj, st, 0); } #ifdef S_ISLNK static int mrb_lstat(mrb_state *mrb, mrb_value obj, struct stat *st) { return mrb_stat0(mrb, obj, st, 1); } #endif /* * Document-method: directory? * * call-seq: * File.directory?(file_name) -> true or false * * Returns true if the named file is a directory, * or a symlink that points at a directory, and false * otherwise. * * File.directory?(".") */ static mrb_value mrb_filetest_s_directory_p(mrb_state *mrb, mrb_value klass) { #ifndef S_ISDIR # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif struct stat st; mrb_value obj = mrb_get_arg1(mrb); if (mrb_stat(mrb, obj, &st) < 0) return mrb_false_value(); if (S_ISDIR(st.st_mode)) return mrb_true_value(); return mrb_false_value(); } /* * call-seq: * File.pipe?(file_name) -> true or false * * Returns true if the named file is a pipe. */ static mrb_value mrb_filetest_s_pipe_p(mrb_state *mrb, mrb_value klass) { #if defined(_WIN32) mrb_raise(mrb, E_NOTIMP_ERROR, "pipe is not supported on this platform"); #else #ifdef S_IFIFO # ifndef S_ISFIFO # define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) # endif struct stat st; mrb_value obj = mrb_get_arg1(mrb); if (mrb_stat(mrb, obj, &st) < 0) return mrb_false_value(); if (S_ISFIFO(st.st_mode)) return mrb_true_value(); #endif return mrb_false_value(); #endif } /* * call-seq: * File.symlink?(file_name) -> true or false * * Returns true if the named file is a symbolic link. */ static mrb_value mrb_filetest_s_symlink_p(mrb_state *mrb, mrb_value klass) { #if defined(_WIN32) mrb_raise(mrb, E_NOTIMP_ERROR, "symlink is not supported on this platform"); #else #ifndef S_ISLNK # ifdef _S_ISLNK # define S_ISLNK(m) _S_ISLNK(m) # else # ifdef _S_IFLNK # define S_ISLNK(m) (((m) & S_IFMT) == _S_IFLNK) # else # ifdef S_IFLNK # define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) # endif # endif # endif #endif #ifdef S_ISLNK struct stat st; mrb_value obj = mrb_get_arg1(mrb); if (mrb_lstat(mrb, obj, &st) == -1) return mrb_false_value(); if (S_ISLNK(st.st_mode)) return mrb_true_value(); #endif return mrb_false_value(); #endif } /* * call-seq: * File.socket?(file_name) -> true or false * * Returns true if the named file is a socket. */ static mrb_value mrb_filetest_s_socket_p(mrb_state *mrb, mrb_value klass) { #if defined(_WIN32) mrb_raise(mrb, E_NOTIMP_ERROR, "socket is not supported on this platform"); #else #ifndef S_ISSOCK # ifdef _S_ISSOCK # define S_ISSOCK(m) _S_ISSOCK(m) # else # ifdef _S_IFSOCK # define S_ISSOCK(m) (((m) & S_IFMT) == _S_IFSOCK) # else # ifdef S_IFSOCK # define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) # endif # endif # endif #endif #ifdef S_ISSOCK struct stat st; mrb_value obj = mrb_get_arg1(mrb); if (mrb_stat(mrb, obj, &st) < 0) return mrb_false_value(); if (S_ISSOCK(st.st_mode)) return mrb_true_value(); #endif return mrb_false_value(); #endif } /* * call-seq: * File.exist?(file_name) -> true or false * File.exists?(file_name) -> true or false * * Return true if the named file exists. */ static mrb_value mrb_filetest_s_exist_p(mrb_state *mrb, mrb_value klass) { struct stat st; mrb_value obj = mrb_get_arg1(mrb); if (mrb_stat(mrb, obj, &st) < 0) return mrb_false_value(); return mrb_true_value(); } /* * call-seq: * File.file?(file_name) -> true or false * * Returns true if the named file exists and is a * regular file. */ static mrb_value mrb_filetest_s_file_p(mrb_state *mrb, mrb_value klass) { #ifndef S_ISREG # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #endif struct stat st; mrb_value obj = mrb_get_arg1(mrb); if (mrb_stat(mrb, obj, &st) < 0) return mrb_false_value(); if (S_ISREG(st.st_mode)) return mrb_true_value(); return mrb_false_value(); } /* * call-seq: * File.zero?(file_name) -> true or false * * Returns true if the named file exists and has * a zero size. */ static mrb_value mrb_filetest_s_zero_p(mrb_state *mrb, mrb_value klass) { struct stat st; mrb_value obj = mrb_get_arg1(mrb); if (mrb_stat(mrb, obj, &st) < 0) return mrb_false_value(); if (st.st_size == 0) return mrb_true_value(); return mrb_false_value(); } /* * call-seq: * File.size(file_name) -> integer * * Returns the size of file_name. * * _file_name_ can be an IO object. */ static mrb_value mrb_filetest_s_size(mrb_state *mrb, mrb_value klass) { struct stat st; mrb_value obj = mrb_get_arg1(mrb); if (mrb_stat(mrb, obj, &st) < 0) mrb_sys_fail(mrb, "mrb_stat"); return mrb_int_value(mrb, st.st_size); } /* * call-seq: * File.size?(file_name) -> Integer or nil * * Returns +nil+ if +file_name+ doesn't exist or has zero size, the size of the * file otherwise. */ static mrb_value mrb_filetest_s_size_p(mrb_state *mrb, mrb_value klass) { struct stat st; mrb_value obj = mrb_get_arg1(mrb); if (mrb_stat(mrb, obj, &st) < 0) return mrb_nil_value(); if (st.st_size == 0) return mrb_nil_value(); return mrb_int_value(mrb, st.st_size); } void mrb_init_file_test(mrb_state *mrb) { struct RClass *f; f = mrb_define_module_id(mrb, MRB_SYM(FileTest)); mrb_define_class_method_id(mrb, f, MRB_SYM_Q(directory), mrb_filetest_s_directory_p, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, f, MRB_SYM_Q(exist), mrb_filetest_s_exist_p, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, f, MRB_SYM_Q(exists), mrb_filetest_s_exist_p, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, f, MRB_SYM_Q(file), mrb_filetest_s_file_p, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, f, MRB_SYM_Q(pipe), mrb_filetest_s_pipe_p, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, f, MRB_SYM(size), mrb_filetest_s_size, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, f, MRB_SYM_Q(size), mrb_filetest_s_size_p, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, f, MRB_SYM_Q(socket), mrb_filetest_s_socket_p, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, f, MRB_SYM_Q(symlink), mrb_filetest_s_symlink_p, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, f, MRB_SYM_Q(zero), mrb_filetest_s_zero_p, MRB_ARGS_REQ(1)); } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/stdlib-io.gembox0000644000000000000000000000013015077107276022416 xustar0029 mtime=1761382078.13042052 30 atime=1761382080.149411284 29 ctime=1761382108.93130087 nghttp2-1.68.0/third-party/mruby/mrbgems/stdlib-io.gembox0000644000175100017510000000053715077107276023015 0ustar00runnerrunner# It also works with MRB_NO_FLOAT. MRuby::GemBox.new do |conf| # Use standard IO/File class conf.gem :core => "mruby-io" # Use standard IO/File class conf.gem :core => "mruby-socket" # Use errno extension for a good mruby-io/mruby-socket experience conf.gem :core => "mruby-errno" # Use Dir class conf.gem :core => "mruby-dir" end nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-bin-mirb0000644000000000000000000000013215077107334022100 xustar0030 mtime=1761382108.772301329 30 atime=1761382109.798298363 30 ctime=1761382108.772301329 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mirb/0000755000175100017510000000000015077107334022545 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mirb/PaxHeaders/bintest0000644000000000000000000000013215077107334023550 xustar0030 mtime=1761382108.774301323 30 atime=1761382109.798298363 30 ctime=1761382108.774301323 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mirb/bintest/0000755000175100017510000000000015077107334024215 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mirb/bintest/PaxHeaders/mirb.rb0000644000000000000000000000013215077107276025110 xustar0030 mtime=1761382078.106420629 30 atime=1761382080.127411384 30 ctime=1761382108.774301323 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mirb/bintest/mirb.rb0000644000175100017510000000261615077107276025505 0ustar00runnerrunnerrequire 'open3' require 'tempfile' assert('mirb normal operations') do o, s = Open3.capture2(cmd("mirb"), :stdin_data => "a=1\nb=2\na+b\n") assert_true o.include?('=> 3') assert_true o.include?('=> 2') end assert('mirb multi-line') do o, s = Open3.capture2(cmd("mirb"), :stdin_data => "def a(b)\n return b\n end\na(1)\n") assert_true o.include?('=> :a') assert_true o.include?('=> 1') end assert('regression for #1563') do o, s = Open3.capture2(cmd("mirb"), :stdin_data => "a=1;b=2;c=3\nb\nc") assert_true o.include?('=> 3') end assert('mirb -d option') do o, _ = Open3.capture2(cmd("mirb"), :stdin_data => "$DEBUG\n") assert_true o.include?('=> false') o, _ = Open3.capture2("#{cmd('mirb')} -d", :stdin_data => "$DEBUG\n") assert_true o.include?('=> true') end assert('mirb -r option') do lib = Tempfile.new('lib.rb') lib.write < "Hoge.new.hoge\n") assert_true o.include?('=> :hoge') end assert('top level local variables are in file scope') do lib = Tempfile.new('lib.rb') lib.write <<-TESTLIB a = 1 A = -> { a } TESTLIB lib.flush o, _ = Open3.capture2("#{cmd('mirb')} -r #{lib.path}", :stdin_data => <<-TESTCODE) a a = 5 A.call TESTCODE assert_kind_of Integer, o =~ /\bundefined method 'a' .*\(NoMethodError\).*=> 5\b.*=> 1\b/m end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mirb/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024277 xustar0030 mtime=1761382078.106420629 30 atime=1761382080.127411384 30 ctime=1761382108.772301329 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mirb/mrbgem.rake0000644000175100017510000000440015077107276024665 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-bin-mirb') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'mirb command' if spec.build.cc.search_header_path 'readline/readline.h' spec.cc.defines << "MRB_USE_READLINE" spec.cc.defines << "MRB_READLINE_HEADER=''" spec.cc.defines << "MRB_READLINE_HISTORY=''" if spec.build.cc.search_header_path 'termcap.h' if MRUBY_BUILD_HOST_IS_CYGWIN || MRUBY_BUILD_HOST_IS_OPENBSD if spec.build.cc.search_header_path 'termcap.h' if MRUBY_BUILD_HOST_IS_CYGWIN then spec.linker.libraries << 'ncurses' else spec.linker.libraries << 'termcap' end end end end if RUBY_PLATFORM.include?('netbsd') spec.linker.libraries << 'edit' else spec.linker.libraries << 'readline' if RUBY_PLATFORM.include?('darwin') # Workaround to build with Homebrew's readline on Mac (#4537) lib_path = spec.build.cc.header_search_paths.find do |include_path| lib_path = File.expand_path("#{include_path}/../lib") break lib_path if File.exist?("#{lib_path}/libreadline.dylib") || File.exist?("#{lib_path}/libreadline.a") end spec.linker.library_paths << lib_path if lib_path elsif spec.build.cc.search_header_path 'curses.h' spec.linker.libraries << 'ncurses' if spec.build.cc.search_header_path 'term.h' spec.linker.libraries << 'tinfo' end elsif spec.build.cc.search_header_path 'ncursesw/curses.h' spec.linker.libraries << 'ncursesw' if spec.build.cc.search_header_path 'ncursesw/term.h' spec.linker.libraries << 'tinfow' end end end elsif spec.build.cc.search_header_path 'edit/readline/readline.h' spec.cc.defines << "MRB_USE_READLINE" spec.cc.defines << "MRB_READLINE_HEADER=''" spec.cc.defines << "MRB_READLINE_HISTORY=''" spec.linker.libraries << "edit" elsif spec.build.cc.search_header_path 'linenoise.h' spec.cc.defines << "MRB_USE_LINENOISE" end spec.bins = %w(mirb) spec.add_dependency('mruby-compiler', :core => 'mruby-compiler') end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mirb/PaxHeaders/tools0000644000000000000000000000013215077107334023240 xustar0030 mtime=1761382108.202302977 30 atime=1761382109.798298363 30 ctime=1761382108.202302977 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mirb/tools/0000755000175100017510000000000015077107334023705 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mirb/tools/PaxHeaders/mirb0000644000000000000000000000013215077107334024171 xustar0030 mtime=1761382108.771301332 30 atime=1761382109.798298363 30 ctime=1761382108.771301332 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mirb/tools/mirb/0000755000175100017510000000000015077107334024636 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mirb/tools/mirb/PaxHeaders/mirb.c0000644000000000000000000000013215077107276025350 xustar0030 mtime=1761382078.107420625 30 atime=1761382080.127411384 30 ctime=1761382108.771301332 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c0000644000175100017510000004160515077107276025746 0ustar00runnerrunner/* ** mirb - Embeddable Interactive Ruby Shell ** ** This program takes code from the user in ** an interactive way and executes it ** immediately. It's a REPL... */ #include #ifdef MRB_NO_STDIO # error mruby-bin-mirb conflicts 'MRB_NO_STDIO' in your build configuration #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* obsolete configuration */ #ifdef ENABLE_READLINE # define MRB_USE_READLINE #endif #ifdef ENABLE_LINENOISE # define MRB_USE_LINENOISE #endif #ifdef DISABLE_MIRB_UNDERSCORE # define MRB_NO_MIRB_UNDERSCORE #endif #ifdef MRB_USE_READLINE #include MRB_READLINE_HEADER #include MRB_READLINE_HISTORY #define MIRB_ADD_HISTORY(line) add_history(line) #define MIRB_READLINE(ch) readline(ch) #if !defined(RL_READLINE_VERSION) || RL_READLINE_VERSION < 0x600 /* libedit & older readline do not have rl_free() */ #define MIRB_LINE_FREE(line) free(line) #else #define MIRB_LINE_FREE(line) rl_free(line) #endif #define MIRB_WRITE_HISTORY(path) write_history(path) #define MIRB_READ_HISTORY(path) read_history(path) #define MIRB_USING_HISTORY() using_history() #elif defined(MRB_USE_LINENOISE) #define MRB_USE_READLINE #include #define MIRB_ADD_HISTORY(line) linenoiseHistoryAdd(line) #define MIRB_READLINE(ch) linenoise(ch) #define MIRB_LINE_FREE(line) linenoiseFree(line) #define MIRB_WRITE_HISTORY(path) linenoiseHistorySave(path) #define MIRB_READ_HISTORY(path) linenoiseHistoryLoad(history_path) #define MIRB_USING_HISTORY() #endif #if !defined(_WIN32) && defined(_POSIX_C_SOURCE) #define MIRB_SIGSETJMP(env) sigsetjmp(env, 1) #define MIRB_SIGLONGJMP(env, val) siglongjmp(env, val) #define SIGJMP_BUF sigjmp_buf #else #define MIRB_SIGSETJMP(env) setjmp(env) #define MIRB_SIGLONGJMP(env, val) longjmp(env, val) #define SIGJMP_BUF jmp_buf #endif #ifdef MRB_USE_READLINE static const char history_file_name[] = ".mirb_history"; static char * get_history_path(mrb_state *mrb) { char *path = NULL; const char *home = getenv("HOME"); #ifdef _WIN32 if (home != NULL) { home = getenv("USERPROFILE"); } #endif if (home != NULL) { int len = snprintf(NULL, 0, "%s/%s", home, history_file_name); if (len >= 0) { size_t size = len + 1; path = (char*)mrb_malloc_simple(mrb, size); if (path != NULL) { int n = snprintf(path, size, "%s/%s", home, history_file_name); if (n != len) { mrb_free(mrb, path); path = NULL; } } } } return path; } #endif static void p(mrb_state *mrb, mrb_value obj) { mrb_value val; char* msg; val = mrb_funcall_argv(mrb, obj, MRB_SYM(inspect), 0, NULL); if (!mrb->exc) { fputs(" => ", stdout); } else { val = mrb_exc_get_output(mrb, mrb->exc); } if (!mrb_string_p(val)) { val = mrb_obj_as_string(mrb, obj); } msg = mrb_locale_from_utf8(RSTRING_PTR(val), (int)RSTRING_LEN(val)); fwrite(msg, strlen(msg), 1, stdout); mrb_locale_free(msg); putc('\n', stdout); } static void p_error(mrb_state *mrb, struct RObject* exc) { mrb_value val; char* msg; val = mrb_exc_get_output(mrb, exc); if (!mrb_string_p(val)) { val = mrb_obj_as_string(mrb, val); } msg = mrb_locale_from_utf8(RSTRING_PTR(val), (int)RSTRING_LEN(val)); fwrite(msg, strlen(msg), 1, stdout); mrb_locale_free(msg); putc('\n', stdout); } /* Guess if the user might want to enter more * or if they wants an evaluation of their code now */ static mrb_bool is_code_block_open(struct mrb_parser_state *parser) { mrb_bool code_block_open = FALSE; /* check for heredoc */ if (parser->parsing_heredoc != NULL) return TRUE; /* check for unterminated string */ if (parser->lex_strterm) return TRUE; /* check if parser error are available */ if (0 < parser->nerr) { const char unexpected_end[] = "syntax error, unexpected end of file"; const char *message = parser->error_buffer[0].message; /* a parser error occur, we have to check if */ /* we need to read one more line or if there is */ /* a different issue which we have to show to */ /* the user */ if (strncmp(message, unexpected_end, sizeof(unexpected_end) - 1) == 0) { code_block_open = TRUE; } else if (strcmp(message, "syntax error, unexpected keyword_end") == 0) { code_block_open = FALSE; } else if (strcmp(message, "syntax error, unexpected tREGEXP_BEG") == 0) { code_block_open = FALSE; } return code_block_open; } switch (parser->lstate) { /* all states which need more code */ case EXPR_BEG: /* beginning of a statement, */ /* that means previous line ended */ code_block_open = FALSE; break; case EXPR_DOT: /* a message dot was the last token, */ /* there has to come more */ code_block_open = TRUE; break; case EXPR_CLASS: /* a class keyword is not enough! */ /* we need also a name of the class */ code_block_open = TRUE; break; case EXPR_FNAME: /* a method name is necessary */ code_block_open = TRUE; break; case EXPR_VALUE: /* if, elsif, etc. without condition */ code_block_open = TRUE; break; /* now all the states which are closed */ case EXPR_ARG: /* an argument is the last token */ code_block_open = FALSE; break; /* all states which are unsure */ case EXPR_CMDARG: break; case EXPR_END: /* an expression was ended */ break; case EXPR_ENDARG: /* closing parenthesis */ break; case EXPR_ENDFN: /* definition end */ break; case EXPR_MID: /* jump keyword like break, return, ... */ break; case EXPR_MAX_STATE: /* don't know what to do with this token */ break; default: /* this state is unexpected! */ break; } return code_block_open; } struct _args { FILE *rfp; mrb_bool verbose : 1; mrb_bool debug : 1; int argc; char** argv; int libc; char **libv; }; static void usage(const char *name) { static const char *const usage_msg[] = { "switches:", "-d set $DEBUG to true (same as `mruby -d`)", "-r library same as `mruby -r`", "-v print version number, then run in verbose mode", "--verbose run in verbose mode", "--version print the version", "--copyright print the copyright", NULL }; const char *const *p = usage_msg; printf("Usage: %s [switches] [programfile] [arguments]\n", name); while (*p) printf(" %s\n", *p++); } static char * dup_arg_item(mrb_state *mrb, const char *item) { size_t buflen = strlen(item) + 1; char *buf = (char*)mrb_malloc(mrb, buflen); memcpy(buf, item, buflen); return buf; } static int parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args) { char **origargv = argv; static const struct _args args_zero = { 0 }; *args = args_zero; for (argc--,argv++; argc > 0; argc--,argv++) { char *item; if (argv[0][0] != '-') break; item = argv[0] + 1; switch (*item++) { case 'd': args->debug = TRUE; break; case 'r': if (!item[0]) { if (argc <= 1) { printf("%s: No library specified for -r\n", *origargv); return EXIT_FAILURE; } argc--; argv++; item = argv[0]; } if (args->libc == 0) { args->libv = (char**)mrb_malloc(mrb, sizeof(char*)); } else { args->libv = (char**)mrb_realloc(mrb, args->libv, sizeof(char*) * (args->libc + 1)); } args->libv[args->libc++] = dup_arg_item(mrb, item); break; case 'v': if (!args->verbose) mrb_show_version(mrb); args->verbose = TRUE; break; case '-': if (strcmp((*argv) + 2, "version") == 0) { mrb_show_version(mrb); exit(EXIT_SUCCESS); } else if (strcmp((*argv) + 2, "verbose") == 0) { args->verbose = TRUE; break; } else if (strcmp((*argv) + 2, "copyright") == 0) { mrb_show_copyright(mrb); exit(EXIT_SUCCESS); } default: return EXIT_FAILURE; } } if (args->rfp == NULL) { if (*argv != NULL) { args->rfp = fopen(argv[0], "r"); if (args->rfp == NULL) { printf("Cannot open program file. (%s)\n", *argv); return EXIT_FAILURE; } argc--; argv++; } } args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1)); memcpy(args->argv, argv, (argc+1) * sizeof(char*)); args->argc = argc; return EXIT_SUCCESS; } static void cleanup(mrb_state *mrb, struct _args *args) { if (args->rfp) fclose(args->rfp); mrb_free(mrb, args->argv); if (args->libc) { while (args->libc--) { mrb_free(mrb, args->libv[args->libc]); } mrb_free(mrb, args->libv); } mrb_close(mrb); } /* Print a short remark for the user */ static void print_hint(void) { printf("mirb - Embeddable Interactive Ruby Shell\n\n"); } #ifndef MRB_USE_READLINE /* Print the command line prompt of the REPL */ static void print_cmdline(int code_block_open) { if (code_block_open) { printf("* "); } else { printf("> "); } fflush(stdout); } #endif static int check_keyword(const char *buf, const char *word) { const char *p = buf; size_t len = strlen(word); /* skip preceding spaces */ while (*p && ISSPACE(*p)) { p++; } /* check keyword */ if (strncmp(p, word, len) != 0) { return 0; } p += len; /* skip trailing spaces */ while (*p) { if (!ISSPACE(*p)) return 0; p++; } return 1; } #ifndef MRB_USE_READLINE volatile sig_atomic_t input_canceled = 0; void ctrl_c_handler(int signo) { input_canceled = 1; } #else SIGJMP_BUF ctrl_c_buf; void ctrl_c_handler(int signo) { MIRB_SIGLONGJMP(ctrl_c_buf, 1); } #endif #ifndef MRB_NO_MIRB_UNDERSCORE void decl_lv_underscore(mrb_state *mrb, mrb_ccontext *cxt) { struct RProc *proc; struct mrb_parser_state *parser; parser = mrb_parse_string(mrb, "_=nil", cxt); if (parser == NULL) { fputs("create parser state error\n", stderr); mrb_close(mrb); exit(EXIT_FAILURE); } proc = mrb_generate_code(mrb, parser); mrb_vm_run(mrb, proc, mrb_top_self(mrb), 0); mrb_parser_free(parser); } #endif int main(int argc, char **argv) { char ruby_code[4096] = { 0 }; char last_code_line[1024] = { 0 }; #ifndef MRB_USE_READLINE int last_char; size_t char_index; #else char *history_path; char* line; #endif mrb_ccontext *cxt; struct mrb_parser_state *parser; mrb_state *mrb; mrb_value result; struct _args args; mrb_value ARGV; int n; int i; mrb_bool code_block_open = FALSE; int ai; unsigned int stack_keep = 0; /* new interpreter instance */ mrb = mrb_open(); if (mrb == NULL) { fputs("Invalid mrb interpreter, exiting mirb\n", stderr); return EXIT_FAILURE; } n = parse_args(mrb, argc, argv, &args); if (n == EXIT_FAILURE) { cleanup(mrb, &args); usage(argv[0]); return n; } ARGV = mrb_ary_new_capa(mrb, args.argc); for (i = 0; i < args.argc; i++) { char* utf8 = mrb_utf8_from_locale(args.argv[i], -1); if (utf8) { mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8)); mrb_utf8_free(utf8); } } mrb_define_global_const(mrb, "ARGV", ARGV); mrb_gv_set(mrb, mrb_intern_lit(mrb, "$DEBUG"), mrb_bool_value(args.debug)); #ifdef MRB_USE_READLINE history_path = get_history_path(mrb); if (history_path == NULL) { fputs("failed to get history path\n", stderr); mrb_close(mrb); return EXIT_FAILURE; } MIRB_USING_HISTORY(); MIRB_READ_HISTORY(history_path); #endif print_hint(); cxt = mrb_ccontext_new(mrb); /* Load libraries */ for (i = 0; i < args.libc; i++) { FILE *lfp = fopen(args.libv[i], "r"); if (lfp == NULL) { printf("Cannot open library file. (%s)\n", args.libv[i]); cleanup(mrb, &args); return EXIT_FAILURE; } mrb_load_file_cxt(mrb, lfp, cxt); fclose(lfp); mrb_vm_ci_env_clear(mrb, mrb->c->cibase); mrb_ccontext_cleanup_local_variables(cxt); } #ifndef MRB_NO_MIRB_UNDERSCORE decl_lv_underscore(mrb, cxt); #endif cxt->capture_errors = TRUE; cxt->lineno = 1; mrb_ccontext_filename(mrb, cxt, "(mirb)"); if (args.verbose) cxt->dump_result = TRUE; ai = mrb_gc_arena_save(mrb); while (TRUE) { char *utf8; if (args.rfp) { if (fgets(last_code_line, sizeof(last_code_line)-1, args.rfp) != NULL) goto done; break; } #ifndef MRB_USE_READLINE print_cmdline(code_block_open); signal(SIGINT, ctrl_c_handler); char_index = 0; while ((last_char = getchar()) != '\n') { if (last_char == EOF) break; if (char_index >= sizeof(last_code_line)-2) { fputs("input string too long\n", stderr); continue; } last_code_line[char_index++] = last_char; } signal(SIGINT, SIG_DFL); if (input_canceled) { ruby_code[0] = '\0'; last_code_line[0] = '\0'; code_block_open = FALSE; puts("^C"); input_canceled = 0; continue; } if (last_char == EOF) { fputs("\n", stdout); break; } last_code_line[char_index++] = '\n'; last_code_line[char_index] = '\0'; #else if (MIRB_SIGSETJMP(ctrl_c_buf) == 0) { ; } else { ruby_code[0] = '\0'; last_code_line[0] = '\0'; code_block_open = FALSE; puts("^C"); } signal(SIGINT, ctrl_c_handler); line = MIRB_READLINE(code_block_open ? "* " : "> "); signal(SIGINT, SIG_DFL); if (line == NULL) { printf("\n"); break; } if (strlen(line) > sizeof(last_code_line)-2) { fputs("input string too long\n", stderr); continue; } strcpy(last_code_line, line); strcat(last_code_line, "\n"); if (strlen(line) > 0) { MIRB_ADD_HISTORY(line); } MIRB_LINE_FREE(line); #endif done: if (code_block_open) { if (strlen(ruby_code)+strlen(last_code_line) > sizeof(ruby_code)-1) { fputs("concatenated input string too long\n", stderr); continue; } strcat(ruby_code, last_code_line); } else { if (check_keyword(last_code_line, "quit") || check_keyword(last_code_line, "exit")) { break; } strcpy(ruby_code, last_code_line); } utf8 = mrb_utf8_from_locale(ruby_code, -1); if (!utf8) abort(); /* parse code */ parser = mrb_parser_new(mrb); if (parser == NULL) { fputs("create parser state error\n", stderr); break; } parser->s = utf8; parser->send = utf8 + strlen(utf8); parser->lineno = cxt->lineno; mrb_parser_parse(parser, cxt); code_block_open = is_code_block_open(parser); mrb_utf8_free(utf8); if (code_block_open) { /* no evaluation of code */ } else { if (0 < parser->nwarn) { /* warning */ char* msg = mrb_locale_from_utf8(parser->warn_buffer[0].message, -1); printf("line %d: %s\n", parser->warn_buffer[0].lineno, msg); mrb_locale_free(msg); } if (0 < parser->nerr) { /* syntax error */ char* msg = mrb_locale_from_utf8(parser->error_buffer[0].message, -1); printf("line %d: %s\n", parser->error_buffer[0].lineno, msg); mrb_locale_free(msg); } else { /* generate bytecode */ struct RProc *proc = mrb_generate_code(mrb, parser); if (proc == NULL) { mrb_parser_free(parser); continue; } if (args.verbose) { mrb_codedump_all(mrb, proc); } /* adjust stack length of toplevel environment */ if (mrb->c->cibase->u.env) { struct REnv *e = mrb_vm_ci_env(mrb->c->cibase); if (e && MRB_ENV_LEN(e) < proc->body.irep->nlocals) { MRB_ENV_SET_LEN(e, proc->body.irep->nlocals); } } /* pass a proc for evaluation */ /* evaluate the bytecode */ result = mrb_vm_run(mrb, proc, mrb_top_self(mrb), stack_keep); stack_keep = proc->body.irep->nlocals; /* did an exception occur? */ if (mrb->exc) { MRB_EXC_CHECK_EXIT(mrb, mrb->exc); p_error(mrb, mrb->exc); mrb->exc = 0; } else { /* no */ if (!mrb_respond_to(mrb, result, MRB_SYM(inspect))){ result = mrb_any_to_s(mrb, result); } p(mrb, result); #ifndef MRB_NO_MIRB_UNDERSCORE *(mrb->c->ci->stack + 1) = result; #endif } } ruby_code[0] = '\0'; last_code_line[0] = '\0'; mrb_gc_arena_restore(mrb, ai); } mrb_parser_free(parser); cxt->lineno++; } #ifdef MRB_USE_READLINE MIRB_WRITE_HISTORY(history_path); mrb_free(mrb, history_path); #endif if (args.rfp) fclose(args.rfp); mrb_free(mrb, args.argv); if (args.libv) { for (i = 0; i < args.libc; i++) { mrb_free(mrb, args.libv[i]); } mrb_free(mrb, args.libv); } mrb_ccontext_free(mrb, cxt); mrb_close(mrb); return 0; } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-fiber0000644000000000000000000000013215077107334021470 xustar0030 mtime=1761382108.836301144 30 atime=1761382109.798298363 30 ctime=1761382108.836301144 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-fiber/0000755000175100017510000000000015077107334022135 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-fiber/PaxHeaders/mrbgem.rake0000644000000000000000000000013115077107276023666 xustar0029 mtime=1761382078.11942057 30 atime=1761382080.139411329 30 ctime=1761382108.836301144 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-fiber/mrbgem.rake0000644000175100017510000000022515077107276024256 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-fiber') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Fiber class' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-fiber/PaxHeaders/test0000644000000000000000000000013215077107334022447 xustar0030 mtime=1761382108.839301136 30 atime=1761382109.798298363 30 ctime=1761382108.839301136 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-fiber/test/0000755000175100017510000000000015077107334023114 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-fiber/test/PaxHeaders/fiber2.rb0000644000000000000000000000013115077107276024226 xustar0029 mtime=1761382078.11942057 30 atime=1761382080.140411325 30 ctime=1761382108.837301141 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-fiber/test/fiber2.rb0000644000175100017510000001152015077107276024616 0ustar00runnerrunner# This file tests fiber switching crossing C functions unless RUBY_ENGINE == "mruby" class Fiber alias resume_by_c_func resume alias resume_by_c_method resume class << self alias yield_by_c_func yield def yield_by_c_method(*args) raise FiberError, "ycan't cross C function boundary" end end end def Proc.c_tunnel yield end end begin $fiber_test_activity = __FILE__ assert('Call Fiber#resume nested with C') do assert_equal "ok1", Fiber.new { Fiber.new { "ok1" }.resume_by_c_func }.resume_by_c_func assert_equal "ok2", Fiber.new { Fiber.new { "ok2" }.resume_by_c_method }.resume_by_c_func assert_equal "ok3", Fiber.new { Fiber.new { "ok3" }.resume_by_c_func }.resume_by_c_method assert_equal "ok4", Fiber.new { Fiber.new { "ok4" }.resume_by_c_method }.resume_by_c_method assert_equal "ok5", Fiber.new { Proc.c_tunnel { Fiber.new { "ok5" }.resume_by_c_func } }.resume_by_c_func assert_equal "ok6", Fiber.new { Proc.c_tunnel { Fiber.new { "ok6" }.resume_by_c_method } }.resume_by_c_func assert_equal "ok7", Fiber.new { Proc.c_tunnel { Fiber.new { "ok7" }.resume_by_c_func } }.resume_by_c_method assert_equal "ok8", Fiber.new { Proc.c_tunnel { Fiber.new { "ok8" }.resume_by_c_method } }.resume_by_c_method assert_equal "ok9", Fiber.new { Proc.c_tunnel { Fiber.new { "ok9" }.resume } }.resume_by_c_func assert_equal "ok10", Fiber.new { Proc.c_tunnel { Fiber.new { "ok10" }.resume } }.resume_by_c_method end assert('Call Fiber#resume and Fiber.yield mixed with C.') do assert_equal 1, Fiber.new { Fiber.yield 1 }.resume_by_c_func assert_equal 2, Fiber.new { Fiber.yield 2 }.resume_by_c_method assert_equal 3, Fiber.new { Fiber.yield_by_c_func 3 }.resume assert_equal 4, Fiber.new { Fiber.yield_by_c_func 4 }.resume_by_c_func assert_equal 5, Fiber.new { Fiber.yield_by_c_func 5 }.resume_by_c_method assert_raise(FiberError) { Fiber.new { Fiber.yield_by_c_method "bad" }.resume } assert_raise(FiberError) { Fiber.new { Fiber.yield_by_c_method "bad" }.resume_by_c_func } assert_raise(FiberError) { Fiber.new { Fiber.yield_by_c_method "bad" }.resume_by_c_method } result = [] f1 = Fiber.new { result << Fiber.new { Fiber.yield 1; "bad" }.resume_by_c_func; 2 } f2 = Fiber.new { result << f1.resume; 3 } result << f2.resume assert_equal [1, 2, 3], result f1 = Fiber.new { -> { Fiber.yield 1 Fiber.yield_by_c_func 2 f2 = Fiber.new { -> { Fiber.yield_by_c_func 3 Fiber.yield 4 Fiber.yield_by_c_func 5 Fiber.yield 6 }.call 7 } Fiber.yield f2.resume_by_c_func Fiber.yield f2.resume Fiber.yield f2.resume_by_c_method Fiber.yield f2.resume Fiber.yield f2.resume_by_c_func Fiber.yield 8 }.call Fiber.yield 9 10 } result = [] 10.times { result << f1.resume } assert_equal [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], result end assert('Call Fiber#resume and Fiber.yield mixed with C and raising exceptions') do f = Fiber.new do raise ZeroDivisionError rescue Fiber.yield "rescue" "pass1" ensure Fiber.yield "ensure" end assert_equal "rescue", f.resume_by_c_method assert_equal "ensure", f.resume_by_c_method assert_equal "pass1", f.resume_by_c_method assert_raise(FiberError) { f.resume_by_c_method } f = Fiber.new do raise ZeroDivisionError rescue Fiber.yield "rescue" "pass2" ensure Fiber.yield "ensure" end assert_equal "rescue", f.resume_by_c_func assert_equal "ensure", f.resume_by_c_func assert_equal "pass2", f.resume_by_c_func assert_raise(FiberError) { f.resume_by_c_func } f2 = Fiber.new do -> do Fiber.yield 1 raise "3" ensure Fiber.yield 2 end.call "NOT REACH 1" end f1 = Fiber.new do Fiber.yield f2.resume_by_c_func begin Fiber.yield f2.resume Fiber.yield f2.resume_by_c_method Fiber.yield "NOT REACH 2" rescue => e Fiber.yield e.message Fiber.yield 4 ensure Fiber.yield 5 end Fiber.yield 6 7 end result = [] 7.times { result << f1.resume } assert_equal [1, 2, "3", 4, 5, 6, 7], result end assert('Call Fiber#transfer with C') do assert_equal "ok1", Fiber.new { Fiber.new { "ok1" }.resume_by_c_method }.transfer assert_equal "ok2", Fiber.new { Fiber.new { "ok2" }.resume_by_c_func }.transfer assert_raise(FiberError) { Proc.c_tunnel { Fiber.new { "BAD!" }.transfer } } b = Fiber.current a = Fiber.new { Proc.c_tunnel { Fiber.new { b.transfer }.resume } } assert_raise(FiberError) { a.transfer } end ensure $fiber_test_activity = nil end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-fiber/test/PaxHeaders/fibertest.c0000644000000000000000000000013115077107276024663 xustar0029 mtime=1761382078.11942057 30 atime=1761382080.140411325 30 ctime=1761382108.838301138 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-fiber/test/fibertest.c0000644000175100017510000000546115077107276025262 0ustar00runnerrunner#include #include #include #include #include static mrb_value fiber_s_yield_by_c_func(mrb_state *mrb, mrb_value self) { mrb_value a = mrb_get_arg1(mrb); return mrb_fiber_yield(mrb, 1, &a); } static mrb_value fiber_s_yield_by_c_method(mrb_state *mrb, mrb_value self) { mrb_value a = mrb_get_arg1(mrb); return mrb_funcall_argv(mrb, self, mrb_intern_lit(mrb, "yield"), 1, &a); } static mrb_value fiber_resume_by_c_func(mrb_state *mrb, mrb_value self) { ptrdiff_t ci_index = mrb->c->ci - mrb->c->cibase; mrb_value ret = mrb_fiber_resume(mrb, self, 0, NULL); if (ci_index != mrb->c->ci - mrb->c->cibase) { mrb_raisef(mrb, E_EXCEPTION, "[BUG] INVALID CI POSITION (expected %d, but actual %d) [BUG]", (int)ci_index, (int)(mrb->c->ci - mrb->c->cibase)); } return ret; } static mrb_value fiber_resume_by_c_method(mrb_state *mrb, mrb_value self) { ptrdiff_t ci_index = mrb->c->ci - mrb->c->cibase; mrb_value ret = mrb_funcall_argv(mrb, self, mrb_intern_lit(mrb, "resume"), 0, NULL); if (ci_index != mrb->c->ci - mrb->c->cibase) { mrb_raisef(mrb, E_EXCEPTION, "[BUG] INVALID CI POSITION (expected %d, but actual %d) [BUG]", (int)ci_index, (int)(mrb->c->ci - mrb->c->cibase)); } return ret; } static mrb_value fiber_transfer_by_c(mrb_state *mrb, mrb_value self) { return mrb_funcall_argv(mrb, self, mrb_intern_lit(mrb, "transfer"), 0, NULL); } static mrb_value proc_s_c_tunnel(mrb_state *mrb, mrb_value self) { mrb_value b; mrb_get_args(mrb, "&!", &b); return mrb_yield_argv(mrb, b, 0, NULL); } static void check_activity(mrb_state *mrb) { mrb_value act = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$fiber_test_activity")); if (mrb_test(act)) { act = mrb_obj_as_string(mrb, act); fprintf(stderr, "\n\t<<<%s%.*s>>>\n", "mruby VM has an unexpected outage in ", (int)RSTRING_LEN(act), RSTRING_PTR(act)); abort(); } } void mrb_mruby_fiber_gem_test(mrb_state *mrb) { struct RClass *fiber_class = mrb_class_get(mrb, "Fiber"); mrb_define_class_method(mrb, fiber_class, "yield_by_c_func", fiber_s_yield_by_c_func, MRB_ARGS_ANY()); mrb_define_class_method(mrb, fiber_class, "yield_by_c_method", fiber_s_yield_by_c_method, MRB_ARGS_ANY()); mrb_define_method(mrb, fiber_class, "resume_by_c_func", fiber_resume_by_c_func, MRB_ARGS_NONE()); mrb_define_method(mrb, fiber_class, "resume_by_c_method", fiber_resume_by_c_method, MRB_ARGS_NONE()); mrb_define_method(mrb, fiber_class, "transfer_by_c", fiber_transfer_by_c, MRB_ARGS_NONE()); mrb_define_class_method(mrb, mrb->proc_class, "c_tunnel", proc_s_c_tunnel, MRB_ARGS_NONE() | MRB_ARGS_BLOCK()); mrb_gv_set(mrb, mrb_intern_lit(mrb, "$fiber_test_activity"), mrb_nil_value()); mrb_state_atexit(mrb, check_activity); } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-fiber/test/PaxHeaders/fiber.rb0000644000000000000000000000013115077107276024144 xustar0029 mtime=1761382078.11942057 30 atime=1761382080.139411329 30 ctime=1761382108.839301136 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-fiber/test/fiber.rb0000644000175100017510000001036115077107276024536 0ustar00runnerrunnerbegin $fiber_test_activity = __FILE__ assert('Fiber.new') do f = Fiber.new{} assert_kind_of Fiber, f end assert('Fiber#resume') do f = Fiber.new{|x| x } assert_equal 2, f.resume(2) end assert('Fiber#transfer') do ary = [] f2 = nil f1 = Fiber.new{ ary << f2.transfer(:foo) :ok } f2 = Fiber.new{ ary << f1.transfer(:baz) :ng } assert_equal(:ok, f1.transfer) assert_equal([:baz], ary) assert_false f1.alive? end assert('Fiber#alive?') do f = Fiber.new{ Fiber.yield } f.resume assert_true f.alive? f.resume assert_false f.alive? end assert('Fiber#==') do root = Fiber.current assert_equal root, root assert_equal root, Fiber.current assert_false root != Fiber.current f = Fiber.new { assert_false root == Fiber.current } f.resume assert_false f == root assert_true f != root end assert('Fiber.yield') do f = Fiber.new{|x| Fiber.yield x } assert_equal 3, f.resume(3) assert_true f.alive? end assert('FiberError') do assert_equal StandardError, FiberError.superclass end assert('Fiber iteration') do f1 = Fiber.new{ [1,2,3].each{|x| Fiber.yield(x)} } f2 = Fiber.new{ [9,8,7].each{|x| Fiber.yield(x)} } a = [] 3.times { a << f1.resume a << f2.resume } assert_equal [1,9,2,8,3,7], a end assert('Fiber with splat in the block argument list') { assert_equal([1], Fiber.new{|*x|x}.resume(1)) } assert('Fiber raises on resume when dead') do assert_raise(FiberError) do f = Fiber.new{} f.resume assert_false f.alive? f.resume end end assert('Yield raises when called on root fiber') do assert_raise(FiberError) { Fiber.yield } end assert('Double resume of Fiber') do f1 = Fiber.new {} f2 = Fiber.new { f1.resume assert_raise(FiberError) { f2.resume } Fiber.yield 0 } assert_equal 0, f2.resume f2.resume assert_false f1.alive? assert_false f2.alive? end assert('Recursive resume of Fiber') do f1, f2 = nil, nil f1 = Fiber.new { assert_raise(FiberError) { f2.resume } } f2 = Fiber.new { f1.resume Fiber.yield 0 } f3 = Fiber.new { f2.resume } assert_equal 0, f3.resume f2.resume assert_false f1.alive? assert_false f2.alive? assert_false f3.alive? end assert('Root fiber resume') do root = Fiber.current assert_raise(FiberError) { root.resume } f = Fiber.new { assert_raise(FiberError) { root.resume } } f.resume assert_false f.alive? end assert('Fiber without block') do assert_raise(ArgumentError) { Fiber.new } end assert('Transfer to self.') do result = [] f = Fiber.new { result << :start; f.transfer; result << :end } f.transfer assert_equal [:start, :end], result result = [] f = Fiber.new { result << :start; f.transfer; result << :end } f.resume assert_equal [:start, :end], result end assert('Resume transferred fiber') do f = Fiber.new { assert_raise(FiberError) { f.resume } } f.transfer end assert('Root fiber transfer.') do result = nil root = Fiber.current f = Fiber.new { result = :ok root.transfer } f.transfer assert_true f.alive? assert_equal :ok, result end assert('Break nested fiber with root fiber transfer') do root = Fiber.current result = nil f2 = nil f1 = Fiber.new { root.transfer(f2.transfer) result = :f1 } f2 = Fiber.new { result = :to_root root.transfer :from_f2 result = :f2 } assert_equal :from_f2, f1.transfer assert_equal :to_root, result assert_equal :f2, f2.transfer assert_equal :f2, result assert_false f2.alive? assert_equal nil, f1.transfer assert_equal :f1, f1.transfer assert_equal :f1, result assert_false f1.alive? end assert('CRuby Fiber#transfer test.') do ary = [] f2 = nil f1 = Fiber.new{ ary << f2.transfer(:foo) :ok } f2 = Fiber.new{ ary << f1.transfer(:baz) :ng } assert_equal :ok, f1.transfer assert_equal [:baz], ary end ensure $fiber_test_activity = nil end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-fiber/PaxHeaders/src0000644000000000000000000000013015077107334022255 xustar0029 mtime=1761382108.84130113 30 atime=1761382109.798298363 29 ctime=1761382108.84130113 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-fiber/src/0000755000175100017510000000000015077107334022724 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-fiber/src/PaxHeaders/fiber.c0000644000000000000000000000013015077107276023572 xustar0029 mtime=1761382078.11942057 30 atime=1761382080.139411329 29 ctime=1761382108.84130113 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-fiber/src/fiber.c0000644000175100017510000003625615077107276024200 0ustar00runnerrunner#include #include #include #include #include #include #include #include #include #define fiber_ptr(o) ((struct RFiber*)mrb_ptr(o)) #define FIBER_STACK_INIT_SIZE 64 #define FIBER_CI_INIT_SIZE 8 /* copied from vm.c */ #define CINFO_RESUMED 3 static mrb_value init_fiber(mrb_state *mrb, struct RFiber *f, const struct RProc *p) { static const struct mrb_context mrb_context_zero = { 0 }; if (f->cxt) { mrb_raise(mrb, E_RUNTIME_ERROR, "cannot initialize twice"); } if (MRB_PROC_CFUNC_P(p)) { mrb_raise(mrb, E_FIBER_ERROR, "tried to create Fiber from C defined method"); } struct mrb_context *c = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context)); *c = mrb_context_zero; f->cxt = c; /* initialize VM stack */ size_t slen = FIBER_STACK_INIT_SIZE; if (p->body.irep->nregs > slen) { slen += p->body.irep->nregs; } c->stbase = (mrb_value*)mrb_malloc(mrb, slen*sizeof(mrb_value)); c->stend = c->stbase + slen; { mrb_value *s = c->stbase + 1; mrb_value *send = c->stend; while (s < send) { SET_NIL_VALUE(*s); s++; } } /* copy receiver from a block */ c->stbase[0] = mrb->c->ci->stack[0]; /* initialize callinfo stack */ static const mrb_callinfo ci_zero = { 0 }; c->cibase = (mrb_callinfo*)mrb_malloc(mrb, FIBER_CI_INIT_SIZE * sizeof(mrb_callinfo)); c->ciend = c->cibase + FIBER_CI_INIT_SIZE; c->ci = c->cibase; c->cibase[0] = ci_zero; /* adjust return callinfo */ mrb_callinfo *ci = c->ci; mrb_vm_ci_target_class_set(ci, MRB_PROC_TARGET_CLASS(p)); mrb_vm_ci_proc_set(ci, p); mrb_field_write_barrier(mrb, (struct RBasic*)f, (struct RBasic*)p); ci->stack = c->stbase; ci[1] = ci[0]; c->ci++; /* push dummy callinfo */ c->fib = f; c->status = MRB_FIBER_CREATED; return mrb_obj_value(f); } /* * call-seq: * Fiber.new{...} -> obj * * Creates a fiber, whose execution is suspended until it is explicitly * resumed using Fiber#resume method. * The code running inside the fiber can give up control by calling * Fiber.yield in which case it yields control back to caller * (the caller of the Fiber#resume). * * Upon yielding or termination the Fiber returns the value of the last * executed expression * * For instance: * * fiber = Fiber.new do * Fiber.yield 1 * 2 * end * * puts fiber.resume * puts fiber.resume * puts fiber.resume * * produces * * 1 * 2 * resuming dead fiber (FiberError) * * The Fiber#resume method accepts an arbitrary number of * parameters, if it is the first call to resume then they * will be passed as block arguments. Otherwise they will be the return * value of the call to Fiber.yield * * Example: * * fiber = Fiber.new do |first| * second = Fiber.yield first + 2 * end * * puts fiber.resume 10 * puts fiber.resume 14 * puts fiber.resume 18 * * produces * * 12 * 14 * resuming dead fiber (FiberError) * */ static mrb_value fiber_init(mrb_state *mrb, mrb_value self) { mrb_value blk; mrb_get_args(mrb, "&!", &blk); return init_fiber(mrb, fiber_ptr(self), mrb_proc_ptr(blk)); } static struct mrb_context* fiber_check(mrb_state *mrb, mrb_value fib) { struct RFiber *f = fiber_ptr(fib); mrb_assert(f->tt == MRB_TT_FIBER); if (!f->cxt) { mrb_raise(mrb, E_FIBER_ERROR, "uninitialized Fiber"); } return f->cxt; } static mrb_value fiber_result(mrb_state *mrb, const mrb_value *a, mrb_int len) { if (len == 0) return mrb_nil_value(); if (len == 1) return a[0]; return mrb_ary_new_from_values(mrb, len, a); } /* mark return from context modifying method */ #define MARK_CONTEXT_MODIFY(c) (c)->ci->u.keep_context = NULL static void fiber_check_cfunc(mrb_state *mrb, struct mrb_context *c) { mrb_callinfo *ci; for (ci = c->ci; ci >= c->cibase; ci--) { if (ci->cci > 0) { mrb_raise(mrb, E_FIBER_ERROR, "can't cross C function boundary"); } } } static void fiber_check_cfunc_recursive(mrb_state *mrb, struct mrb_context *c) { for (;; c = c->prev) { fiber_check_cfunc(mrb, c); if (c == mrb->root_c || !c->prev) { break; } } } static void fiber_switch_context(mrb_state *mrb, struct mrb_context *c) { if (mrb->c->fib) { mrb_write_barrier(mrb, (struct RBasic*)mrb->c->fib); } c->status = MRB_FIBER_RUNNING; mrb->c = c; } /* * Argument mesg is limited to a string literal or "static const" string. * Also, it must be called as `return fiber_error(...)`. */ static mrb_value fiber_error(mrb_state *mrb, const char *mesg) { mrb_value str = mrb_str_new_static(mrb, mesg, strlen(mesg)); mrb_value exc = mrb_exc_new_str(mrb, E_FIBER_ERROR, str); if (mrb->jmp) { mrb_exc_raise(mrb, exc); } mrb->exc = mrb_obj_ptr(exc); return exc; } /* This function must be called as `return fiber_switch(...)` */ static mrb_value fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mrb_bool resume, mrb_bool vmexec) { struct mrb_context *c = fiber_check(mrb, self); struct mrb_context *old_c = mrb->c; mrb_value value; if (resume && c == mrb->c) { return fiber_error(mrb, "attempt to resume the current fiber"); } enum mrb_fiber_state status = c->status; switch (status) { case MRB_FIBER_TRANSFERRED: if (resume) { return fiber_error(mrb, "resuming transferred fiber"); } break; case MRB_FIBER_RUNNING: case MRB_FIBER_RESUMED: return fiber_error(mrb, "double resume"); break; case MRB_FIBER_TERMINATED: return fiber_error(mrb, "resuming dead fiber"); break; default: break; } fiber_check_cfunc(mrb, c); if (resume) { old_c->status = MRB_FIBER_RESUMED; c->prev = mrb->c; } else { old_c->status = MRB_FIBER_TRANSFERRED; // c->prev = mrb->root_c; c->prev = NULL; } fiber_switch_context(mrb, c); if (status == MRB_FIBER_CREATED) { if (!c->ci->proc) { return fiber_error(mrb, "double resume (current)"); } if (vmexec) { c->ci--; /* pop dummy callinfo */ } if (len >= 15) { mrb_stack_extend(mrb, 3); /* for receiver, args and (optional) block */ c->stbase[1] = mrb_ary_new_from_values(mrb, len, a); len = 15; } else { mrb_stack_extend(mrb, len+2); /* for receiver and (optional) block */ mrb_value *b = c->stbase+1; mrb_value *e = b + len; while (bcibase->n = (uint8_t)len; struct REnv *env = MRB_PROC_ENV(c->cibase->proc); if (env && env->stack) { value = env->stack[0]; } else { value = mrb_top_self(mrb); } c->stbase[0] = value; } else { value = fiber_result(mrb, a, len); if (vmexec) { if (c->ci > c->cibase) c->ci--; /* pop dummy callinfo */ c->ci[1].stack[0] = value; } } if (vmexec) { int cci = old_c->ci->cci; c->vmexec = TRUE; value = mrb_vm_exec(mrb, c->ci->proc, c->ci->pc); mrb->c = old_c; old_c->ci->cci = cci; /* restore values as they may have changed in Fiber.yield */ } else { MARK_CONTEXT_MODIFY(c); } return value; } /* * call-seq: * fiber.resume(args, ...) -> obj * * Resumes the fiber from the point at which the last Fiber.yield * was called, or starts running it if it is the first call to * resume. Arguments passed to resume will be the value of * the Fiber.yield expression or will be passed as block * parameters to the fiber's block if this is the first resume. * * Alternatively, when resume is called it evaluates to the arguments passed * to the next Fiber.yield statement inside the fiber's block * or to the block value if it runs to completion without any * Fiber.yield */ static mrb_value fiber_resume(mrb_state *mrb, mrb_value self) { const mrb_value *a; mrb_int len; mrb_bool vmexec = FALSE; mrb_get_args(mrb, "*!", &a, &len); if (mrb->c->ci->cci > 0) { vmexec = TRUE; } return fiber_switch(mrb, self, len, a, TRUE, vmexec); } MRB_API mrb_value mrb_fiber_resume(mrb_state *mrb, mrb_value fib, mrb_int len, const mrb_value *a) { return fiber_switch(mrb, fib, len, a, TRUE, TRUE); } /* * call-seq: * fiber.alive? -> true or false * * Returns true if the fiber can still be resumed. After finishing * execution of the fiber block this method will always return false. */ MRB_API mrb_value mrb_fiber_alive_p(mrb_state *mrb, mrb_value self) { struct mrb_context *c = fiber_check(mrb, self); return mrb_bool_value(c->status != MRB_FIBER_TERMINATED); } #define fiber_alive_p mrb_fiber_alive_p static mrb_value fiber_eq(mrb_state *mrb, mrb_value self) { mrb_value other = mrb_get_arg1(mrb); if (!mrb_fiber_p(other)) { return mrb_false_value(); } return mrb_bool_value(fiber_ptr(self) == fiber_ptr(other)); } /* * call-seq: * fiber.to_s -> string * fiber.inspect -> string * * Returns fiber object information as a string. * * If the file information cannot be obtained, it is replaced with `(unknown):0`. * Also, if the fiber is terminated, it will be replaced in the same way (mruby limitation). */ static mrb_value fiber_to_s(mrb_state *mrb, mrb_value self) { fiber_check(mrb, self); const struct RFiber *f = fiber_ptr(self); mrb_value s = mrb_str_new_lit(mrb, "#<"); mrb_value cname = mrb_class_path(mrb, mrb_class_real(mrb_class(mrb, self))); if (mrb_nil_p(cname)) { mrb_str_cat_lit(mrb, s, "Fiber:"); } else { mrb_str_cat_str(mrb, s, cname); mrb_str_cat_lit(mrb, s, ":"); } mrb_str_cat_str(mrb, s, mrb_ptr_to_str(mrb, mrb_ptr(self))); const char *file; int32_t line; const struct RProc *p; if (f->cxt->status != MRB_FIBER_TERMINATED && !MRB_PROC_CFUNC_P(p = f->cxt->cibase->proc) && !MRB_PROC_ALIAS_P(p) && mrb_debug_get_position(mrb, p->body.irep, 0, &line, &file)) { mrb_str_cat_lit(mrb, s, " "); mrb_str_cat_cstr(mrb, s, file); mrb_str_cat_lit(mrb, s, ":"); char buf[16]; mrb_str_cat_cstr(mrb, s, mrb_int_to_cstr(buf, sizeof(buf), line, 10)); } const char *st; switch (fiber_ptr(self)->cxt->status) { case MRB_FIBER_CREATED: st = "created"; break; case MRB_FIBER_RUNNING: st = "resumed"; break; case MRB_FIBER_RESUMED: st = "suspended by resuming"; break; case MRB_FIBER_SUSPENDED: st = "suspended"; break; case MRB_FIBER_TRANSFERRED: st = "suspended"; break; case MRB_FIBER_TERMINATED: st = "terminated"; break; default: st = "UNKNOWN STATUS (BUG)"; break; } mrb_str_cat_lit(mrb, s, " ("); mrb_str_cat_cstr(mrb, s, st); mrb_str_cat_lit(mrb, s, ")>"); return s; } /* * call-seq: * fiber.transfer(args, ...) -> obj * * Transfers control to receiver fiber of the method call. * Unlike resume the receiver wouldn't be pushed to call * stack of fibers. Instead it will switch to the call stack of * transferring fiber. * When resuming a fiber that was transferred to another fiber it would * cause double resume error. Though when the fiber is re-transferred * and Fiber.yield is called, the fiber would be resumable. */ static mrb_value fiber_transfer(mrb_state *mrb, mrb_value self) { struct mrb_context *c = fiber_check(mrb, self); const mrb_value* a; mrb_int len; fiber_check_cfunc_recursive(mrb, mrb->c); mrb_get_args(mrb, "*!", &a, &len); if (c->status == MRB_FIBER_RESUMED) { mrb_raise(mrb, E_FIBER_ERROR, "attempt to transfer to a resuming fiber"); } if (c == mrb->root_c) { mrb->c->status = MRB_FIBER_TRANSFERRED; fiber_switch_context(mrb, c); MARK_CONTEXT_MODIFY(c); return fiber_result(mrb, a, len); } if (c == mrb->c) { return fiber_result(mrb, a, len); } return fiber_switch(mrb, self, len, a, FALSE, FALSE); } MRB_API mrb_value mrb_fiber_yield(mrb_state *mrb, mrb_int len, const mrb_value *a) { struct mrb_context *c = mrb->c; if (!c->prev) { return fiber_error(mrb, "attempt to yield on a not resumed fiber"); } if (c == mrb->root_c) { return fiber_error(mrb, "can't yield from root fiber"); } if (c->prev->status == MRB_FIBER_TRANSFERRED) { return fiber_error(mrb, "attempt to yield on a not resumed fiber"); } fiber_check_cfunc(mrb, c); c->status = MRB_FIBER_SUSPENDED; fiber_switch_context(mrb, c->prev); c->prev = NULL; if (c->vmexec) { c->vmexec = FALSE; mrb->c->ci->cci = CINFO_RESUMED; } MARK_CONTEXT_MODIFY(mrb->c); return fiber_result(mrb, a, len); } /* * call-seq: * Fiber.yield(args, ...) -> obj * * Yields control back to the context that resumed the fiber, passing * along any arguments that were passed to it. The fiber will resume * processing at this point when resume is called next. * Any arguments passed to the next resume will be the * * mruby limitation: Fiber resume/yield cannot cross C function boundary. * thus you cannot yield from #initialize which is called by mrb_funcall(). * * This method cannot be called from C using mrb_funcall(). * Use mrb_fiber_yield() function instead. */ static mrb_value fiber_yield(mrb_state *mrb, mrb_value self) { const mrb_value *a; mrb_int len; mrb_get_args(mrb, "*!", &a, &len); return mrb_fiber_yield(mrb, len, a); } /* * call-seq: * Fiber.current() -> fiber * * Returns the current fiber. If you are not running in the context of * a fiber this method will return the root fiber. */ static mrb_value fiber_current(mrb_state *mrb, mrb_value self) { if (!mrb->c->fib) { struct RFiber *f = MRB_OBJ_ALLOC(mrb, MRB_TT_FIBER, mrb_class_ptr(self)); f->cxt = mrb->c; mrb->c->fib = f; } return mrb_obj_value(mrb->c->fib); } MRB_API mrb_value mrb_fiber_new(mrb_state *mrb, const struct RProc *p) { struct RClass *c = mrb_class_get_id(mrb, MRB_SYM(Fiber)); if (MRB_INSTANCE_TT(c) != MRB_TT_FIBER) { mrb_raise(mrb, E_TYPE_ERROR, "wrong Fiber class"); } struct RFiber *f = MRB_OBJ_ALLOC(mrb, MRB_TT_FIBER, c); return init_fiber(mrb, f, p); } void mrb_mruby_fiber_gem_init(mrb_state* mrb) { struct RClass *c = mrb_define_class_id(mrb, MRB_SYM(Fiber), mrb->object_class); MRB_SET_INSTANCE_TT(c, MRB_TT_FIBER); mrb_define_method_id(mrb, c, MRB_SYM(initialize), fiber_init, MRB_ARGS_NONE()|MRB_ARGS_BLOCK()); mrb_define_method_id(mrb, c, MRB_SYM(resume), fiber_resume, MRB_ARGS_ANY()); mrb_define_method_id(mrb, c, MRB_SYM(transfer), fiber_transfer, MRB_ARGS_ANY()); mrb_define_method_id(mrb, c, MRB_SYM_Q(alive), fiber_alive_p, MRB_ARGS_NONE()); mrb_define_method_id(mrb, c, MRB_OPSYM(eq), fiber_eq, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, c, MRB_SYM(to_s), fiber_to_s, MRB_ARGS_NONE()); mrb_define_alias_id(mrb, c, MRB_SYM(inspect), MRB_SYM(to_s)); mrb_define_class_method_id(mrb, c, MRB_SYM(yield), fiber_yield, MRB_ARGS_ANY()); mrb_define_class_method_id(mrb, c, MRB_SYM(current), fiber_current, MRB_ARGS_NONE()); mrb_define_class_id(mrb, MRB_SYM(FiberError), E_STANDARD_ERROR); } void mrb_mruby_fiber_gem_final(mrb_state* mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-proc-binding0000644000000000000000000000013215077107334022754 xustar0030 mtime=1761382108.788301283 30 atime=1761382109.798298363 30 ctime=1761382108.788301283 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-binding/0000755000175100017510000000000015077107334023421 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-binding/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276025153 xustar0030 mtime=1761382078.124420547 30 atime=1761382080.144411306 30 ctime=1761382108.788301283 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-binding/mrbgem.rake0000644000175100017510000000066115077107276025546 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-proc-binding') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Proc#binding method' spec.add_dependency('mruby-binding', :core => 'mruby-binding') spec.add_dependency('mruby-proc-ext', :core => 'mruby-proc-ext') spec.add_test_dependency('mruby-eval', :core => 'mruby-eval') spec.add_test_dependency('mruby-compiler', :core => 'mruby-compiler') end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-binding/PaxHeaders/test0000644000000000000000000000013215077107334023733 xustar0030 mtime=1761382108.791301274 30 atime=1761382109.798298363 30 ctime=1761382108.791301274 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-binding/test/0000755000175100017510000000000015077107334024400 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-binding/test/PaxHeaders/proc_binding.rb0000644000000000000000000000013215077107276026777 xustar0030 mtime=1761382078.124420547 30 atime=1761382080.144411306 30 ctime=1761382108.791301274 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-binding/test/proc_binding.rb0000644000175100017510000000122615077107276027370 0ustar00runnerrunnerassert "Proc#binding" do block = ->(i) {} a, b, c = 1, 2, 3 bind = block.binding assert_equal([:a, :b, :bind, :block, :c], bind.local_variables.sort) assert_equal(1, bind.local_variable_get(:a)) assert_equal(5, bind.eval("b + c")) bind.local_variable_set(:x, 9) assert_equal(9, bind.local_variable_get(:x)) end assert("Binding#source_location after Proc#binding") do skip unless -> {}.source_location block, source_location = -> {}, [__FILE__, __LINE__] assert_equal source_location, block.binding.source_location end assert "Proc#binding and .eval from C" do bind = proc_in_c.binding assert_nothing_raised { bind.eval("self") } end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-binding/test/PaxHeaders/proc_binding.c0000644000000000000000000000013115077107276026615 xustar0030 mtime=1761382078.124420547 30 atime=1761382080.144411306 29 ctime=1761382108.78930128 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-binding/test/proc_binding.c0000644000175100017510000000046615077107276027214 0ustar00runnerrunner#include #include static mrb_value proc_in_c(mrb_state *mrb, mrb_value self) { return mrb_load_string(mrb, "proc { |a, b| a + b }"); } void mrb_mruby_proc_binding_gem_test(mrb_state *mrb) { mrb_define_method(mrb, mrb->object_class, "proc_in_c", proc_in_c, MRB_ARGS_NONE()); } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-binding/PaxHeaders/src0000644000000000000000000000013215077107334023543 xustar0030 mtime=1761382108.792301271 30 atime=1761382109.798298363 30 ctime=1761382108.792301271 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-binding/src/0000755000175100017510000000000015077107334024210 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-binding/src/PaxHeaders/proc_binding.c0000644000000000000000000000013215077107276026426 xustar0030 mtime=1761382078.124420547 30 atime=1761382080.144411306 30 ctime=1761382108.792301271 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-proc-binding/src/proc_binding.c0000644000175100017510000000232515077107276027020 0ustar00runnerrunner#include #include #include #include /* provided by mruby-proc-ext */ mrb_value mrb_proc_source_location(mrb_state *mrb, const struct RProc *p); /* provided by mruby-binding */ mrb_value mrb_binding_new(mrb_state *mrb, const struct RProc *proc, mrb_value recv, struct REnv *env); static mrb_value mrb_proc_binding(mrb_state *mrb, mrb_value procval) { const struct RProc *proc = mrb_proc_ptr(procval); struct REnv *env; mrb_value receiver; if (!proc || MRB_PROC_CFUNC_P(proc) || !proc->upper || MRB_PROC_CFUNC_P(proc->upper)) { env = NULL; proc = NULL; receiver = mrb_nil_value(); } else { env = MRB_PROC_ENV(proc); mrb_assert(env); proc = proc->upper; receiver = MRB_ENV_LEN(env) > 0 ? env->stack[0] : mrb_nil_value(); } mrb_value binding = mrb_binding_new(mrb, proc, receiver, env); mrb_iv_set(mrb, binding, MRB_SYM(source_location), mrb_proc_source_location(mrb, mrb_proc_ptr(procval))); return binding; } void mrb_mruby_proc_binding_gem_init(mrb_state *mrb) { mrb_define_method_id(mrb, mrb->proc_class, MRB_SYM(binding), mrb_proc_binding, MRB_ARGS_NONE()); } void mrb_mruby_proc_binding_gem_final(mrb_state *mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-binding0000644000000000000000000000013215077107334022013 xustar0030 mtime=1761382108.740301422 30 atime=1761382109.798298363 30 ctime=1761382108.740301422 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-binding/0000755000175100017510000000000015077107334022460 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-binding/PaxHeaders/mrbgem.rake0000644000000000000000000000013115077107276024211 xustar0029 mtime=1761382078.10842062 30 atime=1761382080.128411379 30 ctime=1761382108.740301422 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-binding/mrbgem.rake0000644000175100017510000000036715077107276024610 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-binding') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Binding class (core features only)' spec.add_test_dependency('mruby-proc-ext', :core => 'mruby-proc-ext') end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-binding/PaxHeaders/test0000644000000000000000000000013215077107334022772 xustar0030 mtime=1761382108.743301413 30 atime=1761382109.798298363 30 ctime=1761382108.743301413 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-binding/test/0000755000175100017510000000000015077107334023437 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-binding/test/PaxHeaders/binding.c0000644000000000000000000000013115077107276024631 xustar0029 mtime=1761382078.10842062 30 atime=1761382080.128411379 30 ctime=1761382108.743301413 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-binding/test/binding.c0000644000175100017510000000053615077107276025226 0ustar00runnerrunner#include #include static mrb_value binding_in_c(mrb_state *mrb, mrb_value self) { return mrb_funcall_argv(mrb, mrb_obj_value(mrb->object_class), MRB_SYM(binding), 0, NULL); } void mrb_mruby_binding_gem_test(mrb_state *mrb) { mrb_define_method(mrb, mrb->object_class, "binding_in_c", binding_in_c, MRB_ARGS_NONE()); } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-binding/test/PaxHeaders/binding.rb0000644000000000000000000000013115077107276025012 xustar0029 mtime=1761382078.10842062 30 atime=1761382080.128411379 30 ctime=1761382108.741301419 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-binding/test/binding.rb0000644000175100017510000000327515077107276025412 0ustar00runnerrunnerassert("Kernel.#binding") do assert_kind_of Binding, binding end assert("Binding#local_variables") do block = Proc.new do |a| b = 1 binding end assert_equal [:a, :b, :block], block.call(0).local_variables.sort end assert("Binding#local_variable_set") do bind = binding 1.times { assert_equal(9, bind.local_variable_set(:x, 9)) assert_raise(NameError) { x } assert_equal([:bind, :x], bind.local_variables.sort) } end assert("Binding#local_variable_get") do bind = binding x = 1 1.times { y = 2 assert_equal(1, bind.local_variable_get(:x)) x = 10 assert_equal(10, bind.local_variable_get(:x)) assert_raise(NameError) { bind.local_variable_get(:y) } assert_equal([:bind, :x], bind.local_variables.sort) } end assert("Binding#source_location") do skip unless -> {}.source_location bind, source_location = binding, [__FILE__, __LINE__] assert_equal source_location, bind.source_location end assert("Binding#dup") do x = 5 bind1 = binding bind1.local_variable_set(:y, 10) bind2 = bind1.dup assert_equal 5, bind2.local_variable_get(:x) assert_equal 10, bind2.local_variable_get(:y) x = 50 assert_equal 50, bind1.local_variable_get(:x) assert_equal 50, bind2.local_variable_get(:x) bind1.local_variable_set(:y, 20) assert_equal 20, bind1.local_variable_get(:y) assert_equal 20, bind2.local_variable_get(:y) bind1.local_variable_set(:z, 30) assert_raise(NameError) { bind2.local_variable_get(:z) } bind2.local_variable_set(:z, 40) assert_equal 30, bind1.local_variable_get(:z) assert_equal 40, bind2.local_variable_get(:z) end assert "Kernel#binding and .eval from C" do assert_raise(RuntimeError) { binding_in_c } end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-binding/PaxHeaders/src0000644000000000000000000000013015077107334022600 xustar0029 mtime=1761382108.74430141 30 atime=1761382109.798298363 29 ctime=1761382108.74430141 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-binding/src/0000755000175100017510000000000015077107334023247 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-binding/src/PaxHeaders/binding.c0000644000000000000000000000013015077107276024440 xustar0029 mtime=1761382078.10842062 30 atime=1761382080.128411379 29 ctime=1761382108.74430141 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-binding/src/binding.c0000644000175100017510000002736215077107276025044 0ustar00runnerrunner#include #include #include #include #include #include #include #include #include #include #define BINDING_UPPER_DEFAULT 20 #define BINDING_UPPER_MINIMUM 10 #define BINDING_UPPER_MAXIMUM 100 #ifndef MRB_BINDING_UPPER_MAX # define BINDING_UPPER_MAX BINDING_UPPER_DEFAULT #else # if (MRB_BINDING_UPPER_MAX) > BINDING_UPPER_MAXIMUM # define BINDING_UPPER_MAX BINDING_UPPER_MAXIMUM # elif (MRB_BINDING_UPPER_MAX) < BINDING_UPPER_MINIMUM # define BINDING_UPPER_MAX BINDING_UPPER_MINIMUM # else # define BINDING_UPPER_MAX MRB_BINDING_UPPER_MAX # endif #endif static mrb_int binding_extract_pc(mrb_state *mrb, mrb_value binding) { mrb_value obj = mrb_iv_get(mrb, binding, MRB_SYM(pc)); if (mrb_nil_p(obj)) { return -1; } else { mrb_check_type(mrb, obj, MRB_TT_INTEGER); return mrb_int(mrb, obj); } } const struct RProc * mrb_binding_extract_proc(mrb_state *mrb, mrb_value binding) { mrb_value obj = mrb_iv_get(mrb, binding, MRB_SYM(proc)); mrb_check_type(mrb, obj, MRB_TT_PROC); return mrb_proc_ptr(obj); } struct REnv * mrb_binding_extract_env(mrb_state *mrb, mrb_value binding) { mrb_value obj = mrb_iv_get(mrb, binding, MRB_SYM(env)); if (mrb_nil_p(obj)) { return NULL; } else { mrb_check_type(mrb, obj, MRB_TT_ENV); return (struct REnv*)mrb_obj_ptr(obj); } } static mrb_irep * binding_irep_new_lvspace(mrb_state *mrb) { static const mrb_code iseq_dummy[] = { OP_RETURN, 0 }; mrb_irep *irep = mrb_add_irep(mrb); irep->flags = MRB_ISEQ_NO_FREE; irep->iseq = iseq_dummy; irep->ilen = sizeof(iseq_dummy) / sizeof(iseq_dummy[0]); irep->lv = NULL; irep->nlocals = 1; irep->nregs = 1; return irep; } static struct RProc * binding_proc_new_lvspace(mrb_state *mrb, const struct RProc *upper, struct REnv *env) { struct RProc *lvspace = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, mrb->proc_class); lvspace->body.irep = binding_irep_new_lvspace(mrb); lvspace->upper = upper; if (env && env->tt == MRB_TT_ENV) { lvspace->e.env = env; lvspace->flags |= MRB_PROC_ENVSET; } return lvspace; } static struct REnv * binding_env_new_lvspace(mrb_state *mrb, const struct REnv *e) { struct REnv *env = MRB_OBJ_ALLOC(mrb, MRB_TT_ENV, NULL); mrb_value *stacks = (mrb_value*)mrb_calloc(mrb, 1, sizeof(mrb_value)); env->mid = 0; env->stack = stacks; if (e && e->stack && MRB_ENV_LEN(e) > 0) { env->stack[0] = e->stack[0]; } else { env->stack[0] = mrb_nil_value(); } MRB_ENV_SET_LEN(env, 1); return env; } static void binding_check_proc_upper_count(mrb_state *mrb, const struct RProc *proc) { for (size_t count = 0; proc && !MRB_PROC_CFUNC_P(proc); proc = proc->upper) { count++; if (count > BINDING_UPPER_MAX) { mrb_raise(mrb, E_RUNTIME_ERROR, "too many upper procs for local variables (mruby limitation; maximum is " MRB_STRINGIZE(BINDING_UPPER_MAX) ")"); } if (MRB_PROC_SCOPE_P(proc)) break; } } mrb_bool mrb_binding_p(mrb_state *mrb, mrb_value obj) { if (!mrb_obj_is_kind_of(mrb, obj, mrb_class_get_id(mrb, MRB_SYM(Binding)))) return FALSE; if (mrb_type(obj) != MRB_TT_OBJECT) return FALSE; if (!mrb_obj_iv_defined(mrb, mrb_obj_ptr(obj), MRB_SYM(proc))) return FALSE; if (!mrb_obj_iv_defined(mrb, mrb_obj_ptr(obj), MRB_SYM(recv))) return FALSE; if (!mrb_obj_iv_defined(mrb, mrb_obj_ptr(obj), MRB_SYM(env))) return FALSE; return TRUE; } static void binding_type_ensure(mrb_state *mrb, mrb_value obj) { if (mrb_binding_p(mrb, obj)) return; mrb_raise(mrb, E_TYPE_ERROR, "not a binding"); } static struct RProc* binding_wrap_lvspace(mrb_state *mrb, const struct RProc *proc, struct REnv **envp) { /* * local variable space: It is a space to hold the top-level variable of * binding.eval and binding.local_variable_set. */ struct RProc *lvspace = binding_proc_new_lvspace(mrb, proc, *envp); *envp = binding_env_new_lvspace(mrb, *envp); return lvspace; } static mrb_value binding_initialize_copy(mrb_state *mrb, mrb_value binding) { mrb_value src = mrb_get_arg1(mrb); binding_type_ensure(mrb, src); const struct RProc *src_proc = mrb_binding_extract_proc(mrb, src); struct REnv *src_env = mrb_binding_extract_env(mrb, src); mrb_check_frozen(mrb, mrb_obj_ptr(binding)); struct RProc *lvspace; struct REnv *env; if (MRB_ENV_LEN(src_env) < 2) { /* when local variables of src are self only */ env = src_proc->e.env; lvspace = binding_wrap_lvspace(mrb, src_proc->upper, &env); } else { binding_check_proc_upper_count(mrb, src_proc); env = src_env; lvspace = binding_wrap_lvspace(mrb, src_proc, &env); // The reason for using the mrb_obj_iv_set_force() function is to allow local // variables to be modified even if src is frozen. This behavior is CRuby imitation. src_proc = binding_wrap_lvspace(mrb, src_proc, &src_env); struct RObject *o = mrb_obj_ptr(src); mrb_obj_iv_set_force(mrb, o, MRB_SYM(proc), mrb_obj_value((struct RProc*)src_proc)); mrb_obj_iv_set_force(mrb, o, MRB_SYM(env), mrb_obj_value(src_env)); } mrb_iv_set(mrb, binding, MRB_SYM(proc), mrb_obj_value(lvspace)); mrb_iv_set(mrb, binding, MRB_SYM(env), mrb_obj_value(env)); return binding; } static void binding_local_variable_name_check(mrb_state *mrb, mrb_sym id) { if (id == 0) { badname: mrb_raisef(mrb, E_NAME_ERROR, "wrong local variable name %!n for binding", id); } mrb_int len; const char *name = mrb_sym_name_len(mrb, id, &len); if (len == 0) { goto badname; } if (ISASCII(*name) && !(*name == '_' || ISLOWER(*name))) { goto badname; } len--; name++; for (; len > 0; len--, name++) { if (ISASCII(*name) && !(*name == '_' || ISALNUM(*name))) { goto badname; } } } static mrb_value * binding_local_variable_search(mrb_state *mrb, const struct RProc *proc, struct REnv *env, mrb_sym varname) { binding_local_variable_name_check(mrb, varname); while (proc) { if (MRB_PROC_CFUNC_P(proc)) break; const mrb_irep *irep = proc->body.irep; const mrb_sym *lv; if (irep && (lv = irep->lv)) { for (int i = 0; i + 1 < irep->nlocals; i++, lv++) { if (varname == *lv) { return (env && MRB_ENV_LEN(env) > i) ? &env->stack[i + 1] : NULL; } } } if (MRB_PROC_SCOPE_P(proc)) break; env = MRB_PROC_ENV(proc); proc = proc->upper; } return NULL; } /* * call-seq: * local_variable_defined?(symbol) -> bool */ static mrb_value binding_local_variable_defined_p(mrb_state *mrb, mrb_value self) { mrb_sym varname; mrb_get_args(mrb, "n", &varname); const struct RProc *proc = mrb_binding_extract_proc(mrb, self); struct REnv *env = mrb_binding_extract_env(mrb, self); mrb_value *e = binding_local_variable_search(mrb, proc, env, varname); if (e) { return mrb_true_value(); } else { return mrb_false_value(); } } /* * call-seq: * local_variable_get(symbol) -> object */ static mrb_value binding_local_variable_get(mrb_state *mrb, mrb_value self) { mrb_sym varname; mrb_get_args(mrb, "n", &varname); const struct RProc *proc = mrb_binding_extract_proc(mrb, self); struct REnv *env = mrb_binding_extract_env(mrb, self); mrb_value *e = binding_local_variable_search(mrb, proc, env, varname); if (!e) { mrb_raisef(mrb, E_NAME_ERROR, "local variable %!n is not defined", varname); } return *e; } static mrb_value binding_local_variable_set(mrb_state *mrb, mrb_value self) { mrb_sym varname; mrb_value obj; mrb_get_args(mrb, "no", &varname, &obj); const struct RProc *proc = mrb_binding_extract_proc(mrb, self); struct REnv *env = mrb_binding_extract_env(mrb, self); mrb_value *e = binding_local_variable_search(mrb, proc, env, varname); if (e) { *e = obj; if (!mrb_immediate_p(obj)) { mrb_field_write_barrier(mrb, (struct RBasic*)env, (struct RBasic*)mrb_obj_ptr(obj)); } } else { mrb_proc_merge_lvar(mrb, (mrb_irep*)proc->body.irep, env, 1, &varname, &obj); } return obj; } static mrb_value binding_local_variables(mrb_state *mrb, mrb_value self) { const struct RProc *proc = mrb_proc_ptr(mrb_iv_get(mrb, self, MRB_SYM(proc))); return mrb_proc_local_variables(mrb, proc); } static mrb_value binding_receiver(mrb_state *mrb, mrb_value self) { return mrb_iv_get(mrb, self, MRB_SYM(recv)); } /* * call-seq: * source_location -> [String, Integer] */ static mrb_value binding_source_location(mrb_state *mrb, mrb_value self) { if (mrb_iv_defined(mrb, self, MRB_SYM(source_location))) { return mrb_iv_get(mrb, self, MRB_SYM(source_location)); } mrb_value srcloc; const struct RProc *proc = mrb_binding_extract_proc(mrb, self); if (!proc || MRB_PROC_CFUNC_P(proc) || !proc->upper || MRB_PROC_CFUNC_P(proc->upper)) { srcloc = mrb_nil_value(); } else { const mrb_irep *irep = proc->upper->body.irep; mrb_int pc = binding_extract_pc(mrb, self); if (pc < 0) { srcloc = mrb_nil_value(); } else { const char *fname; int32_t line; if (!mrb_debug_get_position(mrb, irep, (uint32_t)pc, &line, &fname)) { srcloc = mrb_nil_value(); } else { srcloc = mrb_assoc_new(mrb, mrb_str_new_cstr(mrb, fname), mrb_fixnum_value(line)); } } } if (!mrb_frozen_p(mrb_obj_ptr(self))) { mrb_iv_set(mrb, self, MRB_SYM(source_location), srcloc); } return srcloc; } mrb_value mrb_binding_new(mrb_state *mrb, const struct RProc *proc, mrb_value recv, struct REnv *env) { struct RObject *binding = MRB_OBJ_ALLOC(mrb, MRB_TT_OBJECT, mrb_class_get_id(mrb, MRB_SYM(Binding))); if (proc && !MRB_PROC_CFUNC_P(proc)) { const mrb_irep *irep = proc->body.irep; mrb_obj_iv_set(mrb, binding, MRB_SYM(pc), mrb_fixnum_value(mrb->c->ci[-1].pc - irep->iseq - 1 /* step back */)); } proc = binding_wrap_lvspace(mrb, proc, &env); mrb_obj_iv_set(mrb, binding, MRB_SYM(proc), mrb_obj_value((void*)proc)); mrb_obj_iv_set(mrb, binding, MRB_SYM(recv), recv); mrb_obj_iv_set(mrb, binding, MRB_SYM(env), mrb_obj_value(env)); return mrb_obj_value(binding); } static mrb_value mrb_f_binding(mrb_state *mrb, mrb_value self) { struct RProc *proc; struct REnv *env; if (mrb->c->ci->cci != 0) { caller_err: mrb_raise(mrb, E_RUNTIME_ERROR, "Cannot create Binding object for non-Ruby caller"); } proc = (struct RProc*)mrb_proc_get_caller(mrb, &env); if (!env || MRB_PROC_CFUNC_P(proc)) { goto caller_err; } return mrb_binding_new(mrb, proc, self, env); } void mrb_mruby_binding_gem_init(mrb_state *mrb) { struct RClass *binding = mrb_define_class_id(mrb, MRB_SYM(Binding), mrb->object_class); MRB_SET_INSTANCE_TT(binding, MRB_TT_OBJECT); MRB_UNDEF_ALLOCATOR(binding); mrb_undef_class_method_id(mrb, binding, MRB_SYM(new)); mrb_undef_class_method_id(mrb, binding, MRB_SYM(allocate)); mrb_define_private_method_id(mrb, mrb->kernel_module, MRB_SYM(binding), mrb_f_binding, MRB_ARGS_NONE()); mrb_define_method_id(mrb, binding, MRB_SYM(initialize_copy), binding_initialize_copy, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, binding, MRB_SYM_Q(local_variable_defined), binding_local_variable_defined_p, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, binding, MRB_SYM(local_variable_get), binding_local_variable_get, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, binding, MRB_SYM(local_variable_set), binding_local_variable_set, MRB_ARGS_REQ(2)); mrb_define_method_id(mrb, binding, MRB_SYM(local_variables), binding_local_variables, MRB_ARGS_NONE()); mrb_define_method_id(mrb, binding, MRB_SYM(receiver), binding_receiver, MRB_ARGS_NONE()); mrb_define_method_id(mrb, binding, MRB_SYM(source_location), binding_source_location, MRB_ARGS_NONE()); mrb_define_method_id(mrb, binding, MRB_SYM(inspect), mrb_any_to_s, MRB_ARGS_NONE()); } void mrb_mruby_binding_gem_final(mrb_state *mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-object-ext0000644000000000000000000000013215077107334022445 xustar0030 mtime=1761382108.722301474 30 atime=1761382109.798298363 30 ctime=1761382108.722301474 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-object-ext/0000755000175100017510000000000015077107334023112 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-object-ext/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024644 xustar0030 mtime=1761382078.123420552 30 atime=1761382080.143411311 30 ctime=1761382108.722301474 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-object-ext/mrbgem.rake0000644000175100017510000000027015077107276025233 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-object-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'extensional methods shared by all objects' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-object-ext/PaxHeaders/mrblib0000644000000000000000000000013215077107334023714 xustar0030 mtime=1761382108.727301459 30 atime=1761382109.798298363 30 ctime=1761382108.727301459 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-object-ext/mrblib/0000755000175100017510000000000015077107334024361 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-object-ext/mrblib/PaxHeaders/object.rb0000644000000000000000000000013215077107276025571 xustar0030 mtime=1761382078.123420552 30 atime=1761382080.143411311 30 ctime=1761382108.727301459 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-object-ext/mrblib/object.rb0000644000175100017510000000170215077107276026161 0ustar00runnerrunnermodule Kernel # call-seq: # obj.yield_self {|_obj|...} -> an_object # obj.then {|_obj|...} -> an_object # # Yields obj and returns the result. # # 'my string'.yield_self {|s|s.upcase} #=> "MY STRING" # def yield_self(&block) return to_enum :yield_self unless block block.call(self) end alias then yield_self ## # call-seq: # obj.tap{|x|...} -> obj # # Yields x to the block, and then returns x. # The primary purpose of this method is to "tap into" a method chain, # in order to perform operations on intermediate results within the chain. # # (1..10) .tap {|x| puts "original: #{x.inspect}"} # .to_a .tap {|x| puts "array: #{x.inspect}"} # .select {|x| x%2==0} .tap {|x| puts "evens: #{x.inspect}"} # .map { |x| x*x } .tap {|x| puts "squares: #{x.inspect}"} # def tap yield self self end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-object-ext/PaxHeaders/test0000644000000000000000000000013215077107334023424 xustar0030 mtime=1761382108.726301462 30 atime=1761382109.798298363 30 ctime=1761382108.726301462 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-object-ext/test/0000755000175100017510000000000015077107334024071 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-object-ext/test/PaxHeaders/object_ext.c0000644000000000000000000000013215077107276026000 xustar0030 mtime=1761382078.123420552 30 atime=1761382080.143411311 30 ctime=1761382108.723301471 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-object-ext/test/object_ext.c0000644000175100017510000000072715077107276026376 0ustar00runnerrunner#include static mrb_value obj_instance_exec_from_c(mrb_state *mrb, mrb_value self) { mrb_int argc; const mrb_value *argv; mrb_value blk; mrb_get_args(mrb, "*&!", &argv, &argc, &blk); return mrb_funcall_with_block(mrb, self, mrb_intern_lit(mrb, "instance_exec"), argc, argv, blk); } void mrb_mruby_object_ext_gem_test(mrb_state *mrb) { mrb_define_method(mrb, mrb->kernel_module, "instance_exec_from_c", obj_instance_exec_from_c, MRB_ARGS_ANY()); } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-object-ext/test/PaxHeaders/nil.rb0000644000000000000000000000013215077107276024615 xustar0030 mtime=1761382078.123420552 30 atime=1761382080.143411311 30 ctime=1761382108.724301468 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-object-ext/test/nil.rb0000644000175100017510000000044115077107276025204 0ustar00runnerrunnerassert('NilClass#to_a') do assert_equal [], nil.to_a end assert('NilClass#to_f') do skip unless Object.const_defined?(:Float) assert_equal 0.0, nil.to_f end assert('NilClass#to_h') do assert_equal Hash.new, nil.to_h end assert('NilClass#to_i') do assert_equal 0, nil.to_i end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-object-ext/test/PaxHeaders/object.rb0000644000000000000000000000013215077107276025301 xustar0030 mtime=1761382078.123420552 30 atime=1761382080.143411311 30 ctime=1761382108.726301462 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-object-ext/test/object.rb0000644000175100017510000000365415077107276025701 0ustar00runnerrunnerassert('Object#instance_exec') do class KlassWithSecret def initialize @secret = 99 end end k = KlassWithSecret.new assert_equal 104, k.instance_exec(5) {|x| @secret+x } end assert('Object#tap') do ret = [] (1..10) .tap {|x| ret << "original: #{x.inspect}"} .to_a .tap {|x| ret << "array: #{x.inspect}"} .select {|x| x%2==0} .tap {|x| ret << "evens: #{x.inspect}"} .map { |x| x*x } .tap {|x| ret << "squares: #{x.inspect}"} assert_equal [ "original: 1..10", "array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]", "evens: [2, 4, 6, 8, 10]", "squares: [4, 16, 36, 64, 100]" ], ret assert_equal(:tap_ok, Class.new {def m; tap{return :tap_ok}; end}.new.m) end assert('instance_exec on primitives with class and module definition') do begin class A 1.instance_exec do class B end end end assert_kind_of Class, A::B ensure Object.__send__(:remove_const,:A) end begin class A 1.instance_exec do module B end end end assert_kind_of Module, A::B ensure Object.__send__(:remove_const,:A) end end assert('argument forwarding via instance_exec') do assert_equal [[], {}, nil], instance_exec { |*args, **kw, &blk| [args, kw, blk] } assert_equal [[1, 2, 3], {}, nil], instance_exec(1, 2, 3) { |*args, **kw, &blk| [args, kw, blk] } assert_equal [[], { a: 1 }, nil], instance_exec(a: 1) { |*args, **kw, &blk| [args, kw, blk] } end assert('argument forwarding via instance_exec from c') do assert_equal [[], {}, nil], instance_exec_from_c { |*args, **kw, &blk| [args, kw, blk] } assert_equal [[1, 2, 3], {}, nil], instance_exec_from_c(1, 2, 3) { |*args, **kw, &blk| [args, kw, blk] } # currently there is no easy way to call a method from C passing keyword arguments #assert_equal [[], { a: 1 }, nil], instance_exec_from_c(a: 1) { |*args, **kw, &blk| [args, kw, blk] } end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-object-ext/PaxHeaders/src0000644000000000000000000000013215077107334023234 xustar0030 mtime=1761382108.728301456 30 atime=1761382109.798298363 30 ctime=1761382108.728301456 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-object-ext/src/0000755000175100017510000000000015077107334023701 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-object-ext/src/PaxHeaders/object.c0000644000000000000000000000013215077107276024730 xustar0030 mtime=1761382078.123420552 30 atime=1761382080.143411311 30 ctime=1761382108.728301456 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-object-ext/src/object.c0000644000175100017510000000503015077107276025316 0ustar00runnerrunner#include #include #include #include #include #include #include /* * call-seq: * nil.to_a -> [] * * Always returns an empty array. */ static mrb_value nil_to_a(mrb_state *mrb, mrb_value obj) { return mrb_ary_new(mrb); } #ifndef MRB_NO_FLOAT /* * call-seq: * nil.to_f -> 0.0 * * Always returns zero. */ static mrb_value nil_to_f(mrb_state *mrb, mrb_value obj) { return mrb_float_value(mrb, 0.0); } #endif /* * call-seq: * nil.to_h -> {} * * Always returns an empty hash. */ static mrb_value nil_to_h(mrb_state *mrb, mrb_value obj) { return mrb_hash_new(mrb); } /* * call-seq: * nil.to_i -> 0 * * Always returns zero. */ static mrb_value nil_to_i(mrb_state *mrb, mrb_value obj) { return mrb_fixnum_value(0); } /* * Document-method: Kernel#itself * * call-seq: * obj.itself -> an_object * * Returns obj. * * string = 'my string' #=> "my string" * string.itself.object_id == string.object_id #=> true * */ /* * call-seq: * obj.instance_exec(arg...) {|var...| block } -> obj * * Executes the given block within the context of the receiver * (_obj_). In order to set the context, the variable +self+ is set * to _obj_ while the code is executing, giving the code access to * _obj_'s instance variables. Arguments are passed as block parameters. * * class KlassWithSecret * def initialize * @secret = 99 * end * end * k = KlassWithSecret.new * k.instance_exec(5) {|x| @secret+x } #=> 104 */ static mrb_value obj_instance_exec(mrb_state *mrb, mrb_value self) { return mrb_object_exec(mrb, self, mrb_singleton_class_ptr(mrb, self)); } void mrb_mruby_object_ext_gem_init(mrb_state* mrb) { struct RClass * n = mrb->nil_class; mrb_define_method_id(mrb, n, MRB_SYM(to_a), nil_to_a, MRB_ARGS_NONE()); #ifndef MRB_NO_FLOAT mrb_define_method_id(mrb, n, MRB_SYM(to_f), nil_to_f, MRB_ARGS_NONE()); #endif mrb_define_method_id(mrb, n, MRB_SYM(to_h), nil_to_h, MRB_ARGS_NONE()); mrb_define_method_id(mrb, n, MRB_SYM(to_i), nil_to_i, MRB_ARGS_NONE()); mrb_define_method_id(mrb, mrb->kernel_module, MRB_SYM(itself), mrb_obj_itself, MRB_ARGS_NONE()); mrb_define_method_id(mrb, mrb_class_get_id(mrb, MRB_SYM(BasicObject)), MRB_SYM(instance_exec), obj_instance_exec, MRB_ARGS_ANY() | MRB_ARGS_BLOCK()); } void mrb_mruby_object_ext_gem_final(mrb_state* mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-set0000644000000000000000000000013215077107334021174 xustar0030 mtime=1761382108.621301766 30 atime=1761382109.798298363 30 ctime=1761382108.621301766 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-set/0000755000175100017510000000000015077107334021641 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-set/PaxHeaders/LICENSE0000644000000000000000000000013215077107276022263 xustar0030 mtime=1761382078.126420538 30 atime=1761382080.145411302 30 ctime=1761382108.620301769 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-set/LICENSE0000644000175100017510000000215315077107276022654 0ustar00runnerrunnermruby-set Copyright (c) yui-knk 2016 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. [ MIT license: http://www.opensource.org/licenses/mit-license.php ] nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-set/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276023373 xustar0030 mtime=1761382078.126420538 30 atime=1761382080.145411302 30 ctime=1761382108.615301783 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-set/mrbgem.rake0000644000175100017510000000036415077107276023766 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-set') do |spec| spec.license = 'MIT' spec.authors = 'yui-knk' spec.add_dependency "mruby-hash-ext", :core => "mruby-hash-ext" spec.add_dependency "mruby-enumerator", :core => "mruby-enumerator" end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-set/PaxHeaders/mrblib0000644000000000000000000000013215077107334022443 xustar0030 mtime=1761382108.619301772 30 atime=1761382109.798298363 30 ctime=1761382108.619301772 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-set/mrblib/0000755000175100017510000000000015077107334023110 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-set/mrblib/PaxHeaders/set.rb0000644000000000000000000000013215077107276023645 xustar0030 mtime=1761382078.126420538 30 atime=1761382080.145411302 30 ctime=1761382108.619301772 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-set/mrblib/set.rb0000644000175100017510000001306115077107276024236 0ustar00runnerrunnerclass Set include Enumerable # internal method def __do_with_enum(enum, &block) if enum.respond_to?(:each) enum.each(&block) else raise ArgumentError, "value must be enumerable" end end # internal method to get internal hash def __get_hash @hash end def self.[](*ary) new(ary) end def initialize(enum = nil, &block) @hash ||= Hash.new enum.nil? and return if block_given? __do_with_enum(enum) { |o| add(block.call(o)) } else merge(enum) end end def initialize_copy(orig) super @hash = orig.__get_hash.dup end # def freeze # @hash.freeze # super # end def size @hash.size end alias length size def empty? @hash.empty? end def clear @hash.clear self end def replace(enum) clear merge(enum) end def to_a @hash.keys end # def to_set # end # def flatten_merge(set, seen = Set.new) seen.add(set.object_id) set.each { |e| if e.is_a?(Set) if seen.include?(e_id = e.object_id) raise ArgumentError, "tried to flatten recursive Set" end flatten_merge(e, seen) else add(e) end } seen.delete(set.object_id) self end def flatten self.class.new.flatten_merge(self) end def flatten! if detect { |e| e.is_a?(Set) } replace(flatten()) else nil end end def include?(o) @hash.include?(o) end alias member? include? alias === include? def superset?(set) raise ArgumentError, "value must be a set" unless set.is_a?(Set) return false if size < set.size set.all? { |o| include?(o) } end alias >= superset? def proper_superset?(set) raise ArgumentError, "value must be a set" unless set.is_a?(Set) return false if size <= set.size set.all? { |o| include?(o) } end alias > proper_superset? def subset?(set) raise ArgumentError, "value must be a set" unless set.is_a?(Set) return false if set.size < size all? { |o| set.include?(o) } end alias <= subset? def proper_subset?(set) raise ArgumentError, "value must be a set" unless set.is_a?(Set) return false if set.size <= size all? { |o| set.include?(o) } end alias < proper_subset? def intersect?(set) raise ArgumentError, "value must be a set" unless set.is_a?(Set) if size < set.size any? { |o| set.include?(o) } else set.any? { |o| include?(o) } end end def disjoint?(set) !intersect?(set) end def each(&block) return to_enum :each unless block_given? @hash.each_key(&block) self end def add(o) @hash[o] = true self end alias << add def add?(o) if include?(o) nil else add(o) end end def delete(o) @hash.delete(o) self end def delete?(o) if include?(o) delete(o) else nil end end def delete_if return to_enum :delete_if unless block_given? select { |o| yield o }.each { |o| @hash.delete(o) } self end def keep_if return to_enum :keep_if unless block_given? reject { |o| yield o }.each { |o| @hash.delete(o) } self end def collect! return to_enum :collect! unless block_given? set = self.class.new each { |o| set << yield(o) } replace(set) end alias map! collect! def reject!(&block) return to_enum :reject! unless block_given? n = size delete_if(&block) size == n ? nil : self end def select!(&block) return to_enum :select! unless block_given? n = size keep_if(&block) size == n ? nil : self end alias filter! select! def merge(enum) if enum.instance_of?(self.class) @hash.merge!(enum.__get_hash) else __do_with_enum(enum) { |o| add(o) } end self end def subtract(enum) __do_with_enum(enum) { |o| delete(o) } self end def |(enum) dup.merge(enum) end alias + | alias union | def -(enum) dup.subtract(enum) end alias difference - def &(enum) n = Set.new __do_with_enum(enum) { |o| n.add(o) if include?(o) } n end alias intersection & def ^(enum) (self | Set.new(enum)) - (self & Set.new(enum)) end def ==(other) if self.equal?(other) true elsif other.instance_of?(self.class) && self.size == other.size @hash == other.__get_hash elsif other.is_a?(self.class) && self.size == other.size other.all? { |o| include?(o) } else false end end def <=>(set) return unless set.is_a?(Set) case size <=> set.size when -1 then -1 if proper_subset?(set) when +1 then +1 if proper_superset?(set) else 0 if self.==(set) end end def hash @hash.hash end def eql?(o) return false unless o.is_a?(Set) @hash.eql?(o.__get_hash) end def classify return to_enum :classify unless block_given? h = {} each { |i| x = yield(i) (h[x] ||= self.class.new).add(i) } h end def divide(&func) return to_enum :divide unless block_given? if func.arity == 2 raise NotImplementedError, "Set#divide with 2 arity block is not implemented." end Set.new(classify(&func).values) end def join(separator = nil) to_a.join(separator) end def inspect return "#<#{self.class}: {}>" if empty? return "#<#{self.class}: {...}>" if self.__inspect_recursive? ary = map {|o| o.inspect } "#<#{self.class}: {#{ary.join(", ")}}>" end alias to_s inspect def reset if frozen? raise FrozenError, "can't modify frozen Set" else @hash.rehash end end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-set/PaxHeaders/README.md0000644000000000000000000000013115077107276022534 xustar0030 mtime=1761382078.126420538 30 atime=1761382080.145411302 29 ctime=1761382108.61630178 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-set/README.md0000644000175100017510000000065215077107276023130 0ustar00runnerrunner# mruby-set Set class ## Example ```ruby set1 = Set.new([1,2]) set2 = Set[1,2,3] set3 = Set[4] set1 + set3 #=> # set2 - set1 #=> # set2 & set1 #=> # set1 ^ set2 #=> # ``` ## Limitations These methods are not implemented yet: - freeze - to_set - divide(Set#divide with 2 arity block is not implemented.) ## License Under the MIT License: - see [LICENSE](LICENSE) file nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-set/PaxHeaders/test0000644000000000000000000000013215077107334022153 xustar0030 mtime=1761382108.618301774 30 atime=1761382109.798298363 30 ctime=1761382108.618301774 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-set/test/0000755000175100017510000000000015077107334022620 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-set/test/PaxHeaders/set.rb0000644000000000000000000000013215077107276023355 xustar0030 mtime=1761382078.126420538 30 atime=1761382080.145411302 30 ctime=1761382108.618301774 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-set/test/set.rb0000644000175100017510000003117215077107276023751 0ustar00runnerrunner## ## Set Test ## assert("Set.new") do assert_nothing_raised { Set.new() Set.new(nil) Set.new([]) Set.new([1,2]) Set.new(1..3) } assert_raise(ArgumentError) { Set.new(false) } assert_raise(ArgumentError) { Set.new(1) } assert_raise(ArgumentError) { Set.new(1,2) } ary = [2,4,6,4] set = Set.new(ary) ary.clear assert_false set.empty? assert_equal(3, set.size) ary = [1,2,3] s = Set.new(ary) { |o| o * 2 } assert_equal([2,4,6], s.sort) end assert("Set.[]") do assert_nothing_raised { Set[] Set[nil] Set[[]] Set[[1,2]] Set['a'..'c'] Set[false] Set[1] Set[1,2] } ary = [2,4,6,4] set = Set[ary] ary.clear assert_false set.empty? assert_equal([[]], set.to_a) end assert("Set#clone") do set1 = Set.new set2 = set1.clone assert_false set1.equal?(set2) # assert_not_same assert_equal(set1, set2) set1 << 'abc' assert_equal(Set.new, set2) end assert("Set#dup") do set1 = Set[1,2] set2 = set1.dup assert_false set1.equal?(set2) # assert_not_same assert_equal(set1, set2) set1 << 'abc' assert_equal(Set[1,2], set2) end assert("Set#size") do assert_equal(0, Set[].size) assert_equal(1, Set[nil].size) assert_equal(1, Set[[]].size) assert_equal(1, Set[[nil]].size) end assert("Set#empty?") do assert_true Set[].empty? assert_false Set[1,2].empty? end assert("Set#clear") do set = Set[1,2] ret = set.clear assert_true set.equal?(ret) # assert_same assert_true set.empty? end assert("Set#replace") do set = Set[1,2] ret = set.replace(['a','b','c']) assert_true set.equal?(ret) # assert_same assert_equal(Set['a','b','c'], set) set = Set[1,2] ret = set.replace(Set['a','b','c']) assert_true set.equal?(ret) # assert_same assert_equal(Set['a','b','c'], set) end assert("Set#to_a") do set = Set[1,2,3,2] ary = set.to_a assert_equal([1,2,3], ary.sort) end assert("Set#flatten") do # test1 set1 = Set[ 1, Set[ 5, Set[7, Set[0] ], Set[6,2], 1 ], 3, Set[3,4] ] set2 = set1.flatten set3 = Set.new(0..7) assert_false set1.equal?(set2) # assert_not_same assert_equal(set3, set2) # test2; multiple occurrences of a set in an set set1 = Set[1, 2] set2 = Set[set1, Set[set1, 4], 3] assert_nothing_raised { set3 = set2.flatten } assert_equal(Set.new(1..4), set3) # test3; recursion set2 = Set[] set1 = Set[1, set2] set2.add(set1) assert_raise(ArgumentError) { set1.flatten } # test4; miscellaneous empty = Set[] set = Set[Set[empty, "a"], Set[empty, "b"]] assert_nothing_raised { set.flatten } end assert("Set#flatten!") do # test1 set1 = Set[ 1, Set[ 5, Set[7, Set[0] ], Set[6,2], 1 ], 3, Set[3,4] ] set3 = Set.new(0..7) orig_set1 = set1 set1.flatten! assert_true orig_set1.equal?(set1) # assert_same assert_equal(set3, set1) # test2; multiple occurrences of a set in an set set1 = Set[1, 2] set2 = Set[set1, Set[set1, 4], 3] assert_nothing_raised { set2.flatten! } assert_equal(Set.new(1..4), set2) # test3; recursion set2 = Set[] set1 = Set[1, set2] set2.add(set1) assert_raise(ArgumentError) { set1.flatten! } # test4; miscellaneous assert_nil(Set.new(0..31).flatten!) x = Set[Set[],Set[1,2]].flatten! y = Set[1,2] assert_equal(x, y) end assert("Set#include?") do set = Set[1,2,3] assert_true set.include?(1) assert_true set.include?(2) assert_true set.include?(3) assert_false set.include?(0) assert_false set.include?(nil) set = Set["1",nil,"2",nil,"0","1",false] assert_true set.include?(nil) assert_true set.include?(false) assert_true set.include?("1") assert_false set.include?(0) assert_false set.include?(true) assert_false set.include?(2) end assert("Set#superset?") do set = Set[1,2,3] assert_raise(ArgumentError) { set.superset?(nil) } assert_raise(ArgumentError) { set.superset?(2) } assert_raise(ArgumentError) { set.superset?([2]) } assert_true set.superset?(Set[]) assert_true set.superset?(Set[1,2]) assert_true set.superset?(Set[1,2,3]) assert_false set.superset?(Set[1,2,3,4]) assert_false set.superset?(Set[1,4]) assert_true set >= Set[1,2] assert_true set >= Set[1,2,3] assert_true Set[].superset?(Set[]) end assert("Set#proper_superset?") do set = Set[1,2,3] assert_raise(ArgumentError) { set.proper_superset?(nil) } assert_raise(ArgumentError) { set.proper_superset?(2) } assert_raise(ArgumentError) { set.proper_superset?([2]) } assert_true set.proper_superset?(Set[]) assert_true set.proper_superset?(Set[1,2]) assert_false set.proper_superset?(Set[1,2,3]) assert_false set.proper_superset?(Set[1,2,3,4]) assert_false set.proper_superset?(Set[1,4]) assert_true set > Set[1,2] assert_false set > Set[1,2,3] assert_false Set[].proper_superset?(Set[]) end assert("Set#subset?") do set = Set[1,2,3] assert_raise(ArgumentError) { set.subset?(nil) } assert_raise(ArgumentError) { set.subset?(2) } assert_raise(ArgumentError) { set.subset?([2]) } assert_true set.subset?(Set[1,2,3,4]) assert_true set.subset?(Set[1,2,3]) assert_false set.subset?(Set[1,2]) assert_false set.subset?(Set[]) assert_true set <= Set[1,2,3] assert_false set <= Set[1,2] assert_true Set[].subset?(Set[1]) assert_true Set[].subset?(Set[]) end assert("Set#proper_subset?") do set = Set[1,2,3] assert_raise(ArgumentError) { set.proper_subset?(nil) } assert_raise(ArgumentError) { set.proper_subset?(2) } assert_raise(ArgumentError) { set.proper_subset?([2]) } assert_true set.proper_subset?(Set[1,2,3,4]) assert_false set.proper_subset?(Set[1,2,3]) assert_false set.proper_subset?(Set[1,2]) assert_false set.proper_subset?(Set[]) assert_true set < Set[1,2,3,4] assert_false set < Set[1,2,3] assert_true Set[].proper_subset?(Set[1]) assert_false Set[].proper_subset?(Set[]) end assert("Set#intersect?") do set = Set[3,4,5] assert_raise(ArgumentError) { set.intersect?(3) } assert_raise(ArgumentError) { set.intersect?([2,4,6]) } assert_true set.intersect?(set) assert_true set.intersect?(Set[2,4]) assert_true set.intersect?(Set[5,6,7]) assert_true set.intersect?(Set[1,2,6,8,4]) assert_false(set.intersect?(Set[])) assert_false(set.intersect?(Set[0,2])) assert_false(set.intersect?(Set[0,2,6])) assert_false(set.intersect?(Set[0,2,6,8,10])) # Make sure set hasn't changed assert_equal(Set[3,4,5], set) end assert("Set#disjoint?") do set = Set[3,4,5] assert_raise(ArgumentError) { set.disjoint?(3) } assert_raise(ArgumentError) { set.disjoint?([2,4,6]) } assert_true(set.disjoint?(Set[])) assert_true(set.disjoint?(Set[0,2])) assert_true(set.disjoint?(Set[0,2,6])) assert_true(set.disjoint?(Set[0,2,6,8,10])) assert_false set.disjoint?(set) assert_false set.disjoint?(Set[2,4]) assert_false set.disjoint?(Set[5,6,7]) assert_false set.disjoint?(Set[1,2,6,8,4]) # Make sure set hasn't changed assert_equal(Set[3,4,5], set) end assert("Set#each") do ary = [1,3,5,7,10,20] set = Set.new(ary) ret = set.each { |o| } assert_true set.equal?(ret) # assert_same e = set.each assert_true e.instance_of?(Enumerator) assert_nothing_raised { set.each { |o| ary.delete(o) or raise "unexpected element: #{o}" } ary.empty? or raise "forgotten elements: #{ary.join(', ')}" } end assert("Set#add") do set = Set[1,2,3] ret = set.add(2) assert_true set.equal?(ret) # assert_same assert_equal(Set[1,2,3], set) ret = set.add(4) assert_true set.equal?(ret) # assert_same assert_equal(Set[1,2,3,4], set) end assert("Set#add?") do set = Set[1,2,3] ret = set.add?(2) assert_nil ret assert_equal(Set[1,2,3], set) ret = set.add?(4) assert_true set.equal?(ret) # assert_same assert_equal(Set[1,2,3,4], set) end assert("Set#delete") do set = Set[1,2,3] ret = set.delete(4) assert_true set.equal?(ret) # assert_same assert_equal(Set[1,2,3], set) ret = set.delete(2) assert_true set.equal?(ret) # assert_same assert_equal(Set[1,3], set) end assert("Set#delete?") do set = Set[1,2,3] ret = set.delete?(4) assert_nil ret assert_equal(Set[1,2,3], set) ret = set.delete?(1) assert_true set.equal?(ret) # assert_same assert_equal(Set[2,3], set) end assert("Set#delete_if") do set = Set.new(1..10) ret = set.delete_if { |i| i > 10 } assert_true set.equal?(ret) # assert_same assert_equal(Set.new(1..10), set) set = Set.new(1..10) ret = set.delete_if { |i| i % 3 == 0 } assert_true set.equal?(ret) # assert_same assert_equal(Set[1,2,4,5,7,8,10], set) end assert("Set#keep_if") do set = Set.new(1..10) ret = set.keep_if { |i| i <= 10 } assert_true set.equal?(ret) # assert_same assert_equal(Set.new(1..10), set) set = Set.new(1..10) ret = set.keep_if { |i| i % 3 != 0 } assert_true set.equal?(ret) # assert_same assert_equal(Set[1,2,4,5,7,8,10], set) end assert("Set#collect!") do set = Set[1,2,3,'a','b','c',-1..1,2..4] ret = set.collect! { |i| case i when Numeric i * 2 when String i.upcase else nil end } assert_true set.equal?(ret) # assert_same assert_equal(Set[2,4,6,"A","B","C",nil], set) end assert("Set#reject!") do set = Set.new(1..10) ret = set.reject! { |i| i > 10 } assert_nil(ret) assert_equal(Set.new(1..10), set) ret = set.reject! { |i| i % 3 == 0 } assert_true set.equal?(ret) # assert_same assert_equal(Set[1,2,4,5,7,8,10], set) end # this test is not in CRuby assert("Set#select!") do set = Set.new(1..10) ret = set.select! { |i| i <= 10 } assert_nil(ret) assert_equal(Set.new(1..10), set) ret = set.select! { |i| i % 3 != 0 } assert_true set.equal?(ret) # assert_same assert_equal(Set[1,2,4,5,7,8,10], set) end assert("Set#merge") do set = Set[1,2,3] ret = set.merge([2,4,6]) assert_true set.equal?(ret) # assert_same assert_equal(Set[1,2,3,4,6], set) end assert("Set#subtract") do set = Set[1,2,3] ret = set.subtract([2,4,6]) assert_true set.equal?(ret) # assert_same assert_equal(Set[1,3], set) end assert("Set#+") do set = Set[1,2,3] ret = set + [2,4,6] assert_false set.equal?(ret) # assert_not_same assert_equal(Set[1,2,3,4,6], ret) end assert("Set#-") do set = Set[1,2,3] ret = set - [2,4,6] assert_false set.equal?(ret) # assert_not_same assert_equal(Set[1,3], ret) end assert("Set#&") do set = Set[1,2,3,4] ret = set & [2,4,6] assert_false set.equal?(ret) # assert_not_same assert_equal(Set[2,4], ret) end assert("Set#^") do set = Set[1,2,3,4] ret = set ^ [2,4,5,5] assert_false set.equal?(ret) # assert_not_same assert_equal(Set[1,3,5], ret) end assert("Set#==") do set1 = Set[2,3,1] set2 = Set[1,2,3] assert_equal(set1, set1) assert_equal(set1, set2) assert_not_equal(Set[1], [1]) set1 = Class.new(Set)["a", "b"] set2 = Set["a", "b", set1] set1 = set1.add(set1.clone) assert_equal(set2, set2.clone) assert_equal(set1.clone, set1) end assert("Set#classify") do set = Set.new(1..10) ret = set.classify { |i| i % 3 } assert_equal(3, ret.size) assert_equal(Hash, ret.class) ret.each_value { |v| assert_equal(Set, v.class) } assert_equal(Set[3,6,9], ret[0]) assert_equal(Set[1,4,7,10], ret[1]) assert_equal(Set[2,5,8], ret[2]) end assert("Set#divide") do # arity is 1 set = Set.new(1..10) ret = set.divide { |i| i % 3 } assert_equal(3, ret.size) n = 0 ret.each { |s| n += s.size } assert_equal(set.size, n) assert_equal(set, ret.flatten) assert_equal(Set, ret.class) assert_true(ret.include?(Set[3,6,9])) assert_true(ret.include?(Set[1,4,7,10])) assert_true(ret.include?(Set[2,5,8])) # arity is 2 set = Set[7,10,5,11,1,3,4,9,0] assert_raise(NotImplementedError) { ret = set.divide { |a, b| (a - b).abs == 1 } } # assert_equal(4, ret.size) # n = 0 # ret.each { |s| n += s.size } # assert_equal(set.size, n) # assert_equal(set, ret.flatten) # assert_equal(Set, ret.class) end # freeze is not implemented yet #assert("freeze") do # orig = set = Set[1,2,3] # assert_equal false, set.frozen? # set << 4 # assert_same orig, set.freeze # assert_equal true, set.frozen? # assert_raise(RuntimeError) { # set << 5 # } # assert_equal 4, set.size #end # assert("freeze_dup") do # set1 = Set[1,2,3] # set1.freeze # set2 = set1.dup # # assert_not_predicate set2, :frozen? # assert_nothing_raised { # set2.add 4 # } # end # assert("reeze_clone") do # set1 = Set[1,2,3] # set1.freeze # set2 = set1.clone # # assert_predicate set2, :frozen? # assert_raise(RuntimeError) { # set2.add 5 # } # end # assert("Set#inspect") do set = Set[1,2,3] assert_equal("#", set.inspect) end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-set/PaxHeaders/mruby-set.gem0000644000000000000000000000013215077107276023677 xustar0030 mtime=1761382078.126420538 30 atime=1761382080.145411302 30 ctime=1761382108.621301766 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-set/mruby-set.gem0000644000175100017510000000025015077107276024264 0ustar00runnerrunnername: mruby-set description: Set class author: yui-knk website: https://github.com/yui-knk/mruby-set protocol: git repository: https://github.com/yui-knk/mruby-set.git nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-catch0000644000000000000000000000013215077107334021463 xustar0030 mtime=1761382108.633301731 30 atime=1761382109.798298363 30 ctime=1761382108.633301731 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-catch/0000755000175100017510000000000015077107334022130 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-catch/PaxHeaders/mrbgem.rake0000644000000000000000000000013115077107276023661 xustar0029 mtime=1761382078.10842062 30 atime=1761382080.128411379 30 ctime=1761382108.633301731 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-catch/mrbgem.rake0000644000175100017510000000024615077107276024254 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-catch') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Catch / Throw non-local Jump' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-catch/PaxHeaders/mrblib0000644000000000000000000000013215077107334022732 xustar0030 mtime=1761382108.636301722 30 atime=1761382109.798298363 30 ctime=1761382108.636301722 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-catch/mrblib/0000755000175100017510000000000015077107334023377 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-catch/mrblib/PaxHeaders/catch.rb0000644000000000000000000000013115077107276024422 xustar0029 mtime=1761382078.10842062 30 atime=1761382080.128411379 30 ctime=1761382108.636301722 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-catch/mrblib/catch.rb0000644000175100017510000000027015077107276025012 0ustar00runnerrunnerclass UncaughtThrowError < ArgumentError attr_reader :tag, :value def initialize(tag, value) @tag = tag @value = value super("uncaught throw #{tag.inspect}") end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-catch/PaxHeaders/test0000644000000000000000000000013215077107334022442 xustar0030 mtime=1761382108.634301728 30 atime=1761382109.798298363 30 ctime=1761382108.634301728 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-catch/test/0000755000175100017510000000000015077107334023107 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-catch/test/PaxHeaders/catch.rb0000644000000000000000000000013115077107276024132 xustar0029 mtime=1761382078.10842062 30 atime=1761382080.128411379 30 ctime=1761382108.634301728 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-catch/test/catch.rb0000644000175100017510000000276615077107276024536 0ustar00runnerrunnerassert "return throw value" do val = ["val"] result = catch :foo do loop do loop do begin throw :foo, val rescue Exception flunk("should not reach here 1") end break end flunk("should not reach here 2") end false end assert_same(val, result) end assert "no throw" do assert_equal(:foo, catch(:bar){:foo}) end assert "no throw value" do result = catch :foo do throw :foo 1 end assert_equal(nil, result) end assert "pass the given tag to block" do tag = [:foo] catch(tag){|t| assert_same(tag, t)} end assert "tag identity, uncaught throw" do tag, val = [:tag], [:val] catch [:tag] do throw tag, val end flunk("should not reach here") rescue Exception => e assert_match("uncaught throw *", e.message) assert_same(tag, e.tag) assert_same(val, e.value) end assert "without catch arguments" do result = catch do |tag1| catch do |tag2| throw tag1, 1 flunk("should not reach here 1") end flunk("should not reach here 2") end assert_equal(1, result) end assert "catches across invocation boundaries" do v = [] catch :one do v << 1 catch :two do v << 2 throw :one v << 3 end v << 4 end assert_equal([1,2], v) end assert "catches in the nested invocation with the same key" do v = [] catch :tag do v << 1 catch :tag do v << 2 throw :tag v << 3 end v << 4 end assert_equal([1,2,4], v) end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-catch/PaxHeaders/src0000644000000000000000000000013215077107334022252 xustar0030 mtime=1761382108.637301719 30 atime=1761382109.798298363 30 ctime=1761382108.637301719 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-catch/src/0000755000175100017510000000000015077107334022717 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-catch/src/PaxHeaders/catch.c0000644000000000000000000000013115077107276023561 xustar0029 mtime=1761382078.10842062 30 atime=1761382080.128411379 30 ctime=1761382108.637301719 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-catch/src/catch.c0000644000175100017510000000553415077107276024161 0ustar00runnerrunner#include #include #include #include #include #include #include MRB_PRESYM_DEFINE_VAR_AND_INITER(catch_syms, 3, MRB_SYM(Object), MRB_SYM(new), MRB_SYM(call)) /* * def catch(r1 = Object.new, &r2) * r2.call(r1) * end */ static const mrb_code catch_iseq[] = { OP_ENTER, 0x00, 0x20, 0x01, // 000 ENTER 0:1:0:0:0:0:1 (0x2001) OP_JMP, 0x00, 0x06, // 004 JMP 013 // copy for block parameter "tag" when method argument are given OP_MOVE, 0x03, 0x01, // 007 MOVE R3 R1 OP_JMP, 0x00, 0x0a, // 010 JMP 023 // create a tag for default parameter OP_GETCONST, 0x03, 0x00, // 013 GETCONST R3 Object OP_SEND, 0x03, 0x01, 0x00, // 016 SEND R3 :new n=0 OP_MOVE, 0x01, 0x03, // 020 MOVE R1 R3 // to save on the stack, block variables are used as is OP_SEND, 0x02, 0x02, 0x01, // 023 SEND R2 :call n=1 OP_RETURN, 0x02, // 027 RETURN R2 }; static const mrb_irep catch_irep = { 3,5,0, MRB_IREP_STATIC,catch_iseq, NULL,catch_syms,NULL, NULL, NULL, sizeof(catch_iseq),0,3,0,0 }; mrb_alignas(8) static const struct RProc catch_proc = { NULL, NULL, MRB_TT_PROC, MRB_GC_RED, MRB_OBJ_IS_FROZEN, MRB_PROC_SCOPE | MRB_PROC_STRICT, { &catch_irep }, NULL, { NULL } }; static size_t find_catcher(mrb_state *mrb, mrb_value tag) { const mrb_callinfo *ci = mrb->c->ci - 1; // skip oneself throw ptrdiff_t n = ci - mrb->c->cibase; for (; n > 0; n--, ci--) { const mrb_value *arg1 = ci->stack + 1; if (ci->proc == &catch_proc && mrb_obj_eq(mrb, *arg1, tag)) { return (uintptr_t)n; } } return 0; } static mrb_value throw_m(mrb_state *mrb, mrb_value self) { mrb_value tag, obj; if (mrb_get_args(mrb, "o|o", &tag, &obj) == 1) { obj = mrb_nil_value(); } uintptr_t ci_index = find_catcher(mrb, tag); if (ci_index == 0) { mrb_value argv[2] = {tag, obj}; mrb_exc_raise(mrb, mrb_obj_new(mrb, mrb_exc_get_id(mrb, MRB_ERROR_SYM(UncaughtThrowError)), 2, argv)); } struct RBreak *b = MRB_OBJ_ALLOC(mrb, MRB_TT_BREAK, NULL); mrb_break_value_set(b, obj); b->ci_break_index = ci_index; /* Back to the caller directly */ mrb_exc_raise(mrb, mrb_obj_value(b)); /* not reached */ return mrb_nil_value(); } void mrb_mruby_catch_gem_init(mrb_state *mrb) { mrb_method_t m; MRB_PRESYM_INIT_SYMBOLS(mrb, catch_syms); MRB_METHOD_FROM_PROC(m, &catch_proc); m.flags |= MRB_METHOD_PRIVATE_FL; mrb_define_method_raw(mrb, mrb->kernel_module, MRB_SYM(catch), m); mrb_define_private_method_id(mrb, mrb->kernel_module, MRB_SYM(throw), throw_m, MRB_ARGS_ARG(1,1)); } void mrb_mruby_catch_gem_final(mrb_state *mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-struct0000644000000000000000000000013215077107334021725 xustar0030 mtime=1761382108.651301679 30 atime=1761382109.798298363 30 ctime=1761382108.651301679 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-struct/0000755000175100017510000000000015077107334022372 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-struct/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024124 xustar0030 mtime=1761382078.128420529 30 atime=1761382080.147411293 30 ctime=1761382108.651301679 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-struct/mrbgem.rake0000644000175100017510000000024015077107276024510 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-struct') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'standard Struct class' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-struct/PaxHeaders/mrblib0000644000000000000000000000013015077107334023172 xustar0029 mtime=1761382108.65430167 30 atime=1761382109.798298363 29 ctime=1761382108.65430167 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-struct/mrblib/0000755000175100017510000000000015077107334023641 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-struct/mrblib/PaxHeaders/struct.rb0000644000000000000000000000013115077107276025126 xustar0030 mtime=1761382078.128420529 30 atime=1761382080.147411293 29 ctime=1761382108.65430167 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-struct/mrblib/struct.rb0000644000175100017510000000240215077107276025515 0ustar00runnerrunner## # Struct # # ISO 15.2.18 class Struct include Enumerable ## # Calls the given block for each element of +self+ # and pass the respective element. # # ISO 15.2.18.4.4 def each(&block) self.class.members.each{|field| block.call(self[field]) } self end ## # Calls the given block for each element of +self+ # and pass the name and value of the respective # element. # # ISO 15.2.18.4.5 def each_pair(&block) self.class.members.each{|field| block.call(field.to_sym, self[field]) } self end ## # Calls the given block for each element of +self+ # and returns an array with all elements of which # block is not false. # # ISO 15.2.18.4.7 def select(&block) ary = [] self.class.members.each{|field| val = self[field] ary.push(val) if block.call(val) } ary end ## # 15.2.18.4.11(x) # alias to_s inspect ## # call-seq: # hsh.dig(key,...) -> object # # Extracts the nested value specified by the sequence of key # objects by calling +dig+ at each step, returning +nil+ if any # intermediate step is +nil+. # def dig(idx,*args) n = self[idx] if args.size > 0 n&.dig(*args) else n end end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-struct/PaxHeaders/test0000644000000000000000000000013215077107334022704 xustar0030 mtime=1761382108.653301673 30 atime=1761382109.798298363 30 ctime=1761382108.653301673 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-struct/test/0000755000175100017510000000000015077107334023351 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-struct/test/PaxHeaders/struct.rb0000644000000000000000000000013215077107276024637 xustar0030 mtime=1761382078.129420524 30 atime=1761382080.147411293 30 ctime=1761382108.653301673 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-struct/test/struct.rb0000644000175100017510000001214715077107276025234 0ustar00runnerrunner## # Struct ISO Test assert('Struct', '15.2.18') do assert_equal Class, Struct.class end assert('Struct.new', '15.2.18.3.1') do c = Struct.new(:m1, :m2) assert_equal Struct, c.superclass assert_equal [:m1, :m2], c.members end assert('Struct#==', '15.2.18.4.1') do c = Struct.new(:m1, :m2) cc1 = c.new(1,2) cc2 = c.new(1,2) assert_true cc1 == cc2 Struct.new(:m1, :m2) { def foo; end } assert_raise(NoMethodError) { Struct.new(:m1).new.foo } end assert('Struct#[]', '15.2.18.4.2') do c = Struct.new(:m1, :m2) cc = c.new(1,2) assert_equal 1, cc[:m1] assert_equal 2, cc["m2"] assert_equal 1, cc[0] assert_equal 2, cc[-1] assert_raise(TypeError) { cc[[]] } assert_raise(IndexError) { cc[2] } assert_raise(NameError) { cc['tama'] } end assert('Struct#[]=', '15.2.18.4.3') do c = Struct.new(:m1, :m2) cc = c.new(1,2) cc[:m1] = 3 assert_equal 3, cc[:m1] cc["m2"] = 3 assert_equal 3, cc["m2"] cc[0] = 4 assert_equal 4, cc[0] cc[-1] = 5 assert_equal 5, cc[-1] assert_raise(TypeError) { cc[[]] = 3 } assert_raise(IndexError) { cc[2] = 7 } assert_raise(NameError) { cc['pochi'] = 8 } end assert('Struct#each', '15.2.18.4.4') do c = Struct.new(:m1, :m2) cc = c.new(1,2) a = [] cc.each{|x| a << x } assert_equal [1, 2], a end assert('Struct#each_pair', '15.2.18.4.5') do c = Struct.new(:m1, :m2) cc = c.new(1,2) a = [] cc.each_pair{|k,v| a << [k,v] } assert_equal [[:m1, 1], [:m2, 2]], a end assert('Struct#members', '15.2.18.4.6') do c = Struct.new(:m1, :m2) assert_equal [:m1, :m2], c.new(1,2).members end assert('Struct#select', '15.2.18.4.7') do c = Struct.new(:m1, :m2) assert_equal([2]) { c.new(1,2).select{|v| v % 2 == 0} } end assert('large struct') do c = Struct.new(:m1, :m2, :m3, :m4, :m5, :m6, :m7, :m8, :m9, :m10, :m11, :m12, :m13) cc = c.new(1,2,3,4,5,6,7,8,9,10,11,12,13) assert_equal 1, cc.m1 assert_equal 2, cc.m2 assert_equal 3, cc.m3 assert_equal 4, cc.m4 assert_equal 5, cc.m5 assert_equal 6, cc.m6 assert_equal 7, cc.m7 assert_equal 8, cc.m8 assert_equal 9, cc.m9 assert_equal 10, cc.m10 assert_equal 13, cc.m13 cc.m13 = 'test' assert_equal 'test', cc.m13 assert_raise(NoMethodError) { cc.m14 } end assert('wrong struct arg count') do c = Struct.new(:m1) assert_raise ArgumentError do cc = c.new(1,2,3) end end assert('struct dup') do c = Struct.new(:m1, :m2, :m3, :m4, :m5) cc = c.new(1,2,3,4,5) assert_nothing_raised { assert_equal(cc, cc.dup) } end assert('struct inspect') do c = Struct.new(:m1, :m2, :m3, :m4, :m5, :recur) cc = c.new(1,2,3,4,5,nil) cc.recur = cc assert_equal "#>", cc.inspect end assert('Struct#length, Struct#size') do s = Struct.new(:f1, :f2).new(0, 1) assert_equal 2, s.size assert_equal 2, s.length end assert('Struct#to_a, Struct#values') do s = Struct.new(:mem1, :mem2).new('a', 'b') assert_equal ['a', 'b'], s.to_a assert_equal ['a', 'b'], s.values end assert('Struct#to_h') do s = Struct.new(:white, :red, :green).new('ruuko', 'yuzuki', 'hitoe') assert_equal({:white => 'ruuko', :red => 'yuzuki', :green => 'hitoe'}) { s.to_h } end assert('Struct#values_at') do a = Struct.new(:blue, :purple).new('aki', 'io') assert_equal ['aki'], a.values_at(0) assert_equal ['io', 'aki'], a.values_at(1, 0) assert_raise(IndexError) { a.values_at 2 } end assert("Struct#dig") do a = Struct.new(:blue, :purple).new('aki', Struct.new(:red).new(1)) assert_equal 'aki', a.dig(:blue) assert_equal 1, a.dig(:purple, :red) assert_equal 1, a.dig(1, 0) end # TODO: suppress redefining Struct warning during test # assert("Struct.new removes existing constant") do # begin # assert_not_equal Struct.new("Test", :a), Struct.new("Test", :a, :b) # ensure # Struct.remove_const :Test # end # end assert("Struct#initialize_copy requires struct to be the same type") do begin Struct.new("Test", :a) a = Struct::Test.new("a") Struct.__send__(:remove_const,:Test) Struct.new("Test", :a, :b) assert_raise(TypeError) do a.initialize_copy(Struct::Test.new("a", "b")) end ensure Struct.__send__(:remove_const,:Test) end end assert("Struct.new does not allow array") do assert_raise(TypeError) do Struct.new("Test", [:a]) end end assert("Struct.new does not allow invalid class name") do assert_raise(NameError) { Struct.new("Test-", :a) } end assert("Struct.new generates subclass of Struct") do begin original_struct = Struct Struct = String assert_equal original_struct, original_struct.new(:foo).superclass ensure Struct = original_struct end end assert 'Struct#freeze' do c = Struct.new :m o = c.new o.m = :test assert_equal :test, o.m o.freeze assert_raise(FrozenError) { o.m = :modify } assert_raise(FrozenError) { o[:m] = :modify } assert_equal :test, o.m end assert 'method visibility with Struct' do c = Struct.new :r, :g, :b do def good! "GOOD!" end private def bad! "BAD!" end end assert_equal "GOOD!" do c.new.good! end assert_raise NoMethodError do c.new.bad! end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-struct/PaxHeaders/src0000644000000000000000000000013215077107334022514 xustar0030 mtime=1761382108.655301667 30 atime=1761382109.798298363 30 ctime=1761382108.655301667 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-struct/src/0000755000175100017510000000000015077107334023161 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-struct/src/PaxHeaders/struct.c0000644000000000000000000000013215077107276024266 xustar0030 mtime=1761382078.129420524 30 atime=1761382080.147411293 30 ctime=1761382108.655301667 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-struct/src/struct.c0000644000175100017510000005046415077107276024667 0ustar00runnerrunner/* ** struct.c - Struct class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include #include #define RSTRUCT_LEN(st) RARRAY_LEN(st) #define RSTRUCT_PTR(st) RARRAY_PTR(st) #define mrb_struct_p(o) (mrb_type(o) == MRB_TT_STRUCT) static struct RClass * struct_class(mrb_state *mrb) { return mrb_class_get_id(mrb, MRB_SYM(Struct)); } static void struct_corrupted(mrb_state *mrb) { mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct"); } static mrb_value struct_s_members(mrb_state *mrb, struct RClass *c) { struct RClass* sclass = struct_class(mrb); for (;;) { mrb_value members = mrb_iv_get(mrb, mrb_obj_value(c), MRB_SYM(__members__)); if (!mrb_nil_p(members)) { if (!mrb_array_p(members)) { struct_corrupted(mrb); } return members; } c = c->super; if (c == sclass || c == 0) { mrb_raise(mrb, E_TYPE_ERROR, "uninitialized struct"); } } } static mrb_value struct_members(mrb_state *mrb, mrb_value s) { if (!mrb_struct_p(s)) { struct_corrupted(mrb); } mrb_value members = struct_s_members(mrb, mrb_obj_class(mrb, s)); mrb_int len = RSTRUCT_LEN(s); mrb_int mlen = RARRAY_LEN(members); if (len > 0 && len != mlen) { mrb_raisef(mrb, E_TYPE_ERROR, "struct size differs (%i required %i given)", mlen, len); } return members; } static mrb_value mrb_struct_s_members_m(mrb_state *mrb, mrb_value klass) { mrb_value members = struct_s_members(mrb, mrb_class_ptr(klass)); mrb_value ary = mrb_ary_new_capa(mrb, RARRAY_LEN(members)); mrb_ary_replace(mrb, ary, members); return ary; } #define mrb_struct_modify(mrb,s) mrb_check_frozen((mrb), mrb_basic_ptr(s)) /* 15.2.18.4.6 */ /* * call-seq: * struct.members -> array * * Returns an array of strings representing the names of the instance * variables. * * Customer = Struct.new(:name, :address, :zip) * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) * joe.members #=> [:name, :address, :zip] */ static mrb_value mrb_struct_members(mrb_state *mrb, mrb_value obj) { return mrb_struct_s_members_m(mrb, mrb_obj_value(mrb_obj_class(mrb, obj))); } static mrb_int num_members(mrb_state *mrb, mrb_value self) { mrb_value members = struct_members(mrb, self); return RARRAY_LEN(members); } static mrb_value mrb_struct_ref(mrb_state *mrb, mrb_value obj) { mrb_int argc = mrb_get_argc(mrb); if (argc != 0) { mrb_argnum_error(mrb, argc, 0, 0); } mrb_int i = mrb_integer(mrb_proc_cfunc_env_get(mrb, 0)); mrb_int len = num_members(mrb, obj); mrb_value *ptr = RSTRUCT_PTR(obj); if (!ptr || len <= i) return mrb_nil_value(); return ptr[i]; } static mrb_sym mrb_id_attrset(mrb_state *mrb, mrb_sym id) { #define ONSTACK_ALLOC_MAX 32 #define ONSTACK_STRLEN_MAX (ONSTACK_ALLOC_MAX - 1) /* '=' character */ char *buf; mrb_int len; char onstack[ONSTACK_ALLOC_MAX]; const char *name = mrb_sym_name_len(mrb, id, &len); if (len > ONSTACK_STRLEN_MAX) { buf = (char*)mrb_malloc(mrb, (size_t)len+1); } else { buf = onstack; } memcpy(buf, name, (size_t)len); buf[len] = '='; mrb_sym mid = mrb_intern(mrb, buf, len+1); if (buf != onstack) { mrb_free(mrb, buf); } return mid; } static mrb_value mrb_struct_set_m(mrb_state *mrb, mrb_value obj) { mrb_int i = mrb_integer(mrb_proc_cfunc_env_get(mrb, 0)); mrb_value val = mrb_get_arg1(mrb); mrb_ary_set(mrb, obj, i, val); return val; } static void make_struct_define_accessors(mrb_state *mrb, mrb_value members, struct RClass *c) { const mrb_value *ptr_members = RARRAY_PTR(members); mrb_int len = RARRAY_LEN(members); int ai = mrb_gc_arena_save(mrb); for (mrb_int i=0; ibasic.c->super = c->c; */ make_struct_define_accessors(mrb, members, c); return nstr; } /* 15.2.18.3.1 */ /* * call-seq: * Struct.new( [aString] [, aSym]+> ) -> StructClass * StructClass.new(arg, ...) -> obj * StructClass[arg, ...] -> obj * * Creates a new class, named by aString, containing accessor * methods for the given symbols. If the name aString is * omitted, an anonymous structure class will be created. Otherwise, * the name of this struct will appear as a constant in class * Struct, so it must be unique for all * Structs in the system and should start with a capital * letter. Assigning a structure class to a constant effectively gives * the class the name of the constant. * * Struct::new returns a new Class object, * which can then be used to create specific instances of the new * structure. The number of actual parameters must be * less than or equal to the number of attributes defined for this * class; unset parameters default to nil. Passing too many * parameters will raise an ArgumentError. * * The remaining methods listed in this section (class and instance) * are defined for this generated class. * * # Create a structure with a name in Struct * Struct.new("Customer", :name, :address) #=> Struct::Customer * Struct::Customer.new("Dave", "123 Main") #=> # * * # Create a structure named by its constant * Customer = Struct.new(:name, :address) #=> Customer * Customer.new("Dave", "123 Main") #=> # */ static mrb_value mrb_struct_s_def(mrb_state *mrb, mrb_value klass) { mrb_value name = mrb_nil_value(); mrb_value b; const mrb_value *argv; mrb_int argc; mrb_get_args(mrb, "*&", &argv, &argc, &b); if (argc == 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (given 0, expected 1+)"); } const mrb_value *pargv = argv; mrb_int argcnt = argc; if (argc > 0 && !mrb_symbol_p(argv[0])) { /* 1stArgument:!symbol -> name=argv[0] rest=argv[0..n] */ name = argv[0]; pargv++; argcnt--; } mrb_value members = mrb_ary_new_from_values(mrb, argcnt, pargv); for (mrb_int i=0; i= RSTRUCT_LEN(s)) return mrb_nil_value(); return RSTRUCT_PTR(s)[idx]; } /* 15.2.18.4.2 */ /* * call-seq: * struct[symbol] -> anObject * struct[fixnum] -> anObject * * Attribute Reference---Returns the value of the instance variable * named by symbol, or indexed (0..length-1) by * fixnum. Will raise NameError if the named * variable does not exist, or IndexError if the index is * out of range. * * Customer = Struct.new(:name, :address, :zip) * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) * * joe["name"] #=> "Joe Smith" * joe[:name] #=> "Joe Smith" * joe[0] #=> "Joe Smith" */ static mrb_value mrb_struct_aref(mrb_state *mrb, mrb_value s) { mrb_value idx = mrb_get_arg1(mrb); if (mrb_string_p(idx)) { mrb_sym sym = mrb_intern_str(mrb, idx); idx = mrb_symbol_value(sym); } if (mrb_symbol_p(idx)) { return struct_aref_sym(mrb, s, mrb_symbol(idx)); } return struct_aref_int(mrb, s, mrb_as_int(mrb, idx)); } static mrb_value mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val) { mrb_value members = struct_members(mrb, s); mrb_int len = RARRAY_LEN(members); const mrb_value *ptr_members = RARRAY_PTR(members); for (mrb_int i=0; i obj * struct[fixnum] = obj -> obj * * Attribute Assignment---Assigns to the instance variable named by * symbol or fixnum the value obj and * returns it. Will raise a NameError if the named * variable does not exist, or an IndexError if the index * is out of range. * * Customer = Struct.new(:name, :address, :zip) * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) * * joe["name"] = "Luke" * joe[:zip] = "90210" * * joe.name #=> "Luke" * joe.zip #=> "90210" */ static mrb_value mrb_struct_aset(mrb_state *mrb, mrb_value s) { mrb_value idx; mrb_value val; mrb_get_args(mrb, "oo", &idx, &val); if (mrb_string_p(idx)) { mrb_sym sym = mrb_intern_str(mrb, idx); idx = mrb_symbol_value(sym); } if (mrb_symbol_p(idx)) { return mrb_struct_aset_sym(mrb, s, mrb_symbol(idx), val); } mrb_int i = struct_index(mrb, mrb_as_int(mrb, idx), num_members(mrb, s)); mrb_ary_set(mrb, s, i, val); return val; } /* 15.2.18.4.1 */ /* * call-seq: * struct == other_struct -> true or false * * Equality---Returns true if other_struct is * equal to this one: they must be of the same class as generated by * Struct::new, and the values of all instance variables * must be equal (according to Object#==). * * Customer = Struct.new(:name, :address, :zip) * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) * joejr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) * jane = Customer.new("Jane Doe", "456 Elm, Anytown NC", 12345) * joe == joejr #=> true * joe == jane #=> false */ static mrb_value mrb_struct_equal(mrb_state *mrb, mrb_value s) { mrb_value s2 = mrb_get_arg1(mrb); if (mrb_obj_equal(mrb, s, s2)) { return mrb_true_value(); } if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) { return mrb_false_value(); } if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) { return mrb_false_value(); } mrb_value *ptr = RSTRUCT_PTR(s); mrb_value *ptr2 = RSTRUCT_PTR(s2); mrb_int len = RSTRUCT_LEN(s); int ai = mrb_gc_arena_save(mrb); for (mrb_int i=0; i true or false * * Two structures are equal if they are the same object, or if all their * fields are equal (using eql?). */ static mrb_value mrb_struct_eql(mrb_state *mrb, mrb_value s) { mrb_value s2 = mrb_get_arg1(mrb); mrb_value *ptr, *ptr2; mrb_int i, len; if (mrb_obj_equal(mrb, s, s2)) { return mrb_true_value(); } if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) { return mrb_false_value(); } if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) { return mrb_false_value(); } ptr = RSTRUCT_PTR(s); ptr2 = RSTRUCT_PTR(s2); len = RSTRUCT_LEN(s); for (i=0; i Integer * struct.size -> Integer * * Returns number of struct members. */ static mrb_value mrb_struct_len(mrb_state *mrb, mrb_value self) { return mrb_fixnum_value(RSTRUCT_LEN(self)); } /* * call-seq: * struct.to_a -> array * struct.values -> array * * Create an array from struct values. */ static mrb_value mrb_struct_to_a(mrb_state *mrb, mrb_value self) { return mrb_ary_new_from_values(mrb, RSTRUCT_LEN(self), RSTRUCT_PTR(self)); } /* * call-seq: * struct.to_h -> hash * * Create a hash from member names and struct values. */ static mrb_value mrb_struct_to_h(mrb_state *mrb, mrb_value self) { mrb_value members = struct_members(mrb, self); mrb_value ret = mrb_hash_new_capa(mrb, RARRAY_LEN(members)); for (mrb_int i = 0; i < RARRAY_LEN(members); i++) { mrb_hash_set(mrb, ret, RARRAY_PTR(members)[i], mrb_ary_ref(mrb, self, i)); } return ret; } static mrb_value mrb_struct_values_at(mrb_state *mrb, mrb_value self) { mrb_int argc; const mrb_value *argv; mrb_get_args(mrb, "*", &argv, &argc); return mrb_get_values_at(mrb, self, RSTRUCT_LEN(self), argc, argv, struct_aref_int); } /* * call-seq: * struct.to_s -> string * struct.inspect -> string * * Returns a string representation of Data */ static mrb_value mrb_struct_to_s(mrb_state *mrb, mrb_value self) { mrb->c->ci->mid = MRB_SYM(inspect); mrb_value ret = mrb_str_new_lit(mrb, "#"); return ret; } mrb_value members = struct_members(mrb, self); mrb_int mlen = RARRAY_LEN(members); mrb_value *mems = RARRAY_PTR(members); for (mrb_int i=0; i0) mrb_str_cat_lit(mrb, ret, ", "); mrb_str_cat(mrb, ret, name, len); mrb_str_cat_lit(mrb, ret, "="); mrb_str_cat_str(mrb, ret, mrb_inspect(mrb, mrb_ary_ref(mrb, self, i))); mrb_gc_arena_restore(mrb, ai); } mrb_str_cat_lit(mrb, ret, ">"); return ret; } /* * A Struct is a convenient way to bundle a number of * attributes together, using accessor methods, without having to write * an explicit class. * * The Struct class is a generator of specific classes, * each one of which is defined to hold a set of variables and their * accessors. In these examples, we'll call the generated class * "CustomerClass," and we'll show an example instance of that * class as "CustomerInst." * * In the descriptions that follow, the parameter symbol refers * to a symbol, which is either a quoted string or a * Symbol (such as :name). */ void mrb_mruby_struct_gem_init(mrb_state* mrb) { struct RClass *st = mrb_define_class_id(mrb, MRB_SYM(Struct), mrb->object_class); MRB_SET_INSTANCE_TT(st, MRB_TT_STRUCT); MRB_UNDEF_ALLOCATOR(st); mrb_define_class_method_id(mrb, st, MRB_SYM(new), mrb_struct_s_def, MRB_ARGS_ANY()); /* 15.2.18.3.1 */ mrb_define_method_id(mrb, st, MRB_OPSYM(eq), mrb_struct_equal, MRB_ARGS_REQ(1)); /* 15.2.18.4.1 */ mrb_define_method_id(mrb, st, MRB_OPSYM(aref), mrb_struct_aref, MRB_ARGS_REQ(1)); /* 15.2.18.4.2 */ mrb_define_method_id(mrb, st, MRB_OPSYM(aset), mrb_struct_aset, MRB_ARGS_REQ(2)); /* 15.2.18.4.3 */ mrb_define_method_id(mrb, st, MRB_SYM(members), mrb_struct_members, MRB_ARGS_NONE()); /* 15.2.18.4.6 */ mrb_define_method_id(mrb, st, MRB_SYM(initialize), mrb_struct_initialize, MRB_ARGS_ANY()); /* 15.2.18.4.8 */ mrb_define_method_id(mrb, st, MRB_SYM(initialize_copy), mrb_struct_init_copy, MRB_ARGS_REQ(1)); /* 15.2.18.4.9 */ mrb_define_method_id(mrb, st, MRB_SYM_Q(eql), mrb_struct_eql, MRB_ARGS_REQ(1)); /* 15.2.18.4.12(x) */ mrb_define_method_id(mrb, st, MRB_SYM(to_s), mrb_struct_to_s, MRB_ARGS_NONE()); /* 15.2.18.4.11(x) */ mrb_define_method_id(mrb, st, MRB_SYM(inspect), mrb_struct_to_s, MRB_ARGS_NONE()); /* 15.2.18.4.10(x) */ mrb_define_method_id(mrb, st, MRB_SYM(size), mrb_struct_len, MRB_ARGS_NONE()); mrb_define_method_id(mrb, st, MRB_SYM(length), mrb_struct_len, MRB_ARGS_NONE()); mrb_define_method_id(mrb, st, MRB_SYM(to_a), mrb_struct_to_a, MRB_ARGS_NONE()); mrb_define_method_id(mrb, st, MRB_SYM(values), mrb_struct_to_a, MRB_ARGS_NONE()); mrb_define_method_id(mrb, st, MRB_SYM(to_h), mrb_struct_to_h, MRB_ARGS_NONE()); mrb_define_method_id(mrb, st, MRB_SYM(values_at), mrb_struct_values_at, MRB_ARGS_ANY()); } void mrb_mruby_struct_gem_final(mrb_state* mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-compiler0000644000000000000000000000013215077107334022213 xustar0030 mtime=1761382108.697301546 30 atime=1761382109.798298363 30 ctime=1761382108.697301546 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compiler/0000755000175100017510000000000015077107334022660 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compiler/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024412 xustar0030 mtime=1761382078.114420593 30 atime=1761382080.136411343 30 ctime=1761382108.697301546 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compiler/mrbgem.rake0000644000175100017510000000220015077107276024774 0ustar00runnerrunnerMRuby::Gem::Specification.new 'mruby-compiler' do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'mruby compiler library' objs = %w[codegen y.tab].map do |name| src = "#{dir}/core/#{name}.c" if build.cxx_exception_enabled? build.compile_as_cxx(src) else objfile(src.pathmap("#{build_dir}/core/%n")) end end build.libmruby_core_objs << objs end dir = __dir__ lex_def = "#{dir}/core/lex.def" unless Rake::Task.task_defined?(lex_def) # Parser file "#{dir}/core/y.tab.c" => ["#{dir}/core/parse.y", lex_def] do |t| MRuby.targets["host"].yacc.run t.name, t.prerequisites.first replace_line_directive(t.name) end # Lexical analyzer file lex_def => "#{dir}/core/keywords" do |t| MRuby.targets["host"].gperf.run t.name, t.prerequisites.first replace_line_directive(t.name) end def replace_line_directive(path) content = File.read(path).gsub(%r{ ^\#line\s+\d+\s+"\K.*(?="$) | # #line directive ^/\*\s+Command-line:.*\s\K\S+(?=\s+\*/$) # header comment in lex.def }x, &:relative_path) File.write(path, content) end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compiler/PaxHeaders/core0000644000000000000000000000013215077107334023143 xustar0030 mtime=1761382108.696301549 30 atime=1761382109.798298363 30 ctime=1761382108.696301549 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compiler/core/0000755000175100017510000000000015077107334023610 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compiler/core/PaxHeaders/parse.y0000644000000000000000000000013115077107276024530 xustar0030 mtime=1761382078.111420607 29 atime=1761382080.13041137 30 ctime=1761382108.689301569 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compiler/core/parse.y0000644000175100017510000061555615077107276025143 0ustar00runnerrunner/* ** parse.y - mruby parser ** ** See Copyright Notice in mruby.h */ %{ #undef PARSER_DEBUG #ifdef PARSER_DEBUG # define YYDEBUG 1 #endif #define YYSTACK_USE_ALLOCA 1 #include #include #include #include #include #include #include #include #include #include #include #include #include "node.h" #define YYLEX_PARAM p #define mrbc_malloc(s) mrb_basic_alloc_func(NULL,(s)) #define mrbc_realloc(p,s) mrb_basic_alloc_func((p),(s)) #define mrbc_free(p) mrb_basic_alloc_func((p),0) typedef mrb_ast_node node; typedef struct mrb_parser_state parser_state; typedef struct mrb_parser_heredoc_info parser_heredoc_info; static int yyparse(parser_state *p); static int yylex(void *lval, void *lp, parser_state *p); static void yyerror(void *lp, parser_state *p, const char *s); static void yywarning(parser_state *p, const char *s); static void backref_error(parser_state *p, node *n); static void void_expr_error(parser_state *p, node *n); static void tokadd(parser_state *p, int32_t c); #define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c)) typedef unsigned int stack_type; #define BITSTACK_PUSH(stack, n) ((stack) = ((stack)<<1)|((n)&1)) #define BITSTACK_POP(stack) ((stack) = (stack) >> 1) #define BITSTACK_LEXPOP(stack) ((stack) = ((stack) >> 1) | ((stack) & 1)) #define BITSTACK_SET_P(stack) ((stack)&1) #define COND_PUSH(n) BITSTACK_PUSH(p->cond_stack, (n)) #define COND_POP() BITSTACK_POP(p->cond_stack) #define COND_LEXPOP() BITSTACK_LEXPOP(p->cond_stack) #define COND_P() BITSTACK_SET_P(p->cond_stack) #define CMDARG_PUSH(n) BITSTACK_PUSH(p->cmdarg_stack, (n)) #define CMDARG_POP() BITSTACK_POP(p->cmdarg_stack) #define CMDARG_LEXPOP() BITSTACK_LEXPOP(p->cmdarg_stack) #define CMDARG_P() BITSTACK_SET_P(p->cmdarg_stack) #define SET_LINENO(c,n) ((c)->lineno = (n)) #define NODE_LINENO(c,n) do {\ if (n) {\ (c)->filename_index = (n)->filename_index;\ (c)->lineno = (n)->lineno;\ }\ } while (0) #define sym(x) ((mrb_sym)(intptr_t)(x)) #define nsym(x) ((node*)(intptr_t)(x)) #define nint(x) ((node*)(intptr_t)(x)) #define intn(x) ((int)(intptr_t)(x)) #define typen(x) ((enum node_type)(intptr_t)(x)) #define NUM_SUFFIX_R (1<<0) #define NUM_SUFFIX_I (1<<1) static inline mrb_sym intern_cstr_gen(parser_state *p, const char *s) { return mrb_intern_cstr(p->mrb, s); } #define intern_cstr(s) intern_cstr_gen(p,(s)) static inline mrb_sym intern_gen(parser_state *p, const char *s, size_t len) { return mrb_intern(p->mrb, s, len); } #define intern(s,len) intern_gen(p,(s),(len)) #define intern_op(op) MRB_OPSYM_2(p->mrb, op) static mrb_sym intern_numparam_gen(parser_state *p, int num) { char buf[3]; buf[0] = '_'; buf[1] = '0'+num; buf[2] = '\0'; return intern(buf, 2); } #define intern_numparam(n) intern_numparam_gen(p,(n)) static void cons_free_gen(parser_state *p, node *cons) { cons->cdr = p->cells; p->cells = cons; } #define cons_free(c) cons_free_gen(p, (c)) static void* parser_palloc(parser_state *p, size_t size) { void *m = mempool_alloc(p->pool, size); if (!m) { MRB_THROW(p->mrb->jmp); } return m; } #define parser_pfree(ptr) do { if (sizeof(node) <= sizeof(*(ptr))) cons_free((node*)ptr);} while (0) static node* cons_gen(parser_state *p, node *car, node *cdr) { node *c; if (p->cells) { c = p->cells; p->cells = p->cells->cdr; } else { c = (node*)parser_palloc(p, sizeof(mrb_ast_node)); } c->car = car; c->cdr = cdr; c->lineno = p->lineno; c->filename_index = p->current_filename_index; /* beginning of next partial file; need to point the previous file */ if (p->lineno == 0 && p->current_filename_index > 0) { c->filename_index--; } return c; } #define cons(a,b) cons_gen(p,(a),(b)) static node* list1_gen(parser_state *p, node *a) { return cons(a, 0); } #define list1(a) list1_gen(p, (a)) static node* list2_gen(parser_state *p, node *a, node *b) { return cons(a, cons(b,0)); } #define list2(a,b) list2_gen(p, (a),(b)) static node* list3_gen(parser_state *p, node *a, node *b, node *c) { return cons(a, cons(b, cons(c,0))); } #define list3(a,b,c) list3_gen(p, (a),(b),(c)) static node* list4_gen(parser_state *p, node *a, node *b, node *c, node *d) { return cons(a, cons(b, cons(c, cons(d, 0)))); } #define list4(a,b,c,d) list4_gen(p, (a),(b),(c),(d)) static node* list5_gen(parser_state *p, node *a, node *b, node *c, node *d, node *e) { return cons(a, cons(b, cons(c, cons(d, cons(e, 0))))); } #define list5(a,b,c,d,e) list5_gen(p, (a),(b),(c),(d),(e)) static node* list6_gen(parser_state *p, node *a, node *b, node *c, node *d, node *e, node *f) { return cons(a, cons(b, cons(c, cons(d, cons(e, cons(f, 0)))))); } #define list6(a,b,c,d,e,f) list6_gen(p, (a),(b),(c),(d),(e),(f)) static node* append_gen(parser_state *p, node *a, node *b) { node *c = a; if (!a) return b; if (!b) return a; while (c->cdr) { c = c->cdr; } c->cdr = b; return a; } #define append(a,b) append_gen(p,(a),(b)) #define push(a,b) append_gen(p,(a),list1(b)) static char* parser_strndup(parser_state *p, const char *s, size_t len) { char *b = (char*)parser_palloc(p, len+1); memcpy(b, s, len); b[len] = '\0'; return b; } #undef strndup #define strndup(s,len) parser_strndup(p, s, len) static char* parser_strdup(parser_state *p, const char *s) { return parser_strndup(p, s, strlen(s)); } #undef strdup #define strdup(s) parser_strdup(p, s) static void dump_int(uint16_t i, char *s) { char *p = s; char *t = s; while (i > 0) { *p++ = (i % 10)+'0'; i /= 10; } if (p == s) *p++ = '0'; *p = 0; p--; /* point the last char */ while (t < p) { char c = *t; *t++ = *p; *p-- = c; } } /* xxx ----------------------------- */ static node* local_switch(parser_state *p) { node *prev = p->locals; p->locals = cons(0, 0); return prev; } static void local_resume(parser_state *p, node *prev) { p->locals = prev; } static void local_nest(parser_state *p) { p->locals = cons(0, p->locals); } static void local_unnest(parser_state *p) { if (p->locals) { p->locals = p->locals->cdr; } } static mrb_bool local_var_p(parser_state *p, mrb_sym sym) { const struct RProc *u; node *l = p->locals; while (l) { node *n = l->car; while (n) { if (sym(n->car) == sym) return TRUE; n = n->cdr; } l = l->cdr; } u = p->upper; while (u && !MRB_PROC_CFUNC_P(u)) { const struct mrb_irep *ir = u->body.irep; const mrb_sym *v = ir->lv; int i; if (v) { for (i=0; i+1 < ir->nlocals; i++) { if (v[i] == sym) return TRUE; } } if (MRB_PROC_SCOPE_P(u)) break; u = u->upper; } return FALSE; } static void local_add_f(parser_state *p, mrb_sym sym) { if (p->locals) { node *n = p->locals->car; while (n) { if (sym(n->car) == sym) { mrb_int len; const char* name = mrb_sym_name_len(p->mrb, sym, &len); if (len > 0 && name[0] != '_') { yyerror(NULL, p, "duplicated argument name"); return; } } n = n->cdr; } p->locals->car = push(p->locals->car, nsym(sym)); } } static void local_add(parser_state *p, mrb_sym sym) { if (!local_var_p(p, sym)) { local_add_f(p, sym); } } /* allocate register for block */ #define local_add_blk(p) local_add_f(p, 0) static void local_add_kw(parser_state *p, mrb_sym kwd) { /* allocate register for keywords hash */ local_add_f(p, kwd ? kwd : intern_op(pow)); } static node* locals_node(parser_state *p) { return p->locals ? p->locals->car : NULL; } static void nvars_nest(parser_state *p) { p->nvars = cons(nint(0), p->nvars); } static void nvars_block(parser_state *p) { p->nvars = cons(nint(-2), p->nvars); } static void nvars_unnest(parser_state *p) { p->nvars = p->nvars->cdr; } /* (:scope (vars..) (prog...)) */ static node* new_scope(parser_state *p, node *body) { return cons((node*)NODE_SCOPE, cons(locals_node(p), body)); } /* (:begin prog...) */ static node* new_begin(parser_state *p, node *body) { if (body) { return list2((node*)NODE_BEGIN, body); } return cons((node*)NODE_BEGIN, 0); } #define newline_node(n) (n) /* (:rescue body rescue else) */ static node* new_rescue(parser_state *p, node *body, node *resq, node *els) { return list4((node*)NODE_RESCUE, body, resq, els); } static node* new_mod_rescue(parser_state *p, node *body, node *resq) { return new_rescue(p, body, list1(list3(0, 0, resq)), 0); } /* (:ensure body ensure) */ static node* new_ensure(parser_state *p, node *a, node *b) { return cons((node*)NODE_ENSURE, cons(a, cons(0, b))); } /* (:nil) */ static node* new_nil(parser_state *p) { return list1((node*)NODE_NIL); } /* (:true) */ static node* new_true(parser_state *p) { return list1((node*)NODE_TRUE); } /* (:false) */ static node* new_false(parser_state *p) { return list1((node*)NODE_FALSE); } /* (:alias new old) */ static node* new_alias(parser_state *p, mrb_sym a, mrb_sym b) { return cons((node*)NODE_ALIAS, cons(nsym(a), nsym(b))); } /* (:if cond then else) */ static node* new_if(parser_state *p, node *a, node *b, node *c) { void_expr_error(p, a); return list4((node*)NODE_IF, a, b, c); } /* (:unless cond then else) */ static node* new_unless(parser_state *p, node *a, node *b, node *c) { void_expr_error(p, a); return list4((node*)NODE_IF, a, c, b); } /* (:while cond body) */ static node* new_while(parser_state *p, node *a, node *b) { void_expr_error(p, a); return cons((node*)NODE_WHILE, cons(a, b)); } /* (:until cond body) */ static node* new_until(parser_state *p, node *a, node *b) { void_expr_error(p, a); return cons((node*)NODE_UNTIL, cons(a, b)); } /* (:for var obj body) */ static node* new_for(parser_state *p, node *v, node *o, node *b) { void_expr_error(p, o); return list4((node*)NODE_FOR, v, o, b); } /* (:case a ((when ...) body) ((when...) body)) */ static node* new_case(parser_state *p, node *a, node *b) { node *n = list2((node*)NODE_CASE, a); node *n2 = n; void_expr_error(p, a); while (n2->cdr) { n2 = n2->cdr; } n2->cdr = b; return n; } /* (:postexe a) */ static node* new_postexe(parser_state *p, node *a) { return cons((node*)NODE_POSTEXE, a); } /* (:self) */ static node* new_self(parser_state *p) { return list1((node*)NODE_SELF); } /* (:call a b c) */ static node* new_call(parser_state *p, node *a, mrb_sym b, node *c, int pass) { node *n = list4(nint(pass?NODE_CALL:NODE_SCALL), a, nsym(b), c); void_expr_error(p, a); NODE_LINENO(n, a); return n; } /* (:fcall self mid args) */ static node* new_fcall(parser_state *p, mrb_sym b, node *c) { node *n = list4((node*)NODE_FCALL, 0, nsym(b), c); NODE_LINENO(n, c); return n; } /* (a b . c) */ static node* new_callargs(parser_state *p, node *a, node *b, node *c) { return cons(a, cons(b, c)); } /* (:super . c) */ static node* new_super(parser_state *p, node *c) { return cons((node*)NODE_SUPER, c); } /* (:zsuper) */ static node* new_zsuper(parser_state *p) { return cons((node*)NODE_ZSUPER, 0); } /* (:yield . c) */ static node* new_yield(parser_state *p, node *c) { if (c && c->cdr && c->cdr->cdr) { yyerror(NULL, p, "both block arg and actual block given"); } return cons((node*)NODE_YIELD, c); } /* (:return . c) */ static node* new_return(parser_state *p, node *c) { return cons((node*)NODE_RETURN, c); } /* (:break . c) */ static node* new_break(parser_state *p, node *c) { return cons((node*)NODE_BREAK, c); } /* (:next . c) */ static node* new_next(parser_state *p, node *c) { return cons((node*)NODE_NEXT, c); } /* (:redo) */ static node* new_redo(parser_state *p) { return list1((node*)NODE_REDO); } /* (:retry) */ static node* new_retry(parser_state *p) { return list1((node*)NODE_RETRY); } /* (:dot2 a b) */ static node* new_dot2(parser_state *p, node *a, node *b) { return cons((node*)NODE_DOT2, cons(a, b)); } /* (:dot3 a b) */ static node* new_dot3(parser_state *p, node *a, node *b) { return cons((node*)NODE_DOT3, cons(a, b)); } /* (:colon2 b c) */ static node* new_colon2(parser_state *p, node *b, mrb_sym c) { void_expr_error(p, b); return cons((node*)NODE_COLON2, cons(b, nsym(c))); } /* (:colon3 . c) */ static node* new_colon3(parser_state *p, mrb_sym c) { return cons((node*)NODE_COLON3, nsym(c)); } /* (:and a b) */ static node* new_and(parser_state *p, node *a, node *b) { void_expr_error(p, a); return cons((node*)NODE_AND, cons(a, b)); } /* (:or a b) */ static node* new_or(parser_state *p, node *a, node *b) { void_expr_error(p, a); return cons((node*)NODE_OR, cons(a, b)); } /* (:array a...) */ static node* new_array(parser_state *p, node *a) { return cons((node*)NODE_ARRAY, a); } /* (:splat . a) */ static node* new_splat(parser_state *p, node *a) { void_expr_error(p, a); return cons((node*)NODE_SPLAT, a); } /* (:hash (k . v) (k . v)...) */ static node* new_hash(parser_state *p, node *a) { return cons((node*)NODE_HASH, a); } /* (:kw_hash (k . v) (k . v)...) */ static node* new_kw_hash(parser_state *p, node *a) { return cons((node*)NODE_KW_HASH, a); } /* (:sym . a) */ static node* new_sym(parser_state *p, mrb_sym sym) { return cons((node*)NODE_SYM, nsym(sym)); } static mrb_sym new_strsym(parser_state *p, node* str) { const char *s = (const char*)str->cdr->car; size_t len = (size_t)str->cdr->cdr; return mrb_intern(p->mrb, s, len); } /* (:lvar . a) */ static node* new_lvar(parser_state *p, mrb_sym sym) { return cons((node*)NODE_LVAR, nsym(sym)); } /* (:gvar . a) */ static node* new_gvar(parser_state *p, mrb_sym sym) { return cons((node*)NODE_GVAR, nsym(sym)); } /* (:ivar . a) */ static node* new_ivar(parser_state *p, mrb_sym sym) { return cons((node*)NODE_IVAR, nsym(sym)); } /* (:cvar . a) */ static node* new_cvar(parser_state *p, mrb_sym sym) { return cons((node*)NODE_CVAR, nsym(sym)); } /* (:nvar . a) */ static node* new_nvar(parser_state *p, int num) { int nvar; node *nvars = p->nvars->cdr; while (nvars) { nvar = intn(nvars->car); if (nvar == -2) break; /* top of the scope */ if (nvar > 0) { yyerror(NULL, p, "numbered parameter used in outer block"); break; } nvars->car = nint(-1); nvars = nvars->cdr; } nvar = intn(p->nvars->car); if (nvar == -1) { yyerror(NULL, p, "numbered parameter used in inner block"); } else { p->nvars->car = nint(nvar > num ? nvar : num); } return cons((node*)NODE_NVAR, nint(num)); } /* (:const . a) */ static node* new_const(parser_state *p, mrb_sym sym) { return cons((node*)NODE_CONST, nsym(sym)); } /* (:undef a...) */ static node* new_undef(parser_state *p, mrb_sym sym) { return list2((node*)NODE_UNDEF, nsym(sym)); } /* (:class class super body) */ static node* new_class(parser_state *p, node *c, node *s, node *b) { void_expr_error(p, s); return list4((node*)NODE_CLASS, c, s, cons(locals_node(p), b)); } /* (:sclass obj body) */ static node* new_sclass(parser_state *p, node *o, node *b) { void_expr_error(p, o); return list3((node*)NODE_SCLASS, o, cons(locals_node(p), b)); } /* (:module module body) */ static node* new_module(parser_state *p, node *m, node *b) { return list3((node*)NODE_MODULE, m, cons(locals_node(p), b)); } /* (:def m lv (arg . body)) */ static node* new_def(parser_state *p, mrb_sym m, node *a, node *b) { return list5((node*)NODE_DEF, nsym(m), 0, a, b); } static void defn_setup(parser_state *p, node *d, node *a, node *b) { node *n = d->cdr->cdr; n->car = locals_node(p); p->cmdarg_stack = intn(n->cdr->car); n->cdr->car = a; local_resume(p, n->cdr->cdr->car); n->cdr->cdr->car = b; } /* (:sdef obj m lv (arg . body)) */ static node* new_sdef(parser_state *p, node *o, mrb_sym m, node *a, node *b) { void_expr_error(p, o); return list6((node*)NODE_SDEF, o, nsym(m), 0, a, b); } static void defs_setup(parser_state *p, node *d, node *a, node *b) { node *n = d->cdr->cdr->cdr; n->car = locals_node(p); p->cmdarg_stack = intn(n->cdr->car); n->cdr->car = a; local_resume(p, n->cdr->cdr->car); n->cdr->cdr->car = b; } /* (:arg . sym) */ static node* new_arg(parser_state *p, mrb_sym sym) { return cons((node*)NODE_ARG, nsym(sym)); } static void local_add_margs(parser_state *p, node *n) { while (n) { if (typen(n->car->car) == NODE_MASGN) { node *t = n->car->cdr->cdr; n->car->cdr->cdr = NULL; while (t) { local_add_f(p, sym(t->car)); t = t->cdr; } local_add_margs(p, n->car->cdr->car->car); local_add_margs(p, n->car->cdr->car->cdr->cdr->car); } n = n->cdr; } } static void local_add_lv(parser_state *p, node *lv) { while (lv) { local_add_f(p, sym(lv->car)); lv = lv->cdr; } } /* (m o r m2 tail) */ /* m: (a b c) */ /* o: ((a . e1) (b . e2)) */ /* r: a */ /* m2: (a b c) */ /* b: a */ static node* new_args(parser_state *p, node *m, node *opt, mrb_sym rest, node *m2, node *tail) { node *n; local_add_margs(p, m); local_add_margs(p, m2); n = cons(m2, tail); n = cons(nsym(rest), n); n = cons(opt, n); while (opt) { /* opt: (sym . (opt . lv)) -> (sym . opt) */ local_add_lv(p, opt->car->cdr->cdr); opt->car->cdr = opt->car->cdr->car; opt = opt->cdr; } return cons(m, n); } /* (:args_tail keywords rest_keywords_sym block_sym) */ static node* new_args_tail(parser_state *p, node *kws, node *kwrest, mrb_sym blk) { node *k; if (kws || kwrest) { local_add_kw(p, (kwrest && kwrest->cdr)? sym(kwrest->cdr) : 0); } local_add_blk(p); if (blk) local_add_f(p, blk); /* allocate register for keywords arguments */ /* order is for Proc#parameters */ for (k = kws; k; k = k->cdr) { if (!k->car->cdr->cdr->car) { /* allocate required keywords */ local_add_f(p, sym(k->car->cdr->car)); } } for (k = kws; k; k = k->cdr) { if (k->car->cdr->cdr->car) { /* allocate keywords with default */ local_add_lv(p, k->car->cdr->cdr->car->cdr); k->car->cdr->cdr->car = k->car->cdr->cdr->car->car; local_add_f(p, sym(k->car->cdr->car)); } } return list4((node*)NODE_ARGS_TAIL, kws, kwrest, nsym(blk)); } /* (:kw_arg kw_sym def_arg) */ static node* new_kw_arg(parser_state *p, mrb_sym kw, node *def_arg) { mrb_assert(kw); return list3((node*)NODE_KW_ARG, nsym(kw), def_arg); } /* (:kw_rest_args . a) */ static node* new_kw_rest_args(parser_state *p, mrb_sym sym) { return cons((node*)NODE_KW_REST_ARGS, nsym(sym)); } static node* new_args_dots(parser_state *p, node *m) { mrb_sym r = intern_op(mul); mrb_sym k = intern_op(pow); mrb_sym b = intern_op(and); local_add_f(p, r); return new_args(p, m, 0, r, 0, new_args_tail(p, 0, new_kw_rest_args(p, k), b)); } /* (:block_arg . a) */ static node* new_block_arg(parser_state *p, node *a) { return cons((node*)NODE_BLOCK_ARG, a); } static node* setup_numparams(parser_state *p, node *a) { int nvars = intn(p->nvars->car); if (nvars > 0) { int i; mrb_sym sym; // m || opt || rest || tail if (a && (a->car || (a->cdr && a->cdr->car) || (a->cdr->cdr && a->cdr->cdr->car) || (a->cdr->cdr->cdr->cdr && a->cdr->cdr->cdr->cdr->car))) { yyerror(NULL, p, "ordinary parameter is defined"); } else if (p->locals) { /* p->locals should not be NULL unless error happens before the point */ node* args = 0; for (i = nvars; i > 0; i--) { char buf[3]; buf[0] = '_'; buf[1] = i+'0'; buf[2] = '\0'; sym = intern_cstr(buf); args = cons(new_arg(p, sym), args); p->locals->car = cons(nsym(sym), p->locals->car); } a = new_args(p, args, 0, 0, 0, 0); } } return a; } /* (:block arg body) */ static node* new_block(parser_state *p, node *a, node *b) { a = setup_numparams(p, a); return list4((node*)NODE_BLOCK, locals_node(p), a, b); } /* (:lambda arg body) */ static node* new_lambda(parser_state *p, node *a, node *b) { a = setup_numparams(p, a); return list4((node*)NODE_LAMBDA, locals_node(p), a, b); } /* (:asgn lhs rhs) */ static node* new_asgn(parser_state *p, node *a, node *b) { void_expr_error(p, b); return cons((node*)NODE_ASGN, cons(a, b)); } /* (:masgn mlhs=(pre rest post) mrhs) */ static node* new_masgn(parser_state *p, node *a, node *b) { void_expr_error(p, b); return cons((node*)NODE_MASGN, cons(a, b)); } /* (:masgn mlhs mrhs) no check */ static node* new_masgn_param(parser_state *p, node *a, node *b) { return cons((node*)NODE_MASGN, cons(a, b)); } /* (:asgn lhs rhs) */ static node* new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b) { void_expr_error(p, b); return list4((node*)NODE_OP_ASGN, a, nsym(op), b); } static node* new_imaginary(parser_state *p, node *imaginary) { return new_fcall(p, MRB_SYM_2(p->mrb, Complex), new_callargs(p, list2(list3((node*)NODE_INT, (node*)strdup("0"), nint(10)), imaginary), 0, 0)); } static node* new_rational(parser_state *p, node *rational) { return new_fcall(p, MRB_SYM_2(p->mrb, Rational), new_callargs(p, list1(rational), 0, 0)); } /* (:int . i) */ static node* new_int(parser_state *p, const char *s, int base, int suffix) { node* result = list3((node*)NODE_INT, (node*)strdup(s), nint(base)); if (suffix & NUM_SUFFIX_R) { result = new_rational(p, result); } if (suffix & NUM_SUFFIX_I) { result = new_imaginary(p, result); } return result; } #ifndef MRB_NO_FLOAT /* (:float . i) */ static node* new_float(parser_state *p, const char *s, int suffix) { node* result = cons((node*)NODE_FLOAT, (node*)strdup(s)); if (suffix & NUM_SUFFIX_R) { result = new_rational(p, result); } if (suffix & NUM_SUFFIX_I) { result = new_imaginary(p, result); } return result; } #endif /* (:str . (s . len)) */ static node* new_str(parser_state *p, const char *s, size_t len) { return cons((node*)NODE_STR, cons((node*)strndup(s, len), nint(len))); } /* (:dstr . a) */ static node* new_dstr(parser_state *p, node *a) { return cons((node*)NODE_DSTR, a); } static int string_node_p(node *n) { return (int)(typen(n->car) == NODE_STR); } static node* composite_string_node(parser_state *p, node *a, node *b) { size_t newlen = (size_t)a->cdr + (size_t)b->cdr; char *str = (char*)mempool_realloc(p->pool, a->car, (size_t)a->cdr + 1, newlen + 1); memcpy(str + (size_t)a->cdr, b->car, (size_t)b->cdr); str[newlen] = '\0'; a->car = (node*)str; a->cdr = (node*)newlen; cons_free(b); return a; } static node* concat_string(parser_state *p, node *a, node *b) { if (string_node_p(a)) { if (string_node_p(b)) { /* a == NODE_STR && b == NODE_STR */ composite_string_node(p, a->cdr, b->cdr); cons_free(b); return a; } else { /* a == NODE_STR && b == NODE_DSTR */ if (string_node_p(b->cdr->car)) { /* a == NODE_STR && b->[NODE_STR, ...] */ composite_string_node(p, a->cdr, b->cdr->car->cdr); cons_free(b->cdr->car); b->cdr->car = a; return b; } } } else { node *c; /* last node of a */ for (c = a; c->cdr != NULL; c = c->cdr) ; if (string_node_p(b)) { /* a == NODE_DSTR && b == NODE_STR */ if (string_node_p(c->car)) { /* a->[..., NODE_STR] && b == NODE_STR */ composite_string_node(p, c->car->cdr, b->cdr); cons_free(b); return a; } push(a, b); return a; } else { /* a == NODE_DSTR && b == NODE_DSTR */ if (string_node_p(c->car) && string_node_p(b->cdr->car)) { /* a->[..., NODE_STR] && b->[NODE_STR, ...] */ node *d = b->cdr; cons_free(b); composite_string_node(p, c->car->cdr, d->car->cdr); cons_free(d->car); c->cdr = d->cdr; cons_free(d); return a; } else { c->cdr = b->cdr; cons_free(b); return a; } } } return new_dstr(p, list2(a, b)); } /* (:str . (s . len)) */ static node* new_xstr(parser_state *p, const char *s, int len) { return cons((node*)NODE_XSTR, cons((node*)strndup(s, len), nint(len))); } /* (:xstr . a) */ static node* new_dxstr(parser_state *p, node *a) { return cons((node*)NODE_DXSTR, a); } /* (:dsym . a) */ static node* new_dsym(parser_state *p, node *a) { return cons((node*)NODE_DSYM, a); } /* (:regx . (s . (opt . enc))) */ static node* new_regx(parser_state *p, const char *p1, const char* p2, const char* p3) { return cons((node*)NODE_REGX, cons((node*)p1, cons((node*)p2, (node*)p3))); } /* (:dregx . (a . b)) */ static node* new_dregx(parser_state *p, node *a, node *b) { return cons((node*)NODE_DREGX, cons(a, b)); } /* (:backref . n) */ static node* new_back_ref(parser_state *p, int n) { return cons((node*)NODE_BACK_REF, nint(n)); } /* (:nthref . n) */ static node* new_nth_ref(parser_state *p, int n) { return cons((node*)NODE_NTH_REF, nint(n)); } /* (:heredoc . a) */ static node* new_heredoc(parser_state *p) { parser_heredoc_info *inf = (parser_heredoc_info*)parser_palloc(p, sizeof(parser_heredoc_info)); return cons((node*)NODE_HEREDOC, (node*)inf); } static void new_bv(parser_state *p, mrb_sym id) { } static node* new_literal_delim(parser_state *p) { return cons((node*)NODE_LITERAL_DELIM, 0); } /* (:words . a) */ static node* new_words(parser_state *p, node *a) { return cons((node*)NODE_WORDS, a); } /* (:symbols . a) */ static node* new_symbols(parser_state *p, node *a) { return cons((node*)NODE_SYMBOLS, a); } /* xxx ----------------------------- */ /* (:call a op) */ static node* call_uni_op(parser_state *p, node *recv, const char *m) { void_expr_error(p, recv); return new_call(p, recv, intern_cstr(m), 0, '.'); } /* (:call a op b) */ static node* call_bin_op(parser_state *p, node *recv, const char *m, node *arg1) { return new_call(p, recv, intern_cstr(m), new_callargs(p, list1(arg1), 0, 0), '.'); } static void args_with_block(parser_state *p, node *a, node *b) { if (b) { if (a->cdr && a->cdr->cdr) { yyerror(NULL, p, "both block arg and actual block given"); } a->cdr->cdr = b; } } static void endless_method_name(parser_state *p, node *defn) { mrb_sym sym = sym(defn->cdr->car); mrb_int len; const char *name = mrb_sym_name_len(p->mrb, sym, &len); if (len > 1 && name[len-1] == '=') { for (int i=0; icar)) { case NODE_SUPER: case NODE_ZSUPER: if (!a->cdr) a->cdr = new_callargs(p, 0, 0, b); else args_with_block(p, a->cdr, b); break; case NODE_CALL: case NODE_FCALL: case NODE_SCALL: /* (NODE_CALL recv mid (args kw . blk)) */ n = a->cdr->cdr->cdr; /* (args kw . blk) */ if (!n->car) n->car = new_callargs(p, 0, 0, b); else args_with_block(p, n->car, b); break; case NODE_RETURN: case NODE_BREAK: case NODE_NEXT: if (a->cdr == NULL) return; call_with_block(p, a->cdr, b); break; default: break; } } static node* new_negate(parser_state *p, node *n) { return cons((node*)NODE_NEGATE, n); } static node* cond(node *n) { return n; } static node* ret_args(parser_state *p, node *n) { if (n->cdr->cdr) { yyerror(NULL, p, "block argument should not be given"); return NULL; } if (!n->car) return NULL; if (!n->car->cdr) return n->car->car; return new_array(p, n->car); } static void assignable(parser_state *p, node *lhs) { switch (intn(lhs->car)) { case NODE_LVAR: local_add(p, sym(lhs->cdr)); break; case NODE_CONST: if (p->in_def) yyerror(NULL, p, "dynamic constant assignment"); break; } } static node* var_reference(parser_state *p, node *lhs) { if (intn(lhs->car) == NODE_LVAR) { if (!local_var_p(p, sym(lhs->cdr))) { node *n = new_fcall(p, sym(lhs->cdr), 0); cons_free(lhs); return n; } } return lhs; } static node* label_reference(parser_state *p, mrb_sym sym) { const char *name = mrb_sym_name(p->mrb, sym); if (local_var_p(p, sym)) { return new_lvar(p, sym); } else if (ISUPPER(name[0])) { return new_const(p, sym); } else { return new_fcall(p, sym, 0); } } typedef enum mrb_string_type string_type; typedef struct parser_lex_strterm { int type; int level; int term; int paren; struct parser_lex_strterm *prev; } parser_lex_strterm; static parser_lex_strterm* new_strterm(parser_state *p, string_type type, int term, int paren) { parser_lex_strterm *lex = (parser_lex_strterm*)parser_palloc(p, sizeof(parser_lex_strterm)); lex->type = type; lex->level = 0; lex->term = term; lex->paren = paren; lex->prev = p->lex_strterm; return lex; } static void end_strterm(parser_state *p) { parser_lex_strterm *term = p->lex_strterm->prev; parser_pfree(p->lex_strterm); p->lex_strterm = term; } static node* push_strterm(parser_state *p) { node *n = cons((node*)p->lex_strterm, p->parsing_heredoc); p->lex_strterm = NULL; return n; } static void pop_strterm(parser_state *p, node *n) { p->lex_strterm = (parser_lex_strterm*)n->car; p->parsing_heredoc = n->cdr; cons_free(n); } static parser_heredoc_info * parsing_heredoc_info(parser_state *p) { node *nd = p->parsing_heredoc; if (nd == NULL) return NULL; /* mrb_assert(nd->car->car == NODE_HEREDOC); */ return (parser_heredoc_info*)nd->car->cdr; } static void heredoc_treat_nextline(parser_state *p) { if (p->heredocs_from_nextline == NULL) return; if (p->parsing_heredoc && p->lex_strterm) { append(p->heredocs_from_nextline, p->parsing_heredoc); } p->parsing_heredoc = p->heredocs_from_nextline; p->lex_strterm = new_strterm(p, parsing_heredoc_info(p)->type, 0, 0); p->heredocs_from_nextline = NULL; } static void heredoc_end(parser_state *p) { p->parsing_heredoc = p->parsing_heredoc->cdr; if (p->parsing_heredoc == NULL) { p->lstate = EXPR_BEG; end_strterm(p); } else { /* next heredoc */ p->lex_strterm->type = parsing_heredoc_info(p)->type; } } #define is_strterm_type(p,str_func) ((p)->lex_strterm->type & (str_func)) static void prohibit_literals(parser_state *p, node *n) { if (n == 0) { yyerror(NULL, p, "can't define singleton method for ()."); } else { switch (typen(n->car)) { case NODE_INT: case NODE_STR: case NODE_DSTR: case NODE_XSTR: case NODE_DXSTR: case NODE_DREGX: case NODE_MATCH: case NODE_FLOAT: case NODE_ARRAY: case NODE_HEREDOC: yyerror(NULL, p, "can't define singleton method for literals"); default: break; } } } /* xxx ----------------------------- */ %} %define parse.error verbose %define api.pure %parse-param {parser_state *p} %lex-param {parser_state *p} %union { node *nd; mrb_sym id; int num; stack_type stack; const struct vtable *vars; } %token keyword_class "'class'" keyword_module "'module'" keyword_def "'def'" keyword_begin "'begin'" keyword_if "'if'" keyword_unless "'unless'" keyword_while "'while'" keyword_until "'until'" keyword_for "'for'" %token keyword_undef "'undef'" keyword_rescue "'rescue'" keyword_ensure "'ensure'" keyword_end "'end'" keyword_then "'then'" keyword_elsif "'elsif'" keyword_else "'else'" keyword_case "'case'" keyword_when "'when'" keyword_break "'break'" keyword_next "'next'" keyword_redo "'redo'" keyword_retry "'retry'" keyword_in "'in'" keyword_do "'do'" keyword_do_cond "'do' for condition" keyword_do_block "'do' for block" keyword_do_LAMBDA "'do' for lambda" keyword_return "'return'" keyword_yield "'yield'" keyword_super "'super'" keyword_self "'self'" keyword_nil "'nil'" keyword_true "'true'" keyword_false "'false'" keyword_and "'and'" keyword_or "'or'" keyword_not "'not'" modifier_if "'if' modifier" modifier_unless "'unless' modifier" modifier_while "'while' modifier" modifier_until "'until' modifier" modifier_rescue "'rescue' modifier" keyword_alias "'alias'" keyword_BEGIN "'BEGIN'" keyword_END "'END'" keyword__LINE__ "'__LINE__'" keyword__FILE__ "'__FILE__'" keyword__ENCODING__ "'__ENCODING__'" %token tIDENTIFIER "local variable or method" %token tFID "method" %token tGVAR "global variable" %token tIVAR "instance variable" %token tCONSTANT "constant" %token tCVAR "class variable" %token tLABEL_TAG "label" %token tINTEGER "integer literal" %token tFLOAT "float literal" %token tCHAR "character literal" %token tXSTRING tREGEXP %token tSTRING tSTRING_PART tSTRING_MID %token tNTH_REF tBACK_REF %token tREGEXP_END %token tNUMPARAM "numbered parameter" %type singleton string string_fragment string_rep string_interp xstring regexp %type literal numeric cpath symbol defn_head defs_head %type top_compstmt top_stmts top_stmt %type bodystmt compstmt stmts stmt expr arg primary command command_call method_call %type expr_value arg_rhs primary_value %type if_tail opt_else case_body cases opt_rescue exc_list exc_var opt_ensure %type args call_args opt_call_args %type paren_args opt_paren_args variable %type command_args aref_args opt_block_arg block_arg var_ref var_lhs %type command_asgn command_rhs mrhs superclass block_call block_command %type f_block_optarg f_block_opt %type f_opt_arglist_paren f_arglist_paren f_arglist %type f_args f_arg f_arg_item f_optarg f_margs %type assoc_list assocs assoc undef_list backref for_var %type block_param opt_block_param block_param_def f_opt %type bv_decls opt_bv_decl bvar f_larglist lambda_body %type brace_block cmd_brace_block do_block lhs none f_bad_arg %type mlhs mlhs_list mlhs_post mlhs_basic mlhs_item mlhs_node mlhs_inner %type fsym sym basic_symbol operation operation2 operation3 %type cname fname op f_rest_arg f_block_arg opt_f_block_arg f_norm_arg f_opt_asgn %type heredoc words symbols %type call_op call_op2 /* 0:'&.', 1:'.', 2:'::' */ %type args_tail opt_args_tail f_kwarg f_kw f_kwrest %type f_block_kwarg f_block_kw block_args_tail opt_block_args_tail %type f_label %token tUPLUS "unary plus" %token tUMINUS "unary minus" %token tCMP "<=>" %token tEQ "==" %token tEQQ "===" %token tNEQ "!=" %token tGEQ ">=" %token tLEQ "<=" %token tANDOP "&&" %token tOROP "||" %token tMATCH "=~" %token tNMATCH "!~" %token tDOT2 ".." %token tDOT3 "..." %token tBDOT2 tBDOT3 /* (.. and (... */ %token tAREF tASET /* [] and []= */ %token tLSHFT "<<" %token tRSHFT ">>" %token tCOLON2 "::" %token tCOLON3 /* :: at EXPR_BEG */ %token tOP_ASGN /* +=, -= etc. */ %token tASSOC "=>" %token tLPAREN tLPAREN_ARG "(" %token tRPAREN ")" %token tLBRACK "[" %token tLBRACE tLBRACE_ARG "{" %token tSTAR "*" %token tPOW tDSTAR "**" %token tAMPER "&" %token tLAMBDA "->" %token tANDDOT "&." %token tSYMBEG "symbol" %token tSTRING_BEG "string literal" %token tXSTRING_BEG tSTRING_DVAR tREGEXP_BEG tWORDS_BEG tSYMBOLS_BEG tLAMBEG %token tHEREDOC_BEG "here document" %token tHEREDOC_END tLITERAL_DELIM tHD_LITERAL_DELIM %token tHD_STRING_PART tHD_STRING_MID /* * precedence table */ %nonassoc tLOWEST %nonassoc tLBRACE_ARG %nonassoc modifier_if modifier_unless modifier_while modifier_until %left keyword_or keyword_and %right keyword_not %right '=' tOP_ASGN %left modifier_rescue %right '?' ':' tLABEL_TAG %nonassoc tDOT2 tDOT3 tBDOT2 tBDOT3 %left tOROP %left tANDOP %nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH %left '>' tGEQ '<' tLEQ %left '|' '^' %left '&' %left tLSHFT tRSHFT %left '+' '-' %left '*' '/' '%' %right tUMINUS_NUM tUMINUS %right tPOW %right '!' '~' tUPLUS %token tLAST_TOKEN %% program : { p->lstate = EXPR_BEG; if (!p->locals) p->locals = cons(0,0); } top_compstmt { p->tree = new_scope(p, $2); NODE_LINENO(p->tree, $2); } ; top_compstmt : top_stmts opt_terms { $$ = $1; } ; top_stmts : none { $$ = new_begin(p, 0); } | top_stmt { $$ = new_begin(p, $1); NODE_LINENO($$, $1); } | top_stmts terms top_stmt { $$ = push($1, newline_node($3)); } | error top_stmt { $$ = new_begin(p, 0); } ; top_stmt : stmt | keyword_BEGIN { $$ = local_switch(p); nvars_block(p); } '{' top_compstmt '}' { yyerror(&@1, p, "BEGIN not supported"); local_resume(p, $2); nvars_unnest(p); $$ = 0; } ; bodystmt : compstmt opt_rescue opt_else opt_ensure { if ($2) { $$ = new_rescue(p, $1, $2, $3); NODE_LINENO($$, $1); } else if ($3) { yywarning(p, "else without rescue is useless"); $$ = push($1, $3); } else { $$ = $1; } if ($4) { if ($$) { $$ = new_ensure(p, $$, $4); } else { $$ = push($4, new_nil(p)); } } } ; compstmt : stmts opt_terms { $$ = $1; } ; stmts : none { $$ = new_begin(p, 0); } | stmt { $$ = new_begin(p, $1); NODE_LINENO($$, $1); } | stmts terms stmt { $$ = push($1, newline_node($3)); } | error stmt { $$ = new_begin(p, $2); } ; stmt : keyword_alias fsym {p->lstate = EXPR_FNAME;} fsym { $$ = new_alias(p, $2, $4); } | keyword_undef undef_list { $$ = $2; } | stmt modifier_if expr_value { $$ = new_if(p, cond($3), $1, 0); } | stmt modifier_unless expr_value { $$ = new_unless(p, cond($3), $1, 0); } | stmt modifier_while expr_value { $$ = new_while(p, cond($3), $1); } | stmt modifier_until expr_value { $$ = new_until(p, cond($3), $1); } | stmt modifier_rescue stmt { $$ = new_mod_rescue(p, $1, $3); } | keyword_END '{' compstmt '}' { yyerror(&@1, p, "END not supported"); $$ = new_postexe(p, $3); } | command_asgn | mlhs '=' command_call { $$ = new_masgn(p, $1, $3); } | lhs '=' mrhs { $$ = new_asgn(p, $1, new_array(p, $3)); } | mlhs '=' arg { $$ = new_masgn(p, $1, $3); } | mlhs '=' mrhs { $$ = new_masgn(p, $1, new_array(p, $3)); } | arg tASSOC tIDENTIFIER { node *lhs = new_lvar(p, $3); assignable(p, lhs); $$ = new_asgn(p, lhs, $1); } | expr ; command_asgn : lhs '=' command_rhs { $$ = new_asgn(p, $1, $3); } | var_lhs tOP_ASGN command_rhs { $$ = new_op_asgn(p, $1, $2, $3); } | primary_value '[' opt_call_args ']' tOP_ASGN command_rhs { $$ = new_op_asgn(p, new_call(p, $1, intern_op(aref), $3, '.'), $5, $6); } | primary_value call_op tIDENTIFIER tOP_ASGN command_rhs { $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5); } | primary_value call_op tCONSTANT tOP_ASGN command_rhs { $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5); } | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call { yyerror(&@1, p, "constant re-assignment"); $$ = 0; } | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_rhs { $$ = new_op_asgn(p, new_call(p, $1, $3, 0, tCOLON2), $4, $5); } | defn_head f_opt_arglist_paren '=' command { $$ = $1; endless_method_name(p, $1); void_expr_error(p, $4); defn_setup(p, $$, $2, $4); nvars_unnest(p); p->in_def--; } | defn_head f_opt_arglist_paren '=' command modifier_rescue arg { $$ = $1; endless_method_name(p, $1); void_expr_error(p, $4); defn_setup(p, $$, $2, new_mod_rescue(p, $4, $6)); nvars_unnest(p); p->in_def--; } | defs_head f_opt_arglist_paren '=' command { $$ = $1; void_expr_error(p, $4); defs_setup(p, $$, $2, $4); nvars_unnest(p); p->in_def--; p->in_single--; } | defs_head f_opt_arglist_paren '=' command modifier_rescue arg { $$ = $1; void_expr_error(p, $4); defs_setup(p, $$, $2, new_mod_rescue(p, $4, $6)); nvars_unnest(p); p->in_def--; p->in_single--; } | backref tOP_ASGN command_rhs { backref_error(p, $1); $$ = new_begin(p, 0); } ; command_rhs : command_call %prec tOP_ASGN | command_call modifier_rescue stmt { $$ = new_mod_rescue(p, $1, $3); } | command_asgn ; expr : command_call | expr keyword_and expr { $$ = new_and(p, $1, $3); } | expr keyword_or expr { $$ = new_or(p, $1, $3); } | keyword_not opt_nl expr { $$ = call_uni_op(p, cond($3), "!"); } | '!' command_call { $$ = call_uni_op(p, cond($2), "!"); } | arg ; defn_head : keyword_def fname { $$ = new_def(p, $2, nint(p->cmdarg_stack), local_switch(p)); p->cmdarg_stack = 0; p->in_def++; nvars_block(p); } ; defs_head : keyword_def singleton dot_or_colon { p->lstate = EXPR_FNAME; } fname { $$ = new_sdef(p, $2, $5, nint(p->cmdarg_stack), local_switch(p)); p->cmdarg_stack = 0; p->in_def++; p->in_single++; nvars_block(p); p->lstate = EXPR_ENDFN; /* force for args */ } ; expr_value : expr { if (!$1) $$ = new_nil(p); else { $$ = $1; } } ; command_call : command | block_command ; block_command : block_call | block_call call_op2 operation2 command_args { $$ = new_call(p, $1, $3, $4, $2); } ; cmd_brace_block : tLBRACE_ARG { local_nest(p); nvars_nest(p); } opt_block_param compstmt '}' { $$ = new_block(p, $3, $4); local_unnest(p); nvars_unnest(p); } ; command : operation command_args %prec tLOWEST { $$ = new_fcall(p, $1, $2); } | operation command_args cmd_brace_block { args_with_block(p, $2, $3); $$ = new_fcall(p, $1, $2); } | primary_value call_op operation2 command_args %prec tLOWEST { $$ = new_call(p, $1, $3, $4, $2); } | primary_value call_op operation2 command_args cmd_brace_block { args_with_block(p, $4, $5); $$ = new_call(p, $1, $3, $4, $2); } | primary_value tCOLON2 operation2 command_args %prec tLOWEST { $$ = new_call(p, $1, $3, $4, tCOLON2); } | primary_value tCOLON2 operation2 command_args cmd_brace_block { args_with_block(p, $4, $5); $$ = new_call(p, $1, $3, $4, tCOLON2); } | keyword_super command_args { $$ = new_super(p, $2); } | keyword_yield command_args { $$ = new_yield(p, $2); } | keyword_return call_args { $$ = new_return(p, ret_args(p, $2)); } | keyword_break call_args { $$ = new_break(p, ret_args(p, $2)); } | keyword_next call_args { $$ = new_next(p, ret_args(p, $2)); } ; mlhs : mlhs_basic { $$ = $1; } | tLPAREN mlhs_inner rparen { $$ = $2; } ; mlhs_inner : mlhs_basic | tLPAREN mlhs_inner rparen { $$ = $2; } ; mlhs_basic : mlhs_list { $$ = list1($1); } | mlhs_list mlhs_item { $$ = list1(push($1,$2)); } | mlhs_list tSTAR mlhs_node { $$ = list2($1, $3); } | mlhs_list tSTAR mlhs_node ',' mlhs_post { $$ = list3($1, $3, $5); } | mlhs_list tSTAR { $$ = list2($1, new_nil(p)); } | mlhs_list tSTAR ',' mlhs_post { $$ = list3($1, new_nil(p), $4); } | tSTAR mlhs_node { $$ = list2(0, $2); } | tSTAR mlhs_node ',' mlhs_post { $$ = list3(0, $2, $4); } | tSTAR { $$ = list2(0, new_nil(p)); } | tSTAR ',' mlhs_post { $$ = list3(0, new_nil(p), $3); } ; mlhs_item : mlhs_node | tLPAREN mlhs_inner rparen { $$ = new_masgn(p, $2, NULL); } ; mlhs_list : mlhs_item ',' { $$ = list1($1); } | mlhs_list mlhs_item ',' { $$ = push($1, $2); } ; mlhs_post : mlhs_item { $$ = list1($1); } | mlhs_list mlhs_item { $$ = push($1, $2); } ; mlhs_node : variable { assignable(p, $1); } | primary_value '[' opt_call_args ']' { $$ = new_call(p, $1, intern_op(aref), $3, '.'); } | primary_value call_op tIDENTIFIER { $$ = new_call(p, $1, $3, 0, $2); } | primary_value tCOLON2 tIDENTIFIER { $$ = new_call(p, $1, $3, 0, tCOLON2); } | primary_value call_op tCONSTANT { $$ = new_call(p, $1, $3, 0, $2); } | primary_value tCOLON2 tCONSTANT { if (p->in_def || p->in_single) yyerror(&@1, p, "dynamic constant assignment"); $$ = new_colon2(p, $1, $3); } | tCOLON3 tCONSTANT { if (p->in_def || p->in_single) yyerror(&@1, p, "dynamic constant assignment"); $$ = new_colon3(p, $2); } | backref { backref_error(p, $1); $$ = 0; } ; lhs : variable { assignable(p, $1); } | primary_value '[' opt_call_args ']' { $$ = new_call(p, $1, intern_op(aref), $3, '.'); } | primary_value call_op tIDENTIFIER { $$ = new_call(p, $1, $3, 0, $2); } | primary_value tCOLON2 tIDENTIFIER { $$ = new_call(p, $1, $3, 0, tCOLON2); } | primary_value call_op tCONSTANT { $$ = new_call(p, $1, $3, 0, $2); } | primary_value tCOLON2 tCONSTANT { if (p->in_def || p->in_single) yyerror(&@1, p, "dynamic constant assignment"); $$ = new_colon2(p, $1, $3); } | tCOLON3 tCONSTANT { if (p->in_def || p->in_single) yyerror(&@1, p, "dynamic constant assignment"); $$ = new_colon3(p, $2); } | backref { backref_error(p, $1); $$ = 0; } | tNUMPARAM { yyerror(&@1, p, "can't assign to numbered parameter"); } ; cname : tIDENTIFIER { yyerror(&@1, p, "class/module name must be CONSTANT"); } | tCONSTANT ; cpath : tCOLON3 cname { $$ = cons(nint(1), nsym($2)); } | cname { $$ = cons(nint(0), nsym($1)); } | primary_value tCOLON2 cname { void_expr_error(p, $1); $$ = cons($1, nsym($3)); } ; fname : tIDENTIFIER | tCONSTANT | tFID | op { p->lstate = EXPR_ENDFN; $$ = $1; } | reswords { p->lstate = EXPR_ENDFN; $$ = $1; } ; fsym : fname | basic_symbol ; undef_list : fsym { $$ = new_undef(p, $1); } | undef_list ',' {p->lstate = EXPR_FNAME;} fsym { $$ = push($1, nsym($4)); } ; op : '|' { $$ = intern_op(or); } | '^' { $$ = intern_op(xor); } | '&' { $$ = intern_op(and); } | tCMP { $$ = intern_op(cmp); } | tEQ { $$ = intern_op(eq); } | tEQQ { $$ = intern_op(eqq); } | tMATCH { $$ = intern_op(match); } | tNMATCH { $$ = intern_op(nmatch); } | '>' { $$ = intern_op(gt); } | tGEQ { $$ = intern_op(ge); } | '<' { $$ = intern_op(lt); } | tLEQ { $$ = intern_op(le); } | tNEQ { $$ = intern_op(neq); } | tLSHFT { $$ = intern_op(lshift); } | tRSHFT { $$ = intern_op(rshift); } | '+' { $$ = intern_op(add); } | '-' { $$ = intern_op(sub); } | '*' { $$ = intern_op(mul); } | tSTAR { $$ = intern_op(mul); } | '/' { $$ = intern_op(div); } | '%' { $$ = intern_op(mod); } | tPOW { $$ = intern_op(pow); } | tDSTAR { $$ = intern_op(pow); } | '!' { $$ = intern_op(not); } | '~' { $$ = intern_op(neg); } | tUPLUS { $$ = intern_op(plus); } | tUMINUS { $$ = intern_op(minus); } | tAREF { $$ = intern_op(aref); } | tASET { $$ = intern_op(aset); } | '`' { $$ = intern_op(tick); } ; reswords : keyword__LINE__ | keyword__FILE__ | keyword__ENCODING__ | keyword_BEGIN | keyword_END | keyword_alias | keyword_and | keyword_begin | keyword_break | keyword_case | keyword_class | keyword_def | keyword_do | keyword_else | keyword_elsif | keyword_end | keyword_ensure | keyword_false | keyword_for | keyword_in | keyword_module | keyword_next | keyword_nil | keyword_not | keyword_or | keyword_redo | keyword_rescue | keyword_retry | keyword_return | keyword_self | keyword_super | keyword_then | keyword_true | keyword_undef | keyword_when | keyword_yield | keyword_if | keyword_unless | keyword_while | keyword_until ; arg : lhs '=' arg_rhs { $$ = new_asgn(p, $1, $3); } | var_lhs tOP_ASGN arg_rhs { $$ = new_op_asgn(p, $1, $2, $3); } | primary_value '[' opt_call_args ']' tOP_ASGN arg_rhs { $$ = new_op_asgn(p, new_call(p, $1, intern_op(aref), $3, '.'), $5, $6); } | primary_value call_op tIDENTIFIER tOP_ASGN arg_rhs { $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5); } | primary_value call_op tCONSTANT tOP_ASGN arg_rhs { $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5); } | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg_rhs { $$ = new_op_asgn(p, new_call(p, $1, $3, 0, tCOLON2), $4, $5); } | primary_value tCOLON2 tCONSTANT tOP_ASGN arg_rhs { yyerror(&@1, p, "constant re-assignment"); $$ = new_begin(p, 0); } | tCOLON3 tCONSTANT tOP_ASGN arg_rhs { yyerror(&@1, p, "constant re-assignment"); $$ = new_begin(p, 0); } | backref tOP_ASGN arg_rhs { backref_error(p, $1); $$ = new_begin(p, 0); } | arg tDOT2 arg { $$ = new_dot2(p, $1, $3); } | arg tDOT2 { $$ = new_dot2(p, $1, new_nil(p)); } | tBDOT2 arg { $$ = new_dot2(p, new_nil(p), $2); } | arg tDOT3 arg { $$ = new_dot3(p, $1, $3); } | arg tDOT3 { $$ = new_dot3(p, $1, new_nil(p)); } | tBDOT3 arg { $$ = new_dot3(p, new_nil(p), $2); } | arg '+' arg { $$ = call_bin_op(p, $1, "+", $3); } | arg '-' arg { $$ = call_bin_op(p, $1, "-", $3); } | arg '*' arg { $$ = call_bin_op(p, $1, "*", $3); } | arg '/' arg { $$ = call_bin_op(p, $1, "/", $3); } | arg '%' arg { $$ = call_bin_op(p, $1, "%", $3); } | arg tPOW arg { $$ = call_bin_op(p, $1, "**", $3); } | tUMINUS_NUM tINTEGER tPOW arg { $$ = new_negate(p, call_bin_op(p, $2, "**", $4)); } | tUMINUS_NUM tFLOAT tPOW arg { $$ = new_negate(p, call_bin_op(p, $2, "**", $4)); } | tUPLUS arg { $$ = call_uni_op(p, $2, "+@"); } | tUMINUS arg { $$ = new_negate(p, $2); } | arg '|' arg { $$ = call_bin_op(p, $1, "|", $3); } | arg '^' arg { $$ = call_bin_op(p, $1, "^", $3); } | arg '&' arg { $$ = call_bin_op(p, $1, "&", $3); } | arg tCMP arg { $$ = call_bin_op(p, $1, "<=>", $3); } | arg '>' arg { $$ = call_bin_op(p, $1, ">", $3); } | arg tGEQ arg { $$ = call_bin_op(p, $1, ">=", $3); } | arg '<' arg { $$ = call_bin_op(p, $1, "<", $3); } | arg tLEQ arg { $$ = call_bin_op(p, $1, "<=", $3); } | arg tEQ arg { $$ = call_bin_op(p, $1, "==", $3); } | arg tEQQ arg { $$ = call_bin_op(p, $1, "===", $3); } | arg tNEQ arg { $$ = call_bin_op(p, $1, "!=", $3); } | arg tMATCH arg { $$ = call_bin_op(p, $1, "=~", $3); } | arg tNMATCH arg { $$ = call_bin_op(p, $1, "!~", $3); } | '!' arg { $$ = call_uni_op(p, cond($2), "!"); } | '~' arg { $$ = call_uni_op(p, cond($2), "~"); } | arg tLSHFT arg { $$ = call_bin_op(p, $1, "<<", $3); } | arg tRSHFT arg { $$ = call_bin_op(p, $1, ">>", $3); } | arg tANDOP arg { $$ = new_and(p, $1, $3); } | arg tOROP arg { $$ = new_or(p, $1, $3); } | arg '?' arg opt_nl ':' arg { $$ = new_if(p, cond($1), $3, $6); } | arg '?' arg opt_nl tLABEL_TAG arg { $$ = new_if(p, cond($1), $3, $6); } | defn_head f_opt_arglist_paren '=' arg { $$ = $1; endless_method_name(p, $1); void_expr_error(p, $4); defn_setup(p, $$, $2, $4); nvars_unnest(p); p->in_def--; } | defn_head f_opt_arglist_paren '=' arg modifier_rescue arg { $$ = $1; endless_method_name(p, $1); void_expr_error(p, $4); defn_setup(p, $$, $2, new_mod_rescue(p, $4, $6)); nvars_unnest(p); p->in_def--; } | defs_head f_opt_arglist_paren '=' arg { $$ = $1; void_expr_error(p, $4); defs_setup(p, $$, $2, $4); nvars_unnest(p); p->in_def--; p->in_single--; } | defs_head f_opt_arglist_paren '=' arg modifier_rescue arg { $$ = $1; void_expr_error(p, $4); defs_setup(p, $$, $2, new_mod_rescue(p, $4, $6)); nvars_unnest(p); p->in_def--; p->in_single--; } | primary { $$ = $1; } ; aref_args : none | args trailer { $$ = $1; NODE_LINENO($$, $1); } | args comma assocs trailer { $$ = push($1, new_hash(p, $3)); } | assocs trailer { $$ = cons(new_kw_hash(p, $1), 0); NODE_LINENO($$, $1); } ; arg_rhs : arg %prec tOP_ASGN { $$ = $1; } | arg modifier_rescue arg { void_expr_error(p, $1); $$ = new_mod_rescue(p, $1, $3); } ; paren_args : '(' opt_call_args ')' { $$ = $2; } | '(' args comma tBDOT3 rparen { mrb_sym r = intern_op(mul); mrb_sym k = intern_op(pow); mrb_sym b = intern_op(and); $$ = new_callargs(p, push($2, new_splat(p, new_lvar(p, r))), new_kw_hash(p, list1(cons(new_kw_rest_args(p, 0), new_lvar(p, k)))), new_block_arg(p, new_lvar(p, b))); } | '(' tBDOT3 rparen { mrb_sym r = intern_op(mul); mrb_sym k = intern_op(pow); mrb_sym b = intern_op(and); if (local_var_p(p, r) && local_var_p(p, k) && local_var_p(p, b)) { $$ = new_callargs(p, list1(new_splat(p, new_lvar(p, r))), new_kw_hash(p, list1(cons(new_kw_rest_args(p, 0), new_lvar(p, k)))), new_block_arg(p, new_lvar(p, b))); } else { yyerror(&@1, p, "unexpected argument forwarding ..."); $$ = 0; } } ; opt_paren_args : none | paren_args ; opt_call_args : none | call_args opt_terms | args comma { $$ = new_callargs(p,$1,0,0); NODE_LINENO($$, $1); } | args comma assocs comma { $$ = new_callargs(p,$1,new_kw_hash(p,$3),0); NODE_LINENO($$, $1); } | assocs comma { $$ = new_callargs(p,0,new_kw_hash(p,$1),0); NODE_LINENO($$, $1); } ; call_args : command { void_expr_error(p, $1); $$ = new_callargs(p, list1($1), 0, 0); NODE_LINENO($$, $1); } | args opt_block_arg { $$ = new_callargs(p, $1, 0, $2); NODE_LINENO($$, $1); } | assocs opt_block_arg { $$ = new_callargs(p, 0, new_kw_hash(p, $1), $2); NODE_LINENO($$, $1); } | args comma assocs opt_block_arg { $$ = new_callargs(p, $1, new_kw_hash(p, $3), $4); NODE_LINENO($$, $1); } | block_arg { $$ = new_callargs(p, 0, 0, $1); NODE_LINENO($$, $1); } ; command_args : { $$ = p->cmdarg_stack; CMDARG_PUSH(1); } call_args { p->cmdarg_stack = $1; $$ = $2; } ; block_arg : tAMPER arg { $$ = new_block_arg(p, $2); } | tAMPER { $$ = new_block_arg(p, 0); } ; opt_block_arg : comma block_arg { $$ = $2; } | none { $$ = 0; } ; comma : ',' opt_nl ; args : arg { void_expr_error(p, $1); $$ = list1($1); NODE_LINENO($$, $1); } | tSTAR { $$ = list1(new_splat(p, new_lvar(p, intern_op(mul)))); } | tSTAR arg { $$ = list1(new_splat(p, $2)); NODE_LINENO($$, $2); } | args comma arg { void_expr_error(p, $3); $$ = push($1, $3); } | args comma tSTAR { $$ = push($1, new_splat(p, new_lvar(p, intern_op(mul)))); } | args comma tSTAR arg { $$ = push($1, new_splat(p, $4)); } ; mrhs : args comma arg { void_expr_error(p, $3); $$ = push($1, $3); } | args comma tSTAR arg { $$ = push($1, new_splat(p, $4)); } | tSTAR arg { $$ = list1(new_splat(p, $2)); } ; primary : literal | string | xstring | regexp | heredoc | var_ref | backref | tFID { $$ = new_fcall(p, $1, 0); } | keyword_begin { $$ = p->cmdarg_stack; p->cmdarg_stack = 0; } bodystmt keyword_end { p->cmdarg_stack = $2; $$ = $3; } | tLPAREN_ARG { $$ = p->cmdarg_stack; p->cmdarg_stack = 0; } stmt {p->lstate = EXPR_ENDARG;} rparen { p->cmdarg_stack = $2; $$ = $3; } | tLPAREN_ARG {p->lstate = EXPR_ENDARG;} rparen { $$ = new_nil(p); } | tLPAREN compstmt ')' { $$ = $2; } | primary_value tCOLON2 tCONSTANT { $$ = new_colon2(p, $1, $3); } | tCOLON3 tCONSTANT { $$ = new_colon3(p, $2); } | tLBRACK aref_args ']' { $$ = new_array(p, $2); NODE_LINENO($$, $2); } | tLBRACE assoc_list '}' { $$ = new_hash(p, $2); NODE_LINENO($$, $2); } | keyword_return { $$ = new_return(p, 0); } | keyword_yield opt_paren_args { $$ = new_yield(p, $2); } | keyword_not '(' expr rparen { $$ = call_uni_op(p, cond($3), "!"); } | keyword_not '(' rparen { $$ = call_uni_op(p, new_nil(p), "!"); } | operation brace_block { $$ = new_fcall(p, $1, new_callargs(p, 0, 0, $2)); } | method_call | method_call brace_block { call_with_block(p, $1, $2); $$ = $1; } | tLAMBDA { local_nest(p); nvars_nest(p); $$ = p->lpar_beg; p->lpar_beg = ++p->paren_nest; } f_larglist { $$ = p->cmdarg_stack; p->cmdarg_stack = 0; } lambda_body { p->lpar_beg = $2; $$ = new_lambda(p, $3, $5); local_unnest(p); nvars_unnest(p); p->cmdarg_stack = $4; CMDARG_LEXPOP(); } | keyword_if expr_value then compstmt if_tail keyword_end { $$ = new_if(p, cond($2), $4, $5); SET_LINENO($$, $1); } | keyword_unless expr_value then compstmt opt_else keyword_end { $$ = new_unless(p, cond($2), $4, $5); SET_LINENO($$, $1); } | keyword_while {COND_PUSH(1);} expr_value do {COND_POP();} compstmt keyword_end { $$ = new_while(p, cond($3), $6); SET_LINENO($$, $1); } | keyword_until {COND_PUSH(1);} expr_value do {COND_POP();} compstmt keyword_end { $$ = new_until(p, cond($3), $6); SET_LINENO($$, $1); } | keyword_case expr_value opt_terms case_body keyword_end { $$ = new_case(p, $2, $4); } | keyword_case opt_terms case_body keyword_end { $$ = new_case(p, 0, $3); } | keyword_for for_var keyword_in {COND_PUSH(1);} expr_value do {COND_POP();} compstmt keyword_end { $$ = new_for(p, $2, $5, $8); SET_LINENO($$, $1); } | keyword_class cpath superclass { if (p->in_def || p->in_single) yyerror(&@1, p, "class definition in method body"); $$ = local_switch(p); nvars_block(p); } bodystmt keyword_end { $$ = new_class(p, $2, $3, $5); SET_LINENO($$, $1); local_resume(p, $4); nvars_unnest(p); } | keyword_class tLSHFT expr { $$ = p->in_def; p->in_def = 0; } term { $$ = cons(local_switch(p), nint(p->in_single)); nvars_block(p); p->in_single = 0; } bodystmt keyword_end { $$ = new_sclass(p, $3, $7); SET_LINENO($$, $1); local_resume(p, $6->car); nvars_unnest(p); p->in_def = $4; p->in_single = intn($6->cdr); } | keyword_module cpath { if (p->in_def || p->in_single) yyerror(&@1, p, "module definition in method body"); $$ = local_switch(p); nvars_block(p); } bodystmt keyword_end { $$ = new_module(p, $2, $4); SET_LINENO($$, $1); local_resume(p, $3); nvars_unnest(p); } | defn_head f_arglist bodystmt keyword_end { $$ = $1; defn_setup(p, $$, $2, $3); nvars_unnest(p); p->in_def--; } | defs_head f_arglist bodystmt keyword_end { $$ = $1; defs_setup(p, $$, $2, $3); nvars_unnest(p); p->in_def--; p->in_single--; } | keyword_break { $$ = new_break(p, 0); } | keyword_next { $$ = new_next(p, 0); } | keyword_redo { $$ = new_redo(p); } | keyword_retry { $$ = new_retry(p); } ; primary_value : primary { $$ = $1; if (!$$) $$ = new_nil(p); } ; then : term | keyword_then | term keyword_then ; do : term | keyword_do_cond ; if_tail : opt_else | keyword_elsif expr_value then compstmt if_tail { $$ = new_if(p, cond($2), $4, $5); } ; opt_else : none | keyword_else compstmt { $$ = $2; } ; for_var : lhs { $$ = list1(list1($1)); } | mlhs ; f_margs : f_arg { $$ = list3($1,0,0); } | f_arg ',' tSTAR f_norm_arg { $$ = list3($1, new_arg(p, $4), 0); } | f_arg ',' tSTAR f_norm_arg ',' f_arg { $$ = list3($1, new_arg(p, $4), $6); } | f_arg ',' tSTAR { local_add_f(p, intern_op(mul)); $$ = list3($1, nint(-1), 0); } | f_arg ',' tSTAR ',' f_arg { $$ = list3($1, nint(-1), $5); } | tSTAR f_norm_arg { $$ = list3(0, new_arg(p, $2), 0); } | tSTAR f_norm_arg ',' f_arg { $$ = list3(0, new_arg(p, $2), $4); } | tSTAR { local_add_f(p, intern_op(mul)); $$ = list3(0, nint(-1), 0); } | tSTAR ',' { local_add_f(p, intern_op(mul)); } f_arg { $$ = list3(0, nint(-1), $4); } ; block_args_tail : f_block_kwarg ',' f_kwrest opt_f_block_arg { $$ = new_args_tail(p, $1, $3, $4); } | f_block_kwarg opt_f_block_arg { $$ = new_args_tail(p, $1, 0, $2); } | f_kwrest opt_f_block_arg { $$ = new_args_tail(p, 0, $1, $2); } | f_block_arg { $$ = new_args_tail(p, 0, 0, $1); } ; opt_block_args_tail : ',' block_args_tail { $$ = $2; } | /* none */ { $$ = new_args_tail(p, 0, 0, 0); } ; block_param : f_arg ',' f_block_optarg ',' f_rest_arg opt_block_args_tail { $$ = new_args(p, $1, $3, $5, 0, $6); } | f_arg ',' f_block_optarg ',' f_rest_arg ',' f_arg opt_block_args_tail { $$ = new_args(p, $1, $3, $5, $7, $8); } | f_arg ',' f_block_optarg opt_block_args_tail { $$ = new_args(p, $1, $3, 0, 0, $4); } | f_arg ',' f_block_optarg ',' f_arg opt_block_args_tail { $$ = new_args(p, $1, $3, 0, $5, $6); } | f_arg ',' f_rest_arg opt_block_args_tail { $$ = new_args(p, $1, 0, $3, 0, $4); } | f_arg ',' opt_block_args_tail { $$ = new_args(p, $1, 0, 0, 0, $3); } | f_arg ',' f_rest_arg ',' f_arg opt_block_args_tail { $$ = new_args(p, $1, 0, $3, $5, $6); } | f_arg opt_block_args_tail { $$ = new_args(p, $1, 0, 0, 0, $2); } | f_block_optarg ',' f_rest_arg opt_block_args_tail { $$ = new_args(p, 0, $1, $3, 0, $4); } | f_block_optarg ',' f_rest_arg ',' f_arg opt_block_args_tail { $$ = new_args(p, 0, $1, $3, $5, $6); } | f_block_optarg opt_block_args_tail { $$ = new_args(p, 0, $1, 0, 0, $2); } | f_block_optarg ',' f_arg opt_block_args_tail { $$ = new_args(p, 0, $1, 0, $3, $4); } | f_rest_arg opt_block_args_tail { $$ = new_args(p, 0, 0, $1, 0, $2); } | f_rest_arg ',' f_arg opt_block_args_tail { $$ = new_args(p, 0, 0, $1, $3, $4); } | block_args_tail { $$ = new_args(p, 0, 0, 0, 0, $1); } ; opt_block_param : none { local_add_blk(p); $$ = 0; } | block_param_def { p->cmd_start = TRUE; $$ = $1; } ; block_param_def : '|' {local_add_blk(p);} opt_bv_decl '|' { $$ = 0; } | tOROP { local_add_blk(p); $$ = 0; } | '|' block_param opt_bv_decl '|' { $$ = $2; } ; opt_bv_decl : opt_nl { $$ = 0; } | opt_nl ';' bv_decls opt_nl { $$ = 0; } ; bv_decls : bvar | bv_decls ',' bvar ; bvar : tIDENTIFIER { local_add_f(p, $1); new_bv(p, $1); } | f_bad_arg ; f_larglist : '(' f_args opt_bv_decl ')' { $$ = $2; } | f_args { $$ = $1; } ; lambda_body : tLAMBEG compstmt '}' { $$ = $2; } | keyword_do_LAMBDA bodystmt keyword_end { $$ = $2; } ; do_block : keyword_do_block { local_nest(p); nvars_nest(p); $$ = p->lineno; } opt_block_param bodystmt keyword_end { $$ = new_block(p,$3,$4); SET_LINENO($$, $2); local_unnest(p); nvars_unnest(p); } ; block_call : command do_block { if (typen($1->car) == NODE_YIELD) { yyerror(&@1, p, "block given to yield"); } else { call_with_block(p, $1, $2); } $$ = $1; } | block_call call_op2 operation2 opt_paren_args { $$ = new_call(p, $1, $3, $4, $2); } | block_call call_op2 operation2 opt_paren_args brace_block { $$ = new_call(p, $1, $3, $4, $2); call_with_block(p, $$, $5); } | block_call call_op2 operation2 command_args do_block { $$ = new_call(p, $1, $3, $4, $2); call_with_block(p, $$, $5); } ; method_call : operation paren_args { $$ = new_fcall(p, $1, $2); } | primary_value call_op operation2 opt_paren_args { $$ = new_call(p, $1, $3, $4, $2); } | primary_value tCOLON2 operation2 paren_args { $$ = new_call(p, $1, $3, $4, tCOLON2); } | primary_value tCOLON2 operation3 { $$ = new_call(p, $1, $3, 0, tCOLON2); } | primary_value call_op paren_args { $$ = new_call(p, $1, MRB_SYM_2(p->mrb, call), $3, $2); } | primary_value tCOLON2 paren_args { $$ = new_call(p, $1, MRB_SYM_2(p->mrb, call), $3, tCOLON2); } | keyword_super paren_args { $$ = new_super(p, $2); } | keyword_super { $$ = new_zsuper(p); } | primary_value '[' opt_call_args ']' { $$ = new_call(p, $1, intern_op(aref), $3, '.'); } ; brace_block : '{' { local_nest(p); nvars_nest(p); $$ = p->lineno; } opt_block_param compstmt '}' { $$ = new_block(p,$3,$4); SET_LINENO($$, $2); local_unnest(p); nvars_unnest(p); } | keyword_do { local_nest(p); nvars_nest(p); $$ = p->lineno; } opt_block_param bodystmt keyword_end { $$ = new_block(p,$3,$4); SET_LINENO($$, $2); local_unnest(p); nvars_unnest(p); } ; case_body : keyword_when args then compstmt cases { $$ = cons(cons($2, $4), $5); } ; cases : opt_else { if ($1) { $$ = cons(cons(0, $1), 0); } else { $$ = 0; } } | case_body ; opt_rescue : keyword_rescue exc_list exc_var then compstmt opt_rescue { $$ = list1(list3($2, $3, $5)); if ($6) $$ = append($$, $6); } | none ; exc_list : arg { $$ = list1($1); } | mrhs | none ; exc_var : tASSOC lhs { $$ = $2; } | none ; opt_ensure : keyword_ensure compstmt { $$ = $2; } | none ; literal : numeric | symbol | words | symbols ; string : string_fragment | string string_fragment { $$ = concat_string(p, $1, $2); } ; string_fragment : tCHAR | tSTRING | tSTRING_BEG tSTRING { $$ = $2; } | tSTRING_BEG string_rep tSTRING { node *n = $2; if (intn($3->cdr->cdr) > 0) { n = push(n, $3); } else { cons_free($3); } $$ = new_dstr(p, n); } ; string_rep : string_interp | string_rep string_interp { $$ = append($1, $2); } ; string_interp : tSTRING_MID { $$ = list1($1); } | tSTRING_PART { $$ = push_strterm(p); } compstmt '}' { pop_strterm(p,$2); $$ = list2($1, $3); } | tLITERAL_DELIM { $$ = list1(new_literal_delim(p)); } | tHD_LITERAL_DELIM heredoc_bodies { $$ = list1(new_literal_delim(p)); } ; xstring : tXSTRING_BEG tXSTRING { $$ = $2; } | tXSTRING_BEG string_rep tXSTRING { node *n = $2; if (intn($3->cdr->cdr) > 0) { n = push(n, $3); } else { cons_free($3); } $$ = new_dxstr(p, n); } ; regexp : tREGEXP_BEG tREGEXP { $$ = $2; } | tREGEXP_BEG string_rep tREGEXP { $$ = new_dregx(p, $2, $3); } ; heredoc : tHEREDOC_BEG ; heredoc_bodies : heredoc_body | heredoc_bodies heredoc_body ; heredoc_body : tHEREDOC_END { parser_heredoc_info *info = parsing_heredoc_info(p); info->doc = push(info->doc, new_str(p, "", 0)); heredoc_end(p); } | heredoc_string_rep tHEREDOC_END { heredoc_end(p); } ; heredoc_string_rep : heredoc_string_interp | heredoc_string_rep heredoc_string_interp ; heredoc_string_interp : tHD_STRING_MID { parser_heredoc_info *info = parsing_heredoc_info(p); info->doc = push(info->doc, $1); heredoc_treat_nextline(p); } | tHD_STRING_PART { $$ = push_strterm(p); } compstmt '}' { pop_strterm(p, $2); parser_heredoc_info *info = parsing_heredoc_info(p); info->doc = push(push(info->doc, $1), $3); } ; words : tWORDS_BEG tSTRING { $$ = new_words(p, list1($2)); } | tWORDS_BEG string_rep tSTRING { node *n = $2; if (intn($3->cdr->cdr) > 0) { n = push(n, $3); } else { cons_free($3); } $$ = new_words(p, n); } ; symbol : basic_symbol { $$ = new_sym(p, $1); } | tSYMBEG tSTRING_BEG string_rep tSTRING { node *n = $3; p->lstate = EXPR_ENDARG; if (intn($4->cdr->cdr) > 0) { n = push(n, $4); } else { cons_free($4); } $$ = new_dsym(p, new_dstr(p, n)); } | tSYMBEG tNUMPARAM { mrb_sym sym = intern_numparam($2); $$ = new_sym(p, sym); } ; basic_symbol : tSYMBEG sym { p->lstate = EXPR_END; $$ = $2; } ; sym : fname | tIVAR | tGVAR | tCVAR | tSTRING { $$ = new_strsym(p, $1); } | tSTRING_BEG tSTRING { $$ = new_strsym(p, $2); } ; symbols : tSYMBOLS_BEG tSTRING { $$ = new_symbols(p, list1($2)); } | tSYMBOLS_BEG string_rep tSTRING { node *n = $2; if (intn($3->cdr->cdr) > 0) { n = push(n, $3); } $$ = new_symbols(p, n); } ; numeric : tINTEGER | tFLOAT | tUMINUS_NUM tINTEGER %prec tLOWEST { $$ = new_negate(p, $2); } | tUMINUS_NUM tFLOAT %prec tLOWEST { $$ = new_negate(p, $2); } ; variable : tIDENTIFIER { $$ = new_lvar(p, $1); } | tIVAR { $$ = new_ivar(p, $1); } | tGVAR { $$ = new_gvar(p, $1); } | tCVAR { $$ = new_cvar(p, $1); } | tCONSTANT { $$ = new_const(p, $1); } ; var_lhs : variable { assignable(p, $1); } | tNUMPARAM { yyerror(&@1, p, "can't assign to numbered parameter"); } ; var_ref : variable { $$ = var_reference(p, $1); } | tNUMPARAM { $$ = new_nvar(p, $1); } | keyword_nil { $$ = new_nil(p); } | keyword_self { $$ = new_self(p); } | keyword_true { $$ = new_true(p); } | keyword_false { $$ = new_false(p); } | keyword__FILE__ { const char *fn = mrb_sym_name_len(p->mrb, p->filename_sym, NULL); if (!fn) { fn = "(null)"; } $$ = new_str(p, fn, strlen(fn)); } | keyword__LINE__ { char buf[16]; dump_int(p->lineno, buf); $$ = new_int(p, buf, 10, 0); } | keyword__ENCODING__ { $$ = new_fcall(p, MRB_SYM_2(p->mrb, __ENCODING__), 0); } ; backref : tNTH_REF | tBACK_REF ; superclass : /* term */ { $$ = 0; } | '<' { p->lstate = EXPR_BEG; p->cmd_start = TRUE; } expr_value term { $$ = $3; } /* | error term { yyerrok; $$ = 0; } */ ; f_opt_arglist_paren : f_arglist_paren | none ; f_arglist_paren : '(' f_args rparen { $$ = $2; p->lstate = EXPR_BEG; p->cmd_start = TRUE; } | '(' f_arg ',' tBDOT3 rparen { $$ = new_args_dots(p, $2); } | '(' tBDOT3 rparen { $$ = new_args_dots(p, 0); } ; f_arglist : f_arglist_paren | f_args term { $$ = $1; } | f_arg ',' tBDOT3 term { $$ = new_args_dots(p, $1); } | tDOT3 term { $$ = new_args_dots(p, 0); } ; f_label : tIDENTIFIER tLABEL_TAG { local_nest(p); } | tNUMPARAM tLABEL_TAG { local_nest(p); } ; f_kw : f_label arg { void_expr_error(p, $2); $$ = new_kw_arg(p, $1, cons($2, locals_node(p))); local_unnest(p); } | f_label { $$ = new_kw_arg(p, $1, 0); local_unnest(p); } ; f_block_kw : f_label primary_value { void_expr_error(p, $2); $$ = new_kw_arg(p, $1, cons($2, locals_node(p))); local_unnest(p); } | f_label { $$ = new_kw_arg(p, $1, 0); local_unnest(p); } ; f_block_kwarg : f_block_kw { $$ = list1($1); } | f_block_kwarg ',' f_block_kw { $$ = push($1, $3); } ; f_kwarg : f_kw { $$ = list1($1); } | f_kwarg ',' f_kw { $$ = push($1, $3); } ; kwrest_mark : tPOW | tDSTAR ; f_kwrest : kwrest_mark tIDENTIFIER { $$ = new_kw_rest_args(p, $2); } | kwrest_mark { $$ = new_kw_rest_args(p, 0); } ; args_tail : f_kwarg ',' f_kwrest opt_f_block_arg { $$ = new_args_tail(p, $1, $3, $4); } | f_kwarg opt_f_block_arg { $$ = new_args_tail(p, $1, 0, $2); } | f_kwrest opt_f_block_arg { $$ = new_args_tail(p, 0, $1, $2); } | f_block_arg { $$ = new_args_tail(p, 0, 0, $1); } ; opt_args_tail : ',' args_tail { $$ = $2; } | /* none */ { $$ = new_args_tail(p, 0, 0, 0); } ; f_args : f_arg ',' f_optarg ',' f_rest_arg opt_args_tail { $$ = new_args(p, $1, $3, $5, 0, $6); } | f_arg ',' f_optarg ',' f_rest_arg ',' f_arg opt_args_tail { $$ = new_args(p, $1, $3, $5, $7, $8); } | f_arg ',' f_optarg opt_args_tail { $$ = new_args(p, $1, $3, 0, 0, $4); } | f_arg ',' f_optarg ',' f_arg opt_args_tail { $$ = new_args(p, $1, $3, 0, $5, $6); } | f_arg ',' f_rest_arg opt_args_tail { $$ = new_args(p, $1, 0, $3, 0, $4); } | f_arg ',' f_rest_arg ',' f_arg opt_args_tail { $$ = new_args(p, $1, 0, $3, $5, $6); } | f_arg opt_args_tail { $$ = new_args(p, $1, 0, 0, 0, $2); } | f_optarg ',' f_rest_arg opt_args_tail { $$ = new_args(p, 0, $1, $3, 0, $4); } | f_optarg ',' f_rest_arg ',' f_arg opt_args_tail { $$ = new_args(p, 0, $1, $3, $5, $6); } | f_optarg opt_args_tail { $$ = new_args(p, 0, $1, 0, 0, $2); } | f_optarg ',' f_arg opt_args_tail { $$ = new_args(p, 0, $1, 0, $3, $4); } | f_rest_arg opt_args_tail { $$ = new_args(p, 0, 0, $1, 0, $2); } | f_rest_arg ',' f_arg opt_args_tail { $$ = new_args(p, 0, 0, $1, $3, $4); } | args_tail { $$ = new_args(p, 0, 0, 0, 0, $1); } | /* none */ { local_add_f(p, 0); $$ = new_args(p, 0, 0, 0, 0, 0); } ; f_bad_arg : tCONSTANT { yyerror(&@1, p, "formal argument cannot be a constant"); $$ = 0; } | tIVAR { yyerror(&@1, p, "formal argument cannot be an instance variable"); $$ = 0; } | tGVAR { yyerror(&@1, p, "formal argument cannot be a global variable"); $$ = 0; } | tCVAR { yyerror(&@1, p, "formal argument cannot be a class variable"); $$ = 0; } | tNUMPARAM { yyerror(&@1, p, "formal argument cannot be a numbered parameter"); $$ = 0; } ; f_norm_arg : f_bad_arg { $$ = 0; } | tIDENTIFIER { local_add_f(p, $1); $$ = $1; } ; f_arg_item : f_norm_arg { $$ = new_arg(p, $1); } | tLPAREN { $$ = local_switch(p); } f_margs rparen { $$ = new_masgn_param(p, $3, p->locals->car); local_resume(p, $2); local_add_f(p, 0); } ; f_arg : f_arg_item { $$ = list1($1); } | f_arg ',' f_arg_item { $$ = push($1, $3); } ; f_opt_asgn : tIDENTIFIER '=' { local_add_f(p, $1); local_nest(p); $$ = $1; } ; f_opt : f_opt_asgn arg { void_expr_error(p, $2); $$ = cons(nsym($1), cons($2, locals_node(p))); local_unnest(p); } ; f_block_opt : f_opt_asgn primary_value { void_expr_error(p, $2); $$ = cons(nsym($1), cons($2, locals_node(p))); local_unnest(p); } ; f_block_optarg : f_block_opt { $$ = list1($1); } | f_block_optarg ',' f_block_opt { $$ = push($1, $3); } ; f_optarg : f_opt { $$ = list1($1); } | f_optarg ',' f_opt { $$ = push($1, $3); } ; restarg_mark : '*' | tSTAR ; f_rest_arg : restarg_mark tIDENTIFIER { local_add_f(p, $2); $$ = $2; } | restarg_mark { $$ = intern_op(mul); local_add_f(p, $$); } ; blkarg_mark : '&' | tAMPER ; f_block_arg : blkarg_mark tIDENTIFIER { $$ = $2; } | blkarg_mark { $$ = intern_op(and); } ; opt_f_block_arg : ',' f_block_arg { $$ = $2; } | none { $$ = 0; } ; singleton : var_ref { prohibit_literals(p, $1); $$ = $1; if (!$$) $$ = new_nil(p); } | '(' {p->lstate = EXPR_BEG;} expr rparen { prohibit_literals(p, $3); $$ = $3; } ; assoc_list : none | assocs trailer { $$ = $1; } ; assocs : assoc { $$ = list1($1); NODE_LINENO($$, $1); } | assocs comma assoc { $$ = push($1, $3); } ; assoc : arg tASSOC arg { void_expr_error(p, $1); void_expr_error(p, $3); $$ = cons($1, $3); } | tIDENTIFIER tLABEL_TAG arg { void_expr_error(p, $3); $$ = cons(new_sym(p, $1), $3); } | tIDENTIFIER tLABEL_TAG { $$ = cons(new_sym(p, $1), label_reference(p, $1)); } | tNUMPARAM tLABEL_TAG { mrb_sym sym = intern_numparam($1); $$ = cons(new_sym(p, sym), label_reference(p, sym)); } | tNUMPARAM tLABEL_TAG arg { void_expr_error(p, $3); $$ = cons(new_sym(p, intern_numparam($1)), $3); } | string_fragment tLABEL_TAG arg { void_expr_error(p, $3); if (typen($1->car) == NODE_DSTR) { $$ = cons(new_dsym(p, $1), $3); } else { $$ = cons(new_sym(p, new_strsym(p, $1)), $3); } } | tDSTAR arg { void_expr_error(p, $2); $$ = cons(new_kw_rest_args(p, 0), $2); } | tDSTAR { $$ = cons(new_kw_rest_args(p, 0), new_lvar(p, intern_op(pow))); } ; operation : tIDENTIFIER | tCONSTANT | tFID ; operation2 : tIDENTIFIER | tCONSTANT | tFID | op ; operation3 : tIDENTIFIER | tFID | op ; dot_or_colon : '.' | tCOLON2 ; call_op : '.' { $$ = '.'; } | tANDDOT { $$ = 0; } ; call_op2 : call_op | tCOLON2 { $$ = tCOLON2; } ; opt_terms : /* none */ | terms ; opt_nl : /* none */ | opt_nl nl ; rparen : opt_terms ')' ; trailer : /* none */ | terms | comma ; term : ';' {yyerrok;} | nl ; nl : '\n' { p->lineno += $1; p->column = 0; } | heredoc_body ; terms : term | terms term ; none : /* none */ { $$ = 0; } ; %% #define pylval (*((YYSTYPE*)(p->ylval))) static void yyerror(void *lp, parser_state *p, const char *s) { char* c; size_t n; if (! p->capture_errors) { #ifndef MRB_NO_STDIO if (p->filename_sym) { const char *filename = mrb_sym_name_len(p->mrb, p->filename_sym, NULL); fprintf(stderr, "%s:%d:%d: %s\n", filename, p->lineno, p->column, s); } else { fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s); } #endif } else if (p->nerr < sizeof(p->error_buffer) / sizeof(p->error_buffer[0])) { n = strlen(s); c = (char*)parser_palloc(p, n + 1); memcpy(c, s, n + 1); p->error_buffer[p->nerr].message = c; p->error_buffer[p->nerr].lineno = p->lineno; p->error_buffer[p->nerr].column = p->column; } p->nerr++; } static void yyerror_c(parser_state *p, const char *msg, char c) { char buf[256]; strncpy(buf, msg, sizeof(buf) - 2); buf[sizeof(buf) - 2] = '\0'; strncat(buf, &c, 1); yyerror(NULL, p, buf); } static void yywarning(parser_state *p, const char *s) { char* c; size_t n; if (! p->capture_errors) { #ifndef MRB_NO_STDIO if (p->filename_sym) { const char *filename = mrb_sym_name_len(p->mrb, p->filename_sym, NULL); fprintf(stderr, "%s:%d:%d: warning: %s\n", filename, p->lineno, p->column, s); } else { fprintf(stderr, "line %d:%d: warning: %s\n", p->lineno, p->column, s); } #endif } else if (p->nwarn < sizeof(p->warn_buffer) / sizeof(p->warn_buffer[0])) { n = strlen(s); c = (char*)parser_palloc(p, n + 1); memcpy(c, s, n + 1); p->warn_buffer[p->nwarn].message = c; p->warn_buffer[p->nwarn].lineno = p->lineno; p->warn_buffer[p->nwarn].column = p->column; } p->nwarn++; } static void yywarning_s(parser_state *p, const char *msg, const char *s) { char buf[256]; strncpy(buf, msg, sizeof(buf) - 1); buf[sizeof(buf) - 1] = '\0'; strncat(buf, ": ", sizeof(buf) - strlen(buf) - 1); strncat(buf, s, sizeof(buf) - strlen(buf) - 1); yywarning(p, buf); } static void backref_error(parser_state *p, node *n) { int c; c = intn(n->car); if (c == NODE_NTH_REF) { yyerror_c(p, "can't set variable $", (char)intn(n->cdr)+'0'); } else if (c == NODE_BACK_REF) { yyerror_c(p, "can't set variable $", (char)intn(n->cdr)); } else { yyerror(NULL, p, "Internal error in backref_error()"); } } static void void_expr_error(parser_state *p, node *n) { int c; if (n == NULL) return; c = intn(n->car); switch (c) { case NODE_BREAK: case NODE_RETURN: case NODE_NEXT: case NODE_REDO: case NODE_RETRY: yyerror(NULL, p, "void value expression"); break; case NODE_AND: case NODE_OR: if (n->cdr) { void_expr_error(p, n->cdr->car); void_expr_error(p, n->cdr->cdr); } break; case NODE_BEGIN: if (n->cdr) { while (n->cdr) { n = n->cdr; } void_expr_error(p, n->car); } break; default: break; } } static void pushback(parser_state *p, int c); static mrb_bool peeks(parser_state *p, const char *s); static mrb_bool skips(parser_state *p, const char *s); static inline int nextc0(parser_state *p) { if (p->s && p->s < p->send) { return (unsigned char)*p->s++; } else { #ifndef MRB_NO_STDIO int c; if (p->f) { c = fgetc(p->f); if (!feof(p->f)) return c; } #endif return -1; } } static inline int nextc(parser_state *p) { int c; if (p->pb) { node *tmp; c = intn(p->pb->car); tmp = p->pb; p->pb = p->pb->cdr; cons_free(tmp); } else { c = nextc0(p); if (c < 0) goto eof; } if (c >= 0) { p->column++; } if (c == '\r') { const int lf = nextc0(p); if (lf == '\n') { return '\n'; } if (lf > 0) pushback(p, lf); } return c; eof: if (!p->cxt) return -1; else { if (p->cxt->partial_hook(p) < 0) return -1; /* end of program(s) */ return -2; /* end of a file in the program files */ } } static void pushback(parser_state *p, int c) { if (c >= 0) { p->column--; } p->pb = cons(nint(c), p->pb); } static void skip(parser_state *p, char term) { int c; for (;;) { c = nextc(p); if (c < 0) break; if (c == term) break; } } static int peekc_n(parser_state *p, int n) { node *list = 0; int c0; do { c0 = nextc(p); if (c0 == -1) return c0; /* do not skip partial EOF */ if (c0 >= 0) --p->column; list = push(list, nint(c0)); } while(n--); if (p->pb) { p->pb = append(list, p->pb); } else { p->pb = list; } return c0; } static mrb_bool peek_n(parser_state *p, int c, int n) { return peekc_n(p, n) == c && c >= 0; } #define peek(p,c) peek_n((p), (c), 0) static mrb_bool peeks(parser_state *p, const char *s) { size_t len = strlen(s); #ifndef MRB_NO_STDIO if (p->f) { int n = 0; while (*s) { if (!peek_n(p, *s++, n++)) return FALSE; } return TRUE; } else #endif if (p->s && p->s + len <= p->send) { if (memcmp(p->s, s, len) == 0) return TRUE; } return FALSE; } static mrb_bool skips(parser_state *p, const char *s) { int c; for (;;) { /* skip until first char */ for (;;) { c = nextc(p); if (c < 0) return FALSE; if (c == '\n') { p->lineno++; p->column = 0; } if (c == *s) break; } s++; if (peeks(p, s)) { size_t len = strlen(s); while (len--) { if (nextc(p) == '\n') { p->lineno++; p->column = 0; } } return TRUE; } else{ s--; } } return FALSE; } static int newtok(parser_state *p) { if (p->tokbuf != p->buf) { mrbc_free(p->tokbuf); p->tokbuf = p->buf; p->tsiz = MRB_PARSER_TOKBUF_SIZE; } p->tidx = 0; return p->column - 1; } static void tokadd(parser_state *p, int32_t c) { char utf8[4]; int i, len; /* mrb_assert(-0x10FFFF <= c && c <= 0xFF); */ if (c >= 0) { /* Single byte from source or non-Unicode escape */ utf8[0] = (char)c; len = 1; } else { /* Unicode character */ c = -c; if (c < 0x80) { utf8[0] = (char)c; len = 1; } else if (c < 0x800) { utf8[0] = (char)(0xC0 | (c >> 6)); utf8[1] = (char)(0x80 | (c & 0x3F)); len = 2; } else if (c < 0x10000) { utf8[0] = (char)(0xE0 | (c >> 12) ); utf8[1] = (char)(0x80 | ((c >> 6) & 0x3F)); utf8[2] = (char)(0x80 | ( c & 0x3F)); len = 3; } else { utf8[0] = (char)(0xF0 | (c >> 18) ); utf8[1] = (char)(0x80 | ((c >> 12) & 0x3F)); utf8[2] = (char)(0x80 | ((c >> 6) & 0x3F)); utf8[3] = (char)(0x80 | ( c & 0x3F)); len = 4; } } if (p->tidx+len >= p->tsiz) { if (p->tsiz >= MRB_PARSER_TOKBUF_MAX) { p->tidx += len; return; } p->tsiz *= 2; if (p->tokbuf == p->buf) { p->tokbuf = (char*)mrbc_malloc(p->tsiz); memcpy(p->tokbuf, p->buf, MRB_PARSER_TOKBUF_SIZE); } else { p->tokbuf = (char*)mrbc_realloc(p->tokbuf, p->tsiz); } } for (i = 0; i < len; i++) { p->tokbuf[p->tidx++] = utf8[i]; } } static int toklast(parser_state *p) { return p->tokbuf[p->tidx-1]; } static void tokfix(parser_state *p) { if (p->tidx >= MRB_PARSER_TOKBUF_MAX) { p->tidx = MRB_PARSER_TOKBUF_MAX-1; yyerror(NULL, p, "string too long (truncated)"); } p->tokbuf[p->tidx] = '\0'; } static const char* tok(parser_state *p) { return p->tokbuf; } static int toklen(parser_state *p) { return p->tidx; } #define IS_ARG() (p->lstate == EXPR_ARG || p->lstate == EXPR_CMDARG) #define IS_END() (p->lstate == EXPR_END || p->lstate == EXPR_ENDARG || p->lstate == EXPR_ENDFN) #define IS_BEG() (p->lstate == EXPR_BEG || p->lstate == EXPR_MID || p->lstate == EXPR_VALUE || p->lstate == EXPR_CLASS) #define IS_SPCARG(c) (IS_ARG() && space_seen && !ISSPACE(c)) #define IS_LABEL_POSSIBLE() ((p->lstate == EXPR_BEG && !cmd_state) || IS_ARG()) #define IS_LABEL_SUFFIX(n) (peek_n(p, ':',(n)) && !peek_n(p, ':', (n)+1)) static int32_t scan_oct(const int *start, int len, int *retlen) { const int *s = start; int32_t retval = 0; /* mrb_assert(len <= 3) */ while (len-- && *s >= '0' && *s <= '7') { retval <<= 3; retval |= *s++ - '0'; } *retlen = (int)(s - start); return retval; } static int32_t scan_hex(parser_state *p, const int *start, int len, int *retlen) { static const char hexdigit[] = "0123456789abcdef0123456789ABCDEF"; const int *s = start; uint32_t retval = 0; char *tmp; /* mrb_assert(len <= 8) */ while (len-- && *s && (tmp = (char*)strchr(hexdigit, *s))) { retval <<= 4; retval |= (tmp - hexdigit) & 15; s++; } *retlen = (int)(s - start); return (int32_t)retval; } static int32_t read_escape_unicode(parser_state *p, int limit) { int buf[9]; int i; int32_t hex; /* Look for opening brace */ i = 0; buf[0] = nextc(p); if (buf[0] < 0) { eof: yyerror(NULL, p, "invalid escape character syntax"); return -1; } if (ISXDIGIT(buf[0])) { /* \uxxxx form */ for (i=1; i 0x10FFFF || (hex & 0xFFFFF800) == 0xD800) { yyerror(NULL, p, "invalid Unicode code point"); return -1; } return hex; } /* Return negative to indicate Unicode code point */ static int32_t read_escape(parser_state *p) { int32_t c; switch (c = nextc(p)) { case '\\':/* Backslash */ return c; case 'n':/* newline */ return '\n'; case 't':/* horizontal tab */ return '\t'; case 'r':/* carriage-return */ return '\r'; case 'f':/* form-feed */ return '\f'; case 'v':/* vertical tab */ return '\13'; case 'a':/* alarm(bell) */ return '\007'; case 'e':/* escape */ return 033; case '0': case '1': case '2': case '3': /* octal constant */ case '4': case '5': case '6': case '7': { int buf[3]; int i; buf[0] = c; for (i=1; i<3; i++) { buf[i] = nextc(p); if (buf[i] < 0) goto eof; if (buf[i] < '0' || '7' < buf[i]) { pushback(p, buf[i]); break; } } c = scan_oct(buf, i, &i); } return c; case 'x': /* hex constant */ { int buf[2]; int i; for (i=0; i<2; i++) { buf[i] = nextc(p); if (buf[i] < 0) goto eof; if (!ISXDIGIT(buf[i])) { pushback(p, buf[i]); break; } } if (i == 0) { yyerror(NULL, p, "invalid hex escape"); return -1; } return scan_hex(p, buf, i, &i); } case 'u': /* Unicode */ if (peek(p, '{')) { /* \u{xxxxxxxx} form */ nextc(p); c = read_escape_unicode(p, 8); if (c < 0) return 0; if (nextc(p) != '}') goto eof; } else { c = read_escape_unicode(p, 4); if (c < 0) return 0; } return -c; case 'b':/* backspace */ return '\010'; case 's':/* space */ return ' '; case 'M': if ((c = nextc(p)) != '-') { yyerror(NULL, p, "Invalid escape character syntax"); pushback(p, c); return '\0'; } if ((c = nextc(p)) == '\\') { return read_escape(p) | 0x80; } else if (c < 0) goto eof; else { return ((c & 0xff) | 0x80); } case 'C': if ((c = nextc(p)) != '-') { yyerror(NULL, p, "Invalid escape character syntax"); pushback(p, c); return '\0'; } case 'c': if ((c = nextc(p))== '\\') { c = read_escape(p); } else if (c == '?') return 0177; else if (c < 0) goto eof; return c & 0x9f; eof: case -1: case -2: /* end of a file */ yyerror(NULL, p, "Invalid escape character syntax"); return '\0'; default: return c; } } static void heredoc_count_indent(parser_heredoc_info *hinfo, const char *str, size_t len, size_t spaces, size_t *offset) { size_t indent = 0; *offset = 0; for (size_t i = 0; i < len; i++) { size_t size; if (str[i] == '\n') break; else if (str[i] == '\t') size = 8; else if (ISSPACE(str[i])) size = 1; else break; size_t nindent = indent + size; if (nindent > spaces || nindent > hinfo->indent) break; indent = nindent; *offset += 1; } } static void heredoc_remove_indent(parser_state *p, parser_heredoc_info *hinfo) { if (!hinfo->remove_indent || hinfo->indent == 0) return; node *indented, *n, *pair, *escaped, *nspaces; const char *str; size_t len, spaces, offset, start, end; indented = hinfo->indented; while (indented) { n = indented->car; pair = n->car; str = (char*)pair->car; len = (size_t)pair->cdr; escaped = n->cdr->car; nspaces = n->cdr->cdr; if (escaped) { char *newstr = strndup(str, len); size_t newlen = 0; start = 0; while (start < len) { end = escaped ? (size_t)escaped->car : len; if (end > len) end = len; spaces = (size_t)nspaces->car; size_t esclen = end - start; heredoc_count_indent(hinfo, str + start, esclen, spaces, &offset); esclen -= offset; memcpy(newstr + newlen, str + start + offset, esclen); newlen += esclen; start = end; if (escaped) escaped = escaped->cdr; nspaces = nspaces->cdr; } if (newlen < len) newstr[newlen] = '\0'; pair->car = (node*)newstr; pair->cdr = (node*)newlen; } else { spaces = (size_t)nspaces->car; heredoc_count_indent(hinfo, str, len, spaces, &offset); pair->car = (node*)(str + offset); pair->cdr = (node*)(len - offset); } indented = indented->cdr; } } static void heredoc_push_indented(parser_state *p, parser_heredoc_info *hinfo, node *pair, node *escaped, node *nspaces, mrb_bool empty_line) { hinfo->indented = push(hinfo->indented, cons(pair, cons(escaped, nspaces))); while (nspaces) { size_t tspaces = (size_t)nspaces->car; if ((hinfo->indent == ~0U || tspaces < hinfo->indent) && !empty_line) hinfo->indent = tspaces; nspaces = nspaces->cdr; } } static int parse_string(parser_state *p) { int c; string_type type = (string_type)p->lex_strterm->type; int nest_level = p->lex_strterm->level; int beg = p->lex_strterm->paren; int end = p->lex_strterm->term; parser_heredoc_info *hinfo = (type & STR_FUNC_HEREDOC) ? parsing_heredoc_info(p) : NULL; mrb_bool unindent = hinfo && hinfo->remove_indent; mrb_bool head = hinfo && hinfo->line_head; mrb_bool empty = TRUE; size_t spaces = 0; size_t pos = -1; node *escaped = NULL; node *nspaces = NULL; if (beg == 0) beg = -3; /* should never happen */ if (end == 0) end = -3; newtok(p); while ((c = nextc(p)) != end || nest_level != 0) { pos++; if (hinfo && (c == '\n' || c < 0)) { mrb_bool line_head; tokadd(p, '\n'); tokfix(p); p->lineno++; p->column = 0; line_head = hinfo->line_head; hinfo->line_head = TRUE; if (line_head) { /* check whether end of heredoc */ const char *s = tok(p); int len = toklen(p); if (hinfo->allow_indent) { while (ISSPACE(*s) && len > 0) { s++; len--; } } if (hinfo->term_len > 0 && len-1 == hinfo->term_len && strncmp(s, hinfo->term, len-1) == 0) { heredoc_remove_indent(p, hinfo); return tHEREDOC_END; } } if (c < 0) { char buf[256]; const char s1[] = "can't find heredoc delimiter \""; const char s2[] = "\" anywhere before EOF"; if (sizeof(s1)+sizeof(s2)+strlen(hinfo->term)+1 >= sizeof(buf)) { yyerror(NULL, p, "can't find heredoc delimiter anywhere before EOF"); } else { strcpy(buf, s1); strcat(buf, hinfo->term); strcat(buf, s2); yyerror(NULL, p, buf); } return 0; } node *nd = new_str(p, tok(p), toklen(p)); pylval.nd = nd; if (unindent && head) { nspaces = push(nspaces, nint(spaces)); heredoc_push_indented(p, hinfo, nd->cdr, escaped, nspaces, empty && line_head); } return tHD_STRING_MID; } if (unindent && empty) { if (c == '\t') spaces += 8; else if (ISSPACE(c)) spaces++; else empty = FALSE; } if (c < 0) { yyerror(NULL, p, "unterminated string meets end of file"); return 0; } else if (c == beg) { nest_level++; p->lex_strterm->level = nest_level; } else if (c == end) { nest_level--; p->lex_strterm->level = nest_level; } else if (c == '\\') { c = nextc(p); if (type & STR_FUNC_EXPAND) { if (c == end || c == beg) { tokadd(p, c); } else if (c == '\n') { p->lineno++; p->column = 0; if (unindent) { nspaces = push(nspaces, nint(spaces)); escaped = push(escaped, nint(pos)); pos--; empty = TRUE; spaces = 0; } if (type & STR_FUNC_ARRAY) { tokadd(p, '\n'); } } else if (type & STR_FUNC_REGEXP) { tokadd(p, '\\'); tokadd(p, c); } else if (c == 'u' && peek(p, '{')) { /* \u{xxxx xxxx xxxx} form */ nextc(p); while (1) { do c = nextc(p); while (ISSPACE(c)); if (c == '}') break; pushback(p, c); c = read_escape_unicode(p, 8); if (c < 0) break; tokadd(p, -c); } if (hinfo) hinfo->line_head = FALSE; } else { pushback(p, c); tokadd(p, read_escape(p)); if (hinfo) hinfo->line_head = FALSE; } } else { if (c != beg && c != end) { if (c == '\n') { p->lineno++; p->column = 0; } if (!(c == '\\' || ((type & STR_FUNC_ARRAY) && ISSPACE(c)))) { tokadd(p, '\\'); } } tokadd(p, c); } continue; } else if ((c == '#') && (type & STR_FUNC_EXPAND)) { c = nextc(p); if (c == '{') { tokfix(p); p->lstate = EXPR_BEG; p->cmd_start = TRUE; node *nd = new_str(p, tok(p), toklen(p)); pylval.nd = nd; if (hinfo) { if (unindent && head) { nspaces = push(nspaces, nint(spaces)); heredoc_push_indented(p, hinfo, nd->cdr, escaped, nspaces, FALSE); } hinfo->line_head = FALSE; return tHD_STRING_PART; } return tSTRING_PART; } tokadd(p, '#'); pushback(p, c); continue; } if ((type & STR_FUNC_ARRAY) && ISSPACE(c)) { if (toklen(p) == 0) { do { if (c == '\n') { p->lineno++; p->column = 0; heredoc_treat_nextline(p); if (p->parsing_heredoc != NULL) { return tHD_LITERAL_DELIM; } } c = nextc(p); } while (ISSPACE(c)); pushback(p, c); return tLITERAL_DELIM; } else { pushback(p, c); tokfix(p); pylval.nd = new_str(p, tok(p), toklen(p)); return tSTRING_MID; } } if (c == '\n') { p->lineno++; p->column = 0; } tokadd(p, c); } tokfix(p); p->lstate = EXPR_END; end_strterm(p); if (type & STR_FUNC_XQUOTE) { pylval.nd = new_xstr(p, tok(p), toklen(p)); return tXSTRING; } if (type & STR_FUNC_REGEXP) { int f = 0; int re_opt; char *s = strndup(tok(p), toklen(p)); char flags[3]; char *flag = flags; char enc = '\0'; char *encp; char *dup; newtok(p); while (re_opt = nextc(p), re_opt >= 0 && ISALPHA(re_opt)) { switch (re_opt) { case 'i': f |= 1; break; case 'x': f |= 2; break; case 'm': f |= 4; break; case 'u': f |= 16; break; case 'n': f |= 32; break; case 'o': break; default: tokadd(p, re_opt); break; } } pushback(p, re_opt); if (toklen(p)) { char msg[128]; strcpy(msg, "unknown regexp option"); tokfix(p); if (toklen(p) > 1) { strcat(msg, "s"); } strcat(msg, " - "); strncat(msg, tok(p), sizeof(msg) - strlen(msg) - 1); yyerror(NULL, p, msg); } if (f != 0) { if (f & 1) *flag++ = 'i'; if (f & 2) *flag++ = 'x'; if (f & 4) *flag++ = 'm'; if (f & 16) enc = 'u'; if (f & 32) enc = 'n'; } if (flag > flags) { dup = strndup(flags, (size_t)(flag - flags)); } else { dup = NULL; } if (enc) { encp = strndup(&enc, 1); } else { encp = NULL; } pylval.nd = new_regx(p, s, dup, encp); return tREGEXP; } pylval.nd = new_str(p, tok(p), toklen(p)); return tSTRING; } static int number_literal_suffix(parser_state *p) { int c, result = 0; node *list = 0; int column = p->column; int mask = NUM_SUFFIX_R|NUM_SUFFIX_I; while ((c = nextc(p)) != -1) { list = push(list, nint(c)); if ((mask & NUM_SUFFIX_I) && c == 'i') { result |= (mask & NUM_SUFFIX_I); mask &= ~NUM_SUFFIX_I; /* r after i, rational of complex is disallowed */ mask &= ~NUM_SUFFIX_R; continue; } if ((mask & NUM_SUFFIX_R) && c == 'r') { result |= (mask & NUM_SUFFIX_R); mask &= ~NUM_SUFFIX_R; continue; } if (!ISASCII(c) || ISALPHA(c) || c == '_') { p->column = column; if (p->pb) { p->pb = append(list, p->pb); } else { p->pb = list; } return 0; } pushback(p, c); break; } return result; } static int heredoc_identifier(parser_state *p) { int c; int type = str_heredoc; mrb_bool indent = FALSE; mrb_bool squiggly = FALSE; mrb_bool quote = FALSE; node *newnode; parser_heredoc_info *info; c = nextc(p); if (ISSPACE(c) || c == '=') { pushback(p, c); return 0; } if (c == '-' || c == '~') { if (c == '-') indent = TRUE; if (c == '~') squiggly = TRUE; c = nextc(p); } if (c == '\'' || c == '"') { int term = c; if (c == '\'') quote = TRUE; newtok(p); while ((c = nextc(p)) >= 0 && c != term) { if (c == '\n') { c = -1; break; } tokadd(p, c); } if (c < 0) { yyerror(NULL, p, "unterminated here document identifier"); return 0; } } else { if (c < 0) { return 0; /* missing here document identifier */ } if (! identchar(c)) { pushback(p, c); if (indent) pushback(p, '-'); if (squiggly) pushback(p, '~'); return 0; } newtok(p); do { tokadd(p, c); } while ((c = nextc(p)) >= 0 && identchar(c)); pushback(p, c); } tokfix(p); newnode = new_heredoc(p); info = (parser_heredoc_info*)newnode->cdr; info->term = strndup(tok(p), toklen(p)); info->term_len = toklen(p); if (! quote) type |= STR_FUNC_EXPAND; info->type = (string_type)type; info->allow_indent = indent || squiggly; info->remove_indent = squiggly; info->indent = ~0U; info->indented = NULL; info->line_head = TRUE; info->doc = NULL; p->heredocs_from_nextline = push(p->heredocs_from_nextline, newnode); p->lstate = EXPR_END; pylval.nd = newnode; return tHEREDOC_BEG; } static int arg_ambiguous(parser_state *p) { yywarning(p, "ambiguous first argument; put parentheses or even spaces"); return 1; } #include "lex.def" static int parser_yylex(parser_state *p) { int32_t c; int nlines = 1; int space_seen = 0; int cmd_state; enum mrb_lex_state_enum last_state; int token_column; if (p->lex_strterm) { if (is_strterm_type(p, STR_FUNC_HEREDOC)) { if (p->parsing_heredoc != NULL) return parse_string(p); } else return parse_string(p); } cmd_state = p->cmd_start; p->cmd_start = FALSE; retry: last_state = p->lstate; switch (c = nextc(p)) { case '\004': /* ^D */ case '\032': /* ^Z */ case '\0': /* NUL */ case -1: /* end of script. */ if (p->heredocs_from_nextline) goto maybe_heredoc; return 0; /* white spaces */ case ' ': case '\t': case '\f': case '\r': case '\13': /* '\v' */ space_seen = 1; goto retry; case '#': /* it's a comment */ skip(p, '\n'); /* fall through */ case -2: /* end of a file */ case '\n': maybe_heredoc: heredoc_treat_nextline(p); p->column = 0; switch (p->lstate) { case EXPR_BEG: case EXPR_FNAME: case EXPR_DOT: case EXPR_CLASS: case EXPR_VALUE: p->lineno++; if (p->parsing_heredoc != NULL) { if (p->lex_strterm) { return parse_string(p); } } goto retry; default: break; } if (p->parsing_heredoc != NULL) { pylval.num = nlines; return '\n'; } while ((c = nextc(p))) { switch (c) { case ' ': case '\t': case '\f': case '\r': case '\13': /* '\v' */ space_seen = 1; break; case '#': /* comment as a whitespace */ skip(p, '\n'); nlines++; break; case '.': if (!peek(p, '.')) { pushback(p, '.'); p->lineno+=nlines; nlines=1; goto retry; } pushback(p, c); goto normal_newline; case '&': if (peek(p, '.')) { pushback(p, '&'); p->lineno+=nlines; nlines=1; goto retry; } pushback(p, c); goto normal_newline; case -1: /* EOF */ case -2: /* end of a file */ goto normal_newline; default: pushback(p, c); goto normal_newline; } } normal_newline: p->cmd_start = TRUE; p->lstate = EXPR_BEG; pylval.num = nlines; return '\n'; case '*': if ((c = nextc(p)) == '*') { if ((c = nextc(p)) == '=') { pylval.id = intern_op(pow); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); if (IS_SPCARG(c)) { yywarning(p, "'**' interpreted as argument prefix"); c = tDSTAR; } else if (IS_BEG()) { c = tDSTAR; } else { c = tPOW; /* "**", "argument prefix" */ } } else { if (c == '=') { pylval.id = intern_op(mul); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); if (IS_SPCARG(c)) { yywarning(p, "'*' interpreted as argument prefix"); c = tSTAR; } else if (IS_BEG()) { c = tSTAR; } else { c = '*'; } } if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; } else { p->lstate = EXPR_BEG; } return c; case '!': c = nextc(p); if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; if (c == '@') { return '!'; } } else { p->lstate = EXPR_BEG; } if (c == '=') { return tNEQ; } if (c == '~') { return tNMATCH; } pushback(p, c); return '!'; case '=': if (p->column == 1) { static const char begin[] = "begin"; static const char end[] = "\n=end"; if (peeks(p, begin)) { c = peekc_n(p, sizeof(begin)-1); if (c < 0 || ISSPACE(c)) { do { if (!skips(p, end)) { yyerror(NULL, p, "embedded document meets end of file"); return 0; } c = nextc(p); } while (!(c < 0 || ISSPACE(c))); if (c != '\n') skip(p, '\n'); p->lineno+=nlines; nlines=1; p->column = 0; goto retry; } } } if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; } else { p->lstate = EXPR_BEG; } if ((c = nextc(p)) == '=') { if ((c = nextc(p)) == '=') { return tEQQ; } pushback(p, c); return tEQ; } if (c == '~') { return tMATCH; } else if (c == '>') { return tASSOC; } pushback(p, c); return '='; case '<': c = nextc(p); if (c == '<' && p->lstate != EXPR_DOT && p->lstate != EXPR_CLASS && !IS_END() && (!IS_ARG() || space_seen)) { int token = heredoc_identifier(p); if (token) return token; } if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; } else { p->lstate = EXPR_BEG; if (p->lstate == EXPR_CLASS) { p->cmd_start = TRUE; } } if (c == '=') { if ((c = nextc(p)) == '>') { return tCMP; } pushback(p, c); return tLEQ; } if (c == '<') { if ((c = nextc(p)) == '=') { pylval.id = intern_op(lshift); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); return tLSHFT; } pushback(p, c); return '<'; case '>': if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; } else { p->lstate = EXPR_BEG; } if ((c = nextc(p)) == '=') { return tGEQ; } if (c == '>') { if ((c = nextc(p)) == '=') { pylval.id = intern_op(rshift); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); return tRSHFT; } pushback(p, c); return '>'; case '"': p->lex_strterm = new_strterm(p, str_dquote, '"', 0); return tSTRING_BEG; case '\'': p->lex_strterm = new_strterm(p, str_squote, '\'', 0); return parse_string(p); case '`': if (p->lstate == EXPR_FNAME) { p->lstate = EXPR_ENDFN; return '`'; } if (p->lstate == EXPR_DOT) { if (cmd_state) p->lstate = EXPR_CMDARG; else p->lstate = EXPR_ARG; return '`'; } p->lex_strterm = new_strterm(p, str_xquote, '`', 0); return tXSTRING_BEG; case '?': if (IS_END()) { p->lstate = EXPR_VALUE; return '?'; } c = nextc(p); if (c < 0) { yyerror(NULL, p, "incomplete character syntax"); return 0; } if (ISSPACE(c)) { if (!IS_ARG()) { int c2; switch (c) { case ' ': c2 = 's'; break; case '\n': c2 = 'n'; break; case '\t': c2 = 't'; break; case '\v': c2 = 'v'; break; case '\r': c2 = 'r'; break; case '\f': c2 = 'f'; break; default: c2 = 0; break; } if (c2) { char buf[256]; char cc[] = { (char)c2, '\0' }; strcpy(buf, "invalid character syntax; use ?\\"); strncat(buf, cc, 2); yyerror(NULL, p, buf); } } ternary: pushback(p, c); p->lstate = EXPR_VALUE; return '?'; } newtok(p); /* need support UTF-8 if configured */ if ((ISALNUM(c) || c == '_')) { int c2 = nextc(p); pushback(p, c2); if ((ISALNUM(c2) || c2 == '_')) { goto ternary; } } if (c == '\\') { c = read_escape(p); tokadd(p, c); } else { tokadd(p, c); } tokfix(p); pylval.nd = new_str(p, tok(p), toklen(p)); p->lstate = EXPR_END; return tCHAR; case '&': if ((c = nextc(p)) == '&') { p->lstate = EXPR_BEG; if ((c = nextc(p)) == '=') { pylval.id = intern_op(andand); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); return tANDOP; } else if (c == '.') { p->lstate = EXPR_DOT; return tANDDOT; } else if (c == '=') { pylval.id = intern_op(and); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); if (IS_SPCARG(c)) { yywarning(p, "'&' interpreted as argument prefix"); c = tAMPER; } else if (IS_BEG()) { c = tAMPER; } else { c = '&'; } if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; } else { p->lstate = EXPR_BEG; } return c; case '|': if ((c = nextc(p)) == '|') { p->lstate = EXPR_BEG; if ((c = nextc(p)) == '=') { pylval.id = intern_op(oror); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); return tOROP; } if (c == '=') { pylval.id = intern_op(or); p->lstate = EXPR_BEG; return tOP_ASGN; } if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; } else { p->lstate = EXPR_BEG; } pushback(p, c); return '|'; case '+': c = nextc(p); if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; if (c == '@') { return tUPLUS; } pushback(p, c); return '+'; } if (c == '=') { pylval.id = intern_op(add); p->lstate = EXPR_BEG; return tOP_ASGN; } if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p))) { p->lstate = EXPR_BEG; pushback(p, c); if (c >= 0 && ISDIGIT(c)) { c = '+'; goto start_num; } return tUPLUS; } p->lstate = EXPR_BEG; pushback(p, c); return '+'; case '-': c = nextc(p); if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; if (c == '@') { return tUMINUS; } pushback(p, c); return '-'; } if (c == '=') { pylval.id = intern_op(sub); p->lstate = EXPR_BEG; return tOP_ASGN; } if (c == '>') { p->lstate = EXPR_ENDFN; return tLAMBDA; } if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p))) { p->lstate = EXPR_BEG; pushback(p, c); if (c >= 0 && ISDIGIT(c)) { return tUMINUS_NUM; } return tUMINUS; } p->lstate = EXPR_BEG; pushback(p, c); return '-'; case '.': { int is_beg = IS_BEG(); p->lstate = EXPR_MID; if ((c = nextc(p)) == '.') { if ((c = nextc(p)) == '.') { return is_beg ? tBDOT3 : tDOT3; } pushback(p, c); return is_beg ? tBDOT2 : tDOT2; } pushback(p, c); p->lstate = EXPR_BEG; if (c >= 0 && ISDIGIT(c)) { yyerror(NULL, p, "no . floating literal anymore; put 0 before dot"); } p->lstate = EXPR_DOT; return '.'; } start_num: case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { int is_float, seen_point, seen_e, nondigit; int suffix = 0; is_float = seen_point = seen_e = nondigit = 0; p->lstate = EXPR_END; newtok(p); if (c == '-') { tokadd(p, c); c = nextc(p); } else if (c == '+') { c = nextc(p); } if (c == '0') { #define no_digits() do {yyerror(NULL, p,"numeric literal without digits"); return 0;} while (0) int start = toklen(p); c = nextc(p); if (c == 'x' || c == 'X') { /* hexadecimal */ c = nextc(p); if (c >= 0 && ISXDIGIT(c)) { do { if (c == '_') { if (nondigit) break; nondigit = c; continue; } if (!ISXDIGIT(c)) break; nondigit = 0; tokadd(p, tolower(c)); } while ((c = nextc(p)) >= 0); } pushback(p, c); tokfix(p); if (toklen(p) == start) { no_digits(); } else if (nondigit) goto trailing_uc; suffix = number_literal_suffix(p); pylval.nd = new_int(p, tok(p), 16, suffix); return tINTEGER; } if (c == 'b' || c == 'B') { /* binary */ c = nextc(p); if (c == '0' || c == '1') { do { if (c == '_') { if (nondigit) break; nondigit = c; continue; } if (c != '0' && c != '1') break; nondigit = 0; tokadd(p, c); } while ((c = nextc(p)) >= 0); } pushback(p, c); tokfix(p); if (toklen(p) == start) { no_digits(); } else if (nondigit) goto trailing_uc; suffix = number_literal_suffix(p); pylval.nd = new_int(p, tok(p), 2, suffix); return tINTEGER; } if (c == 'd' || c == 'D') { /* decimal */ c = nextc(p); if (c >= 0 && ISDIGIT(c)) { do { if (c == '_') { if (nondigit) break; nondigit = c; continue; } if (!ISDIGIT(c)) break; nondigit = 0; tokadd(p, c); } while ((c = nextc(p)) >= 0); } pushback(p, c); tokfix(p); if (toklen(p) == start) { no_digits(); } else if (nondigit) goto trailing_uc; suffix = number_literal_suffix(p); pylval.nd = new_int(p, tok(p), 10, suffix); return tINTEGER; } if (c == '_') { /* 0_0 */ goto octal_number; } if (c == 'o' || c == 'O') { /* prefixed octal */ c = nextc(p); if (c < 0 || c == '_' || !ISDIGIT(c)) { no_digits(); } } if (c >= '0' && c <= '7') { /* octal */ octal_number: do { if (c == '_') { if (nondigit) break; nondigit = c; continue; } if (c < '0' || c > '9') break; if (c > '7') goto invalid_octal; nondigit = 0; tokadd(p, c); } while ((c = nextc(p)) >= 0); if (toklen(p) > start) { pushback(p, c); tokfix(p); if (nondigit) goto trailing_uc; suffix = number_literal_suffix(p); pylval.nd = new_int(p, tok(p), 8, suffix); return tINTEGER; } if (nondigit) { pushback(p, c); goto trailing_uc; } } if (c > '7' && c <= '9') { invalid_octal: yyerror(NULL, p, "Invalid octal digit"); } else if (c == '.' || c == 'e' || c == 'E') { tokadd(p, '0'); } else { pushback(p, c); suffix = number_literal_suffix(p); pylval.nd = new_int(p, "0", 10, suffix); return tINTEGER; } } for (;;) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': nondigit = 0; tokadd(p, c); break; case '.': if (nondigit) goto trailing_uc; if (seen_point || seen_e) { goto decode_num; } else { int c0 = nextc(p); if (c0 < 0 || !ISDIGIT(c0)) { pushback(p, c0); goto decode_num; } c = c0; } tokadd(p, '.'); tokadd(p, c); is_float++; seen_point++; nondigit = 0; break; case 'e': case 'E': if (nondigit) { pushback(p, c); c = nondigit; goto decode_num; } if (seen_e) { goto decode_num; } tokadd(p, c); seen_e++; is_float++; nondigit = c; c = nextc(p); if (c != '-' && c != '+') continue; tokadd(p, c); nondigit = c; break; case '_': /* '_' in number just ignored */ if (nondigit) goto decode_num; nondigit = c; break; default: goto decode_num; } c = nextc(p); } decode_num: pushback(p, c); if (nondigit) { trailing_uc: yyerror_c(p, "trailing non digit in number: ", (char)nondigit); } tokfix(p); if (is_float) { #ifdef MRB_NO_FLOAT yywarning_s(p, "floating-point numbers are not supported", tok(p)); pylval.nd = new_int(p, "0", 10, 0); return tINTEGER; #else double d; if (!mrb_read_float(tok(p), NULL, &d)) { yywarning_s(p, "corrupted float value", tok(p)); } suffix = number_literal_suffix(p); if (seen_e && (suffix & NUM_SUFFIX_R)) { pushback(p, 'r'); suffix &= ~NUM_SUFFIX_R; } pylval.nd = new_float(p, tok(p), suffix); return tFLOAT; #endif } suffix = number_literal_suffix(p); pylval.nd = new_int(p, tok(p), 10, suffix); return tINTEGER; } case ')': case ']': p->paren_nest--; /* fall through */ case '}': COND_LEXPOP(); CMDARG_LEXPOP(); if (c == ')') p->lstate = EXPR_ENDFN; else p->lstate = EXPR_END; return c; case ':': c = nextc(p); if (c == ':') { if (IS_BEG() || p->lstate == EXPR_CLASS || IS_SPCARG(-1)) { p->lstate = EXPR_BEG; return tCOLON3; } p->lstate = EXPR_DOT; return tCOLON2; } if (!space_seen && IS_END()) { pushback(p, c); p->lstate = EXPR_BEG; return tLABEL_TAG; } if (IS_END() || ISSPACE(c) || c == '#') { pushback(p, c); p->lstate = EXPR_BEG; return ':'; } pushback(p, c); p->lstate = EXPR_FNAME; return tSYMBEG; case '/': if (IS_BEG()) { p->lex_strterm = new_strterm(p, str_regexp, '/', 0); return tREGEXP_BEG; } if ((c = nextc(p)) == '=') { pylval.id = intern_op(div); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); if (IS_SPCARG(c)) { p->lex_strterm = new_strterm(p, str_regexp, '/', 0); return tREGEXP_BEG; } if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; } else { p->lstate = EXPR_BEG; } return '/'; case '^': if ((c = nextc(p)) == '=') { pylval.id = intern_op(xor); p->lstate = EXPR_BEG; return tOP_ASGN; } if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; } else { p->lstate = EXPR_BEG; } pushback(p, c); return '^'; case ';': p->lstate = EXPR_BEG; return ';'; case ',': p->lstate = EXPR_BEG; return ','; case '~': if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { if ((c = nextc(p)) != '@') { pushback(p, c); } p->lstate = EXPR_ARG; } else { p->lstate = EXPR_BEG; } return '~'; case '(': if (IS_BEG()) { c = tLPAREN; } else if (IS_SPCARG(-1)) { c = tLPAREN_ARG; } else if (p->lstate == EXPR_END && space_seen) { c = tLPAREN_ARG; } p->paren_nest++; COND_PUSH(0); CMDARG_PUSH(0); p->lstate = EXPR_BEG; return c; case '[': p->paren_nest++; if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; p->paren_nest--; if ((c = nextc(p)) == ']') { if ((c = nextc(p)) == '=') { return tASET; } pushback(p, c); return tAREF; } pushback(p, c); return '['; } else if (IS_BEG()) { c = tLBRACK; } else if (IS_ARG() && space_seen) { c = tLBRACK; } p->lstate = EXPR_BEG; COND_PUSH(0); CMDARG_PUSH(0); return c; case '{': if (p->lpar_beg && p->lpar_beg == p->paren_nest) { p->lstate = EXPR_BEG; p->lpar_beg = 0; p->paren_nest--; COND_PUSH(0); CMDARG_PUSH(0); return tLAMBEG; } if (IS_ARG() || p->lstate == EXPR_END || p->lstate == EXPR_ENDFN) c = '{'; /* block (primary) */ else if (p->lstate == EXPR_ENDARG) c = tLBRACE_ARG; /* block (expr) */ else c = tLBRACE; /* hash */ COND_PUSH(0); CMDARG_PUSH(0); p->lstate = EXPR_BEG; return c; case '\\': c = nextc(p); if (c == '\n') { p->lineno+=nlines; nlines=1; p->column = 0; space_seen = 1; goto retry; /* skip \\n */ } pushback(p, c); return '\\'; case '%': if (IS_BEG()) { int term; int paren; c = nextc(p); quotation: if (c < 0 || !ISALNUM(c)) { term = c; c = 'Q'; } else { term = nextc(p); if (ISALNUM(term)) { yyerror(NULL, p, "unknown type of %string"); return 0; } } if (c < 0 || term < 0) { yyerror(NULL, p, "unterminated quoted string meets end of file"); return 0; } paren = term; if (term == '(') term = ')'; else if (term == '[') term = ']'; else if (term == '{') term = '}'; else if (term == '<') term = '>'; else paren = 0; switch (c) { case 'Q': p->lex_strterm = new_strterm(p, str_dquote, term, paren); return tSTRING_BEG; case 'q': p->lex_strterm = new_strterm(p, str_squote, term, paren); return parse_string(p); case 'W': p->lex_strterm = new_strterm(p, str_dword, term, paren); return tWORDS_BEG; case 'w': p->lex_strterm = new_strterm(p, str_sword, term, paren); return tWORDS_BEG; case 'x': p->lex_strterm = new_strterm(p, str_xquote, term, paren); return tXSTRING_BEG; case 'r': p->lex_strterm = new_strterm(p, str_regexp, term, paren); return tREGEXP_BEG; case 's': p->lex_strterm = new_strterm(p, str_ssym, term, paren); return tSYMBEG; case 'I': p->lex_strterm = new_strterm(p, str_dsymbols, term, paren); return tSYMBOLS_BEG; case 'i': p->lex_strterm = new_strterm(p, str_ssymbols, term, paren); return tSYMBOLS_BEG; default: yyerror(NULL, p, "unknown type of %string"); return 0; } } if ((c = nextc(p)) == '=') { pylval.id = intern_op(mod); p->lstate = EXPR_BEG; return tOP_ASGN; } if (IS_SPCARG(c)) { goto quotation; } if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; } else { p->lstate = EXPR_BEG; } pushback(p, c); return '%'; case '$': p->lstate = EXPR_END; token_column = newtok(p); c = nextc(p); if (c < 0) { yyerror(NULL, p, "incomplete global variable syntax"); return 0; } switch (c) { case '_': /* $_: last read line string */ c = nextc(p); if (c >= 0 && identchar(c)) { /* if there is more after _ it is a variable */ tokadd(p, '$'); tokadd(p, c); break; } pushback(p, c); c = '_'; /* fall through */ case '~': /* $~: match-data */ case '*': /* $*: argv */ case '$': /* $$: pid */ case '?': /* $?: last status */ case '!': /* $!: error string */ case '@': /* $@: error position */ case '/': /* $/: input record separator */ case '\\': /* $\: output record separator */ case ';': /* $;: field separator */ case ',': /* $,: output field separator */ case '.': /* $.: last read line number */ case '=': /* $=: ignorecase */ case ':': /* $:: load path */ case '<': /* $<: reading filename */ case '>': /* $>: default output handle */ case '\"': /* $": already loaded files */ tokadd(p, '$'); tokadd(p, c); tokfix(p); pylval.id = intern(tok(p), toklen(p)); return tGVAR; case '-': tokadd(p, '$'); tokadd(p, c); c = nextc(p); pushback(p, c); gvar: tokfix(p); pylval.id = intern(tok(p), toklen(p)); return tGVAR; case '&': /* $&: last match */ case '`': /* $`: string before last match */ case '\'': /* $': string after last match */ case '+': /* $+: string matches last pattern */ if (last_state == EXPR_FNAME) { tokadd(p, '$'); tokadd(p, c); goto gvar; } pylval.nd = new_back_ref(p, c); return tBACK_REF; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': do { tokadd(p, c); c = nextc(p); } while (c >= 0 && ISDIGIT(c)); pushback(p, c); if (last_state == EXPR_FNAME) goto gvar; tokfix(p); { mrb_int n; if (!mrb_read_int(tok(p), NULL, NULL, &n)) { yywarning(p, "capture group index too big; always nil"); return keyword_nil; } pylval.nd = new_nth_ref(p, (int)n); } return tNTH_REF; default: if (!identchar(c)) { pushback(p, c); return '$'; } /* fall through */ case '0': tokadd(p, '$'); } break; case '@': c = nextc(p); token_column = newtok(p); tokadd(p, '@'); if (c == '@') { tokadd(p, '@'); c = nextc(p); } if (c < 0) { if (p->tidx == 1) { yyerror(NULL, p, "incomplete instance variable syntax"); } else { yyerror(NULL, p, "incomplete class variable syntax"); } return 0; } else if (ISDIGIT(c)) { if (p->tidx == 1) { yyerror_c(p, "wrong instance variable name: @", c); } else { yyerror_c(p, "wrong class variable name: @@", c); } return 0; } if (!identchar(c)) { pushback(p, c); return '@'; } break; case '_': token_column = newtok(p); break; default: if (!identchar(c)) { char buf[36]; const char s[] = "Invalid char in expression: 0x"; const char hexdigits[] = "0123456789ABCDEF"; strcpy(buf, s); buf[sizeof(s)-1] = hexdigits[(c & 0xf0) >> 4]; buf[sizeof(s)] = hexdigits[(c & 0x0f)]; buf[sizeof(s)+1] = 0; yyerror(NULL, p, buf); goto retry; } token_column = newtok(p); break; } do { tokadd(p, c); c = nextc(p); if (c < 0) break; } while (identchar(c)); if (token_column == 0 && toklen(p) == 7 && (c < 0 || c == '\n') && strncmp(tok(p), "__END__", toklen(p)) == 0) return -1; switch (tok(p)[0]) { case '@': case '$': pushback(p, c); break; default: if ((c == '!' || c == '?') && !peek(p, '=')) { tokadd(p, c); } else { pushback(p, c); } } tokfix(p); { int result = 0; switch (tok(p)[0]) { case '$': p->lstate = EXPR_END; result = tGVAR; break; case '@': p->lstate = EXPR_END; if (tok(p)[1] == '@') result = tCVAR; else result = tIVAR; break; case '_': if (toklen(p) == 2 && ISDIGIT(tok(p)[1]) && p->nvars) { int n = tok(p)[1] - '0'; int nvar; if (n > 0) { nvar = intn(p->nvars->car); if (nvar != -2) { /* numbered parameters never appear on toplevel */ pylval.num = n; p->lstate = EXPR_END; return tNUMPARAM; } } } /* fall through */ default: if (toklast(p) == '!' || toklast(p) == '?') { result = tFID; } else { if (p->lstate == EXPR_FNAME) { if ((c = nextc(p)) == '=' && !peek(p, '~') && !peek(p, '>') && (!peek(p, '=') || (peek_n(p, '>', 1)))) { result = tIDENTIFIER; tokadd(p, c); tokfix(p); } else { pushback(p, c); } if ((c = nextc(p)) == '=' && !peek(p, '~') && !peek(p, '>') && (!peek(p, '=') || (peek_n(p, '>', 1)))) { result = tIDENTIFIER; tokadd(p, c); tokfix(p); } else { pushback(p, c); } } if (result == 0 && ISUPPER(tok(p)[0])) { result = tCONSTANT; } else { result = tIDENTIFIER; } } if (IS_LABEL_POSSIBLE()) { if (IS_LABEL_SUFFIX(0)) { p->lstate = EXPR_END; tokfix(p); pylval.id = intern(tok(p), toklen(p)); return tIDENTIFIER; } } if (p->lstate != EXPR_DOT) { const struct kwtable *kw; /* See if it is a reserved word. */ kw = mrb_reserved_word(tok(p), toklen(p)); if (kw) { enum mrb_lex_state_enum state = p->lstate; pylval.num = p->lineno; p->lstate = kw->state; if (state == EXPR_FNAME) { pylval.id = intern_cstr(kw->name); return kw->id[0]; } if (p->lstate == EXPR_BEG) { p->cmd_start = TRUE; } if (kw->id[0] == keyword_do) { if (p->lpar_beg && p->lpar_beg == p->paren_nest) { p->lpar_beg = 0; p->paren_nest--; return keyword_do_LAMBDA; } if (COND_P()) return keyword_do_cond; if (CMDARG_P() && state != EXPR_CMDARG) return keyword_do_block; if (state == EXPR_ENDARG || state == EXPR_BEG) return keyword_do_block; return keyword_do; } if (state == EXPR_BEG || state == EXPR_VALUE || state == EXPR_CLASS) return kw->id[0]; else { if (kw->id[0] != kw->id[1]) p->lstate = EXPR_BEG; return kw->id[1]; } } } if (IS_BEG() || p->lstate == EXPR_DOT || IS_ARG()) { if (cmd_state) { p->lstate = EXPR_CMDARG; } else { p->lstate = EXPR_ARG; } } else if (p->lstate == EXPR_FNAME) { p->lstate = EXPR_ENDFN; } else { p->lstate = EXPR_END; } } { mrb_sym ident = intern(tok(p), toklen(p)); pylval.id = ident; if (last_state != EXPR_DOT && ISLOWER(tok(p)[0]) && local_var_p(p, ident)) { p->lstate = EXPR_END; } } return result; } } static int yylex(void *lval, void *lp, parser_state *p) { p->ylval = lval; return parser_yylex(p); } static void parser_init_cxt(parser_state *p, mrb_ccontext *cxt) { if (!cxt) return; if (cxt->filename) mrb_parser_set_filename(p, cxt->filename); if (cxt->lineno) p->lineno = cxt->lineno; if (cxt->syms) { int i; p->locals = cons(0,0); for (i=0; islen; i++) { local_add_f(p, cxt->syms[i]); } } p->capture_errors = cxt->capture_errors; p->no_optimize = cxt->no_optimize; p->no_ext_ops = cxt->no_ext_ops; p->upper = cxt->upper; if (cxt->partial_hook) { p->cxt = cxt; } } static void parser_update_cxt(parser_state *p, mrb_ccontext *cxt) { node *n, *n0; int i = 0; if (!cxt) return; if (!p->tree) return; if (intn(p->tree->car) != NODE_SCOPE) return; n0 = n = p->tree->cdr->car; while (n) { i++; n = n->cdr; } cxt->syms = (mrb_sym*)mrbc_realloc(cxt->syms, i*sizeof(mrb_sym)); cxt->slen = i; for (i=0, n=n0; n; i++,n=n->cdr) { cxt->syms[i] = sym(n->car); } } void mrb_parser_dump(mrb_state *mrb, node *tree, int offset); MRB_API void mrb_parser_parse(parser_state *p, mrb_ccontext *c) { struct mrb_jmpbuf buf1; struct mrb_jmpbuf *prev = p->mrb->jmp; p->mrb->jmp = &buf1; MRB_TRY(p->mrb->jmp) { int n = 1; p->cmd_start = TRUE; p->in_def = p->in_single = 0; p->nerr = p->nwarn = 0; p->lex_strterm = NULL; parser_init_cxt(p, c); n = yyparse(p); if (n != 0 || p->nerr > 0) { p->tree = 0; p->mrb->jmp = prev; return; } parser_update_cxt(p, c); if (c && c->dump_result) { mrb_parser_dump(p->mrb, p->tree, 0); } } MRB_CATCH(p->mrb->jmp) { p->nerr++; if (p->mrb->exc == NULL) { yyerror(NULL, p, "memory allocation error"); p->nerr++; p->tree = 0; } } MRB_END_EXC(p->jmp); p->mrb->jmp = prev; } MRB_API parser_state* mrb_parser_new(mrb_state *mrb) { mempool *pool; parser_state *p; static const parser_state parser_state_zero = { 0 }; pool = mempool_open(); if (!pool) return NULL; p = (parser_state*)mempool_alloc(pool, sizeof(parser_state)); if (!p) return NULL; *p = parser_state_zero; p->mrb = mrb; p->pool = pool; p->s = p->send = NULL; #ifndef MRB_NO_STDIO p->f = NULL; #endif p->cmd_start = TRUE; p->in_def = p->in_single = 0; p->capture_errors = FALSE; p->lineno = 1; p->column = 0; #if defined(PARSER_TEST) || defined(PARSER_DEBUG) yydebug = 1; #endif p->tsiz = MRB_PARSER_TOKBUF_SIZE; p->tokbuf = p->buf; p->lex_strterm = NULL; p->current_filename_index = -1; p->filename_table = NULL; p->filename_table_length = 0; return p; } MRB_API void mrb_parser_free(parser_state *p) { if (p->tokbuf != p->buf) { mrbc_free(p->tokbuf); } mempool_close(p->pool); } MRB_API mrb_ccontext* mrb_ccontext_new(mrb_state *mrb) { static const mrb_ccontext cc_zero = { 0 }; mrb_ccontext *cc = (mrb_ccontext*)mrbc_malloc(sizeof(mrb_ccontext)); *cc = cc_zero; return cc; } MRB_API void mrb_ccontext_free(mrb_state *mrb, mrb_ccontext *cxt) { mrbc_free(cxt->filename); mrbc_free(cxt->syms); mrbc_free(cxt); } MRB_API const char* mrb_ccontext_filename(mrb_state *mrb, mrb_ccontext *c, const char *s) { if (s) { size_t len = strlen(s); char *p = (char*)mrbc_malloc(len + 1); if (p == NULL) return NULL; memcpy(p, s, len + 1); if (c->filename) { mrbc_free(c->filename); } c->filename = p; } return c->filename; } MRB_API void mrb_ccontext_partial_hook(mrb_ccontext *c, int (*func)(struct mrb_parser_state*), void *data) { c->partial_hook = func; c->partial_data = data; } MRB_API void mrb_ccontext_cleanup_local_variables(mrb_ccontext *c) { if (c->syms) { mrbc_free(c->syms); c->syms = NULL; c->slen = 0; } c->keep_lv = FALSE; } MRB_API void mrb_parser_set_filename(struct mrb_parser_state *p, const char *f) { mrb_sym sym; uint16_t i; mrb_sym* new_table; sym = mrb_intern_cstr(p->mrb, f); p->filename_sym = sym; p->lineno = (p->filename_table_length > 0)? 0 : 1; for (i = 0; i < p->filename_table_length; i++) { if (p->filename_table[i] == sym) { p->current_filename_index = i; return; } } if (p->filename_table_length == UINT16_MAX) { yyerror(NULL, p, "too many files to compile"); return; } p->current_filename_index = p->filename_table_length++; new_table = (mrb_sym*)parser_palloc(p, sizeof(mrb_sym) * p->filename_table_length); if (p->filename_table) { memmove(new_table, p->filename_table, sizeof(mrb_sym) * p->current_filename_index); } p->filename_table = new_table; p->filename_table[p->filename_table_length - 1] = sym; } MRB_API mrb_sym mrb_parser_get_filename(struct mrb_parser_state* p, uint16_t idx) { if (idx >= p->filename_table_length) return 0; else { return p->filename_table[idx]; } } #ifndef MRB_NO_STDIO static struct mrb_parser_state * mrb_parse_file_continue(mrb_state *mrb, FILE *f, const void *prebuf, size_t prebufsize, mrb_ccontext *c) { parser_state *p; p = mrb_parser_new(mrb); if (!p) return NULL; if (prebuf) { p->s = (const char*)prebuf; p->send = (const char*)prebuf + prebufsize; } else { p->s = p->send = NULL; } p->f = f; mrb_parser_parse(p, c); return p; } MRB_API parser_state* mrb_parse_file(mrb_state *mrb, FILE *f, mrb_ccontext *c) { return mrb_parse_file_continue(mrb, f, NULL, 0, c); } #endif MRB_API parser_state* mrb_parse_nstring(mrb_state *mrb, const char *s, size_t len, mrb_ccontext *c) { parser_state *p; p = mrb_parser_new(mrb); if (!p) return NULL; p->s = s; p->send = s + len; mrb_parser_parse(p, c); return p; } MRB_API parser_state* mrb_parse_string(mrb_state *mrb, const char *s, mrb_ccontext *c) { return mrb_parse_nstring(mrb, s, strlen(s), c); } MRB_API mrb_value mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrb_ccontext *c) { struct RClass *target = mrb->object_class; struct RProc *proc; mrb_value v; mrb_int keep = 0; if (!p) { return mrb_undef_value(); } if (!p->tree || p->nerr) { if (c) c->parser_nerr = p->nerr; if (p->capture_errors) { char buf[256]; strcpy(buf, "line "); dump_int(p->error_buffer[0].lineno, buf+5); strcat(buf, ": "); strncat(buf, p->error_buffer[0].message, sizeof(buf) - strlen(buf) - 1); mrb->exc = mrb_obj_ptr(mrb_exc_new(mrb, E_SYNTAX_ERROR, buf, strlen(buf))); mrb_parser_free(p); return mrb_undef_value(); } else { if (mrb->exc == NULL) { mrb->exc = mrb_obj_ptr(mrb_exc_new_lit(mrb, E_SYNTAX_ERROR, "syntax error")); } mrb_parser_free(p); return mrb_undef_value(); } } proc = mrb_generate_code(mrb, p); mrb_parser_free(p); if (proc == NULL) { if (mrb->exc == NULL) { mrb->exc = mrb_obj_ptr(mrb_exc_new_lit(mrb, E_SCRIPT_ERROR, "codegen error")); } return mrb_undef_value(); } if (c) { if (c->dump_result) mrb_codedump_all(mrb, proc); if (c->no_exec) return mrb_obj_value(proc); if (c->target_class) { target = c->target_class; } if (c->keep_lv) { keep = c->slen + 1; } else { c->keep_lv = TRUE; } } MRB_PROC_SET_TARGET_CLASS(proc, target); if (mrb->c->ci) { mrb_vm_ci_target_class_set(mrb->c->ci, target); } v = mrb_top_run(mrb, proc, mrb_top_self(mrb), keep); if (mrb->exc) return mrb_nil_value(); return v; } #ifndef MRB_NO_STDIO MRB_API mrb_value mrb_load_file_cxt(mrb_state *mrb, FILE *f, mrb_ccontext *c) { return mrb_load_exec(mrb, mrb_parse_file(mrb, f, c), c); } MRB_API mrb_value mrb_load_file(mrb_state *mrb, FILE *f) { return mrb_load_file_cxt(mrb, f, NULL); } #define DETECT_SIZE 64 /* * In order to be recognized as a `.mrb` file, the following three points must be satisfied: * - File starts with "RITE" * - At least `sizeof(struct rite_binary_header)` bytes can be read * - `NUL` is included in the first 64 bytes of the file */ MRB_API mrb_value mrb_load_detect_file_cxt(mrb_state *mrb, FILE *fp, mrb_ccontext *c) { union { char b[DETECT_SIZE]; struct rite_binary_header h; } leading; size_t bufsize; if (mrb == NULL || fp == NULL) { return mrb_nil_value(); } bufsize = fread(leading.b, sizeof(char), sizeof(leading), fp); if (bufsize < sizeof(leading.h) || memcmp(leading.h.binary_ident, RITE_BINARY_IDENT, sizeof(leading.h.binary_ident)) != 0 || memchr(leading.b, '\0', bufsize) == NULL) { return mrb_load_exec(mrb, mrb_parse_file_continue(mrb, fp, leading.b, bufsize, c), c); } else { mrb_int binsize; uint8_t *bin; mrb_value bin_obj = mrb_nil_value(); /* temporary string object */ mrb_value result; binsize = bin_to_uint32(leading.h.binary_size); bin_obj = mrb_str_new(mrb, NULL, binsize); bin = (uint8_t*)RSTRING_PTR(bin_obj); if ((size_t)binsize > bufsize) { memcpy(bin, leading.b, bufsize); if (fread(bin + bufsize, binsize - bufsize, 1, fp) == 0) { binsize = bufsize; /* The error is reported by mrb_load_irep_buf_cxt() */ } } result = mrb_load_irep_buf_cxt(mrb, bin, binsize, c); if (mrb_string_p(bin_obj)) mrb_str_resize(mrb, bin_obj, 0); return result; } } #endif MRB_API mrb_value mrb_load_nstring_cxt(mrb_state *mrb, const char *s, size_t len, mrb_ccontext *c) { return mrb_load_exec(mrb, mrb_parse_nstring(mrb, s, len, c), c); } MRB_API mrb_value mrb_load_nstring(mrb_state *mrb, const char *s, size_t len) { return mrb_load_nstring_cxt(mrb, s, len, NULL); } MRB_API mrb_value mrb_load_string_cxt(mrb_state *mrb, const char *s, mrb_ccontext *c) { return mrb_load_nstring_cxt(mrb, s, strlen(s), c); } MRB_API mrb_value mrb_load_string(mrb_state *mrb, const char *s) { return mrb_load_string_cxt(mrb, s, NULL); } #ifndef MRB_NO_STDIO static void dump_prefix(node *tree, int offset) { printf("%05d ", tree->lineno); while (offset--) { putc(' ', stdout); putc(' ', stdout); } } static void dump_recur(mrb_state *mrb, node *tree, int offset) { while (tree) { mrb_parser_dump(mrb, tree->car, offset); tree = tree->cdr; } } static void dump_args(mrb_state *mrb, node *n, int offset) { if (n->car) { dump_prefix(n, offset+1); printf("mandatory args:\n"); dump_recur(mrb, n->car, offset+2); } n = n->cdr; if (n->car) { dump_prefix(n, offset+1); printf("optional args:\n"); { node *n2 = n->car; while (n2) { dump_prefix(n2, offset+2); printf("%s=\n", mrb_sym_name(mrb, sym(n2->car->car))); mrb_parser_dump(mrb, n2->car->cdr, offset+3); n2 = n2->cdr; } } } n = n->cdr; if (n->car) { mrb_sym rest = sym(n->car); dump_prefix(n, offset+1); if (rest == MRB_OPSYM(mul)) printf("rest=*\n"); else printf("rest=*%s\n", mrb_sym_name(mrb, rest)); } n = n->cdr; if (n->car) { dump_prefix(n, offset+1); printf("post mandatory args:\n"); dump_recur(mrb, n->car, offset+2); } n = n->cdr; if (n) { mrb_assert(intn(n->car) == NODE_ARGS_TAIL); mrb_parser_dump(mrb, n, offset); } } /* * This function restores the GC arena on return. * For this reason, if a process that further generates an object is * performed at the caller, the string pointer returned as the return * value may become invalid. */ static const char* str_dump(mrb_state *mrb, const char *str, int len) { int ai = mrb_gc_arena_save(mrb); mrb_value s; # if INT_MAX > MRB_INT_MAX / 4 /* check maximum length with "\xNN" character */ if (len > MRB_INT_MAX / 4) { len = MRB_INT_MAX / 4; } # endif s = mrb_str_new(mrb, str, (mrb_int)len); s = mrb_str_dump(mrb, s); mrb_gc_arena_restore(mrb, ai); return RSTRING_PTR(s); } #endif void mrb_parser_dump(mrb_state *mrb, node *tree, int offset) { #ifndef MRB_NO_STDIO int nodetype; if (!tree) return; again: dump_prefix(tree, offset); nodetype = intn(tree->car); tree = tree->cdr; switch (nodetype) { case NODE_BEGIN: printf("NODE_BEGIN:\n"); dump_recur(mrb, tree, offset+1); break; case NODE_RESCUE: printf("NODE_RESCUE:\n"); if (tree->car) { dump_prefix(tree, offset+1); printf("body:\n"); mrb_parser_dump(mrb, tree->car, offset+2); } tree = tree->cdr; if (tree->car) { node *n2 = tree->car; dump_prefix(n2, offset+1); printf("rescue:\n"); while (n2) { node *n3 = n2->car; if (n3->car) { dump_prefix(n2, offset+2); printf("handle classes:\n"); dump_recur(mrb, n3->car, offset+3); } if (n3->cdr->car) { dump_prefix(n3, offset+2); printf("exc_var:\n"); mrb_parser_dump(mrb, n3->cdr->car, offset+3); } if (n3->cdr->cdr->car) { dump_prefix(n3, offset+2); printf("rescue body:\n"); mrb_parser_dump(mrb, n3->cdr->cdr->car, offset+3); } n2 = n2->cdr; } } tree = tree->cdr; if (tree->car) { dump_prefix(tree, offset+1); printf("else:\n"); mrb_parser_dump(mrb, tree->car, offset+2); } break; case NODE_ENSURE: printf("NODE_ENSURE:\n"); dump_prefix(tree, offset+1); printf("body:\n"); mrb_parser_dump(mrb, tree->car, offset+2); dump_prefix(tree, offset+1); printf("ensure:\n"); mrb_parser_dump(mrb, tree->cdr->cdr, offset+2); break; case NODE_LAMBDA: printf("NODE_LAMBDA:\n"); dump_prefix(tree, offset+1); goto block; case NODE_BLOCK: block: printf("NODE_BLOCK:\n"); tree = tree->cdr; if (tree->car) { dump_args(mrb, tree->car, offset+1); } dump_prefix(tree, offset+1); printf("body:\n"); mrb_parser_dump(mrb, tree->cdr->car, offset+2); break; case NODE_IF: printf("NODE_IF:\n"); dump_prefix(tree, offset+1); printf("cond:\n"); mrb_parser_dump(mrb, tree->car, offset+2); dump_prefix(tree, offset+1); printf("then:\n"); mrb_parser_dump(mrb, tree->cdr->car, offset+2); if (tree->cdr->cdr->car) { dump_prefix(tree, offset+1); printf("else:\n"); mrb_parser_dump(mrb, tree->cdr->cdr->car, offset+2); } break; case NODE_AND: printf("NODE_AND:\n"); mrb_parser_dump(mrb, tree->car, offset+1); mrb_parser_dump(mrb, tree->cdr, offset+1); break; case NODE_OR: printf("NODE_OR:\n"); mrb_parser_dump(mrb, tree->car, offset+1); mrb_parser_dump(mrb, tree->cdr, offset+1); break; case NODE_CASE: printf("NODE_CASE:\n"); if (tree->car) { mrb_parser_dump(mrb, tree->car, offset+1); } tree = tree->cdr; while (tree) { dump_prefix(tree, offset+1); printf("case:\n"); dump_recur(mrb, tree->car->car, offset+2); dump_prefix(tree, offset+1); printf("body:\n"); mrb_parser_dump(mrb, tree->car->cdr, offset+2); tree = tree->cdr; } break; case NODE_WHILE: printf("NODE_WHILE:\n"); dump_prefix(tree, offset+1); printf("cond:\n"); mrb_parser_dump(mrb, tree->car, offset+2); dump_prefix(tree, offset+1); printf("body:\n"); mrb_parser_dump(mrb, tree->cdr, offset+2); break; case NODE_UNTIL: printf("NODE_UNTIL:\n"); dump_prefix(tree, offset+1); printf("cond:\n"); mrb_parser_dump(mrb, tree->car, offset+2); dump_prefix(tree, offset+1); printf("body:\n"); mrb_parser_dump(mrb, tree->cdr, offset+2); break; case NODE_FOR: printf("NODE_FOR:\n"); dump_prefix(tree, offset+1); printf("var:\n"); { node *n2 = tree->car; if (n2->car) { dump_prefix(n2, offset+2); printf("pre:\n"); dump_recur(mrb, n2->car, offset+3); } n2 = n2->cdr; if (n2) { if (n2->car) { dump_prefix(n2, offset+2); printf("rest:\n"); mrb_parser_dump(mrb, n2->car, offset+3); } n2 = n2->cdr; if (n2) { if (n2->car) { dump_prefix(n2, offset+2); printf("post:\n"); dump_recur(mrb, n2->car, offset+3); } } } } tree = tree->cdr; dump_prefix(tree, offset+1); printf("in:\n"); mrb_parser_dump(mrb, tree->car, offset+2); tree = tree->cdr; dump_prefix(tree, offset+1); printf("do:\n"); mrb_parser_dump(mrb, tree->car, offset+2); break; case NODE_SCOPE: printf("NODE_SCOPE:\n"); { node *n2 = tree->car; mrb_bool first_lval = TRUE; if (n2 && (n2->car || n2->cdr)) { dump_prefix(n2, offset+1); printf("local variables:\n"); dump_prefix(n2, offset+2); while (n2) { if (n2->car) { if (!first_lval) printf(", "); printf("%s", mrb_sym_name(mrb, sym(n2->car))); first_lval = FALSE; } n2 = n2->cdr; } printf("\n"); } } tree = tree->cdr; offset++; goto again; case NODE_FCALL: case NODE_CALL: case NODE_SCALL: switch (nodetype) { case NODE_FCALL: printf("NODE_FCALL:\n"); break; case NODE_CALL: printf("NODE_CALL(.):\n"); break; case NODE_SCALL: printf("NODE_SCALL(&.):\n"); break; default: break; } mrb_parser_dump(mrb, tree->car, offset+1); dump_prefix(tree, offset+1); printf("method='%s' (%d)\n", mrb_sym_dump(mrb, sym(tree->cdr->car)), intn(tree->cdr->car)); tree = tree->cdr->cdr->car; if (tree) { dump_prefix(tree, offset+1); printf("args:\n"); dump_recur(mrb, tree->car, offset+2); if (tree->cdr) { if (tree->cdr->car) { dump_prefix(tree, offset+1); printf("kwargs:\n"); mrb_parser_dump(mrb, tree->cdr->car, offset+2); } if (tree->cdr->cdr) { dump_prefix(tree, offset+1); printf("block:\n"); mrb_parser_dump(mrb, tree->cdr->cdr, offset+2); } } } break; case NODE_DOT2: printf("NODE_DOT2:\n"); mrb_parser_dump(mrb, tree->car, offset+1); mrb_parser_dump(mrb, tree->cdr, offset+1); break; case NODE_DOT3: printf("NODE_DOT3:\n"); mrb_parser_dump(mrb, tree->car, offset+1); mrb_parser_dump(mrb, tree->cdr, offset+1); break; case NODE_COLON2: printf("NODE_COLON2:\n"); mrb_parser_dump(mrb, tree->car, offset+1); dump_prefix(tree, offset+1); printf("::%s\n", mrb_sym_name(mrb, sym(tree->cdr))); break; case NODE_COLON3: printf("NODE_COLON3: ::%s\n", mrb_sym_name(mrb, sym(tree))); break; case NODE_ARRAY: printf("NODE_ARRAY:\n"); dump_recur(mrb, tree, offset+1); break; case NODE_HASH: printf("NODE_HASH:\n"); while (tree) { dump_prefix(tree, offset+1); printf("key:\n"); mrb_parser_dump(mrb, tree->car->car, offset+2); dump_prefix(tree, offset+1); printf("value:\n"); mrb_parser_dump(mrb, tree->car->cdr, offset+2); tree = tree->cdr; } break; case NODE_KW_HASH: printf("NODE_KW_HASH:\n"); while (tree) { dump_prefix(tree, offset+1); printf("key:\n"); mrb_parser_dump(mrb, tree->car->car, offset+2); dump_prefix(tree, offset+1); printf("value:\n"); mrb_parser_dump(mrb, tree->car->cdr, offset+2); tree = tree->cdr; } break; case NODE_SPLAT: printf("NODE_SPLAT:\n"); mrb_parser_dump(mrb, tree, offset+1); break; case NODE_ASGN: printf("NODE_ASGN:\n"); dump_prefix(tree, offset+1); printf("lhs:\n"); mrb_parser_dump(mrb, tree->car, offset+2); dump_prefix(tree, offset+1); printf("rhs:\n"); mrb_parser_dump(mrb, tree->cdr, offset+2); break; case NODE_MASGN: printf("NODE_MASGN:\n"); dump_prefix(tree, offset+1); printf("mlhs:\n"); { node *n2 = tree->car; if (n2->car) { dump_prefix(tree, offset+2); printf("pre:\n"); dump_recur(mrb, n2->car, offset+3); } n2 = n2->cdr; if (n2) { if (n2->car) { dump_prefix(n2, offset+2); printf("rest:\n"); if (n2->car == nint(-1)) { dump_prefix(n2, offset+2); printf("(empty)\n"); } else { mrb_parser_dump(mrb, n2->car, offset+3); } } n2 = n2->cdr; if (n2 && n2->car) { dump_prefix(n2, offset+2); printf("post:\n"); dump_recur(mrb, n2->car, offset+3); } } } dump_prefix(tree, offset+1); printf("rhs:\n"); mrb_parser_dump(mrb, tree->cdr, offset+2); break; case NODE_OP_ASGN: printf("NODE_OP_ASGN:\n"); dump_prefix(tree, offset+1); printf("lhs:\n"); mrb_parser_dump(mrb, tree->car, offset+2); tree = tree->cdr; dump_prefix(tree, offset+1); printf("op='%s' (%d)\n", mrb_sym_name(mrb, sym(tree->car)), intn(tree->car)); tree = tree->cdr; mrb_parser_dump(mrb, tree->car, offset+1); break; case NODE_SUPER: printf("NODE_SUPER:\n"); if (tree) { dump_prefix(tree, offset+1); printf("args:\n"); dump_recur(mrb, tree->car, offset+2); if (tree->cdr) { dump_prefix(tree, offset+1); printf("block:\n"); mrb_parser_dump(mrb, tree->cdr, offset+2); } } break; case NODE_ZSUPER: printf("NODE_ZSUPER:\n"); if (tree) { dump_prefix(tree, offset+1); printf("args:\n"); dump_recur(mrb, tree->car, offset+2); if (tree->cdr) { dump_prefix(tree, offset+1); printf("block:\n"); mrb_parser_dump(mrb, tree->cdr, offset+2); } } break; case NODE_RETURN: printf("NODE_RETURN:\n"); mrb_parser_dump(mrb, tree, offset+1); break; case NODE_YIELD: printf("NODE_YIELD:\n"); dump_recur(mrb, tree, offset+1); break; case NODE_BREAK: printf("NODE_BREAK:\n"); mrb_parser_dump(mrb, tree, offset+1); break; case NODE_NEXT: printf("NODE_NEXT:\n"); mrb_parser_dump(mrb, tree, offset+1); break; case NODE_REDO: printf("NODE_REDO\n"); break; case NODE_RETRY: printf("NODE_RETRY\n"); break; case NODE_LVAR: printf("NODE_LVAR %s\n", mrb_sym_name(mrb, sym(tree))); break; case NODE_GVAR: printf("NODE_GVAR %s\n", mrb_sym_name(mrb, sym(tree))); break; case NODE_IVAR: printf("NODE_IVAR %s\n", mrb_sym_name(mrb, sym(tree))); break; case NODE_CVAR: printf("NODE_CVAR %s\n", mrb_sym_name(mrb, sym(tree))); break; case NODE_NVAR: printf("NODE_NVAR %d\n", intn(tree)); break; case NODE_CONST: printf("NODE_CONST %s\n", mrb_sym_name(mrb, sym(tree))); break; case NODE_MATCH: printf("NODE_MATCH:\n"); dump_prefix(tree, offset + 1); printf("lhs:\n"); mrb_parser_dump(mrb, tree->car, offset + 2); dump_prefix(tree, offset + 1); printf("rhs:\n"); mrb_parser_dump(mrb, tree->cdr, offset + 2); break; case NODE_BACK_REF: printf("NODE_BACK_REF: $%c\n", intn(tree)); break; case NODE_NTH_REF: printf("NODE_NTH_REF: $%d\n", intn(tree)); break; case NODE_ARG: printf("NODE_ARG %s\n", mrb_sym_name(mrb, sym(tree))); break; case NODE_BLOCK_ARG: printf("NODE_BLOCK_ARG:\n"); mrb_parser_dump(mrb, tree, offset+1); break; case NODE_INT: printf("NODE_INT %s base %d\n", (char*)tree->car, intn(tree->cdr->car)); break; case NODE_FLOAT: printf("NODE_FLOAT %s\n", (char*)tree); break; case NODE_NEGATE: printf("NODE_NEGATE:\n"); mrb_parser_dump(mrb, tree, offset+1); break; case NODE_STR: printf("NODE_STR %s len %d\n", str_dump(mrb, (char*)tree->car, intn(tree->cdr)), intn(tree->cdr)); break; case NODE_DSTR: printf("NODE_DSTR:\n"); dump_recur(mrb, tree, offset+1); break; case NODE_XSTR: printf("NODE_XSTR %s len %d\n", str_dump(mrb, (char*)tree->car, intn(tree->cdr)), intn(tree->cdr)); break; case NODE_DXSTR: printf("NODE_DXSTR:\n"); dump_recur(mrb, tree, offset+1); break; case NODE_REGX: printf("NODE_REGX /%s/\n", (char*)tree->car); if (tree->cdr->car) { dump_prefix(tree, offset+1); printf("opt: %s\n", (char*)tree->cdr->car); } if (tree->cdr->cdr) { dump_prefix(tree, offset+1); printf("enc: %s\n", (char*)tree->cdr->cdr); } break; case NODE_DREGX: printf("NODE_DREGX:\n"); dump_recur(mrb, tree->car, offset+1); dump_prefix(tree, offset+1); printf("tail: %s\n", (char*)tree->cdr->cdr->car); if (tree->cdr->cdr->cdr->car) { dump_prefix(tree, offset+1); printf("opt: %s\n", (char*)tree->cdr->cdr->cdr->car); } if (tree->cdr->cdr->cdr->cdr) { dump_prefix(tree, offset+1); printf("enc: %s\n", (char*)tree->cdr->cdr->cdr->cdr); } break; case NODE_SYM: printf("NODE_SYM :%s (%d)\n", mrb_sym_dump(mrb, sym(tree)), intn(tree)); break; case NODE_DSYM: printf("NODE_DSYM:\n"); mrb_parser_dump(mrb, tree, offset+1); break; case NODE_WORDS: printf("NODE_WORDS:\n"); dump_recur(mrb, tree, offset+1); break; case NODE_SYMBOLS: printf("NODE_SYMBOLS:\n"); dump_recur(mrb, tree, offset+1); break; case NODE_LITERAL_DELIM: printf("NODE_LITERAL_DELIM\n"); break; case NODE_SELF: printf("NODE_SELF\n"); break; case NODE_NIL: printf("NODE_NIL\n"); break; case NODE_TRUE: printf("NODE_TRUE\n"); break; case NODE_FALSE: printf("NODE_FALSE\n"); break; case NODE_ALIAS: printf("NODE_ALIAS %s %s:\n", mrb_sym_dump(mrb, sym(tree->car)), mrb_sym_dump(mrb, sym(tree->cdr))); break; case NODE_UNDEF: printf("NODE_UNDEF"); { node *t = tree; while (t) { printf(" %s", mrb_sym_dump(mrb, sym(t->car))); t = t->cdr; } } printf(":\n"); break; case NODE_CLASS: printf("NODE_CLASS:\n"); if (tree->car->car == nint(0)) { dump_prefix(tree, offset+1); printf(":%s\n", mrb_sym_name(mrb, sym(tree->car->cdr))); } else if (tree->car->car == nint(1)) { dump_prefix(tree, offset+1); printf("::%s\n", mrb_sym_name(mrb, sym(tree->car->cdr))); } else { mrb_parser_dump(mrb, tree->car->car, offset+1); dump_prefix(tree, offset+1); printf("::%s\n", mrb_sym_name(mrb, sym(tree->car->cdr))); } if (tree->cdr->car) { dump_prefix(tree, offset+1); printf("super:\n"); mrb_parser_dump(mrb, tree->cdr->car, offset+2); } dump_prefix(tree, offset+1); printf("body:\n"); mrb_parser_dump(mrb, tree->cdr->cdr->car->cdr, offset+2); break; case NODE_MODULE: printf("NODE_MODULE:\n"); if (tree->car->car == nint(0)) { dump_prefix(tree, offset+1); printf(":%s\n", mrb_sym_name(mrb, sym(tree->car->cdr))); } else if (tree->car->car == nint(1)) { dump_prefix(tree, offset+1); printf("::%s\n", mrb_sym_name(mrb, sym(tree->car->cdr))); } else { mrb_parser_dump(mrb, tree->car->car, offset+1); dump_prefix(tree, offset+1); printf("::%s\n", mrb_sym_name(mrb, sym(tree->car->cdr))); } dump_prefix(tree, offset+1); printf("body:\n"); mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2); break; case NODE_SCLASS: printf("NODE_SCLASS:\n"); mrb_parser_dump(mrb, tree->car, offset+1); dump_prefix(tree, offset+1); printf("body:\n"); mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2); break; case NODE_DEF: printf("NODE_DEF:\n"); dump_prefix(tree, offset+1); printf("%s\n", mrb_sym_dump(mrb, sym(tree->car))); tree = tree->cdr; { node *n2 = tree->car; mrb_bool first_lval = TRUE; if (n2 && (n2->car || n2->cdr)) { dump_prefix(n2, offset+1); printf("local variables:\n"); dump_prefix(n2, offset+2); while (n2) { if (n2->car) { if (!first_lval) printf(", "); printf("%s", mrb_sym_name(mrb, sym(n2->car))); first_lval = FALSE; } n2 = n2->cdr; } printf("\n"); } } tree = tree->cdr; if (tree->car) { dump_args(mrb, tree->car, offset); } mrb_parser_dump(mrb, tree->cdr->car, offset+1); break; case NODE_SDEF: printf("NODE_SDEF:\n"); mrb_parser_dump(mrb, tree->car, offset+1); tree = tree->cdr; dump_prefix(tree, offset+1); printf(":%s\n", mrb_sym_dump(mrb, sym(tree->car))); tree = tree->cdr->cdr; if (tree->car) { dump_args(mrb, tree->car, offset+1); } tree = tree->cdr; mrb_parser_dump(mrb, tree->car, offset+1); break; case NODE_POSTEXE: printf("NODE_POSTEXE:\n"); mrb_parser_dump(mrb, tree, offset+1); break; case NODE_HEREDOC: printf("NODE_HEREDOC (<<%s):\n", ((parser_heredoc_info*)tree)->term); dump_recur(mrb, ((parser_heredoc_info*)tree)->doc, offset+1); break; case NODE_ARGS_TAIL: printf("NODE_ARGS_TAIL:\n"); { node *kws = tree->car; while (kws) { mrb_parser_dump(mrb, kws->car, offset+1); kws = kws->cdr; } } tree = tree->cdr; if (tree->car) { mrb_assert(intn(tree->car->car) == NODE_KW_REST_ARGS); mrb_parser_dump(mrb, tree->car, offset+1); } tree = tree->cdr; if (tree->car) { dump_prefix(tree, offset+1); printf("block='%s'\n", mrb_sym_name(mrb, sym(tree->car))); } break; case NODE_KW_ARG: printf("NODE_KW_ARG %s:\n", mrb_sym_name(mrb, sym(tree->car))); mrb_parser_dump(mrb, tree->cdr->car, offset + 1); break; case NODE_KW_REST_ARGS: if (tree) printf("NODE_KW_REST_ARGS %s\n", mrb_sym_name(mrb, sym(tree))); else printf("NODE_KW_REST_ARGS\n"); break; default: printf("node type: %d (0x%x)\n", nodetype, (unsigned)nodetype); break; } #endif } typedef mrb_bool mrb_parser_foreach_top_variable_func(mrb_state *mrb, mrb_sym sym, void *user); void mrb_parser_foreach_top_variable(mrb_state *mrb, struct mrb_parser_state *p, mrb_parser_foreach_top_variable_func *func, void *user); void mrb_parser_foreach_top_variable(mrb_state *mrb, struct mrb_parser_state *p, mrb_parser_foreach_top_variable_func *func, void *user) { const mrb_ast_node *n = p->tree; if ((intptr_t)n->car == NODE_SCOPE) { n = n->cdr->car; for (; n; n = n->cdr) { mrb_sym sym = sym(n->car); if (sym && !func(mrb, sym, user)) break; } } } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compiler/core/PaxHeaders/node.h0000644000000000000000000000013015077107276024321 xustar0030 mtime=1761382078.110420611 29 atime=1761382080.13041137 29 ctime=1761382108.69230156 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compiler/core/node.h0000644000175100017510000000270615077107276024720 0ustar00runnerrunner/* ** node.h - nodes of abstract syntax tree ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_COMPILER_NODE_H #define MRUBY_COMPILER_NODE_H enum node_type { NODE_METHOD, NODE_SCOPE, NODE_BLOCK, NODE_IF, NODE_CASE, NODE_WHEN, NODE_WHILE, NODE_UNTIL, NODE_ITER, NODE_FOR, NODE_BREAK, NODE_NEXT, NODE_REDO, NODE_RETRY, NODE_BEGIN, NODE_RESCUE, NODE_ENSURE, NODE_AND, NODE_OR, NODE_NOT, NODE_MASGN, NODE_ASGN, NODE_CDECL, NODE_CVASGN, NODE_CVDECL, NODE_OP_ASGN, NODE_CALL, NODE_SCALL, NODE_FCALL, NODE_SUPER, NODE_ZSUPER, NODE_ARRAY, NODE_ZARRAY, NODE_HASH, NODE_KW_HASH, NODE_RETURN, NODE_YIELD, NODE_LVAR, NODE_DVAR, NODE_GVAR, NODE_IVAR, NODE_CONST, NODE_CVAR, NODE_NVAR, NODE_NTH_REF, NODE_BACK_REF, NODE_MATCH, NODE_INT, NODE_FLOAT, NODE_NEGATE, NODE_LAMBDA, NODE_SYM, NODE_STR, NODE_DSTR, NODE_XSTR, NODE_DXSTR, NODE_REGX, NODE_DREGX, NODE_DREGX_ONCE, NODE_ARG, NODE_ARGS_TAIL, NODE_KW_ARG, NODE_KW_REST_ARGS, NODE_SPLAT, NODE_TO_ARY, NODE_SVALUE, NODE_BLOCK_ARG, NODE_DEF, NODE_SDEF, NODE_ALIAS, NODE_UNDEF, NODE_CLASS, NODE_MODULE, NODE_SCLASS, NODE_COLON2, NODE_COLON3, NODE_DOT2, NODE_DOT3, NODE_SELF, NODE_NIL, NODE_TRUE, NODE_FALSE, NODE_DEFINED, NODE_POSTEXE, NODE_DSYM, NODE_HEREDOC, NODE_LITERAL_DELIM, NODE_WORDS, NODE_SYMBOLS, NODE_LAST }; #endif /* MRUBY_COMPILER_NODE_H */ nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compiler/core/PaxHeaders/codegen.c0000644000000000000000000000013215077107276024775 xustar0030 mtime=1761382078.110420611 30 atime=1761382080.128411379 30 ctime=1761382108.695301552 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compiler/core/codegen.c0000644000175100017510000027631315077107276025401 0ustar00runnerrunner/* ** codegen.c - mruby code generator ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include "node.h" #include #include #include #include #include #include #define mrbc_malloc(s) mrb_basic_alloc_func(NULL,(s)) #define mrbc_realloc(p,s) mrb_basic_alloc_func((p),(s)) #define mrbc_free(p) mrb_basic_alloc_func((p),0) #ifndef MRB_CODEGEN_LEVEL_MAX #define MRB_CODEGEN_LEVEL_MAX 256 #endif #define MAXARG_S (1<<16) typedef mrb_ast_node node; typedef struct mrb_parser_state parser_state; enum looptype { LOOP_NORMAL, LOOP_BLOCK, LOOP_FOR, LOOP_BEGIN, LOOP_RESCUE, }; struct loopinfo { enum looptype type; uint32_t pc0; /* `next` destination */ uint32_t pc1; /* `redo` destination */ uint32_t pc2; /* `break` destination */ int reg; /* destination register */ struct loopinfo *prev; }; typedef struct scope { mrb_state *mrb; mempool *mpool; struct scope *prev; node *lv; uint16_t sp; uint32_t pc; uint32_t lastpc; uint32_t lastlabel; uint16_t ainfo:15; mrb_bool mscope:1; struct loopinfo *loop; mrb_sym filename_sym; uint16_t lineno; mrb_code *iseq; uint16_t *lines; uint32_t icapa; mrb_irep *irep; mrb_irep_pool *pool; mrb_sym *syms; mrb_irep **reps; struct mrb_irep_catch_handler *catch_table; uint32_t pcapa, scapa, rcapa; uint16_t nlocals; uint16_t nregs; int ai; int debug_start_pos; uint16_t filename_index; parser_state* parser; int rlev; /* recursion levels */ } codegen_scope; static codegen_scope* scope_new(mrb_state *mrb, codegen_scope *prev, node *lv); static void scope_finish(codegen_scope *s); static struct loopinfo *loop_push(codegen_scope *s, enum looptype t); static void loop_break(codegen_scope *s, node *tree); static void loop_pop(codegen_scope *s, int val); /* * The search for catch handlers starts at the end of the table in mrb_vm_run(). * Therefore, the next handler to be added must meet one of the following conditions. * - Larger start position * - Same start position but smaller end position */ static int catch_handler_new(codegen_scope *s); static void catch_handler_set(codegen_scope *s, int ent, enum mrb_catch_type type, uint32_t begin, uint32_t end, uint32_t target); static void gen_assignment(codegen_scope *s, node *tree, node *rhs, int sp, int val); static void gen_massignment(codegen_scope *s, node *tree, int sp, int val); static void codegen(codegen_scope *s, node *tree, int val); static void raise_error(codegen_scope *s, const char *msg); static void codegen_error(codegen_scope *s, const char *message) { if (!s) return; #ifndef MRB_NO_STDIO if (s->filename_sym && s->lineno) { const char *filename = mrb_sym_name_len(s->mrb, s->filename_sym, NULL); fprintf(stderr, "%s:%d: %s\n", filename, s->lineno, message); } else { fprintf(stderr, "%s\n", message); } #endif while (s->prev) { codegen_scope *tmp = s->prev; if (s->irep) { mrbc_free(s->iseq); for (int i=0; iirep->plen; i++) { mrb_irep_pool *p = &s->pool[i]; if ((p->tt & 0x3) == IREP_TT_STR || p->tt == IREP_TT_BIGINT) { mrbc_free((void*)p->u.str); } } mrbc_free(s->pool); mrbc_free(s->syms); mrbc_free(s->catch_table); if (s->reps) { /* copied from mrb_irep_free() in state.c */ for (int i=0; iirep->rlen; i++) { if (s->reps[i]) mrb_irep_decref(s->mrb, (mrb_irep*)s->reps[i]); } mrbc_free(s->reps); } mrbc_free(s->lines); } mempool_close(s->mpool); s = tmp; } MRB_THROW(s->mrb->jmp); } static void* codegen_palloc(codegen_scope *s, size_t len) { void *p = mempool_alloc(s->mpool, len); if (!p) codegen_error(s, "pool memory allocation"); return p; } static void check_no_ext_ops(codegen_scope *s, uint16_t a, uint16_t b) { if (s->parser->no_ext_ops && (a | b) > 0xff) { codegen_error(s, "need OP_EXTs instruction (currently OP_EXTs are prohibited)"); } } static int new_label(codegen_scope *s) { return s->lastlabel = s->pc; } static void emit_B(codegen_scope *s, uint32_t pc, uint8_t i) { if (pc >= s->icapa) { if (pc == UINT32_MAX) { codegen_error(s, "too big code block"); } if (pc >= UINT32_MAX / 2) { pc = UINT32_MAX; } else { s->icapa *= 2; } s->iseq = (mrb_code*)mrbc_realloc(s->iseq, sizeof(mrb_code)*s->icapa); if (s->lines) { s->lines = (uint16_t*)mrbc_realloc(s->lines, sizeof(uint16_t)*s->icapa); } } if (s->lines) { if (s->lineno > 0 || pc == 0) s->lines[pc] = s->lineno; else s->lines[pc] = s->lines[pc-1]; } s->iseq[pc] = i; } static void emit_S(codegen_scope *s, int pc, uint16_t i) { uint8_t hi = i>>8; uint8_t lo = i&0xff; emit_B(s, pc, hi); emit_B(s, pc+1, lo); } static void gen_B(codegen_scope *s, uint8_t i) { emit_B(s, s->pc, i); s->pc++; } static void gen_S(codegen_scope *s, uint16_t i) { emit_S(s, s->pc, i); s->pc += 2; } static void genop_0(codegen_scope *s, mrb_code i) { s->lastpc = s->pc; gen_B(s, i); } static void genop_1(codegen_scope *s, mrb_code i, uint16_t a) { s->lastpc = s->pc; check_no_ext_ops(s, a, 0); if (a > 0xff) { gen_B(s, OP_EXT1); gen_B(s, i); gen_S(s, a); } else { gen_B(s, i); gen_B(s, (uint8_t)a); } } static void genop_2(codegen_scope *s, mrb_code i, uint16_t a, uint16_t b) { s->lastpc = s->pc; check_no_ext_ops(s, a, b); if (a > 0xff && b > 0xff) { gen_B(s, OP_EXT3); gen_B(s, i); gen_S(s, a); gen_S(s, b); } else if (b > 0xff) { gen_B(s, OP_EXT2); gen_B(s, i); gen_B(s, (uint8_t)a); gen_S(s, b); } else if (a > 0xff) { gen_B(s, OP_EXT1); gen_B(s, i); gen_S(s, a); gen_B(s, (uint8_t)b); } else { gen_B(s, i); gen_B(s, (uint8_t)a); gen_B(s, (uint8_t)b); } } static void genop_3(codegen_scope *s, mrb_code i, uint16_t a, uint16_t b, uint16_t c) { genop_2(s, i, a, b); gen_B(s, (uint8_t)c); } static void genop_2S(codegen_scope *s, mrb_code i, uint16_t a, uint16_t b) { genop_1(s, i, a); gen_S(s, b); } static void genop_2SS(codegen_scope *s, mrb_code i, uint16_t a, uint32_t b) { genop_1(s, i, a); gen_S(s, b>>16); gen_S(s, b&0xffff); } static void genop_W(codegen_scope *s, mrb_code i, uint32_t a) { uint8_t a1 = (a>>16) & 0xff; uint8_t a2 = (a>>8) & 0xff; uint8_t a3 = a & 0xff; s->lastpc = s->pc; gen_B(s, i); gen_B(s, a1); gen_B(s, a2); gen_B(s, a3); } #define NOVAL 0 #define VAL 1 static mrb_bool no_optimize(codegen_scope *s) { if (s && s->parser && s->parser->no_optimize) return TRUE; return FALSE; } struct mrb_insn_data mrb_decode_insn(const mrb_code *pc) { struct mrb_insn_data data = { 0 }; if (pc == 0) return data; data.addr = pc; mrb_code insn = READ_B(); uint16_t a = 0; uint16_t b = 0; uint16_t c = 0; switch (insn) { #define FETCH_Z() /* empty */ #define OPCODE(i,x) case OP_ ## i: FETCH_ ## x (); break; #include #undef OPCODE } switch (insn) { case OP_EXT1: insn = READ_B(); switch (insn) { #define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _1 (); break; #include #undef OPCODE } break; case OP_EXT2: insn = READ_B(); switch (insn) { #define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _2 (); break; #include #undef OPCODE } break; case OP_EXT3: insn = READ_B(); switch (insn) { #define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _3 (); break; #include #undef OPCODE } break; default: break; } data.insn = insn; data.a = a; data.b = b; data.c = c; return data; } #undef OPCODE #define Z 1 #define S 3 #define W 4 #define OPCODE(_,x) x, /* instruction sizes */ static uint8_t mrb_insn_size[] = { #define B 2 #define BB 3 #define BBB 4 #define BS 4 #define BSS 6 #include #undef B #undef BB #undef BBB #undef BS #undef BSS }; /* EXT1 instruction sizes */ static uint8_t mrb_insn_size1[] = { #define B 3 #define BB 4 #define BBB 5 #define BS 5 #define BSS 7 #include #undef B #undef BS #undef BSS }; /* EXT2 instruction sizes */ static uint8_t mrb_insn_size2[] = { #define B 2 #define BS 4 #define BSS 6 #include #undef B #undef BB #undef BBB #undef BS #undef BSS }; /* EXT3 instruction sizes */ #define B 3 #define BB 5 #define BBB 6 #define BS 5 #define BSS 7 static uint8_t mrb_insn_size3[] = { #include }; #undef B #undef BB #undef BBB #undef BS #undef BSS #undef OPCODE static const mrb_code* mrb_prev_pc(codegen_scope *s, const mrb_code *pc) { const mrb_code *prev_pc = NULL; const mrb_code *i = s->iseq; mrb_assert(pc < s->iseq + s->icapa); while (iiseq[(s)->pc]) #define addr_pc(s, addr) (uint32_t)((addr) - s->iseq) #define rewind_pc(s) s->pc = s->lastpc static struct mrb_insn_data mrb_last_insn(codegen_scope *s) { if (s->pc == 0) { struct mrb_insn_data data = { OP_NOP, 0 }; return data; } return mrb_decode_insn(&s->iseq[s->lastpc]); } static mrb_bool no_peephole(codegen_scope *s) { return no_optimize(s) || s->lastlabel == s->pc || s->pc == 0 || s->pc == s->lastpc; } #define JMPLINK_START UINT32_MAX static void gen_jmpdst(codegen_scope *s, uint32_t pc) { if (pc == JMPLINK_START) { pc = 0; } uint32_t pos2 = s->pc+2; int32_t off = pc - pos2; if (off > INT16_MAX || INT16_MIN > off) { codegen_error(s, "too big jump offset"); } gen_S(s, (uint16_t)off); } static uint32_t genjmp(codegen_scope *s, mrb_code i, uint32_t pc) { uint32_t pos; genop_0(s, i); pos = s->pc; gen_jmpdst(s, pc); return pos; } #define genjmp_0(s,i) genjmp(s,i,JMPLINK_START) static uint32_t genjmp2(codegen_scope *s, mrb_code i, uint16_t a, uint32_t pc, int val) { uint32_t pos; if (!no_peephole(s) && !val) { struct mrb_insn_data data = mrb_last_insn(s); switch (data.insn) { case OP_MOVE: if (data.a == a && data.a > s->nlocals) { rewind_pc(s); a = data.b; } break; case OP_LOADNIL: case OP_LOADF: if (data.a == a || data.a > s->nlocals) { s->pc = addr_pc(s, data.addr); if (i == OP_JMPNOT || (i == OP_JMPNIL && data.insn == OP_LOADNIL)) { return genjmp(s, OP_JMP, pc); } else { /* OP_JMPIF */ return JMPLINK_START; } } break; case OP_LOADT: case OP_LOADI8: case OP_LOADINEG: case OP_LOADI__1: case OP_LOADI_0: case OP_LOADI_1: case OP_LOADI_2: case OP_LOADI_3: case OP_LOADI_4: case OP_LOADI_5: case OP_LOADI_6: case OP_LOADI_7: if (data.a == a || data.a > s->nlocals) { s->pc = addr_pc(s, data.addr); if (i == OP_JMPIF) { return genjmp(s, OP_JMP, pc); } else { /* OP_JMPNOT and OP_JMPNIL */ return JMPLINK_START; } } break; } } if (a > 0xff) { check_no_ext_ops(s, a, 0); gen_B(s, OP_EXT1); genop_0(s, i); gen_S(s, a); } else { genop_0(s, i); gen_B(s, (uint8_t)a); } pos = s->pc; gen_jmpdst(s, pc); return pos; } #define genjmp2_0(s,i,a,val) genjmp2(s,i,a,JMPLINK_START,val) static mrb_bool get_int_operand(codegen_scope *s, struct mrb_insn_data *data, mrb_int *ns); static void gen_int(codegen_scope *s, uint16_t dst, mrb_int i); static void gen_move(codegen_scope *s, uint16_t dst, uint16_t src, int nopeep) { if (nopeep || no_peephole(s)) goto normal; else if (dst == src) return; else { struct mrb_insn_data data = mrb_last_insn(s); switch (data.insn) { case OP_MOVE: if (dst == src) return; /* remove useless MOVE */ if (data.a == src) { if (data.b == dst) /* skip swapping MOVE */ return; if (data.a < s->nlocals) goto normal; rewind_pc(s); s->lastpc = addr_pc(s, mrb_prev_pc(s, data.addr)); gen_move(s, dst, data.b, FALSE); return; } if (dst == data.a) { /* skip overwritten move */ rewind_pc(s); s->lastpc = addr_pc(s, mrb_prev_pc(s, data.addr)); gen_move(s, dst, src, FALSE); return; } goto normal; case OP_LOADNIL: case OP_LOADSELF: case OP_LOADT: case OP_LOADF: case OP_LOADI__1: case OP_LOADI_0: case OP_LOADI_1: case OP_LOADI_2: case OP_LOADI_3: case OP_LOADI_4: case OP_LOADI_5: case OP_LOADI_6: case OP_LOADI_7: if (data.a != src || data.a < s->nlocals) goto normal; rewind_pc(s); genop_1(s, data.insn, dst); return; case OP_HASH: if (data.b != 0) goto normal; /* fall through */ case OP_LOADI8: case OP_LOADINEG: case OP_LOADL: case OP_LOADSYM: case OP_GETGV: case OP_GETSV: case OP_GETIV: case OP_GETCV: case OP_GETCONST: case OP_STRING: case OP_LAMBDA: case OP_BLOCK: case OP_METHOD: case OP_BLKPUSH: if (data.a != src || data.a < s->nlocals) goto normal; rewind_pc(s); genop_2(s, data.insn, dst, data.b); return; case OP_LOADI16: if (data.a != src || data.a < s->nlocals) goto normal; rewind_pc(s); genop_2S(s, data.insn, dst, data.b); return; case OP_LOADI32: if (data.a != src || data.a < s->nlocals) goto normal; else { uint32_t i = (uint32_t)data.b<<16|data.c; rewind_pc(s); genop_2SS(s, data.insn, dst, i); } return; case OP_ARRAY: if (data.a != src || data.a < s->nlocals || data.a < dst) goto normal; rewind_pc(s); if (data.b == 0 || dst == data.a) genop_2(s, OP_ARRAY, dst, 0); else genop_3(s, OP_ARRAY2, dst, data.a, data.b); return; case OP_ARRAY2: if (data.a != src || data.a < s->nlocals || data.a < dst) goto normal; rewind_pc(s); genop_3(s, OP_ARRAY2, dst, data.b, data.c); return; case OP_AREF: case OP_GETUPVAR: if (data.a != src || data.a < s->nlocals) goto normal; rewind_pc(s); genop_3(s, data.insn, dst, data.b, data.c); return; case OP_ADDI: case OP_SUBI: if (addr_pc(s, data.addr) == s->lastlabel || data.a != src || data.a < s->nlocals) goto normal; else { struct mrb_insn_data data0 = mrb_decode_insn(mrb_prev_pc(s, data.addr)); if (data0.insn != OP_MOVE || data0.a != data.a || data0.b != dst) goto normal; s->pc = addr_pc(s, data0.addr); if (addr_pc(s, data0.addr) != s->lastlabel) { /* constant folding */ data0 = mrb_decode_insn(mrb_prev_pc(s, data0.addr)); mrb_int n; if (data0.a == dst && get_int_operand(s, &data0, &n)) { if ((data.insn == OP_ADDI && !mrb_int_add_overflow(n, data.b, &n)) || (data.insn == OP_SUBI && !mrb_int_sub_overflow(n, data.b, &n))) { s->pc = addr_pc(s, data0.addr); gen_int(s, dst, n); return; } } } } genop_2(s, data.insn, dst, data.b); return; default: break; } } normal: genop_2(s, OP_MOVE, dst, src); return; } static int search_upvar(codegen_scope *s, mrb_sym id, int *idx); static void gen_getupvar(codegen_scope *s, uint16_t dst, mrb_sym id) { int idx; int lv = search_upvar(s, id, &idx); if (!no_peephole(s)) { struct mrb_insn_data data = mrb_last_insn(s); if (data.insn == OP_SETUPVAR && data.a == dst && data.b == idx && data.c == lv) { /* skip GETUPVAR right after SETUPVAR */ return; } } genop_3(s, OP_GETUPVAR, dst, idx, lv); } static void gen_setupvar(codegen_scope *s, uint16_t dst, mrb_sym id) { int idx; int lv = search_upvar(s, id, &idx); if (!no_peephole(s)) { struct mrb_insn_data data = mrb_last_insn(s); if (data.insn == OP_MOVE && data.a == dst) { dst = data.b; rewind_pc(s); } } genop_3(s, OP_SETUPVAR, dst, idx, lv); } static void gen_return(codegen_scope *s, uint8_t op, uint16_t src) { if (no_peephole(s)) { genop_1(s, op, src); } else { struct mrb_insn_data data = mrb_last_insn(s); if (data.insn == OP_MOVE && src == data.a) { rewind_pc(s); genop_1(s, op, data.b); } else if (data.insn != OP_RETURN) { genop_1(s, op, src); } } } static mrb_bool get_int_operand(codegen_scope *s, struct mrb_insn_data *data, mrb_int *n) { switch (data->insn) { case OP_LOADI__1: *n = -1; return TRUE; case OP_LOADINEG: *n = -data->b; return TRUE; case OP_LOADI_0: case OP_LOADI_1: case OP_LOADI_2: case OP_LOADI_3: case OP_LOADI_4: case OP_LOADI_5: case OP_LOADI_6: case OP_LOADI_7: *n = data->insn - OP_LOADI_0; return TRUE; case OP_LOADI8: case OP_LOADI16: *n = (int16_t)data->b; return TRUE; case OP_LOADI32: *n = (int32_t)((uint32_t)data->b<<16)+data->c; return TRUE; case OP_LOADL: { mrb_irep_pool *p = &s->pool[data->b]; if (p->tt == IREP_TT_INT32) { *n = (mrb_int)p->u.i32; } #ifdef MRB_INT64 else if (p->tt == IREP_TT_INT64) { *n = (mrb_int)p->u.i64; } #endif else { return FALSE; } } return TRUE; default: return FALSE; } } static int new_lit_str2(codegen_scope *s, const char *str1, mrb_int len1, const char *str2, mrb_int len2); static int find_pool_str(codegen_scope *s, const char *str1, mrb_int len1, const char *str2, mrb_int len2); static void realloc_pool_str(codegen_scope *s, mrb_irep_pool *p, mrb_int len) { char *str; if ((p->tt & 3) == IREP_TT_SSTR) { str = (char*)mrbc_malloc(len+1); } else { str = (char*)p->u.str; str = (char*)mrbc_realloc(str, len+1); } p->tt = (uint32_t)(len<<2 | IREP_TT_STR); str[len] = '\0'; p->u.str = (const char*)str; } static void free_pool_str(codegen_scope *s, mrb_irep_pool *p) { if ((p->tt & 3) != IREP_TT_SSTR) { mrbc_free((char*)p->u.str); } p->u.str = NULL; s->irep->plen--; } static void merge_op_string(codegen_scope *s, uint16_t dst, uint16_t b1, uint16_t b2, const mrb_code *pc) { int used = 0; const mrb_code *i = s->iseq; /* scan OP_STRING that refers b1 or b2 */ mrb_assert(pc < s->iseq + s->icapa); while (ipool[b1]; mrb_irep_pool *p2 = &s->pool[b2]; mrb_int len1 = p1->tt>>2; mrb_int len2 = p2->tt>>2; int off = find_pool_str(s, p1->u.str, len1, p2->u.str, len2); if (off < 0) { switch (used) { case 0: /* both pools are free */ case 2: /* b2 is referenced */ /* overwrite p1; free b2 if possible */ off = b1; realloc_pool_str(s, p1, len1+len2); memcpy((void*)(p1->u.str+len1), (void*)p2->u.str, len2); if (b1 != b2 && used == 0 && b2+1 == s->irep->plen) { free_pool_str(s, p2); } break; case 1: /* b1 is referenced */ /* overwrite p2 */ off = b2; realloc_pool_str(s, p2, len1+len2); memmove((void*)(p2->u.str+len1), (void*)p2->u.str, len2); memcpy((void*)p2->u.str, p1->u.str, len1); break; case 3: /* both b1&b2 are referenced */ /* create new pool */ off = new_lit_str2(s, p1->u.str, len1, p2->u.str, len2); break; } } s->pc = addr_pc(s, pc); genop_2(s, OP_STRING, dst, off); } static void gen_addsub(codegen_scope *s, uint8_t op, uint16_t dst) { if (no_peephole(s)) { normal: genop_1(s, op, dst); return; } else { struct mrb_insn_data data = mrb_last_insn(s); mrb_int n; if (!get_int_operand(s, &data, &n)) { /* not integer immediate */ if (op == OP_ADD && data.insn == OP_STRING) { struct mrb_insn_data data0 = mrb_decode_insn(mrb_prev_pc(s, data.addr)); if (data0.insn == OP_STRING) { merge_op_string(s, dst, data0.b, data.b, data0.addr); return; } } goto normal; } struct mrb_insn_data data0 = mrb_decode_insn(mrb_prev_pc(s, data.addr)); mrb_int n0; if (addr_pc(s, data.addr) == s->lastlabel || !get_int_operand(s, &data0, &n0)) { /* OP_ADDI/OP_SUBI takes upto 8bits */ if (n > UINT8_MAX || n < -UINT8_MAX) goto normal; rewind_pc(s); if (n == 0) return; if (n > 0) { if (op == OP_ADD) genop_2(s, OP_ADDI, dst, (uint16_t)n); else genop_2(s, OP_SUBI, dst, (uint16_t)n); } else { /* n < 0 */ n = -n; if (op == OP_ADD) genop_2(s, OP_SUBI, dst, (uint16_t)n); else genop_2(s, OP_ADDI, dst, (uint16_t)n); } return; } if (op == OP_ADD) { if (mrb_int_add_overflow(n0, n, &n)) goto normal; } else { /* OP_SUB */ if (mrb_int_sub_overflow(n0, n, &n)) goto normal; } s->pc = addr_pc(s, data0.addr); gen_int(s, dst, n); } } static void gen_muldiv(codegen_scope *s, uint8_t op, uint16_t dst) { if (no_peephole(s)) { normal: genop_1(s, op, dst); return; } else { struct mrb_insn_data data = mrb_last_insn(s); mrb_int n, n0; if (addr_pc(s, data.addr) == s->lastlabel || !get_int_operand(s, &data, &n)) { /* not integer immediate */ goto normal; } struct mrb_insn_data data0 = mrb_decode_insn(mrb_prev_pc(s, data.addr)); if (!get_int_operand(s, &data0, &n0)) { goto normal; } if (op == OP_MUL) { if (mrb_int_mul_overflow(n0, n, &n)) goto normal; } else { /* OP_DIV */ if (n == 0) goto normal; if (n0 == MRB_INT_MIN && n == -1) goto normal; n = mrb_div_int(n0, n); } s->pc = addr_pc(s, data0.addr); gen_int(s, dst, n); } } mrb_bool mrb_num_shift(mrb_state *mrb, mrb_int val, mrb_int width, mrb_int *num); static mrb_bool gen_binop(codegen_scope *s, mrb_sym op, uint16_t dst) { if (no_peephole(s)) return FALSE; else if (op == MRB_OPSYM_2(s->mrb, aref)) { genop_1(s, OP_GETIDX, dst); return TRUE; } else { struct mrb_insn_data data = mrb_last_insn(s); mrb_int n, n0; if (addr_pc(s, data.addr) == s->lastlabel || !get_int_operand(s, &data, &n)) { /* not integer immediate */ return FALSE; } struct mrb_insn_data data0 = mrb_decode_insn(mrb_prev_pc(s, data.addr)); if (!get_int_operand(s, &data0, &n0)) { return FALSE; } if (op == MRB_OPSYM_2(s->mrb, lshift)) { if (!mrb_num_shift(s->mrb, n0, n, &n)) return FALSE; } else if (op == MRB_OPSYM_2(s->mrb, rshift)) { if (n == MRB_INT_MIN) return FALSE; if (!mrb_num_shift(s->mrb, n0, -n, &n)) return FALSE; } else if (op == MRB_OPSYM_2(s->mrb, mod) && n != 0) { if (n0 == MRB_INT_MIN && n == -1) { n = 0; } else { mrb_int n1 = n0 % n; if ((n0 < 0) != (n < 0) && n1 != 0) { n1 += n; } n = n1; } } else if (op == MRB_OPSYM_2(s->mrb, and)) { n = n0 & n; } else if (op == MRB_OPSYM_2(s->mrb, or)) { n = n0 | n; } else if (op == MRB_OPSYM_2(s->mrb, xor)) { n = n0 ^ n; } else { return FALSE; } s->pc = addr_pc(s, data0.addr); gen_int(s, dst, n); return TRUE; } } static uint32_t dispatch(codegen_scope *s, uint32_t pos0) { int32_t pos1; int32_t offset; int16_t newpos; if (pos0 == JMPLINK_START) return 0; pos1 = pos0 + 2; offset = s->pc - pos1; if (offset > INT16_MAX) { codegen_error(s, "too big jmp offset"); } s->lastlabel = s->pc; newpos = (int16_t)PEEK_S(s->iseq+pos0); emit_S(s, pos0, (uint16_t)offset); if (newpos == 0) return 0; return pos1+newpos; } static void dispatch_linked(codegen_scope *s, uint32_t pos) { if (pos==JMPLINK_START) return; for (;;) { pos = dispatch(s, pos); if (pos==0) break; } } #define nregs_update do {if (s->sp > s->nregs) s->nregs = s->sp;} while (0) static void push_n_(codegen_scope *s, int n) { if (s->sp+n >= 0xffff) { codegen_error(s, "too complex expression"); } s->sp+=n; nregs_update; } static void pop_n_(codegen_scope *s, int n) { if ((int)s->sp-n < 0) { codegen_error(s, "stack pointer underflow"); } s->sp-=n; } #define push() push_n_(s,1) #define push_n(n) push_n_(s,n) #define pop() pop_n_(s,1) #define pop_n(n) pop_n_(s,n) #define cursp() (s->sp) static mrb_irep_pool* lit_pool_extend(codegen_scope *s) { if (s->irep->plen == s->pcapa) { s->pcapa *= 2; s->pool = (mrb_irep_pool*)mrbc_realloc(s->pool, sizeof(mrb_irep_pool)*s->pcapa); } return &s->pool[s->irep->plen++]; } static int new_litbint(codegen_scope *s, const char *p, int base) { int i; size_t plen; mrb_irep_pool *pv; plen = strlen(p); if (plen > 255) { codegen_error(s, "integer too big"); } for (i=0; iirep->plen; i++) { size_t len; pv = &s->pool[i]; if (pv->tt != IREP_TT_BIGINT) continue; len = pv->u.str[0]; if (len == plen && pv->u.str[1] == base && memcmp(pv->u.str+2, p, len) == 0) return i; } pv = lit_pool_extend(s); char *buf; pv->tt = IREP_TT_BIGINT; buf = (char*)mrbc_malloc(plen+3); buf[0] = (char)plen; buf[1] = base; memcpy(buf+2, p, plen); buf[plen+2] = '\0'; pv->u.str = buf; return i; } static int find_pool_str(codegen_scope *s, const char *str1, mrb_int len1, const char *str2, mrb_int len2) { mrb_irep_pool *pool; mrb_int len = len1 + len2; int i; for (i=0; iirep->plen; i++) { pool = &s->pool[i]; if (pool->tt & IREP_TT_NFLAG) continue; mrb_int plen = pool->tt>>2; if (len != plen) continue; if (memcmp(pool->u.str, str1, len1) == 0 && (len2 == 0 || memcmp(pool->u.str + len1, str2, len2) == 0)) return i; } return -1; } static int new_lit_str2(codegen_scope *s, const char *str1, mrb_int len1, const char *str2, mrb_int len2) { int i = find_pool_str(s, str1, len1, str2, len2); if (i >= 0) return i; i = s->irep->plen; mrb_irep_pool *pool = lit_pool_extend(s); mrb_int len = len1 + len2; if (mrb_ro_data_p(str1) && !str2) { pool->tt = (uint32_t)(len<<2) | IREP_TT_SSTR; pool->u.str = str1; } else { char *p; pool->tt = (uint32_t)(len<<2) | IREP_TT_STR; p = (char*)mrbc_malloc(len+1); memcpy(p, str1, len1); if (str2) memcpy(p+len1, str2, len2); p[len] = '\0'; pool->u.str = p; } return i; } static int new_lit_str(codegen_scope *s, const char *str, mrb_int len) { return new_lit_str2(s, str, len, NULL, 0); } static int new_lit_cstr(codegen_scope *s, const char *str) { return new_lit_str(s, str, (mrb_int)strlen(str)); } static int new_lit_int(codegen_scope *s, mrb_int num) { int i; mrb_irep_pool *pool; for (i=0; iirep->plen; i++) { pool = &s->pool[i]; if (pool->tt == IREP_TT_INT32) { if (num == pool->u.i32) return i; } #ifdef MRB_64BIT else if (pool->tt == IREP_TT_INT64) { if (num == pool->u.i64) return i; } continue; #endif } pool = lit_pool_extend(s); #ifdef MRB_INT64 pool->tt = IREP_TT_INT64; pool->u.i64 = num; #else pool->tt = IREP_TT_INT32; pool->u.i32 = num; #endif return i; } #ifndef MRB_NO_FLOAT static int new_lit_float(codegen_scope *s, mrb_float num) { int i; mrb_irep_pool *pool; for (i=0; iirep->plen; i++) { mrb_float f; pool = &s->pool[i]; if (pool->tt != IREP_TT_FLOAT) continue; f = pool->u.f; if (f == num && !signbit(f) == !signbit(num)) return i; } pool = lit_pool_extend(s); pool->tt = IREP_TT_FLOAT; pool->u.f = num; return i; } #endif static int new_sym(codegen_scope *s, mrb_sym sym) { int i, len; mrb_assert(s->irep); len = s->irep->slen; for (i=0; isyms[i] == sym) return i; } if (s->irep->slen >= s->scapa) { s->scapa *= 2; if (s->scapa > 0xffff) { codegen_error(s, "too many symbols"); } s->syms = (mrb_sym*)mrbc_realloc(s->syms, sizeof(mrb_sym)*s->scapa); } s->syms[s->irep->slen] = sym; return s->irep->slen++; } static void gen_setxv(codegen_scope *s, uint8_t op, uint16_t dst, mrb_sym sym, int val) { int idx = new_sym(s, sym); if (!val && !no_peephole(s)) { struct mrb_insn_data data = mrb_last_insn(s); if (data.insn == OP_MOVE && data.a == dst) { dst = data.b; rewind_pc(s); } } genop_2(s, op, dst, idx); } static void gen_int(codegen_scope *s, uint16_t dst, mrb_int i) { if (i < 0) { if (i == -1) genop_1(s, OP_LOADI__1, dst); else if (i >= -0xff) genop_2(s, OP_LOADINEG, dst, (uint16_t)-i); else if (i >= INT16_MIN) genop_2S(s, OP_LOADI16, dst, (uint16_t)i); else if (i >= INT32_MIN) genop_2SS(s, OP_LOADI32, dst, (uint32_t)i); else goto int_lit; } else if (i < 8) genop_1(s, OP_LOADI_0 + (uint8_t)i, dst); else if (i <= 0xff) genop_2(s, OP_LOADI8, dst, (uint16_t)i); else if (i <= INT16_MAX) genop_2S(s, OP_LOADI16, dst, (uint16_t)i); else if (i <= INT32_MAX) genop_2SS(s, OP_LOADI32, dst, (uint32_t)i); else { int_lit: genop_2(s, OP_LOADL, dst, new_lit_int(s, i)); } } static mrb_bool gen_uniop(codegen_scope *s, mrb_sym sym, uint16_t dst) { if (no_peephole(s)) return FALSE; struct mrb_insn_data data = mrb_last_insn(s); mrb_int n; if (!get_int_operand(s, &data, &n)) return FALSE; if (sym == MRB_OPSYM_2(s->mrb, plus)) { /* unary plus does nothing */ } else if (sym == MRB_OPSYM_2(s->mrb, minus)) { if (n == MRB_INT_MIN) return FALSE; n = -n; } else if (sym == MRB_OPSYM_2(s->mrb, neg)) { n = ~n; } else { return FALSE; } s->pc = addr_pc(s, data.addr); gen_int(s, dst, n); return TRUE; } static int node_len(node *tree) { int n = 0; while (tree) { n++; tree = tree->cdr; } return n; } #define nint(x) ((int)(intptr_t)(x)) #define nchar(x) ((char)(intptr_t)(x)) #define nsym(x) ((mrb_sym)(intptr_t)(x)) #define lv_name(lv) nsym((lv)->car) static int lv_idx(codegen_scope *s, mrb_sym id) { node *lv = s->lv; int n = 1; while (lv) { if (lv_name(lv) == id) return n; n++; lv = lv->cdr; } return 0; } static int search_upvar(codegen_scope *s, mrb_sym id, int *idx) { const struct RProc *u; int lv = 0; codegen_scope *up = s->prev; while (up) { *idx = lv_idx(up, id); if (*idx > 0) { return lv; } lv++; up = up->prev; } if (lv < 1) lv = 1; u = s->parser->upper; while (u && !MRB_PROC_CFUNC_P(u)) { const struct mrb_irep *ir = u->body.irep; uint_fast16_t n = ir->nlocals; int i; const mrb_sym *v = ir->lv; if (v) { for (i=1; n > 1; n--, v++, i++) { if (*v == id) { *idx = i; return lv - 1; } } } if (MRB_PROC_SCOPE_P(u)) break; u = u->upper; lv++; } if (id == MRB_OPSYM_2(s->mrb, and)) { codegen_error(s, "No anonymous block parameter"); } else if (id == MRB_OPSYM_2(s->mrb, mul)) { codegen_error(s, "No anonymous rest parameter"); } else if (id == MRB_OPSYM_2(s->mrb, pow)) { codegen_error(s, "No anonymous keyword rest parameter"); } else { codegen_error(s, "Can't find local variables"); } return -1; /* not reached */ } static void for_body(codegen_scope *s, node *tree) { codegen_scope *prev = s; int idx; struct loopinfo *lp; node *n2; /* generate receiver */ codegen(s, tree->cdr->car, VAL); /* generate loop-block */ s = scope_new(s->mrb, s, NULL); push(); /* push for a block parameter */ /* generate loop variable */ n2 = tree->car; genop_W(s, OP_ENTER, 0x40000); if (n2->car && !n2->car->cdr && !n2->cdr) { gen_assignment(s, n2->car->car, NULL, 1, NOVAL); } else { gen_massignment(s, n2, 1, VAL); } /* construct loop */ lp = loop_push(s, LOOP_FOR); lp->pc1 = new_label(s); genop_0(s, OP_NOP); /* for redo */ /* loop body */ codegen(s, tree->cdr->cdr->car, VAL); pop(); gen_return(s, OP_RETURN, cursp()); loop_pop(s, NOVAL); scope_finish(s); s = prev; genop_2(s, OP_BLOCK, cursp(), s->irep->rlen-1); push();pop(); /* space for a block */ pop(); idx = new_sym(s, MRB_SYM_2(s->mrb, each)); genop_3(s, OP_SENDB, cursp(), idx, 0); } static int lambda_body(codegen_scope *s, node *tree, int blk) { codegen_scope *parent = s; s = scope_new(s->mrb, s, tree->car); s->mscope = !blk; if (blk) { struct loopinfo *lp = loop_push(s, LOOP_BLOCK); lp->pc0 = new_label(s); } tree = tree->cdr; if (tree->car == NULL) { genop_W(s, OP_ENTER, 0); s->ainfo = 0; } else { mrb_aspec a; int ma, oa, ra, pa, ka, kd, ba, i; uint32_t pos; node *opt; node *margs, *pargs; node *tail; /* mandatory arguments */ ma = node_len(tree->car->car); margs = tree->car->car; tail = tree->car->cdr->cdr->cdr->cdr; /* optional arguments */ oa = node_len(tree->car->cdr->car); /* rest argument? */ ra = tree->car->cdr->cdr->car ? 1 : 0; /* mandatory arguments after rest argument */ pa = node_len(tree->car->cdr->cdr->cdr->car); pargs = tree->car->cdr->cdr->cdr->car; /* keyword arguments */ ka = tail ? node_len(tail->cdr->car) : 0; /* keyword dictionary? */ kd = tail && tail->cdr->cdr->car? 1 : 0; /* block argument? */ ba = tail && tail->cdr->cdr->cdr->car ? 1 : 0; if (ma > 0x1f || oa > 0x1f || pa > 0x1f || ka > 0x1f) { codegen_error(s, "too many formal arguments"); } /* (23bits = 5:5:1:5:5:1:1) */ a = MRB_ARGS_REQ(ma) | MRB_ARGS_OPT(oa) | (ra ? MRB_ARGS_REST() : 0) | MRB_ARGS_POST(pa) | MRB_ARGS_KEY(ka, kd) | (ba ? MRB_ARGS_BLOCK() : 0); genop_W(s, OP_ENTER, a); /* (12bits = 5:1:5:1) */ s->ainfo = (((ma+oa) & 0x3f) << 7) | ((ra & 0x1) << 6) | ((pa & 0x1f) << 1) | (ka || kd); /* generate jump table for optional arguments initializer */ pos = new_label(s); for (i=0; i 0) { genjmp_0(s, OP_JMP); } opt = tree->car->cdr->car; i = 0; while (opt) { int idx; mrb_sym id = nsym(opt->car->car); dispatch(s, pos+i*3+1); codegen(s, opt->car->cdr, VAL); pop(); idx = lv_idx(s, id); if (idx > 0) { gen_move(s, idx, cursp(), 0); } else { gen_getupvar(s, cursp(), id); } i++; opt = opt->cdr; } if (oa > 0) { dispatch(s, pos+i*3+1); } /* keyword arguments */ if (tail) { node *kwds = tail->cdr->car; int kwrest = 0; if (tail->cdr->cdr->car) { kwrest = 1; } mrb_assert(nint(tail->car) == NODE_ARGS_TAIL); mrb_assert(node_len(tail) == 4); while (kwds) { int jmpif_key_p, jmp_def_set = -1; node *kwd = kwds->car, *def_arg = kwd->cdr->cdr->car; mrb_sym kwd_sym = nsym(kwd->cdr->car); mrb_assert(nint(kwd->car) == NODE_KW_ARG); if (def_arg) { int idx; genop_2(s, OP_KEY_P, lv_idx(s, kwd_sym), new_sym(s, kwd_sym)); jmpif_key_p = genjmp2_0(s, OP_JMPIF, lv_idx(s, kwd_sym), NOVAL); codegen(s, def_arg, VAL); pop(); idx = lv_idx(s, kwd_sym); if (idx > 0) { gen_move(s, idx, cursp(), 0); } else { gen_getupvar(s, cursp(), kwd_sym); } jmp_def_set = genjmp_0(s, OP_JMP); dispatch(s, jmpif_key_p); } genop_2(s, OP_KARG, lv_idx(s, kwd_sym), new_sym(s, kwd_sym)); if (jmp_def_set != -1) { dispatch(s, jmp_def_set); } i++; kwds = kwds->cdr; } if (tail->cdr->car && !kwrest) { genop_0(s, OP_KEYEND); } if (ba) { mrb_sym bparam = nsym(tail->cdr->cdr->cdr->car); pos = ma+oa+ra+pa+(ka||kd); if (bparam) { int idx = lv_idx(s, bparam); genop_2(s, OP_MOVE, idx, pos+1); } } } /* argument destructuring */ if (margs) { node *n = margs; pos = 1; while (n) { if (nint(n->car->car) == NODE_MASGN) { gen_massignment(s, n->car->cdr->car, pos, NOVAL); } pos++; n = n->cdr; } } if (pargs) { node *n = pargs; pos = ma+oa+ra+1; while (n) { if (nint(n->car->car) == NODE_MASGN) { gen_massignment(s, n->car->cdr->car, pos, NOVAL); } pos++; n = n->cdr; } } } codegen(s, tree->cdr->car, VAL); pop(); if (s->pc > 0) { gen_return(s, OP_RETURN, cursp()); } if (blk) { loop_pop(s, NOVAL); } scope_finish(s); return parent->irep->rlen - 1; } static int scope_body(codegen_scope *s, node *tree, int val) { codegen_scope *scope = scope_new(s->mrb, s, tree->car); codegen(scope, tree->cdr, VAL); gen_return(scope, OP_RETURN, scope->sp-1); if (!s->iseq) { genop_0(scope, OP_STOP); } scope_finish(scope); if (!s->irep) { /* should not happen */ return 0; } return s->irep->rlen - 1; } static mrb_bool nosplat(node *t) { while (t) { if (nint(t->car->car) == NODE_SPLAT) return FALSE; t = t->cdr; } return TRUE; } static mrb_sym attrsym(codegen_scope *s, mrb_sym a) { const char *name; mrb_int len; char *name2; name = mrb_sym_name_len(s->mrb, a, &len); name2 = (char*)codegen_palloc(s, (size_t)len + 1 /* '=' */ + 1 /* '\0' */ ); mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); memcpy(name2, name, (size_t)len); name2[len] = '='; name2[len+1] = '\0'; return mrb_intern(s->mrb, name2, len+1); } #define CALL_MAXARGS 15 #define GEN_LIT_ARY_MAX 64 #define GEN_VAL_STACK_MAX 99 static int gen_values(codegen_scope *s, node *t, int val, int limit) { int n = 0; int first = 1; int slimit = GEN_VAL_STACK_MAX; if (limit == 0) limit = GEN_LIT_ARY_MAX; if (cursp() >= slimit) slimit = INT16_MAX; if (!val) { while (t) { codegen(s, t->car, NOVAL); n++; t = t->cdr; } return n; } while (t) { int is_splat = nint(t->car->car) == NODE_SPLAT; if (is_splat || cursp() >= slimit) { /* flush stack */ pop_n(n); if (first) { if (n == 0) { genop_1(s, OP_LOADNIL, cursp()); } else { genop_2(s, OP_ARRAY, cursp(), n); } push(); first = 0; limit = GEN_LIT_ARY_MAX; } else if (n > 0) { pop(); genop_2(s, OP_ARYPUSH, cursp(), n); push(); } n = 0; } codegen(s, t->car, val); if (is_splat) { pop(); pop(); genop_1(s, OP_ARYCAT, cursp()); push(); } else { n++; } t = t->cdr; } if (!first) { pop(); if (n > 0) { pop_n(n); genop_2(s, OP_ARYPUSH, cursp(), n); } return -1; /* variable length */ } else if (n > limit) { pop_n(n); genop_2(s, OP_ARRAY, cursp(), n); return -1; } return n; } static int gen_hash(codegen_scope *s, node *tree, int val, int limit) { int slimit = GEN_VAL_STACK_MAX; if (cursp() >= GEN_LIT_ARY_MAX) slimit = INT16_MAX; int len = 0; mrb_bool update = FALSE; mrb_bool first = TRUE; while (tree) { if (nint(tree->car->car->car) == NODE_KW_REST_ARGS) { if (val && first) { genop_2(s, OP_HASH, cursp(), 0); push(); update = TRUE; } else if (val && len > 0) { pop_n(len*2); if (!update) { genop_2(s, OP_HASH, cursp(), len); } else { pop(); genop_2(s, OP_HASHADD, cursp(), len); } push(); } codegen(s, tree->car->cdr, val); if (val && (len > 0 || update)) { pop(); pop(); genop_1(s, OP_HASHCAT, cursp()); push(); } update = TRUE; len = 0; } else { codegen(s, tree->car->car, val); codegen(s, tree->car->cdr, val); len++; } tree = tree->cdr; if (val && cursp() >= slimit) { pop_n(len*2); if (!update) { genop_2(s, OP_HASH, cursp(), len); } else { pop(); genop_2(s, OP_HASHADD, cursp(), len); } push(); update = TRUE; len = 0; } first = FALSE; } if (val && len > limit) { pop_n(len*2); genop_2(s, OP_HASH, cursp(), len); push(); return -1; } if (update) { if (val && len > 0) { pop_n(len*2+1); genop_2(s, OP_HASHADD, cursp(), len); push(); } return -1; /* variable length */ } return len; } static void gen_call(codegen_scope *s, node *tree, int val, int safe) { mrb_sym sym = nsym(tree->cdr->car); int skip = 0, n = 0, nk = 0, noop = no_optimize(s), noself = 0, blk = 0, sp_save = cursp(); enum mrb_insn opt_op = OP_NOP; if (!noop) { if (sym == MRB_OPSYM_2(s->mrb, add)) opt_op = OP_ADD; else if (sym == MRB_OPSYM_2(s->mrb, sub)) opt_op = OP_SUB; else if (sym == MRB_OPSYM_2(s->mrb, mul)) opt_op = OP_MUL; else if (sym == MRB_OPSYM_2(s->mrb, div)) opt_op = OP_DIV; else if (sym == MRB_OPSYM_2(s->mrb, lt)) opt_op = OP_LT; else if (sym == MRB_OPSYM_2(s->mrb, le)) opt_op = OP_LE; else if (sym == MRB_OPSYM_2(s->mrb, gt)) opt_op = OP_GT; else if (sym == MRB_OPSYM_2(s->mrb, ge)) opt_op = OP_GE; else if (sym == MRB_OPSYM_2(s->mrb, eq)) opt_op = OP_EQ; else if (sym == MRB_OPSYM_2(s->mrb, aref)) opt_op = OP_GETIDX; else if (sym == MRB_OPSYM_2(s->mrb, aset)) opt_op = OP_SETIDX; } if (!tree->car || (opt_op == OP_NOP && nint(tree->car->car) == NODE_SELF)) { noself = 1; push(); } else { codegen(s, tree->car, VAL); /* receiver */ } if (safe) { int recv = cursp()-1; gen_move(s, cursp(), recv, 1); skip = genjmp2_0(s, OP_JMPNIL, cursp(), val); } tree = tree->cdr->cdr->car; if (tree) { if (tree->car) { /* positional arguments */ n = gen_values(s, tree->car, VAL, 14); if (n < 0) { /* variable length */ noop = 1; /* not operator */ n = 15; push(); } } if (tree->cdr->car) { /* keyword arguments */ noop = 1; nk = gen_hash(s, tree->cdr->car->cdr, VAL, 14); if (nk < 0) nk = 15; } } if (tree && tree->cdr && tree->cdr->cdr) { codegen(s, tree->cdr->cdr, VAL); pop(); noop = 1; blk = 1; } push(); s->sp = sp_save; if (opt_op == OP_ADD && n == 1) { gen_addsub(s, OP_ADD, cursp()); } else if (opt_op == OP_SUB && n == 1) { gen_addsub(s, OP_SUB, cursp()); } else if (opt_op == OP_MUL && n == 1) { gen_muldiv(s, OP_MUL, cursp()); } else if (opt_op == OP_DIV && n == 1) { gen_muldiv(s, OP_DIV, cursp()); } else if (opt_op == OP_LT && n == 1) { genop_1(s, OP_LT, cursp()); } else if (opt_op == OP_LE && n == 1) { genop_1(s, OP_LE, cursp()); } else if (opt_op == OP_GT && n == 1) { genop_1(s, OP_GT, cursp()); } else if (opt_op == OP_GE && n == 1) { genop_1(s, OP_GE, cursp()); } else if (opt_op == OP_EQ && n == 1) { genop_1(s, OP_EQ, cursp()); } else if (opt_op == OP_SETIDX && n == 2) { genop_1(s, OP_SETIDX, cursp()); } else if (!noop && n == 0 && gen_uniop(s, sym, cursp())) { /* constant folding succeeded */ } else if (!noop && n == 1 && gen_binop(s, sym, cursp())) { /* constant folding succeeded */ } else if (noself) { genop_3(s, blk ? OP_SSENDB : OP_SSEND, cursp(), new_sym(s, sym), n|(nk<<4)); } else { genop_3(s, blk ? OP_SENDB : OP_SEND, cursp(), new_sym(s, sym), n|(nk<<4)); } if (safe) { dispatch(s, skip); } if (val) { push(); } } static void gen_assignment(codegen_scope *s, node *tree, node *rhs, int sp, int val) { int idx; int type = nint(tree->car); switch (type) { case NODE_GVAR: case NODE_ARG: case NODE_LVAR: case NODE_IVAR: case NODE_CVAR: case NODE_CONST: case NODE_NIL: case NODE_MASGN: if (rhs) { codegen(s, rhs, VAL); pop(); sp = cursp(); } break; case NODE_COLON2: case NODE_COLON3: case NODE_CALL: case NODE_SCALL: /* keep evaluation order */ break; case NODE_NVAR: /* never happens; should have already checked in the parser */ codegen_error(s, "Can't assign to numbered parameter"); break; default: codegen_error(s, "unknown lhs"); break; } tree = tree->cdr; switch (type) { case NODE_GVAR: gen_setxv(s, OP_SETGV, sp, nsym(tree), val); break; case NODE_ARG: case NODE_LVAR: idx = lv_idx(s, nsym(tree)); if (idx > 0) { if (idx != sp) { gen_move(s, idx, sp, val); } break; } else { /* upvar */ gen_setupvar(s, sp, nsym(tree)); } break; case NODE_IVAR: gen_setxv(s, OP_SETIV, sp, nsym(tree), val); break; case NODE_CVAR: gen_setxv(s, OP_SETCV, sp, nsym(tree), val); break; case NODE_CONST: gen_setxv(s, OP_SETCONST, sp, nsym(tree), val); break; case NODE_COLON2: case NODE_COLON3: if (sp) { gen_move(s, cursp(), sp, 0); } sp = cursp(); push(); if (type == NODE_COLON2) { codegen(s, tree->car, VAL); idx = new_sym(s, nsym(tree->cdr)); } else { /* NODE_COLON3 */ genop_1(s, OP_OCLASS, cursp()); push(); idx = new_sym(s, nsym(tree)); } if (rhs) { codegen(s, rhs, VAL); pop(); gen_move(s, sp, cursp(), 0); } pop_n(2); genop_2(s, OP_SETMCNST, sp, idx); break; case NODE_CALL: case NODE_SCALL: { int noself = 0, safe = (type == NODE_SCALL), skip = 0, top, call, n = 0; mrb_sym mid = nsym(tree->cdr->car); top = cursp(); if (val || sp == cursp()) { push(); /* room for retval */ } call = cursp(); if (!tree->car) { noself = 1; push(); } else { codegen(s, tree->car, VAL); /* receiver */ } if (safe) { int recv = cursp()-1; gen_move(s, cursp(), recv, 1); skip = genjmp2_0(s, OP_JMPNIL, cursp(), val); } tree = tree->cdr->cdr->car; if (tree) { if (tree->car) { /* positional arguments */ n = gen_values(s, tree->car, VAL, (tree->cdr->car)?13:14); if (n < 0) { /* variable length */ n = 15; push(); } } if (tree->cdr->car) { /* keyword arguments */ if (n == 13 || n == 14) { pop_n(n); genop_2(s, OP_ARRAY, cursp(), n); push(); n = 15; } gen_hash(s, tree->cdr->car->cdr, VAL, 0); if (n < 14) { n++; } else { pop_n(2); genop_2(s, OP_ARYPUSH, cursp(), 1); } push(); } } if (rhs) { codegen(s, rhs, VAL); pop(); } else { gen_move(s, cursp(), sp, 0); } if (val) { gen_move(s, top, cursp(), 1); } if (n < 15) { n++; if (n == 15) { pop_n(14); genop_2(s, OP_ARRAY, cursp(), 15); } } else { pop(); genop_2(s, OP_ARYPUSH, cursp(), 1); } push(); pop(); s->sp = call; if (mid == MRB_OPSYM_2(s->mrb, aref) && n == 2) { push_n(4); pop_n(4); /* self + idx + value + (invisible block for OP_SEND) */ genop_1(s, OP_SETIDX, cursp()); } else { int st = 2 /* self + block */ + (((n >> 0) & 0x0f) < 15 ? ((n >> 0) & 0x0f) : 1) + (((n >> 4) & 0x0f) < 15 ? ((n >> 4) & 0x0f) * 2 : 1); push_n(st); pop_n(st); genop_3(s, noself ? OP_SSEND : OP_SEND, cursp(), new_sym(s, attrsym(s, mid)), n); } if (safe) { dispatch(s, skip); } s->sp = top; } break; case NODE_MASGN: gen_massignment(s, tree->car, sp, val); break; /* splat without assignment */ case NODE_NIL: break; default: codegen_error(s, "unknown lhs"); break; } if (val) push(); } static void gen_massignment(codegen_scope *s, node *tree, int rhs, int val) { int n = 0, post = 0; node *t, *p; if (tree->car) { /* pre */ t = tree->car; n = 0; while (t) { int sp = cursp(); genop_3(s, OP_AREF, sp, rhs, n); push(); gen_assignment(s, t->car, NULL, sp, NOVAL); pop(); n++; t = t->cdr; } } t = tree->cdr; if (t) { if (t->cdr) { /* post count */ p = t->cdr->car; while (p) { post++; p = p->cdr; } } gen_move(s, cursp(), rhs, val); push_n(post+1); pop_n(post+1); genop_3(s, OP_APOST, cursp(), n, post); n = 1; if (t->car && t->car != (node*)-1) { /* rest */ gen_assignment(s, t->car, NULL, cursp(), NOVAL); } if (t->cdr && t->cdr->car) { t = t->cdr->car; while (t) { gen_assignment(s, t->car, NULL, cursp()+n, NOVAL); t = t->cdr; n++; } } if (val) { gen_move(s, cursp(), rhs, 0); } } } static void gen_intern(codegen_scope *s) { pop(); if (!no_peephole(s)) { struct mrb_insn_data data = mrb_last_insn(s); if (data.insn == OP_STRING && data.a == cursp()) { rewind_pc(s); genop_2(s, OP_SYMBOL, data.a, data.b); push(); return; } } genop_1(s, OP_INTERN, cursp()); push(); } static void gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val) { if (val) { int i = 0, j = 0, gen = 0; while (tree) { switch (nint(tree->car->car)) { case NODE_STR: if ((tree->cdr == NULL) && (nint(tree->car->cdr->cdr) == 0)) break; /* fall through */ case NODE_BEGIN: codegen(s, tree->car, VAL); j++; break; case NODE_LITERAL_DELIM: if (j > 0) { j = 0; i++; if (sym) gen_intern(s); } break; } while (j >= 2) { pop(); pop(); genop_1(s, OP_STRCAT, cursp()); push(); j--; } if (i > GEN_LIT_ARY_MAX) { pop_n(i); if (gen) { pop(); genop_2(s, OP_ARYPUSH, cursp(), i); } else { genop_2(s, OP_ARRAY, cursp(), i); gen = 1; } push(); i = 0; } tree = tree->cdr; } if (j > 0) { i++; if (sym) gen_intern(s); } pop_n(i); if (gen) { pop(); genop_2(s, OP_ARYPUSH, cursp(), i); } else { genop_2(s, OP_ARRAY, cursp(), i); } push(); } else { while (tree) { switch (nint(tree->car->car)) { case NODE_BEGIN: case NODE_BLOCK: codegen(s, tree->car, NOVAL); } tree = tree->cdr; } } } static void raise_error(codegen_scope *s, const char *msg) { int idx = new_lit_cstr(s, msg); genop_1(s, OP_ERR, idx); } static mrb_int readint(codegen_scope *s, const char *p, int base, mrb_bool neg, mrb_bool *overflow) { const char *e = p + strlen(p); mrb_int result = 0; mrb_assert(base >= 2 && base <= 16); if (*p == '+') p++; while (p < e) { int n; char c = *p; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': n = c - '0'; break; case '8': case '9': n = c - '0'; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': n = c - 'a' + 10; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': n = c - 'A' + 10; break; default: codegen_error(s, "malformed readint input"); *overflow = TRUE; /* not reached */ return result; } if (mrb_int_mul_overflow(result, base, &result)) { overflow: *overflow = TRUE; return 0; } mrb_uint tmp = ((mrb_uint)result)+n; if (neg && tmp == (mrb_uint)MRB_INT_MAX+1) { *overflow = FALSE; return MRB_INT_MIN; } if (tmp > MRB_INT_MAX) goto overflow; result = (mrb_int)tmp; p++; } *overflow = FALSE; if (neg) return -result; return result; } static void gen_retval(codegen_scope *s, node *tree) { if (nint(tree->car) == NODE_SPLAT) { codegen(s, tree, VAL); pop(); genop_1(s, OP_ARYSPLAT, cursp()); } else { codegen(s, tree, VAL); pop(); } } static mrb_bool true_always(node *tree) { switch (nint(tree->car)) { case NODE_TRUE: case NODE_INT: case NODE_STR: case NODE_SYM: return TRUE; default: return FALSE; } } static mrb_bool false_always(node *tree) { switch (nint(tree->car)) { case NODE_FALSE: case NODE_NIL: return TRUE; default: return FALSE; } } static void gen_blkmove(codegen_scope *s, uint16_t ainfo, int lv) { int m1 = (ainfo>>7)&0x3f; int r = (ainfo>>6)&0x1; int m2 = (ainfo>>1)&0x1f; int kd = (ainfo)&0x1; int off = m1+r+m2+kd+1; if (lv == 0) { gen_move(s, cursp(), off, 0); } else { genop_3(s, OP_GETUPVAR, cursp(), off, lv); } push(); } static void codegen(codegen_scope *s, node *tree, int val) { int nt; int rlev = s->rlev; if (!tree) { if (val) { genop_1(s, OP_LOADNIL, cursp()); push(); } return; } s->rlev++; if (s->rlev > MRB_CODEGEN_LEVEL_MAX) { codegen_error(s, "too complex expression"); } if (s->irep && s->filename_index != tree->filename_index) { mrb_sym fname = mrb_parser_get_filename(s->parser, s->filename_index); const char *filename = mrb_sym_name_len(s->mrb, fname, NULL); mrb_debug_info_append_file(s->mrb, s->irep->debug_info, filename, s->lines, s->debug_start_pos, s->pc); s->debug_start_pos = s->pc; s->filename_index = tree->filename_index; s->filename_sym = mrb_parser_get_filename(s->parser, tree->filename_index); } nt = nint(tree->car); s->lineno = tree->lineno; tree = tree->cdr; switch (nt) { case NODE_BEGIN: if (val && !tree) { genop_1(s, OP_LOADNIL, cursp()); push(); } while (tree) { codegen(s, tree->car, tree->cdr ? NOVAL : val); tree = tree->cdr; } break; case NODE_RESCUE: { int noexc; uint32_t exend, pos1, pos2, tmp; struct loopinfo *lp; int catch_entry, begin, end; if (tree->car == NULL) goto exit; lp = loop_push(s, LOOP_BEGIN); lp->pc0 = new_label(s); catch_entry = catch_handler_new(s); begin = s->pc; codegen(s, tree->car, VAL); pop(); lp->type = LOOP_RESCUE; end = s->pc; noexc = genjmp_0(s, OP_JMP); catch_handler_set(s, catch_entry, MRB_CATCH_RESCUE, begin, end, s->pc); tree = tree->cdr; exend = JMPLINK_START; pos1 = JMPLINK_START; if (tree->car) { node *n2 = tree->car; int exc = cursp(); genop_1(s, OP_EXCEPT, exc); push(); while (n2) { node *n3 = n2->car; node *n4 = n3->car; dispatch(s, pos1); pos2 = JMPLINK_START; do { if (n4 && n4->car && nint(n4->car->car) == NODE_SPLAT) { codegen(s, n4->car, VAL); gen_move(s, cursp(), exc, 0); push_n(2); pop_n(2); /* space for one arg and a block */ pop(); genop_3(s, OP_SEND, cursp(), new_sym(s, MRB_SYM_2(s->mrb, __case_eqq)), 1); } else { if (n4) { codegen(s, n4->car, VAL); } else { genop_2(s, OP_GETCONST, cursp(), new_sym(s, MRB_SYM_2(s->mrb, StandardError))); push(); } pop(); genop_2(s, OP_RESCUE, exc, cursp()); } tmp = genjmp2(s, OP_JMPIF, cursp(), pos2, val); pos2 = tmp; if (n4) { n4 = n4->cdr; } } while (n4); pos1 = genjmp_0(s, OP_JMP); dispatch_linked(s, pos2); pop(); if (n3->cdr->car) { gen_assignment(s, n3->cdr->car, NULL, exc, NOVAL); } if (n3->cdr->cdr->car) { codegen(s, n3->cdr->cdr->car, val); if (val) pop(); } tmp = genjmp(s, OP_JMP, exend); exend = tmp; n2 = n2->cdr; push(); } if (pos1 != JMPLINK_START) { dispatch(s, pos1); genop_1(s, OP_RAISEIF, exc); } } pop(); tree = tree->cdr; dispatch(s, noexc); if (tree->car) { codegen(s, tree->car, val); } else if (val) { push(); } dispatch_linked(s, exend); loop_pop(s, NOVAL); } break; case NODE_ENSURE: if (!tree->cdr || !tree->cdr->cdr || (nint(tree->cdr->cdr->car) == NODE_BEGIN && tree->cdr->cdr->cdr)) { int catch_entry, begin, end, target; int idx; catch_entry = catch_handler_new(s); begin = s->pc; codegen(s, tree->car, val); end = target = s->pc; push(); idx = cursp(); genop_1(s, OP_EXCEPT, idx); push(); codegen(s, tree->cdr->cdr, NOVAL); pop(); genop_1(s, OP_RAISEIF, idx); pop(); catch_handler_set(s, catch_entry, MRB_CATCH_ENSURE, begin, end, target); } else { /* empty ensure ignored */ codegen(s, tree->car, val); } break; case NODE_LAMBDA: if (val) { int idx = lambda_body(s, tree, 1); genop_2(s, OP_LAMBDA, cursp(), idx); push(); } break; case NODE_BLOCK: if (val) { int idx = lambda_body(s, tree, 1); genop_2(s, OP_BLOCK, cursp(), idx); push(); } break; case NODE_IF: { uint32_t pos1, pos2; mrb_bool nil_p = FALSE; node *elsepart = tree->cdr->cdr->car; if (!tree->car) { codegen(s, elsepart, val); goto exit; } if (true_always(tree->car)) { codegen(s, tree->cdr->car, val); goto exit; } if (false_always(tree->car)) { codegen(s, elsepart, val); goto exit; } if (nint(tree->car->car) == NODE_CALL) { node *n = tree->car->cdr; mrb_sym mid = nsym(n->cdr->car); mrb_sym sym_nil_p = MRB_SYM_Q_2(s->mrb, nil); if (mid == sym_nil_p && n->cdr->cdr->car == NULL) { nil_p = TRUE; codegen(s, n->car, VAL); } } if (!nil_p) { codegen(s, tree->car, VAL); } pop(); if (val || tree->cdr->car) { if (nil_p) { pos2 = genjmp2_0(s, OP_JMPNIL, cursp(), val); pos1 = genjmp_0(s, OP_JMP); dispatch(s, pos2); } else { pos1 = genjmp2_0(s, OP_JMPNOT, cursp(), val); } codegen(s, tree->cdr->car, val); if (val) pop(); if (elsepart || val) { pos2 = genjmp_0(s, OP_JMP); dispatch(s, pos1); codegen(s, elsepart, val); dispatch(s, pos2); } else { dispatch(s, pos1); } } else { /* empty then-part */ if (elsepart) { if (nil_p) { pos1 = genjmp2_0(s, OP_JMPNIL, cursp(), val); } else { pos1 = genjmp2_0(s, OP_JMPIF, cursp(), val); } codegen(s, elsepart, val); dispatch(s, pos1); } else if (val && !nil_p) { genop_1(s, OP_LOADNIL, cursp()); push(); } } } break; case NODE_AND: { uint32_t pos; if (true_always(tree->car)) { codegen(s, tree->cdr, val); goto exit; } if (false_always(tree->car)) { codegen(s, tree->car, val); goto exit; } codegen(s, tree->car, VAL); pop(); pos = genjmp2_0(s, OP_JMPNOT, cursp(), val); codegen(s, tree->cdr, val); dispatch(s, pos); } break; case NODE_OR: { uint32_t pos; if (true_always(tree->car)) { codegen(s, tree->car, val); goto exit; } if (false_always(tree->car)) { codegen(s, tree->cdr, val); goto exit; } codegen(s, tree->car, VAL); pop(); pos = genjmp2_0(s, OP_JMPIF, cursp(), val); codegen(s, tree->cdr, val); dispatch(s, pos); } break; case NODE_WHILE: case NODE_UNTIL: { if (true_always(tree->car)) { if (nt == NODE_UNTIL) { if (val) { genop_1(s, OP_LOADNIL, cursp()); push(); } goto exit; } } else if (false_always(tree->car)) { if (nt == NODE_WHILE) { if (val) { genop_1(s, OP_LOADNIL, cursp()); push(); } goto exit; } } uint32_t pos = JMPLINK_START; struct loopinfo *lp = loop_push(s, LOOP_NORMAL); if (!val) lp->reg = -1; lp->pc0 = new_label(s); codegen(s, tree->car, VAL); pop(); if (nt == NODE_WHILE) { pos = genjmp2_0(s, OP_JMPNOT, cursp(), NOVAL); } else { pos = genjmp2_0(s, OP_JMPIF, cursp(), NOVAL); } lp->pc1 = new_label(s); genop_0(s, OP_NOP); /* for redo */ codegen(s, tree->cdr, NOVAL); genjmp(s, OP_JMP, lp->pc0); dispatch(s, pos); loop_pop(s, val); } break; case NODE_FOR: for_body(s, tree); if (val) push(); break; case NODE_CASE: { int head = 0; uint32_t pos1, pos2, pos3, tmp; node *n; pos3 = JMPLINK_START; if (tree->car) { head = cursp(); codegen(s, tree->car, VAL); } tree = tree->cdr; while (tree) { n = tree->car->car; pos1 = pos2 = JMPLINK_START; while (n) { codegen(s, n->car, VAL); if (head) { gen_move(s, cursp(), head, 0); push(); push(); pop(); pop(); pop(); if (nint(n->car->car) == NODE_SPLAT) { genop_3(s, OP_SEND, cursp(), new_sym(s, MRB_SYM_2(s->mrb, __case_eqq)), 1); } else { genop_3(s, OP_SEND, cursp(), new_sym(s, MRB_OPSYM_2(s->mrb, eqq)), 1); } } else { pop(); } tmp = genjmp2(s, OP_JMPIF, cursp(), pos2, !head); pos2 = tmp; n = n->cdr; } if (tree->car->car) { pos1 = genjmp_0(s, OP_JMP); dispatch_linked(s, pos2); } codegen(s, tree->car->cdr, val); if (val) pop(); tmp = genjmp(s, OP_JMP, pos3); pos3 = tmp; dispatch(s, pos1); tree = tree->cdr; } if (val) { uint32_t pos = cursp(); genop_1(s, OP_LOADNIL, cursp()); if (pos3 != JMPLINK_START) dispatch_linked(s, pos3); if (head) pop(); if (cursp() != pos) { gen_move(s, cursp(), pos, 0); } push(); } else { if (pos3 != JMPLINK_START) { dispatch_linked(s, pos3); } if (head) { pop(); } } } break; case NODE_SCOPE: scope_body(s, tree, NOVAL); break; case NODE_CALL: case NODE_FCALL: gen_call(s, tree, val, 0); break; case NODE_SCALL: gen_call(s, tree, val, 1); break; case NODE_DOT2: codegen(s, tree->car, val); codegen(s, tree->cdr, val); if (val) { pop(); pop(); genop_1(s, OP_RANGE_INC, cursp()); push(); } break; case NODE_DOT3: codegen(s, tree->car, val); codegen(s, tree->cdr, val); if (val) { pop(); pop(); genop_1(s, OP_RANGE_EXC, cursp()); push(); } break; case NODE_COLON2: { int sym = new_sym(s, nsym(tree->cdr)); codegen(s, tree->car, VAL); pop(); genop_2(s, OP_GETMCNST, cursp(), sym); if (val) push(); } break; case NODE_COLON3: { int sym = new_sym(s, nsym(tree)); genop_1(s, OP_OCLASS, cursp()); genop_2(s, OP_GETMCNST, cursp(), sym); if (val) push(); } break; case NODE_ARRAY: { int n; n = gen_values(s, tree, val, 0); if (val) { if (n >= 0) { pop_n(n); genop_2(s, OP_ARRAY, cursp(), n); } push(); } } break; case NODE_HASH: case NODE_KW_HASH: { int nk = gen_hash(s, tree, val, GEN_LIT_ARY_MAX); if (val && nk >= 0) { pop_n(nk*2); genop_2(s, OP_HASH, cursp(), nk); push(); } } break; case NODE_SPLAT: codegen(s, tree, val); break; case NODE_ASGN: gen_assignment(s, tree->car, tree->cdr, 0, val); break; case NODE_MASGN: { int len = 0, n = 0, post = 0; node *t = tree->cdr, *p; int rhs = cursp(); if (!val && nint(t->car) == NODE_ARRAY && t->cdr && nosplat(t->cdr)) { /* fixed rhs */ t = t->cdr; while (t) { codegen(s, t->car, VAL); len++; t = t->cdr; } tree = tree->car; if (tree->car) { /* pre */ t = tree->car; n = 0; while (t) { if (n < len) { gen_assignment(s, t->car, NULL, rhs+n, NOVAL); n++; } else { genop_1(s, OP_LOADNIL, rhs+n); gen_assignment(s, t->car, NULL, rhs+n, NOVAL); } t = t->cdr; } } t = tree->cdr; if (t) { if (t->cdr) { /* post count */ p = t->cdr->car; while (p) { post++; p = p->cdr; } } if (t->car) { /* rest (len - pre - post) */ int rn; if (len < post + n) { rn = 0; } else { rn = len - post - n; } if (cursp() == rhs+n) { genop_2(s, OP_ARRAY, cursp(), rn); } else { genop_3(s, OP_ARRAY2, cursp(), rhs+n, rn); } gen_assignment(s, t->car, NULL, cursp(), NOVAL); n += rn; } if (t->cdr && t->cdr->car) { t = t->cdr->car; while (t) { if (ncar, NULL, rhs+n, NOVAL); } else { genop_1(s, OP_LOADNIL, cursp()); gen_assignment(s, t->car, NULL, cursp(), NOVAL); } t = t->cdr; n++; } } } pop_n(len); } else { /* variable rhs */ codegen(s, t, VAL); gen_massignment(s, tree->car, rhs, val); if (!val) { pop(); } } } break; case NODE_OP_ASGN: { mrb_sym sym = nsym(tree->cdr->car); mrb_int len; const char *name = mrb_sym_name_len(s->mrb, sym, &len); int idx, callargs = -1, vsp = -1; if ((len == 2 && name[0] == '|' && name[1] == '|') && (nint(tree->car->car) == NODE_CONST || nint(tree->car->car) == NODE_CVAR)) { int catch_entry, begin, end; int noexc, exc; struct loopinfo *lp; lp = loop_push(s, LOOP_BEGIN); lp->pc0 = new_label(s); catch_entry = catch_handler_new(s); begin = s->pc; exc = cursp(); codegen(s, tree->car, VAL); end = s->pc; noexc = genjmp_0(s, OP_JMP); lp->type = LOOP_RESCUE; catch_handler_set(s, catch_entry, MRB_CATCH_RESCUE, begin, end, s->pc); genop_1(s, OP_EXCEPT, exc); genop_1(s, OP_LOADF, exc); dispatch(s, noexc); loop_pop(s, NOVAL); } else if (nint(tree->car->car) == NODE_CALL) { node *n = tree->car->cdr; int base, i, nargs = 0; callargs = 0; if (val) { vsp = cursp(); push(); } codegen(s, n->car, VAL); /* receiver */ idx = new_sym(s, nsym(n->cdr->car)); base = cursp()-1; if (n->cdr->cdr->car) { nargs = gen_values(s, n->cdr->cdr->car->car, VAL, 13); if (nargs >= 0) { callargs = nargs; } else { /* varargs */ push(); nargs = 1; callargs = CALL_MAXARGS; } } /* copy receiver and arguments */ gen_move(s, cursp(), base, 1); for (i=0; icar, VAL); } if (len == 2 && ((name[0] == '|' && name[1] == '|') || (name[0] == '&' && name[1] == '&'))) { uint32_t pos; pop(); if (val) { if (vsp >= 0) { gen_move(s, vsp, cursp(), 1); } pos = genjmp2_0(s, name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), val); } else { pos = genjmp2_0(s, name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), val); } codegen(s, tree->cdr->cdr->car, VAL); pop(); if (val && vsp >= 0) { gen_move(s, vsp, cursp(), 1); } if (nint(tree->car->car) == NODE_CALL) { if (callargs == CALL_MAXARGS) { pop(); genop_2(s, OP_ARYPUSH, cursp(), 1); } else { pop_n(callargs); callargs++; } pop(); idx = new_sym(s, attrsym(s, nsym(tree->car->cdr->cdr->car))); genop_3(s, OP_SEND, cursp(), idx, callargs); } else { gen_assignment(s, tree->car, NULL, cursp(), val); } dispatch(s, pos); goto exit; } codegen(s, tree->cdr->cdr->car, VAL); push(); pop(); pop(); pop(); if (len == 1 && name[0] == '+') { gen_addsub(s, OP_ADD, cursp()); } else if (len == 1 && name[0] == '-') { gen_addsub(s, OP_SUB, cursp()); } else if (len == 1 && name[0] == '*') { genop_1(s, OP_MUL, cursp()); } else if (len == 1 && name[0] == '/') { genop_1(s, OP_DIV, cursp()); } else if (len == 1 && name[0] == '<') { genop_1(s, OP_LT, cursp()); } else if (len == 2 && name[0] == '<' && name[1] == '=') { genop_1(s, OP_LE, cursp()); } else if (len == 1 && name[0] == '>') { genop_1(s, OP_GT, cursp()); } else if (len == 2 && name[0] == '>' && name[1] == '=') { genop_1(s, OP_GE, cursp()); } else { idx = new_sym(s, sym); genop_3(s, OP_SEND, cursp(), idx, 1); } if (callargs < 0) { gen_assignment(s, tree->car, NULL, cursp(), val); } else { if (val && vsp >= 0) { gen_move(s, vsp, cursp(), 0); } if (callargs == CALL_MAXARGS) { pop(); genop_2(s, OP_ARYPUSH, cursp(), 1); } else { pop_n(callargs); callargs++; } pop(); idx = new_sym(s, attrsym(s,nsym(tree->car->cdr->cdr->car))); genop_3(s, OP_SEND, cursp(), idx, callargs); } } break; case NODE_SUPER: { codegen_scope *s2 = s; int lv = 0; int n = 0, nk = 0, st = 0; push(); while (!s2->mscope) { lv++; s2 = s2->prev; if (!s2) break; } if (tree) { node *args = tree->car; if (args) { st = n = gen_values(s, args, VAL, 14); if (n < 0) { st = 1; n = 15; push(); } } /* keyword arguments */ if (tree->cdr->car) { nk = gen_hash(s, tree->cdr->car->cdr, VAL, 14); if (nk < 0) {st++; nk = 15;} else st += nk*2; n |= nk<<4; } /* block arguments */ if (tree->cdr->cdr) { codegen(s, tree->cdr->cdr, VAL); } else if (s2) gen_blkmove(s, s2->ainfo, lv); else { genop_1(s, OP_LOADNIL, cursp()); push(); } } else { if (s2) gen_blkmove(s, s2->ainfo, lv); else { genop_1(s, OP_LOADNIL, cursp()); push(); } } st++; pop_n(st+1); genop_2(s, OP_SUPER, cursp(), n); if (val) push(); } break; case NODE_ZSUPER: { codegen_scope *s2 = s; int lv = 0; uint16_t ainfo = 0; int n = CALL_MAXARGS; int sp = cursp(); push(); /* room for receiver */ while (!s2->mscope) { lv++; s2 = s2->prev; if (!s2) break; } if (s2 && s2->ainfo > 0) { ainfo = s2->ainfo; } if (lv > 0xf) codegen_error(s, "too deep nesting"); if (ainfo > 0) { genop_2S(s, OP_ARGARY, cursp(), (ainfo<<4)|(lv & 0xf)); push(); push(); push(); /* ARGARY pushes 3 values at most */ pop(); pop(); pop(); /* keyword arguments */ if (ainfo & 0x1) { n |= CALL_MAXARGS<<4; push(); } /* block argument */ if (tree && tree->cdr && tree->cdr->cdr) { push(); codegen(s, tree->cdr->cdr, VAL); } } else { /* block argument */ if (tree && tree->cdr && tree->cdr->cdr) { codegen(s, tree->cdr->cdr, VAL); } else if (s2) { gen_blkmove(s, 0, lv); } else { genop_1(s, OP_LOADNIL, cursp()); } n = 0; } s->sp = sp; genop_2(s, OP_SUPER, cursp(), n); if (val) push(); } break; case NODE_RETURN: if (tree) { gen_retval(s, tree); } else { genop_1(s, OP_LOADNIL, cursp()); } if (s->loop) { gen_return(s, OP_RETURN_BLK, cursp()); } else { gen_return(s, OP_RETURN, cursp()); } if (val) push(); break; case NODE_YIELD: { codegen_scope *s2 = s; int lv = 0, ainfo = -1; int n = 0, nk = 0, sendv = 0; while (!s2->mscope) { lv++; s2 = s2->prev; if (!s2) break; } if (s2) { ainfo = (int)s2->ainfo; } if (ainfo < 0) codegen_error(s, "invalid yield (SyntaxError)"); if (lv > 0xf) codegen_error(s, "too deep nesting"); push(); if (tree) { if (tree->car) { n = gen_values(s, tree->car, VAL, 14); if (n < 0) { n = sendv = 1; push(); } } if (tree->cdr->car) { nk = gen_hash(s, tree->cdr->car->cdr, VAL, 14); if (nk < 0) { nk = 15; } } } push();pop(); /* space for a block */ pop_n(n + (nk == 15 ? 1 : nk * 2) + 1); genop_2S(s, OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf)); if (sendv) n = CALL_MAXARGS; genop_3(s, OP_SEND, cursp(), new_sym(s, MRB_SYM_2(s->mrb, call)), n|(nk<<4)); if (val) push(); } break; case NODE_BREAK: loop_break(s, tree); if (val) push(); break; case NODE_NEXT: if (!s->loop) { raise_error(s, "unexpected next"); } else if (s->loop->type == LOOP_NORMAL) { codegen(s, tree, NOVAL); genjmp(s, OP_JMPUW, s->loop->pc0); } else { if (tree) { codegen(s, tree, VAL); pop(); } else { genop_1(s, OP_LOADNIL, cursp()); } gen_return(s, OP_RETURN, cursp()); } if (val) push(); break; case NODE_REDO: for (const struct loopinfo *lp = s->loop; ; lp = lp->prev) { if (!lp) { raise_error(s, "unexpected redo"); break; } if (lp->type != LOOP_BEGIN && lp->type != LOOP_RESCUE) { genjmp(s, OP_JMPUW, lp->pc1); break; } } if (val) push(); break; case NODE_RETRY: { const struct loopinfo *lp = s->loop; while (lp && lp->type != LOOP_RESCUE) { lp = lp->prev; } if (!lp) { raise_error(s, "unexpected retry"); break; } else { genjmp(s, OP_JMPUW, lp->pc0); } if (val) push(); } break; case NODE_LVAR: if (val) { int idx = lv_idx(s, nsym(tree)); if (idx > 0) { gen_move(s, cursp(), idx, val); } else { gen_getupvar(s, cursp(), nsym(tree)); } push(); } break; case NODE_NVAR: if (val) { int idx = nint(tree); gen_move(s, cursp(), idx, val); push(); } break; case NODE_GVAR: { int sym = new_sym(s, nsym(tree)); genop_2(s, OP_GETGV, cursp(), sym); if (val) push(); } break; case NODE_IVAR: { int sym = new_sym(s, nsym(tree)); genop_2(s, OP_GETIV, cursp(), sym); if (val) push(); } break; case NODE_CVAR: { int sym = new_sym(s, nsym(tree)); genop_2(s, OP_GETCV, cursp(), sym); if (val) push(); } break; case NODE_CONST: { int sym = new_sym(s, nsym(tree)); genop_2(s, OP_GETCONST, cursp(), sym); if (val) push(); } break; case NODE_BACK_REF: if (val) { char buf[] = {'$', nchar(tree)}; int sym = new_sym(s, mrb_intern(s->mrb, buf, sizeof(buf))); genop_2(s, OP_GETGV, cursp(), sym); push(); } break; case NODE_NTH_REF: if (val) { mrb_state *mrb = s->mrb; mrb_value str; int sym; str = mrb_format(mrb, "$%d", nint(tree)); sym = new_sym(s, mrb_intern_str(mrb, str)); genop_2(s, OP_GETGV, cursp(), sym); push(); } break; case NODE_ARG: /* should not happen */ break; case NODE_BLOCK_ARG: if (!tree) { int idx = lv_idx(s, MRB_OPSYM_2(s->mrb, and)); if (idx == 0) { gen_getupvar(s, cursp(), MRB_OPSYM_2(s->mrb, and)); } else { gen_move(s, cursp(), idx, val); } if (val) push(); } else { codegen(s, tree, val); } break; case NODE_INT: if (val) { char *p = (char*)tree->car; int base = nint(tree->cdr->car); mrb_int i; mrb_bool overflow; i = readint(s, p, base, FALSE, &overflow); if (overflow) { int off = new_litbint(s, p, base); genop_2(s, OP_LOADL, cursp(), off); } else { gen_int(s, cursp(), i); } push(); } break; #ifndef MRB_NO_FLOAT case NODE_FLOAT: if (val) { char *p = (char*)tree; double f; mrb_read_float(p, NULL, &f); int off = new_lit_float(s, (mrb_float)f); genop_2(s, OP_LOADL, cursp(), off); push(); } break; #endif case NODE_NEGATE: { nt = nint(tree->car); switch (nt) { #ifndef MRB_NO_FLOAT case NODE_FLOAT: if (val) { char *p = (char*)tree->cdr; double f; mrb_read_float(p, NULL, &f); int off = new_lit_float(s, (mrb_float)-f); genop_2(s, OP_LOADL, cursp(), off); push(); } break; #endif case NODE_INT: if (val) { char *p = (char*)tree->cdr->car; int base = nint(tree->cdr->cdr->car); mrb_int i; mrb_bool overflow; i = readint(s, p, base, TRUE, &overflow); if (overflow) { base = -base; int off = new_litbint(s, p, base); genop_2(s, OP_LOADL, cursp(), off); } else { gen_int(s, cursp(), i); } push(); } break; default: codegen(s, tree, VAL); pop(); push_n(2);pop_n(2); /* space for receiver&block */ mrb_sym minus = MRB_OPSYM_2(s->mrb, minus); if (!gen_uniop(s, minus, cursp())) { genop_3(s, OP_SEND, cursp(), new_sym(s, minus), 0); } if (val) push(); break; } } break; case NODE_STR: if (val) { char *p = (char*)tree->car; mrb_int len = nint(tree->cdr); int off = new_lit_str(s, p, len); genop_2(s, OP_STRING, cursp(), off); push(); } break; case NODE_HEREDOC: tree = ((struct mrb_parser_heredoc_info*)tree)->doc; /* fall through */ case NODE_DSTR: if (val) { node *n = tree; if (!n) { genop_1(s, OP_LOADNIL, cursp()); push(); break; } codegen(s, n->car, VAL); n = n->cdr; while (n) { codegen(s, n->car, VAL); pop(); pop(); genop_1(s, OP_STRCAT, cursp()); push(); n = n->cdr; } } else { node *n = tree; while (n) { if (nint(n->car->car) != NODE_STR) { codegen(s, n->car, NOVAL); } n = n->cdr; } } break; case NODE_WORDS: gen_literal_array(s, tree, FALSE, val); break; case NODE_SYMBOLS: gen_literal_array(s, tree, TRUE, val); break; case NODE_DXSTR: { node *n; int sym = new_sym(s, MRB_SYM_2(s->mrb, Kernel)); push(); codegen(s, tree->car, VAL); n = tree->cdr; while (n) { if (nint(n->car->car) == NODE_XSTR) { n->car->car = (struct mrb_ast_node*)(intptr_t)NODE_STR; mrb_assert(!n->cdr); /* must be the end */ } codegen(s, n->car, VAL); pop(); pop(); genop_1(s, OP_STRCAT, cursp()); push(); n = n->cdr; } push(); /* for block */ pop_n(3); sym = new_sym(s, MRB_OPSYM_2(s->mrb, tick)); /* ` */ genop_3(s, OP_SSEND, cursp(), sym, 1); if (val) push(); } break; case NODE_XSTR: { char *p = (char*)tree->car; mrb_int len = nint(tree->cdr); int off = new_lit_str(s, p, len); int sym; push(); genop_2(s, OP_STRING, cursp(), off); push(); push(); pop_n(3); sym = new_sym(s, MRB_OPSYM_2(s->mrb, tick)); /* ` */ genop_3(s, OP_SSEND, cursp(), sym, 1); if (val) push(); } break; case NODE_REGX: if (val) { char *p1 = (char*)tree->car; char *p2 = (char*)tree->cdr->car; char *p3 = (char*)tree->cdr->cdr; int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS)); int off = new_lit_cstr(s, p1); int argc = 1; genop_1(s, OP_OCLASS, cursp()); genop_2(s, OP_GETMCNST, cursp(), sym); push(); genop_2(s, OP_STRING, cursp(), off); push(); if (p2 || p3) { if (p2) { /* opt */ off = new_lit_cstr(s, p2); genop_2(s, OP_STRING, cursp(), off); } else { genop_1(s, OP_LOADNIL, cursp()); } push(); argc++; if (p3) { /* enc */ off = new_lit_str(s, p3, 1); genop_2(s, OP_STRING, cursp(), off); push(); argc++; } } push(); /* space for a block */ pop_n(argc+2); sym = new_sym(s, MRB_SYM_2(s->mrb, compile)); genop_3(s, OP_SEND, cursp(), sym, argc); push(); } break; case NODE_DREGX: if (val) { node *n = tree->car; int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS)); int argc = 1; int off; char *p; genop_1(s, OP_OCLASS, cursp()); genop_2(s, OP_GETMCNST, cursp(), sym); push(); codegen(s, n->car, VAL); n = n->cdr; while (n) { codegen(s, n->car, VAL); pop(); pop(); genop_1(s, OP_STRCAT, cursp()); push(); n = n->cdr; } n = tree->cdr->cdr; if (n->car) { /* tail */ p = (char*)n->car; off = new_lit_cstr(s, p); codegen(s, tree->car, VAL); genop_2(s, OP_STRING, cursp(), off); pop(); genop_1(s, OP_STRCAT, cursp()); push(); } if (n->cdr->car) { /* opt */ char *p2 = (char*)n->cdr->car; off = new_lit_cstr(s, p2); genop_2(s, OP_STRING, cursp(), off); push(); argc++; } if (n->cdr->cdr) { /* enc */ char *p2 = (char*)n->cdr->cdr; off = new_lit_cstr(s, p2); genop_2(s, OP_STRING, cursp(), off); push(); argc++; } push(); /* space for a block */ pop_n(argc+2); sym = new_sym(s, MRB_SYM_2(s->mrb, compile)); genop_3(s, OP_SEND, cursp(), sym, argc); push(); } else { node *n = tree->car; while (n) { if (nint(n->car->car) != NODE_STR) { codegen(s, n->car, NOVAL); } n = n->cdr; } } break; case NODE_SYM: if (val) { int sym = new_sym(s, nsym(tree)); genop_2(s, OP_LOADSYM, cursp(), sym); push(); } break; case NODE_DSYM: codegen(s, tree, val); if (val) { gen_intern(s); } break; case NODE_SELF: if (val) { genop_1(s, OP_LOADSELF, cursp()); push(); } break; case NODE_NIL: if (val) { genop_1(s, OP_LOADNIL, cursp()); push(); } break; case NODE_TRUE: if (val) { genop_1(s, OP_LOADT, cursp()); push(); } break; case NODE_FALSE: if (val) { genop_1(s, OP_LOADF, cursp()); push(); } break; case NODE_ALIAS: { int a = new_sym(s, nsym(tree->car)); int b = new_sym(s, nsym(tree->cdr)); genop_2(s, OP_ALIAS, a, b); if (val) { genop_1(s, OP_LOADNIL, cursp()); push(); } } break; case NODE_UNDEF: { node *t = tree; while (t) { int symbol = new_sym(s, nsym(t->car)); genop_1(s, OP_UNDEF, symbol); t = t->cdr; } if (val) { genop_1(s, OP_LOADNIL, cursp()); push(); } } break; case NODE_CLASS: { int idx; node *body; if (tree->car->car == (node*)0) { genop_1(s, OP_LOADNIL, cursp()); push(); } else if (tree->car->car == (node*)1) { genop_1(s, OP_OCLASS, cursp()); push(); } else { codegen(s, tree->car->car, VAL); } if (tree->cdr->car) { codegen(s, tree->cdr->car, VAL); } else { genop_1(s, OP_LOADNIL, cursp()); push(); } pop(); pop(); idx = new_sym(s, nsym(tree->car->cdr)); genop_2(s, OP_CLASS, cursp(), idx); body = tree->cdr->cdr->car; if (nint(body->cdr->car) == NODE_BEGIN && body->cdr->cdr == NULL) { genop_1(s, OP_LOADNIL, cursp()); } else { idx = scope_body(s, body, val); genop_2(s, OP_EXEC, cursp(), idx); } if (val) { push(); } } break; case NODE_MODULE: { int idx; if (tree->car->car == (node*)0) { genop_1(s, OP_LOADNIL, cursp()); push(); } else if (tree->car->car == (node*)1) { genop_1(s, OP_OCLASS, cursp()); push(); } else { codegen(s, tree->car->car, VAL); } pop(); idx = new_sym(s, nsym(tree->car->cdr)); genop_2(s, OP_MODULE, cursp(), idx); if (nint(tree->cdr->car->cdr->car) == NODE_BEGIN && tree->cdr->car->cdr->cdr == NULL) { genop_1(s, OP_LOADNIL, cursp()); } else { idx = scope_body(s, tree->cdr->car, val); genop_2(s, OP_EXEC, cursp(), idx); } if (val) { push(); } } break; case NODE_SCLASS: { int idx; codegen(s, tree->car, VAL); pop(); genop_1(s, OP_SCLASS, cursp()); if (nint(tree->cdr->car->cdr->car) == NODE_BEGIN && tree->cdr->car->cdr->cdr == NULL) { genop_1(s, OP_LOADNIL, cursp()); } else { idx = scope_body(s, tree->cdr->car, val); genop_2(s, OP_EXEC, cursp(), idx); } if (val) { push(); } } break; case NODE_DEF: { int sym = new_sym(s, nsym(tree->car)); int idx = lambda_body(s, tree->cdr, 0); genop_1(s, OP_TCLASS, cursp()); push(); genop_2(s, OP_METHOD, cursp(), idx); push(); pop(); pop(); genop_2(s, OP_DEF, cursp(), sym); if (val) push(); } break; case NODE_SDEF: { node *recv = tree->car; int sym = new_sym(s, nsym(tree->cdr->car)); int idx = lambda_body(s, tree->cdr->cdr, 0); codegen(s, recv, VAL); pop(); genop_1(s, OP_SCLASS, cursp()); push(); genop_2(s, OP_METHOD, cursp(), idx); push(); pop(); pop(); genop_2(s, OP_DEF, cursp(), sym); if (val) push(); } break; case NODE_POSTEXE: codegen(s, tree, NOVAL); break; default: break; } exit: s->rlev = rlev; } static void scope_add_irep(codegen_scope *s) { mrb_irep *irep; codegen_scope *prev = s->prev; if (prev->irep == NULL) { irep = mrb_add_irep(s->mrb); prev->irep = s->irep = irep; return; } else { if (prev->irep->rlen == UINT16_MAX) { codegen_error(s, "too many nested blocks/methods"); } s->irep = irep = mrb_add_irep(s->mrb); if (prev->irep->rlen == prev->rcapa) { prev->rcapa *= 2; prev->reps = (mrb_irep**)mrbc_realloc(prev->reps, sizeof(mrb_irep*)*prev->rcapa); } prev->reps[prev->irep->rlen] = irep; prev->irep->rlen++; } } static codegen_scope* scope_new(mrb_state *mrb, codegen_scope *prev, node *nlv) { static const codegen_scope codegen_scope_zero = { 0 }; mempool *pool = mempool_open(); codegen_scope *s = (codegen_scope*)mempool_alloc(pool, sizeof(codegen_scope)); if (!s) { if (prev) codegen_error(prev, "unexpected scope"); return NULL; } *s = codegen_scope_zero; s->mrb = mrb; s->mpool = pool; if (!prev) return s; s->prev = prev; s->ainfo = 0; s->mscope = 0; scope_add_irep(s); s->rcapa = 8; s->reps = (mrb_irep**)mrbc_malloc(sizeof(mrb_irep*)*s->rcapa); s->icapa = 1024; s->iseq = (mrb_code*)mrbc_malloc(sizeof(mrb_code)*s->icapa); s->pcapa = 32; s->pool = (mrb_irep_pool*)mrbc_malloc(sizeof(mrb_irep_pool)*s->pcapa); s->scapa = 256; s->syms = (mrb_sym*)mrbc_malloc(sizeof(mrb_sym)*s->scapa); s->lv = nlv; s->sp += node_len(nlv)+1; /* add self */ s->nlocals = s->nregs = s->sp; if (nlv) { mrb_sym *lv; node *n = nlv; size_t i = 0; s->irep->lv = lv = (mrb_sym*)mrbc_malloc(sizeof(mrb_sym)*(s->nlocals-1)); for (i=0, n=nlv; n; i++,n=n->cdr) { lv[i] = lv_name(n); } mrb_assert(i + 1 == s->nlocals); } s->ai = mrb_gc_arena_save(mrb); s->filename_sym = prev->filename_sym; if (s->filename_sym) { s->lines = (uint16_t*)mrbc_malloc(sizeof(short)*s->icapa); } s->lineno = prev->lineno; /* debug setting */ s->debug_start_pos = 0; if (s->filename_sym) { mrb_debug_info_alloc(mrb, s->irep); } else { s->irep->debug_info = NULL; } s->parser = prev->parser; s->filename_index = prev->filename_index; s->rlev = prev->rlev+1; return s; } static void scope_finish(codegen_scope *s) { mrb_state *mrb = s->mrb; mrb_irep *irep = s->irep; if (s->nlocals > 0xff) { codegen_error(s, "too many local variables"); } irep->flags = 0; if (s->iseq) { size_t catchsize = sizeof(struct mrb_irep_catch_handler) * irep->clen; irep->iseq = (const mrb_code*)mrbc_realloc(s->iseq, sizeof(mrb_code)*s->pc + catchsize); irep->ilen = s->pc; if (irep->clen > 0) { memcpy((void*)(irep->iseq + irep->ilen), s->catch_table, catchsize); } } else { irep->clen = 0; } mrbc_free(s->catch_table); s->catch_table = NULL; irep->pool = (const mrb_irep_pool*)mrbc_realloc(s->pool, sizeof(mrb_irep_pool)*irep->plen); irep->syms = (const mrb_sym*)mrbc_realloc(s->syms, sizeof(mrb_sym)*irep->slen); irep->reps = (const mrb_irep**)mrbc_realloc(s->reps, sizeof(mrb_irep*)*irep->rlen); if (s->filename_sym) { mrb_sym fname = mrb_parser_get_filename(s->parser, s->filename_index); const char *filename = mrb_sym_name_len(s->mrb, fname, NULL); mrb_debug_info_append_file(s->mrb, s->irep->debug_info, filename, s->lines, s->debug_start_pos, s->pc); } mrbc_free(s->lines); irep->nlocals = s->nlocals; irep->nregs = s->nregs; mrb_gc_arena_restore(mrb, s->ai); mempool_close(s->mpool); } static struct loopinfo* loop_push(codegen_scope *s, enum looptype t) { struct loopinfo *p = (struct loopinfo*)codegen_palloc(s, sizeof(struct loopinfo)); p->type = t; p->pc0 = p->pc1 = p->pc2 = JMPLINK_START; p->prev = s->loop; p->reg = cursp(); s->loop = p; return p; } static void loop_break(codegen_scope *s, node *tree) { if (!s->loop) { codegen(s, tree, NOVAL); raise_error(s, "unexpected break"); } else { struct loopinfo *loop; loop = s->loop; if (tree) { if (loop->reg < 0) { codegen(s, tree, NOVAL); } else { gen_retval(s, tree); } } while (loop) { if (loop->type == LOOP_BEGIN) { loop = loop->prev; } else if (loop->type == LOOP_RESCUE) { loop = loop->prev; } else{ break; } } if (!loop) { raise_error(s, "unexpected break"); return; } if (loop->type == LOOP_NORMAL) { int tmp; if (loop->reg >= 0) { if (tree) { gen_move(s, loop->reg, cursp(), 0); } else { genop_1(s, OP_LOADNIL, loop->reg); } } tmp = genjmp(s, OP_JMPUW, loop->pc2); loop->pc2 = tmp; } else { if (!tree) { genop_1(s, OP_LOADNIL, cursp()); } gen_return(s, OP_BREAK, cursp()); } } } static void loop_pop(codegen_scope *s, int val) { if (val) { genop_1(s, OP_LOADNIL, cursp()); } dispatch_linked(s, s->loop->pc2); s->loop = s->loop->prev; if (val) push(); } static int catch_handler_new(codegen_scope *s) { size_t newsize = sizeof(struct mrb_irep_catch_handler) * (s->irep->clen + 1); s->catch_table = (struct mrb_irep_catch_handler*)mrbc_realloc((void*)s->catch_table, newsize); return s->irep->clen++; } static void catch_handler_set(codegen_scope *s, int ent, enum mrb_catch_type type, uint32_t begin, uint32_t end, uint32_t target) { struct mrb_irep_catch_handler *e; mrb_assert(ent >= 0 && ent < s->irep->clen); e = &s->catch_table[ent]; uint8_to_bin(type, &e->type); mrb_irep_catch_handler_pack(begin, e->begin); mrb_irep_catch_handler_pack(end, e->end); mrb_irep_catch_handler_pack(target, e->target); } static struct RProc* generate_code(mrb_state *mrb, parser_state *p, int val) { codegen_scope *scope = scope_new(mrb, 0, 0); struct mrb_jmpbuf *prev_jmp = mrb->jmp; struct mrb_jmpbuf jmpbuf; struct RProc *proc; mrb->jmp = &jmpbuf; scope->mrb = mrb; scope->parser = p; scope->filename_sym = p->filename_sym; scope->filename_index = p->current_filename_index; MRB_TRY(mrb->jmp) { /* prepare irep */ codegen(scope, p->tree, val); proc = mrb_proc_new(mrb, scope->irep); mrb_irep_decref(mrb, scope->irep); mempool_close(scope->mpool); proc->c = NULL; if (mrb->c->cibase && mrb->c->cibase->proc == proc->upper) { proc->upper = NULL; } mrb->jmp = prev_jmp; return proc; } MRB_CATCH(mrb->jmp) { mrb_irep_decref(mrb, scope->irep); mempool_close(scope->mpool); mrb->jmp = prev_jmp; return NULL; } MRB_END_EXC(mrb->jmp); } MRB_API struct RProc* mrb_generate_code(mrb_state *mrb, parser_state *p) { return generate_code(mrb, p, VAL); } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compiler/core/PaxHeaders/lex.def0000644000000000000000000000013215077107276024475 xustar0030 mtime=1761382078.110420611 30 atime=1761382080.129411375 30 ctime=1761382108.696301549 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compiler/core/lex.def0000644000175100017510000002313215077107276025066 0ustar00runnerrunner/* ANSI-C code produced by gperf version 3.1 */ /* Command-line: gperf -L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k'1,3,$' mrbgems/mruby-compiler/core/keywords */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ #error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif #line 1 "mrbgems/mruby-compiler/core/keywords" /* Workaround for `enable_cxx_exception` (#5199) */ #if defined __cplusplus && __cplusplus >= 201103L # define register #endif struct kwtable {const char *name; int id[2]; enum mrb_lex_state_enum state;}; #line 10 "mrbgems/mruby-compiler/core/keywords" struct kwtable; #define TOTAL_KEYWORDS 40 #define MIN_WORD_LENGTH 2 #define MAX_WORD_LENGTH 12 #define MIN_HASH_VALUE 8 #define MAX_HASH_VALUE 50 /* maximum key range = 43, duplicates = 0 */ #ifdef __GNUC__ __inline #else #ifdef __cplusplus inline #endif #endif static unsigned int hash (register const char *str, register size_t len) { static const unsigned char asso_values[] = { 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 14, 51, 16, 8, 11, 13, 51, 51, 51, 51, 10, 51, 13, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 11, 51, 13, 1, 26, 4, 1, 8, 28, 51, 23, 51, 1, 1, 27, 5, 19, 21, 51, 8, 3, 3, 11, 51, 21, 24, 16, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51 }; register unsigned int hval = len; switch (hval) { default: hval += asso_values[(unsigned char)str[2]]; /*FALLTHROUGH*/ case 2: case 1: hval += asso_values[(unsigned char)str[0]]; break; } return hval + asso_values[(unsigned char)str[len - 1]]; } const struct kwtable * mrb_reserved_word (register const char *str, register size_t len) { static const struct kwtable wordlist[] = { {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, #line 20 "mrbgems/mruby-compiler/core/keywords" {"break", {keyword_break, keyword_break}, EXPR_MID}, #line 25 "mrbgems/mruby-compiler/core/keywords" {"else", {keyword_else, keyword_else}, EXPR_BEG}, #line 35 "mrbgems/mruby-compiler/core/keywords" {"nil", {keyword_nil, keyword_nil}, EXPR_END}, #line 28 "mrbgems/mruby-compiler/core/keywords" {"ensure", {keyword_ensure, keyword_ensure}, EXPR_BEG}, #line 27 "mrbgems/mruby-compiler/core/keywords" {"end", {keyword_end, keyword_end}, EXPR_END}, #line 44 "mrbgems/mruby-compiler/core/keywords" {"then", {keyword_then, keyword_then}, EXPR_BEG}, #line 36 "mrbgems/mruby-compiler/core/keywords" {"not", {keyword_not, keyword_not}, EXPR_ARG}, #line 29 "mrbgems/mruby-compiler/core/keywords" {"false", {keyword_false, keyword_false}, EXPR_END}, #line 42 "mrbgems/mruby-compiler/core/keywords" {"self", {keyword_self, keyword_self}, EXPR_END}, #line 26 "mrbgems/mruby-compiler/core/keywords" {"elsif", {keyword_elsif, keyword_elsif}, EXPR_VALUE}, #line 39 "mrbgems/mruby-compiler/core/keywords" {"rescue", {keyword_rescue, modifier_rescue}, EXPR_MID}, #line 45 "mrbgems/mruby-compiler/core/keywords" {"true", {keyword_true, keyword_true}, EXPR_END}, #line 48 "mrbgems/mruby-compiler/core/keywords" {"until", {keyword_until, modifier_until}, EXPR_VALUE}, #line 47 "mrbgems/mruby-compiler/core/keywords" {"unless", {keyword_unless, modifier_unless}, EXPR_VALUE}, #line 41 "mrbgems/mruby-compiler/core/keywords" {"return", {keyword_return, keyword_return}, EXPR_MID}, #line 23 "mrbgems/mruby-compiler/core/keywords" {"def", {keyword_def, keyword_def}, EXPR_FNAME}, #line 18 "mrbgems/mruby-compiler/core/keywords" {"and", {keyword_and, keyword_and}, EXPR_VALUE}, #line 24 "mrbgems/mruby-compiler/core/keywords" {"do", {keyword_do, keyword_do}, EXPR_BEG}, #line 51 "mrbgems/mruby-compiler/core/keywords" {"yield", {keyword_yield, keyword_yield}, EXPR_ARG}, #line 30 "mrbgems/mruby-compiler/core/keywords" {"for", {keyword_for, keyword_for}, EXPR_VALUE}, #line 46 "mrbgems/mruby-compiler/core/keywords" {"undef", {keyword_undef, keyword_undef}, EXPR_FNAME}, #line 37 "mrbgems/mruby-compiler/core/keywords" {"or", {keyword_or, keyword_or}, EXPR_VALUE}, #line 32 "mrbgems/mruby-compiler/core/keywords" {"in", {keyword_in, keyword_in}, EXPR_VALUE}, #line 49 "mrbgems/mruby-compiler/core/keywords" {"when", {keyword_when, keyword_when}, EXPR_VALUE}, #line 40 "mrbgems/mruby-compiler/core/keywords" {"retry", {keyword_retry, keyword_retry}, EXPR_END}, #line 31 "mrbgems/mruby-compiler/core/keywords" {"if", {keyword_if, modifier_if}, EXPR_VALUE}, #line 21 "mrbgems/mruby-compiler/core/keywords" {"case", {keyword_case, keyword_case}, EXPR_VALUE}, #line 38 "mrbgems/mruby-compiler/core/keywords" {"redo", {keyword_redo, keyword_redo}, EXPR_END}, #line 34 "mrbgems/mruby-compiler/core/keywords" {"next", {keyword_next, keyword_next}, EXPR_MID}, #line 43 "mrbgems/mruby-compiler/core/keywords" {"super", {keyword_super, keyword_super}, EXPR_ARG}, #line 33 "mrbgems/mruby-compiler/core/keywords" {"module", {keyword_module, keyword_module}, EXPR_VALUE}, #line 19 "mrbgems/mruby-compiler/core/keywords" {"begin", {keyword_begin, keyword_begin}, EXPR_BEG}, #line 14 "mrbgems/mruby-compiler/core/keywords" {"__LINE__", {keyword__LINE__, keyword__LINE__}, EXPR_END}, #line 13 "mrbgems/mruby-compiler/core/keywords" {"__FILE__", {keyword__FILE__, keyword__FILE__}, EXPR_END}, #line 12 "mrbgems/mruby-compiler/core/keywords" {"__ENCODING__", {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END}, #line 16 "mrbgems/mruby-compiler/core/keywords" {"END", {keyword_END, keyword_END}, EXPR_END}, #line 17 "mrbgems/mruby-compiler/core/keywords" {"alias", {keyword_alias, keyword_alias}, EXPR_FNAME}, #line 15 "mrbgems/mruby-compiler/core/keywords" {"BEGIN", {keyword_BEGIN, keyword_BEGIN}, EXPR_END}, {""}, #line 22 "mrbgems/mruby-compiler/core/keywords" {"class", {keyword_class, keyword_class}, EXPR_CLASS}, {""}, {""}, #line 50 "mrbgems/mruby-compiler/core/keywords" {"while", {keyword_while, modifier_while}, EXPR_VALUE} }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register unsigned int key = hash (str, len); if (key <= MAX_HASH_VALUE) { register const char *s = wordlist[key].name; if (*str == *s && !strcmp (str + 1, s + 1)) return &wordlist[key]; } } return 0; } #line 52 "mrbgems/mruby-compiler/core/keywords" nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compiler/core/PaxHeaders/y.tab.c0000644000000000000000000000013215077107276024406 xustar0030 mtime=1761382078.114420593 30 atime=1761382080.131411366 30 ctime=1761382108.691301563 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compiler/core/y.tab.c0000644000175100017510000216066415077107276025015 0ustar00runnerrunner/* A Bison parser, made by Lrama 0.7.0. */ /* Bison implementation for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, especially those whose name start with YY_ or yy_. They are private implementation details that can be changed or removed. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output, and Bison version. */ #define YYBISON 30802 /* Bison version string. */ #define YYBISON_VERSION "3.8.2" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 1 /* Push parsers. */ #define YYPUSH 0 /* Pull parsers. */ #define YYPULL 1 /* First part of user prologue. */ #line 7 "mrbgems/mruby-compiler/core/parse.y" #undef PARSER_DEBUG #ifdef PARSER_DEBUG # define YYDEBUG 1 #endif #define YYSTACK_USE_ALLOCA 1 #include #include #include #include #include #include #include #include #include #include #include #include #include "node.h" #define YYLEX_PARAM p #define mrbc_malloc(s) mrb_basic_alloc_func(NULL,(s)) #define mrbc_realloc(p,s) mrb_basic_alloc_func((p),(s)) #define mrbc_free(p) mrb_basic_alloc_func((p),0) typedef mrb_ast_node node; typedef struct mrb_parser_state parser_state; typedef struct mrb_parser_heredoc_info parser_heredoc_info; static int yyparse(parser_state *p); static int yylex(void *lval, void *lp, parser_state *p); static void yyerror(void *lp, parser_state *p, const char *s); static void yywarning(parser_state *p, const char *s); static void backref_error(parser_state *p, node *n); static void void_expr_error(parser_state *p, node *n); static void tokadd(parser_state *p, int32_t c); #define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c)) typedef unsigned int stack_type; #define BITSTACK_PUSH(stack, n) ((stack) = ((stack)<<1)|((n)&1)) #define BITSTACK_POP(stack) ((stack) = (stack) >> 1) #define BITSTACK_LEXPOP(stack) ((stack) = ((stack) >> 1) | ((stack) & 1)) #define BITSTACK_SET_P(stack) ((stack)&1) #define COND_PUSH(n) BITSTACK_PUSH(p->cond_stack, (n)) #define COND_POP() BITSTACK_POP(p->cond_stack) #define COND_LEXPOP() BITSTACK_LEXPOP(p->cond_stack) #define COND_P() BITSTACK_SET_P(p->cond_stack) #define CMDARG_PUSH(n) BITSTACK_PUSH(p->cmdarg_stack, (n)) #define CMDARG_POP() BITSTACK_POP(p->cmdarg_stack) #define CMDARG_LEXPOP() BITSTACK_LEXPOP(p->cmdarg_stack) #define CMDARG_P() BITSTACK_SET_P(p->cmdarg_stack) #define SET_LINENO(c,n) ((c)->lineno = (n)) #define NODE_LINENO(c,n) do {\ if (n) {\ (c)->filename_index = (n)->filename_index;\ (c)->lineno = (n)->lineno;\ }\ } while (0) #define sym(x) ((mrb_sym)(intptr_t)(x)) #define nsym(x) ((node*)(intptr_t)(x)) #define nint(x) ((node*)(intptr_t)(x)) #define intn(x) ((int)(intptr_t)(x)) #define typen(x) ((enum node_type)(intptr_t)(x)) #define NUM_SUFFIX_R (1<<0) #define NUM_SUFFIX_I (1<<1) static inline mrb_sym intern_cstr_gen(parser_state *p, const char *s) { return mrb_intern_cstr(p->mrb, s); } #define intern_cstr(s) intern_cstr_gen(p,(s)) static inline mrb_sym intern_gen(parser_state *p, const char *s, size_t len) { return mrb_intern(p->mrb, s, len); } #define intern(s,len) intern_gen(p,(s),(len)) #define intern_op(op) MRB_OPSYM_2(p->mrb, op) static mrb_sym intern_numparam_gen(parser_state *p, int num) { char buf[3]; buf[0] = '_'; buf[1] = '0'+num; buf[2] = '\0'; return intern(buf, 2); } #define intern_numparam(n) intern_numparam_gen(p,(n)) static void cons_free_gen(parser_state *p, node *cons) { cons->cdr = p->cells; p->cells = cons; } #define cons_free(c) cons_free_gen(p, (c)) static void* parser_palloc(parser_state *p, size_t size) { void *m = mempool_alloc(p->pool, size); if (!m) { MRB_THROW(p->mrb->jmp); } return m; } #define parser_pfree(ptr) do { if (sizeof(node) <= sizeof(*(ptr))) cons_free((node*)ptr);} while (0) static node* cons_gen(parser_state *p, node *car, node *cdr) { node *c; if (p->cells) { c = p->cells; p->cells = p->cells->cdr; } else { c = (node*)parser_palloc(p, sizeof(mrb_ast_node)); } c->car = car; c->cdr = cdr; c->lineno = p->lineno; c->filename_index = p->current_filename_index; /* beginning of next partial file; need to point the previous file */ if (p->lineno == 0 && p->current_filename_index > 0) { c->filename_index--; } return c; } #define cons(a,b) cons_gen(p,(a),(b)) static node* list1_gen(parser_state *p, node *a) { return cons(a, 0); } #define list1(a) list1_gen(p, (a)) static node* list2_gen(parser_state *p, node *a, node *b) { return cons(a, cons(b,0)); } #define list2(a,b) list2_gen(p, (a),(b)) static node* list3_gen(parser_state *p, node *a, node *b, node *c) { return cons(a, cons(b, cons(c,0))); } #define list3(a,b,c) list3_gen(p, (a),(b),(c)) static node* list4_gen(parser_state *p, node *a, node *b, node *c, node *d) { return cons(a, cons(b, cons(c, cons(d, 0)))); } #define list4(a,b,c,d) list4_gen(p, (a),(b),(c),(d)) static node* list5_gen(parser_state *p, node *a, node *b, node *c, node *d, node *e) { return cons(a, cons(b, cons(c, cons(d, cons(e, 0))))); } #define list5(a,b,c,d,e) list5_gen(p, (a),(b),(c),(d),(e)) static node* list6_gen(parser_state *p, node *a, node *b, node *c, node *d, node *e, node *f) { return cons(a, cons(b, cons(c, cons(d, cons(e, cons(f, 0)))))); } #define list6(a,b,c,d,e,f) list6_gen(p, (a),(b),(c),(d),(e),(f)) static node* append_gen(parser_state *p, node *a, node *b) { node *c = a; if (!a) return b; if (!b) return a; while (c->cdr) { c = c->cdr; } c->cdr = b; return a; } #define append(a,b) append_gen(p,(a),(b)) #define push(a,b) append_gen(p,(a),list1(b)) static char* parser_strndup(parser_state *p, const char *s, size_t len) { char *b = (char*)parser_palloc(p, len+1); memcpy(b, s, len); b[len] = '\0'; return b; } #undef strndup #define strndup(s,len) parser_strndup(p, s, len) static char* parser_strdup(parser_state *p, const char *s) { return parser_strndup(p, s, strlen(s)); } #undef strdup #define strdup(s) parser_strdup(p, s) static void dump_int(uint16_t i, char *s) { char *p = s; char *t = s; while (i > 0) { *p++ = (i % 10)+'0'; i /= 10; } if (p == s) *p++ = '0'; *p = 0; p--; /* point the last char */ while (t < p) { char c = *t; *t++ = *p; *p-- = c; } } /* xxx ----------------------------- */ static node* local_switch(parser_state *p) { node *prev = p->locals; p->locals = cons(0, 0); return prev; } static void local_resume(parser_state *p, node *prev) { p->locals = prev; } static void local_nest(parser_state *p) { p->locals = cons(0, p->locals); } static void local_unnest(parser_state *p) { if (p->locals) { p->locals = p->locals->cdr; } } static mrb_bool local_var_p(parser_state *p, mrb_sym sym) { const struct RProc *u; node *l = p->locals; while (l) { node *n = l->car; while (n) { if (sym(n->car) == sym) return TRUE; n = n->cdr; } l = l->cdr; } u = p->upper; while (u && !MRB_PROC_CFUNC_P(u)) { const struct mrb_irep *ir = u->body.irep; const mrb_sym *v = ir->lv; int i; if (v) { for (i=0; i+1 < ir->nlocals; i++) { if (v[i] == sym) return TRUE; } } if (MRB_PROC_SCOPE_P(u)) break; u = u->upper; } return FALSE; } static void local_add_f(parser_state *p, mrb_sym sym) { if (p->locals) { node *n = p->locals->car; while (n) { if (sym(n->car) == sym) { mrb_int len; const char* name = mrb_sym_name_len(p->mrb, sym, &len); if (len > 0 && name[0] != '_') { yyerror(NULL, p, "duplicated argument name"); return; } } n = n->cdr; } p->locals->car = push(p->locals->car, nsym(sym)); } } static void local_add(parser_state *p, mrb_sym sym) { if (!local_var_p(p, sym)) { local_add_f(p, sym); } } /* allocate register for block */ #define local_add_blk(p) local_add_f(p, 0) static void local_add_kw(parser_state *p, mrb_sym kwd) { /* allocate register for keywords hash */ local_add_f(p, kwd ? kwd : intern_op(pow)); } static node* locals_node(parser_state *p) { return p->locals ? p->locals->car : NULL; } static void nvars_nest(parser_state *p) { p->nvars = cons(nint(0), p->nvars); } static void nvars_block(parser_state *p) { p->nvars = cons(nint(-2), p->nvars); } static void nvars_unnest(parser_state *p) { p->nvars = p->nvars->cdr; } /* (:scope (vars..) (prog...)) */ static node* new_scope(parser_state *p, node *body) { return cons((node*)NODE_SCOPE, cons(locals_node(p), body)); } /* (:begin prog...) */ static node* new_begin(parser_state *p, node *body) { if (body) { return list2((node*)NODE_BEGIN, body); } return cons((node*)NODE_BEGIN, 0); } #define newline_node(n) (n) /* (:rescue body rescue else) */ static node* new_rescue(parser_state *p, node *body, node *resq, node *els) { return list4((node*)NODE_RESCUE, body, resq, els); } static node* new_mod_rescue(parser_state *p, node *body, node *resq) { return new_rescue(p, body, list1(list3(0, 0, resq)), 0); } /* (:ensure body ensure) */ static node* new_ensure(parser_state *p, node *a, node *b) { return cons((node*)NODE_ENSURE, cons(a, cons(0, b))); } /* (:nil) */ static node* new_nil(parser_state *p) { return list1((node*)NODE_NIL); } /* (:true) */ static node* new_true(parser_state *p) { return list1((node*)NODE_TRUE); } /* (:false) */ static node* new_false(parser_state *p) { return list1((node*)NODE_FALSE); } /* (:alias new old) */ static node* new_alias(parser_state *p, mrb_sym a, mrb_sym b) { return cons((node*)NODE_ALIAS, cons(nsym(a), nsym(b))); } /* (:if cond then else) */ static node* new_if(parser_state *p, node *a, node *b, node *c) { void_expr_error(p, a); return list4((node*)NODE_IF, a, b, c); } /* (:unless cond then else) */ static node* new_unless(parser_state *p, node *a, node *b, node *c) { void_expr_error(p, a); return list4((node*)NODE_IF, a, c, b); } /* (:while cond body) */ static node* new_while(parser_state *p, node *a, node *b) { void_expr_error(p, a); return cons((node*)NODE_WHILE, cons(a, b)); } /* (:until cond body) */ static node* new_until(parser_state *p, node *a, node *b) { void_expr_error(p, a); return cons((node*)NODE_UNTIL, cons(a, b)); } /* (:for var obj body) */ static node* new_for(parser_state *p, node *v, node *o, node *b) { void_expr_error(p, o); return list4((node*)NODE_FOR, v, o, b); } /* (:case a ((when ...) body) ((when...) body)) */ static node* new_case(parser_state *p, node *a, node *b) { node *n = list2((node*)NODE_CASE, a); node *n2 = n; void_expr_error(p, a); while (n2->cdr) { n2 = n2->cdr; } n2->cdr = b; return n; } /* (:postexe a) */ static node* new_postexe(parser_state *p, node *a) { return cons((node*)NODE_POSTEXE, a); } /* (:self) */ static node* new_self(parser_state *p) { return list1((node*)NODE_SELF); } /* (:call a b c) */ static node* new_call(parser_state *p, node *a, mrb_sym b, node *c, int pass) { node *n = list4(nint(pass?NODE_CALL:NODE_SCALL), a, nsym(b), c); void_expr_error(p, a); NODE_LINENO(n, a); return n; } /* (:fcall self mid args) */ static node* new_fcall(parser_state *p, mrb_sym b, node *c) { node *n = list4((node*)NODE_FCALL, 0, nsym(b), c); NODE_LINENO(n, c); return n; } /* (a b . c) */ static node* new_callargs(parser_state *p, node *a, node *b, node *c) { return cons(a, cons(b, c)); } /* (:super . c) */ static node* new_super(parser_state *p, node *c) { return cons((node*)NODE_SUPER, c); } /* (:zsuper) */ static node* new_zsuper(parser_state *p) { return cons((node*)NODE_ZSUPER, 0); } /* (:yield . c) */ static node* new_yield(parser_state *p, node *c) { if (c && c->cdr && c->cdr->cdr) { yyerror(NULL, p, "both block arg and actual block given"); } return cons((node*)NODE_YIELD, c); } /* (:return . c) */ static node* new_return(parser_state *p, node *c) { return cons((node*)NODE_RETURN, c); } /* (:break . c) */ static node* new_break(parser_state *p, node *c) { return cons((node*)NODE_BREAK, c); } /* (:next . c) */ static node* new_next(parser_state *p, node *c) { return cons((node*)NODE_NEXT, c); } /* (:redo) */ static node* new_redo(parser_state *p) { return list1((node*)NODE_REDO); } /* (:retry) */ static node* new_retry(parser_state *p) { return list1((node*)NODE_RETRY); } /* (:dot2 a b) */ static node* new_dot2(parser_state *p, node *a, node *b) { return cons((node*)NODE_DOT2, cons(a, b)); } /* (:dot3 a b) */ static node* new_dot3(parser_state *p, node *a, node *b) { return cons((node*)NODE_DOT3, cons(a, b)); } /* (:colon2 b c) */ static node* new_colon2(parser_state *p, node *b, mrb_sym c) { void_expr_error(p, b); return cons((node*)NODE_COLON2, cons(b, nsym(c))); } /* (:colon3 . c) */ static node* new_colon3(parser_state *p, mrb_sym c) { return cons((node*)NODE_COLON3, nsym(c)); } /* (:and a b) */ static node* new_and(parser_state *p, node *a, node *b) { void_expr_error(p, a); return cons((node*)NODE_AND, cons(a, b)); } /* (:or a b) */ static node* new_or(parser_state *p, node *a, node *b) { void_expr_error(p, a); return cons((node*)NODE_OR, cons(a, b)); } /* (:array a...) */ static node* new_array(parser_state *p, node *a) { return cons((node*)NODE_ARRAY, a); } /* (:splat . a) */ static node* new_splat(parser_state *p, node *a) { void_expr_error(p, a); return cons((node*)NODE_SPLAT, a); } /* (:hash (k . v) (k . v)...) */ static node* new_hash(parser_state *p, node *a) { return cons((node*)NODE_HASH, a); } /* (:kw_hash (k . v) (k . v)...) */ static node* new_kw_hash(parser_state *p, node *a) { return cons((node*)NODE_KW_HASH, a); } /* (:sym . a) */ static node* new_sym(parser_state *p, mrb_sym sym) { return cons((node*)NODE_SYM, nsym(sym)); } static mrb_sym new_strsym(parser_state *p, node* str) { const char *s = (const char*)str->cdr->car; size_t len = (size_t)str->cdr->cdr; return mrb_intern(p->mrb, s, len); } /* (:lvar . a) */ static node* new_lvar(parser_state *p, mrb_sym sym) { return cons((node*)NODE_LVAR, nsym(sym)); } /* (:gvar . a) */ static node* new_gvar(parser_state *p, mrb_sym sym) { return cons((node*)NODE_GVAR, nsym(sym)); } /* (:ivar . a) */ static node* new_ivar(parser_state *p, mrb_sym sym) { return cons((node*)NODE_IVAR, nsym(sym)); } /* (:cvar . a) */ static node* new_cvar(parser_state *p, mrb_sym sym) { return cons((node*)NODE_CVAR, nsym(sym)); } /* (:nvar . a) */ static node* new_nvar(parser_state *p, int num) { int nvar; node *nvars = p->nvars->cdr; while (nvars) { nvar = intn(nvars->car); if (nvar == -2) break; /* top of the scope */ if (nvar > 0) { yyerror(NULL, p, "numbered parameter used in outer block"); break; } nvars->car = nint(-1); nvars = nvars->cdr; } nvar = intn(p->nvars->car); if (nvar == -1) { yyerror(NULL, p, "numbered parameter used in inner block"); } else { p->nvars->car = nint(nvar > num ? nvar : num); } return cons((node*)NODE_NVAR, nint(num)); } /* (:const . a) */ static node* new_const(parser_state *p, mrb_sym sym) { return cons((node*)NODE_CONST, nsym(sym)); } /* (:undef a...) */ static node* new_undef(parser_state *p, mrb_sym sym) { return list2((node*)NODE_UNDEF, nsym(sym)); } /* (:class class super body) */ static node* new_class(parser_state *p, node *c, node *s, node *b) { void_expr_error(p, s); return list4((node*)NODE_CLASS, c, s, cons(locals_node(p), b)); } /* (:sclass obj body) */ static node* new_sclass(parser_state *p, node *o, node *b) { void_expr_error(p, o); return list3((node*)NODE_SCLASS, o, cons(locals_node(p), b)); } /* (:module module body) */ static node* new_module(parser_state *p, node *m, node *b) { return list3((node*)NODE_MODULE, m, cons(locals_node(p), b)); } /* (:def m lv (arg . body)) */ static node* new_def(parser_state *p, mrb_sym m, node *a, node *b) { return list5((node*)NODE_DEF, nsym(m), 0, a, b); } static void defn_setup(parser_state *p, node *d, node *a, node *b) { node *n = d->cdr->cdr; n->car = locals_node(p); p->cmdarg_stack = intn(n->cdr->car); n->cdr->car = a; local_resume(p, n->cdr->cdr->car); n->cdr->cdr->car = b; } /* (:sdef obj m lv (arg . body)) */ static node* new_sdef(parser_state *p, node *o, mrb_sym m, node *a, node *b) { void_expr_error(p, o); return list6((node*)NODE_SDEF, o, nsym(m), 0, a, b); } static void defs_setup(parser_state *p, node *d, node *a, node *b) { node *n = d->cdr->cdr->cdr; n->car = locals_node(p); p->cmdarg_stack = intn(n->cdr->car); n->cdr->car = a; local_resume(p, n->cdr->cdr->car); n->cdr->cdr->car = b; } /* (:arg . sym) */ static node* new_arg(parser_state *p, mrb_sym sym) { return cons((node*)NODE_ARG, nsym(sym)); } static void local_add_margs(parser_state *p, node *n) { while (n) { if (typen(n->car->car) == NODE_MASGN) { node *t = n->car->cdr->cdr; n->car->cdr->cdr = NULL; while (t) { local_add_f(p, sym(t->car)); t = t->cdr; } local_add_margs(p, n->car->cdr->car->car); local_add_margs(p, n->car->cdr->car->cdr->cdr->car); } n = n->cdr; } } static void local_add_lv(parser_state *p, node *lv) { while (lv) { local_add_f(p, sym(lv->car)); lv = lv->cdr; } } /* (m o r m2 tail) */ /* m: (a b c) */ /* o: ((a . e1) (b . e2)) */ /* r: a */ /* m2: (a b c) */ /* b: a */ static node* new_args(parser_state *p, node *m, node *opt, mrb_sym rest, node *m2, node *tail) { node *n; local_add_margs(p, m); local_add_margs(p, m2); n = cons(m2, tail); n = cons(nsym(rest), n); n = cons(opt, n); while (opt) { /* opt: (sym . (opt . lv)) -> (sym . opt) */ local_add_lv(p, opt->car->cdr->cdr); opt->car->cdr = opt->car->cdr->car; opt = opt->cdr; } return cons(m, n); } /* (:args_tail keywords rest_keywords_sym block_sym) */ static node* new_args_tail(parser_state *p, node *kws, node *kwrest, mrb_sym blk) { node *k; if (kws || kwrest) { local_add_kw(p, (kwrest && kwrest->cdr)? sym(kwrest->cdr) : 0); } local_add_blk(p); if (blk) local_add_f(p, blk); /* allocate register for keywords arguments */ /* order is for Proc#parameters */ for (k = kws; k; k = k->cdr) { if (!k->car->cdr->cdr->car) { /* allocate required keywords */ local_add_f(p, sym(k->car->cdr->car)); } } for (k = kws; k; k = k->cdr) { if (k->car->cdr->cdr->car) { /* allocate keywords with default */ local_add_lv(p, k->car->cdr->cdr->car->cdr); k->car->cdr->cdr->car = k->car->cdr->cdr->car->car; local_add_f(p, sym(k->car->cdr->car)); } } return list4((node*)NODE_ARGS_TAIL, kws, kwrest, nsym(blk)); } /* (:kw_arg kw_sym def_arg) */ static node* new_kw_arg(parser_state *p, mrb_sym kw, node *def_arg) { mrb_assert(kw); return list3((node*)NODE_KW_ARG, nsym(kw), def_arg); } /* (:kw_rest_args . a) */ static node* new_kw_rest_args(parser_state *p, mrb_sym sym) { return cons((node*)NODE_KW_REST_ARGS, nsym(sym)); } static node* new_args_dots(parser_state *p, node *m) { mrb_sym r = intern_op(mul); mrb_sym k = intern_op(pow); mrb_sym b = intern_op(and); local_add_f(p, r); return new_args(p, m, 0, r, 0, new_args_tail(p, 0, new_kw_rest_args(p, k), b)); } /* (:block_arg . a) */ static node* new_block_arg(parser_state *p, node *a) { return cons((node*)NODE_BLOCK_ARG, a); } static node* setup_numparams(parser_state *p, node *a) { int nvars = intn(p->nvars->car); if (nvars > 0) { int i; mrb_sym sym; // m || opt || rest || tail if (a && (a->car || (a->cdr && a->cdr->car) || (a->cdr->cdr && a->cdr->cdr->car) || (a->cdr->cdr->cdr->cdr && a->cdr->cdr->cdr->cdr->car))) { yyerror(NULL, p, "ordinary parameter is defined"); } else if (p->locals) { /* p->locals should not be NULL unless error happens before the point */ node* args = 0; for (i = nvars; i > 0; i--) { char buf[3]; buf[0] = '_'; buf[1] = i+'0'; buf[2] = '\0'; sym = intern_cstr(buf); args = cons(new_arg(p, sym), args); p->locals->car = cons(nsym(sym), p->locals->car); } a = new_args(p, args, 0, 0, 0, 0); } } return a; } /* (:block arg body) */ static node* new_block(parser_state *p, node *a, node *b) { a = setup_numparams(p, a); return list4((node*)NODE_BLOCK, locals_node(p), a, b); } /* (:lambda arg body) */ static node* new_lambda(parser_state *p, node *a, node *b) { a = setup_numparams(p, a); return list4((node*)NODE_LAMBDA, locals_node(p), a, b); } /* (:asgn lhs rhs) */ static node* new_asgn(parser_state *p, node *a, node *b) { void_expr_error(p, b); return cons((node*)NODE_ASGN, cons(a, b)); } /* (:masgn mlhs=(pre rest post) mrhs) */ static node* new_masgn(parser_state *p, node *a, node *b) { void_expr_error(p, b); return cons((node*)NODE_MASGN, cons(a, b)); } /* (:masgn mlhs mrhs) no check */ static node* new_masgn_param(parser_state *p, node *a, node *b) { return cons((node*)NODE_MASGN, cons(a, b)); } /* (:asgn lhs rhs) */ static node* new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b) { void_expr_error(p, b); return list4((node*)NODE_OP_ASGN, a, nsym(op), b); } static node* new_imaginary(parser_state *p, node *imaginary) { return new_fcall(p, MRB_SYM_2(p->mrb, Complex), new_callargs(p, list2(list3((node*)NODE_INT, (node*)strdup("0"), nint(10)), imaginary), 0, 0)); } static node* new_rational(parser_state *p, node *rational) { return new_fcall(p, MRB_SYM_2(p->mrb, Rational), new_callargs(p, list1(rational), 0, 0)); } /* (:int . i) */ static node* new_int(parser_state *p, const char *s, int base, int suffix) { node* result = list3((node*)NODE_INT, (node*)strdup(s), nint(base)); if (suffix & NUM_SUFFIX_R) { result = new_rational(p, result); } if (suffix & NUM_SUFFIX_I) { result = new_imaginary(p, result); } return result; } #ifndef MRB_NO_FLOAT /* (:float . i) */ static node* new_float(parser_state *p, const char *s, int suffix) { node* result = cons((node*)NODE_FLOAT, (node*)strdup(s)); if (suffix & NUM_SUFFIX_R) { result = new_rational(p, result); } if (suffix & NUM_SUFFIX_I) { result = new_imaginary(p, result); } return result; } #endif /* (:str . (s . len)) */ static node* new_str(parser_state *p, const char *s, size_t len) { return cons((node*)NODE_STR, cons((node*)strndup(s, len), nint(len))); } /* (:dstr . a) */ static node* new_dstr(parser_state *p, node *a) { return cons((node*)NODE_DSTR, a); } static int string_node_p(node *n) { return (int)(typen(n->car) == NODE_STR); } static node* composite_string_node(parser_state *p, node *a, node *b) { size_t newlen = (size_t)a->cdr + (size_t)b->cdr; char *str = (char*)mempool_realloc(p->pool, a->car, (size_t)a->cdr + 1, newlen + 1); memcpy(str + (size_t)a->cdr, b->car, (size_t)b->cdr); str[newlen] = '\0'; a->car = (node*)str; a->cdr = (node*)newlen; cons_free(b); return a; } static node* concat_string(parser_state *p, node *a, node *b) { if (string_node_p(a)) { if (string_node_p(b)) { /* a == NODE_STR && b == NODE_STR */ composite_string_node(p, a->cdr, b->cdr); cons_free(b); return a; } else { /* a == NODE_STR && b == NODE_DSTR */ if (string_node_p(b->cdr->car)) { /* a == NODE_STR && b->[NODE_STR, ...] */ composite_string_node(p, a->cdr, b->cdr->car->cdr); cons_free(b->cdr->car); b->cdr->car = a; return b; } } } else { node *c; /* last node of a */ for (c = a; c->cdr != NULL; c = c->cdr) ; if (string_node_p(b)) { /* a == NODE_DSTR && b == NODE_STR */ if (string_node_p(c->car)) { /* a->[..., NODE_STR] && b == NODE_STR */ composite_string_node(p, c->car->cdr, b->cdr); cons_free(b); return a; } push(a, b); return a; } else { /* a == NODE_DSTR && b == NODE_DSTR */ if (string_node_p(c->car) && string_node_p(b->cdr->car)) { /* a->[..., NODE_STR] && b->[NODE_STR, ...] */ node *d = b->cdr; cons_free(b); composite_string_node(p, c->car->cdr, d->car->cdr); cons_free(d->car); c->cdr = d->cdr; cons_free(d); return a; } else { c->cdr = b->cdr; cons_free(b); return a; } } } return new_dstr(p, list2(a, b)); } /* (:str . (s . len)) */ static node* new_xstr(parser_state *p, const char *s, int len) { return cons((node*)NODE_XSTR, cons((node*)strndup(s, len), nint(len))); } /* (:xstr . a) */ static node* new_dxstr(parser_state *p, node *a) { return cons((node*)NODE_DXSTR, a); } /* (:dsym . a) */ static node* new_dsym(parser_state *p, node *a) { return cons((node*)NODE_DSYM, a); } /* (:regx . (s . (opt . enc))) */ static node* new_regx(parser_state *p, const char *p1, const char* p2, const char* p3) { return cons((node*)NODE_REGX, cons((node*)p1, cons((node*)p2, (node*)p3))); } /* (:dregx . (a . b)) */ static node* new_dregx(parser_state *p, node *a, node *b) { return cons((node*)NODE_DREGX, cons(a, b)); } /* (:backref . n) */ static node* new_back_ref(parser_state *p, int n) { return cons((node*)NODE_BACK_REF, nint(n)); } /* (:nthref . n) */ static node* new_nth_ref(parser_state *p, int n) { return cons((node*)NODE_NTH_REF, nint(n)); } /* (:heredoc . a) */ static node* new_heredoc(parser_state *p) { parser_heredoc_info *inf = (parser_heredoc_info*)parser_palloc(p, sizeof(parser_heredoc_info)); return cons((node*)NODE_HEREDOC, (node*)inf); } static void new_bv(parser_state *p, mrb_sym id) { } static node* new_literal_delim(parser_state *p) { return cons((node*)NODE_LITERAL_DELIM, 0); } /* (:words . a) */ static node* new_words(parser_state *p, node *a) { return cons((node*)NODE_WORDS, a); } /* (:symbols . a) */ static node* new_symbols(parser_state *p, node *a) { return cons((node*)NODE_SYMBOLS, a); } /* xxx ----------------------------- */ /* (:call a op) */ static node* call_uni_op(parser_state *p, node *recv, const char *m) { void_expr_error(p, recv); return new_call(p, recv, intern_cstr(m), 0, '.'); } /* (:call a op b) */ static node* call_bin_op(parser_state *p, node *recv, const char *m, node *arg1) { return new_call(p, recv, intern_cstr(m), new_callargs(p, list1(arg1), 0, 0), '.'); } static void args_with_block(parser_state *p, node *a, node *b) { if (b) { if (a->cdr && a->cdr->cdr) { yyerror(NULL, p, "both block arg and actual block given"); } a->cdr->cdr = b; } } static void endless_method_name(parser_state *p, node *defn) { mrb_sym sym = sym(defn->cdr->car); mrb_int len; const char *name = mrb_sym_name_len(p->mrb, sym, &len); if (len > 1 && name[len-1] == '=') { for (int i=0; icar)) { case NODE_SUPER: case NODE_ZSUPER: if (!a->cdr) a->cdr = new_callargs(p, 0, 0, b); else args_with_block(p, a->cdr, b); break; case NODE_CALL: case NODE_FCALL: case NODE_SCALL: /* (NODE_CALL recv mid (args kw . blk)) */ n = a->cdr->cdr->cdr; /* (args kw . blk) */ if (!n->car) n->car = new_callargs(p, 0, 0, b); else args_with_block(p, n->car, b); break; case NODE_RETURN: case NODE_BREAK: case NODE_NEXT: if (a->cdr == NULL) return; call_with_block(p, a->cdr, b); break; default: break; } } static node* new_negate(parser_state *p, node *n) { return cons((node*)NODE_NEGATE, n); } static node* cond(node *n) { return n; } static node* ret_args(parser_state *p, node *n) { if (n->cdr->cdr) { yyerror(NULL, p, "block argument should not be given"); return NULL; } if (!n->car) return NULL; if (!n->car->cdr) return n->car->car; return new_array(p, n->car); } static void assignable(parser_state *p, node *lhs) { switch (intn(lhs->car)) { case NODE_LVAR: local_add(p, sym(lhs->cdr)); break; case NODE_CONST: if (p->in_def) yyerror(NULL, p, "dynamic constant assignment"); break; } } static node* var_reference(parser_state *p, node *lhs) { if (intn(lhs->car) == NODE_LVAR) { if (!local_var_p(p, sym(lhs->cdr))) { node *n = new_fcall(p, sym(lhs->cdr), 0); cons_free(lhs); return n; } } return lhs; } static node* label_reference(parser_state *p, mrb_sym sym) { const char *name = mrb_sym_name(p->mrb, sym); if (local_var_p(p, sym)) { return new_lvar(p, sym); } else if (ISUPPER(name[0])) { return new_const(p, sym); } else { return new_fcall(p, sym, 0); } } typedef enum mrb_string_type string_type; typedef struct parser_lex_strterm { int type; int level; int term; int paren; struct parser_lex_strterm *prev; } parser_lex_strterm; static parser_lex_strterm* new_strterm(parser_state *p, string_type type, int term, int paren) { parser_lex_strterm *lex = (parser_lex_strterm*)parser_palloc(p, sizeof(parser_lex_strterm)); lex->type = type; lex->level = 0; lex->term = term; lex->paren = paren; lex->prev = p->lex_strterm; return lex; } static void end_strterm(parser_state *p) { parser_lex_strterm *term = p->lex_strterm->prev; parser_pfree(p->lex_strterm); p->lex_strterm = term; } static node* push_strterm(parser_state *p) { node *n = cons((node*)p->lex_strterm, p->parsing_heredoc); p->lex_strterm = NULL; return n; } static void pop_strterm(parser_state *p, node *n) { p->lex_strterm = (parser_lex_strterm*)n->car; p->parsing_heredoc = n->cdr; cons_free(n); } static parser_heredoc_info * parsing_heredoc_info(parser_state *p) { node *nd = p->parsing_heredoc; if (nd == NULL) return NULL; /* mrb_assert(nd->car->car == NODE_HEREDOC); */ return (parser_heredoc_info*)nd->car->cdr; } static void heredoc_treat_nextline(parser_state *p) { if (p->heredocs_from_nextline == NULL) return; if (p->parsing_heredoc && p->lex_strterm) { append(p->heredocs_from_nextline, p->parsing_heredoc); } p->parsing_heredoc = p->heredocs_from_nextline; p->lex_strterm = new_strterm(p, parsing_heredoc_info(p)->type, 0, 0); p->heredocs_from_nextline = NULL; } static void heredoc_end(parser_state *p) { p->parsing_heredoc = p->parsing_heredoc->cdr; if (p->parsing_heredoc == NULL) { p->lstate = EXPR_BEG; end_strterm(p); } else { /* next heredoc */ p->lex_strterm->type = parsing_heredoc_info(p)->type; } } #define is_strterm_type(p,str_func) ((p)->lex_strterm->type & (str_func)) static void prohibit_literals(parser_state *p, node *n) { if (n == 0) { yyerror(NULL, p, "can't define singleton method for ()."); } else { switch (typen(n->car)) { case NODE_INT: case NODE_STR: case NODE_DSTR: case NODE_XSTR: case NODE_DXSTR: case NODE_DREGX: case NODE_MATCH: case NODE_FLOAT: case NODE_ARRAY: case NODE_HEREDOC: yyerror(NULL, p, "can't define singleton method for literals"); default: break; } } } /* xxx ----------------------------- */ #line 1551 "mrbgems/mruby-compiler/core/y.tab.c" # ifndef YY_CAST # ifdef __cplusplus # define YY_CAST(Type, Val) static_cast (Val) # define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) # else # define YY_CAST(Type, Val) ((Type) (Val)) # define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) # endif # endif # ifndef YY_NULLPTR # if defined __cplusplus # if 201103L <= __cplusplus # define YY_NULLPTR nullptr # else # define YY_NULLPTR 0 # endif # else # define YY_NULLPTR ((void*)0) # endif # endif /* Use api.header.include to #include this header instead of duplicating it here. */ /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif #if YYDEBUG && !defined(yydebug) extern int yydebug; #endif /* Token kinds. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE enum yytokentype { YYEMPTY = -2, YYEOF = 0, /* "end of file" */ YYerror = 256, /* error */ YYUNDEF = 257, /* "invalid token" */ keyword_class = 258, /* "'class'" */ keyword_module = 259, /* "'module'" */ keyword_def = 260, /* "'def'" */ keyword_begin = 261, /* "'begin'" */ keyword_if = 262, /* "'if'" */ keyword_unless = 263, /* "'unless'" */ keyword_while = 264, /* "'while'" */ keyword_until = 265, /* "'until'" */ keyword_for = 266, /* "'for'" */ keyword_undef = 267, /* "'undef'" */ keyword_rescue = 268, /* "'rescue'" */ keyword_ensure = 269, /* "'ensure'" */ keyword_end = 270, /* "'end'" */ keyword_then = 271, /* "'then'" */ keyword_elsif = 272, /* "'elsif'" */ keyword_else = 273, /* "'else'" */ keyword_case = 274, /* "'case'" */ keyword_when = 275, /* "'when'" */ keyword_break = 276, /* "'break'" */ keyword_next = 277, /* "'next'" */ keyword_redo = 278, /* "'redo'" */ keyword_retry = 279, /* "'retry'" */ keyword_in = 280, /* "'in'" */ keyword_do = 281, /* "'do'" */ keyword_do_cond = 282, /* "'do' for condition" */ keyword_do_block = 283, /* "'do' for block" */ keyword_do_LAMBDA = 284, /* "'do' for lambda" */ keyword_return = 285, /* "'return'" */ keyword_yield = 286, /* "'yield'" */ keyword_super = 287, /* "'super'" */ keyword_self = 288, /* "'self'" */ keyword_nil = 289, /* "'nil'" */ keyword_true = 290, /* "'true'" */ keyword_false = 291, /* "'false'" */ keyword_and = 292, /* "'and'" */ keyword_or = 293, /* "'or'" */ keyword_not = 294, /* "'not'" */ modifier_if = 295, /* "'if' modifier" */ modifier_unless = 296, /* "'unless' modifier" */ modifier_while = 297, /* "'while' modifier" */ modifier_until = 298, /* "'until' modifier" */ modifier_rescue = 299, /* "'rescue' modifier" */ keyword_alias = 300, /* "'alias'" */ keyword_BEGIN = 301, /* "'BEGIN'" */ keyword_END = 302, /* "'END'" */ keyword__LINE__ = 303, /* "'__LINE__'" */ keyword__FILE__ = 304, /* "'__FILE__'" */ keyword__ENCODING__ = 305, /* "'__ENCODING__'" */ tIDENTIFIER = 306, /* "local variable or method" */ tFID = 307, /* "method" */ tGVAR = 308, /* "global variable" */ tIVAR = 309, /* "instance variable" */ tCONSTANT = 310, /* "constant" */ tCVAR = 311, /* "class variable" */ tLABEL_TAG = 312, /* "label" */ tINTEGER = 313, /* "integer literal" */ tFLOAT = 314, /* "float literal" */ tCHAR = 315, /* "character literal" */ tXSTRING = 316, /* tXSTRING */ tREGEXP = 317, /* tREGEXP */ tSTRING = 318, /* tSTRING */ tSTRING_PART = 319, /* tSTRING_PART */ tSTRING_MID = 320, /* tSTRING_MID */ tNTH_REF = 321, /* tNTH_REF */ tBACK_REF = 322, /* tBACK_REF */ tREGEXP_END = 323, /* tREGEXP_END */ tNUMPARAM = 324, /* "numbered parameter" */ tUPLUS = 325, /* "unary plus" */ tUMINUS = 326, /* "unary minus" */ tCMP = 327, /* "<=>" */ tEQ = 328, /* "==" */ tEQQ = 329, /* "===" */ tNEQ = 330, /* "!=" */ tGEQ = 331, /* ">=" */ tLEQ = 332, /* "<=" */ tANDOP = 333, /* "&&" */ tOROP = 334, /* "||" */ tMATCH = 335, /* "=~" */ tNMATCH = 336, /* "!~" */ tDOT2 = 337, /* ".." */ tDOT3 = 338, /* "..." */ tBDOT2 = 339, /* tBDOT2 */ tBDOT3 = 340, /* tBDOT3 */ tAREF = 341, /* tAREF */ tASET = 342, /* tASET */ tLSHFT = 343, /* "<<" */ tRSHFT = 344, /* ">>" */ tCOLON2 = 345, /* "::" */ tCOLON3 = 346, /* tCOLON3 */ tOP_ASGN = 347, /* tOP_ASGN */ tASSOC = 348, /* "=>" */ tLPAREN = 349, /* tLPAREN */ tLPAREN_ARG = 350, /* "(" */ tRPAREN = 351, /* ")" */ tLBRACK = 352, /* "[" */ tLBRACE = 353, /* tLBRACE */ tLBRACE_ARG = 354, /* "{" */ tSTAR = 355, /* "*" */ tPOW = 356, /* tPOW */ tDSTAR = 357, /* "**" */ tAMPER = 358, /* "&" */ tLAMBDA = 359, /* "->" */ tANDDOT = 360, /* "&." */ tSYMBEG = 361, /* "symbol" */ tSTRING_BEG = 362, /* "string literal" */ tXSTRING_BEG = 363, /* tXSTRING_BEG */ tSTRING_DVAR = 364, /* tSTRING_DVAR */ tREGEXP_BEG = 365, /* tREGEXP_BEG */ tWORDS_BEG = 366, /* tWORDS_BEG */ tSYMBOLS_BEG = 367, /* tSYMBOLS_BEG */ tLAMBEG = 368, /* tLAMBEG */ tHEREDOC_BEG = 369, /* "here document" */ tHEREDOC_END = 370, /* tHEREDOC_END */ tLITERAL_DELIM = 371, /* tLITERAL_DELIM */ tHD_LITERAL_DELIM = 372, /* tHD_LITERAL_DELIM */ tHD_STRING_PART = 373, /* tHD_STRING_PART */ tHD_STRING_MID = 374, /* tHD_STRING_MID */ tLOWEST = 375, /* tLOWEST */ tUMINUS_NUM = 376, /* tUMINUS_NUM */ tLAST_TOKEN = 377 /* tLAST_TOKEN */ }; typedef enum yytokentype yytoken_kind_t; #endif /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { #line 1494 "mrbgems/mruby-compiler/core/parse.y" node *nd; mrb_sym id; int num; stack_type stack; const struct vtable *vars; #line 1730 "mrbgems/mruby-compiler/core/y.tab.c" }; typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif /* Location type. */ #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED typedef struct YYLTYPE YYLTYPE; struct YYLTYPE { int first_line; int first_column; int last_line; int last_column; }; # define YYLTYPE_IS_DECLARED 1 # define YYLTYPE_IS_TRIVIAL 1 #endif int yyparse (parser_state *p); /* Symbol kind. */ enum yysymbol_kind_t { YYSYMBOL_YYEMPTY = -2, YYSYMBOL_YYEOF = 0, /* "end of file" */ YYSYMBOL_YYerror = 1, /* error */ YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ YYSYMBOL_keyword_class = 3, /* "'class'" */ YYSYMBOL_keyword_module = 4, /* "'module'" */ YYSYMBOL_keyword_def = 5, /* "'def'" */ YYSYMBOL_keyword_begin = 6, /* "'begin'" */ YYSYMBOL_keyword_if = 7, /* "'if'" */ YYSYMBOL_keyword_unless = 8, /* "'unless'" */ YYSYMBOL_keyword_while = 9, /* "'while'" */ YYSYMBOL_keyword_until = 10, /* "'until'" */ YYSYMBOL_keyword_for = 11, /* "'for'" */ YYSYMBOL_keyword_undef = 12, /* "'undef'" */ YYSYMBOL_keyword_rescue = 13, /* "'rescue'" */ YYSYMBOL_keyword_ensure = 14, /* "'ensure'" */ YYSYMBOL_keyword_end = 15, /* "'end'" */ YYSYMBOL_keyword_then = 16, /* "'then'" */ YYSYMBOL_keyword_elsif = 17, /* "'elsif'" */ YYSYMBOL_keyword_else = 18, /* "'else'" */ YYSYMBOL_keyword_case = 19, /* "'case'" */ YYSYMBOL_keyword_when = 20, /* "'when'" */ YYSYMBOL_keyword_break = 21, /* "'break'" */ YYSYMBOL_keyword_next = 22, /* "'next'" */ YYSYMBOL_keyword_redo = 23, /* "'redo'" */ YYSYMBOL_keyword_retry = 24, /* "'retry'" */ YYSYMBOL_keyword_in = 25, /* "'in'" */ YYSYMBOL_keyword_do = 26, /* "'do'" */ YYSYMBOL_keyword_do_cond = 27, /* "'do' for condition" */ YYSYMBOL_keyword_do_block = 28, /* "'do' for block" */ YYSYMBOL_keyword_do_LAMBDA = 29, /* "'do' for lambda" */ YYSYMBOL_keyword_return = 30, /* "'return'" */ YYSYMBOL_keyword_yield = 31, /* "'yield'" */ YYSYMBOL_keyword_super = 32, /* "'super'" */ YYSYMBOL_keyword_self = 33, /* "'self'" */ YYSYMBOL_keyword_nil = 34, /* "'nil'" */ YYSYMBOL_keyword_true = 35, /* "'true'" */ YYSYMBOL_keyword_false = 36, /* "'false'" */ YYSYMBOL_keyword_and = 37, /* "'and'" */ YYSYMBOL_keyword_or = 38, /* "'or'" */ YYSYMBOL_keyword_not = 39, /* "'not'" */ YYSYMBOL_modifier_if = 40, /* "'if' modifier" */ YYSYMBOL_modifier_unless = 41, /* "'unless' modifier" */ YYSYMBOL_modifier_while = 42, /* "'while' modifier" */ YYSYMBOL_modifier_until = 43, /* "'until' modifier" */ YYSYMBOL_modifier_rescue = 44, /* "'rescue' modifier" */ YYSYMBOL_keyword_alias = 45, /* "'alias'" */ YYSYMBOL_keyword_BEGIN = 46, /* "'BEGIN'" */ YYSYMBOL_keyword_END = 47, /* "'END'" */ YYSYMBOL_keyword__LINE__ = 48, /* "'__LINE__'" */ YYSYMBOL_keyword__FILE__ = 49, /* "'__FILE__'" */ YYSYMBOL_keyword__ENCODING__ = 50, /* "'__ENCODING__'" */ YYSYMBOL_tIDENTIFIER = 51, /* "local variable or method" */ YYSYMBOL_tFID = 52, /* "method" */ YYSYMBOL_tGVAR = 53, /* "global variable" */ YYSYMBOL_tIVAR = 54, /* "instance variable" */ YYSYMBOL_tCONSTANT = 55, /* "constant" */ YYSYMBOL_tCVAR = 56, /* "class variable" */ YYSYMBOL_tLABEL_TAG = 57, /* "label" */ YYSYMBOL_tINTEGER = 58, /* "integer literal" */ YYSYMBOL_tFLOAT = 59, /* "float literal" */ YYSYMBOL_tCHAR = 60, /* "character literal" */ YYSYMBOL_tXSTRING = 61, /* tXSTRING */ YYSYMBOL_tREGEXP = 62, /* tREGEXP */ YYSYMBOL_tSTRING = 63, /* tSTRING */ YYSYMBOL_tSTRING_PART = 64, /* tSTRING_PART */ YYSYMBOL_tSTRING_MID = 65, /* tSTRING_MID */ YYSYMBOL_tNTH_REF = 66, /* tNTH_REF */ YYSYMBOL_tBACK_REF = 67, /* tBACK_REF */ YYSYMBOL_tREGEXP_END = 68, /* tREGEXP_END */ YYSYMBOL_tNUMPARAM = 69, /* "numbered parameter" */ YYSYMBOL_tUPLUS = 70, /* "unary plus" */ YYSYMBOL_tUMINUS = 71, /* "unary minus" */ YYSYMBOL_tCMP = 72, /* "<=>" */ YYSYMBOL_tEQ = 73, /* "==" */ YYSYMBOL_tEQQ = 74, /* "===" */ YYSYMBOL_tNEQ = 75, /* "!=" */ YYSYMBOL_tGEQ = 76, /* ">=" */ YYSYMBOL_tLEQ = 77, /* "<=" */ YYSYMBOL_tANDOP = 78, /* "&&" */ YYSYMBOL_tOROP = 79, /* "||" */ YYSYMBOL_tMATCH = 80, /* "=~" */ YYSYMBOL_tNMATCH = 81, /* "!~" */ YYSYMBOL_tDOT2 = 82, /* ".." */ YYSYMBOL_tDOT3 = 83, /* "..." */ YYSYMBOL_tBDOT2 = 84, /* tBDOT2 */ YYSYMBOL_tBDOT3 = 85, /* tBDOT3 */ YYSYMBOL_tAREF = 86, /* tAREF */ YYSYMBOL_tASET = 87, /* tASET */ YYSYMBOL_tLSHFT = 88, /* "<<" */ YYSYMBOL_tRSHFT = 89, /* ">>" */ YYSYMBOL_tCOLON2 = 90, /* "::" */ YYSYMBOL_tCOLON3 = 91, /* tCOLON3 */ YYSYMBOL_tOP_ASGN = 92, /* tOP_ASGN */ YYSYMBOL_tASSOC = 93, /* "=>" */ YYSYMBOL_tLPAREN = 94, /* tLPAREN */ YYSYMBOL_tLPAREN_ARG = 95, /* "(" */ YYSYMBOL_tRPAREN = 96, /* ")" */ YYSYMBOL_tLBRACK = 97, /* "[" */ YYSYMBOL_tLBRACE = 98, /* tLBRACE */ YYSYMBOL_tLBRACE_ARG = 99, /* "{" */ YYSYMBOL_tSTAR = 100, /* "*" */ YYSYMBOL_tPOW = 101, /* tPOW */ YYSYMBOL_tDSTAR = 102, /* "**" */ YYSYMBOL_tAMPER = 103, /* "&" */ YYSYMBOL_tLAMBDA = 104, /* "->" */ YYSYMBOL_tANDDOT = 105, /* "&." */ YYSYMBOL_tSYMBEG = 106, /* "symbol" */ YYSYMBOL_tSTRING_BEG = 107, /* "string literal" */ YYSYMBOL_tXSTRING_BEG = 108, /* tXSTRING_BEG */ YYSYMBOL_tSTRING_DVAR = 109, /* tSTRING_DVAR */ YYSYMBOL_tREGEXP_BEG = 110, /* tREGEXP_BEG */ YYSYMBOL_tWORDS_BEG = 111, /* tWORDS_BEG */ YYSYMBOL_tSYMBOLS_BEG = 112, /* tSYMBOLS_BEG */ YYSYMBOL_tLAMBEG = 113, /* tLAMBEG */ YYSYMBOL_tHEREDOC_BEG = 114, /* "here document" */ YYSYMBOL_tHEREDOC_END = 115, /* tHEREDOC_END */ YYSYMBOL_tLITERAL_DELIM = 116, /* tLITERAL_DELIM */ YYSYMBOL_tHD_LITERAL_DELIM = 117, /* tHD_LITERAL_DELIM */ YYSYMBOL_tHD_STRING_PART = 118, /* tHD_STRING_PART */ YYSYMBOL_tHD_STRING_MID = 119, /* tHD_STRING_MID */ YYSYMBOL_tLOWEST = 120, /* tLOWEST */ YYSYMBOL_121_ = 121, /* '=' */ YYSYMBOL_122_ = 122, /* '?' */ YYSYMBOL_123_ = 123, /* ':' */ YYSYMBOL_124_ = 124, /* '>' */ YYSYMBOL_125_ = 125, /* '<' */ YYSYMBOL_126_ = 126, /* '|' */ YYSYMBOL_127_ = 127, /* '^' */ YYSYMBOL_128_ = 128, /* '&' */ YYSYMBOL_129_ = 129, /* '+' */ YYSYMBOL_130_ = 130, /* '-' */ YYSYMBOL_131_ = 131, /* '*' */ YYSYMBOL_132_ = 132, /* '/' */ YYSYMBOL_133_ = 133, /* '%' */ YYSYMBOL_tUMINUS_NUM = 134, /* tUMINUS_NUM */ YYSYMBOL_135_ = 135, /* '!' */ YYSYMBOL_136_ = 136, /* '~' */ YYSYMBOL_tLAST_TOKEN = 137, /* tLAST_TOKEN */ YYSYMBOL_138_ = 138, /* '{' */ YYSYMBOL_139_ = 139, /* '}' */ YYSYMBOL_140_ = 140, /* '[' */ YYSYMBOL_141_ = 141, /* ']' */ YYSYMBOL_142_ = 142, /* ',' */ YYSYMBOL_143_ = 143, /* '`' */ YYSYMBOL_144_ = 144, /* '(' */ YYSYMBOL_145_ = 145, /* ')' */ YYSYMBOL_146_ = 146, /* ';' */ YYSYMBOL_147_ = 147, /* '.' */ YYSYMBOL_148_n_ = 148, /* '\n' */ YYSYMBOL_YYACCEPT = 149, /* $accept */ YYSYMBOL_150_1 = 150, /* $@1 */ YYSYMBOL_program = 151, /* program */ YYSYMBOL_top_compstmt = 152, /* top_compstmt */ YYSYMBOL_top_stmts = 153, /* top_stmts */ YYSYMBOL_top_stmt = 154, /* top_stmt */ YYSYMBOL_155_2 = 155, /* @2 */ YYSYMBOL_bodystmt = 156, /* bodystmt */ YYSYMBOL_compstmt = 157, /* compstmt */ YYSYMBOL_stmts = 158, /* stmts */ YYSYMBOL_159_3 = 159, /* $@3 */ YYSYMBOL_stmt = 160, /* stmt */ YYSYMBOL_command_asgn = 161, /* command_asgn */ YYSYMBOL_command_rhs = 162, /* command_rhs */ YYSYMBOL_expr = 163, /* expr */ YYSYMBOL_defn_head = 164, /* defn_head */ YYSYMBOL_165_4 = 165, /* $@4 */ YYSYMBOL_defs_head = 166, /* defs_head */ YYSYMBOL_expr_value = 167, /* expr_value */ YYSYMBOL_command_call = 168, /* command_call */ YYSYMBOL_block_command = 169, /* block_command */ YYSYMBOL_170_5 = 170, /* $@5 */ YYSYMBOL_cmd_brace_block = 171, /* cmd_brace_block */ YYSYMBOL_command = 172, /* command */ YYSYMBOL_mlhs = 173, /* mlhs */ YYSYMBOL_mlhs_inner = 174, /* mlhs_inner */ YYSYMBOL_mlhs_basic = 175, /* mlhs_basic */ YYSYMBOL_mlhs_item = 176, /* mlhs_item */ YYSYMBOL_mlhs_list = 177, /* mlhs_list */ YYSYMBOL_mlhs_post = 178, /* mlhs_post */ YYSYMBOL_mlhs_node = 179, /* mlhs_node */ YYSYMBOL_lhs = 180, /* lhs */ YYSYMBOL_cname = 181, /* cname */ YYSYMBOL_cpath = 182, /* cpath */ YYSYMBOL_fname = 183, /* fname */ YYSYMBOL_fsym = 184, /* fsym */ YYSYMBOL_undef_list = 185, /* undef_list */ YYSYMBOL_186_6 = 186, /* $@6 */ YYSYMBOL_op = 187, /* op */ YYSYMBOL_reswords = 188, /* reswords */ YYSYMBOL_arg = 189, /* arg */ YYSYMBOL_aref_args = 190, /* aref_args */ YYSYMBOL_arg_rhs = 191, /* arg_rhs */ YYSYMBOL_paren_args = 192, /* paren_args */ YYSYMBOL_opt_paren_args = 193, /* opt_paren_args */ YYSYMBOL_opt_call_args = 194, /* opt_call_args */ YYSYMBOL_call_args = 195, /* call_args */ YYSYMBOL_196_7 = 196, /* @7 */ YYSYMBOL_command_args = 197, /* command_args */ YYSYMBOL_block_arg = 198, /* block_arg */ YYSYMBOL_opt_block_arg = 199, /* opt_block_arg */ YYSYMBOL_comma = 200, /* comma */ YYSYMBOL_args = 201, /* args */ YYSYMBOL_mrhs = 202, /* mrhs */ YYSYMBOL_primary = 203, /* primary */ YYSYMBOL_204_8 = 204, /* @8 */ YYSYMBOL_205_9 = 205, /* @9 */ YYSYMBOL_206_10 = 206, /* $@10 */ YYSYMBOL_207_11 = 207, /* $@11 */ YYSYMBOL_208_12 = 208, /* @12 */ YYSYMBOL_209_13 = 209, /* @13 */ YYSYMBOL_210_14 = 210, /* $@14 */ YYSYMBOL_211_15 = 211, /* $@15 */ YYSYMBOL_212_16 = 212, /* $@16 */ YYSYMBOL_213_17 = 213, /* $@17 */ YYSYMBOL_214_18 = 214, /* $@18 */ YYSYMBOL_215_19 = 215, /* $@19 */ YYSYMBOL_216_20 = 216, /* @20 */ YYSYMBOL_217_21 = 217, /* @21 */ YYSYMBOL_218_22 = 218, /* @22 */ YYSYMBOL_219_23 = 219, /* @23 */ YYSYMBOL_primary_value = 220, /* primary_value */ YYSYMBOL_then = 221, /* then */ YYSYMBOL_do = 222, /* do */ YYSYMBOL_if_tail = 223, /* if_tail */ YYSYMBOL_opt_else = 224, /* opt_else */ YYSYMBOL_for_var = 225, /* for_var */ YYSYMBOL_f_margs = 226, /* f_margs */ YYSYMBOL_227_24 = 227, /* $@24 */ YYSYMBOL_block_args_tail = 228, /* block_args_tail */ YYSYMBOL_opt_block_args_tail = 229, /* opt_block_args_tail */ YYSYMBOL_block_param = 230, /* block_param */ YYSYMBOL_opt_block_param = 231, /* opt_block_param */ YYSYMBOL_232_25 = 232, /* $@25 */ YYSYMBOL_block_param_def = 233, /* block_param_def */ YYSYMBOL_opt_bv_decl = 234, /* opt_bv_decl */ YYSYMBOL_bv_decls = 235, /* bv_decls */ YYSYMBOL_bvar = 236, /* bvar */ YYSYMBOL_f_larglist = 237, /* f_larglist */ YYSYMBOL_lambda_body = 238, /* lambda_body */ YYSYMBOL_239_26 = 239, /* @26 */ YYSYMBOL_do_block = 240, /* do_block */ YYSYMBOL_block_call = 241, /* block_call */ YYSYMBOL_method_call = 242, /* method_call */ YYSYMBOL_243_27 = 243, /* @27 */ YYSYMBOL_brace_block = 244, /* brace_block */ YYSYMBOL_245_28 = 245, /* @28 */ YYSYMBOL_case_body = 246, /* case_body */ YYSYMBOL_cases = 247, /* cases */ YYSYMBOL_opt_rescue = 248, /* opt_rescue */ YYSYMBOL_exc_list = 249, /* exc_list */ YYSYMBOL_exc_var = 250, /* exc_var */ YYSYMBOL_opt_ensure = 251, /* opt_ensure */ YYSYMBOL_literal = 252, /* literal */ YYSYMBOL_string = 253, /* string */ YYSYMBOL_string_fragment = 254, /* string_fragment */ YYSYMBOL_string_rep = 255, /* string_rep */ YYSYMBOL_string_interp = 256, /* string_interp */ YYSYMBOL_257_29 = 257, /* @29 */ YYSYMBOL_xstring = 258, /* xstring */ YYSYMBOL_regexp = 259, /* regexp */ YYSYMBOL_heredoc = 260, /* heredoc */ YYSYMBOL_heredoc_bodies = 261, /* heredoc_bodies */ YYSYMBOL_heredoc_body = 262, /* heredoc_body */ YYSYMBOL_heredoc_string_rep = 263, /* heredoc_string_rep */ YYSYMBOL_heredoc_string_interp = 264, /* heredoc_string_interp */ YYSYMBOL_265_30 = 265, /* @30 */ YYSYMBOL_words = 266, /* words */ YYSYMBOL_symbol = 267, /* symbol */ YYSYMBOL_basic_symbol = 268, /* basic_symbol */ YYSYMBOL_sym = 269, /* sym */ YYSYMBOL_symbols = 270, /* symbols */ YYSYMBOL_numeric = 271, /* numeric */ YYSYMBOL_variable = 272, /* variable */ YYSYMBOL_var_lhs = 273, /* var_lhs */ YYSYMBOL_var_ref = 274, /* var_ref */ YYSYMBOL_backref = 275, /* backref */ YYSYMBOL_superclass = 276, /* superclass */ YYSYMBOL_277_31 = 277, /* $@31 */ YYSYMBOL_f_opt_arglist_paren = 278, /* f_opt_arglist_paren */ YYSYMBOL_f_arglist_paren = 279, /* f_arglist_paren */ YYSYMBOL_f_arglist = 280, /* f_arglist */ YYSYMBOL_f_label = 281, /* f_label */ YYSYMBOL_f_kw = 282, /* f_kw */ YYSYMBOL_f_block_kw = 283, /* f_block_kw */ YYSYMBOL_f_block_kwarg = 284, /* f_block_kwarg */ YYSYMBOL_f_kwarg = 285, /* f_kwarg */ YYSYMBOL_kwrest_mark = 286, /* kwrest_mark */ YYSYMBOL_f_kwrest = 287, /* f_kwrest */ YYSYMBOL_args_tail = 288, /* args_tail */ YYSYMBOL_opt_args_tail = 289, /* opt_args_tail */ YYSYMBOL_f_args = 290, /* f_args */ YYSYMBOL_f_bad_arg = 291, /* f_bad_arg */ YYSYMBOL_f_norm_arg = 292, /* f_norm_arg */ YYSYMBOL_f_arg_item = 293, /* f_arg_item */ YYSYMBOL_294_32 = 294, /* @32 */ YYSYMBOL_f_arg = 295, /* f_arg */ YYSYMBOL_f_opt_asgn = 296, /* f_opt_asgn */ YYSYMBOL_f_opt = 297, /* f_opt */ YYSYMBOL_f_block_opt = 298, /* f_block_opt */ YYSYMBOL_f_block_optarg = 299, /* f_block_optarg */ YYSYMBOL_f_optarg = 300, /* f_optarg */ YYSYMBOL_restarg_mark = 301, /* restarg_mark */ YYSYMBOL_f_rest_arg = 302, /* f_rest_arg */ YYSYMBOL_blkarg_mark = 303, /* blkarg_mark */ YYSYMBOL_f_block_arg = 304, /* f_block_arg */ YYSYMBOL_opt_f_block_arg = 305, /* opt_f_block_arg */ YYSYMBOL_singleton = 306, /* singleton */ YYSYMBOL_307_33 = 307, /* $@33 */ YYSYMBOL_assoc_list = 308, /* assoc_list */ YYSYMBOL_assocs = 309, /* assocs */ YYSYMBOL_assoc = 310, /* assoc */ YYSYMBOL_operation = 311, /* operation */ YYSYMBOL_operation2 = 312, /* operation2 */ YYSYMBOL_operation3 = 313, /* operation3 */ YYSYMBOL_dot_or_colon = 314, /* dot_or_colon */ YYSYMBOL_call_op = 315, /* call_op */ YYSYMBOL_call_op2 = 316, /* call_op2 */ YYSYMBOL_opt_terms = 317, /* opt_terms */ YYSYMBOL_opt_nl = 318, /* opt_nl */ YYSYMBOL_rparen = 319, /* rparen */ YYSYMBOL_trailer = 320, /* trailer */ YYSYMBOL_term = 321, /* term */ YYSYMBOL_nl = 322, /* nl */ YYSYMBOL_terms = 323, /* terms */ YYSYMBOL_none = 324 /* none */ }; typedef enum yysymbol_kind_t yysymbol_kind_t; #ifdef short # undef short #endif /* On compilers that do not define __PTRDIFF_MAX__ etc., make sure and (if available) are included so that the code can choose integer types of a good width. */ #ifndef __PTRDIFF_MAX__ # include /* INFRINGES ON USER NAME SPACE */ # if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ # include /* INFRINGES ON USER NAME SPACE */ # define YY_STDINT_H # endif #endif /* Narrow types that promote to a signed type and that can represent a signed or unsigned integer of at least N bits. In tables they can save space and decrease cache pressure. Promoting to a signed type helps avoid bugs in integer arithmetic. */ #ifdef __INT_LEAST8_MAX__ typedef __INT_LEAST8_TYPE__ yytype_int8; #elif defined YY_STDINT_H typedef int_least8_t yytype_int8; #else typedef signed char yytype_int8; #endif #ifdef __INT_LEAST16_MAX__ typedef __INT_LEAST16_TYPE__ yytype_int16; #elif defined YY_STDINT_H typedef int_least16_t yytype_int16; #else typedef short yytype_int16; #endif /* Work around bug in HP-UX 11.23, which defines these macros incorrectly for preprocessor constants. This workaround can likely be removed in 2023, as HPE has promised support for HP-UX 11.23 (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of . */ #ifdef __hpux # undef UINT_LEAST8_MAX # undef UINT_LEAST16_MAX # define UINT_LEAST8_MAX 255 # define UINT_LEAST16_MAX 65535 #endif #if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ typedef __UINT_LEAST8_TYPE__ yytype_uint8; #elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ && UINT_LEAST8_MAX <= INT_MAX) typedef uint_least8_t yytype_uint8; #elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX typedef unsigned char yytype_uint8; #else typedef short yytype_uint8; #endif #if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ typedef __UINT_LEAST16_TYPE__ yytype_uint16; #elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ && UINT_LEAST16_MAX <= INT_MAX) typedef uint_least16_t yytype_uint16; #elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX typedef unsigned short yytype_uint16; #else typedef int yytype_uint16; #endif #ifndef YYPTRDIFF_T # if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ # define YYPTRDIFF_T __PTRDIFF_TYPE__ # define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ # elif defined PTRDIFF_MAX # ifndef ptrdiff_t # include /* INFRINGES ON USER NAME SPACE */ # endif # define YYPTRDIFF_T ptrdiff_t # define YYPTRDIFF_MAXIMUM PTRDIFF_MAX # else # define YYPTRDIFF_T long # define YYPTRDIFF_MAXIMUM LONG_MAX # endif #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned # endif #endif #define YYSIZE_MAXIMUM \ YY_CAST (YYPTRDIFF_T, \ (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ ? YYPTRDIFF_MAXIMUM \ : YY_CAST (YYSIZE_T, -1))) #define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) /* Stored state numbers (used for stacks). */ typedef yytype_int16 yy_state_t; /* State numbers in computations. */ typedef int yy_state_fast_t; #ifndef YY_ # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(Msgid) dgettext ("bison-runtime", Msgid) # endif # endif # ifndef YY_ # define YY_(Msgid) Msgid # endif #endif #ifndef YY_ATTRIBUTE_PURE # if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) # define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) # else # define YY_ATTRIBUTE_PURE # endif #endif #ifndef YY_ATTRIBUTE_UNUSED # if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) # define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) # else # define YY_ATTRIBUTE_UNUSED # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YY_USE(E) ((void) (E)) #else # define YY_USE(E) /* empty */ #endif /* Suppress an incorrect diagnostic about yylval being uninitialized. */ #if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__ # if __GNUC__ * 100 + __GNUC_MINOR__ < 407 # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") # else # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") # endif # define YY_IGNORE_MAYBE_UNINITIALIZED_END \ _Pragma ("GCC diagnostic pop") #else # define YY_INITIAL_VALUE(Value) Value #endif #ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_END #endif #ifndef YY_INITIAL_VALUE # define YY_INITIAL_VALUE(Value) /* Nothing. */ #endif #if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ # define YY_IGNORE_USELESS_CAST_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") # define YY_IGNORE_USELESS_CAST_END \ _Pragma ("GCC diagnostic pop") #endif #ifndef YY_IGNORE_USELESS_CAST_BEGIN # define YY_IGNORE_USELESS_CAST_BEGIN # define YY_IGNORE_USELESS_CAST_END #endif #define YY_ASSERT(E) ((void) (0 && (E))) #if 1 /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS # include /* INFRINGES ON USER NAME SPACE */ /* Use EXIT_SUCCESS as a witness for stdlib.h. */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's 'empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined EXIT_SUCCESS \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined EXIT_SUCCESS void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined EXIT_SUCCESS void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* 1 */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yy_state_t yyss_alloc; YYSTYPE yyvs_alloc; YYLTYPE yyls_alloc; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE) \ + YYSIZEOF (YYLTYPE)) \ + 2 * YYSTACK_GAP_MAXIMUM) # define YYCOPY_NEEDED 1 /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack_alloc, Stack) \ do \ { \ YYPTRDIFF_T yynewbytes; \ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ Stack = &yyptr->Stack_alloc; \ yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / YYSIZEOF (*yyptr); \ } \ while (0) #endif #if defined YYCOPY_NEEDED && YYCOPY_NEEDED /* Copy COUNT objects from SRC to DST. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(Dst, Src, Count) \ __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) # else # define YYCOPY(Dst, Src, Count) \ do \ { \ YYPTRDIFF_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (Dst)[yyi] = (Src)[yyi]; \ } \ while (0) # endif # endif #endif /* !YYCOPY_NEEDED */ /* YYFINAL -- State number of the termination state. */ #define YYFINAL 106 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 13386 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 149 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 176 /* YYNRULES -- Number of rules. */ #define YYNRULES 620 /* YYNSTATES -- Number of states. */ #define YYNSTATES 1085 /* YYMAXUTOK -- Last valid token kind. */ #define YYMAXUTOK 377 /* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM as returned by yylex, with out-of-bounds checking. */ #define YYTRANSLATE(YYX) \ (0 <= (YYX) && (YYX) <= YYMAXUTOK \ ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ : YYSYMBOL_YYUNDEF) /* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM as returned by yylex. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 148, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 135, 2, 2, 2, 133, 128, 2, 144, 145, 131, 129, 142, 130, 147, 132, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 123, 146, 125, 121, 124, 122, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 140, 2, 141, 127, 2, 143, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 138, 126, 139, 136, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 134, 137 }; #if YYDEBUG /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_int16 yyrline[] = { 0, 1665, 1665, 1665, 1676, 1682, 1686, 1691, 1695, 1701, 1703, 1702, 1716, 1743, 1749, 1753, 1758, 1762, 1768, 1768, 1772, 1776, 1780, 1784, 1788, 1792, 1796, 1801, 1802, 1806, 1810, 1814, 1818, 1824, 1827, 1831, 1835, 1839, 1843, 1847, 1852, 1856, 1865, 1874, 1883, 1892, 1899, 1900, 1904, 1908, 1909, 1913, 1917, 1921, 1925, 1929, 1939, 1938, 1953, 1962, 1963, 1966, 1967, 1974, 1973, 1988, 1992, 1997, 2001, 2006, 2010, 2015, 2019, 2023, 2027, 2031, 2037, 2041, 2047, 2048, 2054, 2058, 2062, 2066, 2070, 2074, 2078, 2082, 2086, 2090, 2096, 2097, 2103, 2107, 2113, 2117, 2123, 2127, 2131, 2135, 2139, 2143, 2149, 2155, 2162, 2166, 2170, 2174, 2178, 2182, 2188, 2194, 2199, 2205, 2209, 2212, 2216, 2220, 2227, 2228, 2229, 2230, 2235, 2242, 2243, 2246, 2250, 2250, 2256, 2257, 2258, 2259, 2260, 2261, 2262, 2263, 2264, 2265, 2266, 2267, 2268, 2269, 2270, 2271, 2272, 2273, 2274, 2275, 2276, 2277, 2278, 2279, 2280, 2281, 2282, 2283, 2284, 2285, 2288, 2288, 2288, 2289, 2289, 2290, 2290, 2290, 2291, 2291, 2291, 2291, 2292, 2292, 2292, 2293, 2293, 2293, 2294, 2294, 2294, 2294, 2295, 2295, 2295, 2295, 2296, 2296, 2296, 2296, 2297, 2297, 2297, 2297, 2298, 2298, 2298, 2298, 2299, 2299, 2302, 2306, 2310, 2314, 2318, 2322, 2326, 2331, 2336, 2341, 2345, 2349, 2353, 2357, 2361, 2365, 2369, 2373, 2377, 2381, 2385, 2389, 2393, 2397, 2401, 2405, 2409, 2413, 2417, 2421, 2425, 2429, 2433, 2437, 2441, 2445, 2449, 2453, 2457, 2461, 2465, 2469, 2473, 2477, 2481, 2485, 2489, 2498, 2507, 2516, 2525, 2531, 2532, 2537, 2541, 2548, 2552, 2559, 2563, 2572, 2589, 2590, 2593, 2594, 2595, 2600, 2605, 2612, 2618, 2623, 2628, 2633, 2640, 2640, 2651, 2655, 2661, 2665, 2671, 2674, 2680, 2684, 2689, 2694, 2698, 2704, 2709, 2713, 2719, 2720, 2721, 2722, 2723, 2724, 2725, 2726, 2731, 2730, 2742, 2746, 2741, 2751, 2751, 2755, 2759, 2763, 2767, 2772, 2777, 2781, 2785, 2789, 2793, 2797, 2798, 2804, 2811, 2803, 2824, 2832, 2840, 2840, 2840, 2847, 2847, 2847, 2854, 2860, 2865, 2867, 2864, 2876, 2874, 2892, 2897, 2890, 2914, 2912, 2928, 2938, 2949, 2953, 2957, 2961, 2967, 2974, 2975, 2976, 2979, 2980, 2983, 2984, 2992, 2993, 2999, 3003, 3006, 3010, 3014, 3018, 3023, 3027, 3031, 3035, 3041, 3040, 3050, 3054, 3058, 3062, 3068, 3073, 3078, 3082, 3086, 3090, 3094, 3098, 3102, 3106, 3110, 3114, 3118, 3122, 3126, 3130, 3134, 3140, 3145, 3152, 3152, 3156, 3161, 3168, 3172, 3178, 3179, 3182, 3187, 3190, 3194, 3200, 3204, 3211, 3210, 3227, 3237, 3241, 3246, 3253, 3257, 3261, 3265, 3269, 3273, 3277, 3281, 3285, 3292, 3291, 3306, 3305, 3321, 3329, 3338, 3341, 3348, 3351, 3355, 3356, 3359, 3363, 3366, 3370, 3373, 3374, 3375, 3376, 3379, 3380, 3386, 3387, 3388, 3392, 3405, 3406, 3412, 3417, 3416, 3426, 3430, 3436, 3440, 3453, 3457, 3463, 3466, 3467, 3470, 3476, 3482, 3483, 3486, 3493, 3492, 3505, 3509, 3523, 3527, 3539, 3546, 3553, 3554, 3555, 3556, 3557, 3561, 3567, 3571, 3581, 3582, 3583, 3587, 3593, 3597, 3601, 3605, 3609, 3615, 3619, 3625, 3629, 3633, 3637, 3641, 3645, 3649, 3657, 3664, 3670, 3671, 3675, 3679, 3678, 3695, 3696, 3699, 3705, 3709, 3715, 3716, 3720, 3724, 3730, 3734, 3740, 3746, 3753, 3759, 3766, 3770, 3776, 3780, 3786, 3787, 3790, 3794, 3800, 3804, 3808, 3812, 3818, 3823, 3828, 3832, 3836, 3840, 3844, 3848, 3852, 3856, 3860, 3864, 3868, 3872, 3876, 3880, 3885, 3891, 3896, 3901, 3906, 3911, 3918, 3922, 3929, 3934, 3933, 3945, 3949, 3955, 3963, 3971, 3979, 3983, 3989, 3993, 3999, 4000, 4003, 4008, 4015, 4016, 4019, 4023, 4029, 4033, 4039, 4045, 4045, 4052, 4053, 4059, 4064, 4070, 4076, 4081, 4085, 4090, 4095, 4105, 4110, 4116, 4117, 4118, 4121, 4122, 4123, 4124, 4127, 4128, 4129, 4132, 4133, 4136, 4140, 4146, 4147, 4153, 4154, 4157, 4158, 4161, 4164, 4165, 4166, 4169, 4170, 4173, 4178, 4181, 4182, 4186 }; #endif /** Accessing symbol of state STATE. */ #define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) #if 1 /* The user-facing name of the symbol whose (internal) number is YYSYMBOL. No bounds checking. */ static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "\"end of file\"", "error", "\"invalid token\"", "\"'class'\"", "\"'module'\"", "\"'def'\"", "\"'begin'\"", "\"'if'\"", "\"'unless'\"", "\"'while'\"", "\"'until'\"", "\"'for'\"", "\"'undef'\"", "\"'rescue'\"", "\"'ensure'\"", "\"'end'\"", "\"'then'\"", "\"'elsif'\"", "\"'else'\"", "\"'case'\"", "\"'when'\"", "\"'break'\"", "\"'next'\"", "\"'redo'\"", "\"'retry'\"", "\"'in'\"", "\"'do'\"", "\"'do' for condition\"", "\"'do' for block\"", "\"'do' for lambda\"", "\"'return'\"", "\"'yield'\"", "\"'super'\"", "\"'self'\"", "\"'nil'\"", "\"'true'\"", "\"'false'\"", "\"'and'\"", "\"'or'\"", "\"'not'\"", "\"'if' modifier\"", "\"'unless' modifier\"", "\"'while' modifier\"", "\"'until' modifier\"", "\"'rescue' modifier\"", "\"'alias'\"", "\"'BEGIN'\"", "\"'END'\"", "\"'__LINE__'\"", "\"'__FILE__'\"", "\"'__ENCODING__'\"", "\"local variable or method\"", "\"method\"", "\"global variable\"", "\"instance variable\"", "\"constant\"", "\"class variable\"", "\"label\"", "\"integer literal\"", "\"float literal\"", "\"character literal\"", "tXSTRING", "tREGEXP", "tSTRING", "tSTRING_PART", "tSTRING_MID", "tNTH_REF", "tBACK_REF", "tREGEXP_END", "\"numbered parameter\"", "\"unary plus\"", "\"unary minus\"", "\"<=>\"", "\"==\"", "\"===\"", "\"!=\"", "\">=\"", "\"<=\"", "\"&&\"", "\"||\"", "\"=~\"", "\"!~\"", "\"..\"", "\"...\"", "tBDOT2", "tBDOT3", "tAREF", "tASET", "\"<<\"", "\">>\"", "\"::\"", "tCOLON3", "tOP_ASGN", "\"=>\"", "tLPAREN", "\"(\"", "\")\"", "\"[\"", "tLBRACE", "\"{\"", "\"*\"", "tPOW", "\"**\"", "\"&\"", "\"->\"", "\"&.\"", "\"symbol\"", "\"string literal\"", "tXSTRING_BEG", "tSTRING_DVAR", "tREGEXP_BEG", "tWORDS_BEG", "tSYMBOLS_BEG", "tLAMBEG", "\"here document\"", "tHEREDOC_END", "tLITERAL_DELIM", "tHD_LITERAL_DELIM", "tHD_STRING_PART", "tHD_STRING_MID", "tLOWEST", "'='", "'?'", "':'", "'>'", "'<'", "'|'", "'^'", "'&'", "'+'", "'-'", "'*'", "'/'", "'%'", "tUMINUS_NUM", "'!'", "'~'", "tLAST_TOKEN", "'{'", "'}'", "'['", "']'", "','", "'`'", "'('", "')'", "';'", "'.'", "'\\n'", "$accept", "$@1", "program", "top_compstmt", "top_stmts", "top_stmt", "@2", "bodystmt", "compstmt", "stmts", "$@3", "stmt", "command_asgn", "command_rhs", "expr", "defn_head", "$@4", "defs_head", "expr_value", "command_call", "block_command", "$@5", "cmd_brace_block", "command", "mlhs", "mlhs_inner", "mlhs_basic", "mlhs_item", "mlhs_list", "mlhs_post", "mlhs_node", "lhs", "cname", "cpath", "fname", "fsym", "undef_list", "$@6", "op", "reswords", "arg", "aref_args", "arg_rhs", "paren_args", "opt_paren_args", "opt_call_args", "call_args", "@7", "command_args", "block_arg", "opt_block_arg", "comma", "args", "mrhs", "primary", "@8", "@9", "$@10", "$@11", "@12", "@13", "$@14", "$@15", "$@16", "$@17", "$@18", "$@19", "@20", "@21", "@22", "@23", "primary_value", "then", "do", "if_tail", "opt_else", "for_var", "f_margs", "$@24", "block_args_tail", "opt_block_args_tail", "block_param", "opt_block_param", "$@25", "block_param_def", "opt_bv_decl", "bv_decls", "bvar", "f_larglist", "lambda_body", "@26", "do_block", "block_call", "method_call", "@27", "brace_block", "@28", "case_body", "cases", "opt_rescue", "exc_list", "exc_var", "opt_ensure", "literal", "string", "string_fragment", "string_rep", "string_interp", "@29", "xstring", "regexp", "heredoc", "heredoc_bodies", "heredoc_body", "heredoc_string_rep", "heredoc_string_interp", "@30", "words", "symbol", "basic_symbol", "sym", "symbols", "numeric", "variable", "var_lhs", "var_ref", "backref", "superclass", "$@31", "f_opt_arglist_paren", "f_arglist_paren", "f_arglist", "f_label", "f_kw", "f_block_kw", "f_block_kwarg", "f_kwarg", "kwrest_mark", "f_kwrest", "args_tail", "opt_args_tail", "f_args", "f_bad_arg", "f_norm_arg", "f_arg_item", "@32", "f_arg", "f_opt_asgn", "f_opt", "f_block_opt", "f_block_optarg", "f_optarg", "restarg_mark", "f_rest_arg", "blkarg_mark", "f_block_arg", "opt_f_block_arg", "singleton", "$@33", "assoc_list", "assocs", "assoc", "operation", "operation2", "operation3", "dot_or_colon", "call_op", "call_op2", "opt_terms", "opt_nl", "rparen", "trailer", "term", "nl", "terms", "none", YY_NULLPTR }; static const char * yysymbol_name (yysymbol_kind_t yysymbol) { return yytname[yysymbol]; } #endif #define YYPACT_NINF (-870) #define yypact_value_is_default(Yyn) \ ((Yyn) == YYPACT_NINF) #define YYTABLE_NINF (-621) #define yytable_value_is_error(Yyn) \ ((Yyn) == YYTABLE_NINF) /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ static const yytype_int16 yypact[] = { -870, 3957, 130, 8673, 10797, 11139, 6981, -870, 10443, 10443, -870, -870, 10911, 8163, 6597, 8909, 8909, -870, -870, 8909, 4478, 1831, -870, -870, -870, -870, 19, 8163, -870, 36, -870, -870, -870, 7123, 4070, -870, -870, 7265, -870, -870, -870, -870, -870, -870, -870, 89, 10561, 10561, 10561, 10561, 152, 5856, 1426, 9381, 9735, 8445, -870, 7881, 422, 515, 1221, 920, 1245, -870, 90, 10679, 10561, -870, 1443, -870, 988, -870, 306, 1273, 1273, -870, -870, 138, 69, -870, 74, 11025, -870, 107, 2381, 735, 769, 47, 53, -870, 399, -870, -870, -870, -870, -870, -870, -870, -870, -870, 319, 119, -870, 347, 98, -870, -870, -870, -870, -870, -870, 124, 124, 19, 743, 773, -870, 10443, 120, 5975, 292, 1295, 1295, -870, 147, -870, 775, -870, -870, 98, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, 23, 51, 54, 87, -870, -870, -870, -870, -870, -870, 93, 102, 112, 145, -870, 193, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, 200, 5034, 222, 306, 1273, 1273, 188, 134, 13191, 831, 218, 199, 224, 188, 10443, 10443, 852, 243, -870, -870, 960, 275, 79, 104, -870, -870, -870, -870, -870, -870, -870, -870, -870, 8022, -870, -870, 167, -870, -870, -870, -870, -870, -870, 1443, -870, 620, -870, 310, -870, -870, 1443, 4206, 122, 10561, 10561, 10561, 10561, -870, 13129, -870, -870, 195, 320, 195, -870, -870, -870, 9027, -870, -870, 8909, -870, -870, -870, -870, 6597, 6835, -870, 250, 6094, -870, 987, 296, 13253, 13253, 425, 8791, 5856, 263, 1443, 988, 1443, 317, -870, 8791, 1443, 281, 1129, 1129, -870, 13129, 302, 1129, -870, 389, 11253, 316, 1004, 1007, 1068, 1402, -870, -870, -870, -870, -870, 1252, -870, -870, -870, -870, -870, -870, 666, 1255, -870, -870, 1021, -870, 1242, -870, 1317, -870, 1320, 374, 378, -870, -870, -870, -870, 6359, 10443, 10443, 10443, 10443, 8791, 10443, 10443, 68, -870, -870, -870, -870, 424, 1443, -870, -870, -870, -870, -870, -870, -870, 1589, 369, 381, 5034, 10561, -870, 367, 465, 380, -870, 1443, -870, -870, -870, 392, 10561, -870, 410, 509, 423, 518, -870, -870, 462, 5034, -870, -870, 9853, -870, 5856, 8559, 444, 9853, 10561, 10561, 10561, 10561, 10561, 10561, 10561, 10561, 10561, 10561, 10561, 10561, 10561, 10561, 539, 10561, 10561, 10561, 10561, 10561, 10561, 10561, 10561, 10561, 10561, 10561, 10561, 3007, -870, 8909, -870, 3752, -870, -870, 12649, -870, -870, -870, -870, 10679, 10679, -870, 503, -870, 306, -870, 1093, -870, -870, -870, -870, -870, -870, 11531, 8909, 11617, 5034, 10443, -870, -870, -870, 595, 604, 225, 501, 505, -870, 5180, 608, 10561, 11703, 8909, 11789, 10561, 10561, 5472, 308, 308, 106, 11875, 8909, 11961, -870, 566, -870, 6094, 310, -870, -870, 9971, 621, -870, 10561, 10561, 13191, 13191, 13191, 10561, -870, -870, 9145, -870, 10561, -870, 9499, 6716, 493, 1443, 195, 195, -870, -870, 318, 499, -870, -870, -870, 8163, 5591, 506, 11703, 11789, 10561, 988, 1443, -870, -870, 6478, 504, 988, -870, -870, 9617, -870, 1443, 9735, -870, -870, -870, 1093, 74, 11253, -870, 11253, 12047, 8909, 12133, 2010, -870, -870, 507, -870, 1345, 6094, 666, -870, -870, -870, -870, -870, -870, -870, 10561, 10561, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, 1431, 1443, 1443, 513, 10679, 642, 13191, 605, -870, -870, -870, 37, -870, -870, 1691, -870, 13191, 2010, -870, -870, 1712, -870, -870, 10679, 644, 83, 10561, -870, 12845, 195, -870, 1443, 11253, 521, -870, -870, -870, 616, 549, 3375, -870, -870, 1116, 307, 2135, 2135, 2135, 2135, 2029, 2029, 3438, 2505, 2135, 2135, 13253, 13253, 1521, 1521, -870, 296, 13191, 2029, 2029, 1668, 1668, 1694, 237, 237, 296, 296, 296, 3634, 7621, 4750, 7739, -870, 124, -870, 530, 195, 432, -870, 450, -870, -870, 4342, -870, -870, 2280, 83, 83, -870, 2882, -870, -870, -870, -870, -870, 1443, 10443, 5034, 874, 654, -870, 124, 536, 124, 671, 318, 8304, -870, 10089, 674, -870, 10561, 10561, 278, -870, 7383, 7502, 556, 309, 327, 674, -870, -870, -870, -870, 64, 73, 558, 111, 121, 10443, 8163, 572, 690, 13191, 81, -870, 13191, 13191, 13191, 287, 10561, 13129, -870, 195, 13191, -870, -870, -870, -870, 9263, 9499, -870, -870, -870, 576, -870, -870, 22, 988, 1443, 1129, 444, -870, 874, 654, 575, 1031, 1156, -870, 113, 2010, -870, 579, -870, 296, 296, -870, -870, 912, 1443, 578, -870, -870, 1929, 681, 12721, -870, 679, 424, -870, 380, -870, 1443, -870, -870, 586, 589, 606, -870, 607, 679, 606, 710, 12783, -870, -870, 2010, 5034, -870, -870, 12916, 10207, -870, -870, 11253, 8791, 10679, 10561, 12219, 8909, 12305, 561, 10679, 10679, -870, 503, 551, 9145, 10679, 10679, -870, 503, 53, 138, 5034, 6094, 83, -870, 1443, 740, -870, -870, -870, -870, 12845, -870, 665, -870, 5737, 746, -870, 10443, 749, -870, 10561, 10561, 379, 10561, 10561, 753, 6240, 6240, 128, 308, -870, -870, -870, 10325, 5326, 13191, -870, 6716, 195, -870, -870, -870, 161, 625, 1449, 5034, 6094, -870, -870, -870, 619, -870, 1484, 1443, 10561, 10561, -870, -870, 2010, -870, 1712, -870, 1712, -870, 1712, -870, -870, 10561, 10561, -870, -870, -870, 11367, -870, 632, 380, 637, 11367, -870, 640, 645, -870, 774, 10561, 12987, -870, -870, 13191, 4614, 4886, 647, 407, 463, 10561, 10561, -870, -870, -870, -870, -870, 10679, -870, -870, -870, -870, -870, -870, -870, 778, 656, 6094, 5034, -870, -870, 11481, 188, -870, -870, 6240, -870, -870, 188, -870, 10561, -870, 782, 790, -870, 13191, 168, -870, 9499, -870, 1615, 791, 672, 1457, 1457, 1063, -870, 13191, 13191, 606, 675, 606, 606, 13191, 13191, 687, 688, 763, 1119, 605, -870, -870, 1421, -870, 1119, 2010, -870, 1712, -870, -870, 13058, 478, 13191, 13191, -870, -870, -870, -870, 691, 805, 776, -870, 1127, 1007, 1068, 5034, -870, 5180, -870, -870, 6240, -870, -870, -870, -870, 693, -870, -870, -870, -870, 696, 696, 1457, 697, -870, 1712, -870, -870, -870, -870, -870, -870, 12391, -870, 380, 605, -870, -870, 701, 705, 708, -870, 720, 708, -870, -870, 1093, 12477, 8909, 12563, 604, 278, 851, 1615, 287, 1457, 696, 1457, 606, 723, 733, -870, 2010, -870, 1712, -870, 1712, -870, 1712, -870, -870, 874, 654, 745, 299, 535, -870, -870, -870, -870, 696, -870, 708, 751, 708, 708, 161, -870, 1712, -870, -870, -870, 708, -870 }; /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. Performed when YYTABLE does not specify something else to do. Zero means the default is an error. */ static const yytype_int16 yydefact[] = { 2, 0, 0, 0, 0, 0, 0, 294, 0, 0, 318, 321, 0, 0, 606, 338, 339, 340, 341, 306, 270, 270, 491, 490, 492, 493, 608, 0, 10, 0, 495, 494, 496, 481, 592, 483, 482, 485, 484, 477, 478, 438, 439, 497, 498, 489, 0, 0, 0, 0, 0, 0, 296, 620, 620, 88, 313, 0, 0, 0, 0, 0, 0, 453, 0, 0, 0, 3, 606, 6, 9, 27, 33, 545, 545, 49, 60, 59, 0, 76, 0, 80, 90, 0, 54, 248, 0, 61, 311, 286, 287, 436, 288, 289, 290, 434, 433, 465, 435, 432, 488, 0, 291, 292, 270, 5, 1, 8, 338, 339, 306, 620, 414, 0, 113, 114, 489, 0, 0, 0, 0, 545, 545, 116, 499, 342, 0, 488, 292, 0, 334, 168, 178, 169, 165, 194, 195, 196, 197, 176, 191, 184, 174, 173, 189, 172, 171, 167, 192, 166, 179, 183, 185, 177, 170, 186, 193, 188, 187, 180, 190, 175, 164, 182, 181, 163, 161, 162, 158, 159, 160, 118, 120, 119, 153, 154, 131, 132, 133, 140, 137, 139, 134, 135, 155, 156, 141, 142, 146, 149, 150, 136, 138, 128, 129, 130, 143, 144, 145, 147, 148, 151, 152, 157, 576, 55, 121, 122, 575, 0, 0, 0, 58, 545, 545, 0, 0, 54, 0, 488, 0, 292, 0, 0, 0, 112, 0, 353, 352, 0, 0, 488, 292, 187, 180, 190, 175, 158, 159, 160, 118, 119, 0, 123, 125, 20, 124, 456, 461, 460, 614, 616, 606, 617, 0, 458, 0, 618, 615, 607, 590, 489, 278, 589, 273, 0, 265, 277, 74, 269, 620, 436, 620, 580, 75, 73, 620, 259, 307, 0, 72, 258, 413, 71, 606, 0, 18, 0, 0, 221, 0, 222, 209, 212, 303, 0, 0, 0, 606, 15, 606, 78, 14, 0, 606, 0, 611, 611, 249, 0, 0, 611, 578, 0, 0, 86, 0, 96, 103, 545, 471, 470, 472, 473, 467, 0, 469, 468, 440, 445, 444, 447, 0, 0, 442, 449, 0, 451, 0, 463, 0, 475, 0, 479, 480, 53, 236, 237, 4, 607, 0, 0, 0, 0, 0, 0, 0, 552, 548, 547, 546, 549, 550, 0, 554, 566, 521, 522, 570, 569, 565, 545, 0, 507, 0, 514, 519, 620, 524, 620, 544, 0, 551, 553, 556, 530, 0, 563, 530, 568, 530, 572, 528, 503, 0, 0, 401, 403, 0, 92, 0, 84, 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 211, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 603, 620, 602, 0, 605, 604, 0, 418, 416, 312, 437, 0, 0, 407, 65, 310, 331, 113, 114, 115, 479, 480, 507, 500, 329, 0, 620, 0, 0, 0, 601, 600, 56, 0, 620, 303, 0, 0, 344, 0, 343, 0, 0, 620, 0, 0, 0, 0, 0, 0, 303, 0, 620, 0, 326, 0, 126, 0, 0, 457, 459, 0, 0, 619, 584, 585, 279, 588, 272, 0, 608, 266, 0, 275, 0, 267, 0, 606, 0, 606, 620, 620, 260, 271, 606, 0, 309, 52, 609, 0, 0, 0, 0, 0, 0, 17, 606, 301, 13, 607, 77, 297, 300, 304, 613, 250, 612, 613, 252, 305, 579, 102, 94, 0, 89, 0, 0, 620, 0, 545, 314, 398, 530, 474, 0, 0, 448, 454, 441, 443, 450, 452, 464, 476, 0, 0, 7, 21, 22, 23, 24, 25, 50, 51, 511, 558, 512, 510, 0, 606, 606, 530, 0, 0, 513, 0, 526, 574, 523, 0, 527, 508, 0, 537, 559, 0, 540, 567, 0, 542, 571, 0, 0, 620, 278, 28, 30, 0, 31, 606, 0, 82, 93, 48, 34, 46, 0, 253, 198, 29, 0, 292, 226, 231, 232, 233, 228, 230, 240, 241, 234, 235, 207, 210, 238, 239, 32, 218, 608, 227, 229, 223, 224, 225, 213, 214, 215, 216, 217, 593, 598, 594, 599, 412, 270, 410, 0, 620, 593, 595, 594, 596, 411, 270, 593, 594, 270, 620, 620, 35, 253, 199, 45, 206, 63, 66, 0, 0, 0, 113, 114, 117, 0, 0, 620, 0, 606, 0, 295, 620, 620, 424, 0, 0, 620, 345, 597, 302, 0, 593, 594, 620, 347, 319, 346, 322, 597, 302, 0, 593, 594, 0, 0, 0, 0, 277, 0, 325, 583, 586, 582, 276, 281, 280, 274, 620, 587, 581, 257, 255, 261, 262, 264, 308, 610, 19, 0, 26, 205, 79, 16, 606, 611, 95, 87, 99, 101, 0, 98, 100, 608, 0, 0, 466, 0, 455, 219, 220, 552, 550, 361, 606, 354, 506, 504, 0, 41, 244, 336, 0, 0, 520, 620, 573, 0, 529, 557, 530, 530, 530, 564, 530, 552, 530, 43, 246, 337, 389, 387, 0, 386, 385, 285, 0, 91, 85, 0, 0, 0, 0, 0, 620, 0, 0, 0, 0, 409, 69, 415, 262, 0, 0, 408, 67, 404, 62, 0, 0, 620, 332, 0, 0, 415, 335, 577, 57, 425, 426, 620, 427, 0, 620, 350, 0, 0, 348, 0, 0, 415, 0, 0, 0, 0, 0, 415, 0, 127, 462, 324, 0, 0, 282, 268, 606, 620, 11, 298, 251, 97, 0, 391, 0, 0, 315, 446, 362, 359, 555, 0, 606, 0, 0, 525, 509, 0, 533, 0, 535, 0, 541, 0, 538, 543, 0, 0, 384, 608, 608, 516, 517, 620, 620, 369, 0, 561, 369, 369, 367, 0, 281, 283, 83, 47, 254, 593, 594, 0, 593, 594, 0, 0, 40, 203, 39, 204, 70, 0, 37, 201, 38, 202, 68, 405, 406, 0, 0, 0, 0, 501, 330, 0, 0, 429, 351, 0, 12, 431, 0, 316, 0, 317, 0, 0, 327, 280, 620, 256, 263, 397, 0, 0, 0, 0, 0, 357, 505, 42, 245, 530, 530, 530, 530, 44, 247, 0, 0, 0, 515, 0, 365, 366, 369, 377, 560, 0, 380, 0, 382, 402, 284, 415, 243, 242, 36, 200, 419, 417, 0, 0, 0, 428, 0, 104, 111, 0, 430, 0, 320, 323, 0, 421, 422, 420, 395, 608, 393, 396, 400, 399, 363, 360, 0, 355, 534, 0, 531, 536, 539, 390, 388, 303, 0, 518, 620, 0, 368, 375, 369, 369, 369, 562, 369, 369, 64, 333, 110, 0, 620, 0, 620, 620, 0, 0, 392, 0, 358, 0, 530, 597, 302, 364, 0, 372, 0, 374, 0, 381, 0, 378, 383, 107, 109, 0, 593, 594, 423, 349, 328, 394, 356, 532, 369, 369, 369, 369, 105, 373, 0, 370, 376, 379, 369, 371 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { -870, -870, -870, 372, -870, 43, -870, -299, 301, -870, -870, 46, -165, -305, 34, 1724, -870, 1884, 21, -50, -870, -870, -483, -12, 885, -205, 15, -27, -285, -477, -47, 2818, -59, 893, 5, -18, -870, -870, 38, -870, 1204, -870, 184, 56, -252, -325, 96, -870, 10, -420, -231, 14, 52, -358, 28, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, 9, -188, -444, -141, -623, -870, -870, -870, 115, 258, -870, -583, -870, -870, -464, -870, -142, -870, -870, -870, 91, -870, -870, -870, -87, -870, -470, -870, -131, -870, -870, -870, -870, -870, 103, 41, -160, -870, -870, -870, -870, -870, -277, -870, 670, -870, -870, -870, -11, -870, -870, -870, 2545, 2999, 913, 2385, -870, -870, 60, 512, 25, 135, 341, -40, -870, -870, -870, 117, 730, -185, -226, -824, -706, -513, -870, 186, -751, -547, -869, -42, -536, -870, 252, -870, -44, -343, -870, -870, -870, 101, -445, 600, -353, -870, -870, -60, -870, 24, -20, 492, -250, 508, -280, -46, -1 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { 0, 1, 2, 67, 68, 69, 287, 465, 466, 298, 521, 299, 71, 616, 72, 213, 689, 214, 215, 75, 76, 820, 677, 77, 78, 300, 79, 80, 81, 546, 82, 216, 123, 124, 243, 244, 245, 714, 654, 207, 84, 305, 620, 655, 278, 510, 511, 279, 280, 269, 503, 539, 659, 610, 85, 210, 303, 743, 304, 319, 753, 223, 844, 224, 845, 713, 1001, 680, 678, 929, 460, 290, 471, 705, 836, 837, 230, 763, 954, 1027, 974, 888, 791, 889, 792, 861, 1006, 1007, 552, 865, 605, 397, 87, 88, 670, 447, 669, 494, 1004, 692, 830, 933, 937, 89, 90, 91, 333, 334, 557, 92, 93, 94, 558, 253, 254, 255, 489, 95, 96, 97, 327, 98, 99, 219, 220, 102, 221, 456, 679, 372, 373, 374, 375, 376, 891, 892, 377, 378, 379, 777, 595, 381, 382, 383, 384, 580, 385, 386, 387, 896, 897, 388, 389, 390, 391, 392, 588, 209, 461, 310, 513, 273, 129, 684, 657, 464, 459, 438, 517, 862, 518, 537, 257, 258, 259, 302 }; /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule whose number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_int16 yytable[] = { 105, 441, 246, 266, 266, 520, 285, 266, 315, 286, 86, 205, 86, 126, 126, 345, 246, 218, 218, 281, 716, 229, 349, 218, 218, 218, 435, 437, 218, 545, 222, 283, 125, 125, 479, 252, 592, 707, 256, 895, 125, 507, 212, 212, 206, 621, 107, 70, 212, 70, 782, 206, 308, 312, 402, 559, 868, 540, 779, 451, 86, 542, 326, 729, 316, 206, 301, 270, 270, 833, 746, 270, 393, 393, 218, 585, 277, 282, 656, 439, 843, 778, 665, 125, 726, 668, 818, 819, 726, -107, 316, 528, 348, 553, 729, 206, 604, 470, -109, 395, 336, 338, 340, 342, -104, 306, 686, 1032, 658, 125, 281, 268, 274, -491, 446, 275, 272, 272, 271, 271, 272, 656, 271, 665, 439, 576, 218, 1008, 86, -111, 106, -110, 686, 685, 394, 797, -106, 436, 671, 674, 368, -490, 863, -77, -492, 582, -108, 395, 343, 344, 700, 448, 432, -105, 307, 311, 271, 271, 476, 710, 445, 687, 789, 284, -91, 369, 396, 277, 282, 485, -491, 449, 686, 561, 288, 450, 561, -493, 561, 497, 561, -487, 561, -495, 1032, 445, 832, -415, 493, 577, 398, 440, -494, 442, 434, 611, 247, 686, -490, 248, 249, -492, -496, 598, 470, 601, -99, 294, -593, 790, -112, 443, 393, 393, -487, -101, 399, -594, 779, 86, 1008, -96, 895, 502, 749, 895, 864, 250, 403, 251, 525, 779, 218, 218, -493, -481, 440, 928, 615, 395, -495, 778, 276, -112, 480, 481, -103, 326, -102, -494, 1014, -415, 531, -98, 778, 473, 550, 212, 212, -496, 538, 538, 545, -100, 266, 538, -415, 266, 276, 505, -97, 505, 455, 468, 469, 514, 490, 467, 615, 615, 206, 757, 507, -485, 504, 218, 508, 544, 218, 729, 462, 477, -481, 218, 218, 835, 832, 86, 482, -415, 486, -415, 895, 247, 86, 86, 248, 249, -415, 488, -486, 301, 86, 726, 726, -106, 478, 526, 516, 519, 536, 903, 530, 316, 1002, 752, 917, 545, 512, 782, 493, 270, 923, 829, 250, 704, 251, 502, 419, -104, -485, 527, 125, 355, 356, -111, -110, 463, 607, 533, 452, 453, 297, 617, 613, 355, 356, 778, 86, 218, 218, 218, 218, 86, 218, 218, 556, 778, 428, 429, 430, 569, 570, 571, 572, 515, 589, 506, 589, 271, 272, 823, 271, 86, 212, 212, 212, 212, 522, 574, 575, 726, 568, 617, 617, 70, 561, 419, 683, 444, 573, 841, 247, 945, 86, 248, 249, 218, 529, 86, 316, -486, 622, 814, -106, 301, 816, -106, -106, 842, 297, 266, 535, 247, 966, 967, 248, 249, -111, 125, -106, 874, 514, 247, 814, 251, 248, 249, -76, 444, -104, 541, 218, 520, 543, -106, 266, -106, -108, 656, 609, 665, 622, 622, 250, 609, 251, 514, 547, 41, 778, -96, 42, 266, 250, 693, 251, 218, -111, 86, 218, 941, 266, 663, 514, 566, 663, 1003, 908, 567, 86, 578, 723, 514, 218, 328, 329, 330, 86, -103, 584, 664, 900, 218, 859, 688, 854, 663, 86, 812, -105, 913, -502, 737, 729, 554, 58, 919, 921, 587, 246, 505, 505, 545, 663, 664, 590, 526, 745, 926, 544, 105, 591, 663, 812, 520, 733, 734, -106, 726, 852, 86, 664, 778, 594, 732, 271, 266, 331, 332, 86, 664, 813, 775, 778, 718, -110, 775, 514, 971, 972, -108, 597, -106, 316, 813, 316, 583, 218, 206, 599, 271, 804, 663, 952, 600, 86, -102, 70, 602, 918, -108, 768, 125, -98, 125, 335, 742, 271, 329, 330, 664, 520, 603, -108, 544, 614, 271, 663, 523, 638, 786, -100, 218, 877, 879, 881, 297, 883, -105, 884, 104, 676, 104, 793, 727, 664, 271, 104, 104, 690, 271, 218, 985, 104, 104, 104, 691, 911, 104, 805, 316, 694, 795, 697, 854, 695, 673, 675, 555, 990, 331, 332, 454, 454, 615, 719, 744, 731, 271, 125, 615, 271, 918, 736, 739, -91, 615, 615, 754, -108, 104, 271, -108, -108, 767, 771, 770, 505, 788, 799, 673, 675, 798, 281, 104, 809, 281, 793, 793, 800, 810, -105, 811, 772, 815, 247, 824, 817, 248, 249, -108, 1052, -108, 912, 281, 825, 686, 218, 86, 831, 834, 832, -97, 827, 834, 848, 840, 538, 846, 822, 297, 834, 246, 774, 850, 366, 367, 368, 251, 740, 849, 808, 212, 1064, 857, 860, 104, 866, 104, 870, 277, 218, 472, 277, 872, 505, 206, 876, 924, 472, 878, 851, 369, 847, 491, 576, 554, 248, 249, 808, 508, 277, 609, -302, 996, 899, 212, 880, 882, 617, 998, 206, 615, 885, 931, 617, 915, 932, -302, 936, 955, 617, 617, 940, 520, 764, 495, 942, -590, 950, 544, 696, 589, 970, 1015, 1017, 1018, 1019, 973, 703, 247, 976, 781, 248, 249, 785, 978, 982, 980, 715, 266, 532, 987, -302, 988, 534, 999, -594, -591, 86, -302, 514, 380, 380, 1000, 1009, 316, 86, 622, 104, 1010, 218, 1020, 1021, 622, 218, 1016, 1022, 793, 1036, 622, 622, 104, 104, -342, 125, 86, 86, 934, 1035, 1037, 938, -481, 856, 1044, 271, 271, 1046, 1048, -342, 86, 663, 1053, 218, 904, 780, 1055, -481, 783, 1057, 380, 380, 86, 86, 505, 939, 495, 756, 431, 664, 86, 1059, -485, 1072, 457, 1069, -593, 617, 212, 949, 579, 86, 86, 432, -342, 104, -594, -485, 104, 432, -590, -342, -481, 104, 104, 1077, -590, 104, 593, -481, 589, 589, 1079, 738, 104, 104, 227, 130, 969, -597, 1068, 1070, 104, 975, 887, 271, 893, 925, 433, 1067, -591, 856, -485, 271, 458, 434, -591, 125, 208, -485, 474, 434, 125, 492, 890, 775, 622, 773, 899, 1024, 1029, 899, 1040, 899, 0, 432, 86, 86, 0, 0, 993, -489, 380, 380, 86, 834, 0, 0, 104, 104, 104, 104, 104, 104, 104, 104, -489, 0, 0, 125, 0, 0, 760, -597, 358, 359, 360, 361, 0, 0, 475, 0, 0, 104, 0, 894, 0, 434, -597, 0, 761, 899, 339, 329, 330, 1045, 0, 706, 706, 914, 916, -489, 0, 0, 104, 920, 922, 104, -489, 104, 730, 0, 104, 0, 86, 780, 86, 735, 899, 86, 899, -597, 899, -597, 899, 0, 0, -593, 780, 741, -597, 914, 916, 589, 920, 922, 266, 350, 351, 352, 353, 354, 104, 0, 899, 331, 332, 514, 495, 693, 834, 898, 104, 104, 0, 495, 0, 218, 380, 483, 0, 271, 0, 867, 0, 0, -593, 104, 0, 104, 104, 960, 0, 962, 432, 0, 0, 963, 0, 0, 104, 0, 765, 766, 104, 0, 524, 663, 104, 0, 0, 562, 0, 104, 329, 330, 1025, 0, 104, 893, 0, 432, 893, 548, 893, 664, -488, 0, 0, 484, 380, 986, 796, 0, 890, 0, 434, 890, 432, 0, 890, -488, 890, 760, 0, 358, 359, 360, 361, 927, -593, 104, 0, 0, 986, 0, 475, 961, 0, 0, 104, 761, 935, 434, 0, -593, 331, 332, 0, 1011, 1012, 271, 893, 549, 943, 944, -488, 0, 104, 0, 434, 0, 947, -488, 977, 979, 104, -292, 0, 0, 890, 1031, 0, 1034, 953, 0, 0, 0, -593, 893, -593, 893, -292, 893, -593, 893, 0, -593, 0, 826, 0, -594, -303, 104, 0, 821, 0, 890, 0, 890, 0, 890, 0, 890, 0, 893, 0, -303, 1047, 0, 0, 1049, 104, 0, 1013, 802, 0, -292, 1023, 0, 0, 217, 217, 890, -292, 0, 1038, 217, 267, 267, 432, 0, 267, 432, 1030, 472, 0, 1033, 989, 0, 1028, 432, -303, 1071, 858, 0, 997, 0, 1073, -303, 1075, 0, 0, 247, 1076, -594, 248, 249, 0, 289, 291, 292, 293, 0, 869, 803, 267, 309, 458, 0, -594, 0, 434, 0, 1083, 434, 1039, 0, 346, 347, 502, 0, 0, 434, 250, 0, 251, 0, 104, 104, 380, 0, 337, 875, 329, 330, 1054, 1056, 1058, 0, 1060, 1061, 0, -594, 0, -594, 1041, 0, 1042, -594, 0, 1043, -594, 563, 1074, 329, 330, 341, 329, 330, 0, 0, 104, 0, 555, 329, 330, 560, 329, 330, 217, 0, 0, 357, 0, 358, 359, 360, 361, 930, 1078, 1080, 1081, 1082, 0, 0, 331, 332, 0, 0, 1084, 362, 0, 0, 0, 357, 948, 358, 359, 360, 361, 0, 0, 0, 706, 363, 0, 331, 332, 0, 331, 332, 957, 362, 0, 0, 364, 331, 332, 0, 331, 332, 365, 366, 367, 368, 0, 363, 0, 564, 329, 330, 565, 329, 330, 0, 0, 0, 364, 0, 104, 0, 0, -620, 365, 366, 367, 368, 104, 104, 369, 0, 104, 370, 0, 104, 104, 755, 329, 330, 0, 104, 104, 0, 0, 0, 371, 104, 104, 0, 0, 0, 369, 0, 0, 370, 217, 217, 0, 0, 0, 104, 331, 332, 104, 331, 332, 0, 371, 0, 472, 0, 0, 104, 104, 0, 472, 0, 0, 0, 0, 104, 357, 0, 358, 359, 360, 361, 0, 0, 331, 332, 104, 104, 0, 498, 499, 500, 346, 0, 362, 357, 0, 358, 359, 360, 361, 0, 0, 267, 0, 760, 267, 358, 359, 360, 361, 217, 217, 362, 0, 0, 0, 0, 0, 364, 0, 0, 0, 761, 0, 365, 366, 367, 368, 0, 0, 760, 0, 358, 359, 360, 361, 0, 364, 0, 0, 104, 0, 0, 365, 366, 367, 368, 364, 761, 0, 104, 104, 369, 762, 0, 370, 0, 760, 104, 358, 359, 360, 361, -299, 0, 0, -299, -299, 551, 0, 0, 369, 0, 364, 370, 761, 217, 217, 217, 217, 247, 217, 217, 248, 249, 1026, 247, 0, 0, 248, 249, 0, 0, -299, -299, 0, -299, 0, 0, 0, 364, 586, 0, 0, 0, 0, 956, 0, 0, 0, 0, 250, 596, 251, 0, 0, 0, 951, 104, 251, 104, 0, 0, 104, 608, 0, 0, 0, 0, 619, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 419, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 0, 0, 267, 0, 104, 357, 0, 358, 359, 360, 361, 0, 672, 672, 0, 426, 427, 428, 429, 430, 0, 0, 0, 362, 0, 0, 0, 267, 0, 0, 217, 1005, 0, 358, 359, 360, 361, 0, 0, 581, 0, 0, 672, 0, 267, 0, 672, 672, 364, 761, 0, 0, 0, 267, 365, 366, 367, 368, 0, 0, 0, 0, 717, 0, 0, 720, 721, 0, 0, 0, 722, 0, 0, 725, 0, 728, 0, 309, 293, 0, 0, 0, 369, 0, 0, 370, 0, 0, 0, 0, 73, 0, 73, 121, 121, 672, 0, 0, 0, 0, 0, 121, 0, 0, 0, 725, 0, 357, 309, 358, 359, 360, 361, 0, 0, 0, 0, 0, 267, 0, 0, 416, 417, 0, 0, 362, 0, 0, 784, 0, 358, 359, 360, 361, 419, 758, 759, 0, 0, 0, 73, 776, 0, 0, 121, 0, 362, 416, 417, 0, 364, 0, 0, 769, 0, 0, 365, 366, 367, 368, 419, 425, 426, 427, 428, 429, 430, 0, 0, 0, 121, 364, 787, 0, 0, 794, 0, 0, 366, 367, 368, 0, 0, 0, 369, 0, 0, 370, 426, 427, 428, 429, 430, 0, 0, 0, -414, 0, 0, 0, 0, 0, 0, 0, 0, 369, 0, 0, 73, -414, -414, -414, -414, -414, -414, 0, -414, 0, 0, 0, 0, 0, -414, -414, -414, 0, 0, 0, 0, 0, 0, 0, 0, -414, -414, 0, -414, -414, -414, -414, -414, 0, 0, 0, 0, 0, 0, 0, 217, 0, 74, 0, 74, 122, 122, 0, 0, 0, 0, 0, 828, 122, 0, 769, 787, 0, 0, 0, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, 0, 0, 217, 0, -414, -414, -414, 0, 0, -414, 0, 0, 0, 853, 0, -414, 0, -414, 0, 73, 74, -414, 725, 309, 122, 0, 0, 0, 0, 0, 0, -414, 0, 0, -414, -414, 0, 0, -414, 0, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, 122, 0, 0, 0, -414, -414, -414, -414, -414, 0, 276, -414, -414, -414, -414, 357, 0, 358, 359, 360, 361, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 362, 902, 0, 0, 0, 74, 672, 905, 0, 267, 0, 0, 672, 672, 73, 0, 871, 725, 672, 672, 0, 73, 73, 0, 0, 364, 0, 0, 0, 73, 0, 365, 366, 367, 368, 0, 0, 0, 0, 0, 121, 217, 0, 0, 672, 672, 0, 672, 672, 0, 0, 0, 0, 0, 0, 0, 0, 946, 0, 369, 0, 293, 370, 357, 0, 358, 359, 360, 361, 0, 0, 0, 0, 0, 0, 73, 0, 0, 958, 959, 73, 362, 0, 0, 0, 0, 0, 0, 0, 0, 0, 964, 965, 0, 0, 0, 74, 0, 0, 0, 73, 0, 0, 0, 0, 0, 364, 981, 0, 0, 0, 0, 365, 366, 367, 368, 0, 983, 984, 416, 417, 73, 0, 0, 672, 0, 73, 121, 0, 73, 0, 0, 419, 0, 0, 0, 0, 0, 0, 0, 369, 0, 0, 370, 0, 0, 0, 672, 0, 0, 0, 0, 0, 0, 0, 309, 0, 423, 424, 425, 426, 427, 428, 429, 430, 0, 0, 0, 0, 73, 73, 0, 0, 0, 74, 0, 0, 0, 0, 0, 0, 74, 74, 0, 0, 0, 73, 0, 0, 74, 0, 0, 0, 0, 0, 0, 0, 73, 0, 0, 122, 0, 0, 0, 0, 73, 0, 0, 0, -621, -621, -621, -621, 408, 409, 73, 0, -621, -621, 0, 0, 0, 0, 0, 0, 416, 417, 0, 0, 0, 0, 0, 0, 0, 0, 74, 0, 0, 419, 0, 74, 0, 0, 0, 0, 267, 0, 0, 73, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 0, 74, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 121, 0, 121, 0, 0, 0, 0, 0, 0, 0, 74, -620, 73, 0, 0, 74, 122, 0, 74, 0, 0, 0, 0, 0, -620, -620, -620, -620, -620, -620, 0, -620, 0, 0, 0, 0, 0, -620, -620, 0, 0, 0, 0, 0, 0, 0, 0, 0, -620, -620, 0, -620, -620, -620, -620, -620, 0, 0, 74, 74, 0, 0, 0, 0, 0, 0, 0, 121, 0, 0, 0, 0, 0, 0, 0, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 0, 0, 0, 0, 0, 0, 0, 74, 0, 0, 0, 0, 0, 0, -620, 0, 0, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -620, 103, 0, 103, 128, 128, 0, 0, 0, 0, -620, 0, 232, -620, -620, 0, 0, 0, 0, 73, 0, 74, 0, 0, 0, 0, 0, 0, 0, 0, 74, 0, 0, -620, -620, 0, 0, 0, 0, 276, -620, -620, -620, -620, 122, 0, 122, 0, 0, 0, 0, 103, 0, 0, 0, 318, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, 318, 0, 0, 416, 417, 0, 0, 0, 418, 0, 0, 0, 0, 0, 0, 0, 419, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, 0, 0, 0, 0, 0, 0, 420, 103, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 73, 0, 0, 0, 0, 0, 0, 121, 73, 73, 0, 0, 0, 0, 0, 73, 0, 0, 0, 0, 0, 73, 73, 0, 0, 0, 0, 73, 73, 0, 0, 100, 0, 100, 127, 127, 127, 0, 0, 0, 0, 73, 231, 0, 0, 0, 0, 0, 0, 74, 0, 0, 0, 73, 73, 0, 0, 0, 0, 0, 0, 73, 404, 405, 406, 407, 408, 409, 410, 0, 412, 413, 73, 73, 0, 0, 0, 0, 416, 417, 103, 100, 0, 0, 0, 317, 0, 0, 0, 0, 0, 419, 0, 0, 0, 0, 0, 0, 0, 121, 0, 0, 0, 0, 121, 0, 0, 0, 0, 0, 0, 317, 0, 0, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 0, 0, 0, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 73, 0, 0, 121, 0, 0, 0, 73, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 103, 0, 74, 0, 0, 0, 0, 103, 103, 122, 74, 74, 0, 0, 0, 103, 0, 74, 0, 0, 0, 0, 0, 74, 74, 0, 318, 0, 0, 74, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 0, 0, 0, 73, 0, 73, 0, 0, 73, 0, 0, 74, 74, 0, 0, 0, 0, 103, 0, 74, 0, 0, 103, 0, 0, 0, 0, 0, 0, 0, 74, 74, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, 0, 0, 0, 0, 122, 103, 0, 0, 0, 0, 103, 318, 0, 623, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 74, 0, 0, 122, 0, 0, 83, 74, 83, 0, 0, 0, 0, 0, 0, 623, 623, 228, 0, 0, 100, 0, 0, 0, 0, 0, 0, 100, 100, 0, 0, 0, 103, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 103, 0, 0, 317, 0, 0, 0, 0, 103, 0, 0, 0, 0, 83, 0, 0, 0, 0, 103, 0, 0, 0, 0, 0, 74, 0, 74, 0, 0, 74, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 103, 0, 0, 0, 0, 0, 0, 0, 0, 103, 0, 0, 100, 0, 0, 0, 0, 0, 0, 801, 0, 0, 0, 318, 0, 318, 0, 0, 0, 0, 83, 0, 0, 100, 0, 103, 0, 0, 100, 317, 0, 0, 0, 0, 0, 0, 0, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, 0, 416, 417, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 419, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 318, 0, 0, 101, 0, 101, 0, 420, 100, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 100, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 83, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 651, 652, 0, 0, 653, 0, 0, 103, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 100, 174, 175, 176, 177, 178, 179, 180, 181, 0, 0, 182, 183, 0, 317, 0, 317, 184, 185, 186, 187, 0, 0, 0, 0, 0, 100, 0, 0, 0, 83, 188, 189, 190, 0, 0, 0, 83, 83, 0, 0, 0, 101, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 0, 201, 202, 0, 0, 0, 0, 0, 0, 203, 276, 0, 0, 0, 0, 0, 317, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 0, 0, 0, 0, 83, 0, 0, 0, 103, 0, 0, 0, 0, 0, 0, 318, 103, 623, 0, 0, 0, 0, 0, 623, 83, 0, 0, 0, 0, 623, 623, 0, 0, 0, 0, 103, 103, 0, 0, 0, 0, 101, 0, 0, 0, 83, 0, 0, 0, 103, 83, 0, 0, 618, 0, 0, 0, 100, 0, 0, 0, 103, 103, 0, 0, 0, 0, 0, 0, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 618, 618, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 83, 0, 128, 0, 0, 0, 0, 0, 0, 101, 0, 83, 0, 0, 0, 0, 101, 101, 0, 83, 0, 0, 0, 0, 101, 623, 0, 0, 0, 83, 0, 0, 0, 0, 0, 103, 103, 0, 0, 995, 0, 0, 0, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 83, 0, 0, 317, 100, 0, 0, 0, 101, 83, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 100, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 83, 0, 100, 0, 0, 0, 103, 0, 103, 0, 0, 103, 0, 0, 100, 100, 0, 0, 0, 101, 0, 0, 100, 0, 101, 0, 0, 101, 0, 0, 0, 0, 0, 100, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 801, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 0, 0, 127, 0, 101, 101, 0, 0, 0, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 101, 0, 0, 0, 416, 417, 0, 0, 0, 0, 0, 101, 0, 0, 100, 100, 0, 419, 994, 101, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 420, 83, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 0, 404, 405, 406, 407, 408, 409, 0, -277, 412, 413, 0, 101, 0, 0, 0, 0, 416, 417, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 419, 0, 100, 0, 100, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 83, 618, 0, 0, 0, 0, 0, 618, 0, 0, 0, 0, 0, 618, 618, 0, 0, -597, 0, 83, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, -597, -597, -597, 83, -597, -597, 0, -597, 0, 0, 0, 0, 0, -597, 0, 83, 83, 0, 0, 0, 0, 0, 0, 83, -597, -597, 0, -597, -597, -597, -597, -597, 101, 0, 83, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, 0, 0, 0, 0, -597, -597, -597, 0, 806, -597, 0, 0, 0, 0, 0, 0, 0, -597, 618, 0, 0, -597, 0, 0, 0, 0, 0, 0, 83, 83, 0, -597, 992, 0, -597, -597, 83, -107, -597, 0, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, 0, 0, 0, 0, -597, -597, -597, 0, -99, 0, 0, -597, -597, -597, -597, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 101, 101, 0, 0, 0, 660, 661, 101, 0, 662, 0, 0, 0, 101, 101, 0, 83, 0, 83, 101, 101, 83, 0, 0, 174, 175, 176, 177, 178, 179, 180, 181, 0, 101, 182, 183, 0, 0, 0, 0, 184, 185, 186, 187, 0, 101, 101, 0, 0, 0, 0, 0, 0, 101, 188, 189, 190, 0, 0, 0, 0, 0, 0, 0, 101, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 0, 201, 202, 0, 0, 0, 0, 0, 0, 203, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, 101, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -620, 3, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0, 0, 0, 0, 0, 14, 0, 15, 16, 17, 18, 0, 0, 0, 0, 0, 19, 20, 21, 22, 23, 24, 25, 0, 101, 26, 101, 0, 0, 101, 0, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 50, 0, 0, 51, 52, 0, 53, 54, 0, 55, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, -293, 63, -620, 0, 0, -620, -620, 0, 0, 0, 0, 0, 0, -293, -293, -293, -293, -293, -293, 0, -293, 64, 65, 66, 0, 0, 0, -293, -293, -293, 0, 0, 0, -620, 0, -620, 0, -293, -293, 0, -293, -293, -293, -293, -293, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -293, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, 0, 0, 0, 0, -293, -293, -293, 0, 0, -293, 0, 0, 0, 0, 0, -293, 0, -293, 0, 0, 0, -293, 0, 0, 0, 0, 0, 0, 0, -293, 0, -293, 0, 0, -293, -293, 0, 0, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, 0, 0, -481, 0, 0, -293, -293, -293, -293, 0, 0, -293, -293, -293, -293, -481, -481, -481, -481, -481, -481, 0, -481, 0, 0, 0, 0, 0, 0, -481, -481, 0, 0, 0, 0, 0, 0, 0, 0, -481, -481, 0, -481, -481, -481, -481, -481, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 496, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, 0, 0, 0, 0, -481, -481, -481, 0, -481, -481, 0, 0, 0, 0, 0, -481, 0, -481, 0, 0, 0, -481, 0, 0, 0, 0, 0, 0, 0, 0, 0, -481, 0, 0, -481, -481, 0, -481, -481, 0, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, 0, 0, -620, 0, 0, -481, -481, -481, -481, 0, 0, -481, -481, -481, -481, -620, -620, -620, -620, -620, -620, 0, -620, 0, 0, 0, 0, 0, -620, -620, -620, 0, 0, 0, 0, 0, 0, 0, 0, -620, -620, 0, -620, -620, -620, -620, -620, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, 0, 0, 0, 0, -620, -620, -620, 0, 0, -620, 0, 0, 0, 0, 0, -620, 0, -620, 0, 0, 0, -620, 0, 0, 0, 0, 0, 0, 0, 0, 0, -620, 0, 0, -620, -620, 0, 0, -620, 0, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, 0, 0, -620, 0, -620, -620, -620, -620, -620, 0, 276, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, 0, -620, 0, 0, 0, 0, 0, 0, -620, -620, 0, 0, 0, 0, 0, 0, 0, 0, -620, -620, 0, -620, -620, -620, -620, -620, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, 0, 0, 0, 0, -620, -620, -620, 0, 0, -620, 0, 0, 0, 0, 0, -620, 0, -620, 0, 0, 0, -620, 0, 0, 0, 0, 0, 0, 0, 0, 0, -620, 0, 0, -620, -620, 0, 0, -620, 0, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, 0, 0, -597, 0, 0, -620, -620, -620, -620, 0, 276, -620, -620, -620, -620, -597, -597, -597, 0, -597, -597, 0, -597, 0, 0, 0, 0, 0, -597, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -597, -597, 0, -597, -597, -597, -597, -597, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, 0, 0, 0, 0, -597, -597, -597, 0, 806, -597, 0, 0, 0, 0, 0, 0, 0, -597, 0, 0, 0, -597, 0, 0, 0, 0, 0, 0, 0, 0, 0, -597, 0, 0, -597, -597, 0, -107, -597, 0, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, 0, 0, -302, 0, -597, -597, -597, 0, -597, 0, 0, -597, -597, -597, -597, -302, -302, -302, 0, -302, -302, 0, -302, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -302, -302, 0, -302, -302, -302, -302, -302, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, 0, 0, 0, 0, -302, -302, -302, 0, 807, -302, 0, 0, 0, 0, 0, 0, 0, -302, 0, 0, 0, -302, 0, 0, 0, 0, 0, 0, 0, 0, 0, -302, 0, 0, -302, -302, 0, -109, -302, 0, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, 0, 0, -302, 0, 0, -302, -302, 0, -101, 0, 0, -302, -302, -302, -302, -302, -302, -302, 0, -302, -302, 0, -302, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -302, -302, 0, -302, -302, -302, -302, -302, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, 0, 0, 0, 0, -302, -302, -302, 0, 807, -302, 0, 0, 0, 0, 0, 0, 0, -302, 0, 0, 0, -302, 0, 0, 0, 0, 0, 0, 0, 0, 0, -302, 0, 0, -302, -302, 0, -109, -302, 0, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, 0, 0, 0, 0, 0, -302, -302, 0, -302, 0, 0, -302, -302, -302, -302, 295, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, -620, -620, -620, 0, 0, -620, 14, 0, 15, 16, 17, 18, 0, 0, 0, 0, 0, 19, 20, 21, 22, 23, 24, 25, 0, 0, 26, 0, 0, 0, 0, 0, 27, 0, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 50, 0, 0, 51, 52, 0, 53, 54, 0, 55, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, -620, 0, 0, -620, -620, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 65, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, -620, 295, -620, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0, -620, 0, -620, -620, 14, 0, 15, 16, 17, 18, 0, 0, 0, 0, 0, 19, 20, 21, 22, 23, 24, 25, 0, 0, 26, 0, 0, 0, 0, 0, 27, 0, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 50, 0, 0, 51, 52, 0, 53, 54, 0, 55, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, -620, 0, 0, -620, -620, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 65, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, -620, 295, -620, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0, -620, 0, 0, -620, 14, -620, 15, 16, 17, 18, 0, 0, 0, 0, 0, 19, 20, 21, 22, 23, 24, 25, 0, 0, 26, 0, 0, 0, 0, 0, 27, 0, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 50, 0, 0, 51, 52, 0, 53, 54, 0, 55, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, -620, 0, 0, -620, -620, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 65, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, -620, 295, -620, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0, -620, 0, 0, -620, 14, 0, 15, 16, 17, 18, 0, 0, 0, 0, 0, 19, 20, 21, 22, 23, 24, 25, 0, 0, 26, 0, 0, 0, 0, 0, 27, 0, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 50, 0, 0, 51, 52, 0, 53, 54, 0, 55, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, -620, 0, 0, -620, -620, 3, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0, 64, 65, 66, 0, 14, 0, 15, 16, 17, 18, 0, 0, -620, 0, -620, 19, 20, 21, 22, 23, 24, 25, 0, 0, 26, 0, 0, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 50, 0, 0, 51, 52, 0, 53, 54, 0, 55, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, -620, 0, 0, -620, -620, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 65, 66, 0, 0, -620, 0, 0, 0, 0, 0, 0, -620, 295, -620, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, -620, -620, 0, 0, 0, 14, 0, 15, 16, 17, 18, 0, 0, 0, 0, 0, 19, 20, 21, 22, 23, 24, 25, 0, 0, 26, 0, 0, 0, 0, 0, 27, 0, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 50, 0, 0, 51, 52, 0, 53, 54, 0, 55, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, -620, 0, 0, -620, -620, 295, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0, 64, 65, 66, 0, 14, 0, 15, 16, 17, 18, 0, 0, -620, 0, -620, 19, 20, 21, 22, 23, 24, 25, 0, 0, 26, 0, 0, 0, 0, 0, 27, 0, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 50, 0, 0, 296, 52, 0, 53, 54, 0, 55, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, -620, 0, 0, -620, -620, 295, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0, 64, 65, 66, 0, 14, 0, 15, 16, 17, 18, 0, -620, -620, 0, -620, 19, 20, 21, 22, 23, 24, 25, 0, 0, 26, 0, 0, 0, 0, 0, 27, 0, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 50, 0, 0, 51, 52, 0, 53, 54, 0, 55, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, -620, 0, 0, -620, -620, 295, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0, 64, 65, 66, 0, 14, 0, 15, 16, 17, 18, 0, -620, -620, 0, -620, 19, 20, 21, 22, 23, 24, 25, 0, 0, 26, 0, 0, 0, 0, 0, 27, 0, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 50, 0, 0, 51, 52, 0, 53, 54, 0, 55, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, -620, 0, 0, -620, -620, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 65, 66, 0, 0, -620, 0, 0, 0, 0, 0, 0, -620, 295, -620, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0, -620, 0, 0, 0, 14, 0, 15, 16, 17, 18, 0, 0, 0, 0, 0, 19, 20, 21, 22, 23, 24, 25, 0, 0, 26, 0, 0, 0, 0, 0, 27, 0, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 50, 0, 0, 51, 52, 0, 53, 54, 0, 55, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, -620, 0, 0, -620, -620, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0, 64, 65, 66, 0, 14, 0, 15, 16, 17, 18, 0, 0, -620, 0, -620, 19, 20, 21, 22, 23, 24, 25, 0, 0, 26, 0, 0, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 50, 0, 0, 51, 52, 0, 53, 54, 0, 55, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 247, 0, 0, 248, 249, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0, 64, 65, 66, 0, 14, 0, 15, 16, 17, 18, 0, 0, 250, 0, 251, 19, 20, 21, 22, 23, 24, 25, 0, 0, 26, 0, 0, 0, 0, 0, 27, 0, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 50, 0, 0, 51, 52, 0, 53, 54, 0, 55, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 247, 0, 0, 248, 249, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 64, 65, 66, 0, 14, 0, 15, 16, 17, 18, 0, 0, 250, 0, 251, 19, 20, 21, 22, 23, 24, 25, 0, 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 211, 0, 0, 119, 52, 0, 53, 54, 0, 0, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 247, 0, 0, 248, 249, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 64, 65, 66, 0, 14, 0, 108, 109, 17, 18, 0, 0, 250, 0, 251, 110, 111, 112, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 211, 0, 0, 119, 52, 0, 53, 54, 0, 0, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 247, 0, 0, 248, 249, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 64, 265, 66, 0, 14, 0, 15, 16, 17, 18, 0, 0, 250, 0, 251, 19, 20, 21, 22, 23, 24, 25, 0, 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 211, 0, 0, 119, 52, 0, 53, 54, 0, 0, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 247, 0, 0, 248, 249, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 65, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 0, 0, 0, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 0, 0, 0, 0, 0, 165, 166, 167, 168, 169, 170, 171, 172, 35, 36, 173, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 174, 175, 176, 177, 178, 179, 180, 181, 0, 0, 182, 183, 0, 0, 0, 0, 184, 185, 186, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 189, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 0, 201, 202, 0, 0, 0, 0, 0, 0, 203, 204, -590, -590, -590, -590, -590, -590, -590, -590, -590, 0, 0, 0, 0, 0, 0, 0, -590, 0, -590, -590, -590, -590, 0, -590, 0, 0, 0, -590, -590, -590, -590, -590, -590, -590, 0, 0, -590, 0, 0, 0, 0, 0, 0, 0, 0, -590, -590, -590, -590, -590, -590, -590, -590, -590, 0, -590, -590, -590, 0, 0, -590, 0, 0, -590, -590, 0, -590, -590, -590, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -590, -590, 0, 0, 0, 0, 0, -590, 0, 0, -590, -590, 0, -590, -590, 0, -590, 0, -590, -590, -590, 0, -590, -590, -590, 0, -590, -590, -590, 0, -590, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -590, -590, -590, 0, -590, 0, 0, 0, 0, 0, -590, -591, -591, -591, -591, -591, -591, -591, -591, -591, 0, 0, 0, 0, 0, 0, 0, -591, 0, -591, -591, -591, -591, 0, -591, 0, 0, 0, -591, -591, -591, -591, -591, -591, -591, 0, 0, -591, 0, 0, 0, 0, 0, 0, 0, 0, -591, -591, -591, -591, -591, -591, -591, -591, -591, 0, -591, -591, -591, 0, 0, -591, 0, 0, -591, -591, 0, -591, -591, -591, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -591, -591, 0, 0, 0, 0, 0, -591, 0, 0, -591, -591, 0, -591, -591, 0, -591, 0, -591, -591, -591, 0, -591, -591, -591, 0, -591, -591, -591, 0, -591, 0, 0, 0, 0, 0, 0, -593, -593, -593, -593, -593, -593, -593, -593, -593, 0, 0, 0, 0, -591, -591, -591, -593, -591, -593, -593, -593, -593, 0, -591, 0, 0, 0, -593, -593, -593, -593, -593, -593, -593, 0, 0, -593, 0, 0, 0, 0, 0, 0, 0, 0, -593, -593, -593, -593, -593, -593, -593, -593, -593, 0, -593, -593, -593, 0, 0, -593, 0, 0, -593, -593, 0, -593, -593, -593, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -593, -593, 0, 0, 0, 0, 0, -593, 838, 0, -593, -593, 0, -593, -593, 0, -593, 0, -593, -593, -593, 0, -593, -593, -593, 0, -593, -593, -593, 0, -593, 0, 0, 0, 0, 0, 0, -107, -594, -594, -594, -594, -594, -594, -594, -594, -594, 0, 0, 0, -593, -593, -593, 0, -594, 0, -594, -594, -594, -594, -593, 0, 0, 0, 0, -594, -594, -594, -594, -594, -594, -594, 0, 0, -594, 0, 0, 0, 0, 0, 0, 0, 0, -594, -594, -594, -594, -594, -594, -594, -594, -594, 0, -594, -594, -594, 0, 0, -594, 0, 0, -594, -594, 0, -594, -594, -594, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -594, -594, 0, 0, 0, 0, 0, -594, 839, 0, -594, -594, 0, -594, -594, 0, -594, 0, -594, -594, -594, 0, -594, -594, -594, 0, -594, -594, -594, 0, -594, 0, 0, 0, 0, 0, 0, -109, -595, -595, -595, -595, -595, -595, -595, -595, -595, 0, 0, 0, -594, -594, -594, 0, -595, 0, -595, -595, -595, -595, -594, 0, 0, 0, 0, -595, -595, -595, -595, -595, -595, -595, 0, 0, -595, 0, 0, 0, 0, 0, 0, 0, 0, -595, -595, -595, -595, -595, -595, -595, -595, -595, 0, -595, -595, -595, 0, 0, -595, 0, 0, -595, -595, 0, -595, -595, -595, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -595, -595, 0, 0, 0, 0, 0, -595, 0, 0, -595, -595, 0, -595, -595, 0, -595, 0, -595, -595, -595, 0, -595, -595, -595, 0, -595, -595, -595, 0, -595, 0, 0, 0, 0, 0, 0, -596, -596, -596, -596, -596, -596, -596, -596, -596, 0, 0, 0, 0, -595, -595, -595, -596, 0, -596, -596, -596, -596, 0, -595, 0, 0, 0, -596, -596, -596, -596, -596, -596, -596, 0, 0, -596, 0, 0, 0, 0, 0, 0, 0, 0, -596, -596, -596, -596, -596, -596, -596, -596, -596, 0, -596, -596, -596, 0, 0, -596, 0, 0, -596, -596, 0, -596, -596, -596, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -596, -596, 0, 0, 0, 0, 0, -596, 0, 0, -596, -596, 0, -596, -596, 0, -596, 0, -596, -596, -596, 0, -596, -596, -596, 0, -596, -596, -596, 0, -596, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -596, -596, -596, 0, 0, 0, 0, 0, 0, 0, -596, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 0, 0, 0, 155, 156, 157, 233, 234, 235, 236, 162, 163, 164, 0, 0, 0, 0, 0, 165, 166, 167, 237, 238, 239, 240, 172, 320, 321, 241, 322, 0, 0, 0, 0, 0, 0, 323, 0, 0, 0, 0, 0, 324, 174, 175, 176, 177, 178, 179, 180, 181, 0, 0, 182, 183, 0, 0, 0, 0, 184, 185, 186, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 189, 190, 0, 0, 0, 0, 325, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 0, 201, 202, 0, 0, 0, 0, 0, 0, 203, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 0, 0, 0, 155, 156, 157, 233, 234, 235, 236, 162, 163, 164, 0, 0, 0, 0, 0, 165, 166, 167, 237, 238, 239, 240, 172, 320, 321, 241, 322, 0, 0, 0, 0, 0, 0, 323, 0, 0, 0, 0, 0, 0, 174, 175, 176, 177, 178, 179, 180, 181, 0, 0, 182, 183, 0, 0, 0, 0, 184, 185, 186, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 189, 190, 0, 0, 0, 0, 487, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 0, 201, 202, 0, 0, 0, 0, 0, 0, 203, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 0, 0, 0, 155, 156, 157, 233, 234, 235, 236, 162, 163, 164, 0, 0, 0, 0, 0, 165, 166, 167, 237, 238, 239, 240, 172, 0, 0, 241, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 175, 176, 177, 178, 179, 180, 181, 0, 0, 182, 183, 0, 0, 0, 0, 184, 185, 186, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 189, 190, 0, 0, 0, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 0, 201, 202, 0, 0, 0, 0, 0, 0, 203, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 0, 0, 0, 155, 156, 157, 233, 234, 235, 236, 162, 163, 164, 0, 0, 0, 0, 0, 165, 166, 167, 237, 238, 239, 240, 172, 0, 0, 241, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 175, 176, 177, 178, 179, 180, 181, 0, 0, 182, 183, 0, 0, 0, 0, 184, 185, 186, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 189, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 0, 201, 202, 0, 0, 0, 0, 0, 0, 203, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 14, 0, 108, 109, 17, 18, 0, 0, 0, 0, 0, 110, 111, 112, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 313, 0, 0, 119, 52, 0, 53, 54, 0, 0, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 14, 120, 108, 109, 17, 18, 0, 0, 0, 314, 0, 110, 111, 112, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 313, 0, 0, 119, 52, 0, 53, 54, 0, 0, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0, 0, 0, 0, 0, 14, 120, 15, 16, 17, 18, 0, 0, 0, 612, 0, 19, 20, 21, 22, 23, 24, 25, 0, 0, 26, 0, 0, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 50, 0, 0, 51, 52, 0, 53, 54, 0, 55, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0, 0, 64, 65, 66, 14, 0, 15, 16, 17, 18, 0, 0, 0, 0, 0, 19, 20, 21, 22, 23, 24, 25, 0, 0, 26, 0, 0, 0, 0, 0, 27, 0, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 50, 0, 0, 51, 52, 0, 53, 54, 0, 55, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 64, 65, 66, 14, 0, 15, 16, 17, 18, 0, 0, 0, 0, 0, 19, 20, 21, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 260, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 261, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 211, 0, 0, 119, 52, 0, 53, 54, 0, 262, 0, 263, 264, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 64, 265, 66, 14, 0, 15, 16, 17, 18, 0, 0, 0, 0, 0, 19, 20, 21, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 260, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 261, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 509, 0, 0, 0, 0, 0, 211, 0, 0, 119, 52, 0, 53, 54, 0, 262, 0, 263, 264, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 64, 265, 66, 14, 0, 108, 109, 17, 18, 0, 0, 0, 0, 0, 110, 111, 112, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 260, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 261, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 211, 0, 0, 119, 52, 0, 53, 54, 0, 724, 0, 263, 264, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 64, 265, 66, 14, 0, 108, 109, 17, 18, 0, 0, 0, 0, 0, 110, 111, 112, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 260, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 261, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 855, 0, 0, 0, 0, 0, 211, 0, 0, 119, 52, 0, 53, 54, 0, 724, 0, 263, 264, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 64, 265, 66, 14, 0, 108, 109, 17, 18, 0, 0, 0, 0, 0, 110, 111, 112, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 260, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 261, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 211, 0, 0, 119, 52, 0, 53, 54, 0, 262, 0, 263, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 64, 265, 66, 14, 0, 108, 109, 17, 18, 0, 0, 0, 0, 0, 110, 111, 112, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 260, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 261, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 211, 0, 0, 119, 52, 0, 53, 54, 0, 0, 0, 263, 264, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 64, 265, 66, 14, 0, 108, 109, 17, 18, 0, 0, 0, 0, 0, 110, 111, 112, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 260, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 261, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 211, 0, 0, 119, 52, 0, 53, 54, 0, 724, 0, 263, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 64, 265, 66, 14, 0, 108, 109, 17, 18, 0, 0, 0, 0, 0, 110, 111, 112, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 260, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 261, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 211, 0, 0, 119, 52, 0, 53, 54, 0, 0, 0, 263, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 64, 265, 66, 14, 0, 15, 16, 17, 18, 0, 0, 0, 0, 0, 19, 20, 21, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 211, 0, 0, 119, 52, 0, 53, 54, 0, 606, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 64, 265, 66, 14, 0, 108, 109, 17, 18, 0, 0, 0, 0, 0, 110, 111, 112, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 211, 0, 0, 119, 52, 0, 53, 54, 0, 262, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 64, 265, 66, 14, 0, 108, 109, 17, 18, 0, 0, 0, 0, 0, 110, 111, 112, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 211, 0, 0, 119, 52, 0, 53, 54, 0, 606, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 64, 265, 66, 14, 0, 108, 109, 17, 18, 0, 0, 0, 0, 0, 110, 111, 112, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 211, 0, 0, 119, 52, 0, 53, 54, 0, 901, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 64, 265, 66, 14, 0, 108, 109, 17, 18, 0, 0, 0, 0, 0, 110, 111, 112, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 211, 0, 0, 119, 52, 0, 53, 54, 0, 724, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 64, 265, 66, 14, 0, 15, 16, 17, 18, 0, 0, 0, 0, 0, 19, 20, 21, 22, 23, 24, 25, 0, 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 211, 0, 0, 119, 52, 0, 53, 54, 0, 0, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 64, 65, 66, 14, 0, 108, 109, 17, 18, 0, 0, 0, 0, 0, 110, 111, 112, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 211, 0, 0, 119, 52, 0, 53, 54, 0, 0, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 64, 265, 66, 14, 0, 15, 16, 17, 18, 0, 0, 0, 0, 0, 19, 20, 21, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 211, 0, 0, 119, 52, 0, 53, 54, 0, 0, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 64, 265, 66, 14, 0, 108, 109, 17, 18, 0, 0, 0, 0, 0, 110, 111, 112, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 114, 34, 35, 36, 115, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 0, 0, 118, 0, 0, 119, 52, 0, 53, 54, 0, 0, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 14, 120, 108, 109, 17, 18, 0, 0, 0, 0, 0, 110, 111, 112, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 226, 0, 0, 51, 52, 0, 53, 54, 0, 55, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 14, 120, 108, 109, 17, 18, 0, 0, 0, 0, 0, 110, 111, 112, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 313, 0, 0, 400, 52, 0, 53, 54, 0, 401, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 14, 120, 108, 109, 17, 18, 0, 0, 0, 0, 0, 110, 111, 112, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 114, 34, 35, 36, 115, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 118, 0, 0, 119, 52, 0, 53, 54, 0, 0, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 14, 120, 108, 109, 17, 18, 0, 0, 0, 0, 0, 110, 111, 112, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 313, 0, 0, 400, 52, 0, 53, 54, 0, 0, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 14, 120, 108, 109, 17, 18, 0, 0, 0, 0, 0, 110, 111, 112, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 968, 0, 0, 119, 52, 0, 53, 54, 0, 0, 0, 0, 0, 56, 0, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 14, 120, 108, 109, 17, 18, 0, 0, 0, 0, 0, 110, 111, 112, 22, 23, 24, 25, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 0, 0, 42, 0, 0, 43, 44, 0, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 991, 0, 0, 119, 52, 0, 53, 54, 0, 0, 681, 652, 0, 56, 682, 57, 58, 59, 0, 60, 61, 62, 0, 63, 0, 0, 0, 0, 0, 174, 175, 176, 177, 178, 179, 180, 181, 0, 0, 182, 183, 0, 0, 120, 0, 184, 185, 186, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 189, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 0, 201, 202, 666, 661, 0, 0, 667, 0, 203, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 175, 176, 177, 178, 179, 180, 181, 0, 0, 182, 183, 0, 0, 0, 0, 184, 185, 186, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 189, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 0, 201, 202, 698, 652, 0, 0, 699, 0, 203, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 175, 176, 177, 178, 179, 180, 181, 0, 0, 182, 183, 0, 0, 0, 0, 184, 185, 186, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 189, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 0, 201, 202, 701, 661, 0, 0, 702, 0, 203, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 175, 176, 177, 178, 179, 180, 181, 0, 0, 182, 183, 0, 0, 0, 0, 184, 185, 186, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 189, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 0, 201, 202, 708, 652, 0, 0, 709, 0, 203, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 175, 176, 177, 178, 179, 180, 181, 0, 0, 182, 183, 0, 0, 0, 0, 184, 185, 186, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 189, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 0, 201, 202, 711, 661, 0, 0, 712, 0, 203, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 175, 176, 177, 178, 179, 180, 181, 0, 0, 182, 183, 0, 0, 0, 0, 184, 185, 186, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 189, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 0, 201, 202, 747, 652, 0, 0, 748, 0, 203, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 175, 176, 177, 178, 179, 180, 181, 0, 0, 182, 183, 0, 0, 0, 0, 184, 185, 186, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 189, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 0, 201, 202, 750, 661, 0, 0, 751, 0, 203, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 175, 176, 177, 178, 179, 180, 181, 0, 0, 182, 183, 0, 0, 0, 0, 184, 185, 186, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 189, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 0, 201, 202, 906, 652, 0, 0, 907, 0, 203, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 175, 176, 177, 178, 179, 180, 181, 0, 0, 182, 183, 0, 0, 0, 0, 184, 185, 186, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 189, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 0, 201, 202, 909, 661, 0, 0, 910, 0, 203, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 175, 176, 177, 178, 179, 180, 181, 0, 0, 182, 183, 0, 0, 0, 0, 184, 185, 186, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 189, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 0, 201, 202, 1050, 652, 0, 0, 1051, 0, 203, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 175, 176, 177, 178, 179, 180, 181, 0, 0, 182, 183, 0, 0, 0, 0, 184, 185, 186, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 189, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 0, 201, 202, 1062, 652, 0, 0, 1063, 0, 203, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 175, 176, 177, 178, 179, 180, 181, 0, 0, 182, 183, 0, 0, 0, 0, 184, 185, 186, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 189, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 0, 201, 202, 1065, 661, 0, 0, 1066, 0, 203, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 175, 176, 177, 178, 179, 180, 181, 0, 0, 182, 183, 0, 0, 0, 0, 184, 185, 186, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 189, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 0, 201, 202, 666, 661, 0, 0, 667, 0, 203, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 175, 176, 177, 178, 179, 180, 181, 0, 0, 182, 183, 0, 0, 0, 0, 184, 185, 186, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 189, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 873, 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 0, 201, 202, 0, 0, 0, 0, 0, 0, 203, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, 0, 416, 417, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 419, 0, 0, 0, 0, 886, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 420, 0, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, 0, 416, 417, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 419, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 420, 0, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, 0, 416, 417, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 419, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 420, 0, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 0, 0, 0, 0, 0, 0, 0, 0, -277, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, 0, 416, 417, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 419, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 420, 0, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 0, 0, 0, 0, 0, 0, 0, 0, -279, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, 0, 416, 417, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 419, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 420, 0, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 0, 0, 0, 0, 0, 0, 0, 0, -280, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, 0, 416, 417, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 419, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 420, 0, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 0, 0, 0, 0, 0, 0, 0, 0, -282, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, 0, 416, 417, 0, 0, 0, 501, 0, 0, 0, 0, 0, 0, 0, 419, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 420, 0, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, 0, 416, 417, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 419, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 420, 0, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, -621, -621, 0, 0, 0, 0, 416, 417, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 419, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430 }; static const yytype_int16 yycheck[] = { 1, 88, 13, 15, 16, 285, 26, 19, 55, 27, 1, 6, 3, 4, 5, 65, 27, 8, 9, 20, 490, 12, 68, 14, 15, 16, 86, 87, 19, 314, 9, 21, 4, 5, 222, 14, 379, 481, 14, 790, 12, 272, 8, 9, 6, 403, 3, 1, 14, 3, 597, 13, 53, 54, 81, 332, 762, 307, 594, 118, 51, 311, 57, 508, 55, 27, 51, 15, 16, 692, 547, 19, 73, 74, 65, 374, 20, 21, 431, 26, 703, 594, 435, 55, 504, 438, 669, 670, 508, 25, 81, 296, 68, 319, 539, 57, 395, 16, 25, 74, 59, 60, 61, 62, 25, 53, 459, 976, 433, 81, 111, 15, 16, 90, 104, 19, 15, 16, 15, 16, 19, 474, 19, 476, 26, 57, 117, 951, 119, 25, 0, 25, 485, 458, 74, 612, 25, 90, 443, 444, 103, 90, 29, 121, 90, 371, 25, 122, 58, 59, 475, 117, 105, 25, 53, 54, 53, 54, 218, 484, 104, 460, 79, 144, 142, 128, 28, 111, 112, 229, 147, 51, 525, 333, 138, 55, 336, 90, 338, 57, 340, 92, 342, 90, 1053, 129, 18, 26, 20, 121, 121, 138, 90, 90, 147, 400, 115, 550, 147, 118, 119, 147, 90, 388, 16, 390, 142, 55, 144, 126, 121, 92, 213, 214, 92, 142, 142, 144, 754, 210, 1044, 142, 973, 142, 549, 976, 113, 146, 121, 148, 290, 767, 223, 224, 147, 90, 138, 820, 403, 214, 147, 754, 144, 121, 223, 224, 142, 242, 142, 147, 956, 90, 298, 142, 767, 121, 316, 223, 224, 147, 306, 307, 547, 142, 276, 311, 105, 279, 144, 270, 142, 272, 125, 213, 214, 276, 252, 55, 443, 444, 242, 558, 513, 90, 270, 276, 272, 314, 279, 734, 90, 92, 147, 284, 285, 17, 18, 288, 55, 138, 25, 140, 1053, 115, 295, 296, 118, 119, 147, 142, 92, 296, 303, 733, 734, 16, 92, 92, 284, 285, 306, 798, 298, 314, 947, 551, 809, 612, 276, 876, 20, 279, 815, 691, 146, 27, 148, 142, 101, 121, 147, 295, 314, 37, 38, 121, 121, 147, 398, 303, 58, 59, 51, 403, 401, 37, 38, 870, 349, 350, 351, 352, 353, 354, 355, 356, 325, 880, 131, 132, 133, 350, 351, 352, 353, 279, 377, 57, 379, 276, 279, 680, 279, 374, 350, 351, 352, 353, 138, 355, 356, 811, 349, 443, 444, 349, 556, 101, 457, 92, 354, 92, 115, 847, 395, 118, 119, 398, 145, 400, 401, 92, 403, 665, 115, 400, 668, 118, 119, 92, 119, 433, 141, 115, 888, 889, 118, 119, 121, 401, 121, 774, 433, 115, 686, 148, 118, 119, 121, 92, 121, 139, 433, 723, 55, 146, 458, 148, 121, 802, 398, 804, 443, 444, 146, 403, 148, 458, 142, 60, 973, 142, 63, 475, 146, 466, 148, 458, 121, 460, 461, 92, 484, 435, 475, 101, 438, 947, 803, 101, 471, 57, 502, 484, 475, 63, 64, 65, 479, 142, 121, 435, 791, 484, 744, 461, 727, 459, 489, 92, 121, 806, 121, 521, 949, 319, 107, 812, 813, 142, 521, 512, 513, 798, 476, 459, 51, 92, 545, 818, 547, 522, 142, 485, 92, 805, 512, 513, 121, 949, 718, 522, 476, 1046, 142, 511, 433, 549, 116, 117, 531, 485, 92, 587, 1057, 493, 121, 591, 549, 892, 893, 16, 142, 121, 545, 92, 547, 371, 549, 521, 51, 458, 622, 525, 863, 142, 557, 142, 522, 51, 92, 121, 584, 545, 142, 547, 61, 531, 475, 64, 65, 525, 862, 121, 121, 612, 142, 484, 550, 288, 51, 603, 142, 584, 779, 780, 781, 296, 783, 121, 785, 1, 99, 3, 605, 504, 550, 504, 8, 9, 15, 508, 603, 918, 14, 15, 16, 13, 57, 19, 640, 612, 121, 609, 16, 856, 121, 443, 444, 63, 929, 116, 117, 121, 122, 800, 15, 536, 145, 536, 612, 806, 539, 92, 145, 139, 142, 812, 813, 142, 115, 51, 549, 118, 119, 142, 51, 15, 659, 15, 44, 477, 478, 142, 665, 65, 656, 668, 669, 670, 121, 141, 121, 659, 69, 665, 115, 141, 668, 118, 119, 146, 1025, 148, 123, 686, 15, 1040, 679, 680, 691, 692, 18, 142, 689, 696, 714, 141, 744, 141, 679, 400, 703, 714, 587, 15, 101, 102, 103, 148, 526, 139, 656, 679, 1039, 139, 141, 117, 139, 119, 142, 665, 713, 215, 668, 44, 727, 689, 142, 816, 222, 142, 718, 128, 713, 115, 57, 551, 118, 119, 684, 727, 686, 691, 90, 933, 790, 713, 142, 142, 800, 939, 714, 918, 44, 15, 806, 807, 93, 105, 14, 142, 812, 813, 15, 1045, 580, 259, 15, 26, 145, 798, 471, 774, 142, 960, 961, 962, 963, 142, 479, 115, 142, 597, 118, 119, 600, 142, 141, 15, 489, 803, 300, 15, 140, 139, 304, 15, 144, 26, 791, 147, 803, 73, 74, 15, 15, 798, 799, 800, 210, 139, 803, 126, 126, 806, 807, 142, 55, 820, 15, 812, 813, 223, 224, 90, 798, 818, 819, 830, 139, 55, 833, 90, 733, 142, 733, 734, 142, 142, 105, 832, 804, 142, 835, 799, 594, 142, 105, 597, 142, 121, 122, 844, 845, 856, 835, 349, 557, 90, 804, 852, 142, 90, 1049, 90, 15, 144, 918, 835, 856, 363, 863, 864, 105, 140, 276, 144, 105, 279, 105, 138, 147, 140, 284, 285, 141, 144, 288, 381, 147, 892, 893, 142, 522, 295, 296, 12, 5, 890, 26, 1042, 1044, 303, 895, 790, 803, 790, 817, 140, 1041, 138, 811, 140, 811, 140, 147, 144, 890, 6, 147, 90, 147, 895, 254, 790, 970, 918, 587, 973, 970, 973, 976, 993, 978, -1, 105, 928, 929, -1, -1, 932, 90, 213, 214, 936, 947, -1, -1, 349, 350, 351, 352, 353, 354, 355, 356, 105, -1, -1, 932, -1, -1, 51, 90, 53, 54, 55, 56, -1, -1, 140, -1, -1, 374, -1, 790, -1, 147, 105, -1, 69, 1026, 63, 64, 65, 1006, -1, 480, 481, 806, 807, 140, -1, -1, 395, 812, 813, 398, 147, 400, 509, -1, 403, -1, 996, 754, 998, 516, 1053, 1001, 1055, 138, 1057, 140, 1059, -1, -1, 144, 767, 528, 147, 838, 839, 1025, 841, 842, 1039, 40, 41, 42, 43, 44, 433, -1, 1079, 116, 117, 1039, 531, 1041, 1042, 790, 443, 444, -1, 538, -1, 1039, 319, 90, -1, 949, -1, 142, -1, -1, 26, 458, -1, 460, 461, 876, -1, 878, 105, -1, -1, 882, -1, -1, 471, -1, 581, 582, 475, -1, 90, 1040, 479, -1, -1, 61, -1, 484, 64, 65, 970, -1, 489, 973, -1, 105, 976, 90, 978, 1040, 90, -1, -1, 140, 371, 918, 611, -1, 970, -1, 147, 973, 105, -1, 976, 105, 978, 51, -1, 53, 54, 55, 56, 819, 90, 522, -1, -1, 941, -1, 140, 876, -1, -1, 531, 69, 832, 147, -1, 105, 116, 117, -1, 954, 955, 1039, 1026, 140, 844, 845, 140, -1, 549, -1, 147, -1, 852, 147, 897, 898, 557, 90, -1, -1, 1026, 976, -1, 978, 864, -1, -1, -1, 138, 1053, 140, 1055, 105, 1057, 144, 1059, -1, 147, -1, 688, -1, 26, 90, 584, -1, 678, -1, 1053, -1, 1055, -1, 1057, -1, 1059, -1, 1079, -1, 105, 1013, -1, -1, 1016, 603, -1, 142, 90, -1, 140, 90, -1, -1, 8, 9, 1079, 147, -1, 90, 14, 15, 16, 105, -1, 19, 105, 973, 718, -1, 976, 928, -1, 973, 105, 140, 1048, 743, -1, 936, -1, 1053, 147, 1055, -1, -1, 115, 1059, 90, 118, 119, -1, 46, 47, 48, 49, -1, 763, 140, 53, 54, 140, -1, 105, -1, 147, -1, 1079, 147, 140, -1, 65, 66, 142, -1, -1, 147, 146, -1, 148, -1, 679, 680, 551, -1, 62, 776, 64, 65, 1029, 1030, 1031, -1, 1033, 1034, -1, 138, -1, 140, 996, -1, 998, 144, -1, 1001, 147, 62, 1053, 64, 65, 63, 64, 65, -1, -1, 713, -1, 63, 64, 65, 63, 64, 65, 117, -1, -1, 51, -1, 53, 54, 55, 56, 822, 1073, 1074, 1075, 1076, -1, -1, 116, 117, -1, -1, 1083, 69, -1, -1, -1, 51, 855, 53, 54, 55, 56, -1, -1, -1, 847, 83, -1, 116, 117, -1, 116, 117, 871, 69, -1, -1, 94, 116, 117, -1, 116, 117, 100, 101, 102, 103, -1, 83, -1, 63, 64, 65, 63, 64, 65, -1, -1, -1, 94, -1, 791, -1, -1, 121, 100, 101, 102, 103, 799, 800, 128, -1, 803, 131, -1, 806, 807, 63, 64, 65, -1, 812, 813, -1, -1, -1, 144, 818, 819, -1, -1, -1, 128, -1, -1, 131, 223, 224, -1, -1, -1, 832, 116, 117, 835, 116, 117, -1, 144, -1, 933, -1, -1, 844, 845, -1, 939, -1, -1, -1, -1, 852, 51, -1, 53, 54, 55, 56, -1, -1, 116, 117, 863, 864, -1, 262, 263, 264, 265, -1, 69, 51, -1, 53, 54, 55, 56, -1, -1, 276, -1, 51, 279, 53, 54, 55, 56, 284, 285, 69, -1, -1, -1, -1, -1, 94, -1, -1, -1, 69, -1, 100, 101, 102, 103, -1, -1, 51, -1, 53, 54, 55, 56, -1, 94, -1, -1, 918, -1, -1, 100, 101, 102, 103, 94, 69, -1, 928, 929, 128, 100, -1, 131, -1, 51, 936, 53, 54, 55, 56, 115, -1, -1, 118, 119, 144, -1, -1, 128, -1, 94, 131, 69, 350, 351, 352, 353, 115, 355, 356, 118, 119, 142, 115, -1, -1, 118, 119, -1, -1, 145, 146, -1, 148, -1, -1, -1, 94, 375, -1, -1, -1, -1, 100, -1, -1, -1, -1, 146, 386, 148, -1, -1, -1, 146, 996, 148, 998, -1, -1, 1001, 398, -1, -1, -1, -1, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 101, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, -1, -1, 433, -1, 1039, 51, -1, 53, 54, 55, 56, -1, 443, 444, -1, 129, 130, 131, 132, 133, -1, -1, -1, 69, -1, -1, -1, 458, -1, -1, 461, 51, -1, 53, 54, 55, 56, -1, -1, 85, -1, -1, 473, -1, 475, -1, 477, 478, 94, 69, -1, -1, -1, 484, 100, 101, 102, 103, -1, -1, -1, -1, 493, -1, -1, 496, 497, -1, -1, -1, 501, -1, -1, 504, -1, 506, -1, 508, 509, -1, -1, -1, 128, -1, -1, 131, -1, -1, -1, -1, 1, -1, 3, 4, 5, 526, -1, -1, -1, -1, -1, 12, -1, -1, -1, 536, -1, 51, 539, 53, 54, 55, 56, -1, -1, -1, -1, -1, 549, -1, -1, 88, 89, -1, -1, 69, -1, -1, 51, -1, 53, 54, 55, 56, 101, 566, 567, -1, -1, -1, 51, 85, -1, -1, 55, -1, 69, 88, 89, -1, 94, -1, -1, 584, -1, -1, 100, 101, 102, 103, 101, 128, 129, 130, 131, 132, 133, -1, -1, -1, 81, 94, 603, -1, -1, 606, -1, -1, 101, 102, 103, -1, -1, -1, 128, -1, -1, 131, 129, 130, 131, 132, 133, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, 128, -1, -1, 119, 13, 14, 15, 16, 17, 18, -1, 20, -1, -1, -1, -1, -1, 26, 27, 28, -1, -1, -1, -1, -1, -1, -1, -1, 37, 38, -1, 40, 41, 42, 43, 44, -1, -1, -1, -1, -1, -1, -1, 679, -1, 1, -1, 3, 4, 5, -1, -1, -1, -1, -1, 691, 12, -1, 694, 695, -1, -1, -1, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, -1, 713, -1, 88, 89, 90, -1, -1, 93, -1, -1, -1, 724, -1, 99, -1, 101, -1, 210, 51, 105, 733, 734, 55, -1, -1, -1, -1, -1, -1, 115, -1, -1, 118, 119, -1, -1, 122, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 81, -1, -1, -1, 138, 139, 140, 141, 142, -1, 144, 145, 146, 147, 148, 51, -1, 53, 54, 55, 56, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 69, 795, -1, -1, -1, 119, 800, 801, -1, 803, -1, -1, 806, 807, 288, -1, 85, 811, 812, 813, -1, 295, 296, -1, -1, 94, -1, -1, -1, 303, -1, 100, 101, 102, 103, -1, -1, -1, -1, -1, 314, 835, -1, -1, 838, 839, -1, 841, 842, -1, -1, -1, -1, -1, -1, -1, -1, 851, -1, 128, -1, 855, 131, 51, -1, 53, 54, 55, 56, -1, -1, -1, -1, -1, -1, 349, -1, -1, 872, 873, 354, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, 885, 886, -1, -1, -1, 210, -1, -1, -1, 374, -1, -1, -1, -1, -1, 94, 901, -1, -1, -1, -1, 100, 101, 102, 103, -1, 911, 912, 88, 89, 395, -1, -1, 918, -1, 400, 401, -1, 403, -1, -1, 101, -1, -1, -1, -1, -1, -1, -1, 128, -1, -1, 131, -1, -1, -1, 941, -1, -1, -1, -1, -1, -1, -1, 949, -1, 126, 127, 128, 129, 130, 131, 132, 133, -1, -1, -1, -1, 443, 444, -1, -1, -1, 288, -1, -1, -1, -1, -1, -1, 295, 296, -1, -1, -1, 460, -1, -1, 303, -1, -1, -1, -1, -1, -1, -1, 471, -1, -1, 314, -1, -1, -1, -1, 479, -1, -1, -1, 72, 73, 74, 75, 76, 77, 489, -1, 80, 81, -1, -1, -1, -1, -1, -1, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, 349, -1, -1, 101, -1, 354, -1, -1, -1, -1, 1039, -1, -1, 522, -1, -1, -1, -1, -1, -1, -1, -1, 531, -1, -1, 374, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 545, -1, 547, -1, -1, -1, -1, -1, -1, -1, 395, 0, 557, -1, -1, 400, 401, -1, 403, -1, -1, -1, -1, -1, 13, 14, 15, 16, 17, 18, -1, 20, -1, -1, -1, -1, -1, 26, 27, -1, -1, -1, -1, -1, -1, -1, -1, -1, 37, 38, -1, 40, 41, 42, 43, 44, -1, -1, 443, 444, -1, -1, -1, -1, -1, -1, -1, 612, -1, -1, -1, -1, -1, -1, -1, 460, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 471, -1, -1, -1, -1, -1, -1, -1, 479, -1, -1, -1, -1, -1, -1, 90, -1, -1, 489, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 105, 1, -1, 3, 4, 5, -1, -1, -1, -1, 115, -1, 12, 118, 119, -1, -1, -1, -1, 680, -1, 522, -1, -1, -1, -1, -1, -1, -1, -1, 531, -1, -1, 138, 139, -1, -1, -1, -1, 144, 145, 146, 147, 148, 545, -1, 547, -1, -1, -1, -1, 51, -1, -1, -1, 55, 557, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, 81, -1, -1, 88, 89, -1, -1, -1, 93, -1, -1, -1, -1, -1, -1, -1, 101, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 612, -1, -1, -1, -1, -1, -1, 122, 119, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 791, -1, -1, -1, -1, -1, -1, 798, 799, 800, -1, -1, -1, -1, -1, 806, -1, -1, -1, -1, -1, 812, 813, -1, -1, -1, -1, 818, 819, -1, -1, 1, -1, 3, 4, 5, 6, -1, -1, -1, -1, 832, 12, -1, -1, -1, -1, -1, -1, 680, -1, -1, -1, 844, 845, -1, -1, -1, -1, -1, -1, 852, 72, 73, 74, 75, 76, 77, 78, -1, 80, 81, 863, 864, -1, -1, -1, -1, 88, 89, 210, 51, -1, -1, -1, 55, -1, -1, -1, -1, -1, 101, -1, -1, -1, -1, -1, -1, -1, 890, -1, -1, -1, -1, 895, -1, -1, -1, -1, -1, -1, 81, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, -1, -1, 918, -1, -1, -1, -1, -1, -1, -1, -1, -1, 928, 929, -1, -1, 932, -1, -1, -1, 936, -1, -1, -1, 119, -1, -1, -1, -1, -1, -1, -1, -1, 288, -1, 791, -1, -1, -1, -1, 295, 296, 798, 799, 800, -1, -1, -1, 303, -1, 806, -1, -1, -1, -1, -1, 812, 813, -1, 314, -1, -1, 818, 819, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 832, -1, -1, -1, 996, -1, 998, -1, -1, 1001, -1, -1, 844, 845, -1, -1, -1, -1, 349, -1, 852, -1, -1, 354, -1, -1, -1, -1, -1, -1, -1, 863, 864, -1, -1, -1, -1, -1, -1, 210, -1, -1, -1, 374, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 890, -1, -1, -1, -1, 895, 395, -1, -1, -1, -1, 400, 401, -1, 403, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 918, -1, -1, -1, -1, -1, -1, -1, -1, -1, 928, 929, -1, -1, 932, -1, -1, 1, 936, 3, -1, -1, -1, -1, -1, -1, 443, 444, 12, -1, -1, 288, -1, -1, -1, -1, -1, -1, 295, 296, -1, -1, -1, 460, -1, -1, 303, -1, -1, -1, -1, -1, -1, -1, 471, -1, -1, 314, -1, -1, -1, -1, 479, -1, -1, -1, -1, 51, -1, -1, -1, -1, 489, -1, -1, -1, -1, -1, 996, -1, 998, -1, -1, 1001, -1, -1, -1, -1, -1, -1, -1, -1, 349, -1, -1, -1, -1, 354, -1, -1, -1, -1, -1, -1, -1, 522, -1, -1, -1, -1, -1, -1, -1, -1, 531, -1, -1, 374, -1, -1, -1, -1, -1, -1, 44, -1, -1, -1, 545, -1, 547, -1, -1, -1, -1, 119, -1, -1, 395, -1, 557, -1, -1, 400, 401, -1, -1, -1, -1, -1, -1, -1, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, -1, -1, -1, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 101, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 612, -1, -1, 1, -1, 3, -1, 122, 460, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 471, -1, -1, -1, -1, -1, -1, -1, 479, -1, -1, -1, 210, -1, -1, -1, -1, -1, 489, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 51, -1, -1, -1, -1, -1, -1, -1, 51, 52, -1, -1, 55, -1, -1, 680, -1, 522, -1, -1, -1, -1, -1, -1, -1, -1, 531, 70, 71, 72, 73, 74, 75, 76, 77, -1, -1, 80, 81, -1, 545, -1, 547, 86, 87, 88, 89, -1, -1, -1, -1, -1, 557, -1, -1, -1, 288, 100, 101, 102, -1, -1, -1, 295, 296, -1, -1, -1, 119, -1, -1, 303, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, -1, -1, -1, -1, -1, -1, 143, 144, -1, -1, -1, -1, -1, 612, -1, -1, -1, -1, -1, -1, -1, -1, -1, 349, -1, -1, -1, -1, 354, -1, -1, -1, 791, -1, -1, -1, -1, -1, -1, 798, 799, 800, -1, -1, -1, -1, -1, 806, 374, -1, -1, -1, -1, 812, 813, -1, -1, -1, -1, 818, 819, -1, -1, -1, -1, 210, -1, -1, -1, 395, -1, -1, -1, 832, 400, -1, -1, 403, -1, -1, -1, 680, -1, -1, -1, 844, 845, -1, -1, -1, -1, -1, -1, 852, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 863, 864, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 443, 444, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 890, -1, -1, 460, -1, 895, -1, -1, -1, -1, -1, -1, 288, -1, 471, -1, -1, -1, -1, 295, 296, -1, 479, -1, -1, -1, -1, 303, 918, -1, -1, -1, 489, -1, -1, -1, -1, -1, 928, 929, -1, -1, 932, -1, -1, -1, 936, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 791, -1, -1, -1, 522, -1, -1, 798, 799, -1, -1, -1, 349, 531, -1, -1, -1, 354, -1, -1, -1, -1, -1, -1, -1, -1, -1, 818, 819, -1, -1, -1, -1, -1, -1, -1, -1, 374, -1, 557, -1, 832, -1, -1, -1, 996, -1, 998, -1, -1, 1001, -1, -1, 844, 845, -1, -1, -1, 395, -1, -1, 852, -1, 400, -1, -1, 403, -1, -1, -1, -1, -1, 863, 864, -1, -1, -1, -1, -1, -1, -1, -1, -1, 44, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 890, -1, -1, -1, -1, 895, -1, 443, 444, -1, -1, -1, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 460, -1, -1, -1, 88, 89, -1, -1, -1, -1, -1, 471, -1, -1, 928, 929, -1, 101, 932, 479, -1, -1, 936, -1, -1, -1, -1, -1, -1, 489, -1, -1, -1, -1, -1, -1, -1, -1, 122, 680, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 72, 73, 74, 75, 76, 77, -1, 142, 80, 81, -1, 522, -1, -1, -1, -1, 88, 89, -1, -1, 531, -1, -1, -1, -1, -1, -1, -1, -1, 101, -1, 996, -1, 998, -1, -1, 1001, -1, -1, -1, -1, -1, -1, -1, -1, -1, 557, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 791, -1, -1, -1, -1, -1, -1, -1, 799, 800, -1, -1, -1, -1, -1, 806, -1, -1, -1, -1, -1, 812, 813, -1, -1, 0, -1, 818, 819, -1, -1, -1, -1, -1, -1, -1, -1, -1, 13, 14, 15, 832, 17, 18, -1, 20, -1, -1, -1, -1, -1, 26, -1, 844, 845, -1, -1, -1, -1, -1, -1, 852, 37, 38, -1, 40, 41, 42, 43, 44, 680, -1, 863, 864, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, -1, -1, -1, 88, 89, 90, -1, 92, 93, -1, -1, -1, -1, -1, -1, -1, 101, 918, -1, -1, 105, -1, -1, -1, -1, -1, -1, 928, 929, -1, 115, 932, -1, 118, 119, 936, 121, 122, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, -1, -1, -1, 138, 139, 140, -1, 142, -1, -1, 145, 146, 147, 148, -1, -1, -1, -1, -1, -1, -1, 791, -1, -1, -1, -1, -1, -1, -1, 799, 800, -1, -1, -1, 51, 52, 806, -1, 55, -1, -1, -1, 812, 813, -1, 996, -1, 998, 818, 819, 1001, -1, -1, 70, 71, 72, 73, 74, 75, 76, 77, -1, 832, 80, 81, -1, -1, -1, -1, 86, 87, 88, 89, -1, 844, 845, -1, -1, -1, -1, -1, -1, 852, 100, 101, 102, -1, -1, -1, -1, -1, -1, -1, 863, 864, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, -1, -1, -1, -1, -1, -1, 143, 144, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 918, -1, -1, -1, -1, -1, -1, -1, -1, -1, 928, 929, -1, -1, -1, -1, -1, -1, 936, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1, -1, -1, -1, -1, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, 996, 39, 998, -1, -1, 1001, -1, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, 0, 114, 115, -1, -1, 118, 119, -1, -1, -1, -1, -1, -1, 13, 14, 15, 16, 17, 18, -1, 20, 134, 135, 136, -1, -1, -1, 27, 28, 29, -1, -1, -1, 146, -1, 148, -1, 37, 38, -1, 40, 41, 42, 43, 44, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 57, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, -1, -1, -1, 88, 89, 90, -1, -1, 93, -1, -1, -1, -1, -1, 99, -1, 101, -1, -1, -1, 105, -1, -1, -1, -1, -1, -1, -1, 113, -1, 115, -1, -1, 118, 119, -1, -1, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, -1, 0, -1, -1, 139, 140, 141, 142, -1, -1, 145, 146, 147, 148, 13, 14, 15, 16, 17, 18, -1, 20, -1, -1, -1, -1, -1, -1, 27, 28, -1, -1, -1, -1, -1, -1, -1, -1, 37, 38, -1, 40, 41, 42, 43, 44, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 57, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, -1, -1, -1, 88, 89, 90, -1, 92, 93, -1, -1, -1, -1, -1, 99, -1, 101, -1, -1, -1, 105, -1, -1, -1, -1, -1, -1, -1, -1, -1, 115, -1, -1, 118, 119, -1, 121, 122, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, -1, 0, -1, -1, 139, 140, 141, 142, -1, -1, 145, 146, 147, 148, 13, 14, 15, 16, 17, 18, -1, 20, -1, -1, -1, -1, -1, 26, 27, 28, -1, -1, -1, -1, -1, -1, -1, -1, 37, 38, -1, 40, 41, 42, 43, 44, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, -1, -1, -1, 88, 89, 90, -1, -1, 93, -1, -1, -1, -1, -1, 99, -1, 101, -1, -1, -1, 105, -1, -1, -1, -1, -1, -1, -1, -1, -1, 115, -1, -1, 118, 119, -1, -1, 122, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, -1, 0, -1, 138, 139, 140, 141, 142, -1, 144, 145, 146, 147, 148, 13, 14, 15, 16, 17, 18, -1, 20, -1, -1, -1, -1, -1, -1, 27, 28, -1, -1, -1, -1, -1, -1, -1, -1, 37, 38, -1, 40, 41, 42, 43, 44, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, -1, -1, -1, 88, 89, 90, -1, -1, 93, -1, -1, -1, -1, -1, 99, -1, 101, -1, -1, -1, 105, -1, -1, -1, -1, -1, -1, -1, -1, -1, 115, -1, -1, 118, 119, -1, -1, 122, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, -1, 0, -1, -1, 139, 140, 141, 142, -1, 144, 145, 146, 147, 148, 13, 14, 15, -1, 17, 18, -1, 20, -1, -1, -1, -1, -1, 26, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 37, 38, -1, 40, 41, 42, 43, 44, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, -1, -1, -1, 88, 89, 90, -1, 92, 93, -1, -1, -1, -1, -1, -1, -1, 101, -1, -1, -1, 105, -1, -1, -1, -1, -1, -1, -1, -1, -1, 115, -1, -1, 118, 119, -1, 121, 122, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, -1, 0, -1, 138, 139, 140, -1, 142, -1, -1, 145, 146, 147, 148, 13, 14, 15, -1, 17, 18, -1, 20, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 37, 38, -1, 40, 41, 42, 43, 44, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, -1, -1, -1, 88, 89, 90, -1, 92, 93, -1, -1, -1, -1, -1, -1, -1, 101, -1, -1, -1, 105, -1, -1, -1, -1, -1, -1, -1, -1, -1, 115, -1, -1, 118, 119, -1, 121, 122, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, -1, 0, -1, -1, 139, 140, -1, 142, -1, -1, 145, 146, 147, 148, 13, 14, 15, -1, 17, 18, -1, 20, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 37, 38, -1, 40, 41, 42, 43, 44, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, -1, -1, -1, 88, 89, 90, -1, 92, 93, -1, -1, -1, -1, -1, -1, -1, 101, -1, -1, -1, 105, -1, -1, -1, -1, -1, -1, -1, -1, -1, 115, -1, -1, 118, 119, -1, 121, 122, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, -1, -1, -1, -1, 139, 140, -1, 142, -1, -1, 145, 146, 147, 148, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, -1, -1, 18, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, 115, -1, -1, 118, 119, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 134, 135, 136, -1, -1, -1, -1, -1, -1, -1, -1, -1, 146, 1, 148, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1, 15, -1, 17, 18, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, 115, -1, -1, 118, 119, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 134, 135, 136, -1, -1, -1, -1, -1, -1, -1, -1, -1, 146, 1, 148, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1, 15, -1, -1, 18, 19, 20, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, 115, -1, -1, 118, 119, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 134, 135, 136, -1, -1, -1, -1, -1, -1, -1, -1, -1, 146, 1, 148, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1, 15, -1, -1, 18, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, 115, -1, -1, 118, 119, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1, 134, 135, 136, -1, 19, -1, 21, 22, 23, 24, -1, -1, 146, -1, 148, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, 115, -1, -1, 118, 119, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 134, 135, 136, -1, -1, 139, -1, -1, -1, -1, -1, -1, 146, 1, 148, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, 14, 15, -1, -1, -1, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, 115, -1, -1, 118, 119, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1, 134, 135, 136, -1, 19, -1, 21, 22, 23, 24, -1, -1, 146, -1, 148, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, 115, -1, -1, 118, 119, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1, 134, 135, 136, -1, 19, -1, 21, 22, 23, 24, -1, 145, 146, -1, 148, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, 115, -1, -1, 118, 119, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1, 134, 135, 136, -1, 19, -1, 21, 22, 23, 24, -1, 145, 146, -1, 148, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, 115, -1, -1, 118, 119, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 134, 135, 136, -1, -1, 139, -1, -1, -1, -1, -1, -1, 146, 1, 148, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1, 15, -1, -1, -1, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, 115, -1, -1, 118, 119, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1, 134, 135, 136, -1, 19, -1, 21, 22, 23, 24, -1, -1, 146, -1, 148, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, 115, -1, -1, 118, 119, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1, 134, 135, 136, -1, 19, -1, 21, 22, 23, 24, -1, -1, 146, -1, 148, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, 115, -1, -1, 118, 119, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, 134, 135, 136, -1, 19, -1, 21, 22, 23, 24, -1, -1, 146, -1, 148, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, 115, -1, -1, 118, 119, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, 134, 135, 136, -1, 19, -1, 21, 22, 23, 24, -1, -1, 146, -1, 148, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, 115, -1, -1, 118, 119, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, 134, 135, 136, -1, 19, -1, 21, 22, 23, 24, -1, -1, 146, -1, 148, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, 115, -1, -1, 118, 119, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 134, 135, 136, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 148, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, -1, -1, -1, -1, -1, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 69, 70, 71, 72, 73, 74, 75, 76, 77, -1, -1, 80, 81, -1, -1, -1, -1, 86, 87, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 100, 101, 102, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, -1, -1, -1, -1, -1, -1, 143, 144, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, 19, -1, 21, 22, 23, 24, -1, 26, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, 102, 103, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 134, 135, 136, -1, 138, -1, -1, -1, -1, -1, 144, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, 19, -1, 21, 22, 23, 24, -1, 26, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, 102, 103, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, 134, 135, 136, 19, 138, 21, 22, 23, 24, -1, 144, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, 92, -1, 94, 95, -1, 97, 98, -1, 100, -1, 102, 103, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 121, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, 134, 135, 136, -1, 19, -1, 21, 22, 23, 24, 144, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, 92, -1, 94, 95, -1, 97, 98, -1, 100, -1, 102, 103, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 121, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, 134, 135, 136, -1, 19, -1, 21, 22, 23, 24, 144, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, 102, 103, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, 134, 135, 136, 19, -1, 21, 22, 23, 24, -1, 144, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, 102, 103, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 134, 135, 136, -1, -1, -1, -1, -1, -1, -1, 144, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, -1, -1, -1, -1, -1, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, -1, -1, -1, -1, -1, 63, -1, -1, -1, -1, -1, 69, 70, 71, 72, 73, 74, 75, 76, 77, -1, -1, 80, 81, -1, -1, -1, -1, 86, 87, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 100, 101, 102, -1, -1, -1, -1, 107, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, -1, -1, -1, -1, -1, -1, 143, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, -1, -1, -1, -1, -1, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, -1, -1, -1, -1, -1, 63, -1, -1, -1, -1, -1, -1, 70, 71, 72, 73, 74, 75, 76, 77, -1, -1, 80, 81, -1, -1, -1, -1, 86, 87, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 100, 101, 102, -1, -1, -1, -1, 107, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, -1, -1, -1, -1, -1, -1, 143, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, -1, -1, -1, -1, -1, 45, 46, 47, 48, 49, 50, 51, 52, -1, -1, 55, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 70, 71, 72, 73, 74, 75, 76, 77, -1, -1, 80, 81, -1, -1, -1, -1, 86, 87, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 100, 101, 102, -1, -1, -1, 106, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, -1, -1, -1, -1, -1, -1, 143, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, -1, -1, -1, -1, -1, 45, 46, 47, 48, 49, 50, 51, 52, -1, -1, 55, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 70, 71, 72, 73, 74, 75, 76, 77, -1, -1, 80, 81, -1, -1, -1, -1, 86, 87, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 100, 101, 102, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, -1, -1, -1, -1, -1, -1, 143, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, 19, 134, 21, 22, 23, 24, -1, -1, -1, 142, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1, -1, -1, -1, -1, 19, 134, 21, 22, 23, 24, -1, -1, -1, 142, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1, -1, 134, 135, 136, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, 134, 135, 136, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, 102, 103, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, 134, 135, 136, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, 102, 103, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, 134, 135, 136, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, 102, 103, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, 134, 135, 136, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, 102, 103, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, 134, 135, 136, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, 102, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, 134, 135, 136, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, -1, -1, 102, 103, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, 134, 135, 136, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, 102, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, 134, 135, 136, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, -1, -1, 102, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, 134, 135, 136, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, 134, 135, 136, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, 134, 135, 136, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, 134, 135, 136, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, 134, 135, 136, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, 134, 135, 136, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, 134, 135, 136, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, 134, 135, 136, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, 70, 71, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, 85, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, 134, 135, 136, 19, -1, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 88, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, 19, 134, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, 19, 134, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, 100, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, 19, 134, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, 19, 134, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, 19, 134, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, 19, 134, 21, 22, 23, 24, -1, -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, 58, 59, 60, -1, -1, 63, -1, -1, 66, 67, -1, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 91, -1, -1, 94, 95, -1, 97, 98, -1, -1, 51, 52, -1, 104, 55, 106, 107, 108, -1, 110, 111, 112, -1, 114, -1, -1, -1, -1, -1, 70, 71, 72, 73, 74, 75, 76, 77, -1, -1, 80, 81, -1, -1, 134, -1, 86, 87, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 100, 101, 102, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, 51, 52, -1, -1, 55, -1, 143, 144, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 70, 71, 72, 73, 74, 75, 76, 77, -1, -1, 80, 81, -1, -1, -1, -1, 86, 87, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 100, 101, 102, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, 51, 52, -1, -1, 55, -1, 143, 144, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 70, 71, 72, 73, 74, 75, 76, 77, -1, -1, 80, 81, -1, -1, -1, -1, 86, 87, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 100, 101, 102, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, 51, 52, -1, -1, 55, -1, 143, 144, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 70, 71, 72, 73, 74, 75, 76, 77, -1, -1, 80, 81, -1, -1, -1, -1, 86, 87, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 100, 101, 102, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, 51, 52, -1, -1, 55, -1, 143, 144, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 70, 71, 72, 73, 74, 75, 76, 77, -1, -1, 80, 81, -1, -1, -1, -1, 86, 87, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 100, 101, 102, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, 51, 52, -1, -1, 55, -1, 143, 144, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 70, 71, 72, 73, 74, 75, 76, 77, -1, -1, 80, 81, -1, -1, -1, -1, 86, 87, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 100, 101, 102, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, 51, 52, -1, -1, 55, -1, 143, 144, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 70, 71, 72, 73, 74, 75, 76, 77, -1, -1, 80, 81, -1, -1, -1, -1, 86, 87, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 100, 101, 102, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, 51, 52, -1, -1, 55, -1, 143, 144, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 70, 71, 72, 73, 74, 75, 76, 77, -1, -1, 80, 81, -1, -1, -1, -1, 86, 87, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 100, 101, 102, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, 51, 52, -1, -1, 55, -1, 143, 144, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 70, 71, 72, 73, 74, 75, 76, 77, -1, -1, 80, 81, -1, -1, -1, -1, 86, 87, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 100, 101, 102, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, 51, 52, -1, -1, 55, -1, 143, 144, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 70, 71, 72, 73, 74, 75, 76, 77, -1, -1, 80, 81, -1, -1, -1, -1, 86, 87, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 100, 101, 102, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, 51, 52, -1, -1, 55, -1, 143, 144, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 70, 71, 72, 73, 74, 75, 76, 77, -1, -1, 80, 81, -1, -1, -1, -1, 86, 87, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 100, 101, 102, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, 51, 52, -1, -1, 55, -1, 143, 144, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 70, 71, 72, 73, 74, 75, 76, 77, -1, -1, 80, 81, -1, -1, -1, -1, 86, 87, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 100, 101, 102, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, 51, 52, -1, -1, 55, -1, 143, 144, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 70, 71, 72, 73, 74, 75, 76, 77, -1, -1, 80, 81, -1, -1, -1, -1, 86, 87, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 100, 101, 102, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, 51, 52, -1, -1, 55, -1, 143, 144, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 70, 71, 72, 73, 74, 75, 76, 77, -1, -1, 80, 81, -1, -1, -1, -1, 86, 87, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 100, 101, 102, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 44, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, -1, -1, -1, -1, -1, -1, 143, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, -1, -1, -1, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 101, -1, -1, -1, -1, 44, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 122, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, -1, -1, -1, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 101, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 122, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, -1, -1, -1, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 101, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 122, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, -1, -1, -1, -1, -1, -1, -1, 142, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, -1, -1, -1, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 101, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 122, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, -1, -1, -1, -1, -1, -1, -1, 142, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, -1, -1, -1, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 101, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 122, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, -1, -1, -1, -1, -1, -1, -1, 142, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, -1, -1, -1, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 101, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 122, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, -1, -1, -1, -1, -1, -1, -1, 142, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, -1, -1, -1, 88, 89, -1, -1, -1, 93, -1, -1, -1, -1, -1, -1, -1, 101, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 122, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, -1, -1, -1, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 101, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 122, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, -1, -1, -1, 88, 89, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 101, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133 }; /* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of state STATE-NUM. */ static const yytype_int16 yystos[] = { 0, 150, 151, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 19, 21, 22, 23, 24, 30, 31, 32, 33, 34, 35, 36, 39, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 58, 59, 60, 63, 66, 67, 69, 70, 71, 84, 85, 91, 94, 95, 97, 98, 100, 104, 106, 107, 108, 110, 111, 112, 114, 134, 135, 136, 152, 153, 154, 160, 161, 163, 164, 166, 168, 169, 172, 173, 175, 176, 177, 179, 180, 189, 203, 220, 241, 242, 252, 253, 254, 258, 259, 260, 266, 267, 268, 270, 271, 272, 273, 274, 275, 311, 324, 0, 154, 21, 22, 30, 31, 32, 39, 51, 55, 69, 88, 91, 94, 134, 164, 166, 181, 182, 203, 220, 272, 275, 311, 182, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 45, 46, 47, 48, 49, 50, 51, 52, 55, 70, 71, 72, 73, 74, 75, 76, 77, 80, 81, 86, 87, 88, 89, 100, 101, 102, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 135, 136, 143, 144, 183, 187, 188, 274, 306, 204, 91, 163, 164, 166, 167, 180, 189, 220, 272, 273, 275, 167, 210, 212, 69, 91, 173, 180, 220, 225, 272, 275, 33, 34, 35, 36, 48, 49, 50, 51, 55, 106, 183, 184, 185, 268, 115, 118, 119, 146, 148, 167, 262, 263, 264, 317, 321, 322, 323, 51, 69, 100, 102, 103, 135, 172, 189, 195, 198, 201, 254, 309, 310, 195, 195, 144, 192, 193, 196, 197, 324, 192, 197, 144, 318, 184, 155, 138, 189, 220, 189, 189, 189, 55, 1, 94, 157, 158, 160, 174, 175, 324, 205, 207, 190, 201, 309, 324, 189, 308, 309, 324, 91, 142, 179, 220, 272, 275, 208, 53, 54, 56, 63, 69, 107, 183, 269, 63, 64, 65, 116, 117, 255, 256, 61, 255, 62, 255, 63, 255, 63, 255, 58, 59, 168, 189, 189, 317, 323, 40, 41, 42, 43, 44, 37, 38, 51, 53, 54, 55, 56, 69, 83, 94, 100, 101, 102, 103, 128, 131, 144, 278, 279, 280, 281, 282, 285, 286, 287, 288, 290, 291, 292, 293, 295, 296, 297, 300, 301, 302, 303, 304, 324, 278, 280, 28, 240, 121, 142, 94, 100, 176, 121, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 88, 89, 93, 101, 122, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 90, 105, 140, 147, 315, 90, 315, 316, 26, 138, 244, 254, 92, 92, 192, 197, 244, 163, 51, 55, 181, 58, 59, 279, 125, 276, 90, 140, 315, 219, 307, 90, 147, 314, 156, 157, 55, 278, 278, 16, 221, 321, 121, 90, 140, 315, 92, 92, 221, 167, 167, 55, 90, 140, 315, 25, 107, 142, 265, 317, 115, 264, 20, 246, 321, 57, 57, 189, 189, 189, 93, 142, 199, 200, 324, 57, 199, 200, 85, 194, 195, 201, 309, 324, 195, 163, 317, 319, 163, 322, 159, 138, 157, 90, 315, 92, 160, 174, 145, 317, 323, 319, 160, 319, 141, 200, 320, 323, 200, 320, 139, 320, 55, 176, 177, 178, 142, 90, 140, 315, 144, 237, 290, 295, 63, 255, 257, 261, 262, 63, 256, 61, 62, 63, 63, 101, 101, 154, 167, 167, 167, 167, 160, 163, 163, 57, 121, 57, 321, 294, 85, 290, 295, 121, 156, 189, 142, 305, 324, 51, 142, 305, 321, 142, 289, 189, 142, 289, 51, 142, 289, 51, 121, 156, 239, 100, 168, 189, 201, 202, 174, 142, 179, 142, 161, 162, 168, 180, 189, 191, 202, 220, 275, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 51, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 51, 52, 55, 187, 192, 312, 313, 194, 201, 51, 52, 55, 187, 192, 312, 51, 55, 312, 245, 243, 162, 189, 191, 162, 191, 99, 171, 217, 277, 216, 51, 55, 181, 312, 194, 312, 156, 163, 165, 15, 13, 248, 324, 121, 121, 157, 16, 51, 55, 194, 51, 55, 157, 27, 222, 321, 222, 51, 55, 194, 51, 55, 214, 186, 157, 246, 189, 201, 15, 189, 189, 189, 318, 100, 189, 198, 309, 189, 310, 319, 145, 317, 200, 200, 319, 145, 184, 152, 139, 191, 319, 160, 206, 309, 176, 178, 51, 55, 194, 51, 55, 290, 209, 142, 63, 157, 262, 189, 189, 51, 69, 100, 226, 295, 319, 319, 142, 172, 189, 15, 51, 69, 282, 287, 304, 85, 288, 293, 300, 302, 295, 297, 302, 51, 295, 172, 189, 15, 79, 126, 231, 233, 324, 189, 200, 319, 178, 142, 44, 121, 44, 90, 140, 315, 318, 92, 92, 192, 197, 141, 200, 92, 92, 193, 197, 193, 197, 231, 231, 170, 321, 167, 156, 141, 15, 319, 183, 189, 202, 249, 324, 18, 224, 324, 17, 223, 224, 92, 92, 141, 92, 92, 224, 211, 213, 141, 167, 184, 139, 15, 200, 221, 189, 199, 85, 309, 139, 319, 320, 141, 234, 318, 29, 113, 238, 139, 142, 292, 319, 142, 85, 44, 44, 305, 321, 142, 289, 142, 289, 142, 289, 142, 289, 289, 44, 44, 228, 230, 232, 281, 283, 284, 287, 295, 296, 298, 299, 302, 304, 156, 100, 189, 178, 160, 189, 51, 55, 194, 51, 55, 57, 123, 162, 191, 168, 191, 171, 92, 162, 191, 162, 191, 171, 244, 240, 156, 157, 231, 218, 321, 15, 93, 250, 324, 157, 14, 251, 324, 167, 15, 92, 15, 157, 157, 222, 189, 157, 319, 200, 145, 146, 156, 157, 227, 142, 100, 319, 189, 189, 295, 302, 295, 295, 189, 189, 234, 234, 91, 220, 142, 305, 305, 142, 229, 220, 142, 229, 142, 229, 15, 189, 141, 189, 189, 162, 191, 15, 139, 157, 156, 91, 180, 220, 272, 275, 221, 157, 221, 15, 15, 215, 224, 246, 247, 51, 235, 236, 291, 15, 139, 295, 295, 142, 292, 289, 142, 289, 289, 289, 126, 126, 55, 90, 283, 287, 142, 228, 229, 299, 302, 295, 298, 302, 295, 139, 15, 55, 90, 140, 315, 157, 157, 157, 142, 318, 142, 295, 142, 295, 51, 55, 305, 142, 229, 142, 229, 142, 229, 142, 229, 229, 51, 55, 194, 51, 55, 248, 223, 15, 236, 295, 289, 295, 302, 295, 295, 141, 229, 142, 229, 229, 229, 295, 229 }; /* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */ static const yytype_int16 yyr1[] = { 0, 149, 150, 151, 152, 153, 153, 153, 153, 154, 155, 154, 156, 157, 158, 158, 158, 158, 159, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 162, 162, 162, 163, 163, 163, 163, 163, 163, 164, 165, 166, 167, 168, 168, 169, 169, 170, 171, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 173, 173, 174, 174, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 176, 176, 177, 177, 178, 178, 179, 179, 179, 179, 179, 179, 179, 179, 180, 180, 180, 180, 180, 180, 180, 180, 180, 181, 181, 182, 182, 182, 183, 183, 183, 183, 183, 184, 184, 185, 186, 185, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 190, 190, 190, 190, 191, 191, 192, 192, 192, 193, 193, 194, 194, 194, 194, 194, 195, 195, 195, 195, 195, 196, 197, 198, 198, 199, 199, 200, 201, 201, 201, 201, 201, 201, 202, 202, 202, 203, 203, 203, 203, 203, 203, 203, 203, 204, 203, 205, 206, 203, 207, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 208, 209, 203, 203, 203, 210, 211, 203, 212, 213, 203, 203, 203, 214, 215, 203, 216, 203, 217, 218, 203, 219, 203, 203, 203, 203, 203, 203, 203, 220, 221, 221, 221, 222, 222, 223, 223, 224, 224, 225, 225, 226, 226, 226, 226, 226, 226, 226, 226, 227, 226, 228, 228, 228, 228, 229, 229, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 231, 231, 232, 233, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239, 240, 241, 241, 241, 241, 242, 242, 242, 242, 242, 242, 242, 242, 242, 243, 244, 245, 244, 246, 247, 247, 248, 248, 249, 249, 249, 250, 250, 251, 251, 252, 252, 252, 252, 253, 253, 254, 254, 254, 254, 255, 255, 256, 257, 256, 256, 256, 258, 258, 259, 259, 260, 261, 261, 262, 262, 263, 263, 264, 265, 264, 266, 266, 267, 267, 267, 268, 269, 269, 269, 269, 269, 269, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272, 272, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 276, 277, 276, 278, 278, 279, 279, 279, 280, 280, 280, 280, 281, 281, 282, 282, 283, 283, 284, 284, 285, 285, 286, 286, 287, 287, 288, 288, 288, 288, 289, 289, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 291, 291, 291, 291, 291, 292, 292, 293, 294, 293, 295, 295, 296, 297, 298, 299, 299, 300, 300, 301, 301, 302, 302, 303, 303, 304, 304, 305, 305, 306, 307, 306, 308, 308, 309, 309, 310, 310, 310, 310, 310, 310, 310, 310, 311, 311, 311, 312, 312, 312, 312, 313, 313, 313, 314, 314, 315, 315, 316, 316, 317, 317, 318, 318, 319, 320, 320, 320, 321, 321, 322, 322, 323, 323, 324 }; /* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */ static const yytype_int8 yyr2[] = { 0, 2, 0, 2, 2, 1, 1, 3, 2, 1, 0, 5, 4, 2, 1, 1, 3, 2, 0, 4, 2, 3, 3, 3, 3, 3, 4, 1, 3, 3, 3, 3, 3, 1, 3, 3, 6, 5, 5, 5, 5, 4, 6, 4, 6, 3, 1, 3, 1, 1, 3, 3, 3, 2, 1, 2, 0, 5, 1, 1, 1, 1, 4, 0, 5, 2, 3, 4, 5, 4, 5, 2, 2, 2, 2, 2, 1, 3, 1, 3, 1, 2, 3, 5, 2, 4, 2, 4, 1, 3, 1, 3, 2, 3, 1, 2, 1, 4, 3, 3, 3, 3, 2, 1, 1, 4, 3, 3, 3, 3, 2, 1, 1, 1, 1, 2, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 0, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 6, 5, 5, 5, 5, 4, 3, 3, 2, 2, 3, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 3, 3, 3, 3, 6, 6, 4, 6, 4, 6, 1, 1, 2, 4, 2, 1, 3, 3, 5, 3, 1, 1, 1, 2, 2, 4, 2, 1, 2, 2, 4, 1, 0, 2, 2, 1, 2, 1, 2, 1, 1, 2, 3, 3, 4, 3, 4, 2, 1, 1, 1, 1, 1, 1, 1, 1, 0, 4, 0, 0, 5, 0, 3, 3, 3, 2, 3, 3, 1, 2, 4, 3, 2, 1, 2, 0, 0, 5, 6, 6, 0, 0, 7, 0, 0, 7, 5, 4, 0, 0, 9, 0, 6, 0, 0, 8, 0, 5, 4, 4, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 5, 1, 2, 1, 1, 1, 4, 6, 3, 5, 2, 4, 1, 0, 4, 4, 2, 2, 1, 2, 0, 6, 8, 4, 6, 4, 3, 6, 2, 4, 6, 2, 4, 2, 4, 1, 1, 1, 0, 4, 1, 4, 1, 4, 1, 3, 1, 1, 4, 1, 3, 3, 0, 5, 2, 4, 5, 5, 2, 4, 4, 3, 3, 3, 2, 1, 4, 0, 5, 0, 5, 5, 1, 1, 6, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 3, 1, 2, 1, 0, 4, 1, 2, 2, 3, 2, 3, 1, 1, 2, 1, 2, 1, 2, 1, 0, 4, 2, 3, 1, 4, 2, 2, 1, 1, 1, 1, 1, 2, 2, 3, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 4, 1, 1, 3, 5, 3, 1, 2, 4, 2, 2, 2, 2, 1, 2, 1, 1, 3, 1, 3, 1, 1, 2, 1, 4, 2, 2, 1, 2, 0, 6, 8, 4, 6, 4, 6, 2, 4, 6, 2, 4, 2, 4, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 4, 1, 3, 2, 2, 2, 1, 3, 1, 3, 1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 1, 0, 4, 1, 2, 1, 3, 3, 3, 2, 2, 3, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 2, 2, 0, 1, 1, 1, 1, 1, 1, 1, 2, 0 }; enum { YYENOMEM = -2 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab #define YYNOMEM goto yyexhaustedlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY) \ { \ yychar = (Token); \ yylval = (Value); \ YYPOPSTACK (yylen); \ yystate = *yyssp; \ goto yybackup; \ } \ else \ { \ yyerror (&yylloc, p, YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (0) /* Backward compatibility with an undocumented macro. Use YYerror or YYUNDEF. */ #define YYERRCODE YYUNDEF /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. If N is 0, then set CURRENT to the empty location which ends the previous symbol: RHS[0] (always defined). */ #ifndef YYLLOC_DEFAULT # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (N) \ { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } \ else \ { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ while (0) #endif #define YYRHSLOC(Rhs, K) ((Rhs)[K]) /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (0) /* YYLOCATION_PRINT -- Print the location on the stream. This macro was not mandated originally: define only if we know we won't break user code: when these are the locations we know. */ # ifndef YYLOCATION_PRINT # if defined YY_LOCATION_PRINT /* Temporary convenience wrapper in case some people defined the undocumented and private YY_LOCATION_PRINT macros. */ # define YYLOCATION_PRINT(File, Loc, p) YY_LOCATION_PRINT(File, *(Loc), p) # elif defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL /* Print *YYLOCP on YYO. Private, do not rely on its existence. */ YY_ATTRIBUTE_UNUSED static int yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) { int res = 0; int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; if (0 <= yylocp->first_line) { res += YYFPRINTF (yyo, "%d", yylocp->first_line); if (0 <= yylocp->first_column) res += YYFPRINTF (yyo, ".%d", yylocp->first_column); } if (0 <= yylocp->last_line) { if (yylocp->first_line < yylocp->last_line) { res += YYFPRINTF (yyo, "-%d", yylocp->last_line); if (0 <= end_col) res += YYFPRINTF (yyo, ".%d", end_col); } else if (0 <= end_col && yylocp->first_column < end_col) res += YYFPRINTF (yyo, "-%d", end_col); } return res; } # define YYLOCATION_PRINT yy_location_print_ /* Temporary convenience wrapper in case some people defined the undocumented and private YY_LOCATION_PRINT macros. */ # define YY_LOCATION_PRINT(File, Loc, p) YYLOCATION_PRINT(File, &(Loc), p) # else # define YYLOCATION_PRINT(File, Loc, p) ((void) 0) /* Temporary convenience wrapper in case some people defined the undocumented and private YY_LOCATION_PRINT macros. */ # define YY_LOCATION_PRINT YYLOCATION_PRINT # endif # endif /* !defined YYLOCATION_PRINT */ # define YY_SYMBOL_PRINT(Title, Kind, Value, Location, p) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Kind, Value, Location, p); \ YYFPRINTF (stderr, "\n"); \ } \ } while (0) /*-----------------------------------. | Print this symbol's value on YYO. | `-----------------------------------*/ static void yy_symbol_value_print (FILE *yyo, yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, parser_state *p) { FILE *yyoutput = yyo; YY_USE (yyoutput); YY_USE (yylocationp); YY_USE (p); if (!yyvaluep) return; YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN switch (yykind) { default: break; } YY_IGNORE_MAYBE_UNINITIALIZED_END } /*---------------------------. | Print this symbol on YYO. | `---------------------------*/ static void yy_symbol_print (FILE *yyo, yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, parser_state *p) { YYFPRINTF (yyo, "%s %s (", yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); YYLOCATION_PRINT (yyo, yylocationp, p); YYFPRINTF (yyo, ": "); yy_symbol_value_print (yyo, yykind, yyvaluep, yylocationp, p); YYFPRINTF (yyo, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ static void yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop, parser_state *p) { YYFPRINTF (stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) { int yybot = *yybottom; YYFPRINTF (stderr, " %d", yybot); } YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top, p) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top), p); \ } while (0) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ static void yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, parser_state *p) { int yylno = yyrline[yyrule]; int yynrhs = yyr2[yyrule]; int yyi; YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), &yyvsp[(yyi + 1) - (yynrhs)], &(yylsp[(yyi + 1) - (yynrhs)]), p); YYFPRINTF (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule, p) \ do { \ if (yydebug) \ yy_reduce_print (yyssp, yyvsp, yylsp, Rule, p); \ } while (0) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ #ifndef yydebug int yydebug; #endif #else /* !YYDEBUG */ # define YYDPRINTF(Args) ((void) 0) # define YY_SYMBOL_PRINT(Title, Kind, Value, Location, p) # define YY_STACK_PRINT(Bottom, Top, p) # define YY_REDUCE_PRINT(Rule, p) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif /* Context of a parse error. */ typedef struct { yy_state_t *yyssp; yysymbol_kind_t yytoken; YYLTYPE *yylloc; } yypcontext_t; /* Put in YYARG at most YYARGN of the expected tokens given the current YYCTX, and return the number of tokens stored in YYARG. If YYARG is null, return the number of expected tokens (guaranteed to be less than YYNTOKENS). Return YYENOMEM on memory exhaustion. Return 0 if there are more than YYARGN expected tokens, yet fill YYARG up to YYARGN. */ static int yypcontext_expected_tokens (const yypcontext_t *yyctx, yysymbol_kind_t yyarg[], int yyargn) { /* Actual size of YYARG. */ int yycount = 0; int yyn = yypact[+*yyctx->yyssp]; if (!yypact_value_is_default (yyn)) { /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. In other words, skip the first -YYN actions for this state because they are default actions. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yyx; for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYSYMBOL_YYerror && !yytable_value_is_error (yytable[yyx + yyn])) { if (!yyarg) ++yycount; else if (yycount == yyargn) return 0; else yyarg[yycount++] = YY_CAST (yysymbol_kind_t, yyx); } } if (yyarg && yycount == 0 && 0 < yyargn) yyarg[0] = YYSYMBOL_YYEMPTY; return yycount; } #ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) # else /* Return the length of YYSTR. */ static YYPTRDIFF_T yystrlen (const char *yystr) { YYPTRDIFF_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif #endif #ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ static char * yystpcpy (char *yydest, const char *yysrc) { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif #endif #ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYPTRDIFF_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYPTRDIFF_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; else goto append; append: default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (yyres) return yystpcpy (yyres, yystr) - yyres; else return yystrlen (yystr); } #endif static int yy_syntax_error_arguments (const yypcontext_t *yyctx, yysymbol_kind_t yyarg[], int yyargn) { /* Actual size of YYARG. */ int yycount = 0; /* There are many possibilities here to consider: - If this state is a consistent state with a default action, then the only way this function was invoked is if the default action is an error action. In that case, don't check for expected tokens because there are none. - The only way there can be no lookahead present (in yychar) is if this state is a consistent state with a default action. Thus, detecting the absence of a lookahead is sufficient to determine that there is no unexpected or expected token to report. In that case, just report a simple "syntax error". - Don't assume there isn't a lookahead just because this state is a consistent state with a default action. There might have been a previous inconsistent state, consistent state with a non-default action, or user semantic action that manipulated yychar. - Of course, the expected token list depends on states to have correct lookahead information, and it depends on the parser not to perform extra reductions after fetching a lookahead from the scanner and before detecting a syntax error. Thus, state merging (from LALR or IELR) and default reductions corrupt the expected token list. However, the list is correct for canonical LR with one exception: it will still contain any token that will not be accepted due to an error action in a later state. */ if (yyctx->yytoken != YYSYMBOL_YYEMPTY) { int yyn; if (yyarg) yyarg[yycount] = yyctx->yytoken; ++yycount; yyn = yypcontext_expected_tokens (yyctx, yyarg ? yyarg + 1 : yyarg, yyargn - 1); if (yyn == YYENOMEM) return YYENOMEM; else yycount += yyn; } return yycount; } /* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message about the unexpected token YYTOKEN for the state stack whose top is YYSSP. Return 0 if *YYMSG was successfully written. Return -1 if *YYMSG is not large enough to hold the message. In that case, also set *YYMSG_ALLOC to the required number of bytes. Return YYENOMEM if the required number of bytes is too large to store. */ static int yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, const yypcontext_t *yyctx, parser_state *p) { enum { YYARGS_MAX = 5 }; /* Internationalized format string. */ const char *yyformat = YY_NULLPTR; /* Arguments of yyformat: reported tokens (one for the "unexpected", one per "expected"). */ yysymbol_kind_t yyarg[YYARGS_MAX]; /* Cumulated lengths of YYARG. */ YYPTRDIFF_T yysize = 0; /* Actual size of YYARG. */ int yycount = yy_syntax_error_arguments (yyctx, yyarg, YYARGS_MAX); if (yycount == YYENOMEM) return YYENOMEM; switch (yycount) { #define YYCASE_(N, S) \ case N: \ yyformat = S; \ break default: /* Avoid compiler warnings. */ YYCASE_(0, YY_("syntax error")); YYCASE_(1, YY_("syntax error, unexpected %s")); YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); #undef YYCASE_ } /* Compute error message size. Don't count the "%s"s, but reserve room for the terminator. */ yysize = yystrlen (yyformat) - 2 * yycount + 1; { int yyi; for (yyi = 0; yyi < yycount; ++yyi) { YYPTRDIFF_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyarg[yyi]]); if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) yysize = yysize1; else return YYENOMEM; } } if (*yymsg_alloc < yysize) { *yymsg_alloc = 2 * yysize; if (! (yysize <= *yymsg_alloc && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; return -1; } /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ { char *yyp = *yymsg; int yyi = 0; while ((*yyp = *yyformat) != '\0') if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yytname[yyarg[yyi++]]); yyformat += 2; } else { ++yyp; ++yyformat; } } return 0; } /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ static void yydestruct (const char *yymsg, yysymbol_kind_t yykind, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, parser_state *p) { YY_USE (yyvaluep); YY_USE (yylocationp); YY_USE (p); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp, p); YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN switch (yykind) { default: break; } YY_IGNORE_MAYBE_UNINITIALIZED_END } /*----------. | yyparse. | `----------*/ int yyparse (parser_state *p) { /* Lookahead token kind. */ int yychar; /* The semantic value of the lookahead symbol. */ /* Default value used for initialization, for pacifying older GCCs or non-GCC compilers. */ #ifdef __cplusplus static const YYSTYPE yyval_default = {}; (void) yyval_default; #else YY_INITIAL_VALUE (static const YYSTYPE yyval_default;) #endif YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); /* Location data for the lookahead symbol. */ static const YYLTYPE yyloc_default # if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL = { 1, 1, 1, 1 } # endif ; YYLTYPE yylloc = yyloc_default; /* Number of syntax errors so far. */ int yynerrs = 0; YY_USE (yynerrs); /* Silence compiler warning. */ yy_state_fast_t yystate = 0; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus = 0; /* Refer to the stacks through separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* Their size. */ YYPTRDIFF_T yystacksize = YYINITDEPTH; /* The state stack: array, bottom, top. */ yy_state_t yyssa[YYINITDEPTH]; yy_state_t *yyss = yyssa; yy_state_t *yyssp = yyss; /* The semantic value stack: array, bottom, top. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs = yyvsa; YYSTYPE *yyvsp = yyvs; /* The location stack: array, bottom, top. */ YYLTYPE yylsa[YYINITDEPTH]; YYLTYPE *yyls = yylsa; YYLTYPE *yylsp = yyls; int yyn; /* The return value of yyparse. */ int yyresult; /* Lookahead symbol kind. */ yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; YYLTYPE yyloc; /* The locations where the error started and ended. */ YYLTYPE yyerror_range[3]; /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; YYDPRINTF ((stderr, "Starting parse\n")); yychar = YYEMPTY; /* Cause a token to be read. */ #line 6528 "mrbgems/mruby-compiler/core/y.tab.c" yylsp[0] = yylloc; goto yysetstate; /*------------------------------------------------------------. | yynewstate -- push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; /*--------------------------------------------------------------------. | yysetstate -- set current state (the top of the stack) to yystate. | `--------------------------------------------------------------------*/ yysetstate: YYDPRINTF ((stderr, "Entering state %d\n", yystate)); YY_ASSERT (0 <= yystate && yystate < YYNSTATES); YY_IGNORE_USELESS_CAST_BEGIN *yyssp = YY_CAST (yy_state_t, yystate); YY_IGNORE_USELESS_CAST_END YY_STACK_PRINT (yyss, yyssp, p); if (yyss + yystacksize - 1 <= yyssp) #if !defined yyoverflow && !defined YYSTACK_RELOCATE YYNOMEM; #else { /* Get the current used size of the three stacks, in elements. */ YYPTRDIFF_T yysize = yyssp - yyss + 1; # if defined yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ yy_state_t *yyss1 = yyss; YYSTYPE *yyvs1 = yyvs; YYLTYPE *yyls1 = yyls; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * YYSIZEOF (*yyssp), &yyvs1, yysize * YYSIZEOF (*yyvsp), &yyls1, yysize * YYSIZEOF (*yylsp), &yystacksize); yyss = yyss1; yyvs = yyvs1; yyls = yyls1; } # else /* defined YYSTACK_RELOCATE */ /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) YYNOMEM; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yy_state_t *yyss1 = yyss; union yyalloc *yyptr = YY_CAST (union yyalloc *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); if (! yyptr) YYNOMEM; YYSTACK_RELOCATE (yyss_alloc, yyss); YYSTACK_RELOCATE (yyvs_alloc, yyvs); YYSTACK_RELOCATE (yyls_alloc, yyls); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; yylsp = yyls + yysize - 1; YY_IGNORE_USELESS_CAST_BEGIN YYDPRINTF ((stderr, "Stack size increased to %ld\n", YY_CAST (long, yystacksize))); YY_IGNORE_USELESS_CAST_END if (yyss + yystacksize - 1 <= yyssp) YYABORT; } #endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ if (yystate == YYFINAL) YYACCEPT; goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a lookahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; if (yypact_value_is_default (yyn)) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token\n")); yychar = yylex (&yylval, &yylloc, p); } if (yychar <= YYEOF) { yychar = YYEOF; yytoken = YYSYMBOL_YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else if (yychar == YYerror) { /* The scanner already issued an error message, process directly to error recovery. But do not keep the error token as lookahead, it is too special and may lead us to an endless loop in error recovery. */ yychar = YYUNDEF; yytoken = YYSYMBOL_YYerror; yyerror_range[1] = yylloc; goto yyerrlab1; } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc, p); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yytable_value_is_error (yyn)) goto yyerrlab; yyn = -yyn; goto yyreduce; } /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc, p); yystate = yyn; YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END *++yylsp = yylloc; /* Discard the shifted token. */ yychar = YYEMPTY; goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: '$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; /* Default location. */ YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); yyerror_range[1] = yyloc; YY_REDUCE_PRINT (yyn, p); switch (yyn) { case 2: /* $@1: %empty */ #line 1665 "mrbgems/mruby-compiler/core/parse.y" { p->lstate = EXPR_BEG; if (!p->locals) p->locals = cons(0,0); } #line 6746 "mrbgems/mruby-compiler/core/y.tab.c" break; case 3: /* program: $@1 top_compstmt */ #line 1670 "mrbgems/mruby-compiler/core/parse.y" { p->tree = new_scope(p, (yyvsp[0].nd)); NODE_LINENO(p->tree, (yyvsp[0].nd)); } #line 6755 "mrbgems/mruby-compiler/core/y.tab.c" break; case 4: /* top_compstmt: top_stmts opt_terms */ #line 1677 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-1].nd); } #line 6763 "mrbgems/mruby-compiler/core/y.tab.c" break; case 5: /* top_stmts: none */ #line 1683 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_begin(p, 0); } #line 6771 "mrbgems/mruby-compiler/core/y.tab.c" break; case 6: /* top_stmts: top_stmt */ #line 1687 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_begin(p, (yyvsp[0].nd)); NODE_LINENO((yyval.nd), (yyvsp[0].nd)); } #line 6780 "mrbgems/mruby-compiler/core/y.tab.c" break; case 7: /* top_stmts: top_stmts terms top_stmt */ #line 1692 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = push((yyvsp[-2].nd), newline_node((yyvsp[0].nd))); } #line 6788 "mrbgems/mruby-compiler/core/y.tab.c" break; case 8: /* top_stmts: error top_stmt */ #line 1696 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_begin(p, 0); } #line 6796 "mrbgems/mruby-compiler/core/y.tab.c" break; case 10: /* @2: %empty */ #line 1703 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = local_switch(p); nvars_block(p); } #line 6805 "mrbgems/mruby-compiler/core/y.tab.c" break; case 11: /* top_stmt: "'BEGIN'" @2 '{' top_compstmt '}' */ #line 1708 "mrbgems/mruby-compiler/core/parse.y" { yyerror(&(yylsp[-4]), p, "BEGIN not supported"); local_resume(p, (yyvsp[-3].nd)); nvars_unnest(p); (yyval.nd) = 0; } #line 6816 "mrbgems/mruby-compiler/core/y.tab.c" break; case 12: /* bodystmt: compstmt opt_rescue opt_else opt_ensure */ #line 1720 "mrbgems/mruby-compiler/core/parse.y" { if ((yyvsp[-2].nd)) { (yyval.nd) = new_rescue(p, (yyvsp[-3].nd), (yyvsp[-2].nd), (yyvsp[-1].nd)); NODE_LINENO((yyval.nd), (yyvsp[-3].nd)); } else if ((yyvsp[-1].nd)) { yywarning(p, "else without rescue is useless"); (yyval.nd) = push((yyvsp[-3].nd), (yyvsp[-1].nd)); } else { (yyval.nd) = (yyvsp[-3].nd); } if ((yyvsp[0].nd)) { if ((yyval.nd)) { (yyval.nd) = new_ensure(p, (yyval.nd), (yyvsp[0].nd)); } else { (yyval.nd) = push((yyvsp[0].nd), new_nil(p)); } } } #line 6842 "mrbgems/mruby-compiler/core/y.tab.c" break; case 13: /* compstmt: stmts opt_terms */ #line 1744 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-1].nd); } #line 6850 "mrbgems/mruby-compiler/core/y.tab.c" break; case 14: /* stmts: none */ #line 1750 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_begin(p, 0); } #line 6858 "mrbgems/mruby-compiler/core/y.tab.c" break; case 15: /* stmts: stmt */ #line 1754 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_begin(p, (yyvsp[0].nd)); NODE_LINENO((yyval.nd), (yyvsp[0].nd)); } #line 6867 "mrbgems/mruby-compiler/core/y.tab.c" break; case 16: /* stmts: stmts terms stmt */ #line 1759 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = push((yyvsp[-2].nd), newline_node((yyvsp[0].nd))); } #line 6875 "mrbgems/mruby-compiler/core/y.tab.c" break; case 17: /* stmts: error stmt */ #line 1763 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_begin(p, (yyvsp[0].nd)); } #line 6883 "mrbgems/mruby-compiler/core/y.tab.c" break; case 18: /* $@3: %empty */ #line 1768 "mrbgems/mruby-compiler/core/parse.y" {p->lstate = EXPR_FNAME;} #line 6889 "mrbgems/mruby-compiler/core/y.tab.c" break; case 19: /* stmt: "'alias'" fsym $@3 fsym */ #line 1769 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_alias(p, (yyvsp[-2].id), (yyvsp[0].id)); } #line 6897 "mrbgems/mruby-compiler/core/y.tab.c" break; case 20: /* stmt: "'undef'" undef_list */ #line 1773 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[0].nd); } #line 6905 "mrbgems/mruby-compiler/core/y.tab.c" break; case 21: /* stmt: stmt "'if' modifier" expr_value */ #line 1777 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_if(p, cond((yyvsp[0].nd)), (yyvsp[-2].nd), 0); } #line 6913 "mrbgems/mruby-compiler/core/y.tab.c" break; case 22: /* stmt: stmt "'unless' modifier" expr_value */ #line 1781 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_unless(p, cond((yyvsp[0].nd)), (yyvsp[-2].nd), 0); } #line 6921 "mrbgems/mruby-compiler/core/y.tab.c" break; case 23: /* stmt: stmt "'while' modifier" expr_value */ #line 1785 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_while(p, cond((yyvsp[0].nd)), (yyvsp[-2].nd)); } #line 6929 "mrbgems/mruby-compiler/core/y.tab.c" break; case 24: /* stmt: stmt "'until' modifier" expr_value */ #line 1789 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_until(p, cond((yyvsp[0].nd)), (yyvsp[-2].nd)); } #line 6937 "mrbgems/mruby-compiler/core/y.tab.c" break; case 25: /* stmt: stmt "'rescue' modifier" stmt */ #line 1793 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_mod_rescue(p, (yyvsp[-2].nd), (yyvsp[0].nd)); } #line 6945 "mrbgems/mruby-compiler/core/y.tab.c" break; case 26: /* stmt: "'END'" '{' compstmt '}' */ #line 1797 "mrbgems/mruby-compiler/core/parse.y" { yyerror(&(yylsp[-3]), p, "END not supported"); (yyval.nd) = new_postexe(p, (yyvsp[-1].nd)); } #line 6954 "mrbgems/mruby-compiler/core/y.tab.c" break; case 28: /* stmt: mlhs '=' command_call */ #line 1803 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_masgn(p, (yyvsp[-2].nd), (yyvsp[0].nd)); } #line 6962 "mrbgems/mruby-compiler/core/y.tab.c" break; case 29: /* stmt: lhs '=' mrhs */ #line 1807 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_asgn(p, (yyvsp[-2].nd), new_array(p, (yyvsp[0].nd))); } #line 6970 "mrbgems/mruby-compiler/core/y.tab.c" break; case 30: /* stmt: mlhs '=' arg */ #line 1811 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_masgn(p, (yyvsp[-2].nd), (yyvsp[0].nd)); } #line 6978 "mrbgems/mruby-compiler/core/y.tab.c" break; case 31: /* stmt: mlhs '=' mrhs */ #line 1815 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_masgn(p, (yyvsp[-2].nd), new_array(p, (yyvsp[0].nd))); } #line 6986 "mrbgems/mruby-compiler/core/y.tab.c" break; case 32: /* stmt: arg "=>" "local variable or method" */ #line 1819 "mrbgems/mruby-compiler/core/parse.y" { node *lhs = new_lvar(p, (yyvsp[0].id)); assignable(p, lhs); (yyval.nd) = new_asgn(p, lhs, (yyvsp[-2].nd)); } #line 6996 "mrbgems/mruby-compiler/core/y.tab.c" break; case 34: /* command_asgn: lhs '=' command_rhs */ #line 1828 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_asgn(p, (yyvsp[-2].nd), (yyvsp[0].nd)); } #line 7004 "mrbgems/mruby-compiler/core/y.tab.c" break; case 35: /* command_asgn: var_lhs tOP_ASGN command_rhs */ #line 1832 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_op_asgn(p, (yyvsp[-2].nd), (yyvsp[-1].id), (yyvsp[0].nd)); } #line 7012 "mrbgems/mruby-compiler/core/y.tab.c" break; case 36: /* command_asgn: primary_value '[' opt_call_args ']' tOP_ASGN command_rhs */ #line 1836 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_op_asgn(p, new_call(p, (yyvsp[-5].nd), intern_op(aref), (yyvsp[-3].nd), '.'), (yyvsp[-1].id), (yyvsp[0].nd)); } #line 7020 "mrbgems/mruby-compiler/core/y.tab.c" break; case 37: /* command_asgn: primary_value call_op "local variable or method" tOP_ASGN command_rhs */ #line 1840 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_op_asgn(p, new_call(p, (yyvsp[-4].nd), (yyvsp[-2].id), 0, (yyvsp[-3].num)), (yyvsp[-1].id), (yyvsp[0].nd)); } #line 7028 "mrbgems/mruby-compiler/core/y.tab.c" break; case 38: /* command_asgn: primary_value call_op "constant" tOP_ASGN command_rhs */ #line 1844 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_op_asgn(p, new_call(p, (yyvsp[-4].nd), (yyvsp[-2].id), 0, (yyvsp[-3].num)), (yyvsp[-1].id), (yyvsp[0].nd)); } #line 7036 "mrbgems/mruby-compiler/core/y.tab.c" break; case 39: /* command_asgn: primary_value "::" "constant" tOP_ASGN command_call */ #line 1848 "mrbgems/mruby-compiler/core/parse.y" { yyerror(&(yylsp[-4]), p, "constant re-assignment"); (yyval.nd) = 0; } #line 7045 "mrbgems/mruby-compiler/core/y.tab.c" break; case 40: /* command_asgn: primary_value "::" "local variable or method" tOP_ASGN command_rhs */ #line 1853 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_op_asgn(p, new_call(p, (yyvsp[-4].nd), (yyvsp[-2].id), 0, tCOLON2), (yyvsp[-1].id), (yyvsp[0].nd)); } #line 7053 "mrbgems/mruby-compiler/core/y.tab.c" break; case 41: /* command_asgn: defn_head f_opt_arglist_paren '=' command */ #line 1857 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-3].nd); endless_method_name(p, (yyvsp[-3].nd)); void_expr_error(p, (yyvsp[0].nd)); defn_setup(p, (yyval.nd), (yyvsp[-2].nd), (yyvsp[0].nd)); nvars_unnest(p); p->in_def--; } #line 7066 "mrbgems/mruby-compiler/core/y.tab.c" break; case 42: /* command_asgn: defn_head f_opt_arglist_paren '=' command "'rescue' modifier" arg */ #line 1866 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-5].nd); endless_method_name(p, (yyvsp[-5].nd)); void_expr_error(p, (yyvsp[-2].nd)); defn_setup(p, (yyval.nd), (yyvsp[-4].nd), new_mod_rescue(p, (yyvsp[-2].nd), (yyvsp[0].nd))); nvars_unnest(p); p->in_def--; } #line 7079 "mrbgems/mruby-compiler/core/y.tab.c" break; case 43: /* command_asgn: defs_head f_opt_arglist_paren '=' command */ #line 1875 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-3].nd); void_expr_error(p, (yyvsp[0].nd)); defs_setup(p, (yyval.nd), (yyvsp[-2].nd), (yyvsp[0].nd)); nvars_unnest(p); p->in_def--; p->in_single--; } #line 7092 "mrbgems/mruby-compiler/core/y.tab.c" break; case 44: /* command_asgn: defs_head f_opt_arglist_paren '=' command "'rescue' modifier" arg */ #line 1884 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-5].nd); void_expr_error(p, (yyvsp[-2].nd)); defs_setup(p, (yyval.nd), (yyvsp[-4].nd), new_mod_rescue(p, (yyvsp[-2].nd), (yyvsp[0].nd))); nvars_unnest(p); p->in_def--; p->in_single--; } #line 7105 "mrbgems/mruby-compiler/core/y.tab.c" break; case 45: /* command_asgn: backref tOP_ASGN command_rhs */ #line 1893 "mrbgems/mruby-compiler/core/parse.y" { backref_error(p, (yyvsp[-2].nd)); (yyval.nd) = new_begin(p, 0); } #line 7114 "mrbgems/mruby-compiler/core/y.tab.c" break; case 47: /* command_rhs: command_call "'rescue' modifier" stmt */ #line 1901 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_mod_rescue(p, (yyvsp[-2].nd), (yyvsp[0].nd)); } #line 7122 "mrbgems/mruby-compiler/core/y.tab.c" break; case 50: /* expr: expr "'and'" expr */ #line 1910 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_and(p, (yyvsp[-2].nd), (yyvsp[0].nd)); } #line 7130 "mrbgems/mruby-compiler/core/y.tab.c" break; case 51: /* expr: expr "'or'" expr */ #line 1914 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_or(p, (yyvsp[-2].nd), (yyvsp[0].nd)); } #line 7138 "mrbgems/mruby-compiler/core/y.tab.c" break; case 52: /* expr: "'not'" opt_nl expr */ #line 1918 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_uni_op(p, cond((yyvsp[0].nd)), "!"); } #line 7146 "mrbgems/mruby-compiler/core/y.tab.c" break; case 53: /* expr: '!' command_call */ #line 1922 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_uni_op(p, cond((yyvsp[0].nd)), "!"); } #line 7154 "mrbgems/mruby-compiler/core/y.tab.c" break; case 55: /* defn_head: "'def'" fname */ #line 1930 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_def(p, (yyvsp[0].id), nint(p->cmdarg_stack), local_switch(p)); p->cmdarg_stack = 0; p->in_def++; nvars_block(p); } #line 7165 "mrbgems/mruby-compiler/core/y.tab.c" break; case 56: /* $@4: %empty */ #line 1939 "mrbgems/mruby-compiler/core/parse.y" { p->lstate = EXPR_FNAME; } #line 7173 "mrbgems/mruby-compiler/core/y.tab.c" break; case 57: /* defs_head: "'def'" singleton dot_or_colon $@4 fname */ #line 1943 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_sdef(p, (yyvsp[-3].nd), (yyvsp[0].id), nint(p->cmdarg_stack), local_switch(p)); p->cmdarg_stack = 0; p->in_def++; p->in_single++; nvars_block(p); p->lstate = EXPR_ENDFN; /* force for args */ } #line 7186 "mrbgems/mruby-compiler/core/y.tab.c" break; case 58: /* expr_value: expr */ #line 1954 "mrbgems/mruby-compiler/core/parse.y" { if (!(yyvsp[0].nd)) (yyval.nd) = new_nil(p); else { (yyval.nd) = (yyvsp[0].nd); } } #line 7197 "mrbgems/mruby-compiler/core/y.tab.c" break; case 62: /* block_command: block_call call_op2 operation2 command_args */ #line 1968 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_call(p, (yyvsp[-3].nd), (yyvsp[-1].id), (yyvsp[0].nd), (yyvsp[-2].num)); } #line 7205 "mrbgems/mruby-compiler/core/y.tab.c" break; case 63: /* $@5: %empty */ #line 1974 "mrbgems/mruby-compiler/core/parse.y" { local_nest(p); nvars_nest(p); } #line 7214 "mrbgems/mruby-compiler/core/y.tab.c" break; case 64: /* cmd_brace_block: "{" $@5 opt_block_param compstmt '}' */ #line 1981 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_block(p, (yyvsp[-2].nd), (yyvsp[-1].nd)); local_unnest(p); nvars_unnest(p); } #line 7224 "mrbgems/mruby-compiler/core/y.tab.c" break; case 65: /* command: operation command_args */ #line 1989 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_fcall(p, (yyvsp[-1].id), (yyvsp[0].nd)); } #line 7232 "mrbgems/mruby-compiler/core/y.tab.c" break; case 66: /* command: operation command_args cmd_brace_block */ #line 1993 "mrbgems/mruby-compiler/core/parse.y" { args_with_block(p, (yyvsp[-1].nd), (yyvsp[0].nd)); (yyval.nd) = new_fcall(p, (yyvsp[-2].id), (yyvsp[-1].nd)); } #line 7241 "mrbgems/mruby-compiler/core/y.tab.c" break; case 67: /* command: primary_value call_op operation2 command_args */ #line 1998 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_call(p, (yyvsp[-3].nd), (yyvsp[-1].id), (yyvsp[0].nd), (yyvsp[-2].num)); } #line 7249 "mrbgems/mruby-compiler/core/y.tab.c" break; case 68: /* command: primary_value call_op operation2 command_args cmd_brace_block */ #line 2002 "mrbgems/mruby-compiler/core/parse.y" { args_with_block(p, (yyvsp[-1].nd), (yyvsp[0].nd)); (yyval.nd) = new_call(p, (yyvsp[-4].nd), (yyvsp[-2].id), (yyvsp[-1].nd), (yyvsp[-3].num)); } #line 7258 "mrbgems/mruby-compiler/core/y.tab.c" break; case 69: /* command: primary_value "::" operation2 command_args */ #line 2007 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_call(p, (yyvsp[-3].nd), (yyvsp[-1].id), (yyvsp[0].nd), tCOLON2); } #line 7266 "mrbgems/mruby-compiler/core/y.tab.c" break; case 70: /* command: primary_value "::" operation2 command_args cmd_brace_block */ #line 2011 "mrbgems/mruby-compiler/core/parse.y" { args_with_block(p, (yyvsp[-1].nd), (yyvsp[0].nd)); (yyval.nd) = new_call(p, (yyvsp[-4].nd), (yyvsp[-2].id), (yyvsp[-1].nd), tCOLON2); } #line 7275 "mrbgems/mruby-compiler/core/y.tab.c" break; case 71: /* command: "'super'" command_args */ #line 2016 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_super(p, (yyvsp[0].nd)); } #line 7283 "mrbgems/mruby-compiler/core/y.tab.c" break; case 72: /* command: "'yield'" command_args */ #line 2020 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_yield(p, (yyvsp[0].nd)); } #line 7291 "mrbgems/mruby-compiler/core/y.tab.c" break; case 73: /* command: "'return'" call_args */ #line 2024 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_return(p, ret_args(p, (yyvsp[0].nd))); } #line 7299 "mrbgems/mruby-compiler/core/y.tab.c" break; case 74: /* command: "'break'" call_args */ #line 2028 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_break(p, ret_args(p, (yyvsp[0].nd))); } #line 7307 "mrbgems/mruby-compiler/core/y.tab.c" break; case 75: /* command: "'next'" call_args */ #line 2032 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_next(p, ret_args(p, (yyvsp[0].nd))); } #line 7315 "mrbgems/mruby-compiler/core/y.tab.c" break; case 76: /* mlhs: mlhs_basic */ #line 2038 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[0].nd); } #line 7323 "mrbgems/mruby-compiler/core/y.tab.c" break; case 77: /* mlhs: tLPAREN mlhs_inner rparen */ #line 2042 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-1].nd); } #line 7331 "mrbgems/mruby-compiler/core/y.tab.c" break; case 79: /* mlhs_inner: tLPAREN mlhs_inner rparen */ #line 2049 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-1].nd); } #line 7339 "mrbgems/mruby-compiler/core/y.tab.c" break; case 80: /* mlhs_basic: mlhs_list */ #line 2055 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list1((yyvsp[0].nd)); } #line 7347 "mrbgems/mruby-compiler/core/y.tab.c" break; case 81: /* mlhs_basic: mlhs_list mlhs_item */ #line 2059 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list1(push((yyvsp[-1].nd),(yyvsp[0].nd))); } #line 7355 "mrbgems/mruby-compiler/core/y.tab.c" break; case 82: /* mlhs_basic: mlhs_list "*" mlhs_node */ #line 2063 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list2((yyvsp[-2].nd), (yyvsp[0].nd)); } #line 7363 "mrbgems/mruby-compiler/core/y.tab.c" break; case 83: /* mlhs_basic: mlhs_list "*" mlhs_node ',' mlhs_post */ #line 2067 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list3((yyvsp[-4].nd), (yyvsp[-2].nd), (yyvsp[0].nd)); } #line 7371 "mrbgems/mruby-compiler/core/y.tab.c" break; case 84: /* mlhs_basic: mlhs_list "*" */ #line 2071 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list2((yyvsp[-1].nd), new_nil(p)); } #line 7379 "mrbgems/mruby-compiler/core/y.tab.c" break; case 85: /* mlhs_basic: mlhs_list "*" ',' mlhs_post */ #line 2075 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list3((yyvsp[-3].nd), new_nil(p), (yyvsp[0].nd)); } #line 7387 "mrbgems/mruby-compiler/core/y.tab.c" break; case 86: /* mlhs_basic: "*" mlhs_node */ #line 2079 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list2(0, (yyvsp[0].nd)); } #line 7395 "mrbgems/mruby-compiler/core/y.tab.c" break; case 87: /* mlhs_basic: "*" mlhs_node ',' mlhs_post */ #line 2083 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list3(0, (yyvsp[-2].nd), (yyvsp[0].nd)); } #line 7403 "mrbgems/mruby-compiler/core/y.tab.c" break; case 88: /* mlhs_basic: "*" */ #line 2087 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list2(0, new_nil(p)); } #line 7411 "mrbgems/mruby-compiler/core/y.tab.c" break; case 89: /* mlhs_basic: "*" ',' mlhs_post */ #line 2091 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list3(0, new_nil(p), (yyvsp[0].nd)); } #line 7419 "mrbgems/mruby-compiler/core/y.tab.c" break; case 91: /* mlhs_item: tLPAREN mlhs_inner rparen */ #line 2098 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_masgn(p, (yyvsp[-1].nd), NULL); } #line 7427 "mrbgems/mruby-compiler/core/y.tab.c" break; case 92: /* mlhs_list: mlhs_item ',' */ #line 2104 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list1((yyvsp[-1].nd)); } #line 7435 "mrbgems/mruby-compiler/core/y.tab.c" break; case 93: /* mlhs_list: mlhs_list mlhs_item ',' */ #line 2108 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = push((yyvsp[-2].nd), (yyvsp[-1].nd)); } #line 7443 "mrbgems/mruby-compiler/core/y.tab.c" break; case 94: /* mlhs_post: mlhs_item */ #line 2114 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list1((yyvsp[0].nd)); } #line 7451 "mrbgems/mruby-compiler/core/y.tab.c" break; case 95: /* mlhs_post: mlhs_list mlhs_item */ #line 2118 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = push((yyvsp[-1].nd), (yyvsp[0].nd)); } #line 7459 "mrbgems/mruby-compiler/core/y.tab.c" break; case 96: /* mlhs_node: variable */ #line 2124 "mrbgems/mruby-compiler/core/parse.y" { assignable(p, (yyvsp[0].nd)); } #line 7467 "mrbgems/mruby-compiler/core/y.tab.c" break; case 97: /* mlhs_node: primary_value '[' opt_call_args ']' */ #line 2128 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_call(p, (yyvsp[-3].nd), intern_op(aref), (yyvsp[-1].nd), '.'); } #line 7475 "mrbgems/mruby-compiler/core/y.tab.c" break; case 98: /* mlhs_node: primary_value call_op "local variable or method" */ #line 2132 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_call(p, (yyvsp[-2].nd), (yyvsp[0].id), 0, (yyvsp[-1].num)); } #line 7483 "mrbgems/mruby-compiler/core/y.tab.c" break; case 99: /* mlhs_node: primary_value "::" "local variable or method" */ #line 2136 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_call(p, (yyvsp[-2].nd), (yyvsp[0].id), 0, tCOLON2); } #line 7491 "mrbgems/mruby-compiler/core/y.tab.c" break; case 100: /* mlhs_node: primary_value call_op "constant" */ #line 2140 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_call(p, (yyvsp[-2].nd), (yyvsp[0].id), 0, (yyvsp[-1].num)); } #line 7499 "mrbgems/mruby-compiler/core/y.tab.c" break; case 101: /* mlhs_node: primary_value "::" "constant" */ #line 2144 "mrbgems/mruby-compiler/core/parse.y" { if (p->in_def || p->in_single) yyerror(&(yylsp[-2]), p, "dynamic constant assignment"); (yyval.nd) = new_colon2(p, (yyvsp[-2].nd), (yyvsp[0].id)); } #line 7509 "mrbgems/mruby-compiler/core/y.tab.c" break; case 102: /* mlhs_node: tCOLON3 "constant" */ #line 2150 "mrbgems/mruby-compiler/core/parse.y" { if (p->in_def || p->in_single) yyerror(&(yylsp[-1]), p, "dynamic constant assignment"); (yyval.nd) = new_colon3(p, (yyvsp[0].id)); } #line 7519 "mrbgems/mruby-compiler/core/y.tab.c" break; case 103: /* mlhs_node: backref */ #line 2156 "mrbgems/mruby-compiler/core/parse.y" { backref_error(p, (yyvsp[0].nd)); (yyval.nd) = 0; } #line 7528 "mrbgems/mruby-compiler/core/y.tab.c" break; case 104: /* lhs: variable */ #line 2163 "mrbgems/mruby-compiler/core/parse.y" { assignable(p, (yyvsp[0].nd)); } #line 7536 "mrbgems/mruby-compiler/core/y.tab.c" break; case 105: /* lhs: primary_value '[' opt_call_args ']' */ #line 2167 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_call(p, (yyvsp[-3].nd), intern_op(aref), (yyvsp[-1].nd), '.'); } #line 7544 "mrbgems/mruby-compiler/core/y.tab.c" break; case 106: /* lhs: primary_value call_op "local variable or method" */ #line 2171 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_call(p, (yyvsp[-2].nd), (yyvsp[0].id), 0, (yyvsp[-1].num)); } #line 7552 "mrbgems/mruby-compiler/core/y.tab.c" break; case 107: /* lhs: primary_value "::" "local variable or method" */ #line 2175 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_call(p, (yyvsp[-2].nd), (yyvsp[0].id), 0, tCOLON2); } #line 7560 "mrbgems/mruby-compiler/core/y.tab.c" break; case 108: /* lhs: primary_value call_op "constant" */ #line 2179 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_call(p, (yyvsp[-2].nd), (yyvsp[0].id), 0, (yyvsp[-1].num)); } #line 7568 "mrbgems/mruby-compiler/core/y.tab.c" break; case 109: /* lhs: primary_value "::" "constant" */ #line 2183 "mrbgems/mruby-compiler/core/parse.y" { if (p->in_def || p->in_single) yyerror(&(yylsp[-2]), p, "dynamic constant assignment"); (yyval.nd) = new_colon2(p, (yyvsp[-2].nd), (yyvsp[0].id)); } #line 7578 "mrbgems/mruby-compiler/core/y.tab.c" break; case 110: /* lhs: tCOLON3 "constant" */ #line 2189 "mrbgems/mruby-compiler/core/parse.y" { if (p->in_def || p->in_single) yyerror(&(yylsp[-1]), p, "dynamic constant assignment"); (yyval.nd) = new_colon3(p, (yyvsp[0].id)); } #line 7588 "mrbgems/mruby-compiler/core/y.tab.c" break; case 111: /* lhs: backref */ #line 2195 "mrbgems/mruby-compiler/core/parse.y" { backref_error(p, (yyvsp[0].nd)); (yyval.nd) = 0; } #line 7597 "mrbgems/mruby-compiler/core/y.tab.c" break; case 112: /* lhs: "numbered parameter" */ #line 2200 "mrbgems/mruby-compiler/core/parse.y" { yyerror(&(yylsp[0]), p, "can't assign to numbered parameter"); } #line 7605 "mrbgems/mruby-compiler/core/y.tab.c" break; case 113: /* cname: "local variable or method" */ #line 2206 "mrbgems/mruby-compiler/core/parse.y" { yyerror(&(yylsp[0]), p, "class/module name must be CONSTANT"); } #line 7613 "mrbgems/mruby-compiler/core/y.tab.c" break; case 115: /* cpath: tCOLON3 cname */ #line 2213 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = cons(nint(1), nsym((yyvsp[0].id))); } #line 7621 "mrbgems/mruby-compiler/core/y.tab.c" break; case 116: /* cpath: cname */ #line 2217 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = cons(nint(0), nsym((yyvsp[0].id))); } #line 7629 "mrbgems/mruby-compiler/core/y.tab.c" break; case 117: /* cpath: primary_value "::" cname */ #line 2221 "mrbgems/mruby-compiler/core/parse.y" { void_expr_error(p, (yyvsp[-2].nd)); (yyval.nd) = cons((yyvsp[-2].nd), nsym((yyvsp[0].id))); } #line 7638 "mrbgems/mruby-compiler/core/y.tab.c" break; case 121: /* fname: op */ #line 2231 "mrbgems/mruby-compiler/core/parse.y" { p->lstate = EXPR_ENDFN; (yyval.id) = (yyvsp[0].id); } #line 7647 "mrbgems/mruby-compiler/core/y.tab.c" break; case 122: /* fname: reswords */ #line 2236 "mrbgems/mruby-compiler/core/parse.y" { p->lstate = EXPR_ENDFN; (yyval.id) = (yyvsp[0].id); } #line 7656 "mrbgems/mruby-compiler/core/y.tab.c" break; case 125: /* undef_list: fsym */ #line 2247 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_undef(p, (yyvsp[0].id)); } #line 7664 "mrbgems/mruby-compiler/core/y.tab.c" break; case 126: /* $@6: %empty */ #line 2250 "mrbgems/mruby-compiler/core/parse.y" {p->lstate = EXPR_FNAME;} #line 7670 "mrbgems/mruby-compiler/core/y.tab.c" break; case 127: /* undef_list: undef_list ',' $@6 fsym */ #line 2251 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = push((yyvsp[-3].nd), nsym((yyvsp[0].id))); } #line 7678 "mrbgems/mruby-compiler/core/y.tab.c" break; case 128: /* op: '|' */ #line 2256 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(or); } #line 7684 "mrbgems/mruby-compiler/core/y.tab.c" break; case 129: /* op: '^' */ #line 2257 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(xor); } #line 7690 "mrbgems/mruby-compiler/core/y.tab.c" break; case 130: /* op: '&' */ #line 2258 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(and); } #line 7696 "mrbgems/mruby-compiler/core/y.tab.c" break; case 131: /* op: "<=>" */ #line 2259 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(cmp); } #line 7702 "mrbgems/mruby-compiler/core/y.tab.c" break; case 132: /* op: "==" */ #line 2260 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(eq); } #line 7708 "mrbgems/mruby-compiler/core/y.tab.c" break; case 133: /* op: "===" */ #line 2261 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(eqq); } #line 7714 "mrbgems/mruby-compiler/core/y.tab.c" break; case 134: /* op: "=~" */ #line 2262 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(match); } #line 7720 "mrbgems/mruby-compiler/core/y.tab.c" break; case 135: /* op: "!~" */ #line 2263 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(nmatch); } #line 7726 "mrbgems/mruby-compiler/core/y.tab.c" break; case 136: /* op: '>' */ #line 2264 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(gt); } #line 7732 "mrbgems/mruby-compiler/core/y.tab.c" break; case 137: /* op: ">=" */ #line 2265 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(ge); } #line 7738 "mrbgems/mruby-compiler/core/y.tab.c" break; case 138: /* op: '<' */ #line 2266 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(lt); } #line 7744 "mrbgems/mruby-compiler/core/y.tab.c" break; case 139: /* op: "<=" */ #line 2267 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(le); } #line 7750 "mrbgems/mruby-compiler/core/y.tab.c" break; case 140: /* op: "!=" */ #line 2268 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(neq); } #line 7756 "mrbgems/mruby-compiler/core/y.tab.c" break; case 141: /* op: "<<" */ #line 2269 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(lshift); } #line 7762 "mrbgems/mruby-compiler/core/y.tab.c" break; case 142: /* op: ">>" */ #line 2270 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(rshift); } #line 7768 "mrbgems/mruby-compiler/core/y.tab.c" break; case 143: /* op: '+' */ #line 2271 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(add); } #line 7774 "mrbgems/mruby-compiler/core/y.tab.c" break; case 144: /* op: '-' */ #line 2272 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(sub); } #line 7780 "mrbgems/mruby-compiler/core/y.tab.c" break; case 145: /* op: '*' */ #line 2273 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(mul); } #line 7786 "mrbgems/mruby-compiler/core/y.tab.c" break; case 146: /* op: "*" */ #line 2274 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(mul); } #line 7792 "mrbgems/mruby-compiler/core/y.tab.c" break; case 147: /* op: '/' */ #line 2275 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(div); } #line 7798 "mrbgems/mruby-compiler/core/y.tab.c" break; case 148: /* op: '%' */ #line 2276 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(mod); } #line 7804 "mrbgems/mruby-compiler/core/y.tab.c" break; case 149: /* op: tPOW */ #line 2277 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(pow); } #line 7810 "mrbgems/mruby-compiler/core/y.tab.c" break; case 150: /* op: "**" */ #line 2278 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(pow); } #line 7816 "mrbgems/mruby-compiler/core/y.tab.c" break; case 151: /* op: '!' */ #line 2279 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(not); } #line 7822 "mrbgems/mruby-compiler/core/y.tab.c" break; case 152: /* op: '~' */ #line 2280 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(neg); } #line 7828 "mrbgems/mruby-compiler/core/y.tab.c" break; case 153: /* op: "unary plus" */ #line 2281 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(plus); } #line 7834 "mrbgems/mruby-compiler/core/y.tab.c" break; case 154: /* op: "unary minus" */ #line 2282 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(minus); } #line 7840 "mrbgems/mruby-compiler/core/y.tab.c" break; case 155: /* op: tAREF */ #line 2283 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(aref); } #line 7846 "mrbgems/mruby-compiler/core/y.tab.c" break; case 156: /* op: tASET */ #line 2284 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(aset); } #line 7852 "mrbgems/mruby-compiler/core/y.tab.c" break; case 157: /* op: '`' */ #line 2285 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(tick); } #line 7858 "mrbgems/mruby-compiler/core/y.tab.c" break; case 198: /* arg: lhs '=' arg_rhs */ #line 2303 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_asgn(p, (yyvsp[-2].nd), (yyvsp[0].nd)); } #line 7866 "mrbgems/mruby-compiler/core/y.tab.c" break; case 199: /* arg: var_lhs tOP_ASGN arg_rhs */ #line 2307 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_op_asgn(p, (yyvsp[-2].nd), (yyvsp[-1].id), (yyvsp[0].nd)); } #line 7874 "mrbgems/mruby-compiler/core/y.tab.c" break; case 200: /* arg: primary_value '[' opt_call_args ']' tOP_ASGN arg_rhs */ #line 2311 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_op_asgn(p, new_call(p, (yyvsp[-5].nd), intern_op(aref), (yyvsp[-3].nd), '.'), (yyvsp[-1].id), (yyvsp[0].nd)); } #line 7882 "mrbgems/mruby-compiler/core/y.tab.c" break; case 201: /* arg: primary_value call_op "local variable or method" tOP_ASGN arg_rhs */ #line 2315 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_op_asgn(p, new_call(p, (yyvsp[-4].nd), (yyvsp[-2].id), 0, (yyvsp[-3].num)), (yyvsp[-1].id), (yyvsp[0].nd)); } #line 7890 "mrbgems/mruby-compiler/core/y.tab.c" break; case 202: /* arg: primary_value call_op "constant" tOP_ASGN arg_rhs */ #line 2319 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_op_asgn(p, new_call(p, (yyvsp[-4].nd), (yyvsp[-2].id), 0, (yyvsp[-3].num)), (yyvsp[-1].id), (yyvsp[0].nd)); } #line 7898 "mrbgems/mruby-compiler/core/y.tab.c" break; case 203: /* arg: primary_value "::" "local variable or method" tOP_ASGN arg_rhs */ #line 2323 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_op_asgn(p, new_call(p, (yyvsp[-4].nd), (yyvsp[-2].id), 0, tCOLON2), (yyvsp[-1].id), (yyvsp[0].nd)); } #line 7906 "mrbgems/mruby-compiler/core/y.tab.c" break; case 204: /* arg: primary_value "::" "constant" tOP_ASGN arg_rhs */ #line 2327 "mrbgems/mruby-compiler/core/parse.y" { yyerror(&(yylsp[-4]), p, "constant re-assignment"); (yyval.nd) = new_begin(p, 0); } #line 7915 "mrbgems/mruby-compiler/core/y.tab.c" break; case 205: /* arg: tCOLON3 "constant" tOP_ASGN arg_rhs */ #line 2332 "mrbgems/mruby-compiler/core/parse.y" { yyerror(&(yylsp[-3]), p, "constant re-assignment"); (yyval.nd) = new_begin(p, 0); } #line 7924 "mrbgems/mruby-compiler/core/y.tab.c" break; case 206: /* arg: backref tOP_ASGN arg_rhs */ #line 2337 "mrbgems/mruby-compiler/core/parse.y" { backref_error(p, (yyvsp[-2].nd)); (yyval.nd) = new_begin(p, 0); } #line 7933 "mrbgems/mruby-compiler/core/y.tab.c" break; case 207: /* arg: arg ".." arg */ #line 2342 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_dot2(p, (yyvsp[-2].nd), (yyvsp[0].nd)); } #line 7941 "mrbgems/mruby-compiler/core/y.tab.c" break; case 208: /* arg: arg ".." */ #line 2346 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_dot2(p, (yyvsp[-1].nd), new_nil(p)); } #line 7949 "mrbgems/mruby-compiler/core/y.tab.c" break; case 209: /* arg: tBDOT2 arg */ #line 2350 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_dot2(p, new_nil(p), (yyvsp[0].nd)); } #line 7957 "mrbgems/mruby-compiler/core/y.tab.c" break; case 210: /* arg: arg "..." arg */ #line 2354 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_dot3(p, (yyvsp[-2].nd), (yyvsp[0].nd)); } #line 7965 "mrbgems/mruby-compiler/core/y.tab.c" break; case 211: /* arg: arg "..." */ #line 2358 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_dot3(p, (yyvsp[-1].nd), new_nil(p)); } #line 7973 "mrbgems/mruby-compiler/core/y.tab.c" break; case 212: /* arg: tBDOT3 arg */ #line 2362 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_dot3(p, new_nil(p), (yyvsp[0].nd)); } #line 7981 "mrbgems/mruby-compiler/core/y.tab.c" break; case 213: /* arg: arg '+' arg */ #line 2366 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "+", (yyvsp[0].nd)); } #line 7989 "mrbgems/mruby-compiler/core/y.tab.c" break; case 214: /* arg: arg '-' arg */ #line 2370 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "-", (yyvsp[0].nd)); } #line 7997 "mrbgems/mruby-compiler/core/y.tab.c" break; case 215: /* arg: arg '*' arg */ #line 2374 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "*", (yyvsp[0].nd)); } #line 8005 "mrbgems/mruby-compiler/core/y.tab.c" break; case 216: /* arg: arg '/' arg */ #line 2378 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "/", (yyvsp[0].nd)); } #line 8013 "mrbgems/mruby-compiler/core/y.tab.c" break; case 217: /* arg: arg '%' arg */ #line 2382 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "%", (yyvsp[0].nd)); } #line 8021 "mrbgems/mruby-compiler/core/y.tab.c" break; case 218: /* arg: arg tPOW arg */ #line 2386 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "**", (yyvsp[0].nd)); } #line 8029 "mrbgems/mruby-compiler/core/y.tab.c" break; case 219: /* arg: tUMINUS_NUM "integer literal" tPOW arg */ #line 2390 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_negate(p, call_bin_op(p, (yyvsp[-2].nd), "**", (yyvsp[0].nd))); } #line 8037 "mrbgems/mruby-compiler/core/y.tab.c" break; case 220: /* arg: tUMINUS_NUM "float literal" tPOW arg */ #line 2394 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_negate(p, call_bin_op(p, (yyvsp[-2].nd), "**", (yyvsp[0].nd))); } #line 8045 "mrbgems/mruby-compiler/core/y.tab.c" break; case 221: /* arg: "unary plus" arg */ #line 2398 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_uni_op(p, (yyvsp[0].nd), "+@"); } #line 8053 "mrbgems/mruby-compiler/core/y.tab.c" break; case 222: /* arg: "unary minus" arg */ #line 2402 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_negate(p, (yyvsp[0].nd)); } #line 8061 "mrbgems/mruby-compiler/core/y.tab.c" break; case 223: /* arg: arg '|' arg */ #line 2406 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "|", (yyvsp[0].nd)); } #line 8069 "mrbgems/mruby-compiler/core/y.tab.c" break; case 224: /* arg: arg '^' arg */ #line 2410 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "^", (yyvsp[0].nd)); } #line 8077 "mrbgems/mruby-compiler/core/y.tab.c" break; case 225: /* arg: arg '&' arg */ #line 2414 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "&", (yyvsp[0].nd)); } #line 8085 "mrbgems/mruby-compiler/core/y.tab.c" break; case 226: /* arg: arg "<=>" arg */ #line 2418 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "<=>", (yyvsp[0].nd)); } #line 8093 "mrbgems/mruby-compiler/core/y.tab.c" break; case 227: /* arg: arg '>' arg */ #line 2422 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), ">", (yyvsp[0].nd)); } #line 8101 "mrbgems/mruby-compiler/core/y.tab.c" break; case 228: /* arg: arg ">=" arg */ #line 2426 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), ">=", (yyvsp[0].nd)); } #line 8109 "mrbgems/mruby-compiler/core/y.tab.c" break; case 229: /* arg: arg '<' arg */ #line 2430 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "<", (yyvsp[0].nd)); } #line 8117 "mrbgems/mruby-compiler/core/y.tab.c" break; case 230: /* arg: arg "<=" arg */ #line 2434 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "<=", (yyvsp[0].nd)); } #line 8125 "mrbgems/mruby-compiler/core/y.tab.c" break; case 231: /* arg: arg "==" arg */ #line 2438 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "==", (yyvsp[0].nd)); } #line 8133 "mrbgems/mruby-compiler/core/y.tab.c" break; case 232: /* arg: arg "===" arg */ #line 2442 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "===", (yyvsp[0].nd)); } #line 8141 "mrbgems/mruby-compiler/core/y.tab.c" break; case 233: /* arg: arg "!=" arg */ #line 2446 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "!=", (yyvsp[0].nd)); } #line 8149 "mrbgems/mruby-compiler/core/y.tab.c" break; case 234: /* arg: arg "=~" arg */ #line 2450 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "=~", (yyvsp[0].nd)); } #line 8157 "mrbgems/mruby-compiler/core/y.tab.c" break; case 235: /* arg: arg "!~" arg */ #line 2454 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "!~", (yyvsp[0].nd)); } #line 8165 "mrbgems/mruby-compiler/core/y.tab.c" break; case 236: /* arg: '!' arg */ #line 2458 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_uni_op(p, cond((yyvsp[0].nd)), "!"); } #line 8173 "mrbgems/mruby-compiler/core/y.tab.c" break; case 237: /* arg: '~' arg */ #line 2462 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_uni_op(p, cond((yyvsp[0].nd)), "~"); } #line 8181 "mrbgems/mruby-compiler/core/y.tab.c" break; case 238: /* arg: arg "<<" arg */ #line 2466 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "<<", (yyvsp[0].nd)); } #line 8189 "mrbgems/mruby-compiler/core/y.tab.c" break; case 239: /* arg: arg ">>" arg */ #line 2470 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), ">>", (yyvsp[0].nd)); } #line 8197 "mrbgems/mruby-compiler/core/y.tab.c" break; case 240: /* arg: arg "&&" arg */ #line 2474 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_and(p, (yyvsp[-2].nd), (yyvsp[0].nd)); } #line 8205 "mrbgems/mruby-compiler/core/y.tab.c" break; case 241: /* arg: arg "||" arg */ #line 2478 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_or(p, (yyvsp[-2].nd), (yyvsp[0].nd)); } #line 8213 "mrbgems/mruby-compiler/core/y.tab.c" break; case 242: /* arg: arg '?' arg opt_nl ':' arg */ #line 2482 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_if(p, cond((yyvsp[-5].nd)), (yyvsp[-3].nd), (yyvsp[0].nd)); } #line 8221 "mrbgems/mruby-compiler/core/y.tab.c" break; case 243: /* arg: arg '?' arg opt_nl "label" arg */ #line 2486 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_if(p, cond((yyvsp[-5].nd)), (yyvsp[-3].nd), (yyvsp[0].nd)); } #line 8229 "mrbgems/mruby-compiler/core/y.tab.c" break; case 244: /* arg: defn_head f_opt_arglist_paren '=' arg */ #line 2490 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-3].nd); endless_method_name(p, (yyvsp[-3].nd)); void_expr_error(p, (yyvsp[0].nd)); defn_setup(p, (yyval.nd), (yyvsp[-2].nd), (yyvsp[0].nd)); nvars_unnest(p); p->in_def--; } #line 8242 "mrbgems/mruby-compiler/core/y.tab.c" break; case 245: /* arg: defn_head f_opt_arglist_paren '=' arg "'rescue' modifier" arg */ #line 2499 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-5].nd); endless_method_name(p, (yyvsp[-5].nd)); void_expr_error(p, (yyvsp[-2].nd)); defn_setup(p, (yyval.nd), (yyvsp[-4].nd), new_mod_rescue(p, (yyvsp[-2].nd), (yyvsp[0].nd))); nvars_unnest(p); p->in_def--; } #line 8255 "mrbgems/mruby-compiler/core/y.tab.c" break; case 246: /* arg: defs_head f_opt_arglist_paren '=' arg */ #line 2508 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-3].nd); void_expr_error(p, (yyvsp[0].nd)); defs_setup(p, (yyval.nd), (yyvsp[-2].nd), (yyvsp[0].nd)); nvars_unnest(p); p->in_def--; p->in_single--; } #line 8268 "mrbgems/mruby-compiler/core/y.tab.c" break; case 247: /* arg: defs_head f_opt_arglist_paren '=' arg "'rescue' modifier" arg */ #line 2517 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-5].nd); void_expr_error(p, (yyvsp[-2].nd)); defs_setup(p, (yyval.nd), (yyvsp[-4].nd), new_mod_rescue(p, (yyvsp[-2].nd), (yyvsp[0].nd))); nvars_unnest(p); p->in_def--; p->in_single--; } #line 8281 "mrbgems/mruby-compiler/core/y.tab.c" break; case 248: /* arg: primary */ #line 2526 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[0].nd); } #line 8289 "mrbgems/mruby-compiler/core/y.tab.c" break; case 250: /* aref_args: args trailer */ #line 2533 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-1].nd); NODE_LINENO((yyval.nd), (yyvsp[-1].nd)); } #line 8298 "mrbgems/mruby-compiler/core/y.tab.c" break; case 251: /* aref_args: args comma assocs trailer */ #line 2538 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = push((yyvsp[-3].nd), new_hash(p, (yyvsp[-1].nd))); } #line 8306 "mrbgems/mruby-compiler/core/y.tab.c" break; case 252: /* aref_args: assocs trailer */ #line 2542 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = cons(new_kw_hash(p, (yyvsp[-1].nd)), 0); NODE_LINENO((yyval.nd), (yyvsp[-1].nd)); } #line 8315 "mrbgems/mruby-compiler/core/y.tab.c" break; case 253: /* arg_rhs: arg */ #line 2549 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[0].nd); } #line 8323 "mrbgems/mruby-compiler/core/y.tab.c" break; case 254: /* arg_rhs: arg "'rescue' modifier" arg */ #line 2553 "mrbgems/mruby-compiler/core/parse.y" { void_expr_error(p, (yyvsp[-2].nd)); (yyval.nd) = new_mod_rescue(p, (yyvsp[-2].nd), (yyvsp[0].nd)); } #line 8332 "mrbgems/mruby-compiler/core/y.tab.c" break; case 255: /* paren_args: '(' opt_call_args ')' */ #line 2560 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-1].nd); } #line 8340 "mrbgems/mruby-compiler/core/y.tab.c" break; case 256: /* paren_args: '(' args comma tBDOT3 rparen */ #line 2564 "mrbgems/mruby-compiler/core/parse.y" { mrb_sym r = intern_op(mul); mrb_sym k = intern_op(pow); mrb_sym b = intern_op(and); (yyval.nd) = new_callargs(p, push((yyvsp[-3].nd), new_splat(p, new_lvar(p, r))), new_kw_hash(p, list1(cons(new_kw_rest_args(p, 0), new_lvar(p, k)))), new_block_arg(p, new_lvar(p, b))); } #line 8353 "mrbgems/mruby-compiler/core/y.tab.c" break; case 257: /* paren_args: '(' tBDOT3 rparen */ #line 2573 "mrbgems/mruby-compiler/core/parse.y" { mrb_sym r = intern_op(mul); mrb_sym k = intern_op(pow); mrb_sym b = intern_op(and); if (local_var_p(p, r) && local_var_p(p, k) && local_var_p(p, b)) { (yyval.nd) = new_callargs(p, list1(new_splat(p, new_lvar(p, r))), new_kw_hash(p, list1(cons(new_kw_rest_args(p, 0), new_lvar(p, k)))), new_block_arg(p, new_lvar(p, b))); } else { yyerror(&(yylsp[-2]), p, "unexpected argument forwarding ..."); (yyval.nd) = 0; } } #line 8372 "mrbgems/mruby-compiler/core/y.tab.c" break; case 262: /* opt_call_args: args comma */ #line 2596 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_callargs(p,(yyvsp[-1].nd),0,0); NODE_LINENO((yyval.nd), (yyvsp[-1].nd)); } #line 8381 "mrbgems/mruby-compiler/core/y.tab.c" break; case 263: /* opt_call_args: args comma assocs comma */ #line 2601 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_callargs(p,(yyvsp[-3].nd),new_kw_hash(p,(yyvsp[-1].nd)),0); NODE_LINENO((yyval.nd), (yyvsp[-3].nd)); } #line 8390 "mrbgems/mruby-compiler/core/y.tab.c" break; case 264: /* opt_call_args: assocs comma */ #line 2606 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_callargs(p,0,new_kw_hash(p,(yyvsp[-1].nd)),0); NODE_LINENO((yyval.nd), (yyvsp[-1].nd)); } #line 8399 "mrbgems/mruby-compiler/core/y.tab.c" break; case 265: /* call_args: command */ #line 2613 "mrbgems/mruby-compiler/core/parse.y" { void_expr_error(p, (yyvsp[0].nd)); (yyval.nd) = new_callargs(p, list1((yyvsp[0].nd)), 0, 0); NODE_LINENO((yyval.nd), (yyvsp[0].nd)); } #line 8409 "mrbgems/mruby-compiler/core/y.tab.c" break; case 266: /* call_args: args opt_block_arg */ #line 2619 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_callargs(p, (yyvsp[-1].nd), 0, (yyvsp[0].nd)); NODE_LINENO((yyval.nd), (yyvsp[-1].nd)); } #line 8418 "mrbgems/mruby-compiler/core/y.tab.c" break; case 267: /* call_args: assocs opt_block_arg */ #line 2624 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_callargs(p, 0, new_kw_hash(p, (yyvsp[-1].nd)), (yyvsp[0].nd)); NODE_LINENO((yyval.nd), (yyvsp[-1].nd)); } #line 8427 "mrbgems/mruby-compiler/core/y.tab.c" break; case 268: /* call_args: args comma assocs opt_block_arg */ #line 2629 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_callargs(p, (yyvsp[-3].nd), new_kw_hash(p, (yyvsp[-1].nd)), (yyvsp[0].nd)); NODE_LINENO((yyval.nd), (yyvsp[-3].nd)); } #line 8436 "mrbgems/mruby-compiler/core/y.tab.c" break; case 269: /* call_args: block_arg */ #line 2634 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_callargs(p, 0, 0, (yyvsp[0].nd)); NODE_LINENO((yyval.nd), (yyvsp[0].nd)); } #line 8445 "mrbgems/mruby-compiler/core/y.tab.c" break; case 270: /* @7: %empty */ #line 2640 "mrbgems/mruby-compiler/core/parse.y" { (yyval.stack) = p->cmdarg_stack; CMDARG_PUSH(1); } #line 8454 "mrbgems/mruby-compiler/core/y.tab.c" break; case 271: /* command_args: @7 call_args */ #line 2645 "mrbgems/mruby-compiler/core/parse.y" { p->cmdarg_stack = (yyvsp[-1].stack); (yyval.nd) = (yyvsp[0].nd); } #line 8463 "mrbgems/mruby-compiler/core/y.tab.c" break; case 272: /* block_arg: "&" arg */ #line 2652 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_block_arg(p, (yyvsp[0].nd)); } #line 8471 "mrbgems/mruby-compiler/core/y.tab.c" break; case 273: /* block_arg: "&" */ #line 2656 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_block_arg(p, 0); } #line 8479 "mrbgems/mruby-compiler/core/y.tab.c" break; case 274: /* opt_block_arg: comma block_arg */ #line 2662 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[0].nd); } #line 8487 "mrbgems/mruby-compiler/core/y.tab.c" break; case 275: /* opt_block_arg: none */ #line 2666 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = 0; } #line 8495 "mrbgems/mruby-compiler/core/y.tab.c" break; case 277: /* args: arg */ #line 2675 "mrbgems/mruby-compiler/core/parse.y" { void_expr_error(p, (yyvsp[0].nd)); (yyval.nd) = list1((yyvsp[0].nd)); NODE_LINENO((yyval.nd), (yyvsp[0].nd)); } #line 8505 "mrbgems/mruby-compiler/core/y.tab.c" break; case 278: /* args: "*" */ #line 2681 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list1(new_splat(p, new_lvar(p, intern_op(mul)))); } #line 8513 "mrbgems/mruby-compiler/core/y.tab.c" break; case 279: /* args: "*" arg */ #line 2685 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list1(new_splat(p, (yyvsp[0].nd))); NODE_LINENO((yyval.nd), (yyvsp[0].nd)); } #line 8522 "mrbgems/mruby-compiler/core/y.tab.c" break; case 280: /* args: args comma arg */ #line 2690 "mrbgems/mruby-compiler/core/parse.y" { void_expr_error(p, (yyvsp[0].nd)); (yyval.nd) = push((yyvsp[-2].nd), (yyvsp[0].nd)); } #line 8531 "mrbgems/mruby-compiler/core/y.tab.c" break; case 281: /* args: args comma "*" */ #line 2695 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = push((yyvsp[-2].nd), new_splat(p, new_lvar(p, intern_op(mul)))); } #line 8539 "mrbgems/mruby-compiler/core/y.tab.c" break; case 282: /* args: args comma "*" arg */ #line 2699 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = push((yyvsp[-3].nd), new_splat(p, (yyvsp[0].nd))); } #line 8547 "mrbgems/mruby-compiler/core/y.tab.c" break; case 283: /* mrhs: args comma arg */ #line 2705 "mrbgems/mruby-compiler/core/parse.y" { void_expr_error(p, (yyvsp[0].nd)); (yyval.nd) = push((yyvsp[-2].nd), (yyvsp[0].nd)); } #line 8556 "mrbgems/mruby-compiler/core/y.tab.c" break; case 284: /* mrhs: args comma "*" arg */ #line 2710 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = push((yyvsp[-3].nd), new_splat(p, (yyvsp[0].nd))); } #line 8564 "mrbgems/mruby-compiler/core/y.tab.c" break; case 285: /* mrhs: "*" arg */ #line 2714 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list1(new_splat(p, (yyvsp[0].nd))); } #line 8572 "mrbgems/mruby-compiler/core/y.tab.c" break; case 293: /* primary: "method" */ #line 2727 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_fcall(p, (yyvsp[0].id), 0); } #line 8580 "mrbgems/mruby-compiler/core/y.tab.c" break; case 294: /* @8: %empty */ #line 2731 "mrbgems/mruby-compiler/core/parse.y" { (yyval.stack) = p->cmdarg_stack; p->cmdarg_stack = 0; } #line 8589 "mrbgems/mruby-compiler/core/y.tab.c" break; case 295: /* primary: "'begin'" @8 bodystmt "'end'" */ #line 2737 "mrbgems/mruby-compiler/core/parse.y" { p->cmdarg_stack = (yyvsp[-2].stack); (yyval.nd) = (yyvsp[-1].nd); } #line 8598 "mrbgems/mruby-compiler/core/y.tab.c" break; case 296: /* @9: %empty */ #line 2742 "mrbgems/mruby-compiler/core/parse.y" { (yyval.stack) = p->cmdarg_stack; p->cmdarg_stack = 0; } #line 8607 "mrbgems/mruby-compiler/core/y.tab.c" break; case 297: /* $@10: %empty */ #line 2746 "mrbgems/mruby-compiler/core/parse.y" {p->lstate = EXPR_ENDARG;} #line 8613 "mrbgems/mruby-compiler/core/y.tab.c" break; case 298: /* primary: "(" @9 stmt $@10 rparen */ #line 2747 "mrbgems/mruby-compiler/core/parse.y" { p->cmdarg_stack = (yyvsp[-3].stack); (yyval.nd) = (yyvsp[-2].nd); } #line 8622 "mrbgems/mruby-compiler/core/y.tab.c" break; case 299: /* $@11: %empty */ #line 2751 "mrbgems/mruby-compiler/core/parse.y" {p->lstate = EXPR_ENDARG;} #line 8628 "mrbgems/mruby-compiler/core/y.tab.c" break; case 300: /* primary: "(" $@11 rparen */ #line 2752 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_nil(p); } #line 8636 "mrbgems/mruby-compiler/core/y.tab.c" break; case 301: /* primary: tLPAREN compstmt ')' */ #line 2756 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-1].nd); } #line 8644 "mrbgems/mruby-compiler/core/y.tab.c" break; case 302: /* primary: primary_value "::" "constant" */ #line 2760 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_colon2(p, (yyvsp[-2].nd), (yyvsp[0].id)); } #line 8652 "mrbgems/mruby-compiler/core/y.tab.c" break; case 303: /* primary: tCOLON3 "constant" */ #line 2764 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_colon3(p, (yyvsp[0].id)); } #line 8660 "mrbgems/mruby-compiler/core/y.tab.c" break; case 304: /* primary: "[" aref_args ']' */ #line 2768 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_array(p, (yyvsp[-1].nd)); NODE_LINENO((yyval.nd), (yyvsp[-1].nd)); } #line 8669 "mrbgems/mruby-compiler/core/y.tab.c" break; case 305: /* primary: tLBRACE assoc_list '}' */ #line 2773 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_hash(p, (yyvsp[-1].nd)); NODE_LINENO((yyval.nd), (yyvsp[-1].nd)); } #line 8678 "mrbgems/mruby-compiler/core/y.tab.c" break; case 306: /* primary: "'return'" */ #line 2778 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_return(p, 0); } #line 8686 "mrbgems/mruby-compiler/core/y.tab.c" break; case 307: /* primary: "'yield'" opt_paren_args */ #line 2782 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_yield(p, (yyvsp[0].nd)); } #line 8694 "mrbgems/mruby-compiler/core/y.tab.c" break; case 308: /* primary: "'not'" '(' expr rparen */ #line 2786 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_uni_op(p, cond((yyvsp[-1].nd)), "!"); } #line 8702 "mrbgems/mruby-compiler/core/y.tab.c" break; case 309: /* primary: "'not'" '(' rparen */ #line 2790 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = call_uni_op(p, new_nil(p), "!"); } #line 8710 "mrbgems/mruby-compiler/core/y.tab.c" break; case 310: /* primary: operation brace_block */ #line 2794 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_fcall(p, (yyvsp[-1].id), new_callargs(p, 0, 0, (yyvsp[0].nd))); } #line 8718 "mrbgems/mruby-compiler/core/y.tab.c" break; case 312: /* primary: method_call brace_block */ #line 2799 "mrbgems/mruby-compiler/core/parse.y" { call_with_block(p, (yyvsp[-1].nd), (yyvsp[0].nd)); (yyval.nd) = (yyvsp[-1].nd); } #line 8727 "mrbgems/mruby-compiler/core/y.tab.c" break; case 313: /* @12: %empty */ #line 2804 "mrbgems/mruby-compiler/core/parse.y" { local_nest(p); nvars_nest(p); (yyval.num) = p->lpar_beg; p->lpar_beg = ++p->paren_nest; } #line 8738 "mrbgems/mruby-compiler/core/y.tab.c" break; case 314: /* @13: %empty */ #line 2811 "mrbgems/mruby-compiler/core/parse.y" { (yyval.stack) = p->cmdarg_stack; p->cmdarg_stack = 0; } #line 8747 "mrbgems/mruby-compiler/core/y.tab.c" break; case 315: /* primary: "->" @12 f_larglist @13 lambda_body */ #line 2816 "mrbgems/mruby-compiler/core/parse.y" { p->lpar_beg = (yyvsp[-3].num); (yyval.nd) = new_lambda(p, (yyvsp[-2].nd), (yyvsp[0].nd)); local_unnest(p); nvars_unnest(p); p->cmdarg_stack = (yyvsp[-1].stack); CMDARG_LEXPOP(); } #line 8760 "mrbgems/mruby-compiler/core/y.tab.c" break; case 316: /* primary: "'if'" expr_value then compstmt if_tail "'end'" */ #line 2828 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_if(p, cond((yyvsp[-4].nd)), (yyvsp[-2].nd), (yyvsp[-1].nd)); SET_LINENO((yyval.nd), (yyvsp[-5].num)); } #line 8769 "mrbgems/mruby-compiler/core/y.tab.c" break; case 317: /* primary: "'unless'" expr_value then compstmt opt_else "'end'" */ #line 2836 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_unless(p, cond((yyvsp[-4].nd)), (yyvsp[-2].nd), (yyvsp[-1].nd)); SET_LINENO((yyval.nd), (yyvsp[-5].num)); } #line 8778 "mrbgems/mruby-compiler/core/y.tab.c" break; case 318: /* $@14: %empty */ #line 2840 "mrbgems/mruby-compiler/core/parse.y" {COND_PUSH(1);} #line 8784 "mrbgems/mruby-compiler/core/y.tab.c" break; case 319: /* $@15: %empty */ #line 2840 "mrbgems/mruby-compiler/core/parse.y" {COND_POP();} #line 8790 "mrbgems/mruby-compiler/core/y.tab.c" break; case 320: /* primary: "'while'" $@14 expr_value do $@15 compstmt "'end'" */ #line 2843 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_while(p, cond((yyvsp[-4].nd)), (yyvsp[-1].nd)); SET_LINENO((yyval.nd), (yyvsp[-6].num)); } #line 8799 "mrbgems/mruby-compiler/core/y.tab.c" break; case 321: /* $@16: %empty */ #line 2847 "mrbgems/mruby-compiler/core/parse.y" {COND_PUSH(1);} #line 8805 "mrbgems/mruby-compiler/core/y.tab.c" break; case 322: /* $@17: %empty */ #line 2847 "mrbgems/mruby-compiler/core/parse.y" {COND_POP();} #line 8811 "mrbgems/mruby-compiler/core/y.tab.c" break; case 323: /* primary: "'until'" $@16 expr_value do $@17 compstmt "'end'" */ #line 2850 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_until(p, cond((yyvsp[-4].nd)), (yyvsp[-1].nd)); SET_LINENO((yyval.nd), (yyvsp[-6].num)); } #line 8820 "mrbgems/mruby-compiler/core/y.tab.c" break; case 324: /* primary: "'case'" expr_value opt_terms case_body "'end'" */ #line 2857 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_case(p, (yyvsp[-3].nd), (yyvsp[-1].nd)); } #line 8828 "mrbgems/mruby-compiler/core/y.tab.c" break; case 325: /* primary: "'case'" opt_terms case_body "'end'" */ #line 2861 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_case(p, 0, (yyvsp[-1].nd)); } #line 8836 "mrbgems/mruby-compiler/core/y.tab.c" break; case 326: /* $@18: %empty */ #line 2865 "mrbgems/mruby-compiler/core/parse.y" {COND_PUSH(1);} #line 8842 "mrbgems/mruby-compiler/core/y.tab.c" break; case 327: /* $@19: %empty */ #line 2867 "mrbgems/mruby-compiler/core/parse.y" {COND_POP();} #line 8848 "mrbgems/mruby-compiler/core/y.tab.c" break; case 328: /* primary: "'for'" for_var "'in'" $@18 expr_value do $@19 compstmt "'end'" */ #line 2870 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_for(p, (yyvsp[-7].nd), (yyvsp[-4].nd), (yyvsp[-1].nd)); SET_LINENO((yyval.nd), (yyvsp[-8].num)); } #line 8857 "mrbgems/mruby-compiler/core/y.tab.c" break; case 329: /* @20: %empty */ #line 2876 "mrbgems/mruby-compiler/core/parse.y" { if (p->in_def || p->in_single) yyerror(&(yylsp[-2]), p, "class definition in method body"); (yyval.nd) = local_switch(p); nvars_block(p); } #line 8868 "mrbgems/mruby-compiler/core/y.tab.c" break; case 330: /* primary: "'class'" cpath superclass @20 bodystmt "'end'" */ #line 2884 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_class(p, (yyvsp[-4].nd), (yyvsp[-3].nd), (yyvsp[-1].nd)); SET_LINENO((yyval.nd), (yyvsp[-5].num)); local_resume(p, (yyvsp[-2].nd)); nvars_unnest(p); } #line 8879 "mrbgems/mruby-compiler/core/y.tab.c" break; case 331: /* @21: %empty */ #line 2892 "mrbgems/mruby-compiler/core/parse.y" { (yyval.num) = p->in_def; p->in_def = 0; } #line 8888 "mrbgems/mruby-compiler/core/y.tab.c" break; case 332: /* @22: %empty */ #line 2897 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = cons(local_switch(p), nint(p->in_single)); nvars_block(p); p->in_single = 0; } #line 8898 "mrbgems/mruby-compiler/core/y.tab.c" break; case 333: /* primary: "'class'" "<<" expr @21 term @22 bodystmt "'end'" */ #line 2904 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_sclass(p, (yyvsp[-5].nd), (yyvsp[-1].nd)); SET_LINENO((yyval.nd), (yyvsp[-7].num)); local_resume(p, (yyvsp[-2].nd)->car); nvars_unnest(p); p->in_def = (yyvsp[-4].num); p->in_single = intn((yyvsp[-2].nd)->cdr); } #line 8911 "mrbgems/mruby-compiler/core/y.tab.c" break; case 334: /* @23: %empty */ #line 2914 "mrbgems/mruby-compiler/core/parse.y" { if (p->in_def || p->in_single) yyerror(&(yylsp[-1]), p, "module definition in method body"); (yyval.nd) = local_switch(p); nvars_block(p); } #line 8922 "mrbgems/mruby-compiler/core/y.tab.c" break; case 335: /* primary: "'module'" cpath @23 bodystmt "'end'" */ #line 2922 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_module(p, (yyvsp[-3].nd), (yyvsp[-1].nd)); SET_LINENO((yyval.nd), (yyvsp[-4].num)); local_resume(p, (yyvsp[-2].nd)); nvars_unnest(p); } #line 8933 "mrbgems/mruby-compiler/core/y.tab.c" break; case 336: /* primary: defn_head f_arglist bodystmt "'end'" */ #line 2932 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-3].nd); defn_setup(p, (yyval.nd), (yyvsp[-2].nd), (yyvsp[-1].nd)); nvars_unnest(p); p->in_def--; } #line 8944 "mrbgems/mruby-compiler/core/y.tab.c" break; case 337: /* primary: defs_head f_arglist bodystmt "'end'" */ #line 2942 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-3].nd); defs_setup(p, (yyval.nd), (yyvsp[-2].nd), (yyvsp[-1].nd)); nvars_unnest(p); p->in_def--; p->in_single--; } #line 8956 "mrbgems/mruby-compiler/core/y.tab.c" break; case 338: /* primary: "'break'" */ #line 2950 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_break(p, 0); } #line 8964 "mrbgems/mruby-compiler/core/y.tab.c" break; case 339: /* primary: "'next'" */ #line 2954 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_next(p, 0); } #line 8972 "mrbgems/mruby-compiler/core/y.tab.c" break; case 340: /* primary: "'redo'" */ #line 2958 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_redo(p); } #line 8980 "mrbgems/mruby-compiler/core/y.tab.c" break; case 341: /* primary: "'retry'" */ #line 2962 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_retry(p); } #line 8988 "mrbgems/mruby-compiler/core/y.tab.c" break; case 342: /* primary_value: primary */ #line 2968 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[0].nd); if (!(yyval.nd)) (yyval.nd) = new_nil(p); } #line 8997 "mrbgems/mruby-compiler/core/y.tab.c" break; case 349: /* if_tail: "'elsif'" expr_value then compstmt if_tail */ #line 2987 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_if(p, cond((yyvsp[-3].nd)), (yyvsp[-1].nd), (yyvsp[0].nd)); } #line 9005 "mrbgems/mruby-compiler/core/y.tab.c" break; case 351: /* opt_else: "'else'" compstmt */ #line 2994 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[0].nd); } #line 9013 "mrbgems/mruby-compiler/core/y.tab.c" break; case 352: /* for_var: lhs */ #line 3000 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list1(list1((yyvsp[0].nd))); } #line 9021 "mrbgems/mruby-compiler/core/y.tab.c" break; case 354: /* f_margs: f_arg */ #line 3007 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list3((yyvsp[0].nd),0,0); } #line 9029 "mrbgems/mruby-compiler/core/y.tab.c" break; case 355: /* f_margs: f_arg ',' "*" f_norm_arg */ #line 3011 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list3((yyvsp[-3].nd), new_arg(p, (yyvsp[0].id)), 0); } #line 9037 "mrbgems/mruby-compiler/core/y.tab.c" break; case 356: /* f_margs: f_arg ',' "*" f_norm_arg ',' f_arg */ #line 3015 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list3((yyvsp[-5].nd), new_arg(p, (yyvsp[-2].id)), (yyvsp[0].nd)); } #line 9045 "mrbgems/mruby-compiler/core/y.tab.c" break; case 357: /* f_margs: f_arg ',' "*" */ #line 3019 "mrbgems/mruby-compiler/core/parse.y" { local_add_f(p, intern_op(mul)); (yyval.nd) = list3((yyvsp[-2].nd), nint(-1), 0); } #line 9054 "mrbgems/mruby-compiler/core/y.tab.c" break; case 358: /* f_margs: f_arg ',' "*" ',' f_arg */ #line 3024 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list3((yyvsp[-4].nd), nint(-1), (yyvsp[0].nd)); } #line 9062 "mrbgems/mruby-compiler/core/y.tab.c" break; case 359: /* f_margs: "*" f_norm_arg */ #line 3028 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list3(0, new_arg(p, (yyvsp[0].id)), 0); } #line 9070 "mrbgems/mruby-compiler/core/y.tab.c" break; case 360: /* f_margs: "*" f_norm_arg ',' f_arg */ #line 3032 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list3(0, new_arg(p, (yyvsp[-2].id)), (yyvsp[0].nd)); } #line 9078 "mrbgems/mruby-compiler/core/y.tab.c" break; case 361: /* f_margs: "*" */ #line 3036 "mrbgems/mruby-compiler/core/parse.y" { local_add_f(p, intern_op(mul)); (yyval.nd) = list3(0, nint(-1), 0); } #line 9087 "mrbgems/mruby-compiler/core/y.tab.c" break; case 362: /* $@24: %empty */ #line 3041 "mrbgems/mruby-compiler/core/parse.y" { local_add_f(p, intern_op(mul)); } #line 9095 "mrbgems/mruby-compiler/core/y.tab.c" break; case 363: /* f_margs: "*" ',' $@24 f_arg */ #line 3045 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list3(0, nint(-1), (yyvsp[0].nd)); } #line 9103 "mrbgems/mruby-compiler/core/y.tab.c" break; case 364: /* block_args_tail: f_block_kwarg ',' f_kwrest opt_f_block_arg */ #line 3051 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args_tail(p, (yyvsp[-3].nd), (yyvsp[-1].nd), (yyvsp[0].id)); } #line 9111 "mrbgems/mruby-compiler/core/y.tab.c" break; case 365: /* block_args_tail: f_block_kwarg opt_f_block_arg */ #line 3055 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args_tail(p, (yyvsp[-1].nd), 0, (yyvsp[0].id)); } #line 9119 "mrbgems/mruby-compiler/core/y.tab.c" break; case 366: /* block_args_tail: f_kwrest opt_f_block_arg */ #line 3059 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args_tail(p, 0, (yyvsp[-1].nd), (yyvsp[0].id)); } #line 9127 "mrbgems/mruby-compiler/core/y.tab.c" break; case 367: /* block_args_tail: f_block_arg */ #line 3063 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args_tail(p, 0, 0, (yyvsp[0].id)); } #line 9135 "mrbgems/mruby-compiler/core/y.tab.c" break; case 368: /* opt_block_args_tail: ',' block_args_tail */ #line 3069 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[0].nd); } #line 9143 "mrbgems/mruby-compiler/core/y.tab.c" break; case 369: /* opt_block_args_tail: %empty */ #line 3073 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args_tail(p, 0, 0, 0); } #line 9151 "mrbgems/mruby-compiler/core/y.tab.c" break; case 370: /* block_param: f_arg ',' f_block_optarg ',' f_rest_arg opt_block_args_tail */ #line 3079 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, (yyvsp[-5].nd), (yyvsp[-3].nd), (yyvsp[-1].id), 0, (yyvsp[0].nd)); } #line 9159 "mrbgems/mruby-compiler/core/y.tab.c" break; case 371: /* block_param: f_arg ',' f_block_optarg ',' f_rest_arg ',' f_arg opt_block_args_tail */ #line 3083 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, (yyvsp[-7].nd), (yyvsp[-5].nd), (yyvsp[-3].id), (yyvsp[-1].nd), (yyvsp[0].nd)); } #line 9167 "mrbgems/mruby-compiler/core/y.tab.c" break; case 372: /* block_param: f_arg ',' f_block_optarg opt_block_args_tail */ #line 3087 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, (yyvsp[-3].nd), (yyvsp[-1].nd), 0, 0, (yyvsp[0].nd)); } #line 9175 "mrbgems/mruby-compiler/core/y.tab.c" break; case 373: /* block_param: f_arg ',' f_block_optarg ',' f_arg opt_block_args_tail */ #line 3091 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, (yyvsp[-5].nd), (yyvsp[-3].nd), 0, (yyvsp[-1].nd), (yyvsp[0].nd)); } #line 9183 "mrbgems/mruby-compiler/core/y.tab.c" break; case 374: /* block_param: f_arg ',' f_rest_arg opt_block_args_tail */ #line 3095 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, (yyvsp[-3].nd), 0, (yyvsp[-1].id), 0, (yyvsp[0].nd)); } #line 9191 "mrbgems/mruby-compiler/core/y.tab.c" break; case 375: /* block_param: f_arg ',' opt_block_args_tail */ #line 3099 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, (yyvsp[-2].nd), 0, 0, 0, (yyvsp[0].nd)); } #line 9199 "mrbgems/mruby-compiler/core/y.tab.c" break; case 376: /* block_param: f_arg ',' f_rest_arg ',' f_arg opt_block_args_tail */ #line 3103 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, (yyvsp[-5].nd), 0, (yyvsp[-3].id), (yyvsp[-1].nd), (yyvsp[0].nd)); } #line 9207 "mrbgems/mruby-compiler/core/y.tab.c" break; case 377: /* block_param: f_arg opt_block_args_tail */ #line 3107 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, (yyvsp[-1].nd), 0, 0, 0, (yyvsp[0].nd)); } #line 9215 "mrbgems/mruby-compiler/core/y.tab.c" break; case 378: /* block_param: f_block_optarg ',' f_rest_arg opt_block_args_tail */ #line 3111 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, 0, (yyvsp[-3].nd), (yyvsp[-1].id), 0, (yyvsp[0].nd)); } #line 9223 "mrbgems/mruby-compiler/core/y.tab.c" break; case 379: /* block_param: f_block_optarg ',' f_rest_arg ',' f_arg opt_block_args_tail */ #line 3115 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, 0, (yyvsp[-5].nd), (yyvsp[-3].id), (yyvsp[-1].nd), (yyvsp[0].nd)); } #line 9231 "mrbgems/mruby-compiler/core/y.tab.c" break; case 380: /* block_param: f_block_optarg opt_block_args_tail */ #line 3119 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, 0, (yyvsp[-1].nd), 0, 0, (yyvsp[0].nd)); } #line 9239 "mrbgems/mruby-compiler/core/y.tab.c" break; case 381: /* block_param: f_block_optarg ',' f_arg opt_block_args_tail */ #line 3123 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, 0, (yyvsp[-3].nd), 0, (yyvsp[-1].nd), (yyvsp[0].nd)); } #line 9247 "mrbgems/mruby-compiler/core/y.tab.c" break; case 382: /* block_param: f_rest_arg opt_block_args_tail */ #line 3127 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, 0, 0, (yyvsp[-1].id), 0, (yyvsp[0].nd)); } #line 9255 "mrbgems/mruby-compiler/core/y.tab.c" break; case 383: /* block_param: f_rest_arg ',' f_arg opt_block_args_tail */ #line 3131 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, 0, 0, (yyvsp[-3].id), (yyvsp[-1].nd), (yyvsp[0].nd)); } #line 9263 "mrbgems/mruby-compiler/core/y.tab.c" break; case 384: /* block_param: block_args_tail */ #line 3135 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, 0, 0, 0, 0, (yyvsp[0].nd)); } #line 9271 "mrbgems/mruby-compiler/core/y.tab.c" break; case 385: /* opt_block_param: none */ #line 3141 "mrbgems/mruby-compiler/core/parse.y" { local_add_blk(p); (yyval.nd) = 0; } #line 9280 "mrbgems/mruby-compiler/core/y.tab.c" break; case 386: /* opt_block_param: block_param_def */ #line 3146 "mrbgems/mruby-compiler/core/parse.y" { p->cmd_start = TRUE; (yyval.nd) = (yyvsp[0].nd); } #line 9289 "mrbgems/mruby-compiler/core/y.tab.c" break; case 387: /* $@25: %empty */ #line 3152 "mrbgems/mruby-compiler/core/parse.y" {local_add_blk(p);} #line 9295 "mrbgems/mruby-compiler/core/y.tab.c" break; case 388: /* block_param_def: '|' $@25 opt_bv_decl '|' */ #line 3153 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = 0; } #line 9303 "mrbgems/mruby-compiler/core/y.tab.c" break; case 389: /* block_param_def: "||" */ #line 3157 "mrbgems/mruby-compiler/core/parse.y" { local_add_blk(p); (yyval.nd) = 0; } #line 9312 "mrbgems/mruby-compiler/core/y.tab.c" break; case 390: /* block_param_def: '|' block_param opt_bv_decl '|' */ #line 3162 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-2].nd); } #line 9320 "mrbgems/mruby-compiler/core/y.tab.c" break; case 391: /* opt_bv_decl: opt_nl */ #line 3169 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = 0; } #line 9328 "mrbgems/mruby-compiler/core/y.tab.c" break; case 392: /* opt_bv_decl: opt_nl ';' bv_decls opt_nl */ #line 3173 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = 0; } #line 9336 "mrbgems/mruby-compiler/core/y.tab.c" break; case 395: /* bvar: "local variable or method" */ #line 3183 "mrbgems/mruby-compiler/core/parse.y" { local_add_f(p, (yyvsp[0].id)); new_bv(p, (yyvsp[0].id)); } #line 9345 "mrbgems/mruby-compiler/core/y.tab.c" break; case 397: /* f_larglist: '(' f_args opt_bv_decl ')' */ #line 3191 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-2].nd); } #line 9353 "mrbgems/mruby-compiler/core/y.tab.c" break; case 398: /* f_larglist: f_args */ #line 3195 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[0].nd); } #line 9361 "mrbgems/mruby-compiler/core/y.tab.c" break; case 399: /* lambda_body: tLAMBEG compstmt '}' */ #line 3201 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-1].nd); } #line 9369 "mrbgems/mruby-compiler/core/y.tab.c" break; case 400: /* lambda_body: "'do' for lambda" bodystmt "'end'" */ #line 3205 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-1].nd); } #line 9377 "mrbgems/mruby-compiler/core/y.tab.c" break; case 401: /* @26: %empty */ #line 3211 "mrbgems/mruby-compiler/core/parse.y" { local_nest(p); nvars_nest(p); (yyval.num) = p->lineno; } #line 9387 "mrbgems/mruby-compiler/core/y.tab.c" break; case 402: /* do_block: "'do' for block" @26 opt_block_param bodystmt "'end'" */ #line 3219 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_block(p,(yyvsp[-2].nd),(yyvsp[-1].nd)); SET_LINENO((yyval.nd), (yyvsp[-3].num)); local_unnest(p); nvars_unnest(p); } #line 9398 "mrbgems/mruby-compiler/core/y.tab.c" break; case 403: /* block_call: command do_block */ #line 3228 "mrbgems/mruby-compiler/core/parse.y" { if (typen((yyvsp[-1].nd)->car) == NODE_YIELD) { yyerror(&(yylsp[-1]), p, "block given to yield"); } else { call_with_block(p, (yyvsp[-1].nd), (yyvsp[0].nd)); } (yyval.nd) = (yyvsp[-1].nd); } #line 9412 "mrbgems/mruby-compiler/core/y.tab.c" break; case 404: /* block_call: block_call call_op2 operation2 opt_paren_args */ #line 3238 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_call(p, (yyvsp[-3].nd), (yyvsp[-1].id), (yyvsp[0].nd), (yyvsp[-2].num)); } #line 9420 "mrbgems/mruby-compiler/core/y.tab.c" break; case 405: /* block_call: block_call call_op2 operation2 opt_paren_args brace_block */ #line 3242 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_call(p, (yyvsp[-4].nd), (yyvsp[-2].id), (yyvsp[-1].nd), (yyvsp[-3].num)); call_with_block(p, (yyval.nd), (yyvsp[0].nd)); } #line 9429 "mrbgems/mruby-compiler/core/y.tab.c" break; case 406: /* block_call: block_call call_op2 operation2 command_args do_block */ #line 3247 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_call(p, (yyvsp[-4].nd), (yyvsp[-2].id), (yyvsp[-1].nd), (yyvsp[-3].num)); call_with_block(p, (yyval.nd), (yyvsp[0].nd)); } #line 9438 "mrbgems/mruby-compiler/core/y.tab.c" break; case 407: /* method_call: operation paren_args */ #line 3254 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_fcall(p, (yyvsp[-1].id), (yyvsp[0].nd)); } #line 9446 "mrbgems/mruby-compiler/core/y.tab.c" break; case 408: /* method_call: primary_value call_op operation2 opt_paren_args */ #line 3258 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_call(p, (yyvsp[-3].nd), (yyvsp[-1].id), (yyvsp[0].nd), (yyvsp[-2].num)); } #line 9454 "mrbgems/mruby-compiler/core/y.tab.c" break; case 409: /* method_call: primary_value "::" operation2 paren_args */ #line 3262 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_call(p, (yyvsp[-3].nd), (yyvsp[-1].id), (yyvsp[0].nd), tCOLON2); } #line 9462 "mrbgems/mruby-compiler/core/y.tab.c" break; case 410: /* method_call: primary_value "::" operation3 */ #line 3266 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_call(p, (yyvsp[-2].nd), (yyvsp[0].id), 0, tCOLON2); } #line 9470 "mrbgems/mruby-compiler/core/y.tab.c" break; case 411: /* method_call: primary_value call_op paren_args */ #line 3270 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_call(p, (yyvsp[-2].nd), MRB_SYM_2(p->mrb, call), (yyvsp[0].nd), (yyvsp[-1].num)); } #line 9478 "mrbgems/mruby-compiler/core/y.tab.c" break; case 412: /* method_call: primary_value "::" paren_args */ #line 3274 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_call(p, (yyvsp[-2].nd), MRB_SYM_2(p->mrb, call), (yyvsp[0].nd), tCOLON2); } #line 9486 "mrbgems/mruby-compiler/core/y.tab.c" break; case 413: /* method_call: "'super'" paren_args */ #line 3278 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_super(p, (yyvsp[0].nd)); } #line 9494 "mrbgems/mruby-compiler/core/y.tab.c" break; case 414: /* method_call: "'super'" */ #line 3282 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_zsuper(p); } #line 9502 "mrbgems/mruby-compiler/core/y.tab.c" break; case 415: /* method_call: primary_value '[' opt_call_args ']' */ #line 3286 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_call(p, (yyvsp[-3].nd), intern_op(aref), (yyvsp[-1].nd), '.'); } #line 9510 "mrbgems/mruby-compiler/core/y.tab.c" break; case 416: /* @27: %empty */ #line 3292 "mrbgems/mruby-compiler/core/parse.y" { local_nest(p); nvars_nest(p); (yyval.num) = p->lineno; } #line 9520 "mrbgems/mruby-compiler/core/y.tab.c" break; case 417: /* brace_block: '{' @27 opt_block_param compstmt '}' */ #line 3299 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_block(p,(yyvsp[-2].nd),(yyvsp[-1].nd)); SET_LINENO((yyval.nd), (yyvsp[-3].num)); local_unnest(p); nvars_unnest(p); } #line 9531 "mrbgems/mruby-compiler/core/y.tab.c" break; case 418: /* @28: %empty */ #line 3306 "mrbgems/mruby-compiler/core/parse.y" { local_nest(p); nvars_nest(p); (yyval.num) = p->lineno; } #line 9541 "mrbgems/mruby-compiler/core/y.tab.c" break; case 419: /* brace_block: "'do'" @28 opt_block_param bodystmt "'end'" */ #line 3313 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_block(p,(yyvsp[-2].nd),(yyvsp[-1].nd)); SET_LINENO((yyval.nd), (yyvsp[-3].num)); local_unnest(p); nvars_unnest(p); } #line 9552 "mrbgems/mruby-compiler/core/y.tab.c" break; case 420: /* case_body: "'when'" args then compstmt cases */ #line 3324 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = cons(cons((yyvsp[-3].nd), (yyvsp[-1].nd)), (yyvsp[0].nd)); } #line 9560 "mrbgems/mruby-compiler/core/y.tab.c" break; case 421: /* cases: opt_else */ #line 3330 "mrbgems/mruby-compiler/core/parse.y" { if ((yyvsp[0].nd)) { (yyval.nd) = cons(cons(0, (yyvsp[0].nd)), 0); } else { (yyval.nd) = 0; } } #line 9573 "mrbgems/mruby-compiler/core/y.tab.c" break; case 423: /* opt_rescue: "'rescue'" exc_list exc_var then compstmt opt_rescue */ #line 3344 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list1(list3((yyvsp[-4].nd), (yyvsp[-3].nd), (yyvsp[-1].nd))); if ((yyvsp[0].nd)) (yyval.nd) = append((yyval.nd), (yyvsp[0].nd)); } #line 9582 "mrbgems/mruby-compiler/core/y.tab.c" break; case 425: /* exc_list: arg */ #line 3352 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list1((yyvsp[0].nd)); } #line 9590 "mrbgems/mruby-compiler/core/y.tab.c" break; case 428: /* exc_var: "=>" lhs */ #line 3360 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[0].nd); } #line 9598 "mrbgems/mruby-compiler/core/y.tab.c" break; case 430: /* opt_ensure: "'ensure'" compstmt */ #line 3367 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[0].nd); } #line 9606 "mrbgems/mruby-compiler/core/y.tab.c" break; case 437: /* string: string string_fragment */ #line 3381 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = concat_string(p, (yyvsp[-1].nd), (yyvsp[0].nd)); } #line 9614 "mrbgems/mruby-compiler/core/y.tab.c" break; case 440: /* string_fragment: "string literal" tSTRING */ #line 3389 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[0].nd); } #line 9622 "mrbgems/mruby-compiler/core/y.tab.c" break; case 441: /* string_fragment: "string literal" string_rep tSTRING */ #line 3393 "mrbgems/mruby-compiler/core/parse.y" { node *n = (yyvsp[-1].nd); if (intn((yyvsp[0].nd)->cdr->cdr) > 0) { n = push(n, (yyvsp[0].nd)); } else { cons_free((yyvsp[0].nd)); } (yyval.nd) = new_dstr(p, n); } #line 9637 "mrbgems/mruby-compiler/core/y.tab.c" break; case 443: /* string_rep: string_rep string_interp */ #line 3407 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = append((yyvsp[-1].nd), (yyvsp[0].nd)); } #line 9645 "mrbgems/mruby-compiler/core/y.tab.c" break; case 444: /* string_interp: tSTRING_MID */ #line 3413 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list1((yyvsp[0].nd)); } #line 9653 "mrbgems/mruby-compiler/core/y.tab.c" break; case 445: /* @29: %empty */ #line 3417 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = push_strterm(p); } #line 9661 "mrbgems/mruby-compiler/core/y.tab.c" break; case 446: /* string_interp: tSTRING_PART @29 compstmt '}' */ #line 3422 "mrbgems/mruby-compiler/core/parse.y" { pop_strterm(p,(yyvsp[-2].nd)); (yyval.nd) = list2((yyvsp[-3].nd), (yyvsp[-1].nd)); } #line 9670 "mrbgems/mruby-compiler/core/y.tab.c" break; case 447: /* string_interp: tLITERAL_DELIM */ #line 3427 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list1(new_literal_delim(p)); } #line 9678 "mrbgems/mruby-compiler/core/y.tab.c" break; case 448: /* string_interp: tHD_LITERAL_DELIM heredoc_bodies */ #line 3431 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list1(new_literal_delim(p)); } #line 9686 "mrbgems/mruby-compiler/core/y.tab.c" break; case 449: /* xstring: tXSTRING_BEG tXSTRING */ #line 3437 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[0].nd); } #line 9694 "mrbgems/mruby-compiler/core/y.tab.c" break; case 450: /* xstring: tXSTRING_BEG string_rep tXSTRING */ #line 3441 "mrbgems/mruby-compiler/core/parse.y" { node *n = (yyvsp[-1].nd); if (intn((yyvsp[0].nd)->cdr->cdr) > 0) { n = push(n, (yyvsp[0].nd)); } else { cons_free((yyvsp[0].nd)); } (yyval.nd) = new_dxstr(p, n); } #line 9709 "mrbgems/mruby-compiler/core/y.tab.c" break; case 451: /* regexp: tREGEXP_BEG tREGEXP */ #line 3454 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[0].nd); } #line 9717 "mrbgems/mruby-compiler/core/y.tab.c" break; case 452: /* regexp: tREGEXP_BEG string_rep tREGEXP */ #line 3458 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_dregx(p, (yyvsp[-1].nd), (yyvsp[0].nd)); } #line 9725 "mrbgems/mruby-compiler/core/y.tab.c" break; case 456: /* heredoc_body: tHEREDOC_END */ #line 3471 "mrbgems/mruby-compiler/core/parse.y" { parser_heredoc_info *info = parsing_heredoc_info(p); info->doc = push(info->doc, new_str(p, "", 0)); heredoc_end(p); } #line 9735 "mrbgems/mruby-compiler/core/y.tab.c" break; case 457: /* heredoc_body: heredoc_string_rep tHEREDOC_END */ #line 3477 "mrbgems/mruby-compiler/core/parse.y" { heredoc_end(p); } #line 9743 "mrbgems/mruby-compiler/core/y.tab.c" break; case 460: /* heredoc_string_interp: tHD_STRING_MID */ #line 3487 "mrbgems/mruby-compiler/core/parse.y" { parser_heredoc_info *info = parsing_heredoc_info(p); info->doc = push(info->doc, (yyvsp[0].nd)); heredoc_treat_nextline(p); } #line 9753 "mrbgems/mruby-compiler/core/y.tab.c" break; case 461: /* @30: %empty */ #line 3493 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = push_strterm(p); } #line 9761 "mrbgems/mruby-compiler/core/y.tab.c" break; case 462: /* heredoc_string_interp: tHD_STRING_PART @30 compstmt '}' */ #line 3498 "mrbgems/mruby-compiler/core/parse.y" { pop_strterm(p, (yyvsp[-2].nd)); parser_heredoc_info *info = parsing_heredoc_info(p); info->doc = push(push(info->doc, (yyvsp[-3].nd)), (yyvsp[-1].nd)); } #line 9771 "mrbgems/mruby-compiler/core/y.tab.c" break; case 463: /* words: tWORDS_BEG tSTRING */ #line 3506 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_words(p, list1((yyvsp[0].nd))); } #line 9779 "mrbgems/mruby-compiler/core/y.tab.c" break; case 464: /* words: tWORDS_BEG string_rep tSTRING */ #line 3510 "mrbgems/mruby-compiler/core/parse.y" { node *n = (yyvsp[-1].nd); if (intn((yyvsp[0].nd)->cdr->cdr) > 0) { n = push(n, (yyvsp[0].nd)); } else { cons_free((yyvsp[0].nd)); } (yyval.nd) = new_words(p, n); } #line 9794 "mrbgems/mruby-compiler/core/y.tab.c" break; case 465: /* symbol: basic_symbol */ #line 3524 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_sym(p, (yyvsp[0].id)); } #line 9802 "mrbgems/mruby-compiler/core/y.tab.c" break; case 466: /* symbol: "symbol" "string literal" string_rep tSTRING */ #line 3528 "mrbgems/mruby-compiler/core/parse.y" { node *n = (yyvsp[-1].nd); p->lstate = EXPR_ENDARG; if (intn((yyvsp[0].nd)->cdr->cdr) > 0) { n = push(n, (yyvsp[0].nd)); } else { cons_free((yyvsp[0].nd)); } (yyval.nd) = new_dsym(p, new_dstr(p, n)); } #line 9818 "mrbgems/mruby-compiler/core/y.tab.c" break; case 467: /* symbol: "symbol" "numbered parameter" */ #line 3540 "mrbgems/mruby-compiler/core/parse.y" { mrb_sym sym = intern_numparam((yyvsp[0].num)); (yyval.nd) = new_sym(p, sym); } #line 9827 "mrbgems/mruby-compiler/core/y.tab.c" break; case 468: /* basic_symbol: "symbol" sym */ #line 3547 "mrbgems/mruby-compiler/core/parse.y" { p->lstate = EXPR_END; (yyval.id) = (yyvsp[0].id); } #line 9836 "mrbgems/mruby-compiler/core/y.tab.c" break; case 473: /* sym: tSTRING */ #line 3558 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = new_strsym(p, (yyvsp[0].nd)); } #line 9844 "mrbgems/mruby-compiler/core/y.tab.c" break; case 474: /* sym: "string literal" tSTRING */ #line 3562 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = new_strsym(p, (yyvsp[0].nd)); } #line 9852 "mrbgems/mruby-compiler/core/y.tab.c" break; case 475: /* symbols: tSYMBOLS_BEG tSTRING */ #line 3568 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_symbols(p, list1((yyvsp[0].nd))); } #line 9860 "mrbgems/mruby-compiler/core/y.tab.c" break; case 476: /* symbols: tSYMBOLS_BEG string_rep tSTRING */ #line 3572 "mrbgems/mruby-compiler/core/parse.y" { node *n = (yyvsp[-1].nd); if (intn((yyvsp[0].nd)->cdr->cdr) > 0) { n = push(n, (yyvsp[0].nd)); } (yyval.nd) = new_symbols(p, n); } #line 9872 "mrbgems/mruby-compiler/core/y.tab.c" break; case 479: /* numeric: tUMINUS_NUM "integer literal" */ #line 3584 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_negate(p, (yyvsp[0].nd)); } #line 9880 "mrbgems/mruby-compiler/core/y.tab.c" break; case 480: /* numeric: tUMINUS_NUM "float literal" */ #line 3588 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_negate(p, (yyvsp[0].nd)); } #line 9888 "mrbgems/mruby-compiler/core/y.tab.c" break; case 481: /* variable: "local variable or method" */ #line 3594 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_lvar(p, (yyvsp[0].id)); } #line 9896 "mrbgems/mruby-compiler/core/y.tab.c" break; case 482: /* variable: "instance variable" */ #line 3598 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_ivar(p, (yyvsp[0].id)); } #line 9904 "mrbgems/mruby-compiler/core/y.tab.c" break; case 483: /* variable: "global variable" */ #line 3602 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_gvar(p, (yyvsp[0].id)); } #line 9912 "mrbgems/mruby-compiler/core/y.tab.c" break; case 484: /* variable: "class variable" */ #line 3606 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_cvar(p, (yyvsp[0].id)); } #line 9920 "mrbgems/mruby-compiler/core/y.tab.c" break; case 485: /* variable: "constant" */ #line 3610 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_const(p, (yyvsp[0].id)); } #line 9928 "mrbgems/mruby-compiler/core/y.tab.c" break; case 486: /* var_lhs: variable */ #line 3616 "mrbgems/mruby-compiler/core/parse.y" { assignable(p, (yyvsp[0].nd)); } #line 9936 "mrbgems/mruby-compiler/core/y.tab.c" break; case 487: /* var_lhs: "numbered parameter" */ #line 3620 "mrbgems/mruby-compiler/core/parse.y" { yyerror(&(yylsp[0]), p, "can't assign to numbered parameter"); } #line 9944 "mrbgems/mruby-compiler/core/y.tab.c" break; case 488: /* var_ref: variable */ #line 3626 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = var_reference(p, (yyvsp[0].nd)); } #line 9952 "mrbgems/mruby-compiler/core/y.tab.c" break; case 489: /* var_ref: "numbered parameter" */ #line 3630 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_nvar(p, (yyvsp[0].num)); } #line 9960 "mrbgems/mruby-compiler/core/y.tab.c" break; case 490: /* var_ref: "'nil'" */ #line 3634 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_nil(p); } #line 9968 "mrbgems/mruby-compiler/core/y.tab.c" break; case 491: /* var_ref: "'self'" */ #line 3638 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_self(p); } #line 9976 "mrbgems/mruby-compiler/core/y.tab.c" break; case 492: /* var_ref: "'true'" */ #line 3642 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_true(p); } #line 9984 "mrbgems/mruby-compiler/core/y.tab.c" break; case 493: /* var_ref: "'false'" */ #line 3646 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_false(p); } #line 9992 "mrbgems/mruby-compiler/core/y.tab.c" break; case 494: /* var_ref: "'__FILE__'" */ #line 3650 "mrbgems/mruby-compiler/core/parse.y" { const char *fn = mrb_sym_name_len(p->mrb, p->filename_sym, NULL); if (!fn) { fn = "(null)"; } (yyval.nd) = new_str(p, fn, strlen(fn)); } #line 10004 "mrbgems/mruby-compiler/core/y.tab.c" break; case 495: /* var_ref: "'__LINE__'" */ #line 3658 "mrbgems/mruby-compiler/core/parse.y" { char buf[16]; dump_int(p->lineno, buf); (yyval.nd) = new_int(p, buf, 10, 0); } #line 10015 "mrbgems/mruby-compiler/core/y.tab.c" break; case 496: /* var_ref: "'__ENCODING__'" */ #line 3665 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_fcall(p, MRB_SYM_2(p->mrb, __ENCODING__), 0); } #line 10023 "mrbgems/mruby-compiler/core/y.tab.c" break; case 499: /* superclass: %empty */ #line 3675 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = 0; } #line 10031 "mrbgems/mruby-compiler/core/y.tab.c" break; case 500: /* $@31: %empty */ #line 3679 "mrbgems/mruby-compiler/core/parse.y" { p->lstate = EXPR_BEG; p->cmd_start = TRUE; } #line 10040 "mrbgems/mruby-compiler/core/y.tab.c" break; case 501: /* superclass: '<' $@31 expr_value term */ #line 3684 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-1].nd); } #line 10048 "mrbgems/mruby-compiler/core/y.tab.c" break; case 504: /* f_arglist_paren: '(' f_args rparen */ #line 3700 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-1].nd); p->lstate = EXPR_BEG; p->cmd_start = TRUE; } #line 10058 "mrbgems/mruby-compiler/core/y.tab.c" break; case 505: /* f_arglist_paren: '(' f_arg ',' tBDOT3 rparen */ #line 3706 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args_dots(p, (yyvsp[-3].nd)); } #line 10066 "mrbgems/mruby-compiler/core/y.tab.c" break; case 506: /* f_arglist_paren: '(' tBDOT3 rparen */ #line 3710 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args_dots(p, 0); } #line 10074 "mrbgems/mruby-compiler/core/y.tab.c" break; case 508: /* f_arglist: f_args term */ #line 3717 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-1].nd); } #line 10082 "mrbgems/mruby-compiler/core/y.tab.c" break; case 509: /* f_arglist: f_arg ',' tBDOT3 term */ #line 3721 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args_dots(p, (yyvsp[-3].nd)); } #line 10090 "mrbgems/mruby-compiler/core/y.tab.c" break; case 510: /* f_arglist: "..." term */ #line 3725 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args_dots(p, 0); } #line 10098 "mrbgems/mruby-compiler/core/y.tab.c" break; case 511: /* f_label: "local variable or method" "label" */ #line 3731 "mrbgems/mruby-compiler/core/parse.y" { local_nest(p); } #line 10106 "mrbgems/mruby-compiler/core/y.tab.c" break; case 512: /* f_label: "numbered parameter" "label" */ #line 3735 "mrbgems/mruby-compiler/core/parse.y" { local_nest(p); } #line 10114 "mrbgems/mruby-compiler/core/y.tab.c" break; case 513: /* f_kw: f_label arg */ #line 3741 "mrbgems/mruby-compiler/core/parse.y" { void_expr_error(p, (yyvsp[0].nd)); (yyval.nd) = new_kw_arg(p, (yyvsp[-1].id), cons((yyvsp[0].nd), locals_node(p))); local_unnest(p); } #line 10124 "mrbgems/mruby-compiler/core/y.tab.c" break; case 514: /* f_kw: f_label */ #line 3747 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_kw_arg(p, (yyvsp[0].id), 0); local_unnest(p); } #line 10133 "mrbgems/mruby-compiler/core/y.tab.c" break; case 515: /* f_block_kw: f_label primary_value */ #line 3754 "mrbgems/mruby-compiler/core/parse.y" { void_expr_error(p, (yyvsp[0].nd)); (yyval.nd) = new_kw_arg(p, (yyvsp[-1].id), cons((yyvsp[0].nd), locals_node(p))); local_unnest(p); } #line 10143 "mrbgems/mruby-compiler/core/y.tab.c" break; case 516: /* f_block_kw: f_label */ #line 3760 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_kw_arg(p, (yyvsp[0].id), 0); local_unnest(p); } #line 10152 "mrbgems/mruby-compiler/core/y.tab.c" break; case 517: /* f_block_kwarg: f_block_kw */ #line 3767 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list1((yyvsp[0].nd)); } #line 10160 "mrbgems/mruby-compiler/core/y.tab.c" break; case 518: /* f_block_kwarg: f_block_kwarg ',' f_block_kw */ #line 3771 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = push((yyvsp[-2].nd), (yyvsp[0].nd)); } #line 10168 "mrbgems/mruby-compiler/core/y.tab.c" break; case 519: /* f_kwarg: f_kw */ #line 3777 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list1((yyvsp[0].nd)); } #line 10176 "mrbgems/mruby-compiler/core/y.tab.c" break; case 520: /* f_kwarg: f_kwarg ',' f_kw */ #line 3781 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = push((yyvsp[-2].nd), (yyvsp[0].nd)); } #line 10184 "mrbgems/mruby-compiler/core/y.tab.c" break; case 523: /* f_kwrest: kwrest_mark "local variable or method" */ #line 3791 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_kw_rest_args(p, (yyvsp[0].id)); } #line 10192 "mrbgems/mruby-compiler/core/y.tab.c" break; case 524: /* f_kwrest: kwrest_mark */ #line 3795 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_kw_rest_args(p, 0); } #line 10200 "mrbgems/mruby-compiler/core/y.tab.c" break; case 525: /* args_tail: f_kwarg ',' f_kwrest opt_f_block_arg */ #line 3801 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args_tail(p, (yyvsp[-3].nd), (yyvsp[-1].nd), (yyvsp[0].id)); } #line 10208 "mrbgems/mruby-compiler/core/y.tab.c" break; case 526: /* args_tail: f_kwarg opt_f_block_arg */ #line 3805 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args_tail(p, (yyvsp[-1].nd), 0, (yyvsp[0].id)); } #line 10216 "mrbgems/mruby-compiler/core/y.tab.c" break; case 527: /* args_tail: f_kwrest opt_f_block_arg */ #line 3809 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args_tail(p, 0, (yyvsp[-1].nd), (yyvsp[0].id)); } #line 10224 "mrbgems/mruby-compiler/core/y.tab.c" break; case 528: /* args_tail: f_block_arg */ #line 3813 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args_tail(p, 0, 0, (yyvsp[0].id)); } #line 10232 "mrbgems/mruby-compiler/core/y.tab.c" break; case 529: /* opt_args_tail: ',' args_tail */ #line 3819 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[0].nd); } #line 10240 "mrbgems/mruby-compiler/core/y.tab.c" break; case 530: /* opt_args_tail: %empty */ #line 3823 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args_tail(p, 0, 0, 0); } #line 10248 "mrbgems/mruby-compiler/core/y.tab.c" break; case 531: /* f_args: f_arg ',' f_optarg ',' f_rest_arg opt_args_tail */ #line 3829 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, (yyvsp[-5].nd), (yyvsp[-3].nd), (yyvsp[-1].id), 0, (yyvsp[0].nd)); } #line 10256 "mrbgems/mruby-compiler/core/y.tab.c" break; case 532: /* f_args: f_arg ',' f_optarg ',' f_rest_arg ',' f_arg opt_args_tail */ #line 3833 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, (yyvsp[-7].nd), (yyvsp[-5].nd), (yyvsp[-3].id), (yyvsp[-1].nd), (yyvsp[0].nd)); } #line 10264 "mrbgems/mruby-compiler/core/y.tab.c" break; case 533: /* f_args: f_arg ',' f_optarg opt_args_tail */ #line 3837 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, (yyvsp[-3].nd), (yyvsp[-1].nd), 0, 0, (yyvsp[0].nd)); } #line 10272 "mrbgems/mruby-compiler/core/y.tab.c" break; case 534: /* f_args: f_arg ',' f_optarg ',' f_arg opt_args_tail */ #line 3841 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, (yyvsp[-5].nd), (yyvsp[-3].nd), 0, (yyvsp[-1].nd), (yyvsp[0].nd)); } #line 10280 "mrbgems/mruby-compiler/core/y.tab.c" break; case 535: /* f_args: f_arg ',' f_rest_arg opt_args_tail */ #line 3845 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, (yyvsp[-3].nd), 0, (yyvsp[-1].id), 0, (yyvsp[0].nd)); } #line 10288 "mrbgems/mruby-compiler/core/y.tab.c" break; case 536: /* f_args: f_arg ',' f_rest_arg ',' f_arg opt_args_tail */ #line 3849 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, (yyvsp[-5].nd), 0, (yyvsp[-3].id), (yyvsp[-1].nd), (yyvsp[0].nd)); } #line 10296 "mrbgems/mruby-compiler/core/y.tab.c" break; case 537: /* f_args: f_arg opt_args_tail */ #line 3853 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, (yyvsp[-1].nd), 0, 0, 0, (yyvsp[0].nd)); } #line 10304 "mrbgems/mruby-compiler/core/y.tab.c" break; case 538: /* f_args: f_optarg ',' f_rest_arg opt_args_tail */ #line 3857 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, 0, (yyvsp[-3].nd), (yyvsp[-1].id), 0, (yyvsp[0].nd)); } #line 10312 "mrbgems/mruby-compiler/core/y.tab.c" break; case 539: /* f_args: f_optarg ',' f_rest_arg ',' f_arg opt_args_tail */ #line 3861 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, 0, (yyvsp[-5].nd), (yyvsp[-3].id), (yyvsp[-1].nd), (yyvsp[0].nd)); } #line 10320 "mrbgems/mruby-compiler/core/y.tab.c" break; case 540: /* f_args: f_optarg opt_args_tail */ #line 3865 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, 0, (yyvsp[-1].nd), 0, 0, (yyvsp[0].nd)); } #line 10328 "mrbgems/mruby-compiler/core/y.tab.c" break; case 541: /* f_args: f_optarg ',' f_arg opt_args_tail */ #line 3869 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, 0, (yyvsp[-3].nd), 0, (yyvsp[-1].nd), (yyvsp[0].nd)); } #line 10336 "mrbgems/mruby-compiler/core/y.tab.c" break; case 542: /* f_args: f_rest_arg opt_args_tail */ #line 3873 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, 0, 0, (yyvsp[-1].id), 0, (yyvsp[0].nd)); } #line 10344 "mrbgems/mruby-compiler/core/y.tab.c" break; case 543: /* f_args: f_rest_arg ',' f_arg opt_args_tail */ #line 3877 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, 0, 0, (yyvsp[-3].id), (yyvsp[-1].nd), (yyvsp[0].nd)); } #line 10352 "mrbgems/mruby-compiler/core/y.tab.c" break; case 544: /* f_args: args_tail */ #line 3881 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_args(p, 0, 0, 0, 0, (yyvsp[0].nd)); } #line 10360 "mrbgems/mruby-compiler/core/y.tab.c" break; case 545: /* f_args: %empty */ #line 3885 "mrbgems/mruby-compiler/core/parse.y" { local_add_f(p, 0); (yyval.nd) = new_args(p, 0, 0, 0, 0, 0); } #line 10369 "mrbgems/mruby-compiler/core/y.tab.c" break; case 546: /* f_bad_arg: "constant" */ #line 3892 "mrbgems/mruby-compiler/core/parse.y" { yyerror(&(yylsp[0]), p, "formal argument cannot be a constant"); (yyval.nd) = 0; } #line 10378 "mrbgems/mruby-compiler/core/y.tab.c" break; case 547: /* f_bad_arg: "instance variable" */ #line 3897 "mrbgems/mruby-compiler/core/parse.y" { yyerror(&(yylsp[0]), p, "formal argument cannot be an instance variable"); (yyval.nd) = 0; } #line 10387 "mrbgems/mruby-compiler/core/y.tab.c" break; case 548: /* f_bad_arg: "global variable" */ #line 3902 "mrbgems/mruby-compiler/core/parse.y" { yyerror(&(yylsp[0]), p, "formal argument cannot be a global variable"); (yyval.nd) = 0; } #line 10396 "mrbgems/mruby-compiler/core/y.tab.c" break; case 549: /* f_bad_arg: "class variable" */ #line 3907 "mrbgems/mruby-compiler/core/parse.y" { yyerror(&(yylsp[0]), p, "formal argument cannot be a class variable"); (yyval.nd) = 0; } #line 10405 "mrbgems/mruby-compiler/core/y.tab.c" break; case 550: /* f_bad_arg: "numbered parameter" */ #line 3912 "mrbgems/mruby-compiler/core/parse.y" { yyerror(&(yylsp[0]), p, "formal argument cannot be a numbered parameter"); (yyval.nd) = 0; } #line 10414 "mrbgems/mruby-compiler/core/y.tab.c" break; case 551: /* f_norm_arg: f_bad_arg */ #line 3919 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = 0; } #line 10422 "mrbgems/mruby-compiler/core/y.tab.c" break; case 552: /* f_norm_arg: "local variable or method" */ #line 3923 "mrbgems/mruby-compiler/core/parse.y" { local_add_f(p, (yyvsp[0].id)); (yyval.id) = (yyvsp[0].id); } #line 10431 "mrbgems/mruby-compiler/core/y.tab.c" break; case 553: /* f_arg_item: f_norm_arg */ #line 3930 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_arg(p, (yyvsp[0].id)); } #line 10439 "mrbgems/mruby-compiler/core/y.tab.c" break; case 554: /* @32: %empty */ #line 3934 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = local_switch(p); } #line 10447 "mrbgems/mruby-compiler/core/y.tab.c" break; case 555: /* f_arg_item: tLPAREN @32 f_margs rparen */ #line 3938 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = new_masgn_param(p, (yyvsp[-1].nd), p->locals->car); local_resume(p, (yyvsp[-2].nd)); local_add_f(p, 0); } #line 10457 "mrbgems/mruby-compiler/core/y.tab.c" break; case 556: /* f_arg: f_arg_item */ #line 3946 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list1((yyvsp[0].nd)); } #line 10465 "mrbgems/mruby-compiler/core/y.tab.c" break; case 557: /* f_arg: f_arg ',' f_arg_item */ #line 3950 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = push((yyvsp[-2].nd), (yyvsp[0].nd)); } #line 10473 "mrbgems/mruby-compiler/core/y.tab.c" break; case 558: /* f_opt_asgn: "local variable or method" '=' */ #line 3956 "mrbgems/mruby-compiler/core/parse.y" { local_add_f(p, (yyvsp[-1].id)); local_nest(p); (yyval.id) = (yyvsp[-1].id); } #line 10483 "mrbgems/mruby-compiler/core/y.tab.c" break; case 559: /* f_opt: f_opt_asgn arg */ #line 3964 "mrbgems/mruby-compiler/core/parse.y" { void_expr_error(p, (yyvsp[0].nd)); (yyval.nd) = cons(nsym((yyvsp[-1].id)), cons((yyvsp[0].nd), locals_node(p))); local_unnest(p); } #line 10493 "mrbgems/mruby-compiler/core/y.tab.c" break; case 560: /* f_block_opt: f_opt_asgn primary_value */ #line 3972 "mrbgems/mruby-compiler/core/parse.y" { void_expr_error(p, (yyvsp[0].nd)); (yyval.nd) = cons(nsym((yyvsp[-1].id)), cons((yyvsp[0].nd), locals_node(p))); local_unnest(p); } #line 10503 "mrbgems/mruby-compiler/core/y.tab.c" break; case 561: /* f_block_optarg: f_block_opt */ #line 3980 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list1((yyvsp[0].nd)); } #line 10511 "mrbgems/mruby-compiler/core/y.tab.c" break; case 562: /* f_block_optarg: f_block_optarg ',' f_block_opt */ #line 3984 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = push((yyvsp[-2].nd), (yyvsp[0].nd)); } #line 10519 "mrbgems/mruby-compiler/core/y.tab.c" break; case 563: /* f_optarg: f_opt */ #line 3990 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list1((yyvsp[0].nd)); } #line 10527 "mrbgems/mruby-compiler/core/y.tab.c" break; case 564: /* f_optarg: f_optarg ',' f_opt */ #line 3994 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = push((yyvsp[-2].nd), (yyvsp[0].nd)); } #line 10535 "mrbgems/mruby-compiler/core/y.tab.c" break; case 567: /* f_rest_arg: restarg_mark "local variable or method" */ #line 4004 "mrbgems/mruby-compiler/core/parse.y" { local_add_f(p, (yyvsp[0].id)); (yyval.id) = (yyvsp[0].id); } #line 10544 "mrbgems/mruby-compiler/core/y.tab.c" break; case 568: /* f_rest_arg: restarg_mark */ #line 4009 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(mul); local_add_f(p, (yyval.id)); } #line 10553 "mrbgems/mruby-compiler/core/y.tab.c" break; case 571: /* f_block_arg: blkarg_mark "local variable or method" */ #line 4020 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = (yyvsp[0].id); } #line 10561 "mrbgems/mruby-compiler/core/y.tab.c" break; case 572: /* f_block_arg: blkarg_mark */ #line 4024 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = intern_op(and); } #line 10569 "mrbgems/mruby-compiler/core/y.tab.c" break; case 573: /* opt_f_block_arg: ',' f_block_arg */ #line 4030 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = (yyvsp[0].id); } #line 10577 "mrbgems/mruby-compiler/core/y.tab.c" break; case 574: /* opt_f_block_arg: none */ #line 4034 "mrbgems/mruby-compiler/core/parse.y" { (yyval.id) = 0; } #line 10585 "mrbgems/mruby-compiler/core/y.tab.c" break; case 575: /* singleton: var_ref */ #line 4040 "mrbgems/mruby-compiler/core/parse.y" { prohibit_literals(p, (yyvsp[0].nd)); (yyval.nd) = (yyvsp[0].nd); if (!(yyval.nd)) (yyval.nd) = new_nil(p); } #line 10595 "mrbgems/mruby-compiler/core/y.tab.c" break; case 576: /* $@33: %empty */ #line 4045 "mrbgems/mruby-compiler/core/parse.y" {p->lstate = EXPR_BEG;} #line 10601 "mrbgems/mruby-compiler/core/y.tab.c" break; case 577: /* singleton: '(' $@33 expr rparen */ #line 4046 "mrbgems/mruby-compiler/core/parse.y" { prohibit_literals(p, (yyvsp[-1].nd)); (yyval.nd) = (yyvsp[-1].nd); } #line 10610 "mrbgems/mruby-compiler/core/y.tab.c" break; case 579: /* assoc_list: assocs trailer */ #line 4054 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = (yyvsp[-1].nd); } #line 10618 "mrbgems/mruby-compiler/core/y.tab.c" break; case 580: /* assocs: assoc */ #line 4060 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = list1((yyvsp[0].nd)); NODE_LINENO((yyval.nd), (yyvsp[0].nd)); } #line 10627 "mrbgems/mruby-compiler/core/y.tab.c" break; case 581: /* assocs: assocs comma assoc */ #line 4065 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = push((yyvsp[-2].nd), (yyvsp[0].nd)); } #line 10635 "mrbgems/mruby-compiler/core/y.tab.c" break; case 582: /* assoc: arg "=>" arg */ #line 4071 "mrbgems/mruby-compiler/core/parse.y" { void_expr_error(p, (yyvsp[-2].nd)); void_expr_error(p, (yyvsp[0].nd)); (yyval.nd) = cons((yyvsp[-2].nd), (yyvsp[0].nd)); } #line 10645 "mrbgems/mruby-compiler/core/y.tab.c" break; case 583: /* assoc: "local variable or method" "label" arg */ #line 4077 "mrbgems/mruby-compiler/core/parse.y" { void_expr_error(p, (yyvsp[0].nd)); (yyval.nd) = cons(new_sym(p, (yyvsp[-2].id)), (yyvsp[0].nd)); } #line 10654 "mrbgems/mruby-compiler/core/y.tab.c" break; case 584: /* assoc: "local variable or method" "label" */ #line 4082 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = cons(new_sym(p, (yyvsp[-1].id)), label_reference(p, (yyvsp[-1].id))); } #line 10662 "mrbgems/mruby-compiler/core/y.tab.c" break; case 585: /* assoc: "numbered parameter" "label" */ #line 4086 "mrbgems/mruby-compiler/core/parse.y" { mrb_sym sym = intern_numparam((yyvsp[-1].num)); (yyval.nd) = cons(new_sym(p, sym), label_reference(p, sym)); } #line 10671 "mrbgems/mruby-compiler/core/y.tab.c" break; case 586: /* assoc: "numbered parameter" "label" arg */ #line 4091 "mrbgems/mruby-compiler/core/parse.y" { void_expr_error(p, (yyvsp[0].nd)); (yyval.nd) = cons(new_sym(p, intern_numparam((yyvsp[-2].num))), (yyvsp[0].nd)); } #line 10680 "mrbgems/mruby-compiler/core/y.tab.c" break; case 587: /* assoc: string_fragment "label" arg */ #line 4096 "mrbgems/mruby-compiler/core/parse.y" { void_expr_error(p, (yyvsp[0].nd)); if (typen((yyvsp[-2].nd)->car) == NODE_DSTR) { (yyval.nd) = cons(new_dsym(p, (yyvsp[-2].nd)), (yyvsp[0].nd)); } else { (yyval.nd) = cons(new_sym(p, new_strsym(p, (yyvsp[-2].nd))), (yyvsp[0].nd)); } } #line 10694 "mrbgems/mruby-compiler/core/y.tab.c" break; case 588: /* assoc: "**" arg */ #line 4106 "mrbgems/mruby-compiler/core/parse.y" { void_expr_error(p, (yyvsp[0].nd)); (yyval.nd) = cons(new_kw_rest_args(p, 0), (yyvsp[0].nd)); } #line 10703 "mrbgems/mruby-compiler/core/y.tab.c" break; case 589: /* assoc: "**" */ #line 4111 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = cons(new_kw_rest_args(p, 0), new_lvar(p, intern_op(pow))); } #line 10711 "mrbgems/mruby-compiler/core/y.tab.c" break; case 602: /* call_op: '.' */ #line 4137 "mrbgems/mruby-compiler/core/parse.y" { (yyval.num) = '.'; } #line 10719 "mrbgems/mruby-compiler/core/y.tab.c" break; case 603: /* call_op: "&." */ #line 4141 "mrbgems/mruby-compiler/core/parse.y" { (yyval.num) = 0; } #line 10727 "mrbgems/mruby-compiler/core/y.tab.c" break; case 605: /* call_op2: "::" */ #line 4148 "mrbgems/mruby-compiler/core/parse.y" { (yyval.num) = tCOLON2; } #line 10735 "mrbgems/mruby-compiler/core/y.tab.c" break; case 614: /* term: ';' */ #line 4169 "mrbgems/mruby-compiler/core/parse.y" {yyerrok;} #line 10741 "mrbgems/mruby-compiler/core/y.tab.c" break; case 616: /* nl: '\n' */ #line 4174 "mrbgems/mruby-compiler/core/parse.y" { p->lineno += (yyvsp[0].num); p->column = 0; } #line 10750 "mrbgems/mruby-compiler/core/y.tab.c" break; case 620: /* none: %empty */ #line 4186 "mrbgems/mruby-compiler/core/parse.y" { (yyval.nd) = 0; } #line 10758 "mrbgems/mruby-compiler/core/y.tab.c" break; #line 10762 "mrbgems/mruby-compiler/core/y.tab.c" default: break; } /* User semantic actions sometimes alter yychar, and that requires that yytoken be updated with the new translation. We take the approach of translating immediately before every use of yytoken. One alternative is translating here after every semantic action, but that translation would be missed if the semantic action invokes YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an incorrect destructor might then be invoked immediately. In the case of YYERROR or YYBACKUP, subsequent parser actions might lead to an incorrect destructor call or verbose syntax error message before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc, p); YYPOPSTACK (yylen); yylen = 0; *++yyvsp = yyval; *++yylsp = yyloc; /* Now 'shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ { const int yylhs = yyr1[yyn] - YYNTOKENS; const int yyi = yypgoto[yylhs] + *yyssp; yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp ? yytable[yyi] : yydefgoto[yylhs]); } goto yynewstate; /*--------------------------------------. | yyerrlab -- here on detecting error. | `--------------------------------------*/ yyerrlab: /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; { yypcontext_t yyctx = {yyssp, yytoken, &yylloc}; char const *yymsgp = YY_("syntax error"); int yysyntax_error_status; yysyntax_error_status = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx, p); if (yysyntax_error_status == 0) yymsgp = yymsg; else if (yysyntax_error_status == -1) { if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = YY_CAST (char *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); if (yymsg) { yysyntax_error_status = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx, p); yymsgp = yymsg; } else { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; yysyntax_error_status = YYENOMEM; } } yyerror (&yylloc, p, yymsgp); if (yysyntax_error_status == YYENOMEM) YYNOMEM; } } yyerror_range[1] = yylloc; if (yyerrstatus == 3) { /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval, &yylloc, p); yychar = YYEMPTY; } } /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (0) YYERROR; ++yynerrs; /* Do not reclaim the symbols of the rule whose action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp, p); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ /* Pop stack until we find a state that shifts the error token. */ for (;;) { yyn = yypact[yystate]; if (!yypact_value_is_default (yyn)) { yyn += YYSYMBOL_YYerror; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yyerror_range[1] = *yylsp; yydestruct ("Error: popping", YY_ACCESSING_SYMBOL (yystate), yyvsp, yylsp, p); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp, p); } YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END yyerror_range[2] = yylloc; ++yylsp; YYLLOC_DEFAULT (*yylsp, yyerror_range, 2); /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp, p); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturnlab; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturnlab; /*-----------------------------------------------------------. | yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here. | `-----------------------------------------------------------*/ yyexhaustedlab: yyerror (&yylloc, p, YY_("memory exhausted")); yyresult = 2; goto yyreturnlab; /*----------------------------------------------------------. | yyreturnlab -- parsing is finished, clean up and return. | `----------------------------------------------------------*/ yyreturnlab: if (yychar != YYEMPTY) { /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = YYTRANSLATE (yychar); yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval, &yylloc, p); } /* Do not reclaim the symbols of the rule whose action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp, p); while (yyssp != yyss) { yydestruct ("Cleanup: popping", YY_ACCESSING_SYMBOL (+*yyssp), yyvsp, yylsp, p); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); return yyresult; } #line 4190 "mrbgems/mruby-compiler/core/parse.y" #define pylval (*((YYSTYPE*)(p->ylval))) static void yyerror(void *lp, parser_state *p, const char *s) { char* c; size_t n; if (! p->capture_errors) { #ifndef MRB_NO_STDIO if (p->filename_sym) { const char *filename = mrb_sym_name_len(p->mrb, p->filename_sym, NULL); fprintf(stderr, "%s:%d:%d: %s\n", filename, p->lineno, p->column, s); } else { fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s); } #endif } else if (p->nerr < sizeof(p->error_buffer) / sizeof(p->error_buffer[0])) { n = strlen(s); c = (char*)parser_palloc(p, n + 1); memcpy(c, s, n + 1); p->error_buffer[p->nerr].message = c; p->error_buffer[p->nerr].lineno = p->lineno; p->error_buffer[p->nerr].column = p->column; } p->nerr++; } static void yyerror_c(parser_state *p, const char *msg, char c) { char buf[256]; strncpy(buf, msg, sizeof(buf) - 2); buf[sizeof(buf) - 2] = '\0'; strncat(buf, &c, 1); yyerror(NULL, p, buf); } static void yywarning(parser_state *p, const char *s) { char* c; size_t n; if (! p->capture_errors) { #ifndef MRB_NO_STDIO if (p->filename_sym) { const char *filename = mrb_sym_name_len(p->mrb, p->filename_sym, NULL); fprintf(stderr, "%s:%d:%d: warning: %s\n", filename, p->lineno, p->column, s); } else { fprintf(stderr, "line %d:%d: warning: %s\n", p->lineno, p->column, s); } #endif } else if (p->nwarn < sizeof(p->warn_buffer) / sizeof(p->warn_buffer[0])) { n = strlen(s); c = (char*)parser_palloc(p, n + 1); memcpy(c, s, n + 1); p->warn_buffer[p->nwarn].message = c; p->warn_buffer[p->nwarn].lineno = p->lineno; p->warn_buffer[p->nwarn].column = p->column; } p->nwarn++; } static void yywarning_s(parser_state *p, const char *msg, const char *s) { char buf[256]; strncpy(buf, msg, sizeof(buf) - 1); buf[sizeof(buf) - 1] = '\0'; strncat(buf, ": ", sizeof(buf) - strlen(buf) - 1); strncat(buf, s, sizeof(buf) - strlen(buf) - 1); yywarning(p, buf); } static void backref_error(parser_state *p, node *n) { int c; c = intn(n->car); if (c == NODE_NTH_REF) { yyerror_c(p, "can't set variable $", (char)intn(n->cdr)+'0'); } else if (c == NODE_BACK_REF) { yyerror_c(p, "can't set variable $", (char)intn(n->cdr)); } else { yyerror(NULL, p, "Internal error in backref_error()"); } } static void void_expr_error(parser_state *p, node *n) { int c; if (n == NULL) return; c = intn(n->car); switch (c) { case NODE_BREAK: case NODE_RETURN: case NODE_NEXT: case NODE_REDO: case NODE_RETRY: yyerror(NULL, p, "void value expression"); break; case NODE_AND: case NODE_OR: if (n->cdr) { void_expr_error(p, n->cdr->car); void_expr_error(p, n->cdr->cdr); } break; case NODE_BEGIN: if (n->cdr) { while (n->cdr) { n = n->cdr; } void_expr_error(p, n->car); } break; default: break; } } static void pushback(parser_state *p, int c); static mrb_bool peeks(parser_state *p, const char *s); static mrb_bool skips(parser_state *p, const char *s); static inline int nextc0(parser_state *p) { if (p->s && p->s < p->send) { return (unsigned char)*p->s++; } else { #ifndef MRB_NO_STDIO int c; if (p->f) { c = fgetc(p->f); if (!feof(p->f)) return c; } #endif return -1; } } static inline int nextc(parser_state *p) { int c; if (p->pb) { node *tmp; c = intn(p->pb->car); tmp = p->pb; p->pb = p->pb->cdr; cons_free(tmp); } else { c = nextc0(p); if (c < 0) goto eof; } if (c >= 0) { p->column++; } if (c == '\r') { const int lf = nextc0(p); if (lf == '\n') { return '\n'; } if (lf > 0) pushback(p, lf); } return c; eof: if (!p->cxt) return -1; else { if (p->cxt->partial_hook(p) < 0) return -1; /* end of program(s) */ return -2; /* end of a file in the program files */ } } static void pushback(parser_state *p, int c) { if (c >= 0) { p->column--; } p->pb = cons(nint(c), p->pb); } static void skip(parser_state *p, char term) { int c; for (;;) { c = nextc(p); if (c < 0) break; if (c == term) break; } } static int peekc_n(parser_state *p, int n) { node *list = 0; int c0; do { c0 = nextc(p); if (c0 == -1) return c0; /* do not skip partial EOF */ if (c0 >= 0) --p->column; list = push(list, nint(c0)); } while(n--); if (p->pb) { p->pb = append(list, p->pb); } else { p->pb = list; } return c0; } static mrb_bool peek_n(parser_state *p, int c, int n) { return peekc_n(p, n) == c && c >= 0; } #define peek(p,c) peek_n((p), (c), 0) static mrb_bool peeks(parser_state *p, const char *s) { size_t len = strlen(s); #ifndef MRB_NO_STDIO if (p->f) { int n = 0; while (*s) { if (!peek_n(p, *s++, n++)) return FALSE; } return TRUE; } else #endif if (p->s && p->s + len <= p->send) { if (memcmp(p->s, s, len) == 0) return TRUE; } return FALSE; } static mrb_bool skips(parser_state *p, const char *s) { int c; for (;;) { /* skip until first char */ for (;;) { c = nextc(p); if (c < 0) return FALSE; if (c == '\n') { p->lineno++; p->column = 0; } if (c == *s) break; } s++; if (peeks(p, s)) { size_t len = strlen(s); while (len--) { if (nextc(p) == '\n') { p->lineno++; p->column = 0; } } return TRUE; } else{ s--; } } return FALSE; } static int newtok(parser_state *p) { if (p->tokbuf != p->buf) { mrbc_free(p->tokbuf); p->tokbuf = p->buf; p->tsiz = MRB_PARSER_TOKBUF_SIZE; } p->tidx = 0; return p->column - 1; } static void tokadd(parser_state *p, int32_t c) { char utf8[4]; int i, len; /* mrb_assert(-0x10FFFF <= c && c <= 0xFF); */ if (c >= 0) { /* Single byte from source or non-Unicode escape */ utf8[0] = (char)c; len = 1; } else { /* Unicode character */ c = -c; if (c < 0x80) { utf8[0] = (char)c; len = 1; } else if (c < 0x800) { utf8[0] = (char)(0xC0 | (c >> 6)); utf8[1] = (char)(0x80 | (c & 0x3F)); len = 2; } else if (c < 0x10000) { utf8[0] = (char)(0xE0 | (c >> 12) ); utf8[1] = (char)(0x80 | ((c >> 6) & 0x3F)); utf8[2] = (char)(0x80 | ( c & 0x3F)); len = 3; } else { utf8[0] = (char)(0xF0 | (c >> 18) ); utf8[1] = (char)(0x80 | ((c >> 12) & 0x3F)); utf8[2] = (char)(0x80 | ((c >> 6) & 0x3F)); utf8[3] = (char)(0x80 | ( c & 0x3F)); len = 4; } } if (p->tidx+len >= p->tsiz) { if (p->tsiz >= MRB_PARSER_TOKBUF_MAX) { p->tidx += len; return; } p->tsiz *= 2; if (p->tokbuf == p->buf) { p->tokbuf = (char*)mrbc_malloc(p->tsiz); memcpy(p->tokbuf, p->buf, MRB_PARSER_TOKBUF_SIZE); } else { p->tokbuf = (char*)mrbc_realloc(p->tokbuf, p->tsiz); } } for (i = 0; i < len; i++) { p->tokbuf[p->tidx++] = utf8[i]; } } static int toklast(parser_state *p) { return p->tokbuf[p->tidx-1]; } static void tokfix(parser_state *p) { if (p->tidx >= MRB_PARSER_TOKBUF_MAX) { p->tidx = MRB_PARSER_TOKBUF_MAX-1; yyerror(NULL, p, "string too long (truncated)"); } p->tokbuf[p->tidx] = '\0'; } static const char* tok(parser_state *p) { return p->tokbuf; } static int toklen(parser_state *p) { return p->tidx; } #define IS_ARG() (p->lstate == EXPR_ARG || p->lstate == EXPR_CMDARG) #define IS_END() (p->lstate == EXPR_END || p->lstate == EXPR_ENDARG || p->lstate == EXPR_ENDFN) #define IS_BEG() (p->lstate == EXPR_BEG || p->lstate == EXPR_MID || p->lstate == EXPR_VALUE || p->lstate == EXPR_CLASS) #define IS_SPCARG(c) (IS_ARG() && space_seen && !ISSPACE(c)) #define IS_LABEL_POSSIBLE() ((p->lstate == EXPR_BEG && !cmd_state) || IS_ARG()) #define IS_LABEL_SUFFIX(n) (peek_n(p, ':',(n)) && !peek_n(p, ':', (n)+1)) static int32_t scan_oct(const int *start, int len, int *retlen) { const int *s = start; int32_t retval = 0; /* mrb_assert(len <= 3) */ while (len-- && *s >= '0' && *s <= '7') { retval <<= 3; retval |= *s++ - '0'; } *retlen = (int)(s - start); return retval; } static int32_t scan_hex(parser_state *p, const int *start, int len, int *retlen) { static const char hexdigit[] = "0123456789abcdef0123456789ABCDEF"; const int *s = start; uint32_t retval = 0; char *tmp; /* mrb_assert(len <= 8) */ while (len-- && *s && (tmp = (char*)strchr(hexdigit, *s))) { retval <<= 4; retval |= (tmp - hexdigit) & 15; s++; } *retlen = (int)(s - start); return (int32_t)retval; } static int32_t read_escape_unicode(parser_state *p, int limit) { int buf[9]; int i; int32_t hex; /* Look for opening brace */ i = 0; buf[0] = nextc(p); if (buf[0] < 0) { eof: yyerror(NULL, p, "invalid escape character syntax"); return -1; } if (ISXDIGIT(buf[0])) { /* \uxxxx form */ for (i=1; i 0x10FFFF || (hex & 0xFFFFF800) == 0xD800) { yyerror(NULL, p, "invalid Unicode code point"); return -1; } return hex; } /* Return negative to indicate Unicode code point */ static int32_t read_escape(parser_state *p) { int32_t c; switch (c = nextc(p)) { case '\\':/* Backslash */ return c; case 'n':/* newline */ return '\n'; case 't':/* horizontal tab */ return '\t'; case 'r':/* carriage-return */ return '\r'; case 'f':/* form-feed */ return '\f'; case 'v':/* vertical tab */ return '\13'; case 'a':/* alarm(bell) */ return '\007'; case 'e':/* escape */ return 033; case '0': case '1': case '2': case '3': /* octal constant */ case '4': case '5': case '6': case '7': { int buf[3]; int i; buf[0] = c; for (i=1; i<3; i++) { buf[i] = nextc(p); if (buf[i] < 0) goto eof; if (buf[i] < '0' || '7' < buf[i]) { pushback(p, buf[i]); break; } } c = scan_oct(buf, i, &i); } return c; case 'x': /* hex constant */ { int buf[2]; int i; for (i=0; i<2; i++) { buf[i] = nextc(p); if (buf[i] < 0) goto eof; if (!ISXDIGIT(buf[i])) { pushback(p, buf[i]); break; } } if (i == 0) { yyerror(NULL, p, "invalid hex escape"); return -1; } return scan_hex(p, buf, i, &i); } case 'u': /* Unicode */ if (peek(p, '{')) { /* \u{xxxxxxxx} form */ nextc(p); c = read_escape_unicode(p, 8); if (c < 0) return 0; if (nextc(p) != '}') goto eof; } else { c = read_escape_unicode(p, 4); if (c < 0) return 0; } return -c; case 'b':/* backspace */ return '\010'; case 's':/* space */ return ' '; case 'M': if ((c = nextc(p)) != '-') { yyerror(NULL, p, "Invalid escape character syntax"); pushback(p, c); return '\0'; } if ((c = nextc(p)) == '\\') { return read_escape(p) | 0x80; } else if (c < 0) goto eof; else { return ((c & 0xff) | 0x80); } case 'C': if ((c = nextc(p)) != '-') { yyerror(NULL, p, "Invalid escape character syntax"); pushback(p, c); return '\0'; } case 'c': if ((c = nextc(p))== '\\') { c = read_escape(p); } else if (c == '?') return 0177; else if (c < 0) goto eof; return c & 0x9f; eof: case -1: case -2: /* end of a file */ yyerror(NULL, p, "Invalid escape character syntax"); return '\0'; default: return c; } } static void heredoc_count_indent(parser_heredoc_info *hinfo, const char *str, size_t len, size_t spaces, size_t *offset) { size_t indent = 0; *offset = 0; for (size_t i = 0; i < len; i++) { size_t size; if (str[i] == '\n') break; else if (str[i] == '\t') size = 8; else if (ISSPACE(str[i])) size = 1; else break; size_t nindent = indent + size; if (nindent > spaces || nindent > hinfo->indent) break; indent = nindent; *offset += 1; } } static void heredoc_remove_indent(parser_state *p, parser_heredoc_info *hinfo) { if (!hinfo->remove_indent || hinfo->indent == 0) return; node *indented, *n, *pair, *escaped, *nspaces; const char *str; size_t len, spaces, offset, start, end; indented = hinfo->indented; while (indented) { n = indented->car; pair = n->car; str = (char*)pair->car; len = (size_t)pair->cdr; escaped = n->cdr->car; nspaces = n->cdr->cdr; if (escaped) { char *newstr = strndup(str, len); size_t newlen = 0; start = 0; while (start < len) { end = escaped ? (size_t)escaped->car : len; if (end > len) end = len; spaces = (size_t)nspaces->car; size_t esclen = end - start; heredoc_count_indent(hinfo, str + start, esclen, spaces, &offset); esclen -= offset; memcpy(newstr + newlen, str + start + offset, esclen); newlen += esclen; start = end; if (escaped) escaped = escaped->cdr; nspaces = nspaces->cdr; } if (newlen < len) newstr[newlen] = '\0'; pair->car = (node*)newstr; pair->cdr = (node*)newlen; } else { spaces = (size_t)nspaces->car; heredoc_count_indent(hinfo, str, len, spaces, &offset); pair->car = (node*)(str + offset); pair->cdr = (node*)(len - offset); } indented = indented->cdr; } } static void heredoc_push_indented(parser_state *p, parser_heredoc_info *hinfo, node *pair, node *escaped, node *nspaces, mrb_bool empty_line) { hinfo->indented = push(hinfo->indented, cons(pair, cons(escaped, nspaces))); while (nspaces) { size_t tspaces = (size_t)nspaces->car; if ((hinfo->indent == ~0U || tspaces < hinfo->indent) && !empty_line) hinfo->indent = tspaces; nspaces = nspaces->cdr; } } static int parse_string(parser_state *p) { int c; string_type type = (string_type)p->lex_strterm->type; int nest_level = p->lex_strterm->level; int beg = p->lex_strterm->paren; int end = p->lex_strterm->term; parser_heredoc_info *hinfo = (type & STR_FUNC_HEREDOC) ? parsing_heredoc_info(p) : NULL; mrb_bool unindent = hinfo && hinfo->remove_indent; mrb_bool head = hinfo && hinfo->line_head; mrb_bool empty = TRUE; size_t spaces = 0; size_t pos = -1; node *escaped = NULL; node *nspaces = NULL; if (beg == 0) beg = -3; /* should never happen */ if (end == 0) end = -3; newtok(p); while ((c = nextc(p)) != end || nest_level != 0) { pos++; if (hinfo && (c == '\n' || c < 0)) { mrb_bool line_head; tokadd(p, '\n'); tokfix(p); p->lineno++; p->column = 0; line_head = hinfo->line_head; hinfo->line_head = TRUE; if (line_head) { /* check whether end of heredoc */ const char *s = tok(p); int len = toklen(p); if (hinfo->allow_indent) { while (ISSPACE(*s) && len > 0) { s++; len--; } } if (hinfo->term_len > 0 && len-1 == hinfo->term_len && strncmp(s, hinfo->term, len-1) == 0) { heredoc_remove_indent(p, hinfo); return tHEREDOC_END; } } if (c < 0) { char buf[256]; const char s1[] = "can't find heredoc delimiter \""; const char s2[] = "\" anywhere before EOF"; if (sizeof(s1)+sizeof(s2)+strlen(hinfo->term)+1 >= sizeof(buf)) { yyerror(NULL, p, "can't find heredoc delimiter anywhere before EOF"); } else { strcpy(buf, s1); strcat(buf, hinfo->term); strcat(buf, s2); yyerror(NULL, p, buf); } return 0; } node *nd = new_str(p, tok(p), toklen(p)); pylval.nd = nd; if (unindent && head) { nspaces = push(nspaces, nint(spaces)); heredoc_push_indented(p, hinfo, nd->cdr, escaped, nspaces, empty && line_head); } return tHD_STRING_MID; } if (unindent && empty) { if (c == '\t') spaces += 8; else if (ISSPACE(c)) spaces++; else empty = FALSE; } if (c < 0) { yyerror(NULL, p, "unterminated string meets end of file"); return 0; } else if (c == beg) { nest_level++; p->lex_strterm->level = nest_level; } else if (c == end) { nest_level--; p->lex_strterm->level = nest_level; } else if (c == '\\') { c = nextc(p); if (type & STR_FUNC_EXPAND) { if (c == end || c == beg) { tokadd(p, c); } else if (c == '\n') { p->lineno++; p->column = 0; if (unindent) { nspaces = push(nspaces, nint(spaces)); escaped = push(escaped, nint(pos)); pos--; empty = TRUE; spaces = 0; } if (type & STR_FUNC_ARRAY) { tokadd(p, '\n'); } } else if (type & STR_FUNC_REGEXP) { tokadd(p, '\\'); tokadd(p, c); } else if (c == 'u' && peek(p, '{')) { /* \u{xxxx xxxx xxxx} form */ nextc(p); while (1) { do c = nextc(p); while (ISSPACE(c)); if (c == '}') break; pushback(p, c); c = read_escape_unicode(p, 8); if (c < 0) break; tokadd(p, -c); } if (hinfo) hinfo->line_head = FALSE; } else { pushback(p, c); tokadd(p, read_escape(p)); if (hinfo) hinfo->line_head = FALSE; } } else { if (c != beg && c != end) { if (c == '\n') { p->lineno++; p->column = 0; } if (!(c == '\\' || ((type & STR_FUNC_ARRAY) && ISSPACE(c)))) { tokadd(p, '\\'); } } tokadd(p, c); } continue; } else if ((c == '#') && (type & STR_FUNC_EXPAND)) { c = nextc(p); if (c == '{') { tokfix(p); p->lstate = EXPR_BEG; p->cmd_start = TRUE; node *nd = new_str(p, tok(p), toklen(p)); pylval.nd = nd; if (hinfo) { if (unindent && head) { nspaces = push(nspaces, nint(spaces)); heredoc_push_indented(p, hinfo, nd->cdr, escaped, nspaces, FALSE); } hinfo->line_head = FALSE; return tHD_STRING_PART; } return tSTRING_PART; } tokadd(p, '#'); pushback(p, c); continue; } if ((type & STR_FUNC_ARRAY) && ISSPACE(c)) { if (toklen(p) == 0) { do { if (c == '\n') { p->lineno++; p->column = 0; heredoc_treat_nextline(p); if (p->parsing_heredoc != NULL) { return tHD_LITERAL_DELIM; } } c = nextc(p); } while (ISSPACE(c)); pushback(p, c); return tLITERAL_DELIM; } else { pushback(p, c); tokfix(p); pylval.nd = new_str(p, tok(p), toklen(p)); return tSTRING_MID; } } if (c == '\n') { p->lineno++; p->column = 0; } tokadd(p, c); } tokfix(p); p->lstate = EXPR_END; end_strterm(p); if (type & STR_FUNC_XQUOTE) { pylval.nd = new_xstr(p, tok(p), toklen(p)); return tXSTRING; } if (type & STR_FUNC_REGEXP) { int f = 0; int re_opt; char *s = strndup(tok(p), toklen(p)); char flags[3]; char *flag = flags; char enc = '\0'; char *encp; char *dup; newtok(p); while (re_opt = nextc(p), re_opt >= 0 && ISALPHA(re_opt)) { switch (re_opt) { case 'i': f |= 1; break; case 'x': f |= 2; break; case 'm': f |= 4; break; case 'u': f |= 16; break; case 'n': f |= 32; break; case 'o': break; default: tokadd(p, re_opt); break; } } pushback(p, re_opt); if (toklen(p)) { char msg[128]; strcpy(msg, "unknown regexp option"); tokfix(p); if (toklen(p) > 1) { strcat(msg, "s"); } strcat(msg, " - "); strncat(msg, tok(p), sizeof(msg) - strlen(msg) - 1); yyerror(NULL, p, msg); } if (f != 0) { if (f & 1) *flag++ = 'i'; if (f & 2) *flag++ = 'x'; if (f & 4) *flag++ = 'm'; if (f & 16) enc = 'u'; if (f & 32) enc = 'n'; } if (flag > flags) { dup = strndup(flags, (size_t)(flag - flags)); } else { dup = NULL; } if (enc) { encp = strndup(&enc, 1); } else { encp = NULL; } pylval.nd = new_regx(p, s, dup, encp); return tREGEXP; } pylval.nd = new_str(p, tok(p), toklen(p)); return tSTRING; } static int number_literal_suffix(parser_state *p) { int c, result = 0; node *list = 0; int column = p->column; int mask = NUM_SUFFIX_R|NUM_SUFFIX_I; while ((c = nextc(p)) != -1) { list = push(list, nint(c)); if ((mask & NUM_SUFFIX_I) && c == 'i') { result |= (mask & NUM_SUFFIX_I); mask &= ~NUM_SUFFIX_I; /* r after i, rational of complex is disallowed */ mask &= ~NUM_SUFFIX_R; continue; } if ((mask & NUM_SUFFIX_R) && c == 'r') { result |= (mask & NUM_SUFFIX_R); mask &= ~NUM_SUFFIX_R; continue; } if (!ISASCII(c) || ISALPHA(c) || c == '_') { p->column = column; if (p->pb) { p->pb = append(list, p->pb); } else { p->pb = list; } return 0; } pushback(p, c); break; } return result; } static int heredoc_identifier(parser_state *p) { int c; int type = str_heredoc; mrb_bool indent = FALSE; mrb_bool squiggly = FALSE; mrb_bool quote = FALSE; node *newnode; parser_heredoc_info *info; c = nextc(p); if (ISSPACE(c) || c == '=') { pushback(p, c); return 0; } if (c == '-' || c == '~') { if (c == '-') indent = TRUE; if (c == '~') squiggly = TRUE; c = nextc(p); } if (c == '\'' || c == '"') { int term = c; if (c == '\'') quote = TRUE; newtok(p); while ((c = nextc(p)) >= 0 && c != term) { if (c == '\n') { c = -1; break; } tokadd(p, c); } if (c < 0) { yyerror(NULL, p, "unterminated here document identifier"); return 0; } } else { if (c < 0) { return 0; /* missing here document identifier */ } if (! identchar(c)) { pushback(p, c); if (indent) pushback(p, '-'); if (squiggly) pushback(p, '~'); return 0; } newtok(p); do { tokadd(p, c); } while ((c = nextc(p)) >= 0 && identchar(c)); pushback(p, c); } tokfix(p); newnode = new_heredoc(p); info = (parser_heredoc_info*)newnode->cdr; info->term = strndup(tok(p), toklen(p)); info->term_len = toklen(p); if (! quote) type |= STR_FUNC_EXPAND; info->type = (string_type)type; info->allow_indent = indent || squiggly; info->remove_indent = squiggly; info->indent = ~0U; info->indented = NULL; info->line_head = TRUE; info->doc = NULL; p->heredocs_from_nextline = push(p->heredocs_from_nextline, newnode); p->lstate = EXPR_END; pylval.nd = newnode; return tHEREDOC_BEG; } static int arg_ambiguous(parser_state *p) { yywarning(p, "ambiguous first argument; put parentheses or even spaces"); return 1; } #include "lex.def" static int parser_yylex(parser_state *p) { int32_t c; int nlines = 1; int space_seen = 0; int cmd_state; enum mrb_lex_state_enum last_state; int token_column; if (p->lex_strterm) { if (is_strterm_type(p, STR_FUNC_HEREDOC)) { if (p->parsing_heredoc != NULL) return parse_string(p); } else return parse_string(p); } cmd_state = p->cmd_start; p->cmd_start = FALSE; retry: last_state = p->lstate; switch (c = nextc(p)) { case '\004': /* ^D */ case '\032': /* ^Z */ case '\0': /* NUL */ case -1: /* end of script. */ if (p->heredocs_from_nextline) goto maybe_heredoc; return 0; /* white spaces */ case ' ': case '\t': case '\f': case '\r': case '\13': /* '\v' */ space_seen = 1; goto retry; case '#': /* it's a comment */ skip(p, '\n'); /* fall through */ case -2: /* end of a file */ case '\n': maybe_heredoc: heredoc_treat_nextline(p); p->column = 0; switch (p->lstate) { case EXPR_BEG: case EXPR_FNAME: case EXPR_DOT: case EXPR_CLASS: case EXPR_VALUE: p->lineno++; if (p->parsing_heredoc != NULL) { if (p->lex_strterm) { return parse_string(p); } } goto retry; default: break; } if (p->parsing_heredoc != NULL) { pylval.num = nlines; return '\n'; } while ((c = nextc(p))) { switch (c) { case ' ': case '\t': case '\f': case '\r': case '\13': /* '\v' */ space_seen = 1; break; case '#': /* comment as a whitespace */ skip(p, '\n'); nlines++; break; case '.': if (!peek(p, '.')) { pushback(p, '.'); p->lineno+=nlines; nlines=1; goto retry; } pushback(p, c); goto normal_newline; case '&': if (peek(p, '.')) { pushback(p, '&'); p->lineno+=nlines; nlines=1; goto retry; } pushback(p, c); goto normal_newline; case -1: /* EOF */ case -2: /* end of a file */ goto normal_newline; default: pushback(p, c); goto normal_newline; } } normal_newline: p->cmd_start = TRUE; p->lstate = EXPR_BEG; pylval.num = nlines; return '\n'; case '*': if ((c = nextc(p)) == '*') { if ((c = nextc(p)) == '=') { pylval.id = intern_op(pow); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); if (IS_SPCARG(c)) { yywarning(p, "'**' interpreted as argument prefix"); c = tDSTAR; } else if (IS_BEG()) { c = tDSTAR; } else { c = tPOW; /* "**", "argument prefix" */ } } else { if (c == '=') { pylval.id = intern_op(mul); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); if (IS_SPCARG(c)) { yywarning(p, "'*' interpreted as argument prefix"); c = tSTAR; } else if (IS_BEG()) { c = tSTAR; } else { c = '*'; } } if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; } else { p->lstate = EXPR_BEG; } return c; case '!': c = nextc(p); if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; if (c == '@') { return '!'; } } else { p->lstate = EXPR_BEG; } if (c == '=') { return tNEQ; } if (c == '~') { return tNMATCH; } pushback(p, c); return '!'; case '=': if (p->column == 1) { static const char begin[] = "begin"; static const char end[] = "\n=end"; if (peeks(p, begin)) { c = peekc_n(p, sizeof(begin)-1); if (c < 0 || ISSPACE(c)) { do { if (!skips(p, end)) { yyerror(NULL, p, "embedded document meets end of file"); return 0; } c = nextc(p); } while (!(c < 0 || ISSPACE(c))); if (c != '\n') skip(p, '\n'); p->lineno+=nlines; nlines=1; p->column = 0; goto retry; } } } if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; } else { p->lstate = EXPR_BEG; } if ((c = nextc(p)) == '=') { if ((c = nextc(p)) == '=') { return tEQQ; } pushback(p, c); return tEQ; } if (c == '~') { return tMATCH; } else if (c == '>') { return tASSOC; } pushback(p, c); return '='; case '<': c = nextc(p); if (c == '<' && p->lstate != EXPR_DOT && p->lstate != EXPR_CLASS && !IS_END() && (!IS_ARG() || space_seen)) { int token = heredoc_identifier(p); if (token) return token; } if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; } else { p->lstate = EXPR_BEG; if (p->lstate == EXPR_CLASS) { p->cmd_start = TRUE; } } if (c == '=') { if ((c = nextc(p)) == '>') { return tCMP; } pushback(p, c); return tLEQ; } if (c == '<') { if ((c = nextc(p)) == '=') { pylval.id = intern_op(lshift); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); return tLSHFT; } pushback(p, c); return '<'; case '>': if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; } else { p->lstate = EXPR_BEG; } if ((c = nextc(p)) == '=') { return tGEQ; } if (c == '>') { if ((c = nextc(p)) == '=') { pylval.id = intern_op(rshift); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); return tRSHFT; } pushback(p, c); return '>'; case '"': p->lex_strterm = new_strterm(p, str_dquote, '"', 0); return tSTRING_BEG; case '\'': p->lex_strterm = new_strterm(p, str_squote, '\'', 0); return parse_string(p); case '`': if (p->lstate == EXPR_FNAME) { p->lstate = EXPR_ENDFN; return '`'; } if (p->lstate == EXPR_DOT) { if (cmd_state) p->lstate = EXPR_CMDARG; else p->lstate = EXPR_ARG; return '`'; } p->lex_strterm = new_strterm(p, str_xquote, '`', 0); return tXSTRING_BEG; case '?': if (IS_END()) { p->lstate = EXPR_VALUE; return '?'; } c = nextc(p); if (c < 0) { yyerror(NULL, p, "incomplete character syntax"); return 0; } if (ISSPACE(c)) { if (!IS_ARG()) { int c2; switch (c) { case ' ': c2 = 's'; break; case '\n': c2 = 'n'; break; case '\t': c2 = 't'; break; case '\v': c2 = 'v'; break; case '\r': c2 = 'r'; break; case '\f': c2 = 'f'; break; default: c2 = 0; break; } if (c2) { char buf[256]; char cc[] = { (char)c2, '\0' }; strcpy(buf, "invalid character syntax; use ?\\"); strncat(buf, cc, 2); yyerror(NULL, p, buf); } } ternary: pushback(p, c); p->lstate = EXPR_VALUE; return '?'; } newtok(p); /* need support UTF-8 if configured */ if ((ISALNUM(c) || c == '_')) { int c2 = nextc(p); pushback(p, c2); if ((ISALNUM(c2) || c2 == '_')) { goto ternary; } } if (c == '\\') { c = read_escape(p); tokadd(p, c); } else { tokadd(p, c); } tokfix(p); pylval.nd = new_str(p, tok(p), toklen(p)); p->lstate = EXPR_END; return tCHAR; case '&': if ((c = nextc(p)) == '&') { p->lstate = EXPR_BEG; if ((c = nextc(p)) == '=') { pylval.id = intern_op(andand); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); return tANDOP; } else if (c == '.') { p->lstate = EXPR_DOT; return tANDDOT; } else if (c == '=') { pylval.id = intern_op(and); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); if (IS_SPCARG(c)) { yywarning(p, "'&' interpreted as argument prefix"); c = tAMPER; } else if (IS_BEG()) { c = tAMPER; } else { c = '&'; } if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; } else { p->lstate = EXPR_BEG; } return c; case '|': if ((c = nextc(p)) == '|') { p->lstate = EXPR_BEG; if ((c = nextc(p)) == '=') { pylval.id = intern_op(oror); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); return tOROP; } if (c == '=') { pylval.id = intern_op(or); p->lstate = EXPR_BEG; return tOP_ASGN; } if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; } else { p->lstate = EXPR_BEG; } pushback(p, c); return '|'; case '+': c = nextc(p); if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; if (c == '@') { return tUPLUS; } pushback(p, c); return '+'; } if (c == '=') { pylval.id = intern_op(add); p->lstate = EXPR_BEG; return tOP_ASGN; } if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p))) { p->lstate = EXPR_BEG; pushback(p, c); if (c >= 0 && ISDIGIT(c)) { c = '+'; goto start_num; } return tUPLUS; } p->lstate = EXPR_BEG; pushback(p, c); return '+'; case '-': c = nextc(p); if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; if (c == '@') { return tUMINUS; } pushback(p, c); return '-'; } if (c == '=') { pylval.id = intern_op(sub); p->lstate = EXPR_BEG; return tOP_ASGN; } if (c == '>') { p->lstate = EXPR_ENDFN; return tLAMBDA; } if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p))) { p->lstate = EXPR_BEG; pushback(p, c); if (c >= 0 && ISDIGIT(c)) { return tUMINUS_NUM; } return tUMINUS; } p->lstate = EXPR_BEG; pushback(p, c); return '-'; case '.': { int is_beg = IS_BEG(); p->lstate = EXPR_MID; if ((c = nextc(p)) == '.') { if ((c = nextc(p)) == '.') { return is_beg ? tBDOT3 : tDOT3; } pushback(p, c); return is_beg ? tBDOT2 : tDOT2; } pushback(p, c); p->lstate = EXPR_BEG; if (c >= 0 && ISDIGIT(c)) { yyerror(NULL, p, "no . floating literal anymore; put 0 before dot"); } p->lstate = EXPR_DOT; return '.'; } start_num: case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { int is_float, seen_point, seen_e, nondigit; int suffix = 0; is_float = seen_point = seen_e = nondigit = 0; p->lstate = EXPR_END; newtok(p); if (c == '-') { tokadd(p, c); c = nextc(p); } else if (c == '+') { c = nextc(p); } if (c == '0') { #define no_digits() do {yyerror(NULL, p,"numeric literal without digits"); return 0;} while (0) int start = toklen(p); c = nextc(p); if (c == 'x' || c == 'X') { /* hexadecimal */ c = nextc(p); if (c >= 0 && ISXDIGIT(c)) { do { if (c == '_') { if (nondigit) break; nondigit = c; continue; } if (!ISXDIGIT(c)) break; nondigit = 0; tokadd(p, tolower(c)); } while ((c = nextc(p)) >= 0); } pushback(p, c); tokfix(p); if (toklen(p) == start) { no_digits(); } else if (nondigit) goto trailing_uc; suffix = number_literal_suffix(p); pylval.nd = new_int(p, tok(p), 16, suffix); return tINTEGER; } if (c == 'b' || c == 'B') { /* binary */ c = nextc(p); if (c == '0' || c == '1') { do { if (c == '_') { if (nondigit) break; nondigit = c; continue; } if (c != '0' && c != '1') break; nondigit = 0; tokadd(p, c); } while ((c = nextc(p)) >= 0); } pushback(p, c); tokfix(p); if (toklen(p) == start) { no_digits(); } else if (nondigit) goto trailing_uc; suffix = number_literal_suffix(p); pylval.nd = new_int(p, tok(p), 2, suffix); return tINTEGER; } if (c == 'd' || c == 'D') { /* decimal */ c = nextc(p); if (c >= 0 && ISDIGIT(c)) { do { if (c == '_') { if (nondigit) break; nondigit = c; continue; } if (!ISDIGIT(c)) break; nondigit = 0; tokadd(p, c); } while ((c = nextc(p)) >= 0); } pushback(p, c); tokfix(p); if (toklen(p) == start) { no_digits(); } else if (nondigit) goto trailing_uc; suffix = number_literal_suffix(p); pylval.nd = new_int(p, tok(p), 10, suffix); return tINTEGER; } if (c == '_') { /* 0_0 */ goto octal_number; } if (c == 'o' || c == 'O') { /* prefixed octal */ c = nextc(p); if (c < 0 || c == '_' || !ISDIGIT(c)) { no_digits(); } } if (c >= '0' && c <= '7') { /* octal */ octal_number: do { if (c == '_') { if (nondigit) break; nondigit = c; continue; } if (c < '0' || c > '9') break; if (c > '7') goto invalid_octal; nondigit = 0; tokadd(p, c); } while ((c = nextc(p)) >= 0); if (toklen(p) > start) { pushback(p, c); tokfix(p); if (nondigit) goto trailing_uc; suffix = number_literal_suffix(p); pylval.nd = new_int(p, tok(p), 8, suffix); return tINTEGER; } if (nondigit) { pushback(p, c); goto trailing_uc; } } if (c > '7' && c <= '9') { invalid_octal: yyerror(NULL, p, "Invalid octal digit"); } else if (c == '.' || c == 'e' || c == 'E') { tokadd(p, '0'); } else { pushback(p, c); suffix = number_literal_suffix(p); pylval.nd = new_int(p, "0", 10, suffix); return tINTEGER; } } for (;;) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': nondigit = 0; tokadd(p, c); break; case '.': if (nondigit) goto trailing_uc; if (seen_point || seen_e) { goto decode_num; } else { int c0 = nextc(p); if (c0 < 0 || !ISDIGIT(c0)) { pushback(p, c0); goto decode_num; } c = c0; } tokadd(p, '.'); tokadd(p, c); is_float++; seen_point++; nondigit = 0; break; case 'e': case 'E': if (nondigit) { pushback(p, c); c = nondigit; goto decode_num; } if (seen_e) { goto decode_num; } tokadd(p, c); seen_e++; is_float++; nondigit = c; c = nextc(p); if (c != '-' && c != '+') continue; tokadd(p, c); nondigit = c; break; case '_': /* '_' in number just ignored */ if (nondigit) goto decode_num; nondigit = c; break; default: goto decode_num; } c = nextc(p); } decode_num: pushback(p, c); if (nondigit) { trailing_uc: yyerror_c(p, "trailing non digit in number: ", (char)nondigit); } tokfix(p); if (is_float) { #ifdef MRB_NO_FLOAT yywarning_s(p, "floating-point numbers are not supported", tok(p)); pylval.nd = new_int(p, "0", 10, 0); return tINTEGER; #else double d; if (!mrb_read_float(tok(p), NULL, &d)) { yywarning_s(p, "corrupted float value", tok(p)); } suffix = number_literal_suffix(p); if (seen_e && (suffix & NUM_SUFFIX_R)) { pushback(p, 'r'); suffix &= ~NUM_SUFFIX_R; } pylval.nd = new_float(p, tok(p), suffix); return tFLOAT; #endif } suffix = number_literal_suffix(p); pylval.nd = new_int(p, tok(p), 10, suffix); return tINTEGER; } case ')': case ']': p->paren_nest--; /* fall through */ case '}': COND_LEXPOP(); CMDARG_LEXPOP(); if (c == ')') p->lstate = EXPR_ENDFN; else p->lstate = EXPR_END; return c; case ':': c = nextc(p); if (c == ':') { if (IS_BEG() || p->lstate == EXPR_CLASS || IS_SPCARG(-1)) { p->lstate = EXPR_BEG; return tCOLON3; } p->lstate = EXPR_DOT; return tCOLON2; } if (!space_seen && IS_END()) { pushback(p, c); p->lstate = EXPR_BEG; return tLABEL_TAG; } if (IS_END() || ISSPACE(c) || c == '#') { pushback(p, c); p->lstate = EXPR_BEG; return ':'; } pushback(p, c); p->lstate = EXPR_FNAME; return tSYMBEG; case '/': if (IS_BEG()) { p->lex_strterm = new_strterm(p, str_regexp, '/', 0); return tREGEXP_BEG; } if ((c = nextc(p)) == '=') { pylval.id = intern_op(div); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); if (IS_SPCARG(c)) { p->lex_strterm = new_strterm(p, str_regexp, '/', 0); return tREGEXP_BEG; } if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; } else { p->lstate = EXPR_BEG; } return '/'; case '^': if ((c = nextc(p)) == '=') { pylval.id = intern_op(xor); p->lstate = EXPR_BEG; return tOP_ASGN; } if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; } else { p->lstate = EXPR_BEG; } pushback(p, c); return '^'; case ';': p->lstate = EXPR_BEG; return ';'; case ',': p->lstate = EXPR_BEG; return ','; case '~': if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { if ((c = nextc(p)) != '@') { pushback(p, c); } p->lstate = EXPR_ARG; } else { p->lstate = EXPR_BEG; } return '~'; case '(': if (IS_BEG()) { c = tLPAREN; } else if (IS_SPCARG(-1)) { c = tLPAREN_ARG; } else if (p->lstate == EXPR_END && space_seen) { c = tLPAREN_ARG; } p->paren_nest++; COND_PUSH(0); CMDARG_PUSH(0); p->lstate = EXPR_BEG; return c; case '[': p->paren_nest++; if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; p->paren_nest--; if ((c = nextc(p)) == ']') { if ((c = nextc(p)) == '=') { return tASET; } pushback(p, c); return tAREF; } pushback(p, c); return '['; } else if (IS_BEG()) { c = tLBRACK; } else if (IS_ARG() && space_seen) { c = tLBRACK; } p->lstate = EXPR_BEG; COND_PUSH(0); CMDARG_PUSH(0); return c; case '{': if (p->lpar_beg && p->lpar_beg == p->paren_nest) { p->lstate = EXPR_BEG; p->lpar_beg = 0; p->paren_nest--; COND_PUSH(0); CMDARG_PUSH(0); return tLAMBEG; } if (IS_ARG() || p->lstate == EXPR_END || p->lstate == EXPR_ENDFN) c = '{'; /* block (primary) */ else if (p->lstate == EXPR_ENDARG) c = tLBRACE_ARG; /* block (expr) */ else c = tLBRACE; /* hash */ COND_PUSH(0); CMDARG_PUSH(0); p->lstate = EXPR_BEG; return c; case '\\': c = nextc(p); if (c == '\n') { p->lineno+=nlines; nlines=1; p->column = 0; space_seen = 1; goto retry; /* skip \\n */ } pushback(p, c); return '\\'; case '%': if (IS_BEG()) { int term; int paren; c = nextc(p); quotation: if (c < 0 || !ISALNUM(c)) { term = c; c = 'Q'; } else { term = nextc(p); if (ISALNUM(term)) { yyerror(NULL, p, "unknown type of %string"); return 0; } } if (c < 0 || term < 0) { yyerror(NULL, p, "unterminated quoted string meets end of file"); return 0; } paren = term; if (term == '(') term = ')'; else if (term == '[') term = ']'; else if (term == '{') term = '}'; else if (term == '<') term = '>'; else paren = 0; switch (c) { case 'Q': p->lex_strterm = new_strterm(p, str_dquote, term, paren); return tSTRING_BEG; case 'q': p->lex_strterm = new_strterm(p, str_squote, term, paren); return parse_string(p); case 'W': p->lex_strterm = new_strterm(p, str_dword, term, paren); return tWORDS_BEG; case 'w': p->lex_strterm = new_strterm(p, str_sword, term, paren); return tWORDS_BEG; case 'x': p->lex_strterm = new_strterm(p, str_xquote, term, paren); return tXSTRING_BEG; case 'r': p->lex_strterm = new_strterm(p, str_regexp, term, paren); return tREGEXP_BEG; case 's': p->lex_strterm = new_strterm(p, str_ssym, term, paren); return tSYMBEG; case 'I': p->lex_strterm = new_strterm(p, str_dsymbols, term, paren); return tSYMBOLS_BEG; case 'i': p->lex_strterm = new_strterm(p, str_ssymbols, term, paren); return tSYMBOLS_BEG; default: yyerror(NULL, p, "unknown type of %string"); return 0; } } if ((c = nextc(p)) == '=') { pylval.id = intern_op(mod); p->lstate = EXPR_BEG; return tOP_ASGN; } if (IS_SPCARG(c)) { goto quotation; } if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { p->lstate = EXPR_ARG; } else { p->lstate = EXPR_BEG; } pushback(p, c); return '%'; case '$': p->lstate = EXPR_END; token_column = newtok(p); c = nextc(p); if (c < 0) { yyerror(NULL, p, "incomplete global variable syntax"); return 0; } switch (c) { case '_': /* $_: last read line string */ c = nextc(p); if (c >= 0 && identchar(c)) { /* if there is more after _ it is a variable */ tokadd(p, '$'); tokadd(p, c); break; } pushback(p, c); c = '_'; /* fall through */ case '~': /* $~: match-data */ case '*': /* $*: argv */ case '$': /* $$: pid */ case '?': /* $?: last status */ case '!': /* $!: error string */ case '@': /* $@: error position */ case '/': /* $/: input record separator */ case '\\': /* $\: output record separator */ case ';': /* $;: field separator */ case ',': /* $,: output field separator */ case '.': /* $.: last read line number */ case '=': /* $=: ignorecase */ case ':': /* $:: load path */ case '<': /* $<: reading filename */ case '>': /* $>: default output handle */ case '\"': /* $": already loaded files */ tokadd(p, '$'); tokadd(p, c); tokfix(p); pylval.id = intern(tok(p), toklen(p)); return tGVAR; case '-': tokadd(p, '$'); tokadd(p, c); c = nextc(p); pushback(p, c); gvar: tokfix(p); pylval.id = intern(tok(p), toklen(p)); return tGVAR; case '&': /* $&: last match */ case '`': /* $`: string before last match */ case '\'': /* $': string after last match */ case '+': /* $+: string matches last pattern */ if (last_state == EXPR_FNAME) { tokadd(p, '$'); tokadd(p, c); goto gvar; } pylval.nd = new_back_ref(p, c); return tBACK_REF; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': do { tokadd(p, c); c = nextc(p); } while (c >= 0 && ISDIGIT(c)); pushback(p, c); if (last_state == EXPR_FNAME) goto gvar; tokfix(p); { mrb_int n; if (!mrb_read_int(tok(p), NULL, NULL, &n)) { yywarning(p, "capture group index too big; always nil"); return keyword_nil; } pylval.nd = new_nth_ref(p, (int)n); } return tNTH_REF; default: if (!identchar(c)) { pushback(p, c); return '$'; } /* fall through */ case '0': tokadd(p, '$'); } break; case '@': c = nextc(p); token_column = newtok(p); tokadd(p, '@'); if (c == '@') { tokadd(p, '@'); c = nextc(p); } if (c < 0) { if (p->tidx == 1) { yyerror(NULL, p, "incomplete instance variable syntax"); } else { yyerror(NULL, p, "incomplete class variable syntax"); } return 0; } else if (ISDIGIT(c)) { if (p->tidx == 1) { yyerror_c(p, "wrong instance variable name: @", c); } else { yyerror_c(p, "wrong class variable name: @@", c); } return 0; } if (!identchar(c)) { pushback(p, c); return '@'; } break; case '_': token_column = newtok(p); break; default: if (!identchar(c)) { char buf[36]; const char s[] = "Invalid char in expression: 0x"; const char hexdigits[] = "0123456789ABCDEF"; strcpy(buf, s); buf[sizeof(s)-1] = hexdigits[(c & 0xf0) >> 4]; buf[sizeof(s)] = hexdigits[(c & 0x0f)]; buf[sizeof(s)+1] = 0; yyerror(NULL, p, buf); goto retry; } token_column = newtok(p); break; } do { tokadd(p, c); c = nextc(p); if (c < 0) break; } while (identchar(c)); if (token_column == 0 && toklen(p) == 7 && (c < 0 || c == '\n') && strncmp(tok(p), "__END__", toklen(p)) == 0) return -1; switch (tok(p)[0]) { case '@': case '$': pushback(p, c); break; default: if ((c == '!' || c == '?') && !peek(p, '=')) { tokadd(p, c); } else { pushback(p, c); } } tokfix(p); { int result = 0; switch (tok(p)[0]) { case '$': p->lstate = EXPR_END; result = tGVAR; break; case '@': p->lstate = EXPR_END; if (tok(p)[1] == '@') result = tCVAR; else result = tIVAR; break; case '_': if (toklen(p) == 2 && ISDIGIT(tok(p)[1]) && p->nvars) { int n = tok(p)[1] - '0'; int nvar; if (n > 0) { nvar = intn(p->nvars->car); if (nvar != -2) { /* numbered parameters never appear on toplevel */ pylval.num = n; p->lstate = EXPR_END; return tNUMPARAM; } } } /* fall through */ default: if (toklast(p) == '!' || toklast(p) == '?') { result = tFID; } else { if (p->lstate == EXPR_FNAME) { if ((c = nextc(p)) == '=' && !peek(p, '~') && !peek(p, '>') && (!peek(p, '=') || (peek_n(p, '>', 1)))) { result = tIDENTIFIER; tokadd(p, c); tokfix(p); } else { pushback(p, c); } if ((c = nextc(p)) == '=' && !peek(p, '~') && !peek(p, '>') && (!peek(p, '=') || (peek_n(p, '>', 1)))) { result = tIDENTIFIER; tokadd(p, c); tokfix(p); } else { pushback(p, c); } } if (result == 0 && ISUPPER(tok(p)[0])) { result = tCONSTANT; } else { result = tIDENTIFIER; } } if (IS_LABEL_POSSIBLE()) { if (IS_LABEL_SUFFIX(0)) { p->lstate = EXPR_END; tokfix(p); pylval.id = intern(tok(p), toklen(p)); return tIDENTIFIER; } } if (p->lstate != EXPR_DOT) { const struct kwtable *kw; /* See if it is a reserved word. */ kw = mrb_reserved_word(tok(p), toklen(p)); if (kw) { enum mrb_lex_state_enum state = p->lstate; pylval.num = p->lineno; p->lstate = kw->state; if (state == EXPR_FNAME) { pylval.id = intern_cstr(kw->name); return kw->id[0]; } if (p->lstate == EXPR_BEG) { p->cmd_start = TRUE; } if (kw->id[0] == keyword_do) { if (p->lpar_beg && p->lpar_beg == p->paren_nest) { p->lpar_beg = 0; p->paren_nest--; return keyword_do_LAMBDA; } if (COND_P()) return keyword_do_cond; if (CMDARG_P() && state != EXPR_CMDARG) return keyword_do_block; if (state == EXPR_ENDARG || state == EXPR_BEG) return keyword_do_block; return keyword_do; } if (state == EXPR_BEG || state == EXPR_VALUE || state == EXPR_CLASS) return kw->id[0]; else { if (kw->id[0] != kw->id[1]) p->lstate = EXPR_BEG; return kw->id[1]; } } } if (IS_BEG() || p->lstate == EXPR_DOT || IS_ARG()) { if (cmd_state) { p->lstate = EXPR_CMDARG; } else { p->lstate = EXPR_ARG; } } else if (p->lstate == EXPR_FNAME) { p->lstate = EXPR_ENDFN; } else { p->lstate = EXPR_END; } } { mrb_sym ident = intern(tok(p), toklen(p)); pylval.id = ident; if (last_state != EXPR_DOT && ISLOWER(tok(p)[0]) && local_var_p(p, ident)) { p->lstate = EXPR_END; } } return result; } } static int yylex(void *lval, void *lp, parser_state *p) { p->ylval = lval; return parser_yylex(p); } static void parser_init_cxt(parser_state *p, mrb_ccontext *cxt) { if (!cxt) return; if (cxt->filename) mrb_parser_set_filename(p, cxt->filename); if (cxt->lineno) p->lineno = cxt->lineno; if (cxt->syms) { int i; p->locals = cons(0,0); for (i=0; islen; i++) { local_add_f(p, cxt->syms[i]); } } p->capture_errors = cxt->capture_errors; p->no_optimize = cxt->no_optimize; p->no_ext_ops = cxt->no_ext_ops; p->upper = cxt->upper; if (cxt->partial_hook) { p->cxt = cxt; } } static void parser_update_cxt(parser_state *p, mrb_ccontext *cxt) { node *n, *n0; int i = 0; if (!cxt) return; if (!p->tree) return; if (intn(p->tree->car) != NODE_SCOPE) return; n0 = n = p->tree->cdr->car; while (n) { i++; n = n->cdr; } cxt->syms = (mrb_sym*)mrbc_realloc(cxt->syms, i*sizeof(mrb_sym)); cxt->slen = i; for (i=0, n=n0; n; i++,n=n->cdr) { cxt->syms[i] = sym(n->car); } } void mrb_parser_dump(mrb_state *mrb, node *tree, int offset); MRB_API void mrb_parser_parse(parser_state *p, mrb_ccontext *c) { struct mrb_jmpbuf buf1; struct mrb_jmpbuf *prev = p->mrb->jmp; p->mrb->jmp = &buf1; MRB_TRY(p->mrb->jmp) { int n = 1; p->cmd_start = TRUE; p->in_def = p->in_single = 0; p->nerr = p->nwarn = 0; p->lex_strterm = NULL; parser_init_cxt(p, c); n = yyparse(p); if (n != 0 || p->nerr > 0) { p->tree = 0; p->mrb->jmp = prev; return; } parser_update_cxt(p, c); if (c && c->dump_result) { mrb_parser_dump(p->mrb, p->tree, 0); } } MRB_CATCH(p->mrb->jmp) { p->nerr++; if (p->mrb->exc == NULL) { yyerror(NULL, p, "memory allocation error"); p->nerr++; p->tree = 0; } } MRB_END_EXC(p->jmp); p->mrb->jmp = prev; } MRB_API parser_state* mrb_parser_new(mrb_state *mrb) { mempool *pool; parser_state *p; static const parser_state parser_state_zero = { 0 }; pool = mempool_open(); if (!pool) return NULL; p = (parser_state*)mempool_alloc(pool, sizeof(parser_state)); if (!p) return NULL; *p = parser_state_zero; p->mrb = mrb; p->pool = pool; p->s = p->send = NULL; #ifndef MRB_NO_STDIO p->f = NULL; #endif p->cmd_start = TRUE; p->in_def = p->in_single = 0; p->capture_errors = FALSE; p->lineno = 1; p->column = 0; #if defined(PARSER_TEST) || defined(PARSER_DEBUG) yydebug = 1; #endif p->tsiz = MRB_PARSER_TOKBUF_SIZE; p->tokbuf = p->buf; p->lex_strterm = NULL; p->current_filename_index = -1; p->filename_table = NULL; p->filename_table_length = 0; return p; } MRB_API void mrb_parser_free(parser_state *p) { if (p->tokbuf != p->buf) { mrbc_free(p->tokbuf); } mempool_close(p->pool); } MRB_API mrb_ccontext* mrb_ccontext_new(mrb_state *mrb) { static const mrb_ccontext cc_zero = { 0 }; mrb_ccontext *cc = (mrb_ccontext*)mrbc_malloc(sizeof(mrb_ccontext)); *cc = cc_zero; return cc; } MRB_API void mrb_ccontext_free(mrb_state *mrb, mrb_ccontext *cxt) { mrbc_free(cxt->filename); mrbc_free(cxt->syms); mrbc_free(cxt); } MRB_API const char* mrb_ccontext_filename(mrb_state *mrb, mrb_ccontext *c, const char *s) { if (s) { size_t len = strlen(s); char *p = (char*)mrbc_malloc(len + 1); if (p == NULL) return NULL; memcpy(p, s, len + 1); if (c->filename) { mrbc_free(c->filename); } c->filename = p; } return c->filename; } MRB_API void mrb_ccontext_partial_hook(mrb_ccontext *c, int (*func)(struct mrb_parser_state*), void *data) { c->partial_hook = func; c->partial_data = data; } MRB_API void mrb_ccontext_cleanup_local_variables(mrb_ccontext *c) { if (c->syms) { mrbc_free(c->syms); c->syms = NULL; c->slen = 0; } c->keep_lv = FALSE; } MRB_API void mrb_parser_set_filename(struct mrb_parser_state *p, const char *f) { mrb_sym sym; uint16_t i; mrb_sym* new_table; sym = mrb_intern_cstr(p->mrb, f); p->filename_sym = sym; p->lineno = (p->filename_table_length > 0)? 0 : 1; for (i = 0; i < p->filename_table_length; i++) { if (p->filename_table[i] == sym) { p->current_filename_index = i; return; } } if (p->filename_table_length == UINT16_MAX) { yyerror(NULL, p, "too many files to compile"); return; } p->current_filename_index = p->filename_table_length++; new_table = (mrb_sym*)parser_palloc(p, sizeof(mrb_sym) * p->filename_table_length); if (p->filename_table) { memmove(new_table, p->filename_table, sizeof(mrb_sym) * p->current_filename_index); } p->filename_table = new_table; p->filename_table[p->filename_table_length - 1] = sym; } MRB_API mrb_sym mrb_parser_get_filename(struct mrb_parser_state* p, uint16_t idx) { if (idx >= p->filename_table_length) return 0; else { return p->filename_table[idx]; } } #ifndef MRB_NO_STDIO static struct mrb_parser_state * mrb_parse_file_continue(mrb_state *mrb, FILE *f, const void *prebuf, size_t prebufsize, mrb_ccontext *c) { parser_state *p; p = mrb_parser_new(mrb); if (!p) return NULL; if (prebuf) { p->s = (const char*)prebuf; p->send = (const char*)prebuf + prebufsize; } else { p->s = p->send = NULL; } p->f = f; mrb_parser_parse(p, c); return p; } MRB_API parser_state* mrb_parse_file(mrb_state *mrb, FILE *f, mrb_ccontext *c) { return mrb_parse_file_continue(mrb, f, NULL, 0, c); } #endif MRB_API parser_state* mrb_parse_nstring(mrb_state *mrb, const char *s, size_t len, mrb_ccontext *c) { parser_state *p; p = mrb_parser_new(mrb); if (!p) return NULL; p->s = s; p->send = s + len; mrb_parser_parse(p, c); return p; } MRB_API parser_state* mrb_parse_string(mrb_state *mrb, const char *s, mrb_ccontext *c) { return mrb_parse_nstring(mrb, s, strlen(s), c); } MRB_API mrb_value mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrb_ccontext *c) { struct RClass *target = mrb->object_class; struct RProc *proc; mrb_value v; mrb_int keep = 0; if (!p) { return mrb_undef_value(); } if (!p->tree || p->nerr) { if (c) c->parser_nerr = p->nerr; if (p->capture_errors) { char buf[256]; strcpy(buf, "line "); dump_int(p->error_buffer[0].lineno, buf+5); strcat(buf, ": "); strncat(buf, p->error_buffer[0].message, sizeof(buf) - strlen(buf) - 1); mrb->exc = mrb_obj_ptr(mrb_exc_new(mrb, E_SYNTAX_ERROR, buf, strlen(buf))); mrb_parser_free(p); return mrb_undef_value(); } else { if (mrb->exc == NULL) { mrb->exc = mrb_obj_ptr(mrb_exc_new_lit(mrb, E_SYNTAX_ERROR, "syntax error")); } mrb_parser_free(p); return mrb_undef_value(); } } proc = mrb_generate_code(mrb, p); mrb_parser_free(p); if (proc == NULL) { if (mrb->exc == NULL) { mrb->exc = mrb_obj_ptr(mrb_exc_new_lit(mrb, E_SCRIPT_ERROR, "codegen error")); } return mrb_undef_value(); } if (c) { if (c->dump_result) mrb_codedump_all(mrb, proc); if (c->no_exec) return mrb_obj_value(proc); if (c->target_class) { target = c->target_class; } if (c->keep_lv) { keep = c->slen + 1; } else { c->keep_lv = TRUE; } } MRB_PROC_SET_TARGET_CLASS(proc, target); if (mrb->c->ci) { mrb_vm_ci_target_class_set(mrb->c->ci, target); } v = mrb_top_run(mrb, proc, mrb_top_self(mrb), keep); if (mrb->exc) return mrb_nil_value(); return v; } #ifndef MRB_NO_STDIO MRB_API mrb_value mrb_load_file_cxt(mrb_state *mrb, FILE *f, mrb_ccontext *c) { return mrb_load_exec(mrb, mrb_parse_file(mrb, f, c), c); } MRB_API mrb_value mrb_load_file(mrb_state *mrb, FILE *f) { return mrb_load_file_cxt(mrb, f, NULL); } #define DETECT_SIZE 64 /* * In order to be recognized as a `.mrb` file, the following three points must be satisfied: * - File starts with "RITE" * - At least `sizeof(struct rite_binary_header)` bytes can be read * - `NUL` is included in the first 64 bytes of the file */ MRB_API mrb_value mrb_load_detect_file_cxt(mrb_state *mrb, FILE *fp, mrb_ccontext *c) { union { char b[DETECT_SIZE]; struct rite_binary_header h; } leading; size_t bufsize; if (mrb == NULL || fp == NULL) { return mrb_nil_value(); } bufsize = fread(leading.b, sizeof(char), sizeof(leading), fp); if (bufsize < sizeof(leading.h) || memcmp(leading.h.binary_ident, RITE_BINARY_IDENT, sizeof(leading.h.binary_ident)) != 0 || memchr(leading.b, '\0', bufsize) == NULL) { return mrb_load_exec(mrb, mrb_parse_file_continue(mrb, fp, leading.b, bufsize, c), c); } else { mrb_int binsize; uint8_t *bin; mrb_value bin_obj = mrb_nil_value(); /* temporary string object */ mrb_value result; binsize = bin_to_uint32(leading.h.binary_size); bin_obj = mrb_str_new(mrb, NULL, binsize); bin = (uint8_t*)RSTRING_PTR(bin_obj); if ((size_t)binsize > bufsize) { memcpy(bin, leading.b, bufsize); if (fread(bin + bufsize, binsize - bufsize, 1, fp) == 0) { binsize = bufsize; /* The error is reported by mrb_load_irep_buf_cxt() */ } } result = mrb_load_irep_buf_cxt(mrb, bin, binsize, c); if (mrb_string_p(bin_obj)) mrb_str_resize(mrb, bin_obj, 0); return result; } } #endif MRB_API mrb_value mrb_load_nstring_cxt(mrb_state *mrb, const char *s, size_t len, mrb_ccontext *c) { return mrb_load_exec(mrb, mrb_parse_nstring(mrb, s, len, c), c); } MRB_API mrb_value mrb_load_nstring(mrb_state *mrb, const char *s, size_t len) { return mrb_load_nstring_cxt(mrb, s, len, NULL); } MRB_API mrb_value mrb_load_string_cxt(mrb_state *mrb, const char *s, mrb_ccontext *c) { return mrb_load_nstring_cxt(mrb, s, strlen(s), c); } MRB_API mrb_value mrb_load_string(mrb_state *mrb, const char *s) { return mrb_load_string_cxt(mrb, s, NULL); } #ifndef MRB_NO_STDIO static void dump_prefix(node *tree, int offset) { printf("%05d ", tree->lineno); while (offset--) { putc(' ', stdout); putc(' ', stdout); } } static void dump_recur(mrb_state *mrb, node *tree, int offset) { while (tree) { mrb_parser_dump(mrb, tree->car, offset); tree = tree->cdr; } } static void dump_args(mrb_state *mrb, node *n, int offset) { if (n->car) { dump_prefix(n, offset+1); printf("mandatory args:\n"); dump_recur(mrb, n->car, offset+2); } n = n->cdr; if (n->car) { dump_prefix(n, offset+1); printf("optional args:\n"); { node *n2 = n->car; while (n2) { dump_prefix(n2, offset+2); printf("%s=\n", mrb_sym_name(mrb, sym(n2->car->car))); mrb_parser_dump(mrb, n2->car->cdr, offset+3); n2 = n2->cdr; } } } n = n->cdr; if (n->car) { mrb_sym rest = sym(n->car); dump_prefix(n, offset+1); if (rest == MRB_OPSYM(mul)) printf("rest=*\n"); else printf("rest=*%s\n", mrb_sym_name(mrb, rest)); } n = n->cdr; if (n->car) { dump_prefix(n, offset+1); printf("post mandatory args:\n"); dump_recur(mrb, n->car, offset+2); } n = n->cdr; if (n) { mrb_assert(intn(n->car) == NODE_ARGS_TAIL); mrb_parser_dump(mrb, n, offset); } } /* * This function restores the GC arena on return. * For this reason, if a process that further generates an object is * performed at the caller, the string pointer returned as the return * value may become invalid. */ static const char* str_dump(mrb_state *mrb, const char *str, int len) { int ai = mrb_gc_arena_save(mrb); mrb_value s; # if INT_MAX > MRB_INT_MAX / 4 /* check maximum length with "\xNN" character */ if (len > MRB_INT_MAX / 4) { len = MRB_INT_MAX / 4; } # endif s = mrb_str_new(mrb, str, (mrb_int)len); s = mrb_str_dump(mrb, s); mrb_gc_arena_restore(mrb, ai); return RSTRING_PTR(s); } #endif void mrb_parser_dump(mrb_state *mrb, node *tree, int offset) { #ifndef MRB_NO_STDIO int nodetype; if (!tree) return; again: dump_prefix(tree, offset); nodetype = intn(tree->car); tree = tree->cdr; switch (nodetype) { case NODE_BEGIN: printf("NODE_BEGIN:\n"); dump_recur(mrb, tree, offset+1); break; case NODE_RESCUE: printf("NODE_RESCUE:\n"); if (tree->car) { dump_prefix(tree, offset+1); printf("body:\n"); mrb_parser_dump(mrb, tree->car, offset+2); } tree = tree->cdr; if (tree->car) { node *n2 = tree->car; dump_prefix(n2, offset+1); printf("rescue:\n"); while (n2) { node *n3 = n2->car; if (n3->car) { dump_prefix(n2, offset+2); printf("handle classes:\n"); dump_recur(mrb, n3->car, offset+3); } if (n3->cdr->car) { dump_prefix(n3, offset+2); printf("exc_var:\n"); mrb_parser_dump(mrb, n3->cdr->car, offset+3); } if (n3->cdr->cdr->car) { dump_prefix(n3, offset+2); printf("rescue body:\n"); mrb_parser_dump(mrb, n3->cdr->cdr->car, offset+3); } n2 = n2->cdr; } } tree = tree->cdr; if (tree->car) { dump_prefix(tree, offset+1); printf("else:\n"); mrb_parser_dump(mrb, tree->car, offset+2); } break; case NODE_ENSURE: printf("NODE_ENSURE:\n"); dump_prefix(tree, offset+1); printf("body:\n"); mrb_parser_dump(mrb, tree->car, offset+2); dump_prefix(tree, offset+1); printf("ensure:\n"); mrb_parser_dump(mrb, tree->cdr->cdr, offset+2); break; case NODE_LAMBDA: printf("NODE_LAMBDA:\n"); dump_prefix(tree, offset+1); goto block; case NODE_BLOCK: block: printf("NODE_BLOCK:\n"); tree = tree->cdr; if (tree->car) { dump_args(mrb, tree->car, offset+1); } dump_prefix(tree, offset+1); printf("body:\n"); mrb_parser_dump(mrb, tree->cdr->car, offset+2); break; case NODE_IF: printf("NODE_IF:\n"); dump_prefix(tree, offset+1); printf("cond:\n"); mrb_parser_dump(mrb, tree->car, offset+2); dump_prefix(tree, offset+1); printf("then:\n"); mrb_parser_dump(mrb, tree->cdr->car, offset+2); if (tree->cdr->cdr->car) { dump_prefix(tree, offset+1); printf("else:\n"); mrb_parser_dump(mrb, tree->cdr->cdr->car, offset+2); } break; case NODE_AND: printf("NODE_AND:\n"); mrb_parser_dump(mrb, tree->car, offset+1); mrb_parser_dump(mrb, tree->cdr, offset+1); break; case NODE_OR: printf("NODE_OR:\n"); mrb_parser_dump(mrb, tree->car, offset+1); mrb_parser_dump(mrb, tree->cdr, offset+1); break; case NODE_CASE: printf("NODE_CASE:\n"); if (tree->car) { mrb_parser_dump(mrb, tree->car, offset+1); } tree = tree->cdr; while (tree) { dump_prefix(tree, offset+1); printf("case:\n"); dump_recur(mrb, tree->car->car, offset+2); dump_prefix(tree, offset+1); printf("body:\n"); mrb_parser_dump(mrb, tree->car->cdr, offset+2); tree = tree->cdr; } break; case NODE_WHILE: printf("NODE_WHILE:\n"); dump_prefix(tree, offset+1); printf("cond:\n"); mrb_parser_dump(mrb, tree->car, offset+2); dump_prefix(tree, offset+1); printf("body:\n"); mrb_parser_dump(mrb, tree->cdr, offset+2); break; case NODE_UNTIL: printf("NODE_UNTIL:\n"); dump_prefix(tree, offset+1); printf("cond:\n"); mrb_parser_dump(mrb, tree->car, offset+2); dump_prefix(tree, offset+1); printf("body:\n"); mrb_parser_dump(mrb, tree->cdr, offset+2); break; case NODE_FOR: printf("NODE_FOR:\n"); dump_prefix(tree, offset+1); printf("var:\n"); { node *n2 = tree->car; if (n2->car) { dump_prefix(n2, offset+2); printf("pre:\n"); dump_recur(mrb, n2->car, offset+3); } n2 = n2->cdr; if (n2) { if (n2->car) { dump_prefix(n2, offset+2); printf("rest:\n"); mrb_parser_dump(mrb, n2->car, offset+3); } n2 = n2->cdr; if (n2) { if (n2->car) { dump_prefix(n2, offset+2); printf("post:\n"); dump_recur(mrb, n2->car, offset+3); } } } } tree = tree->cdr; dump_prefix(tree, offset+1); printf("in:\n"); mrb_parser_dump(mrb, tree->car, offset+2); tree = tree->cdr; dump_prefix(tree, offset+1); printf("do:\n"); mrb_parser_dump(mrb, tree->car, offset+2); break; case NODE_SCOPE: printf("NODE_SCOPE:\n"); { node *n2 = tree->car; mrb_bool first_lval = TRUE; if (n2 && (n2->car || n2->cdr)) { dump_prefix(n2, offset+1); printf("local variables:\n"); dump_prefix(n2, offset+2); while (n2) { if (n2->car) { if (!first_lval) printf(", "); printf("%s", mrb_sym_name(mrb, sym(n2->car))); first_lval = FALSE; } n2 = n2->cdr; } printf("\n"); } } tree = tree->cdr; offset++; goto again; case NODE_FCALL: case NODE_CALL: case NODE_SCALL: switch (nodetype) { case NODE_FCALL: printf("NODE_FCALL:\n"); break; case NODE_CALL: printf("NODE_CALL(.):\n"); break; case NODE_SCALL: printf("NODE_SCALL(&.):\n"); break; default: break; } mrb_parser_dump(mrb, tree->car, offset+1); dump_prefix(tree, offset+1); printf("method='%s' (%d)\n", mrb_sym_dump(mrb, sym(tree->cdr->car)), intn(tree->cdr->car)); tree = tree->cdr->cdr->car; if (tree) { dump_prefix(tree, offset+1); printf("args:\n"); dump_recur(mrb, tree->car, offset+2); if (tree->cdr) { if (tree->cdr->car) { dump_prefix(tree, offset+1); printf("kwargs:\n"); mrb_parser_dump(mrb, tree->cdr->car, offset+2); } if (tree->cdr->cdr) { dump_prefix(tree, offset+1); printf("block:\n"); mrb_parser_dump(mrb, tree->cdr->cdr, offset+2); } } } break; case NODE_DOT2: printf("NODE_DOT2:\n"); mrb_parser_dump(mrb, tree->car, offset+1); mrb_parser_dump(mrb, tree->cdr, offset+1); break; case NODE_DOT3: printf("NODE_DOT3:\n"); mrb_parser_dump(mrb, tree->car, offset+1); mrb_parser_dump(mrb, tree->cdr, offset+1); break; case NODE_COLON2: printf("NODE_COLON2:\n"); mrb_parser_dump(mrb, tree->car, offset+1); dump_prefix(tree, offset+1); printf("::%s\n", mrb_sym_name(mrb, sym(tree->cdr))); break; case NODE_COLON3: printf("NODE_COLON3: ::%s\n", mrb_sym_name(mrb, sym(tree))); break; case NODE_ARRAY: printf("NODE_ARRAY:\n"); dump_recur(mrb, tree, offset+1); break; case NODE_HASH: printf("NODE_HASH:\n"); while (tree) { dump_prefix(tree, offset+1); printf("key:\n"); mrb_parser_dump(mrb, tree->car->car, offset+2); dump_prefix(tree, offset+1); printf("value:\n"); mrb_parser_dump(mrb, tree->car->cdr, offset+2); tree = tree->cdr; } break; case NODE_KW_HASH: printf("NODE_KW_HASH:\n"); while (tree) { dump_prefix(tree, offset+1); printf("key:\n"); mrb_parser_dump(mrb, tree->car->car, offset+2); dump_prefix(tree, offset+1); printf("value:\n"); mrb_parser_dump(mrb, tree->car->cdr, offset+2); tree = tree->cdr; } break; case NODE_SPLAT: printf("NODE_SPLAT:\n"); mrb_parser_dump(mrb, tree, offset+1); break; case NODE_ASGN: printf("NODE_ASGN:\n"); dump_prefix(tree, offset+1); printf("lhs:\n"); mrb_parser_dump(mrb, tree->car, offset+2); dump_prefix(tree, offset+1); printf("rhs:\n"); mrb_parser_dump(mrb, tree->cdr, offset+2); break; case NODE_MASGN: printf("NODE_MASGN:\n"); dump_prefix(tree, offset+1); printf("mlhs:\n"); { node *n2 = tree->car; if (n2->car) { dump_prefix(tree, offset+2); printf("pre:\n"); dump_recur(mrb, n2->car, offset+3); } n2 = n2->cdr; if (n2) { if (n2->car) { dump_prefix(n2, offset+2); printf("rest:\n"); if (n2->car == nint(-1)) { dump_prefix(n2, offset+2); printf("(empty)\n"); } else { mrb_parser_dump(mrb, n2->car, offset+3); } } n2 = n2->cdr; if (n2 && n2->car) { dump_prefix(n2, offset+2); printf("post:\n"); dump_recur(mrb, n2->car, offset+3); } } } dump_prefix(tree, offset+1); printf("rhs:\n"); mrb_parser_dump(mrb, tree->cdr, offset+2); break; case NODE_OP_ASGN: printf("NODE_OP_ASGN:\n"); dump_prefix(tree, offset+1); printf("lhs:\n"); mrb_parser_dump(mrb, tree->car, offset+2); tree = tree->cdr; dump_prefix(tree, offset+1); printf("op='%s' (%d)\n", mrb_sym_name(mrb, sym(tree->car)), intn(tree->car)); tree = tree->cdr; mrb_parser_dump(mrb, tree->car, offset+1); break; case NODE_SUPER: printf("NODE_SUPER:\n"); if (tree) { dump_prefix(tree, offset+1); printf("args:\n"); dump_recur(mrb, tree->car, offset+2); if (tree->cdr) { dump_prefix(tree, offset+1); printf("block:\n"); mrb_parser_dump(mrb, tree->cdr, offset+2); } } break; case NODE_ZSUPER: printf("NODE_ZSUPER:\n"); if (tree) { dump_prefix(tree, offset+1); printf("args:\n"); dump_recur(mrb, tree->car, offset+2); if (tree->cdr) { dump_prefix(tree, offset+1); printf("block:\n"); mrb_parser_dump(mrb, tree->cdr, offset+2); } } break; case NODE_RETURN: printf("NODE_RETURN:\n"); mrb_parser_dump(mrb, tree, offset+1); break; case NODE_YIELD: printf("NODE_YIELD:\n"); dump_recur(mrb, tree, offset+1); break; case NODE_BREAK: printf("NODE_BREAK:\n"); mrb_parser_dump(mrb, tree, offset+1); break; case NODE_NEXT: printf("NODE_NEXT:\n"); mrb_parser_dump(mrb, tree, offset+1); break; case NODE_REDO: printf("NODE_REDO\n"); break; case NODE_RETRY: printf("NODE_RETRY\n"); break; case NODE_LVAR: printf("NODE_LVAR %s\n", mrb_sym_name(mrb, sym(tree))); break; case NODE_GVAR: printf("NODE_GVAR %s\n", mrb_sym_name(mrb, sym(tree))); break; case NODE_IVAR: printf("NODE_IVAR %s\n", mrb_sym_name(mrb, sym(tree))); break; case NODE_CVAR: printf("NODE_CVAR %s\n", mrb_sym_name(mrb, sym(tree))); break; case NODE_NVAR: printf("NODE_NVAR %d\n", intn(tree)); break; case NODE_CONST: printf("NODE_CONST %s\n", mrb_sym_name(mrb, sym(tree))); break; case NODE_MATCH: printf("NODE_MATCH:\n"); dump_prefix(tree, offset + 1); printf("lhs:\n"); mrb_parser_dump(mrb, tree->car, offset + 2); dump_prefix(tree, offset + 1); printf("rhs:\n"); mrb_parser_dump(mrb, tree->cdr, offset + 2); break; case NODE_BACK_REF: printf("NODE_BACK_REF: $%c\n", intn(tree)); break; case NODE_NTH_REF: printf("NODE_NTH_REF: $%d\n", intn(tree)); break; case NODE_ARG: printf("NODE_ARG %s\n", mrb_sym_name(mrb, sym(tree))); break; case NODE_BLOCK_ARG: printf("NODE_BLOCK_ARG:\n"); mrb_parser_dump(mrb, tree, offset+1); break; case NODE_INT: printf("NODE_INT %s base %d\n", (char*)tree->car, intn(tree->cdr->car)); break; case NODE_FLOAT: printf("NODE_FLOAT %s\n", (char*)tree); break; case NODE_NEGATE: printf("NODE_NEGATE:\n"); mrb_parser_dump(mrb, tree, offset+1); break; case NODE_STR: printf("NODE_STR %s len %d\n", str_dump(mrb, (char*)tree->car, intn(tree->cdr)), intn(tree->cdr)); break; case NODE_DSTR: printf("NODE_DSTR:\n"); dump_recur(mrb, tree, offset+1); break; case NODE_XSTR: printf("NODE_XSTR %s len %d\n", str_dump(mrb, (char*)tree->car, intn(tree->cdr)), intn(tree->cdr)); break; case NODE_DXSTR: printf("NODE_DXSTR:\n"); dump_recur(mrb, tree, offset+1); break; case NODE_REGX: printf("NODE_REGX /%s/\n", (char*)tree->car); if (tree->cdr->car) { dump_prefix(tree, offset+1); printf("opt: %s\n", (char*)tree->cdr->car); } if (tree->cdr->cdr) { dump_prefix(tree, offset+1); printf("enc: %s\n", (char*)tree->cdr->cdr); } break; case NODE_DREGX: printf("NODE_DREGX:\n"); dump_recur(mrb, tree->car, offset+1); dump_prefix(tree, offset+1); printf("tail: %s\n", (char*)tree->cdr->cdr->car); if (tree->cdr->cdr->cdr->car) { dump_prefix(tree, offset+1); printf("opt: %s\n", (char*)tree->cdr->cdr->cdr->car); } if (tree->cdr->cdr->cdr->cdr) { dump_prefix(tree, offset+1); printf("enc: %s\n", (char*)tree->cdr->cdr->cdr->cdr); } break; case NODE_SYM: printf("NODE_SYM :%s (%d)\n", mrb_sym_dump(mrb, sym(tree)), intn(tree)); break; case NODE_DSYM: printf("NODE_DSYM:\n"); mrb_parser_dump(mrb, tree, offset+1); break; case NODE_WORDS: printf("NODE_WORDS:\n"); dump_recur(mrb, tree, offset+1); break; case NODE_SYMBOLS: printf("NODE_SYMBOLS:\n"); dump_recur(mrb, tree, offset+1); break; case NODE_LITERAL_DELIM: printf("NODE_LITERAL_DELIM\n"); break; case NODE_SELF: printf("NODE_SELF\n"); break; case NODE_NIL: printf("NODE_NIL\n"); break; case NODE_TRUE: printf("NODE_TRUE\n"); break; case NODE_FALSE: printf("NODE_FALSE\n"); break; case NODE_ALIAS: printf("NODE_ALIAS %s %s:\n", mrb_sym_dump(mrb, sym(tree->car)), mrb_sym_dump(mrb, sym(tree->cdr))); break; case NODE_UNDEF: printf("NODE_UNDEF"); { node *t = tree; while (t) { printf(" %s", mrb_sym_dump(mrb, sym(t->car))); t = t->cdr; } } printf(":\n"); break; case NODE_CLASS: printf("NODE_CLASS:\n"); if (tree->car->car == nint(0)) { dump_prefix(tree, offset+1); printf(":%s\n", mrb_sym_name(mrb, sym(tree->car->cdr))); } else if (tree->car->car == nint(1)) { dump_prefix(tree, offset+1); printf("::%s\n", mrb_sym_name(mrb, sym(tree->car->cdr))); } else { mrb_parser_dump(mrb, tree->car->car, offset+1); dump_prefix(tree, offset+1); printf("::%s\n", mrb_sym_name(mrb, sym(tree->car->cdr))); } if (tree->cdr->car) { dump_prefix(tree, offset+1); printf("super:\n"); mrb_parser_dump(mrb, tree->cdr->car, offset+2); } dump_prefix(tree, offset+1); printf("body:\n"); mrb_parser_dump(mrb, tree->cdr->cdr->car->cdr, offset+2); break; case NODE_MODULE: printf("NODE_MODULE:\n"); if (tree->car->car == nint(0)) { dump_prefix(tree, offset+1); printf(":%s\n", mrb_sym_name(mrb, sym(tree->car->cdr))); } else if (tree->car->car == nint(1)) { dump_prefix(tree, offset+1); printf("::%s\n", mrb_sym_name(mrb, sym(tree->car->cdr))); } else { mrb_parser_dump(mrb, tree->car->car, offset+1); dump_prefix(tree, offset+1); printf("::%s\n", mrb_sym_name(mrb, sym(tree->car->cdr))); } dump_prefix(tree, offset+1); printf("body:\n"); mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2); break; case NODE_SCLASS: printf("NODE_SCLASS:\n"); mrb_parser_dump(mrb, tree->car, offset+1); dump_prefix(tree, offset+1); printf("body:\n"); mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2); break; case NODE_DEF: printf("NODE_DEF:\n"); dump_prefix(tree, offset+1); printf("%s\n", mrb_sym_dump(mrb, sym(tree->car))); tree = tree->cdr; { node *n2 = tree->car; mrb_bool first_lval = TRUE; if (n2 && (n2->car || n2->cdr)) { dump_prefix(n2, offset+1); printf("local variables:\n"); dump_prefix(n2, offset+2); while (n2) { if (n2->car) { if (!first_lval) printf(", "); printf("%s", mrb_sym_name(mrb, sym(n2->car))); first_lval = FALSE; } n2 = n2->cdr; } printf("\n"); } } tree = tree->cdr; if (tree->car) { dump_args(mrb, tree->car, offset); } mrb_parser_dump(mrb, tree->cdr->car, offset+1); break; case NODE_SDEF: printf("NODE_SDEF:\n"); mrb_parser_dump(mrb, tree->car, offset+1); tree = tree->cdr; dump_prefix(tree, offset+1); printf(":%s\n", mrb_sym_dump(mrb, sym(tree->car))); tree = tree->cdr->cdr; if (tree->car) { dump_args(mrb, tree->car, offset+1); } tree = tree->cdr; mrb_parser_dump(mrb, tree->car, offset+1); break; case NODE_POSTEXE: printf("NODE_POSTEXE:\n"); mrb_parser_dump(mrb, tree, offset+1); break; case NODE_HEREDOC: printf("NODE_HEREDOC (<<%s):\n", ((parser_heredoc_info*)tree)->term); dump_recur(mrb, ((parser_heredoc_info*)tree)->doc, offset+1); break; case NODE_ARGS_TAIL: printf("NODE_ARGS_TAIL:\n"); { node *kws = tree->car; while (kws) { mrb_parser_dump(mrb, kws->car, offset+1); kws = kws->cdr; } } tree = tree->cdr; if (tree->car) { mrb_assert(intn(tree->car->car) == NODE_KW_REST_ARGS); mrb_parser_dump(mrb, tree->car, offset+1); } tree = tree->cdr; if (tree->car) { dump_prefix(tree, offset+1); printf("block='%s'\n", mrb_sym_name(mrb, sym(tree->car))); } break; case NODE_KW_ARG: printf("NODE_KW_ARG %s:\n", mrb_sym_name(mrb, sym(tree->car))); mrb_parser_dump(mrb, tree->cdr->car, offset + 1); break; case NODE_KW_REST_ARGS: if (tree) printf("NODE_KW_REST_ARGS %s\n", mrb_sym_name(mrb, sym(tree))); else printf("NODE_KW_REST_ARGS\n"); break; default: printf("node type: %d (0x%x)\n", nodetype, (unsigned)nodetype); break; } #endif } typedef mrb_bool mrb_parser_foreach_top_variable_func(mrb_state *mrb, mrb_sym sym, void *user); void mrb_parser_foreach_top_variable(mrb_state *mrb, struct mrb_parser_state *p, mrb_parser_foreach_top_variable_func *func, void *user); void mrb_parser_foreach_top_variable(mrb_state *mrb, struct mrb_parser_state *p, mrb_parser_foreach_top_variable_func *func, void *user) { const mrb_ast_node *n = p->tree; if ((intptr_t)n->car == NODE_SCOPE) { n = n->cdr->car; for (; n; n = n->cdr) { mrb_sym sym = sym(n->car); if (sym && !func(mrb, sym, user)) break; } } } nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compiler/core/PaxHeaders/keywords0000644000000000000000000000013215077107276025017 xustar0030 mtime=1761382078.110420611 30 atime=1761382080.129411375 30 ctime=1761382108.693301558 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compiler/core/keywords0000644000175100017510000000560315077107276025413 0ustar00runnerrunner%{ /* Workaround for `enable_cxx_exception` (#5199) */ #if defined __cplusplus && __cplusplus >= 201103L # define register #endif struct kwtable {const char *name; int id[2]; enum mrb_lex_state_enum state;}; %} struct kwtable; %% __ENCODING__, {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END __FILE__, {keyword__FILE__, keyword__FILE__}, EXPR_END __LINE__, {keyword__LINE__, keyword__LINE__}, EXPR_END BEGIN, {keyword_BEGIN, keyword_BEGIN}, EXPR_END END, {keyword_END, keyword_END}, EXPR_END alias, {keyword_alias, keyword_alias}, EXPR_FNAME and, {keyword_and, keyword_and}, EXPR_VALUE begin, {keyword_begin, keyword_begin}, EXPR_BEG break, {keyword_break, keyword_break}, EXPR_MID case, {keyword_case, keyword_case}, EXPR_VALUE class, {keyword_class, keyword_class}, EXPR_CLASS def, {keyword_def, keyword_def}, EXPR_FNAME do, {keyword_do, keyword_do}, EXPR_BEG else, {keyword_else, keyword_else}, EXPR_BEG elsif, {keyword_elsif, keyword_elsif}, EXPR_VALUE end, {keyword_end, keyword_end}, EXPR_END ensure, {keyword_ensure, keyword_ensure}, EXPR_BEG false, {keyword_false, keyword_false}, EXPR_END for, {keyword_for, keyword_for}, EXPR_VALUE if, {keyword_if, modifier_if}, EXPR_VALUE in, {keyword_in, keyword_in}, EXPR_VALUE module, {keyword_module, keyword_module}, EXPR_VALUE next, {keyword_next, keyword_next}, EXPR_MID nil, {keyword_nil, keyword_nil}, EXPR_END not, {keyword_not, keyword_not}, EXPR_ARG or, {keyword_or, keyword_or}, EXPR_VALUE redo, {keyword_redo, keyword_redo}, EXPR_END rescue, {keyword_rescue, modifier_rescue}, EXPR_MID retry, {keyword_retry, keyword_retry}, EXPR_END return, {keyword_return, keyword_return}, EXPR_MID self, {keyword_self, keyword_self}, EXPR_END super, {keyword_super, keyword_super}, EXPR_ARG then, {keyword_then, keyword_then}, EXPR_BEG true, {keyword_true, keyword_true}, EXPR_END undef, {keyword_undef, keyword_undef}, EXPR_FNAME unless, {keyword_unless, modifier_unless}, EXPR_VALUE until, {keyword_until, modifier_until}, EXPR_VALUE when, {keyword_when, keyword_when}, EXPR_VALUE while, {keyword_while, modifier_while}, EXPR_VALUE yield, {keyword_yield, keyword_yield}, EXPR_ARG %% nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-enum-lazy0000644000000000000000000000013215077107334022322 xustar0030 mtime=1761382108.894300976 30 atime=1761382109.798298363 30 ctime=1761382108.894300976 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-lazy/0000755000175100017510000000000015077107334022767 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-lazy/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024521 xustar0030 mtime=1761382078.117420579 30 atime=1761382080.138411334 30 ctime=1761382108.894300976 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-lazy/mrbgem.rake0000644000175100017510000000045615077107276025116 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-enum-lazy') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Enumerator::Lazy class' spec.add_dependency('mruby-enumerator', :core => 'mruby-enumerator') spec.add_dependency('mruby-enum-ext', :core => 'mruby-enum-ext') end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-lazy/PaxHeaders/mrblib0000644000000000000000000000013215077107334023571 xustar0030 mtime=1761382108.896300971 30 atime=1761382109.798298363 30 ctime=1761382108.896300971 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-lazy/mrblib/0000755000175100017510000000000015077107334024236 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-lazy/mrblib/PaxHeaders/lazy.rb0000644000000000000000000000013215077107276025157 xustar0030 mtime=1761382078.117420579 30 atime=1761382080.138411334 30 ctime=1761382108.896300971 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-lazy/mrblib/lazy.rb0000644000175100017510000000725415077107276025557 0ustar00runnerrunnermodule Enumerable # = Enumerable#lazy implementation # # Enumerable#lazy returns an instance of Enumerator::Lazy. # You can use it just like as normal Enumerable object, # except these methods act as 'lazy': # # - map collect # - select find_all # - reject # - grep # - grep_v # - drop # - drop_while # - take_while # - flat_map collect_concat # - zip def lazy Enumerator::Lazy.new(self) end end class Enumerator # == Acknowledgements # # Based on https://github.com/yhara/enumerable-lazy # Inspired by https://github.com/antimon2/enumerable_lz # http://jp.rubyist.net/magazine/?0034-Enumerable_lz (ja) class Lazy < Enumerator def initialize(obj, &block) super(){|yielder| begin obj.each{|x| if block block.call(yielder, x) else yielder << x end } rescue StopIteration end } end def to_enum(meth=:each, *args, &block) unless self.respond_to?(meth) raise ArgumentError, "undefined method #{meth}" end lz = Lazy.new(self, &block) obj = self lz.instance_eval { @obj = obj @meth = meth @args = args } lz end alias enum_for to_enum def map(&block) Lazy.new(self){|yielder, val| yielder << block.call(val) } end alias collect map def select(&block) Lazy.new(self){|yielder, val| if block.call(val) yielder << val end } end alias find_all select def reject(&block) Lazy.new(self){|yielder, val| unless block.call(val) yielder << val end } end def grep(pattern) Lazy.new(self){|yielder, val| if pattern === val yielder << val end } end def grep_v(pattern) Lazy.new(self){|yielder, val| unless pattern === val yielder << val end } end def drop(n) dropped = 0 Lazy.new(self){|yielder, val| if dropped < n dropped += 1 else yielder << val end } end def drop_while(&block) dropping = true Lazy.new(self){|yielder, val| if dropping if not block.call(val) yielder << val dropping = false end else yielder << val end } end def take(n) if n == 0 return Lazy.new(self){raise StopIteration} end taken = 0 Lazy.new(self){|yielder, val| yielder << val taken += 1 if taken >= n raise StopIteration end } end def take_while(&block) Lazy.new(self){|yielder, val| if block.call(val) yielder << val else raise StopIteration end } end def flat_map(&block) Lazy.new(self){|yielder, val| ary = block.call(val) # TODO: check ary is an Array ary.each{|x| yielder << x } } end alias collect_concat flat_map def zip(*args, &block) enums = [self] + args Lazy.new(self){|yielder, val| ary = enums.map{|e| e.next} if block yielder << block.call(ary) else yielder << ary end } end def uniq(&block) hash = {} Lazy.new(self){|yielder, val| if block v = block.call(val) else v = val end unless hash.include?(v) yielder << val hash[v] = val end } end alias force to_a end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-lazy/PaxHeaders/test0000644000000000000000000000013215077107334023301 xustar0030 mtime=1761382108.895300974 30 atime=1761382109.798298363 30 ctime=1761382108.895300974 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-lazy/test/0000755000175100017510000000000015077107334023746 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-lazy/test/PaxHeaders/lazy.rb0000644000000000000000000000013215077107276024667 xustar0030 mtime=1761382078.117420579 30 atime=1761382080.138411334 30 ctime=1761382108.895300974 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-lazy/test/lazy.rb0000644000175100017510000000253415077107276025263 0ustar00runnerrunnerassert("Enumerator::Lazy") do a = [1, 2] assert_equal Enumerator::Lazy, a.lazy.class end assert("Enumerator::Lazy laziness") do a = Object.new def a.each return to_enum :each unless block_given? self.b << 10 yield 1 self.b << 20 yield 2 self.b << 30 yield 3 self.b << 40 yield 4 self.b << 50 yield 5 end def a.b(b=nil) @b = b if b @b end a.b([]) assert_equal [1,2], a.each.lazy.take(2).force assert_equal [10,20], a.b a.b([]) assert_equal [2,4], a.each.lazy.select{|x|x%2==0}.take(2).force assert_equal [10,20,30,40], a.b a.b([]) assert_equal [1], a.each.lazy.take_while{|x|x<2}.take(1).force assert_equal [10], a.b a.b([]) assert_equal [1], a.each.lazy.take_while{|x|x<2}.take(4).force assert_equal [10,20], a.b end assert("Enumerator::Lazy#to_enum") do lazy_enum = (0..).lazy.to_enum(:each_slice, 2) assert_kind_of Enumerator::Lazy, lazy_enum assert_equal [0*1, 2*3, 4*5, 6*7], lazy_enum.map { |a| a.first * a.last }.first(4) end assert("Enumerator::Lazy#grep_v") do lazy_grep_v = (0..).lazy.grep_v(2..4) assert_kind_of Enumerator::Lazy, lazy_grep_v assert_equal [0, 1, 5, 6], lazy_grep_v.first(4) end assert("Enumerator::Lazy#zip with cycle") do e1 = [1, 2, 3].cycle e2 = [:a, :b].cycle assert_equal [[1,:a],[2,:b],[3,:a]], e1.lazy.zip(e2).first(3) end nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-bin-mruby0000644000000000000000000000013215077107334022305 xustar0030 mtime=1761382108.856301086 30 atime=1761382109.798298363 30 ctime=1761382108.856301086 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mruby/0000755000175100017510000000000015077107334022752 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mruby/PaxHeaders/bintest0000644000000000000000000000013215077107334023755 xustar0030 mtime=1761382108.858301081 30 atime=1761382109.798298363 30 ctime=1761382108.858301081 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mruby/bintest/0000755000175100017510000000000015077107334024422 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mruby/bintest/PaxHeaders/mruby.rb0000644000000000000000000000013215077107276025522 xustar0030 mtime=1761382078.107420625 30 atime=1761382080.127411384 30 ctime=1761382108.858301081 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mruby/bintest/mruby.rb0000644000175100017510000001242615077107276026117 0ustar00runnerrunnerrequire 'tempfile' require 'open3' def assert_mruby(exp_out, exp_err, exp_success, args) out, err, stat = Open3.capture3( *(cmd_list("mruby") + args)) assert "assert_mruby" do assert_operator(exp_out, :===, out, "standard output") assert_operator(exp_err, :===, err, "standard error") assert_equal(exp_success, stat.success?, "exit success?") end end assert('regression for #1564') do assert_mruby("", /\A-e:1:2: syntax error, .*\n\z/, false, %w[-e <<]) assert_mruby("", /\A-e:1:3: syntax error, .*\n\z/, false, %w[-e <<-]) end assert('regression for #1572') do script, bin = Tempfile.new('test.rb'), Tempfile.new('test.mrb') File.write script.path, 'p "ok"' system "#{cmd('mrbc')} -g -o #{bin.path} #{script.path}" o = `#{cmd('mruby')} #{bin.path}`.strip assert_equal '"ok"', o end assert '$0 value' do script, bin = Tempfile.new('test.rb'), Tempfile.new('test.mrb') # .rb script script.write "p $0\n" script.flush assert_equal "\"#{script.path}\"", `#{cmd('mruby')} "#{script.path}"`.chomp # .mrb file `#{cmd('mrbc')} -o "#{bin.path}" "#{script.path}"` assert_equal "\"#{bin.path}\"", `#{cmd('mruby')} "#{bin.path}"`.chomp # one liner assert_equal '"-e"', `#{cmd('mruby')} -e #{shellquote('p $0')}`.chomp end assert 'ARGV value' do assert_mruby(%{["ab", "cde"]\n}, "", true, %w[-e p(ARGV) ab cde]) assert_mruby("[]\n", "", true, %w[-e p(ARGV)]) end assert '__END__', '8.6' do script = Tempfile.new('test.rb') script.write < { b = -2; [a, b, c] }' system "#{cmd('mrbc')} -g -o #{cmrb.path} #{crb.path}" File.write drb.path, 'a, b = 5, 6; p A.call; p a, b' system "#{cmd('mrbc')} -g -o #{dmrb.path} #{drb.path}" assert_mruby("[1, -2, 3]\n5\n6\n", "", true, ["-r", crb.path, drb.path]) assert_mruby("[1, -2, 3]\n5\n6\n", "", true, ["-b", "-r", cmrb.path, dmrb.path]) end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mruby/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024504 xustar0030 mtime=1761382078.107420625 30 atime=1761382080.128411379 30 ctime=1761382108.856301086 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mruby/mrbgem.rake0000644000175100017510000000053315077107276025075 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-bin-mruby') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'mruby command' spec.bins = %w(mruby) spec.add_dependency('mruby-compiler', :core => 'mruby-compiler') if build.cxx_exception_enabled? build.compile_as_cxx("#{spec.dir}/tools/mruby/mruby.c") end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mruby/PaxHeaders/tools0000644000000000000000000000013215077107334023445 xustar0030 mtime=1761382108.202302977 30 atime=1761382109.798298363 30 ctime=1761382108.202302977 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mruby/tools/0000755000175100017510000000000015077107334024112 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mruby/tools/PaxHeaders/mruby0000644000000000000000000000013215077107334024603 xustar0030 mtime=1761382108.855301089 30 atime=1761382109.798298363 30 ctime=1761382108.855301089 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mruby/tools/mruby/0000755000175100017510000000000015077107334025250 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mruby/tools/mruby/PaxHeaders/mruby.c0000644000000000000000000000013215077107276026167 xustar0030 mtime=1761382078.107420625 30 atime=1761382080.128411379 30 ctime=1761382108.855301089 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c0000644000175100017510000002226015077107276026561 0ustar00runnerrunner#include #ifdef MRB_NO_STDIO # error mruby-bin-mruby conflicts 'MRB_NO_STDIO' in your build configuration #endif #include #include #include #include #include #include #include #include #include #if defined(_WIN32) # include /* for setmode */ # include #endif struct _args { FILE *rfp; char *cmdline; mrb_bool fname : 1; mrb_bool mrbfile : 1; mrb_bool check_syntax : 1; mrb_bool verbose : 1; mrb_bool version : 1; mrb_bool debug : 1; int argc; char **argv; int libc; char **libv; }; struct options { int argc; char **argv; char *program; char *opt; char short_opt[2]; }; static void usage(const char *name) { static const char *const usage_msg[] = { "switches:", "-b load and execute RiteBinary (mrb) file", "-c check syntax only", "-d set debugging flags (set $DEBUG to true)", "-e 'command' one line of script", "-r library load the library before executing your script", "-v print version number, then run in verbose mode", "--verbose run in verbose mode", "--version print the version", "--copyright print the copyright", NULL }; const char *const *p = usage_msg; printf("Usage: %s [switches] [programfile] [arguments]\n", name); while (*p) printf(" %s\n", *p++); } static mrb_bool mrb_extension_p(const char *path) { const char *e = strrchr(path, '.'); if (e && e[1] == 'm' && e[2] == 'r' && e[3] == 'b' && e[4] == '\0') { return TRUE; } return FALSE; } static void options_init(struct options *opts, int argc, char **argv) { opts->argc = argc; opts->argv = argv; opts->program = *argv; *opts->short_opt = 0; } static const char * options_opt(struct options *opts) { /* concatenated short options (e.g. `-cv`) */ if (*opts->short_opt && *++opts->opt) { short_opt: opts->short_opt[0] = *opts->opt; opts->short_opt[1] = 0; return opts->short_opt; } while (++opts->argv, --opts->argc) { opts->opt = *opts->argv; /* not start with `-` || `-` */ if (opts->opt[0] != '-' || !opts->opt[1]) return NULL; if (opts->opt[1] == '-') { /* `--` */ if (!opts->opt[2]) { opts->argv++, opts->argc--; return NULL; } /* long option */ opts->opt += 2; *opts->short_opt = 0; return opts->opt; } else { /* short option */ opts->opt++; goto short_opt; } } return NULL; } static const char * options_arg(struct options *opts) { if (*opts->short_opt && opts->opt[1]) { /* concatenated short option and option argument (e.g. `-rLIBRARY`) */ *opts->short_opt = 0; return opts->opt + 1; } --opts->argc, ++opts->argv; return opts->argc ? *opts->argv : NULL; } static char * dup_arg_item(mrb_state *mrb, const char *item) { size_t buflen = strlen(item) + 1; char *buf = (char*)mrb_malloc(mrb, buflen); memcpy(buf, item, buflen); return buf; } static int parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args) { static const struct _args args_zero = { 0 }; struct options opts[1]; const char *opt, *item; *args = args_zero; options_init(opts, argc, argv); while ((opt = options_opt(opts))) { if (strcmp(opt, "b") == 0) { args->mrbfile = TRUE; } else if (strcmp(opt, "c") == 0) { args->check_syntax = TRUE; } else if (strcmp(opt, "d") == 0) { args->debug = TRUE; } else if (strcmp(opt, "e") == 0) { if ((item = options_arg(opts))) { if (!args->cmdline) { args->cmdline = dup_arg_item(mrb, item); } else { size_t cmdlinelen; size_t itemlen; cmdlinelen = strlen(args->cmdline); itemlen = strlen(item); args->cmdline = (char*)mrb_realloc(mrb, args->cmdline, cmdlinelen + itemlen + 2); args->cmdline[cmdlinelen] = '\n'; memcpy(args->cmdline + cmdlinelen + 1, item, itemlen + 1); } } else { fprintf(stderr, "%s: No code specified for -e\n", opts->program); return EXIT_FAILURE; } } else if (strcmp(opt, "h") == 0) { usage(opts->program); exit(EXIT_SUCCESS); } else if (strcmp(opt, "r") == 0) { if ((item = options_arg(opts))) { if (args->libc == 0) { args->libv = (char**)mrb_malloc(mrb, sizeof(char*)); } else { args->libv = (char**)mrb_realloc(mrb, args->libv, sizeof(char*) * (args->libc + 1)); } args->libv[args->libc++] = dup_arg_item(mrb, item); } else { fprintf(stderr, "%s: No library specified for -r\n", opts->program); return EXIT_FAILURE; } } else if (strcmp(opt, "v") == 0) { if (!args->verbose) { mrb_show_version(mrb); args->version = TRUE; } args->verbose = TRUE; } else if (strcmp(opt, "version") == 0) { mrb_show_version(mrb); exit(EXIT_SUCCESS); } else if (strcmp(opt, "verbose") == 0) { args->verbose = TRUE; } else if (strcmp(opt, "copyright") == 0) { mrb_show_copyright(mrb); exit(EXIT_SUCCESS); } else { fprintf(stderr, "%s: invalid option %s%s (-h will show valid options)\n", opts->program, opt[1] ? "--" : "-", opt); return EXIT_FAILURE; } } argc = opts->argc; argv = opts->argv; if (args->cmdline == NULL) { if (*argv == NULL) { if (args->version) exit(EXIT_SUCCESS); args->rfp = stdin; } else { args->rfp = strcmp(argv[0], "-") == 0 ? stdin : fopen(argv[0], "rb"); if (args->rfp == NULL) { fprintf(stderr, "%s: Cannot open program file: %s\n", opts->program, argv[0]); return EXIT_FAILURE; } args->fname = TRUE; args->cmdline = argv[0]; argc--; argv++; } } #if defined(_WIN32) if (args->rfp == stdin) { _setmode(_fileno(stdin), O_BINARY); } #endif args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1)); memcpy(args->argv, argv, (argc+1) * sizeof(char*)); args->argc = argc; return EXIT_SUCCESS; } static void cleanup(mrb_state *mrb, struct _args *args) { if (args->rfp && args->rfp != stdin) fclose(args->rfp); if (!args->fname) mrb_free(mrb, args->cmdline); mrb_free(mrb, args->argv); if (args->libc) { while (args->libc--) { mrb_free(mrb, args->libv[args->libc]); } mrb_free(mrb, args->libv); } mrb_close(mrb); } int main(int argc, char **argv) { mrb_state *mrb = mrb_open(); int n = -1; struct _args args; mrb_value ARGV; mrb_value v; if (mrb == NULL) { fprintf(stderr, "%s: Invalid mrb_state, exiting mruby\n", *argv); return EXIT_FAILURE; } n = parse_args(mrb, argc, argv, &args); if (n == EXIT_FAILURE || (args.cmdline == NULL && args.rfp == NULL)) { cleanup(mrb, &args); return n; } else { int ai = mrb_gc_arena_save(mrb); ARGV = mrb_ary_new_capa(mrb, args.argc); for (int i = 0; i < args.argc; i++) { char* utf8 = mrb_utf8_from_locale(args.argv[i], -1); if (utf8) { mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8)); mrb_utf8_free(utf8); } } mrb_define_global_const(mrb, "ARGV", ARGV); mrb_gv_set(mrb, mrb_intern_lit(mrb, "$DEBUG"), mrb_bool_value(args.debug)); mrb_ccontext *c = mrb_ccontext_new(mrb); if (args.verbose) c->dump_result = TRUE; if (args.check_syntax) c->no_exec = TRUE; /* Set $0 */ const char *cmdline; if (args.rfp) { cmdline = args.cmdline ? args.cmdline : "-"; } else { cmdline = "-e"; } mrb_gv_set(mrb, mrb_intern_lit(mrb, "$0"), mrb_str_new_cstr(mrb, cmdline)); /* Load libraries */ for (int i = 0; i < args.libc; i++) { FILE *lfp = fopen(args.libv[i], "rb"); if (lfp == NULL) { fprintf(stderr, "%s: Cannot open library file: %s\n", *argv, args.libv[i]); mrb_ccontext_free(mrb, c); cleanup(mrb, &args); return EXIT_FAILURE; } mrb_ccontext_filename(mrb, c, args.libv[i]); if (mrb_extension_p(args.libv[i])) { mrb_load_irep_file_cxt(mrb, lfp, c); } else { mrb_load_detect_file_cxt(mrb, lfp, c); } fclose(lfp); mrb_vm_ci_env_clear(mrb, mrb->c->cibase); mrb_ccontext_cleanup_local_variables(c); } /* set program filename */ mrb_ccontext_filename(mrb, c, cmdline); /* Load program */ if (args.mrbfile || mrb_extension_p(cmdline)) { v = mrb_load_irep_file_cxt(mrb, args.rfp, c); } else if (args.rfp) { v = mrb_load_detect_file_cxt(mrb, args.rfp, c); } else { char* utf8 = mrb_utf8_from_locale(args.cmdline, -1); if (!utf8) abort(); v = mrb_load_string_cxt(mrb, utf8, c); mrb_utf8_free(utf8); } mrb_gc_arena_restore(mrb, ai); mrb_ccontext_free(mrb, c); if (mrb->exc) { MRB_EXC_CHECK_EXIT(mrb, mrb->exc); if (!mrb_undef_p(v)) { mrb_print_error(mrb); } n = EXIT_FAILURE; } else if (args.check_syntax) { puts("Syntax OK"); } } cleanup(mrb, &args); return n; } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-string-ext0000644000000000000000000000013215077107334022505 xustar0030 mtime=1761382108.606301809 30 atime=1761382109.798298363 30 ctime=1761382108.606301809 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-string-ext/0000755000175100017510000000000015077107334023152 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-string-ext/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024704 xustar0030 mtime=1761382078.128420529 30 atime=1761382080.147411293 30 ctime=1761382108.606301809 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-string-ext/mrbgem.rake0000644000175100017510000000024515077107276025275 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-string-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'String class extension' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-string-ext/PaxHeaders/mrblib0000644000000000000000000000013215077107334023754 xustar0030 mtime=1761382108.611301795 30 atime=1761382109.798298363 30 ctime=1761382108.611301795 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-string-ext/mrblib/0000755000175100017510000000000015077107334024421 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-string-ext/mrblib/PaxHeaders/string.rb0000644000000000000000000000013215077107276025671 xustar0030 mtime=1761382078.128420529 30 atime=1761382080.147411293 30 ctime=1761382108.611301795 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-string-ext/mrblib/string.rb0000644000175100017510000003011715077107276026263 0ustar00runnerrunnerclass String ## # call-seq: # string.clear -> string # # Makes string empty. # # a = "abcde" # a.clear #=> "" # def clear self.replace("") end ## # call-seq: # str.lstrip -> new_str # # Returns a copy of str with leading whitespace removed. See also # String#rstrip and String#strip. # # " hello ".lstrip #=> "hello " # "hello".lstrip #=> "hello" # def lstrip a = 0 z = self.size - 1 a += 1 while a <= z and " \f\n\r\t\v".include?(self[a]) (z >= 0) ? self[a..z] : "" end ## # call-seq: # str.rstrip -> new_str # # Returns a copy of str with trailing whitespace removed. See also # String#lstrip and String#strip. # # " hello ".rstrip #=> " hello" # "hello".rstrip #=> "hello" # def rstrip a = 0 z = self.size - 1 z -= 1 while a <= z and " \f\n\r\t\v\0".include?(self[z]) (z >= 0) ? self[a..z] : "" end ## # call-seq: # str.strip -> new_str # # Returns a copy of str with leading and trailing whitespace removed. # # " hello ".strip #=> "hello" # "\tgoodbye\r\n".strip #=> "goodbye" # def strip a = 0 z = self.size - 1 a += 1 while a <= z and " \f\n\r\t\v".include?(self[a]) z -= 1 while a <= z and " \f\n\r\t\v\0".include?(self[z]) (z >= 0) ? self[a..z] : "" end ## # call-seq: # str.lstrip! -> self or nil # # Removes leading whitespace from str, returning nil if no # change was made. See also String#rstrip! and # String#strip!. # # " hello ".lstrip #=> "hello " # "hello".lstrip! #=> nil # def lstrip! raise FrozenError, "can't modify frozen String" if frozen? s = self.lstrip (s == self) ? nil : self.replace(s) end ## # call-seq: # str.rstrip! -> self or nil # # Removes trailing whitespace from str, returning nil if # no change was made. See also String#lstrip! and # String#strip!. # # " hello ".rstrip #=> " hello" # "hello".rstrip! #=> nil # def rstrip! raise FrozenError, "can't modify frozen String" if frozen? s = self.rstrip (s == self) ? nil : self.replace(s) end ## # call-seq: # str.strip! -> str or nil # # Removes leading and trailing whitespace from str. Returns # nil if str was not altered. # def strip! raise FrozenError, "can't modify frozen String" if frozen? s = self.strip (s == self) ? nil : self.replace(s) end def partition(sep) raise TypeError, "type mismatch: #{sep.class} given" unless sep.is_a? String n = index(sep) unless n.nil? m = n + sep.size [ slice(0, n), sep, slice(m, size - m) ] else [ self[0..-1], "", "" ] end end def rpartition(sep) raise TypeError, "type mismatch: #{sep.class} given" unless sep.is_a? String n = rindex(sep) unless n.nil? m = n + sep.size [ slice(0, n), sep, slice(m, size - m) ] else [ "", "", self ] end end ## # call-seq: # str.slice!(fixnum) -> new_str or nil # str.slice!(fixnum, fixnum) -> new_str or nil # str.slice!(range) -> new_str or nil # str.slice!(other_str) -> new_str or nil # # Deletes the specified portion from str, and returns the portion # deleted. # # string = "this is a string" # string.slice!(2) #=> "i" # string.slice!(3..6) #=> " is " # string.slice!("r") #=> "r" # string #=> "thsa sting" # def slice!(arg1, arg2=nil) raise FrozenError, "can't modify frozen String" if frozen? raise ArgumentError, "wrong number of arguments (expected 1..2)" if arg1.nil? && arg2.nil? if !arg1.nil? && !arg2.nil? idx = arg1 idx += self.size if arg1 < 0 if idx >= 0 && idx <= self.size && arg2 > 0 str = self[idx, arg2] else return nil end else validated = false if arg1.kind_of?(Range) beg = arg1.begin ed = arg1.end beg += self.size if beg < 0 ed += self.size if ed < 0 ed -= 1 if arg1.exclude_end? validated = true elsif arg1.kind_of?(String) validated = true else idx = arg1 idx += self.size if arg1 < 0 validated = true if idx >=0 && arg1 < self.size end if validated str = self[arg1] else return nil end end unless str.nil? || str == "" if !arg1.nil? && !arg2.nil? idx = arg1 >= 0 ? arg1 : self.size+arg1 str2 = self[0...idx] + self[idx+arg2..-1].to_s else if arg1.kind_of?(Range) idx = beg >= 0 ? beg : self.size+beg idx2 = ed>= 0 ? ed : self.size+ed str2 = self[0...idx] + self[idx2+1..-1].to_s elsif arg1.kind_of?(String) idx = self.index(arg1) str2 = self[0...idx] + self[idx+arg1.size..-1] unless idx.nil? else idx = arg1 >= 0 ? arg1 : self.size+arg1 str2 = self[0...idx] + self[idx+1..-1].to_s end end self.replace(str2) unless str2.nil? end str end ## # call-seq: # str.insert(index, other_str) -> str # # Inserts other_str before the character at the given # index, modifying str. Negative indices count from the # end of the string, and insert after the given character. # The intent is insert aString so that it starts at the given # index. # # "abcd".insert(0, 'X') #=> "Xabcd" # "abcd".insert(3, 'X') #=> "abcXd" # "abcd".insert(4, 'X') #=> "abcdX" # "abcd".insert(-3, 'X') #=> "abXcd" # "abcd".insert(-1, 'X') #=> "abcdX" # def insert(idx, str) if idx == -1 return self << str elsif idx < 0 idx += 1 end self[idx, 0] = str self end ## # call-seq: # str.ljust(integer, padstr=' ') -> new_str # # If integer is greater than the length of str, returns a new # String of length integer with str left justified # and padded with padstr; otherwise, returns str. # # "hello".ljust(4) #=> "hello" # "hello".ljust(20) #=> "hello " # "hello".ljust(20, '1234') #=> "hello123412341234123" def ljust(idx, padstr = ' ') raise ArgumentError, 'zero width padding' if padstr == '' return self if idx <= self.size pad_repetitions = idx / padstr.size padding = (padstr * pad_repetitions)[0, idx-self.size] self + padding end ## # call-seq: # str.rjust(integer, padstr=' ') -> new_str # # If integer is greater than the length of str, returns a new # String of length integer with str right justified # and padded with padstr; otherwise, returns str. # # "hello".rjust(4) #=> "hello" # "hello".rjust(20) #=> " hello" # "hello".rjust(20, '1234') #=> "123412341234123hello" def rjust(idx, padstr = ' ') raise ArgumentError, 'zero width padding' if padstr == '' return self if idx <= self.size pad_repetitions = idx / padstr.size padding = (padstr * pad_repetitions)[0, idx-self.size] padding + self end ## # call-seq: # str.center(width, padstr=' ') -> new_str # # Centers +str+ in +width+. If +width+ is greater than the length of +str+, # returns a new String of length +width+ with +str+ centered and padded with # +padstr+; otherwise, returns +str+. # # "hello".center(4) #=> "hello" # "hello".center(20) #=> " hello " # "hello".center(20, '123') #=> "1231231hello12312312" def center(width, padstr = ' ') raise ArgumentError, 'zero width padding' if padstr == '' return self if width <= self.size width -= self.size pad1 = width / 2 pad2 = width - pad1 (padstr*pad1)[0,pad1] + self + (padstr*pad2)[0,pad2] end def chars(&block) if block_given? self.split('').each do |i| block.call(i) end self else self.split('') end end ## # Call the given block for each character of # +self+. def each_char(&block) return to_enum :each_char unless block pos = 0 while pos < self.size block.call(self[pos]) pos += 1 end self end def codepoints(&block) cp = __codepoints() if block_given? cp.each do|x| block.call(x) end self else cp end end alias each_codepoint codepoints ## # call-seq: # str.prepend(other_str) -> str # # Prepend---Prepend the given string to str. # # a = "world" # a.prepend("hello ") #=> "hello world" # a #=> "hello world" def prepend(*args) len = args.size while len > 0 len -= 1 self[0, 0] = args[len] end self end ## # call-seq: # string.lines -> array of string # string.lines {|s| block} -> array of string # # Returns strings per line; # # a = "abc\ndef" # a.lines #=> ["abc\n", "def"] # # If a block is given, it works the same as each_line. def lines(&blk) lines = self.__lines if blk lines.each do |line| blk.call(line) end end lines end ## # call-seq: # str.upto(other_str, exclusive=false) {|s| block } -> str # str.upto(other_str, exclusive=false) -> an_enumerator # # Iterates through successive values, starting at str and # ending at other_str inclusive, passing each value in turn to # the block. The String#succ method is used to generate # each value. If optional second argument exclusive is omitted or is false, # the last value will be included; otherwise it will be excluded. # # If no block is given, an enumerator is returned instead. # # "a8".upto("b6") {|s| print s, ' ' } # for s in "a8".."b6" # print s, ' ' # end # # produces: # # a8 a9 b0 b1 b2 b3 b4 b5 b6 # a8 a9 b0 b1 b2 b3 b4 b5 b6 # # If str and other_str contains only ascii numeric characters, # both are recognized as decimal numbers. In addition, the width of # string (e.g. leading zeros) is handled appropriately. # # "9".upto("11").to_a #=> ["9", "10", "11"] # "25".upto("5").to_a #=> [] # "07".upto("11").to_a #=> ["07", "08", "09", "10", "11"] def upto(max, exclusive=false, &block) return to_enum(:upto, max, exclusive) unless block raise TypeError, "no implicit conversion of #{max.class} into String" unless max.kind_of? String len = self.length maxlen = max.length # single character if len == 1 and maxlen == 1 c = self.ord e = max.ord while c <= e break if exclusive and c == e yield c.chr(__ENCODING__) c += 1 end return self end # both edges are all digits bi = self.to_i(10) ei = max.to_i(10) if (bi > 0 or bi == "0"*len) and (ei > 0 or ei == "0"*maxlen) while bi <= ei break if exclusive and bi == ei s = bi.to_s s = s.rjust(len, "0") if s.length < len yield s bi += 1 end return self end bs = self while true n = (bs <=> max) break if n > 0 break if exclusive and n == 0 yield bs break if n == 0 bsiz = bs.size break if bsiz > max.size || bsiz == 0 bs = bs.succ end self end def __upto_endless(&block) len = self.length # both edges are all digits bi = self.to_i(10) if bi > 0 or bi == "0"*len while true s = bi.to_s s = s.rjust(len, "0") if s.length < len yield s bi += 1 end return self end bs = self while true yield bs bs = bs.succ end self end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-string-ext/PaxHeaders/test0000644000000000000000000000013215077107334023464 xustar0030 mtime=1761382108.610301797 30 atime=1761382109.798298363 30 ctime=1761382108.610301797 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-string-ext/test/0000755000175100017510000000000015077107334024131 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-string-ext/test/PaxHeaders/string.rb0000644000000000000000000000013015077107276025377 xustar0030 mtime=1761382078.128420529 30 atime=1761382080.147411293 28 ctime=1761382108.6093018 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-string-ext/test/string.rb0000644000175100017510000004336515077107276026004 0ustar00runnerrunner## # String(Ext) Test UTF8STRING = __ENCODING__ == "UTF-8" def assert_upto(exp, receiver, *args) act = [] receiver.upto(*args) { |v| act << v } assert_equal exp, act end assert('String#dump') do assert_equal("\"\\x00\"", "\0".dump) assert_equal("\"foo\"", "foo".dump) assert_equal('"\xe3\x82\x8b"', "ã‚‹".dump) assert_nothing_raised { ("\1" * 100).dump } # regress #1210 end assert('String#strip') do s = " abc " assert_equal("abc", s.strip) assert_equal(" abc ", s) assert_equal("", "".strip) assert_equal("", " \t\r\n\f\v".strip) assert_equal("\0a", "\0a\0".strip) assert_equal("abc", "abc".strip) assert_equal("abc", " abc".strip) assert_equal("abc", "abc ".strip) end assert('String#lstrip') do s = " abc " assert_equal("abc ", s.lstrip) assert_equal(" abc ", s) assert_equal("", "".lstrip) assert_equal("", " \t\r\n\f\v".lstrip) assert_equal("\0a\0", "\0a\0".lstrip) assert_equal("abc", "abc".lstrip) assert_equal("abc", " abc".lstrip) assert_equal("abc ", "abc ".lstrip) end assert('String#rstrip') do s = " abc " assert_equal(" abc", s.rstrip) assert_equal(" abc ", s) assert_equal("", "".rstrip) assert_equal("", " \t\r\n\f\v".rstrip) assert_equal("\0a", "\0a\0".rstrip) assert_equal("abc", "abc".rstrip) assert_equal(" abc", " abc".rstrip) assert_equal("abc", "abc ".rstrip) end assert('String#strip!') do s = " abc " t = "abc" assert_equal("abc", s.strip!) assert_equal("abc", s) assert_nil(t.strip!) assert_equal("abc", t) end assert('String#lstrip!') do s = " abc " t = "abc " assert_equal("abc ", s.lstrip!) assert_equal("abc ", s) assert_nil(t.lstrip!) assert_equal("abc ", t) end assert('String#rstrip!') do s = " abc " t = " abc" assert_equal(" abc", s.rstrip!) assert_equal(" abc", s) assert_nil(t.rstrip!) assert_equal(" abc", t) end assert('String#swapcase') do assert_equal "hELLO", "Hello".swapcase assert_equal "CyBeR_pUnK11", "cYbEr_PuNk11".swapcase end assert('String#swapcase!') do s = "Hello" t = s.clone t.swapcase! assert_equal s.swapcase, t end assert('String#concat') do assert_equal "Hello World!", "Hello " << "World" << 33 assert_equal "Hello World!", "Hello ".concat("World").concat(33) assert_raise(TypeError) { "".concat(Object.new) } if UTF8STRING assert_equal "H«", "H" << 0xab assert_equal "Hã¯", "H" << 12399 else assert_equal "H\xab", "H" << 0xab assert_raise(RangeError) { "H" << 12399 } end end assert('String#casecmp') do assert_equal 1, "abcdef".casecmp("abcde") assert_equal 0, "aBcDeF".casecmp("abcdef") assert_equal(-1, "abcdef".casecmp("abcdefg")) assert_equal 0, "abcdef".casecmp("ABCDEF") end assert('String#count') do s = "abccdeff123" assert_equal 0, s.count("") assert_equal 1, s.count("a") assert_equal 2, s.count("ab") assert_equal 9, s.count("^c") assert_equal 8, s.count("a-z") assert_equal 4, s.count("a0-9") end assert('String#tr') do assert_equal "ABC", "abc".tr('a-z', 'A-Z') assert_equal "hippo", "hello".tr('el', 'ip') assert_equal "Ruby", "Lisp".tr("Lisp", "Ruby") assert_equal "*e**o", "hello".tr('^aeiou', '*') assert_equal "heo", "hello".tr('l', '') end assert('String#tr!') do s = "abcdefghijklmnopqR" assert_equal "ab12222hijklmnopqR", s.tr!("cdefg", "12") assert_equal "ab12222hijklmnopqR", s end assert('String#tr_s') do assert_equal "hero", "hello".tr_s('l', 'r') assert_equal "h*o", "hello".tr_s('el', '*') assert_equal "hhxo", "hello".tr_s('el', 'hx') end assert('String#tr_s!') do s = "hello" assert_equal "hero", s.tr_s!('l', 'r') assert_equal "hero", s assert_nil s.tr_s!('l', 'r') end assert('String#squeeze') do assert_equal "yelow mon", "yellow moon".squeeze assert_equal " now is the", " now is the".squeeze(" ") assert_equal "puters shot balls", "putters shoot balls".squeeze("m-z") end assert('String#squeeze!') do s = " now is the" assert_equal " now is the", s.squeeze!(" ") assert_equal " now is the", s end assert('String#delete') do assert_equal "he", "hello".delete("lo") assert_equal "hll", "hello".delete("aeiou") assert_equal "ll", "hello".delete("^l") assert_equal "ho", "hello".delete("ej-m") end assert('String#delete!') do s = "hello" assert_equal "he", s.delete!("lo") assert_equal "he", s assert_nil s.delete!("lz") end assert('String#start_with?') do assert_true "hello".start_with?("heaven", "hell") assert_true !"hello".start_with?("heaven", "paradise") assert_true !"h".start_with?("heaven", "hell") assert_raise TypeError do "hello".start_with?(true) end end assert('String#end_with?') do assert_true "string".end_with?("ing", "mng") assert_true !"string".end_with?("str", "tri") assert_true !"ng".end_with?("ing", "mng") assert_raise TypeError do "hello".end_with?(true) end end assert('String#partition') do assert_equal ["a", "x", "axa"], "axaxa".partition("x") assert_equal ["aaaaa", "", ""], "aaaaa".partition("x") assert_equal ["", "", "aaaaa"], "aaaaa".partition("") assert_equal ["", "a", "aaaa"], "aaaaa".partition("a") assert_equal ["aaaa", "b", ""], "aaaab".partition("b") assert_equal ["", "b", "aaaa"], "baaaa".partition("b") assert_equal ["", "", ""], "".partition("a") end assert('String#rpartition') do assert_equal ["axa", "x", "a"], "axaxa".rpartition("x") assert_equal ["", "", "aaaaa"], "aaaaa".rpartition("x") assert_equal ["aaaaa", "", ""], "aaaaa".rpartition("") assert_equal ["aaaa", "a", ""], "aaaaa".rpartition("a") assert_equal ["aaaa", "b", ""], "aaaab".rpartition("b") assert_equal ["", "b", "aaaa"], "baaaa".rpartition("b") assert_equal ["", "", ""], "".rpartition("a") end assert('String#hex') do assert_equal 16, "10".hex assert_equal 255, "ff".hex assert_equal 16, "0x10".hex assert_equal (-16), "-0x10".hex assert_equal 0, "xyz".hex assert_equal 16, "10z".hex assert_equal 16, "1_0".hex assert_equal 0, "".hex end assert('String#oct') do assert_equal 8, "10".oct assert_equal 7, "7".oct assert_equal 0, "8".oct assert_equal 0, "9".oct assert_equal 0, "xyz".oct assert_equal 8, "10z".oct assert_equal 8, "1_0".oct assert_equal 8, "010".oct assert_equal (-8), "-10".oct end assert('String#lines') do assert_equal ["Hel\n", "lo\n", "World!"], "Hel\nlo\nWorld!".lines assert_equal ["Hel\n", "lo\n", "World!\n"], "Hel\nlo\nWorld!\n".lines assert_equal ["\n", "\n", "\n"], "\n\n\n".lines assert_equal [], "".lines end assert('String#clear') do # embed string s = "foo" assert_equal("", s.clear) assert_equal("", s) # not embed string and not shared string s = "foo" * 100 a = s assert_equal("", s.clear) assert_equal("", s) assert_equal("", a) # shared string s = "foo" * 100 a = s[10, 90] # create shared string assert_equal("", s.clear) # clear assert_equal("", s) # s is cleared assert_not_equal("", a) # a should not be affected end assert('String#slice!') do a = "AooBar" b = a.dup assert_equal "A", a.slice!(0) assert_equal "AooBar", b a = "FooBar" assert_equal "r", a.slice!(-1) assert_equal "FooBa", a a = "FooBar" assert_nil a.slice!(6) assert_nil a.slice!(-7) assert_equal "FooBar", a a = "FooBar" assert_equal "Foo", a.slice!(0, 3) assert_equal "Bar", a a = "FooBar" assert_equal "Bar", a.slice!(-3, 3) assert_equal "Foo", a a = "FooBar" assert_equal "", a.slice!(6, 2) assert_equal "FooBar", a a = "FooBar" assert_nil a.slice!(-7,10) assert_equal "FooBar", a a = "FooBar" assert_equal "Foo", a.slice!(0..2) assert_equal "Bar", a a = "FooBar" assert_equal "Bar", a.slice!(-3..-1) assert_equal "Foo", a a = "FooBar" assert_equal "", a.slice!(6..2) assert_equal "FooBar", a a = "FooBar" assert_nil a.slice!(-10..-7) assert_equal "FooBar", a a = "FooBar" assert_equal "Foo", a.slice!("Foo") assert_equal "Bar", a a = "FooBar" assert_nil a.slice!("xyzzy") assert_equal "FooBar", a assert_raise(ArgumentError) { "foo".slice! } end assert('String#succ') do assert_equal "", "".succ assert_equal "1", "0".succ assert_equal "10", "9".succ assert_equal "01", "00".succ assert_equal "a1", "a0".succ assert_equal "A1", "A0".succ assert_equal "10", "09".succ assert_equal "b0", "a9".succ assert_equal "B0", "A9".succ assert_equal "b", "a".succ assert_equal "aa", "z".succ assert_equal "ab", "aa".succ assert_equal "Ab", "Aa".succ assert_equal "0b", "0a".succ assert_equal "ba", "az".succ assert_equal "Ba", "Az".succ assert_equal "1a", "0z".succ assert_equal "B", "A".succ assert_equal "AA", "Z".succ assert_equal "AB", "AA".succ assert_equal "aB", "aA".succ assert_equal "0B", "0A".succ assert_equal "BA", "AZ".succ assert_equal "bA", "aZ".succ assert_equal "1A", "0Z".succ assert_equal ".", "-".succ assert_equal "\x01\x00", "\xff".succ assert_equal "-b", "-a".succ assert_equal "-aa", "-z".succ assert_equal "-a-b-", "-a-a-".succ assert_equal "-b-", "-a-".succ assert_equal "-aa-", "-z-".succ assert_equal "ã‚b", "ã‚a".succ assert_equal "ã‚ba", "ã‚az".succ a = ""; a.succ! assert_equal "", a a = "0"; a.succ! assert_equal "1", a a = "9"; a.succ! assert_equal "10", a a = "00"; a.succ! assert_equal "01", a a = "a0"; a.succ! assert_equal "a1", a a = "A0"; a.succ! assert_equal "A1", a a = "09"; a.succ! assert_equal "10", a a = "a9"; a.succ! assert_equal "b0", a a = "A9"; a.succ! assert_equal "B0", a a = "a"; a.succ! assert_equal "b", a a = "z"; a.succ! assert_equal "aa", a a = "aa"; a.succ! assert_equal "ab", a a = "Aa"; a.succ! assert_equal "Ab", a a = "0a"; a.succ! assert_equal "0b", a a = "az"; a.succ! assert_equal "ba", a a = "Az"; a.succ! assert_equal "Ba", a a = "0z"; a.succ! assert_equal "1a", a a = "A"; a.succ! assert_equal "B", a a = "Z"; a.succ! assert_equal "AA", a a = "AA"; a.succ! assert_equal "AB", a a = "aA"; a.succ! assert_equal "aB", a a = "0A"; a.succ! assert_equal "0B", a a = "AZ"; a.succ! assert_equal "BA", a a = "aZ"; a.succ! assert_equal "bA", a a = "0Z"; a.succ! assert_equal "1A", a a = "-"; a.succ! assert_equal ".", a a = "\xff"; a.succ! assert_equal "\x01\x00", a a = "-a"; a.succ! assert_equal "-b", a a = "-z"; a.succ! assert_equal "-aa", a a = "-a-a-"; a.succ! assert_equal "-a-b-", a a = "-a-"; a.succ! assert_equal "-b-", a a = "-z-"; a.succ! assert_equal "-aa-", a a = "ã‚b"; a.succ! assert_equal "ã‚c", a a = "ã‚az"; a.succ! assert_equal "ã‚ba", a end assert('String#next') do assert_equal "01", "00".next a = "00"; a.next! assert_equal "01", a end assert('String#insert') do assert_equal "Xabcd", "abcd".insert(0, 'X') assert_equal "abcXd", "abcd".insert(3, 'X') assert_equal "abcdX", "abcd".insert(4, 'X') assert_equal "abXcd", "abcd".insert(-3, 'X') assert_equal "abcdX", "abcd".insert(-1, 'X') assert_raise(IndexError) { "abcd".insert(5, 'X') } assert_raise(IndexError) { "abcd".insert(-6, 'X') } a = "abcd" a.insert(0, 'X') assert_equal "Xabcd", a end assert('String#prepend') do a = "world" assert_equal "hello world", a.prepend("hello ") assert_equal "hello world", a end assert('String#ljust') do assert_equal "hello", "hello".ljust(4) assert_equal "hello ", "hello".ljust(20) assert_equal 20, "hello".ljust(20).length assert_equal "hello123412341234123", "hello".ljust(20, '1234') assert_equal "hello", "hello".ljust(-3) end assert('String#rjust') do assert_equal "hello", "hello".rjust(4) assert_equal " hello", "hello".rjust(20) assert_equal 20, "hello".rjust(20).length assert_equal "123412341234123hello", "hello".rjust(20, '1234') assert_equal "hello", "hello".rjust(-3) end assert('String#center') do assert_equal "hello", "hello".center(4) assert_equal " hello ", "hello".center(20) assert_equal 20, "hello".center(20).length assert_equal "1231231hello12312312", "hello".center(20, '123') assert_equal "hello", "hello".center(-3) end if UTF8STRING assert('String#ljust with UTF8') do assert_equal "helloã‚“ ", "helloã‚“".ljust(20) assert_equal "helloó ", "helloó".ljust(34) assert_equal 34, "helloó".ljust(34).length assert_equal "helloã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“", "hello".ljust(19, 'ã‚“') assert_equal "helloã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“", "hello".ljust(20, 'ã‚“') end assert('String#rjust with UTF8') do assert_equal " helloã‚“", "helloã‚“".rjust(20) assert_equal " helloó", "helloó".rjust(34) # assert_equal 34, "helloó".rjust(34).length assert_equal "ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“hello", "hello".rjust(19, 'ã‚“') assert_equal "ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“ã‚“hello", "hello".rjust(20, 'ã‚“') end assert('UTF8 byte counting') do ret = ' ' ret[-6..-1] = "helloó" assert_equal 34, ret.length end end assert('String#ljust should not change string') do a = "hello" a.ljust(20) assert_equal "hello", a end assert('String#rjust should not change string') do a = "hello" a.rjust(20) assert_equal "hello", a end assert('String#ljust should raise on zero width padding') do assert_raise(ArgumentError) { "foo".ljust(10, '') } end assert('String#rjust should raise on zero width padding') do assert_raise(ArgumentError) { "foo".rjust(10, '') } end assert('String#upto') do assert_upto %w(a8 a9 b0 b1 b2 b3 b4 b5 b6), "a8", "b6" assert_upto ["9", "10", "11"], "9", "11" assert_upto [], "25", "5" assert_upto ["07", "08", "09", "10", "11"], "07", "11" assert_upto ["9", ":", ";", "<", "=", ">", "?", "@", "A"], "9", "A" if UTF8STRING assert_upto %w(ã‚ ãƒ ã„ ã… ã† ã‡ ãˆ ã‰ ãŠ), "ã‚", "ãŠ" end a = "aa" start = "aa" count = 0 assert_equal("aa", a.upto("zz") {|s| assert_equal(start, s) start.succ! count += 1 }) assert_equal(676, count) a = "a" start = "a" count = 0 assert_equal("a", a.upto("a") {|s| assert_equal(start, s) start.succ! count += 1 }) assert_equal(1, count) a = "a" start = "a" count = 0 assert_equal("a", a.upto("b", true) {|s| assert_equal(start, s) start.succ! count += 1 }) assert_equal(1, count) a = "0" start = "0" count = 0 assert_equal("0", a.upto("0") {|s| assert_equal(start, s) start.succ! count += 1 }) assert_equal(1, count) a = "0" start = "0" count = 0 assert_equal("0", a.upto("-1") {|s| assert_equal(start, s) start.succ! count += 1 }) assert_equal(0, count) a = "-1" start = "-1" count = 0 assert_equal("-1", a.upto("-2") {|s| assert_equal(start, s) start.succ! count += 1 }) assert_equal(2, count) assert_raise(TypeError) { "a".upto(:c) {} } end assert('String#ord') do got = "hello!".split('').map {|x| x.ord} expect = [104, 101, 108, 108, 111, 33] unless UTF8STRING got << "\xff".ord expect << 0xff end assert_equal expect, got end assert('String#ord(UTF-8)') do got = "ã“ã‚“ã«ã¡ã¯ä¸–界!".split('').map {|x| x.ord} expect = [0x3053,0x3093,0x306b,0x3061,0x306f,0x4e16,0x754c,0x21] assert_equal expect, got end if UTF8STRING assert('String#chr') do assert_equal "a", "abcde".chr assert_equal "h", "hello!".chr assert_equal "", "".chr end assert('String#chr(UTF-8)') do assert_equal "ã“", "ã“ã‚“ã«ã¡ã¯ä¸–界!".chr end if UTF8STRING assert('String#chars') do expect = ["h", "e", "l", "l", "o", "!"] assert_equal expect, "hello!".chars s = "" "hello!".chars do |x| s += x end assert_equal "hello!", s end assert('String#chars(UTF-8)') do expect = ['ã“', 'ã‚“', 'ã«', 'ã¡', 'ã¯', '世', '界', '!'] assert_equal expect, "ã“ã‚“ã«ã¡ã¯ä¸–界!".chars s = "" "ã“ã‚“ã«ã¡ã¯ä¸–界!".chars do |x| s += x end assert_equal "ã“ã‚“ã«ã¡ã¯ä¸–界!", s end if UTF8STRING assert('String#each_char') do chars = [] "hello!".each_char do |x| chars << x end assert_equal ["h", "e", "l", "l", "o", "!"], chars end assert('String#each_char(UTF-8)') do chars = [] "ã“ã‚“ã«ã¡ã¯ä¸–界!".each_char do |x| chars << x end assert_equal ["ã“", "ã‚“", "ã«", "ã¡", "ã¯", "世", "界", "!"], chars end if UTF8STRING assert('String#codepoints') do expect = [104, 101, 108, 108, 111, 33] assert_equal expect, "hello!".codepoints cp = [] "hello!".codepoints do |x| cp << x end assert_equal expect, cp end assert('String#codepoints(UTF-8)') do expect = [12371, 12435, 12395, 12385, 12399, 19990, 30028, 33] assert_equal expect, "ã“ã‚“ã«ã¡ã¯ä¸–界!".codepoints cp = [] "ã“ã‚“ã«ã¡ã¯ä¸–界!".codepoints do |x| cp << x end assert_equal expect, cp end if UTF8STRING assert('String#each_codepoint') do expect = [104, 101, 108, 108, 111, 33] cp = [] "hello!".each_codepoint do |x| cp << x end assert_equal expect, cp end assert('String#each_codepoint(UTF-8)') do expect = [12371, 12435, 12395, 12385, 12399, 19990, 30028, 33] cp = [] "ã“ã‚“ã«ã¡ã¯ä¸–界!".each_codepoint do |x| cp << x end assert_equal expect, cp end if UTF8STRING assert('String#delete_prefix') do assert_equal "llo", "hello".delete_prefix("he") assert_equal "hello", "hello".delete_prefix("llo") assert_equal "llo", "hello".delete_prefix!("he") assert_nil "hello".delete_prefix!("llo") end assert('String#delete_suffix') do assert_equal "he", "hello".delete_suffix("llo") assert_equal "hello", "hello".delete_suffix("he") assert_equal "he", "hello".delete_suffix!("llo") assert_nil "hello".delete_suffix!("he") end assert('String#+@') do a = +"abc" assert_false(a.frozen?) a = +(a.freeze) assert_false(a.frozen?) end assert('String#-@') do a = -"abc" assert_true(a.frozen?) a = -(a.freeze) assert_true(a.frozen?) end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-string-ext/test/PaxHeaders/numeric.rb0000644000000000000000000000013215077107276025535 xustar0030 mtime=1761382078.128420529 30 atime=1761382080.147411293 30 ctime=1761382108.610301797 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-string-ext/test/numeric.rb0000644000175100017510000000167115077107276026132 0ustar00runnerrunnerassert('Integer#chr') do assert_equal("A", 65.chr) assert_equal("B", 0x42.chr) assert_equal("\xab", 171.chr) assert_raise(RangeError) { -1.chr } assert_raise(RangeError) { 256.chr } assert_equal("A", 65.chr("ASCII-8BIT")) assert_equal("B", 0x42.chr("BINARY")) assert_equal("\xab", 171.chr("ascii-8bit")) assert_raise(RangeError) { -1.chr("binary") } assert_raise(RangeError) { 256.chr("Ascii-8bit") } assert_raise(ArgumentError) { 65.chr("ASCII") } assert_raise(ArgumentError) { 65.chr("ASCII-8BIT", 2) } assert_raise(TypeError) { 65.chr(:BINARY) } if __ENCODING__ == "ASCII-8BIT" assert_raise(ArgumentError) { 65.chr("UTF-8") } else assert_equal("A", 65.chr("UTF-8")) assert_equal("B", 0x42.chr("UTF-8")) assert_equal("«", 171.chr("utf-8")) assert_equal("ã‚", 12354.chr("Utf-8")) assert_raise(RangeError) { -1.chr("utf-8") } assert_raise(RangeError) { 0x110000.chr.chr("UTF-8") } end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-string-ext/test/PaxHeaders/range.rb0000644000000000000000000000013215077107276025167 xustar0030 mtime=1761382078.128420529 30 atime=1761382080.147411293 30 ctime=1761382108.607301806 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-string-ext/test/range.rb0000644000175100017510000000150115077107276025554 0ustar00runnerrunnerassert('Range#max') do # returns the maximum value in the range when called with no arguments assert_equal 'l', ('f'..'l').max assert_equal 'e', ('a'...'f').max # returns nil when the endpoint is less than the start point assert_equal nil, ('z'..'l').max end assert('Range#max given a block') do # returns nil when the endpoint is less than the start point assert_equal nil, (('z'..'l').max { |x, y| x <=> y }) end assert('Range#min') do # returns the minimum value in the range when called with no arguments assert_equal 'f', ('f'..'l').min # returns nil when the start point is greater than the endpoint assert_equal nil, ('z'..'l').min end assert('Range#min given a block') do # returns nil when the start point is greater than the endpoint assert_equal nil, (('z'..'l').min { |x, y| x <=> y }) end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-string-ext/PaxHeaders/src0000644000000000000000000000013215077107334023274 xustar0030 mtime=1761382108.612301792 30 atime=1761382109.798298363 30 ctime=1761382108.612301792 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-string-ext/src/0000755000175100017510000000000015077107334023741 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-string-ext/src/PaxHeaders/string.c0000644000000000000000000000013215077107276025030 xustar0030 mtime=1761382078.128420529 30 atime=1761382080.147411293 30 ctime=1761382108.612301792 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-string-ext/src/string.c0000644000175100017510000010671215077107276025427 0ustar00runnerrunner#include #include #include #include #include #include #include #include #define ENC_ASCII_8BIT "ASCII-8BIT" #define ENC_BINARY "BINARY" #define ENC_UTF8 "UTF-8" #define ENC_COMP_P(enc, enc_lit) \ casecmp_p(RSTRING_PTR(enc), RSTRING_LEN(enc), enc_lit, sizeof(enc_lit"")-1) static mrb_bool casecmp_p(const char *s1, mrb_int len1, const char *s2, mrb_int len2) { if (len1 != len2) return FALSE; const char *e1 = s1 + len1; const char *e2 = s2 + len2; while (s1 < e1 && s2 < e2) { if (*s1 != *s2 && TOUPPER(*s1) != TOUPPER(*s2)) return FALSE; s1++; s2++; } return TRUE; } static mrb_value int_chr_binary(mrb_state *mrb, mrb_value num) { mrb_int cp = mrb_as_int(mrb, num); if (cp < 0 || 0xff < cp) { mrb_raisef(mrb, E_RANGE_ERROR, "%v out of char range", num); } char c = (char)cp; mrb_value str = mrb_str_new(mrb, &c, 1); RSTR_SET_ASCII_FLAG(mrb_str_ptr(str)); return str; } #ifdef MRB_UTF8_STRING static mrb_value int_chr_utf8(mrb_state *mrb, mrb_value num) { mrb_int cp = mrb_int(mrb, num); char utf8[4]; mrb_int len; mrb_value str; uint32_t sb_flag = 0; if (cp < 0 || 0x10FFFF < cp) { mrb_raisef(mrb, E_RANGE_ERROR, "%v out of char range", num); } if (cp < 0x80) { utf8[0] = (char)cp; len = 1; sb_flag = MRB_STR_SINGLE_BYTE; } else if (cp < 0x800) { utf8[0] = (char)(0xC0 | (cp >> 6)); utf8[1] = (char)(0x80 | (cp & 0x3F)); len = 2; } else if (cp < 0x10000) { utf8[0] = (char)(0xE0 | (cp >> 12)); utf8[1] = (char)(0x80 | ((cp >> 6) & 0x3F)); utf8[2] = (char)(0x80 | ( cp & 0x3F)); len = 3; } else { utf8[0] = (char)(0xF0 | (cp >> 18)); utf8[1] = (char)(0x80 | ((cp >> 12) & 0x3F)); utf8[2] = (char)(0x80 | ((cp >> 6) & 0x3F)); utf8[3] = (char)(0x80 | ( cp & 0x3F)); len = 4; } str = mrb_str_new(mrb, utf8, len); mrb_str_ptr(str)->flags |= sb_flag; return str; } #endif /* * call-seq: * str.swapcase! -> str or nil * * Equivalent to String#swapcase, but modifies the receiver in * place, returning str, or nil if no changes were made. * Note: case conversion is effective only in ASCII region. */ static mrb_value str_swapcase_bang(mrb_state *mrb, mrb_value str) { int modify = 0; struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); char *p = RSTRING_PTR(str); char *pend = p + RSTRING_LEN(str); while (p < pend) { if (ISUPPER(*p)) { *p = TOLOWER(*p); modify = 1; } else if (ISLOWER(*p)) { *p = TOUPPER(*p); modify = 1; } p++; } if (modify) return str; return mrb_nil_value(); } /* * call-seq: * str.swapcase -> new_str * * Returns a copy of str with uppercase alphabetic characters converted * to lowercase and lowercase characters converted to uppercase. * Note: case conversion is effective only in ASCII region. * * "Hello".swapcase #=> "hELLO" * "cYbEr_PuNk11".swapcase #=> "CyBeR_pUnK11" */ static mrb_value str_swapcase(mrb_state *mrb, mrb_value self) { mrb_value str = mrb_str_dup(mrb, self); str_swapcase_bang(mrb, str); return str; } static void str_concat(mrb_state *mrb, mrb_value self, mrb_value str, mrb_bool binary) { if (mrb_integer_p(str) || mrb_float_p(str)) { #ifdef MRB_UTF8_STRING if (binary) { str = int_chr_binary(mrb, str); } else { str = int_chr_utf8(mrb, str); } #else str = int_chr_binary(mrb, str); #endif } else mrb_ensure_string_type(mrb, str); mrb_str_cat_str(mrb, self, str); } static mrb_value str_concat0(mrb_state *mrb, mrb_value self, mrb_bool binary) { if (mrb_get_argc(mrb) == 1) { str_concat(mrb, self, mrb_get_arg1(mrb), binary); return self; } mrb_value *args; mrb_int alen; mrb_get_args(mrb, "*", &args, &alen); for (mrb_int i=0; i str * str.concat(*obj) -> str * * s = 'foo' * s.concat('bar', 'baz') # => "foobarbaz" * s # => "foobarbaz" * * For each given object +object+ that is an \Integer, * the value is considered a codepoint and converted to a character before concatenation: * * s = 'foo' * s.concat(32, 'bar', 32, 'baz') # => "foo bar baz" * */ static mrb_value str_concat_m(mrb_state *mrb, mrb_value self) { mrb_bool binary = RSTR_BINARY_P(mrb_str_ptr(self)); return str_concat0(mrb, self, binary); } /* * call-seq: * str.append_as_bytes(*obj) -> str * * Works like `concat` but consider arguments as binary strings. * */ static mrb_value str_append_as_bytes(mrb_state *mrb, mrb_value self) { return str_concat0(mrb, self, TRUE); } /* * call-seq: * str.start_with?([prefixes]+) -> true or false * * Returns true if +str+ starts with one of the +prefixes+ given. * * "hello".start_with?("hell") #=> true * * # returns true if one of the prefixes matches. * "hello".start_with?("heaven", "hell") #=> true * "hello".start_with?("heaven", "paradise") #=> false * "h".start_with?("heaven", "hell") #=> false */ static mrb_value str_start_with(mrb_state *mrb, mrb_value self) { const mrb_value *argv; mrb_int argc; mrb_get_args(mrb, "*", &argv, &argc); for (mrb_int i = 0; i < argc; i++) { int ai = mrb_gc_arena_save(mrb); mrb_value sub = argv[i]; mrb_ensure_string_type(mrb, sub); mrb_gc_arena_restore(mrb, ai); size_t len_l = RSTRING_LEN(self); size_t len_r = RSTRING_LEN(sub); if (len_l >= len_r) { if (memcmp(RSTRING_PTR(self), RSTRING_PTR(sub), len_r) == 0) { return mrb_true_value(); } } } return mrb_false_value(); } /* * call-seq: * str.end_with?([suffixes]+) -> true or false * * Returns true if +str+ ends with one of the +suffixes+ given. */ static mrb_value str_end_with(mrb_state *mrb, mrb_value self) { const mrb_value *argv; mrb_int argc; mrb_get_args(mrb, "*", &argv, &argc); for (mrb_int i = 0; i < argc; i++) { int ai = mrb_gc_arena_save(mrb); mrb_value sub = argv[i]; mrb_ensure_string_type(mrb, sub); mrb_gc_arena_restore(mrb, ai); size_t len_l = RSTRING_LEN(self); size_t len_r = RSTRING_LEN(sub); if (len_l >= len_r) { if (memcmp(RSTRING_PTR(self) + (len_l - len_r), RSTRING_PTR(sub), len_r) == 0) { return mrb_true_value(); } } } return mrb_false_value(); } enum tr_pattern_type { TR_UNINITIALIZED = 0, TR_IN_ORDER = 1, TR_RANGE = 2, }; /* #tr Pattern syntax ::= ()* | '^' ()* ::= | ::= ()+ ::= '-' */ struct tr_pattern { uint8_t type; // 1:in-order, 2:range mrb_bool flag_reverse : 1; mrb_bool flag_on_heap : 1; uint16_t n; union { uint16_t start_pos; char ch[2]; } val; struct tr_pattern *next; }; #define STATIC_TR_PATTERN { 0 } static inline void tr_free_pattern(mrb_state *mrb, struct tr_pattern *pat) { while (pat) { struct tr_pattern *p = pat->next; if (pat->flag_on_heap) { mrb_free(mrb, pat); } pat = p; } } static struct tr_pattern* tr_parse_pattern(mrb_state *mrb, struct tr_pattern *ret, const mrb_value v_pattern, mrb_bool flag_reverse_enable, struct tr_pattern *pat0) { const char *pattern = RSTRING_PTR(v_pattern); mrb_int pattern_length = RSTRING_LEN(v_pattern); mrb_bool flag_reverse = FALSE; mrb_int i = 0; if (flag_reverse_enable && pattern_length >= 2 && pattern[0] == '^') { flag_reverse = TRUE; i++; } while (i < pattern_length) { /* is range pattern ? */ mrb_bool const ret_uninit = (ret->type == TR_UNINITIALIZED); struct tr_pattern *pat1 = ret_uninit ? ret : (struct tr_pattern*)mrb_malloc_simple(mrb, sizeof(struct tr_pattern)); if (pat1 == NULL) { if (pat0) tr_free_pattern(mrb, pat0); tr_free_pattern(mrb, ret); mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err)); return NULL; /* not reached */ } if ((i+2) < pattern_length && pattern[i] != '\\' && pattern[i+1] == '-') { pat1->type = TR_RANGE; pat1->flag_reverse = flag_reverse; pat1->flag_on_heap = !ret_uninit; pat1->n = pattern[i+2] - pattern[i] + 1; pat1->next = NULL; pat1->val.ch[0] = pattern[i]; pat1->val.ch[1] = pattern[i+2]; i += 3; } else { /* in order pattern. */ mrb_int start_pos = i++; while (i < pattern_length) { if ((i+2) < pattern_length && pattern[i] != '\\' && pattern[i+1] == '-') break; i++; } mrb_int len = i - start_pos; if (len > UINT16_MAX) { if (pat0) tr_free_pattern(mrb, pat0); tr_free_pattern(mrb, ret); if (ret != pat1) mrb_free(mrb, pat1); mrb_raise(mrb, E_ARGUMENT_ERROR, "tr pattern too long (max 65535)"); } pat1->type = TR_IN_ORDER; pat1->flag_reverse = flag_reverse; pat1->flag_on_heap = !ret_uninit; pat1->n = (uint16_t)len; pat1->next = NULL; pat1->val.start_pos = (uint16_t)start_pos; } if (!ret_uninit) { struct tr_pattern *p = ret; while (p->next != NULL) { p = p->next; } p->next = pat1; } } return ret; } static inline mrb_int tr_find_character(const struct tr_pattern *pat, const char *pat_str, int ch) { mrb_int ret = -1; mrb_int n_sum = 0; mrb_int flag_reverse = pat ? pat->flag_reverse : 0; while (pat != NULL) { if (pat->type == TR_IN_ORDER) { for (int i = 0; i < pat->n; i++) { if (pat_str[pat->val.start_pos + i] == ch) ret = n_sum + i; } } else if (pat->type == TR_RANGE) { if (pat->val.ch[0] <= ch && ch <= pat->val.ch[1]) ret = n_sum + ch - pat->val.ch[0]; } else { mrb_assert(pat->type == TR_UNINITIALIZED); } n_sum += pat->n; pat = pat->next; } if (flag_reverse) { return (ret < 0) ? MRB_INT_MAX : -1; } return ret; } static inline mrb_int tr_get_character(const struct tr_pattern *pat, const char *pat_str, mrb_int n_th) { mrb_int n_sum = 0; while (pat != NULL) { if (n_th < (n_sum + pat->n)) { mrb_int i = (n_th - n_sum); switch (pat->type) { case TR_IN_ORDER: return pat_str[pat->val.start_pos + i]; case TR_RANGE: return pat->val.ch[0]+i; case TR_UNINITIALIZED: return -1; } } if (pat->next == NULL) { switch (pat->type) { case TR_IN_ORDER: return pat_str[pat->val.start_pos + pat->n - 1]; case TR_RANGE: return pat->val.ch[1]; case TR_UNINITIALIZED: return -1; } } n_sum += pat->n; pat = pat->next; } return -1; } static inline void tr_bitmap_set(uint8_t bitmap[32], uint8_t ch) { uint8_t idx1 = ch / 8; uint8_t idx2 = ch % 8; bitmap[idx1] |= (1<flag_reverse : 0; int i; for (int i=0; i<32; i++) { bitmap[i] = 0; } while (pat != NULL) { if (pat->type == TR_IN_ORDER) { for (i = 0; i < pat->n; i++) { tr_bitmap_set(bitmap, pattern[pat->val.start_pos + i]); } } else if (pat->type == TR_RANGE) { for (i = pat->val.ch[0]; i < pat->val.ch[1]; i++) { tr_bitmap_set(bitmap, i); } } else { mrb_assert(pat->type == TR_UNINITIALIZED); } pat = pat->next; } if (flag_reverse) { for (i=0; i<32; i++) { bitmap[i] ^= 0xff; } } } static mrb_bool str_tr(mrb_state *mrb, mrb_value str, mrb_value p1, mrb_value p2, mrb_bool squeeze) { struct tr_pattern pat = STATIC_TR_PATTERN; struct tr_pattern rep = STATIC_TR_PATTERN; mrb_bool flag_changed = FALSE; mrb_int lastch = -1; mrb_str_modify(mrb, mrb_str_ptr(str)); tr_parse_pattern(mrb, &pat, p1, TRUE, NULL); tr_parse_pattern(mrb, &rep, p2, FALSE, &pat); char *s = RSTRING_PTR(str); mrb_int len = RSTRING_LEN(str); mrb_int i, j; for (i=j=0; ij) s[j] = s[i]; if (n >= 0) { flag_changed = TRUE; mrb_int c = tr_get_character(&rep, RSTRING_PTR(p2), n); if (c < 0 || (squeeze && c == lastch)) { j--; continue; } if (c > 0x80) { tr_free_pattern(mrb, &pat); tr_free_pattern(mrb, &rep); mrb_raisef(mrb, E_ARGUMENT_ERROR, "character (%i) out of range", c); } lastch = c; s[i] = (char)c; } } tr_free_pattern(mrb, &pat); tr_free_pattern(mrb, &rep); if (flag_changed) { RSTR_SET_LEN(RSTRING(str), j); RSTRING_PTR(str)[j] = 0; } return flag_changed; } /* * call-seq: * str.tr(from_str, to_str) => new_str * * Returns a copy of str with the characters in from_str replaced by the * corresponding characters in to_str. If to_str is shorter than from_str, * it is padded with its last character in order to maintain the * correspondence. * * "hello".tr('el', 'ip') #=> "hippo" * "hello".tr('aeiou', '*') #=> "h*ll*" * "hello".tr('aeiou', 'AA*') #=> "hAll*" * * Both strings may use the c1-c2 notation to denote ranges of characters, * and from_str may start with a ^, which denotes all characters except * those listed. * * "hello".tr('a-y', 'b-z') #=> "ifmmp" * "hello".tr('^aeiou', '*') #=> "*e**o" * * The backslash character \ can be used to escape ^ or - and is otherwise * ignored unless it appears at the end of a range or the end of the * from_str or to_str: * * * "hello^world".tr("\\^aeiou", "*") #=> "h*ll**w*rld" * "hello-world".tr("a\\-eo", "*") #=> "h*ll**w*rld" * * "hello\r\nworld".tr("\r", "") #=> "hello\nworld" * "hello\r\nworld".tr("\\r", "") #=> "hello\r\nwold" * "hello\r\nworld".tr("\\\r", "") #=> "hello\nworld" * * "X['\\b']".tr("X\\", "") #=> "['b']" * "X['\\b']".tr("X-\\]", "") #=> "'b'" * * Note: conversion is effective only in ASCII region. */ static mrb_value str_tr_m(mrb_state *mrb, mrb_value str) { mrb_value p1, p2; mrb_get_args(mrb, "SS", &p1, &p2); mrb_value dup = mrb_str_dup(mrb, str); str_tr(mrb, dup, p1, p2, FALSE); return dup; } /* * call-seq: * str.tr!(from_str, to_str) -> str or nil * * Translates str in place, using the same rules as String#tr. * Returns str, or nil if no changes were made. */ static mrb_value str_tr_bang(mrb_state *mrb, mrb_value str) { mrb_value p1, p2; mrb_get_args(mrb, "SS", &p1, &p2); if (str_tr(mrb, str, p1, p2, FALSE)) { return str; } return mrb_nil_value(); } /* * call-seq: * str.tr_s(from_str, to_str) -> new_str * * Processes a copy of str as described under String#tr, then removes * duplicate characters in regions that were affected by the translation. * * "hello".tr_s('l', 'r') #=> "hero" * "hello".tr_s('el', '*') #=> "h*o" * "hello".tr_s('el', 'hx') #=> "hhxo" */ static mrb_value str_tr_s(mrb_state *mrb, mrb_value str) { mrb_value p1, p2; mrb_get_args(mrb, "SS", &p1, &p2); mrb_value dup = mrb_str_dup(mrb, str); str_tr(mrb, dup, p1, p2, TRUE); return dup; } /* * call-seq: * str.tr_s!(from_str, to_str) -> str or nil * * Performs String#tr_s processing on str in place, returning * str, or nil if no changes were made. */ static mrb_value str_tr_s_bang(mrb_state *mrb, mrb_value str) { mrb_value p1, p2; mrb_get_args(mrb, "SS", &p1, &p2); if (str_tr(mrb, str, p1, p2, TRUE)) { return str; } return mrb_nil_value(); } static mrb_bool str_squeeze(mrb_state *mrb, mrb_value str, mrb_value v_pat) { struct tr_pattern pat_storage = STATIC_TR_PATTERN; struct tr_pattern *pat = NULL; mrb_int i, j; mrb_bool flag_changed = FALSE; mrb_int lastch = -1; uint8_t bitmap[32]; mrb_str_modify(mrb, mrb_str_ptr(str)); if (!mrb_nil_p(v_pat)) { pat = tr_parse_pattern(mrb, &pat_storage, v_pat, TRUE, NULL); tr_compile_pattern(pat, v_pat, bitmap); tr_free_pattern(mrb, pat); } char *s = RSTRING_PTR(str); mrb_int len = RSTRING_LEN(str); if (pat) { for (i=j=0; ij) s[j] = s[i]; if (tr_bitmap_detect(bitmap, s[i]) && s[i] == lastch) { flag_changed = TRUE; j--; } lastch = s[i]; } } else { for (i=j=0; ij) s[j] = s[i]; if (s[i] >= 0 && s[i] == lastch) { flag_changed = TRUE; j--; } lastch = s[i]; } } if (flag_changed) { RSTR_SET_LEN(RSTRING(str), j); RSTRING_PTR(str)[j] = 0; } return flag_changed; } /* * call-seq: * str.squeeze([other_str]) -> new_str * * Builds a set of characters from the other_str * parameter(s) using the procedure described for String#count. Returns a * new string where runs of the same character that occur in this set are * replaced by a single character. If no arguments are given, all runs of * identical characters are replaced by a single character. * * "yellow moon".squeeze #=> "yelow mon" * " now is the".squeeze(" ") #=> " now is the" * "putters shoot balls".squeeze("m-z") #=> "puters shot balls" */ static mrb_value str_squeeze_m(mrb_state *mrb, mrb_value str) { mrb_value pat = mrb_nil_value(); mrb_get_args(mrb, "|S", &pat); mrb_value dup = mrb_str_dup(mrb, str); str_squeeze(mrb, dup, pat); return dup; } /* * call-seq: * str.squeeze!([other_str]) -> str or nil * * Squeezes str in place, returning either str, or nil if no * changes were made. */ static mrb_value str_squeeze_bang(mrb_state *mrb, mrb_value str) { mrb_value pat = mrb_nil_value(); mrb_get_args(mrb, "|S", &pat); if (str_squeeze(mrb, str, pat)) { return str; } return mrb_nil_value(); } static mrb_bool str_delete(mrb_state *mrb, mrb_value str, mrb_value v_pat) { struct tr_pattern pat = STATIC_TR_PATTERN; mrb_bool flag_changed = FALSE; uint8_t bitmap[32]; mrb_str_modify(mrb, mrb_str_ptr(str)); tr_parse_pattern(mrb, &pat, v_pat, TRUE, NULL); tr_compile_pattern(&pat, v_pat, bitmap); tr_free_pattern(mrb, &pat); char *s = RSTRING_PTR(str); mrb_int len = RSTRING_LEN(str); mrb_int i, j; for (i=j=0; ij) s[j] = s[i]; if (tr_bitmap_detect(bitmap, s[i])) { flag_changed = TRUE; j--; } } if (flag_changed) { RSTR_SET_LEN(RSTRING(str), j); RSTRING_PTR(str)[j] = 0; } return flag_changed; } static mrb_value str_delete_m(mrb_state *mrb, mrb_value str) { mrb_value pat; mrb_get_args(mrb, "S", &pat); mrb_value dup = mrb_str_dup(mrb, str); str_delete(mrb, dup, pat); return dup; } static mrb_value str_delete_bang(mrb_state *mrb, mrb_value str) { mrb_value pat; mrb_get_args(mrb, "S", &pat); if (str_delete(mrb, str, pat)) { return str; } return mrb_nil_value(); } /* * call_seq: * str.count([other_str]) -> integer * * Each other_str parameter defines a set of characters to count. The * intersection of these sets defines the characters to count in str. Any * other_str that starts with a caret ^ is negated. The sequence c1-c2 * means all characters between c1 and c2. The backslash character \ can * be used to escape ^ or - and is otherwise ignored unless it appears at * the end of a sequence or the end of a other_str. */ static mrb_value str_count(mrb_state *mrb, mrb_value str) { mrb_value v_pat = mrb_nil_value(); struct tr_pattern pat = STATIC_TR_PATTERN; uint8_t bitmap[32]; mrb_get_args(mrb, "S", &v_pat); tr_parse_pattern(mrb, &pat, v_pat, TRUE, NULL); tr_compile_pattern(&pat, v_pat, bitmap); tr_free_pattern(mrb, &pat); char *s = RSTRING_PTR(str); mrb_int len = RSTRING_LEN(str); mrb_int count = 0; for (mrb_int i = 0; i < len; i++) { if (tr_bitmap_detect(bitmap, s[i])) count++; } return mrb_fixnum_value(count); } static mrb_value str_hex(mrb_state *mrb, mrb_value self) { return mrb_str_to_integer(mrb, self, 16, FALSE); } static mrb_value str_oct(mrb_state *mrb, mrb_value self) { return mrb_str_to_integer(mrb, self, 8, FALSE); } /* * call-seq: * string.chr -> string * * Returns a one-character string at the beginning of the string. * * a = "abcde" * a.chr #=> "a" */ static mrb_value str_chr(mrb_state *mrb, mrb_value self) { return mrb_str_substr(mrb, self, 0, 1); } /* * call-seq: * int.chr([encoding]) -> string * * Returns a string containing the character represented by the +int+'s value * according to +encoding+. +"ASCII-8BIT"+ (+"BINARY"+) and +"UTF-8"+ (only * with +MRB_UTF8_STRING+) can be specified as +encoding+ (default is * +"ASCII-8BIT"+). * * 65.chr #=> "A" * 230.chr #=> "\xE6" * 230.chr("ASCII-8BIT") #=> "\xE6" * 230.chr("UTF-8") #=> "\u00E6" */ static mrb_value int_chr(mrb_state *mrb, mrb_value num) { mrb_value enc; mrb_bool enc_given; mrb_get_args(mrb, "|S?", &enc, &enc_given); if (!enc_given || ENC_COMP_P(enc, ENC_ASCII_8BIT) || ENC_COMP_P(enc, ENC_BINARY)) { return int_chr_binary(mrb, num); } #ifdef MRB_UTF8_STRING else if (ENC_COMP_P(enc, ENC_UTF8)) { return int_chr_utf8(mrb, num); } #endif else { mrb_raisef(mrb, E_ARGUMENT_ERROR, "unknown encoding name - %v", enc); } /* not reached */ return mrb_nil_value(); } /* * call-seq: * string.succ -> string * * Returns next sequence of the string; * * a = "bed" * a.succ #=> "bee" */ static mrb_value str_succ_bang(mrb_state *mrb, mrb_value self) { mrb_value result; const char *prepend; struct RString *s = mrb_str_ptr(self); if (RSTRING_LEN(self) == 0) return self; mrb_str_modify(mrb, s); mrb_int l = RSTRING_LEN(self); unsigned char *p, *e, *b, *t; b = p = (unsigned char*) RSTRING_PTR(self); t = e = p + l; *(e--) = 0; // find trailing ascii/number while (e >= b) { if (ISALNUM(*e)) break; e--; } if (e < b) { e = p + l - 1; result = mrb_str_new_lit(mrb, ""); } else { // find leading letter of the ascii/number b = e; while (b > p) { if (!ISALNUM(*b) || (ISALNUM(*b) && *b != '9' && *b != 'z' && *b != 'Z')) break; b--; } if (!ISALNUM(*b)) b++; result = mrb_str_new(mrb, (char*) p, b - p); } while (e >= b) { if (!ISALNUM(*e)) { if (*e == 0xff) { mrb_str_cat_lit(mrb, result, "\x01"); (*e) = 0; } else (*e)++; break; } prepend = NULL; if (*e == '9') { if (e == b) prepend = "1"; *e = '0'; } else if (*e == 'z') { if (e == b) prepend = "a"; *e = 'a'; } else if (*e == 'Z') { if (e == b) prepend = "A"; *e = 'A'; } else { (*e)++; break; } if (prepend) mrb_str_cat_cstr(mrb, result, prepend); e--; } result = mrb_str_cat(mrb, result, (char*) b, t - b); l = RSTRING_LEN(result); mrb_str_resize(mrb, self, l); memcpy(RSTRING_PTR(self), RSTRING_PTR(result), l); return self; } static mrb_value str_succ(mrb_state *mrb, mrb_value self) { mrb_value str = mrb_str_dup(mrb, self); str_succ_bang(mrb, str); return str; } #ifdef MRB_UTF8_STRING extern const char mrb_utf8len_table[]; MRB_INLINE mrb_int utf8code(mrb_state* mrb, const unsigned char* p, const unsigned char *e) { if (p[0] < 0x80) return p[0]; mrb_int len = mrb_utf8len_table[p[0]>>3]; if (p+len <= e && len > 1 && (p[1] & 0xc0) == 0x80) { if (len == 2) return ((p[0] & 0x1f) << 6) + (p[1] & 0x3f); if ((p[2] & 0xc0) == 0x80) { if (len == 3) return ((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6) + (p[2] & 0x3f); if ((p[3] & 0xc0) == 0x80) { if (len == 4) return ((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12) + ((p[2] & 0x3f) << 6) + (p[3] & 0x3f); } } } mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid UTF-8 byte sequence"); /* not reached */ return -1; } static mrb_value str_ord(mrb_state* mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); const unsigned char *p = (unsigned char*)RSTR_PTR(s); const unsigned char *e = p + RSTR_LEN(s); mrb_int c; if (p == e) { mrb_raise(mrb, E_ARGUMENT_ERROR, "empty string"); } if (RSTR_SINGLE_BYTE_P(s) || RSTR_BINARY_P(s)) { c = p[0]; } else { c = utf8code(mrb, p, e); } return mrb_fixnum_value(c); } static mrb_value str_codepoints(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); const unsigned char *p = (unsigned char*)RSTR_PTR(s); const unsigned char *e = p + RSTR_LEN(s); mrb->c->ci->mid = 0; mrb_value result = mrb_ary_new(mrb); if (RSTR_SINGLE_BYTE_P(s) || RSTR_BINARY_P(s)) { while (p < e) { mrb_ary_push(mrb, result, mrb_int_value(mrb, (mrb_int)*p)); p++; } } else { while (p < e) { mrb_int c = utf8code(mrb, p, e); mrb_ary_push(mrb, result, mrb_int_value(mrb, c)); p += mrb_utf8len_table[p[0]>>3]; } } return result; } #else static mrb_value str_ord(mrb_state* mrb, mrb_value str) { if (RSTRING_LEN(str) == 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "empty string"); return mrb_fixnum_value((unsigned char)RSTRING_PTR(str)[0]); } static mrb_value str_codepoints(mrb_state *mrb, mrb_value self) { char *p = RSTRING_PTR(self); char *e = p + RSTRING_LEN(self); mrb->c->ci->mid = 0; mrb_value result = mrb_ary_new(mrb); while (p < e) { mrb_ary_push(mrb, result, mrb_int_value(mrb, (mrb_int)*p)); p++; } return result; } #endif /* * call-seq: * str.delete_prefix!(prefix) -> self or nil * * Deletes leading prefix from str, returning * nil if no change was made. * * "hello".delete_prefix!("hel") #=> "lo" * "hello".delete_prefix!("llo") #=> nil */ static mrb_value str_del_prefix_bang(mrb_state *mrb, mrb_value self) { mrb_int plen; const char *ptr; struct RString *str = RSTRING(self); mrb_get_args(mrb, "s", &ptr, &plen); mrb_int slen = RSTR_LEN(str); if (plen > slen) return mrb_nil_value(); char *s = RSTR_PTR(str); if (memcmp(s, ptr, plen) != 0) return mrb_nil_value(); if (!mrb_frozen_p(str) && (RSTR_SHARED_P(str) || RSTR_FSHARED_P(str))) { str->as.heap.ptr += plen; } else { mrb_str_modify(mrb, str); s = RSTR_PTR(str); memmove(s, s+plen, slen-plen); } RSTR_SET_LEN(str, slen-plen); return self; } /* * call-seq: * str.delete_prefix(prefix) -> new_str * * Returns a copy of str with leading prefix deleted. * * "hello".delete_prefix("hel") #=> "lo" * "hello".delete_prefix("llo") #=> "hello" */ static mrb_value str_del_prefix(mrb_state *mrb, mrb_value self) { mrb_int plen; const char *ptr; mrb_get_args(mrb, "s", &ptr, &plen); mrb_int slen = RSTRING_LEN(self); if (plen > slen) return mrb_str_dup(mrb, self); if (memcmp(RSTRING_PTR(self), ptr, plen) != 0) return mrb_str_dup(mrb, self); return mrb_str_substr(mrb, self, plen, slen-plen); } /* * call-seq: * str.delete_suffix!(suffix) -> self or nil * * Deletes trailing suffix from str, returning * nil if no change was made. * * "hello".delete_suffix!("llo") #=> "he" * "hello".delete_suffix!("hel") #=> nil */ static mrb_value str_del_suffix_bang(mrb_state *mrb, mrb_value self) { mrb_int plen; const char *ptr; struct RString *str = RSTRING(self); mrb_get_args(mrb, "s", &ptr, &plen); mrb_int slen = RSTR_LEN(str); if (plen > slen) return mrb_nil_value(); char *s = RSTR_PTR(str); if (memcmp(s+slen-plen, ptr, plen) != 0) return mrb_nil_value(); if (!mrb_frozen_p(str) && (RSTR_SHARED_P(str) || RSTR_FSHARED_P(str))) { /* no need to modify string */ } else { mrb_str_modify(mrb, str); } RSTR_SET_LEN(str, slen-plen); return self; } /* * call-seq: * str.delete_suffix(suffix) -> new_str * * Returns a copy of str with leading suffix deleted. * * "hello".delete_suffix("hel") #=> "lo" * "hello".delete_suffix("llo") #=> "hello" */ static mrb_value str_del_suffix(mrb_state *mrb, mrb_value self) { mrb_int plen; const char *ptr; mrb_get_args(mrb, "s", &ptr, &plen); mrb_int slen = RSTRING_LEN(self); if (plen > slen) return mrb_str_dup(mrb, self); if (memcmp(RSTRING_PTR(self)+slen-plen, ptr, plen) != 0) return mrb_str_dup(mrb, self); return mrb_str_substr(mrb, self, 0, slen-plen); } #define lesser(a,b) (((a)>(b))?(b):(a)) /* * call-seq: * str.casecmp(other_str) -> -1, 0, +1 or nil * * Case-insensitive version of String#<=>. * * "abcdef".casecmp("abcde") #=> 1 * "aBcDeF".casecmp("abcdef") #=> 0 * "abcdef".casecmp("abcdefg") #=> -1 * "abcdef".casecmp("ABCDEF") #=> 0 */ static mrb_value str_casecmp(mrb_state *mrb, mrb_value self) { mrb_value str = mrb_get_arg1(mrb); if (!mrb_string_p(str)) return mrb_nil_value(); struct RString *s1 = mrb_str_ptr(self); struct RString *s2 = mrb_str_ptr(str); mrb_int len1 = RSTR_LEN(s1); mrb_int len2 = RSTR_LEN(s2); mrb_int len = lesser(len1, len2); char *p1 = RSTR_PTR(s1); char *p2 = RSTR_PTR(s2); if (p1 == p2) return mrb_fixnum_value(0); for (mrb_int i=0; i c2) return mrb_fixnum_value(1); if (c1 < c2) return mrb_fixnum_value(-1); } if (len1 == len2) return mrb_fixnum_value(0); if (len1 > len2) return mrb_fixnum_value(1); return mrb_fixnum_value(-1); } /* * call-seq: * str.casecmp?(other) -> true, false, or nil * * Returns true if str and other_str are equal after case folding, * false if they are not equal, and nil if other is not a string. */ static mrb_value str_casecmp_p(mrb_state *mrb, mrb_value self) { mrb_value c = str_casecmp(mrb, self); if (mrb_nil_p(c)) return c; return mrb_bool_value(mrb_fixnum(c) == 0); } static mrb_value str_lines(mrb_state *mrb, mrb_value self) { mrb_value result; mrb_int len; char *b = RSTRING_PTR(self); char *p = b, *t; char *e = b + RSTRING_LEN(self); mrb->c->ci->mid = 0; result = mrb_ary_new(mrb); int ai = mrb_gc_arena_save(mrb); while (p < e) { t = p; while (p < e && *p != '\n') p++; if (*p == '\n') p++; len = (mrb_int) (p - t); mrb_ary_push(mrb, result, mrb_str_new(mrb, t, len)); mrb_gc_arena_restore(mrb, ai); } return result; } /* * call-seq: * +string -> new_string or self * * Returns +self+ if +self+ is not frozen. * * Otherwise returns self.dup, which is not frozen. */ static mrb_value str_uplus(mrb_state *mrb, mrb_value str) { if (mrb_frozen_p(mrb_obj_ptr(str))) { return mrb_str_dup(mrb, str); } else { return str; } } /* * call-seq: * -string -> frozen_string * * Returns a frozen, possibly pre-existing copy of the string. * */ static mrb_value str_uminus(mrb_state *mrb, mrb_value str) { if (mrb_frozen_p(mrb_obj_ptr(str))) { return str; } return mrb_obj_freeze(mrb, mrb_str_dup(mrb, str)); } static mrb_value str_ascii_only_p(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); const char *p = RSTR_PTR(s); const char *e = p + RSTR_LEN(s); while (p < e) { if (*p & 0x80) return mrb_false_value(); p++; } mrb_str_ptr(str)->flags |= MRB_STR_SINGLE_BYTE; return mrb_true_value(); } static mrb_value str_b(mrb_state *mrb, mrb_value self) { mrb_value str = mrb_str_dup(mrb, self); mrb_str_ptr(str)->flags |= MRB_STR_BINARY; return str; } void mrb_mruby_string_ext_gem_init(mrb_state* mrb) { struct RClass *s = mrb->string_class; mrb_define_method_id(mrb, s, MRB_SYM(dump), mrb_str_dump, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_SYM_B(swapcase), str_swapcase_bang, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_SYM(swapcase), str_swapcase, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_SYM(concat), str_concat_m, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, s, MRB_OPSYM(lshift), str_concat_m, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, s, MRB_SYM(append_as_bytes), str_append_as_bytes, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, s, MRB_SYM(count), str_count, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, s, MRB_SYM(tr), str_tr_m, MRB_ARGS_REQ(2)); mrb_define_method_id(mrb, s, MRB_SYM_B(tr), str_tr_bang, MRB_ARGS_REQ(2)); mrb_define_method_id(mrb, s, MRB_SYM(tr_s), str_tr_s, MRB_ARGS_REQ(2)); mrb_define_method_id(mrb, s, MRB_SYM_B(tr_s), str_tr_s_bang, MRB_ARGS_REQ(2)); mrb_define_method_id(mrb, s, MRB_SYM(squeeze), str_squeeze_m, MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, s, MRB_SYM_B(squeeze), str_squeeze_bang, MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, s, MRB_SYM(delete), str_delete_m, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, s, MRB_SYM_B(delete), str_delete_bang, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, s, MRB_SYM_Q(start_with), str_start_with, MRB_ARGS_REST()); mrb_define_method_id(mrb, s, MRB_SYM_Q(end_with), str_end_with, MRB_ARGS_REST()); mrb_define_method_id(mrb, s, MRB_SYM(hex), str_hex, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_SYM(oct), str_oct, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_SYM(chr), str_chr, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_SYM(succ), str_succ, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_SYM_B(succ), str_succ_bang, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_SYM(next), str_succ, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_SYM_B(next), str_succ_bang, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_SYM(ord), str_ord, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_SYM_B(delete_prefix), str_del_prefix_bang, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, s, MRB_SYM(delete_prefix), str_del_prefix, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, s, MRB_SYM_B(delete_suffix), str_del_suffix_bang, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, s, MRB_SYM(delete_suffix), str_del_suffix, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, s, MRB_SYM(casecmp), str_casecmp, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, s, MRB_SYM_Q(casecmp), str_casecmp_p, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, s, MRB_OPSYM(plus), str_uplus, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, s, MRB_OPSYM(minus), str_uminus, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, s, MRB_SYM_Q(ascii_only), str_ascii_only_p, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_SYM(b), str_b, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_SYM(__lines), str_lines, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_SYM(__codepoints), str_codepoints, MRB_ARGS_NONE()); mrb_define_method_id(mrb, mrb->integer_class, MRB_SYM(chr), int_chr, MRB_ARGS_OPT(1)); } void mrb_mruby_string_ext_gem_final(mrb_state* mrb) { } nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/stdlib-ext.gembox0000644000000000000000000000013115077107276022610 xustar0029 mtime=1761382078.13042052 30 atime=1761382080.149411284 30 ctime=1761382108.793301269 nghttp2-1.68.0/third-party/mruby/mrbgems/stdlib-ext.gembox0000644000175100017510000000075415077107276023207 0ustar00runnerrunner# It also works with MRB_NO_STDIO and MRB_NO_FLOAT. MRuby::GemBox.new do |conf| # Use standard Array#pack, String#unpack methods conf.gem :core => "mruby-pack" # Use standard Kernel#sprintf method conf.gem :core => "mruby-sprintf" # Use standard Time class conf.gem :core => "mruby-time" # Use standard Struct class conf.gem :core => "mruby-struct" # Use standard Data class conf.gem :core => "mruby-data" # Use Random class conf.gem :core => "mruby-random" end nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-enum-chain0000644000000000000000000000013215077107334022425 xustar0030 mtime=1761382108.762301358 30 atime=1761382109.798298363 30 ctime=1761382108.762301358 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-chain/0000755000175100017510000000000015077107334023072 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-chain/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024624 xustar0030 mtime=1761382078.116420584 30 atime=1761382080.138411334 30 ctime=1761382108.762301358 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-chain/mrbgem.rake0000644000175100017510000000035515077107276025217 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-enum-chain') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Enumerator::Chain class' spec.add_dependency('mruby-enumerator', :core => 'mruby-enumerator') end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-chain/PaxHeaders/mrblib0000644000000000000000000000013215077107334023674 xustar0030 mtime=1761382108.765301349 30 atime=1761382109.799298361 30 ctime=1761382108.765301349 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-chain/mrblib/0000755000175100017510000000000015077107334024341 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-chain/mrblib/PaxHeaders/chain.rb0000644000000000000000000000013215077107276025365 xustar0030 mtime=1761382078.116420584 30 atime=1761382080.138411334 30 ctime=1761382108.765301349 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-chain/mrblib/chain.rb0000644000175100017510000000172115077107276025756 0ustar00runnerrunner## # chain.rb Enumerator::Chain class # See Copyright Notice in mruby.h module Enumerable def chain(*args) Enumerator::Chain.new(self, *args) end end class Enumerator def +(other) Chain.new(self, other) end class Chain include Enumerable def initialize(*args) @enums = args.freeze @pos = -1 end def each(&block) return to_enum unless block i = 0 while i < @enums.size @pos = i @enums[i].each(&block) i += 1 end self end def size @enums.reduce(0) do |a, e| return nil unless e.respond_to?(:size) a + e.size end end def rewind while 0 <= @pos && @pos < @enums.size e = @enums[@pos] e.rewind if e.respond_to?(:rewind) @pos -= 1 end self end def +(other) self.class.new(self, other) end def inspect "#<#{self.class}: #{@enums.inspect}>" end end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-chain/PaxHeaders/test0000644000000000000000000000013215077107334023404 xustar0030 mtime=1761382108.763301355 30 atime=1761382109.799298361 30 ctime=1761382108.763301355 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-chain/test/0000755000175100017510000000000015077107334024051 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-chain/test/PaxHeaders/enum_chain.rb0000644000000000000000000000013215077107276026121 xustar0030 mtime=1761382078.116420584 30 atime=1761382080.138411334 30 ctime=1761382108.763301355 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-chain/test/enum_chain.rb0000644000175100017510000000521615077107276026515 0ustar00runnerrunner## # Enumerator::Chain test assert("Enumerable#chain") do a = [] b = {} c = Object.new # not has #each method assert_kind_of Enumerator::Chain, a.chain assert_kind_of Enumerator::Chain, a.chain(b) assert_kind_of Enumerator::Chain, a.chain(b, c) assert_raise(NoMethodError) { c.chain } end assert("Enumerator#+") do a = [].each b = {}.each c = Object.new # not has #each method assert_kind_of Enumerator::Chain, a + b assert_kind_of Enumerator::Chain, a + c assert_kind_of Enumerator::Chain, b + a assert_kind_of Enumerator::Chain, b + c assert_raise(NoMethodError) { c + a } end assert("Enumerator::Chain.new") do a = [] b = {} c = Object.new # not has #each method assert_kind_of Enumerator::Chain, Enumerator::Chain.new assert_kind_of Enumerator::Chain, Enumerator::Chain.new(a, a) assert_kind_of Enumerator::Chain, Enumerator::Chain.new(a, b) assert_kind_of Enumerator::Chain, Enumerator::Chain.new(a, c) assert_kind_of Enumerator::Chain, Enumerator::Chain.new(b, a) assert_kind_of Enumerator::Chain, Enumerator::Chain.new(b, b) assert_kind_of Enumerator::Chain, Enumerator::Chain.new(b, c) assert_kind_of Enumerator::Chain, Enumerator::Chain.new(c, a) end assert("Enumerator::Chain#each") do a = [1, 2, 3] aa = a.chain(a) assert_kind_of Enumerator, aa.each assert_equal [1, 2, 3, 1, 2, 3], aa.each.to_a aa = a.chain(6..9) assert_kind_of Enumerator, aa.each assert_equal [1, 2, 3, 6, 7, 8, 9], aa.each.to_a aa = a.chain((-3..-2).each_with_index, a) assert_kind_of Enumerator, aa.each assert_equal [1, 2, 3, [-3, 0], [-2, 1], 1, 2, 3], aa.each.to_a aa = a.chain(Object.new) assert_kind_of Enumerator, aa.each assert_raise(NoMethodError) { aa.each.to_a } end assert("Enumerator::Chain#size") do a = [1, 2, 3] aa = a.chain(a) assert_equal 6, aa.size aa = a.chain(3..4) assert_nil aa.size aa = a.chain(3..4, a) assert_nil aa.size aa = a.chain(Object.new) assert_nil aa.size end assert("Enumerator::Chain#rewind") do rewound = nil e1 = [1, 2] e2 = (4..6) (class << e1; self end).define_method(:rewind) { rewound << self } (class << e2; self end).define_method(:rewind) { rewound << self } c = e1.chain(e2) rewound = [] c.rewind assert_equal [], rewound rewound = [] c.each{break c}.rewind assert_equal [e1], rewound rewound = [] c.each{}.rewind assert_equal [e2, e1], rewound end assert("Enumerator::Chain#+") do a = [].chain b = {}.chain c = Object.new # not has #each method assert_kind_of Enumerator::Chain, a + b assert_kind_of Enumerator::Chain, a + c assert_kind_of Enumerator::Chain, b + a assert_kind_of Enumerator::Chain, b + c end nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-compar-ext0000644000000000000000000000013215077107334022460 xustar0030 mtime=1761382108.642301705 30 atime=1761382109.799298361 30 ctime=1761382108.642301705 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compar-ext/0000755000175100017510000000000015077107334023125 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compar-ext/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024657 xustar0030 mtime=1761382078.109420616 30 atime=1761382080.128411379 30 ctime=1761382108.642301705 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compar-ext/mrbgem.rake0000644000175100017510000000025215077107276025246 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-compar-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Comparable module extension' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compar-ext/PaxHeaders/mrblib0000644000000000000000000000013215077107334023727 xustar0030 mtime=1761382108.645301696 30 atime=1761382109.799298361 30 ctime=1761382108.645301696 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compar-ext/mrblib/0000755000175100017510000000000015077107334024374 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compar-ext/mrblib/PaxHeaders/compar.rb0000644000000000000000000000013215077107276025617 xustar0030 mtime=1761382078.109420616 30 atime=1761382080.128411379 30 ctime=1761382108.645301696 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compar-ext/mrblib/compar.rb0000644000175100017510000000401015077107276026202 0ustar00runnerrunnermodule Comparable ## # call-seq: # obj.clamp(min, max) -> obj # obj.clamp(range) -> obj # # In (min, max) form, returns _min_ if _obj_ # <=> _min_ is less than zero, _max_ if _obj_ # <=> _max_ is greater than zero, and _obj_ # otherwise. # # 12.clamp(0, 100) #=> 12 # 523.clamp(0, 100) #=> 100 # -3.123.clamp(0, 100) #=> 0 # # 'd'.clamp('a', 'f') #=> 'd' # 'z'.clamp('a', 'f') #=> 'f' # # In (range) form, returns _range.begin_ if _obj_ # <=> _range.begin_ is less than zero, _range.end_ # if _obj_ <=> _range.end_ is greater than zero, and # _obj_ otherwise. # # 12.clamp(0..100) #=> 12 # 523.clamp(0..100) #=> 100 # -3.123.clamp(0..100) #=> 0 # # 'd'.clamp('a'..'f') #=> 'd' # 'z'.clamp('a'..'f') #=> 'f' # # If _range.begin_ is +nil+, it is considered smaller than _obj_, # and if _range.end_ is +nil+, it is considered greater than # _obj_. # # -20.clamp(0..) #=> 0 # 523.clamp(..100) #=> 100 # # When _range.end_ is excluded and not +nil+, an exception is # raised. # # 100.clamp(0...100) # ArgumentError # def clamp(min, max=nil) if max.nil? if min.kind_of?(Range) max = min.end if !max.nil? && min.exclude_end? raise ArgumentError, "cannot clamp with an exclusive range" end min = min.begin end end if !min.nil? && !max.nil? cmp = min <=> max if cmp.nil? raise ArgumentError, "comparison of #{min.class} with #{max.class} failed" elsif cmp > 0 raise ArgumentError, "min argument must be smaller than max argument" end end unless min.nil? cmp = self <=> min return self if cmp == 0 return min if cmp < 0 end unless max.nil? cmp = self <=> max return max if cmp > 0 end return self end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compar-ext/PaxHeaders/test0000644000000000000000000000013215077107334023437 xustar0030 mtime=1761382108.643301702 30 atime=1761382109.799298361 30 ctime=1761382108.643301702 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compar-ext/test/0000755000175100017510000000000015077107334024104 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compar-ext/test/PaxHeaders/compar.rb0000644000000000000000000000013215077107276025327 xustar0030 mtime=1761382078.109420616 30 atime=1761382080.128411379 30 ctime=1761382108.643301702 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-compar-ext/test/compar.rb0000644000175100017510000000106515077107276025721 0ustar00runnerrunnerassert("Comparable#clamp") do assert_equal(12, 12.clamp(0, 100)) assert_equal(100, 532.clamp(0, 100)) assert_equal(0, -3.123.clamp(0, 100)) assert_equal('d', 'd'.clamp('a', 'f')) assert_equal('f', 'z'.clamp('a', 'f')) assert_equal(12, 12.clamp(0..100)) assert_equal(100, 523.clamp(0..100)) assert_equal(0, -3.123.clamp(0..100)) assert_equal('d', 'd'.clamp('a'..'f')) assert_equal('f', 'z'.clamp('a'..'f')) assert_equal(0, -20.clamp(0..)) assert_equal(100, 523.clamp(..100)) assert_raise(ArgumentError) { 100.clamp(0...100) } end nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-enumerator0000644000000000000000000000013015077107334022560 xustar0029 mtime=1761382108.79630126 30 atime=1761382109.799298361 29 ctime=1761382108.79630126 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enumerator/0000755000175100017510000000000015077107334023227 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enumerator/PaxHeaders/mrbgem.rake0000644000000000000000000000013115077107276024760 xustar0030 mtime=1761382078.117420579 30 atime=1761382080.138411334 29 ctime=1761382108.79630126 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enumerator/mrbgem.rake0000644000175100017510000000033415077107276025351 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-enumerator') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.add_dependency('mruby-fiber', :core => 'mruby-fiber') spec.summary = 'Enumerator class' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enumerator/PaxHeaders/mrblib0000644000000000000000000000013215077107334024031 xustar0030 mtime=1761382108.798301254 30 atime=1761382109.799298361 30 ctime=1761382108.798301254 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enumerator/mrblib/0000755000175100017510000000000015077107334024476 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enumerator/mrblib/PaxHeaders/enumerator.rb0000644000000000000000000000013215077107276026621 xustar0030 mtime=1761382078.117420579 30 atime=1761382080.138411334 30 ctime=1761382108.798301254 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enumerator/mrblib/enumerator.rb0000644000175100017510000005323615077107276027222 0ustar00runnerrunner## # enumerator.rb Enumerator class # See Copyright Notice in mruby.h ## # A class which allows both internal and external iteration. # # An Enumerator can be created by the following methods. # - {Kernel#to_enum} # - {Kernel#enum_for} # - {Enumerator#initialize Enumerator.new} # # Most methods have two forms: a block form where the contents # are evaluated for each item in the enumeration, and a non-block form # which returns a new Enumerator wrapping the iteration. # # enumerator = %w(one two three).each # puts enumerator.class # => Enumerator # # enumerator.each_with_object("foo") do |item, obj| # puts "#{obj}: #{item}" # end # # # foo: one # # foo: two # # foo: three # # enum_with_obj = enumerator.each_with_object("foo") # puts enum_with_obj.class # => Enumerator # # enum_with_obj.each do |item, obj| # puts "#{obj}: #{item}" # end # # # foo: one # # foo: two # # foo: three # # This allows you to chain Enumerators together. For example, you # can map a list's elements to strings containing the index # and the element as a string via: # # puts %w[foo bar baz].map.with_index { |w, i| "#{i}:#{w}" } # # => ["0:foo", "1:bar", "2:baz"] # # An Enumerator can also be used as an external iterator. # For example, Enumerator#next returns the next value of the iterator # or raises StopIteration if the Enumerator is at the end. # # e = [1,2,3].each # returns an enumerator object. # puts e.next # => 1 # puts e.next # => 2 # puts e.next # => 3 # puts e.next # raises StopIteration # # You can use this to implement an internal iterator as follows: # # def ext_each(e) # while true # begin # vs = e.next_values # rescue StopIteration # return $!.result # end # y = yield(*vs) # e.feed y # end # end # # o = Object.new # # def o.each # puts yield # puts yield(1) # puts yield(1, 2) # 3 # end # # # use o.each as an internal iterator directly. # puts o.each {|*x| puts x; [:b, *x] } # # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3 # # # convert o.each to an external iterator for # # implementing an internal iterator. # puts ext_each(o.to_enum) {|*x| puts x; [:b, *x] } # # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3 # class Enumerator include Enumerable ## # @overload initialize(obj, method = :each, *args, **kwd) # # Creates a new Enumerator object, which can be used as an # Enumerable. # # In the first form, iteration is defined by the given block, in # which a "yielder" object, given as block parameter, can be used to # yield a value by calling the +yield+ method (aliased as +<<+): # # fib = Enumerator.new do |y| # a = b = 1 # loop do # y << a # a, b = b, a + b # end # end # # p fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] # # In the second, deprecated, form, a generated Enumerator iterates over the # given object using the given method with the given arguments passed. This # form is left only for internal use. # # Use of this form is discouraged. Use Kernel#enum_for or Kernel#to_enum # instead. def initialize(obj=NONE, meth=:each, *args, **kwd, &block) if block obj = Generator.new(&block) elsif NONE.equal?(obj) raise ArgumentError, "wrong number of arguments (given 0, expected 1+)" end @obj = obj @meth = meth @args = args @kwd = kwd @fib = nil @dst = nil @lookahead = nil @feedvalue = nil @stop_exc = false end def initialize_copy(obj) raise TypeError, "can't copy type #{obj.class}" unless obj.kind_of? Enumerator raise TypeError, "can't copy execution context" if obj.instance_eval{@fib} meth = args = kwd = fib = nil obj.instance_eval { obj = @obj meth = @meth args = @args kwd = @kwd } @obj = obj @meth = meth @args = args @kwd = kwd @fib = nil @lookahead = nil @feedvalue = nil self end ## # call-seq: # e.with_index(offset = 0) {|(*args), idx| ... } # e.with_index(offset = 0) # # Iterates the given block for each element with an index, which # starts from +offset+. If no block is given, returns a new Enumerator # that includes the index, starting from +offset+ # # +offset+:: the starting index to use # def with_index(offset=0, &block) return to_enum :with_index, offset unless block if offset.nil? offset = 0 else offset = offset.__to_int end n = offset - 1 __enumerator_block_call do |*i| n += 1 block.call i.__svalue, n end end ## # call-seq: # e.each_with_index {|(*args), idx| ... } # e.each_with_index # # Same as Enumerator#with_index(0), i.e. there is no starting offset. # # If no block is given, a new Enumerator is returned that includes the index. # def each_with_index(&block) with_index(0, &block) end ## # call-seq: # e.each_with_object(obj) {|(*args), obj| ... } # e.each_with_object(obj) # e.with_object(obj) {|(*args), obj| ... } # e.with_object(obj) # # Iterates the given block for each element with an arbitrary object, +obj+, # and returns +obj+ # # If no block is given, returns a new Enumerator. # # @example # to_three = Enumerator.new do |y| # 3.times do |x| # y << x # end # end # # to_three_with_string = to_three.with_object("foo") # to_three_with_string.each do |x,string| # puts "#{string}: #{x}" # end # # # => foo:0 # # => foo:1 # # => foo:2 # def with_object(object, &block) return to_enum(:with_object, object) unless block __enumerator_block_call do |i| block.call [i,object] end object end def inspect if @args && @args.size > 0 args = @args.join(", ") "#<#{self.class}: #{@obj.inspect}:#{@meth}(#{args})>" else "#<#{self.class}: #{@obj.inspect}:#{@meth}>" end end def size if @size @size elsif @obj.respond_to?(:size) @obj.size end end ## # call-seq: # enum.each { |elm| block } -> obj # enum.each -> enum # enum.each(*appending_args) { |elm| block } -> obj # enum.each(*appending_args) -> an_enumerator # # Iterates over the block according to how this Enumerator was constructed. # If no block and no arguments are given, returns self. # # === Examples # # Array.new(3) #=> [nil, nil, nil] # Array.new(3) { |i| i } #=> [0, 1, 2] # Array.to_enum(:new, 3).to_a #=> [0, 1, 2] # Array.to_enum(:new).each(3).to_a #=> [0, 1, 2] # # obj = Object.new # # def obj.each_arg(a, b=:b, *rest) # yield a # yield b # yield rest # :method_returned # end # # enum = obj.to_enum :each_arg, :a, :x # # enum.each.to_a #=> [:a, :x, []] # enum.each.equal?(enum) #=> true # enum.each { |elm| elm } #=> :method_returned # # enum.each(:y, :z).to_a #=> [:a, :x, [:y, :z]] # enum.each(:y, :z).equal?(enum) #=> false # enum.each(:y, :z) { |elm| elm } #=> :method_returned # def each(*argv, &block) obj = self if 0 < argv.length obj = self.dup args = obj.instance_eval{@args} if !args.empty? args = args.dup args.concat argv else args = argv.dup end obj.instance_eval{@args = args} end return obj unless block __enumerator_block_call(&block) end def __enumerator_block_call(&block) @obj.__send__ @meth, *@args, **@kwd, &block end private :__enumerator_block_call ## # call-seq: # e.next -> object # # Returns the next object in the enumerator, and move the internal position # forward. When the position reached at the end, StopIteration is raised. # # === Example # # a = [1,2,3] # e = a.to_enum # p e.next #=> 1 # p e.next #=> 2 # p e.next #=> 3 # p e.next #raises StopIteration # # Note that enumeration sequence by +next+ does not affect other non-external # enumeration methods, unless the underlying iteration methods itself has # side-effect # def next next_values.__svalue end ## # call-seq: # e.next_values -> array # # Returns the next object as an array in the enumerator, and move the # internal position forward. When the position reached at the end, # StopIteration is raised. # # This method can be used to distinguish yield and yield # nil. # # === Example # # o = Object.new # def o.each # yield # yield 1 # yield 1, 2 # yield nil # yield [1, 2] # end # e = o.to_enum # p e.next_values # p e.next_values # p e.next_values # p e.next_values # p e.next_values # e = o.to_enum # p e.next # p e.next # p e.next # p e.next # p e.next # # ## yield args next_values next # # yield [] nil # # yield 1 [1] 1 # # yield 1, 2 [1, 2] [1, 2] # # yield nil [nil] nil # # yield [1, 2] [[1, 2]] [1, 2] # # Note that +next_values+ does not affect other non-external enumeration # methods unless underlying iteration method itself has side-effect # def next_values if @lookahead vs = @lookahead @lookahead = nil return vs end raise @stop_exc if @stop_exc curr = Fiber.current if !@fib || !@fib.alive? @dst = curr @fib = Fiber.new do result = each do |*args| feedvalue = nil Fiber.yield args if @feedvalue feedvalue = @feedvalue @feedvalue = nil end feedvalue end @stop_exc = StopIteration.new "iteration reached an end" @stop_exc.result = result Fiber.yield nil end @lookahead = nil end vs = @fib.resume curr if @stop_exc @fib = nil @dst = nil @lookahead = nil @feedvalue = nil raise @stop_exc end vs end ## # call-seq: # e.peek -> object # # Returns the next object in the enumerator, but doesn't move the internal # position forward. If the position is already at the end, StopIteration # is raised. # # === Example # # a = [1,2,3] # e = a.to_enum # p e.next #=> 1 # p e.peek #=> 2 # p e.peek #=> 2 # p e.peek #=> 2 # p e.next #=> 2 # p e.next #=> 3 # p e.next #raises StopIteration # def peek peek_values.__svalue end ## # call-seq: # e.peek_values -> array # # Returns the next object as an array, similar to Enumerator#next_values, but # doesn't move the internal position forward. If the position is already at # the end, StopIteration is raised. # # === Example # # o = Object.new # def o.each # yield # yield 1 # yield 1, 2 # end # e = o.to_enum # p e.peek_values #=> [] # e.next # p e.peek_values #=> [1] # p e.peek_values #=> [1] # e.next # p e.peek_values #=> [1, 2] # e.next # p e.peek_values # raises StopIteration # def peek_values if @lookahead.nil? @lookahead = next_values end @lookahead.dup end ## # call-seq: # e.rewind -> e # # Rewinds the enumeration sequence to the beginning. # # If the enclosed object responds to a "rewind" method, it is called. # def rewind @obj.rewind if @obj.respond_to? :rewind @fib = nil @dst = nil @lookahead = nil @feedvalue = nil @stop_exc = false self end ## # call-seq: # e.feed obj -> nil # # Sets the value to be returned by the next yield inside +e+. # # If the value is not set, the yield returns nil. # # This value is cleared after being yielded. # # # Array#map passes the array's elements to "yield" and collects the # # results of "yield" as an array. # # Following example shows that "next" returns the passed elements and # # values passed to "feed" are collected as an array which can be # # obtained by StopIteration#result. # e = [1,2,3].map # p e.next #=> 1 # e.feed "a" # p e.next #=> 2 # e.feed "b" # p e.next #=> 3 # e.feed "c" # begin # e.next # rescue StopIteration # p $!.result #=> ["a", "b", "c"] # end # # o = Object.new # def o.each # x = yield # (2) blocks # p x # (5) => "foo" # x = yield # (6) blocks # p x # (8) => nil # x = yield # (9) blocks # p x # not reached w/o another e.next # end # # e = o.to_enum # e.next # (1) # e.feed "foo" # (3) # e.next # (4) # e.next # (7) # # (10) # def feed(value) raise TypeError, "feed value already set" if @feedvalue @feedvalue = value nil end # just for internal class Generator include Enumerable def initialize(&block) raise TypeError, "wrong argument type #{self.class} (expected Proc)" unless block.kind_of? Proc @proc = block end def each(*args, &block) args.unshift Yielder.new(&block) @proc.call(*args) end end # just for internal class Yielder def initialize(&block) raise LocalJumpError, "no block given" unless block @proc = block end def yield(*args) @proc.call(*args) end def << *args self.yield(*args) self end end ## # call-seq: # Enumerator.produce(initial = nil) { |val| } -> enumerator # # Creates an infinite enumerator from any block, just called over and # over. Result of the previous iteration is passed to the next one. # If +initial+ is provided, it is passed to the first iteration, and # becomes the first element of the enumerator; if it is not provided, # first iteration receives +nil+, and its result becomes first # element of the iterator. # # Raising StopIteration from the block stops an iteration. # # Examples of usage: # # Enumerator.produce(1, &:succ) # => enumerator of 1, 2, 3, 4, ... # # Enumerator.produce { rand(10) } # => infinite random number sequence # # ancestors = Enumerator.produce(node) { |prev| node = prev.parent or raise StopIteration } # enclosing_section = ancestors.find { |n| n.type == :section } def Enumerator.produce(init=NONE, &block) raise ArgumentError, "no block given" if block.nil? Enumerator.new do |y| if NONE.equal?(init) val = nil else val = init y.yield(val) end begin while true y.yield(val = block.call(val)) end rescue StopIteration # do nothing end end end end module Kernel ## # call-seq: # obj.to_enum(method = :each, *args) -> enum # obj.enum_for(method = :each, *args) -> enum # # Creates a new Enumerator which will enumerate by calling +method+ on # +obj+, passing +args+ if any. # # === Examples # # str = "xyz" # # enum = str.enum_for(:each_byte) # enum.each { |b| puts b } # # => 120 # # => 121 # # => 122 # # # protect an array from being modified by some_method # a = [1, 2, 3] # some_method(a.to_enum) # # It is typical to call to_enum when defining methods for # a generic Enumerable, in case no block is passed. # # Here is such an example with parameter passing: # # module Enumerable # # a generic method to repeat the values of any enumerable # def repeat(n) # raise ArgumentError, "#{n} is negative!" if n < 0 # unless block_given? # return to_enum(__callee__, n) do # __callee__ is :repeat here # end # each do |*val| # n.times { yield *val } # end # end # end # # %i[hello world].repeat(2) { |w| puts w } # # => Prints 'hello', 'hello', 'world', 'world' # enum = (1..14).repeat(3) # # => returns an Enumerator when called without a block # enum.first(4) # => [1, 1, 1, 2] # def to_enum(meth=:each, *args, **kwd) Enumerator.new self, meth, *args, **kwd end alias enum_for to_enum end module Enumerable # use Enumerator to use infinite sequence def zip(*args, &block) args = args.map do |a| if a.respond_to?(:each) a.to_enum(:each) else raise TypeError, "wrong argument type #{a.class} (must respond to :each)" end end result = block ? nil : [] each do |*val| tmp = [val.__svalue] args.each do |arg| v = if arg.nil? nil else begin arg.next rescue StopIteration nil end end tmp.push(v) end if result.nil? block.call(tmp) else result.push(tmp) end end result end ## # call-seq: # enum.chunk -> enumerator # enum.chunk { |arr| block } -> enumerator # # Each element in the returned enumerator is a 2-element array consisting of: # # - A value returned by the block. # - An array ("chunk") containing the element for which that value was returned, # and all following elements for which the block returned the same value: # # So that: # # - Each block return value that is different from its predecessor # begins a new chunk. # - Each block return value that is the same as its predecessor # continues the same chunk. # # Example: # # e = (0..10).chunk {|i| (i / 3).floor } # => # # # The enumerator elements. # e.next # => [0, [0, 1, 2]] # e.next # => [1, [3, 4, 5]] # e.next # => [2, [6, 7, 8]] # e.next # => [3, [9, 10]] # # You can use the special symbol :_alone to force an element # into its own separate chuck: # # a = [0, 0, 1, 1] # e = a.chunk{|i| i.even? ? :_alone : true } # e.to_a # => [[:_alone, [0]], [:_alone, [0]], [true, [1, 1]]] # # You can use the special symbol :_separator or +nil+ # to force an element to be ignored (not included in any chunk): # # a = [0, 0, -1, 1, 1] # e = a.chunk{|i| i < 0 ? :_separator : true } # e.to_a # => [[true, [0, 0]], [true, [1, 1]]] def chunk(&block) return to_enum :chunk unless block enum = self Enumerator.new do |y| last_value, arr = nil, [] enum.each do |element| value = block.call(element) case value when :_alone y.yield [last_value, arr] if arr.size > 0 y.yield [value, [element]] last_value, arr = nil, [] when :_separator, nil y.yield [last_value, arr] if arr.size > 0 last_value, arr = nil, [] when last_value arr << element else raise 'symbols beginning with an underscore are reserved' if value.is_a?(Symbol) && value.to_s[0] == '_' y.yield [last_value, arr] if arr.size > 0 last_value, arr = value, [element] end end y.yield [last_value, arr] if arr.size > 0 end end ## # call-seq: # enum.chunk_while {|elt_before, elt_after| bool } -> an_enumerator # # Creates an enumerator for each chunked elements. # The beginnings of chunks are defined by the block. # # This method splits each chunk using adjacent elements, # _elt_before_ and _elt_after_, # in the receiver enumerator. # This method split chunks between _elt_before_ and _elt_after_ where # the block returns false. # # The block is called the length of the receiver enumerator minus one. # # The result enumerator yields the chunked elements as an array. # So +each+ method can be called as follows: # # enum.chunk_while { |elt_before, elt_after| bool }.each { |ary| ... } # # Other methods of the Enumerator class and Enumerable module, # such as +to_a+, +map+, etc., are also usable. # # For example, one-by-one increasing subsequence can be chunked as follows: # # a = [1,2,4,9,10,11,12,15,16,19,20,21] # b = a.chunk_while {|i, j| i+1 == j } # p b.to_a #=> [[1, 2], [4], [9, 10, 11, 12], [15, 16], [19, 20, 21]] # c = b.map {|a| a.length < 3 ? a : "#{a.first}-#{a.last}" } # p c #=> [[1, 2], [4], "9-12", [15, 16], "19-21"] # d = c.join(",") # p d #=> "1,2,4,9-12,15,16,19-21" # # Increasing (non-decreasing) subsequence can be chunked as follows: # # a = [0, 9, 2, 2, 3, 2, 7, 5, 9, 5] # p a.chunk_while {|i, j| i <= j }.to_a # #=> [[0, 9], [2, 2, 3], [2, 7], [5, 9], [5]] # # Adjacent evens and odds can be chunked as follows: # (Enumerable#chunk is another way to do it.) # # a = [7, 5, 9, 2, 0, 7, 9, 4, 2, 0] # p a.chunk_while {|i, j| i.even? == j.even? }.to_a # #=> [[7, 5, 9], [2, 0], [7, 9], [4, 2, 0]] # # Enumerable#slice_when does the same, except splitting when the block # returns true instead of false. # def chunk_while(&block) enum = self Enumerator.new do |y| n = 0 last_value, arr = nil, [] enum.each do |element| if n > 0 unless block.call(last_value, element) y.yield arr arr = [] end end arr.push(element) n += 1 last_value = element end y.yield arr if arr.size > 0 end end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enumerator/PaxHeaders/test0000644000000000000000000000013215077107334023541 xustar0030 mtime=1761382108.797301257 30 atime=1761382109.799298361 30 ctime=1761382108.797301257 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enumerator/test/0000755000175100017510000000000015077107334024206 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enumerator/test/PaxHeaders/enumerator.rb0000644000000000000000000000013215077107276026331 xustar0030 mtime=1761382078.117420579 30 atime=1761382080.138411334 30 ctime=1761382108.797301257 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enumerator/test/enumerator.rb0000644000175100017510000003545015077107276026730 0ustar00runnerrunner@obj = Object.new class << @obj include Enumerable def foo *a a.each { |x| yield x } end end def assert_take(exp, enumerator) result = [] n = exp.size enumerator.each do |v| result << v n -= 1 break if n == 0 end if n > 0 assert_equal exp, result end assert 'Enumerator.class' do assert_equal Class, Enumerator.class end assert 'Enumerator.superclass' do assert_equal Object, Enumerator.superclass end assert 'Enumerator.new' do assert_equal [0,1,2], 3.times.map{|i| i}.sort assert_equal [:x,:y,:z], [:x,:y,:z].each.map{|i| i}.sort assert_equal [[:x,1],[:y,2]], {x:1, y:2}.each.map{|i| i}.sort assert_equal [1,2,3], @obj.to_enum(:foo, 1,2,3).to_a assert_take [1,2,3], Enumerator.new { |y| i = 0; loop { y << (i += 1) } } assert_raise(ArgumentError) { Enumerator.new } # examples fib = Enumerator.new do |y| a = b = 1 loop do y << a a, b = b, a + b end end assert_take [1,1,2,3,5,8,13,21,34,55], fib end assert 'Enumerator#initialize_copy' do assert_equal [1, 2, 3], @obj.to_enum(:foo, 1, 2, 3).dup.to_a e = @obj.to_enum :foo, 1, 2, 3 assert_nothing_raised { assert_equal(1, e.next) } assert_raise(TypeError) { e.dup } e = Enumerator.new { |y| i = 0; loop { y << (i += 1) } }.dup assert_nothing_raised { assert_equal(1, e.next) } assert_raise(TypeError) { e.dup } end assert 'Enumerator#with_index' do assert_equal([[1,0],[2,1],[3,2]], @obj.to_enum(:foo, 1, 2, 3).with_index.to_a) assert_equal([[1,5],[2,6],[3,7]], @obj.to_enum(:foo, 1, 2, 3).with_index(5).to_a) a = [] @obj.to_enum(:foo, 1, 2, 3).with_index(10).with_index(20) { |*i| a << i } assert_equal [[[1, 10], 20], [[2, 11], 21], [[3, 12], 22]], a end assert 'Enumerator#with_index string offset' do assert_raise(TypeError){ @obj.to_enum(:foo, 1, 2, 3).with_index('1').to_a } end assert 'Enumerator#each_with_index' do assert_equal([[1,0],[2,1],[3,2]], @obj.to_enum(:foo, 1, 2, 3).each_with_index.to_a) a = [] @obj.to_enum(:foo, 1, 2, 3).each_with_index {|*i| a << i} assert_equal([[1, 0], [2, 1], [3, 2]], a) end assert 'Enumerator#with_object' do obj = [0, 1] ret = (1..10).each.with_object(obj) {|i, memo| memo[0] += i memo[1] *= i } assert_true(obj.equal?(ret)) assert_equal([55, 3628800], ret) end assert 'Enumerator#with_object arguments' do to_three = Enumerator.new do |y| 3.times do |x| y << x end end a = [] to_three_with_string = to_three.with_object("foo") to_three_with_string.each do |x,string| a << "#{string}:#{x}" end assert_equal ["foo:0","foo:1","foo:2"], a end assert 'Enumerator#inspect' do e = (0..10).each assert_equal('#', e.inspect) e = 'FooObject'.enum_for(:foo, 1) assert_equal('#', e.inspect) e = 'FooObject'.enum_for(:foo, 1, 2, 3) assert_equal('#', e.inspect) e = nil.enum_for(:to_s) assert_equal('#', e.inspect) end assert 'Enumerator#each' do o = Object.new def o.each(ary) ary << 1 yield end ary = [] e = o.to_enum.each(ary) e.next assert_equal([1], ary) end assert 'Enumerator#each arguments' do obj = Object.new def obj.each_arg(a, b=:b, *rest) yield a yield b yield rest :method_returned end enum = obj.to_enum :each_arg, :a, :x assert_equal [:a, :x, []], enum.each.to_a assert_true enum.each.equal?(enum) assert_equal :method_returned, enum.each { |elm| elm } assert_equal [:a, :x, [:y, :z]], enum.each(:y, :z).to_a assert_false enum.each(:y, :z).equal?(enum) assert_equal :method_returned, enum.each(:y, :z) { |elm| elm } end assert 'Enumerator#next' do e = 3.times 3.times { |i| assert_equal i, e.next } assert_raise(StopIteration) { e.next } end assert 'Enumerator#next_values' do o = Object.new def o.each yield yield 1 yield 1, 2 end e = o.to_enum assert_equal nil, e.next assert_equal 1, e.next assert_equal [1,2], e.next e = o.to_enum assert_equal [], e.next_values assert_equal [1], e.next_values assert_equal [1,2], e.next_values end assert 'Enumerator#peek' do a = [1] e = a.each assert_equal 1, e.peek assert_equal 1, e.peek assert_equal 1, e.next assert_raise(StopIteration) { e.peek } assert_raise(StopIteration) { e.peek } end assert 'Enumerator#peek modify' do o = Object.new def o.each yield 1,2 end e = o.to_enum a = e.peek a << 3 assert_equal([1,2], e.peek) end assert 'Enumerator#peek_values' do o = Object.new def o.each yield yield 1 yield 1, 2 end e = o.to_enum assert_equal nil, e.peek assert_equal nil, e.next assert_equal 1, e.peek assert_equal 1, e.next assert_equal [1,2], e.peek assert_equal [1,2], e.next e = o.to_enum assert_equal [], e.peek_values assert_equal [], e.next_values assert_equal [1], e.peek_values assert_equal [1], e.next_values assert_equal [1,2], e.peek_values assert_equal [1,2], e.next_values e = o.to_enum assert_equal [], e.peek_values assert_equal nil, e.next assert_equal [1], e.peek_values assert_equal 1, e.next assert_equal [1,2], e.peek_values assert_equal [1,2], e.next e = o.to_enum assert_equal nil, e.peek assert_equal [], e.next_values assert_equal 1, e.peek assert_equal [1], e.next_values assert_equal [1,2], e.peek assert_equal [1,2], e.next_values end assert 'Enumerator#peek_values modify' do o = Object.new def o.each yield 1,2 end e = o.to_enum a = e.peek_values a << 3 assert_equal [1,2], e.peek end assert 'Enumerator#feed' do o = Object.new def o.each(ary) ary << yield ary << yield ary << yield end ary = [] e = o.to_enum :each, ary e.next e.feed 1 e.next e.feed 2 e.next e.feed 3 assert_raise(StopIteration) { e.next } assert_equal [1,2,3], ary end assert 'Enumerator#feed mixed' do o = Object.new def o.each(ary) ary << yield ary << yield ary << yield end ary = [] e = o.to_enum :each, ary e.next e.feed 1 e.next e.next e.feed 3 assert_raise(StopIteration) { e.next } assert_equal [1,nil,3], ary end assert 'Enumerator#feed twice' do o = Object.new def o.each(ary) ary << yield ary << yield ary << yield end ary = [] e = o.to_enum :each, ary e.feed 1 assert_raise(TypeError) { e.feed 2 } end assert 'Enumerator#feed before first next' do o = Object.new def o.each(ary) ary << yield ary << yield ary << yield end ary = [] e = o.to_enum :each, ary e.feed 1 e.next e.next assert_equal [1], ary end assert 'Enumerator#feed yielder' do x = nil e = Enumerator.new {|y| x = y.yield; 10 } e.next e.feed 100 assert_raise(StopIteration) { e.next } assert_equal 100, x end assert 'Enumerator#rewind' do e = @obj.to_enum(:foo, 1, 2, 3) assert_equal 1, e.next assert_equal 2, e.next e.rewind assert_equal 1, e.next assert_equal 2, e.next assert_equal 3, e.next assert_raise(StopIteration) { e.next } end assert 'Enumerator#rewind clear feed' do o = Object.new def o.each(ary) ary << yield ary << yield ary << yield end ary = [] e = o.to_enum(:each, ary) e.next e.feed 1 e.next e.feed 2 e.rewind e.next e.next assert_equal([1,nil], ary) end assert 'Enumerator#rewind clear' do o = Object.new def o.each(ary) ary << yield ary << yield ary << yield end ary = [] e = o.to_enum :each, ary e.next e.feed 1 e.next e.feed 2 e.rewind e.next e.next assert_equal [1,nil], ary end assert 'Enumerator::Generator' do # note: Enumerator::Generator is a class just for internal g = Enumerator::Generator.new {|y| y << 1 << 2 << 3; :foo } g2 = g.dup a = [] assert_equal(:foo, g.each {|x| a << x }) assert_equal([1, 2, 3], a) a = [] assert_equal(:foo, g2.each {|x| a << x }) assert_equal([1, 2, 3], a) end assert 'Enumerator::Generator args' do g = Enumerator::Generator.new {|y, x| y << 1 << 2 << 3; x } a = [] assert_equal(:bar, g.each(:bar) {|x| a << x }) assert_equal([1, 2, 3], a) end assert 'Enumerator::Yielder' do # note: Enumerator::Yielder is a class just for internal a = [] y = Enumerator::Yielder.new {|x| a << x } assert_equal(y, y << 1 << 2 << 3) assert_equal([1, 2, 3], a) a = [] y = Enumerator::Yielder.new {|x| a << x } assert_equal([1], y.yield(1)) assert_equal([1, 2], y.yield(2)) assert_equal([1, 2, 3], y.yield(3)) assert_raise(LocalJumpError) { Enumerator::Yielder.new } end assert 'next after StopIteration' do a = [1] e = a.each assert_equal(1, e.next) assert_raise(StopIteration) { e.next } assert_raise(StopIteration) { e.next } e.rewind assert_equal(1, e.next) assert_raise(StopIteration) { e.next } assert_raise(StopIteration) { e.next } end assert 'gc' do assert_nothing_raised do 1.times do foo = [1,2,3].to_enum GC.start end GC.start end end assert 'nested iteration' do def (o = Object.new).each yield :ok1 yield [:ok2, :x].each.next end e = o.to_enum assert_equal :ok1, e.next assert_equal :ok2, e.next assert_raise(StopIteration) { e.next } end assert 'Kernel#to_enum' do e = nil assert_equal Enumerator, [].to_enum.class assert_nothing_raised { e = [].to_enum(:_not_implemented_) } assert_raise(NoMethodError) { e.first } end assert 'modifying existing methods' do assert_equal Enumerator, loop.class e = 3.times i = 0 loop_ret = loop { assert_equal i, e.next i += 1 } end assert 'Integer#times' do a = 3 b = a.times c = [] b.with_object(c) do |i, obj| obj << i end assert_equal 3, a assert_equal Enumerator, b.class assert_equal [0,1,2], c end assert 'Enumerable#each_with_index' do assert_equal [['a',0],['b',1],['c',2]], ['a','b','c'].each_with_index.to_a end assert 'Enumerable#map' do a = [1,2,3] b = a.map c = b.with_index do |i, index| [i*i, index*index] end assert_equal [1,2,3], a assert_equal [[1,0],[4,1],[9,4]], c end assert 'Enumerable#find_all' do assert_equal [[3,4]], [[1,2],[3,4],[5,6]].find_all.each{ |i| i[1] == 4 } end assert 'Array#each_index' do a = [1,2,3] b = a.each_index c = [] b.with_index do |index1,index2| c << [index1+2,index2+5] end assert_equal [1,2,3], a assert_equal [[2,5],[3,6],[4,7]], c end assert 'Array#map!' do a = [1,2,3] b = a.map! b.with_index do |i, index| [i*i, index*index] end assert_equal [[1,0],[4,1],[9,4]], a end assert 'Hash#each' do a = {a:1,b:2} b = a.each c = [] b.each do |k,v| c << [k,v] end assert_equal [[:a,1], [:b,2]], c.sort end assert 'Hash#each_key' do assert_equal [:a,:b], {a:1,b:2}.each_key.to_a.sort end assert 'Hash#each_value' do assert_equal [1,2], {a:1,b:2}.each_value.to_a.sort end assert 'Hash#select' do h = {1=>2,3=>4,5=>6} hret = h.select.with_index {|a,_b| a[1] == 4} assert_equal({3=>4}, hret) assert_equal({1=>2,3=>4,5=>6}, h) end assert 'Hash#select!' do h = {1=>2,3=>4,5=>6} hret = h.select!.with_index {|a,_b| a[1] == 4} assert_equal h, hret assert_equal({3=>4}, h) end assert 'Hash#reject' do h = {1=>2,3=>4,5=>6} hret = h.reject.with_index {|a,_b| a[1] == 4} assert_equal({1=>2,5=>6}, hret) assert_equal({1=>2,3=>4,5=>6}, h) end assert 'Hash#reject!' do h = {1=>2,3=>4,5=>6} hret = h.reject!.with_index {|a,_b| a[1] == 4} assert_equal h, hret assert_equal({1=>2,5=>6}, h) end assert 'Range#each' do a = (1..5) b = a.each c = [] b.each do |i| c << i end assert_equal [1,2,3,4,5], c end assert 'Enumerable#zip' do assert_equal [[1, 10], [2, 11], [3, 12]], [1,2,3].zip(10..) ret = [] assert_equal nil, [1,2,3].zip(10..) { |i| ret << i } assert_equal [[1, 10], [2, 11], [3, 12]], ret assert_raise(TypeError) { [1].zip(1) } end assert 'Enumerator.produce' do assert_raise(ArgumentError) { Enumerator.produce } # Without initial object passed_args = [] enum = Enumerator.produce {|obj| passed_args << obj; (obj || 0).succ } assert_equal Enumerator, enum.class assert_take [1, 2, 3], enum assert_equal [nil, 1, 2], passed_args # With initial object passed_args = [] enum = Enumerator.produce(1) {|obj| passed_args << obj; obj.succ } assert_take [1, 2, 3], enum assert_equal [1, 2], passed_args # Raising StopIteration words = %w[The quick brown fox jumps over the lazy dog] enum = Enumerator.produce { words.shift or raise StopIteration } assert_equal %w[The quick brown fox jumps over the lazy dog], enum.to_a # Raising StopIteration object = [[[["abc", "def"], "ghi", "jkl"], "mno", "pqr"], "stuv", "wxyz"] enum = Enumerator.produce(object) {|obj| obj.respond_to?(:first) or raise StopIteration obj.first } assert_nothing_raised { assert_equal [ [[[["abc", "def"], "ghi", "jkl"], "mno", "pqr"], "stuv", "wxyz"], [[["abc", "def"], "ghi", "jkl"], "mno", "pqr"], [["abc", "def"], "ghi", "jkl"], ["abc", "def"], "abc", ], enum.to_a } end assert("Enumerable#chunk") do chunk = [1, 2, 3, 1, 2].chunk assert_equal Enumerator, chunk.class result = chunk.with_index { |elt, i| elt - i }.to_a assert_equal [[1, [1, 2, 3]], [-2, [1, 2]]], result assert_equal Enumerator, [].chunk {}.class e = [1, 2, 3] recorded = [] e.chunk { |x| recorded << x }.to_a assert_equal [1, 2, 3], recorded e = [1, 2, 3, 2, 3, 2, 1] result = e.chunk { |x| x < 3 && 1 || 0 }.to_a assert_equal [[1, [1, 2]], [0, [3]], [1, [2]], [0, [3]], [1, [2, 1]]], result e = [1, 2, 3] assert_equal [[1, 2], [3]], e.chunk { |x| x > 2 }.map(&:last) e = [1, 2, 3, 2, 1] result = e.chunk { |x| x < 2 && :_alone }.to_a assert_equal [[:_alone, [1]], [false, [2, 3, 2]], [:_alone, [1]]], result e = [[1, 2]] inner_value = [] e.chunk { |*x| inner_value << x }.to_a assert_equal [[[1, 2]]], inner_value e = [1, 2, 3, 3, 2, 1] result = e.chunk { |x| x == 2 ? :_separator : 1 }.to_a assert_equal [[1, [1]], [1, [3, 3]], [1, [1]]], result e = [1, 2, 3, 2, 1] result = e.chunk { |x| x == 2 ? nil : 1 }.to_a assert_equal [[1, [1]], [1, [3]], [1, [1]]], result e = [1, 2, 3, 2, 1] assert_raise(RuntimeError) { e.chunk { |x| :_arbitrary }.to_a } e = [1, 2, 3] assert_raise(ArgumentError) { e.chunk(1) {} } e = [1, 2, 3, 2, 1] enum = e.chunk { |x| true } assert_nil enum.size end assert("Enumerable#chunk_while") do a = [1,2,4,9,10,11,12,15,16,19,20,21] b = a.chunk_while {|i, j| i+1 == j } assert_equal [[1, 2], [4], [9, 10, 11, 12], [15, 16], [19, 20, 21]], b.to_a c = b.map {|a| a.length < 3 ? a : "#{a.first}-#{a.last}" } assert_equal [[1, 2], [4], "9-12", [15, 16], "19-21"], c a = [0, 9, 2, 2, 3, 2, 7, 5, 9, 5] assert_equal [[0], [9], [2], [2], [3], [2], [7], [5], [9], [5]], a.chunk_while {|i, j| false }.to_a assert_equal [[0, 9], [2, 2, 3], [2, 7], [5, 9], [5]], a.chunk_while {|i, j| i <= j }.to_a a = [7, 5, 9, 2, 0, 7, 9, 4, 2, 0] assert_equal [[7, 5, 9], [2, 0], [7, 9], [4, 2, 0]], a.chunk_while {|i, j| i % 2 == j % 2 }.to_a end nghttp2-1.68.0/third-party/mruby/mrbgems/PaxHeaders/mruby-enum-ext0000644000000000000000000000013215077107334022143 xustar0030 mtime=1761382108.938300849 30 atime=1761382109.799298361 30 ctime=1761382108.938300849 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-ext/0000755000175100017510000000000015077107334022610 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-ext/PaxHeaders/mrbgem.rake0000644000000000000000000000013215077107276024342 xustar0030 mtime=1761382078.116420584 30 atime=1761382080.138411334 30 ctime=1761382108.939300847 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-ext/mrbgem.rake0000644000175100017510000000025015077107276024727 0ustar00runnerrunnerMRuby::Gem::Specification.new('mruby-enum-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Enumerable module extension' end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-ext/PaxHeaders/mrblib0000644000000000000000000000013215077107334023412 xustar0030 mtime=1761382108.941300841 30 atime=1761382109.799298361 30 ctime=1761382108.941300841 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-ext/mrblib/0000755000175100017510000000000015077107334024057 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-ext/mrblib/PaxHeaders/enum.rb0000644000000000000000000000013215077107276024765 xustar0030 mtime=1761382078.117420579 30 atime=1761382080.138411334 30 ctime=1761382108.941300841 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-ext/mrblib/enum.rb0000644000175100017510000006012415077107276025360 0ustar00runnerrunner## # Enumerable # module Enumerable ## # call-seq: # enum.drop(n) -> array # # Drops first n elements from enum, and returns rest elements # in an array. # # a = [1, 2, 3, 4, 5, 0] # a.drop(3) #=> [4, 5, 0] def drop(n) n = n.__to_int raise ArgumentError, "attempt to drop negative size" if n < 0 ary = [] self.each {|*val| n == 0 ? ary << val.__svalue : n -= 1 } ary end ## # call-seq: # enum.drop_while {|arr| block } -> array # enum.drop_while -> an_enumerator # # Drops elements up to, but not including, the first element for # which the block returns +nil+ or +false+ and returns an array # containing the remaining elements. # # If no block is given, an enumerator is returned instead. # # a = [1, 2, 3, 4, 5, 0] # a.drop_while {|i| i < 3 } #=> [3, 4, 5, 0] def drop_while(&block) return to_enum :drop_while unless block ary, state = [], false self.each do |*val| state = true if !state and !block.call(*val) ary << val.__svalue if state end ary end ## # call-seq: # enum.take(n) -> array # # Returns first n elements from enum. # # a = [1, 2, 3, 4, 5, 0] # a.take(3) #=> [1, 2, 3] def take(n) n = n.__to_int i = n.to_i raise ArgumentError, "attempt to take negative size" if i < 0 ary = [] return ary if i == 0 self.each do |*val| ary << val.__svalue i -= 1 break if i == 0 end ary end ## # call-seq: # enum.take_while {|arr| block } -> array # enum.take_while -> an_enumerator # # Passes elements to the block until the block returns +nil+ or +false+, # then stops iterating and returns an array of all prior elements. # # If no block is given, an enumerator is returned instead. # # a = [1, 2, 3, 4, 5, 0] # a.take_while {|i| i < 3 } #=> [1, 2] # def take_while(&block) return to_enum :take_while unless block ary = [] self.each do |*val| return ary unless block.call(*val) ary << val.__svalue end ary end ## # Iterates the given block for each array of consecutive # elements. # # @return [nil] # # @example # (1..10).each_cons(3) {|a| p a} # # outputs below # [1, 2, 3] # [2, 3, 4] # [3, 4, 5] # [4, 5, 6] # [5, 6, 7] # [6, 7, 8] # [7, 8, 9] # [8, 9, 10] def each_cons(n, &block) n = n.__to_int raise ArgumentError, "invalid size" if n <= 0 return to_enum(:each_cons,n) unless block ary = [] n = n.to_i self.each do |*val| ary.shift if ary.size == n ary << val.__svalue block.call(ary.dup) if ary.size == n end nil end ## # Iterates the given block for each slice of elements. # # @return [nil] # # @example # (1..10).each_slice(3) {|a| p a} # # outputs below # [1, 2, 3] # [4, 5, 6] # [7, 8, 9] # [10] def each_slice(n, &block) n = n.__to_int raise ArgumentError, "invalid slice size" if n <= 0 return to_enum(:each_slice,n) unless block ary = [] n = n.to_i self.each do |*val| ary << val.__svalue if ary.size == n block.call(ary) ary = [] end end block.call(ary) unless ary.empty? nil end ## # call-seq: # enum.group_by {| obj | block } -> a_hash # enum.group_by -> an_enumerator # # Returns a hash, which keys are evaluated result from the # block, and values are arrays of elements in enum # corresponding to the key. # # (1..6).group_by {|i| i%3} #=> {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]} # def group_by(&block) return to_enum :group_by unless block h = {} self.each do |*val| key = block.call(*val) sv = val.__svalue h.key?(key) ? (h[key] << sv) : (h[key] = [sv]) end h end ## # call-seq: # enum.sort_by { |obj| block } -> array # enum.sort_by -> an_enumerator # # Sorts enum using a set of keys generated by mapping the # values in enum through the given block. # # If no block is given, an enumerator is returned instead. def sort_by(&block) return to_enum :sort_by unless block self.to_a.sort_by(&block) end ## # call-seq: # enum.first -> obj or nil # enum.first(n) -> an_array # # Returns the first element, or the first +n+ elements, of the enumerable. # If the enumerable is empty, the first form returns nil, and the # second form returns an empty array. def first(*args) case args.length when 0 self.each do |*val| return val.__svalue end return nil when 1 i = args[0].__to_int raise ArgumentError, "attempt to take negative size" if i < 0 ary = [] return ary if i == 0 self.each do |*val| ary << val.__svalue i -= 1 break if i == 0 end ary else raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 0..1)" end end ## # call-seq: # enum.count -> int # enum.count(item) -> int # enum.count { |obj| block } -> int # # Returns the number of items in +enum+ through enumeration. # If an argument is given, the number of items in +enum+ that # are equal to +item+ are counted. If a block is given, it # counts the number of elements yielding a true value. def count(v=NONE, &block) count = 0 if block self.each do |*val| count += 1 if block.call(*val) end else if NONE.equal?(v) self.each { count += 1 } else self.each do |*val| count += 1 if val.__svalue == v end end end count end ## # call-seq: # enum.flat_map { |obj| block } -> array # enum.collect_concat { |obj| block } -> array # enum.flat_map -> an_enumerator # enum.collect_concat -> an_enumerator # # Returns a new array with the concatenated results of running # block once for every element in enum. # # If no block is given, an enumerator is returned instead. # # [1, 2, 3, 4].flat_map { |e| [e, -e] } #=> [1, -1, 2, -2, 3, -3, 4, -4] # [[1, 2], [3, 4]].flat_map { |e| e + [100] } #=> [1, 2, 100, 3, 4, 100] def flat_map(&block) return to_enum :flat_map unless block ary = [] self.each do |*e| e2 = block.call(*e) if e2.respond_to? :each e2.each {|e3| ary.push(e3) } else ary.push(e2) end end ary end alias collect_concat flat_map ## # call-seq: # enum.max_by {|obj| block } -> obj # enum.max_by -> an_enumerator # # Returns the object in enum that gives the maximum # value from the given block. # # If no block is given, an enumerator is returned instead. # # %w[albatross dog horse].max_by {|x| x.length } #=> "albatross" def max_by(&block) return to_enum :max_by unless block first = true max = nil max_cmp = nil self.each do |*val| if first max = val.__svalue max_cmp = block.call(*val) first = false else if (cmp = block.call(*val)) > max_cmp max = val.__svalue max_cmp = cmp end end end max end ## # call-seq: # enum.min_by {|obj| block } -> obj # enum.min_by -> an_enumerator # # Returns the object in enum that gives the minimum # value from the given block. # # If no block is given, an enumerator is returned instead. # # %w[albatross dog horse].min_by {|x| x.length } #=> "dog" def min_by(&block) return to_enum :min_by unless block first = true min = nil min_cmp = nil self.each do |*val| if first min = val.__svalue min_cmp = block.call(*val) first = false else if (cmp = block.call(*val)) < min_cmp min = val.__svalue min_cmp = cmp end end end min end ## # call-seq: # enum.minmax -> [min, max] # enum.minmax { |a, b| block } -> [min, max] # # Returns two elements array which contains the minimum and the # maximum value in the enumerable. The first form assumes all # objects implement Comparable; the second uses the # block to return a <=> b. # # a = %w(albatross dog horse) # a.minmax #=> ["albatross", "horse"] # a.minmax { |a, b| a.length <=> b.length } #=> ["dog", "albatross"] def minmax(&block) max = nil min = nil first = true self.each do |*val| if first val = val.__svalue max = val min = val first = false else val = val.__svalue if block max = val if block.call(val, max) > 0 min = val if block.call(val, min) < 0 else max = val if (val <=> max) > 0 min = val if (val <=> min) < 0 end end end [min, max] end ## # call-seq: # enum.minmax_by { |obj| block } -> [min, max] # enum.minmax_by -> an_enumerator # # Returns a two element array containing the objects in # enum that correspond to the minimum and maximum values respectively # from the given block. # # If no block is given, an enumerator is returned instead. # # %w(albatross dog horse).minmax_by { |x| x.length } #=> ["dog", "albatross"] def minmax_by(&block) return to_enum :minmax_by unless block max = nil max_cmp = nil min = nil min_cmp = nil first = true self.each do |*val| if first max = min = val.__svalue max_cmp = min_cmp = block.call(*val) first = false else if (cmp = block.call(*val)) > max_cmp max = val.__svalue max_cmp = cmp end if (cmp = block.call(*val)) < min_cmp min = val.__svalue min_cmp = cmp end end end [min, max] end ## # call-seq: # enum.none? [{ |obj| block }] -> true or false # enum.none?(pattern) -> true or false # # Passes each element of the collection to the given block. The method # returns true if the block never returns true # for all elements. If the block is not given, none? will return # true only if none of the collection members is true. # # If a pattern is supplied instead, the method returns whether # pattern === element for none of the collection members. # # %w(ant bear cat).none? { |word| word.length == 5 } #=> true # %w(ant bear cat).none? { |word| word.length >= 4 } #=> false # %w{ant bear cat}.none?(/d/) #=> true # [1, 3.14, 42].none?(Float) #=> false # [].none? #=> true # [nil, false].none? #=> true # [nil, true].none? #=> false def none?(pat=NONE, &block) if !NONE.equal?(pat) self.each do |*val| return false if pat === val.__svalue end elsif block self.each do |*val| return false if block.call(*val) end else self.each do |*val| return false if val.__svalue end end true end ## # call-seq: # enum.one? [{ |obj| block }] -> true or false # enum.one?(pattern) -> true or false # # Passes each element of the collection to the given block. The method # returns true if the block returns true # exactly once. If the block is not given, one? will return # true only if exactly one of the collection members is # true. # # If a pattern is supplied instead, the method returns whether # pattern === element for exactly one collection member. # # %w(ant bear cat).one? { |word| word.length == 4 } #=> true # %w(ant bear cat).one? { |word| word.length > 4 } #=> false # %w(ant bear cat).one? { |word| word.length < 4 } #=> false # %w{ant bear cat}.one?(/t/) #=> false # [nil, true, 99].one? #=> false # [nil, true, false].one? #=> true # [ nil, true, 99 ].one?(Integer) #=> true # [].one? #=> false def one?(pat=NONE, &block) count = 0 if !NONE.equal?(pat) self.each do |*val| count += 1 if pat === val.__svalue return false if count > 1 end elsif block self.each do |*val| count += 1 if block.call(*val) return false if count > 1 end else self.each do |*val| count += 1 if val.__svalue return false if count > 1 end end count == 1 ? true : false end # ISO 15.3.2.2.1 # call-seq: # enum.all? [{ |obj| block } ] -> true or false # enum.all?(pattern) -> true or false # # Passes each element of the collection to the given block. The method # returns true if the block never returns # false or nil. If the block is not given, # Ruby adds an implicit block of { |obj| obj } which will # cause #all? to return +true+ when none of the collection members are # +false+ or +nil+. # # If a pattern is supplied instead, the method returns whether # pattern === element for every collection member. # # %w[ant bear cat].all? { |word| word.length >= 3 } #=> true # %w[ant bear cat].all? { |word| word.length >= 4 } #=> false # %w[ant bear cat].all?(/t/) #=> false # [1, 2i, 3.14].all?(Numeric) #=> true # [nil, true, 99].all? #=> false # def all?(pat=NONE, &block) if !NONE.equal?(pat) self.each{|*val| return false unless pat === val.__svalue} elsif block self.each{|*val| return false unless block.call(*val)} else self.each{|*val| return false unless val.__svalue} end true end # ISO 15.3.2.2.2 # call-seq: # enum.any? [{ |obj| block }] -> true or false # enum.any?(pattern) -> true or false # # Passes each element of the collection to the given block. The method # returns true if the block ever returns a value other # than false or nil. If the block is not # given, Ruby adds an implicit block of { |obj| obj } that # will cause #any? to return +true+ if at least one of the collection # members is not +false+ or +nil+. # # If a pattern is supplied instead, the method returns whether # pattern === element for any collection member. # # %w[ant bear cat].any? { |word| word.length >= 3 } #=> true # %w[ant bear cat].any? { |word| word.length >= 4 } #=> true # %w[ant bear cat].any?(/d/) #=> false # [nil, true, 99].any?(Integer) #=> true # [nil, true, 99].any? #=> true # [].any? #=> false # def any?(pat=NONE, &block) if !NONE.equal?(pat) self.each{|*val| return true if pat === val.__svalue} elsif block self.each{|*val| return true if block.call(*val)} else self.each{|*val| return true if val.__svalue} end false end ## # call-seq: # enum.each_with_object(obj) { |(*args), memo_obj| ... } -> obj # enum.each_with_object(obj) -> an_enumerator # # Iterates the given block for each element with an arbitrary # object given, and returns the initially given object. # # If no block is given, returns an enumerator. # # (1..10).each_with_object([]) { |i, a| a << i*2 } # #=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] # def each_with_object(obj, &block) return to_enum(:each_with_object, obj) unless block self.each {|*val| block.call(val.__svalue, obj) } obj end ## # call-seq: # enum.reverse_each { |item| block } -> enum # enum.reverse_each -> an_enumerator # # Builds a temporary array and traverses that array in reverse order. # # If no block is given, an enumerator is returned instead. # # (1..3).reverse_each { |v| p v } # # produces: # # 3 # 2 # 1 # def reverse_each(&block) return to_enum :reverse_each unless block ary = self.to_a i = ary.size - 1 while i>=0 block.call(ary[i]) i -= 1 end self end ## # call-seq: # enum.cycle(n=nil) { |obj| block } -> nil # enum.cycle(n=nil) -> an_enumerator # # Calls block for each element of enum repeatedly _n_ # times or forever if none or +nil+ is given. If a non-positive # number is given or the collection is empty, does nothing. Returns # +nil+ if the loop has finished without getting interrupted. # # Enumerable#cycle saves elements in an internal array so changes # to enum after the first pass have no effect. # # If no block is given, an enumerator is returned instead. # # a = ["a", "b", "c"] # a.cycle { |x| puts x } # print, a, b, c, a, b, c,.. forever. # a.cycle(2) { |x| puts x } # print, a, b, c, a, b, c. # def cycle(nv = nil, &block) return to_enum(:cycle, nv) unless block n = nil if nv.nil? n = -1 else n = nv.__to_int return nil if n <= 0 end ary = [] each do |*i| ary.push(i) yield(*i) end return nil if ary.empty? while n < 0 || 0 < (n -= 1) ary.each do |i| yield(*i) end end nil end ## # call-seq: # enum.find_index(value) -> int or nil # enum.find_index { |obj| block } -> int or nil # enum.find_index -> an_enumerator # # Compares each entry in enum with value or passes # to block. Returns the index for the first for which the # evaluated value is non-false. If no object matches, returns # nil # # If neither block nor argument is given, an enumerator is returned instead. # # (1..10).find_index { |i| i % 5 == 0 and i % 7 == 0 } #=> nil # (1..100).find_index { |i| i % 5 == 0 and i % 7 == 0 } #=> 34 # (1..100).find_index(50) #=> 49 # def find_index(val=NONE, &block) return to_enum(:find_index, val) if !block && NONE.equal?(val) idx = 0 if block self.each do |*e| return idx if block.call(*e) idx += 1 end else self.each do |*e| return idx if e.__svalue == val idx += 1 end end nil end ## # call-seq: # enum.zip(arg, ...) -> an_array_of_array # enum.zip(arg, ...) { |arr| block } -> nil # # Takes one element from enum and merges corresponding # elements from each args. This generates a sequence of # n-element arrays, where n is one more than the # count of arguments. The length of the resulting sequence will be # enum#size. If the size of any argument is less than # enum#size, nil values are supplied. If # a block is given, it is invoked for each output array, otherwise # an array of arrays is returned. # # a = [ 4, 5, 6 ] # b = [ 7, 8, 9 ] # # a.zip(b) #=> [[4, 7], [5, 8], [6, 9]] # [1, 2, 3].zip(a, b) #=> [[1, 4, 7], [2, 5, 8], [3, 6, 9]] # [1, 2].zip(a, b) #=> [[1, 4, 7], [2, 5, 8]] # a.zip([1, 2], [8]) #=> [[4, 1, 8], [5, 2, nil], [6, nil, nil]] # # c = [] # a.zip(b) { |x, y| c << x + y } #=> nil # c #=> [11, 13, 15] # def zip(*arg, &block) result = block ? nil : [] arg = arg.map do |a| unless a.respond_to?(:to_a) raise TypeError, "wrong argument type #{a.class} (must respond to :to_a)" end a.to_a end i = 0 self.each do |*val| a = [] a.push(val.__svalue) idx = 0 while idx < arg.size a.push(arg[idx][i]) idx += 1 end i += 1 if result.nil? block.call(a) else result.push(a) end end result end ## # call-seq: # enum.to_h -> hash # # Returns the result of interpreting enum as a list of # [key, value] pairs. # # %i[hello world].each_with_index.to_h # # => {:hello => 0, :world => 1} # def to_h(&blk) h = {} if blk self.each do |v| v = blk.call(v) raise TypeError, "wrong element type #{v.class} (expected Array)" unless v.is_a? Array raise ArgumentError, "element has wrong array length (expected 2, was #{v.size})" if v.size != 2 h[v[0]] = v[1] end else self.each do |*v| v = v.__svalue raise TypeError, "wrong element type #{v.class} (expected Array)" unless v.is_a? Array raise ArgumentError, "element has wrong array length (expected 2, was #{v.size})" if v.size != 2 h[v[0]] = v[1] end end h end def uniq(&block) hash = {} if block self.each do|*v| v = v.__svalue hash[block.call(v)] ||= v end else self.each do|*v| v = v.__svalue hash[v] ||= v end end hash.values end def filter_map(&blk) return to_enum(:filter_map) unless blk ary = [] self.each do |*x| x = blk.call(*x) ary.push x if x end ary end alias filter select def grep_v(pattern, &block) ary = [] self.each{|*val| sv = val.__svalue unless pattern === sv ary.push((block)? block.call(*val): sv) end } ary end ## # call-seq: # enum.tally -> a_hash # # Tallys the collection. Returns a hash where the keys are the # elements and the values are numbers of elements in the collection # that correspond to the key. # # ["a", "b", "c", "b"].tally #=> {"a"=>1, "b"=>2, "c"=>1} def tally hash = {} self.each do |*x| x = x.__svalue hash[x] = (hash[x]||0)+1 end hash end ## # call-seq: # enum.sum(count=1) -> numeric # enum.sum(count=1){...} -> numeric # # Returns the sum of elements. For example, [e1, e2, e3].sum # returns e1 + e2 + e3. If a block is given, each element is # processed by the block, e.g [e1, e2, e3].sum{_1.m} gives # e1.m + e2.m + e3.m. # def sum(init=0,&block) result=init if block self.each do |*e| result += block.call(*e) end else self.each do |*e| result += e.__svalue end end result end ## # call-seq: # enum.each_entry { |obj| block } -> enum # enum.each_entry -> an_enumerator # # Calls block once for each element in self, passing that # element as a parameter, converting multiple values from yield to an # array. # # If no block is given, an enumerator is returned instead. # # class Foo # include Enumerable # def each # yield 1 # yield 1, 2 # yield # end # end # Foo.new.each_entry{ |o| p o } # # produces: # # 1 # [1, 2] # nil # def each_entry(*args, &blk) return to_enum(:each_entry) unless blk self.each do |*a| yield a.__svalue end return self end end class Array def sort_by(&block) return to_enum :sort_by unless block ary = [] self.each_with_index{|e, i| ary.push([block.call(e), i]) } if ary.size > 1 ary.sort! end ary.collect!{|e,i| self[i]} end def sort_by!(&block) self.replace(self.sort_by(&block)) end end nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-ext/PaxHeaders/test0000644000000000000000000000013215077107334023122 xustar0030 mtime=1761382108.940300844 30 atime=1761382109.799298361 30 ctime=1761382108.940300844 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-ext/test/0000755000175100017510000000000015077107334023567 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-ext/test/PaxHeaders/enum.rb0000644000000000000000000000013215077107276024475 xustar0030 mtime=1761382078.117420579 30 atime=1761382080.138411334 30 ctime=1761382108.940300844 nghttp2-1.68.0/third-party/mruby/mrbgems/mruby-enum-ext/test/enum.rb0000644000175100017510000001310515077107276025065 0ustar00runnerrunner## # Enumerable(Ext) Test assert("Enumerable#drop") do a = [1, 2, 3, 4, 5, 0] assert_equal [4, 5, 0], a.drop(3) assert_equal [], a.drop(6) end assert("Enumerable#drop_while") do a = [1, 2, 3, 4, 5, 0] assert_equal [3, 4, 5, 0], a.drop_while {|i| i < 3 } end assert("Enumerable#take") do a = [1, 2, 3, 4, 5, 0] assert_equal [1, 2, 3], a.take(3) end assert("Enumerable#take_while") do a = [1, 2, 3, 4, 5, 0] assert_equal [1, 2], a.take_while {|i| i < 3} end assert("Enumerable#each_cons") do a = [] b = (1..5).each_cons(3){|e| a << e} assert_equal [[1, 2, 3], [2, 3, 4], [3, 4, 5]], a assert_equal nil, b end assert("Enumerable#each_slice") do a = [] b = (1..10).each_slice(3){|e| a << e} assert_equal [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]], a assert_equal nil, b end assert("Enumerable#group_by") do r = (1..6).group_by {|i| i % 3 } assert_equal [3, 6], r[0] assert_equal [1, 4], r[1] assert_equal [2, 5], r[2] end assert("Enumerable#sort_by") do assert_equal ["car", "train", "bicycle"], %w{car bicycle train}.sort_by {|e| e.length} end assert("Enumerable#first") do a = Object.new a.extend Enumerable def a.each yield 1 yield 2 yield 3 end assert_equal 1, a.first assert_equal [1, 2], a.first(2) assert_equal [1, 2, 3], a.first(10) a = Object.new a.extend Enumerable def a.each end assert_nil a.first end assert("Enumerable#count") do a = [1, 2, 4, 2] assert_equal 4, a.count assert_equal 2, a.count(2) assert_equal 3, a.count{|x| x % 2 == 0} end assert("Enumerable#flat_map") do assert_equal [1, 2, 3, 4], [1, 2, 3, 4].flat_map { |e| e } assert_equal [1, -1, 2, -2, 3, -3, 4, -4], [1, 2, 3, 4].flat_map { |e| [e, -e] } assert_equal [1, 2, 100, 3, 4, 100], [[1, 2], [3, 4]].flat_map { |e| e + [100] } end assert("Enumerable#max_by") do assert_equal "albatross", %w[albatross dog horse].max_by { |x| x.length } end assert("Enumerable#min_by") do assert_equal "dog", %w[albatross dog horse].min_by { |x| x.length } end assert("Enumerable#minmax") do a = %w(albatross dog horse) assert_equal ["albatross", "horse"], a.minmax assert_equal ["dog", "albatross"], a.minmax { |a, b| a.length <=> b.length } end assert("Enumerable#minmax_by") do assert_equal ["dog", "albatross"], %w(albatross dog horse).minmax_by { |x| x.length } end assert("Enumerable#none?") do assert_true %w(ant bear cat).none? { |word| word.length == 5 } assert_false %w(ant bear cat).none? { |word| word.length >= 4 } assert_true [].none? assert_true [nil, false].none? assert_false [nil, true].none? end assert("Enumerable#one?") do assert_true %w(ant bear cat).one? { |word| word.length == 4 } assert_false %w(ant bear cat).one? { |word| word.length > 4 } assert_false %w(ant bear cat).one? { |word| word.length < 4 } assert_false [nil, true, 99].one? assert_true [nil, true, false].one? assert_true [ nil, true, 99 ].one?(Integer) assert_false [].one? assert_true [nil, true, false].one?(NilClass) end assert("Enumerable#all? (enhancement)") do assert_false [1, 2, nil].all?(Integer) assert_true [1, 2, 3].all?(Numeric) end assert("Enumerable#any? (enhancement)") do assert_true [nil, true, 99].any?(Integer) assert_false [1, 2, 3].any?(Array) end assert("Enumerable#each_with_object") do assert_equal [2, 4, 6, 8, 10, 12, 14, 16, 18, 20], (1..10).each_with_object([]) { |i, a| a << i*2 } assert_raise(ArgumentError) { (1..10).each_with_object() { |i, a| a << i*2 } } end assert("Enumerable#reverse_each") do r = (1..3) a = [] assert_same r, r.reverse_each { |v| a << v } assert_equal [3, 2, 1], a end assert("Enumerable#cycle") do a = [] ["a", "b", "c"].cycle(2) { |v| a << v } assert_equal ["a", "b", "c", "a", "b", "c"], a assert_raise(TypeError) { ["a", "b", "c"].cycle("a") { |v| a << v } } empty = Class.new do include Enumerable def each end end assert_nil empty.new.cycle { break :nope } end assert("Enumerable#find_index") do assert_nil (1..10).find_index { |i| i % 5 == 0 and i % 7 == 0 } assert_equal 34, (1..100).find_index { |i| i % 5 == 0 and i % 7 == 0 } assert_equal 49 ,(1..100).find_index(50) end assert("Enumerable#zip") do a = [ 4, 5, 6 ] b = [ 7, 8, 9 ] assert_equal [[4, 7], [5, 8], [6, 9]], a.zip(b) assert_equal [[1, 4, 7], [2, 5, 8], [3, 6, 9]], [1, 2, 3].zip(a, b) assert_equal [[1, 4, 7], [2, 5, 8]], [1, 2].zip(a, b) assert_equal [[4, 1, 8], [5, 2, nil], [6, nil, nil]], a.zip([1, 2], [8]) ret = [] assert_equal nil, a.zip([1, 2], [8]) { |i| ret << i } assert_equal [[4, 1, 8], [5, 2, nil], [6, nil, nil]], ret assert_raise(TypeError) { [1].zip(1) } end assert("Enumerable#to_h") do c = Class.new { include Enumerable def each yield [1,2] yield [3,4] end } h0 = {1=>2, 3=>4} h = c.new.to_h assert_equal Hash, h.class assert_equal h0, h assert_equal({1=>4,3=>8}, c.new.to_h{|k,v|[k,v*2]}) end assert("Enumerable#filter_map") do assert_equal [4, 8, 12, 16, 20], (1..10).filter_map{|i| i * 2 if i%2==0} end assert("Enumerable#tally") do assert_equal({"a"=>1, "b"=>2, "c"=>1}, ["a", "b", "c", "b"].tally) end assert("Enumerable#grep_v") do a = [1, 2, 3, 4, 5, 0] assert_equal [1, 5, 0], a.grep_v(2..4) assert_equal [1, 2, 3, 4, 5, 0], a.grep_v(6..8) assert_equal [2, 4, 6, 8, 10], a.grep_v(0) {|v| v * 2} end assert("Enumerable#each_entry") do each_entry_test = Class.new { include Enumerable def each yield 1 yield 1, 2 yield end } e = each_entry_test.new a = [] e.each_entry {|v| a.push(v) } assert_equal 1, a[0] assert_equal [1,2], a[1] assert_equal nil, a[2] end nghttp2-1.68.0/third-party/mruby/PaxHeaders/README.md0000644000000000000000000000013115077107276017151 xustar0029 mtime=1761382078.09542068 30 atime=1761382080.118411425 30 ctime=1761382108.379302465 nghttp2-1.68.0/third-party/mruby/README.md0000644000175100017510000001217715077107276017552 0ustar00runnerrunner

The mruby programming language

mruby

GitHub Super-Linter
## What is mruby mruby is the lightweight implementation of the Ruby language complying to (part of) the [ISO standard][ISO-standard] with more recent features provided by Ruby 3.x. Also, its syntax is Ruby 3.x compatible except for pattern matching. You can link and embed mruby within your application. The "mruby" interpreter program and the interactive "mirb" shell are provided as examples. You can also compile Ruby programs into compiled byte code using the "mrbc" compiler. All these tools are located in the "bin" directory. "mrbc" can also generate compiled byte code in a C source file. See the "mrbtest" program under the "test" directory for an example. This achievement was sponsored by the Regional Innovation Creation R&D Programs of the Ministry of Economy, Trade and Industry of Japan. ## How to get mruby To get mruby, you can download the stable version 3.4.0 from the official mruby GitHub repository or clone the trunk of the mruby source tree with the "git clone" command. You can also install and compile mruby using [ruby-install](https://github.com/postmodern/ruby-install), [ruby-build](https://github.com/rbenv/ruby-build) or [rvm](https://github.com/rvm/rvm). The latest development version of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/zipball/master](https://github.com/mruby/mruby/zipball/master) The trunk of the mruby source tree can be checked out with the following command: ```console $ git clone https://github.com/mruby/mruby.git ``` ## mruby homepage The URL of the mruby homepage is: . ## Mailing list We don't have a mailing list, but you can use [GitHub issues](https://github.com/mruby/mruby/issues). ## How to compile, test, and install (mruby and gems) For the simplest case, type ```console rake all test ``` See the [compile.md](doc/guides/compile.md) file for the detail. ## Building documentation There are two sets of documentation in mruby: the mruby API (generated by YARD) and C API (Doxygen and Graphviz) To build both of them, simply go ```console rake doc ``` You can also view them in your browser ```console rake view_api rake view_capi ``` ## How to customize mruby (mrbgems) mruby contains a package manager called "mrbgems" that you can use to create extensions in C and/or Ruby. For a guide on how to use mrbgems, consult the [mrbgems.md](doc/guides/mrbgems.md) file, and for example code, refer to the [examples/mrbgems/](examples/mrbgems) folder. ## Index of Document - [About the Limitations of mruby](doc/limitations.md) - [About the Compile](doc/guides/compile.md) - [About the Debugger with the `mrdb` Command](doc/guides/debugger.md) - [About GC Arena](doc/guides/gc-arena-howto.md) - [About the mruby directory structure](doc/guides/hier.md) - [About Linking with `libmruby`](doc/guides/link.md) - [About Memory Allocator Customization](doc/guides/memory.md) - [About Build-time Configurations](doc/guides/mrbconf.md) - [About the Build-time Library Manager](doc/guides/mrbgems.md) - [About the Symbols](doc/guides/symbol.md) - [Internal Implementation / About Value Boxing](doc/internal/boxing.md) - [Internal Implementation / About mruby Virtual Machine Instructions](doc/internal/opcode.md) ## License mruby is released under the [MIT License](LICENSE). ## Note for License mruby has chosen a MIT License due to its permissive license allowing developers to target various environments such as embedded systems. However, the license requires the display of the copyright notice and license information in manuals for instance. Doing so for big projects can be complicated or troublesome. This is why mruby has decided to display "mruby developers" as the copyright name to make it simple conventionally. In the future, mruby might ask you to distribute your new code (that you will commit,) under the MIT License as a member of "mruby developers" but contributors will keep their copyright. (We did not intend for contributors to transfer or waive their copyrights, actual copyright holder name (contributors) will be listed in the [AUTHORS](AUTHORS) file.) Please ask us if you want to distribute your code under another license. ## How to Contribute To contribute to mruby, please refer to the [contribution guidelines][contribution-guidelines] and send a pull request to the [mruby GitHub repository](https://github.com/mruby/mruby). By contributing, you grant non-exclusive rights to your code under the MIT License. [ISO-standard]: https://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=59579 [contribution-guidelines]: CONTRIBUTING.md nghttp2-1.68.0/third-party/mruby/PaxHeaders/tools0000644000000000000000000000013015077107334016747 xustar0029 mtime=1761382108.20830296 30 atime=1761382109.799298361 29 ctime=1761382108.20830296 nghttp2-1.68.0/third-party/mruby/tools/0000755000175100017510000000000015077107334017416 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/tools/PaxHeaders/lrama0000644000000000000000000000013215077107334020045 xustar0030 mtime=1761382108.342302572 30 atime=1761382109.799298361 30 ctime=1761382108.342302572 nghttp2-1.68.0/third-party/mruby/tools/lrama/0000755000175100017510000000000015077107334020512 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/tools/lrama/PaxHeaders/template0000644000000000000000000000013015077107334021656 xustar0029 mtime=1761382108.20830296 30 atime=1761382109.799298361 29 ctime=1761382108.20830296 nghttp2-1.68.0/third-party/mruby/tools/lrama/template/0000755000175100017510000000000015077107334022325 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/tools/lrama/template/PaxHeaders/bison0000644000000000000000000000013215077107334022772 xustar0030 mtime=1761382108.339302581 30 atime=1761382109.799298361 30 ctime=1761382108.339302581 nghttp2-1.68.0/third-party/mruby/tools/lrama/template/bison/0000755000175100017510000000000015077107334023437 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/tools/lrama/template/bison/PaxHeaders/_yacc.h0000644000000000000000000000013215077107276024303 xustar0030 mtime=1761382078.145420451 30 atime=1761382080.167411201 30 ctime=1761382108.339302581 nghttp2-1.68.0/third-party/mruby/tools/lrama/template/bison/_yacc.h0000644000175100017510000000335115077107276024675 0ustar00runnerrunner<%# b4_shared_declarations -%> <%-# b4_cpp_guard_open([b4_spec_mapped_header_file]) -%> <%- if output.spec_mapped_header_file -%> #ifndef <%= output.b4_cpp_guard__b4_spec_mapped_header_file %> # define <%= output.b4_cpp_guard__b4_spec_mapped_header_file %> <%- end -%> <%-# b4_declare_yydebug & b4_YYDEBUG_define -%> /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif #if YYDEBUG && !defined(yydebug) extern int yydebug; #endif <%= output.percent_code("requires") %> <%-# b4_token_enums_defines -%> /* Token kinds. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE enum yytokentype { <%= output.token_enums -%> }; typedef enum yytokentype yytoken_kind_t; #endif <%-# b4_declare_yylstype -%> <%-# b4_value_type_define -%> /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { #line <%= output.grammar.union.lineno %> "<%= output.grammar_file_path %>" <%= output.grammar.union.braces_less_code %> #line [@oline@] [@ofile@] }; typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif <%-# b4_location_type_define -%> /* Location type. */ #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED typedef struct YYLTYPE YYLTYPE; struct YYLTYPE { int first_line; int first_column; int last_line; int last_column; }; # define YYLTYPE_IS_DECLARED 1 # define YYLTYPE_IS_TRIVIAL 1 #endif <%-# b4_declare_yyerror_and_yylex. Not supported -%> <%-# b4_declare_yyparse -%> int yyparse (<%= output.parse_param %>); <%= output.percent_code("provides") %> <%-# b4_cpp_guard_close([b4_spec_mapped_header_file]) -%> <%- if output.spec_mapped_header_file -%> #endif /* !<%= output.b4_cpp_guard__b4_spec_mapped_header_file %> */ <%- end -%> nghttp2-1.68.0/third-party/mruby/tools/lrama/template/bison/PaxHeaders/yacc.h0000644000000000000000000000013215077107276024144 xustar0030 mtime=1761382078.145420451 30 atime=1761382080.168411196 30 ctime=1761382108.335302592 nghttp2-1.68.0/third-party/mruby/tools/lrama/template/bison/yacc.h0000644000175100017510000000347615077107276024546 0ustar00runnerrunner<%# b4_generated_by -%> /* A Bison parser, made by Lrama <%= Lrama::VERSION %>. */ <%# b4_copyright -%> /* Bison interface for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ <%# b4_disclaimer -%> /* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, especially those whose name start with YY_ or yy_. They are private implementation details that can be changed or removed. */ <%= output.render_partial("bison/_yacc.h") %> nghttp2-1.68.0/third-party/mruby/tools/lrama/template/bison/PaxHeaders/yacc.c0000644000000000000000000000013215077107276024137 xustar0030 mtime=1761382078.145420451 30 atime=1761382080.167411201 30 ctime=1761382108.337302587 nghttp2-1.68.0/third-party/mruby/tools/lrama/template/bison/yacc.c0000644000175100017510000017737415077107276024552 0ustar00runnerrunner<%# b4_generated_by -%> /* A Bison parser, made by Lrama <%= Lrama::VERSION %>. */ <%# b4_copyright -%> /* Bison implementation for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ <%# b4_disclaimer -%> /* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, especially those whose name start with YY_ or yy_. They are private implementation details that can be changed or removed. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ <%# b4_identification -%> /* Identify Bison output, and Bison version. */ #define YYBISON 30802 /* Bison version string. */ #define YYBISON_VERSION "3.8.2" /* Skeleton name. */ #define YYSKELETON_NAME "<%= output.template_basename %>" /* Pure parsers. */ #define YYPURE 1 /* Push parsers. */ #define YYPUSH 0 /* Pull parsers. */ #define YYPULL 1 <%# b4_user_pre_prologue -%> <%- if output.aux.prologue -%> /* First part of user prologue. */ #line <%= output.aux.prologue_first_lineno %> "<%= output.grammar_file_path %>" <%= output.aux.prologue %> #line [@oline@] [@ofile@] <%- end -%> <%# b4_cast_define -%> # ifndef YY_CAST # ifdef __cplusplus # define YY_CAST(Type, Val) static_cast (Val) # define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) # else # define YY_CAST(Type, Val) ((Type) (Val)) # define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) # endif # endif <%# b4_null_define -%> # ifndef YY_NULLPTR # if defined __cplusplus # if 201103L <= __cplusplus # define YY_NULLPTR nullptr # else # define YY_NULLPTR 0 # endif # else # define YY_NULLPTR ((void*)0) # endif # endif <%# b4_header_include_if -%> <%- if output.include_header -%> #include "<%= output.include_header %>" <%- else -%> /* Use api.header.include to #include this header instead of duplicating it here. */ <%= output.render_partial("bison/_yacc.h") %> <%- end -%> <%# b4_declare_symbol_enum -%> /* Symbol kind. */ enum yysymbol_kind_t { <%= output.symbol_enum -%> }; typedef enum yysymbol_kind_t yysymbol_kind_t; <%# b4_user_post_prologue -%> <%# b4_c99_int_type_define -%> #ifdef short # undef short #endif /* On compilers that do not define __PTRDIFF_MAX__ etc., make sure and (if available) are included so that the code can choose integer types of a good width. */ #ifndef __PTRDIFF_MAX__ # include /* INFRINGES ON USER NAME SPACE */ # if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ # include /* INFRINGES ON USER NAME SPACE */ # define YY_STDINT_H # endif #endif /* Narrow types that promote to a signed type and that can represent a signed or unsigned integer of at least N bits. In tables they can save space and decrease cache pressure. Promoting to a signed type helps avoid bugs in integer arithmetic. */ #ifdef __INT_LEAST8_MAX__ typedef __INT_LEAST8_TYPE__ yytype_int8; #elif defined YY_STDINT_H typedef int_least8_t yytype_int8; #else typedef signed char yytype_int8; #endif #ifdef __INT_LEAST16_MAX__ typedef __INT_LEAST16_TYPE__ yytype_int16; #elif defined YY_STDINT_H typedef int_least16_t yytype_int16; #else typedef short yytype_int16; #endif /* Work around bug in HP-UX 11.23, which defines these macros incorrectly for preprocessor constants. This workaround can likely be removed in 2023, as HPE has promised support for HP-UX 11.23 (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of . */ #ifdef __hpux # undef UINT_LEAST8_MAX # undef UINT_LEAST16_MAX # define UINT_LEAST8_MAX 255 # define UINT_LEAST16_MAX 65535 #endif #if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ typedef __UINT_LEAST8_TYPE__ yytype_uint8; #elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ && UINT_LEAST8_MAX <= INT_MAX) typedef uint_least8_t yytype_uint8; #elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX typedef unsigned char yytype_uint8; #else typedef short yytype_uint8; #endif #if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ typedef __UINT_LEAST16_TYPE__ yytype_uint16; #elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ && UINT_LEAST16_MAX <= INT_MAX) typedef uint_least16_t yytype_uint16; #elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX typedef unsigned short yytype_uint16; #else typedef int yytype_uint16; #endif <%# b4_sizes_types_define -%> #ifndef YYPTRDIFF_T # if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ # define YYPTRDIFF_T __PTRDIFF_TYPE__ # define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ # elif defined PTRDIFF_MAX # ifndef ptrdiff_t # include /* INFRINGES ON USER NAME SPACE */ # endif # define YYPTRDIFF_T ptrdiff_t # define YYPTRDIFF_MAXIMUM PTRDIFF_MAX # else # define YYPTRDIFF_T long # define YYPTRDIFF_MAXIMUM LONG_MAX # endif #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned # endif #endif #define YYSIZE_MAXIMUM \ YY_CAST (YYPTRDIFF_T, \ (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ ? YYPTRDIFF_MAXIMUM \ : YY_CAST (YYSIZE_T, -1))) #define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) /* Stored state numbers (used for stacks). */ typedef <%= output.int_type_for([output.yynstates - 1]) %> yy_state_t; /* State numbers in computations. */ typedef int yy_state_fast_t; #ifndef YY_ # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(Msgid) dgettext ("bison-runtime", Msgid) # endif # endif # ifndef YY_ # define YY_(Msgid) Msgid # endif #endif <%# b4_attribute_define -%> #ifndef YY_ATTRIBUTE_PURE # if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) # define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) # else # define YY_ATTRIBUTE_PURE # endif #endif #ifndef YY_ATTRIBUTE_UNUSED # if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) # define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) # else # define YY_ATTRIBUTE_UNUSED # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YY_USE(E) ((void) (E)) #else # define YY_USE(E) /* empty */ #endif /* Suppress an incorrect diagnostic about yylval being uninitialized. */ #if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__ # if __GNUC__ * 100 + __GNUC_MINOR__ < 407 # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") # else # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") # endif # define YY_IGNORE_MAYBE_UNINITIALIZED_END \ _Pragma ("GCC diagnostic pop") #else # define YY_INITIAL_VALUE(Value) Value #endif #ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_END #endif #ifndef YY_INITIAL_VALUE # define YY_INITIAL_VALUE(Value) /* Nothing. */ #endif #if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ # define YY_IGNORE_USELESS_CAST_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") # define YY_IGNORE_USELESS_CAST_END \ _Pragma ("GCC diagnostic pop") #endif #ifndef YY_IGNORE_USELESS_CAST_BEGIN # define YY_IGNORE_USELESS_CAST_BEGIN # define YY_IGNORE_USELESS_CAST_END #endif #define YY_ASSERT(E) ((void) (0 && (E))) #if 1 /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS # include /* INFRINGES ON USER NAME SPACE */ /* Use EXIT_SUCCESS as a witness for stdlib.h. */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's 'empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined EXIT_SUCCESS \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined EXIT_SUCCESS void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined EXIT_SUCCESS void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* 1 */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yy_state_t yyss_alloc; YYSTYPE yyvs_alloc; YYLTYPE yyls_alloc; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE) \ + YYSIZEOF (YYLTYPE)) \ + 2 * YYSTACK_GAP_MAXIMUM) # define YYCOPY_NEEDED 1 /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack_alloc, Stack) \ do \ { \ YYPTRDIFF_T yynewbytes; \ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ Stack = &yyptr->Stack_alloc; \ yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / YYSIZEOF (*yyptr); \ } \ while (0) #endif #if defined YYCOPY_NEEDED && YYCOPY_NEEDED /* Copy COUNT objects from SRC to DST. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(Dst, Src, Count) \ __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) # else # define YYCOPY(Dst, Src, Count) \ do \ { \ YYPTRDIFF_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (Dst)[yyi] = (Src)[yyi]; \ } \ while (0) # endif # endif #endif /* !YYCOPY_NEEDED */ /* YYFINAL -- State number of the termination state. */ #define YYFINAL <%= output.yyfinal %> /* YYLAST -- Last index in YYTABLE. */ #define YYLAST <%= output.yylast %> /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS <%= output.yyntokens %> /* YYNNTS -- Number of nonterminals. */ #define YYNNTS <%= output.yynnts %> /* YYNRULES -- Number of rules. */ #define YYNRULES <%= output.yynrules %> /* YYNSTATES -- Number of states. */ #define YYNSTATES <%= output.yynstates %> /* YYMAXUTOK -- Last valid token kind. */ #define YYMAXUTOK <%= output.yymaxutok %> /* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM as returned by yylex, with out-of-bounds checking. */ #define YYTRANSLATE(YYX) \ (0 <= (YYX) && (YYX) <= YYMAXUTOK \ ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ : YYSYMBOL_YYUNDEF) /* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM as returned by yylex. */ static const <%= output.int_type_for(output.context.yytranslate) %> yytranslate[] = { <%= output.yytranslate %> }; <%- if output.error_recovery -%> /* YYTRANSLATE_INVERTED[SYMBOL-NUM] -- Token number corresponding to SYMBOL-NUM */ static const <%= output.int_type_for(output.context.yytranslate_inverted) %> yytranslate_inverted[] = { <%= output.yytranslate_inverted %> }; <%- end -%> #if YYDEBUG /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const <%= output.int_type_for(output.context.yyrline) %> yyrline[] = { <%= output.yyrline %> }; #endif /** Accessing symbol of state STATE. */ #define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) #if 1 /* The user-facing name of the symbol whose (internal) number is YYSYMBOL. No bounds checking. */ static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { <%= output.yytname %> }; static const char * yysymbol_name (yysymbol_kind_t yysymbol) { return yytname[yysymbol]; } #endif #define YYPACT_NINF (<%= output.yypact_ninf %>) #define yypact_value_is_default(Yyn) \ <%= output.table_value_equals(output.context.yypact, "Yyn", output.yypact_ninf, "YYPACT_NINF") %> #define YYTABLE_NINF (<%= output.yytable_ninf %>) #define yytable_value_is_error(Yyn) \ <%= output.table_value_equals(output.context.yytable, "Yyn", output.yytable_ninf, "YYTABLE_NINF") %> <%# b4_parser_tables_define -%> /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ static const <%= output.int_type_for(output.context.yypact) %> yypact[] = { <%= output.int_array_to_string(output.context.yypact) %> }; /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. Performed when YYTABLE does not specify something else to do. Zero means the default is an error. */ static const <%= output.int_type_for(output.context.yydefact) %> yydefact[] = { <%= output.int_array_to_string(output.context.yydefact) %> }; /* YYPGOTO[NTERM-NUM]. */ static const <%= output.int_type_for(output.context.yypgoto) %> yypgoto[] = { <%= output.int_array_to_string(output.context.yypgoto) %> }; /* YYDEFGOTO[NTERM-NUM]. */ static const <%= output.int_type_for(output.context.yydefgoto) %> yydefgoto[] = { <%= output.int_array_to_string(output.context.yydefgoto) %> }; /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule whose number is the opposite. If YYTABLE_NINF, syntax error. */ static const <%= output.int_type_for(output.context.yytable) %> yytable[] = { <%= output.int_array_to_string(output.context.yytable) %> }; static const <%= output.int_type_for(output.context.yycheck) %> yycheck[] = { <%= output.int_array_to_string(output.context.yycheck) %> }; /* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of state STATE-NUM. */ static const <%= output.int_type_for(output.context.yystos) %> yystos[] = { <%= output.int_array_to_string(output.context.yystos) %> }; /* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */ static const <%= output.int_type_for(output.context.yyr1) %> yyr1[] = { <%= output.int_array_to_string(output.context.yyr1) %> }; /* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */ static const <%= output.int_type_for(output.context.yyr2) %> yyr2[] = { <%= output.int_array_to_string(output.context.yyr2) %> }; enum { YYENOMEM = -2 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab #define YYNOMEM goto yyexhaustedlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY) \ { \ yychar = (Token); \ yylval = (Value); \ YYPOPSTACK (yylen); \ yystate = *yyssp; \ goto yybackup; \ } \ else \ { \ yyerror (<%= output.yyerror_args %>, YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (0) /* Backward compatibility with an undocumented macro. Use YYerror or YYUNDEF. */ #define YYERRCODE YYUNDEF <%# b4_yylloc_default_define -%> /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. If N is 0, then set CURRENT to the empty location which ends the previous symbol: RHS[0] (always defined). */ #ifndef YYLLOC_DEFAULT # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (N) \ { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } \ else \ { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ while (0) #endif #define YYRHSLOC(Rhs, K) ((Rhs)[K]) /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (0) <%# b4_yylocation_print_define -%> /* YYLOCATION_PRINT -- Print the location on the stream. This macro was not mandated originally: define only if we know we won't break user code: when these are the locations we know. */ # ifndef YYLOCATION_PRINT # if defined YY_LOCATION_PRINT /* Temporary convenience wrapper in case some people defined the undocumented and private YY_LOCATION_PRINT macros. */ # define YYLOCATION_PRINT(File, Loc<%= output.user_args %>) YY_LOCATION_PRINT(File, *(Loc)<%= output.user_args %>) # elif defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL /* Print *YYLOCP on YYO. Private, do not rely on its existence. */ YY_ATTRIBUTE_UNUSED static int yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) { int res = 0; int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; if (0 <= yylocp->first_line) { res += YYFPRINTF (yyo, "%d", yylocp->first_line); if (0 <= yylocp->first_column) res += YYFPRINTF (yyo, ".%d", yylocp->first_column); } if (0 <= yylocp->last_line) { if (yylocp->first_line < yylocp->last_line) { res += YYFPRINTF (yyo, "-%d", yylocp->last_line); if (0 <= end_col) res += YYFPRINTF (yyo, ".%d", end_col); } else if (0 <= end_col && yylocp->first_column < end_col) res += YYFPRINTF (yyo, "-%d", end_col); } return res; } # define YYLOCATION_PRINT yy_location_print_ /* Temporary convenience wrapper in case some people defined the undocumented and private YY_LOCATION_PRINT macros. */ # define YY_LOCATION_PRINT(File, Loc<%= output.user_args %>) YYLOCATION_PRINT(File, &(Loc)<%= output.user_args %>) # else # define YYLOCATION_PRINT(File, Loc<%= output.user_args %>) ((void) 0) /* Temporary convenience wrapper in case some people defined the undocumented and private YY_LOCATION_PRINT macros. */ # define YY_LOCATION_PRINT YYLOCATION_PRINT # endif # endif /* !defined YYLOCATION_PRINT */ # define YY_SYMBOL_PRINT(Title, Kind, Value, Location<%= output.user_args %>) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Kind, Value, Location<%= output.user_args %>); \ YYFPRINTF (stderr, "\n"); \ } \ } while (0) <%# b4_yy_symbol_print_define -%> /*-----------------------------------. | Print this symbol's value on YYO. | `-----------------------------------*/ static void yy_symbol_value_print (FILE *yyo, yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp<%= output.user_formals %>) { FILE *yyoutput = yyo; <%= output.parse_param_use("yyoutput", "yylocationp") %> if (!yyvaluep) return; YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN <%# b4_symbol_actions(printer) -%> switch (yykind) { <%= output.symbol_actions_for_printer -%> default: break; } YY_IGNORE_MAYBE_UNINITIALIZED_END } /*---------------------------. | Print this symbol on YYO. | `---------------------------*/ static void yy_symbol_print (FILE *yyo, yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp<%= output.user_formals %>) { YYFPRINTF (yyo, "%s %s (", yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); YYLOCATION_PRINT (yyo, yylocationp<%= output.user_args %>); YYFPRINTF (yyo, ": "); yy_symbol_value_print (yyo, yykind, yyvaluep, yylocationp<%= output.user_args %>); YYFPRINTF (yyo, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ static void yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop<%= output.user_formals %>) { YYFPRINTF (stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) { int yybot = *yybottom; YYFPRINTF (stderr, " %d", yybot); } YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top<%= output.user_args %>) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)<%= output.user_args %>); \ } while (0) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ static void yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule<%= output.user_formals %>) { int yylno = yyrline[yyrule]; int yynrhs = yyr2[yyrule]; int yyi; YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), &yyvsp[(yyi + 1) - (yynrhs)], &(yylsp[(yyi + 1) - (yynrhs)])<%= output.user_args %>); YYFPRINTF (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule<%= output.user_args %>) \ do { \ if (yydebug) \ yy_reduce_print (yyssp, yyvsp, yylsp, Rule<%= output.user_args %>); \ } while (0) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ #ifndef yydebug int yydebug; #endif #else /* !YYDEBUG */ # define YYDPRINTF(Args) ((void) 0) # define YY_SYMBOL_PRINT(Title, Kind, Value, Location<%= output.user_args %>) # define YY_STACK_PRINT(Bottom, Top<%= output.user_args %>) # define YY_REDUCE_PRINT(Rule<%= output.user_args %>) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif /* Context of a parse error. */ typedef struct { yy_state_t *yyssp; yysymbol_kind_t yytoken; YYLTYPE *yylloc; } yypcontext_t; /* Put in YYARG at most YYARGN of the expected tokens given the current YYCTX, and return the number of tokens stored in YYARG. If YYARG is null, return the number of expected tokens (guaranteed to be less than YYNTOKENS). Return YYENOMEM on memory exhaustion. Return 0 if there are more than YYARGN expected tokens, yet fill YYARG up to YYARGN. */ static int yypcontext_expected_tokens (const yypcontext_t *yyctx, yysymbol_kind_t yyarg[], int yyargn) { /* Actual size of YYARG. */ int yycount = 0; int yyn = yypact[+*yyctx->yyssp]; if (!yypact_value_is_default (yyn)) { /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. In other words, skip the first -YYN actions for this state because they are default actions. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yyx; for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYSYMBOL_YYerror && !yytable_value_is_error (yytable[yyx + yyn])) { if (!yyarg) ++yycount; else if (yycount == yyargn) return 0; else yyarg[yycount++] = YY_CAST (yysymbol_kind_t, yyx); } } if (yyarg && yycount == 0 && 0 < yyargn) yyarg[0] = YYSYMBOL_YYEMPTY; return yycount; } #ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) # else /* Return the length of YYSTR. */ static YYPTRDIFF_T yystrlen (const char *yystr) { YYPTRDIFF_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif #endif #ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ static char * yystpcpy (char *yydest, const char *yysrc) { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif #endif #ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYPTRDIFF_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYPTRDIFF_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; else goto append; append: default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (yyres) return yystpcpy (yyres, yystr) - yyres; else return yystrlen (yystr); } #endif static int yy_syntax_error_arguments (const yypcontext_t *yyctx, yysymbol_kind_t yyarg[], int yyargn) { /* Actual size of YYARG. */ int yycount = 0; /* There are many possibilities here to consider: - If this state is a consistent state with a default action, then the only way this function was invoked is if the default action is an error action. In that case, don't check for expected tokens because there are none. - The only way there can be no lookahead present (in yychar) is if this state is a consistent state with a default action. Thus, detecting the absence of a lookahead is sufficient to determine that there is no unexpected or expected token to report. In that case, just report a simple "syntax error". - Don't assume there isn't a lookahead just because this state is a consistent state with a default action. There might have been a previous inconsistent state, consistent state with a non-default action, or user semantic action that manipulated yychar. - Of course, the expected token list depends on states to have correct lookahead information, and it depends on the parser not to perform extra reductions after fetching a lookahead from the scanner and before detecting a syntax error. Thus, state merging (from LALR or IELR) and default reductions corrupt the expected token list. However, the list is correct for canonical LR with one exception: it will still contain any token that will not be accepted due to an error action in a later state. */ if (yyctx->yytoken != YYSYMBOL_YYEMPTY) { int yyn; if (yyarg) yyarg[yycount] = yyctx->yytoken; ++yycount; yyn = yypcontext_expected_tokens (yyctx, yyarg ? yyarg + 1 : yyarg, yyargn - 1); if (yyn == YYENOMEM) return YYENOMEM; else yycount += yyn; } return yycount; } /* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message about the unexpected token YYTOKEN for the state stack whose top is YYSSP. Return 0 if *YYMSG was successfully written. Return -1 if *YYMSG is not large enough to hold the message. In that case, also set *YYMSG_ALLOC to the required number of bytes. Return YYENOMEM if the required number of bytes is too large to store. */ static int yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, const yypcontext_t *yyctx<%= output.user_formals %>) { enum { YYARGS_MAX = 5 }; /* Internationalized format string. */ const char *yyformat = YY_NULLPTR; /* Arguments of yyformat: reported tokens (one for the "unexpected", one per "expected"). */ yysymbol_kind_t yyarg[YYARGS_MAX]; /* Cumulated lengths of YYARG. */ YYPTRDIFF_T yysize = 0; /* Actual size of YYARG. */ int yycount = yy_syntax_error_arguments (yyctx, yyarg, YYARGS_MAX); if (yycount == YYENOMEM) return YYENOMEM; switch (yycount) { #define YYCASE_(N, S) \ case N: \ yyformat = S; \ break default: /* Avoid compiler warnings. */ YYCASE_(0, YY_("syntax error")); YYCASE_(1, YY_("syntax error, unexpected %s")); YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); #undef YYCASE_ } /* Compute error message size. Don't count the "%s"s, but reserve room for the terminator. */ yysize = yystrlen (yyformat) - 2 * yycount + 1; { int yyi; for (yyi = 0; yyi < yycount; ++yyi) { YYPTRDIFF_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyarg[yyi]]); if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) yysize = yysize1; else return YYENOMEM; } } if (*yymsg_alloc < yysize) { *yymsg_alloc = 2 * yysize; if (! (yysize <= *yymsg_alloc && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; return -1; } /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ { char *yyp = *yymsg; int yyi = 0; while ((*yyp = *yyformat) != '\0') if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yytname[yyarg[yyi++]]); yyformat += 2; } else { ++yyp; ++yyformat; } } return 0; } <%# b4_yydestruct_define %> /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ static void yydestruct (const char *yymsg, yysymbol_kind_t yykind, YYSTYPE *yyvaluep, YYLTYPE *yylocationp<%= output.user_formals %>) { <%= output.parse_param_use("yyvaluep", "yylocationp") %> if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp<%= output.user_args %>); YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN switch (yykind) { <%= output.symbol_actions_for_destructor -%> default: break; } YY_IGNORE_MAYBE_UNINITIALIZED_END } <%- if output.error_recovery -%> #ifndef YYMAXREPAIR # define YYMAXREPAIR(<%= output.parse_param_name %>) (3) #endif #ifndef YYERROR_RECOVERY_ENABLED # define YYERROR_RECOVERY_ENABLED(<%= output.parse_param_name %>) (1) #endif enum yy_repair_type { inserting, deleting, shifting, }; struct yy_repair { enum yy_repair_type type; yysymbol_kind_t term; }; typedef struct yy_repair yy_repair; struct yy_repairs { /* For debug */ int id; /* For breadth-first traversing */ struct yy_repairs *next; YYPTRDIFF_T stack_length; /* Bottom of states */ yy_state_t *states; /* Top of states */ yy_state_t *state; /* repair length */ int repair_length; /* */ struct yy_repairs *prev_repair; struct yy_repair repair; }; typedef struct yy_repairs yy_repairs; struct yy_term { yysymbol_kind_t kind; YYSTYPE value; YYLTYPE location; }; typedef struct yy_term yy_term; struct yy_repair_terms { int id; int length; yy_term terms[]; }; typedef struct yy_repair_terms yy_repair_terms; static void yy_error_token_initialize (yysymbol_kind_t yykind, YYSTYPE * const yyvaluep, YYLTYPE * const yylocationp<%= output.user_formals %>) { YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN switch (yykind) { <%= output.symbol_actions_for_error_token -%> default: break; } YY_IGNORE_MAYBE_UNINITIALIZED_END } static yy_repair_terms * yy_create_repair_terms(yy_repairs *reps<%= output.user_formals %>) { yy_repairs *r = reps; yy_repair_terms *rep_terms; int count = 0; while (r->prev_repair) { count++; r = r->prev_repair; } rep_terms = (yy_repair_terms *) YYMALLOC (sizeof (yy_repair_terms) + sizeof (yy_term) * count); rep_terms->id = reps->id; rep_terms->length = count; r = reps; while (r->prev_repair) { rep_terms->terms[count-1].kind = r->repair.term; count--; r = r->prev_repair; } return rep_terms; } static void yy_print_repairs(yy_repairs *reps<%= output.user_formals %>) { yy_repairs *r = reps; YYDPRINTF ((stderr, "id: %d, repair_length: %d, repair_state: %d, prev_repair_id: %d\n", reps->id, reps->repair_length, *reps->state, reps->prev_repair->id)); while (r->prev_repair) { YYDPRINTF ((stderr, "%s ", yysymbol_name (r->repair.term))); r = r->prev_repair; } YYDPRINTF ((stderr, "\n")); } static void yy_print_repair_terms(yy_repair_terms *rep_terms<%= output.user_formals %>) { for (int i = 0; i < rep_terms->length; i++) YYDPRINTF ((stderr, "%s ", yysymbol_name (rep_terms->terms[i].kind))); YYDPRINTF ((stderr, "\n")); } static void yy_free_repairs(yy_repairs *reps<%= output.user_formals %>) { while (reps) { yy_repairs *r = reps; reps = reps->next; YYFREE (r->states); YYFREE (r); } } static int yy_process_repairs(yy_repairs *reps, yysymbol_kind_t token) { int yyn; int yystate = *reps->state; int yylen = 0; yysymbol_kind_t yytoken = token; goto yyrecover_backup; yyrecover_newstate: // TODO: check reps->stack_length reps->state += 1; *reps->state = (yy_state_t) yystate; yyrecover_backup: yyn = yypact[yystate]; if (yypact_value_is_default (yyn)) goto yyrecover_default; /* "Reading a token" */ if (yytoken == YYSYMBOL_YYEMPTY) return 1; yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yyrecover_default; yyn = yytable[yyn]; if (yyn <= 0) { if (yytable_value_is_error (yyn)) goto yyrecover_errlab; yyn = -yyn; goto yyrecover_reduce; } /* shift */ yystate = yyn; yytoken = YYSYMBOL_YYEMPTY; goto yyrecover_newstate; yyrecover_default: yyn = yydefact[yystate]; if (yyn == 0) goto yyrecover_errlab; goto yyrecover_reduce; yyrecover_reduce: yylen = yyr2[yyn]; /* YYPOPSTACK */ reps->state -= yylen; yylen = 0; { const int yylhs = yyr1[yyn] - YYNTOKENS; const int yyi = yypgoto[yylhs] + *reps->state; yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *reps->state ? yytable[yyi] : yydefgoto[yylhs]); } goto yyrecover_newstate; yyrecover_errlab: return 0; } static yy_repair_terms * yyrecover(yy_state_t *yyss, yy_state_t *yyssp, int yychar<%= output.user_formals %>) { yysymbol_kind_t yytoken = YYTRANSLATE (yychar); yy_repair_terms *rep_terms = YY_NULLPTR; int count = 0; yy_repairs *head = (yy_repairs *) YYMALLOC (sizeof (yy_repairs)); yy_repairs *current = head; yy_repairs *tail = head; YYPTRDIFF_T stack_length = yyssp - yyss + 1; head->id = count; head->next = 0; head->stack_length = stack_length; head->states = (yy_state_t *) YYMALLOC (sizeof (yy_state_t) * (stack_length)); head->state = head->states + (yyssp - yyss); YYCOPY (head->states, yyss, stack_length); head->repair_length = 0; head->prev_repair = 0; stack_length = (stack_length * 2 > 100) ? (stack_length * 2) : 100; count++; while (current) { int yystate = *current->state; int yyn = yypact[yystate]; /* See also: yypcontext_expected_tokens */ if (!yypact_value_is_default (yyn)) { int yyxbegin = yyn < 0 ? -yyn : 0; int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yyx; for (yyx = yyxbegin; yyx < yyxend; ++yyx) { if (yyx != YYSYMBOL_YYerror) { if (current->repair_length + 1 > YYMAXREPAIR(<%= output.parse_param_name %>)) continue; yy_repairs *reps = (yy_repairs *) YYMALLOC (sizeof (yy_repairs)); reps->id = count; reps->next = 0; reps->stack_length = stack_length; reps->states = (yy_state_t *) YYMALLOC (sizeof (yy_state_t) * (stack_length)); reps->state = reps->states + (current->state - current->states); YYCOPY (reps->states, current->states, current->state - current->states + 1); reps->repair_length = current->repair_length + 1; reps->prev_repair = current; reps->repair.type = inserting; reps->repair.term = (yysymbol_kind_t) yyx; /* Process PDA assuming next token is yyx */ if (! yy_process_repairs (reps, (yysymbol_kind_t)yyx)) { YYFREE (reps); continue; } tail->next = reps; tail = reps; count++; if (yyx == yytoken) { rep_terms = yy_create_repair_terms (current<%= output.user_args %>); YYDPRINTF ((stderr, "repair_terms found. id: %d, length: %d\n", rep_terms->id, rep_terms->length)); yy_print_repairs (current<%= output.user_args %>); yy_print_repair_terms (rep_terms<%= output.user_args %>); goto done; } YYDPRINTF ((stderr, "New repairs is enqueued. count: %d, yystate: %d, yyx: %d\n", count, yystate, yyx)); yy_print_repairs (reps<%= output.user_args %>); } } } current = current->next; } done: yy_free_repairs(head<%= output.user_args %>); if (!rep_terms) { YYDPRINTF ((stderr, "repair_terms not found\n")); } return rep_terms; } <%- end -%> /*----------. | yyparse. | `----------*/ int yyparse (<%= output.parse_param %>) { <%# b4_declare_scanner_communication_variables -%> /* Lookahead token kind. */ int yychar; /* The semantic value of the lookahead symbol. */ /* Default value used for initialization, for pacifying older GCCs or non-GCC compilers. */ #ifdef __cplusplus static const YYSTYPE yyval_default = {}; (void) yyval_default; #else YY_INITIAL_VALUE (static const YYSTYPE yyval_default;) #endif YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); /* Location data for the lookahead symbol. */ static const YYLTYPE yyloc_default # if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL = { 1, 1, 1, 1 } # endif ; YYLTYPE yylloc = yyloc_default; <%# b4_declare_parser_state_variables -%> /* Number of syntax errors so far. */ int yynerrs = 0; YY_USE (yynerrs); /* Silence compiler warning. */ yy_state_fast_t yystate = 0; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus = 0; /* Refer to the stacks through separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* Their size. */ YYPTRDIFF_T yystacksize = YYINITDEPTH; /* The state stack: array, bottom, top. */ yy_state_t yyssa[YYINITDEPTH]; yy_state_t *yyss = yyssa; yy_state_t *yyssp = yyss; /* The semantic value stack: array, bottom, top. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs = yyvsa; YYSTYPE *yyvsp = yyvs; /* The location stack: array, bottom, top. */ YYLTYPE yylsa[YYINITDEPTH]; YYLTYPE *yyls = yylsa; YYLTYPE *yylsp = yyls; int yyn; /* The return value of yyparse. */ int yyresult; /* Lookahead symbol kind. */ yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; YYLTYPE yyloc; /* The locations where the error started and ended. */ YYLTYPE yyerror_range[3]; <%- if output.error_recovery -%> yy_repair_terms *rep_terms = 0; yy_term term_backup; int rep_terms_index; int yychar_backup; <%- end -%> /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; YYDPRINTF ((stderr, "Starting parse\n")); yychar = YYEMPTY; /* Cause a token to be read. */ <%# b4_user_initial_action -%> <%= output.user_initial_action("/* User initialization code. */") %> #line [@oline@] [@ofile@] yylsp[0] = yylloc; goto yysetstate; /*------------------------------------------------------------. | yynewstate -- push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; /*--------------------------------------------------------------------. | yysetstate -- set current state (the top of the stack) to yystate. | `--------------------------------------------------------------------*/ yysetstate: YYDPRINTF ((stderr, "Entering state %d\n", yystate)); YY_ASSERT (0 <= yystate && yystate < YYNSTATES); YY_IGNORE_USELESS_CAST_BEGIN *yyssp = YY_CAST (yy_state_t, yystate); YY_IGNORE_USELESS_CAST_END YY_STACK_PRINT (yyss, yyssp<%= output.user_args %>); if (yyss + yystacksize - 1 <= yyssp) #if !defined yyoverflow && !defined YYSTACK_RELOCATE YYNOMEM; #else { /* Get the current used size of the three stacks, in elements. */ YYPTRDIFF_T yysize = yyssp - yyss + 1; # if defined yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ yy_state_t *yyss1 = yyss; YYSTYPE *yyvs1 = yyvs; YYLTYPE *yyls1 = yyls; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * YYSIZEOF (*yyssp), &yyvs1, yysize * YYSIZEOF (*yyvsp), &yyls1, yysize * YYSIZEOF (*yylsp), &yystacksize); yyss = yyss1; yyvs = yyvs1; yyls = yyls1; } # else /* defined YYSTACK_RELOCATE */ /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) YYNOMEM; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yy_state_t *yyss1 = yyss; union yyalloc *yyptr = YY_CAST (union yyalloc *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); if (! yyptr) YYNOMEM; YYSTACK_RELOCATE (yyss_alloc, yyss); YYSTACK_RELOCATE (yyvs_alloc, yyvs); YYSTACK_RELOCATE (yyls_alloc, yyls); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; yylsp = yyls + yysize - 1; YY_IGNORE_USELESS_CAST_BEGIN YYDPRINTF ((stderr, "Stack size increased to %ld\n", YY_CAST (long, yystacksize))); YY_IGNORE_USELESS_CAST_END if (yyss + yystacksize - 1 <= yyssp) YYABORT; } #endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ if (yystate == YYFINAL) YYACCEPT; goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a lookahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; if (yypact_value_is_default (yyn)) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ <%- if output.error_recovery -%> if (YYERROR_RECOVERY_ENABLED(<%= output.parse_param_name %>)) { if (yychar == YYEMPTY && rep_terms) { if (rep_terms_index < rep_terms->length) { YYDPRINTF ((stderr, "An error recovery token is used\n")); yy_term term = rep_terms->terms[rep_terms_index]; yytoken = term.kind; yylval = term.value; yylloc = term.location; yychar = yytranslate_inverted[yytoken]; YY_SYMBOL_PRINT ("Next error recovery token is", yytoken, &yylval, &yylloc<%= output.user_args %>); rep_terms_index++; } else { YYDPRINTF ((stderr, "Error recovery is completed\n")); yytoken = term_backup.kind; yylval = term_backup.value; yylloc = term_backup.location; yychar = yychar_backup; YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc<%= output.user_args %>); YYFREE (rep_terms); rep_terms = 0; yychar_backup = 0; } } } <%- end -%> /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token\n")); yychar = yylex <%= output.yylex_formals %>; } if (yychar <= <%= output.eof_symbol.id.s_value %>) { yychar = <%= output.eof_symbol.id.s_value %>; yytoken = <%= output.eof_symbol.enum_name %>; YYDPRINTF ((stderr, "Now at end of input.\n")); } else if (yychar == <%= output.error_symbol.id.s_value %>) { /* The scanner already issued an error message, process directly to error recovery. But do not keep the error token as lookahead, it is too special and may lead us to an endless loop in error recovery. */ yychar = <%= output.undef_symbol.id.s_value %>; yytoken = <%= output.error_symbol.enum_name %>; yyerror_range[1] = yylloc; goto yyerrlab1; } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc<%= output.user_args %>); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yytable_value_is_error (yyn)) goto yyerrlab; yyn = -yyn; goto yyreduce; } /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc<%= output.user_args %>); yystate = yyn; YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END *++yylsp = yylloc; <%= output.after_shift_function("/* %after-shift code. */") %> /* Discard the shifted token. */ yychar = YYEMPTY; goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: '$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; <%= output.before_reduce_function("/* %before-reduce function. */") %> /* Default location. */ YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); yyerror_range[1] = yyloc; YY_REDUCE_PRINT (yyn<%= output.user_args %>); switch (yyn) { <%= output.user_actions -%> default: break; } /* User semantic actions sometimes alter yychar, and that requires that yytoken be updated with the new translation. We take the approach of translating immediately before every use of yytoken. One alternative is translating here after every semantic action, but that translation would be missed if the semantic action invokes YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an incorrect destructor might then be invoked immediately. In the case of YYERROR or YYBACKUP, subsequent parser actions might lead to an incorrect destructor call or verbose syntax error message before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc<%= output.user_args %>); YYPOPSTACK (yylen); <%= output.after_reduce_function("/* %after-reduce function. */") %> yylen = 0; *++yyvsp = yyval; *++yylsp = yyloc; /* Now 'shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ { const int yylhs = yyr1[yyn] - YYNTOKENS; const int yyi = yypgoto[yylhs] + *yyssp; yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp ? yytable[yyi] : yydefgoto[yylhs]); } goto yynewstate; /*--------------------------------------. | yyerrlab -- here on detecting error. | `--------------------------------------*/ yyerrlab: /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; { yypcontext_t yyctx = {yyssp, yytoken, &yylloc}; char const *yymsgp = YY_("syntax error"); int yysyntax_error_status; yysyntax_error_status = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx<%= output.user_args %>); if (yysyntax_error_status == 0) yymsgp = yymsg; else if (yysyntax_error_status == -1) { if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = YY_CAST (char *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); if (yymsg) { yysyntax_error_status = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx<%= output.user_args %>); yymsgp = yymsg; } else { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; yysyntax_error_status = YYENOMEM; } } yyerror (<%= output.yyerror_args %>, yymsgp); if (yysyntax_error_status == YYENOMEM) YYNOMEM; } } yyerror_range[1] = yylloc; if (yyerrstatus == 3) { /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= <%= output.eof_symbol.id.s_value %>) { /* Return failure if at end of input. */ if (yychar == <%= output.eof_symbol.id.s_value %>) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval, &yylloc<%= output.user_args %>); yychar = YYEMPTY; } } /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (0) YYERROR; ++yynerrs; /* Do not reclaim the symbols of the rule whose action triggered this YYERROR. */ YYPOPSTACK (yylen); <%= output.after_pop_stack_function("yylen", "/* %after-pop-stack function. */") %> yylen = 0; YY_STACK_PRINT (yyss, yyssp<%= output.user_args %>); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: <%- if output.error_recovery -%> if (YYERROR_RECOVERY_ENABLED(<%= output.parse_param_name %>)) { rep_terms = yyrecover (yyss, yyssp, yychar<%= output.user_args %>); if (rep_terms) { for (int i = 0; i < rep_terms->length; i++) { yy_term *term = &rep_terms->terms[i]; yy_error_token_initialize (term->kind, &term->value, &term->location<%= output.user_args %>); } yychar_backup = yychar; /* Can be packed into (the tail of) rep_terms? */ term_backup.kind = yytoken; term_backup.value = yylval; term_backup.location = yylloc; rep_terms_index = 0; yychar = YYEMPTY; goto yybackup; } } <%- end -%> yyerrstatus = 3; /* Each real token shifted decrements this. */ /* Pop stack until we find a state that shifts the error token. */ for (;;) { yyn = yypact[yystate]; if (!yypact_value_is_default (yyn)) { yyn += YYSYMBOL_YYerror; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yyerror_range[1] = *yylsp; yydestruct ("Error: popping", YY_ACCESSING_SYMBOL (yystate), yyvsp, yylsp<%= output.user_args %>); YYPOPSTACK (1); <%= output.after_pop_stack_function(1, "/* %after-pop-stack function. */") %> yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp<%= output.user_args %>); } YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END yyerror_range[2] = yylloc; ++yylsp; YYLLOC_DEFAULT (*yylsp, yyerror_range, 2); /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp<%= output.user_args %>); <%= output.after_shift_error_token_function("/* %after-shift-error-token code. */") %> yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturnlab; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturnlab; /*-----------------------------------------------------------. | yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here. | `-----------------------------------------------------------*/ yyexhaustedlab: yyerror (<%= output.yyerror_args %>, YY_("memory exhausted")); yyresult = 2; goto yyreturnlab; /*----------------------------------------------------------. | yyreturnlab -- parsing is finished, clean up and return. | `----------------------------------------------------------*/ yyreturnlab: if (yychar != YYEMPTY) { /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = YYTRANSLATE (yychar); yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval, &yylloc<%= output.user_args %>); } /* Do not reclaim the symbols of the rule whose action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp<%= output.user_args %>); while (yyssp != yyss) { yydestruct ("Cleanup: popping", YY_ACCESSING_SYMBOL (+*yyssp), yyvsp, yylsp<%= output.user_args %>); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); return yyresult; } <%# b4_percent_code_get([[epilogue]]) -%> <%- if output.aux.epilogue -%> #line <%= output.aux.epilogue_first_lineno - 1 %> "<%= output.grammar_file_path %>" <%= output.aux.epilogue -%> <%- end -%> nghttp2-1.68.0/third-party/mruby/tools/lrama/PaxHeaders/NEWS.md0000644000000000000000000000013215077107276021225 xustar0030 mtime=1761382078.141420469 30 atime=1761382080.164411215 30 ctime=1761382108.342302572 nghttp2-1.68.0/third-party/mruby/tools/lrama/NEWS.md0000644000175100017510000003713615077107276021627 0ustar00runnerrunner# NEWS for Lrama ## Lrama 0.7.0 (2025-01-21) ## [EXPERIMENTAL] Support the generation of the IELR(1) parser described in this paper Support the generation of the IELR(1) parser described in this paper. https://www.sciencedirect.com/science/article/pii/S0167642309001191 If you use IELR(1) parser, you can write the following directive in your grammar file. ```yacc %define lr.type ielr ``` But, currently IELR(1) parser is experimental feature. If you find any bugs, please report it to us. Thank you. ## Support `-t` option as same as `--debug` option Support to `-t` option as same as `--debug` option. These options align with Bison behavior. So same as `--debug` option. ## Trace only explicit rules Support to trace only explicit rules. If you use `--trace=rules` option, it shows include mid-rule actions. If you want to show only explicit rules, you can use `--trace=only-explicit-rules` option. Example: ```yacc %{ %} %union { int i; } %token number %type program %% program : number { printf("%d", $1); } number { $$ = $1 + $3; } ; %% ``` Result of `--trace=rules`: ```console $ exe/lrama --trace=rules sample.y Grammar rules: $accept -> program YYEOF $@1 -> ε program -> number $@1 number ``` Result of `--trace=only-explicit-rules`: ```console $ exe/lrama --trace=explicit-rules sample.y Grammar rules: $accept -> program YYEOF program -> number number ``` ## Lrama 0.6.11 (2024-12-23) ### Add support for %type declarations using %nterm in Nonterminal Symbols Allow to use `%nterm` in Nonterminal Symbols for `%type` declarations. ```yacc %nterm nonterminal… ``` This directive is also supported for compatibility with Bison, and only non-terminal symbols are allowed. In other words, definitions like the following will result in an error: ```yacc %{ // Prologue %} %token EOI 0 "EOI" %nterm EOI %% program: /* empty */ ; ``` It show an error message like the following: ```command ⯠exe/lrama nterm.y nterm.y:6:7: symbol EOI redeclared as a nonterminal %nterm EOI ^^^ ``` ## Lrama 0.6.10 (2024-09-11) ### Aliased Named References for actions of RHS in parameterizing rules Allow to use aliased named references for actions of RHS in parameterizing rules. ```yacc %rule sum(X, Y): X[summand] '+' Y[addend] { $$ = $summand + $addend } ; ``` https://github.com/ruby/lrama/pull/410 ### Named References for actions of RHS in parameterizing rules caller side Allow to use named references for actions of RHS in parameterizing rules caller side. ```yacc opt_nl: '\n'?[nl] { $$ = $nl; } ; ``` https://github.com/ruby/lrama/pull/414 ### Widen the definable position of parameterizing rules Allow to define parameterizing rules in the middle of the grammar. ```yacc %rule defined_option(X): /* empty */ | X ; %% program : defined_option(number) | defined_list(number) ; %rule defined_list(X): /* empty */ /* <--- here */ | defined_list(X) number ; ``` https://github.com/ruby/lrama/pull/420 ### Report unused terminal symbols Support to report unused terminal symbols. Run `exe/lrama --report=terms` to show unused terminal symbols. ```console $ exe/lrama --report=terms sample/calc.y 11 Unused Terms 0 YYerror 1 YYUNDEF 2 '\\\\' 3 '\\13' 4 keyword_class2 5 tNUMBER 6 tPLUS 7 tMINUS 8 tEQ 9 tEQEQ 10 '>' ``` https://github.com/ruby/lrama/pull/439 ### Report unused rules Support to report unused rules. Run `exe/lrama --report=rules` to show unused rules. ```console $ exe/lrama --report=rules sample/calc.y 3 Unused Rules 0 unused_option 1 unused_list 2 unused_nonempty_list ``` https://github.com/ruby/lrama/pull/441 ### Ensure compatibility with Bison for `%locations` directive Support `%locations` directive to ensure compatibility with Bison. Change to `%locations` directive not set by default. https://github.com/ruby/lrama/pull/446 ### Diagnostics report for parameterizing rules redefine Support to warning redefined parameterizing rules. Run `exe/lrama -W` or `exe/lrama --warnings` to show redefined parameterizing rules. ```console $ exe/lrama -W sample/calc.y parameterizing rule redefined: redefined_method(X) parameterizing rule redefined: redefined_method(X) ``` https://github.com/ruby/lrama/pull/448 ### Support `-v` and `--verbose` option Support to `-v` and `--verbose` option. These options align with Bison behavior. So same as '--report=state' option. https://github.com/ruby/lrama/pull/457 ## Lrama 0.6.9 (2024-05-02) ### Callee side tag specification of parameterizing rules Allow to specify tag on callee side of parameterizing rules. ```yacc %union { int i; } %rule with_tag(X) : X { $$ = $1; } ; ``` ### Named References for actions of RHS in parameterizing rules Allow to use named references for actions of RHS in parameterizing rules. ```yacc %rule option(number): /* empty */ | number { $$ = $number; } ; ``` ## Lrama 0.6.8 (2024-04-29) ### Nested parameterizing rules with tag Allow to nested parameterizing rules with tag. ```yacc %union { int i; } %rule nested_nested_option(X): /* empty */ | X ; %rule nested_option(X): /* empty */ | nested_nested_option(X) ; %rule option(Y): /* empty */ | nested_option(Y) ; ``` ## Lrama 0.6.7 (2024-04-28) ### RHS of user defined parameterizing rules contains `'symbol'?`, `'symbol'+` and `'symbol'*`. User can use `'symbol'?`, `'symbol'+` and `'symbol'*` in RHS of user defined parameterizing rules. ``` %rule with_word_seps(X): /* empty */ | X ' '+ ; ``` ## Lrama 0.6.6 (2024-04-27) ### Trace actions Support trace actions for debugging. Run `exe/lrama --trace=actions` to show grammar rules with actions. ```console $ exe/lrama --trace=actions sample/calc.y Grammar rules with actions: $accept -> list, YYEOF {} list -> ε {} list -> list, LF {} list -> list, expr, LF { printf("=> %d\n", $2); } expr -> NUM {} expr -> expr, '+', expr { $$ = $1 + $3; } expr -> expr, '-', expr { $$ = $1 - $3; } expr -> expr, '*', expr { $$ = $1 * $3; } expr -> expr, '/', expr { $$ = $1 / $3; } expr -> '(', expr, ')' { $$ = $2; } ``` ### Inlining Support inlining for rules. The `%inline` directive causes all references to symbols to be replaced with its definition. ```yacc %rule %inline op: PLUS { + } | TIMES { * } ; %% expr : number { $$ = $1; } | expr op expr { $$ = $1 $2 $3; } ; ``` as same as ```yacc expr : number { $$ = $1; } | expr '+' expr { $$ = $1 + $3; } | expr '*' expr { $$ = $1 * $3; } ; ``` ## Lrama 0.6.5 (2024-03-25) ### Typed Midrule Actions User can specify the type of mid rule action by tag (``) instead of specifying it with in an action. ```yacc primary: k_case expr_value terms? { $$ = p->case_labels; p->case_labels = Qnil; } case_body k_end { ... } ``` can be written as ```yacc primary: k_case expr_value terms? { $$ = p->case_labels; p->case_labels = Qnil; } case_body k_end { ... } ``` `%destructor` for midrule action is invoked only when tag is specified by Typed Midrule Actions. Difference from Bison's Typed Midrule Actions is that tag is postposed in Lrama however it's preposed in Bison. Bison supports this feature from 3.1. ## Lrama 0.6.4 (2024-03-22) ### Parameterizing rules (preceded, terminated, delimited) Support `preceded`, `terminated` and `delimited` rules. ```text program: preceded(opening, X) // Expanded to program: preceded_opening_X preceded_opening_X: opening X ``` ``` program: terminated(X, closing) // Expanded to program: terminated_X_closing terminated_X_closing: X closing ``` ``` program: delimited(opening, X, closing) // Expanded to program: delimited_opening_X_closing delimited_opening_X_closing: opening X closing ``` https://github.com/ruby/lrama/pull/382 ### Support `%destructor` declaration User can set codes for freeing semantic value resources by using `%destructor`. In general, these resources are freed by actions or after parsing. However if syntax error happens in parsing, these codes may not be executed. Codes associated to `%destructor` are executed when semantic value is popped from the stack by an error. ```yacc %token NUM %type expr2 %type expr %destructor { printf("destructor for val1: %d\n", $$); } // printer for TAG %destructor { printf("destructor for val2: %d\n", $$); } %destructor { printf("destructor for expr: %d\n", $$); } expr // printer for symbol ``` Bison supports this feature from 1.75b. https://github.com/ruby/lrama/pull/385 ## Lrama 0.6.3 (2024-02-15) ### Bring Your Own Stack Provide functionalities for Bring Your Own Stack. Ruby’s Ripper library requires their own semantic value stack to manage Ruby Objects returned by user defined callback method. Currently Ripper uses semantic value stack (`yyvsa`) which is used by parser to manage Node. This hack introduces some limitation on Ripper. For example, Ripper can not execute semantic analysis depending on Node structure. Lrama introduces two features to support another semantic value stack by parser generator users. 1. Callback entry points User can emulate semantic value stack by these callbacks. Lrama provides these five callbacks. Registered functions are called when each event happen. For example %after-shift function is called when shift happens on original semantic value stack. - `%after-shift` function_name - `%before-reduce` function_name - `%after-reduce` function_name - `%after-shift-error-token` function_name - `%after-pop-stack` function_name 2. `$:n` variable to access index of each grammar symbols User also needs to access semantic value of their stack in grammar action. `$:n` provides the way to access to it. `$:n` is translated to the minus index from the top of the stack. For example ```yacc primary: k_if expr_value then compstmt if_tail k_end { /*% ripper: if!($:2, $:4, $:5) %*/ /* $:2 = -5, $:4 = -3, $:5 = -2. */ } ``` https://github.com/ruby/lrama/pull/367 ## Lrama 0.6.2 (2024-01-27) ### %no-stdlib directive If `%no-stdlib` directive is set, Lrama doesn't load Lrama standard library for parameterizing rules, stdlib.y. https://github.com/ruby/lrama/pull/344 ## Lrama 0.6.1 (2024-01-13) ### Nested parameterizing rules Allow to pass an instantiated rule to other parameterizing rules. ```yacc %rule constant(X) : X ; %rule option(Y) : /* empty */ | Y ; %% program : option(constant(number)) // Nested rule ; %% ``` Allow to use nested parameterizing rules when define parameterizing rules. ```yacc %rule option(x) : /* empty */ | X ; %rule double(Y) : Y Y ; %rule double_opt(A) : option(double(A)) // Nested rule ; %% program : double_opt(number) ; %% ``` https://github.com/ruby/lrama/pull/337 ## Lrama 0.6.0 (2023-12-25) ### User defined parameterizing rules Allow to define parameterizing rule by `%rule` directive. ```yacc %rule pair(X, Y): X Y { $$ = $1 + $2; } ; %% program: stmt ; stmt: pair(ODD, EVEN) | pair(EVEN, ODD) ; ``` https://github.com/ruby/lrama/pull/285 ## Lrama 0.5.11 (2023-12-02) ### Type specification of parameterizing rules Allow to specify type of rules by specifying tag, `` in below example. Tag is post-modification style. ```yacc %union { int i; } %% program : option(number) | number_alias? ; ``` https://github.com/ruby/lrama/pull/272 ## Lrama 0.5.10 (2023-11-18) ### Parameterizing rules (option, nonempty_list, list) Support function call style parameterizing rules for `option`, `nonempty_list` and `list`. https://github.com/ruby/lrama/pull/197 ### Parameterizing rules (separated_list) Support `separated_list` and `separated_nonempty_list` parameterizing rules. ```text program: separated_list(',', number) // Expanded to program: separated_list_number separated_list_number: ε separated_list_number: separated_nonempty_list_number separated_nonempty_list_number: number separated_nonempty_list_number: separated_nonempty_list_number ',' number ``` ``` program: separated_nonempty_list(',', number) // Expanded to program: separated_nonempty_list_number separated_nonempty_list_number: number separated_nonempty_list_number: separated_nonempty_list_number ',' number ``` https://github.com/ruby/lrama/pull/204 ## Lrama 0.5.9 (2023-11-05) ### Parameterizing rules (suffix) Parameterizing rules are template of rules. It's very common pattern to write "list" grammar rule like: ```yacc opt_args: /* none */ | args ; args: arg | args arg ``` Lrama supports these suffixes: - `?`: option - `+`: nonempty list - `*`: list Idea of Parameterizing rules comes from Menhir LR(1) parser generator (https://gallium.inria.fr/~fpottier/menhir/manual.html#sec32). https://github.com/ruby/lrama/pull/181 ## Lrama 0.5.7 (2023-10-23) ### Racc parser Replace Lrama's parser from hand written parser to LR parser generated by Racc. Lrama uses `--embedded` option to generate LR parser because Racc is changed from default gem to bundled gem by Ruby 3.3 (https://github.com/ruby/lrama/pull/132). https://github.com/ruby/lrama/pull/62 ## Lrama 0.5.4 (2023-08-17) ### Runtime configuration for error recovery Make error recovery function configurable on runtime by two new macros. - `YYMAXREPAIR`: Expected to return max length of repair operations. `%parse-param` is passed to this function. - `YYERROR_RECOVERY_ENABLED`: Expected to return bool value to determine error recovery is enabled or not. `%parse-param` is passed to this function. https://github.com/ruby/lrama/pull/74 ## Lrama 0.5.3 (2023-08-05) ### Error Recovery Support token insert base Error Recovery. `-e` option is needed to generate parser with error recovery functions. https://github.com/ruby/lrama/pull/44 ## Lrama 0.5.2 (2023-06-14) ### Named References Instead of positional references like `$1` or `$$`, named references allow to access to symbol by name. ```yacc primary: k_class cpath superclass bodystmt k_end { $primary = new_class($cpath, $bodystmt, $superclass); } ``` Alias name can be declared. ```yacc expr[result]: expr[ex-left] '+' expr[ex.right] { $result = $[ex-left] + $[ex.right]; } ``` Bison supports this feature from 2.5. ### Add parse params to some macros and functions `%parse-param` are added to these macros and functions to remove ytab.sed hack from Ruby. - `YY_LOCATION_PRINT` - `YY_SYMBOL_PRINT` - `yy_stack_print` - `YY_STACK_PRINT` - `YY_REDUCE_PRINT` - `yysyntax_error` https://github.com/ruby/lrama/pull/40 See also: https://github.com/ruby/ruby/pull/7807 ## Lrama 0.5.0 (2023-05-17) ### stdin mode When `-` is given as grammar file name, reads the grammar source from STDIN, and takes the next argument as the input file name. This mode helps pre-process a grammar source. https://github.com/ruby/lrama/pull/8 ## Lrama 0.4.0 (2023-05-13) This is the first version migrated to Ruby. This version generates "parse.c" compatible with Bison 3.8.2. nghttp2-1.68.0/third-party/mruby/tools/lrama/PaxHeaders/lib0000644000000000000000000000013215077107334020613 xustar0030 mtime=1761382108.333302598 30 atime=1761382109.799298361 30 ctime=1761382108.333302598 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/0000755000175100017510000000000015077107334021260 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/tools/lrama/lib/PaxHeaders/lrama0000644000000000000000000000013215077107334021707 xustar0030 mtime=1761382108.330302607 30 atime=1761382109.799298361 30 ctime=1761382108.330302607 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/0000755000175100017510000000000015077107334022354 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/grammar0000644000000000000000000000013215077107334023335 xustar0030 mtime=1761382108.283302743 30 atime=1761382109.799298361 30 ctime=1761382108.283302743 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/0000755000175100017510000000000015077107334024002 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/PaxHeaders/percent_code.rb0000644000000000000000000000013015077107276026374 xustar0029 mtime=1761382078.14342046 29 atime=1761382080.16541121 30 ctime=1761382108.268302786 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/percent_code.rb0000644000175100017510000000033115077107276026763 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Grammar class PercentCode attr_reader :name, :code def initialize(name, code) @name = name @code = code end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/PaxHeaders/union.rb0000644000000000000000000000013115077107276025073 xustar0029 mtime=1761382078.14342046 30 atime=1761382080.166411206 30 ctime=1761382108.257302818 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/union.rb0000644000175100017510000000036615077107276025471 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Grammar class Union < Struct.new(:code, :lineno, keyword_init: true) def braces_less_code # Braces is already removed by lexer code.s_value end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/PaxHeaders/symbol.rb0000644000000000000000000000013115077107276025250 xustar0029 mtime=1761382078.14342046 30 atime=1761382080.166411206 30 ctime=1761382108.271302777 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/symbol.rb0000644000175100017510000000520615077107276025644 0ustar00runnerrunner# frozen_string_literal: true # Symbol is both of nterm and term # `number` is both for nterm and term # `token_id` is tokentype for term, internal sequence number for nterm # # TODO: Add validation for ASCII code range for Token::Char module Lrama class Grammar class Symbol attr_accessor :id, :alias_name, :tag, :number, :token_id, :nullable, :precedence, :printer, :destructor, :error_token, :first_set, :first_set_bitmap attr_reader :term attr_writer :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol def initialize(id:, term:, alias_name: nil, number: nil, tag: nil, token_id: nil, nullable: nil, precedence: nil, printer: nil, destructor: nil) @id = id @alias_name = alias_name @number = number @tag = tag @term = term @token_id = token_id @nullable = nullable @precedence = precedence @printer = printer @destructor = destructor end def term? term end def nterm? !term end def eof_symbol? !!@eof_symbol end def error_symbol? !!@error_symbol end def undef_symbol? !!@undef_symbol end def accept_symbol? !!@accept_symbol end def display_name alias_name || id.s_value end # name for yysymbol_kind_t # # See: b4_symbol_kind_base # @type var name: String def enum_name case when accept_symbol? name = "YYACCEPT" when eof_symbol? name = "YYEOF" when term? && id.is_a?(Lrama::Lexer::Token::Char) name = number.to_s + display_name when term? && id.is_a?(Lrama::Lexer::Token::Ident) name = id.s_value when nterm? && (id.s_value.include?("$") || id.s_value.include?("@")) name = number.to_s + id.s_value when nterm? name = id.s_value else raise "Unexpected #{self}" end "YYSYMBOL_" + name.gsub(/\W+/, "_") end # comment for yysymbol_kind_t def comment case when accept_symbol? # YYSYMBOL_YYACCEPT id.s_value when eof_symbol? # YYEOF alias_name when (term? && 0 < token_id && token_id < 128) # YYSYMBOL_3_backslash_, YYSYMBOL_14_ alias_name || id.s_value when id.s_value.include?("$") || id.s_value.include?("@") # YYSYMBOL_21_1 id.s_value else # YYSYMBOL_keyword_class, YYSYMBOL_strings_1 alias_name || id.s_value end end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/PaxHeaders/symbols0000644000000000000000000000013215077107334025025 xustar0030 mtime=1761382108.262302803 30 atime=1761382109.799298361 30 ctime=1761382108.262302803 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/symbols/0000755000175100017510000000000015077107334025472 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/symbols/PaxHeaders/resolver.rb0000644000000000000000000000013115077107276027274 xustar0029 mtime=1761382078.14342046 30 atime=1761382080.166411206 30 ctime=1761382108.262302803 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/symbols/resolver.rb0000644000175100017510000002025415077107276027670 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Grammar class Symbols class Resolver attr_reader :terms, :nterms def initialize @terms = [] @nterms = [] end def symbols @symbols ||= (@terms + @nterms) end def sort_by_number! symbols.sort_by!(&:number) end def add_term(id:, alias_name: nil, tag: nil, token_id: nil, replace: false) if token_id && (sym = find_symbol_by_token_id(token_id)) if replace sym.id = id sym.alias_name = alias_name sym.tag = tag end return sym end if (sym = find_symbol_by_id(id)) return sym end @symbols = nil term = Symbol.new( id: id, alias_name: alias_name, number: nil, tag: tag, term: true, token_id: token_id, nullable: false ) @terms << term term end def add_nterm(id:, alias_name: nil, tag: nil) if (sym = find_symbol_by_id(id)) return sym end @symbols = nil nterm = Symbol.new( id: id, alias_name: alias_name, number: nil, tag: tag, term: false, token_id: nil, nullable: nil, ) @nterms << nterm nterm end def find_term_by_s_value(s_value) terms.find { |s| s.id.s_value == s_value } end def find_symbol_by_s_value(s_value) symbols.find { |s| s.id.s_value == s_value } end def find_symbol_by_s_value!(s_value) find_symbol_by_s_value(s_value) || (raise "Symbol not found. value: `#{s_value}`") end def find_symbol_by_id(id) symbols.find do |s| s.id == id || s.alias_name == id.s_value end end def find_symbol_by_id!(id) find_symbol_by_id(id) || (raise "Symbol not found. #{id}") end def find_symbol_by_token_id(token_id) symbols.find {|s| s.token_id == token_id } end def find_symbol_by_number!(number) sym = symbols[number] raise "Symbol not found. number: `#{number}`" unless sym raise "[BUG] Symbol number mismatch. #{number}, #{sym}" if sym.number != number sym end def fill_symbol_number # YYEMPTY = -2 # YYEOF = 0 # YYerror = 1 # YYUNDEF = 2 @number = 3 fill_terms_number fill_nterms_number end def fill_nterm_type(types) types.each do |type| nterm = find_nterm_by_id!(type.id) nterm.tag = type.tag end end def fill_printer(printers) symbols.each do |sym| printers.each do |printer| printer.ident_or_tags.each do |ident_or_tag| case ident_or_tag when Lrama::Lexer::Token::Ident sym.printer = printer if sym.id == ident_or_tag when Lrama::Lexer::Token::Tag sym.printer = printer if sym.tag == ident_or_tag else raise "Unknown token type. #{printer}" end end end end end def fill_destructor(destructors) symbols.each do |sym| destructors.each do |destructor| destructor.ident_or_tags.each do |ident_or_tag| case ident_or_tag when Lrama::Lexer::Token::Ident sym.destructor = destructor if sym.id == ident_or_tag when Lrama::Lexer::Token::Tag sym.destructor = destructor if sym.tag == ident_or_tag else raise "Unknown token type. #{destructor}" end end end end end def fill_error_token(error_tokens) symbols.each do |sym| error_tokens.each do |token| token.ident_or_tags.each do |ident_or_tag| case ident_or_tag when Lrama::Lexer::Token::Ident sym.error_token = token if sym.id == ident_or_tag when Lrama::Lexer::Token::Tag sym.error_token = token if sym.tag == ident_or_tag else raise "Unknown token type. #{token}" end end end end end def token_to_symbol(token) case token when Lrama::Lexer::Token find_symbol_by_id!(token) else raise "Unknown class: #{token}" end end def validate! validate_number_uniqueness! validate_alias_name_uniqueness! end private def find_nterm_by_id!(id) @nterms.find do |s| s.id == id end || (raise "Symbol not found. #{id}") end def fill_terms_number # Character literal in grammar file has # token id corresponding to ASCII code by default, # so start token_id from 256. token_id = 256 @terms.each do |sym| while used_numbers[@number] do @number += 1 end if sym.number.nil? sym.number = @number used_numbers[@number] = true @number += 1 end # If id is Token::Char, it uses ASCII code if sym.token_id.nil? if sym.id.is_a?(Lrama::Lexer::Token::Char) # Ignore ' on the both sides case sym.id.s_value[1..-2] when "\\b" sym.token_id = 8 when "\\f" sym.token_id = 12 when "\\n" sym.token_id = 10 when "\\r" sym.token_id = 13 when "\\t" sym.token_id = 9 when "\\v" sym.token_id = 11 when "\"" sym.token_id = 34 when "'" sym.token_id = 39 when "\\\\" sym.token_id = 92 when /\A\\(\d+)\z/ unless (id = Integer($1, 8)).nil? sym.token_id = id else raise "Unknown Char s_value #{sym}" end when /\A(.)\z/ unless (id = $1&.bytes&.first).nil? sym.token_id = id else raise "Unknown Char s_value #{sym}" end else raise "Unknown Char s_value #{sym}" end else sym.token_id = token_id token_id += 1 end end end end def fill_nterms_number token_id = 0 @nterms.each do |sym| while used_numbers[@number] do @number += 1 end if sym.number.nil? sym.number = @number used_numbers[@number] = true @number += 1 end if sym.token_id.nil? sym.token_id = token_id token_id += 1 end end end def used_numbers return @used_numbers if defined?(@used_numbers) @used_numbers = {} symbols.map(&:number).each do |n| @used_numbers[n] = true end @used_numbers end def validate_number_uniqueness! invalid = symbols.group_by(&:number).select do |number, syms| syms.count > 1 end return if invalid.empty? raise "Symbol number is duplicated. #{invalid}" end def validate_alias_name_uniqueness! invalid = symbols.select(&:alias_name).group_by(&:alias_name).select do |alias_name, syms| syms.count > 1 end return if invalid.empty? raise "Symbol alias name is duplicated. #{invalid}" end end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/PaxHeaders/symbols.rb0000644000000000000000000000013115077107276025433 xustar0029 mtime=1761382078.14342046 30 atime=1761382080.166411206 30 ctime=1761382108.264302798 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/symbols.rb0000644000175100017510000000010315077107276026016 0ustar00runnerrunner# frozen_string_literal: true require_relative "symbols/resolver" nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/PaxHeaders/parameterizing_rule.rb0000644000000000000000000000013115077107276030013 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 30 ctime=1761382108.281302748 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/parameterizing_rule.rb0000644000175100017510000000024615077107276030406 0ustar00runnerrunner# frozen_string_literal: true require_relative 'parameterizing_rule/resolver' require_relative 'parameterizing_rule/rhs' require_relative 'parameterizing_rule/rule' nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/PaxHeaders/rule_builder.rb0000644000000000000000000000013115077107276026420 xustar0029 mtime=1761382078.14342046 30 atime=1761382080.166411206 30 ctime=1761382108.273302772 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/rule_builder.rb0000644000175100017510000002163115077107276027014 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Grammar class RuleBuilder attr_accessor :lhs, :line attr_reader :lhs_tag, :rhs, :user_code, :precedence_sym def initialize(rule_counter, midrule_action_counter, parameterizing_rule_resolver, position_in_original_rule_rhs = nil, lhs_tag: nil, skip_preprocess_references: false) @rule_counter = rule_counter @midrule_action_counter = midrule_action_counter @parameterizing_rule_resolver = parameterizing_rule_resolver @position_in_original_rule_rhs = position_in_original_rule_rhs @skip_preprocess_references = skip_preprocess_references @lhs = nil @lhs_tag = lhs_tag @rhs = [] @user_code = nil @precedence_sym = nil @line = nil @rules = [] @rule_builders_for_parameterizing_rules = [] @rule_builders_for_derived_rules = [] @parameterizing_rules = [] @midrule_action_rules = [] end def add_rhs(rhs) @line ||= rhs.line flush_user_code @rhs << rhs end def user_code=(user_code) @line ||= user_code&.line flush_user_code @user_code = user_code end def precedence_sym=(precedence_sym) flush_user_code @precedence_sym = precedence_sym end def complete_input freeze_rhs end def setup_rules preprocess_references unless @skip_preprocess_references process_rhs build_rules end def rules @parameterizing_rules + @midrule_action_rules + @rules end def has_inline_rules? rhs.any? { |token| @parameterizing_rule_resolver.find_inline(token) } end def resolve_inline_rules resolved_builders = [] #: Array[RuleBuilder] rhs.each_with_index do |token, i| if (inline_rule = @parameterizing_rule_resolver.find_inline(token)) inline_rule.rhs_list.each do |inline_rhs| rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, @parameterizing_rule_resolver, lhs_tag: lhs_tag) if token.is_a?(Lexer::Token::InstantiateRule) resolve_inline_rhs(rule_builder, inline_rhs, i, Binding.new(inline_rule.parameters, token.args)) else resolve_inline_rhs(rule_builder, inline_rhs, i) end rule_builder.lhs = lhs rule_builder.line = line rule_builder.precedence_sym = precedence_sym rule_builder.user_code = replace_inline_user_code(inline_rhs, i) resolved_builders << rule_builder end break end end resolved_builders end private def freeze_rhs @rhs.freeze end def preprocess_references numberize_references end def build_rules tokens = @replaced_rhs rule = Rule.new( id: @rule_counter.increment, _lhs: lhs, _rhs: tokens, lhs_tag: lhs_tag, token_code: user_code, position_in_original_rule_rhs: @position_in_original_rule_rhs, precedence_sym: precedence_sym, lineno: line ) @rules = [rule] @parameterizing_rules = @rule_builders_for_parameterizing_rules.map do |rule_builder| rule_builder.rules end.flatten @midrule_action_rules = @rule_builders_for_derived_rules.map do |rule_builder| rule_builder.rules end.flatten @midrule_action_rules.each do |r| r.original_rule = rule end end # rhs is a mixture of variety type of tokens like `Ident`, `InstantiateRule`, `UserCode` and so on. # `#process_rhs` replaces some kind of tokens to `Ident` so that all `@replaced_rhs` are `Ident` or `Char`. def process_rhs return if @replaced_rhs @replaced_rhs = [] rhs.each_with_index do |token, i| case token when Lrama::Lexer::Token::Char @replaced_rhs << token when Lrama::Lexer::Token::Ident @replaced_rhs << token when Lrama::Lexer::Token::InstantiateRule parameterizing_rule = @parameterizing_rule_resolver.find_rule(token) raise "Unexpected token. #{token}" unless parameterizing_rule bindings = Binding.new(parameterizing_rule.parameters, token.args) lhs_s_value = bindings.concatenated_args_str(token) if (created_lhs = @parameterizing_rule_resolver.created_lhs(lhs_s_value)) @replaced_rhs << created_lhs else lhs_token = Lrama::Lexer::Token::Ident.new(s_value: lhs_s_value, location: token.location) @replaced_rhs << lhs_token @parameterizing_rule_resolver.created_lhs_list << lhs_token parameterizing_rule.rhs_list.each do |r| rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, @parameterizing_rule_resolver, lhs_tag: token.lhs_tag || parameterizing_rule.tag) rule_builder.lhs = lhs_token r.symbols.each { |sym| rule_builder.add_rhs(bindings.resolve_symbol(sym)) } rule_builder.line = line rule_builder.precedence_sym = r.precedence_sym rule_builder.user_code = r.resolve_user_code(bindings) rule_builder.complete_input rule_builder.setup_rules @rule_builders_for_parameterizing_rules << rule_builder end end when Lrama::Lexer::Token::UserCode prefix = token.referred ? "@" : "$@" tag = token.tag || lhs_tag new_token = Lrama::Lexer::Token::Ident.new(s_value: prefix + @midrule_action_counter.increment.to_s) @replaced_rhs << new_token rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, @parameterizing_rule_resolver, i, lhs_tag: tag, skip_preprocess_references: true) rule_builder.lhs = new_token rule_builder.user_code = token rule_builder.complete_input rule_builder.setup_rules @rule_builders_for_derived_rules << rule_builder else raise "Unexpected token. #{token}" end end end def resolve_inline_rhs(rule_builder, inline_rhs, index, bindings = nil) rhs.each_with_index do |token, i| if index == i inline_rhs.symbols.each { |sym| rule_builder.add_rhs(bindings.nil? ? sym : bindings.resolve_symbol(sym)) } else rule_builder.add_rhs(token) end end end def replace_inline_user_code(inline_rhs, index) return user_code if inline_rhs.user_code.nil? return user_code if user_code.nil? code = user_code.s_value.gsub(/\$#{index + 1}/, inline_rhs.user_code.s_value) user_code.references.each do |ref| next if ref.index.nil? || ref.index <= index # nil is a case for `$$` code = code.gsub(/\$#{ref.index}/, "$#{ref.index + (inline_rhs.symbols.count-1)}") code = code.gsub(/@#{ref.index}/, "@#{ref.index + (inline_rhs.symbols.count-1)}") end Lrama::Lexer::Token::UserCode.new(s_value: code, location: user_code.location) end def numberize_references # Bison n'th component is 1-origin (rhs + [user_code]).compact.each.with_index(1) do |token, i| next unless token.is_a?(Lrama::Lexer::Token::UserCode) token.references.each do |ref| ref_name = ref.name if ref_name if ref_name == '$' ref.name = '$' else candidates = ([lhs] + rhs).each_with_index.select {|token, _i| token.referred_by?(ref_name) } if candidates.size >= 2 token.invalid_ref(ref, "Referring symbol `#{ref_name}` is duplicated.") end unless (referring_symbol = candidates.first) token.invalid_ref(ref, "Referring symbol `#{ref_name}` is not found.") end if referring_symbol[1] == 0 # Refers to LHS ref.name = '$' else ref.number = referring_symbol[1] end end end if ref.number ref.index = ref.number end # TODO: Need to check index of @ too? next if ref.type == :at if ref.index # TODO: Prohibit $0 even so Bison allows it? # See: https://www.gnu.org/software/bison/manual/html_node/Actions.html token.invalid_ref(ref, "Can not refer following component. #{ref.index} >= #{i}.") if ref.index >= i rhs[ref.index - 1].referred = true end end end end def flush_user_code if (c = @user_code) @rhs << c @user_code = nil end end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/PaxHeaders/destructor.rb0000644000000000000000000000013115077107276026141 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 30 ctime=1761382108.258302815 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/destructor.rb0000644000175100017510000000047115077107276026534 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Grammar class Destructor < Struct.new(:ident_or_tags, :token_code, :lineno, keyword_init: true) def translated_code(tag) Code::DestructorCode.new(type: :destructor, token_code: token_code, tag: tag).translated_code end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/PaxHeaders/reference.rb0000644000000000000000000000013015077107276025700 xustar0029 mtime=1761382078.14342046 29 atime=1761382080.16541121 30 ctime=1761382108.261302806 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/reference.rb0000644000175100017510000000063415077107276026275 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Grammar # type: :dollar or :at # name: String (e.g. $$, $foo, $expr.right) # number: Integer (e.g. $1) # index: Integer # ex_tag: "$1" (Optional) class Reference < Struct.new(:type, :name, :number, :index, :ex_tag, :first_column, :last_column, keyword_init: true) def value name || number end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/PaxHeaders/code.rb0000644000000000000000000000013115077107276024655 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 30 ctime=1761382108.276302763 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/code.rb0000644000175100017510000000225715077107276025254 0ustar00runnerrunner# frozen_string_literal: true require "forwardable" require_relative "code/destructor_code" require_relative "code/initial_action_code" require_relative "code/no_reference_code" require_relative "code/printer_code" require_relative "code/rule_action" module Lrama class Grammar class Code extend Forwardable def_delegators "token_code", :s_value, :line, :column, :references attr_reader :type, :token_code def initialize(type:, token_code:) @type = type @token_code = token_code end def ==(other) self.class == other.class && self.type == other.type && self.token_code == other.token_code end # $$, $n, @$, @n are translated to C code def translated_code t_code = s_value.dup references.reverse_each do |ref| first_column = ref.first_column last_column = ref.last_column str = reference_to_c(ref) t_code[first_column...last_column] = str end return t_code end private def reference_to_c(ref) raise NotImplementedError.new("#reference_to_c is not implemented") end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/PaxHeaders/code0000644000000000000000000000013215077107334024247 xustar0030 mtime=1761382108.255302824 30 atime=1761382109.799298361 30 ctime=1761382108.255302824 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/code/0000755000175100017510000000000015077107334024714 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/code/PaxHeaders/initial_action_code.r0000644000000000000000000000013115077107276030473 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 30 ctime=1761382108.252302832 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/code/initial_action_code.rb0000644000175100017510000000202415077107276031224 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Grammar class Code class InitialActionCode < Code private # * ($$) yylval # * (@$) yylloc # * ($:$) error # * ($1) error # * (@1) error # * ($:1) error def reference_to_c(ref) case when ref.type == :dollar && ref.name == "$" # $$ "yylval" when ref.type == :at && ref.name == "$" # @$ "yylloc" when ref.type == :index && ref.name == "$" # $:$ raise "$:#{ref.value} can not be used in initial_action." when ref.type == :dollar # $n raise "$#{ref.value} can not be used in initial_action." when ref.type == :at # @n raise "@#{ref.value} can not be used in initial_action." when ref.type == :index # $:n raise "$:#{ref.value} can not be used in initial_action." else raise "Unexpected. #{self}, #{ref}" end end end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/code/PaxHeaders/rule_action.rb0000644000000000000000000000013115077107276027161 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 30 ctime=1761382108.250302838 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/code/rule_action.rb0000644000175100017510000000624615077107276027562 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Grammar class Code class RuleAction < Code def initialize(type:, token_code:, rule:) super(type: type, token_code: token_code) @rule = rule end private # * ($$) yyval # * (@$) yyloc # * ($:$) error # * ($1) yyvsp[i] # * (@1) yylsp[i] # * ($:1) i - 1 # # # Consider a rule like # # class: keyword_class { $1 } tSTRING { $2 + $3 } keyword_end { $class = $1 + $keyword_end } # # For the semantic action of original rule: # # "Rule" class: keyword_class { $1 } tSTRING { $2 + $3 } keyword_end { $class = $1 + $keyword_end } # "Position in grammar" $1 $2 $3 $4 $5 # "Index for yyvsp" -4 -3 -2 -1 0 # "$:n" $:1 $:2 $:3 $:4 $:5 # "index of $:n" -5 -4 -3 -2 -1 # # # For the first midrule action: # # "Rule" class: keyword_class { $1 } tSTRING { $2 + $3 } keyword_end { $class = $1 + $keyword_end } # "Position in grammar" $1 # "Index for yyvsp" 0 # "$:n" $:1 def reference_to_c(ref) case when ref.type == :dollar && ref.name == "$" # $$ tag = ref.ex_tag || lhs.tag raise_tag_not_found_error(ref) unless tag # @type var tag: Lexer::Token::Tag "(yyval.#{tag.member})" when ref.type == :at && ref.name == "$" # @$ "(yyloc)" when ref.type == :index && ref.name == "$" # $:$ raise "$:$ is not supported" when ref.type == :dollar # $n i = -position_in_rhs + ref.index tag = ref.ex_tag || rhs[ref.index - 1].tag raise_tag_not_found_error(ref) unless tag # @type var tag: Lexer::Token::Tag "(yyvsp[#{i}].#{tag.member})" when ref.type == :at # @n i = -position_in_rhs + ref.index "(yylsp[#{i}])" when ref.type == :index # $:n i = -position_in_rhs + ref.index "(#{i} - 1)" else raise "Unexpected. #{self}, #{ref}" end end def position_in_rhs # If rule is not derived rule, User Code is only action at # the end of rule RHS. In such case, the action is located on # `@rule.rhs.count`. @rule.position_in_original_rule_rhs || @rule.rhs.count end # If this is midrule action, RHS is an RHS of the original rule. def rhs (@rule.original_rule || @rule).rhs end # Unlike `rhs`, LHS is always an LHS of the rule. def lhs @rule.lhs end def raise_tag_not_found_error(ref) raise "Tag is not specified for '$#{ref.value}' in '#{@rule.display_name}'" end end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/code/PaxHeaders/printer_code.rb0000644000000000000000000000013015077107276027331 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 29 ctime=1761382108.25330283 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/code/printer_code.rb0000644000175100017510000000227415077107276027730 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Grammar class Code class PrinterCode < Code def initialize(type:, token_code:, tag:) super(type: type, token_code: token_code) @tag = tag end private # * ($$) *yyvaluep # * (@$) *yylocationp # * ($:$) error # * ($1) error # * (@1) error # * ($:1) error def reference_to_c(ref) case when ref.type == :dollar && ref.name == "$" # $$ member = @tag.member "((*yyvaluep).#{member})" when ref.type == :at && ref.name == "$" # @$ "(*yylocationp)" when ref.type == :index && ref.name == "$" # $:$ raise "$:#{ref.value} can not be used in #{type}." when ref.type == :dollar # $n raise "$#{ref.value} can not be used in #{type}." when ref.type == :at # @n raise "@#{ref.value} can not be used in #{type}." when ref.type == :index # $:n raise "$:#{ref.value} can not be used in #{type}." else raise "Unexpected. #{self}, #{ref}" end end end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/code/PaxHeaders/destructor_code.rb0000644000000000000000000000013115077107276030045 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 30 ctime=1761382108.255302824 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/code/destructor_code.rb0000644000175100017510000000227715077107276030446 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Grammar class Code class DestructorCode < Code def initialize(type:, token_code:, tag:) super(type: type, token_code: token_code) @tag = tag end private # * ($$) *yyvaluep # * (@$) *yylocationp # * ($:$) error # * ($1) error # * (@1) error # * ($:1) error def reference_to_c(ref) case when ref.type == :dollar && ref.name == "$" # $$ member = @tag.member "((*yyvaluep).#{member})" when ref.type == :at && ref.name == "$" # @$ "(*yylocationp)" when ref.type == :index && ref.name == "$" # $:$ raise "$:#{ref.value} can not be used in #{type}." when ref.type == :dollar # $n raise "$#{ref.value} can not be used in #{type}." when ref.type == :at # @n raise "@#{ref.value} can not be used in #{type}." when ref.type == :index # $:n raise "$:#{ref.value} can not be used in #{type}." else raise "Unexpected. #{self}, #{ref}" end end end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/code/PaxHeaders/no_reference_code.rb0000644000000000000000000000013115077107276030301 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 30 ctime=1761382108.254302827 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/code/no_reference_code.rb0000644000175100017510000000135315077107276030674 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Grammar class Code class NoReferenceCode < Code private # * ($$) error # * (@$) error # * ($:$) error # * ($1) error # * (@1) error # * ($:1) error def reference_to_c(ref) case when ref.type == :dollar # $$, $n raise "$#{ref.value} can not be used in #{type}." when ref.type == :at # @$, @n raise "@#{ref.value} can not be used in #{type}." when ref.type == :index # $:$, $:n raise "$:#{ref.value} can not be used in #{type}." else raise "Unexpected. #{self}, #{ref}" end end end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/PaxHeaders/parameterizing_rule0000644000000000000000000000013215077107334027405 xustar0030 mtime=1761382108.280302752 30 atime=1761382109.799298361 30 ctime=1761382108.280302752 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/parameterizing_rule/0000755000175100017510000000000015077107334030052 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/parameterizing_rule/PaxHeaders/rhs.rb0000644000000000000000000000013115077107276030607 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 30 ctime=1761382108.279302754 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/parameterizing_rule/rhs.rb0000644000175100017510000000174415077107276031206 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Grammar class ParameterizingRule class Rhs attr_accessor :symbols, :user_code, :precedence_sym def initialize @symbols = [] @user_code = nil @precedence_sym = nil end def resolve_user_code(bindings) return unless user_code resolved = Lexer::Token::UserCode.new(s_value: user_code.s_value, location: user_code.location) var_to_arg = {} #: Hash[String, String] symbols.each do |sym| resolved_sym = bindings.resolve_symbol(sym) if resolved_sym != sym var_to_arg[sym.s_value] = resolved_sym.s_value end end var_to_arg.each do |var, arg| resolved.references.each do |ref| if ref.name == var ref.name = arg end end end return resolved end end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/parameterizing_rule/PaxHeaders/rule.r0000644000000000000000000000013115077107276030620 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 30 ctime=1761382108.280302752 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/parameterizing_rule/rule.rb0000644000175100017510000000114615077107276031355 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Grammar class ParameterizingRule class Rule attr_reader :name, :parameters, :rhs_list, :required_parameters_count, :tag, :is_inline def initialize(name, parameters, rhs_list, tag: nil, is_inline: false) @name = name @parameters = parameters @rhs_list = rhs_list @tag = tag @is_inline = is_inline @required_parameters_count = parameters.count end def to_s "#{@name}(#{@parameters.map(&:s_value).join(', ')})" end end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/parameterizing_rule/PaxHeaders/resolv0000644000000000000000000000013015077107276030722 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 29 ctime=1761382108.27730276 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/parameterizing_rule/resolver.rb0000644000175100017510000000324215077107276032246 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Grammar class ParameterizingRule class Resolver attr_accessor :rules, :created_lhs_list def initialize @rules = [] @created_lhs_list = [] end def add_parameterizing_rule(rule) @rules << rule end def find_rule(token) select_rules(@rules, token).last end def find_inline(token) @rules.reverse.find { |rule| rule.name == token.s_value && rule.is_inline } end def created_lhs(lhs_s_value) @created_lhs_list.reverse.find { |created_lhs| created_lhs.s_value == lhs_s_value } end def redefined_rules @rules.select { |rule| @rules.count { |r| r.name == rule.name && r.required_parameters_count == rule.required_parameters_count } > 1 } end private def select_rules(rules, token) rules = select_not_inline_rules(rules) rules = select_rules_by_name(rules, token.rule_name) rules = rules.select { |rule| rule.required_parameters_count == token.args_count } if rules.empty? raise "Invalid number of arguments. `#{token.rule_name}`" else rules end end def select_not_inline_rules(rules) rules.select { |rule| !rule.is_inline } end def select_rules_by_name(rules, rule_name) rules = rules.select { |rule| rule.name == rule_name } if rules.empty? raise "Parameterizing rule does not exist. `#{rule_name}`" else rules end end end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/PaxHeaders/rule.rb0000644000000000000000000000012715077107276024717 xustar0029 mtime=1761382078.14342046 29 atime=1761382080.16541121 29 ctime=1761382108.27030278 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/rule.rb0000644000175100017510000000372015077107276025305 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Grammar # _rhs holds original RHS element. Use rhs to refer to Symbol. class Rule < Struct.new(:id, :_lhs, :lhs, :lhs_tag, :_rhs, :rhs, :token_code, :position_in_original_rule_rhs, :nullable, :precedence_sym, :lineno, keyword_init: true) attr_accessor :original_rule def ==(other) self.class == other.class && self.lhs == other.lhs && self.lhs_tag == other.lhs_tag && self.rhs == other.rhs && self.token_code == other.token_code && self.position_in_original_rule_rhs == other.position_in_original_rule_rhs && self.nullable == other.nullable && self.precedence_sym == other.precedence_sym && self.lineno == other.lineno end def display_name l = lhs.id.s_value r = empty_rule? ? "ε" : rhs.map {|r| r.id.s_value }.join(" ") "#{l} -> #{r}" end def display_name_without_action l = lhs.id.s_value r = empty_rule? ? "ε" : rhs.map do |r| r.id.s_value if r.first_set.any? end.compact.join(" ") "#{l} -> #{r}" end # Used by #user_actions def as_comment l = lhs.id.s_value r = empty_rule? ? "%empty" : rhs.map(&:display_name).join(" ") "#{l}: #{r}" end def with_actions "#{display_name} {#{token_code&.s_value}}" end # opt_nl: ε <-- empty_rule # | '\n' <-- not empty_rule def empty_rule? rhs.empty? end def precedence precedence_sym&.precedence end def initial_rule? id == 0 end def translated_code return nil unless token_code Code::RuleAction.new(type: :rule_action, token_code: token_code, rule: self).translated_code end def contains_at_reference? return false unless token_code token_code.references.any? {|r| r.type == :at } end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/PaxHeaders/auxiliary.rb0000644000000000000000000000013115077107276025752 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 30 ctime=1761382108.263302801 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/auxiliary.rb0000644000175100017510000000041115077107276026337 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Grammar # Grammar file information not used by States but by Output class Auxiliary < Struct.new(:prologue_first_lineno, :prologue, :epilogue_first_lineno, :epilogue, keyword_init: true) end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/PaxHeaders/precedence.rb0000644000000000000000000000013015077107276026037 xustar0029 mtime=1761382078.14342046 29 atime=1761382080.16541121 30 ctime=1761382108.283302743 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/precedence.rb0000644000175100017510000000037615077107276026437 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Grammar class Precedence < Struct.new(:type, :precedence, keyword_init: true) include Comparable def <=>(other) self.precedence <=> other.precedence end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/PaxHeaders/counter.rb0000644000000000000000000000013115077107276025422 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 30 ctime=1761382108.249302841 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/counter.rb0000644000175100017510000000036215077107276026014 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Grammar class Counter def initialize(number) @number = number end def increment n = @number @number += 1 n end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/PaxHeaders/stdlib.y0000644000000000000000000000013115077107276025071 xustar0029 mtime=1761382078.14342046 30 atime=1761382080.166411206 30 ctime=1761382108.266302792 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/stdlib.y0000644000175100017510000000555615077107276025475 0ustar00runnerrunner/********************************************************************** stdlib.y This is lrama's standard library. It provides a number of parameterizing rule definitions, such as options and lists, that should be useful in a number of situations. **********************************************************************/ // ------------------------------------------------------------------- // Options /* * program: option(number) * * => * * program: option_number * option_number: %empty * option_number: number */ %rule option(X): /* empty */ | X ; // ------------------------------------------------------------------- // Sequences /* * program: preceded(opening, X) * * => * * program: preceded_opening_X * preceded_opening_X: opening X */ %rule preceded(opening, X): opening X { $$ = $2; } ; /* * program: terminated(X, closing) * * => * * program: terminated_X_closing * terminated_X_closing: X closing */ %rule terminated(X, closing): X closing { $$ = $1; } ; /* * program: delimited(opening, X, closing) * * => * * program: delimited_opening_X_closing * delimited_opening_X_closing: opening X closing */ %rule delimited(opening, X, closing): opening X closing { $$ = $2; } ; // ------------------------------------------------------------------- // Lists /* * program: list(number) * * => * * program: list_number * list_number: %empty * list_number: list_number number */ %rule list(X): /* empty */ | list(X) X ; /* * program: nonempty_list(number) * * => * * program: nonempty_list_number * nonempty_list_number: number * nonempty_list_number: nonempty_list_number number */ %rule nonempty_list(X): X | nonempty_list(X) X ; /* * program: separated_nonempty_list(comma, number) * * => * * program: separated_nonempty_list_comma_number * separated_nonempty_list_comma_number: number * separated_nonempty_list_comma_number: separated_nonempty_list_comma_number comma number */ %rule separated_nonempty_list(separator, X): X | separated_nonempty_list(separator, X) separator X ; /* * program: separated_list(comma, number) * * => * * program: separated_list_comma_number * separated_list_comma_number: option_separated_nonempty_list_comma_number * option_separated_nonempty_list_comma_number: %empty * option_separated_nonempty_list_comma_number: separated_nonempty_list_comma_number * separated_nonempty_list_comma_number: number * separated_nonempty_list_comma_number: comma separated_nonempty_list_comma_number number */ %rule separated_list(separator, X): option(separated_nonempty_list(separator, X)) ; %% %union{}; nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/PaxHeaders/printer.rb0000644000000000000000000000013015077107276025425 xustar0029 mtime=1761382078.14342046 29 atime=1761382080.16541121 30 ctime=1761382108.275302766 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/printer.rb0000644000175100017510000000046015077107276026017 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Grammar class Printer < Struct.new(:ident_or_tags, :token_code, :lineno, keyword_init: true) def translated_code(tag) Code::PrinterCode.new(type: :printer, token_code: token_code, tag: tag).translated_code end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/PaxHeaders/type.rb0000644000000000000000000000013115077107276024724 xustar0029 mtime=1761382078.14342046 30 atime=1761382080.166411206 30 ctime=1761382108.267302789 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/type.rb0000644000175100017510000000051115077107276025312 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Grammar class Type attr_reader :id, :tag def initialize(id:, tag:) @id = id @tag = tag end def ==(other) self.class == other.class && self.id == other.id && self.tag == other.tag end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/PaxHeaders/error_token.rb0000644000000000000000000000013115077107276026274 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 30 ctime=1761382108.272302775 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/error_token.rb0000644000175100017510000000046715077107276026674 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Grammar class ErrorToken < Struct.new(:ident_or_tags, :token_code, :lineno, keyword_init: true) def translated_code(tag) Code::PrinterCode.new(type: :error_token, token_code: token_code, tag: tag).translated_code end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/PaxHeaders/binding.rb0000644000000000000000000000013115077107276025355 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 30 ctime=1761382108.259302812 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar/binding.rb0000644000175100017510000000377615077107276025763 0ustar00runnerrunner# rbs_inline: enabled # frozen_string_literal: true module Lrama class Grammar class Binding # @rbs @actual_args: Array[Lexer::Token] # @rbs @param_to_arg: Hash[String, Lexer::Token] # @rbs (Array[Lexer::Token] params, Array[Lexer::Token] actual_args) -> void def initialize(params, actual_args) @actual_args = actual_args @param_to_arg = map_params_to_args(params, @actual_args) end # @rbs (Lexer::Token sym) -> Lexer::Token def resolve_symbol(sym) if sym.is_a?(Lexer::Token::InstantiateRule) Lrama::Lexer::Token::InstantiateRule.new( s_value: sym.s_value, location: sym.location, args: resolved_args(sym), lhs_tag: sym.lhs_tag ) else param_to_arg(sym) end end # @rbs (Lexer::Token::InstantiateRule token) -> String def concatenated_args_str(token) "#{token.rule_name}_#{token_to_args_s_values(token).join('_')}" end private # @rbs (Array[Lexer::Token] params, Array[Lexer::Token] actual_args) -> Hash[String, Lexer::Token] def map_params_to_args(params, actual_args) params.zip(actual_args).map do |param, arg| [param.s_value, arg] end.to_h end # @rbs (Lexer::Token::InstantiateRule sym) -> Array[Lexer::Token] def resolved_args(sym) sym.args.map { |arg| resolve_symbol(arg) } end # @rbs (Lexer::Token sym) -> Lexer::Token def param_to_arg(sym) if (arg = @param_to_arg[sym.s_value].dup) arg.alias_name = sym.alias_name end arg || sym end # @rbs (Lexer::Token::InstantiateRule token) -> Array[String] def token_to_args_s_values(token) token.args.flat_map do |arg| resolved = resolve_symbol(arg) if resolved.is_a?(Lexer::Token::InstantiateRule) [resolved.s_value] + resolved.args.map(&:s_value) else [resolved.s_value] end end end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/command.rb0000644000000000000000000000013115077107276023733 xustar0030 mtime=1761382078.141420469 29 atime=1761382080.16541121 30 ctime=1761382108.307302674 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/command.rb0000644000175100017510000000441715077107276024332 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Command LRAMA_LIB = File.realpath(File.join(File.dirname(__FILE__))) STDLIB_FILE_PATH = File.join(LRAMA_LIB, 'grammar', 'stdlib.y') def run(argv) begin options = OptionParser.new.parse(argv) rescue => e message = e.message message = message.gsub(/.+/, "\e[1m\\&\e[m") if Exception.to_tty? abort message end Report::Duration.enable if options.trace_opts[:time] text = options.y.read options.y.close if options.y != STDIN begin grammar = Lrama::Parser.new(text, options.grammar_file, options.debug, options.define).parse unless grammar.no_stdlib stdlib_grammar = Lrama::Parser.new(File.read(STDLIB_FILE_PATH), STDLIB_FILE_PATH, options.debug).parse grammar.insert_before_parameterizing_rules(stdlib_grammar.parameterizing_rules) end grammar.prepare grammar.validate! rescue => e raise e if options.debug message = e.message message = message.gsub(/.+/, "\e[1m\\&\e[m") if Exception.to_tty? abort message end states = Lrama::States.new(grammar, trace_state: (options.trace_opts[:automaton] || options.trace_opts[:closure])) states.compute states.compute_ielr if grammar.ielr_defined? context = Lrama::Context.new(states) if options.report_file reporter = Lrama::StatesReporter.new(states) File.open(options.report_file, "w+") do |f| reporter.report(f, **options.report_opts) end end reporter = Lrama::TraceReporter.new(grammar) reporter.report(**options.trace_opts) File.open(options.outfile, "w+") do |f| Lrama::Output.new( out: f, output_file_path: options.outfile, template_name: options.skeleton, grammar_file_path: options.grammar_file, header_file_path: options.header_file, context: context, grammar: grammar, error_recovery: options.error_recovery, ).render end logger = Lrama::Logger.new exit false unless Lrama::GrammarValidator.new(grammar, states, logger).valid? Lrama::Diagnostics.new(grammar, states, logger).run(options.diagnostic) end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/report.rb0000644000000000000000000000013215077107276023631 xustar0030 mtime=1761382078.144420456 30 atime=1761382080.167411201 30 ctime=1761382108.330302607 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/report.rb0000644000175100017510000000014415077107276024220 0ustar00runnerrunner# frozen_string_literal: true require_relative 'report/duration' require_relative 'report/profile' nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/logger.rb0000644000000000000000000000013015077107276023573 xustar0029 mtime=1761382078.14342046 30 atime=1761382080.166411206 29 ctime=1761382108.30830267 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/logger.rb0000644000175100017510000000056415077107276024172 0ustar00runnerrunner# rbs_inline: enabled # frozen_string_literal: true module Lrama class Logger # @rbs (IO out) -> void def initialize(out = STDERR) @out = out end # @rbs (String message) -> void def warn(message) @out << message << "\n" end # @rbs (String message) -> void def error(message) @out << message << "\n" end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/states.rb0000644000000000000000000000013215077107276023621 xustar0030 mtime=1761382078.144420456 30 atime=1761382080.167411201 30 ctime=1761382108.245302853 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/states.rb0000644000175100017510000004362615077107276024224 0ustar00runnerrunner# frozen_string_literal: true require "forwardable" require_relative "report/duration" require_relative "states/item" module Lrama # States is passed to a template file # # "Efficient Computation of LALR(1) Look-Ahead Sets" # https://dl.acm.org/doi/pdf/10.1145/69622.357187 class States extend Forwardable include Lrama::Report::Duration def_delegators "@grammar", :symbols, :terms, :nterms, :rules, :accept_symbol, :eof_symbol, :undef_symbol, :find_symbol_by_s_value! attr_reader :states, :reads_relation, :includes_relation, :lookback_relation def initialize(grammar, trace_state: false) @grammar = grammar @trace_state = trace_state @states = [] # `DR(p, A) = {t ∈ T | p -(A)-> r -(t)-> }` # where p is state, A is nterm, t is term. # # `@direct_read_sets` is a hash whose # key is [state.id, nterm.token_id], # value is bitmap of term. @direct_read_sets = {} # Reads relation on nonterminal transitions (pair of state and nterm) # `(p, A) reads (r, C) iff p -(A)-> r -(C)-> and C =>* ε` # where p, r are state, A, C are nterm. # # `@reads_relation` is a hash whose # key is [state.id, nterm.token_id], # value is array of [state.id, nterm.token_id]. @reads_relation = {} # `Read(p, A) =s DR(p, A) ∪ ∪{Read(r, C) | (p, A) reads (r, C)}` # # `@read_sets` is a hash whose # key is [state.id, nterm.token_id], # value is bitmap of term. @read_sets = {} # `(p, A) includes (p', B) iff B -> βAγ, γ =>* ε, p' -(β)-> p` # where p, p' are state, A, B are nterm, β, γ is sequence of symbol. # # `@includes_relation` is a hash whose # key is [state.id, nterm.token_id], # value is array of [state.id, nterm.token_id]. @includes_relation = {} # `(q, A -> ω) lookback (p, A) iff p -(ω)-> q` # where p, q are state, A -> ω is rule, A is nterm, ω is sequence of symbol. # # `@lookback_relation` is a hash whose # key is [state.id, rule.id], # value is array of [state.id, nterm.token_id]. @lookback_relation = {} # `Follow(p, A) =s Read(p, A) ∪ ∪{Follow(p', B) | (p, A) includes (p', B)}` # # `@follow_sets` is a hash whose # key is [state.id, rule.id], # value is bitmap of term. @follow_sets = {} # `LA(q, A -> ω) = ∪{Follow(p, A) | (q, A -> ω) lookback (p, A)` # # `@la` is a hash whose # key is [state.id, rule.id], # value is bitmap of term. @la = {} end def compute # Look Ahead Sets report_duration(:compute_lr0_states) { compute_lr0_states } report_duration(:compute_direct_read_sets) { compute_direct_read_sets } report_duration(:compute_reads_relation) { compute_reads_relation } report_duration(:compute_read_sets) { compute_read_sets } report_duration(:compute_includes_relation) { compute_includes_relation } report_duration(:compute_lookback_relation) { compute_lookback_relation } report_duration(:compute_follow_sets) { compute_follow_sets } report_duration(:compute_look_ahead_sets) { compute_look_ahead_sets } # Conflicts report_duration(:compute_conflicts) { compute_conflicts } report_duration(:compute_default_reduction) { compute_default_reduction } end def compute_ielr report_duration(:split_states) { split_states } report_duration(:compute_direct_read_sets) { compute_direct_read_sets } report_duration(:compute_reads_relation) { compute_reads_relation } report_duration(:compute_read_sets) { compute_read_sets } report_duration(:compute_includes_relation) { compute_includes_relation } report_duration(:compute_lookback_relation) { compute_lookback_relation } report_duration(:compute_follow_sets) { compute_follow_sets } report_duration(:compute_look_ahead_sets) { compute_look_ahead_sets } report_duration(:compute_conflicts) { compute_conflicts } report_duration(:compute_default_reduction) { compute_default_reduction } end def reporter StatesReporter.new(self) end def states_count @states.count end def direct_read_sets @direct_read_sets.transform_values do |v| bitmap_to_terms(v) end end def read_sets @read_sets.transform_values do |v| bitmap_to_terms(v) end end def follow_sets @follow_sets.transform_values do |v| bitmap_to_terms(v) end end def la @la.transform_values do |v| bitmap_to_terms(v) end end def sr_conflicts_count @sr_conflicts_count ||= @states.flat_map(&:sr_conflicts).count end def rr_conflicts_count @rr_conflicts_count ||= @states.flat_map(&:rr_conflicts).count end private def trace_state if @trace_state yield STDERR end end def create_state(accessing_symbol, kernels, states_created) # A item can appear in some states, # so need to use `kernels` (not `kernels.first`) as a key. # # For example... # # %% # program: '+' strings_1 # | '-' strings_2 # ; # # strings_1: string_1 # ; # # strings_2: string_1 # | string_2 # ; # # string_1: string # ; # # string_2: string '+' # ; # # string: tSTRING # ; # %% # # For these grammar, there are 2 states # # State A # string_1: string • # # State B # string_1: string • # string_2: string • '+' # return [states_created[kernels], false] if states_created[kernels] state = State.new(@states.count, accessing_symbol, kernels) @states << state states_created[kernels] = state return [state, true] end def setup_state(state) # closure closure = [] visited = {} queued = {} items = state.kernels.dup items.each do |item| queued[item] = true end while (item = items.shift) do visited[item] = true if (sym = item.next_sym) && sym.nterm? @grammar.find_rules_by_symbol!(sym).each do |rule| i = Item.new(rule: rule, position: 0) next if queued[i] closure << i items << i queued[i] = true end end end state.closure = closure.sort_by {|i| i.rule.id } # Trace trace_state do |out| out << "Closure: input\n" state.kernels.each do |item| out << " #{item.display_rest}\n" end out << "\n\n" out << "Closure: output\n" state.items.each do |item| out << " #{item.display_rest}\n" end out << "\n\n" end # shift & reduce state.compute_shifts_reduces end def enqueue_state(states, state) # Trace previous = state.kernels.first.previous_sym trace_state do |out| out << sprintf("state_list_append (state = %d, symbol = %d (%s))\n", @states.count, previous.number, previous.display_name) end states << state end def compute_lr0_states # State queue states = [] states_created = {} state, _ = create_state(symbols.first, [Item.new(rule: @grammar.rules.first, position: 0)], states_created) enqueue_state(states, state) while (state = states.shift) do # Trace # # Bison 3.8.2 renders "(reached by "end-of-input")" for State 0 but # I think it is not correct... previous = state.kernels.first.previous_sym trace_state do |out| out << "Processing state #{state.id} (reached by #{previous.display_name})\n" end setup_state(state) state.shifts.each do |shift| new_state, created = create_state(shift.next_sym, shift.next_items, states_created) state.set_items_to_state(shift.next_items, new_state) if created enqueue_state(states, new_state) new_state.append_predecessor(state) end end end end def nterm_transitions a = [] @states.each do |state| state.nterm_transitions.each do |shift, next_state| nterm = shift.next_sym a << [state, nterm, next_state] end end a end def compute_direct_read_sets @states.each do |state| state.nterm_transitions.each do |shift, next_state| nterm = shift.next_sym ary = next_state.term_transitions.map do |shift, _| shift.next_sym.number end key = [state.id, nterm.token_id] @direct_read_sets[key] = Bitmap.from_array(ary) end end end def compute_reads_relation @states.each do |state| state.nterm_transitions.each do |shift, next_state| nterm = shift.next_sym next_state.nterm_transitions.each do |shift2, _next_state2| nterm2 = shift2.next_sym if nterm2.nullable key = [state.id, nterm.token_id] @reads_relation[key] ||= [] @reads_relation[key] << [next_state.id, nterm2.token_id] end end end end end def compute_read_sets sets = nterm_transitions.map do |state, nterm, next_state| [state.id, nterm.token_id] end @read_sets = Digraph.new(sets, @reads_relation, @direct_read_sets).compute end # Execute transition of state by symbols # then return final state. def transition(state, symbols) symbols.each do |sym| state = state.transition(sym) end state end def compute_includes_relation @states.each do |state| state.nterm_transitions.each do |shift, next_state| nterm = shift.next_sym @grammar.find_rules_by_symbol!(nterm).each do |rule| i = rule.rhs.count - 1 while (i > -1) do sym = rule.rhs[i] break if sym.term? state2 = transition(state, rule.rhs[0...i]) # p' = state, B = nterm, p = state2, A = sym key = [state2.id, sym.token_id] # TODO: need to omit if state == state2 ? @includes_relation[key] ||= [] @includes_relation[key] << [state.id, nterm.token_id] break unless sym.nullable i -= 1 end end end end end def compute_lookback_relation @states.each do |state| state.nterm_transitions.each do |shift, next_state| nterm = shift.next_sym @grammar.find_rules_by_symbol!(nterm).each do |rule| state2 = transition(state, rule.rhs) # p = state, A = nterm, q = state2, A -> ω = rule key = [state2.id, rule.id] @lookback_relation[key] ||= [] @lookback_relation[key] << [state.id, nterm.token_id] end end end end def compute_follow_sets sets = nterm_transitions.map do |state, nterm, next_state| [state.id, nterm.token_id] end @follow_sets = Digraph.new(sets, @includes_relation, @read_sets).compute end def compute_look_ahead_sets @states.each do |state| rules.each do |rule| ary = @lookback_relation[[state.id, rule.id]] next unless ary ary.each do |state2_id, nterm_token_id| # q = state, A -> ω = rule, p = state2, A = nterm follows = @follow_sets[[state2_id, nterm_token_id]] next if follows == 0 key = [state.id, rule.id] @la[key] ||= 0 look_ahead = @la[key] | follows @la[key] |= look_ahead # No risk of conflict when # * the state only has single reduce # * the state only has nterm_transitions (GOTO) next if state.reduces.count == 1 && state.term_transitions.count == 0 state.set_look_ahead(rule, bitmap_to_terms(look_ahead)) end end end end def bitmap_to_terms(bit) ary = Bitmap.to_array(bit) ary.map do |i| @grammar.find_symbol_by_number!(i) end end def compute_conflicts compute_shift_reduce_conflicts compute_reduce_reduce_conflicts end def compute_shift_reduce_conflicts states.each do |state| state.shifts.each do |shift| state.reduces.each do |reduce| sym = shift.next_sym next unless reduce.look_ahead next unless reduce.look_ahead.include?(sym) # Shift/Reduce conflict shift_prec = sym.precedence reduce_prec = reduce.item.rule.precedence # Can resolve only when both have prec unless shift_prec && reduce_prec state.conflicts << State::ShiftReduceConflict.new(symbols: [sym], shift: shift, reduce: reduce) next end case when shift_prec < reduce_prec # Reduce is selected state.resolved_conflicts << State::ResolvedConflict.new(symbol: sym, reduce: reduce, which: :reduce) shift.not_selected = true next when shift_prec > reduce_prec # Shift is selected state.resolved_conflicts << State::ResolvedConflict.new(symbol: sym, reduce: reduce, which: :shift) reduce.add_not_selected_symbol(sym) next end # shift_prec == reduce_prec, then check associativity case sym.precedence.type when :precedence # %precedence only specifies precedence and not specify associativity # then a conflict is unresolved if precedence is same. state.conflicts << State::ShiftReduceConflict.new(symbols: [sym], shift: shift, reduce: reduce) next when :right # Shift is selected state.resolved_conflicts << State::ResolvedConflict.new(symbol: sym, reduce: reduce, which: :shift, same_prec: true) reduce.add_not_selected_symbol(sym) next when :left # Reduce is selected state.resolved_conflicts << State::ResolvedConflict.new(symbol: sym, reduce: reduce, which: :reduce, same_prec: true) shift.not_selected = true next when :nonassoc # Can not resolve # # nonassoc creates "run-time" error, precedence creates "compile-time" error. # Then omit both the shift and reduce. # # https://www.gnu.org/software/bison/manual/html_node/Using-Precedence.html state.resolved_conflicts << State::ResolvedConflict.new(symbol: sym, reduce: reduce, which: :error) shift.not_selected = true reduce.add_not_selected_symbol(sym) else raise "Unknown precedence type. #{sym}" end end end end end def compute_reduce_reduce_conflicts states.each do |state| count = state.reduces.count (0...count).each do |i| reduce1 = state.reduces[i] next if reduce1.look_ahead.nil? ((i+1)...count).each do |j| reduce2 = state.reduces[j] next if reduce2.look_ahead.nil? intersection = reduce1.look_ahead & reduce2.look_ahead unless intersection.empty? state.conflicts << State::ReduceReduceConflict.new(symbols: intersection, reduce1: reduce1, reduce2: reduce2) end end end end end def compute_default_reduction states.each do |state| next if state.reduces.empty? # Do not set, if conflict exist next unless state.conflicts.empty? # Do not set, if shift with `error` exists. next if state.shifts.map(&:next_sym).include?(@grammar.error_symbol) state.default_reduction_rule = state.reduces.map do |r| [r.rule, r.rule.id, (r.look_ahead || []).count] end.min_by do |rule, rule_id, count| [-count, rule_id] end.first end end def split_states @states.each do |state| state.transitions.each do |shift, next_state| compute_state(state, shift, next_state) end end end def merge_lookaheads(state, filtered_lookaheads) return if state.kernels.all? {|item| (filtered_lookaheads[item] - state.item_lookahead_set[item]).empty? } state.item_lookahead_set = state.item_lookahead_set.merge {|_, v1, v2| v1 | v2 } state.transitions.each do |shift, next_state| next if next_state.lookaheads_recomputed compute_state(state, shift, next_state) end end def compute_state(state, shift, next_state) filtered_lookaheads = state.propagate_lookaheads(next_state) s = next_state.ielr_isocores.find {|st| st.compatible_lookahead?(filtered_lookaheads) } if s.nil? s = next_state.ielr_isocores.last new_state = State.new(@states.count, s.accessing_symbol, s.kernels) new_state.closure = s.closure new_state.compute_shifts_reduces s.transitions.each do |sh, next_state| new_state.set_items_to_state(sh.next_items, next_state) end @states << new_state new_state.lalr_isocore = s s.ielr_isocores << new_state s.ielr_isocores.each do |st| st.ielr_isocores = s.ielr_isocores end new_state.item_lookahead_set = filtered_lookaheads state.update_transition(shift, new_state) elsif(!s.lookaheads_recomputed) s.item_lookahead_set = filtered_lookaheads else state.update_transition(shift, s) merge_lookaheads(s, filtered_lookaheads) end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/counterexamples0000644000000000000000000000013215077107334025125 xustar0030 mtime=1761382108.244302855 30 atime=1761382109.799298361 30 ctime=1761382108.244302855 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/counterexamples/0000755000175100017510000000000015077107334025572 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/counterexamples/PaxHeaders/derivation.rb0000644000000000000000000000013115077107276027677 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 30 ctime=1761382108.240302867 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/counterexamples/derivation.rb0000644000175100017510000000364515077107276030300 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Counterexamples class Derivation attr_reader :item, :left, :right attr_writer :right def initialize(item, left, right = nil) @item = item @left = left @right = right end def to_s "#" end alias :inspect :to_s def render_strings_for_report result = [] #: Array[String] _render_for_report(self, 0, result, 0) result.map(&:rstrip) end def render_for_report render_strings_for_report.join("\n") end private def _render_for_report(derivation, offset, strings, index) item = derivation.item if strings[index] strings[index] << " " * (offset - strings[index].length) else strings[index] = " " * offset end str = strings[index] str << "#{item.rule_id}: #{item.symbols_before_dot.map(&:display_name).join(" ")} " if derivation.left len = str.length str << "#{item.next_sym.display_name}" length = _render_for_report(derivation.left, len, strings, index + 1) # I want String#ljust! str << " " * (length - str.length) if length > str.length else str << " • #{item.symbols_after_dot.map(&:display_name).join(" ")} " return str.length end if derivation.right&.left left = derivation.right&.left #: Derivation length = _render_for_report(left, str.length, strings, index + 1) str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} " # steep:ignore str << " " * (length - str.length) if length > str.length elsif item.next_next_sym str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} " # steep:ignore end return str.length end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/counterexamples/PaxHeaders/transition_path.rb0000644000000000000000000000013115077107276030741 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 30 ctime=1761382108.243302858 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/counterexamples/transition_path.rb0000644000175100017510000000040015077107276031324 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Counterexamples class TransitionPath < Path def type :transition end def transition? true end def production? false end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/counterexamples/PaxHeaders/example.rb0000644000000000000000000000013015077107276027165 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 29 ctime=1761382108.23930287 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/counterexamples/example.rb0000644000175100017510000000672515077107276027571 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Counterexamples class Example attr_reader :path1, :path2, :conflict, :conflict_symbol # path1 is shift conflict when S/R conflict # path2 is always reduce conflict def initialize(path1, path2, conflict, conflict_symbol, counterexamples) @path1 = path1 @path2 = path2 @conflict = conflict @conflict_symbol = conflict_symbol @counterexamples = counterexamples end def type @conflict.type end def path1_item @path1.last.to.item end def path2_item @path2.last.to.item end def derivations1 @derivations1 ||= _derivations(path1) end def derivations2 @derivations2 ||= _derivations(path2) end private def _derivations(paths) derivation = nil #: Derivation current = :production last_path = paths.last #: Path lookahead_sym = last_path.to.item.end_of_rule? ? @conflict_symbol : nil paths.reverse_each do |path| item = path.to.item case current when :production case path when StartPath derivation = Derivation.new(item, derivation) current = :start when TransitionPath derivation = Derivation.new(item, derivation) current = :transition when ProductionPath derivation = Derivation.new(item, derivation) current = :production else raise "Unexpected. #{path}" end if lookahead_sym && item.next_next_sym && item.next_next_sym.first_set.include?(lookahead_sym) state_item = @counterexamples.transitions[[path.to, item.next_sym]] derivation2 = find_derivation_for_symbol(state_item, lookahead_sym) derivation.right = derivation2 # steep:ignore lookahead_sym = nil end when :transition case path when StartPath derivation = Derivation.new(item, derivation) current = :start when TransitionPath # ignore current = :transition when ProductionPath # ignore current = :production end else raise "BUG: Unknown #{current}" end break if current == :start end derivation end def find_derivation_for_symbol(state_item, sym) queue = [] #: Array[Array[StateItem]] queue << [state_item] while (sis = queue.shift) si = sis.last next_sym = si.item.next_sym if next_sym == sym derivation = nil sis.reverse_each do |si| derivation = Derivation.new(si.item, derivation) end return derivation end if next_sym.nterm? && next_sym.first_set.include?(sym) @counterexamples.productions[si].each do |next_item| next if next_item.empty_rule? next_si = StateItem.new(si.state, next_item) next if sis.include?(next_si) queue << (sis + [next_si]) end if next_sym.nullable next_si = @counterexamples.transitions[[si, next_sym]] queue << (sis + [next_si]) end end end end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/counterexamples/PaxHeaders/start_path.rb0000644000000000000000000000013115077107276027704 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 30 ctime=1761382108.236302879 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/counterexamples/start_path.rb0000644000175100017510000000050715077107276030277 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Counterexamples class StartPath < Path def initialize(to_state_item) super nil, to_state_item end def type :start end def transition? false end def production? false end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/counterexamples/PaxHeaders/path.rb0000644000000000000000000000013115077107276026467 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 30 ctime=1761382108.235302881 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/counterexamples/path.rb0000644000175100017510000000074415077107276027065 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Counterexamples class Path def initialize(from_state_item, to_state_item) @from_state_item = from_state_item @to_state_item = to_state_item end def from @from_state_item end def to @to_state_item end def to_s "#" end alias :inspect :to_s def type raise NotImplementedError end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/counterexamples/PaxHeaders/triple.rb0000644000000000000000000000013115077107276027032 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 30 ctime=1761382108.244302855 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/counterexamples/triple.rb0000644000175100017510000000074715077107276027433 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Counterexamples # s: state # itm: item within s # l: precise lookahead set class Triple < Struct.new(:s, :itm, :l) alias :state :s alias :item :itm alias :precise_lookahead_set :l def state_item StateItem.new(state, item) end def inspect "#{state.inspect}. #{item.display_name}. #{l.map(&:id).map(&:s_value)}" end alias :to_s :inspect end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/counterexamples/PaxHeaders/state_item.rb0000644000000000000000000000013115077107276027671 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 30 ctime=1761382108.237302876 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/counterexamples/state_item.rb0000644000175100017510000000020615077107276030260 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Counterexamples class StateItem < Struct.new(:state, :item) end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/counterexamples/PaxHeaders/production_path.rb0000644000000000000000000000013115077107276030735 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 30 ctime=1761382108.241302864 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/counterexamples/production_path.rb0000644000175100017510000000040015077107276031320 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Counterexamples class ProductionPath < Path def type :production end def transition? false end def production? true end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/states0000644000000000000000000000013215077107334023212 xustar0030 mtime=1761382108.331302604 30 atime=1761382109.799298361 30 ctime=1761382108.331302604 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/states/0000755000175100017510000000000015077107334023657 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/states/PaxHeaders/item.rb0000644000000000000000000000013215077107276024557 xustar0030 mtime=1761382078.144420456 30 atime=1761382080.167411201 30 ctime=1761382108.331302604 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/states/item.rb0000644000175100017510000000334315077107276025152 0ustar00runnerrunner# frozen_string_literal: true # TODO: Validate position is not over rule rhs require "forwardable" module Lrama class States class Item < Struct.new(:rule, :position, keyword_init: true) extend Forwardable def_delegators "rule", :lhs, :rhs # Optimization for States#setup_state def hash [rule_id, position].hash end def rule_id rule.id end def empty_rule? rule.empty_rule? end def number_of_rest_symbols rhs.count - position end def next_sym rhs[position] end def next_next_sym rhs[position + 1] end def previous_sym rhs[position - 1] end def end_of_rule? rhs.count == position end def beginning_of_rule? position == 0 end def start_item? rule.initial_rule? && beginning_of_rule? end def new_by_next_position Item.new(rule: rule, position: position + 1) end def symbols_before_dot # steep:ignore rhs[0...position] end def symbols_after_dot # steep:ignore rhs[position..-1] end def symbols_after_transition rhs[position+1..-1] end def to_s "#{lhs.id.s_value}: #{display_name}" end def display_name r = rhs.map(&:display_name).insert(position, "•").join(" ") "#{r} (rule #{rule_id})" end # Right after position def display_rest r = symbols_after_dot.map(&:display_name).join(" ") ". #{r} (rule #{rule_id})" end def predecessor_item_of?(other_item) rule == other_item.rule && position == other_item.position - 1 end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/output.rb0000644000000000000000000000013215077107276023656 xustar0030 mtime=1761382078.144420456 30 atime=1761382080.166411206 30 ctime=1761382108.299302697 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/output.rb0000644000175100017510000002472715077107276024262 0ustar00runnerrunner# frozen_string_literal: true require "erb" require "forwardable" require_relative "report/duration" module Lrama class Output extend Forwardable include Report::Duration attr_reader :grammar_file_path, :context, :grammar, :error_recovery, :include_header def_delegators "@context", :yyfinal, :yylast, :yyntokens, :yynnts, :yynrules, :yynstates, :yymaxutok, :yypact_ninf, :yytable_ninf def_delegators "@grammar", :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol def initialize( out:, output_file_path:, template_name:, grammar_file_path:, context:, grammar:, header_out: nil, header_file_path: nil, error_recovery: false ) @out = out @output_file_path = output_file_path @template_name = template_name @grammar_file_path = grammar_file_path @header_out = header_out @header_file_path = header_file_path @context = context @grammar = grammar @error_recovery = error_recovery @include_header = header_file_path ? header_file_path.sub("./", "") : nil end if ERB.instance_method(:initialize).parameters.last.first == :key def self.erb(input) ERB.new(input, trim_mode: '-') end else def self.erb(input) ERB.new(input, nil, '-') end end def render_partial(file) render_template(partial_file(file)) end def render report_duration(:render) do tmp = eval_template(template_file, @output_file_path) @out << tmp if @header_file_path tmp = eval_template(header_template_file, @header_file_path) if @header_out @header_out << tmp else File.write(@header_file_path, tmp) end end end end # A part of b4_token_enums def token_enums @context.yytokentype.map do |s_value, token_id, display_name| s = sprintf("%s = %d%s", s_value, token_id, token_id == yymaxutok ? "" : ",") if display_name sprintf(" %-30s /* %s */\n", s, display_name) else sprintf(" %s\n", s) end end.join end # b4_symbol_enum def symbol_enum last_sym_number = @context.yysymbol_kind_t.last[1] @context.yysymbol_kind_t.map do |s_value, sym_number, display_name| s = sprintf("%s = %d%s", s_value, sym_number, (sym_number == last_sym_number) ? "" : ",") if display_name sprintf(" %-40s /* %s */\n", s, display_name) else sprintf(" %s\n", s) end end.join end def yytranslate int_array_to_string(@context.yytranslate) end def yytranslate_inverted int_array_to_string(@context.yytranslate_inverted) end def yyrline int_array_to_string(@context.yyrline) end def yytname string_array_to_string(@context.yytname) + " YY_NULLPTR" end # b4_int_type_for def int_type_for(ary) min = ary.min max = ary.max case when (-127 <= min && min <= 127) && (-127 <= max && max <= 127) "yytype_int8" when (0 <= min && min <= 255) && (0 <= max && max <= 255) "yytype_uint8" when (-32767 <= min && min <= 32767) && (-32767 <= max && max <= 32767) "yytype_int16" when (0 <= min && min <= 65535) && (0 <= max && max <= 65535) "yytype_uint16" else "int" end end def symbol_actions_for_printer @grammar.symbols.map do |sym| next unless sym.printer <<-STR case #{sym.enum_name}: /* #{sym.comment} */ #line #{sym.printer.lineno} "#{@grammar_file_path}" {#{sym.printer.translated_code(sym.tag)}} #line [@oline@] [@ofile@] break; STR end.join end def symbol_actions_for_destructor @grammar.symbols.map do |sym| next unless sym.destructor <<-STR case #{sym.enum_name}: /* #{sym.comment} */ #line #{sym.destructor.lineno} "#{@grammar_file_path}" {#{sym.destructor.translated_code(sym.tag)}} #line [@oline@] [@ofile@] break; STR end.join end # b4_user_initial_action def user_initial_action(comment = "") return "" unless @grammar.initial_action <<-STR #{comment} #line #{@grammar.initial_action.line} "#{@grammar_file_path}" {#{@grammar.initial_action.translated_code}} STR end def after_shift_function(comment = "") return "" unless @grammar.after_shift <<-STR #{comment} #line #{@grammar.after_shift.line} "#{@grammar_file_path}" {#{@grammar.after_shift.s_value}(#{parse_param_name});} #line [@oline@] [@ofile@] STR end def before_reduce_function(comment = "") return "" unless @grammar.before_reduce <<-STR #{comment} #line #{@grammar.before_reduce.line} "#{@grammar_file_path}" {#{@grammar.before_reduce.s_value}(yylen#{user_args});} #line [@oline@] [@ofile@] STR end def after_reduce_function(comment = "") return "" unless @grammar.after_reduce <<-STR #{comment} #line #{@grammar.after_reduce.line} "#{@grammar_file_path}" {#{@grammar.after_reduce.s_value}(yylen#{user_args});} #line [@oline@] [@ofile@] STR end def after_shift_error_token_function(comment = "") return "" unless @grammar.after_shift_error_token <<-STR #{comment} #line #{@grammar.after_shift_error_token.line} "#{@grammar_file_path}" {#{@grammar.after_shift_error_token.s_value}(#{parse_param_name});} #line [@oline@] [@ofile@] STR end def after_pop_stack_function(len, comment = "") return "" unless @grammar.after_pop_stack <<-STR #{comment} #line #{@grammar.after_pop_stack.line} "#{@grammar_file_path}" {#{@grammar.after_pop_stack.s_value}(#{len}#{user_args});} #line [@oline@] [@ofile@] STR end def symbol_actions_for_error_token @grammar.symbols.map do |sym| next unless sym.error_token <<-STR case #{sym.enum_name}: /* #{sym.comment} */ #line #{sym.error_token.lineno} "#{@grammar_file_path}" {#{sym.error_token.translated_code(sym.tag)}} #line [@oline@] [@ofile@] break; STR end.join end # b4_user_actions def user_actions action = @context.states.rules.map do |rule| next unless rule.token_code code = rule.token_code spaces = " " * (code.column - 1) <<-STR case #{rule.id + 1}: /* #{rule.as_comment} */ #line #{code.line} "#{@grammar_file_path}" #{spaces}{#{rule.translated_code}} #line [@oline@] [@ofile@] break; STR end.join action + <<-STR #line [@oline@] [@ofile@] STR end def omit_blanks(param) param.strip end # b4_parse_param def parse_param if @grammar.parse_param omit_blanks(@grammar.parse_param) else "" end end def lex_param if @grammar.lex_param omit_blanks(@grammar.lex_param) else "" end end # b4_user_formals def user_formals if @grammar.parse_param ", #{parse_param}" else "" end end # b4_user_args def user_args if @grammar.parse_param ", #{parse_param_name}" else "" end end def extract_param_name(param) param[/\b([a-zA-Z0-9_]+)(?=\s*\z)/] end def parse_param_name if @grammar.parse_param extract_param_name(parse_param) else "" end end def lex_param_name if @grammar.lex_param extract_param_name(lex_param) else "" end end # b4_parse_param_use def parse_param_use(val, loc) str = <<-STR.dup YY_USE (#{val}); YY_USE (#{loc}); STR if @grammar.parse_param str << " YY_USE (#{parse_param_name});" end str end # b4_yylex_formals def yylex_formals ary = ["&yylval"] ary << "&yylloc" if @grammar.locations if @grammar.lex_param ary << lex_param_name end "(#{ary.join(', ')})" end # b4_table_value_equals def table_value_equals(table, value, literal, symbol) if literal < table.min || table.max < literal "0" else "((#{value}) == #{symbol})" end end # b4_yyerror_args def yyerror_args ary = ["&yylloc"] if @grammar.parse_param ary << parse_param_name end "#{ary.join(', ')}" end def template_basename File.basename(template_file) end def aux @grammar.aux end def int_array_to_string(ary) last = ary.count - 1 ary.each_with_index.each_slice(10).map do |slice| " " + slice.map { |e, i| sprintf("%6d%s", e, (i == last) ? "" : ",") }.join end.join("\n") end def spec_mapped_header_file @header_file_path end def b4_cpp_guard__b4_spec_mapped_header_file if @header_file_path "YY_YY_" + @header_file_path.gsub(/[^a-zA-Z_0-9]+/, "_").upcase + "_INCLUDED" else "" end end # b4_percent_code_get def percent_code(name) @grammar.percent_codes.select do |percent_code| percent_code.name == name end.map do |percent_code| percent_code.code end.join end private def eval_template(file, path) tmp = render_template(file) replace_special_variables(tmp, path) end def render_template(file) erb = self.class.erb(File.read(file)) erb.filename = file erb.result_with_hash(context: @context, output: self) end def template_file File.join(template_dir, @template_name) end def header_template_file File.join(template_dir, "bison/yacc.h") end def partial_file(file) File.join(template_dir, file) end def template_dir File.expand_path('../../template', __dir__) end def string_array_to_string(ary) result = "" tmp = " " ary.each do |s| replaced = s.gsub('\\', '\\\\\\\\').gsub('"', '\\"') if (tmp + replaced + " \"\",").length > 75 result = "#{result}#{tmp}\n" tmp = " \"#{replaced}\"," else tmp = "#{tmp} \"#{replaced}\"," end end result + tmp end def replace_special_variables(str, ofile) str.each_line.with_index(1).map do |line, i| line.gsub!("[@oline@]", (i + 1).to_s) line.gsub!("[@ofile@]", "\"#{ofile}\"") line end.join end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/states_reporter.rb0000644000000000000000000000013215077107276025543 xustar0030 mtime=1761382078.145420451 30 atime=1761382080.167411201 30 ctime=1761382108.248302844 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/states_reporter.rb0000644000175100017510000002602415077107276026137 0ustar00runnerrunner# frozen_string_literal: true module Lrama class StatesReporter include Lrama::Report::Duration def initialize(states) @states = states end def report(io, **options) report_duration(:report) do _report(io, **options) end end private def _report(io, grammar: false, rules: false, terms: false, states: false, itemsets: false, lookaheads: false, solved: false, counterexamples: false, verbose: false) report_unused_rules(io) if rules report_unused_terms(io) if terms report_conflicts(io) report_grammar(io) if grammar report_states(io, itemsets, lookaheads, solved, counterexamples, verbose) end def report_unused_terms(io) look_aheads = @states.states.each do |state| state.reduces.flat_map do |reduce| reduce.look_ahead unless reduce.look_ahead.nil? end end next_terms = @states.states.flat_map do |state| state.shifts.map(&:next_sym).select(&:term?) end unused_symbols = @states.terms.select do |term| !(look_aheads + next_terms).include?(term) end unless unused_symbols.empty? io << "#{unused_symbols.count} Unused Terms\n\n" unused_symbols.each_with_index do |term, index| io << sprintf("%5d %s\n", index, term.id.s_value) end io << "\n\n" end end def report_unused_rules(io) used_rules = @states.rules.flat_map(&:rhs) unused_rules = @states.rules.map(&:lhs).select do |rule| !used_rules.include?(rule) && rule.token_id != 0 end unless unused_rules.empty? io << "#{unused_rules.count} Unused Rules\n\n" unused_rules.each_with_index do |rule, index| io << sprintf("%5d %s\n", index, rule.display_name) end io << "\n\n" end end def report_conflicts(io) has_conflict = false @states.states.each do |state| messages = [] cs = state.conflicts.group_by(&:type) if cs[:shift_reduce] messages << "#{cs[:shift_reduce].count} shift/reduce" end if cs[:reduce_reduce] messages << "#{cs[:reduce_reduce].count} reduce/reduce" end unless messages.empty? has_conflict = true io << "State #{state.id} conflicts: #{messages.join(', ')}\n" end end if has_conflict io << "\n\n" end end def report_grammar(io) io << "Grammar\n" last_lhs = nil @states.rules.each do |rule| if rule.empty_rule? r = "ε" else r = rule.rhs.map(&:display_name).join(" ") end if rule.lhs == last_lhs io << sprintf("%5d %s| %s\n", rule.id, " " * rule.lhs.display_name.length, r) else io << "\n" io << sprintf("%5d %s: %s\n", rule.id, rule.lhs.display_name, r) end last_lhs = rule.lhs end io << "\n\n" end def report_states(io, itemsets, lookaheads, solved, counterexamples, verbose) if counterexamples cex = Counterexamples.new(@states) end @states.states.each do |state| # Report State io << "State #{state.id}\n\n" # Report item last_lhs = nil list = itemsets ? state.items : state.kernels list.sort_by {|i| [i.rule_id, i.position] }.each do |item| if item.empty_rule? r = "ε •" else r = item.rhs.map(&:display_name).insert(item.position, "•").join(" ") end if item.lhs == last_lhs l = " " * item.lhs.id.s_value.length + "|" else l = item.lhs.id.s_value + ":" end la = "" if lookaheads && item.end_of_rule? reduce = state.find_reduce_by_item!(item) look_ahead = reduce.selected_look_ahead unless look_ahead.empty? la = " [#{look_ahead.map(&:display_name).join(", ")}]" end end last_lhs = item.lhs io << sprintf("%5i %s %s%s\n", item.rule_id, l, r, la) end io << "\n" # Report shifts tmp = state.term_transitions.reject do |shift, _| shift.not_selected end.map do |shift, next_state| [shift.next_sym, next_state.id] end max_len = tmp.map(&:first).map(&:display_name).map(&:length).max tmp.each do |term, state_id| io << " #{term.display_name.ljust(max_len)} shift, and go to state #{state_id}\n" end io << "\n" unless tmp.empty? # Report error caused by %nonassoc nl = false tmp = state.resolved_conflicts.select do |resolved| resolved.which == :error end.map do |error| error.symbol.display_name end max_len = tmp.map(&:length).max tmp.each do |name| nl = true io << " #{name.ljust(max_len)} error (nonassociative)\n" end io << "\n" unless tmp.empty? # Report reduces nl = false max_len = state.non_default_reduces.flat_map(&:look_ahead).compact.map(&:display_name).map(&:length).max || 0 max_len = [max_len, "$default".length].max if state.default_reduction_rule ary = [] state.non_default_reduces.each do |reduce| reduce.look_ahead.each do |term| ary << [term, reduce] end end ary.sort_by do |term, reduce| term.number end.each do |term, reduce| rule = reduce.item.rule io << " #{term.display_name.ljust(max_len)} reduce using rule #{rule.id} (#{rule.lhs.display_name})\n" nl = true end if (r = state.default_reduction_rule) nl = true s = "$default".ljust(max_len) if r.initial_rule? io << " #{s} accept\n" else io << " #{s} reduce using rule #{r.id} (#{r.lhs.display_name})\n" end end io << "\n" if nl # Report nonterminal transitions tmp = [] max_len = 0 state.nterm_transitions.each do |shift, next_state| nterm = shift.next_sym tmp << [nterm, next_state.id] max_len = [max_len, nterm.id.s_value.length].max end tmp.uniq! tmp.sort_by! do |nterm, state_id| nterm.number end tmp.each do |nterm, state_id| io << " #{nterm.id.s_value.ljust(max_len)} go to state #{state_id}\n" end io << "\n" unless tmp.empty? if solved # Report conflict resolutions state.resolved_conflicts.each do |resolved| io << " #{resolved.report_message}\n" end io << "\n" unless state.resolved_conflicts.empty? end if counterexamples && state.has_conflicts? # Report counterexamples examples = cex.compute(state) examples.each do |example| label0 = example.type == :shift_reduce ? "shift/reduce" : "reduce/reduce" label1 = example.type == :shift_reduce ? "Shift derivation" : "First Reduce derivation" label2 = example.type == :shift_reduce ? "Reduce derivation" : "Second Reduce derivation" io << " #{label0} conflict on token #{example.conflict_symbol.id.s_value}:\n" io << " #{example.path1_item}\n" io << " #{example.path2_item}\n" io << " #{label1}\n" example.derivations1.render_strings_for_report.each do |str| io << " #{str}\n" end io << " #{label2}\n" example.derivations2.render_strings_for_report.each do |str| io << " #{str}\n" end end end if verbose # Report direct_read_sets io << " [Direct Read sets]\n" direct_read_sets = @states.direct_read_sets @states.nterms.each do |nterm| terms = direct_read_sets[[state.id, nterm.token_id]] next unless terms next if terms.empty? str = terms.map {|sym| sym.id.s_value }.join(", ") io << " read #{nterm.id.s_value} shift #{str}\n" end io << "\n" # Report reads_relation io << " [Reads Relation]\n" @states.nterms.each do |nterm| a = @states.reads_relation[[state.id, nterm.token_id]] next unless a a.each do |state_id2, nterm_id2| n = @states.nterms.find {|n| n.token_id == nterm_id2 } io << " (State #{state_id2}, #{n.id.s_value})\n" end end io << "\n" # Report read_sets io << " [Read sets]\n" read_sets = @states.read_sets @states.nterms.each do |nterm| terms = read_sets[[state.id, nterm.token_id]] next unless terms next if terms.empty? terms.each do |sym| io << " #{sym.id.s_value}\n" end end io << "\n" # Report includes_relation io << " [Includes Relation]\n" @states.nterms.each do |nterm| a = @states.includes_relation[[state.id, nterm.token_id]] next unless a a.each do |state_id2, nterm_id2| n = @states.nterms.find {|n| n.token_id == nterm_id2 } io << " (State #{state.id}, #{nterm.id.s_value}) -> (State #{state_id2}, #{n.id.s_value})\n" end end io << "\n" # Report lookback_relation io << " [Lookback Relation]\n" @states.rules.each do |rule| a = @states.lookback_relation[[state.id, rule.id]] next unless a a.each do |state_id2, nterm_id2| n = @states.nterms.find {|n| n.token_id == nterm_id2 } io << " (Rule: #{rule.display_name}) -> (State #{state_id2}, #{n.id.s_value})\n" end end io << "\n" # Report follow_sets io << " [Follow sets]\n" follow_sets = @states.follow_sets @states.nterms.each do |nterm| terms = follow_sets[[state.id, nterm.token_id]] next unless terms terms.each do |sym| io << " #{nterm.id.s_value} -> #{sym.id.s_value}\n" end end io << "\n" # Report LA io << " [Look-Ahead Sets]\n" tmp = [] max_len = 0 @states.rules.each do |rule| syms = @states.la[[state.id, rule.id]] next unless syms tmp << [rule, syms] max_len = ([max_len] + syms.map {|s| s.id.s_value.length }).max end tmp.each do |rule, syms| syms.each do |sym| io << " #{sym.id.s_value.ljust(max_len)} reduce using rule #{rule.id} (#{rule.lhs.id.s_value})\n" end end io << "\n" unless tmp.empty? end # End of Report State io << "\n" end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/state.rb0000644000000000000000000000013215077107276023436 xustar0030 mtime=1761382078.144420456 30 atime=1761382080.167411201 30 ctime=1761382108.234302884 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/state.rb0000644000175100017510000003246415077107276024037 0ustar00runnerrunner# frozen_string_literal: true require_relative "state/reduce" require_relative "state/reduce_reduce_conflict" require_relative "state/resolved_conflict" require_relative "state/shift" require_relative "state/shift_reduce_conflict" module Lrama class State attr_reader :id, :accessing_symbol, :kernels, :conflicts, :resolved_conflicts, :default_reduction_rule, :closure, :items attr_accessor :shifts, :reduces, :ielr_isocores, :lalr_isocore def initialize(id, accessing_symbol, kernels) @id = id @accessing_symbol = accessing_symbol @kernels = kernels.freeze @items = @kernels # Manage relationships between items to state # to resolve next state @items_to_state = {} @conflicts = [] @resolved_conflicts = [] @default_reduction_rule = nil @predecessors = [] @lalr_isocore = self @ielr_isocores = [self] @internal_dependencies = {} @successor_dependencies = {} @always_follows = {} end def closure=(closure) @closure = closure @items = @kernels + @closure end def non_default_reduces reduces.reject do |reduce| reduce.rule == @default_reduction_rule end end def compute_shifts_reduces _shifts = {} reduces = [] items.each do |item| # TODO: Consider what should be pushed if item.end_of_rule? reduces << Reduce.new(item) else key = item.next_sym _shifts[key] ||= [] _shifts[key] << item.new_by_next_position end end # It seems Bison 3.8.2 iterates transitions order by symbol number shifts = _shifts.sort_by do |next_sym, new_items| next_sym.number end.map do |next_sym, new_items| Shift.new(next_sym, new_items.flatten) end self.shifts = shifts.freeze self.reduces = reduces.freeze end def set_items_to_state(items, next_state) @items_to_state[items] = next_state end def set_look_ahead(rule, look_ahead) reduce = reduces.find do |r| r.rule == rule end reduce.look_ahead = look_ahead end def nterm_transitions @nterm_transitions ||= transitions.select {|shift, _| shift.next_sym.nterm? } end def term_transitions @term_transitions ||= transitions.select {|shift, _| shift.next_sym.term? } end def transitions @transitions ||= shifts.map {|shift| [shift, @items_to_state[shift.next_items]] } end def update_transition(shift, next_state) set_items_to_state(shift.next_items, next_state) next_state.append_predecessor(self) clear_transitions_cache end def clear_transitions_cache @nterm_transitions = nil @term_transitions = nil @transitions = nil end def selected_term_transitions term_transitions.reject do |shift, next_state| shift.not_selected end end # Move to next state by sym def transition(sym) result = nil if sym.term? term_transitions.each do |shift, next_state| term = shift.next_sym result = next_state if term == sym end else nterm_transitions.each do |shift, next_state| nterm = shift.next_sym result = next_state if nterm == sym end end raise "Can not transit by #{sym} #{self}" if result.nil? result end def find_reduce_by_item!(item) reduces.find do |r| r.item == item end || (raise "reduce is not found. #{item}") end def default_reduction_rule=(default_reduction_rule) @default_reduction_rule = default_reduction_rule reduces.each do |r| if r.rule == default_reduction_rule r.default_reduction = true end end end def has_conflicts? !@conflicts.empty? end def sr_conflicts @conflicts.select do |conflict| conflict.type == :shift_reduce end end def rr_conflicts @conflicts.select do |conflict| conflict.type == :reduce_reduce end end def propagate_lookaheads(next_state) next_state.kernels.map {|item| lookahead_sets = if item.position == 1 goto_follow_set(item.lhs) else kernel = kernels.find {|k| k.predecessor_item_of?(item) } item_lookahead_set[kernel] end [item, lookahead_sets & next_state.lookahead_set_filters[item]] }.to_h end def lookaheads_recomputed !@item_lookahead_set.nil? end def compatible_lookahead?(filtered_lookahead) !lookaheads_recomputed || @lalr_isocore.annotation_list.all? {|token, actions| a = dominant_contribution(token, actions, item_lookahead_set) b = dominant_contribution(token, actions, filtered_lookahead) a.nil? || b.nil? || a == b } end def lookahead_set_filters kernels.map {|kernel| [kernel, @lalr_isocore.annotation_list.select {|token, actions| token.term? && actions.any? {|action, contributions| !contributions.nil? && contributions.key?(kernel) && contributions[kernel] } }.map {|token, _| token } ] }.to_h end def dominant_contribution(token, actions, lookaheads) a = actions.select {|action, contributions| contributions.nil? || contributions.any? {|item, contributed| contributed && lookaheads[item].include?(token) } }.map {|action, _| action } return nil if a.empty? a.reject {|action| if action.is_a?(State::Shift) action.not_selected elsif action.is_a?(State::Reduce) action.not_selected_symbols.include?(token) end } end def inadequacy_list return @inadequacy_list if @inadequacy_list shift_contributions = shifts.map {|shift| [shift.next_sym, [shift]] }.to_h reduce_contributions = reduces.map {|reduce| (reduce.look_ahead || []).map {|sym| [sym, [reduce]] }.to_h }.reduce(Hash.new([])) {|hash, cont| hash.merge(cont) {|_, a, b| a | b } } list = shift_contributions.merge(reduce_contributions) {|_, a, b| a | b } @inadequacy_list = list.select {|token, actions| token.term? && actions.size > 1 } end def annotation_list return @annotation_list if @annotation_list @annotation_list = annotate_manifestation @annotation_list = @items_to_state.values.map {|next_state| next_state.annotate_predecessor(self) } .reduce(@annotation_list) {|result, annotations| result.merge(annotations) {|_, actions_a, actions_b| if actions_a.nil? || actions_b.nil? actions_a || actions_b else actions_a.merge(actions_b) {|_, contributions_a, contributions_b| if contributions_a.nil? || contributions_b.nil? next contributions_a || contributions_b end contributions_a.merge(contributions_b) {|_, contributed_a, contributed_b| contributed_a || contributed_b } } end } } end def annotate_manifestation inadequacy_list.transform_values {|actions| actions.map {|action| if action.is_a?(Shift) [action, nil] elsif action.is_a?(Reduce) if action.rule.empty_rule? [action, lhs_contributions(action.rule.lhs, inadequacy_list.key(actions))] else contributions = kernels.map {|kernel| [kernel, kernel.rule == action.rule && kernel.end_of_rule?] }.to_h [action, contributions] end end }.to_h } end def annotate_predecessor(predecessor) annotation_list.transform_values {|actions| token = annotation_list.key(actions) actions.transform_values {|inadequacy| next nil if inadequacy.nil? lhs_adequacy = kernels.any? {|kernel| inadequacy[kernel] && kernel.position == 1 && predecessor.lhs_contributions(kernel.lhs, token).nil? } if lhs_adequacy next nil else predecessor.kernels.map {|pred_k| [pred_k, kernels.any? {|k| inadequacy[k] && ( pred_k.predecessor_item_of?(k) && predecessor.item_lookahead_set[pred_k].include?(token) || k.position == 1 && predecessor.lhs_contributions(k.lhs, token)[pred_k] ) }] }.to_h end } } end def lhs_contributions(sym, token) shift, next_state = nterm_transitions.find {|sh, _| sh.next_sym == sym } if always_follows(shift, next_state).include?(token) nil else kernels.map {|kernel| [kernel, follow_kernel_items(shift, next_state, kernel) && item_lookahead_set[kernel].include?(token)] }.to_h end end def follow_kernel_items(shift, next_state, kernel) queue = [[self, shift, next_state]] until queue.empty? st, sh, next_st = queue.pop return true if kernel.next_sym == sh.next_sym && kernel.symbols_after_transition.all?(&:nullable) st.internal_dependencies(sh, next_st).each {|v| queue << v } end false end def item_lookahead_set return @item_lookahead_set if @item_lookahead_set kernels.map {|item| value = if item.lhs.accept_symbol? [] elsif item.position > 1 prev_items = predecessors_with_item(item) prev_items.map {|st, i| st.item_lookahead_set[i] }.reduce([]) {|acc, syms| acc |= syms } elsif item.position == 1 prev_state = @predecessors.find {|p| p.shifts.any? {|shift| shift.next_sym == item.lhs } } shift, next_state = prev_state.nterm_transitions.find {|shift, _| shift.next_sym == item.lhs } prev_state.goto_follows(shift, next_state) end [item, value] }.to_h end def item_lookahead_set=(k) @item_lookahead_set = k end def predecessors_with_item(item) result = [] @predecessors.each do |pre| pre.items.each do |i| result << [pre, i] if i.predecessor_item_of?(item) end end result end def append_predecessor(prev_state) @predecessors << prev_state @predecessors.uniq! end def goto_follow_set(nterm_token) return [] if nterm_token.accept_symbol? shift, next_state = @lalr_isocore.nterm_transitions.find {|sh, _| sh.next_sym == nterm_token } @kernels .select {|kernel| follow_kernel_items(shift, next_state, kernel) } .map {|kernel| item_lookahead_set[kernel] } .reduce(always_follows(shift, next_state)) {|result, terms| result |= terms } end def goto_follows(shift, next_state) queue = internal_dependencies(shift, next_state) + predecessor_dependencies(shift, next_state) terms = always_follows(shift, next_state) until queue.empty? st, sh, next_st = queue.pop terms |= st.always_follows(sh, next_st) st.internal_dependencies(sh, next_st).each {|v| queue << v } st.predecessor_dependencies(sh, next_st).each {|v| queue << v } end terms end def always_follows(shift, next_state) return @always_follows[[shift, next_state]] if @always_follows[[shift, next_state]] queue = internal_dependencies(shift, next_state) + successor_dependencies(shift, next_state) terms = [] until queue.empty? st, sh, next_st = queue.pop terms |= next_st.term_transitions.map {|sh, _| sh.next_sym } st.internal_dependencies(sh, next_st).each {|v| queue << v } st.successor_dependencies(sh, next_st).each {|v| queue << v } end @always_follows[[shift, next_state]] = terms end def internal_dependencies(shift, next_state) return @internal_dependencies[[shift, next_state]] if @internal_dependencies[[shift, next_state]] syms = @items.select {|i| i.next_sym == shift.next_sym && i.symbols_after_transition.all?(&:nullable) && i.position == 0 }.map(&:lhs).uniq @internal_dependencies[[shift, next_state]] = nterm_transitions.select {|sh, _| syms.include?(sh.next_sym) }.map {|goto| [self, *goto] } end def successor_dependencies(shift, next_state) return @successor_dependencies[[shift, next_state]] if @successor_dependencies[[shift, next_state]] @successor_dependencies[[shift, next_state]] = next_state.nterm_transitions .select {|next_shift, _| next_shift.next_sym.nullable } .map {|transition| [next_state, *transition] } end def predecessor_dependencies(shift, next_state) state_items = [] @kernels.select {|kernel| kernel.next_sym == shift.next_sym && kernel.symbols_after_transition.all?(&:nullable) }.each do |item| queue = predecessors_with_item(item) until queue.empty? st, i = queue.pop if i.position == 0 state_items << [st, i] else st.predecessors_with_item(i).each {|v| queue << v } end end end state_items.map {|state, item| sh, next_st = state.nterm_transitions.find {|shi, _| shi.next_sym == item.lhs } [state, sh, next_st] } end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/counterexamples.rb0000644000000000000000000000013115077107276025533 xustar0030 mtime=1761382078.141420469 29 atime=1761382080.16541121 30 ctime=1761382108.300302694 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/counterexamples.rb0000644000175100017510000002332415077107276026130 0ustar00runnerrunner# frozen_string_literal: true require "set" require_relative "counterexamples/derivation" require_relative "counterexamples/example" require_relative "counterexamples/path" require_relative "counterexamples/production_path" require_relative "counterexamples/start_path" require_relative "counterexamples/state_item" require_relative "counterexamples/transition_path" require_relative "counterexamples/triple" module Lrama # See: https://www.cs.cornell.edu/andru/papers/cupex/cupex.pdf # 4. Constructing Nonunifying Counterexamples class Counterexamples attr_reader :transitions, :productions def initialize(states) @states = states setup_transitions setup_productions end def to_s "#" end alias :inspect :to_s def compute(conflict_state) conflict_state.conflicts.flat_map do |conflict| case conflict.type when :shift_reduce # @type var conflict: State::ShiftReduceConflict shift_reduce_example(conflict_state, conflict) when :reduce_reduce # @type var conflict: State::ReduceReduceConflict reduce_reduce_examples(conflict_state, conflict) end end.compact end private def setup_transitions # Hash [StateItem, Symbol] => StateItem @transitions = {} # Hash [StateItem, Symbol] => Set(StateItem) @reverse_transitions = {} @states.states.each do |src_state| trans = {} #: Hash[Grammar::Symbol, State] src_state.transitions.each do |shift, next_state| trans[shift.next_sym] = next_state end src_state.items.each do |src_item| next if src_item.end_of_rule? sym = src_item.next_sym dest_state = trans[sym] dest_state.kernels.each do |dest_item| next unless (src_item.rule == dest_item.rule) && (src_item.position + 1 == dest_item.position) src_state_item = StateItem.new(src_state, src_item) dest_state_item = StateItem.new(dest_state, dest_item) @transitions[[src_state_item, sym]] = dest_state_item # @type var key: [StateItem, Grammar::Symbol] key = [dest_state_item, sym] @reverse_transitions[key] ||= Set.new @reverse_transitions[key] << src_state_item end end end end def setup_productions # Hash [StateItem] => Set(Item) @productions = {} # Hash [State, Symbol] => Set(Item). Symbol is nterm @reverse_productions = {} @states.states.each do |state| # LHS => Set(Item) h = {} #: Hash[Grammar::Symbol, Set[States::Item]] state.closure.each do |item| sym = item.lhs h[sym] ||= Set.new h[sym] << item end state.items.each do |item| next if item.end_of_rule? next if item.next_sym.term? sym = item.next_sym state_item = StateItem.new(state, item) # @type var key: [State, Grammar::Symbol] key = [state, sym] @productions[state_item] = h[sym] @reverse_productions[key] ||= Set.new @reverse_productions[key] << item end end end def shift_reduce_example(conflict_state, conflict) conflict_symbol = conflict.symbols.first # @type var shift_conflict_item: ::Lrama::States::Item shift_conflict_item = conflict_state.items.find { |item| item.next_sym == conflict_symbol } path2 = shortest_path(conflict_state, conflict.reduce.item, conflict_symbol) path1 = find_shift_conflict_shortest_path(path2, conflict_state, shift_conflict_item) Example.new(path1, path2, conflict, conflict_symbol, self) end def reduce_reduce_examples(conflict_state, conflict) conflict_symbol = conflict.symbols.first path1 = shortest_path(conflict_state, conflict.reduce1.item, conflict_symbol) path2 = shortest_path(conflict_state, conflict.reduce2.item, conflict_symbol) Example.new(path1, path2, conflict, conflict_symbol, self) end def find_shift_conflict_shortest_path(reduce_path, conflict_state, conflict_item) state_items = find_shift_conflict_shortest_state_items(reduce_path, conflict_state, conflict_item) build_paths_from_state_items(state_items) end def find_shift_conflict_shortest_state_items(reduce_path, conflict_state, conflict_item) target_state_item = StateItem.new(conflict_state, conflict_item) result = [target_state_item] reversed_reduce_path = reduce_path.to_a.reverse # Index for state_item i = 0 while (path = reversed_reduce_path[i]) # Index for prev_state_item j = i + 1 _j = j while (prev_path = reversed_reduce_path[j]) if prev_path.production? j += 1 else break end end state_item = path.to prev_state_item = prev_path&.to if target_state_item == state_item || target_state_item.item.start_item? result.concat( reversed_reduce_path[_j..-1] #: Array[StartPath|TransitionPath|ProductionPath] .map(&:to)) break end if target_state_item.item.beginning_of_rule? queue = [] #: Array[Array[StateItem]] queue << [target_state_item] # Find reverse production while (sis = queue.shift) si = sis.last # Reach to start state if si.item.start_item? sis.shift result.concat(sis) target_state_item = si break end if si.item.beginning_of_rule? # @type var key: [State, Grammar::Symbol] key = [si.state, si.item.lhs] @reverse_productions[key].each do |item| state_item = StateItem.new(si.state, item) queue << (sis + [state_item]) end else # @type var key: [StateItem, Grammar::Symbol] key = [si, si.item.previous_sym] @reverse_transitions[key].each do |prev_target_state_item| next if prev_target_state_item.state != prev_state_item&.state sis.shift result.concat(sis) result << prev_target_state_item target_state_item = prev_target_state_item i = j queue.clear break end end end else # Find reverse transition # @type var key: [StateItem, Grammar::Symbol] key = [target_state_item, target_state_item.item.previous_sym] @reverse_transitions[key].each do |prev_target_state_item| next if prev_target_state_item.state != prev_state_item&.state result << prev_target_state_item target_state_item = prev_target_state_item i = j break end end end result.reverse end def build_paths_from_state_items(state_items) state_items.zip([nil] + state_items).map do |si, prev_si| case when prev_si.nil? StartPath.new(si) when si.item.beginning_of_rule? ProductionPath.new(prev_si, si) else TransitionPath.new(prev_si, si) end end end def shortest_path(conflict_state, conflict_reduce_item, conflict_term) # queue: is an array of [Triple, [Path]] queue = [] #: Array[[Triple, Array[StartPath|TransitionPath|ProductionPath]]] visited = {} #: Hash[Triple, true] start_state = @states.states.first #: Lrama::State raise "BUG: Start state should be just one kernel." if start_state.kernels.count != 1 start = Triple.new(start_state, start_state.kernels.first, Set.new([@states.eof_symbol])) queue << [start, [StartPath.new(start.state_item)]] while true triple, paths = queue.shift next if visited[triple] visited[triple] = true # Found if triple.state == conflict_state && triple.item == conflict_reduce_item && triple.l.include?(conflict_term) return paths end # transition triple.state.transitions.each do |shift, next_state| next unless triple.item.next_sym && triple.item.next_sym == shift.next_sym next_state.kernels.each do |kernel| next if kernel.rule != triple.item.rule t = Triple.new(next_state, kernel, triple.l) queue << [t, paths + [TransitionPath.new(triple.state_item, t.state_item)]] end end # production step triple.state.closure.each do |item| next unless triple.item.next_sym && triple.item.next_sym == item.lhs l = follow_l(triple.item, triple.l) t = Triple.new(triple.state, item, l) queue << [t, paths + [ProductionPath.new(triple.state_item, t.state_item)]] end break if queue.empty? end return nil end def follow_l(item, current_l) # 1. follow_L (A -> X1 ... Xn-1 • Xn) = L # 2. follow_L (A -> X1 ... Xk • Xk+1 Xk+2 ... Xn) = {Xk+2} if Xk+2 is a terminal # 3. follow_L (A -> X1 ... Xk • Xk+1 Xk+2 ... Xn) = FIRST(Xk+2) if Xk+2 is a nonnullable nonterminal # 4. follow_L (A -> X1 ... Xk • Xk+1 Xk+2 ... Xn) = FIRST(Xk+2) + follow_L (A -> X1 ... Xk+1 • Xk+2 ... Xn) if Xk+2 is a nullable nonterminal case when item.number_of_rest_symbols == 1 current_l when item.next_next_sym.term? Set.new([item.next_next_sym]) when !item.next_next_sym.nullable item.next_next_sym.first_set else item.next_next_sym.first_set + follow_l(item.new_by_next_position, current_l) end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/digraph.rb0000644000000000000000000000013015077107276023732 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 29 ctime=1761382108.28430274 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/digraph.rb0000644000175100017510000000356415077107276024334 0ustar00runnerrunner# rbs_inline: enabled # frozen_string_literal: true module Lrama # Algorithm Digraph of https://dl.acm.org/doi/pdf/10.1145/69622.357187 (P. 625) # # @rbs generic X < Object -- Type of a member of `sets` # @rbs generic Y < _Or -- Type of sets assigned to a member of `sets` class Digraph # TODO: rbs-inline 0.10.0 doesn't support instance variables. # Move these type declarations above instance variable definitions, once it's supported. # # @rbs! # interface _Or # def |: (self) -> self # end # @sets: Array[X] # @relation: Hash[X, Array[X]] # @base_function: Hash[X, Y] # @stack: Array[X] # @h: Hash[X, (Integer|Float)?] # @result: Hash[X, Y] # @rbs sets: Array[X] # @rbs relation: Hash[X, Array[X]] # @rbs base_function: Hash[X, Y] # @rbs return: void def initialize(sets, relation, base_function) # X in the paper @sets = sets # R in the paper @relation = relation # F' in the paper @base_function = base_function # S in the paper @stack = [] # N in the paper @h = Hash.new(0) # F in the paper @result = {} end # @rbs () -> Hash[X, Y] def compute @sets.each do |x| next if @h[x] != 0 traverse(x) end return @result end private # @rbs (X x) -> void def traverse(x) @stack.push(x) d = @stack.count @h[x] = d @result[x] = @base_function[x] # F x = F' x @relation[x]&.each do |y| traverse(y) if @h[y] == 0 @h[x] = [@h[x], @h[y]].min @result[x] |= @result[y] # F x = F x + F y end if @h[x] == d while (z = @stack.pop) do @h[z] = Float::INFINITY break if z == x @result[z] = @result[x] # F (Top of S) = F x end end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/report0000644000000000000000000000013215077107334023222 xustar0030 mtime=1761382108.311302662 30 atime=1761382109.799298361 30 ctime=1761382108.311302662 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/report/0000755000175100017510000000000015077107334023667 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/report/PaxHeaders/duration.rb0000644000000000000000000000013215077107276025456 xustar0030 mtime=1761382078.144420456 30 atime=1761382080.167411201 30 ctime=1761382108.309302668 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/report/duration.rb0000644000175100017510000000077115077107276026053 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Report module Duration def self.enable @_report_duration_enabled = true end def self.enabled? !!@_report_duration_enabled end def report_duration(method_name) time1 = Time.now.to_f result = yield time2 = Time.now.to_f if Duration.enabled? puts sprintf("%s %10.5f s", method_name, time2 - time1) end return result end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/report/PaxHeaders/profile.rb0000644000000000000000000000013215077107276025271 xustar0030 mtime=1761382078.144420456 30 atime=1761382080.167411201 30 ctime=1761382108.311302662 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/report/profile.rb0000644000175100017510000000052115077107276025657 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Report module Profile # See "Profiling Lrama" in README.md for how to use. def self.report_profile require "stackprof" StackProf.run(mode: :cpu, raw: true, out: 'tmp/stackprof-cpu-myapp.dump') do yield end end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/lexer.rb0000644000000000000000000000013115077107276023434 xustar0029 mtime=1761382078.14342046 30 atime=1761382080.166411206 30 ctime=1761382108.312302659 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/lexer.rb0000644000175100017510000001166415077107276024035 0ustar00runnerrunner# frozen_string_literal: true require "strscan" require_relative "lexer/grammar_file" require_relative "lexer/location" require_relative "lexer/token" module Lrama class Lexer attr_reader :head_line, :head_column, :line attr_accessor :status, :end_symbol SYMBOLS = ['%{', '%}', '%%', '{', '}', '\[', '\]', '\(', '\)', '\,', ':', '\|', ';'].freeze PERCENT_TOKENS = %w( %union %token %type %nterm %left %right %nonassoc %expect %define %require %printer %destructor %lex-param %parse-param %initial-action %precedence %prec %error-token %before-reduce %after-reduce %after-shift-error-token %after-shift %after-pop-stack %empty %code %rule %no-stdlib %inline %locations ).freeze def initialize(grammar_file) @grammar_file = grammar_file @scanner = StringScanner.new(grammar_file.text) @head_column = @head = @scanner.pos @head_line = @line = 1 @status = :initial @end_symbol = nil end def next_token case @status when :initial lex_token when :c_declaration lex_c_code end end def column @scanner.pos - @head end def location Location.new( grammar_file: @grammar_file, first_line: @head_line, first_column: @head_column, last_line: line, last_column: column ) end def lex_token until @scanner.eos? do case when @scanner.scan(/\n/) newline when @scanner.scan(/\s+/) # noop when @scanner.scan(/\/\*/) lex_comment when @scanner.scan(/\/\/.*(?\n)?/) newline if @scanner[:newline] else break end end reset_first_position case when @scanner.eos? return when @scanner.scan(/#{SYMBOLS.join('|')}/) return [@scanner.matched, @scanner.matched] when @scanner.scan(/#{PERCENT_TOKENS.join('|')}/) return [@scanner.matched, @scanner.matched] when @scanner.scan(/[\?\+\*]/) return [@scanner.matched, @scanner.matched] when @scanner.scan(/<\w+>/) return [:TAG, Lrama::Lexer::Token::Tag.new(s_value: @scanner.matched, location: location)] when @scanner.scan(/'.'/) return [:CHARACTER, Lrama::Lexer::Token::Char.new(s_value: @scanner.matched, location: location)] when @scanner.scan(/'\\\\'|'\\b'|'\\t'|'\\f'|'\\r'|'\\n'|'\\v'|'\\13'/) return [:CHARACTER, Lrama::Lexer::Token::Char.new(s_value: @scanner.matched, location: location)] when @scanner.scan(/".*?"/) return [:STRING, %Q(#{@scanner.matched})] when @scanner.scan(/\d+/) return [:INTEGER, Integer(@scanner.matched)] when @scanner.scan(/([a-zA-Z_.][-a-zA-Z0-9_.]*)/) token = Lrama::Lexer::Token::Ident.new(s_value: @scanner.matched, location: location) type = if @scanner.check(/\s*(\[\s*[a-zA-Z_.][-a-zA-Z0-9_.]*\s*\])?\s*:/) :IDENT_COLON else :IDENTIFIER end return [type, token] else raise ParseError, "Unexpected token: #{@scanner.peek(10).chomp}." end end def lex_c_code nested = 0 code = '' reset_first_position until @scanner.eos? do case when @scanner.scan(/{/) code += @scanner.matched nested += 1 when @scanner.scan(/}/) if nested == 0 && @end_symbol == '}' @scanner.unscan return [:C_DECLARATION, Lrama::Lexer::Token::UserCode.new(s_value: code, location: location)] else code += @scanner.matched nested -= 1 end when @scanner.check(/#{@end_symbol}/) return [:C_DECLARATION, Lrama::Lexer::Token::UserCode.new(s_value: code, location: location)] when @scanner.scan(/\n/) code += @scanner.matched newline when @scanner.scan(/".*?"/) code += %Q(#{@scanner.matched}) @line += @scanner.matched.count("\n") when @scanner.scan(/'.*?'/) code += %Q(#{@scanner.matched}) when @scanner.scan(/[^\"'\{\}\n]+/) code += @scanner.matched when @scanner.scan(/#{Regexp.escape(@end_symbol)}/) code += @scanner.matched else code += @scanner.getch end end raise ParseError, "Unexpected code: #{code}." end private def lex_comment until @scanner.eos? do case when @scanner.scan_until(/[\s\S]*?\*\//) @scanner.matched.count("\n").times { newline } return when @scanner.scan_until(/\n/) newline end end end def reset_first_position @head_line = line @head_column = column end def newline @line += 1 @head = @scanner.pos end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/lexer0000644000000000000000000000013215077107334023026 xustar0030 mtime=1761382108.328302613 30 atime=1761382109.799298361 30 ctime=1761382108.328302613 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/lexer/0000755000175100017510000000000015077107334023473 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/lexer/PaxHeaders/location.rb0000644000000000000000000000013115077107276025244 xustar0029 mtime=1761382078.14342046 30 atime=1761382080.166411206 30 ctime=1761382108.319302639 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/lexer/location.rb0000644000175100017510000000610315077107276025635 0ustar00runnerrunner# rbs_inline: enabled # frozen_string_literal: true module Lrama class Lexer class Location attr_reader :grammar_file #: GrammarFile attr_reader :first_line #: Integer attr_reader :first_column #: Integer attr_reader :last_line #: Integer attr_reader :last_column #: Integer # @rbs (grammar_file: GrammarFile, first_line: Integer, first_column: Integer, last_line: Integer, last_column: Integer) -> void def initialize(grammar_file:, first_line:, first_column:, last_line:, last_column:) @grammar_file = grammar_file @first_line = first_line @first_column = first_column @last_line = last_line @last_column = last_column end # @rbs (Location other) -> bool def ==(other) self.class == other.class && self.grammar_file == other.grammar_file && self.first_line == other.first_line && self.first_column == other.first_column && self.last_line == other.last_line && self.last_column == other.last_column end # @rbs (Integer left, Integer right) -> Location def partial_location(left, right) offset = -first_column new_first_line = -1 new_first_column = -1 new_last_line = -1 new_last_column = -1 _text.each.with_index do |line, index| new_offset = offset + line.length + 1 if offset <= left && left <= new_offset new_first_line = first_line + index new_first_column = left - offset end if offset <= right && right <= new_offset new_last_line = first_line + index new_last_column = right - offset end offset = new_offset end Location.new( grammar_file: grammar_file, first_line: new_first_line, first_column: new_first_column, last_line: new_last_line, last_column: new_last_column ) end # @rbs () -> String def to_s "#{path} (#{first_line},#{first_column})-(#{last_line},#{last_column})" end # @rbs (String error_message) -> String def generate_error_message(error_message) <<~ERROR.chomp #{path}:#{first_line}:#{first_column}: #{error_message} #{line_with_carets} ERROR end # @rbs () -> String def line_with_carets <<~TEXT #{text} #{carets} TEXT end private # @rbs () -> String def path grammar_file.path end # @rbs () -> String def blanks (text[0...first_column] or raise "#{first_column} is invalid").gsub(/[^\t]/, ' ') end # @rbs () -> String def carets blanks + '^' * (last_column - first_column) end # @rbs () -> String def text @text ||= _text.join("\n") end # @rbs () -> Array[String] def _text @_text ||=begin range = (first_line - 1)...last_line grammar_file.lines[range] or raise "#{range} is invalid" end end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/lexer/PaxHeaders/grammar_file.rb0000644000000000000000000000013115077107276026061 xustar0029 mtime=1761382078.14342046 30 atime=1761382080.166411206 30 ctime=1761382108.328302613 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/lexer/grammar_file.rb0000644000175100017510000000152515077107276026455 0ustar00runnerrunner# rbs_inline: enabled # frozen_string_literal: true module Lrama class Lexer class GrammarFile class Text < String # @rbs () -> String def inspect length <= 50 ? super : "#{self[0..47]}...".inspect end end attr_reader :path #: String attr_reader :text #: String # @rbs (String path, String text) -> void def initialize(path, text) @path = path @text = Text.new(text).freeze end # @rbs () -> String def inspect "<#{self.class}: @path=#{path}, @text=#{text.inspect}>" end # @rbs (GrammarFile other) -> bool def ==(other) self.class == other.class && self.path == other.path end # @rbs () -> Array[String] def lines @lines ||= text.split("\n") end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/lexer/PaxHeaders/token.rb0000644000000000000000000000013115077107276024554 xustar0029 mtime=1761382078.14342046 30 atime=1761382080.166411206 30 ctime=1761382108.318302642 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/lexer/token.rb0000644000175100017510000000333715077107276025153 0ustar00runnerrunner# rbs_inline: enabled # frozen_string_literal: true require_relative 'token/char' require_relative 'token/ident' require_relative 'token/instantiate_rule' require_relative 'token/tag' require_relative 'token/user_code' module Lrama class Lexer class Token attr_reader :s_value #: String attr_reader :location #: Location attr_accessor :alias_name #: String attr_accessor :referred #: bool # @rbs (s_value: String, ?alias_name: String, ?location: Location) -> void def initialize(s_value:, alias_name: nil, location: nil) s_value.freeze @s_value = s_value @alias_name = alias_name @location = location end # @rbs () -> String def to_s "value: `#{s_value}`, location: #{location}" end # @rbs (String string) -> bool def referred_by?(string) [self.s_value, self.alias_name].compact.include?(string) end # @rbs (Token other) -> bool def ==(other) self.class == other.class && self.s_value == other.s_value end # @rbs () -> Integer def first_line location.first_line end alias :line :first_line # @rbs () -> Integer def first_column location.first_column end alias :column :first_column # @rbs () -> Integer def last_line location.last_line end # @rbs () -> Integer def last_column location.last_column end # @rbs (Lrama::Grammar::Reference ref, String message) -> bot def invalid_ref(ref, message) location = self.location.partial_location(ref.first_column, ref.last_column) raise location.generate_error_message(message) end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/lexer/PaxHeaders/token0000644000000000000000000000013215077107334024146 xustar0030 mtime=1761382108.327302615 30 atime=1761382109.799298361 30 ctime=1761382108.327302615 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/lexer/token/0000755000175100017510000000000015077107334024613 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/lexer/token/PaxHeaders/tag.rb0000644000000000000000000000013115077107276025327 xustar0029 mtime=1761382078.14342046 30 atime=1761382080.166411206 30 ctime=1761382108.327302615 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/lexer/token/tag.rb0000644000175100017510000000045415077107276025723 0ustar00runnerrunner# rbs_inline: enabled # frozen_string_literal: true module Lrama class Lexer class Token class Tag < Token # @rbs () -> String def member # Omit "<>" s_value[1..-2] or raise "Unexpected Tag format (#{s_value})" end end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/lexer/token/PaxHeaders/char.rb0000644000000000000000000000013115077107276025471 xustar0029 mtime=1761382078.14342046 30 atime=1761382080.166411206 30 ctime=1761382108.321302633 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/lexer/token/char.rb0000644000175100017510000000022515077107276026061 0ustar00runnerrunner# rbs_inline: enabled # frozen_string_literal: true module Lrama class Lexer class Token class Char < Token end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/lexer/token/PaxHeaders/user_code.rb0000644000000000000000000000013115077107276026524 xustar0029 mtime=1761382078.14342046 30 atime=1761382080.166411206 30 ctime=1761382108.325302621 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/lexer/token/user_code.rb0000644000175100017510000001045215077107276027117 0ustar00runnerrunner# rbs_inline: enabled # frozen_string_literal: true require "strscan" module Lrama class Lexer class Token class UserCode < Token attr_accessor :tag #: Lexer::Token::Tag # @rbs () -> Array[Lrama::Grammar::Reference] def references @references ||= _references end private # @rbs () -> Array[Lrama::Grammar::Reference] def _references scanner = StringScanner.new(s_value) references = [] #: Array[Grammar::Reference] until scanner.eos? do case when reference = scan_reference(scanner) references << reference when scanner.scan(/\/\*/) scanner.scan_until(/\*\//) else scanner.getch end end references end # @rbs (StringScanner scanner) -> Lrama::Grammar::Reference? def scan_reference(scanner) start = scanner.pos case # $ references # It need to wrap an identifier with brackets to use ".-" for identifiers when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?\$/) # $$, $$ tag = scanner[1] ? Lrama::Lexer::Token::Tag.new(s_value: scanner[1]) : nil return Lrama::Grammar::Reference.new(type: :dollar, name: "$", ex_tag: tag, first_column: start, last_column: scanner.pos) when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?(\d+)/) # $1, $2, $1 tag = scanner[1] ? Lrama::Lexer::Token::Tag.new(s_value: scanner[1]) : nil return Lrama::Grammar::Reference.new(type: :dollar, number: Integer(scanner[2]), index: Integer(scanner[2]), ex_tag: tag, first_column: start, last_column: scanner.pos) when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?([a-zA-Z_][a-zA-Z0-9_]*)/) # $foo, $expr, $program (named reference without brackets) tag = scanner[1] ? Lrama::Lexer::Token::Tag.new(s_value: scanner[1]) : nil return Lrama::Grammar::Reference.new(type: :dollar, name: scanner[2], ex_tag: tag, first_column: start, last_column: scanner.pos) when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?\[([a-zA-Z_.][-a-zA-Z0-9_.]*)\]/) # $[expr.right], $[expr-right], $[expr.right] (named reference with brackets) tag = scanner[1] ? Lrama::Lexer::Token::Tag.new(s_value: scanner[1]) : nil return Lrama::Grammar::Reference.new(type: :dollar, name: scanner[2], ex_tag: tag, first_column: start, last_column: scanner.pos) # @ references # It need to wrap an identifier with brackets to use ".-" for identifiers when scanner.scan(/@\$/) # @$ return Lrama::Grammar::Reference.new(type: :at, name: "$", first_column: start, last_column: scanner.pos) when scanner.scan(/@(\d+)/) # @1 return Lrama::Grammar::Reference.new(type: :at, number: Integer(scanner[1]), index: Integer(scanner[1]), first_column: start, last_column: scanner.pos) when scanner.scan(/@([a-zA-Z][a-zA-Z0-9_]*)/) # @foo, @expr (named reference without brackets) return Lrama::Grammar::Reference.new(type: :at, name: scanner[1], first_column: start, last_column: scanner.pos) when scanner.scan(/@\[([a-zA-Z_.][-a-zA-Z0-9_.]*)\]/) # @[expr.right], @[expr-right] (named reference with brackets) return Lrama::Grammar::Reference.new(type: :at, name: scanner[1], first_column: start, last_column: scanner.pos) # $: references when scanner.scan(/\$:\$/) # $:$ return Lrama::Grammar::Reference.new(type: :index, name: "$", first_column: start, last_column: scanner.pos) when scanner.scan(/\$:(\d+)/) # $:1 return Lrama::Grammar::Reference.new(type: :index, number: Integer(scanner[1]), first_column: start, last_column: scanner.pos) when scanner.scan(/\$:([a-zA-Z_][a-zA-Z0-9_]*)/) # $:foo, $:expr (named reference without brackets) return Lrama::Grammar::Reference.new(type: :index, name: scanner[1], first_column: start, last_column: scanner.pos) when scanner.scan(/\$:\[([a-zA-Z_.][-a-zA-Z0-9_.]*)\]/) # $:[expr.right], $:[expr-right] (named reference with brackets) return Lrama::Grammar::Reference.new(type: :index, name: scanner[1], first_column: start, last_column: scanner.pos) end end end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/lexer/token/PaxHeaders/instantiate_rule.rb0000644000000000000000000000013015077107276030125 xustar0029 mtime=1761382078.14342046 30 atime=1761382080.166411206 29 ctime=1761382108.32230263 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/lexer/token/instantiate_rule.rb0000644000175100017510000000143715077107276030524 0ustar00runnerrunner# rbs_inline: enabled # frozen_string_literal: true module Lrama class Lexer class Token class InstantiateRule < Token attr_reader :args #: Array[Lexer::Token] attr_reader :lhs_tag #: Lexer::Token::Tag? # @rbs (s_value: String, ?alias_name: String, ?location: Location, ?args: Array[Lexer::Token], ?lhs_tag: Lexer::Token::Tag?) -> void def initialize(s_value:, alias_name: nil, location: nil, args: [], lhs_tag: nil) super s_value: s_value, alias_name: alias_name, location: location @args = args @lhs_tag = lhs_tag end # @rbs () -> String def rule_name s_value end # @rbs () -> Integer def args_count args.count end end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/lexer/token/PaxHeaders/ident.rb0000644000000000000000000000013115077107276025657 xustar0029 mtime=1761382078.14342046 30 atime=1761382080.166411206 30 ctime=1761382108.324302624 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/lexer/token/ident.rb0000644000175100017510000000022615077107276026250 0ustar00runnerrunner# rbs_inline: enabled # frozen_string_literal: true module Lrama class Lexer class Token class Ident < Token end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/version.rb0000644000000000000000000000013215077107276024003 xustar0030 mtime=1761382078.145420451 30 atime=1761382080.167411201 30 ctime=1761382108.289302725 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/version.rb0000644000175100017510000000011315077107276024366 0ustar00runnerrunner# frozen_string_literal: true module Lrama VERSION = "0.7.0".freeze end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/trace_reporter.rb0000644000000000000000000000013215077107276025336 xustar0030 mtime=1761382078.145420451 30 atime=1761382080.167411201 30 ctime=1761382108.302302688 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/trace_reporter.rb0000644000175100017510000000220215077107276025722 0ustar00runnerrunner# rbs_inline: enabled # frozen_string_literal: true module Lrama class TraceReporter # @rbs (Lrama::Grammar grammar) -> void def initialize(grammar) @grammar = grammar end # @rbs (**Hash[Symbol, bool] options) -> void def report(**options) _report(**options) end private # @rbs rules: (bool rules, bool actions, bool only_explicit_rules, **untyped _) -> void def _report(rules: false, actions: false, only_explicit_rules: false, **_) report_rules if rules && !only_explicit_rules report_only_explicit_rules if only_explicit_rules report_actions if actions end # @rbs () -> void def report_rules puts "Grammar rules:" @grammar.rules.each { |rule| puts rule.display_name } end # @rbs () -> void def report_only_explicit_rules puts "Grammar rules:" @grammar.rules.each do |rule| puts rule.display_name_without_action if rule.lhs.first_set.any? end end # @rbs () -> void def report_actions puts "Grammar rules with actions:" @grammar.rules.each { |rule| puts rule.with_actions } end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/context.rb0000644000000000000000000000013115077107276024001 xustar0030 mtime=1761382078.141420469 29 atime=1761382080.16541121 30 ctime=1761382108.287302731 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/context.rb0000644000175100017510000003056715077107276024405 0ustar00runnerrunner# frozen_string_literal: true require_relative "report/duration" module Lrama # This is passed to a template class Context include Report::Duration ErrorActionNumber = -Float::INFINITY BaseMin = -Float::INFINITY # TODO: It might be better to pass `states` to Output directly? attr_reader :states, :yylast, :yypact_ninf, :yytable_ninf, :yydefact, :yydefgoto def initialize(states) @states = states @yydefact = nil @yydefgoto = nil # Array of array @_actions = [] compute_tables end # enum yytokentype def yytokentype @states.terms.reject do |term| 0 < term.token_id && term.token_id < 128 end.map do |term| [term.id.s_value, term.token_id, term.display_name] end.unshift(["YYEMPTY", -2, nil]) end # enum yysymbol_kind_t def yysymbol_kind_t @states.symbols.map do |sym| [sym.enum_name, sym.number, sym.comment] end.unshift(["YYSYMBOL_YYEMPTY", -2, nil]) end # State number of final (accepted) state def yyfinal @states.states.find do |state| state.items.find do |item| item.lhs.accept_symbol? && item.end_of_rule? end end.id end # Number of terms def yyntokens @states.terms.count end # Number of nterms def yynnts @states.nterms.count end # Number of rules def yynrules @states.rules.count end # Number of states def yynstates @states.states.count end # Last token number def yymaxutok @states.terms.map(&:token_id).max end # YYTRANSLATE # # yytranslate is a mapping from token id to symbol number def yytranslate # 2 is YYSYMBOL_YYUNDEF a = Array.new(yymaxutok, 2) @states.terms.each do |term| a[term.token_id] = term.number end return a end def yytranslate_inverted a = Array.new(@states.symbols.count, @states.undef_symbol.token_id) @states.terms.each do |term| a[term.number] = term.token_id end return a end # Mapping from rule number to line number of the rule is defined. # Dummy rule is appended as the first element whose value is 0 # because 0 means error in yydefact. def yyrline a = [0] @states.rules.each do |rule| a << rule.lineno end return a end # Mapping from symbol number to its name def yytname @states.symbols.sort_by(&:number).map do |sym| sym.display_name end end def yypact @base[0...yynstates] end def yypgoto @base[yynstates..-1] end def yytable @table end def yycheck @check end def yystos @states.states.map do |state| state.accessing_symbol.number end end # Mapping from rule number to symbol number of LHS. # Dummy rule is appended as the first element whose value is 0 # because 0 means error in yydefact. def yyr1 a = [0] @states.rules.each do |rule| a << rule.lhs.number end return a end # Mapping from rule number to length of RHS. # Dummy rule is appended as the first element whose value is 0 # because 0 means error in yydefact. def yyr2 a = [0] @states.rules.each do |rule| a << rule.rhs.count end return a end private # Compute these # # See also: "src/tables.c" of Bison. # # * yydefact # * yydefgoto # * yypact and yypgoto # * yytable # * yycheck # * yypact_ninf # * yytable_ninf def compute_tables report_duration(:compute_yydefact) { compute_yydefact } report_duration(:compute_yydefgoto) { compute_yydefgoto } report_duration(:sort_actions) { sort_actions } # debug_sorted_actions report_duration(:compute_packed_table) { compute_packed_table } end def vectors_count @states.states.count + @states.nterms.count end # In compressed table, rule 0 is appended as an error case # and reduce is represented as minus number. def rule_id_to_action_number(rule_id) (rule_id + 1) * -1 end # Symbol number is assigned to term first then nterm. # This method calculates sequence_number for nterm. def nterm_number_to_sequence_number(nterm_number) nterm_number - @states.terms.count end # Vector is states + nterms def nterm_number_to_vector_number(nterm_number) @states.states.count + (nterm_number - @states.terms.count) end def compute_yydefact # Default action (shift/reduce/error) for each state. # Index is state id, value is `rule id + 1` of a default reduction. @yydefact = Array.new(@states.states.count, 0) @states.states.each do |state| # Action number means # # * number = 0, default action # * number = -Float::INFINITY, error by %nonassoc # * number > 0, shift then move to state "number" # * number < 0, reduce by "-number" rule. Rule "number" is already added by 1. actions = Array.new(@states.terms.count, 0) if state.reduces.map(&:selected_look_ahead).any? {|la| !la.empty? } # Iterate reduces with reverse order so that first rule is used. state.reduces.reverse_each do |reduce| reduce.look_ahead.each do |term| actions[term.number] = rule_id_to_action_number(reduce.rule.id) end end end # Shift is selected when S/R conflict exists. state.selected_term_transitions.each do |shift, next_state| actions[shift.next_sym.number] = next_state.id end state.resolved_conflicts.select do |conflict| conflict.which == :error end.each do |conflict| actions[conflict.symbol.number] = ErrorActionNumber end # If default_reduction_rule, replace default_reduction_rule in # actions with zero. if state.default_reduction_rule actions.map! do |e| if e == rule_id_to_action_number(state.default_reduction_rule.id) 0 else e end end end # If no default_reduction_rule, default behavior is an # error then replace ErrorActionNumber with zero. unless state.default_reduction_rule actions.map! do |e| if e == ErrorActionNumber 0 else e end end end s = actions.each_with_index.map do |n, i| [i, n] end.reject do |i, n| # Remove default_reduction_rule entries n == 0 end if s.count != 0 # Entry of @_actions is an array of # # * State id # * Array of tuple, [from, to] where from is term number and to is action. # * The number of "Array of tuple" used by sort_actions # * "width" used by sort_actions @_actions << [state.id, s, s.count, s.last[0] - s.first[0] + 1] end @yydefact[state.id] = state.default_reduction_rule ? state.default_reduction_rule.id + 1 : 0 end end def compute_yydefgoto # Default GOTO (nterm transition) for each nterm. # Index is sequence number of nterm, value is state id # of a default nterm transition destination. @yydefgoto = Array.new(@states.nterms.count, 0) # Mapping from nterm to next_states nterm_to_next_states = {} @states.states.each do |state| state.nterm_transitions.each do |shift, next_state| key = shift.next_sym nterm_to_next_states[key] ||= [] nterm_to_next_states[key] << [state, next_state] # [from_state, to_state] end end @states.nterms.each do |nterm| if (states = nterm_to_next_states[nterm]) default_state = states.map(&:last).group_by {|s| s }.max_by {|_, v| v.count }.first default_goto = default_state.id not_default_gotos = [] states.each do |from_state, to_state| next if to_state.id == default_goto not_default_gotos << [from_state.id, to_state.id] end else default_goto = 0 not_default_gotos = [] end k = nterm_number_to_sequence_number(nterm.number) @yydefgoto[k] = default_goto if not_default_gotos.count != 0 v = nterm_number_to_vector_number(nterm.number) # Entry of @_actions is an array of # # * Nterm number as vector number # * Array of tuple, [from, to] where from is state number and to is state number. # * The number of "Array of tuple" used by sort_actions # * "width" used by sort_actions @_actions << [v, not_default_gotos, not_default_gotos.count, not_default_gotos.last[0] - not_default_gotos.first[0] + 1] end end end def sort_actions # This is not same with #sort_actions # # @sorted_actions = @_actions.sort_by do |_, _, count, width| # [-width, -count] # end @sorted_actions = [] @_actions.each do |action| if @sorted_actions.empty? @sorted_actions << action next end j = @sorted_actions.count - 1 _state_id, _froms_and_tos, count, width = action while (j >= 0) do case when @sorted_actions[j][3] < width j -= 1 when @sorted_actions[j][3] == width && @sorted_actions[j][2] < count j -= 1 else break end end @sorted_actions.insert(j + 1, action) end end def debug_sorted_actions ary = Array.new @sorted_actions.each do |state_id, froms_and_tos, count, width| ary[state_id] = [state_id, froms_and_tos, count, width] end print sprintf("table_print:\n\n") print sprintf("order [\n") vectors_count.times do |i| print sprintf("%d, ", @sorted_actions[i] ? @sorted_actions[i][0] : 0) print "\n" if i % 10 == 9 end print sprintf("]\n\n") print sprintf("width [\n") vectors_count.times do |i| print sprintf("%d, ", ary[i] ? ary[i][3] : 0) print "\n" if i % 10 == 9 end print sprintf("]\n\n") print sprintf("tally [\n") vectors_count.times do |i| print sprintf("%d, ", ary[i] ? ary[i][2] : 0) print "\n" if i % 10 == 9 end print sprintf("]\n\n") end def compute_packed_table # yypact and yypgoto @base = Array.new(vectors_count, BaseMin) # yytable @table = [] # yycheck @check = [] # Key is froms_and_tos, value is index position pushed = {} used_res = {} lowzero = 0 high = 0 @sorted_actions.each do |state_id, froms_and_tos, _, _| if (res = pushed[froms_and_tos]) @base[state_id] = res next end res = lowzero - froms_and_tos.first[0] while true do ok = true froms_and_tos.each do |from, to| loc = res + from if @table[loc] # If the cell of table is set, can not use the cell. ok = false break end end if ok && used_res[res] ok = false end if ok break else res += 1 end end loc = 0 froms_and_tos.each do |from, to| loc = res + from @table[loc] = to @check[loc] = from end while (@table[lowzero]) do lowzero += 1 end high = loc if high < loc @base[state_id] = res pushed[froms_and_tos] = res used_res[res] = true end @yylast = high # replace_ninf @yypact_ninf = (@base.reject {|i| i == BaseMin } + [0]).min - 1 @base.map! do |i| case i when BaseMin @yypact_ninf else i end end @yytable_ninf = (@table.compact.reject {|i| i == ErrorActionNumber } + [0]).min - 1 @table.map! do |i| case i when nil 0 when ErrorActionNumber @yytable_ninf else i end end @check.map! do |i| case i when nil -1 else i end end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/bitmap.rb0000644000000000000000000000013115077107276023571 xustar0030 mtime=1761382078.141420469 29 atime=1761382080.16541121 30 ctime=1761382108.290302723 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/bitmap.rb0000644000175100017510000000077615077107276024174 0ustar00runnerrunner# rbs_inline: enabled # frozen_string_literal: true module Lrama module Bitmap # @rbs (Array[Integer] ary) -> Integer def self.from_array(ary) bit = 0 ary.each do |int| bit |= (1 << int) end bit end # @rbs (Integer int) -> Array[Integer] def self.to_array(int) a = [] #: Array[Integer] i = 0 while int > 0 do if int & 1 == 1 a << i end i += 1 int >>= 1 end a end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/option_parser.rb0000644000000000000000000000013015077107276025200 xustar0029 mtime=1761382078.14342046 30 atime=1761382080.166411206 29 ctime=1761382108.24630285 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/option_parser.rb0000644000175100017510000001541215077107276025575 0ustar00runnerrunner# frozen_string_literal: true require 'optparse' module Lrama # Handle option parsing for the command line interface. class OptionParser def initialize @options = Options.new @trace = [] @report = [] end def parse(argv) parse_by_option_parser(argv) @options.trace_opts = validate_trace(@trace) @options.report_opts = validate_report(@report) @options.grammar_file = argv.shift unless @options.grammar_file abort "File should be specified\n" end if @options.grammar_file == '-' @options.grammar_file = argv.shift or abort "File name for STDIN should be specified\n" else @options.y = File.open(@options.grammar_file, 'r') end if !@report.empty? && @options.report_file.nil? && @options.grammar_file @options.report_file = File.dirname(@options.grammar_file) + "/" + File.basename(@options.grammar_file, ".*") + ".output" end if !@options.header_file && @options.header case when @options.outfile @options.header_file = File.dirname(@options.outfile) + "/" + File.basename(@options.outfile, ".*") + ".h" when @options.grammar_file @options.header_file = File.dirname(@options.grammar_file) + "/" + File.basename(@options.grammar_file, ".*") + ".h" end end @options end private def parse_by_option_parser(argv) ::OptionParser.new do |o| o.banner = <<~BANNER Lrama is LALR (1) parser generator written by Ruby. Usage: lrama [options] FILE BANNER o.separator '' o.separator 'STDIN mode:' o.separator 'lrama [options] - FILE read grammar from STDIN' o.separator '' o.separator 'Tuning the Parser:' o.on('-S', '--skeleton=FILE', 'specify the skeleton to use') {|v| @options.skeleton = v } o.on('-t', '--debug', 'display debugging outputs of internal parser') {|v| @options.debug = true } o.on('-D', '--define=NAME[=VALUE]', Array, "similar to '%define NAME VALUE'") {|v| @options.define = v } o.separator '' o.separator 'Output:' o.on('-H', '--header=[FILE]', 'also produce a header file named FILE') {|v| @options.header = true; @options.header_file = v } o.on('-d', 'also produce a header file') { @options.header = true } o.on('-r', '--report=REPORTS', Array, 'also produce details on the automaton') {|v| @report = v } o.on_tail '' o.on_tail 'REPORTS is a list of comma-separated words that can include:' o.on_tail ' states describe the states' o.on_tail ' itemsets complete the core item sets with their closure' o.on_tail ' lookaheads explicitly associate lookahead tokens to items' o.on_tail ' solved describe shift/reduce conflicts solving' o.on_tail ' counterexamples, cex generate conflict counterexamples' o.on_tail ' rules list unused rules' o.on_tail ' terms list unused terminals' o.on_tail ' verbose report detailed internal state and analysis results' o.on_tail ' all include all the above reports' o.on_tail ' none disable all reports' o.on('--report-file=FILE', 'also produce details on the automaton output to a file named FILE') {|v| @options.report_file = v } o.on('-o', '--output=FILE', 'leave output to FILE') {|v| @options.outfile = v } o.on('--trace=TRACES', Array, 'also output trace logs at runtime') {|v| @trace = v } o.on_tail '' o.on_tail 'TRACES is a list of comma-separated words that can include:' o.on_tail ' automaton display states' o.on_tail ' closure display states' o.on_tail ' rules display grammar rules' o.on_tail ' only-explicit-rules display only explicit grammar rules' o.on_tail ' actions display grammar rules with actions' o.on_tail ' time display generation time' o.on_tail ' all include all the above traces' o.on_tail ' none disable all traces' o.on('-v', '--verbose', "same as '--report=state'") {|_v| @report << 'states' } o.separator '' o.separator 'Diagnostics:' o.on('-W', '--warnings', 'report the warnings') {|v| @options.diagnostic = true } o.separator '' o.separator 'Error Recovery:' o.on('-e', 'enable error recovery') {|v| @options.error_recovery = true } o.separator '' o.separator 'Other options:' o.on('-V', '--version', "output version information and exit") {|v| puts "lrama #{Lrama::VERSION}"; exit 0 } o.on('-h', '--help', "display this help and exit") {|v| puts o; exit 0 } o.on_tail o.parse!(argv) end end ALIASED_REPORTS = { cex: :counterexamples }.freeze VALID_REPORTS = %i[states itemsets lookaheads solved counterexamples rules terms verbose].freeze def validate_report(report) h = { grammar: true } return h if report.empty? return {} if report == ['none'] if report == ['all'] VALID_REPORTS.each { |r| h[r] = true } return h end report.each do |r| aliased = aliased_report_option(r) if VALID_REPORTS.include?(aliased) h[aliased] = true else raise "Invalid report option \"#{r}\"." end end return h end def aliased_report_option(opt) (ALIASED_REPORTS[opt.to_sym] || opt).to_sym end VALID_TRACES = %w[ locations scan parse automaton bitsets closure grammar rules only-explicit-rules actions resource sets muscles tools m4-early m4 skeleton time ielr cex ].freeze NOT_SUPPORTED_TRACES = %w[ locations scan parse bitsets grammar resource sets muscles tools m4-early m4 skeleton ielr cex ].freeze SUPPORTED_TRACES = VALID_TRACES - NOT_SUPPORTED_TRACES def validate_trace(trace) h = {} return h if trace.empty? || trace == ['none'] all_traces = SUPPORTED_TRACES - %w[only-explicit-rules] if trace == ['all'] all_traces.each { |t| h[t.gsub(/-/, '_').to_sym] = true } return h end trace.each do |t| if SUPPORTED_TRACES.include?(t) h[t.gsub(/-/, '_').to_sym] = true else raise "Invalid trace option \"#{t}\"." end end return h end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/grammar_validator.rb0000644000000000000000000000013115077107276026010 xustar0029 mtime=1761382078.14342046 30 atime=1761382080.166411206 30 ctime=1761382108.286302734 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar_validator.rb0000644000175100017510000000163515077107276026406 0ustar00runnerrunner# frozen_string_literal: true module Lrama class GrammarValidator def initialize(grammar, states, logger) @grammar = grammar @states = states @logger = logger end def valid? conflicts_within_threshold? end private def conflicts_within_threshold? return true unless @grammar.expect [sr_conflicts_within_threshold(@grammar.expect), rr_conflicts_within_threshold(0)].all? end def sr_conflicts_within_threshold(expected) return true if expected == @states.sr_conflicts_count @logger.error("shift/reduce conflicts: #{@states.sr_conflicts_count} found, #{expected} expected") false end def rr_conflicts_within_threshold(expected) return true if expected == @states.rr_conflicts_count @logger.error("reduce/reduce conflicts: #{@states.rr_conflicts_count} found, #{expected} expected") false end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/diagnostics.rb0000644000000000000000000000013115077107276024624 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 30 ctime=1761382108.305302679 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/diagnostics.rb0000644000175100017510000000152015077107276025213 0ustar00runnerrunner# frozen_string_literal: true module Lrama class Diagnostics def initialize(grammar, states, logger) @grammar = grammar @states = states @logger = logger end def run(diagnostic) if diagnostic diagnose_conflict diagnose_parameterizing_redefined end end private def diagnose_conflict if @states.sr_conflicts_count != 0 @logger.warn("shift/reduce conflicts: #{@states.sr_conflicts_count} found") end if @states.rr_conflicts_count != 0 @logger.warn("reduce/reduce conflicts: #{@states.rr_conflicts_count} found") end end def diagnose_parameterizing_redefined @grammar.parameterizing_rule_resolver.redefined_rules.each do |rule| @logger.warn("parameterizing rule redefined: #{rule}") end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/options.rb0000644000000000000000000000013115077107276024010 xustar0029 mtime=1761382078.14342046 30 atime=1761382080.166411206 30 ctime=1761382108.316302647 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/options.rb0000644000175100017510000000125415077107276024403 0ustar00runnerrunner# frozen_string_literal: true module Lrama # Command line options. class Options attr_accessor :skeleton, :header, :header_file, :report_file, :outfile, :error_recovery, :grammar_file, :trace_opts, :report_opts, :diagnostic, :y, :debug, :define def initialize @skeleton = "bison/yacc.c" @define = {} @header = false @header_file = nil @report_file = nil @outfile = "y.tab.c" @error_recovery = false @grammar_file = nil @trace_opts = nil @report_opts = nil @diagnostic = false @y = STDIN @debug = false end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/grammar.rb0000644000000000000000000000013015077107276023742 xustar0030 mtime=1761382078.142420465 29 atime=1761382080.16541121 29 ctime=1761382108.31530265 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/grammar.rb0000644000175100017510000002643615077107276024347 0ustar00runnerrunner# frozen_string_literal: true require "forwardable" require_relative "grammar/auxiliary" require_relative "grammar/binding" require_relative "grammar/code" require_relative "grammar/counter" require_relative "grammar/destructor" require_relative "grammar/error_token" require_relative "grammar/parameterizing_rule" require_relative "grammar/percent_code" require_relative "grammar/precedence" require_relative "grammar/printer" require_relative "grammar/reference" require_relative "grammar/rule" require_relative "grammar/rule_builder" require_relative "grammar/symbol" require_relative "grammar/symbols" require_relative "grammar/type" require_relative "grammar/union" require_relative "lexer" module Lrama # Grammar is the result of parsing an input grammar file class Grammar extend Forwardable attr_reader :percent_codes, :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol, :aux, :parameterizing_rule_resolver attr_accessor :union, :expect, :printers, :error_tokens, :lex_param, :parse_param, :initial_action, :after_shift, :before_reduce, :after_reduce, :after_shift_error_token, :after_pop_stack, :symbols_resolver, :types, :rules, :rule_builders, :sym_to_rules, :no_stdlib, :locations, :define def_delegators "@symbols_resolver", :symbols, :nterms, :terms, :add_nterm, :add_term, :find_term_by_s_value, :find_symbol_by_number!, :find_symbol_by_id!, :token_to_symbol, :find_symbol_by_s_value!, :fill_symbol_number, :fill_nterm_type, :fill_printer, :fill_destructor, :fill_error_token, :sort_by_number! def initialize(rule_counter, define = {}) @rule_counter = rule_counter # Code defined by "%code" @percent_codes = [] @printers = [] @destructors = [] @error_tokens = [] @symbols_resolver = Grammar::Symbols::Resolver.new @types = [] @rule_builders = [] @rules = [] @sym_to_rules = {} @parameterizing_rule_resolver = ParameterizingRule::Resolver.new @empty_symbol = nil @eof_symbol = nil @error_symbol = nil @undef_symbol = nil @accept_symbol = nil @aux = Auxiliary.new @no_stdlib = false @locations = false @define = define.map {|d| d.split('=') }.to_h append_special_symbols end def create_rule_builder(rule_counter, midrule_action_counter) RuleBuilder.new(rule_counter, midrule_action_counter, @parameterizing_rule_resolver) end def add_percent_code(id:, code:) @percent_codes << PercentCode.new(id.s_value, code.s_value) end def add_destructor(ident_or_tags:, token_code:, lineno:) @destructors << Destructor.new(ident_or_tags: ident_or_tags, token_code: token_code, lineno: lineno) end def add_printer(ident_or_tags:, token_code:, lineno:) @printers << Printer.new(ident_or_tags: ident_or_tags, token_code: token_code, lineno: lineno) end def add_error_token(ident_or_tags:, token_code:, lineno:) @error_tokens << ErrorToken.new(ident_or_tags: ident_or_tags, token_code: token_code, lineno: lineno) end def add_type(id:, tag:) @types << Type.new(id: id, tag: tag) end def add_nonassoc(sym, precedence) set_precedence(sym, Precedence.new(type: :nonassoc, precedence: precedence)) end def add_left(sym, precedence) set_precedence(sym, Precedence.new(type: :left, precedence: precedence)) end def add_right(sym, precedence) set_precedence(sym, Precedence.new(type: :right, precedence: precedence)) end def add_precedence(sym, precedence) set_precedence(sym, Precedence.new(type: :precedence, precedence: precedence)) end def set_precedence(sym, precedence) raise "" if sym.nterm? sym.precedence = precedence end def set_union(code, lineno) @union = Union.new(code: code, lineno: lineno) end def add_rule_builder(builder) @rule_builders << builder end def add_parameterizing_rule(rule) @parameterizing_rule_resolver.add_parameterizing_rule(rule) end def parameterizing_rules @parameterizing_rule_resolver.rules end def insert_before_parameterizing_rules(rules) @parameterizing_rule_resolver.rules = rules + @parameterizing_rule_resolver.rules end def prologue_first_lineno=(prologue_first_lineno) @aux.prologue_first_lineno = prologue_first_lineno end def prologue=(prologue) @aux.prologue = prologue end def epilogue_first_lineno=(epilogue_first_lineno) @aux.epilogue_first_lineno = epilogue_first_lineno end def epilogue=(epilogue) @aux.epilogue = epilogue end def prepare resolve_inline_rules normalize_rules collect_symbols set_lhs_and_rhs fill_default_precedence fill_symbols fill_sym_to_rules compute_nullable compute_first_set set_locations end # TODO: More validation methods # # * Validation for no_declared_type_reference def validate! @symbols_resolver.validate! validate_rule_lhs_is_nterm! end def find_rules_by_symbol!(sym) find_rules_by_symbol(sym) || (raise "Rules for #{sym} not found") end def find_rules_by_symbol(sym) @sym_to_rules[sym.number] end def ielr_defined? @define.key?('lr.type') && @define['lr.type'] == 'ielr' end private def compute_nullable @rules.each do |rule| case when rule.empty_rule? rule.nullable = true when rule.rhs.any?(&:term) rule.nullable = false else # noop end end while true do rs = @rules.select {|e| e.nullable.nil? } nts = nterms.select {|e| e.nullable.nil? } rule_count_1 = rs.count nterm_count_1 = nts.count rs.each do |rule| if rule.rhs.all?(&:nullable) rule.nullable = true end end nts.each do |nterm| find_rules_by_symbol!(nterm).each do |rule| if rule.nullable nterm.nullable = true end end end rule_count_2 = @rules.count {|e| e.nullable.nil? } nterm_count_2 = nterms.count {|e| e.nullable.nil? } if (rule_count_1 == rule_count_2) && (nterm_count_1 == nterm_count_2) break end end rules.select {|r| r.nullable.nil? }.each do |rule| rule.nullable = false end nterms.select {|e| e.nullable.nil? }.each do |nterm| nterm.nullable = false end end def compute_first_set terms.each do |term| term.first_set = Set.new([term]).freeze term.first_set_bitmap = Lrama::Bitmap.from_array([term.number]) end nterms.each do |nterm| nterm.first_set = Set.new([]).freeze nterm.first_set_bitmap = Lrama::Bitmap.from_array([]) end while true do changed = false @rules.each do |rule| rule.rhs.each do |r| if rule.lhs.first_set_bitmap | r.first_set_bitmap != rule.lhs.first_set_bitmap changed = true rule.lhs.first_set_bitmap = rule.lhs.first_set_bitmap | r.first_set_bitmap end break unless r.nullable end end break unless changed end nterms.each do |nterm| nterm.first_set = Lrama::Bitmap.to_array(nterm.first_set_bitmap).map do |number| find_symbol_by_number!(number) end.to_set end end def setup_rules @rule_builders.each do |builder| builder.setup_rules end end def append_special_symbols # YYEMPTY (token_id: -2, number: -2) is added when a template is evaluated # term = add_term(id: Token.new(Token::Ident, "YYEMPTY"), token_id: -2) # term.number = -2 # @empty_symbol = term # YYEOF term = add_term(id: Lrama::Lexer::Token::Ident.new(s_value: "YYEOF"), alias_name: "\"end of file\"", token_id: 0) term.number = 0 term.eof_symbol = true @eof_symbol = term # YYerror term = add_term(id: Lrama::Lexer::Token::Ident.new(s_value: "YYerror"), alias_name: "error") term.number = 1 term.error_symbol = true @error_symbol = term # YYUNDEF term = add_term(id: Lrama::Lexer::Token::Ident.new(s_value: "YYUNDEF"), alias_name: "\"invalid token\"") term.number = 2 term.undef_symbol = true @undef_symbol = term # $accept term = add_nterm(id: Lrama::Lexer::Token::Ident.new(s_value: "$accept")) term.accept_symbol = true @accept_symbol = term end def resolve_inline_rules while @rule_builders.any?(&:has_inline_rules?) do @rule_builders = @rule_builders.flat_map do |builder| if builder.has_inline_rules? builder.resolve_inline_rules else builder end end end end def normalize_rules # Add $accept rule to the top of rules rule_builder = @rule_builders.first # : RuleBuilder lineno = rule_builder ? rule_builder.line : 0 @rules << Rule.new(id: @rule_counter.increment, _lhs: @accept_symbol.id, _rhs: [rule_builder.lhs, @eof_symbol.id], token_code: nil, lineno: lineno) setup_rules @rule_builders.each do |builder| builder.rules.each do |rule| add_nterm(id: rule._lhs, tag: rule.lhs_tag) @rules << rule end end @rules.sort_by!(&:id) end # Collect symbols from rules def collect_symbols @rules.flat_map(&:_rhs).each do |s| case s when Lrama::Lexer::Token::Char add_term(id: s) when Lrama::Lexer::Token # skip else raise "Unknown class: #{s}" end end end def set_lhs_and_rhs @rules.each do |rule| rule.lhs = token_to_symbol(rule._lhs) if rule._lhs rule.rhs = rule._rhs.map do |t| token_to_symbol(t) end end end # Rule inherits precedence from the last term in RHS. # # https://www.gnu.org/software/bison/manual/html_node/How-Precedence.html def fill_default_precedence @rules.each do |rule| # Explicitly specified precedence has the highest priority next if rule.precedence_sym precedence_sym = nil rule.rhs.each do |sym| precedence_sym = sym if sym.term? end rule.precedence_sym = precedence_sym end end def fill_symbols fill_symbol_number fill_nterm_type(@types) fill_printer(@printers) fill_destructor(@destructors) fill_error_token(@error_tokens) sort_by_number! end def fill_sym_to_rules @rules.each do |rule| key = rule.lhs.number @sym_to_rules[key] ||= [] @sym_to_rules[key] << rule end end def validate_rule_lhs_is_nterm! errors = [] #: Array[String] rules.each do |rule| next if rule.lhs.nterm? errors << "[BUG] LHS of #{rule.display_name} (line: #{rule.lineno}) is term. It should be nterm." end return if errors.empty? raise errors.join("\n") end def set_locations @locations = @locations || @rules.any? {|rule| rule.contains_at_reference? } end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/parser.rb0000644000000000000000000000013215077107276023612 xustar0030 mtime=1761382078.144420456 30 atime=1761382080.166411206 30 ctime=1761382108.314302653 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/parser.rb0000644000175100017510000016471715077107276024222 0ustar00runnerrunner# # DO NOT MODIFY!!!! # This file is automatically generated by Racc 1.8.1 # from Racc grammar file "parser.y". # ###### racc/parser.rb begin unless $".find {|p| p.end_with?('/racc/parser.rb')} $".push "#{__dir__}/racc/parser.rb" self.class.module_eval(<<'...end racc/parser.rb/module_eval...', 'racc/parser.rb', 1) #-- # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the same terms of ruby. # # As a special exception, when this code is copied by Racc # into a Racc output file, you may use that output file # without restriction. #++ unless $".find {|p| p.end_with?('/racc/info.rb')} $".push "#{__dir__}/racc/info.rb" module Racc VERSION = '1.8.1' Version = VERSION Copyright = 'Copyright (c) 1999-2006 Minero Aoki' end end module Racc class ParseError < StandardError; end end unless defined?(::ParseError) ParseError = Racc::ParseError # :nodoc: end # Racc is an LALR(1) parser generator. # It is written in Ruby itself, and generates Ruby programs. # # == Command-line Reference # # racc [-ofilename] [--output-file=filename] # [-erubypath] [--executable=rubypath] # [-v] [--verbose] # [-Ofilename] [--log-file=filename] # [-g] [--debug] # [-E] [--embedded] # [-l] [--no-line-convert] # [-c] [--line-convert-all] # [-a] [--no-omit-actions] # [-C] [--check-only] # [-S] [--output-status] # [--version] [--copyright] [--help] grammarfile # # [+grammarfile+] # Racc grammar file. Any extension is permitted. # [-o+outfile+, --output-file=+outfile+] # A filename for output. default is <+filename+>.tab.rb # [-O+filename+, --log-file=+filename+] # Place logging output in file +filename+. # Default log file name is <+filename+>.output. # [-e+rubypath+, --executable=+rubypath+] # output executable file(mode 755). where +path+ is the Ruby interpreter. # [-v, --verbose] # verbose mode. create +filename+.output file, like yacc's y.output file. # [-g, --debug] # add debug code to parser class. To display debugging information, # use this '-g' option and set @yydebug true in parser class. # [-E, --embedded] # Output parser which doesn't need runtime files (racc/parser.rb). # [-F, --frozen] # Output parser which declares frozen_string_literals: true # [-C, --check-only] # Check syntax of racc grammar file and quit. # [-S, --output-status] # Print messages time to time while compiling. # [-l, --no-line-convert] # turns off line number converting. # [-c, --line-convert-all] # Convert line number of actions, inner, header and footer. # [-a, --no-omit-actions] # Call all actions, even if an action is empty. # [--version] # print Racc version and quit. # [--copyright] # Print copyright and quit. # [--help] # Print usage and quit. # # == Generating Parser Using Racc # # To compile Racc grammar file, simply type: # # $ racc parse.y # # This creates Ruby script file "parse.tab.y". The -o option can change the output filename. # # == Writing A Racc Grammar File # # If you want your own parser, you have to write a grammar file. # A grammar file contains the name of your parser class, grammar for the parser, # user code, and anything else. # When writing a grammar file, yacc's knowledge is helpful. # If you have not used yacc before, Racc is not too difficult. # # Here's an example Racc grammar file. # # class Calcparser # rule # target: exp { print val[0] } # # exp: exp '+' exp # | exp '*' exp # | '(' exp ')' # | NUMBER # end # # Racc grammar files resemble yacc files. # But (of course), this is Ruby code. # yacc's $$ is the 'result', $0, $1... is # an array called 'val', and $-1, $-2... is an array called '_values'. # # See the {Grammar File Reference}[rdoc-ref:lib/racc/rdoc/grammar.en.rdoc] for # more information on grammar files. # # == Parser # # Then you must prepare the parse entry method. There are two types of # parse methods in Racc, Racc::Parser#do_parse and Racc::Parser#yyparse # # Racc::Parser#do_parse is simple. # # It's yyparse() of yacc, and Racc::Parser#next_token is yylex(). # This method must returns an array like [TOKENSYMBOL, ITS_VALUE]. # EOF is [false, false]. # (TOKENSYMBOL is a Ruby symbol (taken from String#intern) by default. # If you want to change this, see the grammar reference. # # Racc::Parser#yyparse is little complicated, but useful. # It does not use Racc::Parser#next_token, instead it gets tokens from any iterator. # # For example, yyparse(obj, :scan) causes # calling +obj#scan+, and you can return tokens by yielding them from +obj#scan+. # # == Debugging # # When debugging, "-v" or/and the "-g" option is helpful. # # "-v" creates verbose log file (.output). # "-g" creates a "Verbose Parser". # Verbose Parser prints the internal status when parsing. # But it's _not_ automatic. # You must use -g option and set +@yydebug+ to +true+ in order to get output. # -g option only creates the verbose parser. # # === Racc reported syntax error. # # Isn't there too many "end"? # grammar of racc file is changed in v0.10. # # Racc does not use '%' mark, while yacc uses huge number of '%' marks.. # # === Racc reported "XXXX conflicts". # # Try "racc -v xxxx.y". # It causes producing racc's internal log file, xxxx.output. # # === Generated parsers does not work correctly # # Try "racc -g xxxx.y". # This command let racc generate "debugging parser". # Then set @yydebug=true in your parser. # It produces a working log of your parser. # # == Re-distributing Racc runtime # # A parser, which is created by Racc, requires the Racc runtime module; # racc/parser.rb. # # Ruby 1.8.x comes with Racc runtime module, # you need NOT distribute Racc runtime files. # # If you want to include the Racc runtime module with your parser. # This can be done by using '-E' option: # # $ racc -E -omyparser.rb myparser.y # # This command creates myparser.rb which `includes' Racc runtime. # Only you must do is to distribute your parser file (myparser.rb). # # Note: parser.rb is ruby license, but your parser is not. # Your own parser is completely yours. module Racc unless defined?(Racc_No_Extensions) Racc_No_Extensions = false # :nodoc: end class Parser Racc_Runtime_Version = ::Racc::VERSION Racc_Runtime_Core_Version_R = ::Racc::VERSION begin if Object.const_defined?(:RUBY_ENGINE) and RUBY_ENGINE == 'jruby' require 'jruby' require 'racc/cparse-jruby.jar' com.headius.racc.Cparse.new.load(JRuby.runtime, false) else require 'racc/cparse' end unless new.respond_to?(:_racc_do_parse_c, true) raise LoadError, 'old cparse.so' end if Racc_No_Extensions raise LoadError, 'selecting ruby version of racc runtime core' end Racc_Main_Parsing_Routine = :_racc_do_parse_c # :nodoc: Racc_YY_Parse_Method = :_racc_yyparse_c # :nodoc: Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_C # :nodoc: Racc_Runtime_Type = 'c' # :nodoc: rescue LoadError Racc_Main_Parsing_Routine = :_racc_do_parse_rb Racc_YY_Parse_Method = :_racc_yyparse_rb Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_R Racc_Runtime_Type = 'ruby' end def Parser.racc_runtime_type # :nodoc: Racc_Runtime_Type end def _racc_setup @yydebug = false unless self.class::Racc_debug_parser @yydebug = false unless defined?(@yydebug) if @yydebug @racc_debug_out = $stderr unless defined?(@racc_debug_out) @racc_debug_out ||= $stderr end arg = self.class::Racc_arg arg[13] = true if arg.size < 14 arg end def _racc_init_sysvars @racc_state = [0] @racc_tstack = [] @racc_vstack = [] @racc_t = nil @racc_val = nil @racc_read_next = true @racc_user_yyerror = false @racc_error_status = 0 end # The entry point of the parser. This method is used with #next_token. # If Racc wants to get token (and its value), calls next_token. # # Example: # def parse # @q = [[1,1], # [2,2], # [3,3], # [false, '$']] # do_parse # end # # def next_token # @q.shift # end class_eval <<~RUBY, __FILE__, __LINE__ + 1 def do_parse #{Racc_Main_Parsing_Routine}(_racc_setup(), false) end RUBY # The method to fetch next token. # If you use #do_parse method, you must implement #next_token. # # The format of return value is [TOKEN_SYMBOL, VALUE]. # +token-symbol+ is represented by Ruby's symbol by default, e.g. :IDENT # for 'IDENT'. ";" (String) for ';'. # # The final symbol (End of file) must be false. def next_token raise NotImplementedError, "#{self.class}\#next_token is not defined" end def _racc_do_parse_rb(arg, in_debug) action_table, action_check, action_default, action_pointer, _, _, _, _, _, _, token_table, * = arg _racc_init_sysvars tok = act = i = nil catch(:racc_end_parse) { while true if i = action_pointer[@racc_state[-1]] if @racc_read_next if @racc_t != 0 # not EOF tok, @racc_val = next_token() unless tok # EOF @racc_t = 0 else @racc_t = (token_table[tok] or 1) # error token end racc_read_token(@racc_t, tok, @racc_val) if @yydebug @racc_read_next = false end end i += @racc_t unless i >= 0 and act = action_table[i] and action_check[i] == @racc_state[-1] act = action_default[@racc_state[-1]] end else act = action_default[@racc_state[-1]] end while act = _racc_evalact(act, arg) ; end end } end # Another entry point for the parser. # If you use this method, you must implement RECEIVER#METHOD_ID method. # # RECEIVER#METHOD_ID is a method to get next token. # It must 'yield' the token, which format is [TOKEN-SYMBOL, VALUE]. class_eval <<~RUBY, __FILE__, __LINE__ + 1 def yyparse(recv, mid) #{Racc_YY_Parse_Method}(recv, mid, _racc_setup(), false) end RUBY def _racc_yyparse_rb(recv, mid, arg, c_debug) action_table, action_check, action_default, action_pointer, _, _, _, _, _, _, token_table, * = arg _racc_init_sysvars catch(:racc_end_parse) { until i = action_pointer[@racc_state[-1]] while act = _racc_evalact(action_default[@racc_state[-1]], arg) ; end end recv.__send__(mid) do |tok, val| unless tok @racc_t = 0 else @racc_t = (token_table[tok] or 1) # error token end @racc_val = val @racc_read_next = false i += @racc_t unless i >= 0 and act = action_table[i] and action_check[i] == @racc_state[-1] act = action_default[@racc_state[-1]] end while act = _racc_evalact(act, arg) ; end while !(i = action_pointer[@racc_state[-1]]) || ! @racc_read_next || @racc_t == 0 # $ unless i and i += @racc_t and i >= 0 and act = action_table[i] and action_check[i] == @racc_state[-1] act = action_default[@racc_state[-1]] end while act = _racc_evalact(act, arg) ; end end end } end ### ### common ### def _racc_evalact(act, arg) action_table, action_check, _, action_pointer, _, _, _, _, _, _, _, shift_n, reduce_n, * = arg nerr = 0 # tmp if act > 0 and act < shift_n # # shift # if @racc_error_status > 0 @racc_error_status -= 1 unless @racc_t <= 1 # error token or EOF end @racc_vstack.push @racc_val @racc_state.push act @racc_read_next = true if @yydebug @racc_tstack.push @racc_t racc_shift @racc_t, @racc_tstack, @racc_vstack end elsif act < 0 and act > -reduce_n # # reduce # code = catch(:racc_jump) { @racc_state.push _racc_do_reduce(arg, act) false } if code case code when 1 # yyerror @racc_user_yyerror = true # user_yyerror return -reduce_n when 2 # yyaccept return shift_n else raise '[Racc Bug] unknown jump code' end end elsif act == shift_n # # accept # racc_accept if @yydebug throw :racc_end_parse, @racc_vstack[0] elsif act == -reduce_n # # error # case @racc_error_status when 0 unless arg[21] # user_yyerror nerr += 1 on_error @racc_t, @racc_val, @racc_vstack end when 3 if @racc_t == 0 # is $ # We're at EOF, and another error occurred immediately after # attempting auto-recovery throw :racc_end_parse, nil end @racc_read_next = true end @racc_user_yyerror = false @racc_error_status = 3 while true if i = action_pointer[@racc_state[-1]] i += 1 # error token if i >= 0 and (act = action_table[i]) and action_check[i] == @racc_state[-1] break end end throw :racc_end_parse, nil if @racc_state.size <= 1 @racc_state.pop @racc_vstack.pop if @yydebug @racc_tstack.pop racc_e_pop @racc_state, @racc_tstack, @racc_vstack end end return act else raise "[Racc Bug] unknown action #{act.inspect}" end racc_next_state(@racc_state[-1], @racc_state) if @yydebug nil end def _racc_do_reduce(arg, act) _, _, _, _, goto_table, goto_check, goto_default, goto_pointer, nt_base, reduce_table, _, _, _, use_result, * = arg state = @racc_state vstack = @racc_vstack tstack = @racc_tstack i = act * -3 len = reduce_table[i] reduce_to = reduce_table[i+1] method_id = reduce_table[i+2] void_array = [] tmp_t = tstack[-len, len] if @yydebug tmp_v = vstack[-len, len] tstack[-len, len] = void_array if @yydebug vstack[-len, len] = void_array state[-len, len] = void_array # tstack must be updated AFTER method call if use_result vstack.push __send__(method_id, tmp_v, vstack, tmp_v[0]) else vstack.push __send__(method_id, tmp_v, vstack) end tstack.push reduce_to racc_reduce(tmp_t, reduce_to, tstack, vstack) if @yydebug k1 = reduce_to - nt_base if i = goto_pointer[k1] i += state[-1] if i >= 0 and (curstate = goto_table[i]) and goto_check[i] == k1 return curstate end end goto_default[k1] end # This method is called when a parse error is found. # # ERROR_TOKEN_ID is an internal ID of token which caused error. # You can get string representation of this ID by calling # #token_to_str. # # ERROR_VALUE is a value of error token. # # value_stack is a stack of symbol values. # DO NOT MODIFY this object. # # This method raises ParseError by default. # # If this method returns, parsers enter "error recovering mode". def on_error(t, val, vstack) raise ParseError, sprintf("parse error on value %s (%s)", val.inspect, token_to_str(t) || '?') end # Enter error recovering mode. # This method does not call #on_error. def yyerror throw :racc_jump, 1 end # Exit parser. # Return value is +Symbol_Value_Stack[0]+. def yyaccept throw :racc_jump, 2 end # Leave error recovering mode. def yyerrok @racc_error_status = 0 end # For debugging output def racc_read_token(t, tok, val) @racc_debug_out.print 'read ' @racc_debug_out.print tok.inspect, '(', racc_token2str(t), ') ' @racc_debug_out.puts val.inspect @racc_debug_out.puts end def racc_shift(tok, tstack, vstack) @racc_debug_out.puts "shift #{racc_token2str tok}" racc_print_stacks tstack, vstack @racc_debug_out.puts end def racc_reduce(toks, sim, tstack, vstack) out = @racc_debug_out out.print 'reduce ' if toks.empty? out.print ' ' else toks.each {|t| out.print ' ', racc_token2str(t) } end out.puts " --> #{racc_token2str(sim)}" racc_print_stacks tstack, vstack @racc_debug_out.puts end def racc_accept @racc_debug_out.puts 'accept' @racc_debug_out.puts end def racc_e_pop(state, tstack, vstack) @racc_debug_out.puts 'error recovering mode: pop token' racc_print_states state racc_print_stacks tstack, vstack @racc_debug_out.puts end def racc_next_state(curstate, state) @racc_debug_out.puts "goto #{curstate}" racc_print_states state @racc_debug_out.puts end def racc_print_stacks(t, v) out = @racc_debug_out out.print ' [' t.each_index do |i| out.print ' (', racc_token2str(t[i]), ' ', v[i].inspect, ')' end out.puts ' ]' end def racc_print_states(s) out = @racc_debug_out out.print ' [' s.each {|st| out.print ' ', st } out.puts ' ]' end def racc_token2str(tok) self.class::Racc_token_to_s_table[tok] or raise "[Racc Bug] can't convert token #{tok} to string" end # Convert internal ID of token symbol to the string. def token_to_str(t) self.class::Racc_token_to_s_table[t] end end end ...end racc/parser.rb/module_eval... end ###### racc/parser.rb end module Lrama class Parser < Racc::Parser module_eval(<<'...end parser.y/module_eval...', 'parser.y', 428) include Lrama::Report::Duration def initialize(text, path, debug = false, define = {}) @grammar_file = Lrama::Lexer::GrammarFile.new(path, text) @yydebug = debug @rule_counter = Lrama::Grammar::Counter.new(0) @midrule_action_counter = Lrama::Grammar::Counter.new(1) @define = define end def parse report_duration(:parse) do @lexer = Lrama::Lexer.new(@grammar_file) @grammar = Lrama::Grammar.new(@rule_counter, @define) @precedence_number = 0 reset_precs do_parse @grammar end end def next_token @lexer.next_token end def on_error(error_token_id, error_value, value_stack) if error_value.is_a?(Lrama::Lexer::Token) location = error_value.location value = "'#{error_value.s_value}'" else location = @lexer.location value = error_value.inspect end error_message = "parse error on value #{value} (#{token_to_str(error_token_id) || '?'})" raise_parse_error(error_message, location) end def on_action_error(error_message, error_value) if error_value.is_a?(Lrama::Lexer::Token) location = error_value.location else location = @lexer.location end raise_parse_error(error_message, location) end private def reset_precs @prec_seen = false @code_after_prec = false end def begin_c_declaration(end_symbol) @lexer.status = :c_declaration @lexer.end_symbol = end_symbol end def end_c_declaration @lexer.status = :initial @lexer.end_symbol = nil end def raise_parse_error(error_message, location) raise ParseError, location.generate_error_message(error_message) end ...end parser.y/module_eval... ##### State transition tables begin ### racc_action_table = [ 89, 49, 90, 167, 49, 101, 173, 49, 101, 167, 49, 101, 173, 6, 101, 80, 49, 49, 48, 48, 41, 76, 76, 49, 49, 48, 48, 42, 76, 76, 49, 49, 48, 48, 101, 96, 113, 49, 87, 48, 150, 101, 96, 151, 45, 171, 169, 170, 151, 176, 170, 91, 169, 170, 81, 176, 170, 20, 24, 25, 26, 27, 28, 29, 30, 31, 87, 32, 33, 34, 35, 36, 37, 38, 39, 49, 4, 48, 5, 101, 96, 181, 182, 183, 128, 20, 24, 25, 26, 27, 28, 29, 30, 31, 46, 32, 33, 34, 35, 36, 37, 38, 39, 11, 12, 13, 14, 15, 16, 17, 18, 19, 53, 20, 24, 25, 26, 27, 28, 29, 30, 31, 53, 32, 33, 34, 35, 36, 37, 38, 39, 11, 12, 13, 14, 15, 16, 17, 18, 19, 44, 20, 24, 25, 26, 27, 28, 29, 30, 31, 53, 32, 33, 34, 35, 36, 37, 38, 39, 49, 4, 48, 5, 101, 96, 49, 49, 48, 48, 101, 101, 49, 49, 48, 48, 101, 101, 49, 49, 48, 197, 101, 101, 49, 49, 197, 48, 101, 101, 49, 49, 197, 48, 101, 181, 182, 183, 128, 204, 210, 217, 205, 205, 205, 49, 49, 48, 48, 49, 49, 48, 48, 49, 49, 48, 48, 181, 182, 183, 116, 117, 56, 53, 53, 53, 53, 53, 62, 63, 64, 65, 66, 68, 68, 68, 82, 53, 53, 104, 108, 108, 115, 122, 123, 125, 128, 129, 133, 139, 140, 141, 142, 144, 145, 101, 154, 139, 157, 154, 161, 162, 68, 164, 165, 172, 177, 154, 184, 128, 188, 154, 190, 128, 154, 199, 154, 128, 68, 165, 206, 165, 68, 68, 215, 128, 68 ] racc_action_check = [ 47, 153, 47, 153, 159, 153, 159, 178, 159, 178, 189, 178, 189, 1, 189, 39, 35, 36, 35, 36, 5, 35, 36, 37, 38, 37, 38, 6, 37, 38, 59, 74, 59, 74, 59, 59, 74, 60, 45, 60, 138, 60, 60, 138, 9, 156, 153, 153, 156, 159, 159, 47, 178, 178, 39, 189, 189, 45, 45, 45, 45, 45, 45, 45, 45, 45, 83, 45, 45, 45, 45, 45, 45, 45, 45, 61, 0, 61, 0, 61, 61, 166, 166, 166, 166, 83, 83, 83, 83, 83, 83, 83, 83, 83, 11, 83, 83, 83, 83, 83, 83, 83, 83, 3, 3, 3, 3, 3, 3, 3, 3, 3, 13, 3, 3, 3, 3, 3, 3, 3, 3, 3, 14, 3, 3, 3, 3, 3, 3, 3, 3, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 8, 97, 2, 97, 2, 97, 97, 71, 108, 71, 108, 71, 108, 109, 169, 109, 169, 109, 169, 176, 184, 176, 184, 176, 184, 190, 205, 190, 205, 190, 205, 206, 12, 206, 12, 206, 174, 174, 174, 174, 196, 201, 214, 196, 201, 214, 69, 76, 69, 76, 104, 105, 104, 105, 111, 113, 111, 113, 198, 198, 198, 81, 81, 16, 17, 20, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 40, 51, 56, 67, 70, 72, 80, 84, 85, 86, 87, 93, 107, 115, 116, 117, 118, 127, 128, 134, 140, 141, 143, 144, 145, 146, 150, 151, 152, 158, 163, 165, 167, 168, 171, 172, 173, 175, 177, 187, 188, 192, 193, 195, 197, 200, 202, 204, 209, 210, 216 ] racc_action_pointer = [ 66, 13, 150, 90, nil, 13, 27, nil, 118, 35, nil, 88, 187, 63, 73, 101, 216, 173, nil, nil, 174, nil, nil, nil, 175, 176, 177, 222, 223, 224, 225, 226, 224, 225, 226, 13, 14, 20, 21, 10, 233, nil, nil, nil, nil, 34, nil, -5, nil, nil, nil, 187, nil, nil, nil, nil, 188, nil, nil, 27, 34, 72, nil, nil, nil, nil, nil, 230, nil, 201, 231, 162, 232, nil, 28, nil, 202, nil, nil, nil, 200, 215, nil, 62, 233, 221, 222, 191, nil, nil, nil, nil, nil, 244, nil, nil, nil, 156, nil, nil, nil, nil, nil, nil, 205, 206, nil, 241, 163, 168, nil, 209, nil, 210, nil, 243, 206, 209, 240, nil, nil, nil, nil, nil, nil, nil, nil, 209, 248, nil, nil, nil, nil, nil, 247, nil, nil, nil, -2, nil, 208, 251, nil, 255, 211, 204, 210, nil, nil, nil, 253, 257, 217, -2, nil, nil, 3, nil, 218, 1, nil, nil, nil, 222, nil, 219, 30, 226, 214, 169, nil, 226, 223, 230, 143, 218, 174, 226, 4, nil, nil, nil, nil, nil, 175, nil, nil, 272, 228, 7, 180, nil, 222, 269, nil, 232, 156, 238, 165, nil, 234, 157, 273, nil, 274, 181, 186, nil, nil, 233, 230, nil, nil, nil, 158, nil, 277, nil, nil ] racc_action_default = [ -1, -128, -1, -3, -10, -128, -128, -2, -3, -128, -16, -128, -128, -128, -128, -128, -128, -128, -24, -25, -128, -32, -33, -34, -128, -128, -128, -128, -128, -128, -128, -128, -50, -50, -50, -128, -128, -128, -128, -128, -128, -13, 219, -4, -26, -128, -17, -123, -93, -94, -122, -14, -19, -85, -20, -21, -128, -23, -31, -128, -128, -128, -38, -39, -40, -41, -42, -43, -51, -128, -44, -128, -45, -46, -88, -90, -128, -47, -48, -49, -128, -128, -11, -5, -7, -95, -128, -68, -18, -124, -125, -126, -15, -128, -22, -27, -28, -29, -35, -83, -84, -127, -36, -37, -128, -52, -54, -56, -128, -79, -81, -88, -89, -128, -91, -128, -128, -128, -128, -6, -8, -9, -120, -96, -97, -98, -69, -128, -128, -86, -30, -55, -53, -57, -76, -82, -80, -92, -128, -62, -66, -128, -12, -128, -66, -128, -128, -58, -77, -78, -50, -128, -60, -64, -67, -70, -128, -121, -99, -100, -102, -119, -87, -128, -63, -66, -68, -93, -68, -128, -116, -128, -66, -93, -68, -68, -128, -66, -65, -71, -72, -108, -109, -110, -128, -74, -75, -128, -66, -101, -128, -103, -68, -50, -107, -59, -128, -93, -111, -117, -61, -128, -50, -106, -50, -128, -128, -112, -113, -128, -68, -104, -73, -114, -128, -118, -50, -115, -105 ] racc_goto_table = [ 69, 109, 50, 152, 57, 127, 84, 58, 112, 160, 114, 59, 60, 61, 86, 52, 54, 55, 98, 102, 103, 159, 106, 110, 175, 74, 74, 74, 74, 138, 9, 1, 3, 180, 7, 43, 120, 160, 109, 109, 195, 192, 121, 94, 119, 112, 40, 137, 118, 189, 47, 200, 86, 92, 175, 156, 130, 131, 132, 107, 135, 136, 88, 196, 111, 207, 111, 70, 72, 201, 73, 77, 78, 79, 67, 147, 134, 178, 148, 149, 93, 146, 124, 166, 179, 214, 185, 158, 208, 174, 187, 209, 191, 193, 107, 107, 143, nil, nil, 186, nil, 111, nil, 111, nil, nil, 194, nil, 166, nil, 202, nil, nil, nil, 198, nil, nil, nil, 163, 174, 198, nil, nil, nil, nil, nil, nil, nil, 216, nil, nil, nil, nil, nil, nil, 213, 198, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 203, nil, nil, nil, nil, nil, nil, nil, nil, 211, nil, 212, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 218 ] racc_goto_check = [ 27, 20, 29, 33, 15, 40, 8, 15, 46, 39, 46, 15, 15, 15, 12, 16, 16, 16, 22, 22, 22, 50, 28, 43, 38, 29, 29, 29, 29, 32, 7, 1, 6, 36, 6, 7, 5, 39, 20, 20, 33, 36, 9, 15, 8, 46, 10, 46, 11, 50, 13, 33, 12, 16, 38, 32, 22, 28, 28, 29, 43, 43, 14, 37, 29, 36, 29, 24, 24, 37, 25, 25, 25, 25, 23, 30, 31, 34, 41, 42, 44, 45, 48, 20, 40, 37, 40, 49, 51, 20, 52, 53, 40, 40, 29, 29, 54, nil, nil, 20, nil, 29, nil, 29, nil, nil, 20, nil, 20, nil, 40, nil, nil, nil, 20, nil, nil, nil, 27, 20, 20, nil, nil, nil, nil, nil, nil, nil, 40, nil, nil, nil, nil, nil, nil, 20, 20, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 27, nil, nil, nil, nil, nil, nil, nil, nil, 27, nil, 27, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 27 ] racc_goto_pointer = [ nil, 31, nil, nil, nil, -48, 32, 27, -39, -42, 42, -34, -31, 38, 15, -13, 2, nil, nil, nil, -70, nil, -41, 42, 34, 35, nil, -32, -47, -10, -59, -31, -86, -137, -88, nil, -133, -121, -135, -135, -82, -56, -55, -48, 27, -48, -66, nil, -3, -57, -123, -110, -80, -108, -26 ] racc_goto_default = [ nil, nil, 2, 8, 83, nil, nil, nil, nil, nil, nil, nil, 10, nil, nil, 51, nil, 21, 22, 23, 95, 97, nil, nil, nil, nil, 105, 71, nil, 99, nil, nil, nil, nil, 153, 126, nil, nil, 168, 155, nil, 100, nil, nil, nil, nil, 75, 85, nil, nil, nil, nil, nil, nil, nil ] racc_reduce_table = [ 0, 0, :racc_error, 0, 63, :_reduce_1, 2, 63, :_reduce_2, 0, 64, :_reduce_3, 2, 64, :_reduce_4, 1, 65, :_reduce_5, 2, 65, :_reduce_6, 0, 66, :_reduce_none, 1, 66, :_reduce_none, 5, 58, :_reduce_none, 0, 67, :_reduce_10, 0, 68, :_reduce_11, 5, 59, :_reduce_12, 2, 59, :_reduce_none, 1, 73, :_reduce_14, 2, 73, :_reduce_15, 1, 60, :_reduce_none, 2, 60, :_reduce_17, 3, 60, :_reduce_18, 2, 60, :_reduce_none, 2, 60, :_reduce_20, 2, 60, :_reduce_21, 3, 60, :_reduce_22, 2, 60, :_reduce_23, 1, 60, :_reduce_24, 1, 60, :_reduce_25, 2, 60, :_reduce_none, 1, 78, :_reduce_27, 1, 78, :_reduce_28, 1, 79, :_reduce_29, 2, 79, :_reduce_30, 2, 69, :_reduce_31, 1, 69, :_reduce_none, 1, 69, :_reduce_none, 1, 69, :_reduce_none, 3, 69, :_reduce_35, 3, 69, :_reduce_36, 3, 69, :_reduce_37, 2, 69, :_reduce_38, 2, 69, :_reduce_39, 2, 69, :_reduce_40, 2, 69, :_reduce_41, 2, 69, :_reduce_42, 2, 74, :_reduce_none, 2, 74, :_reduce_44, 2, 74, :_reduce_45, 2, 74, :_reduce_46, 2, 74, :_reduce_47, 2, 74, :_reduce_48, 2, 74, :_reduce_49, 0, 84, :_reduce_none, 1, 84, :_reduce_none, 1, 85, :_reduce_52, 2, 85, :_reduce_53, 2, 80, :_reduce_54, 3, 80, :_reduce_55, 0, 88, :_reduce_none, 1, 88, :_reduce_none, 3, 83, :_reduce_58, 8, 75, :_reduce_59, 5, 76, :_reduce_60, 8, 76, :_reduce_61, 1, 89, :_reduce_62, 3, 89, :_reduce_63, 1, 90, :_reduce_64, 3, 90, :_reduce_65, 0, 96, :_reduce_none, 1, 96, :_reduce_none, 0, 97, :_reduce_none, 1, 97, :_reduce_none, 1, 91, :_reduce_70, 3, 91, :_reduce_71, 3, 91, :_reduce_72, 6, 91, :_reduce_73, 3, 91, :_reduce_74, 3, 91, :_reduce_75, 0, 99, :_reduce_none, 1, 99, :_reduce_none, 1, 87, :_reduce_78, 1, 100, :_reduce_79, 2, 100, :_reduce_80, 2, 81, :_reduce_81, 3, 81, :_reduce_82, 1, 77, :_reduce_none, 1, 77, :_reduce_none, 0, 101, :_reduce_85, 0, 102, :_reduce_86, 5, 72, :_reduce_87, 1, 103, :_reduce_88, 2, 103, :_reduce_89, 1, 82, :_reduce_90, 2, 82, :_reduce_91, 3, 82, :_reduce_92, 1, 86, :_reduce_93, 1, 86, :_reduce_94, 0, 105, :_reduce_none, 1, 105, :_reduce_none, 2, 61, :_reduce_none, 2, 61, :_reduce_none, 4, 104, :_reduce_99, 1, 106, :_reduce_100, 3, 106, :_reduce_101, 1, 107, :_reduce_102, 3, 107, :_reduce_103, 5, 107, :_reduce_104, 7, 107, :_reduce_105, 4, 107, :_reduce_106, 3, 107, :_reduce_107, 1, 93, :_reduce_108, 1, 93, :_reduce_109, 1, 93, :_reduce_110, 0, 108, :_reduce_none, 1, 108, :_reduce_none, 2, 94, :_reduce_113, 3, 94, :_reduce_114, 4, 94, :_reduce_115, 0, 109, :_reduce_116, 0, 110, :_reduce_117, 5, 95, :_reduce_118, 3, 92, :_reduce_119, 0, 111, :_reduce_120, 3, 62, :_reduce_121, 1, 70, :_reduce_none, 0, 71, :_reduce_none, 1, 71, :_reduce_none, 1, 71, :_reduce_none, 1, 71, :_reduce_none, 1, 98, :_reduce_127 ] racc_reduce_n = 128 racc_shift_n = 219 racc_token_table = { false => 0, :error => 1, :C_DECLARATION => 2, :CHARACTER => 3, :IDENT_COLON => 4, :IDENTIFIER => 5, :INTEGER => 6, :STRING => 7, :TAG => 8, "%%" => 9, "%{" => 10, "%}" => 11, "%require" => 12, "%expect" => 13, "%define" => 14, "%param" => 15, "%lex-param" => 16, "%parse-param" => 17, "%code" => 18, "%initial-action" => 19, "%no-stdlib" => 20, "%locations" => 21, ";" => 22, "%union" => 23, "%destructor" => 24, "%printer" => 25, "%error-token" => 26, "%after-shift" => 27, "%before-reduce" => 28, "%after-reduce" => 29, "%after-shift-error-token" => 30, "%after-pop-stack" => 31, "-temp-group" => 32, "%token" => 33, "%type" => 34, "%nterm" => 35, "%left" => 36, "%right" => 37, "%precedence" => 38, "%nonassoc" => 39, "%rule" => 40, "(" => 41, ")" => 42, ":" => 43, "%inline" => 44, "," => 45, "|" => 46, "%empty" => 47, "%prec" => 48, "{" => 49, "}" => 50, "?" => 51, "+" => 52, "*" => 53, "[" => 54, "]" => 55, "{...}" => 56 } racc_nt_base = 57 racc_use_result_var = true Racc_arg = [ racc_action_table, racc_action_check, racc_action_default, racc_action_pointer, racc_goto_table, racc_goto_check, racc_goto_default, racc_goto_pointer, racc_nt_base, racc_reduce_table, racc_token_table, racc_shift_n, racc_reduce_n, racc_use_result_var ] Ractor.make_shareable(Racc_arg) if defined?(Ractor) Racc_token_to_s_table = [ "$end", "error", "C_DECLARATION", "CHARACTER", "IDENT_COLON", "IDENTIFIER", "INTEGER", "STRING", "TAG", "\"%%\"", "\"%{\"", "\"%}\"", "\"%require\"", "\"%expect\"", "\"%define\"", "\"%param\"", "\"%lex-param\"", "\"%parse-param\"", "\"%code\"", "\"%initial-action\"", "\"%no-stdlib\"", "\"%locations\"", "\";\"", "\"%union\"", "\"%destructor\"", "\"%printer\"", "\"%error-token\"", "\"%after-shift\"", "\"%before-reduce\"", "\"%after-reduce\"", "\"%after-shift-error-token\"", "\"%after-pop-stack\"", "\"-temp-group\"", "\"%token\"", "\"%type\"", "\"%nterm\"", "\"%left\"", "\"%right\"", "\"%precedence\"", "\"%nonassoc\"", "\"%rule\"", "\"(\"", "\")\"", "\":\"", "\"%inline\"", "\",\"", "\"|\"", "\"%empty\"", "\"%prec\"", "\"{\"", "\"}\"", "\"?\"", "\"+\"", "\"*\"", "\"[\"", "\"]\"", "\"{...}\"", "$start", "input", "prologue_declaration", "bison_declaration", "rules_or_grammar_declaration", "epilogue_declaration", "\"-many@prologue_declaration\"", "\"-many@bison_declaration\"", "\"-many1@rules_or_grammar_declaration\"", "\"-option@epilogue_declaration\"", "@1", "@2", "grammar_declaration", "variable", "value", "param", "\"-many1@param\"", "symbol_declaration", "rule_declaration", "inline_declaration", "symbol", "\"-group@symbol|TAG\"", "\"-many1@-group@symbol|TAG\"", "token_declarations", "symbol_declarations", "token_declarations_for_precedence", "token_declaration", "\"-option@TAG\"", "\"-many1@token_declaration\"", "id", "alias", "\"-option@INTEGER\"", "rule_args", "rule_rhs_list", "rule_rhs", "named_ref", "parameterizing_suffix", "parameterizing_args", "midrule_action", "\"-option@%empty\"", "\"-option@named_ref\"", "string_as_id", "\"-option@string_as_id\"", "\"-many1@symbol\"", "@3", "@4", "\"-many1@id\"", "rules", "\"-option@;\"", "rhs_list", "rhs", "\"-option@parameterizing_suffix\"", "@5", "@6", "@7" ] Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor) Racc_debug_parser = true ##### State transition tables end ##### # reduce 0 omitted module_eval(<<'.,.,', 'parser.y', 11) def _reduce_1(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., module_eval(<<'.,.,', 'parser.y', 11) def _reduce_2(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., module_eval(<<'.,.,', 'parser.y', 11) def _reduce_3(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., module_eval(<<'.,.,', 'parser.y', 11) def _reduce_4(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., module_eval(<<'.,.,', 'parser.y', 11) def _reduce_5(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., module_eval(<<'.,.,', 'parser.y', 11) def _reduce_6(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., # reduce 7 omitted # reduce 8 omitted # reduce 9 omitted module_eval(<<'.,.,', 'parser.y', 12) def _reduce_10(val, _values, result) begin_c_declaration("%}") @grammar.prologue_first_lineno = @lexer.line result end .,., module_eval(<<'.,.,', 'parser.y', 17) def _reduce_11(val, _values, result) end_c_declaration result end .,., module_eval(<<'.,.,', 'parser.y', 21) def _reduce_12(val, _values, result) @grammar.prologue = val[2].s_value result end .,., # reduce 13 omitted module_eval(<<'.,.,', 'parser.y', 54) def _reduce_14(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., module_eval(<<'.,.,', 'parser.y', 54) def _reduce_15(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., # reduce 16 omitted module_eval(<<'.,.,', 'parser.y', 26) def _reduce_17(val, _values, result) @grammar.expect = val[1] result end .,., module_eval(<<'.,.,', 'parser.y', 27) def _reduce_18(val, _values, result) @grammar.define[val[1].s_value] = val[2]&.s_value result end .,., # reduce 19 omitted module_eval(<<'.,.,', 'parser.y', 31) def _reduce_20(val, _values, result) val[1].each {|token| @grammar.lex_param = Grammar::Code::NoReferenceCode.new(type: :lex_param, token_code: token).token_code.s_value } result end .,., module_eval(<<'.,.,', 'parser.y', 37) def _reduce_21(val, _values, result) val[1].each {|token| @grammar.parse_param = Grammar::Code::NoReferenceCode.new(type: :parse_param, token_code: token).token_code.s_value } result end .,., module_eval(<<'.,.,', 'parser.y', 43) def _reduce_22(val, _values, result) @grammar.add_percent_code(id: val[1], code: val[2]) result end .,., module_eval(<<'.,.,', 'parser.y', 47) def _reduce_23(val, _values, result) @grammar.initial_action = Grammar::Code::InitialActionCode.new(type: :initial_action, token_code: val[1]) result end .,., module_eval(<<'.,.,', 'parser.y', 49) def _reduce_24(val, _values, result) @grammar.no_stdlib = true result end .,., module_eval(<<'.,.,', 'parser.y', 50) def _reduce_25(val, _values, result) @grammar.locations = true result end .,., # reduce 26 omitted module_eval(<<'.,.,', 'parser.y', 109) def _reduce_27(val, _values, result) result = val result end .,., module_eval(<<'.,.,', 'parser.y', 109) def _reduce_28(val, _values, result) result = val result end .,., module_eval(<<'.,.,', 'parser.y', 109) def _reduce_29(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., module_eval(<<'.,.,', 'parser.y', 109) def _reduce_30(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., module_eval(<<'.,.,', 'parser.y', 55) def _reduce_31(val, _values, result) @grammar.set_union( Grammar::Code::NoReferenceCode.new(type: :union, token_code: val[1]), val[1].line ) result end .,., # reduce 32 omitted # reduce 33 omitted # reduce 34 omitted module_eval(<<'.,.,', 'parser.y', 65) def _reduce_35(val, _values, result) @grammar.add_destructor( ident_or_tags: val[2].flatten, token_code: val[1], lineno: val[1].line ) result end .,., module_eval(<<'.,.,', 'parser.y', 73) def _reduce_36(val, _values, result) @grammar.add_printer( ident_or_tags: val[2].flatten, token_code: val[1], lineno: val[1].line ) result end .,., module_eval(<<'.,.,', 'parser.y', 81) def _reduce_37(val, _values, result) @grammar.add_error_token( ident_or_tags: val[2].flatten, token_code: val[1], lineno: val[1].line ) result end .,., module_eval(<<'.,.,', 'parser.y', 89) def _reduce_38(val, _values, result) @grammar.after_shift = val[1] result end .,., module_eval(<<'.,.,', 'parser.y', 93) def _reduce_39(val, _values, result) @grammar.before_reduce = val[1] result end .,., module_eval(<<'.,.,', 'parser.y', 97) def _reduce_40(val, _values, result) @grammar.after_reduce = val[1] result end .,., module_eval(<<'.,.,', 'parser.y', 101) def _reduce_41(val, _values, result) @grammar.after_shift_error_token = val[1] result end .,., module_eval(<<'.,.,', 'parser.y', 105) def _reduce_42(val, _values, result) @grammar.after_pop_stack = val[1] result end .,., # reduce 43 omitted module_eval(<<'.,.,', 'parser.y', 111) def _reduce_44(val, _values, result) val[1].each {|hash| hash[:tokens].each {|id| @grammar.add_type(id: id, tag: hash[:tag]) } } result end .,., module_eval(<<'.,.,', 'parser.y', 119) def _reduce_45(val, _values, result) val[1].each {|hash| hash[:tokens].each {|id| if @grammar.find_term_by_s_value(id.s_value) on_action_error("symbol #{id.s_value} redeclared as a nonterminal", id) else @grammar.add_type(id: id, tag: hash[:tag]) end } } result end .,., module_eval(<<'.,.,', 'parser.y', 131) def _reduce_46(val, _values, result) val[1].each {|hash| hash[:tokens].each {|id| sym = @grammar.add_term(id: id) @grammar.add_left(sym, @precedence_number) } } @precedence_number += 1 result end .,., module_eval(<<'.,.,', 'parser.y', 141) def _reduce_47(val, _values, result) val[1].each {|hash| hash[:tokens].each {|id| sym = @grammar.add_term(id: id) @grammar.add_right(sym, @precedence_number) } } @precedence_number += 1 result end .,., module_eval(<<'.,.,', 'parser.y', 151) def _reduce_48(val, _values, result) val[1].each {|hash| hash[:tokens].each {|id| sym = @grammar.add_term(id: id) @grammar.add_precedence(sym, @precedence_number) } } @precedence_number += 1 result end .,., module_eval(<<'.,.,', 'parser.y', 161) def _reduce_49(val, _values, result) val[1].each {|hash| hash[:tokens].each {|id| sym = @grammar.add_term(id: id) @grammar.add_nonassoc(sym, @precedence_number) } } @precedence_number += 1 result end .,., # reduce 50 omitted # reduce 51 omitted module_eval(<<'.,.,', 'parser.y', 184) def _reduce_52(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., module_eval(<<'.,.,', 'parser.y', 184) def _reduce_53(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., module_eval(<<'.,.,', 'parser.y', 172) def _reduce_54(val, _values, result) val[1].each {|token_declaration| @grammar.add_term(id: token_declaration[0], alias_name: token_declaration[2], token_id: token_declaration[1], tag: val[0], replace: true) } result end .,., module_eval(<<'.,.,', 'parser.y', 178) def _reduce_55(val, _values, result) val[2].each {|token_declaration| @grammar.add_term(id: token_declaration[0], alias_name: token_declaration[2], token_id: token_declaration[1], tag: val[1], replace: true) } result end .,., # reduce 56 omitted # reduce 57 omitted module_eval(<<'.,.,', 'parser.y', 183) def _reduce_58(val, _values, result) result = val result end .,., module_eval(<<'.,.,', 'parser.y', 187) def _reduce_59(val, _values, result) rule = Grammar::ParameterizingRule::Rule.new(val[1].s_value, val[3], val[7], tag: val[5]) @grammar.add_parameterizing_rule(rule) result end .,., module_eval(<<'.,.,', 'parser.y', 193) def _reduce_60(val, _values, result) rule = Grammar::ParameterizingRule::Rule.new(val[2].s_value, [], val[4], is_inline: true) @grammar.add_parameterizing_rule(rule) result end .,., module_eval(<<'.,.,', 'parser.y', 198) def _reduce_61(val, _values, result) rule = Grammar::ParameterizingRule::Rule.new(val[2].s_value, val[4], val[7], is_inline: true) @grammar.add_parameterizing_rule(rule) result end .,., module_eval(<<'.,.,', 'parser.y', 202) def _reduce_62(val, _values, result) result = [val[0]] result end .,., module_eval(<<'.,.,', 'parser.y', 203) def _reduce_63(val, _values, result) result = val[0].append(val[2]) result end .,., module_eval(<<'.,.,', 'parser.y', 207) def _reduce_64(val, _values, result) builder = val[0] result = [builder] result end .,., module_eval(<<'.,.,', 'parser.y', 212) def _reduce_65(val, _values, result) builder = val[2] result = val[0].append(builder) result end .,., # reduce 66 omitted # reduce 67 omitted # reduce 68 omitted # reduce 69 omitted module_eval(<<'.,.,', 'parser.y', 218) def _reduce_70(val, _values, result) reset_precs result = Grammar::ParameterizingRule::Rhs.new result end .,., module_eval(<<'.,.,', 'parser.y', 223) def _reduce_71(val, _values, result) token = val[1] token.alias_name = val[2] builder = val[0] builder.symbols << token result = builder result end .,., module_eval(<<'.,.,', 'parser.y', 231) def _reduce_72(val, _values, result) builder = val[0] builder.symbols << Lrama::Lexer::Token::InstantiateRule.new(s_value: val[2], location: @lexer.location, args: [val[1]]) result = builder result end .,., module_eval(<<'.,.,', 'parser.y', 237) def _reduce_73(val, _values, result) builder = val[0] builder.symbols << Lrama::Lexer::Token::InstantiateRule.new(s_value: val[1].s_value, location: @lexer.location, args: val[3], lhs_tag: val[5]) result = builder result end .,., module_eval(<<'.,.,', 'parser.y', 243) def _reduce_74(val, _values, result) user_code = val[1] user_code.alias_name = val[2] builder = val[0] builder.user_code = user_code result = builder result end .,., module_eval(<<'.,.,', 'parser.y', 251) def _reduce_75(val, _values, result) sym = @grammar.find_symbol_by_id!(val[2]) @prec_seen = true builder = val[0] builder.precedence_sym = sym result = builder result end .,., # reduce 76 omitted # reduce 77 omitted module_eval(<<'.,.,', 'parser.y', 258) def _reduce_78(val, _values, result) result = val[0].s_value if val[0] result end .,., module_eval(<<'.,.,', 'parser.y', 271) def _reduce_79(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., module_eval(<<'.,.,', 'parser.y', 271) def _reduce_80(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., module_eval(<<'.,.,', 'parser.y', 262) def _reduce_81(val, _values, result) result = if val[0] [{tag: val[0], tokens: val[1]}] else [{tag: nil, tokens: val[1]}] end result end .,., module_eval(<<'.,.,', 'parser.y', 268) def _reduce_82(val, _values, result) result = val[0].append({tag: val[1], tokens: val[2]}) result end .,., # reduce 83 omitted # reduce 84 omitted module_eval(<<'.,.,', 'parser.y', 274) def _reduce_85(val, _values, result) begin_c_declaration("}") result end .,., module_eval(<<'.,.,', 'parser.y', 278) def _reduce_86(val, _values, result) end_c_declaration result end .,., module_eval(<<'.,.,', 'parser.y', 282) def _reduce_87(val, _values, result) result = val[2] result end .,., module_eval(<<'.,.,', 'parser.y', 290) def _reduce_88(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., module_eval(<<'.,.,', 'parser.y', 290) def _reduce_89(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., module_eval(<<'.,.,', 'parser.y', 285) def _reduce_90(val, _values, result) result = [{tag: nil, tokens: val[0]}] result end .,., module_eval(<<'.,.,', 'parser.y', 286) def _reduce_91(val, _values, result) result = [{tag: val[0], tokens: val[1]}] result end .,., module_eval(<<'.,.,', 'parser.y', 287) def _reduce_92(val, _values, result) result = val[0].append({tag: val[1], tokens: val[2]}) result end .,., module_eval(<<'.,.,', 'parser.y', 289) def _reduce_93(val, _values, result) on_action_error("ident after %prec", val[0]) if @prec_seen result end .,., module_eval(<<'.,.,', 'parser.y', 290) def _reduce_94(val, _values, result) on_action_error("char after %prec", val[0]) if @prec_seen result end .,., # reduce 95 omitted # reduce 96 omitted # reduce 97 omitted # reduce 98 omitted module_eval(<<'.,.,', 'parser.y', 298) def _reduce_99(val, _values, result) lhs = val[0] lhs.alias_name = val[1] val[3].each do |builder| builder.lhs = lhs builder.complete_input @grammar.add_rule_builder(builder) end result end .,., module_eval(<<'.,.,', 'parser.y', 309) def _reduce_100(val, _values, result) builder = val[0] if !builder.line builder.line = @lexer.line - 1 end result = [builder] result end .,., module_eval(<<'.,.,', 'parser.y', 317) def _reduce_101(val, _values, result) builder = val[2] if !builder.line builder.line = @lexer.line - 1 end result = val[0].append(builder) result end .,., module_eval(<<'.,.,', 'parser.y', 326) def _reduce_102(val, _values, result) reset_precs result = @grammar.create_rule_builder(@rule_counter, @midrule_action_counter) result end .,., module_eval(<<'.,.,', 'parser.y', 331) def _reduce_103(val, _values, result) token = val[1] token.alias_name = val[2] builder = val[0] builder.add_rhs(token) result = builder result end .,., module_eval(<<'.,.,', 'parser.y', 339) def _reduce_104(val, _values, result) token = Lrama::Lexer::Token::InstantiateRule.new(s_value: val[2], alias_name: val[3], location: @lexer.location, args: [val[1]], lhs_tag: val[4]) builder = val[0] builder.add_rhs(token) builder.line = val[1].first_line result = builder result end .,., module_eval(<<'.,.,', 'parser.y', 347) def _reduce_105(val, _values, result) token = Lrama::Lexer::Token::InstantiateRule.new(s_value: val[1].s_value, alias_name: val[5], location: @lexer.location, args: val[3], lhs_tag: val[6]) builder = val[0] builder.add_rhs(token) builder.line = val[1].first_line result = builder result end .,., module_eval(<<'.,.,', 'parser.y', 355) def _reduce_106(val, _values, result) user_code = val[1] user_code.alias_name = val[2] user_code.tag = val[3] builder = val[0] builder.user_code = user_code result = builder result end .,., module_eval(<<'.,.,', 'parser.y', 364) def _reduce_107(val, _values, result) sym = @grammar.find_symbol_by_id!(val[2]) @prec_seen = true builder = val[0] builder.precedence_sym = sym result = builder result end .,., module_eval(<<'.,.,', 'parser.y', 371) def _reduce_108(val, _values, result) result = "option" result end .,., module_eval(<<'.,.,', 'parser.y', 372) def _reduce_109(val, _values, result) result = "nonempty_list" result end .,., module_eval(<<'.,.,', 'parser.y', 373) def _reduce_110(val, _values, result) result = "list" result end .,., # reduce 111 omitted # reduce 112 omitted module_eval(<<'.,.,', 'parser.y', 377) def _reduce_113(val, _values, result) result = if val[1] [Lrama::Lexer::Token::InstantiateRule.new(s_value: val[1].s_value, location: @lexer.location, args: val[0])] else [val[0]] end result end .,., module_eval(<<'.,.,', 'parser.y', 383) def _reduce_114(val, _values, result) result = val[0].append(val[2]) result end .,., module_eval(<<'.,.,', 'parser.y', 384) def _reduce_115(val, _values, result) result = [Lrama::Lexer::Token::InstantiateRule.new(s_value: val[0].s_value, location: @lexer.location, args: val[2])] result end .,., module_eval(<<'.,.,', 'parser.y', 388) def _reduce_116(val, _values, result) if @prec_seen on_action_error("multiple User_code after %prec", val[0]) if @code_after_prec @code_after_prec = true end begin_c_declaration("}") result end .,., module_eval(<<'.,.,', 'parser.y', 396) def _reduce_117(val, _values, result) end_c_declaration result end .,., module_eval(<<'.,.,', 'parser.y', 400) def _reduce_118(val, _values, result) result = val[2] result end .,., module_eval(<<'.,.,', 'parser.y', 403) def _reduce_119(val, _values, result) result = val[1].s_value result end .,., module_eval(<<'.,.,', 'parser.y', 407) def _reduce_120(val, _values, result) begin_c_declaration('\Z') @grammar.epilogue_first_lineno = @lexer.line + 1 result end .,., module_eval(<<'.,.,', 'parser.y', 412) def _reduce_121(val, _values, result) end_c_declaration @grammar.epilogue = val[2].s_value result end .,., # reduce 122 omitted # reduce 123 omitted # reduce 124 omitted # reduce 125 omitted # reduce 126 omitted module_eval(<<'.,.,', 'parser.y', 423) def _reduce_127(val, _values, result) result = Lrama::Lexer::Token::Ident.new(s_value: val[0]) result end .,., def _reduce_none(val, _values, result) val[0] end end # class Parser end # module Lrama nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/PaxHeaders/state0000644000000000000000000000013215077107334023027 xustar0030 mtime=1761382108.297302702 30 atime=1761382109.799298361 30 ctime=1761382108.297302702 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/state/0000755000175100017510000000000015077107334023474 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/state/PaxHeaders/resolved_conflict.rb0000644000000000000000000000013115077107276027141 xustar0030 mtime=1761382078.144420456 30 atime=1761382080.167411201 29 ctime=1761382108.29130272 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/state/resolved_conflict.rb0000644000175100017510000000206215077107276027532 0ustar00runnerrunner# frozen_string_literal: true module Lrama class State # * symbol: A symbol under discussion # * reduce: A reduce under discussion # * which: For which a conflict is resolved. :shift, :reduce or :error (for nonassociative) class ResolvedConflict < Struct.new(:symbol, :reduce, :which, :same_prec, keyword_init: true) def report_message s = symbol.display_name r = reduce.rule.precedence_sym&.display_name case when which == :shift && same_prec msg = "resolved as #{which} (%right #{s})" when which == :shift msg = "resolved as #{which} (#{r} < #{s})" when which == :reduce && same_prec msg = "resolved as #{which} (%left #{s})" when which == :reduce msg = "resolved as #{which} (#{s} < #{r})" when which == :error msg = "resolved as an #{which} (%nonassoc #{s})" else raise "Unknown direction. #{self}" end "Conflict between rule #{reduce.rule.id} and token #{s} #{msg}." end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/state/PaxHeaders/shift.rb0000644000000000000000000000013215077107276024553 xustar0030 mtime=1761382078.144420456 30 atime=1761382080.167411201 30 ctime=1761382108.296302705 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/state/shift.rb0000644000175100017510000000043315077107276025143 0ustar00runnerrunner# frozen_string_literal: true module Lrama class State class Shift attr_reader :next_sym, :next_items attr_accessor :not_selected def initialize(next_sym, next_items) @next_sym = next_sym @next_items = next_items end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/state/PaxHeaders/reduce_reduce_conflict.rb0000644000000000000000000000013215077107276030115 xustar0030 mtime=1761382078.144420456 30 atime=1761382080.167411201 30 ctime=1761382108.297302702 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/state/reduce_reduce_conflict.rb0000644000175100017510000000033215077107276030503 0ustar00runnerrunner# frozen_string_literal: true module Lrama class State class ReduceReduceConflict < Struct.new(:symbols, :reduce1, :reduce2, keyword_init: true) def type :reduce_reduce end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/state/PaxHeaders/reduce.rb0000644000000000000000000000013215077107276024705 xustar0030 mtime=1761382078.144420456 30 atime=1761382080.167411201 30 ctime=1761382108.293302714 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/state/reduce.rb0000644000175100017510000000136715077107276025304 0ustar00runnerrunner# frozen_string_literal: true module Lrama class State class Reduce # https://www.gnu.org/software/bison/manual/html_node/Default-Reductions.html attr_reader :item, :look_ahead, :not_selected_symbols attr_accessor :default_reduction def initialize(item) @item = item @look_ahead = nil @not_selected_symbols = [] end def rule @item.rule end def look_ahead=(look_ahead) @look_ahead = look_ahead.freeze end def add_not_selected_symbol(sym) @not_selected_symbols << sym end def selected_look_ahead if look_ahead look_ahead - @not_selected_symbols else [] end end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/state/PaxHeaders/shift_reduce_conflict.rb0000644000000000000000000000013215077107276027763 xustar0030 mtime=1761382078.144420456 30 atime=1761382080.167411201 30 ctime=1761382108.294302711 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama/state/shift_reduce_conflict.rb0000644000175100017510000000032515077107276030353 0ustar00runnerrunner# frozen_string_literal: true module Lrama class State class ShiftReduceConflict < Struct.new(:symbols, :shift, :reduce, keyword_init: true) def type :shift_reduce end end end end nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/PaxHeaders/lrama.rb0000644000000000000000000000013115077107276022315 xustar0030 mtime=1761382078.141420469 29 atime=1761382080.16541121 30 ctime=1761382108.333302598 nghttp2-1.68.0/third-party/mruby/tools/lrama/lib/lrama.rb0000644000175100017510000000132415077107276022706 0ustar00runnerrunner# frozen_string_literal: true require_relative "lrama/bitmap" require_relative "lrama/command" require_relative "lrama/context" require_relative "lrama/counterexamples" require_relative "lrama/diagnostics" require_relative "lrama/digraph" require_relative "lrama/grammar" require_relative "lrama/grammar_validator" require_relative "lrama/lexer" require_relative "lrama/logger" require_relative "lrama/option_parser" require_relative "lrama/options" require_relative "lrama/output" require_relative "lrama/parser" require_relative "lrama/report" require_relative "lrama/state" require_relative "lrama/states" require_relative "lrama/states_reporter" require_relative "lrama/trace_reporter" require_relative "lrama/version" nghttp2-1.68.0/third-party/mruby/tools/lrama/PaxHeaders/LEGAL.md0000644000000000000000000000013215077107276021275 xustar0030 mtime=1761382078.141420469 30 atime=1761382080.164411215 30 ctime=1761382108.231302893 nghttp2-1.68.0/third-party/mruby/tools/lrama/LEGAL.md0000644000175100017510000000056115077107276021667 0ustar00runnerrunner# LEGAL NOTICE INFORMATION All the files in this distribution are covered under the MIT License except some files mentioned below. ## GNU General Public License version 3 These files are licensed under the GNU General Public License version 3 or later. See these files for more information. - template/bison/\_yacc.h - template/bison/yacc.c - template/bison/yacc.h nghttp2-1.68.0/third-party/mruby/tools/lrama/PaxHeaders/exe0000644000000000000000000000013015077107334020624 xustar0029 mtime=1761382108.23230289 30 atime=1761382109.799298361 29 ctime=1761382108.23230289 nghttp2-1.68.0/third-party/mruby/tools/lrama/exe/0000755000175100017510000000000015077107334021273 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/tools/lrama/exe/PaxHeaders/lrama0000644000000000000000000000013015077107276021725 xustar0030 mtime=1761382078.141420469 29 atime=1761382080.16541121 29 ctime=1761382108.23230289 nghttp2-1.68.0/third-party/mruby/tools/lrama/exe/lrama0000755000175100017510000000022015077107276022314 0ustar00runnerrunner#!/usr/bin/env ruby # frozen_string_literal: true $LOAD_PATH << File.join(__dir__, "../lib") require "lrama" Lrama::Command.new.run(ARGV.dup) nghttp2-1.68.0/third-party/mruby/tools/lrama/PaxHeaders/MIT0000644000000000000000000000013215077107276020503 xustar0030 mtime=1761382078.141420469 30 atime=1761382080.164411215 30 ctime=1761382108.340302578 nghttp2-1.68.0/third-party/mruby/tools/lrama/MIT0000644000175100017510000000207215077107276021074 0ustar00runnerrunnerThe MIT License (MIT) Copyright (c) 2023 Yuichiro Kaneko Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. nghttp2-1.68.0/third-party/mruby/PaxHeaders/Gemfile.lock0000644000000000000000000000013015077107276020113 xustar0029 mtime=1761382078.09542068 29 atime=1761382080.11741143 30 ctime=1761382108.343302569 nghttp2-1.68.0/third-party/mruby/Gemfile.lock0000644000175100017510000000051015077107276020501 0ustar00runnerrunnerGEM remote: https://rubygems.org/ specs: coderay (1.1.3) rake (13.2.1) yard (0.9.37) yard-coderay (0.1.0) coderay yard yard-mruby (0.3.0) yard (~> 0.9.0) PLATFORMS ruby x86_64-darwin-21 x86_64-linux DEPENDENCIES rake yard yard-coderay yard-mruby BUNDLED WITH 2.4.10 nghttp2-1.68.0/third-party/mruby/PaxHeaders/docker-compose.yml0000644000000000000000000000013215077107276021330 xustar0030 mtime=1761382078.099420661 30 atime=1761382080.121411411 30 ctime=1761382108.227302905 nghttp2-1.68.0/third-party/mruby/docker-compose.yml0000644000175100017510000000044115077107276021717 0ustar00runnerrunnerversion: "3.8" services: test: build: context: . command: sh -c 'rake deep_clean && rake -m test:run:serial' environment: - MRUBY_CONFIG=ci/gcc-clang - CC=gcc - CXX=g++ - LD=gcc - SKIP=check-executables-have-shebangs working_dir: /app nghttp2-1.68.0/third-party/mruby/PaxHeaders/AUTHORS0000644000000000000000000000013215077107276016743 xustar0030 mtime=1761382078.094420684 30 atime=1761382080.116411435 30 ctime=1761382108.226302908 nghttp2-1.68.0/third-party/mruby/AUTHORS0000644000175100017510000002635715077107276017350 0ustar00runnerrunner# Authors of mruby (mruby developers) ## The List of Contributors sorted by number of commits (as of 2025-05-01 30d1db4) 6190 Yukihiro "Matz" Matsumoto (@matz)* 678 dearblue (@dearblue)* 587 KOBAYASHI Shuji (@shuujii) 353 Daniel Bovensiepen (@bovi)* 345 Takeshi Watanabe (@take-cheeze)* 333 Masaki Muranaka (@monaka) 234 Jun Hiroe (@suzukaze) 228 Tomoyuki Sahara (@tsahara)* 227 John Bampton (@jbampton) 220 Cremno (@cremno)* 209 Yuki Kurihara (@ksss)+ 144 Yasuhiro Matsumoto (@mattn)* 113 Carson McDonald (@carsonmcdonald) 104 Tomasz PÄ™draszewski (@dabroz)* 83 Akira Yumiyama (@akiray03)* 83 skandhas (@skandhas) 80 Masamitsu MURASE (@masamitsu-murase) 73 Hiroshi Mimaki (@mimaki)* 71 Tatsuhiko Kubo (@cubicdaiya)* 71 Yuichiro MASUI (@masuidrive) 62 Yuichiro Kaneko (@yui-knk)+ 59 Kurebayashi, Takahiro (@crimsonwoods)* 56 h2so5 (@h2so5) 52 Ralph Desir (@Mav7)* 48 Paolo Bosetti (@pbosetti)* 45 Rory O'Connell (@RoryO)* 42 fleuria (@flaneur2020) 40 Christopher Aue (@christopheraue) 40 Seba Gamboa (@sagmor) 39 Kouhei Sutou (@kou)* 38 Koji Yoshioka (@kyab)*+ 32 Masayoshi Takahashi (@takahashim)+ 31 MATSUMOTO Ryosuke (@matsumotory)* 30 Nobuyoshi Nakada (@nobu) 26 Hoshiumi Arata (@hoshiumiarata)* 25 Julian Aron Prenner (@furunkel)* 22 Clayton Smith (@clayton-shopify) 22 Uchio Kondo (@udzura)* 22 Zachary Scott (@zzak)* 21 Ryan Lopopolo (@lopopolo) 20 Ryan Scott (@ryan-scott-dev)* 19 Bouke van der Bijl (@bouk) 19 Jared Breeden (@jbreeden)* 19 go kikuta (@gkta)* 18 Corey Powell (@IceDragon200) 18 Hidetaka Takano (@TJ-Hidetaka-Takano) 18 Jon Maken (@jonforums)+ 18 mirichi (@mirichi) 17 Mitchell Blank Jr (@mitchblank)* 16 bggd (@bggd) 16 kano4 (@kano4) 15 Felix Jones (@felixjones)* 14 Blaž Hrastnik (@archseer)* 14 Kazuki Tsujimoto (@k-tsj) 14 Tadashi FUKUZAWA (@FUKUZAWA-Tadashi)+ 14 fn ⌃ ⌥ (@FnControlOption) 14 leviongit (@leviongit) 13 Jose Narvaez (@goyox86) 13 Patrick Hogan (@pbhogan) 12 Akira Kuroda (@akuroda) 12 Kouki Ooyatsu (kaishuu0123)* 12 NAKAMURA Usaku (@unak)* 12 Ray Chason (@chasonr)* 12 Takashi Sawanaka (@sdottaka)* 12 Ukrainskiy Sergey (@ukrainskiysergey) 12 Xuejie "Rafael" Xiao (@xxuejie)* 11 Julien Ammous (@schmurfy) 11 Kazuho Oku (@kazuho) 11 RIZAL Reckordp (@Reckordp)+ 11 Seeker (@SeekingMeaning) 11 takkaw (@takkaw) 10 Miura Hideki (@miura1729) 10 Narihiro Nakamura (@authorNari) 10 YAMAMOTO Masaya (pandax381) 10 Yuichi Nishiwaki (@nyuichi) 9 Akira Mitsui (@murasesyuka)* 9 Frank Celler (@fceller) 9 Tatsuya Matsumoto (@tmash06)* 8 Takashi Sogabe (@sogabe) 8 Wataru Ashihara (@wataash)* 7 Bhargava Shastry (@bshastry)* 7 Kouichi Nakanishi (@keizo042) 7 Rubyist (@expeditiousRubyist) 7 SiZiOUS (@sizious) 7 Simon Génier (@simon-shopify) 7 Terence Lee (@hone) 7 roco (@rystyle)* 6 Akito Mochizuki (@ak-mochi) 6 Beoran (@beoran) 6 David Siaw (@davidsiaw)* 6 Frederick John Milens III (@fjmilens3) 6 Hiro Asari (@BanzaiMan) 6 INOUE Yasuyuki (@yasuyuki) 6 Junji Sawada (@junjis0203) 6 Kenji Okimoto (@okkez)+ 6 Selman ULUG (@selman) 6 Yusuke Endoh (@mame)* 6 masahino (@masahino) 5 Chris Reuter (@suetanvil) 5 Davide D'Agostino (@DAddYE) 5 Eric Hodel (@drbrain) 5 HASUMI Hitoshi (@hasumikin) 5 Hendrik (@Asmod4n) 5 Ichito Nagata (@i110) 5 Keita Obo (@ktaobo)* 5 Max Anselm (@silverhammermba) 5 Rodrigo Malizia (@rmalizia44)+ 5 Syohei YOSHIDA (@syohex) 5 TOMITA Masahiro (@tmtm) 5 Yurie Yamane (@yurie)+ 5 dreamedge (@dreamedge) 5 nkshigeru (@nkshigeru) 5 vickash (@vickash) 5 xuejianqing (@joans321) 4 Dante Catalfamo (@dantecatalfamo) 4 Goro Kikuchi (@gorogit) 4 Herwin Weststrate (@herwinw) 4 Horimoto Yasuhiro (@komainu8) 4 Jon Moss (@maclover7) 4 Ken Muramatsu (@ken-mu)+ 4 Kohei Suzuki (@eagletmt) 4 Lanza (@LanzaSchneider) 4 Li Yazhou (@flaneur2020) 4 Marcus Stollsteimer (@stomar) 4 Mark Delk (@jethrodaniel) 4 NARUSE, Yui (@nurse) 4 Ravil Bayramgalin (@brainopia)*+ 4 Satoshi Odawara (@SatoshiOdawara) 4 Yuhei Okazaki (@Yuuhei-Okazaki)* 4 Yuji Yamano (@yyamano) 4 kurodash (@kurodash)* 4 wanabe (@wanabe)* 3 Anton Davydov (@davydovanton) 3 Aurora Nockert (@auroranockert) 3 Carlo Prelz (@asfluido)* 3 Daniel K. SierpiÅ„ski (@513ry)+ 3 David Turnbull (@AE9RB) 3 Franck Verrot (@franckverrot) 3 Hirohito Higashi (@HirohitoHigashi) 3 J. Mutua (@katmutua)+ 3 Jan Berdajs (@mrbrdo) 3 Jonas Minnberg (@sasq64) 3 Joseph McCullough (@joequery) 3 Mark McCurry (@fundamental) 3 Nobuhiro Iwamatsu (@iwamatsu) 3 Per Lundberg (@perlun)* 3 Rob Fors (@robfors)* 3 Robert Rowe (@CaptainJet) 3 Sebastián Katzer (@katzer)* 3 Shuta Kimura (@kimushu)+ 3 TERAJIMA, Motoyuki (@trmmy) 3 Taichi AOKI (@aoki1980taichi) 3 Takashi Kokubun (@k0kubun) 3 Tatsuhiro Tsujikawa (@tatsuhiro-t) 3 Thiago Scalone (@scalone) 3 Vladimir Dementyev (@palkan)* 3 William Light (@wrl) 3 bamchoh (@bamchoh) 3 sasaki takeru (@takeru) 3 windwiny (@windwiny) 2 Akira Moroo (@retrage) 2 Artur K (@nemerle) 2 Christian Mauceri (@mauceri) 2 Craig Lehmann (@craiglrock)* 2 Dominic Sisneros (@dsisnero)* 2 Dusan D. Majkic (@dmajkic) 2 Emiliano Lesende (@3miliano) 2 Francois Chagnon (@EiNSTeiN-)* 2 Gilad Zohari (@gzohari) 2 Go Saito (@govm) 2 Hiroyuki Iwatsuki (@iwadon) 2 Huei-Horng Yo (@hiroshiyui) 2 Jonas Kulla (@Ancurio) 2 Jun Takeda (@takjn) 2 Kazuaki Tanaka (@kaz0505) 2 Kazuhiko Yamashita (@pyama86)+ 2 Kazuhiro Sera (@seratch) 2 Kuroda Daisuke (@dycoon)+ 2 Lothar Scholz (@llothar) 2 Lukas Joeressen (@kext) 2 Masahiro Wakame (@vvkame)+ 2 Minao Yamamoto (@tarosay)+ 2 Nihad Abbasov (@NARKOZ) 2 Robert Mosolgo (@rmosolgo) 2 Russel Hunter Yukawa (@rhykw)+ 2 Ryunosuke SATO (@tricknotes) 2 Santa Zhang (@santazhang) 2 Serg Podtynnyi (@shtirlic) 2 Shannen Saez (@shancat) 2 Shouji Kuboyama (@Shokuji)* 2 SouthWolf (@southwolf) 2 TJ Singleton (@tjsingleton) 2 Taiyo Mizuhashi (@taiyoslime)+ 2 Tomás Pollak (@tomas)* 2 Yutaka HARA (@yhara)*+ 2 Zhang Xiaohui (@hifoolno) 2 buty4649 (@buty4649) 2 icm7216 (@icm7216) 1 A-Sat (@asatou)+ 1 Abinoam Praxedes Marques Junior (@abinoam) 1 Alex Wang (@nanamiwang)+ 1 AlexDenisov (@AlexDenisov) 1 Andrew Nordman (@cadwallion) 1 Ashish Kurmi (@boahc077) 1 Atsushi Morimoto (@mynz) 1 Ben A Morgan (@BenMorganIO) 1 Benoit Daloze (@eregon) 1 Bradley Whited (@esotericpig) 1 Colin MacKenzie IV (@sinisterchipmunk) 1 Daehyub Kim (@lateau) 1 Daniel Varga (@vargad) 1 Diamond Rivero (@diamant3) 1 Edgar Boda-Majer (@eboda) 1 Fangrui Song (@MaskRay) 1 Flavio Medeiros (@flaviommedeiros) 1 Francis Bogsanyi (@fbogsany) 1 Guo Xiao (@guoxiao) 1 Gwen Boatrite (@boatrit) 1 Gwendolyn Boatrite (@boatrite) 1 HARADA Makoto (@haramako) 1 HAYASHI Kentaro (@kenhys) 1 Hiroki Mori (@yamori813)+ 1 Hiromasa Ishii (@Hir0)+ 1 Hiroyuki Matsuzaki (@Hiroyuki-Matsuzaki) 1 Hugo Logmans (@hlogmans) 1 Jack Danger Canty (@JackDanger) 1 Jeff Federman (@jefffederman) 1 Jeffrey Crowell (@crowell) 1 Jeremy Ong (@jeremyong) 1 Jiro Nishiguchi (@spiritloose) 1 Joachim Baran (@indiedotkim) 1 Joe Kutner (@jkutner) 1 Jun Aruga (@junaruga) 1 Junichi Kajiwara (@kjunichi) 1 Jurriaan Pruis (@jurriaan) 1 Katsuhiko Kageyama (@kishima)+ 1 Katsuyoshi Ito (@katsuyoshi) 1 Kazuhiro NISHIYAMA (@znz) 1 Kei Sawada (@remore) 1 Kim H Madsen (@kimhmadsen) 1 Koichi ITO (@koic) 1 Konstantin Haase (@rkh) 1 Leo Neat (@Leo-Neat) 1 Lian Cheng (@liancheng) 1 Luis Lavena (@luislavena) 1 Lukas Elmer (@lukaselmer) 1 Lukas Stabe (@Ahti) 1 M.Naruoka (@fenrir-naru) 1 Marcelo Juchem (@juchem) 1 Martin Bosslet (@emboss)+ 1 Masahiko Sawada (@MasahikoSawada) 1 Matt Aimonetti (@mattetti) 1 Max Base (@MaxFork) 1 Maxim Abramchuk (@MaximAbramchuck) 1 Megumi Tomita (@tomykaira)+ 1 Mitchell Hashimoto (@mitchellh) 1 Mitsutaka Mimura (@takkanm) 1 Nathan Ladd (@ntl) 1 Nicholas (@knf) 1 Nozomi SATO (@nozomiS) 1 Okumura Takahiro (@hfm) 1 Patrick Ellis (@pje) 1 Patrick Pokatilo (@SHyx0rmZ) 1 Pavel Evstigneev (@Paxa)+ 1 Pete Kinnecom (@petekinnecom) 1 Prayag Verma (@pra85) 1 Ranmocy (@ranmocy) 1 Robert McNally (@wolfmcnally) 1 Ryan Scott Lewis (@RyanScottLewis) 1 Ryo Okubo (@syucream) 1 SAkira a.k.a. Akira Suzuki (@sakisakira) 1 Santiago Rodriguez (@sanrodari) 1 Satoh, Hiroh (@cho45)+ 1 Satoru Naba (@snaba)+ 1 Sayed Abdelhaleem (@visualsayed) 1 Sergey Ukrainskiy (@ukrainskiysergey) 1 Shugo Maeda (@shugo) 1 Sinkevich Artem (@ArtSin) 1 Sorah Fukumori (@sorah) 1 Stephen Jothen (@sjothen) 1 Stuart Hinson (@stuarth) 1 Takuma Kume (@takumakume)+ 1 Takuya ASADA (@syuu1228) 1 Thomas Schmidt (@digitaltom) 1 Timo Schilling (@timoschilling) 1 Tom Black (@blacktm) 1 Utkarsh Kukreti (@utkarshkukreti) 1 W (@graywolf) 1 Wuffers Lightwolf (@w-x-l) 1 YAMAMOTO Yuji (@igrep) 1 Yevhen Viktorov (@yevgenko) 1 Yoji SHIDARA (@darashi) 1 Yoshiori SHOJI (@yoshiori) 1 Yuji Yokoo (@yujiyokoo) 1 Yukang (@chenyukang) 1 Yurii Nakonechnyi (@inobelar) 1 Yusuke Suzuki (@Constellation)+ 1 Yusuke Tanaka (@csouls) 1 alpha.netzilla (@alpha-netzilla) 1 arton (@arton) 1 duangsuse (@duangsuse) 1 fl0l0u (@fl0l0u) 1 hhc0null (@hhc0null) 1 iTitou (@titouanc) 1 javier ramírez (@javier) 1 liyuray (@liyuray) 1 lucas dicioccio (@lucasdicioccio) 1 n4o847 (@n4o847) 1 niyarin (@niyarin) 1 robert (@R-obert) 1 sbsoftware (@sbsoftware) 1 ssmallkirby (@smallkirby) 1 taku toyama (@tsuichu) `*` - Entries unified according to names and addresses `+` - Entries with names different from commits ## Contributors without named commits Yuichi Osawa (Mitsubishi Electric Micro-Computer Application Software) Shota Nakano (Manycolors) Bjorn De Meyer ## Corporate contributors Ministry of Economy, Trade and Industry, Japan Kyushu Bureau of Economy, Trade and Industry SCSK KYUSHU CORPORATION Kyushu Institute of Technology Network Applied Communication Laboratory, Inc. Internet Initiative Japan Inc. Specified non-profit organization mruby Forum Mitsubishi Electric Micro-Computer Application Software Co.,Ltd. Manycolors, Inc. nghttp2-1.68.0/third-party/mruby/PaxHeaders/mruby-source.gemspec0000644000000000000000000000013215077107276021674 xustar0030 mtime=1761382078.131420515 30 atime=1761382080.149411284 30 ctime=1761382108.598301832 nghttp2-1.68.0/third-party/mruby/mruby-source.gemspec0000644000175100017510000000111215077107276022257 0ustar00runnerrunnerlib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'mruby/source' Gem::Specification.new do |spec| spec.name = "mruby-source" spec.version = MRuby::Source::MRUBY_VERSION spec.authors = [ "mruby developers" ] spec.summary = %q{mruby source code wrapper.} spec.description = %q{mruby source code wrapper for use with Ruby libs.} spec.homepage = "https://mruby.org" spec.license = "MIT" spec.files = `git ls-files -z`.split("\x0") spec.require_paths = ["lib"] end nghttp2-1.68.0/third-party/mruby/PaxHeaders/Rakefile0000644000000000000000000000013115077107276017337 xustar0029 mtime=1761382078.09542068 30 atime=1761382080.118411425 30 ctime=1761382108.942300838 nghttp2-1.68.0/third-party/mruby/Rakefile0000644000175100017510000000453015077107276017732 0ustar00runnerrunner# Build description. # basic build file for mruby MRUBY_ROOT = File.dirname(File.expand_path(__FILE__)) MRUBY_BUILD_HOST_IS_CYGWIN = RUBY_PLATFORM.include?('cygwin') MRUBY_BUILD_HOST_IS_OPENBSD = RUBY_PLATFORM.include?('openbsd') Rake.verbose(false) if Rake.verbose == Rake::DSL::DEFAULT $LOAD_PATH << File.join(MRUBY_ROOT, "lib") # load build systems require "mruby/core_ext" require "mruby/build" # load configuration file MRUBY_CONFIG = MRuby::Build.mruby_config_path load MRUBY_CONFIG # load basic rules MRuby.each_target do |build| build.define_rules end # load custom rules load "#{MRUBY_ROOT}/tasks/core.rake" load "#{MRUBY_ROOT}/tasks/mrblib.rake" load "#{MRUBY_ROOT}/tasks/mrbgems.rake" load "#{MRUBY_ROOT}/tasks/libmruby.rake" load "#{MRUBY_ROOT}/tasks/bin.rake" load "#{MRUBY_ROOT}/tasks/presym.rake" load "#{MRUBY_ROOT}/tasks/test.rake" load "#{MRUBY_ROOT}/tasks/benchmark.rake" load "#{MRUBY_ROOT}/tasks/doc.rake" load "#{MRUBY_ROOT}/tasks/install.rake" ############################## # generic build targets, rules task :default => :all desc "build all targets, install (locally) in-repo" task :all => :gensym do Rake::Task[:build].invoke puts puts "Build summary:" puts MRuby.each_target do |build| build.print_build_summary end MRuby::Lockfile.write end task :build => MRuby.targets.flat_map{|_, build| build.products} desc "clean all built and in-repo installed artifacts" task :clean do MRuby.each_target do |build| rm_rf build.build_dir rm_f build.products end puts "Cleaned up target build directory" end desc "clean everything!" task :deep_clean => %w[clean doc:clean] do MRuby.each_target do |build| rm_rf build.gem_clone_dir end rm_rf "#{MRUBY_ROOT}/bin" rm_rf "#{MRUBY_ROOT}/build" puts "Cleaned up mrbgems build directory" end desc "run all pre-commit hooks against all files" task :check do sh "pre-commit run --all-files" end desc "install the pre-commit hooks" task :checkinstall do sh "pre-commit install" end desc "check the pre-commit hooks for updates" task :checkupdate do sh "pre-commit autoupdate" end desc "run all pre-commit hooks against all files with docker-compose" task :composecheck do sh "docker-compose -p mruby run test pre-commit run --all-files" end desc "build and run all mruby tests with docker-compose" task :composetest do sh "docker-compose -p mruby run test" end nghttp2-1.68.0/third-party/mruby/PaxHeaders/minirake0000644000000000000000000000013215077107276017415 xustar0030 mtime=1761382078.103420643 30 atime=1761382080.125411393 30 ctime=1761382108.968300763 nghttp2-1.68.0/third-party/mruby/minirake0000755000175100017510000000004715077107276020011 0ustar00runnerrunner#!/usr/bin/env ruby exec "rake", *ARGV nghttp2-1.68.0/third-party/mruby/PaxHeaders/test0000644000000000000000000000013215077107334016570 xustar0030 mtime=1761382108.535302014 30 atime=1761382109.799298361 30 ctime=1761382108.535302014 nghttp2-1.68.0/third-party/mruby/test/0000755000175100017510000000000015077107334017235 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/test/PaxHeaders/t0000644000000000000000000000013015077107334017031 xustar0029 mtime=1761382108.53330202 30 atime=1761382109.799298361 29 ctime=1761382108.53330202 nghttp2-1.68.0/third-party/mruby/test/t/0000755000175100017510000000000015077107334017500 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/literals.rb0000644000000000000000000000013115077107276021260 xustar0030 mtime=1761382078.140420474 29 atime=1761382080.16341122 30 ctime=1761382108.520302058 nghttp2-1.68.0/third-party/mruby/test/t/literals.rb0000644000175100017510000001515115077107276021654 0ustar00runnerrunner## # Literals ISO Test assert('Literals Numerical', '8.7.6.2') do # signed and unsigned integer assert_equal 1, 1 assert_equal(-1, -1) assert_equal(+1, +1) # signed and unsigned float assert_equal 1.0, 1.0 assert_equal(-1.0, -1.0) # binary assert_equal 128, 0b10000000 assert_equal 128, 0B10000000 # octal assert_equal 8, 0o10 assert_equal 8, 0O10 assert_equal 8, 0_10 # hex assert_equal 255, 0xff assert_equal 255, 0Xff # decimal assert_equal 999, 0d999 assert_equal 999, 0D999 # decimal separator assert_equal 10000000, 10_000_000 assert_equal 10, 1_0 # integer with exponent assert_equal 10.0, 1e1 assert_equal(0.1, 1e-1) assert_equal 10.0, 1e+1 # float with exponent assert_equal 10.0, 1.0e1 assert_equal(0.1, 1.0e-1) assert_equal 10.0, 1.0e+1 end assert('Literals Strings Single Quoted', '8.7.6.3.2') do assert_equal 'abc', 'abc' assert_equal '\'', '\'' assert_equal '\\', '\\' end assert('Literals Strings Double Quoted', '8.7.6.3.3') do a = "abc" assert_equal "abc", "abc" assert_equal "\"", "\"" assert_equal "\\", "\\" assert_equal "abc", "#{a}" end assert('Literals Strings Quoted Non-Expanded', '8.7.6.3.4') do a = %q{abc} b = %q(abc) c = %q[abc] d = %q e = %q/abc/ f = %q/ab\/c/ g = %q{#{a}} assert_equal 'abc', a assert_equal 'abc', b assert_equal 'abc', c assert_equal 'abc', d assert_equal 'abc', e assert_equal 'ab/c', f assert_equal '#{a}', g end assert('Literals Strings Quoted Expanded', '8.7.6.3.5') do a = %Q{abc} b = %Q(abc) c = %Q[abc] d = %Q e = %Q/abc/ f = %Q/ab\/c/ g = %Q{#{a}} assert_equal 'abc', a assert_equal 'abc', b assert_equal 'abc', c assert_equal 'abc', d assert_equal 'abc', e assert_equal 'ab/c', f assert_equal 'abc', g end assert('Literals Strings Here documents', '8.7.6.3.6') do a = < e = %W// f = %W[[ab cd][ef]] g = %W{ ab #{-1}1 2#{2} } h = %W(a\nb test\ abc c\ d x\y x\\y x\\\y) assert_equal ['abc3def', '}g'], a assert_equal ['abc', '5', 'def', '(g'], b assert_equal ['7'],c assert_equal ['9'], d assert_equal [], e assert_equal ['[ab', 'cd][ef]'], f assert_equal ['ab', '-11', '22'], g assert_equal ["a\nb", 'test abc', "c\nd", "xy", "x\\y", "x\\y"], h a = %w{abc#{1+2}def \}g} b = %w(abc #{2+3} def \(g) c = %w[#{3+4}] d = %w< #{4+5} > e = %w// f = %w[[ab cd][ef]] g = %w{ ab #{-1}1 2#{2} } h = %w(a\nb test\ abc c\ d x\y x\\y x\\\y) assert_equal ['abc#{1+2}def', '}g'], a assert_equal ['abc', '#{2+3}', 'def', '(g'], b assert_equal ['#{3+4}'], c assert_equal ['#{4+5}'], d assert_equal [], e assert_equal ['[ab', 'cd][ef]'], f assert_equal ['ab', '#{-1}1', '2#{2}'], g assert_equal ["a\\nb", "test abc", "c\nd", "x\\y", "x\\y", "x\\\\y"], h end assert('Literals Array of symbols') do a = %I{abc#{1+2}def \}g} b = %I(abc #{2+3} def \(g) c = %I[#{3+4}] d = %I< #{4+5} > e = %I// f = %I[[ab cd][ef]] g = %I{ ab #{-1}1 2#{2} } assert_equal [:'abc3def', :'}g'], a assert_equal [:'abc', :'5', :'def', :'(g'], b assert_equal [:'7'],c assert_equal [:'9'], d assert_equal [], e assert_equal [:'[ab', :'cd][ef]'], f assert_equal [:'ab', :'-11', :'22'], g a = %i{abc#{1+2}def \}g} b = %i(abc #{2+3} def \(g) c = %i[#{3+4}] d = %i< #{4+5} > e = %i// f = %i[[ab cd][ef]] g = %i{ ab #{-1}1 2#{2} } assert_equal [:'abc#{1+2}def', :'}g'], a assert_equal [:'abc', :'#{2+3}', :'def', :'(g'], b assert_equal [:'#{3+4}'], c assert_equal [:'#{4+5}'], d assert_equal [] ,e assert_equal [:'[ab', :'cd][ef]'], f assert_equal [:'ab', :'#{-1}1', :'2#{2}'], g end assert('Literals Symbol', '8.7.6.6') do # do not compile error :$asd :@asd :@@asd :asd= :asd! :asd? :+ :+@ :if :BEGIN a = :"asd qwe" b = :'foo bar' c = :"a#{1+2}b" d = %s(asd) e = %s( foo \)) f = %s[asd \[ qwe] g = %s/foo#{1+2}bar/ h = %s{{foo bar}} assert_equal :'asd qwe', a assert_equal :"foo bar", b assert_equal :a3b, c assert_equal :asd, d assert_equal :' foo )', e assert_equal :"asd [\nqwe", f assert_equal :'foo#{1+2}bar', g assert_equal :'{foo bar}', h end # Not Implemented ATM assert('Literals Regular expression', '8.7.6.5') do nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/exception.rb0000644000000000000000000000013115077107276021437 xustar0030 mtime=1761382078.139420479 29 atime=1761382080.16341122 30 ctime=1761382108.484302162 nghttp2-1.68.0/third-party/mruby/test/t/exception.rb0000644000175100017510000001365715077107276022044 0ustar00runnerrunner## # Exception ISO Test assert('Exception', '15.2.22') do assert_equal Class, Exception.class end assert('Exception.exception', '15.2.22.4.1') do e = Exception.exception('a') assert_equal Exception, e.class end assert('Exception#exception', '15.2.22.5.1') do e = Exception.new re = RuntimeError.new assert_equal e, e.exception assert_equal e, e.exception(e) assert_equal re, re.exception(re) changed_re = re.exception('message has changed') assert_not_equal re, changed_re assert_equal 'message has changed', changed_re.message end assert('Exception#message', '15.2.22.5.2') do e = Exception.exception('a') assert_equal 'a', e.message end assert('Exception#to_s', '15.2.22.5.3') do e = Exception.exception('a') assert_equal 'a', e.to_s end assert('Exception.exception', '15.2.22.4.1') do e = Exception.exception() e.__send__(:initialize,'a') assert_equal 'a', e.message end assert('NameError', '15.2.31') do assert_raise(NameError) do raise NameError.new end e = NameError.new "msg", "name" assert_equal "msg", e.message assert_equal "name", e.name end assert('ScriptError', '15.2.37') do assert_raise(ScriptError) do raise ScriptError.new end end assert('SyntaxError', '15.2.38') do assert_raise(SyntaxError) do raise SyntaxError.new end end # Not ISO specified assert('Exception 1') do r=begin 1+1 ensure 2+2 end assert_equal 2, r end assert('Exception 2') do r=begin 1+1 begin 2+2 ensure 3+3 end ensure 4+4 end assert_equal 4, r end assert('Exception 3') do r=begin 1+1 begin 2+2 ensure 3+3 end ensure 4+4 begin 5+5 ensure 6+6 end end assert_equal 4, r end assert('Exception 4') do a = nil 1.times{|e| begin rescue => err end a = err.class } assert_equal NilClass, a end assert('Exception 5') do $ans = [] def m $! end def m2 1.times{ begin return ensure $ans << m end } end m2 assert_equal [nil], $ans end assert('Exception 6') do $i = 0 def m iter{ begin $i += 1 begin $i += 2 break ensure end ensure $i += 4 end $i = 0 } end def iter yield end m assert_equal 7, $i end assert('Exception 7') do $i = 0 def m begin $i += 1 begin $i += 2 return ensure $i += 3 end ensure $i += 4 end p :end end m assert_equal 10, $i end assert('Exception 8') do r=begin 1 rescue 2 else 3 end assert_equal 3, r end assert('Exception 9') do r=begin 1+1 rescue 2+2 else 3+3 ensure 4+4 end assert_equal 6, r end assert('Exception 10') do r=begin 1+1 begin 2+2 rescue 3+3 else 4+4 end rescue 5+5 else 6+6 ensure 7+7 end assert_equal 12, r end assert('Exception 11') do a = :ok begin begin raise Exception rescue a = :ng end rescue Exception end assert_equal :ok, a end assert('Exception 12') do a = :ok begin raise Exception rescue a = :ng rescue Exception end assert_equal :ok, a end assert('Exception 13') do a = :ng begin raise StandardError rescue TypeError, ArgumentError a = :ng rescue a = :ok else a = :ng end assert_equal :ok, a end assert('Exception 14') do def (o = Object.new).exception_test14; UnknownConstant end a = :ng begin o.__send__(:exception_test14) rescue a = :ok end assert_equal :ok, a end assert('Exception 15') do a = begin :ok rescue :ko end assert_equal :ok, a end assert('Exception 16') do begin raise "foo" false rescue => e assert_equal "foo", e.message end end assert('Exception 17') do r=begin raise "a" # RuntimeError rescue ArgumentError 1 rescue StandardError 2 else 3 ensure 4 end assert_equal 2, r end assert('Exception 18') do r=begin 0 rescue ArgumentError 1 rescue StandardError 2 else 3 ensure 4 end assert_equal 3, r end assert('Exception 19') do class Class4Exception19 def a r = @e = false begin b rescue TypeError r = self.z end [ r, @e ] end def b begin 1 * "b" ensure @e = self.zz end end def zz true end def z true end end assert_equal [true, true], Class4Exception19.new.a end assert('Exception#inspect') do assert_equal "Exception", Exception.new.inspect assert_equal "Exception", Exception.new("").inspect assert_equal "#", Exception.new("error!").inspect end assert('Exception#backtrace') do assert_nothing_raised do begin raise "get backtrace" rescue => e e.backtrace end end end assert('Raise in ensure') do assert_raise(ArgumentError) do begin raise "" # RuntimeError ensure raise ArgumentError end end end def backtrace_available? begin raise "XXX" rescue => exception return false if exception.backtrace.empty? not exception.backtrace[0].include?("unknown") end end assert('GC in rescue') do skip "backtrace isn't available" unless backtrace_available? line = nil begin [1].each do [2].each do [3].each do line = __LINE__; raise "XXX" end end end rescue => exception GC.start assert_equal("#{__FILE__}:#{line}", exception.backtrace.first) end end assert('Method call in rescue') do skip "backtrace isn't available" unless backtrace_available? line = nil begin [1].each do [2].each do line = __LINE__; raise "XXX" end end rescue => exception [3].each do end assert_equal("#{__FILE__}:#{line}", exception.backtrace.first) end end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/nameerror.rb0000644000000000000000000000013215077107276021434 xustar0030 mtime=1761382078.140420474 30 atime=1761382080.164411215 30 ctime=1761382108.501302113 nghttp2-1.68.0/third-party/mruby/test/t/nameerror.rb0000644000175100017510000000111515077107276022022 0ustar00runnerrunner## # NameError ISO Test assert('NameError', '15.2.31') do assert_equal Class, NameError.class end assert('NameError#name', '15.2.31.2.1') do # This check is not duplicate with 15.2.31.2.2 check. # Because the NameError in this test is generated in # C API. class TestDummy alias foo bar rescue NameError => e $test_dummy_result = e.name end assert_equal :bar, $test_dummy_result end assert('NameError#initialize', '15.2.31.2.2') do e = NameError.new('a', :foo) assert_equal NameError, e.class assert_equal 'a', e.message assert_equal :foo, e.name end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/codegen.rb0000644000000000000000000000013215077107276021046 xustar0030 mtime=1761382078.139420479 30 atime=1761382080.162411224 30 ctime=1761382108.519302061 nghttp2-1.68.0/third-party/mruby/test/t/codegen.rb0000644000175100017510000001202315077107276021434 0ustar00runnerrunner## # Codegen tests assert('peephole optimization does not eliminate move whose result is reused') do assert_raise LocalJumpError do def method yield end method(&a &&= 0) end end assert('empty condition in ternary expression parses correctly') do assert_equal(() ? 1 : 2, 2) end assert('method call with exactly 127 arguments') do def args_to_ary(*args) args end assert_equal [0]*127, args_to_ary( 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ) end assert('nested empty heredoc') do _, a = nil, <0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9, 10=>10, 11=>11, 12=>12, 13=>13, 14=>14, 15=>15, 16=>16, 17=>17, 18=>18, 19=>19, 20=>20, 21=>21, 22=>22, 23=>23, 24=>24, 25=>25, 26=>26, 27=>27, 28=>28, 29=>29, 30=>30, 31=>31, 32=>32, 33=>33, 34=>34, 35=>35, 36=>36, 37=>37, 38=>38, 39=>39, 40=>40, 41=>41, 42=>42, 43=>43, 44=>44, 45=>45, 46=>46, 47=>47, 48=>48, 49=>49, 50=>50, 51=>51, 52=>52, 53=>53, 54=>54, 55=>55, 56=>56, 57=>57, 58=>58, 59=>59, 60=>60, 61=>61, 62=>62, 63=>63, 64=>64, 65=>65, 66=>66, 67=>67, 68=>68, 69=>69, 70=>70, 71=>71, 72=>72, 73=>73, 74=>74, 75=>75, 76=>76, 77=>77, 78=>78, 79=>79, 80=>80, 81=>81, 82=>82, 83=>83, 84=>84, 85=>85, 86=>86, 87=>87, 88=>88, 89=>89, 90=>90, 91=>91, 92=>92, 93=>93, 94=>94, 95=>95, 96=>96, 97=>97, 98=>98, 99=>99, 100=>100, 101=>101, 102=>102, 103=>103, 104=>104, 105=>105, 106=>106, 107=>107, 108=>108, 109=>109, 110=>110, 111=>111, 112=>112, 113=>113, 114=>114, 115=>115, 116=>116, 117=>117, 118=>118, 119=>119, 120=>120, 121=>121, 122=>122, 123=>123, 124=>124, 125=>125, 126=>126) end # NODE_OP_ASGN o = Object.new class << o attr_accessor :a end o.a = 1 assert_nothing_raised{ o.a += 1 } o.a = 1 assert_nothing_raised{ o.a <<= 1 } o.a = 1 assert_nothing_raised{ o.a &&= 1 } o = { k: 1 } assert_nothing_raised{ o[:k] += 1 } o = { k: 1 } assert_nothing_raised{ o[:k] <<= 1 } o = { k: 1 } assert_nothing_raised{ o[:k] &&= 1 } o = { k: 1 } assert_nothing_raised{ o[*[:k]] += 1 } o = { k: 1 } assert_nothing_raised{ o[*[:k]] <<= 1 } o = { k: 1 } assert_nothing_raised{ o[*[:k]] &&= 1 } # NODE_YIELD def check_node_yield yield end assert_nothing_raised do check_node_yield{} end # NODE_DXSTR assert_raise(NotImplementedError){ `#{:dynamic}` } # NODE_XSTR assert_raise(NotImplementedError){ `static` } # NODE_DREGX class Regexp; end assert_raise(NoMethodError){ /#{'dynamic'}tail/ } assert_raise(NoMethodError){ /#{'dynamic'}tail/iu } # NODE_REGX assert_raise(NoMethodError){ /static/ } assert_raise(NoMethodError){ /static/iu } Object.__send__(:remove_const,:Regexp) # NODE_UNDEF assert_nothing_raised do class << Object.new undef inspect end end # NODE_ALIAS assert_nothing_raised do class << Object.new alias inspect2 inspect end end end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/argumenterror.rb0000644000000000000000000000013215077107276022336 xustar0030 mtime=1761382078.139420479 30 atime=1761382080.162411224 30 ctime=1761382108.504302104 nghttp2-1.68.0/third-party/mruby/test/t/argumenterror.rb0000644000175100017510000000204215077107276022724 0ustar00runnerrunner## # ArgumentError ISO Test def assert_argnum_error(given, expected, &block) assert("wrong number of arguments") do message = "wrong number of arguments (given #{given}, expected #{expected})" assert_raise_with_message(ArgumentError, message, &block) end end assert('ArgumentError', '15.2.24') do e2 = nil a = [] begin # this will cause an exception due to the wrong arguments a[] rescue => e1 e2 = e1 end assert_equal(Class, ArgumentError.class) assert_equal(ArgumentError, e2.class) end assert("'wrong number of arguments' from mrb_get_args") do assert_argnum_error(0, "1+"){__send__} assert_argnum_error(0, 1..2){Object.const_defined?} assert_argnum_error(3, 1..2){Object.const_defined?(:A, true, 2)} assert_argnum_error(2, 0..1){{}.default(1, 2)} assert_argnum_error(1, 2){Object.const_set(:B)} assert_argnum_error(3, 2){Object.const_set(:C, 1, 2)} end assert('Call to MRB_ARGS_NONE method') do assert_raise(ArgumentError) { nil.__id__ 1 } assert_raise(ArgumentError) { nil.__id__ opts: 1 } end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/vformat.rb0000644000000000000000000000013215077107276021120 xustar0030 mtime=1761382078.141420469 30 atime=1761382080.164411215 30 ctime=1761382108.527302037 nghttp2-1.68.0/third-party/mruby/test/t/vformat.rb0000644000175100017510000000535315077107276021516 0ustar00runnerrunnerdef sclass(v) class << v self end end assert('mrb_vformat') do vf = TestVFormat assert_equal '', vf.z('') assert_equal 'No specifier!', vf.z('No specifier!') assert_equal '`c`: C', vf.c('`c`: %c', ?C) assert_equal '`d`: 123', vf.d('`d`: %d', 123) assert_equal '`d`: -79', vf.d('`d`: %d', -79) assert_equal '`i`: 514', vf.i('`i`: %i', 514) assert_equal '`i`: -83', vf.i('`i`: %i', -83) assert_equal '`t`: NilClass', vf.v('`t`: %t', nil) assert_equal '`t`: FalseClass', vf.v('`t`: %t', false) assert_equal '`t`: TrueClass', vf.v('`t`: %t', true) assert_equal '`t`: Integer', vf.v('`t`: %t', 0) assert_equal '`t`: Hash', vf.v('`t`: %t', {k: "value"}) assert_match '#>>', vf.v('%t', sclass({})) assert_equal 'string and length', vf.l('string %l length', 'andante', 3) assert_equal '`n`: sym', vf.n('`n`: %n', :sym) assert_equal '%C文字列%', vf.s('%s', '%C文字列%') assert_equal '`C`: Kernel module', vf.C('`C`: %C module', Kernel) assert_equal '`C`: NilClass', vf.C('`C`: %C', nil.class) assert_match '#>', vf.C('%C', sclass("")) assert_equal '`T`: NilClass', vf.v('`T`: %T', nil) assert_equal '`T`: FalseClass', vf.v('`T`: %T', false) assert_equal '`T`: TrueClass', vf.v('`T`: %T', true) assert_equal '`T`: Integer', vf.v('`T`: %T', 0) assert_equal '`T`: Hash', vf.v('`T`: %T', {k: "value"}) assert_match 'Class', vf.v('%T', sclass({})) assert_equal '`Y`: nil', vf.v('`Y`: %Y', nil) assert_equal '`Y`: false', vf.v('`Y`: %Y', false) assert_equal '`Y`: true', vf.v('`Y`: %Y', true) assert_equal '`Y`: Integer', vf.v('`Y`: %Y', 0) assert_equal '`Y`: Hash', vf.v('`Y`: %Y', {k: "value"}) assert_equal 'Class', vf.v('%Y', sclass({})) assert_match '#>', vf.v('%v', sclass("")) assert_equal '`v`: 1...3', vf.v('`v`: %v', 1...3) assert_equal '`S`: {a: 1, "b" => "c"}', vf.v('`S`: %S', {a: 1, "b" => ?c}) assert_equal 'percent: %', vf.z('percent: %%') assert_equal '"I": inspect char', vf.c('%!c: inspect char', ?I) assert_equal '709: inspect mrb_int', vf.i('%!d: inspect mrb_int', 709) assert_equal '"a\x00b\xff"', vf.l('%!l', "a\000b\xFFc\000d", 4) assert_equal ':"&.": inspect symbol', vf.n('%!n: inspect symbol', :'&.') assert_equal 'inspect "String"', vf.v('inspect %!v', 'String') assert_equal 'inspect Array: [1, :x, {}]', vf.v('inspect Array: %!v', [1,:x,{}]) assert_match '`!C`: #', vf.C('`!C`: %!C', Class.new) assert_equal 'escape: \\%a,b,c,d', vf.v('escape: \\\\\%a,b,\c%v', ',d') skip unless Object.const_defined?(:Float) assert_equal '`f`: 0.0125', vf.f('`f`: %f', 0.0125) assert_equal '-Infinity', vf.f('%f', -Float::INFINITY) assert_equal 'NaN: Not a Number', vf.f('%f: Not a Number', Float::NAN) end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/hash.rb0000644000000000000000000000013115077107276020364 xustar0030 mtime=1761382078.139420479 29 atime=1761382080.16341122 30 ctime=1761382108.499302118 nghttp2-1.68.0/third-party/mruby/test/t/hash.rb0000644000175100017510000006411315077107276020762 0ustar00runnerrunner## # Hash ISO Test class HashKey attr_accessor :value, :error, :callback self.class.alias_method :[], :new def initialize(value, error: nil, callback: nil) @value = value @error = error @callback = callback end def ==(other) @callback.(:==, self, other) if @callback return raise_error(:==) if @error == true || @error == :== other.kind_of?(self.class) && @value == other.value end def eql?(other) @callback.(:eql?, self, other) if @callback return raise_error(:eql?) if @error == true || @error == :eql? other.kind_of?(self.class) && @value.eql?(other.value) end def hash @callback.(:hash, self) if @callback return raise_error(:hash) if @error == true || @error == :hash @value % 3 end def to_s "#{self.class}[#{@value}]" end alias inspect to_s def raise_error(name) raise "##{self}: #{name} error" end end class HashEntries < Array self.class.alias_method :[], :new def initialize(entries) self.replace(entries) end def key(index, k=get=true) get ? self[index][0] : (self[index][0] = k) end def value(index, v=get=true) get ? self[index][1] : (self[index][1] = v) end def keys; map{|k, v| k} end def values; map{|k, v| v} end def each_key(&block) each{|k, v| block.(k)} end def each_value(&block) each{|k, v| block.(v)} end def dup2; self.class[*map{|k, v| [k.dup, v.dup]}] end def to_s; "#{self.class}#{super}" end alias inspect to_s def hash_for(hash={}, &block) each{|k, v| hash[k] = v} block.(hash) if block hash end end def ar_entries HashEntries[ [1, "one"], [HashKey[2], :two], [nil, :two], [:one, 1], ["&", "&"], [HashKey[6], :six], [HashKey[5], :five], # same hash code as HashKey[2] ] end def ht_entries ar_entries.dup.push( ["id", 32], [:date, "2020-05-02"], [200, "OK"], ["modifiers", ["left_shift", "control"]], [:banana, :yellow], ["JSON", "JavaScript Object Notation"], [:size, :large], ["key_code", "h"], ["h", 0x04], [[3, 2, 1], "three, two, one"], [:auto, true], [HashKey[12], "December"], [:path, "/path/to/file"], [:name, "Ruby"], ) end def merge_entries!(entries1, entries2) entries2.each do |k2, v2| entry1 = entries1.find{|k1, _| k1.eql?(k2)} entry1 ? (entry1[1] = v2) : (entries1 << [k2, v2]) end entries1 end def product(*arrays, &block) sizes = Array.new(arrays.size+1, 1) (arrays.size-1).downto(0){|i| sizes[i] = arrays[i].size * sizes[i+1]} size = sizes[0] results = Array.new(size){[]} arrays.each_with_index do |array, arrays_i| results_i = -1 (size / sizes[arrays_i]).times do array.each do |v| sizes[arrays_i+1].times{results[results_i+=1] << v} end end end results.each{block.(_1)} end def assert_iterator(exp, obj, meth) params = [] obj.__send__(meth) {|param| params << param} assert_equal(exp, params) end def assert_nothing_crashed(&block) block.call rescue nil pass end assert('Hash', '15.2.13') do assert_equal(Class, Hash.class) end [[:==, '15.2.13.4.1'], [:eql?, '']].each do |meth, iso| assert("Hash##{meth}", iso) do cls = Class.new(Hash){attr_accessor :foo} [ar_entries, ht_entries].each do |entries| h1 = entries.hash_for h2 = entries.dup.reverse!.hash_for assert_operator(h1, meth, h2) assert_operator(h1, meth, h1) assert_not_operator(h1, meth, true) assert_operator({}, meth, Hash.new) h1 = entries.hash_for(cls.new(1)) {|h| h.foo = 1} h2 = entries.hash_for(cls.new(2)) {|h| h.foo = 2} assert_operator(h1, meth, h2) h1 = entries.hash_for h2 = entries.hash_for(cls.new) assert_operator(h1, meth, h2) h1 = (entries.dup << [:_k, 1]).hash_for h2 = (entries.dup << [:_k, 2]).hash_for assert_not_operator(h1, meth, h2) h1 = (entries.dup << [:_k1, 0]).hash_for h2 = (entries.dup << [:_k2, 0]).hash_for assert_not_operator(h1, meth, h2) h1 = entries.hash_for h2 = (entries.dup << [:_k, 2]).hash_for assert_not_operator(h1, meth, h2) k1, v1 = HashKey[-1], HashKey[-2] k2, v2 = HashKey[-1], HashKey[-2] h1 = (entries.dup << [k1, v1]).hash_for h2 = (entries.dup << [k2, v2]).hash_for product([h1, h2], [k1, k2], %i[eql? hash]) do |h, k, m| [k1, k2].each{_1.callback = nil} k.callback = ->(name, *){h.clear if name == m} assert_nothing_crashed{h1.__send__(meth, h2)} end product([h1, h2], [v1, v2]) do |h, v| [v1, v2].each{_1.callback = nil} v.callback = ->(name, *){h.clear if name == meth} assert_nothing_crashed{h1.__send__(meth, h2)} end if Object.const_defined?(:Float) h1 = (entries.dup << [-1, true]).hash_for h2 = (entries.dup << [-1.0, true]).hash_for assert_not_operator(h1, meth, h2) h1 = (entries.dup << [-1.0, true]).hash_for h2 = (entries.dup << [-1, true]).hash_for assert_not_operator(h1, meth, h2) h1 = (entries.dup << [:_k, 1]).hash_for h2 = (entries.dup << [:_k, 1.0]).hash_for if meth == :== assert_operator(h1, meth, h2) else assert_not_operator(h1, meth, h2) end end end end end assert('Hash#[]', '15.2.13.4.2') do [ar_entries, ht_entries].each do |entries| h = entries.hash_for assert_equal(entries.size, h.size) entries.each{|k, v| assert_equal(v, h[k])} assert_equal(nil, h["_not_found_"]) assert_equal(nil, h[:_not_dound_]) assert_equal(nil, h[-2]) k = HashKey[-4] h[HashKey[-1]] = -1 h[k] = -4 h.delete(k) assert_equal(nil, h[k]) if Object.const_defined?(:Float) h[-2] = 22 assert_equal(nil, h[-2.0]) h[-3.0] = 33 assert_equal(nil, h[-3]) assert_equal(33, h[-3.0]) end k = HashKey[-2] k.callback = ->(name, *){h.clear if name == :eql?} assert_nothing_crashed{h[k]} k.callback = ->(name, *){h.clear if name == :hash} assert_nothing_crashed{h[k]} end # Hash#[] should call #default (#3272) h = {} def h.default(k); self[k] = 1; end h[:foo] += 1 assert_equal(2, h[:foo]) end [%w[[]= 3], %w[store 26]].each do |meth, no| assert("Hash##{meth}", "15.2.13.4.#{no}") do [{}, ht_entries.hash_for].each do |h| # duplicated key k = :_dup_key h.__send__(meth, k, 1) size = h.size h.__send__(meth, k, 2) assert_equal(size, h.size) assert_equal(2, h[k]) # freeze string key k = "_mutable" h.__send__(meth, k, 1) h_k = h.keys[-1] assert_not_same(k, h_k) assert_predicate(h_k, :frozen?) assert_not_predicate(k, :frozen?) # frozen string key k = "_immutable".freeze h.__send__(meth, k, 2) h_k = h.keys[-1] assert_same(k, h_k) assert_predicate(h_k, :frozen?) # numeric key if Object.const_defined?(:Float) h.__send__(meth, 3, :fixnum) h.__send__(meth, 3.0, :float) assert_equal(:fixnum, h[3]) assert_equal(:float, h[3.0]) h.__send__(meth, 4.0, :float) h.__send__(meth, 4, :fixnum) assert_equal(:fixnum, h[4]) assert_equal(:float, h[4.0]) end # other key k = [:_array] h.__send__(meth, k, :_array) h_k = h.keys[-1] assert_same(k, h_k) assert_not_predicate(h_k, :frozen?) assert_not_predicate(k, :frozen?) # deleted key k1, k2, k3 = HashKey[-1], HashKey[-4], HashKey[-7] # same hash code h.__send__(meth, k1, 1) h.__send__(meth, k2, -4) h.__send__(meth, k3, 73) size = h.size h.delete(k1) h.delete(k2) h.__send__(meth, k2, 40) assert_equal(nil, h[k1]) assert_equal(40, h[k2]) assert_equal(73, h[k3]) assert_equal(size - 1, h.size) # frozen h.freeze assert_raise(FrozenError){h.__send__(meth, -100, 1)} end [ar_entries.hash_for, ht_entries.hash_for].each do |h| k = HashKey[-2] k.callback = ->(name, *){h.clear if name == :eql?} assert_nothing_crashed{h.__send__(meth, k, 2)} k.callback = ->(name, *){h.clear if name == :hash} assert_nothing_crashed{h.__send__(meth, k, 2)} end end end assert('Hash#clear', '15.2.13.4.4') do [ar_entries, ht_entries].each do |entries| h = entries.hash_for assert_same(h, h.clear) assert_equal(0, h.size) assert_nil(h[entries.key(3)]) h.freeze assert_raise(FrozenError){h.clear} end h = {}.freeze assert_raise(FrozenError){h.clear} end assert('Hash#dup') do cls = Class.new(Hash){attr_accessor :foo} [ar_entries, ht_entries].each do |entries| h1 = entries.hash_for(cls.new(61)){|h| h.foo = 23}.freeze h2 = h1.dup assert_not_predicate(h2, :frozen?) assert_equal(h1.class, h2.class) assert_equal(entries, h2.to_a) assert_equal(23, h2.foo) assert_equal(61, h2["_not_found_"]) h2[-10] = 10 assert_equal(10, h2[-10]) assert_not_operator(h1, :key?, -10) h = entries.hash_for k = HashKey[-1] h[k] = 1 k.callback = ->(*){h.clear} assert_nothing_crashed{h.dup} end end assert('Hash#default', '15.2.13.4.5') do [ar_entries, ht_entries].each do |entries| h = entries.hash_for(Hash.new) assert_equal(nil, h.default) assert_equal(nil, h.default(-2)) h = entries.hash_for(Hash.new(-88)) assert_equal(-88, h.default) assert_equal(-88, h.default(-2)) assert_not_operator(h, :key?, -2) assert_raise(ArgumentError){h.default(-2,-2)} proc = ->(h, k){h[k] = k * 3} h = entries.hash_for(Hash.new(proc)) assert_equal(proc, h.default(-2)) h = entries.hash_for(Hash.new(&proc)) assert_equal(nil, h.default) assert_not_operator(h, :key?, -2) assert_equal(-6, h.default(-2)) assert_equal(-6, h[-2]) h[-2] = -5 assert_equal(-6, h.default(-2)) assert_equal(-6, h[-2]) end end assert('Hash#default=', '15.2.13.4.6') do [ar_entries, ht_entries].each do |entries| h = entries.hash_for(Hash.new) h.default = 3 assert_equal(3, h[-2]) assert_equal(entries.value(0), h[entries.key(0)]) h.default = 4 assert_equal(4, h[-2]) h.default = nil assert_equal(nil, h[-2]) h.default = [5] assert_same(h[-2], h[-3]) h.freeze assert_raise(FrozenError){h.default = 3} end end assert('Hash#default_proc', '15.2.13.4.7') do [ar_entries, ht_entries].each do |entries| h = entries.hash_for({}) assert_nil(h.default_proc) h = entries.hash_for(Hash.new(34)) assert_nil(h.default_proc) h = entries.hash_for(Hash.new{|h, k| h[k] = k * 3}) proc = h.default_proc assert_equal(Proc, proc.class) assert_equal(6, proc.(h, 2)) assert_equal([2, 6], h.to_a[-1]) end end assert('Hash#delete', '15.2.13.4.8') do [ar_entries, ht_entries].each do |entries| h = entries.hash_for pairs = entries.dup [0, 2, -1].each do |i| k, v = pairs.delete_at(i) assert_equal(v, h.delete(k)) assert_equal(nil, h[k]) assert_equal(false, h.key?(k)) end [entries.key(0), "_not_found_"].each {|k|assert_equal(nil, h.delete(k))} assert_equal(pairs.size, h.size) assert_equal(pairs, h.to_a) pairs.each {|k, v| assert_equal(v, h[k])} h = entries.hash_for pairs = entries.dup [pairs.delete_at(1), ["_not_found_", "_default"]].each do |k, v| assert_equal(v, h.delete(k){"_default"}) assert_equal(nil, h[k]) assert_equal(false, h.key?(k)) end assert_equal(pairs.size, h.size) assert_equal(pairs, h.to_a) pairs.each {|k, v| assert_equal(v, h[k])} if Object.const_defined?(:Float) h = entries.dup.push([-5, 1], [-5.0, 2], [-6.0, 3], [-6, 4]).hash_for assert_equal(1, h.delete(-5)) assert_equal(3, h.delete(-6.0)) end # nil value with block h = entries.hash_for k = "_nil" h[k] = nil assert_equal(nil, h.delete(k){"blk"}) assert_equal(false, h.key?(k)) k = HashKey[-31, callback: ->(*){h.clear}] assert_nothing_crashed{h.delete(k)} end assert_raise(ArgumentError){{}.delete} assert_raise(ArgumentError){{}.delete(1,2)} h = {}.freeze assert_raise(FrozenError){h.delete(1)} end [%w[each 9], %w[each_key 10], %w[each_value 11]].each do |meth, no| assert("Hash##{meth}", "15.2.13.4.#{no}") do [ar_entries, ht_entries].each do |entries| exp = [] entries.__send__(meth){|param| exp << param} assert_iterator(exp, entries.hash_for, meth) h = entries.hash_for entries.shift h.shift entry = entries.delete_at(1) h.delete(entry[0]) h.delete(entries.delete_at(-4)[0]) entries << entry h.store(*entry) exp = [] entries.__send__(meth){|param| exp << param} assert_iterator(exp, h, meth) end assert_iterator([], {}, meth) end end assert('Hash#empty?', '15.2.13.4.12') do [ar_entries, ht_entries].each do |entries| assert_not_predicate entries.hash_for, :empty? h = entries.hash_for h.shift h.delete(entries.key(-1)) assert_not_predicate h, :empty? h = entries.hash_for entries.size.times{h.shift} assert_predicate(h, :empty?) h = entries.hash_for entries.each {|k, v| h.delete(k)} assert_predicate(h, :empty?) end assert_predicate(Hash.new, :empty?) assert_predicate(Hash.new(1), :empty?) assert_predicate(Hash.new{|h, k| h[k] = 2}, :empty?) end [%w[has_key? 13], %w[include? 15], %w[key? 18], %w[member? 21]].each do |meth,no| assert("Hash##{meth}", "15.2.13.4.#{no}") do [ar_entries, ht_entries].each do |entries| pairs = entries.dup.push([HashKey[-3], 3], [nil, "NIL"]) h = pairs.hash_for pairs.each{|k, v| assert_operator(h, meth, k)} assert_not_operator(h, meth, HashKey[-6]) assert_not_operator(h, meth, 3) if Object.const_defined?(:Float) hh = entries.push([-7, :i], [-8.0, :f]).hash_for assert_not_operator(hh, meth, -7.0) assert_not_operator(hh, meth, -8) assert_operator(hh, meth, -8.0) end h.shift assert_not_operator(h, meth, pairs.key(0)) h.delete(pairs.key(3)) assert_not_operator(h, meth, pairs.key(3)) k = HashKey[-31, callback: ->(*){h.clear}] assert_nothing_crashed{h.__send__(meth, k)} end end h = Hash.new{|h, k| h[1] = 1} assert_not_operator(h, meth, 1) end [%w[has_value? 14], %w[value? 24]].each do |meth, no| assert("Hash##{meth}", "15.2.13.4.#{no}") do [ar_entries, ht_entries].each do |entries| entries.push([HashKey[-5], -8], ["NIL", nil]) h = entries.hash_for entries.each{|k, v| assert_operator(h, meth, v)} assert_operator(h, meth, -8.0) if Object.const_defined?(:Float) assert_not_operator(h, meth, "-8") h.shift assert_not_operator(h, meth, entries.value(0)) h.delete(entries.key(3)) assert_not_operator(h, meth, entries.value(3)) v = HashKey[-31, callback: ->(*){h.clear}] assert_nothing_crashed{h.__send__(meth, v)} end end h = Hash.new{|h, k| h[1] = 1} assert_not_operator(h, meth, 1) end assert('Hash#initialize', '15.2.13.4.16') do h = Hash.new assert_equal(Hash, h.class) assert_not_operator(h, :key?, 1) assert_equal(nil, h[1]) h = Hash.new([8]) assert_not_operator(h, :key?, 1) assert_equal([8], h[1]) assert_same(h[1], h[2]) k = "key" h = Hash.new{|hash, key| [hash, key]} assert_not_operator(h, :key?, k) assert_equal([h, k], h[k]) assert_same(h, h[k][0]) assert_same(k, h[k][1]) assert_raise(ArgumentError){Hash.new(1,2)} assert_raise(ArgumentError){Hash.new(1){}} end [%w[keys 19], %w[values 28]].each do |meth, no| assert("Hash##{meth}", "15.2.13.4.#{no}") do [ar_entries, ht_entries].each do |entries| h = entries.hash_for assert_equal(entries.__send__(meth), h.__send__(meth)) h.shift entries.shift h.delete(entries.delete_at(3)[0]) assert_equal(entries.__send__(meth), h.__send__(meth)) end assert_equal([], {}.__send__(meth)) end end [%w[length 20], %w[size 25]].each do |meth, no| assert("Hash##{meth}", "15.2.13.4.#{no}") do [ar_entries, ht_entries].each do |entries| h = entries.hash_for assert_equal(entries.size, h.__send__(meth)) h.shift entries.shift h.delete(entries.delete_at(3)[0]) assert_equal(entries.size, h.__send__(meth)) end assert_equal(0, Hash.new.__send__(meth)) end end assert('Hash#merge', '15.2.13.4.22') do cls = Class.new(Hash){attr_accessor :foo} ar_pairs = HashEntries[ ["id", 32], [nil, :two], ["&", "&"], [:same_key, :AR], [HashKey[2], 20], ] ht_pairs = HashEntries[ *(1..20).map{[_1, _1.to_s]}, [:same_key, :HT], [:age, 32], [HashKey[5], 500], ] [[ar_pairs, ht_pairs], [ht_pairs, ar_pairs]].each do |entries1, entries2| h1 = entries1.hash_for(cls.new(:dv1)){|h| h.foo = :iv1}.freeze h2 = entries2.hash_for(Hash.new(:dv2)).freeze h3 = h1.merge(h2) assert_equal(entries1, h1.to_a) assert_equal(merge_entries!(entries1.dup2, entries2), h3.to_a) assert_equal(cls, h3.class) assert_equal(:dv1, h3.default) assert_equal(:iv1, h3.foo) h3 = {}.merge(entries2.hash_for(cls.new)) assert_equal(merge_entries!([], entries2), h3.to_a) assert_equal(Hash, h3.class) h3 = entries1.hash_for.merge({}) assert_equal(merge_entries!(entries1.dup2, []), h3.to_a) h1 = entries1.hash_for h2 = entries2.hash_for h3 = h1.merge(h2){|k, v1, v2| [k, v1, v2]} exp = merge_entries!(entries1.dup2, entries2) exp.find{|k, _| k == :same_key}[1] = [ :same_key, entries1.find{|k, _| k == :same_key}[1], entries2.find{|k, _| k == :same_key}[1], ] assert_equal(exp, h3.to_a) assert_raise(TypeError){entries1.hash_for.merge("str")} k2 = HashKey[-2] entries2 << [k2, 234] h1, h2 = entries1.hash_for, entries2.hash_for k2.callback = ->(name, *){h1.clear if name == :eql?} assert_nothing_crashed{h1.merge(h2)} h1, h2 = entries1.hash_for, entries2.hash_for k2.callback = ->(name, *){h2.clear if name == :eql?} assert_nothing_crashed{h1.merge(h2)} h1, h2 = entries1.hash_for, entries2.hash_for k2.callback = ->(name, *){h1.clear if name == :hash} assert_nothing_crashed{h1.merge(h2)} h1, h2 = entries1.hash_for, entries2.hash_for k2.callback = ->(name, *){h2.clear if name == :hash} assert_nothing_crashed{h1.merge(h2)} # single arguments assert_equal({a:1,b:2}, {a:1}.merge({b:2})) # multiple arguments assert_equal({a:1,b:2,c:3}, {a:1}.merge({b:2},{c:3})) end end assert("Hash#replace", "15.2.13.4.23") do cls = Class.new(Hash){attr_accessor :foo} e = [ar_entries, ht_entries] [e, e.reverse].each do |entries1, entries2| h1 = entries1.hash_for assert_same(h1, h1.replace(h1)) assert_equal(entries1, h1.to_a) h1 = {} assert_same(h1, h1.replace(entries2.hash_for)) assert_equal(entries2, h1.to_a) h1 = entries1.hash_for assert_same(h1, h1.replace({})) assert_predicate(h1, :empty?) pairs2 = entries2.dup h2 = pairs2.hash_for pairs2.shift h2.shift h2.delete(pairs2.delete_at(2)[0]) h2.delete(pairs2.delete_at(4)[0]) h1 = entries1.hash_for assert_same(h1, h1.replace(h2)) assert_equal(pairs2, h1.to_a) h1 = entries1.hash_for(Hash.new(10)) h2 = entries2.hash_for(Hash.new(20)) assert_same(h1, h1.replace(h2)) assert_equal(entries2, h1.to_a) assert_equal(20, h1.default) h1 = entries1.hash_for(Hash.new{_2}) h2 = entries2.hash_for(Hash.new{_2.to_s}) assert_same(h1, h1.replace(h2)) assert_equal(entries2, h1.to_a) assert_equal("-11", h1[-11]) h1 = entries1.hash_for(Hash.new(10)) h2 = entries2.hash_for(Hash.new{_2.to_s}) assert_same(h1, h1.replace(h2)) assert_equal(entries2, h1.to_a) assert_equal("-11", h1[-11]) h1 = entries1.hash_for(Hash.new{_2}) h2 = entries2.hash_for(Hash.new(20)) assert_same(h1, h1.replace(h2)) assert_equal(entries2, h1.to_a) assert_equal(20, h1[-1]) h1 = entries1.hash_for(cls.new(10)){|h| h.foo = 41} h2 = entries2.hash_for(cls.new(20)){|h| h.foo = 42}.freeze assert_same(h1, h1.replace(h2)) assert_equal(entries2, h1.to_a) assert_equal(20, h1.default) assert_equal(41, h1.foo) h1 = entries1.hash_for h2 = entries2.hash_for(cls.new) assert_same(h1, h1.replace(h2)) assert_equal(entries2, h1.to_a) assert_raise(TypeError){entries1.hash_for.replace([])} k2 = HashKey[-2] pairs2 = entries2.dup pairs2 << [k2, 23] h1 = entries1.hash_for h2 = pairs2.hash_for k2.callback = ->(*){h1.clear; h2.clear} assert_nothing_crashed{h1.replace(h2)} assert_raise(FrozenError){h1.freeze.replace(h1)} assert_raise(FrozenError){{}.freeze.replace({})} end end assert('Hash#shift', '15.2.13.4.24') do [ar_entries, ht_entries].each do |entries| pairs = entries.dup h = pairs.hash_for h.delete(pairs.delete_at(0)[0]) h.delete(pairs.delete_at(3)[0]) until pairs.empty? exp = pairs.shift act = h.shift assert_equal(Array, act.class) assert_equal(exp, act) assert_equal(exp.size, act.size) assert_not_operator(h, :key?, exp[0]) end assert_equal(nil, h.shift) assert_equal(0, h.size) h.default = -456 assert_equal(nil, h.shift) assert_equal(0, h.size) h.freeze assert_raise(FrozenError){h.shift} end h = Hash.new{|h, k| [h, k]} assert_equal(0, h.size) assert_equal(nil, h.shift) end # Not ISO specified %i[reject select].each do |meth| assert("Hash##{meth}") do cls = Class.new(Hash){attr_accessor :foo} [ar_entries, ht_entries].each do |entries| params = nil filter = ->((k, v)) do params << [k, v] String === k end h = entries.hash_for(cls.new(1)) params = [] ret = h.__send__(meth, &filter) assert_equal(entries, params) assert_equal(entries, h.to_a) assert_equal(1, h.default) assert_equal(entries.__send__(meth, &filter), ret.to_a) assert_equal(Hash, ret.class) assert_equal(nil, ret.default) params = [] assert_predicate({}.__send__(meth, &filter), :empty?) assert_predicate(params, :empty?) end end end %i[reject! select!].each do |meth| assert("Hash##{meth}") do [ar_entries, ht_entries].each do |entries| params = nil filter = ->((k, v)) do params << [k, v] String === k end pairs = entries.dup << ["_str", 5] h = pairs.hash_for(Hash.new(1)) params = [] ret = h.__send__(meth, &filter) assert_same(h, ret) assert_equal(pairs, params) assert_equal(pairs.__send__(meth.to_s[0..-2], &filter), h.to_a) assert_equal(1, h.default) h = pairs.hash_for ret = h.__send__(meth){meth == :select!} assert_nil(ret) assert_equal(pairs, h.to_a) assert_raise(FrozenError){h.freeze.__send__(meth, &filter)} end h = {} assert_nil(h.__send__(meth){}) assert_predicate(h, :empty?) end end %i[inspect to_s].each do |meth| assert("Hash##{meth}") do assert_equal('{}', Hash.new.__send__(meth)) h1 = {s: 0, a: [1,2], 37 => :b, d: "del", "c" => nil} h1.shift h1.delete(:d) s1 = 'a: [1, 2], 37 => :b, "c" => nil' h2 = Hash.new(100) (1..14).each{h2[_1] = _1 * 2} h2 = {**h2, **h1} s2 = "1 => 2, 2 => 4, 3 => 6, 4 => 8, 5 => 10, 6 => 12, 7 => 14, 8 => 16, " \ "9 => 18, 10 => 20, 11 => 22, 12 => 24, 13 => 26, 14 => 28, #{s1}" [[h1, s1], [h2, s2]].each do |h, s| assert_equal("{#{s}}", h.__send__(meth)) hh = {} hh[:recur] = hh h.each{|k, v| hh[k] = v} assert_equal("{recur: {...}, #{s}}", hh.__send__(meth)) hh = h.dup hh[hh] = :recur assert_equal("{#{s}, {...} => :recur}", hh.__send__(meth)) end end end assert('Hash#rehash') do cls = Class.new(Hash){attr_accessor :foo} [ar_entries, ht_entries].each do |entries| k1, k2, k3 = HashKey[-1], HashKey[-2], HashKey[-3] pairs = entries.dup.push( [-4, -40], [HashKey[-11], -5], [:_del, "_del"], [k1, :_k1], ["_a", "_b"], [k2, :_k2], ["_c", "_d"], [HashKey[-22], -21], [k3, :_k3], ) h = pairs.hash_for(cls.new(:defvar)){|h| h.foo = "f"} k1.value, k2.value, k3.value = -11, -11, -22 pairs1 = pairs.dup pairs1.delete([:_del, h.delete(:_del)]) exp_pairs1 = pairs1.hash_for.to_a assert_same(h, h.rehash) assert_equal(exp_pairs1, h.to_a) assert_equal(exp_pairs1.size, h.size) assert_equal(:defvar, h.default) assert_equal("f", h.foo) exp_pairs1.each {|k, v| assert_equal(v, h[k])} # If an error occurs during rehash, at least the entry list is not broken. k1.value, k2.value, k3.value = -1, -2, -3 h = pairs.hash_for k1.value = -11 pairs2 = pairs.dup pairs2.delete([:_del, h.delete(:_del)]) exp_pairs2 = pairs2.hash_for.to_a k2.error = :eql? assert_raise{h.rehash} act_pairs2 = h.to_a unless pairs2 == act_pairs2 && pairs2.size == h.size assert_equal(exp_pairs2, act_pairs2) assert_equal(exp_pairs2.size, h.size) end k1.value = -1 k2.error = false h = pairs.hash_for k1.callback = ->(name, *){h.clear if name == :eql?} assert_nothing_crashed{h.rehash} k1.callback = ->(name, *){h.clear if name == :hash} assert_nothing_crashed{h.rehash} end h = {} assert_same(h, h.rehash) assert_predicate(h, :empty?) h = {} (1..17).each{h[_1] = _1 * 2} (2..16).each{h.delete(_1)} assert_same(h, h.rehash) assert_equal([[1, 2], [17, 34]], h.to_a) assert_equal(2, h.size) [1, 17].each{assert_equal(_1 * 2, h[_1])} end assert('Hash#assoc, Hash#rassoc') do h = {foo: 0, bar: 1, baz: 2} assert_equal([:bar, 1], h.assoc(:bar)) assert_nil(h.assoc(:quux)) assert_equal([:foo, 0], h.rassoc(0)) assert_nil(h.rassoc(4)) end assert('#== receiver should be specified value') do [ar_entries, ht_entries].each do |entries| h = entries.hash_for v0 = HashKey[-99] h[-99] = v0 v1 = HashKey[-3, error: :==] %i[has_value? value?].each{|m| assert_raise{h.__send__(m, v1)}} v0.error = :== v1.error = false %i[has_value? value?].each{|m| assert_nothing_raised{h.__send__(m, v1)}} end end assert('test value omission') do x = 1 y = 2 assert_equal({x:1, y:2}, {x:, y:}) end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/superclass.rb0000644000000000000000000000013115077107276021625 xustar0030 mtime=1761382078.141420469 30 atime=1761382080.164411215 29 ctime=1761382108.53330202 nghttp2-1.68.0/third-party/mruby/test/t/superclass.rb0000644000175100017510000000346115077107276022222 0ustar00runnerrunner[ # [:Object, :implementation_defined_value, '15.2.2.1'], [:Module, :Object, '15.2.2.2'], [:Class, :Module, '15.2.3.2'], [:NilClass, :Object, '15.2.4.2'], [:TrueClass, :Object, '15.2.5.2'], [:FalseClass, :Object, '15.2.6.2'], [:Numeric, :Object, '15.2.7.2'], [:Integer, :Numeric, '15.2.8.2'], [:Float, :Numeric, '15.2.9.2'], [:String, :Object, '15.2.10.2'], [:Symbol, :Object, '15.2.11.2'], [:Array, :Object, '15.2.12.2'], [:Hash, :Object, '15.2.13.2'], [:Range, :Object, '15.2.14.2'], # [:Regexp, :Object, '15.2.15.2'], #No Regexp in mruby core # [:MatchData, :Object, '15.2.16.2'], [:Proc, :Object, '15.2.17.2'], # [:Struct, :Object, '15.2.18.2'], # [:Time, :Object, '15.2.19.2'], # [:IO, :Object, '15.2.20.2'], # [:File, :IO, '15.2.21.2'], [:Exception, :Object, '15.2.22.2'], [:StandardError, :Exception, '15.2.23.2'], [:ArgumentError, :StandardError, '15.2.24.2'], [:LocalJumpError, :StandardError, '15.2.25.2'], [:RangeError, :StandardError, '15.2.26.2'], [:RegexpError, :StandardError, '15.2.27.2'], [:RuntimeError, :StandardError, '15.2.28.2'], [:TypeError, :StandardError, '15.2.29.2'], [:ZeroDivisionError, :StandardError, '15.2.30.2'], [:NameError, :StandardError, '15.2.31.2'], [:NoMethodError, :NameError, '15.2.32.2'], [:IndexError, :StandardError, '15.2.33.2'], # [:IOError, :StandardError, '15.2.34.2'], # [:EOFError, :IOError, '15.2.35.2'], # [:SystemCallError, :StandardError, '15.2.36.2'], [:ScriptError, :Exception, '15.2.37.2'], [:SyntaxError, :ScriptError, '15.2.38.2'], # [:LoadError, :ScriptError, '15.2.39,2'], ].each do |cls, super_cls, iso| assert "Direct superclass of #{cls}", iso do skip "#{cls} isn't defined" unless Object.const_defined? cls assert_equal Object.const_get(super_cls), Object.const_get(cls).superclass end end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/basicobject.rb0000644000000000000000000000013215077107276021712 xustar0030 mtime=1761382078.139420479 30 atime=1761382080.162411224 30 ctime=1761382108.515302072 nghttp2-1.68.0/third-party/mruby/test/t/basicobject.rb0000644000175100017510000000024615077107276022304 0ustar00runnerrunner## # BasicObject assert('BasicObject') do assert_equal(Class, BasicObject.class) end assert('BasicObject superclass') do assert_nil(BasicObject.superclass) end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/runtimeerror.rb0000644000000000000000000000013215077107276022177 xustar0030 mtime=1761382078.140420474 30 atime=1761382080.164411215 30 ctime=1761382108.509302089 nghttp2-1.68.0/third-party/mruby/test/t/runtimeerror.rb0000644000175100017510000000015615077107276022571 0ustar00runnerrunner## # RuntimeError ISO Test assert('RuntimeError', '15.2.28') do assert_equal Class, RuntimeError.class end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/iterations.rb0000644000000000000000000000013115077107276021622 xustar0030 mtime=1761382078.139420479 29 atime=1761382080.16341122 30 ctime=1761382108.490302144 nghttp2-1.68.0/third-party/mruby/test/t/iterations.rb0000644000175100017510000000167315077107276022222 0ustar00runnerrunnerassert('while expression', '11.5.2.3.2') do idx = 10 all = [] res = while idx > 0 all << idx idx -= 1 end assert_equal nil, res assert_equal [10,9,8,7,6,5,4,3,2,1], all end assert('until expression', '11.5.2.3.3') do idx = 10 all = [] res = until idx == 0 all << idx idx -= 1 end assert_equal nil, res assert_equal [10,9,8,7,6,5,4,3,2,1], all end assert('break expression', '11.5.2.4.3') do assert_equal :result do while true break :result end end assert_equal :result do until false break :result end end end assert('next expression', '11.5.2.4.4') do assert_equal [8,6,4,2,0] do all = [] idx = 10 while idx > 0 idx -= 1 next if (idx % 2) == 1 all << idx end all end assert_equal [8,6,4,2,0] do all = [] idx = 10 until idx == 0 idx -= 1 next if (idx % 2) == 1 all << idx end all end end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/gc.rb0000644000000000000000000000013115077107276020032 xustar0030 mtime=1761382078.139420479 29 atime=1761382080.16341122 30 ctime=1761382108.510302087 nghttp2-1.68.0/third-party/mruby/test/t/gc.rb0000644000175100017510000000150115077107276020420 0ustar00runnerrunner# Not ISO specified assert('GC.enable') do assert_false GC.disable assert_true GC.enable assert_false GC.enable end assert('GC.disable') do begin assert_false GC.disable assert_true GC.disable ensure GC.enable end end assert('GC.interval_ratio=') do origin = GC.interval_ratio begin assert_equal 150, (GC.interval_ratio = 150) ensure GC.interval_ratio = origin end end assert('GC.step_ratio=') do origin = GC.step_ratio begin assert_equal 150, (GC.step_ratio = 150) ensure GC.step_ratio = origin end end assert('GC.generational_mode=') do origin = GC.generational_mode begin assert_false (GC.generational_mode = false) assert_true (GC.generational_mode = true) assert_true (GC.generational_mode = true) ensure GC.generational_mode = origin end end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/true.rb0000644000000000000000000000013215077107276020421 xustar0030 mtime=1761382078.141420469 30 atime=1761382080.164411215 30 ctime=1761382108.491302142 nghttp2-1.68.0/third-party/mruby/test/t/true.rb0000644000175100017510000000116215077107276021011 0ustar00runnerrunner## # TrueClass ISO Test assert('TrueClass', '15.2.5') do assert_equal Class, TrueClass.class end assert('TrueClass true', '15.2.5.1') do assert_true true assert_equal TrueClass, true.class assert_false TrueClass.method_defined? :new end assert('TrueClass#&', '15.2.5.3.1') do assert_true true.&(true) assert_false true.&(false) end assert('TrueClass#^', '15.2.5.3.2') do assert_false true.^(true) assert_true true.^(false) end assert('TrueClass#to_s', '15.2.5.3.3') do assert_equal 'true', true.to_s end assert('TrueClass#|', '15.2.5.3.4') do assert_true true.|(true) assert_true true.|(false) end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/array.rb0000644000000000000000000000013215077107276020560 xustar0030 mtime=1761382078.139420479 30 atime=1761382080.162411224 30 ctime=1761382108.511302084 nghttp2-1.68.0/third-party/mruby/test/t/array.rb0000644000175100017510000002772715077107276021167 0ustar00runnerrunner## # Array ISO Test assert('Array', '15.2.12') do assert_equal(Class, Array.class) end assert('Array included modules', '15.2.12.3') do assert_true(Array.include?(Enumerable)) end assert('Array.[]', '15.2.12.4.1') do assert_equal([1, 2, 3], Array.[](1,2,3)) end class SubArray < Array end assert('SubArray.[]') do a = SubArray[1, 2, 3] assert_equal(SubArray, a.class) end assert('Array#+', '15.2.12.5.1') do assert_equal([1, 1], [1].+([1])) end assert('Array#*', '15.2.12.5.2') do assert_raise(ArgumentError) do # this will cause an exception due to the wrong argument [1].*(-1) end assert_equal([1, 1, 1], [1].*(3)) assert_equal([], [1].*(0)) assert_equal('abc', ['a', 'b', 'c'].*('')) assert_equal('0, 0, 1, {foo: 0}', [0, [0, 1], {foo: 0}].*(', ')) end assert('Array#<<', '15.2.12.5.3') do assert_equal([1, 1], [1].<<(1)) end assert('Array#[]', '15.2.12.5.4') do a = Array.new assert_raise(ArgumentError) do # this will cause an exception due to the wrong arguments a.[]() end assert_raise(ArgumentError) do # this will cause an exception due to the wrong arguments a.[](1,2,3) end assert_equal(2, [1,2,3].[](1)) assert_equal(nil, [1,2,3].[](4)) assert_equal(3, [1,2,3].[](-1)) assert_equal(nil, [1,2,3].[](-4)) a = [ "a", "b", "c", "d", "e" ] assert_equal(["b", "c"], a[1,2]) assert_equal(["b", "c", "d"], a[1..-2]) assert_equal(["b", "c", "d", "e"], a[1..]) assert_equal(["a", "b", "c"], a[..2]) skip unless Object.const_defined?(:Float) assert_equal("b", a[1.1]) end assert('Array#[]=', '15.2.12.5.5') do a = Array.new assert_raise(ArgumentError) do # this will cause an exception due to the wrong arguments a.[]=() end assert_raise(ArgumentError) do # this will cause an exception due to the wrong arguments a.[]=(1,2,3,4) end assert_raise(IndexError) do # this will cause an exception due to the wrong arguments a = [1,2,3,4,5] a[1, -1] = 10 end assert_equal(4, [1,2,3].[]=(1,4)) assert_equal(3, [1,2,3].[]=(1,2,3)) a = [1,2,3,4,5] a[3..-1] = 6 assert_equal([1,2,3,6], a) a = [1,2,3,4,5] a[3..-1] = [] assert_equal([1,2,3], a) a = [1,2,3,4,5] a[2...4] = 6 assert_equal([1,2,6,5], a) a = [1,2,3,4,5] a[2...] = 6 assert_equal([1,2,6], a) # passing self (#3274) a = [1,2,3] a[1,0] = a assert_equal([1,1,2,3,2,3], a) a = [1,2,3] a[-1,0] = a assert_equal([1,2,1,2,3,3], a) end assert('Array#clear', '15.2.12.5.6') do a = [1] a.clear assert_equal([], a) end assert('Array#collect!', '15.2.12.5.7') do a = [1,2,3] a.collect! { |i| i + i } assert_equal([2,4,6], a) end assert('Array#concat', '15.2.12.5.8') do assert_equal([1,2,3,4], [1, 2].concat([3, 4])) # passing self (#3302) a = [1,2,3] a.concat(a) assert_equal([1,2,3,1,2,3], a) end assert('Array#delete_at', '15.2.12.5.9') do a = [1,2,3] assert_equal(2, a.delete_at(1)) assert_equal([1,3], a) assert_equal(nil, a.delete_at(3)) assert_equal([1,3], a) assert_equal(nil, a.delete_at(-3)) assert_equal([1,3], a) assert_equal(3, a.delete_at(-1)) assert_equal([1], a) end assert('Array#each', '15.2.12.5.10') do a = [1,2,3] b = 0 a.each {|i| b += i} assert_equal(6, b) end assert('Array#each_index', '15.2.12.5.11') do a = [1] b = nil a.each_index {|i| b = i} assert_equal(0, b) end assert('Array#empty?', '15.2.12.5.12') do a = [] b = [b] assert_true([].empty?) assert_false([1].empty?) end assert('Array#first', '15.2.12.5.13') do assert_raise(ArgumentError) do # this will cause an exception due to the wrong argument [1,2,3].first(-1) end assert_raise(ArgumentError) do # this will cause an exception due to the wrong argument [1,2,3].first(1,2) end assert_nil([].first) b = [1,2,3] assert_equal(1, b.first) assert_equal([], b.first(0)) assert_equal([1], b.first(1)) assert_equal([1,2,3], b.first(4)) end assert('Array#index', '15.2.12.5.14') do a = [1,2,3] assert_equal(1, a.index(2)) assert_equal(nil, a.index(0)) end assert("Array#index (block)") do assert_nil (1..10).to_a.index { |i| i % 5 == 0 and i % 7 == 0 } assert_equal 34, (1..100).to_a.index { |i| i % 5 == 0 and i % 7 == 0 } end assert('Array#initialize', '15.2.12.5.15') do a = [].__send__(:initialize,1) b = [].__send__(:initialize,2) c = [].__send__(:initialize,2, 1) d = [].__send__(:initialize,2) {|i| i} assert_equal([nil], a) assert_equal([nil,nil], b) assert_equal([1,1], c) assert_equal([0,1], d) end assert('Array#initialize_copy', '15.2.12.5.16') do a = [1,2,3] b = [].initialize_copy(a) assert_equal([1,2,3], b) end assert('Array#join', '15.2.12.5.17') do a = [1,2,3].join b = [1,2,3].join(',') assert_equal('123', a) assert_equal('1,2,3', b) end assert('Array#last', '15.2.12.5.18') do assert_raise(ArgumentError) do # this will cause an exception due to the wrong argument [1,2,3].last(-1) end a = [1,2,3] assert_equal(3, a.last) assert_nil([].last) end assert('Array#length', '15.2.12.5.19') do a = [1,2,3] assert_equal(3, a.length) end assert('Array#map!', '15.2.12.5.20') do a = [1,2,3] a.map! { |i| i + i } assert_equal([2,4,6], a) end assert('Array#pop', '15.2.12.5.21') do a = [1,2,3] b = a.pop assert_nil([].pop) assert_equal([1,2], a) assert_equal(3, b) assert_raise(FrozenError) { [].freeze.pop } end assert('Array#push', '15.2.12.5.22') do a = [1,2,3] b = a.push(4) assert_equal([1,2,3,4], a) assert_equal([1,2,3,4], b) end assert('Array#replace', '15.2.12.5.23') do a = [1,2,3] b = [].replace(a) assert_equal([1,2,3], b) end assert('Array#reverse', '15.2.12.5.24') do a = [1,2,3] b = a.reverse assert_equal([1,2,3], a) assert_equal([3,2,1], b) end assert('Array#reverse!', '15.2.12.5.25') do a = [1,2,3] b = a.reverse! assert_equal([3,2,1], a) assert_equal([3,2,1], b) end assert('Array#rindex', '15.2.12.5.26') do a = [1,2,3] assert_equal(1, a.rindex(2)) assert_equal(nil, a.rindex(0)) end assert("Array#rindex (block)") do assert_nil (1..10).to_a.rindex { |i| i % 5 == 0 and i % 7 == 0 } assert_equal 69, (1..100).to_a.rindex { |i| i % 5 == 0 and i % 7 == 0 } end assert('Array#shift', '15.2.12.5.27') do a = [1,2,3] b = a.shift assert_nil([].shift) assert_equal([2,3], a) assert_equal(1, b) assert_raise(FrozenError) { [].freeze.shift } # Array#shift with argument assert_equal([], [].shift(1)) a = [1,2,3] b = a.shift(1) assert_equal([2,3], a) assert_equal([1], b) a = [1,2,3,4] b = a.shift(3) assert_equal([4], a) assert_equal([1,2,3], b) a = [1,2,3] b = a.shift(4) assert_equal([], a) assert_equal([1,2,3], b) end assert('Array#size', '15.2.12.5.28') do a = [1,2,3] assert_equal(3, a.size) end assert('Array#slice', '15.2.12.5.29') do a = [*(1..100)] b = a.dup assert_equal(1, a.slice(0)) assert_equal(100, a.slice(99)) assert_nil(a.slice(100)) assert_equal(100, a.slice(-1)) assert_equal(99, a.slice(-2)) assert_equal(1, a.slice(-100)) assert_nil(a.slice(-101)) assert_equal([1], a.slice(0,1)) assert_equal([100], a.slice(99,1)) assert_equal([], a.slice(100,1)) assert_equal([100], a.slice(99,100)) assert_equal([100], a.slice(-1,1)) assert_equal([99], a.slice(-2,1)) assert_equal([10, 11, 12], a.slice(9, 3)) assert_equal([10, 11, 12], a.slice(-91, 3)) assert_nil(a.slice(-101, 2)) assert_equal([1], a.slice(0..0)) assert_equal([100], a.slice(99..99)) assert_equal([], a.slice(100..100)) assert_equal([100], a.slice(99..200)) assert_equal([100], a.slice(-1..-1)) assert_equal([99], a.slice(-2..-2)) assert_equal([10, 11, 12], a.slice(9..11)) assert_equal([10, 11, 12], a.slice(-91..-89)) assert_equal([10, 11, 12], a.slice(-91..-89)) assert_nil(a.slice(-101..-1)) assert_nil(a.slice(10, -3)) assert_equal([], a.slice(10..7)) assert_equal(b, a) end assert('Array#unshift', '15.2.12.5.30') do a = [2,3] b = a.unshift(1) c = [2,3] d = c.unshift(0, 1) assert_equal([1,2,3], a) assert_equal([1,2,3], b) assert_equal([0,1,2,3], c) assert_equal([0,1,2,3], d) end assert('Array#to_s', '15.2.12.5.31 / 15.2.12.5.32') do a = [2, 3, 4, 5] a[4] = a r1 = a.to_s r2 = a.inspect assert_equal(r2, r1) assert_equal("[2, 3, 4, 5, [...]]", r1) end assert('Array#==', '15.2.12.5.33') do assert_false(["a", "c"] == ["a", "c", 7]) assert_true(["a", "c", 7] == ["a", "c", 7]) assert_false(["a", "c", 7] == ["a", "d", "f"]) end assert('Array#eql?', '15.2.12.5.34') do a1 = [ 1, 2, 3 ] a2 = [ 1, 2, 3 ] a3 = [ 1.0, 2.0, 3.0 ] assert_true(a1.eql? a2) assert_false(a1.eql? a3) end assert('Array#hash', '15.2.12.5.35') do a = [ 1, 2, 3 ] assert_true(a.hash.is_a? Integer) assert_equal([1,2].hash, [1,2].hash) end assert('Array#<=>', '15.2.12.5.36') do r1 = [ "a", "a", "c" ] <=> [ "a", "b", "c" ] #=> -1 r2 = [ 1, 2, 3, 4, 5, 6 ] <=> [ 1, 2 ] #=> +1 r3 = [ "a", "b", "c" ] <=> [ "a", "b", "c" ] #=> 0 assert_equal(-1, r1) assert_equal(+1, r2) assert_equal(0, r3) end # Not ISO specified assert("Array (Longish inline array)") do ary = [[0, 0], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6, 6], [7, 7], [8, 8], [9, 9], [10, 10], [11, 11], [12, 12], [13, 13], [14, 14], [15, 15], [16, 16], [17, 17], [18, 18], [19, 19], [20, 20], [21, 21], [22, 22], [23, 23], [24, 24], [25, 25], [26, 26], [27, 27], [28, 28], [29, 29], [30, 30], [31, 31], [32, 32], [33, 33], [34, 34], [35, 35], [36, 36], [37, 37], [38, 38], [39, 39], [40, 40], [41, 41], [42, 42], [43, 43], [44, 44], [45, 45], [46, 46], [47, 47], [48, 48], [49, 49], [50, 50], [51, 51], [52, 52], [53, 53], [54, 54], [55, 55], [56, 56], [57, 57], [58, 58], [59, 59], [60, 60], [61, 61], [62, 62], [63, 63], [64, 64], [65, 65], [66, 66], [67, 67], [68, 68], [69, 69], [70, 70], [71, 71], [72, 72], [73, 73], [74, 74], [75, 75], [76, 76], [77, 77], [78, 78], [79, 79], [80, 80], [81, 81], [82, 82], [83, 83], [84, 84], [85, 85], [86, 86], [87, 87], [88, 88], [89, 89], [90, 90], [91, 91], [92, 92], [93, 93], [94, 94], [95, 95], [96, 96], [97, 97], [98, 98], [99, 99], [100, 100], [101, 101], [102, 102], [103, 103], [104, 104], [105, 105], [106, 106], [107, 107], [108, 108], [109, 109], [110, 110], [111, 111], [112, 112], [113, 113], [114, 114], [115, 115], [116, 116], [117, 117], [118, 118], [119, 119], [120, 120], [121, 121], [122, 122], [123, 123], [124, 124], [125, 125], [126, 126], [127, 127], [128, 128], [129, 129], [130, 130], [131, 131], [132, 132], [133, 133], [134, 134], [135, 135], [136, 136], [137, 137], [138, 138], [139, 139], [140, 140], [141, 141], [142, 142], [143, 143], [144, 144], [145, 145], [146, 146], [147, 147], [148, 148], [149, 149], [150, 150], [151, 151], [152, 152], [153, 153], [154, 154], [155, 155], [156, 156], [157, 157], [158, 158], [159, 159], [160, 160], [161, 161], [162, 162], [163, 163], [164, 164], [165, 165], [166, 166], [167, 167], [168, 168], [169, 169], [170, 170], [171, 171], [172, 172], [173, 173], [174, 174], [175, 175], [176, 176], [177, 177], [178, 178], [179, 179], [180, 180], [181, 181], [182, 182], [183, 183], [184, 184], [185, 185], [186, 186], [187, 187], [188, 188], [189, 189], [190, 190], [191, 191], [192, 192], [193, 193], [194, 194], [195, 195], [196, 196], [197, 197], [198, 198], [199, 199]] h = Hash.new(0) ary.each {|p| h[p.class] += 1} assert_equal({Array=>200}, h) end assert("Array#rindex") do class Sneaky def ==(*) $a.clear $a.replace([1]) false end end $a = [2, 3, 4, 5, 6, 7, 8, 9, 10, Sneaky.new] assert_equal 0, $a.rindex(1) end assert('Array#sort!') do a = [3, 2, 1] assert_equal a, a.sort! # sort! returns self. assert_equal [1, 2, 3], a # it is sorted. end assert('Array#freeze') do a = [].freeze assert_raise(FrozenError) do a[0] = 1 end end assert('Array#delete') do a = ["a", "b", "c"] assert_equal nil, a.delete("x") assert_equal "x", a.delete("x") { _1 } assert_equal ["a", "b", "c"], a assert_equal "a", a.delete("a") assert_equal ["b", "c"], a a = [nil] assert_equal nil, a.delete(nil) { "?" } assert_equal [], a end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/regexperror.rb0000644000000000000000000000013215077107276022006 xustar0030 mtime=1761382078.140420474 30 atime=1761382080.164411215 30 ctime=1761382108.487302153 nghttp2-1.68.0/third-party/mruby/test/t/regexperror.rb0000644000175100017510000000012115077107276022370 0ustar00runnerrunner## # RegexpError ISO Test # TODO broken ATM assert('RegexpError', '15.2.27') do nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/enumerable.rb0000644000000000000000000000013115077107276021560 xustar0030 mtime=1761382078.139420479 29 atime=1761382080.16341122 30 ctime=1761382108.480302173 nghttp2-1.68.0/third-party/mruby/test/t/enumerable.rb0000644000175100017510000000602515077107276022154 0ustar00runnerrunner## # Enumerable ISO Test assert('Enumerable', '15.3.2') do assert_equal(Module, Enumerable.class) end assert('Enumerable#all?', '15.3.2.2.1') do assert_true([1,2,3].all?) assert_false([1,false,3].all?) a = [2,4,6] all = a.all? do |e| e % 2 == 0 end assert_true(all) a = [2,4,7] all = a.all? do |e| e % 2 == 0 end assert_false(all) end assert('Enumerable#any?', '15.3.2.2.2') do assert_true([false,true,false].any?) assert_false([false,false,false].any?) a = [1,3,6] any = a.any? do |e| e % 2 == 0 end assert_true(any) a = [1,3,5] any = a.any? do |e| e % 2 == 0 end assert_false(any) end assert('Enumerable#collect', '15.3.2.2.3') do assert_true [1,2,3].collect { |i| i + i } == [2,4,6] end assert('Enumerable#detect', '15.3.2.2.4') do assert_equal 1, [1,2,3].detect() { true } assert_equal 'a', [1,2,3].detect(->{"a"}) { false } end assert('Array#each_with_index', '15.3.2.2.5') do a = nil b = nil [1].each_with_index {|e,i| a = e; b = i} assert_equal(1, a) assert_equal(0, b) end assert('Enumerable#entries', '15.3.2.2.6') do assert_equal([1], [1].entries) end assert('Enumerable#find', '15.3.2.2.7') do assert_equal 1, [1,2,3].find() { true } assert_equal 'a', [1,2,3].find(->{"a"}) { false } end assert('Enumerable#find_all', '15.3.2.2.8') do assert_equal [2,4,6,8], [1,2,3,4,5,6,7,8,9].find_all() {|i| i%2 == 0} end assert('Enumerable#grep', '15.3.2.2.9') do assert_equal [4,5,6], [1,2,3,4,5,6,7,8,9].grep(4..6) end assert('Enumerable#include?', '15.3.2.2.10') do assert_true [1,2,3,4,5,6,7,8,9].include?(5) assert_false [1,2,3,4,5,6,7,8,9].include?(0) end assert('Enumerable#inject', '15.3.2.2.11') do assert_equal 21, [1,2,3,4,5,6].inject() {|s, n| s + n} assert_equal 22, [1,2,3,4,5,6].inject(1) {|s, n| s + n} end assert('Enumerable#map', '15.3.2.2.12') do assert_equal [2,4,6], [1,2,3].map { |i| i + i } end assert('Enumerable#max', '15.3.2.2.13') do a = ['aaa', 'bb', 'c'] assert_equal 'c', a.max assert_equal 'aaa', a.max {|i1,i2| i1.length <=> i2.length} end assert('Enumerable#min', '15.3.2.2.14') do a = ['aaa', 'bb', 'c'] assert_equal 'aaa', a.min assert_equal 'c', a.min {|i1,i2| i1.length <=> i2.length} end assert('Enumerable#member?', '15.3.2.2.15') do assert_true [1,2,3,4,5,6,7,8,9].member?(5) assert_false [1,2,3,4,5,6,7,8,9].member?(0) end assert('Enumerable#partition', '15.3.2.2.16') do partition = [0,1,2,3,4,5,6,7,8,9].partition do |i| i % 2 == 0 end assert_equal [[0,2,4,6,8], [1,3,5,7,9]], partition end assert('Enumerable#reject', '15.3.2.2.17') do reject = [0,1,2,3,4,5,6,7,8,9].reject do |i| i % 2 == 0 end assert_equal [1,3,5,7,9], reject end assert('Enumerable#select', '15.3.2.2.18') do assert_equal [2,4,6,8], [1,2,3,4,5,6,7,8,9].select() {|i| i%2 == 0} end assert('Enumerable#sort', '15.3.2.2.19') do assert_equal [1,2,3,4,6,7], [7,3,1,2,6,4].sort assert_equal [7,6,4,3,2,1], [7,3,1,2,6,4].sort {|e1,e2|e2<=>e1} end assert('Enumerable#to_a', '15.3.2.2.20') do assert_equal [1], [1].to_a end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/localjumperror.rb0000644000000000000000000000013115077107276022501 xustar0030 mtime=1761382078.140420474 29 atime=1761382080.16341122 30 ctime=1761382108.497302124 nghttp2-1.68.0/third-party/mruby/test/t/localjumperror.rb0000644000175100017510000000047515077107276023100 0ustar00runnerrunner## # LocalJumpError ISO Test assert('LocalJumpError', '15.2.25') do assert_equal Class, LocalJumpError.class # assert_raise LocalJumpError do # # this will cause an exception due to the wrong location # retry # end end # TODO 15.2.25.2.1 LocalJumpError#exit_value # TODO 15.2.25.2.2 LocalJumpError#reason nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/symbol.rb0000644000000000000000000000013215077107276020747 xustar0030 mtime=1761382078.141420469 30 atime=1761382080.164411215 30 ctime=1761382108.524302046 nghttp2-1.68.0/third-party/mruby/test/t/symbol.rb0000644000175100017510000000105415077107276021337 0ustar00runnerrunner## # Symbol ISO Test assert('Symbol') do assert_equal :"a", :a assert_equal :"a#{1}", :a1 assert_equal :'a', :a assert_equal :'a#{1}', :"a\#{1}" end assert('Symbol', '15.2.11') do assert_equal Class, Symbol.class end assert('Symbol#===', '15.2.11.3.1') do assert_true :abc === :abc assert_false :abc === :cba end assert('Symbol#to_s', '15.2.11.3.3') do assert_equal 'abc', :abc.to_s end assert('Symbol#to_sym', '15.2.11.3.4') do assert_equal :abc, :abc.to_sym end assert('Symbol#to_proc') do assert_equal 5, :abs.to_proc[-5] end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/comparable.rb0000644000000000000000000000013115077107276021546 xustar0030 mtime=1761382078.139420479 30 atime=1761382080.162411224 29 ctime=1761382108.48130217 nghttp2-1.68.0/third-party/mruby/test/t/comparable.rb0000644000175100017510000000266715077107276022152 0ustar00runnerrunnerassert('Comparable#<', '15.3.3.2.1') do class Foo include Comparable def <=>(x) x end end assert_false(Foo.new < 0) assert_false(Foo.new < 1) assert_true(Foo.new < -1) assert_raise(ArgumentError){ Foo.new < nil } end assert('Comparable#<=', '15.3.3.2.2') do class Foo include Comparable def <=>(x) x end end assert_true(Foo.new <= 0) assert_false(Foo.new <= 1) assert_true(Foo.new <= -1) assert_raise(ArgumentError){ Foo.new <= nil } end assert('Comparable#==', '15.3.3.2.3') do class Foo include Comparable def <=>(x) 0 end end assert_true(Foo.new == Foo.new) end assert('Comparable#>', '15.3.3.2.4') do class Foo include Comparable def <=>(x) x end end assert_false(Foo.new > 0) assert_true(Foo.new > 1) assert_false(Foo.new > -1) assert_raise(ArgumentError){ Foo.new > nil } end assert('Comparable#>=', '15.3.3.2.5') do class Foo include Comparable def <=>(x) x end end assert_true(Foo.new >= 0) assert_true(Foo.new >= 1) assert_false(Foo.new >= -1) assert_raise(ArgumentError){ Foo.new >= nil } end assert('Comparable#between?', '15.3.3.2.6') do class Foo include Comparable def <=>(x) x end end c = Foo.new assert_false(c.between?(-1, 1)) assert_false(c.between?(-1, -1)) assert_false(c.between?( 1, 1)) assert_true(c.between?( 1, -1)) assert_true(c.between?(0, 0)) end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/string.rb0000644000000000000000000000013215077107276020750 xustar0030 mtime=1761382078.140420474 30 atime=1761382080.164411215 30 ctime=1761382108.513302078 nghttp2-1.68.0/third-party/mruby/test/t/string.rb0000644000175100017510000006036115077107276021346 0ustar00runnerrunner## # String ISO Test UTF8STRING = __ENCODING__ == "UTF-8" assert('String', '15.2.10') do assert_equal Class, String.class end assert('String#<=>', '15.2.10.5.1') do a = '' <=> '' b = '' <=> 'not empty' c = 'not empty' <=> '' d = 'abc' <=> 'cba' e = 'cba' <=> 'abc' assert_equal 0, a assert_equal(-1, b) assert_equal 1, c assert_equal(-1, d) assert_equal 1, e assert_nil 'a' <=> 1024 end assert('String#==', '15.2.10.5.2') do assert_equal 'abc', 'abc' assert_not_equal 'abc', 'cba' end # 'String#=~', '15.2.10.5.3' will be tested in mrbgems. assert('String#+', '15.2.10.5.4') do assert_equal 'ab', 'a' + 'b' end assert('String#*', '15.2.10.5.5') do assert_equal 'aaaaa', 'a' * 5 assert_equal '', 'a' * 0 assert_raise(ArgumentError) { 'a' * -1 } assert_raise(TypeError) { 'a' * '1' } assert_raise(TypeError) { 'a' * nil } skip unless Object.const_defined?(:Float) assert_equal 'aa', 'a' * 2.1 assert_raise(RangeError) { '' * 1e30 } assert_raise(RangeError) { '' * Float::INFINITY } assert_raise(RangeError) { '' * Float::NAN } end assert('String#[]', '15.2.10.5.6') do # length of args is 1 assert_equal 'a', 'abc'[0] assert_equal 'c', 'abc'[-1] assert_nil 'abc'[10] assert_nil 'abc'[-10] assert_equal 'b', 'abc'[1.1] if Object.const_defined?(:Float) # length of args is 2 assert_nil 'abc'[0, -1] assert_nil 'abc'[10, 0] assert_nil 'abc'[-10, 0] assert_equal '', 'abc'[0, 0] assert_equal 'bc', 'abc'[1, 2] # args is String assert_equal 'bc', 'abc'['bc'] assert_nil 'abc'['XX'] assert_raise(TypeError) { 'abc'[nil] } end assert('String#[](UTF-8)', '15.2.10.5.6') do assert_equal "ã¡", "ã“ã‚“ã«ã¡ã¯ä¸–界"[3] assert_equal nil, "ã“ã‚“ã«ã¡ã¯ä¸–界"[20] assert_equal "世", "ã“ã‚“ã«ã¡ã¯ä¸–界"[-2] assert_equal "世界", "ã“ã‚“ã«ã¡ã¯ä¸–界"[-2..-1] assert_equal "ã‚“ã«", "ã“ã‚“ã«ã¡ã¯ä¸–界"[1,2] assert_equal "世", "ã“ã‚“ã«ã¡ã¯ä¸–界"["世"] end if UTF8STRING assert('String#[] with Range') do a1 = 'abc'[1..0] b1 = 'abc'[1..1] c1 = 'abc'[1..2] d1 = 'abc'[1..3] e1 = 'abc'[1..4] f1 = 'abc'[0..-2] g1 = 'abc'[-2..3] h1 = 'abc'[3..4] i1 = 'abc'[4..5] j1 = 'abcdefghijklmnopqrstuvwxyz'[1..3] k1 = 'abcdefghijklmnopqrstuvwxyz'[-3..] a2 = 'abc'[1...0] b2 = 'abc'[1...1] c2 = 'abc'[1...2] d2 = 'abc'[1...3] e2 = 'abc'[1...4] f2 = 'abc'[0...-2] g2 = 'abc'[-2...3] h2 = 'abc'[3...4] i2 = 'abc'[4...5] j2 = 'abcdefghijklmnopqrstuvwxyz'[1...3] k2 = 'abcdefghijklmnopqrstuvwxyz'[-3...] assert_equal '', a1 assert_equal 'b', b1 assert_equal 'bc', c1 assert_equal 'bc', d1 assert_equal 'bc', e1 assert_equal 'ab', f1 assert_equal 'bc', g1 assert_equal '', h1 assert_nil i2 assert_equal 'bcd', j1 assert_equal 'xyz', k1 assert_equal '', a2 assert_equal '', b2 assert_equal 'b', c2 assert_equal 'bc', d2 assert_equal 'bc', e2 assert_equal 'a', f2 assert_equal 'bc', g2 assert_equal '', h2 assert_nil i2 assert_equal 'bc', j2 assert_equal 'xyz', k2 end assert('String#[]=') do # length of args is 1 a = 'abc' assert_equal 'X', (a[0] = 'X') assert_equal 'Xbc', a b = 'abc' b[-1] = 'X' assert_equal 'abX', b c = 'abc' assert_raise(IndexError) do c[10] = 'X' end d = 'abc' assert_raise(IndexError) do d[-10] = 'X' end if Object.const_defined?(:Float) e = 'abc' e[1.1] = 'X' assert_equal 'aXc', e end f = 'abc' assert_equal 'X', f.[]=(0, 'X') assert_equal 'Xbc', f assert_raise(TypeError) { 'a'[0] = 1 } assert_raise(TypeError) { 'a'[:a] = '1' } # length of args is 2 a1 = 'abc' assert_raise(IndexError) do a1[0, -1] = 'X' end b1 = 'abc' assert_raise(IndexError) do b1[10, 0] = 'X' end c1 = 'abc' assert_raise(IndexError) do c1[-10, 0] = 'X' end d1 = 'abc' d1[0, 0] = 'X' assert_equal 'Xabc', d1 e1 = 'abc' assert_equal 'X', (e1[1, 3] = 'X') assert_equal 'aX', e1 f1 = 'abc' assert_equal 'X', f1.[]=(0, 1, 'X') assert_equal 'Xbc', f1 # args is RegExp # It will be tested in mrbgems. # args is String a3 = 'abc' assert_equal 'X', (a3['bc'] = 'X') assert_equal a3, 'aX' b3 = 'abc' assert_raise(IndexError) do b3['XX'] = 'Y' end c3 = 'abc' assert_equal 'X', c3.[]=('bc', 'X') assert_equal 'aX', c3 assert_raise(TypeError) { 'a'[:a, 0] = '1' } assert_raise(TypeError) { 'a'[0, :a] = '1' } assert_raise(TypeError) { 'a'[0, 1] = 1 } end assert('String[]=(UTF-8)') do a = "➀âžâž‚➃➄" a[3] = "⚃" assert_equal "➀âžâž‚⚃➄", a b = "➀âžâž‚➃➄" b[3, 0] = "⛄" assert_equal "➀âžâž‚⛄➃➄", b c = "➀âžâž‚➃➄" c[3, 2] = "⚃⚄" assert_equal "➀âžâž‚⚃⚄", c d = "➀âžâž‚➃➄" d[5] = "⛄" assert_equal "➀âžâž‚➃➄⛄", d e = "➀âžâž‚➃➄" e[5, 0] = "⛄" assert_equal "➀âžâž‚➃➄⛄", e f = "➀âžâž‚➃➄" f[5, 2] = "⛄" assert_equal "➀âžâž‚➃➄⛄", f g = "➀âžâž‚➃➄" assert_raise(IndexError) { g[6] = "⛄" } h = "➀âžâž‚➃➄" assert_raise(IndexError) { h[6, 0] = "⛄" } i = "➀âžâž‚➃➄" assert_raise(IndexError) { i[6, 2] = "⛄" } j = "➀âžâž‚➃➄" j["➃"] = "⚃" assert_equal "➀âžâž‚⚃➄", j k = "➀âžâž‚➃➄" assert_raise(IndexError) { k["⛄"] = "⛇" } l = "➀âžâž‚➃➄" assert_nothing_raised { l["âž‚"] = "" } assert_equal "➀âžâžƒâž„", l m = "➀âžâž‚➃➄" assert_raise(TypeError) { m["âž‚"] = nil } assert_equal "➀âžâž‚➃➄", m end if UTF8STRING assert('String#capitalize', '15.2.10.5.7') do a = 'abc' a.capitalize assert_equal 'abc', a assert_equal 'Abc', 'abc'.capitalize end assert('String#capitalize!', '15.2.10.5.8') do a = 'abc' a.capitalize! assert_equal 'Abc', a assert_equal nil, 'Abc'.capitalize! end assert('String#chomp', '15.2.10.5.9') do a = 'abc'.chomp b = ''.chomp c = "abc\n".chomp d = "abc\n\n".chomp e = "abc\t".chomp("\t") f = "abc\n" f.chomp assert_equal 'abc', a assert_equal '', b assert_equal 'abc', c assert_equal "abc\n", d assert_equal 'abc', e assert_equal "abc\n", f end assert('String#chomp!', '15.2.10.5.10') do a = 'abc' b = '' c = "abc\n" d = "abc\n\n" e = "abc\t" a.chomp! b.chomp! c.chomp! d.chomp! e.chomp!("\t") assert_equal 'abc', a assert_equal '', b assert_equal 'abc', c assert_equal "abc\n", d assert_equal 'abc', e end assert('String#chop', '15.2.10.5.11') do a = ''.chop b = 'abc'.chop c = 'abc' c.chop assert_equal '', a assert_equal 'ab', b assert_equal 'abc', c end assert('String#chop(UTF-8)', '15.2.10.5.11') do a = ''.chop b = 'ã‚ã„ã†'.chop c = "ã‚\nã„".chop.chop assert_equal '', a assert_equal 'ã‚ã„', b assert_equal 'ã‚', c end if UTF8STRING assert('String#chop!', '15.2.10.5.12') do a = '' b = 'abc' a.chop! b.chop! assert_equal a, '' assert_equal b, 'ab' end assert('String#chop!(UTF-8)', '15.2.10.5.12') do a = '' b = "ã‚ã„ã†ãˆ\n" c = "ã‚ã„ã†ãˆ\n" a.chop! b.chop! c.chop! c.chop! assert_equal a, '' assert_equal b, 'ã‚ã„ã†ãˆ' assert_equal c, 'ã‚ã„ã†' end if UTF8STRING assert('String#downcase', '15.2.10.5.13') do a = 'ABC'.downcase b = 'ABC' b.downcase assert_equal 'abc', a assert_equal 'ABC', b end assert('String#downcase!', '15.2.10.5.14') do a = 'ABC' a.downcase! assert_equal 'abc', a assert_equal nil, 'abc'.downcase! end assert('String#each_line', '15.2.10.5.15') do a = "first line\nsecond line\nthird line" list = ["first line\n", "second line\n", "third line"] n_list = [] a.each_line do |line| n_list << line end assert_equal list, n_list n_list.clear a.each_line("li") do |line| n_list << line end assert_equal ["first li", "ne\nsecond li", "ne\nthird li", "ne"], n_list end assert('String#empty?', '15.2.10.5.16') do a = '' b = 'not empty' assert_true a.empty? assert_false b.empty? end assert('String#eql?', '15.2.10.5.17') do assert_true 'abc'.eql?('abc') assert_false 'abc'.eql?('cba') end assert('String#gsub', '15.2.10.5.18') do assert_equal('aBcaBc', 'abcabc'.gsub('b', 'B'), 'gsub without block') assert_equal('aBcaBc', 'abcabc'.gsub('b'){|w| w.capitalize }, 'gsub with block') assert_equal('$a$a$', '#a#a#'.gsub('#', '$'), 'mruby/mruby#847') assert_equal('$a$a$', '#a#a#'.gsub('#'){|_w| '$' }, 'mruby/mruby#847 with block') assert_equal('$$a$$', '##a##'.gsub('##', '$$'), 'mruby/mruby#847 another case') assert_equal('$$a$$', '##a##'.gsub('##'){|_w| '$$' }, 'mruby/mruby#847 another case with block') assert_equal('A', 'a'.gsub('a', 'A')) assert_equal('A', 'a'.gsub('a'){|w| w.capitalize }) assert_equal("<><>", 'a'.gsub('a', '<\0><\1><\2>')) assert_equal(".h.e.l.l.o.", "hello".gsub("", ".")) a = [] assert_equal(".h.e.l.l.o.", "hello".gsub("") { |i| a << i; "." }) assert_equal(["", "", "", "", "", ""], a) assert_raise(ArgumentError) { "".gsub } assert_raise(ArgumentError) { "".gsub("", "", "") } end assert('String#gsub with backslash') do s = 'abXcdXef' assert_equal 'ab<\\>cd<\\>ef', s.gsub('X', '<\\\\>') assert_equal 'abcdef', s.gsub('X', '<\\&>') assert_equal 'abcdef', s.gsub('X', '<\\0>') assert_equal 'abcdef', s.gsub('X', '<\\`>') assert_equal 'abcdef', s.gsub('X', '<\\\'>') end assert('String#gsub!', '15.2.10.5.19') do a = 'abcabc' a.gsub!('b', 'B') b = 'abcabc' b.gsub!('b') { |w| w.capitalize } assert_equal 'aBcaBc', a assert_equal 'aBcaBc', b end assert('String#hash', '15.2.10.5.20') do a = 'abc' assert_equal 'abc'.hash, a.hash end assert('String#include?', '15.2.10.5.21') do assert_true 'abc'.include?('a') assert_false 'abc'.include?('d') end assert('String#index', '15.2.10.5.22') do assert_equal 0, 'abc'.index('a') assert_nil 'abc'.index('d') assert_equal 3, 'abcabc'.index('a', 1) assert_equal 5, "hello".index("", 5) assert_equal nil, "hello".index("", 6) assert_equal 3, "hello".index("l", -2) assert_raise(ArgumentError) { "hello".index } assert_raise(TypeError) { "hello".index(101) } end assert('String#index(UTF-8)', '15.2.10.5.22') do assert_equal 0, '⓿➊➋➌âžâžŽ'.index('â“¿') assert_nil '⓿➊➋➌âžâžŽ'.index('âž“') assert_equal 6, '⓿➊➋➌âžâžŽâ“¿âžŠâž‹âžŒâžâžŽ'.index('â“¿', 1) assert_equal 6, '⓿➊➋➌âžâžŽâ“¿âžŠâž‹âžŒâžâžŽ'.index('â“¿', -7) assert_equal 6, "⓿➊➋➌âžâžŽ".index("", 6) assert_equal nil, "⓿➊➋➌âžâžŽ".index("", 7) assert_equal 0, '⓿➊➋➌âžâžŽ'.index("\xe2") assert_equal nil, '⓿➊➋➌âžâžŽ'.index("\xe3") assert_equal 6, "\xd1\xd1\xd1\xd1\xd1\xd1⓿➊➋➌âžâžŽ".index('â“¿') end if UTF8STRING assert('String#initialize', '15.2.10.5.23') do a = '' a.__send__(:initialize,'abc') assert_equal 'abc', a a.__send__(:initialize,'abcdefghijklmnopqrstuvwxyz') assert_equal 'abcdefghijklmnopqrstuvwxyz', a end assert('String#initialize_copy', '15.2.10.5.24') do a = '' a.initialize_copy('abc') assert_equal 'abc', a end assert('String#intern', '15.2.10.5.25') do assert_equal :abc, 'abc'.intern end assert('String#length', '15.2.10.5.26') do assert_equal 3, 'abc'.length end # 'String#match', '15.2.10.5.27' will be tested in mrbgems. assert('String#replace', '15.2.10.5.28') do a = '' a.replace('abc') assert_equal 'abc', a assert_equal 'abc', 'cba'.replace(a) b = 'abc' * 10 c = ('cba' * 10).dup b.replace(c) c.replace(b) assert_equal c, b # shared string s = "foo" * 100 a = s[10, 90] # create shared string assert_equal("", s.replace("")) # clear assert_equal("", s) # s is cleared assert_not_equal("", a) # a should not be affected end assert('String#reverse', '15.2.10.5.29') do a = 'abc' a.reverse assert_equal 'abc', a assert_equal 'cba', 'abc'.reverse end assert('String#reverse(UTF-8)', '15.2.10.5.29') do a = 'ã“ã‚“ã«ã¡ã¯ä¸–界!' a.reverse assert_equal 'ã“ã‚“ã«ã¡ã¯ä¸–界!', a assert_equal '!界世ã¯ã¡ã«ã‚“ã“', 'ã“ã‚“ã«ã¡ã¯ä¸–界!'.reverse assert_equal 'ã‚', 'ã‚'.reverse end if UTF8STRING assert('String#reverse!', '15.2.10.5.30') do a = 'abc' a.reverse! assert_equal 'cba', a assert_equal 'cba', 'abc'.reverse! end assert('String#reverse!(UTF-8)', '15.2.10.5.30') do a = 'ã“ã‚“ã«ã¡ã¯ä¸–界!' a.reverse! assert_equal '!界世ã¯ã¡ã«ã‚“ã“', a assert_equal '!界世ã¯ã¡ã«ã‚“ã“', 'ã“ã‚“ã«ã¡ã¯ä¸–界!'.reverse! b = 'ã‚' b.reverse! assert_equal 'ã‚', b end if UTF8STRING assert('String#rindex', '15.2.10.5.31') do assert_equal 0, 'abc'.rindex('a') assert_equal 0, 'abc'.rindex('a', 3) assert_nil 'abc'.rindex('a', -4) assert_nil 'abc'.rindex('d') assert_equal 6, 'abcabc'.rindex('') assert_equal 3, 'abcabc'.rindex('a') assert_equal 0, 'abcabc'.rindex('a', 1) assert_equal 3, 'abcabc'.rindex('a', 4) assert_equal 0, 'abcabc'.rindex('a', -4) assert_raise(ArgumentError) { "hello".rindex } assert_raise(TypeError) { "hello".rindex(101) } end assert('String#rindex(UTF-8)', '15.2.10.5.31') do str = "ã“ã‚“ã«ã¡ã¯ä¸–界!\nã“ã‚“ã«ã¡ã¯ä¸–界!" assert_nil str.rindex('ã•') assert_equal 12, str.rindex('ã¡') assert_equal 3, str.rindex('ã¡', 10) assert_equal 3, str.rindex('ã¡', -6) broken = "\xf0☀\xf1â˜\xf2☂\xf3☃\xf0☀\xf1â˜\xf2☂\xf3☃" assert_nil broken.rindex("\x81") # "\x81" is a part of "â˜" ("\xe2\x98\x81") assert_equal 11, broken.rindex("â˜") assert_equal 11, broken.rindex("â˜", 12) assert_equal 11, broken.rindex("â˜", 11) assert_equal 3, broken.rindex("â˜", 10) end if UTF8STRING # assert('String#scan', '15.2.10.5.32') do # # Not implemented yet # end assert('String#size', '15.2.10.5.33') do assert_equal 3, 'abc'.size end assert('String#size(UTF-8)', '15.2.10.5.33') do str = 'ã“ã‚“ã«ã¡ã¯ä¸–界!' assert_equal 8, str.size assert_not_equal str.bytesize, str.size assert_equal 2, str[1, 2].size end if UTF8STRING assert('String#slice', '15.2.10.5.34') do # length of args is 1 a = 'abc'.slice(0) b = 'abc'.slice(-1) c = 'abc'.slice(10) d = 'abc'.slice(-10) # length of args is 2 a1 = 'abc'.slice(0, -1) b1 = 'abc'.slice(10, 0) c1 = 'abc'.slice(-10, 0) d1 = 'abc'.slice(0, 0) e1 = 'abc'.slice(1, 2) # slice of shared string e11 = e1.slice(0) # args is RegExp # It will be tested in mrbgems. # args is String a3 = 'abc'.slice('bc') b3 = 'abc'.slice('XX') assert_equal 'a', a assert_equal 'c', b assert_nil c assert_nil d assert_nil a1 assert_nil b1 assert_nil c1 assert_equal '', d1 assert_equal 'bc', e1 assert_equal 'b', e11 assert_equal 'bc', a3 assert_nil b3 end # TODO Broken ATM assert('String#split', '15.2.10.5.35') do # without RegExp behavior is actually unspecified assert_equal ['abc', 'abc', 'abc'], 'abc abc abc'.split assert_equal ["a", "b", "c", "", "d"], 'a,b,c,,d'.split(',') assert_equal ['abc', 'abc', 'abc'], 'abc abc abc'.split(nil) assert_equal ['a', 'b', 'c'], 'abc'.split("") end assert('String#split(UTF-8)', '15.2.10.5.35') do got = "ã“ã‚“ã«ã¡ã¯ä¸–界!".split('') assert_equal ['ã“', 'ã‚“', 'ã«', 'ã¡', 'ã¯', '世', '界', '!'], got got = "ã“ã‚“ã«ã¡ã¯ä¸–界!".split('ã«') assert_equal ['ã“ã‚“', 'ã¡ã¯ä¸–界!'], got end if UTF8STRING assert('String#sub', '15.2.10.5.36') do assert_equal 'aBcabc', 'abcabc'.sub('b', 'B') assert_equal 'aBcabc', 'abcabc'.sub('b') { |w| w.capitalize } assert_equal 'aa$', 'aa#'.sub('#', '$') assert_equal '.abc', "abc".sub("", ".") str = "abc" miss = str.sub("X", "Z") assert_equal str, miss assert_not_same str, miss a = [] assert_equal '.abc', "abc".sub("") { |i| a << i; "." } assert_equal [""], a end assert('String#sub with backslash') do s = 'abXcdXef' assert_equal 'ab<\\>cdXef', s.sub('X', '<\\\\>') assert_equal 'abcdXef', s.sub('X', '<\\&>') assert_equal 'abcdXef', s.sub('X', '<\\0>') assert_equal 'abcdXef', s.sub('X', '<\\`>') assert_equal 'abcdXef', s.sub('X', '<\\\'>') end assert('String#sub!', '15.2.10.5.37') do a = 'abcabc' a.sub!('b', 'B') b = 'abcabc' b.sub!('b') { |w| w.capitalize } assert_equal 'aBcabc', a assert_equal 'aBcabc', b end assert('String#to_f', '15.2.10.5.38') do assert_operator(0.0, :eql?, ''.to_f) assert_operator(123456789.0, :eql?, '123456789'.to_f) assert_operator(12345.6789, :eql?, '12345.6789'.to_f) assert_operator(0.0, :eql?, '1e-2147483648'.to_f) assert_operator(Float::INFINITY, :eql?, '1e2147483648'.to_f) assert_operator(0.0, :eql?, 'a'.to_f) assert_operator(4.0, :eql?, '4a5'.to_f) assert_operator(12.0, :eql?, '1_2__3'.to_f) assert_operator(123.0, :eql?, '1_2_3'.to_f) assert_operator(68.0, :eql?, '68_'.to_f) assert_operator(68.0, :eql?, '68._7'.to_f) assert_operator(68.7, :eql?, '68.7_'.to_f) assert_operator(68.7, :eql?, '68.7_ '.to_f) assert_operator(6.0, :eql?, '6 8.7'.to_f) assert_operator(68.0, :eql?, '68. 7'.to_f) assert_operator(0.0, :eql?, '_68'.to_f) assert_operator(0.0, :eql?, ' _68'.to_f) assert_operator(12.34, :eql?, '1_2.3_4'.to_f) assert_operator(12.3, :eql?, '1_2.3__4'.to_f) assert_operator(0.9, :eql?, '.9'.to_f) assert_operator(0.9, :eql?, "\t\r\n\f\v .9 \t\r\n\f\v".to_f) end if Object.const_defined?(:Float) assert('String#to_i', '15.2.10.5.39') do assert_operator 0, :eql?, ''.to_i assert_operator 32143, :eql?, '32143'.to_i assert_operator 10, :eql?, 'a'.to_i(16) assert_operator 4, :eql?, '100'.to_i(2) assert_operator 1_000, :eql?, '1_000'.to_i assert_operator 0, :eql?, 'a'.to_i assert_operator 4, :eql?, '4a5'.to_i assert_operator 12, :eql?, '1_2__3'.to_i assert_operator 123, :eql?, '1_2_3'.to_i assert_operator 68, :eql?, '68_'.to_i assert_operator 68, :eql?, '68_ '.to_i assert_operator 0, :eql?, '_68'.to_i assert_operator 0, :eql?, ' _68'.to_i assert_operator 68, :eql?, "\t\r\n\f\v 68 \t\r\n\f\v".to_i assert_operator 6, :eql?, ' 6 8 '.to_i end assert('String#to_s', '15.2.10.5.40') do assert_equal 'abc', 'abc'.to_s end assert('String#to_sym', '15.2.10.5.41') do assert_equal :abc, 'abc'.to_sym end assert('String#upcase', '15.2.10.5.42') do a = 'abc'.upcase b = 'abc' b.upcase assert_equal 'ABC', a assert_equal 'abc', b end assert('String#upcase!', '15.2.10.5.43') do a = 'abc' a.upcase! assert_equal 'ABC', a assert_equal nil, 'ABC'.upcase! a = 'abcdefghijklmnopqrstuvwxyz' b = a.dup a.upcase! b.upcase! assert_equal 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', b end assert('String#inspect', '15.2.10.5.46') do assert_equal "\"\\x00\"", "\0".inspect assert_equal "\"foo\"", "foo".inspect if UTF8STRING assert_equal '"ã‚‹"', "ã‚‹".inspect else assert_equal '"\xe3\x82\x8b"', "ã‚‹".inspect end # should not raise an exception - regress #1210 assert_nothing_raised do ("\1" * 100).inspect end end # Not ISO specified assert('String interpolation (mrb_str_concat for shared strings)') do a = "A" * 32 assert_equal "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:", "#{a}:" end assert('String#bytes') do str1 = "hello" bytes1 = [104, 101, 108, 108, 111] str2 = "\xFF" bytes2 = [0xFF] assert_equal bytes1, str1.bytes assert_equal bytes2, str2.bytes end assert('String#each_byte') do str1 = "hello" bytes1 = [104, 101, 108, 108, 111] bytes2 = [] str1.each_byte {|b| bytes2 << b } assert_equal bytes1, bytes2 end assert('String#freeze') do str = "hello" str.freeze assert_raise(FrozenError) { str.upcase! } end assert('String literal concatenation') do assert_equal 2, ("A" "B").size assert_equal 3, ('A' "B" 'C').size assert_equal 4, (%(A) "B#{?C}" "D").size end assert('String#getbyte') do str1 = "hello" bytes1 = [104, 101, 108, 108, 111] assert_equal bytes1[0], str1.getbyte(0) assert_equal bytes1[-1], str1.getbyte(-1) assert_equal bytes1[6], str1.getbyte(6) str2 = "\xFF" bytes2 = [0xFF] assert_equal bytes2[0], str2.getbyte(0) end assert('String#setbyte') do str1 = "hello" h = "H".getbyte(0) str1.setbyte(0, h) assert_equal(h, str1.getbyte(0)) assert_equal("Hello", str1) end assert('String#byteslice') do str1 = "hello" str2 = "\u3042ab" # "\xE3\x81\x82ab" assert_equal("h", str1.byteslice(0)) assert_equal("e", str1.byteslice(1)) assert_equal(nil, str1.byteslice(5)) assert_equal("o", str1.byteslice(-1)) assert_equal(nil, str1.byteslice(-6)) assert_equal("\xE3", str2.byteslice(0)) assert_equal("\x81", str2.byteslice(1)) assert_equal(nil, str2.byteslice(5)) assert_equal("b", str2.byteslice(-1)) assert_equal(nil, str2.byteslice(-6)) assert_equal("", str1.byteslice(0, 0)) assert_equal(str1, str1.byteslice(0, 6)) assert_equal("el", str1.byteslice(1, 2)) assert_equal("", str1.byteslice(5, 1)) assert_equal("o", str1.byteslice(-1, 6)) assert_equal(nil, str1.byteslice(-6, 1)) assert_equal(nil, str1.byteslice(0, -1)) assert_equal("", str2.byteslice(0, 0)) assert_equal(str2, str2.byteslice(0, 6)) assert_equal("\x81\x82", str2.byteslice(1, 2)) assert_equal("", str2.byteslice(5, 1)) assert_equal("b", str2.byteslice(-1, 6)) assert_equal(nil, str2.byteslice(-6, 1)) assert_equal(nil, str2.byteslice(0, -1)) assert_equal("ell", str1.byteslice(1..3)) assert_equal("el", str1.byteslice(1...3)) assert_equal("h", str1.byteslice(0..0)) assert_equal("", str1.byteslice(5..0)) assert_equal("o", str1.byteslice(4..5)) assert_equal(nil, str1.byteslice(6..0)) assert_equal("", str1.byteslice(-1..0)) assert_equal("llo", str1.byteslice(-3..5)) assert_equal("\x81\x82a", str2.byteslice(1..3)) assert_equal("\x81\x82", str2.byteslice(1...3)) assert_equal("\xE3", str2.byteslice(0..0)) assert_equal("", str2.byteslice(5..0)) assert_equal("b", str2.byteslice(4..5)) assert_equal(nil, str2.byteslice(6..0)) assert_equal("", str2.byteslice(-1..0)) assert_equal("\x82ab", str2.byteslice(-3..5)) assert_raise(ArgumentError) { str1.byteslice } assert_raise(ArgumentError) { str1.byteslice(1, 2, 3) } assert_raise(TypeError) { str1.byteslice("1") } assert_raise(TypeError) { str1.byteslice("1", 2) } assert_raise(TypeError) { str1.byteslice(1, "2") } assert_raise(TypeError) { str1.byteslice(1..2, 3) } skip unless Object.const_defined?(:Float) assert_equal("o", str1.byteslice(4.0)) assert_equal("\x82ab", str2.byteslice(2.0, 3.0)) end assert('String#bytesplice') do # range, replace (len1=len2) a = "0123456789" assert_equal "0ab3456789", a.bytesplice(1..2, "ab") # range, replace (len1>len2) a = "0123456789" assert_equal "0ab456789", a.bytesplice(1..3, "ab") # range, replace (len1len2) a = "0123456789" assert_equal "0ab456789", a.bytesplice(1, 3, "ab") # idx, len, replace (len1len2) a = "0123456789" assert_equal "0bc456789", a.bytesplice(1..3, b, 1..2) # range, replace, range (len1len2) a = "0123456789" assert_equal "0bc456789", a.bytesplice(1, 3, b, 1, 2) # idx, len, replace, idx, len (len1 e assert_equal [3, 1, 4], e.args end end end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/float.rb0000644000000000000000000000013115077107276020546 xustar0030 mtime=1761382078.139420479 29 atime=1761382080.16341122 30 ctime=1761382108.531302026 nghttp2-1.68.0/third-party/mruby/test/t/float.rb0000644000175100017510000002032515077107276021141 0ustar00runnerrunner## # Float ISO Test if Object.const_defined?(:Float) assert('Float', '15.2.9') do assert_equal Class, Float.class end assert('Float#+', '15.2.9.3.1') do a = 3.123456788 + 0.000000001 b = 3.123456789 + 1 assert_float(3.123456789, a) assert_float(4.123456789, b) assert_raise(TypeError){ 0.0+nil } assert_raise(TypeError){ 1.0+nil } end assert('Float#-', '15.2.9.3.2') do a = 3.123456790 - 0.000000001 b = 5.123456789 - 1 assert_float(3.123456789, a) assert_float(4.123456789, b) end assert('Float#*', '15.2.9.3.3') do a = 3.125 * 3.125 b = 3.125 * 1 assert_float(9.765625, a) assert_float(3.125 , b) end assert('Float#/', '15.2.9.3.4') do assert_float(1.0, 3.123456789 / 3.123456789) assert_float(3.123456789, 3.123456789 / 1) assert_float(2.875, -5.75 / -2.0) assert_float(-2.875, 5.75 / -2) assert_float(-2.875, -5.75 / 2.0) assert_float(Float::NAN, 0.0 / 0.0) assert_float(Float::NAN, -0.0 / -0.0) assert_float(Float::NAN, -0.0 / 0.0) assert_float(Float::NAN, Float::NAN / Float::NAN) assert_float(Float::NAN, Float::NAN / 0.0) assert_float(Float::NAN, Float::NAN / -0.0) assert_float(Float::NAN, Float::NAN / 2.0) assert_float(Float::NAN, Float::NAN / -2.0) assert_float(Float::NAN, 0.0 / Float::NAN) assert_float(Float::NAN, -0.0 / Float::NAN) assert_float(Float::NAN, 2.0 / Float::NAN) assert_float(Float::NAN, -2.0 / Float::NAN) assert_float(Float::NAN, Float::INFINITY / Float::INFINITY) assert_float(Float::NAN, -Float::INFINITY / Float::INFINITY) assert_float(Float::NAN, Float::INFINITY / -Float::INFINITY) assert_float(Float::NAN, -Float::INFINITY / -Float::INFINITY) assert_float(Float::INFINITY, 1.0 / 0.0) assert_float(Float::INFINITY, -1.0 / -0.0) assert_float(-Float::INFINITY, 1.0 / -0.0) assert_float(-Float::INFINITY, -1.0 / 0.0) assert_float(0.0, 1.0 / Float::INFINITY) assert_float(0.0, -1.0 / -Float::INFINITY) assert_float(-0.0, -1.0 / Float::INFINITY) assert_float(-0.0, 1.0 / -Float::INFINITY) end assert('Float#quo') do assert_float(1.0, 3.123456789.quo(3.123456789)) assert_float(3.123456789, 3.123456789.quo(1)) assert_float(2.875, -5.75.quo(-2.0)) assert_float(-2.875, 5.75.quo(-2)) assert_float(-2.875, -5.75.quo(2.0)) assert_float(Float::NAN, 0.0.quo(0.0)) assert_float(Float::NAN, -0.0.quo(-0.0)) assert_float(Float::NAN, -0.0.quo(0.0)) assert_float(Float::NAN, Float::NAN.quo(Float::NAN)) assert_float(Float::NAN, Float::NAN.quo(0.0)) assert_float(Float::NAN, Float::NAN.quo(-0.0)) assert_float(Float::NAN, Float::NAN.quo(2.0)) assert_float(Float::NAN, Float::NAN.quo(-2.0)) assert_float(Float::NAN, 0.0.quo(Float::NAN)) assert_float(Float::NAN, -0.0.quo(Float::NAN)) assert_float(Float::NAN, 2.0.quo(Float::NAN)) assert_float(Float::NAN, -2.0.quo(Float::NAN)) assert_float(Float::NAN, Float::INFINITY.quo(Float::INFINITY)) assert_float(Float::NAN, -Float::INFINITY.quo(Float::INFINITY)) assert_float(Float::NAN, Float::INFINITY.quo(-Float::INFINITY)) assert_float(Float::NAN, -Float::INFINITY.quo(-Float::INFINITY)) assert_float(Float::INFINITY, 1.0.quo(0.0)) assert_float(Float::INFINITY, -1.0.quo(-0.0)) assert_float(-Float::INFINITY, 1.0.quo(-0.0)) assert_float(-Float::INFINITY, -1.0.quo(0.0)) assert_float(0.0, 1.0.quo(Float::INFINITY)) assert_float(0.0, -1.0.quo(-Float::INFINITY)) assert_float(-0.0, -1.0.quo(Float::INFINITY)) assert_float(-0.0, 1.0.quo(-Float::INFINITY)) end assert('Float#%', '15.2.9.3.5') do a = 3.125 % 3.125 b = 3.125 % 1 assert_float(0.0 , a) assert_float(0.125, b) end assert('Float#<=>', '15.2.9.3.6') do a = 3.125 <=> 3.123 b = 3.125 <=> 3.125 c = 3.125 <=> 3.126 a2 = 3.125 <=> 3 c2 = 3.125 <=> 4 assert_equal( 1, a) assert_equal( 0, b) assert_equal(-1, c) assert_equal( 1, a2) assert_equal(-1, c2) end assert('Float#==', '15.2.9.3.7') do assert_true 3.1 == 3.1 assert_false 3.1 == 3.2 end assert('Float#ceil', '15.2.9.3.8') do a = 3.123456789.ceil b = 3.0.ceil c = -3.123456789.ceil d = -3.0.ceil assert_equal( 4, a) assert_equal( 3, b) assert_equal(-3, c) assert_equal(-3, d) end assert('Float#finite?', '15.2.9.3.9') do assert_predicate 3.123456789, :finite? assert_not_predicate 1.0 / 0.0, :finite? end assert('Float#floor', '15.2.9.3.10') do a = 3.123456789.floor b = 3.0.floor c = -3.123456789.floor d = -3.0.floor assert_equal( 3, a) assert_equal( 3, b) assert_equal(-4, c) assert_equal(-3, d) end assert('Float#infinite?', '15.2.9.3.11') do a = 3.123456789.infinite? b = (1.0 / 0.0).infinite? c = (-1.0 / 0.0).infinite? assert_nil a assert_equal( 1, b) assert_equal(-1, c) end assert('Float#round', '15.2.9.3.12') do a = 3.123456789.round b = 3.5.round c = 3.4999.round d = (-3.123456789).round e = (-3.5).round f = 12345.67.round(-1) g = 3.423456789.round(0) h = 3.423456789.round(1) i = 3.423456789.round(3) assert_equal( 3, a) assert_equal( 4, b) assert_equal( 3, c) assert_equal( -3, d) assert_equal( -4, e) assert_equal(12350, f) assert_equal( 3, g) assert_float( 3.4, h) assert_float(3.423, i) assert_equal(42.0, 42.0.round(307)) assert_equal(1.0e307, 1.0e307.round(2)) inf = 1.0/0.0 assert_raise(FloatDomainError){ inf.round } assert_raise(FloatDomainError){ inf.round(-1) } assert_equal(inf, inf.round(1)) nan = 0.0/0.0 assert_raise(FloatDomainError){ nan.round } assert_raise(FloatDomainError){ nan.round(-1) } assert_predicate(nan.round(1), :nan?) end assert('Float#to_f', '15.2.9.3.13') do a = 3.123456789 assert_float(a, a.to_f) end assert('Float#to_i', '15.2.9.3.14') do assert_equal(3, 3.123456789.to_i) assert_raise(FloatDomainError) { Float::INFINITY.to_i } assert_raise(FloatDomainError) { (-Float::INFINITY).to_i } assert_raise(FloatDomainError) { Float::NAN.to_i } end assert('Float#truncate', '15.2.9.3.15') do assert_equal( 3, 3.123456789.truncate) assert_equal(-3, -3.1.truncate) end assert('Float#divmod') do def check_floats(exp, act) assert_float exp[0], act[0] assert_float exp[1], act[1] end # Note: quotients are Float because mruby does not have Bignum. check_floats [ 0, 0.0], 0.0.divmod(1) check_floats [ 0, 1.1], 1.1.divmod(3) check_floats [ 3, 0.2], 3.2.divmod(1) check_floats [ 2, 6.3], 20.3.divmod(7) check_floats [-1, 1.6], -3.4.divmod(5) check_floats [-2, -0.5], 25.5.divmod(-13) check_floats [ 1, -6.6], -13.6.divmod(-7) check_floats [ 3, 0.2], 9.8.divmod(3.2) end assert('Float#nan?') do assert_predicate(0.0/0.0, :nan?) assert_not_predicate(0.0, :nan?) assert_not_predicate(1.0/0.0, :nan?) assert_not_predicate(-1.0/0.0, :nan?) end assert('Float#to_s') do uses_float = 4e38.infinite? # enable MRB_USE_FLOAT32? assert_equal("Infinity", Float::INFINITY.to_s) assert_equal("-Infinity", (-Float::INFINITY).to_s) assert_equal("NaN", Float::NAN.to_s) assert_equal("0.0", 0.0.to_s) assert_equal("-0.0", -0.0.to_s) assert_equal("-3.25", -3.25.to_s) assert_equal("50.0", 50.0.to_s) assert_equal("0.0125", 0.0125.to_s) assert_equal("-0.0125", -0.0125.to_s) assert_equal("1.0e-10", 0.0000000001.to_s) assert_equal("-1.0e-10", -0.0000000001.to_s) assert_equal("1.0e+20", 1e20.to_s) assert_equal("-1.0e+20", -1e20.to_s) assert_equal("100000.0", 100000.0.to_s) assert_equal("-100000.0", -100000.0.to_s) if uses_float assert_equal("1.0e+08", 100000000.0.to_s) assert_equal("-1.0e+08", -100000000.0.to_s) assert_equal("1.0e+07", 10000000.0.to_s) assert_equal("-1.0e+07", -10000000.0.to_s) else assert_equal("1.0e+15", 1000000000000000.0.to_s) assert_equal("-1.0e+15", -1000000000000000.0.to_s) assert_equal("100000000000000.0", 100000000000000.0.to_s) assert_equal("-100000000000000.0", -100000000000000.0.to_s) end end assert('Float#inspect') do assert_equal("-3.25", -3.25.inspect) assert_equal("50.0", 50.0.inspect) end assert('Float#eql?') do assert_operator(5.0, :eql?, 5.0) assert_not_operator(5.0, :eql?, 5) assert_not_operator(5.0, :eql?, "5.0") end assert('Float#abs') do f = 1.0 assert_equal(1.0, f.abs) f = -1.0 assert_equal(1.0, f.abs) f = 0.0 assert_equal(0.0, f.abs) # abs(negative zero) should be positive zero f = -0.0 assert_equal(0.0, f.abs) end end # const_defined?(:Float) nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/ensure.rb0000644000000000000000000000013215077107276020743 xustar0030 mtime=1761382078.139420479 30 atime=1761382080.162411224 30 ctime=1761382108.506302098 nghttp2-1.68.0/third-party/mruby/test/t/ensure.rb0000644000175100017510000000120615077107276021332 0ustar00runnerrunner## # ensure Test class EnsureYieldBreak attr_reader :ensure_context def try yield ensure @ensure_context = self end end assert('ensure - context - yield') do yielder = EnsureYieldBreak.new yielder.try do end assert_equal yielder, yielder.ensure_context end assert('ensure - context - yield and break') do yielder = EnsureYieldBreak.new yielder.try do break end assert_equal yielder, yielder.ensure_context end assert('ensure - context - yield and return') do yielder = EnsureYieldBreak.new lambda do yielder.try do return end end.call assert_equal yielder, yielder.ensure_context end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/numeric.rb0000644000000000000000000000013215077107276021104 xustar0030 mtime=1761382078.140420474 30 atime=1761382080.164411215 30 ctime=1761382108.528302034 nghttp2-1.68.0/third-party/mruby/test/t/numeric.rb0000644000175100017510000000673315077107276021505 0ustar00runnerrunner## # Numeric ISO Test def assert_step(exp, receiver, args, inf: false) act = [] ret = receiver.step(*args) do |i| act << i break if inf && exp.size == act.size end expr = "#{receiver.inspect}.step(#{args.map(&:inspect).join(', ')})" assert "assert_step" do assert_true(exp.eql?(act), "#{expr}: counters", assertion_diff(exp, act)) assert_same(receiver, ret, "#{expr}: return value") unless inf end end assert('Numeric', '15.2.7') do assert_equal(Class, Numeric.class) end assert('Numeric#+@', '15.2.7.4.1') do assert_equal(+1, +1) end assert('Numeric#-@', '15.2.7.4.2') do assert_equal(-1, -1) end assert('Numeric#abs', '15.2.7.4.3') do assert_equal(1, 1.abs) skip unless Object.const_defined?(:Float) assert_equal(1.0, -1.abs) end assert('Numeric#/', '15.2.8.3.4') do n = Class.new(Numeric){ def /(x); 15.1;end }.new assert_equal(2, 10/5) assert_equal(0, 1/16) assert_equal(15.1, n/10) assert_raise(TypeError){ 1/n } assert_raise(TypeError){ 1/nil } end # Not ISO specified assert('Numeric#**') do assert_equal(8, 2 ** 3) assert_equal(-8, -2 ** 3) assert_equal(1, 2 ** 0) skip unless Object.const_defined?(:Float) assert_equal(1.0, 2.2 ** 0) assert_equal(0.5, 2 ** -1) assert_equal(8.0, 2.0**3) end assert('Numeric#step') do assert_raise(ArgumentError) { 1.step(2, 0) { break } } assert_step([2, 3, 4], 2, [4]) assert_step([10, 8, 6, 4, 2], 10, [1, -2]) assert_step([], 2, [1, 3]) assert_step([], -2, [-1, -3]) assert_step([10, 11, 12, 13], 10, [], inf: true) assert_step([10, 7, 4], 10, [nil, -3], inf: true) skip unless Object.const_defined?(:Float) inf = Float::INFINITY assert_raise(ArgumentError) { 1.step(2, 0.0) { break } } assert_step([2, 3, 4], 2, [4.0]) assert_step([7.0, 4.0, 1.0, -2.0], 7, [-4, -3.0]) assert_step([2.0, 3.0, 4.0], 2.0, [4]) assert_step([10.0, 11.0, 12.0, 13.0], 10.0, [], inf: true) assert_step([10.0, 7.0, 4.0], 10, [nil, -3.0], inf: true) assert_step([1.0], 1, [nil, inf]) assert_step([1.0], 1, [nil, -inf]) assert_step([1.0], 1, [3, inf]) assert_step([], 1, [-3, inf]) assert_step([], 1, [3, -inf]) assert_step([1.0], 1, [-3, -inf]) assert_step([1.0], 1, [inf, inf]) assert_step([], 1, [inf, -inf]) assert_step([], 1, [-inf, inf]) assert_step([1.0], 1, [-inf, -inf]) assert_step([], inf, [2]) assert_step([], inf, [-2]) assert_step([], inf, [2, 3]) assert_step([inf, inf, inf], inf, [2, -3], inf: true) assert_step([], inf, [2, inf]) assert_step([inf], inf, [2, -inf]) assert_step([], inf, [-2, inf]) assert_step([inf], inf, [-2, -inf]) assert_step([], inf, [-2, 3]) assert_step([inf, inf, inf], inf, [-2, -3], inf: true) assert_step([inf], inf, [inf]) assert_step([], inf, [-inf]) assert_step([inf], inf, [inf, inf]) assert_step([inf], inf, [inf, -inf]) assert_step([inf], inf, [-inf, -inf]) assert_step([-inf, -inf, -inf], -inf, [2], inf: true) assert_step([-inf, -inf, -inf], -inf, [-2], inf: true) assert_step([-inf, -inf, -inf], -inf, [2, 3], inf: true) assert_step([], -inf, [2, -3]) assert_step([-inf], -inf, [2, inf]) assert_step([], -inf, [2, -inf]) assert_step([-inf], -inf, [-2, inf]) assert_step([], -inf, [-2, -inf]) assert_step([-inf, -inf, -inf], -inf, [-2, 3], inf: true) assert_step([], -inf, [-2, -3]) assert_step([-inf, -inf, -inf], -inf, [inf], inf: true) assert_step([-inf], -inf, [-inf]) assert_step([-inf], -inf, [inf, inf]) assert_step([], -inf, [inf, -inf]) assert_step([-inf], -inf, [-inf, -inf]) end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/bs_literal.rb0000644000000000000000000000013215077107276021562 xustar0030 mtime=1761382078.139420479 30 atime=1761382080.162411224 30 ctime=1761382108.486302156 nghttp2-1.68.0/third-party/mruby/test/t/bs_literal.rb0000644000175100017510000000112015077107276022144 0ustar00runnerrunner## # Bootstrap test for literals assert('BS Literal 1') do assert_true true end assert('BS Literal 2') do assert_equal TrueClass, true.class end assert('BS Literal 3') do assert_false false end assert('BS Literal 4') do assert_equal FalseClass, false.class end assert('BS Literal 5') do assert_equal 'nil', nil.inspect end assert('BS Literal 6') do assert_equal NilClass, nil.class end assert('BS Literal 7') do assert_equal Symbol, :sym.class end assert('BS Literal 8') do assert_equal 1234, 1234 end assert('BS Literal 9') do assert_equal Integer, 1234.class end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/integer.rb0000644000000000000000000000013115077107276021076 xustar0030 mtime=1761382078.139420479 29 atime=1761382080.16341122 30 ctime=1761382108.517302066 nghttp2-1.68.0/third-party/mruby/test/t/integer.rb0000644000175100017510000001077515077107276021501 0ustar00runnerrunner## # Integer ISO Test assert('Integer', '15.2.8') do assert_equal Class, Integer.class end assert('Integer#+', '15.2.8.3.1') do a = 1+1 b = 1+1.0 if Object.const_defined?(:Float) assert_equal 2, a assert_equal 2.0, b if Object.const_defined?(:Float) assert_raise(TypeError){ 0+nil } assert_raise(TypeError){ 1+nil } end assert('Integer#-', '15.2.8.3.2') do a = 2-1 b = 2-1.0 if Object.const_defined?(:Float) assert_equal 1, a assert_equal 1.0, b if Object.const_defined?(:Float) end assert('Integer#*', '15.2.8.3.3') do a = 1*1 assert_equal 1, a if Object.const_defined?(:Float) b = 1*1.0 assert_equal 1.0, b end assert_raise(TypeError){ 0*nil } assert_raise(TypeError){ 1*nil } end assert('Integer#/', '15.2.8.3.4') do a = 2/1 assert_equal 2, a a = 5/2 assert_equal 2, a b = -1/2 assert_equal(-1, b) b = 1/-2 assert_equal(-1, b) skip unless Object.const_defined?(:Float) b = 2/1.0 assert_equal 2.0, b end if Object.const_defined?(:Float) assert('Integer#quo') do a = 6.quo(5) assert_equal 1.2, a end end assert('Integer#%', '15.2.8.3.5') do a = 1%1 b = 2%4 c = 2%5 d = 2%-5 e = -2%5 f = -2%-5 g = 2%-2 h = -2%2 i = -2%-2 assert_equal 0, a assert_equal 2, b assert_equal 2, c assert_equal(-3, d) assert_equal 3, e assert_equal(-2, f) assert_equal 0, g assert_equal 0, h assert_equal 0, i skip unless Object.const_defined?(:Float) j = 1%1.0 assert_equal 0.0, j end assert('Integer#<=>', '15.2.9.3.6') do a = 1<=>0 b = 1<=>1 c = 1<=>2 assert_equal 1, a assert_equal 0, b assert_equal(-1, c) end assert('Integer#==', '15.2.8.3.7') do a = 1==0 b = 1==1 assert_false a assert_true b end assert('Integer#~', '15.2.8.3.8') do # Complement assert_equal(-1, ~0) assert_equal(-3, ~2) end assert('Integer#&', '15.2.8.3.9') do # Bitwise AND # 0101 (5) # & 0011 (3) # = 0001 (1) assert_equal 1, 5 & 3 end assert('Integer#|', '15.2.8.3.10') do # Bitwise OR # 0101 (5) # | 0011 (3) # = 0111 (7) assert_equal 7, 5 | 3 end assert('Integer#^', '15.2.8.3.11') do # Bitwise XOR # 0101 (5) # ^ 0011 (3) # = 0110 (6) assert_equal 6, 5 ^ 3 end assert('Integer#<<', '15.2.8.3.12') do # Left Shift by one # 00010111 (23) # = 00101110 (46) assert_equal 46, 23 << 1 # Left Shift by a negative is Right Shift assert_equal 23, 46 << -1 skip unless Object.const_defined?(:Float) end assert('Integer#>>', '15.2.8.3.13') do # Right Shift by one # 00101110 (46) # = 00010111 (23) assert_equal 23, 46 >> 1 # Right Shift by a negative is Left Shift assert_equal 46, 23 >> -1 # Don't raise on large Right Shift assert_equal 0, 23 >> 128 end assert('Integer#ceil', '15.2.8.3.14') do assert_equal 10, 10.ceil end assert('Integer#downto', '15.2.8.3.15') do a = 0 3.downto(1) do |i| a += i end assert_equal 6, a end assert('Integer#eql?', '15.2.8.3.16') do a = 1.eql?(1) b = 1.eql?(2) c = 1.eql?(nil) assert_true a assert_false b assert_false c end assert('Integer#floor', '15.2.8.3.17') do a = 1.floor assert_equal 1, a end assert('Integer#next', '15.2.8.3.19') do assert_equal 2, 1.next end assert('Integer#round', '15.2.8.3.20') do assert_equal 1, 1.round end assert('Integer#succ', '15.2.8.3.21') do assert_equal 2, 1.succ end assert('Integer#times', '15.2.8.3.22') do a = 0 3.times do a += 1 end assert_equal 3, a end assert('Integer#to_f', '15.2.8.3.23') do skip unless Object.const_defined?(:Float) assert_equal 1.0, 1.to_f end assert('Integer#to_i', '15.2.8.3.24') do assert_equal 1, 1.to_i end assert('Integer#to_s', '15.2.8.3.25') do assert_equal "1", 1.to_s assert_equal "-1", -1.to_s assert_equal "1010", 10.to_s(2) assert_equal "a", 10.to_s(36) assert_equal "-a", -10.to_s(36) assert_equal "30071", 12345.to_s(8) assert_raise(ArgumentError) { 10.to_s(-1) } assert_raise(ArgumentError) { 10.to_s(0) } assert_raise(ArgumentError) { 10.to_s(1) } assert_raise(ArgumentError) { 10.to_s(37) } end assert('Integer#truncate', '15.2.8.3.26') do assert_equal 1, 1.truncate end assert('Integer#upto', '15.2.8.3.27') do a = 0 1.upto(3) do |i| a += i end assert_equal 6, a end assert('Integer#divmod', '15.2.8.3.30') do assert_equal [ 0, 0], 0.divmod(1) assert_equal [ 0, 1], 1.divmod(3) assert_equal [ 3, 0], 3.divmod(1) assert_equal [ 2, 6], 20.divmod(7) assert_equal [-1, 2], -3.divmod(5) assert_equal [-2, -1], 25.divmod(-13) assert_equal [ 1, -6], -13.divmod(-7) end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/syntax.rb0000644000000000000000000000013215077107276020770 xustar0030 mtime=1761382078.141420469 30 atime=1761382080.164411215 30 ctime=1761382108.500302116 nghttp2-1.68.0/third-party/mruby/test/t/syntax.rb0000644000175100017510000004144415077107276021367 0ustar00runnerrunnerassert('__FILE__') do file = __FILE__[-9, 9] assert_equal 'syntax.rb', file end assert('__LINE__') do assert_equal 7, __LINE__ end assert('super', '11.3.4') do assert_raise NoMethodError do super end class SuperFoo def foo true end def bar(*a) a end end class SuperBar < SuperFoo def foo super end def bar(*a) super(*a) end end bar = SuperBar.new assert_true bar.foo assert_equal [1,2,3], bar.bar(1,2,3) end assert('yield', '11.3.5') do # it's syntax error now # assert_raise LocalJumpError do # yield # end assert_raise LocalJumpError do o = Object.new def o.foo yield end o.foo end end assert('break', '11.5.2.4.3') do n = 0 a = [] while true n += 1 a.push(n) if n > 3 break end end assert_equal [1,2,3,4], a n = 0 a = [] 6.times do n += 1 a.push(n) if n > 3 break end end assert_equal [1,2,3,4], a a = [] begin while true a.push 1 break a.push "NG" end ensure a.push 2 end assert_equal [1, 2], a a = [] begin while true a.push 1 break end a.push 2 ensure a.push 3 end assert_equal [1, 2, 3], a a = [] begin while true begin a.push 1 break ensure a.push 2 end a.push "NG" end ensure a.push 3 end assert_equal [1, 2, 3], a a = [] begin while true begin a.push 1 break ensure a.push 2 end a.push "NG" end a.push 3 ensure a.push 4 end assert_equal [1, 2, 3, 4], a end assert('redo', '11.5.2.4.5') do sum = 0 for i in 1..10 sum += i i -= 1 if i > 0 redo end end assert_equal 220, sum n = 0 a = [] 3.times do n += 1 if n == 2 redo end a.push(n) end assert_equal [1,3,4], a a = [] limit = 3 e = RuntimeError.new("!") for i in 0...3 begin limit -= 1 break unless limit > 0 a.push i * 3 + 1 raise e rescue a.push i * 3 + 2 redo ensure a.push i * 3 + 3 end end assert_equal [1, 2, 3, 1, 2, 3, 3], a a = [] limit = 3 e = RuntimeError.new("!") for i in 0...3 a.push i * 4 + 1 begin limit -= 1 break unless limit > 0 a.push i * 4 + 2 raise e rescue a.push i * 4 + 3 redo ensure a.push i * 4 + 4 end end assert_equal [1, 2, 3, 4, 1, 2, 3, 4, 1, 4], a end assert('Abbreviated variable assignment', '11.4.2.3.2') do a ||= 1 b &&= 1 c = 1 c += 2 assert_equal 1, a assert_nil b assert_equal 3, c end assert('case expression', '11.5.2.2.4') do # case-expression-with-expression, one when-clause x = 0 case "a" when "a" x = 1 end assert_equal 1, x # case-expression-with-expression, multiple when-clauses x = 0 case "b" when "a" x = 1 when "b" x = 2 end assert_equal 2, x # no matching when-clause x = 0 case "c" when "a" x = 1 when "b" x = 2 end assert_equal 0, x # case-expression-with-expression, one when-clause and one else-clause a = 0 case "c" when "a" x = 1 else x = 3 end assert_equal 3, x # case-expression-without-expression, one when-clause x = 0 case when true x = 1 end assert_equal 1, x # case-expression-without-expression, multiple when-clauses x = 0 case when 0 == 1 x = 1 when 1 == 1 x = 2 end assert_equal 2, x # case-expression-without-expression, one when-clause and one else-clause x = 0 case when 0 == 1 x = 1 else x = 3 end assert_equal 3, x # multiple when-arguments x = 0 case 4 when 1, 3, 5 x = 1 when 2, 4, 6 x = 2 end assert_equal 2, x # when-argument with splatting argument x = :integer odds = [ 1, 3, 5, 7, 9 ] evens = [ 2, 4, 6, 8 ] case 5 when *odds x = :odd when *evens x = :even end assert_equal :odd, x true end assert('Nested const reference') do module Syntax4Const CONST1 = "hello world" class Const2 def const1 CONST1 end end end assert_equal "hello world", Syntax4Const::CONST1 assert_equal "hello world", Syntax4Const::Const2.new.const1 assert_raise(NameError) { Syntax4Const::Object } end assert('Abbreviated variable assignment as returns') do module Syntax4AbbrVarAsgnAsReturns class A def b @c ||= 1 end end end assert_equal 1, Syntax4AbbrVarAsgnAsReturns::A.new.b end assert('Abbreviated variable assignment of object attribute') do module Syntax4AbbrVarAsgnObjectAttr class A attr_accessor :c def b self.c ||= 1 end end end assert_equal 1, Syntax4AbbrVarAsgnObjectAttr::A.new.b end assert('Splat and multiple assignment') do *a = *[1,2,3] b, *c = *[7,8,9] assert_equal [1,2,3], a assert_equal 7, b assert_equal [8,9], c (a, b), c = [1,2],3 assert_equal [1,2,3], [a,b,c] (a, b), c = 1,2,3 assert_equal [1,nil,2], [a,b,c] end assert('Splat and multiple assignment from variable') do a = [1, 2, 3] b, *c = a assert_equal 1, b assert_equal [2, 3], c end assert('Splat and multiple assignment from variables') do a = [1, 2, 3] b = [4, 5, 6, 7] c, d, *e, f, g = *a, *b assert_equal 1, c assert_equal 2, d assert_equal [3, 4, 5], e assert_equal 6, f assert_equal 7, g end assert('Splat and multiple assignment in for') do a = [1, 2, 3, 4, 5, 6, 7] for b, c, *d, e, f in [a] do end assert_equal 1, b assert_equal 2, c assert_equal [3, 4, 5], d assert_equal 6, e assert_equal 7, f end assert('Splat without assignment') do * = [0] a, * = [1, 2] assert_equal 1, a end assert('multiple assignment (rest)') do *a = 0 assert_equal [0], a end assert('multiple assignment (rest+post)') do *a, b = 0, 1, 2 *c, d = 3 assert_equal [0, 1], a assert_equal 2, b assert_equal [], c assert_equal 3, d end assert('multiple assignment (nosplat array rhs)') do a, *b = [] *c, d = [0] e, *f, g = [1, 2] assert_nil a assert_equal [], b assert_equal [], c assert_equal 0, d assert_equal 1, e assert_equal [], f assert_equal 2, g end assert('multiple assignment (empty array rhs #3236, #3239)') do a,b,*c = []; assert_equal [nil, nil, []], [a, b, c] a,b,*c = [1]; assert_equal [1, nil, []], [a, b, c] a,b,*c = [nil]; assert_equal [nil,nil, []], [a, b, c] a,b,*c = [[]]; assert_equal [[], nil, []], [a, b, c] end assert('Return values of case statements') do a = [] << case 1 when 3 then 2 when 2 then 2 when 1 then 2 end b = [] << case 1 when 2 then 2 else end def fb n = 0 Proc.new do n += 1 case when n % 15 == 0 else n end end end assert_equal [2], a assert_equal [nil], b assert_equal 1, fb.call end assert('Return values of if and case statements') do true_clause_value = if true 1 else case 2 when 3 end 4 end assert_equal 1, true_clause_value end assert('Return values of no expression case statement') do when_value = case when true 1 end assert_equal 1, when_value end assert('splat object in assignment') do o = Object.new def o.to_a nil end assert_equal [o], (a = *o) def o.to_a 1 end assert_raise(TypeError) { a = *o } def o.to_a [2] end assert_equal [2], (a = *o) end assert('one-line pattern match') do 1 => a assert_equal(1, a) end assert('splat object in case statement') do o = Object.new def o.to_a nil end a = case o when *o 1 end assert_equal 1, a end assert('splat in case statement') do values = [3,5,1,7,8] testa = [1,2,7] testb = [5,6] resulta = [] resultb = [] resultc = [] values.each do |value| case value when *testa resulta << value when *testb resultb << value else resultc << value end end assert_equal [1,7], resulta assert_equal [5], resultb assert_equal [3,8], resultc end assert('External command execution.') do module Kernel sym = '`'.to_sym alias_method :old_cmd, sym results = [] define_method(sym) do |str| results.push str str end `test` # NOVAL NODE_XSTR `test dynamic #{sym}` # NOVAL NODE_DXSTR assert_equal ['test', 'test dynamic `'], results t = `test` # VAL NODE_XSTR assert_equal 'test', t assert_equal ['test', 'test dynamic `', 'test'], results t = `test dynamic #{sym}` # VAL NODE_DXSTR assert_equal 'test dynamic `', t assert_equal ['test', 'test dynamic `', 'test', 'test dynamic `'], results results = [] assert_equal 'test sym test sym test', `test #{:sym} test #{:sym} test` alias_method sym, :old_cmd end true end assert('parenthesed do-block in cmdarg') do class ParenDoBlockCmdArg def test(block) block.call end end x = ParenDoBlockCmdArg.new result = x.test (Proc.new do :ok; end) assert_equal :ok, result end assert('method definition in cmdarg') do result = class MethodDefinitionInCmdarg def self.bar(arg); arg end bar def foo; self.each do end end end assert_equal(:foo, result) end assert('optional argument in the rhs default expressions') do class OptArgInRHS def foo "method called" end def t(foo = foo) foo end def t2(foo = foo()) foo end end o = OptArgInRHS.new assert_nil(o.t) assert_equal("method called", o.t2) end assert('optional block argument in the rhs default expressions') do assert_nil(Proc.new {|foo = foo| foo}.call) end assert('local variable definition in default value and subsequent arguments') do def m(a = b = 1, c) [a, b, c] end assert_equal([1, 1, :c], m(:c)) assert_equal([:a, nil, :c], m(:a, :c)) def m(a = b = 1, &c) [a, b, c ? true : nil] end assert_equal([1, 1, nil], m) assert_equal([1, 1, true], m{}) assert_equal([:a, nil, nil], m(:a)) assert_equal([:a, nil, true], m(:a){}) end assert('multiline comments work correctly') do =begin this is a comment with nothing after begin and end =end =begin this is a comment this is a comment with extra after =begin =end =begin this is a comment that has =end with spaces after it =end =begin this is a comment this is a comment that has extra after =begin and =end with spaces after it =end line = __LINE__ =begin this is a comment this is a comment that has extra after =begin and =end with tabs after it =end xxxxxxxxxxxxxxxxxxxxxxxxxx assert_equal(line + 4, __LINE__) end assert 'keyword arguments' do def m(a, b:1) [a, b] end assert_equal [1, 1], m(1) assert_equal [1, 2], m(1, b: 2) def m(a, b:) [a, b] end assert_equal [1, 2], m(1, b: 2) assert_raise(ArgumentError) { m b: 1 } assert_raise(ArgumentError) { m 1 } def m(a:) a end assert_equal 1, m(a: 1) assert_raise(ArgumentError) { m } assert_raise(ArgumentError) { m 'a' => 1, a: 1 } h = { a: 1 } assert_equal 1, m(**h) def m(a: 1) a end assert_equal 1, m assert_equal 2, m(a: 2) assert_raise(ArgumentError) { m 1 } def m(**) end assert_nil m assert_nil m a: 1, b: 2 assert_raise(ArgumentError) { m 2 } def m(a, **) a end assert_equal 1, m(1) assert_equal 1, m(1, a: 2, b: 3) assert_raise(ArgumentError) { m('a' => 1, b: 2) } def m(a, **k) [a, k] end assert_equal [1, {}], m(1) assert_equal [1, {a: 2, b: 3}], m(1, a: 2, b: 3) assert_raise(ArgumentError) { m('a' => 1, b: 2) } def m(a=1, **) a end assert_equal 1, m assert_equal 2, m(2, a: 1, b: 0) def m(a=1, **k) [a, k] end assert_equal [1, {}], m assert_equal [1, {a: 1}], m(a: 1) assert_equal [2, {a: 1, b: 2}], m(2, a: 1, b: 2) assert_equal [{a: 1}, {b: 2}], m({a: 1}, b: 2) assert_raise(ArgumentError) { m({a: 1}, {b: 2}) } def m(*, a:) a end assert_equal 1, m(a: 1) assert_equal 3, m(1, 2, a: 3) assert_raise(ArgumentError) { m('a' => 1, a: 2) } def m(*a, b:) [a, b] end assert_equal [[], 1], m(b: 1) assert_equal [[1, 2], 3], m(1, 2, b: 3) assert_raise(ArgumentError) { m('a' => 1, b: 2) } def m(*a, b: 1) [a, b] end assert_equal [[], 1], m assert_equal [[1, 2, 3], 4], m(1, 2, 3, b: 4) assert_raise(ArgumentError) { m('a' => 1, b: 2) } def m(*, **) end assert_nil m() assert_nil m(a: 1, b: 2) assert_nil m(1, 2, 3, a: 4, b: 5) def m(*a, **) a end assert_equal [], m() assert_equal [1, 2, 3], m(1, 2, 3, a: 4, b: 5) assert_equal [1], m(1, **{a: 2}) def m(*, **k) k end assert_equal({}, m()) assert_equal({a: 4, b: 5}, m(1, 2, 3, a: 4, b: 5)) def m(a = nil, b = nil, **k) [a, k] end assert_equal [nil, {}], m() assert_equal([nil, {a: 1}], m(a: 1)) assert_equal([{"a" => 1}, {a: 1}], m({ "a" => 1 }, a: 1)) assert_equal([{a: 1}, {}], m({a: 1}, {})) assert_equal([{}, {}], m({})) def m(*a, **k) [a, k] end assert_equal([[], {}], m()) assert_equal([[1], {}], m(1)) assert_equal([[], {a: 1, b: 2}], m(a: 1, b: 2)) assert_equal([[1, 2, 3], {a: 2}], m(1, 2, 3, a: 2)) assert_equal([[], {a: 1}], m(a: 1)) assert_equal([[{"a" => 1}], {a: 1}], m({ "a" => 1 }, a: 1)) assert_equal([[{a: 1}, {}], {}], m({a: 1}, {})) def m(a:, b:) [a, b] end assert_equal([1, 2], m(a: 1, b: 2)) assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) } def m(a:, b: 1) [a, b] end assert_equal([1, 1], m(a: 1)) assert_equal([1, 2], m(a: 1, b: 2)) assert_raise(ArgumentError) { m(b: 1) } assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) } def m(a:, **) a end assert_equal(1, m(a: 1)) assert_equal(1, m(a: 1, b: 2)) def m(a:, **k) [a, k] end assert_equal([1, {}], m(a: 1)) assert_equal([1, {b: 2, c: 3}], m(a: 1, b: 2, c: 3)) def m(a:, &b) [a, b] end assert_equal([1, nil], m(a: 1)) result = m(a: 1, &(l = ->{})) assert_equal([1, l], result) def m(a: 1, b:) [a, b] end assert_equal([1, 0], m(b: 0)) assert_equal([3, 2], m(b: 2, a: 3)) assert_raise(ArgumentError) { m a: 1 } def m(a: def m(a: 1) a end, b:) [a, b] end assert_equal([2, 3], m(a: 2, b: 3)) assert_equal([:m, 1], m(b: 1)) # Note the default value of a: in the original method. assert_equal(1, m()) def m(a: 1, b: 2) [a, b] end assert_equal([1, 2], m()) assert_equal([4, 3], m(b: 3, a: 4)) def m(a: 1, **) a end assert_equal(1, m()) assert_equal(2, m(a: 2, b: 1)) def m(a: 1, **k) [a, k] end assert_equal([1, {b: 2, c: 3}], m(b: 2, c: 3)) def m(a:, **) yield end assert_raise(ArgumentError) { m { :blk } } assert_equal :blk, m(a: 1){ :blk } def m(a:, **k, &b) [b.call, k] end assert_raise(ArgumentError) { m { :blk } } assert_equal [:blk, {b: 2}], m(a: 1, b: 2){ :blk } def m(**k, &b) [k, b] end assert_equal([{ a: 1, b: 2}, nil], m(a: 1, b: 2)) assert_equal :blk, m{ :blk }[1].call =begin def m(a, b=1, *c, (*d, (e)), f: 2, g:, h:, **k, &l) [a, b, c, d, e, f, g, h, k, l] end result = m(9, 8, 7, 6, f: 5, g: 4, h: 3, &(l = ->{})) assert_equal([9, 8, [7], [], 6, 5, 4, 3, {}, l], result) def m a, b=1, *c, d, e:, f: 2, g:, **k, &l [a, b, c, d, e, f, g, k, l] end result = m(1, 2, e: 3, g: 4, h: 5, i: 6, &(l = ->{})) assert_equal([1, 1, [], 2, 3, 2, 4, { h: 5, i: 6 }, l], result) =end def m(a: b = 1, c:) [a, b, c] end assert_equal([1, 1, :c], m(c: :c)) assert_equal([:a, nil, :c], m(a: :a, c: :c)) end assert('numbered parameters') do assert_equal(15, [1,2,3,4,5].reduce {_1+_2}) assert_equal(45, Proc.new do _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 end.call(*[1, 2, 3, 4, 5, 6, 7, 8, 9])) assert_equal(5, -> { _1 }.call(5)) end assert('_0 is not numbered parameter') do _0 = :l assert_equal(:l, ->{_0}.call) end assert('numbered parameters in symbol name (https://github.com/mruby/mruby/issues/5295)') do assert_equal([:_1], Array.new(1) {:_1}) end assert('numbered parameters as hash key') do h = {_1: 3} assert_equal(3, h[:_1]) assert_equal(7, -> { _1 }.call(7)) end assert('numbered parameters as singleton') do o = Object.new lambda { def _1.a(b) = "a#{b}" }.call(o) assert_equal('ab', o.a('b')) end assert('argument forwarding') do c = Class.new { def a0(*a,&b) assert_equal([1,2,3], a) assert_not_nil(b) end def a(...) a0(...) end def b(a,...) assert_equal(a,1) a0(1,...) end def c ... a(...) end def d a,... assert_equal(a,1) b(1,...) end } o = c.new o.a(1,2,3){} o.b(1,2,3){} o.c(1,2,3){} o.d(1,2,3){} end assert('endless def') do c = Class.new { def m1 = 42 def m2() = 42 def m3(x) = x+1 def self.s1 = 42 def self.s2() = 42 def self.s3(x) = x + 1 def cm1 = m3 42 def cm2() = m3 42 def cm3(x) = m3 x+1 def self.cs1 = s3 42 def self.cs2() = s3 42 def self.cs3(x) = s3 x + 1 } o = c.new assert_equal(42, o.m1) assert_equal(43, o.m3(o.m2)) assert_equal(42, c.s1) assert_equal(43, c.s3(c.s2)) assert_equal(43, o.cm1) assert_equal(45, o.cm3(o.cm2)) assert_equal(43, c.cs1) assert_equal(45, c.cs3(c.cs2)) end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/class.rb0000644000000000000000000000013215077107276020547 xustar0030 mtime=1761382078.139420479 30 atime=1761382080.162411224 30 ctime=1761382108.489302147 nghttp2-1.68.0/third-party/mruby/test/t/class.rb0000644000175100017510000002104715077107276021143 0ustar00runnerrunner## # Class ISO Test assert('Class', '15.2.3') do assert_equal(Class, Class.class) end assert('Class#initialize', '15.2.3.3.1') do c = Class.new do def test :test end end.new assert_equal(c.test, :test) end assert('Class#initialize_copy', '15.2.3.3.2') do class TestClass attr_accessor :n def initialize(n) @n = n end def initialize_copy(obj) @n = n end end c1 = TestClass.new('Foo') c2 = c1.dup c3 = TestClass.new('Bar') assert_equal(c1.n, c2.n) assert_not_equal(c1.n, c3.n) end assert('Class#new', '15.2.3.3.3') do assert_raise(TypeError, 'Singleton should raise TypeError') do (class <<"a"; self; end).new end class TestClass def initialize(args, &block) @result = if not args.nil? and block.nil? # only arguments :only_args elsif not args.nil? and not block.nil? # args and block is given :args_and_block else # this should never happen :broken end end def result; @result; end end assert_equal(:only_args, TestClass.new(:arg).result) # with block doesn't work yet end assert('Class#superclass', '15.2.3.3.4') do class SubClass < String; end assert_equal(String, SubClass.superclass) end # Not ISO specified assert('Class 1') do class C1; end assert_equal(Class, C1.class) end assert('Class 2') do class C2; end assert_equal(C2, C2.new.class) end assert('Class 3') do class C3; end assert_equal(Class, C3.new.class.class) end assert('Class 4') do class C4_A; end class C4 < C4_A; end assert_equal(Class, C4.class) end assert('Class 5') do class C5_A; end class C5 < C5_A; end assert_equal(C5, C5.new.class) end assert('Class 6') do class C6_A; end class C6 < C6_A; end assert_equal(Class, C6.new.class.class) end assert('Class 7') do class C7_A; end class C7_B; end class C7 < C7_A; end assert_raise(TypeError) do # Different superclass. class C7 < C7_B; end end end assert('Class 8') do class C8_A; end class C8; end # superclass is Object assert_raise(TypeError) do # Different superclass. class C8 < C8_A; end end end assert('Class 9') do Class9Const = "a" assert_raise(TypeError) do class Class9Const; end end end assert('Class Module 1') do module M; end assert_equal(Module, M.class) end assert('Class Module 2') do module M; end class C; include M; end assert_equal(C, C.new.class) end # nested class assert('Class Nested 1') do class A; end class A::B; end assert_equal(A::B, A::B) end assert('Class Nested 2') do class A; end class A::B; end assert_equal(A::B, A::B.new.class) end assert('Class Nested 3') do class A; end class A::B; end assert_equal(Class, A::B.new.class.class) end assert('Class Nested 4') do class A; end class A::B; end class A::B::C; end assert_equal(A::B::C, A::B::C) end assert('Class Nested 5') do class A; end class A::B; end class A::B::C; end assert_equal(Class, A::B::C.class) end assert('Class Nested 6') do class A; end class A::B; end class A::B::C; end assert_equal(A::B::C, A::B::C.new.class) end assert('Class Nested 7') do class A; end class A::B; end class A::B2 < A::B; end assert_equal(A::B2, A::B2) end assert('Class Nested 8') do class A; end class A::B; end class A::B2 < A::B; end assert_equal(Class, A::B2.class) end assert('Class Colon 1') do class A; end A::C = 1 assert_equal(1, A::C) end assert('Class Colon 2') do class A; class ::C; end end assert_equal(C, C) end assert('Class Colon 3') do class A; class ::C; end end assert_equal(Class, C.class) end assert('Class Dup 1') do class C; end assert_equal(Class, C.dup.class) end assert('Class Dup 2') do module M; end assert_equal(Module, M.dup.class) end assert('Class.new') do assert_equal(Class, Class.new.class) a = [] klass = Class.new do |c| a << c end assert_equal([klass], a) end assert('class to return the last value') do m = class C; :m end assert_equal(m, :m) end assert('class to return nil if body is empty') do assert_nil(class C end) assert_nil(class << self; end) end assert('raise when superclass is not a class') do module FirstModule; end assert_raise(TypeError, 'should raise TypeError') do class FirstClass < FirstModule; end end class SecondClass; end assert_raise(TypeError, 'should raise TypeError') do class SecondClass < false; end end class ThirdClass; end assert_raise(TypeError, 'should raise TypeError') do class ThirdClass < ThirdClass; end end end assert('Class#inherited') do class Foo @@subclass_name = nil def self.inherited(subclass) @@subclass_name = subclass end def self.subclass_name @@subclass_name end end assert_equal(nil, Foo.subclass_name) class Bar < Foo end assert_equal(Bar, Foo.subclass_name) class Baz < Bar end assert_equal(Baz, Foo.subclass_name) end assert('singleton tests') do module FooMod def run_foo_mod 100 end end bar = String.new baz = class << bar extend FooMod def self.run_baz 200 end end assert_equal :run_baz, baz assert_raise(NoMethodError, 'should raise NoMethodError') do bar.run_foo_mod end assert_raise(NoMethodError, 'should raise NoMethodError') do bar.run_baz end baz = class << bar extend FooMod def self.run_baz 300 end self end assert_true baz.respond_to? :run_baz assert_true baz.respond_to? :run_foo_mod assert_equal 100, baz.run_foo_mod assert_equal 300, baz.run_baz assert_raise(NoMethodError, 'should raise NoMethodError') do bar.run_foo_mod end assert_raise(NoMethodError, 'should raise NoMethodError') do bar.run_baz end fv = false class << fv def self.run_false 5 end end nv = nil class << nv def self.run_nil 6 end end tv = true class << tv def self.run_nil 7 end end assert_raise(TypeError, 'should raise TypeError') do num = 1.0 class << num def self.run_nil 7 end end end if Object.const_defined?(:Float) o = Object.new sc = class << o; self end o.freeze assert_predicate(sc, :frozen?) assert_predicate(class << Object.new.freeze; self end, :frozen?) end assert('clone Class') do class Foo def func true end end assert_true(Foo.clone.new.func) end assert('class definition in singleton class') do class AClassS class << self class BClass end def iclass BClass end end end assert_equal(Class, AClassS.iclass.class) end assert('class variable and class << self style class method') do class ClassVariableTest @@class_variable = "value" class << self def class_variable @@class_variable end end end assert_equal("value", ClassVariableTest.class_variable) end assert('class variable definition in singleton_class') do class ClassVariableDefinitionInSingletonTest class << self @@class_variable = "value" end def class_variable @@class_variable end end assert_equal("value", ClassVariableDefinitionInSingletonTest.new.class_variable) end assert('class variable in module and class << self style class method') do module ClassVariableInModuleTest @@class_variable = "value" class << self def class_variable @@class_variable end end end assert_equal("value", ClassVariableInModuleTest.class_variable) end assert('child class/module defined in singleton class get parent constant') do actual = module GetParentConstantTest EXPECT = "value" class << self class CHILD class << self EXPECT end end end end assert_equal("value", actual) end assert('overriding class variable with a module (#3235)') do module ModuleWithCVar @@class_variable = 1 end class CVarOverrideTest @@class_variable = 2 include ModuleWithCVar assert_equal(1, @@class_variable) end end assert('class variable for frozen class/module') do module CVarForFrozenModule freeze assert_raise(FrozenError) { @@cv = 1 } end class CVarForFrozenClassA @@a = nil freeze end class CVarForFrozenClassB < CVarForFrozenClassA def a=(v) @@a = v end end b = CVarForFrozenClassB.new assert_raise(FrozenError) { b.a = 1 } end assert('class with non-class/module outer raises TypeError') do assert_raise(TypeError) { class 0::C1; end } assert_raise(TypeError) { class []::C2; end } end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/lang.rb0000644000000000000000000000013115077107276020362 xustar0030 mtime=1761382078.140420474 29 atime=1761382080.16341122 30 ctime=1761382108.492302139 nghttp2-1.68.0/third-party/mruby/test/t/lang.rb0000755000175100017510000000263215077107276020761 0ustar00runnerrunner# The aim of these tests is to detect pitfall for optimized VM. # Test for or/and # # You may think instruction fusion(OP_EQ and OP_JMPIF) for avoiding # generate intermediate boolean value. # But and/or is pitfall for this fusioning. # # For example, the following mruby code: # # if i > 0 and i < 10 # # compiles to the following byte code: # # 1 000 LOADI_0 R1 (0) ; R1:i # 2 002 MOVE R2 R1 ; R1:i # 2 005 LOADI_0 R3 (0) # 2 007 GT R2 R3 # 2 009 JMPNOT R2 021 # 2 013 MOVE R2 R1 ; R1:i # 2 016 LOADI8 R3 10 # 2 019 LT R2 R3 # 2 021 JMPNOT R2 (The address of end of then part) # # When the instruction fusion the OP_GT and OP_JMPNOT you fell into the pitfalls. # The deleted intermediate boolean value is used in OP_JMPNOT (address 008). assert('and', '11.2.3') do a = 1 if a > 0 and a < 10 b = 1 else b = 0 end assert_equal 1, b if a < 0 and a < 10 b = 1 else b = 0 end assert_equal 0, b if a < 0 and a > 10 b = 1 else b = 0 end assert_equal 0, b end assert('or','11.2.4') do a = 1 if a > 0 or a < 10 b = 1 else b = 0 end assert_equal 1, b if a < 0 or a < 10 b = 1 else b = 0 end assert_equal 1, b if a < 0 or a > 10 b = 1 else b = 0 end assert_equal 0, b end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/range.rb0000644000000000000000000000013215077107276020536 xustar0030 mtime=1761382078.140420474 30 atime=1761382080.164411215 30 ctime=1761382108.494302133 nghttp2-1.68.0/third-party/mruby/test/t/range.rb0000644000175100017510000001130015077107276021121 0ustar00runnerrunner## # Range ISO Test assert('Range', '15.2.14') do assert_equal Class, Range.class end assert('Range#==', '15.2.14.4.1') do assert_true (1..10) == (1..10) assert_false (1..10) == (1..100) assert_false (1..10) == (1..) assert_false (1..10) == (..10) assert_true (1..) == (1..nil) assert_true (1..) == (1..) assert_false (1..) == (1...) assert_true (..1) == (nil..1) assert_true (..1) == (..1) assert_false (..1) == (...1) skip unless Object.const_defined?(:Float) assert_true (1..10) == Range.new(1.0, 10.0) end assert('Range#===', '15.2.14.4.2') do a = (1..10) b = (1..) c = (..10) assert_true a === 5 assert_false a === 20 assert_true b === 20 assert_false b === 0 assert_false c === 20 assert_true c === 0 end assert('Range#begin', '15.2.14.4.3') do assert_equal 1, (1..10).begin assert_equal 1, (1..).begin assert_nil (..1).begin end assert('Range#each', '15.2.14.4.4') do a = (1..3) b = 0 a.each {|i| b += i} assert_equal 6, b c = [] (1..).each { |i| c << i; break if c.size == 10 } assert_equal [1, 2, 3, 4, 5, 6, 7, 8 ,9, 10], c end assert('Range#end', '15.2.14.4.5') do assert_equal 10, (1..10).end assert_nil (1..).end assert_equal 10, (..10).end end assert('Range#exclude_end?', '15.2.14.4.6') do assert_true (1...10).exclude_end? assert_false (1..10).exclude_end? assert_true (1...).exclude_end? assert_false (1..).exclude_end? assert_true (...1).exclude_end? assert_false (..1).exclude_end? end assert('Range#first', '15.2.14.4.7') do assert_equal 1, (1..10).first assert_equal 1, (1..).first end assert('Range#include?', '15.2.14.4.8') do assert_true (1..10).include?(10) assert_false (1..10).include?(11) assert_true (1..).include?(10) assert_false (1..).include?(0) assert_true (..10).include?(10) assert_true (..10).include?(0) assert_true (1...10).include?(9) assert_false (1...10).include?(10) assert_true (1...).include?(10) assert_false (1...).include?(0) assert_false (...10).include?(10) assert_true (...10).include?(0) end assert('Range#initialize', '15.2.14.4.9') do a = Range.new(1, 10, true) b = Range.new(1, 10, false) assert_equal (1...10), a assert_true a.exclude_end? assert_equal (1..10), b assert_false b.exclude_end? assert_raise(NameError) { (0..1).__send__(:initialize, 1, 3) } c = Range.new(1, nil, true) d = Range.new(1, nil, false) assert_equal (1...nil), c assert_true c.exclude_end? assert_equal (1..nil), d assert_false d.exclude_end? end assert('Range#last', '15.2.14.4.10') do assert_equal 10, (1..10).last assert_nil (1..).last end assert('Range#member?', '15.2.14.4.11') do a = (1..10) b = (1..) assert_true a.member?(5) assert_false a.member?(20) assert_true b.member?(20) assert_false b.member?(0) end assert('Range#to_s', '15.2.14.4.12') do assert_equal "0..1", (0..1).to_s assert_equal "0...1", (0...1).to_s assert_equal "a..b", ("a".."b").to_s assert_equal "a...b", ("a"..."b").to_s assert_equal "0..", (0..).to_s assert_equal "0...", (0...).to_s assert_equal "a..", ("a"..).to_s assert_equal "a...", ("a"...).to_s end assert('Range#inspect', '15.2.14.4.13') do assert_equal "0..1", (0..1).inspect assert_equal "0...1", (0...1).inspect assert_equal "\"a\"..\"b\"", ("a".."b").inspect assert_equal "\"a\"...\"b\"", ("a"..."b").inspect assert_equal "0..", (0..).inspect assert_equal "0...", (0...).inspect assert_equal "\"a\"..", ("a"..).inspect assert_equal "\"a\"...", ("a"...).inspect end assert('Range#eql?', '15.2.14.4.14') do assert_true (1..10).eql? (1..10) assert_false (1..10).eql? (1..100) assert_false (1..10).eql? "1..10" assert_true (1..).eql? (1..) assert_false (1..).eql? (2..) assert_false (1..).eql? "1.." skip unless Object.const_defined?(:Float) assert_false (1..10).eql? (Range.new(1.0, 10.0)) assert_false (1..).eql? (Range.new(1.0, nil)) end assert('Range#initialize_copy', '15.2.14.4.15') do assert_raise(NameError) { (0..1).__send__(:initialize_copy, 1..3) } end assert('Range#hash', '15.3.1.3.15') do assert_kind_of(Integer, (1..10).hash) assert_equal (1..10).hash, (1..10).hash assert_not_equal (1..10).hash, (1...10).hash assert_equal (1..).hash, (1..).hash assert_not_equal (1..).hash, (1...).hash end assert('Range#dup') do r = (1..3).dup assert_equal 1, r.begin assert_equal 3, r.end assert_false r.exclude_end? r = ("a"..."z").dup assert_equal "a", r.begin assert_equal "z", r.end assert_true r.exclude_end? r = (1..).dup assert_equal 1, r.begin assert_nil r.end assert_false r.exclude_end? end assert('Range#to_a') do assert_equal([1, 2, 3, 4, 5], (1..5).to_a) assert_equal([1, 2, 3, 4], (1...5).to_a) assert_raise(RangeError) { (1..).to_a } end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/standarderror.rb0000644000000000000000000000013215077107276022314 xustar0030 mtime=1761382078.140420474 30 atime=1761382080.164411215 30 ctime=1761382108.532302023 nghttp2-1.68.0/third-party/mruby/test/t/standarderror.rb0000644000175100017510000000016115077107276022702 0ustar00runnerrunner## # StandardError ISO Test assert('StandardError', '15.2.23') do assert_equal Class, StandardError.class end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/indexerror.rb0000644000000000000000000000013115077107276021622 xustar0030 mtime=1761382078.139420479 29 atime=1761382080.16341122 30 ctime=1761382108.508302092 nghttp2-1.68.0/third-party/mruby/test/t/indexerror.rb0000644000175100017510000000015015077107276022207 0ustar00runnerrunner## # IndexError ISO Test assert('IndexError', '15.2.33') do assert_equal Class, IndexError.class end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/typeerror.rb0000644000000000000000000000013215077107276021475 xustar0030 mtime=1761382078.141420469 30 atime=1761382080.164411215 30 ctime=1761382108.523302049 nghttp2-1.68.0/third-party/mruby/test/t/typeerror.rb0000644000175100017510000000014515077107276022065 0ustar00runnerrunner## # TypeError ISO Test assert('TypeError', '15.2.29') do assert_equal Class, TypeError.class end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/nil.rb0000644000000000000000000000013215077107276020224 xustar0030 mtime=1761382078.140420474 30 atime=1761382080.164411215 30 ctime=1761382108.522302052 nghttp2-1.68.0/third-party/mruby/test/t/nil.rb0000644000175100017510000000133515077107276020616 0ustar00runnerrunner## # NilClass ISO Test assert('NilClass', '15.2.4') do assert_equal Class, NilClass.class end assert('NilClass', '15.2.4.1') do assert_equal NilClass, nil.class assert_false NilClass.method_defined? :new end assert('NilClass#&', '15.2.4.3.1') do assert_false nil.&(true) assert_false nil.&(nil) end assert('NilClass#^', '15.2.4.3.2') do assert_true nil.^(true) assert_false nil.^(false) end assert('NilClass#|', '15.2.4.3.3') do assert_true nil.|(true) assert_false nil.|(false) end assert('NilClass#nil?', '15.2.4.3.4') do assert_true nil.nil? end assert('NilClass#to_s', '15.2.4.3.5') do assert_equal '', nil.to_s end assert('safe navigation') do assert_nil nil&.size assert_equal 0, []&.size end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/bs_block.rb0000644000000000000000000000013215077107276021220 xustar0030 mtime=1761382078.139420479 30 atime=1761382080.162411224 30 ctime=1761382108.529302032 nghttp2-1.68.0/third-party/mruby/test/t/bs_block.rb0000644000175100017510000002031415077107276021610 0ustar00runnerrunner## # Bootstrap tests for blocks assert('BS Block 1') do assert_equal(1) do 1.times{ begin a = 1 ensure foo = nil end } end end assert('BS Block 2') do assert_equal 2, [1,2,3].find{|x| x == 2} end assert('BS Block 3') do class E include Enumerable def each(&block) [1, 2, 3].each(&block) end end assert_equal 2, E.new.find {|x| x == 2 } end assert('BS Block 3') do sum = 0 for x in [1, 2, 3] sum += x end assert_equal 6, sum end assert('BS Block 4') do sum = 0 for x in (1..5) sum += x end assert_equal 15, sum end assert('BS Block 5') do sum = 0 for x in [] sum += x end assert_equal 0, sum end assert('BS Block 6') do ans = [] assert_equal(1) do 1.times{ for n in 1..3 a = n ans << a end } end end assert('BS Block 7') do ans = [] assert_equal((1..3)) do for m in 1..3 for n in 2..4 a = [m, n] ans << a end end end end assert('BS Block 8') do assert_equal [1, 2, 3], (1..3).to_a end assert('BS Block 9') do assert_equal([4, 8, 12]) do (1..3).map{|e| e * 4 } end end assert('BS Block 10') do def m yield end def n yield end assert_equal(100) do m{ n{ 100 } } end end assert('BS Block 11') do def m yield 1 end assert_equal(20) do m{|ib| m{|jb| i = 20 } } end end assert('BS Block 12') do def m yield 1 end assert_equal(2) do m{|ib| m{|jb| ib = 20 kb = 2 } } end end assert('BS Block 13') do def iter1 iter2{ yield } end def iter2 yield end assert_equal(3) do iter1{ jb = 2 iter1{ jb = 3 } jb } end end assert('BS Block 14') do def iter1 iter2{ yield } end def iter2 yield end assert_equal(2) do iter1{ jb = 2 iter1{ jb } jb } end end assert('BS Block 15') do def m yield 1 end assert_equal(2) do m{|ib| ib*2 } end end assert('BS Block 16') do def m yield 12345, 67890 end assert_equal(92580) do m{|ib,jb| ib*2+jb } end end assert('BS Block 17') do def iter yield 10 end a = nil assert_equal [10, nil] do [iter{|a| a }, a] end end assert('BS Block 18') do def iter yield 10 end assert_equal(21) do iter{|a| iter{|a| a + 1 } + a } end end assert('BS Block 19') do def iter yield 10, 20, 30, 40 end a = b = c = d = nil assert_equal([10, 20, 30, 40, nil, nil, nil, nil]) do iter{|a, b, c, d| [a, b, c, d] } + [a, b, c, d] end end assert('BS Block 20') do def iter yield 10, 20, 30, 40 end a = b = nil assert_equal([10, 20, 30, 40, nil, nil]) do iter{|a, b, c, d| [a, b, c, d] } + [a, b] end end assert('BS Block 21') do def iter yield 1, 2 end assert_equal([1, [2]]) do iter{|a, *b| [a, b] } end end assert('BS Block 22') do def iter yield 1, 2 end assert_equal([[1, 2]]) do iter{|*a| [a] } end end assert('BS Block 23') do def iter yield 1, 2 end assert_equal([1, 2, []]) do iter{|a, b, *c| [a, b, c] } end end assert('BS Block 24') do def m yield end assert_equal(1) do m{ 1 } end end assert('BS Block 25') do def m yield 123 end assert_equal(15129) do m{|ib| m{|jb| ib*jb } } end end assert('BS Block 26') do def m(a) yield a end assert_equal(2) do m(1){|ib| m(2){|jb| ib*jb } } end end assert('BS Block 27') do sum = 0 3.times{|ib| 2.times{|jb| sum += ib + jb }} assert_equal sum, 9 end assert('BS Block 28') do assert_equal(10) do 3.times{ break 10 } end end assert('BS Block 29') do def iter yield 1,2,3 end assert_equal([1, 2]) do iter{|i, j| [i, j] } end end assert('BS Block 30') do def iter yield 1 end assert_equal([1, nil]) do iter{|i, j| [i, j] } end end assert('BS Block [ruby-dev:31147]') do def m yield end assert_nil m{|&b| b} end assert('BS Block [ruby-dev:31160]') do def m yield end assert_nil m {|(v,(*))|} end assert('BS Block [issue #750]') do def m(a, *b) yield end args = [1, 2, 3] assert_equal m(*args){ 1 }, 1 end assert('BS Block 31') do def m yield end assert_nil m {|((*))|} end assert('BS Block [ruby-dev:31440]') do def m yield [0] end assert_equal m{|v, &b| v}, [0] end assert('BS Block 32') do r = false; 1.times{|&b| r = b} assert_equal NilClass, r.class end assert('BS Block [ruby-core:14395]') do assert_nothing_raised do class Controller def respond_to(&block) responder = Responder.new block.call(responder) responder.respond end def test_for_bug respond_to{|format| format.js{ "in test" render{|obj| obj } } } end def render(&block) "in render" end end class Responder def method_missing(symbol, &block) "enter method_missing" @response = Proc.new{ 'in method missing' block.call } "leave method_missing" end def respond @response.call end end t = Controller.new t.test_for_bug end end assert("BS Block 33") do module TestReturnFromNestedBlock def self.test 1.times do 1.times do return :ok end end :bad end end assert_equal :ok, TestReturnFromNestedBlock.test end assert("BS Block 34") do module TestReturnFromNestedBlock_BSBlock34 def self.test 1.times do while true return :ok end end :bad end end assert_equal :ok, TestReturnFromNestedBlock_BSBlock34.test end assert("BS Block 35") do module TestReturnFromNestedBlock_BSBlock35 def self.test 1.times do until false return :ok end end :bad end end assert_equal :ok, TestReturnFromNestedBlock_BSBlock35.test end assert('BS Block 36') do def iter yield 1, 2, 3, 4, 5 end assert_equal([1, 2, [3, 4], 5]) do iter{|a, b, *c, d| [a, b, c, d] } end end assert('BS Block 37') do def iter yield 1, 2, 3 end assert_equal([1, 2, [], 3]) do iter{|a, b, *c, d| [a, b, c, d] } end end assert('BS Block 38') do def iter yield 1,2,3,4,5,6 end assert_equal [1,2,3,4,5], iter{|a,b,c=:c,d,e| [a,b,c,d,e]} end assert('BS Block 39') do def iter yield 1 end assert_equal([1, 2, nil]) do iter{|a, b=2, c| [a, b, c] } end end assert('BS Block 40 (https://github.com/mruby/mruby/issues/6411)') do assert_equal "GOOD" do Object.new.instance_eval do def test(&b) if b b.call else test { return "GOOD" } end "BAD" end test end end assert_equal "GOOD" do Object.new.instance_eval do # since Kernel#proc is defined in proc-ext def make_proc(&b) b end def chocolate(&b) biscuit(&b) end def biscuit(&b) if b b.call else b = make_proc { return "GOOD" } chocolate(&b) end "BAD" end biscuit end end assert_equal [0, 1, 2, 3] do Object.new.instance_eval do def test(a = [], &b) if b b.call else if a.empty? a << 0 test(a) else a << 1 test(a) { return 1 } end a << 2 end a << 3 end test end end assert_equal [0, 1, 3, 2, 3, 2, 3] do Object.new.instance_eval do def test(a = [], &b) if b b.call else if a.empty? a << 0 test(a) else a << 1 test(a, &-> { return 1 }) end a << 2 end a << 3 end test end end end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/module.rb0000644000000000000000000000013115077107276020726 xustar0030 mtime=1761382078.140420474 29 atime=1761382080.16341122 30 ctime=1761382108.503302107 nghttp2-1.68.0/third-party/mruby/test/t/module.rb0000644000175100017510000005340315077107276021324 0ustar00runnerrunner## # Module ISO Test def labeled_module(name, &block) Module.new do (class <", m.to_s) assert_not_predicate(m, :frozen?) assert_not_predicate(TestModuleDup.freeze.dup, :frozen?) end assert('Module#define_method') do c = Class.new { define_method(:m1) { :ok } define_method(:m2, Proc.new { :ok }) } assert_equal c.new.m1, :ok assert_equal c.new.m2, :ok assert_raise(TypeError) do Class.new { define_method(:n1, nil) } end end # @!group prepend assert('Module#prepend') do module M0 def m1; [:M0] end end module M1 def m1; [:M1, super, :M1] end end module M2 def m1; [:M2, super, :M2] end end M3 = Module.new do def m1; [:M3, super, :M3] end end module M4 def m1; [:M4, super, :M4] end end class P0 include M0 prepend M1 def m1; [:C0, super, :C0] end end class P1 < P0 prepend M2, M3 include M4 def m1; [:C1, super, :C1] end end obj = P1.new expected = [:M2,[:M3,[:C1,[:M4,[:M1,[:C0,[:M0],:C0],:M1],:M4],:C1],:M3],:M2] assert_equal(expected, obj.m1) end assert('Module#prepend result') do module TestPrepended; end module TestPrependResult @prepend_result = prepend TestPrepended class << self attr_reader :prepend_result end end assert_equal TestPrependResult, TestPrependResult.prepend_result end # mruby shouldn't be affected by this since there is # no visibility control (yet) assert('Module#prepend public') do assert_nothing_raised('ruby/ruby #8846') do Class.new.prepend(Module.new) end end assert('Module#prepend inheritance') do bug6654 = '[ruby-core:45914]' a = labeled_module('a') b = labeled_module('b') { include a } c = labeled_module('c') { prepend b } #assert bug6654 do # the Module#< operator should be used here instead, but we don't have it assert_include(c.ancestors, a) assert_include(c.ancestors, b) #end bug8357 = '[ruby-core:54736] [Bug #8357]' b = labeled_module('b') { prepend a } c = labeled_class('c') { include b } #assert bug8357 do # the Module#< operator should be used here instead, but we don't have it assert_include(c.ancestors, a) assert_include(c.ancestors, b) #end bug8357 = '[ruby-core:54742] [Bug #8357]' assert_kind_of(b, c.new, bug8357) end assert 'Module#prepend + Class#ancestors' do bug6658 = '[ruby-core:45919]' m = labeled_module("m") c = labeled_class("c") {prepend m} assert_equal([m, c], c.ancestors[0, 2], bug6658) bug6662 = '[ruby-dev:45868]' c2 = labeled_class("c2", c) as = c2.ancestors assert_equal([c2, m, c, Object], as[0..as.index(Object)], bug6662) end assert 'Module#prepend + Module#ancestors' do bug6659 = '[ruby-dev:45861]' m0 = labeled_module("m0") { def x; [:m0, *super] end } m1 = labeled_module("m1") { def x; [:m1, *super] end; prepend m0 } m2 = labeled_module("m2") { def x; [:m2, *super] end; prepend m1 } c0 = labeled_class("c0") { def x; [:c0] end } c1 = labeled_class("c1") { def x; [:c1] end; prepend m2 } c2 = labeled_class("c2", c0) { def x; [:c2, *super] end; include m2 } # assert_equal([m0, m1], m1.ancestors, bug6659) # bug6662 = '[ruby-dev:45868]' assert_equal([m0, m1, m2], m2.ancestors, bug6662) assert_equal([m0, m1, m2, c1], c1.ancestors[0, 4], bug6662) assert_equal([:m0, :m1, :m2, :c1], c1.new.x) assert_equal([c2, m0, m1, m2, c0], c2.ancestors[0, 5], bug6662) assert_equal([:c2, :m0, :m1, :m2, :c0], c2.new.x) # m3 = labeled_module("m3") { include m1; prepend m1 } assert_equal([m3, m0, m1], m3.ancestors) m3 = labeled_module("m3") { prepend m1; include m1 } assert_equal([m0, m1, m3], m3.ancestors) m3 = labeled_module("m3") { prepend m1; prepend m1 } assert_equal([m0, m1, m3], m3.ancestors) m3 = labeled_module("m3") { include m1; include m1 } assert_equal([m3, m0, m1], m3.ancestors) end assert 'cyclic Module#prepend' do bug7841 = '[ruby-core:52205] [Bug #7841]' m1 = Module.new m2 = Module.new m1.instance_eval { prepend(m2) } assert_raise(ArgumentError, bug7841) do m2.instance_eval { prepend(m1) } end end # these assertions will not run without a #assert_separately method #assert 'test_prepend_optmethod' do # bug7983 = '[ruby-dev:47124] [Bug #7983]' # assert_separately [], %{ # module M # def /(other) # to_f / other # end # end # Integer.send(:prepend, M) # assert_equal(0.5, 1 / 2, "#{bug7983}") # } # assert_equal(0, 1 / 2) #end # mruby has no visibility control # assert 'Module#prepend visibility' do # bug8005 = '[ruby-core:53106] [Bug #8005]' # c = Class.new do # prepend Module.new {} # def foo() end # protected :foo # end # a = c.new # assert_true a.respond_to?(:foo), bug8005 # assert_nothing_raised(bug8005) {a.send :foo} # end # mruby has no visibility control # assert 'Module#prepend inherited visibility' do # bug8238 = '[ruby-core:54105] [Bug #8238]' # module Test4PrependVisibilityInherited # class A # def foo() A; end # private :foo # end # class B < A # public :foo # prepend Module.new # end # end # assert_equal(Test4PrependVisibilityInherited::A, Test4PrependVisibilityInherited::B.new.foo, "#{bug8238}") # end # assert 'Module#prepend super in alias' do # skip "super does not currently work in aliased methods" # bug7842 = '[Bug #7842]' # p = labeled_module("P") do # def m; "P"+super; end # end # a = labeled_class("A") do # def m; "A"; end # end # b = labeled_class("B", a) do # def m; "B"+super; end # alias m2 m # prepend p # alias m3 m # end # assert_nothing_raised do # assert_equal("BA", b.new.m2, bug7842) # end # assert_nothing_raised do # assert_equal("PBA", b.new.m3, bug7842) # end # end assert 'Module#prepend each class' do m = labeled_module("M") c1 = labeled_class("C1") {prepend m} c2 = labeled_class("C2", c1) {prepend m} assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should be able to prepend each class") end assert 'Module#prepend no duplication' do m = labeled_module("M") c = labeled_class("C") {prepend m; prepend m} assert_equal([m, c], c.ancestors[0, 2], "should never duplicate") end assert 'Module#prepend in superclass' do m = labeled_module("M") c1 = labeled_class("C1") c2 = labeled_class("C2", c1) {prepend m} c1.class_eval {prepend m} assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should accessible prepended module in superclass") end # requires #assert_separately #assert 'Module#prepend call super' do # assert_separately([], <<-'end;') #do # bug10847 = '[ruby-core:68093] [Bug #10847]' # module M; end # Float.prepend M # assert_nothing_raised(SystemStackError, bug10847) do # 0.3.numerator # end # end #end assert 'Module#prepend to frozen class' do assert_raise(FrozenError) { Class.new.freeze.prepend Module.new } end # @!endgroup prepend assert('Module#to_s') do module Outer class Inner; end const_set :SetInner, Class.new end assert_equal 'Outer', Outer.to_s assert_equal 'Outer::Inner', Outer::Inner.to_s assert_equal 'Outer::SetInner', Outer::SetInner.to_s outer = Module.new do const_set :SetInner, Class.new end Object.const_set :SetOuter, outer assert_equal 'SetOuter', SetOuter.to_s assert_equal 'SetOuter::SetInner', SetOuter::SetInner.to_s assert_match "#", Module.new.to_s assert_match "#", Class.new.to_s assert_equal "FrozenClassToS", (FrozenClassToS = Class.new.freeze).to_s assert_equal "Outer::A", (Outer::A = Module.new.freeze).to_s assert_match "#::A", (Module.new::A = Class.new.freeze).to_s end assert('Module#inspect') do module Test4to_sModules end assert_equal 'Test4to_sModules', Test4to_sModules.inspect end assert('Issue 1467') do module M1 def initialize super() end end class C1 include M1 def initialize super() end end class C2 include M1 end assert_kind_of(M1, C1.new) assert_kind_of(M1, C2.new) end assert('clone Module') do module M1 def foo true end end class B include M1.clone end assert_true(B.new.foo) end assert('method visibility') do class CallTypeTest def test_private(&block) func(&block) end def test_protected(&block) self.func(&block) end private def func yield end end v = CallTypeTest.new assert_raise_with_message_pattern(NameError, "private method 'func' called for CallTypeTest") do v.func { :test } end assert_equal :test, v.test_private { :test } class CallTypeTest protected :func end assert_raise_with_message_pattern(NameError, "protected method 'func' called for CallTypeTest") do v.func { :test } end assert_equal :test, v.test_protected { :test } assert_equal :test, v.test_private { :test } class CallTypeTest public def public_func :test end public :func end assert_equal :test, v.public_func assert_equal :test, v.func { :test } assert_equal :test, v.test_protected { :test } assert_equal :test, v.test_private { :test } end assert('method visibility with meta programming') do assert_equal "GOOD!" do f = nil c = Class.new { private f = ->(&blk) { class_eval(&blk) } } f.call { def good! "GOOD!" end } c.new.good! end assert_equal "GOOD!" do c = Class.new c.class_eval { private c.class_eval { def good! "GOOD!" end } } c.new.good! end assert_raise NoMethodError do f = nil c = Class.new { private f = -> { def bad! "BAD!" end } } f.call c.new.bad! end end assert('Module#module_function') do module M def modfunc; end module_function :modfunc end assert_true M.respond_to?(:modfunc) assert_equal nil do M.modfunc end end assert('module with non-class/module outer raises TypeError') do assert_raise(TypeError) { module 0::M1 end } assert_raise(TypeError) { module []::M2 end } end assert('module to return the last value') do m = module M; :m end assert_equal(:m, m) end assert('module to return nil if body is empty') do assert_nil(module M end) end assert('get constant of parent module in singleton class; issue #3568') do actual = module GetConstantInSingletonTest EXPECTED = "value" class << self EXPECTED end end assert_equal("value", actual) end assert('shared empty iv_tbl (include)') do m1 = Module.new m2 = Module.new{include m1} c = Class.new{include m2} m1::CONST1 = 1 assert_equal 1, m2::CONST1 assert_equal 1, c::CONST1 m2::CONST2 = 2 assert_equal 2, c::CONST2 end assert('shared empty iv_tbl (prepend)') do m1 = Module.new m2 = Module.new{prepend m1} c = Class.new{include m2} m1::CONST1 = 1 assert_equal 1, m2::CONST1 assert_equal 1, c::CONST1 m2::CONST2 = 2 assert_equal 2, c::CONST2 end assert('constant lookup #6506') do Module.new do module X module A class WWW; end end end module X::Y; end module X::Y::Z extend X::A class << self assert_nothing_raised{WWW} end end end end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/methods.rb0000644000000000000000000000013115077107276021104 xustar0030 mtime=1761382078.140420474 29 atime=1761382080.16341122 30 ctime=1761382108.505302101 nghttp2-1.68.0/third-party/mruby/test/t/methods.rb0000644000175100017510000000671515077107276021506 0ustar00runnerrunner## # Chapter 13.3 "Methods" ISO Test assert('The alias statement', '13.3.6 a) 4)') do # check aliasing in all possible ways def alias_test_method_original; true; end alias alias_test_method_a alias_test_method_original alias :alias_test_method_b :alias_test_method_original assert_true(alias_test_method_original) assert_true(alias_test_method_a) assert_true(alias_test_method_b) end assert('The alias statement (overwrite original)', '13.3.6 a) 4)') do # check that an aliased method can be overwritten # without side effect def alias_test_method_original; true; end alias alias_test_method_a alias_test_method_original alias :alias_test_method_b :alias_test_method_original assert_true(alias_test_method_original) def alias_test_method_original; false; end assert_false(alias_test_method_original) assert_true(alias_test_method_a) assert_true(alias_test_method_b) end assert('The alias statement', '13.3.6 a) 5)') do # check that alias is raising NameError if # non-existing method should be undefined assert_raise(NameError) do alias new_name_a non_existing_method end assert_raise(NameError) do alias :new_name_b :non_existing_method end end assert('The undef statement', '13.3.7 a) 4)') do # check that undef is undefining method # based on the method name def existing_method_a; true; end def existing_method_b; true; end def existing_method_c; true; end def existing_method_d; true; end def existing_method_e; true; end def existing_method_f; true; end # check that methods are defined assert_true(existing_method_a, 'Method should be defined') assert_true(existing_method_b, 'Method should be defined') assert_true(existing_method_c, 'Method should be defined') assert_true(existing_method_d, 'Method should be defined') assert_true(existing_method_e, 'Method should be defined') assert_true(existing_method_f, 'Method should be defined') # undefine in all possible ways and check that method # is undefined undef existing_method_a assert_raise(NoMethodError) do existing_method_a end undef :existing_method_b assert_raise(NoMethodError) do existing_method_b end undef existing_method_c, existing_method_d assert_raise(NoMethodError) do existing_method_c end assert_raise(NoMethodError) do existing_method_d end undef :existing_method_e, :existing_method_f assert_raise(NoMethodError) do existing_method_e end assert_raise(NoMethodError) do existing_method_f end end assert('The undef statement (method undefined)', '13.3.7 a) 5)') do # check that undef is raising NameError if # non-existing method should be undefined assert_raise(NameError) do undef non_existing_method end assert_raise(NameError) do undef :non_existing_method end end assert('method_added hook') do c = Class.new do # method to retrieve @name def self.name; @name; end # hook method on method definition def self.method_added(name) @name = name; end # method definition def foo; end end assert_equal(:foo, c.name) c.define_method(:bar){} assert_equal(:bar, c.name) end assert('singleton_method_added hook') do a = Object.new # method to retrieve @name def a.name; @name; end # hook method on singleton method definition def a.singleton_method_added(name) @name = name; end # singleton method definition def a.foo; end assert_equal(:foo, a.name) class < { block_given? }[] end assert_false bg_try_in_block assert_true bg_try_in_block{} end assert('Kernel#class', '15.3.1.3.7') do assert_equal Module, Kernel.class end assert('Kernel#clone', '15.3.1.3.8') do class KernelCloneTest def initialize @v = 0 end def get @v end def set(v) @v = v end end a = KernelCloneTest.new a.set(1) b = a.clone def a.test end a.set(2) c = a.clone immutables = [ 1, :foo, true, false, nil ] error_count = 0 immutables.each do |i| begin i.clone rescue TypeError error_count += 1 end end assert_equal 2, a.get assert_equal 1, b.get assert_equal 2, c.get assert_true a.respond_to?(:test) assert_false b.respond_to?(:test) assert_true c.respond_to?(:test) a.freeze d = a.clone assert_true d.frozen? end assert('Kernel#dup', '15.3.1.3.9') do class KernelDupTest def initialize @v = 0 end def get @v end def set(v) @v = v end end a = KernelDupTest.new a.set(1) b = a.dup def a.test end a.set(2) c = a.dup assert_equal 2, a.get assert_equal 1, b.get assert_equal 2, c.get assert_true a.respond_to?(:test) assert_false b.respond_to?(:test) assert_false c.respond_to?(:test) end assert('Kernel#dup class') do assert_nothing_raised do Array.dup.new(200) Range.dup.new(2, 3) String.dup.new("a"*50) end end # Kernel#eval is provided by mruby-eval mrbgem '15.3.1.3.12' assert('Kernel#extend', '15.3.1.3.13') do class Test4ExtendClass end module Test4ExtendModule def test_method; end end a = Test4ExtendClass.new a.extend(Test4ExtendModule) b = Test4ExtendClass.new assert_true a.respond_to?(:test_method) assert_false b.respond_to?(:test_method) assert_raise(FrozenError) { Object.new.freeze.extend(Test4ExtendModule) } assert_raise(FrozenError, TypeError) { :sym.extend(Test4ExtendModule) } end assert('Kernel#extend works on toplevel', '15.3.1.3.13') do module Test4ExtendModule def test_method; end end # This would crash... extend(Test4ExtendModule) assert_true respond_to?(:test_method) end assert('Kernel#freeze') do obj = Object.new assert_equal obj, obj.freeze assert_equal 0, 0.freeze assert_equal :a, :a.freeze assert_equal true, true.freeze assert_equal false, false.freeze assert_equal nil, nil.freeze skip unless Object.const_defined?(:Float) assert_equal 0.0, 0.0.freeze end assert('Kernel#frozen?') do assert_false "".frozen? assert_true "".freeze.frozen? assert_true 0.frozen? assert_true :a.frozen? assert_true true.frozen? assert_true false.frozen? assert_true nil.frozen? skip unless Object.const_defined?(:Float) assert_true 0.0.frozen? end assert('Kernel#hash', '15.3.1.3.15') do assert_equal hash, hash end assert('Kernel#inspect', '15.3.1.3.17') do s = inspect assert_equal String, s.class assert_equal "main", s end assert('Kernel#is_a?', '15.3.1.3.24') do assert_true is_a?(Kernel) assert_false is_a?(Array) assert_raise TypeError do 42.is_a?(42) end end assert('Kernel#iterator?', '15.3.1.3.25') do assert_false iterator? end assert('Kernel#kind_of?', '15.3.1.3.26') do assert_true kind_of?(Kernel) assert_false kind_of?(Array) end assert('Kernel#lambda', '15.3.1.3.27') do l = lambda do true end m = lambda(&l) assert_true l.call assert_equal Proc, l.class assert_true m.call assert_equal Proc, m.class end assert('Kernel#loop', '15.3.1.3.29') do i = 0 loop do i += 1 break if i == 100 end assert_equal i, 100 end assert('Kernel#method_missing', '15.3.1.3.30') do class MMTestClass def method_missing(sym) "A call to #{sym}" end end mm_test = MMTestClass.new assert_equal 'A call to no_method_named_this', mm_test.no_method_named_this class SuperMMTestClass < MMTestClass def no_super_method_named_this super end end super_mm_test = SuperMMTestClass.new assert_equal 'A call to no_super_method_named_this', super_mm_test.no_super_method_named_this class NoSuperMethodTestClass def no_super_method_named_this super end end no_super_test = NoSuperMethodTestClass.new msg = "no superclass method 'no_super_method_named_this' for NoSuperMethodTestClass" assert_raise_with_message(NoMethodError, msg) do no_super_test.no_super_method_named_this end a = String.new msg = "undefined method 'no_method_named_this' for String" assert_raise_with_message(NoMethodError, msg) do a.no_method_named_this end end assert('Kernel#nil?', '15.3.1.3.32') do assert_false nil? end assert('Kernel#object_id', '15.3.1.3.33') do a = "" b = "" assert_not_equal a.object_id, b.object_id assert_kind_of Numeric, object_id assert_kind_of Numeric, "".object_id assert_kind_of Numeric, true.object_id assert_kind_of Numeric, false.object_id assert_kind_of Numeric, nil.object_id assert_kind_of Numeric, :no.object_id assert_kind_of Numeric, 1.object_id assert_kind_of Numeric, 1.0.object_id end # Kernel#p test is skipped due to the side effect. '15.3.1.3.34' #assert('Kernel#p', '15.3.1.3.34') do # assert_equal nil, p # assert_equal nil, p(p) # assert_equal [:a, :b], p(:a, :b) #end # Kernel#print is defined in mruby-io mrbgem. '15.3.1.3.35' # Kernel#puts is defined in mruby-io mrbgem. '15.3.1.3.39' assert('Kernel#raise', '15.3.1.3.40') do assert_raise RuntimeError do raise end assert_raise RuntimeError do raise RuntimeError.new end end assert('Kernel#remove_instance_variable', '15.3.1.3.41') do class Test4RemoveInstanceVar attr_reader :var def initialize @var = 99 end def remove remove_instance_variable(:@var) end end tri = Test4RemoveInstanceVar.new assert_equal 99, tri.var assert_equal 99, tri.remove assert_equal nil, tri.var assert_raise(NameError) { tri.remove } assert_raise(NameError) { tri.remove_instance_variable(:var) } assert_raise(FrozenError) { tri.freeze.remove } assert_raise(FrozenError, NameError) { :a.remove_instance_variable(:@v) } end # Kernel#require is defined in mruby-require. '15.3.1.3.42' assert('Kernel#respond_to?', '15.3.1.3.43') do class Test4RespondTo def valid_method; end def test_method; end undef test_method end assert_raise TypeError do Test4RespondTo.new.respond_to?(1) end assert_raise ArgumentError do Test4RespondTo.new.respond_to? end assert_raise ArgumentError do Test4RespondTo.new.respond_to? :a, true, :aa end assert_true respond_to?(:nil?) assert_true Test4RespondTo.new.respond_to?(:valid_method) assert_true Test4RespondTo.new.respond_to?('valid_method') assert_false Test4RespondTo.new.respond_to?(:test_method) end assert('Kernel#to_s', '15.3.1.3.46') do assert_equal to_s.class, String end assert('Kernel#!=') do str1 = "hello" str2 = str1 str3 = "world" assert_false (str1[1] != 'e') assert_true (str1 != str3) assert_false (str2 != str1) end # operator "!~" is defined in ISO Ruby 11.4.4. assert('Kernel#!~') do x = "x" def x.=~(other) other == "x" end assert_false x !~ "x" assert_true x !~ "z" y = "y" def y.=~(other) other == "y" end def y.!~(other) other == "not y" end assert_false y !~ "y" assert_false y !~ "z" assert_true y !~ "not y" end assert('Kernel#respond_to_missing?') do class Test4RespondToMissing def respond_to_missing?(method_name, include_private = false) method_name == :a_method end end assert_true Test4RespondToMissing.new.respond_to?(:a_method) assert_false Test4RespondToMissing.new.respond_to?(:no_method) end assert('stack extend') do def recurse(count, stop) return count if count > stop recurse(count+1, stop) end assert_equal 6, recurse(0, 5) end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/false.rb0000644000000000000000000000013115077107276020533 xustar0030 mtime=1761382078.139420479 29 atime=1761382080.16341122 30 ctime=1761382108.482302167 nghttp2-1.68.0/third-party/mruby/test/t/false.rb0000644000175100017510000000121215077107276021120 0ustar00runnerrunner## # FalseClass ISO Test assert('FalseClass', '15.2.6') do assert_equal Class, FalseClass.class end assert('FalseClass false', '15.2.6.1') do assert_false false assert_equal FalseClass, false.class assert_false FalseClass.method_defined? :new end assert('FalseClass#&', '15.2.6.3.1') do assert_false false.&(true) assert_false false.&(false) end assert('FalseClass#^', '15.2.6.3.2') do assert_true false.^(true) assert_false false.^(false) end assert('FalseClass#to_s', '15.2.6.3.3') do assert_equal 'false', false.to_s end assert('FalseClass#|', '15.2.6.3.4') do assert_true false.|(true) assert_false false.|(false) end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/proc.rb0000644000000000000000000000013215077107276020405 xustar0030 mtime=1761382078.140420474 30 atime=1761382080.164411215 30 ctime=1761382108.514302075 nghttp2-1.68.0/third-party/mruby/test/t/proc.rb0000644000175100017510000001012515077107276020774 0ustar00runnerrunner## # Proc ISO Test assert('Proc', '15.2.17') do assert_equal Class, Proc.class end assert('Proc.new', '15.2.17.3.1') do assert_raise ArgumentError do Proc.new end assert_equal (Proc.new {}).class, Proc assert_raise LocalJumpError do Proc.new{ break }.call end end assert('Proc#[]', '15.2.17.4.1') do a = 0 b = Proc.new { a += 1 } b.[] a2 = 0 b2 = Proc.new { |i| a2 += i } b2.[](5) assert_equal 1, a assert_equal 5, a2 end assert('Proc#arity', '15.2.17.4.2') do a = Proc.new {|x, y|}.arity b = Proc.new {|x, *y, z|}.arity c = Proc.new {|x=0, y|}.arity d = Proc.new {|(x, y), z=0|}.arity assert_equal 2, a assert_equal(-3, b) assert_equal 1, c assert_equal 1, d e = ->(x=0, y){}.arity f = ->((x, y), z=0){}.arity g = ->(x=0){}.arity assert_equal(-2, e) assert_equal(-2, f) assert_equal(-1, g) end assert('Proc#call', '15.2.17.4.3') do a = 0 b = Proc.new { a += 1 } b.call a2 = 0 b2 = Proc.new { |i| a2 += i } b2.call(5) assert_equal 1, a assert_equal 5, a2 end assert('Proc#call proc args pos block') do pr = Proc.new {|a,b,&c| [a, b, c.class, c&&c.call(:x)] } assert_equal [nil, nil, Proc, :proc], (pr.call(){ :proc }) assert_equal [1, nil, Proc, :proc], (pr.call(1){ :proc }) assert_equal [1, 2, Proc, :proc], (pr.call(1, 2){ :proc }) assert_equal [1, 2, Proc, :proc], (pr.call(1, 2, 3){ :proc }) assert_equal [1, 2, Proc, :proc], (pr.call(1, 2, 3, 4){ :proc }) assert_equal [nil, nil, Proc, :x], (pr.call(){|x| x}) assert_equal [1, nil, Proc, :x], (pr.call(1){|x| x}) assert_equal [1, 2, Proc, :x], (pr.call(1, 2){|x| x}) assert_equal [1, 2, Proc, :x], (pr.call(1, 2, 3){|x| x}) assert_equal [1, 2, Proc, :x], (pr.call(1, 2, 3, 4){|x| x}) end assert('Proc#call proc args pos rest post') do pr = Proc.new {|a,b,*c,d,e| [a,b,c,d,e] } assert_equal [nil, nil, [], nil, nil], pr.call() assert_equal [1, nil, [], nil, nil], pr.call(1) assert_equal [1, 2, [], nil, nil], pr.call(1,2) assert_equal [1, 2, [], 3, nil], pr.call(1,2,3) assert_equal [1, 2, [], 3, 4], pr.call(1,2,3,4) assert_equal [1, 2, [3], 4, 5], pr.call(1,2,3,4,5) assert_equal [1, 2, [3, 4], 5, 6], pr.call(1,2,3,4,5,6) assert_equal [1, 2, [3, 4, 5], 6,7], pr.call(1,2,3,4,5,6,7) assert_equal [nil, nil, [], nil, nil], pr.call([]) assert_equal [1, nil, [], nil, nil], pr.call([1]) assert_equal [1, 2, [], nil, nil], pr.call([1,2]) assert_equal [1, 2, [], 3, nil], pr.call([1,2,3]) assert_equal [1, 2, [], 3, 4], pr.call([1,2,3,4]) assert_equal [1, 2, [3], 4, 5], pr.call([1,2,3,4,5]) assert_equal [1, 2, [3, 4], 5, 6], pr.call([1,2,3,4,5,6]) assert_equal [1, 2, [3, 4, 5], 6,7], pr.call([1,2,3,4,5,6,7]) end assert('Proc#return_does_not_break_self') do class TestClass attr_accessor :block def initialize end def return_array @block = Proc.new { self } return [] end def return_instance_variable @block = Proc.new { self } return @block end def return_const_fixnum @block = Proc.new { self } return 123 end def return_nil @block = Proc.new { self } return nil end end c = TestClass.new assert_equal [], c.return_array assert_equal c, c.block.call c.return_instance_variable assert_equal c, c.block.call assert_equal 123, c.return_const_fixnum assert_equal c, c.block.call assert_equal nil, c.return_nil assert_equal c, c.block.call end assert('call Proc#initialize if defined') do a = [] c = Class.new(Proc) do define_method(:initialize) do a << :ok end end assert_kind_of c, c.new{} assert_equal [:ok], a end assert('&obj call to_proc if defined') do pr = Proc.new{} def mock(&b) b end assert_same pr, mock(&pr) assert_equal pr, mock(&pr) obj = Object.new def obj.to_proc Proc.new{ :from_to_proc } end assert_equal :from_to_proc, mock(&obj).call assert_raise(TypeError){ mock(&(Object.new)) } end assert('Creation of a proc through the block of a method') do def m(&b) b end assert_equal m{}.class, Proc assert_raise LocalJumpError do m{ break }.call end end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/unicode.rb0000644000000000000000000000013215077107276021070 xustar0030 mtime=1761382078.141420469 30 atime=1761382080.164411215 30 ctime=1761382108.496302127 nghttp2-1.68.0/third-party/mruby/test/t/unicode.rb0000644000175100017510000000213615077107276021462 0ustar00runnerrunner# Test of the \u notation assert('bare \u notation test') do # Minimum and maximum one byte characters assert_equal("\x00", "\u0000") assert_equal("\x7F", "\u007F") # Minimum and maximum two byte characters assert_equal("\xC2\x80", "\u0080") assert_equal("\xDF\xBF", "\u07FF") # Minimum and maximum three byte characters assert_equal("\xE0\xA0\x80", "\u0800") assert_equal("\xEF\xBF\xBF", "\uFFFF") # Four byte characters require the \U notation end assert('braced \u notation test') do # Minimum and maximum one byte characters assert_equal("\x00", "\u{0000}") assert_equal("\x7F", "\u{007F}") # Minimum and maximum two byte characters assert_equal("\xC2\x80", "\u{0080}") assert_equal("\xDF\xBF", "\u{07FF}") # Minimum and maximum three byte characters assert_equal("\xE0\xA0\x80", "\u{0800}") assert_equal("\xEF\xBF\xBF", "\u{FFFF}") # Minimum and maximum four byte characters assert_equal("\xF0\x90\x80\x80", "\u{10000}") assert_equal("\xF4\x8F\xBF\xBF", "\u{10FFFF}") end assert('braced multiple \u notation test') do assert_equal("ABC", "\u{41 42 43}") end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/object.rb0000644000000000000000000000013215077107276020710 xustar0030 mtime=1761382078.140420474 30 atime=1761382080.164411215 30 ctime=1761382108.525302043 nghttp2-1.68.0/third-party/mruby/test/t/object.rb0000644000175100017510000000027115077107276021300 0ustar00runnerrunner## # Object ISO Test assert('Object', '15.2.1') do assert_equal Class, Object.class end assert('Object superclass', '15.2.1.2') do assert_equal BasicObject, Object.superclass end nghttp2-1.68.0/third-party/mruby/test/t/PaxHeaders/rangeerror.rb0000644000000000000000000000013215077107276021610 xustar0030 mtime=1761382078.140420474 30 atime=1761382080.164411215 30 ctime=1761382108.485302159 nghttp2-1.68.0/third-party/mruby/test/t/rangeerror.rb0000644000175100017510000000015015077107276022174 0ustar00runnerrunner## # RangeError ISO Test assert('RangeError', '15.2.26') do assert_equal Class, RangeError.class end nghttp2-1.68.0/third-party/mruby/test/PaxHeaders/assert.rb0000644000000000000000000000013215077107276020500 xustar0030 mtime=1761382078.138420483 30 atime=1761382080.162411224 30 ctime=1761382108.535302014 nghttp2-1.68.0/third-party/mruby/test/assert.rb0000644000175100017510000002546615077107276021105 0ustar00runnerrunner$undefined = Object.new $ok_test = 0 $ko_test = 0 $kill_test = 0 $warning_test = 0 $skip_test = 0 $asserts = [] $test_start = Time.now if Object.const_defined?(:Time) # For bintest on Ruby unless RUBY_ENGINE == "mruby" def t_print(*args) print(*args) $stdout.flush nil end def _str_match?(pattern, str) File.fnmatch?(pattern, str, File::FNM_EXTGLOB|File::FNM_DOTMATCH) end end class Array def _assertion_join join("-") end end class String def _assertion_indent(indent) indent = indent.to_s off = 0 str = self while nl = index("\n", off) nl += 1 nl += 1 while slice(nl) == "\n" break if nl >= size str = indent.dup if off == 0 str += slice(off, nl - off) + indent off = nl end if off == 0 str = indent + self else str += slice(off..-1) end str end end ## # Create the assertion in a readable way def assertion_string(err, str, iso=nil, e=nil, bt=nil) msg = "#{err}#{str}" msg += " [#{iso}]" if iso && !iso.empty? msg += " => #{e}" if e && !e.to_s.empty? if Object.const_defined?(:GEMNAME) msg += " (#{GEMNAME == 'mruby-test' ? 'core' : "mrbgems: #{GEMNAME}"})" end if $mrbtest_assert $mrbtest_assert.each do |idx, assert_msg, diff| msg += "\n - Assertion[#{idx}]" msg += " #{assert_msg}." if assert_msg && !assert_msg.empty? msg += "\n#{diff}" if diff && !diff.empty? end end msg += "\nbacktrace:\n #{bt.join("\n ")}" if bt && !bt.empty? msg end ## # Verify a code block. # # str : A remark which will be printed in case # this assertion fails # iso : The ISO reference code of the feature # which will be tested by this # assertion def assert(str = 'assert', iso = '') t_print(str, (iso != '' ? " [#{iso}]" : ''), ' : ') if $mrbtest_verbose begin $mrbtest_child_noassert ||= [0] $mrbtest_child_noassert << 0 parent_asserts = $asserts $asserts = [] parent_mrbtest_assert = $mrbtest_assert $mrbtest_assert = [] if $mrbtest_assert_idx && !$mrbtest_assert_idx.empty? $mrbtest_assert_idx[-1] += 1 $mrbtest_assert_idx << 0 else $mrbtest_assert_idx = [0] class << $mrbtest_assert_idx alias to_s _assertion_join end end yield if $mrbtest_assert.size > 0 if $mrbtest_assert.size == $mrbtest_child_noassert[-1] $asserts.push(assertion_string('Skip: ', str, iso)) $mrbtest_child_noassert[-2] += 1 $skip_test += 1 t_print('?') else $asserts.push(assertion_string('Fail: ', str, iso)) $ko_test += 1 t_print('F') end elsif $mrbtest_assert_idx[-1] == 0 $asserts.push(assertion_string('Warn: ', str, iso, 'no assertion')) $warning_test += 1 t_print('W') else $ok_test += 1 t_print('.') end rescue MRubyTestSkip => e $asserts.push(assertion_string('Skip: ', str, iso, e)) $skip_test += 1 $mrbtest_child_noassert[-2] += 1 t_print('?') rescue Exception => e $asserts.push(assertion_string("#{e.class}: ", str, iso, e, e.backtrace)) $kill_test += 1 t_print('X') ensure if $mrbtest_assert_idx.size > 1 $asserts.each do |mesg| idx = $mrbtest_assert_idx[0..-2]._assertion_join mesg = mesg._assertion_indent(" ") # Give `mesg` as a `diff` argument to avoid adding extra periods. parent_mrbtest_assert << [idx, nil, mesg] end else parent_asserts.concat $asserts end $asserts = parent_asserts $mrbtest_assert = parent_mrbtest_assert $mrbtest_assert_idx.pop $mrbtest_assert_idx = nil if $mrbtest_assert_idx.empty? $mrbtest_child_noassert.pop nil end t_print("\n") if $mrbtest_verbose end def assertion_diff(exp, act) " Expected: #{exp.inspect}\n" \ " Actual: #{act.inspect}" end def assert_true(obj, msg = nil, diff = nil) if $mrbtest_assert_idx && $mrbtest_assert_idx.size > 0 $mrbtest_assert_idx[-1] += 1 unless obj == true diff ||= " Expected #{obj.inspect} to be true." $mrbtest_assert.push([$mrbtest_assert_idx.to_s, msg, diff]) end end obj end def assert_false(obj, msg = nil, diff = nil) unless obj == false diff ||= " Expected #{obj.inspect} to be false." end assert_true(!obj, msg, diff) end def assert_equal(exp, act_or_msg = nil, msg = nil, &block) ret, exp, act, msg = _eval_assertion(:==, exp, act_or_msg, msg, block) unless ret diff = assertion_diff(exp, act) end assert_true(ret, msg, diff) end def assert_not_equal(exp, act_or_msg = nil, msg = nil, &block) ret, exp, act, msg = _eval_assertion(:==, exp, act_or_msg, msg, block) if ret diff = " Expected #{act.inspect} to not be equal to #{exp.inspect}." end assert_true(!ret, msg, diff) end def assert_same(*args); _assert_same(true, *args) end def assert_not_same(*args); _assert_same(false, *args) end def _assert_same(affirmed, exp, act, msg = nil) unless ret = exp.equal?(act) == affirmed exp_str, act_str = [exp, act].map do |o| "#{o.inspect} (class=#{o.class}, oid=#{o.__id__})" end diff = " Expected #{act_str} to #{'not ' unless affirmed}be the same as #{exp_str}." end assert_true(ret, msg, diff) end def assert_nil(obj, msg = nil) unless ret = obj.nil? diff = " Expected #{obj.inspect} to be nil." end assert_true(ret, msg, diff) end def assert_not_nil(obj, msg = nil) if ret = obj.nil? diff = " Expected #{obj.inspect} to not be nil." end assert_false(ret, msg, diff) end def assert_include(*args); _assert_include(true, *args) end def assert_not_include(*args); _assert_include(false, *args) end def _assert_include(affirmed, collection, obj, msg = nil) unless ret = collection.include?(obj) == affirmed diff = " Expected #{collection.inspect} to #{'not ' unless affirmed}include #{obj.inspect}." end assert_true(ret, msg, diff) end def assert_predicate(*args); _assert_predicate(true, *args) end def assert_not_predicate(*args); _assert_predicate(false, *args) end def _assert_predicate(affirmed, obj, op, msg = nil) unless ret = obj.__send__(op) == affirmed diff = " Expected #{obj.inspect} to #{'not ' unless affirmed}be #{op}." end assert_true(ret, msg, diff) end def assert_operator(*args); _assert_operator(true, *args) end def assert_not_operator(*args); _assert_operator(false, *args) end def _assert_operator(affirmed, obj1, op, obj2 = $undefined, msg = nil) return _assert_predicate(affirmed, obj1, op, msg) if $undefined.equal?(obj2) unless ret = obj1.__send__(op, obj2) == affirmed diff = " Expected #{obj1.inspect} to #{'not ' unless affirmed}be #{op} #{obj2.inspect}." end assert_true(ret, msg, diff) end ## # Fail unless +str+ matches against +pattern+. # # +pattern+ is interpreted as pattern for File.fnmatch?. It may contain the # following metacharacters: # # * :: # Matches any string. # # ? :: # Matches any one character. # # [_SET_], [^_SET_] ([!_SET_]) :: # Matches any one character in _SET_. Behaves like character sets in # Regexp, including set negation ([^a-z]). # # {_A_,_B_} :: # Matches pattern _A_ or pattern _B_. # # \ :: # Escapes the next character. def assert_match(*args); _assert_match(true, *args) end def assert_not_match(*args); _assert_match(false, *args) end def _assert_match(affirmed, pattern, str, msg = nil) unless ret = _str_match?(pattern, str) == affirmed diff = " Expected #{pattern.inspect} to #{'not ' unless affirmed}match #{str.inspect}." end assert_true(ret, msg, diff) end ## # Fails unless +obj+ is a kind of +cls+. def assert_kind_of(cls, obj, msg = nil) unless ret = obj.kind_of?(cls) diff = " Expected #{obj.inspect} to be a kind of #{cls}, not #{obj.class}." end assert_true(ret, msg, diff) end ## # Fails unless +exp+ is equal to +act+ in terms of a Float def assert_float(exp, act, msg = nil) e, a = exp.to_f, act.to_f if e.finite? && a.finite? && (n = (e - a).abs) > Mrbtest::FLOAT_TOLERANCE flunk(msg, " Expected |#{exp} - #{act}| (#{n}) to be <= #{Mrbtest::FLOAT_TOLERANCE}.") elsif (e.infinite? || a.infinite?) && e != a || e.nan? && !a.nan? || !e.nan? && a.nan? flunk(msg, " Expected #{act} to be #{exp}.") else pass end end def assert_raise(*exc) msg = (exc.last.is_a? String) ? exc.pop : nil exc = exc.empty? ? StandardError : exc.size == 1 ? exc[0] : exc begin yield rescue *exc => e pass e rescue Exception => e diff = " #{exc} exception expected, not\n" \ " Class: <#{e.class}>\n" \ " Message: <#{e}>" flunk(msg, diff) else diff = " #{exc} expected but nothing was raised." flunk(msg, diff) end end def assert_nothing_raised(msg = nil) begin yield rescue Exception => e diff = " Exception raised:\n" \ " Class: <#{e.class}>\n" \ " Message: <#{e}>" flunk(msg, diff) else pass end end def assert_raise_with_message(*args, &block) _assert_raise_with_message(:plain, *args, &block) end def assert_raise_with_message_pattern(*args, &block) _assert_raise_with_message(:pattern, *args, &block) end def _assert_raise_with_message(type, exc, exp_msg, msg = nil, &block) e = msg ? assert_raise(exc, msg, &block) : assert_raise(exc, &block) e ? ($mrbtest_assert_idx[-1]-=1) : (return e) err_msg = e.message unless ret = type == :pattern ? _str_match?(exp_msg, err_msg) : exp_msg == err_msg diff = " Expected Exception(#{exc}) was raised, but the message doesn't match.\n" if type == :pattern diff += " Expected #{exp_msg.inspect} to match #{err_msg.inspect}." else diff += assertion_diff(exp_msg, err_msg) end end assert_true(ret, msg, diff) end def pass assert_true(true) end def flunk(msg = "Epic Fail!", diff = "") assert_true(false, msg, diff) end ## # Report the test result and print all assertions # which were reported broken. def report t_print("\n") $asserts.each do |msg| t_print("#{msg}\n") end $total_test = $ok_test + $ko_test + $kill_test + $warning_test + $skip_test t_print(" Total: #{$total_test}\n") t_print(" OK: #{$ok_test}\n") t_print(" KO: #{$ko_test}\n") t_print(" Crash: #{$kill_test}\n") t_print("Warning: #{$warning_test}\n") t_print(" Skip: #{$skip_test}\n") if Object.const_defined?(:Time) t_time = Time.now - $test_start t_print(" Time: #{t_time.round(2)} seconds\n") end $ko_test == 0 && $kill_test == 0 end def _eval_assertion(meth, exp, act_or_msg, msg, block) if block exp, act, msg = exp, block.call, act_or_msg else exp, act, msg = exp, act_or_msg, msg end return exp.__send__(meth, act), exp, act, msg end ## # Skip the test class MRubyTestSkip < NotImplementedError; end def skip(cause = "") raise MRubyTestSkip.new(cause) end nghttp2-1.68.0/third-party/mruby/test/PaxHeaders/bintest.rb0000644000000000000000000000013215077107276020647 xustar0030 mtime=1761382078.138420483 30 atime=1761382080.162411224 30 ctime=1761382108.478302179 nghttp2-1.68.0/third-party/mruby/test/bintest.rb0000644000175100017510000000175215077107276021244 0ustar00runnerrunner$:.unshift File.dirname(File.dirname(File.expand_path(__FILE__))) require 'test/assert.rb' GEMNAME = "" def cmd_list(s) path = s == "mrbc" ? ENV['MRBCFILE'] : "#{ENV['BUILD_DIR']}/bin/#{s}" path = path.sub(/\.exe\z/, "") if /mswin(?!ce)|mingw|bccwin/ =~ RbConfig::CONFIG['host_os'] path = "#{path}.exe".tr("/", "\\") end path_list = [path] emu = ENV['EMULATOR'] path_list.unshift emu if emu && !emu.empty? path_list end def cmd(s) cmd_list(s).join(' ') end def cmd_bin(s) cmd_list(s).pop end def shellquote(s) case RbConfig::CONFIG['host_os'] when /mswin(?!ce)|mingw|bccwin/ "\"#{s}\"" else "'#{s}'" end end print "bintest - Command Binary Test\n\n" ARGV.each do |gem| case gem when '-v'; $mrbtest_verbose = true end case RbConfig::CONFIG['host_os'] when /mswin(?!ce)|mingw|bccwin/ gem = gem.tr('\\', '/') end Dir["#{gem}/bintest/**/*.rb"].each do |file| GEMNAME.replace(File.basename(gem)) load file end end exit report nghttp2-1.68.0/third-party/mruby/PaxHeaders/appveyor.yml0000644000000000000000000000013115077107276020262 xustar0029 mtime=1761382078.09542068 30 atime=1761382080.118411425 30 ctime=1761382108.344302566 nghttp2-1.68.0/third-party/mruby/appveyor.yml0000644000175100017510000000350515077107276020656 0ustar00runnerrunnerversion: "{build}" shallow_clone: true environment: matrix: - job_name: Visual Studio 2022 64-bit visualcpp: C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat appveyor_build_worker_image: Visual Studio 2022 - job_name: Visual Studio 2019 64-bit visualcpp: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat appveyor_build_worker_image: Visual Studio 2019 - job_name: Visual Studio 2019 32-bit visualcpp: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat appveyor_build_worker_image: Visual Studio 2019 - job_name: Visual Studio 2017 64-bit visualcpp: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat appveyor_build_worker_image: Visual Studio 2017 - job_name: Visual Studio 2017 32-bit visualcpp: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat appveyor_build_worker_image: Visual Studio 2017 - job_name: Visual Studio 2015 64-bit visualcpp: C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat appveyor_build_worker_image: Visual Studio 2015 machine: x86_amd64 - job_name: Visual Studio 2015 32-bit visualcpp: C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat appveyor_build_worker_image: Visual Studio 2015 machine: x86 init: - call "%visualcpp%" %machine% # For using RubyInstaller's Ruby 2.6 64-bit # 2.6 is the highest supported Ruby version across all historical # Visual Studio AppVeyor images. Ruby 2.7 is only on the 2019 image. - set PATH=C:\Ruby26-x64\bin;%PATH% - ruby --version build_script: - set MRUBY_CONFIG=ci/msvc - rake -m test:run:serial nghttp2-1.68.0/third-party/mruby/PaxHeaders/Doxyfile0000644000000000000000000000013115077107276017400 xustar0029 mtime=1761382078.09542068 30 atime=1761382080.116411435 30 ctime=1761382108.537302009 nghttp2-1.68.0/third-party/mruby/Doxyfile0000644000175100017510000035576515077107276020016 0ustar00runnerrunner# Doxyfile 1.9.6 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). # # Note: # # Use doxygen to compare the used configuration file with the template # configuration file: # doxygen -x [configFile] # Use doxygen to compare the used configuration file with the template # configuration file without replacing the environment variables or CMake type # replacement variables: # doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the configuration # file that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = mruby # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = 3.4.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "mruby is the lightweight implementation of the Ruby language" # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = doc/mruby_logo_red_icon.png # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = doc/capi # If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 # sub-directories (in 2 levels) under the output directory of each output format # and will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to # control the number of sub-directories. # The default value is: NO. CREATE_SUBDIRS = NO # Controls the number of sub-directories that will be created when # CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every # level increment doubles the number of directories, resulting in 4096 # directories at level 8 which is the default and also the maximum value. The # sub-directories are organized in 2 levels, the first level always has a fixed # number of 16 directories. # Minimum value: 0, maximum value: 8, default value: 8. # This tag requires that the tag CREATE_SUBDIRS is set to YES. CREATE_SUBDIRS_LEVEL = 8 # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, # Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English # (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, # Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with # English messages), Korean, Korean-en (Korean with English messages), Latvian, # Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, # Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, # Swedish, Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = YES # If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line # such as # /*************** # as being the beginning of a Javadoc-style comment "banner". If set to NO, the # Javadoc-style will behave just like regular comments and it will not be # interpreted by doxygen. # The default value is: NO. JAVADOC_BANNER = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # By default Python docstrings are displayed as preformatted text and doxygen's # special commands cannot be used. By setting PYTHON_DOCSTRING to NO the # doxygen's special commands can be used and the contents of the docstring # documentation blocks is shown as doxygen documentation. # The default value is: YES. PYTHON_DOCSTRING = YES # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:^^" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". Note that you cannot put \n's in the value part of an alias # to insert newlines (in the resulting output). You can put ^^ in the value part # of an alias to insert a newline as if a physical newline was in the original # file. When you need a literal { or } or , in the value part of an alias you # have to escape them by means of a backslash (\), this can lead to conflicts # with the commands \{ and \} for these it is advised to use the version @{ and # @} or use a double escape (\\{ and \\}) ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice # sources only. Doxygen will then generate output that is more tailored for that # language. For instance, namespaces will be presented as modules, types will be # separated into more groups, etc. # The default value is: NO. OPTIMIZE_OUTPUT_SLICE = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, JavaScript, # Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, # VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files). For instance to make doxygen treat .inc files # as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. When specifying no_extension you should add # * to the FILE_PATTERNS. # # Note see also the list of default file extension mappings. EXTENSION_MAPPING = no_extension=md # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. # Minimum value: 0, maximum value: 99, default value: 5. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 0 # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 # The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use # during processing. When set to 0 doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, # which effectively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. NUM_PROC_THREADS = 1 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual # methods of a class will be included in the documentation. # The default value is: NO. EXTRACT_PRIV_VIRTUAL = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If this flag is set to YES, the name of an unnamed parameter in a declaration # will be determined by the corresponding definition. By default unnamed # parameters remain unnamed in the output. # The default value is: YES. RESOLVE_UNNAMED_PARAMS = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # will also hide undocumented C++ concepts if enabled. This option has no effect # if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # declarations. If set to NO, these declarations will be included in the # documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # With the correct setting of option CASE_SENSE_NAMES doxygen will better be # able to match the capabilities of the underlying filesystem. In case the # filesystem is case sensitive (i.e. it supports files in the same directory # whose names only differ in casing), the option must be set to YES to properly # deal with such files in case they appear in the input. For filesystems that # are not case sensitive the option should be set to NO to properly deal with # output files written for symbols that only differ in casing, such as for two # classes, one named CLASS and the other named Class, and to also support # references to files without having to specify the exact matching casing. On # Windows (including Cygwin) and MacOS, users should typically set this option # to NO, whereas on Linux or other Unix flavors it should typically be set to # YES. # Possible values are: SYSTEM, NO and YES. # The default value is: SYSTEM. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_HEADERFILE tag is set to YES then the documentation for a class # will show which file needs to be included to use the class. # The default value is: YES. SHOW_HEADERFILE = YES # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. See also section "Changing the # layout of pages" for information. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = NO # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as documenting some parameters in # a documented function twice, or documenting parameters that don't exist or # using markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete # function parameter documentation. If set to NO, doxygen will accept that some # parameters have no documentation without warning. # The default value is: YES. WARN_IF_INCOMPLETE_DOC = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong parameter # documentation, but not about the absence of documentation. If EXTRACT_ALL is # set to YES then this flag will automatically be disabled. See also # WARN_IF_INCOMPLETE_DOC # The default value is: NO. WARN_NO_PARAMDOC = NO # If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about # undocumented enumeration values. If set to NO, doxygen will accept # undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: NO. WARN_IF_UNDOC_ENUM_VAL = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS # then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but # at the end of the doxygen process doxygen will return with a non-zero status. # Possible values are: NO, YES and FAIL_ON_WARNINGS. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # See also: WARN_LINE_FORMAT # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # In the $text part of the WARN_FORMAT command it is possible that a reference # to a more specific place is given. To make it easier to jump to this place # (outside of doxygen) the user can define a custom "cut" / "paste" string. # Example: # WARN_LINE_FORMAT = "'vi $file +$line'" # See also: WARN_FORMAT # The default value is: at line $line of file $file. WARN_LINE_FORMAT = "at line $line of file $file" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). In case the file specified cannot be opened for writing the # warning and error messages are written to standard error. When as file - is # specified the warning and error messages are written to standard output # (stdout). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = CONTRIBUTING.md \ README.md \ SECURITY.md \ TODO.md \ src \ include \ include/mruby \ mrblib \ doc \ doc/guides \ doc/internal \ LEGAL \ LICENSE \ NEWS # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: # https://www.gnu.org/software/libiconv/) for the list of possible encodings. # See also: INPUT_FILE_ENCODING # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # This tag can be used to specify the character encoding of the source files # that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify # character encoding on a per file pattern basis. Doxygen will compare the file # name with each pattern and apply the encoding instead of the default # INPUT_ENCODING) if there is a match. The character encodings are a list of the # form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding # "INPUT_ENCODING" for further information on supported encodings. INPUT_FILE_ENCODING = # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # Note the list of default checked file patterns might differ from the list of # default file extension mappings. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, # *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C # comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, # *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.c \ *.cc \ *.cxx \ *.cpp \ *.c++ \ *.java \ *.ii \ *.ixx \ *.ipp \ *.i++ \ *.inl \ *.idl \ *.ddl \ *.odl \ *.h \ *.hh \ *.hxx \ *.hpp \ *.h++ \ *.cs \ *.d \ *.php \ *.php4 \ *.php5 \ *.phtml \ *.inc \ *.m \ *.markdown \ *.md \ *.mm \ *.dox \ *.py \ *.pyw \ *.f90 \ *.f95 \ *.f03 \ *.f08 \ *.f \ *.for \ *.tcl \ *.vhd \ *.vhdl \ *.ucf \ *.qsf # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # ANamespace::AClass, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that doxygen will use the data processed and written to standard output # for further processing, therefore nothing else, like debug statements or used # commands (so in case of a Windows batch file always use @echo OFF), should be # written to standard output. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = README.md # The Fortran standard specifies that for fixed formatted Fortran code all # characters from position 72 are to be considered as comment. A common # extension is to allow longer lines before the automatic comment starts. The # setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can # be processed before the automatic comment starts. # Minimum value: 7, maximum value: 10000, default value: 72. FORTRAN_COMMENT_AFTER = 72 #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) # that should be ignored while generating the index headers. The IGNORE_PREFIX # tag works for classes, function and member names. The entity will be placed in # the alphabetical list under the first letter of the entity name that remains # after removing the prefix. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). # Note: Since the styling of scrollbars can currently not be overruled in # Webkit/Chromium, the styling will be left out of the default doxygen.css if # one or more extra stylesheets have been specified. So if scrollbar # customization is desired it has to be added explicitly. For an example see the # documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE tag can be used to specify if the generated HTML output # should be rendered with a dark or light theme. # Possible values are: LIGHT always generate light mode output, DARK always # generate dark mode output, AUTO_LIGHT automatically set the mode according to # the user preference, use light mode if no preference is set (the default), # AUTO_DARK automatically set the mode according to the user preference, use # dark mode if no preference is set and TOGGLE allow to user to switch between # light and dark mode via a button. # The default value is: AUTO_LIGHT. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE = AUTO_LIGHT # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a color-wheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 359 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use gray-scales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to YES can help to show when doxygen was last run and thus if the # documentation is up to date. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = NO # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML # page. Disable this option to support browsers that do not have JavaScript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: # https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To # create a documentation set, doxygen will generate a Makefile in the HTML # output directory. Running make will produce the docset in that directory and # running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag determines the URL of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDURL = # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # on Windows. In the beginning of 2021 Microsoft took the original page, with # a.o. the download links, offline the HTML help workshop was already many years # in maintenance mode). You can download the HTML help workshop from the web # archives at Installation executable (see: # http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo # ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location (absolute path # including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to # run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine tune the look of the index (see "Fine-tuning the output"). As an # example, the default style sheet generated by doxygen has an example that # shows how to put an image at the root of the tree instead of the PROJECT_NAME. # Since the tree basically has the same information as the tab index, you could # consider setting DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the # FULL_SIDEBAR option determines if the side bar is limited to only the treeview # area (value NO) or if it should extend to the full height of the window (value # YES). Setting this to YES gives a layout similar to # https://docs.readthedocs.io with more room for contents, but less room for the # project logo, title, and description. If either GENERATE_TREEVIEW or # DISABLE_INDEX is set to NO, this option has no effect. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. FULL_SIDEBAR = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email # addresses. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. OBFUSCATE_EMAILS = YES # If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for # the HTML output. These images will generally look nicer at scaled resolutions. # Possible values are: png (the default) and svg (looks nicer but requires the # pdf2svg or inkscape tool). # The default value is: png. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FORMULA_FORMAT = png # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. FORMULA_MACROFILE = # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # With MATHJAX_VERSION it is possible to specify the MathJax version to be used. # Note that the different versions of MathJax have different requirements with # regards to the different settings, so it is possible that also other MathJax # settings have to be changed when switching between the different MathJax # versions. # Possible values are: MathJax_2 and MathJax_3. # The default value is: MathJax_2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_VERSION = MathJax_2 # When MathJax is enabled you can set the default output format to be used for # the MathJax output. For more details about the output format see MathJax # version 2 (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 # (see: # http://docs.mathjax.org/en/latest/web/components/output.html). # Possible values are: HTML-CSS (which is slower, but has the best # compatibility. This is the name for Mathjax version 2, for MathJax version 3 # this will be translated into chtml), NativeMML (i.e. MathML. Only supported # for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This # is the name for Mathjax version 3, for MathJax version 2 this will be # translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from https://www.mathjax.org before deployment. The default value is: # - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 # - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # for MathJax version 2 (see # https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # For example for MathJax version 3 (see # http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): # MATHJAX_EXTENSIONS = ams # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /Node, # Edge and Graph Attributes specification You need to make sure dot is able # to find the font, which can be done by putting it in a standard location or by # setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. Default graphviz fontsize is 14. # The default value is: fontname=Helvetica,fontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" # DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can # add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. Complete documentation about # arrows shapes. # The default value is: labelfontname=Helvetica,labelfontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" # DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes # around nodes set 'shape=plain' or 'shape=plaintext' Shapes specification # The default value is: shape=box,height=0.2,width=0.4. # This tag requires that the tag HAVE_DOT is set to YES. DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" # You can set the path where dot can find font specified with fontname in # DOT_COMMON_ATTR and others dot attributes. # This tag requires that the tag HAVE_DOT is set to YES. DOT_FONTPATH = # If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a # graph for each documented class showing the direct and indirect inheritance # relations. In case HAVE_DOT is set as well dot will be used to draw the graph, # otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set # to TEXT the direct and indirect inheritance relations will be shown as texts / # links. # Possible values are: NO, YES, TEXT and GRAPH. # The default value is: YES. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a # graph for each documented class showing the direct and indirect implementation # dependencies (inheritance, containment, and class references variables) of the # class with other documented classes. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for # groups, showing the direct groups dependencies. See also the chapter Grouping # in the manual. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES, doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. UML_LOOK = NO # If the UML_LOOK tag is enabled, the fields and methods are shown inside the # class node. If there are many fields or methods and many nodes the graph may # become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the # number of items for each type to make the size more manageable. Set this to 0 # for no limit. Note that the threshold may be exceeded by 50% before the limit # is enforced. So when you set the threshold to 10, up to 15 fields may appear, # but if the number exceeds 15, the total amount of fields shown is limited to # 10. # Minimum value: 0, maximum value: 100, default value: 10. # This tag requires that the tag UML_LOOK is set to YES. UML_LIMIT_NUM_FIELDS = 10 # If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and # methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS # tag is set to YES, doxygen will add type and arguments for attributes and # methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen # will not generate fields with class member information in the UML graphs. The # class diagrams will look similar to the default class diagrams but using UML # notation for the relationships. # Possible values are: NO, YES and NONE. # The default value is: NO. # This tag requires that the tag UML_LOOK is set to YES. DOT_UML_DETAILS = NO # The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters # to display on a single line. If the actual line length exceeds this threshold # significantly it will wrapped across multiple lines. Some heuristics are apply # to avoid ugly line breaks. # Minimum value: 0, maximum value: 1000, default value: 17. # This tag requires that the tag HAVE_DOT is set to YES. DOT_WRAP_THRESHOLD = 17 # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # collaboration graphs will show the relations between templates and their # instances. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. TEMPLATE_RELATIONS = NO # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to # YES then doxygen will generate a graph for each documented file showing the # direct and indirect include dependencies of the file with other documented # files. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. INCLUDE_GRAPH = YES # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are # set to YES then doxygen will generate a graph for each documented file showing # the direct and indirect include dependencies of the file with other documented # files. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH tag is set to YES then doxygen will generate a call # dependency graph for every global function or class method. # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected # functions only using the \callgraph command. Disabling a call graph can be # accomplished by means of the command \hidecallgraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. CALL_GRAPH = NO # If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller # dependency graph for every global function or class method. # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected # functions only using the \callergraph command. Disabling a caller graph can be # accomplished by means of the command \hidecallergraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical # hierarchy of all classes instead of a textual one. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the # dependencies a directory has on other directories in a graphical way. The # dependency relations are determined by the #include relations between the # files in the directories. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. DIRECTORY_GRAPH = YES # The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels # of child directories generated in directory dependency graphs by dot. # Minimum value: 1, maximum value: 25, default value: 1. # This tag requires that the tag DIRECTORY_GRAPH is set to YES. DIR_GRAPH_MAX_DEPTH = 1 # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. For an explanation of the image formats see the section # output formats in the documentation of the dot tool (Graphviz (see: # http://www.graphviz.org/)). # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). # Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, # png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and # png:gdiplus:gdiplus. # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # # Note that this requires a modern browser other than Internet Explorer. Tested # and working are Firefox, Chrome, Safari, and Opera. # Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make # the SVG files visible. Older versions of IE do not have SVG support. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. INTERACTIVE_SVG = NO # The DOT_PATH tag can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. # This tag requires that the tag HAVE_DOT is set to YES. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the \dotfile # command). # This tag requires that the tag HAVE_DOT is set to YES. DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the \mscfile # command). MSCFILE_DIRS = # The DIAFILE_DIRS tag can be used to specify one or more directories that # contain dia files that are included in the documentation (see the \diafile # command). DIAFILE_DIRS = # When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the # path where java can find the plantuml.jar file or to the filename of jar file # to be used. If left blank, it is assumed PlantUML is not used or called during # a preprocessing step. Doxygen will generate a warning when it encounters a # \startuml command in this case and will not generate output for the diagram. PLANTUML_JAR_PATH = # When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a # configuration file for plantuml. PLANTUML_CFG_FILE = # When using plantuml, the specified paths are searched for files specified by # the !include statement in a plantuml block. PLANTUML_INCLUDE_PATH = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes # larger than this value, doxygen will truncate the graph, which is visualized # by representing a node as a red box. Note that doxygen if the number of direct # children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that # the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. # Minimum value: 0, maximum value: 10000, default value: 50. # This tag requires that the tag HAVE_DOT is set to YES. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs # generated by dot. A depth value of 3 means that only nodes reachable from the # root by following a path via at most 3 edges will be shown. Nodes that lay # further from the root node will be omitted. Note that setting this option to 1 # or 2 may greatly reduce the computation time needed for large code bases. Also # note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. # Minimum value: 0, maximum value: 1000, default value: 0. # This tag requires that the tag HAVE_DOT is set to YES. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support # this, this feature is disabled by default. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated # graphs. # Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal # graphical representation for inheritance and collaboration diagrams is used. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate # files that are used to generate the various graphs. # # Note: This setting is not only used for dot files but also for msc temporary # files. # The default value is: YES. DOT_CLEANUP = YES nghttp2-1.68.0/third-party/mruby/PaxHeaders/include0000644000000000000000000000013215077107335017235 xustar0030 mtime=1761382109.016300624 30 atime=1761382109.799298361 30 ctime=1761382109.016300624 nghttp2-1.68.0/third-party/mruby/include/0000755000175100017510000000000015077107335017702 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/include/PaxHeaders/mruby0000644000000000000000000000013215077107335020373 xustar0030 mtime=1761382109.013300633 30 atime=1761382109.799298361 30 ctime=1761382109.013300633 nghttp2-1.68.0/third-party/mruby/include/mruby/0000755000175100017510000000000015077107335021040 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/boxing_word.h0000644000000000000000000000013215077107276023146 xustar0030 mtime=1761382078.101420652 30 atime=1761382080.122411407 30 ctime=1761382108.989300702 nghttp2-1.68.0/third-party/mruby/include/mruby/boxing_word.h0000644000175100017510000001735115077107276023545 0ustar00runnerrunner/** ** @file mruby/boxing_word.h - word boxing mrb_value definition ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_BOXING_WORD_H #define MRUBY_BOXING_WORD_H #if defined(MRB_32BIT) && !defined(MRB_USE_FLOAT32) && !defined(MRB_WORDBOX_NO_FLOAT_TRUNCATE) # define MRB_WORDBOX_NO_FLOAT_TRUNCATE #endif #if !defined(MRB_NO_FLOAT) && defined(MRB_WORDBOX_NO_FLOAT_TRUNCATE) struct RFloat { MRB_OBJECT_HEADER; mrb_float f; }; #endif struct RInteger { MRB_OBJECT_HEADER; mrb_int i; }; enum mrb_special_consts { MRB_Qnil = 0, MRB_Qfalse = 4, MRB_Qtrue = 12, MRB_Qundef = 20, }; #if defined(MRB_64BIT) && defined(MRB_INT32) #define MRB_FIXNUM_SHIFT 0 #else #define MRB_FIXNUM_SHIFT WORDBOX_FIXNUM_SHIFT #endif #define MRB_SYMBOL_SHIFT WORDBOX_SYMBOL_SHIFT #if defined(MRB_64BIT) && defined(MRB_INT64) # define MRB_FIXNUM_MIN (INT64_MIN>>MRB_FIXNUM_SHIFT) # define MRB_FIXNUM_MAX (INT64_MAX>>MRB_FIXNUM_SHIFT) #else # define MRB_FIXNUM_MIN (INT32_MIN>>MRB_FIXNUM_SHIFT) # define MRB_FIXNUM_MAX (INT32_MAX>>MRB_FIXNUM_SHIFT) #endif #define WORDBOX_FIXNUM_BIT_POS 1 #define WORDBOX_FIXNUM_SHIFT WORDBOX_FIXNUM_BIT_POS #define WORDBOX_FIXNUM_FLAG (1 << (WORDBOX_FIXNUM_BIT_POS - 1)) #define WORDBOX_FIXNUM_MASK ((1 << WORDBOX_FIXNUM_BIT_POS) - 1) #if defined(MRB_WORDBOX_NO_FLOAT_TRUNCATE) || defined(MRB_NO_FLOAT) /* floats are allocated in heaps */ #define WORDBOX_IMMEDIATE_MASK 0x03 #define WORDBOX_SYMBOL_BIT_POS 2 #define WORDBOX_SYMBOL_SHIFT WORDBOX_SYMBOL_BIT_POS #define WORDBOX_SYMBOL_FLAG (1 << (WORDBOX_SYMBOL_BIT_POS - 1)) #define WORDBOX_SYMBOL_MASK ((1 << WORDBOX_SYMBOL_BIT_POS) - 1) #else #define WORDBOX_FLOAT_FLAG 2 #define WORDBOX_FLOAT_MASK 3 #if defined(MRB_64BIT) #define WORDBOX_SYMBOL_SHIFT 32 #else /* MRB_32BIT */ #define WORDBOX_SYMBOL_SHIFT 5 #endif #define WORDBOX_SYMBOL_FLAG 0x1c #define WORDBOX_SYMBOL_MASK 0x1f #endif #ifndef WORDBOX_IMMEDIATE_MASK #define WORDBOX_IMMEDIATE_MASK 0x07 #endif #define WORDBOX_SET_SHIFT_VALUE(o,n,v) \ ((o).w = (((uintptr_t)(v)) << WORDBOX_##n##_SHIFT) | WORDBOX_##n##_FLAG) #define WORDBOX_SHIFT_VALUE_P(o,n) \ (((o).w & WORDBOX_##n##_MASK) == WORDBOX_##n##_FLAG) #define WORDBOX_OBJ_TYPE_P(o,n) \ (!mrb_immediate_p(o) && mrb_val_union(o).bp->tt == MRB_TT_##n) /* * mrb_value representation: * * 64-bit word with inline float: * nil : ...0000 0000 (all bits are 0) * false : ...0000 0100 (mrb_fixnum(v) != 0) * true : ...0000 1100 * undef : ...0001 0100 * symbol: ...0001 1100 (use only upper 32-bit as symbol value with MRB_64BIT) * fixnum: ...IIII III1 * float : ...FFFF FF10 (51 bit significands; require MRB_64BIT) * object: ...PPPP P000 * * 32-bit word with inline float: * nil : ...0000 0000 (all bits are 0) * false : ...0000 0100 (mrb_fixnum(v) != 0) * true : ...0000 1100 * undef : ...0001 0100 * symbol: ...SSS1 0100 (symbol occupies 20bits) * fixnum: ...IIII III1 * float : ...FFFF FF10 (22 bit significands; require MRB_64BIT) * object: ...PPPP P000 * * and word boxing without inline float (MRB_WORDBOX_NO_FLOAT_TRUNCATE): * nil : ...0000 0000 (all bits are 0) * false : ...0000 0100 (mrb_fixnum(v) != 0) * true : ...0000 1100 * undef : ...0001 0100 * fixnum: ...IIII III1 * symbol: ...SSSS SS10 * object: ...PPPP PP00 (any bits are 1) */ typedef struct mrb_value { uintptr_t w; } mrb_value; union mrb_value_ { void *p; struct RBasic *bp; #ifndef MRB_NO_FLOAT #ifndef MRB_WORDBOX_NO_FLOAT_TRUNCATE mrb_float f; #else struct RFloat *fp; #endif #endif struct RInteger *ip; struct RCptr *vp; uintptr_t w; mrb_value value; }; mrb_static_assert(sizeof(mrb_value) == sizeof(union mrb_value_)); static inline union mrb_value_ mrb_val_union(mrb_value v) { union mrb_value_ x; x.value = v; return x; } MRB_API mrb_value mrb_word_boxing_cptr_value(struct mrb_state*, void*); #ifndef MRB_NO_FLOAT MRB_API mrb_value mrb_word_boxing_float_value(struct mrb_state*, mrb_float); #endif MRB_API mrb_value mrb_boxing_int_value(struct mrb_state*, mrb_int); #if WORDBOX_IMMEDIATE_MASK == 0x3 #define mrb_immediate_p(o) ((o).w & WORDBOX_IMMEDIATE_MASK || (o).w <= MRB_Qundef) #else #define mrb_immediate_p(o) ((o).w & WORDBOX_IMMEDIATE_MASK || (o).w == MRB_Qnil) #endif #define mrb_ptr(o) mrb_val_union(o).p #define mrb_cptr(o) mrb_val_union(o).vp->p #ifndef MRB_NO_FLOAT #ifndef MRB_WORDBOX_NO_FLOAT_TRUNCATE MRB_API mrb_float mrb_word_boxing_value_float(mrb_value v); #define mrb_float(o) mrb_word_boxing_value_float(o) #else #define mrb_float(o) mrb_val_union(o).fp->f #endif #endif #define mrb_fixnum(o) (mrb_int)(((intptr_t)(o).w) >> WORDBOX_FIXNUM_SHIFT) MRB_INLINE mrb_int mrb_integer_func(mrb_value o) { if (mrb_immediate_p(o)) return mrb_fixnum(o); return mrb_val_union(o).ip->i; } #define mrb_integer(o) mrb_integer_func(o) #define mrb_symbol(o) (mrb_sym)(((o).w) >> WORDBOX_SYMBOL_SHIFT) #define mrb_bool(o) (((o).w & ~(uintptr_t)MRB_Qfalse) != 0) #define mrb_fixnum_p(o) WORDBOX_SHIFT_VALUE_P(o, FIXNUM) #define mrb_integer_p(o) (WORDBOX_SHIFT_VALUE_P(o, FIXNUM)||WORDBOX_OBJ_TYPE_P(o, INTEGER)) #define mrb_symbol_p(o) WORDBOX_SHIFT_VALUE_P(o, SYMBOL) #define mrb_undef_p(o) ((o).w == MRB_Qundef) #define mrb_nil_p(o) ((o).w == MRB_Qnil) #define mrb_false_p(o) ((o).w == MRB_Qfalse) #define mrb_true_p(o) ((o).w == MRB_Qtrue) #ifndef MRB_NO_FLOAT #ifndef MRB_WORDBOX_NO_FLOAT_TRUNCATE #define mrb_float_p(o) WORDBOX_SHIFT_VALUE_P(o, FLOAT) #else #define mrb_float_p(o) WORDBOX_OBJ_TYPE_P(o, FLOAT) #endif #else #define mrb_float_p(o) FALSE #endif #define mrb_array_p(o) WORDBOX_OBJ_TYPE_P(o, ARRAY) #define mrb_string_p(o) WORDBOX_OBJ_TYPE_P(o, STRING) #define mrb_hash_p(o) WORDBOX_OBJ_TYPE_P(o, HASH) #define mrb_cptr_p(o) WORDBOX_OBJ_TYPE_P(o, CPTR) #define mrb_exception_p(o) WORDBOX_OBJ_TYPE_P(o, EXCEPTION) #define mrb_free_p(o) WORDBOX_OBJ_TYPE_P(o, FREE) #define mrb_object_p(o) WORDBOX_OBJ_TYPE_P(o, OBJECT) #define mrb_class_p(o) WORDBOX_OBJ_TYPE_P(o, CLASS) #define mrb_module_p(o) WORDBOX_OBJ_TYPE_P(o, MODULE) #define mrb_iclass_p(o) WORDBOX_OBJ_TYPE_P(o, ICLASS) #define mrb_sclass_p(o) WORDBOX_OBJ_TYPE_P(o, SCLASS) #define mrb_proc_p(o) WORDBOX_OBJ_TYPE_P(o, PROC) #define mrb_range_p(o) WORDBOX_OBJ_TYPE_P(o, RANGE) #define mrb_env_p(o) WORDBOX_OBJ_TYPE_P(o, ENV) #define mrb_data_p(o) WORDBOX_OBJ_TYPE_P(o, DATA) #define mrb_fiber_p(o) WORDBOX_OBJ_TYPE_P(o, FIBER) #define mrb_istruct_p(o) WORDBOX_OBJ_TYPE_P(o, ISTRUCT) #define mrb_break_p(o) WORDBOX_OBJ_TYPE_P(o, BREAK) #ifndef MRB_NO_FLOAT #define SET_FLOAT_VALUE(mrb,r,v) ((r) = mrb_word_boxing_float_value(mrb, v)) #endif #define SET_CPTR_VALUE(mrb,r,v) ((r) = mrb_word_boxing_cptr_value(mrb, v)) #define SET_UNDEF_VALUE(r) ((r).w = MRB_Qundef) #define SET_NIL_VALUE(r) ((r).w = MRB_Qnil) #define SET_FALSE_VALUE(r) ((r).w = MRB_Qfalse) #define SET_TRUE_VALUE(r) ((r).w = MRB_Qtrue) #define SET_BOOL_VALUE(r,b) ((b) ? SET_TRUE_VALUE(r) : SET_FALSE_VALUE(r)) #define SET_INT_VALUE(mrb,r,n) ((r) = mrb_boxing_int_value(mrb, n)) #define SET_FIXNUM_VALUE(r,n) WORDBOX_SET_SHIFT_VALUE(r, FIXNUM, n) #define SET_SYM_VALUE(r,n) WORDBOX_SET_SHIFT_VALUE(r, SYMBOL, n) #define SET_OBJ_VALUE(r,v) ((r).w = (uintptr_t)(v)) MRB_INLINE enum mrb_vtype mrb_type(mrb_value o) { return !mrb_bool(o) ? MRB_TT_FALSE : mrb_true_p(o) ? MRB_TT_TRUE : mrb_fixnum_p(o) ? MRB_TT_INTEGER : mrb_symbol_p(o) ? MRB_TT_SYMBOL : mrb_undef_p(o) ? MRB_TT_UNDEF : mrb_float_p(o) ? MRB_TT_FLOAT : mrb_val_union(o).bp->tt; } MRB_INLINE enum mrb_vtype mrb_unboxed_type(mrb_value o) { if (mrb_nil_p(o)) { return MRB_TT_FALSE; } else if ((o.w & WORDBOX_IMMEDIATE_MASK) == 0) { return mrb_val_union(o).bp->tt; } else { return MRB_TT_FALSE; } } #endif /* MRUBY_BOXING_WORD_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/dump.h0000644000000000000000000000013215077107276021572 xustar0030 mtime=1761382078.101420652 30 atime=1761382080.122411407 30 ctime=1761382109.004300659 nghttp2-1.68.0/third-party/mruby/include/mruby/dump.h0000644000175100017510000000763015077107276022170 0ustar00runnerrunner/** ** @file mruby/dump.h - mruby binary dumper (mrbc binary format) ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_DUMP_H #define MRUBY_DUMP_H #include #include #include "common.h" /** * Dumping compiled mruby script. */ MRB_BEGIN_DECL /* flags for mrb_dump_irep{,_binary,_cfunc,_cstruct} */ #define MRB_DUMP_DEBUG_INFO 1 #define MRB_DUMP_STATIC 2 #define MRB_DUMP_NO_LVAR 4 #ifndef MRB_NO_STDIO MRB_API mrb_value mrb_load_irep_file(mrb_state*,FILE*); MRB_API mrb_value mrb_load_irep_file_cxt(mrb_state*, FILE*, mrb_ccontext*); mrb_irep *mrb_read_irep_file(mrb_state*, FILE*); int mrb_dump_irep_binary(mrb_state*, const mrb_irep*, uint8_t, FILE*); #endif /* avoid mrb_read_irep(); use mrb_read_irep_buf() instead (may cause buffer overflow) */ MRB_API mrb_irep *mrb_read_irep(mrb_state*, const uint8_t*); MRB_API mrb_irep *mrb_read_irep_buf(mrb_state*, const void*, size_t); /* dump/load error code * * NOTE: MRB_DUMP_GENERAL_FAILURE is caused by * unspecified issues like malloc failed. */ #define MRB_DUMP_OK 0 #define MRB_DUMP_GENERAL_FAILURE (-1) #define MRB_DUMP_WRITE_FAULT (-2) #define MRB_DUMP_READ_FAULT (-3) #define MRB_DUMP_INVALID_FILE_HEADER (-4) #define MRB_DUMP_INVALID_IREP (-5) #define MRB_DUMP_INVALID_ARGUMENT (-6) /* null symbol length */ #define MRB_DUMP_NULL_SYM_LEN 0xFFFF /* Rite Binary File header */ #define RITE_BINARY_IDENT "RITE" /* Binary Format Version Major:Minor */ /* Major: Incompatible to prior versions */ /* Minor: Upper-compatible to prior versions */ #define RITE_BINARY_MAJOR_VER "03" #define RITE_BINARY_MINOR_VER "00" #define RITE_BINARY_FORMAT_VER RITE_BINARY_MAJOR_VER RITE_BINARY_MINOR_VER #define RITE_COMPILER_NAME "MATZ" #define RITE_COMPILER_VERSION "0000" #define RITE_VM_VER "0300" #define RITE_BINARY_EOF "END\0" #define RITE_SECTION_IREP_IDENT "IREP" #define RITE_SECTION_DEBUG_IDENT "DBG\0" #define RITE_SECTION_LV_IDENT "LVAR" #define MRB_DUMP_DEFAULT_STR_LEN 128 #define MRB_DUMP_ALIGNMENT sizeof(uint32_t) /* binary header */ struct rite_binary_header { uint8_t binary_ident[4]; /* Binary Identifier */ uint8_t major_version[2]; /* Binary Format Major Version */ uint8_t minor_version[2]; /* Binary Format Minor Version */ uint8_t binary_size[4]; /* Binary Size */ uint8_t compiler_name[4]; /* Compiler name */ uint8_t compiler_version[4]; }; /* section header */ #define RITE_SECTION_HEADER \ uint8_t section_ident[4]; \ uint8_t section_size[4] struct rite_section_header { RITE_SECTION_HEADER; }; struct rite_section_irep_header { RITE_SECTION_HEADER; uint8_t rite_version[4]; /* Rite Instruction Specification Version */ }; struct rite_section_debug_header { RITE_SECTION_HEADER; }; struct rite_section_lv_header { RITE_SECTION_HEADER; }; #define RITE_LV_NULL_MARK UINT16_MAX struct rite_binary_footer { RITE_SECTION_HEADER; }; static inline size_t uint8_to_bin(uint8_t s, uint8_t *bin) { *bin = s; return sizeof(uint8_t); } static inline size_t uint16_to_bin(uint16_t s, uint8_t *bin) { *bin++ = (s >> 8) & 0xff; *bin = s & 0xff; return sizeof(uint16_t); } static inline size_t uint32_to_bin(uint32_t l, uint8_t *bin) { *bin++ = (l >> 24) & 0xff; *bin++ = (l >> 16) & 0xff; *bin++ = (l >> 8) & 0xff; *bin = l & 0xff; return sizeof(uint32_t); } static inline uint32_t bin_to_uint32(const uint8_t *bin) { return (uint32_t)bin[0] << 24 | (uint32_t)bin[1] << 16 | (uint32_t)bin[2] << 8 | (uint32_t)bin[3]; } static inline uint16_t bin_to_uint16(const uint8_t *bin) { return (uint16_t)bin[0] << 8 | (uint16_t)bin[1]; } static inline uint8_t bin_to_uint8(const uint8_t *bin) { return (uint8_t)bin[0]; } MRB_END_DECL #endif /* MRUBY_DUMP_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/error.h0000644000000000000000000000013215077107276021756 xustar0030 mtime=1761382078.101420652 30 atime=1761382080.122411407 30 ctime=1761382108.994300687 nghttp2-1.68.0/third-party/mruby/include/mruby/error.h0000644000175100017510000000744215077107276022355 0ustar00runnerrunner/** ** @file mruby/error.h - Exception class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_ERROR_H #define MRUBY_ERROR_H #include "common.h" /** * mruby error handling. */ MRB_BEGIN_DECL struct RException { MRB_OBJECT_HEADER; struct iv_tbl *iv; struct RBasic *mesg; // NULL or probably RString struct RBasic *backtrace; // NULL, RArray or RData }; /* error that should terminate execution */ #define MRB_EXC_EXIT 65536 #define MRB_EXC_EXIT_P(e) ((e)->flags & MRB_EXC_EXIT) /* retrieve status value from exc; need and */ #define MRB_EXC_EXIT_STATUS(mrb,e) ((int)mrb_as_int((mrb),mrb_obj_iv_get((mrb),(e),MRB_SYM(status)))) /* exit with SystemExit status */ #define MRB_EXC_CHECK_EXIT(mrb,e) do {if (MRB_EXC_EXIT_P(e)) exit(MRB_EXC_EXIT_STATUS((mrb),(e)));} while (0) #define mrb_exc_ptr(v) ((struct RException*)mrb_ptr(v)) MRB_API mrb_noreturn void mrb_sys_fail(mrb_state *mrb, const char *mesg); MRB_API mrb_value mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str); #define mrb_exc_new_lit(mrb, c, lit) mrb_exc_new_str(mrb, c, mrb_str_new_lit(mrb, lit)) MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, const char *fmt, ...); #if defined(MRB_64BIT) || defined(MRB_USE_FLOAT32) || defined(MRB_NAN_BOXING) || defined(MRB_WORD_BOXING) #undef MRB_USE_RBREAK_VALUE_UNION #else #define MRB_USE_RBREAK_VALUE_UNION 1 #endif /* * flags: * 0..7: enum mrb_vtype (only when defined MRB_USE_RBREAK_VALUE_UNION) * 8..10: RBREAK_TAGs in src/vm.c (otherwise, set to 0) */ struct RBreak { MRB_OBJECT_HEADER; uintptr_t ci_break_index; // The top-level ci index to break. One before the return destination. #ifndef MRB_USE_RBREAK_VALUE_UNION mrb_value val; #else union mrb_value_union value; #endif }; #ifndef MRB_USE_RBREAK_VALUE_UNION #define mrb_break_value_get(brk) ((brk)->val) #define mrb_break_value_set(brk, v) ((brk)->val = v) #else #define RBREAK_VALUE_TT_MASK ((1 << 8) - 1) static inline mrb_value mrb_break_value_get(struct RBreak *brk) { mrb_value val; val.value = brk->value; val.tt = (enum mrb_vtype)(brk->flags & RBREAK_VALUE_TT_MASK); return val; } static inline void mrb_break_value_set(struct RBreak *brk, mrb_value val) { brk->value = val.value; brk->flags &= ~RBREAK_VALUE_TT_MASK; brk->flags |= val.tt; } #endif /* MRB_USE_RBREAK_VALUE_UNION */ /** * Error check * */ /* clear error status in the mrb_state structure */ MRB_API void mrb_clear_error(mrb_state *mrb); /* returns TRUE if error in the previous call; internally calls mrb_clear_error() */ MRB_API mrb_bool mrb_check_error(mrb_state *mrb); /** * Protect * */ typedef mrb_value mrb_protect_error_func(mrb_state *mrb, void *userdata); MRB_API mrb_value mrb_protect_error(mrb_state *mrb, mrb_protect_error_func *body, void *userdata, mrb_bool *error); /** * Protect (takes mrb_value for body argument) * * Implemented in the mruby-error mrbgem */ MRB_API mrb_value mrb_protect(mrb_state *mrb, mrb_func_t body, mrb_value data, mrb_bool *state); /** * Ensure * * Implemented in the mruby-error mrbgem */ MRB_API mrb_value mrb_ensure(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t ensure, mrb_value e_data); /** * Rescue * * Implemented in the mruby-error mrbgem */ MRB_API mrb_value mrb_rescue(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t rescue, mrb_value r_data); /** * Rescue exception * * Implemented in the mruby-error mrbgem */ MRB_API mrb_value mrb_rescue_exceptions(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t rescue, mrb_value r_data, mrb_int len, struct RClass **classes); MRB_END_DECL #endif /* MRUBY_ERROR_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/throw.h0000644000000000000000000000013215077107276021770 xustar0030 mtime=1761382078.102420648 30 atime=1761382080.124411398 30 ctime=1761382108.986300711 nghttp2-1.68.0/third-party/mruby/include/mruby/throw.h0000644000175100017510000000237515077107276022367 0ustar00runnerrunner/** ** @file mruby/throw.h - mruby exception throwing handler ** ** See Copyright Notice in mruby.h */ #ifndef MRB_THROW_H #define MRB_THROW_H #if defined(MRB_USE_CXX_ABI) && !defined(__cplusplus) # error Trying to use C++ exception handling in C code #endif #if defined(MRB_USE_CXX_EXCEPTION) # if defined(__cplusplus) #define MRB_TRY(buf) try { #define MRB_CATCH(buf) } catch(mrb_jmpbuf *e) { if (e != (buf)) { throw e; } #define MRB_END_EXC(buf) } #define MRB_THROW(buf) throw(buf) typedef void *mrb_jmpbuf_impl; # else # error "need to be compiled with C++ compiler" # endif /* __cplusplus */ #else #include #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #define MRB_SETJMP _setjmp #define MRB_LONGJMP _longjmp #elif defined(__MINGW64__) && defined(__GNUC__) && __GNUC__ >= 4 #define MRB_SETJMP __builtin_setjmp #define MRB_LONGJMP __builtin_longjmp #else #define MRB_SETJMP setjmp #define MRB_LONGJMP longjmp #endif #define MRB_TRY(buf) if (MRB_SETJMP((buf)->impl) == 0) { #define MRB_CATCH(buf) } else { #define MRB_END_EXC(buf) } #define MRB_THROW(buf) MRB_LONGJMP((buf)->impl, 1); #define mrb_jmpbuf_impl jmp_buf #endif struct mrb_jmpbuf { mrb_jmpbuf_impl impl; }; #endif /* MRB_THROW_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/gc.h0000644000000000000000000000013215077107276021216 xustar0030 mtime=1761382078.101420652 30 atime=1761382080.122411407 30 ctime=1761382109.008300647 nghttp2-1.68.0/third-party/mruby/include/mruby/gc.h0000644000175100017510000000443015077107276021607 0ustar00runnerrunner/** ** @file mruby/gc.h - garbage collector for mruby ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_GC_H #define MRUBY_GC_H #include "common.h" /** * Uncommon memory management stuffs. */ MRB_BEGIN_DECL struct mrb_state; #define MRB_EACH_OBJ_OK 0 #define MRB_EACH_OBJ_BREAK 1 typedef int (mrb_each_object_callback)(struct mrb_state *mrb, struct RBasic *obj, void *data); void mrb_objspace_each_objects(struct mrb_state *mrb, mrb_each_object_callback *callback, void *data); size_t mrb_objspace_page_slot_size(void); MRB_API void mrb_free_context(struct mrb_state *mrb, struct mrb_context *c); #ifndef MRB_GC_ARENA_SIZE #define MRB_GC_ARENA_SIZE 100 #endif typedef enum { MRB_GC_STATE_ROOT = 0, MRB_GC_STATE_MARK, MRB_GC_STATE_SWEEP } mrb_gc_state; typedef struct mrb_gc { struct mrb_heap_page *heaps; /* all heaps pages */ struct mrb_heap_page *free_heaps;/* heaps for allocation */ struct mrb_heap_page *sweeps; /* page where sweep starts */ struct RBasic *gray_list; /* list of gray objects to be traversed incrementally */ struct RBasic *atomic_gray_list; /* list of objects to be traversed atomically */ size_t live; /* count of live objects */ size_t live_after_mark; /* old generation objects */ size_t threshold; /* threshold to start GC */ size_t oldgen_threshold; /* threshold to kick major GC */ mrb_gc_state state; /* current state of gc */ int interval_ratio; int step_ratio; int current_white_part :2; /* make white object by white_part */ mrb_bool iterating :1; /* currently iterating over objects */ mrb_bool disabled :1; /* GC disabled */ mrb_bool generational :1; /* generational GC mode */ mrb_bool full :1; /* major GC mode */ mrb_bool out_of_memory :1; /* out-of-memory error occurred */ #ifdef MRB_GC_FIXED_ARENA struct RBasic *arena[MRB_GC_ARENA_SIZE]; /* GC protection array */ #else struct RBasic **arena; /* GC protection array */ int arena_capa; /* size of protection array */ #endif int arena_idx; } mrb_gc; MRB_API mrb_bool mrb_object_dead_p(struct mrb_state *mrb, struct RBasic *object); #define MRB_GC_RED 7 MRB_END_DECL #endif /* MRUBY_GC_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/array.h0000644000000000000000000000013215077107276021743 xustar0030 mtime=1761382078.101420652 30 atime=1761382080.122411407 30 ctime=1761382108.984300716 nghttp2-1.68.0/third-party/mruby/include/mruby/array.h0000644000175100017510000001676515077107276022352 0ustar00runnerrunner/** ** @file mruby/array.h - Array class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_ARRAY_H #define MRUBY_ARRAY_H #include "common.h" /* * Array class */ MRB_BEGIN_DECL typedef struct mrb_shared_array { int refcnt; mrb_ssize len; mrb_value *ptr; } mrb_shared_array; #if defined(MRB_32BIT) && defined(MRB_NO_BOXING) && !defined(MRB_USE_FLOAT32) && !defined(MRB_ARY_NO_EMBED) # define MRB_ARY_NO_EMBED #endif #ifdef MRB_ARY_NO_EMBED # define MRB_ARY_EMBED_LEN_MAX 0 #else # define MRB_ARY_EMBED_LEN_MAX ((mrb_int)(sizeof(void*)*3/sizeof(mrb_value))) mrb_static_assert(MRB_ARY_EMBED_LEN_MAX > 0, "MRB_ARY_EMBED_LEN_MAX > 0"); #endif struct RArray { MRB_OBJECT_HEADER; union { struct { mrb_ssize len; union { mrb_ssize capa; mrb_shared_array *shared; } aux; mrb_value *ptr; } heap; #ifndef MRB_ARY_NO_EMBED mrb_value ary[MRB_ARY_EMBED_LEN_MAX]; #endif } as; }; #define mrb_ary_ptr(v) ((struct RArray*)(mrb_ptr(v))) #define mrb_ary_value(p) mrb_obj_value((void*)(p)) #define RARRAY(v) ((struct RArray*)(mrb_ptr(v))) #ifdef MRB_ARY_NO_EMBED #define ARY_EMBED_P(a) 0 #define ARY_UNSET_EMBED_FLAG(a) (void)0 #define ARY_EMBED_LEN(a) 0 #define ARY_SET_EMBED_LEN(a,len) (void)0 #define ARY_EMBED_PTR(a) 0 #else #define MRB_ARY_EMBED_MASK 7 #define ARY_EMBED_P(a) ((a)->flags & MRB_ARY_EMBED_MASK) #define ARY_UNSET_EMBED_FLAG(a) ((a)->flags &= ~(MRB_ARY_EMBED_MASK)) #define ARY_EMBED_LEN(a) ((mrb_int)(((a)->flags & MRB_ARY_EMBED_MASK) - 1)) #define ARY_SET_EMBED_LEN(a,len) ((a)->flags = ((a)->flags&~MRB_ARY_EMBED_MASK) | ((uint32_t)(len) + 1)) #define ARY_EMBED_PTR(a) ((a)->as.ary) #endif #define ARY_LEN(a) (ARY_EMBED_P(a)?ARY_EMBED_LEN(a):(mrb_int)(a)->as.heap.len) #define ARY_PTR(a) (ARY_EMBED_P(a)?ARY_EMBED_PTR(a):(a)->as.heap.ptr) #define RARRAY_LEN(a) ARY_LEN(RARRAY(a)) #define RARRAY_PTR(a) ARY_PTR(RARRAY(a)) #define ARY_SET_LEN(a,n) do {\ if (ARY_EMBED_P(a)) {\ mrb_assert((n) <= MRB_ARY_EMBED_LEN_MAX); \ ARY_SET_EMBED_LEN(a,n);\ }\ else\ (a)->as.heap.len = (n);\ } while (0) #define ARY_CAPA(a) (ARY_EMBED_P(a)?MRB_ARY_EMBED_LEN_MAX:(a)->as.heap.aux.capa) #define MRB_ARY_SHARED 256 #define ARY_SHARED_P(a) ((a)->flags & MRB_ARY_SHARED) #define ARY_SET_SHARED_FLAG(a) ((a)->flags |= MRB_ARY_SHARED) #define ARY_UNSET_SHARED_FLAG(a) ((a)->flags &= ~MRB_ARY_SHARED) MRB_API void mrb_ary_modify(mrb_state*, struct RArray*); MRB_API mrb_value mrb_ary_new_capa(mrb_state*, mrb_int); /* * Initializes a new array. * * Equivalent to: * * Array.new * * @param mrb The mruby state reference. * @return The initialized array. */ MRB_API mrb_value mrb_ary_new(mrb_state *mrb); /* * Initializes a new array with initial values * * Equivalent to: * * Array[value1, value2, ...] * * @param mrb The mruby state reference. * @param size The number of values. * @param vals The actual values. * @return The initialized array. */ MRB_API mrb_value mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals); /* * Initializes a new array with two initial values * * Equivalent to: * * Array[car, cdr] * * @param mrb The mruby state reference. * @param car The first value. * @param cdr The second value. * @return The initialized array. */ MRB_API mrb_value mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr); /* * Concatenate two arrays. The target array will be modified * * Equivalent to: * ary.concat(other) * * @param mrb The mruby state reference. * @param self The target array. * @param other The array that will be concatenated to self. */ MRB_API void mrb_ary_concat(mrb_state *mrb, mrb_value self, mrb_value other); /* * Create an array from the input. It tries calling to_a on the * value. If value does not respond to that, it creates a new * array with just this value. * * @param mrb The mruby state reference. * @param value The value to change into an array. * @return An array representation of value. */ MRB_API mrb_value mrb_ary_splat(mrb_state *mrb, mrb_value value); /* * Pushes value into array. * * Equivalent to: * * ary << value * * @param mrb The mruby state reference. * @param ary The array in which the value will be pushed * @param value The value to be pushed into array */ MRB_API void mrb_ary_push(mrb_state *mrb, mrb_value array, mrb_value value); /* * Pops the last element from the array. * * Equivalent to: * * ary.pop * * @param mrb The mruby state reference. * @param ary The array from which the value will be popped. * @return The popped value. */ MRB_API mrb_value mrb_ary_pop(mrb_state *mrb, mrb_value ary); /* * Sets a value on an array at the given index * * Equivalent to: * * ary[n] = val * * @param mrb The mruby state reference. * @param ary The target array. * @param n The array index being referenced. * @param val The value being set. */ MRB_API void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val); /* * Replace the array with another array * * Equivalent to: * * ary.replace(other) * * @param mrb The mruby state reference * @param self The target array. * @param other The array to replace it with. */ MRB_API void mrb_ary_replace(mrb_state *mrb, mrb_value self, mrb_value other); /* * Unshift an element into the array * * Equivalent to: * * ary.unshift(item) * * @param mrb The mruby state reference. * @param self The target array. * @param item The item to unshift. */ MRB_API mrb_value mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item); /* * Get nth element in the array * * Equivalent to: * * ary[offset] * * @param ary The target array. * @param offset The element position (negative counts from the tail). */ MRB_API mrb_value mrb_ary_entry(mrb_value ary, mrb_int offset); #define mrb_ary_ref(mrb, ary, n) mrb_ary_entry(ary, n) /* * Replace subsequence of an array. * * Equivalent to: * * ary[head, len] = rpl * * @param mrb The mruby state reference. * @param self The array from which the value will be partiality replaced. * @param head Beginning position of a replacement subsequence. * @param len Length of a replacement subsequence. * @param rpl The array of replacement elements. * It is possible to pass `mrb_undef_value()` instead of an empty array. * @return The receiver array. */ MRB_API mrb_value mrb_ary_splice(mrb_state *mrb, mrb_value self, mrb_int head, mrb_int len, mrb_value rpl); /* * Shifts the first element from the array. * * Equivalent to: * * ary.shift * * @param mrb The mruby state reference. * @param self The array from which the value will be shifted. * @return The shifted value. */ MRB_API mrb_value mrb_ary_shift(mrb_state *mrb, mrb_value self); /* * Removes all elements from the array * * Equivalent to: * * ary.clear * * @param mrb The mruby state reference. * @param self The target array. * @return self */ MRB_API mrb_value mrb_ary_clear(mrb_state *mrb, mrb_value self); /* * Join the array elements together in a string * * Equivalent to: * * ary.join(sep="") * * @param mrb The mruby state reference. * @param ary The target array * @param sep The separator, can be NULL */ MRB_API mrb_value mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep); /* * Update the capacity of the array * * @param mrb The mruby state reference. * @param ary The target array. * @param new_len The new capacity of the array */ MRB_API mrb_value mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int new_len); MRB_END_DECL #endif /* MRUBY_ARRAY_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/hash.h0000644000000000000000000000013215077107276021550 xustar0030 mtime=1761382078.101420652 30 atime=1761382080.122411407 30 ctime=1761382108.972300751 nghttp2-1.68.0/third-party/mruby/include/mruby/hash.h0000644000175100017510000001327515077107276022150 0ustar00runnerrunner/** ** @file mruby/hash.h - Hash class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_HASH_H #define MRUBY_HASH_H #include "common.h" /** * Hash class */ MRB_BEGIN_DECL /* offset of `iv` must be 3 words */ struct RHash { MRB_OBJECT_HEADER; #ifdef MRB_64BIT uint32_t size; struct iv_tbl *iv; uint32_t ea_capa; uint32_t ea_n_used; #else struct iv_tbl *iv; uint32_t size; #endif union { struct hash_entry *ea; struct hash_table *ht; } hsh; }; #define mrb_hash_ptr(v) ((struct RHash*)(mrb_ptr(v))) #define mrb_hash_value(p) mrb_obj_value((void*)(p)) MRB_API mrb_value mrb_hash_new_capa(mrb_state *mrb, mrb_int capa); /* * Initializes a new hash. * * Equivalent to: * * Hash.new * * @param mrb The mruby state reference. * @return The initialized hash. */ MRB_API mrb_value mrb_hash_new(mrb_state *mrb); /* * Sets a keys and values to hashes. * * Equivalent to: * * hash[key] = val * * @param mrb The mruby state reference. * @param hash The target hash. * @param key The key to set. * @param val The value to set. * @return The value. */ MRB_API void mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val); /* * Gets a value from a key. If the key is not found, the default of the * hash is used. * * Equivalent to: * * hash[key] * * @param mrb The mruby state reference. * @param hash The target hash. * @param key The key to get. * @return The found value. */ MRB_API mrb_value mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key); /* * Gets a value from a key. If the key is not found, the default parameter is * used. * * Equivalent to: * * hash.key?(key) ? hash[key] : def * * @param mrb The mruby state reference. * @param hash The target hash. * @param key The key to get. * @param def The default value. * @return The found value. */ MRB_API mrb_value mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def); /* * Deletes hash key and value pair. * * Equivalent to: * * hash.delete(key) * * @param mrb The mruby state reference. * @param hash The target hash. * @param key The key to delete. * @return The deleted value. This value is not protected from GC. Use `mrb_gc_protect()` if necessary. */ MRB_API mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key); /* * Gets an array of keys. * * Equivalent to: * * hash.keys * * @param mrb The mruby state reference. * @param hash The target hash. * @return An array with the keys of the hash. */ MRB_API mrb_value mrb_hash_keys(mrb_state *mrb, mrb_value hash); /* * Check if the hash has the key. * * Equivalent to: * * hash.key?(key) * * @param mrb The mruby state reference. * @param hash The target hash. * @param key The key to check existence. * @return True if the hash has the key */ MRB_API mrb_bool mrb_hash_key_p(mrb_state *mrb, mrb_value hash, mrb_value key); /* * Check if the hash is empty * * Equivalent to: * * hash.empty? * * @param mrb The mruby state reference. * @param self The target hash. * @return True if the hash is empty, false otherwise. */ MRB_API mrb_bool mrb_hash_empty_p(mrb_state *mrb, mrb_value self); /* * Gets an array of values. * * Equivalent to: * * hash.values * * @param mrb The mruby state reference. * @param hash The target hash. * @return An array with the values of the hash. */ MRB_API mrb_value mrb_hash_values(mrb_state *mrb, mrb_value hash); /* * Clears the hash. * * Equivalent to: * * hash.clear * * @param mrb The mruby state reference. * @param hash The target hash. * @return The hash */ MRB_API mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash); /* * Get hash size. * * Equivalent to: * * hash.size * * @param mrb The mruby state reference. * @param hash The target hash. * @return The hash size. */ MRB_API mrb_int mrb_hash_size(mrb_state *mrb, mrb_value hash); /* * Copies the hash. This function does NOT copy the instance variables * (except for the default value). Use mrb_obj_dup() to copy the instance * variables as well. * * @param mrb The mruby state reference. * @param hash The target hash. * @return The copy of the hash */ MRB_API mrb_value mrb_hash_dup(mrb_state *mrb, mrb_value hash); /* * Merges two hashes. The first hash will be modified by the * second hash. * * @param mrb The mruby state reference. * @param hash1 The target hash. * @param hash2 Updating hash */ MRB_API void mrb_hash_merge(mrb_state *mrb, mrb_value hash1, mrb_value hash2); #define RHASH(hash) ((struct RHash*)(mrb_ptr(hash))) #define MRB_HASH_IB_BIT_BIT 5 #define MRB_HASH_AR_EA_CAPA_BIT 5 #define MRB_HASH_IB_BIT_SHIFT 0 #define MRB_HASH_AR_EA_CAPA_SHIFT 0 #define MRB_HASH_AR_EA_N_USED_SHIFT MRB_HASH_AR_EA_CAPA_BIT #define MRB_HASH_SIZE_FLAGS_SHIFT (MRB_HASH_AR_EA_CAPA_BIT * 2) #define MRB_HASH_IB_BIT_MASK ((1 << MRB_HASH_IB_BIT_BIT) - 1) #define MRB_HASH_AR_EA_CAPA_MASK ((1 << MRB_HASH_AR_EA_CAPA_BIT) - 1) #define MRB_HASH_AR_EA_N_USED_MASK (MRB_HASH_AR_EA_CAPA_MASK << MRB_HASH_AR_EA_N_USED_SHIFT) #define MRB_HASH_DEFAULT (1 << (MRB_HASH_SIZE_FLAGS_SHIFT + 0)) #define MRB_HASH_PROC_DEFAULT (1 << (MRB_HASH_SIZE_FLAGS_SHIFT + 1)) #define MRB_HASH_HT (1 << (MRB_HASH_SIZE_FLAGS_SHIFT + 2)) #define MRB_RHASH_DEFAULT_P(hash) (RHASH(hash)->flags & MRB_HASH_DEFAULT) #define MRB_RHASH_PROCDEFAULT_P(hash) (RHASH(hash)->flags & MRB_HASH_PROC_DEFAULT) /* return non zero to break the loop */ typedef int (mrb_hash_foreach_func)(mrb_state *mrb, mrb_value key, mrb_value val, void *data); MRB_API void mrb_hash_foreach(mrb_state *mrb, struct RHash *hash, mrb_hash_foreach_func *func, void *p); MRB_END_DECL #endif /* MRUBY_HASH_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/irep.h0000644000000000000000000000013215077107276021564 xustar0030 mtime=1761382078.101420652 30 atime=1761382080.123411402 30 ctime=1761382108.981300725 nghttp2-1.68.0/third-party/mruby/include/mruby/irep.h0000644000175100017510000000761415077107276022164 0ustar00runnerrunner/** ** @file mruby/irep.h - mrb_irep structure ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_IREP_H #define MRUBY_IREP_H #include "common.h" #include /** * Compiled mruby scripts. */ MRB_BEGIN_DECL enum irep_pool_type { IREP_TT_STR = 0, /* string (need free) */ IREP_TT_SSTR = 2, /* string (static) */ IREP_TT_INT32 = 1, /* 32-bit integer */ IREP_TT_INT64 = 3, /* 64-bit integer */ IREP_TT_BIGINT = 7, /* big integer (not yet supported) */ IREP_TT_FLOAT = 5, /* float (double/float) */ }; #define IREP_TT_NFLAG 1 /* number (non string) flag */ #define IREP_TT_SFLAG 2 /* static string flag */ typedef struct mrb_irep_pool { uint32_t tt; /* packed type and length (for string) */ union { const char *str; int32_t i32; int64_t i64; #ifndef MRB_NO_FLOAT mrb_float f; #endif } u; } mrb_irep_pool; enum mrb_catch_type { MRB_CATCH_RESCUE = 0, MRB_CATCH_ENSURE = 1, }; struct mrb_irep_catch_handler { uint8_t type; /* enum mrb_catch_type */ uint8_t begin[4]; /* The starting address to match the handler. Includes this. */ uint8_t end[4]; /* The endpoint address that matches the handler. Not Includes this. */ uint8_t target[4]; /* The address to jump to if a match is made. */ }; /* Program data array struct */ struct mrb_irep { uint16_t nlocals; /* Number of local variables */ uint16_t nregs; /* Number of register variables */ uint16_t clen; /* Number of catch handlers */ uint8_t flags; const mrb_code *iseq; /* * A catch handler table is placed after the iseq entity. * The reason it doesn't add fields to the structure is to keep the mrb_irep * structure from bloating. The catch handler table can be obtained with * `mrb_irep_catch_handler_table(irep)`. */ const mrb_irep_pool *pool; const mrb_sym *syms; const struct mrb_irep *const *reps; const mrb_sym *lv; /* debug info */ struct mrb_irep_debug_info *debug_info; uint32_t ilen; uint16_t plen, slen; uint16_t rlen; uint16_t refcnt; }; #define MRB_ISEQ_NO_FREE 1 #define MRB_IREP_NO_FREE 2 #define MRB_IREP_STATIC (MRB_ISEQ_NO_FREE | MRB_IREP_NO_FREE) MRB_API mrb_irep *mrb_add_irep(mrb_state *mrb); /** * load mruby bytecode functions * * Please note! Currently due to interactions with the GC calling these * functions will leak one RProc object per function call. To prevent this save * the current memory arena before calling and restore the arena right after, * like so * * int ai = mrb_gc_arena_save(mrb); * mrb_value status = mrb_load_irep(mrb, buffer); * mrb_gc_arena_restore(mrb, ai); * * Also, when called from a C function defined as a method, the current stack is * destroyed. If processing continues after this function, the objects obtained * from the arguments must be protected as needed before this function. */ /* @param [const uint8_t*] irep code, expected as a literal */ MRB_API mrb_value mrb_load_irep(mrb_state *, const uint8_t *); /* * @param [const void*] irep code * @param [size_t] size of irep buffer. */ MRB_API mrb_value mrb_load_irep_buf(mrb_state *, const void *, size_t); /* @param [const uint8_t*] irep code, expected as a literal */ MRB_API mrb_value mrb_load_irep_cxt(mrb_state *, const uint8_t *, mrbc_context *); /* * @param [const void*] irep code * @param [size_t] size of irep buffer. */ MRB_API mrb_value mrb_load_irep_buf_cxt(mrb_state *, const void *, size_t, mrbc_context *); struct mrb_insn_data { uint8_t insn; uint32_t a; uint16_t b; uint16_t c; const mrb_code *addr; }; #define mrb_irep_catch_handler_pack(n, v) uint32_to_bin(n, v) #define mrb_irep_catch_handler_unpack(v) bin_to_uint32(v) void mrb_irep_incref(mrb_state *, struct mrb_irep *); void mrb_irep_decref(mrb_state *, struct mrb_irep *); void mrb_irep_cutref(mrb_state *, struct mrb_irep *); MRB_END_DECL #endif /* MRUBY_IREP_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/compile.h0000644000000000000000000000013215077107276022255 xustar0030 mtime=1761382078.101420652 30 atime=1761382080.122411407 30 ctime=1761382108.983300719 nghttp2-1.68.0/third-party/mruby/include/mruby/compile.h0000644000175100017510000001573315077107276022656 0ustar00runnerrunner/** ** @file mruby/compile.h - mruby parser ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_COMPILE_H #define MRUBY_COMPILE_H #include "common.h" #include "mruby/mempool.h" /** * mruby Compiler */ MRB_BEGIN_DECL #include struct mrb_parser_state; /* load context */ typedef struct mrb_ccontext { mrb_sym *syms; int slen; char *filename; uint16_t lineno; int (*partial_hook)(struct mrb_parser_state*); void *partial_data; struct RClass *target_class; mrb_bool capture_errors:1; mrb_bool dump_result:1; mrb_bool no_exec:1; mrb_bool keep_lv:1; mrb_bool no_optimize:1; mrb_bool no_ext_ops:1; const struct RProc *upper; size_t parser_nerr; } mrb_ccontext; /* compiler context */ MRB_API mrb_ccontext* mrb_ccontext_new(mrb_state *mrb); MRB_API void mrb_ccontext_free(mrb_state *mrb, mrb_ccontext *cxt); MRB_API const char *mrb_ccontext_filename(mrb_state *mrb, mrb_ccontext *c, const char *s); MRB_API void mrb_ccontext_partial_hook(mrb_ccontext *c, int (*partial_hook)(struct mrb_parser_state*), void*data); MRB_API void mrb_ccontext_cleanup_local_variables(mrb_ccontext *c); /* compatibility macros */ #define mrbc_context mrb_ccontext #define mrbc_context_new mrb_ccontext_new #define mrbc_context_free mrb_ccontext_free #define mrbc_filename mrb_ccontext_filename #define mrbc_partial_hook mrb_ccontext_partial_hook #define mrbc_cleanup_local_variables mrb_ccontext_cleanup_local_variables /* AST node structure */ typedef struct mrb_ast_node { struct mrb_ast_node *car, *cdr; uint16_t lineno, filename_index; } mrb_ast_node; /* lexer states */ enum mrb_lex_state_enum { EXPR_BEG, /* ignore newline, +/- is a sign. */ EXPR_END, /* newline significant, +/- is an operator. */ EXPR_ENDARG, /* ditto, and unbound braces. */ EXPR_ENDFN, /* ditto, and unbound braces. */ EXPR_ARG, /* newline significant, +/- is an operator. */ EXPR_CMDARG, /* newline significant, +/- is an operator. */ EXPR_MID, /* newline significant, +/- is a sign. */ EXPR_FNAME, /* ignore newline, no reserved words. */ EXPR_DOT, /* right after '.' or '::', no reserved words. */ EXPR_CLASS, /* immediate after 'class', no here document. */ EXPR_VALUE, /* alike EXPR_BEG but label is disallowed. */ EXPR_MAX_STATE }; /* saved error message */ struct mrb_parser_message { uint16_t lineno; int column; char* message; }; #define STR_FUNC_PARSING 0x01 #define STR_FUNC_EXPAND 0x02 #define STR_FUNC_REGEXP 0x04 #define STR_FUNC_WORD 0x08 #define STR_FUNC_SYMBOL 0x10 #define STR_FUNC_ARRAY 0x20 #define STR_FUNC_HEREDOC 0x40 #define STR_FUNC_XQUOTE 0x80 enum mrb_string_type { str_not_parsing = (0), str_squote = (STR_FUNC_PARSING), str_dquote = (STR_FUNC_PARSING|STR_FUNC_EXPAND), str_regexp = (STR_FUNC_PARSING|STR_FUNC_REGEXP|STR_FUNC_EXPAND), str_sword = (STR_FUNC_PARSING|STR_FUNC_WORD|STR_FUNC_ARRAY), str_dword = (STR_FUNC_PARSING|STR_FUNC_WORD|STR_FUNC_ARRAY|STR_FUNC_EXPAND), str_ssym = (STR_FUNC_PARSING|STR_FUNC_SYMBOL), str_ssymbols = (STR_FUNC_PARSING|STR_FUNC_SYMBOL|STR_FUNC_ARRAY), str_dsymbols = (STR_FUNC_PARSING|STR_FUNC_SYMBOL|STR_FUNC_ARRAY|STR_FUNC_EXPAND), str_heredoc = (STR_FUNC_PARSING|STR_FUNC_HEREDOC), str_xquote = (STR_FUNC_PARSING|STR_FUNC_XQUOTE|STR_FUNC_EXPAND), }; /* heredoc structure */ struct mrb_parser_heredoc_info { mrb_bool allow_indent:1; mrb_bool remove_indent:1; mrb_bool line_head:1; size_t indent; mrb_ast_node *indented; enum mrb_string_type type; const char *term; int term_len; mrb_ast_node *doc; }; #define MRB_PARSER_TOKBUF_MAX (UINT16_MAX-1) #define MRB_PARSER_TOKBUF_SIZE 256 /* parser structure */ struct mrb_parser_state { mrb_state *mrb; mempool *pool; mrb_ast_node *cells; const char *s, *send; #ifndef MRB_NO_STDIO /* If both f and s are non-null, it will be taken preferentially from s until s < send. */ FILE *f; #endif mrb_ccontext *cxt; mrb_sym filename_sym; uint16_t lineno; int column; enum mrb_lex_state_enum lstate; struct parser_lex_strterm *lex_strterm; unsigned int cond_stack; unsigned int cmdarg_stack; int paren_nest; int lpar_beg; int in_def, in_single; mrb_bool cmd_start:1; mrb_ast_node *locals; mrb_ast_node *pb; char *tokbuf; char buf[MRB_PARSER_TOKBUF_SIZE]; int tidx; int tsiz; mrb_ast_node *heredocs_from_nextline; mrb_ast_node *parsing_heredoc; void *ylval; size_t nerr; size_t nwarn; mrb_ast_node *tree; mrb_bool no_optimize:1; mrb_bool capture_errors:1; mrb_bool no_ext_ops:1; const struct RProc *upper; struct mrb_parser_message error_buffer[10]; struct mrb_parser_message warn_buffer[10]; mrb_sym* filename_table; uint16_t filename_table_length; uint16_t current_filename_index; mrb_ast_node *nvars; }; MRB_API struct mrb_parser_state* mrb_parser_new(mrb_state*); MRB_API void mrb_parser_free(struct mrb_parser_state*); MRB_API void mrb_parser_parse(struct mrb_parser_state*,mrb_ccontext*); MRB_API void mrb_parser_set_filename(struct mrb_parser_state*, char const*); MRB_API mrb_sym mrb_parser_get_filename(struct mrb_parser_state*, uint16_t idx); /* utility functions */ #ifndef MRB_NO_STDIO MRB_API struct mrb_parser_state* mrb_parse_file(mrb_state*,FILE*,mrb_ccontext*); #endif MRB_API struct mrb_parser_state* mrb_parse_string(mrb_state*,const char*,mrb_ccontext*); MRB_API struct mrb_parser_state* mrb_parse_nstring(mrb_state*,const char*,size_t,mrb_ccontext*); MRB_API struct RProc* mrb_generate_code(mrb_state*, struct mrb_parser_state*); MRB_API mrb_value mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrb_ccontext *c); /** * program load functions * * Please note! Currently due to interactions with the GC calling these functions will * leak one RProc object per function call. * To prevent this save the current memory arena before calling and restore the arena * right after, like so * * int ai = mrb_gc_arena_save(mrb); * mrb_value status = mrb_load_string(mrb, buffer); * mrb_gc_arena_restore(mrb, ai); * * Also, when called from a C function defined as a method, the current stack is destroyed. * If processing continues after this function, the objects obtained from the arguments * must be protected as needed before this function. */ #ifndef MRB_NO_STDIO MRB_API mrb_value mrb_load_file(mrb_state*,FILE*); MRB_API mrb_value mrb_load_file_cxt(mrb_state*,FILE*, mrb_ccontext *cxt); MRB_API mrb_value mrb_load_detect_file_cxt(mrb_state *mrb, FILE *fp, mrb_ccontext *c); #endif MRB_API mrb_value mrb_load_string(mrb_state *mrb, const char *s); MRB_API mrb_value mrb_load_nstring(mrb_state *mrb, const char *s, size_t len); MRB_API mrb_value mrb_load_string_cxt(mrb_state *mrb, const char *s, mrb_ccontext *cxt); MRB_API mrb_value mrb_load_nstring_cxt(mrb_state *mrb, const char *s, size_t len, mrb_ccontext *cxt); /** @} */ MRB_END_DECL #endif /* MRUBY_COMPILE_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/boxing_nan.h0000644000000000000000000000013215077107276022747 xustar0030 mtime=1761382078.101420652 30 atime=1761382080.122411407 30 ctime=1761382108.999300673 nghttp2-1.68.0/third-party/mruby/include/mruby/boxing_nan.h0000644000175100017510000001222315077107276023337 0ustar00runnerrunner/** ** @file mruby/boxing_nan.h - nan boxing mrb_value definition ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_BOXING_NAN_H #define MRUBY_BOXING_NAN_H #ifdef MRB_USE_FLOAT32 # error ---->> MRB_NAN_BOXING and MRB_USE_FLOAT32 conflict <<---- #endif #ifdef MRB_NO_FLOAT # error ---->> MRB_NAN_BOXING and MRB_NO_FLOAT conflict <<---- #endif #define MRB_FIXNUM_MIN INT32_MIN #define MRB_FIXNUM_MAX INT32_MAX enum mrb_nanbox_tt_inline { MRB_NANBOX_TT_OBJECT = 0, MRB_NANBOX_TT_INTEGER = 1, MRB_NANBOX_TT_MISC = 2, MRB_NANBOX_TT_CPTR = 3, }; /* value representation by nan-boxing: * float : SEEEEEEE EEEEFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF * +/-inf: S1111111 11110000 00000000 00000000 00000000 00000000 00000000 00000000 * nan : 01111111 11111000 00000000 00000000 00000000 00000000 00000000 00000000 * int : 01111111 11111001 00000000 00000000 IIIIIIII IIIIIIII IIIIIIII IIIIIIII * sym : 01111111 11111110 00000000 00TTTTTT SSSSSSSS SSSSSSSS SSSSSSSS SSSSSSSS * misc : 01111111 11111110 00000000 00TTTTTT 00000000 00000000 00000000 0000MMMM * object: 01111111 11111100 PPPPPPPP PPPPPPPP PPPPPPPP PPPPPPPP PPPPPPPP PPPPPP00 * cptr : 01111111 11111111 PPPPPPPP PPPPPPPP PPPPPPPP PPPPPPPP PPPPPPPP PPPPPPPP * Stored as O = R + 0x8004000000000000, retrieved as R = O - 0x8004000000000000. * This makes pointers have all zeros in the top 32 bits. */ typedef struct mrb_value { uint64_t u; } mrb_value; static inline mrb_float mrb_nan_boxing_value_float(mrb_value v) { union { mrb_float f; uint64_t u; } x; x.u = v.u - 0x8004000000000000; return x.f; } #define SET_FLOAT_VALUE(mrb,r,f) do { \ union { \ mrb_float f; \ uint64_t u; \ } float_uint_union; \ if ((f) != (f)) { /* NaN */ \ float_uint_union.u = 0x7ff8000000000000UL; \ } \ else { \ float_uint_union.f = (f); \ } \ r.u = float_uint_union.u + 0x8004000000000000; \ } while(0) #define mrb_float_p(o) (((uint64_t)((o).u)&0xfffc000000000000) != 0) struct RInteger { MRB_OBJECT_HEADER; mrb_int i; }; #define mrb_nb_tt(o) ((enum mrb_nanbox_tt_inline)((uint32_t)((o).u>>48)&3)) MRB_INLINE enum mrb_vtype mrb_type(mrb_value o) { if (mrb_float_p(o)) return MRB_TT_FLOAT; int64_t u = o.u; switch (mrb_nb_tt(o)) { case MRB_NANBOX_TT_OBJECT: { if (u == 0) return MRB_TT_FALSE; return ((struct RBasic*)(uintptr_t)u)->tt; } case MRB_NANBOX_TT_INTEGER: return MRB_TT_INTEGER; case MRB_NANBOX_TT_MISC: return (enum mrb_vtype)((uint32_t)(o.u >> 32) & 0x1f); case MRB_NANBOX_TT_CPTR: return MRB_TT_CPTR; default: /* never happen */ return MRB_TT_FLOAT; } } MRB_INLINE enum mrb_vtype mrb_unboxed_type(mrb_value o) { if (!mrb_float_p(o) && mrb_nb_tt(o) == MRB_NANBOX_TT_OBJECT && o.u != 0) { return ((struct RBasic*)(uintptr_t)o.u)->tt; } else { return MRB_TT_FALSE; } } #define NANBOX_SET_MISC_VALUE(r,t,i) NANBOX_SET_VALUE(r, MRB_NANBOX_TT_MISC, ((uint64_t)(t)<<32) | (i)) #define mrb_float(o) mrb_nan_boxing_value_float(o) #ifdef MRB_INT64 /* #ifdef MRB_32BIT #define mrb_fixnum(o) ((mrb_int)((intptr_t)0xffffffffffff&((o).u))|(((o).u & 0x800000000000)?0xffff000000000000:0)) #else #define mrb_fixnum(o) ((mrb_int)(int32_t)((o).u)) #endif */ #define mrb_fixnum(o) ((mrb_int)(int32_t)((o).u)) static inline mrb_int mrb_nan_boxing_value_int(mrb_value v) { uint64_t u = v.u; if (mrb_nb_tt(v)==MRB_NANBOX_TT_OBJECT) { struct RInteger *p = (struct RInteger*)(uintptr_t)u; return p->i; } return mrb_fixnum(v); } #define mrb_integer(o) mrb_nan_boxing_value_int(o) #else #define mrb_fixnum(o) ((mrb_int)(((uintptr_t)0xffffffff)&((o).u))) #define mrb_integer(o) mrb_fixnum(o) #endif #define mrb_symbol(o) ((mrb_sym)((uintptr_t)0xffffffff)&((o).u)) #define mrb_ptr(o) ((void*)(uintptr_t)(o).u) #define mrb_cptr(o) ((void*)(uintptr_t)(0xffffffffffffULL&(o).u)) #define NANBOX_SET_VALUE(o, tt, v) do { \ (o).u = ((uint64_t)(tt)<<48) | ((uint64_t)(v)); \ } while (0) #define SET_NIL_VALUE(r) ((r).u = 0) #define SET_FALSE_VALUE(r) NANBOX_SET_MISC_VALUE(r, MRB_TT_FALSE, 1) #define SET_TRUE_VALUE(r) NANBOX_SET_MISC_VALUE(r, MRB_TT_TRUE, 1) #define SET_BOOL_VALUE(r,b) NANBOX_SET_MISC_VALUE(r, (b) ? MRB_TT_TRUE : MRB_TT_FALSE, 1) #ifdef MRB_INT64 MRB_API mrb_value mrb_boxing_int_value(struct mrb_state*, mrb_int); #define SET_INT_VALUE(mrb, r, n) ((r) = mrb_boxing_int_value(mrb, n)) #else #define SET_INT_VALUE(mrb, r, n) SET_FIXNUM_VALUE(r, n) #endif #define SET_FIXNUM_VALUE(r,n) NANBOX_SET_VALUE(r, MRB_NANBOX_TT_INTEGER, (uint32_t)(n)) #define SET_SYM_VALUE(r,v) NANBOX_SET_MISC_VALUE(r, MRB_TT_SYMBOL, (uint32_t)(v)) #define SET_OBJ_VALUE(r,v) do {(r).u = (uint64_t)(uintptr_t)(v);} while (0) #define SET_CPTR_VALUE(mrb,r,v) NANBOX_SET_VALUE(r, MRB_NANBOX_TT_CPTR, (uint64_t)(uintptr_t)(v) & 0x0000ffffffffffffULL) #define SET_UNDEF_VALUE(r) NANBOX_SET_MISC_VALUE(r, MRB_TT_UNDEF, 4) #define mrb_immediate_p(o) ((mrb_float_p(o) || mrb_nb_tt(o) != MRB_NANBOX_TT_OBJECT) || (o).u == 0) #define mrb_nil_p(o) ((o).u == 0) #define mrb_false_p(o) (mrb_type(o) == MRB_TT_FALSE || (o).u == 0) #define mrb_fixnum_p(o) (!mrb_float_p(o) && mrb_nb_tt(o)==MRB_NANBOX_TT_INTEGER) #endif /* MRUBY_BOXING_NAN_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/string.h0000644000000000000000000000013215077107276022133 xustar0030 mtime=1761382078.102420648 30 atime=1761382080.123411402 30 ctime=1761382108.974300745 nghttp2-1.68.0/third-party/mruby/include/mruby/string.h0000644000175100017510000003052115077107276022524 0ustar00runnerrunner/** ** @file mruby/string.h - String class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_STRING_H #define MRUBY_STRING_H #include "common.h" /** * String class */ MRB_BEGIN_DECL extern const char mrb_digitmap[]; #define RSTRING_EMBED_LEN_MAX \ ((mrb_int)(sizeof(void*) * 3 + sizeof(void*) - 32 / CHAR_BIT - 1)) struct RString { MRB_OBJECT_HEADER; union { struct { mrb_ssize len; union { mrb_ssize capa; struct mrb_shared_string *shared; struct RString *fshared; } aux; char *ptr; } heap; } as; }; struct RStringEmbed { MRB_OBJECT_HEADER; char ary[RSTRING_EMBED_LEN_MAX+1]; }; #define RSTR_SET_TYPE(s, type) ((s)->flags = ((s)->flags & ~(MRB_STR_TYPE_MASK|MRB_STR_EMBED_LEN_MASK)) | MRB_STR_##type) #define MRB_STR_NORMAL 0 #define MRB_STR_SHARED 1 #define MRB_STR_FSHARED 2 #define MRB_STR_NOFREE 4 #define MRB_STR_EMBED 8 #define MRB_STR_TYPE_MASK 15 #define MRB_STR_EMBED_LEN_SHIFT 6 #define MRB_STR_EMBED_LEN_BITS 5 #define MRB_STR_EMBED_LEN_MASK (((1 << MRB_STR_EMBED_LEN_BITS) - 1) << MRB_STR_EMBED_LEN_SHIFT) #define MRB_STR_BINARY 16 #define MRB_STR_SINGLE_BYTE 32 #define MRB_STR_STATE_MASK 48 #define RSTR_EMBED_P(s) ((s)->flags & MRB_STR_EMBED) #define RSTR_SET_EMBED_FLAG(s) ((s)->flags |= MRB_STR_EMBED) #define RSTR_SET_EMBED_LEN(s, n) do {\ size_t tmp_n = (n);\ (s)->flags &= ~MRB_STR_EMBED_LEN_MASK;\ (s)->flags |= (tmp_n) << MRB_STR_EMBED_LEN_SHIFT;\ } while (0) #define RSTR_SET_LEN(s, n) do {\ if (RSTR_EMBED_P(s)) {\ RSTR_SET_EMBED_LEN((s),(n));\ }\ else {\ (s)->as.heap.len = (mrb_ssize)(n);\ }\ } while (0) #define RSTR_EMBED_PTR(s) (((struct RStringEmbed*)(s))->ary) #define RSTR_EMBED_LEN(s)\ (mrb_int)(((s)->flags & MRB_STR_EMBED_LEN_MASK) >> MRB_STR_EMBED_LEN_SHIFT) #define RSTR_EMBEDDABLE_P(len) ((len) <= RSTRING_EMBED_LEN_MAX) #define RSTR_PTR(s) ((RSTR_EMBED_P(s)) ? RSTR_EMBED_PTR(s) : (s)->as.heap.ptr) #define RSTR_LEN(s) ((RSTR_EMBED_P(s)) ? RSTR_EMBED_LEN(s) : (s)->as.heap.len) #define RSTR_CAPA(s) (RSTR_EMBED_P(s) ? RSTRING_EMBED_LEN_MAX : (s)->as.heap.aux.capa) #define RSTR_SHARED_P(s) ((s)->flags & MRB_STR_SHARED) #define RSTR_FSHARED_P(s) ((s)->flags & MRB_STR_FSHARED) #define RSTR_NOFREE_P(s) ((s)->flags & MRB_STR_NOFREE) #ifdef MRB_UTF8_STRING # define RSTR_SINGLE_BYTE_P(s) ((s)->flags & MRB_STR_SINGLE_BYTE) # define RSTR_SET_SINGLE_BYTE_FLAG(s) ((s)->flags |= MRB_STR_SINGLE_BYTE) # define RSTR_UNSET_SINGLE_BYTE_FLAG(s) ((s)->flags &= ~MRB_STR_SINGLE_BYTE) # define RSTR_WRITE_SINGLE_BYTE_FLAG(s, v) (RSTR_UNSET_SINGLE_BYTE_FLAG(s), (s)->flags |= v) # define RSTR_COPY_SINGLE_BYTE_FLAG(dst, src) RSTR_WRITE_SINGLE_BYTE_FLAG(dst, RSTR_SINGLE_BYTE_P(src)) #else # define RSTR_SINGLE_BYTE_P(s) TRUE # define RSTR_SET_SINGLE_BYTE_FLAG(s) (void)0 # define RSTR_UNSET_SINGLE_BYTE_FLAG(s) (void)0 # define RSTR_WRITE_SINGLE_BYTE_FLAG(s, v) (void)0 # define RSTR_COPY_SINGLE_BYTE_FLAG(dst, src) (void)0 #endif #define RSTR_SET_ASCII_FLAG(s) RSTR_SET_SINGLE_BYTE_FLAG(s) #define RSTR_BINARY_P(s) ((s)->flags & MRB_STR_BINARY) /** * Returns a pointer from a Ruby string */ #define mrb_str_ptr(s) ((struct RString*)(mrb_ptr(s))) #define RSTRING(s) mrb_str_ptr(s) #define RSTRING_PTR(s) RSTR_PTR(RSTRING(s)) #define RSTRING_EMBED_LEN(s) RSTR_EMBED_LEN(RSTRING(s)) #define RSTRING_LEN(s) RSTR_LEN(RSTRING(s)) #define RSTRING_CAPA(s) RSTR_CAPA(RSTRING(s)) #define RSTRING_END(s) (RSTRING_PTR(s) + RSTRING_LEN(s)) #define RSTRING_CSTR(mrb,s) mrb_string_cstr(mrb, s) MRB_API void mrb_str_modify(mrb_state *mrb, struct RString *s); /* mrb_str_modify() with keeping ASCII flag if set */ MRB_API void mrb_str_modify_keep_ascii(mrb_state *mrb, struct RString *s); /** * Finds the index of a substring in a string */ MRB_API mrb_int mrb_str_index(mrb_state *mrb, mrb_value str, const char *p, mrb_int len, mrb_int offset); #define mrb_str_index_lit(mrb, str, lit, off) mrb_str_index(mrb, str, lit, mrb_strlen_lit(lit), off); /** * Appends self to other. Returns self as a concatenated string. * * * Example: * * int * main(int argc, * char **argv) * { * // Variable declarations. * mrb_value str1; * mrb_value str2; * * mrb_state *mrb = mrb_open(); * if (!mrb) * { * // handle error * } * * // Creates new Ruby strings. * str1 = mrb_str_new_lit(mrb, "abc"); * str2 = mrb_str_new_lit(mrb, "def"); * * // Concatenates str2 to str1. * mrb_str_concat(mrb, str1, str2); * * // Prints new Concatenated Ruby string. * mrb_p(mrb, str1); * * mrb_close(mrb); * return 0; * } * * Result: * * => "abcdef" * * @param mrb The current mruby state. * @param self String to concatenate. * @param other String to append to self. * @return [mrb_value] Returns a new String appending other to self. */ MRB_API void mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other); /** * Adds two strings together. * * * Example: * * int * main(int argc, * char **argv) * { * // Variable declarations. * mrb_value a; * mrb_value b; * mrb_value c; * * mrb_state *mrb = mrb_open(); * if (!mrb) * { * // handle error * } * * // Creates two Ruby strings from the passed in C strings. * a = mrb_str_new_lit(mrb, "abc"); * b = mrb_str_new_lit(mrb, "def"); * * // Prints both C strings. * mrb_p(mrb, a); * mrb_p(mrb, b); * * // Concatenates both Ruby strings. * c = mrb_str_plus(mrb, a, b); * * // Prints new Concatenated Ruby string. * mrb_p(mrb, c); * * mrb_close(mrb); * return 0; * } * * * Result: * * => "abc" # First string * => "def" # Second string * => "abcdef" # First & Second concatenated. * * @param mrb The current mruby state. * @param a First string to concatenate. * @param b Second string to concatenate. * @return [mrb_value] Returns a new String containing a concatenated to b. */ MRB_API mrb_value mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b); /** * Converts pointer into a Ruby string. * * @param mrb The current mruby state. * @param p The pointer to convert to Ruby string. * @return [mrb_value] Returns a new Ruby String. */ MRB_API mrb_value mrb_ptr_to_str(mrb_state *mrb, void *p); /** * Returns an object as a Ruby string. * * @param mrb The current mruby state. * @param obj An object to return as a Ruby string. * @return [mrb_value] An object as a Ruby string. */ MRB_API mrb_value mrb_obj_as_string(mrb_state *mrb, mrb_value obj); /** * Resizes the string's length. Returns the amount of characters * in the specified by len. * * Example: * * int * main(int argc, * char **argv) * { * // Variable declaration. * mrb_value str; * * mrb_state *mrb = mrb_open(); * if (!mrb) * { * // handle error * } * // Creates a new string. * str = mrb_str_new_lit(mrb, "Hello, world!"); * // Returns 5 characters of * mrb_str_resize(mrb, str, 5); * mrb_p(mrb, str); * * mrb_close(mrb); * return 0; * } * * Result: * * => "Hello" * * @param mrb The current mruby state. * @param str The Ruby string to resize. * @param len The length. * @return [mrb_value] An object as a Ruby string. */ MRB_API mrb_value mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len); /** * Returns a sub string. * * Example: * * int * main(int argc, * char const **argv) * { * // Variable declarations. * mrb_value str1; * mrb_value str2; * * mrb_state *mrb = mrb_open(); * if (!mrb) * { * // handle error * } * // Creates new string. * str1 = mrb_str_new_lit(mrb, "Hello, world!"); * // Returns a sub-string within the range of 0..2 * str2 = mrb_str_substr(mrb, str1, 0, 2); * * // Prints sub-string. * mrb_p(mrb, str2); * * mrb_close(mrb); * return 0; * } * * Result: * * => "He" * * @param mrb The current mruby state. * @param str Ruby string. * @param beg The beginning point of the sub-string. * @param len The end point of the sub-string. * @return [mrb_value] An object as a Ruby sub-string. */ MRB_API mrb_value mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len); MRB_API mrb_value mrb_str_new_capa(mrb_state *mrb, mrb_int capa); #define mrb_str_buf_new(mrb, capa) mrb_str_new_capa(mrb, (capa)) /* NULL terminated C string from mrb_value */ MRB_API const char *mrb_string_cstr(mrb_state *mrb, mrb_value str); /* NULL terminated C string from mrb_value; `str` will be updated */ MRB_API const char *mrb_string_value_cstr(mrb_state *mrb, mrb_value *str); /* obsolete: use RSTRING_PTR() */ #define mrb_string_value_ptr(mrb, str) RSTRING_PTR(str) /* obsolete: use RSTRING_LEN() */ #define mrb_string_value_len(mrb, str) RSTRING_LEN(str) /* obsolete: substituted by a macro; shall be removed */ #define mrb_str_strlen(mrb, s) strlen(RSTR_PTR(s)) /** * Duplicates a string object. * * * @param mrb The current mruby state. * @param str Ruby string. * @return [mrb_value] Duplicated Ruby string. */ MRB_API mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str); /** * Returns a symbol from a passed in Ruby string. * * @param mrb The current mruby state. * @param self Ruby string. * @return [mrb_value] A symbol. */ MRB_API mrb_value mrb_str_intern(mrb_state *mrb, mrb_value self); MRB_API mrb_value mrb_str_to_integer(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck); /* obsolete: use mrb_str_to_integer() */ #define mrb_str_to_inum(mrb, str, base, badcheck) mrb_str_to_integer(mrb, str, base, badcheck) MRB_API double mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck); /** * Returns true if the strings match and false if the strings don't match. * * @param mrb The current mruby state. * @param str1 Ruby string to compare. * @param str2 Ruby string to compare. * @return [mrb_value] boolean value. */ MRB_API mrb_bool mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2); /** * Returns a concatenated string comprised of a Ruby string and a C string. * * @param mrb The current mruby state. * @param str Ruby string. * @param ptr A C string. * @param len length of C string. * @return [mrb_value] A Ruby string. * @see mrb_str_cat_cstr */ MRB_API mrb_value mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len); /** * Returns a concatenated string comprised of a Ruby string and a C string. * * @param mrb The current mruby state. * @param str Ruby string. * @param ptr A C string. * @return [mrb_value] A Ruby string. * @see mrb_str_cat */ MRB_API mrb_value mrb_str_cat_cstr(mrb_state *mrb, mrb_value str, const char *ptr); MRB_API mrb_value mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2); #define mrb_str_cat_lit(mrb, str, lit) mrb_str_cat(mrb, str, lit, mrb_strlen_lit(lit)) /** * Adds str2 to the end of str1. */ MRB_API mrb_value mrb_str_append(mrb_state *mrb, mrb_value str, mrb_value str2); /** * Returns 0 if both Ruby strings are equal. Returns a value < 0 if Ruby str1 is less than Ruby str2. Returns a value > 0 if Ruby str2 is greater than Ruby str1. */ MRB_API int mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2); /** * Returns a newly allocated C string from a Ruby string. * This is an utility function to pass a Ruby string to C library functions. * * - Returned string does not contain any NUL characters (but terminator). * - It raises an ArgumentError exception if Ruby string contains * NUL characters. * - Returned string will be freed automatically on next GC. * - Caller can modify returned string without affecting Ruby string * (e.g. it can be used for mkstemp(3)). * * @param mrb The current mruby state. * @param str Ruby string. Must be an instance of String. * @return [char *] A newly allocated C string. */ MRB_API char *mrb_str_to_cstr(mrb_state *mrb, mrb_value str); /* For backward compatibility */ #define mrb_str_cat2(mrb, str, ptr) mrb_str_cat_cstr(mrb, str, ptr) #define mrb_str_buf_cat(mrb, str, ptr, len) mrb_str_cat(mrb, str, ptr, len) #define mrb_str_buf_append(mrb, str, str2) mrb_str_cat_str(mrb, str, str2) MRB_END_DECL #endif /* MRUBY_STRING_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/class.h0000644000000000000000000000013215077107276021732 xustar0030 mtime=1761382078.101420652 30 atime=1761382080.122411407 30 ctime=1761382108.979300731 nghttp2-1.68.0/third-party/mruby/include/mruby/class.h0000644000175100017510000000537315077107276022332 0ustar00runnerrunner/** ** @file mruby/class.h - Class class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_CLASS_H #define MRUBY_CLASS_H #include "common.h" /** * Class class */ MRB_BEGIN_DECL struct RClass { MRB_OBJECT_HEADER; struct iv_tbl *iv; struct mt_tbl *mt; struct RClass *super; }; #define mrb_class_ptr(v) ((struct RClass*)(mrb_ptr(v))) MRB_INLINE struct RClass* mrb_class(mrb_state *mrb, mrb_value v) { if (!mrb_immediate_p(v)) { return mrb_obj_ptr(v)->c; } switch (mrb_type(v)) { case MRB_TT_FALSE: if (mrb_fixnum(v)) return mrb->false_class; return mrb->nil_class; case MRB_TT_TRUE: return mrb->true_class; case MRB_TT_SYMBOL: return mrb->symbol_class; case MRB_TT_INTEGER: return mrb->integer_class; #ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: return mrb->float_class; #endif case MRB_TT_CPTR: return mrb->object_class; default: return NULL; } } /* flags: 20: frozen 19: is_prepended 18: is_origin 17: is_inherited (used by method cache) 7-16: unused 6: prohibit Class#allocate 0-5: instance type */ #define MRB_FL_CLASS_IS_PREPENDED (1 << 19) #define MRB_FL_CLASS_IS_ORIGIN (1 << 18) #define MRB_CLASS_ORIGIN(c) do {\ if ((c)->flags & MRB_FL_CLASS_IS_PREPENDED) {\ (c) = (c)->super;\ while (!((c)->flags & MRB_FL_CLASS_IS_ORIGIN)) {\ (c) = (c)->super;\ }\ }\ } while (0) #define MRB_FL_CLASS_IS_INHERITED (1 << 17) #define MRB_INSTANCE_TT_MASK (0x1F) #define MRB_SET_INSTANCE_TT(c, tt) ((c)->flags = (((c)->flags & ~MRB_INSTANCE_TT_MASK) | (char)(tt))) #define MRB_INSTANCE_TT(c) (enum mrb_vtype)((c)->flags & MRB_INSTANCE_TT_MASK) #define MRB_FL_UNDEF_ALLOCATE (1 << 6) #define MRB_UNDEF_ALLOCATOR(c) (mrb_assert((c)->tt == MRB_TT_CLASS), (c)->flags |= MRB_FL_UNDEF_ALLOCATE) #define MRB_UNDEF_ALLOCATOR_P(c) ((c)->flags & MRB_FL_UNDEF_ALLOCATE) #define MRB_DEFINE_ALLOCATOR(c) ((c)->flags &= ~MRB_FL_UNDEF_ALLOCATE) MRB_API void mrb_define_method_raw(mrb_state*, struct RClass*, mrb_sym, mrb_method_t); MRB_API void mrb_alias_method(mrb_state*, struct RClass *c, mrb_sym a, mrb_sym b); MRB_API void mrb_remove_method(mrb_state *mrb, struct RClass *c, mrb_sym sym); MRB_API mrb_method_t mrb_method_search_vm(mrb_state*, struct RClass**, mrb_sym); MRB_API mrb_method_t mrb_method_search(mrb_state*, struct RClass*, mrb_sym); MRB_API struct RClass* mrb_class_real(struct RClass* cl); #ifndef MRB_NO_METHOD_CACHE void mrb_mc_clear_by_class(mrb_state *mrb, struct RClass* c); #else #define mrb_mc_clear_by_class(mrb,c) #endif /* return non zero to break the loop */ typedef int (mrb_mt_foreach_func)(mrb_state*,mrb_sym,mrb_method_t,void*); MRB_API void mrb_mt_foreach(mrb_state*, struct RClass*, mrb_mt_foreach_func*, void*); MRB_END_DECL #endif /* MRUBY_CLASS_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/object.h0000644000000000000000000000013215077107276022073 xustar0030 mtime=1761382078.102420648 30 atime=1761382080.123411402 30 ctime=1761382108.977300737 nghttp2-1.68.0/third-party/mruby/include/mruby/object.h0000644000175100017510000000172315077107276022466 0ustar00runnerrunner/** ** @file mruby/object.h - mruby object definition ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_OBJECT_H #define MRUBY_OBJECT_H #define MRB_OBJECT_HEADER \ struct RClass *c; \ struct RBasic *gcnext; \ enum mrb_vtype tt:8; \ unsigned int gc_color:3; \ unsigned int frozen:1; \ uint32_t flags:20 #define MRB_FLAG_TEST(obj, flag) ((obj)->flags & (flag)) struct RBasic { MRB_OBJECT_HEADER; }; #define mrb_basic_ptr(v) ((struct RBasic*)(mrb_ptr(v))) #define MRB_OBJ_IS_FROZEN 1 #define mrb_frozen_p(o) ((o)->frozen) struct RObject { MRB_OBJECT_HEADER; struct iv_tbl *iv; }; #define mrb_obj_ptr(v) ((struct RObject*)(mrb_ptr(v))) #define mrb_special_const_p(x) mrb_immediate_p(x) struct RFiber { MRB_OBJECT_HEADER; struct mrb_context *cxt; }; #define mrb_static_assert_object_size(st) \ mrb_static_assert(sizeof(st) <= sizeof(void*) * 6, \ #st " size must be within 6 words") #endif /* MRUBY_OBJECT_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/opcode.h0000644000000000000000000000013215077107276022076 xustar0030 mtime=1761382078.102420648 30 atime=1761382080.123411402 30 ctime=1761382108.970300757 nghttp2-1.68.0/third-party/mruby/include/mruby/opcode.h0000644000175100017510000000426615077107276022476 0ustar00runnerrunner/** ** @file mruby/opcode.h - RiteVM operation codes ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_OPCODE_H #define MRUBY_OPCODE_H enum mrb_insn { #define OPCODE(x,_) OP_ ## x, #include #undef OPCODE }; #define OP_L_STRICT 1 #define OP_L_CAPTURE 2 #define OP_L_METHOD OP_L_STRICT #define OP_L_LAMBDA (OP_L_STRICT|OP_L_CAPTURE) #define OP_L_BLOCK OP_L_CAPTURE #define PEEK_B(pc) (*(pc)) #define PEEK_S(pc) ((pc)[0]<<8|(pc)[1]) #define PEEK_W(pc) ((pc)[0]<<16|(pc)[1]<<8|(pc)[2]) #define READ_B() PEEK_B(pc++) #define READ_S() (pc+=2, PEEK_S(pc-2)) #define READ_W() (pc+=3, PEEK_W(pc-3)) #define FETCH_Z() /* nothing */ #define FETCH_B() do {a=READ_B();} while (0) #define FETCH_BB() do {a=READ_B(); b=READ_B();} while (0) #define FETCH_BBB() do {a=READ_B(); b=READ_B(); c=READ_B();} while (0) #define FETCH_BS() do {a=READ_B(); b=READ_S();} while (0) #define FETCH_BSS() do {a=READ_B(); b=READ_S(); c=READ_S();} while (0) #define FETCH_S() do {a=READ_S();} while (0) #define FETCH_W() do {a=READ_W();} while (0) /* with OP_EXT1 (1st 16bit) */ #define FETCH_Z_1() FETCH_Z() #define FETCH_B_1() FETCH_S() #define FETCH_BB_1() do {a=READ_S(); b=READ_B();} while (0) #define FETCH_BBB_1() do {a=READ_S(); b=READ_B(); c=READ_B();} while (0) #define FETCH_BS_1() do {a=READ_S(); b=READ_S();} while (0) #define FETCH_BSS_1() do {a=READ_S(); b=READ_S();c=READ_S();} while (0) #define FETCH_S_1() FETCH_S() #define FETCH_W_1() FETCH_W() /* with OP_EXT2 (2nd 16bit) */ #define FETCH_Z_2() FETCH_Z() #define FETCH_B_2() FETCH_B() #define FETCH_BB_2() do {a=READ_B(); b=READ_S();} while (0) #define FETCH_BBB_2() do {a=READ_B(); b=READ_S(); c=READ_B();} while (0) #define FETCH_BS_2() FETCH_BS() #define FETCH_BSS_2() FETCH_BSS() #define FETCH_S_2() FETCH_S() #define FETCH_W_2() FETCH_W() /* with OP_EXT3 (1st & 2nd 16bit) */ #define FETCH_Z_3() FETCH_Z() #define FETCH_B_3() FETCH_B() #define FETCH_BB_3() do {a=READ_S(); b=READ_S();} while (0) #define FETCH_BBB_3() do {a=READ_S(); b=READ_S(); c=READ_B();} while (0) #define FETCH_BS_3() do {a=READ_S(); b=READ_S();} while (0) #define FETCH_BSS_3() FETCH_BSS_1() #define FETCH_S_3() FETCH_S() #define FETCH_W_3() FETCH_W() #endif /* MRUBY_OPCODE_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/proc.h0000644000000000000000000000013215077107276021570 xustar0030 mtime=1761382078.102420648 30 atime=1761382080.123411402 30 ctime=1761382109.013300633 nghttp2-1.68.0/third-party/mruby/include/mruby/proc.h0000644000175100017510000001576015077107276022171 0ustar00runnerrunner/** ** @file mruby/proc.h - Proc class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_PROC_H #define MRUBY_PROC_H #include "common.h" #include #include /** * Proc class */ MRB_BEGIN_DECL /* * env object (for internal used) * * - don't create multiple envs on one ci. * - don't share a env to different ci. * - don't attach a closed env to any ci. */ struct REnv { MRB_OBJECT_HEADER; mrb_value *stack; struct mrb_context *cxt; /* if not null, it means that the stack is shared with the call frame */ mrb_sym mid; }; /* flags (20bits): 1(ZERO):1(separate module):2(visibility):8(cioff/bidx):8(stack_len) */ #define MRB_ENV_SET_LEN(e,len) ((e)->flags = (((e)->flags & ~0xff)|((unsigned int)(len) & 0xff))) #define MRB_ENV_LEN(e) ((mrb_int)((e)->flags & 0xff)) #define MRB_ENV_CLOSE(e) ((e)->cxt = NULL) #define MRB_ENV_ONSTACK_P(e) ((e)->cxt != NULL) #define MRB_ENV_BIDX(e) (((e)->flags >> 8) & 0xff) #define MRB_ENV_SET_BIDX(e,idx) ((e)->flags = (((e)->flags & ~(0xff<<8))|((unsigned int)(idx) & 0xff)<<8)) #define MRB_ENV_SET_VISIBILITY(e, vis) MRB_FLAGS_SET((e)->flags, 16, 2, vis) #define MRB_ENV_VISIBILITY(e) MRB_FLAGS_GET((e)->flags, 16, 2) #define MRB_ENV_VISIBILITY_BREAK_P(e) MRB_FLAG_CHECK((e)->flags, 18) #define MRB_ENV_COPY_FLAGS_FROM_CI(e, ci) MRB_FLAGS_SET((e)->flags, 16, 3, (ci)->vis) /* * Returns TRUE on success. * If the function fails: * * Returns FALSE if noraise is TRUE. * * Raises a NoMemoryError exception if noraise is FALSE. */ mrb_bool mrb_env_unshare(mrb_state*, struct REnv*, mrb_bool noraise); struct RProc { MRB_OBJECT_HEADER; union { const mrb_irep *irep; mrb_func_t func; mrb_sym mid; } body; const struct RProc *upper; union { struct RClass *target_class; struct REnv *env; } e; }; /* aspec access */ #define MRB_ASPEC_REQ(a) (((a) >> 18) & 0x1f) #define MRB_ASPEC_OPT(a) (((a) >> 13) & 0x1f) #define MRB_ASPEC_REST(a) (((a) >> 12) & 0x1) #define MRB_ASPEC_POST(a) (((a) >> 7) & 0x1f) #define MRB_ASPEC_KEY(a) (((a) >> 2) & 0x1f) #define MRB_ASPEC_KDICT(a) (((a) >> 1) & 0x1) #define MRB_ASPEC_BLOCK(a) ((a) & 1) #define MRB_PROC_CFUNC_FL 128 #define MRB_PROC_CFUNC_P(p) (((p)->flags & MRB_PROC_CFUNC_FL) != 0) #define MRB_PROC_CFUNC(p) (p)->body.func #define MRB_PROC_STRICT 256 #define MRB_PROC_STRICT_P(p) (((p)->flags & MRB_PROC_STRICT) != 0) #define MRB_PROC_ORPHAN 512 #define MRB_PROC_ORPHAN_P(p) (((p)->flags & MRB_PROC_ORPHAN) != 0) #define MRB_PROC_ENVSET 1024 #define MRB_PROC_ENV_P(p) (((p)->flags & MRB_PROC_ENVSET) != 0) #define MRB_PROC_ENV(p) (MRB_PROC_ENV_P(p) ? (p)->e.env : NULL) #define MRB_PROC_TARGET_CLASS(p) (MRB_PROC_ENV_P(p) ? (p)->e.env->c : (p)->e.target_class) #define MRB_PROC_SET_TARGET_CLASS(p,tc) do {\ if (MRB_PROC_ENV_P(p)) {\ (p)->e.env->c = (tc);\ mrb_field_write_barrier(mrb, (struct RBasic*)(p)->e.env, (struct RBasic*)(tc));\ }\ else {\ (p)->e.target_class = (tc);\ mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)(tc));\ }\ } while (0) #define MRB_PROC_SCOPE 2048 #define MRB_PROC_SCOPE_P(p) (((p)->flags & MRB_PROC_SCOPE) != 0) #define MRB_PROC_NOARG 4096 /* for MRB_PROC_CFUNC_FL, it would be something like MRB_ARGS_NONE() or MRB_METHOD_NOARG_FL */ #define MRB_PROC_NOARG_P(p) (((p)->flags & MRB_PROC_NOARG) != 0) #define MRB_PROC_ALIAS 8192 #define MRB_PROC_ALIAS_P(p) (((p)->flags & MRB_PROC_ALIAS) != 0) #define mrb_proc_ptr(v) ((struct RProc*)(mrb_ptr(v))) struct RProc *mrb_proc_new(mrb_state*, const mrb_irep*); MRB_API struct RProc *mrb_proc_new_cfunc(mrb_state*, mrb_func_t); MRB_API struct RProc *mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals); /* following functions are defined in mruby-proc-ext so please include it when using */ MRB_API struct RProc *mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t func, mrb_int argc, const mrb_value *argv); MRB_API mrb_value mrb_proc_cfunc_env_get(mrb_state *mrb, mrb_int idx); /* old name */ #define mrb_cfunc_env_get(mrb, idx) mrb_proc_cfunc_env_get(mrb, idx) #define MRB_METHOD_FUNC_FL 8 #define MRB_METHOD_NOARG_FL 4 #define MRB_METHOD_PUBLIC_FL 0 #define MRB_METHOD_PRIVATE_FL 1 #define MRB_METHOD_PROTECTED_FL 2 #define MRB_METHOD_VDEFAULT_FL 3 #define MRB_METHOD_VISIBILITY_MASK 3 #define MRB_METHOD_FUNC_P(m) ((m).flags&MRB_METHOD_FUNC_FL) #define MRB_METHOD_NOARG_P(m) (((m).flags&MRB_METHOD_NOARG_FL)?1:0) #define MRB_METHOD_FUNC(m) ((m).as.func) #define MRB_METHOD_NOARG_SET(m) do{(m).flags|=MRB_METHOD_NOARG_FL;}while(0) #define MRB_METHOD_FROM_FUNC(m,fn) do{(m).flags=MRB_METHOD_FUNC_FL;(m).as.func=(fn);}while(0) #define MRB_METHOD_FROM_PROC(m,pr) do{(m).flags=0;(m).as.proc=(pr);}while(0) #define MRB_METHOD_PROC_P(m) (!MRB_METHOD_FUNC_P(m)) #define MRB_METHOD_PROC(m) ((m).as.proc) #define MRB_METHOD_UNDEF_P(m) ((m).as.proc==NULL) #define MRB_METHOD_VISIBILITY(m) ((m).flags & MRB_METHOD_VISIBILITY_MASK) #define MRB_SET_VISIBILITY_FLAGS(f,v) ((f)=(((f)&~MRB_METHOD_VISIBILITY_MASK)|(v))) #define MRB_METHOD_SET_VISIBILITY(m,v) MRB_SET_VISIBILITY_FLAGS((m).flags,(v)) #define MRB_METHOD_CFUNC_P(m) (MRB_METHOD_FUNC_P(m) || (MRB_METHOD_PROC(m)?(MRB_PROC_CFUNC_P(MRB_METHOD_PROC(m))):FALSE)) /* use MRB_METHOD_CFUNC(m) only when MRB_METHOD_CFUNC_P(m) is true */ #define MRB_METHOD_CFUNC(m) (MRB_METHOD_FUNC_P(m)?MRB_METHOD_FUNC(m):MRB_PROC_CFUNC(MRB_METHOD_PROC(m))) MRB_API mrb_value mrb_load_proc(mrb_state *mrb, const struct RProc *proc); /** * It can be used to isolate top-level scopes referenced by blocks generated by * `mrb_load_string_cxt()` or similar called before entering the mruby VM (e.g. from `main()`). * In that case, the `ci` parameter should be `mrb->c->cibase`. * * #include * #include * #include * * int * main(int argc, char **argv) * { * mrb_state *mrb; * mrb_ccontext *cxt; * mrb_value blk, ret; * * mrb = mrb_open(); * cxt = mrb_ccontext_new(mrb); * blk = mrb_load_string_cxt(mrb, "x, y, z = 1, 2, 3; proc { [x, y, z] }", cxt); * mrb_vm_ci_env_clear(mrb, mrb->c->cibase); * mrb_load_string_cxt(mrb, "x, y, z = 4, 5, 6", cxt); * ret = mrb_funcall(mrb, blk, "call", 0); * mrb_p(mrb, ret); // => [1, 2, 3] * // => [4, 5, 6] if `mrb_vm_ci_env_clear()` is commented out * mrb_ccontext_free(mrb, cxt); * mrb_close(mrb); * * return 0; * } * * The top-level local variable names stored in `mrb_ccontext` are retained. * Use also `mrb_ccontext_cleanup_local_variables()` at the same time, if necessary. */ MRB_API void mrb_vm_ci_env_clear(mrb_state *mrb, mrb_callinfo *ci); void mrb_vm_ci_proc_set(mrb_callinfo *ci, const struct RProc *p); struct RClass * mrb_vm_ci_target_class(const mrb_callinfo *ci); void mrb_vm_ci_target_class_set(mrb_callinfo *ci, struct RClass *tc); struct REnv * mrb_vm_ci_env(const mrb_callinfo *ci); void mrb_vm_ci_env_set(mrb_callinfo *ci, struct REnv *e); MRB_END_DECL #endif /* MRUBY_PROC_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/presym0000644000000000000000000000013215077107335021712 xustar0030 mtime=1761382109.012300635 30 atime=1761382109.799298361 30 ctime=1761382109.012300635 nghttp2-1.68.0/third-party/mruby/include/mruby/presym/0000755000175100017510000000000015077107335022357 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/include/mruby/presym/PaxHeaders/disable.h0000644000000000000000000000013215077107276023547 xustar0030 mtime=1761382078.102420648 30 atime=1761382080.123411402 30 ctime=1761382109.011300638 nghttp2-1.68.0/third-party/mruby/include/mruby/presym/disable.h0000644000175100017510000000630315077107276024141 0ustar00runnerrunner/** ** @file mruby/presym/disable.h - Disable Preallocated Symbols ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_PRESYM_DISABLE_H #define MRUBY_PRESYM_DISABLE_H #include #define MRB_PRESYM_MAX 0 #define MRB_OPSYM(name) MRB_OPSYM__##name(mrb) #define MRB_GVSYM(name) mrb_intern_lit(mrb, "$" #name) #define MRB_CVSYM(name) mrb_intern_lit(mrb, "@@" #name) #define MRB_IVSYM(name) mrb_intern_lit(mrb, "@" #name) #define MRB_SYM_B(name) mrb_intern_lit(mrb, #name "!") #define MRB_SYM_Q(name) mrb_intern_lit(mrb, #name "?") #define MRB_SYM_E(name) mrb_intern_lit(mrb, #name "=") #define MRB_SYM(name) mrb_intern_lit(mrb, #name) #define MRB_OPSYM_2(mrb, name) MRB_OPSYM__##name(mrb) #define MRB_GVSYM_2(mrb, name) mrb_intern_lit(mrb, "$" #name) #define MRB_CVSYM_2(mrb, name) mrb_intern_lit(mrb, "@@" #name) #define MRB_IVSYM_2(mrb, name) mrb_intern_lit(mrb, "@" #name) #define MRB_SYM_B_2(mrb, name) mrb_intern_lit(mrb, #name "!") #define MRB_SYM_Q_2(mrb, name) mrb_intern_lit(mrb, #name "?") #define MRB_SYM_E_2(mrb, name) mrb_intern_lit(mrb, #name "=") #define MRB_SYM_2(mrb, name) mrb_intern_lit(mrb, #name) #define MRB_OPSYM__not(mrb) mrb_intern_lit(mrb, "!") #define MRB_OPSYM__mod(mrb) mrb_intern_lit(mrb, "%") #define MRB_OPSYM__and(mrb) mrb_intern_lit(mrb, "&") #define MRB_OPSYM__mul(mrb) mrb_intern_lit(mrb, "*") #define MRB_OPSYM__add(mrb) mrb_intern_lit(mrb, "+") #define MRB_OPSYM__sub(mrb) mrb_intern_lit(mrb, "-") #define MRB_OPSYM__div(mrb) mrb_intern_lit(mrb, "/") #define MRB_OPSYM__lt(mrb) mrb_intern_lit(mrb, "<") #define MRB_OPSYM__gt(mrb) mrb_intern_lit(mrb, ">") #define MRB_OPSYM__xor(mrb) mrb_intern_lit(mrb, "^") #define MRB_OPSYM__tick(mrb) mrb_intern_lit(mrb, "`") #define MRB_OPSYM__or(mrb) mrb_intern_lit(mrb, "|") #define MRB_OPSYM__neg(mrb) mrb_intern_lit(mrb, "~") #define MRB_OPSYM__neq(mrb) mrb_intern_lit(mrb, "!=") #define MRB_OPSYM__nmatch(mrb) mrb_intern_lit(mrb, "!~") #define MRB_OPSYM__andand(mrb) mrb_intern_lit(mrb, "&&") #define MRB_OPSYM__pow(mrb) mrb_intern_lit(mrb, "**") #define MRB_OPSYM__plus(mrb) mrb_intern_lit(mrb, "+@") #define MRB_OPSYM__minus(mrb) mrb_intern_lit(mrb, "-@") #define MRB_OPSYM__lshift(mrb) mrb_intern_lit(mrb, "<<") #define MRB_OPSYM__le(mrb) mrb_intern_lit(mrb, "<=") #define MRB_OPSYM__eq(mrb) mrb_intern_lit(mrb, "==") #define MRB_OPSYM__match(mrb) mrb_intern_lit(mrb, "=~") #define MRB_OPSYM__ge(mrb) mrb_intern_lit(mrb, ">=") #define MRB_OPSYM__rshift(mrb) mrb_intern_lit(mrb, ">>") #define MRB_OPSYM__aref(mrb) mrb_intern_lit(mrb, "[]") #define MRB_OPSYM__oror(mrb) mrb_intern_lit(mrb, "||") #define MRB_OPSYM__cmp(mrb) mrb_intern_lit(mrb, "<=>") #define MRB_OPSYM__eqq(mrb) mrb_intern_lit(mrb, "===") #define MRB_OPSYM__aset(mrb) mrb_intern_lit(mrb, "[]=") #define MRB_PRESYM_DEFINE_VAR_AND_INITER(name, size, ...) \ static mrb_sym name[size]; \ static void presym_init_##name(mrb_state *mrb) { \ mrb_sym name__[] = {__VA_ARGS__}; \ memcpy(name, name__, sizeof(name)); \ } #define MRB_PRESYM_INIT_SYMBOLS(mrb, name) presym_init_##name(mrb) #endif /* MRUBY_PRESYM_DISABLE_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/presym/PaxHeaders/scanning.h0000644000000000000000000000013215077107276023744 xustar0030 mtime=1761382078.102420648 30 atime=1761382080.123411402 30 ctime=1761382109.012300635 nghttp2-1.68.0/third-party/mruby/include/mruby/presym/scanning.h0000644000175100017510000001107115077107276024334 0ustar00runnerrunner/** ** @file mruby/presym/scanning.h - Scanning Preallocated Symbols ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_PRESYM_SCANNING_H #define MRUBY_PRESYM_SCANNING_H #define MRB_PRESYM_SCANNING_TAGGED(arg) <@! arg !@> #undef mrb_intern_lit #define mrb_intern_lit(mrb, name) MRB_PRESYM_SCANNING_TAGGED(name) #define mrb_intern_cstr(mrb, name) MRB_PRESYM_SCANNING_TAGGED(name) #define mrb_define_method(mrb, c, name, ...) MRB_PRESYM_SCANNING_TAGGED(name) (c) (__VA_ARGS__) #define mrb_define_class_method(mrb, c, name, ...) MRB_PRESYM_SCANNING_TAGGED(name) (c) (__VA_ARGS__) #define mrb_define_singleton_method(mrb, c, name, ...) MRB_PRESYM_SCANNING_TAGGED(name) (c) (__VA_ARGS__) #define mrb_define_class(mrb, name, s) MRB_PRESYM_SCANNING_TAGGED(name) (s) #define mrb_define_class_under(mrb, o, name, s) MRB_PRESYM_SCANNING_TAGGED(name) (o) (s) #define mrb_define_module(mrb, name) MRB_PRESYM_SCANNING_TAGGED(name) #define mrb_define_module_under(mrb, o, name) MRB_PRESYM_SCANNING_TAGGED(name) (o) #define mrb_define_module_function(mrb, c, name, ...) MRB_PRESYM_SCANNING_TAGGED(name) (c) (__VA_ARGS__) #define mrb_define_const(mrb, c, name, v) MRB_PRESYM_SCANNING_TAGGED(name) (c) (v) #define mrb_define_global_const(mrb, name, v) MRB_PRESYM_SCANNING_TAGGED(name) (v) #define mrb_define_alias(mrb, c, a, b) MRB_PRESYM_SCANNING_TAGGED(a) MRB_PRESYM_SCANNING_TAGGED(b) (c) #define mrb_class_get(mrb, name) MRB_PRESYM_SCANNING_TAGGED(name) #define mrb_class_get_under(mrb, outer, name) MRB_PRESYM_SCANNING_TAGGED(name) (outer) #define mrb_module_get(mrb, name) MRB_PRESYM_SCANNING_TAGGED(name) #define mrb_module_get_under(mrb, outer, name) MRB_PRESYM_SCANNING_TAGGED(name) (outer) #define mrb_funcall(mrb, v, name, ...) MRB_PRESYM_SCANNING_TAGGED(name) (v) (__VA_ARGS__) #define MRB_OPSYM(name) MRB_OPSYM__##name(mrb) #define MRB_GVSYM(name) MRB_PRESYM_SCANNING_TAGGED("$" #name) #define MRB_CVSYM(name) MRB_PRESYM_SCANNING_TAGGED("@@" #name) #define MRB_IVSYM(name) MRB_PRESYM_SCANNING_TAGGED("@" #name) #define MRB_SYM_B(name) MRB_PRESYM_SCANNING_TAGGED(#name "!") #define MRB_SYM_Q(name) MRB_PRESYM_SCANNING_TAGGED(#name "?") #define MRB_SYM_E(name) MRB_PRESYM_SCANNING_TAGGED(#name "=") #define MRB_SYM(name) MRB_PRESYM_SCANNING_TAGGED(#name) #define MRB_OPSYM_2(mrb, name) MRB_OPSYM__##name(mrb) #define MRB_GVSYM_2(mrb, name) MRB_PRESYM_SCANNING_TAGGED("$" #name) #define MRB_CVSYM_2(mrb, name) MRB_PRESYM_SCANNING_TAGGED("@@" #name) #define MRB_IVSYM_2(mrb, name) MRB_PRESYM_SCANNING_TAGGED("@" #name) #define MRB_SYM_B_2(mrb, name) MRB_PRESYM_SCANNING_TAGGED(#name "!") #define MRB_SYM_Q_2(mrb, name) MRB_PRESYM_SCANNING_TAGGED(#name "?") #define MRB_SYM_E_2(mrb, name) MRB_PRESYM_SCANNING_TAGGED(#name "=") #define MRB_SYM_2(mrb, name) MRB_PRESYM_SCANNING_TAGGED(#name) #define MRB_OPSYM__not(mrb) MRB_PRESYM_SCANNING_TAGGED("!") #define MRB_OPSYM__mod(mrb) MRB_PRESYM_SCANNING_TAGGED("%") #define MRB_OPSYM__and(mrb) MRB_PRESYM_SCANNING_TAGGED("&") #define MRB_OPSYM__mul(mrb) MRB_PRESYM_SCANNING_TAGGED("*") #define MRB_OPSYM__add(mrb) MRB_PRESYM_SCANNING_TAGGED("+") #define MRB_OPSYM__sub(mrb) MRB_PRESYM_SCANNING_TAGGED("-") #define MRB_OPSYM__div(mrb) MRB_PRESYM_SCANNING_TAGGED("/") #define MRB_OPSYM__lt(mrb) MRB_PRESYM_SCANNING_TAGGED("<") #define MRB_OPSYM__gt(mrb) MRB_PRESYM_SCANNING_TAGGED(">") #define MRB_OPSYM__xor(mrb) MRB_PRESYM_SCANNING_TAGGED("^") #define MRB_OPSYM__tick(mrb) MRB_PRESYM_SCANNING_TAGGED("`") #define MRB_OPSYM__or(mrb) MRB_PRESYM_SCANNING_TAGGED("|") #define MRB_OPSYM__neg(mrb) MRB_PRESYM_SCANNING_TAGGED("~") #define MRB_OPSYM__neq(mrb) MRB_PRESYM_SCANNING_TAGGED("!=") #define MRB_OPSYM__nmatch(mrb) MRB_PRESYM_SCANNING_TAGGED("!~") #define MRB_OPSYM__andand(mrb) MRB_PRESYM_SCANNING_TAGGED("&&") #define MRB_OPSYM__pow(mrb) MRB_PRESYM_SCANNING_TAGGED("**") #define MRB_OPSYM__plus(mrb) MRB_PRESYM_SCANNING_TAGGED("+@") #define MRB_OPSYM__minus(mrb) MRB_PRESYM_SCANNING_TAGGED("-@") #define MRB_OPSYM__lshift(mrb) MRB_PRESYM_SCANNING_TAGGED("<<") #define MRB_OPSYM__le(mrb) MRB_PRESYM_SCANNING_TAGGED("<=") #define MRB_OPSYM__eq(mrb) MRB_PRESYM_SCANNING_TAGGED("==") #define MRB_OPSYM__match(mrb) MRB_PRESYM_SCANNING_TAGGED("=~") #define MRB_OPSYM__ge(mrb) MRB_PRESYM_SCANNING_TAGGED(">=") #define MRB_OPSYM__rshift(mrb) MRB_PRESYM_SCANNING_TAGGED(">>") #define MRB_OPSYM__aref(mrb) MRB_PRESYM_SCANNING_TAGGED("[]") #define MRB_OPSYM__oror(mrb) MRB_PRESYM_SCANNING_TAGGED("||") #define MRB_OPSYM__cmp(mrb) MRB_PRESYM_SCANNING_TAGGED("<=>") #define MRB_OPSYM__eqq(mrb) MRB_PRESYM_SCANNING_TAGGED("===") #define MRB_OPSYM__aset(mrb) MRB_PRESYM_SCANNING_TAGGED("[]=") #endif /* MRUBY_PRESYM_SCANNING_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/presym/PaxHeaders/enable.h0000644000000000000000000000013215077107276023372 xustar0030 mtime=1761382078.102420648 30 atime=1761382080.123411402 30 ctime=1761382109.010300641 nghttp2-1.68.0/third-party/mruby/include/mruby/presym/enable.h0000644000175100017510000000233715077107276023767 0ustar00runnerrunner/** ** @file mruby/presym/enable.h - Enable Preallocated Symbols ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_PRESYM_ENABLE_H #define MRUBY_PRESYM_ENABLE_H #include #define MRB_OPSYM(name) MRB_OPSYM__##name #define MRB_GVSYM(name) MRB_GVSYM__##name #define MRB_CVSYM(name) MRB_CVSYM__##name #define MRB_IVSYM(name) MRB_IVSYM__##name #define MRB_SYM_B(name) MRB_SYM_B__##name #define MRB_SYM_Q(name) MRB_SYM_Q__##name #define MRB_SYM_E(name) MRB_SYM_E__##name #define MRB_SYM(name) MRB_SYM__##name #define MRB_OPSYM_2(mrb, name) MRB_OPSYM__##name #define MRB_GVSYM_2(mrb, name) MRB_GVSYM__##name #define MRB_CVSYM_2(mrb, name) MRB_CVSYM__##name #define MRB_IVSYM_2(mrb, name) MRB_IVSYM__##name #define MRB_SYM_B_2(mrb, name) MRB_SYM_B__##name #define MRB_SYM_Q_2(mrb, name) MRB_SYM_Q__##name #define MRB_SYM_E_2(mrb, name) MRB_SYM_E__##name #define MRB_SYM_2(mrb, name) MRB_SYM__##name #define MRB_PRESYM_DEFINE_VAR_AND_INITER(name, size, ...) \ static const mrb_sym name[] = {__VA_ARGS__}; #define MRB_PRESYM_INIT_SYMBOLS(mrb, name) (void)(mrb) /* use MRB_SYM() for E_RUNTIME_ERROR etc. */ #undef MRB_ERROR_SYM #define MRB_ERROR_SYM(sym) MRB_SYM(sym) #endif /* MRUBY_PRESYM_ENABLE_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/endian.h0000644000000000000000000000013215077107276022063 xustar0030 mtime=1761382078.101420652 30 atime=1761382080.122411407 30 ctime=1761382109.002300664 nghttp2-1.68.0/third-party/mruby/include/mruby/endian.h0000644000175100017510000000162115077107276022453 0ustar00runnerrunner/** ** @file mruby/endian.h - detect endian-ness ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_ENDIAN_H #define MRUBY_ENDIAN_H #include MRB_BEGIN_DECL #if !defined(BYTE_ORDER) && defined(__BYTE_ORDER__) # define BYTE_ORDER __BYTE_ORDER__ #endif #if !defined(BIG_ENDIAN) && defined(__ORDER_BIG_ENDIAN__) # define BIG_ENDIAN __ORDER_BIG_ENDIAN__ #endif #if !defined(LITTLE_ENDIAN) && defined(__ORDER_LITTLE_ENDIAN__) # define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ #endif #ifdef BYTE_ORDER # if BYTE_ORDER == BIG_ENDIAN # define littleendian 0 # elif BYTE_ORDER == LITTLE_ENDIAN # define littleendian 1 # endif #endif #ifndef littleendian /* can't distinguish endian in compile time */ static inline int check_little_endian(void) { unsigned int n = 1; return (*(unsigned char*)&n == 1); } # define littleendian check_little_endian() #endif MRB_END_DECL #endif /* MRUBY_ENDIAN_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/ops.h0000644000000000000000000000013215077107276021426 xustar0030 mtime=1761382078.102420648 30 atime=1761382080.123411402 30 ctime=1761382108.980300728 nghttp2-1.68.0/third-party/mruby/include/mruby/ops.h0000644000175100017510000001514515077107276022024 0ustar00runnerrunner/* operand types: + Z: no operand + B: 8bit + BB: 8+8bit + BBB: 8+8+8bit + BS: 8+16bit + BSS: 8+16+16bit + S: 16bit + W: 24bit */ /*----------------------------------------------------------------------- operation code operands semantics ------------------------------------------------------------------------*/ OPCODE(NOP, Z) /* no operation */ OPCODE(MOVE, BB) /* R[a] = R[b] */ OPCODE(LOADL, BB) /* R[a] = Pool[b] */ OPCODE(LOADI8, BB) /* R[a] = mrb_int(b) */ OPCODE(LOADINEG, BB) /* R[a] = mrb_int(-b) */ OPCODE(LOADI__1, B) /* R[a] = mrb_int(-1) */ OPCODE(LOADI_0, B) /* R[a] = mrb_int(0) */ OPCODE(LOADI_1, B) /* R[a] = mrb_int(1) */ OPCODE(LOADI_2, B) /* R[a] = mrb_int(2) */ OPCODE(LOADI_3, B) /* R[a] = mrb_int(3) */ OPCODE(LOADI_4, B) /* R[a] = mrb_int(4) */ OPCODE(LOADI_5, B) /* R[a] = mrb_int(5) */ OPCODE(LOADI_6, B) /* R[a] = mrb_int(6) */ OPCODE(LOADI_7, B) /* R[a] = mrb_int(7) */ OPCODE(LOADI16, BS) /* R[a] = mrb_int(b) */ OPCODE(LOADI32, BSS) /* R[a] = mrb_int((b<<16)+c) */ OPCODE(LOADSYM, BB) /* R[a] = Syms[b] */ OPCODE(LOADNIL, B) /* R[a] = nil */ OPCODE(LOADSELF, B) /* R[a] = self */ OPCODE(LOADT, B) /* R[a] = true */ OPCODE(LOADF, B) /* R[a] = false */ OPCODE(GETGV, BB) /* R[a] = getglobal(Syms[b]) */ OPCODE(SETGV, BB) /* setglobal(Syms[b], R[a]) */ OPCODE(GETSV, BB) /* R[a] = Special[Syms[b]] */ OPCODE(SETSV, BB) /* Special[Syms[b]] = R[a] */ OPCODE(GETIV, BB) /* R[a] = ivget(Syms[b]) */ OPCODE(SETIV, BB) /* ivset(Syms[b],R[a]) */ OPCODE(GETCV, BB) /* R[a] = cvget(Syms[b]) */ OPCODE(SETCV, BB) /* cvset(Syms[b],R[a]) */ OPCODE(GETCONST, BB) /* R[a] = constget(Syms[b]) */ OPCODE(SETCONST, BB) /* constset(Syms[b],R[a]) */ OPCODE(GETMCNST, BB) /* R[a] = R[a]::Syms[b] */ OPCODE(SETMCNST, BB) /* R[a+1]::Syms[b] = R[a] */ OPCODE(GETUPVAR, BBB) /* R[a] = uvget(b,c) */ OPCODE(SETUPVAR, BBB) /* uvset(b,c,R[a]) */ OPCODE(GETIDX, B) /* R[a] = R[a][R[a+1]] */ OPCODE(SETIDX, B) /* R[a][R[a+1]] = R[a+2] */ OPCODE(JMP, S) /* pc+=a */ OPCODE(JMPIF, BS) /* if R[a] pc+=b */ OPCODE(JMPNOT, BS) /* if !R[a] pc+=b */ OPCODE(JMPNIL, BS) /* if R[a]==nil pc+=b */ OPCODE(JMPUW, S) /* unwind_and_jump_to(a) */ OPCODE(EXCEPT, B) /* R[a] = exc */ OPCODE(RESCUE, BB) /* R[b] = R[a].isa?(R[b]) */ OPCODE(RAISEIF, B) /* raise(R[a]) if R[a] */ OPCODE(SSEND, BBB) /* R[a] = self.send(Syms[b],R[a+1]..,R[a+n+1]:R[a+n+2]..) (c=n|k<<4) */ OPCODE(SSENDB, BBB) /* R[a] = self.send(Syms[b],R[a+1]..,R[a+n+1]:R[a+n+2]..,&R[a+n+2k+1]) */ OPCODE(SEND, BBB) /* R[a] = R[a].send(Syms[b],R[a+1]..,R[a+n+1]:R[a+n+2]..) (c=n|k<<4) */ OPCODE(SENDB, BBB) /* R[a] = R[a].send(Syms[b],R[a+1]..,R[a+n+1]:R[a+n+2]..,&R[a+n+2k+1]) */ OPCODE(CALL, Z) /* self.call(*, **, &) (But overlay the current call frame; tailcall) */ OPCODE(SUPER, BB) /* R[a] = super(R[a+1],... ,R[a+b+1]) */ OPCODE(ARGARY, BS) /* R[a] = argument array (16=m5:r1:m5:d1:lv4) */ OPCODE(ENTER, W) /* arg setup according to flags (23=m5:o5:r1:m5:k5:d1:b1) */ OPCODE(KEY_P, BB) /* R[a] = kdict.key?(Syms[b]) */ OPCODE(KEYEND, Z) /* raise unless kdict.empty? */ OPCODE(KARG, BB) /* R[a] = kdict[Syms[b]]; kdict.delete(Syms[b]) */ OPCODE(RETURN, B) /* return R[a] (normal) */ OPCODE(RETURN_BLK, B) /* return R[a] (in-block return) */ OPCODE(BREAK, B) /* break R[a] */ OPCODE(BLKPUSH, BS) /* R[a] = block (16=m5:r1:m5:d1:lv4) */ OPCODE(ADD, B) /* R[a] = R[a]+R[a+1] */ OPCODE(ADDI, BB) /* R[a] = R[a]+mrb_int(b) */ OPCODE(SUB, B) /* R[a] = R[a]-R[a+1] */ OPCODE(SUBI, BB) /* R[a] = R[a]-mrb_int(b) */ OPCODE(MUL, B) /* R[a] = R[a]*R[a+1] */ OPCODE(DIV, B) /* R[a] = R[a]/R[a+1] */ OPCODE(EQ, B) /* R[a] = R[a]==R[a+1] */ OPCODE(LT, B) /* R[a] = R[a]R[a+1] */ OPCODE(GE, B) /* R[a] = R[a]>=R[a+1] */ OPCODE(ARRAY, BB) /* R[a] = ary_new(R[a],R[a+1]..R[a+b]) */ OPCODE(ARRAY2, BBB) /* R[a] = ary_new(R[b],R[b+1]..R[b+c]) */ OPCODE(ARYCAT, B) /* ary_cat(R[a],R[a+1]) */ OPCODE(ARYPUSH, BB) /* ary_push(R[a],R[a+1]..R[a+b]) */ OPCODE(ARYSPLAT, B) /* R[a] = ary_splat(R[a]) */ OPCODE(AREF, BBB) /* R[a] = R[b][c] */ OPCODE(ASET, BBB) /* R[b][c] = R[a] */ OPCODE(APOST, BBB) /* *R[a],R[a+1]..R[a+c] = R[a][b..] */ OPCODE(INTERN, B) /* R[a] = intern(R[a]) */ OPCODE(SYMBOL, BB) /* R[a] = intern(Pool[b]) */ OPCODE(STRING, BB) /* R[a] = str_dup(Pool[b]) */ OPCODE(STRCAT, B) /* str_cat(R[a],R[a+1]) */ OPCODE(HASH, BB) /* R[a] = hash_new(R[a],R[a+1]..R[a+b*2-1]) */ OPCODE(HASHADD, BB) /* hash_push(R[a],R[a+1]..R[a+b*2]) */ OPCODE(HASHCAT, B) /* R[a] = hash_cat(R[a],R[a+1]) */ OPCODE(LAMBDA, BB) /* R[a] = lambda(Irep[b],L_LAMBDA) */ OPCODE(BLOCK, BB) /* R[a] = lambda(Irep[b],L_BLOCK) */ OPCODE(METHOD, BB) /* R[a] = lambda(Irep[b],L_METHOD) */ OPCODE(RANGE_INC, B) /* R[a] = range_new(R[a],R[a+1],FALSE) */ OPCODE(RANGE_EXC, B) /* R[a] = range_new(R[a],R[a+1],TRUE) */ OPCODE(OCLASS, B) /* R[a] = ::Object */ OPCODE(CLASS, BB) /* R[a] = newclass(R[a],Syms[b],R[a+1]) */ OPCODE(MODULE, BB) /* R[a] = newmodule(R[a],Syms[b]) */ OPCODE(EXEC, BB) /* R[a] = blockexec(R[a],Irep[b]) */ OPCODE(DEF, BB) /* R[a].newmethod(Syms[b],R[a+1]); R[a] = Syms[b] */ OPCODE(ALIAS, BB) /* alias_method(target_class,Syms[a],Syms[b]) */ OPCODE(UNDEF, B) /* undef_method(target_class,Syms[a]) */ OPCODE(SCLASS, B) /* R[a] = R[a].singleton_class */ OPCODE(TCLASS, B) /* R[a] = target_class */ OPCODE(DEBUG, BBB) /* print a,b,c */ OPCODE(ERR, B) /* raise(LocalJumpError, Pool[a]) */ OPCODE(EXT1, Z) /* make 1st operand (a) 16bit */ OPCODE(EXT2, Z) /* make 2nd operand (b) 16bit */ OPCODE(EXT3, Z) /* make 1st and 2nd operands 16bit */ OPCODE(STOP, Z) /* stop VM */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/range.h0000644000000000000000000000013215077107276021721 xustar0030 mtime=1761382078.102420648 30 atime=1761382080.123411402 30 ctime=1761382108.985300714 nghttp2-1.68.0/third-party/mruby/include/mruby/range.h0000644000175100017510000000405015077107276022310 0ustar00runnerrunner/** ** @file mruby/range.h - Range class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_RANGE_H #define MRUBY_RANGE_H #include "common.h" /** * Range class */ MRB_BEGIN_DECL #if defined(MRB_NAN_BOXING) && defined(MRB_64BIT) || defined(MRB_WORD_BOXING) # define MRB_RANGE_EMBED #endif #ifdef MRB_RANGE_EMBED struct RRange { MRB_OBJECT_HEADER; mrb_value beg; mrb_value end; mrb_bool excl; }; # define mrb_gc_free_range(mrb, p) ((void)0) # define RANGE_BEG(p) ((p)->beg) # define RANGE_END(p) ((p)->end) #else typedef struct mrb_range_edges { mrb_value beg; mrb_value end; } mrb_range_edges; struct RRange { MRB_OBJECT_HEADER; mrb_range_edges *edges; mrb_bool excl; }; # define mrb_gc_free_range(mrb, p) mrb_free(mrb, (p)->edges) # define RANGE_BEG(p) ((p)->edges->beg) # define RANGE_END(p) ((p)->edges->end) #endif #define mrb_range_beg(mrb, r) RANGE_BEG(mrb_range_ptr(mrb, r)) #define mrb_range_end(mrb, r) RANGE_END(mrb_range_ptr(mrb, r)) #define mrb_range_excl_p(mrb, r) RANGE_EXCL(mrb_range_ptr(mrb, r)) #define mrb_range_raw_ptr(r) ((struct RRange*)mrb_ptr(r)) #define mrb_range_value(p) mrb_obj_value((void*)(p)) #define RANGE_EXCL(p) ((p)->excl) MRB_API struct RRange* mrb_range_ptr(mrb_state *mrb, mrb_value range); /* * Initializes a Range. * * If the third parameter is FALSE then it includes the last value in the range. * If the third parameter is TRUE then it excludes the last value in the range. * * @param start the beginning value. * @param end the ending value. * @param exclude represents the inclusion or exclusion of the last value. */ MRB_API mrb_value mrb_range_new(mrb_state *mrb, mrb_value start, mrb_value end, mrb_bool exclude); enum mrb_range_beg_len { MRB_RANGE_TYPE_MISMATCH = 0, /* (failure) not range */ MRB_RANGE_OK = 1, /* (success) range */ MRB_RANGE_OUT = 2 /* (failure) out of range */ }; MRB_API enum mrb_range_beg_len mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc); MRB_END_DECL #endif /* MRUBY_RANGE_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/data.h0000644000000000000000000000013215077107276021536 xustar0030 mtime=1761382078.101420652 30 atime=1761382080.122411407 30 ctime=1761382108.992300693 nghttp2-1.68.0/third-party/mruby/include/mruby/data.h0000644000175100017510000000427315077107276022134 0ustar00runnerrunner/** ** @file mruby/data.h - Data class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_DATA_H #define MRUBY_DATA_H #include "common.h" /** * Custom C wrapped data. * * Defining Ruby wrappers around native objects. */ MRB_BEGIN_DECL /** * Custom data type description. */ typedef struct mrb_data_type { /** data type name */ const char *struct_name; /** data type release function pointer */ void (*dfree)(mrb_state *mrb, void*); } mrb_data_type; struct RData { MRB_OBJECT_HEADER; struct iv_tbl *iv; const mrb_data_type *type; void *data; }; MRB_API struct RData *mrb_data_object_alloc(mrb_state *mrb, struct RClass* klass, void *datap, const mrb_data_type *type); #define Data_Wrap_Struct(mrb,klass,type,ptr)\ mrb_data_object_alloc(mrb,klass,ptr,type) #define Data_Make_Struct(mrb,klass,strct,type,sval,data_obj) do { \ (data_obj) = Data_Wrap_Struct(mrb,klass,type,NULL);\ (sval) = (strct*)mrb_malloc(mrb, sizeof(strct)); \ { static const strct zero = { 0 }; *(sval) = zero; };\ (data_obj)->data = (sval);\ } while (0) #define RDATA(obj) ((struct RData*)(mrb_ptr(obj))) #define DATA_PTR(d) (RDATA(d)->data) #define DATA_TYPE(d) (RDATA(d)->type) MRB_API void mrb_data_check_type(mrb_state *mrb, mrb_value, const mrb_data_type*); MRB_API void *mrb_data_get_ptr(mrb_state *mrb, mrb_value, const mrb_data_type*); #define DATA_GET_PTR(mrb,obj,dtype,type) (type*)mrb_data_get_ptr(mrb,obj,dtype) MRB_API void *mrb_data_check_get_ptr(mrb_state *mrb, mrb_value, const mrb_data_type*); #define DATA_CHECK_GET_PTR(mrb,obj,dtype,type) (type*)mrb_data_check_get_ptr(mrb,obj,dtype) /* obsolete functions and macros */ #define mrb_data_check_and_get(mrb,obj,dtype) mrb_data_get_ptr(mrb,obj,dtype) #define mrb_get_datatype(mrb,val,type) mrb_data_get_ptr(mrb, val, type) #define mrb_check_datatype(mrb,val,type) mrb_data_get_ptr(mrb, val, type) #define Data_Get_Struct(mrb,obj,type,sval) do {\ *(void**)&sval = mrb_data_get_ptr(mrb, obj, type); \ } while (0) MRB_INLINE void mrb_data_init(mrb_value v, void *ptr, const mrb_data_type *type) { mrb_assert(mrb_data_p(v)); DATA_PTR(v) = ptr; DATA_TYPE(v) = type; } MRB_END_DECL #endif /* MRUBY_DATA_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/presym.h0000644000000000000000000000013215077107276022144 xustar0030 mtime=1761382078.102420648 30 atime=1761382080.123411402 30 ctime=1761382108.975300742 nghttp2-1.68.0/third-party/mruby/include/mruby/presym.h0000644000175100017510000000266215077107276022542 0ustar00runnerrunner/** ** @file mruby/presym.h - Preallocated Symbols ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_PRESYM_H #define MRUBY_PRESYM_H #if defined(MRB_NO_PRESYM) # include #elif !defined(MRB_PRESYM_SCANNING) # include #endif /* * Where `mrb_intern_lit` is allowed for symbol interning, it is directly * replaced by the symbol ID if presym is enabled by using the following * macros. * * MRB_OPSYM(xor) //=> ^ (Operator) * MRB_GVSYM(xor) //=> $xor (Global Variable) * MRB_CVSYM(xor) //=> @@xor (Class Variable) * MRB_IVSYM(xor) //=> @xor (Instance Variable) * MRB_SYM_B(xor) //=> xor! (Method with Bang) * MRB_SYM_Q(xor) //=> xor? (Method with Question mark) * MRB_SYM_E(xor) //=> xor= (Method with Equal) * MRB_SYM(xor) //=> xor (Word characters) * * For `MRB_OPSYM`, specify the names corresponding to operators (see * `MRuby::Presym::OPERATORS` in `lib/mruby/presym.rb` for the names that * can be specified for it). Other than that, describe only word characters * excluding leading and ending punctuation. * * These macros are expanded to `mrb_intern_lit` if presym is disabled, * therefore the mruby state variable is required. The above macros can be * used when the variable name is `mrb`. If you want to use other variable * names, you need to use macros with `_2` suffix, such as `MRB_SYM_2`. */ #endif /* MRUBY_PRESYM_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/common.h0000644000000000000000000000013215077107276022115 xustar0030 mtime=1761382078.101420652 30 atime=1761382080.122411407 30 ctime=1761382108.993300691 nghttp2-1.68.0/third-party/mruby/include/mruby/common.h0000644000175100017510000000607015077107276022510 0ustar00runnerrunner/** ** @file common.h - mruby common platform definition" ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_COMMON_H #define MRUBY_COMMON_H #ifdef __APPLE__ #ifndef __TARGETCONDITIONALS__ #include "TargetConditionals.h" #endif #endif #ifdef __cplusplus #ifdef MRB_USE_CXX_ABI #define MRB_BEGIN_DECL #define MRB_END_DECL #else # define MRB_BEGIN_DECL extern "C" { # define MRB_END_DECL } #endif #else /** Start declarations in C mode */ # define MRB_BEGIN_DECL /** End declarations in C mode */ # define MRB_END_DECL #endif #include #if defined _MSC_VER #include typedef SSIZE_T ssize_t; #endif /** * Shared compiler macros */ MRB_BEGIN_DECL /** Declare a function that never returns. */ #if defined __STDC_VERSION__ && __STDC_VERSION__ >= 201112L # define mrb_noreturn _Noreturn #elif defined __GNUC__ && !defined __STRICT_ANSI__ # define mrb_noreturn __attribute__((noreturn)) #elif defined _MSC_VER # define mrb_noreturn __declspec(noreturn) #else # define mrb_noreturn #endif /** Mark a function as deprecated. */ #if defined __GNUC__ && !defined __STRICT_ANSI__ # define mrb_deprecated __attribute__((deprecated)) #elif defined _MSC_VER # define mrb_deprecated __declspec(deprecated) #else # define mrb_deprecated #endif /** Declare a type or object as an alignment requirement. */ #ifndef mrb_alignas # if defined(__cplusplus) && __cplusplus >= 201103L # // https://en.cppreference.com/w/cpp/language/alignas # define mrb_alignas(n) alignas(n) # elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L # // https://en.cppreference.com/w/c/language/_Alignas # define mrb_alignas(n) _Alignas(n) # elif defined(_MSC_VER) || defined(__INTEL_COMPILER) # // https://learn.microsoft.com/en-us/cpp/cpp/align-cpp?view=msvc-170 # define mrb_alignas(n) __declspec(align(n)) # elif defined(__GNUC__) || defined(__clang__) # // https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#index-aligned-type-attribute # define mrb_alignas(n) __attribute__((aligned(n))) # else # // `mrb_alignas` defined as dummy. If necessary, send issues to https://github.com/mruby/mruby . # define mrb_alignas(n) # endif #endif /** Declare a function as always inlined. */ #if defined _MSC_VER && _MSC_VER < 1900 # ifndef __cplusplus # define inline __inline # endif #endif #define MRB_INLINE static inline /** Declare a public mruby API function. */ #ifndef MRB_API #if defined(MRB_BUILD_AS_DLL) #if defined(MRB_CORE) || defined(MRB_LIB) # define MRB_API __declspec(dllexport) #else # define MRB_API __declspec(dllimport) #endif #else # define MRB_API extern #endif #endif /** Declare mingw versions */ #if defined(__MINGW32__) || defined(__MINGW64__) # include <_mingw.h> # if defined(__MINGW64_VERSION_MAJOR) # define MRB_MINGW64_VERSION (__MINGW64_VERSION_MAJOR * 1000 + __MINGW64_VERSION_MINOR) # elif defined(__MINGW32_MAJOR_VERSION) # define MRB_MINGW32_VERSION (__MINGW32_MAJOR_VERSION * 1000 + __MINGW32_MINOR_VERSION) # endif # if defined(__MINGW32__) && !defined(__MINGW64__) # define MRB_MINGW32_LEGACY # endif #endif MRB_END_DECL #endif /* MRUBY_COMMON_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/mempool.h0000644000000000000000000000013215077107276022275 xustar0030 mtime=1761382078.102420648 30 atime=1761382080.123411402 30 ctime=1761382108.976300739 nghttp2-1.68.0/third-party/mruby/include/mruby/mempool.h0000644000175100017510000000124315077107276022665 0ustar00runnerrunner/* ** mempool.h - memory pool ** ** See Copyright Notice in mruby.h */ /* memory pool implementation */ typedef struct mempool mempool; MRB_API struct mempool* mempool_open(void); MRB_API void mempool_close(struct mempool*); MRB_API void* mempool_alloc(struct mempool*, size_t); MRB_API void* mempool_realloc(struct mempool*, void*, size_t oldlen, size_t newlen); /* compatibility layer */ typedef struct mempool mrb_mempool; #define mrb_mempool_open(m) mempool_open() #define mrb_mempool_close(m) mempool_close(m) #define mrb_mempool_alloc(m, size) mempool_alloc((m),(size)) #define mrb_mempool_realloc(m, ptr, oldlen, newlen) mempool_realloc((m),(ptr),(oldlen),(newlen)) nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/re.h0000644000000000000000000000013215077107276021233 xustar0030 mtime=1761382078.102420648 30 atime=1761382080.123411402 30 ctime=1761382108.971300754 nghttp2-1.68.0/third-party/mruby/include/mruby/re.h0000644000175100017510000000032115077107276021617 0ustar00runnerrunner/** ** @file mruby/re.h - Regexp class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_RE_H #define MRUBY_RE_H MRB_BEGIN_DECL #define REGEXP_CLASS "Regexp" MRB_END_DECL #endif /* RE_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/variable.h0000644000000000000000000000013215077107276022412 xustar0030 mtime=1761382078.103420643 30 atime=1761382080.124411398 30 ctime=1761382108.988300705 nghttp2-1.68.0/third-party/mruby/include/mruby/variable.h0000644000175100017510000000620115077107276023001 0ustar00runnerrunner/** ** @file mruby/variable.h - mruby variables ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_VARIABLE_H #define MRUBY_VARIABLE_H #include "common.h" /** * Functions to access mruby variables. */ MRB_BEGIN_DECL MRB_API mrb_value mrb_const_get(mrb_state*, mrb_value, mrb_sym); MRB_API void mrb_const_set(mrb_state*, mrb_value, mrb_sym, mrb_value); MRB_API mrb_bool mrb_const_defined(mrb_state*, mrb_value, mrb_sym); MRB_API void mrb_const_remove(mrb_state*, mrb_value, mrb_sym); MRB_API mrb_bool mrb_iv_name_sym_p(mrb_state *mrb, mrb_sym sym); MRB_API void mrb_iv_name_sym_check(mrb_state *mrb, mrb_sym sym); MRB_API mrb_value mrb_obj_iv_get(mrb_state *mrb, struct RObject *obj, mrb_sym sym); MRB_API void mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v); MRB_API mrb_bool mrb_obj_iv_defined(mrb_state *mrb, struct RObject *obj, mrb_sym sym); MRB_API mrb_value mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym); MRB_API void mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v); MRB_API mrb_bool mrb_iv_defined(mrb_state*, mrb_value, mrb_sym); MRB_API mrb_value mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym); MRB_API void mrb_iv_copy(mrb_state *mrb, mrb_value dst, mrb_value src); MRB_API mrb_bool mrb_const_defined_at(mrb_state *mrb, mrb_value mod, mrb_sym id); /** * Get a global variable. Will return nil if the var does not exist * * Example: * * !!!ruby * # Ruby style * var = $value * * !!!c * // C style * mrb_sym sym = mrb_intern_lit(mrb, "$value"); * mrb_value var = mrb_gv_get(mrb, sym); * * @param mrb The mruby state reference * @param sym The name of the global variable * @return The value of that global variable. May be nil */ MRB_API mrb_value mrb_gv_get(mrb_state *mrb, mrb_sym sym); /** * Set a global variable * * Example: * * !!!ruby * # Ruby style * $value = "foo" * * !!!c * // C style * mrb_sym sym = mrb_intern_lit(mrb, "$value"); * mrb_gv_set(mrb, sym, mrb_str_new_lit("foo")); * * @param mrb The mruby state reference * @param sym The name of the global variable * @param val The value of the global variable */ MRB_API void mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value val); /** * Remove a global variable. * * Example: * * # Ruby style * $value = nil * * // C style * mrb_sym sym = mrb_intern_lit(mrb, "$value"); * mrb_gv_remove(mrb, sym); * * @param mrb The mruby state reference * @param sym The name of the global variable */ MRB_API void mrb_gv_remove(mrb_state *mrb, mrb_sym sym); MRB_API mrb_value mrb_cv_get(mrb_state *mrb, mrb_value mod, mrb_sym sym); MRB_API void mrb_mod_cv_set(mrb_state *mrb, struct RClass * c, mrb_sym sym, mrb_value v); MRB_API void mrb_cv_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v); MRB_API mrb_bool mrb_cv_defined(mrb_state *mrb, mrb_value mod, mrb_sym sym); /* return non zero to break the loop */ typedef int (mrb_iv_foreach_func)(mrb_state*,mrb_sym,mrb_value,void*); MRB_API void mrb_iv_foreach(mrb_state *mrb, mrb_value obj, mrb_iv_foreach_func *func, void *p); MRB_END_DECL #endif /* MRUBY_VARIABLE_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/istruct.h0000644000000000000000000000013215077107276022322 xustar0030 mtime=1761382078.102420648 30 atime=1761382080.123411402 30 ctime=1761382108.990300699 nghttp2-1.68.0/third-party/mruby/include/mruby/istruct.h0000644000175100017510000000167715077107276022725 0ustar00runnerrunner/** ** @file mruby/istruct.h - Inline structures ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_ISTRUCT_H #define MRUBY_ISTRUCT_H #include "common.h" #include /** * Inline structures that fit in RVALUE * * They cannot have finalizer, and cannot have instance variables. */ MRB_BEGIN_DECL #define ISTRUCT_DATA_SIZE (sizeof(void*) * 3) struct RIStruct { MRB_OBJECT_HEADER; union { intptr_t inline_alignment[3]; char inline_data[ISTRUCT_DATA_SIZE]; }; }; #define RISTRUCT(obj) ((struct RIStruct*)(mrb_ptr(obj))) #define ISTRUCT_PTR(obj) (RISTRUCT(obj)->inline_data) MRB_INLINE mrb_int mrb_istruct_size(void) { return ISTRUCT_DATA_SIZE; } MRB_INLINE void* mrb_istruct_ptr(mrb_value object) { return ISTRUCT_PTR(object); } MRB_INLINE void mrb_istruct_copy(mrb_value dest, mrb_value src) { memcpy(ISTRUCT_PTR(dest), ISTRUCT_PTR(src), ISTRUCT_DATA_SIZE); } MRB_END_DECL #endif /* MRUBY_ISTRUCT_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/debug.h0000644000000000000000000000013215077107276021713 xustar0030 mtime=1761382078.101420652 30 atime=1761382080.122411407 30 ctime=1761382108.996300682 nghttp2-1.68.0/third-party/mruby/include/mruby/debug.h0000644000175100017510000000361515077107276022310 0ustar00runnerrunner/** ** @file mruby/debug.h - mruby debug info ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_DEBUG_H #define MRUBY_DEBUG_H #include "common.h" /** * mruby Debugging. */ MRB_BEGIN_DECL typedef enum mrb_debug_line_type { mrb_debug_line_ary = 0, mrb_debug_line_flat_map, mrb_debug_line_packed_map } mrb_debug_line_type; typedef struct mrb_irep_debug_info_line { uint32_t start_pos; uint16_t line; } mrb_irep_debug_info_line; typedef struct mrb_irep_debug_info_file { uint32_t start_pos; mrb_sym filename_sym; uint32_t line_entry_count; mrb_debug_line_type line_type; union { const char *s; void *ptr; const uint16_t *ary; const mrb_irep_debug_info_line *flat_map; const uint8_t *packed_map; } lines; } mrb_irep_debug_info_file; typedef struct mrb_irep_debug_info { uint32_t pc_count; uint16_t flen; mrb_irep_debug_info_file **files; } mrb_irep_debug_info; /* * get filename from irep's debug info and program counter * @return returns NULL if not found */ MRB_API const char *mrb_debug_get_filename(mrb_state *mrb, const mrb_irep *irep, uint32_t pc); /* * get line from irep's debug info and program counter * @return returns -1 if not found */ MRB_API int32_t mrb_debug_get_line(mrb_state *mrb, const mrb_irep *irep, uint32_t pc); /* * get line and filename from irep's debug info and program counter * @return returns FALSE if not found */ MRB_API mrb_bool mrb_debug_get_position(mrb_state *mrb, const mrb_irep *irep, uint32_t pc, int32_t *lp, const char **fp); MRB_API mrb_irep_debug_info *mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep); MRB_API mrb_irep_debug_info_file *mrb_debug_info_append_file( mrb_state *mrb, mrb_irep_debug_info *info, const char *filename, uint16_t *lines, uint32_t start_pos, uint32_t end_pos); MRB_API void mrb_debug_info_free(mrb_state *mrb, mrb_irep_debug_info *d); MRB_END_DECL #endif /* MRUBY_DEBUG_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/numeric.h0000644000000000000000000000013215077107276022267 xustar0030 mtime=1761382078.102420648 30 atime=1761382080.123411402 30 ctime=1761382108.998300676 nghttp2-1.68.0/third-party/mruby/include/mruby/numeric.h0000644000175100017510000001221015077107276022653 0ustar00runnerrunner/** ** @file mruby/numeric.h - Numeric, Integer, Float class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_NUMERIC_H #define MRUBY_NUMERIC_H #include "common.h" /** * Numeric class and it's sub-classes. * * Integer and Float */ MRB_BEGIN_DECL #define TYPED_POSFIXABLE(f,t) ((f) <= (t)MRB_FIXNUM_MAX) #define TYPED_NEGFIXABLE(f,t) ((f) >= (t)MRB_FIXNUM_MIN) #define TYPED_FIXABLE(f,t) (TYPED_POSFIXABLE(f,t) && TYPED_NEGFIXABLE(f,t)) #define POSFIXABLE(f) TYPED_POSFIXABLE(f,mrb_int) #define NEGFIXABLE(f) TYPED_NEGFIXABLE(f,mrb_int) #define FIXABLE(f) TYPED_FIXABLE(f,mrb_int) #ifndef MRB_NO_FLOAT #ifdef MRB_INT64 #define FIXABLE_FLOAT(f) ((f)>=-9223372036854775808.0 && (f)<9223372036854775808.0) #else #define FIXABLE_FLOAT(f) TYPED_FIXABLE(f,mrb_float) #endif #endif /* utility functions */ MRB_API mrb_value mrb_num_add(mrb_state *mrb, mrb_value x, mrb_value y); MRB_API mrb_value mrb_num_sub(mrb_state *mrb, mrb_value x, mrb_value y); MRB_API mrb_value mrb_num_mul(mrb_state *mrb, mrb_value x, mrb_value y); /* obsolete old names */ #define mrb_num_plus(mrb, x, y) mrb_num_add(mrb, x, y) #define mrb_num_minus(mrb, x, y) mrb_num_sub(mrb, x, y) MRB_API mrb_value mrb_integer_to_str(mrb_state *mrb, mrb_value x, mrb_int base); MRB_API char *mrb_int_to_cstr(char *buf, size_t len, mrb_int n, mrb_int base); /* obsolete function(s); will be removed */ #define mrb_fixnum_to_str(mrb, x, base) mrb_integer_to_str(mrb, x, base) #ifndef __has_builtin #define __has_builtin(x) 0 #endif #if (defined(__GNUC__) && __GNUC__ >= 5) || \ (__has_builtin(__builtin_add_overflow) && \ __has_builtin(__builtin_sub_overflow) && \ __has_builtin(__builtin_mul_overflow)) # define MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS #endif /* // Clang 3.8 and 3.9 have problem compiling mruby in 32-bit mode, when MRB_INT64 is set // because of missing __mulodi4 and similar functions in its runtime. We need to use custom // implementation for them. */ #ifdef MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS #if defined(__clang__) && (__clang_major__ == 3) && (__clang_minor__ >= 8) && \ defined(MRB_32BIT) && defined(MRB_INT64) #undef MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS #endif #endif #ifdef MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS static inline mrb_bool mrb_int_add_overflow(mrb_int augend, mrb_int addend, mrb_int *sum) { return __builtin_add_overflow(augend, addend, sum); } static inline mrb_bool mrb_int_sub_overflow(mrb_int minuend, mrb_int subtrahend, mrb_int *difference) { return __builtin_sub_overflow(minuend, subtrahend, difference); } static inline mrb_bool mrb_int_mul_overflow(mrb_int multiplier, mrb_int multiplicand, mrb_int *product) { return __builtin_mul_overflow(multiplier, multiplicand, product); } #else #define MRB_INT_OVERFLOW_MASK ((mrb_uint)1 << (MRB_INT_BIT - 1)) static inline mrb_bool mrb_int_add_overflow(mrb_int a, mrb_int b, mrb_int *c) { mrb_uint x = (mrb_uint)a; mrb_uint y = (mrb_uint)b; mrb_uint z = (mrb_uint)(x + y); *c = (mrb_int)z; return !!(((x ^ z) & (y ^ z)) & MRB_INT_OVERFLOW_MASK); } static inline mrb_bool mrb_int_sub_overflow(mrb_int a, mrb_int b, mrb_int *c) { mrb_uint x = (mrb_uint)a; mrb_uint y = (mrb_uint)b; mrb_uint z = (mrb_uint)(x - y); *c = (mrb_int)z; return !!(((x ^ z) & (~y ^ z)) & MRB_INT_OVERFLOW_MASK); } static inline mrb_bool mrb_int_mul_overflow(mrb_int a, mrb_int b, mrb_int *c) { #ifdef MRB_INT32 int64_t n = (int64_t)a * b; *c = (mrb_int)n; return n > MRB_INT_MAX || n < MRB_INT_MIN; #else /* MRB_INT64 */ if (a > 0 && b > 0 && a > MRB_INT_MAX / b) return TRUE; if (a < 0 && b > 0 && a < MRB_INT_MIN / b) return TRUE; if (a > 0 && b < 0 && b < MRB_INT_MIN / a) return TRUE; if (a < 0 && b < 0 && (a <= MRB_INT_MIN || b <= MRB_INT_MIN || -a > MRB_INT_MAX / -b)) return TRUE; *c = a * b; return FALSE; #endif } #undef MRB_INT_OVERFLOW_MASK #endif #ifndef MRB_NO_FLOAT # define MRB_FLT_RADIX FLT_RADIX # ifdef MRB_USE_FLOAT32 # define MRB_FLT_MANT_DIG FLT_MANT_DIG # define MRB_FLT_EPSILON FLT_EPSILON # define MRB_FLT_DIG FLT_DIG # define MRB_FLT_MIN_EXP FLT_MIN_EXP # define MRB_FLT_MIN FLT_MIN # define MRB_FLT_MIN_10_EXP FLT_MIN_10_EXP # define MRB_FLT_MAX_EXP FLT_MAX_EXP # define MRB_FLT_MAX FLT_MAX # define MRB_FLT_MAX_10_EXP FLT_MAX_10_EXP # else /* not MRB_USE_FLOAT32 */ # define MRB_FLT_MANT_DIG DBL_MANT_DIG # define MRB_FLT_EPSILON DBL_EPSILON # define MRB_FLT_DIG DBL_DIG # define MRB_FLT_MIN_EXP DBL_MIN_EXP # define MRB_FLT_MIN DBL_MIN # define MRB_FLT_MIN_10_EXP DBL_MIN_10_EXP # define MRB_FLT_MAX_EXP DBL_MAX_EXP # define MRB_FLT_MAX DBL_MAX # define MRB_FLT_MAX_10_EXP DBL_MAX_10_EXP # endif /* MRB_USE_FLOAT32 */ MRB_API mrb_value mrb_float_to_integer(mrb_state *mrb, mrb_value val); /* internal functions */ mrb_float mrb_div_float(mrb_float x, mrb_float y); mrb_value mrb_float_to_str(mrb_state *mrb, mrb_value x, const char *fmt); int mrb_format_float(mrb_float f, char *buf, size_t buf_size, char fmt, int prec, char sign); #endif /* MRB_NO_FLOAT */ MRB_END_DECL #endif /* MRUBY_NUMERIC_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/version.h0000644000000000000000000000013215077107276022312 xustar0030 mtime=1761382078.103420643 30 atime=1761382080.124411398 30 ctime=1761382109.006300653 nghttp2-1.68.0/third-party/mruby/include/mruby/version.h0000644000175100017510000000522215077107276022703 0ustar00runnerrunner/** ** @file mruby/version.h - mruby version definition ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_VERSION_H #define MRUBY_VERSION_H #include "common.h" /** * mruby version definition macros */ MRB_BEGIN_DECL /* * A passed in expression. */ #define MRB_STRINGIZE0(expr) #expr /* * Passes in an expression to MRB_STRINGIZE0. */ #define MRB_STRINGIZE(expr) MRB_STRINGIZE0(expr) /* * The version of Ruby used by mruby. */ #define MRUBY_RUBY_VERSION "3.4" /* * Ruby engine. */ #define MRUBY_RUBY_ENGINE "mruby" /* * Major release version number. */ #define MRUBY_RELEASE_MAJOR 3 /* * Minor release version number. */ #define MRUBY_RELEASE_MINOR 4 /* * Tiny release version number. */ #define MRUBY_RELEASE_TEENY 0 /* * Patch level. */ #define MRUBY_PATCHLEVEL -1 /* * Patch level string. (optional) */ #define MRUBY_PATCHLEVEL_STR "" #ifndef MRUBY_PATCHLEVEL_STR # if MRUBY_PATCHLEVEL < 0 # define MRUBY_PATCHLEVEL_STR "dev" # else # define MRUBY_PATCHLEVEL_STR "p"MRB_STRINGIZE(MRUBY_PATCHLEVEL) # endif #endif /* * The mruby version. */ #define MRUBY_VERSION MRB_STRINGIZE(MRUBY_RELEASE_MAJOR) "." MRB_STRINGIZE(MRUBY_RELEASE_MINOR) "." MRB_STRINGIZE(MRUBY_RELEASE_TEENY) /* * Release number. */ #define MRUBY_RELEASE_NO (MRUBY_RELEASE_MAJOR * 100 * 100 + MRUBY_RELEASE_MINOR * 100 + MRUBY_RELEASE_TEENY) /* * Release year. */ #define MRUBY_RELEASE_YEAR 2025 /* * Release month. */ #define MRUBY_RELEASE_MONTH 4 /* * Release day. */ #define MRUBY_RELEASE_DAY 20 /* * Release date as a string. */ #define MRUBY_RELEASE_DATE \ MRUBY_RELEASE_YEAR_STR "-" \ MRUBY_RELEASE_MONTH_STR "-" \ MRUBY_RELEASE_DAY_STR #define MRUBY_RELEASE_YEAR_STR MRB_STRINGIZE(MRUBY_RELEASE_YEAR) #if MRUBY_RELEASE_MONTH < 10 #define MRUBY_RELEASE_MONTH_STR "0" MRB_STRINGIZE(MRUBY_RELEASE_MONTH) #else #define MRUBY_RELEASE_MONTH_STR MRB_STRINGIZE(MRUBY_RELEASE_MONTH) #endif #if MRUBY_RELEASE_DAY < 10 #define MRUBY_RELEASE_DAY_STR "0" MRB_STRINGIZE(MRUBY_RELEASE_DAY) #else #define MRUBY_RELEASE_DAY_STR MRB_STRINGIZE(MRUBY_RELEASE_DAY) #endif /* * The year mruby was first created. */ #define MRUBY_BIRTH_YEAR 2010 /* * mruby's authors. */ #define MRUBY_AUTHOR "mruby developers" /* * mruby's version, and release date. */ #define MRUBY_DESCRIPTION \ "mruby " MRUBY_VERSION \ MRUBY_PATCHLEVEL_STR \ " (" MRUBY_RELEASE_DATE ")" \ /* * mruby's copyright information. */ #define MRUBY_COPYRIGHT \ "mruby - Copyright (c) " \ MRB_STRINGIZE(MRUBY_BIRTH_YEAR)"-" \ MRB_STRINGIZE(MRUBY_RELEASE_YEAR)" " \ MRUBY_AUTHOR \ MRB_END_DECL #endif /* MRUBY_VERSION_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/boxing_no.h0000644000000000000000000000013215077107276022607 xustar0030 mtime=1761382078.101420652 30 atime=1761382080.122411407 30 ctime=1761382108.997300679 nghttp2-1.68.0/third-party/mruby/include/mruby/boxing_no.h0000644000175100017510000000345715077107276023210 0ustar00runnerrunner/** ** @file mruby/boxing_no.h - unboxed mrb_value definition ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_BOXING_NO_H #define MRUBY_BOXING_NO_H #define MRB_FIXNUM_SHIFT 0 #define MRB_SYMBOL_SHIFT 0 #define MRB_FIXNUM_MIN MRB_INT_MIN #define MRB_FIXNUM_MAX MRB_INT_MAX union mrb_value_union { #ifndef MRB_NO_FLOAT mrb_float f; #endif void *p; mrb_int i; mrb_sym sym; }; typedef struct mrb_value { union mrb_value_union value; enum mrb_vtype tt; } mrb_value; #define mrb_ptr(o) (o).value.p #define mrb_cptr(o) mrb_ptr(o) #ifndef MRB_NO_FLOAT #define mrb_float(o) (o).value.f #endif #define mrb_fixnum(o) (o).value.i #define mrb_integer(o) mrb_fixnum(o) #define mrb_symbol(o) (o).value.sym #define mrb_type(o) (o).tt #define mrb_unboxed_type(o) (o).tt #define BOXNO_SET_VALUE(o, ttt, attr, v) do {\ (o).tt = ttt;\ (o).attr = v;\ } while (0) #define SET_NIL_VALUE(r) BOXNO_SET_VALUE(r, MRB_TT_FALSE, value.i, 0) #define SET_FALSE_VALUE(r) BOXNO_SET_VALUE(r, MRB_TT_FALSE, value.i, 1) #define SET_TRUE_VALUE(r) BOXNO_SET_VALUE(r, MRB_TT_TRUE, value.i, 1) #define SET_BOOL_VALUE(r,b) BOXNO_SET_VALUE(r, b ? MRB_TT_TRUE : MRB_TT_FALSE, value.i, 1) #define SET_INT_VALUE(mrb,r,n) BOXNO_SET_VALUE(r, MRB_TT_INTEGER, value.i, (n)) #define SET_FIXNUM_VALUE(r,n) BOXNO_SET_VALUE(r, MRB_TT_INTEGER, value.i, (n)) #ifndef MRB_NO_FLOAT #define SET_FLOAT_VALUE(mrb,r,v) BOXNO_SET_VALUE(r, MRB_TT_FLOAT, value.f, (v)) #endif #define SET_SYM_VALUE(r,v) BOXNO_SET_VALUE(r, MRB_TT_SYMBOL, value.sym, (v)) #define SET_OBJ_VALUE(r,v) BOXNO_SET_VALUE(r, (((struct RObject*)(v))->tt), value.p, (v)) #define SET_CPTR_VALUE(mrb,r,v) BOXNO_SET_VALUE(r, MRB_TT_CPTR, value.p, v) #define SET_UNDEF_VALUE(r) BOXNO_SET_VALUE(r, MRB_TT_UNDEF, value.i, 0) #endif /* MRUBY_BOXING_NO_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/internal.h0000644000000000000000000000013215077107276022441 xustar0030 mtime=1761382078.101420652 30 atime=1761382080.123411402 30 ctime=1761382109.003300661 nghttp2-1.68.0/third-party/mruby/include/mruby/internal.h0000644000175100017510000002655715077107276023050 0ustar00runnerrunner/** ** @file mruby/internal.h - Functions only called from within the library ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_INTERNAL_H #define MRUBY_INTERNAL_H #ifdef MRUBY_ARRAY_H void mrb_ary_decref(mrb_state*, mrb_shared_array*); mrb_value mrb_ary_subseq(mrb_state *mrb, mrb_value ary, mrb_int beg, mrb_int len); #endif mrb_bool mrb_inspect_recursive_p(mrb_state *mrb, mrb_value self); #ifdef MRUBY_CLASS_H struct RClass *mrb_vm_define_class(mrb_state*, mrb_value, mrb_value, mrb_sym); struct RClass *mrb_vm_define_module(mrb_state*, mrb_value, mrb_sym); mrb_value mrb_instance_new(mrb_state *mrb, mrb_value cv); void mrb_class_name_class(mrb_state*, struct RClass*, struct RClass*, mrb_sym); mrb_bool mrb_const_name_p(mrb_state*, const char*, mrb_int); mrb_value mrb_class_find_path(mrb_state*, struct RClass*); mrb_value mrb_mod_to_s(mrb_state *, mrb_value); void mrb_method_added(mrb_state *mrb, struct RClass *c, mrb_sym mid); mrb_noreturn void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args); mrb_method_t mrb_vm_find_method(mrb_state *mrb, struct RClass *c, struct RClass **cp, mrb_sym mid); mrb_value mrb_mod_const_missing(mrb_state *mrb, mrb_value mod); mrb_value mrb_const_missing(mrb_state *mrb, mrb_value mod, mrb_sym sym); size_t mrb_class_mt_memsize(mrb_state*, struct RClass*); mrb_value mrb_obj_extend(mrb_state*, mrb_value obj); #endif mrb_value mrb_obj_equal_m(mrb_state *mrb, mrb_value); /* debug */ size_t mrb_packed_int_len(uint32_t num); size_t mrb_packed_int_encode(uint32_t num, uint8_t *p); uint32_t mrb_packed_int_decode(const uint8_t *p, const uint8_t **newpos); /* dump */ #ifdef MRUBY_IREP_H int mrb_dump_irep(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size); #ifndef MRB_NO_STDIO int mrb_dump_irep_cfunc(mrb_state *mrb, const mrb_irep*, uint8_t flags, FILE *f, const char *initname); int mrb_dump_irep_cstruct(mrb_state *mrb, const mrb_irep*, uint8_t flags, FILE *f, const char *initname); #endif #endif /* codedump */ void mrb_codedump_all(mrb_state *mrb, struct RProc *proc); #ifndef MRB_NO_STDIO void mrb_codedump_all_file(mrb_state *mrb, struct RProc *proc, FILE *out); #endif /* error */ mrb_value mrb_exc_inspect(mrb_state *mrb, mrb_value exc); mrb_value mrb_exc_backtrace(mrb_state *mrb, mrb_value exc); mrb_value mrb_get_backtrace(mrb_state *mrb); void mrb_exc_mesg_set(mrb_state *mrb, struct RException *exc, mrb_value mesg); mrb_value mrb_exc_mesg_get(mrb_state *mrb, struct RException *exc); mrb_value mrb_f_raise(mrb_state*, mrb_value); mrb_value mrb_make_exception(mrb_state *mrb, mrb_value exc, mrb_value mesg); mrb_value mrb_exc_get_output(mrb_state *mrb, struct RObject *exc); struct RBacktrace { MRB_OBJECT_HEADER; size_t len; struct mrb_backtrace_location *locations; }; struct mrb_backtrace_location { mrb_sym method_id; int32_t idx; const mrb_irep *irep; }; /* gc */ size_t mrb_gc_mark_mt(mrb_state*, struct RClass*); void mrb_gc_free_mt(mrb_state*, struct RClass*); /* hash */ size_t mrb_hash_memsize(mrb_value obj); size_t mrb_gc_mark_hash(mrb_state*, struct RHash*); void mrb_gc_free_hash(mrb_state*, struct RHash*); mrb_value mrb_hash_first_key(mrb_state*, mrb_value); /* irep */ struct mrb_insn_data mrb_decode_insn(const mrb_code *pc); #ifdef MRUBY_IREP_H void mrb_irep_free(mrb_state*, struct mrb_irep*); static inline const struct mrb_irep_catch_handler * mrb_irep_catch_handler_table(const struct mrb_irep *irep) { if (irep->clen > 0) { return (const struct mrb_irep_catch_handler*)(irep->iseq + irep->ilen); } else { return (const struct mrb_irep_catch_handler*)NULL; } } #endif /* numeric */ mrb_value mrb_div_int_value(mrb_state *mrb, mrb_int x, mrb_int y); mrb_int mrb_div_int(mrb_int x, mrb_int y); mrb_value mrb_int_add(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_int_sub(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_int_mul(mrb_state *mrb, mrb_value x, mrb_value y); mrb_noreturn void mrb_int_zerodiv(mrb_state *mrb); mrb_noreturn void mrb_int_overflow(mrb_state *mrb, const char *reason); #ifndef MRB_NO_FLOAT void mrb_check_num_exact(mrb_state *mrb, mrb_float num); #endif #ifdef MRB_USE_COMPLEX mrb_value mrb_complex_new(mrb_state *mrb, mrb_float x, mrb_float y); mrb_value mrb_complex_add(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_complex_sub(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_complex_mul(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_complex_div(mrb_state *mrb, mrb_value x, mrb_value y); void mrb_complex_copy(mrb_state *mrb, mrb_value x, mrb_value y); #endif #ifdef MRB_USE_RATIONAL mrb_value mrb_rational_new(mrb_state *mrb, mrb_int x, mrb_int y); mrb_value mrb_rational_add(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_rational_sub(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_rational_mul(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_rational_div(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_as_rational(mrb_state *mrb, mrb_value x); void mrb_rational_copy(mrb_state *mrb, mrb_value x, mrb_value y); int mrb_rational_mark(mrb_state *mrb, struct RBasic *rat); #endif #ifdef MRUBY_PROC_H struct RProc *mrb_closure_new(mrb_state*, const mrb_irep*); void mrb_proc_copy(mrb_state *mrb, struct RProc *a, const struct RProc *b); mrb_int mrb_proc_arity(const struct RProc *p); struct REnv *mrb_env_new(mrb_state *mrb, struct mrb_context *c, mrb_callinfo *ci, int nstacks, mrb_value *stack, struct RClass *tc); void mrb_proc_merge_lvar(mrb_state *mrb, mrb_irep *irep, struct REnv *env, int num, const mrb_sym *lv, const mrb_value *stack); mrb_value mrb_proc_local_variables(mrb_state *mrb, const struct RProc *proc); const struct RProc *mrb_proc_get_caller(mrb_state *mrb, struct REnv **env); mrb_value mrb_proc_get_self(mrb_state *mrb, const struct RProc *p, struct RClass **target_class_p); mrb_bool mrb_proc_eql(mrb_state *mrb, mrb_value self, mrb_value other); #endif /* range */ #ifdef MRUBY_RANGE_H mrb_value mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, const mrb_value *argv, mrb_value (*func)(mrb_state*, mrb_value, mrb_int)); size_t mrb_gc_mark_range(mrb_state *mrb, struct RRange *r); #endif /* string */ void mrb_gc_free_str(mrb_state*, struct RString*); uint32_t mrb_str_hash(mrb_state *mrb, mrb_value str); mrb_value mrb_str_dump(mrb_state *mrb, mrb_value str); mrb_value mrb_str_inspect(mrb_state *mrb, mrb_value str); mrb_bool mrb_str_beg_len(mrb_int str_len, mrb_int *begp, mrb_int *lenp); mrb_value mrb_str_byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len); mrb_value mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value idx, mrb_value len); uint32_t mrb_byte_hash(const uint8_t*, mrb_int); uint32_t mrb_byte_hash_step(const uint8_t*, mrb_int, uint32_t); #ifdef MRB_UTF8_STRING mrb_int mrb_utf8len(const char *str, const char *end); mrb_int mrb_utf8_strlen(const char *str, mrb_int byte_len); #endif /* variable */ mrb_value mrb_vm_special_get(mrb_state*, mrb_sym); void mrb_vm_special_set(mrb_state*, mrb_sym, mrb_value); mrb_value mrb_vm_cv_get(mrb_state*, mrb_sym); void mrb_vm_cv_set(mrb_state*, mrb_sym, mrb_value); mrb_value mrb_vm_const_get(mrb_state*, mrb_sym); size_t mrb_obj_iv_tbl_memsize(mrb_value); mrb_value mrb_obj_iv_inspect(mrb_state*, struct RObject*); void mrb_obj_iv_set_force(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v); mrb_value mrb_mod_constants(mrb_state *mrb, mrb_value mod); mrb_value mrb_mod_const_at(mrb_state *mrb, struct RClass *c, mrb_value ary); mrb_value mrb_f_global_variables(mrb_state *mrb, mrb_value self); mrb_value mrb_obj_instance_variables(mrb_state*, mrb_value); mrb_value mrb_mod_class_variables(mrb_state*, mrb_value); mrb_value mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym); mrb_bool mrb_mod_cv_defined(mrb_state *mrb, struct RClass *c, mrb_sym sym); mrb_bool mrb_ident_p(const char *s, mrb_int len); mrb_value mrb_exc_const_get(mrb_state *mrb, mrb_sym sym); /* GC functions */ void mrb_gc_mark_gv(mrb_state*); void mrb_gc_free_gv(mrb_state*); size_t mrb_gc_mark_iv(mrb_state*, struct RObject*); void mrb_gc_free_iv(mrb_state*, struct RObject*); /* VM */ #define MRB_CI_VISIBILITY(ci) MRB_FLAGS_GET((ci)->vis, 0, 2) #define MRB_CI_SET_VISIBILITY(ci, visi) MRB_FLAGS_SET((ci)->vis, 0, 2, visi) #define MRB_CI_VISIBILITY_BREAK_P(ci) MRB_FLAG_CHECK((ci)->vis, 2) #define MRB_CI_SET_VISIBILITY_BREAK(ci) MRB_FLAG_ON((ci)->vis, 2) mrb_int mrb_ci_bidx(mrb_callinfo *ci); mrb_int mrb_ci_nregs(mrb_callinfo *ci); mrb_value mrb_exec_irep(mrb_state *mrb, mrb_value self, const struct RProc *p); mrb_value mrb_obj_instance_eval(mrb_state*, mrb_value); mrb_value mrb_object_exec(mrb_state *mrb, mrb_value self, struct RClass *target_class); mrb_value mrb_mod_module_eval(mrb_state*, mrb_value); mrb_value mrb_f_send(mrb_state *mrb, mrb_value self); mrb_value mrb_f_public_send(mrb_state *mrb, mrb_value self); #ifdef MRB_USE_BIGINT mrb_value mrb_bint_new_int(mrb_state *mrb, mrb_int x); #ifdef MRB_INT64 #define mrb_bint_new_int64(mrb,x) mrb_bint_new_int((mrb),(mrb_int)(x)) #else mrb_value mrb_bint_new_int64(mrb_state *mrb, int64_t x); #endif mrb_value mrb_bint_new_uint64(mrb_state *mrb, uint64_t x); mrb_value mrb_bint_new_str(mrb_state *mrb, const char *x, mrb_int len, mrb_int base); mrb_value mrb_as_bint(mrb_state *mrb, mrb_value x); mrb_value mrb_bint_add(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_bint_sub(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_bint_add_n(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_bint_sub_n(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_bint_mul(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_bint_div(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_bint_divmod(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_bint_add_ii(mrb_state *mrb, mrb_int x, mrb_int y); mrb_value mrb_bint_sub_ii(mrb_state *mrb, mrb_int x, mrb_int y); mrb_value mrb_bint_mul_ii(mrb_state *mrb, mrb_int x, mrb_int y); mrb_value mrb_bint_mod(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_bint_rem(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_bint_pow(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_bint_powm(mrb_state *mrb, mrb_value x, mrb_value y, mrb_value z); mrb_value mrb_bint_and(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_bint_or(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_bint_neg(mrb_state *mrb, mrb_value x); mrb_value mrb_bint_xor(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_bint_rev(mrb_state *mrb, mrb_value x); mrb_value mrb_bint_lshift(mrb_state *mrb, mrb_value x, mrb_int width); mrb_value mrb_bint_rshift(mrb_state *mrb, mrb_value x, mrb_int width); mrb_value mrb_bint_to_s(mrb_state *mrb, mrb_value x, mrb_int base); #ifndef MRB_NO_FLOAT mrb_value mrb_bint_new_float(mrb_state *mrb, mrb_float x); mrb_float mrb_bint_as_float(mrb_state *mrb, mrb_value x); #endif mrb_int mrb_bint_as_int(mrb_state *mrb, mrb_value x); #ifdef MRB_INT64 #define mrb_bint_as_int64(mrb, x) mrb_bint_as_int((mrb), (x)) #else int64_t mrb_bint_as_int64(mrb_state *mrb, mrb_value x); #endif uint64_t mrb_bint_as_uint64(mrb_state *mrb, mrb_value x); mrb_int mrb_bint_cmp(mrb_state *mrb, mrb_value x, mrb_value y); void mrb_gc_free_bint(mrb_state *mrb, struct RBasic *x); void mrb_bint_copy(mrb_state *mrb, mrb_value x, mrb_value y); size_t mrb_bint_memsize(mrb_value x); mrb_value mrb_bint_hash(mrb_state *mrb, mrb_value x); mrb_value mrb_bint_sqrt(mrb_state *mrb, mrb_value x); #endif #endif /* MRUBY_INTERNAL_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/khash.h0000644000000000000000000000013115077107276021722 xustar0030 mtime=1761382078.102420648 30 atime=1761382080.123411402 29 ctime=1761382109.00730065 nghttp2-1.68.0/third-party/mruby/include/mruby/khash.h0000644000175100017510000003575215077107276022327 0ustar00runnerrunner/** ** @file mruby/khash.h - Hash for mruby ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_KHASH_H #define MRUBY_KHASH_H #include #include #include "common.h" /** * khash definitions used in mruby's hash table. */ MRB_BEGIN_DECL typedef uint32_t khint_t; typedef khint_t khiter_t; #ifndef KHASH_DEFAULT_SIZE # define KHASH_DEFAULT_SIZE 32 #endif #define KHASH_MIN_SIZE 8 #define UPPER_BOUND(x) ((x)>>2|(x)>>1) /* extern uint8_t __m[]; */ /* mask for flags */ static const uint8_t __m_empty[] = {0x02, 0x08, 0x20, 0x80}; static const uint8_t __m_del[] = {0x01, 0x04, 0x10, 0x40}; static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0}; #define __ac_isempty(ed_flag, i) (ed_flag[(i)/4]&__m_empty[(i)%4]) #define __ac_isdel(ed_flag, i) (ed_flag[(i)/4]&__m_del[(i)%4]) #define __ac_iseither(ed_flag, i) (ed_flag[(i)/4]&__m_either[(i)%4]) #define khash_power2(v) do { \ v--;\ v |= v >> 1;\ v |= v >> 2;\ v |= v >> 4;\ v |= v >> 8;\ v |= v >> 16;\ v++;\ } while (0) #define khash_mask(h) ((h)->n_buckets-1) #define khash_upper_bound(h) (UPPER_BOUND((h)->n_buckets)) /* declare struct kh_xxx and kh_xxx_funcs name: hash name khkey_t: key data type khval_t: value data type kh_is_map: (0: hash set / 1: hash map) */ #define KHASH_DECLARE(name, khkey_t, khval_t, kh_is_map) \ typedef struct kh_##name { \ khint_t n_buckets; \ khint_t size; \ uint8_t *ed_flags; \ khkey_t *keys; \ khval_t *vals; \ } kh_##name##_t; \ void kh_alloc_##name(mrb_state *mrb, kh_##name##_t *h); \ kh_##name##_t *kh_init_##name##_size(mrb_state *mrb, khint_t size); \ kh_##name##_t *kh_init_##name(mrb_state *mrb); \ void kh_destroy_##name(mrb_state *mrb, kh_##name##_t *h); \ void kh_clear_##name(mrb_state *mrb, kh_##name##_t *h); \ khint_t kh_get_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key); \ khint_t kh_put_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key, int *ret); \ void kh_resize_##name(mrb_state *mrb, kh_##name##_t *h, khint_t new_n_buckets); \ void kh_del_##name(mrb_state *mrb, kh_##name##_t *h, khint_t x); \ kh_##name##_t *kh_copy_##name(mrb_state *mrb, kh_##name##_t *h); static inline void kh_fill_flags(uint8_t *p, uint8_t c, size_t len) { while (len-- > 0) { *p++ = c; } } /* define kh_xxx_funcs name: hash name khkey_t: key data type khval_t: value data type kh_is_map: (0: hash set / 1: hash map) __hash_func: hash function __hash_equal: hash comparison function */ #define KHASH_DEFINE(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ mrb_noreturn void mrb_raise_nomemory(mrb_state *mrb); \ int kh_alloc_simple_##name(mrb_state *mrb, kh_##name##_t *h) \ { \ khint_t sz = h->n_buckets; \ size_t len = sizeof(khkey_t) + (kh_is_map ? sizeof(khval_t) : 0); \ uint8_t *p = (uint8_t*)mrb_malloc_simple(mrb, sizeof(uint8_t)*sz/4+len*sz); \ if (!p) { return 1; } \ h->size = 0; \ h->keys = (khkey_t*)p; \ h->vals = kh_is_map ? (khval_t*)(p+sizeof(khkey_t)*sz) : NULL; \ h->ed_flags = p+len*sz; \ kh_fill_flags(h->ed_flags, 0xaa, sz/4); \ return 0; \ } \ void kh_alloc_##name(mrb_state *mrb, kh_##name##_t *h) \ { \ if (kh_alloc_simple_##name(mrb, h)) { \ mrb_raise_nomemory(mrb); \ } \ } \ kh_##name##_t *kh_init_##name##_size(mrb_state *mrb, khint_t size) { \ kh_##name##_t *h = (kh_##name##_t*)mrb_calloc(mrb, 1, sizeof(kh_##name##_t)); \ if (size < KHASH_MIN_SIZE) \ size = KHASH_MIN_SIZE; \ khash_power2(size); \ h->n_buckets = size; \ if (kh_alloc_simple_##name(mrb, h)) { \ mrb_free(mrb, h); \ mrb_raise_nomemory(mrb); \ } \ return h; \ } \ kh_##name##_t *kh_init_##name(mrb_state *mrb) { \ return kh_init_##name##_size(mrb, KHASH_DEFAULT_SIZE); \ } \ void kh_destroy_##name(mrb_state *mrb, kh_##name##_t *h) \ { \ if (h) { \ mrb_free(mrb, h->keys); \ mrb_free(mrb, h); \ } \ } \ void kh_clear_##name(mrb_state *mrb, kh_##name##_t *h) \ { \ (void)mrb; \ if (h && h->ed_flags) { \ kh_fill_flags(h->ed_flags, 0xaa, h->n_buckets/4); \ h->size = 0; \ } \ } \ khint_t kh_get_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key) \ { \ khint_t k = __hash_func(mrb,key) & khash_mask(h), step = 0; \ (void)mrb; \ while (!__ac_isempty(h->ed_flags, k)) { \ if (!__ac_isdel(h->ed_flags, k)) { \ if (__hash_equal(mrb,h->keys[k], key)) return k; \ } \ k = (k+(++step)) & khash_mask(h); \ } \ return kh_end(h); \ } \ void kh_resize_##name(mrb_state *mrb, kh_##name##_t *h, khint_t new_n_buckets) \ { \ if (new_n_buckets < KHASH_MIN_SIZE) \ new_n_buckets = KHASH_MIN_SIZE; \ khash_power2(new_n_buckets); \ { \ kh_##name##_t hh; \ uint8_t *old_ed_flags = h->ed_flags; \ khkey_t *old_keys = h->keys; \ khval_t *old_vals = h->vals; \ khint_t old_n_buckets = h->n_buckets; \ khint_t i; \ hh.n_buckets = new_n_buckets; \ kh_alloc_##name(mrb, &hh); \ /* relocate */ \ for (i=0; isize >= khash_upper_bound(h)) { \ kh_resize_##name(mrb, h, h->n_buckets*2); \ } \ k = __hash_func(mrb,key) & khash_mask(h); \ del_k = kh_end(h); \ while (!__ac_isempty(h->ed_flags, k)) { \ if (!__ac_isdel(h->ed_flags, k)) { \ if (__hash_equal(mrb,h->keys[k], key)) { \ if (ret) *ret = 0; \ return k; \ } \ } \ else if (del_k == kh_end(h)) { \ del_k = k; \ } \ k = (k+(++step)) & khash_mask(h); \ } \ if (del_k != kh_end(h)) { \ /* put at del */ \ h->keys[del_k] = key; \ h->ed_flags[del_k/4] &= ~__m_del[del_k%4]; \ h->size++; \ if (ret) *ret = 2; \ return del_k; \ } \ else { \ /* put at empty */ \ h->keys[k] = key; \ h->ed_flags[k/4] &= ~__m_empty[k%4]; \ h->size++; \ if (ret) *ret = 1; \ return k; \ } \ } \ void kh_del_##name(mrb_state *mrb, kh_##name##_t *h, khint_t x) \ { \ (void)mrb; \ mrb_assert(x != h->n_buckets && !__ac_iseither(h->ed_flags, x)); \ h->ed_flags[x/4] |= __m_del[x%4]; \ h->size--; \ } \ kh_##name##_t *kh_copy_##name(mrb_state *mrb, kh_##name##_t *h) \ { \ kh_##name##_t *h2; \ khiter_t k, k2; \ \ h2 = kh_init_##name(mrb); \ for (k = kh_begin(h); k != kh_end(h); k++) { \ if (kh_exist(h, k)) { \ k2 = kh_put_##name(mrb, h2, kh_key(h, k), NULL); \ if (kh_is_map) kh_value(h2, k2) = kh_value(h, k); \ } \ } \ return h2; \ } #define khash_t(name) kh_##name##_t #define kh_init_size(name,mrb,size) kh_init_##name##_size(mrb,size) #define kh_init(name,mrb) kh_init_##name(mrb) #define kh_destroy(name, mrb, h) kh_destroy_##name(mrb, h) #define kh_clear(name, mrb, h) kh_clear_##name(mrb, h) #define kh_resize(name, mrb, h, s) kh_resize_##name(mrb, h, s) #define kh_put(name, mrb, h, k) kh_put_##name(mrb, h, k, NULL) #define kh_put2(name, mrb, h, k, r) kh_put_##name(mrb, h, k, r) #define kh_get(name, mrb, h, k) kh_get_##name(mrb, h, k) #define kh_del(name, mrb, h, k) kh_del_##name(mrb, h, k) #define kh_copy(name, mrb, h) kh_copy_##name(mrb, h) #define kh_exist(h, x) (!__ac_iseither((h)->ed_flags, (x))) #define kh_key(h, x) ((h)->keys[x]) #define kh_val(h, x) ((h)->vals[x]) #define kh_value(h, x) ((h)->vals[x]) #define kh_begin(h) (khint_t)(0) #define kh_end(h) ((h)->n_buckets) #define kh_size(h) ((h)->size) #define kh_n_buckets(h) ((h)->n_buckets) #define kh_int_hash_func(mrb,key) mrb_int_hash_func(mrb,key) #define kh_int_hash_equal(mrb,a, b) (a == b) #define kh_int64_hash_func(mrb,key) (khint_t)((key)>>33^(key)^(key)<<11) #define kh_int64_hash_equal(mrb,a, b) (a == b) static inline khint_t __ac_X31_hash_string(const char *s) { khint_t h = *s; if (h) for (++s; *s; ++s) h = (h << 5) - h + *s; return h; } #define kh_str_hash_func(mrb,key) __ac_X31_hash_string(key) #define kh_str_hash_equal(mrb,a, b) (strcmp(a, b) == 0) typedef const char *kh_cstr_t; MRB_END_DECL #endif /* MRUBY_KHASH_H */ nghttp2-1.68.0/third-party/mruby/include/mruby/PaxHeaders/value.h0000644000000000000000000000013215077107276021741 xustar0030 mtime=1761382078.103420643 30 atime=1761382080.124411398 30 ctime=1761382109.001300667 nghttp2-1.68.0/third-party/mruby/include/mruby/value.h0000644000175100017510000002674015077107276022342 0ustar00runnerrunner/** ** @file mruby/value.h - mruby value definitions ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_VALUE_H #define MRUBY_VALUE_H #include "common.h" /* * mruby Value definition functions and macros. */ MRB_BEGIN_DECL /** * mruby Symbol. * @class mrb_sym * * You can create an mrb_sym by simply using mrb_str_intern() or mrb_intern_cstr() */ typedef uint32_t mrb_sym; /** * mruby Boolean. * @class mrb_bool * * * Used internally to represent boolean. Can be TRUE or FALSE. * Not to be confused with Ruby's boolean classes, which can be * obtained using mrb_false_value() and mrb_true_value() */ #if defined(__cplusplus) || (defined(__bool_true_false_are_defined) && __bool_true_false_are_defined) typedef bool mrb_bool; # ifndef FALSE # define FALSE false # endif # ifndef TRUE # define TRUE true # endif #else # if __STDC_VERSION__ >= 199901L typedef _Bool mrb_bool; # else typedef uint8_t mrb_bool; # endif # ifndef FALSE # define FALSE 0 # endif # ifndef TRUE # define TRUE 1 # endif #endif struct mrb_state; #if defined _MSC_VER && _MSC_VER < 1800 # define PRIo64 "llo" # define PRId64 "lld" # define PRIu64 "llu" # define PRIx64 "llx" # define PRIo16 "ho" # define PRId16 "hd" # define PRIu16 "hu" # define PRIx16 "hx" # define PRIo32 "o" # define PRId32 "d" # define PRIu32 "u" # define PRIx32 "x" #else # include #endif #if defined(MRB_INT64) typedef int64_t mrb_int; typedef uint64_t mrb_uint; # define MRB_INT_BIT 64 # define MRB_INT_MIN INT64_MIN # define MRB_INT_MAX INT64_MAX # define MRB_PRIo PRIo64 # define MRB_PRId PRId64 # define MRB_PRIx PRIx64 #else typedef int32_t mrb_int; typedef uint32_t mrb_uint; # define MRB_INT_BIT 32 # define MRB_INT_MIN INT32_MIN # define MRB_INT_MAX INT32_MAX # define MRB_PRIo PRIo32 # define MRB_PRId PRId32 # define MRB_PRIx PRIx32 #endif #define MRB_FLAGS_MASK(shift, width) (~(~0U << (width)) << (shift)) #define MRB_FLAGS_GET(b, s, w) (((b) >> (s)) & MRB_FLAGS_MASK(0, w)) #define MRB_FLAGS_SET(b, s, w, n) ((b) = MRB_FLAGS_ZERO(b, s, w) | MRB_FLAGS_MAKE(s, w, n)) #define MRB_FLAGS_ZERO(b, s, w) ((b) & ~MRB_FLAGS_MASK(s, w)) #define MRB_FLAGS_MAKE(s, w, n) (((n) & MRB_FLAGS_MASK(0, w)) << (s)) #define MRB_FLAG_ON(b, s) ((b) |= MRB_FLAGS_MASK(s, 1)) #define MRB_FLAG_OFF(b, s) ((b) &= ~MRB_FLAGS_MASK(s, 1)) #define MRB_FLAG_CHECK(b, s) (!!((b) & MRB_FLAGS_MASK(s, 1))) MRB_API mrb_bool mrb_read_int(const char *p, const char *e, char **endp, mrb_int *np); /* obsolete; do not use mrb_int_read() */ MRB_API mrb_int mrb_int_read(const char*, const char*, char**); #ifndef MRB_NO_FLOAT MRB_API mrb_bool mrb_read_float(const char *p, char **endp, double *fp); /* obsolete; do not use mrb_float_read() */ MRB_API double mrb_float_read(const char *p, char **endp); #ifdef MRB_USE_FLOAT32 typedef float mrb_float; #else typedef double mrb_float; #endif #endif #if defined _MSC_VER && _MSC_VER < 1900 MRB_API int mrb_msvc_vsnprintf(char *s, size_t n, const char *format, va_list arg); MRB_API int mrb_msvc_snprintf(char *s, size_t n, const char *format, ...); # define vsnprintf(s, n, format, arg) mrb_msvc_vsnprintf(s, n, format, arg) # define snprintf(s, n, format, ...) mrb_msvc_snprintf(s, n, format, __VA_ARGS__) # if _MSC_VER < 1800 && !defined MRB_NO_FLOAT # define isfinite(n) _finite(n) # define isnan _isnan # define isinf(n) (!_finite(n) && !_isnan(n)) # define signbit(n) (_copysign(1.0, (n)) < 0.0) static const unsigned int IEEE754_INFINITY_BITS_SINGLE = 0x7F800000; # define INFINITY (*(float*)&IEEE754_INFINITY_BITS_SINGLE) # define NAN ((float)(INFINITY - INFINITY)) # endif #endif #define MRB_VTYPE_FOREACH(f) \ /* mrb_vtype */ /* c type */ /* ruby class */ \ f(MRB_TT_FALSE, void, "false") \ f(MRB_TT_TRUE, void, "true") \ f(MRB_TT_SYMBOL, void, "Symbol") \ f(MRB_TT_UNDEF, void, "undefined") \ f(MRB_TT_FREE, void, "free") \ f(MRB_TT_FLOAT, struct RFloat, "Float") \ f(MRB_TT_INTEGER, struct RInteger, "Integer") \ f(MRB_TT_CPTR, struct RCptr, "cptr") \ f(MRB_TT_OBJECT, struct RObject, "Object") \ f(MRB_TT_CLASS, struct RClass, "Class") \ f(MRB_TT_MODULE, struct RClass, "Module") \ f(MRB_TT_SCLASS, struct RClass, "SClass") \ f(MRB_TT_HASH, struct RHash, "Hash") \ f(MRB_TT_CDATA, struct RData, "C data") \ f(MRB_TT_EXCEPTION, struct RException, "Exception") \ f(MRB_TT_ICLASS, struct RClass, "iClass") \ f(MRB_TT_PROC, struct RProc, "Proc") \ f(MRB_TT_ARRAY, struct RArray, "Array") \ f(MRB_TT_STRING, struct RString, "String") \ f(MRB_TT_RANGE, struct RRange, "Range") \ f(MRB_TT_ENV, struct REnv, "env") \ f(MRB_TT_FIBER, struct RFiber, "Fiber") \ f(MRB_TT_STRUCT, struct RArray, "Struct") \ f(MRB_TT_ISTRUCT, struct RIStruct, "istruct") \ f(MRB_TT_BREAK, struct RBreak, "break") \ f(MRB_TT_COMPLEX, struct RComplex, "Complex") \ f(MRB_TT_RATIONAL, struct RRational, "Rational") \ f(MRB_TT_BIGINT, struct RBigint, "Integer") \ f(MRB_TT_BACKTRACE, struct RBacktrace, "backtrace") enum mrb_vtype { #define MRB_VTYPE_DEFINE(tt, type, name) tt, MRB_VTYPE_FOREACH(MRB_VTYPE_DEFINE) #undef MRB_VTYPE_DEFINE MRB_TT_MAXDEFINE }; /* obsolete name for MRB_TT_CDATA */ #define MRB_TT_DATA MRB_TT_CDATA #define MRB_VTYPE_TYPEOF(tt) MRB_TYPEOF_##tt #define MRB_VTYPE_TYPEDEF(tt, type, name) typedef type MRB_VTYPE_TYPEOF(tt); MRB_VTYPE_FOREACH(MRB_VTYPE_TYPEDEF) #undef MRB_VTYPE_TYPEDEF /* for compatibility */ #define MRB_TT_FIXNUM MRB_TT_INTEGER #include #ifdef MRB_DOCUMENTATION_BLOCK /** * @abstract * mruby value boxing. * * Actual implementation depends on configured boxing type. * * @see mruby/boxing_word.h Word boxing representation (Default) * @see mruby/boxing_no.h No boxing representation * @see mruby/boxing_nan.h Boxed double representation */ typedef void mrb_value; #endif #if defined(MRB_WORD_BOXING) || (defined(MRB_NAN_BOXING) && defined(MRB_64BIT)) struct RCptr { MRB_OBJECT_HEADER; void *p; }; #endif #if defined(MRB_NAN_BOXING) #include "boxing_nan.h" #elif defined(MRB_WORD_BOXING) #include "boxing_word.h" #else #include "boxing_no.h" #endif #if INTPTR_MAX < MRB_INT_MAX typedef intptr_t mrb_ssize; # define MRB_SSIZE_MAX INTPTR_MAX #else typedef mrb_int mrb_ssize; # define MRB_SSIZE_MAX MRB_INT_MAX #endif #ifndef mrb_immediate_p #define mrb_immediate_p(o) (mrb_type(o) <= MRB_TT_CPTR) #endif #ifndef mrb_integer_p #define mrb_integer_p(o) (mrb_type(o) == MRB_TT_INTEGER) #endif #ifndef mrb_fixnum_p #define mrb_fixnum_p(o) mrb_integer_p(o) #endif #ifndef mrb_symbol_p #define mrb_symbol_p(o) (mrb_type(o) == MRB_TT_SYMBOL) #endif #ifndef mrb_undef_p #define mrb_undef_p(o) (mrb_type(o) == MRB_TT_UNDEF) #endif #ifndef mrb_nil_p #define mrb_nil_p(o) (mrb_type(o) == MRB_TT_FALSE && !mrb_fixnum(o)) #endif #ifndef mrb_false_p #define mrb_false_p(o) (mrb_type(o) == MRB_TT_FALSE && !!mrb_fixnum(o)) #endif #ifndef mrb_true_p #define mrb_true_p(o) (mrb_type(o) == MRB_TT_TRUE) #endif #ifndef mrb_float_p #ifndef MRB_NO_FLOAT #define mrb_float_p(o) (mrb_type(o) == MRB_TT_FLOAT) #else #define mrb_float_p(o) FALSE #endif #endif #ifndef mrb_array_p #define mrb_array_p(o) (mrb_type(o) == MRB_TT_ARRAY) #endif #ifndef mrb_string_p #define mrb_string_p(o) (mrb_type(o) == MRB_TT_STRING) #endif #ifndef mrb_hash_p #define mrb_hash_p(o) (mrb_type(o) == MRB_TT_HASH) #endif #ifndef mrb_cptr_p #define mrb_cptr_p(o) (mrb_type(o) == MRB_TT_CPTR) #endif #ifndef mrb_exception_p #define mrb_exception_p(o) (mrb_type(o) == MRB_TT_EXCEPTION) #endif #ifndef mrb_free_p #define mrb_free_p(o) (mrb_type(o) == MRB_TT_FREE) #endif #ifndef mrb_object_p #define mrb_object_p(o) (mrb_type(o) == MRB_TT_OBJECT) #endif #ifndef mrb_class_p #define mrb_class_p(o) (mrb_type(o) == MRB_TT_CLASS) #endif #ifndef mrb_module_p #define mrb_module_p(o) (mrb_type(o) == MRB_TT_MODULE) #endif #ifndef mrb_iclass_p #define mrb_iclass_p(o) (mrb_type(o) == MRB_TT_ICLASS) #endif #ifndef mrb_sclass_p #define mrb_sclass_p(o) (mrb_type(o) == MRB_TT_SCLASS) #endif #ifndef mrb_proc_p #define mrb_proc_p(o) (mrb_type(o) == MRB_TT_PROC) #endif #ifndef mrb_range_p #define mrb_range_p(o) (mrb_type(o) == MRB_TT_RANGE) #endif #ifndef mrb_env_p #define mrb_env_p(o) (mrb_type(o) == MRB_TT_ENV) #endif #ifndef mrb_data_p #define mrb_data_p(o) (mrb_type(o) == MRB_TT_CDATA) #endif #ifndef mrb_fiber_p #define mrb_fiber_p(o) (mrb_type(o) == MRB_TT_FIBER) #endif #ifndef mrb_istruct_p #define mrb_istruct_p(o) (mrb_type(o) == MRB_TT_ISTRUCT) #endif #ifndef mrb_break_p #define mrb_break_p(o) (mrb_type(o) == MRB_TT_BREAK) #endif #ifndef mrb_bool #define mrb_bool(o) (mrb_type(o) != MRB_TT_FALSE) #endif #define mrb_test(o) mrb_bool(o) #ifndef mrb_bigint_p #define mrb_bigint_p(o) (mrb_type(o) == MRB_TT_BIGINT) #endif /** * Returns a float in Ruby. * * Takes a float and boxes it into an mrb_value */ #ifndef MRB_NO_FLOAT MRB_INLINE mrb_value mrb_float_value(struct mrb_state *mrb, mrb_float f) { mrb_value v; (void) mrb; SET_FLOAT_VALUE(mrb, v, f); return v; } #endif MRB_INLINE mrb_value mrb_cptr_value(struct mrb_state *mrb, void *p) { mrb_value v; (void) mrb; SET_CPTR_VALUE(mrb,v,p); return v; } /** * Returns an integer in Ruby. */ MRB_INLINE mrb_value mrb_int_value(struct mrb_state *mrb, mrb_int i) { mrb_value v; SET_INT_VALUE(mrb, v, i); return v; } MRB_INLINE mrb_value mrb_fixnum_value(mrb_int i) { mrb_value v; SET_FIXNUM_VALUE(v, i); return v; } MRB_INLINE mrb_value mrb_symbol_value(mrb_sym i) { mrb_value v; SET_SYM_VALUE(v, i); return v; } MRB_INLINE mrb_value mrb_obj_value(void *p) { mrb_value v; SET_OBJ_VALUE(v, (struct RBasic*)p); return v; } /** * Get a nil mrb_value object. * * @return * nil mrb_value object reference. */ MRB_INLINE mrb_value mrb_nil_value(void) { mrb_value v; SET_NIL_VALUE(v); return v; } /** * Returns false in Ruby. */ MRB_INLINE mrb_value mrb_false_value(void) { mrb_value v; SET_FALSE_VALUE(v); return v; } /** * Returns true in Ruby. */ MRB_INLINE mrb_value mrb_true_value(void) { mrb_value v; SET_TRUE_VALUE(v); return v; } MRB_INLINE mrb_value mrb_bool_value(mrb_bool boolean) { mrb_value v; SET_BOOL_VALUE(v, boolean); return v; } MRB_INLINE mrb_value mrb_undef_value(void) { mrb_value v; SET_UNDEF_VALUE(v); return v; } #if defined(MRB_USE_CUSTOM_RO_DATA_P) /* If you define `MRB_USE_CUSTOM_RO_DATA_P`, you must implement `mrb_ro_data_p()`. */ mrb_bool mrb_ro_data_p(const char *p); #elif !defined(MRB_NO_DEFAULT_RO_DATA_P) #if defined(MRB_USE_ETEXT_RO_DATA_P) #define MRB_LINK_TIME_RO_DATA_P extern char etext, edata; static inline mrb_bool mrb_ro_data_p(const char *p) { return &etext < p && p < &edata; } #elif defined(__APPLE__) #define MRB_LINK_TIME_RO_DATA_P #include #include // for _NSGetMachExecuteHeader static inline mrb_bool mrb_ro_data_p(const char *p) { #ifdef __LP64__ struct mach_header_64 *mhp; #else struct mach_header *mhp; #endif mhp = _NSGetMachExecuteHeader(); unsigned long textsize; char *text = (char*)getsegmentdata(mhp, SEG_TEXT, &textsize); return text <= p && p < text + textsize; } #endif /* Linux or macOS */ #endif /* MRB_NO_DEFAULT_RO_DATA_P */ #ifndef MRB_LINK_TIME_RO_DATA_P # define mrb_ro_data_p(p) FALSE #endif MRB_END_DECL #endif /* MRUBY_VALUE_H */ nghttp2-1.68.0/third-party/mruby/include/PaxHeaders/mruby.h0000644000000000000000000000013215077107276020625 xustar0030 mtime=1761382078.100420657 30 atime=1761382080.121411411 30 ctime=1761382109.016300624 nghttp2-1.68.0/third-party/mruby/include/mruby.h0000644000175100017510000015543615077107276021233 0ustar00runnerrunner/* ** mruby - An embeddable Ruby implementation ** ** Copyright (c) mruby developers 2010- ** ** Permission is hereby granted, free of charge, to any person obtaining ** a copy of this software and associated documentation files (the ** "Software"), to deal in the Software without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Software, and to ** permit persons to whom the Software is furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be ** included in all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ** ** [ MIT license: https://www.opensource.org/licenses/mit-license.php ] */ /** * @file mruby.h */ #ifndef MRUBY_H #define MRUBY_H #ifdef __cplusplus #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS #define __STDC_FORMAT_MACROS #endif #include #include #include #include #ifdef __cplusplus #ifndef UINTPTR_MAX #error Must be placed `#include ` before `#include ` #endif #ifndef SIZE_MAX #ifdef __SIZE_MAX__ #define SIZE_MAX __SIZE_MAX__ #else #define SIZE_MAX std::numeric_limits::max() #endif #endif #endif #ifdef _MSC_VER # define __func__ __FUNCTION__ #endif #ifdef MRB_DEBUG #include #define mrb_assert(p) assert(p) #define mrb_assert_int_fit(t1,n,t2,max) assert((n)>=0 && ((sizeof(n)<=sizeof(t2))||(n<=(t1)(max)))) #else #define mrb_assert(p) ((void)0) #define mrb_assert_int_fit(t1,n,t2,max) ((void)0) #endif #if (defined __cplusplus && __cplusplus >= 201703L) # define mrb_static_assert(...) static_assert(__VA_ARGS__) # define mrb_static_assert1(exp) static_assert(exp) # define mrb_static_assert2(exp, str) static_assert(exp, str) #elif (defined __cplusplus && __cplusplus >= 201103L) || \ (defined _MSC_VER) || \ (defined __GXX_EXPERIMENTAL_CXX0X__) /* for old G++/Clang++ */ # define mrb_static_assert2(exp, str) static_assert(exp, str) #elif defined __STDC_VERSION__ && \ ((__STDC_VERSION__ >= 201112L) || \ (defined __GNUC__ && __GNUC__ * 100 + __GNUC_MINOR__ >= 406)) # define mrb_static_assert2(exp, str) _Static_assert(exp, str) #else # /* alternative implementation of static_assert() */ # define _mrb_static_assert_cat0(a, b) a##b # define _mrb_static_assert_cat(a, b) _mrb_static_assert_cat0(a, b) # ifdef __COUNTER__ # define _mrb_static_assert_id(prefix) _mrb_static_assert_cat(prefix, __COUNTER__) # else # define _mrb_static_assert_id(prefix) _mrb_static_assert_cat(prefix, __LINE__) # endif # define mrb_static_assert2(exp, str) \ struct _mrb_static_assert_id(_mrb_static_assert_) { char x[(exp) ? 1 : -1]; } #endif #ifndef mrb_static_assert # define mrb_static_assert1(exp) mrb_static_assert2(exp, #exp) # define mrb_static_assert_expand(...) __VA_ARGS__ /* for MSVC behaviour - https://stackoverflow.com/q/5530505 */ # define mrb_static_assert_selector(a, b, name, ...) name /** * The `mrb_static_assert()` macro function takes one or two arguments. * * !!!c * mrb_static_assert(expect_condition); * mrb_static_assert(expect_condition, error_message); */ # define mrb_static_assert(...) \ mrb_static_assert_expand(mrb_static_assert_selector(__VA_ARGS__, mrb_static_assert2, mrb_static_assert1, _)(__VA_ARGS__)) #endif #define mrb_static_assert_powerof2(num) mrb_static_assert((num) > 0 && (num) == ((num) & -(num)), "need power of 2 for " #num) #include "mrbconf.h" #include #include #include #include #ifndef MRB_NO_FLOAT #include #include #ifndef FLT_EPSILON #define FLT_EPSILON (1.19209290e-07f) #endif #ifndef DBL_EPSILON #define DBL_EPSILON ((double)2.22044604925031308085e-16L) #endif #ifndef LDBL_EPSILON #define LDBL_EPSILON (1.08420217248550443401e-19L) #endif #ifdef MRB_USE_FLOAT32 #define MRB_FLOAT_EPSILON FLT_EPSILON #else #define MRB_FLOAT_EPSILON DBL_EPSILON #endif #endif /** * mruby C API entry point */ MRB_BEGIN_DECL typedef uint8_t mrb_code; /** * \class mrb_aspec * * Specifies the number of arguments a function takes * * Example: `MRB_ARGS_REQ(2) | MRB_ARGS_OPT(1)` for a method that expects 2..3 arguments */ typedef uint32_t mrb_aspec; typedef struct mrb_irep mrb_irep; struct mrb_state; #ifndef MRB_FIXED_STATE_ATEXIT_STACK_SIZE #define MRB_FIXED_STATE_ATEXIT_STACK_SIZE 5 #endif typedef struct { uint8_t n:4; /* (15=*) c=n|nk<<4 */ uint8_t nk:4; /* (15=*) */ uint8_t cci; /* called from C function */ uint8_t vis; /* 5(ZERO):1(separate module):2(method visibility) */ /* under 3-bit flags are copied to env, and after that, env takes precedence */ mrb_sym mid; const struct RProc *proc; struct RProc *blk; mrb_value *stack; const mrb_code *pc; /* current address on iseq of this proc */ union { struct REnv *env; struct RClass *target_class; const void *keep_context; /* if NULL, it means that the fiber has switched; for internal use */ } u; } mrb_callinfo; enum mrb_fiber_state { MRB_FIBER_CREATED = 0, MRB_FIBER_RUNNING, MRB_FIBER_RESUMED, MRB_FIBER_SUSPENDED, MRB_FIBER_TRANSFERRED, MRB_FIBER_TERMINATED, }; struct mrb_context { struct mrb_context *prev; mrb_value *stbase, *stend; /* stack of virtual machine */ mrb_callinfo *ci; mrb_callinfo *cibase, *ciend; enum mrb_fiber_state status : 4; mrb_bool vmexec : 1; struct RFiber *fib; }; #ifdef MRB_METHOD_CACHE_SIZE # undef MRB_NO_METHOD_CACHE mrb_static_assert_powerof2(MRB_METHOD_CACHE_SIZE); #else /* default method cache size: 256 */ /* cache size needs to be power of 2 */ # define MRB_METHOD_CACHE_SIZE (1<<8) #endif /** * Function pointer type for a function callable by mruby. * * The arguments to the function are stored on the mrb_state. To get them see mrb_get_args * * @param mrb The mruby state * @param self The self object * @return [mrb_value] The function's return value */ typedef mrb_value (*mrb_func_t)(struct mrb_state *mrb, mrb_value self); typedef struct { uint32_t flags; /* compatible with mt keys in class.c */ union { const struct RProc *proc; mrb_func_t func; } as; } mrb_method_t; #ifndef MRB_NO_METHOD_CACHE struct mrb_cache_entry { struct RClass *c, *c0; /* mrb_sym mid; // mid is stored in mrb_method_t::flags */ mrb_method_t m; }; #endif struct mrb_jmpbuf; typedef void (*mrb_atexit_func)(struct mrb_state*); typedef struct mrb_state { struct mrb_jmpbuf *jmp; struct mrb_context *c; struct mrb_context *root_c; struct iv_tbl *globals; /* global variable table */ struct RObject *exc; /* exception */ struct RObject *top_self; struct RClass *object_class; /* Object class */ struct RClass *class_class; struct RClass *module_class; struct RClass *proc_class; struct RClass *string_class; struct RClass *array_class; struct RClass *hash_class; struct RClass *range_class; #ifndef MRB_NO_FLOAT struct RClass *float_class; #endif struct RClass *integer_class; struct RClass *true_class; struct RClass *false_class; struct RClass *nil_class; struct RClass *symbol_class; struct RClass *kernel_module; mrb_gc gc; #ifndef MRB_NO_METHOD_CACHE struct mrb_cache_entry cache[MRB_METHOD_CACHE_SIZE]; #endif mrb_sym symidx; const char **symtbl; uint8_t *symlink; uint8_t *symflags; mrb_sym symhash[256]; size_t symcapa; #ifndef MRB_USE_ALL_SYMBOLS char symbuf[8]; /* buffer for small symbol names */ #endif #ifdef MRB_USE_DEBUG_HOOK void (*code_fetch_hook)(struct mrb_state* mrb, const struct mrb_irep *irep, const mrb_code *pc, mrb_value *regs); void (*debug_op_hook)(struct mrb_state* mrb, const struct mrb_irep *irep, const mrb_code *pc, mrb_value *regs); #endif #ifdef MRB_BYTECODE_DECODE_OPTION mrb_code (*bytecode_decoder)(struct mrb_state* mrb, mrb_code code); #endif struct RClass *eException_class; struct RClass *eStandardError_class; struct RObject *nomem_err; /* pre-allocated NoMemoryError */ struct RObject *stack_err; /* pre-allocated SystemStackError */ #ifdef MRB_GC_FIXED_ARENA struct RObject *arena_err; /* pre-allocated arena overflow error */ #endif void *ud; /* auxiliary data */ #ifdef MRB_FIXED_STATE_ATEXIT_STACK mrb_atexit_func atexit_stack[MRB_FIXED_STATE_ATEXIT_STACK_SIZE]; #else mrb_atexit_func *atexit_stack; #endif uint16_t atexit_stack_len; } mrb_state; /** * Defines a new class. * * If you're creating a gem it may look something like this: * * !!!c * void mrb_example_gem_init(mrb_state* mrb) { * struct RClass *example_class; * example_class = mrb_define_class(mrb, "Example_Class", mrb->object_class); * } * * void mrb_example_gem_final(mrb_state* mrb) { * //free(TheAnimals); * } * * @param mrb The current mruby state. * @param name The name of the defined class. * @param super The new class parent. * @return [struct RClass *] Reference to the newly defined class. * @see mrb_define_class_under */ MRB_API struct RClass *mrb_define_class(mrb_state *mrb, const char *name, struct RClass *super); MRB_API struct RClass *mrb_define_class_id(mrb_state *mrb, mrb_sym name, struct RClass *super); /** * Defines a new module. * * @param mrb The current mruby state. * @param name The name of the module. * @return [struct RClass *] Reference to the newly defined module. */ MRB_API struct RClass *mrb_define_module(mrb_state *mrb, const char *name); MRB_API struct RClass *mrb_define_module_id(mrb_state *mrb, mrb_sym name); /** * Returns the singleton class of an object. * * Raises a `TypeError` exception for immediate values. */ MRB_API mrb_value mrb_singleton_class(mrb_state *mrb, mrb_value val); /** * Returns the singleton class of an object. * * Returns `NULL` for immediate values, */ MRB_API struct RClass *mrb_singleton_class_ptr(mrb_state *mrb, mrb_value val); /** * Include a module in another class or module. * Equivalent to: * * module B * include A * end * @param mrb The current mruby state. * @param cla A reference to module or a class. * @param included A reference to the module to be included. */ MRB_API void mrb_include_module(mrb_state *mrb, struct RClass *cla, struct RClass *included); /** * Prepends a module in another class or module. * * Equivalent to: * module B * prepend A * end * @param mrb The current mruby state. * @param cla A reference to module or a class. * @param prepended A reference to the module to be prepended. */ MRB_API void mrb_prepend_module(mrb_state *mrb, struct RClass *cla, struct RClass *prepended); /** * Defines a global function in ruby. * * If you're creating a gem it may look something like this * * Example: * * mrb_value example_method(mrb_state* mrb, mrb_value self) * { * puts("Executing example command!"); * return self; * } * * void mrb_example_gem_init(mrb_state* mrb) * { * mrb_define_method(mrb, mrb->kernel_module, "example_method", example_method, MRB_ARGS_NONE()); * } * * @param mrb The mruby state reference. * @param cla The class pointer where the method will be defined. * @param name The name of the method being defined. * @param func The function pointer to the method definition. * @param aspec The method parameters declaration. */ MRB_API void mrb_define_method(mrb_state *mrb, struct RClass *cla, const char *name, mrb_func_t func, mrb_aspec aspec); MRB_API void mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec); MRB_API void mrb_define_private_method(mrb_state *mrb, struct RClass *cla, const char *name, mrb_func_t func, mrb_aspec aspec); MRB_API void mrb_define_private_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec); /** * Defines a class method. * * Example: * * # Ruby style * class Foo * def Foo.bar * end * end * // C style * mrb_value bar_method(mrb_state* mrb, mrb_value self){ * return mrb_nil_value(); * } * void mrb_example_gem_init(mrb_state* mrb){ * struct RClass *foo; * foo = mrb_define_class(mrb, "Foo", mrb->object_class); * mrb_define_class_method(mrb, foo, "bar", bar_method, MRB_ARGS_NONE()); * } * @param mrb The mruby state reference. * @param cla The class where the class method will be defined. * @param name The name of the class method being defined. * @param fun The function pointer to the class method definition. * @param aspec The method parameters declaration. */ MRB_API void mrb_define_class_method(mrb_state *mrb, struct RClass *cla, const char *name, mrb_func_t fun, mrb_aspec aspec); MRB_API void mrb_define_class_method_id(mrb_state *mrb, struct RClass *cla, mrb_sym name, mrb_func_t fun, mrb_aspec aspec); /** * Defines a singleton method * * @see mrb_define_class_method */ MRB_API void mrb_define_singleton_method(mrb_state *mrb, struct RObject *cla, const char *name, mrb_func_t fun, mrb_aspec aspec); MRB_API void mrb_define_singleton_method_id(mrb_state *mrb, struct RObject *cla, mrb_sym name, mrb_func_t fun, mrb_aspec aspec); /** * Defines a module function. * * Example: * * # Ruby style * module Foo * def Foo.bar * end * end * // C style * mrb_value bar_method(mrb_state* mrb, mrb_value self){ * return mrb_nil_value(); * } * void mrb_example_gem_init(mrb_state* mrb){ * struct RClass *foo; * foo = mrb_define_module(mrb, "Foo"); * mrb_define_module_function(mrb, foo, "bar", bar_method, MRB_ARGS_NONE()); * } * @param mrb The mruby state reference. * @param cla The module where the module function will be defined. * @param name The name of the module function being defined. * @param fun The function pointer to the module function definition. * @param aspec The method parameters declaration. */ MRB_API void mrb_define_module_function(mrb_state *mrb, struct RClass *cla, const char *name, mrb_func_t fun, mrb_aspec aspec); MRB_API void mrb_define_module_function_id(mrb_state *mrb, struct RClass *cla, mrb_sym name, mrb_func_t fun, mrb_aspec aspec); /** * Defines a constant. * * Example: * * # Ruby style * class ExampleClass * AGE = 22 * end * // C style * #include * #include * * void * mrb_example_gem_init(mrb_state* mrb){ * mrb_define_const(mrb, mrb->kernel_module, "AGE", mrb_fixnum_value(22)); * } * * mrb_value * mrb_example_gem_final(mrb_state* mrb){ * } * @param mrb The mruby state reference. * @param cla A class or module the constant is defined in. * @param name The name of the constant being defined. * @param val The value for the constant. */ MRB_API void mrb_define_const(mrb_state* mrb, struct RClass* cla, const char *name, mrb_value val); MRB_API void mrb_define_const_id(mrb_state* mrb, struct RClass* cla, mrb_sym name, mrb_value val); /** * Undefines a method. * * Example: * * # Ruby style * * class ExampleClassA * def example_method * "example" * end * end * ExampleClassA.new.example_method # => example * * class ExampleClassB < ExampleClassA * undef_method :example_method * end * * ExampleClassB.new.example_method # => undefined method 'example_method' for ExampleClassB (NoMethodError) * * // C style * #include * #include * * mrb_value * mrb_example_method(mrb_state *mrb){ * return mrb_str_new_lit(mrb, "example"); * } * * void * mrb_example_gem_init(mrb_state* mrb){ * struct RClass *example_class_a; * struct RClass *example_class_b; * struct RClass *example_class_c; * * example_class_a = mrb_define_class(mrb, "ExampleClassA", mrb->object_class); * mrb_define_method(mrb, example_class_a, "example_method", mrb_example_method, MRB_ARGS_NONE()); * example_class_b = mrb_define_class(mrb, "ExampleClassB", example_class_a); * example_class_c = mrb_define_class(mrb, "ExampleClassC", example_class_b); * mrb_undef_method(mrb, example_class_c, "example_method"); * } * * mrb_example_gem_final(mrb_state* mrb){ * } * @param mrb The mruby state reference. * @param cla The class the method will be undefined from. * @param name The name of the method to be undefined. */ MRB_API void mrb_undef_method(mrb_state *mrb, struct RClass *cla, const char *name); MRB_API void mrb_undef_method_id(mrb_state*, struct RClass*, mrb_sym); /** * Undefine a class method. * Example: * * # Ruby style * class ExampleClass * def self.example_method * "example" * end * end * * ExampleClass.example_method * * // C style * #include * #include * * mrb_value * mrb_example_method(mrb_state *mrb){ * return mrb_str_new_lit(mrb, "example"); * } * * void * mrb_example_gem_init(mrb_state* mrb){ * struct RClass *example_class; * example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class); * mrb_define_class_method(mrb, example_class, "example_method", mrb_example_method, MRB_ARGS_NONE()); * mrb_undef_class_method(mrb, example_class, "example_method"); * } * * void * mrb_example_gem_final(mrb_state* mrb){ * } * @param mrb The mruby state reference. * @param cls A class the class method will be undefined from. * @param name The name of the class method to be undefined. */ MRB_API void mrb_undef_class_method(mrb_state *mrb, struct RClass *cls, const char *name); MRB_API void mrb_undef_class_method_id(mrb_state *mrb, struct RClass *cls, mrb_sym name); /** * Initialize a new object instance of c class. * * Example: * * # Ruby style * class ExampleClass * end * * p ExampleClass # => # * // C style * #include * #include * * void * mrb_example_gem_init(mrb_state* mrb) { * struct RClass *example_class; * mrb_value obj; * example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class); # => class ExampleClass; end * obj = mrb_obj_new(mrb, example_class, 0, NULL); # => ExampleClass.new * mrb_p(mrb, obj); // => Kernel#p * } * @param mrb The current mruby state. * @param c Reference to the class of the new object. * @param argc Number of arguments in argv * @param argv Array of mrb_value to initialize the object * @return [mrb_value] The newly initialized object */ MRB_API mrb_value mrb_obj_new(mrb_state *mrb, struct RClass *c, mrb_int argc, const mrb_value *argv); /** @see mrb_obj_new */ MRB_INLINE mrb_value mrb_class_new_instance(mrb_state *mrb, mrb_int argc, const mrb_value *argv, struct RClass *c) { return mrb_obj_new(mrb,c,argc,argv); } /** * Creates a new instance of Class, Class. * * Example: * * void * mrb_example_gem_init(mrb_state* mrb) { * struct RClass *example_class; * * mrb_value obj; * example_class = mrb_class_new(mrb, mrb->object_class); * obj = mrb_obj_new(mrb, example_class, 0, NULL); // => #<#:0x9a94588> * mrb_p(mrb, obj); // => Kernel#p * } * * @param mrb The current mruby state. * @param super The super class or parent. * @return [struct RClass *] Reference to the new class. */ MRB_API struct RClass * mrb_class_new(mrb_state *mrb, struct RClass *super); /** * Creates a new module, Module. * * Example: * void * mrb_example_gem_init(mrb_state* mrb) { * struct RClass *example_module; * * example_module = mrb_module_new(mrb); * } * * @param mrb The current mruby state. * @return [struct RClass *] Reference to the new module. */ MRB_API struct RClass * mrb_module_new(mrb_state *mrb); /** * Returns an mrb_bool. True if class was defined, and false if the class was not defined. * * Example: * void * mrb_example_gem_init(mrb_state* mrb) { * struct RClass *example_class; * mrb_bool cd; * * example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class); * cd = mrb_class_defined(mrb, "ExampleClass"); * * // If mrb_class_defined returns TRUE then puts "True" * // If mrb_class_defined returns FALSE then puts "False" * if (cd) { * puts("True"); * } * else { * puts("False"); * } * } * * @param mrb The current mruby state. * @param name A string representing the name of the class. * @return [mrb_bool] A boolean value. */ MRB_API mrb_bool mrb_class_defined(mrb_state *mrb, const char *name); MRB_API mrb_bool mrb_class_defined_id(mrb_state *mrb, mrb_sym name); /** * Gets a class. * @param mrb The current mruby state. * @param name The name of the class. * @return [struct RClass *] A reference to the class. */ MRB_API struct RClass* mrb_class_get(mrb_state *mrb, const char *name); MRB_API struct RClass* mrb_class_get_id(mrb_state *mrb, mrb_sym name); /** * Gets a exception class. * @param mrb The current mruby state. * @param name The name of the class. * @return [struct RClass *] A reference to the class. */ MRB_API struct RClass* mrb_exc_get_id(mrb_state *mrb, mrb_sym name); #define mrb_exc_get(mrb, name) mrb_exc_get_id(mrb, mrb_intern_cstr(mrb, name)) /** * Returns an mrb_bool. True if inner class was defined, and false if the inner class was not defined. * * Example: * void * mrb_example_gem_init(mrb_state* mrb) { * struct RClass *example_outer, *example_inner; * mrb_bool cd; * * example_outer = mrb_define_module(mrb, "ExampleOuter"); * * example_inner = mrb_define_class_under(mrb, example_outer, "ExampleInner", mrb->object_class); * cd = mrb_class_defined_under(mrb, example_outer, "ExampleInner"); * * // If mrb_class_defined_under returns TRUE then puts "True" * // If mrb_class_defined_under returns FALSE then puts "False" * if (cd) { * puts("True"); * } * else { * puts("False"); * } * } * * @param mrb The current mruby state. * @param outer The name of the outer class. * @param name A string representing the name of the inner class. * @return [mrb_bool] A boolean value. */ MRB_API mrb_bool mrb_class_defined_under(mrb_state *mrb, struct RClass *outer, const char *name); MRB_API mrb_bool mrb_class_defined_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name); /** * Gets a child class. * @param mrb The current mruby state. * @param outer The name of the parent class. * @param name The name of the class. * @return [struct RClass *] A reference to the class. */ MRB_API struct RClass * mrb_class_get_under(mrb_state *mrb, struct RClass *outer, const char *name); MRB_API struct RClass * mrb_class_get_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name); /** * Gets a module. * @param mrb The current mruby state. * @param name The name of the module. * @return [struct RClass *] A reference to the module. */ MRB_API struct RClass * mrb_module_get(mrb_state *mrb, const char *name); MRB_API struct RClass * mrb_module_get_id(mrb_state *mrb, mrb_sym name); /** * Gets a module defined under another module. * @param mrb The current mruby state. * @param outer The name of the outer module. * @param name The name of the module. * @return [struct RClass *] A reference to the module. */ MRB_API struct RClass * mrb_module_get_under(mrb_state *mrb, struct RClass *outer, const char *name); MRB_API struct RClass * mrb_module_get_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name); /* a function to raise NotImplementedError with current method name */ MRB_API void mrb_notimplement(mrb_state*); /* a function to be replacement of unimplemented method */ MRB_API mrb_value mrb_notimplement_m(mrb_state*, mrb_value); /* just return it self */ MRB_API mrb_value mrb_obj_itself(mrb_state*, mrb_value); /** * Duplicate an object. * * Equivalent to: * Object#dup * @param mrb The current mruby state. * @param obj Object to be duplicate. * @return [mrb_value] The newly duplicated object. */ MRB_API mrb_value mrb_obj_dup(mrb_state *mrb, mrb_value obj); /** * Returns true if obj responds to the given method. If the method was defined for that * class it returns true, it returns false otherwise. * * Example: * # Ruby style * class ExampleClass * def example_method * end * end * * ExampleClass.new.respond_to?(:example_method) # => true * * // C style * void * mrb_example_gem_init(mrb_state* mrb) { * struct RClass *example_class; * mrb_sym mid; * mrb_bool obj_resp; * * example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class); * mrb_define_method(mrb, example_class, "example_method", exampleMethod, MRB_ARGS_NONE()); * mid = mrb_intern_str(mrb, mrb_str_new_lit(mrb, "example_method" )); * obj_resp = mrb_obj_respond_to(mrb, example_class, mid); // => TRUE (true in Ruby world) * * // If mrb_obj_respond_to returns TRUE then puts "True" * // If mrb_obj_respond_to returns FALSE then puts "False" * if (obj_resp) { * puts("True"); * } * else { * puts("False"); * } * } * * @param mrb The current mruby state. * @param c A reference to a class. * @param mid A symbol referencing a method id. * @return [mrb_bool] A boolean value. */ MRB_API mrb_bool mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid); /** * Defines a new class under a given module * * @param mrb The current mruby state. * @param outer Reference to the module under which the new class will be defined * @param name The name of the defined class * @param super The new class parent * @return [struct RClass *] Reference to the newly defined class * @see mrb_define_class */ MRB_API struct RClass* mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super); MRB_API struct RClass* mrb_define_class_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name, struct RClass *super); MRB_API struct RClass* mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name); MRB_API struct RClass* mrb_define_module_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name); /** * Function requires n arguments. * * @param n * The number of required arguments. */ #define MRB_ARGS_REQ(n) ((mrb_aspec)((n)&0x1f) << 18) /** * Function takes n optional arguments * * @param n * The number of optional arguments. */ #define MRB_ARGS_OPT(n) ((mrb_aspec)((n)&0x1f) << 13) /** * Function takes n1 mandatory arguments and n2 optional arguments * * @param n1 * The number of required arguments. * @param n2 * The number of optional arguments. */ #define MRB_ARGS_ARG(n1,n2) (MRB_ARGS_REQ(n1)|MRB_ARGS_OPT(n2)) /** rest argument */ #define MRB_ARGS_REST() ((mrb_aspec)(1 << 12)) /** required arguments after rest */ #define MRB_ARGS_POST(n) ((mrb_aspec)((n)&0x1f) << 7) /** keyword arguments (n of keys, kdict) */ #define MRB_ARGS_KEY(n1,n2) ((mrb_aspec)((((n1)&0x1f) << 2) | ((n2)?(1<<1):0))) /** * Function takes a block argument */ #define MRB_ARGS_BLOCK() ((mrb_aspec)1) /** * Function accepts any number of arguments */ #define MRB_ARGS_ANY() MRB_ARGS_REST() /** * Function accepts no arguments */ #define MRB_ARGS_NONE() ((mrb_aspec)0) /** * Format specifiers for {mrb_get_args} function * * Must be a C string composed of the following format specifiers: * * | char | Ruby type | C types | Notes | * |:----:|----------------|-------------------|----------------------------------------------------| * | `o` | {Object} | {mrb_value} | Could be used to retrieve any type of argument | * | `C` | {Class}/{Module} | {mrb_value} | when `!` follows, the value may be `nil` | * | `S` | {String} | {mrb_value} | when `!` follows, the value may be `nil` | * | `A` | {Array} | {mrb_value} | when `!` follows, the value may be `nil` | * | `H` | {Hash} | {mrb_value} | when `!` follows, the value may be `nil` | * | `s` | {String} | const char *, {mrb_int} | Receive two arguments; `s!` gives (`NULL`,`0`) for `nil` | * | `z` | {String} | const char * | `NULL` terminated string; `z!` gives `NULL` for `nil` | * | `a` | {Array} | const {mrb_value} *, {mrb_int} | Receive two arguments; `a!` gives (`NULL`,`0`) for `nil` | * | `c` | {Class}/{Module} | strcut RClass * | `c!` gives `NULL` for `nil` | * | `f` | {Integer}/{Float} | {mrb_float} | | * | `i` | {Integer}/{Float} | {mrb_int} | | * | `b` | boolean | {mrb_bool} | | * | `n` | {String}/{Symbol} | {mrb_sym} | | * | `d` | data | void *, {mrb_data_type} const | 2nd argument will be used to check data type so it won't be modified; when `!` follows, the value may be `nil` | * | `I` | inline struct | void *, struct RClass | `I!` gives `NULL` for `nil` | * | `&` | block | {mrb_value} | &! raises exception if no block given. | * | `*` | rest arguments | const {mrb_value} *, {mrb_int} | Receive the rest of arguments as an array; `*!` avoid copy of the stack. | * | \| | optional | | After this spec following specs would be optional. | * | `?` | optional given | {mrb_bool} | `TRUE` if preceding argument is given. Used to check optional argument is given. | * | `:` | keyword args | {mrb_kwargs} const | Get keyword arguments. @see mrb_kwargs | * * @see mrb_get_args * * Immediately after format specifiers it can add format modifiers: * * | char | Notes | * |:----:|-----------------------------------------------------------------------------------------| * | `!` | Switch to the alternate mode; The behaviour changes depending on the format specifier | * | `+` | Request a not frozen object; However, except nil value | */ typedef const char *mrb_args_format; /** * Get keyword arguments by `mrb_get_args()` with `:` specifier. * * `mrb_kwargs::num` indicates that the total number of keyword values. * * `mrb_kwargs::required` indicates that the specified number of keywords starting from the beginning of the `mrb_sym` array are required. * * `mrb_kwargs::table` accepts a `mrb_sym` array of C. * * `mrb_kwargs::values` is an object array of C, and the keyword argument corresponding to the `mrb_sym` array is assigned. * Note that `undef` is assigned if there is no keyword argument corresponding over `mrb_kwargs::required` to `mrb_kwargs::num`. * * `mrb_kwargs::rest` is the remaining keyword argument that can be accepted as `**rest` in Ruby. * If `NULL` is specified, `ArgumentError` is raised when there is an undefined keyword. * * Examples: * * // def method(a: 1, b: 2) * * mrb_int kw_num = 2; * mrb_int kw_required = 0; * mrb_sym kw_names[] = { mrb_intern_lit(mrb, "a"), mrb_intern_lit(mrb, "b") }; * mrb_value kw_values[kw_num]; * mrb_kwargs kwargs = { kw_num, kw_required, kw_names, kw_values, NULL }; * * mrb_get_args(mrb, ":", &kwargs); * if (mrb_undef_p(kw_values[0])) { kw_values[0] = mrb_fixnum_value(1); } * if (mrb_undef_p(kw_values[1])) { kw_values[1] = mrb_fixnum_value(2); } * * * // def method(str, x:, y: 2, z: "default string", **opts) * * mrb_value str, kw_rest; * uint32_t kw_num = 3; * uint32_t kw_required = 1; * // Note that `#include ` is required beforehand because `MRB_SYM()` is used. * // If the usage of `MRB_SYM()` is not desired, replace it with `mrb_intern_lit()`. * mrb_sym kw_names[] = { MRB_SYM(x), MRB_SYM(y), MRB_SYM(z) }; * mrb_value kw_values[kw_num]; * mrb_kwargs kwargs = { kw_num, kw_required, kw_names, kw_values, &kw_rest }; * * mrb_get_args(mrb, "S:", &str, &kwargs); * // or: mrb_get_args(mrb, ":S", &kwargs, &str); * if (mrb_undef_p(kw_values[1])) { kw_values[1] = mrb_fixnum_value(2); } * if (mrb_undef_p(kw_values[2])) { kw_values[2] = mrb_str_new_cstr(mrb, "default string"); } */ typedef struct mrb_kwargs mrb_kwargs; struct mrb_kwargs { mrb_int num; /* number of keyword arguments */ mrb_int required; /* number of required keyword arguments */ const mrb_sym *table; /* C array of symbols for keyword names */ mrb_value *values; /* keyword argument values */ mrb_value *rest; /* keyword rest (dict) */ }; /** * Retrieve arguments from mrb_state. * * @param mrb The current mruby state. * @param format is a list of format specifiers * @param ... The passing variadic arguments must be a pointer of retrieving type. * @return the number of arguments retrieved. * @see mrb_args_format * @see mrb_kwargs */ MRB_API mrb_int mrb_get_args(mrb_state *mrb, mrb_args_format format, ...); /** * Array version of mrb_get_args() * * @param ptr Array of void*, in the same order as the varargs version. */ MRB_API mrb_int mrb_get_args_a(mrb_state *mrb, mrb_args_format format, void** ptr); MRB_INLINE mrb_sym mrb_get_mid(mrb_state *mrb) /* get method symbol */ { return mrb->c->ci->mid; } /** * Retrieve number of arguments from mrb_state. * * Correctly handles *splat arguments. */ MRB_API mrb_int mrb_get_argc(mrb_state *mrb); /** * Retrieve an array of arguments from mrb_state. * * Correctly handles *splat arguments. */ MRB_API const mrb_value *mrb_get_argv(mrb_state *mrb); /** * Retrieve the first and only argument from mrb_state. * Raises ArgumentError unless the number of arguments is exactly one. * * Correctly handles *splat arguments. */ MRB_API mrb_value mrb_get_arg1(mrb_state *mrb); /** * Check if a block argument is given from mrb_state. */ MRB_API mrb_bool mrb_block_given_p(mrb_state *mrb); /* `strlen` for character string literals (use with caution or `strlen` instead) Adjacent string literals are concatenated in C/C++ in translation phase 6. If `lit` is not one, the compiler will report a syntax error: MSVC: "error C2143: syntax error : missing ')' before 'string'" GCC: "error: expected ')' before string constant" */ #define mrb_strlen_lit(lit) (sizeof(lit "") - 1) /** * Call existing ruby functions. * * Example: * * #include * #include * #include * * int * main() * { * mrb_int i = 99; * mrb_state *mrb = mrb_open(); * * if (!mrb) { } * FILE *fp = fopen("test.rb","r"); * mrb_value obj = mrb_load_file(mrb,fp); * mrb_funcall(mrb, obj, "method_name", 1, mrb_fixnum_value(i)); * mrb_funcall_id(mrb, obj, MRB_SYM(method_name), 1, mrb_fixnum_value(i)); * fclose(fp); * mrb_close(mrb); * } * * @param mrb The current mruby state. * @param val A reference to an mruby value. * @param name The name of the method. * @param argc The number of arguments the method has. * @param ... Variadic values(not type safe!). * @return [mrb_value] mruby function value. */ MRB_API mrb_value mrb_funcall(mrb_state *mrb, mrb_value val, const char *name, mrb_int argc, ...); MRB_API mrb_value mrb_funcall_id(mrb_state *mrb, mrb_value val, mrb_sym mid, mrb_int argc, ...); /** * Call existing ruby functions. This is basically the type safe version of mrb_funcall. * * #include * #include * #include * int * main() * { * mrb_state *mrb = mrb_open(); * mrb_value obj = mrb_fixnum_value(1); * * if (!mrb) { } * * FILE *fp = fopen("test.rb","r"); * mrb_value obj = mrb_load_file(mrb,fp); * mrb_funcall_argv(mrb, obj, MRB_SYM(method_name), 1, &obj); // Calling ruby function from test.rb. * fclose(fp); * mrb_close(mrb); * } * @param mrb The current mruby state. * @param val A reference to an mruby value. * @param name_sym The symbol representing the method. * @param argc The number of arguments the method has. * @param obj Pointer to the object. * @return [mrb_value] mrb_value mruby function value. * @see mrb_funcall */ MRB_API mrb_value mrb_funcall_argv(mrb_state *mrb, mrb_value val, mrb_sym name, mrb_int argc, const mrb_value *argv); /** * Call existing ruby functions with a block. */ MRB_API mrb_value mrb_funcall_with_block(mrb_state *mrb, mrb_value val, mrb_sym name, mrb_int argc, const mrb_value *argv, mrb_value block); /** * Create a symbol from C string. But usually it's better to * use MRB_SYM, MRB_OPSYM, MRB_CVSYM, MRB_IVSYM, MRB_GVSYM, * MRB_SYM_B, MRB_SYM_Q, MRB_SYM_E macros. * * Example: * * # Ruby style: * :pizza # => :pizza * * // C style: * mrb_sym sym1 = mrb_intern_lit(mrb, "pizza"); // => :pizza * mrb_sym sym2 = MRB_SYM(pizza); // => :pizza * mrb_sym sym3 = MRB_SYM_Q(pizza); // => :pizza? * * @param mrb The current mruby state. * @param str The string to be symbolized * @return [mrb_sym] mrb_sym A symbol. */ MRB_API mrb_sym mrb_intern_cstr(mrb_state *mrb, const char* str); MRB_API mrb_sym mrb_intern(mrb_state*,const char*,size_t); MRB_API mrb_sym mrb_intern_static(mrb_state*,const char*,size_t); #define mrb_intern_lit(mrb, lit) mrb_intern_static(mrb, (lit ""), mrb_strlen_lit(lit)) MRB_API mrb_sym mrb_intern_str(mrb_state*,mrb_value); /* mrb_intern_check series functions returns 0 if the symbol is not defined */ MRB_API mrb_sym mrb_intern_check_cstr(mrb_state*,const char*); MRB_API mrb_sym mrb_intern_check(mrb_state*,const char*,size_t); MRB_API mrb_sym mrb_intern_check_str(mrb_state*,mrb_value); /* mrb_check_intern series functions returns nil if the symbol is not defined */ /* otherwise returns mrb_value */ MRB_API mrb_value mrb_check_intern_cstr(mrb_state*,const char*); MRB_API mrb_value mrb_check_intern(mrb_state*,const char*,size_t); MRB_API mrb_value mrb_check_intern_str(mrb_state*,mrb_value); MRB_API const char *mrb_sym_name(mrb_state*,mrb_sym); MRB_API const char *mrb_sym_name_len(mrb_state*,mrb_sym,mrb_int*); MRB_API const char *mrb_sym_dump(mrb_state*,mrb_sym); MRB_API mrb_value mrb_sym_str(mrb_state*,mrb_sym); #define mrb_sym2name(mrb,sym) mrb_sym_name(mrb,sym) #define mrb_sym2name_len(mrb,sym,len) mrb_sym_name_len(mrb,sym,len) #define mrb_sym2str(mrb,sym) mrb_sym_str(mrb,sym) MRB_API void *mrb_malloc(mrb_state*, size_t); /* raise RuntimeError if no mem */ MRB_API void *mrb_calloc(mrb_state*, size_t, size_t); /* ditto */ MRB_API void *mrb_realloc(mrb_state*, void*, size_t); /* ditto */ MRB_API void *mrb_realloc_simple(mrb_state*, void*, size_t); /* return NULL if no memory available */ MRB_API void *mrb_malloc_simple(mrb_state*, size_t); /* return NULL if no memory available */ MRB_API struct RBasic *mrb_obj_alloc(mrb_state*, enum mrb_vtype, struct RClass*); MRB_API void mrb_free(mrb_state*, void*); /** * Allocates a Ruby object that matches the constant literal defined in * `enum mrb_vtype` and returns a pointer to the corresponding C type. * * @param mrb The current mruby state * @param tt The constant literal of `enum mrb_vtype` * @param klass A Class object * @return Reference to the newly created object */ #define MRB_OBJ_ALLOC(mrb, tt, klass) ((MRB_VTYPE_TYPEOF(tt)*)mrb_obj_alloc(mrb, tt, klass)) MRB_API mrb_value mrb_str_new(mrb_state *mrb, const char *p, mrb_int len); /** * Turns a C string into a Ruby string value. */ MRB_API mrb_value mrb_str_new_cstr(mrb_state*, const char*); MRB_API mrb_value mrb_str_new_static(mrb_state *mrb, const char *p, mrb_int len); #define mrb_str_new_lit(mrb, lit) mrb_str_new_static(mrb, (lit), mrb_strlen_lit(lit)) MRB_API mrb_value mrb_obj_freeze(mrb_state*, mrb_value); #define mrb_str_new_frozen(mrb,p,len) mrb_obj_freeze(mrb,mrb_str_new(mrb,p,len)) #define mrb_str_new_cstr_frozen(mrb,p) mrb_obj_freeze(mrb,mrb_str_new_cstr(mrb,p)) #define mrb_str_new_static_frozen(mrb,p,len) mrb_obj_freeze(mrb,mrb_str_new_static(mrb,p,len)) #define mrb_str_new_lit_frozen(mrb,lit) mrb_obj_freeze(mrb,mrb_str_new_lit(mrb,lit)) #ifdef _WIN32 MRB_API char* mrb_utf8_from_locale(const char *p, int len); MRB_API char* mrb_locale_from_utf8(const char *p, int len); #define mrb_locale_free(p) free(p) #define mrb_utf8_free(p) free(p) #else #define mrb_utf8_from_locale(p, l) ((char*)(p)) #define mrb_locale_from_utf8(p, l) ((char*)(p)) #define mrb_locale_free(p) #define mrb_utf8_free(p) #endif /** * Creates new mrb_state. * * @return * Pointer to the newly created mrb_state. */ MRB_API mrb_state* mrb_open(void); /** * Create new mrb_state with just the mruby core * * @param f * Reference to the allocation function. * Use mrb_basic_alloc_func for the default * @param ud * User data will be passed to custom allocator f. * If user data isn't required just pass NULL. * @return * Pointer to the newly created mrb_state. */ MRB_API mrb_state* mrb_open_core(void); /** * Closes and frees a mrb_state. * * @param mrb * Pointer to the mrb_state to be closed. */ MRB_API void mrb_close(mrb_state *mrb); /** * The memory allocation function. You can redefine this function for your own allocator. * */ MRB_API void* mrb_basic_alloc_func(void*, size_t); MRB_API mrb_value mrb_top_self(mrb_state *mrb); /** * Enter the mruby VM and execute the proc. * * @param mrb * The current mruby state. * @param proc * An object containing `irep`. * If supplied an object containing anything other than `irep`, it will probably crash. * @param self * `self` on the execution context of `proc`. * @param stack_keep * Specifies the number of values to hold from the stack top. * Values on the stack outside this range will be initialized to `nil`. * * @note * When called from a C function defined as a method, the current stack is destroyed. * If you want to use arguments obtained by `mrb_get_args()` or other methods after `mrb_top_run()`, * you must protect them by `mrb_gc_protect()` or other ways before this function. * Or consider using `mrb_yield()` family functions. */ MRB_API mrb_value mrb_top_run(mrb_state *mrb, const struct RProc *proc, mrb_value self, mrb_int stack_keep); MRB_API mrb_value mrb_vm_run(mrb_state *mrb, const struct RProc *proc, mrb_value self, mrb_int stack_keep); MRB_API mrb_value mrb_vm_exec(mrb_state *mrb, const struct RProc *proc, const mrb_code *iseq); /* compatibility macros */ #define mrb_toplevel_run_keep(m,p,k) mrb_top_run((m),(p),mrb_top_self(m),(k)) #define mrb_toplevel_run(m,p) mrb_toplevel_run_keep((m),(p),0) #define mrb_context_run(m,p,s,k) mrb_vm_run((m),(p),(s),(k)) MRB_API void mrb_p(mrb_state*, mrb_value); MRB_API mrb_int mrb_obj_id(mrb_value obj); MRB_API mrb_sym mrb_obj_to_sym(mrb_state *mrb, mrb_value name); MRB_API mrb_bool mrb_obj_eq(mrb_state *mrb, mrb_value a, mrb_value b); MRB_API mrb_bool mrb_obj_equal(mrb_state *mrb, mrb_value a, mrb_value b); MRB_API mrb_bool mrb_equal(mrb_state *mrb, mrb_value obj1, mrb_value obj2); #ifndef MRB_NO_FLOAT MRB_API mrb_value mrb_ensure_float_type(mrb_state *mrb, mrb_value val); #define mrb_as_float(mrb, x) mrb_float(mrb_ensure_float_type(mrb, x)) /* obsolete: use mrb_ensure_float_type() instead */ #define mrb_to_float(mrb, val) mrb_ensure_float_type(mrb, val) #endif MRB_API mrb_value mrb_inspect(mrb_state *mrb, mrb_value obj); MRB_API mrb_bool mrb_eql(mrb_state *mrb, mrb_value obj1, mrb_value obj2); /* mrb_cmp(mrb, obj1, obj2): 1:0:-1; -2 for error */ MRB_API mrb_int mrb_cmp(mrb_state *mrb, mrb_value obj1, mrb_value obj2); #define mrb_gc_arena_save(mrb) ((mrb)->gc.arena_idx) #define mrb_gc_arena_restore(mrb, idx) ((mrb)->gc.arena_idx = (idx)) MRB_API void mrb_garbage_collect(mrb_state*); MRB_API void mrb_full_gc(mrb_state*); MRB_API void mrb_incremental_gc(mrb_state*); MRB_API void mrb_gc_mark(mrb_state*,struct RBasic*); #define mrb_gc_mark_value(mrb,val) do {\ if (!mrb_immediate_p(val)) mrb_gc_mark((mrb), mrb_basic_ptr(val)); \ } while (0) MRB_API void mrb_field_write_barrier(mrb_state*, struct RBasic*, struct RBasic*); #define mrb_field_write_barrier_value(mrb, obj, val) do{\ if (!mrb_immediate_p(val)) mrb_field_write_barrier((mrb), (obj), mrb_basic_ptr(val)); \ } while (0) MRB_API void mrb_write_barrier(mrb_state *, struct RBasic*); MRB_API mrb_value mrb_type_convert(mrb_state *mrb, mrb_value val, enum mrb_vtype type, mrb_sym method); #define mrb_convert_type(mrb, val, type, tname, method) mrb_type_convert(mrb, val, type, mrb_intern_lit(mrb, method)) MRB_API mrb_value mrb_type_convert_check(mrb_state *mrb, mrb_value val, enum mrb_vtype type, mrb_sym method); #define mrb_check_convert_type(mrb, val, type, tname, method) mrb_type_convert_check(mrb, val, type, mrb_intern_lit(mrb, method)) MRB_API mrb_value mrb_any_to_s(mrb_state *mrb, mrb_value obj); MRB_API const char * mrb_obj_classname(mrb_state *mrb, mrb_value obj); MRB_API struct RClass* mrb_obj_class(mrb_state *mrb, mrb_value obj); MRB_API mrb_value mrb_class_path(mrb_state *mrb, struct RClass *c); MRB_API mrb_bool mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c); MRB_API mrb_value mrb_obj_inspect(mrb_state *mrb, mrb_value self); MRB_API mrb_value mrb_obj_clone(mrb_state *mrb, mrb_value self); #ifndef ISPRINT #define ISASCII(c) ((unsigned)(c) <= 0x7f) #define ISPRINT(c) (((unsigned)(c) - 0x20) < 0x5f) #define ISSPACE(c) ((c) == ' ' || (unsigned)(c) - '\t' < 5) #define ISUPPER(c) (((unsigned)(c) - 'A') < 26) #define ISLOWER(c) (((unsigned)(c) - 'a') < 26) #define ISALPHA(c) ((((unsigned)(c) | 0x20) - 'a') < 26) #define ISDIGIT(c) (((unsigned)(c) - '0') < 10) #define ISXDIGIT(c) (ISDIGIT(c) || ((unsigned)(c) | 0x20) - 'a' < 6) #define ISALNUM(c) (ISALPHA(c) || ISDIGIT(c)) #define ISBLANK(c) ((c) == ' ' || (c) == '\t') #define ISCNTRL(c) ((unsigned)(c) < 0x20 || (c) == 0x7f) #define TOUPPER(c) (ISLOWER(c) ? ((c) & 0x5f) : (c)) #define TOLOWER(c) (ISUPPER(c) ? ((c) | 0x20) : (c)) #endif MRB_API mrb_value mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, mrb_int len); MRB_API mrb_noreturn void mrb_exc_raise(mrb_state *mrb, mrb_value exc); MRB_API mrb_noreturn void mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg); MRB_API mrb_noreturn void mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...); MRB_API mrb_noreturn void mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...); MRB_API mrb_noreturn void mrb_frozen_error(mrb_state *mrb, void *frozen_obj); MRB_API mrb_noreturn void mrb_argnum_error(mrb_state *mrb, mrb_int argc, int min, int max); MRB_API void mrb_warn(mrb_state *mrb, const char *fmt, ...); MRB_API mrb_noreturn void mrb_bug(mrb_state *mrb, const char *mesg); MRB_API void mrb_print_backtrace(mrb_state *mrb); MRB_API void mrb_print_error(mrb_state *mrb); /* function for `raisef` formatting */ MRB_API mrb_value mrb_vformat(mrb_state *mrb, const char *format, va_list ap); /* macros to get typical exception objects note: + those E_* macros requires mrb_state* variable named mrb. + exception objects obtained from those macros are local to mrb */ #define MRB_ERROR_SYM(sym) mrb_intern_lit(mrb, #sym) #define E_EXCEPTION mrb->eException_class #define E_STANDARD_ERROR mrb->eStandardError_class #define E_RUNTIME_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(RuntimeError)) #define E_TYPE_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(TypeError)) #define E_ZERODIV_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(ZeroDivisionError)) #define E_ARGUMENT_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(ArgumentError)) #define E_INDEX_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(IndexError)) #define E_RANGE_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(RangeError)) #define E_NAME_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(NameError)) #define E_NOMETHOD_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(NoMethodError)) #define E_SCRIPT_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(ScriptError)) #define E_SYNTAX_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(SyntaxError)) #define E_LOCALJUMP_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(LocalJumpError)) #define E_REGEXP_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(RegexpError)) #define E_FROZEN_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(FrozenError)) #define E_NOTIMP_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(NotImplementedError)) #define E_KEY_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(KeyError)) #ifndef MRB_NO_FLOAT # define E_FLOATDOMAIN_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(FloatDomainError)) #endif MRB_API mrb_value mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg); MRB_API mrb_value mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv); MRB_API mrb_value mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv, mrb_value self, struct RClass *c); /* continue execution to the proc */ /* this function should always be called as the last function of a method */ /* e.g. return mrb_yield_cont(mrb, proc, self, argc, argv); */ mrb_value mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const mrb_value *argv); /* mrb_gc_protect() leaves the object in the arena */ MRB_API void mrb_gc_protect(mrb_state *mrb, mrb_value obj); /* mrb_gc_register() keeps the object from GC. */ MRB_API void mrb_gc_register(mrb_state *mrb, mrb_value obj); /* mrb_gc_unregister() removes the object from GC root. */ MRB_API void mrb_gc_unregister(mrb_state *mrb, mrb_value obj); /* type conversion/check functions */ MRB_API mrb_value mrb_ensure_array_type(mrb_state *mrb, mrb_value self); MRB_API mrb_value mrb_check_array_type(mrb_state *mrb, mrb_value self); MRB_API mrb_value mrb_ensure_hash_type(mrb_state *mrb, mrb_value hash); MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash); MRB_API mrb_value mrb_ensure_string_type(mrb_state *mrb, mrb_value str); MRB_API mrb_value mrb_check_string_type(mrb_state *mrb, mrb_value str); /* obsolete: use mrb_ensure_string_type() instead */ #define mrb_string_type(mrb, str) mrb_ensure_string_type(mrb,str) #define mrb_to_str(mrb, str) mrb_ensure_string_type(mrb,str) /* obsolete: use mrb_obj_as_string() instead */ #define mrb_str_to_str(mrb, str) mrb_obj_as_string(mrb, str) /* check if val is an integer (including Bigint) */ MRB_API mrb_value mrb_ensure_integer_type(mrb_state *mrb, mrb_value val); /* check if val fit in mrb_int */ MRB_API mrb_value mrb_ensure_int_type(mrb_state *mrb, mrb_value val); #define mrb_as_int(mrb, val) mrb_integer(mrb_ensure_int_type(mrb, val)) /* obsolete: use mrb_ensure_int_type() instead */ #define mrb_to_integer(mrb, val) mrb_ensure_int_type(mrb, val) #define mrb_to_int(mrb, val) mrb_ensure_int_type(mrb, val) /* string type checking (contrary to the name, it doesn't convert) */ MRB_API void mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t); MRB_API void mrb_check_frozen(mrb_state *mrb, void *); MRB_API void mrb_check_frozen_value(mrb_state *mrb, mrb_value v); MRB_API void mrb_define_alias(mrb_state *mrb, struct RClass *c, const char *a, const char *b); MRB_API void mrb_define_alias_id(mrb_state *mrb, struct RClass *c, mrb_sym a, mrb_sym b); MRB_API const char *mrb_class_name(mrb_state *mrb, struct RClass* klass); MRB_API void mrb_define_global_const(mrb_state *mrb, const char *name, mrb_value val); MRB_API mrb_value mrb_attr_get(mrb_state *mrb, mrb_value obj, mrb_sym id); MRB_API mrb_bool mrb_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym mid); MRB_API mrb_bool mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, const struct RClass* c); MRB_API mrb_bool mrb_func_basic_p(mrb_state *mrb, mrb_value obj, mrb_sym mid, mrb_func_t func); /* obsolete function(s); will be removed */ #define mrb_int(mrb, val) mrb_as_int(mrb, val) /** * Create a new Fiber from proc object * * Implemented in mruby-fiber */ MRB_API mrb_value mrb_fiber_new(mrb_state *mrb, const struct RProc *proc); /** * Resume a Fiber * * Implemented in mruby-fiber * * Switches to the specified fiber and executes. Like the `Fiber#resume` method. */ MRB_API mrb_value mrb_fiber_resume(mrb_state *mrb, mrb_value fib, mrb_int argc, const mrb_value *argv); /** * Yield a Fiber * * Implemented in mruby-fiber * * Passes control to the caller fiber of the running fiber. Like the `Fiber.yield` method. * * @note This function is only available from inside a function defined as a method by, * for example, `mrb_define_method()`. * Also, the work following `mrb_fiber_yield()` cannot be performed, * and the return value of `mrb_fiber_yield()` must be returned as is. * * return mrb_fiber_yield(mrb, argc, argv); */ MRB_API mrb_value mrb_fiber_yield(mrb_state *mrb, mrb_int argc, const mrb_value *argv); /** * Check if a Fiber is alive * * Implemented in mruby-fiber */ MRB_API mrb_value mrb_fiber_alive_p(mrb_state *mrb, mrb_value fib); /** * FiberError reference * * Implemented in mruby-fiber */ #define E_FIBER_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(FiberError)) MRB_API void mrb_stack_extend(mrb_state*, mrb_int); /* temporary memory allocation, only effective while GC arena is kept */ MRB_API void* mrb_alloca(mrb_state *mrb, size_t); MRB_API void mrb_state_atexit(mrb_state *mrb, mrb_atexit_func func); MRB_API void mrb_show_version(mrb_state *mrb); MRB_API void mrb_show_copyright(mrb_state *mrb); MRB_API mrb_value mrb_format(mrb_state *mrb, const char *format, ...); #ifdef MRB_PRESYM_SCANNING # include #endif #if 0 /* memcpy and memset does not work with gdb reverse-next on my box */ /* use naive memcpy and memset instead */ #undef memcpy #undef memset static void* mrbmemcpy(void *dst, const void *src, size_t n) { char *d = (char*)dst; const char *s = (const char*)src; while (n--) *d++ = *s++; return d; } #define memcpy(a,b,c) mrbmemcpy(a,b,c) static void* mrbmemset(void *s, int c, size_t n) { char *t = (char*)s; while (n--) *t++ = c; return s; } #define memset(a,b,c) mrbmemset(a,b,c) #endif #define mrb_int_hash_func(mrb,key) (uint32_t)((key)^((key)<<2)^((key)>>2)) MRB_END_DECL #endif /* MRUBY_H */ nghttp2-1.68.0/third-party/mruby/include/PaxHeaders/mrbconf.h0000644000000000000000000000013215077107276021115 xustar0030 mtime=1761382078.100420657 30 atime=1761382080.121411411 30 ctime=1761382109.015300627 nghttp2-1.68.0/third-party/mruby/include/mrbconf.h0000644000175100017510000001376615077107276021522 0ustar00runnerrunner/* ** mrbconf.h - mruby core configuration ** ** See Copyright Notice in mruby.h */ #ifndef MRUBYCONF_H #define MRUBYCONF_H /* architecture selection: */ /* specify -DMRB_32BIT or -DMRB_64BIT to override */ #if !defined(MRB_32BIT) && !defined(MRB_64BIT) #if UINT64_MAX == SIZE_MAX #define MRB_64BIT #else #define MRB_32BIT #endif #endif #if defined(MRB_32BIT) && defined(MRB_64BIT) #error Cannot build for 32 and 64-bit architecture at the same time #endif /* configuration options: */ /* add -DMRB_USE_FLOAT32 to use float instead of double for floating-point numbers */ //#define MRB_USE_FLOAT32 /* exclude floating-point numbers */ //#define MRB_NO_FLOAT /* obsolete configuration */ #if defined(MRB_USE_FLOAT) # define MRB_USE_FLOAT32 #endif /* obsolete configuration */ #if defined(MRB_WITHOUT_FLOAT) # define MRB_NO_FLOAT #endif #if defined(MRB_USE_FLOAT32) && defined(MRB_NO_FLOAT) #error Cannot define MRB_USE_FLOAT32 and MRB_NO_FLOAT at the same time #endif /* add -DMRB_NO_METHOD_CACHE to disable method cache to save memory */ //#define MRB_NO_METHOD_CACHE /* size of the method cache (need to be the power of 2) */ //#define MRB_METHOD_CACHE_SIZE (1<<8) //#define MRB_USE_INLINE_METHOD_CACHE /* define on big endian machines; used by MRB_NAN_BOXING, etc. */ #ifndef MRB_ENDIAN_BIG # if (defined(BYTE_ORDER) && defined(BIG_ENDIAN) && BYTE_ORDER == BIG_ENDIAN) || \ (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) # define MRB_ENDIAN_BIG # endif #endif /* represent mrb_value in boxed double; conflict with MRB_USE_FLOAT32 and MRB_NO_FLOAT */ //#define MRB_NAN_BOXING /* represent mrb_value as a word (natural unit of data for the processor) */ //#define MRB_WORD_BOXING /* represent mrb_value as a struct; occupies 2 words */ //#define MRB_NO_BOXING /* if no specific boxing type is chosen */ #if !defined(MRB_NAN_BOXING) && !defined(MRB_WORD_BOXING) && !defined(MRB_NO_BOXING) # define MRB_WORD_BOXING #endif /* if defined mruby allocates Float objects in the heap to keep full precision if needed */ //#define MRB_WORDBOX_NO_FLOAT_TRUNCATE /* add -DMRB_INT32 to use 32-bit integer for mrb_int; conflict with MRB_INT64; Default for 32-bit CPU mode. */ //#define MRB_INT32 /* add -DMRB_INT64 to use 64-bit integer for mrb_int; conflict with MRB_INT32; Default for 64-bit CPU mode (unless using MRB_NAN_BOXING). */ //#define MRB_INT64 /* if no specific integer type is chosen */ #if !defined(MRB_INT32) && !defined(MRB_INT64) # if defined(MRB_64BIT) && !defined(MRB_NAN_BOXING) /* Use 64-bit integers on 64-bit architecture (without MRB_NAN_BOXING) */ # define MRB_INT64 # else /* Otherwise use 32-bit integers */ # define MRB_INT32 # endif #endif /* call malloc_trim(0) from mrb_full_gc() */ //#define MRB_USE_MALLOC_TRIM /* string class to handle UTF-8 encoding */ //#define MRB_UTF8_STRING /* maximum length of strings */ /* the default value is 1MB */ /* set this value to zero to skip the check */ //#define MRB_STR_LENGTH_MAX 1048576 /* maximum length of arrays */ /* the default value is 2**17 entries */ /* set this value to zero to skip the check */ //#define MRB_ARY_LENGTH_MAX 131072 /* argv max size in mrb_funcall */ //#define MRB_FUNCALL_ARGC_MAX 16 /* number of object per heap page */ //#define MRB_HEAP_PAGE_SIZE 1024 /* define if your platform does not support etext, edata */ //#define MRB_NO_DEFAULT_RO_DATA_P /* define if your platform supports etext, edata */ //#define MRB_USE_RO_DATA_P_ETEXT /* use MRB_USE_ETEXT_RO_DATA_P by default on Linux */ #if (defined(__linux__) && !defined(__KERNEL__)) #define MRB_USE_ETEXT_RO_DATA_P #endif /* you can provide and use mrb_ro_data_p() for your platform. prototype is `mrb_bool mrb_ro_data_p(const char *ptr)` */ //#define MRB_USE_CUSTOM_RO_DATA_P /* turn off generational GC by default */ //#define MRB_GC_TURN_OFF_GENERATIONAL /* default size of khash table bucket */ //#define KHASH_DEFAULT_SIZE 32 /* allocated memory address alignment */ //#define POOL_ALIGNMENT 4 /* page size of memory pool */ //#define POOL_PAGE_SIZE 16000 /* arena size */ //#define MRB_GC_ARENA_SIZE 100 /* fixed size GC arena */ //#define MRB_GC_FIXED_ARENA /* state atexit stack size */ //#define MRB_FIXED_STATE_ATEXIT_STACK_SIZE 5 /* fixed size state atexit stack */ //#define MRB_FIXED_STATE_ATEXIT_STACK /* -DMRB_NO_XXXX to drop following features */ //#define MRB_NO_STDIO /* use of stdio */ /* -DMRB_USE_XXXX to enable following features */ //#define MRB_USE_DEBUG_HOOK /* hooks for debugger */ //#define MRB_USE_ALL_SYMBOLS /* Symbol.all_symbols */ /* obsolete configurations */ #if defined(DISABLE_STDIO) || defined(MRB_DISABLE_STDIO) # define MRB_NO_STDIO #endif #if defined(MRB_DISABLE_DIRECT_THREADING) || defined(MRB_NO_DIRECT_THREADING) # define MRB_USE_VM_SWITCH_DISPATCH #endif #if defined(ENABLE_DEBUG) || defined(MRB_ENABLE_DEBUG_HOOK) # define MRB_USE_DEBUG_HOOK #endif #ifdef MRB_ENABLE_ALL_SYMBOLS # define MRB_USE_ALL_SYMBOLS #endif #ifdef MRB_ENABLE_CXX_ABI # define MRB_USE_CXX_ABI #endif #ifdef MRB_ENABLE_CXX_EXCEPTION # define MRB_USE_CXX_EXCEPTION #endif /* end of configuration */ #ifndef MRB_NO_STDIO # include #endif /* ** mruby tuning profiles **/ /* A profile for micro controllers */ #if defined(MRB_CONSTRAINED_BASELINE_PROFILE) # ifndef MRB_NO_METHOD_CACHE # define MRB_NO_METHOD_CACHE # endif # ifndef KHASH_DEFAULT_SIZE # define KHASH_DEFAULT_SIZE 16 # endif # ifndef MRB_HEAP_PAGE_SIZE # define MRB_HEAP_PAGE_SIZE 256 # endif /* A profile for default mruby */ #elif defined(MRB_BASELINE_PROFILE) /* A profile for desktop computers or workstations; rich memory! */ #elif defined(MRB_MAIN_PROFILE) # ifndef MRB_METHOD_CACHE_SIZE # define MRB_METHOD_CACHE_SIZE (1<<10) # endif # ifndef MRB_HEAP_PAGE_SIZE # define MRB_HEAP_PAGE_SIZE 4096 # endif /* A profile for server; mruby vm is long life */ #elif defined(MRB_HIGH_PROFILE) # ifndef MRB_METHOD_CACHE_SIZE # define MRB_METHOD_CACHE_SIZE (1<<12) # endif # ifndef MRB_HEAP_PAGE_SIZE # define MRB_HEAP_PAGE_SIZE 4096 # endif #endif #endif /* MRUBYCONF_H */ nghttp2-1.68.0/third-party/mruby/PaxHeaders/src0000644000000000000000000000013215077107334016400 xustar0030 mtime=1761382108.597301835 30 atime=1761382109.799298361 30 ctime=1761382108.597301835 nghttp2-1.68.0/third-party/mruby/src/0000755000175100017510000000000015077107334017045 5ustar00runnerrunnernghttp2-1.68.0/third-party/mruby/src/PaxHeaders/string.c0000644000000000000000000000013215077107276020134 xustar0030 mtime=1761382078.136420492 30 atime=1761382080.159411238 30 ctime=1761382108.567301922 nghttp2-1.68.0/third-party/mruby/src/string.c0000644000175100017510000024660215077107276020536 0ustar00runnerrunner/* ** string.c - String class ** ** See Copyright Notice in mruby.h */ #ifdef _MSC_VER # define _CRT_NONSTDC_NO_DEPRECATE # define WIN32_LEAN_AND_MEAN #endif #include #include #include #include #include #include #include #include #include typedef struct mrb_shared_string { int refcnt; mrb_int capa; char *ptr; } mrb_shared_string; const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz"; #define mrb_obj_alloc_string(mrb) MRB_OBJ_ALLOC((mrb), MRB_TT_STRING, (mrb)->string_class) #ifndef MRB_STR_LENGTH_MAX #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) #define MRB_STR_LENGTH_MAX 0 #else #define MRB_STR_LENGTH_MAX 1048576 #endif #endif static void str_check_length(mrb_state *mrb, mrb_int len) { if (len < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative (or overflowed) string size"); } #if MRB_STR_LENGTH_MAX != 0 if (len > MRB_STR_LENGTH_MAX-1) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "string too long (len=%i max=" MRB_STRINGIZE(MRB_STR_LENGTH_MAX) ")", len); } #endif } static struct RString* str_init_normal_capa(mrb_state *mrb, struct RString *s, const char *p, mrb_int len, mrb_int capa) { str_check_length(mrb, capa); char *dst = (char*)mrb_malloc(mrb, capa + 1); if (p) memcpy(dst, p, len); dst[len] = '\0'; s->as.heap.ptr = dst; s->as.heap.len = len; s->as.heap.aux.capa = capa; RSTR_SET_TYPE(s, NORMAL); return s; } static struct RString* str_init_normal(mrb_state *mrb, struct RString *s, const char *p, mrb_int len) { return str_init_normal_capa(mrb, s, p, len, len); } static struct RString* str_init_embed(struct RString *s, const char *p, mrb_int len) { mrb_assert(len >= 0); if (p) memcpy(RSTR_EMBED_PTR(s), p, len); RSTR_EMBED_PTR(s)[len] = '\0'; RSTR_SET_TYPE(s, EMBED); RSTR_SET_EMBED_LEN(s, len); return s; } static struct RString* str_init_nofree(struct RString *s, const char *p, mrb_int len) { s->as.heap.ptr = (char*)p; s->as.heap.len = len; s->as.heap.aux.capa = 0; /* nofree */ RSTR_SET_TYPE(s, NOFREE); return s; } static struct RString* str_init_shared(mrb_state *mrb, const struct RString *orig, struct RString *s, mrb_shared_string *shared) { if (shared) { shared->refcnt++; } else { shared = (mrb_shared_string*)mrb_malloc(mrb, sizeof(mrb_shared_string)); shared->refcnt = 1; shared->ptr = orig->as.heap.ptr; shared->capa = orig->as.heap.aux.capa; } s->as.heap.ptr = orig->as.heap.ptr; s->as.heap.len = orig->as.heap.len; s->as.heap.aux.shared = shared; RSTR_SET_TYPE(s, SHARED); return s; } static struct RString* str_init_fshared(const struct RString *orig, struct RString *s, struct RString *fshared) { s->as.heap.ptr = orig->as.heap.ptr; s->as.heap.len = orig->as.heap.len; s->as.heap.aux.fshared = fshared; RSTR_SET_TYPE(s, FSHARED); return s; } static struct RString* str_init_modifiable(mrb_state *mrb, struct RString *s, const char *p, mrb_int len) { if (RSTR_EMBEDDABLE_P(len)) { return str_init_embed(s, p, len); } return str_init_normal(mrb, s, p, len); } static struct RString* str_new_static(mrb_state *mrb, const char *p, mrb_int len) { if (RSTR_EMBEDDABLE_P(len)) { return str_init_embed(mrb_obj_alloc_string(mrb), p, len); } return str_init_nofree(mrb_obj_alloc_string(mrb), p, len); } static struct RString* str_new(mrb_state *mrb, const char *p, mrb_int len) { str_check_length(mrb, len); if (RSTR_EMBEDDABLE_P(len)) { return str_init_embed(mrb_obj_alloc_string(mrb), p, len); } if (p && mrb_ro_data_p(p)) { return str_init_nofree(mrb_obj_alloc_string(mrb), p, len); } return str_init_normal(mrb, mrb_obj_alloc_string(mrb), p, len); } MRB_API mrb_value mrb_str_new_capa(mrb_state *mrb, mrb_int capa) { struct RString *s = mrb_obj_alloc_string(mrb); if (RSTR_EMBEDDABLE_P(capa)) { s = str_init_embed(s, NULL, 0); } else { s = str_init_normal_capa(mrb, s, NULL, 0, capa); } return mrb_obj_value(s); } static void resize_capa(mrb_state *mrb, struct RString *s, mrb_int capacity) { if (RSTR_EMBED_P(s)) { if (!RSTR_EMBEDDABLE_P(capacity)) { str_init_normal_capa(mrb, s, RSTR_EMBED_PTR(s), RSTR_EMBED_LEN(s), capacity); } } else { str_check_length(mrb, capacity); s->as.heap.ptr = (char*)mrb_realloc(mrb, RSTR_PTR(s), capacity+1); s->as.heap.aux.capa = (mrb_ssize)capacity; } } MRB_API mrb_value mrb_str_new(mrb_state *mrb, const char *p, mrb_int len) { return mrb_obj_value(str_new(mrb, p, len)); } MRB_API mrb_value mrb_str_new_cstr(mrb_state *mrb, const char *p) { struct RString *s; mrb_int len; if (p) { len = strlen(p); } else { len = 0; } s = str_new(mrb, p, len); return mrb_obj_value(s); } MRB_API mrb_value mrb_str_new_static(mrb_state *mrb, const char *p, mrb_int len) { struct RString *s = str_new_static(mrb, p, len); return mrb_obj_value(s); } static void str_decref(mrb_state *mrb, mrb_shared_string *shared) { shared->refcnt--; if (shared->refcnt == 0) { mrb_free(mrb, shared->ptr); mrb_free(mrb, shared); } } static void str_unshare_buffer(mrb_state *mrb, struct RString *s) { if (RSTR_SHARED_P(s)) { mrb_shared_string *shared = s->as.heap.aux.shared; if (shared->refcnt == 1 && s->as.heap.ptr == shared->ptr) { s->as.heap.aux.capa = shared->capa; s->as.heap.ptr[s->as.heap.len] = '\0'; RSTR_SET_TYPE(s, NORMAL); mrb_free(mrb, shared); } else { str_init_modifiable(mrb, s, s->as.heap.ptr, s->as.heap.len); str_decref(mrb, shared); } } else if (RSTR_NOFREE_P(s) || RSTR_FSHARED_P(s)) { str_init_modifiable(mrb, s, s->as.heap.ptr, s->as.heap.len); } } static void check_null_byte(mrb_state *mrb, struct RString *str) { const char *p = RSTR_PTR(str); if (p && memchr(p, '\0', RSTR_LEN(str))) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte"); } } void mrb_gc_free_str(mrb_state *mrb, struct RString *str) { if (RSTR_EMBED_P(str)) /* no code */; else if (RSTR_SHARED_P(str)) str_decref(mrb, str->as.heap.aux.shared); else if (!RSTR_NOFREE_P(str) && !RSTR_FSHARED_P(str)) mrb_free(mrb, str->as.heap.ptr); } #if defined(__i386) || defined(__i386__) || defined(_M_IX86) || \ defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || \ defined(__powerpc64__) || defined(__POWERPC__) || defined(__aarch64__) || \ defined(__mc68020__) # define ALIGNED_WORD_ACCESS 0 #else # define ALIGNED_WORD_ACCESS 1 #endif #ifdef MRB_64BIT #define bitint uint64_t #define MASK01 0x0101010101010101ull #else #define bitint uint32_t #define MASK01 0x01010101ul #endif #ifdef MRB_UTF8_STRING #define NOASCII(c) ((c) & 0x80) #ifdef SIMPLE_SEARCH_NONASCII /* the naive implementation. define SIMPLE_SEARCH_NONASCII, */ /* if you need it for any constraint (e.g. code size). */ static const char* search_nonascii(const char* p, const char *e) { for (; p < e; ++p) { if (NOASCII(*p)) return p; } return e; } #elif defined(__SSE2__) # include static inline const char * search_nonascii(const char *p, const char *e) { if (sizeof(__m128i) < (size_t)(e - p)) { if (!_mm_movemask_epi8(_mm_loadu_si128((__m128i const*)p))) { const intptr_t lowbits = sizeof(__m128i) - 1; const __m128i *s, *t; s = (const __m128i*)(~lowbits & ((intptr_t)p + lowbits)); t = (const __m128i*)(~lowbits & (intptr_t)e); for (; s < t; ++s) { if (_mm_movemask_epi8(_mm_load_si128(s))) break; } p = (const char *)s; } } switch (e - p) { default: case 15: if (NOASCII(*p)) return p; ++p; case 14: if (NOASCII(*p)) return p; ++p; case 13: if (NOASCII(*p)) return p; ++p; case 12: if (NOASCII(*p)) return p; ++p; case 11: if (NOASCII(*p)) return p; ++p; case 10: if (NOASCII(*p)) return p; ++p; case 9: if (NOASCII(*p)) return p; ++p; case 8: if (NOASCII(*p)) return p; ++p; case 7: if (NOASCII(*p)) return p; ++p; case 6: if (NOASCII(*p)) return p; ++p; case 5: if (NOASCII(*p)) return p; ++p; case 4: if (NOASCII(*p)) return p; ++p; case 3: if (NOASCII(*p)) return p; ++p; case 2: if (NOASCII(*p)) return p; ++p; case 1: if (NOASCII(*p)) return p; ++p; if (NOASCII(*p)) return p; case 0: break; } return e; } #else static const char* search_nonascii(const char *p, const char *e) { ptrdiff_t byte_len = e - p; const char *be = p + sizeof(bitint) * (byte_len / sizeof(bitint)); for (; p < be; p+=sizeof(bitint)) { bitint t0; memcpy(&t0, p, sizeof(bitint)); const bitint t1 = t0 & (MASK01*0x80); if (t1) { e = p + sizeof(bitint)-1; byte_len = sizeof(bitint)-1; break; } } switch (byte_len % sizeof(bitint)) { #ifdef MRB_64BIT case 7: if (e[-7]&0x80) return e-7; case 6: if (e[-6]&0x80) return e-6; case 5: if (e[-5]&0x80) return e-5; case 4: if (e[-4]&0x80) return e-4; #endif case 3: if (e[-3]&0x80) return e-3; case 2: if (e[-2]&0x80) return e-2; case 1: if (e[-1]&0x80) return e-1; } return e; } #endif /* SIMPLE_SEARCH_NONASCII */ #define utf8_islead(c) ((unsigned char)((c)&0xc0) != 0x80) extern const char mrb_utf8len_table[]; const char mrb_utf8len_table[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0 }; mrb_int mrb_utf8len(const char* p, const char* e) { mrb_int len = mrb_utf8len_table[(unsigned char)p[0] >> 3]; if (len > e - p) return 1; switch (len) { case 0: return 1; case 4: if (utf8_islead(p[3])) return 1; case 3: if (utf8_islead(p[2])) return 1; case 2: if (utf8_islead(p[1])) return 1; } return len; } #if defined(__GNUC__) || __has_builtin(__builtin_popcount) # ifdef MRB_64BIT # define popcount(x) __builtin_popcountll(x) # else # define popcount(x) __builtin_popcountl(x) # endif #else static inline uint32_t popcount(bitint x) { x = (x & (MASK01*0x55)) + ((x >> 1) & (MASK01*0x55)); x = (x & (MASK01*0x33)) + ((x >> 2) & (MASK01*0x33)); x = (x & (MASK01*0x0F)) + ((x >> 4) & (MASK01*0x0F)); return (x * MASK01) >> 56; } #endif mrb_int mrb_utf8_strlen(const char *str, mrb_int byte_len) { const char *p = str; const char *e = str + byte_len; mrb_int len = 0; while (p < e) { const char *np = search_nonascii(p, e); len += np - p; if (np == e) break; p = np; while (NOASCII(*p)) { p += mrb_utf8len(p, e); len++; } } return len; } static mrb_int utf8_strlen(mrb_value str) { struct RString *s = mrb_str_ptr(str); mrb_int byte_len = RSTR_LEN(s); if (RSTR_SINGLE_BYTE_P(s)) { return byte_len; } else { mrb_int utf8_len = mrb_utf8_strlen(RSTR_PTR(s), byte_len); mrb_assert(utf8_len <= byte_len); if (byte_len == utf8_len) RSTR_SET_SINGLE_BYTE_FLAG(s); return utf8_len; } } #define RSTRING_CHAR_LEN(s) utf8_strlen(s) /* map character index to byte offset index */ static mrb_int chars2bytes(mrb_value str, mrb_int off, mrb_int idx) { struct RString *s = mrb_str_ptr(str); if (RSTR_SINGLE_BYTE_P(s) || RSTR_BINARY_P(s)) { return idx; } const char *o = RSTR_PTR(s); const char *p0 = o + off; const char *p = p0; const char *e = o + RSTR_LEN(s); mrb_int i = 0; while (p 1 && utf8_islead(ptr[1])) return ptr+1; if (len > 2 && utf8_islead(ptr[2])) return ptr+2; if (len > 3 && utf8_islead(ptr[3])) return ptr+3; return ptr; } static const char* char_backtrack(const char *ptr, const char *end) { ptrdiff_t len = end - ptr; if (len < 1 || utf8_islead(end[-1])) return end-1; if (len > 1 && utf8_islead(end[-2])) return end-2; if (len > 2 && utf8_islead(end[-3])) return end-3; if (len > 3 && utf8_islead(end[-4])) return end-4; return end - 1; } static mrb_int str_index_str_by_char(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos) { const char *ptr = RSTRING_PTR(sub); mrb_int len = RSTRING_LEN(sub); if (pos > 0) { pos = chars2bytes(str, 0, pos); } pos = mrb_str_index(mrb, str, ptr, len, pos); if (pos > 0) { pos = bytes2chars(str, pos); } return pos; } #else #define RSTRING_CHAR_LEN(s) RSTRING_LEN(s) #define chars2bytes(s, off, ci) (ci) #define bytes2chars(s, bi) (bi) #define char_adjust(ptr, end) (ptr) #define char_backtrack(ptr, end) ((end) - 1) #define str_index_str_by_char(mrb, str, sub, pos) str_index_str((mrb), (str), (sub), (pos)) #endif /* memsearch_swar (SWAR stands for SIMD within a register) */ /* See https://en.wikipedia.org/wiki/SWAR */ /* The function is taken from http://0x80.pl/articles/simd-strfind.html */ /* The original source code is under 2-clause BSD license; see LEGAL file. */ /* The modifications: * port from C++ to C * returns mrb_int * remove alignment issue * support bigendian CPU * fixed potential buffer overflow */ static inline mrb_int memsearch_swar(const char *xs, mrb_int m, const char *ys, mrb_int n) { #define MASK7f (MASK01*0x7f) #define MASK80 (MASK01*0x80) #if defined(MRB_ENDIAN_BIG) #ifdef MRB_64BIT #define MASKtop 0x8000000000000000ull #else #define MASKtop 0x80000000ul #endif #else #define MASKtop 0x80 #endif const bitint first = MASK01 * (uint8_t)xs[0]; const bitint last = MASK01 * (uint8_t)xs[m-1]; const char *s0 = ys; const char *s1 = ys+m-1; const mrb_int lim = n - m - (mrb_int)sizeof(bitint); mrb_int i; for (i=0; i < lim; i+=sizeof(bitint)) { bitint t0, t1; memcpy(&t0, s0+i, sizeof(bitint)); memcpy(&t1, s1+i, sizeof(bitint)); const bitint eq = (t0 ^ first) | (t1 ^ last); bitint zeros = ((~eq & MASK7f) + MASK01) & (~eq & MASK80); for (size_t j = 0; zeros; j++) { if (zeros & MASKtop) { const mrb_int idx = i + j; const char* p = s0 + idx + 1; if (memcmp(p, xs + 1, m - 2) == 0) { return idx; } } #if defined(MRB_ENDIAN_BIG) zeros <<= 8; #else zeros >>= 8; #endif } } if (i+m < n) { const char *p = s0; const char *e = ys + n; while (p n) return -1; else if (m == n) { return memcmp(x, y, m) == 0 ? 0 : -1; } else if (m < 1) { return 0; } else if (m == 1) { const char *p = (const char*)memchr(y, *x, n); if (p) return (mrb_int)(p - y); return -1; } return memsearch_swar(x, m, y, n); } static void str_share(mrb_state *mrb, struct RString *orig, struct RString *s) { size_t len = (size_t)orig->as.heap.len; mrb_assert(!RSTR_EMBED_P(orig)); if (RSTR_NOFREE_P(orig)) { str_init_nofree(s, orig->as.heap.ptr, len); } else if (RSTR_SHARED_P(orig)) { str_init_shared(mrb, orig, s, orig->as.heap.aux.shared); } else if (RSTR_FSHARED_P(orig)) { str_init_fshared(orig, s, orig->as.heap.aux.fshared); } else { if (orig->as.heap.aux.capa > orig->as.heap.len) { orig->as.heap.ptr = (char*)mrb_realloc(mrb, orig->as.heap.ptr, len+1); orig->as.heap.aux.capa = (mrb_ssize)len; } str_init_shared(mrb, orig, s, NULL); str_init_shared(mrb, orig, orig, s->as.heap.aux.shared); } } mrb_value mrb_str_byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) { struct RString *orig = mrb_str_ptr(str); struct RString *s = mrb_obj_alloc_string(mrb); if (RSTR_EMBEDDABLE_P(len)) { str_init_embed(s, RSTR_PTR(orig)+beg, len); } else { str_share(mrb, orig, s); s->as.heap.ptr += (mrb_ssize)beg; s->as.heap.len = (mrb_ssize)len; } RSTR_COPY_SINGLE_BYTE_FLAG(s, orig); return mrb_obj_value(s); } #ifdef MRB_UTF8_STRING static inline mrb_value str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) { beg = chars2bytes(str, 0, beg); len = chars2bytes(str, beg, len); return mrb_str_byte_subseq(mrb, str, beg, len); } #else #define str_subseq(mrb, str, beg, len) mrb_str_byte_subseq(mrb, str, beg, len) #endif mrb_bool mrb_str_beg_len(mrb_int str_len, mrb_int *begp, mrb_int *lenp) { if (str_len < *begp || *lenp < 0) return FALSE; if (*begp < 0) { *begp += str_len; if (*begp < 0) return FALSE; } if (*lenp > str_len - *begp) *lenp = str_len - *begp; if (*lenp <= 0) { *lenp = 0; } return TRUE; } static mrb_value str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) { return mrb_str_beg_len(RSTRING_CHAR_LEN(str), &beg, &len) ? str_subseq(mrb, str, beg, len) : mrb_nil_value(); } MRB_API mrb_int mrb_str_index(mrb_state *mrb, mrb_value str, const char *sptr, mrb_int slen, mrb_int offset) { mrb_int len = RSTRING_LEN(str); if (offset < 0) { offset += len; if (offset < 0) return -1; } if (len - offset < slen) return -1; char *s = RSTRING_PTR(str); if (offset) { s += offset; } if (slen == 0) return offset; /* need proceed one character at a time */ len = RSTRING_LEN(str) - offset; mrb_int pos = mrb_memsearch(sptr, slen, s, len); if (pos < 0) return pos; return pos + offset; } static mrb_int str_index_str(mrb_state *mrb, mrb_value str, mrb_value str2, mrb_int offset) { const char *ptr = RSTRING_PTR(str2); mrb_int len = RSTRING_LEN(str2); return mrb_str_index(mrb, str, ptr, len, offset); } static mrb_value str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2) { mrb_check_frozen(mrb, s1); if (s1 == s2) return mrb_obj_value(s1); RSTR_COPY_SINGLE_BYTE_FLAG(s1, s2); if (RSTR_SHARED_P(s1)) { str_decref(mrb, s1->as.heap.aux.shared); } else if (!RSTR_EMBED_P(s1) && !RSTR_NOFREE_P(s1) && !RSTR_FSHARED_P(s1)) { mrb_free(mrb, s1->as.heap.ptr); } size_t len = (size_t)RSTR_LEN(s2); if (RSTR_EMBEDDABLE_P(len)) { str_init_embed(s1, RSTR_PTR(s2), len); } else { str_share(mrb, s2, s1); } return mrb_obj_value(s1); } static mrb_int str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos) { const char *s, *sbeg, *send, *t; struct RString *ps = mrb_str_ptr(str); mrb_int len = RSTRING_LEN(sub); mrb_int slen = RSTR_LEN(ps); /* substring longer than string */ if (slen < len) return -1; if (slen - pos < len) { pos = slen - len; } sbeg = RSTR_PTR(ps); send = sbeg + slen; s = sbeg + pos; t = RSTRING_PTR(sub); if (len) { s = char_adjust(s, send); while (sbeg <= s) { if ((mrb_int)(send - s) >= len && memcmp(s, t, len) == 0) { return (mrb_int)(s - sbeg); } s = char_backtrack(sbeg, s); } return -1; } else { return pos; } } #ifdef _WIN32 #include #include #include char* mrb_utf8_from_locale(const char *str, int len) { wchar_t* wcsp; char* mbsp; int mbssize, wcssize; if (len == 0) return strdup(""); if (len == -1) len = (int)strlen(str); wcssize = MultiByteToWideChar(GetACP(), 0, str, len, NULL, 0); wcsp = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t)); if (!wcsp) return NULL; wcssize = MultiByteToWideChar(GetACP(), 0, str, len, wcsp, wcssize + 1); wcsp[wcssize] = 0; mbssize = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) wcsp, -1, NULL, 0, NULL, NULL); mbsp = (char*) malloc((mbssize + 1)); if (!mbsp) { free(wcsp); return NULL; } mbssize = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) wcsp, -1, mbsp, mbssize, NULL, NULL); mbsp[mbssize] = 0; free(wcsp); return mbsp; } char* mrb_locale_from_utf8(const char *utf8, int len) { wchar_t* wcsp; char* mbsp; int mbssize, wcssize; if (len == 0) return strdup(""); if (len == -1) len = (int)strlen(utf8); wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, len, NULL, 0); wcsp = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t)); if (!wcsp) return NULL; wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, len, wcsp, wcssize + 1); wcsp[wcssize] = 0; mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsp, -1, NULL, 0, NULL, NULL); mbsp = (char*) malloc((mbssize + 1)); if (!mbsp) { free(wcsp); return NULL; } mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsp, -1, mbsp, mbssize, NULL, NULL); mbsp[mbssize] = 0; free(wcsp); return mbsp; } #endif MRB_API void mrb_str_modify_keep_ascii(mrb_state *mrb, struct RString *s) { mrb_check_frozen(mrb, s); str_unshare_buffer(mrb, s); } MRB_API void mrb_str_modify(mrb_state *mrb, struct RString *s) { mrb_str_modify_keep_ascii(mrb, s); RSTR_UNSET_SINGLE_BYTE_FLAG(s); } MRB_API mrb_value mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len) { mrb_int slen; struct RString *s = mrb_str_ptr(str); str_check_length(mrb, len); mrb_str_modify(mrb, s); slen = RSTR_LEN(s); if (len != slen) { if (slen < len || slen - len > 256) { resize_capa(mrb, s, len); } RSTR_SET_LEN(s, len); RSTR_PTR(s)[len] = '\0'; /* sentinel */ } return str; } MRB_API char* mrb_str_to_cstr(mrb_state *mrb, mrb_value str0) { struct RString *s; const char *p = RSTRING_PTR(str0); mrb_int len = RSTRING_LEN(str0); check_null_byte(mrb, RSTRING(str0)); s = str_init_modifiable(mrb, mrb_obj_alloc_string(mrb), p, len); return RSTR_PTR(s); } MRB_API void mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other) { other = mrb_obj_as_string(mrb, other); mrb_str_cat_str(mrb, self, other); } MRB_API mrb_value mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b) { struct RString *s = mrb_str_ptr(a); struct RString *s2 = mrb_str_ptr(b); struct RString *t; mrb_int slen = RSTR_LEN(s); mrb_int s2len = RSTR_LEN(s2); const char *p = RSTR_PTR(s); const char *p2 = RSTR_PTR(s2); t = str_new(mrb, 0, slen + s2len); char *pt = RSTR_PTR(t); memcpy(pt, p, slen); memcpy(pt + slen, p2, s2len); return mrb_obj_value(t); } /* 15.2.10.5.2 */ /* * call-seq: * str + other_str -> new_str * * Concatenation---Returns a new String containing * other_str concatenated to str. * * "Hello from " + self.to_s #=> "Hello from main" */ static mrb_value mrb_str_plus_m(mrb_state *mrb, mrb_value self) { mrb_value str; mrb_get_args(mrb, "S", &str); return mrb_str_plus(mrb, self, str); } /* 15.2.10.5.26 */ /* 15.2.10.5.33 */ /* * call-seq: * "abcd".size => int * * Returns the length of string. */ static mrb_value mrb_str_size(mrb_state *mrb, mrb_value self) { mrb_int len = RSTRING_CHAR_LEN(self); return mrb_int_value(mrb, len); } static mrb_value mrb_str_bytesize(mrb_state *mrb, mrb_value self) { return mrb_int_value(mrb, RSTRING_LEN(self)); } /* 15.2.10.5.1 */ /* * call-seq: * str * integer => new_str * * Copy---Returns a new String containing integer copies of * the receiver. * * "Ho! " * 3 #=> "Ho! Ho! Ho! " */ static mrb_value mrb_str_times(mrb_state *mrb, mrb_value self) { mrb_int len, times; mrb_get_args(mrb, "i", ×); if (times < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument"); } if (mrb_int_mul_overflow(RSTRING_LEN(self), times, &len)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "argument too big"); } struct RString *str2 = str_new(mrb, 0, len); char *p = RSTR_PTR(str2); if (len > 0) { mrb_int n = RSTRING_LEN(self); memcpy(p, RSTRING_PTR(self), n); while (n <= len/2) { memcpy(p + n, p, n); n *= 2; } memcpy(p + n, p, len-n); } p[RSTR_LEN(str2)] = '\0'; RSTR_COPY_SINGLE_BYTE_FLAG(str2, mrb_str_ptr(self)); return mrb_obj_value(str2); } /* -------------------------------------------------------------- */ #define lesser(a,b) (((a)>(b))?(b):(a)) /* ---------------------------*/ /* * call-seq: * mrb_value str1 <=> mrb_value str2 => int * > 1 * = 0 * < -1 */ MRB_API int mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2) { struct RString *s1 = mrb_str_ptr(str1); struct RString *s2 = mrb_str_ptr(str2); mrb_int len1 = RSTR_LEN(s1); mrb_int len2 = RSTR_LEN(s2); mrb_int len = lesser(len1, len2); mrb_int retval = memcmp(RSTR_PTR(s1), RSTR_PTR(s2), len); if (retval == 0) { if (len1 == len2) return 0; if (len1 > len2) return 1; return -1; } if (retval > 0) return 1; return -1; } /* 15.2.10.5.3 */ /* * call-seq: * str <=> other_str => -1, 0, +1 * * Comparison---Returns -1 if other_str is less than, 0 if * other_str is equal to, and +1 if other_str is greater than * str. If the strings are of different lengths, and the strings are * equal when compared up to the shortest length, then the longer string is * considered greater than the shorter one. If the variable $= is * false, the comparison is based on comparing the binary values * of each character in the string. In older versions of Ruby, setting * $= allowed case-insensitive comparisons; this is now deprecated * in favor of using String#casecmp. * * <=> is the basis for the methods <, * <=, >, >=, and between?, * included from module Comparable. The method * String#== does not use Comparable#==. * * "abcdef" <=> "abcde" #=> 1 * "abcdef" <=> "abcdef" #=> 0 * "abcdef" <=> "abcdefg" #=> -1 * "abcdef" <=> "ABCDEF" #=> 1 */ static mrb_value mrb_str_cmp_m(mrb_state *mrb, mrb_value str1) { mrb_value str2 = mrb_get_arg1(mrb); if (!mrb_string_p(str2)) { return mrb_nil_value(); } mrb_int result = mrb_str_cmp(mrb, str1, str2); return mrb_int_value(mrb, result); } static mrb_bool str_eql(mrb_state *mrb, const mrb_value str1, const mrb_value str2) { const mrb_int len = RSTRING_LEN(str1); if (len != RSTRING_LEN(str2)) return FALSE; return (memcmp(RSTRING_PTR(str1), RSTRING_PTR(str2), (size_t)len) == 0); } MRB_API mrb_bool mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2) { if (!mrb_string_p(str2)) return FALSE; return str_eql(mrb, str1, str2); } /* 15.2.10.5.4 */ /* * call-seq: * str == obj => true or false * * Equality--- * If obj is not a String, returns false. * Otherwise, returns false or true * * caution:if str <=> obj returns zero. */ static mrb_value mrb_str_equal_m(mrb_state *mrb, mrb_value str1) { mrb_value str2 = mrb_get_arg1(mrb); return mrb_bool_value(mrb_str_equal(mrb, str1, str2)); } /* ---------------------------------- */ MRB_API mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); struct RString *dup = str_new(mrb, 0, 0); return str_replace(mrb, dup, s); } enum str_convert_range { /* `beg` and `len` are byte unit in `0 ... str.bytesize` */ STR_BYTE_RANGE_CORRECTED = 1, /* `beg` and `len` are char unit in any range */ STR_CHAR_RANGE = 2, /* `beg` and `len` are char unit in `0 ... str.size` */ STR_CHAR_RANGE_CORRECTED = 3, /* `beg` is out of range */ STR_OUT_OF_RANGE = -1 }; static enum str_convert_range str_convert_range(mrb_state *mrb, mrb_value str, mrb_value idx, mrb_value alen, mrb_int *beg, mrb_int *len) { if (!mrb_undef_p(alen)) { *beg = mrb_as_int(mrb, idx); *len = mrb_as_int(mrb, alen); return STR_CHAR_RANGE; } else { switch (mrb_type(idx)) { default: idx = mrb_ensure_int_type(mrb, idx); /* fall through */ case MRB_TT_INTEGER: *beg = mrb_integer(idx); *len = 1; return STR_CHAR_RANGE; case MRB_TT_STRING: *beg = str_index_str(mrb, str, idx, 0); if (*beg < 0) { break; } *len = RSTRING_LEN(idx); return STR_BYTE_RANGE_CORRECTED; case MRB_TT_RANGE: *len = RSTRING_CHAR_LEN(str); switch (mrb_range_beg_len(mrb, idx, beg, len, *len, TRUE)) { case MRB_RANGE_OK: return STR_CHAR_RANGE_CORRECTED; case MRB_RANGE_OUT: return STR_OUT_OF_RANGE; default: break; } } } return STR_OUT_OF_RANGE; } mrb_value mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value idx, mrb_value alen) { mrb_int beg, len; switch (str_convert_range(mrb, str, idx, alen, &beg, &len)) { case STR_CHAR_RANGE_CORRECTED: return str_subseq(mrb, str, beg, len); case STR_CHAR_RANGE: str = str_substr(mrb, str, beg, len); if (mrb_undef_p(alen) && !mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value(); return str; case STR_BYTE_RANGE_CORRECTED: if (mrb_string_p(idx)) { return mrb_str_dup(mrb, idx); } else { return mrb_str_byte_subseq(mrb, str, beg, len); } case STR_OUT_OF_RANGE: default: return mrb_nil_value(); } } /* 15.2.10.5.6 */ /* 15.2.10.5.34 */ /* * call-seq: * str[int] => int or nil * str[int, int] => new_str or nil * str[range] => new_str or nil * str[other_str] => new_str or nil * str.slice(int) => int or nil * str.slice(int, int) => new_str or nil * str.slice(range) => new_str or nil * str.slice(other_str) => new_str or nil * * Element Reference---If passed a single Integer, returns the code * of the character at that position. If passed two Integer * objects, returns a substring starting at the offset given by the first, and * a length given by the second. If given a range, a substring containing * characters at offsets given by the range is returned. In all three cases, if * an offset is negative, it is counted from the end of str. Returns * nil if the initial offset falls outside the string, the length * is negative, or the beginning of the range is greater than the end. * * If a String is given, that string is returned if it occurs in * str. In both cases, nil is returned if there is no * match. * * a = "hello there" * a[1] #=> 101(1.8.7) "e"(1.9.2) * a[1.1] #=> "e"(1.9.2) * a[1,3] #=> "ell" * a[1..3] #=> "ell" * a[-3,2] #=> "er" * a[-4..-2] #=> "her" * a[12..-1] #=> nil * a[-2..-4] #=> "" * a["lo"] #=> "lo" * a["bye"] #=> nil */ static mrb_value mrb_str_aref_m(mrb_state *mrb, mrb_value str) { mrb_value a1, a2; if (mrb_get_args(mrb, "o|o", &a1, &a2) == 1) { a2 = mrb_undef_value(); } return mrb_str_aref(mrb, str, a1, a2); } static mrb_noreturn void str_out_of_index(mrb_state *mrb, mrb_value index) { mrb_raisef(mrb, E_INDEX_ERROR, "index %v out of string", index); } static mrb_value str_replace_partial(mrb_state *mrb, mrb_value src, mrb_int pos, mrb_int end, mrb_value rep) { const mrb_int shrink_threshold = 256; struct RString *str = mrb_str_ptr(src); mrb_int len = RSTR_LEN(str); mrb_int replen, newlen; char *strp; if (end > len) { end = len; } if (pos < 0 || pos > len) { str_out_of_index(mrb, mrb_int_value(mrb, pos)); } replen = (mrb_nil_p(rep) ? 0 : RSTRING_LEN(rep)); if (mrb_int_add_overflow(replen, len - (end - pos), &newlen)) { mrb_raise(mrb, E_RUNTIME_ERROR, "string size too big"); } mrb_str_modify(mrb, str); if (len < newlen) { resize_capa(mrb, str, newlen); } strp = RSTR_PTR(str); memmove(strp + newlen - (len - end), strp + end, len - end); if (!mrb_nil_p(rep)) { memmove(strp + pos, RSTRING_PTR(rep), replen); } RSTR_SET_LEN(str, newlen); strp[newlen] = '\0'; if (len - newlen >= shrink_threshold) { resize_capa(mrb, str, newlen); } return src; } #define IS_EVSTR(p,e) ((p) < (e) && (*(p) == '$' || *(p) == '@' || *(p) == '{')) static mrb_value str_escape(mrb_state *mrb, mrb_value str, mrb_bool inspect) { const char *p, *pend; char buf[4]; /* `\x??` or UTF-8 character */ mrb_value result = mrb_str_new_lit(mrb, "\""); #ifdef MRB_UTF8_STRING uint32_t sb_flag = MRB_STR_SINGLE_BYTE; #endif p = RSTRING_PTR(str); pend = RSTRING_END(str); for (;p < pend; p++) { unsigned char c, cc; #ifdef MRB_UTF8_STRING if (inspect) { mrb_int clen = mrb_utf8len(p, pend); if (clen > 1) { mrb_str_cat(mrb, result, p, clen); p += clen-1; sb_flag = 0; continue; } } #endif c = *p; if (c == '"'|| c == '\\' || (c == '#' && IS_EVSTR(p+1, pend))) { buf[0] = '\\'; buf[1] = c; mrb_str_cat(mrb, result, buf, 2); continue; } if (ISPRINT(c)) { buf[0] = c; mrb_str_cat(mrb, result, buf, 1); continue; } switch (c) { case '\n': cc = 'n'; break; case '\r': cc = 'r'; break; case '\t': cc = 't'; break; case '\f': cc = 'f'; break; case '\013': cc = 'v'; break; case '\010': cc = 'b'; break; case '\007': cc = 'a'; break; case 033: cc = 'e'; break; default: cc = 0; break; } buf[0] = '\\'; if (cc) { buf[1] = (char)cc; mrb_str_cat(mrb, result, buf, 2); } else { buf[1] = 'x'; buf[3] = mrb_digitmap[c % 16]; c /= 16; buf[2] = mrb_digitmap[c % 16]; mrb_str_cat(mrb, result, buf, 4); } } mrb_str_cat_lit(mrb, result, "\""); #ifdef MRB_UTF8_STRING if (inspect) { mrb_str_ptr(str)->flags |= sb_flag; mrb_str_ptr(result)->flags |= sb_flag; } else { RSTR_SET_SINGLE_BYTE_FLAG(mrb_str_ptr(result)); } #endif return result; } static void mrb_str_aset(mrb_state *mrb, mrb_value str, mrb_value idx, mrb_value alen, mrb_value replace) { mrb_int beg, len, charlen; mrb_ensure_string_type(mrb, replace); switch (str_convert_range(mrb, str, idx, alen, &beg, &len)) { case STR_OUT_OF_RANGE: default: mrb_raise(mrb, E_INDEX_ERROR, "string not matched"); case STR_CHAR_RANGE: if (len < 0) { mrb_raisef(mrb, E_INDEX_ERROR, "negative length %v", alen); } charlen = RSTRING_CHAR_LEN(str); if (beg < 0) { beg += charlen; } if (beg < 0 || beg > charlen) { str_out_of_index(mrb, idx); } /* fall through */ case STR_CHAR_RANGE_CORRECTED: beg = chars2bytes(str, 0, beg); len = chars2bytes(str, beg, len); /* fall through */ case STR_BYTE_RANGE_CORRECTED: if (mrb_int_add_overflow(beg, len, &len)) { mrb_raise(mrb, E_RUNTIME_ERROR, "string index too big"); } str_replace_partial(mrb, str, beg, len, replace); } } /* * call-seq: * str[int] = replace * str[int, int] = replace * str[range] = replace * str[other_str] = replace * * Modify +self+ by replacing the content of +self+. * The portion of the string affected is determined using the same criteria as +String#[]+. * The return value of this expression is +replace+. */ static mrb_value mrb_str_aset_m(mrb_state *mrb, mrb_value str) { mrb_value idx, alen, replace; switch (mrb_get_args(mrb, "oo|S!", &idx, &alen, &replace)) { case 2: replace = alen; alen = mrb_undef_value(); break; case 3: break; } mrb_str_aset(mrb, str, idx, alen, replace); return replace; } /* 15.2.10.5.8 */ /* * call-seq: * str.capitalize! => str or nil * * Modifies str by converting the first character to uppercase and the * remainder to lowercase. Returns nil if no changes are made. * * a = "hello" * a.capitalize! #=> "Hello" * a #=> "Hello" * a.capitalize! #=> nil */ static mrb_value mrb_str_capitalize_bang(mrb_state *mrb, mrb_value str) { mrb_bool modify = FALSE; struct RString *s = mrb_str_ptr(str); mrb_int len = RSTR_LEN(s); mrb_str_modify_keep_ascii(mrb, s); char *p = RSTR_PTR(s); char *pend = RSTR_PTR(s) + len; if (len == 0 || p == NULL) return mrb_nil_value(); if (ISLOWER(*p)) { *p = TOUPPER(*p); modify = TRUE; } while (++p < pend) { if (ISUPPER(*p)) { *p = TOLOWER(*p); modify = TRUE; } } if (modify) return str; return mrb_nil_value(); } /* 15.2.10.5.7 */ /* * call-seq: * str.capitalize => new_str * * Returns a copy of str with the first character converted to uppercase * and the remainder to lowercase. * * "hello".capitalize #=> "Hello" * "HELLO".capitalize #=> "Hello" * "123ABC".capitalize #=> "123abc" */ static mrb_value mrb_str_capitalize(mrb_state *mrb, mrb_value self) { mrb_value str; str = mrb_str_dup(mrb, self); mrb_str_capitalize_bang(mrb, str); return str; } /* 15.2.10.5.10 */ /* * call-seq: * str.chomp!(separator="\n") => str or nil * * Modifies str in place as described for String#chomp, * returning str, or nil if no modifications were made. */ static mrb_value mrb_str_chomp_bang(mrb_state *mrb, mrb_value str) { mrb_value rs; mrb_int newline; char *p, *pp; mrb_int rslen; mrb_int len; mrb_int argc; struct RString *s = mrb_str_ptr(str); argc = mrb_get_args(mrb, "|S", &rs); mrb_str_modify_keep_ascii(mrb, s); len = RSTR_LEN(s); if (argc == 0) { if (len == 0) return mrb_nil_value(); smart_chomp: if (RSTR_PTR(s)[len-1] == '\n') { RSTR_SET_LEN(s, RSTR_LEN(s) - 1); if (RSTR_LEN(s) > 0 && RSTR_PTR(s)[RSTR_LEN(s)-1] == '\r') { RSTR_SET_LEN(s, RSTR_LEN(s) - 1); } } else if (RSTR_PTR(s)[len-1] == '\r') { RSTR_SET_LEN(s, RSTR_LEN(s) - 1); } else { return mrb_nil_value(); } RSTR_PTR(s)[RSTR_LEN(s)] = '\0'; return str; } if (len == 0 || mrb_nil_p(rs)) return mrb_nil_value(); p = RSTR_PTR(s); rslen = RSTRING_LEN(rs); if (rslen == 0) { while (len>0 && p[len-1] == '\n') { len--; if (len>0 && p[len-1] == '\r') len--; } if (len < RSTR_LEN(s)) { RSTR_SET_LEN(s, len); p[len] = '\0'; return str; } return mrb_nil_value(); } if (rslen > len) return mrb_nil_value(); newline = RSTRING_PTR(rs)[rslen-1]; if (rslen == 1 && newline == '\n') newline = RSTRING_PTR(rs)[rslen-1]; if (rslen == 1 && newline == '\n') goto smart_chomp; pp = p + len - rslen; if (p[len-1] == newline && (rslen <= 1 || memcmp(RSTRING_PTR(rs), pp, rslen) == 0)) { RSTR_SET_LEN(s, len - rslen); p[RSTR_LEN(s)] = '\0'; return str; } return mrb_nil_value(); } /* 15.2.10.5.9 */ /* * call-seq: * str.chomp(separator="\n") => new_str * * Returns a new String with the given record separator removed * from the end of str (if present). chomp also removes * carriage return characters (that is it will remove \n, * \r, and \r\n). * * "hello".chomp #=> "hello" * "hello\n".chomp #=> "hello" * "hello\r\n".chomp #=> "hello" * "hello\n\r".chomp #=> "hello\n" * "hello\r".chomp #=> "hello" * "hello \n there".chomp #=> "hello \n there" * "hello".chomp("llo") #=> "he" */ static mrb_value mrb_str_chomp(mrb_state *mrb, mrb_value self) { mrb_value str; str = mrb_str_dup(mrb, self); mrb_str_chomp_bang(mrb, str); return str; } /* 15.2.10.5.12 */ /* * call-seq: * str.chop! => str or nil * * Processes str as for String#chop, returning str, * or nil if str is the empty string. See also * String#chomp!. */ static mrb_value mrb_str_chop_bang(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); mrb_str_modify_keep_ascii(mrb, s); if (RSTR_LEN(s) > 0) { mrb_int len; #ifdef MRB_UTF8_STRING const char* t = RSTR_PTR(s), *p = t; const char* e = p + RSTR_LEN(s); while (p=e) break; p += clen; } len = p - t; #else len = RSTR_LEN(s) - 1; #endif if (RSTR_PTR(s)[len] == '\n') { if (len > 0 && RSTR_PTR(s)[len-1] == '\r') { len--; } } RSTR_SET_LEN(s, len); RSTR_PTR(s)[len] = '\0'; return str; } return mrb_nil_value(); } /* 15.2.10.5.11 */ /* * call-seq: * str.chop => new_str * * Returns a new String with the last character removed. If the * string ends with \r\n, both characters are removed. Applying * chop to an empty string returns an empty * string. String#chomp is often a safer alternative, as it leaves * the string unchanged if it doesn't end in a record separator. * * "string\r\n".chop #=> "string" * "string\n\r".chop #=> "string\n" * "string\n".chop #=> "string" * "string".chop #=> "strin" * "x".chop #=> "" */ static mrb_value mrb_str_chop(mrb_state *mrb, mrb_value self) { mrb_value str; str = mrb_str_dup(mrb, self); mrb_str_chop_bang(mrb, str); return str; } /* 15.2.10.5.14 */ /* * call-seq: * str.downcase! => str or nil * * Downcases the contents of str, returning nil if no * changes were made. */ static mrb_value mrb_str_downcase_bang(mrb_state *mrb, mrb_value str) { char *p, *pend; mrb_bool modify = FALSE; struct RString *s = mrb_str_ptr(str); mrb_str_modify_keep_ascii(mrb, s); p = RSTR_PTR(s); pend = RSTR_PTR(s) + RSTR_LEN(s); while (p < pend) { if (ISUPPER(*p)) { *p = TOLOWER(*p); modify = TRUE; } p++; } if (modify) return str; return mrb_nil_value(); } /* 15.2.10.5.13 */ /* * call-seq: * str.downcase => new_str * * Returns a copy of str with all uppercase letters replaced with their * lowercase counterparts. The operation is locale insensitive---only * characters 'A' to 'Z' are affected. * * "hEllO".downcase #=> "hello" */ static mrb_value mrb_str_downcase(mrb_state *mrb, mrb_value self) { mrb_value str; str = mrb_str_dup(mrb, self); mrb_str_downcase_bang(mrb, str); return str; } /* 15.2.10.5.16 */ /* * call-seq: * str.empty? => true or false * * Returns true if str has a length of zero. * * "hello".empty? #=> false * "".empty? #=> true */ static mrb_value mrb_str_empty_p(mrb_state *mrb, mrb_value self) { struct RString *s = mrb_str_ptr(self); return mrb_bool_value(RSTR_LEN(s) == 0); } /* 15.2.10.5.17 */ /* * call-seq: * str.eql?(other) => true or false * * Two strings are equal if the have the same length and content. */ static mrb_value mrb_str_eql(mrb_state *mrb, mrb_value self) { mrb_value str2 = mrb_get_arg1(mrb); mrb_bool eql_p = (mrb_string_p(str2)) && str_eql(mrb, self, str2); return mrb_bool_value(eql_p); } MRB_API mrb_value mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) { return str_substr(mrb, str, beg, len); } /* * 32-bit magic FNV-0 and FNV-1 prime b */ #define FNV_32_PRIME ((uint32_t)0x01000193) #define FNV1_32_INIT ((uint32_t)0x811c9dc5) uint32_t mrb_byte_hash_step(const uint8_t *s, mrb_int len, uint32_t hval) { const uint8_t *send = s + len; /* * FNV-1 hash each octet in the buffer */ while (s < send) { /* multiply by the 32-bit FNV magic prime mod 2^32 */ #if defined(NO_FNV_GCC_OPTIMIZATION) hval *= FNV_32_PRIME; #else hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24); #endif /* xor the bottom with the current octet */ hval ^= (uint32_t)*s++; } /* return our new hash value */ return hval; } uint32_t mrb_byte_hash(const uint8_t *s, mrb_int len) { return mrb_byte_hash_step(s, len, FNV1_32_INIT); } uint32_t mrb_str_hash(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); return mrb_byte_hash((uint8_t*)RSTR_PTR(s), RSTR_LEN(s)); } /* 15.2.10.5.20 */ /* * call-seq: * str.hash => int * * Return a hash based on the string's length and content. */ static mrb_value mrb_str_hash_m(mrb_state *mrb, mrb_value self) { mrb_int key = mrb_str_hash(mrb, self); return mrb_int_value(mrb, key); } /* 15.2.10.5.21 */ /* * call-seq: * str.include? other_str => true or false * str.include? int => true or false * * Returns true if str contains the given string or * character. * * "hello".include? "lo" #=> true * "hello".include? "ol" #=> false * "hello".include? ?h #=> true */ static mrb_value mrb_str_include(mrb_state *mrb, mrb_value self) { mrb_value str2; mrb_get_args(mrb, "S", &str2); if (str_index_str(mrb, self, str2, 0) < 0) return mrb_bool_value(FALSE); return mrb_bool_value(TRUE); } /* * call-seq: * str.byteindex(substring, offset = 0) -> integer or nil * * Returns the \Integer byte-based index of the first occurrence of the given +substring+, * or +nil+ if none found: * * 'foo'.byteindex('f') # => 0 * 'foo'.byteindex('oo') # => 1 * 'foo'.byteindex('ooo') # => nil */ static mrb_value mrb_str_byteindex_m(mrb_state *mrb, mrb_value str) { mrb_value sub; mrb_int pos; if (mrb_get_args(mrb, "S|i", &sub, &pos) == 1) { pos = 0; } else if (pos < 0) { pos += RSTRING_LEN(str); if (pos < 0) { return mrb_nil_value(); } } pos = str_index_str(mrb, str, sub, pos); if (pos == -1) return mrb_nil_value(); return mrb_int_value(mrb, pos); } /* 15.2.10.5.22 */ /* * call-seq: * str.index(substring [, offset]) => int or nil * * Returns the index of the first occurrence of the given * substring. Returns nil if not found. * If the second parameter is present, it * specifies the position in the string to begin the search. * * "hello".index('l') #=> 2 * "hello".index('lo') #=> 3 * "hello".index('a') #=> nil * "hello".index('l', -2) #=> 3 */ #ifdef MRB_UTF8_STRING static mrb_value mrb_str_index_m(mrb_state *mrb, mrb_value str) { if (RSTR_SINGLE_BYTE_P(mrb_str_ptr(str))) { return mrb_str_byteindex_m(mrb, str); } mrb_value sub; mrb_int pos; if (mrb_get_args(mrb, "S|i", &sub, &pos) == 1) { pos = 0; } else if (pos < 0) { mrb_int clen = RSTRING_CHAR_LEN(str); pos += clen; if (pos < 0) { return mrb_nil_value(); } } pos = str_index_str_by_char(mrb, str, sub, pos); if (pos == -1) return mrb_nil_value(); return mrb_int_value(mrb, pos); } #else #define mrb_str_index_m mrb_str_byteindex_m #endif /* 15.2.10.5.24 */ /* 15.2.10.5.28 */ /* * call-seq: * str.replace(other_str) => str * * s = "hello" #=> "hello" * s.replace "world" #=> "world" */ static mrb_value mrb_str_replace(mrb_state *mrb, mrb_value str) { mrb_value str2; mrb_get_args(mrb, "S", &str2); return str_replace(mrb, mrb_str_ptr(str), mrb_str_ptr(str2)); } /* 15.2.10.5.23 */ /* * call-seq: * String.new(str="") => new_str * * Returns a new string object containing a copy of str. */ static mrb_value mrb_str_init(mrb_state *mrb, mrb_value self) { mrb_value str2; if (mrb_get_args(mrb, "|S", &str2) == 0) { str2 = mrb_str_new(mrb, 0, 0); } str_replace(mrb, mrb_str_ptr(self), mrb_str_ptr(str2)); return self; } /* 15.2.10.5.25 */ /* 15.2.10.5.41 */ /* * call-seq: * str.intern => symbol * str.to_sym => symbol * * Returns the Symbol corresponding to str, creating the * symbol if it did not previously exist. * * "Koala".intern #=> :Koala * s = 'cat'.to_sym #=> :cat * s == :cat #=> true * s = '@cat'.to_sym #=> :@cat * s == :@cat #=> true * * This can also be used to create symbols that cannot be represented using the * :xxx notation. * * 'cat and dog'.to_sym #=> :"cat and dog" */ MRB_API mrb_value mrb_str_intern(mrb_state *mrb, mrb_value self) { return mrb_symbol_value(mrb_intern_str(mrb, self)); } /* ---------------------------------- */ MRB_API mrb_value mrb_obj_as_string(mrb_state *mrb, mrb_value obj) { switch (mrb_type(obj)) { case MRB_TT_STRING: return obj; case MRB_TT_SYMBOL: return mrb_sym_str(mrb, mrb_symbol(obj)); case MRB_TT_INTEGER: return mrb_integer_to_str(mrb, obj, 10); case MRB_TT_SCLASS: case MRB_TT_CLASS: case MRB_TT_MODULE: return mrb_mod_to_s(mrb, obj); default: return mrb_type_convert(mrb, obj, MRB_TT_STRING, MRB_SYM(to_s)); } } MRB_API mrb_value mrb_ptr_to_str(mrb_state *mrb, void *p) { struct RString *p_str; char *p1; char *p2; uintptr_t n = (uintptr_t)p; p_str = str_new(mrb, NULL, 2 + sizeof(uintptr_t) * CHAR_BIT / 4); p1 = RSTR_PTR(p_str); *p1++ = '0'; *p1++ = 'x'; p2 = p1; do { *p2++ = mrb_digitmap[n % 16]; n /= 16; } while (n > 0); *p2 = '\0'; RSTR_SET_LEN(p_str, (mrb_int)(p2 - RSTR_PTR(p_str))); while (p1 < p2) { const char c = *p1; *p1++ = *--p2; *p2 = c; } return mrb_obj_value(p_str); } static inline void str_reverse(char *p, char *e) { char c; while (p < e) { c = *p; *p++ = *e; *e-- = c; } } /* 15.2.10.5.30 */ /* * call-seq: * str.reverse! => str * * Reverses str in place. */ static mrb_value mrb_str_reverse_bang(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); char *p, *e; #ifdef MRB_UTF8_STRING mrb_int utf8_len = RSTRING_CHAR_LEN(str); mrb_int len = RSTR_LEN(s); if (utf8_len < 2) return str; if (utf8_len < len) { mrb_str_modify(mrb, s); p = RSTR_PTR(s); e = p + RSTR_LEN(s); while (p 1) { mrb_str_modify(mrb, s); goto bytes; } return str; bytes: p = RSTR_PTR(s); e = p + RSTR_LEN(s) - 1; str_reverse(p, e); return str; } /* ---------------------------------- */ /* 15.2.10.5.29 */ /* * call-seq: * str.reverse => new_str * * Returns a new string with the characters from str in reverse order. * * "stressed".reverse #=> "desserts" */ static mrb_value mrb_str_reverse(mrb_state *mrb, mrb_value str) { mrb_value str2 = mrb_str_dup(mrb, str); mrb_str_reverse_bang(mrb, str2); return str2; } /* * call-seq: * byterindex(substring, offset = self.bytesize) -> integer or nil * * Returns the \Integer byte-based index of the _last_ occurrence of the given +substring+, * or +nil+ if none found: * * 'foo'.byterindex('f') # => 0 * 'foo'.byterindex('o') # => 2 * 'foo'.byterindex('oo') # => 1 * 'foo'.byterindex('ooo') # => nil */ static mrb_value mrb_str_byterindex_m(mrb_state *mrb, mrb_value str) { mrb_value sub; mrb_int pos; mrb_int len = RSTRING_LEN(str); if (mrb_get_args(mrb, "S|i", &sub, &pos) == 1) { pos = len; } else { if (pos < 0) { pos += len; if (pos < 0) { return mrb_nil_value(); } } if (pos > len) pos = len; } pos = str_rindex(mrb, str, sub, pos); if (pos < 0) { return mrb_nil_value(); } return mrb_int_value(mrb, pos); } /* 15.2.10.5.31 */ /* * call-seq: * str.rindex(substring [, offset]) => int or nil * * Returns the index of the last occurrence of the given substring. * Returns nil if not found. If the second parameter is * present, it specifies the position in the string to end the * search---characters beyond this point will not be considered. * * "hello".rindex('e') #=> 1 * "hello".rindex('l') #=> 3 * "hello".rindex('a') #=> nil * "hello".rindex('l', 2) #=> 2 */ #ifdef MRB_UTF8_STRING static mrb_value mrb_str_rindex_m(mrb_state *mrb, mrb_value str) { if (RSTR_SINGLE_BYTE_P(mrb_str_ptr(str))) { return mrb_str_byterindex_m(mrb, str); } mrb_value sub; mrb_int pos; if (mrb_get_args(mrb, "S|i", &sub, &pos) == 1) { pos = RSTRING_LEN(str); } else if (pos >= 0) { pos = chars2bytes(str, 0, pos); } else { const char *p = RSTRING_PTR(str); const char *e = RSTRING_END(str); while (pos++ < 0 && p < e) { e = char_backtrack(p, e); } if (p == e) return mrb_nil_value(); pos = (mrb_int)(e - p); } pos = str_rindex(mrb, str, sub, pos); if (pos >= 0) { pos = bytes2chars(str, pos); if (pos < 0) return mrb_nil_value(); return mrb_int_value(mrb, pos); } return mrb_nil_value(); } #else #define mrb_str_rindex_m mrb_str_byterindex_m #endif /* 15.2.10.5.35 */ /* * call-seq: * str.split(separator=nil, [limit]) => anArray * * Divides str into substrings based on a delimiter, returning an array * of these substrings. * * If separator is a String, then its contents are used as * the delimiter when splitting str. If separator is a single * space, str is split on whitespace, with leading whitespace and runs * of contiguous whitespace characters ignored. * * If separator is omitted or nil (which is the default), * str is split on whitespace as if ' ' were specified. * * If the limit parameter is omitted, trailing null fields are * suppressed. If limit is a positive number, at most that number of * fields will be returned (if limit is 1, the entire * string is returned as the only entry in an array). If negative, there is no * limit to the number of fields returned, and trailing null fields are not * suppressed. * * " now's the time".split #=> ["now's", "the", "time"] * " now's the time".split(' ') #=> ["now's", "the", "time"] * * "mellow yellow".split("ello") #=> ["m", "w y", "w"] * "1,2,,3,4,,".split(',') #=> ["1", "2", "", "3", "4"] * "1,2,,3,4,,".split(',', 4) #=> ["1", "2", "", "3,4,,"] * "1,2,,3,4,,".split(',', -4) #=> ["1", "2", "", "3", "4", "", ""] */ static mrb_value mrb_str_split_m(mrb_state *mrb, mrb_value str) { mrb_int argc; mrb_value spat = mrb_nil_value(); enum {awk, string} split_type = string; mrb_int i = 0; mrb_int beg; mrb_int end; mrb_int lim = 0; mrb_bool lim_p; mrb_value result, tmp; argc = mrb_get_args(mrb, "|oi", &spat, &lim); lim_p = (lim > 0 && argc == 2); if (argc == 2) { if (lim == 1) { if (RSTRING_LEN(str) == 0) return mrb_ary_new_capa(mrb, 0); return mrb_ary_new_from_values(mrb, 1, &str); } i = 1; } if (argc == 0 || mrb_nil_p(spat)) { split_type = awk; } else if (!mrb_string_p(spat)) { mrb_raise(mrb, E_TYPE_ERROR, "expected String"); } else if (RSTRING_LEN(spat) == 1 && RSTRING_PTR(spat)[0] == ' ') { split_type = awk; } result = mrb_ary_new(mrb); beg = 0; if (split_type == awk) { mrb_bool skip = TRUE; mrb_int idx = 0; mrb_int str_len = RSTRING_LEN(str); unsigned int c; int ai = mrb_gc_arena_save(mrb); idx = end = beg; while (idx < str_len) { c = (unsigned char)RSTRING_PTR(str)[idx++]; if (skip) { if (ISSPACE(c)) { beg = idx; } else { end = idx; skip = FALSE; if (lim_p && lim <= i) break; } } else if (ISSPACE(c)) { mrb_ary_push(mrb, result, mrb_str_byte_subseq(mrb, str, beg, end-beg)); mrb_gc_arena_restore(mrb, ai); skip = TRUE; beg = idx; if (lim_p) i++; } else { end = idx; } } } else { /* split_type == string */ mrb_int str_len = RSTRING_LEN(str); mrb_int pat_len = RSTRING_LEN(spat); mrb_int idx = 0; int ai = mrb_gc_arena_save(mrb); while (idx < str_len) { if (pat_len > 0) { end = mrb_memsearch(RSTRING_PTR(spat), pat_len, RSTRING_PTR(str)+idx, str_len - idx); if (end < 0) break; } else { end = chars2bytes(str, idx, 1); } mrb_ary_push(mrb, result, mrb_str_byte_subseq(mrb, str, idx, end)); mrb_gc_arena_restore(mrb, ai); idx += end + pat_len; if (lim_p && lim <= ++i) break; } beg = idx; } if (RSTRING_LEN(str) > 0 && (lim_p || RSTRING_LEN(str) > beg || lim < 0)) { if (RSTRING_LEN(str) == beg) { tmp = mrb_str_new(mrb, 0, 0); } else { tmp = mrb_str_byte_subseq(mrb, str, beg, RSTRING_LEN(str)-beg); } mrb_ary_push(mrb, result, tmp); } if (!lim_p && lim == 0) { mrb_int len; while ((len = RARRAY_LEN(result)) > 0 && (tmp = RARRAY_PTR(result)[len-1], RSTRING_LEN(tmp) == 0)) mrb_ary_pop(mrb, result); } return result; } static mrb_bool trailingbad(const char *str, const char *p, const char *pend) { if (p == str) return TRUE; /* no number */ if (*(p - 1) == '_') return TRUE; /* trailing '_' */ while (p=pend) { if (badcheck) goto bad; return mrb_fixnum_value(0); } if (*p == '0') { /* squeeze preceding 0s */ p++; while (p= base) { break; } if (mrb_int_mul_overflow(n, base, &n)) goto overflow; if (MRB_INT_MAX - c < n) { if (sign == 0 && MRB_INT_MAX - n == c - 1) { n = MRB_INT_MIN; sign = 1; break; } overflow: #ifdef MRB_USE_BIGINT ; const char *p3 = p2; while (p3 < pend) { char c = TOLOWER(*p3); const char *p4 = strchr(mrb_digitmap, c); if (p4 == NULL && c != '_') break; if (p4 - mrb_digitmap >= base) break; p3++; } if (badcheck && trailingbad(str, p, pend)) goto bad; return mrb_bint_new_str(mrb, p2, (mrb_int)(p3-p2), sign ? base : -base); #else mrb_raisef(mrb, E_RANGE_ERROR, "string (%l) too big for integer", str, pend-str); #endif } n += c; } val = (mrb_int)n; if (badcheck && trailingbad(str, p, pend)) goto bad; return mrb_int_value(mrb, sign ? val : -val); bad: mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for number(%!l)", str, pend-str); /* not reached */ return mrb_fixnum_value(0); } /* obsolete: use RSTRING_CSTR() or mrb_string_cstr() */ MRB_API const char* mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr) { struct RString *ps; const char *p; mrb_int len; mrb_ensure_string_type(mrb, *ptr); ps = mrb_str_ptr(*ptr); check_null_byte(mrb, ps); p = RSTR_PTR(ps); len = RSTR_LEN(ps); if (p == NULL) return ""; if (p[len] == '\0') { return p; } /* * Even after str_modify_keep_ascii(), NULL termination is not ensured if * RSTR_SET_LEN() is used explicitly (e.g. String#delete_suffix!). */ str_unshare_buffer(mrb, ps); RSTR_PTR(ps)[len] = '\0'; return RSTR_PTR(ps); } MRB_API const char* mrb_string_cstr(mrb_state *mrb, mrb_value str) { return mrb_string_value_cstr(mrb, &str); } MRB_API mrb_value mrb_str_to_integer(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck) { const char *s; mrb_int len; mrb_ensure_string_type(mrb, str); s = RSTRING_PTR(str); len = RSTRING_LEN(str); return mrb_str_len_to_integer(mrb, s, len, base, badcheck); } /* 15.2.10.5.38 */ /* * call-seq: * str.to_i(base=10) => integer * * Returns the result of interpreting leading characters in str as an * integer base base (between 2 and 36). Extraneous characters past the * end of a valid number are ignored. If there is not a valid number at the * start of str, 0 is returned. This method never raises an * exception. * * "12345".to_i #=> 12345 * "99 red balloons".to_i #=> 99 * "0a".to_i #=> 0 * "0a".to_i(16) #=> 10 * "hello".to_i #=> 0 * "1100101".to_i(2) #=> 101 * "1100101".to_i(8) #=> 294977 * "1100101".to_i(10) #=> 1100101 * "1100101".to_i(16) #=> 17826049 */ static mrb_value mrb_str_to_i(mrb_state *mrb, mrb_value self) { mrb_int base = 10; mrb_get_args(mrb, "|i", &base); if (base < 0 || 36 < base) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %i", base); } return mrb_str_to_integer(mrb, self, base, FALSE); } #ifndef MRB_NO_FLOAT static double mrb_str_len_to_dbl(mrb_state *mrb, const char *s, size_t len, mrb_bool badcheck) { char buf[DBL_DIG * 4 + 20]; const char *p = s, *p2; const char *pend = p + len; char *end; char *n; char prev = 0; double d; mrb_bool dot = FALSE; if (!p) return 0.0; while (p 2 && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { mrb_value x; if (!badcheck) return 0.0; x = mrb_str_len_to_integer(mrb, p, pend-p, 0, badcheck); if (mrb_integer_p(x)) d = (double)mrb_integer(x); else /* if (mrb_float_p(x)) */ d = mrb_float(x); return d; } while (p < pend) { if (!*p) { if (badcheck) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string for Float contains null byte"); /* not reached */ } pend = p; p = p2; goto nocopy; } if (!badcheck && *p == ' ') { pend = p; p = p2; goto nocopy; } if (*p == '_') break; p++; } p = p2; n = buf; while (p < pend) { char c = *p++; if (c == '.') dot = TRUE; if (c == '_') { /* remove an underscore between digits */ if (n == buf || !ISDIGIT(prev) || p == pend) { if (badcheck) goto bad; break; } } else if (badcheck && prev == '_' && !ISDIGIT(c)) goto bad; else { const char *bend = buf+sizeof(buf)-1; if (n==bend) { /* buffer overflow */ if (dot) break; /* cut off remaining fractions */ return INFINITY; } *n++ = c; } prev = c; } *n = '\0'; p = buf; pend = n; nocopy: if (mrb_read_float(p, &end, &d) == FALSE) { if (badcheck) { bad: mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for float(%!s)", s); /* not reached */ } return 0.0; } if (badcheck) { if (!end || p == end) goto bad; while (end float * * Returns the result of interpreting leading characters in str as a * floating-point number. Extraneous characters past the end of a valid number * are ignored. If there is not a valid number at the start of str, * 0.0 is returned. This method never raises an exception. * * "123.45e1".to_f #=> 1234.5 * "45.67 degrees".to_f #=> 45.67 * "thx1138".to_f #=> 0.0 */ static mrb_value mrb_str_to_f(mrb_state *mrb, mrb_value self) { return mrb_float_value(mrb, mrb_str_to_dbl(mrb, self, FALSE)); } #endif /* 15.2.10.5.40 */ /* * call-seq: * str.to_s => str * * Returns the receiver. */ static mrb_value mrb_str_to_s(mrb_state *mrb, mrb_value self) { if (mrb_obj_class(mrb, self) != mrb->string_class) { return mrb_str_dup(mrb, self); } return self; } /* 15.2.10.5.43 */ /* * call-seq: * str.upcase! => str or nil * * Upcases the contents of str, returning nil if no changes * were made. */ static mrb_value mrb_str_upcase_bang(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); char *p, *pend; mrb_bool modify = FALSE; mrb_str_modify_keep_ascii(mrb, s); p = RSTRING_PTR(str); pend = RSTRING_END(str); while (p < pend) { if (ISLOWER(*p)) { *p = TOUPPER(*p); modify = TRUE; } p++; } if (modify) return str; return mrb_nil_value(); } /* 15.2.10.5.42 */ /* * call-seq: * str.upcase => new_str * * Returns a copy of str with all lowercase letters replaced with their * uppercase counterparts. The operation is locale insensitive---only * characters 'a' to 'z' are affected. * * "hEllO".upcase #=> "HELLO" */ static mrb_value mrb_str_upcase(mrb_state *mrb, mrb_value self) { mrb_value str; str = mrb_str_dup(mrb, self); mrb_str_upcase_bang(mrb, str); return str; } /* * call-seq: * str.dump -> new_str * * Produces a version of str with all nonprinting characters replaced by * \nnn notation and all special characters escaped. */ mrb_value mrb_str_dump(mrb_state *mrb, mrb_value str) { return str_escape(mrb, str, FALSE); } MRB_API mrb_value mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len) { struct RString *s = mrb_str_ptr(str); mrb_int capa; mrb_int total; ptrdiff_t off = -1; if (len == 0) return str; mrb_str_modify(mrb, s); if (ptr >= RSTR_PTR(s) && ptr <= RSTR_PTR(s) + (size_t)RSTR_LEN(s)) { off = ptr - RSTR_PTR(s); } capa = RSTR_CAPA(s); if (mrb_int_add_overflow(RSTR_LEN(s), len, &total)) { size_error: mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); } if (capa <= total) { if (capa == 0) capa = 1; while (capa <= total) { if (mrb_int_mul_overflow(capa, 2, &capa)) goto size_error; } resize_capa(mrb, s, capa); } if (off != -1) { ptr = RSTR_PTR(s) + off; } memcpy(RSTR_PTR(s) + RSTR_LEN(s), ptr, len); RSTR_SET_LEN(s, total); RSTR_PTR(s)[total] = '\0'; /* sentinel */ return str; } MRB_API mrb_value mrb_str_cat_cstr(mrb_state *mrb, mrb_value str, const char *ptr) { return mrb_str_cat(mrb, str, ptr, ptr ? strlen(ptr) : 0); } MRB_API mrb_value mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2) { if (mrb_str_ptr(str) == mrb_str_ptr(str2)) { mrb_str_modify(mrb, mrb_str_ptr(str)); } return mrb_str_cat(mrb, str, RSTRING_PTR(str2), RSTRING_LEN(str2)); } MRB_API mrb_value mrb_str_append(mrb_state *mrb, mrb_value str1, mrb_value str2) { mrb_ensure_string_type(mrb, str2); return mrb_str_cat_str(mrb, str1, str2); } /* * call-seq: * str.inspect -> string * * Returns a printable version of _str_, surrounded by quote marks, * with special characters escaped. * * str = "hello" * str[3] = "\b" * str.inspect #=> "\"hel\\bo\"" */ mrb_value mrb_str_inspect(mrb_state *mrb, mrb_value str) { return str_escape(mrb, str, TRUE); } /* * call-seq: * str.bytes -> array of int * * Returns an array of bytes in _str_. * * str = "hello" * str.bytes #=> [104, 101, 108, 108, 111] */ static mrb_value mrb_str_bytes(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); mrb_value a = mrb_ary_new_capa(mrb, RSTR_LEN(s)); unsigned char *p = (unsigned char*)(RSTR_PTR(s)), *pend = p + RSTR_LEN(s); while (p < pend) { mrb_ary_push(mrb, a, mrb_fixnum_value(p[0])); p++; } return a; } /* * call-seq: * str.getbyte(index) -> 0 .. 255 * * returns the indexth byte as an integer. */ static mrb_value mrb_str_getbyte(mrb_state *mrb, mrb_value str) { mrb_int pos; mrb_get_args(mrb, "i", &pos); if (pos < 0) pos += RSTRING_LEN(str); if (pos < 0 || RSTRING_LEN(str) <= pos) return mrb_nil_value(); return mrb_fixnum_value((unsigned char)RSTRING_PTR(str)[pos]); } /* * call-seq: * str.setbyte(index, integer) -> integer * * modifies the indexth byte as integer. */ static mrb_value mrb_str_setbyte(mrb_state *mrb, mrb_value str) { mrb_int pos, byte; mrb_int len; mrb_get_args(mrb, "ii", &pos, &byte); len = RSTRING_LEN(str); if (pos < -len || len <= pos) mrb_raisef(mrb, E_INDEX_ERROR, "index %i out of string", pos); if (pos < 0) pos += len; mrb_str_modify(mrb, mrb_str_ptr(str)); byte &= 0xff; RSTRING_PTR(str)[pos] = (unsigned char)byte; return mrb_fixnum_value((unsigned char)byte); } /* * call-seq: * str.byteslice(integer) -> new_str or nil * str.byteslice(integer, integer) -> new_str or nil * str.byteslice(range) -> new_str or nil * * Byte Reference---If passed a single Integer, returns a * substring of one byte at that position. If passed two Integer * objects, returns a substring starting at the offset given by the first, and * a length given by the second. If given a Range, a substring containing * bytes at offsets given by the range is returned. In all three cases, if * an offset is negative, it is counted from the end of str. Returns * nil if the initial offset falls outside the string, the length * is negative, or the beginning of the range is greater than the end. * The encoding of the resulted string keeps original encoding. * * "hello".byteslice(1) #=> "e" * "hello".byteslice(-1) #=> "o" * "hello".byteslice(1, 2) #=> "el" * "\x80\u3042".byteslice(1, 3) #=> "\u3042" * "\x03\u3042\xff".byteslice(1..3) #=> "\u3042" */ static mrb_value mrb_str_byteslice(mrb_state *mrb, mrb_value str) { mrb_value a1; mrb_int str_len, beg, len; mrb_bool empty = TRUE; len = mrb_get_argc(mrb); switch (len) { case 2: mrb_get_args(mrb, "ii", &beg, &len); str_len = RSTRING_LEN(str); break; case 1: a1 = mrb_get_arg1(mrb); str_len = RSTRING_LEN(str); if (mrb_range_p(a1)) { if (mrb_range_beg_len(mrb, a1, &beg, &len, str_len, TRUE) != MRB_RANGE_OK) { return mrb_nil_value(); } } else { beg = mrb_as_int(mrb, a1); len = 1; empty = FALSE; } break; default: mrb_argnum_error(mrb, len, 1, 2); break; } if (mrb_str_beg_len(str_len, &beg, &len) && (empty || len != 0)) { return mrb_str_byte_subseq(mrb, str, beg, len); } else { return mrb_nil_value(); } } static mrb_value sub_replace(mrb_state *mrb, mrb_value self) { char *p, *match; mrb_int plen, mlen; mrb_int found, offset; mrb_value result; mrb_get_args(mrb, "ssi", &p, &plen, &match, &mlen, &found); if (found < 0 || RSTRING_LEN(self) < found) { mrb_raise(mrb, E_RUNTIME_ERROR, "argument out of range"); } result = mrb_str_new(mrb, 0, 0); for (mrb_int i=0; i offset) { mrb_str_cat(mrb, result, RSTRING_PTR(self)+offset, RSTRING_LEN(self)-offset); } break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* ignore sub-group match (no Regexp supported) */ break; default: mrb_str_cat(mrb, result, &p[i-1], 2); break; } } return result; } static mrb_value str_bytesplice(mrb_state *mrb, mrb_value str, mrb_int idx1, mrb_int len1, mrb_value replace, mrb_int idx2, mrb_int len2) { struct RString *s = RSTRING(str); if (idx1 < 0) { idx1 += RSTR_LEN(s); } if (idx2 < 0) { idx2 += RSTRING_LEN(replace); } if (RSTR_LEN(s) < idx1 || idx1 < 0 || RSTRING_LEN(replace) < idx2 || idx2 < 0) { mrb_raise(mrb, E_INDEX_ERROR, "index out of string"); } if (len1 < 0 || len2 < 0) { mrb_raise(mrb, E_INDEX_ERROR, "negative length"); } mrb_int n; if (mrb_int_add_overflow(idx1, len1, &n) || RSTR_LEN(s) < n) { len1 = RSTR_LEN(s) - idx1; } if (mrb_int_add_overflow(idx2, len2, &n) || RSTRING_LEN(replace) < n) { len2 = RSTRING_LEN(replace) - idx2; } mrb_str_modify(mrb, s); if (len1 >= len2) { memmove(RSTR_PTR(s)+idx1, RSTRING_PTR(replace)+idx2, len2); if (len1 > len2) { memmove(RSTR_PTR(s)+idx1+len2, RSTR_PTR(s)+idx1+len1, RSTR_LEN(s)-(idx1+len1)); RSTR_SET_LEN(s, RSTR_LEN(s)-(len1-len2)); } } else { /* len1 < len2 */ mrb_int slen = RSTR_LEN(s); mrb_str_resize(mrb, str, slen+len2-len1); memmove(RSTR_PTR(s)+idx1+len2, RSTR_PTR(s)+idx1+len1, slen-(idx1+len1)); memmove(RSTR_PTR(s)+idx1, RSTRING_PTR(replace)+idx2, len2); } return str; } /* * call-seq: * bytesplice(index, length, str) -> string * bytesplice(index, length, str, str_index, str_length) -> string * bytesplice(range, str) -> string * bytesplice(range, str, str_range) -> string * * Replaces some or all of the content of +self+ with +str+, and returns +self+. * The portion of the string affected is determined using * the same criteria as String#byteslice, except that +length+ cannot be omitted. * If the replacement string is not the same length as the text it is replacing, * the string will be adjusted accordingly. * * If +str_index+ and +str_length+, or +str_range+ are given, the content of +self+ is replaced by str.byteslice(str_index, str_length) or str.byteslice(str_range); however the substring of +str+ is not allocated as a new string. * * The form that take an Integer will raise an IndexError if the value is out * of range; the Range form will raise a RangeError. * If the beginning or ending offset does not land on character (codepoint) * boundary, an IndexError will be raised. */ static mrb_value mrb_str_bytesplice(mrb_state *mrb, mrb_value str) { mrb_int idx1, len1, idx2, len2; mrb_value range1, range2, replace; switch (mrb_get_argc(mrb)) { case 3: mrb_get_args(mrb, "ooo", &range1, &replace, &range2); if (mrb_integer_p(range1)) { mrb_get_args(mrb, "iiS", &idx1, &len1, &replace); return str_bytesplice(mrb, str, idx1, len1, replace, 0, RSTRING_LEN(replace)); } mrb_ensure_string_type(mrb, replace); if (mrb_range_beg_len(mrb, range1, &idx1, &len1, RSTRING_LEN(str), FALSE) != MRB_RANGE_OK) break; if (mrb_range_beg_len(mrb, range2, &idx2, &len2, RSTRING_LEN(replace), FALSE) != MRB_RANGE_OK) break; return str_bytesplice(mrb, str, idx1, len1, replace, idx2, len2); case 5: mrb_get_args(mrb, "iiSii", &idx1, &len1, &replace, &idx2, &len2); return str_bytesplice(mrb, str, idx1, len1, replace, idx2, len2); case 2: mrb_get_args(mrb, "oS", &range1, &replace); if (mrb_range_beg_len(mrb, range1, &idx1, &len1, RSTRING_LEN(str), FALSE) == MRB_RANGE_OK) { return str_bytesplice(mrb, str, idx1, len1, replace, 0, RSTRING_LEN(replace)); } default: break; } mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arumgnts"); } static mrb_value mrb_encoding(mrb_state *mrb, mrb_value self) { mrb_get_args(mrb, ""); #ifdef MRB_UTF8_STRING return mrb_str_new_lit(mrb, "UTF-8"); #else return mrb_str_new_lit(mrb, "ASCII-8BIT"); #endif } /* ---------------------------*/ void mrb_init_string(mrb_state *mrb) { struct RClass *s; mrb_static_assert(RSTRING_EMBED_LEN_MAX < (1 << MRB_STR_EMBED_LEN_BITS), "pointer size too big for embedded string"); mrb->string_class = s = mrb_define_class_id(mrb, MRB_SYM(String), mrb->object_class); /* 15.2.10 */ MRB_SET_INSTANCE_TT(s, MRB_TT_STRING); mrb_define_method_id(mrb, s, MRB_SYM(bytesize), mrb_str_bytesize, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_OPSYM(cmp), mrb_str_cmp_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.1 */ mrb_define_method_id(mrb, s, MRB_OPSYM(eq), mrb_str_equal_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.2 */ mrb_define_method_id(mrb, s, MRB_OPSYM(add), mrb_str_plus_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.4 */ mrb_define_method_id(mrb, s, MRB_OPSYM(mul), mrb_str_times, MRB_ARGS_REQ(1)); /* 15.2.10.5.5 */ mrb_define_method_id(mrb, s, MRB_OPSYM(aref), mrb_str_aref_m, MRB_ARGS_ANY()); /* 15.2.10.5.6 */ mrb_define_method_id(mrb, s, MRB_OPSYM(aset), mrb_str_aset_m, MRB_ARGS_ANY()); mrb_define_method_id(mrb, s, MRB_SYM(capitalize), mrb_str_capitalize, MRB_ARGS_NONE()); /* 15.2.10.5.7 */ mrb_define_method_id(mrb, s, MRB_SYM_B(capitalize), mrb_str_capitalize_bang, MRB_ARGS_NONE()); /* 15.2.10.5.8 */ mrb_define_method_id(mrb, s, MRB_SYM(chomp), mrb_str_chomp, MRB_ARGS_ANY()); /* 15.2.10.5.9 */ mrb_define_method_id(mrb, s, MRB_SYM_B(chomp), mrb_str_chomp_bang, MRB_ARGS_ANY()); /* 15.2.10.5.10 */ mrb_define_method_id(mrb, s, MRB_SYM(chop), mrb_str_chop, MRB_ARGS_NONE()); /* 15.2.10.5.11 */ mrb_define_method_id(mrb, s, MRB_SYM_B(chop), mrb_str_chop_bang, MRB_ARGS_NONE()); /* 15.2.10.5.12 */ mrb_define_method_id(mrb, s, MRB_SYM(downcase), mrb_str_downcase, MRB_ARGS_NONE()); /* 15.2.10.5.13 */ mrb_define_method_id(mrb, s, MRB_SYM_B(downcase), mrb_str_downcase_bang, MRB_ARGS_NONE()); /* 15.2.10.5.14 */ mrb_define_method_id(mrb, s, MRB_SYM_Q(empty), mrb_str_empty_p, MRB_ARGS_NONE()); /* 15.2.10.5.16 */ mrb_define_method_id(mrb, s, MRB_SYM_Q(eql), mrb_str_eql, MRB_ARGS_REQ(1)); /* 15.2.10.5.17 */ mrb_define_method_id(mrb, s, MRB_SYM(hash), mrb_str_hash_m, MRB_ARGS_NONE()); /* 15.2.10.5.20 */ mrb_define_method_id(mrb, s, MRB_SYM_Q(include), mrb_str_include, MRB_ARGS_REQ(1)); /* 15.2.10.5.21 */ mrb_define_method_id(mrb, s, MRB_SYM(index), mrb_str_index_m, MRB_ARGS_ARG(1,1)); /* 15.2.10.5.22 */ mrb_define_method_id(mrb, s, MRB_SYM(initialize), mrb_str_init, MRB_ARGS_REQ(1)); /* 15.2.10.5.23 */ mrb_define_method_id(mrb, s, MRB_SYM(initialize_copy), mrb_str_replace, MRB_ARGS_REQ(1)); /* 15.2.10.5.24 */ mrb_define_method_id(mrb, s, MRB_SYM(intern), mrb_str_intern, MRB_ARGS_NONE()); /* 15.2.10.5.25 */ mrb_define_method_id(mrb, s, MRB_SYM(length), mrb_str_size, MRB_ARGS_NONE()); /* 15.2.10.5.26 */ mrb_define_method_id(mrb, s, MRB_SYM(replace), mrb_str_replace, MRB_ARGS_REQ(1)); /* 15.2.10.5.28 */ mrb_define_method_id(mrb, s, MRB_SYM(reverse), mrb_str_reverse, MRB_ARGS_NONE()); /* 15.2.10.5.29 */ mrb_define_method_id(mrb, s, MRB_SYM_B(reverse), mrb_str_reverse_bang, MRB_ARGS_NONE()); /* 15.2.10.5.30 */ mrb_define_method_id(mrb, s, MRB_SYM(rindex), mrb_str_rindex_m, MRB_ARGS_ANY()); /* 15.2.10.5.31 */ mrb_define_method_id(mrb, s, MRB_SYM(size), mrb_str_size, MRB_ARGS_NONE()); /* 15.2.10.5.33 */ mrb_define_method_id(mrb, s, MRB_SYM(slice), mrb_str_aref_m, MRB_ARGS_ANY()); /* 15.2.10.5.34 */ mrb_define_method_id(mrb, s, MRB_SYM(split), mrb_str_split_m, MRB_ARGS_ANY()); /* 15.2.10.5.35 */ #ifndef MRB_NO_FLOAT mrb_define_method_id(mrb, s, MRB_SYM(to_f), mrb_str_to_f, MRB_ARGS_NONE()); /* 15.2.10.5.38 */ #endif mrb_define_method_id(mrb, s, MRB_SYM(to_i), mrb_str_to_i, MRB_ARGS_ANY()); /* 15.2.10.5.39 */ mrb_define_method_id(mrb, s, MRB_SYM(to_s), mrb_str_to_s, MRB_ARGS_NONE()); /* 15.2.10.5.40 */ mrb_define_method_id(mrb, s, MRB_SYM(to_str), mrb_str_to_s, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_SYM(to_sym), mrb_str_intern, MRB_ARGS_NONE()); /* 15.2.10.5.41 */ mrb_define_method_id(mrb, s, MRB_SYM(upcase), mrb_str_upcase, MRB_ARGS_NONE()); /* 15.2.10.5.42 */ mrb_define_method_id(mrb, s, MRB_SYM_B(upcase), mrb_str_upcase_bang, MRB_ARGS_NONE()); /* 15.2.10.5.43 */ mrb_define_method_id(mrb, s, MRB_SYM(inspect), mrb_str_inspect, MRB_ARGS_NONE()); /* 15.2.10.5.46(x) */ mrb_define_method_id(mrb, s, MRB_SYM(bytes), mrb_str_bytes, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_SYM(getbyte), mrb_str_getbyte, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, s, MRB_SYM(setbyte), mrb_str_setbyte, MRB_ARGS_REQ(2)); mrb_define_method_id(mrb, s, MRB_SYM(byteindex), mrb_str_byteindex_m, MRB_ARGS_ARG(1,1)); mrb_define_method_id(mrb, s, MRB_SYM(byterindex), mrb_str_byterindex_m, MRB_ARGS_ARG(1,1)); mrb_define_method_id(mrb, s, MRB_SYM(byteslice), mrb_str_byteslice, MRB_ARGS_ARG(1,1)); mrb_define_method_id(mrb, s, MRB_SYM(bytesplice), mrb_str_bytesplice, MRB_ARGS_ANY()); mrb_define_method_id(mrb, s, MRB_SYM(__sub_replace), sub_replace, MRB_ARGS_REQ(3)); /* internal */ mrb_define_method_id(mrb, mrb->kernel_module, MRB_SYM(__ENCODING__), mrb_encoding, MRB_ARGS_NONE()); } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/load.c0000644000000000000000000000013215077107276017545 xustar0030 mtime=1761382078.135420497 30 atime=1761382080.156411252 30 ctime=1761382108.561301939 nghttp2-1.68.0/third-party/mruby/src/load.c0000644000175100017510000005153415077107276020145 0ustar00runnerrunner/* ** load.c - mruby binary loader ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include #include #if SIZE_MAX < UINT32_MAX # error size_t must be at least 32 bits wide #endif #define FLAG_SRC_MALLOC 1 #define FLAG_SRC_STATIC 0 #define SIZE_ERROR_MUL(nmemb, size) ((size_t)(nmemb) > SIZE_MAX / (size)) #define DEFINE_READ_IREP_FUNC(funcdecl, basecall) \ funcdecl \ { \ int ai = mrb_gc_arena_save(mrb); \ struct RProc *proc = basecall; \ struct mrb_irep *irep = (mrb_irep*)(proc ? proc->body.irep : NULL); \ if (irep) proc->body.irep = NULL; \ mrb_gc_arena_restore(mrb, ai); \ return irep; \ } #ifndef MRB_NO_FLOAT static double str_to_double(mrb_state *mrb, const char *p) { /* dump IEEE754 little endian binary */ union { char s[sizeof(double)]; double f; } u; if (littleendian) { memcpy(u.s, p, sizeof(double)); } else { size_t i; for (i=0; inlocals = bin_to_uint16(src); src += sizeof(uint16_t); /* number of register variable */ irep->nregs = bin_to_uint16(src); src += sizeof(uint16_t); /* number of child irep */ irep->rlen = bin_to_uint16(src); src += sizeof(uint16_t); /* Binary Data Section */ /* ISEQ BLOCK (and CATCH HANDLER TABLE BLOCK) */ irep->clen = bin_to_uint16(src); /* number of catch handler */ src += sizeof(uint16_t); irep->ilen = bin_to_uint32(src); src += sizeof(uint32_t); if (irep->ilen > 0) { size_t data_len = sizeof(mrb_code) * irep->ilen + sizeof(struct mrb_irep_catch_handler) * irep->clen; mrb_static_assert(sizeof(struct mrb_irep_catch_handler) == 13); if (SIZE_ERROR_MUL(irep->ilen, sizeof(mrb_code))) { return FALSE; } if (src + data_len > end) return FALSE; if ((flags & FLAG_SRC_MALLOC) == 0) { irep->iseq = (mrb_code*)src; irep->flags |= MRB_ISEQ_NO_FREE; } else { void *buf = mrb_malloc(mrb, data_len); irep->iseq = (mrb_code*)buf; memcpy(buf, src, data_len); } src += data_len; } /* POOL BLOCK */ plen = bin_to_uint16(src); /* number of pool */ src += sizeof(uint16_t); if (src > end) return FALSE; if (plen > 0) { if (SIZE_ERROR_MUL(plen, sizeof(mrb_value))) { return FALSE; } irep->pool = pool = (mrb_irep_pool*)mrb_calloc(mrb, sizeof(mrb_irep_pool), plen); for (i = 0; i < plen; i++) { mrb_bool st = (flags & FLAG_SRC_MALLOC)==0; tt = *src++; /* pool TT */ switch (tt) { /* pool data */ case IREP_TT_INT32: { if (src + sizeof(uint32_t) > end) return FALSE; mrb_int v = (int32_t)bin_to_uint32(src); src += sizeof(uint32_t); #ifdef MRB_64BIT pool[i].tt = IREP_TT_INT64; pool[i].u.i64 = (int64_t)v; #else pool[i].tt = IREP_TT_INT32; pool[i].u.i32 = v; #endif } break; case IREP_TT_INT64: #ifdef MRB_INT64 { if (src + sizeof(uint32_t)*2 > end) return FALSE; uint64_t i64 = bin_to_uint32(src); src += sizeof(uint32_t); i64 <<= 32; i64 |= bin_to_uint32(src); src += sizeof(uint32_t); pool[i].tt = tt; pool[i].u.i64 = (int64_t)i64; } break; #else return FALSE; #endif case IREP_TT_BIGINT: pool_data_len = bin_to_uint8(src) + 2; /* pool data length */ if (src + pool_data_len > end) return FALSE; else { char *p; pool[i].tt = IREP_TT_BIGINT; p = (char*)mrb_malloc(mrb, pool_data_len); memcpy(p, src, pool_data_len); pool[i].u.str = (const char*)p; } src += pool_data_len; break; case IREP_TT_FLOAT: #ifndef MRB_NO_FLOAT if (src + sizeof(double) > end) return FALSE; pool[i].tt = tt; pool[i].u.f = str_to_double(mrb, (const char*)src); src += sizeof(double); break; #else return FALSE; /* MRB_NO_FLOAT */ #endif case IREP_TT_STR: pool_data_len = bin_to_uint16(src); /* pool data length */ src += sizeof(uint16_t); if (src + pool_data_len > end) return FALSE; if (st) { pool[i].tt = (pool_data_len<<2) | IREP_TT_SSTR; pool[i].u.str = (const char*)src; } else { char *p; pool[i].tt = (pool_data_len<<2) | IREP_TT_STR; p = (char*)mrb_malloc(mrb, pool_data_len+1); memcpy(p, src, pool_data_len+1); pool[i].u.str = (const char*)p; } src += pool_data_len + 1; break; default: /* should not happen */ return FALSE; } irep->plen = i+1; } } /* SYMS BLOCK */ irep->slen = bin_to_uint16(src); /* syms length */ src += sizeof(uint16_t); if (src > end) return FALSE; if (irep->slen > 0) { if (SIZE_ERROR_MUL(irep->slen, sizeof(mrb_sym))) { return FALSE; } irep->syms = syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * irep->slen); for (i = 0; i < irep->slen; i++) { snl = bin_to_uint16(src); /* symbol name length */ src += sizeof(uint16_t); if (snl == MRB_DUMP_NULL_SYM_LEN) { syms[i] = 0; continue; } if (src + snl > end) return FALSE; if (flags & FLAG_SRC_MALLOC) { syms[i] = mrb_intern(mrb, (char*)src, snl); } else { syms[i] = mrb_intern_static(mrb, (char*)src, snl); } src += snl + 1; mrb_gc_arena_restore(mrb, ai); } } diff = src - bin; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); *len = (size_t)diff; return TRUE; } static mrb_bool read_irep_record(mrb_state *mrb, const uint8_t *bin, const uint8_t *end, size_t *len, uint8_t flags, mrb_irep **irepp) { int ai = mrb_gc_arena_save(mrb); mrb_bool readsuccess = read_irep_record_1(mrb, bin, end, len, flags, irepp); mrb_irep **reps; int i; mrb_gc_arena_restore(mrb, ai); if (!readsuccess) { return FALSE; } reps = (mrb_irep**)mrb_calloc(mrb, (*irepp)->rlen, sizeof(mrb_irep*)); (*irepp)->reps = (const mrb_irep**)reps; bin += *len; for (i=0; i<(*irepp)->rlen; i++) { size_t rlen; readsuccess = read_irep_record(mrb, bin, end, &rlen, flags, &reps[i]); mrb_gc_arena_restore(mrb, ai); if (!readsuccess) { return FALSE; } bin += rlen; *len += rlen; } return TRUE; } static mrb_irep* read_section_irep(mrb_state *mrb, const uint8_t *bin, size_t size, uint8_t flags, struct RProc **proc) { if (size < sizeof(struct rite_section_irep_header)) return NULL; /* * This proc object keeps all the data in progress to avoid memory leaks * if something goes wrong while reading irep. */ *proc = mrb_proc_new(mrb, NULL); mrb_irep **irepp = (mrb_irep**)&(*proc)->body.irep; size_t len; bin += sizeof(struct rite_section_irep_header); if (read_irep_record(mrb, bin, bin+size, &len, flags, irepp)) { return *irepp; } else { return NULL; } } static int read_debug_record(mrb_state *mrb, const uint8_t *start, const uint8_t *end, mrb_irep* irep, size_t *record_len, const mrb_sym *filenames, size_t filenames_len) { const uint8_t *bin = start; ptrdiff_t diff; size_t record_size; uint16_t f_idx; int i; mrb_irep_debug_info *debug; if (irep->debug_info) { return MRB_DUMP_INVALID_IREP; } irep->debug_info = debug = (mrb_irep_debug_info*)mrb_calloc(mrb, 1, sizeof(mrb_irep_debug_info)); debug->pc_count = (uint32_t)irep->ilen; record_size = (size_t)bin_to_uint32(bin); bin += sizeof(uint32_t); debug->flen = bin_to_uint16(bin); bin += sizeof(uint16_t); if (bin > end) return MRB_DUMP_GENERAL_FAILURE; debug->files = (mrb_irep_debug_info_file**)mrb_calloc(mrb, irep->debug_info->flen, sizeof(mrb_irep_debug_info*)); for (f_idx = 0; f_idx < debug->flen; f_idx++) { mrb_irep_debug_info_file *file; uint16_t filename_idx; if (bin > end) return MRB_DUMP_GENERAL_FAILURE; file = (mrb_irep_debug_info_file*)mrb_calloc(mrb, 1, sizeof(*file)); debug->files[f_idx] = file; file->start_pos = bin_to_uint32(bin); bin += sizeof(uint32_t); /* filename */ filename_idx = bin_to_uint16(bin); bin += sizeof(uint16_t); mrb_assert(filename_idx < filenames_len); file->filename_sym = filenames[filename_idx]; file->line_entry_count = bin_to_uint32(bin); bin += sizeof(uint32_t); file->line_type = (mrb_debug_line_type)bin_to_uint8(bin); bin += sizeof(uint8_t); switch (file->line_type) { case mrb_debug_line_ary: { size_t l = sizeof(uint16_t) * (size_t)file->line_entry_count; if (bin + l > end) return MRB_DUMP_GENERAL_FAILURE; uint16_t *ary = (uint16_t*)mrb_malloc(mrb, l); for (l = 0; l < file->line_entry_count; l++) { ary[l] = bin_to_uint16(bin); bin += sizeof(uint16_t); } file->lines.ary = ary; } break; case mrb_debug_line_flat_map: { size_t c = (size_t)file->line_entry_count; size_t n = sizeof(mrb_irep_debug_info_line); if (bin + c*n > end) return MRB_DUMP_GENERAL_FAILURE; mrb_irep_debug_info_line *flat_map = (mrb_irep_debug_info_line*)mrb_calloc(mrb, c, n); for (size_t l = 0; l < file->line_entry_count; l++) { flat_map[l].start_pos = bin_to_uint32(bin); bin += sizeof(uint32_t); flat_map[l].line = bin_to_uint16(bin); bin += sizeof(uint16_t); } file->lines.flat_map = flat_map; } break; case mrb_debug_line_packed_map: { size_t l = (size_t)file->line_entry_count; if (bin + l > end) return MRB_DUMP_GENERAL_FAILURE; uint8_t *packed_map = (uint8_t*)mrb_malloc(mrb, l); memcpy(packed_map, bin, file->line_entry_count); file->lines.packed_map = packed_map; bin += file->line_entry_count; } break; default: return MRB_DUMP_GENERAL_FAILURE; } } diff = bin - start; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); if (record_size != (size_t)diff) { return MRB_DUMP_GENERAL_FAILURE; } for (i = 0; i < irep->rlen; i++) { size_t len; int ret; ret = read_debug_record(mrb, bin, end, (mrb_irep*)irep->reps[i], &len, filenames, filenames_len); if (ret != MRB_DUMP_OK) return ret; bin += len; } diff = bin - start; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); *record_len = (size_t)diff; return MRB_DUMP_OK; } static int read_section_debug(mrb_state *mrb, const uint8_t *start, size_t size, mrb_irep *irep, uint8_t flags) { const uint8_t *bin; const uint8_t *end = start + size; ptrdiff_t diff; struct rite_section_debug_header *header; uint16_t i; size_t len = 0; int result; uint16_t filenames_len; mrb_sym *filenames; mrb_value filenames_obj; bin = start; header = (struct rite_section_debug_header*)bin; bin += sizeof(struct rite_section_debug_header); filenames_len = bin_to_uint16(bin); bin += sizeof(uint16_t); if (bin > end) return MRB_DUMP_GENERAL_FAILURE; filenames_obj = mrb_str_new(mrb, NULL, sizeof(mrb_sym) * (size_t)filenames_len); filenames = (mrb_sym*)RSTRING_PTR(filenames_obj); for (i = 0; i < filenames_len; i++) { uint16_t f_len = bin_to_uint16(bin); bin += sizeof(uint16_t); if (bin + f_len > end) { result = MRB_DUMP_GENERAL_FAILURE; goto debug_exit; } if (flags & FLAG_SRC_MALLOC) { filenames[i] = mrb_intern(mrb, (const char*)bin, (size_t)f_len); } else { filenames[i] = mrb_intern_static(mrb, (const char*)bin, (size_t)f_len); } bin += f_len; } result = read_debug_record(mrb, bin, end, irep, &len, filenames, filenames_len); if (result != MRB_DUMP_OK) goto debug_exit; bin += len; diff = bin - start; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); if ((uint32_t)diff != bin_to_uint32(header->section_size)) { result = MRB_DUMP_GENERAL_FAILURE; } debug_exit: mrb_str_resize(mrb, filenames_obj, 0); return result; } static int read_lv_record(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, size_t *record_len, mrb_sym const *syms, uint32_t syms_len) { const uint8_t *bin = start; mrb_sym *lv; ptrdiff_t diff; int i; if (irep->nlocals == 0) return MRB_DUMP_GENERAL_FAILURE; irep->lv = lv = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * (irep->nlocals - 1)); for (i = 0; i + 1 < irep->nlocals; i++) { uint16_t const sym_idx = bin_to_uint16(bin); bin += sizeof(uint16_t); if (sym_idx == RITE_LV_NULL_MARK) { lv[i] = 0; } else { if (sym_idx >= syms_len) { return MRB_DUMP_GENERAL_FAILURE; } lv[i] = syms[sym_idx]; } } for (i = 0; i < irep->rlen; i++) { size_t len; int ret; ret = read_lv_record(mrb, bin, (mrb_irep*)irep->reps[i], &len, syms, syms_len); if (ret != MRB_DUMP_OK) return ret; bin += len; } diff = bin - start; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); *record_len = (size_t)diff; return MRB_DUMP_OK; } static int read_section_lv(mrb_state *mrb, const uint8_t *start, size_t size, mrb_irep *irep, uint8_t flags) { const uint8_t *bin; const uint8_t *end = start + size; ptrdiff_t diff; struct rite_section_lv_header const *header; uint32_t i; size_t len = 0; int result; uint32_t syms_len; mrb_sym *syms; mrb_value syms_obj; mrb_sym (*intern_func)(mrb_state*, const char*, size_t) = (flags & FLAG_SRC_MALLOC)? mrb_intern : mrb_intern_static; bin = start; header = (struct rite_section_lv_header const*)bin; bin += sizeof(struct rite_section_lv_header); syms_len = bin_to_uint32(bin); bin += sizeof(uint32_t); if (bin > end) return MRB_DUMP_READ_FAULT; syms_obj = mrb_str_new(mrb, NULL, sizeof(mrb_sym) * (size_t)syms_len); syms = (mrb_sym*)RSTRING_PTR(syms_obj); for (i = 0; i < syms_len; i++) { uint16_t const str_len = bin_to_uint16(bin); bin += sizeof(uint16_t); if (bin > end) return MRB_DUMP_READ_FAULT; syms[i] = intern_func(mrb, (const char*)bin, str_len); bin += str_len; } result = read_lv_record(mrb, bin, irep, &len, syms, syms_len); if (result != MRB_DUMP_OK) goto lv_exit; bin += len; diff = bin - start; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); if ((uint32_t)diff != bin_to_uint32(header->section_size)) { result = MRB_DUMP_GENERAL_FAILURE; } lv_exit: mrb_str_resize(mrb, syms_obj, 0); return result; } static int read_binary_header(const uint8_t *bin, size_t bufsize, size_t *bin_size, uint8_t *flags) { const struct rite_binary_header *header = (const struct rite_binary_header*)bin; if (bufsize < sizeof(struct rite_binary_header)) { return MRB_DUMP_READ_FAULT; } if (memcmp(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)) != 0) { return MRB_DUMP_INVALID_FILE_HEADER; } /* if major version is different, they are incompatible */ if (memcmp(header->major_version, RITE_BINARY_MAJOR_VER, sizeof(header->major_version)) != 0) { return MRB_DUMP_INVALID_FILE_HEADER; } /* if minor version is different, we can accept the older version */ if (memcmp(header->minor_version, RITE_BINARY_MINOR_VER, sizeof(header->minor_version)) > 0) { return MRB_DUMP_INVALID_FILE_HEADER; } *bin_size = (size_t)bin_to_uint32(header->binary_size); if (bufsize < *bin_size) { return MRB_DUMP_READ_FAULT; } return MRB_DUMP_OK; } static struct RProc* read_irep(mrb_state *mrb, const uint8_t *bin, size_t bufsize, uint8_t flags) { int result; struct RProc *proc = NULL; mrb_irep *irep = NULL; const struct rite_section_header *section_header; size_t bin_size = 0; if ((mrb == NULL) || (bin == NULL)) { return NULL; } result = read_binary_header(bin, bufsize, &bin_size, &flags); if (result != MRB_DUMP_OK) { return NULL; } bin += sizeof(struct rite_binary_header); bin_size -= sizeof(struct rite_binary_header); while (bin_size > sizeof(struct rite_section_header)) { section_header = (const struct rite_section_header*)bin; uint32_t section_size = bin_to_uint32(section_header->section_size); if (bin_size < section_size) return NULL; if (memcmp(section_header->section_ident, RITE_SECTION_IREP_IDENT, sizeof(section_header->section_ident)) == 0) { irep = read_section_irep(mrb, bin, bin_size, flags, &proc); if (!irep) return NULL; } else if (memcmp(section_header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(section_header->section_ident)) == 0) { if (!irep) return NULL; /* corrupted data */ result = read_section_debug(mrb, bin, bin_size, irep, flags); if (result < MRB_DUMP_OK) { return NULL; } } else if (memcmp(section_header->section_ident, RITE_SECTION_LV_IDENT, sizeof(section_header->section_ident)) == 0) { if (!irep) return NULL; result = read_section_lv(mrb, bin, bin_size, irep, flags); if (result < MRB_DUMP_OK) { return NULL; } } else if (memcmp(section_header->section_ident, RITE_BINARY_EOF, sizeof(section_header->section_ident)) != 0) { break; } bin += section_size; bin_size -= section_size; } return proc; } static struct RProc* mrb_proc_read_irep(mrb_state *mrb, const uint8_t *bin) { uint8_t flags = mrb_ro_data_p((char*)bin) ? FLAG_SRC_STATIC : FLAG_SRC_MALLOC; return read_irep(mrb, bin, (size_t)UINT32_MAX, flags); } DEFINE_READ_IREP_FUNC( mrb_irep *mrb_read_irep(mrb_state *mrb, const uint8_t *bin), mrb_proc_read_irep(mrb, bin)) static struct RProc* mrb_proc_read_irep_buf(mrb_state *mrb, const void *buf, size_t bufsize) { return read_irep(mrb, (const uint8_t*)buf, bufsize, FLAG_SRC_MALLOC); } DEFINE_READ_IREP_FUNC( MRB_API mrb_irep *mrb_read_irep_buf(mrb_state *mrb, const void *buf, size_t bufsize), mrb_proc_read_irep_buf(mrb, buf, bufsize)) void mrb_exc_set(mrb_state *mrb, mrb_value exc); static void irep_error(mrb_state *mrb) { mrb_exc_set(mrb, mrb_exc_new_lit(mrb, E_SCRIPT_ERROR, "irep load error")); } static mrb_value load_irep(mrb_state *mrb, struct RProc *proc, mrb_ccontext *c) { if (!proc || !proc->body.irep) { irep_error(mrb); return mrb_nil_value(); } proc->c = NULL; if (c && c->dump_result) mrb_codedump_all(mrb, proc); if (c && c->no_exec) return mrb_obj_value(proc); return mrb_top_run(mrb, proc, mrb_top_self(mrb), 0); } MRB_API mrb_value mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrb_ccontext *c) { struct RProc *proc = mrb_proc_read_irep(mrb, bin); if (!proc) return mrb_undef_value(); return load_irep(mrb, proc, c); } MRB_API mrb_value mrb_load_irep_buf_cxt(mrb_state *mrb, const void *buf, size_t bufsize, mrb_ccontext *c) { return load_irep(mrb, mrb_proc_read_irep_buf(mrb, buf, bufsize), c); } MRB_API mrb_value mrb_load_irep(mrb_state *mrb, const uint8_t *bin) { return mrb_load_irep_cxt(mrb, bin, NULL); } MRB_API mrb_value mrb_load_irep_buf(mrb_state *mrb, const void *buf, size_t bufsize) { return mrb_load_irep_buf_cxt(mrb, buf, bufsize, NULL); } MRB_API mrb_value mrb_load_proc(mrb_state *mrb, const struct RProc *proc) { return mrb_top_run(mrb, proc, mrb_top_self(mrb), 0); } #ifndef MRB_NO_STDIO static struct RProc* mrb_proc_read_irep_file(mrb_state *mrb, FILE *fp) { struct RProc *proc = NULL; uint8_t *buf; const size_t header_size = sizeof(struct rite_binary_header); size_t buf_size = 0; uint8_t flags = 0; int result; if ((mrb == NULL) || (fp == NULL)) { return NULL; } buf = (uint8_t*)mrb_malloc(mrb, header_size); if (fread(buf, header_size, 1, fp) == 0) { goto irep_exit; } result = read_binary_header(buf, (size_t)-1, &buf_size, &flags); if (result != MRB_DUMP_OK || buf_size <= header_size) { goto irep_exit; } buf = (uint8_t*)mrb_realloc(mrb, buf, buf_size); if (fread(buf+header_size, buf_size-header_size, 1, fp) == 0) { goto irep_exit; } proc = read_irep(mrb, buf, (size_t)-1, FLAG_SRC_MALLOC); irep_exit: mrb_free(mrb, buf); return proc; } DEFINE_READ_IREP_FUNC( mrb_irep *mrb_read_irep_file(mrb_state *mrb, FILE *fp), mrb_proc_read_irep_file(mrb, fp)) MRB_API mrb_value mrb_load_irep_file_cxt(mrb_state *mrb, FILE* fp, mrb_ccontext *c) { return load_irep(mrb, mrb_proc_read_irep_file(mrb, fp), c); } MRB_API mrb_value mrb_load_irep_file(mrb_state *mrb, FILE* fp) { return mrb_load_irep_file_cxt(mrb, fp, NULL); } #endif /* MRB_NO_STDIO */ nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/gc.c0000644000000000000000000000013215077107276017217 xustar0030 mtime=1761382078.134420501 30 atime=1761382080.154411261 30 ctime=1761382108.559301945 nghttp2-1.68.0/third-party/mruby/src/gc.c0000644000175100017510000011125315077107276017612 0ustar00runnerrunner/* ** gc.c - garbage collector for mruby ** ** See Copyright Notice in mruby.h */ #include #ifdef MRB_USE_MALLOC_TRIM #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MRB_GC_STRESS #include #endif /* = Tri-color Incremental Garbage Collection mruby's GC is Tri-color Incremental GC with Mark & Sweep. Algorithm details are omitted. Instead, the implementation part is described below. == Object's Color Each object can be painted in three colors: * White - Unmarked. * Gray - Marked, But the child objects are unmarked. * Black - Marked, the child objects are also marked. Extra color * Red - Static (ROM object) no need to be collected. - All child objects should be Red as well. == Two White Types There are two white color types in a flip-flop fashion: White-A and White-B, which respectively represent the Current White color (the newly allocated objects in the current GC cycle) and the Sweep Target White color (the dead objects to be swept). A and B will be switched just at the beginning of the next GC cycle. At that time, all the dead objects have been swept, while the newly created objects in the current GC cycle which finally remains White are now regarded as dead objects. Instead of traversing all the White-A objects and painting them as White-B, just switch the meaning of White-A and White-B as this will be much cheaper. As a result, the objects we sweep in the current GC cycle are always left from the previous GC cycle. This allows us to sweep objects incrementally, without the disturbance of the newly created objects. == Execution Timing GC Execution Time and Each step interval are decided by live objects count. List of Adjustment API: * gc_interval_ratio_set * gc_step_ratio_set For details, see the comments for each function. == Write Barrier mruby implementer and C extension library writer must insert a write barrier when updating a reference from a field of an object. When updating a reference from a field of object A to object B, two different types of write barrier are available: * mrb_field_write_barrier - target B object for a mark. * mrb_write_barrier - target A object for a mark. == Generational Mode mruby's GC offers an Generational Mode while reusing the tri-color GC infrastructure. It will treat the Black objects as Old objects after each sweep phase, instead of painting them White. The key ideas are still the same as traditional generational GC: * Minor GC - just traverse the Young objects (Gray objects) in the mark phase, then only sweep the newly created objects, and leave the Old objects live. * Major GC - same as a full regular GC cycle. The difference from "traditional" generational GC is, that the major GC in mruby is triggered incrementally in a tri-color manner. For details, see the comments for each function. */ typedef struct RVALUE RVALUE; struct free_obj { MRB_OBJECT_HEADER; RVALUE *next; }; struct RVALUE_initializer { MRB_OBJECT_HEADER; char padding[sizeof(void*) * 4 - sizeof(uint32_t)]; }; struct RVALUE { union { struct RVALUE_initializer init; /* must be first member to ensure initialization */ struct free_obj free; struct RBasic basic; struct RObject object; struct RClass klass; struct RString string; struct RArray array; struct RHash hash; struct RRange range; struct RData data; struct RIStruct istruct; struct RProc proc; struct REnv env; struct RFiber fiber; struct RException exc; struct RBreak brk; } as; }; #ifdef GC_DEBUG #define DEBUG(x) (x) #else #define DEBUG(x) #endif #ifndef MRB_HEAP_PAGE_SIZE #define MRB_HEAP_PAGE_SIZE 1024 #endif typedef struct mrb_heap_page { RVALUE *freelist; struct mrb_heap_page *next; struct mrb_heap_page *free_next; mrb_bool old:1; RVALUE objects[MRB_HEAP_PAGE_SIZE]; } mrb_heap_page; #define GC_STEP_SIZE 1024 /* white: 001 or 010, black: 100, gray: 000, red:111 */ #define GC_GRAY 0 #define GC_WHITE_A 1 #define GC_WHITE_B 2 #define GC_BLACK 4 #define GC_RED MRB_GC_RED #define GC_WHITES (GC_WHITE_A | GC_WHITE_B) #define GC_COLOR_MASK 7 mrb_static_assert(MRB_GC_RED <= GC_COLOR_MASK); #define paint_gray(o) ((o)->gc_color = GC_GRAY) #define paint_black(o) ((o)->gc_color = GC_BLACK) #define paint_white(o) ((o)->gc_color = GC_WHITES) #define paint_partial_white(s, o) ((o)->gc_color = (s)->current_white_part) #define is_gray(o) ((o)->gc_color == GC_GRAY) #define is_white(o) ((o)->gc_color & GC_WHITES) #define is_black(o) ((o)->gc_color == GC_BLACK) #define is_red(o) ((o)->gc_color == GC_RED) #define flip_white_part(s) ((s)->current_white_part = other_white_part(s)) #define other_white_part(s) ((s)->current_white_part ^ GC_WHITES) #define is_dead(s, o) (((o)->gc_color & other_white_part(s) & GC_WHITES) || (o)->tt == MRB_TT_FREE) mrb_noreturn void mrb_raise_nomemory(mrb_state *mrb); MRB_API void* mrb_realloc_simple(mrb_state *mrb, void *p, size_t len) { void *p2; #if defined(MRB_GC_STRESS) && defined(MRB_DEBUG) if (mrb->gc.state != MRB_GC_STATE_SWEEP) { mrb_full_gc(mrb); } #endif p2 = mrb_basic_alloc_func(p, len); if (!p2 && len > 0 && mrb->gc.heaps && mrb->gc.state != MRB_GC_STATE_SWEEP) { mrb_full_gc(mrb); p2 = mrb_basic_alloc_func(p, len); } return p2; } MRB_API void* mrb_realloc(mrb_state *mrb, void *p, size_t len) { void *p2; p2 = mrb_realloc_simple(mrb, p, len); if (len == 0) return p2; if (p2 == NULL) { mrb->gc.out_of_memory = TRUE; mrb_raise_nomemory(mrb); } else { mrb->gc.out_of_memory = FALSE; } return p2; } MRB_API void* mrb_malloc(mrb_state *mrb, size_t len) { return mrb_realloc(mrb, 0, len); } MRB_API void* mrb_malloc_simple(mrb_state *mrb, size_t len) { return mrb_realloc_simple(mrb, 0, len); } MRB_API void* mrb_calloc(mrb_state *mrb, size_t nelem, size_t len) { void *p; if (nelem > 0 && len > 0 && nelem <= SIZE_MAX / len) { size_t size; size = nelem * len; p = mrb_malloc(mrb, size); memset(p, 0, size); } else { p = NULL; } return p; } MRB_API void mrb_free(mrb_state *mrb, void *p) { mrb_basic_alloc_func(p, 0); } MRB_API void* mrb_alloca(mrb_state *mrb, size_t size) { struct RString *s; s = MRB_OBJ_ALLOC(mrb, MRB_TT_STRING, NULL); return s->as.heap.ptr = (char*)mrb_malloc(mrb, size); } static mrb_bool heap_p(mrb_gc *gc, const struct RBasic *object) { mrb_heap_page* page; page = gc->heaps; while (page) { RVALUE *p; p = page->objects; if ((uintptr_t)object - (uintptr_t)p <= (MRB_HEAP_PAGE_SIZE - 1) * sizeof(RVALUE)) { return TRUE; } page = page->next; } return FALSE; } MRB_API mrb_bool mrb_object_dead_p(mrb_state *mrb, struct RBasic *object) { mrb_gc *gc = &mrb->gc; if (!heap_p(gc, object)) return TRUE; return is_dead(gc, object); } static void add_heap(mrb_state *mrb, mrb_gc *gc) { mrb_heap_page *page = (mrb_heap_page*)mrb_calloc(mrb, 1, sizeof(mrb_heap_page)); RVALUE *p, *e; RVALUE *prev = NULL; for (p = page->objects, e=p+MRB_HEAP_PAGE_SIZE; pas.free.tt = MRB_TT_FREE; p->as.free.next = prev; prev = p; } page->freelist = prev; page->next = gc->heaps; gc->heaps = page; page->free_next = gc->free_heaps; gc->free_heaps = page; } #define DEFAULT_GC_INTERVAL_RATIO 200 #define DEFAULT_GC_STEP_RATIO 200 #define MAJOR_GC_INC_RATIO 120 #define MAJOR_GC_TOOMANY 10000 #define is_generational(gc) ((gc)->generational) #define is_major_gc(gc) (is_generational(gc) && (gc)->full) #define is_minor_gc(gc) (is_generational(gc) && !(gc)->full) void mrb_gc_init(mrb_state *mrb, mrb_gc *gc) { #ifndef MRB_GC_FIXED_ARENA gc->arena = (struct RBasic**)mrb_malloc(mrb, sizeof(struct RBasic*)*MRB_GC_ARENA_SIZE); gc->arena_capa = MRB_GC_ARENA_SIZE; #endif gc->current_white_part = GC_WHITE_A; gc->heaps = NULL; gc->free_heaps = NULL; add_heap(mrb, gc); gc->interval_ratio = DEFAULT_GC_INTERVAL_RATIO; gc->step_ratio = DEFAULT_GC_STEP_RATIO; #ifndef MRB_GC_TURN_OFF_GENERATIONAL gc->generational = TRUE; gc->full = TRUE; #endif } static void obj_free(mrb_state *mrb, struct RBasic *obj, mrb_bool end); static void free_heap(mrb_state *mrb, mrb_gc *gc) { mrb_heap_page *page = gc->heaps; mrb_heap_page *tmp; RVALUE *p, *e; while (page) { tmp = page; page = page->next; for (p = tmp->objects, e=p+MRB_HEAP_PAGE_SIZE; pas.free.tt != MRB_TT_FREE) obj_free(mrb, &p->as.basic, TRUE); } mrb_free(mrb, tmp); } } void mrb_gc_destroy(mrb_state *mrb, mrb_gc *gc) { free_heap(mrb, gc); #ifndef MRB_GC_FIXED_ARENA mrb_free(mrb, gc->arena); #endif } static void gc_arena_keep(mrb_state *mrb, mrb_gc *gc) { #ifdef MRB_GC_FIXED_ARENA if (gc->arena_idx >= MRB_GC_ARENA_SIZE) { /* arena overflow error */ gc->arena_idx = MRB_GC_ARENA_SIZE - 4; /* force room in arena */ mrb_exc_raise(mrb, mrb_obj_value(mrb->arena_err)); } #else if (gc->arena_idx >= gc->arena_capa) { /* extend arena */ int newcapa = gc->arena_capa * 3 / 2; gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*newcapa); gc->arena_capa = newcapa; } #endif } static inline void gc_protect(mrb_state *mrb, mrb_gc *gc, struct RBasic *p) { #ifdef MRB_GC_FIXED_ARENA mrb_assert(gc->arena_idx < MRB_GC_ARENA_SIZE); #else mrb_assert(gc->arena_idx < gc->arena_capa); #endif gc->arena[gc->arena_idx++] = p; } /* mrb_gc_protect() leaves the object in the arena */ MRB_API void mrb_gc_protect(mrb_state *mrb, mrb_value obj) { if (mrb_immediate_p(obj)) return; struct RBasic *p = mrb_basic_ptr(obj); if (is_red(p)) return; gc_arena_keep(mrb, &mrb->gc); gc_protect(mrb, &mrb->gc, p); } #define GC_ROOT_SYM MRB_SYM(_gc_root_) /* mrb_gc_register() keeps the object from GC. Register your object when it's exported to C world, without reference from Ruby world, e.g. callback arguments. Don't forget to remove the object using mrb_gc_unregister, otherwise your object will leak. */ MRB_API void mrb_gc_register(mrb_state *mrb, mrb_value obj) { mrb_value table; if (mrb_immediate_p(obj)) return; table = mrb_gv_get(mrb, GC_ROOT_SYM); int ai = mrb_gc_arena_save(mrb); mrb_gc_protect(mrb, obj); if (!mrb_array_p(table)) { table = mrb_ary_new(mrb); mrb_obj_ptr(table)->c = NULL; /* hide from ObjectSpace.each_object */ mrb_gv_set(mrb, GC_ROOT_SYM, table); } mrb_ary_push(mrb, table, obj); mrb_gc_arena_restore(mrb, ai); } /* mrb_gc_unregister() removes the object from GC root. */ MRB_API void mrb_gc_unregister(mrb_state *mrb, mrb_value obj) { mrb_value table; struct RArray *a; if (mrb_immediate_p(obj)) return; table = mrb_gv_get(mrb, GC_ROOT_SYM); if (!mrb_array_p(table)) return; a = mrb_ary_ptr(table); mrb_ary_modify(mrb, a); mrb_int len = ARY_LEN(a)-1; mrb_value *ptr = ARY_PTR(a); for (mrb_int i = 0; i <= len; i++) { if (mrb_ptr(ptr[i]) == mrb_ptr(obj)) { ARY_SET_LEN(a, len); memmove(&ptr[i], &ptr[i + 1], (len - i) * sizeof(mrb_value)); break; } } } MRB_API struct RBasic* mrb_obj_alloc(mrb_state *mrb, enum mrb_vtype ttype, struct RClass *cls) { static const RVALUE RVALUE_zero = { { { NULL, NULL, MRB_TT_FALSE } } }; mrb_gc *gc = &mrb->gc; if (cls) { enum mrb_vtype tt; switch (cls->tt) { case MRB_TT_CLASS: case MRB_TT_SCLASS: case MRB_TT_MODULE: case MRB_TT_ENV: break; default: mrb_raise(mrb, E_TYPE_ERROR, "allocation failure"); } tt = MRB_INSTANCE_TT(cls); if (tt != MRB_TT_FALSE && ttype != MRB_TT_SCLASS && ttype != MRB_TT_ICLASS && ttype != MRB_TT_ENV && ttype != MRB_TT_BIGINT && ttype != tt) { mrb_raisef(mrb, E_TYPE_ERROR, "allocation failure of %C", cls); } } if (ttype <= MRB_TT_FREE) { mrb_raisef(mrb, E_TYPE_ERROR, "allocation failure of %C (type %d)", cls, (int)ttype); } #ifdef MRB_GC_STRESS mrb_full_gc(mrb); #endif if (gc->threshold < gc->live) { mrb_incremental_gc(mrb); } gc_arena_keep(mrb, gc); if (gc->free_heaps == NULL) { add_heap(mrb, gc); } RVALUE *p = gc->free_heaps->freelist; gc->free_heaps->freelist = p->as.free.next; if (gc->free_heaps->freelist == NULL) { gc->free_heaps = gc->free_heaps->free_next; } gc->live++; gc_protect(mrb, gc, &p->as.basic); *p = RVALUE_zero; p->as.basic.tt = ttype; p->as.basic.c = cls; paint_partial_white(gc, &p->as.basic); return &p->as.basic; } static inline void add_gray_list(mrb_gc *gc, struct RBasic *obj) { #ifdef MRB_GC_STRESS if (obj->tt > MRB_TT_MAXDEFINE) { abort(); } #endif paint_gray(obj); obj->gcnext = gc->gray_list; gc->gray_list = obj; } static void mark_context_stack(mrb_state *mrb, struct mrb_context *c) { size_t i, e; if (c->stbase == NULL) return; if (c->ci) { e = (c->ci->stack ? c->ci->stack - c->stbase : 0); e += mrb_ci_nregs(c->ci); } else { e = 0; } if (c->stbase + e > c->stend) e = c->stend - c->stbase; for (i=0; istbase[i]; if (!mrb_immediate_p(v)) { mrb_gc_mark(mrb, mrb_basic_ptr(v)); } } e = c->stend - c->stbase; for (; istbase[i]); } } static void mark_context(mrb_state *mrb, struct mrb_context *c) { mrb_callinfo *ci; start: if (c->status == MRB_FIBER_TERMINATED) return; /* mark VM stack */ mark_context_stack(mrb, c); /* mark call stack */ if (c->cibase) { for (ci = c->cibase; ci <= c->ci; ci++) { mrb_gc_mark(mrb, (struct RBasic*)ci->proc); mrb_gc_mark(mrb, (struct RBasic*)ci->u.target_class); } } /* mark fibers */ mrb_gc_mark(mrb, (struct RBasic*)c->fib); if (c->prev) { c = c->prev; goto start; } } static size_t gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) { size_t children = 0; mrb_assert(is_gray(obj)); paint_black(obj); mrb_gc_mark(mrb, (struct RBasic*)obj->c); switch (obj->tt) { case MRB_TT_ICLASS: { struct RClass *c = (struct RClass*)obj; if (MRB_FLAG_TEST(c, MRB_FL_CLASS_IS_ORIGIN)) { children += mrb_gc_mark_mt(mrb, c); } mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super); children++; } break; case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_SCLASS: { struct RClass *c = (struct RClass*)obj; mrb_gc_mark_mt(mrb, c); mrb_gc_mark(mrb, (struct RBasic*)c->super); children += mrb_gc_mark_mt(mrb, c); children++; } /* fall through */ case MRB_TT_OBJECT: case MRB_TT_CDATA: children += mrb_gc_mark_iv(mrb, (struct RObject*)obj); break; case MRB_TT_PROC: { struct RProc *p = (struct RProc*)obj; mrb_gc_mark(mrb, (struct RBasic*)p->upper); mrb_gc_mark(mrb, (struct RBasic*)p->e.env); children+=2; } break; case MRB_TT_ENV: { struct REnv *e = (struct REnv*)obj; // The data stack must always be protected from GC regardless of the MRB_ENV_CLOSE flag. // This is because the data stack is not protected if the fiber is GC'd. mrb_int len = MRB_ENV_LEN(e); for (mrb_int i=0; istack[i]); } children += len; } break; case MRB_TT_FIBER: { struct mrb_context *c = ((struct RFiber*)obj)->cxt; if (!c || c->status == MRB_FIBER_TERMINATED) break; mark_context(mrb, c); if (!c->ci) break; /* mark stack */ size_t i = c->ci->stack - c->stbase; i += mrb_ci_nregs(c->ci); if (c->stbase + i > c->stend) i = c->stend - c->stbase; children += i; /* mark closure */ if (c->cibase) { children += c->ci - c->cibase + 1; } } break; case MRB_TT_STRUCT: case MRB_TT_ARRAY: { struct RArray *a = (struct RArray*)obj; size_t len = ARY_LEN(a); mrb_value *p = ARY_PTR(a); for (size_t i=0; ias.heap.aux.fshared); } break; case MRB_TT_RANGE: children += mrb_gc_mark_range(mrb, (struct RRange*)obj); break; case MRB_TT_BREAK: { struct RBreak *brk = (struct RBreak*)obj; mrb_gc_mark_value(mrb, mrb_break_value_get(brk)); children++; } break; case MRB_TT_EXCEPTION: children += mrb_gc_mark_iv(mrb, (struct RObject*)obj); if (((struct RException*)obj)->mesg) { mrb_gc_mark(mrb, (struct RBasic*)((struct RException*)obj)->mesg); children++; } if (((struct RException*)obj)->backtrace) { mrb_gc_mark(mrb, (struct RBasic*)((struct RException*)obj)->backtrace); children++; } break; case MRB_TT_BACKTRACE: children += ((struct RBacktrace*)obj)->len; break; #if defined(MRB_USE_RATIONAL) && defined(MRB_USE_BIGINT) case MRB_TT_RATIONAL: children += mrb_rational_mark(mrb, obj); break; #endif default: break; } return children; } MRB_API void mrb_gc_mark(mrb_state *mrb, struct RBasic *obj) { if (obj == 0) return; if (!is_white(obj)) return; if (is_red(obj)) return; mrb_assert((obj)->tt != MRB_TT_FREE); add_gray_list(&mrb->gc, obj); } static void obj_free(mrb_state *mrb, struct RBasic *obj, mrb_bool end) { DEBUG(fprintf(stderr, "obj_free(%p,tt=%d)\n",obj,obj->tt)); switch (obj->tt) { case MRB_TT_OBJECT: mrb_gc_free_iv(mrb, (struct RObject*)obj); break; case MRB_TT_EXCEPTION: mrb_gc_free_iv(mrb, (struct RObject*)obj); break; case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_SCLASS: mrb_gc_free_mt(mrb, (struct RClass*)obj); mrb_gc_free_iv(mrb, (struct RObject*)obj); if (!end) mrb_mc_clear_by_class(mrb, (struct RClass*)obj); break; case MRB_TT_ICLASS: if (MRB_FLAG_TEST(obj, MRB_FL_CLASS_IS_ORIGIN)) mrb_gc_free_mt(mrb, (struct RClass*)obj); if (!end) mrb_mc_clear_by_class(mrb, (struct RClass*)obj); break; case MRB_TT_ENV: { struct REnv *e = (struct REnv*)obj; if (!MRB_ENV_ONSTACK_P(e)) { mrb_free(mrb, e->stack); } } break; case MRB_TT_FIBER: { struct mrb_context *c = ((struct RFiber*)obj)->cxt; if (c && c != mrb->root_c) { if (!end && c->status != MRB_FIBER_TERMINATED) { mrb_callinfo *ci = c->ci; mrb_callinfo *ce = c->cibase; while (ce <= ci) { struct REnv *e = ci->u.env; if (e && heap_p(&mrb->gc, (struct RBasic*)e) && !is_dead(&mrb->gc, (struct RBasic*)e) && e->tt == MRB_TT_ENV && MRB_ENV_ONSTACK_P(e)) { mrb_env_unshare(mrb, e, TRUE); } ci--; } } mrb_free_context(mrb, c); } } break; case MRB_TT_STRUCT: case MRB_TT_ARRAY: if (ARY_SHARED_P(obj)) mrb_ary_decref(mrb, ((struct RArray*)obj)->as.heap.aux.shared); else if (!ARY_EMBED_P(obj)) mrb_free(mrb, ((struct RArray*)obj)->as.heap.ptr); break; case MRB_TT_HASH: mrb_gc_free_iv(mrb, (struct RObject*)obj); mrb_gc_free_hash(mrb, (struct RHash*)obj); break; case MRB_TT_STRING: mrb_gc_free_str(mrb, (struct RString*)obj); break; case MRB_TT_PROC: { struct RProc *p = (struct RProc*)obj; if (!MRB_PROC_CFUNC_P(p) && !MRB_PROC_ALIAS_P(p) && p->body.irep) { mrb_irep *irep = (mrb_irep*)p->body.irep; if (end) { mrb_irep_cutref(mrb, irep); } mrb_irep_decref(mrb, irep); } } break; case MRB_TT_RANGE: mrb_gc_free_range(mrb, ((struct RRange*)obj)); break; case MRB_TT_CDATA: { struct RData *d = (struct RData*)obj; if (d->type && d->type->dfree) { d->type->dfree(mrb, d->data); } mrb_gc_free_iv(mrb, (struct RObject*)obj); } break; #if defined(MRB_USE_RATIONAL) && defined(MRB_INT64) && defined(MRB_32BIT) case MRB_TT_RATIONAL: { struct RData *o = (struct RData*)obj; mrb_free(mrb, o->iv); } break; #endif #if defined(MRB_USE_COMPLEX) && defined(MRB_32BIT) && !defined(MRB_USE_FLOAT32) case MRB_TT_COMPLEX: { struct RData *o = (struct RData*)obj; mrb_free(mrb, o->iv); } break; #endif #ifdef MRB_USE_BIGINT case MRB_TT_BIGINT: mrb_gc_free_bint(mrb, obj); break; #endif case MRB_TT_BACKTRACE: { struct RBacktrace *bt = (struct RBacktrace*)obj; for (size_t i = 0; i < bt->len; i++) { const mrb_irep *irep = bt->locations[i].irep; if (irep == NULL) continue; mrb_irep_decref(mrb, (mrb_irep*)irep); } mrb_free(mrb, bt->locations); } default: break; } #if defined(MRB_GC_STRESS) && defined(MRB_DEBUG) memset(obj, -1, sizeof(RVALUE)); paint_white(obj); #endif obj->tt = MRB_TT_FREE; } static void root_scan_phase(mrb_state *mrb, mrb_gc *gc) { int i, e; if (!is_minor_gc(gc)) { gc->gray_list = NULL; gc->atomic_gray_list = NULL; } mrb_gc_mark_gv(mrb); /* mark arena */ for (i=0,e=gc->arena_idx; iarena[i]); } /* mark class hierarchy */ mrb_gc_mark(mrb, (struct RBasic*)mrb->object_class); /* mark built-in classes */ mrb_gc_mark(mrb, (struct RBasic*)mrb->class_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->module_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->proc_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->string_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->array_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->hash_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->range_class); #ifndef MRB_NO_FLOAT mrb_gc_mark(mrb, (struct RBasic*)mrb->float_class); #endif mrb_gc_mark(mrb, (struct RBasic*)mrb->integer_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->true_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->false_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->nil_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->symbol_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->kernel_module); mrb_gc_mark(mrb, (struct RBasic*)mrb->eException_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->eStandardError_class); /* mark top_self */ mrb_gc_mark(mrb, (struct RBasic*)mrb->top_self); /* mark exception */ mrb_gc_mark(mrb, (struct RBasic*)mrb->exc); mark_context(mrb, mrb->c); if (mrb->root_c != mrb->c) { mark_context(mrb, mrb->root_c); } } static void gc_mark_gray_list(mrb_state *mrb, mrb_gc *gc) { while (gc->gray_list) { struct RBasic *obj = gc->gray_list; gc->gray_list = obj->gcnext; obj->gcnext = NULL; gc_mark_children(mrb, gc, obj); } } static size_t incremental_marking_phase(mrb_state *mrb, mrb_gc *gc, size_t limit) { size_t tried_marks = 0; while (gc->gray_list && tried_marks < limit) { struct RBasic *obj = gc->gray_list; gc->gray_list = obj->gcnext; obj->gcnext = NULL; tried_marks += gc_mark_children(mrb, gc, obj); } return tried_marks; } static void clear_error_object(mrb_state *mrb, struct RObject *obj) { if (obj == 0) return; if (!is_white(obj)) return; paint_black(obj); mrb_gc_mark(mrb, (struct RBasic*)obj->c); mrb_gc_free_iv(mrb, obj); struct RException *err = (struct RException*)obj; err->iv = NULL; err->mesg = NULL; err->backtrace = NULL; } static void final_marking_phase(mrb_state *mrb, mrb_gc *gc) { int i, e; /* mark arena */ for (i=0,e=gc->arena_idx; iarena[i]); } mrb_gc_mark_gv(mrb); mark_context(mrb, mrb->c); if (mrb->c != mrb->root_c) { mark_context(mrb, mrb->root_c); } mrb_gc_mark(mrb, (struct RBasic*)mrb->exc); /* mark pre-allocated exception */ clear_error_object(mrb, mrb->nomem_err); clear_error_object(mrb, mrb->stack_err); #ifdef MRB_GC_FIXED_ARENA clear_error_object(mrb, mrb->arena_err); #endif gc_mark_gray_list(mrb, gc); mrb_assert(gc->gray_list == NULL); gc->gray_list = gc->atomic_gray_list; gc->atomic_gray_list = NULL; gc_mark_gray_list(mrb, gc); mrb_assert(gc->gray_list == NULL); } static void prepare_incremental_sweep(mrb_state *mrb, mrb_gc *gc) { // mrb_assert(gc->atomic_gray_list == NULL); // mrb_assert(gc->gray_list == NULL); gc->state = MRB_GC_STATE_SWEEP; gc->sweeps = NULL; gc->live_after_mark = gc->live; } static size_t incremental_sweep_phase(mrb_state *mrb, mrb_gc *gc, size_t limit) { mrb_heap_page *prev = gc->sweeps; mrb_heap_page *page = prev ? prev->next : gc->heaps; size_t tried_sweep = 0; while (page && (tried_sweep < limit)) { RVALUE *p = page->objects; RVALUE *e = p + MRB_HEAP_PAGE_SIZE; size_t freed = 0; mrb_bool dead_slot = TRUE; if (is_minor_gc(gc) && page->old) { /* skip a slot which doesn't contain any young object */ p = e; dead_slot = FALSE; } while (pas.basic)) { if (p->as.basic.tt != MRB_TT_FREE) { obj_free(mrb, &p->as.basic, FALSE); if (p->as.basic.tt == MRB_TT_FREE) { p->as.free.next = page->freelist; page->freelist = p; freed++; } else { dead_slot = FALSE; } } } else { if (!is_generational(gc)) paint_partial_white(gc, &p->as.basic); /* next gc target */ dead_slot = FALSE; } p++; } /* free dead slot */ if (dead_slot) { mrb_heap_page *next = page->next; if (prev) prev->next = next; if (gc->heaps == page) gc->heaps = page->next; mrb_free(mrb, page); page = next; } else { if (page->freelist == NULL && is_minor_gc(gc)) page->old = TRUE; else page->old = FALSE; prev = page; page = page->next; } tried_sweep += MRB_HEAP_PAGE_SIZE; gc->live -= freed; gc->live_after_mark -= freed; } gc->sweeps = prev; /* rebuild free_heaps link */ gc->free_heaps = NULL; for (mrb_heap_page *p = gc->heaps; p; p=p->next) { if (p->freelist) { p->free_next = gc->free_heaps; gc->free_heaps = p; } } return tried_sweep; } static size_t incremental_gc(mrb_state *mrb, mrb_gc *gc, size_t limit) { switch (gc->state) { case MRB_GC_STATE_ROOT: root_scan_phase(mrb, gc); gc->state = MRB_GC_STATE_MARK; flip_white_part(gc); return 0; case MRB_GC_STATE_MARK: if (gc->gray_list) { return incremental_marking_phase(mrb, gc, limit); } else { final_marking_phase(mrb, gc); prepare_incremental_sweep(mrb, gc); return 0; } case MRB_GC_STATE_SWEEP: { size_t tried_sweep = 0; tried_sweep = incremental_sweep_phase(mrb, gc, limit); if (tried_sweep == 0) gc->state = MRB_GC_STATE_ROOT; return tried_sweep; } default: /* unknown state */ mrb_assert(0); return 0; } } static void incremental_gc_finish(mrb_state *mrb, mrb_gc *gc) { do { incremental_gc(mrb, gc, SIZE_MAX); } while (gc->state != MRB_GC_STATE_ROOT); } static void incremental_gc_step(mrb_state *mrb, mrb_gc *gc) { size_t limit = 0, result = 0; limit = (GC_STEP_SIZE/100) * gc->step_ratio; while (result < limit) { result += incremental_gc(mrb, gc, limit); if (gc->state == MRB_GC_STATE_ROOT) break; } gc->threshold = gc->live + GC_STEP_SIZE; } static void clear_all_old(mrb_state *mrb, mrb_gc *gc) { mrb_assert(is_generational(gc)); if (gc->full) { /* finish the half baked GC */ incremental_gc_finish(mrb, gc); } /* Sweep the dead objects, then reset all the live objects * (including all the old objects, of course) to white. */ gc->generational = FALSE; prepare_incremental_sweep(mrb, gc); incremental_gc_finish(mrb, gc); gc->generational = TRUE; /* The gray objects have already been painted as white */ gc->atomic_gray_list = gc->gray_list = NULL; } MRB_API void mrb_incremental_gc(mrb_state *mrb) { mrb_gc *gc = &mrb->gc; if (gc->disabled || gc->iterating) return; if (is_minor_gc(gc)) { incremental_gc_finish(mrb, gc); } else { incremental_gc_step(mrb, gc); } if (gc->state == MRB_GC_STATE_ROOT) { mrb_assert(gc->live >= gc->live_after_mark); gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio; if (gc->threshold < GC_STEP_SIZE) { gc->threshold = GC_STEP_SIZE; } if (is_major_gc(gc)) { size_t threshold = gc->live_after_mark/100 * MAJOR_GC_INC_RATIO; gc->full = FALSE; if (threshold < MAJOR_GC_TOOMANY) { gc->oldgen_threshold = threshold; } else { /* too many objects allocated during incremental GC, */ /* instead of increasing threshold, invoke full GC. */ mrb_full_gc(mrb); } } else if (is_minor_gc(gc) && gc->live > gc->oldgen_threshold) { clear_all_old(mrb, gc); gc->full = TRUE; } } } /* Perform a full gc cycle */ MRB_API void mrb_full_gc(mrb_state *mrb) { mrb_gc *gc = &mrb->gc; if (!mrb->c) return; if (gc->disabled || gc->iterating) return; if (is_generational(gc)) { /* clear all the old objects back to young */ clear_all_old(mrb, gc); gc->full = TRUE; } else if (gc->state != MRB_GC_STATE_ROOT) { /* finish half baked GC cycle */ incremental_gc_finish(mrb, gc); } incremental_gc_finish(mrb, gc); gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio; if (is_generational(gc)) { gc->oldgen_threshold = gc->live_after_mark/100 * MAJOR_GC_INC_RATIO; gc->full = FALSE; } #ifdef MRB_USE_MALLOC_TRIM malloc_trim(0); #endif } MRB_API void mrb_garbage_collect(mrb_state *mrb) { mrb_full_gc(mrb); } /* * Field write barrier * Paint obj(Black) -> value(White) to obj(Black) -> value(Gray). */ MRB_API void mrb_field_write_barrier(mrb_state *mrb, struct RBasic *obj, struct RBasic *value) { mrb_gc *gc = &mrb->gc; if (!value) return; if (!is_black(obj)) return; if (!is_white(value)) return; if (is_red(value)) return; mrb_assert(gc->state == MRB_GC_STATE_MARK || (!is_dead(gc, value) && !is_dead(gc, obj))); mrb_assert(is_generational(gc) || gc->state != MRB_GC_STATE_ROOT); if (is_generational(gc) || gc->state == MRB_GC_STATE_MARK) { add_gray_list(gc, value); } else { mrb_assert(gc->state == MRB_GC_STATE_SWEEP); paint_partial_white(gc, obj); /* for never write barriers */ } } /* * Write barrier * Paint obj(Black) to obj(Gray). * * The object that is painted gray will be traversed atomically in final * mark phase. So you use this write barrier if it's frequency written spot. * e.g. Set element on Array. */ MRB_API void mrb_write_barrier(mrb_state *mrb, struct RBasic *obj) { mrb_gc *gc = &mrb->gc; if (!is_black(obj)) return; mrb_assert(!is_dead(gc, obj)); mrb_assert(is_generational(gc) || gc->state != MRB_GC_STATE_ROOT); paint_gray(obj); obj->gcnext = gc->atomic_gray_list; gc->atomic_gray_list = obj; } /* * call-seq: * GC.start -> nil * * Initiates full garbage collection. * */ static mrb_value gc_start(mrb_state *mrb, mrb_value obj) { mrb_full_gc(mrb); return mrb_nil_value(); } /* * call-seq: * GC.enable -> true or false * * Enables garbage collection, returning true if garbage * collection was previously disabled. * * GC.disable #=> false * GC.enable #=> true * GC.enable #=> false * */ static mrb_value gc_enable(mrb_state *mrb, mrb_value obj) { mrb_bool old = mrb->gc.disabled; mrb->gc.disabled = FALSE; return mrb_bool_value(old); } /* * call-seq: * GC.disable -> true or false * * Disables garbage collection, returning true if garbage * collection was already disabled. * * GC.disable #=> false * GC.disable #=> true * */ static mrb_value gc_disable(mrb_state *mrb, mrb_value obj) { mrb_bool old = mrb->gc.disabled; mrb->gc.disabled = TRUE; return mrb_bool_value(old); } /* * call-seq: * GC.interval_ratio -> int * * Returns ratio of GC interval. Default value is 200(%). * */ static mrb_value gc_interval_ratio_get(mrb_state *mrb, mrb_value obj) { return mrb_int_value(mrb, mrb->gc.interval_ratio); } /* * call-seq: * GC.interval_ratio = int -> nil * * Updates ratio of GC interval. Default value is 200(%). * GC start as soon as after end all step of GC if you set 100(%). * */ static mrb_value gc_interval_ratio_set(mrb_state *mrb, mrb_value obj) { mrb_int ratio; mrb_get_args(mrb, "i", &ratio); mrb->gc.interval_ratio = (int)ratio; return mrb_nil_value(); } /* * call-seq: * GC.step_ratio -> int * * Returns step span ratio of Incremental GC. Default value is 200(%). * */ static mrb_value gc_step_ratio_get(mrb_state *mrb, mrb_value obj) { return mrb_int_value(mrb, mrb->gc.step_ratio); } /* * call-seq: * GC.step_ratio = int -> nil * * Updates step span ratio of Incremental GC. Default value is 200(%). * 1 step of incrementalGC becomes long if a rate is big. * */ static mrb_value gc_step_ratio_set(mrb_state *mrb, mrb_value obj) { mrb_int ratio; mrb_get_args(mrb, "i", &ratio); mrb->gc.step_ratio = (int)ratio; return mrb_nil_value(); } static void change_gen_gc_mode(mrb_state *mrb, mrb_gc *gc, mrb_bool enable) { if (gc->disabled || gc->iterating) { mrb_raise(mrb, E_RUNTIME_ERROR, "generational mode changed when GC disabled"); return; } if (is_generational(gc) && !enable) { clear_all_old(mrb, gc); mrb_assert(gc->state == MRB_GC_STATE_ROOT); gc->full = FALSE; } else if (!is_generational(gc) && enable) { incremental_gc_finish(mrb, gc); gc->oldgen_threshold = gc->live_after_mark/100 * MAJOR_GC_INC_RATIO; gc->full = FALSE; } gc->generational = enable; } /* * call-seq: * GC.generational_mode -> true or false * * Returns generational or normal gc mode. * */ static mrb_value gc_generational_mode_get(mrb_state *mrb, mrb_value self) { return mrb_bool_value(mrb->gc.generational); } /* * call-seq: * GC.generational_mode = true or false -> true or false * * Changes to generational or normal gc mode. * */ static mrb_value gc_generational_mode_set(mrb_state *mrb, mrb_value self) { mrb_bool enable; mrb_get_args(mrb, "b", &enable); if (mrb->gc.generational != enable) change_gen_gc_mode(mrb, &mrb->gc, enable); return mrb_bool_value(enable); } static void gc_each_objects(mrb_state *mrb, mrb_gc *gc, mrb_each_object_callback *callback, void *data) { mrb_heap_page* page; page = gc->heaps; while (page != NULL) { RVALUE *p; p = page->objects; for (int i=0; i < MRB_HEAP_PAGE_SIZE; i++) { if ((*callback)(mrb, &p[i].as.basic, data) == MRB_EACH_OBJ_BREAK) return; } page = page->next; } } void mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, void *data) { mrb_bool iterating = mrb->gc.iterating; mrb_full_gc(mrb); mrb->gc.iterating = TRUE; if (iterating) { gc_each_objects(mrb, &mrb->gc, callback, data); } else { struct mrb_jmpbuf *prev_jmp = mrb->jmp; struct mrb_jmpbuf c_jmp; MRB_TRY(&c_jmp) { mrb->jmp = &c_jmp; gc_each_objects(mrb, &mrb->gc, callback, data); mrb->jmp = prev_jmp; mrb->gc.iterating = iterating; } MRB_CATCH(&c_jmp) { mrb->gc.iterating = iterating; mrb->jmp = prev_jmp; MRB_THROW(prev_jmp); } MRB_END_EXC(&c_jmp); } } size_t mrb_objspace_page_slot_size(void) { return sizeof(RVALUE); } void mrb_init_gc(mrb_state *mrb) { struct RClass *gc; mrb_static_assert_object_size(RVALUE); gc = mrb_define_module_id(mrb, MRB_SYM(GC)); mrb_define_class_method_id(mrb, gc, MRB_SYM(start), gc_start, MRB_ARGS_NONE()); mrb_define_class_method_id(mrb, gc, MRB_SYM(enable), gc_enable, MRB_ARGS_NONE()); mrb_define_class_method_id(mrb, gc, MRB_SYM(disable), gc_disable, MRB_ARGS_NONE()); mrb_define_class_method_id(mrb, gc, MRB_SYM(interval_ratio), gc_interval_ratio_get, MRB_ARGS_NONE()); mrb_define_class_method_id(mrb, gc, MRB_SYM_E(interval_ratio), gc_interval_ratio_set, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, gc, MRB_SYM(step_ratio), gc_step_ratio_get, MRB_ARGS_NONE()); mrb_define_class_method_id(mrb, gc, MRB_SYM_E(step_ratio), gc_step_ratio_set, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, gc, MRB_SYM_E(generational_mode), gc_generational_mode_set, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, gc, MRB_SYM(generational_mode), gc_generational_mode_get, MRB_ARGS_NONE()); } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/state.c0000644000000000000000000000013215077107276017746 xustar0030 mtime=1761382078.136420492 30 atime=1761382080.158411242 30 ctime=1761382108.566301925 nghttp2-1.68.0/third-party/mruby/src/state.c0000644000175100017510000001117015077107276020336 0ustar00runnerrunner/* ** state.c - mrb_state open/close functions ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include void mrb_init_core(mrb_state*); void mrb_init_mrbgems(mrb_state*); void mrb_gc_init(mrb_state*, mrb_gc *gc); void mrb_gc_destroy(mrb_state*, mrb_gc *gc); int mrb_core_init_protect(mrb_state *mrb, void (*body)(mrb_state*, void*), void *opaque); static void init_gc_and_core(mrb_state *mrb, void *opaque) { static const struct mrb_context mrb_context_zero = { 0 }; mrb_gc_init(mrb, &mrb->gc); mrb->c = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context)); *mrb->c = mrb_context_zero; mrb->root_c = mrb->c; mrb_init_core(mrb); } MRB_API mrb_state* mrb_open_core(void) { static const mrb_state mrb_state_zero = { 0 }; mrb_state *mrb; mrb = (mrb_state*)mrb_basic_alloc_func(NULL, sizeof(mrb_state)); if (mrb == NULL) return NULL; *mrb = mrb_state_zero; mrb->atexit_stack_len = 0; if (mrb_core_init_protect(mrb, init_gc_and_core, NULL)) { mrb_close(mrb); return NULL; } return mrb; } #ifndef MRB_NO_GEMS static void init_mrbgems(mrb_state *mrb, void *opaque) { mrb_init_mrbgems(mrb); } #endif MRB_API mrb_state* mrb_open(void) { mrb_state *mrb = mrb_open_core(); if (mrb == NULL) { return NULL; } #ifndef MRB_NO_GEMS if (mrb_core_init_protect(mrb, init_mrbgems, NULL)) { mrb_close(mrb); return NULL; } mrb_gc_arena_restore(mrb, 0); #endif return mrb; } void mrb_free_symtbl(mrb_state *mrb); void mrb_irep_incref(mrb_state *mrb, mrb_irep *irep) { if (irep->flags & MRB_IREP_NO_FREE) return; if (irep->refcnt == UINT16_MAX) { mrb_garbage_collect(mrb); if (irep->refcnt == UINT16_MAX) { mrb_raise(mrb, E_RUNTIME_ERROR, "too many irep references"); } } irep->refcnt++; } void mrb_irep_decref(mrb_state *mrb, mrb_irep *irep) { if (irep->flags & MRB_IREP_NO_FREE) return; irep->refcnt--; if (irep->refcnt == 0) { mrb_irep_free(mrb, irep); } } void mrb_irep_cutref(mrb_state *mrb, mrb_irep *irep) { mrb_irep **reps; int i; if (irep->flags & MRB_IREP_NO_FREE) return; reps = (mrb_irep**)irep->reps; if (!reps) return; for (i=0; irlen; i++) { mrb_irep *tmp = reps[i]; reps[i] = NULL; if (tmp) mrb_irep_decref(mrb, tmp); } } void mrb_irep_free(mrb_state *mrb, mrb_irep *irep) { int i; if (irep->flags & MRB_IREP_NO_FREE) return; if (!(irep->flags & MRB_ISEQ_NO_FREE)) mrb_free(mrb, (void*)irep->iseq); if (irep->pool) { for (i=0; iplen; i++) { if ((irep->pool[i].tt & 3) == IREP_TT_STR || irep->pool[i].tt == IREP_TT_BIGINT) { mrb_free(mrb, (void*)irep->pool[i].u.str); } } mrb_free(mrb, (void*)irep->pool); } mrb_free(mrb, (void*)irep->syms); if (irep->reps) { for (i=0; irlen; i++) { if (irep->reps[i]) mrb_irep_decref(mrb, (mrb_irep*)irep->reps[i]); } mrb_free(mrb, (void*)irep->reps); } mrb_free(mrb, (void*)irep->lv); mrb_debug_info_free(mrb, irep->debug_info); #ifdef MRB_DEBUG memset(irep, -1, sizeof(*irep)); #endif mrb_free(mrb, irep); } MRB_API void mrb_free_context(mrb_state *mrb, struct mrb_context *c) { if (!c) return; mrb_free(mrb, c->stbase); mrb_free(mrb, c->cibase); mrb_free(mrb, c); } void mrb_protect_atexit(mrb_state *mrb); MRB_API void mrb_close(mrb_state *mrb) { if (!mrb) return; mrb_protect_atexit(mrb); /* free */ mrb_gc_free_gv(mrb); mrb_gc_destroy(mrb, &mrb->gc); mrb_free_context(mrb, mrb->root_c); mrb_free_symtbl(mrb); mrb_free(mrb, mrb); } MRB_API mrb_irep* mrb_add_irep(mrb_state *mrb) { static const mrb_irep mrb_irep_zero = { 0 }; mrb_irep *irep; irep = (mrb_irep*)mrb_malloc(mrb, sizeof(mrb_irep)); *irep = mrb_irep_zero; irep->refcnt = 1; return irep; } MRB_API mrb_value mrb_top_self(mrb_state *mrb) { return mrb_obj_value(mrb->top_self); } MRB_API void mrb_state_atexit(mrb_state *mrb, mrb_atexit_func f) { #ifdef MRB_FIXED_STATE_ATEXIT_STACK if (mrb->atexit_stack_len + 1 > MRB_FIXED_STATE_ATEXIT_STACK_SIZE) { mrb_raise(mrb, E_RUNTIME_ERROR, "exceeded fixed state atexit stack limit"); } #else size_t stack_size; stack_size = sizeof(mrb_atexit_func) * (mrb->atexit_stack_len + 1); if (mrb->atexit_stack_len == 0) { mrb->atexit_stack = (mrb_atexit_func*)mrb_malloc(mrb, stack_size); } else { mrb->atexit_stack = (mrb_atexit_func*)mrb_realloc(mrb, mrb->atexit_stack, stack_size); } #endif mrb->atexit_stack[mrb->atexit_stack_len++] = f; } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/kernel.c0000644000000000000000000000013215077107276020106 xustar0030 mtime=1761382078.135420497 30 atime=1761382080.156411252 30 ctime=1761382108.588301861 nghttp2-1.68.0/third-party/mruby/src/kernel.c0000644000175100017510000004556615077107276020516 0ustar00runnerrunner/* ** kernel.c - Kernel module ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include #include MRB_API mrb_bool mrb_func_basic_p(mrb_state *mrb, mrb_value obj, mrb_sym mid, mrb_func_t func) { struct RClass *c = mrb_class(mrb, obj); mrb_method_t m = mrb_method_search_vm(mrb, &c, mid); const struct RProc *p; if (MRB_METHOD_UNDEF_P(m)) return FALSE; if (MRB_METHOD_FUNC_P(m)) return MRB_METHOD_FUNC(m) == func; p = MRB_METHOD_PROC(m); if (MRB_PROC_CFUNC_P(p) && (MRB_PROC_CFUNC(p) == func)) return TRUE; return FALSE; } static mrb_bool mrb_obj_basic_to_s_p(mrb_state *mrb, mrb_value obj) { return mrb_func_basic_p(mrb, obj, MRB_SYM(to_s), mrb_any_to_s); } /* 15.3.1.3.17 */ /* * call-seq: * obj.inspect -> string * * Returns a string containing a human-readable representation of * obj. If not overridden and no instance variables, uses the * to_s method to generate the string. * obj. If not overridden, uses the to_s method to * generate the string. * * [ 1, 2, 3..4, 'five' ].inspect #=> "[1, 2, 3..4, \"five\"]" * Time.new.inspect #=> "2008-03-08 19:43:39 +0900" */ MRB_API mrb_value mrb_obj_inspect(mrb_state *mrb, mrb_value obj) { if (mrb_object_p(obj) && mrb_obj_basic_to_s_p(mrb, obj)) { return mrb_obj_iv_inspect(mrb, mrb_obj_ptr(obj)); } return mrb_any_to_s(mrb, obj); } /* 15.3.1.3.2 */ /* * call-seq: * obj === other -> true or false * * Case Equality---For class Object, effectively the same * as calling #==, but typically overridden by descendants * to provide meaningful semantics in case statements. */ static mrb_value mrb_eqq_m(mrb_state *mrb, mrb_value self) { mrb_value arg = mrb_get_arg1(mrb); return mrb_bool_value(mrb_equal(mrb, self, arg)); } static mrb_value mrb_cmp_m(mrb_state *mrb, mrb_value self) { mrb_value arg = mrb_get_arg1(mrb); /* recursion check */ for (mrb_callinfo *ci=&mrb->c->ci[-1]; ci>=mrb->c->cibase; ci--) { if (ci->mid == MRB_OPSYM(cmp) && mrb_obj_eq(mrb, self, ci->stack[0]) && mrb_obj_eq(mrb, arg, ci->stack[1])) { /* recursive <=> calling returns `nil` */ return mrb_nil_value(); } } if (mrb_equal(mrb, self, arg)) return mrb_fixnum_value(0); return mrb_nil_value(); } static mrb_bool inspect_recursive_p(mrb_state *mrb, mrb_value obj, int n) { for (mrb_callinfo *ci=&mrb->c->ci[-1-n]; ci>=mrb->c->cibase; ci--) { if (ci->mid == MRB_SYM(inspect) && mrb_obj_eq(mrb, obj, ci->stack[0])) { return TRUE; } } return FALSE; } mrb_bool mrb_inspect_recursive_p(mrb_state *mrb, mrb_value obj) { return inspect_recursive_p(mrb, obj, 0); } static mrb_value mrb_obj_inspect_recursive_p(mrb_state *mrb, mrb_value obj) { return mrb_bool_value(inspect_recursive_p(mrb, obj, 1)); } /* 15.3.1.3.3 */ /* 15.3.1.3.33 */ /* * Document-method: __id__ * Document-method: object_id * * call-seq: * obj.__id__ -> int * obj.object_id -> int * * Returns an integer identifier for obj. The same number will * be returned on all calls to id for a given object, and * no two active objects will share an id. * Object#object_id is a different concept from the * :name notation, which returns the symbol id of * name. Replaces the deprecated Object#id. */ mrb_value mrb_obj_id_m(mrb_state *mrb, mrb_value self) { return mrb_fixnum_value(mrb_obj_id(self)); } static int env_bidx(struct REnv *e) { int bidx; /* use saved block arg position */ bidx = MRB_ENV_BIDX(e); /* bidx may be useless (e.g. define_method) */ if (bidx >= MRB_ENV_LEN(e)) return -1; return bidx; } /* 15.3.1.2.2 */ /* 15.3.1.2.5 */ /* 15.3.1.3.6 */ /* 15.3.1.3.25 */ /* * call-seq: * block_given? -> true or false * iterator? -> true or false * * Returns true if yield would execute a * block in the current context. The iterator? form * is mildly deprecated. * * def try * if block_given? * yield * else * "no block" * end * end * try #=> "no block" * try { "hello" } #=> "hello" * try do "hello" end #=> "hello" */ static mrb_value mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self) { mrb_callinfo *ci = &mrb->c->ci[-1]; mrb_callinfo *cibase = mrb->c->cibase; mrb_value *bp; int bidx; struct REnv *e = NULL; const struct RProc *p; if (ci <= cibase) { /* toplevel does not have block */ return mrb_false_value(); } p = ci->proc; /* search method/class/module proc */ while (p) { if (MRB_PROC_SCOPE_P(p)) break; e = MRB_PROC_ENV(p); p = p->upper; } if (p == NULL) return mrb_false_value(); if (e) { bidx = env_bidx(e); if (bidx < 0) return mrb_false_value(); bp = &e->stack[bidx]; goto block_given; } /* search ci corresponding to proc */ while (cibase < ci) { if (ci->proc == p) break; ci--; } if (ci == cibase) { /* proc is closure */ if (!MRB_PROC_ENV_P(p)) return mrb_false_value(); e = MRB_PROC_ENV(p); bidx = env_bidx(e); if (bidx < 0) return mrb_false_value(); bp = &e->stack[bidx]; } else if ((e = mrb_vm_ci_env(ci)) != NULL) { /* top-level does not have block slot (always false) */ if (e->stack == mrb->c->stbase) return mrb_false_value(); bidx = env_bidx(e); /* bidx may be useless (e.g. define_method) */ if (bidx < 0) return mrb_false_value(); bp = &e->stack[bidx]; } else { uint8_t n = ci->n == 15 ? 1 : ci->n; uint8_t k = ci->nk == 15 ? 1 : ci->nk*2; bidx = n + k + 1; /* self + args + kargs => bidx */ bp = &ci->stack[bidx]; } block_given: if (mrb_nil_p(*bp)) return mrb_false_value(); return mrb_true_value(); } /* 15.3.1.3.7 */ /* * call-seq: * obj.class -> class * * Returns the class of obj. This method must always be * called with an explicit receiver, as class is also a * reserved word in Ruby. * * 1.class #=> Integer * self.class #=> Object */ static mrb_value mrb_obj_class_m(mrb_state *mrb, mrb_value self) { return mrb_obj_value(mrb_obj_class(mrb, self)); } MRB_API mrb_value mrb_obj_freeze(mrb_state *mrb, mrb_value self) { if (!mrb_immediate_p(self)) { struct RBasic *b = mrb_basic_ptr(self); if (!mrb_frozen_p(b)) { b->frozen = 1; if (b->c->tt == MRB_TT_SCLASS) b->c->frozen = 1; } } return self; } static mrb_value mrb_obj_frozen(mrb_state *mrb, mrb_value self) { return mrb_bool_value(mrb_immediate_p(self) || mrb_frozen_p(mrb_basic_ptr(self))); } /* 15.3.1.3.15 */ /* * call-seq: * obj.hash -> int * * Generates a Integer hash value for this object. This * function must have the property that a.eql?(b) implies * a.hash == b.hash. The hash value is used by class * Hash. Any hash value that exceeds the capacity of a * Integer will be truncated before being used. */ static mrb_value mrb_obj_hash(mrb_state *mrb, mrb_value self) { #ifdef MRB_USE_BIGINT if (mrb_bigint_p(self)) { return mrb_bint_hash(mrb, self); } #endif return mrb_int_value(mrb, mrb_obj_id(self)); } /* 15.3.1.3.16 */ mrb_value mrb_obj_init_copy(mrb_state *mrb, mrb_value self) { mrb_value orig = mrb_get_arg1(mrb); if (mrb_obj_equal(mrb, self, orig)) return self; if ((mrb_type(self) != mrb_type(orig)) || (mrb_obj_class(mrb, self) != mrb_obj_class(mrb, orig))) { mrb_raise(mrb, E_TYPE_ERROR, "initialize_copy should take same class object"); } return self; } MRB_API mrb_bool mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, const struct RClass* c) { if (mrb_obj_class(mrb, obj) == c) return TRUE; return FALSE; } /* 15.3.1.3.19 */ /* * call-seq: * obj.instance_of?(class) -> true or false * * Returns true if obj is an instance of the given * class. See also Object#kind_of?. */ static mrb_value obj_is_instance_of(mrb_state *mrb, mrb_value self) { struct RClass *c; mrb_get_args(mrb, "c", &c); return mrb_bool_value(mrb_obj_is_instance_of(mrb, self, c)); } /* 15.3.1.3.24 */ /* 15.3.1.3.26 */ /* * call-seq: * obj.is_a?(class) -> true or false * obj.kind_of?(class) -> true or false * * Returns true if class is the class of * obj, or if class is one of the superclasses of * obj or modules included in obj. * * module M; end * class A * include M * end * class B < A; end * class C < B; end * b = B.new * b.instance_of? A #=> false * b.instance_of? B #=> true * b.instance_of? C #=> false * b.instance_of? M #=> false * b.kind_of? A #=> true * b.kind_of? B #=> true * b.kind_of? C #=> false * b.kind_of? M #=> true */ static mrb_value mrb_obj_is_kind_of_m(mrb_state *mrb, mrb_value self) { struct RClass *c; mrb_get_args(mrb, "c", &c); return mrb_bool_value(mrb_obj_is_kind_of(mrb, self, c)); } /* 15.3.1.3.32 */ /* * call_seq: * nil.nil? -> true * .nil? -> false * * Only the object nil responds true to nil?. */ static mrb_value mrb_false(mrb_state *mrb, mrb_value self) { return mrb_false_value(); } /* 15.3.1.2.12 */ /* 15.3.1.3.40 */ /* * call-seq: * raise * raise(string) * raise(exception [, string]) * * With no arguments, raises a RuntimeError * With a single +String+ argument, raises a * +RuntimeError+ with the string as a message. Otherwise, * the first parameter should be the name of an +Exception+ * class (or an object that returns an +Exception+ object when sent * an +exception+ message). The optional second parameter sets the * message associated with the exception, and the third parameter is an * array of callback information. Exceptions are caught by the * +rescue+ clause of begin...end blocks. * * raise "Failed to create socket" * raise ArgumentError, "No parameters", caller */ mrb_value mrb_f_raise(mrb_state *mrb, mrb_value self) { mrb_value exc, mesg; mrb_int argc; argc = mrb_get_args(mrb, "|oo", &exc, &mesg); mrb->c->ci->mid = 0; switch (argc) { case 0: mrb_raise(mrb, E_RUNTIME_ERROR, ""); break; case 1: if (mrb_string_p(exc)) { mesg = exc; exc = mrb_obj_value(E_RUNTIME_ERROR); } else { mesg = mrb_nil_value(); } /* fall through */ default: exc = mrb_make_exception(mrb, exc, mesg); mrb_exc_raise(mrb, exc); break; } return mrb_nil_value(); /* not reached */ } /* 15.3.1.3.41 */ /* * call-seq: * obj.remove_instance_variable(symbol) -> obj * * Removes the named instance variable from obj, returning that * variable's value. * * class Dummy * attr_reader :var * def initialize * @var = 99 * end * def remove * remove_instance_variable(:@var) * end * end * d = Dummy.new * d.var #=> 99 * d.remove #=> 99 * d.var #=> nil */ static mrb_value mrb_obj_remove_instance_variable(mrb_state *mrb, mrb_value self) { mrb_sym sym; mrb_value val; mrb_get_args(mrb, "n", &sym); mrb_iv_name_sym_check(mrb, sym); val = mrb_iv_remove(mrb, self, sym); if (mrb_undef_p(val)) { mrb_name_error(mrb, sym, "instance variable %n not defined", sym); } return val; } /* 15.3.1.3.43 */ /* * call-seq: * obj.respond_to?(symbol, include_private=false) -> true or false * * Returns +true+ if _obj_ responds to the given * method. Private methods are included in the search only if the * optional second parameter evaluates to +true+. * * If the method is not implemented, * as Process.fork on Windows, File.lchmod on GNU/Linux, etc., * false is returned. * * If the method is not defined, respond_to_missing? * method is called and the result is returned. */ static mrb_value obj_respond_to(mrb_state *mrb, mrb_value self) { mrb_sym id; mrb_bool priv = FALSE, respond_to_p; mrb_get_args(mrb, "n|b", &id, &priv); respond_to_p = mrb_respond_to(mrb, self, id); if (!respond_to_p) { mrb_sym rtm_id = MRB_SYM_Q(respond_to_missing); if (!mrb_func_basic_p(mrb, self, rtm_id, mrb_false)) { mrb_value v; v = mrb_funcall_id(mrb, self, rtm_id, 2, mrb_symbol_value(id), mrb_bool_value(priv)); return mrb_bool_value(mrb_bool(v)); } } return mrb_bool_value(respond_to_p); } static mrb_value mrb_obj_ceqq(mrb_state *mrb, mrb_value self) { mrb_value v = mrb_get_arg1(mrb); mrb_int i, len; mrb_sym eqq = MRB_OPSYM(eqq); mrb_value ary; mrb->c->ci->mid = 0; if (mrb_array_p(self)) { ary = self; } else if (mrb_nil_p(self)) { return mrb_false_value(); } else if (!mrb_respond_to(mrb, self, MRB_SYM(to_a))) { mrb_value c = mrb_funcall_argv(mrb, self, eqq, 1, &v); if (mrb_test(c)) return mrb_true_value(); return mrb_false_value(); } else { ary = mrb_funcall_argv(mrb, self, MRB_SYM(to_a), 0, NULL); if (mrb_nil_p(ary)) { return mrb_funcall_argv(mrb, self, eqq, 1, &v); } mrb_ensure_array_type(mrb, ary); } len = RARRAY_LEN(ary); for (i=0; ikernel_module = krn = mrb_define_module_id(mrb, MRB_SYM(Kernel)); /* 15.3.1 */ #if 0 mrb_define_class_method_id(mrb, krn, MRB_SYM_Q(block_given), mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.2.2 */ mrb_define_class_method_id(mrb, krn, MRB_SYM_Q(iterator), mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.2.5 */ #endif mrb_define_class_method_id(mrb, krn, MRB_SYM(raise), mrb_f_raise, MRB_ARGS_OPT(2)); /* 15.3.1.2.12 */ mrb_define_method_id(mrb, krn, MRB_OPSYM(eqq), mrb_eqq_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.2 */ mrb_define_method_id(mrb, krn, MRB_OPSYM(cmp), mrb_cmp_m, MRB_ARGS_REQ(1)); mrb_define_private_method_id(mrb, krn, MRB_SYM_Q(block_given), mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.3.6 */ mrb_define_method_id(mrb, krn, MRB_SYM(class), mrb_obj_class_m, MRB_ARGS_NONE()); /* 15.3.1.3.7 */ mrb_define_method_id(mrb, krn, MRB_SYM(clone), mrb_obj_clone, MRB_ARGS_NONE()); /* 15.3.1.3.8 */ mrb_define_method_id(mrb, krn, MRB_SYM(dup), mrb_obj_dup, MRB_ARGS_NONE()); /* 15.3.1.3.9 */ mrb_define_method_id(mrb, krn, MRB_SYM_Q(eql), mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.10 */ mrb_define_method_id(mrb, krn, MRB_SYM(freeze), mrb_obj_freeze, MRB_ARGS_NONE()); mrb_define_method_id(mrb, krn, MRB_SYM_Q(frozen), mrb_obj_frozen, MRB_ARGS_NONE()); mrb_define_method_id(mrb, krn, MRB_SYM(extend), mrb_obj_extend, MRB_ARGS_ANY()); /* 15.3.1.3.13 */ mrb_define_method_id(mrb, krn, MRB_SYM(hash), mrb_obj_hash, MRB_ARGS_NONE()); /* 15.3.1.3.15 */ mrb_define_private_method_id(mrb, krn, MRB_SYM(initialize_copy), mrb_obj_init_copy, MRB_ARGS_REQ(1)); /* 15.3.1.3.16 */ mrb_define_method_id(mrb, krn, MRB_SYM(inspect), mrb_obj_inspect, MRB_ARGS_NONE()); /* 15.3.1.3.17 */ mrb_define_method_id(mrb, krn, MRB_SYM_Q(instance_of), obj_is_instance_of, MRB_ARGS_REQ(1)); /* 15.3.1.3.19 */ mrb_define_method_id(mrb, krn, MRB_SYM_Q(is_a), mrb_obj_is_kind_of_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.24 */ mrb_define_private_method_id(mrb, krn, MRB_SYM_Q(iterator), mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.3.25 */ mrb_define_method_id(mrb, krn, MRB_SYM_Q(kind_of), mrb_obj_is_kind_of_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.26 */ mrb_define_method_id(mrb, krn, MRB_SYM_Q(nil), mrb_false, MRB_ARGS_NONE()); /* 15.3.1.3.32 */ mrb_define_method_id(mrb, krn, MRB_SYM(object_id), mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.33 */ mrb_define_private_method_id(mrb, krn, MRB_SYM(p), mrb_p_m, MRB_ARGS_ANY()); /* 15.3.1.3.34 */ #ifndef HAVE_MRUBY_IO_GEM mrb_define_private_method_id(mrb, krn, MRB_SYM(print), mrb_print_m, MRB_ARGS_ANY()); /* 15.3.1.3.35 */ #endif mrb_define_private_method_id(mrb, krn, MRB_SYM(raise), mrb_f_raise, MRB_ARGS_OPT(2)); /* 15.3.1.3.40 */ mrb_define_method_id(mrb, krn, MRB_SYM(remove_instance_variable), mrb_obj_remove_instance_variable,MRB_ARGS_REQ(1)); /* 15.3.1.3.41 */ mrb_define_method_id(mrb, krn, MRB_SYM_Q(respond_to), obj_respond_to, MRB_ARGS_ARG(1,1)); /* 15.3.1.3.43 */ mrb_define_method_id(mrb, krn, MRB_SYM(to_s), mrb_any_to_s, MRB_ARGS_NONE()); /* 15.3.1.3.46 */ mrb_define_method_id(mrb, krn, MRB_SYM(__case_eqq), mrb_obj_ceqq, MRB_ARGS_REQ(1)); /* internal */ mrb_define_method_id(mrb, krn, MRB_SYM(__to_int), mrb_ensure_int_type, MRB_ARGS_NONE()); /* internal */ mrb_define_private_method_id(mrb, krn, MRB_SYM_Q(respond_to_missing), mrb_false, MRB_ARGS_ARG(1,1)); mrb_define_method_id(mrb, krn, MRB_SYM_Q(__inspect_recursive), mrb_obj_inspect_recursive_p, MRB_ARGS_NONE()); mrb_include_module(mrb, mrb->object_class, mrb->kernel_module); } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/numops.c0000644000000000000000000000013215077107276020147 xustar0030 mtime=1761382078.135420497 30 atime=1761382080.158411242 30 ctime=1761382108.576301896 nghttp2-1.68.0/third-party/mruby/src/numops.c0000644000175100017510000000475015077107276020545 0ustar00runnerrunner/* ** numeric.c - Numeric, Integer, Float class ** ** See Copyright Notice in mruby.h */ #include #include #include #include MRB_API mrb_value mrb_num_add(mrb_state *mrb, mrb_value x, mrb_value y) { #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { return mrb_bint_add(mrb, x, y); } #endif if (mrb_integer_p(x)) { return mrb_int_add(mrb, x, y); } #ifndef MRB_NO_FLOAT if (mrb_float_p(x)) { return mrb_float_value(mrb, mrb_float(x) + mrb_as_float(mrb, y)); } #endif #if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX) switch (mrb_type(x)) { #if defined(MRB_USE_RATIONAL) case MRB_TT_RATIONAL: return mrb_rational_add(mrb, x, y); #endif #if defined(MRB_USE_COMPLEX) case MRB_TT_COMPLEX: return mrb_complex_add(mrb, x, y); #endif default: break; } #endif mrb_raise(mrb, E_TYPE_ERROR, "no number addition"); return mrb_nil_value(); /* not reached */ } MRB_API mrb_value mrb_num_sub(mrb_state *mrb, mrb_value x, mrb_value y) { #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { return mrb_bint_sub(mrb, x, y); } #endif if (mrb_integer_p(x)) { return mrb_int_sub(mrb, x, y); } #ifndef MRB_NO_FLOAT if (mrb_float_p(x)) { return mrb_float_value(mrb, mrb_float(x) - mrb_as_float(mrb, y)); } #endif #if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX) switch (mrb_type(x)) { #if defined(MRB_USE_RATIONAL) case MRB_TT_RATIONAL: return mrb_rational_sub(mrb, x, y); #endif #if defined(MRB_USE_COMPLEX) case MRB_TT_COMPLEX: return mrb_complex_sub(mrb, x, y); #endif default: break; } #endif mrb_raise(mrb, E_TYPE_ERROR, "no number subtraction"); return mrb_nil_value(); /* not reached */ } MRB_API mrb_value mrb_num_mul(mrb_state *mrb, mrb_value x, mrb_value y) { #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { return mrb_bint_mul(mrb, x, y); } #endif if (mrb_integer_p(x)) { return mrb_int_mul(mrb, x, y); } #ifndef MRB_NO_FLOAT if (mrb_float_p(x)) { return mrb_float_value(mrb, mrb_float(x) * mrb_as_float(mrb, y)); } #endif #if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX) switch (mrb_type(x)) { #if defined(MRB_USE_RATIONAL) case MRB_TT_RATIONAL: return mrb_rational_mul(mrb, x, y); #endif #if defined(MRB_USE_COMPLEX) case MRB_TT_COMPLEX: return mrb_complex_mul(mrb, x, y); #endif default: break; } #endif mrb_raise(mrb, E_TYPE_ERROR, "no number multiply"); return mrb_nil_value(); /* not reached */ } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/fmt_fp.c0000644000000000000000000000013215077107276020101 xustar0030 mtime=1761382078.134420501 30 atime=1761382080.154411261 30 ctime=1761382108.581301881 nghttp2-1.68.0/third-party/mruby/src/fmt_fp.c0000644000175100017510000002065015077107276020474 0ustar00runnerrunner#include #include #ifndef MRB_NO_FLOAT /*********************************************************************** Routine for converting a single-precision floating-point number into a string. The code in this function was inspired from Fred Bayer's pdouble.c. Since pdouble.c was released as Public Domain, I'm releasing this code as public domain as well. Dave Hylands The original code can be found in https://github.com/dhylands/format-float ***********************************************************************/ /*********************************************************************** I modified the routine for mruby: * support `double` * support `#` (alt_form) modifier My modifications in this file are also placed in the public domain. Matz (Yukihiro Matsumoto) ***********************************************************************/ #include #ifdef MRB_USE_FLOAT32 // 1 sign bit, 8 exponent bits, and 23 mantissa bits. // exponent values 0 and 255 are reserved, exponent can be 1 to 254. // exponent is stored with a bias of 127. // The min and max floats are on the order of 1x10^37 and 1x10^-37 #define FLT_DECEXP 32 #define FLT_ROUND_TO_ONE 0.9999995F #define FLT_MIN_BUF_SIZE 6 // -9e+99 #else // 1 sign bit, 11 exponent bits, and 52 mantissa bits. #define FLT_DECEXP 256 #define FLT_ROUND_TO_ONE 0.999999999995 #define FLT_MIN_BUF_SIZE 7 // -9e+199 #endif /* MRB_USE_FLOAT32 */ static const mrb_float g_pos_pow[] = { #ifndef MRB_USE_FLOAT32 1e256, 1e128, 1e64, #endif 1e32, 1e16, 1e8, 1e4, 1e2, 1e1 }; static const mrb_float g_neg_pow[] = { #ifndef MRB_USE_FLOAT32 1e-256, 1e-128, 1e-64, #endif 1e-32, 1e-16, 1e-8, 1e-4, 1e-2, 1e-1 }; /* * mrb_format_float(mrb_float f, char *buf, size_t buf_size, char fmt, int prec, char sign) * * fmt: should be one of 'e', 'E', 'f', 'F', 'g', or 'G'. (|0x80 for '#') * prec: is the precision (as specified in printf) * sign: should be '\0', '+', or ' ' ('\0' is the normal one - only print * a sign if ```f``` is negative. Anything else is printed as the * sign character for positive numbers. */ int mrb_format_float(mrb_float f, char *buf, size_t buf_size, char fmt, int prec, char sign) { char *s = buf; int buf_remaining = (int)buf_size - 1; int alt_form = 0; if ((uint8_t)fmt & 0x80) { fmt &= 0x7f; /* turn off alt_form flag */ alt_form = 1; } if (buf_size <= FLT_MIN_BUF_SIZE) { // Smallest exp notion is -9e+99 (-9e+199) which is 6 (7) chars plus terminating // null. if (buf_size >= 2) { *s++ = '?'; } if (buf_size >= 1) { *s++ = '\0'; } return buf_size >= 2; } if (signbit(f)) { *s++ = '-'; f = -f; } else if (sign) { *s++ = sign; } buf_remaining -= (int)(s - buf); // Adjust for sign { char uc = fmt & 0x20; if (isinf(f)) { *s++ = 'I' ^ uc; *s++ = 'N' ^ uc; *s++ = 'F' ^ uc; goto ret; } else if (isnan(f)) { *s++ = 'N' ^ uc; *s++ = 'A' ^ uc; *s++ = 'N' ^ uc; ret: *s = '\0'; return (int)(s - buf); } } if (prec < 0) { prec = 6; } char e_char = 'E' | (fmt & 0x20); // e_char will match case of fmt fmt |= 0x20; // Force fmt to be lowercase char org_fmt = fmt; if (fmt == 'g' && prec == 0) { prec = 1; } int e, e1; int dec = 0; char e_sign = '\0'; int num_digits = 0; const mrb_float *pos_pow = g_pos_pow; const mrb_float *neg_pow = g_neg_pow; if (f == 0.0) { e = 0; if (fmt == 'e') { e_sign = '+'; } else if (fmt == 'f') { num_digits = prec + 1; } } else if (f < 1.0) { // f < 1.0 char first_dig = '0'; if (f >= FLT_ROUND_TO_ONE) { first_dig = '1'; } // Build negative exponent for (e = 0, e1 = FLT_DECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) { if (*neg_pow > f) { e += e1; f *= *pos_pow; } } char e_sign_char = '-'; if (f < 1.0) { if (f >= FLT_ROUND_TO_ONE) { f = 1.0; if (e == 0) { e_sign_char = '+'; } } else { e++; f *= 10.0; } } // If the user specified 'g' format, and e is <= 4, then we'll switch // to the fixed format ('f') if (fmt == 'f' || (fmt == 'g' && e <= 4)) { fmt = 'f'; dec = -1; *s++ = first_dig; if (org_fmt == 'g') { prec += (e - 1); } // truncate precision to prevent buffer overflow if (prec + 2 > buf_remaining) { prec = buf_remaining - 2; } num_digits = prec; if (num_digits || alt_form) { *s++ = '.'; while (--e && num_digits) { *s++ = '0'; num_digits--; } } } else { // For e & g formats, we'll be printing the exponent, so set the // sign. e_sign = e_sign_char; dec = 0; if (prec > (buf_remaining - FLT_MIN_BUF_SIZE)) { prec = buf_remaining - FLT_MIN_BUF_SIZE; if (fmt == 'g') { prec++; } } } } else { // Build positive exponent for (e = 0, e1 = FLT_DECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) { if (*pos_pow <= f) { e += e1; f *= *neg_pow; } } // If the user specified fixed format (fmt == 'f') and e makes the // number too big to fit into the available buffer, then we'll // switch to the 'e' format. if (fmt == 'f') { if (e >= buf_remaining) { fmt = 'e'; } else if ((e + prec + 2) > buf_remaining) { prec = buf_remaining - e - 2; if (prec < 0) { // This means no decimal point, so we can add one back // for the decimal. prec++; } } } if (fmt == 'e' && prec > (buf_remaining - 6)) { prec = buf_remaining - 6; } // If the user specified 'g' format, and e is < prec, then we'll switch // to the fixed format. if (fmt == 'g' && e < prec) { fmt = 'f'; prec -= (e + 1); } if (fmt == 'f') { dec = e; num_digits = prec + e + 1; } else { e_sign = '+'; } } if (prec < 0) { // This can happen when the prec is trimmed to prevent buffer overflow prec = 0; } // We now have f as a floating-point number between >= 1 and < 10 // (or equal to zero), and e contains the absolute value of the power of // 10 exponent, and (dec + 1) == the number of digits before the decimal. // For e, prec is # digits after the decimal // For f, prec is # digits after the decimal // For g, prec is the max number of significant digits // // For e & g there will be a single digit before the decimal // for f there will be e digits before the decimal if (fmt == 'e') { num_digits = prec + 1; if (prec == 0) prec = 1; } else if (fmt == 'g') { num_digits = prec; } // Print the digits of the mantissa for (int i = 0; i < num_digits; i++,dec--) { int8_t d = (int8_t)((int)f)%10; *s++ = '0' + d; if (dec == 0 && (prec > 0 || alt_form)) { *s++ = '.'; } f -= (mrb_float)d; f *= 10.0; } // Round if (f >= 5.0) { char *rs = s; rs--; while (1) { if (*rs == '.') { rs--; continue; } if (*rs < '0' || *rs > '9') { // + or - rs++; // So we sit on the digit to the right of the sign break; } if (*rs < '9') { (*rs)++; break; } *rs = '0'; if (rs == buf) { break; } rs--; } if (*rs == '0') { // We need to insert a 1 if (fmt != 'f' && rs[1] == '.') { // We're going to round 9.99 to 10.00 // Move the decimal point rs[0] = '.'; rs[1] = '0'; if (e_sign == '-') { e--; } else { e++; } } s++; char *ss = s; while (ss > rs) { *ss = ss[-1]; ss--; } *rs = '1'; if (f < 1.0 && fmt == 'f') { // We rounded up to 1.0 prec--; } } } if (org_fmt == 'g' && prec > 0 && !alt_form) { // Remove trailing zeros and a trailing decimal point while (s[-1] == '0') { s--; } if (s[-1] == '.') { s--; } } // Append the exponent if (e_sign) { *s++ = e_char; *s++ = e_sign; if (e >= 100) { *s++ = '0' + (e / 100); e %= 100; } *s++ = '0' + (e / 10); *s++ = '0' + (e % 10); } *s = '\0'; return (int)(s - buf); } #endif nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/readnum.c0000644000000000000000000000013215077107276020261 xustar0030 mtime=1761382078.136420492 30 atime=1761382080.158411242 30 ctime=1761382108.570301913 nghttp2-1.68.0/third-party/mruby/src/readnum.c0000644000175100017510000000204215077107276020647 0ustar00runnerrunner/* this file defines obsolete functions: mrb_int_read() and mrb_float_read() */ /* use mrb_read_int() and mrb_read_float() instead */ #include #include #include /* mrb_int_read(): read mrb_int from a string (base 10 only) */ /* const char *p - string to read */ /* const char *e - end of string */ /* char **endp - end of parsed integer */ /* if integer overflows, errno will be set to ERANGE */ /* also endp will be set to NULL on overflow */ MRB_API mrb_int mrb_int_read(const char *p, const char *e, char **endp) { mrb_int n; if (!mrb_read_int(p, e, endp, &n)) { if (endp) *endp = NULL; errno = ERANGE; return MRB_INT_MAX; } if (endp) *endp = (char*)p; return n; } #ifndef MRB_NO_FLOAT //#include //#include MRB_API double mrb_float_read(const char *str, char **endp) { double d; if (!mrb_read_float(str, endp, &d)) { errno = ERANGE; } return d; } #endif nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/value_array.h0000644000000000000000000000013215077107276021145 xustar0030 mtime=1761382078.136420492 30 atime=1761382080.160411233 30 ctime=1761382108.596301838 nghttp2-1.68.0/third-party/mruby/src/value_array.h0000644000175100017510000000067015077107276021540 0ustar00runnerrunner#ifndef MRB_VALUE_ARRAY_H__ #define MRB_VALUE_ARRAY_H__ #include static inline void value_move(mrb_value *s1, const mrb_value *s2, size_t n) { if (n == 0) return; if (s1 > s2 && s1 < s2 + n) { s1 += n; s2 += n; while (n-- > 0) { *--s1 = *--s2; } } else if (s1 != s2) { while (n-- > 0) { *s1++ = *s2++; } } else { /* nothing to do. */ } } #endif /* MRB_VALUE_ARRAY_H__ */ nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/backtrace.c0000644000000000000000000000013215077107276020545 xustar0030 mtime=1761382078.132420511 30 atime=1761382080.151411274 30 ctime=1761382108.580301884 nghttp2-1.68.0/third-party/mruby/src/backtrace.c0000644000175100017510000001526115077107276021142 0ustar00runnerrunner/* ** backtrace.c - ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include #include #include static void copy_backtrace(mrb_state *mrb, const struct mrb_backtrace_location *loc, struct mrb_backtrace_location *ptr, size_t n) { ptr[n] = *loc; if (loc->irep) { if (loc->irep->refcnt == UINT16_MAX) { ptr[n].irep = NULL; } else { mrb_irep_incref(mrb, (mrb_irep*)loc->irep); } } } static size_t pack_backtrace(mrb_state *mrb, ptrdiff_t ciidx, struct mrb_backtrace_location *ptr) { size_t n = 0; for (ptrdiff_t i=ciidx; i >= 0; i--) { struct mrb_backtrace_location loc; mrb_callinfo *ci; const mrb_code *pc; ci = &mrb->c->cibase[i]; loc.method_id = ci->mid; if (ci->proc && !MRB_PROC_CFUNC_P(ci->proc)) { mrb_assert(!MRB_PROC_ALIAS_P(ci->proc)); loc.irep = ci->proc->body.irep; if (!loc.irep) continue; if (!loc.irep->debug_info) continue; if (!ci->pc) continue; pc = &ci->pc[-1]; loc.idx = (uint32_t)(pc - loc.irep->iseq); } else { if (!loc.method_id) continue; loc.irep = NULL; for (ptrdiff_t j=i-1; j >= 0; j--) { ci = &mrb->c->cibase[j]; if (!ci->proc) continue; if (MRB_PROC_CFUNC_P(ci->proc)) continue; mrb_assert(!MRB_PROC_ALIAS_P(ci->proc)); const mrb_irep *irep = ci->proc->body.irep; if (!irep) continue; if (!irep->debug_info) continue; if (!ci->pc) continue; pc = &ci->pc[-1]; loc.irep = irep; loc.idx = (uint32_t)(pc - irep->iseq); break; } } copy_backtrace(mrb, &loc, ptr, n); n++; } return n; } static struct RBasic* packed_backtrace(mrb_state *mrb) { ptrdiff_t ciidx = mrb->c->ci - mrb->c->cibase; if (ciidx >= mrb->c->ciend - mrb->c->cibase) ciidx = mrb->c->ciend - mrb->c->cibase; /* ciidx is broken... */ ptrdiff_t len = ciidx + 1; struct RBacktrace *backtrace = MRB_OBJ_ALLOC(mrb, MRB_TT_BACKTRACE, NULL); void *ptr = mrb_malloc(mrb, len * sizeof(struct mrb_backtrace_location)); backtrace->locations = (struct mrb_backtrace_location*)ptr; backtrace->len = pack_backtrace(mrb, ciidx, backtrace->locations); return (struct RBasic*)backtrace; } static void store_backtrace(mrb_state *mrb, mrb_value exc, struct RBasic *backtrace) { struct RException *e = mrb_exc_ptr(exc); e->backtrace = backtrace; mrb_field_write_barrier(mrb, (struct RBasic*)e, backtrace); } void mrb_keep_backtrace(mrb_state *mrb, mrb_value exc) { int ai; if (mrb->c->ci == NULL) return; if (mrb_exc_ptr(exc)->backtrace) return; ai = mrb_gc_arena_save(mrb); struct RBasic *backtrace = packed_backtrace(mrb); store_backtrace(mrb, exc, backtrace); mrb_gc_arena_restore(mrb, ai); } static mrb_value decode_location(mrb_state *mrb, const struct mrb_backtrace_location *entry) { mrb_value btline; int32_t lineno; const char *filename; if (!entry->irep || !mrb_debug_get_position(mrb, entry->irep, entry->idx, &lineno, &filename)) { btline = mrb_str_new_lit(mrb, "(unknown):0"); } else if (lineno != -1) {//debug info was available btline = mrb_format(mrb, "%s:%d", filename, (int)lineno); } else { //all that was left was the stack frame btline = mrb_format(mrb, "%s:0", filename); } if (entry->method_id != 0) { mrb_str_cat_lit(mrb, btline, ":in "); mrb_str_cat_cstr(mrb, btline, mrb_sym_name(mrb, entry->method_id)); } return btline; } static struct RBasic* mrb_unpack_backtrace(mrb_state *mrb, struct RBasic *backtrace) { if (backtrace == NULL) { return mrb_basic_ptr(mrb_ary_new_capa(mrb, 0)); } if (backtrace->tt == MRB_TT_ARRAY) return backtrace; mrb_assert(backtrace->tt == MRB_TT_BACKTRACE); struct RBacktrace *bt = (struct RBacktrace*)backtrace; mrb_int n = (mrb_int)bt->len; const struct mrb_backtrace_location *loc = bt->locations; backtrace = mrb_basic_ptr(mrb_ary_new_capa(mrb, n)); int ai = mrb_gc_arena_save(mrb); for (mrb_int i = 0; i < n; i++) { mrb_value btline = decode_location(mrb, &loc[i]); mrb_ary_push(mrb, mrb_obj_value(backtrace), btline); mrb_gc_arena_restore(mrb, ai); } return backtrace; } mrb_value mrb_exc_backtrace(mrb_state *mrb, mrb_value exc) { struct RBasic *backtrace = mrb_exc_ptr(exc)->backtrace; if (backtrace == NULL) { return mrb_nil_value(); } if (backtrace->tt == MRB_TT_ARRAY) { return mrb_obj_value(backtrace); } /* unpack packed-backtrace */ backtrace = mrb_unpack_backtrace(mrb, backtrace); store_backtrace(mrb, exc, backtrace); return mrb_obj_value(backtrace); } mrb_value mrb_get_backtrace(mrb_state *mrb) { return mrb_obj_value(mrb_unpack_backtrace(mrb, packed_backtrace(mrb))); } #ifndef MRB_NO_STDIO static void print_backtrace(mrb_state *mrb, struct RObject *exc, struct RBasic *ptr) { struct RArray *ary = NULL; struct RBacktrace *bt = NULL; mrb_int n = 0; if (ptr) { if (ptr->tt == MRB_TT_ARRAY) { ary = (struct RArray*)ptr; n = ARY_LEN(ary); } else { bt = (struct RBacktrace*)ptr; n = (mrb_int)bt->len; } } if (n != 0) { mrb_value btline; fputs("trace (most recent call last):\n", stderr); for (mrb_int i=n-1; i>0; i--) { if (ary) btline = ARY_PTR(ary)[i]; else btline = decode_location(mrb, &bt->locations[i]); if (mrb_string_p(btline)) { fprintf(stderr, "\t[%d] ", (int)i); fwrite(RSTRING_PTR(btline), (int)RSTRING_LEN(btline), 1, stderr); fputc('\n', stderr); } } if (ary) btline = ARY_PTR(ary)[0]; else btline = decode_location(mrb, &bt->locations[0]); if (mrb_string_p(btline)) { fwrite(RSTRING_PTR(btline), (int)RSTRING_LEN(btline), 1, stderr); fputs(": ", stderr); } } else { fputs("(unknown):0: ", stderr); } if (exc == mrb->nomem_err) { static const char nomem[] = "Out of memory (NoMemoryError)\n"; fwrite(nomem, sizeof(nomem)-1, 1, stderr); } else { mrb_value output = mrb_exc_get_output(mrb, exc); fwrite(RSTRING_PTR(output), RSTRING_LEN(output), 1, stderr); fputc('\n', stderr); } } /* mrb_print_backtrace function to retrieve backtrace information from the last exception. */ MRB_API void mrb_print_backtrace(mrb_state *mrb) { if (!mrb->exc || mrb->exc->tt != MRB_TT_EXCEPTION) { return; } print_backtrace(mrb, mrb->exc, ((struct RException*)mrb->exc)->backtrace); } #else MRB_API void mrb_print_backtrace(mrb_state *mrb) { } #endif nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/numeric.c0000644000000000000000000000013215077107276020270 xustar0030 mtime=1761382078.135420497 30 atime=1761382080.157411247 30 ctime=1761382108.568301919 nghttp2-1.68.0/third-party/mruby/src/numeric.c0000644000175100017510000015230415077107276020665 0ustar00runnerrunner/* ** numeric.c - Numeric, Integer, Float class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #ifndef MRB_NO_FLOAT #ifdef MRB_USE_FLOAT32 #define trunc(f) truncf(f) #define fmod(x,y) fmodf(x,y) #else #endif #endif mrb_noreturn void mrb_int_overflow(mrb_state *mrb, const char *reason) { mrb_raisef(mrb, E_RANGE_ERROR, "integer overflow in %s", reason); } mrb_noreturn void mrb_int_zerodiv(mrb_state *mrb) { mrb_raise(mrb, E_ZERODIV_ERROR, "divided by 0"); } static mrb_noreturn void mrb_int_noconv(mrb_state *mrb, mrb_value y) { mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %Y into Integer", y); } mrb_value mrb_int_pow(mrb_state *mrb, mrb_value x, mrb_value y) { #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { #ifndef MRB_NO_FLOAT if (mrb_float_p(y)) { return mrb_float_value(mrb, pow(mrb_bint_as_float(mrb, x), mrb_float(y))); } #endif return mrb_bint_pow(mrb, x, y); } #endif mrb_int base = mrb_integer(x); mrb_int result = 1; mrb_int exp; #ifndef MRB_NO_FLOAT if (mrb_float_p(y)) { return mrb_float_value(mrb, pow((double)base, mrb_float(y))); } else if (mrb_integer_p(y)) { exp = mrb_integer(y); } else #endif { exp = mrb_as_int(mrb, y); } if (exp < 0) { #ifndef MRB_NO_FLOAT return mrb_float_value(mrb, pow((double)base, (double)exp)); #else mrb_int_overflow(mrb, "negative power"); #endif } for (;;) { if (exp & 1) { if (mrb_int_mul_overflow(result, base, &result)) { #ifdef MRB_USE_BIGINT return mrb_bint_pow(mrb, mrb_bint_new_int(mrb, mrb_integer(x)), y); #else mrb_int_overflow(mrb, "power"); #endif } } exp >>= 1; if (exp == 0) break; if (mrb_int_mul_overflow(base, base, &base)) { #ifdef MRB_USE_BIGINT return mrb_bint_pow(mrb, mrb_bint_new_int(mrb, mrb_integer(x)), y); #else mrb_int_overflow(mrb, "power"); #endif } } return mrb_int_value(mrb, result); } /* * call-seq: * * num ** other -> num * * Raises num the other power. * * 2.0**3 #=> 8.0 */ static mrb_value int_pow(mrb_state *mrb, mrb_value x) { return mrb_int_pow(mrb, x, mrb_get_arg1(mrb)); } mrb_int mrb_div_int(mrb_int x, mrb_int y) { mrb_int div = x / y; if ((x ^ y) < 0 && x != div * y) { div -= 1; } return div; } mrb_value mrb_div_int_value(mrb_state *mrb, mrb_int x, mrb_int y) { if (y == 0) { mrb_int_zerodiv(mrb); } else if (x == MRB_INT_MIN && y == -1) { #ifdef MRB_USE_BIGINT return mrb_bint_mul_ii(mrb, x, y); #else mrb_int_overflow(mrb, "division"); #endif } return mrb_int_value(mrb, mrb_div_int(x, y)); } /* 15.2.8.3.6 */ /* * call-seq: * int / num -> num * * Performs division: the class of the resulting object depends on * the class of num and on the magnitude of the * result. */ static mrb_value int_div(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { if (mrb_bigint_p(y) || mrb_integer_p(y)) { return mrb_bint_div(mrb, x, y); } } else #endif if (mrb_integer_p(y)) { return mrb_div_int_value(mrb, mrb_integer(x), mrb_integer(y)); } switch (mrb_type(y)) { #ifdef MRB_USE_BIGINT case MRB_TT_INTEGER: case MRB_TT_BIGINT: return mrb_bint_div(mrb, mrb_as_bint(mrb, x), y); #endif #ifdef MRB_USE_RATIONAL case MRB_TT_RATIONAL: return mrb_rational_div(mrb, mrb_as_rational(mrb, x), y); #endif #ifdef MRB_USE_COMPLEX case MRB_TT_COMPLEX: x = mrb_complex_new(mrb, mrb_as_float(mrb, x), 0); return mrb_complex_div(mrb, x, y); #endif #ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: return mrb_float_value(mrb, mrb_div_float(mrb_as_float(mrb, x), mrb_as_float(mrb, y))); #endif default: mrb_int_noconv(mrb, y); } } /* 15.2.9.3.19(x) */ /* * call-seq: * num.quo(numeric) -> real * * Returns most exact division. */ /* * call-seq: * int.div(other) -> int * * Performs division: resulting integer. */ static mrb_value int_idiv(mrb_state *mrb, mrb_value x) { #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { return mrb_bint_div(mrb, x, mrb_get_arg1(mrb)); } #endif mrb_int y = mrb_as_int(mrb, mrb_get_arg1(mrb)); return mrb_div_int_value(mrb, mrb_integer(x), y); } #ifndef MRB_NO_FLOAT static mrb_value int_fdiv(mrb_state *mrb, mrb_value x) { mrb_float y = mrb_as_float(mrb, mrb_get_arg1(mrb)); if (y == 0) { mrb_int_zerodiv(mrb); } #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { return mrb_float_value(mrb, mrb_bint_as_float(mrb, x) / y); } #endif return mrb_float_value(mrb, mrb_integer(x) / y); } #endif static mrb_value int_quo(mrb_state *mrb, mrb_value x) { #ifndef MRB_USE_RATIONAL #ifdef MRB_NO_FLOAT return int_idiv(mrb, x); #else return int_fdiv(mrb, x); #endif #else mrb_int a = mrb_integer(x); mrb_value y = mrb_get_arg1(mrb); if (mrb_integer_p(y) && mrb_class_defined_id(mrb, MRB_SYM(Rational))) { return mrb_rational_new(mrb, a, mrb_integer(y)); } switch (mrb_type(y)) { case MRB_TT_RATIONAL: x = mrb_rational_new(mrb, a, 1); return mrb_rational_div(mrb, x, y); default: #ifndef MRB_NO_FLOAT return mrb_float_value(mrb, mrb_div_float((mrb_float)a, mrb_as_float(mrb, y))); #else mrb_int_noconv(mrb, y); break; #endif } #endif } static mrb_value coerce_step_counter(mrb_state *mrb, mrb_value self) { mrb->c->ci->mid = 0; #ifndef MRB_NO_FLOAT mrb_value step = mrb_get_arg1(mrb); if (mrb_float_p(step)) { return mrb_ensure_float_type(mrb, self); } #endif return self; } #ifndef MRB_NO_FLOAT /******************************************************************** * * Document-class: Float * * Float objects represent inexact real numbers using * the native architecture's double-precision floating-point * representation. */ static mrb_value flo_pow(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); mrb_float d = pow(mrb_as_float(mrb, x), mrb_as_float(mrb, y)); return mrb_float_value(mrb, d); } static mrb_value flo_idiv(mrb_state *mrb, mrb_value xv) { mrb_float x = mrb_float(xv); mrb_check_num_exact(mrb, x); mrb_int y = mrb_as_int(mrb, mrb_get_arg1(mrb)); return mrb_div_int_value(mrb, (mrb_int)x, y); } mrb_float mrb_div_float(mrb_float x, mrb_float y) { if (y != 0.0) { return x / y; } else if (x == 0.0) { return NAN; } else { return x * (signbit(y) ? -1.0 : 1.0) * INFINITY; } } /* 15.2.9.3.6 */ /* * call-seq: * float / num -> float * * Returns a new Float which is the result of dividing float by num. */ static mrb_value flo_div(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); mrb_float a = mrb_float(x); switch(mrb_type(y)) { #ifdef MRB_USE_COMPLEX case MRB_TT_COMPLEX: return mrb_complex_div(mrb, mrb_complex_new(mrb, a, 0), y); #endif case MRB_TT_FLOAT: a = mrb_div_float(a, mrb_float(y)); return mrb_float_value(mrb, a); default: a = mrb_div_float(a, mrb_as_float(mrb, y)); return mrb_float_value(mrb, a); } return mrb_float_value(mrb, a); } static mrb_value num_fdiv(mrb_state *mrb, mrb_value x) { return flo_div(mrb, mrb_ensure_float_type(mrb, x)); } /* the argument `fmt` is no longer used; you can pass `NULL` */ mrb_value mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt) { char buf[25]; #ifdef MRB_USE_FLOAT32 const int prec = 7; #else const int prec = 15; #endif mrb_format_float(mrb_float(flo), buf, sizeof(buf), 'g', prec, '\0'); for (char *p = buf; *p; p++) { if (*p == '.') goto exit; if (*p == 'e') { memmove(p+2, p, strlen(p)+1); p[0] = '.'; p[1] = '0'; goto exit; } } strcat(buf, ".0"); exit: return mrb_str_new_cstr(mrb, buf); } /* 15.2.9.3.16(x) */ /* * call-seq: * flt.to_s -> string * flt.inspect -> string * * Returns a string containing a representation of self. As well as a * fixed or exponential form of the number, the call may return * "NaN", "Infinity", and * "-Infinity". * * 3.0.to_s #=> 3.0 * 3.25.to_s #=> 3.25 */ static mrb_value flo_to_s(mrb_state *mrb, mrb_value flt) { mrb_float f = mrb_float(flt); mrb_value str; if (isinf(f)) { str = f < 0 ? mrb_str_new_lit(mrb, "-Infinity") : mrb_str_new_lit(mrb, "Infinity"); } else if (isnan(f)) { str = mrb_str_new_lit(mrb, "NaN"); } else { str = mrb_float_to_str(mrb, flt, NULL); } RSTR_SET_ASCII_FLAG(mrb_str_ptr(str)); return str; } /* 15.2.9.3.3 */ /* * call-seq: * float + other -> float * * Returns a new float which is the sum of float * and other. */ static mrb_value flo_add(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); mrb_float a = mrb_float(x); switch (mrb_type(y)) { case MRB_TT_FLOAT: return mrb_float_value(mrb, a + mrb_float(y)); #if defined(MRB_USE_COMPLEX) case MRB_TT_COMPLEX: return mrb_complex_add(mrb, y, x); #endif default: return mrb_float_value(mrb, a + mrb_as_float(mrb, y)); } } /* 15.2.9.3.4 */ /* * call-seq: * float - other -> float * * Returns a new float which is the difference of float * and other. */ static mrb_value flo_sub(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); mrb_float a = mrb_float(x); switch (mrb_type(y)) { case MRB_TT_FLOAT: return mrb_float_value(mrb, a - mrb_float(y)); #if defined(MRB_USE_COMPLEX) case MRB_TT_COMPLEX: return mrb_complex_sub(mrb, mrb_complex_new(mrb, a, 0), y); #endif default: return mrb_float_value(mrb, a - mrb_as_float(mrb, y)); } } /* 15.2.9.3.5 */ /* * call-seq: * float * other -> float * * Returns a new float which is the product of float * and other. */ static mrb_value flo_mul(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); mrb_float a = mrb_float(x); switch (mrb_type(y)) { case MRB_TT_FLOAT: return mrb_float_value(mrb, a * mrb_float(y)); #if defined(MRB_USE_COMPLEX) case MRB_TT_COMPLEX: return mrb_complex_mul(mrb, y, x); #endif default: return mrb_float_value(mrb, a * mrb_as_float(mrb, y)); } } static void flodivmod(mrb_state *mrb, double x, double y, mrb_float *divp, mrb_float *modp) { double div, mod; if (isnan(y)) { /* y is NaN so all results are NaN */ div = mod = y; goto exit; } if (y == 0.0) { mrb_int_zerodiv(mrb); } if (isinf(y) && !isinf(x)) { mod = x; } else { mod = fmod(x, y); } if (isinf(x) && !isinf(y)) { div = x; } else { div = (x - mod) / y; if (modp && divp) div = round(div); } if (div == 0) div = 0.0; if (mod == 0) mod = 0.0; if (y*mod < 0) { mod += y; div -= 1.0; } exit: if (modp) *modp = mod; if (divp) *divp = div; } /* 15.2.9.3.5 */ /* * call-seq: * flt % other -> float * flt.modulo(other) -> float * * Return the modulo after division of flt by other. * * 6543.21.modulo(137) #=> 104.21 * 6543.21.modulo(137.24) #=> 92.9299999999996 */ static mrb_value flo_mod(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); mrb_float mod; flodivmod(mrb, mrb_float(x), mrb_as_float(mrb, y), NULL, &mod); return mrb_float_value(mrb, mod); } #endif /* 15.2.8.3.16 */ /* * call-seq: * num.eql?(numeric) -> true or false * * Returns true if num and numeric are the * same type and have equal values. * * 1 == 1.0 #=> true * 1.eql?(1.0) #=> false * (1.0).eql?(1.0) #=> true */ static mrb_value num_eql(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { return mrb_bool_value(mrb_bint_cmp(mrb, x, y) == 0); } #endif #ifndef MRB_NO_FLOAT if (mrb_float_p(x)) { if (!mrb_float_p(y)) return mrb_false_value(); return mrb_bool_value(mrb_float(x) == mrb_float(y)); } #endif if (mrb_integer_p(x)) { if (!mrb_integer_p(y)) return mrb_false_value(); return mrb_bool_value(mrb_integer(x) == mrb_integer(y)); } return mrb_bool_value(mrb_equal(mrb, x, y)); } #ifndef MRB_NO_FLOAT /* 15.2.9.3.7 */ /* * call-seq: * flt == obj -> true or false * * Returns true only if obj has the same value * as flt. Contrast this with Float#eql?, which * requires obj to be a Float. * * 1.0 == 1 #=> true * */ static mrb_value flo_eq(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); switch (mrb_type(y)) { case MRB_TT_INTEGER: return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_integer(y)); case MRB_TT_FLOAT: return mrb_bool_value(mrb_float(x) == mrb_float(y)); #ifdef MRB_USE_RATIONAL case MRB_TT_RATIONAL: return mrb_bool_value(mrb_float(x) == mrb_as_float(mrb, y)); #endif #ifdef MRB_USE_COMPLEX case MRB_TT_COMPLEX: return mrb_bool_value(mrb_equal(mrb, y, x)); #endif default: return mrb_false_value(); } } /* 15.2.9.3.13 */ /* * Document-method: Float#to_f * * call-seq: * flt.to_f -> self * * As flt is already a float, returns +self+. */ /* 15.2.9.3.11 */ /* * call-seq: * flt.infinite? -> nil, -1, +1 * * Returns nil, -1, or +1 depending on whether flt * is finite, -infinity, or +infinity. * * (0.0).infinite? #=> nil * (-1.0/0.0).infinite? #=> -1 * (+1.0/0.0).infinite? #=> 1 */ static mrb_value flo_infinite_p(mrb_state *mrb, mrb_value num) { mrb_float value = mrb_float(num); if (isinf(value)) { return mrb_fixnum_value(value < 0 ? -1 : 1); } return mrb_nil_value(); } /* 15.2.9.3.9 */ /* * call-seq: * flt.finite? -> true or false * * Returns true if flt is a valid IEEE floating * point number (it is not infinite, and nan? is * false). * */ static mrb_value flo_finite_p(mrb_state *mrb, mrb_value num) { return mrb_bool_value(isfinite(mrb_float(num))); } /* * Document-class: FloatDomainError * * Raised when attempting to convert special float values * (in particular infinite or NaN) * to numerical classes which don't support them. * * Float::INFINITY.to_i * * raises the exception: * * FloatDomainError: Infinity */ /* ------------------------------------------------------------------------*/ void mrb_check_num_exact(mrb_state *mrb, mrb_float num) { if (isinf(num)) { mrb_raise(mrb, E_FLOATDOMAIN_ERROR, num < 0 ? "-Infinity" : "Infinity"); } if (isnan(num)) { mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN"); } } static mrb_value flo_rounding_int(mrb_state *mrb, mrb_float f) { if (!FIXABLE_FLOAT(f)) { #ifdef MRB_USE_BIGINT return mrb_bint_new_float(mrb, f); #else mrb_int_overflow(mrb, "rounding"); #endif } return mrb_int_value(mrb, (mrb_int)f); } static mrb_value flo_rounding(mrb_state *mrb, mrb_value num, double (*func)(double)) { mrb_float f = mrb_float(num); mrb_int ndigits = 0; #ifdef MRB_USE_FLOAT32 const int fprec = 7; #else const int fprec = 15; #endif mrb_get_args(mrb, "|i", &ndigits); if (f == 0.0) { return ndigits > 0 ? mrb_float_value(mrb, f) : mrb_fixnum_value(0); } if (ndigits > 0) { if (ndigits > fprec) return num; mrb_float d = pow(10, (double)ndigits); f = func(f * d) / d; mrb_check_num_exact(mrb, f); return mrb_float_value(mrb, f); } if (ndigits < 0) { mrb_float d = pow(10, -(double)ndigits); f = func(f / d) * d; } else { /* ndigits == 0 */ f = func(f); } mrb_check_num_exact(mrb, f); return flo_rounding_int(mrb, f); } /* 15.2.9.3.10 */ /* * call-seq: * float.floor([ndigits]) -> integer or float * * Returns the largest number less than or equal to +float+ with * a precision of +ndigits+ decimal digits (default: 0). * * When the precision is negative, the returned value is an integer * with at least ndigits.abs trailing zeros. * * Returns a floating-point number when +ndigits+ is positive, * otherwise returns an integer. * * 1.2.floor #=> 1 * 2.0.floor #=> 2 * (-1.2).floor #=> -2 * (-2.0).floor #=> -2 * * 1.234567.floor(2) #=> 1.23 * 1.234567.floor(3) #=> 1.234 * 1.234567.floor(4) #=> 1.2345 * 1.234567.floor(5) #=> 1.23456 * * 34567.89.floor(-5) #=> 0 * 34567.89.floor(-4) #=> 30000 * 34567.89.floor(-3) #=> 34000 * 34567.89.floor(-2) #=> 34500 * 34567.89.floor(-1) #=> 34560 * 34567.89.floor(0) #=> 34567 * 34567.89.floor(1) #=> 34567.8 * 34567.89.floor(2) #=> 34567.89 * 34567.89.floor(3) #=> 34567.89 * * Note that the limited precision of floating-point arithmetic * might lead to surprising results: * * (0.3 / 0.1).floor #=> 2 (!) */ static mrb_value flo_floor(mrb_state *mrb, mrb_value num) { return flo_rounding(mrb, num, floor); } /* 15.2.9.3.8 */ /* * call-seq: * float.ceil([ndigits]) -> integer or float * * Returns the smallest number greater than or equal to +float+ with * a precision of +ndigits+ decimal digits (default: 0). * * When the precision is negative, the returned value is an integer * with at least ndigits.abs trailing zeros. * * Returns a floating-point number when +ndigits+ is positive, * otherwise returns an integer. * * 1.2.ceil #=> 2 * 2.0.ceil #=> 2 * (-1.2).ceil #=> -1 * (-2.0).ceil #=> -2 * * 1.234567.ceil(2) #=> 1.24 * 1.234567.ceil(3) #=> 1.235 * 1.234567.ceil(4) #=> 1.2346 * 1.234567.ceil(5) #=> 1.23457 * * 34567.89.ceil(-5) #=> 100000 * 34567.89.ceil(-4) #=> 40000 * 34567.89.ceil(-3) #=> 35000 * 34567.89.ceil(-2) #=> 34600 * 34567.89.ceil(-1) #=> 34570 * 34567.89.ceil(0) #=> 34568 * 34567.89.ceil(1) #=> 34567.9 * 34567.89.ceil(2) #=> 34567.89 * 34567.89.ceil(3) #=> 34567.89 * * Note that the limited precision of floating-point arithmetic * might lead to surprising results: * * (2.1 / 0.7).ceil #=> 4 (!) */ static mrb_value flo_ceil(mrb_state *mrb, mrb_value num) { return flo_rounding(mrb, num, ceil); } /* 15.2.9.3.12 */ /* * call-seq: * flt.round([ndigits]) -> integer or float * * Rounds flt to a given precision in decimal digits (default 0 digits). * Precision may be negative. Returns a floating-point number when ndigits * is more than zero. * * 1.4.round #=> 1 * 1.5.round #=> 2 * 1.6.round #=> 2 * (-1.5).round #=> -2 * * 1.234567.round(2) #=> 1.23 * 1.234567.round(3) #=> 1.235 * 1.234567.round(4) #=> 1.2346 * 1.234567.round(5) #=> 1.23457 * * 34567.89.round(-5) #=> 0 * 34567.89.round(-4) #=> 30000 * 34567.89.round(-3) #=> 35000 * 34567.89.round(-2) #=> 34600 * 34567.89.round(-1) #=> 34570 * 34567.89.round(0) #=> 34568 * 34567.89.round(1) #=> 34567.9 * 34567.89.round(2) #=> 34567.89 * 34567.89.round(3) #=> 34567.89 * */ static mrb_value flo_round(mrb_state *mrb, mrb_value num) { double number, f; mrb_int ndigits = 0; mrb_int i; mrb_get_args(mrb, "|i", &ndigits); number = mrb_float(num); if (0 < ndigits && (isinf(number) || isnan(number))) { return num; } mrb_check_num_exact(mrb, number); f = 1.0; if (ndigits < -DBL_DIG-2) return mrb_fixnum_value(0); i = ndigits >= 0 ? ndigits : -ndigits; if (ndigits > DBL_DIG+2) return num; while (--i >= 0) f = f*10.0; if (isinf(f)) { if (ndigits < 0) number = 0; } else { double d; if (ndigits < 0) number /= f; else number *= f; /* home-made inline implementation of round(3) */ if (number > 0.0) { d = floor(number); number = d + (number - d >= 0.5); } else if (number < 0.0) { d = ceil(number); number = d - (d - number >= 0.5); } if (ndigits < 0) number *= f; else number /= f; } if (ndigits > 0) { if (!isfinite(number)) return num; return mrb_float_value(mrb, number); } if (!FIXABLE_FLOAT(number)) return mrb_float_value(mrb, number); return mrb_int_value(mrb, (mrb_int)number); } /* 15.2.9.3.14 */ static mrb_value flo_to_i(mrb_state *mrb, mrb_value num) { mrb_float f = mrb_float(num); mrb_check_num_exact(mrb, f); if (!FIXABLE_FLOAT(f)) { #ifdef MRB_USE_BIGINT return mrb_bint_new_float(mrb, f); #else mrb_int_overflow(mrb, "to_f"); #endif } if (f > 0.0) f = floor(f); if (f < 0.0) f = ceil(f); return mrb_int_value(mrb, (mrb_int)f); } /* 15.2.9.3.15 */ /* * call-seq: * flt.to_i -> integer * flt.truncate -> integer * * Returns flt truncated to an Integer. */ static mrb_value flo_truncate(mrb_state *mrb, mrb_value num) { if (signbit(mrb_float(num))) return flo_ceil(mrb, num); return flo_floor(mrb, num); } static mrb_value flo_nan_p(mrb_state *mrb, mrb_value num) { return mrb_bool_value(isnan(mrb_float(num))); } static mrb_value flo_abs(mrb_state *mrb, mrb_value num) { mrb_float f = mrb_float(num); if (signbit(f)) return mrb_float_value(mrb, -f); return num; } #endif /* * Document-class: Integer * * Integer is hold whole numbers. * */ /* 15.2.9.3.24 */ /* * Document-method: Integer#to_i * Document-method: Integer#to_int * * call-seq: * int.to_i -> integer * int.to_int -> integer * * As int is already an Integer, all these * methods simply return the receiver. */ mrb_value mrb_int_mul(mrb_state *mrb, mrb_value x, mrb_value y) { mrb_int a; a = mrb_integer(x); if (mrb_integer_p(y)) { mrb_int b, c; if (a == 0) return x; if (a == 1) return y; b = mrb_integer(y); if (b == 0) return y; if (b == 1) return x; if (mrb_int_mul_overflow(a, b, &c)) { #ifdef MRB_USE_BIGINT x = mrb_bint_new_int(mrb, a); return mrb_bint_mul(mrb, x, y); #else mrb_int_overflow(mrb, "multiplication"); #endif } return mrb_int_value(mrb, c); } switch (mrb_type(y)) { #ifdef MRB_USE_BIGINT case MRB_TT_BIGINT: if (a == 0) return x; if (a == 1) return y; return mrb_bint_mul(mrb, y, x); #endif #ifdef MRB_USE_RATIONAL case MRB_TT_RATIONAL: if (a == 0) return x; if (a == 1) return y; return mrb_rational_mul(mrb, y, x); #endif #ifdef MRB_USE_COMPLEX case MRB_TT_COMPLEX: if (a == 0) return x; if (a == 1) return y; return mrb_complex_mul(mrb, y, x); #endif #ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: return mrb_float_value(mrb, (mrb_float)a * mrb_as_float(mrb, y)); #endif default: mrb_int_noconv(mrb, y); } } /* 15.2.8.3.5 */ /* * call-seq: * int * numeric -> numeric_result * * Performs multiplication: the class of the resulting object depends on * the class of numeric and on the magnitude of the * result. */ static mrb_value int_mul(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { return mrb_bint_mul(mrb, x, y); } #endif return mrb_int_mul(mrb, x, y); } static void intdivmod(mrb_state *mrb, mrb_int x, mrb_int y, mrb_int *divp, mrb_int *modp) { if (y == 0) { mrb_int_zerodiv(mrb); } else if (x == MRB_INT_MIN && y == -1) { mrb_int_overflow(mrb, "division"); } else { mrb_int div = x / y; mrb_int mod = x - div * y; if ((x ^ y) < 0 && x != div * y) { mod += y; div -= 1; } if (divp) *divp = div; if (modp) *modp = mod; } } /* 15.2.8.3.7 */ /* * call-seq: * int % num -> num * * Returns int modulo other. * See numeric.divmod for more information. */ static mrb_value int_mod(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); mrb_int a, b; #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { return mrb_bint_mod(mrb, x, y); } if (mrb_bigint_p(y)) { return mrb_bint_mod(mrb, mrb_as_bint(mrb, x), y); } #endif a = mrb_integer(x); if (a == 0) return x; if (mrb_integer_p(y)) { b = mrb_integer(y); if (b == 0) mrb_int_zerodiv(mrb); if (a == MRB_INT_MIN && b == -1) return mrb_fixnum_value(0); mrb_int mod = a % b; if ((a < 0) != (b < 0) && mod != 0) { mod += b; } return mrb_int_value(mrb, mod); } #ifdef MRB_NO_FLOAT mrb_raise(mrb, E_TYPE_ERROR, "non integer modulo"); #else mrb_float mod; flodivmod(mrb, (mrb_float)a, mrb_as_float(mrb, y), NULL, &mod); return mrb_float_value(mrb, mod); #endif } #ifndef MRB_NO_FLOAT static mrb_value flo_divmod(mrb_state *mrb, mrb_value x); #endif /* * call-seq: * int.divmod(numeric) -> array * * See Numeric#divmod. */ static mrb_value int_divmod(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { #ifndef MRB_NO_FLOAT if (mrb_float_p(y)) { mrb_float f = mrb_bint_as_float(mrb, x); return flo_divmod(mrb, mrb_float_value(mrb, f)); } #endif return mrb_bint_divmod(mrb, x, y); } if (mrb_bigint_p(y)) { return mrb_bint_divmod(mrb, mrb_as_bint(mrb, x), y); } #endif if (mrb_integer_p(y)) { mrb_int div, mod; intdivmod(mrb, mrb_integer(x), mrb_integer(y), &div, &mod); return mrb_assoc_new(mrb, mrb_int_value(mrb, div), mrb_int_value(mrb, mod)); } #ifdef MRB_NO_FLOAT mrb_raise(mrb, E_TYPE_ERROR, "non integer divmod"); #else return flo_divmod(mrb, x); #endif } #ifndef MRB_NO_FLOAT static mrb_value flo_divmod(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); mrb_float div, mod; mrb_value a, b; flodivmod(mrb, mrb_float(x), mrb_as_float(mrb, y), &div, &mod); if (!FIXABLE_FLOAT(div)) a = mrb_float_value(mrb, div); else a = mrb_int_value(mrb, (mrb_int)div); b = mrb_float_value(mrb, mod); return mrb_assoc_new(mrb, a, b); } #endif /* 15.2.8.3.2 */ /* * call-seq: * int == other -> true or false * * Return true if int equals other * numerically. * * 1 == 2 #=> false * 1 == 1.0 #=> true */ static mrb_value int_equal(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); switch (mrb_type(y)) { case MRB_TT_INTEGER: return mrb_bool_value(mrb_integer(x) == mrb_integer(y)); #ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: return mrb_bool_value((mrb_float)mrb_integer(x) == mrb_float(y)); #endif #ifdef MRB_USE_BIGINT case MRB_TT_BIGINT: return mrb_bool_value(mrb_bint_cmp(mrb, y, x) == 0); #endif #ifdef MRB_USE_RATIONAL case MRB_TT_RATIONAL: return mrb_bool_value(mrb_equal(mrb, y, x)); #endif #ifdef MRB_USE_COMPLEX case MRB_TT_COMPLEX: return mrb_bool_value(mrb_equal(mrb, y, x)); #endif default: return mrb_false_value(); } } /* 15.2.8.3.8 */ /* * call-seq: * ~int -> integer * * One's complement: returns a number where each bit is flipped. * ex.0---00001 (1)-> 1---11110 (-2) * ex.0---00010 (2)-> 1---11101 (-3) * ex.0---00100 (4)-> 1---11011 (-5) */ static mrb_value int_rev(mrb_state *mrb, mrb_value num) { #ifdef MRB_USE_BIGINT if (mrb_bigint_p(num)) { return mrb_bint_rev(mrb, num); } #endif mrb_int val = mrb_integer(num); return mrb_int_value(mrb, ~val); } #define bit_op(x,y,op1,op2) do {\ return mrb_int_value(mrb, (mrb_integer(x) op2 mrb_integer(y)));\ } while(0) /* 15.2.8.3.9 */ /* * call-seq: * int & integer -> integer_result * * Bitwise AND. */ static mrb_value int_and(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { return mrb_bint_and(mrb, x, y); } if (mrb_bigint_p(y)) { return mrb_bint_and(mrb, mrb_as_bint(mrb, x), y); } #endif bit_op(x, y, and, &); } /* 15.2.8.3.10 */ /* * call-seq: * int | integer -> integer_result * * Bitwise OR. */ static mrb_value int_or(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { return mrb_bint_or(mrb, x, y); } if (mrb_bigint_p(y)) { return mrb_bint_or(mrb, mrb_as_bint(mrb, x), y); } #endif bit_op(x, y, or, |); } /* 15.2.8.3.11 */ /* * call-seq: * int ^ integer -> integer_result * * Bitwise EXCLUSIVE OR. */ static mrb_value int_xor(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { return mrb_bint_xor(mrb, x, y); } if (mrb_bigint_p(y)) { return mrb_bint_xor(mrb, mrb_as_bint(mrb, x), y); } #endif bit_op(x, y, xor, ^); } #define NUMERIC_SHIFT_WIDTH_MAX (MRB_INT_BIT-1) mrb_bool mrb_num_shift(mrb_state *mrb, mrb_int val, mrb_int width, mrb_int *num) { if (width < 0) { /* rshift */ if (width == MRB_INT_MIN || -width >= NUMERIC_SHIFT_WIDTH_MAX) { if (val < 0) { *num = -1; } else { *num = 0; } } else { *num = val >> -width; } } else if (val > 0) { if ((width > NUMERIC_SHIFT_WIDTH_MAX) || (val > (MRB_INT_MAX >> width))) { return FALSE; } *num = val << width; } else { if ((width > NUMERIC_SHIFT_WIDTH_MAX) || (val < (MRB_INT_MIN >> width))) { return FALSE; } if (width == NUMERIC_SHIFT_WIDTH_MAX) *num = MRB_INT_MIN; else *num = val * ((mrb_int)1 << width); } return TRUE; } /* 15.2.8.3.12 */ /* * call-seq: * int << count -> integer or float * * Shifts _int_ left _count_ positions (right if _count_ is negative). */ static mrb_value int_lshift(mrb_state *mrb, mrb_value x) { mrb_int width, val; width = mrb_as_int(mrb, mrb_get_arg1(mrb)); if (width == 0) { return x; } if (width == MRB_INT_MIN) mrb_int_overflow(mrb, "bit shift"); #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { return mrb_bint_lshift(mrb, x, width); } #endif val = mrb_integer(x); if (val == 0) return x; if (!mrb_num_shift(mrb, val, width, &val)) { #ifdef MRB_USE_BIGINT return mrb_bint_lshift(mrb, mrb_bint_new_int(mrb, val), width); #else mrb_int_overflow(mrb, "bit shift"); #endif } return mrb_int_value(mrb, val); } /* 15.2.8.3.13 */ /* * call-seq: * int >> count -> integer or float * * Shifts _int_ right _count_ positions (left if _count_ is negative). */ static mrb_value int_rshift(mrb_state *mrb, mrb_value x) { mrb_int width, val; width = mrb_as_int(mrb, mrb_get_arg1(mrb)); if (width == 0) { return x; } if (width == MRB_INT_MIN) mrb_int_overflow(mrb, "bit shift"); #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { return mrb_bint_rshift(mrb, x, width); } #endif val = mrb_integer(x); if (val == 0) return x; if (!mrb_num_shift(mrb, val, -width, &val)) { #ifdef MRB_USE_BIGINT return mrb_bint_rshift(mrb, mrb_bint_new_int(mrb, val), width); #else mrb_int_overflow(mrb, "bit shift"); #endif } return mrb_int_value(mrb, val); } static mrb_value prepare_int_rounding(mrb_state *mrb, mrb_value x) { mrb_int nd = 0; size_t bytes; mrb_get_args(mrb, "|i", &nd); if (nd >= 0) { return mrb_nil_value(); } #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { bytes = mrb_bint_memsize(x); } else #endif bytes = sizeof(mrb_int); if (-0.415241 * nd - 0.125 > bytes) { return mrb_undef_value(); } return mrb_int_pow(mrb, mrb_fixnum_value(10), mrb_fixnum_value(-nd)); } /* 15.2.8.3.14 Integer#ceil */ /* * call-seq: * int.ceil -> int * int.ceil(ndigits) -> int * * Returns self. * * When the precision (ndigits) is negative, the returned value is an integer * with at least ndigits.abs trailing zeros. */ static mrb_value int_ceil(mrb_state *mrb, mrb_value x) { mrb_value f = prepare_int_rounding(mrb, x); if (mrb_undef_p(f)) return mrb_fixnum_value(0); if (mrb_nil_p(f)) return x; #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { x = mrb_bint_add_n(mrb, x, f); return mrb_bint_sub(mrb, x, mrb_bint_mod(mrb, x, f)); } #endif mrb_int a = mrb_integer(x); mrb_int b = mrb_integer(f); mrb_int c = a % b; int neg = a < 0; a -= c; if (!neg) { if (mrb_int_add_overflow(a, b, &c)) { #ifdef MRB_USE_BIGINT x = mrb_bint_new_int(mrb, a); return mrb_bint_add(mrb, x, f); #else mrb_int_overflow(mrb, "ceil"); #endif } a = c; } return mrb_int_value(mrb, a); } /* 15.2.8.3.17 Integer#floor */ /* * call-seq: * int.floor -> int * int.floor(ndigits) -> int * * Returns self. * * When the precision (ndigits) is negative, the returned value is an integer * with at least ndigits.abs trailing zeros. */ static mrb_value int_floor(mrb_state *mrb, mrb_value x) { mrb_value f = prepare_int_rounding(mrb, x); if (mrb_undef_p(f)) return mrb_fixnum_value(0); if (mrb_nil_p(f)) return x; #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { return mrb_bint_sub(mrb, x, mrb_bint_mod(mrb, x, f)); } #endif mrb_int a = mrb_integer(x); mrb_int b = mrb_integer(f); mrb_int c = a % b; int neg = a < 0; a -= c; if (neg) { if (mrb_int_sub_overflow(a, b, &c)) { #ifdef MRB_USE_BIGINT x = mrb_bint_new_int(mrb, a); return mrb_bint_sub(mrb, x, f); #else mrb_int_overflow(mrb, "floor"); #endif } a = c; } return mrb_int_value(mrb, a); } /* 15.2.8.3.20 Integer#round */ /* * call-seq: * int.round -> int * int.round(ndigits) -> int * * Returns self. * * When the precision (ndigits) is negative, the returned value is an integer * with at least ndigits.abs trailing zeros. */ static mrb_value int_round(mrb_state *mrb, mrb_value x) { mrb_value f = prepare_int_rounding(mrb, x); if (mrb_undef_p(f)) return mrb_fixnum_value(0); if (mrb_nil_p(f)) return x; #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { mrb_value r = mrb_bint_mod(mrb, x, f); mrb_value n = mrb_bint_sub(mrb, x, r); mrb_value h = mrb_bigint_p(f) ? mrb_bint_rshift(mrb, f, 1) : mrb_int_value(mrb, mrb_integer(f)>>1); mrb_int cmp = mrb_bigint_p(r) ? mrb_bint_cmp(mrb, r, h) : (mrb_bigint_p(h) ? -mrb_bint_cmp(mrb, h, r) : (mrb_integer(r)-mrb_integer(h))); if ((cmp > 0) || (cmp == 0 && mrb_bint_cmp(mrb, x, mrb_fixnum_value(0)) > 0)) { n = mrb_as_bint(mrb, n); n = mrb_bint_add(mrb, n, f); } return n; } #endif mrb_int a = mrb_integer(x); mrb_int b = mrb_integer(f); mrb_int c = a % b; a -= c; if (c < 0) { c = -c; if (b/2 < c) { if (mrb_int_sub_overflow(a, b, &c)) { #ifdef MRB_USE_BIGINT x = mrb_bint_new_int(mrb, a); return mrb_bint_sub(mrb, x, f); #else mrb_int_overflow(mrb, "round"); #endif } } a = c; } else { if (b/2 < c) { if (mrb_int_add_overflow(a, b, &c)) { #ifdef MRB_USE_BIGINT x = mrb_bint_new_int(mrb, a); return mrb_bint_add(mrb, x, f); #else mrb_int_overflow(mrb, "round"); #endif } } a = c; } return mrb_int_value(mrb, a); } /* 15.2.8.3.26 Integer#truncate */ /* * call-seq: * int.truncate -> int * int.truncate(ndigits) -> int * * Returns self. * * When the precision (ndigits) is negative, the returned value is an integer * with at least ndigits.abs trailing zeros. */ static mrb_value int_truncate(mrb_state *mrb, mrb_value x) { mrb_value f = prepare_int_rounding(mrb, x); if (mrb_undef_p(f)) return mrb_fixnum_value(0); if (mrb_nil_p(f)) return x; #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { mrb_value m = mrb_bint_mod(mrb, x, f); x = mrb_bint_sub_n(mrb, x, m); if (mrb_bint_cmp(mrb, x, mrb_fixnum_value(0)) < 0) { return mrb_bint_add(mrb, x, f); } return x; } #endif mrb_int a = mrb_integer(x); mrb_int b = mrb_integer(f); return mrb_int_value(mrb, a - (a % b)); } /* 15.2.8.3.23 */ /* * call-seq: * int.to_f -> float * * Converts int to a Float. * */ #ifndef MRB_NO_FLOAT static mrb_value int_to_f(mrb_state *mrb, mrb_value num) { #ifdef MRB_USE_BIGINT if (mrb_bigint_p(num)) { return mrb_float_value(mrb, mrb_bint_as_float(mrb, num)); } #endif return mrb_float_value(mrb, (mrb_float)mrb_integer(num)); } MRB_API mrb_value mrb_float_to_integer(mrb_state *mrb, mrb_value x) { if (!mrb_float_p(x)) { mrb_raise(mrb, E_TYPE_ERROR, "non float value"); } mrb_float f = mrb_float(x); if (isinf(f) || isnan(f)) { mrb_raisef(mrb, E_RANGE_ERROR, "float %f out of range", f); } return flo_to_i(mrb, x); } #endif mrb_value mrb_int_add(mrb_state *mrb, mrb_value x, mrb_value y) { mrb_int a; a = mrb_integer(x); if (mrb_integer_p(y)) { mrb_int b, c; if (a == 0) return y; b = mrb_integer(y); if (b == 0) return x; if (mrb_int_add_overflow(a, b, &c)) { #ifdef MRB_USE_BIGINT x = mrb_bint_new_int(mrb, a); return mrb_bint_add(mrb, x, y); #else mrb_int_overflow(mrb, "addition"); #endif } return mrb_int_value(mrb, c); } switch (mrb_type(y)) { #ifdef MRB_USE_BIGINT case MRB_TT_BIGINT: return mrb_bint_add(mrb, y, x); #endif #ifdef MRB_USE_RATIONAL case MRB_TT_RATIONAL: return mrb_rational_add(mrb, y, x); #endif #ifdef MRB_USE_COMPLEX case MRB_TT_COMPLEX: return mrb_complex_add(mrb, y, x); #endif default: #ifdef MRB_NO_FLOAT mrb_raise(mrb, E_TYPE_ERROR, "non integer addition"); #else return mrb_float_value(mrb, (mrb_float)a + mrb_as_float(mrb, y)); #endif } } /* 15.2.8.3.3 */ /* * call-seq: * int + numeric -> numeric_result * * Performs addition: the class of the resulting object depends on * the class of numeric and on the magnitude of the * result. */ static mrb_value int_add(mrb_state *mrb, mrb_value self) { mrb_value other = mrb_get_arg1(mrb); #ifdef MRB_USE_BIGINT if (mrb_bigint_p(self)) { return mrb_bint_add(mrb, self, other); } #endif return mrb_int_add(mrb, self, other); } mrb_value mrb_int_sub(mrb_state *mrb, mrb_value x, mrb_value y) { mrb_int a; a = mrb_integer(x); if (mrb_integer_p(y)) { mrb_int b, c; b = mrb_integer(y); if (mrb_int_sub_overflow(a, b, &c)) { #ifdef MRB_USE_BIGINT x = mrb_bint_new_int(mrb, a); return mrb_bint_sub(mrb, x, y); #else mrb_int_overflow(mrb, "subtraction"); #endif } return mrb_int_value(mrb, c); } switch (mrb_type(y)) { #ifdef MRB_USE_BIGINT case MRB_TT_BIGINT: return mrb_bint_sub(mrb, mrb_bint_new_int(mrb, a), y); #endif #ifdef MRB_USE_RATIONAL case MRB_TT_RATIONAL: return mrb_rational_sub(mrb, mrb_rational_new(mrb, a, 1), y); #endif #ifdef MRB_USE_COMPLEX case MRB_TT_COMPLEX: return mrb_complex_sub(mrb, mrb_complex_new(mrb, (mrb_float)a, 0), y); #endif default: #ifdef MRB_NO_FLOAT mrb_raise(mrb, E_TYPE_ERROR, "non integer subtraction"); #else return mrb_float_value(mrb, (mrb_float)a - mrb_as_float(mrb, y)); #endif } } /* 15.2.8.3.4 */ /* * call-seq: * int - numeric -> numeric * * Performs subtraction: the class of the resulting object depends on * the class of numeric and on the magnitude of the * result. */ static mrb_value int_sub(mrb_state *mrb, mrb_value self) { mrb_value other = mrb_get_arg1(mrb); #ifdef MRB_USE_BIGINT if (mrb_bigint_p(self)) { return mrb_bint_sub(mrb, self, other); } #endif return mrb_int_sub(mrb, self, other); } MRB_API char* mrb_int_to_cstr(char *buf, size_t len, mrb_int n, mrb_int base) { char *bufend = buf + len; char *b = bufend-1; if (base < 2 || 36 < base) return NULL; if (len < 2) return NULL; if (n == 0) { buf[0] = '0'; buf[1] = '\0'; return buf; } *b = '\0'; if (n < 0) { do { if (b-- == buf) return NULL; *b = mrb_digitmap[-(n % base)]; } while (n /= base); if (b-- == buf) return NULL; *b = '-'; } else { do { if (b-- == buf) return NULL; *b = mrb_digitmap[(int)(n % base)]; } while (n /= base); } return b; } MRB_API mrb_value mrb_integer_to_str(mrb_state *mrb, mrb_value x, mrb_int base) { char buf[MRB_INT_BIT+1]; if (base < 2 || 36 < base) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %i", base); } #ifdef MRB_USE_BIGINT if (mrb_bigint_p(x)) { return mrb_bint_to_s(mrb, x, base); } #endif mrb_int val = mrb_integer(x); const char *p = mrb_int_to_cstr(buf, sizeof(buf), val, base); mrb_assert(p != NULL); mrb_value str = mrb_str_new_cstr(mrb, p); RSTR_SET_ASCII_FLAG(mrb_str_ptr(str)); return str; } /* 15.2.8.3.25 */ /* * call-seq: * int.to_s(base=10) -> string * * Returns a string containing the representation of int radix * base (between 2 and 36). * * 12345.to_s #=> "12345" * 12345.to_s(2) #=> "11000000111001" * 12345.to_s(8) #=> "30071" * 12345.to_s(10) #=> "12345" * 12345.to_s(16) #=> "3039" * 12345.to_s(36) #=> "9ix" * */ static mrb_value int_to_s(mrb_state *mrb, mrb_value self) { mrb_int base; if (mrb_get_argc(mrb) > 0) { base = mrb_integer(mrb_get_arg1(mrb)); } else { base = 10; } return mrb_integer_to_str(mrb, self, base); } /* compare two numbers: (1:0:-1; -2 for error) */ static mrb_int cmpnum(mrb_state *mrb, mrb_value v1, mrb_value v2) { #ifdef MRB_NO_FLOAT /* integer version */ if (!mrb_fixnum_p(v2)) { if (!mrb_obj_is_kind_of(mrb, v2, mrb_class_get_id(mrb, MRB_SYM(Numeric)))) { return -2; } v1 = mrb_funcall_argv(mrb, v2, MRB_OPSYM(cmp), 1, &v1); if (mrb_integer_p(v1)) { return -mrb_integer(v1); } return -2; } mrb_int x = mrb_as_int(mrb, v1); mrb_int y = mrb_integer(v2); #else /* float version */ mrb_float x, y; if (mrb_fixnum_p(v1)) { if (mrb_fixnum_p(v2)) { mrb_int x = mrb_integer(v1); mrb_int y = mrb_integer(v2); if (x > y) return 1; else if (x < y) return -1; return 0; } x = (mrb_float)mrb_integer(v1); } else { x = mrb_as_float(mrb, v1); } switch (mrb_type(v2)) { #ifdef MRB_USE_RATIONAL case MRB_TT_RATIONAL: #endif #ifdef MRB_USE_BIGINT case MRB_TT_BIGINT: #endif case MRB_TT_INTEGER: if (mrb_fixnum_p(v2)) { y = (mrb_float)mrb_integer(v2); break; } /* fall through */ case MRB_TT_FLOAT: y = mrb_as_float(mrb, v2); break; default: if (!mrb_obj_is_kind_of(mrb, v2, mrb_class_get_id(mrb, MRB_SYM(Numeric)))) { return -2; } /* fall through */ #ifdef MRB_USE_COMPLEX case MRB_TT_COMPLEX: #endif v1 = mrb_funcall_argv(mrb, v2, MRB_OPSYM(cmp), 1, &v1); if (mrb_fixnum_p(v1)) { return -mrb_integer(v1); } return -2; } #endif if (x > y) return 1; else if (x < y) return -1; return 0; } static mrb_value int_hash(mrb_state *mrb, mrb_value self) { #ifdef MRB_USE_BIGINT if (mrb_bigint_p(self)) { return mrb_bint_hash(mrb, self); } #endif mrb_int n = mrb_integer(self); return mrb_int_value(mrb, mrb_byte_hash((uint8_t*)&n, sizeof(n))); } /* 15.2.8.3.1 */ /* 15.2.9.3.1 */ /* * call-seq: * self.f <=> other.f => -1, 0, +1, or nil * < => -1 * = => 0 * > => +1 * Comparison---Returns -1, 0, or +1 depending on whether int is * less than, equal to, or greater than numeric. This is the * basis for the tests in Comparable. When the operands are * not comparable, it returns nil instead of raising an exception. */ static mrb_value num_cmp(mrb_state *mrb, mrb_value self) { mrb_value other = mrb_get_arg1(mrb); mrb_int n; n = cmpnum(mrb, self, other); if (n == -2) return mrb_nil_value(); return mrb_fixnum_value(n); } static mrb_noreturn void cmperr(mrb_state *mrb, mrb_value v1, mrb_value v2) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "comparison of %t with %t failed", v1, v2); } static mrb_value num_lt(mrb_state *mrb, mrb_value self) { mrb_value other = mrb_get_arg1(mrb); mrb_int n; n = cmpnum(mrb, self, other); if (n == -2) cmperr(mrb, self, other); if (n < 0) return mrb_true_value(); return mrb_false_value(); } static mrb_value num_le(mrb_state *mrb, mrb_value self) { mrb_value other = mrb_get_arg1(mrb); mrb_int n; n = cmpnum(mrb, self, other); if (n == -2) cmperr(mrb, self, other); if (n <= 0) return mrb_true_value(); return mrb_false_value(); } static mrb_value num_gt(mrb_state *mrb, mrb_value self) { mrb_value other = mrb_get_arg1(mrb); mrb_int n; n = cmpnum(mrb, self, other); if (n == -2) cmperr(mrb, self, other); if (n > 0) return mrb_true_value(); return mrb_false_value(); } static mrb_value num_ge(mrb_state *mrb, mrb_value self) { mrb_value other = mrb_get_arg1(mrb); mrb_int n; n = cmpnum(mrb, self, other); if (n == -2) cmperr(mrb, self, other); if (n >= 0) return mrb_true_value(); return mrb_false_value(); } MRB_API mrb_int mrb_cmp(mrb_state *mrb, mrb_value obj1, mrb_value obj2) { mrb_value v; if (mrb_fixnum_p(obj1) || mrb_float_p(obj1)) { return cmpnum(mrb, obj1, obj2); } switch (mrb_type(obj1)) { case MRB_TT_INTEGER: case MRB_TT_FLOAT: case MRB_TT_BIGINT: return cmpnum(mrb, obj1, obj2); case MRB_TT_STRING: if (!mrb_string_p(obj2)) return -2; return mrb_str_cmp(mrb, obj1, obj2); default: v = mrb_funcall_argv(mrb, obj1, MRB_OPSYM(cmp), 1, &obj2); if (mrb_nil_p(v) || !mrb_integer_p(v)) return -2; return mrb_integer(v); } } static mrb_value num_finite_p(mrb_state *mrb, mrb_value self) { return mrb_true_value(); } static mrb_value num_infinite_p(mrb_state *mrb, mrb_value self) { return mrb_false_value(); } #ifndef MRB_NO_FLOAT static mrb_value flo_hash(mrb_state *mrb, mrb_value flo) { mrb_float f = mrb_float(flo); /* normalize -0.0 to 0.0 */ if (f == 0) f = 0.0; return mrb_int_value(mrb, (mrb_int)mrb_byte_hash((uint8_t*)&f, sizeof(f))); } #endif /* ------------------------------------------------------------------------*/ void mrb_init_numeric(mrb_state *mrb) { struct RClass *numeric, *integer; #ifndef MRB_NO_FLOAT struct RClass *fl; #endif /* Numeric Class */ numeric = mrb_define_class_id(mrb, MRB_SYM(Numeric), mrb->object_class); /* 15.2.7 */ mrb_define_method_id(mrb, numeric, MRB_SYM_Q(finite), num_finite_p, MRB_ARGS_NONE()); mrb_define_method_id(mrb, numeric, MRB_SYM_Q(infinite),num_infinite_p, MRB_ARGS_NONE()); mrb_define_method_id(mrb, numeric, MRB_SYM_Q(eql), num_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */ #ifndef MRB_NO_FLOAT mrb_define_method_id(mrb, numeric, MRB_SYM(fdiv), num_fdiv, MRB_ARGS_REQ(1)); #endif /* Integer Class */ mrb->integer_class = integer = mrb_define_class_id(mrb, MRB_SYM(Integer), numeric); /* 15.2.8 */ MRB_SET_INSTANCE_TT(integer, MRB_TT_INTEGER); MRB_UNDEF_ALLOCATOR(integer); mrb_undef_class_method_id(mrb, integer, MRB_SYM(new)); mrb_define_method_id(mrb, integer, MRB_OPSYM(pow), int_pow, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, integer, MRB_OPSYM(cmp), num_cmp, MRB_ARGS_REQ(1)); /* 15.2.8.3.1 */ mrb_define_method_id(mrb, integer, MRB_OPSYM(lt), num_lt, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, integer, MRB_OPSYM(le), num_le, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, integer, MRB_OPSYM(gt), num_gt, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, integer, MRB_OPSYM(ge), num_ge, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, integer, MRB_SYM(to_i), mrb_obj_itself, MRB_ARGS_NONE()); /* 15.2.8.3.24 */ mrb_define_method_id(mrb, integer, MRB_SYM(to_int), mrb_obj_itself, MRB_ARGS_NONE()); mrb_define_method_id(mrb, integer, MRB_OPSYM(add), int_add, MRB_ARGS_REQ(1)); /* 15.2.8.3.1 */ mrb_define_method_id(mrb, integer, MRB_OPSYM(sub), int_sub, MRB_ARGS_REQ(1)); /* 15.2.8.3.2 */ mrb_define_method_id(mrb, integer, MRB_OPSYM(mul), int_mul, MRB_ARGS_REQ(1)); /* 15.2.8.3.3 */ mrb_define_method_id(mrb, integer, MRB_OPSYM(mod), int_mod, MRB_ARGS_REQ(1)); /* 15.2.8.3.5 */ mrb_define_method_id(mrb, integer, MRB_OPSYM(div), int_div, MRB_ARGS_REQ(1)); /* 15.2.8.3.6 */ mrb_define_method_id(mrb, integer, MRB_SYM(quo), int_quo, MRB_ARGS_REQ(1)); /* 15.2.7.4.5(x) */ mrb_define_method_id(mrb, integer, MRB_SYM(div), int_idiv, MRB_ARGS_REQ(1)); #ifndef MRB_NO_FLOAT mrb_define_method_id(mrb, integer, MRB_SYM(fdiv), int_fdiv, MRB_ARGS_REQ(1)); #endif mrb_define_method_id(mrb, integer, MRB_OPSYM(eq), int_equal, MRB_ARGS_REQ(1)); /* 15.2.8.3.7 */ mrb_define_method_id(mrb, integer, MRB_OPSYM(neg), int_rev, MRB_ARGS_NONE()); /* 15.2.8.3.8 */ mrb_define_method_id(mrb, integer, MRB_OPSYM(and), int_and, MRB_ARGS_REQ(1)); /* 15.2.8.3.9 */ mrb_define_method_id(mrb, integer, MRB_OPSYM(or), int_or, MRB_ARGS_REQ(1)); /* 15.2.8.3.10 */ mrb_define_method_id(mrb, integer, MRB_OPSYM(xor), int_xor, MRB_ARGS_REQ(1)); /* 15.2.8.3.11 */ mrb_define_method_id(mrb, integer, MRB_OPSYM(lshift), int_lshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.12 */ mrb_define_method_id(mrb, integer, MRB_OPSYM(rshift), int_rshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.13 */ mrb_define_method_id(mrb, integer, MRB_SYM(ceil), int_ceil, MRB_ARGS_OPT(1)); /* 15.2.8.3.14 */ mrb_define_method_id(mrb, integer, MRB_SYM(floor), int_floor, MRB_ARGS_OPT(1)); /* 15.2.8.3.17 */ mrb_define_method_id(mrb, integer, MRB_SYM(round), int_round, MRB_ARGS_OPT(1)); /* 15.2.8.3.20 */ mrb_define_method_id(mrb, integer, MRB_SYM(truncate), int_truncate, MRB_ARGS_OPT(1)); /* 15.2.8.3.26 */ mrb_define_method_id(mrb, integer, MRB_SYM(hash), int_hash, MRB_ARGS_NONE()); /* 15.2.8.3.18 */ #ifndef MRB_NO_FLOAT mrb_define_method_id(mrb, integer, MRB_SYM(to_f), int_to_f, MRB_ARGS_NONE()); /* 15.2.8.3.23 */ #endif mrb_define_method_id(mrb, integer, MRB_SYM(to_s), int_to_s, MRB_ARGS_OPT(1)); /* 15.2.8.3.25 */ mrb_define_method_id(mrb, integer, MRB_SYM(inspect), int_to_s, MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, integer, MRB_SYM(divmod), int_divmod, MRB_ARGS_REQ(1)); /* 15.2.8.3.30(x) */ mrb_define_method_id(mrb, integer, MRB_SYM(__coerce_step_counter), coerce_step_counter, MRB_ARGS_REQ(1)); /* Fixnum Class for compatibility */ mrb_define_const_id(mrb, mrb->object_class, MRB_SYM(Fixnum), mrb_obj_value(integer)); #ifndef MRB_NO_FLOAT /* Float Class */ mrb->float_class = fl = mrb_define_class_id(mrb, MRB_SYM(Float), numeric); /* 15.2.9 */ MRB_SET_INSTANCE_TT(fl, MRB_TT_FLOAT); MRB_UNDEF_ALLOCATOR(fl); mrb_undef_class_method(mrb, fl, "new"); mrb_define_method_id(mrb, fl, MRB_OPSYM(pow), flo_pow, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, fl, MRB_OPSYM(div), flo_div, MRB_ARGS_REQ(1)); /* 15.2.9.3.6 */ mrb_define_method_id(mrb, fl, MRB_SYM(quo), flo_div, MRB_ARGS_REQ(1)); /* 15.2.7.4.5(x) */ mrb_define_method_id(mrb, fl, MRB_SYM(fdiv), flo_div, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, fl, MRB_SYM(div), flo_idiv, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, fl, MRB_OPSYM(add), flo_add, MRB_ARGS_REQ(1)); /* 15.2.9.3.3 */ mrb_define_method_id(mrb, fl, MRB_OPSYM(sub), flo_sub, MRB_ARGS_REQ(1)); /* 15.2.9.3.4 */ mrb_define_method_id(mrb, fl, MRB_OPSYM(mul), flo_mul, MRB_ARGS_REQ(1)); /* 15.2.9.3.5 */ mrb_define_method_id(mrb, fl, MRB_OPSYM(mod), flo_mod, MRB_ARGS_REQ(1)); /* 15.2.9.3.7 */ mrb_define_method_id(mrb, fl, MRB_OPSYM(cmp), num_cmp, MRB_ARGS_REQ(1)); /* 15.2.9.3.1 */ mrb_define_method_id(mrb, fl, MRB_OPSYM(lt), num_lt, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, fl, MRB_OPSYM(le), num_le, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, fl, MRB_OPSYM(gt), num_gt, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, fl, MRB_OPSYM(ge), num_ge, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, fl, MRB_OPSYM(eq), flo_eq, MRB_ARGS_REQ(1)); /* 15.2.9.3.2 */ mrb_define_method_id(mrb, fl, MRB_SYM(ceil), flo_ceil, MRB_ARGS_OPT(1)); /* 15.2.9.3.8 */ mrb_define_method_id(mrb, fl, MRB_SYM_Q(finite), flo_finite_p, MRB_ARGS_NONE()); /* 15.2.9.3.9 */ mrb_define_method_id(mrb, fl, MRB_SYM(floor), flo_floor, MRB_ARGS_OPT(1)); /* 15.2.9.3.10 */ mrb_define_method_id(mrb, fl, MRB_SYM_Q(infinite),flo_infinite_p, MRB_ARGS_NONE()); /* 15.2.9.3.11 */ mrb_define_method_id(mrb, fl, MRB_SYM(round), flo_round, MRB_ARGS_OPT(1)); /* 15.2.9.3.12 */ mrb_define_method_id(mrb, fl, MRB_SYM(to_f), mrb_obj_itself, MRB_ARGS_NONE()); /* 15.2.9.3.13 */ mrb_define_method_id(mrb, fl, MRB_SYM(to_i), flo_to_i, MRB_ARGS_NONE()); /* 15.2.9.3.14 */ mrb_define_method_id(mrb, fl, MRB_SYM(truncate), flo_truncate, MRB_ARGS_OPT(1)); /* 15.2.9.3.15 */ mrb_define_method_id(mrb, fl, MRB_SYM(divmod), flo_divmod, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, fl, MRB_SYM(to_s), flo_to_s, MRB_ARGS_NONE()); /* 15.2.9.3.16(x) */ mrb_define_method_id(mrb, fl, MRB_SYM(inspect), flo_to_s, MRB_ARGS_NONE()); mrb_define_method_id(mrb, fl, MRB_SYM_Q(nan), flo_nan_p, MRB_ARGS_NONE()); mrb_define_method_id(mrb, fl, MRB_SYM(abs), flo_abs, MRB_ARGS_NONE()); /* 15.2.7.4.3 */ mrb_define_method_id(mrb, fl, MRB_SYM(hash), flo_hash, MRB_ARGS_NONE()); #ifdef INFINITY mrb_define_const_id(mrb, fl, MRB_SYM(INFINITY), mrb_float_value(mrb, INFINITY)); #endif #ifdef NAN mrb_define_const_id(mrb, fl, MRB_SYM(NAN), mrb_float_value(mrb, NAN)); #endif #endif } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/enum.c0000644000000000000000000000013115077107276017571 xustar0030 mtime=1761382078.133420506 30 atime=1761382080.154411261 29 ctime=1761382108.57130191 nghttp2-1.68.0/third-party/mruby/src/enum.c0000644000175100017510000000127415077107276020166 0ustar00runnerrunner/* ** enum.c - Enumerable module ** ** See Copyright Notice in mruby.h */ #include #include #include /* internal method `__update_hash(oldhash, index, itemhash)` */ static mrb_value enum_update_hash(mrb_state *mrb, mrb_value self) { mrb_int hash; mrb_int index; mrb_int hv; mrb_get_args(mrb, "iii", &hash, &index, &hv); hash ^= ((uint32_t)hv << (index % 16)); return mrb_int_value(mrb, hash); } void mrb_init_enumerable(mrb_state *mrb) { struct RClass *enumerable = mrb_define_module_id(mrb, MRB_SYM(Enumerable)); /* 15.3.2 */ mrb_define_module_function_id(mrb, enumerable, MRB_SYM(__update_hash), enum_update_hash, MRB_ARGS_REQ(3)); } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/array.c0000644000000000000000000000013115077107276017743 xustar0030 mtime=1761382078.132420511 30 atime=1761382080.150411279 29 ctime=1761382108.59230185 nghttp2-1.68.0/third-party/mruby/src/array.c0000644000175100017510000012711015077107276020336 0ustar00runnerrunner/* ** array.c - Array class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include "value_array.h" #define ARY_DEFAULT_LEN 4 #define ARY_SHRINK_RATIO 5 /* must be larger than 2 */ #define ARY_C_MAX_SIZE (SIZE_MAX / sizeof(mrb_value)) #ifndef MRB_ARY_LENGTH_MAX #define MRB_ARY_LENGTH_MAX 131072 #endif #define ARY_MAX_SIZE ((mrb_int)((ARY_C_MAX_SIZE < (size_t)MRB_INT_MAX) ? ARY_C_MAX_SIZE : MRB_INT_MAX-1)) static void ary_too_big(mrb_state *mrb) { mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } static inline void ary_check_too_big(mrb_state *mrb, mrb_int a, mrb_int b) { if (a > ARY_MAX_SIZE - b || a < 0) ary_too_big(mrb); #if MRB_ARY_LENGTH_MAX != 0 if (a > MRB_ARY_LENGTH_MAX - b || a < 0) ary_too_big(mrb); #endif } static struct RArray* ary_new_capa(mrb_state *mrb, mrb_int capa) { ary_check_too_big(mrb, capa, 0); size_t blen = capa * sizeof(mrb_value); struct RArray *a = MRB_OBJ_ALLOC(mrb, MRB_TT_ARRAY, mrb->array_class); if (capa <= MRB_ARY_EMBED_LEN_MAX) { ARY_SET_EMBED_LEN(a, 0); } else { a->as.heap.ptr = (mrb_value *)mrb_malloc(mrb, blen); a->as.heap.aux.capa = capa; a->as.heap.len = 0; } return a; } MRB_API mrb_value mrb_ary_new_capa(mrb_state *mrb, mrb_int capa) { struct RArray *a = ary_new_capa(mrb, capa); return mrb_obj_value(a); } MRB_API mrb_value mrb_ary_new(mrb_state *mrb) { return mrb_ary_new_capa(mrb, 0); } /* * To copy array, use this instead of memcpy because of portability * * gcc on ARM may fail optimization of memcpy * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56620 * * gcc on MIPS also fail * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39755 * * memcpy doesn't exist on freestanding environment * * If you optimize for binary size, use memcpy instead of this at your own risk * of above portability issue. * * See also https://togetter.com/li/462898 (Japanese) */ static inline void array_copy(mrb_value *dst, const mrb_value *src, mrb_int size) { for (mrb_int i = 0; i < size; i++) { dst[i] = src[i]; } } static struct RArray* ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals) { struct RArray *a = ary_new_capa(mrb, size); array_copy(ARY_PTR(a), vals, size); ARY_SET_LEN(a, size); return a; } MRB_API mrb_value mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals) { struct RArray *a = ary_new_from_values(mrb, size, vals); return mrb_obj_value(a); } MRB_API mrb_value mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr) { struct RArray *a = ary_new_capa(mrb, 2); mrb_value *p = ARY_PTR(a); p[0] = car; p[1] = cdr; ARY_SET_LEN(a, 2); return mrb_obj_value(a); } static void ary_fill_with_nil(mrb_value *ptr, mrb_int size) { mrb_value nil = mrb_nil_value(); while (size--) { *ptr++ = nil; } } #define ary_modify_check(mrb, a) mrb_check_frozen((mrb), (a)) static void ary_modify(mrb_state *mrb, struct RArray *a) { ary_modify_check(mrb, a); if (ARY_SHARED_P(a)) { mrb_shared_array *shared = a->as.heap.aux.shared; if (shared->refcnt == 1 && a->as.heap.ptr == shared->ptr) { a->as.heap.ptr = shared->ptr; a->as.heap.aux.capa = a->as.heap.len; mrb_free(mrb, shared); } else { mrb_value *p = a->as.heap.ptr; mrb_value *ptr = (mrb_value*)mrb_malloc(mrb, a->as.heap.len * sizeof(mrb_value)); if (p) { array_copy(ptr, p, a->as.heap.len); } a->as.heap.ptr = ptr; a->as.heap.aux.capa = a->as.heap.len; mrb_ary_decref(mrb, shared); } ARY_UNSET_SHARED_FLAG(a); } } MRB_API void mrb_ary_modify(mrb_state *mrb, struct RArray* a) { mrb_write_barrier(mrb, (struct RBasic*)a); ary_modify(mrb, a); } static void ary_make_shared(mrb_state *mrb, struct RArray *a) { if (!ARY_SHARED_P(a) && !ARY_EMBED_P(a)) { mrb_shared_array *shared = (mrb_shared_array*)mrb_malloc(mrb, sizeof(mrb_shared_array)); mrb_value *ptr = a->as.heap.ptr; mrb_int len = a->as.heap.len; shared->refcnt = 1; if (a->as.heap.aux.capa > len) { a->as.heap.ptr = shared->ptr = (mrb_value*)mrb_realloc(mrb, ptr, sizeof(mrb_value)*len+1); } else { shared->ptr = ptr; } shared->len = len; a->as.heap.aux.shared = shared; ARY_SET_SHARED_FLAG(a); } } static void ary_expand_capa(mrb_state *mrb, struct RArray *a, mrb_int len) { mrb_int capa = ARY_CAPA(a); ary_check_too_big(mrb, len, 0); if (capa < ARY_DEFAULT_LEN) { capa = ARY_DEFAULT_LEN; } while (capa < len) { if (capa <= ARY_MAX_SIZE / 2) { capa *= 2; } else { capa = len; } } if (capa > ARY_MAX_SIZE) { ary_too_big(mrb); } if (ARY_EMBED_P(a)) { mrb_value *ptr = ARY_EMBED_PTR(a); mrb_int slen = ARY_EMBED_LEN(a); mrb_value *expanded_ptr = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value)*capa); ARY_UNSET_EMBED_FLAG(a); array_copy(expanded_ptr, ptr, slen); a->as.heap.len = slen; a->as.heap.aux.capa = capa; a->as.heap.ptr = expanded_ptr; } else if (capa > a->as.heap.aux.capa) { mrb_value *expanded_ptr = (mrb_value*)mrb_realloc(mrb, a->as.heap.ptr, sizeof(mrb_value)*capa); a->as.heap.aux.capa = capa; a->as.heap.ptr = expanded_ptr; } } static void ary_shrink_capa(mrb_state *mrb, struct RArray *a) { if (ARY_EMBED_P(a)) return; mrb_int capa = a->as.heap.aux.capa; if (capa < ARY_DEFAULT_LEN * 2) return; if (capa <= a->as.heap.len * ARY_SHRINK_RATIO) return; do { capa /= 2; if (capa < ARY_DEFAULT_LEN) { capa = ARY_DEFAULT_LEN; break; } } while (capa > a->as.heap.len * ARY_SHRINK_RATIO); if (capa > a->as.heap.len && capa < a->as.heap.aux.capa) { a->as.heap.aux.capa = capa; a->as.heap.ptr = (mrb_value*)mrb_realloc(mrb, a->as.heap.ptr, sizeof(mrb_value)*capa); } } MRB_API mrb_value mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int new_len) { struct RArray *a = mrb_ary_ptr(ary); ary_modify(mrb, a); mrb_int old_len = RARRAY_LEN(ary); if (old_len != new_len) { if (new_len < old_len) { ary_shrink_capa(mrb, a); } else { ary_expand_capa(mrb, a, new_len); ary_fill_with_nil(ARY_PTR(a) + old_len, new_len - old_len); } ARY_SET_LEN(a, new_len); } return ary; } static mrb_value mrb_ary_s_create(mrb_state *mrb, mrb_value klass) { const mrb_value *vals; mrb_int len; mrb_get_args(mrb, "*!", &vals, &len); mrb_value ary = mrb_ary_new_from_values(mrb, len, vals); struct RArray *a = mrb_ary_ptr(ary); a->c = mrb_class_ptr(klass); return ary; } static void ary_replace(mrb_state*, struct RArray*, struct RArray*); static mrb_value mrb_ary_init(mrb_state *mrb, mrb_value ary) { mrb_value ss = mrb_fixnum_value(0); mrb_value obj = mrb_nil_value(); mrb_value blk = mrb_nil_value(); mrb_get_args(mrb, "|oo&", &ss, &obj, &blk); if (mrb_array_p(ss) && mrb_nil_p(obj) && mrb_nil_p(blk)) { ary_replace(mrb, mrb_ary_ptr(ary), mrb_ary_ptr(ss)); return ary; } mrb_int size = mrb_as_int(mrb, ss); struct RArray *a = mrb_ary_ptr(ary); if (ARY_CAPA(a) < size) { ary_expand_capa(mrb, a, size); } int ai = mrb_gc_arena_save(mrb); for (mrb_int i=0; i self * * Adds to +array+ all elements from each \Array in +other_arrays+; returns +self+: * * a = [0, 1] * a.concat([2, 3], [4, 5]) # => [0, 1, 2, 3, 4, 5] */ static mrb_value mrb_ary_concat_m(mrb_state *mrb, mrb_value self) { mrb_value *args; mrb_int len; mrb_get_args(mrb, "*!", &args, &len); for (int i=0; ias.heap.aux.shared); a->as.heap.aux.capa = 0; a->as.heap.len = 0; a->as.heap.ptr = NULL; ARY_UNSET_SHARED_FLAG(a); } if (ARY_SHARED_P(b)) { shared_b: if (ARY_EMBED_P(a)) { ARY_UNSET_EMBED_FLAG(a); } else { mrb_free(mrb, a->as.heap.ptr); } a->as.heap.ptr = b->as.heap.ptr; a->as.heap.len = len; a->as.heap.aux.shared = b->as.heap.aux.shared; a->as.heap.aux.shared->refcnt++; ARY_SET_SHARED_FLAG(a); mrb_write_barrier(mrb, (struct RBasic*)a); return; } if (!mrb_frozen_p(b) && len > ARY_REPLACE_SHARED_MIN) { ary_make_shared(mrb, b); goto shared_b; } if (ARY_CAPA(a) < len) ary_expand_capa(mrb, a, len); array_copy(ARY_PTR(a), ARY_PTR(b), len); mrb_write_barrier(mrb, (struct RBasic*)a); ARY_SET_LEN(a, len); } MRB_API void mrb_ary_replace(mrb_state *mrb, mrb_value self, mrb_value other) { struct RArray *a1 = mrb_ary_ptr(self); struct RArray *a2 = mrb_ary_ptr(other); if (a1 != a2) { ary_replace(mrb, a1, a2); } } static mrb_value mrb_ary_replace_m(mrb_state *mrb, mrb_value self) { mrb_value other; mrb_get_args(mrb, "A", &other); mrb_ary_replace(mrb, self, other); return self; } static mrb_value mrb_ary_times(mrb_state *mrb, mrb_value self) { struct RArray *a1 = mrb_ary_ptr(self); mrb_value arg = mrb_get_arg1(mrb); mrb_value tmp = mrb_check_string_type(mrb, arg); if (!mrb_nil_p(tmp)) { return mrb_ary_join(mrb, self, tmp); } mrb_int times = mrb_as_int(mrb, arg); if (times < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument"); } if (times == 0) return mrb_ary_new(mrb); if (ARY_MAX_SIZE / times < ARY_LEN(a1)) { ary_too_big(mrb); } mrb_int len1 = ARY_LEN(a1); struct RArray *a2 = ary_new_capa(mrb, len1 * times); ARY_SET_LEN(a2, len1 * times); mrb_value *ptr = ARY_PTR(a2); while (times--) { array_copy(ptr, ARY_PTR(a1), len1); ptr += len1; } return mrb_obj_value(a2); } static mrb_value mrb_ary_reverse_bang(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_int len = ARY_LEN(a); if (len > 1) { ary_modify(mrb, a); mrb_value *p1 = ARY_PTR(a); mrb_value *p2 = p1 + len - 1; while (p1 < p2) { mrb_value tmp = *p1; *p1++ = *p2; *p2-- = tmp; } } return self; } static mrb_value mrb_ary_reverse(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self), *b = ary_new_capa(mrb, ARY_LEN(a)); mrb_int len = ARY_LEN(a); if (len > 0) { mrb_value *p1 = ARY_PTR(a); mrb_value *e = p1 + len; mrb_value *p2 = ARY_PTR(b) + len - 1; while (p1 < e) { *p2-- = *p1++; } ARY_SET_LEN(b, len); } return mrb_obj_value(b); } MRB_API void mrb_ary_push(mrb_state *mrb, mrb_value ary, mrb_value elem) { struct RArray *a = mrb_ary_ptr(ary); mrb_int len = ARY_LEN(a); ary_modify(mrb, a); if (len == ARY_CAPA(a)) ary_expand_capa(mrb, a, len + 1); ARY_PTR(a)[len] = elem; ARY_SET_LEN(a, len+1); mrb_field_write_barrier_value(mrb, (struct RBasic*)a, elem); } static mrb_value mrb_ary_push_m(mrb_state *mrb, mrb_value self) { mrb_int argc = mrb_get_argc(mrb); if (argc == 1) { mrb_ary_push(mrb, self, mrb_get_argv(mrb)[0]); return self; } struct RArray *a = mrb_ary_ptr(self); mrb_int len = ARY_LEN(a); mrb_int len2 = len + argc; ary_modify(mrb, a); if (ARY_CAPA(a) < len2) { ary_expand_capa(mrb, a, len2); } const mrb_value *argv = mrb_get_argv(mrb); array_copy(ARY_PTR(a)+len, argv, argc); ARY_SET_LEN(a, len2); while (argc--) { mrb_field_write_barrier_value(mrb, (struct RBasic*)a, *argv); argv++; } return self; } MRB_API mrb_value mrb_ary_pop(mrb_state *mrb, mrb_value ary) { struct RArray *a = mrb_ary_ptr(ary); mrb_int len = ARY_LEN(a); ary_modify_check(mrb, a); if (len == 0) return mrb_nil_value(); ARY_SET_LEN(a, len-1); return ARY_PTR(a)[len-1]; } #define ARY_SHIFT_SHARED_MIN 10 MRB_API mrb_value mrb_ary_shift(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_int len = ARY_LEN(a); ary_modify_check(mrb, a); if (len == 0) return mrb_nil_value(); if (ARY_SHARED_P(a)) { L_SHIFT: a->as.heap.ptr++; a->as.heap.len--; return a->as.heap.ptr[-1]; } else if (len > ARY_SHIFT_SHARED_MIN) { ary_make_shared(mrb, a); goto L_SHIFT; } else { mrb_value *ptr = ARY_PTR(a); mrb_int size = len; mrb_value val = *ptr; while (--size) { *ptr = *(ptr+1); ptr++; } ARY_SET_LEN(a, len-1); return val; } } static mrb_value mrb_ary_shift_m(mrb_state *mrb, mrb_value self) { if (mrb_get_argc(mrb) == 0) { return mrb_ary_shift(mrb, self); } mrb_int n = mrb_as_int(mrb, mrb_get_arg1(mrb)); struct RArray *a = mrb_ary_ptr(self); mrb_int len = ARY_LEN(a); ary_modify_check(mrb, a); if (len == 0 || n == 0) return mrb_ary_new(mrb); if (n < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array shift"); if (n > len) n = len; mrb_value val = mrb_ary_new_from_values(mrb, n, ARY_PTR(a)); if (ARY_SHARED_P(a)) { L_SHIFT: a->as.heap.ptr+=n; a->as.heap.len-=n; return val; } if (len > ARY_SHIFT_SHARED_MIN) { ary_make_shared(mrb, a); goto L_SHIFT; } else if (len == n) { ARY_SET_LEN(a, 0); } else { mrb_value *ptr = ARY_PTR(a); mrb_int size = len-n; while (size--) { *ptr = *(ptr+n); ptr++; } ARY_SET_LEN(a, len-n); } return val; } /* self = [1,2,3] item = 0 self.unshift item p self #=> [0, 1, 2, 3] */ MRB_API mrb_value mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item) { struct RArray *a = mrb_ary_ptr(self); mrb_int len = ARY_LEN(a); if (ARY_SHARED_P(a) && a->as.heap.aux.shared->refcnt == 1 /* shared only referenced from this array */ && a->as.heap.ptr - a->as.heap.aux.shared->ptr >= 1) /* there's room for unshifted item */ { a->as.heap.ptr--; a->as.heap.ptr[0] = item; } else { mrb_value *ptr; ary_modify(mrb, a); if (ARY_CAPA(a) < len + 1) ary_expand_capa(mrb, a, len + 1); ptr = ARY_PTR(a); value_move(ptr + 1, ptr, len); ptr[0] = item; } ARY_SET_LEN(a, len+1); mrb_field_write_barrier_value(mrb, (struct RBasic*)a, item); return self; } /* * call-seq: * array.unshift(*objects) -> self * * Prepends the given +objects+ to +self+: * * a = [:foo, 'bar', 2] * a.unshift(:bam, :bat) # => [:bam, :bat, :foo, "bar", 2] * * Array#prepend is an alias for Array#unshift. * * Related: #push, #pop, #shift. */ static mrb_value mrb_ary_unshift_m(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_value *ptr; mrb_int alen = mrb_get_argc(mrb); if (alen == 0) { ary_modify_check(mrb, a); return self; } const mrb_value *vals = mrb_get_argv(mrb); mrb_int len = ARY_LEN(a); if (alen > ARY_MAX_SIZE - len) { ary_too_big(mrb); } if (ARY_SHARED_P(a) && a->as.heap.aux.shared->refcnt == 1 /* shared only referenced from this array */ && a->as.heap.ptr - a->as.heap.aux.shared->ptr >= alen) /* there's room for unshifted item */ { ary_modify_check(mrb, a); a->as.heap.ptr -= alen; ptr = a->as.heap.ptr; } else { mrb_bool same = vals == ARY_PTR(a); ary_modify(mrb, a); if (ARY_CAPA(a) < len + alen) ary_expand_capa(mrb, a, len + alen); ptr = ARY_PTR(a); value_move(ptr + alen, ptr, len); if (same) vals = ptr; } array_copy(ptr, vals, alen); ARY_SET_LEN(a, len+alen); while (alen--) { mrb_field_write_barrier_value(mrb, (struct RBasic*)a, vals[alen]); } return self; } MRB_API void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val) { struct RArray *a = mrb_ary_ptr(ary); mrb_int len = ARY_LEN(a); ary_modify(mrb, a); /* range check */ if (n < 0) { n += len; if (n < 0) { mrb_raisef(mrb, E_INDEX_ERROR, "index %i out of array", n - len); } } if (n >= ARY_MAX_SIZE) { mrb_raise(mrb, E_INDEX_ERROR, "index too big"); } if (len <= n) { if (ARY_CAPA(a) <= n) ary_expand_capa(mrb, a, n + 1); ary_fill_with_nil(ARY_PTR(a) + len, n + 1 - len); ARY_SET_LEN(a, n+1); } ARY_PTR(a)[n] = val; mrb_field_write_barrier_value(mrb, (struct RBasic*)a, val); } static struct RArray* ary_dup(mrb_state *mrb, struct RArray *a) { return ary_new_from_values(mrb, ARY_LEN(a), ARY_PTR(a)); } MRB_API mrb_value mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_value rpl) { struct RArray *a = mrb_ary_ptr(ary); mrb_int alen = ARY_LEN(a); const mrb_value *argv; mrb_int argc; mrb_int tail; ary_modify(mrb, a); /* len check */ if (len < 0) mrb_raisef(mrb, E_INDEX_ERROR, "negative length (%i)", len); /* range check */ if (head < 0) { head += alen; if (head < 0) goto out_of_range; } if (head > ARY_MAX_SIZE - len) { out_of_range: mrb_raisef(mrb, E_INDEX_ERROR, "index %i is out of array", head); } tail = head + len; if (alen < len || alen < tail) { len = alen - head; tail = head + len; } /* size check */ if (mrb_array_p(rpl)) { argc = RARRAY_LEN(rpl); argv = RARRAY_PTR(rpl); if (argv == ARY_PTR(a)) { struct RArray *r; if (argc > 32767) { mrb_raise(mrb, E_ARGUMENT_ERROR, "too big recursive splice"); } r = ary_dup(mrb, a); argv = ARY_PTR(r); } } else if (mrb_undef_p(rpl)) { argc = 0; argv = NULL; } else { argc = 1; argv = &rpl; } if (head >= alen) { if (head > ARY_MAX_SIZE - argc) goto out_of_range; len = head + argc; if (len > ARY_CAPA(a)) { ary_expand_capa(mrb, a, len); } ary_fill_with_nil(ARY_PTR(a) + alen, head - alen); if (argc > 0) { array_copy(ARY_PTR(a) + head, argv, argc); } ARY_SET_LEN(a, len); } else { if (alen - len > ARY_MAX_SIZE - argc) { head = alen + argc - len; goto out_of_range; } mrb_int newlen = alen + argc - len; if (newlen > ARY_CAPA(a)) { ary_expand_capa(mrb, a, newlen); } if (len != argc) { mrb_value *ptr = ARY_PTR(a); value_move(ptr + head + argc, ptr + tail, alen - tail); ARY_SET_LEN(a, newlen); } if (argc > 0) { value_move(ARY_PTR(a) + head, argv, argc); } } mrb_write_barrier(mrb, (struct RBasic*)a); return ary; } void mrb_ary_decref(mrb_state *mrb, mrb_shared_array *shared) { shared->refcnt--; if (shared->refcnt == 0) { mrb_free(mrb, shared->ptr); mrb_free(mrb, shared); } } static mrb_value ary_subseq(mrb_state *mrb, struct RArray *a, mrb_int beg, mrb_int len) { struct RArray *b; if (!ARY_SHARED_P(a) && len <= ARY_SHIFT_SHARED_MIN) { return mrb_ary_new_from_values(mrb, len, ARY_PTR(a)+beg); } ary_make_shared(mrb, a); b = MRB_OBJ_ALLOC(mrb, MRB_TT_ARRAY, mrb->array_class); b->as.heap.ptr = a->as.heap.ptr + beg; b->as.heap.len = len; b->as.heap.aux.shared = a->as.heap.aux.shared; b->as.heap.aux.shared->refcnt++; ARY_SET_SHARED_FLAG(b); return mrb_obj_value(b); } mrb_value mrb_ary_subseq(mrb_state *mrb, mrb_value ary, mrb_int beg, mrb_int len) { struct RArray *a = mrb_ary_ptr(ary); return ary_subseq(mrb, a, beg, len); } static mrb_int aget_index(mrb_state *mrb, mrb_value index) { if (mrb_integer_p(index)) { return mrb_integer(index); } #ifndef MRB_NO_FLOAT else if (mrb_float_p(index)) { return (mrb_int)mrb_float(index); } #endif else { mrb_int i, argc; const mrb_value *argv; mrb_get_args(mrb, "i*!", &i, &argv, &argc); return i; } } /* * call-seq: * ary[index] -> obj or nil * ary[start, length] -> new_ary or nil * ary[range] -> new_ary or nil * ary.slice(index) -> obj or nil * ary.slice(start, length) -> new_ary or nil * ary.slice(range) -> new_ary or nil * * Element Reference --- Returns the element at +index+, or returns a * subarray starting at the +start+ index and continuing for +length+ * elements, or returns a subarray specified by +range+ of indices. * * Negative indices count backward from the end of the array (-1 is the last * element). For +start+ and +range+ cases the starting index is just before * an element. Additionally, an empty array is returned when the starting * index for an element range is at the end of the array. * * Returns +nil+ if the index (or starting index) are out of range. * * a = [ "a", "b", "c", "d", "e" ] * a[1] => "b" * a[1,2] => ["b", "c"] * a[1..-2] => ["b", "c", "d"] * */ static mrb_value mrb_ary_aget(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_int i, len; mrb_value index; if (mrb_get_argc(mrb) == 1) { index = mrb_get_arg1(mrb); switch (mrb_type(index)) { /* a[n..m] */ case MRB_TT_RANGE: if (mrb_range_beg_len(mrb, index, &i, &len, ARY_LEN(a), TRUE) == MRB_RANGE_OK) { return ary_subseq(mrb, a, i, len); } else { return mrb_nil_value(); } case MRB_TT_INTEGER: return mrb_ary_ref(mrb, self, mrb_integer(index)); default: return mrb_ary_ref(mrb, self, aget_index(mrb, index)); } } mrb_get_args(mrb, "oi", &index, &len); i = aget_index(mrb, index); mrb_int alen = ARY_LEN(a); if (i < 0) i += alen; if (i < 0 || alen < i) return mrb_nil_value(); if (len < 0) return mrb_nil_value(); if (alen == i) return mrb_ary_new(mrb); if (len > alen - i) len = alen - i; return ary_subseq(mrb, a, i, len); } /* * call-seq: * ary[index] = obj -> obj * ary[start, length] = obj or other_ary or nil -> obj or other_ary or nil * ary[range] = obj or other_ary or nil -> obj or other_ary or nil * * Element Assignment --- Sets the element at +index+, or replaces a subarray * from the +start+ index for +length+ elements, or replaces a subarray * specified by the +range+ of indices. * * If indices are greater than the current capacity of the array, the array * grows automatically. Elements are inserted into the array at +start+ if * +length+ is zero. * * Negative indices will count backward from the end of the array. For * +start+ and +range+ cases the starting index is just before an element. * * An IndexError is raised if a negative index points past the beginning of * the array. * * See also Array#push, and Array#unshift. * * a = Array.new * a[4] = "4"; #=> [nil, nil, nil, nil, "4"] * a[0, 3] = [ 'a', 'b', 'c' ] #=> ["a", "b", "c", nil, "4"] * a[1..2] = [ 1, 2 ] #=> ["a", 1, 2, nil, "4"] * a[0, 2] = "?" #=> ["?", 2, nil, "4"] * a[0..2] = "A" #=> ["A", "4"] * a[-1] = "Z" #=> ["A", "Z"] * a[1..-1] = nil #=> ["A", nil] * a[1..-1] = [] #=> ["A"] * a[0, 0] = [ 1, 2 ] #=> [1, 2, "A"] * a[3, 0] = "B" #=> [1, 2, "A", "B"] */ static mrb_value mrb_ary_aset(mrb_state *mrb, mrb_value self) { mrb_value v1, v2, v3; if (mrb_get_argc(mrb) == 2) { mrb_int i, len; const mrb_value *vs = mrb_get_argv(mrb); v1 = vs[0]; v2 = vs[1]; /* a[n..m] = v */ switch (mrb_range_beg_len(mrb, v1, &i, &len, RARRAY_LEN(self), FALSE)) { case MRB_RANGE_TYPE_MISMATCH: mrb_ary_set(mrb, self, aget_index(mrb, v1), v2); break; case MRB_RANGE_OK: mrb_ary_splice(mrb, self, i, len, v2); break; case MRB_RANGE_OUT: mrb_raisef(mrb, E_RANGE_ERROR, "%v out of range", v1); break; } return v2; } mrb_get_args(mrb, "ooo", &v1, &v2, &v3); /* a[n,m] = v */ mrb_ary_splice(mrb, self, aget_index(mrb, v1), aget_index(mrb, v2), v3); return v3; } mrb_value mrb_ary_delete_at(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_int index = mrb_as_int(mrb, mrb_get_arg1(mrb)); mrb_int alen = ARY_LEN(a); if (index < 0) index += alen; if (index < 0 || alen <= index) return mrb_nil_value(); ary_modify(mrb, a); mrb_value *ptr = ARY_PTR(a); mrb_value val = ptr[index]; ptr += index; mrb_int len = alen - index; while (--len) { *ptr = *(ptr+1); ptr++; } ARY_SET_LEN(a, alen-1); ary_shrink_capa(mrb, a); return val; } static mrb_value mrb_ary_first(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_int size; if (mrb_get_argc(mrb) == 0) { if (ARY_LEN(a) > 0) return ARY_PTR(a)[0]; return mrb_nil_value(); } mrb_get_args(mrb, "|i", &size); if (size < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size"); } mrb_int alen = ARY_LEN(a); if (size > alen) size = alen; if (ARY_SHARED_P(a)) { return ary_subseq(mrb, a, 0, size); } return mrb_ary_new_from_values(mrb, size, ARY_PTR(a)); } static mrb_value mrb_ary_last(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_int alen = ARY_LEN(a); if (mrb_get_argc(mrb) == 0) { if (alen > 0) return ARY_PTR(a)[alen - 1]; return mrb_nil_value(); } mrb_int size = mrb_integer(mrb_get_arg1(mrb)); if (size < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size"); } if (size > alen) size = alen; if (ARY_SHARED_P(a) || size > ARY_DEFAULT_LEN) { return ary_subseq(mrb, a, alen - size, size); } return mrb_ary_new_from_values(mrb, size, ARY_PTR(a) + alen - size); } /* * call-seq: * ary.index(val) -> int or nil * ary.index {|item| block } -> int or nil * array.index -> enumerator * * Returns the _index_ of the first object in +ary+ such that the object is * == to +obj+. * * If a block is given instead of an argument, returns the _index_ of the * first object for which the block returns +true+. Returns +nil+ if no * match is found. * * ISO 15.2.12.5.14 */ static mrb_value mrb_ary_index_m(mrb_state *mrb, mrb_value self) { mrb_value obj, blk; if (mrb_get_args(mrb, "|o&", &obj, &blk) == 0 && mrb_nil_p(blk)) { return mrb_funcall_id(mrb, self, MRB_SYM(to_enum), 1, mrb_symbol_value(MRB_SYM(index))); } if (mrb_nil_p(blk)) { for (mrb_int i = 0; i < RARRAY_LEN(self); i++) { if (mrb_equal(mrb, RARRAY_PTR(self)[i], obj)) { return mrb_int_value(mrb, i); } } } else { for (mrb_int i = 0; i < RARRAY_LEN(self); i++) { mrb_value eq = mrb_yield(mrb, blk, RARRAY_PTR(self)[i]); if (mrb_test(eq)) { return mrb_int_value(mrb, i); } } } return mrb_nil_value(); } /* * call-seq: * ary.rindex(val) -> int or nil * ary.rindex {|item| block } -> int or nil * array.rindex -> enumerator * * Returns the _index_ of the first object in +ary+ such that the object is * == to +obj+. * * If a block is given instead of an argument, returns the _index_ of the * first object for which the block returns +true+. Returns +nil+ if no * match is found. * * ISO 15.2.12.5.26 */ static mrb_value mrb_ary_rindex_m(mrb_state *mrb, mrb_value self) { mrb_value obj, blk; if (mrb_get_args(mrb, "|o&", &obj, &blk) == 0 && mrb_nil_p(blk)) { return mrb_funcall_id(mrb, self, MRB_SYM(to_enum), 1, mrb_symbol_value(MRB_SYM(rindex))); } for (mrb_int i = RARRAY_LEN(self) - 1; i >= 0; i--) { if (mrb_nil_p(blk)) { if (mrb_equal(mrb, RARRAY_PTR(self)[i], obj)) { return mrb_int_value(mrb, i); } } else { mrb_value eq = mrb_yield(mrb, blk, RARRAY_PTR(self)[i]); if (mrb_test(eq)) return mrb_int_value(mrb, i); } mrb_int len = RARRAY_LEN(self); if (i > len) { i = len; } } return mrb_nil_value(); } MRB_API mrb_value mrb_ary_splat(mrb_state *mrb, mrb_value v) { struct RArray *a; if (mrb_array_p(v)) { a = ary_dup(mrb, mrb_ary_ptr(v)); return mrb_obj_value(a); } if (!mrb_respond_to(mrb, v, MRB_SYM(to_a))) { return mrb_ary_new_from_values(mrb, 1, &v); } mrb_value ary = mrb_funcall_argv(mrb, v, MRB_SYM(to_a), 0, NULL); if (mrb_nil_p(ary)) { return mrb_ary_new_from_values(mrb, 1, &v); } mrb_ensure_array_type(mrb, ary); a = mrb_ary_ptr(ary); a = ary_dup(mrb, a); return mrb_obj_value(a); } static mrb_value mrb_ary_size(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); return mrb_int_value(mrb, ARY_LEN(a)); } MRB_API mrb_value mrb_ary_clear(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); ary_modify(mrb, a); if (ARY_SHARED_P(a)) { mrb_ary_decref(mrb, a->as.heap.aux.shared); ARY_UNSET_SHARED_FLAG(a); } else if (!ARY_EMBED_P(a)){ mrb_free(mrb, a->as.heap.ptr); } if (MRB_ARY_EMBED_LEN_MAX > 0) { ARY_SET_EMBED_LEN(a, 0); } else { a->as.heap.ptr = NULL; a->as.heap.aux.capa = 0; ARY_SET_LEN(a, 0); } return self; } static mrb_value mrb_ary_empty_p(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); return mrb_bool_value(ARY_LEN(a) == 0); } MRB_API mrb_value mrb_ary_entry(mrb_value ary, mrb_int n) { struct RArray *a = mrb_ary_ptr(ary); mrb_int len = ARY_LEN(a); /* range check */ if (n < 0) n += len; if (n < 0 || len <= n) return mrb_nil_value(); return ARY_PTR(a)[n]; } static mrb_value join_ary(mrb_state *mrb, mrb_value ary, mrb_value sep, mrb_value list) { /* check recursive */ for (mrb_int i=0; i 0 && !mrb_nil_p(sep)) { mrb_str_cat_str(mrb, result, sep); } mrb_value val = RARRAY_PTR(ary)[i]; switch (mrb_type(val)) { case MRB_TT_ARRAY: ary_join: val = join_ary(mrb, val, sep, list); /* fall through */ case MRB_TT_STRING: str_join: mrb_str_cat_str(mrb, result, val); break; default: if (!mrb_immediate_p(val)) { mrb_value tmp = mrb_check_string_type(mrb, val); if (!mrb_nil_p(tmp)) { val = tmp; goto str_join; } tmp = mrb_check_array_type(mrb, val); if (!mrb_nil_p(tmp)) { val = tmp; goto ary_join; } } val = mrb_obj_as_string(mrb, val); goto str_join; } } mrb_ary_pop(mrb, list); return result; } MRB_API mrb_value mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep) { if (!mrb_nil_p(sep)) { sep = mrb_obj_as_string(mrb, sep); } return join_ary(mrb, ary, sep, mrb_ary_new(mrb)); } /* * call-seq: * ary.join(sep="") -> str * * Returns a string created by converting each element of the array to * a string, separated by sep. * * [ "a", "b", "c" ].join #=> "abc" * [ "a", "b", "c" ].join("-") #=> "a-b-c" */ static mrb_value mrb_ary_join_m(mrb_state *mrb, mrb_value ary) { mrb_value sep = mrb_nil_value(); mrb_get_args(mrb, "|S!", &sep); return mrb_ary_join(mrb, ary, sep); } /* * call-seq: * ary.to_s -> string * ary.inspect -> string * * Return the contents of this array as a string. */ static mrb_value mrb_ary_to_s(mrb_state *mrb, mrb_value self) { mrb->c->ci->mid = MRB_SYM(inspect); mrb_value ret = mrb_str_new_lit(mrb, "["); int ai = mrb_gc_arena_save(mrb); if (mrb_inspect_recursive_p(mrb, self)) { mrb_str_cat_lit(mrb, ret, "...]"); return ret; } for (mrb_int i=0; i0) mrb_str_cat_lit(mrb, ret, ", "); mrb_str_cat_str(mrb, ret, mrb_inspect(mrb, RARRAY_PTR(self)[i])); mrb_gc_arena_restore(mrb, ai); } mrb_str_cat_lit(mrb, ret, "]"); return ret; } /* check array equality: 1=equal,0=not_equal,-1=need_elements_check */ static mrb_int ary_eq(mrb_state *mrb, mrb_value ary1, mrb_value ary2) { if (mrb_obj_equal(mrb, ary1, ary2)) return 1; if (!mrb_array_p(ary2)) return 0; if (RARRAY_LEN(ary1) != RARRAY_LEN(ary2)) return 0; return -1; } /* * call-seq: * array == other -> true or false * * Equality---Two arrays are equal if they contain the same number * of elements and if each element is equal to (according to * Object.==) the corresponding element in the other array. * */ static mrb_value mrb_ary_eq(mrb_state *mrb, mrb_value ary1) { mrb_value ary2 = mrb_get_arg1(mrb); mrb_int n = ary_eq(mrb, ary1, ary2); if (n == 1) return mrb_true_value(); if (n == 0) return mrb_false_value(); int ai = mrb_gc_arena_save(mrb); for (mrb_int i=0; i true or false * * Returns true if +self+ and _other_ are the same object, * or are both arrays with the same content. * */ static mrb_value mrb_ary_eql(mrb_state *mrb, mrb_value ary1) { mrb_value ary2 = mrb_get_arg1(mrb); mrb_int n = ary_eq(mrb, ary1, ary2); if (n == 1) return mrb_true_value(); if (n == 0) return mrb_false_value(); int ai = mrb_gc_arena_save(mrb); for (mrb_int i=0; i other_array -> -1, 0, or 1 * * Comparison---Returns an integer (-1, 0, or +1) * if this array is less than, equal to, or greater than other_ary. * Each object in each array is compared (using <=>). If any value isn't * equal, then that inequality is the return value. If all the * values found are equal, then the return is based on a * comparison of the array lengths. Thus, two arrays are * "equal" according to Array*<=> if and only if they have * the same length and the value of each element is equal to the * value of the corresponding element in the other array. */ static mrb_value mrb_ary_cmp(mrb_state *mrb, mrb_value ary1) { mrb_value ary2 = mrb_get_arg1(mrb); if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_fixnum_value(0); if (!mrb_array_p(ary2)) return mrb_nil_value(); for (mrb_int i=0; i 0) return mrb_fixnum_value(1); else return mrb_fixnum_value(-1); } /* internal method to convert multi-value to single value */ static mrb_value mrb_ary_svalue(mrb_state *mrb, mrb_value ary) { switch (RARRAY_LEN(ary)) { case 0: return mrb_nil_value(); case 1: return RARRAY_PTR(ary)[0]; default: return ary; } } /* * call-seq: * array.delete(obj) -> deleted_object * array.delete(obj) {|nosuch| ... } -> deleted_object or block_return * * Removes zero or more elements from self; returns self. * * When no block is given, removes from self each element e such * that e == obj; returns the last deleted element * * Returns nil if no elements removed. * * When a block is given, removes from self each element e such * that e == obj. If any such elements are found, ignores the block and * returns the last. Otherwise, returns the block's return value. */ static mrb_value mrb_ary_delete(mrb_state *mrb, mrb_value self) { mrb_value obj, blk; mrb_get_args(mrb, "o&", &obj, &blk); struct RArray *ary = RARRAY(self); mrb_value ret = obj; int ai = mrb_gc_arena_save(mrb); mrb_int i = 0; mrb_int j = 0; for (; i < ARY_LEN(ary); i++) { mrb_value elem = ARY_PTR(ary)[i]; if (mrb_equal(mrb, elem, obj)) { mrb_gc_arena_restore(mrb, ai); mrb_gc_protect(mrb, elem); ret = elem; continue; } if (i != j) { if (j >= ARY_LEN(ary)) { // Since breaking here will further change the array length, // there is no choice but to raise an exception or return. mrb_raise(mrb, E_RUNTIME_ERROR, "array modified during delete"); } ary_modify(mrb, ary); ARY_PTR(ary)[j] = elem; } j++; } if (i == j) { if (mrb_nil_p(blk)) return mrb_nil_value(); return mrb_yield(mrb, blk, obj); } ARY_SET_LEN(ary, j); return ret; } static mrb_noreturn void cmp_failed(mrb_state *mrb, mrb_int a, mrb_int b) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "comparison failed (element %d and %d)", a, b); } static mrb_bool sort_cmp(mrb_state *mrb, mrb_value ary, mrb_value *p, mrb_int a, mrb_int b, mrb_value blk) { mrb_int cmp; if (mrb_nil_p(blk)) { cmp = mrb_cmp(mrb, p[a], p[b]); if (cmp == -2) cmp_failed(mrb, a, b); } else { mrb_value args[2] = {p[a], p[b]}; mrb_value c = mrb_yield_argv(mrb, blk, 2, args); if (mrb_nil_p(c) || !mrb_fixnum_p(c)) { cmp_failed(mrb, a, b); } cmp = mrb_fixnum(c); } mrb_int size = RARRAY_LEN(ary); if (RARRAY_PTR(ary) != p || size < a || size < b) { mrb_raise(mrb, E_RUNTIME_ERROR, "array modified during sort"); } return cmp > 0; } static void heapify(mrb_state *mrb, mrb_value ary, mrb_value *a, mrb_int index, mrb_int size, mrb_value blk) { mrb_int max = index; mrb_int left_index = 2 * index + 1; mrb_int right_index = left_index + 1; if (left_index < size && sort_cmp(mrb, ary, a, left_index, max, blk)) { max = left_index; } if (right_index < size && sort_cmp(mrb, ary, a, right_index, max, blk)) { max = right_index; } if (max != index) { mrb_value tmp = a[max]; a[max] = a[index]; a[index] = tmp; heapify(mrb, ary, a, max, size, blk); } } /* * call-seq: * array.sort! -> self * array.sort! {|a, b| ... } -> self * * Sort all elements and replace +self+ with these * elements. */ static mrb_value mrb_ary_sort_bang(mrb_state *mrb, mrb_value ary) { mrb_value blk; mrb_int n = RARRAY_LEN(ary); if (n < 2) return ary; ary_modify(mrb, mrb_ary_ptr(ary)); mrb_get_args(mrb, "&", &blk); mrb_value *a = RARRAY_PTR(ary); for (mrb_int i = n / 2 - 1; i > -1; i--) { heapify(mrb, ary, a, i, n, blk); } for (mrb_int i = n - 1; i > 0; i--) { mrb_value tmp = a[0]; a[0] = a[i]; a[i] = tmp; heapify(mrb, ary, a, 0, i, blk); } return ary; } void mrb_init_array(mrb_state *mrb) { struct RClass *a; mrb->array_class = a = mrb_define_class_id(mrb, MRB_SYM(Array), mrb->object_class); /* 15.2.12 */ MRB_SET_INSTANCE_TT(a, MRB_TT_ARRAY); mrb_define_class_method_id(mrb, a, MRB_OPSYM(aref), mrb_ary_s_create, MRB_ARGS_ANY()); /* 15.2.12.4.1 */ mrb_define_method_id(mrb, a, MRB_OPSYM(add), mrb_ary_plus, MRB_ARGS_REQ(1)); /* 15.2.12.5.1 */ mrb_define_method_id(mrb, a, MRB_OPSYM(mul), mrb_ary_times, MRB_ARGS_REQ(1)); /* 15.2.12.5.2 */ mrb_define_method_id(mrb, a, MRB_OPSYM(lshift), mrb_ary_push_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.3 */ mrb_define_method_id(mrb, a, MRB_OPSYM(aref), mrb_ary_aget, MRB_ARGS_ARG(1,1)); /* 15.2.12.5.4 */ mrb_define_method_id(mrb, a, MRB_OPSYM(aset), mrb_ary_aset, MRB_ARGS_ARG(2,1)); /* 15.2.12.5.5 */ mrb_define_method_id(mrb, a, MRB_SYM(clear), mrb_ary_clear, MRB_ARGS_NONE()); /* 15.2.12.5.6 */ mrb_define_method_id(mrb, a, MRB_OPSYM(cmp), mrb_ary_cmp, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, a, MRB_SYM(concat), mrb_ary_concat_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.8 */ mrb_define_method_id(mrb, a, MRB_SYM(delete), mrb_ary_delete, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, a, MRB_SYM(delete_at), mrb_ary_delete_at, MRB_ARGS_REQ(1)); /* 15.2.12.5.9 */ mrb_define_method_id(mrb, a, MRB_SYM_Q(empty), mrb_ary_empty_p, MRB_ARGS_NONE()); /* 15.2.12.5.12 */ mrb_define_method_id(mrb, a, MRB_OPSYM(eq), mrb_ary_eq, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, a, MRB_SYM_Q(eql), mrb_ary_eql, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, a, MRB_SYM(first), mrb_ary_first, MRB_ARGS_OPT(1)); /* 15.2.12.5.13 */ mrb_define_method_id(mrb, a, MRB_SYM(index), mrb_ary_index_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.14 */ mrb_define_method_id(mrb, a, MRB_SYM(initialize), mrb_ary_init, MRB_ARGS_OPT(2)); /* 15.2.12.5.15 */ mrb_define_method_id(mrb, a, MRB_SYM(initialize_copy), mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.16 */ mrb_define_method_id(mrb, a, MRB_SYM(join), mrb_ary_join_m, MRB_ARGS_OPT(1)); /* 15.2.12.5.17 */ mrb_define_method_id(mrb, a, MRB_SYM(last), mrb_ary_last, MRB_ARGS_OPT(1)); /* 15.2.12.5.18 */ mrb_define_method_id(mrb, a, MRB_SYM(length), mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.19 */ mrb_define_method_id(mrb, a, MRB_SYM(pop), mrb_ary_pop, MRB_ARGS_NONE()); /* 15.2.12.5.21 */ mrb_define_method_id(mrb, a, MRB_SYM(push), mrb_ary_push_m, MRB_ARGS_ANY()); /* 15.2.12.5.22 */ mrb_define_method_id(mrb, a, MRB_SYM(replace), mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.23 */ mrb_define_method_id(mrb, a, MRB_SYM(reverse), mrb_ary_reverse, MRB_ARGS_NONE()); /* 15.2.12.5.24 */ mrb_define_method_id(mrb, a, MRB_SYM_B(reverse), mrb_ary_reverse_bang, MRB_ARGS_NONE()); /* 15.2.12.5.25 */ mrb_define_method_id(mrb, a, MRB_SYM(rindex), mrb_ary_rindex_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.26 */ mrb_define_method_id(mrb, a, MRB_SYM(shift), mrb_ary_shift_m, MRB_ARGS_OPT(1)); /* 15.2.12.5.27 */ mrb_define_method_id(mrb, a, MRB_SYM(size), mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.28 */ mrb_define_method_id(mrb, a, MRB_SYM(slice), mrb_ary_aget, MRB_ARGS_ARG(1,1)); /* 15.2.12.5.29 */ mrb_define_method_id(mrb, a, MRB_SYM(unshift), mrb_ary_unshift_m, MRB_ARGS_ANY()); /* 15.2.12.5.30 */ mrb_define_method_id(mrb, a, MRB_SYM(to_s), mrb_ary_to_s, MRB_ARGS_NONE()); mrb_define_method_id(mrb, a, MRB_SYM(inspect), mrb_ary_to_s, MRB_ARGS_NONE()); mrb_define_method_id(mrb, a, MRB_SYM_B(sort), mrb_ary_sort_bang, MRB_ARGS_NONE()); mrb_define_method_id(mrb, a, MRB_SYM(__svalue), mrb_ary_svalue, MRB_ARGS_NONE()); } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/error.c0000644000000000000000000000013215077107276017757 xustar0030 mtime=1761382078.133420506 30 atime=1761382080.154411261 30 ctime=1761382108.563301933 nghttp2-1.68.0/third-party/mruby/src/error.c0000644000175100017510000004772015077107276020361 0ustar00runnerrunner/* ** error.c - Exception class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include #include #include #include void mrb_exc_mesg_set(mrb_state *mrb, struct RException *exc, mrb_value mesg) { if (!mrb_string_p(mesg)) { mesg = mrb_obj_as_string(mrb, mesg); } exc->mesg = mrb_basic_ptr(mesg); mrb_field_write_barrier_value(mrb, (struct RBasic*)exc, mesg); } mrb_value mrb_exc_mesg_get(mrb_state *mrb, struct RException *exc) { if (exc->mesg == NULL) return mrb_nil_value(); return mrb_obj_value(exc->mesg); } MRB_API mrb_value mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str) { mrb_ensure_string_type(mrb, str); struct RException *e = MRB_OBJ_ALLOC(mrb, MRB_TT_EXCEPTION, c); mrb_value exc = mrb_obj_value(e); mrb_exc_mesg_set(mrb, e, str); return exc; } MRB_API mrb_value mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, mrb_int len) { return mrb_exc_new_str(mrb, c, mrb_str_new(mrb, ptr, len)); } /* * call-seq: * Exception.new(msg = nil) -> exception * * Construct a new Exception object, optionally passing in * a message. */ static mrb_value exc_initialize(mrb_state *mrb, mrb_value exc) { mrb_value mesg; if (mrb_get_args(mrb, "|o", &mesg) == 1) { mrb_exc_mesg_set(mrb, mrb_exc_ptr(exc), mesg); } return exc; } /* * Document-method: exception * * call-seq: * exc.exception(string) -> an_exception or exc * * With no argument, or if the argument is the same as the receiver, * return the receiver. Otherwise, create a new * exception object of the same class as the receiver, but with a * message equal to string. * */ static mrb_value exc_exception(mrb_state *mrb, mrb_value self) { mrb_value a; mrb_int argc = mrb_get_args(mrb, "|o", &a); if (argc == 0) return self; if (mrb_obj_equal(mrb, self, a)) return self; mrb_value exc = mrb_obj_clone(mrb, self); mrb_exc_mesg_set(mrb, mrb_exc_ptr(exc), a); return exc; } /* * call-seq: * exception.to_s -> string * * Returns exception's message (or the name of the exception if * no message is set). */ static mrb_value exc_to_s(mrb_state *mrb, mrb_value exc) { mrb_value mesg = mrb_exc_mesg_get(mrb, mrb_exc_ptr(exc)); if (!mrb_string_p(mesg)) { return mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, exc)); } struct RObject *p = mrb_obj_ptr(mesg); if (!p->c) { p->c = mrb->string_class; } return mesg; } /* * call-seq: * exception.inspect -> string * * Returns this exception's filename, line number, * message and class name. * If filename or line number is not set, * returns message and class name. */ mrb_value mrb_exc_inspect(mrb_state *mrb, mrb_value exc) { mrb_value cname = mrb_mod_to_s(mrb, mrb_obj_value(mrb_obj_class(mrb, exc))); mrb_value mesg = mrb_exc_mesg_get(mrb, mrb_exc_ptr(exc)); /* string or nil */ return (mrb_nil_p(mesg)||RSTRING_LEN(mesg)==0) ? cname : mrb_format(mrb, "#<%v: %v>", cname, mesg); } mrb_value mrb_exc_get_output(mrb_state *mrb, struct RObject *exc) { mrb_value cname = mrb_mod_to_s(mrb, mrb_obj_value(mrb_class_real(exc->c))); mrb_value mesg = mrb_exc_mesg_get(mrb, (struct RException*)exc); /* string or nil */ return (mrb_nil_p(mesg)||RSTRING_LEN(mesg)==0) ? cname : mrb_format(mrb, "%v (%v)", mesg, cname); } void mrb_keep_backtrace(mrb_state *mrb, mrb_value exc); static void set_backtrace(mrb_state *mrb, mrb_value exc, mrb_value backtrace) { if (!mrb_array_p(backtrace)) { type_err: mrb_raise(mrb, E_TYPE_ERROR, "backtrace must be Array of String"); } else { const mrb_value *p = RARRAY_PTR(backtrace); const mrb_value *pend = p + RARRAY_LEN(backtrace); while (p < pend) { if (!mrb_string_p(*p)) goto type_err; p++; } } mrb_exc_ptr(exc)->backtrace = mrb_basic_ptr(backtrace); mrb_field_write_barrier_value(mrb, mrb_basic_ptr(exc), backtrace); } static mrb_value exc_set_backtrace(mrb_state *mrb, mrb_value exc) { mrb_value backtrace = mrb_get_arg1(mrb); set_backtrace(mrb, exc, backtrace); return backtrace; } void mrb_exc_set(mrb_state *mrb, mrb_value exc) { if (mrb_nil_p(exc)) { mrb->exc = 0; } else { mrb->exc = mrb_obj_ptr(exc); if (mrb->gc.arena_idx > 0 && (struct RBasic*)mrb->exc == mrb->gc.arena[mrb->gc.arena_idx-1]) { mrb->gc.arena_idx--; } if (!mrb->gc.out_of_memory && !mrb_frozen_p(mrb->exc)) { mrb_keep_backtrace(mrb, exc); } } } static mrb_noreturn void exc_throw(mrb_state *mrb, mrb_value exc) { if (!mrb->jmp) { mrb_print_error(mrb); abort(); } MRB_THROW(mrb->jmp); } MRB_API mrb_noreturn void mrb_exc_raise(mrb_state *mrb, mrb_value exc) { if (mrb_break_p(exc)) { mrb->exc = mrb_obj_ptr(exc); } else { if (mrb_type(exc) != MRB_TT_EXCEPTION) { mrb_raise(mrb, E_TYPE_ERROR, "exception object expected"); } mrb_exc_set(mrb, exc); } exc_throw(mrb, exc); } MRB_API mrb_noreturn void mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg) { mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mrb_str_new_cstr(mrb, msg))); } /* * vsprintf like formatting. * * The syntax of a format sequence is as follows. * * %[modifier]specifier * * The modifiers are: * * ----------+------------------------------------------------------------ * Modifier | Meaning * ----------+------------------------------------------------------------ * ! | Convert to string by corresponding `inspect` instead of * | corresponding `to_s`. * ----------+------------------------------------------------------------ * * The specifiers are: * * ----------+----------------+-------------------------------------------- * Specifier | Argument Type | Note * ----------+----------------+-------------------------------------------- * c | char | * d | int | * f | mrb_float | * i | mrb_int | * l | char*, size_t | Arguments are string and length. * n | mrb_sym | * s | char* | Argument is NUL terminated string. * t | mrb_value | Convert to type (class) of object. * v,S | mrb_value | * C | struct RClass* | * T | mrb_value | Convert to real type (class) of object. * Y | mrb_value | Same as `!v` if argument is `true`, `false` * | | or `nil`, otherwise same as `T`. * % | - | Convert to percent sign itself (no argument * | | taken). * ----------+----------------+-------------------------------------------- */ MRB_API mrb_value mrb_vformat(mrb_state *mrb, const char *format, va_list ap) { const char *chars, *p = format, *b = format, *e; char ch; size_t len; mrb_int i; struct RClass *cls; mrb_bool inspect = FALSE; mrb_value result = mrb_str_new_capa(mrb, 128), obj, str; int ai = mrb_gc_arena_save(mrb); while (*p) { const char c = *p++; e = p; if (c == '%') { if (*p == '!') { inspect = TRUE; p++; } if (!*p) break; switch (*p) { case 'c': ch = (char)va_arg(ap, int); chars = &ch; len = 1; goto L_cat; case 'd': case 'i': #if MRB_INT_MAX < INT_MAX i = (mrb_int)va_arg(ap, int); #else i = *p == 'd' ? (mrb_int)va_arg(ap, int) : va_arg(ap, mrb_int); #endif obj = mrb_int_value(mrb, i); goto L_cat_obj; #ifndef MRB_NO_FLOAT case 'f': obj = mrb_float_value(mrb, (mrb_float)va_arg(ap, double)); goto L_cat_obj; #endif case 'l': chars = va_arg(ap, char*); len = va_arg(ap, size_t); L_cat: if (inspect) { obj = mrb_str_new(mrb, chars, len); goto L_cat_obj; } L_cat_plain: mrb_str_cat(mrb, result, b, e - b - 1); mrb_str_cat(mrb, result, chars, len); b = ++p; mrb_gc_arena_restore(mrb, ai); break; case 'n': #if UINT32_MAX < INT_MAX obj = mrb_symbol_value((mrb_sym)va_arg(ap, int)); #else obj = mrb_symbol_value(va_arg(ap, mrb_sym)); #endif goto L_cat_obj; case 's': chars = va_arg(ap, char*); len = strlen(chars); goto L_cat; case 't': cls = mrb_class(mrb, va_arg(ap, mrb_value)); goto L_cat_class; case 'v': case 'S': obj = va_arg(ap, mrb_value); L_cat_obj: str = (inspect ? mrb_inspect : mrb_obj_as_string)(mrb, obj); if (mrb_type(str) != MRB_TT_STRING) { chars = "void (no string conversion)"; len = strlen(chars); } else { chars = RSTRING_PTR(str); len = RSTRING_LEN(str); } goto L_cat_plain; case 'C': cls = va_arg(ap, struct RClass*); L_cat_class: obj = mrb_obj_value(cls); goto L_cat_obj; case 'T': obj = va_arg(ap, mrb_value); L_cat_real_class_of: cls = mrb_obj_class(mrb, obj); goto L_cat_class; case 'Y': obj = va_arg(ap, mrb_value); if (!mrb_test(obj) || mrb_true_p(obj)) { inspect = TRUE; goto L_cat_obj; } else { goto L_cat_real_class_of; } case '%': L_cat_current: chars = p; len = 1; goto L_cat_plain; default: mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed format string - %%%c", *p); } } else if (c == '\\') { if (!*p) break; goto L_cat_current; } } mrb_str_cat(mrb, result, b, p - b); return result; } MRB_API mrb_value mrb_format(mrb_state *mrb, const char *format, ...) { va_list ap; va_start(ap, format); mrb_value str = mrb_vformat(mrb, format, ap); va_end(ap); return str; } static mrb_value error_va(mrb_state *mrb, struct RClass *c, const char *fmt, va_list ap) { return mrb_exc_new_str(mrb, c, mrb_vformat(mrb, fmt, ap)); } MRB_API mrb_noreturn void mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...) { va_list ap; va_start(ap, fmt); mrb_value exc = error_va(mrb, c, fmt, ap); va_end(ap); mrb_exc_raise(mrb, exc); } MRB_API mrb_noreturn void mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...) { va_list ap; va_start(ap, fmt); mrb_value exc = error_va(mrb, E_NAME_ERROR, fmt, ap); va_end(ap); mrb_iv_set(mrb, exc, MRB_IVSYM(name), mrb_symbol_value(id)); mrb_exc_raise(mrb, exc); } MRB_API void mrb_warn(mrb_state *mrb, const char *fmt, ...) { #ifndef MRB_NO_STDIO va_list ap; va_start(ap, fmt); mrb_value str = mrb_vformat(mrb, fmt, ap); fputs("warning: ", stderr); fwrite(RSTRING_PTR(str), RSTRING_LEN(str), 1, stderr); putc('\n', stderr); va_end(ap); #endif } MRB_API mrb_noreturn void mrb_bug(mrb_state *mrb, const char *mesg) { #ifndef MRB_NO_STDIO fputs("bug: ", stderr); fputs(mesg, stderr); fputs("\n", stderr); #endif exit(EXIT_FAILURE); } mrb_value mrb_make_exception(mrb_state *mrb, mrb_value exc, mrb_value mesg) { mrb_int n = 1; if (mrb_nil_p(mesg)) { n = 0; } if (mrb_class_p(exc)) { exc = mrb_funcall_argv(mrb, exc, MRB_SYM(new), n, &mesg); } else if (mrb_exception_p(exc)) { if (n > 0) { exc = mrb_obj_clone(mrb, exc); mrb_exc_mesg_set(mrb, mrb_exc_ptr(exc), mesg); } } else { mrb_raise(mrb, E_TYPE_ERROR, "exception class/object expected"); } if (mrb_type(exc) != MRB_TT_EXCEPTION) { mrb_raise(mrb, E_EXCEPTION, "exception object expected"); } return exc; } MRB_API mrb_noreturn void mrb_sys_fail(mrb_state *mrb, const char *mesg) { if (mrb_class_defined_id(mrb, MRB_SYM(SystemCallError))) { struct RClass *sce = mrb_class_get_id(mrb, MRB_SYM(SystemCallError)); mrb_int no = (mrb_int)errno; if (mesg != NULL) { mrb_funcall_id(mrb, mrb_obj_value(sce), MRB_SYM(_sys_fail), 2, mrb_fixnum_value(no), mrb_str_new_cstr(mrb, mesg)); } else { mrb_funcall_id(mrb, mrb_obj_value(sce), MRB_SYM(_sys_fail), 1, mrb_fixnum_value(no)); } } mrb_raise(mrb, E_RUNTIME_ERROR, mesg); } MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, char const* fmt, ...) { va_list ap; va_start(ap, fmt); mrb_value exc = error_va(mrb, E_NOMETHOD_ERROR, fmt, ap); va_end(ap); mrb_iv_set(mrb, exc, MRB_IVSYM(name), mrb_symbol_value(id)); mrb_iv_set(mrb, exc, MRB_IVSYM(args), args); mrb_exc_raise(mrb, exc); } static mrb_noreturn void frozen_error(mrb_state *mrb, mrb_value v) { mrb_raisef(mrb, E_FROZEN_ERROR, "can't modify frozen %T", v); } MRB_API mrb_noreturn void mrb_frozen_error(mrb_state *mrb, void *frozen_obj) { frozen_error(mrb, mrb_obj_value(frozen_obj)); } MRB_API void mrb_check_frozen(mrb_state *mrb, void *o) { if (mrb_frozen_p((struct RBasic*)o)) { mrb_frozen_error(mrb, o); } } MRB_API void mrb_check_frozen_value(mrb_state *mrb, mrb_value v) { if (mrb_immediate_p(v) || mrb_frozen_p(mrb_basic_ptr(v))) { frozen_error(mrb, v); } } MRB_API mrb_noreturn void mrb_argnum_error(mrb_state *mrb, mrb_int argc, int min, int max) { #define FMT(exp) "wrong number of arguments (given %i, expected " exp ")" if (min == max) mrb_raisef(mrb, E_ARGUMENT_ERROR, FMT("%d"), argc, min); else if (max < 0) mrb_raisef(mrb, E_ARGUMENT_ERROR, FMT("%d+"), argc, min); else mrb_raisef(mrb, E_ARGUMENT_ERROR, FMT("%d..%d"), argc, min, max); #undef FMT } void mrb_core_init_printabort(mrb_state *mrb); int mrb_core_init_protect(mrb_state *mrb, void (*body)(mrb_state*, void*), void *opaque) { struct mrb_jmpbuf *prev_jmp = mrb->jmp; struct mrb_jmpbuf c_jmp; volatile int err = 1; MRB_TRY(&c_jmp) { mrb->jmp = &c_jmp; body(mrb, opaque); err = 0; } MRB_CATCH(&c_jmp) { if (mrb->exc) { mrb_print_error(mrb); mrb->exc = NULL; } else { mrb_core_init_printabort(mrb); } } MRB_END_EXC(&c_jmp); mrb->jmp = prev_jmp; return err; } mrb_noreturn void mrb_core_init_abort(mrb_state *mrb) { mrb->exc = NULL; exc_throw(mrb, mrb_nil_value()); } void mrb_protect_atexit(mrb_state *mrb) { if (mrb->atexit_stack_len > 0) { if (mrb->c && mrb->c->ci) { // Even if the call stack is incomplete due to some fault, atexit to be executed at the top level is desirable. // Clean-up also makes it easier to collect unnecessary objects. mrb_callinfo zero = { 0 }; struct mrb_context *c = mrb->c = mrb->root_c; mrb_gc_arena_restore(mrb, 0); if (c->ci == c->cibase) { // Since there is no problem with the ci, the env object is detached normally. struct REnv *e = mrb_vm_ci_env(c->ci); *c->ci = zero; c->ci->stack = c->stbase; if (e) { c->ci->u.env = NULL; mrb_env_unshare(mrb, e, TRUE); } } else { // Any env objects on the ci that are in the process of being executed are destroyed. do { struct REnv *e = mrb_vm_ci_env(c->ci); if (e) { e->stack = NULL; MRB_ENV_SET_LEN(e, 0); MRB_ENV_SET_BIDX(e, 0); MRB_ENV_CLOSE(e); } } while (c->ci-- > c->cibase); c->ci = c->cibase; *c->ci = zero; c->ci->stack = c->stbase; } } struct mrb_jmpbuf *prev_jmp = mrb->jmp; struct mrb_jmpbuf c_jmp; int i = mrb->atexit_stack_len; while (i > 0) { MRB_TRY(&c_jmp) { mrb->jmp = &c_jmp; do { mrb->atexit_stack[--i](mrb); mrb_gc_arena_restore(mrb, 0); } while (i > 0); mrb->jmp = prev_jmp; } MRB_CATCH(&c_jmp) { mrb->jmp = prev_jmp; /* ignore atexit errors */ mrb_gc_arena_restore(mrb, 0); } MRB_END_EXC(&c_jmp); } #ifndef MRB_FIXED_STATE_ATEXIT_STACK mrb_free(mrb, mrb->atexit_stack); #endif } } mrb_noreturn void mrb_raise_nomemory(mrb_state *mrb) { if (mrb->nomem_err) { mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err)); } else { mrb_core_init_abort(mrb); } } MRB_API void mrb_print_error(mrb_state *mrb) { #ifndef MRB_NO_STDIO if (mrb->jmp == NULL) { struct mrb_jmpbuf c_jmp; MRB_TRY(&c_jmp) { mrb->jmp = &c_jmp; mrb_print_backtrace(mrb); } MRB_CATCH(&c_jmp) { /* ignore exception during print_backtrace() */ } MRB_END_EXC(&c_jmp); mrb->jmp = NULL; } else { mrb_print_backtrace(mrb); } #endif } /* clear error status in the mrb_state structure */ MRB_API void mrb_clear_error(mrb_state *mrb) { mrb->exc = NULL; } /* returns TRUE if error in the previous call; internally calls mrb_clear_error() */ MRB_API mrb_bool mrb_check_error(mrb_state *mrb) { if (mrb->exc) { mrb_clear_error(mrb); return TRUE; } return FALSE; } void mrb_init_exception(mrb_state *mrb) { struct RClass *exception = mrb->eException_class = mrb_define_class_id(mrb, MRB_SYM(Exception), mrb->object_class); /* 15.2.22 */ MRB_SET_INSTANCE_TT(exception, MRB_TT_EXCEPTION); mrb_define_class_method_id(mrb, exception, MRB_SYM(exception), mrb_instance_new, MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, exception, MRB_SYM(exception), exc_exception, MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, exception, MRB_SYM(initialize), exc_initialize, MRB_ARGS_OPT(1)); mrb_define_method_id(mrb, exception, MRB_SYM(to_s), exc_to_s, MRB_ARGS_NONE()); mrb_define_method_id(mrb, exception, MRB_SYM(message), exc_to_s, MRB_ARGS_NONE()); mrb_define_method_id(mrb, exception, MRB_SYM(inspect), mrb_exc_inspect, MRB_ARGS_NONE()); mrb_define_method_id(mrb, exception, MRB_SYM(backtrace), mrb_exc_backtrace, MRB_ARGS_NONE()); mrb_define_method_id(mrb, exception, MRB_SYM(set_backtrace), exc_set_backtrace, MRB_ARGS_REQ(1)); mrb->eStandardError_class = mrb_define_class_id(mrb, MRB_SYM(StandardError), mrb->eException_class); /* 15.2.23 */ mrb_define_class_id(mrb, MRB_SYM(ArgumentError), E_STANDARD_ERROR); /* 15.2.24 */ mrb_define_class_id(mrb, MRB_SYM(LocalJumpError), E_STANDARD_ERROR); /* 15.2.25 */ struct RClass *range_error = mrb_define_class_id(mrb, MRB_SYM(RangeError), E_STANDARD_ERROR); /* 15.2.26 */ mrb_define_class_id(mrb, MRB_SYM(FloatDomainError), range_error); mrb_define_class_id(mrb, MRB_SYM(RegexpError), E_STANDARD_ERROR); /* 15.2.27 */ struct RClass *runtime_error = mrb_define_class_id(mrb, MRB_SYM(RuntimeError), E_STANDARD_ERROR); /* 15.2.28 */ mrb_define_class_id(mrb, MRB_SYM(FrozenError), runtime_error); mrb_define_class_id(mrb, MRB_SYM(TypeError), E_STANDARD_ERROR); /* 15.2.29 */ mrb_define_class_id(mrb, MRB_SYM(ZeroDivisionError), E_STANDARD_ERROR); /* 15.2.30 */ struct RClass *script_error = mrb_define_class_id(mrb, MRB_SYM(ScriptError), exception); /* 15.2.37 */ mrb_define_class_id(mrb, MRB_SYM(NotImplementedError), script_error); mrb_define_class_id(mrb, MRB_SYM(SyntaxError), script_error); /* 15.2.38 */ struct RClass *index_error = mrb_define_class_id(mrb, MRB_SYM(IndexError), E_STANDARD_ERROR); /* 15.2.33 */ mrb_define_class_id(mrb, MRB_SYM(KeyError), index_error); struct RClass *stack_error = mrb_define_class_id(mrb, MRB_SYM(SystemStackError), exception); mrb->stack_err = mrb_obj_ptr(mrb_exc_new_lit(mrb, stack_error, "stack level too deep")); struct RClass *nomem_error = mrb_define_class_id(mrb, MRB_SYM(NoMemoryError), exception); mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_lit(mrb, nomem_error, "Out of memory")); #ifdef MRB_GC_FIXED_ARENA mrb->arena_err = mrb_obj_ptr(mrb_exc_new_lit(mrb, nomem_error, "arena overflow error")); #endif } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/debug.c0000644000000000000000000000013115077107276017713 xustar0030 mtime=1761382078.133420506 30 atime=1761382080.153411265 29 ctime=1761382108.58530187 nghttp2-1.68.0/third-party/mruby/src/debug.c0000644000175100017510000001330015077107276020301 0ustar00runnerrunner#include #include #include #include static mrb_irep_debug_info_file* get_file(mrb_irep_debug_info *info, uint32_t pc) { if (pc >= info->pc_count) { return NULL; } /* get upper bound */ mrb_irep_debug_info_file **ret = info->files; int32_t count = info->flen; while (count > 0) { int32_t step = count / 2; mrb_irep_debug_info_file **it = ret + step; if (!(pc < (*it)->start_pos)) { ret = it + 1; count -= step + 1; } else { count = step; } } --ret; /* check returning file exists inside debug info */ mrb_assert(info->files <= ret && ret < (info->files + info->flen)); /* check pc is within the range of returning file */ mrb_assert((*ret)->start_pos <= pc && pc < (((ret + 1 - info->files) < info->flen) ? (*(ret+1))->start_pos : info->pc_count)); return *ret; } size_t mrb_packed_int_len(uint32_t num) { size_t llen = 0; do { llen++; } while (num >>= 7); return llen; } size_t mrb_packed_int_encode(uint32_t num, uint8_t *p) { size_t llen = 0; do { uint8_t byte = num & 0x7f; num >>= 7; if (num != 0) byte |= 0x80; *p++ = byte; llen++; } while (num != 0); return llen; } uint32_t mrb_packed_int_decode(const uint8_t *p, const uint8_t **newpos) { size_t i = 0, shift = 0; uint32_t n = 0; do { n |= ((uint32_t)(p[i] & 0x7f)) << shift; i++; shift += 7; } while (shift < sizeof(uint32_t) * 8 && (p[i - 1] & 0x80)); if (newpos) *newpos = p + i; return n; } static char const* debug_get_filename(mrb_state *mrb, mrb_irep_debug_info_file* f) { if (f == NULL) return NULL; return mrb_sym_name_len(mrb, f->filename_sym, NULL); } static int32_t debug_get_line(mrb_state *mrb, mrb_irep_debug_info_file* f, uint32_t pc) { if (f == NULL) return -1; switch (f->line_type) { case mrb_debug_line_ary: case mrb_debug_line_flat_map: default: break; case mrb_debug_line_packed_map: { const uint8_t *p = f->lines.packed_map; const uint8_t *pend = p + f->line_entry_count; uint32_t pos = 0, line = 0; while (p < pend) { pos += mrb_packed_int_decode(p, &p); uint32_t line_diff = mrb_packed_int_decode(p, &p); if (pc < pos) break; line += line_diff; } return line; } } return -1; } MRB_API char const* mrb_debug_get_filename(mrb_state *mrb, const mrb_irep *irep, uint32_t pc) { if (irep && pc < irep->ilen) { if (!irep->debug_info) return NULL; return debug_get_filename(mrb, get_file(irep->debug_info, pc)); } return NULL; } MRB_API int32_t mrb_debug_get_line(mrb_state *mrb, const mrb_irep *irep, uint32_t pc) { if (irep && pc < irep->ilen) { if (!irep->debug_info) return -1; return debug_get_line(mrb, get_file(irep->debug_info, pc), pc); } return -1; } MRB_API mrb_bool mrb_debug_get_position(mrb_state *mrb, const mrb_irep *irep, uint32_t pc, int32_t *lp, const char **fp) { if (irep && pc < irep->ilen && irep->debug_info) { mrb_irep_debug_info_file *f = get_file(irep->debug_info, pc); *lp = debug_get_line(mrb, f, pc); if (*lp > 0) { *fp = debug_get_filename(mrb, f); if (*fp) return TRUE; } } *lp = -1; *fp = NULL; return FALSE; } MRB_API mrb_irep_debug_info* mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep) { static const mrb_irep_debug_info initial = { 0, 0, NULL }; mrb_assert(!irep->debug_info); mrb_irep_debug_info *ret = (mrb_irep_debug_info*)mrb_malloc(mrb, sizeof(*ret)); *ret = initial; irep->debug_info = ret; return ret; } MRB_API mrb_irep_debug_info_file* mrb_debug_info_append_file(mrb_state *mrb, mrb_irep_debug_info *d, const char *filename, uint16_t *lines, uint32_t start_pos, uint32_t end_pos) { if (!d) return NULL; if (start_pos == end_pos) return NULL; mrb_assert(filename); mrb_assert(lines); if (d->flen > 0) { const char *fn = mrb_sym_name_len(mrb, d->files[d->flen - 1]->filename_sym, NULL); if (strcmp(filename, fn) == 0) return NULL; } mrb_irep_debug_info_file *f = (mrb_irep_debug_info_file*)mrb_malloc(mrb, sizeof(*f)); d->files = (mrb_irep_debug_info_file**)mrb_realloc(mrb, d->files, sizeof(mrb_irep_debug_info_file*) * (d->flen + 1)); d->files[d->flen++] = f; uint32_t file_pc_count = end_pos - start_pos; f->start_pos = start_pos; d->pc_count = end_pos; size_t fn_len = strlen(filename); f->filename_sym = mrb_intern(mrb, filename, fn_len); f->line_type = mrb_debug_line_packed_map; f->lines.ptr = NULL; uint16_t prev_line = 0; uint32_t prev_pc = 0; size_t packed_size = 0; uint8_t *p; for (uint32_t i = 0; i < file_pc_count; i++) { if (lines[start_pos + i] == prev_line) continue; packed_size += mrb_packed_int_len(start_pos+i-prev_pc); prev_pc = start_pos+i; packed_size += mrb_packed_int_len(lines[start_pos+i]-prev_line); prev_line = lines[start_pos + i]; } f->lines.packed_map = p = (uint8_t*)mrb_malloc(mrb, packed_size); prev_line = 0; prev_pc = 0; for (uint32_t i = 0; i < file_pc_count; i++) { if (lines[start_pos + i] == prev_line) continue; p += mrb_packed_int_encode(start_pos+i-prev_pc, p); prev_pc = start_pos + i; p += mrb_packed_int_encode(lines[start_pos + i]-prev_line, p); prev_line = lines[start_pos + i]; } f->line_entry_count = (uint32_t)packed_size; return f; } MRB_API void mrb_debug_info_free(mrb_state *mrb, mrb_irep_debug_info *d) { if (!d) { return; } if (d->files) { for (uint32_t i = 0; i < d->flen; i++) { if (d->files[i]) { mrb_free(mrb, d->files[i]->lines.ptr); mrb_free(mrb, d->files[i]); } } mrb_free(mrb, d->files); } mrb_free(mrb, d); } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/etc.c0000644000000000000000000000013215077107276017401 xustar0030 mtime=1761382078.133420506 30 atime=1761382080.154411261 30 ctime=1761382108.554301959 nghttp2-1.68.0/third-party/mruby/src/etc.c0000644000175100017510000001234315077107276017774 0ustar00runnerrunner/* ** etc.c ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include MRB_API struct RData* mrb_data_object_alloc(mrb_state *mrb, struct RClass *klass, void *ptr, const mrb_data_type *type) { struct RData *data = MRB_OBJ_ALLOC(mrb, MRB_TT_CDATA, klass); data->data = ptr; data->type = type; return data; } MRB_API void mrb_data_check_type(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) { if (!mrb_data_p(obj)) { mrb_check_type(mrb, obj, MRB_TT_CDATA); } if (DATA_TYPE(obj) != type) { const mrb_data_type *t2 = DATA_TYPE(obj); if (t2) { mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %s (expected %s)", t2->struct_name, type->struct_name); } else { mrb_raisef(mrb, E_TYPE_ERROR, "uninitialized %t (expected %s)", obj, type->struct_name); } } } MRB_API void* mrb_data_check_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) { if (!mrb_data_p(obj)) { return NULL; } if (DATA_TYPE(obj) != type) { return NULL; } return DATA_PTR(obj); } MRB_API void* mrb_data_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) { mrb_data_check_type(mrb, obj, type); return DATA_PTR(obj); } MRB_API mrb_sym mrb_obj_to_sym(mrb_state *mrb, mrb_value name) { if (mrb_symbol_p(name)) return mrb_symbol(name); if (mrb_string_p(name)) return mrb_intern_str(mrb, name); mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a symbol nor a string", name); return 0; /* not reached */ } #if !defined(MRB_NO_FLOAT) && !defined(MRB_NAN_BOXING) static mrb_int mrb_float_id(mrb_float f) { /* normalize -0.0 to 0.0 */ if (f == 0) f = 0.0; return (mrb_int)mrb_byte_hash((uint8_t*)&f, sizeof(f)); } #endif MRB_API mrb_int mrb_obj_id(mrb_value obj) { #if defined(MRB_NAN_BOXING) #ifdef MRB_INT64 return obj.u; #else uint64_t u = obj.u; return (mrb_int)(u>>32)^u; #endif #elif defined(MRB_WORD_BOXING) if (!mrb_immediate_p(obj)) { if (mrb_integer_p(obj)) return mrb_integer(obj); #ifndef MRB_NO_FLOAT if (mrb_float_p(obj)) { return mrb_float_id(mrb_float(obj)); } #endif } return (mrb_int)obj.w; #else /* MRB_NO_BOXING */ #define MakeID(p,t) (mrb_int)(((intptr_t)(p))^(t)) enum mrb_vtype tt = mrb_type(obj); switch (tt) { case MRB_TT_FREE: case MRB_TT_UNDEF: return MakeID(0, tt); /* should not happen */ case MRB_TT_FALSE: if (mrb_nil_p(obj)) return MakeID(4, tt); else return MakeID(0, tt); case MRB_TT_TRUE: return MakeID(2, tt); case MRB_TT_SYMBOL: return MakeID(mrb_symbol(obj), tt); case MRB_TT_INTEGER: return MakeID(mrb_integer(obj), tt); #ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: return MakeID(mrb_float_id(mrb_float(obj)), tt); #endif case MRB_TT_STRING: case MRB_TT_OBJECT: case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_ICLASS: case MRB_TT_SCLASS: case MRB_TT_PROC: case MRB_TT_ARRAY: case MRB_TT_HASH: case MRB_TT_RANGE: case MRB_TT_EXCEPTION: case MRB_TT_CDATA: case MRB_TT_ISTRUCT: default: return MakeID(mrb_ptr(obj), tt); } #endif } #ifdef MRB_WORD_BOXING #ifndef MRB_NO_FLOAT MRB_API mrb_value mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f) { union mrb_value_ v; #ifdef MRB_WORDBOX_NO_FLOAT_TRUNCATE v.p = mrb_obj_alloc(mrb, MRB_TT_FLOAT, mrb->float_class); v.fp->f = f; v.bp->frozen = 1; #elif defined(MRB_64BIT) && defined(MRB_USE_FLOAT32) v.w = 0; v.f = f; v.w = (v.w<<2) | 2; #else v.f = f; v.w = (v.w & ~3) | 2; #endif return v.value; } #ifndef MRB_WORDBOX_NO_FLOAT_TRUNCATE MRB_API mrb_float mrb_word_boxing_value_float(mrb_value v) { union mrb_value_ u; u.value = v; #if defined(MRB_64BIT) && defined(MRB_USE_FLOAT32) u.w >>= 2; #else u.w &= ~3; #endif return u.f; } #endif #endif /* MRB_NO_FLOAT */ MRB_API mrb_value mrb_word_boxing_cptr_value(mrb_state *mrb, void *p) { mrb_value v; struct RCptr *cptr = MRB_OBJ_ALLOC(mrb, MRB_TT_CPTR, mrb->object_class); SET_OBJ_VALUE(v, cptr); cptr->p = p; return v; } #endif /* MRB_WORD_BOXING */ #if defined(MRB_WORD_BOXING) || (defined(MRB_NAN_BOXING) && defined(MRB_INT64)) MRB_API mrb_value mrb_boxing_int_value(mrb_state *mrb, mrb_int n) { if (FIXABLE(n)) return mrb_fixnum_value(n); else { mrb_value v; struct RInteger *p = (struct RInteger*)mrb_obj_alloc(mrb, MRB_TT_INTEGER, mrb->integer_class); p->i = n; p->frozen = 1; SET_OBJ_VALUE(v, p); return v; } } #endif #if defined _MSC_VER && _MSC_VER < 1900 #ifndef va_copy static void mrb_msvc_va_copy(va_list *dest, va_list src) { *dest = src; } #define va_copy(dest, src) mrb_msvc_va_copy(&(dest), src) #endif MRB_API int mrb_msvc_vsnprintf(char *s, size_t n, const char *format, va_list arg) { int cnt; va_list argcp; va_copy(argcp, arg); if (n == 0 || (cnt = _vsnprintf_s(s, n, _TRUNCATE, format, argcp)) < 0) { cnt = _vscprintf(format, arg); } va_end(argcp); return cnt; } MRB_API int mrb_msvc_snprintf(char *s, size_t n, const char *format, ...) { va_list arg; va_start(arg, format); int ret = mrb_msvc_vsnprintf(s, n, format, arg); va_end(arg); return ret; } #endif /* defined _MSC_VER && _MSC_VER < 1900 */ nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/init.c0000644000000000000000000000013215077107276017571 xustar0030 mtime=1761382078.134420501 30 atime=1761382080.156411252 30 ctime=1761382108.558301948 nghttp2-1.68.0/third-party/mruby/src/init.c0000644000175100017510000000232415077107276020162 0ustar00runnerrunner/* ** init.c - initialize mruby core ** ** See Copyright Notice in mruby.h */ #include void mrb_init_symtbl(mrb_state*); void mrb_init_class(mrb_state*); void mrb_init_object(mrb_state*); void mrb_init_kernel(mrb_state*); void mrb_init_enumerable(mrb_state*); void mrb_init_symbol(mrb_state*); void mrb_init_string(mrb_state*); void mrb_init_exception(mrb_state*); void mrb_init_proc(mrb_state*); void mrb_init_array(mrb_state*); void mrb_init_hash(mrb_state*); void mrb_init_numeric(mrb_state*); void mrb_init_range(mrb_state*); void mrb_init_gc(mrb_state*); void mrb_init_math(mrb_state*); void mrb_init_version(mrb_state*); void mrb_init_mrblib(mrb_state*); #define DONE mrb_gc_arena_restore(mrb, 0); void mrb_init_core(mrb_state *mrb) { mrb_init_symtbl(mrb); DONE; mrb_init_class(mrb); DONE; mrb_init_object(mrb); DONE; mrb_init_kernel(mrb); DONE; mrb_init_enumerable(mrb); DONE; mrb_init_symbol(mrb); DONE; mrb_init_string(mrb); DONE; mrb_init_exception(mrb); DONE; mrb_init_proc(mrb); DONE; mrb_init_array(mrb); DONE; mrb_init_hash(mrb); DONE; mrb_init_numeric(mrb); DONE; mrb_init_range(mrb); DONE; mrb_init_gc(mrb); DONE; mrb_init_version(mrb); DONE; mrb_init_mrblib(mrb); DONE; } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/range.c0000644000000000000000000000013215077107276017722 xustar0030 mtime=1761382078.135420497 30 atime=1761382080.158411242 30 ctime=1761382108.583301876 nghttp2-1.68.0/third-party/mruby/src/range.c0000644000175100017510000003434215077107276020320 0ustar00runnerrunner/* ** range.c - Range class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #define RANGE_INITIALIZED_FLAG 1 #define RANGE_INITIALIZED(p) ((p)->flags |= RANGE_INITIALIZED_FLAG) #define RANGE_INITIALIZED_P(p) ((p)->flags & RANGE_INITIALIZED_FLAG) static void r_check(mrb_state *mrb, mrb_value a, mrb_value b) { enum mrb_vtype ta; enum mrb_vtype tb; mrb_int n; ta = mrb_type(a); tb = mrb_type(b); #ifdef MRB_NO_FLOAT if (ta == MRB_TT_INTEGER && tb == MRB_TT_INTEGER ) return; #else if ((ta == MRB_TT_INTEGER || ta == MRB_TT_FLOAT) && (tb == MRB_TT_INTEGER || tb == MRB_TT_FLOAT)) { return; } #endif if (mrb_nil_p(a) || mrb_nil_p(b)) return; n = mrb_cmp(mrb, a, b); if (n == -2) { /* can not be compared */ mrb_raise(mrb, E_ARGUMENT_ERROR, "bad value for range"); } } static mrb_bool r_le(mrb_state *mrb, mrb_value a, mrb_value b) { mrb_int n = mrb_cmp(mrb, a, b); if (n == 0 || n == -1) return TRUE; return FALSE; } static mrb_bool r_gt(mrb_state *mrb, mrb_value a, mrb_value b) { return mrb_cmp(mrb, a, b) == 1; } static mrb_bool r_ge(mrb_state *mrb, mrb_value a, mrb_value b) { mrb_int n = mrb_cmp(mrb, a, b); if (n == 0 || n == 1) return TRUE; return FALSE; } static void range_ptr_alloc_edges(mrb_state *mrb, struct RRange *r) { #ifndef MRB_RANGE_EMBED r->edges = (mrb_range_edges*)mrb_malloc(mrb, sizeof(mrb_range_edges)); #endif } static struct RRange * range_ptr_init(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl) { r_check(mrb, beg, end); if (r) { if (RANGE_INITIALIZED_P(r)) { /* Ranges are immutable, so that they should be initialized only once. */ mrb_name_error(mrb, MRB_SYM(initialize), "'initialize' called twice"); } else { range_ptr_alloc_edges(mrb, r); } } else { r = MRB_OBJ_ALLOC(mrb, MRB_TT_RANGE, mrb->range_class); range_ptr_alloc_edges(mrb, r); } RANGE_BEG(r) = beg; RANGE_END(r) = end; RANGE_EXCL(r) = excl; RANGE_INITIALIZED(r); return r; } static void range_ptr_replace(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl) { range_ptr_init(mrb, r, beg, end, excl); mrb_write_barrier(mrb, (struct RBasic*)r); } /* * call-seq: * rng.first => obj * rng.begin => obj * * Returns the first object in rng. */ static mrb_value range_beg(mrb_state *mrb, mrb_value range) { return mrb_range_beg(mrb, range); } /* * call-seq: * rng.end => obj * rng.last => obj * * Returns the object that defines the end of rng. * * (1..10).end #=> 10 * (1...10).end #=> 10 */ static mrb_value range_end(mrb_state *mrb, mrb_value range) { return mrb_range_end(mrb, range); } /* * call-seq: * range.exclude_end? => true or false * * Returns true if range excludes its end value. */ static mrb_value range_excl(mrb_state *mrb, mrb_value range) { return mrb_bool_value(mrb_range_excl_p(mrb, range)); } /* * call-seq: * Range.new(start, end, exclusive=false) => range * * Constructs a range using the given start and end. If the third * parameter is omitted or is false, the range will include * the end object; otherwise, it will be excluded. */ static mrb_value range_initialize(mrb_state *mrb, mrb_value range) { mrb_value beg, end; mrb_bool exclusive = FALSE; mrb_get_args(mrb, "oo|b", &beg, &end, &exclusive); range_ptr_replace(mrb, mrb_range_raw_ptr(range), beg, end, exclusive); mrb_obj_freeze(mrb, range); return range; } /* * call-seq: * range == obj => true or false * * Returns true only if * 1) obj is a Range, * 2) obj has equivalent beginning and end items (by comparing them with ==), * 3) obj has the same #exclude_end? setting as rng
. * * (0..2) == (0..2) #=> true * (0..2) == Range.new(0,2) #=> true * (0..2) == (0...2) #=> false */ static mrb_value range_eq(mrb_state *mrb, mrb_value range) { struct RRange *rr; struct RRange *ro; mrb_value obj = mrb_get_arg1(mrb); mrb_bool v1, v2; if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value(); if (!mrb_obj_is_instance_of(mrb, obj, mrb_obj_class(mrb, range))) { /* same class? */ return mrb_false_value(); } rr = mrb_range_ptr(mrb, range); ro = mrb_range_ptr(mrb, obj); v1 = mrb_equal(mrb, RANGE_BEG(rr), RANGE_BEG(ro)); v2 = mrb_equal(mrb, RANGE_END(rr), RANGE_END(ro)); if (!v1 || !v2 || RANGE_EXCL(rr) != RANGE_EXCL(ro)) { return mrb_false_value(); } return mrb_true_value(); } /* * call-seq: * range === obj => true or false * range.member?(val) => true or false * range.include?(val) => true or false */ static mrb_value range_include(mrb_state *mrb, mrb_value range) { mrb_value val = mrb_get_arg1(mrb); struct RRange *r = mrb_range_ptr(mrb, range); mrb_value beg, end; beg = RANGE_BEG(r); end = RANGE_END(r); if (mrb_nil_p(beg)) { if (RANGE_EXCL(r) ? r_gt(mrb, end, val) /* end > val */ : r_ge(mrb, end, val)) { /* end >= val */ return mrb_true_value(); } } else if (r_le(mrb, beg, val)) { /* beg <= val */ if (mrb_nil_p(end)) { return mrb_true_value(); } if (RANGE_EXCL(r) ? r_gt(mrb, end, val) /* end > val */ : r_ge(mrb, end, val)) { /* end >= val */ return mrb_true_value(); } } return mrb_false_value(); } /* 15.2.14.4.12(x) */ /* * call-seq: * rng.to_s -> string * * Convert this range object to a printable form. */ static mrb_value range_to_s(mrb_state *mrb, mrb_value range) { mrb_value str, str2; struct RRange *r = mrb_range_ptr(mrb, range); str = mrb_obj_as_string(mrb, RANGE_BEG(r)); str2 = mrb_obj_as_string(mrb, RANGE_END(r)); str = mrb_str_dup(mrb, str); mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2); mrb_str_cat_str(mrb, str, str2); return str; } /* 15.2.14.4.13(x) */ /* * call-seq: * rng.inspect -> string * * Convert this range object to a printable form (using * inspect to convert the start and end * objects). */ static mrb_value range_inspect(mrb_state *mrb, mrb_value range) { mrb_value str; struct RRange *r = mrb_range_ptr(mrb, range); if (!mrb_nil_p(RANGE_BEG(r))) { str = mrb_inspect(mrb, RANGE_BEG(r)); str = mrb_str_dup(mrb, str); mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2); } else { str = mrb_str_new(mrb, "...", RANGE_EXCL(r) ? 3 : 2); } if (!mrb_nil_p(RANGE_END(r))) { mrb_value str2 = mrb_inspect(mrb, RANGE_END(r)); mrb_str_cat_str(mrb, str, str2); } return str; } /* 15.2.14.4.14(x) */ /* * call-seq: * rng.eql?(obj) -> true or false * * Returns true only if obj is a Range, has equivalent * beginning and end items (by comparing them with #eql?), and has the same * #exclude_end? setting as rng. * * (0..2).eql?(0..2) #=> true * (0..2).eql?(Range.new(0,2)) #=> true * (0..2).eql?(0...2) #=> false */ static mrb_value range_eql(mrb_state *mrb, mrb_value range) { mrb_value obj = mrb_get_arg1(mrb); struct RRange *r, *o; if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value(); if (!mrb_range_p(obj)) return mrb_false_value(); r = mrb_range_ptr(mrb, range); o = mrb_range_ptr(mrb, obj); if (!mrb_eql(mrb, RANGE_BEG(r), RANGE_BEG(o)) || !mrb_eql(mrb, RANGE_END(r), RANGE_END(o)) || (RANGE_EXCL(r) != RANGE_EXCL(o))) { return mrb_false_value(); } return mrb_true_value(); } /* 15.2.14.4.15(x) */ static mrb_value range_initialize_copy(mrb_state *mrb, mrb_value copy) { mrb_value src = mrb_get_arg1(mrb); struct RRange *r; if (mrb_obj_equal(mrb, copy, src)) return copy; if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) { mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class"); } r = mrb_range_ptr(mrb, src); range_ptr_replace(mrb, mrb_range_raw_ptr(copy), RANGE_BEG(r), RANGE_END(r), RANGE_EXCL(r)); mrb_obj_freeze(mrb, copy); return copy; } static mrb_value range_num_to_a(mrb_state *mrb, mrb_value range) { struct RRange *r = mrb_range_ptr(mrb, range); mrb_value beg = RANGE_BEG(r); mrb_value end = RANGE_END(r); mrb_value ary; mrb->c->ci->mid = 0; if (mrb_nil_p(end)) { mrb_raise(mrb, E_RANGE_ERROR, "cannot convert endless range to an array"); } if (mrb_integer_p(beg)) { if (mrb_integer_p(end)) { mrb_int a = mrb_integer(beg); mrb_int b = mrb_integer(end); if (a > b) { return mrb_ary_new_capa(mrb, 0); } mrb_int len; if (mrb_int_sub_overflow(b, a, &len)) { too_long: mrb_raise(mrb, E_RANGE_ERROR, "integer range too long"); } if (!RANGE_EXCL(r)) { if (len == MRB_INT_MAX) goto too_long; len++; } ary = mrb_ary_new_capa(mrb, len); mrb_value *ptr = RARRAY_PTR(ary); for (mrb_int i=0; i b) { return mrb_ary_new_capa(mrb, 0); } mrb_int alen = (mrb_int)(b - a) + 1; ary = mrb_ary_new_capa(mrb, alen); mrb_value *ptr = RARRAY_PTR(ary); if (RANGE_EXCL(r)) { for (mrb_int i=0; a len) return MRB_RANGE_OUT; if (end > len) end = len; } if (end < 0) end += len; if (!excl && (!trunc || end < len)) end++; /* include end point */ len = end - beg; if (len < 0) len = 0; *begp = beg; *lenp = len; return MRB_RANGE_OK; } void mrb_init_range(mrb_state *mrb) { struct RClass *r; r = mrb_define_class_id(mrb, MRB_SYM(Range), mrb->object_class); /* 15.2.14 */ mrb->range_class = r; MRB_SET_INSTANCE_TT(r, MRB_TT_RANGE); mrb_define_method_id(mrb, r, MRB_SYM(begin), range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.3 */ mrb_define_method_id(mrb, r, MRB_SYM(end), range_end, MRB_ARGS_NONE()); /* 15.2.14.4.5 */ mrb_define_method_id(mrb, r, MRB_OPSYM(eq), range_eq, MRB_ARGS_REQ(1)); /* 15.2.14.4.1 */ mrb_define_method_id(mrb, r, MRB_OPSYM(eqq), range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.2 */ mrb_define_method_id(mrb, r, MRB_SYM_Q(exclude_end), range_excl, MRB_ARGS_NONE()); /* 15.2.14.4.6 */ mrb_define_method_id(mrb, r, MRB_SYM(first), range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.7 */ mrb_define_method_id(mrb, r, MRB_SYM_Q(include), range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.8 */ mrb_define_method_id(mrb, r, MRB_SYM(initialize), range_initialize, MRB_ARGS_ANY()); /* 15.2.14.4.9 */ mrb_define_method_id(mrb, r, MRB_SYM(last), range_end, MRB_ARGS_NONE()); /* 15.2.14.4.10 */ mrb_define_method_id(mrb, r, MRB_SYM_Q(member), range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.11 */ mrb_define_method_id(mrb, r, MRB_SYM(to_s), range_to_s, MRB_ARGS_NONE()); /* 15.2.14.4.12(x) */ mrb_define_method_id(mrb, r, MRB_SYM(inspect), range_inspect, MRB_ARGS_NONE()); /* 15.2.14.4.13(x) */ mrb_define_method_id(mrb, r, MRB_SYM_Q(eql), range_eql, MRB_ARGS_REQ(1)); /* 15.2.14.4.14(x) */ mrb_define_method_id(mrb, r, MRB_SYM(initialize_copy), range_initialize_copy, MRB_ARGS_REQ(1)); /* 15.2.14.4.15(x) */ mrb_define_method_id(mrb, r, MRB_SYM(__num_to_a), range_num_to_a, MRB_ARGS_NONE()); } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/codedump.c0000644000000000000000000000013215077107276020426 xustar0030 mtime=1761382078.133420506 30 atime=1761382080.153411265 30 ctime=1761382108.562301936 nghttp2-1.68.0/third-party/mruby/src/codedump.c0000644000175100017510000004452615077107276021031 0ustar00runnerrunner#include #include #include #include #include #include #include #include #ifndef MRB_NO_STDIO static mrb_bool print_r_p(mrb_state *mrb, const mrb_irep *irep, size_t n) { if (n == 0) return FALSE; if (!irep->lv) return FALSE; if (n >= irep->nlocals) return FALSE; if (!irep->lv[n-1]) return FALSE; return TRUE; } static void print_r(mrb_state *mrb, const mrb_irep *irep, size_t n, FILE *out) { if (n == 0) return; if (n >= irep->nlocals) return; if (!irep->lv[n-1]) return; fprintf(out, " R%d:%s", (int)n, mrb_sym_dump(mrb, irep->lv[n-1])); } static void print_lv_a(mrb_state *mrb, const mrb_irep *irep, uint16_t a, FILE *out) { if (print_r_p(mrb, irep, a)) { fprintf(out, "\t;"); print_r(mrb, irep, a, out); } fprintf(out, "\n"); } static void print_lv_ab(mrb_state *mrb, const mrb_irep *irep, uint16_t a, uint16_t b, FILE *out) { if (print_r_p(mrb, irep, a) || print_r_p(mrb, irep, b)) { fprintf(out, "\t;"); print_r(mrb, irep, a, out); print_r(mrb, irep, b, out); } fprintf(out, "\n"); } static void print_header(mrb_state *mrb, const mrb_irep *irep, ptrdiff_t i, FILE *out) { int32_t line; mrb_assert(i <= UINT32_MAX); line = mrb_debug_get_line(mrb, irep, (uint32_t)i); if (line < 0) { fprintf(out, " "); } else { fprintf(out, "%5d ", line); } fprintf(out, "%03d ", (int)i); } static void print_args(uint16_t i, FILE *out) { mrb_assert(i <= 255); uint8_t n = i&0xf; uint8_t nk = (i>>4)&0xf; if (n == 15) { fprintf(out, "n=*"); } else { fprintf(out, "n=%d", n); } if (nk > 0) { fprintf(out, "|"); if (nk == 15) { fprintf(out, "nk=*"); } else { fprintf(out, "nk=%d", nk); } } fprintf(out, "\n"); } #define CASE(insn,ops) case insn: FETCH_ ## ops (); L_ ## insn static void codedump(mrb_state *mrb, const mrb_irep *irep, FILE *out) { const char *file = NULL; if (!irep) return; fprintf(out, "irep %p nregs=%d nlocals=%d pools=%d syms=%d reps=%d ilen=%d\n", (void*)irep, irep->nregs, irep->nlocals, (int)irep->plen, (int)irep->slen, (int)irep->rlen, (int)irep->ilen); if (irep->lv) { int head = FALSE; for (int i = 1; i < irep->nlocals; i++) { char const *s = mrb_sym_dump(mrb, irep->lv[i - 1]); if (s) { if (!head) { head = TRUE; fprintf(out, "local variable names:\n"); } fprintf(out, " R%d:%s\n", i, s); } } } if (irep->clen > 0) { const struct mrb_irep_catch_handler *e = mrb_irep_catch_handler_table(irep); for (int i = irep->clen; i > 0; i--,e++) { uint32_t begin = mrb_irep_catch_handler_unpack(e->begin); uint32_t end = mrb_irep_catch_handler_unpack(e->end); uint32_t target = mrb_irep_catch_handler_unpack(e->target); char buf[20]; const char *type; switch (e->type) { case MRB_CATCH_RESCUE: type = "rescue"; break; case MRB_CATCH_ENSURE: type = "ensure"; break; default: buf[0] = '\0'; snprintf(buf, sizeof(buf), "0x%02x ", (int)e->type); type = buf; break; } fprintf(out, "catch type: %-8s begin: %04" PRIu32 " end: %04" PRIu32 " target: %04" PRIu32 "\n", type, begin, end, target); } } const mrb_code *pc = irep->iseq; const mrb_code *pcend = pc + irep->ilen; while (pc < pcend) { uint32_t a; uint16_t b; uint16_t c; mrb_code ins; int ai = mrb_gc_arena_save(mrb); ptrdiff_t i = pc - irep->iseq; const char *next_file = mrb_debug_get_filename(mrb, irep, (uint32_t)i); if (next_file && file != next_file) { fprintf(out, "file: %s\n", next_file); file = next_file; } print_header(mrb, irep, i, out); ins = READ_B(); switch (ins) { CASE(OP_NOP, Z): fprintf(out, "NOP\n"); break; CASE(OP_MOVE, BB): fprintf(out, "MOVE\t\tR%d\tR%d\t", a, b); print_lv_ab(mrb, irep, a, b, out); break; CASE(OP_LOADL, BB): switch (irep->pool[b].tt) { #ifndef MRB_NO_FLOAT case IREP_TT_FLOAT: fprintf(out, "LOADL\t\tR%d\tL[%d]\t; %f", a, b, (double)irep->pool[b].u.f); break; #endif case IREP_TT_INT32: fprintf(out, "LOADL\t\tR%d\tL[%d]\t; %" PRId32, a, b, irep->pool[b].u.i32); break; #ifdef MRB_64BIT case IREP_TT_INT64: fprintf(out, "LOADL\t\tR%d\tL[%d]\t; %" PRId64, a, b, irep->pool[b].u.i64); break; #endif default: fprintf(out, "LOADL\t\tR%d\tL[%d]\t", a, b); break; } print_lv_a(mrb, irep, a, out); break; CASE(OP_LOADI8, BB): fprintf(out, "LOADI8\tR%d\t%d\t", a, b); print_lv_a(mrb, irep, a, out); break; CASE(OP_LOADINEG, BB): fprintf(out, "LOADINEG\tR%d\t-%d\t", a, b); print_lv_a(mrb, irep, a, out); break; CASE(OP_LOADI16, BS): fprintf(out, "LOADI16\tR%d\t%d\t", a, (int)(int16_t)b); print_lv_a(mrb, irep, a, out); break; CASE(OP_LOADI32, BSS): fprintf(out, "LOADI32\tR%d\t%d\t", a, (int32_t)(((uint32_t)b<<16)+c)); print_lv_a(mrb, irep, a, out); break; CASE(OP_LOADI__1, B): fprintf(out, "LOADI__1\tR%d\t(-1)\t", a); print_lv_a(mrb, irep, a, out); break; CASE(OP_LOADI_0, B): goto L_LOADI; CASE(OP_LOADI_1, B): goto L_LOADI; CASE(OP_LOADI_2, B): goto L_LOADI; CASE(OP_LOADI_3, B): goto L_LOADI; CASE(OP_LOADI_4, B): goto L_LOADI; CASE(OP_LOADI_5, B): goto L_LOADI; CASE(OP_LOADI_6, B): goto L_LOADI; CASE(OP_LOADI_7, B): L_LOADI: b = ins-(int)OP_LOADI_0; fprintf(out, "LOADI_%d\tR%d\t(%d)\t", b, a, b); print_lv_a(mrb, irep, a, out); break; CASE(OP_LOADSYM, BB): fprintf(out, "LOADSYM\tR%d\t:%s\t", a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a, out); break; CASE(OP_LOADNIL, B): fprintf(out, "LOADNIL\tR%d\t(nil)\t", a); print_lv_a(mrb, irep, a, out); break; CASE(OP_LOADSELF, B): fprintf(out, "LOADSELF\tR%d\t(R0)\t", a); print_lv_a(mrb, irep, a, out); break; CASE(OP_LOADT, B): fprintf(out, "LOADT\t\tR%d\t(true)\t", a); print_lv_a(mrb, irep, a, out); break; CASE(OP_LOADF, B): fprintf(out, "LOADF\t\tR%d\t(false)\t", a); print_lv_a(mrb, irep, a, out); break; CASE(OP_GETGV, BB): fprintf(out, "GETGV\t\tR%d\t%s\t", a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a, out); break; CASE(OP_SETGV, BB): fprintf(out, "SETGV\t\t%s\tR%d\t", mrb_sym_dump(mrb, irep->syms[b]), a); print_lv_a(mrb, irep, a, out); break; CASE(OP_GETSV, BB): fprintf(out, "GETSV\t\tR%d\t%s\t", a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a, out); break; CASE(OP_SETSV, BB): fprintf(out, "SETSV\t\t%s\tR%d\t", mrb_sym_dump(mrb, irep->syms[b]), a); print_lv_a(mrb, irep, a, out); break; CASE(OP_GETCONST, BB): fprintf(out, "GETCONST\tR%d\t%s\t", a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a, out); break; CASE(OP_SETCONST, BB): fprintf(out, "SETCONST\t%s\tR%d\t", mrb_sym_dump(mrb, irep->syms[b]), a); print_lv_a(mrb, irep, a, out); break; CASE(OP_GETMCNST, BB): fprintf(out, "GETMCNST\tR%d\t(R%d)::%s\t", a, a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a, out); break; CASE(OP_SETMCNST, BB): fprintf(out, "SETMCNST\t(R%d)::%s\tR%d\t", a+1, mrb_sym_dump(mrb, irep->syms[b]), a); print_lv_a(mrb, irep, a, out); break; CASE(OP_GETIV, BB): fprintf(out, "GETIV\t\tR%d\t%s\t", a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a, out); break; CASE(OP_SETIV, BB): fprintf(out, "SETIV\t\t%s\tR%d\t", mrb_sym_dump(mrb, irep->syms[b]), a); print_lv_a(mrb, irep, a, out); break; CASE(OP_GETUPVAR, BBB): fprintf(out, "GETUPVAR\tR%d\t%d\t%d\t", a, b, c); print_lv_a(mrb, irep, a, out); break; CASE(OP_SETUPVAR, BBB): fprintf(out, "SETUPVAR\tR%d\t%d\t%d\t", a, b, c); print_lv_a(mrb, irep, a, out); break; CASE(OP_GETCV, BB): fprintf(out, "GETCV\t\tR%d\t%s\t", a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a, out); break; CASE(OP_SETCV, BB): fprintf(out, "SETCV\t\t%s\tR%d\t", mrb_sym_dump(mrb, irep->syms[b]), a); print_lv_a(mrb, irep, a, out); break; CASE(OP_GETIDX, B): fprintf(out, "GETIDX\tR%d\t(R%d)\n", a, a+1); break; CASE(OP_SETIDX, B): fprintf(out, "SETIDX\tR%d\t(R%d)\t(R%d)\n", a, a+1, a+2); break; CASE(OP_JMP, S): i = pc - irep->iseq; fprintf(out, "JMP\t\t%03d\n", (int)i+(int16_t)a); break; CASE(OP_JMPUW, S): i = pc - irep->iseq; fprintf(out, "JMPUW\t\t%03d\n", (int)i+(int16_t)a); break; CASE(OP_JMPIF, BS): i = pc - irep->iseq; fprintf(out, "JMPIF\t\tR%d\t%03d\t", a, (int)i+(int16_t)b); print_lv_a(mrb, irep, a, out); break; CASE(OP_JMPNOT, BS): i = pc - irep->iseq; fprintf(out, "JMPNOT\tR%d\t%03d\t", a, (int)i+(int16_t)b); print_lv_a(mrb, irep, a, out); break; CASE(OP_JMPNIL, BS): i = pc - irep->iseq; fprintf(out, "JMPNIL\tR%d\t%03d\t", a, (int)i+(int16_t)b); print_lv_a(mrb, irep, a, out); break; CASE(OP_SSEND, BBB): fprintf(out, "SSEND\t\tR%d\t:%s\t", a, mrb_sym_dump(mrb, irep->syms[b])); print_args(c, out); break; CASE(OP_SSENDB, BBB): fprintf(out, "SSENDB\tR%d\t:%s\t", a, mrb_sym_dump(mrb, irep->syms[b])); print_args(c, out); break; CASE(OP_SEND, BBB): fprintf(out, "SEND\t\tR%d\t:%s\t", a, mrb_sym_dump(mrb, irep->syms[b])); print_args(c, out); break; CASE(OP_SENDB, BBB): fprintf(out, "SENDB\t\tR%d\t:%s\t", a, mrb_sym_dump(mrb, irep->syms[b])); print_args(c, out); break; CASE(OP_CALL, Z): fprintf(out, "CALL\n"); break; CASE(OP_SUPER, BB): fprintf(out, "SUPER\t\tR%d\t", a); print_args(b, out); break; CASE(OP_ARGARY, BS): fprintf(out, "ARGARY\tR%d\t%d:%d:%d:%d (%d)\t", a, (b>>11)&0x3f, (b>>10)&0x1, (b>>5)&0x1f, (b>>4)&0x1, (b>>0)&0xf); print_lv_a(mrb, irep, a, out); break; CASE(OP_ENTER, W): fprintf(out, "ENTER\t\t%d:%d:%d:%d:%d:%d:%d (0x%x)\n", MRB_ASPEC_REQ(a), MRB_ASPEC_OPT(a), MRB_ASPEC_REST(a), MRB_ASPEC_POST(a), MRB_ASPEC_KEY(a), MRB_ASPEC_KDICT(a), MRB_ASPEC_BLOCK(a), a); break; CASE(OP_KEY_P, BB): fprintf(out, "KEY_P\t\tR%d\t:%s\t", a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a, out); break; CASE(OP_KEYEND, Z): fprintf(out, "KEYEND\n"); break; CASE(OP_KARG, BB): fprintf(out, "KARG\t\tR%d\t:%s\t", a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a, out); break; CASE(OP_RETURN, B): fprintf(out, "RETURN\tR%d\t\t", a); print_lv_a(mrb, irep, a, out); break; CASE(OP_RETURN_BLK, B): fprintf(out, "RETURN_BLK\tR%d\t\t", a); print_lv_a(mrb, irep, a, out); break; CASE(OP_BREAK, B): fprintf(out, "BREAK\t\tR%d\t\t", a); print_lv_a(mrb, irep, a, out); break; CASE(OP_BLKPUSH, BS): fprintf(out, "BLKPUSH\tR%d\t%d:%d:%d:%d (%d)\t", a, (b>>11)&0x3f, (b>>10)&0x1, (b>>5)&0x1f, (b>>4)&0x1, (b>>0)&0xf); print_lv_a(mrb, irep, a, out); break; CASE(OP_LAMBDA, BB): fprintf(out, "LAMBDA\tR%d\tI[%d]\n", a, b); break; CASE(OP_BLOCK, BB): fprintf(out, "BLOCK\t\tR%d\tI[%d]\n", a, b); break; CASE(OP_METHOD, BB): fprintf(out, "METHOD\tR%d\tI[%d]\n", a, b); break; CASE(OP_RANGE_INC, B): fprintf(out, "RANGE_INC\tR%d\n", a); break; CASE(OP_RANGE_EXC, B): fprintf(out, "RANGE_EXC\tR%d\n", a); break; CASE(OP_DEF, BB): fprintf(out, "DEF\t\tR%d\t:%s\t(R%d)\n", a, mrb_sym_dump(mrb, irep->syms[b]),a+1); break; CASE(OP_UNDEF, B): fprintf(out, "UNDEF\t\t:%s\n", mrb_sym_dump(mrb, irep->syms[a])); break; CASE(OP_ALIAS, BB): fprintf(out, "ALIAS\t\t:%s\t%s\n", mrb_sym_dump(mrb, irep->syms[a]), mrb_sym_dump(mrb, irep->syms[b])); break; CASE(OP_ADD, B): fprintf(out, "ADD\t\tR%d\t(R%d)\n", a, a+1); break; CASE(OP_ADDI, BB): fprintf(out, "ADDI\t\tR%d\t%d\t", a, b); print_lv_a(mrb, irep, a, out); break; CASE(OP_SUB, B): fprintf(out, "SUB\t\tR%d\t(R%d)\n", a, a+1); break; CASE(OP_SUBI, BB): fprintf(out, "SUBI\t\tR%d\t%d\t", a, b); print_lv_a(mrb, irep, a, out); break; CASE(OP_MUL, B): fprintf(out, "MUL\t\tR%d\t(R%d)\n", a, a+1); break; CASE(OP_DIV, B): fprintf(out, "DIV\t\tR%d\t(R%d)\n", a, a+1); break; CASE(OP_LT, B): fprintf(out, "LT\t\tR%d\t(R%d)\n", a, a+1); break; CASE(OP_LE, B): fprintf(out, "LE\t\tR%d\t(R%d)\n", a, a+1); break; CASE(OP_GT, B): fprintf(out, "GT\t\tR%d\t(R%d)\n", a, a+1); break; CASE(OP_GE, B): fprintf(out, "GE\t\tR%d\t(R%d)\n", a, a+1); break; CASE(OP_EQ, B): fprintf(out, "EQ\t\tR%d\t(R%d)\n", a, a+1); break; CASE(OP_ARRAY, BB): fprintf(out, "ARRAY\t\tR%d\tR%d\t%d", a, a, b); print_lv_a(mrb, irep, a, out); break; CASE(OP_ARRAY2, BBB): fprintf(out, "ARRAY\t\tR%d\tR%d\t%d", a, b, c); print_lv_ab(mrb, irep, a, b, out); break; CASE(OP_ARYCAT, B): fprintf(out, "ARYCAT\tR%d\t(R%d)\t", a, a+1); print_lv_a(mrb, irep, a, out); break; CASE(OP_ARYPUSH, BB): fprintf(out, "ARYPUSH\tR%d\t%d\t", a, b); print_lv_a(mrb, irep, a, out); break; CASE(OP_ARYSPLAT, B): fprintf(out, "ARYSPLAT\tR%d\t", a); print_lv_a(mrb, irep, a, out); break; CASE(OP_AREF, BBB): fprintf(out, "AREF\t\tR%d\tR%d\t%d", a, b, c); print_lv_ab(mrb, irep, a, b, out); break; CASE(OP_ASET, BBB): fprintf(out, "ASET\t\tR%d\tR%d\t%d", a, b, c); print_lv_ab(mrb, irep, a, b, out); break; CASE(OP_APOST, BBB): fprintf(out, "APOST\t\tR%d\t%d\t%d", a, b, c); print_lv_a(mrb, irep, a, out); break; CASE(OP_INTERN, B): fprintf(out, "INTERN\tR%d\t\t", a); print_lv_a(mrb, irep, a, out); break; CASE(OP_SYMBOL, BB): mrb_assert((irep->pool[b].tt&IREP_TT_NFLAG)==0); fprintf(out, "SYMBOL\tR%d\tL[%d]\t; %s", a, b, irep->pool[b].u.str); print_lv_a(mrb, irep, a, out); break; CASE(OP_STRING, BB): mrb_assert((irep->pool[b].tt&IREP_TT_NFLAG)==0); fprintf(out, "STRING\tR%d\tL[%d]\t; %s", a, b, irep->pool[b].u.str); print_lv_a(mrb, irep, a, out); break; CASE(OP_STRCAT, B): fprintf(out, "STRCAT\tR%d\t(R%d)\t", a, a+1); print_lv_a(mrb, irep, a, out); break; CASE(OP_HASH, BB): fprintf(out, "HASH\t\tR%d\t%d\t", a, b); print_lv_a(mrb, irep, a, out); break; CASE(OP_HASHADD, BB): fprintf(out, "HASHADD\tR%d\t%d\t", a, b); print_lv_a(mrb, irep, a, out); break; CASE(OP_HASHCAT, B): fprintf(out, "HASHCAT\tR%d\t(R%d)\t", a, a+1); print_lv_a(mrb, irep, a, out); break; CASE(OP_OCLASS, B): fprintf(out, "OCLASS\tR%d\t\t", a); print_lv_a(mrb, irep, a, out); break; CASE(OP_CLASS, BB): fprintf(out, "CLASS\t\tR%d\t:%s", a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a, out); break; CASE(OP_MODULE, BB): fprintf(out, "MODULE\tR%d\t:%s", a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a, out); break; CASE(OP_EXEC, BB): fprintf(out, "EXEC\t\tR%d\tI[%d]", a, b); print_lv_a(mrb, irep, a, out); break; CASE(OP_SCLASS, B): fprintf(out, "SCLASS\tR%d\t", a); print_lv_a(mrb, irep, a, out); break; CASE(OP_TCLASS, B): fprintf(out, "TCLASS\tR%d\t\t", a); print_lv_a(mrb, irep, a, out); break; CASE(OP_ERR, B): if ((irep->pool[a].tt & IREP_TT_NFLAG) == 0) { fprintf(out, "ERR\t\t%s\n", irep->pool[a].u.str); } else { fprintf(out, "ERR\tL[%d]\n", a); } break; CASE(OP_EXCEPT, B): fprintf(out, "EXCEPT\tR%d\t\t", a); print_lv_a(mrb, irep, a, out); break; CASE(OP_RESCUE, BB): fprintf(out, "RESCUE\tR%d\tR%d", a, b); print_lv_ab(mrb, irep, a, b, out); break; CASE(OP_RAISEIF, B): fprintf(out, "RAISEIF\tR%d\t\t", a); print_lv_a(mrb, irep, a, out); break; CASE(OP_DEBUG, BBB): fprintf(out, "DEBUG\t\t%d\t%d\t%d\n", a, b, c); break; CASE(OP_STOP, Z): fprintf(out, "STOP\n"); break; CASE(OP_EXT1, Z): fprintf(out, "EXT1\n"); print_header(mrb, irep, pc-irep->iseq, out); ins = READ_B(); switch (ins) { #define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _1 (); goto L_OP_ ## i; #include #undef OPCODE } break; CASE(OP_EXT2, Z): fprintf(out, "EXT2\n"); print_header(mrb, irep, pc-irep->iseq, out); ins = READ_B(); switch (ins) { #define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _2 (); goto L_OP_ ## i; #include #undef OPCODE } break; CASE(OP_EXT3, Z): fprintf(out, "EXT3\n"); print_header(mrb, irep, pc-irep->iseq, out); ins = READ_B(); switch (ins) { #define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _3 (); goto L_OP_ ## i; #include #undef OPCODE } break; default: fprintf(out, "unknown_op (0x%x)\n", ins); break; } mrb_gc_arena_restore(mrb, ai); } fprintf(out, "\n"); } static void codedump_recur(mrb_state *mrb, const mrb_irep *irep, FILE *out) { codedump(mrb, irep, out); if (irep->reps) { for (int i=0; irlen; i++) { codedump_recur(mrb, irep->reps[i], out); } } } void mrb_codedump_all_file(mrb_state *mrb, struct RProc *proc, FILE *out) { codedump_recur(mrb, proc->body.irep, out); fflush(out); } #endif void mrb_codedump_all(mrb_state *mrb, struct RProc *proc) { #ifndef MRB_NO_STDIO mrb_codedump_all_file(mrb, proc, stdout); #endif } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/print.c0000644000000000000000000000013215077107276017762 xustar0030 mtime=1761382078.135420497 30 atime=1761382080.158411242 30 ctime=1761382108.572301907 nghttp2-1.68.0/third-party/mruby/src/print.c0000644000175100017510000000543215077107276020356 0ustar00runnerrunner/* ** print.c - Kernel#p, Kernel#print ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #if defined(_WIN32) # include # include #ifdef _MSC_VER # define isatty(x) _isatty(x) # define fileno(x) _fileno(x) #endif #else # include #endif #ifndef MRB_NO_STDIO static void printcstr(mrb_state *mrb, const char *str, size_t len, FILE *stream) { #if defined(_WIN32) if (isatty(fileno(stream))) { DWORD written; int wlen = MultiByteToWideChar(CP_UTF8, 0, str, (int)len, NULL, 0); wchar_t* utf16 = (wchar_t*)mrb_malloc(mrb, (wlen+1) * sizeof(wchar_t)); if (MultiByteToWideChar(CP_UTF8, 0, str, (int)len, utf16, wlen) > 0) { utf16[wlen] = 0; WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), utf16, (DWORD)wlen, &written, NULL); } mrb_free(mrb, utf16); return; } #endif fwrite(str, (size_t)len, 1, stream); } static void printstr(mrb_state *mrb, mrb_value obj, FILE *stream) { if (!mrb_string_p(obj)) { obj = mrb_obj_as_string(mrb, obj); } printcstr(mrb, RSTRING_PTR(obj), RSTRING_LEN(obj), stream); } static void printstrln(mrb_state *mrb, mrb_value obj, FILE *stream) { printstr(mrb, obj, stream); printcstr(mrb, "\n", 1, stdout); } void mrb_core_init_printabort(mrb_state *mrb) { static const char *str = "Failed mruby core initialization"; printcstr(mrb, str, strlen(str), stdout); } #ifndef HAVE_MRUBY_IO_GEM mrb_value mrb_print_m(mrb_state *mrb, mrb_value self) { mrb_int argc; mrb_value *argv; mrb_get_args(mrb, "*", &argv, &argc); for (mrb_int i=0; inomem_err) { static const char *str = "Out of memory\n"; printcstr(mrb, str, strlen(str), stdout); } else { printstrln(mrb, mrb_inspect(mrb, obj), stdout); } if (isatty(fileno(stdout))) fflush(stdout); } MRB_API void mrb_show_version(mrb_state *mrb) { printstrln(mrb, mrb_const_get(mrb, mrb_obj_value(mrb->object_class), MRB_SYM(MRUBY_DESCRIPTION)), stdout); } MRB_API void mrb_show_copyright(mrb_state *mrb) { printstrln(mrb, mrb_const_get(mrb, mrb_obj_value(mrb->object_class), MRB_SYM(MRUBY_COPYRIGHT)), stdout); } #else void mrb_core_init_printabort(mrb_state *mrb) { } mrb_value mrb_print_m(mrb_state *mrb, mrb_value self) { return mrb_nil_value(); } MRB_API void mrb_p(mrb_state *mrb, mrb_value obj) { } MRB_API void mrb_show_version(mrb_state *mrb) { } MRB_API void mrb_show_copyright(mrb_state *mrb) { } #endif nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/allocf.c0000644000000000000000000000013215077107276020066 xustar0030 mtime=1761382078.131420515 30 atime=1761382080.150411279 30 ctime=1761382108.597301835 nghttp2-1.68.0/third-party/mruby/src/allocf.c0000644000175100017510000000176415077107276020466 0ustar00runnerrunner/* ** allocf.c - default memory allocation function ** ** See Copyright Notice in mruby.h */ #include #include /* This function serves as the default memory allocation function and accepts two arguments: * * - `p`: The previous pointer to the memory region. For memory allocation, this parameter is NULL. * - `size`: The new size of the memory region to be returned. If size is 0, the memory region will be freed. * * All memory allocation from the inside of mruby uses this function. * * If you want to use your own memory allocator, you have two options: * * - provide your own version of malloc() / realloc() / free() * * - redefine mrb_basic_alloc_func() in your application. * * See doc/guides/memory.md for detail. */ void* mrb_basic_alloc_func(void *p, size_t size) { if (size == 0) { /* `free(NULL)` should be no-op */ free(p); return NULL; } else { /* `realloc(NULL, size)` should work as `malloc(size)` */ return realloc(p, size); } } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/symbol.c0000644000000000000000000000013215077107276020133 xustar0030 mtime=1761382078.136420492 30 atime=1761382080.160411233 30 ctime=1761382108.586301867 nghttp2-1.68.0/third-party/mruby/src/symbol.c0000644000175100017510000004012715077107276020527 0ustar00runnerrunner/* ** symbol.c - Symbol class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #ifndef MRB_NO_PRESYM #ifndef MRB_PRESYM_SCANNING /* const uint16_t presym_length_table[] */ /* const char * const presym_name_table[] */ # include #endif static mrb_sym presym_find(const char *name, size_t len) { if (presym_length_table[MRB_PRESYM_MAX-1] < len) return 0; mrb_sym presym_size = MRB_PRESYM_MAX; for (mrb_sym start = 0; presym_size != 0; presym_size/=2) { mrb_sym idx = start+presym_size/2; int cmp = (int)len-(int)presym_length_table[idx]; if (cmp == 0) { cmp = memcmp(name, presym_name_table[idx], len); if (cmp == 0) return idx+1; } if (0 < cmp) { start = ++idx; --presym_size; } } return 0; } static const char* presym_sym2name(mrb_sym sym, mrb_int *lenp) { if (sym > MRB_PRESYM_MAX) return NULL; if (lenp) *lenp = presym_length_table[sym-1]; return presym_name_table[sym-1]; } #endif /* MRB_NO_PRESYM */ /* ------------------------------------------------------ */ static void sym_validate_len(mrb_state *mrb, size_t len) { if (len >= UINT16_MAX) { mrb_raise(mrb, E_ARGUMENT_ERROR, "symbol length too long"); } } #ifdef MRB_USE_ALL_SYMBOLS # define SYMBOL_INLINE_P(sym) FALSE # define sym_inline_pack(name, len) 0 # define sym_inline_unpack(sym, buf, lenp) NULL #else # define SYMBOL_INLINE_P(sym) ((sym) >= (1<<20)) static const char pack_table[] = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; static mrb_sym sym_inline_pack(const char *name, size_t len) { const size_t pack_length_max = 4; mrb_sym sym = 0; if (len > pack_length_max) return 0; /* too long */ if (len == 0) return 0; /* empty string */ for (size_t i=0; i>(20-i*6) & 0x3f; if (bits == 0) break; buf[i] = pack_table[bits-1]; } buf[i] = '\0'; if (lenp) *lenp = i; return buf; } #endif #define sym_lit_p(mrb, i) (mrb->symflags[i>>3]&(1<<(i&7))) #define sym_lit_set(mrb, i) mrb->symflags[i>>3]|=(1<<(i&7)) #define sym_flags_clear(mrb, i) mrb->symflags[i>>3]&=~(1<<(i&7)) static mrb_bool sym_check(mrb_state *mrb, const char *name, size_t len, mrb_sym i) { const char *symname = mrb->symtbl[i]; size_t symlen; if (sym_lit_p(mrb, i)) { symlen = strlen(symname); } else { /* length in BER */ symlen = mrb_packed_int_decode((const uint8_t*)symname, (const uint8_t**)&symname); } if (len == symlen && memcmp(symname, name, len) == 0) { return TRUE; } return FALSE; } static mrb_sym find_symbol(mrb_state *mrb, const char *name, size_t len, uint8_t *hashp) { mrb_sym i; uint8_t hash; #ifndef MRB_NO_PRESYM /* presym */ i = presym_find(name, len); if (i > 0) return i; #endif /* inline symbol */ i = sym_inline_pack(name, len); if (i > 0) return i; hash = mrb_byte_hash((const uint8_t*)name, len); if (hashp) *hashp = hash; i = mrb->symhash[hash]; if (i == 0) return 0; for (;;) { if (sym_check(mrb, name, len, i)) { return (i+MRB_PRESYM_MAX); } uint8_t diff = mrb->symlink[i]; if (diff == 0xff) { i -= 0xff; while (i > 0) { if (sym_check(mrb, name, len, i)) { return (i+MRB_PRESYM_MAX); } i--; } return 0; } if (diff == 0) return 0; i -= diff; } return 0; } static mrb_sym sym_intern(mrb_state *mrb, const char *name, size_t len, mrb_bool lit) { mrb_sym sym; uint8_t hash; sym_validate_len(mrb, len); sym = find_symbol(mrb, name, len, &hash); if (sym > 0) return sym; /* registering a new symbol */ sym = mrb->symidx + 1; if (mrb->symcapa <= sym) { size_t symcapa = mrb->symcapa; if (symcapa == 0) symcapa = 100; else symcapa = (size_t)(symcapa * 6 / 5); mrb->symtbl = (const char**)mrb_realloc(mrb, (void*)mrb->symtbl, sizeof(char*)*symcapa); mrb->symflags = (uint8_t*)mrb_realloc(mrb, mrb->symflags, symcapa/8+1); memset(mrb->symflags+mrb->symcapa/8+1, 0, (symcapa-mrb->symcapa)/8); mrb->symlink = (uint8_t*)mrb_realloc(mrb, mrb->symlink, symcapa); mrb->symcapa = symcapa; } sym_flags_clear(mrb, sym); if ((lit || mrb_ro_data_p(name)) && name[len] == 0 && strlen(name) == len) { sym_lit_set(mrb, sym); mrb->symtbl[sym] = name; } else { uint32_t ulen = (uint32_t)len; size_t ilen = mrb_packed_int_len(ulen); char *p = (char*)mrb_malloc(mrb, len+ilen+1); mrb_packed_int_encode(ulen, (uint8_t*)p); memcpy(p+ilen, name, len); p[ilen+len] = 0; mrb->symtbl[sym] = p; } if (mrb->symhash[hash]) { mrb_sym i = sym - mrb->symhash[hash]; if (i > 0xff) mrb->symlink[sym] = 0xff; else mrb->symlink[sym] = i; } else { mrb->symlink[sym] = 0; } mrb->symhash[hash] = mrb->symidx = sym; return (sym+MRB_PRESYM_MAX); } MRB_API mrb_sym mrb_intern(mrb_state *mrb, const char *name, size_t len) { return sym_intern(mrb, name, len, FALSE); } MRB_API mrb_sym mrb_intern_static(mrb_state *mrb, const char *name, size_t len) { return sym_intern(mrb, name, len, TRUE); } MRB_API mrb_sym mrb_intern_cstr(mrb_state *mrb, const char *name) { return mrb_intern(mrb, name, strlen(name)); } MRB_API mrb_sym mrb_intern_str(mrb_state *mrb, mrb_value str) { return mrb_intern(mrb, RSTRING_PTR(str), RSTRING_LEN(str)); } MRB_API mrb_sym mrb_intern_check(mrb_state *mrb, const char *name, size_t len) { mrb_sym sym; sym_validate_len(mrb, len); sym = find_symbol(mrb, name, len, NULL); if (sym > 0) return sym; return 0; } MRB_API mrb_value mrb_check_intern(mrb_state *mrb, const char *name, size_t len) { mrb_sym sym = mrb_intern_check(mrb, name, len); if (sym == 0) return mrb_nil_value(); return mrb_symbol_value(sym); } MRB_API mrb_sym mrb_intern_check_cstr(mrb_state *mrb, const char *name) { return mrb_intern_check(mrb, name, strlen(name)); } MRB_API mrb_value mrb_check_intern_cstr(mrb_state *mrb, const char *name) { mrb_sym sym = mrb_intern_check_cstr(mrb, name); if (sym == 0) return mrb_nil_value(); return mrb_symbol_value(sym); } MRB_API mrb_sym mrb_intern_check_str(mrb_state *mrb, mrb_value str) { return mrb_intern_check(mrb, RSTRING_PTR(str), RSTRING_LEN(str)); } MRB_API mrb_value mrb_check_intern_str(mrb_state *mrb, mrb_value str) { mrb_sym sym = mrb_intern_check_str(mrb, str); if (sym == 0) return mrb_nil_value(); return mrb_symbol_value(sym); } static const char* sym2name_len(mrb_state *mrb, mrb_sym sym, char *buf, mrb_int *lenp) { if (sym == 0) goto outofsym; if (SYMBOL_INLINE_P(sym)) return sym_inline_unpack(sym, buf, lenp); #ifndef MRB_NO_PRESYM { const char *name = presym_sym2name(sym, lenp); if (name) return name; } #endif sym -= MRB_PRESYM_MAX; if (mrb->symidx < sym) { outofsym: if (lenp) *lenp = 0; return NULL; } const char *symname = mrb->symtbl[sym]; if (!sym_lit_p(mrb, sym)) { uint32_t len = mrb_packed_int_decode((const uint8_t*)symname, (const uint8_t**)&symname); if (lenp) *lenp = (mrb_int)len; } else if (lenp) { *lenp = (mrb_int)strlen(symname); } return symname; } MRB_API const char* mrb_sym_name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp) { #ifdef MRB_USE_ALL_SYMBOLS return sym2name_len(mrb, sym, NULL, lenp); #else return sym2name_len(mrb, sym, mrb->symbuf, lenp); #endif } void mrb_free_symtbl(mrb_state *mrb) { mrb_sym i, lim; for (i=1,lim=mrb->symidx+1; isymtbl[i]); } } mrb_free(mrb, (void*)mrb->symtbl); mrb_free(mrb, (void*)mrb->symlink); mrb_free(mrb, (void*)mrb->symflags); } void mrb_init_symtbl(mrb_state *mrb) { } /********************************************************************** * Document-class: Symbol * * Symbol objects represent names and some strings * inside the Ruby * interpreter. They are generated using the :name and * :"string" literals * syntax, and by the various to_sym methods. The same * Symbol object will be created for a given name or string * for the duration of a program's execution, regardless of the context * or meaning of that name. Thus if Fred is a constant in * one context, a method in another, and a class in a third, the * Symbol :Fred will be the same object in * all three contexts. * * module One * class Fred * end * $f1 = :Fred * end * module Two * Fred = 1 * $f2 = :Fred * end * def Fred() * end * $f3 = :Fred * $f1.object_id #=> 2514190 * $f2.object_id #=> 2514190 * $f3.object_id #=> 2514190 * */ /* 15.2.11.3.2 */ /* 15.2.11.3.3 */ /* * call-seq: * sym.to_s -> string * * Returns the name or string corresponding to sym. * * :fred.to_s #=> "fred" */ static mrb_value sym_to_s(mrb_state *mrb, mrb_value sym) { return mrb_sym_str(mrb, mrb_symbol(sym)); } /* * call-seq: * sym.name -> string * * Returns the name or string corresponding to sym. Unlike #to_s, the * returned string is frozen. * * :fred.name #=> "fred" * :fred.name.frozen? #=> true */ static mrb_value sym_name(mrb_state *mrb, mrb_value vsym) { mrb_sym sym = mrb_symbol(vsym); mrb_int len; const char *name = mrb_sym_name_len(mrb, sym, &len); mrb_assert(name != NULL); if (SYMBOL_INLINE_P(sym)) { return mrb_str_new_frozen(mrb, name, len); } return mrb_str_new_static_frozen(mrb, name, len); } /* 15.2.11.3.4 */ /* * Document-method: Symbol#to_sym * * call-seq: * sym.to_sym -> sym * sym.intern -> sym * * In general, to_sym returns the Symbol corresponding * to an object. As sym is already a symbol, self is returned * in this case. */ /* 15.2.11.3.5(x) */ /* * call-seq: * sym.inspect -> string * * Returns the representation of sym as a symbol literal. * * :fred.inspect #=> ":fred" */ #if __STDC__ # define SIGN_EXTEND_CHAR(c) ((signed char)(c)) #else /* not __STDC__ */ /* As in Harbison and Steele. */ # define SIGN_EXTEND_CHAR(c) ((((unsigned char)(c)) ^ 128) - 128) #endif #define is_identchar(c) (SIGN_EXTEND_CHAR(c)!=-1&&(ISALNUM(c) || (c) == '_')) static mrb_bool is_special_global_name(const char* m) { switch (*m) { case '~': case '*': case '$': case '?': case '!': case '@': case '/': case '\\': case ';': case ',': case '.': case '=': case ':': case '<': case '>': case '\"': case '&': case '`': case '\'': case '+': case '0': m++; break; case '-': m++; if (is_identchar(*m)) m += 1; break; default: if (!ISDIGIT(*m)) return FALSE; do m++; while (ISDIGIT(*m)); break; } return !*m; } static mrb_bool symname_p(const char *name) { const char *m = name; mrb_bool localid = FALSE; if (!m) return FALSE; switch (*m) { case '\0': return FALSE; case '$': if (is_special_global_name(++m)) return TRUE; goto id; case '@': if (*++m == '@') ++m; goto id; case '<': switch (*++m) { case '<': ++m; break; case '=': if (*++m == '>') m++; break; default: break; } break; case '>': switch (*++m) { case '>': case '=': ++m; break; default: break; } break; case '=': switch (*++m) { case '~': m++; break; case '=': if (*++m == '=') m++; break; default: return FALSE; } break; case '*': if (*++m == '*') m++; break; case '!': switch (*++m) { case '=': case '~': m++; } break; case '+': case '-': if (*++m == '@') m++; break; case '|': if (*++m == '|') m++; break; case '&': if (*++m == '&') m++; break; case '^': case '/': case '%': case '~': case '`': m++; break; case '[': if (*++m != ']') return FALSE; if (*++m == '=') m++; break; default: localid = !ISUPPER(*m); id: if (*m != '_' && !ISALPHA(*m)) return FALSE; while (is_identchar(*m)) m += 1; if (localid) { switch (*m) { case '!': case '?': case '=': m++; default: break; } } break; } return *m ? FALSE : TRUE; } static mrb_value sym_inspect(mrb_state *mrb, mrb_value sym) { mrb_value str; const char *name; mrb_int len; mrb_sym id = mrb_symbol(sym); char *sp; name = mrb_sym_name_len(mrb, id, &len); str = mrb_str_new(mrb, NULL, len+1); sp = RSTRING_PTR(str); sp[0] = ':'; memcpy(sp+1, name, len); mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); if (!symname_p(name) || strlen(name) != (size_t)len) { str = mrb_str_inspect(mrb, str); sp = RSTRING_PTR(str); sp[0] = ':'; sp[1] = '"'; } #ifdef MRB_UTF8_STRING if (SYMBOL_INLINE_P(id)) RSTR_SET_ASCII_FLAG(mrb_str_ptr(str)); #endif return str; } MRB_API mrb_value mrb_sym_str(mrb_state *mrb, mrb_sym sym) { mrb_int len; const char *name = mrb_sym_name_len(mrb, sym, &len); if (!name) return mrb_undef_value(); /* can't happen */ if (SYMBOL_INLINE_P(sym)) { mrb_value str = mrb_str_new(mrb, name, len); RSTR_SET_ASCII_FLAG(mrb_str_ptr(str)); return str; } return mrb_str_new_static(mrb, name, len); } static const char* sym_cstr(mrb_state *mrb, mrb_sym sym, mrb_bool dump) { mrb_int len; const char *name = mrb_sym_name_len(mrb, sym, &len); if (!name) return NULL; if (strlen(name) == (size_t)len && (!dump || symname_p(name))) { return name; } else { mrb_value str = mrb_str_new_static(mrb, name, len); str = mrb_str_dump(mrb, str); return RSTRING_PTR(str); } } MRB_API const char* mrb_sym_name(mrb_state *mrb, mrb_sym sym) { return sym_cstr(mrb, sym, FALSE); } MRB_API const char* mrb_sym_dump(mrb_state *mrb, mrb_sym sym) { return sym_cstr(mrb, sym, TRUE); } #define lesser(a,b) (((a)>(b))?(b):(a)) static mrb_value sym_cmp(mrb_state *mrb, mrb_value s1) { mrb_value s2 = mrb_get_arg1(mrb); mrb_sym sym1, sym2; if (!mrb_symbol_p(s2)) return mrb_nil_value(); sym1 = mrb_symbol(s1); sym2 = mrb_symbol(s2); if (sym1 == sym2) return mrb_fixnum_value(0); else { const char *p1, *p2; int retval; mrb_int len, len1, len2; char buf1[8], buf2[8]; p1 = sym2name_len(mrb, sym1, buf1, &len1); p2 = sym2name_len(mrb, sym2, buf2, &len2); len = lesser(len1, len2); retval = memcmp(p1, p2, len); if (retval == 0) { if (len1 == len2) return mrb_fixnum_value(0); if (len1 > len2) return mrb_fixnum_value(1); return mrb_fixnum_value(-1); } if (retval > 0) return mrb_fixnum_value(1); return mrb_fixnum_value(-1); } } void mrb_init_symbol(mrb_state *mrb) { struct RClass *sym; mrb->symbol_class = sym = mrb_define_class_id(mrb, MRB_SYM(Symbol), mrb->object_class); /* 15.2.11 */ MRB_SET_INSTANCE_TT(sym, MRB_TT_SYMBOL); mrb_undef_class_method_id(mrb, sym, MRB_SYM(new)); mrb_define_method_id(mrb, sym, MRB_SYM(to_s), sym_to_s, MRB_ARGS_NONE()); /* 15.2.11.3.3 */ mrb_define_method_id(mrb, sym, MRB_SYM(name), sym_name, MRB_ARGS_NONE()); mrb_define_method_id(mrb, sym, MRB_SYM(to_sym), mrb_obj_itself, MRB_ARGS_NONE()); /* 15.2.11.3.4 */ mrb_define_method_id(mrb, sym, MRB_SYM(inspect), sym_inspect, MRB_ARGS_NONE()); /* 15.2.11.3.5(x) */ mrb_define_method_id(mrb, sym, MRB_OPSYM(cmp), sym_cmp, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, sym, MRB_OPSYM(eq), mrb_obj_equal_m, MRB_ARGS_REQ(1)); } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/hash.c0000644000000000000000000000013215077107276017551 xustar0030 mtime=1761382078.134420501 30 atime=1761382080.155411256 30 ctime=1761382108.577301893 nghttp2-1.68.0/third-party/mruby/src/hash.c0000644000175100017510000016124115077107276020146 0ustar00runnerrunner/* ** hash.c - Hash class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include /* * === Glossary * * [EA] * Entry Array. Store `Hash' entries in insertion order. * * [AR] * Array Table Implementation. The structure of `Hash` that doesn't have a * hash table and linearly searches EA. It is used when `Hash` size <= 16. * * [IB] * Index Buckets. The buckets of hash table, where the bucket value is EA * index. The index is represented by variable length bits according to * the capacity. * * [HT] * Hash Table Implementation. The structure of `Hash` that has IB and is * searched by hash table algorithm. It is used when `Hash` size > 16. * Collision resolution strategy is open addressing method. * * [size] * The number of `Hash` entries (value of `Hash#size`). * * [slot] * The generic term for EA or IB elements. * * [active] * The state in which a slot is recognized as a `Hash` entry. * * [deleted] * The state in which a slot is marked as deleted. * * [used] * The state in which a slot is active or deleted. * * [empty] * The state in which a slot is not used. Capacity is equal to the sum of * the number of used slots and the number of empty slots. */ #define EA_N_RESERVED_INDICES 2 /* empty and deleted */ #define EA_INCREASE_RATIO 6 / 5 + 6 #define EA_MAX_INCREASE UINT16_MAX #define EA_MAX_CAPA U32(lesser(IB_MAX_CAPA - EA_N_RESERVED_INDICES, MRB_INT_MAX)) #define IB_MAX_CAPA (U32(1) << IB_MAX_BIT) #define IB_TYPE_BIT 32 #define IB_INIT_BIT ( \ ib_upper_bound_for(32) <= AR_MAX_SIZE ? 6 : \ ib_upper_bound_for(16) <= AR_MAX_SIZE ? 5 : \ 4 \ ) #define IB_MAX_BIT (IB_TYPE_BIT - 1) #define AR_DEFAULT_CAPA 4 #define AR_MAX_SIZE 16 #define H_MAX_SIZE EA_MAX_CAPA mrb_static_assert(offsetof(struct RHash, iv) == offsetof(struct RObject, iv)); mrb_static_assert(AR_MAX_SIZE < (1 << MRB_HASH_AR_EA_CAPA_BIT)); typedef struct hash_entry { mrb_value key; mrb_value val; } hash_entry; typedef struct hash_table { hash_entry *ea; #ifdef MRB_32BIT uint32_t ea_capa; uint32_t ea_n_used; #endif uint32_t ib[]; } hash_table; typedef struct index_buckets_iter { struct RHash *h; uint32_t bit; uint32_t mask; uint32_t pos; uint32_t ary_index; uint32_t ea_index; uint32_t shift1; uint32_t shift2; uint32_t step; } index_buckets_iter; /* * `c_` :: receiver class (category) * `n_` :: attribute name * `t_` :: attribute type * `p_` :: struct member path * `k_` :: macro key */ #define DEFINE_GETTER(c_, n_, t_, p_) \ MRB_INLINE t_ c_##_##n_(const struct RHash *h) {return h->p_;} #define DEFINE_SETTER(c_, n_, t_, p_) \ MRB_INLINE void c_##_set_##n_(struct RHash *h, t_ v) {h->p_ = v;} #define DEFINE_ACCESSOR(c_, n_, t_, p_) \ DEFINE_GETTER(c_, n_, t_, p_) \ DEFINE_SETTER(c_, n_, t_, p_) #define DEFINE_FLAG_GETTER(c_, n_, t_, k_) \ MRB_INLINE t_ c_##_##n_(const struct RHash *h) { \ return (t_)((h->flags & MRB_HASH_##k_##_MASK) >> MRB_HASH_##k_##_SHIFT); \ } #define DEFINE_FLAG_SETTER(c_, n_, t_, k_) \ MRB_INLINE void c_##_set_##n_(struct RHash *h, t_ v) { \ h->flags &= ~MRB_HASH_##k_##_MASK; \ h->flags |= v << MRB_HASH_##k_##_SHIFT; \ } #define DEFINE_FLAG_ACCESSOR(c_, n_, t_, k_) \ DEFINE_FLAG_GETTER(c_, n_, t_, k_) \ DEFINE_FLAG_SETTER(c_, n_, t_, k_) #define DEFINE_INCREMENTER(c_, n_) \ MRB_INLINE void c_##_inc_##n_(struct RHash *h) { \ c_##_set_##n_(h, c_##_##n_(h) + 1); \ } #define DEFINE_DECREMENTER(c_, n_) \ MRB_INLINE void c_##_dec_##n_(struct RHash *h) { \ c_##_set_##n_(h, c_##_##n_(h) - 1); \ } #define DEFINE_SWITCHER(n_, k_) \ MRB_INLINE void h_##n_##_on(struct RHash *h) { \ h->flags |= MRB_HASH_##k_; \ } \ MRB_INLINE void h_##n_##_off(struct RHash *h) { \ h->flags &= ~MRB_HASH_##k_; \ } \ MRB_INLINE mrb_bool h_##n_##_p(const struct RHash *h) { \ return (h->flags & MRB_HASH_##k_) == MRB_HASH_##k_; \ } #ifdef MRB_64BIT DEFINE_ACCESSOR(ar, ea_capa, uint32_t, ea_capa) /* ar_ea_capa ar_set_ea_capa */ DEFINE_ACCESSOR(ar, ea_n_used, uint32_t, ea_n_used) /* ar_ea_n_used ar_set_ea_n_used */ DEFINE_ACCESSOR(ht, ea_capa, uint32_t, ea_capa) /* ht_ea_capa ht_set_ea_capa */ DEFINE_ACCESSOR(ht, ea_n_used, uint32_t, ea_n_used) /* ht_ea_n_used ht_set_ea_n_used */ #else DEFINE_FLAG_ACCESSOR(ar, ea_capa, uint32_t, AR_EA_CAPA) /* ar_ea_capa ar_set_ea_capa */ DEFINE_FLAG_ACCESSOR(ar, ea_n_used, uint32_t, AR_EA_N_USED) /* ar_ea_n_used ar_set_ea_n_used */ DEFINE_ACCESSOR(ht, ea_capa, uint32_t, hsh.ht->ea_capa) /* ht_ea_capa ht_set_ea_capa */ DEFINE_ACCESSOR(ht, ea_n_used, uint32_t, hsh.ht->ea_n_used) /* ht_ea_n_used ht_set_ea_n_used */ #endif DEFINE_FLAG_ACCESSOR(ib, bit, uint32_t, IB_BIT) /* ib_bit ib_set_bit */ DEFINE_ACCESSOR(ar, size, uint32_t, size) /* ar_size ar_set_size */ DEFINE_ACCESSOR(ar, ea, hash_entry*, hsh.ea) /* ar_ea ar_set_ea */ DEFINE_DECREMENTER(ar, size) /* ar_dec_size */ DEFINE_ACCESSOR(ht, size, uint32_t, size) /* ht_size ht_set_size */ DEFINE_ACCESSOR(ht, ea, hash_entry*, hsh.ht->ea) /* ht_ea ht_set_ea */ DEFINE_GETTER(ht, ib, uint32_t*, hsh.ht->ib) /* ht_ib */ DEFINE_INCREMENTER(ht, size) /* ht_inc_size */ DEFINE_DECREMENTER(ht, size) /* ht_dec_size */ DEFINE_GETTER(h, size, uint32_t, size) /* h_size */ DEFINE_ACCESSOR(h, ht, hash_table*, hsh.ht) /* h_ht h_set_ht */ DEFINE_SWITCHER(ht, HT) /* h_ht_on h_ht_off h_ht_p */ #define EA_EACH_USED(ea, n_used, entry_var) \ for (hash_entry *entry_var = (ea), *ea_end__ = (entry_var) + (n_used); \ entry_var < ea_end__; \ entry_var++) #define EA_EACH(ea, size, entry_var) \ for (uint32_t ea_size__ = (size); ea_size__; ea_size__ = 0) \ for (hash_entry *entry_var = (ea); \ ea_size__ && (entry_var = entry_skip_deleted(entry_var), TRUE); \ entry_var++, ea_size__--) #define IB_CYCLE_BY_KEY(mrb, h, key, it_var) \ for (index_buckets_iter it_var[1] = { ib_it_init(mrb, h, key) }; \ (ib_it_next(it_var), TRUE); \ /* do nothing */) #define IB_FIND_BY_KEY(mrb, h, key, it_var) \ for (index_buckets_iter it_var[1] = { ib_it_init(mrb, h, key) }; \ ib_it_find_by_key(mrb, it_var, key); \ it_var[0].h = NULL) #define H_EACH(h, entry_var) \ EA_EACH((h_ar_p(h) ? ar_ea(h) : ht_ea(h)), \ (h_ar_p(h) ? ar_size(h) : ht_size(h)), \ entry_var) /* * In `H_CHECK_MODIFIED()`, in the case of `MRB_NO_BOXING`, `ht_ea()` or * `ht_ea_capa()` for AR may read uninitialized area (#5332). Therefore, do * not use those macros for AR in `MRB_NO_BOXING` (but in the case of * `MRB_64BIT`, `ht_ea_capa()` is the same as `ar_ea_capa()`, so use it). */ #ifdef MRB_NO_BOXING # define H_CHECK_MODIFIED_USE_HT_EA_FOR_AR FALSE # ifdef MRB_64BIT # define H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR TRUE # else # define H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR FALSE # endif /* MRB_64BIT */ #else # define H_CHECK_MODIFIED_USE_HT_EA_FOR_AR TRUE # define H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR TRUE /* * `H_CHECK_MODIFIED` raises an exception when a dangerous modification is * made to `h` by executing `code`. * * `H_CHECK_MODIFIED` macro is not called if `h->hsh.ht` (`h->hsh.ea`) is `NULL` * (`Hash` size is zero). And because the `hash_entry` is rather large, * `h->hsh.ht->ea` and `h->hsh.ht->ea_capa` are able to be safely accessed even for * AR. This nature is used to eliminate branch of AR or HT. * * `HT_ASSERT_SAFE_READ` checks if members can be accessed according to its * assumptions. */ # define HT_ASSERT_SAFE_READ(attr_name) \ mrb_static_assert( \ offsetof(hash_table, attr_name) + sizeof(((hash_table*)0)->attr_name) <= \ sizeof(hash_entry)) HT_ASSERT_SAFE_READ(ea); # ifdef MRB_32BIT HT_ASSERT_SAFE_READ(ea_capa); # endif # undef HT_ASSERT_SAFE_READ #endif /* MRB_NO_BOXING */ /* * `H_CHECK_MODIFIED` raises an exception when a dangerous modification is * made to `h` by executing code block. */ #define H_CHECK_MODIFIED(mrb, h) \ for (struct h_check_modified h_checker__ = h_check_modified_init(mrb, h); \ h_checker__.tbl; \ h_check_modified_validate(mrb, &h_checker__, h), h_checker__.tbl = NULL) #define U32(v) ((uint32_t)(v)) #define h_ar_p(h) (!h_ht_p(h)) #define h_ar_on(h) h_ht_off(h) #define lesser(a, b) ((a) < (b) ? (a) : (b)) #define RHASH_IFNONE(hash) mrb_iv_get(mrb, (hash), MRB_SYM(ifnone)) #define RHASH_PROCDEFAULT(hash) RHASH_IFNONE(hash) static uint32_t ib_upper_bound_for(uint32_t capa); static uint32_t ib_bit_to_capa(uint32_t bit); static hash_entry *ib_it_entry(index_buckets_iter *it); static void ht_init( mrb_state *mrb, struct RHash *h, uint32_t size, hash_entry *ea, uint32_t ea_capa, hash_table *ht, uint32_t ib_bit); static void ht_set(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value val); static uint32_t next_power2(uint32_t v) { mrb_assert(v != 0); #ifdef __GNUC__ return U32(1) << ((sizeof(unsigned) * CHAR_BIT) - __builtin_clz(v)); #else v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; #endif } struct h_check_modified { uint32_t flags; void *tbl; uint32_t ht_ea_capa; hash_entry *ht_ea; }; #define H_CHECK_MODIFIED_FLAGS_MASK (MRB_HASH_HT | MRB_HASH_IB_BIT_MASK | MRB_HASH_AR_EA_CAPA_MASK) static struct h_check_modified h_check_modified_init(mrb_state *mrb, struct RHash *h) { mrb_assert(h->hsh.ht); struct h_check_modified checker; checker.flags = h->flags & H_CHECK_MODIFIED_FLAGS_MASK; checker.tbl = h->hsh.ht; checker.ht_ea_capa = (H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR || h_ht_p(h)) ? ht_ea_capa(h) : 0; checker.ht_ea = (H_CHECK_MODIFIED_USE_HT_EA_FOR_AR || h_ht_p(h)) ? ht_ea(h) : NULL; return checker; } static void h_check_modified_validate(mrb_state *mrb, struct h_check_modified *checker, struct RHash *h) { if (checker->flags != (h->flags & H_CHECK_MODIFIED_FLAGS_MASK) || checker->tbl != h->hsh.ht || ((H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR || h_ht_p(h)) && checker->ht_ea_capa != ht_ea_capa(h)) || ((H_CHECK_MODIFIED_USE_HT_EA_FOR_AR || h_ht_p(h)) && checker->ht_ea != ht_ea(h))) { mrb_raise(mrb, E_RUNTIME_ERROR, "hash modified"); } } static uint32_t obj_hash_code(mrb_state *mrb, mrb_value key, struct RHash *h) { enum mrb_vtype tt = mrb_type(key); uint32_t hash_code; mrb_value hash_code_obj; switch (tt) { case MRB_TT_STRING: hash_code = mrb_str_hash(mrb, key); break; case MRB_TT_TRUE: case MRB_TT_FALSE: case MRB_TT_SYMBOL: hash_code = U32(mrb_fixnum(key)); break; case MRB_TT_INTEGER: if (mrb_fixnum_p(key)) { hash_code = U32(mrb_fixnum(key)); break; } #ifndef MRB_NO_FLOAT /* fall through */ case MRB_TT_FLOAT: #endif hash_code = U32(mrb_obj_id(key)); break; default: H_CHECK_MODIFIED(mrb, h) { hash_code_obj = mrb_funcall_argv(mrb, key, MRB_SYM(hash), 0, NULL); } hash_code = U32(tt) ^ U32(mrb_integer(hash_code_obj)); break; } return hash_code ^ (hash_code << 2) ^ (hash_code >> 2); } static mrb_bool obj_eql(mrb_state *mrb, mrb_value a, mrb_value b, struct RHash *h) { mrb_bool eql; switch (mrb_type(a)) { case MRB_TT_STRING: return mrb_str_equal(mrb, a, b); case MRB_TT_SYMBOL: if (!mrb_symbol_p(b)) return FALSE; return mrb_symbol(a) == mrb_symbol(b); case MRB_TT_INTEGER: if (!mrb_integer_p(b)) return FALSE; return mrb_integer(a) == mrb_integer(b); #ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: if (!mrb_float_p(b)) return FALSE; return mrb_float(a) == mrb_float(b); #endif default: H_CHECK_MODIFIED(mrb, h) {eql = mrb_eql(mrb, a, b);} return eql; } } static inline mrb_bool entry_deleted_p(const hash_entry* entry) { return mrb_undef_p(entry->key); } static void entry_delete(hash_entry* entry) { entry->key = mrb_undef_value(); } static hash_entry* entry_skip_deleted(hash_entry *e) { for (; entry_deleted_p(e); e++) ; return e; } static uint32_t ea_next_capa_for(uint32_t size, uint32_t max_capa) { if (size < AR_DEFAULT_CAPA) { return AR_DEFAULT_CAPA; } else { /* * For 32-bit CPU, the theoretical value of maximum EA capacity is * `UINT32_MAX / sizeof (hash_entry)`. At this time, if * `EA_INCREASE_RATIO` is the current value, 32-bit range will not be * exceeded during the calculation of `capa`, so `size_t` is used. */ size_t capa = (size_t)size * EA_INCREASE_RATIO, inc = capa - size; if (EA_MAX_INCREASE < inc) capa = size + EA_MAX_INCREASE; return capa <= max_capa ? U32(capa) : max_capa; } } static hash_entry* ea_resize(mrb_state *mrb, hash_entry *ea, uint32_t capa) { return (hash_entry*)mrb_realloc(mrb, ea, sizeof(hash_entry) * capa); } static void ea_compress(hash_entry *ea, uint32_t n_used) { hash_entry *w_entry = ea; EA_EACH_USED(ea, n_used, r_entry) { if (entry_deleted_p(r_entry)) continue; if (r_entry != w_entry) *w_entry = *r_entry; w_entry++; } } /* * Increase or decrease capacity of `ea` to a standard size that can * accommodate `*capap + 1` entries (but, not exceed `max_capa`). Set the * changed capacity to `*capap` and return a pointer to `mrb_realloc`ed EA. */ static hash_entry* ea_adjust(mrb_state *mrb, hash_entry *ea, uint32_t *capap, uint32_t max_capa) { *capap = ea_next_capa_for(*capap, max_capa); return ea_resize(mrb, ea, *capap); } static hash_entry* ea_dup(mrb_state *mrb, const hash_entry *ea, uint32_t capa) { size_t byte_size = sizeof(hash_entry) * capa; hash_entry *new_ea = (hash_entry*)mrb_malloc(mrb, byte_size); return (hash_entry*)memcpy(new_ea, ea, byte_size); } static hash_entry* ea_get_by_key(mrb_state *mrb, hash_entry *ea, uint32_t size, mrb_value key, struct RHash *h) { EA_EACH(ea, size, entry) { if (obj_eql(mrb, key, entry->key, h)) return entry; } return NULL; } static hash_entry* ea_get(hash_entry *ea, uint32_t index) { return &ea[index]; } static void ea_set(hash_entry *ea, uint32_t index, mrb_value key, mrb_value val) { ea[index].key = key; ea[index].val = val; } static void ar_init(struct RHash *h, uint32_t size, hash_entry *ea, uint32_t ea_capa, uint32_t ea_n_used) { h_ar_on(h); ar_set_size(h, size); ar_set_ea(h, ea); ar_set_ea_capa(h, ea_capa); ar_set_ea_n_used(h, ea_n_used); } static void ar_free(mrb_state *mrb, struct RHash *h) { mrb_free(mrb, ar_ea(h)); } static void ar_adjust_ea(mrb_state *mrb, struct RHash *h, uint32_t size, uint32_t max_ea_capa) { uint32_t ea_capa = size; hash_entry *ea = ea_adjust(mrb, ar_ea(h), &ea_capa, max_ea_capa); ar_set_ea(h, ea); ar_set_ea_capa(h, ea_capa); } static void ar_compress(mrb_state *mrb, struct RHash *h) { uint32_t size = ar_size(h); ea_compress(ar_ea(h), ar_ea_n_used(h)); ar_set_ea_n_used(h, size); ar_adjust_ea(mrb, h, size, lesser(ar_ea_capa(h), AR_MAX_SIZE)); } static mrb_bool ar_get(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp) { EA_EACH(ar_ea(h), ar_size(h), entry) { if (!obj_eql(mrb, key, entry->key, h)) continue; *valp = entry->val; return TRUE; } return FALSE; } static void ar_set(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value val) { uint32_t size = ar_size(h); hash_entry *entry; if ((entry = ea_get_by_key(mrb, ar_ea(h), size, key, h))) { entry->val = val; } else { uint32_t ea_capa = ar_ea_capa(h), ea_n_used = ar_ea_n_used(h); if (ea_capa == ea_n_used) { if (size == ea_n_used) { if (size == AR_MAX_SIZE) { ht_init(mrb, h, size, ar_ea(h), ea_capa, NULL, IB_INIT_BIT); ht_set(mrb, h, key, val); return; } else { ar_adjust_ea(mrb, h, size, AR_MAX_SIZE); } } else { ar_compress(mrb, h); ea_n_used = size; } } ea_set(ar_ea(h), ea_n_used, key, val); ar_set_size(h, ++size); ar_set_ea_n_used(h, ++ea_n_used); } } static mrb_bool ar_delete(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp) { hash_entry *entry = ea_get_by_key(mrb, ar_ea(h), ar_size(h), key, h); if (!entry) return FALSE; *valp = entry->val; entry_delete(entry); ar_dec_size(h); return TRUE; } static void ar_shift(mrb_state *mrb, struct RHash *h, mrb_value *keyp, mrb_value *valp) { uint32_t size = ar_size(h); EA_EACH(ar_ea(h), size, entry) { *keyp = entry->key; *valp = entry->val; entry_delete(entry); ar_set_size(h, --size); return; } } static void ar_rehash(mrb_state *mrb, struct RHash *h) { /* see comments in `h_rehash` */ uint32_t size = ar_size(h), w_size = 0, ea_capa = ar_ea_capa(h); hash_entry *ea = ar_ea(h), *w_entry; EA_EACH(ea, size, r_entry) { if ((w_entry = ea_get_by_key(mrb, ea, w_size, r_entry->key, h))) { w_entry->val = r_entry->val; ar_set_size(h, --size); entry_delete(r_entry); } else { if (w_size != U32(r_entry - ea)) { ea_set(ea, w_size, r_entry->key, r_entry->val); entry_delete(r_entry); } w_size++; } } mrb_assert(size == w_size); ar_set_ea_n_used(h, size); ar_adjust_ea(mrb, h, size, ea_capa); } static uint32_t ib_it_pos_for(index_buckets_iter *it, uint32_t v) { return v & it->mask; } static uint32_t ib_it_empty_value(const index_buckets_iter *it) { return it->mask; } static uint32_t ib_it_deleted_value(const index_buckets_iter *it) { return it->mask - 1; } static mrb_bool ib_it_empty_p(const index_buckets_iter *it) { return it->ea_index == ib_it_empty_value(it); } static mrb_bool ib_it_deleted_p(const index_buckets_iter *it) { return it->ea_index == ib_it_deleted_value(it); } static mrb_bool ib_it_active_p(const index_buckets_iter *it) { return it->ea_index < ib_it_deleted_value(it); } static index_buckets_iter ib_it_init(mrb_state *mrb, struct RHash *h, mrb_value key) { index_buckets_iter it; it.h = h; it.bit = ib_bit(h); it.mask = ib_bit_to_capa(it.bit) - 1; it.pos = ib_it_pos_for(&it, obj_hash_code(mrb, key, h)); it.step = 0; return it; } static void ib_it_next(index_buckets_iter *it) { /* * [IB image] * * ary_index(1) --. * \ .-- shift1(3) .-- shift2(29) * pos(6) --. \ / / * View | \ \ <-o-> <----------o----------> * -------- +---------------------\----\--+-----------------------------+----- * array | 0 `--. `-|--- o 1 | ... * +---------+---------+-----+\--+-----+---------+---------+---+----- * buckets | 0 | 1 | ... | o 6 | 7 | 8 | ... * +---------+---------+-----+=========+---------+---------+--------- * bit set |1 1 1 0 0|0 0 0 1 1| ... |0 1 0 1 1|0 1 1 1 0|0 1 0 1 0| ... * +---------+---------+-----+========*+---------+---------+--------- * <---o---> \ * \ `-- bit_pos(34) * `-- bit(5) */ /* Slide to handle as `capa == 32` to avoid 64-bit operations */ uint32_t slid_pos = it->pos & (IB_TYPE_BIT - 1); uint32_t slid_bit_pos = it->bit * (slid_pos + 1) - 1; uint32_t slid_ary_index = slid_bit_pos / IB_TYPE_BIT; it->ary_index = slid_ary_index + it->pos / IB_TYPE_BIT * it->bit; it->shift2 = (slid_ary_index + 1) * IB_TYPE_BIT - slid_bit_pos - 1; it->ea_index = (ht_ib(it->h)[it->ary_index] >> it->shift2) & it->mask; if (IB_TYPE_BIT - it->bit < it->shift2) { it->shift1 = IB_TYPE_BIT - it->shift2; it->ea_index |= (ht_ib(it->h)[it->ary_index - 1] << it->shift1) & it->mask; } else { it->shift1 = 0; } it->pos = ib_it_pos_for(it, it->pos + (++it->step)); } static mrb_bool ib_it_find_by_key(mrb_state *mrb, index_buckets_iter *it, mrb_value key) { if (!it->h) return FALSE; for (;;) { ib_it_next(it); if (ib_it_empty_p(it)) return FALSE; if (!ib_it_deleted_p(it) && obj_eql(mrb, key, ib_it_entry(it)->key, it->h)) { return TRUE; } } } static uint32_t ib_it_get(const index_buckets_iter *it) { return it->ea_index; } static void ib_it_set(index_buckets_iter *it, uint32_t ea_index) { uint32_t mask, i; it->ea_index = ea_index; if (it->shift1) { i = it->ary_index - 1; mask = it->mask >> it->shift1; ht_ib(it->h)[i] = (ht_ib(it->h)[i] & ~mask) | (ea_index >> it->shift1); } i = it->ary_index; mask = it->mask << it->shift2; ht_ib(it->h)[i] = (ht_ib(it->h)[i] & ~mask) | (ea_index << it->shift2); } static void ib_it_delete(index_buckets_iter *it) { ib_it_set(it, ib_it_deleted_value(it)); } static hash_entry* ib_it_entry(index_buckets_iter *it) { return ea_get(ht_ea(it->h), it->ea_index); } static uint32_t ib_capa_to_bit(uint32_t capa) { #ifdef __GNUC__ return U32(__builtin_ctz(capa)); #else /* http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn */ static const uint32_t MultiplyDeBruijnBitPosition2[] = { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 }; return MultiplyDeBruijnBitPosition2[U32(capa * 0x077CB531U) >> 27]; #endif } static uint32_t ib_bit_to_capa(uint32_t bit) { return U32(1) << bit; } static uint32_t ib_upper_bound_for(uint32_t capa) { return (capa >> 2) | (capa >> 1); /* 3/4 */ } static uint32_t ib_bit_for(uint32_t size) { uint32_t capa = next_power2(size); if (capa != IB_MAX_CAPA && ib_upper_bound_for(capa) < size) capa *= 2; return ib_capa_to_bit(capa); } static uint32_t ib_byte_size_for(uint32_t ib_bit) { mrb_assert(IB_INIT_BIT <= ib_bit); uint32_t ary_size = IB_INIT_BIT == 4 ? ib_bit_to_capa(ib_bit) * 2 / IB_TYPE_BIT * ib_bit / 2 : ib_bit_to_capa(ib_bit) / IB_TYPE_BIT * ib_bit; return U32(sizeof(uint32_t) * ary_size); } static void ib_init(mrb_state *mrb, struct RHash *h, uint32_t ib_bit, size_t ib_byte_size) { hash_entry *ea = ht_ea(h); memset(ht_ib(h), 0xff, ib_byte_size); ib_set_bit(h, ib_bit); EA_EACH_USED(ea, ht_ea_n_used(h), entry) { IB_CYCLE_BY_KEY(mrb, h, entry->key, it) { if (!ib_it_empty_p(it)) continue; ib_it_set(it, U32(entry - ea)); break; } } } static void ht_init(mrb_state *mrb, struct RHash *h, uint32_t size, hash_entry *ea, uint32_t ea_capa, hash_table *ht, uint32_t ib_bit) { size_t ib_byte_size = ib_byte_size_for(ib_bit); size_t ht_byte_size = sizeof(hash_table) + ib_byte_size; ht = (hash_table*)mrb_realloc(mrb, ht, ht_byte_size); h_ht_on(h); h_set_ht(h, ht); ht_set_size(h, size); ht_set_ea(h, ea); ht_set_ea_capa(h, ea_capa); ht_set_ea_n_used(h, size); ib_init(mrb, h, ib_bit, ib_byte_size); } static void ht_free(mrb_state *mrb, struct RHash *h) { mrb_free(mrb, ht_ea(h)); mrb_free(mrb, h_ht(h)); } static hash_table* ht_dup(mrb_state *mrb, const struct RHash *h) { size_t ib_byte_size = ib_byte_size_for(ib_bit(h)); size_t ht_byte_size = sizeof(hash_table) + ib_byte_size; hash_table *new_ht = (hash_table*)mrb_malloc(mrb, ht_byte_size); return (hash_table*)memcpy(new_ht, h_ht(h), ht_byte_size); } static void ht_adjust_ea(mrb_state *mrb, struct RHash *h, uint32_t size, uint32_t max_ea_capa) { uint32_t ea_capa = size; hash_entry *ea = ea_adjust(mrb, ht_ea(h), &ea_capa, max_ea_capa); ht_set_ea(h, ea); ht_set_ea_capa(h, ea_capa); } static void ht_to_ar(mrb_state *mrb, struct RHash *h) { uint32_t size = ht_size(h), ea_capa = size; hash_entry *ea = ht_ea(h); ea_compress(ea, ht_ea_n_used(h)); ea = ea_adjust(mrb, ea, &ea_capa, AR_MAX_SIZE); mrb_free(mrb, h_ht(h)); ar_init(h, size, ea, ea_capa, size); } static mrb_bool ht_get(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp) { IB_FIND_BY_KEY(mrb, h, key, it) { *valp = ib_it_entry(it)->val; return TRUE; } return FALSE; } static void ht_set_as_ar(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value val) { ht_to_ar(mrb, h); ar_set(mrb, h, key, val); } static void ht_set(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value val) { uint32_t size = ht_size(h); uint32_t ib_bit_width = ib_bit(h), ib_capa = ib_bit_to_capa(ib_bit_width); if (ib_upper_bound_for(ib_capa) <= size) { if (size != ht_ea_n_used(h)) ea_compress(ht_ea(h), ht_ea_n_used(h)); ht_init(mrb, h, size, ht_ea(h), ht_ea_capa(h), h_ht(h), ++ib_bit_width); } else if (size != ht_ea_n_used(h)) { if (ib_capa - EA_N_RESERVED_INDICES <= ht_ea_n_used(h)) goto compress; if (ht_ea_capa(h) == ht_ea_n_used(h)) { if (size <= AR_MAX_SIZE) { ht_set_as_ar(mrb, h, key, val); return; } if (ea_next_capa_for(size, EA_MAX_CAPA) <= ht_ea_capa(h)) { compress: ea_compress(ht_ea(h), ht_ea_n_used(h)); ht_adjust_ea(mrb, h, size, ht_ea_capa(h)); ht_init(mrb, h, size, ht_ea(h), ht_ea_capa(h), h_ht(h), ib_bit_width); } } } mrb_assert(ht_size(h) < ib_bit_to_capa(ib_bit(h))); IB_CYCLE_BY_KEY(mrb, h, key, it) { if (ib_it_active_p(it)) { if (!obj_eql(mrb, key, ib_it_entry(it)->key, h)) continue; ib_it_entry(it)->val = val; } else if (ib_it_deleted_p(it)) { continue; } else { uint32_t ea_n_used = ht_ea_n_used(h); if (ea_n_used == H_MAX_SIZE) { mrb_assert(ht_size(h) == ea_n_used); mrb_raise(mrb, E_ARGUMENT_ERROR, "hash too big"); } if (ea_n_used == ht_ea_capa(h)) ht_adjust_ea(mrb, h, ea_n_used, EA_MAX_CAPA); ib_it_set(it, ea_n_used); ea_set(ht_ea(h), ea_n_used, key, val); ht_inc_size(h); ht_set_ea_n_used(h, ++ea_n_used); } return; } } static mrb_bool ht_delete(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp) { IB_FIND_BY_KEY(mrb, h, key, it) { hash_entry *entry = ib_it_entry(it); *valp = entry->val; ib_it_delete(it); entry_delete(entry); ht_dec_size(h); return TRUE; } return FALSE; } static void ht_shift(mrb_state *mrb, struct RHash *h, mrb_value *keyp, mrb_value *valp) { hash_entry *ea = ht_ea(h); EA_EACH(ea, ht_size(h), entry) { IB_CYCLE_BY_KEY(mrb, h, entry->key, it) { if (ib_it_get(it) != U32(entry - ea)) continue; *keyp = entry->key; *valp = entry->val; ib_it_delete(it); entry_delete(entry); ht_dec_size(h); return; } } } static void ht_rehash(mrb_state *mrb, struct RHash *h) { /* see comments in `h_rehash` */ uint32_t size = ht_size(h); if (size <= AR_MAX_SIZE) { ht_to_ar(mrb, h); ar_rehash(mrb, h); return; } uint32_t w_size = 0, ea_capa = ht_ea_capa(h); hash_entry *ea = ht_ea(h); ht_init(mrb, h, 0, ea, ea_capa, h_ht(h), ib_bit_for(size)); ht_set_size(h, size); ht_set_ea_n_used(h, ht_ea_n_used(h)); EA_EACH(ea, size, r_entry) { IB_CYCLE_BY_KEY(mrb, h, r_entry->key, it) { if (ib_it_active_p(it)) { if (!obj_eql(mrb, r_entry->key, ib_it_entry(it)->key, h)) continue; ib_it_entry(it)->val = r_entry->val; ht_set_size(h, --size); entry_delete(r_entry); } else { if (w_size != U32(r_entry - ea)) { ea_set(ea, w_size, r_entry->key, r_entry->val); entry_delete(r_entry); } ib_it_set(it, w_size++); } break; } } mrb_assert(size == w_size); ht_set_ea_n_used(h, size); size <= AR_MAX_SIZE ? ht_to_ar(mrb, h) : ht_adjust_ea(mrb, h, size, ea_capa); } static mrb_value h_key_for(mrb_state *mrb, mrb_value key) { if (mrb_string_p(key) && !mrb_frozen_p(mrb_str_ptr(key))) { key = mrb_str_dup(mrb, key); mrb_str_ptr(key)->frozen = 1; } return key; } static struct RHash* h_alloc(mrb_state *mrb) { return MRB_OBJ_ALLOC(mrb, MRB_TT_HASH, mrb->hash_class); } static void h_init(struct RHash *h) { ar_init(h, 0, NULL, 0, 0); } static void h_free_table(mrb_state *mrb, struct RHash *h) { (h_ar_p(h) ? ar_free : ht_free)(mrb, h); } static void h_clear(mrb_state *mrb, struct RHash *h) { h_free_table(mrb, h); h_init(h); } static mrb_bool h_get(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp) { return (h_ar_p(h) ? ar_get : ht_get)(mrb, h, key, valp); } static void h_set(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value val) { (h_ar_p(h) ? ar_set : ht_set)(mrb, h, key, val); } static mrb_bool h_delete(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp) { return (h_ar_p(h) ? ar_delete : ht_delete)(mrb, h, key, valp); } /* find first element in the table, and remove it. */ static void h_shift(mrb_state *mrb, struct RHash *h, mrb_value *keyp, mrb_value *valp) { (h_ar_p(h) ? ar_shift : ht_shift)(mrb, h, keyp, valp); } static void h_rehash(mrb_state *mrb, struct RHash *h) { /* * ==== Comments common to `ar_rehash` and `ht_rehash` * * - Because reindex (such as elimination of duplicate keys) must be * guaranteed, it is necessary to set one by one. * * - To prevent EA from breaking if an exception occurs in the middle, * delete the slot before moving when moving the entry, and update size * at any time when overwriting. */ (h_size(h) == 0 ? h_clear : h_ar_p(h) ? ar_rehash : ht_rehash)(mrb, h); } static void h_replace(mrb_state *mrb, struct RHash *h, struct RHash *orig_h) { uint32_t size = h_size(orig_h); if (size == 0) { h_clear(mrb, h); } else if (h_ar_p(orig_h)) { uint32_t ea_capa = ar_ea_capa(orig_h); hash_entry *ea = ea_dup(mrb, ar_ea(orig_h), ea_capa); h_free_table(mrb, h); ar_init(h, size, ea, ea_capa, ar_ea_n_used(orig_h)); } else { /* HT */ uint32_t ea_capa = ht_ea_capa(orig_h); hash_entry *ea = ea_dup(mrb, ht_ea(orig_h), ea_capa); hash_table *ht = ht_dup(mrb, orig_h); h_free_table(mrb, h); h_ht_on(h); h_set_ht(h, ht); ht_set_size(h, size); ht_set_ea(h, ea); #ifdef MRB_64BIT ht_set_ea_capa(h, ea_capa); ht_set_ea_n_used(h, ht_ea_n_used(orig_h)); #endif ib_set_bit(h, ib_bit(orig_h)); } } size_t mrb_gc_mark_hash(mrb_state *mrb, struct RHash *h) { H_EACH(h, entry) { mrb_gc_mark_value(mrb, entry->key); mrb_gc_mark_value(mrb, entry->val); } return h_size(h) * 2; } void mrb_gc_free_hash(mrb_state *mrb, struct RHash *h) { h_free_table(mrb, h); } size_t mrb_hash_memsize(mrb_value self) { struct RHash *h = mrb_hash_ptr(self); return mrb_obj_iv_tbl_memsize(self) + (h_ar_p(h) ? (ar_ea_capa(h) * sizeof(hash_entry)) : (ht_ea_capa(h) * sizeof(hash_entry) + sizeof(hash_table) + ib_byte_size_for(ib_bit(h)))); } /* Iterates over the key/value pairs. */ MRB_API void mrb_hash_foreach(mrb_state *mrb, struct RHash *h, mrb_hash_foreach_func *func, void *data) { H_EACH(h, entry) { int n; H_CHECK_MODIFIED(mrb, h) { n = func(mrb, entry->key, entry->val, data); } if (n != 0) return; } } mrb_value mrb_hash_first_key(mrb_state *mrb, mrb_value h) { H_EACH(mrb_hash_ptr(h), entry) { return entry->key; } return mrb_nil_value(); } MRB_API mrb_value mrb_hash_new(mrb_state *mrb) { struct RHash *h = h_alloc(mrb); return mrb_obj_value(h); } /* * Set the capacity of EA and IB to minimum capacity (and appropriate load * factor) that does not cause expansion when inserting `capa` elements. */ MRB_API mrb_value mrb_hash_new_capa(mrb_state *mrb, mrb_int capa) { if (capa < 0 || EA_MAX_CAPA < capa) { mrb_raise(mrb, E_ARGUMENT_ERROR, "hash too big"); return mrb_nil_value(); /* not reached */ } else if (capa == 0) { return mrb_hash_new(mrb); } else { uint32_t size = U32(capa); struct RHash *h = h_alloc(mrb); hash_entry *ea = ea_resize(mrb, NULL, size); if (size <= AR_MAX_SIZE) { ar_init(h, 0, ea, size, 0); } else { ht_init(mrb, h, 0, ea, size, NULL, ib_bit_for(size)); } return mrb_obj_value(h); } } static mrb_value mrb_hash_default(mrb_state *mrb, mrb_value hash); static void hash_modify(mrb_state *mrb, mrb_value hash) { mrb_check_frozen(mrb, mrb_hash_ptr(hash)); } static mrb_value hash_default(mrb_state *mrb, mrb_value hash, mrb_value key) { if (MRB_RHASH_DEFAULT_P(hash)) { if (MRB_RHASH_PROCDEFAULT_P(hash)) { return mrb_funcall_id(mrb, RHASH_PROCDEFAULT(hash), MRB_SYM(call), 2, hash, key); } else { return RHASH_IFNONE(hash); } } return mrb_nil_value(); } static void hash_replace(mrb_state *mrb, mrb_value self, mrb_value orig) { struct RHash *h = mrb_hash_ptr(self), *orig_h = mrb_hash_ptr(orig); uint32_t mask = MRB_HASH_DEFAULT | MRB_HASH_PROC_DEFAULT; mrb_sym name; h_replace(mrb, h, orig_h); name = MRB_SYM(ifnone); if (orig_h->flags & MRB_HASH_DEFAULT) { mrb_iv_set(mrb, self, name, mrb_iv_get(mrb, orig, name)); } else { mrb_iv_remove(mrb, self, name); } h->flags &= ~mask; h->flags |= orig_h->flags & mask; } static mrb_value mrb_hash_init_copy(mrb_state *mrb, mrb_value self) { mrb_value orig; mrb_get_args(mrb, "H", &orig); hash_modify(mrb, self); if (mrb_hash_ptr(self) != mrb_hash_ptr(orig)) hash_replace(mrb, self, orig); return self; } MRB_API mrb_value mrb_hash_dup(mrb_state *mrb, mrb_value self) { struct RHash* copy_h = h_alloc(mrb); mrb_value copy = mrb_obj_value(copy_h); copy_h->c = mrb_hash_ptr(self)->c; hash_replace(mrb, copy, self); return copy; } MRB_API mrb_value mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key) { mrb_value val; mrb_sym mid; if (h_get(mrb, mrb_hash_ptr(hash), key, &val)) { return val; } mid = MRB_SYM(default); if (mrb_func_basic_p(mrb, hash, mid, mrb_hash_default)) { return hash_default(mrb, hash, key); } /* xxx mrb_funcall_tailcall(mrb, hash, "default", 1, key); */ return mrb_funcall_argv(mrb, hash, mid, 1, &key); } MRB_API mrb_value mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def) { mrb_value val; if (h_get(mrb, mrb_hash_ptr(hash), key, &val)) { return val; } /* not found */ return def; } MRB_API void mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val) { hash_modify(mrb, hash); key = h_key_for(mrb, key); h_set(mrb, mrb_hash_ptr(hash), key, val); mrb_field_write_barrier_value(mrb, mrb_basic_ptr(hash), key); mrb_field_write_barrier_value(mrb, mrb_basic_ptr(hash), val); } static void hash_set_default_proc(mrb_state *mrb, mrb_value hash, mrb_value proc) { struct RProc *p = mrb_proc_ptr(proc); if (MRB_PROC_STRICT_P(p)) { mrb_int n = mrb_proc_arity(p); if (n != 2 && (n >= 0 || n < -3)) { if (n < 0) n = -n-1; mrb_raisef(mrb, E_TYPE_ERROR, "default_proc takes two arguments (2 for %d)", n); } } mrb_iv_set(mrb, hash, MRB_SYM(ifnone), proc); RHASH(hash)->flags |= MRB_HASH_PROC_DEFAULT; RHASH(hash)->flags |= MRB_HASH_DEFAULT; } /* 15.2.13.4.16 */ /* * call-seq: * Hash.new -> new_hash * Hash.new(obj) -> new_hash * Hash.new {|hash, key| block } -> new_hash * * Returns a new, empty hash. If this hash is subsequently accessed by * a key that doesn't correspond to a hash entry, the value returned * depends on the style of new used to create the hash. In * the first form, the access returns nil. If * obj is specified, this single object will be used for * all default values. If a block is specified, it will be * called with the hash object and the key, and should return the * default value. It is the block's responsibility to store the value * in the hash if required. * * h = Hash.new("Go Fish") * h["a"] = 100 * h["b"] = 200 * h["a"] #=> 100 * h["c"] #=> "Go Fish" * # The following alters the single default object * h["c"].upcase! #=> "GO FISH" * h["d"] #=> "GO FISH" * h.keys #=> ["a", "b"] * * # While this creates a new default object each time * h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" } * h["c"] #=> "Go Fish: c" * h["c"].upcase! #=> "GO FISH: C" * h["d"] #=> "Go Fish: d" * h.keys #=> ["c", "d"] * */ static mrb_value mrb_hash_init(mrb_state *mrb, mrb_value hash) { mrb_value block, ifnone; mrb_bool ifnone_p; ifnone = mrb_nil_value(); mrb_get_args(mrb, "&|o?", &block, &ifnone, &ifnone_p); hash_modify(mrb, hash); if (!mrb_nil_p(block)) { if (ifnone_p) { mrb_argnum_error(mrb, 1, 0, 0); } hash_set_default_proc(mrb, hash, block); return hash; } if (ifnone_p && !mrb_nil_p(ifnone)) { RHASH(hash)->flags |= MRB_HASH_DEFAULT; mrb_iv_set(mrb, hash, MRB_SYM(ifnone), ifnone); } return hash; } /* 15.2.13.4.2 */ /* * call-seq: * hsh[key] -> value * * Element Reference---Retrieves the value object corresponding * to the key object. If not found, returns the default value (see * Hash::new for details). * * h = { "a" => 100, "b" => 200 } * h["a"] #=> 100 * h["c"] #=> nil * */ static mrb_value mrb_hash_aget(mrb_state *mrb, mrb_value self) { mrb_value key = mrb_get_arg1(mrb); return mrb_hash_get(mrb, self, key); } /* 15.2.13.4.5 */ /* * call-seq: * hsh.default(key=nil) -> obj * * Returns the default value, the value that would be returned by * hsh[key] if key did not exist in hsh. * See also Hash::new and Hash#default=. * * h = Hash.new #=> {} * h.default #=> nil * h.default(2) #=> nil * * h = Hash.new("cat") #=> {} * h.default #=> "cat" * h.default(2) #=> "cat" * * h = Hash.new {|h,k| h[k] = k.to_i*10} #=> {} * h.default #=> nil * h.default(2) #=> 20 */ static mrb_value mrb_hash_default(mrb_state *mrb, mrb_value hash) { mrb_value key; mrb_bool given; mrb_get_args(mrb, "|o?", &key, &given); if (MRB_RHASH_DEFAULT_P(hash)) { if (MRB_RHASH_PROCDEFAULT_P(hash)) { if (!given) return mrb_nil_value(); return mrb_funcall_id(mrb, RHASH_PROCDEFAULT(hash), MRB_SYM(call), 2, hash, key); } else { return RHASH_IFNONE(hash); } } return mrb_nil_value(); } /* 15.2.13.4.6 */ /* * call-seq: * hsh.default = obj -> obj * * Sets the default value, the value returned for a key that does not * exist in the hash. It is not possible to set the default to a * Proc that will be executed on each key lookup. * * h = { "a" => 100, "b" => 200 } * h.default = "Go fish" * h["a"] #=> 100 * h["z"] #=> "Go fish" * # This doesn't do what you might hope... * h.default = proc do |hash, key| * hash[key] = key + key * end * h[2] #=> # * h["cat"] #=> # */ static mrb_value mrb_hash_set_default(mrb_state *mrb, mrb_value hash) { mrb_value ifnone = mrb_get_arg1(mrb); hash_modify(mrb, hash); mrb_iv_set(mrb, hash, MRB_SYM(ifnone), ifnone); RHASH(hash)->flags &= ~MRB_HASH_PROC_DEFAULT; if (!mrb_nil_p(ifnone)) { RHASH(hash)->flags |= MRB_HASH_DEFAULT; } else { RHASH(hash)->flags &= ~MRB_HASH_DEFAULT; } return ifnone; } /* 15.2.13.4.7 */ /* * call-seq: * hsh.default_proc -> anObject * * If Hash::new was invoked with a block, return that * block, otherwise return nil. * * h = Hash.new {|h,k| h[k] = k*k } #=> {} * p = h.default_proc #=> # * a = [] #=> [] * p.call(a, 2) * a #=> [nil, nil, 4] */ static mrb_value mrb_hash_default_proc(mrb_state *mrb, mrb_value hash) { if (MRB_RHASH_PROCDEFAULT_P(hash)) { return RHASH_PROCDEFAULT(hash); } return mrb_nil_value(); } /* * call-seq: * hsh.default_proc = proc_obj -> proc_obj * * Sets the default proc to be executed on each key lookup. * * h.default_proc = proc do |hash, key| * hash[key] = key + key * end * h[2] #=> 4 * h["cat"] #=> "catcat" */ static mrb_value mrb_hash_set_default_proc(mrb_state *mrb, mrb_value hash) { mrb_value ifnone = mrb_get_arg1(mrb); hash_modify(mrb, hash); mrb_bool has_ifnone = !mrb_nil_p(ifnone); if (has_ifnone) { mrb_check_type(mrb, ifnone, MRB_TT_PROC); } mrb_iv_set(mrb, hash, MRB_SYM(ifnone), ifnone); if (has_ifnone) { hash_set_default_proc(mrb, hash, ifnone); } else { RHASH(hash)->flags &= ~MRB_HASH_DEFAULT; RHASH(hash)->flags &= ~MRB_HASH_PROC_DEFAULT; } return ifnone; } MRB_API mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key) { mrb_value del_val; hash_modify(mrb, hash); if (h_delete(mrb, mrb_hash_ptr(hash), key, &del_val)) { return del_val; } /* not found */ return mrb_nil_value(); } static mrb_value mrb_hash_delete(mrb_state *mrb, mrb_value self) { mrb_value key = mrb_get_arg1(mrb); mrb->c->ci->mid = 0; return mrb_hash_delete_key(mrb, self, key); } /* 15.2.13.4.24 */ /* * call-seq: * hsh.shift -> anArray or obj * * Removes a key-value pair from hsh and returns it as the * two-item array [ key, value ], or * the hash's default value if the hash is empty. * * h = { 1 => "a", 2 => "b", 3 => "c" } * h.shift #=> [1, "a"] * h #=> {2=>"b", 3=>"c"} */ static mrb_value mrb_hash_shift(mrb_state *mrb, mrb_value hash) { struct RHash *h = mrb_hash_ptr(hash); hash_modify(mrb, hash); if (h_size(h) == 0) { return mrb_nil_value(); } else { mrb_value del_key, del_val; h_shift(mrb, h, &del_key, &del_val); mrb_gc_protect(mrb, del_key); mrb_gc_protect(mrb, del_val); return mrb_assoc_new(mrb, del_key, del_val); } } /* 15.2.13.4.4 */ /* * call-seq: * hsh.clear -> hsh * * Removes all key-value pairs from `hsh`. * * h = { "a" => 100, "b" => 200 } #=> {"a"=>100, "b"=>200} * h.clear #=> {} * */ MRB_API mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash) { hash_modify(mrb, hash); h_clear(mrb, mrb_hash_ptr(hash)); return hash; } /* 15.2.13.4.3 */ /* 15.2.13.4.26 */ /* * call-seq: * hsh[key] = value -> value * hsh.store(key, value) -> value * * Element Assignment---Associates the value given by * value with the key given by key. * key should not have its value changed while it is in * use as a key (a String passed as a key will be * duplicated and frozen). * * h = { "a" => 100, "b" => 200 } * h["a"] = 9 * h["c"] = 4 * h #=> {"a"=>9, "b"=>200, "c"=>4} * */ static mrb_value mrb_hash_aset(mrb_state *mrb, mrb_value self) { mrb_int argc = mrb_get_argc(mrb); if (argc != 2) { mrb_argnum_error(mrb, argc, 2, 2); } const mrb_value *argv = mrb_get_argv(mrb); mrb_value key = argv[0]; mrb_value val = argv[1]; mrb_hash_set(mrb, self, key, val); return val; } MRB_API mrb_int mrb_hash_size(mrb_state *mrb, mrb_value hash) { return (mrb_int)h_size(mrb_hash_ptr(hash)); } /* 15.2.13.4.20 */ /* 15.2.13.4.25 */ /* * call-seq: * hsh.length -> integer * hsh.size -> integer * * Returns the number of key-value pairs in the hash. * * h = { "d" => 100, "a" => 200, "v" => 300, "e" => 400 } * h.length #=> 4 * h.delete("a") #=> 200 * h.length #=> 3 */ static mrb_value mrb_hash_size_m(mrb_state *mrb, mrb_value self) { mrb_int size = mrb_hash_size(mrb, self); return mrb_int_value(mrb, size); } MRB_API mrb_bool mrb_hash_empty_p(mrb_state *mrb, mrb_value self) { return h_size(mrb_hash_ptr(self)) == 0; } /* 15.2.13.4.12 */ /* * call-seq: * hsh.empty? -> true or false * * Returns true if hsh contains no key-value pairs. * * {}.empty? #=> true * */ static mrb_value mrb_hash_empty_m(mrb_state *mrb, mrb_value self) { return mrb_bool_value(mrb_hash_empty_p(mrb, self)); } /* 15.2.13.4.19 */ /* * call-seq: * hsh.keys -> array * * Returns a new array populated with the keys from this hash. See also * Hash#values. * * h = { "a" => 100, "b" => 200, "c" => 300, "d" => 400 } * h.keys #=> ["a", "b", "c", "d"] * */ MRB_API mrb_value mrb_hash_keys(mrb_state *mrb, mrb_value hash) { struct RHash *h = mrb_hash_ptr(hash); mrb_value ary = mrb_ary_new_capa(mrb, (mrb_int)h_size(h)); H_EACH(h, entry) { mrb_ary_push(mrb, ary, entry->key); } return ary; } /* 15.2.13.4.28 */ /* * call-seq: * hsh.values -> array * * Returns a new array populated with the values from hsh. See * also Hash#keys. * * h = { "a" => 100, "b" => 200, "c" => 300 } * h.values #=> [100, 200, 300] * */ MRB_API mrb_value mrb_hash_values(mrb_state *mrb, mrb_value hash) { struct RHash *h = mrb_hash_ptr(hash); mrb_value ary = mrb_ary_new_capa(mrb, (mrb_int)h_size(h)); H_EACH(h, entry) { mrb_ary_push(mrb, ary, entry->val); } return ary; } /* 15.2.13.4.13 */ /* 15.2.13.4.15 */ /* 15.2.13.4.18 */ /* 15.2.13.4.21 */ /* * call-seq: * hsh.has_key?(key) -> true or false * hsh.include?(key) -> true or false * hsh.key?(key) -> true or false * hsh.member?(key) -> true or false * * Returns true if the given key is present in hsh. * * h = { "a" => 100, "b" => 200 } * h.has_key?("a") #=> true * h.has_key?("z") #=> false * */ MRB_API mrb_bool mrb_hash_key_p(mrb_state *mrb, mrb_value hash, mrb_value key) { mrb_value val; return h_get(mrb, mrb_hash_ptr(hash), key, &val); } static mrb_value mrb_hash_has_key(mrb_state *mrb, mrb_value hash) { mrb_value key = mrb_get_arg1(mrb); mrb_bool key_p; key_p = mrb_hash_key_p(mrb, hash, key); return mrb_bool_value(key_p); } /* 15.2.13.4.14 */ /* 15.2.13.4.27 */ /* * call-seq: * hsh.has_value?(value) -> true or false * hsh.value?(value) -> true or false * * Returns true if the given value is present for some key * in hsh. * * h = { "a" => 100, "b" => 200 } * h.has_value?(100) #=> true * h.has_value?(999) #=> false */ static mrb_value mrb_hash_has_value(mrb_state *mrb, mrb_value hash) { mrb_value val = mrb_get_arg1(mrb); struct RHash *h = mrb_hash_ptr(hash); H_EACH(h, entry) { H_CHECK_MODIFIED(mrb, h) { if (mrb_equal(mrb, val, entry->val)) return mrb_true_value(); } } return mrb_false_value(); } MRB_API void mrb_hash_merge(mrb_state *mrb, mrb_value hash1, mrb_value hash2) { struct RHash *h1, *h2; hash_modify(mrb, hash1); mrb_ensure_hash_type(mrb, hash2); h1 = mrb_hash_ptr(hash1); h2 = mrb_hash_ptr(hash2); if (h1 == h2) return; if (h_size(h2) == 0) return; H_EACH(h2, entry) { H_CHECK_MODIFIED(mrb, h2) {h_set(mrb, h1, entry->key, entry->val);} mrb_field_write_barrier_value(mrb, (struct RBasic*)h1, entry->key); mrb_field_write_barrier_value(mrb, (struct RBasic*)h1, entry->val); } } static mrb_value mrb_hash_merge_m(mrb_state *mrb, mrb_value hash) { mrb_int argc; mrb_value *argv; mrb_get_args(mrb, "*", &argv, &argc); while (argc--) { mrb_hash_merge(mrb, hash, *argv++); } return hash; } /* * call-seq: * hsh.rehash -> hsh * * Rebuilds the hash based on the current hash values for each key. If * values of key objects have changed since they were inserted, this * method will reindex hsh. * * keys = (1..17).map{|n| [n]} * k = keys[0] * h = {} * keys.each{|key| h[key] = key[0]} * h #=> { [1]=>1, [2]=>2, ... [16]=>16, [17]=>17} * h[k] #=> 1 * k[0] = keys.size + 1 * h #=> {[18]=>1, [2]=>2, ... [16]=>16, [17]=>17} * h[k] #=> nil * h.rehash * h[k] #=> 1 */ static mrb_value mrb_hash_rehash(mrb_state *mrb, mrb_value self) { hash_modify(mrb, self); h_rehash(mrb, mrb_hash_ptr(self)); return self; } static mrb_value mrb_hash_compact(mrb_state *mrb, mrb_value hash) { struct RHash *h = mrb_hash_ptr(hash); mrb_bool ht_p = h_ht_p(h); uint32_t size = ht_p ? ht_size(h) : ar_size(h); uint32_t dec = 0; hash_modify(mrb, hash); H_EACH(h, entry) { if (mrb_nil_p(entry->val)) { entry_delete(entry); dec++; } } if (dec == 0) return mrb_nil_value(); size -= dec; if (ht_p) { ht_set_size(h, size); } else { ar_set_size(h, size); } return hash; } /* * call-seq: * hash.to_s -> string * hash.inspect -> string * * Return the contents of this hash as a string. */ static mrb_value mrb_hash_to_s(mrb_state *mrb, mrb_value self) { mrb->c->ci->mid = MRB_SYM(inspect); mrb_value ret = mrb_str_new_lit(mrb, "{"); int ai = mrb_gc_arena_save(mrb); if (mrb_inspect_recursive_p(mrb, self)) { mrb_str_cat_lit(mrb, ret, "...}"); return ret; } mrb_int i = 0; struct RHash *h = mrb_hash_ptr(self); H_EACH(h, entry) { if (i++ > 0) mrb_str_cat_lit(mrb, ret, ", "); if (mrb_symbol_p(entry->key)) { mrb_str_cat_str(mrb, ret, mrb_obj_as_string(mrb, entry->key)); mrb_gc_arena_restore(mrb, ai); mrb_str_cat_lit(mrb, ret, ": "); } else { H_CHECK_MODIFIED(mrb, h) { mrb_str_cat_str(mrb, ret, mrb_inspect(mrb, entry->key)); } mrb_gc_arena_restore(mrb, ai); mrb_str_cat_lit(mrb, ret, " => "); } H_CHECK_MODIFIED(mrb, h) { mrb_str_cat_str(mrb, ret, mrb_inspect(mrb, entry->val)); } mrb_gc_arena_restore(mrb, ai); } mrb_str_cat_lit(mrb, ret, "}"); return ret; } /* * call-seq: * hash.to_hash -> self * * Returns self. */ static mrb_value mrb_hash_to_hash(mrb_state *mrb, mrb_value self) { return self; } /* * call-seq: * hash.assoc(key) -> new_array or nil * * If the given key is found, returns a 2-element Array containing that key * and its value: * * h = {foo: 0, bar: 1, baz: 2} * h.assoc(:bar) # => [:bar, 1] * * Returns nil if key key is not found. */ static mrb_value mrb_hash_assoc(mrb_state *mrb, mrb_value hash) { mrb_value key = mrb_get_arg1(mrb); struct RHash *h = mrb_hash_ptr(hash); H_EACH(h, entry) { if (obj_eql(mrb, entry->key, key, h)) { return mrb_assoc_new(mrb, entry->key, entry->val); } } return mrb_nil_value(); } /* * call-seq: * hash.rassoc(value) -> new_array or nil * * Returns a new 2-element Array consisting of the key and value of the * first-found entry whose value is == to value. * * h = {foo: 0, bar: 1, baz: 1} * h.rassoc(1) # => [:bar, 1] * * Returns nil if no such value found. */ static mrb_value mrb_hash_rassoc(mrb_state *mrb, mrb_value hash) { mrb_value value = mrb_get_arg1(mrb); struct RHash *h = mrb_hash_ptr(hash); H_EACH(h, entry) { if (obj_eql(mrb, entry->val, value, h)) { return mrb_assoc_new(mrb, entry->key, entry->val); } } return mrb_nil_value(); } void mrb_init_hash(mrb_state *mrb) { struct RClass *h; mrb->hash_class = h = mrb_define_class_id(mrb, MRB_SYM(Hash), mrb->object_class); /* 15.2.13 */ MRB_SET_INSTANCE_TT(h, MRB_TT_HASH); mrb_define_method_id(mrb, h, MRB_OPSYM(aref), mrb_hash_aget, MRB_ARGS_REQ(1)); /* 15.2.13.4.2 */ mrb_define_method_id(mrb, h, MRB_OPSYM(aset), mrb_hash_aset, MRB_ARGS_REQ(2)); /* 15.2.13.4.3 */ mrb_define_method_id(mrb, h, MRB_SYM(clear), mrb_hash_clear, MRB_ARGS_NONE()); /* 15.2.13.4.4 */ mrb_define_method_id(mrb, h, MRB_SYM(default), mrb_hash_default, MRB_ARGS_OPT(1)); /* 15.2.13.4.5 */ mrb_define_method_id(mrb, h, MRB_SYM_E(default), mrb_hash_set_default, MRB_ARGS_REQ(1)); /* 15.2.13.4.6 */ mrb_define_method_id(mrb, h, MRB_SYM(default_proc), mrb_hash_default_proc,MRB_ARGS_NONE()); /* 15.2.13.4.7 */ mrb_define_method_id(mrb, h, MRB_SYM_E(default_proc), mrb_hash_set_default_proc,MRB_ARGS_REQ(1)); /* 15.2.13.4.7 */ mrb_define_method_id(mrb, h, MRB_SYM(__delete), mrb_hash_delete, MRB_ARGS_REQ(1)); /* core of 15.2.13.4.8 */ mrb_define_method_id(mrb, h, MRB_SYM_Q(empty), mrb_hash_empty_m, MRB_ARGS_NONE()); /* 15.2.13.4.12 */ mrb_define_method_id(mrb, h, MRB_SYM_Q(has_key), mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.13 */ mrb_define_method_id(mrb, h, MRB_SYM_Q(has_value), mrb_hash_has_value, MRB_ARGS_REQ(1)); /* 15.2.13.4.14 */ mrb_define_method_id(mrb, h, MRB_SYM_Q(include), mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.15 */ mrb_define_method_id(mrb, h, MRB_SYM(initialize), mrb_hash_init, MRB_ARGS_OPT(1)|MRB_ARGS_BLOCK()); /* 15.2.13.4.16 */ mrb_define_method_id(mrb, h, MRB_SYM(initialize_copy), mrb_hash_init_copy, MRB_ARGS_REQ(1)); /* 15.2.13.4.17 */ mrb_define_method_id(mrb, h, MRB_SYM_Q(key), mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.18 */ mrb_define_method_id(mrb, h, MRB_SYM(keys), mrb_hash_keys, MRB_ARGS_NONE()); /* 15.2.13.4.19 */ mrb_define_method_id(mrb, h, MRB_SYM(length), mrb_hash_size_m, MRB_ARGS_NONE()); /* 15.2.13.4.20 */ mrb_define_method_id(mrb, h, MRB_SYM_Q(member), mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.21 */ mrb_define_method_id(mrb, h, MRB_SYM(replace), mrb_hash_init_copy, MRB_ARGS_REQ(1)); /* 15.2.13.4.23 */ mrb_define_method_id(mrb, h, MRB_SYM(shift), mrb_hash_shift, MRB_ARGS_NONE()); /* 15.2.13.4.24 */ mrb_define_method_id(mrb, h, MRB_SYM(size), mrb_hash_size_m, MRB_ARGS_NONE()); /* 15.2.13.4.25 */ mrb_define_method_id(mrb, h, MRB_SYM(store), mrb_hash_aset, MRB_ARGS_REQ(2)); /* 15.2.13.4.26 */ mrb_define_method_id(mrb, h, MRB_SYM_Q(value), mrb_hash_has_value, MRB_ARGS_REQ(1)); /* 15.2.13.4.27 */ mrb_define_method_id(mrb, h, MRB_SYM(values), mrb_hash_values, MRB_ARGS_NONE()); /* 15.2.13.4.28 */ mrb_define_method_id(mrb, h, MRB_SYM(to_s), mrb_hash_to_s, MRB_ARGS_NONE()); mrb_define_method_id(mrb, h, MRB_SYM(inspect), mrb_hash_to_s, MRB_ARGS_NONE()); mrb_define_method_id(mrb, h, MRB_SYM(rehash), mrb_hash_rehash, MRB_ARGS_NONE()); mrb_define_method_id(mrb, h, MRB_SYM(to_hash), mrb_hash_to_hash, MRB_ARGS_NONE()); mrb_define_method_id(mrb, h, MRB_SYM(assoc), mrb_hash_assoc, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, h, MRB_SYM(rassoc), mrb_hash_rassoc, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, h, MRB_SYM(__merge), mrb_hash_merge_m, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, h, MRB_SYM(__compact), mrb_hash_compact, MRB_ARGS_NONE()); /* implementation of Hash#compact! */ } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/object.c0000644000000000000000000000013215077107276020074 xustar0030 mtime=1761382078.135420497 30 atime=1761382080.158411242 30 ctime=1761382108.589301858 nghttp2-1.68.0/third-party/mruby/src/object.c0000644000175100017510000003773715077107276020505 0ustar00runnerrunner/* ** object.c - Object, NilClass, TrueClass, FalseClass class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include MRB_API mrb_bool mrb_obj_eq(mrb_state *mrb, mrb_value v1, mrb_value v2) { #if defined(MRB_NAN_BOXING) return v1.u == v2.u; #elif defined(MRB_WORD_BOXING) return v1.w == v2.w; #else /* MRB_NO_BOXING */ if (mrb_type(v1) != mrb_type(v2)) return FALSE; switch (mrb_type(v1)) { case MRB_TT_TRUE: return TRUE; case MRB_TT_FALSE: return (mrb_fixnum(v1) == mrb_fixnum(v2)); case MRB_TT_INTEGER: return (mrb_integer(v1) == mrb_integer(v2)); case MRB_TT_SYMBOL: return (mrb_symbol(v1) == mrb_symbol(v2)); #ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: return (mrb_float(v1) == mrb_float(v2)); #endif default: return (mrb_ptr(v1) == mrb_ptr(v2)); } #endif } MRB_API mrb_bool mrb_obj_equal(mrb_state *mrb, mrb_value v1, mrb_value v2) { /* temporary definition */ return mrb_obj_eq(mrb, v1, v2); } MRB_API mrb_bool mrb_equal(mrb_state *mrb, mrb_value obj1, mrb_value obj2) { if (mrb_obj_eq(mrb, obj1, obj2)) return TRUE; #ifndef MRB_NO_FLOAT /* value mixing with integer and float */ else if (mrb_integer_p(obj1) && mrb_float_p(obj2)) { if ((mrb_float)mrb_integer(obj1) == mrb_float(obj2)) return TRUE; } else if (mrb_float_p(obj1) && mrb_integer_p(obj2)) { if (mrb_float(obj1) == (mrb_float)mrb_integer(obj2)) return TRUE; } #endif #ifdef MRB_USE_BIGINT else if (mrb_bigint_p(obj1) && (mrb_integer_p(obj2) || mrb_bigint_p(obj2) || mrb_float_p(obj2))) { if (mrb_bint_cmp(mrb, obj1, obj2) == 0) return TRUE; } #endif else if (!mrb_func_basic_p(mrb, obj1, MRB_OPSYM(eq), mrb_obj_equal_m)) { mrb_value result = mrb_funcall_argv(mrb, obj1, MRB_OPSYM(eq), 1, &obj2); if (mrb_test(result)) return TRUE; } return FALSE; } /* * Document-class: NilClass * * The class of the singleton object nil. */ /* 15.2.4.3.4 */ /* * call_seq: * nil.nil? -> true * * Only the object nil responds true to nil?. */ static mrb_value mrb_true(mrb_state *mrb, mrb_value obj) { return mrb_true_value(); } /* 15.2.4.3.5 */ /* * call-seq: * nil.to_s -> "" * * Always returns the empty string. */ static mrb_value nil_to_s(mrb_state *mrb, mrb_value obj) { mrb_value str = mrb_str_new_frozen(mrb, NULL, 0); RSTR_SET_ASCII_FLAG(mrb_str_ptr(str)); return str; } static mrb_value nil_inspect(mrb_state *mrb, mrb_value obj) { mrb_value str = mrb_str_new_lit_frozen(mrb, "nil"); RSTR_SET_ASCII_FLAG(mrb_str_ptr(str)); return str; } /*********************************************************************** * Document-class: TrueClass * * The global value true is the only instance of class * TrueClass and represents a logically true value in * boolean expressions. The class provides operators allowing * true to be used in logical expressions. */ /* 15.2.5.3.1 */ /* * call-seq: * true & obj -> true or false * * And---Returns false if obj is * nil or false, true otherwise. */ static mrb_value true_and(mrb_state *mrb, mrb_value obj) { mrb_bool obj2; mrb_get_args(mrb, "b", &obj2); return mrb_bool_value(obj2); } /* 15.2.5.3.2 */ /* * call-seq: * true ^ obj -> !obj * * Exclusive Or---Returns true if obj is * nil or false, false * otherwise. */ static mrb_value true_xor(mrb_state *mrb, mrb_value obj) { mrb_bool obj2; mrb_get_args(mrb, "b", &obj2); return mrb_bool_value(!obj2); } /* 15.2.5.3.3 */ /* * call-seq: * true.to_s -> "true" * * The string representation of true is "true". */ static mrb_value true_to_s(mrb_state *mrb, mrb_value obj) { mrb_value str = mrb_str_new_lit_frozen(mrb, "true"); RSTR_SET_ASCII_FLAG(mrb_str_ptr(str)); return str; } /* 15.2.5.3.4 */ /* * call-seq: * true | obj -> true * * Or---Returns true. As anObject is an argument to * a method call, it is always evaluated; there is no short-circuit * evaluation in this case. * * true | puts("or") * true || puts("logical or") * * produces: * * or */ static mrb_value true_or(mrb_state *mrb, mrb_value obj) { return mrb_true_value(); } /* * Document-class: FalseClass * * The global value false is the only instance of class * FalseClass and represents a logically false value in * boolean expressions. The class provides operators allowing * false to participate correctly in logical expressions. * */ /* 15.2.4.3.1 */ /* 15.2.6.3.1 */ /* * call-seq: * false & obj -> false * nil & obj -> false * * And---Returns false. obj is always * evaluated as it is the argument to a method call---there is no * short-circuit evaluation in this case. */ static mrb_value false_and(mrb_state *mrb, mrb_value obj) { return mrb_false_value(); } /* 15.2.4.3.2 */ /* 15.2.6.3.2 */ /* * call-seq: * false ^ obj -> true or false * nil ^ obj -> true or false * * Exclusive Or---If obj is nil or * false, returns false; otherwise, returns * true. * */ static mrb_value false_xor(mrb_state *mrb, mrb_value obj) { mrb_bool obj2; mrb_get_args(mrb, "b", &obj2); return mrb_bool_value(obj2); } /* 15.2.4.3.3 */ /* 15.2.6.3.4 */ /* * call-seq: * false | obj -> true or false * nil | obj -> true or false * * Or---Returns false if obj is * nil or false; true otherwise. */ static mrb_value false_or(mrb_state *mrb, mrb_value obj) { mrb_bool obj2; mrb_get_args(mrb, "b", &obj2); return mrb_bool_value(obj2); } /* 15.2.6.3.3 */ /* * call-seq: * false.to_s -> "false" * * 'nuf said... */ static mrb_value false_to_s(mrb_state *mrb, mrb_value obj) { mrb_value str = mrb_str_new_lit_frozen(mrb, "false"); RSTR_SET_ASCII_FLAG(mrb_str_ptr(str)); return str; } void mrb_init_object(mrb_state *mrb) { struct RClass *n; struct RClass *t; struct RClass *f; mrb->nil_class = n = mrb_define_class_id(mrb, MRB_SYM(NilClass), mrb->object_class); MRB_SET_INSTANCE_TT(n, MRB_TT_FALSE); mrb_undef_class_method_id(mrb, n, MRB_SYM(new)); mrb_define_method_id(mrb, n, MRB_OPSYM(and), false_and, MRB_ARGS_REQ(1)); /* 15.2.4.3.1 */ mrb_define_method_id(mrb, n, MRB_OPSYM(or), false_or, MRB_ARGS_REQ(1)); /* 15.2.4.3.2 */ mrb_define_method_id(mrb, n, MRB_OPSYM(xor), false_xor, MRB_ARGS_REQ(1)); /* 15.2.4.3.3 */ mrb_define_method_id(mrb, n, MRB_SYM_Q(nil), mrb_true, MRB_ARGS_NONE()); /* 15.2.4.3.4 */ mrb_define_method_id(mrb, n, MRB_SYM(to_s), nil_to_s, MRB_ARGS_NONE()); /* 15.2.4.3.5 */ mrb_define_method_id(mrb, n, MRB_SYM(inspect), nil_inspect, MRB_ARGS_NONE()); mrb->true_class = t = mrb_define_class_id(mrb, MRB_SYM(TrueClass), mrb->object_class); MRB_SET_INSTANCE_TT(t, MRB_TT_TRUE); mrb_undef_class_method_id(mrb, t, MRB_SYM(new)); mrb_define_method_id(mrb, t, MRB_OPSYM(and), true_and, MRB_ARGS_REQ(1)); /* 15.2.5.3.1 */ mrb_define_method_id(mrb, t, MRB_OPSYM(or), true_or, MRB_ARGS_REQ(1)); /* 15.2.5.3.2 */ mrb_define_method_id(mrb, t, MRB_OPSYM(xor), true_xor, MRB_ARGS_REQ(1)); /* 15.2.5.3.3 */ mrb_define_method_id(mrb, t, MRB_SYM(to_s), true_to_s, MRB_ARGS_NONE()); /* 15.2.5.3.4 */ mrb_define_method_id(mrb, t, MRB_SYM(inspect), true_to_s, MRB_ARGS_NONE()); mrb->false_class = f = mrb_define_class_id(mrb, MRB_SYM(FalseClass), mrb->object_class); MRB_SET_INSTANCE_TT(f, MRB_TT_FALSE); mrb_undef_class_method_id(mrb, f, MRB_SYM(new)); mrb_define_method_id(mrb, f, MRB_OPSYM(and), false_and, MRB_ARGS_REQ(1)); /* 15.2.6.3.1 */ mrb_define_method_id(mrb, f, MRB_OPSYM(or), false_or, MRB_ARGS_REQ(1)); /* 15.2.6.3.2 */ mrb_define_method_id(mrb, f, MRB_OPSYM(xor), false_xor, MRB_ARGS_REQ(1)); /* 15.2.6.3.3 */ mrb_define_method_id(mrb, f, MRB_SYM(to_s), false_to_s, MRB_ARGS_NONE()); /* 15.2.6.3.4 */ mrb_define_method_id(mrb, f, MRB_SYM(inspect), false_to_s, MRB_ARGS_NONE()); } static const char* type_name(enum mrb_vtype t) { switch (t) { #define MRB_VTYPE_NAME(tt, type, name) case tt: return name; MRB_VTYPE_FOREACH(MRB_VTYPE_NAME) #undef MRB_VTYPE_NAME default: return NULL; } } static mrb_value convert_type(mrb_state *mrb, mrb_value val, const char *tname, mrb_sym method, mrb_bool raise) { if (!mrb_respond_to(mrb, val, method)) { if (raise) { if (tname) mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %Y into %s", val, tname); mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %Y", val); } return mrb_nil_value(); } return mrb_funcall_argv(mrb, val, method, 0, 0); } MRB_API mrb_value mrb_type_convert(mrb_state *mrb, mrb_value val, enum mrb_vtype type, mrb_sym method) { mrb_value v; const char *tname; if (mrb_type(val) == type) return val; tname = type_name(type); v = convert_type(mrb, val, tname, method, TRUE); if (mrb_type(v) != type) { if (type == MRB_TT_STRING) return mrb_any_to_s(mrb, val); mrb_raisef(mrb, E_TYPE_ERROR, "%v cannot be converted to %s by #%n", val, tname, method); } return v; } MRB_API mrb_value mrb_type_convert_check(mrb_state *mrb, mrb_value val, enum mrb_vtype type, mrb_sym method) { mrb_value v; if (mrb_type(val) == type && type != MRB_TT_CDATA && type != MRB_TT_ISTRUCT) return val; v = convert_type(mrb, val, type_name(type), method, FALSE); if (mrb_nil_p(v) || mrb_type(v) != type) return mrb_nil_value(); return v; } MRB_API void mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t) { enum mrb_vtype xt = mrb_type(x); const char *tname, *ename; if (t == xt) return; tname = type_name(t); if (mrb_nil_p(x)) { ename = "nil"; } else if (mrb_integer_p(x)) { ename = "Integer"; } else if (mrb_symbol_p(x)) { ename = "Symbol"; } else if (mrb_immediate_p(x)) { ename = RSTRING_PTR(mrb_obj_as_string(mrb, x)); } else { ename = mrb_obj_classname(mrb, x); } if (tname) { mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %s (expected %s)", ename, tname); } mrb_raisef(mrb, E_TYPE_ERROR, "unknown type %d (%s given)", t, ename); } /* 15.3.1.3.46 */ /* * call-seq: * obj.to_s => string * * Returns a string representing obj. The default * to_s prints the object's class and an encoding of the * object id. As a special case, the top-level object that is the * initial execution context of Ruby programs returns "main." */ MRB_API mrb_value mrb_any_to_s(mrb_state *mrb, mrb_value obj) { mrb_value str = mrb_str_new_capa(mrb, 20); const char *cname = mrb_obj_classname(mrb, obj); mrb_str_cat_lit(mrb, str, "#<"); mrb_str_cat_cstr(mrb, str, cname); if (!mrb_immediate_p(obj)) { mrb_str_cat_lit(mrb, str, ":"); mrb_str_cat_str(mrb, str, mrb_ptr_to_str(mrb, mrb_ptr(obj))); } mrb_str_cat_lit(mrb, str, ">"); return str; } /* * call-seq: * obj.is_a?(class) => true or false * obj.kind_of?(class) => true or false * * Returns true if class is the class of * obj, or if class is one of the superclasses of * obj or modules included in obj. * * module M; end * class A * include M * end * class B < A; end * class C < B; end * b = B.new * b.instance_of? A #=> false * b.instance_of? B #=> true * b.instance_of? C #=> false * b.instance_of? M #=> false * b.kind_of? A #=> true * b.kind_of? B #=> true * b.kind_of? C #=> false * b.kind_of? M #=> true */ MRB_API mrb_bool mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c) { struct RClass *cl = mrb_class(mrb, obj); switch (c->tt) { case MRB_TT_MODULE: case MRB_TT_CLASS: case MRB_TT_ICLASS: case MRB_TT_SCLASS: break; default: mrb_raise(mrb, E_TYPE_ERROR, "class or module required"); } MRB_CLASS_ORIGIN(c); while (cl) { if (cl == c || cl->mt == c->mt) return TRUE; cl = cl->super; } return FALSE; } #ifdef MRB_USE_RATIONAL // provided by mruby-rational with MRB_USE_RATIONAL mrb_value mrb_rational_to_i(mrb_state *mrb, mrb_value rat); mrb_value mrb_rational_to_f(mrb_state *mrb, mrb_value rat); #endif #ifdef MRB_USE_COMPLEX // provided by mruby-complex with MRB_USE_COMPLEX mrb_value mrb_complex_to_f(mrb_state *mrb, mrb_value comp); mrb_value mrb_complex_to_i(mrb_state *mrb, mrb_value comp); #endif MRB_API mrb_value mrb_ensure_integer_type(mrb_state *mrb, mrb_value val) { if (!mrb_integer_p(val)) { #ifndef MRB_NO_FLOAT if (mrb_float_p(val)) { return mrb_float_to_integer(mrb, val); } else { switch (mrb_type(val)) { #ifdef MRB_USE_BIGINT case MRB_TT_BIGINT: return val; #endif #ifdef MRB_USE_RATIONAL case MRB_TT_RATIONAL: return mrb_rational_to_i(mrb, val); #endif #ifdef MRB_USE_COMPLEX case MRB_TT_COMPLEX: return mrb_complex_to_i(mrb, val); #endif default: break; } } #endif mrb_raisef(mrb, E_TYPE_ERROR, "%Y cannot be converted to Integer", val); } return val; } MRB_API mrb_value mrb_ensure_int_type(mrb_state *mrb, mrb_value val) { val = mrb_ensure_integer_type(mrb, val); #ifdef MRB_USE_BIGINT if (mrb_bigint_p(val)) { return mrb_int_value(mrb, mrb_bint_as_int(mrb, val)); } #endif return val; } #ifndef MRB_NO_FLOAT MRB_API mrb_value mrb_ensure_float_type(mrb_state *mrb, mrb_value val) { if (mrb_nil_p(val)) { mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Float"); } switch (mrb_type(val)) { case MRB_TT_INTEGER: return mrb_float_value(mrb, (mrb_float)mrb_integer(val)); case MRB_TT_FLOAT: return val; #ifdef MRB_USE_RATIONAL case MRB_TT_RATIONAL: return mrb_rational_to_f(mrb, val); #endif #ifdef MRB_USE_COMPLEX case MRB_TT_COMPLEX: return mrb_complex_to_f(mrb, val); #endif #ifdef MRB_USE_BIGINT case MRB_TT_BIGINT: return mrb_float_value(mrb, mrb_bint_as_float(mrb, val)); #endif default: mrb_raisef(mrb, E_TYPE_ERROR, "%Y cannot be converted to Float", val); /* not reached */ return val; } } #endif MRB_API mrb_value mrb_ensure_string_type(mrb_state *mrb, mrb_value str) { if (!mrb_string_p(str)) { mrb_raisef(mrb, E_TYPE_ERROR, "%Y cannot be converted to String", str); } return str; } MRB_API mrb_value mrb_check_string_type(mrb_state *mrb, mrb_value str) { if (!mrb_string_p(str)) return mrb_nil_value(); return str; } MRB_API mrb_value mrb_ensure_array_type(mrb_state *mrb, mrb_value ary) { if (!mrb_array_p(ary)) { mrb_raisef(mrb, E_TYPE_ERROR, "%Y cannot be converted to Array", ary); } return ary; } MRB_API mrb_value mrb_check_array_type(mrb_state *mrb, mrb_value ary) { if (!mrb_array_p(ary)) return mrb_nil_value(); return ary; } MRB_API mrb_value mrb_ensure_hash_type(mrb_state *mrb, mrb_value hash) { if (!mrb_hash_p(hash)) { mrb_raisef(mrb, E_TYPE_ERROR, "%Y cannot be converted to Hash", hash); } return hash; } MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash) { if (!mrb_hash_p(hash)) return mrb_nil_value(); return hash; } MRB_API mrb_value mrb_inspect(mrb_state *mrb, mrb_value obj) { mrb_value v = mrb_funcall_argv(mrb, obj, MRB_SYM(inspect), 0, NULL); if (!mrb_string_p(v)) { v = mrb_obj_as_string(mrb, obj); } return v; } MRB_API mrb_bool mrb_eql(mrb_state *mrb, mrb_value obj1, mrb_value obj2) { if (mrb_obj_eq(mrb, obj1, obj2)) return TRUE; return mrb_test(mrb_funcall_argv(mrb, obj1, MRB_SYM_Q(eql), 1, &obj2)); } MRB_API mrb_value mrb_obj_itself(mrb_state *mrb, mrb_value self) { return self; } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/vm.c0000644000000000000000000000013215077107276017250 xustar0030 mtime=1761382078.138420483 30 atime=1761382080.160411233 30 ctime=1761382108.584301873 nghttp2-1.68.0/third-party/mruby/src/vm.c0000644000175100017510000024754715077107276017663 0ustar00runnerrunner/* ** vm.c - virtual machine for mruby ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include #include #include #include "value_array.h" #include #include #include #include #ifdef MRB_NO_STDIO #if defined(__cplusplus) extern "C" { #endif void abort(void); #if defined(__cplusplus) } /* extern "C" */ #endif #endif #define STACK_INIT_SIZE 128 #define CALLINFO_INIT_SIZE 32 /* Define amount of linear stack growth. */ #ifndef MRB_STACK_GROWTH #define MRB_STACK_GROWTH 128 #endif /* Maximum recursive depth. Should be set lower on memory constrained systems. */ #ifdef __clang__ #if __has_feature(address_sanitizer) && !defined(__SANITIZE_ADDRESS__) #define __SANITIZE_ADDRESS__ #endif #endif #ifndef MRB_CALL_LEVEL_MAX #if defined(__SANITIZE_ADDRESS__) #define MRB_CALL_LEVEL_MAX 128 #else #define MRB_CALL_LEVEL_MAX 512 #endif #endif /* Maximum stack depth. Should be set lower on memory constrained systems. The value below allows about 60000 recursive calls in the simplest case. */ #ifndef MRB_STACK_MAX #define MRB_STACK_MAX (0x40000 - MRB_STACK_GROWTH) #endif #ifdef VM_DEBUG # define DEBUG(x) (x) #else # define DEBUG(x) #endif #ifndef MRB_GC_FIXED_ARENA static void mrb_gc_arena_shrink(mrb_state *mrb, int idx) { mrb_gc *gc = &mrb->gc; int capa = gc->arena_capa; gc->arena_idx = idx; if (idx < capa / 4) { capa >>= 2; if (capa < MRB_GC_ARENA_SIZE) { capa = MRB_GC_ARENA_SIZE; } if (capa != gc->arena_capa) { gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*capa); gc->arena_capa = capa; } } } #else #define mrb_gc_arena_shrink(mrb, idx) mrb_gc_arena_restore(mrb, idx) #endif #define CALL_MAXARGS 15 #define CALL_VARARGS (CALL_MAXARGS<<4 | CALL_MAXARGS) static inline void stack_clear(mrb_value *from, size_t count) { while (count-- > 0) { SET_NIL_VALUE(*from); from++; } } static inline void stack_copy(mrb_value *dst, const mrb_value *src, size_t size) { if (!src) return; memcpy(dst, src, sizeof(mrb_value)*size); } static void stack_init(mrb_state *mrb) { struct mrb_context *c = mrb->c; /* mrb_assert(mrb->stack == NULL); */ c->stbase = (mrb_value*)mrb_malloc(mrb, STACK_INIT_SIZE * sizeof(mrb_value)); c->stend = c->stbase + STACK_INIT_SIZE; /* mrb_assert(ci == NULL); */ static const mrb_callinfo ci_zero = { 0 }; c->cibase = (mrb_callinfo*)mrb_malloc(mrb, CALLINFO_INIT_SIZE * sizeof(mrb_callinfo)); c->ciend = c->cibase + CALLINFO_INIT_SIZE; c->cibase[0] = ci_zero; c->ci = c->cibase; c->ci->u.target_class = mrb->object_class; c->ci->stack = c->stbase; c->ci->vis = MRB_METHOD_PRIVATE_FL; } static inline void envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase) { mrb_callinfo *ci = mrb->c->cibase; ptrdiff_t delta = newbase - oldbase; if (delta == 0) return; while (ci <= mrb->c->ci) { struct REnv *e = mrb_vm_ci_env(ci); if (e) { mrb_assert(e->cxt == mrb->c && MRB_ENV_ONSTACK_P(e)); mrb_assert(e->stack == ci->stack); e->stack += delta; } ci->stack += delta; ci++; } } /** def rec; $deep =+ 1; if $deep > 1000; return 0; end; rec; end **/ static void stack_extend_alloc(mrb_state *mrb, mrb_int room) { mrb_value *oldbase = mrb->c->stbase; size_t oldsize = mrb->c->stend - mrb->c->stbase; size_t size = oldsize; size_t off = mrb->c->ci->stack ? mrb->c->stend - mrb->c->ci->stack : 0; if (off > size) size = off; #ifdef MRB_STACK_EXTEND_DOUBLING if ((size_t)room <= size) size *= 2; else size += room; #else /* Use linear stack growth. It is slightly slower than doubling the stack space, but it saves memory on small devices. */ if (room <= MRB_STACK_GROWTH) size += MRB_STACK_GROWTH; else size += room; #endif mrb_value *newstack = (mrb_value*)mrb_realloc(mrb, mrb->c->stbase, sizeof(mrb_value) * size); stack_clear(&(newstack[oldsize]), size - oldsize); envadjust(mrb, oldbase, newstack); mrb->c->stbase = newstack; mrb->c->stend = mrb->c->stbase + size; /* Raise an exception if the new stack size will be too large, to prevent infinite recursion. However, do this only after resizing the stack, so mrb_raise has stack space to work with. */ if (size > MRB_STACK_MAX) { mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); } } static inline void stack_extend(mrb_state *mrb, mrb_int room) { if (!mrb->c->ci->stack || mrb->c->ci->stack + room >= mrb->c->stend) { stack_extend_alloc(mrb, room); } } MRB_API void mrb_stack_extend(mrb_state *mrb, mrb_int room) { stack_extend(mrb, room); } static void stack_extend_adjust(mrb_state *mrb, mrb_int room, const mrb_value **argp) { const struct mrb_context *c = mrb->c; ptrdiff_t voff = *argp - c->stbase; if (voff < 0 || voff >= c->stend - c->stbase) { stack_extend(mrb, room); } else { stack_extend(mrb, room); *argp = c->stbase + voff; } } static inline struct REnv* uvenv(mrb_state *mrb, mrb_int up) { const struct RProc *proc = mrb->c->ci->proc; while (up--) { proc = proc->upper; if (!proc) return NULL; } struct REnv *e = MRB_PROC_ENV(proc); if (e) return e; /* proc has enclosed env */ return NULL; } static inline const struct RProc* top_proc(mrb_state *mrb, const struct RProc *proc, const struct REnv **envp) { while (proc->upper) { if (MRB_PROC_SCOPE_P(proc) || MRB_PROC_STRICT_P(proc)) return proc; *envp = proc->e.env; proc = proc->upper; } return proc; } #define CI_PROC_SET(ci, p) do {\ ci->proc = p;\ mrb_assert(!p || !MRB_PROC_ALIAS_P(p));\ ci->pc = (p && !MRB_PROC_CFUNC_P(p) && p->body.irep) ? p->body.irep->iseq : NULL;\ } while (0) void mrb_vm_ci_proc_set(mrb_callinfo *ci, const struct RProc *p) { CI_PROC_SET(ci, p); } #define CI_TARGET_CLASS(ci) (((ci)->u.env && (ci)->u.env->tt == MRB_TT_ENV)? (ci)->u.env->c : (ci)->u.target_class) struct RClass* mrb_vm_ci_target_class(const mrb_callinfo *ci) { return CI_TARGET_CLASS(ci); } void mrb_vm_ci_target_class_set(mrb_callinfo *ci, struct RClass *tc) { struct REnv *e = ci->u.env; if (e && e->tt == MRB_TT_ENV) { e->c = tc; } else { ci->u.target_class = tc; } } #define CI_ENV(ci) (((ci)->u.env && (ci)->u.env->tt == MRB_TT_ENV)? (ci)->u.env : NULL) struct REnv* mrb_vm_ci_env(const mrb_callinfo *ci) { return CI_ENV(ci); } static inline void ci_env_set(mrb_callinfo *ci, struct REnv *e) { if (ci->u.env) { if (ci->u.env->tt == MRB_TT_ENV) { if (e) { e->c = ci->u.env->c; ci->u.env = e; } else { ci->u.target_class = ci->u.env->c; } } else if (e) { e->c = ci->u.target_class; ci->u.env = e; } } else { ci->u.env = e; } } void mrb_vm_ci_env_set(mrb_callinfo *ci, struct REnv *e) { ci_env_set(ci, e); } MRB_API void mrb_vm_ci_env_clear(mrb_state *mrb, mrb_callinfo *ci) { struct REnv *e = ci->u.env; if (e && e->tt == MRB_TT_ENV) { ci->u.target_class = e->c; mrb_env_unshare(mrb, e, FALSE); } } #define CINFO_NONE 0 // called method from mruby VM (without C functions) #define CINFO_SKIP 1 // ignited mruby VM from C #define CINFO_DIRECT 2 // called method from C #define CINFO_RESUMED 3 // resumed by `Fiber.yield` (probably the main call is `mrb_fiber_resume()`) #define BLK_PTR(b) ((mrb_proc_p(b)) ? mrb_proc_ptr(b) : NULL) static inline mrb_callinfo* cipush(mrb_state *mrb, mrb_int push_stacks, uint8_t cci, struct RClass *target_class, const struct RProc *proc, struct RProc *blk, mrb_sym mid, uint16_t argc) { struct mrb_context *c = mrb->c; mrb_callinfo *ci = c->ci + 1; if (ci < c->ciend) { c->ci = ci; } else { ptrdiff_t size = ci - c->cibase; if (size >= MRB_CALL_LEVEL_MAX) { mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); } c->cibase = (mrb_callinfo*)mrb_realloc(mrb, c->cibase, sizeof(mrb_callinfo)*size*2); c->ci = ci = c->cibase + size; c->ciend = c->cibase + size * 2; } ci->mid = mid; CI_PROC_SET(ci, proc); ci->blk = blk; ci->stack = ci[-1].stack + push_stacks; ci->n = argc & 0xf; ci->nk = (argc>>4) & 0xf; ci->cci = cci; ci->vis = MRB_METHOD_PUBLIC_FL; ci->u.target_class = target_class; return ci; } static void fiber_terminate(mrb_state *mrb, struct mrb_context *c, mrb_callinfo *ci) { mrb_assert(c != mrb->root_c); struct REnv *env = CI_ENV(ci); mrb_assert(env == NULL || MRB_ENV_LEN(env) <= c->stend - ci->stack); c->status = MRB_FIBER_TERMINATED; mrb_free(mrb, c->cibase); c->cibase = c->ciend = c->ci = NULL; mrb_value *stack = c->stbase; c->stbase = c->stend = NULL; if (!env) { mrb_free(mrb, stack); } else { size_t len = (size_t)MRB_ENV_LEN(env); if (len == 0) { env->stack = NULL; MRB_ENV_CLOSE(env); mrb_free(mrb, stack); } else { mrb_assert(stack == env->stack); mrb_write_barrier(mrb, (struct RBasic*)env); // don't call MRB_ENV_CLOSE() before mrb_realloc(). // the reason is that env->stack may be freed by mrb_realloc() if MRB_DEBUG + MRB_GC_STRESS are enabled. // realloc() on a freed heap will cause double-free. stack = (mrb_value*)mrb_realloc(mrb, stack, len * sizeof(mrb_value)); if (mrb_object_dead_p(mrb, (struct RBasic*)env)) { mrb_free(mrb, stack); } else { env->stack = stack; MRB_ENV_CLOSE(env); } } } /* fiber termination should automatic yield or transfer to root */ mrb->c = c->prev; if (!mrb->c) mrb->c = mrb->root_c; else c->prev = NULL; mrb->c->status = MRB_FIBER_RUNNING; } mrb_bool mrb_env_unshare(mrb_state *mrb, struct REnv *e, mrb_bool noraise) { mrb_assert(e != NULL); mrb_assert(MRB_ENV_ONSTACK_P(e)); size_t len = (size_t)MRB_ENV_LEN(e); if (len == 0) { e->stack = NULL; MRB_ENV_CLOSE(e); return TRUE; } size_t live = mrb->gc.live; mrb_value *p = (mrb_value*)mrb_malloc_simple(mrb, sizeof(mrb_value)*len); if (live != mrb->gc.live && mrb_object_dead_p(mrb, (struct RBasic*)e)) { // The e object is now subject to GC inside mrb_malloc_simple(). // Moreover, if NULL is returned due to mrb_malloc_simple() failure, simply ignore it. mrb_free(mrb, p); return TRUE; } else if (p) { stack_copy(p, e->stack, len); e->stack = p; MRB_ENV_CLOSE(e); mrb_write_barrier(mrb, (struct RBasic*)e); return TRUE; } else { e->stack = NULL; MRB_ENV_CLOSE(e); MRB_ENV_SET_LEN(e, 0); MRB_ENV_SET_BIDX(e, 0); if (!noraise) { mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err)); } return FALSE; } } static inline mrb_callinfo* cipop(mrb_state *mrb) { struct mrb_context *c = mrb->c; mrb_callinfo *ci = c->ci; struct REnv *env = CI_ENV(ci); ci_env_set(ci, NULL); // make possible to free env by GC if not needed struct RProc *b = ci->blk; if (b && !MRB_PROC_STRICT_P(b) && MRB_PROC_ENV(b) == CI_ENV(&ci[-1])) { b->flags |= MRB_PROC_ORPHAN; } if (env && !mrb_env_unshare(mrb, env, TRUE)) { c->ci--; // exceptions are handled at the method caller; see #3087 mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err)); } c->ci--; return c->ci; } MRB_API mrb_value mrb_protect_error(mrb_state *mrb, mrb_protect_error_func *body, void *userdata, mrb_bool *error) { struct mrb_jmpbuf *prev_jmp = mrb->jmp; struct mrb_jmpbuf c_jmp; mrb_value result; int ai = mrb_gc_arena_save(mrb); const struct mrb_context *c = mrb->c; ptrdiff_t ci_index = c->ci - c->cibase; if (error) { *error = FALSE; } MRB_TRY(&c_jmp) { mrb->jmp = &c_jmp; result = body(mrb, userdata); mrb->jmp = prev_jmp; } MRB_CATCH(&c_jmp) { mrb->jmp = prev_jmp; result = mrb_obj_value(mrb->exc); mrb->exc = NULL; if (error) { *error = TRUE; } if (mrb->c == c) { while (c->ci - c->cibase > ci_index) { cipop(mrb); } } else { // It was probably switched by mrb_fiber_resume(). // Simply destroy all successive CINFO_DIRECTs once the fiber has been switched. c = mrb->c; while (c->ci > c->cibase && c->ci->cci == CINFO_DIRECT) { cipop(mrb); } } } MRB_END_EXC(&c_jmp); mrb_gc_arena_restore(mrb, ai); mrb_gc_protect(mrb, result); return result; } void mrb_exc_set(mrb_state *mrb, mrb_value exc); static mrb_value mrb_run(mrb_state *mrb, const struct RProc* proc, mrb_value self); #ifndef MRB_FUNCALL_ARGC_MAX #define MRB_FUNCALL_ARGC_MAX 16 #endif MRB_API mrb_value mrb_funcall(mrb_state *mrb, mrb_value self, const char *name, mrb_int argc, ...) { mrb_value argv[MRB_FUNCALL_ARGC_MAX]; mrb_sym mid = mrb_intern_cstr(mrb, name); if (argc > MRB_FUNCALL_ARGC_MAX) { mrb_raise(mrb, E_ARGUMENT_ERROR, "Too long arguments. (limit=" MRB_STRINGIZE(MRB_FUNCALL_ARGC_MAX) ")"); } va_list ap; va_start(ap, argc); for (mrb_int i = 0; i < argc; i++) { argv[i] = va_arg(ap, mrb_value); } va_end(ap); return mrb_funcall_argv(mrb, self, mid, argc, argv); } MRB_API mrb_value mrb_funcall_id(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, ...) { mrb_value argv[MRB_FUNCALL_ARGC_MAX]; if (argc > MRB_FUNCALL_ARGC_MAX) { mrb_raise(mrb, E_ARGUMENT_ERROR, "Too long arguments. (limit=" MRB_STRINGIZE(MRB_FUNCALL_ARGC_MAX) ")"); } va_list ap; va_start(ap, argc); for (mrb_int i = 0; i < argc; i++) { argv[i] = va_arg(ap, mrb_value); } va_end(ap); return mrb_funcall_argv(mrb, self, mid, argc, argv); } static mrb_int mrb_ci_kidx(const mrb_callinfo *ci) { if (ci->nk == 0) return -1; return (ci->n == CALL_MAXARGS) ? 2 : ci->n + 1; } static inline mrb_int mrb_bidx(uint8_t n, uint8_t k) { if (n == 15) n = 1; if (k == 15) n += 1; else n += k*2; return n + 1; /* self + args + kargs */ } static inline mrb_int ci_bidx(mrb_callinfo *ci) { return mrb_bidx(ci->n, ci->nk); } mrb_int mrb_ci_bidx(mrb_callinfo *ci) { return ci_bidx(ci); } mrb_int mrb_ci_nregs(mrb_callinfo *ci) { if (!ci) return 4; mrb_int nregs = ci_bidx(ci) + 1; /* self + args + kargs + blk */ const struct RProc *p = ci->proc; if (p && !MRB_PROC_CFUNC_P(p) && p->body.irep && p->body.irep->nregs > nregs) { return p->body.irep->nregs; } return nregs; } mrb_value mrb_obj_missing(mrb_state *mrb, mrb_value mod); static mrb_method_t prepare_missing(mrb_state *mrb, mrb_callinfo *ci, mrb_value recv, mrb_sym mid, mrb_value blk, mrb_bool super) { mrb_sym missing = MRB_SYM(method_missing); mrb_value *argv = &ci->stack[1]; mrb_value args; mrb_method_t m; /* pack positional arguments */ if (ci->n == 15) args = argv[0]; else args = mrb_ary_new_from_values(mrb, ci->n, argv); if (mrb_func_basic_p(mrb, recv, missing, mrb_obj_missing)) { method_missing: if (super) mrb_no_method_error(mrb, mid, args, "no superclass method '%n' for %T", mid, recv); else mrb_method_missing(mrb, mid, recv, args); /* not reached */ } if (mid != missing) { ci->u.target_class = mrb_class(mrb, recv); } m = mrb_vm_find_method(mrb, ci->u.target_class, &ci->u.target_class, missing); if (MRB_METHOD_UNDEF_P(m)) goto method_missing; /* just in case */ stack_extend(mrb, 4); argv = &ci->stack[1]; /* maybe reallocated */ if (ci->nk == 0) { argv[1] = blk; } else { mrb_assert(ci->nk == 15); argv[1] = argv[ci->n]; argv[2] = blk; } argv[0] = args; /* must be replaced after saving argv[0] as it may be a keyword argument */ ci->n = CALL_MAXARGS; /* ci->nk is already set to zero or CALL_MAXARGS */ mrb_ary_unshift(mrb, args, mrb_symbol_value(mid)); ci->mid = missing; return m; } static void funcall_args_capture(mrb_state *mrb, int stoff, mrb_int argc, const mrb_value *argv, mrb_value block, mrb_callinfo *ci) { if (argc < 0 || argc > INT32_MAX) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative or too big argc for funcall (%i)", argc); } ci->nk = 0; /* funcall does not support keyword arguments */ if (argc < CALL_MAXARGS) { mrb_int extends = stoff + argc + 2 /* self + block */; stack_extend_adjust(mrb, extends, &argv); mrb_value *args = mrb->c->ci->stack + stoff + 1 /* self */; stack_copy(args, argv, argc); args[argc] = block; ci->n = (uint8_t)argc; } else { int extends = stoff + 3 /* self + splat + block */; stack_extend_adjust(mrb, extends, &argv); mrb_value *args = mrb->c->ci->stack + stoff + 1 /* self */; args[0] = mrb_ary_new_from_values(mrb, argc, argv); args[1] = block; ci->n = CALL_MAXARGS; } } static inline mrb_value ensure_block(mrb_state *mrb, mrb_value blk) { if (!mrb_nil_p(blk) && !mrb_proc_p(blk)) { blk = mrb_type_convert(mrb, blk, MRB_TT_PROC, MRB_SYM(to_proc)); /* The stack might have been reallocated during mrb_type_convert(), see #3622 */ } return blk; } MRB_API mrb_value mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, const mrb_value *argv, mrb_value blk) { mrb_value val; int ai = mrb_gc_arena_save(mrb); if (!mrb->jmp) { struct mrb_jmpbuf c_jmp; ptrdiff_t nth_ci = mrb->c->ci - mrb->c->cibase; MRB_TRY(&c_jmp) { mrb->jmp = &c_jmp; /* recursive call */ val = mrb_funcall_with_block(mrb, self, mid, argc, argv, blk); mrb->jmp = NULL; } MRB_CATCH(&c_jmp) { /* error */ while (nth_ci < (mrb->c->ci - mrb->c->cibase)) { cipop(mrb); } mrb->jmp = 0; val = mrb_obj_value(mrb->exc); } MRB_END_EXC(&c_jmp); mrb->jmp = NULL; } else { mrb_method_t m; mrb_callinfo *ci = mrb->c->ci; mrb_int n = mrb_ci_nregs(ci); if (!mrb->c->stbase) { stack_init(mrb); } if (ci - mrb->c->cibase > MRB_CALL_LEVEL_MAX) { mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); } blk = ensure_block(mrb, blk); ci = cipush(mrb, n, CINFO_DIRECT, NULL, NULL, BLK_PTR(blk), 0, 0); funcall_args_capture(mrb, 0, argc, argv, blk, ci); ci->u.target_class = mrb_class(mrb, self); m = mrb_vm_find_method(mrb, ci->u.target_class, &ci->u.target_class, mid); if (MRB_METHOD_UNDEF_P(m)) { m = prepare_missing(mrb, ci, self, mid, mrb_nil_value(), FALSE); } else { ci->mid = mid; } ci->proc = MRB_METHOD_PROC_P(m) ? MRB_METHOD_PROC(m) : NULL; if (MRB_METHOD_CFUNC_P(m)) { mrb->exc = NULL; ci->stack[0] = self; val = MRB_METHOD_CFUNC(m)(mrb, self); cipop(mrb); if (mrb->exc != NULL) { mrb_exc_raise(mrb, mrb_obj_value(mrb->exc)); } } else { /* handle alias */ if (MRB_PROC_ALIAS_P(ci->proc)) { ci->mid = ci->proc->body.mid; ci->proc = ci->proc->upper; } ci->cci = CINFO_SKIP; val = mrb_run(mrb, ci->proc, self); } } mrb_gc_arena_restore(mrb, ai); mrb_gc_protect(mrb, val); return val; } MRB_API mrb_value mrb_funcall_argv(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, const mrb_value *argv) { return mrb_funcall_with_block(mrb, self, mid, argc, argv, mrb_nil_value()); } static void check_method_noarg(mrb_state *mrb, const mrb_callinfo *ci) { mrb_int argc = ci->n == CALL_MAXARGS ? RARRAY_LEN(ci->stack[1]) : ci->n; if (ci->nk > 0) { mrb_value kdict = ci->stack[mrb_ci_kidx(ci)]; if (!(mrb_hash_p(kdict) && mrb_hash_empty_p(mrb, kdict))) { argc++; } } if (argc > 0) { mrb_argnum_error(mrb, argc, 0, 0); } } static mrb_value exec_irep(mrb_state *mrb, mrb_value self, const struct RProc *p) { mrb_callinfo *ci = mrb->c->ci; ci->stack[0] = self; /* handle alias */ if (MRB_PROC_ALIAS_P(p)) { ci->mid = p->body.mid; p = p->upper; } CI_PROC_SET(ci, p); if (MRB_PROC_CFUNC_P(p)) { if (MRB_PROC_NOARG_P(p) && (ci->n > 0 || ci->nk > 0)) { check_method_noarg(mrb, ci); } return MRB_PROC_CFUNC(p)(mrb, self); } mrb_int nregs = p->body.irep->nregs; mrb_int keep = ci_bidx(ci)+1; if (nregs < keep) { stack_extend(mrb, keep); } else { stack_extend(mrb, nregs); stack_clear(ci->stack+keep, nregs-keep); } cipush(mrb, 0, 0, NULL, NULL, NULL, 0, 0); return self; } mrb_value mrb_exec_irep(mrb_state *mrb, mrb_value self, const struct RProc *p) { mrb_callinfo *ci = mrb->c->ci; if (ci->cci == CINFO_NONE) { return exec_irep(mrb, self, p); } else { mrb_value ret; if (MRB_PROC_CFUNC_P(p)) { if (MRB_PROC_NOARG_P(p) && (ci->n > 0 || ci->nk > 0)) { check_method_noarg(mrb, ci); } ci = cipush(mrb, 0, CINFO_DIRECT, CI_TARGET_CLASS(ci), p, NULL, ci->mid, ci->n|(ci->nk<<4)); mrb->exc = NULL; ret = MRB_PROC_CFUNC(p)(mrb, self); cipop(mrb); } else { mrb_int keep = ci_bidx(ci) + 1; /* receiver + block */ ci = cipush(mrb, 0, CINFO_SKIP, CI_TARGET_CLASS(ci), p, NULL, ci->mid, ci->n|(ci->nk<<4)); ret = mrb_vm_run(mrb, p, self, keep); } if (mrb->exc && mrb->jmp) { mrb_exc_raise(mrb, mrb_obj_value(mrb->exc)); } return ret; } } mrb_value mrb_object_exec(mrb_state *mrb, mrb_value self, struct RClass *target_class) { mrb_callinfo *ci = mrb->c->ci; mrb_int bidx = ci_bidx(ci); mrb_value blk = ci->stack[bidx]; if (mrb_nil_p(blk)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } mrb_assert(mrb_proc_p(blk)); mrb_gc_protect(mrb, blk); ci->stack[bidx] = mrb_nil_value(); mrb_vm_ci_target_class_set(ci, target_class); return mrb_exec_irep(mrb, self, mrb_proc_ptr(blk)); } static mrb_noreturn void vis_error(mrb_state *mrb, mrb_sym mid, mrb_value args, mrb_value recv, mrb_bool priv) { mrb_no_method_error(mrb, mid, args, "%s method '%n' called for %T", (priv ? "private" : "protected"), mid, recv); } static mrb_value send_method(mrb_state *mrb, mrb_value self, mrb_bool pub) { mrb_callinfo *ci = mrb->c->ci; int n = ci->n; mrb_sym name; if (ci->cci > CINFO_NONE) { funcall:; const mrb_value *argv; mrb_int argc; mrb_value block; mrb_get_args(mrb, "n*&", &name, &argv, &argc, &block); return mrb_funcall_with_block(mrb, self, name, argc, argv, block); } mrb_method_t m; mrb_value *regs = mrb->c->ci->stack+1; if (n == 0) { argnum_error: mrb_argnum_error(mrb, 0, 1, -1); } else if (n == 15) { if (RARRAY_LEN(regs[0]) == 0) goto argnum_error; name = mrb_obj_to_sym(mrb, RARRAY_PTR(regs[0])[0]); } else { name = mrb_obj_to_sym(mrb, regs[0]); } struct RClass *c = mrb_class(mrb, self); m = mrb_vm_find_method(mrb, c, &c, name); if (MRB_METHOD_UNDEF_P(m)) { /* call method_missing */ goto funcall; } if (pub) { mrb_bool priv = TRUE; if (m.flags & MRB_METHOD_PRIVATE_FL) { vis_err:; if (n == 15) { n = RARRAY_LEN(regs[0]) - 1; regs = RARRAY_PTR(regs[0]); } vis_error(mrb, name, mrb_ary_new_from_values(mrb, n, regs+1), self, priv); } else if ((m.flags & MRB_METHOD_PROTECTED_FL) && mrb_obj_is_kind_of(mrb, self, ci->u.target_class)) { priv = FALSE; goto vis_err; } } ci->mid = name; ci->u.target_class = c; /* remove first symbol from arguments */ if (n == 15) { /* variable length arguments */ regs[0] = mrb_ary_subseq(mrb, regs[0], 1, RARRAY_LEN(regs[0]) - 1); } else { /* n > 0 */ for (int i=0; ink > 0) { regs[n+1] = regs[n+2]; /* copy block */ } ci->n--; } const struct RProc *p; if (MRB_METHOD_PROC_P(m)) { p = MRB_METHOD_PROC(m); /* handle alias */ if (MRB_PROC_ALIAS_P(p)) { ci->mid = p->body.mid; p = p->upper; } CI_PROC_SET(ci, p); } if (MRB_METHOD_CFUNC_P(m)) { if (MRB_METHOD_NOARG_P(m) && (ci->n > 0 || ci->nk > 0)) { check_method_noarg(mrb, ci); } return MRB_METHOD_CFUNC(m)(mrb, self); } return exec_irep(mrb, self, p); } /* 15.3.1.3.4 */ /* 15.3.1.3.44 */ /* * call-seq: * obj.send(symbol [, args...]) -> obj * obj.__send__(symbol [, args...]) -> obj * * Invokes the method identified by _symbol_, passing it any * arguments specified. You can use __send__ if the name * +send+ clashes with an existing method in _obj_. * * class Klass * def hello(*args) * "Hello " + args.join(' ') * end * end * k = Klass.new * k.send :hello, "gentle", "readers" #=> "Hello gentle readers" */ mrb_value mrb_f_send(mrb_state *mrb, mrb_value self) { return send_method(mrb, self, FALSE); } /* * call-seq: * obj.public_send(symbol [, args...]) -> obj * * Invokes the method identified by symbol, passing it any * arguments specified. Unlike send, public_send calls public methods only. * When the method is identified by a string, the string is converted to a * symbol. * * 1.public_send(:puts, "hello") # causes NoMethodError */ mrb_value mrb_f_public_send(mrb_state *mrb, mrb_value self) { return send_method(mrb, self, TRUE); } static void check_block(mrb_state *mrb, mrb_value blk) { if (mrb_nil_p(blk)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } if (!mrb_proc_p(blk)) { mrb_raise(mrb, E_TYPE_ERROR, "not a block"); } } static mrb_value eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c) { check_block(mrb, blk); mrb_callinfo *ci = mrb->c->ci; if (ci->cci == CINFO_DIRECT) { return mrb_yield_with_class(mrb, blk, 1, &self, self, c); } ci->u.target_class = c; const struct RProc *p = mrb_proc_ptr(blk); /* just in case irep is NULL; #6065 */ if (p->body.irep == NULL) return mrb_nil_value(); CI_PROC_SET(ci, p); ci->n = 1; ci->nk = 0; ci->mid = ci[-1].mid; MRB_CI_SET_VISIBILITY_BREAK(ci); if (MRB_PROC_CFUNC_P(p)) { stack_extend(mrb, 4); mrb->c->ci->stack[0] = self; mrb->c->ci->stack[1] = self; mrb->c->ci->stack[2] = mrb_nil_value(); return MRB_PROC_CFUNC(p)(mrb, self); } int nregs = p->body.irep->nregs; if (nregs < 4) nregs = 4; stack_extend(mrb, nregs); mrb->c->ci->stack[0] = self; mrb->c->ci->stack[1] = self; stack_clear(mrb->c->ci->stack+2, nregs-2); cipush(mrb, 0, 0, NULL, NULL, NULL, 0, 0); return self; } /* 15.2.2.4.35 */ /* * call-seq: * mod.class_eval {| | block } -> obj * mod.module_eval {| | block } -> obj * * Evaluates block in the context of _mod_. This can * be used to add methods to a class. module_eval returns * the result of evaluating its argument. */ mrb_value mrb_mod_module_eval(mrb_state *mrb, mrb_value mod) { mrb_value a, b; if (mrb_get_args(mrb, "|S&", &a, &b) == 1) { mrb_raise(mrb, E_NOTIMP_ERROR, "module_eval/class_eval with string not implemented"); } return eval_under(mrb, mod, b, mrb_class_ptr(mod)); } /* 15.3.1.3.18 */ /* * call-seq: * obj.instance_eval {| | block } -> obj * * Evaluates the given block,within the context of the receiver (_obj_). * In order to set the context, the variable +self+ is set to _obj_ while * the code is executing, giving the code access to _obj_'s * instance variables. In the version of instance_eval * that takes a +String+, the optional second and third * parameters supply a filename and starting line number that are used * when reporting compilation errors. * * class KlassWithSecret * def initialize * @secret = 99 * end * end * k = KlassWithSecret.new * k.instance_eval { @secret } #=> 99 */ mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self) { mrb_value a, b; if (mrb_get_args(mrb, "|S&", &a, &b) == 1) { mrb_raise(mrb, E_NOTIMP_ERROR, "instance_eval with string not implemented"); } return eval_under(mrb, self, b, mrb_singleton_class_ptr(mrb, self)); } static mrb_value yield_with_attr(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv, mrb_value self, struct RClass *c, mrb_bool vis_break) { check_block(mrb, b); mrb_callinfo *ci = mrb->c->ci; mrb_int n = mrb_ci_nregs(ci); const struct RProc *p = mrb_proc_ptr(b); mrb_sym mid; if (MRB_PROC_ENV_P(p)) { mid = p->e.env->mid; } else { mid = ci->mid; } ci = cipush(mrb, n, CINFO_DIRECT, NULL, NULL, NULL, mid, 0); funcall_args_capture(mrb, 0, argc, argv, mrb_nil_value(), ci); ci->u.target_class = c; ci->proc = p; if (vis_break) { MRB_CI_SET_VISIBILITY_BREAK(ci); } mrb_value val; if (MRB_PROC_CFUNC_P(p)) { mrb->exc = NULL; ci->stack[0] = self; val = MRB_PROC_CFUNC(p)(mrb, self); cipop(mrb); if (mrb->exc && mrb->jmp) { mrb_exc_raise(mrb, mrb_obj_value(mrb->exc)); } } else { ci->cci = CINFO_SKIP; val = mrb_run(mrb, p, self); } return val; } MRB_API mrb_value mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv, mrb_value self, struct RClass *c) { return yield_with_attr(mrb, b, argc, argv, self, c, TRUE); } MRB_API mrb_value mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv) { const struct RProc *p = mrb_proc_ptr(b); struct RClass *tc; mrb_value self = mrb_proc_get_self(mrb, p, &tc); return yield_with_attr(mrb, b, argc, argv, self, tc, FALSE); } MRB_API mrb_value mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg) { const struct RProc *p = mrb_proc_ptr(b); struct RClass *tc; mrb_value self = mrb_proc_get_self(mrb, p, &tc); return yield_with_attr(mrb, b, 1, &arg, self, tc, FALSE); } mrb_value mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const mrb_value *argv) { check_block(mrb, b); const struct RProc *p = mrb_proc_ptr(b); mrb_callinfo *ci = mrb->c->ci; stack_extend_adjust(mrb, 4, &argv); mrb->c->ci->stack[1] = mrb_ary_new_from_values(mrb, argc, argv); mrb->c->ci->stack[2] = mrb_nil_value(); mrb->c->ci->stack[3] = mrb_nil_value(); ci->n = 15; ci->nk = 0; return exec_irep(mrb, self, p); } #define RBREAK_TAG_FOREACH(f) \ f(RBREAK_TAG_BREAK, 0) \ f(RBREAK_TAG_JUMP, 1) \ f(RBREAK_TAG_STOP, 2) #define RBREAK_TAG_DEFINE(tag, i) tag = i, enum { RBREAK_TAG_FOREACH(RBREAK_TAG_DEFINE) }; #undef RBREAK_TAG_DEFINE #define RBREAK_TAG_BIT 3 #define RBREAK_TAG_BIT_OFF 8 #define RBREAK_TAG_MASK (~(~UINT32_C(0) << RBREAK_TAG_BIT)) static inline uint32_t mrb_break_tag_get(struct RBreak *brk) { return (brk->flags >> RBREAK_TAG_BIT_OFF) & RBREAK_TAG_MASK; } static inline void mrb_break_tag_set(struct RBreak *brk, uint32_t tag) { brk->flags &= ~(RBREAK_TAG_MASK << RBREAK_TAG_BIT_OFF); brk->flags |= (tag & RBREAK_TAG_MASK) << RBREAK_TAG_BIT_OFF; } static struct RBreak* break_new(mrb_state *mrb, uint32_t tag, const mrb_callinfo *return_ci, mrb_value val) { mrb_assert((size_t)(return_ci - mrb->c->cibase) <= (size_t)(mrb->c->ci - mrb->c->cibase)); struct RBreak *brk = MRB_OBJ_ALLOC(mrb, MRB_TT_BREAK, NULL); brk->ci_break_index = return_ci - mrb->c->cibase; mrb_break_value_set(brk, val); mrb_break_tag_set(brk, tag); return brk; } #define MRB_CATCH_FILTER_RESCUE (UINT32_C(1) << MRB_CATCH_RESCUE) #define MRB_CATCH_FILTER_ENSURE (UINT32_C(1) << MRB_CATCH_ENSURE) #define MRB_CATCH_FILTER_ALL (MRB_CATCH_FILTER_RESCUE | MRB_CATCH_FILTER_ENSURE) static const struct mrb_irep_catch_handler * catch_handler_find(const mrb_irep *irep, const mrb_code *pc, uint32_t filter) { /* The comparison operators use `>` and `<=` because pc already points to the next instruction */ #define catch_cover_p(pc, beg, end) ((pc) > (ptrdiff_t)(beg) && (pc) <= (ptrdiff_t)(end)) mrb_assert(irep && irep->clen > 0); ptrdiff_t xpc = pc - irep->iseq; /* If it retry at the top level, pc will be 0, so check with -1 as the start position */ mrb_assert(catch_cover_p(xpc, -1, irep->ilen)); if (!catch_cover_p(xpc, -1, irep->ilen)) return NULL; /* Currently uses a simple linear search to avoid processing complexity. */ size_t cnt = irep->clen; const struct mrb_irep_catch_handler *e = mrb_irep_catch_handler_table(irep) + cnt - 1; for (; cnt > 0; cnt--, e--) { if (((UINT32_C(1) << e->type) & filter) && catch_cover_p(xpc, mrb_irep_catch_handler_unpack(e->begin), mrb_irep_catch_handler_unpack(e->end))) { return e; } } #undef catch_cover_p return NULL; } #define RAISE_EXC(mrb, exc) do { \ mrb_value exc_value = (exc); \ mrb_exc_set(mrb, exc_value); \ goto L_RAISE; \ } while (0) #define RAISE_LIT(mrb, c, str) RAISE_EXC(mrb, mrb_exc_new_lit(mrb, c, str)) #define RAISE_FORMAT(mrb, c, fmt, ...) RAISE_EXC(mrb, mrb_exc_new_str(mrb, c, mrb_format(mrb, fmt, __VA_ARGS__))) static void argnum_error(mrb_state *mrb, mrb_int num) { mrb_int argc = mrb->c->ci->n; if (argc == 15) { mrb_value args = mrb->c->ci->stack[1]; if (mrb_array_p(args)) { argc = RARRAY_LEN(args); } } if (argc == 0 && mrb->c->ci->nk != 0 && !mrb_hash_empty_p(mrb, mrb->c->ci->stack[1])) { argc++; } mrb_value str = mrb_format(mrb, "wrong number of arguments (given %i, expected %i)", argc, num); mrb_value exc = mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str); mrb_exc_set(mrb, exc); } static mrb_bool break_tag_p(struct RBreak *brk, uint32_t tag) { return (brk != NULL && brk->tt == MRB_TT_BREAK) ? TRUE : FALSE; } static void prepare_tagged_break(mrb_state *mrb, uint32_t tag, const mrb_callinfo *return_ci, mrb_value val) { if (break_tag_p((struct RBreak*)mrb->exc, tag)) { mrb_break_tag_set((struct RBreak*)mrb->exc, tag); } else { mrb->exc = (struct RObject*)break_new(mrb, tag, return_ci, val); } } #define THROW_TAGGED_BREAK(mrb, tag, return_ci, val) \ do { \ prepare_tagged_break(mrb, tag, return_ci, val); \ goto L_CATCH_TAGGED_BREAK; \ } while (0) #define UNWIND_ENSURE(mrb, ci, pc, tag, return_ci, val) \ do { \ const struct RProc *proc = (ci)->proc; \ if (proc && !MRB_PROC_CFUNC_P(proc) && (irep = proc->body.irep) && irep->clen > 0 && \ (ch = catch_handler_find(irep, pc, MRB_CATCH_FILTER_ENSURE))) { \ THROW_TAGGED_BREAK(mrb, tag, return_ci, val); \ } \ } while (0) /* * CHECKPOINT_RESTORE(tag) { * This part is executed when jumping by the same "tag" of RBreak (it is not executed the first time). * Write the code required (initialization of variables, etc.) for the subsequent processing. * } * CHECKPOINT_MAIN(tag) { * This part is always executed. * } * CHECKPOINT_END(tag); * * ... * * // Jump to CHECKPOINT_RESTORE with the same "tag". * goto CHECKPOINT_LABEL_MAKE(tag); */ #define CHECKPOINT_LABEL_MAKE(tag) L_CHECKPOINT_ ## tag #define CHECKPOINT_RESTORE(tag) \ do { \ if (FALSE) { \ CHECKPOINT_LABEL_MAKE(tag): \ do { #define CHECKPOINT_MAIN(tag) \ } while (0); \ } \ do { #define CHECKPOINT_END(tag) \ } while (0); \ } while (0) #ifdef MRB_USE_DEBUG_HOOK #define CODE_FETCH_HOOK(mrb, irep, pc, regs) if ((mrb)->code_fetch_hook) (mrb)->code_fetch_hook((mrb), (irep), (pc), (regs)); #else #define CODE_FETCH_HOOK(mrb, irep, pc, regs) #endif #ifdef MRB_BYTECODE_DECODE_OPTION #define BYTECODE_DECODER(x) ((mrb)->bytecode_decoder)?(mrb)->bytecode_decoder((mrb), (x)):(x) #else #define BYTECODE_DECODER(x) (x) #endif #ifndef MRB_USE_VM_SWITCH_DISPATCH #if !defined __GNUC__ && !defined __clang__ && !defined __INTEL_COMPILER #define MRB_USE_VM_SWITCH_DISPATCH #endif #endif /* ifndef MRB_USE_VM_SWITCH_DISPATCH */ #ifdef MRB_USE_VM_SWITCH_DISPATCH #define INIT_DISPATCH for (;;) { insn = BYTECODE_DECODER(*ci->pc); CODE_FETCH_HOOK(mrb, irep, ci->pc, regs); switch (insn) { #define CASE(insn,ops) case insn: { const mrb_code *pc = ci->pc+1; FETCH_ ## ops (); ci->pc = pc; } L_ ## insn ## _BODY: #define NEXT goto L_END_DISPATCH #define JUMP NEXT #define END_DISPATCH L_END_DISPATCH:;}} #else #define INIT_DISPATCH JUMP; return mrb_nil_value(); #define CASE(insn,ops) L_ ## insn: { const mrb_code *pc = ci->pc+1; FETCH_ ## ops (); ci->pc = pc; } L_ ## insn ## _BODY: #define NEXT insn=BYTECODE_DECODER(*ci->pc); CODE_FETCH_HOOK(mrb, irep, ci->pc, regs); goto *optable[insn] #define JUMP NEXT #define END_DISPATCH #endif MRB_API mrb_value mrb_vm_run(mrb_state *mrb, const struct RProc *proc, mrb_value self, mrb_int stack_keep) { const mrb_irep *irep = proc->body.irep; struct mrb_context *c = mrb->c; #ifdef MRB_DEBUG ptrdiff_t cioff = c->ci - c->cibase; #endif mrb_int nregs = irep->nregs; if (!c->stbase) { stack_init(mrb); } if (stack_keep > nregs) nregs = stack_keep; else { struct REnv *e = CI_ENV(mrb->c->ci); if (e && (stack_keep == 0 || irep->nlocals < MRB_ENV_LEN(e))) { ci_env_set(mrb->c->ci, NULL); mrb_env_unshare(mrb, e, FALSE); } } stack_extend(mrb, nregs); stack_clear(c->ci->stack + stack_keep, nregs - stack_keep); c->ci->stack[0] = self; mrb_value result = mrb_vm_exec(mrb, proc, irep->iseq); mrb_assert(mrb->c == c); /* do not switch fibers via mrb_vm_run(), unlike mrb_vm_exec() */ mrb_assert(c->ci == c->cibase || (c->ci - c->cibase) == cioff - 1); return result; } static struct RClass* check_target_class(mrb_state *mrb) { struct RClass *target = CI_TARGET_CLASS(mrb->c->ci); if (!target) { mrb_raise(mrb, E_TYPE_ERROR, "no class/module to add method"); } return target; } #define regs (ci->stack) static mrb_value hash_new_from_regs(mrb_state *mrb, mrb_int argc, mrb_int idx) { mrb_value hash = mrb_hash_new_capa(mrb, argc); mrb_callinfo *ci = mrb->c->ci; while (argc--) { mrb_hash_set(mrb, hash, regs[idx+0], regs[idx+1]); ci = mrb->c->ci; idx += 2; } return hash; } #define ary_new_from_regs(mrb, argc, idx) mrb_ary_new_from_values(mrb, (argc), ®s[idx]); MRB_API mrb_value mrb_vm_exec(mrb_state *mrb, const struct RProc *begin_proc, const mrb_code *iseq) { /* mrb_assert(MRB_PROC_CFUNC_P(begin_proc)) */ const mrb_irep *irep = begin_proc->body.irep; mrb_code insn; int ai = mrb_gc_arena_save(mrb); struct mrb_jmpbuf *prev_jmp = mrb->jmp; struct mrb_jmpbuf c_jmp; uint32_t a; uint16_t b; uint16_t c; mrb_sym mid; const struct mrb_irep_catch_handler *ch; #ifndef MRB_USE_VM_SWITCH_DISPATCH static const void * const optable[] = { #define OPCODE(x,_) &&L_OP_ ## x, #include #undef OPCODE }; #endif mrb->exc = NULL; mrb_callinfo *ci = mrb->c->ci; CI_PROC_SET(ci, begin_proc); ci->pc = iseq; RETRY_TRY_BLOCK: MRB_TRY(&c_jmp) { if (mrb->exc) { mrb_gc_arena_restore(mrb, ai); if (mrb->exc->tt == MRB_TT_BREAK) goto L_BREAK; goto L_RAISE; } mrb->jmp = &c_jmp; INIT_DISPATCH { CASE(OP_NOP, Z) { /* do nothing */ NEXT; } CASE(OP_MOVE, BB) { regs[a] = regs[b]; NEXT; } CASE(OP_LOADL, BB) { switch (irep->pool[b].tt) { /* number */ case IREP_TT_INT32: regs[a] = mrb_int_value(mrb, (mrb_int)irep->pool[b].u.i32); break; case IREP_TT_INT64: #if defined(MRB_INT64) regs[a] = mrb_int_value(mrb, (mrb_int)irep->pool[b].u.i64); break; #else #if defined(MRB_64BIT) if (INT32_MIN <= irep->pool[b].u.i64 && irep->pool[b].u.i64 <= INT32_MAX) { regs[a] = mrb_int_value(mrb, (mrb_int)irep->pool[b].u.i64); break; } #endif goto L_INT_OVERFLOW; #endif case IREP_TT_BIGINT: #ifdef MRB_USE_BIGINT { const char *s = irep->pool[b].u.str; regs[a] = mrb_bint_new_str(mrb, s+2, (uint8_t)s[0], s[1]); } break; #else goto L_INT_OVERFLOW; #endif #ifndef MRB_NO_FLOAT case IREP_TT_FLOAT: regs[a] = mrb_float_value(mrb, irep->pool[b].u.f); break; #endif default: /* should not happen (tt:string) */ regs[a] = mrb_nil_value(); break; } NEXT; } CASE(OP_LOADI8, BB) { SET_FIXNUM_VALUE(regs[a], b); NEXT; } CASE(OP_LOADINEG, BB) { SET_FIXNUM_VALUE(regs[a], -b); NEXT; } CASE(OP_LOADI__1,B) goto L_LOADI; CASE(OP_LOADI_0,B) goto L_LOADI; CASE(OP_LOADI_1,B) goto L_LOADI; CASE(OP_LOADI_2,B) goto L_LOADI; CASE(OP_LOADI_3,B) goto L_LOADI; CASE(OP_LOADI_4,B) goto L_LOADI; CASE(OP_LOADI_5,B) goto L_LOADI; CASE(OP_LOADI_6,B) goto L_LOADI; CASE(OP_LOADI_7, B) { L_LOADI: SET_FIXNUM_VALUE(regs[a], (mrb_int)insn - (mrb_int)OP_LOADI_0); NEXT; } CASE(OP_LOADI16, BS) { SET_FIXNUM_VALUE(regs[a], (mrb_int)(int16_t)b); NEXT; } CASE(OP_LOADI32, BSS) { SET_INT_VALUE(mrb, regs[a], (int32_t)(((uint32_t)b<<16)+c)); NEXT; } CASE(OP_LOADSYM, BB) { SET_SYM_VALUE(regs[a], irep->syms[b]); NEXT; } CASE(OP_LOADNIL, B) { SET_NIL_VALUE(regs[a]); NEXT; } CASE(OP_LOADSELF, B) { regs[a] = regs[0]; NEXT; } CASE(OP_LOADT, B) { SET_TRUE_VALUE(regs[a]); NEXT; } CASE(OP_LOADF, B) { SET_FALSE_VALUE(regs[a]); NEXT; } CASE(OP_GETGV, BB) { mrb_value val = mrb_gv_get(mrb, irep->syms[b]); ci = mrb->c->ci; regs[a] = val; NEXT; } CASE(OP_SETGV, BB) { mrb_gv_set(mrb, irep->syms[b], regs[a]); ci = mrb->c->ci; NEXT; } CASE(OP_GETSV, BB) { mrb_value val = mrb_vm_special_get(mrb, irep->syms[b]); ci = mrb->c->ci; regs[a] = val; NEXT; } CASE(OP_SETSV, BB) { mrb_vm_special_set(mrb, irep->syms[b], regs[a]); ci = mrb->c->ci; NEXT; } CASE(OP_GETIV, BB) { regs[a] = mrb_iv_get(mrb, regs[0], irep->syms[b]); ci = mrb->c->ci; NEXT; } CASE(OP_SETIV, BB) { mrb_iv_set(mrb, regs[0], irep->syms[b], regs[a]); ci = mrb->c->ci; NEXT; } CASE(OP_GETCV, BB) { mrb_value val; val = mrb_vm_cv_get(mrb, irep->syms[b]); ci = mrb->c->ci; regs[a] = val; NEXT; } CASE(OP_SETCV, BB) { mrb_vm_cv_set(mrb, irep->syms[b], regs[a]); ci = mrb->c->ci; NEXT; } CASE(OP_GETIDX, B) { mrb_value va = regs[a], vb = regs[a+1]; switch (mrb_type(va)) { case MRB_TT_ARRAY: if (!mrb_integer_p(vb)) goto getidx_fallback; else { mrb_int idx = mrb_integer(vb); if (0 <= idx && idx < RARRAY_LEN(va)) { regs[a] = RARRAY_PTR(va)[idx]; } else { regs[a] = mrb_ary_entry(va, idx); } } break; case MRB_TT_HASH: va = mrb_hash_get(mrb, va, vb); ci = mrb->c->ci; regs[a] = va; break; case MRB_TT_STRING: switch (mrb_type(vb)) { case MRB_TT_INTEGER: case MRB_TT_STRING: case MRB_TT_RANGE: va = mrb_str_aref(mrb, va, vb, mrb_undef_value()); regs[a] = va; break; default: goto getidx_fallback; } break; default: getidx_fallback: mid = MRB_OPSYM(aref); goto L_SEND_SYM; } NEXT; } CASE(OP_SETIDX, B) { c = 2; mid = MRB_OPSYM(aset); SET_NIL_VALUE(regs[a+3]); goto L_SENDB_SYM; } CASE(OP_GETCONST, BB) { mrb_value v = mrb_vm_const_get(mrb, irep->syms[b]); ci = mrb->c->ci; regs[a] = v; NEXT; } CASE(OP_SETCONST, BB) { ci = mrb->c->ci; struct RClass *c = MRB_PROC_TARGET_CLASS(ci->proc); if (!c) c = mrb->object_class; mrb_const_set(mrb, mrb_obj_value(c), irep->syms[b], regs[a]); NEXT; } CASE(OP_GETMCNST, BB) { mrb_value v = mrb_const_get(mrb, regs[a], irep->syms[b]); ci = mrb->c->ci; regs[a] = v; NEXT; } CASE(OP_SETMCNST, BB) { mrb_const_set(mrb, regs[a+1], irep->syms[b], regs[a]); ci = mrb->c->ci; NEXT; } CASE(OP_GETUPVAR, BBB) { struct REnv *e = uvenv(mrb, c); if (e && b < MRB_ENV_LEN(e)) { regs[a] = e->stack[b]; } else { regs[a] = mrb_nil_value(); } NEXT; } CASE(OP_SETUPVAR, BBB) { struct REnv *e = uvenv(mrb, c); if (e) { if (b < MRB_ENV_LEN(e)) { e->stack[b] = regs[a]; mrb_write_barrier(mrb, (struct RBasic*)e); } } NEXT; } CASE(OP_JMP, S) { ci->pc += (int16_t)a; JUMP; } CASE(OP_JMPIF, BS) { if (mrb_test(regs[a])) { ci->pc += (int16_t)b; JUMP; } NEXT; } CASE(OP_JMPNOT, BS) { if (!mrb_test(regs[a])) { ci->pc += (int16_t)b; JUMP; } NEXT; } CASE(OP_JMPNIL, BS) { if (mrb_nil_p(regs[a])) { ci->pc += (int16_t)b; JUMP; } NEXT; } CASE(OP_JMPUW, S) { a = (uint32_t)((ci->pc - irep->iseq) + (int16_t)a); CHECKPOINT_RESTORE(RBREAK_TAG_JUMP) { struct RBreak *brk = (struct RBreak*)mrb->exc; mrb_value target = mrb_break_value_get(brk); mrb_assert(mrb_integer_p(target)); a = (uint32_t)mrb_integer(target); mrb_assert(a >= 0 && a < irep->ilen); } CHECKPOINT_MAIN(RBREAK_TAG_JUMP) { if (irep->clen > 0 && (ch = catch_handler_find(irep, ci->pc, MRB_CATCH_FILTER_ENSURE))) { /* avoiding a jump from a catch handler into the same handler */ if (a < mrb_irep_catch_handler_unpack(ch->begin) || a > mrb_irep_catch_handler_unpack(ch->end)) { THROW_TAGGED_BREAK(mrb, RBREAK_TAG_JUMP, mrb->c->ci, mrb_fixnum_value(a)); } } } CHECKPOINT_END(RBREAK_TAG_JUMP); mrb->exc = NULL; /* clear break object */ ci->pc = irep->iseq + a; JUMP; } CASE(OP_EXCEPT, B) { mrb_value exc; if (mrb->exc == NULL) { exc = mrb_nil_value(); } else { switch (mrb->exc->tt) { case MRB_TT_BREAK: case MRB_TT_EXCEPTION: exc = mrb_obj_value(mrb->exc); break; default: mrb_assert(!"bad mrb_type"); exc = mrb_nil_value(); break; } mrb->exc = NULL; } regs[a] = exc; NEXT; } CASE(OP_RESCUE, BB) { mrb_value exc = regs[a]; /* exc on stack */ mrb_value e = regs[b]; struct RClass *ec; switch (mrb_type(e)) { case MRB_TT_CLASS: case MRB_TT_MODULE: break; default: RAISE_LIT(mrb, E_TYPE_ERROR, "class or module required for rescue clause"); } ec = mrb_class_ptr(e); regs[b] = mrb_bool_value(mrb_obj_is_kind_of(mrb, exc, ec)); NEXT; } CASE(OP_RAISEIF, B) { mrb_value exc; exc = regs[a]; if (mrb_nil_p(exc)) { mrb->exc = NULL; } else if (mrb_break_p(exc)) { struct RBreak *brk; mrb->exc = mrb_obj_ptr(exc); L_BREAK: brk = (struct RBreak*)mrb->exc; switch (mrb_break_tag_get(brk)) { #define DISPATCH_CHECKPOINTS(n, i) case n: goto CHECKPOINT_LABEL_MAKE(n); RBREAK_TAG_FOREACH(DISPATCH_CHECKPOINTS) #undef DISPATCH_CHECKPOINTS default: mrb_assert(!"wrong break tag"); } } else { mrb_exc_set(mrb, exc); L_RAISE: ci = mrb->c->ci; while (!ci->proc || MRB_PROC_CFUNC_P(ci->proc) || !(irep = ci->proc->body.irep) || irep->clen < 1 || (ch = catch_handler_find(irep, ci->pc, MRB_CATCH_FILTER_ALL)) == NULL) { if (ci != mrb->c->cibase) { ci = cipop(mrb); if (ci[1].cci == CINFO_SKIP) { mrb_assert(prev_jmp != NULL); mrb->jmp = prev_jmp; MRB_THROW(prev_jmp); } } else if (mrb->c == mrb->root_c) { ci->stack = mrb->c->stbase; mrb->jmp = prev_jmp; return mrb_obj_value(mrb->exc); } else { struct mrb_context *c = mrb->c; fiber_terminate(mrb, c, ci); if (!c->vmexec) goto L_RAISE; mrb->jmp = prev_jmp; if (!prev_jmp) return mrb_obj_value(mrb->exc); MRB_THROW(prev_jmp); } } if (FALSE) { L_CATCH_TAGGED_BREAK: /* from THROW_TAGGED_BREAK() or UNWIND_ENSURE() */ ci = mrb->c->ci; } irep = ci->proc->body.irep; stack_extend(mrb, irep->nregs); ci->pc = irep->iseq + mrb_irep_catch_handler_unpack(ch->target); } NEXT; } CASE(OP_SSEND, BBB) { regs[a] = regs[0]; } goto L_SENDB; CASE(OP_SSENDB, BBB) { regs[a] = regs[0]; } goto L_SENDB; CASE(OP_SEND, BBB) goto L_SENDB; L_SEND_SYM: c = 1; /* push nil after arguments */ SET_NIL_VALUE(regs[a+2]); goto L_SENDB_SYM; CASE(OP_SENDB, BBB) L_SENDB: mid = irep->syms[b]; L_SENDB_SYM: { mrb_method_t m; mrb_value recv, blk; mrb_int bidx, new_bidx; if (c < CALL_MAXARGS) { /* fast path limited to fixed length arguments of less than 15 */ bidx = a + c + 1 /* self */; new_bidx = bidx; } else { int n = c&0xf; int nk = (c>>4)&0xf; bidx = a + mrb_bidx(n,nk); new_bidx = bidx; if (nk == CALL_MAXARGS) { mrb_ensure_hash_type(mrb, regs[a+(n==CALL_MAXARGS?1:n)+1]); } else if (nk > 0) { /* pack keyword arguments */ mrb_int kidx = a+(n==CALL_MAXARGS?1:n)+1; mrb_value kdict = hash_new_from_regs(mrb, nk, kidx); ci = mrb->c->ci; regs[kidx] = kdict; nk = CALL_MAXARGS; c = n | (nk<<4); new_bidx = a+mrb_bidx(n, nk); } } mrb_assert(bidx < irep->nregs); if (insn == OP_SEND || insn == OP_SSEND) { /* clear block argument */ SET_NIL_VALUE(regs[new_bidx]); SET_NIL_VALUE(blk); } else { blk = ensure_block(mrb, regs[bidx]); ci = mrb->c->ci; regs[new_bidx] = blk; } ci = cipush(mrb, a, CINFO_DIRECT, NULL, NULL, BLK_PTR(blk), 0, c); recv = regs[0]; ci->u.target_class = (insn == OP_SUPER) ? CI_TARGET_CLASS(ci - 1)->super : mrb_class(mrb, recv); m = mrb_vm_find_method(mrb, ci->u.target_class, &ci->u.target_class, mid); if (MRB_METHOD_UNDEF_P(m)) { m = prepare_missing(mrb, ci, recv, mid, blk, (insn == OP_SUPER)); } else { ci->mid = mid; } if (insn == OP_SEND || insn == OP_SENDB) { mrb_bool priv = TRUE; if (m.flags & MRB_METHOD_PRIVATE_FL) { vis_err:; mrb_value args = (ci->n == 15) ? regs[1] : mrb_ary_new_from_values(mrb, ci->n, regs+1); vis_error(mrb, mid, args, recv, priv); } else if ((m.flags & MRB_METHOD_PROTECTED_FL) && mrb_obj_is_kind_of(mrb, recv, ci->u.target_class)) { priv = FALSE; goto vis_err; } } ci->cci = CINFO_NONE; if (MRB_METHOD_PROC_P(m)) { const struct RProc *p = MRB_METHOD_PROC(m); /* handle alias */ if (MRB_PROC_ALIAS_P(p)) { ci->mid = p->body.mid; p = p->upper; } CI_PROC_SET(ci, p); if (!MRB_PROC_CFUNC_P(p)) { /* setup environment for calling method */ irep = p->body.irep; stack_extend(mrb, (irep->nregs < 4) ? 4 : irep->nregs); ci->pc = irep->iseq; JUMP; } else { if (MRB_PROC_NOARG_P(p) && (ci->n > 0 || ci->nk > 0)) { check_method_noarg(mrb, ci); } recv = MRB_PROC_CFUNC(p)(mrb, recv); } } else { if (MRB_METHOD_NOARG_P(m) && (ci->n > 0 || ci->nk > 0)) { check_method_noarg(mrb, ci); } recv = MRB_METHOD_FUNC(m)(mrb, recv); } /* cfunc epilogue */ mrb_gc_arena_shrink(mrb, ai); if (mrb->exc) goto L_RAISE; ci = mrb->c->ci; if (!ci->u.keep_context) { /* return from context modifying method (resume/yield) */ if (ci->cci == CINFO_RESUMED) { mrb->jmp = prev_jmp; return recv; } else { mrb_assert(!MRB_PROC_CFUNC_P(ci[-1].proc)); irep = ci[-1].proc->body.irep; } } mrb_assert(ci > mrb->c->cibase); ci->stack[0] = recv; /* pop stackpos */ ci = cipop(mrb); JUMP; } CASE(OP_CALL, Z) { mrb_value recv = ci->stack[0]; const struct RProc *p = mrb_proc_ptr(recv); /* handle alias */ if (MRB_PROC_ALIAS_P(p)) { ci->mid = p->body.mid; p = p->upper; } else if (MRB_PROC_ENV_P(p)) { ci->mid = MRB_PROC_ENV(p)->mid; } /* replace callinfo */ ci->u.target_class = MRB_PROC_TARGET_CLASS(p); CI_PROC_SET(ci, p); /* prepare stack */ if (MRB_PROC_CFUNC_P(p)) { recv = MRB_PROC_CFUNC(p)(mrb, recv); mrb_gc_arena_shrink(mrb, ai); if (mrb->exc) goto L_RAISE; /* pop stackpos */ ci = cipop(mrb); ci[1].stack[0] = recv; irep = ci->proc->body.irep; } else { /* setup environment for calling method */ irep = p->body.irep; if (!irep) { ci->stack[0] = mrb_nil_value(); a = 0; goto L_OP_RETURN_BODY; } mrb_int nargs = ci_bidx(ci)+1; if (nargs < irep->nregs) { stack_extend(mrb, irep->nregs); stack_clear(regs+nargs, irep->nregs-nargs); } if (MRB_PROC_ENV_P(p)) { regs[0] = MRB_PROC_ENV(p)->stack[0]; } ci->pc = irep->iseq; } JUMP; } CASE(OP_SUPER, BB) { mrb_value recv; struct RClass* target_class = CI_TARGET_CLASS(ci); mid = ci->mid; if (mid == 0 || !target_class) { RAISE_LIT(mrb, E_NOMETHOD_ERROR, "super called outside of method"); } if ((target_class->flags & MRB_FL_CLASS_IS_PREPENDED) || target_class->tt == MRB_TT_MODULE) { goto super_typeerror; } recv = regs[0]; if (!mrb_obj_is_kind_of(mrb, recv, target_class)) { super_typeerror: RAISE_LIT(mrb, E_TYPE_ERROR, "self has wrong type to call super in this context"); } c = b; // arg info regs[a] = recv; goto L_SENDB_SYM; } CASE(OP_ARGARY, BS) { mrb_int m1 = (b>>11)&0x3f; mrb_int r = (b>>10)&0x1; mrb_int m2 = (b>>5)&0x1f; mrb_int kd = (b>>4)&0x1; mrb_int lv = (b>>0)&0xf; mrb_value *stack; if (ci->mid == 0 || CI_TARGET_CLASS(ci) == NULL) { L_NOSUPER: RAISE_LIT(mrb, E_NOMETHOD_ERROR, "super called outside of method"); } if (lv == 0) stack = regs + 1; else { struct REnv *e = uvenv(mrb, lv-1); if (!e) goto L_NOSUPER; if (MRB_ENV_LEN(e) <= m1+r+m2+1) goto L_NOSUPER; stack = e->stack + 1; } if (r == 0) { regs[a] = mrb_ary_new_from_values(mrb, m1+m2, stack); } else { mrb_value *pp = NULL; struct RArray *rest; mrb_int len = 0; if (mrb_array_p(stack[m1])) { struct RArray *ary = mrb_ary_ptr(stack[m1]); pp = ARY_PTR(ary); len = ARY_LEN(ary); } regs[a] = mrb_ary_new_capa(mrb, m1+len+m2); rest = mrb_ary_ptr(regs[a]); if (m1 > 0) { stack_copy(ARY_PTR(rest), stack, m1); } if (len > 0) { stack_copy(ARY_PTR(rest)+m1, pp, len); } if (m2 > 0) { stack_copy(ARY_PTR(rest)+m1+len, stack+m1+1, m2); } ARY_SET_LEN(rest, m1+len+m2); } if (kd) { regs[a+1] = stack[m1+r+m2]; regs[a+2] = stack[m1+r+m2+1]; } else { regs[a+1] = stack[m1+r+m2]; } mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_ENTER, W) { mrb_int argc = ci->n; mrb_value *argv = regs+1; mrb_int m1 = MRB_ASPEC_REQ(a); /* no other args */ if ((a & ~0x7c0001) == 0 && argc < 15 && MRB_PROC_STRICT_P(ci->proc)) { if (argc+(ci->nk==15) != m1) { /* count kdict too */ argnum_error(mrb, m1); goto L_RAISE; } /* clear local (but non-argument) variables */ mrb_int pos = m1+2; /* self+m1+blk */ if (irep->nlocals-pos > 0) { stack_clear(®s[pos], irep->nlocals-pos); } NEXT; } mrb_int o = MRB_ASPEC_OPT(a); mrb_int r = MRB_ASPEC_REST(a); mrb_int m2 = MRB_ASPEC_POST(a); mrb_int kd = (MRB_ASPEC_KEY(a) > 0 || MRB_ASPEC_KDICT(a))? 1 : 0; /* unused int b = MRB_ASPEC_BLOCK(a); */ mrb_int const len = m1 + o + r + m2; mrb_value * const argv0 = argv; mrb_value blk = regs[ci_bidx(ci)]; mrb_value kdict = mrb_nil_value(); /* keyword arguments */ if (ci->nk == 15) { kdict = regs[mrb_ci_kidx(ci)]; } if (!kd) { if (!mrb_nil_p(kdict) && mrb_hash_size(mrb, kdict) > 0) { if (argc < 14) { ci->n++; argc++; /* include kdict in normal arguments */ } else if (argc == 14) { /* pack arguments and kdict */ regs[1] = ary_new_from_regs(mrb, argc+1, 1); argc = ci->n = 15; } else {/* argc == 15 */ /* push kdict to packed arguments */ mrb_ary_push(mrb, regs[1], kdict); } } kdict = mrb_nil_value(); ci->nk = 0; } else if (MRB_ASPEC_KEY(a) > 0 && !mrb_nil_p(kdict)) { kdict = mrb_hash_dup(mrb, kdict); } else if (!mrb_nil_p(kdict)) { mrb_gc_protect(mrb, kdict); } /* arguments is passed with Array */ if (argc == 15) { struct RArray *ary = mrb_ary_ptr(regs[1]); argv = ARY_PTR(ary); argc = (int)ARY_LEN(ary); mrb_gc_protect(mrb, regs[1]); } /* strict argument check */ if (ci->proc && MRB_PROC_STRICT_P(ci->proc)) { if (argc < m1 + m2 || (r == 0 && argc > len)) { argnum_error(mrb, m1+m2); goto L_RAISE; } } /* extract first argument array to arguments */ else if (len > 1 && argc == 1 && mrb_array_p(argv[0])) { mrb_gc_protect(mrb, argv[0]); argc = (int)RARRAY_LEN(argv[0]); argv = RARRAY_PTR(argv[0]); } /* rest arguments */ mrb_value rest; if (argc < len) { mrb_int mlen = m2; if (argc < m1+m2) { mlen = m1 < argc ? argc - m1 : 0; } /* copy mandatory and optional arguments */ if (argv0 != argv && argv) { value_move(®s[1], argv, argc-mlen); /* m1 + o */ } if (argc < m1) { stack_clear(®s[argc+1], m1-argc); } /* copy post mandatory arguments */ if (mlen) { value_move(®s[len-m2+1], &argv[argc-mlen], mlen); } if (mlen < m2) { stack_clear(®s[len-m2+mlen+1], m2-mlen); } /* initialize rest arguments with empty Array */ if (r) { rest = mrb_ary_new_capa(mrb, 0); regs[m1+o+1] = rest; } /* skip initializer of passed arguments */ if (o > 0 && argc > m1+m2) ci->pc += (argc - m1 - m2)*3; } else { mrb_int rnum = 0; if (argv0 != argv) { mrb_gc_protect(mrb, blk); value_move(®s[1], argv, m1+o); } if (r) { rnum = argc-m1-o-m2; rest = mrb_ary_new_from_values(mrb, rnum, argv+m1+o); regs[m1+o+1] = rest; } if (m2 > 0 && argc-m2 > m1) { value_move(®s[m1+o+r+1], &argv[m1+o+rnum], m2); } ci->pc += o*3; } /* need to be update blk first to protect blk from GC */ mrb_int const kw_pos = len + kd; /* where kwhash should be */ mrb_int const blk_pos = kw_pos + 1; /* where block should be */ regs[blk_pos] = blk; /* move block */ if (kd) { if (mrb_nil_p(kdict)) { kdict = mrb_hash_new_capa(mrb, 0); } regs[kw_pos] = kdict; /* set kwhash */ ci->nk = 15; } /* format arguments for generated code */ ci->n = (uint8_t)len; /* clear local (but non-argument) variables */ if (irep->nlocals-blk_pos-1 > 0) { stack_clear(®s[blk_pos+1], irep->nlocals-blk_pos-1); } JUMP; } CASE(OP_KARG, BB) { mrb_value k = mrb_symbol_value(irep->syms[b]); mrb_int kidx = mrb_ci_kidx(ci); mrb_value kdict, v; if (kidx < 0 || !mrb_hash_p(kdict=regs[kidx]) || !mrb_hash_key_p(mrb, kdict, k)) { RAISE_FORMAT(mrb, E_ARGUMENT_ERROR, "missing keyword: %v", k); } v = mrb_hash_get(mrb, kdict, k); ci = mrb->c->ci; regs[a] = v; mrb_hash_delete_key(mrb, kdict, k); ci = mrb->c->ci; NEXT; } CASE(OP_KEY_P, BB) { mrb_value k = mrb_symbol_value(irep->syms[b]); mrb_int kidx = mrb_ci_kidx(ci); mrb_value kdict; mrb_bool key_p = FALSE; if (kidx >= 0 && mrb_hash_p(kdict=regs[kidx])) { key_p = mrb_hash_key_p(mrb, kdict, k); ci = mrb->c->ci; } regs[a] = mrb_bool_value(key_p); NEXT; } CASE(OP_KEYEND, Z) { mrb_int kidx = mrb_ci_kidx(ci); mrb_value kdict; if (kidx >= 0 && mrb_hash_p(kdict=regs[kidx]) && !mrb_hash_empty_p(mrb, kdict)) { mrb_value key1 = mrb_hash_first_key(mrb, kdict); RAISE_FORMAT(mrb, E_ARGUMENT_ERROR, "unknown keyword: %v", key1); } NEXT; } CASE(OP_BREAK, B) { if (MRB_PROC_STRICT_P(ci->proc)) goto NORMAL_RETURN; if (!MRB_PROC_ORPHAN_P(ci->proc) && MRB_PROC_ENV_P(ci->proc) && ci->proc->e.env->cxt == mrb->c) { const struct RProc *dst = ci->proc->upper; for (ptrdiff_t i = ci - mrb->c->cibase; i > 0; i--, ci--) { if (ci[-1].proc == dst) { goto L_UNWINDING; } } } RAISE_LIT(mrb, E_LOCALJUMP_ERROR, "break from proc-closure"); /* not reached */ } CASE(OP_RETURN_BLK, B) { if (!MRB_PROC_ENV_P(ci->proc) || MRB_PROC_STRICT_P(ci->proc)) { goto NORMAL_RETURN; } const struct REnv *env = ci->u.env; const struct RProc *dst = top_proc(mrb, ci->proc, &env); if (!MRB_PROC_ENV_P(dst) || dst->e.env->cxt == mrb->c) { /* check jump destination */ for (ptrdiff_t i = ci - mrb->c->cibase; i >= 0; i--, ci--) { if (ci->u.env == env) { goto L_UNWINDING; } } } /* no jump destination */ RAISE_LIT(mrb, E_LOCALJUMP_ERROR, "unexpected return"); /* not reached */ } CASE(OP_RETURN, B) { mrb_int acc; mrb_value v; mrb_callinfo *return_ci; NORMAL_RETURN: v = regs[a]; mrb_gc_protect(mrb, v); return_ci = ci; CHECKPOINT_RESTORE(RBREAK_TAG_BREAK) { if (TRUE) { struct RBreak *brk = (struct RBreak*)mrb->exc; return_ci = &mrb->c->cibase[brk->ci_break_index]; v = mrb_break_value_get(brk); } else { L_UNWINDING: return_ci = ci; ci = mrb->c->ci; v = ci->stack[a]; } mrb_gc_protect(mrb, v); } CHECKPOINT_MAIN(RBREAK_TAG_BREAK) { for (;;) { UNWIND_ENSURE(mrb, ci, ci->pc, RBREAK_TAG_BREAK, return_ci, v); if (ci == return_ci) { break; } ci = cipop(mrb); if (ci[1].cci != CINFO_NONE) { mrb_assert(prev_jmp != NULL); mrb->exc = (struct RObject*)break_new(mrb, RBREAK_TAG_BREAK, return_ci, v); mrb_gc_arena_restore(mrb, ai); mrb->c->vmexec = FALSE; mrb->jmp = prev_jmp; MRB_THROW(prev_jmp); } } } CHECKPOINT_END(RBREAK_TAG_BREAK); mrb->exc = NULL; /* clear break object */ if (ci == mrb->c->cibase) { struct mrb_context *c = mrb->c; if (c == mrb->root_c) { /* toplevel return */ mrb_gc_arena_restore(mrb, ai); mrb->jmp = prev_jmp; return v; } fiber_terminate(mrb, c, ci); if (c->vmexec || (mrb->c == mrb->root_c && mrb->c->ci == mrb->c->cibase) /* case using Fiber#transfer in mrb_fiber_resume() */) { mrb_gc_arena_restore(mrb, ai); c->vmexec = FALSE; mrb->jmp = prev_jmp; return v; } ci = mrb->c->ci; } if (mrb->c->vmexec && !ci->u.keep_context) { mrb_gc_arena_restore(mrb, ai); mrb->c->vmexec = FALSE; mrb->jmp = prev_jmp; return v; } acc = ci->cci; ci = cipop(mrb); if (acc == CINFO_SKIP || acc == CINFO_DIRECT) { mrb_gc_arena_restore(mrb, ai); mrb->jmp = prev_jmp; return v; } DEBUG(fprintf(stderr, "from :%s\n", mrb_sym_name(mrb, ci->mid))); irep = ci->proc->body.irep; ci[1].stack[0] = v; mrb_gc_arena_restore(mrb, ai); JUMP; } CASE(OP_BLKPUSH, BS) { int m1 = (b>>11)&0x3f; int r = (b>>10)&0x1; int m2 = (b>>5)&0x1f; int kd = (b>>4)&0x1; int lv = (b>>0)&0xf; int offset = m1+r+m2+kd; mrb_value *stack; if (lv == 0) stack = regs + 1; else { struct REnv *e = uvenv(mrb, lv-1); if (!e || (!MRB_ENV_ONSTACK_P(e) && e->mid == 0) || MRB_ENV_LEN(e) <= offset+1) { RAISE_LIT(mrb, E_LOCALJUMP_ERROR, "unexpected yield"); } stack = e->stack + 1; } if (mrb_nil_p(stack[offset])) { RAISE_LIT(mrb, E_LOCALJUMP_ERROR, "unexpected yield"); } regs[a] = stack[offset]; NEXT; } #if !defined(MRB_USE_BIGINT) || defined(MRB_INT32) L_INT_OVERFLOW: RAISE_LIT(mrb, E_RANGE_ERROR, "integer overflow"); #endif #define TYPES2(a,b) ((((uint16_t)(a))<<8)|(((uint16_t)(b))&0xff)) #define OP_MATH(op_name) \ /* need to check if op is overridden */ \ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) { \ OP_MATH_CASE_INTEGER(op_name); \ OP_MATH_CASE_FLOAT(op_name, integer, float); \ OP_MATH_CASE_FLOAT(op_name, float, integer); \ OP_MATH_CASE_FLOAT(op_name, float, float); \ OP_MATH_CASE_STRING_##op_name(); \ default: \ mid = MRB_OPSYM(op_name); \ goto L_SEND_SYM; \ } \ NEXT; #define OP_MATH_CASE_INTEGER(op_name) \ case TYPES2(MRB_TT_INTEGER, MRB_TT_INTEGER): \ { \ mrb_int x = mrb_integer(regs[a]), y = mrb_integer(regs[a+1]), z; \ if (mrb_int_##op_name##_overflow(x, y, &z)) { \ OP_MATH_OVERFLOW_INT(op_name,x,y); \ } \ else \ SET_INT_VALUE(mrb,regs[a], z); \ } \ break #ifdef MRB_NO_FLOAT #define OP_MATH_CASE_FLOAT(op_name, t1, t2) (void)0 #else #define OP_MATH_CASE_FLOAT(op_name, t1, t2) \ case TYPES2(OP_MATH_TT_##t1, OP_MATH_TT_##t2): \ { \ mrb_float z = mrb_##t1(regs[a]) OP_MATH_OP_##op_name mrb_##t2(regs[a+1]); \ SET_FLOAT_VALUE(mrb, regs[a], z); \ } \ break #endif #ifdef MRB_USE_BIGINT #define OP_MATH_OVERFLOW_INT(op,x,y) regs[a] = mrb_bint_##op##_ii(mrb,x,y) #else #define OP_MATH_OVERFLOW_INT(op,x,y) goto L_INT_OVERFLOW #endif #define OP_MATH_CASE_STRING_add() \ case TYPES2(MRB_TT_STRING, MRB_TT_STRING): \ regs[a] = mrb_str_plus(mrb, regs[a], regs[a+1]); \ mrb_gc_arena_restore(mrb, ai); \ break #define OP_MATH_CASE_STRING_sub() (void)0 #define OP_MATH_CASE_STRING_mul() (void)0 #define OP_MATH_OP_add + #define OP_MATH_OP_sub - #define OP_MATH_OP_mul * #define OP_MATH_TT_integer MRB_TT_INTEGER #define OP_MATH_TT_float MRB_TT_FLOAT CASE(OP_ADD, B) { OP_MATH(add); } CASE(OP_SUB, B) { OP_MATH(sub); } CASE(OP_MUL, B) { OP_MATH(mul); } CASE(OP_DIV, B) { #ifndef MRB_NO_FLOAT mrb_float x, y, f; #endif /* need to check if op is overridden */ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) { case TYPES2(MRB_TT_INTEGER,MRB_TT_INTEGER): { mrb_int x = mrb_integer(regs[a]); mrb_int y = mrb_integer(regs[a+1]); regs[a] = mrb_div_int_value(mrb, x, y); } NEXT; #ifndef MRB_NO_FLOAT case TYPES2(MRB_TT_INTEGER,MRB_TT_FLOAT): x = (mrb_float)mrb_integer(regs[a]); y = mrb_float(regs[a+1]); break; case TYPES2(MRB_TT_FLOAT,MRB_TT_INTEGER): x = mrb_float(regs[a]); y = (mrb_float)mrb_integer(regs[a+1]); break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT): x = mrb_float(regs[a]); y = mrb_float(regs[a+1]); break; #endif default: mid = MRB_OPSYM(div); goto L_SEND_SYM; } #ifndef MRB_NO_FLOAT f = mrb_div_float(x, y); SET_FLOAT_VALUE(mrb, regs[a], f); #endif NEXT; } #define OP_MATHI(op_name) \ /* need to check if op is overridden */ \ switch (mrb_type(regs[a])) { \ OP_MATHI_CASE_INTEGER(op_name); \ OP_MATHI_CASE_FLOAT(op_name); \ default: \ SET_INT_VALUE(mrb,regs[a+1], b); \ mid = MRB_OPSYM(op_name); \ goto L_SEND_SYM; \ } \ NEXT; #define OP_MATHI_CASE_INTEGER(op_name) \ case MRB_TT_INTEGER: \ { \ mrb_int x = mrb_integer(regs[a]), y = (mrb_int)b, z; \ if (mrb_int_##op_name##_overflow(x, y, &z)) { \ OP_MATH_OVERFLOW_INT(op_name,x,y); \ } \ else \ SET_INT_VALUE(mrb,regs[a], z); \ } \ break #ifdef MRB_NO_FLOAT #define OP_MATHI_CASE_FLOAT(op_name) (void)0 #else #define OP_MATHI_CASE_FLOAT(op_name) \ case MRB_TT_FLOAT: \ { \ mrb_float z = mrb_float(regs[a]) OP_MATH_OP_##op_name b; \ SET_FLOAT_VALUE(mrb, regs[a], z); \ } \ break #endif CASE(OP_ADDI, BB) { OP_MATHI(add); } CASE(OP_SUBI, BB) { OP_MATHI(sub); } #define OP_CMP_BODY(op,v1,v2) (v1(regs[a]) op v2(regs[a+1])) #ifdef MRB_NO_FLOAT #define OP_CMP(op,sym) do {\ int result;\ /* need to check if - is overridden */\ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {\ case TYPES2(MRB_TT_INTEGER,MRB_TT_INTEGER):\ result = OP_CMP_BODY(op,mrb_fixnum,mrb_fixnum);\ break;\ default:\ mid = MRB_OPSYM(sym);\ goto L_SEND_SYM;\ }\ if (result) {\ SET_TRUE_VALUE(regs[a]);\ }\ else {\ SET_FALSE_VALUE(regs[a]);\ }\ } while(0) #else #define OP_CMP(op, sym) do {\ int result;\ /* need to check if - is overridden */\ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {\ case TYPES2(MRB_TT_INTEGER,MRB_TT_INTEGER):\ result = OP_CMP_BODY(op,mrb_integer,mrb_integer);\ break;\ case TYPES2(MRB_TT_INTEGER,MRB_TT_FLOAT):\ result = OP_CMP_BODY(op,mrb_integer,mrb_float);\ break;\ case TYPES2(MRB_TT_FLOAT,MRB_TT_INTEGER):\ result = OP_CMP_BODY(op,mrb_float,mrb_integer);\ break;\ case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):\ result = OP_CMP_BODY(op,mrb_float,mrb_float);\ break;\ default:\ mid = MRB_OPSYM(sym);\ goto L_SEND_SYM;\ }\ if (result) {\ SET_TRUE_VALUE(regs[a]);\ }\ else {\ SET_FALSE_VALUE(regs[a]);\ }\ } while(0) #endif CASE(OP_EQ, B) { if (mrb_obj_eq(mrb, regs[a], regs[a+1])) { SET_TRUE_VALUE(regs[a]); } else if (mrb_symbol_p(regs[a])) { SET_FALSE_VALUE(regs[a]); } else { OP_CMP(==,eq); } NEXT; } CASE(OP_LT, B) { OP_CMP(<,lt); NEXT; } CASE(OP_LE, B) { OP_CMP(<=,le); NEXT; } CASE(OP_GT, B) { OP_CMP(>,gt); NEXT; } CASE(OP_GE, B) { OP_CMP(>=,ge); NEXT; } CASE(OP_ARRAY, BB) { regs[a] = ary_new_from_regs(mrb, b, a); mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_ARRAY2, BBB) { regs[a] = ary_new_from_regs(mrb, c, b); mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_ARYCAT, B) { mrb_value splat = mrb_ary_splat(mrb, regs[a+1]); ci = mrb->c->ci; if (mrb_nil_p(regs[a])) { regs[a] = splat; } else { mrb_assert(mrb_array_p(regs[a])); mrb_ary_concat(mrb, regs[a], splat); } mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_ARYPUSH, BB) { mrb_assert(mrb_array_p(regs[a])); for (mrb_int i=0; ic->ci; regs[a] = ary; mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_AREF, BBB) { mrb_value v = regs[b]; if (!mrb_array_p(v)) { if (c == 0) { regs[a] = v; } else { SET_NIL_VALUE(regs[a]); } } else { v = mrb_ary_ref(mrb, v, c); regs[a] = v; } NEXT; } CASE(OP_ASET, BBB) { mrb_assert(mrb_array_p(regs[a])); mrb_ary_set(mrb, regs[b], c, regs[a]); NEXT; } CASE(OP_APOST, BBB) { mrb_value v = regs[a]; int pre = b; int post = c; struct RArray *ary; int len, idx; if (!mrb_array_p(v)) { v = ary_new_from_regs(mrb, 1, a); } ary = mrb_ary_ptr(v); len = (int)ARY_LEN(ary); if (len > pre + post) { v = mrb_ary_new_from_values(mrb, len - pre - post, ARY_PTR(ary)+pre); regs[a++] = v; while (post--) { regs[a++] = ARY_PTR(ary)[len-post-1]; } } else { v = mrb_ary_new_capa(mrb, 0); regs[a++] = v; for (idx=0; idx+prepool[b].tt&IREP_TT_NFLAG)==0); len = irep->pool[b].tt >> 2; if (irep->pool[b].tt & IREP_TT_SFLAG) { sym = mrb_intern_static(mrb, irep->pool[b].u.str, len); } else { sym = mrb_intern(mrb, irep->pool[b].u.str, len); } regs[a] = mrb_symbol_value(sym); NEXT; } CASE(OP_STRING, BB) { mrb_int len; mrb_assert((irep->pool[b].tt&IREP_TT_NFLAG)==0); len = irep->pool[b].tt >> 2; if (irep->pool[b].tt & IREP_TT_SFLAG) { regs[a] = mrb_str_new_static(mrb, irep->pool[b].u.str, len); } else { regs[a] = mrb_str_new(mrb, irep->pool[b].u.str, len); } mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_STRCAT, B) { mrb_assert(mrb_string_p(regs[a])); mrb_str_concat(mrb, regs[a], regs[a+1]); ci = mrb->c->ci; NEXT; } CASE(OP_HASH, BB) { mrb_value hash = mrb_hash_new_capa(mrb, b); int lim = a+b*2; for (int i=a; ic->ci; } regs[a] = hash; mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_HASHADD, BB) { mrb_value hash; int lim = a+b*2+1; hash = regs[a]; mrb_ensure_hash_type(mrb, hash); for (int i=a+1; ic->ci; } mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_HASHCAT, B) { mrb_value hash = regs[a]; mrb_assert(mrb_hash_p(hash)); mrb_hash_merge(mrb, hash, regs[a+1]); ci = mrb->c->ci; mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_LAMBDA, BB) c = OP_L_LAMBDA; L_MAKE_LAMBDA: { struct RProc *p; const mrb_irep *nirep = irep->reps[b]; if (c & OP_L_CAPTURE) { p = mrb_closure_new(mrb, nirep); } else { p = mrb_proc_new(mrb, nirep); p->flags |= MRB_PROC_SCOPE; } if (c & OP_L_STRICT) p->flags |= MRB_PROC_STRICT; regs[a] = mrb_obj_value(p); mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_BLOCK, BB) { c = OP_L_BLOCK; goto L_MAKE_LAMBDA; } CASE(OP_METHOD, BB) { c = OP_L_METHOD; goto L_MAKE_LAMBDA; } CASE(OP_RANGE_INC, B) { mrb_value v = mrb_range_new(mrb, regs[a], regs[a+1], FALSE); ci = mrb->c->ci; regs[a] = v; mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_RANGE_EXC, B) { mrb_value v = mrb_range_new(mrb, regs[a], regs[a+1], TRUE); ci = mrb->c->ci; regs[a] = v; mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_OCLASS, B) { regs[a] = mrb_obj_value(mrb->object_class); NEXT; } CASE(OP_CLASS, BB) { struct RClass *c = 0, *baseclass; mrb_sym id = irep->syms[b]; mrb_value base = regs[a]; mrb_value super = regs[a+1]; if (mrb_nil_p(base)) { baseclass = MRB_PROC_TARGET_CLASS(ci->proc); if (!baseclass) baseclass = mrb->object_class; base = mrb_obj_value(baseclass); } c = mrb_vm_define_class(mrb, base, super, id); ci = mrb->c->ci; regs[a] = mrb_obj_value(c); mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_MODULE, BB) { struct RClass *cls = 0, *baseclass; mrb_sym id = irep->syms[b]; mrb_value base = regs[a]; if (mrb_nil_p(base)) { baseclass = MRB_PROC_TARGET_CLASS(ci->proc); if (!baseclass) baseclass = mrb->object_class; base = mrb_obj_value(baseclass); } cls = mrb_vm_define_module(mrb, base, id); ci = mrb->c->ci; regs[a] = mrb_obj_value(cls); mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_EXEC, BB) { mrb_value recv = regs[a]; struct RClass *c = mrb_class_ptr(recv); const mrb_irep *nirep = irep->reps[b]; /* prepare closure */ struct RProc *p = mrb_proc_new(mrb, nirep); p->c = NULL; mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)ci->proc); MRB_PROC_SET_TARGET_CLASS(p, c); p->flags |= MRB_PROC_SCOPE; /* prepare call stack */ ci = cipush(mrb, a, 0, c, p, NULL, 0, 0); irep = p->body.irep; stack_extend(mrb, irep->nregs); stack_clear(regs+1, irep->nregs-1); ci->pc = irep->iseq; JUMP; } CASE(OP_DEF, BB) { struct RClass *target = mrb_class_ptr(regs[a]); const struct RProc *p = mrb_proc_ptr(regs[a+1]); mrb_method_t m; mrb_sym mid = irep->syms[b]; MRB_METHOD_FROM_PROC(m, p); MRB_METHOD_SET_VISIBILITY(m, MRB_METHOD_VDEFAULT_FL); mrb_define_method_raw(mrb, target, mid, m); mrb_method_added(mrb, target, mid); ci = mrb->c->ci; mrb_gc_arena_restore(mrb, ai); regs[a] = mrb_symbol_value(mid); NEXT; } CASE(OP_SCLASS, B) { regs[a] = mrb_singleton_class(mrb, regs[a]); mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_TCLASS, B) { struct RClass *target = check_target_class(mrb); if (!target) goto L_RAISE; regs[a] = mrb_obj_value(target); NEXT; } CASE(OP_ALIAS, BB) { struct RClass *target = check_target_class(mrb); if (!target) goto L_RAISE; mrb_alias_method(mrb, target, irep->syms[a], irep->syms[b]); mrb_method_added(mrb, target, irep->syms[a]); ci = mrb->c->ci; NEXT; } CASE(OP_UNDEF, B) { struct RClass *target = check_target_class(mrb); if (!target) goto L_RAISE; mrb_undef_method_id(mrb, target, irep->syms[a]); ci = mrb->c->ci; NEXT; } CASE(OP_DEBUG, Z) { const mrb_code *pc = ci->pc; FETCH_BBB(); ci->pc = pc; #ifdef MRB_USE_DEBUG_HOOK mrb->debug_op_hook(mrb, irep, ci->pc, regs); #else #ifndef MRB_NO_STDIO printf("OP_DEBUG %d %d %d\n", a, b, c); #else abort(); #endif #endif NEXT; } CASE(OP_ERR, B) { size_t len = irep->pool[a].tt >> 2; mrb_value exc; mrb_assert((irep->pool[a].tt&IREP_TT_NFLAG)==0); exc = mrb_exc_new(mrb, E_LOCALJUMP_ERROR, irep->pool[a].u.str, len); RAISE_EXC(mrb, exc); } CASE(OP_EXT1, Z) { const mrb_code *pc = ci->pc; insn = READ_B(); switch (insn) { #define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _1(); ci->pc = pc; goto L_OP_ ## insn ## _BODY; #include #undef OPCODE } NEXT; } CASE(OP_EXT2, Z) { const mrb_code *pc = ci->pc; insn = READ_B(); switch (insn) { #define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _2(); ci->pc = pc; goto L_OP_ ## insn ## _BODY; #include #undef OPCODE } NEXT; } CASE(OP_EXT3, Z) { const mrb_code *pc = ci->pc; insn = READ_B(); switch (insn) { #define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _3(); ci->pc = pc; goto L_OP_ ## insn ## _BODY; #include #undef OPCODE } NEXT; } CASE(OP_STOP, Z) { /* stop VM */ mrb_value v; v = mrb->exc ? mrb_obj_value(mrb->exc) : mrb_nil_value(); CHECKPOINT_RESTORE(RBREAK_TAG_STOP) { struct RBreak *brk = (struct RBreak*)mrb->exc; v = mrb_break_value_get(brk); } CHECKPOINT_MAIN(RBREAK_TAG_STOP) { UNWIND_ENSURE(mrb, ci, ci->pc, RBREAK_TAG_STOP, ci, v); } CHECKPOINT_END(RBREAK_TAG_STOP); mrb->jmp = prev_jmp; if (!mrb_nil_p(v)) { mrb->exc = mrb_obj_ptr(v); return v; } mrb->exc = NULL; return regs[irep->nlocals]; } } END_DISPATCH; #undef regs } MRB_CATCH(&c_jmp) { mrb_assert(mrb->exc != NULL); ci = mrb->c->ci; while (ci > mrb->c->cibase && ci->cci == CINFO_DIRECT) { ci = cipop(mrb); } goto RETRY_TRY_BLOCK; } MRB_END_EXC(&c_jmp); } static mrb_value mrb_run(mrb_state *mrb, const struct RProc *proc, mrb_value self) { return mrb_vm_run(mrb, proc, self, ci_bidx(mrb->c->ci) + 1); } MRB_API mrb_value mrb_top_run(mrb_state *mrb, const struct RProc *proc, mrb_value self, mrb_int stack_keep) { if (mrb->c->cibase && mrb->c->ci > mrb->c->cibase) { cipush(mrb, 0, CINFO_SKIP, mrb->object_class, NULL, NULL, 0, 0); } return mrb_vm_run(mrb, proc, self, stack_keep); } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/readfloat.c0000644000000000000000000000013115077107276020566 xustar0030 mtime=1761382078.136420492 30 atime=1761382080.158411242 29 ctime=1761382108.56430193 nghttp2-1.68.0/third-party/mruby/src/readfloat.c0000644000175100017510000000337015077107276021162 0ustar00runnerrunner#include #ifndef MRB_NO_FLOAT #include #include MRB_API mrb_bool mrb_read_float(const char *str, char **endp, double *fp) { const char *p = str; const char *a = p; double res = 0.0, frac = 0.0, div = 1.0; int sign = 1; int digits = 0; // Skip whitespace while (ISSPACE((unsigned char)*p)) p++; // Handle sign if (*p == '-') { sign = -1; p++; } else if (*p == '+') p++; // Parse integer part while (ISDIGIT(*p)) { res = res * 10.0 + (*p - '0'); digits++; a = ++p; } // Parse fractional part if (*p == '.') { p++; while (ISDIGIT(*p)) { frac = frac * 10.0 + (*p++ - '0'); div *= 10.0; digits++; } a = p; } // If no digits were found, return 0 if (digits == 0) { if (endp) *endp = (char*)str; *fp = 0.0; return FALSE; } // Combine integer and fractional parts res += frac / div; res *= sign; // Handle exponent if ((*p | 32) == 'e') { int e = 0; sign = 1; p++; if (*p == '-') { sign = -1; p++; } else if (*p == '+') p++; // If no digits follow 'e', ignore the exponent part if (!ISDIGIT(*p)) goto done; while (ISDIGIT(*p)) { if (e < 10000) // 10000 is big enough to get Infinity e = e * 10 + (*p - '0'); p++; } res *= pow(10.0, sign * e); a = p; } // Set endp done: if (endp) *endp = (char*)a; *fp = res; // strtod(3) stores ERANGE to errno for overflow/underflow // mruby does not require those checks #if 0 // Check for underflow after applying the exponent if (res != 0.0 && fabs(res) < DBL_MIN) { return FALSE; } // Check if the result is infinity or NaN if (isinf(res) || isnan(res)) { return FALSE; } #endif return TRUE; } #endif nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/version.c0000644000000000000000000000013215077107276020313 xustar0030 mtime=1761382078.137420488 30 atime=1761382080.160411233 30 ctime=1761382108.579301887 nghttp2-1.68.0/third-party/mruby/src/version.c0000644000175100017510000000150415077107276020703 0ustar00runnerrunner#include #include void mrb_init_version(mrb_state* mrb) { mrb_value mruby_version = mrb_str_new_lit(mrb, MRUBY_VERSION); mrb_define_global_const(mrb, "RUBY_VERSION", mrb_str_new_lit(mrb, MRUBY_RUBY_VERSION)); mrb_define_global_const(mrb, "RUBY_ENGINE", mrb_str_new_lit(mrb, MRUBY_RUBY_ENGINE)); mrb_define_global_const(mrb, "RUBY_ENGINE_VERSION", mruby_version); mrb_define_global_const(mrb, "MRUBY_VERSION", mruby_version); mrb_define_global_const(mrb, "MRUBY_RELEASE_NO", mrb_fixnum_value(MRUBY_RELEASE_NO)); mrb_define_global_const(mrb, "MRUBY_RELEASE_DATE", mrb_str_new_lit(mrb, MRUBY_RELEASE_DATE)); mrb_define_global_const(mrb, "MRUBY_DESCRIPTION", mrb_str_new_lit(mrb, MRUBY_DESCRIPTION)); mrb_define_global_const(mrb, "MRUBY_COPYRIGHT", mrb_str_new_lit(mrb, MRUBY_COPYRIGHT)); } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/class.c0000644000000000000000000000013215077107276017733 xustar0030 mtime=1761382078.133420506 30 atime=1761382080.151411274 30 ctime=1761382108.594301844 nghttp2-1.68.0/third-party/mruby/src/class.c0000644000175100017510000026054215077107276020334 0ustar00runnerrunner/* ** class.c - Class class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define METHOD_MID(m) MT_KEY_SYM((m).flags) union mt_ptr { const struct RProc *proc; mrb_func_t func; }; #define MT_KEY_SHIFT 4 #define MT_KEY_MASK ((1<>MT_KEY_SHIFT) != 0) #define MT_FUNC MRB_METHOD_FUNC_FL #define MT_NOARG MRB_METHOD_NOARG_FL #define MT_PUBLIC MRB_METHOD_PUBLIC_FL #define MT_PRIVATE MRB_METHOD_PRIVATE_FL #define MT_PROTECTED MRB_METHOD_PROTECTED_FL #define MT_VDEFAULT MRB_METHOD_VDEFAULT_FL #define MT_VMASK MRB_METHOD_VISIBILITY_MASK #define MT_EMPTY 0 #define MT_DELETED 1 #define MT_KEY(sym, flags) ((sym)<>MT_KEY_SHIFT) #define MT_KEY_FLG(k) ((k)&MT_KEY_MASK) /* method table structure */ typedef struct mt_tbl { int size; int alloc; union mt_ptr *ptr; } mt_tbl; #ifdef MRB_USE_INLINE_METHOD_CACHE #define MT_INLINE_CACHE_SIZE 256 static uint8_t mt_cache[MT_INLINE_CACHE_SIZE]; #endif /* Creates the method table. */ static mt_tbl* mt_new(mrb_state *mrb) { mt_tbl *t; t = (mt_tbl*)mrb_malloc(mrb, sizeof(mt_tbl)); t->size = 0; t->alloc = 0; t->ptr = NULL; return t; } static void mt_put(mrb_state *mrb, mt_tbl *t, mrb_sym sym, mrb_sym flags, union mt_ptr ptr); static void mt_rehash(mrb_state *mrb, mt_tbl *t) { int old_alloc = t->alloc; int new_alloc = old_alloc > 0 ? old_alloc << 1 : 8; union mt_ptr *old_ptr = t->ptr; t->ptr = (union mt_ptr*)mrb_calloc(mrb, new_alloc, sizeof(union mt_ptr)+sizeof(mrb_sym)); t->alloc = new_alloc; t->size = 0; if (old_alloc == 0) return; mrb_sym *keys = (mrb_sym*)&old_ptr[old_alloc]; union mt_ptr *vals = old_ptr; for (int i = 0; i < old_alloc; i++) { mrb_sym key = keys[i]; if (MT_KEY_P(key)) { mt_put(mrb, t, MT_KEY_SYM(key), MT_KEY_FLG(key), vals[i]); } } mrb_free(mrb, old_ptr); } #define slot_empty_p(slot) ((slot)->key == 0 && (slot)->func_p == 0) /* Set the value for the symbol in the method table. */ static void mt_put(mrb_state *mrb, mt_tbl *t, mrb_sym sym, mrb_sym flags, union mt_ptr ptr) { int pos, start, dpos = -1; if (t->alloc == 0) { mt_rehash(mrb, t); } mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc]; union mt_ptr *vals = t->ptr; int hash = mrb_int_hash_func(mrb, sym); start = pos = hash & (t->alloc-1); for (;;) { mrb_sym key = keys[pos]; if (MT_KEY_SYM(key) == sym) { value_set: keys[pos] = MT_KEY(sym, flags); vals[pos] = ptr; return; } else if (key == MT_EMPTY) { t->size++; goto value_set; } else if (key == MT_DELETED && dpos < 0) { dpos = pos; } pos = (pos+1) & (t->alloc-1); if (pos == start) { /* not found */ if (dpos > 0) { t->size++; pos = dpos; goto value_set; } /* no room */ mt_rehash(mrb, t); start = pos = hash & (t->alloc-1); keys = (mrb_sym*)&t->ptr[t->alloc]; vals = t->ptr; } } } /* Get a value for a symbol from the method table. */ static mrb_sym mt_get(mrb_state *mrb, mt_tbl *t, mrb_sym sym, union mt_ptr *pp) { int pos; if (t == NULL) return 0; if (t->alloc == 0) return 0; if (t->size == 0) return 0; mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc]; union mt_ptr *vals = t->ptr; int hash = mrb_int_hash_func(mrb, sym); #ifdef MRB_USE_INLINE_METHOD_CACHE int cpos = (hash^(uintptr_t)t) % MT_INLINE_CACHE_SIZE; pos = mt_cache[cpos]; if (pos < t->alloc) { mrb_sym key = keys[pos]; if (key) { if (MT_KEY_SYM(key) == sym) { *pp = vals[pos]; return key; } } } #endif int start = pos = hash & (t->alloc-1); for (;;) { mrb_sym key = keys[pos]; if (MT_KEY_SYM(key) == sym) { *pp = vals[pos]; #ifdef MRB_USE_INLINE_METHOD_CACHE if (pos < 0x100) { mt_cache[cpos] = pos; } #endif return key; } else if (key == MT_EMPTY) { return 0; } pos = (pos+1) & (t->alloc-1); if (pos == start) { /* not found */ return 0; } } } /* Deletes the value for the symbol from the method table. */ static mrb_bool mt_del(mrb_state *mrb, mt_tbl *t, mrb_sym sym) { int pos, start; if (t == NULL) return FALSE; if (t->alloc == 0) return FALSE; if (t->size == 0) return FALSE; mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc]; int hash = mrb_int_hash_func(mrb, sym); start = pos = hash & (t->alloc-1); for (;;) { mrb_sym key = keys[pos]; if (MT_KEY_SYM(key) == sym) { t->size--; keys[pos] = MT_DELETED; return TRUE; } else if (key == MT_EMPTY) { return FALSE; } pos = (pos+1) & (t->alloc-1); if (pos == start) { /* not found */ return FALSE; } } } /* Copy the method table. */ static struct mt_tbl* mt_copy(mrb_state *mrb, mt_tbl *t) { mt_tbl *t2; if (t == NULL) return NULL; if (t->alloc == 0) return NULL; if (t->size == 0) return NULL; t2 = mt_new(mrb); mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc]; union mt_ptr *vals = t->ptr; for (int i=0; ialloc; i++) { if (MT_KEY_P(keys[i])) { mt_put(mrb, t2, MT_KEY_SYM(keys[i]), MT_KEY_FLG(keys[i]), vals[i]); } } return t2; } /* Free memory of the method table. */ static void mt_free(mrb_state *mrb, mt_tbl *t) { mrb_free(mrb, t->ptr); mrb_free(mrb, t); } static inline mrb_method_t create_method_value(mrb_state *mrb, mrb_sym key, union mt_ptr val) { mrb_method_t m = { key, { val.proc } }; return m; } MRB_API void mrb_mt_foreach(mrb_state *mrb, struct RClass *c, mrb_mt_foreach_func *fn, void *p) { mt_tbl *t = c->mt; if (t == NULL) return; if (t->alloc == 0) return; if (t->size == 0) return; mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc]; union mt_ptr *vals = t->ptr; for (int i=0; ialloc; i++) { mrb_sym key = keys[i]; if (MT_KEY_SYM(key)) { if (fn(mrb, MT_KEY_SYM(key), create_method_value(mrb, key, vals[i]), p) != 0) return; } } return; } size_t mrb_gc_mark_mt(mrb_state *mrb, struct RClass *c) { mt_tbl *t = c->mt; if (t == NULL) return 0; if (t->alloc == 0) return 0; if (t->size == 0) return 0; mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc]; union mt_ptr *vals = t->ptr; for (int i=0; ialloc; i++) { if (MT_KEY_P(keys[i]) && (keys[i] & MT_FUNC) == 0) { /* Proc pointer */ const struct RProc *p = vals[i].proc; mrb_gc_mark(mrb, (struct RBasic*)p); } } if (!t) return 0; return (size_t)t->size; } size_t mrb_class_mt_memsize(mrb_state *mrb, struct RClass *c) { struct mt_tbl *h = c->mt; if (!h) return 0; return sizeof(struct mt_tbl) + (size_t)h->size * sizeof(mrb_method_t); } void mrb_gc_free_mt(mrb_state *mrb, struct RClass *c) { if (c->mt) mt_free(mrb, c->mt); } void mrb_class_name_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb_sym id) { mrb_value name; mrb_sym nsym = MRB_SYM(__classname__); if (mrb_obj_iv_defined(mrb, (struct RObject*)c, nsym)) return; if (outer == NULL || outer == mrb->object_class) { name = mrb_symbol_value(id); } else { name = mrb_class_path(mrb, outer); if (mrb_nil_p(name)) { /* unnamed outer class */ if (outer != mrb->object_class && outer != c) { mrb_obj_iv_set_force(mrb, (struct RObject*)c, MRB_SYM(__outer__), mrb_obj_value(outer)); } return; } else { mrb_int len; const char *n = mrb_sym_name_len(mrb, id, &len); mrb_str_cat_lit(mrb, name, "::"); mrb_str_cat(mrb, name, n, len); } } mrb_obj_iv_set_force(mrb, (struct RObject*)c, nsym, name); } mrb_bool mrb_const_name_p(mrb_state *mrb, const char *name, mrb_int len) { return len > 0 && ISUPPER(name[0]) && mrb_ident_p(name+1, len-1); } static void setup_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb_sym id) { mrb_const_set(mrb, mrb_obj_value(outer), id, mrb_obj_value(c)); } #define make_metaclass(mrb, c) prepare_singleton_class((mrb), (struct RBasic*)(c)) static void prepare_singleton_class(mrb_state *mrb, struct RBasic *o) { struct RClass *c; mrb_assert(o->c); if (o->c->tt == MRB_TT_SCLASS) return; struct RClass *sc = MRB_OBJ_ALLOC(mrb, MRB_TT_SCLASS, mrb->class_class); sc->flags |= MRB_FL_CLASS_IS_INHERITED; sc->mt = NULL; sc->iv = NULL; if (o->tt == MRB_TT_CLASS) { c = (struct RClass*)o; if (!c->super) { sc->super = mrb->class_class; } else { sc->super = c->super->c; } } else if (o->tt == MRB_TT_SCLASS) { c = (struct RClass*)o; while (c->super->tt == MRB_TT_ICLASS) c = c->super; make_metaclass(mrb, c->super); sc->super = c->super->c; } else { sc->super = o->c; prepare_singleton_class(mrb, (struct RBasic*)sc); } o->c = sc; mrb_field_write_barrier(mrb, (struct RBasic*)o, (struct RBasic*)sc); mrb_obj_iv_set(mrb, (struct RObject*)sc, MRB_SYM(__attached__), mrb_obj_value(o)); sc->frozen = o->frozen; } static mrb_value class_name_str(mrb_state *mrb, struct RClass* c) { mrb_value path = mrb_class_path(mrb, c); if (mrb_nil_p(path)) { path = c->tt == MRB_TT_MODULE ? mrb_str_new_lit(mrb, "#"); } return path; } static struct RClass* class_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id) { mrb_value c = mrb_const_get(mrb, mrb_obj_value(klass), id); mrb_check_type(mrb, c, MRB_TT_CLASS); return mrb_class_ptr(c); } static struct RClass* module_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id) { mrb_value c = mrb_const_get(mrb, mrb_obj_value(klass), id); mrb_check_type(mrb, c, MRB_TT_MODULE); return mrb_class_ptr(c); } static mrb_bool class_ptr_p(mrb_value obj) { switch (mrb_type(obj)) { case MRB_TT_CLASS: case MRB_TT_SCLASS: case MRB_TT_MODULE: return TRUE; default: return FALSE; } } static void check_if_class_or_module(mrb_state *mrb, mrb_value obj) { if (!class_ptr_p(obj)) { mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a class/module", obj); } } static struct RClass* define_module(mrb_state *mrb, mrb_sym name, struct RClass *outer) { if (mrb_const_defined_at(mrb, mrb_obj_value(outer), name)) { return module_from_sym(mrb, outer, name); } struct RClass *m = mrb_module_new(mrb); setup_class(mrb, outer, m, name); return m; } MRB_API struct RClass* mrb_define_module_id(mrb_state *mrb, mrb_sym name) { return define_module(mrb, name, mrb->object_class); } MRB_API struct RClass* mrb_define_module(mrb_state *mrb, const char *name) { return define_module(mrb, mrb_intern_cstr(mrb, name), mrb->object_class); } struct RClass* mrb_vm_define_module(mrb_state *mrb, mrb_value outer, mrb_sym id) { check_if_class_or_module(mrb, outer); if (mrb_const_defined_at(mrb, outer, id)) { mrb_value old = mrb_const_get(mrb, outer, id); if (!mrb_module_p(old)) { mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a module", old); } return mrb_class_ptr(old); } return define_module(mrb, id, mrb_class_ptr(outer)); } MRB_API struct RClass* mrb_define_module_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name) { struct RClass * c = define_module(mrb, name, outer); setup_class(mrb, outer, c, name); return c; } MRB_API struct RClass* mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name) { mrb_sym id = mrb_intern_cstr(mrb, name); struct RClass * c = define_module(mrb, id, outer); setup_class(mrb, outer, c, id); return c; } static struct RClass* find_origin(struct RClass *c) { MRB_CLASS_ORIGIN(c); return c; } static struct RClass* define_class(mrb_state *mrb, mrb_sym name, struct RClass *super, struct RClass *outer) { struct RClass * c; if (mrb_const_defined_at(mrb, mrb_obj_value(outer), name)) { c = class_from_sym(mrb, outer, name); MRB_CLASS_ORIGIN(c); if (super && mrb_class_real(c->super) != super) { mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for Class %n (%C not %C)", name, c->super, super); } return c; } c = mrb_class_new(mrb, super); setup_class(mrb, outer, c, name); return c; } MRB_API struct RClass* mrb_define_class_id(mrb_state *mrb, mrb_sym name, struct RClass *super) { if (!super) { mrb_warn(mrb, "no super class for '%n', Object assumed", name); } return define_class(mrb, name, super, mrb->object_class); } MRB_API struct RClass* mrb_define_class(mrb_state *mrb, const char *name, struct RClass *super) { return mrb_define_class_id(mrb, mrb_intern_cstr(mrb, name), super); } static mrb_value mrb_do_nothing(mrb_state *mrb, mrb_value); #ifndef MRB_NO_METHOD_CACHE static void mc_clear(mrb_state *mrb); static void mc_clear_by_id(mrb_state *mrb, mrb_sym mid); #else #define mc_clear(mrb) #define mc_clear_by_id(mrb,mid) #endif static void mrb_class_inherited(mrb_state *mrb, struct RClass *super, struct RClass *klass) { if (!super) super = mrb->object_class; super->flags |= MRB_FL_CLASS_IS_INHERITED; mrb_value s = mrb_obj_value(super); mrb_sym mid = MRB_SYM(inherited); if (!mrb_func_basic_p(mrb, s, mid, mrb_do_nothing)) { mrb_value c = mrb_obj_value(klass); mrb_funcall_argv(mrb, s, mid, 1, &c); } } struct RClass* mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id) { struct RClass *s; struct RClass *c; if (!mrb_nil_p(super)) { if (!mrb_class_p(super)) { mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%!v given)", super); } s = mrb_class_ptr(super); } else { s = NULL; } check_if_class_or_module(mrb, outer); if (mrb_const_defined_at(mrb, outer, id)) { mrb_value old = mrb_const_get(mrb, outer, id); if (!mrb_class_p(old)) { mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a class", old); } c = mrb_class_ptr(old); if (s) { /* check super class */ if (mrb_class_real(c->super) != s) { mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for %v", old); } } return c; } c = define_class(mrb, id, s, mrb_class_ptr(outer)); mrb_class_inherited(mrb, mrb_class_real(c->super), c); return c; } MRB_API mrb_bool mrb_class_defined(mrb_state *mrb, const char *name) { mrb_sym sym = mrb_intern_check_cstr(mrb, name); if (!sym) return FALSE; return mrb_const_defined(mrb, mrb_obj_value(mrb->object_class), sym); } MRB_API mrb_bool mrb_class_defined_id(mrb_state *mrb, mrb_sym name) { return mrb_const_defined(mrb, mrb_obj_value(mrb->object_class), name); } MRB_API mrb_bool mrb_class_defined_under(mrb_state *mrb, struct RClass *outer, const char *name) { mrb_sym sym = mrb_intern_check_cstr(mrb, name); if (!sym) return FALSE; return mrb_const_defined_at(mrb, mrb_obj_value(outer), sym); } MRB_API mrb_bool mrb_class_defined_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name) { return mrb_const_defined_at(mrb, mrb_obj_value(outer), name); } MRB_API struct RClass* mrb_class_get_under(mrb_state *mrb, struct RClass *outer, const char *name) { return class_from_sym(mrb, outer, mrb_intern_cstr(mrb, name)); } MRB_API struct RClass* mrb_class_get_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name) { return class_from_sym(mrb, outer, name); } MRB_API struct RClass* mrb_class_get(mrb_state *mrb, const char *name) { return mrb_class_get_under(mrb, mrb->object_class, name); } MRB_API struct RClass* mrb_class_get_id(mrb_state *mrb, mrb_sym name) { return mrb_class_get_under_id(mrb, mrb->object_class, name); } MRB_API struct RClass* mrb_exc_get_id(mrb_state *mrb, mrb_sym name) { mrb_value c = mrb_exc_const_get(mrb, name); if (!mrb_class_p(c)) { mrb_raise(mrb, E_EXCEPTION, "exception corrupted"); } struct RClass *exc = mrb_class_ptr(c); for (struct RClass *e = exc; e; e = e->super) { if (e == E_EXCEPTION) return exc; } mrb_raise(mrb, E_EXCEPTION, "non-exception raised"); /* not reached */ return NULL; } MRB_API struct RClass* mrb_module_get_under(mrb_state *mrb, struct RClass *outer, const char *name) { return module_from_sym(mrb, outer, mrb_intern_cstr(mrb, name)); } MRB_API struct RClass* mrb_module_get_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name) { return module_from_sym(mrb, outer, name); } MRB_API struct RClass* mrb_module_get(mrb_state *mrb, const char *name) { return mrb_module_get_under(mrb, mrb->object_class, name); } MRB_API struct RClass* mrb_module_get_id(mrb_state *mrb, mrb_sym name) { return mrb_module_get_under_id(mrb, mrb->object_class, name); } /*! * Defines a class under the namespace of \a outer. * \param outer a class which contains the new class. * \param name name of the new class * \param super a class from which the new class will derive. * NULL means \c Object class. * \return the created class * \throw TypeError if the constant name \a name is already taken but * the constant is not a \c Class. * \throw NameError if the class is already defined but the class can not * be reopened because its superclass is not \a super. * \post top-level constant named \a name refers the returned class. * * \note if a class named \a name is already defined and its superclass is * \a super, the function just returns the defined class. */ MRB_API struct RClass* mrb_define_class_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name, struct RClass *super) { struct RClass * c; #if 0 if (!super) { mrb_warn(mrb, "no super class for '%C::%n', Object assumed", outer, id); } #endif c = define_class(mrb, name, super, outer); setup_class(mrb, outer, c, name); return c; } MRB_API struct RClass* mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super) { return mrb_define_class_under_id(mrb, outer, mrb_intern_cstr(mrb, name), super); } static mrb_bool check_visibility_break(const struct RProc *p, const struct RClass *c, mrb_callinfo *ci, struct REnv *env) { if (!p || p->upper == NULL || MRB_PROC_SCOPE_P(p) || p->e.env == NULL || !MRB_PROC_ENV_P(p)) { return TRUE; } if (env) { return p->e.env->c != c || MRB_ENV_VISIBILITY_BREAK_P(env); } return mrb_vm_ci_target_class(ci) != c || MRB_CI_VISIBILITY_BREAK_P(ci); } static void find_visibility_scope(mrb_state *mrb, const struct RClass *c, int n, mrb_callinfo **cp, struct REnv **ep) { const struct mrb_context *ec = mrb->c; mrb_callinfo *ci = ec->ci - n; const struct RProc *p = ci->proc; if (c == NULL) c = mrb_vm_ci_target_class(ci); if (check_visibility_break(p, c, ci, NULL)) { mrb_assert(ci->u.env); *ep = NULL; *cp = ci; return; } for (;;) { struct REnv *env = p->e.env; p = p->upper; if (check_visibility_break(p, c, ci, env)) { *ep = env; *cp = NULL; return; } } } MRB_API void mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_method_t m) { union mt_ptr ptr; MRB_CLASS_ORIGIN(c); mt_tbl *h = c->mt; if (c->tt == MRB_TT_SCLASS && mrb_frozen_p(c)) { mrb_value v = mrb_iv_get(mrb, mrb_obj_value(c), MRB_SYM(__attached__)); mrb_check_frozen_value(mrb, v); } else { mrb_check_frozen(mrb, c); } if (!h) h = c->mt = mt_new(mrb); if (MRB_METHOD_PROC_P(m)) { struct RProc *p = (struct RProc*)MRB_METHOD_PROC(m); ptr.proc = p; if (p) { if (p->gc_color != MRB_GC_RED) { p->flags |= MRB_PROC_SCOPE; p->c = NULL; mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)p); if (!MRB_PROC_ENV_P(p)) { MRB_PROC_SET_TARGET_CLASS(p, c); } } else { mrb_assert(mrb_frozen_p(p) && MRB_PROC_SCOPE_P(p)); mrb_assert(p->c == NULL && p->upper == NULL && p->e.target_class == NULL); } } } else { ptr.func = MRB_METHOD_FUNC(m); } int flags = MT_KEY_FLG(m.flags); if (mid == MRB_SYM(initialize)) { MRB_SET_VISIBILITY_FLAGS(flags, MT_PRIVATE); } else if ((flags & MT_VMASK) == MT_VDEFAULT) { mrb_callinfo *ci; struct REnv *e; find_visibility_scope(mrb, c, 0, &ci, &e); mrb_assert(ci || e); MRB_SET_VISIBILITY_FLAGS(flags, (e ? MRB_ENV_VISIBILITY(e) : MRB_CI_VISIBILITY(ci))); } mt_put(mrb, h, mid, flags, ptr); mc_clear_by_id(mrb, mid); } static void define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec, int vis) { mrb_method_t m; int ai = mrb_gc_arena_save(mrb); MRB_METHOD_FROM_FUNC(m, func); if (aspec == MRB_ARGS_NONE()) { MRB_METHOD_NOARG_SET(m); } MRB_METHOD_SET_VISIBILITY(m, vis); mrb_define_method_raw(mrb, c, mid, m); mrb_gc_arena_restore(mrb, ai); } MRB_API void mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec) { define_method_id(mrb, c, mid, func, aspec, MT_PUBLIC); } MRB_API void mrb_define_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec) { mrb_define_method_id(mrb, c, mrb_intern_cstr(mrb, name), func, aspec); } MRB_API void mrb_define_private_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec) { define_method_id(mrb, c, mid, func, aspec, MT_PRIVATE); } MRB_API void mrb_define_private_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec) { mrb_define_private_method_id(mrb, c, mrb_intern_cstr(mrb, name), func, aspec); } /* a function to raise NotImplementedError with current method name */ MRB_API void mrb_notimplement(mrb_state *mrb) { mrb_callinfo *ci = mrb->c->ci; if (ci->mid) { mrb_raisef(mrb, E_NOTIMP_ERROR, "%n() function is unimplemented on this machine", ci->mid); } } /* a function to be replacement of unimplemented method */ MRB_API mrb_value mrb_notimplement_m(mrb_state *mrb, mrb_value self) { mrb_notimplement(mrb); /* not reached */ return mrb_nil_value(); } static void ensure_class_type(mrb_state *mrb, mrb_value val) { if (!class_ptr_p(val)) { mrb_raisef(mrb, E_TYPE_ERROR, "%v is not class/module", val); } } #define to_sym(mrb, ss) mrb_obj_to_sym(mrb, ss) MRB_API mrb_int mrb_get_argc(mrb_state *mrb) { mrb_int argc = mrb->c->ci->n; if (argc == 15) { struct RArray *a = mrb_ary_ptr(mrb->c->ci->stack[1]); a->c = NULL; /* hide from ObjectSpace.each_object */ argc = ARY_LEN(a); } return argc; } MRB_API const mrb_value* mrb_get_argv(mrb_state *mrb) { mrb_int argc = mrb->c->ci->n; mrb_value *array_argv = mrb->c->ci->stack + 1; if (argc == 15) { struct RArray *a = mrb_ary_ptr(*array_argv); a->c = NULL; /* hide from ObjectSpace.each_object */ array_argv = ARY_PTR(a); } return array_argv; } MRB_API mrb_value mrb_get_arg1(mrb_state *mrb) { mrb_callinfo *ci = mrb->c->ci; mrb_int argc = ci->n; mrb_value *array_argv = ci->stack + 1; if (argc == 15) { struct RArray *a = mrb_ary_ptr(*array_argv); argc = ARY_LEN(a); array_argv = ARY_PTR(a); } if (argc == 0 && ci->nk == 15) { mrb_int n = ci->n; if (n == 15) n = 1; return ci->stack[n+1]; /* kwhash next to positional arguments */ } if (argc != 1) { mrb_argnum_error(mrb, argc, 1, 1); } return array_argv[0]; } MRB_API mrb_bool mrb_block_given_p(mrb_state *mrb) { mrb_callinfo *ci = mrb->c->ci; mrb_value b = ci->stack[mrb_ci_bidx(ci)]; return !mrb_nil_p(b); } #define GET_ARG(_type) (ptr ? ((_type)(*ptr++)) : va_arg((*ap), _type)) static mrb_int get_args_v(mrb_state *mrb, mrb_args_format format, void** ptr, va_list *ap) { const char *fmt = format; char c; mrb_int i = 0; mrb_callinfo *ci = mrb->c->ci; mrb_int argc = ci->n; const mrb_value *argv = ci->stack+1; mrb_bool argv_on_stack; mrb_bool opt = FALSE; mrb_bool opt_skip = TRUE; const mrb_value *pickarg = NULL; /* arguments currently being processed */ mrb_value kdict = mrb_nil_value(); mrb_bool reqkarg = FALSE; int argc_min = 0, argc_max = 0; while ((c = *fmt++)) { switch (c) { case '|': opt = TRUE; break; case '*': opt_skip = FALSE; argc_max = -1; if (!reqkarg) reqkarg = strchr(fmt, ':') ? TRUE : FALSE; goto check_exit; case '!': case '+': break; case ':': reqkarg = TRUE; /* fall through */ case '&': case '?': if (opt) opt_skip = FALSE; break; default: if (!opt) argc_min++; argc_max++; break; } } check_exit: if (!reqkarg && ci->nk > 0) { mrb_assert(ci->nk == 15); kdict = ci->stack[mrb_ci_bidx(ci)-1]; if (mrb_hash_p(kdict) && mrb_hash_size(mrb, kdict) > 0) { if (argc < 14) { ci->n++; argc++; /* include kdict in normal arguments */ } else { /* 14+1 == 15 so pack first */ if (argc == 14) { /* pack arguments and kdict */ ci->stack[1] = mrb_ary_new_from_values(mrb, argc+1, &ci->stack[1]); argc = ci->n = 15; } else { /* push kdict to packed arguments */ mrb_ary_push(mrb, ci->stack[1], kdict); } ci->stack[2] = ci->stack[mrb_ci_bidx(ci)]; } ci->nk = 0; } } if (reqkarg && ci->nk > 0) { kdict = ci->stack[mrb_ci_bidx(ci)-1]; mrb_assert(ci->nk == 15); mrb_assert(mrb_hash_p(kdict)); } argv_on_stack = argc < 15; if (!argv_on_stack) { struct RArray *a = mrb_ary_ptr(*argv); argv = ARY_PTR(a); argc = ARY_LEN(a); a->c = NULL; /* hide from ObjectSpace.each_object */ } opt = FALSE; i = 0; while ((c = *format++)) { mrb_bool altmode = FALSE; mrb_bool needmodify = FALSE; for (; *format; format++) { switch (*format) { case '!': if (altmode) goto modifier_exit; /* not accept for multiple '!' */ altmode = TRUE; break; case '+': if (needmodify) goto modifier_exit; /* not accept for multiple '+' */ needmodify = TRUE; break; default: goto modifier_exit; } } modifier_exit: switch (c) { case '|': case '*': case '&': case '?': case ':': if (needmodify) { bad_needmodify: mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong `%c+` modified specifier`", c); } break; default: if (i < argc) { pickarg = &argv[i++]; if (needmodify && !mrb_nil_p(*pickarg)) { mrb_check_frozen_value(mrb, *pickarg); } } else { if (opt) { pickarg = NULL; } else { mrb_argnum_error(mrb, argc, argc_min, argc_max); } } break; } switch (c) { case 'o': case 'C': case 'S': case 'A': case 'H': { mrb_value *p; p = GET_ARG(mrb_value*); if (pickarg) { if (!(altmode && mrb_nil_p(*pickarg))) { switch (c) { case 'C': ensure_class_type(mrb, *pickarg); break; case 'S': mrb_ensure_string_type(mrb, *pickarg); break; case 'A': mrb_ensure_array_type(mrb, *pickarg); break; case 'H': mrb_ensure_hash_type(mrb, *pickarg); break; } } *p = *pickarg; } } break; case 'c': { struct RClass **p; p = GET_ARG(struct RClass**); if (pickarg) { if (altmode && mrb_nil_p(*pickarg)) { *p = NULL; } else { ensure_class_type(mrb, *pickarg); *p = mrb_class_ptr(*pickarg); } } } break; case 's': { const char **ps = NULL; mrb_int *pl = NULL; ps = GET_ARG(const char**); pl = GET_ARG(mrb_int*); if (needmodify) goto bad_needmodify; if (pickarg) { if (altmode && mrb_nil_p(*pickarg)) { *ps = NULL; *pl = 0; } else { mrb_ensure_string_type(mrb, *pickarg); *ps = RSTRING_PTR(*pickarg); *pl = RSTRING_LEN(*pickarg); } } } break; case 'z': { const char **ps; ps = GET_ARG(const char**); if (needmodify) goto bad_needmodify; if (pickarg) { if (altmode && mrb_nil_p(*pickarg)) { *ps = NULL; } else { mrb_ensure_string_type(mrb, *pickarg); *ps = RSTRING_CSTR(mrb, *pickarg); } } } break; case 'a': { struct RArray *a; const mrb_value **pb; mrb_int *pl; pb = GET_ARG(const mrb_value**); pl = GET_ARG(mrb_int*); if (needmodify) goto bad_needmodify; if (pickarg) { if (altmode && mrb_nil_p(*pickarg)) { *pb = NULL; *pl = 0; } else { mrb_ensure_array_type(mrb, *pickarg); a = mrb_ary_ptr(*pickarg); *pb = ARY_PTR(a); *pl = ARY_LEN(a); } } } break; #ifndef MRB_NO_FLOAT case 'f': { mrb_float *p; p = GET_ARG(mrb_float*); if (pickarg) { *p = mrb_as_float(mrb, *pickarg); } } break; #endif case 'i': { mrb_int *p; p = GET_ARG(mrb_int*); if (pickarg) { *p = mrb_as_int(mrb, *pickarg); } } break; case 'b': { mrb_bool *boolp = GET_ARG(mrb_bool*); if (pickarg) { *boolp = mrb_test(*pickarg); } } break; case 'n': { mrb_sym *symp; symp = GET_ARG(mrb_sym*); if (pickarg) { *symp = to_sym(mrb, *pickarg); } } break; case 'd': { void** datap; struct mrb_data_type const* type; datap = GET_ARG(void**); type = GET_ARG(struct mrb_data_type const*); if (pickarg) { if (altmode && mrb_nil_p(*pickarg)) { *datap = NULL; } else { *datap = mrb_data_get_ptr(mrb, *pickarg, type); } } } break; case '&': { mrb_value *p, *bp; p = GET_ARG(mrb_value*); bp = ci->stack + mrb_ci_bidx(ci); if (altmode && mrb_nil_p(*bp)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } *p = *bp; } break; case '|': if (opt_skip && i == argc) goto finish; opt = TRUE; break; case '?': { mrb_bool *p; p = GET_ARG(mrb_bool*); *p = pickarg ? TRUE : FALSE; } break; case '*': { const mrb_value **var; mrb_int *pl; mrb_bool nocopy = (altmode || !argv_on_stack) ? TRUE : FALSE; var = GET_ARG(const mrb_value**); pl = GET_ARG(mrb_int*); if (argc > i) { *pl = argc-i; if (*pl > 0) { if (nocopy) { *var = argv+i; } else { mrb_value args = mrb_ary_new_from_values(mrb, *pl, argv+i); RARRAY(args)->c = NULL; *var = RARRAY_PTR(args); } } i = argc; } else { *pl = 0; *var = NULL; } } break; case ':': { mrb_value ksrc = mrb_hash_p(kdict) ? mrb_hash_dup(mrb, kdict) : mrb_hash_new(mrb); const mrb_kwargs *kwargs = GET_ARG(const mrb_kwargs*); mrb_value *rest; if (kwargs == NULL) { rest = NULL; } else { mrb_int kwnum = kwargs->num; mrb_int required = kwargs->required; const mrb_sym *kname = kwargs->table; mrb_value *values = kwargs->values; mrb_int j; const mrb_int keyword_max = 40; mrb_assert(kwnum >= 0); mrb_assert(required >= 0); if (kwnum > keyword_max || required > kwnum) { mrb_raise(mrb, E_ARGUMENT_ERROR, "keyword number is too large"); } for (j = required; j > 0; j--, kname++, values++) { mrb_value k = mrb_symbol_value(*kname); if (!mrb_hash_key_p(mrb, ksrc, k)) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "missing keyword: %n", *kname); } *values = mrb_hash_delete_key(mrb, ksrc, k); mrb_gc_protect(mrb, *values); } for (j = kwnum - required; j > 0; j--, kname++, values++) { mrb_value k = mrb_symbol_value(*kname); if (mrb_hash_key_p(mrb, ksrc, k)) { *values = mrb_hash_delete_key(mrb, ksrc, k); mrb_gc_protect(mrb, *values); } else { *values = mrb_undef_value(); } } rest = kwargs->rest; } if (rest) { *rest = ksrc; } else if (!mrb_hash_empty_p(mrb, ksrc)) { ksrc = mrb_hash_first_key(mrb, ksrc); mrb_raisef(mrb, E_ARGUMENT_ERROR, "unknown keyword: %v", ksrc); } } break; default: mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid argument specifier %c", c); break; } } if (!c && argc > i) { mrb_argnum_error(mrb, argc, argc_min, argc_max); } finish: return i; } /* retrieve arguments from mrb_state. mrb_get_args(mrb, format, ...) returns number of arguments parsed. format specifiers: string mruby type C type note ---------------------------------------------------------------------------------------------- o: Object [mrb_value] C: Class/Module [mrb_value] when ! follows, the value may be nil S: String [mrb_value] when ! follows, the value may be nil A: Array [mrb_value] when ! follows, the value may be nil H: Hash [mrb_value] when ! follows, the value may be nil s: String [const char*,mrb_int] Receive two arguments; s! gives (NULL,0) for nil z: String [const char*] NUL terminated string; z! gives NULL for nil a: Array [const mrb_value*,mrb_int] Receive two arguments; a! gives (NULL,0) for nil c: Class/Module [struct RClass*] c! gives NULL for nil f: Integer/Float [mrb_float] i: Integer/Float [mrb_int] b: boolean [mrb_bool] n: String/Symbol [mrb_sym] d: data [void*,mrb_data_type const] 2nd argument will be used to check data type so it won't be modified; when ! follows, the value may be nil &: block [mrb_value] &! raises exception if no block given *: rest argument [const mrb_value*,mrb_int] The rest of the arguments as an array; *! avoid copy of the stack |: optional Following arguments are optional ?: optional given [mrb_bool] true if preceding argument (optional) is given ':': keyword args [mrb_kwargs const] Get keyword arguments format modifiers: string note ---------------------------------------------------------------------------------------------- !: Switch to the alternate mode; The behaviour changes depending on the specifier +: Request a not frozen object; However, except nil value */ MRB_API mrb_int mrb_get_args(mrb_state *mrb, mrb_args_format format, ...) { va_list ap; va_start(ap, format); mrb_int rc = get_args_v(mrb, format, NULL, &ap); va_end(ap); return rc; } MRB_API mrb_int mrb_get_args_a(mrb_state *mrb, mrb_args_format format, void **args) { return get_args_v(mrb, format, args, NULL); } static struct RClass* boot_defclass(mrb_state *mrb, struct RClass *super) { struct RClass *c = MRB_OBJ_ALLOC(mrb, MRB_TT_CLASS, mrb->class_class); if (super) { c->super = super; mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)super); c->flags |= MRB_FL_CLASS_IS_INHERITED; } else { c->super = mrb->object_class; } c->mt = mt_new(mrb); return c; } static void boot_initmod(mrb_state *mrb, struct RClass *mod) { if (!mod->mt) { mod->mt = mt_new(mrb); } } static struct RClass* include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super) { struct RClass *ic = MRB_OBJ_ALLOC(mrb, MRB_TT_ICLASS, mrb->class_class); if (m->tt == MRB_TT_ICLASS) { m = m->c; } MRB_CLASS_ORIGIN(m); ic->mt = m->mt; ic->super = super; if (m->tt == MRB_TT_ICLASS) { ic->c = m->c; } else { ic->c = m; } return ic; } static int include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, struct RClass *m, int search_super) { struct RClass *ic; void *klass_mt = find_origin(c)->mt; while (m) { struct RClass *p = c->super; int original_seen = FALSE; int superclass_seen = FALSE; if (c == ins_pos) original_seen = TRUE; if (m->flags & MRB_FL_CLASS_IS_PREPENDED) goto skip; if (klass_mt && klass_mt == m->mt) return -1; while (p) { if (c == p) original_seen = TRUE; if (p->tt == MRB_TT_ICLASS) { if (p->mt == m->mt) { if (!superclass_seen && original_seen) { ins_pos = p; /* move insert point */ } goto skip; } } else if (p->tt == MRB_TT_CLASS) { if (!search_super) break; superclass_seen = TRUE; } p = p->super; } ic = include_class_new(mrb, m, ins_pos->super); m->flags |= MRB_FL_CLASS_IS_INHERITED; ins_pos->super = ic; mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ic); ins_pos = ic; skip: m = m->super; } mc_clear(mrb); return 0; } static int fix_include_module(mrb_state *mrb, struct RBasic *obj, void *data) { struct RClass **m = (struct RClass**)data; if (obj->tt == MRB_TT_ICLASS && obj->c == m[0] && !MRB_FLAG_TEST(obj, MRB_FL_CLASS_IS_ORIGIN)) { struct RClass *ic = (struct RClass*)obj; include_module_at(mrb, ic, ic, m[1], 1); } return MRB_EACH_OBJ_OK; } MRB_API void mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) { mrb_check_frozen(mrb, c); if (include_module_at(mrb, c, find_origin(c), m, 1) < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected"); } if (c->tt == MRB_TT_MODULE && (c->flags & MRB_FL_CLASS_IS_INHERITED)) { struct RClass *data[2]; data[0] = c; data[1] = m; mrb_objspace_each_objects(mrb, fix_include_module, data); } } static int fix_prepend_module(mrb_state *mrb, struct RBasic *obj, void *data) { struct RClass **m = (struct RClass**)data; struct RClass *c = (struct RClass*)obj; if (c->tt == MRB_TT_CLASS || c->tt == MRB_TT_MODULE) { struct RClass *p = c->super; struct RClass *ins_pos = c; while (p) { if (c == m[0]) break; if (p == m[0]->super->c) { ins_pos = c; } if (p->tt == MRB_TT_CLASS) break; if (p->c == m[0]) { include_module_at(mrb, ins_pos, ins_pos, m[1], 0); break; } c = p; p = p->super; } } return MRB_EACH_OBJ_OK; } MRB_API void mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m) { mrb_check_frozen(mrb, c); if (!(c->flags & MRB_FL_CLASS_IS_PREPENDED)) { struct RClass *origin = MRB_OBJ_ALLOC(mrb, MRB_TT_ICLASS, c); origin->flags |= MRB_FL_CLASS_IS_ORIGIN | MRB_FL_CLASS_IS_INHERITED; origin->super = c->super; c->super = origin; origin->mt = c->mt; c->mt = NULL; mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)origin); c->flags |= MRB_FL_CLASS_IS_PREPENDED; } if (include_module_at(mrb, c, c, m, 0) < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic prepend detected"); } if (c->tt == MRB_TT_MODULE && (c->flags & (MRB_FL_CLASS_IS_INHERITED|MRB_FL_CLASS_IS_PREPENDED))) { struct RClass *data[2]; data[0] = c; data[1] = m; mrb_objspace_each_objects(mrb, fix_prepend_module, data); } } static mrb_value mrb_mod_prepend(mrb_state *mrb, mrb_value mod) { struct RClass *c = mrb_class_ptr(mod); mrb_int argc; mrb_value *argv; mrb_sym prepended = MRB_SYM(prepended); mrb_get_args(mrb, "*", &argv, &argc); while (argc--) { mrb_value m = argv[argc]; mrb_check_type(mrb, m, MRB_TT_MODULE); mrb_prepend_module(mrb, c, mrb_class_ptr(m)); if (!mrb_func_basic_p(mrb, m, prepended, mrb_do_nothing)) { mrb_funcall_argv(mrb, m, prepended, 1, &mod); } } return mod; } static mrb_value mrb_mod_include(mrb_state *mrb, mrb_value mod) { struct RClass *c = mrb_class_ptr(mod); mrb_int argc; mrb_value *argv; mrb_sym included = MRB_SYM(included); mrb_get_args(mrb, "*", &argv, &argc); while (argc--) { mrb_value m = argv[argc]; mrb_check_type(mrb, m, MRB_TT_MODULE); mrb_include_module(mrb, c, mrb_class_ptr(m)); if (!mrb_func_basic_p(mrb, m, included, mrb_do_nothing)) { mrb_funcall_argv(mrb, m, included, 1, &mod); } } return mod; } /* 15.3.1.3.13 */ /* * call-seq: * obj.extend(module, ...) -> obj * * Adds to _obj_ the instance methods from each module given as a * parameter. * * module Mod * def hello * "Hello from Mod.\n" * end * end * * class Klass * def hello * "Hello from Klass.\n" * end * end * * k = Klass.new * k.hello #=> "Hello from Klass.\n" * k.extend(Mod) #=> # * k.hello #=> "Hello from Mod.\n" * */ mrb_value mrb_obj_extend(mrb_state *mrb, mrb_value obj) { mrb_int argc; mrb_value *argv; mrb_sym extended = MRB_SYM(extended); mrb_get_args(mrb, "*", &argv, &argc); mrb_value cc = mrb_singleton_class(mrb, obj); while (argc--) { mrb_value mod = argv[argc]; mrb_check_type(mrb, mod, MRB_TT_MODULE); mrb_include_module(mrb, mrb_class_ptr(cc), mrb_class_ptr(mod)); if (!mrb_func_basic_p(mrb, cc, extended, mrb_do_nothing)) { mrb_funcall_argv(mrb, cc, extended, 1, &mod); } } return obj; } /* 15.2.2.4.28 */ /* * call-seq: * mod.include?(module) -> true or false * * Returns true if module is included in * mod or one of mod's ancestors. * * module A * end * class B * include A * end * class C < B * end * B.include?(A) #=> true * C.include?(A) #=> true * A.include?(A) #=> false */ static mrb_value mrb_mod_include_p(mrb_state *mrb, mrb_value mod) { mrb_value mod2; struct RClass *c = mrb_class_ptr(mod); mrb_get_args(mrb, "C", &mod2); mrb_check_type(mrb, mod2, MRB_TT_MODULE); while (c) { if (c->tt == MRB_TT_ICLASS) { if (c->c == mrb_class_ptr(mod2)) return mrb_true_value(); } c = c->super; } return mrb_false_value(); } static mrb_value mrb_mod_ancestors(mrb_state *mrb, mrb_value self) { struct RClass *c = mrb_class_ptr(self); mrb_value result = mrb_ary_new(mrb); while (c) { if (c->tt == MRB_TT_ICLASS) { mrb_ary_push(mrb, result, mrb_obj_value(c->c)); } else if (!(c->flags & MRB_FL_CLASS_IS_PREPENDED)) { mrb_ary_push(mrb, result, mrb_obj_value(c)); } c = c->super; } return result; } static mrb_value mrb_mod_initialize(mrb_state *mrb, mrb_value mod) { mrb_value b; struct RClass *m = mrb_class_ptr(mod); boot_initmod(mrb, m); /* bootstrap a newly initialized module */ mrb_get_args(mrb, "|&", &b); if (!mrb_nil_p(b)) { mrb_yield_with_class(mrb, b, 1, &mod, mod, m); } return mod; } static void mrb_mod_visibility(mrb_state *mrb, mrb_value mod, int vis) { mrb_assert((vis&MT_VMASK)==vis); mrb_int argc; mrb_value *argv; struct RClass *c = mrb_class_ptr(mod); mrb_get_args(mrb, "*!", &argv, &argc); if (argc == 0) { mrb_callinfo *ci; struct REnv *e; find_visibility_scope(mrb, NULL, 1, &ci, &e); if (e) { MRB_ENV_SET_VISIBILITY(e, vis); } else { MRB_CI_SET_VISIBILITY(ci, vis); } } else { mt_tbl *h = c->mt; for (int i=0; iobject_class); mrb_mod_visibility(mrb, self, MT_PUBLIC); return self; } static mrb_value top_private(mrb_state *mrb, mrb_value self) { self = mrb_obj_value(mrb->object_class); mrb_mod_visibility(mrb, self, MT_PRIVATE); return self; } static mrb_value top_protected(mrb_state *mrb, mrb_value self) { self = mrb_obj_value(mrb->object_class); mrb_mod_visibility(mrb, self, MT_PROTECTED); return self; } MRB_API struct RClass* mrb_singleton_class_ptr(mrb_state *mrb, mrb_value v) { struct RBasic *obj; switch (mrb_type(v)) { case MRB_TT_FALSE: if (mrb_nil_p(v)) return mrb->nil_class; return mrb->false_class; case MRB_TT_TRUE: return mrb->true_class; case MRB_TT_CPTR: case MRB_TT_SYMBOL: case MRB_TT_INTEGER: #ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: #endif return NULL; default: break; } obj = mrb_basic_ptr(v); if (obj->c == NULL) return NULL; prepare_singleton_class(mrb, obj); return obj->c; } MRB_API mrb_value mrb_singleton_class(mrb_state *mrb, mrb_value v) { struct RClass *c = mrb_singleton_class_ptr(mrb, v); if (c == NULL) { mrb_raise(mrb, E_TYPE_ERROR, "can't define singleton"); } return mrb_obj_value(c); } MRB_API void mrb_define_singleton_method(mrb_state *mrb, struct RObject *o, const char *name, mrb_func_t func, mrb_aspec aspec) { prepare_singleton_class(mrb, (struct RBasic*)o); mrb_define_method_id(mrb, o->c, mrb_intern_cstr(mrb, name), func, aspec); } MRB_API void mrb_define_singleton_method_id(mrb_state *mrb, struct RObject *o, mrb_sym name, mrb_func_t func, mrb_aspec aspec) { prepare_singleton_class(mrb, (struct RBasic*)o); mrb_define_method_id(mrb, o->c, name, func, aspec); } MRB_API void mrb_define_class_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec) { mrb_define_singleton_method(mrb, (struct RObject*)c, name, func, aspec); } MRB_API void mrb_define_class_method_id(mrb_state *mrb, struct RClass *c, mrb_sym name, mrb_func_t func, mrb_aspec aspec) { mrb_define_singleton_method_id(mrb, (struct RObject*)c, name, func, aspec); } MRB_API void mrb_define_module_function_id(mrb_state *mrb, struct RClass *c, mrb_sym name, mrb_func_t func, mrb_aspec aspec) { mrb_define_class_method_id(mrb, c, name, func, aspec); mrb_define_private_method_id(mrb, c, name, func, aspec); } MRB_API void mrb_define_module_function(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec) { mrb_define_module_function_id(mrb, c, mrb_intern_cstr(mrb, name), func, aspec); } #ifndef MRB_NO_METHOD_CACHE /* clear whole method cache table */ static void mc_clear(mrb_state *mrb) { static const struct mrb_cache_entry ce_zero ={0}; for (int i=0; icache[i] = ce_zero; } } /* clear method cache for a class */ void mrb_mc_clear_by_class(mrb_state *mrb, struct RClass *c) { struct mrb_cache_entry *mc = mrb->cache; for (int i=0; ic == c || mc->c0 == c) mc->c = NULL; } } static void mc_clear_by_id(mrb_state *mrb, mrb_sym id) { struct mrb_cache_entry *mc = mrb->cache; for (int i=0; im) == id) mc->c = NULL; } } #endif // MRB_NO_METHOD_CACHE mrb_method_t mrb_vm_find_method(mrb_state *mrb, struct RClass *c, struct RClass **cp, mrb_sym mid) { mrb_method_t m; #ifndef MRB_NO_METHOD_CACHE struct RClass *oc = c; int h = mrb_int_hash_func(mrb, ((intptr_t)oc) ^ mid) & (MRB_METHOD_CACHE_SIZE-1); struct mrb_cache_entry *mc = &mrb->cache[h]; if (mc->c == c && METHOD_MID(mc->m) == mid) { *cp = mc->c0; return mc->m; } #endif while (c) { mt_tbl *h = c->mt; if (h) { union mt_ptr ptr; mrb_sym ret = mt_get(mrb, h, mid, &ptr); if (ret) { if (ptr.proc == 0) break; *cp = c; m = create_method_value(mrb, ret, ptr); #ifndef MRB_NO_METHOD_CACHE mc->c = oc; mc->c0 = c; mc->m = m; #endif return m; } } c = c->super; } MRB_METHOD_FROM_PROC(m, NULL); return m; /* no method */ } MRB_API mrb_method_t mrb_method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid) { return mrb_vm_find_method(mrb, *cp, cp, mid); } MRB_API mrb_method_t mrb_method_search(mrb_state *mrb, struct RClass *c, mrb_sym mid) { mrb_method_t m; m = mrb_method_search_vm(mrb, &c, mid); if (MRB_METHOD_UNDEF_P(m)) { mrb_name_error(mrb, mid, "undefined method '%n' for class %C", mid, c); } return m; } #define ONSTACK_ALLOC_MAX 32 static mrb_sym prepare_name_common(mrb_state *mrb, mrb_sym sym, const char *prefix, const char *suffix) { char onstack[ONSTACK_ALLOC_MAX]; mrb_int sym_len; const char *sym_str = mrb_sym_name_len(mrb, sym, &sym_len); size_t prefix_len = prefix ? strlen(prefix) : 0; size_t suffix_len = suffix ? strlen(suffix) : 0; size_t name_len = sym_len + prefix_len + suffix_len; char *buf = name_len > sizeof(onstack) ? (char*)mrb_alloca(mrb, name_len) : onstack; char *p = buf; if (prefix_len > 0) { memcpy(p, prefix, prefix_len); p += prefix_len; } memcpy(p, sym_str, sym_len); p += sym_len; if (suffix_len > 0) { memcpy(p, suffix, suffix_len); } return mrb_intern(mrb, buf, name_len); } static mrb_value prepare_ivar_name(mrb_state *mrb, mrb_sym sym) { sym = prepare_name_common(mrb, sym, "@", NULL); mrb_iv_name_sym_check(mrb, sym); return mrb_symbol_value(sym); } static mrb_sym prepare_writer_name(mrb_state *mrb, mrb_sym sym) { return prepare_name_common(mrb, sym, NULL, "="); } static mrb_value mod_attr_define(mrb_state *mrb, mrb_value mod, mrb_value (*accessor)(mrb_state*, mrb_value), mrb_sym (*access_name)(mrb_state*, mrb_sym)) { struct RClass *c = mrb_class_ptr(mod); const mrb_value *argv; mrb_int argc; mrb_get_args(mrb, "*", &argv, &argc); int ai = mrb_gc_arena_save(mrb); for (int i=0; itt == MRB_TT_SCLASS) mrb_raise(mrb, E_TYPE_ERROR, "can't create instance of singleton class"); if (c == mrb->nil_class || c == mrb->false_class) { mrb_assert(ttype == 0); } else if (ttype == 0) { ttype = MRB_TT_OBJECT; } if (MRB_UNDEF_ALLOCATOR_P(c)) { mrb_raisef(mrb, E_TYPE_ERROR, "allocator undefined for %v", cv); } if (ttype <= MRB_TT_CPTR) { mrb_raisef(mrb, E_TYPE_ERROR, "can't create instance of %v", cv); } struct RObject *o = (struct RObject*)mrb_obj_alloc(mrb, ttype, c); return mrb_obj_value(o); } /* * call-seq: * class.new(args, ...) -> obj * * Creates a new object of class's class, then * invokes that object's initialize method, * passing it args. This is the method that ends * up getting called whenever an object is constructed using * `.new`. * */ mrb_value mrb_instance_new(mrb_state *mrb, mrb_value cv) { const mrb_value *argv; mrb_int argc; mrb_value blk; mrb_sym init; mrb_get_args(mrb, "*!&", &argv, &argc, &blk); mrb_value obj = mrb_instance_alloc(mrb, cv); init = MRB_SYM(initialize); if (!mrb_func_basic_p(mrb, obj, init, mrb_do_nothing)) { mrb_funcall_with_block(mrb, obj, init, argc, argv, blk); } return obj; } MRB_API mrb_value mrb_obj_new(mrb_state *mrb, struct RClass *c, mrb_int argc, const mrb_value *argv) { mrb_value obj = mrb_instance_alloc(mrb, mrb_obj_value(c)); mrb_sym mid = MRB_SYM(initialize); if (!mrb_func_basic_p(mrb, obj, mid, mrb_do_nothing)) { mrb_funcall_argv(mrb, obj, mid, argc, argv); } return obj; } static mrb_value mrb_class_initialize(mrb_state *mrb, mrb_value obj) { struct RClass *c = mrb_class_ptr(obj); if (c->iv) { mrb_raise(mrb, E_TYPE_ERROR, "already initialized class"); } mrb_value a, b; mrb_get_args(mrb, "|C&", &a, &b); if (!mrb_nil_p(b)) { mrb_yield_with_class(mrb, b, 1, &obj, obj, c); } return obj; } static mrb_value mrb_class_new_class(mrb_state *mrb, mrb_value cv) { mrb_value super, blk; mrb_int n = mrb_get_args(mrb, "|C&", &super, &blk); if (n == 0) { super = mrb_obj_value(mrb->object_class); } mrb_value new_class = mrb_obj_value(mrb_class_new(mrb, mrb_class_ptr(super))); mrb_sym mid = MRB_SYM(initialize); if (mrb_func_basic_p(mrb, new_class, mid, mrb_class_initialize)) { mrb_class_initialize(mrb, new_class); } else { mrb_funcall_with_block(mrb, new_class, mid, n, &super, blk); } mrb_class_inherited(mrb, mrb_class_ptr(super), mrb_class_ptr(new_class)); return new_class; } static mrb_value mrb_class_superclass(mrb_state *mrb, mrb_value klass) { struct RClass *c = mrb_class_ptr(klass); c = find_origin(c)->super; while (c && c->tt == MRB_TT_ICLASS) { c = find_origin(c)->super; } if (!c) return mrb_nil_value(); return mrb_obj_value(c); } static mrb_value mrb_do_nothing(mrb_state *mrb, mrb_value cv) { return mrb_nil_value(); } static mrb_value mrb_bob_not(mrb_state *mrb, mrb_value cv) { return mrb_bool_value(!mrb_test(cv)); } /* 15.3.1.3.1 */ /* 15.3.1.3.10 */ /* 15.3.1.3.11 */ /* * call-seq: * obj == other -> true or false * obj.equal?(other) -> true or false * obj.eql?(other) -> true or false * * Equality---At the Object level, == returns * true only if obj and other are the * same object. Typically, this method is overridden in descendant * classes to provide class-specific meaning. * * Unlike ==, the equal? method should never be * overridden by subclasses: it is used to determine object identity * (that is, a.equal?(b) iff a is the same * object as b). * * The eql? method returns true if * obj and anObject have the same value. Used by * Hash to test members for equality. For objects of * class Object, eql? is synonymous with * ==. Subclasses normally continue this tradition, but * there are exceptions. Numeric types, for example, * perform type conversion across ==, but not across * eql?, so: * * 1 == 1.0 #=> true * 1.eql? 1.0 #=> false */ mrb_value mrb_obj_equal_m(mrb_state *mrb, mrb_value self) { mrb_value arg = mrb_get_arg1(mrb); return mrb_bool_value(mrb_obj_equal(mrb, self, arg)); } MRB_API mrb_bool mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid) { mrb_method_t m = mrb_method_search_vm(mrb, &c, mid); if (MRB_METHOD_UNDEF_P(m)) { return FALSE; } return TRUE; } MRB_API mrb_bool mrb_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym mid) { return mrb_obj_respond_to(mrb, mrb_class(mrb, obj), mid); } MRB_API mrb_value mrb_class_path(mrb_state *mrb, struct RClass *c) { mrb_sym nsym = MRB_SYM(__classname__); mrb_value path = mrb_obj_iv_get(mrb, (struct RObject*)c, nsym); if (mrb_nil_p(path)) { /* no name (yet) */ return mrb_class_find_path(mrb, c); } else if (mrb_symbol_p(path)) { /* toplevel class/module */ return mrb_sym_str(mrb, mrb_symbol(path)); } return mrb_str_dup(mrb, path); } MRB_API struct RClass* mrb_class_real(struct RClass* cl) { if (cl == 0) return NULL; while ((cl->tt == MRB_TT_SCLASS) || (cl->tt == MRB_TT_ICLASS)) { cl = cl->super; if (cl == 0) return NULL; } return cl; } MRB_API const char* mrb_class_name(mrb_state *mrb, struct RClass* c) { if (c == NULL) return NULL; mrb_value name = class_name_str(mrb, c); return RSTRING_PTR(name); } MRB_API const char* mrb_obj_classname(mrb_state *mrb, mrb_value obj) { return mrb_class_name(mrb, mrb_obj_class(mrb, obj)); } /*! * Ensures a class can be derived from super. * * \param super a reference to an object. * \exception TypeError if \a super is not a Class or \a super is a singleton class. */ static void mrb_check_inheritable(mrb_state *mrb, struct RClass *super) { if (super->tt != MRB_TT_CLASS) { mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%C given)", super); } if (super->tt == MRB_TT_SCLASS) { mrb_raise(mrb, E_TYPE_ERROR, "can't make subclass of singleton class"); } if (super == mrb->class_class) { mrb_raise(mrb, E_TYPE_ERROR, "can't make subclass of Class"); } } /*! * Creates a new class. * \param super a class from which the new class derives. * \exception TypeError \a super is not inheritable. * \exception TypeError \a super is the Class class. */ MRB_API struct RClass* mrb_class_new(mrb_state *mrb, struct RClass *super) { if (super) { mrb_check_inheritable(mrb, super); } struct RClass *c = boot_defclass(mrb, super); if (super) { MRB_SET_INSTANCE_TT(c, MRB_INSTANCE_TT(super)); c->flags |= super->flags & MRB_FL_UNDEF_ALLOCATE; } make_metaclass(mrb, c); return c; } /*! * Creates a new module. */ MRB_API struct RClass* mrb_module_new(mrb_state *mrb) { struct RClass *m = MRB_OBJ_ALLOC(mrb, MRB_TT_MODULE, mrb->module_class); boot_initmod(mrb, m); return m; } /* * call-seq: * obj.class => class * * Returns the class of obj, now preferred over * Object#type, as an object's type in Ruby is only * loosely tied to that object's class. This method must always be * called with an explicit receiver, as class is also a * reserved word in Ruby. * * 1.class #=> Integer * self.class #=> Object */ MRB_API struct RClass* mrb_obj_class(mrb_state *mrb, mrb_value obj) { return mrb_class_real(mrb_class(mrb, obj)); } MRB_API void mrb_alias_method(mrb_state *mrb, struct RClass *c, mrb_sym a, mrb_sym b) { if (a == b) return; mrb_method_t m = mrb_method_search(mrb, c, b); if (!MRB_METHOD_CFUNC_P(m)) { const struct RProc *p = MRB_METHOD_PROC(m); if (!MRB_PROC_CFUNC_P(p) && !MRB_PROC_ALIAS_P(p)) { struct RProc *pnew = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, mrb->proc_class); int vis = MRB_METHOD_VISIBILITY(m); pnew->body.mid = b; pnew->upper = p; pnew->e.env = NULL; pnew->flags |= MRB_PROC_ALIAS; MRB_METHOD_FROM_PROC(m, pnew); MRB_METHOD_SET_VISIBILITY(m, vis); } } mrb_define_method_raw(mrb, c, a, m); } /*! * Defines an alias of a method. * \param mrb the mruby state * \param klass the class which the original method belongs to * \param name1 a new name for the method * \param name2 the original name of the method */ MRB_API void mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const char *name2) { mrb_alias_method(mrb, klass, mrb_intern_cstr(mrb, name1), mrb_intern_cstr(mrb, name2)); } MRB_API void mrb_define_alias_id(mrb_state *mrb, struct RClass *klass, mrb_sym a, mrb_sym b) { mrb_alias_method(mrb, klass, a, b); } /* * call-seq: * mod.to_s -> string * * Return a string representing this module or class. For basic * classes and modules, this is the name. For singletons, we * show information on the thing we're attached to as well. */ mrb_value mrb_mod_to_s(mrb_state *mrb, mrb_value klass) { if (mrb_sclass_p(klass)) { mrb_value v = mrb_iv_get(mrb, klass, MRB_SYM(__attached__)); mrb_value str = mrb_str_new_lit(mrb, "#"); } else { return class_name_str(mrb, mrb_class_ptr(klass)); } } static mrb_value mrb_mod_alias(mrb_state *mrb, mrb_value mod) { struct RClass *c = mrb_class_ptr(mod); mrb_sym new_name, old_name; mrb_get_args(mrb, "nn", &new_name, &old_name); mrb_alias_method(mrb, c, new_name, old_name); mrb_method_added(mrb, c, new_name); return mod; } static void undef_method(mrb_state *mrb, struct RClass *c, mrb_sym a) { mrb_method_t m; mrb_sym undefined; mrb_value recv; MRB_METHOD_FROM_PROC(m, NULL); mrb_define_method_raw(mrb, c, a, m); if (c->tt == MRB_TT_SCLASS) { undefined = MRB_SYM(singleton_method_undefined); recv = mrb_iv_get(mrb, mrb_obj_value(c), MRB_SYM(__attached__)); } else { undefined = MRB_SYM(method_undefined); recv = mrb_obj_value(c); } if (!mrb_func_basic_p(mrb, recv, undefined, mrb_do_nothing)) { mrb_value sym = mrb_symbol_value(a); mrb_funcall_argv(mrb, recv, undefined, 1, &sym); } } MRB_API void mrb_undef_method_id(mrb_state *mrb, struct RClass *c, mrb_sym a) { if (!mrb_obj_respond_to(mrb, c, a)) { mrb_name_error(mrb, a, "undefined method '%n' for class '%C'", a, c); } undef_method(mrb, c, a); } MRB_API void mrb_undef_method(mrb_state *mrb, struct RClass *c, const char *name) { undef_method(mrb, c, mrb_intern_cstr(mrb, name)); } MRB_API void mrb_undef_class_method_id(mrb_state *mrb, struct RClass *c, mrb_sym name) { mrb_undef_method_id(mrb, mrb_class_ptr(mrb_singleton_class(mrb, mrb_obj_value(c))), name); } MRB_API void mrb_undef_class_method(mrb_state *mrb, struct RClass *c, const char *name) { mrb_undef_method(mrb, mrb_class_ptr(mrb_singleton_class(mrb, mrb_obj_value(c))), name); } MRB_API void mrb_remove_method(mrb_state *mrb, struct RClass *c0, mrb_sym mid) { struct RClass *c = c0; MRB_CLASS_ORIGIN(c); mt_tbl *h = c->mt; if (h && mt_del(mrb, h, mid)) { mrb_sym removed; mrb_value recv; mc_clear_by_id(mrb, mid); if (c0->tt == MRB_TT_SCLASS) { removed = MRB_SYM(singleton_method_removed); recv = mrb_iv_get(mrb, mrb_obj_value(c0), MRB_SYM(__attached__)); } else { removed = MRB_SYM(method_removed); recv = mrb_obj_value(c0); } if (!mrb_func_basic_p(mrb, recv, removed, mrb_do_nothing)) { mrb_value sym = mrb_symbol_value(mid); mrb_funcall_argv(mrb, recv, removed, 1, &sym); } return; } mrb_name_error(mrb, mid, "method '%n' not defined in %C", mid, c); } static mrb_value mrb_mod_undef(mrb_state *mrb, mrb_value mod) { struct RClass *c = mrb_class_ptr(mod); mrb_int argc; const mrb_value *argv; mrb_get_args(mrb, "*", &argv, &argc); while (argc--) { mrb_undef_method_id(mrb, c, to_sym(mrb, *argv)); argv++; } return mrb_nil_value(); } static void check_const_name_sym(mrb_state *mrb, mrb_sym id) { mrb_int len; const char *name = mrb_sym_name_len(mrb, id, &len); if (!mrb_const_name_p(mrb, name, len)) { mrb_name_error(mrb, id, "wrong constant name %n", id); } } static mrb_value mrb_mod_const_defined(mrb_state *mrb, mrb_value mod) { mrb_sym id; mrb_bool inherit = TRUE; mrb_get_args(mrb, "n|b", &id, &inherit); check_const_name_sym(mrb, id); if (inherit) { return mrb_bool_value(mrb_const_defined(mrb, mod, id)); } return mrb_bool_value(mrb_const_defined_at(mrb, mod, id)); } static mrb_value mrb_const_get_sym(mrb_state *mrb, mrb_value mod, mrb_sym id) { check_const_name_sym(mrb, id); return mrb_const_get(mrb, mod, id); } static mrb_value mrb_mod_const_get(mrb_state *mrb, mrb_value mod) { mrb_value path = mrb_get_arg1(mrb); if (mrb_symbol_p(path)) { /* const get with symbol */ return mrb_const_get_sym(mrb, mod, mrb_symbol(path)); } /* const get with class path string */ mrb_ensure_string_type(mrb, path); char *ptr = RSTRING_PTR(path); mrb_int len = RSTRING_LEN(path); mrb_int off = 0; while (off < len) { mrb_int end = mrb_str_index_lit(mrb, path, "::", off); if (end == -1) end = len; mrb_sym id = mrb_intern(mrb, ptr+off, end-off); mod = mrb_const_get_sym(mrb, mod, id); if (end == len) off = end; else { off = end + 2; if (off == len) { /* trailing "::" */ mrb_name_error(mrb, id, "wrong constant name '%v'", path); } } } return mod; } static mrb_value mrb_mod_const_set(mrb_state *mrb, mrb_value mod) { mrb_sym id; mrb_value value; mrb_get_args(mrb, "no", &id, &value); check_const_name_sym(mrb, id); mrb_const_set(mrb, mod, id, value); return value; } static mrb_value mrb_mod_remove_const(mrb_state *mrb, mrb_value mod) { mrb_sym id; mrb_get_args(mrb, "n", &id); check_const_name_sym(mrb, id); mrb_value val = mrb_iv_remove(mrb, mod, id); if (mrb_undef_p(val)) { mrb_name_error(mrb, id, "constant %n not defined", id); } return val; } mrb_value mrb_const_missing(mrb_state *mrb, mrb_value mod, mrb_sym sym) { if (mrb_class_real(mrb_class_ptr(mod)) != mrb->object_class) { mrb_name_error(mrb, sym, "uninitialized constant %v::%n", mod, sym); } else { mrb_name_error(mrb, sym, "uninitialized constant %n", sym); } /* not reached */ return mrb_nil_value(); } mrb_value mrb_mod_const_missing(mrb_state *mrb, mrb_value mod) { mrb_sym sym; mrb_get_args(mrb, "n", &sym); mrb->c->ci->mid = 0; return mrb_const_missing(mrb, mod, sym); } /* 15.2.2.4.34 */ /* * call-seq: * mod.method_defined?(symbol) -> true or false * * Returns +true+ if the named method is defined by * _mod_ (or its included modules and, if _mod_ is a class, * its ancestors). Public and protected methods are matched. * * module A * def method1() end * end * class B * def method2() end * end * class C < B * include A * def method3() end * end * * A.method_defined? :method1 #=> true * C.method_defined? "method1" #=> true * C.method_defined? "method2" #=> true * C.method_defined? "method3" #=> true * C.method_defined? "method4" #=> false */ static mrb_value mrb_mod_method_defined(mrb_state *mrb, mrb_value mod) { mrb_sym id; mrb_get_args(mrb, "n", &id); return mrb_bool_value(mrb_obj_respond_to(mrb, mrb_class_ptr(mod), id)); } void mrb_method_added(mrb_state *mrb, struct RClass *c, mrb_sym mid) { mrb_sym added; mrb_value recv = mrb_obj_value(c); if (c->tt == MRB_TT_SCLASS) { added = MRB_SYM(singleton_method_added); recv = mrb_iv_get(mrb, recv, MRB_SYM(__attached__)); } else { added = MRB_SYM(method_added); } if (!mrb_func_basic_p(mrb, recv, added, mrb_do_nothing)) { mrb_value sym = mrb_symbol_value(mid); mrb_funcall_argv(mrb, recv, added, 1, &sym); } } mrb_value define_method_m(mrb_state *mrb, struct RClass *c, int vis) { mrb_sym mid; mrb_value proc = mrb_undef_value(); mrb_value blk; mrb_get_args(mrb, "n|o&", &mid, &proc, &blk); switch (mrb_type(proc)) { case MRB_TT_PROC: blk = proc; break; case MRB_TT_UNDEF: /* ignored */ break; default: mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %T (expected Proc)", proc); break; } if (mrb_nil_p(blk)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } struct RProc *p = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, mrb->proc_class); mrb_proc_copy(mrb, p, mrb_proc_ptr(blk)); p->flags |= MRB_PROC_STRICT; mrb_method_t m; MRB_METHOD_FROM_PROC(m, p); MRB_METHOD_SET_VISIBILITY(m, vis); mrb_define_method_raw(mrb, c, mid, m); mrb_method_added(mrb, c, mid); return mrb_symbol_value(mid); } mrb_value mrb_mod_define_method_m(mrb_state *mrb, struct RClass *c) { return define_method_m(mrb, c, MT_PUBLIC); } static mrb_value mod_define_method(mrb_state *mrb, mrb_value self) { return mrb_mod_define_method_m(mrb, mrb_class_ptr(self)); } static mrb_value top_define_method(mrb_state *mrb, mrb_value self) { return define_method_m(mrb, mrb->object_class, MT_PRIVATE); } static mrb_value mrb_mod_eqq(mrb_state *mrb, mrb_value mod) { mrb_value obj = mrb_get_arg1(mrb); mrb_bool eqq = mrb_obj_is_kind_of(mrb, obj, mrb_class_ptr(mod)); return mrb_bool_value(eqq); } static mrb_value mrb_mod_dup(mrb_state *mrb, mrb_value self) { mrb_value mod = mrb_obj_clone(mrb, self); mrb_obj_ptr(mod)->frozen = 0; return mod; } static mrb_value mrb_mod_module_function(mrb_state *mrb, mrb_value mod) { const mrb_value *argv; mrb_int argc; mrb_check_type(mrb, mod, MRB_TT_MODULE); mrb_get_args(mrb, "*", &argv, &argc); if (argc == 0) { /* set MODFUNC SCOPE if implemented */ return mod; } /* set PRIVATE method visibility if implemented */ /* mrb_mod_dummy_visibility(mrb, mod); */ struct RClass *rclass = mrb_class_ptr(mod); int ai = mrb_gc_arena_save(mrb); for (int i=0; ic, mid, m); mrb_gc_arena_restore(mrb, ai); } return mod; } static struct RClass* mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj) { struct RClass *klass = mrb_basic_ptr(obj)->c; if (klass->tt != MRB_TT_SCLASS) return klass; else { /* copy singleton(unnamed) class */ struct RClass *clone = (struct RClass*)mrb_obj_alloc(mrb, klass->tt, mrb->class_class); switch (mrb_type(obj)) { case MRB_TT_CLASS: case MRB_TT_SCLASS: break; default: clone->c = mrb_singleton_class_clone(mrb, mrb_obj_value(klass)); break; } clone->super = klass->super; if (klass->iv) { mrb_iv_copy(mrb, mrb_obj_value(clone), mrb_obj_value(klass)); mrb_obj_iv_set(mrb, (struct RObject*)clone, MRB_SYM(__attached__), obj); } if (klass->mt) { clone->mt = mt_copy(mrb, klass->mt); } else { clone->mt = mt_new(mrb); } clone->tt = MRB_TT_SCLASS; return clone; } } static void copy_class(mrb_state *mrb, mrb_value dst, mrb_value src) { struct RClass *dc = mrb_class_ptr(dst); struct RClass *sc = mrb_class_ptr(src); /* if the origin is not the same as the class, then the origin and the current class need to be copied */ if (sc->flags & MRB_FL_CLASS_IS_PREPENDED) { struct RClass *c0 = sc->super; struct RClass *c1 = dc; /* copy prepended iclasses */ while (!(c0->flags & MRB_FL_CLASS_IS_ORIGIN)) { c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0))); c1 = c1->super; c0 = c0->super; } c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0))); c1->super->flags |= MRB_FL_CLASS_IS_ORIGIN; } if (sc->mt) { if (sc->tt == MRB_TT_ICLASS && !(sc->flags & MRB_FL_CLASS_IS_ORIGIN)) { dc->mt = sc->mt; } else { dc->mt = mt_copy(mrb, sc->mt); } } dc->super = sc->super; dc->flags = sc->flags; dc->frozen = 0; } /* 15.3.1.3.16 */ mrb_value mrb_obj_init_copy(mrb_state *mrb, mrb_value self); static void init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj) { mrb_assert((mrb_type(dest) == mrb_type(obj))); switch (mrb_unboxed_type(obj)) { case MRB_TT_ICLASS: copy_class(mrb, dest, obj); return; case MRB_TT_CLASS: case MRB_TT_MODULE: copy_class(mrb, dest, obj); mrb_iv_copy(mrb, dest, obj); mrb_iv_remove(mrb, dest, MRB_SYM(__classname__)); break; case MRB_TT_OBJECT: case MRB_TT_SCLASS: case MRB_TT_HASH: case MRB_TT_CDATA: case MRB_TT_EXCEPTION: mrb_iv_copy(mrb, dest, obj); break; case MRB_TT_ISTRUCT: mrb_istruct_copy(dest, obj); break; #if !defined(MRB_NO_FLOAT) && defined(MRB_WORDBOX_NO_FLOAT_TRUNCATE) case MRB_TT_FLOAT: { struct RFloat *f = (struct RFloat*)mrb_obj_ptr(dest); f->f = mrb_float(obj); } break; #endif #ifdef MRB_USE_BIGINT case MRB_TT_BIGINT: mrb_bint_copy(mrb, dest, obj); break; #endif #ifdef MRB_USE_RATIONAL case MRB_TT_RATIONAL: mrb_rational_copy(mrb, dest, obj); break; #endif #ifdef MRB_USE_COMPLEX case MRB_TT_COMPLEX: mrb_complex_copy(mrb, dest, obj); break; #endif default: break; } if (!mrb_func_basic_p(mrb, dest, MRB_SYM(initialize_copy), mrb_obj_init_copy)) { mrb_funcall_argv(mrb, dest, MRB_SYM(initialize_copy), 1, &obj); } } /* 15.3.1.3.8 */ /* * call-seq: * obj.clone -> an_object * * Produces a shallow copy of obj---the instance variables of * obj are copied, but not the objects they reference. Copies * the frozen state of obj. See also the discussion * under Object#dup. * * class Klass * attr_accessor :str * end * s1 = Klass.new #=> # * s1.str = "Hello" #=> "Hello" * s2 = s1.clone #=> # * s2.str[1,4] = "i" #=> "i" * s1.inspect #=> "#" * s2.inspect #=> "#" * * This method may have class-specific behavior. If so, that * behavior will be documented under the #+initialize_copy+ method of * the class. * * Some Class(True False Nil Symbol Integer Float) Object cannot clone. */ MRB_API mrb_value mrb_obj_clone(mrb_state *mrb, mrb_value self) { if (mrb_immediate_p(self)) { return self; } if (mrb_sclass_p(self)) { mrb_raise(mrb, E_TYPE_ERROR, "can't clone singleton class"); } struct RObject *p = (struct RObject*)mrb_obj_alloc(mrb, mrb_unboxed_type(self), mrb_obj_class(mrb, self)); p->c = mrb_singleton_class_clone(mrb, self); mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)p->c); mrb_value clone = mrb_obj_value(p); init_copy(mrb, clone, self); p->frozen = mrb_obj_ptr(self)->frozen; return clone; } /* 15.3.1.3.9 */ /* * call-seq: * obj.dup -> an_object * * Produces a shallow copy of obj---the instance variables of * obj are copied, but not the objects they reference. * dup copies the frozen state of obj. See also * the discussion under Object#clone. In general, * clone and dup may have different semantics * in descendant classes. While clone is used to duplicate * an object, including its internal state, dup typically * uses the class of the descendant object to create the new instance. * * This method may have class-specific behavior. If so, that * behavior will be documented under the #+initialize_copy+ method of * the class. */ MRB_API mrb_value mrb_obj_dup(mrb_state *mrb, mrb_value obj) { if (mrb_immediate_p(obj)) { return obj; } if (mrb_sclass_p(obj)) { mrb_raise(mrb, E_TYPE_ERROR, "can't dup singleton class"); } struct RBasic *p = mrb_obj_alloc(mrb, mrb_type(obj), mrb_obj_class(mrb, obj)); mrb_value dup = mrb_obj_value(p); init_copy(mrb, dup, obj); return dup; } /* implementation of __id__ */ mrb_value mrb_obj_id_m(mrb_state *mrb, mrb_value self); mrb_noreturn void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args) { mrb_no_method_error(mrb, name, args, "undefined method '%n' for %T", name, self); } /* 15.3.1.3.30 */ /* * call-seq: * obj.method_missing(symbol [, *args] ) -> result * * Invoked by Ruby when obj is sent a message it cannot handle. * symbol is the symbol for the method called, and args * are any arguments that were passed to it. By default, the interpreter * raises an error when this method is called. However, it is possible * to override the method to provide more dynamic behavior. * If it is decided that a particular method should not be handled, then * super should be called, so that ancestors can pick up the * missing method. * The example below creates * a class Roman, which responds to methods with names * consisting of roman numerals, returning the corresponding integer * values. * * class Roman * def romanToInt(str) * # ... * end * def method_missing(sym) * str = sym.to_s * romanToInt(str) * end * end * * r = Roman.new * r.iv #=> 4 * r.xxiii #=> 23 * r.mm #=> 2000 */ mrb_value mrb_obj_missing(mrb_state *mrb, mrb_value mod) { mrb_sym name; const mrb_value *a; mrb_int alen; mrb->c->ci->mid = 0; mrb_get_args(mrb, "n*!", &name, &a, &alen); mrb_method_missing(mrb, name, mod, mrb_ary_new_from_values(mrb, alen, a)); /* not reached */ return mrb_nil_value(); } static mrb_value inspect_main(mrb_state *mrb, mrb_value mod) { return mrb_str_new_lit(mrb, "main"); } static const mrb_code new_iseq[] = { OP_ENTER, 0x0, 0x10, 0x3, // OP_ENTER 0:0:1:0:0:1:1 OP_SSEND, 4, 0, 0, // OP_SSEND R4 :allocate n=0 OP_MOVE, 0, 4, // OP_MOVE R0 R4 OP_MOVE, 4, 3, // OP_MOVE R4 R3 (&) OP_MOVE, 3, 2, // OP_MOVE R3 R2 (**) OP_MOVE, 2, 1, // OP_MOVE R2 R1 (*) OP_SSENDB, 1, 1, 255, // OP_SSENDB R1 :initialize n=*|nk=* OP_RETURN, 0 // OP_RETURN R0 }; MRB_PRESYM_DEFINE_VAR_AND_INITER(new_syms, 2, MRB_SYM(allocate), MRB_SYM(initialize)) static const mrb_irep new_irep = { 4, 6, 0, MRB_IREP_STATIC, new_iseq, NULL, new_syms, NULL, NULL, NULL, sizeof(new_iseq), 0, 2, 0, 0, }; mrb_alignas(8) static const struct RProc new_proc = { NULL, NULL, MRB_TT_PROC, MRB_GC_RED, MRB_OBJ_IS_FROZEN, MRB_PROC_SCOPE | MRB_PROC_STRICT, { &new_irep }, NULL, { NULL } }; static void init_class_new(mrb_state *mrb, struct RClass *cls) { mrb_method_t m; MRB_PRESYM_INIT_SYMBOLS(mrb, new_syms); MRB_METHOD_FROM_PROC(m, &new_proc); mrb_define_method_raw(mrb, cls, MRB_SYM(new), m); } static const mrb_code neq_iseq[] = { OP_ENTER, 0x4, 0, 0, // OP_ENTER 1:0:0:0:0:0:0 OP_EQ, 0, // OP_EQ R0 (R1) OP_JMPNOT, 0, 0, 5, // OP_JMPNOT R3 016 OP_LOADF, 0, // OP_LOADF R0 (true) OP_JMP, 0, 2, // OP_JMP R1 018 OP_LOADT, 0, // OP_LOADT R3 (true) OP_RETURN, 0 // OP_RETURN R0 }; static const mrb_irep neq_irep = { 4, 6, 0, MRB_IREP_STATIC, neq_iseq, NULL, NULL, NULL, NULL, NULL, sizeof(neq_iseq), 0, 2, 0, 0, }; mrb_alignas(8) static const struct RProc neq_proc = { NULL, NULL, MRB_TT_PROC, MRB_GC_RED, MRB_OBJ_IS_FROZEN, MRB_PROC_SCOPE | MRB_PROC_STRICT, { &neq_irep }, NULL, { NULL } }; void mrb_init_class(mrb_state *mrb) { struct RClass *bob; /* BasicObject */ struct RClass *obj; /* Object */ struct RClass *mod; /* Module */ struct RClass *cls; /* Class */ /* boot class hierarchy */ bob = boot_defclass(mrb, 0); obj = boot_defclass(mrb, bob); mrb->object_class = obj; mod = boot_defclass(mrb, obj); mrb->module_class = mod;/* obj -> mod */ cls = boot_defclass(mrb, mod); mrb->class_class = cls; /* obj -> cls */ /* fix-up loose ends */ bob->c = obj->c = mod->c = cls->c = cls; make_metaclass(mrb, bob); make_metaclass(mrb, obj); make_metaclass(mrb, mod); make_metaclass(mrb, cls); /* name basic classes */ mrb_define_const_id(mrb, bob, MRB_SYM(BasicObject), mrb_obj_value(bob)); mrb_define_const_id(mrb, obj, MRB_SYM(Object), mrb_obj_value(obj)); mrb_define_const_id(mrb, obj, MRB_SYM(Module), mrb_obj_value(mod)); mrb_define_const_id(mrb, obj, MRB_SYM(Class), mrb_obj_value(cls)); /* name each classes */ mrb_class_name_class(mrb, NULL, bob, MRB_SYM(BasicObject)); mrb_class_name_class(mrb, NULL, obj, MRB_SYM(Object)); /* 15.2.1 */ mrb_class_name_class(mrb, NULL, mod, MRB_SYM(Module)); /* 15.2.2 */ mrb_class_name_class(mrb, NULL, cls, MRB_SYM(Class)); /* 15.2.3 */ MRB_SET_INSTANCE_TT(cls, MRB_TT_CLASS); mrb_define_method_id(mrb, bob, MRB_SYM(initialize), mrb_do_nothing, MRB_ARGS_NONE()); mrb_define_method_id(mrb, bob, MRB_OPSYM(not), mrb_bob_not, MRB_ARGS_NONE()); mrb_define_method_id(mrb, bob, MRB_OPSYM(eq), mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.1 */ mrb_define_method_id(mrb, bob, MRB_SYM(__id__), mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.4 */ mrb_define_method_id(mrb, bob, MRB_SYM(__send__), mrb_f_send, MRB_ARGS_REQ(1)|MRB_ARGS_REST()|MRB_ARGS_BLOCK()); /* 15.3.1.3.5 */ mrb_define_method_id(mrb, bob, MRB_SYM_Q(equal), mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.11 */ mrb_define_method_id(mrb, bob, MRB_SYM(instance_eval), mrb_obj_instance_eval, MRB_ARGS_OPT(1)|MRB_ARGS_BLOCK()); /* 15.3.1.3.18 */ mrb_define_private_method_id(mrb, bob, MRB_SYM(singleton_method_added), mrb_do_nothing, MRB_ARGS_REQ(1)); mrb_define_private_method_id(mrb, bob, MRB_SYM(singleton_method_removed),mrb_do_nothing, MRB_ARGS_REQ(1)); mrb_define_private_method_id(mrb, bob, MRB_SYM(singleton_method_undefined),mrb_do_nothing, MRB_ARGS_REQ(1)); mrb_define_private_method_id(mrb, bob, MRB_SYM(method_missing), mrb_obj_missing, MRB_ARGS_ANY()); /* 15.3.1.3.30 */ mrb_method_t m; MRB_METHOD_FROM_PROC(m, &neq_proc); mrb_define_method_raw(mrb, bob, MRB_OPSYM(neq), m); mrb_define_class_method_id(mrb, cls, MRB_SYM(new), mrb_class_new_class, MRB_ARGS_OPT(1)|MRB_ARGS_BLOCK()); mrb_define_method_id(mrb, cls, MRB_SYM(allocate), mrb_instance_alloc, MRB_ARGS_NONE()); mrb_define_method_id(mrb, cls, MRB_SYM(superclass), mrb_class_superclass, MRB_ARGS_NONE()); /* 15.2.3.3.4 */ mrb_define_method_id(mrb, cls, MRB_SYM(initialize), mrb_class_initialize, MRB_ARGS_OPT(1)); /* 15.2.3.3.1 */ mrb_define_private_method_id(mrb, cls, MRB_SYM(inherited), mrb_do_nothing, MRB_ARGS_REQ(1)); init_class_new(mrb, cls); MRB_SET_INSTANCE_TT(mod, MRB_TT_MODULE); mrb_define_private_method_id(mrb, mod, MRB_SYM(extended), mrb_do_nothing, MRB_ARGS_REQ(1)); /* 15.2.2.4.26 */ mrb_define_private_method_id(mrb, mod, MRB_SYM(prepended), mrb_do_nothing, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, mod, MRB_SYM_Q(include), mrb_mod_include_p, MRB_ARGS_REQ(1)); /* 15.2.2.4.28 */ mrb_define_method_id(mrb, mod, MRB_SYM(include), mrb_mod_include, MRB_ARGS_REQ(1)); /* 15.2.2.4.27 */ mrb_define_method_id(mrb, mod, MRB_SYM(prepend), mrb_mod_prepend, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, mod, MRB_SYM(class_eval), mrb_mod_module_eval, MRB_ARGS_ANY()); /* 15.2.2.4.15 */ mrb_define_private_method_id(mrb, mod, MRB_SYM(included), mrb_do_nothing, MRB_ARGS_REQ(1)); /* 15.2.2.4.29 */ mrb_define_method_id(mrb, mod, MRB_SYM(initialize), mrb_mod_initialize, MRB_ARGS_NONE()); /* 15.2.2.4.31 */ mrb_define_method_id(mrb, mod, MRB_SYM(module_eval), mrb_mod_module_eval, MRB_ARGS_ANY()); /* 15.2.2.4.35 */ mrb_define_private_method_id(mrb, mod, MRB_SYM(module_function), mrb_mod_module_function, MRB_ARGS_ANY()); mrb_define_private_method_id(mrb, mod, MRB_SYM(private), mrb_mod_private, MRB_ARGS_ANY()); /* 15.2.2.4.36 */ mrb_define_private_method_id(mrb, mod, MRB_SYM(protected), mrb_mod_protected, MRB_ARGS_ANY()); /* 15.2.2.4.37 */ mrb_define_private_method_id(mrb, mod, MRB_SYM(public), mrb_mod_public, MRB_ARGS_ANY()); /* 15.2.2.4.38 */ mrb_define_method_id(mrb, mod, MRB_SYM(attr_accessor), mrb_mod_attr_accessor, MRB_ARGS_ANY()); /* 15.2.2.4.12 */ mrb_define_method_id(mrb, mod, MRB_SYM(attr_reader), mrb_mod_attr_reader, MRB_ARGS_ANY()); /* 15.2.2.4.13 */ mrb_define_method_id(mrb, mod, MRB_SYM(attr_writer), mrb_mod_attr_writer, MRB_ARGS_ANY()); /* 15.2.2.4.14 */ mrb_define_alias_id(mrb, mod, MRB_SYM(attr), MRB_SYM(attr_reader)); /* 15.2.2.4.11 */ mrb_define_method_id(mrb, mod, MRB_SYM(to_s), mrb_mod_to_s, MRB_ARGS_NONE()); mrb_define_method_id(mrb, mod, MRB_SYM(inspect), mrb_mod_to_s, MRB_ARGS_NONE()); mrb_define_method_id(mrb, mod, MRB_SYM(alias_method), mrb_mod_alias, MRB_ARGS_ANY()); /* 15.2.2.4.8 */ mrb_define_method_id(mrb, mod, MRB_SYM(ancestors), mrb_mod_ancestors, MRB_ARGS_NONE()); /* 15.2.2.4.9 */ mrb_define_method_id(mrb, mod, MRB_SYM(undef_method), mrb_mod_undef, MRB_ARGS_ANY()); /* 15.2.2.4.41 */ mrb_define_method_id(mrb, mod, MRB_SYM_Q(const_defined), mrb_mod_const_defined, MRB_ARGS_ARG(1,1)); /* 15.2.2.4.20 */ mrb_define_method_id(mrb, mod, MRB_SYM(const_get), mrb_mod_const_get, MRB_ARGS_REQ(1)); /* 15.2.2.4.21 */ mrb_define_method_id(mrb, mod, MRB_SYM(const_set), mrb_mod_const_set, MRB_ARGS_REQ(2)); /* 15.2.2.4.23 */ mrb_define_private_method_id(mrb, mod, MRB_SYM(remove_const), mrb_mod_remove_const, MRB_ARGS_REQ(1)); /* 15.2.2.4.40 */ mrb_define_method_id(mrb, mod, MRB_SYM(const_missing), mrb_mod_const_missing, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, mod, MRB_SYM_Q(method_defined), mrb_mod_method_defined, MRB_ARGS_REQ(1)); /* 15.2.2.4.34 */ mrb_define_method_id(mrb, mod, MRB_SYM(define_method), mod_define_method, MRB_ARGS_ARG(1,1)); mrb_define_method_id(mrb, mod, MRB_OPSYM(eqq), mrb_mod_eqq, MRB_ARGS_REQ(1)); /* 15.2.2.4.7 */ mrb_define_method_id(mrb, mod, MRB_SYM(dup), mrb_mod_dup, MRB_ARGS_NONE()); mrb_define_private_method_id(mrb, mod, MRB_SYM(method_added), mrb_do_nothing, MRB_ARGS_REQ(1)); mrb_define_private_method_id(mrb, mod, MRB_SYM(method_removed), mrb_do_nothing, MRB_ARGS_REQ(1)); mrb_define_private_method_id(mrb, mod, MRB_SYM(method_undefined), mrb_do_nothing, MRB_ARGS_REQ(1)); mrb_define_private_method_id(mrb, mod, MRB_SYM(const_added), mrb_do_nothing, MRB_ARGS_REQ(1)); mrb_undef_method_id(mrb, cls, MRB_SYM(module_function)); mrb->top_self = MRB_OBJ_ALLOC(mrb, MRB_TT_OBJECT, mrb->object_class); mrb_define_singleton_method_id(mrb, mrb->top_self, MRB_SYM(inspect), inspect_main, MRB_ARGS_NONE()); mrb_define_singleton_method_id(mrb, mrb->top_self, MRB_SYM(to_s), inspect_main, MRB_ARGS_NONE()); mrb_define_singleton_method_id(mrb, mrb->top_self, MRB_SYM(define_method), top_define_method, MRB_ARGS_ARG(1,1)); mrb_define_singleton_method_id(mrb, mrb->top_self, MRB_SYM(public), top_public, MRB_ARGS_ANY()); mrb_define_singleton_method_id(mrb, mrb->top_self, MRB_SYM(private), top_private, MRB_ARGS_ANY()); mrb_define_singleton_method_id(mrb, mrb->top_self, MRB_SYM(protected), top_protected, MRB_ARGS_ANY()); } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/mempool.c0000644000000000000000000000013215077107276020276 xustar0030 mtime=1761382078.135420497 30 atime=1761382080.157411247 30 ctime=1761382108.573301905 nghttp2-1.68.0/third-party/mruby/src/mempool.c0000644000175100017510000000715715077107276020700 0ustar00runnerrunner/* ** mempool.c - memory pool ** ** See Copyright Notice in mruby.h */ #include #include #include /* configuration section */ /* allocated memory address should be multiple of POOL_ALIGNMENT */ /* or undef it if alignment does not matter */ #ifndef POOL_ALIGNMENT #if INTPTR_MAX == INT64_MAX #define POOL_ALIGNMENT 8 #else #define POOL_ALIGNMENT 4 #endif #endif /* page size of memory pool */ #ifndef POOL_PAGE_SIZE #define POOL_PAGE_SIZE 16000 #endif /* end of configuration section */ /* Disable MSVC warning "C4200: nonstandard extension used: zero-sized array * in struct/union" when in C++ mode */ #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4200) #endif struct mempool_page { struct mempool_page *next; size_t offset; size_t len; void *last; char page[]; }; #ifdef _MSC_VER #pragma warning(pop) #endif struct mempool { struct mempool_page *pages; }; #ifndef TEST_POOL /* use mruby's memory allocator */ #define malloc(s) mrb_basic_alloc_func(NULL, (s)) #define free(p) mrb_basic_alloc_func((p), 0) #endif #ifdef POOL_ALIGNMENT # define ALIGN_PADDING(x) ((SIZE_MAX - (x) + 1) & (POOL_ALIGNMENT - 1)) #else # define ALIGN_PADDING(x) (0) #endif MRB_API mempool* mempool_open(void) { mempool *pool = (mempool*)malloc(sizeof(struct mempool)); if (pool) { pool->pages = NULL; } return pool; } MRB_API void mempool_close(mempool *pool) { struct mempool_page *page; if (!pool) return; page = pool->pages; while (page) { struct mempool_page *tmp = page; page = page->next; free(tmp); } free(pool); } static struct mempool_page* page_alloc(mempool *pool, size_t len) { if (len < POOL_PAGE_SIZE) len = POOL_PAGE_SIZE; struct mempool_page *page = (struct mempool_page*)malloc(sizeof(struct mempool_page)+len); if (page) { page->offset = 0; page->len = len; } return page; } MRB_API void* mempool_alloc(mempool *pool, size_t len) { struct mempool_page *page; if (!pool) return NULL; len += ALIGN_PADDING(len); for (page = pool->pages; page; page = page->next) { if (page->offset + len <= page->len) { size_t n = page->offset; page->offset += len; page->last = (void*)(page->page+n); return page->last; } } page = page_alloc(pool, len); if (!page) return NULL; page->offset = len; page->next = pool->pages; pool->pages = page; page->last = (void*)page->page; return page->last; } MRB_API void* mempool_realloc(mempool *pool, void *p, size_t oldlen, size_t newlen) { if (!pool) return NULL; if (newlen < oldlen) return p; oldlen += ALIGN_PADDING(oldlen); newlen += ALIGN_PADDING(newlen); for (struct mempool_page *page = pool->pages; page; page = page->next) { if (page->last == p) { /* if p is a last allocation from the page */ size_t beg = (char*)p - page->page; /* check beg + oldlen points bottom */ /* assert(beg + oldlen == page->offset) */ if (beg + oldlen != page->offset) break; if (beg + newlen > page->len) { /* new allocation need more space */ /* abandon this space */ page->offset = beg; break; } page->offset = beg + newlen; return p; } } void *np = mempool_alloc(pool, newlen); if (np == NULL) { return NULL; } memcpy(np, p, oldlen); return np; } #ifdef TEST_POOL int main(void) { int i, len = 250; mempool *pool; void *p; pool = mempool_open(); p = mempool_alloc(pool, len); for (i=1; i<20; i++) { printf("%p (len=%d)\n", p, len); p = mempool_realloc(pool, p, len, len*2); len *= 2; } mempool_close(pool); return 0; } #endif nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/variable.c0000644000000000000000000000013215077107276020413 xustar0030 mtime=1761382078.137420488 30 atime=1761382080.160411233 30 ctime=1761382108.555301956 nghttp2-1.68.0/third-party/mruby/src/variable.c0000644000175100017510000006175615077107276021022 0ustar00runnerrunner/* ** variable.c - mruby variables ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include /* Instance variable table structure */ typedef struct iv_tbl { int size, alloc; mrb_value *ptr; } iv_tbl; #define IV_EMPTY 0 #define IV_DELETED (1UL<<31) #define IV_KEY_P(k) (((k)&~((uint32_t)IV_DELETED))!=0) /* Creates the instance variable table. */ static iv_tbl* iv_new(mrb_state *mrb) { iv_tbl *t; t = (iv_tbl*)mrb_malloc(mrb, sizeof(iv_tbl)); t->size = 0; t->alloc = 0; t->ptr = NULL; return t; } static void iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val); #define IV_INITIAL_SIZE 4 static void iv_rehash(mrb_state *mrb, iv_tbl *t) { int old_alloc = t->alloc; int new_alloc = old_alloc > 0 ? old_alloc << 1 : IV_INITIAL_SIZE; mrb_value *old_ptr = t->ptr; t->ptr = (mrb_value*)mrb_calloc(mrb, sizeof(mrb_value)+sizeof(mrb_sym), new_alloc); t->size = 0; t->alloc = new_alloc; if (old_alloc == 0) return; mrb_sym *keys = (mrb_sym*)&old_ptr[old_alloc]; mrb_value *vals = old_ptr; for (int i = 0; i < old_alloc; i++) { if (IV_KEY_P(keys[i])) { iv_put(mrb, t, keys[i], vals[i]); } } mrb_free(mrb, old_ptr); } /* Set the value for the symbol in the instance variable table. */ static void iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val) { int hash, pos, start, dpos = -1; if (t->alloc == 0) { iv_rehash(mrb, t); } mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc]; mrb_value *vals = t->ptr; hash = mrb_int_hash_func(mrb, sym); start = pos = hash & (t->alloc-1); for (;;) { mrb_sym key = keys[pos]; if (key == sym) { vals[pos] = val; return; } else if (key == IV_EMPTY) { t->size++; keys[pos] = sym; vals[pos] = val; return; } else if (key == IV_DELETED && dpos < 0) { dpos = pos; } pos = (pos+1) & (t->alloc-1); if (pos == start) { /* not found */ if (dpos >= 0) { t->size++; keys[dpos] = sym; vals[dpos] = val; return; } /* no room */ iv_rehash(mrb, t); keys = (mrb_sym*)&t->ptr[t->alloc]; vals = t->ptr; start = pos = hash & (t->alloc-1); } } } /* Get a value for a symbol from the instance variable table. */ static int iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp) { int hash, pos, start; if (t == NULL) return 0; if (t->alloc == 0) return 0; if (t->size == 0) return 0; mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc]; mrb_value *vals = t->ptr; hash = mrb_int_hash_func(mrb, sym); start = pos = hash & (t->alloc-1); for (;;) { mrb_sym key = keys[pos]; if (key == sym) { if (vp) *vp = vals[pos]; return pos+1; } else if (key == IV_EMPTY) { return 0; } pos = (pos+1) & (t->alloc-1); if (pos == start) { /* not found */ return 0; } } } /* Deletes the value for the symbol from the instance variable table. */ static mrb_bool iv_del(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp) { int hash, pos, start; if (t == NULL) return FALSE; if (t->alloc == 0) return FALSE; if (t->size == 0) return FALSE; mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc]; mrb_value *vals = t->ptr; hash = mrb_int_hash_func(mrb, sym); start = pos = hash & (t->alloc-1); for (;;) { mrb_sym key = keys[pos]; if (key == sym) { if (vp) *vp = vals[pos]; t->size--; keys[pos] = IV_DELETED; return TRUE; } else if (key == IV_EMPTY) { return FALSE; } pos = (pos+1) & (t->alloc-1); if (pos == start) { /* not found */ return FALSE; } } } /* Iterates over the instance variable table. */ static void iv_foreach(mrb_state *mrb, iv_tbl *t, mrb_iv_foreach_func *func, void *p) { if (t == NULL) return; if (t->alloc == 0) return; if (t->size == 0) return; mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc]; mrb_value *vals = t->ptr; for (int i=0; ialloc; i++) { if (IV_KEY_P(keys[i])) { if ((*func)(mrb, keys[i], vals[i], p) != 0) { return; } } } return; } /* Get the size of the instance variable table. */ /* Size is approximated by the allocated table size. */ static size_t iv_size(mrb_state *mrb, iv_tbl *t) { if (t == NULL) return 0; return (size_t)t->size; } /* Copy the instance variable table. */ static iv_tbl* iv_copy(mrb_state *mrb, iv_tbl *t) { iv_tbl *t2; if (t == NULL) return NULL; if (t->alloc == 0) return NULL; if (t->size == 0) return NULL; mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc]; mrb_value *vals = t->ptr; t2 = iv_new(mrb); for (int i=0; ialloc; i++) { if (IV_KEY_P(keys[i])) { iv_put(mrb, t2, keys[i], vals[i]); } } return t2; } /* Free memory of the instance variable table. */ static void iv_free(mrb_state *mrb, iv_tbl *t) { mrb_free(mrb, t->ptr); mrb_free(mrb, t); } static int iv_mark_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { mrb_gc_mark_value(mrb, v); return 0; } static void mark_tbl(mrb_state *mrb, iv_tbl *t) { iv_foreach(mrb, t, iv_mark_i, 0); } void mrb_gc_mark_gv(mrb_state *mrb) { mark_tbl(mrb, mrb->globals); } void mrb_gc_free_gv(mrb_state *mrb) { if (mrb->globals) { iv_free(mrb, mrb->globals); mrb->globals = NULL; } } size_t mrb_gc_mark_iv(mrb_state *mrb, struct RObject *obj) { mark_tbl(mrb, obj->iv); return iv_size(mrb, obj->iv); } void mrb_gc_free_iv(mrb_state *mrb, struct RObject *obj) { if (obj->iv) { iv_free(mrb, obj->iv); } } mrb_value mrb_vm_special_get(mrb_state *mrb, mrb_sym i) { return mrb_fixnum_value(0); } void mrb_vm_special_set(mrb_state *mrb, mrb_sym i, mrb_value v) { } static mrb_bool obj_iv_p(mrb_value obj) { switch (mrb_unboxed_type(obj)) { case MRB_TT_OBJECT: case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_SCLASS: case MRB_TT_HASH: case MRB_TT_CDATA: case MRB_TT_EXCEPTION: return TRUE; default: return FALSE; } } static iv_tbl* class_iv_ptr(struct RClass *c) { return c->tt == MRB_TT_ICLASS ? c->c->iv : c->iv; } MRB_API mrb_value mrb_obj_iv_get(mrb_state *mrb, struct RObject *obj, mrb_sym sym) { mrb_value v; if (obj->iv && iv_get(mrb, obj->iv, sym, &v)) return v; return mrb_nil_value(); } MRB_API mrb_value mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym) { if (obj_iv_p(obj)) { return mrb_obj_iv_get(mrb, mrb_obj_ptr(obj), sym); } return mrb_nil_value(); } static inline mrb_bool namespace_p(enum mrb_vtype tt) { return tt == MRB_TT_CLASS || tt == MRB_TT_MODULE ? TRUE : FALSE; } static inline void assign_class_name(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v) { if (namespace_p(mrb_type(v))) { struct RObject *c = mrb_obj_ptr(v); if (obj != c && ISUPPER(mrb_sym_name_len(mrb, sym, NULL)[0])) { mrb_sym id_classname = MRB_SYM(__classname__); mrb_value o = mrb_obj_iv_get(mrb, c, id_classname); if (mrb_nil_p(o)) { mrb_sym id_outer = MRB_SYM(__outer__); o = mrb_obj_iv_get(mrb, c, id_outer); if (mrb_nil_p(o)) { if ((struct RClass*)obj == mrb->object_class) { mrb_obj_iv_set_force(mrb, c, id_classname, mrb_symbol_value(sym)); } else { mrb_obj_iv_set_force(mrb, c, id_outer, mrb_obj_value(obj)); } } } } } } void mrb_obj_iv_set_force(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v) { if (namespace_p(obj->tt)) { assign_class_name(mrb, obj, sym, v); } if (!obj->iv) { obj->iv = iv_new(mrb); } iv_put(mrb, obj->iv, sym, v); mrb_field_write_barrier_value(mrb, (struct RBasic*)obj, v); } MRB_API void mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v) { mrb_check_frozen(mrb, obj); mrb_obj_iv_set_force(mrb, obj, sym, v); } /* Iterates over the instance variable table. */ MRB_API void mrb_iv_foreach(mrb_state *mrb, mrb_value obj, mrb_iv_foreach_func *func, void *p) { if (!obj_iv_p(obj)) return; iv_foreach(mrb, mrb_obj_ptr(obj)->iv, func, p); } MRB_API void mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v) { if (obj_iv_p(obj)) { mrb_obj_iv_set(mrb, mrb_obj_ptr(obj), sym, v); } else { mrb_raise(mrb, E_ARGUMENT_ERROR, "cannot set instance variable"); } } MRB_API mrb_bool mrb_obj_iv_defined(mrb_state *mrb, struct RObject *obj, mrb_sym sym) { iv_tbl *t; t = obj->iv; if (t && iv_get(mrb, t, sym, NULL)) return TRUE; return FALSE; } MRB_API mrb_bool mrb_iv_defined(mrb_state *mrb, mrb_value obj, mrb_sym sym) { if (!obj_iv_p(obj)) return FALSE; return mrb_obj_iv_defined(mrb, mrb_obj_ptr(obj), sym); } MRB_API mrb_bool mrb_iv_name_sym_p(mrb_state *mrb, mrb_sym iv_name) { const char *s; mrb_int len; s = mrb_sym_name_len(mrb, iv_name, &len); if (len < 2) return FALSE; if (s[0] != '@') return FALSE; if (ISDIGIT(s[1])) return FALSE; return mrb_ident_p(s+1, len-1); } MRB_API void mrb_iv_name_sym_check(mrb_state *mrb, mrb_sym iv_name) { if (!mrb_iv_name_sym_p(mrb, iv_name)) { mrb_name_error(mrb, iv_name, "'%n' is not allowed as an instance variable name", iv_name); } } MRB_API void mrb_iv_copy(mrb_state *mrb, mrb_value dest, mrb_value src) { struct RObject *d = mrb_obj_ptr(dest); struct RObject *s = mrb_obj_ptr(src); if (d->iv) { iv_free(mrb, d->iv); d->iv = 0; } if (s->iv) { mrb_write_barrier(mrb, (struct RBasic*)d); d->iv = iv_copy(mrb, s->iv); } } static int inspect_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { mrb_value str = *(mrb_value*)p; const char *s; mrb_int len; mrb_value ins; char *sp = RSTRING_PTR(str); /* need not to show internal data */ if (sp[0] == '-') { /* first element */ sp[0] = '#'; mrb_str_cat_lit(mrb, str, " "); } else { mrb_str_cat_lit(mrb, str, ", "); } s = mrb_sym_name_len(mrb, sym, &len); mrb_str_cat(mrb, str, s, len); mrb_str_cat_lit(mrb, str, "="); ins = mrb_inspect(mrb, v); mrb_str_cat_str(mrb, str, ins); return 0; } mrb_value mrb_obj_iv_inspect(mrb_state *mrb, struct RObject *obj) { iv_tbl *t = obj->iv; size_t len = iv_size(mrb, t); if (len > 0) { const char *cn = mrb_obj_classname(mrb, mrb_obj_value(obj)); mrb_value str = mrb_str_new_capa(mrb, 30); mrb_str_cat_lit(mrb, str, "-<"); mrb_str_cat_cstr(mrb, str, cn); mrb_str_cat_lit(mrb, str, ":"); mrb_str_cat_str(mrb, str, mrb_ptr_to_str(mrb, obj)); if (mrb_inspect_recursive_p(mrb, mrb_obj_value(obj))) { mrb_str_cat_lit(mrb, str, " ...>"); return str; } iv_foreach(mrb, t, inspect_i, &str); mrb_str_cat_lit(mrb, str, ">"); return str; } return mrb_any_to_s(mrb, mrb_obj_value(obj)); } MRB_API mrb_value mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym) { if (obj_iv_p(obj)) { struct RObject *o = mrb_obj_ptr(obj); iv_tbl *t = o->iv; mrb_value val; mrb_check_frozen(mrb, o); if (iv_del(mrb, t, sym, &val)) { return val; } } return mrb_undef_value(); } static int iv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { mrb_value ary; const char* s; mrb_int len; ary = *(mrb_value*)p; s = mrb_sym_name_len(mrb, sym, &len); if (len > 1 && s[0] == '@' && s[1] != '@') { mrb_ary_push(mrb, ary, mrb_symbol_value(sym)); } return 0; } /* 15.3.1.3.23 */ /* * call-seq: * obj.instance_variables -> array * * Returns an array of instance variable names for the receiver. Note * that simply defining an accessor does not create the corresponding * instance variable. * * class Fred * attr_accessor :a1 * def initialize * @iv = 3 * end * end * Fred.new.instance_variables #=> [:@iv] */ mrb_value mrb_obj_instance_variables(mrb_state *mrb, mrb_value self) { mrb_value ary; ary = mrb_ary_new(mrb); if (obj_iv_p(self)) { iv_foreach(mrb, mrb_obj_ptr(self)->iv, iv_i, &ary); } return ary; } static int cv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { mrb_value ary; const char* s; mrb_int len; ary = *(mrb_value*)p; s = mrb_sym_name_len(mrb, sym, &len); if (len > 2 && s[0] == '@' && s[1] == '@') { mrb_ary_push(mrb, ary, mrb_symbol_value(sym)); } return 0; } /* 15.2.2.4.19 */ /* * call-seq: * mod.class_variables(inherit=true) -> array * * Returns an array of the names of class variables in mod. * * class One * @@var1 = 1 * end * class Two < One * @@var2 = 2 * end * One.class_variables #=> [:@@var1] * Two.class_variables #=> [:@@var2] */ mrb_value mrb_mod_class_variables(mrb_state *mrb, mrb_value mod) { mrb_value ary; struct RClass *c; mrb_bool inherit = TRUE; mrb_get_args(mrb, "|b", &inherit); ary = mrb_ary_new(mrb); c = mrb_class_ptr(mod); while (c) { iv_foreach(mrb, class_iv_ptr(c), cv_i, &ary); if (!inherit) break; c = c->super; } return ary; } mrb_value mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym) { struct RClass * cls = c; mrb_value v; mrb_bool given = FALSE; while (c) { if (iv_get(mrb, class_iv_ptr(c), sym, &v)) { given = TRUE; } c = c->super; } if (given) return v; if (cls->tt == MRB_TT_SCLASS) { mrb_value klass; klass = mrb_obj_iv_get(mrb, (struct RObject*)cls, MRB_SYM(__attached__)); c = mrb_class_ptr(klass); if (c->tt == MRB_TT_CLASS || c->tt == MRB_TT_MODULE) { given = FALSE; while (c) { if (iv_get(mrb, class_iv_ptr(c), sym, &v)) { given = TRUE; } c = c->super; } if (given) return v; } } mrb_name_error(mrb, sym, "uninitialized class variable %n in %C", sym, cls); /* not reached */ return mrb_nil_value(); } MRB_API mrb_value mrb_cv_get(mrb_state *mrb, mrb_value mod, mrb_sym sym) { return mrb_mod_cv_get(mrb, mrb_class_ptr(mod), sym); } MRB_API void mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v) { struct RClass * cls = c; while (c) { iv_tbl *t = class_iv_ptr(c); int pos = iv_get(mrb, t, sym, NULL); if (pos) { mrb_check_frozen(mrb, c); t->ptr[pos-1] = v; /* iv_get returns pos+1 to put */ mrb_field_write_barrier_value(mrb, (struct RBasic*)c, v); return; } c = c->super; } if (cls->tt == MRB_TT_SCLASS) { mrb_value klass; klass = mrb_obj_iv_get(mrb, (struct RObject*)cls, MRB_SYM(__attached__)); switch (mrb_type(klass)) { case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_SCLASS: c = mrb_class_ptr(klass); break; default: c = cls; break; } } else if (cls->tt == MRB_TT_ICLASS) { c = cls->c; } else { c = cls; } mrb_check_frozen(mrb, c); if (!c->iv) { c->iv = iv_new(mrb); } iv_put(mrb, c->iv, sym, v); mrb_field_write_barrier_value(mrb, (struct RBasic*)c, v); } MRB_API void mrb_cv_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v) { mrb_mod_cv_set(mrb, mrb_class_ptr(mod), sym, v); } mrb_bool mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym) { while (c) { iv_tbl *t = class_iv_ptr(c); if (iv_get(mrb, t, sym, NULL)) return TRUE; c = c->super; } return FALSE; } MRB_API mrb_bool mrb_cv_defined(mrb_state *mrb, mrb_value mod, mrb_sym sym) { return mrb_mod_cv_defined(mrb, mrb_class_ptr(mod), sym); } mrb_value mrb_vm_cv_get(mrb_state *mrb, mrb_sym sym) { struct RClass *c; const struct RProc *p = mrb->c->ci->proc; for (;;) { c = MRB_PROC_TARGET_CLASS(p); if (c && c->tt != MRB_TT_SCLASS) break; p = p->upper; } return mrb_mod_cv_get(mrb, c, sym); } void mrb_vm_cv_set(mrb_state *mrb, mrb_sym sym, mrb_value v) { struct RClass *c; const struct RProc *p = mrb->c->ci->proc; for (;;) { c = MRB_PROC_TARGET_CLASS(p); if (c && c->tt != MRB_TT_SCLASS) break; p = p->upper; } mrb_mod_cv_set(mrb, c, sym, v); } static void mod_const_check(mrb_state *mrb, mrb_value mod) { switch (mrb_type(mod)) { case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_SCLASS: break; default: mrb_raise(mrb, E_TYPE_ERROR, "constant look-up for non class/module"); break; } } static mrb_value const_get_nohook(mrb_state *mrb, struct RClass *base, mrb_sym sym, mrb_bool skip) { struct RClass *c = base; mrb_value v; mrb_bool retry = FALSE; /* if skip then skip the current class (already searched) */ if (skip) c = c->super; L_RETRY: while (c) { if (!MRB_FLAG_TEST(c, MRB_FL_CLASS_IS_PREPENDED) && iv_get(mrb, class_iv_ptr(c), sym, &v)) { return v; } c = c->super; if (!skip && c == mrb->object_class) break; } if (!retry && base->tt == MRB_TT_MODULE && skip) { c = mrb->object_class; retry = TRUE; goto L_RETRY; } return mrb_undef_value(); } static mrb_value const_get(mrb_state *mrb, struct RClass *base, mrb_sym sym, mrb_bool skip) { mrb_value v = const_get_nohook(mrb, base, sym, skip); /* call const_missing hook */ if (mrb_undef_p(v)) { mrb_value mod = mrb_obj_value(base); if (mrb_func_basic_p(mrb, mod, MRB_SYM(const_missing), mrb_mod_const_missing)) { return mrb_const_missing(mrb, mod, sym); } mrb_value name = mrb_symbol_value(sym); return mrb_funcall_argv(mrb, mod, MRB_SYM(const_missing), 1, &name); } return v; } mrb_value mrb_exc_const_get(mrb_state *mrb, mrb_sym sym) { return const_get_nohook(mrb, mrb->object_class, sym, FALSE); } MRB_API mrb_value mrb_const_get(mrb_state *mrb, mrb_value mod, mrb_sym sym) { mod_const_check(mrb, mod); return const_get(mrb, mrb_class_ptr(mod), sym, FALSE); } mrb_value mrb_vm_const_get(mrb_state *mrb, mrb_sym sym) { const struct RProc *proc = mrb->c->ci->proc; struct RClass *c = MRB_PROC_TARGET_CLASS(proc), *c2; mrb_value v; if (!c) c = mrb->object_class; if (iv_get(mrb, class_iv_ptr(c), sym, &v)) { return v; } for (proc = proc->upper; proc; proc = proc->upper) { c2 = MRB_PROC_TARGET_CLASS(proc); if (!c2) c2 = mrb->object_class; if (iv_get(mrb, class_iv_ptr(c2), sym, &v)) { return v; } } if (c->tt == MRB_TT_SCLASS) { v = const_get_nohook(mrb, c, sym, TRUE); if (!mrb_undef_p(v)) { return v; } mrb_value klass; for (c2 = c; c2 && c2->tt == MRB_TT_SCLASS; c2 = mrb_class_ptr(klass)) { if (!iv_get(mrb, class_iv_ptr(c2), MRB_SYM(__attached__), &klass)) { c2 = NULL; break; } } if (c2 && (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE)) c = c2; } return const_get(mrb, c, sym, TRUE); } MRB_API void mrb_const_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v) { mod_const_check(mrb, mod); if (mrb_type(v) == MRB_TT_CLASS || mrb_type(v) == MRB_TT_MODULE) { mrb_class_name_class(mrb, mrb_class_ptr(mod), mrb_class_ptr(v), sym); } mrb_obj_iv_set(mrb, mrb_obj_ptr(mod), sym, v); mrb_value name = mrb_symbol_value(sym); mrb_funcall_argv(mrb, mod, MRB_SYM(const_added), 1, &name); } MRB_API void mrb_const_remove(mrb_state *mrb, mrb_value mod, mrb_sym sym) { mod_const_check(mrb, mod); mrb_iv_remove(mrb, mod, sym); } MRB_API void mrb_define_const_id(mrb_state *mrb, struct RClass *mod, mrb_sym name, mrb_value v) { mrb_obj_iv_set(mrb, (struct RObject*)mod, name, v); } MRB_API void mrb_define_const(mrb_state *mrb, struct RClass *mod, const char *name, mrb_value v) { mrb_obj_iv_set(mrb, (struct RObject*)mod, mrb_intern_cstr(mrb, name), v); } MRB_API void mrb_define_global_const(mrb_state *mrb, const char *name, mrb_value val) { mrb_define_const(mrb, mrb->object_class, name, val); } static int const_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { mrb_value ary; const char* s; mrb_int len; ary = *(mrb_value*)p; s = mrb_sym_name_len(mrb, sym, &len); if (len >= 1 && ISUPPER(s[0])) { mrb_int i, alen = RARRAY_LEN(ary); for (i=0; i array * * Returns an array of all names of constants defined in the receiver. */ mrb_value mrb_mod_constants(mrb_state *mrb, mrb_value mod) { mrb_value ary; mrb_bool inherit = TRUE; struct RClass *c = mrb_class_ptr(mod); mrb_get_args(mrb, "|b", &inherit); ary = mrb_ary_new(mrb); while (c) { mrb_mod_const_at(mrb, c, ary); if (!inherit) break; c = c->super; if (c == mrb->object_class) break; } return ary; } MRB_API mrb_value mrb_gv_get(mrb_state *mrb, mrb_sym sym) { mrb_value v; if (iv_get(mrb, mrb->globals, sym, &v)) return v; return mrb_nil_value(); } MRB_API void mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value v) { iv_tbl *t; if (!mrb->globals) { mrb->globals = iv_new(mrb); } t = mrb->globals; iv_put(mrb, t, sym, v); } MRB_API void mrb_gv_remove(mrb_state *mrb, mrb_sym sym) { iv_del(mrb, mrb->globals, sym, NULL); } static int gv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { mrb_value ary; ary = *(mrb_value*)p; mrb_ary_push(mrb, ary, mrb_symbol_value(sym)); return 0; } /* 15.3.1.2.4 */ /* 15.3.1.3.14 */ /* * call-seq: * global_variables -> array * * Returns an array of the names of global variables. * * global_variables.grep /std/ #=> [:$stdin, :$stdout, :$stderr] */ mrb_value mrb_f_global_variables(mrb_state *mrb, mrb_value self) { iv_tbl *t = mrb->globals; mrb_value ary = mrb_ary_new(mrb); iv_foreach(mrb, t, gv_i, &ary); return ary; } static mrb_bool const_defined_0(mrb_state *mrb, mrb_value mod, mrb_sym id, mrb_bool exclude, mrb_bool recurse) { struct RClass *klass = mrb_class_ptr(mod); struct RClass *tmp; mrb_bool mod_retry = FALSE; tmp = klass; retry: while (tmp) { if (iv_get(mrb, class_iv_ptr(tmp), id, NULL)) { return TRUE; } if (!recurse && (klass != mrb->object_class)) break; tmp = tmp->super; } if (!exclude && !mod_retry && (klass->tt == MRB_TT_MODULE)) { mod_retry = TRUE; tmp = mrb->object_class; goto retry; } return FALSE; } MRB_API mrb_bool mrb_const_defined(mrb_state *mrb, mrb_value mod, mrb_sym id) { return const_defined_0(mrb, mod, id, TRUE, TRUE); } MRB_API mrb_bool mrb_const_defined_at(mrb_state *mrb, mrb_value mod, mrb_sym id) { return const_defined_0(mrb, mod, id, TRUE, FALSE); } MRB_API mrb_value mrb_attr_get(mrb_state *mrb, mrb_value obj, mrb_sym id) { return mrb_iv_get(mrb, obj, id); } struct csym_arg { struct RClass *c; mrb_sym sym; }; static int csym_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { struct csym_arg *a = (struct csym_arg*)p; struct RClass *c = a->c; if (mrb_type(v) == c->tt && mrb_class_ptr(v) == c) { a->sym = sym; return 1; /* stop iteration */ } return 0; } static mrb_sym find_class_sym(mrb_state *mrb, struct RClass *outer, struct RClass *c) { struct csym_arg arg; if (!outer) return 0; if (outer == c) return 0; arg.c = c; arg.sym = 0; iv_foreach(mrb, class_iv_ptr(outer), csym_i, &arg); return arg.sym; } static struct RClass* outer_class(mrb_state *mrb, struct RClass *c) { mrb_value ov; ov = mrb_obj_iv_get(mrb, (struct RObject*)c, MRB_SYM(__outer__)); if (mrb_nil_p(ov)) return NULL; switch (mrb_type(ov)) { case MRB_TT_CLASS: case MRB_TT_MODULE: return mrb_class_ptr(ov); default: break; } return NULL; } static mrb_bool detect_outer_loop(mrb_state *mrb, struct RClass *c) { struct RClass *t = c; /* tortoise */ struct RClass *h = c; /* hare */ for (;;) { if (h == NULL) return FALSE; h = outer_class(mrb, h); if (h == NULL) return FALSE; h = outer_class(mrb, h); t = outer_class(mrb, t); if (t == h) return TRUE; } } mrb_value mrb_class_find_path(mrb_state *mrb, struct RClass *c) { struct RClass *outer; mrb_value path; mrb_sym name; const char *str; mrb_int len; if (detect_outer_loop(mrb, c)) return mrb_nil_value(); outer = outer_class(mrb, c); if (outer == NULL) return mrb_nil_value(); name = find_class_sym(mrb, outer, c); if (name == 0) return mrb_nil_value(); path = mrb_str_new_capa(mrb, 40); str = mrb_class_name(mrb, outer); mrb_str_cat_cstr(mrb, path, str); mrb_str_cat_cstr(mrb, path, "::"); str = mrb_sym_name_len(mrb, name, &len); mrb_str_cat(mrb, path, str, len); if (RSTRING_PTR(path)[0] != '#') { iv_del(mrb, c->iv, MRB_SYM(__outer__), NULL); iv_put(mrb, c->iv, MRB_SYM(__classname__), path); mrb_field_write_barrier_value(mrb, (struct RBasic*)c, path); path = mrb_str_dup(mrb, path); } return path; } size_t mrb_obj_iv_tbl_memsize(mrb_value obj) { iv_tbl *t = mrb_obj_ptr(obj)->iv; if (t == NULL) return 0; return sizeof(iv_tbl) + t->alloc*(sizeof(mrb_value)+sizeof(mrb_sym)); } #define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c)) mrb_bool mrb_ident_p(const char *s, mrb_int len) { for (mrb_int i = 0; i < len; i++) { if (!identchar(s[i])) return FALSE; } return TRUE; } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/readint.c0000644000000000000000000000013215077107276020254 xustar0030 mtime=1761382078.136420492 30 atime=1761382080.158411242 30 ctime=1761382108.575301899 nghttp2-1.68.0/third-party/mruby/src/readint.c0000644000175100017510000000152715077107276020651 0ustar00runnerrunner#include #include /* mrb_read_int(): read mrb_int from a string (base 10 only) */ /* const char *p - string to read */ /* const char *e - end of string */ /* char **endp - end of parsed integer */ /* mrb_int *np - variable to save the result */ /* returns TRUE if read succeeded */ /* if integer overflows, returns FALSE */ MRB_API mrb_bool mrb_read_int(const char *p, const char *e, char **endp, mrb_int *np) { mrb_int n = 0; while ((e == NULL || p < e) && ISDIGIT(*p)) { int ch = *p - '0'; if (mrb_int_mul_overflow(n, 10, &n) || mrb_int_add_overflow(n, ch, &n)) { return FALSE; } p++; } if (endp) *endp = (char*)p; *np = n; return TRUE; } nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/dump.c0000644000000000000000000000013215077107276017573 xustar0030 mtime=1761382078.133420506 30 atime=1761382080.153411265 30 ctime=1761382108.593301847 nghttp2-1.68.0/third-party/mruby/src/dump.c0000644000175100017510000005663315077107276020200 0ustar00runnerrunner/* ** dump.c - mruby binary dumper (mrbc binary format) ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #ifndef MRB_NO_FLOAT #include #endif static size_t get_irep_record_size_1(mrb_state *mrb, const mrb_irep *irep); #if UINT32_MAX > SIZE_MAX # error This code cannot be built on your environment. #endif static size_t get_irep_header_size(mrb_state *mrb) { size_t size = 0; size += sizeof(uint32_t) * 1; size += sizeof(uint16_t) * 3; return size; } static ptrdiff_t write_irep_header(mrb_state *mrb, const mrb_irep *irep, uint8_t *buf) { uint8_t *cur = buf; cur += uint32_to_bin((uint32_t)get_irep_record_size_1(mrb, irep), cur); /* record size */ cur += uint16_to_bin((uint16_t)irep->nlocals, cur); /* number of local variable */ cur += uint16_to_bin((uint16_t)irep->nregs, cur); /* number of register variable */ cur += uint16_to_bin((uint16_t)irep->rlen, cur); /* number of child irep */ return cur - buf; } static size_t get_iseq_block_size(mrb_state *mrb, const mrb_irep *irep) { size_t size = 0; size += sizeof(uint16_t); /* clen */ size += sizeof(uint32_t); /* ilen */ size += irep->ilen * sizeof(mrb_code); /* iseq(n) */ size += irep->clen * sizeof(struct mrb_irep_catch_handler); return size; } static ptrdiff_t write_iseq_block(mrb_state *mrb, const mrb_irep *irep, uint8_t *buf, uint8_t flags) { uint8_t *cur = buf; size_t seqlen = irep->ilen * sizeof(mrb_code) + irep->clen * sizeof(struct mrb_irep_catch_handler); cur += uint16_to_bin(irep->clen, cur); /* number of catch handlers */ cur += uint32_to_bin(irep->ilen, cur); /* number of opcode */ memcpy(cur, irep->iseq, seqlen); cur += seqlen; return cur - buf; } #ifndef MRB_NO_FLOAT static void dump_float(mrb_state *mrb, uint8_t *buf, mrb_float f) { /* dump IEEE754 binary in little endian */ union { double f; char s[sizeof(double)]; } u = {(double)f}; if (littleendian) { memcpy(buf, u.s, sizeof(double)); } else { for (size_t i=0; iplen * sizeof(uint8_t); /* len(n) */ for (int pool_no = 0; pool_no < irep->plen; pool_no++) { int ai = mrb_gc_arena_save(mrb); switch (irep->pool[pool_no].tt) { case IREP_TT_INT64: #if defined(MRB_64BIT) || defined(MRB_INT64) { int64_t i = irep->pool[pool_no].u.i64; if (i < INT32_MIN || INT32_MAX < i) size += 8; else size += 4; } break; #else /* fall through */ #endif case IREP_TT_INT32: size += 4; /* 32 bits = 4 bytes */ break; case IREP_TT_BIGINT: { mrb_int len = irep->pool[pool_no].u.str[0]; mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); size += (size_t)len+2; } break; case IREP_TT_FLOAT: #ifndef MRB_NO_FLOAT { size += sizeof(double); } #endif break; default: /* packed IREP_TT_STRING */ { mrb_int len = irep->pool[pool_no].tt >> 2; /* unpack length */ mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); size += sizeof(uint16_t); size += (size_t)len+1; } break; } mrb_gc_arena_restore(mrb, ai); } return size; } static ptrdiff_t write_pool_block(mrb_state *mrb, const mrb_irep *irep, uint8_t *buf) { uint8_t *cur = buf; mrb_int len; const char *ptr; cur += uint16_to_bin(irep->plen, cur); /* number of pool */ for (int pool_no = 0; pool_no < irep->plen; pool_no++) { int ai = mrb_gc_arena_save(mrb); switch (irep->pool[pool_no].tt) { case IREP_TT_INT64: #if defined(MRB_64BIT) || defined(MRB_INT64) { int64_t i = irep->pool[pool_no].u.i64; if (i < INT32_MIN || INT32_MAX < i) { cur += uint8_to_bin(IREP_TT_INT64, cur); /* data type */ cur += uint32_to_bin((uint32_t)((i>>32) & 0xffffffff), cur); /* i64 hi */ cur += uint32_to_bin((uint32_t)((i ) & 0xffffffff), cur); /* i64 lo */ } else { cur += uint8_to_bin(IREP_TT_INT32, cur); /* data type */ cur += uint32_to_bin(irep->pool[pool_no].u.i32, cur); /* i32 */ } } break; #endif case IREP_TT_INT32: cur += uint8_to_bin(IREP_TT_INT32, cur); /* data type */ cur += uint32_to_bin(irep->pool[pool_no].u.i32, cur); /* i32 */ break; case IREP_TT_BIGINT: cur += uint8_to_bin(IREP_TT_BIGINT, cur); /* data type */ len = irep->pool[pool_no].u.str[0]; memcpy(cur, irep->pool[pool_no].u.str, (size_t)len+2); cur += len+2; break; case IREP_TT_FLOAT: cur += uint8_to_bin(IREP_TT_FLOAT, cur); /* data type */ #ifndef MRB_NO_FLOAT { dump_float(mrb, cur,irep->pool[pool_no].u.f); cur += sizeof(double); } #else cur += uint16_to_bin(0, cur); /* zero length */ #endif break; default: /* string */ cur += uint8_to_bin(IREP_TT_STR, cur); /* data type */ ptr = irep->pool[pool_no].u.str; len = irep->pool[pool_no].tt>>2; mrb_assert_int_fit(mrb_int, len, uint16_t, UINT16_MAX); cur += uint16_to_bin((uint16_t)len, cur); /* data length */ memcpy(cur, ptr, (size_t)len); cur += len; *cur++ = '\0'; break; } mrb_gc_arena_restore(mrb, ai); } return cur - buf; } static size_t get_syms_block_size(mrb_state *mrb, const mrb_irep *irep) { size_t size = 0; int sym_no; mrb_int len; size += sizeof(uint16_t); /* slen */ for (sym_no = 0; sym_no < irep->slen; sym_no++) { size += sizeof(uint16_t); /* snl(n) */ if (irep->syms[sym_no] != 0) { mrb_sym_name_len(mrb, irep->syms[sym_no], &len); size += len + 1; /* sn(n) + null char */ } } return size; } static ptrdiff_t write_syms_block(mrb_state *mrb, const mrb_irep *irep, uint8_t *buf) { uint8_t *cur = buf; cur += uint16_to_bin(irep->slen, cur); /* number of symbol */ for (int sym_no = 0; sym_no < irep->slen; sym_no++) { if (irep->syms[sym_no] != 0) { mrb_int len; const char *name = mrb_sym_name_len(mrb, irep->syms[sym_no], &len); mrb_assert_int_fit(mrb_int, len, uint16_t, UINT16_MAX); cur += uint16_to_bin((uint16_t)len, cur); /* length of symbol name */ memcpy(cur, name, len); /* symbol name */ cur += (uint16_t)len; *cur++ = '\0'; } else { cur += uint16_to_bin(MRB_DUMP_NULL_SYM_LEN, cur); /* length of symbol name */ } } return cur - buf; } static size_t get_irep_record_size_1(mrb_state *mrb, const mrb_irep *irep) { size_t size = get_irep_header_size(mrb); size += get_iseq_block_size(mrb, irep); size += get_pool_block_size(mrb, irep); size += get_syms_block_size(mrb, irep); return size; } static size_t get_irep_record_size(mrb_state *mrb, const mrb_irep *irep) { size_t size = get_irep_record_size_1(mrb, irep); for (int irep_no = 0; irep_no < irep->rlen; irep_no++) { size += get_irep_record_size(mrb, irep->reps[irep_no]); } return size; } static int write_irep_record(mrb_state *mrb, const mrb_irep *irep, uint8_t *bin, size_t *irep_record_size, uint8_t flags) { uint8_t *src = bin; if (irep == NULL) { return MRB_DUMP_INVALID_IREP; } bin += write_irep_header(mrb, irep, bin); bin += write_iseq_block(mrb, irep, bin, flags); bin += write_pool_block(mrb, irep, bin); bin += write_syms_block(mrb, irep, bin); for (int i = 0; i < irep->rlen; i++) { int result; size_t rsize; result = write_irep_record(mrb, irep->reps[i], bin, &rsize, flags); if (result != MRB_DUMP_OK) { return result; } bin += rsize; } *irep_record_size = bin - src; return MRB_DUMP_OK; } static uint32_t write_footer(mrb_state *mrb, uint8_t *bin) { struct rite_binary_footer footer; memcpy(footer.section_ident, RITE_BINARY_EOF, sizeof(footer.section_ident)); uint32_to_bin(sizeof(struct rite_binary_footer), footer.section_size); memcpy(bin, &footer, sizeof(struct rite_binary_footer)); return sizeof(struct rite_binary_footer); } static int write_section_irep_header(mrb_state *mrb, size_t section_size, uint8_t *bin) { struct rite_section_irep_header *header = (struct rite_section_irep_header*)bin; memcpy(header->section_ident, RITE_SECTION_IREP_IDENT, sizeof(header->section_ident)); mrb_assert_int_fit(size_t, section_size, uint32_t, UINT32_MAX); uint32_to_bin((uint32_t)section_size, header->section_size); memcpy(header->rite_version, RITE_VM_VER, sizeof(header->rite_version)); return MRB_DUMP_OK; } static int write_section_irep(mrb_state *mrb, const mrb_irep *irep, uint8_t *bin, size_t *len_p, uint8_t flags) { uint8_t *cur = bin; if (mrb == NULL || bin == NULL) { return MRB_DUMP_INVALID_ARGUMENT; } cur += sizeof(struct rite_section_irep_header); size_t rsize = 0; int result = write_irep_record(mrb, irep, cur, &rsize, flags); if (result != MRB_DUMP_OK) { return result; } mrb_assert(rsize == get_irep_record_size(mrb, irep)); *len_p = cur - bin + rsize; write_section_irep_header(mrb, *len_p, bin); return MRB_DUMP_OK; } static size_t get_debug_record_size(mrb_state *mrb, const mrb_irep *irep) { size_t ret = sizeof(uint32_t); /* record size */ ret += sizeof(uint16_t); /* file count */ for (uint16_t f_idx = 0; f_idx < irep->debug_info->flen; f_idx++) { mrb_irep_debug_info_file const* file = irep->debug_info->files[f_idx]; ret += sizeof(uint32_t); /* position */ ret += sizeof(uint16_t); /* filename index */ /* lines */ ret += sizeof(uint32_t); /* entry count */ ret += sizeof(uint8_t); /* line type */ switch (file->line_type) { case mrb_debug_line_ary: ret += sizeof(uint16_t) * (size_t)(file->line_entry_count); break; case mrb_debug_line_flat_map: ret += (sizeof(uint32_t) + sizeof(uint16_t)) * (size_t)(file->line_entry_count); break; case mrb_debug_line_packed_map: ret += (size_t)(file->line_entry_count); break; default: mrb_assert(0); break; } } for (int i=0; irlen; i++) { ret += get_debug_record_size(mrb, irep->reps[i]); } return ret; } static int find_filename_index(const mrb_sym *ary, int ary_len, mrb_sym s) { for (int i = 0; i < ary_len; i++) { if (ary[i] == s) return i; } return -1; } static size_t get_filename_table_size(mrb_state *mrb, const mrb_irep *irep, mrb_sym **fp, uint16_t *lp) { mrb_sym *filenames = *fp; size_t size = 0; const mrb_irep_debug_info *di = irep->debug_info; mrb_assert(lp); for (int i = 0; i < di->flen; i++) { mrb_irep_debug_info_file *file; mrb_int filename_len; file = di->files[i]; if (find_filename_index(filenames, *lp, file->filename_sym) == -1) { /* register filename */ *lp += 1; *fp = filenames = (mrb_sym*)mrb_realloc(mrb, filenames, sizeof(mrb_sym) * (*lp)); filenames[*lp - 1] = file->filename_sym; /* filename */ mrb_sym_name_len(mrb, file->filename_sym, &filename_len); size += sizeof(uint16_t) + (size_t)filename_len; } } for (int i=0; irlen; i++) { size += get_filename_table_size(mrb, irep->reps[i], fp, lp); } return size; } static size_t write_debug_record_1(mrb_state *mrb, const mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len) { uint8_t *cur; cur = bin + sizeof(uint32_t); /* skip record size */ cur += uint16_to_bin(irep->debug_info->flen, cur); /* file count */ for (int f_idx = 0; f_idx < irep->debug_info->flen; f_idx++) { int filename_idx; const mrb_irep_debug_info_file *file = irep->debug_info->files[f_idx]; /* position */ cur += uint32_to_bin(file->start_pos, cur); /* filename index */ filename_idx = find_filename_index(filenames, filenames_len, file->filename_sym); mrb_assert_int_fit(int, filename_idx, uint16_t, UINT16_MAX); cur += uint16_to_bin((uint16_t)filename_idx, cur); /* lines */ cur += uint32_to_bin(file->line_entry_count, cur); cur += uint8_to_bin(file->line_type, cur); switch (file->line_type) { case mrb_debug_line_ary: { uint32_t l; for (l = 0; l < file->line_entry_count; l++) { cur += uint16_to_bin(file->lines.ary[l], cur); } } break; case mrb_debug_line_flat_map: { uint32_t line; for (line = 0; line < file->line_entry_count; line++) { cur += uint32_to_bin(file->lines.flat_map[line].start_pos, cur); cur += uint16_to_bin(file->lines.flat_map[line].line, cur); } } break; case mrb_debug_line_packed_map: { memcpy(cur, file->lines.packed_map, file->line_entry_count); cur += file->line_entry_count; } break; default: mrb_assert(0); break; } } ptrdiff_t ret = cur - bin; mrb_assert_int_fit(ptrdiff_t, ret, uint32_t, UINT32_MAX); uint32_to_bin((uint32_t)ret, bin); mrb_assert_int_fit(ptrdiff_t, ret, size_t, SIZE_MAX); return (size_t)ret; } static size_t write_debug_record(mrb_state *mrb, const mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len) { size_t size = write_debug_record_1(mrb, irep, bin, filenames, filenames_len); bin += size; for (int irep_no = 0; irep_no < irep->rlen; irep_no++) { size_t len = write_debug_record(mrb, irep->reps[irep_no], bin, filenames, filenames_len); bin += len; size += len; } mrb_assert(size == get_debug_record_size(mrb, irep)); return size; } static int write_section_debug(mrb_state *mrb, const mrb_irep *irep, uint8_t *cur, mrb_sym const *filenames, uint16_t filenames_len) { const uint8_t *bin = cur; if (mrb == NULL || cur == NULL) { return MRB_DUMP_INVALID_ARGUMENT; } struct rite_section_debug_header *header = (struct rite_section_debug_header*)bin; size_t section_size = sizeof(struct rite_section_debug_header); cur += section_size; /* filename table */ cur += uint16_to_bin(filenames_len, cur); section_size += sizeof(uint16_t); for (int i = 0; i < filenames_len; i++) { char const *sym; mrb_int sym_len; sym = mrb_sym_name_len(mrb, filenames[i], &sym_len); mrb_assert(sym); cur += uint16_to_bin((uint16_t)sym_len, cur); memcpy(cur, sym, sym_len); cur += sym_len; section_size += sizeof(uint16_t) + sym_len; } /* debug records */ size_t dlen = write_debug_record(mrb, irep, cur, filenames, filenames_len); section_size += dlen; memcpy(header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(header->section_ident)); mrb_assert(section_size <= INT32_MAX); uint32_to_bin((uint32_t)section_size, header->section_size); return MRB_DUMP_OK; } static void create_lv_sym_table(mrb_state *mrb, const mrb_irep *irep, mrb_sym **syms, uint32_t *syms_len) { if (*syms == NULL) { *syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * 1); } for (int i = 0; i + 1 < irep->nlocals; i++) { mrb_sym const name = irep->lv[i]; if (name == 0) continue; if (find_filename_index(*syms, *syms_len, name) != -1) continue; (*syms_len)++; *syms = (mrb_sym*)mrb_realloc(mrb, *syms, sizeof(mrb_sym) * (*syms_len)); (*syms)[*syms_len - 1] = name; } for (int i = 0; i < irep->rlen; i++) { create_lv_sym_table(mrb, irep->reps[i], syms, syms_len); } } static int write_lv_sym_table(mrb_state *mrb, uint8_t **start, mrb_sym const *syms, uint32_t syms_len) { uint8_t *cur = *start; cur += uint32_to_bin(syms_len, cur); for (uint32_t i = 0; i < syms_len; i++) { mrb_int str_len; const char *str = mrb_sym_name_len(mrb, syms[i], &str_len); cur += uint16_to_bin((uint16_t)str_len, cur); memcpy(cur, str, str_len); cur += str_len; } *start = cur; return MRB_DUMP_OK; } static int write_lv_record(mrb_state *mrb, const mrb_irep *irep, uint8_t **start, mrb_sym const *syms, uint32_t syms_len) { uint8_t *cur = *start; for (int i = 0; i + 1 < irep->nlocals; i++) { if (irep->lv[i] == 0) { cur += uint16_to_bin(RITE_LV_NULL_MARK, cur); } else { int const sym_idx = find_filename_index(syms, syms_len, irep->lv[i]); mrb_assert(sym_idx != -1); /* local variable name must be in syms */ cur += uint16_to_bin(sym_idx, cur); } } for (int i = 0; i < irep->rlen; i++) { write_lv_record(mrb, irep->reps[i], &cur, syms, syms_len); } *start = cur; return MRB_DUMP_OK; } static size_t get_lv_record_size(mrb_state *mrb, const mrb_irep *irep) { size_t ret = sizeof(uint16_t) * (irep->nlocals - 1); for (int i = 0; i < irep->rlen; i++) { ret += get_lv_record_size(mrb, irep->reps[i]); } return ret; } static size_t get_lv_section_size(mrb_state *mrb, const mrb_irep *irep, mrb_sym const *syms, uint32_t syms_len) { size_t ret = sizeof(uint32_t); /* syms_len */ ret += sizeof(uint16_t) * syms_len; /* symbol name lengths */ for (uint32_t i = 0; i < syms_len; i++) { mrb_int str_len; mrb_sym_name_len(mrb, syms[i], &str_len); ret += str_len; } ret += get_lv_record_size(mrb, irep); return ret; } static int write_section_lv(mrb_state *mrb, const mrb_irep *irep, uint8_t *start, mrb_sym const *syms, uint32_t const syms_len) { uint8_t *cur = start; if (mrb == NULL || cur == NULL) { return MRB_DUMP_INVALID_ARGUMENT; } struct rite_section_lv_header *header = (struct rite_section_lv_header*)cur; cur += sizeof(struct rite_section_lv_header); int result = write_lv_sym_table(mrb, &cur, syms, syms_len); if (result != MRB_DUMP_OK) { return result; } result = write_lv_record(mrb, irep, &cur, syms, syms_len); if (result != MRB_DUMP_OK) { return result; } memcpy(header->section_ident, RITE_SECTION_LV_IDENT, sizeof(header->section_ident)); ptrdiff_t diff = cur - start; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); uint32_to_bin((uint32_t)diff, header->section_size); return result; } static int write_rite_binary_header(mrb_state *mrb, size_t binary_size, uint8_t *bin, uint8_t flags) { struct rite_binary_header *header = (struct rite_binary_header*)bin; memcpy(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)); memcpy(header->major_version, RITE_BINARY_MAJOR_VER, sizeof(header->major_version)); memcpy(header->minor_version, RITE_BINARY_MINOR_VER, sizeof(header->minor_version)); memcpy(header->compiler_name, RITE_COMPILER_NAME, sizeof(header->compiler_name)); memcpy(header->compiler_version, RITE_COMPILER_VERSION, sizeof(header->compiler_version)); mrb_assert(binary_size <= UINT32_MAX); uint32_to_bin((uint32_t)binary_size, header->binary_size); return MRB_DUMP_OK; } static mrb_bool debug_info_defined_p(const mrb_irep *irep) { if (!irep->debug_info) return FALSE; for (int i = 0; i < irep->rlen; i++) { if (!debug_info_defined_p(irep->reps[i])) return FALSE; } return TRUE; } static mrb_bool lv_defined_p(const mrb_irep *irep) { if (irep->lv) return TRUE; for (int i = 0; i < irep->rlen; i++) { if (lv_defined_p(irep->reps[i])) return TRUE; } return FALSE; } int mrb_dump_irep(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size) { size_t section_lineno_size = 0, section_lv_size = 0; uint8_t *cur = NULL; mrb_bool const debug_info_defined = (flags & MRB_DUMP_DEBUG_INFO) ? debug_info_defined_p(irep) : FALSE; mrb_bool lv_defined = (flags & MRB_DUMP_NO_LVAR) ? FALSE : lv_defined_p(irep); mrb_sym *lv_syms = NULL; uint32_t lv_syms_len = 0; mrb_sym *filenames = NULL; uint16_t filenames_len = 0; if (mrb == NULL) { *bin = NULL; return MRB_DUMP_GENERAL_FAILURE; } size_t section_irep_size = sizeof(struct rite_section_irep_header); section_irep_size += get_irep_record_size(mrb, irep); /* DEBUG section size */ if (debug_info_defined) { section_lineno_size += sizeof(struct rite_section_debug_header); /* filename table size */ section_lineno_size += sizeof(uint16_t); section_lineno_size += get_filename_table_size(mrb, irep, &filenames, &filenames_len); section_lineno_size += get_debug_record_size(mrb, irep); } if (lv_defined) { section_lv_size += sizeof(struct rite_section_lv_header); create_lv_sym_table(mrb, irep, &lv_syms, &lv_syms_len); section_lv_size += get_lv_section_size(mrb, irep, lv_syms, lv_syms_len); } size_t malloc_size = sizeof(struct rite_binary_header) + section_irep_size + section_lineno_size + section_lv_size + sizeof(struct rite_binary_footer); cur = *bin = (uint8_t*)mrb_malloc(mrb, malloc_size); cur += sizeof(struct rite_binary_header); int result = write_section_irep(mrb, irep, cur, §ion_irep_size, flags); if (result != MRB_DUMP_OK) { goto error_exit; } cur += section_irep_size; *bin_size = sizeof(struct rite_binary_header) + section_irep_size + section_lineno_size + section_lv_size + sizeof(struct rite_binary_footer); /* write DEBUG section */ if ((flags & MRB_DUMP_DEBUG_INFO) && debug_info_defined) { result = write_section_debug(mrb, irep, cur, filenames, filenames_len); if (result != MRB_DUMP_OK) { goto error_exit; } cur += section_lineno_size; } if (lv_defined) { result = write_section_lv(mrb, irep, cur, lv_syms, lv_syms_len); if (result != MRB_DUMP_OK) { goto error_exit; } cur += section_lv_size; } write_footer(mrb, cur); write_rite_binary_header(mrb, *bin_size, *bin, flags); error_exit: if (result != MRB_DUMP_OK) { mrb_free(mrb, *bin); *bin = NULL; } mrb_free(mrb, lv_syms); mrb_free(mrb, filenames); return result; } #ifndef MRB_NO_STDIO int mrb_dump_irep_binary(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, FILE* fp) { uint8_t *bin = NULL; if (fp == NULL) { return MRB_DUMP_INVALID_ARGUMENT; } size_t bin_size; int result = mrb_dump_irep(mrb, irep, flags, &bin, &bin_size); if (result == MRB_DUMP_OK) { if (fwrite(bin, sizeof(bin[0]), bin_size, fp) != bin_size) { result = MRB_DUMP_WRITE_FAULT; } } mrb_free(mrb, bin); return result; } int mrb_dump_irep_cfunc(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, FILE *fp, const char *initname) { uint8_t *bin = NULL; if (fp == NULL || initname == NULL || initname[0] == '\0') { return MRB_DUMP_INVALID_ARGUMENT; } size_t bin_size, bin_idx = 0; int result = mrb_dump_irep(mrb, irep, flags, &bin, &bin_size); if (result == MRB_DUMP_OK) { if (fprintf(fp, "#include \n") < 0) { /* for uint8_t under at least Darwin */ mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } if (fprintf(fp, "%s\n" "const uint8_t %s[] = {", (flags & MRB_DUMP_STATIC) ? "static" : "#ifdef __cplusplus\n" "extern\n" "#endif", initname) < 0) { mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } while (bin_idx < bin_size) { if (bin_idx % 16 == 0) { if (fputs("\n", fp) == EOF) { mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } } if (fprintf(fp, "0x%02x,", bin[bin_idx++]) < 0) { mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } } if (fputs("\n};\n", fp) == EOF) { mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } } mrb_free(mrb, bin); return result; } #endif /* MRB_NO_STDIO */ nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/cdump.c0000644000000000000000000000013215077107276017736 xustar0030 mtime=1761382078.132420511 30 atime=1761382080.151411274 30 ctime=1761382108.557301951 nghttp2-1.68.0/third-party/mruby/src/cdump.c0000644000175100017510000003315415077107276020334 0ustar00runnerrunner/* ** cdump.c - mruby binary dumper (in C) ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #ifndef MRB_NO_STDIO #ifndef MRB_NO_FLOAT #include #define MRB_FLOAT_FMT "%.17g" #endif static int cdump_pool(mrb_state *mrb, const mrb_irep_pool *p, FILE *fp) { if (p->tt & IREP_TT_NFLAG) { /* number */ switch (p->tt) { #ifdef MRB_64BIT case IREP_TT_INT64: if (p->u.i64 < INT32_MIN || INT32_MAX < p->u.i64) { fprintf(fp, "{IREP_TT_INT64, {.i64=%" PRId64 "}},\n", p->u.i64); } else { fprintf(fp, "{IREP_TT_INT32, {.i32=%" PRId32 "}},\n", (int32_t)p->u.i64); } break; #endif case IREP_TT_INT32: fprintf(fp, "{IREP_TT_INT32, {.i32=%" PRId32 "}},\n", p->u.i32); break; case IREP_TT_FLOAT: #ifndef MRB_NO_FLOAT fprintf(fp, "{IREP_TT_FLOAT, {.f=" MRB_FLOAT_FMT "}},\n", p->u.f); #endif break; case IREP_TT_BIGINT: { const char *s = p->u.str; int len = s[0]+2; fputs("{IREP_TT_BIGINT, {\"", fp); for (int i=0; itt>>2; const char *s = p->u.str; fprintf(fp, "{IREP_TT_STR|(%d<<2), {\"", len); for (i=0; i= 2 && name[len-1] == '=' && sym_name_word_p(name, len-1); } static mrb_bool sym_name_with_question_mark_p(const char *name, mrb_int len) { return len >= 2 && name[len-1] == '?' && sym_name_word_p(name, len-1); } static mrb_bool sym_name_with_bang_p(const char *name, mrb_int len) { return len >= 2 && name[len-1] == '!' && sym_name_word_p(name, len-1); } static mrb_bool sym_name_ivar_p(const char *name, mrb_int len) { return len >= 2 && name[0] == '@' && sym_name_word_p(name+1, len-1); } static mrb_bool sym_name_cvar_p(const char *name, mrb_int len) { return len >= 3 && name[0] == '@' && sym_name_ivar_p(name+1, len-1); } #define OPERATOR_SYMBOL(sym_name, name) {name, sym_name, sizeof(sym_name)-1} struct operator_symbol { const char *name; const char *sym_name; uint16_t sym_name_len; }; static const struct operator_symbol operator_table[] = { OPERATOR_SYMBOL("!", "not"), OPERATOR_SYMBOL("%", "mod"), OPERATOR_SYMBOL("&", "and"), OPERATOR_SYMBOL("*", "mul"), OPERATOR_SYMBOL("+", "add"), OPERATOR_SYMBOL("-", "sub"), OPERATOR_SYMBOL("/", "div"), OPERATOR_SYMBOL("<", "lt"), OPERATOR_SYMBOL(">", "gt"), OPERATOR_SYMBOL("^", "xor"), OPERATOR_SYMBOL("`", "tick"), OPERATOR_SYMBOL("|", "or"), OPERATOR_SYMBOL("~", "neg"), OPERATOR_SYMBOL("!=", "neq"), OPERATOR_SYMBOL("!~", "nmatch"), OPERATOR_SYMBOL("&&", "andand"), OPERATOR_SYMBOL("**", "pow"), OPERATOR_SYMBOL("+@", "plus"), OPERATOR_SYMBOL("-@", "minus"), OPERATOR_SYMBOL("<<", "lshift"), OPERATOR_SYMBOL("<=", "le"), OPERATOR_SYMBOL("==", "eq"), OPERATOR_SYMBOL("=~", "match"), OPERATOR_SYMBOL(">=", "ge"), OPERATOR_SYMBOL(">>", "rshift"), OPERATOR_SYMBOL("[]", "aref"), OPERATOR_SYMBOL("||", "oror"), OPERATOR_SYMBOL("<=>", "cmp"), OPERATOR_SYMBOL("===", "eqq"), OPERATOR_SYMBOL("[]=", "aset"), }; static const char* sym_operator_name(const char *sym_name, mrb_int len) { mrb_sym table_size = sizeof(operator_table)/sizeof(struct operator_symbol); if (operator_table[table_size-1].sym_name_len < len) return NULL; for (mrb_sym start = 0; table_size != 0; table_size/=2) { mrb_sym idx = start+table_size/2; const struct operator_symbol *op_sym = &operator_table[idx]; int cmp = (int)len-(int)op_sym->sym_name_len; if (cmp == 0) { cmp = memcmp(sym_name, op_sym->sym_name, len); if (cmp == 0) return op_sym->name; } if (0 < cmp) { start = ++idx; table_size--; } } return NULL; } static const char* sym_var_name(mrb_state *mrb, const char *initname, const char *key, int n) { char buf[32]; mrb_value s = mrb_str_new_cstr(mrb, initname); mrb_str_cat_lit(mrb, s, "_"); mrb_str_cat_cstr(mrb, s, key); mrb_str_cat_lit(mrb, s, "_"); snprintf(buf, sizeof(buf), "%d", n); mrb_str_cat_cstr(mrb, s, buf); return RSTRING_PTR(s); } static int cdump_sym(mrb_state *mrb, mrb_sym sym, const char *var_name, int idx, mrb_value init_syms_code, FILE *fp) { if (sym == 0) { fputs("0,", fp); return MRB_DUMP_OK; } mrb_int len; const char *name = mrb_sym_name_len(mrb, sym, &len), *op_name; if (!name) return MRB_DUMP_INVALID_ARGUMENT; if (sym_name_word_p(name, len)) { fprintf(fp, "MRB_SYM(%s)", name); } else if (sym_name_with_equal_p(name, len)) { fprintf(fp, "MRB_SYM_E(%.*s)", (int)(len-1), name); } else if (sym_name_with_question_mark_p(name, len)) { fprintf(fp, "MRB_SYM_Q(%.*s)", (int)(len-1), name); } else if (sym_name_with_bang_p(name, len)) { fprintf(fp, "MRB_SYM_B(%.*s)", (int)(len-1), name); } else if (sym_name_ivar_p(name, len)) { fprintf(fp, "MRB_IVSYM(%s)", name+1); } else if (sym_name_cvar_p(name, len)) { fprintf(fp, "MRB_CVSYM(%s)", name+2); } else if ((op_name = sym_operator_name(name, len))) { fprintf(fp, "MRB_OPSYM(%s)", op_name); } else { char buf[32]; mrb_value name_obj = mrb_str_new(mrb, name, len); mrb_str_cat_lit(mrb, init_syms_code, " "); mrb_str_cat_cstr(mrb, init_syms_code, var_name); snprintf(buf, sizeof(buf), "[%d] = ", idx); mrb_str_cat_cstr(mrb, init_syms_code, buf); mrb_str_cat_lit(mrb, init_syms_code, "mrb_intern_lit(mrb, "); mrb_str_cat_str(mrb, init_syms_code, mrb_str_dump(mrb, name_obj)); mrb_str_cat_lit(mrb, init_syms_code, ");\n"); fputs("0", fp); } fputs(", ", fp); return MRB_DUMP_OK; } static int cdump_syms(mrb_state *mrb, const char *name, const char *key, int n, int syms_len, const mrb_sym *syms, mrb_value init_syms_code, FILE *fp) { int ai = mrb_gc_arena_save(mrb); mrb_int code_len = RSTRING_LEN(init_syms_code); const char *var_name = sym_var_name(mrb, name, key, n); fprintf(fp, "mrb_DEFINE_SYMS_VAR(%s, %d, (", var_name, syms_len); for (int i=0; iflen != 1) { return 0; } return 1; } //Adds debug information to c-structs and //adds filenames in init_syms_code block static int cdump_debug(mrb_state *mrb, const char *name, int n, mrb_irep_debug_info *info, mrb_value init_syms_code, FILE *fp) { int ai = mrb_gc_arena_save(mrb); char buffer[256]; const char *line_type = "mrb_debug_line_ary"; if (!simple_debug_info(info)) return MRB_DUMP_INVALID_IREP; int len = info->files[0]->line_entry_count; const char *filename = mrb_sym_name_len(mrb, info->files[0]->filename_sym, NULL); snprintf(buffer, sizeof(buffer), " %s_debug_file_%d.filename_sym = mrb_intern_lit(mrb,", name, n); mrb_str_cat_cstr(mrb, init_syms_code, buffer); mrb_str_cat_str(mrb, init_syms_code, mrb_str_dump(mrb, mrb_str_new_cstr(mrb, filename))); mrb_str_cat_cstr(mrb, init_syms_code, ");\n"); switch (info->files[0]->line_type) { case mrb_debug_line_ary: fprintf(fp, "static uint16_t %s_debug_lines_%d[%d] = {", name, n, len); for (int i=0; ifiles[0]->lines.ary[i]); } fputs("};\n", fp); break; case mrb_debug_line_flat_map: line_type = "mrb_debug_line_flat_map"; fprintf(fp, "static struct mrb_irep_debug_info_line %s_debug_lines_%d[%d] = {", name, n, len); for (int i=0; ifiles[0]->lines.flat_map[i]; fprintf(fp, "\t{.start_pos=0x%04x,.line=%d},\n", fmap->start_pos, fmap->line); } fputs("};\n", fp); break; case mrb_debug_line_packed_map: line_type = "mrb_debug_line_packed_map"; fprintf(fp, "static const char %s_debug_lines_%d[] = \"", name, n); const uint8_t *pmap = info->files[0]->lines.packed_map; for (int i=0; ifiles[0]->start_pos, info->files[0]->filename_sym, info->files[0]->line_entry_count, line_type, name, n); fprintf(fp, "static mrb_irep_debug_info_file *%s_debug_file_%d_ = &%s_debug_file_%d;\n", name, n, name, n); fprintf(fp, "static mrb_irep_debug_info %s_debug_%d = {\n", name, n); fprintf(fp, "%d, %d, &%s_debug_file_%d_};\n", info->pc_count, info->flen, name, n); mrb_gc_arena_restore(mrb, ai); return MRB_DUMP_OK; } static int cdump_irep_struct(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, FILE *fp, const char *name, int n, mrb_value init_syms_code, int *mp) { int i, len; int max = *mp; int debug_available = 0; /* dump reps */ if (irep->reps) { for (i=0,len=irep->rlen; ireps[i], flags, fp, name, max+i, init_syms_code, mp) != MRB_DUMP_OK) return MRB_DUMP_INVALID_ARGUMENT; } fprintf(fp, "static const mrb_irep *%s_reps_%d[%d] = {\n", name, n, len); for (i=0,len=irep->rlen; ipool) { len=irep->plen; fprintf(fp, "static const mrb_irep_pool %s_pool_%d[%d] = {\n", name, n, len); for (i=0; ipool[i], fp) != MRB_DUMP_OK) return MRB_DUMP_INVALID_ARGUMENT; } fputs("};\n", fp); } /* dump syms */ if (irep->syms) { cdump_syms(mrb, name, "syms", n, irep->slen, irep->syms, init_syms_code, fp); } /* dump iseq */ len=irep->ilen+sizeof(struct mrb_irep_catch_handler)*irep->clen; fprintf(fp, "static const mrb_code %s_iseq_%d[%d] = {", name, n, len); for (i=0; iiseq[i]); } fputs("};\n", fp); /* dump lv */ if (irep->lv) { cdump_syms(mrb, name, "lv", n, irep->nlocals-1, irep->lv, init_syms_code, fp); } /* dump debug */ if (flags & MRB_DUMP_DEBUG_INFO) { if (cdump_debug(mrb, name, n, irep->debug_info, init_syms_code, fp) == MRB_DUMP_OK) { debug_available = 1; } } /* dump irep */ fprintf(fp, "static const mrb_irep %s_irep_%d = {\n", name, n); fprintf(fp, " %d,%d,%d,\n", irep->nlocals, irep->nregs, irep->clen); fprintf(fp, " MRB_IREP_STATIC,%s_iseq_%d,\n", name, n); if (irep->pool) { fprintf(fp, " %s_pool_%d,", name, n); } else { fputs( " NULL,", fp); } if (irep->syms) { fprintf(fp, "%s_syms_%d,", name, n); } else { fputs( "NULL,", fp); } if (irep->reps) { fprintf(fp, "%s_reps_%d,\n", name, n); } else { fputs( "NULL,\n", fp); } if (irep->lv) { fprintf(fp, " %s_lv_%d,\n", name, n); } else { fputs( " NULL,\t\t\t\t\t/* lv */\n", fp); } if (debug_available) { fprintf(fp, " &%s_debug_%d,\n", name, n); } else { fputs(" NULL,\t\t\t\t\t/* debug_info */\n", fp); } fprintf(fp, " %d,%d,%d,%d,0\n};\n", irep->ilen, irep->plen, irep->slen, irep->rlen); return MRB_DUMP_OK; } int mrb_dump_irep_cstruct(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, FILE *fp, const char *initname) { if (fp == NULL || initname == NULL || initname[0] == '\0') { return MRB_DUMP_INVALID_ARGUMENT; } if (fprintf(fp, "#include \n" "#include \n" "#include \n" "#include \n" "#include \n" "\n") < 0) { return MRB_DUMP_WRITE_FAULT; } fputs("#define mrb_BRACED(...) {__VA_ARGS__}\n", fp); fputs("#define mrb_DEFINE_SYMS_VAR(name, len, syms, qualifier) \\\n", fp); fputs(" static qualifier mrb_sym name[len] = mrb_BRACED syms\n", fp); fputs("\n", fp); mrb_value init_syms_code = mrb_str_new_capa(mrb, 0); int max = 1; int n = cdump_irep_struct(mrb, irep, flags, fp, initname, 0, init_syms_code, &max); if (n != MRB_DUMP_OK) return n; fprintf(fp, "%s\n" "const struct RProc %s[] = {{\n", (flags & MRB_DUMP_STATIC) ? "static" : "#ifdef __cplusplus\n" "extern\n" "#endif", initname); fprintf(fp, "NULL,NULL,MRB_TT_PROC,MRB_GC_RED,MRB_OBJ_IS_FROZEN,0,{&%s_irep_0},NULL,{NULL},\n}};\n", initname); fputs("static void\n", fp); fprintf(fp, "%s_init_syms(mrb_state *mrb)\n", initname); fputs("{\n", fp); fputs(RSTRING_PTR(init_syms_code), fp); fputs("}\n", fp); return MRB_DUMP_OK; } #endif nghttp2-1.68.0/third-party/mruby/src/PaxHeaders/proc.c0000644000000000000000000000013215077107276017571 xustar0030 mtime=1761382078.135420497 30 atime=1761382080.158411242 30 ctime=1761382108.590301855 nghttp2-1.68.0/third-party/mruby/src/proc.c0000644000175100017510000003220415077107276020162 0ustar00runnerrunner/* ** proc.c - Proc class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include static const mrb_code call_iseq[] = { OP_CALL, }; static const mrb_irep call_irep = { 0, /* nlocals */ 2, /* nregs */ 0, /* clen */ MRB_ISEQ_NO_FREE | MRB_IREP_NO_FREE, /* flags */ call_iseq, /* iseq */ NULL, /* pool */ NULL, /* syms */ NULL, /* reps */ NULL, /* lv */ NULL, /* debug_info */ 1, /* ilen */ 0, /* plen */ 0, /* slen */ 1, /* rlen */ 0, /* refcnt */ }; mrb_alignas(8) static const struct RProc call_proc = { NULL, NULL, MRB_TT_PROC, MRB_GC_RED, MRB_OBJ_IS_FROZEN, MRB_PROC_SCOPE | MRB_PROC_STRICT, { &call_irep }, NULL, { NULL } }; struct RProc* mrb_proc_new(mrb_state *mrb, const mrb_irep *irep) { struct RProc *p; mrb_callinfo *ci = mrb->c->ci; p = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, mrb->proc_class); if (ci) { struct RClass *tc = NULL; if (ci->proc) { tc = MRB_PROC_TARGET_CLASS(ci->proc); } if (tc == NULL) { tc = mrb_vm_ci_target_class(ci); } p->upper = ci->proc; p->e.target_class = tc; } if (irep) { mrb_irep_incref(mrb, (mrb_irep*)irep); } p->body.irep = irep; return p; } struct REnv* mrb_env_new(mrb_state *mrb, struct mrb_context *c, mrb_callinfo *ci, int nstacks, mrb_value *stack, struct RClass *tc) { struct REnv *e; mrb_int bidx = 1; int n = ci->n; int nk = ci->nk; e = MRB_OBJ_ALLOC(mrb, MRB_TT_ENV, NULL); e->c = tc; MRB_ENV_SET_LEN(e, nstacks); bidx += (n == 15) ? 1 : n; bidx += (nk == 15) ? 1 : (2*nk); MRB_ENV_SET_BIDX(e, bidx); e->mid = ci->mid; e->stack = stack; e->cxt = c; MRB_ENV_COPY_FLAGS_FROM_CI(e, ci); return e; } static void closure_setup(mrb_state *mrb, struct RProc *p) { mrb_callinfo *ci = mrb->c->ci; const struct RProc *up = p->upper; struct REnv *e = NULL; mrb_assert(ci != NULL); if ((e = mrb_vm_ci_env(ci)) != NULL) { /* do nothing, because e is assigned already */ } else if (up) { struct RClass *tc = ci->u.target_class; if (MRB_PROC_ALIAS_P(up)) { /* alias */ up = up->upper; } e = mrb_env_new(mrb, mrb->c, ci, up->body.irep->nlocals, ci->stack, tc); ci->u.env = e; if (MRB_PROC_ENV_P(up) && MRB_PROC_ENV(up)->cxt == NULL) { e->mid = MRB_PROC_ENV(up)->mid; } } if (e) { p->e.env = e; p->flags |= MRB_PROC_ENVSET; mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e); } } struct RProc* mrb_closure_new(mrb_state *mrb, const mrb_irep *irep) { struct RProc *p = mrb_proc_new(mrb, irep); closure_setup(mrb, p); return p; } MRB_API struct RProc* mrb_proc_new_cfunc(mrb_state *mrb, mrb_func_t func) { struct RProc *p; p = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, mrb->proc_class); p->body.func = func; p->flags |= MRB_PROC_CFUNC_FL; p->upper = 0; p->e.target_class = 0; return p; } MRB_API struct RProc* mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t func, mrb_int argc, const mrb_value *argv) { struct RProc *p = mrb_proc_new_cfunc(mrb, func); struct REnv *e; int i; p->e.env = e = mrb_env_new(mrb, mrb->c, mrb->c->ci, 0, NULL, NULL); p->flags |= MRB_PROC_ENVSET; mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e); MRB_ENV_CLOSE(e); e->stack = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * argc); MRB_ENV_SET_LEN(e, argc); if (argv) { for (i = 0; i < argc; i++) { e->stack[i] = argv[i]; } } else { for (i = 0; i < argc; i++) { SET_NIL_VALUE(e->stack[i]); } } return p; } MRB_API struct RProc* mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals) { return mrb_proc_new_cfunc_with_env(mrb, func, nlocals, NULL); } MRB_API mrb_value mrb_proc_cfunc_env_get(mrb_state *mrb, mrb_int idx) { const struct RProc *p = mrb->c->ci->proc; struct REnv *e; if (!p || !MRB_PROC_CFUNC_P(p)) { mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from non-cfunc proc"); } e = MRB_PROC_ENV(p); if (!e) { mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from cfunc Proc without REnv"); } if (idx < 0 || MRB_ENV_LEN(e) <= idx) { mrb_raisef(mrb, E_INDEX_ERROR, "Env index out of range: %i (expected: 0 <= index < %i)", idx, MRB_ENV_LEN(e)); } return e->stack[idx]; } mrb_value mrb_proc_get_self(mrb_state *mrb, const struct RProc *p, struct RClass **target_class_p) { if (MRB_PROC_CFUNC_P(p)) { *target_class_p = mrb->object_class; return mrb_nil_value(); } else { struct REnv *e = p->e.env; if (!e || e->tt != MRB_TT_ENV) { *target_class_p = mrb->object_class; return mrb_top_self(mrb); } else if (MRB_ENV_LEN(e) < 1) { mrb_raise(mrb, E_ARGUMENT_ERROR, "self is lost (probably ran out of memory when the block became independent)"); } *target_class_p = e->c; return e->stack[0]; } } void mrb_proc_copy(mrb_state *mrb, struct RProc *a, const struct RProc *b) { if (a->body.irep) { /* already initialized proc */ return; } if (!MRB_PROC_CFUNC_P(b) && b->body.irep) { mrb_irep_incref(mrb, (mrb_irep*)b->body.irep); } a->flags = b->flags; a->body = b->body; a->upper = b->upper; a->e.env = b->e.env; /* a->e.target_class = a->e.target_class; */ } static mrb_value mrb_proc_s_new(mrb_state *mrb, mrb_value proc_class) { mrb_value blk; mrb_value proc; struct RProc *p; /* Calling Proc.new without a block is not implemented yet */ mrb_get_args(mrb, "&!", &blk); p = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, mrb_class_ptr(proc_class)); mrb_proc_copy(mrb, p, mrb_proc_ptr(blk)); proc = mrb_obj_value(p); mrb_funcall_with_block(mrb, proc, MRB_SYM(initialize), 0, NULL, proc); if (!MRB_PROC_STRICT_P(p) && mrb->c->ci > mrb->c->cibase && MRB_PROC_ENV(p) == mrb->c->ci[-1].u.env) { p->flags |= MRB_PROC_ORPHAN; } return proc; } static void check_proc(mrb_state *mrb, mrb_value proc) { if (!mrb_proc_p(proc)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "not a proc"); } } static mrb_value mrb_proc_init_copy(mrb_state *mrb, mrb_value self) { mrb_value proc = mrb_get_arg1(mrb); check_proc(mrb, proc); mrb_proc_copy(mrb, mrb_proc_ptr(self), mrb_proc_ptr(proc)); return self; } static mrb_value proc_arity(mrb_state *mrb, mrb_value self) { return mrb_int_value(mrb, mrb_proc_arity(mrb_proc_ptr(self))); } mrb_bool mrb_proc_eql(mrb_state *mrb, mrb_value self, mrb_value other) { if (mrb_type(self) != MRB_TT_PROC) return FALSE; if (mrb_type(other) != MRB_TT_PROC) return FALSE; const struct RProc *p1 = mrb_proc_ptr(self); const struct RProc *p2 = mrb_proc_ptr(other); if (MRB_PROC_CFUNC_P(p1)) { if (!MRB_PROC_CFUNC_P(p1)) return FALSE; if (p1->body.func != p2->body.func) return FALSE; } else if (MRB_PROC_CFUNC_P(p2)) return FALSE; else if (p1->body.irep != p2->body.irep) return FALSE; return TRUE; } static mrb_value proc_eql(mrb_state *mrb, mrb_value self) { return mrb_bool_value(mrb_proc_eql(mrb, self, mrb_get_arg1(mrb))); } static mrb_value proc_hash(mrb_state *mrb, mrb_value self) { const struct RProc *p = mrb_proc_ptr(self); return mrb_int_value(mrb, (mrb_int)(((intptr_t)p->body.irep)^MRB_TT_PROC)); } /* 15.3.1.2.6 */ /* 15.3.1.3.27 */ /* * call-seq: * lambda { |...| block } -> a_proc * * Equivalent to Proc.new, except the resulting Proc objects * check the number of parameters passed when called. */ static mrb_value proc_lambda(mrb_state *mrb, mrb_value self) { mrb_value blk; const struct RProc *p; mrb_get_args(mrb, "&", &blk); if (mrb_nil_p(blk)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block"); } check_proc(mrb, blk); p = mrb_proc_ptr(blk); if (!MRB_PROC_STRICT_P(p)) { struct RProc *p2 = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, p->c); mrb_proc_copy(mrb, p2, p); p2->flags |= MRB_PROC_STRICT; return mrb_obj_value(p2); } return blk; } mrb_int mrb_proc_arity(const struct RProc *p) { const mrb_irep *irep; const mrb_code *pc; mrb_aspec aspec; int ma, op, ra, pa, arity; if (MRB_PROC_CFUNC_P(p)) { /* TODO cfunc aspec not implemented yet */ return -1; } irep = p->body.irep; if (!irep) { return 0; } pc = irep->iseq; /* arity is depend on OP_ENTER */ if (*pc != OP_ENTER) { return 0; } aspec = PEEK_W(pc+1); ma = MRB_ASPEC_REQ(aspec); op = MRB_ASPEC_OPT(aspec); ra = MRB_ASPEC_REST(aspec); pa = MRB_ASPEC_POST(aspec); arity = ra || (MRB_PROC_STRICT_P(p) && op) ? -(ma + pa + 1) : ma + pa; return arity; } mrb_value mrb_proc_local_variables(mrb_state *mrb, const struct RProc *proc) { const mrb_irep *irep; mrb_value vars; size_t i; if (proc == NULL || MRB_PROC_CFUNC_P(proc)) { return mrb_ary_new(mrb); } vars = mrb_hash_new(mrb); while (proc) { if (MRB_PROC_CFUNC_P(proc)) break; irep = proc->body.irep; if (irep->lv) { for (i = 0; i + 1 < irep->nlocals; i++) { if (irep->lv[i]) { mrb_sym sym = irep->lv[i]; const char *name = mrb_sym_name(mrb, sym); switch (name[0]) { case '*': case '&': break; default: mrb_hash_set(mrb, vars, mrb_symbol_value(sym), mrb_true_value()); break; } } } } if (MRB_PROC_SCOPE_P(proc)) break; proc = proc->upper; } return mrb_hash_keys(mrb, vars); } const struct RProc * mrb_proc_get_caller(mrb_state *mrb, struct REnv **envp) { struct mrb_context *c = mrb->c; mrb_callinfo *ci = (c->ci > c->cibase) ? c->ci - 1 : c->cibase; const struct RProc *proc = ci->proc; if (!proc || MRB_PROC_CFUNC_P(proc)) { if (envp) *envp = NULL; } else { struct REnv *e = mrb_vm_ci_env(ci); if (e == NULL) { int nstacks = proc->body.irep->nlocals; e = mrb_env_new(mrb, c, ci, nstacks, ci->stack, mrb_vm_ci_target_class(ci)); ci->u.env = e; } if (envp) *envp = e; } return proc; } #define IREP_LVAR_MERGE_DEFAULT 50 #define IREP_LVAR_MERGE_MINIMUM 8 #define IREP_LVAR_MERGE_MAXIMUM 240 #ifdef MRB_IREP_LVAR_MERGE_LIMIT # define IREP_LVAR_MERGE_LIMIT \ ((MRB_IREP_LVAR_MERGE_LIMIT) < IREP_LVAR_MERGE_MINIMUM ? IREP_LVAR_MERGE_MINIMUM : \ (MRB_IREP_LVAR_MERGE_LIMIT) > IREP_LVAR_MERGE_MAXIMUM ? IREP_LVAR_MERGE_MAXIMUM : \ (MRB_IREP_LVAR_MERGE_LIMIT)) #else # define IREP_LVAR_MERGE_LIMIT IREP_LVAR_MERGE_DEFAULT #endif void mrb_proc_merge_lvar(mrb_state *mrb, mrb_irep *irep, struct REnv *env, int num, const mrb_sym *lv, const mrb_value *stack) { mrb_assert(!(irep->flags & MRB_IREP_NO_FREE)); if ((irep->nlocals + num) > IREP_LVAR_MERGE_LIMIT) { mrb_raise(mrb, E_RUNTIME_ERROR, "too many local variables for binding (mruby limitation)"); } if (!lv) { mrb_raise(mrb, E_RUNTIME_ERROR, "unavailable local variable names"); } irep->lv = (mrb_sym*)mrb_realloc(mrb, (mrb_sym*)irep->lv, sizeof(mrb_sym) * (irep->nlocals - 1 /* self */ + num)); env->stack = (mrb_value*)mrb_realloc(mrb, env->stack, sizeof(mrb_value) * (irep->nlocals + num)); mrb_sym *destlv = (mrb_sym*)irep->lv + irep->nlocals - 1 /* self */; mrb_value *destst = env->stack + irep->nlocals; memmove(destlv, lv, sizeof(mrb_sym) * num); if (stack) { memmove(destst, stack, sizeof(mrb_value) * num); for (int i = 0; i < num; i++) { if (!mrb_immediate_p(stack[i])) { mrb_field_write_barrier(mrb, (struct RBasic*)env, (struct RBasic*)mrb_obj_ptr(stack[i])); } } } else { for (int i = num; i > 0; i--, destst++) { *destst = mrb_nil_value(); } } irep->nlocals += num; irep->nregs = irep->nlocals; MRB_ENV_SET_LEN(env, irep->nlocals); } void mrb_init_proc(mrb_state *mrb) { mrb_method_t m; struct RClass *pc = mrb->proc_class = mrb_define_class_id(mrb, MRB_SYM(Proc), mrb->object_class); /* 15.2.17 */ MRB_SET_INSTANCE_TT(pc, MRB_TT_PROC); MRB_UNDEF_ALLOCATOR(pc); mrb_define_class_method_id(mrb, pc, MRB_SYM(new), mrb_proc_s_new, MRB_ARGS_NONE()|MRB_ARGS_BLOCK()); mrb_define_method_id(mrb, pc, MRB_SYM(initialize_copy), mrb_proc_init_copy, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, pc, MRB_SYM(arity), proc_arity, MRB_ARGS_NONE()); /* 15.2.17.4.2 */ mrb_define_method_id(mrb, pc, MRB_OPSYM(eq), proc_eql, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, pc, MRB_SYM_Q(eql), proc_eql, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, pc, MRB_SYM(hash), proc_hash, MRB_ARGS_NONE()); /* 15.2.17.4.2 */ MRB_METHOD_FROM_PROC(m, &call_proc); mrb_define_method_raw(mrb, pc, MRB_SYM(call), m); /* 15.2.17.4.3 */ mrb_define_method_raw(mrb, pc, MRB_OPSYM(aref), m); /* 15.2.17.4.1 */ mrb_define_private_method_id(mrb, mrb->kernel_module, MRB_SYM(lambda), proc_lambda, MRB_ARGS_NONE()|MRB_ARGS_BLOCK()); /* 15.3.1.3.27 */ } nghttp2-1.68.0/third-party/mruby/PaxHeaders/Makefile0000644000000000000000000000013115077107276017332 xustar0029 mtime=1761382078.09542068 30 atime=1761382080.118411425 30 ctime=1761382108.356302532 nghttp2-1.68.0/third-party/mruby/Makefile0000644000175100017510000000215215077107276017723 0ustar00runnerrunner# mruby is using Rake (https://ruby.github.io/rake/) as a build tool. RAKE = rake DOCKER_COMPOSE = docker-compose PRE_COMMIT = pre-commit define check_command @command -v $(1) >/dev/null 2>&1 || { \ echo "Error: $(1) is not installed or not in PATH."; \ exit 1; \ } endef all : check_rake $(RAKE) .PHONY : all test : check_rake all $(RAKE) test .PHONY : test clean : check_rake $(RAKE) clean .PHONY : clean check : check_pre_commit $(PRE_COMMIT) run --all-files .PHONY : check checkinstall : check_pre_commit $(PRE_COMMIT) install .PHONY : checkinstall checkupdate : check_pre_commit $(PRE_COMMIT) autoupdate .PHONY : checkupdate composecheck : check_docker_compose check_pre_commit $(DOCKER_COMPOSE) -p mruby run test $(PRE_COMMIT) run --all-files .PHONY : composecheck composetest : check_docker_compose $(DOCKER_COMPOSE) -p mruby run test .PHONY : composetest check_rake: $(call check_command, $(RAKE)) .PHONY : check_rake check_docker_compose: $(call check_command, $(DOCKER_COMPOSE)) .PHONY : check_docker_compose check_pre_commit: $(call check_command, $(PRE_COMMIT)) .PHONY : check_pre_commit nghttp2-1.68.0/third-party/mruby/PaxHeaders/SECURITY.md0000644000000000000000000000013115077107276017463 xustar0029 mtime=1761382078.09542068 30 atime=1761382080.118411425 30 ctime=1761382108.944300832 nghttp2-1.68.0/third-party/mruby/SECURITY.md0000644000175100017510000000105215077107276020052 0ustar00runnerrunner# Security Policy ## Reporting a Vulnerability If you have any security concern, contact . ## Scope We consider the following issues as vulnerabilities: - Remote code execution - Crash caused by a valid Ruby script We _don't_ consider the following issues as vulnerabilities: - Runtime C undefined behavior (including integer overflow) - Crash caused by misused API - Crash caused by modified compiled binary - ASAN/Valgrind warning for too big memory allocation mruby assumes `malloc(3)` returns `NULL` for too big allocations nghttp2-1.68.0/third-party/PaxHeaders/llhttp0000644000000000000000000000013215077107334015762 xustar0030 mtime=1761382108.200302983 30 atime=1761382109.799298361 30 ctime=1761382108.200302983 nghttp2-1.68.0/third-party/llhttp/0000755000175100017510000000000015077107334016427 5ustar00runnerrunnernghttp2-1.68.0/third-party/llhttp/PaxHeaders/include0000644000000000000000000000013215077107334017405 xustar0030 mtime=1761382108.217302934 30 atime=1761382109.799298361 30 ctime=1761382108.217302934 nghttp2-1.68.0/third-party/llhttp/include/0000755000175100017510000000000015077107334020052 5ustar00runnerrunnernghttp2-1.68.0/third-party/llhttp/include/PaxHeaders/llhttp.h0000644000000000000000000000013215077107271021142 xustar0030 mtime=1761382073.009444065 30 atime=1761382080.109411467 30 ctime=1761382108.217302934 nghttp2-1.68.0/third-party/llhttp/include/llhttp.h0000644000175100017510000007367215077107271021551 0ustar00runnerrunner #ifndef INCLUDE_LLHTTP_H_ #define INCLUDE_LLHTTP_H_ #define LLHTTP_VERSION_MAJOR 9 #define LLHTTP_VERSION_MINOR 3 #define LLHTTP_VERSION_PATCH 0 #ifndef INCLUDE_LLHTTP_ITSELF_H_ #define INCLUDE_LLHTTP_ITSELF_H_ #ifdef __cplusplus extern "C" { #endif #include typedef struct llhttp__internal_s llhttp__internal_t; struct llhttp__internal_s { int32_t _index; void* _span_pos0; void* _span_cb0; int32_t error; const char* reason; const char* error_pos; void* data; void* _current; uint64_t content_length; uint8_t type; uint8_t method; uint8_t http_major; uint8_t http_minor; uint8_t header_state; uint16_t lenient_flags; uint8_t upgrade; uint8_t finish; uint16_t flags; uint16_t status_code; uint8_t initial_message_completed; void* settings; }; int llhttp__internal_init(llhttp__internal_t* s); int llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* INCLUDE_LLHTTP_ITSELF_H_ */ #ifndef LLLLHTTP_C_HEADERS_ #define LLLLHTTP_C_HEADERS_ #ifdef __cplusplus extern "C" { #endif enum llhttp_errno { HPE_OK = 0, HPE_INTERNAL = 1, HPE_STRICT = 2, HPE_CR_EXPECTED = 25, HPE_LF_EXPECTED = 3, HPE_UNEXPECTED_CONTENT_LENGTH = 4, HPE_UNEXPECTED_SPACE = 30, HPE_CLOSED_CONNECTION = 5, HPE_INVALID_METHOD = 6, HPE_INVALID_URL = 7, HPE_INVALID_CONSTANT = 8, HPE_INVALID_VERSION = 9, HPE_INVALID_HEADER_TOKEN = 10, HPE_INVALID_CONTENT_LENGTH = 11, HPE_INVALID_CHUNK_SIZE = 12, HPE_INVALID_STATUS = 13, HPE_INVALID_EOF_STATE = 14, HPE_INVALID_TRANSFER_ENCODING = 15, HPE_CB_MESSAGE_BEGIN = 16, HPE_CB_HEADERS_COMPLETE = 17, HPE_CB_MESSAGE_COMPLETE = 18, HPE_CB_CHUNK_HEADER = 19, HPE_CB_CHUNK_COMPLETE = 20, HPE_PAUSED = 21, HPE_PAUSED_UPGRADE = 22, HPE_PAUSED_H2_UPGRADE = 23, HPE_USER = 24, HPE_CB_URL_COMPLETE = 26, HPE_CB_STATUS_COMPLETE = 27, HPE_CB_METHOD_COMPLETE = 32, HPE_CB_VERSION_COMPLETE = 33, HPE_CB_HEADER_FIELD_COMPLETE = 28, HPE_CB_HEADER_VALUE_COMPLETE = 29, HPE_CB_CHUNK_EXTENSION_NAME_COMPLETE = 34, HPE_CB_CHUNK_EXTENSION_VALUE_COMPLETE = 35, HPE_CB_RESET = 31, HPE_CB_PROTOCOL_COMPLETE = 38 }; typedef enum llhttp_errno llhttp_errno_t; enum llhttp_flags { F_CONNECTION_KEEP_ALIVE = 0x1, F_CONNECTION_CLOSE = 0x2, F_CONNECTION_UPGRADE = 0x4, F_CHUNKED = 0x8, F_UPGRADE = 0x10, F_CONTENT_LENGTH = 0x20, F_SKIPBODY = 0x40, F_TRAILING = 0x80, F_TRANSFER_ENCODING = 0x200 }; typedef enum llhttp_flags llhttp_flags_t; enum llhttp_lenient_flags { LENIENT_HEADERS = 0x1, LENIENT_CHUNKED_LENGTH = 0x2, LENIENT_KEEP_ALIVE = 0x4, LENIENT_TRANSFER_ENCODING = 0x8, LENIENT_VERSION = 0x10, LENIENT_DATA_AFTER_CLOSE = 0x20, LENIENT_OPTIONAL_LF_AFTER_CR = 0x40, LENIENT_OPTIONAL_CRLF_AFTER_CHUNK = 0x80, LENIENT_OPTIONAL_CR_BEFORE_LF = 0x100, LENIENT_SPACES_AFTER_CHUNK_SIZE = 0x200 }; typedef enum llhttp_lenient_flags llhttp_lenient_flags_t; enum llhttp_type { HTTP_BOTH = 0, HTTP_REQUEST = 1, HTTP_RESPONSE = 2 }; typedef enum llhttp_type llhttp_type_t; enum llhttp_finish { HTTP_FINISH_SAFE = 0, HTTP_FINISH_SAFE_WITH_CB = 1, HTTP_FINISH_UNSAFE = 2 }; typedef enum llhttp_finish llhttp_finish_t; enum llhttp_method { HTTP_DELETE = 0, HTTP_GET = 1, HTTP_HEAD = 2, HTTP_POST = 3, HTTP_PUT = 4, HTTP_CONNECT = 5, HTTP_OPTIONS = 6, HTTP_TRACE = 7, HTTP_COPY = 8, HTTP_LOCK = 9, HTTP_MKCOL = 10, HTTP_MOVE = 11, HTTP_PROPFIND = 12, HTTP_PROPPATCH = 13, HTTP_SEARCH = 14, HTTP_UNLOCK = 15, HTTP_BIND = 16, HTTP_REBIND = 17, HTTP_UNBIND = 18, HTTP_ACL = 19, HTTP_REPORT = 20, HTTP_MKACTIVITY = 21, HTTP_CHECKOUT = 22, HTTP_MERGE = 23, HTTP_MSEARCH = 24, HTTP_NOTIFY = 25, HTTP_SUBSCRIBE = 26, HTTP_UNSUBSCRIBE = 27, HTTP_PATCH = 28, HTTP_PURGE = 29, HTTP_MKCALENDAR = 30, HTTP_LINK = 31, HTTP_UNLINK = 32, HTTP_SOURCE = 33, HTTP_PRI = 34, HTTP_DESCRIBE = 35, HTTP_ANNOUNCE = 36, HTTP_SETUP = 37, HTTP_PLAY = 38, HTTP_PAUSE = 39, HTTP_TEARDOWN = 40, HTTP_GET_PARAMETER = 41, HTTP_SET_PARAMETER = 42, HTTP_REDIRECT = 43, HTTP_RECORD = 44, HTTP_FLUSH = 45, HTTP_QUERY = 46 }; typedef enum llhttp_method llhttp_method_t; enum llhttp_status { HTTP_STATUS_CONTINUE = 100, HTTP_STATUS_SWITCHING_PROTOCOLS = 101, HTTP_STATUS_PROCESSING = 102, HTTP_STATUS_EARLY_HINTS = 103, HTTP_STATUS_RESPONSE_IS_STALE = 110, HTTP_STATUS_REVALIDATION_FAILED = 111, HTTP_STATUS_DISCONNECTED_OPERATION = 112, HTTP_STATUS_HEURISTIC_EXPIRATION = 113, HTTP_STATUS_MISCELLANEOUS_WARNING = 199, HTTP_STATUS_OK = 200, HTTP_STATUS_CREATED = 201, HTTP_STATUS_ACCEPTED = 202, HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203, HTTP_STATUS_NO_CONTENT = 204, HTTP_STATUS_RESET_CONTENT = 205, HTTP_STATUS_PARTIAL_CONTENT = 206, HTTP_STATUS_MULTI_STATUS = 207, HTTP_STATUS_ALREADY_REPORTED = 208, HTTP_STATUS_TRANSFORMATION_APPLIED = 214, HTTP_STATUS_IM_USED = 226, HTTP_STATUS_MISCELLANEOUS_PERSISTENT_WARNING = 299, HTTP_STATUS_MULTIPLE_CHOICES = 300, HTTP_STATUS_MOVED_PERMANENTLY = 301, HTTP_STATUS_FOUND = 302, HTTP_STATUS_SEE_OTHER = 303, HTTP_STATUS_NOT_MODIFIED = 304, HTTP_STATUS_USE_PROXY = 305, HTTP_STATUS_SWITCH_PROXY = 306, HTTP_STATUS_TEMPORARY_REDIRECT = 307, HTTP_STATUS_PERMANENT_REDIRECT = 308, HTTP_STATUS_BAD_REQUEST = 400, HTTP_STATUS_UNAUTHORIZED = 401, HTTP_STATUS_PAYMENT_REQUIRED = 402, HTTP_STATUS_FORBIDDEN = 403, HTTP_STATUS_NOT_FOUND = 404, HTTP_STATUS_METHOD_NOT_ALLOWED = 405, HTTP_STATUS_NOT_ACCEPTABLE = 406, HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407, HTTP_STATUS_REQUEST_TIMEOUT = 408, HTTP_STATUS_CONFLICT = 409, HTTP_STATUS_GONE = 410, HTTP_STATUS_LENGTH_REQUIRED = 411, HTTP_STATUS_PRECONDITION_FAILED = 412, HTTP_STATUS_PAYLOAD_TOO_LARGE = 413, HTTP_STATUS_URI_TOO_LONG = 414, HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415, HTTP_STATUS_RANGE_NOT_SATISFIABLE = 416, HTTP_STATUS_EXPECTATION_FAILED = 417, HTTP_STATUS_IM_A_TEAPOT = 418, HTTP_STATUS_PAGE_EXPIRED = 419, HTTP_STATUS_ENHANCE_YOUR_CALM = 420, HTTP_STATUS_MISDIRECTED_REQUEST = 421, HTTP_STATUS_UNPROCESSABLE_ENTITY = 422, HTTP_STATUS_LOCKED = 423, HTTP_STATUS_FAILED_DEPENDENCY = 424, HTTP_STATUS_TOO_EARLY = 425, HTTP_STATUS_UPGRADE_REQUIRED = 426, HTTP_STATUS_PRECONDITION_REQUIRED = 428, HTTP_STATUS_TOO_MANY_REQUESTS = 429, HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL = 430, HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431, HTTP_STATUS_LOGIN_TIMEOUT = 440, HTTP_STATUS_NO_RESPONSE = 444, HTTP_STATUS_RETRY_WITH = 449, HTTP_STATUS_BLOCKED_BY_PARENTAL_CONTROL = 450, HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451, HTTP_STATUS_CLIENT_CLOSED_LOAD_BALANCED_REQUEST = 460, HTTP_STATUS_INVALID_X_FORWARDED_FOR = 463, HTTP_STATUS_REQUEST_HEADER_TOO_LARGE = 494, HTTP_STATUS_SSL_CERTIFICATE_ERROR = 495, HTTP_STATUS_SSL_CERTIFICATE_REQUIRED = 496, HTTP_STATUS_HTTP_REQUEST_SENT_TO_HTTPS_PORT = 497, HTTP_STATUS_INVALID_TOKEN = 498, HTTP_STATUS_CLIENT_CLOSED_REQUEST = 499, HTTP_STATUS_INTERNAL_SERVER_ERROR = 500, HTTP_STATUS_NOT_IMPLEMENTED = 501, HTTP_STATUS_BAD_GATEWAY = 502, HTTP_STATUS_SERVICE_UNAVAILABLE = 503, HTTP_STATUS_GATEWAY_TIMEOUT = 504, HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED = 505, HTTP_STATUS_VARIANT_ALSO_NEGOTIATES = 506, HTTP_STATUS_INSUFFICIENT_STORAGE = 507, HTTP_STATUS_LOOP_DETECTED = 508, HTTP_STATUS_BANDWIDTH_LIMIT_EXCEEDED = 509, HTTP_STATUS_NOT_EXTENDED = 510, HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511, HTTP_STATUS_WEB_SERVER_UNKNOWN_ERROR = 520, HTTP_STATUS_WEB_SERVER_IS_DOWN = 521, HTTP_STATUS_CONNECTION_TIMEOUT = 522, HTTP_STATUS_ORIGIN_IS_UNREACHABLE = 523, HTTP_STATUS_TIMEOUT_OCCURED = 524, HTTP_STATUS_SSL_HANDSHAKE_FAILED = 525, HTTP_STATUS_INVALID_SSL_CERTIFICATE = 526, HTTP_STATUS_RAILGUN_ERROR = 527, HTTP_STATUS_SITE_IS_OVERLOADED = 529, HTTP_STATUS_SITE_IS_FROZEN = 530, HTTP_STATUS_IDENTITY_PROVIDER_AUTHENTICATION_ERROR = 561, HTTP_STATUS_NETWORK_READ_TIMEOUT = 598, HTTP_STATUS_NETWORK_CONNECT_TIMEOUT = 599 }; typedef enum llhttp_status llhttp_status_t; #define HTTP_ERRNO_MAP(XX) \ XX(0, OK, OK) \ XX(1, INTERNAL, INTERNAL) \ XX(2, STRICT, STRICT) \ XX(25, CR_EXPECTED, CR_EXPECTED) \ XX(3, LF_EXPECTED, LF_EXPECTED) \ XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \ XX(30, UNEXPECTED_SPACE, UNEXPECTED_SPACE) \ XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \ XX(6, INVALID_METHOD, INVALID_METHOD) \ XX(7, INVALID_URL, INVALID_URL) \ XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \ XX(9, INVALID_VERSION, INVALID_VERSION) \ XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \ XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \ XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \ XX(13, INVALID_STATUS, INVALID_STATUS) \ XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \ XX(15, INVALID_TRANSFER_ENCODING, INVALID_TRANSFER_ENCODING) \ XX(16, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \ XX(17, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \ XX(18, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \ XX(19, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \ XX(20, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \ XX(21, PAUSED, PAUSED) \ XX(22, PAUSED_UPGRADE, PAUSED_UPGRADE) \ XX(23, PAUSED_H2_UPGRADE, PAUSED_H2_UPGRADE) \ XX(24, USER, USER) \ XX(26, CB_URL_COMPLETE, CB_URL_COMPLETE) \ XX(27, CB_STATUS_COMPLETE, CB_STATUS_COMPLETE) \ XX(32, CB_METHOD_COMPLETE, CB_METHOD_COMPLETE) \ XX(33, CB_VERSION_COMPLETE, CB_VERSION_COMPLETE) \ XX(28, CB_HEADER_FIELD_COMPLETE, CB_HEADER_FIELD_COMPLETE) \ XX(29, CB_HEADER_VALUE_COMPLETE, CB_HEADER_VALUE_COMPLETE) \ XX(34, CB_CHUNK_EXTENSION_NAME_COMPLETE, CB_CHUNK_EXTENSION_NAME_COMPLETE) \ XX(35, CB_CHUNK_EXTENSION_VALUE_COMPLETE, CB_CHUNK_EXTENSION_VALUE_COMPLETE) \ XX(31, CB_RESET, CB_RESET) \ XX(38, CB_PROTOCOL_COMPLETE, CB_PROTOCOL_COMPLETE) \ #define HTTP_METHOD_MAP(XX) \ XX(0, DELETE, DELETE) \ XX(1, GET, GET) \ XX(2, HEAD, HEAD) \ XX(3, POST, POST) \ XX(4, PUT, PUT) \ XX(5, CONNECT, CONNECT) \ XX(6, OPTIONS, OPTIONS) \ XX(7, TRACE, TRACE) \ XX(8, COPY, COPY) \ XX(9, LOCK, LOCK) \ XX(10, MKCOL, MKCOL) \ XX(11, MOVE, MOVE) \ XX(12, PROPFIND, PROPFIND) \ XX(13, PROPPATCH, PROPPATCH) \ XX(14, SEARCH, SEARCH) \ XX(15, UNLOCK, UNLOCK) \ XX(16, BIND, BIND) \ XX(17, REBIND, REBIND) \ XX(18, UNBIND, UNBIND) \ XX(19, ACL, ACL) \ XX(20, REPORT, REPORT) \ XX(21, MKACTIVITY, MKACTIVITY) \ XX(22, CHECKOUT, CHECKOUT) \ XX(23, MERGE, MERGE) \ XX(24, MSEARCH, M-SEARCH) \ XX(25, NOTIFY, NOTIFY) \ XX(26, SUBSCRIBE, SUBSCRIBE) \ XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \ XX(28, PATCH, PATCH) \ XX(29, PURGE, PURGE) \ XX(30, MKCALENDAR, MKCALENDAR) \ XX(31, LINK, LINK) \ XX(32, UNLINK, UNLINK) \ XX(33, SOURCE, SOURCE) \ XX(46, QUERY, QUERY) \ #define RTSP_METHOD_MAP(XX) \ XX(1, GET, GET) \ XX(3, POST, POST) \ XX(6, OPTIONS, OPTIONS) \ XX(35, DESCRIBE, DESCRIBE) \ XX(36, ANNOUNCE, ANNOUNCE) \ XX(37, SETUP, SETUP) \ XX(38, PLAY, PLAY) \ XX(39, PAUSE, PAUSE) \ XX(40, TEARDOWN, TEARDOWN) \ XX(41, GET_PARAMETER, GET_PARAMETER) \ XX(42, SET_PARAMETER, SET_PARAMETER) \ XX(43, REDIRECT, REDIRECT) \ XX(44, RECORD, RECORD) \ XX(45, FLUSH, FLUSH) \ #define HTTP_ALL_METHOD_MAP(XX) \ XX(0, DELETE, DELETE) \ XX(1, GET, GET) \ XX(2, HEAD, HEAD) \ XX(3, POST, POST) \ XX(4, PUT, PUT) \ XX(5, CONNECT, CONNECT) \ XX(6, OPTIONS, OPTIONS) \ XX(7, TRACE, TRACE) \ XX(8, COPY, COPY) \ XX(9, LOCK, LOCK) \ XX(10, MKCOL, MKCOL) \ XX(11, MOVE, MOVE) \ XX(12, PROPFIND, PROPFIND) \ XX(13, PROPPATCH, PROPPATCH) \ XX(14, SEARCH, SEARCH) \ XX(15, UNLOCK, UNLOCK) \ XX(16, BIND, BIND) \ XX(17, REBIND, REBIND) \ XX(18, UNBIND, UNBIND) \ XX(19, ACL, ACL) \ XX(20, REPORT, REPORT) \ XX(21, MKACTIVITY, MKACTIVITY) \ XX(22, CHECKOUT, CHECKOUT) \ XX(23, MERGE, MERGE) \ XX(24, MSEARCH, M-SEARCH) \ XX(25, NOTIFY, NOTIFY) \ XX(26, SUBSCRIBE, SUBSCRIBE) \ XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \ XX(28, PATCH, PATCH) \ XX(29, PURGE, PURGE) \ XX(30, MKCALENDAR, MKCALENDAR) \ XX(31, LINK, LINK) \ XX(32, UNLINK, UNLINK) \ XX(33, SOURCE, SOURCE) \ XX(34, PRI, PRI) \ XX(35, DESCRIBE, DESCRIBE) \ XX(36, ANNOUNCE, ANNOUNCE) \ XX(37, SETUP, SETUP) \ XX(38, PLAY, PLAY) \ XX(39, PAUSE, PAUSE) \ XX(40, TEARDOWN, TEARDOWN) \ XX(41, GET_PARAMETER, GET_PARAMETER) \ XX(42, SET_PARAMETER, SET_PARAMETER) \ XX(43, REDIRECT, REDIRECT) \ XX(44, RECORD, RECORD) \ XX(45, FLUSH, FLUSH) \ XX(46, QUERY, QUERY) \ #define HTTP_STATUS_MAP(XX) \ XX(100, CONTINUE, CONTINUE) \ XX(101, SWITCHING_PROTOCOLS, SWITCHING_PROTOCOLS) \ XX(102, PROCESSING, PROCESSING) \ XX(103, EARLY_HINTS, EARLY_HINTS) \ XX(110, RESPONSE_IS_STALE, RESPONSE_IS_STALE) \ XX(111, REVALIDATION_FAILED, REVALIDATION_FAILED) \ XX(112, DISCONNECTED_OPERATION, DISCONNECTED_OPERATION) \ XX(113, HEURISTIC_EXPIRATION, HEURISTIC_EXPIRATION) \ XX(199, MISCELLANEOUS_WARNING, MISCELLANEOUS_WARNING) \ XX(200, OK, OK) \ XX(201, CREATED, CREATED) \ XX(202, ACCEPTED, ACCEPTED) \ XX(203, NON_AUTHORITATIVE_INFORMATION, NON_AUTHORITATIVE_INFORMATION) \ XX(204, NO_CONTENT, NO_CONTENT) \ XX(205, RESET_CONTENT, RESET_CONTENT) \ XX(206, PARTIAL_CONTENT, PARTIAL_CONTENT) \ XX(207, MULTI_STATUS, MULTI_STATUS) \ XX(208, ALREADY_REPORTED, ALREADY_REPORTED) \ XX(214, TRANSFORMATION_APPLIED, TRANSFORMATION_APPLIED) \ XX(226, IM_USED, IM_USED) \ XX(299, MISCELLANEOUS_PERSISTENT_WARNING, MISCELLANEOUS_PERSISTENT_WARNING) \ XX(300, MULTIPLE_CHOICES, MULTIPLE_CHOICES) \ XX(301, MOVED_PERMANENTLY, MOVED_PERMANENTLY) \ XX(302, FOUND, FOUND) \ XX(303, SEE_OTHER, SEE_OTHER) \ XX(304, NOT_MODIFIED, NOT_MODIFIED) \ XX(305, USE_PROXY, USE_PROXY) \ XX(306, SWITCH_PROXY, SWITCH_PROXY) \ XX(307, TEMPORARY_REDIRECT, TEMPORARY_REDIRECT) \ XX(308, PERMANENT_REDIRECT, PERMANENT_REDIRECT) \ XX(400, BAD_REQUEST, BAD_REQUEST) \ XX(401, UNAUTHORIZED, UNAUTHORIZED) \ XX(402, PAYMENT_REQUIRED, PAYMENT_REQUIRED) \ XX(403, FORBIDDEN, FORBIDDEN) \ XX(404, NOT_FOUND, NOT_FOUND) \ XX(405, METHOD_NOT_ALLOWED, METHOD_NOT_ALLOWED) \ XX(406, NOT_ACCEPTABLE, NOT_ACCEPTABLE) \ XX(407, PROXY_AUTHENTICATION_REQUIRED, PROXY_AUTHENTICATION_REQUIRED) \ XX(408, REQUEST_TIMEOUT, REQUEST_TIMEOUT) \ XX(409, CONFLICT, CONFLICT) \ XX(410, GONE, GONE) \ XX(411, LENGTH_REQUIRED, LENGTH_REQUIRED) \ XX(412, PRECONDITION_FAILED, PRECONDITION_FAILED) \ XX(413, PAYLOAD_TOO_LARGE, PAYLOAD_TOO_LARGE) \ XX(414, URI_TOO_LONG, URI_TOO_LONG) \ XX(415, UNSUPPORTED_MEDIA_TYPE, UNSUPPORTED_MEDIA_TYPE) \ XX(416, RANGE_NOT_SATISFIABLE, RANGE_NOT_SATISFIABLE) \ XX(417, EXPECTATION_FAILED, EXPECTATION_FAILED) \ XX(418, IM_A_TEAPOT, IM_A_TEAPOT) \ XX(419, PAGE_EXPIRED, PAGE_EXPIRED) \ XX(420, ENHANCE_YOUR_CALM, ENHANCE_YOUR_CALM) \ XX(421, MISDIRECTED_REQUEST, MISDIRECTED_REQUEST) \ XX(422, UNPROCESSABLE_ENTITY, UNPROCESSABLE_ENTITY) \ XX(423, LOCKED, LOCKED) \ XX(424, FAILED_DEPENDENCY, FAILED_DEPENDENCY) \ XX(425, TOO_EARLY, TOO_EARLY) \ XX(426, UPGRADE_REQUIRED, UPGRADE_REQUIRED) \ XX(428, PRECONDITION_REQUIRED, PRECONDITION_REQUIRED) \ XX(429, TOO_MANY_REQUESTS, TOO_MANY_REQUESTS) \ XX(430, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL) \ XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, REQUEST_HEADER_FIELDS_TOO_LARGE) \ XX(440, LOGIN_TIMEOUT, LOGIN_TIMEOUT) \ XX(444, NO_RESPONSE, NO_RESPONSE) \ XX(449, RETRY_WITH, RETRY_WITH) \ XX(450, BLOCKED_BY_PARENTAL_CONTROL, BLOCKED_BY_PARENTAL_CONTROL) \ XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, UNAVAILABLE_FOR_LEGAL_REASONS) \ XX(460, CLIENT_CLOSED_LOAD_BALANCED_REQUEST, CLIENT_CLOSED_LOAD_BALANCED_REQUEST) \ XX(463, INVALID_X_FORWARDED_FOR, INVALID_X_FORWARDED_FOR) \ XX(494, REQUEST_HEADER_TOO_LARGE, REQUEST_HEADER_TOO_LARGE) \ XX(495, SSL_CERTIFICATE_ERROR, SSL_CERTIFICATE_ERROR) \ XX(496, SSL_CERTIFICATE_REQUIRED, SSL_CERTIFICATE_REQUIRED) \ XX(497, HTTP_REQUEST_SENT_TO_HTTPS_PORT, HTTP_REQUEST_SENT_TO_HTTPS_PORT) \ XX(498, INVALID_TOKEN, INVALID_TOKEN) \ XX(499, CLIENT_CLOSED_REQUEST, CLIENT_CLOSED_REQUEST) \ XX(500, INTERNAL_SERVER_ERROR, INTERNAL_SERVER_ERROR) \ XX(501, NOT_IMPLEMENTED, NOT_IMPLEMENTED) \ XX(502, BAD_GATEWAY, BAD_GATEWAY) \ XX(503, SERVICE_UNAVAILABLE, SERVICE_UNAVAILABLE) \ XX(504, GATEWAY_TIMEOUT, GATEWAY_TIMEOUT) \ XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP_VERSION_NOT_SUPPORTED) \ XX(506, VARIANT_ALSO_NEGOTIATES, VARIANT_ALSO_NEGOTIATES) \ XX(507, INSUFFICIENT_STORAGE, INSUFFICIENT_STORAGE) \ XX(508, LOOP_DETECTED, LOOP_DETECTED) \ XX(509, BANDWIDTH_LIMIT_EXCEEDED, BANDWIDTH_LIMIT_EXCEEDED) \ XX(510, NOT_EXTENDED, NOT_EXTENDED) \ XX(511, NETWORK_AUTHENTICATION_REQUIRED, NETWORK_AUTHENTICATION_REQUIRED) \ XX(520, WEB_SERVER_UNKNOWN_ERROR, WEB_SERVER_UNKNOWN_ERROR) \ XX(521, WEB_SERVER_IS_DOWN, WEB_SERVER_IS_DOWN) \ XX(522, CONNECTION_TIMEOUT, CONNECTION_TIMEOUT) \ XX(523, ORIGIN_IS_UNREACHABLE, ORIGIN_IS_UNREACHABLE) \ XX(524, TIMEOUT_OCCURED, TIMEOUT_OCCURED) \ XX(525, SSL_HANDSHAKE_FAILED, SSL_HANDSHAKE_FAILED) \ XX(526, INVALID_SSL_CERTIFICATE, INVALID_SSL_CERTIFICATE) \ XX(527, RAILGUN_ERROR, RAILGUN_ERROR) \ XX(529, SITE_IS_OVERLOADED, SITE_IS_OVERLOADED) \ XX(530, SITE_IS_FROZEN, SITE_IS_FROZEN) \ XX(561, IDENTITY_PROVIDER_AUTHENTICATION_ERROR, IDENTITY_PROVIDER_AUTHENTICATION_ERROR) \ XX(598, NETWORK_READ_TIMEOUT, NETWORK_READ_TIMEOUT) \ XX(599, NETWORK_CONNECT_TIMEOUT, NETWORK_CONNECT_TIMEOUT) \ #ifdef __cplusplus } /* extern "C" */ #endif #endif /* LLLLHTTP_C_HEADERS_ */ #ifndef INCLUDE_LLHTTP_API_H_ #define INCLUDE_LLHTTP_API_H_ #ifdef __cplusplus extern "C" { #endif #include #if defined(__wasm__) #define LLHTTP_EXPORT __attribute__((visibility("default"))) #elif defined(_WIN32) #define LLHTTP_EXPORT __declspec(dllexport) #else #define LLHTTP_EXPORT #endif typedef llhttp__internal_t llhttp_t; typedef struct llhttp_settings_s llhttp_settings_t; typedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length); typedef int (*llhttp_cb)(llhttp_t*); struct llhttp_settings_s { /* Possible return values 0, -1, `HPE_PAUSED` */ llhttp_cb on_message_begin; /* Possible return values 0, -1, HPE_USER */ llhttp_data_cb on_protocol; llhttp_data_cb on_url; llhttp_data_cb on_status; llhttp_data_cb on_method; llhttp_data_cb on_version; llhttp_data_cb on_header_field; llhttp_data_cb on_header_value; llhttp_data_cb on_chunk_extension_name; llhttp_data_cb on_chunk_extension_value; /* Possible return values: * 0 - Proceed normally * 1 - Assume that request/response has no body, and proceed to parsing the * next message * 2 - Assume absence of body (as above) and make `llhttp_execute()` return * `HPE_PAUSED_UPGRADE` * -1 - Error * `HPE_PAUSED` */ llhttp_cb on_headers_complete; /* Possible return values 0, -1, HPE_USER */ llhttp_data_cb on_body; /* Possible return values 0, -1, `HPE_PAUSED` */ llhttp_cb on_message_complete; llhttp_cb on_protocol_complete; llhttp_cb on_url_complete; llhttp_cb on_status_complete; llhttp_cb on_method_complete; llhttp_cb on_version_complete; llhttp_cb on_header_field_complete; llhttp_cb on_header_value_complete; llhttp_cb on_chunk_extension_name_complete; llhttp_cb on_chunk_extension_value_complete; /* When on_chunk_header is called, the current chunk length is stored * in parser->content_length. * Possible return values 0, -1, `HPE_PAUSED` */ llhttp_cb on_chunk_header; llhttp_cb on_chunk_complete; llhttp_cb on_reset; }; /* Initialize the parser with specific type and user settings. * * NOTE: lifetime of `settings` has to be at least the same as the lifetime of * the `parser` here. In practice, `settings` has to be either a static * variable or be allocated with `malloc`, `new`, etc. */ LLHTTP_EXPORT void llhttp_init(llhttp_t* parser, llhttp_type_t type, const llhttp_settings_t* settings); LLHTTP_EXPORT llhttp_t* llhttp_alloc(llhttp_type_t type); LLHTTP_EXPORT void llhttp_free(llhttp_t* parser); LLHTTP_EXPORT uint8_t llhttp_get_type(llhttp_t* parser); LLHTTP_EXPORT uint8_t llhttp_get_http_major(llhttp_t* parser); LLHTTP_EXPORT uint8_t llhttp_get_http_minor(llhttp_t* parser); LLHTTP_EXPORT uint8_t llhttp_get_method(llhttp_t* parser); LLHTTP_EXPORT int llhttp_get_status_code(llhttp_t* parser); LLHTTP_EXPORT uint8_t llhttp_get_upgrade(llhttp_t* parser); /* Reset an already initialized parser back to the start state, preserving the * existing parser type, callback settings, user data, and lenient flags. */ LLHTTP_EXPORT void llhttp_reset(llhttp_t* parser); /* Initialize the settings object */ LLHTTP_EXPORT void llhttp_settings_init(llhttp_settings_t* settings); /* Parse full or partial request/response, invoking user callbacks along the * way. * * If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing * interrupts, and such errno is returned from `llhttp_execute()`. If * `HPE_PAUSED` was used as a errno, the execution can be resumed with * `llhttp_resume()` call. * * In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE` * is returned after fully parsing the request/response. If the user wishes to * continue parsing, they need to invoke `llhttp_resume_after_upgrade()`. * * NOTE: if this function ever returns a non-pause type error, it will continue * to return the same error upon each successive call up until `llhttp_init()` * is called. */ LLHTTP_EXPORT llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len); /* This method should be called when the other side has no further bytes to * send (e.g. shutdown of readable side of the TCP connection.) * * Requests without `Content-Length` and other messages might require treating * all incoming bytes as the part of the body, up to the last byte of the * connection. This method will invoke `on_message_complete()` callback if the * request was terminated safely. Otherwise a error code would be returned. */ LLHTTP_EXPORT llhttp_errno_t llhttp_finish(llhttp_t* parser); /* Returns `1` if the incoming message is parsed until the last byte, and has * to be completed by calling `llhttp_finish()` on EOF */ LLHTTP_EXPORT int llhttp_message_needs_eof(const llhttp_t* parser); /* Returns `1` if there might be any other messages following the last that was * successfully parsed. */ LLHTTP_EXPORT int llhttp_should_keep_alive(const llhttp_t* parser); /* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set * appropriate error reason. * * Important: do not call this from user callbacks! User callbacks must return * `HPE_PAUSED` if pausing is required. */ LLHTTP_EXPORT void llhttp_pause(llhttp_t* parser); /* Might be called to resume the execution after the pause in user's callback. * See `llhttp_execute()` above for details. * * Call this only if `llhttp_execute()` returns `HPE_PAUSED`. */ LLHTTP_EXPORT void llhttp_resume(llhttp_t* parser); /* Might be called to resume the execution after the pause in user's callback. * See `llhttp_execute()` above for details. * * Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE` */ LLHTTP_EXPORT void llhttp_resume_after_upgrade(llhttp_t* parser); /* Returns the latest return error */ LLHTTP_EXPORT llhttp_errno_t llhttp_get_errno(const llhttp_t* parser); /* Returns the verbal explanation of the latest returned error. * * Note: User callback should set error reason when returning the error. See * `llhttp_set_error_reason()` for details. */ LLHTTP_EXPORT const char* llhttp_get_error_reason(const llhttp_t* parser); /* Assign verbal description to the returned error. Must be called in user * callbacks right before returning the errno. * * Note: `HPE_USER` error code might be useful in user callbacks. */ LLHTTP_EXPORT void llhttp_set_error_reason(llhttp_t* parser, const char* reason); /* Returns the pointer to the last parsed byte before the returned error. The * pointer is relative to the `data` argument of `llhttp_execute()`. * * Note: this method might be useful for counting the number of parsed bytes. */ LLHTTP_EXPORT const char* llhttp_get_error_pos(const llhttp_t* parser); /* Returns textual name of error code */ LLHTTP_EXPORT const char* llhttp_errno_name(llhttp_errno_t err); /* Returns textual name of HTTP method */ LLHTTP_EXPORT const char* llhttp_method_name(llhttp_method_t method); /* Returns textual name of HTTP status */ LLHTTP_EXPORT const char* llhttp_status_name(llhttp_status_t status); /* Enables/disables lenient header value parsing (disabled by default). * * Lenient parsing disables header value token checks, extending llhttp's * protocol support to highly non-compliant clients/server. No * `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when * lenient parsing is "on". * * **Enabling this flag can pose a security issue since you will be exposed to * request smuggling attacks. USE WITH CAUTION!** */ LLHTTP_EXPORT void llhttp_set_lenient_headers(llhttp_t* parser, int enabled); /* Enables/disables lenient handling of conflicting `Transfer-Encoding` and * `Content-Length` headers (disabled by default). * * Normally `llhttp` would error when `Transfer-Encoding` is present in * conjunction with `Content-Length`. This error is important to prevent HTTP * request smuggling, but may be less desirable for small number of cases * involving legacy servers. * * **Enabling this flag can pose a security issue since you will be exposed to * request smuggling attacks. USE WITH CAUTION!** */ LLHTTP_EXPORT void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled); /* Enables/disables lenient handling of `Connection: close` and HTTP/1.0 * requests responses. * * Normally `llhttp` would error on (in strict mode) or discard (in loose mode) * the HTTP request/response after the request/response with `Connection: close` * and `Content-Length`. This is important to prevent cache poisoning attacks, * but might interact badly with outdated and insecure clients. With this flag * the extra request/response will be parsed normally. * * **Enabling this flag can pose a security issue since you will be exposed to * poisoning attacks. USE WITH CAUTION!** */ LLHTTP_EXPORT void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled); /* Enables/disables lenient handling of `Transfer-Encoding` header. * * Normally `llhttp` would error when a `Transfer-Encoding` has `chunked` value * and another value after it (either in a single header or in multiple * headers whose value are internally joined using `, `). * This is mandated by the spec to reliably determine request body size and thus * avoid request smuggling. * With this flag the extra value will be parsed normally. * * **Enabling this flag can pose a security issue since you will be exposed to * request smuggling attacks. USE WITH CAUTION!** */ LLHTTP_EXPORT void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled); /* Enables/disables lenient handling of HTTP version. * * Normally `llhttp` would error when the HTTP version in the request or status line * is not `0.9`, `1.0`, `1.1` or `2.0`. * With this flag the invalid value will be parsed normally. * * **Enabling this flag can pose a security issue since you will allow unsupported * HTTP versions. USE WITH CAUTION!** */ LLHTTP_EXPORT void llhttp_set_lenient_version(llhttp_t* parser, int enabled); /* Enables/disables lenient handling of additional data received after a message ends * and keep-alive is disabled. * * Normally `llhttp` would error when additional unexpected data is received if the message * contains the `Connection` header with `close` value. * With this flag the extra data will discarded without throwing an error. * * **Enabling this flag can pose a security issue since you will be exposed to * poisoning attacks. USE WITH CAUTION!** */ LLHTTP_EXPORT void llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled); /* Enables/disables lenient handling of incomplete CRLF sequences. * * Normally `llhttp` would error when a CR is not followed by LF when terminating the * request line, the status line, the headers or a chunk header. * With this flag only a CR is required to terminate such sections. * * **Enabling this flag can pose a security issue since you will be exposed to * request smuggling attacks. USE WITH CAUTION!** */ LLHTTP_EXPORT void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled); /* * Enables/disables lenient handling of line separators. * * Normally `llhttp` would error when a LF is not preceded by CR when terminating the * request line, the status line, the headers, a chunk header or a chunk data. * With this flag only a LF is required to terminate such sections. * * **Enabling this flag can pose a security issue since you will be exposed to * request smuggling attacks. USE WITH CAUTION!** */ LLHTTP_EXPORT void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled); /* Enables/disables lenient handling of chunks not separated via CRLF. * * Normally `llhttp` would error when after a chunk data a CRLF is missing before * starting a new chunk. * With this flag the new chunk can start immediately after the previous one. * * **Enabling this flag can pose a security issue since you will be exposed to * request smuggling attacks. USE WITH CAUTION!** */ LLHTTP_EXPORT void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled); /* Enables/disables lenient handling of spaces after chunk size. * * Normally `llhttp` would error when after a chunk size is followed by one or more * spaces are present instead of a CRLF or `;`. * With this flag this check is disabled. * * **Enabling this flag can pose a security issue since you will be exposed to * request smuggling attacks. USE WITH CAUTION!** */ LLHTTP_EXPORT void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* INCLUDE_LLHTTP_API_H_ */ #endif /* INCLUDE_LLHTTP_H_ */ nghttp2-1.68.0/third-party/llhttp/PaxHeaders/src0000644000000000000000000000013215077107334016551 xustar0030 mtime=1761382108.215302939 30 atime=1761382109.799298361 30 ctime=1761382108.215302939 nghttp2-1.68.0/third-party/llhttp/src/0000755000175100017510000000000015077107334017216 5ustar00runnerrunnernghttp2-1.68.0/third-party/llhttp/src/PaxHeaders/http.c0000644000000000000000000000013215077107271017751 xustar0030 mtime=1761382073.009444065 30 atime=1761382080.109411467 30 ctime=1761382108.214302942 nghttp2-1.68.0/third-party/llhttp/src/http.c0000644000175100017510000001213115077107271020337 0ustar00runnerrunner#include #ifndef LLHTTP__TEST # include "llhttp.h" #else # define llhttp_t llparse_t #endif /* */ int llhttp_message_needs_eof(const llhttp_t* parser); int llhttp_should_keep_alive(const llhttp_t* parser); int llhttp__before_headers_complete(llhttp_t* parser, const char* p, const char* endp) { /* Set this here so that on_headers_complete() callbacks can see it */ if ((parser->flags & F_UPGRADE) && (parser->flags & F_CONNECTION_UPGRADE)) { /* For responses, "Upgrade: foo" and "Connection: upgrade" are * mandatory only when it is a 101 Switching Protocols response, * otherwise it is purely informational, to announce support. */ parser->upgrade = (parser->type == HTTP_REQUEST || parser->status_code == 101); } else { parser->upgrade = (parser->method == HTTP_CONNECT); } return 0; } /* Return values: * 0 - No body, `restart`, message_complete * 1 - CONNECT request, `restart`, message_complete, and pause * 2 - chunk_size_start * 3 - body_identity * 4 - body_identity_eof * 5 - invalid transfer-encoding for request */ int llhttp__after_headers_complete(llhttp_t* parser, const char* p, const char* endp) { int hasBody; hasBody = parser->flags & F_CHUNKED || parser->content_length > 0; if ( (parser->upgrade && (parser->method == HTTP_CONNECT || (parser->flags & F_SKIPBODY) || !hasBody)) || /* See RFC 2616 section 4.4 - 1xx e.g. Continue */ (parser->type == HTTP_RESPONSE && parser->status_code == 101) ) { /* Exit, the rest of the message is in a different protocol. */ return 1; } if (parser->type == HTTP_RESPONSE && parser->status_code == 100) { /* No body, restart as the message is complete */ return 0; } /* See RFC 2616 section 4.4 */ if ( parser->flags & F_SKIPBODY || /* response to a HEAD request */ ( parser->type == HTTP_RESPONSE && ( parser->status_code == 102 || /* Processing */ parser->status_code == 103 || /* Early Hints */ parser->status_code == 204 || /* No Content */ parser->status_code == 304 /* Not Modified */ ) ) ) { return 0; } else if (parser->flags & F_CHUNKED) { /* chunked encoding - ignore Content-Length header, prepare for a chunk */ return 2; } else if (parser->flags & F_TRANSFER_ENCODING) { if (parser->type == HTTP_REQUEST && (parser->lenient_flags & LENIENT_CHUNKED_LENGTH) == 0 && (parser->lenient_flags & LENIENT_TRANSFER_ENCODING) == 0) { /* RFC 7230 3.3.3 */ /* If a Transfer-Encoding header field * is present in a request and the chunked transfer coding is not * the final encoding, the message body length cannot be determined * reliably; the server MUST respond with the 400 (Bad Request) * status code and then close the connection. */ return 5; } else { /* RFC 7230 3.3.3 */ /* If a Transfer-Encoding header field is present in a response and * the chunked transfer coding is not the final encoding, the * message body length is determined by reading the connection until * it is closed by the server. */ return 4; } } else { if (!(parser->flags & F_CONTENT_LENGTH)) { if (!llhttp_message_needs_eof(parser)) { /* Assume content-length 0 - read the next */ return 0; } else { /* Read body until EOF */ return 4; } } else if (parser->content_length == 0) { /* Content-Length header given but zero: Content-Length: 0\r\n */ return 0; } else { /* Content-Length header given and non-zero */ return 3; } } } int llhttp__after_message_complete(llhttp_t* parser, const char* p, const char* endp) { int should_keep_alive; should_keep_alive = llhttp_should_keep_alive(parser); parser->finish = HTTP_FINISH_SAFE; parser->flags = 0; /* NOTE: this is ignored in loose parsing mode */ return should_keep_alive; } int llhttp_message_needs_eof(const llhttp_t* parser) { if (parser->type == HTTP_REQUEST) { return 0; } /* See RFC 2616 section 4.4 */ if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ parser->status_code == 204 || /* No Content */ parser->status_code == 304 || /* Not Modified */ (parser->flags & F_SKIPBODY)) { /* response to a HEAD request */ return 0; } /* RFC 7230 3.3.3, see `llhttp__after_headers_complete` */ if ((parser->flags & F_TRANSFER_ENCODING) && (parser->flags & F_CHUNKED) == 0) { return 1; } if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) { return 0; } return 1; } int llhttp_should_keep_alive(const llhttp_t* parser) { if (parser->http_major > 0 && parser->http_minor > 0) { /* HTTP/1.1 */ if (parser->flags & F_CONNECTION_CLOSE) { return 0; } } else { /* HTTP/1.0 or earlier */ if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { return 0; } } return !llhttp_message_needs_eof(parser); } nghttp2-1.68.0/third-party/llhttp/src/PaxHeaders/api.c0000644000000000000000000000013215077107271017543 xustar0030 mtime=1761382073.009444065 30 atime=1761382080.109411467 30 ctime=1761382108.213302945 nghttp2-1.68.0/third-party/llhttp/src/api.c0000644000175100017510000003154015077107271020136 0ustar00runnerrunner#include #include #include #include "llhttp.h" #define CALLBACK_MAYBE(PARSER, NAME) \ do { \ const llhttp_settings_t* settings; \ settings = (const llhttp_settings_t*) (PARSER)->settings; \ if (settings == NULL || settings->NAME == NULL) { \ err = 0; \ break; \ } \ err = settings->NAME((PARSER)); \ } while (0) #define SPAN_CALLBACK_MAYBE(PARSER, NAME, START, LEN) \ do { \ const llhttp_settings_t* settings; \ settings = (const llhttp_settings_t*) (PARSER)->settings; \ if (settings == NULL || settings->NAME == NULL) { \ err = 0; \ break; \ } \ err = settings->NAME((PARSER), (START), (LEN)); \ if (err == -1) { \ err = HPE_USER; \ llhttp_set_error_reason((PARSER), "Span callback error in " #NAME); \ } \ } while (0) void llhttp_init(llhttp_t* parser, llhttp_type_t type, const llhttp_settings_t* settings) { llhttp__internal_init(parser); parser->type = type; parser->settings = (void*) settings; } #if defined(__wasm__) extern int wasm_on_message_begin(llhttp_t * p); extern int wasm_on_url(llhttp_t* p, const char* at, size_t length); extern int wasm_on_status(llhttp_t* p, const char* at, size_t length); extern int wasm_on_header_field(llhttp_t* p, const char* at, size_t length); extern int wasm_on_header_value(llhttp_t* p, const char* at, size_t length); extern int wasm_on_headers_complete(llhttp_t * p, int status_code, uint8_t upgrade, int should_keep_alive); extern int wasm_on_body(llhttp_t* p, const char* at, size_t length); extern int wasm_on_message_complete(llhttp_t * p); static int wasm_on_headers_complete_wrap(llhttp_t* p) { return wasm_on_headers_complete(p, p->status_code, p->upgrade, llhttp_should_keep_alive(p)); } const llhttp_settings_t wasm_settings = { .on_message_begin = wasm_on_message_begin, .on_url = wasm_on_url, .on_status = wasm_on_status, .on_header_field = wasm_on_header_field, .on_header_value = wasm_on_header_value, .on_headers_complete = wasm_on_headers_complete_wrap, .on_body = wasm_on_body, .on_message_complete = wasm_on_message_complete, }; llhttp_t* llhttp_alloc(llhttp_type_t type) { llhttp_t* parser = malloc(sizeof(llhttp_t)); llhttp_init(parser, type, &wasm_settings); return parser; } void llhttp_free(llhttp_t* parser) { free(parser); } #endif // defined(__wasm__) /* Some getters required to get stuff from the parser */ uint8_t llhttp_get_type(llhttp_t* parser) { return parser->type; } uint8_t llhttp_get_http_major(llhttp_t* parser) { return parser->http_major; } uint8_t llhttp_get_http_minor(llhttp_t* parser) { return parser->http_minor; } uint8_t llhttp_get_method(llhttp_t* parser) { return parser->method; } int llhttp_get_status_code(llhttp_t* parser) { return parser->status_code; } uint8_t llhttp_get_upgrade(llhttp_t* parser) { return parser->upgrade; } void llhttp_reset(llhttp_t* parser) { llhttp_type_t type = parser->type; const llhttp_settings_t* settings = parser->settings; void* data = parser->data; uint16_t lenient_flags = parser->lenient_flags; llhttp__internal_init(parser); parser->type = type; parser->settings = (void*) settings; parser->data = data; parser->lenient_flags = lenient_flags; } llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) { return llhttp__internal_execute(parser, data, data + len); } void llhttp_settings_init(llhttp_settings_t* settings) { memset(settings, 0, sizeof(*settings)); } llhttp_errno_t llhttp_finish(llhttp_t* parser) { int err; /* We're in an error state. Don't bother doing anything. */ if (parser->error != 0) { return 0; } switch (parser->finish) { case HTTP_FINISH_SAFE_WITH_CB: CALLBACK_MAYBE(parser, on_message_complete); if (err != HPE_OK) return err; /* FALLTHROUGH */ case HTTP_FINISH_SAFE: return HPE_OK; case HTTP_FINISH_UNSAFE: parser->reason = "Invalid EOF state"; return HPE_INVALID_EOF_STATE; default: abort(); } } void llhttp_pause(llhttp_t* parser) { if (parser->error != HPE_OK) { return; } parser->error = HPE_PAUSED; parser->reason = "Paused"; } void llhttp_resume(llhttp_t* parser) { if (parser->error != HPE_PAUSED) { return; } parser->error = 0; } void llhttp_resume_after_upgrade(llhttp_t* parser) { if (parser->error != HPE_PAUSED_UPGRADE) { return; } parser->error = 0; } llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) { return parser->error; } const char* llhttp_get_error_reason(const llhttp_t* parser) { return parser->reason; } void llhttp_set_error_reason(llhttp_t* parser, const char* reason) { parser->reason = reason; } const char* llhttp_get_error_pos(const llhttp_t* parser) { return parser->error_pos; } const char* llhttp_errno_name(llhttp_errno_t err) { #define HTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return "HPE_" #NAME; switch (err) { HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) default: abort(); } #undef HTTP_ERRNO_GEN } const char* llhttp_method_name(llhttp_method_t method) { #define HTTP_METHOD_GEN(NUM, NAME, STRING) case HTTP_##NAME: return #STRING; switch (method) { HTTP_ALL_METHOD_MAP(HTTP_METHOD_GEN) default: abort(); } #undef HTTP_METHOD_GEN } const char* llhttp_status_name(llhttp_status_t status) { #define HTTP_STATUS_GEN(NUM, NAME, STRING) case HTTP_STATUS_##NAME: return #STRING; switch (status) { HTTP_STATUS_MAP(HTTP_STATUS_GEN) default: abort(); } #undef HTTP_STATUS_GEN } void llhttp_set_lenient_headers(llhttp_t* parser, int enabled) { if (enabled) { parser->lenient_flags |= LENIENT_HEADERS; } else { parser->lenient_flags &= ~LENIENT_HEADERS; } } void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled) { if (enabled) { parser->lenient_flags |= LENIENT_CHUNKED_LENGTH; } else { parser->lenient_flags &= ~LENIENT_CHUNKED_LENGTH; } } void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled) { if (enabled) { parser->lenient_flags |= LENIENT_KEEP_ALIVE; } else { parser->lenient_flags &= ~LENIENT_KEEP_ALIVE; } } void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled) { if (enabled) { parser->lenient_flags |= LENIENT_TRANSFER_ENCODING; } else { parser->lenient_flags &= ~LENIENT_TRANSFER_ENCODING; } } void llhttp_set_lenient_version(llhttp_t* parser, int enabled) { if (enabled) { parser->lenient_flags |= LENIENT_VERSION; } else { parser->lenient_flags &= ~LENIENT_VERSION; } } void llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled) { if (enabled) { parser->lenient_flags |= LENIENT_DATA_AFTER_CLOSE; } else { parser->lenient_flags &= ~LENIENT_DATA_AFTER_CLOSE; } } void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled) { if (enabled) { parser->lenient_flags |= LENIENT_OPTIONAL_LF_AFTER_CR; } else { parser->lenient_flags &= ~LENIENT_OPTIONAL_LF_AFTER_CR; } } void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled) { if (enabled) { parser->lenient_flags |= LENIENT_OPTIONAL_CRLF_AFTER_CHUNK; } else { parser->lenient_flags &= ~LENIENT_OPTIONAL_CRLF_AFTER_CHUNK; } } void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled) { if (enabled) { parser->lenient_flags |= LENIENT_OPTIONAL_CR_BEFORE_LF; } else { parser->lenient_flags &= ~LENIENT_OPTIONAL_CR_BEFORE_LF; } } void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled) { if (enabled) { parser->lenient_flags |= LENIENT_SPACES_AFTER_CHUNK_SIZE; } else { parser->lenient_flags &= ~LENIENT_SPACES_AFTER_CHUNK_SIZE; } } /* Callbacks */ int llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_message_begin); return err; } int llhttp__on_protocol(llhttp_t* s, const char* p, const char* endp) { int err; SPAN_CALLBACK_MAYBE(s, on_protocol, p, endp - p); return err; } int llhttp__on_protocol_complete(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_protocol_complete); return err; } int llhttp__on_url(llhttp_t* s, const char* p, const char* endp) { int err; SPAN_CALLBACK_MAYBE(s, on_url, p, endp - p); return err; } int llhttp__on_url_complete(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_url_complete); return err; } int llhttp__on_status(llhttp_t* s, const char* p, const char* endp) { int err; SPAN_CALLBACK_MAYBE(s, on_status, p, endp - p); return err; } int llhttp__on_status_complete(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_status_complete); return err; } int llhttp__on_method(llhttp_t* s, const char* p, const char* endp) { int err; SPAN_CALLBACK_MAYBE(s, on_method, p, endp - p); return err; } int llhttp__on_method_complete(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_method_complete); return err; } int llhttp__on_version(llhttp_t* s, const char* p, const char* endp) { int err; SPAN_CALLBACK_MAYBE(s, on_version, p, endp - p); return err; } int llhttp__on_version_complete(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_version_complete); return err; } int llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) { int err; SPAN_CALLBACK_MAYBE(s, on_header_field, p, endp - p); return err; } int llhttp__on_header_field_complete(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_header_field_complete); return err; } int llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) { int err; SPAN_CALLBACK_MAYBE(s, on_header_value, p, endp - p); return err; } int llhttp__on_header_value_complete(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_header_value_complete); return err; } int llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_headers_complete); return err; } int llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_message_complete); return err; } int llhttp__on_body(llhttp_t* s, const char* p, const char* endp) { int err; SPAN_CALLBACK_MAYBE(s, on_body, p, endp - p); return err; } int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_chunk_header); return err; } int llhttp__on_chunk_extension_name(llhttp_t* s, const char* p, const char* endp) { int err; SPAN_CALLBACK_MAYBE(s, on_chunk_extension_name, p, endp - p); return err; } int llhttp__on_chunk_extension_name_complete(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_chunk_extension_name_complete); return err; } int llhttp__on_chunk_extension_value(llhttp_t* s, const char* p, const char* endp) { int err; SPAN_CALLBACK_MAYBE(s, on_chunk_extension_value, p, endp - p); return err; } int llhttp__on_chunk_extension_value_complete(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_chunk_extension_value_complete); return err; } int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_chunk_complete); return err; } int llhttp__on_reset(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_reset); return err; } /* Private */ void llhttp__debug(llhttp_t* s, const char* p, const char* endp, const char* msg) { if (p == endp) { fprintf(stderr, "p=%p type=%d flags=%02x next=null debug=%s\n", s, s->type, s->flags, msg); } else { fprintf(stderr, "p=%p type=%d flags=%02x next=%02x debug=%s\n", s, s->type, s->flags, *p, msg); } } nghttp2-1.68.0/third-party/llhttp/src/PaxHeaders/llhttp.c0000644000000000000000000000013215077107271020301 xustar0030 mtime=1761382073.011444055 30 atime=1761382080.109411467 30 ctime=1761382108.215302939 nghttp2-1.68.0/third-party/llhttp/src/llhttp.c0000644000175100017510000115046115077107271020701 0ustar00runnerrunner#include #include #include #ifdef __SSE4_2__ #ifdef _MSC_VER #include #else /* !_MSC_VER */ #include #endif /* _MSC_VER */ #endif /* __SSE4_2__ */ #ifdef __ARM_NEON__ #include #endif /* __ARM_NEON__ */ #ifdef __wasm__ #include #endif /* __wasm__ */ #ifdef _MSC_VER #define ALIGN(n) _declspec(align(n)) #define UNREACHABLE __assume(0) #else /* !_MSC_VER */ #define ALIGN(n) __attribute__((aligned(n))) #define UNREACHABLE __builtin_unreachable() #endif /* _MSC_VER */ #include "llhttp.h" typedef int (*llhttp__internal__span_cb)( llhttp__internal_t*, const char*, const char*); static const unsigned char llparse_blob0[] = { 'o', 'n' }; static const unsigned char llparse_blob1[] = { 'e', 'c', 't', 'i', 'o', 'n' }; static const unsigned char llparse_blob2[] = { 'l', 'o', 's', 'e' }; static const unsigned char llparse_blob3[] = { 'e', 'e', 'p', '-', 'a', 'l', 'i', 'v', 'e' }; static const unsigned char llparse_blob4[] = { 'p', 'g', 'r', 'a', 'd', 'e' }; static const unsigned char llparse_blob5[] = { 'c', 'h', 'u', 'n', 'k', 'e', 'd' }; #ifdef __SSE4_2__ static const unsigned char ALIGN(16) llparse_blob6[] = { 0x9, 0x9, ' ', '~', 0x80, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; #endif /* __SSE4_2__ */ #ifdef __SSE4_2__ static const unsigned char ALIGN(16) llparse_blob7[] = { '!', '!', '#', '\'', '*', '+', '-', '.', '0', '9', 'A', 'Z', '^', 'z', '|', '|' }; #endif /* __SSE4_2__ */ #ifdef __SSE4_2__ static const unsigned char ALIGN(16) llparse_blob8[] = { '~', '~', 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; #endif /* __SSE4_2__ */ static const unsigned char llparse_blob9[] = { 'e', 'n', 't', '-', 'l', 'e', 'n', 'g', 't', 'h' }; static const unsigned char llparse_blob10[] = { 'r', 'o', 'x', 'y', '-', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n' }; static const unsigned char llparse_blob11[] = { 'r', 'a', 'n', 's', 'f', 'e', 'r', '-', 'e', 'n', 'c', 'o', 'd', 'i', 'n', 'g' }; static const unsigned char llparse_blob12[] = { 'p', 'g', 'r', 'a', 'd', 'e' }; static const unsigned char llparse_blob13[] = { 'T', 'T', 'P' }; static const unsigned char llparse_blob14[] = { 0xd, 0xa, 0xd, 0xa, 'S', 'M', 0xd, 0xa, 0xd, 0xa }; static const unsigned char llparse_blob15[] = { 'C', 'E' }; static const unsigned char llparse_blob16[] = { 'T', 'S', 'P' }; static const unsigned char llparse_blob17[] = { 'N', 'O', 'U', 'N', 'C', 'E' }; static const unsigned char llparse_blob18[] = { 'I', 'N', 'D' }; static const unsigned char llparse_blob19[] = { 'E', 'C', 'K', 'O', 'U', 'T' }; static const unsigned char llparse_blob20[] = { 'N', 'E', 'C', 'T' }; static const unsigned char llparse_blob21[] = { 'E', 'T', 'E' }; static const unsigned char llparse_blob22[] = { 'C', 'R', 'I', 'B', 'E' }; static const unsigned char llparse_blob23[] = { 'L', 'U', 'S', 'H' }; static const unsigned char llparse_blob24[] = { 'E', 'T' }; static const unsigned char llparse_blob25[] = { 'P', 'A', 'R', 'A', 'M', 'E', 'T', 'E', 'R' }; static const unsigned char llparse_blob26[] = { 'E', 'A', 'D' }; static const unsigned char llparse_blob27[] = { 'N', 'K' }; static const unsigned char llparse_blob28[] = { 'C', 'K' }; static const unsigned char llparse_blob29[] = { 'S', 'E', 'A', 'R', 'C', 'H' }; static const unsigned char llparse_blob30[] = { 'R', 'G', 'E' }; static const unsigned char llparse_blob31[] = { 'C', 'T', 'I', 'V', 'I', 'T', 'Y' }; static const unsigned char llparse_blob32[] = { 'L', 'E', 'N', 'D', 'A', 'R' }; static const unsigned char llparse_blob33[] = { 'V', 'E' }; static const unsigned char llparse_blob34[] = { 'O', 'T', 'I', 'F', 'Y' }; static const unsigned char llparse_blob35[] = { 'P', 'T', 'I', 'O', 'N', 'S' }; static const unsigned char llparse_blob36[] = { 'C', 'H' }; static const unsigned char llparse_blob37[] = { 'S', 'E' }; static const unsigned char llparse_blob38[] = { 'A', 'Y' }; static const unsigned char llparse_blob39[] = { 'S', 'T' }; static const unsigned char llparse_blob40[] = { 'I', 'N', 'D' }; static const unsigned char llparse_blob41[] = { 'A', 'T', 'C', 'H' }; static const unsigned char llparse_blob42[] = { 'G', 'E' }; static const unsigned char llparse_blob43[] = { 'U', 'E', 'R', 'Y' }; static const unsigned char llparse_blob44[] = { 'I', 'N', 'D' }; static const unsigned char llparse_blob45[] = { 'O', 'R', 'D' }; static const unsigned char llparse_blob46[] = { 'I', 'R', 'E', 'C', 'T' }; static const unsigned char llparse_blob47[] = { 'O', 'R', 'T' }; static const unsigned char llparse_blob48[] = { 'R', 'C', 'H' }; static const unsigned char llparse_blob49[] = { 'P', 'A', 'R', 'A', 'M', 'E', 'T', 'E', 'R' }; static const unsigned char llparse_blob50[] = { 'U', 'R', 'C', 'E' }; static const unsigned char llparse_blob51[] = { 'B', 'S', 'C', 'R', 'I', 'B', 'E' }; static const unsigned char llparse_blob52[] = { 'A', 'R', 'D', 'O', 'W', 'N' }; static const unsigned char llparse_blob53[] = { 'A', 'C', 'E' }; static const unsigned char llparse_blob54[] = { 'I', 'N', 'D' }; static const unsigned char llparse_blob55[] = { 'N', 'K' }; static const unsigned char llparse_blob56[] = { 'C', 'K' }; static const unsigned char llparse_blob57[] = { 'U', 'B', 'S', 'C', 'R', 'I', 'B', 'E' }; static const unsigned char llparse_blob58[] = { 'T', 'T', 'P' }; static const unsigned char llparse_blob59[] = { 'C', 'E' }; static const unsigned char llparse_blob60[] = { 'T', 'S', 'P' }; static const unsigned char llparse_blob61[] = { 'A', 'D' }; static const unsigned char llparse_blob62[] = { 'T', 'P', '/' }; enum llparse_match_status_e { kMatchComplete, kMatchPause, kMatchMismatch }; typedef enum llparse_match_status_e llparse_match_status_t; struct llparse_match_s { llparse_match_status_t status; const unsigned char* current; }; typedef struct llparse_match_s llparse_match_t; static llparse_match_t llparse__match_sequence_to_lower( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp, const unsigned char* seq, uint32_t seq_len) { uint32_t index; llparse_match_t res; index = s->_index; for (; p != endp; p++) { unsigned char current; current = ((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p)); if (current == seq[index]) { if (++index == seq_len) { res.status = kMatchComplete; goto reset; } } else { res.status = kMatchMismatch; goto reset; } } s->_index = index; res.status = kMatchPause; res.current = p; return res; reset: s->_index = 0; res.current = p; return res; } static llparse_match_t llparse__match_sequence_to_lower_unsafe( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp, const unsigned char* seq, uint32_t seq_len) { uint32_t index; llparse_match_t res; index = s->_index; for (; p != endp; p++) { unsigned char current; current = ((*p) | 0x20); if (current == seq[index]) { if (++index == seq_len) { res.status = kMatchComplete; goto reset; } } else { res.status = kMatchMismatch; goto reset; } } s->_index = index; res.status = kMatchPause; res.current = p; return res; reset: s->_index = 0; res.current = p; return res; } static llparse_match_t llparse__match_sequence_id( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp, const unsigned char* seq, uint32_t seq_len) { uint32_t index; llparse_match_t res; index = s->_index; for (; p != endp; p++) { unsigned char current; current = *p; if (current == seq[index]) { if (++index == seq_len) { res.status = kMatchComplete; goto reset; } } else { res.status = kMatchMismatch; goto reset; } } s->_index = index; res.status = kMatchPause; res.current = p; return res; reset: s->_index = 0; res.current = p; return res; } enum llparse_state_e { s_error, s_n_llhttp__internal__n_closed, s_n_llhttp__internal__n_invoke_llhttp__after_message_complete, s_n_llhttp__internal__n_pause_1, s_n_llhttp__internal__n_invoke_is_equal_upgrade, s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2, s_n_llhttp__internal__n_chunk_data_almost_done_1, s_n_llhttp__internal__n_chunk_data_almost_done, s_n_llhttp__internal__n_consume_content_length, s_n_llhttp__internal__n_span_start_llhttp__on_body, s_n_llhttp__internal__n_invoke_is_equal_content_length, s_n_llhttp__internal__n_chunk_size_almost_done, s_n_llhttp__internal__n_invoke_test_lenient_flags_9, s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete, s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1, s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2, s_n_llhttp__internal__n_invoke_test_lenient_flags_10, s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete, s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1, s_n_llhttp__internal__n_chunk_extension_quoted_value_done, s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2, s_n_llhttp__internal__n_error_30, s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair, s_n_llhttp__internal__n_error_31, s_n_llhttp__internal__n_chunk_extension_quoted_value, s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3, s_n_llhttp__internal__n_error_33, s_n_llhttp__internal__n_chunk_extension_value, s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value, s_n_llhttp__internal__n_error_34, s_n_llhttp__internal__n_chunk_extension_name, s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name, s_n_llhttp__internal__n_chunk_extensions, s_n_llhttp__internal__n_chunk_size_otherwise, s_n_llhttp__internal__n_chunk_size, s_n_llhttp__internal__n_chunk_size_digit, s_n_llhttp__internal__n_invoke_update_content_length_1, s_n_llhttp__internal__n_consume_content_length_1, s_n_llhttp__internal__n_span_start_llhttp__on_body_1, s_n_llhttp__internal__n_eof, s_n_llhttp__internal__n_span_start_llhttp__on_body_2, s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete, s_n_llhttp__internal__n_error_5, s_n_llhttp__internal__n_headers_almost_done, s_n_llhttp__internal__n_header_field_colon_discard_ws, s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete, s_n_llhttp__internal__n_span_start_llhttp__on_header_value, s_n_llhttp__internal__n_header_value_discard_lws, s_n_llhttp__internal__n_header_value_discard_ws_almost_done, s_n_llhttp__internal__n_header_value_lws, s_n_llhttp__internal__n_header_value_almost_done, s_n_llhttp__internal__n_invoke_test_lenient_flags_17, s_n_llhttp__internal__n_header_value_lenient, s_n_llhttp__internal__n_error_54, s_n_llhttp__internal__n_header_value_otherwise, s_n_llhttp__internal__n_header_value_connection_token, s_n_llhttp__internal__n_header_value_connection_ws, s_n_llhttp__internal__n_header_value_connection_1, s_n_llhttp__internal__n_header_value_connection_2, s_n_llhttp__internal__n_header_value_connection_3, s_n_llhttp__internal__n_header_value_connection, s_n_llhttp__internal__n_error_56, s_n_llhttp__internal__n_error_57, s_n_llhttp__internal__n_header_value_content_length_ws, s_n_llhttp__internal__n_header_value_content_length, s_n_llhttp__internal__n_error_59, s_n_llhttp__internal__n_error_58, s_n_llhttp__internal__n_header_value_te_token_ows, s_n_llhttp__internal__n_header_value, s_n_llhttp__internal__n_header_value_te_token, s_n_llhttp__internal__n_header_value_te_chunked_last, s_n_llhttp__internal__n_header_value_te_chunked, s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1, s_n_llhttp__internal__n_header_value_discard_ws, s_n_llhttp__internal__n_invoke_load_header_state, s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete, s_n_llhttp__internal__n_header_field_general_otherwise, s_n_llhttp__internal__n_header_field_general, s_n_llhttp__internal__n_header_field_colon, s_n_llhttp__internal__n_header_field_3, s_n_llhttp__internal__n_header_field_4, s_n_llhttp__internal__n_header_field_2, s_n_llhttp__internal__n_header_field_1, s_n_llhttp__internal__n_header_field_5, s_n_llhttp__internal__n_header_field_6, s_n_llhttp__internal__n_header_field_7, s_n_llhttp__internal__n_header_field, s_n_llhttp__internal__n_span_start_llhttp__on_header_field, s_n_llhttp__internal__n_header_field_start, s_n_llhttp__internal__n_headers_start, s_n_llhttp__internal__n_url_to_http_09, s_n_llhttp__internal__n_url_skip_to_http09, s_n_llhttp__internal__n_url_skip_lf_to_http09_1, s_n_llhttp__internal__n_url_skip_lf_to_http09, s_n_llhttp__internal__n_req_pri_upgrade, s_n_llhttp__internal__n_req_http_complete_crlf, s_n_llhttp__internal__n_req_http_complete, s_n_llhttp__internal__n_invoke_load_method_1, s_n_llhttp__internal__n_invoke_llhttp__on_version_complete, s_n_llhttp__internal__n_error_67, s_n_llhttp__internal__n_error_74, s_n_llhttp__internal__n_req_http_minor, s_n_llhttp__internal__n_error_75, s_n_llhttp__internal__n_req_http_dot, s_n_llhttp__internal__n_error_76, s_n_llhttp__internal__n_req_http_major, s_n_llhttp__internal__n_span_start_llhttp__on_version, s_n_llhttp__internal__n_req_after_protocol, s_n_llhttp__internal__n_invoke_load_method, s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete, s_n_llhttp__internal__n_error_82, s_n_llhttp__internal__n_req_after_http_start_1, s_n_llhttp__internal__n_invoke_load_method_2, s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1, s_n_llhttp__internal__n_req_after_http_start_2, s_n_llhttp__internal__n_invoke_load_method_3, s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2, s_n_llhttp__internal__n_req_after_http_start_3, s_n_llhttp__internal__n_req_after_http_start, s_n_llhttp__internal__n_span_start_llhttp__on_protocol, s_n_llhttp__internal__n_req_http_start, s_n_llhttp__internal__n_url_to_http, s_n_llhttp__internal__n_url_skip_to_http, s_n_llhttp__internal__n_url_fragment, s_n_llhttp__internal__n_span_end_stub_query_3, s_n_llhttp__internal__n_url_query, s_n_llhttp__internal__n_url_query_or_fragment, s_n_llhttp__internal__n_url_path, s_n_llhttp__internal__n_span_start_stub_path_2, s_n_llhttp__internal__n_span_start_stub_path, s_n_llhttp__internal__n_span_start_stub_path_1, s_n_llhttp__internal__n_url_server_with_at, s_n_llhttp__internal__n_url_server, s_n_llhttp__internal__n_url_schema_delim_1, s_n_llhttp__internal__n_url_schema_delim, s_n_llhttp__internal__n_span_end_stub_schema, s_n_llhttp__internal__n_url_schema, s_n_llhttp__internal__n_url_start, s_n_llhttp__internal__n_span_start_llhttp__on_url_1, s_n_llhttp__internal__n_url_entry_normal, s_n_llhttp__internal__n_span_start_llhttp__on_url, s_n_llhttp__internal__n_url_entry_connect, s_n_llhttp__internal__n_req_spaces_before_url, s_n_llhttp__internal__n_req_first_space_before_url, s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1, s_n_llhttp__internal__n_after_start_req_2, s_n_llhttp__internal__n_after_start_req_3, s_n_llhttp__internal__n_after_start_req_1, s_n_llhttp__internal__n_after_start_req_4, s_n_llhttp__internal__n_after_start_req_6, s_n_llhttp__internal__n_after_start_req_8, s_n_llhttp__internal__n_after_start_req_9, s_n_llhttp__internal__n_after_start_req_7, s_n_llhttp__internal__n_after_start_req_5, s_n_llhttp__internal__n_after_start_req_12, s_n_llhttp__internal__n_after_start_req_13, s_n_llhttp__internal__n_after_start_req_11, s_n_llhttp__internal__n_after_start_req_10, s_n_llhttp__internal__n_after_start_req_14, s_n_llhttp__internal__n_after_start_req_17, s_n_llhttp__internal__n_after_start_req_16, s_n_llhttp__internal__n_after_start_req_15, s_n_llhttp__internal__n_after_start_req_18, s_n_llhttp__internal__n_after_start_req_20, s_n_llhttp__internal__n_after_start_req_21, s_n_llhttp__internal__n_after_start_req_19, s_n_llhttp__internal__n_after_start_req_23, s_n_llhttp__internal__n_after_start_req_24, s_n_llhttp__internal__n_after_start_req_26, s_n_llhttp__internal__n_after_start_req_28, s_n_llhttp__internal__n_after_start_req_29, s_n_llhttp__internal__n_after_start_req_27, s_n_llhttp__internal__n_after_start_req_25, s_n_llhttp__internal__n_after_start_req_30, s_n_llhttp__internal__n_after_start_req_22, s_n_llhttp__internal__n_after_start_req_31, s_n_llhttp__internal__n_after_start_req_32, s_n_llhttp__internal__n_after_start_req_35, s_n_llhttp__internal__n_after_start_req_36, s_n_llhttp__internal__n_after_start_req_34, s_n_llhttp__internal__n_after_start_req_37, s_n_llhttp__internal__n_after_start_req_38, s_n_llhttp__internal__n_after_start_req_42, s_n_llhttp__internal__n_after_start_req_43, s_n_llhttp__internal__n_after_start_req_41, s_n_llhttp__internal__n_after_start_req_40, s_n_llhttp__internal__n_after_start_req_39, s_n_llhttp__internal__n_after_start_req_45, s_n_llhttp__internal__n_after_start_req_44, s_n_llhttp__internal__n_after_start_req_33, s_n_llhttp__internal__n_after_start_req_46, s_n_llhttp__internal__n_after_start_req_49, s_n_llhttp__internal__n_after_start_req_50, s_n_llhttp__internal__n_after_start_req_51, s_n_llhttp__internal__n_after_start_req_52, s_n_llhttp__internal__n_after_start_req_48, s_n_llhttp__internal__n_after_start_req_47, s_n_llhttp__internal__n_after_start_req_55, s_n_llhttp__internal__n_after_start_req_57, s_n_llhttp__internal__n_after_start_req_58, s_n_llhttp__internal__n_after_start_req_56, s_n_llhttp__internal__n_after_start_req_54, s_n_llhttp__internal__n_after_start_req_59, s_n_llhttp__internal__n_after_start_req_60, s_n_llhttp__internal__n_after_start_req_53, s_n_llhttp__internal__n_after_start_req_62, s_n_llhttp__internal__n_after_start_req_63, s_n_llhttp__internal__n_after_start_req_61, s_n_llhttp__internal__n_after_start_req_66, s_n_llhttp__internal__n_after_start_req_68, s_n_llhttp__internal__n_after_start_req_69, s_n_llhttp__internal__n_after_start_req_67, s_n_llhttp__internal__n_after_start_req_70, s_n_llhttp__internal__n_after_start_req_65, s_n_llhttp__internal__n_after_start_req_64, s_n_llhttp__internal__n_after_start_req, s_n_llhttp__internal__n_span_start_llhttp__on_method_1, s_n_llhttp__internal__n_res_line_almost_done, s_n_llhttp__internal__n_invoke_test_lenient_flags_30, s_n_llhttp__internal__n_res_status, s_n_llhttp__internal__n_span_start_llhttp__on_status, s_n_llhttp__internal__n_res_status_code_otherwise, s_n_llhttp__internal__n_res_status_code_digit_3, s_n_llhttp__internal__n_res_status_code_digit_2, s_n_llhttp__internal__n_res_status_code_digit_1, s_n_llhttp__internal__n_res_after_version, s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1, s_n_llhttp__internal__n_error_93, s_n_llhttp__internal__n_error_107, s_n_llhttp__internal__n_res_http_minor, s_n_llhttp__internal__n_error_108, s_n_llhttp__internal__n_res_http_dot, s_n_llhttp__internal__n_error_109, s_n_llhttp__internal__n_res_http_major, s_n_llhttp__internal__n_span_start_llhttp__on_version_1, s_n_llhttp__internal__n_res_after_protocol, s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3, s_n_llhttp__internal__n_error_115, s_n_llhttp__internal__n_res_after_start_1, s_n_llhttp__internal__n_res_after_start_2, s_n_llhttp__internal__n_res_after_start_3, s_n_llhttp__internal__n_res_after_start, s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1, s_n_llhttp__internal__n_invoke_llhttp__on_method_complete, s_n_llhttp__internal__n_req_or_res_method_2, s_n_llhttp__internal__n_invoke_update_type_1, s_n_llhttp__internal__n_req_or_res_method_3, s_n_llhttp__internal__n_req_or_res_method_1, s_n_llhttp__internal__n_req_or_res_method, s_n_llhttp__internal__n_span_start_llhttp__on_method, s_n_llhttp__internal__n_start_req_or_res, s_n_llhttp__internal__n_invoke_load_type, s_n_llhttp__internal__n_invoke_update_finish, s_n_llhttp__internal__n_start, }; typedef enum llparse_state_e llparse_state_t; int llhttp__on_method( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_url( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_protocol( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_version( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_header_field( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_header_value( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_body( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_chunk_extension_name( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_chunk_extension_value( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_status( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_load_initial_message_completed( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return state->initial_message_completed; } int llhttp__on_reset( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_update_finish( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->finish = 2; return 0; } int llhttp__on_message_begin( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_load_type( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return state->type; } int llhttp__internal__c_store_method( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp, int match) { state->method = match; return 0; } int llhttp__on_method_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_is_equal_method( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return state->method == 5; } int llhttp__internal__c_update_http_major( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->http_major = 0; return 0; } int llhttp__internal__c_update_http_minor( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->http_minor = 9; return 0; } int llhttp__on_url_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_test_lenient_flags( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->lenient_flags & 1) == 1; } int llhttp__internal__c_test_lenient_flags_1( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->lenient_flags & 256) == 256; } int llhttp__internal__c_test_flags( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->flags & 128) == 128; } int llhttp__on_chunk_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_message_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_is_equal_upgrade( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return state->upgrade == 1; } int llhttp__after_message_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_update_content_length( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->content_length = 0; return 0; } int llhttp__internal__c_update_initial_message_completed( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->initial_message_completed = 1; return 0; } int llhttp__internal__c_update_finish_1( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->finish = 0; return 0; } int llhttp__internal__c_test_lenient_flags_2( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->lenient_flags & 4) == 4; } int llhttp__internal__c_test_lenient_flags_3( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->lenient_flags & 32) == 32; } int llhttp__before_headers_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_headers_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__after_headers_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_mul_add_content_length( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp, int match) { /* Multiplication overflow */ if (state->content_length > 0xffffffffffffffffULL / 16) { return 1; } state->content_length *= 16; /* Addition overflow */ if (match >= 0) { if (state->content_length > 0xffffffffffffffffULL - match) { return 1; } } else { if (state->content_length < 0ULL - match) { return 1; } } state->content_length += match; return 0; } int llhttp__internal__c_test_lenient_flags_4( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->lenient_flags & 512) == 512; } int llhttp__on_chunk_header( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_is_equal_content_length( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return state->content_length == 0; } int llhttp__internal__c_test_lenient_flags_7( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->lenient_flags & 128) == 128; } int llhttp__internal__c_or_flags( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 128; return 0; } int llhttp__internal__c_test_lenient_flags_8( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->lenient_flags & 64) == 64; } int llhttp__on_chunk_extension_name_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_chunk_extension_value_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_update_finish_3( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->finish = 1; return 0; } int llhttp__internal__c_or_flags_1( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 64; return 0; } int llhttp__internal__c_update_upgrade( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->upgrade = 1; return 0; } int llhttp__internal__c_store_header_state( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp, int match) { state->header_state = match; return 0; } int llhttp__on_header_field_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_load_header_state( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return state->header_state; } int llhttp__internal__c_test_flags_4( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->flags & 512) == 512; } int llhttp__internal__c_test_lenient_flags_22( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->lenient_flags & 2) == 2; } int llhttp__internal__c_or_flags_5( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 1; return 0; } int llhttp__internal__c_update_header_state( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->header_state = 1; return 0; } int llhttp__on_header_value_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_or_flags_6( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 2; return 0; } int llhttp__internal__c_or_flags_7( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 4; return 0; } int llhttp__internal__c_or_flags_8( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 8; return 0; } int llhttp__internal__c_update_header_state_3( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->header_state = 6; return 0; } int llhttp__internal__c_update_header_state_1( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->header_state = 0; return 0; } int llhttp__internal__c_update_header_state_6( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->header_state = 5; return 0; } int llhttp__internal__c_update_header_state_7( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->header_state = 7; return 0; } int llhttp__internal__c_test_flags_2( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->flags & 32) == 32; } int llhttp__internal__c_mul_add_content_length_1( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp, int match) { /* Multiplication overflow */ if (state->content_length > 0xffffffffffffffffULL / 10) { return 1; } state->content_length *= 10; /* Addition overflow */ if (match >= 0) { if (state->content_length > 0xffffffffffffffffULL - match) { return 1; } } else { if (state->content_length < 0ULL - match) { return 1; } } state->content_length += match; return 0; } int llhttp__internal__c_or_flags_17( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 32; return 0; } int llhttp__internal__c_test_flags_3( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->flags & 8) == 8; } int llhttp__internal__c_test_lenient_flags_20( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->lenient_flags & 8) == 8; } int llhttp__internal__c_or_flags_18( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 512; return 0; } int llhttp__internal__c_and_flags( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags &= -9; return 0; } int llhttp__internal__c_update_header_state_8( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->header_state = 8; return 0; } int llhttp__internal__c_or_flags_20( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 16; return 0; } int llhttp__on_protocol_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_load_method( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return state->method; } int llhttp__internal__c_store_http_major( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp, int match) { state->http_major = match; return 0; } int llhttp__internal__c_store_http_minor( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp, int match) { state->http_minor = match; return 0; } int llhttp__internal__c_test_lenient_flags_24( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->lenient_flags & 16) == 16; } int llhttp__on_version_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_load_http_major( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return state->http_major; } int llhttp__internal__c_load_http_minor( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return state->http_minor; } int llhttp__internal__c_update_status_code( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->status_code = 0; return 0; } int llhttp__internal__c_mul_add_status_code( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp, int match) { /* Multiplication overflow */ if (state->status_code > 0xffff / 10) { return 1; } state->status_code *= 10; /* Addition overflow */ if (match >= 0) { if (state->status_code > 0xffff - match) { return 1; } } else { if (state->status_code < 0 - match) { return 1; } } state->status_code += match; return 0; } int llhttp__on_status_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_update_type( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->type = 1; return 0; } int llhttp__internal__c_update_type_1( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->type = 2; return 0; } int llhttp__internal_init(llhttp__internal_t* state) { memset(state, 0, sizeof(*state)); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_start; return 0; } static llparse_state_t llhttp__internal__run( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { int match; switch ((llparse_state_t) (intptr_t) state->_current) { case s_n_llhttp__internal__n_closed: s_n_llhttp__internal__n_closed: { if (p == endp) { return s_n_llhttp__internal__n_closed; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_closed; } case 13: { p++; goto s_n_llhttp__internal__n_closed; } default: { p++; goto s_n_llhttp__internal__n_invoke_test_lenient_flags_3; } } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_llhttp__after_message_complete: s_n_llhttp__internal__n_invoke_llhttp__after_message_complete: { switch (llhttp__after_message_complete(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_update_content_length; default: goto s_n_llhttp__internal__n_invoke_update_finish_1; } UNREACHABLE; } case s_n_llhttp__internal__n_pause_1: s_n_llhttp__internal__n_pause_1: { state->error = 0x16; state->reason = "Pause on CONNECT/Upgrade"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; return s_error; UNREACHABLE; } case s_n_llhttp__internal__n_invoke_is_equal_upgrade: s_n_llhttp__internal__n_invoke_is_equal_upgrade: { switch (llhttp__internal__c_is_equal_upgrade(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; default: goto s_n_llhttp__internal__n_pause_1; } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2: s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2: { switch (llhttp__on_message_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_is_equal_upgrade; case 21: goto s_n_llhttp__internal__n_pause_13; default: goto s_n_llhttp__internal__n_error_38; } UNREACHABLE; } case s_n_llhttp__internal__n_chunk_data_almost_done_1: s_n_llhttp__internal__n_chunk_data_almost_done_1: { if (p == endp) { return s_n_llhttp__internal__n_chunk_data_almost_done_1; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete; } default: { goto s_n_llhttp__internal__n_invoke_test_lenient_flags_7; } } UNREACHABLE; } case s_n_llhttp__internal__n_chunk_data_almost_done: s_n_llhttp__internal__n_chunk_data_almost_done: { if (p == endp) { return s_n_llhttp__internal__n_chunk_data_almost_done; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_invoke_test_lenient_flags_6; } case 13: { p++; goto s_n_llhttp__internal__n_chunk_data_almost_done_1; } default: { goto s_n_llhttp__internal__n_invoke_test_lenient_flags_7; } } UNREACHABLE; } case s_n_llhttp__internal__n_consume_content_length: s_n_llhttp__internal__n_consume_content_length: { size_t avail; uint64_t need; avail = endp - p; need = state->content_length; if (avail >= need) { p += need; state->content_length = 0; goto s_n_llhttp__internal__n_span_end_llhttp__on_body; } state->content_length -= avail; return s_n_llhttp__internal__n_consume_content_length; UNREACHABLE; } case s_n_llhttp__internal__n_span_start_llhttp__on_body: s_n_llhttp__internal__n_span_start_llhttp__on_body: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_body; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_body; goto s_n_llhttp__internal__n_consume_content_length; UNREACHABLE; } case s_n_llhttp__internal__n_invoke_is_equal_content_length: s_n_llhttp__internal__n_invoke_is_equal_content_length: { switch (llhttp__internal__c_is_equal_content_length(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_span_start_llhttp__on_body; default: goto s_n_llhttp__internal__n_invoke_or_flags; } UNREACHABLE; } case s_n_llhttp__internal__n_chunk_size_almost_done: s_n_llhttp__internal__n_chunk_size_almost_done: { if (p == endp) { return s_n_llhttp__internal__n_chunk_size_almost_done; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header; } default: { goto s_n_llhttp__internal__n_invoke_test_lenient_flags_8; } } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_test_lenient_flags_9: s_n_llhttp__internal__n_invoke_test_lenient_flags_9: { switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_chunk_size_almost_done; default: goto s_n_llhttp__internal__n_error_20; } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete: s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete: { switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_test_lenient_flags_9; case 21: goto s_n_llhttp__internal__n_pause_5; default: goto s_n_llhttp__internal__n_error_19; } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1: s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1: { switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_chunk_size_almost_done; case 21: goto s_n_llhttp__internal__n_pause_6; default: goto s_n_llhttp__internal__n_error_21; } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2: s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2: { switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_chunk_extensions; case 21: goto s_n_llhttp__internal__n_pause_7; default: goto s_n_llhttp__internal__n_error_22; } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_test_lenient_flags_10: s_n_llhttp__internal__n_invoke_test_lenient_flags_10: { switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_chunk_size_almost_done; default: goto s_n_llhttp__internal__n_error_25; } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete: s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete: { switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_test_lenient_flags_10; case 21: goto s_n_llhttp__internal__n_pause_8; default: goto s_n_llhttp__internal__n_error_24; } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1: s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1: { switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_chunk_size_almost_done; case 21: goto s_n_llhttp__internal__n_pause_9; default: goto s_n_llhttp__internal__n_error_26; } UNREACHABLE; } case s_n_llhttp__internal__n_chunk_extension_quoted_value_done: s_n_llhttp__internal__n_chunk_extension_quoted_value_done: { if (p == endp) { return s_n_llhttp__internal__n_chunk_extension_quoted_value_done; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_invoke_test_lenient_flags_11; } case 13: { p++; goto s_n_llhttp__internal__n_chunk_size_almost_done; } case ';': { p++; goto s_n_llhttp__internal__n_chunk_extensions; } default: { goto s_n_llhttp__internal__n_error_29; } } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2: s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2: { switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_chunk_extension_quoted_value_done; case 21: goto s_n_llhttp__internal__n_pause_10; default: goto s_n_llhttp__internal__n_error_27; } UNREACHABLE; } case s_n_llhttp__internal__n_error_30: s_n_llhttp__internal__n_error_30: { state->error = 0x2; state->reason = "Invalid quoted-pair in chunk extensions quoted value"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } case s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair: s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; if (p == endp) { return s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_chunk_extension_quoted_value; } default: { goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_3; } } UNREACHABLE; } case s_n_llhttp__internal__n_error_31: s_n_llhttp__internal__n_error_31: { state->error = 0x2; state->reason = "Invalid character in chunk extensions quoted value"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } case s_n_llhttp__internal__n_chunk_extension_quoted_value: s_n_llhttp__internal__n_chunk_extension_quoted_value: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; if (p == endp) { return s_n_llhttp__internal__n_chunk_extension_quoted_value; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_chunk_extension_quoted_value; } case 2: { p++; goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_2; } case 3: { p++; goto s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair; } default: { goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_4; } } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3: s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3: { switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_chunk_extensions; case 21: goto s_n_llhttp__internal__n_pause_11; default: goto s_n_llhttp__internal__n_error_32; } UNREACHABLE; } case s_n_llhttp__internal__n_error_33: s_n_llhttp__internal__n_error_33: { state->error = 0x2; state->reason = "Invalid character in chunk extensions value"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } case s_n_llhttp__internal__n_chunk_extension_value: s_n_llhttp__internal__n_chunk_extension_value: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 3, 3, 3, 3, 3, 0, 0, 3, 3, 0, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 5, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_chunk_extension_value; } switch (lookup_table[(uint8_t) *p]) { case 1: { goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value; } case 2: { goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_1; } case 3: { p++; goto s_n_llhttp__internal__n_chunk_extension_value; } case 4: { p++; goto s_n_llhttp__internal__n_chunk_extension_quoted_value; } case 5: { goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_5; } default: { goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_6; } } UNREACHABLE; } case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value: s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_chunk_extension_value; goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_3; UNREACHABLE; } case s_n_llhttp__internal__n_error_34: s_n_llhttp__internal__n_error_34: { state->error = 0x2; state->reason = "Invalid character in chunk extensions name"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } case s_n_llhttp__internal__n_chunk_extension_name: s_n_llhttp__internal__n_chunk_extension_name: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 3, 3, 3, 3, 0, 0, 3, 3, 0, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 4, 0, 5, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_chunk_extension_name; } switch (lookup_table[(uint8_t) *p]) { case 1: { goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name; } case 2: { goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_1; } case 3: { p++; goto s_n_llhttp__internal__n_chunk_extension_name; } case 4: { goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_2; } case 5: { goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_3; } default: { goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_4; } } UNREACHABLE; } case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name: s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_chunk_extension_name; goto s_n_llhttp__internal__n_chunk_extension_name; UNREACHABLE; } case s_n_llhttp__internal__n_chunk_extensions: s_n_llhttp__internal__n_chunk_extensions: { if (p == endp) { return s_n_llhttp__internal__n_chunk_extensions; } switch (*p) { case 13: { p++; goto s_n_llhttp__internal__n_error_17; } case ' ': { p++; goto s_n_llhttp__internal__n_error_18; } default: { goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name; } } UNREACHABLE; } case s_n_llhttp__internal__n_chunk_size_otherwise: s_n_llhttp__internal__n_chunk_size_otherwise: { if (p == endp) { return s_n_llhttp__internal__n_chunk_size_otherwise; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_invoke_test_lenient_flags_4; } case 10: { p++; goto s_n_llhttp__internal__n_invoke_test_lenient_flags_5; } case 13: { p++; goto s_n_llhttp__internal__n_chunk_size_almost_done; } case ' ': { p++; goto s_n_llhttp__internal__n_invoke_test_lenient_flags_4; } case ';': { p++; goto s_n_llhttp__internal__n_chunk_extensions; } default: { goto s_n_llhttp__internal__n_error_35; } } UNREACHABLE; } case s_n_llhttp__internal__n_chunk_size: s_n_llhttp__internal__n_chunk_size: { if (p == endp) { return s_n_llhttp__internal__n_chunk_size; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'A': { p++; match = 10; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'B': { p++; match = 11; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'C': { p++; match = 12; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'D': { p++; match = 13; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'E': { p++; match = 14; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'F': { p++; match = 15; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'a': { p++; match = 10; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'b': { p++; match = 11; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'c': { p++; match = 12; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'd': { p++; match = 13; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'e': { p++; match = 14; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'f': { p++; match = 15; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } default: { goto s_n_llhttp__internal__n_chunk_size_otherwise; } } UNREACHABLE; } case s_n_llhttp__internal__n_chunk_size_digit: s_n_llhttp__internal__n_chunk_size_digit: { if (p == endp) { return s_n_llhttp__internal__n_chunk_size_digit; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'A': { p++; match = 10; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'B': { p++; match = 11; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'C': { p++; match = 12; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'D': { p++; match = 13; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'E': { p++; match = 14; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'F': { p++; match = 15; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'a': { p++; match = 10; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'b': { p++; match = 11; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'c': { p++; match = 12; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'd': { p++; match = 13; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'e': { p++; match = 14; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'f': { p++; match = 15; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } default: { goto s_n_llhttp__internal__n_error_37; } } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_update_content_length_1: s_n_llhttp__internal__n_invoke_update_content_length_1: { switch (llhttp__internal__c_update_content_length(state, p, endp)) { default: goto s_n_llhttp__internal__n_chunk_size_digit; } UNREACHABLE; } case s_n_llhttp__internal__n_consume_content_length_1: s_n_llhttp__internal__n_consume_content_length_1: { size_t avail; uint64_t need; avail = endp - p; need = state->content_length; if (avail >= need) { p += need; state->content_length = 0; goto s_n_llhttp__internal__n_span_end_llhttp__on_body_1; } state->content_length -= avail; return s_n_llhttp__internal__n_consume_content_length_1; UNREACHABLE; } case s_n_llhttp__internal__n_span_start_llhttp__on_body_1: s_n_llhttp__internal__n_span_start_llhttp__on_body_1: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_body_1; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_body; goto s_n_llhttp__internal__n_consume_content_length_1; UNREACHABLE; } case s_n_llhttp__internal__n_eof: s_n_llhttp__internal__n_eof: { if (p == endp) { return s_n_llhttp__internal__n_eof; } p++; goto s_n_llhttp__internal__n_eof; UNREACHABLE; } case s_n_llhttp__internal__n_span_start_llhttp__on_body_2: s_n_llhttp__internal__n_span_start_llhttp__on_body_2: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_body_2; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_body; goto s_n_llhttp__internal__n_eof; UNREACHABLE; } case s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete: s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete: { switch (llhttp__after_headers_complete(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1; case 2: goto s_n_llhttp__internal__n_invoke_update_content_length_1; case 3: goto s_n_llhttp__internal__n_span_start_llhttp__on_body_1; case 4: goto s_n_llhttp__internal__n_invoke_update_finish_3; case 5: goto s_n_llhttp__internal__n_error_39; default: goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete; } UNREACHABLE; } case s_n_llhttp__internal__n_error_5: s_n_llhttp__internal__n_error_5: { state->error = 0xa; state->reason = "Invalid header field char"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } case s_n_llhttp__internal__n_headers_almost_done: s_n_llhttp__internal__n_headers_almost_done: { if (p == endp) { return s_n_llhttp__internal__n_headers_almost_done; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_invoke_test_flags_1; } default: { goto s_n_llhttp__internal__n_invoke_test_lenient_flags_12; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_field_colon_discard_ws: s_n_llhttp__internal__n_header_field_colon_discard_ws: { if (p == endp) { return s_n_llhttp__internal__n_header_field_colon_discard_ws; } switch (*p) { case ' ': { p++; goto s_n_llhttp__internal__n_header_field_colon_discard_ws; } default: { goto s_n_llhttp__internal__n_header_field_colon; } } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete: s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete: { switch (llhttp__on_header_value_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_header_field_start; case 21: goto s_n_llhttp__internal__n_pause_18; default: goto s_n_llhttp__internal__n_error_48; } UNREACHABLE; } case s_n_llhttp__internal__n_span_start_llhttp__on_header_value: s_n_llhttp__internal__n_span_start_llhttp__on_header_value: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_header_value; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_header_value; goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value; UNREACHABLE; } case s_n_llhttp__internal__n_header_value_discard_lws: s_n_llhttp__internal__n_header_value_discard_lws: { if (p == endp) { return s_n_llhttp__internal__n_header_value_discard_lws; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_invoke_test_lenient_flags_15; } case ' ': { p++; goto s_n_llhttp__internal__n_invoke_test_lenient_flags_15; } default: { goto s_n_llhttp__internal__n_invoke_load_header_state_1; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_value_discard_ws_almost_done: s_n_llhttp__internal__n_header_value_discard_ws_almost_done: { if (p == endp) { return s_n_llhttp__internal__n_header_value_discard_ws_almost_done; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_header_value_discard_lws; } default: { goto s_n_llhttp__internal__n_invoke_test_lenient_flags_16; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_value_lws: s_n_llhttp__internal__n_header_value_lws: { if (p == endp) { return s_n_llhttp__internal__n_header_value_lws; } switch (*p) { case 9: { goto s_n_llhttp__internal__n_invoke_test_lenient_flags_18; } case ' ': { goto s_n_llhttp__internal__n_invoke_test_lenient_flags_18; } default: { goto s_n_llhttp__internal__n_invoke_load_header_state_5; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_value_almost_done: s_n_llhttp__internal__n_header_value_almost_done: { if (p == endp) { return s_n_llhttp__internal__n_header_value_almost_done; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_header_value_lws; } default: { goto s_n_llhttp__internal__n_error_53; } } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_test_lenient_flags_17: s_n_llhttp__internal__n_invoke_test_lenient_flags_17: { switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_header_value_almost_done; default: goto s_n_llhttp__internal__n_error_51; } UNREACHABLE; } case s_n_llhttp__internal__n_header_value_lenient: s_n_llhttp__internal__n_header_value_lenient: { if (p == endp) { return s_n_llhttp__internal__n_header_value_lenient; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4; } case 13: { goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5; } default: { p++; goto s_n_llhttp__internal__n_header_value_lenient; } } UNREACHABLE; } case s_n_llhttp__internal__n_error_54: s_n_llhttp__internal__n_error_54: { state->error = 0xa; state->reason = "Invalid header value char"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } case s_n_llhttp__internal__n_header_value_otherwise: s_n_llhttp__internal__n_header_value_otherwise: { if (p == endp) { return s_n_llhttp__internal__n_header_value_otherwise; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1; } case 13: { goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2; } default: { goto s_n_llhttp__internal__n_invoke_test_lenient_flags_19; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_value_connection_token: s_n_llhttp__internal__n_header_value_connection_token: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; if (p == endp) { return s_n_llhttp__internal__n_header_value_connection_token; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_header_value_connection_token; } case 2: { p++; goto s_n_llhttp__internal__n_header_value_connection; } default: { goto s_n_llhttp__internal__n_header_value_otherwise; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_value_connection_ws: s_n_llhttp__internal__n_header_value_connection_ws: { if (p == endp) { return s_n_llhttp__internal__n_header_value_connection_ws; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_header_value_otherwise; } case 13: { goto s_n_llhttp__internal__n_header_value_otherwise; } case ' ': { p++; goto s_n_llhttp__internal__n_header_value_connection_ws; } case ',': { p++; goto s_n_llhttp__internal__n_invoke_load_header_state_6; } default: { goto s_n_llhttp__internal__n_invoke_update_header_state_5; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_value_connection_1: s_n_llhttp__internal__n_header_value_connection_1: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_value_connection_1; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob2, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_invoke_update_header_state_3; } case kMatchPause: { return s_n_llhttp__internal__n_header_value_connection_1; } case kMatchMismatch: { goto s_n_llhttp__internal__n_header_value_connection_token; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_value_connection_2: s_n_llhttp__internal__n_header_value_connection_2: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_value_connection_2; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob3, 9); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_invoke_update_header_state_6; } case kMatchPause: { return s_n_llhttp__internal__n_header_value_connection_2; } case kMatchMismatch: { goto s_n_llhttp__internal__n_header_value_connection_token; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_value_connection_3: s_n_llhttp__internal__n_header_value_connection_3: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_value_connection_3; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob4, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_invoke_update_header_state_7; } case kMatchPause: { return s_n_llhttp__internal__n_header_value_connection_3; } case kMatchMismatch: { goto s_n_llhttp__internal__n_header_value_connection_token; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_value_connection: s_n_llhttp__internal__n_header_value_connection: { if (p == endp) { return s_n_llhttp__internal__n_header_value_connection; } switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) { case 9: { p++; goto s_n_llhttp__internal__n_header_value_connection; } case ' ': { p++; goto s_n_llhttp__internal__n_header_value_connection; } case 'c': { p++; goto s_n_llhttp__internal__n_header_value_connection_1; } case 'k': { p++; goto s_n_llhttp__internal__n_header_value_connection_2; } case 'u': { p++; goto s_n_llhttp__internal__n_header_value_connection_3; } default: { goto s_n_llhttp__internal__n_header_value_connection_token; } } UNREACHABLE; } case s_n_llhttp__internal__n_error_56: s_n_llhttp__internal__n_error_56: { state->error = 0xb; state->reason = "Content-Length overflow"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } case s_n_llhttp__internal__n_error_57: s_n_llhttp__internal__n_error_57: { state->error = 0xb; state->reason = "Invalid character in Content-Length"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } case s_n_llhttp__internal__n_header_value_content_length_ws: s_n_llhttp__internal__n_header_value_content_length_ws: { if (p == endp) { return s_n_llhttp__internal__n_header_value_content_length_ws; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_invoke_or_flags_17; } case 13: { goto s_n_llhttp__internal__n_invoke_or_flags_17; } case ' ': { p++; goto s_n_llhttp__internal__n_header_value_content_length_ws; } default: { goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_7; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_value_content_length: s_n_llhttp__internal__n_header_value_content_length: { if (p == endp) { return s_n_llhttp__internal__n_header_value_content_length; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } default: { goto s_n_llhttp__internal__n_header_value_content_length_ws; } } UNREACHABLE; } case s_n_llhttp__internal__n_error_59: s_n_llhttp__internal__n_error_59: { state->error = 0xf; state->reason = "Invalid `Transfer-Encoding` header value"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } case s_n_llhttp__internal__n_error_58: s_n_llhttp__internal__n_error_58: { state->error = 0xf; state->reason = "Invalid `Transfer-Encoding` header value"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } case s_n_llhttp__internal__n_header_value_te_token_ows: s_n_llhttp__internal__n_header_value_te_token_ows: { if (p == endp) { return s_n_llhttp__internal__n_header_value_te_token_ows; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_header_value_te_token_ows; } case ' ': { p++; goto s_n_llhttp__internal__n_header_value_te_token_ows; } default: { goto s_n_llhttp__internal__n_header_value_te_chunked; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_value: s_n_llhttp__internal__n_header_value: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; if (p == endp) { return s_n_llhttp__internal__n_header_value; } #ifdef __SSE4_2__ if (endp - p >= 16) { __m128i ranges; __m128i input; int match_len; /* Load input */ input = _mm_loadu_si128((__m128i const*) p); ranges = _mm_loadu_si128((__m128i const*) llparse_blob6); /* Find first character that does not match `ranges` */ match_len = _mm_cmpestri(ranges, 6, input, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | _SIDD_NEGATIVE_POLARITY); if (match_len != 0) { p += match_len; goto s_n_llhttp__internal__n_header_value; } goto s_n_llhttp__internal__n_header_value_otherwise; } #endif /* __SSE4_2__ */ #ifdef __ARM_NEON__ while (endp - p >= 16) { uint8x16_t input; uint8x16_t single; uint8x16_t mask; uint8x8_t narrow; uint64_t match_mask; int match_len; /* Load input */ input = vld1q_u8(p); /* Find first character that does not match `ranges` */ single = vceqq_u8(input, vdupq_n_u8(0x9)); mask = single; single = vandq_u16( vcgeq_u8(input, vdupq_n_u8(' ')), vcleq_u8(input, vdupq_n_u8('~')) ); mask = vorrq_u16(mask, single); single = vandq_u16( vcgeq_u8(input, vdupq_n_u8(0x80)), vcleq_u8(input, vdupq_n_u8(0xff)) ); mask = vorrq_u16(mask, single); narrow = vshrn_n_u16(mask, 4); match_mask = ~vget_lane_u64(vreinterpret_u64_u8(narrow), 0); match_len = __builtin_ctzll(match_mask) >> 2; if (match_len != 16) { p += match_len; goto s_n_llhttp__internal__n_header_value_otherwise; } p += 16; } if (p == endp) { return s_n_llhttp__internal__n_header_value; } #endif /* __ARM_NEON__ */ #ifdef __wasm_simd128__ while (endp - p >= 16) { v128_t input; v128_t mask; v128_t single; int match_len; /* Load input */ input = wasm_v128_load(p); /* Find first character that does not match `ranges` */ single = wasm_i8x16_eq(input, wasm_u8x16_const_splat(0x9)); mask = single; single = wasm_v128_and( wasm_i8x16_ge(input, wasm_u8x16_const_splat(' ')), wasm_i8x16_le(input, wasm_u8x16_const_splat('~')) ); mask = wasm_v128_or(mask, single); single = wasm_v128_and( wasm_i8x16_ge(input, wasm_u8x16_const_splat(0x80)), wasm_i8x16_le(input, wasm_u8x16_const_splat(0xff)) ); mask = wasm_v128_or(mask, single); match_len = __builtin_ctz( ~wasm_i8x16_bitmask(mask) ); if (match_len != 16) { p += match_len; goto s_n_llhttp__internal__n_header_value_otherwise; } p += 16; } if (p == endp) { return s_n_llhttp__internal__n_header_value; } #endif /* __wasm_simd128__ */ switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_header_value; } default: { goto s_n_llhttp__internal__n_header_value_otherwise; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_value_te_token: s_n_llhttp__internal__n_header_value_te_token: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; if (p == endp) { return s_n_llhttp__internal__n_header_value_te_token; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_header_value_te_token; } case 2: { p++; goto s_n_llhttp__internal__n_header_value_te_token_ows; } default: { goto s_n_llhttp__internal__n_invoke_update_header_state_9; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_value_te_chunked_last: s_n_llhttp__internal__n_header_value_te_chunked_last: { if (p == endp) { return s_n_llhttp__internal__n_header_value_te_chunked_last; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_invoke_update_header_state_8; } case 13: { goto s_n_llhttp__internal__n_invoke_update_header_state_8; } case ' ': { p++; goto s_n_llhttp__internal__n_header_value_te_chunked_last; } case ',': { goto s_n_llhttp__internal__n_invoke_load_type_1; } default: { goto s_n_llhttp__internal__n_header_value_te_token; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_value_te_chunked: s_n_llhttp__internal__n_header_value_te_chunked: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_value_te_chunked; } match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob5, 7); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_header_value_te_chunked_last; } case kMatchPause: { return s_n_llhttp__internal__n_header_value_te_chunked; } case kMatchMismatch: { goto s_n_llhttp__internal__n_header_value_te_token; } } UNREACHABLE; } case s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1: s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_header_value; goto s_n_llhttp__internal__n_invoke_load_header_state_3; UNREACHABLE; } case s_n_llhttp__internal__n_header_value_discard_ws: s_n_llhttp__internal__n_header_value_discard_ws: { if (p == endp) { return s_n_llhttp__internal__n_header_value_discard_ws; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_header_value_discard_ws; } case 10: { p++; goto s_n_llhttp__internal__n_invoke_test_lenient_flags_14; } case 13: { p++; goto s_n_llhttp__internal__n_header_value_discard_ws_almost_done; } case ' ': { p++; goto s_n_llhttp__internal__n_header_value_discard_ws; } default: { goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; } } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_load_header_state: s_n_llhttp__internal__n_invoke_load_header_state: { switch (llhttp__internal__c_load_header_state(state, p, endp)) { case 2: goto s_n_llhttp__internal__n_invoke_test_flags_4; case 3: goto s_n_llhttp__internal__n_invoke_test_flags_5; default: goto s_n_llhttp__internal__n_header_value_discard_ws; } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete: s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete: { switch (llhttp__on_header_field_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_load_header_state; case 21: goto s_n_llhttp__internal__n_pause_19; default: goto s_n_llhttp__internal__n_error_45; } UNREACHABLE; } case s_n_llhttp__internal__n_header_field_general_otherwise: s_n_llhttp__internal__n_header_field_general_otherwise: { if (p == endp) { return s_n_llhttp__internal__n_header_field_general_otherwise; } switch (*p) { case ':': { goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_2; } default: { goto s_n_llhttp__internal__n_error_62; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_field_general: s_n_llhttp__internal__n_header_field_general: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_header_field_general; } #ifdef __SSE4_2__ if (endp - p >= 16) { __m128i ranges; __m128i input; int match_len; /* Load input */ input = _mm_loadu_si128((__m128i const*) p); ranges = _mm_loadu_si128((__m128i const*) llparse_blob7); /* Find first character that does not match `ranges` */ match_len = _mm_cmpestri(ranges, 16, input, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | _SIDD_NEGATIVE_POLARITY); if (match_len != 0) { p += match_len; goto s_n_llhttp__internal__n_header_field_general; } ranges = _mm_loadu_si128((__m128i const*) llparse_blob8); /* Find first character that does not match `ranges` */ match_len = _mm_cmpestri(ranges, 2, input, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | _SIDD_NEGATIVE_POLARITY); if (match_len != 0) { p += match_len; goto s_n_llhttp__internal__n_header_field_general; } goto s_n_llhttp__internal__n_header_field_general_otherwise; } #endif /* __SSE4_2__ */ switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_header_field_general; } default: { goto s_n_llhttp__internal__n_header_field_general_otherwise; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_field_colon: s_n_llhttp__internal__n_header_field_colon: { if (p == endp) { return s_n_llhttp__internal__n_header_field_colon; } switch (*p) { case ' ': { goto s_n_llhttp__internal__n_invoke_test_lenient_flags_13; } case ':': { goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1; } default: { goto s_n_llhttp__internal__n_invoke_update_header_state_10; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_field_3: s_n_llhttp__internal__n_header_field_3: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_field_3; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob1, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 1; goto s_n_llhttp__internal__n_invoke_store_header_state; } case kMatchPause: { return s_n_llhttp__internal__n_header_field_3; } case kMatchMismatch: { goto s_n_llhttp__internal__n_invoke_update_header_state_11; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_field_4: s_n_llhttp__internal__n_header_field_4: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_field_4; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob9, 10); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 2; goto s_n_llhttp__internal__n_invoke_store_header_state; } case kMatchPause: { return s_n_llhttp__internal__n_header_field_4; } case kMatchMismatch: { goto s_n_llhttp__internal__n_invoke_update_header_state_11; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_field_2: s_n_llhttp__internal__n_header_field_2: { if (p == endp) { return s_n_llhttp__internal__n_header_field_2; } switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) { case 'n': { p++; goto s_n_llhttp__internal__n_header_field_3; } case 't': { p++; goto s_n_llhttp__internal__n_header_field_4; } default: { goto s_n_llhttp__internal__n_invoke_update_header_state_11; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_field_1: s_n_llhttp__internal__n_header_field_1: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_field_1; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob0, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_header_field_2; } case kMatchPause: { return s_n_llhttp__internal__n_header_field_1; } case kMatchMismatch: { goto s_n_llhttp__internal__n_invoke_update_header_state_11; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_field_5: s_n_llhttp__internal__n_header_field_5: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_field_5; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob10, 15); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 1; goto s_n_llhttp__internal__n_invoke_store_header_state; } case kMatchPause: { return s_n_llhttp__internal__n_header_field_5; } case kMatchMismatch: { goto s_n_llhttp__internal__n_invoke_update_header_state_11; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_field_6: s_n_llhttp__internal__n_header_field_6: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_field_6; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob11, 16); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 3; goto s_n_llhttp__internal__n_invoke_store_header_state; } case kMatchPause: { return s_n_llhttp__internal__n_header_field_6; } case kMatchMismatch: { goto s_n_llhttp__internal__n_invoke_update_header_state_11; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_field_7: s_n_llhttp__internal__n_header_field_7: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_field_7; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob12, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 4; goto s_n_llhttp__internal__n_invoke_store_header_state; } case kMatchPause: { return s_n_llhttp__internal__n_header_field_7; } case kMatchMismatch: { goto s_n_llhttp__internal__n_invoke_update_header_state_11; } } UNREACHABLE; } case s_n_llhttp__internal__n_header_field: s_n_llhttp__internal__n_header_field: { if (p == endp) { return s_n_llhttp__internal__n_header_field; } switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) { case 'c': { p++; goto s_n_llhttp__internal__n_header_field_1; } case 'p': { p++; goto s_n_llhttp__internal__n_header_field_5; } case 't': { p++; goto s_n_llhttp__internal__n_header_field_6; } case 'u': { p++; goto s_n_llhttp__internal__n_header_field_7; } default: { goto s_n_llhttp__internal__n_invoke_update_header_state_11; } } UNREACHABLE; } case s_n_llhttp__internal__n_span_start_llhttp__on_header_field: s_n_llhttp__internal__n_span_start_llhttp__on_header_field: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_header_field; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_header_field; goto s_n_llhttp__internal__n_header_field; UNREACHABLE; } case s_n_llhttp__internal__n_header_field_start: s_n_llhttp__internal__n_header_field_start: { if (p == endp) { return s_n_llhttp__internal__n_header_field_start; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_invoke_test_lenient_flags_1; } case 13: { p++; goto s_n_llhttp__internal__n_headers_almost_done; } case ':': { goto s_n_llhttp__internal__n_error_44; } default: { goto s_n_llhttp__internal__n_span_start_llhttp__on_header_field; } } UNREACHABLE; } case s_n_llhttp__internal__n_headers_start: s_n_llhttp__internal__n_headers_start: { if (p == endp) { return s_n_llhttp__internal__n_headers_start; } switch (*p) { case ' ': { p++; goto s_n_llhttp__internal__n_invoke_test_lenient_flags; } default: { goto s_n_llhttp__internal__n_header_field_start; } } UNREACHABLE; } case s_n_llhttp__internal__n_url_to_http_09: s_n_llhttp__internal__n_url_to_http_09: { if (p == endp) { return s_n_llhttp__internal__n_url_to_http_09; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_error_2; } case 12: { p++; goto s_n_llhttp__internal__n_error_2; } default: { goto s_n_llhttp__internal__n_invoke_update_http_major; } } UNREACHABLE; } case s_n_llhttp__internal__n_url_skip_to_http09: s_n_llhttp__internal__n_url_skip_to_http09: { if (p == endp) { return s_n_llhttp__internal__n_url_skip_to_http09; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_error_2; } case 12: { p++; goto s_n_llhttp__internal__n_error_2; } default: { p++; goto s_n_llhttp__internal__n_url_to_http_09; } } UNREACHABLE; } case s_n_llhttp__internal__n_url_skip_lf_to_http09_1: s_n_llhttp__internal__n_url_skip_lf_to_http09_1: { if (p == endp) { return s_n_llhttp__internal__n_url_skip_lf_to_http09_1; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_url_to_http_09; } default: { goto s_n_llhttp__internal__n_error_63; } } UNREACHABLE; } case s_n_llhttp__internal__n_url_skip_lf_to_http09: s_n_llhttp__internal__n_url_skip_lf_to_http09: { if (p == endp) { return s_n_llhttp__internal__n_url_skip_lf_to_http09; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_error_2; } case 12: { p++; goto s_n_llhttp__internal__n_error_2; } case 13: { p++; goto s_n_llhttp__internal__n_url_skip_lf_to_http09_1; } default: { goto s_n_llhttp__internal__n_error_63; } } UNREACHABLE; } case s_n_llhttp__internal__n_req_pri_upgrade: s_n_llhttp__internal__n_req_pri_upgrade: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_req_pri_upgrade; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob14, 10); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_error_72; } case kMatchPause: { return s_n_llhttp__internal__n_req_pri_upgrade; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_73; } } UNREACHABLE; } case s_n_llhttp__internal__n_req_http_complete_crlf: s_n_llhttp__internal__n_req_http_complete_crlf: { if (p == endp) { return s_n_llhttp__internal__n_req_http_complete_crlf; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_headers_start; } default: { goto s_n_llhttp__internal__n_invoke_test_lenient_flags_26; } } UNREACHABLE; } case s_n_llhttp__internal__n_req_http_complete: s_n_llhttp__internal__n_req_http_complete: { if (p == endp) { return s_n_llhttp__internal__n_req_http_complete; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_invoke_test_lenient_flags_25; } case 13: { p++; goto s_n_llhttp__internal__n_req_http_complete_crlf; } default: { goto s_n_llhttp__internal__n_error_71; } } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_load_method_1: s_n_llhttp__internal__n_invoke_load_method_1: { switch (llhttp__internal__c_load_method(state, p, endp)) { case 34: goto s_n_llhttp__internal__n_req_pri_upgrade; default: goto s_n_llhttp__internal__n_req_http_complete; } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_llhttp__on_version_complete: s_n_llhttp__internal__n_invoke_llhttp__on_version_complete: { switch (llhttp__on_version_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_load_method_1; case 21: goto s_n_llhttp__internal__n_pause_21; default: goto s_n_llhttp__internal__n_error_68; } UNREACHABLE; } case s_n_llhttp__internal__n_error_67: s_n_llhttp__internal__n_error_67: { state->error = 0x9; state->reason = "Invalid HTTP version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } case s_n_llhttp__internal__n_error_74: s_n_llhttp__internal__n_error_74: { state->error = 0x9; state->reason = "Invalid minor version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } case s_n_llhttp__internal__n_req_http_minor: s_n_llhttp__internal__n_req_http_minor: { if (p == endp) { return s_n_llhttp__internal__n_req_http_minor; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_store_http_minor; } default: { goto s_n_llhttp__internal__n_span_end_llhttp__on_version_2; } } UNREACHABLE; } case s_n_llhttp__internal__n_error_75: s_n_llhttp__internal__n_error_75: { state->error = 0x9; state->reason = "Expected dot"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } case s_n_llhttp__internal__n_req_http_dot: s_n_llhttp__internal__n_req_http_dot: { if (p == endp) { return s_n_llhttp__internal__n_req_http_dot; } switch (*p) { case '.': { p++; goto s_n_llhttp__internal__n_req_http_minor; } default: { goto s_n_llhttp__internal__n_span_end_llhttp__on_version_3; } } UNREACHABLE; } case s_n_llhttp__internal__n_error_76: s_n_llhttp__internal__n_error_76: { state->error = 0x9; state->reason = "Invalid major version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } case s_n_llhttp__internal__n_req_http_major: s_n_llhttp__internal__n_req_http_major: { if (p == endp) { return s_n_llhttp__internal__n_req_http_major; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_store_http_major; } default: { goto s_n_llhttp__internal__n_span_end_llhttp__on_version_4; } } UNREACHABLE; } case s_n_llhttp__internal__n_span_start_llhttp__on_version: s_n_llhttp__internal__n_span_start_llhttp__on_version: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_version; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_version; goto s_n_llhttp__internal__n_req_http_major; UNREACHABLE; } case s_n_llhttp__internal__n_req_after_protocol: s_n_llhttp__internal__n_req_after_protocol: { if (p == endp) { return s_n_llhttp__internal__n_req_after_protocol; } switch (*p) { case '/': { p++; goto s_n_llhttp__internal__n_span_start_llhttp__on_version; } default: { goto s_n_llhttp__internal__n_error_77; } } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_load_method: s_n_llhttp__internal__n_invoke_load_method: { switch (llhttp__internal__c_load_method(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_req_after_protocol; case 1: goto s_n_llhttp__internal__n_req_after_protocol; case 2: goto s_n_llhttp__internal__n_req_after_protocol; case 3: goto s_n_llhttp__internal__n_req_after_protocol; case 4: goto s_n_llhttp__internal__n_req_after_protocol; case 5: goto s_n_llhttp__internal__n_req_after_protocol; case 6: goto s_n_llhttp__internal__n_req_after_protocol; case 7: goto s_n_llhttp__internal__n_req_after_protocol; case 8: goto s_n_llhttp__internal__n_req_after_protocol; case 9: goto s_n_llhttp__internal__n_req_after_protocol; case 10: goto s_n_llhttp__internal__n_req_after_protocol; case 11: goto s_n_llhttp__internal__n_req_after_protocol; case 12: goto s_n_llhttp__internal__n_req_after_protocol; case 13: goto s_n_llhttp__internal__n_req_after_protocol; case 14: goto s_n_llhttp__internal__n_req_after_protocol; case 15: goto s_n_llhttp__internal__n_req_after_protocol; case 16: goto s_n_llhttp__internal__n_req_after_protocol; case 17: goto s_n_llhttp__internal__n_req_after_protocol; case 18: goto s_n_llhttp__internal__n_req_after_protocol; case 19: goto s_n_llhttp__internal__n_req_after_protocol; case 20: goto s_n_llhttp__internal__n_req_after_protocol; case 21: goto s_n_llhttp__internal__n_req_after_protocol; case 22: goto s_n_llhttp__internal__n_req_after_protocol; case 23: goto s_n_llhttp__internal__n_req_after_protocol; case 24: goto s_n_llhttp__internal__n_req_after_protocol; case 25: goto s_n_llhttp__internal__n_req_after_protocol; case 26: goto s_n_llhttp__internal__n_req_after_protocol; case 27: goto s_n_llhttp__internal__n_req_after_protocol; case 28: goto s_n_llhttp__internal__n_req_after_protocol; case 29: goto s_n_llhttp__internal__n_req_after_protocol; case 30: goto s_n_llhttp__internal__n_req_after_protocol; case 31: goto s_n_llhttp__internal__n_req_after_protocol; case 32: goto s_n_llhttp__internal__n_req_after_protocol; case 33: goto s_n_llhttp__internal__n_req_after_protocol; case 34: goto s_n_llhttp__internal__n_req_after_protocol; case 46: goto s_n_llhttp__internal__n_req_after_protocol; default: goto s_n_llhttp__internal__n_error_66; } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete: s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete: { switch (llhttp__on_protocol_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_load_method; case 21: goto s_n_llhttp__internal__n_pause_22; default: goto s_n_llhttp__internal__n_error_65; } UNREACHABLE; } case s_n_llhttp__internal__n_error_82: s_n_llhttp__internal__n_error_82: { state->error = 0x8; state->reason = "Expected HTTP/, RTSP/ or ICE/"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } case s_n_llhttp__internal__n_req_after_http_start_1: s_n_llhttp__internal__n_req_after_http_start_1: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_req_after_http_start_1; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob13, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol; } case kMatchPause: { return s_n_llhttp__internal__n_req_after_http_start_1; } case kMatchMismatch: { goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3; } } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_load_method_2: s_n_llhttp__internal__n_invoke_load_method_2: { switch (llhttp__internal__c_load_method(state, p, endp)) { case 33: goto s_n_llhttp__internal__n_req_after_protocol; default: goto s_n_llhttp__internal__n_error_79; } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1: s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1: { switch (llhttp__on_protocol_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_load_method_2; case 21: goto s_n_llhttp__internal__n_pause_23; default: goto s_n_llhttp__internal__n_error_78; } UNREACHABLE; } case s_n_llhttp__internal__n_req_after_http_start_2: s_n_llhttp__internal__n_req_after_http_start_2: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_req_after_http_start_2; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob15, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_1; } case kMatchPause: { return s_n_llhttp__internal__n_req_after_http_start_2; } case kMatchMismatch: { goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3; } } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_load_method_3: s_n_llhttp__internal__n_invoke_load_method_3: { switch (llhttp__internal__c_load_method(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_req_after_protocol; case 3: goto s_n_llhttp__internal__n_req_after_protocol; case 6: goto s_n_llhttp__internal__n_req_after_protocol; case 35: goto s_n_llhttp__internal__n_req_after_protocol; case 36: goto s_n_llhttp__internal__n_req_after_protocol; case 37: goto s_n_llhttp__internal__n_req_after_protocol; case 38: goto s_n_llhttp__internal__n_req_after_protocol; case 39: goto s_n_llhttp__internal__n_req_after_protocol; case 40: goto s_n_llhttp__internal__n_req_after_protocol; case 41: goto s_n_llhttp__internal__n_req_after_protocol; case 42: goto s_n_llhttp__internal__n_req_after_protocol; case 43: goto s_n_llhttp__internal__n_req_after_protocol; case 44: goto s_n_llhttp__internal__n_req_after_protocol; case 45: goto s_n_llhttp__internal__n_req_after_protocol; default: goto s_n_llhttp__internal__n_error_81; } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2: s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2: { switch (llhttp__on_protocol_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_load_method_3; case 21: goto s_n_llhttp__internal__n_pause_24; default: goto s_n_llhttp__internal__n_error_80; } UNREACHABLE; } case s_n_llhttp__internal__n_req_after_http_start_3: s_n_llhttp__internal__n_req_after_http_start_3: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_req_after_http_start_3; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob16, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_2; } case kMatchPause: { return s_n_llhttp__internal__n_req_after_http_start_3; } case kMatchMismatch: { goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3; } } UNREACHABLE; } case s_n_llhttp__internal__n_req_after_http_start: s_n_llhttp__internal__n_req_after_http_start: { if (p == endp) { return s_n_llhttp__internal__n_req_after_http_start; } switch (*p) { case 'H': { p++; goto s_n_llhttp__internal__n_req_after_http_start_1; } case 'I': { p++; goto s_n_llhttp__internal__n_req_after_http_start_2; } case 'R': { p++; goto s_n_llhttp__internal__n_req_after_http_start_3; } default: { goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3; } } UNREACHABLE; } case s_n_llhttp__internal__n_span_start_llhttp__on_protocol: s_n_llhttp__internal__n_span_start_llhttp__on_protocol: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_protocol; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_protocol; goto s_n_llhttp__internal__n_req_after_http_start; UNREACHABLE; } case s_n_llhttp__internal__n_req_http_start: s_n_llhttp__internal__n_req_http_start: { if (p == endp) { return s_n_llhttp__internal__n_req_http_start; } switch (*p) { case ' ': { p++; goto s_n_llhttp__internal__n_req_http_start; } default: { goto s_n_llhttp__internal__n_span_start_llhttp__on_protocol; } } UNREACHABLE; } case s_n_llhttp__internal__n_url_to_http: s_n_llhttp__internal__n_url_to_http: { if (p == endp) { return s_n_llhttp__internal__n_url_to_http; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_error_2; } case 12: { p++; goto s_n_llhttp__internal__n_error_2; } default: { goto s_n_llhttp__internal__n_invoke_llhttp__on_url_complete_1; } } UNREACHABLE; } case s_n_llhttp__internal__n_url_skip_to_http: s_n_llhttp__internal__n_url_skip_to_http: { if (p == endp) { return s_n_llhttp__internal__n_url_skip_to_http; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_error_2; } case 12: { p++; goto s_n_llhttp__internal__n_error_2; } default: { p++; goto s_n_llhttp__internal__n_url_to_http; } } UNREACHABLE; } case s_n_llhttp__internal__n_url_fragment: s_n_llhttp__internal__n_url_fragment: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_url_fragment; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_error_2; } case 2: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_6; } case 3: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_7; } case 4: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_8; } case 5: { p++; goto s_n_llhttp__internal__n_url_fragment; } default: { goto s_n_llhttp__internal__n_error_83; } } UNREACHABLE; } case s_n_llhttp__internal__n_span_end_stub_query_3: s_n_llhttp__internal__n_span_end_stub_query_3: { if (p == endp) { return s_n_llhttp__internal__n_span_end_stub_query_3; } p++; goto s_n_llhttp__internal__n_url_fragment; UNREACHABLE; } case s_n_llhttp__internal__n_url_query: s_n_llhttp__internal__n_url_query: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_url_query; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_error_2; } case 2: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_9; } case 3: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_10; } case 4: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_11; } case 5: { p++; goto s_n_llhttp__internal__n_url_query; } case 6: { goto s_n_llhttp__internal__n_span_end_stub_query_3; } default: { goto s_n_llhttp__internal__n_error_84; } } UNREACHABLE; } case s_n_llhttp__internal__n_url_query_or_fragment: s_n_llhttp__internal__n_url_query_or_fragment: { if (p == endp) { return s_n_llhttp__internal__n_url_query_or_fragment; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_error_2; } case 10: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_3; } case 12: { p++; goto s_n_llhttp__internal__n_error_2; } case 13: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_4; } case ' ': { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_5; } case '#': { p++; goto s_n_llhttp__internal__n_url_fragment; } case '?': { p++; goto s_n_llhttp__internal__n_url_query; } default: { goto s_n_llhttp__internal__n_error_85; } } UNREACHABLE; } case s_n_llhttp__internal__n_url_path: s_n_llhttp__internal__n_url_path: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_url_path; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_error_2; } case 2: { p++; goto s_n_llhttp__internal__n_url_path; } default: { goto s_n_llhttp__internal__n_url_query_or_fragment; } } UNREACHABLE; } case s_n_llhttp__internal__n_span_start_stub_path_2: s_n_llhttp__internal__n_span_start_stub_path_2: { if (p == endp) { return s_n_llhttp__internal__n_span_start_stub_path_2; } p++; goto s_n_llhttp__internal__n_url_path; UNREACHABLE; } case s_n_llhttp__internal__n_span_start_stub_path: s_n_llhttp__internal__n_span_start_stub_path: { if (p == endp) { return s_n_llhttp__internal__n_span_start_stub_path; } p++; goto s_n_llhttp__internal__n_url_path; UNREACHABLE; } case s_n_llhttp__internal__n_span_start_stub_path_1: s_n_llhttp__internal__n_span_start_stub_path_1: { if (p == endp) { return s_n_llhttp__internal__n_span_start_stub_path_1; } p++; goto s_n_llhttp__internal__n_url_path; UNREACHABLE; } case s_n_llhttp__internal__n_url_server_with_at: s_n_llhttp__internal__n_url_server_with_at: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 7, 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_url_server_with_at; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_error_2; } case 2: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_12; } case 3: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_13; } case 4: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_14; } case 5: { p++; goto s_n_llhttp__internal__n_url_server; } case 6: { goto s_n_llhttp__internal__n_span_start_stub_path_1; } case 7: { p++; goto s_n_llhttp__internal__n_url_query; } case 8: { p++; goto s_n_llhttp__internal__n_error_86; } default: { goto s_n_llhttp__internal__n_error_87; } } UNREACHABLE; } case s_n_llhttp__internal__n_url_server: s_n_llhttp__internal__n_url_server: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 7, 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_url_server; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_error_2; } case 2: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url; } case 3: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_1; } case 4: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_2; } case 5: { p++; goto s_n_llhttp__internal__n_url_server; } case 6: { goto s_n_llhttp__internal__n_span_start_stub_path; } case 7: { p++; goto s_n_llhttp__internal__n_url_query; } case 8: { p++; goto s_n_llhttp__internal__n_url_server_with_at; } default: { goto s_n_llhttp__internal__n_error_88; } } UNREACHABLE; } case s_n_llhttp__internal__n_url_schema_delim_1: s_n_llhttp__internal__n_url_schema_delim_1: { if (p == endp) { return s_n_llhttp__internal__n_url_schema_delim_1; } switch (*p) { case '/': { p++; goto s_n_llhttp__internal__n_url_server; } default: { goto s_n_llhttp__internal__n_error_89; } } UNREACHABLE; } case s_n_llhttp__internal__n_url_schema_delim: s_n_llhttp__internal__n_url_schema_delim: { if (p == endp) { return s_n_llhttp__internal__n_url_schema_delim; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_error_2; } case 10: { p++; goto s_n_llhttp__internal__n_error_2; } case 12: { p++; goto s_n_llhttp__internal__n_error_2; } case 13: { p++; goto s_n_llhttp__internal__n_error_2; } case ' ': { p++; goto s_n_llhttp__internal__n_error_2; } case '/': { p++; goto s_n_llhttp__internal__n_url_schema_delim_1; } default: { goto s_n_llhttp__internal__n_error_89; } } UNREACHABLE; } case s_n_llhttp__internal__n_span_end_stub_schema: s_n_llhttp__internal__n_span_end_stub_schema: { if (p == endp) { return s_n_llhttp__internal__n_span_end_stub_schema; } p++; goto s_n_llhttp__internal__n_url_schema_delim; UNREACHABLE; } case s_n_llhttp__internal__n_url_schema: s_n_llhttp__internal__n_url_schema: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_url_schema; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_error_2; } case 2: { goto s_n_llhttp__internal__n_span_end_stub_schema; } case 3: { p++; goto s_n_llhttp__internal__n_url_schema; } default: { goto s_n_llhttp__internal__n_error_90; } } UNREACHABLE; } case s_n_llhttp__internal__n_url_start: s_n_llhttp__internal__n_url_start: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_url_start; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_error_2; } case 2: { goto s_n_llhttp__internal__n_span_start_stub_path_2; } case 3: { goto s_n_llhttp__internal__n_url_schema; } default: { goto s_n_llhttp__internal__n_error_91; } } UNREACHABLE; } case s_n_llhttp__internal__n_span_start_llhttp__on_url_1: s_n_llhttp__internal__n_span_start_llhttp__on_url_1: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_url_1; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_url; goto s_n_llhttp__internal__n_url_start; UNREACHABLE; } case s_n_llhttp__internal__n_url_entry_normal: s_n_llhttp__internal__n_url_entry_normal: { if (p == endp) { return s_n_llhttp__internal__n_url_entry_normal; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_error_2; } case 12: { p++; goto s_n_llhttp__internal__n_error_2; } default: { goto s_n_llhttp__internal__n_span_start_llhttp__on_url_1; } } UNREACHABLE; } case s_n_llhttp__internal__n_span_start_llhttp__on_url: s_n_llhttp__internal__n_span_start_llhttp__on_url: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_url; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_url; goto s_n_llhttp__internal__n_url_server; UNREACHABLE; } case s_n_llhttp__internal__n_url_entry_connect: s_n_llhttp__internal__n_url_entry_connect: { if (p == endp) { return s_n_llhttp__internal__n_url_entry_connect; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_error_2; } case 12: { p++; goto s_n_llhttp__internal__n_error_2; } default: { goto s_n_llhttp__internal__n_span_start_llhttp__on_url; } } UNREACHABLE; } case s_n_llhttp__internal__n_req_spaces_before_url: s_n_llhttp__internal__n_req_spaces_before_url: { if (p == endp) { return s_n_llhttp__internal__n_req_spaces_before_url; } switch (*p) { case ' ': { p++; goto s_n_llhttp__internal__n_req_spaces_before_url; } default: { goto s_n_llhttp__internal__n_invoke_is_equal_method; } } UNREACHABLE; } case s_n_llhttp__internal__n_req_first_space_before_url: s_n_llhttp__internal__n_req_first_space_before_url: { if (p == endp) { return s_n_llhttp__internal__n_req_first_space_before_url; } switch (*p) { case ' ': { p++; goto s_n_llhttp__internal__n_req_spaces_before_url; } default: { goto s_n_llhttp__internal__n_error_92; } } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1: s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1: { switch (llhttp__on_method_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_req_first_space_before_url; case 21: goto s_n_llhttp__internal__n_pause_29; default: goto s_n_llhttp__internal__n_error_111; } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_2: s_n_llhttp__internal__n_after_start_req_2: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_2; } switch (*p) { case 'L': { p++; match = 19; goto s_n_llhttp__internal__n_invoke_store_method_1; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_3: s_n_llhttp__internal__n_after_start_req_3: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_3; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob17, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 36; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_3; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_1: s_n_llhttp__internal__n_after_start_req_1: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_1; } switch (*p) { case 'C': { p++; goto s_n_llhttp__internal__n_after_start_req_2; } case 'N': { p++; goto s_n_llhttp__internal__n_after_start_req_3; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_4: s_n_llhttp__internal__n_after_start_req_4: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_4; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob18, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 16; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_4; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_6: s_n_llhttp__internal__n_after_start_req_6: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_6; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob19, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 22; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_6; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_8: s_n_llhttp__internal__n_after_start_req_8: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_8; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob20, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 5; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_8; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_9: s_n_llhttp__internal__n_after_start_req_9: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_9; } switch (*p) { case 'Y': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_store_method_1; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_7: s_n_llhttp__internal__n_after_start_req_7: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_7; } switch (*p) { case 'N': { p++; goto s_n_llhttp__internal__n_after_start_req_8; } case 'P': { p++; goto s_n_llhttp__internal__n_after_start_req_9; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_5: s_n_llhttp__internal__n_after_start_req_5: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_5; } switch (*p) { case 'H': { p++; goto s_n_llhttp__internal__n_after_start_req_6; } case 'O': { p++; goto s_n_llhttp__internal__n_after_start_req_7; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_12: s_n_llhttp__internal__n_after_start_req_12: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_12; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob21, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 0; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_12; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_13: s_n_llhttp__internal__n_after_start_req_13: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_13; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob22, 5); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 35; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_13; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_11: s_n_llhttp__internal__n_after_start_req_11: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_11; } switch (*p) { case 'L': { p++; goto s_n_llhttp__internal__n_after_start_req_12; } case 'S': { p++; goto s_n_llhttp__internal__n_after_start_req_13; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_10: s_n_llhttp__internal__n_after_start_req_10: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_10; } switch (*p) { case 'E': { p++; goto s_n_llhttp__internal__n_after_start_req_11; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_14: s_n_llhttp__internal__n_after_start_req_14: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_14; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob23, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 45; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_14; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_17: s_n_llhttp__internal__n_after_start_req_17: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_17; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob25, 9); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 41; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_17; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_16: s_n_llhttp__internal__n_after_start_req_16: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_16; } switch (*p) { case '_': { p++; goto s_n_llhttp__internal__n_after_start_req_17; } default: { match = 1; goto s_n_llhttp__internal__n_invoke_store_method_1; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_15: s_n_llhttp__internal__n_after_start_req_15: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_15; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob24, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_after_start_req_16; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_15; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_18: s_n_llhttp__internal__n_after_start_req_18: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_18; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob26, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 2; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_18; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_20: s_n_llhttp__internal__n_after_start_req_20: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_20; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob27, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 31; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_20; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_21: s_n_llhttp__internal__n_after_start_req_21: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_21; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob28, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 9; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_21; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_19: s_n_llhttp__internal__n_after_start_req_19: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_19; } switch (*p) { case 'I': { p++; goto s_n_llhttp__internal__n_after_start_req_20; } case 'O': { p++; goto s_n_llhttp__internal__n_after_start_req_21; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_23: s_n_llhttp__internal__n_after_start_req_23: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_23; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob29, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 24; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_23; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_24: s_n_llhttp__internal__n_after_start_req_24: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_24; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob30, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 23; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_24; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_26: s_n_llhttp__internal__n_after_start_req_26: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_26; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob31, 7); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 21; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_26; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_28: s_n_llhttp__internal__n_after_start_req_28: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_28; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob32, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 30; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_28; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_29: s_n_llhttp__internal__n_after_start_req_29: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_29; } switch (*p) { case 'L': { p++; match = 10; goto s_n_llhttp__internal__n_invoke_store_method_1; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_27: s_n_llhttp__internal__n_after_start_req_27: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_27; } switch (*p) { case 'A': { p++; goto s_n_llhttp__internal__n_after_start_req_28; } case 'O': { p++; goto s_n_llhttp__internal__n_after_start_req_29; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_25: s_n_llhttp__internal__n_after_start_req_25: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_25; } switch (*p) { case 'A': { p++; goto s_n_llhttp__internal__n_after_start_req_26; } case 'C': { p++; goto s_n_llhttp__internal__n_after_start_req_27; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_30: s_n_llhttp__internal__n_after_start_req_30: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_30; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob33, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 11; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_30; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_22: s_n_llhttp__internal__n_after_start_req_22: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_22; } switch (*p) { case '-': { p++; goto s_n_llhttp__internal__n_after_start_req_23; } case 'E': { p++; goto s_n_llhttp__internal__n_after_start_req_24; } case 'K': { p++; goto s_n_llhttp__internal__n_after_start_req_25; } case 'O': { p++; goto s_n_llhttp__internal__n_after_start_req_30; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_31: s_n_llhttp__internal__n_after_start_req_31: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_31; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob34, 5); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 25; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_31; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_32: s_n_llhttp__internal__n_after_start_req_32: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_32; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob35, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 6; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_32; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_35: s_n_llhttp__internal__n_after_start_req_35: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_35; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob36, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 28; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_35; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_36: s_n_llhttp__internal__n_after_start_req_36: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_36; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob37, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 39; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_36; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_34: s_n_llhttp__internal__n_after_start_req_34: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_34; } switch (*p) { case 'T': { p++; goto s_n_llhttp__internal__n_after_start_req_35; } case 'U': { p++; goto s_n_llhttp__internal__n_after_start_req_36; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_37: s_n_llhttp__internal__n_after_start_req_37: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_37; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob38, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 38; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_37; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_38: s_n_llhttp__internal__n_after_start_req_38: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_38; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob39, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 3; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_38; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_42: s_n_llhttp__internal__n_after_start_req_42: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_42; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob40, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 12; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_42; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_43: s_n_llhttp__internal__n_after_start_req_43: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_43; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob41, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 13; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_43; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_41: s_n_llhttp__internal__n_after_start_req_41: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_41; } switch (*p) { case 'F': { p++; goto s_n_llhttp__internal__n_after_start_req_42; } case 'P': { p++; goto s_n_llhttp__internal__n_after_start_req_43; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_40: s_n_llhttp__internal__n_after_start_req_40: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_40; } switch (*p) { case 'P': { p++; goto s_n_llhttp__internal__n_after_start_req_41; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_39: s_n_llhttp__internal__n_after_start_req_39: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_39; } switch (*p) { case 'I': { p++; match = 34; goto s_n_llhttp__internal__n_invoke_store_method_1; } case 'O': { p++; goto s_n_llhttp__internal__n_after_start_req_40; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_45: s_n_llhttp__internal__n_after_start_req_45: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_45; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob42, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 29; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_45; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_44: s_n_llhttp__internal__n_after_start_req_44: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_44; } switch (*p) { case 'R': { p++; goto s_n_llhttp__internal__n_after_start_req_45; } case 'T': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_store_method_1; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_33: s_n_llhttp__internal__n_after_start_req_33: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_33; } switch (*p) { case 'A': { p++; goto s_n_llhttp__internal__n_after_start_req_34; } case 'L': { p++; goto s_n_llhttp__internal__n_after_start_req_37; } case 'O': { p++; goto s_n_llhttp__internal__n_after_start_req_38; } case 'R': { p++; goto s_n_llhttp__internal__n_after_start_req_39; } case 'U': { p++; goto s_n_llhttp__internal__n_after_start_req_44; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_46: s_n_llhttp__internal__n_after_start_req_46: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_46; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob43, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 46; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_46; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_49: s_n_llhttp__internal__n_after_start_req_49: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_49; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob44, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 17; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_49; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_50: s_n_llhttp__internal__n_after_start_req_50: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_50; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob45, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 44; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_50; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_51: s_n_llhttp__internal__n_after_start_req_51: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_51; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob46, 5); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 43; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_51; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_52: s_n_llhttp__internal__n_after_start_req_52: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_52; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob47, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 20; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_52; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_48: s_n_llhttp__internal__n_after_start_req_48: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_48; } switch (*p) { case 'B': { p++; goto s_n_llhttp__internal__n_after_start_req_49; } case 'C': { p++; goto s_n_llhttp__internal__n_after_start_req_50; } case 'D': { p++; goto s_n_llhttp__internal__n_after_start_req_51; } case 'P': { p++; goto s_n_llhttp__internal__n_after_start_req_52; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_47: s_n_llhttp__internal__n_after_start_req_47: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_47; } switch (*p) { case 'E': { p++; goto s_n_llhttp__internal__n_after_start_req_48; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_55: s_n_llhttp__internal__n_after_start_req_55: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_55; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob48, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 14; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_55; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_57: s_n_llhttp__internal__n_after_start_req_57: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_57; } switch (*p) { case 'P': { p++; match = 37; goto s_n_llhttp__internal__n_invoke_store_method_1; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_58: s_n_llhttp__internal__n_after_start_req_58: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_58; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob49, 9); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 42; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_58; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_56: s_n_llhttp__internal__n_after_start_req_56: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_56; } switch (*p) { case 'U': { p++; goto s_n_llhttp__internal__n_after_start_req_57; } case '_': { p++; goto s_n_llhttp__internal__n_after_start_req_58; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_54: s_n_llhttp__internal__n_after_start_req_54: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_54; } switch (*p) { case 'A': { p++; goto s_n_llhttp__internal__n_after_start_req_55; } case 'T': { p++; goto s_n_llhttp__internal__n_after_start_req_56; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_59: s_n_llhttp__internal__n_after_start_req_59: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_59; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob50, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 33; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_59; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_60: s_n_llhttp__internal__n_after_start_req_60: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_60; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob51, 7); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 26; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_60; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_53: s_n_llhttp__internal__n_after_start_req_53: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_53; } switch (*p) { case 'E': { p++; goto s_n_llhttp__internal__n_after_start_req_54; } case 'O': { p++; goto s_n_llhttp__internal__n_after_start_req_59; } case 'U': { p++; goto s_n_llhttp__internal__n_after_start_req_60; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_62: s_n_llhttp__internal__n_after_start_req_62: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_62; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob52, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 40; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_62; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_63: s_n_llhttp__internal__n_after_start_req_63: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_63; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob53, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 7; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_63; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_61: s_n_llhttp__internal__n_after_start_req_61: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_61; } switch (*p) { case 'E': { p++; goto s_n_llhttp__internal__n_after_start_req_62; } case 'R': { p++; goto s_n_llhttp__internal__n_after_start_req_63; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_66: s_n_llhttp__internal__n_after_start_req_66: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_66; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob54, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 18; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_66; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_68: s_n_llhttp__internal__n_after_start_req_68: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_68; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob55, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 32; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_68; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_69: s_n_llhttp__internal__n_after_start_req_69: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_69; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob56, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 15; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_69; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_67: s_n_llhttp__internal__n_after_start_req_67: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_67; } switch (*p) { case 'I': { p++; goto s_n_llhttp__internal__n_after_start_req_68; } case 'O': { p++; goto s_n_llhttp__internal__n_after_start_req_69; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_70: s_n_llhttp__internal__n_after_start_req_70: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_after_start_req_70; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob57, 8); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 27; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_after_start_req_70; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_65: s_n_llhttp__internal__n_after_start_req_65: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_65; } switch (*p) { case 'B': { p++; goto s_n_llhttp__internal__n_after_start_req_66; } case 'L': { p++; goto s_n_llhttp__internal__n_after_start_req_67; } case 'S': { p++; goto s_n_llhttp__internal__n_after_start_req_70; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req_64: s_n_llhttp__internal__n_after_start_req_64: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req_64; } switch (*p) { case 'N': { p++; goto s_n_llhttp__internal__n_after_start_req_65; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_after_start_req: s_n_llhttp__internal__n_after_start_req: { if (p == endp) { return s_n_llhttp__internal__n_after_start_req; } switch (*p) { case 'A': { p++; goto s_n_llhttp__internal__n_after_start_req_1; } case 'B': { p++; goto s_n_llhttp__internal__n_after_start_req_4; } case 'C': { p++; goto s_n_llhttp__internal__n_after_start_req_5; } case 'D': { p++; goto s_n_llhttp__internal__n_after_start_req_10; } case 'F': { p++; goto s_n_llhttp__internal__n_after_start_req_14; } case 'G': { p++; goto s_n_llhttp__internal__n_after_start_req_15; } case 'H': { p++; goto s_n_llhttp__internal__n_after_start_req_18; } case 'L': { p++; goto s_n_llhttp__internal__n_after_start_req_19; } case 'M': { p++; goto s_n_llhttp__internal__n_after_start_req_22; } case 'N': { p++; goto s_n_llhttp__internal__n_after_start_req_31; } case 'O': { p++; goto s_n_llhttp__internal__n_after_start_req_32; } case 'P': { p++; goto s_n_llhttp__internal__n_after_start_req_33; } case 'Q': { p++; goto s_n_llhttp__internal__n_after_start_req_46; } case 'R': { p++; goto s_n_llhttp__internal__n_after_start_req_47; } case 'S': { p++; goto s_n_llhttp__internal__n_after_start_req_53; } case 'T': { p++; goto s_n_llhttp__internal__n_after_start_req_61; } case 'U': { p++; goto s_n_llhttp__internal__n_after_start_req_64; } default: { goto s_n_llhttp__internal__n_error_112; } } UNREACHABLE; } case s_n_llhttp__internal__n_span_start_llhttp__on_method_1: s_n_llhttp__internal__n_span_start_llhttp__on_method_1: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_method_1; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_method; goto s_n_llhttp__internal__n_after_start_req; UNREACHABLE; } case s_n_llhttp__internal__n_res_line_almost_done: s_n_llhttp__internal__n_res_line_almost_done: { if (p == endp) { return s_n_llhttp__internal__n_res_line_almost_done; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; } case 13: { p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; } default: { goto s_n_llhttp__internal__n_invoke_test_lenient_flags_29; } } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_test_lenient_flags_30: s_n_llhttp__internal__n_invoke_test_lenient_flags_30: { switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; default: goto s_n_llhttp__internal__n_error_98; } UNREACHABLE; } case s_n_llhttp__internal__n_res_status: s_n_llhttp__internal__n_res_status: { if (p == endp) { return s_n_llhttp__internal__n_res_status; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_span_end_llhttp__on_status; } case 13: { goto s_n_llhttp__internal__n_span_end_llhttp__on_status_1; } default: { p++; goto s_n_llhttp__internal__n_res_status; } } UNREACHABLE; } case s_n_llhttp__internal__n_span_start_llhttp__on_status: s_n_llhttp__internal__n_span_start_llhttp__on_status: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_status; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_status; goto s_n_llhttp__internal__n_res_status; UNREACHABLE; } case s_n_llhttp__internal__n_res_status_code_otherwise: s_n_llhttp__internal__n_res_status_code_otherwise: { if (p == endp) { return s_n_llhttp__internal__n_res_status_code_otherwise; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_invoke_test_lenient_flags_28; } case 13: { p++; goto s_n_llhttp__internal__n_res_line_almost_done; } case ' ': { p++; goto s_n_llhttp__internal__n_span_start_llhttp__on_status; } default: { goto s_n_llhttp__internal__n_error_99; } } UNREACHABLE; } case s_n_llhttp__internal__n_res_status_code_digit_3: s_n_llhttp__internal__n_res_status_code_digit_3: { if (p == endp) { return s_n_llhttp__internal__n_res_status_code_digit_3; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; } default: { goto s_n_llhttp__internal__n_error_101; } } UNREACHABLE; } case s_n_llhttp__internal__n_res_status_code_digit_2: s_n_llhttp__internal__n_res_status_code_digit_2: { if (p == endp) { return s_n_llhttp__internal__n_res_status_code_digit_2; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; } default: { goto s_n_llhttp__internal__n_error_103; } } UNREACHABLE; } case s_n_llhttp__internal__n_res_status_code_digit_1: s_n_llhttp__internal__n_res_status_code_digit_1: { if (p == endp) { return s_n_llhttp__internal__n_res_status_code_digit_1; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } default: { goto s_n_llhttp__internal__n_error_105; } } UNREACHABLE; } case s_n_llhttp__internal__n_res_after_version: s_n_llhttp__internal__n_res_after_version: { if (p == endp) { return s_n_llhttp__internal__n_res_after_version; } switch (*p) { case ' ': { p++; goto s_n_llhttp__internal__n_invoke_update_status_code; } default: { goto s_n_llhttp__internal__n_error_106; } } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1: s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1: { switch (llhttp__on_version_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_res_after_version; case 21: goto s_n_llhttp__internal__n_pause_28; default: goto s_n_llhttp__internal__n_error_94; } UNREACHABLE; } case s_n_llhttp__internal__n_error_93: s_n_llhttp__internal__n_error_93: { state->error = 0x9; state->reason = "Invalid HTTP version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } case s_n_llhttp__internal__n_error_107: s_n_llhttp__internal__n_error_107: { state->error = 0x9; state->reason = "Invalid minor version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } case s_n_llhttp__internal__n_res_http_minor: s_n_llhttp__internal__n_res_http_minor: { if (p == endp) { return s_n_llhttp__internal__n_res_http_minor; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } default: { goto s_n_llhttp__internal__n_span_end_llhttp__on_version_7; } } UNREACHABLE; } case s_n_llhttp__internal__n_error_108: s_n_llhttp__internal__n_error_108: { state->error = 0x9; state->reason = "Expected dot"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } case s_n_llhttp__internal__n_res_http_dot: s_n_llhttp__internal__n_res_http_dot: { if (p == endp) { return s_n_llhttp__internal__n_res_http_dot; } switch (*p) { case '.': { p++; goto s_n_llhttp__internal__n_res_http_minor; } default: { goto s_n_llhttp__internal__n_span_end_llhttp__on_version_8; } } UNREACHABLE; } case s_n_llhttp__internal__n_error_109: s_n_llhttp__internal__n_error_109: { state->error = 0x9; state->reason = "Invalid major version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } case s_n_llhttp__internal__n_res_http_major: s_n_llhttp__internal__n_res_http_major: { if (p == endp) { return s_n_llhttp__internal__n_res_http_major; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } default: { goto s_n_llhttp__internal__n_span_end_llhttp__on_version_9; } } UNREACHABLE; } case s_n_llhttp__internal__n_span_start_llhttp__on_version_1: s_n_llhttp__internal__n_span_start_llhttp__on_version_1: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_version_1; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_version; goto s_n_llhttp__internal__n_res_http_major; UNREACHABLE; } case s_n_llhttp__internal__n_res_after_protocol: s_n_llhttp__internal__n_res_after_protocol: { if (p == endp) { return s_n_llhttp__internal__n_res_after_protocol; } switch (*p) { case '/': { p++; goto s_n_llhttp__internal__n_span_start_llhttp__on_version_1; } default: { goto s_n_llhttp__internal__n_error_114; } } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3: s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3: { switch (llhttp__on_protocol_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_res_after_protocol; case 21: goto s_n_llhttp__internal__n_pause_30; default: goto s_n_llhttp__internal__n_error_113; } UNREACHABLE; } case s_n_llhttp__internal__n_error_115: s_n_llhttp__internal__n_error_115: { state->error = 0x8; state->reason = "Expected HTTP/, RTSP/ or ICE/"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } case s_n_llhttp__internal__n_res_after_start_1: s_n_llhttp__internal__n_res_after_start_1: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_res_after_start_1; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob58, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_4; } case kMatchPause: { return s_n_llhttp__internal__n_res_after_start_1; } case kMatchMismatch: { goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5; } } UNREACHABLE; } case s_n_llhttp__internal__n_res_after_start_2: s_n_llhttp__internal__n_res_after_start_2: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_res_after_start_2; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob59, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_4; } case kMatchPause: { return s_n_llhttp__internal__n_res_after_start_2; } case kMatchMismatch: { goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5; } } UNREACHABLE; } case s_n_llhttp__internal__n_res_after_start_3: s_n_llhttp__internal__n_res_after_start_3: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_res_after_start_3; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob60, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_4; } case kMatchPause: { return s_n_llhttp__internal__n_res_after_start_3; } case kMatchMismatch: { goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5; } } UNREACHABLE; } case s_n_llhttp__internal__n_res_after_start: s_n_llhttp__internal__n_res_after_start: { if (p == endp) { return s_n_llhttp__internal__n_res_after_start; } switch (*p) { case 'H': { p++; goto s_n_llhttp__internal__n_res_after_start_1; } case 'I': { p++; goto s_n_llhttp__internal__n_res_after_start_2; } case 'R': { p++; goto s_n_llhttp__internal__n_res_after_start_3; } default: { goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5; } } UNREACHABLE; } case s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1: s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_protocol; goto s_n_llhttp__internal__n_res_after_start; UNREACHABLE; } case s_n_llhttp__internal__n_invoke_llhttp__on_method_complete: s_n_llhttp__internal__n_invoke_llhttp__on_method_complete: { switch (llhttp__on_method_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_req_first_space_before_url; case 21: goto s_n_llhttp__internal__n_pause_26; default: goto s_n_llhttp__internal__n_error_1; } UNREACHABLE; } case s_n_llhttp__internal__n_req_or_res_method_2: s_n_llhttp__internal__n_req_or_res_method_2: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_req_or_res_method_2; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob61, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 2; goto s_n_llhttp__internal__n_invoke_store_method; } case kMatchPause: { return s_n_llhttp__internal__n_req_or_res_method_2; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_110; } } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_update_type_1: s_n_llhttp__internal__n_invoke_update_type_1: { switch (llhttp__internal__c_update_type_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_span_start_llhttp__on_version_1; } UNREACHABLE; } case s_n_llhttp__internal__n_req_or_res_method_3: s_n_llhttp__internal__n_req_or_res_method_3: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_req_or_res_method_3; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob62, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_span_end_llhttp__on_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_req_or_res_method_3; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_110; } } UNREACHABLE; } case s_n_llhttp__internal__n_req_or_res_method_1: s_n_llhttp__internal__n_req_or_res_method_1: { if (p == endp) { return s_n_llhttp__internal__n_req_or_res_method_1; } switch (*p) { case 'E': { p++; goto s_n_llhttp__internal__n_req_or_res_method_2; } case 'T': { p++; goto s_n_llhttp__internal__n_req_or_res_method_3; } default: { goto s_n_llhttp__internal__n_error_110; } } UNREACHABLE; } case s_n_llhttp__internal__n_req_or_res_method: s_n_llhttp__internal__n_req_or_res_method: { if (p == endp) { return s_n_llhttp__internal__n_req_or_res_method; } switch (*p) { case 'H': { p++; goto s_n_llhttp__internal__n_req_or_res_method_1; } default: { goto s_n_llhttp__internal__n_error_110; } } UNREACHABLE; } case s_n_llhttp__internal__n_span_start_llhttp__on_method: s_n_llhttp__internal__n_span_start_llhttp__on_method: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_method; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_method; goto s_n_llhttp__internal__n_req_or_res_method; UNREACHABLE; } case s_n_llhttp__internal__n_start_req_or_res: s_n_llhttp__internal__n_start_req_or_res: { if (p == endp) { return s_n_llhttp__internal__n_start_req_or_res; } switch (*p) { case 'H': { goto s_n_llhttp__internal__n_span_start_llhttp__on_method; } default: { goto s_n_llhttp__internal__n_invoke_update_type_2; } } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_load_type: s_n_llhttp__internal__n_invoke_load_type: { switch (llhttp__internal__c_load_type(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_span_start_llhttp__on_method_1; case 2: goto s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1; default: goto s_n_llhttp__internal__n_start_req_or_res; } UNREACHABLE; } case s_n_llhttp__internal__n_invoke_update_finish: s_n_llhttp__internal__n_invoke_update_finish: { switch (llhttp__internal__c_update_finish(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__on_message_begin; } UNREACHABLE; } case s_n_llhttp__internal__n_start: s_n_llhttp__internal__n_start: { if (p == endp) { return s_n_llhttp__internal__n_start; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_start; } case 13: { p++; goto s_n_llhttp__internal__n_start; } default: { goto s_n_llhttp__internal__n_invoke_load_initial_message_completed; } } UNREACHABLE; } default: UNREACHABLE; } s_n_llhttp__internal__n_error_2: { state->error = 0x7; state->reason = "Invalid characters in url"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_finish_2: { switch (llhttp__internal__c_update_finish_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_start; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_initial_message_completed: { switch (llhttp__internal__c_update_initial_message_completed(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_finish_2; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_content_length: { switch (llhttp__internal__c_update_content_length(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_initial_message_completed; } UNREACHABLE; } s_n_llhttp__internal__n_error_8: { state->error = 0x5; state->reason = "Data after `Connection: close`"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_3: { switch (llhttp__internal__c_test_lenient_flags_3(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_closed; default: goto s_n_llhttp__internal__n_error_8; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_2: { switch (llhttp__internal__c_test_lenient_flags_2(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_update_initial_message_completed; default: goto s_n_llhttp__internal__n_closed; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_finish_1: { switch (llhttp__internal__c_update_finish_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_test_lenient_flags_2; } UNREACHABLE; } s_n_llhttp__internal__n_pause_13: { state->error = 0x15; state->reason = "on_message_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_upgrade; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_38: { state->error = 0x12; state->reason = "`on_message_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_pause_15: { state->error = 0x15; state->reason = "on_chunk_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_40: { state->error = 0x14; state->reason = "`on_chunk_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1: { switch (llhttp__on_chunk_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; case 21: goto s_n_llhttp__internal__n_pause_15; default: goto s_n_llhttp__internal__n_error_40; } UNREACHABLE; } s_n_llhttp__internal__n_pause_2: { state->error = 0x15; state->reason = "on_message_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_pause_1; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_9: { state->error = 0x12; state->reason = "`on_message_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1: { switch (llhttp__on_message_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_pause_1; case 21: goto s_n_llhttp__internal__n_pause_2; default: goto s_n_llhttp__internal__n_error_9; } UNREACHABLE; } s_n_llhttp__internal__n_error_36: { state->error = 0xc; state->reason = "Chunk size overflow"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_10: { state->error = 0xc; state->reason = "Invalid character in chunk size"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_4: { switch (llhttp__internal__c_test_lenient_flags_4(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_chunk_size_otherwise; default: goto s_n_llhttp__internal__n_error_10; } UNREACHABLE; } s_n_llhttp__internal__n_pause_3: { state->error = 0x15; state->reason = "on_chunk_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_content_length_1; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_14: { state->error = 0x14; state->reason = "`on_chunk_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete: { switch (llhttp__on_chunk_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_update_content_length_1; case 21: goto s_n_llhttp__internal__n_pause_3; default: goto s_n_llhttp__internal__n_error_14; } UNREACHABLE; } s_n_llhttp__internal__n_error_13: { state->error = 0x19; state->reason = "Missing expected CR after chunk data"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_6: { switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete; default: goto s_n_llhttp__internal__n_error_13; } UNREACHABLE; } s_n_llhttp__internal__n_error_15: { state->error = 0x2; state->reason = "Expected LF after chunk data"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_7: { switch (llhttp__internal__c_test_lenient_flags_7(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete; default: goto s_n_llhttp__internal__n_error_15; } UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_body: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_body(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_data_almost_done; return s_error; } goto s_n_llhttp__internal__n_chunk_data_almost_done; UNREACHABLE; } s_n_llhttp__internal__n_invoke_or_flags: { switch (llhttp__internal__c_or_flags(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_field_start; } UNREACHABLE; } s_n_llhttp__internal__n_pause_4: { state->error = 0x15; state->reason = "on_chunk_header pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_content_length; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_12: { state->error = 0x13; state->reason = "`on_chunk_header` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header: { switch (llhttp__on_chunk_header(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_is_equal_content_length; case 21: goto s_n_llhttp__internal__n_pause_4; default: goto s_n_llhttp__internal__n_error_12; } UNREACHABLE; } s_n_llhttp__internal__n_error_16: { state->error = 0x2; state->reason = "Expected LF after chunk size"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_8: { switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header; default: goto s_n_llhttp__internal__n_error_16; } UNREACHABLE; } s_n_llhttp__internal__n_error_11: { state->error = 0x19; state->reason = "Missing expected CR after chunk size"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_5: { switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_chunk_size_almost_done; default: goto s_n_llhttp__internal__n_error_11; } UNREACHABLE; } s_n_llhttp__internal__n_error_17: { state->error = 0x2; state->reason = "Invalid character in chunk extensions"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_18: { state->error = 0x2; state->reason = "Invalid character in chunk extensions"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_20: { state->error = 0x19; state->reason = "Missing expected CR after chunk extension name"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_pause_5: { state->error = 0x15; state->reason = "on_chunk_extension_name pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_9; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_19: { state->error = 0x22; state->reason = "`on_chunk_extension_name` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_chunk_extension_name(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete; return s_error; } goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete; UNREACHABLE; } s_n_llhttp__internal__n_pause_6: { state->error = 0x15; state->reason = "on_chunk_extension_name pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_21: { state->error = 0x22; state->reason = "`on_chunk_extension_name` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_1: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_chunk_extension_name(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1; return s_error; } p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1; UNREACHABLE; } s_n_llhttp__internal__n_pause_7: { state->error = 0x15; state->reason = "on_chunk_extension_name pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extensions; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_22: { state->error = 0x22; state->reason = "`on_chunk_extension_name` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_2: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_chunk_extension_name(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2; return s_error; } p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2; UNREACHABLE; } s_n_llhttp__internal__n_error_25: { state->error = 0x19; state->reason = "Missing expected CR after chunk extension value"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_pause_8: { state->error = 0x15; state->reason = "on_chunk_extension_value pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_10; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_24: { state->error = 0x23; state->reason = "`on_chunk_extension_value` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_chunk_extension_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete; return s_error; } goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete; UNREACHABLE; } s_n_llhttp__internal__n_pause_9: { state->error = 0x15; state->reason = "on_chunk_extension_value pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_26: { state->error = 0x23; state->reason = "`on_chunk_extension_value` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_1: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_chunk_extension_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1; return s_error; } p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1; UNREACHABLE; } s_n_llhttp__internal__n_error_28: { state->error = 0x19; state->reason = "Missing expected CR after chunk extension value"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_11: { switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_chunk_size_almost_done; default: goto s_n_llhttp__internal__n_error_28; } UNREACHABLE; } s_n_llhttp__internal__n_error_29: { state->error = 0x2; state->reason = "Invalid character in chunk extensions quote value"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_pause_10: { state->error = 0x15; state->reason = "on_chunk_extension_value pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extension_quoted_value_done; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_27: { state->error = 0x23; state->reason = "`on_chunk_extension_value` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_2: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_chunk_extension_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2; return s_error; } goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_3: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_chunk_extension_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_30; return s_error; } p++; goto s_n_llhttp__internal__n_error_30; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_4: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_chunk_extension_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_31; return s_error; } p++; goto s_n_llhttp__internal__n_error_31; UNREACHABLE; } s_n_llhttp__internal__n_pause_11: { state->error = 0x15; state->reason = "on_chunk_extension_value pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extensions; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_32: { state->error = 0x23; state->reason = "`on_chunk_extension_value` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_5: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_chunk_extension_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3; return s_error; } p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_6: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_chunk_extension_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_33; return s_error; } p++; goto s_n_llhttp__internal__n_error_33; UNREACHABLE; } s_n_llhttp__internal__n_pause_12: { state->error = 0x15; state->reason = "on_chunk_extension_name pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extension_value; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_23: { state->error = 0x22; state->reason = "`on_chunk_extension_name` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_3: { switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_chunk_extension_value; case 21: goto s_n_llhttp__internal__n_pause_12; default: goto s_n_llhttp__internal__n_error_23; } UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_3: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_chunk_extension_name(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value; return s_error; } p++; goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_4: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_chunk_extension_name(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_34; return s_error; } p++; goto s_n_llhttp__internal__n_error_34; UNREACHABLE; } s_n_llhttp__internal__n_error_35: { state->error = 0xc; state->reason = "Invalid character in chunk size"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_mul_add_content_length: { switch (llhttp__internal__c_mul_add_content_length(state, p, endp, match)) { case 1: goto s_n_llhttp__internal__n_error_36; default: goto s_n_llhttp__internal__n_chunk_size; } UNREACHABLE; } s_n_llhttp__internal__n_error_37: { state->error = 0xc; state->reason = "Invalid character in chunk size"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_body_1: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_body(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; return s_error; } goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_finish_3: { switch (llhttp__internal__c_update_finish_3(state, p, endp)) { default: goto s_n_llhttp__internal__n_span_start_llhttp__on_body_2; } UNREACHABLE; } s_n_llhttp__internal__n_error_39: { state->error = 0xf; state->reason = "Request has invalid `Transfer-Encoding`"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_pause: { state->error = 0x15; state->reason = "on_message_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_7: { state->error = 0x12; state->reason = "`on_message_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_llhttp__on_message_complete: { switch (llhttp__on_message_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; case 21: goto s_n_llhttp__internal__n_pause; default: goto s_n_llhttp__internal__n_error_7; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_or_flags_1: { switch (llhttp__internal__c_or_flags_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_or_flags_2: { switch (llhttp__internal__c_or_flags_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_upgrade: { switch (llhttp__internal__c_update_upgrade(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_or_flags_2; } UNREACHABLE; } s_n_llhttp__internal__n_pause_14: { state->error = 0x15; state->reason = "Paused by on_headers_complete"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_6: { state->error = 0x11; state->reason = "User callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete: { switch (llhttp__on_headers_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; case 1: goto s_n_llhttp__internal__n_invoke_or_flags_1; case 2: goto s_n_llhttp__internal__n_invoke_update_upgrade; case 21: goto s_n_llhttp__internal__n_pause_14; default: goto s_n_llhttp__internal__n_error_6; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete: { switch (llhttp__before_headers_complete(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_flags: { switch (llhttp__internal__c_test_flags(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1; default: goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_1: { switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_test_flags; default: goto s_n_llhttp__internal__n_error_5; } UNREACHABLE; } s_n_llhttp__internal__n_pause_17: { state->error = 0x15; state->reason = "on_chunk_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_42: { state->error = 0x14; state->reason = "`on_chunk_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_2: { switch (llhttp__on_chunk_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; case 21: goto s_n_llhttp__internal__n_pause_17; default: goto s_n_llhttp__internal__n_error_42; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_or_flags_3: { switch (llhttp__internal__c_or_flags_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_or_flags_4: { switch (llhttp__internal__c_or_flags_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_upgrade_1: { switch (llhttp__internal__c_update_upgrade(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_or_flags_4; } UNREACHABLE; } s_n_llhttp__internal__n_pause_16: { state->error = 0x15; state->reason = "Paused by on_headers_complete"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_41: { state->error = 0x11; state->reason = "User callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete_1: { switch (llhttp__on_headers_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; case 1: goto s_n_llhttp__internal__n_invoke_or_flags_3; case 2: goto s_n_llhttp__internal__n_invoke_update_upgrade_1; case 21: goto s_n_llhttp__internal__n_pause_16; default: goto s_n_llhttp__internal__n_error_41; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete_1: { switch (llhttp__before_headers_complete(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete_1; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_flags_1: { switch (llhttp__internal__c_test_flags(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_2; default: goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete_1; } UNREACHABLE; } s_n_llhttp__internal__n_error_43: { state->error = 0x2; state->reason = "Expected LF after headers"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_12: { switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_test_flags_1; default: goto s_n_llhttp__internal__n_error_43; } UNREACHABLE; } s_n_llhttp__internal__n_error_44: { state->error = 0xa; state->reason = "Invalid header token"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_header_field: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_field(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_5; return s_error; } p++; goto s_n_llhttp__internal__n_error_5; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_13: { switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_header_field_colon_discard_ws; default: goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field; } UNREACHABLE; } s_n_llhttp__internal__n_error_60: { state->error = 0xb; state->reason = "Content-Length can't be present with Transfer-Encoding"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_47: { state->error = 0xa; state->reason = "Invalid header value char"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_15: { switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_header_value_discard_ws; default: goto s_n_llhttp__internal__n_error_47; } UNREACHABLE; } s_n_llhttp__internal__n_error_49: { state->error = 0xb; state->reason = "Empty Content-Length"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_pause_18: { state->error = 0x15; state->reason = "on_header_value_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_field_start; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_48: { state->error = 0x1d; state->reason = "`on_header_value_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_header_value: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; return s_error; } goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_header_state: { switch (llhttp__internal__c_update_header_state(state, p, endp)) { default: goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_or_flags_5: { switch (llhttp__internal__c_or_flags_5(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_or_flags_6: { switch (llhttp__internal__c_or_flags_6(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_or_flags_7: { switch (llhttp__internal__c_or_flags_7(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_or_flags_8: { switch (llhttp__internal__c_or_flags_8(state, p, endp)) { default: goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_load_header_state_2: { switch (llhttp__internal__c_load_header_state(state, p, endp)) { case 5: goto s_n_llhttp__internal__n_invoke_or_flags_5; case 6: goto s_n_llhttp__internal__n_invoke_or_flags_6; case 7: goto s_n_llhttp__internal__n_invoke_or_flags_7; case 8: goto s_n_llhttp__internal__n_invoke_or_flags_8; default: goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_load_header_state_1: { switch (llhttp__internal__c_load_header_state(state, p, endp)) { case 2: goto s_n_llhttp__internal__n_error_49; default: goto s_n_llhttp__internal__n_invoke_load_header_state_2; } UNREACHABLE; } s_n_llhttp__internal__n_error_46: { state->error = 0xa; state->reason = "Invalid header value char"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_14: { switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_header_value_discard_lws; default: goto s_n_llhttp__internal__n_error_46; } UNREACHABLE; } s_n_llhttp__internal__n_error_50: { state->error = 0x2; state->reason = "Expected LF after CR"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_16: { switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_header_value_discard_lws; default: goto s_n_llhttp__internal__n_error_50; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_header_state_1: { switch (llhttp__internal__c_update_header_state_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_load_header_state_4: { switch (llhttp__internal__c_load_header_state(state, p, endp)) { case 8: goto s_n_llhttp__internal__n_invoke_update_header_state_1; default: goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; } UNREACHABLE; } s_n_llhttp__internal__n_error_52: { state->error = 0xa; state->reason = "Unexpected whitespace after header value"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_18: { switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_load_header_state_4; default: goto s_n_llhttp__internal__n_error_52; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_header_state_2: { switch (llhttp__internal__c_update_header_state(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_or_flags_9: { switch (llhttp__internal__c_or_flags_5(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state_2; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_or_flags_10: { switch (llhttp__internal__c_or_flags_6(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state_2; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_or_flags_11: { switch (llhttp__internal__c_or_flags_7(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state_2; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_or_flags_12: { switch (llhttp__internal__c_or_flags_8(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_load_header_state_5: { switch (llhttp__internal__c_load_header_state(state, p, endp)) { case 5: goto s_n_llhttp__internal__n_invoke_or_flags_9; case 6: goto s_n_llhttp__internal__n_invoke_or_flags_10; case 7: goto s_n_llhttp__internal__n_invoke_or_flags_11; case 8: goto s_n_llhttp__internal__n_invoke_or_flags_12; default: goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; } UNREACHABLE; } s_n_llhttp__internal__n_error_53: { state->error = 0x3; state->reason = "Missing expected LF after header value"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_51: { state->error = 0x19; state->reason = "Missing expected CR after header value"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_17; return s_error; } goto s_n_llhttp__internal__n_invoke_test_lenient_flags_17; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; return s_error; } p++; goto s_n_llhttp__internal__n_header_value_almost_done; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; return s_error; } goto s_n_llhttp__internal__n_header_value_almost_done; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; return s_error; } p++; goto s_n_llhttp__internal__n_header_value_almost_done; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_54; return s_error; } goto s_n_llhttp__internal__n_error_54; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_19: { switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_header_value_lenient; default: goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_header_state_4: { switch (llhttp__internal__c_update_header_state(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_connection; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_or_flags_13: { switch (llhttp__internal__c_or_flags_5(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state_4; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_or_flags_14: { switch (llhttp__internal__c_or_flags_6(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state_4; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_or_flags_15: { switch (llhttp__internal__c_or_flags_7(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state_4; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_or_flags_16: { switch (llhttp__internal__c_or_flags_8(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_connection; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_load_header_state_6: { switch (llhttp__internal__c_load_header_state(state, p, endp)) { case 5: goto s_n_llhttp__internal__n_invoke_or_flags_13; case 6: goto s_n_llhttp__internal__n_invoke_or_flags_14; case 7: goto s_n_llhttp__internal__n_invoke_or_flags_15; case 8: goto s_n_llhttp__internal__n_invoke_or_flags_16; default: goto s_n_llhttp__internal__n_header_value_connection; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_header_state_5: { switch (llhttp__internal__c_update_header_state_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_connection_token; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_header_state_3: { switch (llhttp__internal__c_update_header_state_3(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_connection_ws; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_header_state_6: { switch (llhttp__internal__c_update_header_state_6(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_connection_ws; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_header_state_7: { switch (llhttp__internal__c_update_header_state_7(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_connection_ws; } UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_header_value_6: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_56; return s_error; } goto s_n_llhttp__internal__n_error_56; UNREACHABLE; } s_n_llhttp__internal__n_invoke_mul_add_content_length_1: { switch (llhttp__internal__c_mul_add_content_length_1(state, p, endp, match)) { case 1: goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_6; default: goto s_n_llhttp__internal__n_header_value_content_length; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_or_flags_17: { switch (llhttp__internal__c_or_flags_17(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_otherwise; } UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_header_value_7: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_57; return s_error; } goto s_n_llhttp__internal__n_error_57; UNREACHABLE; } s_n_llhttp__internal__n_error_55: { state->error = 0x4; state->reason = "Duplicate Content-Length"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_flags_2: { switch (llhttp__internal__c_test_flags_2(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_header_value_content_length; default: goto s_n_llhttp__internal__n_error_55; } UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_header_value_9: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_59; return s_error; } p++; goto s_n_llhttp__internal__n_error_59; UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_header_state_8: { switch (llhttp__internal__c_update_header_state_8(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_otherwise; } UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_header_value_8: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_58; return s_error; } p++; goto s_n_llhttp__internal__n_error_58; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_20: { switch (llhttp__internal__c_test_lenient_flags_20(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_8; default: goto s_n_llhttp__internal__n_header_value_te_chunked; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_load_type_1: { switch (llhttp__internal__c_load_type(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_test_lenient_flags_20; default: goto s_n_llhttp__internal__n_header_value_te_chunked; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_header_state_9: { switch (llhttp__internal__c_update_header_state_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_and_flags: { switch (llhttp__internal__c_and_flags(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_te_chunked; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_or_flags_19: { switch (llhttp__internal__c_or_flags_18(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_and_flags; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_21: { switch (llhttp__internal__c_test_lenient_flags_20(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_9; default: goto s_n_llhttp__internal__n_invoke_or_flags_19; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_load_type_2: { switch (llhttp__internal__c_load_type(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_test_lenient_flags_21; default: goto s_n_llhttp__internal__n_invoke_or_flags_19; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_or_flags_18: { switch (llhttp__internal__c_or_flags_18(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_and_flags; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_flags_3: { switch (llhttp__internal__c_test_flags_3(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_load_type_2; default: goto s_n_llhttp__internal__n_invoke_or_flags_18; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_or_flags_20: { switch (llhttp__internal__c_or_flags_20(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state_9; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_load_header_state_3: { switch (llhttp__internal__c_load_header_state(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_header_value_connection; case 2: goto s_n_llhttp__internal__n_invoke_test_flags_2; case 3: goto s_n_llhttp__internal__n_invoke_test_flags_3; case 4: goto s_n_llhttp__internal__n_invoke_or_flags_20; default: goto s_n_llhttp__internal__n_header_value; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_22: { switch (llhttp__internal__c_test_lenient_flags_22(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_error_60; default: goto s_n_llhttp__internal__n_header_value_discard_ws; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_flags_4: { switch (llhttp__internal__c_test_flags_4(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_test_lenient_flags_22; default: goto s_n_llhttp__internal__n_header_value_discard_ws; } UNREACHABLE; } s_n_llhttp__internal__n_error_61: { state->error = 0xf; state->reason = "Transfer-Encoding can't be present with Content-Length"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_23: { switch (llhttp__internal__c_test_lenient_flags_22(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_error_61; default: goto s_n_llhttp__internal__n_header_value_discard_ws; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_flags_5: { switch (llhttp__internal__c_test_flags_2(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_test_lenient_flags_23; default: goto s_n_llhttp__internal__n_header_value_discard_ws; } UNREACHABLE; } s_n_llhttp__internal__n_pause_19: { state->error = 0x15; state->reason = "on_header_field_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_header_state; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_45: { state->error = 0x1c; state->reason = "`on_header_field_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_field(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; return s_error; } p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_header_field_2: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_field(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; return s_error; } p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; UNREACHABLE; } s_n_llhttp__internal__n_error_62: { state->error = 0xa; state->reason = "Invalid header token"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_header_state_10: { switch (llhttp__internal__c_update_header_state_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_field_general; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_store_header_state: { switch (llhttp__internal__c_store_header_state(state, p, endp, match)) { default: goto s_n_llhttp__internal__n_header_field_colon; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_header_state_11: { switch (llhttp__internal__c_update_header_state_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_field_general; } UNREACHABLE; } s_n_llhttp__internal__n_error_4: { state->error = 0x1e; state->reason = "Unexpected space after start line"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags: { switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_header_field_start; default: goto s_n_llhttp__internal__n_error_4; } UNREACHABLE; } s_n_llhttp__internal__n_pause_20: { state->error = 0x15; state->reason = "on_url_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_headers_start; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_3: { state->error = 0x1a; state->reason = "`on_url_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_llhttp__on_url_complete: { switch (llhttp__on_url_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_headers_start; case 21: goto s_n_llhttp__internal__n_pause_20; default: goto s_n_llhttp__internal__n_error_3; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_http_minor: { switch (llhttp__internal__c_update_http_minor(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__on_url_complete; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_http_major: { switch (llhttp__internal__c_update_http_major(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_http_minor; } UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_url_3: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http09; UNREACHABLE; } s_n_llhttp__internal__n_error_63: { state->error = 0x7; state->reason = "Expected CRLF"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_url_4: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_lf_to_http09; UNREACHABLE; } s_n_llhttp__internal__n_error_72: { state->error = 0x17; state->reason = "Pause on PRI/Upgrade"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_73: { state->error = 0x9; state->reason = "Expected HTTP/2 Connection Preface"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_70: { state->error = 0x2; state->reason = "Expected CRLF after version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_26: { switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_headers_start; default: goto s_n_llhttp__internal__n_error_70; } UNREACHABLE; } s_n_llhttp__internal__n_error_69: { state->error = 0x9; state->reason = "Expected CRLF after version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_25: { switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_req_http_complete_crlf; default: goto s_n_llhttp__internal__n_error_69; } UNREACHABLE; } s_n_llhttp__internal__n_error_71: { state->error = 0x9; state->reason = "Expected CRLF after version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_pause_21: { state->error = 0x15; state->reason = "on_version_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method_1; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_68: { state->error = 0x21; state->reason = "`on_version_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_version_1: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_version(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_version_complete; return s_error; } goto s_n_llhttp__internal__n_invoke_llhttp__on_version_complete; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_version: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_version(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_67; return s_error; } goto s_n_llhttp__internal__n_error_67; UNREACHABLE; } s_n_llhttp__internal__n_invoke_load_http_minor: { switch (llhttp__internal__c_load_http_minor(state, p, endp)) { case 9: goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1; default: goto s_n_llhttp__internal__n_span_end_llhttp__on_version; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_load_http_minor_1: { switch (llhttp__internal__c_load_http_minor(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1; case 1: goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1; default: goto s_n_llhttp__internal__n_span_end_llhttp__on_version; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_load_http_minor_2: { switch (llhttp__internal__c_load_http_minor(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1; default: goto s_n_llhttp__internal__n_span_end_llhttp__on_version; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_load_http_major: { switch (llhttp__internal__c_load_http_major(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_load_http_minor; case 1: goto s_n_llhttp__internal__n_invoke_load_http_minor_1; case 2: goto s_n_llhttp__internal__n_invoke_load_http_minor_2; default: goto s_n_llhttp__internal__n_span_end_llhttp__on_version; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_24: { switch (llhttp__internal__c_test_lenient_flags_24(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1; default: goto s_n_llhttp__internal__n_invoke_load_http_major; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_store_http_minor: { switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) { default: goto s_n_llhttp__internal__n_invoke_test_lenient_flags_24; } UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_version_2: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_version(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_74; return s_error; } goto s_n_llhttp__internal__n_error_74; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_version_3: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_version(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_75; return s_error; } goto s_n_llhttp__internal__n_error_75; UNREACHABLE; } s_n_llhttp__internal__n_invoke_store_http_major: { switch (llhttp__internal__c_store_http_major(state, p, endp, match)) { default: goto s_n_llhttp__internal__n_req_http_dot; } UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_version_4: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_version(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_76; return s_error; } goto s_n_llhttp__internal__n_error_76; UNREACHABLE; } s_n_llhttp__internal__n_error_77: { state->error = 0x8; state->reason = "Expected HTTP/, RTSP/ or ICE/"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_66: { state->error = 0x8; state->reason = "Invalid method for HTTP/x.x request"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_pause_22: { state->error = 0x15; state->reason = "on_protocol_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_65: { state->error = 0x26; state->reason = "`on_protocol_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_protocol: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_protocol(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete; return s_error; } goto s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_protocol(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_82; return s_error; } goto s_n_llhttp__internal__n_error_82; UNREACHABLE; } s_n_llhttp__internal__n_error_79: { state->error = 0x8; state->reason = "Expected SOURCE method for ICE/x.x request"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_pause_23: { state->error = 0x15; state->reason = "on_protocol_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method_2; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_78: { state->error = 0x26; state->reason = "`on_protocol_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_protocol_1: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_protocol(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1; return s_error; } goto s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1; UNREACHABLE; } s_n_llhttp__internal__n_error_81: { state->error = 0x8; state->reason = "Invalid method for RTSP/x.x request"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_pause_24: { state->error = 0x15; state->reason = "on_protocol_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method_3; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_80: { state->error = 0x26; state->reason = "`on_protocol_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_protocol_2: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_protocol(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2; return s_error; } goto s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2; UNREACHABLE; } s_n_llhttp__internal__n_pause_25: { state->error = 0x15; state->reason = "on_url_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_http_start; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_64: { state->error = 0x1a; state->reason = "`on_url_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_llhttp__on_url_complete_1: { switch (llhttp__on_url_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_req_http_start; case 21: goto s_n_llhttp__internal__n_pause_25; default: goto s_n_llhttp__internal__n_error_64; } UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_url_5: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_url_6: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http09; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_url_7: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_lf_to_http09; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_url_8: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http; UNREACHABLE; } s_n_llhttp__internal__n_error_83: { state->error = 0x7; state->reason = "Invalid char in url fragment start"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_url_9: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http09; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_url_10: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_lf_to_http09; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_url_11: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http; UNREACHABLE; } s_n_llhttp__internal__n_error_84: { state->error = 0x7; state->reason = "Invalid char in url query"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_85: { state->error = 0x7; state->reason = "Invalid char in url path"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_url: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http09; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_url_1: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_lf_to_http09; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_url_2: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_url_12: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http09; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_url_13: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_lf_to_http09; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_url_14: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http; UNREACHABLE; } s_n_llhttp__internal__n_error_86: { state->error = 0x7; state->reason = "Double @ in url"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_87: { state->error = 0x7; state->reason = "Unexpected char in url server"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_88: { state->error = 0x7; state->reason = "Unexpected char in url server"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_89: { state->error = 0x7; state->reason = "Unexpected char in url schema"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_90: { state->error = 0x7; state->reason = "Unexpected char in url schema"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_91: { state->error = 0x7; state->reason = "Unexpected start char in url"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_is_equal_method: { switch (llhttp__internal__c_is_equal_method(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_url_entry_normal; default: goto s_n_llhttp__internal__n_url_entry_connect; } UNREACHABLE; } s_n_llhttp__internal__n_error_92: { state->error = 0x6; state->reason = "Expected space after method"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_pause_29: { state->error = 0x15; state->reason = "on_method_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_first_space_before_url; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_111: { state->error = 0x20; state->reason = "`on_method_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_method_2: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_method(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1; return s_error; } goto s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1; UNREACHABLE; } s_n_llhttp__internal__n_invoke_store_method_1: { switch (llhttp__internal__c_store_method(state, p, endp, match)) { default: goto s_n_llhttp__internal__n_span_end_llhttp__on_method_2; } UNREACHABLE; } s_n_llhttp__internal__n_error_112: { state->error = 0x6; state->reason = "Invalid method encountered"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_104: { state->error = 0xd; state->reason = "Invalid status code"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_102: { state->error = 0xd; state->reason = "Invalid status code"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_100: { state->error = 0xd; state->reason = "Invalid status code"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_pause_27: { state->error = 0x15; state->reason = "on_status_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_headers_start; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_96: { state->error = 0x1b; state->reason = "`on_status_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_llhttp__on_status_complete: { switch (llhttp__on_status_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_headers_start; case 21: goto s_n_llhttp__internal__n_pause_27; default: goto s_n_llhttp__internal__n_error_96; } UNREACHABLE; } s_n_llhttp__internal__n_error_95: { state->error = 0xd; state->reason = "Invalid response status"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_28: { switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; default: goto s_n_llhttp__internal__n_error_95; } UNREACHABLE; } s_n_llhttp__internal__n_error_97: { state->error = 0x2; state->reason = "Expected LF after CR"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_29: { switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; default: goto s_n_llhttp__internal__n_error_97; } UNREACHABLE; } s_n_llhttp__internal__n_error_98: { state->error = 0x19; state->reason = "Missing expected CR after response line"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_status: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_status(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_30; return s_error; } p++; goto s_n_llhttp__internal__n_invoke_test_lenient_flags_30; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_status_1: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_status(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_line_almost_done; return s_error; } p++; goto s_n_llhttp__internal__n_res_line_almost_done; UNREACHABLE; } s_n_llhttp__internal__n_error_99: { state->error = 0xd; state->reason = "Invalid response status"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_mul_add_status_code_2: { switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) { case 1: goto s_n_llhttp__internal__n_error_100; default: goto s_n_llhttp__internal__n_res_status_code_otherwise; } UNREACHABLE; } s_n_llhttp__internal__n_error_101: { state->error = 0xd; state->reason = "Invalid status code"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_mul_add_status_code_1: { switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) { case 1: goto s_n_llhttp__internal__n_error_102; default: goto s_n_llhttp__internal__n_res_status_code_digit_3; } UNREACHABLE; } s_n_llhttp__internal__n_error_103: { state->error = 0xd; state->reason = "Invalid status code"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_mul_add_status_code: { switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) { case 1: goto s_n_llhttp__internal__n_error_104; default: goto s_n_llhttp__internal__n_res_status_code_digit_2; } UNREACHABLE; } s_n_llhttp__internal__n_error_105: { state->error = 0xd; state->reason = "Invalid status code"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_status_code: { switch (llhttp__internal__c_update_status_code(state, p, endp)) { default: goto s_n_llhttp__internal__n_res_status_code_digit_1; } UNREACHABLE; } s_n_llhttp__internal__n_error_106: { state->error = 0x9; state->reason = "Expected space after version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_pause_28: { state->error = 0x15; state->reason = "on_version_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_after_version; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_94: { state->error = 0x21; state->reason = "`on_version_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_version_6: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_version(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1; return s_error; } goto s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_version_5: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_version(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_93; return s_error; } goto s_n_llhttp__internal__n_error_93; UNREACHABLE; } s_n_llhttp__internal__n_invoke_load_http_minor_3: { switch (llhttp__internal__c_load_http_minor(state, p, endp)) { case 9: goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6; default: goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_load_http_minor_4: { switch (llhttp__internal__c_load_http_minor(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6; case 1: goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6; default: goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_load_http_minor_5: { switch (llhttp__internal__c_load_http_minor(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6; default: goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_load_http_major_1: { switch (llhttp__internal__c_load_http_major(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_load_http_minor_3; case 1: goto s_n_llhttp__internal__n_invoke_load_http_minor_4; case 2: goto s_n_llhttp__internal__n_invoke_load_http_minor_5; default: goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_test_lenient_flags_27: { switch (llhttp__internal__c_test_lenient_flags_24(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6; default: goto s_n_llhttp__internal__n_invoke_load_http_major_1; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_store_http_minor_1: { switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) { default: goto s_n_llhttp__internal__n_invoke_test_lenient_flags_27; } UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_version_7: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_version(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_107; return s_error; } goto s_n_llhttp__internal__n_error_107; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_version_8: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_version(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_108; return s_error; } goto s_n_llhttp__internal__n_error_108; UNREACHABLE; } s_n_llhttp__internal__n_invoke_store_http_major_1: { switch (llhttp__internal__c_store_http_major(state, p, endp, match)) { default: goto s_n_llhttp__internal__n_res_http_dot; } UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_version_9: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_version(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_109; return s_error; } goto s_n_llhttp__internal__n_error_109; UNREACHABLE; } s_n_llhttp__internal__n_error_114: { state->error = 0x8; state->reason = "Expected HTTP/, RTSP/ or ICE/"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_pause_30: { state->error = 0x15; state->reason = "on_protocol_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_after_protocol; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_113: { state->error = 0x26; state->reason = "`on_protocol_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_protocol_4: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_protocol(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3; return s_error; } goto s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_protocol(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_115; return s_error; } goto s_n_llhttp__internal__n_error_115; UNREACHABLE; } s_n_llhttp__internal__n_pause_26: { state->error = 0x15; state->reason = "on_method_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_first_space_before_url; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_1: { state->error = 0x20; state->reason = "`on_method_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_method: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_method(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_method_complete; return s_error; } goto s_n_llhttp__internal__n_invoke_llhttp__on_method_complete; UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_type: { switch (llhttp__internal__c_update_type(state, p, endp)) { default: goto s_n_llhttp__internal__n_span_end_llhttp__on_method; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_store_method: { switch (llhttp__internal__c_store_method(state, p, endp, match)) { default: goto s_n_llhttp__internal__n_invoke_update_type; } UNREACHABLE; } s_n_llhttp__internal__n_error_110: { state->error = 0x8; state->reason = "Invalid word encountered"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_span_end_llhttp__on_method_1: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_method(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_type_1; return s_error; } goto s_n_llhttp__internal__n_invoke_update_type_1; UNREACHABLE; } s_n_llhttp__internal__n_invoke_update_type_2: { switch (llhttp__internal__c_update_type(state, p, endp)) { default: goto s_n_llhttp__internal__n_span_start_llhttp__on_method_1; } UNREACHABLE; } s_n_llhttp__internal__n_pause_31: { state->error = 0x15; state->reason = "on_message_begin pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_type; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error: { state->error = 0x10; state->reason = "`on_message_begin` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_llhttp__on_message_begin: { switch (llhttp__on_message_begin(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_load_type; case 21: goto s_n_llhttp__internal__n_pause_31; default: goto s_n_llhttp__internal__n_error; } UNREACHABLE; } s_n_llhttp__internal__n_pause_32: { state->error = 0x15; state->reason = "on_reset pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_finish; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_error_116: { state->error = 0x1f; state->reason = "`on_reset` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; UNREACHABLE; } s_n_llhttp__internal__n_invoke_llhttp__on_reset: { switch (llhttp__on_reset(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_update_finish; case 21: goto s_n_llhttp__internal__n_pause_32; default: goto s_n_llhttp__internal__n_error_116; } UNREACHABLE; } s_n_llhttp__internal__n_invoke_load_initial_message_completed: { switch (llhttp__internal__c_load_initial_message_completed(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_llhttp__on_reset; default: goto s_n_llhttp__internal__n_invoke_update_finish; } UNREACHABLE; } } int llhttp__internal_execute(llhttp__internal_t* state, const char* p, const char* endp) { llparse_state_t next; /* check lingering errors */ if (state->error != 0) { return state->error; } /* restart spans */ if (state->_span_pos0 != NULL) { state->_span_pos0 = (void*) p; } next = llhttp__internal__run(state, (const unsigned char*) p, (const unsigned char*) endp); if (next == s_error) { return state->error; } state->_current = (void*) (intptr_t) next; /* execute spans */ if (state->_span_pos0 != NULL) { int error; error = ((llhttp__internal__span_cb) state->_span_cb0)(state, state->_span_pos0, (const char*) endp); if (error != 0) { state->error = error; state->error_pos = endp; return error; } } return 0; }nghttp2-1.68.0/third-party/PaxHeaders/CMakeLists.txt0000644000000000000000000000013215077107271017270 xustar0030 mtime=1761382073.009444065 30 atime=1761382080.108411471 30 ctime=1761382108.223302916 nghttp2-1.68.0/third-party/CMakeLists.txt0000644000175100017510000000525215077107271017664 0ustar00runnerrunnerif(ENABLE_THIRD_PARTY) set(LIBLLHTTP_SOURCES llhttp/src/api.c llhttp/src/http.c llhttp/src/llhttp.c ) add_library(llhttp OBJECT ${LIBLLHTTP_SOURCES}) target_include_directories(llhttp PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/llhttp/include" ) set_target_properties(llhttp PROPERTIES POSITION_INDEPENDENT_CODE ON ) set(LIBURLPARSE_SOURCES urlparse/urlparse.c ) add_library(urlparse OBJECT ${LIBURLPARSE_SOURCES}) set_target_properties(urlparse PROPERTIES POSITION_INDEPENDENT_CODE ON) if(HAVE_NEVERBLEED) set(NEVERBLEED_SOURCES neverbleed/neverbleed.c ) add_library(neverbleed ${NEVERBLEED_SOURCES}) target_include_directories(neverbleed PRIVATE ${OPENSSL_INCLUDE_DIRS}) target_include_directories(neverbleed INTERFACE "${CMAKE_SOURCE_DIR}/third-party/neverbleed" ) target_link_libraries(neverbleed ${OPENSSL_LIBRARIES}) target_compile_definitions(neverbleed PUBLIC _GNU_SOURCE) endif() if(HAVE_MRUBY) # EXTRA_DIST = build_config.rb mruby/* set(MRUBY_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/mruby/build") set(MRUBY_LIBRARY "${CMAKE_STATIC_LIBRARY_PREFIX}mruby${CMAKE_STATIC_LIBRARY_SUFFIX}" ) # The mruby build needs some env vars. Alternatively, look at cmake -P if(CMAKE_VERSION VERSION_LESS "3.1") # XXX works only for Unixes? set(ENV_COMMAND env) else() set(ENV_COMMAND ${CMAKE_COMMAND} -E env) endif() # Required for the Ninja generator. For older CMake, you first have to # invoke 'ninja mruby' before building dependents. if(CMAKE_VERSION VERSION_LESS "3.2") set(_byproducts) else() set(_byproducts BYPRODUCTS "mruby/build/lib/${MRUBY_LIBRARY}") endif() add_custom_target(mruby COMMAND ${ENV_COMMAND} "MRUBY_CONFIG=${CMAKE_CURRENT_SOURCE_DIR}/build_config.rb" "BUILD_DIR=${MRUBY_BUILD_DIR}" "INSTALL_DIR=${MRUBY_BUILD_DIR}/install/bin" "MRUBY_CC=${CMAKE_C_COMPILER}" "MRUBY_CXX=${CMAKE_CXX_COMPILER}" "${CMAKE_CURRENT_SOURCE_DIR}/mruby/minirake" -f "${CMAKE_CURRENT_SOURCE_DIR}/mruby/Rakefile" ${_byproducts} VERBATIM ) # Make the mruby library available to others in this project without them # having to worry about include dirs and the mruby location. add_library(mruby-lib STATIC IMPORTED GLOBAL) set_target_properties(mruby-lib PROPERTIES IMPORTED_LOCATION "${MRUBY_BUILD_DIR}/lib/${MRUBY_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/mruby/include" ) add_dependencies(mruby-lib mruby) set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES mruby/build ) endif() endif() nghttp2-1.68.0/third-party/PaxHeaders/Makefile.in0000644000000000000000000000013215077107305016573 xustar0030 mtime=1761382085.839386001 30 atime=1761382103.861320749 30 ctime=1761382108.211302951 nghttp2-1.68.0/third-party/Makefile.in0000644000175100017510000016160115077107305017170 0ustar00runnerrunner# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # nghttp2 - HTTP/2 C Library # Copyright (c) 2014 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ @ENABLE_THIRD_PARTY_TRUE@@HAVE_NEVERBLEED_TRUE@am__append_1 = libneverbleed.la subdir = third-party ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libllhttp_la_LIBADD = am__libllhttp_la_SOURCES_DIST = llhttp/src/api.c llhttp/src/http.c \ llhttp/src/llhttp.c llhttp/include/llhttp.h am__dirstamp = $(am__leading_dot)dirstamp @ENABLE_THIRD_PARTY_TRUE@am_libllhttp_la_OBJECTS = \ @ENABLE_THIRD_PARTY_TRUE@ llhttp/src/libllhttp_la-api.lo \ @ENABLE_THIRD_PARTY_TRUE@ llhttp/src/libllhttp_la-http.lo \ @ENABLE_THIRD_PARTY_TRUE@ llhttp/src/libllhttp_la-llhttp.lo libllhttp_la_OBJECTS = $(am_libllhttp_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = @ENABLE_THIRD_PARTY_TRUE@am_libllhttp_la_rpath = libneverbleed_la_DEPENDENCIES = am__libneverbleed_la_SOURCES_DIST = neverbleed/neverbleed.c \ neverbleed/neverbleed.h @ENABLE_THIRD_PARTY_TRUE@@HAVE_NEVERBLEED_TRUE@am_libneverbleed_la_OBJECTS = neverbleed/libneverbleed_la-neverbleed.lo libneverbleed_la_OBJECTS = $(am_libneverbleed_la_OBJECTS) @ENABLE_THIRD_PARTY_TRUE@@HAVE_NEVERBLEED_TRUE@am_libneverbleed_la_rpath = liburlparse_la_LIBADD = am__liburlparse_la_SOURCES_DIST = urlparse/urlparse.c \ urlparse/urlparse.h @ENABLE_THIRD_PARTY_TRUE@am_liburlparse_la_OBJECTS = \ @ENABLE_THIRD_PARTY_TRUE@ urlparse/urlparse.lo liburlparse_la_OBJECTS = $(am_liburlparse_la_OBJECTS) @ENABLE_THIRD_PARTY_TRUE@am_liburlparse_la_rpath = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = llhttp/src/$(DEPDIR)/libllhttp_la-api.Plo \ llhttp/src/$(DEPDIR)/libllhttp_la-http.Plo \ llhttp/src/$(DEPDIR)/libllhttp_la-llhttp.Plo \ neverbleed/$(DEPDIR)/libneverbleed_la-neverbleed.Plo \ urlparse/$(DEPDIR)/urlparse.Plo am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libllhttp_la_SOURCES) $(libneverbleed_la_SOURCES) \ $(liburlparse_la_SOURCES) DIST_SOURCES = $(am__libllhttp_la_SOURCES_DIST) \ $(am__libneverbleed_la_SOURCES_DIST) \ $(am__liburlparse_la_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPLDFLAGS = @APPLDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BPFCFLAGS = @BPFCFLAGS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CXX = @CXX@ CXX1XCXXFLAGS = @CXX1XCXXFLAGS@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXTRABPFCFLAGS = @EXTRABPFCFLAGS@ EXTRACFLAG = @EXTRACFLAG@ EXTRA_DEFS = @EXTRA_DEFS@ FGREP = @FGREP@ FILECMD = @FILECMD@ GREP = @GREP@ HAVE_CXX20 = @HAVE_CXX20@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ JANSSON_CFLAGS = @JANSSON_CFLAGS@ JANSSON_LIBS = @JANSSON_LIBS@ JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ JEMALLOC_LIBS = @JEMALLOC_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBBPF_CFLAGS = @LIBBPF_CFLAGS@ LIBBPF_LIBS = @LIBBPF_LIBS@ LIBBROTLIDEC_CFLAGS = @LIBBROTLIDEC_CFLAGS@ LIBBROTLIDEC_LIBS = @LIBBROTLIDEC_LIBS@ LIBBROTLIENC_CFLAGS = @LIBBROTLIENC_CFLAGS@ LIBBROTLIENC_LIBS = @LIBBROTLIENC_LIBS@ LIBCARES_CFLAGS = @LIBCARES_CFLAGS@ LIBCARES_LIBS = @LIBCARES_LIBS@ LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ LIBEV_CFLAGS = @LIBEV_CFLAGS@ LIBEV_LIBS = @LIBEV_LIBS@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@ LIBMRUBY_LIBS = @LIBMRUBY_LIBS@ LIBNGHTTP3_CFLAGS = @LIBNGHTTP3_CFLAGS@ LIBNGHTTP3_LIBS = @LIBNGHTTP3_LIBS@ LIBNGTCP2_CFLAGS = @LIBNGTCP2_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS = @LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_LIBS = @LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS = @LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS@ LIBNGTCP2_CRYPTO_LIBRESSL_LIBS = @LIBNGTCP2_CRYPTO_LIBRESSL_LIBS@ LIBNGTCP2_CRYPTO_OSSL_CFLAGS = @LIBNGTCP2_CRYPTO_OSSL_CFLAGS@ LIBNGTCP2_CRYPTO_OSSL_LIBS = @LIBNGTCP2_CRYPTO_OSSL_LIBS@ LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS = @LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ LIBNGTCP2_CRYPTO_QUICTLS_LIBS = @LIBNGTCP2_CRYPTO_QUICTLS_LIBS@ LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS = @LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS@ LIBNGTCP2_CRYPTO_WOLFSSL_LIBS = @LIBNGTCP2_CRYPTO_WOLFSSL_LIBS@ LIBNGTCP2_LIBS = @LIBNGTCP2_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBTOOL_LDFLAGS = @LIBTOOL_LDFLAGS@ LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ LIBXML2_LIBS = @LIBXML2_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_AGE = @LT_AGE@ LT_CURRENT = @LT_CURRENT@ LT_REVISION = @LT_REVISION@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ SYSTEMD_LIBS = @SYSTEMD_LIBS@ TESTLDADD = @TESTLDADD@ VERSION = @VERSION@ WARNCFLAGS = @WARNCFLAGS@ WARNCXXFLAGS = @WARNCXXFLAGS@ WOLFSSL_CFLAGS = @WOLFSSL_CFLAGS@ WOLFSSL_LIBS = @WOLFSSL_LIBS@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = @DEFS@ # Enumerate all mruby files with the following command: # find mruby -type f ! -ipath 'mruby/.*' | awk '{print "\t"$0" \\"}' EXTRA_DIST = CMakeLists.txt build_config.rb mruby/AUTHORS \ mruby/docker-compose.yml mruby/CONTRIBUTING.md mruby/NEWS \ mruby/tools/lrama/LEGAL.md mruby/tools/lrama/exe/lrama \ mruby/tools/lrama/lib/lrama/state.rb \ mruby/tools/lrama/lib/lrama/counterexamples/path.rb \ mruby/tools/lrama/lib/lrama/counterexamples/start_path.rb \ mruby/tools/lrama/lib/lrama/counterexamples/state_item.rb \ mruby/tools/lrama/lib/lrama/counterexamples/example.rb \ mruby/tools/lrama/lib/lrama/counterexamples/derivation.rb \ mruby/tools/lrama/lib/lrama/counterexamples/production_path.rb \ mruby/tools/lrama/lib/lrama/counterexamples/transition_path.rb \ mruby/tools/lrama/lib/lrama/counterexamples/triple.rb \ mruby/tools/lrama/lib/lrama/states.rb \ mruby/tools/lrama/lib/lrama/option_parser.rb \ mruby/tools/lrama/lib/lrama/states_reporter.rb \ mruby/tools/lrama/lib/lrama/grammar/counter.rb \ mruby/tools/lrama/lib/lrama/grammar/code/rule_action.rb \ mruby/tools/lrama/lib/lrama/grammar/code/initial_action_code.rb \ mruby/tools/lrama/lib/lrama/grammar/code/printer_code.rb \ mruby/tools/lrama/lib/lrama/grammar/code/no_reference_code.rb \ mruby/tools/lrama/lib/lrama/grammar/code/destructor_code.rb \ mruby/tools/lrama/lib/lrama/grammar/union.rb \ mruby/tools/lrama/lib/lrama/grammar/destructor.rb \ mruby/tools/lrama/lib/lrama/grammar/binding.rb \ mruby/tools/lrama/lib/lrama/grammar/reference.rb \ mruby/tools/lrama/lib/lrama/grammar/symbols/resolver.rb \ mruby/tools/lrama/lib/lrama/grammar/auxiliary.rb \ mruby/tools/lrama/lib/lrama/grammar/symbols.rb \ mruby/tools/lrama/lib/lrama/grammar/stdlib.y \ mruby/tools/lrama/lib/lrama/grammar/type.rb \ mruby/tools/lrama/lib/lrama/grammar/percent_code.rb \ mruby/tools/lrama/lib/lrama/grammar/rule.rb \ mruby/tools/lrama/lib/lrama/grammar/symbol.rb \ mruby/tools/lrama/lib/lrama/grammar/error_token.rb \ mruby/tools/lrama/lib/lrama/grammar/rule_builder.rb \ mruby/tools/lrama/lib/lrama/grammar/printer.rb \ mruby/tools/lrama/lib/lrama/grammar/code.rb \ mruby/tools/lrama/lib/lrama/grammar/parameterizing_rule/resolver.rb \ mruby/tools/lrama/lib/lrama/grammar/parameterizing_rule/rhs.rb \ mruby/tools/lrama/lib/lrama/grammar/parameterizing_rule/rule.rb \ mruby/tools/lrama/lib/lrama/grammar/parameterizing_rule.rb \ mruby/tools/lrama/lib/lrama/grammar/precedence.rb \ mruby/tools/lrama/lib/lrama/digraph.rb \ mruby/tools/lrama/lib/lrama/grammar_validator.rb \ mruby/tools/lrama/lib/lrama/context.rb \ mruby/tools/lrama/lib/lrama/version.rb \ mruby/tools/lrama/lib/lrama/bitmap.rb \ mruby/tools/lrama/lib/lrama/state/resolved_conflict.rb \ mruby/tools/lrama/lib/lrama/state/reduce.rb \ mruby/tools/lrama/lib/lrama/state/shift_reduce_conflict.rb \ mruby/tools/lrama/lib/lrama/state/shift.rb \ mruby/tools/lrama/lib/lrama/state/reduce_reduce_conflict.rb \ mruby/tools/lrama/lib/lrama/output.rb \ mruby/tools/lrama/lib/lrama/counterexamples.rb \ mruby/tools/lrama/lib/lrama/trace_reporter.rb \ mruby/tools/lrama/lib/lrama/diagnostics.rb \ mruby/tools/lrama/lib/lrama/command.rb \ mruby/tools/lrama/lib/lrama/logger.rb \ mruby/tools/lrama/lib/lrama/report/duration.rb \ mruby/tools/lrama/lib/lrama/report/profile.rb \ mruby/tools/lrama/lib/lrama/lexer.rb \ mruby/tools/lrama/lib/lrama/parser.rb \ mruby/tools/lrama/lib/lrama/grammar.rb \ mruby/tools/lrama/lib/lrama/options.rb \ mruby/tools/lrama/lib/lrama/lexer/token.rb \ mruby/tools/lrama/lib/lrama/lexer/location.rb \ mruby/tools/lrama/lib/lrama/lexer/token/char.rb \ mruby/tools/lrama/lib/lrama/lexer/token/instantiate_rule.rb \ mruby/tools/lrama/lib/lrama/lexer/token/ident.rb \ mruby/tools/lrama/lib/lrama/lexer/token/user_code.rb \ mruby/tools/lrama/lib/lrama/lexer/token/tag.rb \ mruby/tools/lrama/lib/lrama/lexer/grammar_file.rb \ mruby/tools/lrama/lib/lrama/report.rb \ mruby/tools/lrama/lib/lrama/states/item.rb \ mruby/tools/lrama/lib/lrama.rb \ mruby/tools/lrama/template/bison/yacc.h \ mruby/tools/lrama/template/bison/yacc.c \ mruby/tools/lrama/template/bison/_yacc.h mruby/tools/lrama/MIT \ mruby/tools/lrama/NEWS.md mruby/Gemfile.lock \ mruby/appveyor.yml mruby/Dockerfile mruby/Gemfile \ mruby/benchmark/plot.gpl mruby/benchmark/bm_ao_render.rb \ mruby/benchmark/bm_so_mandelbrot.rb mruby/benchmark/bm_fib.rb \ mruby/benchmark/bm_so_lists.rb \ mruby/benchmark/bm_app_lc_fizzbuzz.rb mruby/Makefile \ mruby/LEGAL mruby/tasks/libmruby.rake mruby/tasks/core.rake \ mruby/tasks/toolchains/visualcpp.rake \ mruby/tasks/toolchains/openwrt.rake \ mruby/tasks/toolchains/android.rake \ mruby/tasks/toolchains/gcc.rake \ mruby/tasks/toolchains/clang.rake \ mruby/tasks/toolchains/emscripten.rake mruby/tasks/doc.rake \ mruby/tasks/benchmark.rake mruby/tasks/mrbgems.rake \ mruby/tasks/presym.rake mruby/tasks/install.rake \ mruby/tasks/bin.rake mruby/tasks/test.rake \ mruby/tasks/mrblib.rake mruby/README.md \ mruby/oss-fuzz/ruby.proto mruby/oss-fuzz/proto_to_ruby.cpp \ mruby/oss-fuzz/mruby_proto_fuzzer.cpp \ mruby/oss-fuzz/config/mruby.dict \ mruby/oss-fuzz/config/mruby_proto_fuzzer.options \ mruby/oss-fuzz/config/mruby_fuzzer.options \ mruby/oss-fuzz/mruby_fuzzer.c mruby/oss-fuzz/proto_to_ruby.h \ mruby/build_config/cross-mingw-winetest.rb \ mruby/build_config/host-f32.rb \ mruby/build_config/nintendo_wii.rb \ mruby/build_config/i586-pc-msdosdjgpp.rb \ mruby/build_config/chipKITMax32.rb \ mruby/build_config/gameboyadvance.rb \ mruby/build_config/minimal.rb mruby/build_config/host-m32.rb \ mruby/build_config/host-shared.rb \ mruby/build_config/host-debug.rb mruby/build_config/bench.rb \ mruby/build_config/host-cxx.rb mruby/build_config/mrbc.rb \ mruby/build_config/no-float.rb \ mruby/build_config/ArduinoDue.rb \ mruby/build_config/IntelEdison.rb \ mruby/build_config/playstationportable.rb \ mruby/build_config/boxing.rb \ mruby/build_config/IntelGalileo.rb \ mruby/build_config/ci/msvc.rb \ mruby/build_config/ci/gcc-clang.rb \ mruby/build_config/nintendo_switch.rb \ mruby/build_config/serenity.rb \ mruby/build_config/dreamcast_shelf.rb \ mruby/build_config/default.rb mruby/build_config/emscripten.rb \ mruby/build_config/android_armeabi_v7a_neon_hard.rb \ mruby/build_config/host-gprof.rb \ mruby/build_config/clang-asan.rb mruby/build_config/RX630.rb \ mruby/build_config/luckfox_pico.rb \ mruby/build_config/host-nofloat.rb \ mruby/build_config/emscripten-cxx.rb \ mruby/build_config/android_arm64_v8a.rb \ mruby/build_config/cross-32bit.rb \ mruby/build_config/milkv_duo.rb \ mruby/build_config/cross-mingw.rb \ mruby/build_config/helpers/wine_runner.rb \ mruby/lib/mruby/build/command.rb \ mruby/lib/mruby/build/load_gems.rb mruby/lib/mruby/lockfile.rb \ mruby/lib/mruby/build.rb mruby/lib/mruby/core_ext.rb \ mruby/lib/mruby/doc.rb mruby/lib/mruby/gem.rb \ mruby/lib/mruby/presym.rb mruby/lib/mruby/source.rb \ mruby/examples/mrbgems/ruby_extension_example/mrbgem.rake \ mruby/examples/mrbgems/ruby_extension_example/README.md \ mruby/examples/mrbgems/ruby_extension_example/test/example.rb \ mruby/examples/mrbgems/ruby_extension_example/mrblib/example.rb \ mruby/examples/mrbgems/cdata_extension_example/mrbgem.rake \ mruby/examples/mrbgems/cdata_extension_example/README.md \ mruby/examples/mrbgems/cdata_extension_example/test/example.c \ mruby/examples/mrbgems/cdata_extension_example/src/example.c \ mruby/examples/mrbgems/c_and_ruby_extension_example/mrbgem.rake \ mruby/examples/mrbgems/c_and_ruby_extension_example/README.md \ mruby/examples/mrbgems/c_and_ruby_extension_example/test/example.rb \ mruby/examples/mrbgems/c_and_ruby_extension_example/mrblib/example.rb \ mruby/examples/mrbgems/c_and_ruby_extension_example/src/example.c \ mruby/examples/mrbgems/c_extension_example/mrbgem.rake \ mruby/examples/mrbgems/c_extension_example/README.md \ mruby/examples/mrbgems/c_extension_example/test/example.rb \ mruby/examples/mrbgems/c_extension_example/test/example.c \ mruby/examples/mrbgems/c_extension_example/src/example.c \ mruby/examples/mrbgems/mruby-YOUR-bigint/TODO-HINT.md \ mruby/examples/mrbgems/mruby-YOUR-bigint/core/bigint.c \ mruby/examples/mrbgems/mruby-YOUR-bigint/mrbgem.rake \ mruby/test/bintest.rb mruby/test/t/enumerable.rb \ mruby/test/t/comparable.rb mruby/test/t/false.rb \ mruby/test/t/exception.rb mruby/test/t/rangeerror.rb \ mruby/test/t/bs_literal.rb mruby/test/t/regexperror.rb \ mruby/test/t/class.rb mruby/test/t/iterations.rb \ mruby/test/t/true.rb mruby/test/t/lang.rb \ mruby/test/t/range.rb mruby/test/t/kernel.rb \ mruby/test/t/unicode.rb mruby/test/t/localjumperror.rb \ mruby/test/t/hash.rb mruby/test/t/syntax.rb \ mruby/test/t/nameerror.rb mruby/test/t/module.rb \ mruby/test/t/argumenterror.rb mruby/test/t/methods.rb \ mruby/test/t/ensure.rb mruby/test/t/indexerror.rb \ mruby/test/t/runtimeerror.rb mruby/test/t/gc.rb \ mruby/test/t/array.rb mruby/test/t/string.rb \ mruby/test/t/proc.rb mruby/test/t/basicobject.rb \ mruby/test/t/integer.rb mruby/test/t/nomethoderror.rb \ mruby/test/t/codegen.rb mruby/test/t/literals.rb \ mruby/test/t/nil.rb mruby/test/t/typeerror.rb \ mruby/test/t/symbol.rb mruby/test/t/object.rb \ mruby/test/t/vformat.rb mruby/test/t/numeric.rb \ mruby/test/t/bs_block.rb mruby/test/t/float.rb \ mruby/test/t/standarderror.rb mruby/test/t/superclass.rb \ mruby/test/assert.rb mruby/TODO.md mruby/Doxyfile \ mruby/mrblib/range.rb mruby/mrblib/kernel.rb \ mruby/mrblib/hash.rb mruby/mrblib/10error.rb \ mruby/mrblib/array.rb mruby/mrblib/string.rb \ mruby/mrblib/compar.rb mruby/mrblib/symbol.rb \ mruby/mrblib/enum.rb mruby/mrblib/numeric.rb \ mruby/build_config.rb mruby/LICENSE mruby/src/etc.c \ mruby/src/variable.c mruby/src/cdump.c mruby/src/init.c \ mruby/src/gc.c mruby/src/load.c mruby/src/codedump.c \ mruby/src/error.c mruby/src/readfloat.c mruby/src/state.c \ mruby/src/string.c mruby/src/numeric.c mruby/src/readnum.c \ mruby/src/enum.c mruby/src/print.c mruby/src/mempool.c \ mruby/src/readint.c mruby/src/numops.c mruby/src/hash.c \ mruby/src/version.c mruby/src/backtrace.c mruby/src/fmt_fp.c \ mruby/src/range.c mruby/src/vm.c mruby/src/debug.c \ mruby/src/symbol.c mruby/src/kernel.c mruby/src/object.c \ mruby/src/proc.c mruby/src/array.c mruby/src/dump.c \ mruby/src/class.c mruby/src/value_array.h mruby/src/allocf.c \ mruby/mruby-source.gemspec \ mruby/mrbgems/mruby-eval/mrbgem.rake \ mruby/mrbgems/mruby-eval/test/binding.rb \ mruby/mrbgems/mruby-eval/test/eval.rb \ mruby/mrbgems/mruby-eval/src/eval.c \ mruby/mrbgems/stdlib.gembox \ mruby/mrbgems/mruby-string-ext/mrbgem.rake \ mruby/mrbgems/mruby-string-ext/test/range.rb \ mruby/mrbgems/mruby-string-ext/test/string.rb \ mruby/mrbgems/mruby-string-ext/test/numeric.rb \ mruby/mrbgems/mruby-string-ext/mrblib/string.rb \ mruby/mrbgems/mruby-string-ext/src/string.c \ mruby/mrbgems/default-no-stdio.gembox \ mruby/mrbgems/mruby-set/mrbgem.rake \ mruby/mrbgems/mruby-set/README.md \ mruby/mrbgems/mruby-set/test/set.rb \ mruby/mrbgems/mruby-set/mrblib/set.rb \ mruby/mrbgems/mruby-set/LICENSE \ mruby/mrbgems/mruby-set/mruby-set.gem \ mruby/mrbgems/mruby-method/mrbgem.rake \ mruby/mrbgems/mruby-method/README.md \ mruby/mrbgems/mruby-method/test/method.rb \ mruby/mrbgems/mruby-method/mrblib/method.rb \ mruby/mrbgems/mruby-method/src/method.c \ mruby/mrbgems/mruby-random/mrbgem.rake \ mruby/mrbgems/mruby-random/test/random.rb \ mruby/mrbgems/mruby-random/src/random.c \ mruby/mrbgems/mruby-catch/mrbgem.rake \ mruby/mrbgems/mruby-catch/test/catch.rb \ mruby/mrbgems/mruby-catch/mrblib/catch.rb \ mruby/mrbgems/mruby-catch/src/catch.c \ mruby/mrbgems/mruby-cmath/mrbgem.rake \ mruby/mrbgems/mruby-cmath/test/cmath.rb \ mruby/mrbgems/mruby-cmath/src/cmath.c \ mruby/mrbgems/mruby-compar-ext/mrbgem.rake \ mruby/mrbgems/mruby-compar-ext/test/compar.rb \ mruby/mrbgems/mruby-compar-ext/mrblib/compar.rb \ mruby/mrbgems/mruby-pack/mrbgem.rake \ mruby/mrbgems/mruby-pack/README.md \ mruby/mrbgems/mruby-pack/test/pack.rb \ mruby/mrbgems/mruby-pack/src/pack.c \ mruby/mrbgems/mruby-struct/mrbgem.rake \ mruby/mrbgems/mruby-struct/test/struct.rb \ mruby/mrbgems/mruby-struct/mrblib/struct.rb \ mruby/mrbgems/mruby-struct/src/struct.c \ mruby/mrbgems/mruby-bigint/core/bigint.c \ mruby/mrbgems/mruby-bigint/core/bigint.h \ mruby/mrbgems/mruby-bigint/mrbgem.rake \ mruby/mrbgems/mruby-bigint/README.md \ mruby/mrbgems/mruby-bigint/test/bigint.rb \ mruby/mrbgems/mruby-bigint/README-fgmp.md \ mruby/mrbgems/mruby-symbol-ext/mrbgem.rake \ mruby/mrbgems/mruby-symbol-ext/test/symbol.rb \ mruby/mrbgems/mruby-symbol-ext/mrblib/symbol.rb \ mruby/mrbgems/mruby-symbol-ext/src/symbol.c \ mruby/mrbgems/mruby-io/mrbgem.rake \ mruby/mrbgems/mruby-io/README.md \ mruby/mrbgems/mruby-io/test/mruby_io_test.c \ mruby/mrbgems/mruby-io/test/io.rb \ mruby/mrbgems/mruby-io/test/file.rb \ mruby/mrbgems/mruby-io/test/file_test.rb \ mruby/mrbgems/mruby-io/mrblib/io.rb \ mruby/mrbgems/mruby-io/mrblib/file.rb \ mruby/mrbgems/mruby-io/mrblib/file_constants.rb \ mruby/mrbgems/mruby-io/mrblib/kernel.rb \ mruby/mrbgems/mruby-io/src/mruby_io_gem.c \ mruby/mrbgems/mruby-io/src/file_test.c \ mruby/mrbgems/mruby-io/src/io.c \ mruby/mrbgems/mruby-io/src/file.c \ mruby/mrbgems/mruby-io/include/mruby/ext/io.h \ mruby/mrbgems/mruby-compiler/core/parse.y \ mruby/mrbgems/mruby-compiler/core/y.tab.c \ mruby/mrbgems/mruby-compiler/core/node.h \ mruby/mrbgems/mruby-compiler/core/keywords \ mruby/mrbgems/mruby-compiler/core/codegen.c \ mruby/mrbgems/mruby-compiler/core/lex.def \ mruby/mrbgems/mruby-compiler/mrbgem.rake \ mruby/mrbgems/mruby-bin-config/mrbgem.rake \ mruby/mrbgems/mruby-bin-config/mruby-config \ mruby/mrbgems/mruby-bin-config/mruby-config.bat \ mruby/mrbgems/mruby-proc-ext/mrbgem.rake \ mruby/mrbgems/mruby-proc-ext/test/proc.rb \ mruby/mrbgems/mruby-proc-ext/test/proc.c \ mruby/mrbgems/mruby-proc-ext/mrblib/proc.rb \ mruby/mrbgems/mruby-proc-ext/src/proc.c \ mruby/mrbgems/mruby-data/mrbgem.rake \ mruby/mrbgems/mruby-data/test/data.rb \ mruby/mrbgems/mruby-data/src/data.c \ mruby/mrbgems/mruby-dir/mrbgem.rake \ mruby/mrbgems/mruby-dir/README.md \ mruby/mrbgems/mruby-dir/test/dir.rb \ mruby/mrbgems/mruby-dir/test/dirtest.c \ mruby/mrbgems/mruby-dir/mrblib/dir.rb \ mruby/mrbgems/mruby-dir/src/Win/dirent.c \ mruby/mrbgems/mruby-dir/src/dir.c \ mruby/mrbgems/mruby-object-ext/mrbgem.rake \ mruby/mrbgems/mruby-object-ext/test/object_ext.c \ mruby/mrbgems/mruby-object-ext/test/nil.rb \ mruby/mrbgems/mruby-object-ext/test/object.rb \ mruby/mrbgems/mruby-object-ext/mrblib/object.rb \ mruby/mrbgems/mruby-object-ext/src/object.c \ mruby/mrbgems/mruby-kernel-ext/mrbgem.rake \ mruby/mrbgems/mruby-kernel-ext/test/kernel.rb \ mruby/mrbgems/mruby-kernel-ext/src/kernel.c \ mruby/mrbgems/mruby-class-ext/mrbgem.rake \ mruby/mrbgems/mruby-class-ext/test/class.rb \ mruby/mrbgems/mruby-class-ext/test/module.rb \ mruby/mrbgems/mruby-class-ext/mrblib/module.rb \ mruby/mrbgems/mruby-class-ext/src/class.c \ mruby/mrbgems/mruby-binding/mrbgem.rake \ mruby/mrbgems/mruby-binding/test/binding.rb \ mruby/mrbgems/mruby-binding/test/binding.c \ mruby/mrbgems/mruby-binding/src/binding.c \ mruby/mrbgems/mruby-hash-ext/mrbgem.rake \ mruby/mrbgems/mruby-hash-ext/test/hash.rb \ mruby/mrbgems/mruby-hash-ext/mrblib/hash.rb \ mruby/mrbgems/mruby-hash-ext/src/hash_ext.c \ mruby/mrbgems/mruby-sleep/example/sleep.rb \ mruby/mrbgems/mruby-sleep/mrbgem.rake \ mruby/mrbgems/mruby-sleep/README.md \ mruby/mrbgems/mruby-sleep/test/sleep_test.rb \ mruby/mrbgems/mruby-sleep/src/sleep.c \ mruby/mrbgems/mruby-sprintf/mrbgem.rake \ mruby/mrbgems/mruby-sprintf/test/sprintf.rb \ mruby/mrbgems/mruby-sprintf/mrblib/string.rb \ mruby/mrbgems/mruby-sprintf/src/sprintf.c \ mruby/mrbgems/mruby-enum-chain/mrbgem.rake \ mruby/mrbgems/mruby-enum-chain/test/enum_chain.rb \ mruby/mrbgems/mruby-enum-chain/mrblib/chain.rb \ mruby/mrbgems/mruby-range-ext/mrbgem.rake \ mruby/mrbgems/mruby-range-ext/test/range.rb \ mruby/mrbgems/mruby-range-ext/mrblib/range.rb \ mruby/mrbgems/mruby-range-ext/src/range.c \ mruby/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c \ mruby/mrbgems/mruby-bin-mirb/mrbgem.rake \ mruby/mrbgems/mruby-bin-mirb/bintest/mirb.rb \ mruby/mrbgems/mruby-metaprog/mrbgem.rake \ mruby/mrbgems/mruby-metaprog/test/metaprog.rb \ mruby/mrbgems/mruby-metaprog/src/metaprog.c \ mruby/mrbgems/mruby-test-inline-struct/mrbgem.rake \ mruby/mrbgems/mruby-test-inline-struct/test/inline.c \ mruby/mrbgems/mruby-test-inline-struct/test/inline.rb \ mruby/mrbgems/mruby-time/mrbgem.rake \ mruby/mrbgems/mruby-time/test/time.rb \ mruby/mrbgems/mruby-time/src/time.c \ mruby/mrbgems/mruby-time/include/mruby/time.h \ mruby/mrbgems/mruby-proc-binding/mrbgem.rake \ mruby/mrbgems/mruby-proc-binding/test/proc_binding.c \ mruby/mrbgems/mruby-proc-binding/test/proc_binding.rb \ mruby/mrbgems/mruby-proc-binding/src/proc_binding.c \ mruby/mrbgems/stdlib-ext.gembox mruby/mrbgems/metaprog.gembox \ mruby/mrbgems/mruby-enumerator/mrbgem.rake \ mruby/mrbgems/mruby-enumerator/test/enumerator.rb \ mruby/mrbgems/mruby-enumerator/mrblib/enumerator.rb \ mruby/mrbgems/math.gembox \ mruby/mrbgems/mruby-socket/mrbgem.rake \ mruby/mrbgems/mruby-socket/README.md \ mruby/mrbgems/mruby-socket/test/socket.rb \ mruby/mrbgems/mruby-socket/test/udpsocket.rb \ mruby/mrbgems/mruby-socket/test/tcpsocket.rb \ mruby/mrbgems/mruby-socket/test/sockettest.c \ mruby/mrbgems/mruby-socket/test/basicsocket.rb \ mruby/mrbgems/mruby-socket/test/addrinfo.rb \ mruby/mrbgems/mruby-socket/test/unix.rb \ mruby/mrbgems/mruby-socket/test/ipsocket.rb \ mruby/mrbgems/mruby-socket/mrblib/socket.rb \ mruby/mrbgems/mruby-socket/src/socket.c \ mruby/mrbgems/mruby-socket/src/gen.rb \ mruby/mrbgems/mruby-socket/src/const.cstub \ mruby/mrbgems/mruby-socket/src/const.def \ mruby/mrbgems/mruby-objectspace/mrbgem.rake \ mruby/mrbgems/mruby-objectspace/test/objectspace.rb \ mruby/mrbgems/mruby-objectspace/src/mruby_objectspace.c \ mruby/mrbgems/full-core.gembox \ mruby/mrbgems/mruby-rational/mrbgem.rake \ mruby/mrbgems/mruby-rational/test/rational.rb \ mruby/mrbgems/mruby-rational/mrblib/rational.rb \ mruby/mrbgems/mruby-rational/src/rational.c \ mruby/mrbgems/mruby-numeric-ext/mrbgem.rake \ mruby/mrbgems/mruby-numeric-ext/test/numeric.rb \ mruby/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb \ mruby/mrbgems/mruby-numeric-ext/src/numeric_ext.c \ mruby/mrbgems/mruby-fiber/mrbgem.rake \ mruby/mrbgems/mruby-fiber/test/fiber2.rb \ mruby/mrbgems/mruby-fiber/test/fibertest.c \ mruby/mrbgems/mruby-fiber/test/fiber.rb \ mruby/mrbgems/mruby-fiber/src/fiber.c \ mruby/mrbgems/mruby-complex/mrbgem.rake \ mruby/mrbgems/mruby-complex/test/complex.rb \ mruby/mrbgems/mruby-complex/mrblib/complex.rb \ mruby/mrbgems/mruby-complex/src/complex.c \ mruby/mrbgems/mruby-exit/mrbgem.rake \ mruby/mrbgems/mruby-exit/src/mruby_exit.c \ mruby/mrbgems/mruby-error/mrbgem.rake \ mruby/mrbgems/mruby-error/test/exception.rb \ mruby/mrbgems/mruby-error/test/exception.c \ mruby/mrbgems/mruby-error/src/exception.c \ mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c \ mruby/mrbgems/mruby-bin-mruby/mrbgem.rake \ mruby/mrbgems/mruby-bin-mruby/bintest/mruby.rb \ mruby/mrbgems/mruby-test/mrbgem.rake \ mruby/mrbgems/mruby-test/vformat.c \ mruby/mrbgems/mruby-test/README.md \ mruby/mrbgems/mruby-test/driver.c \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apistring.h \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.h \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apistring.c \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.h \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c \ mruby/mrbgems/mruby-bin-debugger/mrbgem.rake \ mruby/mrbgems/mruby-bin-debugger/bintest/mrdb.rb \ mruby/mrbgems/mruby-bin-debugger/bintest/print.rb \ mruby/mrbgems/mruby-array-ext/mrbgem.rake \ mruby/mrbgems/mruby-array-ext/test/array.rb \ mruby/mrbgems/mruby-array-ext/mrblib/array.rb \ mruby/mrbgems/mruby-array-ext/src/array.c \ mruby/mrbgems/mruby-enum-lazy/mrbgem.rake \ mruby/mrbgems/mruby-enum-lazy/test/lazy.rb \ mruby/mrbgems/mruby-enum-lazy/mrblib/lazy.rb \ mruby/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby_strip.c \ mruby/mrbgems/mruby-bin-strip/mrbgem.rake \ mruby/mrbgems/mruby-bin-strip/bintest/mruby_strip.rb \ mruby/mrbgems/mruby-errno/mrbgem.rake \ mruby/mrbgems/mruby-errno/README.md \ mruby/mrbgems/mruby-errno/test/errno.rb \ mruby/mrbgems/mruby-errno/mrblib/errno.rb \ mruby/mrbgems/mruby-errno/src/gen.rb \ mruby/mrbgems/mruby-errno/src/known_errors_def.cstub \ mruby/mrbgems/mruby-errno/src/known_errors.def \ mruby/mrbgems/mruby-errno/src/errno.c \ mruby/mrbgems/mruby-toplevel-ext/mrbgem.rake \ mruby/mrbgems/mruby-toplevel-ext/test/toplevel.rb \ mruby/mrbgems/mruby-toplevel-ext/mrblib/toplevel.rb \ mruby/mrbgems/mruby-math/mrbgem.rake \ mruby/mrbgems/mruby-math/test/math.rb \ mruby/mrbgems/mruby-math/src/math.c \ mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/stub.c \ mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c \ mruby/mrbgems/mruby-bin-mrbc/mrbgem.rake \ mruby/mrbgems/mruby-bin-mrbc/bintest/mrbc.rb \ mruby/mrbgems/mruby-encoding/mrbgem.rake \ mruby/mrbgems/mruby-encoding/test/string.rb \ mruby/mrbgems/mruby-encoding/test/numeric.rb \ mruby/mrbgems/mruby-encoding/src/encoding.c \ mruby/mrbgems/stdlib-io.gembox mruby/mrbgems/default.gembox \ mruby/mrbgems/mruby-os-memsize/mrbgem.rake \ mruby/mrbgems/mruby-os-memsize/test/memsize.rb \ mruby/mrbgems/mruby-os-memsize/src/memsize.c \ mruby/mrbgems/default-no-fpu.gembox \ mruby/mrbgems/mruby-enum-ext/mrbgem.rake \ mruby/mrbgems/mruby-enum-ext/test/enum.rb \ mruby/mrbgems/mruby-enum-ext/mrblib/enum.rb mruby/Rakefile \ mruby/SECURITY.md mruby/doc/mruby_logo_red_icon.png \ mruby/doc/mruby3.0.md mruby/doc/mruby3.1.md \ mruby/doc/internal/opcode.md mruby/doc/internal/boxing.md \ mruby/doc/limitations.md mruby/doc/mruby3.2.md \ mruby/doc/mruby3.4.md mruby/doc/mruby3.3.md \ mruby/doc/guides/compile.md mruby/doc/guides/memory.md \ mruby/doc/guides/mrbgems.md mruby/doc/guides/debugger.md \ mruby/doc/guides/hier.md mruby/doc/guides/link.md \ mruby/doc/guides/symbol.md mruby/doc/guides/gc-arena-howto.md \ mruby/doc/guides/mrbconf.md mruby/minirake \ mruby/include/mruby/opcode.h mruby/include/mruby/re.h \ mruby/include/mruby/hash.h mruby/include/mruby/string.h \ mruby/include/mruby/presym.h mruby/include/mruby/mempool.h \ mruby/include/mruby/object.h mruby/include/mruby/class.h \ mruby/include/mruby/ops.h mruby/include/mruby/irep.h \ mruby/include/mruby/compile.h mruby/include/mruby/array.h \ mruby/include/mruby/range.h mruby/include/mruby/throw.h \ mruby/include/mruby/variable.h \ mruby/include/mruby/boxing_word.h \ mruby/include/mruby/istruct.h mruby/include/mruby/data.h \ mruby/include/mruby/common.h mruby/include/mruby/error.h \ mruby/include/mruby/debug.h mruby/include/mruby/boxing_no.h \ mruby/include/mruby/numeric.h mruby/include/mruby/boxing_nan.h \ mruby/include/mruby/value.h mruby/include/mruby/endian.h \ mruby/include/mruby/internal.h mruby/include/mruby/dump.h \ mruby/include/mruby/version.h mruby/include/mruby/khash.h \ mruby/include/mruby/gc.h mruby/include/mruby/presym/enable.h \ mruby/include/mruby/presym/disable.h \ mruby/include/mruby/presym/scanning.h \ mruby/include/mruby/proc.h mruby/include/mrbconf.h \ mruby/include/mruby.h @ENABLE_THIRD_PARTY_TRUE@noinst_LTLIBRARIES = liburlparse.la \ @ENABLE_THIRD_PARTY_TRUE@ libllhttp.la $(am__append_1) @ENABLE_THIRD_PARTY_TRUE@liburlparse_la_SOURCES = \ @ENABLE_THIRD_PARTY_TRUE@ urlparse/urlparse.c \ @ENABLE_THIRD_PARTY_TRUE@ urlparse/urlparse.h @ENABLE_THIRD_PARTY_TRUE@libllhttp_la_SOURCES = \ @ENABLE_THIRD_PARTY_TRUE@ llhttp/src/api.c \ @ENABLE_THIRD_PARTY_TRUE@ llhttp/src/http.c \ @ENABLE_THIRD_PARTY_TRUE@ llhttp/src/llhttp.c \ @ENABLE_THIRD_PARTY_TRUE@ llhttp/include/llhttp.h @ENABLE_THIRD_PARTY_TRUE@libllhttp_la_CPPFLAGS = -I${srcdir}/llhttp/include @ENABLE_THIRD_PARTY_TRUE@@HAVE_NEVERBLEED_TRUE@libneverbleed_la_CPPFLAGS = @OPENSSL_CFLAGS@ -D_GNU_SOURCE @ENABLE_THIRD_PARTY_TRUE@@HAVE_NEVERBLEED_TRUE@libneverbleed_la_LIBADD = @OPENSSL_LIBS@ @ENABLE_THIRD_PARTY_TRUE@@HAVE_NEVERBLEED_TRUE@libneverbleed_la_SOURCES = neverbleed/neverbleed.c neverbleed/neverbleed.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu third-party/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu third-party/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } llhttp/src/$(am__dirstamp): @$(MKDIR_P) llhttp/src @: > llhttp/src/$(am__dirstamp) llhttp/src/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) llhttp/src/$(DEPDIR) @: > llhttp/src/$(DEPDIR)/$(am__dirstamp) llhttp/src/libllhttp_la-api.lo: llhttp/src/$(am__dirstamp) \ llhttp/src/$(DEPDIR)/$(am__dirstamp) llhttp/src/libllhttp_la-http.lo: llhttp/src/$(am__dirstamp) \ llhttp/src/$(DEPDIR)/$(am__dirstamp) llhttp/src/libllhttp_la-llhttp.lo: llhttp/src/$(am__dirstamp) \ llhttp/src/$(DEPDIR)/$(am__dirstamp) libllhttp.la: $(libllhttp_la_OBJECTS) $(libllhttp_la_DEPENDENCIES) $(EXTRA_libllhttp_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(am_libllhttp_la_rpath) $(libllhttp_la_OBJECTS) $(libllhttp_la_LIBADD) $(LIBS) neverbleed/$(am__dirstamp): @$(MKDIR_P) neverbleed @: > neverbleed/$(am__dirstamp) neverbleed/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) neverbleed/$(DEPDIR) @: > neverbleed/$(DEPDIR)/$(am__dirstamp) neverbleed/libneverbleed_la-neverbleed.lo: neverbleed/$(am__dirstamp) \ neverbleed/$(DEPDIR)/$(am__dirstamp) libneverbleed.la: $(libneverbleed_la_OBJECTS) $(libneverbleed_la_DEPENDENCIES) $(EXTRA_libneverbleed_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(am_libneverbleed_la_rpath) $(libneverbleed_la_OBJECTS) $(libneverbleed_la_LIBADD) $(LIBS) urlparse/$(am__dirstamp): @$(MKDIR_P) urlparse @: > urlparse/$(am__dirstamp) urlparse/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) urlparse/$(DEPDIR) @: > urlparse/$(DEPDIR)/$(am__dirstamp) urlparse/urlparse.lo: urlparse/$(am__dirstamp) \ urlparse/$(DEPDIR)/$(am__dirstamp) liburlparse.la: $(liburlparse_la_OBJECTS) $(liburlparse_la_DEPENDENCIES) $(EXTRA_liburlparse_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(am_liburlparse_la_rpath) $(liburlparse_la_OBJECTS) $(liburlparse_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f llhttp/src/*.$(OBJEXT) -rm -f llhttp/src/*.lo -rm -f neverbleed/*.$(OBJEXT) -rm -f neverbleed/*.lo -rm -f urlparse/*.$(OBJEXT) -rm -f urlparse/*.lo distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@llhttp/src/$(DEPDIR)/libllhttp_la-api.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@llhttp/src/$(DEPDIR)/libllhttp_la-http.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@llhttp/src/$(DEPDIR)/libllhttp_la-llhttp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@neverbleed/$(DEPDIR)/libneverbleed_la-neverbleed.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@urlparse/$(DEPDIR)/urlparse.Plo@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< llhttp/src/libllhttp_la-api.lo: llhttp/src/api.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libllhttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT llhttp/src/libllhttp_la-api.lo -MD -MP -MF llhttp/src/$(DEPDIR)/libllhttp_la-api.Tpo -c -o llhttp/src/libllhttp_la-api.lo `test -f 'llhttp/src/api.c' || echo '$(srcdir)/'`llhttp/src/api.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) llhttp/src/$(DEPDIR)/libllhttp_la-api.Tpo llhttp/src/$(DEPDIR)/libllhttp_la-api.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='llhttp/src/api.c' object='llhttp/src/libllhttp_la-api.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libllhttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o llhttp/src/libllhttp_la-api.lo `test -f 'llhttp/src/api.c' || echo '$(srcdir)/'`llhttp/src/api.c llhttp/src/libllhttp_la-http.lo: llhttp/src/http.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libllhttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT llhttp/src/libllhttp_la-http.lo -MD -MP -MF llhttp/src/$(DEPDIR)/libllhttp_la-http.Tpo -c -o llhttp/src/libllhttp_la-http.lo `test -f 'llhttp/src/http.c' || echo '$(srcdir)/'`llhttp/src/http.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) llhttp/src/$(DEPDIR)/libllhttp_la-http.Tpo llhttp/src/$(DEPDIR)/libllhttp_la-http.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='llhttp/src/http.c' object='llhttp/src/libllhttp_la-http.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libllhttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o llhttp/src/libllhttp_la-http.lo `test -f 'llhttp/src/http.c' || echo '$(srcdir)/'`llhttp/src/http.c llhttp/src/libllhttp_la-llhttp.lo: llhttp/src/llhttp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libllhttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT llhttp/src/libllhttp_la-llhttp.lo -MD -MP -MF llhttp/src/$(DEPDIR)/libllhttp_la-llhttp.Tpo -c -o llhttp/src/libllhttp_la-llhttp.lo `test -f 'llhttp/src/llhttp.c' || echo '$(srcdir)/'`llhttp/src/llhttp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) llhttp/src/$(DEPDIR)/libllhttp_la-llhttp.Tpo llhttp/src/$(DEPDIR)/libllhttp_la-llhttp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='llhttp/src/llhttp.c' object='llhttp/src/libllhttp_la-llhttp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libllhttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o llhttp/src/libllhttp_la-llhttp.lo `test -f 'llhttp/src/llhttp.c' || echo '$(srcdir)/'`llhttp/src/llhttp.c neverbleed/libneverbleed_la-neverbleed.lo: neverbleed/neverbleed.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libneverbleed_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT neverbleed/libneverbleed_la-neverbleed.lo -MD -MP -MF neverbleed/$(DEPDIR)/libneverbleed_la-neverbleed.Tpo -c -o neverbleed/libneverbleed_la-neverbleed.lo `test -f 'neverbleed/neverbleed.c' || echo '$(srcdir)/'`neverbleed/neverbleed.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) neverbleed/$(DEPDIR)/libneverbleed_la-neverbleed.Tpo neverbleed/$(DEPDIR)/libneverbleed_la-neverbleed.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='neverbleed/neverbleed.c' object='neverbleed/libneverbleed_la-neverbleed.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libneverbleed_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o neverbleed/libneverbleed_la-neverbleed.lo `test -f 'neverbleed/neverbleed.c' || echo '$(srcdir)/'`neverbleed/neverbleed.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs -rm -rf llhttp/src/.libs llhttp/src/_libs -rm -rf neverbleed/.libs neverbleed/_libs -rm -rf urlparse/.libs urlparse/_libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am @ENABLE_THIRD_PARTY_FALSE@all-local: @HAVE_MRUBY_FALSE@all-local: all-am: Makefile $(LTLIBRARIES) all-local installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -rm -f llhttp/src/$(DEPDIR)/$(am__dirstamp) -rm -f llhttp/src/$(am__dirstamp) -rm -f neverbleed/$(DEPDIR)/$(am__dirstamp) -rm -f neverbleed/$(am__dirstamp) -rm -f urlparse/$(DEPDIR)/$(am__dirstamp) -rm -f urlparse/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." @ENABLE_THIRD_PARTY_FALSE@clean-local: @HAVE_MRUBY_FALSE@clean-local: clean: clean-am clean-am: clean-generic clean-libtool clean-local \ clean-noinstLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -f llhttp/src/$(DEPDIR)/libllhttp_la-api.Plo -rm -f llhttp/src/$(DEPDIR)/libllhttp_la-http.Plo -rm -f llhttp/src/$(DEPDIR)/libllhttp_la-llhttp.Plo -rm -f neverbleed/$(DEPDIR)/libneverbleed_la-neverbleed.Plo -rm -f urlparse/$(DEPDIR)/urlparse.Plo -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f llhttp/src/$(DEPDIR)/libllhttp_la-api.Plo -rm -f llhttp/src/$(DEPDIR)/libllhttp_la-http.Plo -rm -f llhttp/src/$(DEPDIR)/libllhttp_la-llhttp.Plo -rm -f neverbleed/$(DEPDIR)/libneverbleed_la-neverbleed.Plo -rm -f urlparse/$(DEPDIR)/urlparse.Plo -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am all-local am--depfiles check \ check-am clean clean-generic clean-libtool clean-local \ clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am .PRECIOUS: Makefile @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@.PHONY: all-local clean mruby @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@mruby: @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@ mkdir -p "${abs_builddir}/mruby/build" @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@ diff "${srcdir}/build_config.rb" "${abs_builddir}/mruby/build/build_config.rb" >& /dev/null || \ @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@ cp "${srcdir}/build_config.rb" "${abs_builddir}/mruby/build" @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@ MRUBY_CONFIG="${abs_builddir}/mruby/build/build_config.rb" \ @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@ BUILD_DIR="${abs_builddir}/mruby/build" \ @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@ INSTALL_DIR="${abs_builddir}/mruby/build/install/bin" \ @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@ MRUBY_CC="${CC}" MRUBY_CXX="$(firstword $(CXX))" MRUBY_LD="${LD}" \ @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@ MRUBY_AR="${AR}" \ @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@ HOST="${host}" BUILD="${build}" \ @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@ "${srcdir}/mruby/minirake" -f "${srcdir}/mruby/Rakefile" @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@all-local: mruby @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@clean-local: @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@ [ ! -f "${abs_builddir}/mruby/build/build_config.rb" ] || \ @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@ MRUBY_CONFIG="${abs_builddir}/mruby/build/build_config.rb" \ @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@ BUILD_DIR="${abs_builddir}/mruby/build" \ @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@ MRUBY_CC="${CC}" \ @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@ "${srcdir}/mruby/minirake" -f "${srcdir}/mruby/Rakefile" clean # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nghttp2-1.68.0/third-party/PaxHeaders/Makefile.am0000644000000000000000000000013215077107271016564 xustar0030 mtime=1761382073.009444065 30 atime=1761382080.108411471 30 ctime=1761382108.210302954 nghttp2-1.68.0/third-party/Makefile.am0000644000175100017510000006521115077107271017161 0ustar00runnerrunner# nghttp2 - HTTP/2 C Library # Copyright (c) 2014 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. AM_CPPFLAGS = @DEFS@ EXTRA_DIST = CMakeLists.txt build_config.rb # Enumerate all mruby files with the following command: # find mruby -type f ! -ipath 'mruby/.*' | awk '{print "\t"$0" \\"}' EXTRA_DIST += \ mruby/AUTHORS \ mruby/docker-compose.yml \ mruby/CONTRIBUTING.md \ mruby/NEWS \ mruby/tools/lrama/LEGAL.md \ mruby/tools/lrama/exe/lrama \ mruby/tools/lrama/lib/lrama/state.rb \ mruby/tools/lrama/lib/lrama/counterexamples/path.rb \ mruby/tools/lrama/lib/lrama/counterexamples/start_path.rb \ mruby/tools/lrama/lib/lrama/counterexamples/state_item.rb \ mruby/tools/lrama/lib/lrama/counterexamples/example.rb \ mruby/tools/lrama/lib/lrama/counterexamples/derivation.rb \ mruby/tools/lrama/lib/lrama/counterexamples/production_path.rb \ mruby/tools/lrama/lib/lrama/counterexamples/transition_path.rb \ mruby/tools/lrama/lib/lrama/counterexamples/triple.rb \ mruby/tools/lrama/lib/lrama/states.rb \ mruby/tools/lrama/lib/lrama/option_parser.rb \ mruby/tools/lrama/lib/lrama/states_reporter.rb \ mruby/tools/lrama/lib/lrama/grammar/counter.rb \ mruby/tools/lrama/lib/lrama/grammar/code/rule_action.rb \ mruby/tools/lrama/lib/lrama/grammar/code/initial_action_code.rb \ mruby/tools/lrama/lib/lrama/grammar/code/printer_code.rb \ mruby/tools/lrama/lib/lrama/grammar/code/no_reference_code.rb \ mruby/tools/lrama/lib/lrama/grammar/code/destructor_code.rb \ mruby/tools/lrama/lib/lrama/grammar/union.rb \ mruby/tools/lrama/lib/lrama/grammar/destructor.rb \ mruby/tools/lrama/lib/lrama/grammar/binding.rb \ mruby/tools/lrama/lib/lrama/grammar/reference.rb \ mruby/tools/lrama/lib/lrama/grammar/symbols/resolver.rb \ mruby/tools/lrama/lib/lrama/grammar/auxiliary.rb \ mruby/tools/lrama/lib/lrama/grammar/symbols.rb \ mruby/tools/lrama/lib/lrama/grammar/stdlib.y \ mruby/tools/lrama/lib/lrama/grammar/type.rb \ mruby/tools/lrama/lib/lrama/grammar/percent_code.rb \ mruby/tools/lrama/lib/lrama/grammar/rule.rb \ mruby/tools/lrama/lib/lrama/grammar/symbol.rb \ mruby/tools/lrama/lib/lrama/grammar/error_token.rb \ mruby/tools/lrama/lib/lrama/grammar/rule_builder.rb \ mruby/tools/lrama/lib/lrama/grammar/printer.rb \ mruby/tools/lrama/lib/lrama/grammar/code.rb \ mruby/tools/lrama/lib/lrama/grammar/parameterizing_rule/resolver.rb \ mruby/tools/lrama/lib/lrama/grammar/parameterizing_rule/rhs.rb \ mruby/tools/lrama/lib/lrama/grammar/parameterizing_rule/rule.rb \ mruby/tools/lrama/lib/lrama/grammar/parameterizing_rule.rb \ mruby/tools/lrama/lib/lrama/grammar/precedence.rb \ mruby/tools/lrama/lib/lrama/digraph.rb \ mruby/tools/lrama/lib/lrama/grammar_validator.rb \ mruby/tools/lrama/lib/lrama/context.rb \ mruby/tools/lrama/lib/lrama/version.rb \ mruby/tools/lrama/lib/lrama/bitmap.rb \ mruby/tools/lrama/lib/lrama/state/resolved_conflict.rb \ mruby/tools/lrama/lib/lrama/state/reduce.rb \ mruby/tools/lrama/lib/lrama/state/shift_reduce_conflict.rb \ mruby/tools/lrama/lib/lrama/state/shift.rb \ mruby/tools/lrama/lib/lrama/state/reduce_reduce_conflict.rb \ mruby/tools/lrama/lib/lrama/output.rb \ mruby/tools/lrama/lib/lrama/counterexamples.rb \ mruby/tools/lrama/lib/lrama/trace_reporter.rb \ mruby/tools/lrama/lib/lrama/diagnostics.rb \ mruby/tools/lrama/lib/lrama/command.rb \ mruby/tools/lrama/lib/lrama/logger.rb \ mruby/tools/lrama/lib/lrama/report/duration.rb \ mruby/tools/lrama/lib/lrama/report/profile.rb \ mruby/tools/lrama/lib/lrama/lexer.rb \ mruby/tools/lrama/lib/lrama/parser.rb \ mruby/tools/lrama/lib/lrama/grammar.rb \ mruby/tools/lrama/lib/lrama/options.rb \ mruby/tools/lrama/lib/lrama/lexer/token.rb \ mruby/tools/lrama/lib/lrama/lexer/location.rb \ mruby/tools/lrama/lib/lrama/lexer/token/char.rb \ mruby/tools/lrama/lib/lrama/lexer/token/instantiate_rule.rb \ mruby/tools/lrama/lib/lrama/lexer/token/ident.rb \ mruby/tools/lrama/lib/lrama/lexer/token/user_code.rb \ mruby/tools/lrama/lib/lrama/lexer/token/tag.rb \ mruby/tools/lrama/lib/lrama/lexer/grammar_file.rb \ mruby/tools/lrama/lib/lrama/report.rb \ mruby/tools/lrama/lib/lrama/states/item.rb \ mruby/tools/lrama/lib/lrama.rb \ mruby/tools/lrama/template/bison/yacc.h \ mruby/tools/lrama/template/bison/yacc.c \ mruby/tools/lrama/template/bison/_yacc.h \ mruby/tools/lrama/MIT \ mruby/tools/lrama/NEWS.md \ mruby/Gemfile.lock \ mruby/appveyor.yml \ mruby/Dockerfile \ mruby/Gemfile \ mruby/benchmark/plot.gpl \ mruby/benchmark/bm_ao_render.rb \ mruby/benchmark/bm_so_mandelbrot.rb \ mruby/benchmark/bm_fib.rb \ mruby/benchmark/bm_so_lists.rb \ mruby/benchmark/bm_app_lc_fizzbuzz.rb \ mruby/Makefile \ mruby/LEGAL \ mruby/tasks/libmruby.rake \ mruby/tasks/core.rake \ mruby/tasks/toolchains/visualcpp.rake \ mruby/tasks/toolchains/openwrt.rake \ mruby/tasks/toolchains/android.rake \ mruby/tasks/toolchains/gcc.rake \ mruby/tasks/toolchains/clang.rake \ mruby/tasks/toolchains/emscripten.rake \ mruby/tasks/doc.rake \ mruby/tasks/benchmark.rake \ mruby/tasks/mrbgems.rake \ mruby/tasks/presym.rake \ mruby/tasks/install.rake \ mruby/tasks/bin.rake \ mruby/tasks/test.rake \ mruby/tasks/mrblib.rake \ mruby/README.md \ mruby/oss-fuzz/ruby.proto \ mruby/oss-fuzz/proto_to_ruby.cpp \ mruby/oss-fuzz/mruby_proto_fuzzer.cpp \ mruby/oss-fuzz/config/mruby.dict \ mruby/oss-fuzz/config/mruby_proto_fuzzer.options \ mruby/oss-fuzz/config/mruby_fuzzer.options \ mruby/oss-fuzz/mruby_fuzzer.c \ mruby/oss-fuzz/proto_to_ruby.h \ mruby/build_config/cross-mingw-winetest.rb \ mruby/build_config/host-f32.rb \ mruby/build_config/nintendo_wii.rb \ mruby/build_config/i586-pc-msdosdjgpp.rb \ mruby/build_config/chipKITMax32.rb \ mruby/build_config/gameboyadvance.rb \ mruby/build_config/minimal.rb \ mruby/build_config/host-m32.rb \ mruby/build_config/host-shared.rb \ mruby/build_config/host-debug.rb \ mruby/build_config/bench.rb \ mruby/build_config/host-cxx.rb \ mruby/build_config/mrbc.rb \ mruby/build_config/no-float.rb \ mruby/build_config/ArduinoDue.rb \ mruby/build_config/IntelEdison.rb \ mruby/build_config/playstationportable.rb \ mruby/build_config/boxing.rb \ mruby/build_config/IntelGalileo.rb \ mruby/build_config/ci/msvc.rb \ mruby/build_config/ci/gcc-clang.rb \ mruby/build_config/nintendo_switch.rb \ mruby/build_config/serenity.rb \ mruby/build_config/dreamcast_shelf.rb \ mruby/build_config/default.rb \ mruby/build_config/emscripten.rb \ mruby/build_config/android_armeabi_v7a_neon_hard.rb \ mruby/build_config/host-gprof.rb \ mruby/build_config/clang-asan.rb \ mruby/build_config/RX630.rb \ mruby/build_config/luckfox_pico.rb \ mruby/build_config/host-nofloat.rb \ mruby/build_config/emscripten-cxx.rb \ mruby/build_config/android_arm64_v8a.rb \ mruby/build_config/cross-32bit.rb \ mruby/build_config/milkv_duo.rb \ mruby/build_config/cross-mingw.rb \ mruby/build_config/helpers/wine_runner.rb \ mruby/lib/mruby/build/command.rb \ mruby/lib/mruby/build/load_gems.rb \ mruby/lib/mruby/lockfile.rb \ mruby/lib/mruby/build.rb \ mruby/lib/mruby/core_ext.rb \ mruby/lib/mruby/doc.rb \ mruby/lib/mruby/gem.rb \ mruby/lib/mruby/presym.rb \ mruby/lib/mruby/source.rb \ mruby/examples/mrbgems/ruby_extension_example/mrbgem.rake \ mruby/examples/mrbgems/ruby_extension_example/README.md \ mruby/examples/mrbgems/ruby_extension_example/test/example.rb \ mruby/examples/mrbgems/ruby_extension_example/mrblib/example.rb \ mruby/examples/mrbgems/cdata_extension_example/mrbgem.rake \ mruby/examples/mrbgems/cdata_extension_example/README.md \ mruby/examples/mrbgems/cdata_extension_example/test/example.c \ mruby/examples/mrbgems/cdata_extension_example/src/example.c \ mruby/examples/mrbgems/c_and_ruby_extension_example/mrbgem.rake \ mruby/examples/mrbgems/c_and_ruby_extension_example/README.md \ mruby/examples/mrbgems/c_and_ruby_extension_example/test/example.rb \ mruby/examples/mrbgems/c_and_ruby_extension_example/mrblib/example.rb \ mruby/examples/mrbgems/c_and_ruby_extension_example/src/example.c \ mruby/examples/mrbgems/c_extension_example/mrbgem.rake \ mruby/examples/mrbgems/c_extension_example/README.md \ mruby/examples/mrbgems/c_extension_example/test/example.rb \ mruby/examples/mrbgems/c_extension_example/test/example.c \ mruby/examples/mrbgems/c_extension_example/src/example.c \ mruby/examples/mrbgems/mruby-YOUR-bigint/TODO-HINT.md \ mruby/examples/mrbgems/mruby-YOUR-bigint/core/bigint.c \ mruby/examples/mrbgems/mruby-YOUR-bigint/mrbgem.rake \ mruby/test/bintest.rb \ mruby/test/t/enumerable.rb \ mruby/test/t/comparable.rb \ mruby/test/t/false.rb \ mruby/test/t/exception.rb \ mruby/test/t/rangeerror.rb \ mruby/test/t/bs_literal.rb \ mruby/test/t/regexperror.rb \ mruby/test/t/class.rb \ mruby/test/t/iterations.rb \ mruby/test/t/true.rb \ mruby/test/t/lang.rb \ mruby/test/t/range.rb \ mruby/test/t/kernel.rb \ mruby/test/t/unicode.rb \ mruby/test/t/localjumperror.rb \ mruby/test/t/hash.rb \ mruby/test/t/syntax.rb \ mruby/test/t/nameerror.rb \ mruby/test/t/module.rb \ mruby/test/t/argumenterror.rb \ mruby/test/t/methods.rb \ mruby/test/t/ensure.rb \ mruby/test/t/indexerror.rb \ mruby/test/t/runtimeerror.rb \ mruby/test/t/gc.rb \ mruby/test/t/array.rb \ mruby/test/t/string.rb \ mruby/test/t/proc.rb \ mruby/test/t/basicobject.rb \ mruby/test/t/integer.rb \ mruby/test/t/nomethoderror.rb \ mruby/test/t/codegen.rb \ mruby/test/t/literals.rb \ mruby/test/t/nil.rb \ mruby/test/t/typeerror.rb \ mruby/test/t/symbol.rb \ mruby/test/t/object.rb \ mruby/test/t/vformat.rb \ mruby/test/t/numeric.rb \ mruby/test/t/bs_block.rb \ mruby/test/t/float.rb \ mruby/test/t/standarderror.rb \ mruby/test/t/superclass.rb \ mruby/test/assert.rb \ mruby/TODO.md \ mruby/Doxyfile \ mruby/mrblib/range.rb \ mruby/mrblib/kernel.rb \ mruby/mrblib/hash.rb \ mruby/mrblib/10error.rb \ mruby/mrblib/array.rb \ mruby/mrblib/string.rb \ mruby/mrblib/compar.rb \ mruby/mrblib/symbol.rb \ mruby/mrblib/enum.rb \ mruby/mrblib/numeric.rb \ mruby/build_config.rb \ mruby/LICENSE \ mruby/src/etc.c \ mruby/src/variable.c \ mruby/src/cdump.c \ mruby/src/init.c \ mruby/src/gc.c \ mruby/src/load.c \ mruby/src/codedump.c \ mruby/src/error.c \ mruby/src/readfloat.c \ mruby/src/state.c \ mruby/src/string.c \ mruby/src/numeric.c \ mruby/src/readnum.c \ mruby/src/enum.c \ mruby/src/print.c \ mruby/src/mempool.c \ mruby/src/readint.c \ mruby/src/numops.c \ mruby/src/hash.c \ mruby/src/version.c \ mruby/src/backtrace.c \ mruby/src/fmt_fp.c \ mruby/src/range.c \ mruby/src/vm.c \ mruby/src/debug.c \ mruby/src/symbol.c \ mruby/src/kernel.c \ mruby/src/object.c \ mruby/src/proc.c \ mruby/src/array.c \ mruby/src/dump.c \ mruby/src/class.c \ mruby/src/value_array.h \ mruby/src/allocf.c \ mruby/mruby-source.gemspec \ mruby/mrbgems/mruby-eval/mrbgem.rake \ mruby/mrbgems/mruby-eval/test/binding.rb \ mruby/mrbgems/mruby-eval/test/eval.rb \ mruby/mrbgems/mruby-eval/src/eval.c \ mruby/mrbgems/stdlib.gembox \ mruby/mrbgems/mruby-string-ext/mrbgem.rake \ mruby/mrbgems/mruby-string-ext/test/range.rb \ mruby/mrbgems/mruby-string-ext/test/string.rb \ mruby/mrbgems/mruby-string-ext/test/numeric.rb \ mruby/mrbgems/mruby-string-ext/mrblib/string.rb \ mruby/mrbgems/mruby-string-ext/src/string.c \ mruby/mrbgems/default-no-stdio.gembox \ mruby/mrbgems/mruby-set/mrbgem.rake \ mruby/mrbgems/mruby-set/README.md \ mruby/mrbgems/mruby-set/test/set.rb \ mruby/mrbgems/mruby-set/mrblib/set.rb \ mruby/mrbgems/mruby-set/LICENSE \ mruby/mrbgems/mruby-set/mruby-set.gem \ mruby/mrbgems/mruby-method/mrbgem.rake \ mruby/mrbgems/mruby-method/README.md \ mruby/mrbgems/mruby-method/test/method.rb \ mruby/mrbgems/mruby-method/mrblib/method.rb \ mruby/mrbgems/mruby-method/src/method.c \ mruby/mrbgems/mruby-random/mrbgem.rake \ mruby/mrbgems/mruby-random/test/random.rb \ mruby/mrbgems/mruby-random/src/random.c \ mruby/mrbgems/mruby-catch/mrbgem.rake \ mruby/mrbgems/mruby-catch/test/catch.rb \ mruby/mrbgems/mruby-catch/mrblib/catch.rb \ mruby/mrbgems/mruby-catch/src/catch.c \ mruby/mrbgems/mruby-cmath/mrbgem.rake \ mruby/mrbgems/mruby-cmath/test/cmath.rb \ mruby/mrbgems/mruby-cmath/src/cmath.c \ mruby/mrbgems/mruby-compar-ext/mrbgem.rake \ mruby/mrbgems/mruby-compar-ext/test/compar.rb \ mruby/mrbgems/mruby-compar-ext/mrblib/compar.rb \ mruby/mrbgems/mruby-pack/mrbgem.rake \ mruby/mrbgems/mruby-pack/README.md \ mruby/mrbgems/mruby-pack/test/pack.rb \ mruby/mrbgems/mruby-pack/src/pack.c \ mruby/mrbgems/mruby-struct/mrbgem.rake \ mruby/mrbgems/mruby-struct/test/struct.rb \ mruby/mrbgems/mruby-struct/mrblib/struct.rb \ mruby/mrbgems/mruby-struct/src/struct.c \ mruby/mrbgems/mruby-bigint/core/bigint.c \ mruby/mrbgems/mruby-bigint/core/bigint.h \ mruby/mrbgems/mruby-bigint/mrbgem.rake \ mruby/mrbgems/mruby-bigint/README.md \ mruby/mrbgems/mruby-bigint/test/bigint.rb \ mruby/mrbgems/mruby-bigint/README-fgmp.md \ mruby/mrbgems/mruby-symbol-ext/mrbgem.rake \ mruby/mrbgems/mruby-symbol-ext/test/symbol.rb \ mruby/mrbgems/mruby-symbol-ext/mrblib/symbol.rb \ mruby/mrbgems/mruby-symbol-ext/src/symbol.c \ mruby/mrbgems/mruby-io/mrbgem.rake \ mruby/mrbgems/mruby-io/README.md \ mruby/mrbgems/mruby-io/test/mruby_io_test.c \ mruby/mrbgems/mruby-io/test/io.rb \ mruby/mrbgems/mruby-io/test/file.rb \ mruby/mrbgems/mruby-io/test/file_test.rb \ mruby/mrbgems/mruby-io/mrblib/io.rb \ mruby/mrbgems/mruby-io/mrblib/file.rb \ mruby/mrbgems/mruby-io/mrblib/file_constants.rb \ mruby/mrbgems/mruby-io/mrblib/kernel.rb \ mruby/mrbgems/mruby-io/src/mruby_io_gem.c \ mruby/mrbgems/mruby-io/src/file_test.c \ mruby/mrbgems/mruby-io/src/io.c \ mruby/mrbgems/mruby-io/src/file.c \ mruby/mrbgems/mruby-io/include/mruby/ext/io.h \ mruby/mrbgems/mruby-compiler/core/parse.y \ mruby/mrbgems/mruby-compiler/core/y.tab.c \ mruby/mrbgems/mruby-compiler/core/node.h \ mruby/mrbgems/mruby-compiler/core/keywords \ mruby/mrbgems/mruby-compiler/core/codegen.c \ mruby/mrbgems/mruby-compiler/core/lex.def \ mruby/mrbgems/mruby-compiler/mrbgem.rake \ mruby/mrbgems/mruby-bin-config/mrbgem.rake \ mruby/mrbgems/mruby-bin-config/mruby-config \ mruby/mrbgems/mruby-bin-config/mruby-config.bat \ mruby/mrbgems/mruby-proc-ext/mrbgem.rake \ mruby/mrbgems/mruby-proc-ext/test/proc.rb \ mruby/mrbgems/mruby-proc-ext/test/proc.c \ mruby/mrbgems/mruby-proc-ext/mrblib/proc.rb \ mruby/mrbgems/mruby-proc-ext/src/proc.c \ mruby/mrbgems/mruby-data/mrbgem.rake \ mruby/mrbgems/mruby-data/test/data.rb \ mruby/mrbgems/mruby-data/src/data.c \ mruby/mrbgems/mruby-dir/mrbgem.rake \ mruby/mrbgems/mruby-dir/README.md \ mruby/mrbgems/mruby-dir/test/dir.rb \ mruby/mrbgems/mruby-dir/test/dirtest.c \ mruby/mrbgems/mruby-dir/mrblib/dir.rb \ mruby/mrbgems/mruby-dir/src/Win/dirent.c \ mruby/mrbgems/mruby-dir/src/dir.c \ mruby/mrbgems/mruby-object-ext/mrbgem.rake \ mruby/mrbgems/mruby-object-ext/test/object_ext.c \ mruby/mrbgems/mruby-object-ext/test/nil.rb \ mruby/mrbgems/mruby-object-ext/test/object.rb \ mruby/mrbgems/mruby-object-ext/mrblib/object.rb \ mruby/mrbgems/mruby-object-ext/src/object.c \ mruby/mrbgems/mruby-kernel-ext/mrbgem.rake \ mruby/mrbgems/mruby-kernel-ext/test/kernel.rb \ mruby/mrbgems/mruby-kernel-ext/src/kernel.c \ mruby/mrbgems/mruby-class-ext/mrbgem.rake \ mruby/mrbgems/mruby-class-ext/test/class.rb \ mruby/mrbgems/mruby-class-ext/test/module.rb \ mruby/mrbgems/mruby-class-ext/mrblib/module.rb \ mruby/mrbgems/mruby-class-ext/src/class.c \ mruby/mrbgems/mruby-binding/mrbgem.rake \ mruby/mrbgems/mruby-binding/test/binding.rb \ mruby/mrbgems/mruby-binding/test/binding.c \ mruby/mrbgems/mruby-binding/src/binding.c \ mruby/mrbgems/mruby-hash-ext/mrbgem.rake \ mruby/mrbgems/mruby-hash-ext/test/hash.rb \ mruby/mrbgems/mruby-hash-ext/mrblib/hash.rb \ mruby/mrbgems/mruby-hash-ext/src/hash_ext.c \ mruby/mrbgems/mruby-sleep/example/sleep.rb \ mruby/mrbgems/mruby-sleep/mrbgem.rake \ mruby/mrbgems/mruby-sleep/README.md \ mruby/mrbgems/mruby-sleep/test/sleep_test.rb \ mruby/mrbgems/mruby-sleep/src/sleep.c \ mruby/mrbgems/mruby-sprintf/mrbgem.rake \ mruby/mrbgems/mruby-sprintf/test/sprintf.rb \ mruby/mrbgems/mruby-sprintf/mrblib/string.rb \ mruby/mrbgems/mruby-sprintf/src/sprintf.c \ mruby/mrbgems/mruby-enum-chain/mrbgem.rake \ mruby/mrbgems/mruby-enum-chain/test/enum_chain.rb \ mruby/mrbgems/mruby-enum-chain/mrblib/chain.rb \ mruby/mrbgems/mruby-range-ext/mrbgem.rake \ mruby/mrbgems/mruby-range-ext/test/range.rb \ mruby/mrbgems/mruby-range-ext/mrblib/range.rb \ mruby/mrbgems/mruby-range-ext/src/range.c \ mruby/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c \ mruby/mrbgems/mruby-bin-mirb/mrbgem.rake \ mruby/mrbgems/mruby-bin-mirb/bintest/mirb.rb \ mruby/mrbgems/mruby-metaprog/mrbgem.rake \ mruby/mrbgems/mruby-metaprog/test/metaprog.rb \ mruby/mrbgems/mruby-metaprog/src/metaprog.c \ mruby/mrbgems/mruby-test-inline-struct/mrbgem.rake \ mruby/mrbgems/mruby-test-inline-struct/test/inline.c \ mruby/mrbgems/mruby-test-inline-struct/test/inline.rb \ mruby/mrbgems/mruby-time/mrbgem.rake \ mruby/mrbgems/mruby-time/test/time.rb \ mruby/mrbgems/mruby-time/src/time.c \ mruby/mrbgems/mruby-time/include/mruby/time.h \ mruby/mrbgems/mruby-proc-binding/mrbgem.rake \ mruby/mrbgems/mruby-proc-binding/test/proc_binding.c \ mruby/mrbgems/mruby-proc-binding/test/proc_binding.rb \ mruby/mrbgems/mruby-proc-binding/src/proc_binding.c \ mruby/mrbgems/stdlib-ext.gembox \ mruby/mrbgems/metaprog.gembox \ mruby/mrbgems/mruby-enumerator/mrbgem.rake \ mruby/mrbgems/mruby-enumerator/test/enumerator.rb \ mruby/mrbgems/mruby-enumerator/mrblib/enumerator.rb \ mruby/mrbgems/math.gembox \ mruby/mrbgems/mruby-socket/mrbgem.rake \ mruby/mrbgems/mruby-socket/README.md \ mruby/mrbgems/mruby-socket/test/socket.rb \ mruby/mrbgems/mruby-socket/test/udpsocket.rb \ mruby/mrbgems/mruby-socket/test/tcpsocket.rb \ mruby/mrbgems/mruby-socket/test/sockettest.c \ mruby/mrbgems/mruby-socket/test/basicsocket.rb \ mruby/mrbgems/mruby-socket/test/addrinfo.rb \ mruby/mrbgems/mruby-socket/test/unix.rb \ mruby/mrbgems/mruby-socket/test/ipsocket.rb \ mruby/mrbgems/mruby-socket/mrblib/socket.rb \ mruby/mrbgems/mruby-socket/src/socket.c \ mruby/mrbgems/mruby-socket/src/gen.rb \ mruby/mrbgems/mruby-socket/src/const.cstub \ mruby/mrbgems/mruby-socket/src/const.def \ mruby/mrbgems/mruby-objectspace/mrbgem.rake \ mruby/mrbgems/mruby-objectspace/test/objectspace.rb \ mruby/mrbgems/mruby-objectspace/src/mruby_objectspace.c \ mruby/mrbgems/full-core.gembox \ mruby/mrbgems/mruby-rational/mrbgem.rake \ mruby/mrbgems/mruby-rational/test/rational.rb \ mruby/mrbgems/mruby-rational/mrblib/rational.rb \ mruby/mrbgems/mruby-rational/src/rational.c \ mruby/mrbgems/mruby-numeric-ext/mrbgem.rake \ mruby/mrbgems/mruby-numeric-ext/test/numeric.rb \ mruby/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb \ mruby/mrbgems/mruby-numeric-ext/src/numeric_ext.c \ mruby/mrbgems/mruby-fiber/mrbgem.rake \ mruby/mrbgems/mruby-fiber/test/fiber2.rb \ mruby/mrbgems/mruby-fiber/test/fibertest.c \ mruby/mrbgems/mruby-fiber/test/fiber.rb \ mruby/mrbgems/mruby-fiber/src/fiber.c \ mruby/mrbgems/mruby-complex/mrbgem.rake \ mruby/mrbgems/mruby-complex/test/complex.rb \ mruby/mrbgems/mruby-complex/mrblib/complex.rb \ mruby/mrbgems/mruby-complex/src/complex.c \ mruby/mrbgems/mruby-exit/mrbgem.rake \ mruby/mrbgems/mruby-exit/src/mruby_exit.c \ mruby/mrbgems/mruby-error/mrbgem.rake \ mruby/mrbgems/mruby-error/test/exception.rb \ mruby/mrbgems/mruby-error/test/exception.c \ mruby/mrbgems/mruby-error/src/exception.c \ mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c \ mruby/mrbgems/mruby-bin-mruby/mrbgem.rake \ mruby/mrbgems/mruby-bin-mruby/bintest/mruby.rb \ mruby/mrbgems/mruby-test/mrbgem.rake \ mruby/mrbgems/mruby-test/vformat.c \ mruby/mrbgems/mruby-test/README.md \ mruby/mrbgems/mruby-test/driver.c \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apistring.h \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.h \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apistring.c \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.h \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c \ mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c \ mruby/mrbgems/mruby-bin-debugger/mrbgem.rake \ mruby/mrbgems/mruby-bin-debugger/bintest/mrdb.rb \ mruby/mrbgems/mruby-bin-debugger/bintest/print.rb \ mruby/mrbgems/mruby-array-ext/mrbgem.rake \ mruby/mrbgems/mruby-array-ext/test/array.rb \ mruby/mrbgems/mruby-array-ext/mrblib/array.rb \ mruby/mrbgems/mruby-array-ext/src/array.c \ mruby/mrbgems/mruby-enum-lazy/mrbgem.rake \ mruby/mrbgems/mruby-enum-lazy/test/lazy.rb \ mruby/mrbgems/mruby-enum-lazy/mrblib/lazy.rb \ mruby/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby_strip.c \ mruby/mrbgems/mruby-bin-strip/mrbgem.rake \ mruby/mrbgems/mruby-bin-strip/bintest/mruby_strip.rb \ mruby/mrbgems/mruby-errno/mrbgem.rake \ mruby/mrbgems/mruby-errno/README.md \ mruby/mrbgems/mruby-errno/test/errno.rb \ mruby/mrbgems/mruby-errno/mrblib/errno.rb \ mruby/mrbgems/mruby-errno/src/gen.rb \ mruby/mrbgems/mruby-errno/src/known_errors_def.cstub \ mruby/mrbgems/mruby-errno/src/known_errors.def \ mruby/mrbgems/mruby-errno/src/errno.c \ mruby/mrbgems/mruby-toplevel-ext/mrbgem.rake \ mruby/mrbgems/mruby-toplevel-ext/test/toplevel.rb \ mruby/mrbgems/mruby-toplevel-ext/mrblib/toplevel.rb \ mruby/mrbgems/mruby-math/mrbgem.rake \ mruby/mrbgems/mruby-math/test/math.rb \ mruby/mrbgems/mruby-math/src/math.c \ mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/stub.c \ mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c \ mruby/mrbgems/mruby-bin-mrbc/mrbgem.rake \ mruby/mrbgems/mruby-bin-mrbc/bintest/mrbc.rb \ mruby/mrbgems/mruby-encoding/mrbgem.rake \ mruby/mrbgems/mruby-encoding/test/string.rb \ mruby/mrbgems/mruby-encoding/test/numeric.rb \ mruby/mrbgems/mruby-encoding/src/encoding.c \ mruby/mrbgems/stdlib-io.gembox \ mruby/mrbgems/default.gembox \ mruby/mrbgems/mruby-os-memsize/mrbgem.rake \ mruby/mrbgems/mruby-os-memsize/test/memsize.rb \ mruby/mrbgems/mruby-os-memsize/src/memsize.c \ mruby/mrbgems/default-no-fpu.gembox \ mruby/mrbgems/mruby-enum-ext/mrbgem.rake \ mruby/mrbgems/mruby-enum-ext/test/enum.rb \ mruby/mrbgems/mruby-enum-ext/mrblib/enum.rb \ mruby/Rakefile \ mruby/SECURITY.md \ mruby/doc/mruby_logo_red_icon.png \ mruby/doc/mruby3.0.md \ mruby/doc/mruby3.1.md \ mruby/doc/internal/opcode.md \ mruby/doc/internal/boxing.md \ mruby/doc/limitations.md \ mruby/doc/mruby3.2.md \ mruby/doc/mruby3.4.md \ mruby/doc/mruby3.3.md \ mruby/doc/guides/compile.md \ mruby/doc/guides/memory.md \ mruby/doc/guides/mrbgems.md \ mruby/doc/guides/debugger.md \ mruby/doc/guides/hier.md \ mruby/doc/guides/link.md \ mruby/doc/guides/symbol.md \ mruby/doc/guides/gc-arena-howto.md \ mruby/doc/guides/mrbconf.md \ mruby/minirake \ mruby/include/mruby/opcode.h \ mruby/include/mruby/re.h \ mruby/include/mruby/hash.h \ mruby/include/mruby/string.h \ mruby/include/mruby/presym.h \ mruby/include/mruby/mempool.h \ mruby/include/mruby/object.h \ mruby/include/mruby/class.h \ mruby/include/mruby/ops.h \ mruby/include/mruby/irep.h \ mruby/include/mruby/compile.h \ mruby/include/mruby/array.h \ mruby/include/mruby/range.h \ mruby/include/mruby/throw.h \ mruby/include/mruby/variable.h \ mruby/include/mruby/boxing_word.h \ mruby/include/mruby/istruct.h \ mruby/include/mruby/data.h \ mruby/include/mruby/common.h \ mruby/include/mruby/error.h \ mruby/include/mruby/debug.h \ mruby/include/mruby/boxing_no.h \ mruby/include/mruby/numeric.h \ mruby/include/mruby/boxing_nan.h \ mruby/include/mruby/value.h \ mruby/include/mruby/endian.h \ mruby/include/mruby/internal.h \ mruby/include/mruby/dump.h \ mruby/include/mruby/version.h \ mruby/include/mruby/khash.h \ mruby/include/mruby/gc.h \ mruby/include/mruby/presym/enable.h \ mruby/include/mruby/presym/disable.h \ mruby/include/mruby/presym/scanning.h \ mruby/include/mruby/proc.h \ mruby/include/mrbconf.h \ mruby/include/mruby.h if ENABLE_THIRD_PARTY noinst_LTLIBRARIES = liburlparse.la liburlparse_la_SOURCES = \ urlparse/urlparse.c \ urlparse/urlparse.h noinst_LTLIBRARIES += libllhttp.la libllhttp_la_SOURCES = \ llhttp/src/api.c \ llhttp/src/http.c \ llhttp/src/llhttp.c \ llhttp/include/llhttp.h libllhttp_la_CPPFLAGS = -I${srcdir}/llhttp/include if HAVE_NEVERBLEED noinst_LTLIBRARIES += libneverbleed.la libneverbleed_la_CPPFLAGS = @OPENSSL_CFLAGS@ -D_GNU_SOURCE libneverbleed_la_LIBADD = @OPENSSL_LIBS@ libneverbleed_la_SOURCES = neverbleed/neverbleed.c neverbleed/neverbleed.h endif # HAVE_NEVERBLEED if HAVE_MRUBY .PHONY: all-local clean mruby mruby: mkdir -p "${abs_builddir}/mruby/build" diff "${srcdir}/build_config.rb" "${abs_builddir}/mruby/build/build_config.rb" >& /dev/null || \ cp "${srcdir}/build_config.rb" "${abs_builddir}/mruby/build" MRUBY_CONFIG="${abs_builddir}/mruby/build/build_config.rb" \ BUILD_DIR="${abs_builddir}/mruby/build" \ INSTALL_DIR="${abs_builddir}/mruby/build/install/bin" \ MRUBY_CC="${CC}" MRUBY_CXX="$(firstword $(CXX))" MRUBY_LD="${LD}" \ MRUBY_AR="${AR}" \ HOST="${host}" BUILD="${build}" \ "${srcdir}/mruby/minirake" -f "${srcdir}/mruby/Rakefile" all-local: mruby clean-local: [ ! -f "${abs_builddir}/mruby/build/build_config.rb" ] || \ MRUBY_CONFIG="${abs_builddir}/mruby/build/build_config.rb" \ BUILD_DIR="${abs_builddir}/mruby/build" \ MRUBY_CC="${CC}" \ "${srcdir}/mruby/minirake" -f "${srcdir}/mruby/Rakefile" clean endif # HAVE_MRUBY endif # ENABLE_THIRD_PARTY nghttp2-1.68.0/PaxHeaders/install-sh0000644000000000000000000000013215077107305014260 xustar0030 mtime=1761382085.335388159 30 atime=1761382104.857316361 30 ctime=1761382107.842304018 nghttp2-1.68.0/install-sh0000755000175100017510000003577615077107305014675 0ustar00runnerrunner#!/bin/sh # install - install a program, script, or datafile scriptversion=2020-11-14.01; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. tab=' ' nl=' ' IFS=" $tab$nl" # Set DOITPROG to "echo" to test this script. doit=${DOITPROG-} doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_mkdir= # Desired mode of installed file. mode=0755 # Create dirs (including intermediate dirs) using mode 755. # This is like GNU 'install' as of coreutils 8.32 (2020). mkdir_umask=22 backupsuffix= chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -p pass -p to $cpprog. -s $stripprog installed files. -S SUFFIX attempt to back up existing files, with suffix SUFFIX. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG By default, rm is invoked with -f; when overridden with RMPROG, it's up to you to specify -f if you want it. If -S is not specified, no backups are attempted. Email bug reports to bug-automake@gnu.org. Automake home page: https://www.gnu.org/software/automake/ " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -p) cpprog="$cpprog -p";; -s) stripcmd=$stripprog;; -S) backupsuffix="$2" shift;; -t) is_target_a_directory=always dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done # We allow the use of options -d and -T together, by making -d # take the precedence; this is for compatibility with GNU install. if test -n "$dir_arg"; then if test -n "$dst_arg"; then echo "$0: target directory not allowed when installing a directory." >&2 exit 1 fi fi if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then if test $# -gt 1 || test "$is_target_a_directory" = always; then if test ! -d "$dst_arg"; then echo "$0: $dst_arg: Is not a directory." >&2 exit 1 fi fi fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? # Don't chown directories that already exist. if test $dstdir_status = 0; then chowncmd="" fi else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dstbase=`basename "$src"` case $dst in */) dst=$dst$dstbase;; *) dst=$dst/$dstbase;; esac dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi case $dstdir in */) dstdirslash=$dstdir;; *) dstdirslash=$dstdir/;; esac obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false # The $RANDOM variable is not portable (e.g., dash). Use it # here however when possible just to lower collision chance. tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap ' ret=$? rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null exit $ret ' 0 # Because "mkdir -p" follows existing symlinks and we likely work # directly in world-writeable /tmp, make sure that the '$tmpdir' # directory is successfully created first before we actually test # 'mkdir -p'. if (umask $mkdir_umask && $mkdirprog $mkdir_mode "$tmpdir" && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. test_tmpdir="$tmpdir/a" ls_ld_tmpdir=`ls -ld "$test_tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null fi trap '' 0;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac oIFS=$IFS IFS=/ set -f set fnord $dstdir shift set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=${dstdirslash}_inst.$$_ rmtmp=${dstdirslash}_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && { test -z "$stripcmd" || { # Create $dsttmp read-write so that cp doesn't create it read-only, # which would cause strip to fail. if test -z "$doit"; then : >"$dsttmp" # No need to fork-exec 'touch'. else $doit touch "$dsttmp" fi } } && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # If $backupsuffix is set, and the file being installed # already exists, attempt a backup. Don't worry if it fails, # e.g., if mv doesn't support -f. if test -n "$backupsuffix" && test -f "$dst"; then $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null fi # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: nghttp2-1.68.0/PaxHeaders/CMakeLists.txt0000644000000000000000000000013215077107270015020 xustar0030 mtime=1761382072.960444291 30 atime=1761382104.869316308 30 ctime=1761382107.854303983 nghttp2-1.68.0/CMakeLists.txt0000644000175100017510000004701315077107270015415 0ustar00runnerrunner# nghttp2 - HTTP/2 C Library # # Copyright (c) 2012, 2013, 2014, 2015 Tatsuhiro Tsujikawa # Copyright (c) 2016 Peter Wu # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cmake_minimum_required(VERSION 3.14) # XXX using 1.8.90 instead of 1.9.0-DEV project(nghttp2 VERSION 1.68.0 LANGUAGES C) # See versioning rule: # https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html set(LT_CURRENT 43) set(LT_REVISION 2) set(LT_AGE 29) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) include(Version) math(EXPR LT_SOVERSION "${LT_CURRENT} - ${LT_AGE}") set(LT_VERSION "${LT_SOVERSION}.${LT_AGE}.${LT_REVISION}") set(PACKAGE_VERSION "${PROJECT_VERSION}") HexVersion(PACKAGE_VERSION_NUM ${PROJECT_VERSION_MAJOR} ${PROJECT_VERSION_MINOR} ${PROJECT_VERSION_PATCH}) if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the build type" FORCE) # Include "None" as option to disable any additional (optimization) flags, # relying on just CMAKE_C_FLAGS and CMAKE_CXX_FLAGS (which are empty by # default). These strings are presented in cmake-gui. set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "None" "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() include(GNUInstallDirs) include(CMakeDependentOption) # For documentation find_package(Python3 COMPONENTS Interpreter) # Auto-detection of features that can be toggled if(NOT ENABLE_LIB_ONLY) enable_language(CXX) find_package(Libev 4.11) find_package(Libcares 1.7.5) find_package(ZLIB 1.2.3) find_package(Libbrotlienc 1.0.9) find_package(Libbrotlidec 1.0.9) endif() if(WITH_WOLFSSL) find_package(WolfSSL 5.7.0) else() find_package(OpenSSL 1.1.1) endif() find_package(Libngtcp2 1.15.0) find_package(Libnghttp3 1.11.0) if(WITH_LIBBPF) find_package(Libbpf 0.7.0) if(NOT LIBBPF_FOUND) message(FATAL_ERROR "libbpf was requested (WITH_LIBBPF=1) but not found.") endif() endif() if((OPENSSL_FOUND OR WOLFSSL_FOUND) AND LIBEV_FOUND AND ZLIB_FOUND) set(ENABLE_APP_DEFAULT ON) else() set(ENABLE_APP_DEFAULT OFF) endif() find_package(Systemd 209) find_package(Jansson 2.5) set(ENABLE_HPACK_TOOLS_DEFAULT ${JANSSON_FOUND}) # 2.0.8 is required because we use evconnlistener_set_error_cb() find_package(Libevent 2.0.8 COMPONENTS core extra openssl) set(ENABLE_EXAMPLES_DEFAULT ${LIBEVENT_OPENSSL_FOUND}) find_package(LibXml2 2.6.26) set(WITH_LIBXML2_DEFAULT ${LIBXML2_FOUND}) find_package(Jemalloc) set(WITH_JEMALLOC_DEFAULT ${JEMALLOC_FOUND}) include(CMakeOptions.txt) if(ENABLE_LIB_ONLY AND (ENABLE_APP OR ENABLE_HPACK_TOOLS OR ENABLE_EXAMPLES)) # Remember when disabled options are disabled for later diagnostics. set(ENABLE_LIB_ONLY_DISABLED_OTHERS 1) else() set(ENABLE_LIB_ONLY_DISABLED_OTHERS 0) endif() if(ENABLE_LIB_ONLY) set(ENABLE_APP OFF) set(ENABLE_HPACK_TOOLS OFF) set(ENABLE_EXAMPLES OFF) endif() # Do not disable assertions based on CMAKE_BUILD_TYPE. foreach(_build_type "Release" "MinSizeRel" "RelWithDebInfo") foreach(_lang C CXX) string(TOUPPER "CMAKE_${_lang}_FLAGS_${_build_type}" _var) string(REGEX REPLACE "(^| )[/-]D *NDEBUG($| )" " " ${_var} "${${_var}}") endforeach() endforeach() if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") set(HINT_NORETURN "__attribute__((noreturn))") else() set(HINT_NORETURN) endif() if(NOT ENABLE_LIB_ONLY) include(ExtractValidFlags) foreach(_cxx1x_flag -std=c++20) extract_valid_cxx_flags(_cxx1x_flag_supported ${_cxx1x_flag}) if(_cxx1x_flag_supported) set(CXX1XCXXFLAGS ${_cxx1x_flag}) break() endif() endforeach() include(CMakePushCheckState) include(CheckCXXSourceCompiles) cmake_push_check_state() set(CMAKE_REQUIRED_DEFINITIONS "${CXX1XCXXFLAGS}") # Check that std::future is available. check_cxx_source_compiles(" #include #include int main() { std::vector> v; }" HAVE_STD_FUTURE) # Check that std::chrono::time_zone is available. check_cxx_source_compiles(" #include int main() { auto tz = std::chrono::current_zone(); (void)tz; }" HAVE_STD_CHRONO_TIME_ZONE) cmake_pop_check_state() endif() # Checks for libraries. # Additional libraries required for programs under src directory. set(APP_LIBRARIES) set(CMAKE_THREAD_PREFER_PTHREAD 1) find_package(Threads) if(CMAKE_USE_PTHREADS_INIT) list(APPEND APP_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) endif() # XXX android and C++, is this still needed in cmake? # case "$host" in # *android*) # android_build=yes # # android does not need -pthread, but needs following 3 libs for C++ # APPLDFLAGS="$APPLDFLAGS -lstdc++ -latomic -lsupc++" # dl: openssl requires libdl when it is statically linked. # XXX shouldn't ${CMAKE_DL_LIBS} be appended to OPENSSL_LIBRARIES instead of # APP_LIBRARIES if it is really specific to OpenSSL? enable_testing() add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}) # openssl (for src) include(CheckSymbolExists) set(HAVE_OPENSSL ${OPENSSL_FOUND}) if(NOT ENABLE_LIB_ONLY AND OPENSSL_FOUND) set(OPENSSL_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIR}) cmake_push_check_state() set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") set(CMAKE_REQUIRED_LIBRARIES "${OPENSSL_LIBRARIES}") if(WIN32) set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}" "ws2_32" "bcrypt") endif() check_symbol_exists("LIBRESSL_VERSION_NUMBER" "openssl/opensslv.h" LIBRESSL_FOUND) if(ENABLE_HTTP3) check_symbol_exists(SSL_provide_quic_data "openssl/ssl.h" HAVE_SSL_PROVIDE_QUIC_DATA) if(NOT HAVE_SSL_PROVIDE_QUIC_DATA) check_symbol_exists(SSL_set_quic_tls_cbs "openssl/ssl.h" HAVE_SSL_SET_QUIC_TLS_CBS) if(NOT HAVE_SSL_SET_QUIC_TLS_CBS) message(WARNING "OpenSSL in ${OPENSSL_LIBRARIES} has neither SSL_provide_quic_data nor SSL_set_quic_tls_cbs. HTTP/3 support cannot be enabled") endif() endif() endif() cmake_pop_check_state() else() set(OPENSSL_INCLUDE_DIRS "") set(OPENSSL_LIBRARIES "") endif() # wolfSSL (for src) set(HAVE_WOLFSSL ${WOLFSSL_FOUND}) if(WOLFSSL_FOUND) set(WOLFSSL_INCLUDE_DIRS ${WOLFSSL_INCLUDE_DIR}) cmake_push_check_state() set(CMAKE_REQUIRED_INCLUDES "${WOLFSSL_INCLUDE_DIR}") set(CMAKE_REQUIRED_LIBRARIES "${WOLFSSL_LIBRARIES}") check_symbol_exists(SSL_provide_quic_data "wolfssl/options.h;wolfssl/ssl.h" HAVE_WOLFSSL_SSL_PROVIDE_QUIC_DATA) if(NOT HAVE_WOLFSSL_SSL_PROVIDE_QUIC_DATA) message(WARNING "wolfSSL in ${WOLFSSL_LIBRARIES} does not have SSL_provide_quic_data. HTTP/3 support cannot be enabled") endif() cmake_pop_check_state() else() set(WOLFSSL_INCLUDE_DIRS "") set(WOLFSSL_LIBRARIES "") endif() # libev (for src) set(HAVE_LIBEV ${LIBEV_FOUND}) set(HAVE_ZLIB ${ZLIB_FOUND}) set(HAVE_SYSTEMD ${SYSTEMD_FOUND}) set(HAVE_LIBEVENT_OPENSSL ${LIBEVENT_FOUND}) if(LIBEVENT_FOUND) # Must both link the core and openssl libraries. set(LIBEVENT_OPENSSL_LIBRARIES ${LIBEVENT_LIBRARIES}) endif() # libc-ares (for src) set(HAVE_LIBCARES ${LIBCARES_FOUND}) if(LIBCARES_FOUND) set(LIBCARES_INCLUDE_DIRS ${LIBCARES_INCLUDE_DIR}) else() set(LIBCARES_INCLUDE_DIRS "") set(LIBCARES_LIBRARIES "") endif() # jansson (for src/nghttp, src/deflatehd and src/inflatehd) set(HAVE_JANSSON ${JANSSON_FOUND}) # libxml2 (for src/nghttp) set(HAVE_LIBXML2 ${LIBXML2_FOUND}) if(LIBXML2_FOUND) set(LIBXML2_INCLUDE_DIRS ${LIBXML2_INCLUDE_DIR}) else() set(LIBXML2_INCLUDE_DIRS "") set(LIBXML2_LIBRARIES "") endif() # jemalloc set(HAVE_JEMALLOC ${JEMALLOC_FOUND}) # libbrotli (for src) set(HAVE_LIBBROTLIENC ${LIBBROTLIENC_FOUND}) set(HAVE_LIBBROTLIDEC ${LIBBROTLIDEC_FOUND}) if(LIBBROTLIENC_FOUND AND LIBBROTLIDEC_FOUND) set(HAVE_LIBBROTLI 1) endif() # libbpf (for bpf) set(HAVE_LIBBPF ${LIBBPF_FOUND}) if(LIBBPF_FOUND) set(BPFCFLAGS -Wall -O2 -g) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") # For Debian/Ubuntu set(EXTRABPFCFLAGS -I/usr/include/${CMAKE_SYSTEM_PROCESSOR}-linux-gnu) endif() check_c_source_compiles(" #include int main() { enum bpf_stats_type foo; (void)foo; }" HAVE_BPF_STATS_TYPE) endif() # The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL and libev if(ENABLE_APP AND NOT (ZLIB_FOUND AND (OPENSSL_FOUND OR WOLFSSL_FOUND) AND LIBEV_FOUND)) message(FATAL_ERROR "Applications were requested (ENABLE_APP=1) but dependencies are not met.") endif() if(ENABLE_HTTP3) if(HAVE_SSL_PROVIDE_QUIC_DATA AND NOT LIBRESSL_FOUND) find_package(Libngtcp2_crypto_quictls 1.15.0) if(LIBNGTCP2_CRYPTO_QUICTLS_FOUND) set(HAVE_LIBNGTCP2_CRYPTO_QUICTLS 1) endif() elseif(HAVE_SSL_PROVIDE_QUIC_DATA AND LIBRESSL_FOUND) find_package(Libngtcp2_crypto_libressl 1.15.0) if(LIBNGTCP2_CRYPTO_LIBRESSL_FOUND) set(HAVE_LIBNGTCP2_CRYPTO_LIBRESSL 1) endif() elseif(HAVE_WOLFSSL_SSL_PROVIDE_QUIC_DATA) find_package(Libngtcp2_crypto_wolfssl 1.15.0) if(LIBNGTCP2_CRYPTO_WOLFSSL_FOUND) set(HAVE_LIBNGTCP2_CRYPTO_WOLFSSL 1) endif() elseif(HAVE_SSL_SET_QUIC_TLS_CBS) find_package(Libngtcp2_crypto_ossl 1.15.0) if(LIBNGTCP2_CRYPTO_OSSL_FOUND) set(HAVE_LIBNGTCP2_CRYPTO_OSSL 1) endif() endif() endif() # HTTP/3 requires libngtcp2 + nghttp3 + one of: # # - quictls/openssl + libngtcp2_crypto_quictls # - libressl + libngtcp2_crypto_libressl # - wolfSSL + libngtcp2_crypto_wolfssl # - openssl/openssl + libngtcp2_crypto_ossl if(ENABLE_HTTP3 AND NOT (LIBNGTCP2_FOUND AND LIBNGHTTP3_FOUND AND ((HAVE_SSL_PROVIDE_QUIC_DATA AND NOT LIBRESSL_FOUND AND LIBNGTCP2_CRYPTO_QUICTLS_FOUND) OR (HAVE_SSL_PROVIDE_QUIC_DATA AND LIBRESSL_FOUND AND LIBNGTCP2_CRYPTO_LIBRESSL_FOUND) OR (HAVE_WOLFSSL_SSL_PROVIDE_QUIC_DATA AND LIBNGTCP2_CRYPTO_WOLFSSL_FOUND) OR (HAVE_SSL_SET_QUIC_TLS_CBS AND LIBNGTCP2_CRYPTO_OSSL_FOUND)))) message(FATAL_ERROR "HTTP/3 was requested (ENABLE_HTTP3=1) but dependencies are not met.") endif() # HPACK tools requires jansson if(ENABLE_HPACK_TOOLS AND NOT HAVE_JANSSON) message(FATAL_ERROR "HPACK tools were requested (ENABLE_HPACK_TOOLS=1) but dependencies are not met.") endif() # examples if(ENABLE_EXAMPLES AND NOT (OPENSSL_FOUND AND LIBEVENT_OPENSSL_FOUND)) message(FATAL_ERROR "examples were requested (ENABLE_EXAMPLES=1) but dependencies are not met.") endif() # third-party http-parser only be built when needed if(ENABLE_EXAMPLES OR ENABLE_APP OR ENABLE_HPACK_TOOLS) set(ENABLE_THIRD_PARTY 1) # mruby (for src/nghttpx) set(HAVE_MRUBY ${WITH_MRUBY}) set(HAVE_NEVERBLEED ${WITH_NEVERBLEED}) else() set(HAVE_MRUBY 0) set(HAVE_NEVERBLEED 0) endif() # Checks for header files. include(CheckIncludeFile) check_include_file("arpa/inet.h" HAVE_ARPA_INET_H) check_include_file("fcntl.h" HAVE_FCNTL_H) check_include_file("inttypes.h" HAVE_INTTYPES_H) check_include_file("limits.h" HAVE_LIMITS_H) check_include_file("netdb.h" HAVE_NETDB_H) check_include_file("netinet/in.h" HAVE_NETINET_IN_H) check_include_file("netinet/ip.h" HAVE_NETINET_IP_H) check_include_file("pwd.h" HAVE_PWD_H) check_include_file("sys/socket.h" HAVE_SYS_SOCKET_H) check_include_file("sys/time.h" HAVE_SYS_TIME_H) check_include_file("syslog.h" HAVE_SYSLOG_H) check_include_file("unistd.h" HAVE_UNISTD_H) check_include_file("windows.h" HAVE_WINDOWS_H) include(CheckTypeSize) # Checks for typedefs, structures, and compiler characteristics. # AC_TYPE_SIZE_T check_type_size("ssize_t" SIZEOF_SSIZE_T) if(SIZEOF_SSIZE_T STREQUAL "") # ssize_t is a signed type in POSIX storing at least -1. # Set it to "int" to match the behavior of AC_TYPE_SSIZE_T (autotools). set(ssize_t int) endif() # AC_TYPE_UINT8_T # AC_TYPE_UINT16_T # AC_TYPE_UINT32_T # AC_TYPE_UINT64_T # AC_TYPE_INT8_T # AC_TYPE_INT16_T # AC_TYPE_INT32_T # AC_TYPE_INT64_T # AC_TYPE_OFF_T # AC_TYPE_PID_T # AC_TYPE_UID_T # XXX To support inline for crappy compilers, see https://cmake.org/Wiki/CMakeTestInline # AC_C_INLINE # XXX is AC_SYS_LARGEFILE still needed for modern systems? # add_definitions(-D_FILE_OFFSET_BITS=64) include(CheckStructHasMember) check_struct_has_member("struct tm" tm_gmtoff time.h HAVE_STRUCT_TM_TM_GMTOFF) # Checks for library functions. include(CheckFunctionExists) check_function_exists(_Exit HAVE__EXIT) check_function_exists(accept4 HAVE_ACCEPT4) check_function_exists(clock_gettime HAVE_CLOCK_GETTIME) check_function_exists(mkostemp HAVE_MKOSTEMP) check_function_exists(pipe2 HAVE_PIPE2) check_symbol_exists(GetTickCount64 "windows.h;sysinfoapi.h" HAVE_GETTICKCOUNT64) include(CheckSymbolExists) # XXX does this correctly detect initgroups (un)availability on cygwin? check_symbol_exists(initgroups grp.h HAVE_DECL_INITGROUPS) if(NOT HAVE_DECL_INITGROUPS AND HAVE_UNISTD_H) # FreeBSD declares initgroups() in unistd.h check_symbol_exists(initgroups unistd.h HAVE_DECL_INITGROUPS2) if(HAVE_DECL_INITGROUPS2) set(HAVE_DECL_INITGROUPS 1) endif() endif() check_symbol_exists(CLOCK_MONOTONIC "time.h" HAVE_DECL_CLOCK_MONOTONIC) set(WARNCFLAGS) set(WARNCXXFLAGS) if(CMAKE_C_COMPILER_ID MATCHES "MSVC") if(ENABLE_WERROR) set(WARNCFLAGS /WX) set(WARNCXXFLAGS /WX) endif() else() if(ENABLE_WERROR) set(WARNCFLAGS "-Werror") set(WARNCXXFLAGS "-Werror") endif() include(PickyWarningsC) include(PickyWarningsCXX) endif() if(ENABLE_STATIC_CRT) foreach(lang C CXX) foreach(suffix "" _DEBUG _MINSIZEREL _RELEASE _RELWITHDEBINFO) set(var "CMAKE_${lang}_FLAGS${suffix}") string(REPLACE "/MD" "/MT" ${var} "${${var}}") endforeach() endforeach() endif() if(ENABLE_DEBUG) set(DEBUGBUILD 1) endif() # Some platform does not have working std::future. We disable # threading for those platforms. if(NOT ENABLE_THREADS OR NOT HAVE_STD_FUTURE) set(NOTHREADS 1) endif() add_definitions(-DHAVE_CONFIG_H) configure_file(cmakeconfig.h.in config.h) # autotools-compatible names # Sphinx expects relative paths in the .rst files. Use the fact that the files # below are all one directory level deep. file(RELATIVE_PATH top_srcdir "${CMAKE_CURRENT_BINARY_DIR}/dir" "${CMAKE_CURRENT_SOURCE_DIR}") file(RELATIVE_PATH top_builddir "${CMAKE_CURRENT_BINARY_DIR}/dir" "${CMAKE_CURRENT_BINARY_DIR}") set(abs_top_srcdir "${CMAKE_CURRENT_SOURCE_DIR}") set(abs_top_builddir "${CMAKE_CURRENT_BINARY_DIR}") # libnghttp2.pc (pkg-config file) set(prefix "${CMAKE_INSTALL_PREFIX}") set(exec_prefix "${CMAKE_INSTALL_PREFIX}") set(libdir "${CMAKE_INSTALL_FULL_LIBDIR}") set(includedir "${CMAKE_INSTALL_FULL_INCLUDEDIR}") set(VERSION "${PACKAGE_VERSION}") # For init scripts and systemd service file (in contrib/) set(bindir "${CMAKE_INSTALL_FULL_BINDIR}") set(sbindir "${CMAKE_INSTALL_FULL_SBINDIR}") foreach(name lib/libnghttp2.pc lib/includes/nghttp2/nghttp2ver.h integration-tests/config.go integration-tests/setenv doc/conf.py doc/index.rst doc/package_README.rst doc/tutorial-client.rst doc/tutorial-server.rst doc/tutorial-hpack.rst doc/nghttpx-howto.rst doc/h2load-howto.rst doc/building-android-binary.rst doc/nghttp2.h.rst doc/nghttp2ver.h.rst doc/contribute.rst ) configure_file("${name}.in" "${name}" @ONLY) endforeach() if(APPLE) add_definitions(-D__APPLE_USE_RFC_3542) endif() include_directories( "${CMAKE_CURRENT_BINARY_DIR}" # for config.h ) # For use in src/CMakeLists.txt set(PKGDATADIR "${CMAKE_INSTALL_FULL_DATADIR}/${CMAKE_PROJECT_NAME}") set(PKGLIBDIR "${CMAKE_INSTALL_FULL_LIBDIR}/${CMAKE_PROJECT_NAME}") install(FILES README.rst DESTINATION "${CMAKE_INSTALL_DOCDIR}") add_subdirectory(lib) #add_subdirectory(lib/includes) add_subdirectory(third-party) add_subdirectory(src) add_subdirectory(examples) if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING) add_subdirectory(tests) #add_subdirectory(tests/testdata) add_subdirectory(integration-tests) endif() if(ENABLE_DOC) add_subdirectory(doc) endif() add_subdirectory(contrib) add_subdirectory(bpf) string(TOUPPER "${CMAKE_BUILD_TYPE}" _build_type) message(STATUS "summary of build options: Package version: ${VERSION} Library version: ${LT_CURRENT}:${LT_REVISION}:${LT_AGE} Install prefix: ${CMAKE_INSTALL_PREFIX} Target system: ${CMAKE_SYSTEM_NAME} Compiler: Build type: ${CMAKE_BUILD_TYPE} C compiler: ${CMAKE_C_COMPILER} CFLAGS: ${CMAKE_C_FLAGS_${_build_type}} ${CMAKE_C_FLAGS} C++ compiler: ${CMAKE_CXX_COMPILER} CXXFLAGS: ${CMAKE_CXX_FLAGS_${_build_type}} ${CMAKE_CXX_FLAGS} WARNCFLAGS: ${WARNCFLAGS} CXX1XCXXFLAGS: ${CXX1XCXXFLAGS} WARNCXXFLAGS: ${WARNCXXFLAGS} Python: Python: ${Python3_EXECUTABLE} Python3_VERSION: ${Python3_VERSION} Test: Failmalloc: ${ENABLE_FAILMALLOC} Build Test: ${BUILD_TESTING} Libs: OpenSSL: ${HAVE_OPENSSL} (LIBS='${OPENSSL_LIBRARIES}') wolfSSL: ${HAVE_WOLFSSL} (LIBS='${WOLFSSL_LIBRARIES}') Libxml2: ${HAVE_LIBXML2} (LIBS='${LIBXML2_LIBRARIES}') Libev: ${HAVE_LIBEV} (LIBS='${LIBEV_LIBRARIES}') Libc-ares: ${HAVE_LIBCARES} (LIBS='${LIBCARES_LIBRARIES}') Libngtcp2: ${HAVE_LIBNGTCP2} (LIBS='${LIBNGTCP2_LIBRARIES}') Libngtcp2_crypto_quictls: ${HAVE_LIBNGTCP2_CRYPTO_QUICTLS} (LIBS='${LIBNGTCP2_CRYPTO_QUICTLS_LIBRARIES}') Libngtcp2_crypto_libressl: ${HAVE_LIBNGTCP2_CRYPTO_LIBRESSL} (LIBS='${LIBNGTCP2_CRYPTO_LIBRESSL_LIBRARIES}') Libngtcp2_crypto_wolfssl: ${HAVE_LIBNGTCP2_CRYPTO_WOLFSSL} (LIBS='${LIBNGTCP2_CRYPTO_WOLFSSL_LIBRARIES}') Libnghttp3: ${HAVE_LIBNGHTTP3} (LIBS='${LIBNGHTTP3_LIBRARIES}') Libbpf: ${HAVE_LIBBPF} (LIBS='${LIBBPF_LIBRARIES}') Libevent(SSL): ${HAVE_LIBEVENT_OPENSSL} (LIBS='${LIBEVENT_OPENSSL_LIBRARIES}') Jansson: ${HAVE_JANSSON} (LIBS='${JANSSON_LIBRARIES}') Jemalloc: ${HAVE_JEMALLOC} (LIBS='${JEMALLOC_LIBRARIES}') Zlib: ${HAVE_ZLIB} (LIBS='${ZLIB_LIBRARIES}') Systemd: ${HAVE_SYSTEMD} (LIBS='${SYSTEMD_LIBRARIES}') Libbrotlienc: ${HAVE_LIBBROTLIENC} (LIBS='${LIBBROTLIENC_LIBRARIES}') Libbrotlidec: ${HAVE_LIBBROTLIDEC} (LIBS='${LIBBROTLIDEC_LIBRARIES}') Third-party: http-parser: ${ENABLE_THIRD_PARTY} MRuby: ${HAVE_MRUBY} Neverbleed: ${HAVE_NEVERBLEED} Features: Applications: ${ENABLE_APP} HPACK tools: ${ENABLE_HPACK_TOOLS} Examples: ${ENABLE_EXAMPLES} Threading: ${ENABLE_THREADS} HTTP/3(EXPERIMENTAL): ${ENABLE_HTTP3} ") if(ENABLE_LIB_ONLY_DISABLED_OTHERS) message("Only the library will be built. To build other components " "(such as applications and examples), set ENABLE_LIB_ONLY=OFF.") endif() nghttp2-1.68.0/PaxHeaders/Makefile.in0000644000000000000000000000013215077107305014324 xustar0030 mtime=1761382085.441387705 30 atime=1761382103.764321176 30 ctime=1761382107.827304061 nghttp2-1.68.0/Makefile.in0000644000175100017510000010137015077107305014716 0ustar00runnerrunner# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # nghttp2 - HTTP/2 C Library # Copyright (c) 2012 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ $(am__configure_deps) $(dist_doc_DATA) $(am__DIST_COMMON) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = lib/includes/nghttp2/nghttp2ver.h CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(docdir)" DATA = $(dist_doc_DATA) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ cscope distdir distdir-am dist dist-all distcheck am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) \ config.h.in # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \ $(top_srcdir)/lib/includes/nghttp2/nghttp2ver.h.in AUTHORS \ COPYING ChangeLog INSTALL NEWS README compile config.guess \ config.sub install-sh ltmain.sh missing DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ if test -d "$(distdir)"; then \ find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ && rm -rf "$(distdir)" \ || { sleep 5 && rm -rf "$(distdir)"; }; \ else :; fi am__post_remove_distdir = $(am__remove_distdir) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" DIST_ARCHIVES = $(distdir).tar.gz GZIP_ENV = --best DIST_TARGETS = dist-gzip # Exists only to be overridden by the user if desired. AM_DISTCHECK_DVI_TARGET = dvi distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPLDFLAGS = @APPLDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BPFCFLAGS = @BPFCFLAGS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CXX = @CXX@ CXX1XCXXFLAGS = @CXX1XCXXFLAGS@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXTRABPFCFLAGS = @EXTRABPFCFLAGS@ EXTRACFLAG = @EXTRACFLAG@ EXTRA_DEFS = @EXTRA_DEFS@ FGREP = @FGREP@ FILECMD = @FILECMD@ GREP = @GREP@ HAVE_CXX20 = @HAVE_CXX20@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ JANSSON_CFLAGS = @JANSSON_CFLAGS@ JANSSON_LIBS = @JANSSON_LIBS@ JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ JEMALLOC_LIBS = @JEMALLOC_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBBPF_CFLAGS = @LIBBPF_CFLAGS@ LIBBPF_LIBS = @LIBBPF_LIBS@ LIBBROTLIDEC_CFLAGS = @LIBBROTLIDEC_CFLAGS@ LIBBROTLIDEC_LIBS = @LIBBROTLIDEC_LIBS@ LIBBROTLIENC_CFLAGS = @LIBBROTLIENC_CFLAGS@ LIBBROTLIENC_LIBS = @LIBBROTLIENC_LIBS@ LIBCARES_CFLAGS = @LIBCARES_CFLAGS@ LIBCARES_LIBS = @LIBCARES_LIBS@ LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ LIBEV_CFLAGS = @LIBEV_CFLAGS@ LIBEV_LIBS = @LIBEV_LIBS@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@ LIBMRUBY_LIBS = @LIBMRUBY_LIBS@ LIBNGHTTP3_CFLAGS = @LIBNGHTTP3_CFLAGS@ LIBNGHTTP3_LIBS = @LIBNGHTTP3_LIBS@ LIBNGTCP2_CFLAGS = @LIBNGTCP2_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS = @LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_LIBS = @LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS = @LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS@ LIBNGTCP2_CRYPTO_LIBRESSL_LIBS = @LIBNGTCP2_CRYPTO_LIBRESSL_LIBS@ LIBNGTCP2_CRYPTO_OSSL_CFLAGS = @LIBNGTCP2_CRYPTO_OSSL_CFLAGS@ LIBNGTCP2_CRYPTO_OSSL_LIBS = @LIBNGTCP2_CRYPTO_OSSL_LIBS@ LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS = @LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ LIBNGTCP2_CRYPTO_QUICTLS_LIBS = @LIBNGTCP2_CRYPTO_QUICTLS_LIBS@ LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS = @LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS@ LIBNGTCP2_CRYPTO_WOLFSSL_LIBS = @LIBNGTCP2_CRYPTO_WOLFSSL_LIBS@ LIBNGTCP2_LIBS = @LIBNGTCP2_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBTOOL_LDFLAGS = @LIBTOOL_LDFLAGS@ LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ LIBXML2_LIBS = @LIBXML2_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_AGE = @LT_AGE@ LT_CURRENT = @LT_CURRENT@ LT_REVISION = @LT_REVISION@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ SYSTEMD_LIBS = @SYSTEMD_LIBS@ TESTLDADD = @TESTLDADD@ VERSION = @VERSION@ WARNCFLAGS = @WARNCFLAGS@ WARNCXXFLAGS = @WARNCXXFLAGS@ WOLFSSL_CFLAGS = @WOLFSSL_CFLAGS@ WOLFSSL_LIBS = @WOLFSSL_LIBS@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SUBDIRS = lib tests third-party src bpf examples integration-tests \ doc contrib ACLOCAL_AMFLAGS = -I m4 dist_doc_DATA = README.rst EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-env \ Dockerfile.android \ cmakeconfig.h.in \ CMakeLists.txt \ CMakeOptions.txt \ cmake/ExtractValidFlags.cmake \ cmake/FindJemalloc.cmake \ cmake/FindLibev.cmake \ cmake/Version.cmake \ cmake/FindLibevent.cmake \ cmake/FindJansson.cmake \ cmake/FindLibcares.cmake \ cmake/FindSystemd.cmake \ cmake/FindLibbpf.cmake \ cmake/FindLibnghttp3.cmake \ cmake/FindLibngtcp2.cmake \ cmake/FindLibngtcp2_crypto_quictls.cmake \ cmake/FindLibbrotlienc.cmake \ cmake/FindLibbrotlidec.cmake \ cmake/FindLibngtcp2_crypto_wolfssl.cmake \ cmake/FindLibngtcp2_crypto_ossl.cmake \ cmake/FindWolfSSL.cmake \ cmake/PickyWarningsC.cmake \ cmake/PickyWarningsCXX.cmake all: config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive .SUFFIXES: am--refresh: Makefile @: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ echo ' cd $(srcdir) && $(AUTOMAKE) --gnu'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --gnu \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(top_srcdir)/configure: $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): config.h: stamp-h1 @test -f $@ || rm -f stamp-h1 @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status @rm -f stamp-h1 cd $(top_builddir) && $(SHELL) ./config.status config.h $(srcdir)/config.h.in: $(am__configure_deps) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f stamp-h1 touch $@ distclean-hdr: -rm -f config.h stamp-h1 lib/includes/nghttp2/nghttp2ver.h: $(top_builddir)/config.status $(top_srcdir)/lib/includes/nghttp2/nghttp2ver.h.in cd $(top_builddir) && $(SHELL) ./config.status $@ mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs distclean-libtool: -rm -f libtool config.lt install-dist_docDATA: $(dist_doc_DATA) @$(NORMAL_INSTALL) @list='$(dist_doc_DATA)'; test -n "$(docdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(docdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(docdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(docdir)" || exit $$?; \ done uninstall-dist_docDATA: @$(NORMAL_UNINSTALL) @list='$(dist_doc_DATA)'; test -n "$(docdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(docdir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscope: cscope.files test ! -s cscope.files \ || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) clean-cscope: -rm -f cscope.files cscope.files: clean-cscope cscopelist cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done -test -n "$(am__skip_mode_fix)" \ || find "$(distdir)" -type d ! -perm -755 \ -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz $(am__post_remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 $(am__post_remove_distdir) dist-lzip: distdir tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz $(am__post_remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz $(am__post_remove_distdir) dist-zstd: distdir tardir=$(distdir) && $(am__tar) | zstd -c $${ZSTD_CLEVEL-$${ZSTD_OPT--19}} >$(distdir).tar.zst $(am__post_remove_distdir) dist-tarZ: distdir @echo WARNING: "Support for distribution archives compressed with" \ "legacy program 'compress' is deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__post_remove_distdir) dist-shar: distdir @echo WARNING: "Support for shar distribution archives is" \ "deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz $(am__post_remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) $(am__post_remove_distdir) dist dist-all: $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' $(am__post_remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lz*) \ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ *.tar.zst*) \ zstd -dc $(distdir).tar.zst | $(am__untar) ;;\ esac chmod -R a-w $(distdir) chmod u+w $(distdir) mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build/sub \ && ../../configure \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ --srcdir=../.. --prefix="$$dc_install_base" \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) $(AM_DISTCHECK_DVI_TARGET) \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) uninstall \ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ distuninstallcheck \ && chmod -R a-w "$$dc_install_base" \ && ({ \ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ } || { rm -rf "$$dc_destdir"; exit 1; }) \ && rm -rf "$$dc_destdir" \ && $(MAKE) $(AM_MAKEFLAGS) dist \ && rm -rf $(DIST_ARCHIVES) \ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 $(am__post_remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: @test -n '$(distuninstallcheck_dir)' || { \ echo 'ERROR: trying to run $@ with an empty' \ '$$(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ $(am__cd) '$(distuninstallcheck_dir)' || { \ echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left after uninstall:" ; \ if test -n "$(DESTDIR)"; then \ echo " (check DESTDIR support)"; \ fi ; \ $(distuninstallcheck_listfiles) ; \ exit 1; } >&2 distcleancheck: distclean @if test '$(srcdir)' = . ; then \ echo "ERROR: distcleancheck can only run from a VPATH build" ; \ exit 1 ; \ fi @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left in build directory after distclean:" ; \ $(distcleancheck_listfiles) ; \ exit 1; } >&2 check-am: all-am check: check-recursive all-am: Makefile $(DATA) config.h installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(docdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -f Makefile distclean-am: clean-am distclean-generic distclean-hdr \ distclean-libtool distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dist_docDATA install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-dist_docDATA .MAKE: $(am__recursive_targets) all install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ am--refresh check check-am clean clean-cscope clean-generic \ clean-libtool cscope cscopelist-am ctags ctags-am dist \ dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \ dist-xz dist-zip dist-zstd distcheck distclean \ distclean-generic distclean-hdr distclean-libtool \ distclean-tags distcleancheck distdir distuninstallcheck dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dist_docDATA install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ ps ps-am tags tags-am uninstall uninstall-am \ uninstall-dist_docDATA .PRECIOUS: Makefile .PHONY: clang-format # Format source files using clang-format. Don't format source files # under third-party directory since we are not responsible for their # coding style. clang-format: CLANGFORMAT=`git config --get clangformat.binary`; \ test -z $${CLANGFORMAT} && CLANGFORMAT="clang-format"; \ $${CLANGFORMAT} -i lib/*.{c,h} lib/includes/nghttp2/*.h \ src/*.{c,cc,h} examples/*.c \ tests/*.{c,h} bpf/*.c fuzz/*.cc # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nghttp2-1.68.0/PaxHeaders/tests0000644000000000000000000000013215077107334013346 xustar0030 mtime=1761382108.109303246 30 atime=1761382109.800298358 30 ctime=1761382108.109303246 nghttp2-1.68.0/tests/0000755000175100017510000000000015077107334014013 5ustar00runnerrunnernghttp2-1.68.0/tests/PaxHeaders/munit0000644000000000000000000000013215077107335014503 xustar0030 mtime=1761382109.276299872 30 atime=1761382109.800298358 30 ctime=1761382109.276299872 nghttp2-1.68.0/tests/munit/0000755000175100017510000000000015077107335015150 5ustar00runnerrunnernghttp2-1.68.0/tests/munit/PaxHeaders/munitxx.h0000644000000000000000000000013215077107276016451 xustar0030 mtime=1761382078.068420803 30 atime=1761382080.113411448 30 ctime=1761382109.276299872 nghttp2-1.68.0/tests/munit/munitxx.h0000644000175100017510000001203315077107276017040 0ustar00runnerrunner/* µnit Testing Framework * Copyright (c) 2013-2017 Evan Nemerson * Copyright (c) 2023 munit contributors * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef MUNITXX_H #define MUNITXX_H #include #include #include #if __cplusplus >= 201703L # include #endif // __cplusplus >= 201703L #define munit_assert_stdstring_equal(a, b) \ do { \ const std::string munit_tmp_a_ = (a); \ const std::string munit_tmp_b_ = (b); \ if (MUNIT_UNLIKELY(munit_tmp_a_ != munit_tmp_b_)) { \ munit_hexdump_diff(stderr, munit_tmp_a_.c_str(), munit_tmp_a_.size(), \ munit_tmp_b_.c_str(), munit_tmp_b_.size()); \ munit_errorf("assertion failed: string %s == %s (\"%s\" == \"%s\")", #a, \ #b, munit_tmp_a_.c_str(), munit_tmp_b_.c_str()); \ } \ MUNIT_PUSH_DISABLE_MSVC_C4127_ \ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ #if __cplusplus >= 201703L # define munit_assert_stdsv_equal(a, b) \ do { \ const std::string_view munit_tmp_a_ = (a); \ const std::string_view munit_tmp_b_ = (b); \ if (MUNIT_UNLIKELY(munit_tmp_a_ != munit_tmp_b_)) { \ munit_hexdump_diff(stderr, munit_tmp_a_.data(), munit_tmp_a_.size(), \ munit_tmp_b_.data(), munit_tmp_b_.size()); \ munit_errorf( \ "assertion failed: string %s == %s (\"%.*s\" == \"%.*s\")", #a, #b, \ (int)munit_tmp_a_.size(), munit_tmp_a_.data(), \ (int)munit_tmp_b_.size(), munit_tmp_b_.data()); \ } \ MUNIT_PUSH_DISABLE_MSVC_C4127_ \ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ #endif // __cplusplus >= 201703L #define munit_assert_enum_class(a, op, b) \ do { \ auto munit_tmp_a_ = (a); \ auto munit_tmp_b_ = (b); \ if (!(munit_tmp_a_ op munit_tmp_b_)) { \ auto munit_tmp_a_str_ = std::to_string( \ static_cast>( \ munit_tmp_a_)); \ auto munit_tmp_b_str_ = std::to_string( \ static_cast>( \ munit_tmp_b_)); \ munit_errorf("assertion failed: %s %s %s (%s %s %s)", #a, #op, #b, \ munit_tmp_a_str_.c_str(), #op, munit_tmp_b_str_.c_str()); \ } \ MUNIT_PUSH_DISABLE_MSVC_C4127_ \ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ #if defined(MUNIT_ENABLE_ASSERT_ALIASES) # define assert_stdstring_equal(a, b) munit_assert_stdstring_equal(a, b) # if __cplusplus >= 201703L # define assert_stdsv_equal(a, b) munit_assert_stdsv_equal(a, b) # endif // __cplusplus >= 201703L # define assert_enum_class(a, op, b) munit_assert_enum_class(a, op, b) #endif /* defined(MUNIT_ENABLE_ASSERT_ALIASES) */ #endif // MUNITXX_H nghttp2-1.68.0/tests/munit/PaxHeaders/COPYING0000644000000000000000000000013215077107276015617 xustar0030 mtime=1761382078.067420808 30 atime=1761382080.113411448 30 ctime=1761382108.106303254 nghttp2-1.68.0/tests/munit/COPYING0000644000175100017510000000212215077107276016204 0ustar00runnerrunnerµnit Testing Framework Copyright (c) 2013-2016 Evan Nemerson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. nghttp2-1.68.0/tests/munit/PaxHeaders/munit.h0000644000000000000000000000013215077107276016071 xustar0030 mtime=1761382078.068420803 30 atime=1761382080.113411448 30 ctime=1761382108.068303364 nghttp2-1.68.0/tests/munit/munit.h0000644000175100017510000006157315077107276016475 0ustar00runnerrunner/* µnit Testing Framework * Copyright (c) 2013-2017 Evan Nemerson * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef MUNIT_H #define MUNIT_H #include #include #include #include #define MUNIT_VERSION(major, minor, revision) \ (((major) << 16) | ((minor) << 8) | (revision)) #define MUNIT_CURRENT_VERSION MUNIT_VERSION(0, 4, 1) #if defined(_MSC_VER) && (_MSC_VER < 1600) # define munit_int8_t __int8 # define munit_uint8_t unsigned __int8 # define munit_int16_t __int16 # define munit_uint16_t unsigned __int16 # define munit_int32_t __int32 # define munit_uint32_t unsigned __int32 # define munit_int64_t __int64 # define munit_uint64_t unsigned __int64 #else # include # define munit_int8_t int8_t # define munit_uint8_t uint8_t # define munit_int16_t int16_t # define munit_uint16_t uint16_t # define munit_int32_t int32_t # define munit_uint32_t uint32_t # define munit_int64_t int64_t # define munit_uint64_t uint64_t #endif #if defined(_MSC_VER) && (_MSC_VER < 1800) # if !defined(PRIi8) # define PRIi8 "i" # endif # if !defined(PRIi16) # define PRIi16 "i" # endif # if !defined(PRIi32) # define PRIi32 "i" # endif # if !defined(PRIi64) # define PRIi64 "I64i" # endif # if !defined(PRId8) # define PRId8 "d" # endif # if !defined(PRId16) # define PRId16 "d" # endif # if !defined(PRId32) # define PRId32 "d" # endif # if !defined(PRId64) # define PRId64 "I64d" # endif # if !defined(PRIx8) # define PRIx8 "x" # endif # if !defined(PRIx16) # define PRIx16 "x" # endif # if !defined(PRIx32) # define PRIx32 "x" # endif # if !defined(PRIx64) # define PRIx64 "I64x" # endif # if !defined(PRIu8) # define PRIu8 "u" # endif # if !defined(PRIu16) # define PRIu16 "u" # endif # if !defined(PRIu32) # define PRIu32 "u" # endif # if !defined(PRIu64) # define PRIu64 "I64u" # endif #else # include #endif #if !defined(munit_bool) # if defined(bool) # define munit_bool bool # elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) # define munit_bool _Bool # else # define munit_bool int # endif #endif #if defined(__cplusplus) extern "C" { #endif #if defined(__GNUC__) # define MUNIT_LIKELY(expr) (__builtin_expect((expr), 1)) # define MUNIT_UNLIKELY(expr) (__builtin_expect((expr), 0)) # define MUNIT_UNUSED __attribute__((__unused__)) #else # define MUNIT_LIKELY(expr) (expr) # define MUNIT_UNLIKELY(expr) (expr) # define MUNIT_UNUSED #endif #if !defined(_WIN32) # define MUNIT_SIZE_MODIFIER "z" # define MUNIT_CHAR_MODIFIER "hh" # define MUNIT_SHORT_MODIFIER "h" #else # if defined(_M_X64) || defined(__amd64__) # define MUNIT_SIZE_MODIFIER "I64" # else # define MUNIT_SIZE_MODIFIER "" # endif # define MUNIT_CHAR_MODIFIER "" # define MUNIT_SHORT_MODIFIER "" #endif #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L # define MUNIT_NO_RETURN _Noreturn #elif defined(__GNUC__) # define MUNIT_NO_RETURN __attribute__((__noreturn__)) #elif defined(_MSC_VER) # define MUNIT_NO_RETURN __declspec(noreturn) #else # define MUNIT_NO_RETURN #endif #if defined(_MSC_VER) && (_MSC_VER >= 1500) # define MUNIT_PUSH_DISABLE_MSVC_C4127_ \ __pragma(warning(push)) __pragma(warning(disable : 4127)) # define MUNIT_POP_DISABLE_MSVC_C4127_ __pragma(warning(pop)) #else # define MUNIT_PUSH_DISABLE_MSVC_C4127_ # define MUNIT_POP_DISABLE_MSVC_C4127_ #endif typedef enum { MUNIT_LOG_DEBUG, MUNIT_LOG_INFO, MUNIT_LOG_WARNING, MUNIT_LOG_ERROR } MunitLogLevel; #if defined(__GNUC__) && !defined(__MINGW32__) # define MUNIT_PRINTF(string_index, first_to_check) \ __attribute__((format(printf, string_index, first_to_check))) #else # define MUNIT_PRINTF(string_index, first_to_check) #endif MUNIT_PRINTF(4, 5) void munit_logf_ex(MunitLogLevel level, const char *filename, int line, const char *format, ...); #define munit_logf(level, format, ...) \ munit_logf_ex(level, __FILE__, __LINE__, format, __VA_ARGS__) #define munit_log(level, msg) munit_logf(level, "%s", msg) MUNIT_NO_RETURN MUNIT_PRINTF(3, 4) void munit_errorf_ex(const char *filename, int line, const char *format, ...); #define munit_errorf(format, ...) \ munit_errorf_ex(__FILE__, __LINE__, format, __VA_ARGS__) #define munit_error(msg) munit_errorf("%s", msg) #define munit_assert(expr) \ do { \ if (!MUNIT_LIKELY(expr)) { \ munit_error("assertion failed: " #expr); \ } \ MUNIT_PUSH_DISABLE_MSVC_C4127_ \ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ #define munit_assert_true(expr) \ do { \ if (!MUNIT_LIKELY(expr)) { \ munit_error("assertion failed: " #expr " is not true"); \ } \ MUNIT_PUSH_DISABLE_MSVC_C4127_ \ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ #define munit_assert_false(expr) \ do { \ if (!MUNIT_LIKELY(!(expr))) { \ munit_error("assertion failed: " #expr " is not false"); \ } \ MUNIT_PUSH_DISABLE_MSVC_C4127_ \ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ #define munit_assert_type_full(prefix, suffix, T, fmt, a, op, b) \ do { \ T munit_tmp_a_ = (a); \ T munit_tmp_b_ = (b); \ if (!(munit_tmp_a_ op munit_tmp_b_)) { \ munit_errorf("assertion failed: %s %s %s (" prefix "%" fmt suffix \ " %s " prefix "%" fmt suffix ")", \ #a, #op, #b, munit_tmp_a_, #op, munit_tmp_b_); \ } \ MUNIT_PUSH_DISABLE_MSVC_C4127_ \ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ #define munit_assert_type(T, fmt, a, op, b) \ munit_assert_type_full("", "", T, fmt, a, op, b) #define munit_assert_char(a, op, b) \ munit_assert_type_full("'\\x", "'", char, "02" MUNIT_CHAR_MODIFIER "x", a, \ op, b) #define munit_assert_uchar(a, op, b) \ munit_assert_type_full("'\\x", "'", unsigned char, \ "02" MUNIT_CHAR_MODIFIER "x", a, op, b) #define munit_assert_short(a, op, b) \ munit_assert_type(short, MUNIT_SHORT_MODIFIER "d", a, op, b) #define munit_assert_ushort(a, op, b) \ munit_assert_type(unsigned short, MUNIT_SHORT_MODIFIER "u", a, op, b) #define munit_assert_int(a, op, b) munit_assert_type(int, "d", a, op, b) #define munit_assert_uint(a, op, b) \ munit_assert_type(unsigned int, "u", a, op, b) #define munit_assert_long(a, op, b) munit_assert_type(long int, "ld", a, op, b) #define munit_assert_ulong(a, op, b) \ munit_assert_type(unsigned long int, "lu", a, op, b) #define munit_assert_llong(a, op, b) \ munit_assert_type(long long int, "lld", a, op, b) #define munit_assert_ullong(a, op, b) \ munit_assert_type(unsigned long long int, "llu", a, op, b) #define munit_assert_size(a, op, b) \ munit_assert_type(size_t, MUNIT_SIZE_MODIFIER "u", a, op, b) #define munit_assert_ssize(a, op, b) \ munit_assert_type(ssize_t, MUNIT_SIZE_MODIFIER "d", a, op, b) #define munit_assert_float(a, op, b) munit_assert_type(float, "f", a, op, b) #define munit_assert_double(a, op, b) munit_assert_type(double, "g", a, op, b) #define munit_assert_ptr(a, op, b) \ munit_assert_type(const void *, "p", a, op, b) #define munit_assert_int8(a, op, b) \ munit_assert_type(munit_int8_t, PRIi8, a, op, b) #define munit_assert_uint8(a, op, b) \ munit_assert_type(munit_uint8_t, PRIu8, a, op, b) #define munit_assert_int16(a, op, b) \ munit_assert_type(munit_int16_t, PRIi16, a, op, b) #define munit_assert_uint16(a, op, b) \ munit_assert_type(munit_uint16_t, PRIu16, a, op, b) #define munit_assert_int32(a, op, b) \ munit_assert_type(munit_int32_t, PRIi32, a, op, b) #define munit_assert_uint32(a, op, b) \ munit_assert_type(munit_uint32_t, PRIu32, a, op, b) #define munit_assert_int64(a, op, b) \ munit_assert_type(munit_int64_t, PRIi64, a, op, b) #define munit_assert_uint64(a, op, b) \ munit_assert_type(munit_uint64_t, PRIu64, a, op, b) #define munit_assert_ptrdiff(a, op, b) \ munit_assert_type(ptrdiff_t, "td", a, op, b) #define munit_assert_enum(T, a, op, b) munit_assert_type(T, "d", a, op, b) #define munit_assert_double_equal(a, b, precision) \ do { \ const double munit_tmp_a_ = (a); \ const double munit_tmp_b_ = (b); \ const double munit_tmp_diff_ = ((munit_tmp_a_ - munit_tmp_b_) < 0) \ ? -(munit_tmp_a_ - munit_tmp_b_) \ : (munit_tmp_a_ - munit_tmp_b_); \ if (MUNIT_UNLIKELY(munit_tmp_diff_ > 1e-##precision)) { \ munit_errorf("assertion failed: %s == %s (%0." #precision \ "g == %0." #precision "g)", \ #a, #b, munit_tmp_a_, munit_tmp_b_); \ } \ MUNIT_PUSH_DISABLE_MSVC_C4127_ \ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ #include #define munit_assert_string_equal(a, b) \ do { \ const char *munit_tmp_a_ = (a); \ const char *munit_tmp_b_ = (b); \ if (MUNIT_UNLIKELY(strcmp(munit_tmp_a_, munit_tmp_b_) != 0)) { \ munit_hexdump_diff(stderr, munit_tmp_a_, strlen(munit_tmp_a_), \ munit_tmp_b_, strlen(munit_tmp_b_)); \ munit_errorf("assertion failed: string %s == %s (\"%s\" == \"%s\")", #a, \ #b, munit_tmp_a_, munit_tmp_b_); \ } \ MUNIT_PUSH_DISABLE_MSVC_C4127_ \ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ #define munit_assert_string_not_equal(a, b) \ do { \ const char *munit_tmp_a_ = (a); \ const char *munit_tmp_b_ = (b); \ if (MUNIT_UNLIKELY(strcmp(munit_tmp_a_, munit_tmp_b_) == 0)) { \ munit_errorf("assertion failed: string %s != %s (\"%s\" == \"%s\")", #a, \ #b, munit_tmp_a_, munit_tmp_b_); \ } \ MUNIT_PUSH_DISABLE_MSVC_C4127_ \ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ #define munit_assert_memory_equal(size, a, b) \ do { \ const unsigned char *munit_tmp_a_ = (const unsigned char *)(a); \ const unsigned char *munit_tmp_b_ = (const unsigned char *)(b); \ const size_t munit_tmp_size_ = (size); \ if (MUNIT_UNLIKELY(memcmp(munit_tmp_a_, munit_tmp_b_, munit_tmp_size_)) != \ 0) { \ size_t munit_tmp_pos_; \ for (munit_tmp_pos_ = 0; munit_tmp_pos_ < munit_tmp_size_; \ munit_tmp_pos_++) { \ if (munit_tmp_a_[munit_tmp_pos_] != munit_tmp_b_[munit_tmp_pos_]) { \ munit_hexdump_diff(stderr, munit_tmp_a_, size, munit_tmp_b_, size); \ munit_errorf("assertion failed: memory %s == %s, at offset " \ "%" MUNIT_SIZE_MODIFIER "u", \ #a, #b, munit_tmp_pos_); \ break; \ } \ } \ } \ MUNIT_PUSH_DISABLE_MSVC_C4127_ \ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ #define munit_assert_memn_equal(a, a_size, b, b_size) \ do { \ const unsigned char *munit_tmp_a_ = (const unsigned char *)(a); \ const unsigned char *munit_tmp_b_ = (const unsigned char *)(b); \ const size_t munit_tmp_a_size_ = (a_size); \ const size_t munit_tmp_b_size_ = (b_size); \ if (MUNIT_UNLIKELY(munit_tmp_a_size_ != munit_tmp_b_size_) || \ MUNIT_UNLIKELY(munit_tmp_a_size_ && memcmp(munit_tmp_a_, munit_tmp_b_, \ munit_tmp_a_size_)) != 0) { \ munit_hexdump_diff(stderr, munit_tmp_a_, munit_tmp_a_size_, \ munit_tmp_b_, munit_tmp_b_size_); \ munit_errorf("assertion failed: memory %s == %s", #a, #b); \ } \ MUNIT_PUSH_DISABLE_MSVC_C4127_ \ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ #define munit_assert_memory_not_equal(size, a, b) \ do { \ const unsigned char *munit_tmp_a_ = (const unsigned char *)(a); \ const unsigned char *munit_tmp_b_ = (const unsigned char *)(b); \ const size_t munit_tmp_size_ = (size); \ if (MUNIT_UNLIKELY(memcmp(munit_tmp_a_, munit_tmp_b_, munit_tmp_size_)) == \ 0) { \ munit_errorf("assertion failed: memory %s != %s (%zu bytes)", #a, #b, \ munit_tmp_size_); \ } \ MUNIT_PUSH_DISABLE_MSVC_C4127_ \ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ #define munit_assert_ptr_equal(a, b) munit_assert_ptr(a, ==, b) #define munit_assert_ptr_not_equal(a, b) munit_assert_ptr(a, !=, b) #define munit_assert_null(ptr) munit_assert_ptr(ptr, ==, NULL) #define munit_assert_not_null(ptr) munit_assert_ptr(ptr, !=, NULL) #define munit_assert_ptr_null(ptr) munit_assert_ptr(ptr, ==, NULL) #define munit_assert_ptr_not_null(ptr) munit_assert_ptr(ptr, !=, NULL) /*** Memory allocation ***/ void *munit_malloc_ex(const char *filename, int line, size_t size); #define munit_malloc(size) munit_malloc_ex(__FILE__, __LINE__, (size)) #define munit_new(type) ((type *)munit_malloc(sizeof(type))) #define munit_calloc(nmemb, size) munit_malloc((nmemb) * (size)) #define munit_newa(type, nmemb) ((type *)munit_calloc((nmemb), sizeof(type))) /*** Random number generation ***/ void munit_rand_seed(munit_uint32_t seed); munit_uint32_t munit_rand_uint32(void); int munit_rand_int_range(int min, int max); double munit_rand_double(void); void munit_rand_memory(size_t size, munit_uint8_t *buffer); /*** Tests and Suites ***/ typedef enum { /* Test successful */ MUNIT_OK, /* Test failed */ MUNIT_FAIL, /* Test was skipped */ MUNIT_SKIP, /* Test failed due to circumstances not intended to be tested * (things like network errors, invalid parameter value, failure to * allocate memory in the test harness, etc.). */ MUNIT_ERROR } MunitResult; typedef struct { char *name; char **values; } MunitParameterEnum; typedef struct { char *name; char *value; } MunitParameter; const char *munit_parameters_get(const MunitParameter params[], const char *key); typedef enum { MUNIT_TEST_OPTION_NONE = 0, MUNIT_TEST_OPTION_SINGLE_ITERATION = 1 << 0, MUNIT_TEST_OPTION_TODO = 1 << 1 } MunitTestOptions; typedef MunitResult (*MunitTestFunc)(const MunitParameter params[], void *user_data_or_fixture); typedef void *(*MunitTestSetup)(const MunitParameter params[], void *user_data); typedef void (*MunitTestTearDown)(void *fixture); typedef struct { const char *name; MunitTestFunc test; MunitTestSetup setup; MunitTestTearDown tear_down; MunitTestOptions options; MunitParameterEnum *parameters; } MunitTest; typedef enum { MUNIT_SUITE_OPTION_NONE = 0 } MunitSuiteOptions; typedef struct MunitSuite_ MunitSuite; struct MunitSuite_ { const char *prefix; const MunitTest *tests; const MunitSuite *suites; unsigned int iterations; MunitSuiteOptions options; }; int munit_suite_main(const MunitSuite *suite, void *user_data, int argc, char *const *argv); /* Note: I'm not very happy with this API; it's likely to change if I * figure out something better. Suggestions welcome. */ typedef struct MunitArgument_ MunitArgument; struct MunitArgument_ { char *name; munit_bool (*parse_argument)(const MunitSuite *suite, void *user_data, int *arg, int argc, char *const *argv); void (*write_help)(const MunitArgument *argument, void *user_data); }; int munit_suite_main_custom(const MunitSuite *suite, void *user_data, int argc, char *const *argv, const MunitArgument arguments[]); #if defined(MUNIT_ENABLE_ASSERT_ALIASES) # define assert_true(expr) munit_assert_true(expr) # define assert_false(expr) munit_assert_false(expr) # define assert_char(a, op, b) munit_assert_char(a, op, b) # define assert_uchar(a, op, b) munit_assert_uchar(a, op, b) # define assert_short(a, op, b) munit_assert_short(a, op, b) # define assert_ushort(a, op, b) munit_assert_ushort(a, op, b) # define assert_int(a, op, b) munit_assert_int(a, op, b) # define assert_uint(a, op, b) munit_assert_uint(a, op, b) # define assert_long(a, op, b) munit_assert_long(a, op, b) # define assert_ulong(a, op, b) munit_assert_ulong(a, op, b) # define assert_llong(a, op, b) munit_assert_llong(a, op, b) # define assert_ullong(a, op, b) munit_assert_ullong(a, op, b) # define assert_size(a, op, b) munit_assert_size(a, op, b) # define assert_ssize(a, op, b) munit_assert_ssize(a, op, b) # define assert_float(a, op, b) munit_assert_float(a, op, b) # define assert_double(a, op, b) munit_assert_double(a, op, b) # define assert_ptr(a, op, b) munit_assert_ptr(a, op, b) # define assert_int8(a, op, b) munit_assert_int8(a, op, b) # define assert_uint8(a, op, b) munit_assert_uint8(a, op, b) # define assert_int16(a, op, b) munit_assert_int16(a, op, b) # define assert_uint16(a, op, b) munit_assert_uint16(a, op, b) # define assert_int32(a, op, b) munit_assert_int32(a, op, b) # define assert_uint32(a, op, b) munit_assert_uint32(a, op, b) # define assert_int64(a, op, b) munit_assert_int64(a, op, b) # define assert_uint64(a, op, b) munit_assert_uint64(a, op, b) # define assert_ptrdiff(a, op, b) munit_assert_ptrdiff(a, op, b) # define assert_enum(T, a, op, b) munit_assert_enum(T, a, op, b) # define assert_double_equal(a, b, precision) \ munit_assert_double_equal(a, b, precision) # define assert_string_equal(a, b) munit_assert_string_equal(a, b) # define assert_string_not_equal(a, b) munit_assert_string_not_equal(a, b) # define assert_memory_equal(size, a, b) munit_assert_memory_equal(size, a, b) # define assert_memn_equal(a, a_size, b, b_size) \ munit_assert_memn_equal(a, a_size, b, b_size) # define assert_memory_not_equal(size, a, b) \ munit_assert_memory_not_equal(size, a, b) # define assert_ptr_equal(a, b) munit_assert_ptr_equal(a, b) # define assert_ptr_not_equal(a, b) munit_assert_ptr_not_equal(a, b) # define assert_ptr_null(ptr) munit_assert_null_equal(ptr) # define assert_ptr_not_null(ptr) munit_assert_not_null(ptr) # define assert_null(ptr) munit_assert_null(ptr) # define assert_not_null(ptr) munit_assert_not_null(ptr) #endif /* defined(MUNIT_ENABLE_ASSERT_ALIASES) */ #define munit_void_test_decl(func) \ void func(void); \ \ static inline MunitResult wrap_##func(const MunitParameter params[], \ void *fixture) { \ (void)params; \ (void)fixture; \ \ func(); \ return MUNIT_OK; \ } #define munit_void_test(func) \ {"/" #func, wrap_##func, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL} #define munit_test_end() {NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL} int munit_hexdump(FILE *fp, const void *data, size_t datalen); int munit_hexdump_diff(FILE *fp, const void *a, size_t alen, const void *b, size_t blen); #if defined(__cplusplus) } #endif #endif /* !defined(MUNIT_H) */ #if defined(MUNIT_ENABLE_ASSERT_ALIASES) #if defined(assert) # undef assert #endif #define assert(expr) munit_assert(expr) #endif nghttp2-1.68.0/tests/munit/PaxHeaders/munit.c0000644000000000000000000000013215077107276016064 xustar0030 mtime=1761382078.068420803 30 atime=1761382080.113411448 30 ctime=1761382108.067303367 nghttp2-1.68.0/tests/munit/munit.c0000644000175100017510000023222015077107276016455 0ustar00runnerrunner/* Copyright (c) 2013-2018 Evan Nemerson * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /*** Configuration ***/ /* This is just where the output from the test goes. It's really just * meant to let you choose stdout or stderr, but if anyone really want * to direct it to a file let me know, it would be fairly easy to * support. */ #if !defined(MUNIT_OUTPUT_FILE) # define MUNIT_OUTPUT_FILE stdout #endif /* This is a bit more useful; it tells µnit how to format the seconds in * timed tests. If your tests run for longer you might want to reduce * it, and if your computer is really fast and your tests are tiny you * can increase it. */ #if !defined(MUNIT_TEST_TIME_FORMAT) # define MUNIT_TEST_TIME_FORMAT "0.8f" #endif /* If you have long test names you might want to consider bumping * this. The result information takes 43 characters. */ #if !defined(MUNIT_TEST_NAME_LEN) # define MUNIT_TEST_NAME_LEN 37 #endif /* If you don't like the timing information, you can disable it by * defining MUNIT_DISABLE_TIMING. */ #if !defined(MUNIT_DISABLE_TIMING) # define MUNIT_ENABLE_TIMING #endif /*** End configuration ***/ #if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE < 200809L) # undef _POSIX_C_SOURCE #endif #if !defined(_POSIX_C_SOURCE) # define _POSIX_C_SOURCE 200809L #endif /* Solaris freaks out if you try to use a POSIX or SUS standard without * the "right" C standard. */ #if defined(_XOPEN_SOURCE) # undef _XOPEN_SOURCE #endif #if defined(__STDC_VERSION__) # if __STDC_VERSION__ >= 201112L # define _XOPEN_SOURCE 700 # elif __STDC_VERSION__ >= 199901L # define _XOPEN_SOURCE 600 # endif #endif /* Because, according to Microsoft, POSIX is deprecated. You've got * to appreciate the chutzpah. */ #if defined(_MSC_VER) && !defined(_CRT_NONSTDC_NO_DEPRECATE) # define _CRT_NONSTDC_NO_DEPRECATE #endif #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) # include #elif defined(_WIN32) /* https://msdn.microsoft.com/en-us/library/tf4dy80a.aspx */ #endif #include #include #include #include #include #include #include #include #if !defined(MUNIT_NO_NL_LANGINFO) && !defined(_WIN32) # define MUNIT_NL_LANGINFO # include # include # include #endif #if !defined(_WIN32) # include # include # include #else # include # include # include # if !defined(STDERR_FILENO) # define STDERR_FILENO _fileno(stderr) # endif #endif #include "munit.h" #define MUNIT_STRINGIFY(x) #x #define MUNIT_XSTRINGIFY(x) MUNIT_STRINGIFY(x) #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || \ defined(__IBMCPP__) # define MUNIT_THREAD_LOCAL __thread #elif (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L)) || \ defined(_Thread_local) # define MUNIT_THREAD_LOCAL _Thread_local #elif defined(_WIN32) # define MUNIT_THREAD_LOCAL __declspec(thread) #endif /* MSVC 12.0 will emit a warning at /W4 for code like 'do { ... } * while (0)', or 'do { ... } while (1)'. I'm pretty sure nobody * at Microsoft compiles with /W4. */ #if defined(_MSC_VER) && (_MSC_VER <= 1800) # pragma warning(disable : 4127) #endif #if defined(_WIN32) || defined(__EMSCRIPTEN__) # define MUNIT_NO_FORK #endif #if defined(__EMSCRIPTEN__) # define MUNIT_NO_BUFFER #endif /*** Logging ***/ static MunitLogLevel munit_log_level_visible = MUNIT_LOG_INFO; static MunitLogLevel munit_log_level_fatal = MUNIT_LOG_ERROR; #if defined(MUNIT_THREAD_LOCAL) static MUNIT_THREAD_LOCAL munit_bool munit_error_jmp_buf_valid = 0; static MUNIT_THREAD_LOCAL jmp_buf munit_error_jmp_buf; #endif /* At certain warning levels, mingw will trigger warnings about * suggesting the format attribute, which we've explicity *not* set * because it will then choke on our attempts to use the MS-specific * I64 modifier for size_t (which we have to use since MSVC doesn't * support the C99 z modifier). */ #if defined(__MINGW32__) || defined(__MINGW64__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wsuggest-attribute=format" #endif MUNIT_PRINTF(5, 0) static void munit_logf_exv(MunitLogLevel level, FILE *fp, const char *filename, int line, const char *format, va_list ap) { if (level < munit_log_level_visible) return; switch (level) { case MUNIT_LOG_DEBUG: fputs("Debug", fp); break; case MUNIT_LOG_INFO: fputs("Info", fp); break; case MUNIT_LOG_WARNING: fputs("Warning", fp); break; case MUNIT_LOG_ERROR: fputs("Error", fp); break; default: munit_logf_ex(MUNIT_LOG_ERROR, filename, line, "Invalid log level (%d)", level); return; } fputs(": ", fp); if (filename != NULL) fprintf(fp, "%s:%d: ", filename, line); vfprintf(fp, format, ap); fputc('\n', fp); } MUNIT_PRINTF(3, 4) static void munit_logf_internal(MunitLogLevel level, FILE *fp, const char *format, ...) { va_list ap; va_start(ap, format); munit_logf_exv(level, fp, NULL, 0, format, ap); va_end(ap); } static void munit_log_internal(MunitLogLevel level, FILE *fp, const char *message) { munit_logf_internal(level, fp, "%s", message); } void munit_logf_ex(MunitLogLevel level, const char *filename, int line, const char *format, ...) { va_list ap; va_start(ap, format); munit_logf_exv(level, stderr, filename, line, format, ap); va_end(ap); if (level >= munit_log_level_fatal) { #if defined(MUNIT_THREAD_LOCAL) if (munit_error_jmp_buf_valid) longjmp(munit_error_jmp_buf, 1); #endif abort(); } } void munit_errorf_ex(const char *filename, int line, const char *format, ...) { va_list ap; va_start(ap, format); munit_logf_exv(MUNIT_LOG_ERROR, stderr, filename, line, format, ap); va_end(ap); #if defined(MUNIT_THREAD_LOCAL) if (munit_error_jmp_buf_valid) longjmp(munit_error_jmp_buf, 1); #endif abort(); } #if defined(__MINGW32__) || defined(__MINGW64__) # pragma GCC diagnostic pop #endif #if !defined(MUNIT_STRERROR_LEN) # define MUNIT_STRERROR_LEN 80 #endif static void munit_log_errno(MunitLogLevel level, FILE *fp, const char *msg) { #if defined(MUNIT_NO_STRERROR_R) || \ (defined(__MINGW32__) && !defined(MINGW_HAS_SECURE_API)) munit_logf_internal(level, fp, "%s: %s (%d)", msg, strerror(errno), errno); #else char munit_error_str[MUNIT_STRERROR_LEN]; munit_error_str[0] = '\0'; # if !defined(_WIN32) strerror_r(errno, munit_error_str, MUNIT_STRERROR_LEN); # else strerror_s(munit_error_str, MUNIT_STRERROR_LEN, errno); # endif munit_logf_internal(level, fp, "%s: %s (%d)", msg, munit_error_str, errno); #endif } /*** Memory allocation ***/ void *munit_malloc_ex(const char *filename, int line, size_t size) { void *ptr; if (size == 0) return NULL; ptr = calloc(1, size); if (MUNIT_UNLIKELY(ptr == NULL)) { munit_logf_ex(MUNIT_LOG_ERROR, filename, line, "Failed to allocate %" MUNIT_SIZE_MODIFIER "u bytes.", size); } return ptr; } /*** Timer code ***/ #if defined(MUNIT_ENABLE_TIMING) # define psnip_uint64_t munit_uint64_t # define psnip_uint32_t munit_uint32_t /* Code copied from portable-snippets * . If you need to * change something, please do it there so we can keep the code in * sync. */ /* Clocks (v1) * Portable Snippets - https://gitub.com/nemequ/portable-snippets * Created by Evan Nemerson * * To the extent possible under law, the authors have waived all * copyright and related or neighboring rights to this code. For * details, see the Creative Commons Zero 1.0 Universal license at * https://creativecommons.org/publicdomain/zero/1.0/ */ # if !defined(PSNIP_CLOCK_H) # define PSNIP_CLOCK_H # if !defined(psnip_uint64_t) # include "../exact-int/exact-int.h" # endif # if !defined(PSNIP_CLOCK_STATIC_INLINE) # if defined(__GNUC__) # define PSNIP_CLOCK__COMPILER_ATTRIBUTES __attribute__((__unused__)) # else # define PSNIP_CLOCK__COMPILER_ATTRIBUTES # endif # define PSNIP_CLOCK__FUNCTION PSNIP_CLOCK__COMPILER_ATTRIBUTES static # endif enum PsnipClockType { /* This clock provides the current time, in units since 1970-01-01 * 00:00:00 UTC not including leap seconds. In other words, UNIX * time. Keep in mind that this clock doesn't account for leap * seconds, and can go backwards (think NTP adjustments). */ PSNIP_CLOCK_TYPE_WALL = 1, /* The CPU time is a clock which increases only when the current * process is active (i.e., it doesn't increment while blocking on * I/O). */ PSNIP_CLOCK_TYPE_CPU = 2, /* Monotonic time is always running (unlike CPU time), but it only ever moves forward unless you reboot the system. Things like NTP adjustments have no effect on this clock. */ PSNIP_CLOCK_TYPE_MONOTONIC = 3 }; struct PsnipClockTimespec { psnip_uint64_t seconds; psnip_uint64_t nanoseconds; }; /* Methods we support: */ # define PSNIP_CLOCK_METHOD_CLOCK_GETTIME 1 # define PSNIP_CLOCK_METHOD_TIME 2 # define PSNIP_CLOCK_METHOD_GETTIMEOFDAY 3 # define PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER 4 # define PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME 5 # define PSNIP_CLOCK_METHOD_CLOCK 6 # define PSNIP_CLOCK_METHOD_GETPROCESSTIMES 7 # define PSNIP_CLOCK_METHOD_GETRUSAGE 8 # define PSNIP_CLOCK_METHOD_GETSYSTEMTIMEPRECISEASFILETIME 9 # define PSNIP_CLOCK_METHOD_GETTICKCOUNT64 10 # include # if defined(HEDLEY_UNREACHABLE) # define PSNIP_CLOCK_UNREACHABLE() HEDLEY_UNREACHABLE() # else # define PSNIP_CLOCK_UNREACHABLE() assert(0) # endif /* Choose an implementation */ /* #undef PSNIP_CLOCK_WALL_METHOD */ /* #undef PSNIP_CLOCK_CPU_METHOD */ /* #undef PSNIP_CLOCK_MONOTONIC_METHOD */ /* We want to be able to detect the libc implementation, so we include ( isn't available everywhere). */ # if defined(__unix__) || defined(__unix) || defined(__linux__) # include # include # endif # if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) /* These are known to work without librt. If you know of others * please let us know so we can add them. */ # if (defined(__GLIBC__) && \ (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17))) || \ (defined(__FreeBSD__)) # define PSNIP_CLOCK_HAVE_CLOCK_GETTIME # elif !defined(PSNIP_CLOCK_NO_LIBRT) # define PSNIP_CLOCK_HAVE_CLOCK_GETTIME # endif # endif # if defined(_WIN32) # if !defined(PSNIP_CLOCK_CPU_METHOD) # define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_GETPROCESSTIMES # endif # if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) # define PSNIP_CLOCK_MONOTONIC_METHOD \ PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER # endif # endif # if defined(__MACH__) && !defined(__gnu_hurd__) # if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) # define PSNIP_CLOCK_MONOTONIC_METHOD \ PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME # endif # endif # if defined(PSNIP_CLOCK_HAVE_CLOCK_GETTIME) # include # if !defined(PSNIP_CLOCK_WALL_METHOD) # if defined(CLOCK_REALTIME_PRECISE) # define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME # define PSNIP_CLOCK_CLOCK_GETTIME_WALL CLOCK_REALTIME_PRECISE # elif !defined(__sun) # define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME # define PSNIP_CLOCK_CLOCK_GETTIME_WALL CLOCK_REALTIME # endif # endif # if !defined(PSNIP_CLOCK_CPU_METHOD) # if defined(_POSIX_CPUTIME) || defined(CLOCK_PROCESS_CPUTIME_ID) # define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME # define PSNIP_CLOCK_CLOCK_GETTIME_CPU CLOCK_PROCESS_CPUTIME_ID # elif defined(CLOCK_VIRTUAL) # define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME # define PSNIP_CLOCK_CLOCK_GETTIME_CPU CLOCK_VIRTUAL # endif # endif # if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) # if defined(CLOCK_MONOTONIC_RAW) # define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME # define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC # elif defined(CLOCK_MONOTONIC_PRECISE) # define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME # define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC_PRECISE # elif defined(_POSIX_MONOTONIC_CLOCK) || defined(CLOCK_MONOTONIC) # define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME # define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC # endif # endif # endif # if defined(_POSIX_VERSION) && (_POSIX_VERSION >= 200112L) # if !defined(PSNIP_CLOCK_WALL_METHOD) # define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_GETTIMEOFDAY # endif # endif # if !defined(PSNIP_CLOCK_WALL_METHOD) # define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_TIME # endif # if !defined(PSNIP_CLOCK_CPU_METHOD) # define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK # endif /* Primarily here for testing. */ # if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ defined(PSNIP_CLOCK_REQUIRE_MONOTONIC) # error No monotonic clock found. # endif /* Implementations */ # if (defined(PSNIP_CLOCK_CPU_METHOD) && \ (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ (defined(PSNIP_CLOCK_WALL_METHOD) && \ (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ (defined(PSNIP_CLOCK_CPU_METHOD) && \ (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \ (defined(PSNIP_CLOCK_WALL_METHOD) && \ (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \ (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \ (defined(PSNIP_CLOCK_CPU_METHOD) && \ (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_TIME)) || \ (defined(PSNIP_CLOCK_WALL_METHOD) && \ (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME)) || \ (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_TIME)) # include # endif # if (defined(PSNIP_CLOCK_CPU_METHOD) && \ (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) || \ (defined(PSNIP_CLOCK_WALL_METHOD) && \ (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) || \ (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) # include # endif # if (defined(PSNIP_CLOCK_CPU_METHOD) && \ (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \ (defined(PSNIP_CLOCK_WALL_METHOD) && \ (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \ (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ (PSNIP_CLOCK_MONOTONIC_METHOD == \ PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \ (defined(PSNIP_CLOCK_CPU_METHOD) && \ (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) || \ (defined(PSNIP_CLOCK_WALL_METHOD) && \ (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) || \ (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) # include # endif # if (defined(PSNIP_CLOCK_CPU_METHOD) && \ (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) || \ (defined(PSNIP_CLOCK_WALL_METHOD) && \ (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) || \ (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) # include # include # endif # if (defined(PSNIP_CLOCK_CPU_METHOD) && \ (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) || \ (defined(PSNIP_CLOCK_WALL_METHOD) && \ (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) || \ (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ (PSNIP_CLOCK_MONOTONIC_METHOD == \ PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) # include # include # include # endif /*** Implementations ***/ # define PSNIP_CLOCK_NSEC_PER_SEC ((psnip_uint32_t)(1000000000ULL)) # if (defined(PSNIP_CLOCK_CPU_METHOD) && \ (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ (defined(PSNIP_CLOCK_WALL_METHOD) && \ (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) PSNIP_CLOCK__FUNCTION psnip_uint32_t psnip_clock__clock_getres(clockid_t clk_id) { struct timespec res; int r; r = clock_getres(clk_id, &res); if (r != 0) return 0; return (psnip_uint32_t)(PSNIP_CLOCK_NSEC_PER_SEC / (psnip_uint64_t)res.tv_nsec); } PSNIP_CLOCK__FUNCTION int psnip_clock__clock_gettime(clockid_t clk_id, struct PsnipClockTimespec *res) { struct timespec ts; if (clock_gettime(clk_id, &ts) != 0) return -10; res->seconds = (psnip_uint64_t)(ts.tv_sec); res->nanoseconds = (psnip_uint64_t)(ts.tv_nsec); return 0; } # endif PSNIP_CLOCK__FUNCTION psnip_uint32_t psnip_clock_wall_get_precision(void) { # if !defined(PSNIP_CLOCK_WALL_METHOD) return 0; # elif defined(PSNIP_CLOCK_WALL_METHOD) && \ PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_WALL); # elif defined(PSNIP_CLOCK_WALL_METHOD) && \ PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY return 1000000; # elif defined(PSNIP_CLOCK_WALL_METHOD) && \ PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME return 1; # else return 0; # endif } PSNIP_CLOCK__FUNCTION int psnip_clock_wall_get_time(struct PsnipClockTimespec *res) { # if !defined(PSNIP_CLOCK_WALL_METHOD) (void)res; return -2; # elif defined(PSNIP_CLOCK_WALL_METHOD) && \ PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_WALL, res); # elif defined(PSNIP_CLOCK_WALL_METHOD) && \ PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME res->seconds = time(NULL); res->nanoseconds = 0; # elif defined(PSNIP_CLOCK_WALL_METHOD) && \ PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY struct timeval tv; if (gettimeofday(&tv, NULL) != 0) return -6; res->seconds = (psnip_uint64_t)tv.tv_sec; res->nanoseconds = (psnip_uint64_t)tv.tv_usec * 1000; # else (void)res; return -2; # endif return 0; } PSNIP_CLOCK__FUNCTION psnip_uint32_t psnip_clock_cpu_get_precision(void) { # if !defined(PSNIP_CLOCK_CPU_METHOD) return 0; # elif defined(PSNIP_CLOCK_CPU_METHOD) && \ PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_CPU); # elif defined(PSNIP_CLOCK_CPU_METHOD) && \ PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK return CLOCKS_PER_SEC; # elif defined(PSNIP_CLOCK_CPU_METHOD) && \ PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES return PSNIP_CLOCK_NSEC_PER_SEC / 100; # else return 0; # endif } PSNIP_CLOCK__FUNCTION int psnip_clock_cpu_get_time(struct PsnipClockTimespec *res) { # if !defined(PSNIP_CLOCK_CPU_METHOD) (void)res; return -2; # elif defined(PSNIP_CLOCK_CPU_METHOD) && \ PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_CPU, res); # elif defined(PSNIP_CLOCK_CPU_METHOD) && \ PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK clock_t t = clock(); if (t == ((clock_t)-1)) return -5; res->seconds = t / CLOCKS_PER_SEC; res->nanoseconds = (t % CLOCKS_PER_SEC) * (PSNIP_CLOCK_NSEC_PER_SEC / CLOCKS_PER_SEC); # elif defined(PSNIP_CLOCK_CPU_METHOD) && \ PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES FILETIME CreationTime, ExitTime, KernelTime, UserTime; LARGE_INTEGER date, adjust; if (!GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime)) return -7; /* http://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/ */ date.HighPart = (LONG)UserTime.dwHighDateTime; date.LowPart = UserTime.dwLowDateTime; adjust.QuadPart = 11644473600000 * 10000; date.QuadPart -= adjust.QuadPart; res->seconds = (psnip_uint64_t)(date.QuadPart / 10000000); res->nanoseconds = (psnip_uint64_t)(date.QuadPart % 10000000) * (PSNIP_CLOCK_NSEC_PER_SEC / 100); # elif PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE struct rusage usage; if (getrusage(RUSAGE_SELF, &usage) != 0) return -8; res->seconds = usage.ru_utime.tv_sec; res->nanoseconds = tv.tv_usec * 1000; # else (void)res; return -2; # endif return 0; } PSNIP_CLOCK__FUNCTION psnip_uint32_t psnip_clock_monotonic_get_precision(void) { # if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) return 0; # elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC); # elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME static mach_timebase_info_data_t tbi = { 0, }; if (tbi.denom == 0) mach_timebase_info(&tbi); return (psnip_uint32_t)(tbi.numer / tbi.denom); # elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64 return 1000; # elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ PSNIP_CLOCK_MONOTONIC_METHOD == \ PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER LARGE_INTEGER Frequency; QueryPerformanceFrequency(&Frequency); return (psnip_uint32_t)((Frequency.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC) ? PSNIP_CLOCK_NSEC_PER_SEC : Frequency.QuadPart); # else return 0; # endif } PSNIP_CLOCK__FUNCTION int psnip_clock_monotonic_get_time(struct PsnipClockTimespec *res) { # if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) (void)res; return -2; # elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC, res); # elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME psnip_uint64_t nsec = mach_absolute_time(); static mach_timebase_info_data_t tbi = { 0, }; if (tbi.denom == 0) mach_timebase_info(&tbi); nsec *= ((psnip_uint64_t)tbi.numer) / ((psnip_uint64_t)tbi.denom); res->seconds = nsec / PSNIP_CLOCK_NSEC_PER_SEC; res->nanoseconds = nsec % PSNIP_CLOCK_NSEC_PER_SEC; # elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ PSNIP_CLOCK_MONOTONIC_METHOD == \ PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER LARGE_INTEGER t, f; if (QueryPerformanceCounter(&t) == 0) return -12; QueryPerformanceFrequency(&f); res->seconds = (psnip_uint64_t)(t.QuadPart / f.QuadPart); res->nanoseconds = (psnip_uint64_t)(t.QuadPart % f.QuadPart); if (f.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC) res->nanoseconds /= (psnip_uint64_t)f.QuadPart / PSNIP_CLOCK_NSEC_PER_SEC; else res->nanoseconds *= PSNIP_CLOCK_NSEC_PER_SEC / (psnip_uint64_t)f.QuadPart; # elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64 const ULONGLONG msec = GetTickCount64(); res->seconds = msec / 1000; res->nanoseconds = sec % 1000; # else return -2; # endif return 0; } /* Returns the number of ticks per second for the specified clock. * For example, a clock with millisecond precision would return 1000, * and a clock with 1 second (such as the time() function) would * return 1. * * If the requested clock isn't available, it will return 0. * Hopefully this will be rare, but if it happens to you please let us * know so we can work on finding a way to support your system. * * Note that different clocks on the same system often have a * different precisions. */ PSNIP_CLOCK__FUNCTION psnip_uint32_t psnip_clock_get_precision(enum PsnipClockType clock_type) { switch (clock_type) { case PSNIP_CLOCK_TYPE_MONOTONIC: return psnip_clock_monotonic_get_precision(); case PSNIP_CLOCK_TYPE_CPU: return psnip_clock_cpu_get_precision(); case PSNIP_CLOCK_TYPE_WALL: return psnip_clock_wall_get_precision(); } PSNIP_CLOCK_UNREACHABLE(); return 0; } /* Set the provided timespec to the requested time. Returns 0 on * success, or a negative value on failure. */ PSNIP_CLOCK__FUNCTION int psnip_clock_get_time(enum PsnipClockType clock_type, struct PsnipClockTimespec *res) { assert(res != NULL); switch (clock_type) { case PSNIP_CLOCK_TYPE_MONOTONIC: return psnip_clock_monotonic_get_time(res); case PSNIP_CLOCK_TYPE_CPU: return psnip_clock_cpu_get_time(res); case PSNIP_CLOCK_TYPE_WALL: return psnip_clock_wall_get_time(res); } return -1; } # endif /* !defined(PSNIP_CLOCK_H) */ static psnip_uint64_t munit_clock_get_elapsed(struct PsnipClockTimespec *start, struct PsnipClockTimespec *end) { psnip_uint64_t r = (end->seconds - start->seconds) * PSNIP_CLOCK_NSEC_PER_SEC; if (end->nanoseconds < start->nanoseconds) { return r - (start->nanoseconds - end->nanoseconds); } return r + (end->nanoseconds - start->nanoseconds); } #else # include #endif /* defined(MUNIT_ENABLE_TIMING) */ /*** PRNG stuff ***/ /* This is (unless I screwed up, which is entirely possible) the * version of PCG with 32-bit state. It was chosen because it has a * small enough state that we should reliably be able to use CAS * instead of requiring a lock for thread-safety. * * If I did screw up, I probably will not bother changing it unless * there is a significant bias. It's really not important this be * particularly strong, as long as it is fairly random it's much more * important that it be reproducible, so bug reports have a better * chance of being reproducible. */ #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ !defined(__STDC_NO_ATOMICS__) && !defined(__EMSCRIPTEN__) && \ (!defined(__GNUC_MINOR__) || (__GNUC__ > 4) || \ (__GNUC__ == 4 && __GNUC_MINOR__ > 8)) # define HAVE_STDATOMIC #elif defined(__clang__) # if __has_extension(c_atomic) # define HAVE_CLANG_ATOMICS # endif #endif /* Workaround for http://llvm.org/bugs/show_bug.cgi?id=26911 */ #if defined(__clang__) && defined(_WIN32) # undef HAVE_STDATOMIC # if defined(__c2__) # undef HAVE_CLANG_ATOMICS # endif #endif #if defined(_OPENMP) # define ATOMIC_UINT32_T uint32_t #elif defined(HAVE_STDATOMIC) # include # define ATOMIC_UINT32_T _Atomic uint32_t #elif defined(HAVE_CLANG_ATOMICS) # define ATOMIC_UINT32_T _Atomic uint32_t #elif defined(_WIN32) # define ATOMIC_UINT32_T volatile LONG #else # define ATOMIC_UINT32_T volatile uint32_t #endif static ATOMIC_UINT32_T munit_rand_state = 42; #if defined(_OPENMP) static inline void munit_atomic_store(ATOMIC_UINT32_T *dest, ATOMIC_UINT32_T value) { # pragma omp critical(munit_atomics) *dest = value; } static inline uint32_t munit_atomic_load(ATOMIC_UINT32_T *src) { int ret; # pragma omp critical(munit_atomics) ret = *src; return ret; } static inline uint32_t munit_atomic_cas(ATOMIC_UINT32_T *dest, ATOMIC_UINT32_T *expected, ATOMIC_UINT32_T desired) { munit_bool ret; # pragma omp critical(munit_atomics) { if (*dest == *expected) { *dest = desired; ret = 1; } else { ret = 0; } } return ret; } #elif defined(HAVE_STDATOMIC) # define munit_atomic_store(dest, value) atomic_store(dest, value) # define munit_atomic_load(src) atomic_load(src) # define munit_atomic_cas(dest, expected, value) \ atomic_compare_exchange_weak(dest, expected, value) #elif defined(HAVE_CLANG_ATOMICS) # define munit_atomic_store(dest, value) \ __c11_atomic_store(dest, value, __ATOMIC_SEQ_CST) # define munit_atomic_load(src) __c11_atomic_load(src, __ATOMIC_SEQ_CST) # define munit_atomic_cas(dest, expected, value) \ __c11_atomic_compare_exchange_weak(dest, expected, value, \ __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) #elif defined(__GNUC__) && (__GNUC__ > 4) || \ (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) # define munit_atomic_store(dest, value) \ __atomic_store_n(dest, value, __ATOMIC_SEQ_CST) # define munit_atomic_load(src) __atomic_load_n(src, __ATOMIC_SEQ_CST) # define munit_atomic_cas(dest, expected, value) \ __atomic_compare_exchange_n(dest, expected, value, 1, __ATOMIC_SEQ_CST, \ __ATOMIC_SEQ_CST) #elif defined(__GNUC__) && (__GNUC__ >= 4) # define munit_atomic_store(dest, value) \ do { \ *(dest) = (value); \ } while (0) # define munit_atomic_load(src) (*(src)) # define munit_atomic_cas(dest, expected, value) \ __sync_bool_compare_and_swap(dest, *expected, value) #elif defined(_WIN32) /* Untested */ # define munit_atomic_store(dest, value) \ do { \ *(dest) = (value); \ } while (0) # define munit_atomic_load(src) (*(src)) # define munit_atomic_cas(dest, expected, value) \ InterlockedCompareExchange((dest), (value), *(expected)) #else # warning No atomic implementation, PRNG will not be thread-safe # define munit_atomic_store(dest, value) \ do { \ *(dest) = (value); \ } while (0) # define munit_atomic_load(src) (*(src)) static inline munit_bool munit_atomic_cas(ATOMIC_UINT32_T *dest, ATOMIC_UINT32_T *expected, ATOMIC_UINT32_T desired) { if (*dest == *expected) { *dest = desired; return 1; } else { return 0; } } #endif #define MUNIT_PRNG_MULTIPLIER (747796405U) #define MUNIT_PRNG_INCREMENT (1729U) static munit_uint32_t munit_rand_next_state(munit_uint32_t state) { return state * MUNIT_PRNG_MULTIPLIER + MUNIT_PRNG_INCREMENT; } static munit_uint32_t munit_rand_from_state(munit_uint32_t state) { munit_uint32_t res = ((state >> ((state >> 28) + 4)) ^ state) * (277803737U); res ^= res >> 22; return res; } void munit_rand_seed(munit_uint32_t seed) { munit_uint32_t state = munit_rand_next_state(seed + MUNIT_PRNG_INCREMENT); munit_atomic_store(&munit_rand_state, state); } static munit_uint32_t munit_rand_generate_seed(void) { munit_uint32_t seed, state; #if defined(MUNIT_ENABLE_TIMING) struct PsnipClockTimespec wc = { 0, }; psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &wc); seed = (munit_uint32_t)wc.nanoseconds; #else seed = (munit_uint32_t)time(NULL); #endif state = munit_rand_next_state(seed + MUNIT_PRNG_INCREMENT); return munit_rand_from_state(state); } static munit_uint32_t munit_rand_state_uint32(munit_uint32_t *state) { const munit_uint32_t old = *state; *state = munit_rand_next_state(old); return munit_rand_from_state(old); } munit_uint32_t munit_rand_uint32(void) { munit_uint32_t old, state; do { old = munit_atomic_load(&munit_rand_state); state = munit_rand_next_state(old); } while (!munit_atomic_cas(&munit_rand_state, &old, state)); return munit_rand_from_state(old); } static void munit_rand_state_memory(munit_uint32_t *state, size_t size, munit_uint8_t *data) { size_t members_remaining = size / sizeof(munit_uint32_t); size_t bytes_remaining = size % sizeof(munit_uint32_t); munit_uint8_t *b = data; munit_uint32_t rv; while (members_remaining-- > 0) { rv = munit_rand_state_uint32(state); memcpy(b, &rv, sizeof(munit_uint32_t)); b += sizeof(munit_uint32_t); } if (bytes_remaining != 0) { rv = munit_rand_state_uint32(state); memcpy(b, &rv, bytes_remaining); } } void munit_rand_memory(size_t size, munit_uint8_t *data) { munit_uint32_t old, state; do { state = old = munit_atomic_load(&munit_rand_state); munit_rand_state_memory(&state, size, data); } while (!munit_atomic_cas(&munit_rand_state, &old, state)); } static munit_uint32_t munit_rand_state_at_most(munit_uint32_t *state, munit_uint32_t salt, munit_uint32_t max) { /* We want (UINT32_MAX + 1) % max, which in unsigned arithmetic is the same * as (UINT32_MAX + 1 - max) % max = -max % max. We compute -max using not * to avoid compiler warnings. */ const munit_uint32_t min = (~max + 1U) % max; munit_uint32_t x; if (max == (~((munit_uint32_t)0U))) return munit_rand_state_uint32(state) ^ salt; max++; do { x = munit_rand_state_uint32(state) ^ salt; } while (x < min); return x % max; } static munit_uint32_t munit_rand_at_most(munit_uint32_t salt, munit_uint32_t max) { munit_uint32_t old, state; munit_uint32_t retval; do { state = old = munit_atomic_load(&munit_rand_state); retval = munit_rand_state_at_most(&state, salt, max); } while (!munit_atomic_cas(&munit_rand_state, &old, state)); return retval; } int munit_rand_int_range(int min, int max) { munit_uint64_t range = (munit_uint64_t)max - (munit_uint64_t)min; if (min > max) return munit_rand_int_range(max, min); if (range > (~((munit_uint32_t)0U))) range = (~((munit_uint32_t)0U)); return min + (int)munit_rand_at_most(0, (munit_uint32_t)range); } double munit_rand_double(void) { munit_uint32_t old, state; double retval = 0.0; do { state = old = munit_atomic_load(&munit_rand_state); /* See http://mumble.net/~campbell/tmp/random_real.c for how to do * this right. Patches welcome if you feel that this is too * biased. */ retval = munit_rand_state_uint32(&state) / ((~((munit_uint32_t)0U)) + 1.0); } while (!munit_atomic_cas(&munit_rand_state, &old, state)); return retval; } /*** Test suite handling ***/ typedef struct { unsigned int successful; unsigned int skipped; unsigned int failed; unsigned int errored; #if defined(MUNIT_ENABLE_TIMING) munit_uint64_t cpu_clock; munit_uint64_t wall_clock; #endif } MunitReport; typedef struct { const char *prefix; const MunitSuite *suite; const char **tests; munit_uint32_t seed; unsigned int iterations; MunitParameter *parameters; munit_bool single_parameter_mode; void *user_data; MunitReport report; munit_bool colorize; munit_bool fork; munit_bool show_stderr; munit_bool fatal_failures; } MunitTestRunner; const char *munit_parameters_get(const MunitParameter params[], const char *key) { const MunitParameter *param; for (param = params; param != NULL && param->name != NULL; param++) if (strcmp(param->name, key) == 0) return param->value; return NULL; } #if defined(MUNIT_ENABLE_TIMING) static void munit_print_time(FILE *fp, munit_uint64_t nanoseconds) { fprintf(fp, "%" MUNIT_TEST_TIME_FORMAT, ((double)nanoseconds) / ((double)PSNIP_CLOCK_NSEC_PER_SEC)); } #endif /* Add a paramter to an array of parameters. */ static MunitResult munit_parameters_add(size_t *params_size, MunitParameter **params, char *name, char *value) { *params = realloc(*params, sizeof(MunitParameter) * (*params_size + 2)); if (*params == NULL) return MUNIT_ERROR; (*params)[*params_size].name = name; (*params)[*params_size].value = value; (*params_size)++; (*params)[*params_size].name = NULL; (*params)[*params_size].value = NULL; return MUNIT_OK; } /* Concatenate two strings, but just return one of the components * unaltered if the other is NULL or "". */ static char *munit_maybe_concat(size_t *len, char *prefix, char *suffix) { char *res; size_t res_l; const size_t prefix_l = prefix != NULL ? strlen(prefix) : 0; const size_t suffix_l = suffix != NULL ? strlen(suffix) : 0; if (prefix_l == 0 && suffix_l == 0) { res = NULL; res_l = 0; } else if (prefix_l == 0 && suffix_l != 0) { res = suffix; res_l = suffix_l; } else if (prefix_l != 0 && suffix_l == 0) { res = prefix; res_l = prefix_l; } else { res_l = prefix_l + suffix_l; res = malloc(res_l + 1); memcpy(res, prefix, prefix_l); memcpy(res + prefix_l, suffix, suffix_l); res[res_l] = 0; } if (len != NULL) *len = res_l; return res; } /* Possbily free a string returned by munit_maybe_concat. */ static void munit_maybe_free_concat(char *s, const char *prefix, const char *suffix) { if (prefix != s && suffix != s) free(s); } /* Cheap string hash function, just used to salt the PRNG. */ static munit_uint32_t munit_str_hash(const char *name) { const char *p; munit_uint32_t h = 5381U; for (p = name; *p != '\0'; p++) h = (munit_uint32_t)(h << 5) + h + (munit_uint32_t)*p; return h; } static void munit_splice(int from, int to) { munit_uint8_t buf[1024]; #if !defined(_WIN32) ssize_t len; ssize_t bytes_written; ssize_t write_res; #else int len; int bytes_written; int write_res; #endif do { len = read(from, buf, sizeof(buf)); if (len > 0) { bytes_written = 0; do { write_res = write(to, buf + bytes_written, #if !defined(_WIN32) (size_t) #else (unsigned int) #endif (len - bytes_written)); if (write_res < 0) break; bytes_written += write_res; } while (bytes_written < len); } else break; } while (1); } /* This is the part that should be handled in the child process */ static MunitResult munit_test_runner_exec(MunitTestRunner *runner, const MunitTest *test, const MunitParameter params[], MunitReport *report) { unsigned int iterations = runner->iterations; MunitResult result = MUNIT_FAIL; #if defined(MUNIT_ENABLE_TIMING) struct PsnipClockTimespec wall_clock_begin = { 0, }, wall_clock_end = { 0, }; struct PsnipClockTimespec cpu_clock_begin = { 0, }, cpu_clock_end = { 0, }; #endif unsigned int i = 0; if ((test->options & MUNIT_TEST_OPTION_SINGLE_ITERATION) == MUNIT_TEST_OPTION_SINGLE_ITERATION) iterations = 1; else if (iterations == 0) iterations = runner->suite->iterations; munit_rand_seed(runner->seed); do { void *data = (test->setup == NULL) ? runner->user_data : test->setup(params, runner->user_data); #if defined(MUNIT_ENABLE_TIMING) psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &wall_clock_begin); psnip_clock_get_time(PSNIP_CLOCK_TYPE_CPU, &cpu_clock_begin); #endif result = test->test(params, data); #if defined(MUNIT_ENABLE_TIMING) psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &wall_clock_end); psnip_clock_get_time(PSNIP_CLOCK_TYPE_CPU, &cpu_clock_end); #endif if (test->tear_down != NULL) test->tear_down(data); if (MUNIT_LIKELY(result == MUNIT_OK)) { report->successful++; #if defined(MUNIT_ENABLE_TIMING) report->wall_clock += munit_clock_get_elapsed(&wall_clock_begin, &wall_clock_end); report->cpu_clock += munit_clock_get_elapsed(&cpu_clock_begin, &cpu_clock_end); #endif } else { switch ((int)result) { case MUNIT_SKIP: report->skipped++; break; case MUNIT_FAIL: report->failed++; break; case MUNIT_ERROR: report->errored++; break; default: break; } break; } } while (++i < iterations); return result; } #if defined(MUNIT_EMOTICON) # define MUNIT_RESULT_STRING_OK ":)" # define MUNIT_RESULT_STRING_SKIP ":|" # define MUNIT_RESULT_STRING_FAIL ":(" # define MUNIT_RESULT_STRING_ERROR ":o" # define MUNIT_RESULT_STRING_TODO ":/" #else # define MUNIT_RESULT_STRING_OK "OK " # define MUNIT_RESULT_STRING_SKIP "SKIP " # define MUNIT_RESULT_STRING_FAIL "FAIL " # define MUNIT_RESULT_STRING_ERROR "ERROR" # define MUNIT_RESULT_STRING_TODO "TODO " #endif static void munit_test_runner_print_color(const MunitTestRunner *runner, const char *string, char color) { if (runner->colorize) fprintf(MUNIT_OUTPUT_FILE, "\x1b[3%cm%s\x1b[39m", color, string); else fputs(string, MUNIT_OUTPUT_FILE); } #if !defined(MUNIT_NO_BUFFER) static int munit_replace_stderr(FILE *stderr_buf) { if (stderr_buf != NULL) { const int orig_stderr = dup(STDERR_FILENO); int errfd = fileno(stderr_buf); if (MUNIT_UNLIKELY(errfd == -1)) { exit(EXIT_FAILURE); } dup2(errfd, STDERR_FILENO); return orig_stderr; } return -1; } static void munit_restore_stderr(int orig_stderr) { if (orig_stderr != -1) { dup2(orig_stderr, STDERR_FILENO); close(orig_stderr); } } #endif /* !defined(MUNIT_NO_BUFFER) */ /* Run a test with the specified parameters. */ static void munit_test_runner_run_test_with_params(MunitTestRunner *runner, const MunitTest *test, const MunitParameter params[]) { MunitResult result = MUNIT_OK; MunitReport report = {0, 0, 0, 0, #if defined(MUNIT_ENABLE_TIMING) 0, 0 #endif }; unsigned int output_l; munit_bool first; const MunitParameter *param; FILE *stderr_buf; #if !defined(MUNIT_NO_FORK) int pipefd[2]; pid_t fork_pid; ssize_t bytes_written = 0; ssize_t write_res; ssize_t bytes_read = 0; ssize_t read_res; int status = 0; pid_t changed_pid; #endif if (params != NULL) { output_l = 2; fputs(" ", MUNIT_OUTPUT_FILE); first = 1; for (param = params; param != NULL && param->name != NULL; param++) { if (!first) { fputs(", ", MUNIT_OUTPUT_FILE); output_l += 2; } else { first = 0; } output_l += (unsigned int)fprintf(MUNIT_OUTPUT_FILE, "%s=%s", param->name, param->value); } while (output_l++ < MUNIT_TEST_NAME_LEN) { fputc(' ', MUNIT_OUTPUT_FILE); } } fflush(MUNIT_OUTPUT_FILE); stderr_buf = NULL; #if !defined(_WIN32) || defined(__MINGW32__) stderr_buf = tmpfile(); #else tmpfile_s(&stderr_buf); #endif if (stderr_buf == NULL) { munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to create buffer for stderr"); result = MUNIT_ERROR; goto print_result; } #if !defined(MUNIT_NO_FORK) if (runner->fork) { pipefd[0] = -1; pipefd[1] = -1; if (pipe(pipefd) != 0) { munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to create pipe"); result = MUNIT_ERROR; goto print_result; } fork_pid = fork(); if (fork_pid == 0) { int orig_stderr; close(pipefd[0]); orig_stderr = munit_replace_stderr(stderr_buf); munit_test_runner_exec(runner, test, params, &report); /* Note that we don't restore stderr. This is so we can buffer * things written to stderr later on (such as by * asan/tsan/ubsan, valgrind, etc.) */ close(orig_stderr); do { write_res = write(pipefd[1], ((munit_uint8_t *)(&report)) + bytes_written, sizeof(report) - (size_t)bytes_written); if (write_res < 0) { if (stderr_buf != NULL) { munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to write to pipe"); } exit(EXIT_FAILURE); } bytes_written += write_res; } while ((size_t)bytes_written < sizeof(report)); if (stderr_buf != NULL) fclose(stderr_buf); close(pipefd[1]); exit(EXIT_SUCCESS); } else if (fork_pid == -1) { close(pipefd[0]); close(pipefd[1]); if (stderr_buf != NULL) { munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to fork"); } report.errored++; result = MUNIT_ERROR; } else { close(pipefd[1]); do { read_res = read(pipefd[0], ((munit_uint8_t *)(&report)) + bytes_read, sizeof(report) - (size_t)bytes_read); if (read_res < 1) break; bytes_read += read_res; } while (bytes_read < (ssize_t)sizeof(report)); changed_pid = waitpid(fork_pid, &status, 0); if (MUNIT_LIKELY(changed_pid == fork_pid) && MUNIT_LIKELY(WIFEXITED(status))) { if (bytes_read != sizeof(report)) { munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf, "child exited unexpectedly with status %d", WEXITSTATUS(status)); report.errored++; } else if (WEXITSTATUS(status) != EXIT_SUCCESS) { munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf, "child exited with status %d", WEXITSTATUS(status)); report.errored++; } } else { if (WIFSIGNALED(status)) { # if defined(_XOPEN_VERSION) && (_XOPEN_VERSION >= 700) munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf, "child killed by signal %d (%s)", WTERMSIG(status), strsignal(WTERMSIG(status))); # else munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf, "child killed by signal %d", WTERMSIG(status)); # endif } else if (WIFSTOPPED(status)) { munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf, "child stopped by signal %d", WSTOPSIG(status)); } report.errored++; } close(pipefd[0]); waitpid(fork_pid, NULL, 0); } } else #endif { #if !defined(MUNIT_NO_BUFFER) const volatile int orig_stderr = munit_replace_stderr(stderr_buf); #endif #if defined(MUNIT_THREAD_LOCAL) if (MUNIT_UNLIKELY(setjmp(munit_error_jmp_buf) != 0)) { result = MUNIT_FAIL; report.failed++; } else { munit_error_jmp_buf_valid = 1; result = munit_test_runner_exec(runner, test, params, &report); } #else result = munit_test_runner_exec(runner, test, params, &report); #endif #if !defined(MUNIT_NO_BUFFER) munit_restore_stderr(orig_stderr); #endif /* Here just so that the label is used on Windows and we don't get * a warning */ goto print_result; } print_result: fputs("[ ", MUNIT_OUTPUT_FILE); if ((test->options & MUNIT_TEST_OPTION_TODO) == MUNIT_TEST_OPTION_TODO) { if (report.failed != 0 || report.errored != 0 || report.skipped != 0) { munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_TODO, '3'); result = MUNIT_OK; } else { munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_ERROR, '1'); if (MUNIT_LIKELY(stderr_buf != NULL)) munit_log_internal(MUNIT_LOG_ERROR, stderr_buf, "Test marked TODO, but was successful."); runner->report.failed++; result = MUNIT_ERROR; } } else if (report.failed > 0) { munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_FAIL, '1'); runner->report.failed++; result = MUNIT_FAIL; } else if (report.errored > 0) { munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_ERROR, '1'); runner->report.errored++; result = MUNIT_ERROR; } else if (report.skipped > 0) { munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_SKIP, '3'); runner->report.skipped++; result = MUNIT_SKIP; } else if (report.successful > 1) { munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_OK, '2'); #if defined(MUNIT_ENABLE_TIMING) fputs(" ] [ ", MUNIT_OUTPUT_FILE); munit_print_time(MUNIT_OUTPUT_FILE, report.wall_clock / report.successful); fputs(" / ", MUNIT_OUTPUT_FILE); munit_print_time(MUNIT_OUTPUT_FILE, report.cpu_clock / report.successful); fprintf(MUNIT_OUTPUT_FILE, " CPU ]\n %-" MUNIT_XSTRINGIFY(MUNIT_TEST_NAME_LEN) "s Total: [ ", ""); munit_print_time(MUNIT_OUTPUT_FILE, report.wall_clock); fputs(" / ", MUNIT_OUTPUT_FILE); munit_print_time(MUNIT_OUTPUT_FILE, report.cpu_clock); fputs(" CPU", MUNIT_OUTPUT_FILE); #endif runner->report.successful++; result = MUNIT_OK; } else if (report.successful > 0) { munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_OK, '2'); #if defined(MUNIT_ENABLE_TIMING) fputs(" ] [ ", MUNIT_OUTPUT_FILE); munit_print_time(MUNIT_OUTPUT_FILE, report.wall_clock); fputs(" / ", MUNIT_OUTPUT_FILE); munit_print_time(MUNIT_OUTPUT_FILE, report.cpu_clock); fputs(" CPU", MUNIT_OUTPUT_FILE); #endif runner->report.successful++; result = MUNIT_OK; } fputs(" ]\n", MUNIT_OUTPUT_FILE); if (stderr_buf != NULL) { if (result == MUNIT_FAIL || result == MUNIT_ERROR || runner->show_stderr) { fflush(MUNIT_OUTPUT_FILE); rewind(stderr_buf); munit_splice(fileno(stderr_buf), STDERR_FILENO); fflush(stderr); } fclose(stderr_buf); } } static void munit_test_runner_run_test_wild(MunitTestRunner *runner, const MunitTest *test, const char *test_name, MunitParameter *params, MunitParameter *p) { const MunitParameterEnum *pe; char **values; MunitParameter *next; for (pe = test->parameters; pe != NULL && pe->name != NULL; pe++) { if (p->name == pe->name) break; } if (pe == NULL) return; for (values = pe->values; *values != NULL; values++) { next = p + 1; p->value = *values; if (next->name == NULL) { munit_test_runner_run_test_with_params(runner, test, params); } else { munit_test_runner_run_test_wild(runner, test, test_name, params, next); } if (runner->fatal_failures && (runner->report.failed != 0 || runner->report.errored != 0)) break; } } /* Run a single test, with every combination of parameters * requested. */ static void munit_test_runner_run_test(MunitTestRunner *runner, const MunitTest *test, const char *prefix) { char *test_name = munit_maybe_concat(NULL, (char *)prefix, (char *)test->name); /* The array of parameters to pass to * munit_test_runner_run_test_with_params */ MunitParameter *params = NULL; size_t params_l = 0; /* Wildcard parameters are parameters which have possible values * specified in the test, but no specific value was passed to the * CLI. That means we want to run the test once for every * possible combination of parameter values or, if --single was * passed to the CLI, a single time with a random set of * parameters. */ MunitParameter *wild_params = NULL; size_t wild_params_l = 0; const MunitParameterEnum *pe; const MunitParameter *cli_p; munit_bool filled; unsigned int possible; char **vals; size_t first_wild; const MunitParameter *wp; int pidx; munit_rand_seed(runner->seed); fprintf(MUNIT_OUTPUT_FILE, "%-" MUNIT_XSTRINGIFY(MUNIT_TEST_NAME_LEN) "s", test_name); if (test->parameters == NULL) { /* No parameters. Simple, nice. */ munit_test_runner_run_test_with_params(runner, test, NULL); } else { fputc('\n', MUNIT_OUTPUT_FILE); for (pe = test->parameters; pe != NULL && pe->name != NULL; pe++) { /* Did we received a value for this parameter from the CLI? */ filled = 0; for (cli_p = runner->parameters; cli_p != NULL && cli_p->name != NULL; cli_p++) { if (strcmp(cli_p->name, pe->name) == 0) { if (MUNIT_UNLIKELY(munit_parameters_add(¶ms_l, ¶ms, pe->name, cli_p->value) != MUNIT_OK)) goto cleanup; filled = 1; break; } } if (filled) continue; /* Nothing from CLI, is the enum NULL/empty? We're not a * fuzzer… */ if (pe->values == NULL || pe->values[0] == NULL) continue; /* If --single was passed to the CLI, choose a value from the * list of possibilities randomly. */ if (runner->single_parameter_mode) { possible = 0; for (vals = pe->values; *vals != NULL; vals++) possible++; /* We want the tests to be reproducible, even if you're only * running a single test, but we don't want every test with * the same number of parameters to choose the same parameter * number, so use the test name as a primitive salt. */ pidx = (int)munit_rand_at_most(munit_str_hash(test_name), possible - 1); if (MUNIT_UNLIKELY(munit_parameters_add(¶ms_l, ¶ms, pe->name, pe->values[pidx]) != MUNIT_OK)) goto cleanup; } else { /* We want to try every permutation. Put in a placeholder * entry, we'll iterate through them later. */ if (MUNIT_UNLIKELY(munit_parameters_add(&wild_params_l, &wild_params, pe->name, NULL) != MUNIT_OK)) goto cleanup; } } if (wild_params_l != 0) { first_wild = params_l; for (wp = wild_params; wp != NULL && wp->name != NULL; wp++) { for (pe = test->parameters; pe != NULL && pe->name != NULL && pe->values != NULL; pe++) { if (strcmp(wp->name, pe->name) == 0) { if (MUNIT_UNLIKELY(munit_parameters_add(¶ms_l, ¶ms, pe->name, pe->values[0]) != MUNIT_OK)) goto cleanup; } } } munit_test_runner_run_test_wild(runner, test, test_name, params, params + first_wild); } else { munit_test_runner_run_test_with_params(runner, test, params); } cleanup: free(params); free(wild_params); } munit_maybe_free_concat(test_name, prefix, test->name); } /* Recurse through the suite and run all the tests. If a list of * tests to run was provied on the command line, run only those * tests. */ static void munit_test_runner_run_suite(MunitTestRunner *runner, const MunitSuite *suite, const char *prefix) { size_t pre_l; char *pre = munit_maybe_concat(&pre_l, (char *)prefix, (char *)suite->prefix); const MunitTest *test; const char **test_name; const MunitSuite *child_suite; /* Run the tests. */ for (test = suite->tests; test != NULL && test->test != NULL; test++) { if (runner->tests != NULL) { /* Specific tests were requested on the CLI */ for (test_name = runner->tests; test_name != NULL && *test_name != NULL; test_name++) { if ((pre_l == 0 || strncmp(pre, *test_name, pre_l) == 0) && strncmp(test->name, *test_name + pre_l, strlen(*test_name + pre_l)) == 0) { munit_test_runner_run_test(runner, test, pre); if (runner->fatal_failures && (runner->report.failed != 0 || runner->report.errored != 0)) goto cleanup; } } } else { /* Run all tests */ munit_test_runner_run_test(runner, test, pre); } } if (runner->fatal_failures && (runner->report.failed != 0 || runner->report.errored != 0)) goto cleanup; /* Run any child suites. */ for (child_suite = suite->suites; child_suite != NULL && child_suite->prefix != NULL; child_suite++) { munit_test_runner_run_suite(runner, child_suite, pre); } cleanup: munit_maybe_free_concat(pre, prefix, suite->prefix); } static void munit_test_runner_run(MunitTestRunner *runner) { munit_test_runner_run_suite(runner, runner->suite, NULL); } static void munit_print_help(int argc, char *const *argv, void *user_data, const MunitArgument arguments[]) { const MunitArgument *arg; (void)argc; printf("USAGE: %s [OPTIONS...] [TEST...]\n\n", argv[0]); puts( " --seed SEED\n" " Value used to seed the PRNG. Must be a 32-bit integer in " "decimal\n" " notation with no separators (commas, decimals, spaces, " "etc.), or\n" " hexidecimal prefixed by \"0x\".\n" " --iterations N\n" " Run each test N times. 0 means the default number.\n" " --param name value\n" " A parameter key/value pair which will be passed to any test " "with\n" " takes a parameter of that name. If not provided, the test " "will be\n" " run once for each possible parameter value.\n" " --list Write a list of all available tests.\n" " --list-params\n" " Write a list of all available tests and their possible " "parameters.\n" " --single Run each parameterized test in a single configuration " "instead of\n" " every possible combination\n" " --log-visible debug|info|warning|error\n" " --log-fatal debug|info|warning|error\n" " Set the level at which messages of different severities are " "visible,\n" " or cause the test to terminate.\n" #if !defined(MUNIT_NO_FORK) " --no-fork Do not execute tests in a child process. If this option is " "supplied\n" " and a test crashes (including by failing an assertion), no " "further\n" " tests will be performed.\n" #endif " --fatal-failures\n" " Stop executing tests as soon as a failure is found.\n" " --show-stderr\n" " Show data written to stderr by the tests, even if the test " "succeeds.\n" " --color auto|always|never\n" " Colorize (or don't) the output.\n" /* 12345678901234567890123456789012345678901234567890123456789012345678901234567890 */ " --help Print this help message and exit.\n"); #if defined(MUNIT_NL_LANGINFO) setlocale(LC_ALL, ""); fputs((strcasecmp("UTF-8", nl_langinfo(CODESET)) == 0) ? "µnit" : "munit", stdout); #else puts("munit"); #endif printf(" %d.%d.%d\n" "Full documentation at: https://nemequ.github.io/munit/\n", (MUNIT_CURRENT_VERSION >> 16) & 0xff, (MUNIT_CURRENT_VERSION >> 8) & 0xff, (MUNIT_CURRENT_VERSION >> 0) & 0xff); for (arg = arguments; arg != NULL && arg->name != NULL; arg++) arg->write_help(arg, user_data); } static const MunitArgument * munit_arguments_find(const MunitArgument arguments[], const char *name) { const MunitArgument *arg; for (arg = arguments; arg != NULL && arg->name != NULL; arg++) if (strcmp(arg->name, name) == 0) return arg; return NULL; } static void munit_suite_list_tests(const MunitSuite *suite, munit_bool show_params, const char *prefix) { size_t pre_l; char *pre = munit_maybe_concat(&pre_l, (char *)prefix, (char *)suite->prefix); const MunitTest *test; const MunitParameterEnum *params; munit_bool first; char **val; const MunitSuite *child_suite; for (test = suite->tests; test != NULL && test->name != NULL; test++) { if (pre != NULL) fputs(pre, stdout); puts(test->name); if (show_params) { for (params = test->parameters; params != NULL && params->name != NULL; params++) { fprintf(stdout, " - %s: ", params->name); if (params->values == NULL) { puts("Any"); } else { first = 1; for (val = params->values; *val != NULL; val++) { if (!first) { fputs(", ", stdout); } else { first = 0; } fputs(*val, stdout); } putc('\n', stdout); } } } } for (child_suite = suite->suites; child_suite != NULL && child_suite->prefix != NULL; child_suite++) { munit_suite_list_tests(child_suite, show_params, pre); } munit_maybe_free_concat(pre, prefix, suite->prefix); } static munit_bool munit_stream_supports_ansi(FILE *stream) { #if !defined(_WIN32) return isatty(fileno(stream)); #else # if !defined(__MINGW32__) size_t ansicon_size = 0; # endif if (isatty(fileno(stream))) { # if !defined(__MINGW32__) getenv_s(&ansicon_size, NULL, 0, "ANSICON"); return ansicon_size != 0; # else return getenv("ANSICON") != NULL; # endif } return 0; #endif } int munit_suite_main_custom(const MunitSuite *suite, void *user_data, int argc, char *const *argv, const MunitArgument arguments[]) { int result = EXIT_FAILURE; MunitTestRunner runner; size_t parameters_size = 0; size_t tests_size = 0; int arg; char *envptr; unsigned long ts; char *endptr; unsigned long long iterations; MunitLogLevel level; const MunitArgument *argument; const char **runner_tests; unsigned int tests_run; unsigned int tests_total; runner.prefix = NULL; runner.suite = NULL; runner.tests = NULL; runner.seed = 0; runner.iterations = 0; runner.parameters = NULL; runner.single_parameter_mode = 0; runner.user_data = NULL; runner.report.successful = 0; runner.report.skipped = 0; runner.report.failed = 0; runner.report.errored = 0; #if defined(MUNIT_ENABLE_TIMING) runner.report.cpu_clock = 0; runner.report.wall_clock = 0; #endif runner.colorize = 0; #if !defined(_WIN32) runner.fork = 1; #else runner.fork = 0; #endif runner.show_stderr = 0; runner.fatal_failures = 0; runner.suite = suite; runner.user_data = user_data; runner.seed = munit_rand_generate_seed(); runner.colorize = munit_stream_supports_ansi(MUNIT_OUTPUT_FILE); for (arg = 1; arg < argc; arg++) { if (strncmp("--", argv[arg], 2) == 0) { if (strcmp("seed", argv[arg] + 2) == 0) { if (arg + 1 >= argc) { munit_logf_internal(MUNIT_LOG_ERROR, stderr, "%s requires an argument", argv[arg]); goto cleanup; } envptr = argv[arg + 1]; ts = strtoul(argv[arg + 1], &envptr, 0); if (*envptr != '\0' || ts > (~((munit_uint32_t)0U))) { munit_logf_internal(MUNIT_LOG_ERROR, stderr, "invalid value ('%s') passed to %s", argv[arg + 1], argv[arg]); goto cleanup; } runner.seed = (munit_uint32_t)ts; arg++; } else if (strcmp("iterations", argv[arg] + 2) == 0) { if (arg + 1 >= argc) { munit_logf_internal(MUNIT_LOG_ERROR, stderr, "%s requires an argument", argv[arg]); goto cleanup; } endptr = argv[arg + 1]; iterations = strtoul(argv[arg + 1], &endptr, 0); if (*endptr != '\0' || iterations > UINT_MAX) { munit_logf_internal(MUNIT_LOG_ERROR, stderr, "invalid value ('%s') passed to %s", argv[arg + 1], argv[arg]); goto cleanup; } runner.iterations = (unsigned int)iterations; arg++; } else if (strcmp("param", argv[arg] + 2) == 0) { if (arg + 2 >= argc) { munit_logf_internal(MUNIT_LOG_ERROR, stderr, "%s requires two arguments", argv[arg]); goto cleanup; } runner.parameters = realloc(runner.parameters, sizeof(MunitParameter) * (parameters_size + 2)); if (runner.parameters == NULL) { munit_log_internal(MUNIT_LOG_ERROR, stderr, "failed to allocate memory"); goto cleanup; } runner.parameters[parameters_size].name = (char *)argv[arg + 1]; runner.parameters[parameters_size].value = (char *)argv[arg + 2]; parameters_size++; runner.parameters[parameters_size].name = NULL; runner.parameters[parameters_size].value = NULL; arg += 2; } else if (strcmp("color", argv[arg] + 2) == 0) { if (arg + 1 >= argc) { munit_logf_internal(MUNIT_LOG_ERROR, stderr, "%s requires an argument", argv[arg]); goto cleanup; } if (strcmp(argv[arg + 1], "always") == 0) runner.colorize = 1; else if (strcmp(argv[arg + 1], "never") == 0) runner.colorize = 0; else if (strcmp(argv[arg + 1], "auto") == 0) runner.colorize = munit_stream_supports_ansi(MUNIT_OUTPUT_FILE); else { munit_logf_internal(MUNIT_LOG_ERROR, stderr, "invalid value ('%s') passed to %s", argv[arg + 1], argv[arg]); goto cleanup; } arg++; } else if (strcmp("help", argv[arg] + 2) == 0) { munit_print_help(argc, argv, user_data, arguments); result = EXIT_SUCCESS; goto cleanup; } else if (strcmp("single", argv[arg] + 2) == 0) { runner.single_parameter_mode = 1; } else if (strcmp("show-stderr", argv[arg] + 2) == 0) { runner.show_stderr = 1; #if !defined(_WIN32) } else if (strcmp("no-fork", argv[arg] + 2) == 0) { runner.fork = 0; #endif } else if (strcmp("fatal-failures", argv[arg] + 2) == 0) { runner.fatal_failures = 1; } else if (strcmp("log-visible", argv[arg] + 2) == 0 || strcmp("log-fatal", argv[arg] + 2) == 0) { if (arg + 1 >= argc) { munit_logf_internal(MUNIT_LOG_ERROR, stderr, "%s requires an argument", argv[arg]); goto cleanup; } if (strcmp(argv[arg + 1], "debug") == 0) level = MUNIT_LOG_DEBUG; else if (strcmp(argv[arg + 1], "info") == 0) level = MUNIT_LOG_INFO; else if (strcmp(argv[arg + 1], "warning") == 0) level = MUNIT_LOG_WARNING; else if (strcmp(argv[arg + 1], "error") == 0) level = MUNIT_LOG_ERROR; else { munit_logf_internal(MUNIT_LOG_ERROR, stderr, "invalid value ('%s') passed to %s", argv[arg + 1], argv[arg]); goto cleanup; } if (strcmp("log-visible", argv[arg] + 2) == 0) munit_log_level_visible = level; else munit_log_level_fatal = level; arg++; } else if (strcmp("list", argv[arg] + 2) == 0) { munit_suite_list_tests(suite, 0, NULL); result = EXIT_SUCCESS; goto cleanup; } else if (strcmp("list-params", argv[arg] + 2) == 0) { munit_suite_list_tests(suite, 1, NULL); result = EXIT_SUCCESS; goto cleanup; } else { argument = munit_arguments_find(arguments, argv[arg] + 2); if (argument == NULL) { munit_logf_internal(MUNIT_LOG_ERROR, stderr, "unknown argument ('%s')", argv[arg]); goto cleanup; } if (!argument->parse_argument(suite, user_data, &arg, argc, argv)) goto cleanup; } } else { runner_tests = realloc((void *)runner.tests, sizeof(char *) * (tests_size + 2)); if (runner_tests == NULL) { munit_log_internal(MUNIT_LOG_ERROR, stderr, "failed to allocate memory"); goto cleanup; } runner.tests = runner_tests; runner.tests[tests_size++] = argv[arg]; runner.tests[tests_size] = NULL; } } fflush(stderr); fprintf(MUNIT_OUTPUT_FILE, "Running test suite with seed 0x%08" PRIx32 "...\n", runner.seed); munit_test_runner_run(&runner); tests_run = runner.report.successful + runner.report.failed + runner.report.errored; tests_total = tests_run + runner.report.skipped; if (tests_run == 0) { fprintf(stderr, "No tests run, %d (100%%) skipped.\n", runner.report.skipped); } else { fprintf(MUNIT_OUTPUT_FILE, "%d of %d (%0.0f%%) tests successful, %d (%0.0f%%) test skipped.\n", runner.report.successful, tests_run, (((double)runner.report.successful) / ((double)tests_run)) * 100.0, runner.report.skipped, (((double)runner.report.skipped) / ((double)tests_total)) * 100.0); } if (runner.report.failed == 0 && runner.report.errored == 0) { result = EXIT_SUCCESS; } cleanup: free(runner.parameters); free((void *)runner.tests); return result; } int munit_suite_main(const MunitSuite *suite, void *user_data, int argc, char *const *argv) { return munit_suite_main_custom(suite, user_data, argc, argv, NULL); } static uint8_t hexchars[] = "0123456789abcdef"; static uint8_t *hexdump_addr(uint8_t *dest, size_t addr) { size_t i; uint8_t a; for (i = 0; i < 4; ++i) { a = (addr >> (3 - i) * 8) & 0xff; *dest++ = hexchars[a >> 4]; *dest++ = hexchars[a & 0xf]; } return dest; } static uint8_t *asciidump(uint8_t *dest, const uint8_t *data, size_t datalen) { size_t i; *dest++ = '|'; for (i = 0; i < datalen; ++i) { if (0x20 <= data[i] && data[i] <= 0x7e) { *dest++ = data[i]; } else { *dest++ = '.'; } } *dest++ = '|'; return dest; } static uint8_t *hexdump8(uint8_t *dest, const uint8_t *data, size_t datalen) { size_t i; for (i = 0; i < datalen; ++i) { *dest++ = hexchars[data[i] >> 4]; *dest++ = hexchars[data[i] & 0xf]; *dest++ = ' '; } for (; i < 8; ++i) { *dest++ = ' '; *dest++ = ' '; *dest++ = ' '; } return dest; } static uint8_t *hexdump16(uint8_t *dest, const uint8_t *data, size_t datalen) { dest = hexdump8(dest, data, datalen < 8 ? datalen : 8); *dest++ = ' '; if (datalen < 8) { data = NULL; datalen = 0; } else { data += 8; datalen -= 8; } dest = hexdump8(dest, data, datalen); *dest++ = ' '; return dest; } static uint8_t *hexdump_line(uint8_t *dest, const uint8_t *data, size_t datalen, size_t addr) { dest = hexdump_addr(dest, addr); *dest++ = ' '; *dest++ = ' '; dest = hexdump16(dest, data, datalen); dest = asciidump(dest, data, datalen); return dest; } int munit_hexdump(FILE *fp, const void *data, size_t datalen) { size_t offset = 0, n, len; uint8_t buf[128], *p; const uint8_t *s; int repeated = 0; if (datalen == 0) { return 0; } for (; offset < datalen; offset += 16) { n = datalen - offset; s = (const uint8_t *)data + offset; if (n >= 16) { n = 16; if (offset > 0) { if (memcmp(s - 16, s, 16) == 0) { if (repeated) { continue; } repeated = 1; if (fwrite("*\n", 1, 2, fp) < 2) { return -1; } continue; } repeated = 0; } } p = hexdump_line(buf, s, n, offset); *p++ = '\n'; len = (size_t)(p - buf); if (fwrite(buf, 1, len, fp) < len) { return -1; } } p = hexdump_addr(buf, datalen); *p++ = '\n'; len = (size_t)(p - buf); if (fwrite(buf, 1, len, fp) < len) { return -1; } return 0; } int munit_hexdump_diff(FILE *fp, const void *a, size_t alen, const void *b, size_t blen) { size_t offset = 0, k, i, len, ncomp, maxlen, adoff = 0; uint8_t buf[128], *p; const uint8_t mk[2] = {'-', '+'}; struct datasource { const uint8_t *data; size_t datalen; const uint8_t *s; size_t n; } ds[] = {{a, alen, NULL, 0}, {b, blen, NULL, 0}}, *dp; maxlen = alen < blen ? blen : alen; for (; offset < maxlen; offset += 16) { for (k = 0; k < 2; ++k) { dp = &ds[k]; if (offset < dp->datalen) { dp->s = (const uint8_t *)dp->data + offset; dp->n = dp->datalen - offset; if (dp->n > 16) { dp->n = 16; } } else { dp->s = NULL; dp->n = 0; } } if (ds[0].n == ds[1].n && memcmp(ds[0].s, ds[1].s, ds[0].n) == 0) { continue; } for (k = 0; k < 2; ++k) { dp = &ds[k]; if (!dp->n) { continue; } p = buf; *p++ = mk[k]; *p++ = mk[k]; *p++ = mk[k]; *p++ = mk[k]; p = hexdump_line(p, dp->s, dp->n, offset); *p++ = '\n'; len = (size_t)(p - buf); if (fwrite(buf, 1, len, fp) < len) { return -1; } } if (!ds[0].n || !ds[1].n) { continue; } ncomp = ds[0].n < ds[1].n ? ds[0].n : ds[1].n; p = buf + 4 + 10; memset(buf, ' ', 4 + 78); for (i = 0; i < ncomp; ++i) { if (ds[0].s[i] == ds[1].s[i]) { *p++ = ' '; *p++ = ' '; } else { adoff = 4 + 10 + 51 + i; *(buf + adoff) = '^'; *p++ = '^'; *p++ = '^'; } *p++ = ' '; if (i == 7) { *p++ = ' '; } } if (adoff) { len = adoff + 1; } else { len = (size_t)(p - buf); } buf[len++] = '\n'; if (fwrite(buf, 1, len, fp) < len) { return -1; } } return 0; } nghttp2-1.68.0/tests/PaxHeaders/failmalloc_test.h0000644000000000000000000000013115077107271016735 xustar0030 mtime=1761382073.004444088 29 atime=1761382080.10641148 30 ctime=1761382108.060303388 nghttp2-1.68.0/tests/failmalloc_test.h0000644000175100017510000000315615077107271017333 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef FAILMALLOC_TEST_H #define FAILMALLOC_TEST_H #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" extern const MunitSuite failmalloc_suite; munit_void_test_decl(test_nghttp2_session_send) munit_void_test_decl(test_nghttp2_session_send_server) munit_void_test_decl(test_nghttp2_session_recv) munit_void_test_decl(test_nghttp2_frame) munit_void_test_decl(test_nghttp2_hd) #endif /* FAILMALLOC_TEST_H */ nghttp2-1.68.0/tests/PaxHeaders/nghttp2_helper_test.c0000644000000000000000000000013215077107271017553 xustar0030 mtime=1761382073.007444074 30 atime=1761382080.107411476 30 ctime=1761382108.099303275 nghttp2-1.68.0/tests/nghttp2_helper_test.c0000644000175100017510000001655315077107271020155 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_helper_test.h" #include #include "munit.h" #include "nghttp2_helper.h" static const MunitTest tests[] = { munit_void_test(test_nghttp2_adjust_local_window_size), munit_void_test(test_nghttp2_check_header_name), munit_void_test(test_nghttp2_check_header_value), munit_void_test(test_nghttp2_check_header_value_rfc9113), munit_test_end(), }; const MunitSuite helper_suite = { "/helper", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE, }; void test_nghttp2_adjust_local_window_size(void) { int32_t local_window_size = 100; int32_t recv_window_size = 50; int32_t recv_reduction = 0; int32_t delta; delta = 0; assert_int(0, ==, nghttp2_adjust_local_window_size( &local_window_size, &recv_window_size, &recv_reduction, &delta)); assert_int32(100, ==, local_window_size); assert_int32(50, ==, recv_window_size); assert_int32(0, ==, recv_reduction); assert_int32(0, ==, delta); delta = 49; assert_int(0, ==, nghttp2_adjust_local_window_size( &local_window_size, &recv_window_size, &recv_reduction, &delta)); assert_int32(100, ==, local_window_size); assert_int32(1, ==, recv_window_size); assert_int32(0, ==, recv_reduction); assert_int32(49, ==, delta); delta = 1; assert_int(0, ==, nghttp2_adjust_local_window_size( &local_window_size, &recv_window_size, &recv_reduction, &delta)); assert_int32(100, ==, local_window_size); assert_int32(0, ==, recv_window_size); assert_int32(0, ==, recv_reduction); assert_int32(1, ==, delta); delta = 1; assert_int(0, ==, nghttp2_adjust_local_window_size( &local_window_size, &recv_window_size, &recv_reduction, &delta)); assert_int32(101, ==, local_window_size); assert_int32(0, ==, recv_window_size); assert_int32(0, ==, recv_reduction); assert_int32(1, ==, delta); delta = -1; assert_int(0, ==, nghttp2_adjust_local_window_size( &local_window_size, &recv_window_size, &recv_reduction, &delta)); assert_int32(100, ==, local_window_size); assert_int32(-1, ==, recv_window_size); assert_int32(1, ==, recv_reduction); assert_int32(0, ==, delta); delta = 1; assert_int(0, ==, nghttp2_adjust_local_window_size( &local_window_size, &recv_window_size, &recv_reduction, &delta)); assert_int32(101, ==, local_window_size); assert_int32(0, ==, recv_window_size); assert_int32(0, ==, recv_reduction); assert_int32(0, ==, delta); delta = 100; assert_int(0, ==, nghttp2_adjust_local_window_size( &local_window_size, &recv_window_size, &recv_reduction, &delta)); assert_int32(201, ==, local_window_size); assert_int32(0, ==, recv_window_size); assert_int32(0, ==, recv_reduction); assert_int32(100, ==, delta); delta = -3; assert_int(0, ==, nghttp2_adjust_local_window_size( &local_window_size, &recv_window_size, &recv_reduction, &delta)); assert_int32(198, ==, local_window_size); assert_int32(-3, ==, recv_window_size); assert_int32(3, ==, recv_reduction); assert_int32(0, ==, delta); recv_window_size += 3; delta = 3; assert_int(0, ==, nghttp2_adjust_local_window_size( &local_window_size, &recv_window_size, &recv_reduction, &delta)); assert_int32(201, ==, local_window_size); assert_int32(3, ==, recv_window_size); assert_int32(0, ==, recv_reduction); assert_int32(0, ==, delta); local_window_size = 100; recv_window_size = 50; recv_reduction = 0; delta = INT32_MAX; assert_int(NGHTTP2_ERR_FLOW_CONTROL, ==, nghttp2_adjust_local_window_size( &local_window_size, &recv_window_size, &recv_reduction, &delta)); assert_int32(100, ==, local_window_size); assert_int32(50, ==, recv_window_size); assert_int32(0, ==, recv_reduction); assert_int32(INT32_MAX, ==, delta); delta = INT32_MIN; assert_int(NGHTTP2_ERR_FLOW_CONTROL, ==, nghttp2_adjust_local_window_size( &local_window_size, &recv_window_size, &recv_reduction, &delta)); assert_int32(100, ==, local_window_size); assert_int32(50, ==, recv_window_size); assert_int32(0, ==, recv_reduction); assert_int32(INT32_MIN, ==, delta); } #define check_header_name(S) \ nghttp2_check_header_name((const uint8_t *)S, sizeof(S) - 1) void test_nghttp2_check_header_name(void) { assert_true(check_header_name(":path")); assert_true(check_header_name("path")); assert_true(check_header_name("!#$%&'*+-.^_`|~")); assert_false(check_header_name(":PATH")); assert_false(check_header_name("path:")); assert_false(check_header_name("")); assert_false(check_header_name(":")); } #define check_header_value(S) \ nghttp2_check_header_value((const uint8_t *)S, sizeof(S) - 1) void test_nghttp2_check_header_value(void) { uint8_t goodval[] = {'a', 'b', 0x80u, 'c', 0xffu, 'd', '\t', ' '}; uint8_t badval1[] = {'a', 0x1fu, 'b'}; uint8_t badval2[] = {'a', 0x7fu, 'b'}; assert_true(check_header_value(" !|}~")); assert_true(check_header_value(goodval)); assert_false(check_header_value(badval1)); assert_false(check_header_value(badval2)); assert_true(check_header_value("")); assert_true(check_header_value(" ")); assert_true(check_header_value("\t")); } #define check_header_value_rfc9113(S) \ nghttp2_check_header_value_rfc9113((const uint8_t *)S, sizeof(S) - 1) void test_nghttp2_check_header_value_rfc9113(void) { uint8_t goodval[] = {'a', 'b', 0x80u, 'c', 0xffu, 'd'}; uint8_t badval1[] = {'a', 0x1fu, 'b'}; uint8_t badval2[] = {'a', 0x7fu, 'b'}; assert_true(check_header_value_rfc9113("!|}~")); assert_false(check_header_value_rfc9113(" !|}~")); assert_false(check_header_value_rfc9113("!|}~ ")); assert_false(check_header_value_rfc9113("\t!|}~")); assert_false(check_header_value_rfc9113("!|}~\t")); assert_true(check_header_value_rfc9113(goodval)); assert_false(check_header_value_rfc9113(badval1)); assert_false(check_header_value_rfc9113(badval2)); assert_true(check_header_value_rfc9113("")); assert_false(check_header_value_rfc9113(" ")); assert_false(check_header_value_rfc9113("\t")); } nghttp2-1.68.0/tests/PaxHeaders/nghttp2_helper_test.h0000644000000000000000000000013115077107271017557 xustar0030 mtime=1761382073.007444074 30 atime=1761382080.107411476 29 ctime=1761382108.08030333 nghttp2-1.68.0/tests/nghttp2_helper_test.h0000644000175100017510000000316515077107271020155 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_HELPER_TEST_H #define NGHTTP2_HELPER_TEST_H #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" extern const MunitSuite helper_suite; munit_void_test_decl(test_nghttp2_adjust_local_window_size) munit_void_test_decl(test_nghttp2_check_header_name) munit_void_test_decl(test_nghttp2_check_header_value) munit_void_test_decl(test_nghttp2_check_header_value_rfc9113) #endif /* NGHTTP2_HELPER_TEST_H */ nghttp2-1.68.0/tests/PaxHeaders/nghttp2_stream_test.h0000644000000000000000000000013215077107271017574 xustar0030 mtime=1761382073.008444069 30 atime=1761382080.108411471 30 ctime=1761382108.076303341 nghttp2-1.68.0/tests/nghttp2_stream_test.h0000644000175100017510000000245715077107271020174 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_STREAM_TEST_H #define NGHTTP2_STREAM_TEST_H #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #endif /* NGHTTP2_STREAM_TEST_H */ nghttp2-1.68.0/tests/PaxHeaders/nghttp2_http_test.c0000644000000000000000000000013215077107271017253 xustar0030 mtime=1761382073.007444074 30 atime=1761382080.107411476 30 ctime=1761382108.101303269 nghttp2-1.68.0/tests/nghttp2_http_test.c0000644000175100017510000001263415077107271017651 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2022 nghttp3 contributors * Copyright (c) 2022 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_http_test.h" #include #include #include "munit.h" #include "nghttp2_http.h" #include "nghttp2_test_helper.h" static const MunitTest tests[] = { munit_void_test(test_nghttp2_http_parse_priority), munit_test_end(), }; const MunitSuite http_suite = { "/http", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE, }; void test_nghttp2_http_parse_priority(void) { int rv; { nghttp2_extpri pri = {(uint32_t)-1, -1}; const uint8_t v[] = ""; rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1); assert_int(0, ==, rv); assert_uint32((uint32_t)-1, ==, pri.urgency); assert_int(-1, ==, pri.inc); } { nghttp2_extpri pri = {(uint32_t)-1, -1}; const uint8_t v[] = "u=7,i"; rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1); assert_int(0, ==, rv); assert_uint32((uint32_t)7, ==, pri.urgency); assert_int(1, ==, pri.inc); } { nghttp2_extpri pri = {(uint32_t)-1, -1}; const uint8_t v[] = "u=0,i=?0"; rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1); assert_int(0, ==, rv); assert_uint32((uint32_t)0, ==, pri.urgency); assert_int(0, ==, pri.inc); } { nghttp2_extpri pri = {(uint32_t)-1, -1}; const uint8_t v[] = "u=3, i"; rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1); assert_int(0, ==, rv); assert_uint32((uint32_t)3, ==, pri.urgency); assert_int(1, ==, pri.inc); } { nghttp2_extpri pri = {(uint32_t)-1, -1}; const uint8_t v[] = "u=0, i, i=?0, u=6"; rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1); assert_int(0, ==, rv); assert_uint32((uint32_t)6, ==, pri.urgency); assert_int(0, ==, pri.inc); } { nghttp2_extpri pri = {(uint32_t)-1, -1}; const uint8_t v[] = "u=0,"; rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1); assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, rv); } { nghttp2_extpri pri = {(uint32_t)-1, -1}; const uint8_t v[] = "u=0, "; rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1); assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, rv); } { nghttp2_extpri pri = {(uint32_t)-1, -1}; const uint8_t v[] = "u="; rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1); assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, rv); } { nghttp2_extpri pri = {(uint32_t)-1, -1}; const uint8_t v[] = "u"; rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1); assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, rv); } { nghttp2_extpri pri = {(uint32_t)-1, -1}; const uint8_t v[] = "i=?1"; rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1); assert_int(0, ==, rv); assert_uint32((uint32_t)-1, ==, pri.urgency); assert_int(1, ==, pri.inc); } { nghttp2_extpri pri = {(uint32_t)-1, -1}; const uint8_t v[] = "i=?2"; rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1); assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, rv); } { nghttp2_extpri pri = {(uint32_t)-1, -1}; const uint8_t v[] = "i=?"; rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1); assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, rv); } { nghttp2_extpri pri = {(uint32_t)-1, -1}; const uint8_t v[] = "i="; rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1); assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, rv); } { nghttp2_extpri pri = {(uint32_t)-1, -1}; const uint8_t v[] = "u=-1"; rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1); assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, rv); } { nghttp2_extpri pri = {(uint32_t)-1, -1}; const uint8_t v[] = "u=8"; rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1); assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, rv); } { nghttp2_extpri pri = {(uint32_t)-1, -1}; const uint8_t v[] = "i=?0, u=1, a=(x y z), u=2; i=?0;foo=\",,,\", i=?1;i=?0; u=6"; rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1); assert_int(0, ==, rv); assert_uint32((uint32_t)2, ==, pri.urgency); assert_int(1, ==, pri.inc); } { nghttp2_extpri pri = {(uint32_t)-1, -1}; const uint8_t v[] = {'u', '='}; rv = nghttp2_http_parse_priority(&pri, v, sizeof(v)); assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, rv); } } nghttp2-1.68.0/tests/PaxHeaders/nghttp2_stream_test.c0000644000000000000000000000013215077107271017567 xustar0030 mtime=1761382073.008444069 30 atime=1761382080.108411471 30 ctime=1761382108.094303289 nghttp2-1.68.0/tests/nghttp2_stream_test.c0000644000175100017510000000233215077107271020157 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_stream_test.h" #include #include "nghttp2_stream.h" nghttp2-1.68.0/tests/PaxHeaders/nghttp2_queue_test.c0000644000000000000000000000013215077107271017420 xustar0030 mtime=1761382073.007444074 30 atime=1761382080.107411476 30 ctime=1761382108.091303298 nghttp2-1.68.0/tests/nghttp2_queue_test.c0000644000175100017510000000373715077107271020022 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_queue_test.h" #include #include "munit.h" #include "nghttp2_queue.h" static const MunitTest tests[] = { munit_void_test(test_nghttp2_queue), munit_test_end(), }; const MunitSuite queue_suite = { "/queue", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE, }; void test_nghttp2_queue(void) { int ints[] = {1, 2, 3, 4, 5}; int i; nghttp2_queue queue; nghttp2_queue_init(&queue); assert_true(nghttp2_queue_empty(&queue)); for (i = 0; i < 5; ++i) { nghttp2_queue_push(&queue, &ints[i]); assert_int(ints[0], ==, *(int *)(nghttp2_queue_front(&queue))); assert_false(nghttp2_queue_empty(&queue)); } for (i = 0; i < 5; ++i) { assert_int(ints[i], ==, *(int *)(nghttp2_queue_front(&queue))); nghttp2_queue_pop(&queue); } assert_true(nghttp2_queue_empty(&queue)); nghttp2_queue_free(&queue); } nghttp2-1.68.0/tests/PaxHeaders/malloc_wrapper.h0000644000000000000000000000013115077107271016602 xustar0030 mtime=1761382073.005444083 29 atime=1761382080.10641148 30 ctime=1761382108.063303379 nghttp2-1.68.0/tests/malloc_wrapper.h0000644000175100017510000000474415077107271017204 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MALLOC_WRAPPER_H #define MALLOC_WRAPPER_H #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #include #include "nghttp2_mem.h" /* Global variables to control the behavior of malloc() */ /* If nonzero, malloc failure mode is on */ extern int nghttp2_failmalloc; /* If nghttp2_failstart <= nghttp2_nmalloc and nghttp2_failmalloc is nonzero, malloc() fails. */ extern int nghttp2_failstart; /* If nonzero, nghttp2_nmalloc is incremented if malloc() succeeds. */ extern int nghttp2_countmalloc; /* The number of successful invocation of malloc(). This value is only incremented if nghttp2_nmalloc is nonzero. */ extern int nghttp2_nmalloc; /* Returns pointer to nghttp2_mem, which, when dereferenced, contains specifically instrumented memory allocators for failmalloc tests. */ nghttp2_mem *nghttp2_mem_fm(void); /* Copies nghttp2_failmalloc and nghttp2_countmalloc to statically allocated space and sets 0 to them. This will effectively make malloc() work like normal malloc(). This is useful when you want to disable malloc() failure mode temporarily. */ void nghttp2_failmalloc_pause(void); /* Restores the values of nghttp2_failmalloc and nghttp2_countmalloc with the values saved by the previous nghttp2_failmalloc_pause(). */ void nghttp2_failmalloc_unpause(void); #endif /* MALLOC_WRAPPER_H */ nghttp2-1.68.0/tests/PaxHeaders/nghttp2_hd_test.c0000644000000000000000000000013215077107271016667 xustar0030 mtime=1761382073.006444079 30 atime=1761382080.107411476 30 ctime=1761382108.096303283 nghttp2-1.68.0/tests/nghttp2_hd_test.c0000644000175100017510000014604415077107271017270 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_hd_test.h" #include #include #include "munit.h" #include "nghttp2_hd.h" #include "nghttp2_frame.h" #include "nghttp2_test_helper.h" #include "nghttp2_assertion.h" static const MunitTest tests[] = { munit_void_test(test_nghttp2_hd_deflate), munit_void_test(test_nghttp2_hd_deflate_same_indexed_repr), munit_void_test(test_nghttp2_hd_inflate_indexed), munit_void_test(test_nghttp2_hd_inflate_indname_noinc), munit_void_test(test_nghttp2_hd_inflate_indname_inc), munit_void_test(test_nghttp2_hd_inflate_indname_inc_eviction), munit_void_test(test_nghttp2_hd_inflate_newname_noinc), munit_void_test(test_nghttp2_hd_inflate_newname_inc), munit_void_test(test_nghttp2_hd_inflate_clearall_inc), munit_void_test(test_nghttp2_hd_inflate_zero_length_huffman), munit_void_test(test_nghttp2_hd_inflate_expect_table_size_update), munit_void_test(test_nghttp2_hd_inflate_unexpected_table_size_update), munit_void_test(test_nghttp2_hd_ringbuf_reserve), munit_void_test(test_nghttp2_hd_change_table_size), munit_void_test(test_nghttp2_hd_deflate_inflate), munit_void_test(test_nghttp2_hd_no_index), munit_void_test(test_nghttp2_hd_deflate_bound), munit_void_test(test_nghttp2_hd_public_api), munit_void_test(test_nghttp2_hd_deflate_hd_vec), munit_void_test(test_nghttp2_hd_decode_length), munit_void_test(test_nghttp2_hd_huff_encode), munit_void_test(test_nghttp2_hd_huff_decode), munit_test_end(), }; const MunitSuite hd_suite = { "/hd", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE, }; void test_nghttp2_hd_deflate(void) { nghttp2_hd_deflater deflater; nghttp2_hd_inflater inflater; nghttp2_nv nva1[] = {MAKE_NV(":path", "/my-example/index.html"), MAKE_NV(":scheme", "https"), MAKE_NV("hello", "world")}; nghttp2_nv nva2[] = {MAKE_NV(":path", "/script.js"), MAKE_NV(":scheme", "https")}; nghttp2_nv nva3[] = {MAKE_NV("cookie", "k1=v1"), MAKE_NV("cookie", "k2=v2"), MAKE_NV("via", "proxy")}; nghttp2_nv nva4[] = {MAKE_NV(":path", "/style.css"), MAKE_NV("cookie", "k1=v1"), MAKE_NV("cookie", "k1=v1")}; nghttp2_nv nva5[] = {MAKE_NV(":path", "/style.css"), MAKE_NV("x-nghttp2", "")}; nghttp2_bufs bufs; nghttp2_ssize blocklen; nva_out out; int rv; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nva_out_init(&out); assert_int(0, ==, nghttp2_hd_deflate_init(&deflater, mem)); assert_int(0, ==, nghttp2_hd_inflate_init(&inflater, mem)); rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva1, ARRLEN(nva1)); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_int(0, ==, rv); assert_ptrdiff(0, <, blocklen); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(3, ==, out.nvlen); assert_nv_equal(nva1, out.nva, 3, mem); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); /* Second headers */ rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva2, ARRLEN(nva2)); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_int(0, ==, rv); assert_ptrdiff(0, <, blocklen); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(2, ==, out.nvlen); assert_nv_equal(nva2, out.nva, 2, mem); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); /* Third headers, including same header field name, but value is not the same. */ rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva3, ARRLEN(nva3)); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_int(0, ==, rv); assert_ptrdiff(0, <, blocklen); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(3, ==, out.nvlen); assert_nv_equal(nva3, out.nva, 3, mem); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); /* Fourth headers, including duplicate header fields. */ rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva4, ARRLEN(nva4)); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_int(0, ==, rv); assert_ptrdiff(0, <, blocklen); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(3, ==, out.nvlen); assert_nv_equal(nva4, out.nva, 3, mem); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); /* Fifth headers includes empty value */ rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva5, ARRLEN(nva5)); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_int(0, ==, rv); assert_ptrdiff(0, <, blocklen); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(2, ==, out.nvlen); assert_nv_equal(nva5, out.nva, 2, mem); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); /* Cleanup */ nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); } void test_nghttp2_hd_deflate_same_indexed_repr(void) { nghttp2_hd_deflater deflater; nghttp2_hd_inflater inflater; nghttp2_nv nva1[] = {MAKE_NV("host", "alpha"), MAKE_NV("host", "alpha")}; nghttp2_nv nva2[] = {MAKE_NV("host", "alpha"), MAKE_NV("host", "alpha"), MAKE_NV("host", "alpha")}; nghttp2_bufs bufs; nghttp2_ssize blocklen; nva_out out; int rv; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nva_out_init(&out); assert_int(0, ==, nghttp2_hd_deflate_init(&deflater, mem)); assert_int(0, ==, nghttp2_hd_inflate_init(&inflater, mem)); /* Encode 2 same headers. Emit 1 literal reprs and 1 index repr. */ rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva1, ARRLEN(nva1)); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_int(0, ==, rv); assert_ptrdiff(0, <, blocklen); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(2, ==, out.nvlen); assert_nv_equal(nva1, out.nva, 2, mem); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); /* Encode 3 same headers. This time, emits 3 index reprs. */ rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva2, ARRLEN(nva2)); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_int(0, ==, rv); assert_ptrdiff(3, ==, blocklen); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(3, ==, out.nvlen); assert_nv_equal(nva2, out.nva, 3, mem); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); /* Cleanup */ nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); } void test_nghttp2_hd_inflate_indexed(void) { nghttp2_hd_inflater inflater; nghttp2_bufs bufs; nghttp2_ssize blocklen; nghttp2_nv nv = MAKE_NV(":path", "/"); nva_out out; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nva_out_init(&out); nghttp2_hd_inflate_init(&inflater, mem); nghttp2_bufs_addb(&bufs, (1 << 7) | 4); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_ptrdiff(1, ==, blocklen); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(1, ==, out.nvlen); assert_nv_equal(&nv, out.nva, 1, mem); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); /* index = 0 is error */ nghttp2_bufs_addb(&bufs, 1 << 7); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_ptrdiff(1, ==, blocklen); assert_ptrdiff(NGHTTP2_ERR_HEADER_COMP, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); } void test_nghttp2_hd_inflate_indname_noinc(void) { nghttp2_hd_inflater inflater; nghttp2_bufs bufs; nghttp2_ssize blocklen; nghttp2_nv nv[] = {/* Huffman */ MAKE_NV("user-agent", "nghttp2"), /* Expecting no huffman */ MAKE_NV("user-agent", "x")}; size_t i; nva_out out; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nva_out_init(&out); nghttp2_hd_inflate_init(&inflater, mem); for (i = 0; i < ARRLEN(nv); ++i) { assert_int(0, ==, nghttp2_hd_emit_indname_block(&bufs, 57, &nv[i], NGHTTP2_HD_WITHOUT_INDEXING)); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_ptrdiff(0, <, blocklen); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(1, ==, out.nvlen); assert_nv_equal(&nv[i], out.nva, 1, mem); assert_size(0, ==, inflater.ctx.hd_table.len); assert_size(61, ==, nghttp2_hd_inflate_get_num_table_entries(&inflater)); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); } nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); } void test_nghttp2_hd_inflate_indname_inc(void) { nghttp2_hd_inflater inflater; nghttp2_bufs bufs; nghttp2_ssize blocklen; nghttp2_nv nv = MAKE_NV("user-agent", "nghttp2"); nva_out out; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nva_out_init(&out); nghttp2_hd_inflate_init(&inflater, mem); assert_int( 0, ==, nghttp2_hd_emit_indname_block(&bufs, 57, &nv, NGHTTP2_HD_WITH_INDEXING)); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_ptrdiff(0, <, blocklen); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(1, ==, out.nvlen); assert_nv_equal(&nv, out.nva, 1, mem); assert_size(1, ==, inflater.ctx.hd_table.len); assert_size(62, ==, nghttp2_hd_inflate_get_num_table_entries(&inflater)); assert_nv_equal( &nv, nghttp2_hd_inflate_get_table_entry(&inflater, NGHTTP2_STATIC_TABLE_LENGTH + inflater.ctx.hd_table.len), 1, mem); nva_out_reset(&out, mem); nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); } void test_nghttp2_hd_inflate_indname_inc_eviction(void) { nghttp2_hd_inflater inflater; nghttp2_bufs bufs; nghttp2_ssize blocklen; uint8_t value[1025]; nva_out out; nghttp2_nv nv; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nva_out_init(&out); nghttp2_hd_inflate_init(&inflater, mem); memset(value, '0', sizeof(value)); value[sizeof(value) - 1] = '\0'; nv.value = value; nv.valuelen = sizeof(value) - 1; nv.flags = NGHTTP2_NV_FLAG_NONE; assert_int( 0, ==, nghttp2_hd_emit_indname_block(&bufs, 14, &nv, NGHTTP2_HD_WITH_INDEXING)); assert_int( 0, ==, nghttp2_hd_emit_indname_block(&bufs, 15, &nv, NGHTTP2_HD_WITH_INDEXING)); assert_int( 0, ==, nghttp2_hd_emit_indname_block(&bufs, 16, &nv, NGHTTP2_HD_WITH_INDEXING)); assert_int( 0, ==, nghttp2_hd_emit_indname_block(&bufs, 17, &nv, NGHTTP2_HD_WITH_INDEXING)); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_ptrdiff(0, <, blocklen); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(4, ==, out.nvlen); assert_size(14, ==, out.nva[0].namelen); assert_memory_equal(out.nva[0].namelen, "accept-charset", out.nva[0].name); assert_size(sizeof(value) - 1, ==, out.nva[0].valuelen); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); assert_size(3, ==, inflater.ctx.hd_table.len); assert_size(64, ==, nghttp2_hd_inflate_get_num_table_entries(&inflater)); nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); } void test_nghttp2_hd_inflate_newname_noinc(void) { nghttp2_hd_inflater inflater; nghttp2_bufs bufs; nghttp2_ssize blocklen; nghttp2_nv nv[] = {/* Expecting huffman for both */ MAKE_NV("my-long-content-length", "nghttp2"), /* Expecting no huffman for both */ MAKE_NV("x", "y"), /* Huffman for key only */ MAKE_NV("my-long-content-length", "y"), /* Huffman for value only */ MAKE_NV("x", "nghttp2")}; size_t i; nva_out out; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nva_out_init(&out); nghttp2_hd_inflate_init(&inflater, mem); for (i = 0; i < ARRLEN(nv); ++i) { assert_int(0, ==, nghttp2_hd_emit_newname_block(&bufs, &nv[i], NGHTTP2_HD_WITHOUT_INDEXING)); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_ptrdiff(0, <, blocklen); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(1, ==, out.nvlen); assert_nv_equal(&nv[i], out.nva, 1, mem); assert_size(0, ==, inflater.ctx.hd_table.len); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); } nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); } void test_nghttp2_hd_inflate_newname_inc(void) { nghttp2_hd_inflater inflater; nghttp2_bufs bufs; nghttp2_ssize blocklen; nghttp2_nv nv = MAKE_NV("x-rel", "nghttp2"); nva_out out; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nva_out_init(&out); nghttp2_hd_inflate_init(&inflater, mem); assert_int( 0, ==, nghttp2_hd_emit_newname_block(&bufs, &nv, NGHTTP2_HD_WITH_INDEXING)); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_ptrdiff(0, <, blocklen); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(1, ==, out.nvlen); assert_nv_equal(&nv, out.nva, 1, mem); assert_size(1, ==, inflater.ctx.hd_table.len); assert_nv_equal( &nv, nghttp2_hd_inflate_get_table_entry(&inflater, NGHTTP2_STATIC_TABLE_LENGTH + inflater.ctx.hd_table.len), 1, mem); nva_out_reset(&out, mem); nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); } void test_nghttp2_hd_inflate_clearall_inc(void) { nghttp2_hd_inflater inflater; nghttp2_bufs bufs; nghttp2_ssize blocklen; nghttp2_nv nv; uint8_t value[4061]; nva_out out; nghttp2_mem *mem; mem = nghttp2_mem_default(); bufs_large_init(&bufs, 8192); nva_out_init(&out); /* Total 4097 bytes space required to hold this entry */ nv.name = (uint8_t *)"alpha"; nv.namelen = strlen((char *)nv.name); memset(value, '0', sizeof(value)); value[sizeof(value) - 1] = '\0'; nv.value = value; nv.valuelen = sizeof(value) - 1; nv.flags = NGHTTP2_NV_FLAG_NONE; nghttp2_hd_inflate_init(&inflater, mem); assert_int( 0, ==, nghttp2_hd_emit_newname_block(&bufs, &nv, NGHTTP2_HD_WITH_INDEXING)); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_ptrdiff(0, <, blocklen); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(1, ==, out.nvlen); assert_nv_equal(&nv, out.nva, 1, mem); assert_size(0, ==, inflater.ctx.hd_table.len); nva_out_reset(&out, mem); /* Do it again */ assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(1, ==, out.nvlen); assert_nv_equal(&nv, out.nva, 1, mem); assert_size(0, ==, inflater.ctx.hd_table.len); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); /* This time, 4096 bytes space required, which is just fits in the header table */ nv.valuelen = sizeof(value) - 2; assert_int( 0, ==, nghttp2_hd_emit_newname_block(&bufs, &nv, NGHTTP2_HD_WITH_INDEXING)); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_ptrdiff(0, <, blocklen); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(1, ==, out.nvlen); assert_nv_equal(&nv, out.nva, 1, mem); assert_size(1, ==, inflater.ctx.hd_table.len); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); } void test_nghttp2_hd_inflate_zero_length_huffman(void) { nghttp2_hd_inflater inflater; nghttp2_bufs bufs; /* Literal header without indexing - new name */ uint8_t data[] = {0x40, 0x01, 0x78 /* 'x' */, 0x80}; nva_out out; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nva_out_init(&out); nghttp2_bufs_add(&bufs, data, sizeof(data)); /* /\* Literal header without indexing - new name *\/ */ /* ptr[0] = 0x40; */ /* ptr[1] = 1; */ /* ptr[2] = 'x'; */ /* ptr[3] = 0x80; */ nghttp2_hd_inflate_init(&inflater, mem); assert_ptrdiff(4, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(1, ==, out.nvlen); assert_size(1, ==, out.nva[0].namelen); assert_uint8('x', ==, out.nva[0].name[0]); assert_null(out.nva[0].value); assert_size(0, ==, out.nva[0].valuelen); nva_out_reset(&out, mem); nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); } void test_nghttp2_hd_inflate_expect_table_size_update(void) { nghttp2_hd_inflater inflater; nghttp2_bufs bufs; nghttp2_mem *mem; /* Indexed Header: :method: GET */ uint8_t data[] = {0x82}; nva_out out; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nva_out_init(&out); nghttp2_bufs_add(&bufs, data, sizeof(data)); nghttp2_hd_inflate_init(&inflater, mem); /* This will make inflater require table size update in the next inflation. */ nghttp2_hd_inflate_change_table_size(&inflater, 4095); nghttp2_hd_inflate_change_table_size(&inflater, 4096); assert_ptrdiff(NGHTTP2_ERR_HEADER_COMP, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); nva_out_reset(&out, mem); nghttp2_hd_inflate_free(&inflater); /* This does not require for encoder to emit table size update since * size is not changed. */ nghttp2_hd_inflate_init(&inflater, mem); nghttp2_hd_inflate_change_table_size(&inflater, 4096); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); nva_out_reset(&out, mem); nghttp2_hd_inflate_free(&inflater); /* This does not require for encodre to emit table size update since new size is larger than current size. */ nghttp2_hd_inflate_init(&inflater, mem); nghttp2_hd_inflate_change_table_size(&inflater, 4097); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); nva_out_reset(&out, mem); nghttp2_hd_inflate_free(&inflater); /* Received table size is strictly larger than minimum table size */ nghttp2_hd_inflate_init(&inflater, mem); nghttp2_hd_inflate_change_table_size(&inflater, 111); nghttp2_hd_inflate_change_table_size(&inflater, 4096); nghttp2_bufs_reset(&bufs); nghttp2_hd_emit_table_size(&bufs, 112); assert_ptrdiff(NGHTTP2_ERR_HEADER_COMP, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); nva_out_reset(&out, mem); nghttp2_hd_inflate_free(&inflater); /* Receiving 2 table size updates, min and last value */ nghttp2_hd_inflate_init(&inflater, mem); nghttp2_hd_inflate_change_table_size(&inflater, 111); nghttp2_hd_inflate_change_table_size(&inflater, 4096); nghttp2_bufs_reset(&bufs); nghttp2_hd_emit_table_size(&bufs, 111); nghttp2_hd_emit_table_size(&bufs, 4096); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); nva_out_reset(&out, mem); nghttp2_hd_inflate_free(&inflater); /* 2nd update is larger than last value */ nghttp2_hd_inflate_init(&inflater, mem); nghttp2_hd_inflate_change_table_size(&inflater, 111); nghttp2_hd_inflate_change_table_size(&inflater, 4095); nghttp2_bufs_reset(&bufs); nghttp2_hd_emit_table_size(&bufs, 111); nghttp2_hd_emit_table_size(&bufs, 4096); assert_ptrdiff(NGHTTP2_ERR_HEADER_COMP, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); nva_out_reset(&out, mem); nghttp2_hd_inflate_free(&inflater); nghttp2_bufs_free(&bufs); } void test_nghttp2_hd_inflate_unexpected_table_size_update(void) { nghttp2_hd_inflater inflater; nghttp2_bufs bufs; nghttp2_mem *mem; /* Indexed Header: :method: GET, followed by table size update. This violates RFC 7541. */ uint8_t data[] = {0x82, 0x20}; nva_out out; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nva_out_init(&out); nghttp2_bufs_add(&bufs, data, sizeof(data)); nghttp2_hd_inflate_init(&inflater, mem); assert_ptrdiff(NGHTTP2_ERR_HEADER_COMP, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); nva_out_reset(&out, mem); nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); } void test_nghttp2_hd_ringbuf_reserve(void) { nghttp2_hd_deflater deflater; nghttp2_hd_inflater inflater; nghttp2_nv nv; nghttp2_bufs bufs; nva_out out; int i; nghttp2_ssize rv; nghttp2_ssize blocklen; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nva_out_init(&out); nv.flags = NGHTTP2_NV_FLAG_NONE; nv.name = (uint8_t *)"a"; nv.namelen = strlen((const char *)nv.name); nv.valuelen = 4; nv.value = mem->malloc(nv.valuelen + 1, NULL); memset(nv.value, 0, nv.valuelen); nghttp2_hd_deflate_init2(&deflater, 8000, mem); nghttp2_hd_inflate_init(&inflater, mem); nghttp2_hd_inflate_change_table_size(&inflater, 8000); nghttp2_hd_deflate_change_table_size(&deflater, 8000); for (i = 0; i < 150; ++i) { memcpy(nv.value, &i, sizeof(i)); rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, &nv, 1); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_ptrdiff(0, ==, rv); assert_ptrdiff(0, <, blocklen); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(1, ==, out.nvlen); assert_nv_equal(&nv, out.nva, 1, mem); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); } nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); mem->free(nv.value, NULL); } void test_nghttp2_hd_change_table_size(void) { nghttp2_hd_deflater deflater; nghttp2_hd_inflater inflater; nghttp2_nv nva[] = {MAKE_NV("alpha", "bravo"), MAKE_NV("charlie", "delta")}; nghttp2_nv nva2[] = {MAKE_NV(":path", "/")}; nghttp2_bufs bufs; int rv; nva_out out; nghttp2_ssize blocklen; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nva_out_init(&out); nghttp2_hd_deflate_init(&deflater, mem); nghttp2_hd_inflate_init(&inflater, mem); /* inflater changes notifies 8000 max header table size */ assert_int(0, ==, nghttp2_hd_inflate_change_table_size(&inflater, 8000)); assert_int(0, ==, nghttp2_hd_deflate_change_table_size(&deflater, 8000)); assert_size(4096, ==, deflater.ctx.hd_table_bufsize_max); assert_size(4096, ==, inflater.ctx.hd_table_bufsize_max); assert_size(8000, ==, inflater.settings_hd_table_bufsize_max); /* This will emit encoding context update with header table size 4096 */ rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_int(0, ==, rv); assert_ptrdiff(0, <, blocklen); assert_size(2, ==, deflater.ctx.hd_table.len); assert_size(63, ==, nghttp2_hd_deflate_get_num_table_entries(&deflater)); assert_size(4096, ==, deflater.ctx.hd_table_bufsize_max); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(2, ==, inflater.ctx.hd_table.len); assert_size(63, ==, nghttp2_hd_inflate_get_num_table_entries(&inflater)); assert_size(4096, ==, inflater.ctx.hd_table_bufsize_max); assert_size(8000, ==, inflater.settings_hd_table_bufsize_max); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); /* inflater changes header table size to 1024 */ assert_int(0, ==, nghttp2_hd_inflate_change_table_size(&inflater, 1024)); assert_int(0, ==, nghttp2_hd_deflate_change_table_size(&deflater, 1024)); assert_size(1024, ==, deflater.ctx.hd_table_bufsize_max); assert_size(1024, ==, inflater.ctx.hd_table_bufsize_max); assert_size(1024, ==, inflater.settings_hd_table_bufsize_max); rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_int(0, ==, rv); assert_ptrdiff(0, <, blocklen); assert_size(2, ==, deflater.ctx.hd_table.len); assert_size(63, ==, nghttp2_hd_deflate_get_num_table_entries(&deflater)); assert_size(1024, ==, deflater.ctx.hd_table_bufsize_max); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(2, ==, inflater.ctx.hd_table.len); assert_size(63, ==, nghttp2_hd_inflate_get_num_table_entries(&inflater)); assert_size(1024, ==, inflater.ctx.hd_table_bufsize_max); assert_size(1024, ==, inflater.settings_hd_table_bufsize_max); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); /* inflater changes header table size to 0 */ assert_int(0, ==, nghttp2_hd_inflate_change_table_size(&inflater, 0)); assert_int(0, ==, nghttp2_hd_deflate_change_table_size(&deflater, 0)); assert_size(0, ==, deflater.ctx.hd_table.len); assert_size(61, ==, nghttp2_hd_deflate_get_num_table_entries(&deflater)); assert_size(0, ==, deflater.ctx.hd_table_bufsize_max); assert_size(0, ==, inflater.ctx.hd_table.len); assert_size(61, ==, nghttp2_hd_inflate_get_num_table_entries(&inflater)); assert_size(0, ==, inflater.ctx.hd_table_bufsize_max); assert_size(0, ==, inflater.settings_hd_table_bufsize_max); rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_int(0, ==, rv); assert_ptrdiff(0, <, blocklen); assert_size(0, ==, deflater.ctx.hd_table.len); assert_size(61, ==, nghttp2_hd_deflate_get_num_table_entries(&deflater)); assert_size(0, ==, deflater.ctx.hd_table_bufsize_max); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(0, ==, inflater.ctx.hd_table.len); assert_size(61, ==, nghttp2_hd_inflate_get_num_table_entries(&inflater)); assert_size(0, ==, inflater.ctx.hd_table_bufsize_max); assert_size(0, ==, inflater.settings_hd_table_bufsize_max); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); /* Check table buffer is expanded */ frame_pack_bufs_init(&bufs); nghttp2_hd_deflate_init2(&deflater, 8192, mem); nghttp2_hd_inflate_init(&inflater, mem); /* First inflater changes header table size to 8000 */ assert_int(0, ==, nghttp2_hd_inflate_change_table_size(&inflater, 8000)); assert_int(0, ==, nghttp2_hd_deflate_change_table_size(&deflater, 8000)); assert_size(8000, ==, deflater.ctx.hd_table_bufsize_max); assert_size(8000, ==, nghttp2_hd_deflate_get_max_dynamic_table_size(&deflater)); assert_size(4096, ==, inflater.ctx.hd_table_bufsize_max); assert_size(4096, ==, nghttp2_hd_inflate_get_max_dynamic_table_size(&inflater)); assert_size(8000, ==, inflater.settings_hd_table_bufsize_max); rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_int(0, ==, rv); assert_ptrdiff(0, <, blocklen); assert_size(2, ==, deflater.ctx.hd_table.len); assert_size(8000, ==, deflater.ctx.hd_table_bufsize_max); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(2, ==, inflater.ctx.hd_table.len); assert_size(8000, ==, inflater.ctx.hd_table_bufsize_max); assert_size(8000, ==, inflater.settings_hd_table_bufsize_max); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); assert_int(0, ==, nghttp2_hd_inflate_change_table_size(&inflater, 16383)); assert_int(0, ==, nghttp2_hd_deflate_change_table_size(&deflater, 16383)); assert_size(8192, ==, deflater.ctx.hd_table_bufsize_max); assert_size(8192, ==, nghttp2_hd_deflate_get_max_dynamic_table_size(&deflater)); assert_size(8000, ==, inflater.ctx.hd_table_bufsize_max); assert_size(8000, ==, nghttp2_hd_inflate_get_max_dynamic_table_size(&inflater)); assert_size(16383, ==, inflater.settings_hd_table_bufsize_max); rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_int(0, ==, rv); assert_ptrdiff(0, <, blocklen); assert_size(2, ==, deflater.ctx.hd_table.len); assert_size(8192, ==, deflater.ctx.hd_table_bufsize_max); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(2, ==, inflater.ctx.hd_table.len); assert_size(8192, ==, inflater.ctx.hd_table_bufsize_max); assert_size(16383, ==, inflater.settings_hd_table_bufsize_max); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); /* Lastly, check the error condition */ rv = nghttp2_hd_emit_table_size(&bufs, 25600); assert_int(0, ==, rv); assert_ptrdiff(NGHTTP2_ERR_HEADER_COMP, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); /* Check that encoder can handle the case where its allowable buffer size is less than default size, 4096 */ nghttp2_hd_deflate_init2(&deflater, 1024, mem); nghttp2_hd_inflate_init(&inflater, mem); assert_size(1024, ==, deflater.ctx.hd_table_bufsize_max); /* This emits context update with buffer size 1024 */ rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_int(0, ==, rv); assert_ptrdiff(0, <, blocklen); assert_size(2, ==, deflater.ctx.hd_table.len); assert_size(1024, ==, deflater.ctx.hd_table_bufsize_max); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(2, ==, inflater.ctx.hd_table.len); assert_size(1024, ==, inflater.ctx.hd_table_bufsize_max); assert_size(4096, ==, inflater.settings_hd_table_bufsize_max); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); /* Check that table size UINT32_MAX can be received */ nghttp2_hd_deflate_init2(&deflater, UINT32_MAX, mem); nghttp2_hd_inflate_init(&inflater, mem); assert_int(0, ==, nghttp2_hd_inflate_change_table_size(&inflater, UINT32_MAX)); assert_int(0, ==, nghttp2_hd_deflate_change_table_size(&deflater, UINT32_MAX)); rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_int(0, ==, rv); assert_size(UINT32_MAX, ==, deflater.ctx.hd_table_bufsize_max); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(UINT32_MAX, ==, inflater.ctx.hd_table_bufsize_max); assert_size(UINT32_MAX, ==, inflater.settings_hd_table_bufsize_max); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); /* Check that context update emitted twice */ nghttp2_hd_deflate_init2(&deflater, 4096, mem); nghttp2_hd_inflate_init(&inflater, mem); assert_int(0, ==, nghttp2_hd_inflate_change_table_size(&inflater, 0)); assert_int(0, ==, nghttp2_hd_inflate_change_table_size(&inflater, 3000)); assert_int(0, ==, nghttp2_hd_deflate_change_table_size(&deflater, 0)); assert_int(0, ==, nghttp2_hd_deflate_change_table_size(&deflater, 3000)); assert_size(0, ==, deflater.min_hd_table_bufsize_max); assert_size(3000, ==, deflater.ctx.hd_table_bufsize_max); rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva2, 1); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_int(0, ==, rv); assert_ptrdiff(3, <, blocklen); assert_size(3000, ==, deflater.ctx.hd_table_bufsize_max); assert_size(UINT32_MAX, ==, deflater.min_hd_table_bufsize_max); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(3000, ==, inflater.ctx.hd_table_bufsize_max); assert_size(3000, ==, inflater.settings_hd_table_bufsize_max); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); nghttp2_bufs_free(&bufs); } static void check_deflate_inflate(nghttp2_hd_deflater *deflater, nghttp2_hd_inflater *inflater, nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem) { nghttp2_bufs bufs; nghttp2_ssize blocklen; nva_out out; int rv; frame_pack_bufs_init(&bufs); nva_out_init(&out); rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, nva, nvlen); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_int(0, ==, rv); assert_ptrdiff(0, <=, blocklen); assert_ptrdiff(blocklen, ==, inflate_hd(inflater, &out, &bufs, 0, mem)); assert_size(nvlen, ==, out.nvlen); assert_nv_equal(nva, out.nva, nvlen, mem); nva_out_reset(&out, mem); nghttp2_bufs_free(&bufs); } void test_nghttp2_hd_deflate_inflate(void) { nghttp2_hd_deflater deflater; nghttp2_hd_inflater inflater; nghttp2_nv nv1[] = { MAKE_NV(":status", "200 OK"), MAKE_NV("access-control-allow-origin", "*"), MAKE_NV("cache-control", "private, max-age=0, must-revalidate"), MAKE_NV("content-length", "76073"), MAKE_NV("content-type", "text/html"), MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), MAKE_NV("expires", "Sat, 27 Jul 2013 06:22:12 GMT"), MAKE_NV("server", "Apache"), MAKE_NV("vary", "foobar"), MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), MAKE_NV("x-cache", "MISS from alphabravo"), MAKE_NV("x-cache-action", "MISS"), MAKE_NV("x-cache-age", "0"), MAKE_NV("x-cache-lookup", "MISS from alphabravo:3128"), MAKE_NV("x-lb-nocache", "true"), }; nghttp2_nv nv2[] = { MAKE_NV(":status", "304 Not Modified"), MAKE_NV("age", "0"), MAKE_NV("cache-control", "max-age=56682045"), MAKE_NV("content-type", "text/css"), MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), MAKE_NV("expires", "Thu, 14 May 2015 07:22:57 GMT"), MAKE_NV("last-modified", "Tue, 14 May 2013 07:22:15 GMT"), MAKE_NV("vary", "Accept-Encoding"), MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), MAKE_NV("x-cache", "HIT from alphabravo"), MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128")}; nghttp2_nv nv3[] = { MAKE_NV(":status", "304 Not Modified"), MAKE_NV("age", "0"), MAKE_NV("cache-control", "max-age=56682072"), MAKE_NV("content-type", "text/css"), MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), MAKE_NV("expires", "Thu, 14 May 2015 07:23:24 GMT"), MAKE_NV("last-modified", "Tue, 14 May 2013 07:22:13 GMT"), MAKE_NV("vary", "Accept-Encoding"), MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), MAKE_NV("x-cache", "HIT from alphabravo"), MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), }; nghttp2_nv nv4[] = { MAKE_NV(":status", "304 Not Modified"), MAKE_NV("age", "0"), MAKE_NV("cache-control", "max-age=56682022"), MAKE_NV("content-type", "text/css"), MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), MAKE_NV("expires", "Thu, 14 May 2015 07:22:34 GMT"), MAKE_NV("last-modified", "Tue, 14 May 2013 07:22:14 GMT"), MAKE_NV("vary", "Accept-Encoding"), MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), MAKE_NV("x-cache", "HIT from alphabravo"), MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), }; nghttp2_nv nv5[] = { MAKE_NV(":status", "304 Not Modified"), MAKE_NV("age", "0"), MAKE_NV("cache-control", "max-age=4461139"), MAKE_NV("content-type", "application/x-javascript"), MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), MAKE_NV("expires", "Mon, 16 Sep 2013 21:34:31 GMT"), MAKE_NV("last-modified", "Thu, 05 May 2011 09:15:59 GMT"), MAKE_NV("vary", "Accept-Encoding"), MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), MAKE_NV("x-cache", "HIT from alphabravo"), MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), }; nghttp2_nv nv6[] = { MAKE_NV(":status", "304 Not Modified"), MAKE_NV("age", "0"), MAKE_NV("cache-control", "max-age=18645951"), MAKE_NV("content-type", "application/x-javascript"), MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), MAKE_NV("expires", "Fri, 28 Feb 2014 01:48:03 GMT"), MAKE_NV("last-modified", "Tue, 12 Jul 2011 16:02:59 GMT"), MAKE_NV("vary", "Accept-Encoding"), MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), MAKE_NV("x-cache", "HIT from alphabravo"), MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), }; nghttp2_nv nv7[] = { MAKE_NV(":status", "304 Not Modified"), MAKE_NV("age", "0"), MAKE_NV("cache-control", "max-age=31536000"), MAKE_NV("content-type", "application/javascript"), MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), MAKE_NV("etag", "\"6807-4dc5b54e0dcc0\""), MAKE_NV("expires", "Wed, 21 May 2014 08:32:17 GMT"), MAKE_NV("last-modified", "Fri, 10 May 2013 11:18:51 GMT"), MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), MAKE_NV("x-cache", "HIT from alphabravo"), MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), }; nghttp2_nv nv8[] = { MAKE_NV(":status", "304 Not Modified"), MAKE_NV("age", "0"), MAKE_NV("cache-control", "max-age=31536000"), MAKE_NV("content-type", "application/javascript"), MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), MAKE_NV("etag", "\"41c6-4de7d28585b00\""), MAKE_NV("expires", "Thu, 12 Jun 2014 10:00:58 GMT"), MAKE_NV("last-modified", "Thu, 06 Jun 2013 14:30:36 GMT"), MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), MAKE_NV("x-cache", "HIT from alphabravo"), MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), }; nghttp2_nv nv9[] = { MAKE_NV(":status", "304 Not Modified"), MAKE_NV("age", "0"), MAKE_NV("cache-control", "max-age=31536000"), MAKE_NV("content-type", "application/javascript"), MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), MAKE_NV("etag", "\"19d6e-4dc5b35a541c0\""), MAKE_NV("expires", "Wed, 21 May 2014 08:32:18 GMT"), MAKE_NV("last-modified", "Fri, 10 May 2013 11:10:07 GMT"), MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), MAKE_NV("x-cache", "HIT from alphabravo"), MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), }; nghttp2_nv nv10[] = { MAKE_NV(":status", "304 Not Modified"), MAKE_NV("age", "0"), MAKE_NV("cache-control", "max-age=56682045"), MAKE_NV("content-type", "text/css"), MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), MAKE_NV("expires", "Thu, 14 May 2015 07:22:57 GMT"), MAKE_NV("last-modified", "Tue, 14 May 2013 07:21:53 GMT"), MAKE_NV("vary", "Accept-Encoding"), MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), MAKE_NV("x-cache", "HIT from alphabravo"), MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), }; nghttp2_mem *mem; mem = nghttp2_mem_default(); nghttp2_hd_deflate_init(&deflater, mem); nghttp2_hd_inflate_init(&inflater, mem); check_deflate_inflate(&deflater, &inflater, nv1, ARRLEN(nv1), mem); check_deflate_inflate(&deflater, &inflater, nv2, ARRLEN(nv2), mem); check_deflate_inflate(&deflater, &inflater, nv3, ARRLEN(nv3), mem); check_deflate_inflate(&deflater, &inflater, nv4, ARRLEN(nv4), mem); check_deflate_inflate(&deflater, &inflater, nv5, ARRLEN(nv5), mem); check_deflate_inflate(&deflater, &inflater, nv6, ARRLEN(nv6), mem); check_deflate_inflate(&deflater, &inflater, nv7, ARRLEN(nv7), mem); check_deflate_inflate(&deflater, &inflater, nv8, ARRLEN(nv8), mem); check_deflate_inflate(&deflater, &inflater, nv9, ARRLEN(nv9), mem); check_deflate_inflate(&deflater, &inflater, nv10, ARRLEN(nv10), mem); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); } void test_nghttp2_hd_no_index(void) { nghttp2_hd_deflater deflater; nghttp2_hd_inflater inflater; nghttp2_bufs bufs; nghttp2_ssize blocklen; nghttp2_nv nva[] = { MAKE_NV(":method", "GET"), MAKE_NV(":method", "POST"), MAKE_NV(":path", "/foo"), MAKE_NV("version", "HTTP/1.1"), MAKE_NV(":method", "GET"), }; size_t i; nva_out out; int rv; nghttp2_mem *mem; mem = nghttp2_mem_default(); /* 1st :method: GET can be indexable, last one is not */ for (i = 1; i < ARRLEN(nva); ++i) { nva[i].flags = NGHTTP2_NV_FLAG_NO_INDEX; } frame_pack_bufs_init(&bufs); nva_out_init(&out); nghttp2_hd_deflate_init(&deflater, mem); nghttp2_hd_inflate_init(&inflater, mem); rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, ARRLEN(nva)); blocklen = (nghttp2_ssize)nghttp2_bufs_len(&bufs); assert_int(0, ==, rv); assert_ptrdiff(0, <, blocklen); assert_ptrdiff(blocklen, ==, inflate_hd(&inflater, &out, &bufs, 0, mem)); assert_size(ARRLEN(nva), ==, out.nvlen); assert_nv_equal(nva, out.nva, ARRLEN(nva), mem); assert_uint8(NGHTTP2_NV_FLAG_NONE, ==, out.nva[0].flags); for (i = 1; i < ARRLEN(nva); ++i) { assert_uint8(NGHTTP2_NV_FLAG_NO_INDEX, ==, out.nva[i].flags); } nva_out_reset(&out, mem); nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); } void test_nghttp2_hd_deflate_bound(void) { nghttp2_hd_deflater deflater; nghttp2_nv nva[] = {MAKE_NV(":method", "GET"), MAKE_NV("alpha", "bravo")}; nghttp2_bufs bufs; size_t bound, bound2; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nghttp2_hd_deflate_init(&deflater, mem); bound = nghttp2_hd_deflate_bound(&deflater, nva, ARRLEN(nva)); assert_size(12 + 6 * 2 * 2 + nva[0].namelen + nva[0].valuelen + nva[1].namelen + nva[1].valuelen, ==, bound); nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, ARRLEN(nva)); assert_size((size_t)nghttp2_bufs_len(&bufs), <, bound); bound2 = nghttp2_hd_deflate_bound(&deflater, nva, ARRLEN(nva)); assert_size(bound, ==, bound2); nghttp2_bufs_free(&bufs); nghttp2_hd_deflate_free(&deflater); } void test_nghttp2_hd_public_api(void) { nghttp2_hd_deflater *deflater; nghttp2_hd_inflater *inflater; nghttp2_nv nva[] = {MAKE_NV("alpha", "bravo"), MAKE_NV("charlie", "delta")}; uint8_t buf[4096]; size_t buflen; nghttp2_ssize blocklen; nghttp2_bufs bufs; nghttp2_mem *mem; mem = nghttp2_mem_default(); assert_int(0, ==, nghttp2_hd_deflate_new(&deflater, 4096)); assert_int(0, ==, nghttp2_hd_inflate_new(&inflater)); buflen = nghttp2_hd_deflate_bound(deflater, nva, ARRLEN(nva)); blocklen = nghttp2_hd_deflate_hd2(deflater, buf, buflen, nva, ARRLEN(nva)); assert_ptrdiff(0, <, blocklen); nghttp2_bufs_wrap_init(&bufs, buf, (size_t)blocklen, mem); bufs.head->buf.last += blocklen; assert_ptrdiff(blocklen, ==, inflate_hd(inflater, NULL, &bufs, 0, mem)); nghttp2_bufs_wrap_free(&bufs); nghttp2_hd_inflate_del(inflater); nghttp2_hd_deflate_del(deflater); /* See NGHTTP2_ERR_INSUFF_BUFSIZE */ assert_int(0, ==, nghttp2_hd_deflate_new(&deflater, 4096)); blocklen = nghttp2_hd_deflate_hd2(deflater, buf, (size_t)(blocklen - 1), nva, ARRLEN(nva)); assert_ptrdiff(NGHTTP2_ERR_INSUFF_BUFSIZE, ==, blocklen); nghttp2_hd_deflate_del(deflater); } void test_nghttp2_hd_deflate_hd_vec(void) { nghttp2_hd_deflater *deflater; nghttp2_hd_inflater *inflater; nghttp2_nv nva[] = { MAKE_NV(":method", "PUT"), MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost:3000"), MAKE_NV(":path", "/usr/foo/alpha/bravo"), MAKE_NV("content-type", "image/png"), MAKE_NV("content-length", "1000000007"), }; uint8_t buf[4096]; nghttp2_ssize blocklen; nghttp2_mem *mem; nghttp2_vec vec[256]; size_t buflen; nghttp2_bufs bufs; nva_out out; size_t i; mem = nghttp2_mem_default(); nva_out_init(&out); nghttp2_hd_deflate_new(&deflater, 4096); nghttp2_hd_inflate_new(&inflater); buflen = nghttp2_hd_deflate_bound(deflater, nva, ARRLEN(nva)); vec[0].base = &buf[0]; vec[0].len = buflen / 2; vec[1].base = &buf[buflen / 2]; vec[1].len = buflen / 2; blocklen = nghttp2_hd_deflate_hd_vec2(deflater, vec, 2, nva, ARRLEN(nva)); assert_ptrdiff(0, <, blocklen); nghttp2_bufs_wrap_init(&bufs, buf, (size_t)blocklen, mem); bufs.head->buf.last += blocklen; assert_ptrdiff(blocklen, ==, inflate_hd(inflater, &out, &bufs, 0, mem)); assert_size(ARRLEN(nva), ==, out.nvlen); assert_nv_equal(nva, out.nva, ARRLEN(nva), mem); nghttp2_bufs_wrap_free(&bufs); nghttp2_hd_inflate_del(inflater); nghttp2_hd_deflate_del(deflater); nva_out_reset(&out, mem); /* check the case when veclen is 0 */ nghttp2_hd_deflate_new(&deflater, 4096); nghttp2_hd_inflate_new(&inflater); blocklen = nghttp2_hd_deflate_hd_vec2(deflater, NULL, 0, nva, ARRLEN(nva)); assert_ptrdiff(NGHTTP2_ERR_INSUFF_BUFSIZE, ==, blocklen); nghttp2_hd_inflate_del(inflater); nghttp2_hd_deflate_del(deflater); /* check the case when chunk length is 0 */ vec[0].base = NULL; vec[0].len = 0; vec[1].base = NULL; vec[1].len = 0; nghttp2_hd_deflate_new(&deflater, 4096); nghttp2_hd_inflate_new(&inflater); blocklen = nghttp2_hd_deflate_hd_vec2(deflater, vec, 2, nva, ARRLEN(nva)); assert_ptrdiff(NGHTTP2_ERR_INSUFF_BUFSIZE, ==, blocklen); nghttp2_hd_inflate_del(inflater); nghttp2_hd_deflate_del(deflater); /* check the case where chunk size differs in each chunk */ nghttp2_hd_deflate_new(&deflater, 4096); nghttp2_hd_inflate_new(&inflater); buflen = nghttp2_hd_deflate_bound(deflater, nva, ARRLEN(nva)); vec[0].base = &buf[0]; vec[0].len = buflen / 2; vec[1].base = &buf[buflen / 2]; vec[1].len = (buflen / 2) + 1; blocklen = nghttp2_hd_deflate_hd_vec2(deflater, vec, 2, nva, ARRLEN(nva)); assert_ptrdiff(0, <, blocklen); nghttp2_bufs_wrap_init(&bufs, buf, (size_t)blocklen, mem); bufs.head->buf.last += blocklen; assert_ptrdiff(blocklen, ==, inflate_hd(inflater, &out, &bufs, 0, mem)); assert_size(ARRLEN(nva), ==, out.nvlen); assert_nv_equal(nva, out.nva, ARRLEN(nva), mem); nghttp2_bufs_wrap_free(&bufs); nghttp2_hd_inflate_del(inflater); nghttp2_hd_deflate_del(deflater); nva_out_reset(&out, mem); /* check the case where chunk size is 1 */ nghttp2_hd_deflate_new(&deflater, 4096); nghttp2_hd_inflate_new(&inflater); buflen = nghttp2_hd_deflate_bound(deflater, nva, ARRLEN(nva)); assert(buflen <= ARRLEN(vec)); for (i = 0; i < buflen; ++i) { vec[i].base = &buf[i]; vec[i].len = 1; } blocklen = nghttp2_hd_deflate_hd_vec2(deflater, vec, buflen, nva, ARRLEN(nva)); assert_ptrdiff(0, <, blocklen); nghttp2_bufs_wrap_init(&bufs, buf, (size_t)blocklen, mem); bufs.head->buf.last += blocklen; assert_ptrdiff(blocklen, ==, inflate_hd(inflater, &out, &bufs, 0, mem)); assert_size(ARRLEN(nva), ==, out.nvlen); assert_nv_equal(nva, out.nva, ARRLEN(nva), mem); nghttp2_bufs_wrap_free(&bufs); nghttp2_hd_inflate_del(inflater); nghttp2_hd_deflate_del(deflater); nva_out_reset(&out, mem); } static size_t encode_length(uint8_t *buf, uint64_t n, size_t prefix) { size_t k = (size_t)((1 << prefix) - 1); size_t len = 0; *buf = (uint8_t)(*buf & ~k); if (n >= k) { *buf = (uint8_t)(*buf | k); ++buf; n -= k; ++len; } else { *buf = (uint8_t)(*buf | n); ++buf; return 1; } do { ++len; if (n >= 128) { *buf = (uint8_t)((1 << 7) | (n & 0x7f)); ++buf; n >>= 7; } else { *buf++ = (uint8_t)n; break; } } while (n); return len; } void test_nghttp2_hd_decode_length(void) { uint32_t out; size_t shift; int fin; uint8_t buf[16]; uint8_t *bufp; size_t len; nghttp2_ssize rv; size_t i; memset(buf, 0, sizeof(buf)); len = encode_length(buf, UINT32_MAX, 7); rv = nghttp2_hd_decode_length(&out, &shift, &fin, 0, 0, buf, buf + len, 7); assert_ptrdiff((nghttp2_ssize)len, ==, rv); assert_true(fin); assert_uint32(UINT32_MAX, ==, out); /* Make sure that we can decode integer if we feed 1 byte at a time */ out = 0; shift = 0; fin = 0; bufp = buf; for (i = 0; i < len; ++i, ++bufp) { rv = nghttp2_hd_decode_length(&out, &shift, &fin, out, shift, bufp, bufp + 1, 7); assert_ptrdiff(1, ==, rv); if (fin) { break; } } assert_size(len - 1, ==, i); assert_true(fin); assert_size(UINT32_MAX, ==, out); /* Check overflow case */ memset(buf, 0, sizeof(buf)); len = encode_length(buf, 1ll << 32, 7); rv = nghttp2_hd_decode_length(&out, &shift, &fin, 0, 0, buf, buf + len, 7); assert_ptrdiff(-1, ==, rv); /* Check the case that shift goes beyond 32 bits */ buf[0] = 255; buf[1] = 128; buf[2] = 128; buf[3] = 128; buf[4] = 128; buf[5] = 128; buf[6] = 1; rv = nghttp2_hd_decode_length(&out, &shift, &fin, 0, 0, buf, buf + 7, 8); assert_ptrdiff(-1, ==, rv); } void test_nghttp2_hd_huff_encode(void) { int rv; nghttp2_ssize len; nghttp2_buf outbuf; nghttp2_bufs bufs; nghttp2_hd_huff_decode_context ctx; const uint8_t t1[] = {22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; uint8_t b[256]; nghttp2_buf_wrap_init(&outbuf, b, sizeof(b)); frame_pack_bufs_init(&bufs); rv = nghttp2_hd_huff_encode(&bufs, t1, sizeof(t1)); assert_int(0, ==, rv); nghttp2_hd_huff_decode_context_init(&ctx); len = nghttp2_hd_huff_decode(&ctx, &outbuf, bufs.cur->buf.pos, nghttp2_bufs_len(&bufs), 1); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, len); assert_size(sizeof(t1), ==, nghttp2_buf_len(&outbuf)); assert_memory_equal(sizeof(t1), t1, outbuf.pos); nghttp2_bufs_free(&bufs); } void test_nghttp2_hd_huff_decode(void) { const uint8_t e[] = {0x1f, 0xff, 0xff, 0xff, 0xff, 0xff}; nghttp2_hd_huff_decode_context ctx; nghttp2_buf outbuf; uint8_t b[256]; nghttp2_ssize len; nghttp2_buf_wrap_init(&outbuf, b, sizeof(b)); nghttp2_hd_huff_decode_context_init(&ctx); len = nghttp2_hd_huff_decode(&ctx, &outbuf, e, 1, 1); assert_ptrdiff(1, ==, len); assert_memory_equal(1, "a", outbuf.pos); /* Premature sequence must elicit decoding error */ nghttp2_buf_wrap_init(&outbuf, b, sizeof(b)); nghttp2_hd_huff_decode_context_init(&ctx); len = nghttp2_hd_huff_decode(&ctx, &outbuf, e, 2, 1); assert_ptrdiff(NGHTTP2_ERR_HEADER_COMP, ==, len); /* Fully decoding EOS is error */ nghttp2_buf_wrap_init(&outbuf, b, sizeof(b)); nghttp2_hd_huff_decode_context_init(&ctx); len = nghttp2_hd_huff_decode(&ctx, &outbuf, e, 2, 6); assert_ptrdiff(NGHTTP2_ERR_HEADER_COMP, ==, len); /* Check failure state */ nghttp2_buf_wrap_init(&outbuf, b, sizeof(b)); nghttp2_hd_huff_decode_context_init(&ctx); len = nghttp2_hd_huff_decode(&ctx, &outbuf, e, 5, 0); assert_ptrdiff(5, ==, len); assert_true(nghttp2_hd_huff_decode_failure_state(&ctx)); } nghttp2-1.68.0/tests/PaxHeaders/nghttp2_alpn_test.h0000644000000000000000000000013215077107271017233 xustar0030 mtime=1761382073.006444079 30 atime=1761382080.107411476 30 ctime=1761382108.078303335 nghttp2-1.68.0/tests/nghttp2_alpn_test.h0000644000175100017510000000264715077107271017634 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Twist Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_ALPN_TEST_H #define NGHTTP2_ALPN_TEST_H #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" extern const MunitSuite alpn_suite; munit_void_test_decl(test_nghttp2_alpn) #endif /* NGHTTP2_ALPN_TEST_H */ nghttp2-1.68.0/tests/PaxHeaders/nghttp2_ratelim_test.c0000644000000000000000000000013115077107271017730 xustar0030 mtime=1761382073.007444074 30 atime=1761382080.107411476 29 ctime=1761382108.10430326 nghttp2-1.68.0/tests/nghttp2_ratelim_test.c0000644000175100017510000000604415077107271020325 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2023 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_ratelim_test.h" #include #include "munit.h" #include "nghttp2_ratelim.h" static const MunitTest tests[] = { munit_void_test(test_nghttp2_ratelim_update), munit_void_test(test_nghttp2_ratelim_drain), munit_test_end(), }; const MunitSuite ratelim_suite = { "/ratelim", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE, }; void test_nghttp2_ratelim_update(void) { nghttp2_ratelim rl; nghttp2_ratelim_init(&rl, 1000, 21); assert_uint64(1000, ==, rl.val); assert_uint64(1000, ==, rl.burst); assert_uint64(21, ==, rl.rate); assert_uint64(0, ==, rl.tstamp); nghttp2_ratelim_update(&rl, 999); assert_uint64(1000, ==, rl.val); assert_uint64(999, ==, rl.tstamp); nghttp2_ratelim_drain(&rl, 100); assert_uint64(900, ==, rl.val); nghttp2_ratelim_update(&rl, 1000); assert_uint64(921, ==, rl.val); nghttp2_ratelim_update(&rl, 1002); assert_uint64(963, ==, rl.val); nghttp2_ratelim_update(&rl, 1004); assert_uint64(1000, ==, rl.val); assert_uint64(1004, ==, rl.tstamp); /* timer skew */ nghttp2_ratelim_init(&rl, 1000, 21); nghttp2_ratelim_update(&rl, 1); assert_uint64(1000, ==, rl.val); nghttp2_ratelim_update(&rl, 0); assert_uint64(1000, ==, rl.val); /* rate * duration overflow */ nghttp2_ratelim_init(&rl, 1000, 100); nghttp2_ratelim_drain(&rl, 999); assert_uint64(1, ==, rl.val); nghttp2_ratelim_update(&rl, UINT64_MAX); assert_uint64(1000, ==, rl.val); /* val + rate * duration overflow */ nghttp2_ratelim_init(&rl, UINT64_MAX - 1, 2); nghttp2_ratelim_update(&rl, 1); assert_uint64(UINT64_MAX - 1, ==, rl.val); } void test_nghttp2_ratelim_drain(void) { nghttp2_ratelim rl; nghttp2_ratelim_init(&rl, 100, 7); assert_int(-1, ==, nghttp2_ratelim_drain(&rl, 101)); assert_int(0, ==, nghttp2_ratelim_drain(&rl, 51)); assert_int(0, ==, nghttp2_ratelim_drain(&rl, 49)); assert_int(-1, ==, nghttp2_ratelim_drain(&rl, 1)); } nghttp2-1.68.0/tests/PaxHeaders/nghttp2_extpri_test.h0000644000000000000000000000013215077107271017614 xustar0030 mtime=1761382073.006444079 30 atime=1761382080.107411476 30 ctime=1761382108.085303315 nghttp2-1.68.0/tests/nghttp2_extpri_test.h0000644000175100017510000000275715077107271020217 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2022 nghttp3 contributors * Copyright (c) 2022 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_EXTPRI_TEST_H #define NGHTTP2_EXTPRI_TEST_H #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" extern const MunitSuite extpri_suite; munit_void_test_decl(test_nghttp2_extpri_to_uint8) #endif /* NGHTTP2_EXTPRI_TEST_H */ nghttp2-1.68.0/tests/PaxHeaders/nghttp2_extpri_test.c0000644000000000000000000000013215077107271017607 xustar0030 mtime=1761382073.006444079 30 atime=1761382080.107411476 30 ctime=1761382108.103303263 nghttp2-1.68.0/tests/nghttp2_extpri_test.c0000644000175100017510000000370615077107271020205 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2022 nghttp3 contributors * Copyright (c) 2022 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_extpri_test.h" #include #include "munit.h" #include "nghttp2_extpri.h" #include "nghttp2_test_helper.h" static const MunitTest tests[] = { munit_void_test(test_nghttp2_extpri_to_uint8), munit_test_end(), }; const MunitSuite extpri_suite = { "/extpri", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE, }; void test_nghttp2_extpri_to_uint8(void) { { nghttp2_extpri pri = {1, 0}; assert_uint8(1, ==, nghttp2_extpri_to_uint8(&pri)); } { nghttp2_extpri pri = {1, 1}; assert_uint8((0x80 | 1), ==, nghttp2_extpri_to_uint8(&pri)); } { nghttp2_extpri pri = {7, 1}; assert_uint8((0x80 | 7), ==, nghttp2_extpri_to_uint8(&pri)); } { nghttp2_extpri pri = {7, 0}; assert_uint8(7, ==, nghttp2_extpri_to_uint8(&pri)); } } nghttp2-1.68.0/tests/PaxHeaders/nghttp2_session_test.h0000644000000000000000000000013115077107271017763 xustar0030 mtime=1761382073.008444069 30 atime=1761382080.108411471 29 ctime=1761382108.07330335 nghttp2-1.68.0/tests/nghttp2_session_test.h0000644000175100017510000002246115077107271020361 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_SESSION_TEST_H #define NGHTTP2_SESSION_TEST_H #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" extern const MunitSuite session_suite; munit_void_test_decl(test_nghttp2_session_recv) munit_void_test_decl(test_nghttp2_session_recv_invalid_stream_id) munit_void_test_decl(test_nghttp2_session_recv_invalid_frame) munit_void_test_decl(test_nghttp2_session_recv_eof) munit_void_test_decl(test_nghttp2_session_recv_data) munit_void_test_decl(test_nghttp2_session_recv_data_no_auto_flow_control) munit_void_test_decl(test_nghttp2_session_recv_continuation) munit_void_test_decl(test_nghttp2_session_recv_headers_with_priority) munit_void_test_decl(test_nghttp2_session_recv_headers_with_padding) munit_void_test_decl(test_nghttp2_session_recv_headers_early_response) munit_void_test_decl(test_nghttp2_session_recv_headers_for_closed_stream) munit_void_test_decl(test_nghttp2_session_recv_headers_with_extpri) munit_void_test_decl(test_nghttp2_session_server_recv_push_response) munit_void_test_decl(test_nghttp2_session_recv_premature_headers) munit_void_test_decl(test_nghttp2_session_recv_unknown_frame) munit_void_test_decl(test_nghttp2_session_recv_unexpected_continuation) munit_void_test_decl(test_nghttp2_session_recv_settings_header_table_size) munit_void_test_decl(test_nghttp2_session_recv_too_large_frame_length) munit_void_test_decl(test_nghttp2_session_recv_extension) munit_void_test_decl(test_nghttp2_session_recv_altsvc) munit_void_test_decl(test_nghttp2_session_recv_origin) munit_void_test_decl(test_nghttp2_session_recv_priority_update) munit_void_test_decl(test_nghttp2_session_continue) munit_void_test_decl(test_nghttp2_session_add_frame) munit_void_test_decl(test_nghttp2_session_on_request_headers_received) munit_void_test_decl(test_nghttp2_session_on_response_headers_received) munit_void_test_decl(test_nghttp2_session_on_headers_received) munit_void_test_decl(test_nghttp2_session_on_push_response_headers_received) munit_void_test_decl(test_nghttp2_session_on_rst_stream_received) munit_void_test_decl(test_nghttp2_session_on_settings_received) munit_void_test_decl(test_nghttp2_session_on_push_promise_received) munit_void_test_decl(test_nghttp2_session_on_ping_received) munit_void_test_decl(test_nghttp2_session_on_goaway_received) munit_void_test_decl(test_nghttp2_session_on_window_update_received) munit_void_test_decl(test_nghttp2_session_on_data_received) munit_void_test_decl(test_nghttp2_session_on_data_received_fail_fast) munit_void_test_decl(test_nghttp2_session_on_altsvc_received) munit_void_test_decl(test_nghttp2_session_send_headers_start_stream) munit_void_test_decl(test_nghttp2_session_send_headers_reply) munit_void_test_decl(test_nghttp2_session_send_headers_frame_size_error) munit_void_test_decl(test_nghttp2_session_send_headers_push_reply) munit_void_test_decl(test_nghttp2_session_send_rst_stream) munit_void_test_decl(test_nghttp2_session_send_push_promise) munit_void_test_decl(test_nghttp2_session_is_my_stream_id) munit_void_test_decl(test_nghttp2_session_upgrade2) munit_void_test_decl(test_nghttp2_submit_data) munit_void_test_decl(test_nghttp2_submit_data_read_length_too_large) munit_void_test_decl(test_nghttp2_submit_data_read_length_smallest) munit_void_test_decl(test_nghttp2_submit_data_twice) munit_void_test_decl(test_nghttp2_submit_request_with_data) munit_void_test_decl(test_nghttp2_submit_request_without_data) munit_void_test_decl(test_nghttp2_submit_response_with_data) munit_void_test_decl(test_nghttp2_submit_response_without_data) munit_void_test_decl(test_nghttp2_submit_response_push_response) munit_void_test_decl(test_nghttp2_submit_trailer) munit_void_test_decl(test_nghttp2_submit_headers_start_stream) munit_void_test_decl(test_nghttp2_submit_headers_reply) munit_void_test_decl(test_nghttp2_submit_headers_push_reply) munit_void_test_decl(test_nghttp2_submit_headers) munit_void_test_decl(test_nghttp2_submit_headers_continuation) munit_void_test_decl(test_nghttp2_submit_headers_continuation_extra_large) munit_void_test_decl(test_nghttp2_submit_settings) munit_void_test_decl(test_nghttp2_submit_settings_update_local_window_size) munit_void_test_decl(test_nghttp2_submit_settings_multiple_times) munit_void_test_decl(test_nghttp2_submit_push_promise) munit_void_test_decl(test_nghttp2_submit_window_update) munit_void_test_decl(test_nghttp2_submit_window_update_local_window_size) munit_void_test_decl(test_nghttp2_submit_shutdown_notice) munit_void_test_decl(test_nghttp2_submit_invalid_nv) munit_void_test_decl(test_nghttp2_submit_extension) munit_void_test_decl(test_nghttp2_submit_altsvc) munit_void_test_decl(test_nghttp2_submit_origin) munit_void_test_decl(test_nghttp2_submit_priority_update) munit_void_test_decl(test_nghttp2_submit_rst_stream) munit_void_test_decl(test_nghttp2_session_open_stream) munit_void_test_decl(test_nghttp2_session_get_next_ob_item) munit_void_test_decl(test_nghttp2_session_pop_next_ob_item) munit_void_test_decl(test_nghttp2_session_reply_fail) munit_void_test_decl(test_nghttp2_session_max_concurrent_streams) munit_void_test_decl(test_nghttp2_session_stop_data_with_rst_stream) munit_void_test_decl(test_nghttp2_session_defer_data) munit_void_test_decl(test_nghttp2_session_flow_control) munit_void_test_decl(test_nghttp2_session_flow_control_data_recv) munit_void_test_decl(test_nghttp2_session_flow_control_data_with_padding_recv) munit_void_test_decl(test_nghttp2_session_data_read_temporal_failure) munit_void_test_decl(test_nghttp2_session_on_stream_close) munit_void_test_decl(test_nghttp2_session_on_ctrl_not_send) munit_void_test_decl(test_nghttp2_session_get_outbound_queue_size) munit_void_test_decl(test_nghttp2_session_get_effective_local_window_size) munit_void_test_decl(test_nghttp2_session_set_option) munit_void_test_decl(test_nghttp2_session_data_backoff_by_high_pri_frame) munit_void_test_decl(test_nghttp2_session_pack_data_with_padding) munit_void_test_decl(test_nghttp2_session_pack_headers_with_padding) munit_void_test_decl(test_nghttp2_pack_settings_payload) munit_void_test_decl(test_nghttp2_session_stream_get_state) munit_void_test_decl(test_nghttp2_session_find_stream) munit_void_test_decl(test_nghttp2_session_graceful_shutdown) munit_void_test_decl(test_nghttp2_session_on_header_temporal_failure) munit_void_test_decl(test_nghttp2_session_recv_client_magic) munit_void_test_decl(test_nghttp2_session_delete_data_item) munit_void_test_decl(test_nghttp2_session_open_idle_stream) munit_void_test_decl(test_nghttp2_session_cancel_reserved_remote) munit_void_test_decl(test_nghttp2_session_reset_pending_headers) munit_void_test_decl(test_nghttp2_session_send_data_callback) munit_void_test_decl(test_nghttp2_session_on_begin_headers_temporal_failure) munit_void_test_decl(test_nghttp2_session_defer_then_close) munit_void_test_decl(test_nghttp2_session_detach_item_from_closed_stream) munit_void_test_decl(test_nghttp2_session_flooding) munit_void_test_decl(test_nghttp2_session_change_extpri_stream_priority) munit_void_test_decl(test_nghttp2_session_set_local_window_size) munit_void_test_decl(test_nghttp2_session_cancel_from_before_frame_send) munit_void_test_decl(test_nghttp2_session_too_many_settings) munit_void_test_decl(test_nghttp2_session_removed_closed_stream) munit_void_test_decl(test_nghttp2_session_pause_data) munit_void_test_decl(test_nghttp2_session_no_closed_streams) munit_void_test_decl(test_nghttp2_session_set_stream_user_data) munit_void_test_decl(test_nghttp2_session_no_rfc7540_priorities) munit_void_test_decl(test_nghttp2_session_stream_reset_ratelim) munit_void_test_decl(test_nghttp2_http_mandatory_headers) munit_void_test_decl(test_nghttp2_http_content_length) munit_void_test_decl(test_nghttp2_http_content_length_mismatch) munit_void_test_decl(test_nghttp2_http_non_final_response) munit_void_test_decl(test_nghttp2_http_trailer_headers) munit_void_test_decl(test_nghttp2_http_ignore_regular_header) munit_void_test_decl(test_nghttp2_http_ignore_content_length) munit_void_test_decl(test_nghttp2_http_record_request_method) munit_void_test_decl(test_nghttp2_http_push_promise) munit_void_test_decl(test_nghttp2_http_head_method_upgrade_workaround) munit_void_test_decl( test_nghttp2_http_no_rfc9113_leading_and_trailing_ws_validation) #endif /* NGHTTP2_SESSION_TEST_H */ nghttp2-1.68.0/tests/PaxHeaders/nghttp2_pq_test.c0000644000000000000000000000013215077107271016714 xustar0030 mtime=1761382073.007444074 30 atime=1761382080.107411476 30 ctime=1761382108.088303306 nghttp2-1.68.0/tests/nghttp2_pq_test.c0000644000175100017510000001440515077107271017310 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_pq_test.h" #include #include "nghttp2_pq.h" static const MunitTest tests[] = { munit_void_test(test_nghttp2_pq), munit_void_test(test_nghttp2_pq_update), munit_void_test(test_nghttp2_pq_remove), munit_test_end(), }; const MunitSuite pq_suite = { "/pq", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE, }; typedef struct { nghttp2_pq_entry ent; const char *s; } string_entry; static string_entry *string_entry_new(const char *s) { nghttp2_mem *mem; string_entry *ent; mem = nghttp2_mem_default(); ent = nghttp2_mem_malloc(mem, sizeof(string_entry)); ent->s = s; return ent; } static void string_entry_del(string_entry *ent) { free(ent); } static int pq_less(const void *lhs, const void *rhs) { return strcmp(((string_entry *)lhs)->s, ((string_entry *)rhs)->s) < 0; } void test_nghttp2_pq(void) { int i; nghttp2_pq pq; string_entry *top; nghttp2_pq_init(&pq, pq_less, nghttp2_mem_default()); assert_true(nghttp2_pq_empty(&pq)); assert_size(0, ==, nghttp2_pq_size(&pq)); assert_int(0, ==, nghttp2_pq_push(&pq, &string_entry_new("foo")->ent)); assert_false(nghttp2_pq_empty(&pq)); assert_size(1, ==, nghttp2_pq_size(&pq)); top = (string_entry *)nghttp2_pq_top(&pq); assert_string_equal("foo", top->s); assert_int(0, ==, nghttp2_pq_push(&pq, &string_entry_new("bar")->ent)); top = (string_entry *)nghttp2_pq_top(&pq); assert_string_equal("bar", top->s); assert_int(0, ==, nghttp2_pq_push(&pq, &string_entry_new("baz")->ent)); top = (string_entry *)nghttp2_pq_top(&pq); assert_string_equal("bar", top->s); assert_int(0, ==, nghttp2_pq_push(&pq, &string_entry_new("C")->ent)); assert_size(4, ==, nghttp2_pq_size(&pq)); top = (string_entry *)nghttp2_pq_top(&pq); assert_string_equal("C", top->s); string_entry_del(top); nghttp2_pq_pop(&pq); assert_size(3, ==, nghttp2_pq_size(&pq)); top = (string_entry *)nghttp2_pq_top(&pq); assert_string_equal("bar", top->s); nghttp2_pq_pop(&pq); string_entry_del(top); top = (string_entry *)nghttp2_pq_top(&pq); assert_string_equal("baz", top->s); nghttp2_pq_pop(&pq); string_entry_del(top); top = (string_entry *)nghttp2_pq_top(&pq); assert_string_equal("foo", top->s); nghttp2_pq_pop(&pq); string_entry_del(top); assert_true(nghttp2_pq_empty(&pq)); assert_size(0, ==, nghttp2_pq_size(&pq)); assert_null(nghttp2_pq_top(&pq)); /* Add bunch of entry to see realloc works */ for (i = 0; i < 10000; ++i) { assert_int(0, ==, nghttp2_pq_push(&pq, &string_entry_new("foo")->ent)); assert_size((size_t)(i + 1), ==, nghttp2_pq_size(&pq)); } for (i = 10000; i > 0; --i) { top = (string_entry *)nghttp2_pq_top(&pq); assert_not_null(top); nghttp2_pq_pop(&pq); string_entry_del(top); assert_size((size_t)(i - 1), ==, nghttp2_pq_size(&pq)); } nghttp2_pq_free(&pq); } typedef struct { nghttp2_pq_entry ent; int key; int val; } node; static int node_less(const void *lhs, const void *rhs) { node *ln = (node *)lhs; node *rn = (node *)rhs; return ln->key < rn->key; } static int node_update(nghttp2_pq_entry *item, void *arg) { node *nd = (node *)item; (void)arg; if ((nd->key % 2) == 0) { nd->key *= -1; return 1; } else { return 0; } } void test_nghttp2_pq_update(void) { nghttp2_pq pq; node nodes[10]; int i; node *nd; int ans[] = {-8, -6, -4, -2, 0, 1, 3, 5, 7, 9}; nghttp2_pq_init(&pq, node_less, nghttp2_mem_default()); for (i = 0; i < (int)(sizeof(nodes) / sizeof(nodes[0])); ++i) { nodes[i].key = i; nodes[i].val = i; nghttp2_pq_push(&pq, &nodes[i].ent); } nghttp2_pq_update(&pq, node_update, NULL); for (i = 0; i < (int)(sizeof(nodes) / sizeof(nodes[0])); ++i) { nd = (node *)nghttp2_pq_top(&pq); assert_int(ans[i], ==, nd->key); nghttp2_pq_pop(&pq); } nghttp2_pq_free(&pq); } static void push_nodes(nghttp2_pq *pq, node *dest, size_t n) { size_t i; for (i = 0; i < n; ++i) { dest[i].key = (int)i; dest[i].val = (int)i; nghttp2_pq_push(pq, &dest[i].ent); } } static void check_nodes(nghttp2_pq *pq, size_t n, int *ans_key, int *ans_val) { size_t i; for (i = 0; i < n; ++i) { node *nd = (node *)nghttp2_pq_top(pq); assert_int(ans_key[i], ==, nd->key); assert_int(ans_val[i], ==, nd->val); nghttp2_pq_pop(pq); } } void test_nghttp2_pq_remove(void) { nghttp2_pq pq; node nodes[10]; int ans_key1[] = {1, 2, 3, 4, 5}; int ans_val1[] = {1, 2, 3, 4, 5}; int ans_key2[] = {0, 1, 2, 4, 5}; int ans_val2[] = {0, 1, 2, 4, 5}; int ans_key3[] = {0, 1, 2, 3, 4}; int ans_val3[] = {0, 1, 2, 3, 4}; nghttp2_pq_init(&pq, node_less, nghttp2_mem_default()); push_nodes(&pq, nodes, 6); nghttp2_pq_remove(&pq, &nodes[0].ent); check_nodes(&pq, 5, ans_key1, ans_val1); nghttp2_pq_free(&pq); nghttp2_pq_init(&pq, node_less, nghttp2_mem_default()); push_nodes(&pq, nodes, 6); nghttp2_pq_remove(&pq, &nodes[3].ent); check_nodes(&pq, 5, ans_key2, ans_val2); nghttp2_pq_free(&pq); nghttp2_pq_init(&pq, node_less, nghttp2_mem_default()); push_nodes(&pq, nodes, 6); nghttp2_pq_remove(&pq, &nodes[5].ent); check_nodes(&pq, 5, ans_key3, ans_val3); nghttp2_pq_free(&pq); } nghttp2-1.68.0/tests/PaxHeaders/nghttp2_assertion.h0000644000000000000000000000013215077107271017251 xustar0030 mtime=1761382073.006444079 30 atime=1761382080.107411476 30 ctime=1761382108.081303327 nghttp2-1.68.0/tests/nghttp2_assertion.h0000644000175100017510000000543415077107271017647 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2024 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_ASSERTION_H #define NGHTTP2_ASSERTION_H #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #include "munit.h" #include "nghttp2_frame.h" #define assert_nv_equal(A, B, len, mem) \ do { \ size_t alloclen = sizeof(nghttp2_nv) * (len); \ const nghttp2_nv *sa = (A), *sb = (B); \ nghttp2_nv *a = (mem)->malloc(alloclen, NULL); \ nghttp2_nv *b = (mem)->malloc(alloclen, NULL); \ size_t i_; \ memcpy(a, sa, alloclen); \ memcpy(b, sb, alloclen); \ nghttp2_nv_array_sort(a, (len)); \ nghttp2_nv_array_sort(b, (len)); \ for (i_ = 0; i_ < (size_t)(len); ++i_) { \ assert_memn_equal(a[i_].name, a[i_].namelen, b[i_].name, b[i_].namelen); \ assert_memn_equal(a[i_].value, a[i_].valuelen, b[i_].value, \ b[i_].valuelen); \ } \ (mem)->free(b, NULL); \ (mem)->free(a, NULL); \ } while (0); #endif /* NGHTTP2_ASSERTION_H */ nghttp2-1.68.0/tests/PaxHeaders/failmalloc.c0000644000000000000000000000013115077107271015671 xustar0030 mtime=1761382073.002444097 29 atime=1761382080.10641148 30 ctime=1761382108.058303393 nghttp2-1.68.0/tests/failmalloc.c0000644000175100017510000000311515077107271016262 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #include "munit.h" /* include test cases' include files here */ #include "failmalloc_test.h" int main(int argc, char *argv[]) { const MunitSuite suites[] = { failmalloc_suite, {NULL, NULL, NULL, 0, MUNIT_SUITE_OPTION_NONE}, }; const MunitSuite suite = { "", NULL, suites, 1, MUNIT_SUITE_OPTION_NONE, }; return munit_suite_main(&suite, NULL, argc, argv); } nghttp2-1.68.0/tests/PaxHeaders/nghttp2_buf_test.h0000644000000000000000000000013215077107271017055 xustar0030 mtime=1761382073.006444079 30 atime=1761382080.107411476 30 ctime=1761382108.082303324 nghttp2-1.68.0/tests/nghttp2_buf_test.h0000644000175100017510000000350115077107271017444 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_BUF_TEST_H #define NGHTTP2_BUF_TEST_H #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" extern const MunitSuite buf_suite; munit_void_test_decl(test_nghttp2_bufs_add) munit_void_test_decl(test_nghttp2_bufs_add_stack_buffer_overflow_bug) munit_void_test_decl(test_nghttp2_bufs_addb) munit_void_test_decl(test_nghttp2_bufs_orb) munit_void_test_decl(test_nghttp2_bufs_remove) munit_void_test_decl(test_nghttp2_bufs_reset) munit_void_test_decl(test_nghttp2_bufs_advance) munit_void_test_decl(test_nghttp2_bufs_next_present) munit_void_test_decl(test_nghttp2_bufs_realloc) #endif /* NGHTTP2_BUF_TEST_H */ nghttp2-1.68.0/tests/PaxHeaders/nghttp2_map_test.c0000644000000000000000000000013215077107271017051 xustar0030 mtime=1761382073.007444074 30 atime=1761382080.107411476 30 ctime=1761382108.090303301 nghttp2-1.68.0/tests/nghttp2_map_test.c0000644000175100017510000001467015077107271017451 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2017 ngtcp2 contributors * Copyright (c) 2012 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_map_test.h" #include #include "munit.h" #include "nghttp2_map.h" static const MunitTest tests[] = { munit_void_test(test_nghttp2_map), munit_void_test(test_nghttp2_map_functional), munit_void_test(test_nghttp2_map_each), munit_void_test(test_nghttp2_map_clear), munit_test_end(), }; const MunitSuite map_suite = { "/map", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE, }; #define NGHTTP2_TEST_MAP_SEED 0xfefefefe typedef struct strentry { nghttp2_map_key_type key; const char *str; } strentry; static void strentry_init(strentry *entry, nghttp2_map_key_type key, const char *str) { entry->key = key; entry->str = str; } void test_nghttp2_map(void) { strentry foo, FOO, bar, baz, shrubbery; nghttp2_map map; nghttp2_map_init(&map, NGHTTP2_TEST_MAP_SEED, nghttp2_mem_default()); strentry_init(&foo, 1, "foo"); strentry_init(&FOO, 1, "FOO"); strentry_init(&bar, 2, "bar"); strentry_init(&baz, 3, "baz"); strentry_init(&shrubbery, 4, "shrubbery"); assert_int(0, ==, nghttp2_map_insert(&map, foo.key, &foo)); assert_string_equal("foo", ((strentry *)nghttp2_map_find(&map, 1))->str); assert_size(1, ==, nghttp2_map_size(&map)); assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, nghttp2_map_insert(&map, FOO.key, &FOO)); assert_size(1, ==, nghttp2_map_size(&map)); assert_string_equal("foo", ((strentry *)nghttp2_map_find(&map, 1))->str); assert_int(0, ==, nghttp2_map_insert(&map, bar.key, &bar)); assert_size(2, ==, nghttp2_map_size(&map)); assert_int(0, ==, nghttp2_map_insert(&map, baz.key, &baz)); assert_size(3, ==, nghttp2_map_size(&map)); assert_int(0, ==, nghttp2_map_insert(&map, shrubbery.key, &shrubbery)); assert_size(4, ==, nghttp2_map_size(&map)); assert_string_equal("baz", ((strentry *)nghttp2_map_find(&map, 3))->str); nghttp2_map_remove(&map, 3); assert_size(3, ==, nghttp2_map_size(&map)); assert_null(nghttp2_map_find(&map, 3)); nghttp2_map_remove(&map, 1); assert_size(2, ==, nghttp2_map_size(&map)); assert_null(nghttp2_map_find(&map, 1)); /* Erasing non-existent entry */ nghttp2_map_remove(&map, 1); assert_size(2, ==, nghttp2_map_size(&map)); assert_null(nghttp2_map_find(&map, 1)); assert_string_equal("bar", ((strentry *)nghttp2_map_find(&map, 2))->str); assert_string_equal("shrubbery", ((strentry *)nghttp2_map_find(&map, 4))->str); nghttp2_map_free(&map); } static void shuffle(int *a, int n) { int i; for (i = n - 1; i >= 1; --i) { size_t j = (size_t)((double)(i + 1) * rand() / (RAND_MAX + 1.0)); int t = a[j]; a[j] = a[i]; a[i] = t; } } static int eachfun(void *data, void *ptr) { (void)data; (void)ptr; return 0; } #define NUM_ENT 6000 static strentry arr[NUM_ENT]; static int order[NUM_ENT]; void test_nghttp2_map_functional(void) { nghttp2_map map; int i; strentry *ent; nghttp2_map_init(&map, NGHTTP2_TEST_MAP_SEED, nghttp2_mem_default()); for (i = 0; i < NUM_ENT; ++i) { strentry_init(&arr[i], (nghttp2_map_key_type)(i + 1), "foo"); order[i] = i + 1; } /* insertion */ shuffle(order, NUM_ENT); for (i = 0; i < NUM_ENT; ++i) { ent = &arr[order[i] - 1]; assert_int(0, ==, nghttp2_map_insert(&map, ent->key, ent)); } assert_size(NUM_ENT, ==, nghttp2_map_size(&map)); /* traverse */ nghttp2_map_each(&map, eachfun, NULL); /* find */ shuffle(order, NUM_ENT); for (i = 0; i < NUM_ENT; ++i) { assert_not_null(nghttp2_map_find(&map, (nghttp2_map_key_type)order[i])); } /* remove */ for (i = 0; i < NUM_ENT; ++i) { assert_int(0, ==, nghttp2_map_remove(&map, (nghttp2_map_key_type)order[i])); } /* each (but no op function for testing purpose) */ for (i = 0; i < NUM_ENT; ++i) { strentry_init(&arr[i], (nghttp2_map_key_type)(i + 1), "foo"); } /* insert once again */ for (i = 0; i < NUM_ENT; ++i) { ent = &arr[i]; assert_int(0, ==, nghttp2_map_insert(&map, ent->key, ent)); } nghttp2_map_each(&map, eachfun, NULL); nghttp2_map_free(&map); } static int entry_free(void *data, void *ptr) { const nghttp2_mem *mem = ptr; mem->free(data, NULL); return 0; } void test_nghttp2_map_each(void) { const nghttp2_mem *mem = nghttp2_mem_default(); strentry *foo = mem->malloc(sizeof(strentry), NULL), *bar = mem->malloc(sizeof(strentry), NULL), *baz = mem->malloc(sizeof(strentry), NULL), *shrubbery = mem->malloc(sizeof(strentry), NULL); nghttp2_map map; nghttp2_map_init(&map, NGHTTP2_TEST_MAP_SEED, nghttp2_mem_default()); strentry_init(foo, 1, "foo"); strentry_init(bar, 2, "bar"); strentry_init(baz, 3, "baz"); strentry_init(shrubbery, 4, "shrubbery"); nghttp2_map_insert(&map, foo->key, foo); nghttp2_map_insert(&map, bar->key, bar); nghttp2_map_insert(&map, baz->key, baz); nghttp2_map_insert(&map, shrubbery->key, shrubbery); nghttp2_map_each(&map, entry_free, (void *)mem); nghttp2_map_free(&map); } void test_nghttp2_map_clear(void) { nghttp2_mem *mem = nghttp2_mem_default(); nghttp2_map map; strentry foo; strentry_init(&foo, 1, "foo"); nghttp2_map_init(&map, NGHTTP2_TEST_MAP_SEED, mem); assert_int(0, ==, nghttp2_map_insert(&map, foo.key, &foo)); nghttp2_map_clear(&map); assert_size(0, ==, nghttp2_map_size(&map)); nghttp2_map_free(&map); } nghttp2-1.68.0/tests/PaxHeaders/nghttp2_map_test.h0000644000000000000000000000013215077107271017056 xustar0030 mtime=1761382073.007444074 30 atime=1761382080.107411476 30 ctime=1761382108.071303356 nghttp2-1.68.0/tests/nghttp2_map_test.h0000644000175100017510000000314115077107271017445 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2017 ngtcp2 contributors * Copyright (c) 2012 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_MAP_TEST_H #define NGHTTP2_MAP_TEST_H #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" extern const MunitSuite map_suite; munit_void_test_decl(test_nghttp2_map) munit_void_test_decl(test_nghttp2_map_functional) munit_void_test_decl(test_nghttp2_map_each) munit_void_test_decl(test_nghttp2_map_clear) #endif /* NGHTTP2_MAP_TEST_H */ nghttp2-1.68.0/tests/PaxHeaders/nghttp2_test_helper.h0000644000000000000000000000013215077107271017560 xustar0030 mtime=1761382073.008444069 30 atime=1761382080.108411471 30 ctime=1761382108.065303373 nghttp2-1.68.0/tests/nghttp2_test_helper.h0000644000175100017510000001051215077107271020147 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_TEST_HELPER_H #define NGHTTP2_TEST_HELPER_H #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #include "nghttp2_frame.h" #include "nghttp2_hd.h" #include "nghttp2_session.h" #define MAKE_NV(NAME, VALUE) \ { \ (uint8_t *)(NAME), (uint8_t *)(VALUE), sizeof((NAME)) - 1, \ sizeof((VALUE)) - 1, NGHTTP2_NV_FLAG_NONE, \ } #define ARRLEN(ARR) (sizeof(ARR) / sizeof(ARR[0])) int unpack_framebuf(nghttp2_frame *frame, nghttp2_bufs *bufs); int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len); int strmemeq(const char *a, const uint8_t *b, size_t bn); int nvnameeq(const char *a, nghttp2_nv *nv); int nvvalueeq(const char *a, nghttp2_nv *nv); typedef struct { nghttp2_nv nva[256]; size_t nvlen; } nva_out; void nva_out_init(nva_out *out); void nva_out_reset(nva_out *out, nghttp2_mem *mem); void add_out(nva_out *out, nghttp2_nv *nv, nghttp2_mem *mem); nghttp2_ssize inflate_hd(nghttp2_hd_inflater *inflater, nva_out *out, nghttp2_bufs *bufs, size_t offset, nghttp2_mem *mem); int pack_headers(nghttp2_bufs *bufs, nghttp2_hd_deflater *deflater, int32_t stream_id, uint8_t flags, const nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem); int pack_push_promise(nghttp2_bufs *bufs, nghttp2_hd_deflater *deflater, int32_t stream_id, uint8_t flags, int32_t promised_stream_id, const nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem); int frame_pack_bufs_init(nghttp2_bufs *bufs); void bufs_large_init(nghttp2_bufs *bufs, size_t chunk_size); nghttp2_stream *open_stream(nghttp2_session *session, int32_t stream_id); nghttp2_outbound_item *create_data_ob_item(nghttp2_mem *mem); /* Opens stream. This stream is assumed to be sent from |session|, and session->last_sent_stream_id and session->next_stream_id will be adjusted accordingly. */ nghttp2_stream *open_sent_stream(nghttp2_session *session, int32_t stream_id); nghttp2_stream *open_sent_stream2(nghttp2_session *session, int32_t stream_id, nghttp2_stream_state initial_state); nghttp2_stream *open_sent_stream3(nghttp2_session *session, int32_t stream_id, uint8_t flags, nghttp2_stream_state initial_state, void *stream_user_data); /* Opens stream. This stream is assumed to be received by |session|, and session->last_recv_stream_id will be adjusted accordingly. */ nghttp2_stream *open_recv_stream(nghttp2_session *session, int32_t stream_id); nghttp2_stream *open_recv_stream2(nghttp2_session *session, int32_t stream_id, nghttp2_stream_state initial_state); nghttp2_stream *open_recv_stream3(nghttp2_session *session, int32_t stream_id, uint8_t flags, nghttp2_stream_state initial_state, void *stream_user_data); #endif /* NGHTTP2_TEST_HELPER_H */ nghttp2-1.68.0/tests/PaxHeaders/nghttp2_ratelim_test.h0000644000000000000000000000013215077107271017736 xustar0030 mtime=1761382073.007444074 30 atime=1761382080.107411476 30 ctime=1761382108.086303312 nghttp2-1.68.0/tests/nghttp2_ratelim_test.h0000644000175100017510000000277015077107271020334 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2023 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_RATELIM_TEST_H #define NGHTTP2_RATELIM_TEST_H #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" extern const MunitSuite ratelim_suite; munit_void_test_decl(test_nghttp2_ratelim_update) munit_void_test_decl(test_nghttp2_ratelim_drain) #endif /* NGHTTP2_RATELIM_TEST_H */ nghttp2-1.68.0/tests/PaxHeaders/nghttp2_queue_test.h0000644000000000000000000000013215077107271017425 xustar0030 mtime=1761382073.007444074 30 atime=1761382080.107411476 30 ctime=1761382108.072303353 nghttp2-1.68.0/tests/nghttp2_queue_test.h0000644000175100017510000000266515077107271020026 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_QUEUE_TEST_H #define NGHTTP2_QUEUE_TEST_H #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" extern const MunitSuite queue_suite; munit_void_test_decl(test_nghttp2_queue) #endif /* NGHTTP2_QUEUE_TEST_H */ nghttp2-1.68.0/tests/PaxHeaders/nghttp2_http_test.h0000644000000000000000000000013215077107271017260 xustar0030 mtime=1761382073.007444074 30 atime=1761382080.107411476 30 ctime=1761382108.083303321 nghttp2-1.68.0/tests/nghttp2_http_test.h0000644000175100017510000000275315077107271017657 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2022 nghttp3 contributors * Copyright (c) 2022 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_HTTP_TEST_H #define NGHTTP2_HTTP_TEST_H #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" extern const MunitSuite http_suite; munit_void_test_decl(test_nghttp2_http_parse_priority) #endif /* NGHTTP2_HTTP_TEST_H */ nghttp2-1.68.0/tests/PaxHeaders/nghttp2_alpn_test.c0000644000000000000000000000013215077107271017226 xustar0030 mtime=1761382073.006444079 30 atime=1761382080.107411476 30 ctime=1761382108.098303278 nghttp2-1.68.0/tests/nghttp2_alpn_test.c0000644000175100017510000000676415077107271017633 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Twist Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_alpn_test.h" #include #include #include "munit.h" #include static const MunitTest tests[] = { munit_void_test(test_nghttp2_alpn), munit_test_end(), }; const MunitSuite alpn_suite = { "/alpn", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE, }; static void http2(void) { const unsigned char p[] = {8, 'h', 't', 't', 'p', '/', '1', '.', '1', 2, 'h', '2', 6, 's', 'p', 'd', 'y', '/', '3'}; unsigned char outlen; const unsigned char *out; assert_int(1, ==, nghttp2_select_next_protocol((unsigned char **)&out, &outlen, p, sizeof(p))); assert_uchar(NGHTTP2_PROTO_VERSION_ID_LEN, ==, outlen); assert_memory_equal(outlen, NGHTTP2_PROTO_VERSION_ID, out); outlen = 0; out = NULL; assert_int(1, ==, nghttp2_select_alpn(&out, &outlen, p, sizeof(p))); assert_uchar(NGHTTP2_PROTO_VERSION_ID_LEN, ==, outlen); assert_memory_equal(outlen, NGHTTP2_PROTO_VERSION_ID, out); } static void http11(void) { const unsigned char spdy[] = { 6, 's', 'p', 'd', 'y', '/', '4', 8, 's', 'p', 'd', 'y', '/', '2', '.', '1', 8, 'h', 't', 't', 'p', '/', '1', '.', '1', }; unsigned char outlen; const unsigned char *out; assert_int(0, ==, nghttp2_select_next_protocol((unsigned char **)&out, &outlen, spdy, sizeof(spdy))); assert_uchar(8, ==, outlen); assert_memory_equal(outlen, "http/1.1", out); outlen = 0; out = NULL; assert_int(0, ==, nghttp2_select_alpn(&out, &outlen, spdy, sizeof(spdy))); assert_uchar(8, ==, outlen); assert_memory_equal(outlen, "http/1.1", out); } static void no_overlap(void) { const unsigned char spdy[] = { 6, 's', 'p', 'd', 'y', '/', '4', 8, 's', 'p', 'd', 'y', '/', '2', '.', '1', 8, 'h', 't', 't', 'p', '/', '1', '.', '0', }; unsigned char outlen = 0; const unsigned char *out = NULL; assert_int(-1, ==, nghttp2_select_next_protocol((unsigned char **)&out, &outlen, spdy, sizeof(spdy))); assert_uchar(0, ==, outlen); assert_null(out); outlen = 0; out = NULL; assert_int(-1, ==, nghttp2_select_alpn(&out, &outlen, spdy, sizeof(spdy))); assert_uchar(0, ==, outlen); assert_null(out); } void test_nghttp2_alpn(void) { http2(); http11(); no_overlap(); } nghttp2-1.68.0/tests/PaxHeaders/CMakeLists.txt0000644000000000000000000000013115077107271016162 xustar0030 mtime=1761382073.002444097 29 atime=1761382080.10641148 30 ctime=1761382108.105303257 nghttp2-1.68.0/tests/CMakeLists.txt0000644000175100017510000000223615077107271016556 0ustar00runnerrunner# XXX testdata/: EXTRA_DIST = cacert.pem index.html privkey.pem string(REPLACE " " ";" c_flags "${WARNCFLAGS}") add_compile_options(${c_flags}) include_directories( "${CMAKE_SOURCE_DIR}/lib/includes" "${CMAKE_SOURCE_DIR}/lib" "${CMAKE_SOURCE_DIR}/tests/munit" "${CMAKE_BINARY_DIR}/lib/includes" ) set(MAIN_SOURCES main.c nghttp2_pq_test.c nghttp2_map_test.c nghttp2_queue_test.c nghttp2_test_helper.c nghttp2_frame_test.c nghttp2_stream_test.c nghttp2_session_test.c nghttp2_hd_test.c nghttp2_alpn_test.c nghttp2_helper_test.c nghttp2_buf_test.c nghttp2_http_test.c nghttp2_extpri_test.c nghttp2_ratelim_test.c munit/munit.c ) add_executable(main EXCLUDE_FROM_ALL ${MAIN_SOURCES} ) target_link_libraries(main nghttp2_static ) add_test(main main) add_dependencies(check main) if(ENABLE_FAILMALLOC) set(FAILMALLOC_SOURCES failmalloc.c failmalloc_test.c malloc_wrapper.c nghttp2_test_helper.c munit/munit.c ) add_executable(failmalloc EXCLUDE_FROM_ALL ${FAILMALLOC_SOURCES} ) target_link_libraries(failmalloc nghttp2_static ) add_test(failmalloc failmalloc) add_dependencies(check failmalloc) endif() nghttp2-1.68.0/tests/PaxHeaders/Makefile.in0000644000000000000000000000013115077107305015465 xustar0029 mtime=1761382085.79738618 30 atime=1761382103.833320872 30 ctime=1761382108.055303402 nghttp2-1.68.0/tests/Makefile.in0000644000175100017510000013710315077107305016063 0ustar00runnerrunner# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # nghttp2 - HTTP/2 C Library # Copyright (c) 2012 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ check_PROGRAMS = main$(EXEEXT) $(am__EXEEXT_1) @ENABLE_FAILMALLOC_TRUE@am__append_1 = failmalloc TESTS = main$(EXEEXT) $(am__EXEEXT_1) @ENABLE_FAILMALLOC_TRUE@am__append_2 = failmalloc subdir = tests ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = @ENABLE_FAILMALLOC_TRUE@am__EXEEXT_1 = failmalloc$(EXEEXT) am__failmalloc_SOURCES_DIST = failmalloc.c failmalloc_test.c \ failmalloc_test.h malloc_wrapper.c malloc_wrapper.h \ nghttp2_test_helper.c nghttp2_test_helper.h munit/munit.c \ munit/munit.h am__dirstamp = $(am__leading_dot)dirstamp @ENABLE_FAILMALLOC_TRUE@am_failmalloc_OBJECTS = failmalloc.$(OBJEXT) \ @ENABLE_FAILMALLOC_TRUE@ failmalloc_test.$(OBJEXT) \ @ENABLE_FAILMALLOC_TRUE@ malloc_wrapper.$(OBJEXT) \ @ENABLE_FAILMALLOC_TRUE@ nghttp2_test_helper.$(OBJEXT) \ @ENABLE_FAILMALLOC_TRUE@ munit/munit.$(OBJEXT) failmalloc_OBJECTS = $(am_failmalloc_OBJECTS) @ENABLE_STATIC_FALSE@am__DEPENDENCIES_1 = \ @ENABLE_STATIC_FALSE@ ${top_builddir}/lib/.libs/*.o @ENABLE_STATIC_TRUE@am__DEPENDENCIES_1 = \ @ENABLE_STATIC_TRUE@ ${top_builddir}/lib/libnghttp2.la @ENABLE_FAILMALLOC_TRUE@failmalloc_DEPENDENCIES = \ @ENABLE_FAILMALLOC_TRUE@ $(am__DEPENDENCIES_1) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = failmalloc_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(failmalloc_LDFLAGS) $(LDFLAGS) -o $@ am__objects_1 = am__objects_2 = main.$(OBJEXT) nghttp2_pq_test.$(OBJEXT) \ nghttp2_map_test.$(OBJEXT) nghttp2_queue_test.$(OBJEXT) \ nghttp2_test_helper.$(OBJEXT) nghttp2_frame_test.$(OBJEXT) \ nghttp2_stream_test.$(OBJEXT) nghttp2_session_test.$(OBJEXT) \ nghttp2_hd_test.$(OBJEXT) nghttp2_alpn_test.$(OBJEXT) \ nghttp2_helper_test.$(OBJEXT) nghttp2_buf_test.$(OBJEXT) \ nghttp2_http_test.$(OBJEXT) nghttp2_extpri_test.$(OBJEXT) \ nghttp2_ratelim_test.$(OBJEXT) munit/munit.$(OBJEXT) am_main_OBJECTS = $(am__objects_1) $(am__objects_2) main_OBJECTS = $(am_main_OBJECTS) @ENABLE_STATIC_FALSE@main_DEPENDENCIES = \ @ENABLE_STATIC_FALSE@ ${top_builddir}/lib/.libs/*.o @ENABLE_STATIC_TRUE@main_DEPENDENCIES = \ @ENABLE_STATIC_TRUE@ ${top_builddir}/lib/libnghttp2.la main_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(main_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/failmalloc.Po \ ./$(DEPDIR)/failmalloc_test.Po ./$(DEPDIR)/main.Po \ ./$(DEPDIR)/malloc_wrapper.Po ./$(DEPDIR)/nghttp2_alpn_test.Po \ ./$(DEPDIR)/nghttp2_buf_test.Po \ ./$(DEPDIR)/nghttp2_extpri_test.Po \ ./$(DEPDIR)/nghttp2_frame_test.Po \ ./$(DEPDIR)/nghttp2_hd_test.Po \ ./$(DEPDIR)/nghttp2_helper_test.Po \ ./$(DEPDIR)/nghttp2_http_test.Po \ ./$(DEPDIR)/nghttp2_map_test.Po ./$(DEPDIR)/nghttp2_pq_test.Po \ ./$(DEPDIR)/nghttp2_queue_test.Po \ ./$(DEPDIR)/nghttp2_ratelim_test.Po \ ./$(DEPDIR)/nghttp2_session_test.Po \ ./$(DEPDIR)/nghttp2_stream_test.Po \ ./$(DEPDIR)/nghttp2_test_helper.Po munit/$(DEPDIR)/munit.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(failmalloc_SOURCES) $(main_SOURCES) DIST_SOURCES = $(am__failmalloc_SOURCES_DIST) $(main_SOURCES) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ check recheck distdir distdir-am am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` am__tty_colors_dummy = \ mgn= red= grn= lgn= blu= brg= std=; \ am__color_tests=no am__tty_colors = { \ $(am__tty_colors_dummy); \ if test "X$(AM_COLOR_TESTS)" = Xno; then \ am__color_tests=no; \ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ am__color_tests=yes; \ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ am__color_tests=yes; \ fi; \ if test $$am__color_tests = yes; then \ red=''; \ grn=''; \ lgn=''; \ blu=''; \ mgn=''; \ brg=''; \ std=''; \ fi; \ } am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__recheck_rx = ^[ ]*:recheck:[ ]* am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* # A command that, given a newline-separated list of test names on the # standard input, print the name of the tests that are to be re-run # upon "make recheck". am__list_recheck_tests = $(AWK) '{ \ recheck = 1; \ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ { \ if (rc < 0) \ { \ if ((getline line2 < ($$0 ".log")) < 0) \ recheck = 0; \ break; \ } \ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ { \ recheck = 0; \ break; \ } \ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ { \ break; \ } \ }; \ if (recheck) \ print $$0; \ close ($$0 ".trs"); \ close ($$0 ".log"); \ }' # A command that, given a newline-separated list of test names on the # standard input, create the global log from their .trs and .log files. am__create_global_log = $(AWK) ' \ function fatal(msg) \ { \ print "fatal: making $@: " msg | "cat >&2"; \ exit 1; \ } \ function rst_section(header) \ { \ print header; \ len = length(header); \ for (i = 1; i <= len; i = i + 1) \ printf "="; \ printf "\n\n"; \ } \ { \ copy_in_global_log = 1; \ global_test_result = "RUN"; \ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ { \ if (rc < 0) \ fatal("failed to read from " $$0 ".trs"); \ if (line ~ /$(am__global_test_result_rx)/) \ { \ sub("$(am__global_test_result_rx)", "", line); \ sub("[ ]*$$", "", line); \ global_test_result = line; \ } \ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ copy_in_global_log = 0; \ }; \ if (copy_in_global_log) \ { \ rst_section(global_test_result ": " $$0); \ while ((rc = (getline line < ($$0 ".log"))) != 0) \ { \ if (rc < 0) \ fatal("failed to read from " $$0 ".log"); \ print line; \ }; \ printf "\n"; \ }; \ close ($$0 ".trs"); \ close ($$0 ".log"); \ }' # Restructured Text title. am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } # Solaris 10 'make', and several other traditional 'make' implementations, # pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it # by disabling -e (using the XSI extension "set +e") if it's set. am__sh_e_setup = case $$- in *e*) set +e;; esac # Default flags passed to test drivers. am__common_driver_flags = \ --color-tests "$$am__color_tests" \ --enable-hard-errors "$$am__enable_hard_errors" \ --expect-failure "$$am__expect_failure" # To be inserted before the command running the test. Creates the # directory for the log if needed. Stores in $dir the directory # containing $f, in $tst the test, in $log the log. Executes the # developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and # passes TESTS_ENVIRONMENT. Set up options for the wrapper that # will run the test scripts (or their associated LOG_COMPILER, if # thy have one). am__check_pre = \ $(am__sh_e_setup); \ $(am__vpath_adj_setup) $(am__vpath_adj) \ $(am__tty_colors); \ srcdir=$(srcdir); export srcdir; \ case "$@" in \ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ *) am__odir=.;; \ esac; \ test "x$$am__odir" = x"." || test -d "$$am__odir" \ || $(MKDIR_P) "$$am__odir" || exit $$?; \ if test -f "./$$f"; then dir=./; \ elif test -f "$$f"; then dir=; \ else dir="$(srcdir)/"; fi; \ tst=$$dir$$f; log='$@'; \ if test -n '$(DISABLE_HARD_ERRORS)'; then \ am__enable_hard_errors=no; \ else \ am__enable_hard_errors=yes; \ fi; \ case " $(XFAIL_TESTS) " in \ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ am__expect_failure=yes;; \ *) \ am__expect_failure=no;; \ esac; \ $(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) # A shell command to get the names of the tests scripts with any registered # extension removed (i.e., equivalently, the names of the test logs, with # the '.log' extension removed). The result is saved in the shell variable # '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, # we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", # since that might cause problem with VPATH rewrites for suffix-less tests. # See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. am__set_TESTS_bases = \ bases='$(TEST_LOGS)'; \ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ bases=`echo $$bases` AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)' RECHECK_LOGS = $(TEST_LOGS) TEST_SUITE_LOG = test-suite.log TEST_EXTENSIONS = @EXEEXT@ .test LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) am__set_b = \ case '$@' in \ */*) \ case '$*' in \ */*) b='$*';; \ *) b=`echo '$@' | sed 's/\.log$$//'`; \ esac;; \ *) \ b='$*';; \ esac am__test_logs1 = $(TESTS:=.log) am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) TEST_LOGS = $(am__test_logs2:.test.log=.log) TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ $(TEST_LOG_FLAGS) DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp \ $(top_srcdir)/test-driver DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPLDFLAGS = @APPLDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BPFCFLAGS = @BPFCFLAGS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CXX = @CXX@ CXX1XCXXFLAGS = @CXX1XCXXFLAGS@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXTRABPFCFLAGS = @EXTRABPFCFLAGS@ EXTRACFLAG = @EXTRACFLAG@ EXTRA_DEFS = @EXTRA_DEFS@ FGREP = @FGREP@ FILECMD = @FILECMD@ GREP = @GREP@ HAVE_CXX20 = @HAVE_CXX20@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ JANSSON_CFLAGS = @JANSSON_CFLAGS@ JANSSON_LIBS = @JANSSON_LIBS@ JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ JEMALLOC_LIBS = @JEMALLOC_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBBPF_CFLAGS = @LIBBPF_CFLAGS@ LIBBPF_LIBS = @LIBBPF_LIBS@ LIBBROTLIDEC_CFLAGS = @LIBBROTLIDEC_CFLAGS@ LIBBROTLIDEC_LIBS = @LIBBROTLIDEC_LIBS@ LIBBROTLIENC_CFLAGS = @LIBBROTLIENC_CFLAGS@ LIBBROTLIENC_LIBS = @LIBBROTLIENC_LIBS@ LIBCARES_CFLAGS = @LIBCARES_CFLAGS@ LIBCARES_LIBS = @LIBCARES_LIBS@ LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ LIBEV_CFLAGS = @LIBEV_CFLAGS@ LIBEV_LIBS = @LIBEV_LIBS@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@ LIBMRUBY_LIBS = @LIBMRUBY_LIBS@ LIBNGHTTP3_CFLAGS = @LIBNGHTTP3_CFLAGS@ LIBNGHTTP3_LIBS = @LIBNGHTTP3_LIBS@ LIBNGTCP2_CFLAGS = @LIBNGTCP2_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS = @LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_LIBS = @LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS = @LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS@ LIBNGTCP2_CRYPTO_LIBRESSL_LIBS = @LIBNGTCP2_CRYPTO_LIBRESSL_LIBS@ LIBNGTCP2_CRYPTO_OSSL_CFLAGS = @LIBNGTCP2_CRYPTO_OSSL_CFLAGS@ LIBNGTCP2_CRYPTO_OSSL_LIBS = @LIBNGTCP2_CRYPTO_OSSL_LIBS@ LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS = @LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ LIBNGTCP2_CRYPTO_QUICTLS_LIBS = @LIBNGTCP2_CRYPTO_QUICTLS_LIBS@ LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS = @LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS@ LIBNGTCP2_CRYPTO_WOLFSSL_LIBS = @LIBNGTCP2_CRYPTO_WOLFSSL_LIBS@ LIBNGTCP2_LIBS = @LIBNGTCP2_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBTOOL_LDFLAGS = @LIBTOOL_LDFLAGS@ LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ LIBXML2_LIBS = @LIBXML2_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_AGE = @LT_AGE@ LT_CURRENT = @LT_CURRENT@ LT_REVISION = @LT_REVISION@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ SYSTEMD_LIBS = @SYSTEMD_LIBS@ TESTLDADD = @TESTLDADD@ VERSION = @VERSION@ WARNCFLAGS = @WARNCFLAGS@ WARNCXXFLAGS = @WARNCXXFLAGS@ WOLFSSL_CFLAGS = @WOLFSSL_CFLAGS@ WOLFSSL_LIBS = @WOLFSSL_LIBS@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SUBDIRS = testdata EXTRA_DIST = CMakeLists.txt munit/COPYING OBJECTS = main.c nghttp2_pq_test.c nghttp2_map_test.c nghttp2_queue_test.c \ nghttp2_test_helper.c \ nghttp2_frame_test.c \ nghttp2_stream_test.c \ nghttp2_session_test.c \ nghttp2_hd_test.c \ nghttp2_alpn_test.c \ nghttp2_helper_test.c \ nghttp2_buf_test.c \ nghttp2_http_test.c \ nghttp2_extpri_test.c \ nghttp2_ratelim_test.c \ munit/munit.c HFILES = nghttp2_pq_test.h nghttp2_map_test.h nghttp2_queue_test.h \ nghttp2_session_test.h \ nghttp2_frame_test.h nghttp2_stream_test.h nghttp2_hd_test.h \ nghttp2_alpn_test.h nghttp2_helper_test.h \ nghttp2_assertion.h \ nghttp2_test_helper.h \ nghttp2_buf_test.h \ nghttp2_http_test.h \ nghttp2_extpri_test.h \ nghttp2_ratelim_test.h \ munit/munit.h main_SOURCES = $(HFILES) $(OBJECTS) # With static lib disabled and symbol hiding enabled, we have to link object # files directly because the tests use symbols not included in public API. @ENABLE_STATIC_FALSE@main_LDADD = ${top_builddir}/lib/.libs/*.o \ @ENABLE_STATIC_FALSE@ @TESTLDADD@ $(am__empty) @ENABLE_STATIC_TRUE@main_LDADD = ${top_builddir}/lib/libnghttp2.la \ @ENABLE_STATIC_TRUE@ @TESTLDADD@ $(am__empty) main_LDFLAGS = -static @ENABLE_FAILMALLOC_TRUE@failmalloc_SOURCES = failmalloc.c failmalloc_test.c failmalloc_test.h \ @ENABLE_FAILMALLOC_TRUE@ malloc_wrapper.c malloc_wrapper.h \ @ENABLE_FAILMALLOC_TRUE@ nghttp2_test_helper.c nghttp2_test_helper.h \ @ENABLE_FAILMALLOC_TRUE@ munit/munit.c munit/munit.h @ENABLE_FAILMALLOC_TRUE@failmalloc_LDADD = $(main_LDADD) @ENABLE_FAILMALLOC_TRUE@failmalloc_LDFLAGS = $(main_LDFLAGS) AM_CFLAGS = $(WARNCFLAGS) \ -I${top_srcdir}/lib \ -I${top_srcdir}/lib/includes \ -I${top_srcdir}/tests/munit \ -I${top_builddir}/lib/includes \ -DBUILDING_NGHTTP2 \ -DNGHTTP2_STATICLIB \ @DEFS@ all: all-recursive .SUFFIXES: .SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tests/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu tests/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-checkPROGRAMS: @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list munit/$(am__dirstamp): @$(MKDIR_P) munit @: > munit/$(am__dirstamp) munit/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) munit/$(DEPDIR) @: > munit/$(DEPDIR)/$(am__dirstamp) munit/munit.$(OBJEXT): munit/$(am__dirstamp) \ munit/$(DEPDIR)/$(am__dirstamp) failmalloc$(EXEEXT): $(failmalloc_OBJECTS) $(failmalloc_DEPENDENCIES) $(EXTRA_failmalloc_DEPENDENCIES) @rm -f failmalloc$(EXEEXT) $(AM_V_CCLD)$(failmalloc_LINK) $(failmalloc_OBJECTS) $(failmalloc_LDADD) $(LIBS) main$(EXEEXT): $(main_OBJECTS) $(main_DEPENDENCIES) $(EXTRA_main_DEPENDENCIES) @rm -f main$(EXEEXT) $(AM_V_CCLD)$(main_LINK) $(main_OBJECTS) $(main_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f munit/*.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/failmalloc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/failmalloc_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/malloc_wrapper.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_alpn_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_buf_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_extpri_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_frame_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_helper_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_http_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_map_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_pq_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_queue_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_ratelim_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_session_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_stream_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_test_helper.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@munit/$(DEPDIR)/munit.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags # Recover from deleted '.trs' file; this should ensure that # "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create # both 'foo.log' and 'foo.trs'. Break the recipe in two subshells # to avoid problems with "make -n". .log.trs: rm -f $< $@ $(MAKE) $(AM_MAKEFLAGS) $< # Leading 'am--fnord' is there to ensure the list of targets does not # expand to empty, as could happen e.g. with make check TESTS=''. am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) am--force-recheck: @: $(TEST_SUITE_LOG): $(TEST_LOGS) @$(am__set_TESTS_bases); \ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ redo_bases=`for i in $$bases; do \ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ done`; \ if test -n "$$redo_bases"; then \ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ if $(am__make_dryrun); then :; else \ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ fi; \ if test -n "$$am__remaking_logs"; then \ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ "recursion detected" >&2; \ elif test -n "$$redo_logs"; then \ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ fi; \ if $(am__make_dryrun); then :; else \ st=0; \ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ for i in $$redo_bases; do \ test -f $$i.trs && test -r $$i.trs \ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ test -f $$i.log && test -r $$i.log \ || { echo "$$errmsg $$i.log" >&2; st=1; }; \ done; \ test $$st -eq 0 || exit 1; \ fi @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ ws='[ ]'; \ results=`for b in $$bases; do echo $$b.trs; done`; \ test -n "$$results" || results=/dev/null; \ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ if test `expr $$fail + $$xpass + $$error` -eq 0; then \ success=true; \ else \ success=false; \ fi; \ br='==================='; br=$$br$$br$$br$$br; \ result_count () \ { \ if test x"$$1" = x"--maybe-color"; then \ maybe_colorize=yes; \ elif test x"$$1" = x"--no-color"; then \ maybe_colorize=no; \ else \ echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ shift; \ desc=$$1 count=$$2; \ if test $$maybe_colorize = yes && test $$count -gt 0; then \ color_start=$$3 color_end=$$std; \ else \ color_start= color_end=; \ fi; \ echo "$${color_start}# $$desc $$count$${color_end}"; \ }; \ create_testsuite_report () \ { \ result_count $$1 "TOTAL:" $$all "$$brg"; \ result_count $$1 "PASS: " $$pass "$$grn"; \ result_count $$1 "SKIP: " $$skip "$$blu"; \ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ result_count $$1 "FAIL: " $$fail "$$red"; \ result_count $$1 "XPASS:" $$xpass "$$red"; \ result_count $$1 "ERROR:" $$error "$$mgn"; \ }; \ { \ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ $(am__rst_title); \ create_testsuite_report --no-color; \ echo; \ echo ".. contents:: :depth: 2"; \ echo; \ for b in $$bases; do echo $$b; done \ | $(am__create_global_log); \ } >$(TEST_SUITE_LOG).tmp || exit 1; \ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ if $$success; then \ col="$$grn"; \ else \ col="$$red"; \ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ fi; \ echo "$${col}$$br$${std}"; \ echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}"; \ echo "$${col}$$br$${std}"; \ create_testsuite_report --maybe-color; \ echo "$$col$$br$$std"; \ if $$success; then :; else \ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ if test -n "$(PACKAGE_BUGREPORT)"; then \ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ fi; \ echo "$$col$$br$$std"; \ fi; \ $$success || exit 1 check-TESTS: $(check_PROGRAMS) @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ log_list=`for i in $$bases; do echo $$i.log; done`; \ trs_list=`for i in $$bases; do echo $$i.trs; done`; \ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ exit $$?; recheck: all $(check_PROGRAMS) @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ bases=`for i in $$bases; do echo $$i; done \ | $(am__list_recheck_tests)` || exit 1; \ log_list=`for i in $$bases; do echo $$i.log; done`; \ log_list=`echo $$log_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ am__force_recheck=am--force-recheck \ TEST_LOGS="$$log_list"; \ exit $$? main.log: main$(EXEEXT) @p='main$(EXEEXT)'; \ b='main'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) failmalloc.log: failmalloc$(EXEEXT) @p='failmalloc$(EXEEXT)'; \ b='failmalloc'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) .test.log: @p='$<'; \ $(am__set_b); \ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) @am__EXEEXT_TRUE@.test$(EXEEXT).log: @am__EXEEXT_TRUE@ @p='$<'; \ @am__EXEEXT_TRUE@ $(am__set_b); \ @am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ @am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ @am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ @am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) $(MAKE) $(AM_MAKEFLAGS) check-TESTS check: check-recursive all-am: Makefile installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -rm -f munit/$(DEPDIR)/$(am__dirstamp) -rm -f munit/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-checkPROGRAMS clean-generic clean-libtool \ mostlyclean-am distclean: distclean-recursive -rm -f ./$(DEPDIR)/failmalloc.Po -rm -f ./$(DEPDIR)/failmalloc_test.Po -rm -f ./$(DEPDIR)/main.Po -rm -f ./$(DEPDIR)/malloc_wrapper.Po -rm -f ./$(DEPDIR)/nghttp2_alpn_test.Po -rm -f ./$(DEPDIR)/nghttp2_buf_test.Po -rm -f ./$(DEPDIR)/nghttp2_extpri_test.Po -rm -f ./$(DEPDIR)/nghttp2_frame_test.Po -rm -f ./$(DEPDIR)/nghttp2_hd_test.Po -rm -f ./$(DEPDIR)/nghttp2_helper_test.Po -rm -f ./$(DEPDIR)/nghttp2_http_test.Po -rm -f ./$(DEPDIR)/nghttp2_map_test.Po -rm -f ./$(DEPDIR)/nghttp2_pq_test.Po -rm -f ./$(DEPDIR)/nghttp2_queue_test.Po -rm -f ./$(DEPDIR)/nghttp2_ratelim_test.Po -rm -f ./$(DEPDIR)/nghttp2_session_test.Po -rm -f ./$(DEPDIR)/nghttp2_stream_test.Po -rm -f ./$(DEPDIR)/nghttp2_test_helper.Po -rm -f munit/$(DEPDIR)/munit.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f ./$(DEPDIR)/failmalloc.Po -rm -f ./$(DEPDIR)/failmalloc_test.Po -rm -f ./$(DEPDIR)/main.Po -rm -f ./$(DEPDIR)/malloc_wrapper.Po -rm -f ./$(DEPDIR)/nghttp2_alpn_test.Po -rm -f ./$(DEPDIR)/nghttp2_buf_test.Po -rm -f ./$(DEPDIR)/nghttp2_extpri_test.Po -rm -f ./$(DEPDIR)/nghttp2_frame_test.Po -rm -f ./$(DEPDIR)/nghttp2_hd_test.Po -rm -f ./$(DEPDIR)/nghttp2_helper_test.Po -rm -f ./$(DEPDIR)/nghttp2_http_test.Po -rm -f ./$(DEPDIR)/nghttp2_map_test.Po -rm -f ./$(DEPDIR)/nghttp2_pq_test.Po -rm -f ./$(DEPDIR)/nghttp2_queue_test.Po -rm -f ./$(DEPDIR)/nghttp2_ratelim_test.Po -rm -f ./$(DEPDIR)/nghttp2_session_test.Po -rm -f ./$(DEPDIR)/nghttp2_stream_test.Po -rm -f ./$(DEPDIR)/nghttp2_test_helper.Po -rm -f munit/$(DEPDIR)/munit.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(am__recursive_targets) check-am install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ am--depfiles check check-TESTS check-am clean \ clean-checkPROGRAMS clean-generic clean-libtool cscopelist-am \ ctags ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ recheck tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nghttp2-1.68.0/tests/PaxHeaders/nghttp2_session_test.c0000644000000000000000000000013215077107271017757 xustar0030 mtime=1761382073.008444069 30 atime=1761382080.107411476 30 ctime=1761382108.095303286 nghttp2-1.68.0/tests/nghttp2_session_test.c0000644000175100017510000140353715077107271020364 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_session_test.h" #include #include #include "munit.h" #include "nghttp2_session.h" #include "nghttp2_stream.h" #include "nghttp2_net.h" #include "nghttp2_helper.h" #include "nghttp2_test_helper.h" #include "nghttp2_assertion.h" #include "nghttp2_priority_spec.h" #include "nghttp2_extpri.h" static const MunitTest tests[] = { munit_void_test(test_nghttp2_session_recv), munit_void_test(test_nghttp2_session_recv_invalid_stream_id), munit_void_test(test_nghttp2_session_recv_invalid_frame), munit_void_test(test_nghttp2_session_recv_eof), munit_void_test(test_nghttp2_session_recv_data), munit_void_test(test_nghttp2_session_recv_data_no_auto_flow_control), munit_void_test(test_nghttp2_session_recv_continuation), munit_void_test(test_nghttp2_session_recv_headers_with_priority), munit_void_test(test_nghttp2_session_recv_headers_with_padding), munit_void_test(test_nghttp2_session_recv_headers_early_response), munit_void_test(test_nghttp2_session_recv_headers_for_closed_stream), munit_void_test(test_nghttp2_session_recv_headers_with_extpri), munit_void_test(test_nghttp2_session_server_recv_push_response), munit_void_test(test_nghttp2_session_recv_premature_headers), munit_void_test(test_nghttp2_session_recv_unknown_frame), munit_void_test(test_nghttp2_session_recv_unexpected_continuation), munit_void_test(test_nghttp2_session_recv_settings_header_table_size), munit_void_test(test_nghttp2_session_recv_too_large_frame_length), munit_void_test(test_nghttp2_session_recv_extension), munit_void_test(test_nghttp2_session_recv_altsvc), munit_void_test(test_nghttp2_session_recv_origin), munit_void_test(test_nghttp2_session_recv_priority_update), munit_void_test(test_nghttp2_session_continue), munit_void_test(test_nghttp2_session_add_frame), munit_void_test(test_nghttp2_session_on_request_headers_received), munit_void_test(test_nghttp2_session_on_response_headers_received), munit_void_test(test_nghttp2_session_on_headers_received), munit_void_test(test_nghttp2_session_on_push_response_headers_received), munit_void_test(test_nghttp2_session_on_rst_stream_received), munit_void_test(test_nghttp2_session_on_settings_received), munit_void_test(test_nghttp2_session_on_push_promise_received), munit_void_test(test_nghttp2_session_on_ping_received), munit_void_test(test_nghttp2_session_on_goaway_received), munit_void_test(test_nghttp2_session_on_window_update_received), munit_void_test(test_nghttp2_session_on_data_received), munit_void_test(test_nghttp2_session_on_data_received_fail_fast), munit_void_test(test_nghttp2_session_on_altsvc_received), munit_void_test(test_nghttp2_session_send_headers_start_stream), munit_void_test(test_nghttp2_session_send_headers_reply), munit_void_test(test_nghttp2_session_send_headers_frame_size_error), munit_void_test(test_nghttp2_session_send_headers_push_reply), munit_void_test(test_nghttp2_session_send_rst_stream), munit_void_test(test_nghttp2_session_send_push_promise), munit_void_test(test_nghttp2_session_is_my_stream_id), munit_void_test(test_nghttp2_session_upgrade2), munit_void_test(test_nghttp2_submit_data), munit_void_test(test_nghttp2_submit_data_read_length_too_large), munit_void_test(test_nghttp2_submit_data_read_length_smallest), munit_void_test(test_nghttp2_submit_data_twice), munit_void_test(test_nghttp2_submit_request_with_data), munit_void_test(test_nghttp2_submit_request_without_data), munit_void_test(test_nghttp2_submit_response_with_data), munit_void_test(test_nghttp2_submit_response_without_data), munit_void_test(test_nghttp2_submit_response_push_response), munit_void_test(test_nghttp2_submit_trailer), munit_void_test(test_nghttp2_submit_headers_start_stream), munit_void_test(test_nghttp2_submit_headers_reply), munit_void_test(test_nghttp2_submit_headers_push_reply), munit_void_test(test_nghttp2_submit_headers), munit_void_test(test_nghttp2_submit_headers_continuation), munit_void_test(test_nghttp2_submit_headers_continuation_extra_large), munit_void_test(test_nghttp2_submit_settings), munit_void_test(test_nghttp2_submit_settings_update_local_window_size), munit_void_test(test_nghttp2_submit_settings_multiple_times), munit_void_test(test_nghttp2_submit_push_promise), munit_void_test(test_nghttp2_submit_window_update), munit_void_test(test_nghttp2_submit_window_update_local_window_size), munit_void_test(test_nghttp2_submit_shutdown_notice), munit_void_test(test_nghttp2_submit_invalid_nv), munit_void_test(test_nghttp2_submit_extension), munit_void_test(test_nghttp2_submit_altsvc), munit_void_test(test_nghttp2_submit_origin), munit_void_test(test_nghttp2_submit_priority_update), munit_void_test(test_nghttp2_submit_rst_stream), munit_void_test(test_nghttp2_session_open_stream), munit_void_test(test_nghttp2_session_get_next_ob_item), munit_void_test(test_nghttp2_session_pop_next_ob_item), munit_void_test(test_nghttp2_session_reply_fail), munit_void_test(test_nghttp2_session_max_concurrent_streams), munit_void_test(test_nghttp2_session_stop_data_with_rst_stream), munit_void_test(test_nghttp2_session_defer_data), munit_void_test(test_nghttp2_session_flow_control), munit_void_test(test_nghttp2_session_flow_control_data_recv), munit_void_test(test_nghttp2_session_flow_control_data_with_padding_recv), munit_void_test(test_nghttp2_session_data_read_temporal_failure), munit_void_test(test_nghttp2_session_on_stream_close), munit_void_test(test_nghttp2_session_on_ctrl_not_send), munit_void_test(test_nghttp2_session_get_outbound_queue_size), munit_void_test(test_nghttp2_session_get_effective_local_window_size), munit_void_test(test_nghttp2_session_set_option), munit_void_test(test_nghttp2_session_data_backoff_by_high_pri_frame), munit_void_test(test_nghttp2_session_pack_data_with_padding), munit_void_test(test_nghttp2_session_pack_headers_with_padding), munit_void_test(test_nghttp2_pack_settings_payload), munit_void_test(test_nghttp2_session_stream_get_state), munit_void_test(test_nghttp2_session_find_stream), munit_void_test(test_nghttp2_session_graceful_shutdown), munit_void_test(test_nghttp2_session_on_header_temporal_failure), munit_void_test(test_nghttp2_session_recv_client_magic), munit_void_test(test_nghttp2_session_delete_data_item), munit_void_test(test_nghttp2_session_open_idle_stream), munit_void_test(test_nghttp2_session_cancel_reserved_remote), munit_void_test(test_nghttp2_session_reset_pending_headers), munit_void_test(test_nghttp2_session_send_data_callback), munit_void_test(test_nghttp2_session_on_begin_headers_temporal_failure), munit_void_test(test_nghttp2_session_defer_then_close), munit_void_test(test_nghttp2_session_detach_item_from_closed_stream), munit_void_test(test_nghttp2_session_flooding), munit_void_test(test_nghttp2_session_change_extpri_stream_priority), munit_void_test(test_nghttp2_session_set_local_window_size), munit_void_test(test_nghttp2_session_cancel_from_before_frame_send), munit_void_test(test_nghttp2_session_too_many_settings), munit_void_test(test_nghttp2_session_removed_closed_stream), munit_void_test(test_nghttp2_session_pause_data), munit_void_test(test_nghttp2_session_no_closed_streams), munit_void_test(test_nghttp2_session_set_stream_user_data), munit_void_test(test_nghttp2_session_no_rfc7540_priorities), munit_void_test(test_nghttp2_session_stream_reset_ratelim), munit_void_test(test_nghttp2_http_mandatory_headers), munit_void_test(test_nghttp2_http_content_length), munit_void_test(test_nghttp2_http_content_length_mismatch), munit_void_test(test_nghttp2_http_non_final_response), munit_void_test(test_nghttp2_http_trailer_headers), munit_void_test(test_nghttp2_http_ignore_regular_header), munit_void_test(test_nghttp2_http_ignore_content_length), munit_void_test(test_nghttp2_http_record_request_method), munit_void_test(test_nghttp2_http_push_promise), munit_void_test(test_nghttp2_http_head_method_upgrade_workaround), munit_void_test( test_nghttp2_http_no_rfc9113_leading_and_trailing_ws_validation), munit_test_end(), }; const MunitSuite session_suite = { "/session", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE, }; typedef struct { uint8_t buf[65535]; size_t length; } accumulator; typedef struct { uint8_t data[8192]; uint8_t *datamark; uint8_t *datalimit; size_t feedseq[8192]; size_t seqidx; } scripted_data_feed; typedef struct { accumulator *acc; scripted_data_feed *df; int frame_recv_cb_called, invalid_frame_recv_cb_called; uint8_t recv_frame_type; nghttp2_frame_hd recv_frame_hd; int frame_send_cb_called; uint8_t sent_frame_type; int before_frame_send_cb_called; int frame_not_send_cb_called; uint8_t not_sent_frame_type; int not_sent_error; int stream_close_cb_called; uint32_t stream_close_error_code; size_t data_source_length; int32_t stream_id; size_t block_count; int data_chunk_recv_cb_called; const nghttp2_frame *frame; size_t fixed_sendlen; int header_cb_called; int invalid_header_cb_called; int begin_headers_cb_called; nghttp2_nv nv; size_t data_chunk_len; size_t padlen; int begin_frame_cb_called; nghttp2_buf scratchbuf; size_t data_source_read_cb_paused; } my_user_data; static const nghttp2_nv reqnv[] = { MAKE_NV(":method", "GET"), MAKE_NV(":path", "/"), MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"), }; static const nghttp2_nv resnv[] = { MAKE_NV(":status", "200"), }; static const nghttp2_nv trailernv[] = { // from http://tools.ietf.org/html/rfc6249#section-7 MAKE_NV("digest", "SHA-256=" "MWVkMWQxYTRiMzk5MDQ0MzI3NGU5NDEyZTk5OWY1ZGFmNzgyZTJlODYz" "YjRjYzFhOTlmNTQwYzI2M2QwM2U2MQ=="), }; static void scripted_data_feed_init2(scripted_data_feed *df, nghttp2_bufs *bufs) { nghttp2_buf_chain *ci; nghttp2_buf *buf; uint8_t *ptr; size_t len; memset(df, 0, sizeof(scripted_data_feed)); ptr = df->data; len = 0; for (ci = bufs->head; ci; ci = ci->next) { buf = &ci->buf; ptr = nghttp2_cpymem(ptr, buf->pos, nghttp2_buf_len(buf)); len += nghttp2_buf_len(buf); } df->datamark = df->data; df->datalimit = df->data + len; df->feedseq[0] = len; } static nghttp2_ssize null_send_callback(nghttp2_session *session, const uint8_t *data, size_t len, int flags, void *user_data) { (void)session; (void)data; (void)flags; (void)user_data; return (nghttp2_ssize)len; } static nghttp2_ssize fail_send_callback(nghttp2_session *session, const uint8_t *data, size_t len, int flags, void *user_data) { (void)session; (void)data; (void)len; (void)flags; (void)user_data; return NGHTTP2_ERR_CALLBACK_FAILURE; } static nghttp2_ssize fixed_bytes_send_callback(nghttp2_session *session, const uint8_t *data, size_t len, int flags, void *user_data) { size_t fixed_sendlen = ((my_user_data *)user_data)->fixed_sendlen; (void)session; (void)data; (void)flags; return (nghttp2_ssize)(fixed_sendlen < len ? fixed_sendlen : len); } static nghttp2_ssize scripted_recv_callback(nghttp2_session *session, uint8_t *data, size_t len, int flags, void *user_data) { scripted_data_feed *df = ((my_user_data *)user_data)->df; size_t wlen = df->feedseq[df->seqidx] > len ? len : df->feedseq[df->seqidx]; (void)session; (void)flags; memcpy(data, df->datamark, wlen); df->datamark += wlen; df->feedseq[df->seqidx] -= wlen; if (df->feedseq[df->seqidx] == 0) { ++df->seqidx; } return (nghttp2_ssize)wlen; } static nghttp2_ssize eof_recv_callback(nghttp2_session *session, uint8_t *data, size_t len, int flags, void *user_data) { (void)session; (void)data; (void)len; (void)flags; (void)user_data; return NGHTTP2_ERR_EOF; } static nghttp2_ssize accumulator_send_callback(nghttp2_session *session, const uint8_t *buf, size_t len, int flags, void *user_data) { accumulator *acc = ((my_user_data *)user_data)->acc; (void)session; (void)flags; assert(acc->length + len < sizeof(acc->buf)); memcpy(acc->buf + acc->length, buf, len); acc->length += len; return (nghttp2_ssize)len; } static int on_begin_frame_callback(nghttp2_session *session, const nghttp2_frame_hd *hd, void *user_data) { my_user_data *ud = (my_user_data *)user_data; (void)session; (void)hd; ++ud->begin_frame_cb_called; return 0; } static int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { my_user_data *ud = (my_user_data *)user_data; (void)session; ++ud->frame_recv_cb_called; ud->recv_frame_type = frame->hd.type; ud->recv_frame_hd = frame->hd; return 0; } static int on_invalid_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, void *user_data) { my_user_data *ud = (my_user_data *)user_data; (void)session; (void)frame; (void)lib_error_code; ++ud->invalid_frame_recv_cb_called; return 0; } static int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { my_user_data *ud = (my_user_data *)user_data; (void)session; ++ud->frame_send_cb_called; ud->sent_frame_type = frame->hd.type; return 0; } static int on_frame_not_send_callback(nghttp2_session *session, const nghttp2_frame *frame, int lib_error, void *user_data) { my_user_data *ud = (my_user_data *)user_data; (void)session; ++ud->frame_not_send_cb_called; ud->not_sent_frame_type = frame->hd.type; ud->not_sent_error = lib_error; return 0; } static int cancel_before_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { my_user_data *ud = (my_user_data *)user_data; (void)session; (void)frame; ++ud->before_frame_send_cb_called; return NGHTTP2_ERR_CANCEL; } static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data) { my_user_data *ud = (my_user_data *)user_data; (void)session; (void)flags; (void)stream_id; (void)data; ++ud->data_chunk_recv_cb_called; ud->data_chunk_len = len; return 0; } static int pause_on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data) { my_user_data *ud = (my_user_data *)user_data; (void)session; (void)flags; (void)stream_id; (void)data; (void)len; ++ud->data_chunk_recv_cb_called; return NGHTTP2_ERR_PAUSE; } static nghttp2_ssize select_padding_callback(nghttp2_session *session, const nghttp2_frame *frame, size_t max_payloadlen, void *user_data) { my_user_data *ud = (my_user_data *)user_data; (void)session; return (nghttp2_ssize)nghttp2_min_size(max_payloadlen, frame->hd.length + ud->padlen); } static nghttp2_ssize too_large_data_source_length_callback( nghttp2_session *session, uint8_t frame_type, int32_t stream_id, int32_t session_remote_window_size, int32_t stream_remote_window_size, uint32_t remote_max_frame_size, void *user_data) { (void)session; (void)frame_type; (void)stream_id; (void)session_remote_window_size; (void)stream_remote_window_size; (void)remote_max_frame_size; (void)user_data; return NGHTTP2_MAX_FRAME_SIZE_MAX + 1; } static nghttp2_ssize smallest_length_data_source_length_callback( nghttp2_session *session, uint8_t frame_type, int32_t stream_id, int32_t session_remote_window_size, int32_t stream_remote_window_size, uint32_t remote_max_frame_size, void *user_data) { (void)session; (void)frame_type; (void)stream_id; (void)session_remote_window_size; (void)stream_remote_window_size; (void)remote_max_frame_size; (void)user_data; return 1; } static nghttp2_ssize fixed_length_data_source_read_callback( nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len, uint32_t *data_flags, nghttp2_data_source *source, void *user_data) { my_user_data *ud = (my_user_data *)user_data; size_t wlen; (void)session; (void)stream_id; (void)buf; (void)source; if (len < ud->data_source_length) { wlen = len; } else { wlen = ud->data_source_length; } ud->data_source_length -= wlen; if (ud->data_source_length == 0) { *data_flags |= NGHTTP2_DATA_FLAG_EOF; } return (nghttp2_ssize)wlen; } static nghttp2_ssize temporal_failure_data_source_read_callback( nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len, uint32_t *data_flags, nghttp2_data_source *source, void *user_data) { (void)session; (void)stream_id; (void)buf; (void)len; (void)data_flags; (void)source; (void)user_data; return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } static nghttp2_ssize fail_data_source_read_callback(nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len, uint32_t *data_flags, nghttp2_data_source *source, void *user_data) { (void)session; (void)stream_id; (void)buf; (void)len; (void)data_flags; (void)source; (void)user_data; return NGHTTP2_ERR_CALLBACK_FAILURE; } static nghttp2_ssize no_end_stream_data_source_read_callback( nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len, uint32_t *data_flags, nghttp2_data_source *source, void *user_data) { (void)session; (void)stream_id; (void)buf; (void)len; (void)source; (void)user_data; *data_flags |= NGHTTP2_DATA_FLAG_EOF | NGHTTP2_DATA_FLAG_NO_END_STREAM; return 0; } static nghttp2_ssize no_copy_data_source_read_callback( nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len, uint32_t *data_flags, nghttp2_data_source *source, void *user_data) { my_user_data *ud = (my_user_data *)user_data; size_t wlen; (void)session; (void)stream_id; (void)buf; (void)source; if (len < ud->data_source_length) { wlen = len; } else { wlen = ud->data_source_length; } ud->data_source_length -= wlen; *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY; if (ud->data_source_length == 0) { *data_flags |= NGHTTP2_DATA_FLAG_EOF; } return (nghttp2_ssize)wlen; } static int send_data_callback(nghttp2_session *session, nghttp2_frame *frame, const uint8_t *framehd, size_t length, nghttp2_data_source *source, void *user_data) { accumulator *acc = ((my_user_data *)user_data)->acc; (void)session; (void)source; memcpy(acc->buf + acc->length, framehd, NGHTTP2_FRAME_HDLEN); acc->length += NGHTTP2_FRAME_HDLEN; if (frame->data.padlen) { *(acc->buf + acc->length++) = (uint8_t)(frame->data.padlen - 1); } acc->length += length; if (frame->data.padlen) { acc->length += frame->data.padlen - 1; } return 0; } static nghttp2_ssize block_count_send_callback(nghttp2_session *session, const uint8_t *data, size_t len, int flags, void *user_data) { my_user_data *ud = (my_user_data *)user_data; (void)session; (void)data; (void)flags; if (ud->block_count == 0) { return NGHTTP2_ERR_WOULDBLOCK; } --ud->block_count; return (nghttp2_ssize)len; } static int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *user_data) { my_user_data *ud = (my_user_data *)user_data; (void)session; (void)flags; ++ud->header_cb_called; ud->nv.name = (uint8_t *)name; ud->nv.namelen = namelen; ud->nv.value = (uint8_t *)value; ud->nv.valuelen = valuelen; ud->frame = frame; return 0; } static int pause_on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *user_data) { on_header_callback(session, frame, name, namelen, value, valuelen, flags, user_data); return NGHTTP2_ERR_PAUSE; } static int temporal_failure_on_header_callback( nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *user_data) { on_header_callback(session, frame, name, namelen, value, valuelen, flags, user_data); return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } static int on_invalid_header_callback(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *user_data) { my_user_data *ud = (my_user_data *)user_data; (void)session; (void)flags; ++ud->invalid_header_cb_called; ud->nv.name = (uint8_t *)name; ud->nv.namelen = namelen; ud->nv.value = (uint8_t *)value; ud->nv.valuelen = valuelen; ud->frame = frame; return 0; } static int pause_on_invalid_header_callback(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *user_data) { on_invalid_header_callback(session, frame, name, namelen, value, valuelen, flags, user_data); return NGHTTP2_ERR_PAUSE; } static int reset_on_invalid_header_callback(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *user_data) { on_invalid_header_callback(session, frame, name, namelen, value, valuelen, flags, user_data); return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } static int term_on_invalid_header_callback2(nghttp2_session *session, const nghttp2_frame *frame, nghttp2_rcbuf *name, nghttp2_rcbuf *value, uint8_t flags, void *user_data) { (void)session; (void)frame; (void)name; (void)value; (void)flags; (void)user_data; return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } static int on_begin_headers_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { my_user_data *ud = (my_user_data *)user_data; (void)session; (void)frame; ++ud->begin_headers_cb_called; return 0; } static int temporal_failure_on_begin_headers_callback( nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { on_begin_headers_callback(session, frame, user_data); return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } static nghttp2_ssize defer_data_source_read_callback(nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len, uint32_t *data_flags, nghttp2_data_source *source, void *user_data) { (void)session; (void)stream_id; (void)buf; (void)len; (void)data_flags; (void)source; (void)user_data; return NGHTTP2_ERR_DEFERRED; } static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *user_data) { my_user_data *my_data = (my_user_data *)user_data; (void)session; (void)stream_id; (void)error_code; ++my_data->stream_close_cb_called; my_data->stream_close_error_code = error_code; return 0; } static int fatal_error_on_stream_close_callback(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *user_data) { on_stream_close_callback(session, stream_id, error_code, user_data); return NGHTTP2_ERR_CALLBACK_FAILURE; } static nghttp2_ssize pack_extension_callback(nghttp2_session *session, uint8_t *buf, size_t len, const nghttp2_frame *frame, void *user_data) { nghttp2_buf *p = frame->ext.payload; (void)session; (void)len; (void)user_data; memcpy(buf, p->pos, nghttp2_buf_len(p)); return (nghttp2_ssize)nghttp2_buf_len(p); } static int on_extension_chunk_recv_callback(nghttp2_session *session, const nghttp2_frame_hd *hd, const uint8_t *data, size_t len, void *user_data) { my_user_data *my_data = (my_user_data *)user_data; nghttp2_buf *buf = &my_data->scratchbuf; (void)session; (void)hd; buf->last = nghttp2_cpymem(buf->last, data, len); return 0; } static int cancel_on_extension_chunk_recv_callback(nghttp2_session *session, const nghttp2_frame_hd *hd, const uint8_t *data, size_t len, void *user_data) { (void)session; (void)hd; (void)data; (void)len; (void)user_data; return NGHTTP2_ERR_CANCEL; } static int unpack_extension_callback(nghttp2_session *session, void **payload, const nghttp2_frame_hd *hd, void *user_data) { my_user_data *my_data = (my_user_data *)user_data; nghttp2_buf *buf = &my_data->scratchbuf; (void)session; (void)hd; *payload = buf; return 0; } static int cancel_unpack_extension_callback(nghttp2_session *session, void **payload, const nghttp2_frame_hd *hd, void *user_data) { (void)session; (void)payload; (void)hd; (void)user_data; return NGHTTP2_ERR_CANCEL; } static void rand_callback(uint8_t *data, size_t datalen) { memset(data, 0xfe, datalen); } static nghttp2_settings_entry *dup_iv(const nghttp2_settings_entry *iv, size_t niv) { return nghttp2_frame_iv_copy(iv, niv, nghttp2_mem_default()); } static nghttp2_priority_spec pri_spec_default = {0, NGHTTP2_DEFAULT_WEIGHT, 0}; void test_nghttp2_session_recv(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; scripted_data_feed df; my_user_data user_data; nghttp2_bufs bufs; size_t framelen; nghttp2_frame frame; size_t i; nghttp2_outbound_item *item; nghttp2_nv *nva; size_t nvlen; nghttp2_hd_deflater deflater; int rv; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.recv_callback2 = scripted_recv_callback; callbacks.on_frame_recv_callback = on_frame_recv_callback; callbacks.on_begin_frame_callback = on_begin_frame_callback; callbacks.rand_callback = rand_callback; user_data.df = &df; nghttp2_session_server_new(&session, &callbacks, &user_data); nghttp2_hd_deflate_init(&deflater, mem); nvlen = ARRLEN(reqnv); nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); assert_int(0, ==, rv); scripted_data_feed_init2(&df, &bufs); framelen = nghttp2_bufs_len(&bufs); /* Send 1 byte per each read */ for (i = 0; i < framelen; ++i) { df.feedseq[i] = 1; } nghttp2_frame_headers_free(&frame.headers, mem); user_data.frame_recv_cb_called = 0; user_data.begin_frame_cb_called = 0; while (df.seqidx < framelen) { assert_int(0, ==, nghttp2_session_recv(session)); } assert_int(1, ==, user_data.frame_recv_cb_called); assert_int(1, ==, user_data.begin_frame_cb_called); nghttp2_bufs_reset(&bufs); /* Receive PRIORITY */ nghttp2_frame_priority_init(&frame.priority, 5, &pri_spec_default); nghttp2_frame_pack_priority(&bufs, &frame.priority); nghttp2_frame_priority_free(&frame.priority); scripted_data_feed_init2(&df, &bufs); user_data.frame_recv_cb_called = 0; user_data.begin_frame_cb_called = 0; assert_int(0, ==, nghttp2_session_recv(session)); assert_int(0, ==, user_data.frame_recv_cb_called); assert_int(1, ==, user_data.begin_frame_cb_called); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); /* Some tests for frame too large */ nghttp2_session_server_new(&session, &callbacks, &user_data); /* Receive PING with too large payload */ nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL); nghttp2_frame_pack_ping(&bufs, &frame.ping); /* Add extra 16 bytes */ nghttp2_bufs_seek_last_present(&bufs); assert(nghttp2_buf_len(&bufs.cur->buf) >= 16); bufs.cur->buf.last += 16; nghttp2_put_uint32be( bufs.cur->buf.pos, (uint32_t)(((frame.hd.length + 16) << 8) + bufs.cur->buf.pos[3])); nghttp2_frame_ping_free(&frame.ping); scripted_data_feed_init2(&df, &bufs); user_data.frame_recv_cb_called = 0; user_data.begin_frame_cb_called = 0; assert_int(0, ==, nghttp2_session_recv(session)); assert_int(0, ==, user_data.frame_recv_cb_called); assert_int(0, ==, user_data.begin_frame_cb_called); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_uint32(NGHTTP2_FRAME_SIZE_ERROR, ==, item->frame.goaway.error_code); assert_int(0, ==, nghttp2_session_send(session)); nghttp2_bufs_free(&bufs); nghttp2_session_del(session); } void test_nghttp2_session_recv_invalid_stream_id(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; scripted_data_feed df; my_user_data user_data; nghttp2_bufs bufs; nghttp2_frame frame; nghttp2_hd_deflater deflater; int rv; nghttp2_mem *mem; nghttp2_nv *nva; size_t nvlen; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.recv_callback2 = scripted_recv_callback; callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; user_data.df = &df; user_data.invalid_frame_recv_cb_called = 0; nghttp2_session_server_new(&session, &callbacks, &user_data); nghttp2_hd_deflate_init(&deflater, mem); nvlen = ARRLEN(reqnv); nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2, NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); assert_int(0, ==, rv); assert_size(0, <, nghttp2_bufs_len(&bufs)); scripted_data_feed_init2(&df, &bufs); nghttp2_frame_headers_free(&frame.headers, mem); assert_int(0, ==, nghttp2_session_recv(session)); assert_int(1, ==, user_data.invalid_frame_recv_cb_called); nghttp2_bufs_free(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); } void test_nghttp2_session_recv_invalid_frame(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; scripted_data_feed df; my_user_data user_data; nghttp2_bufs bufs; nghttp2_frame frame; nghttp2_nv *nva; size_t nvlen; nghttp2_hd_deflater deflater; int rv; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.recv_callback2 = scripted_recv_callback; callbacks.send_callback2 = null_send_callback; callbacks.on_frame_send_callback = on_frame_send_callback; user_data.df = &df; user_data.frame_send_cb_called = 0; nghttp2_session_server_new(&session, &callbacks, &user_data); nghttp2_hd_deflate_init(&deflater, mem); nvlen = ARRLEN(reqnv); nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); assert_int(0, ==, rv); assert_size(0, <, nghttp2_bufs_len(&bufs)); scripted_data_feed_init2(&df, &bufs); assert_int(0, ==, nghttp2_session_recv(session)); assert_int(0, ==, nghttp2_session_send(session)); assert_int(0, ==, user_data.frame_send_cb_called); /* Receive exactly same bytes of HEADERS is treated as error, because it has * pseudo headers and without END_STREAM flag set */ scripted_data_feed_init2(&df, &bufs); assert_int(0, ==, nghttp2_session_recv(session)); assert_int(0, ==, nghttp2_session_send(session)); assert_int(1, ==, user_data.frame_send_cb_called); assert_uint8(NGHTTP2_GOAWAY, ==, user_data.sent_frame_type); nghttp2_bufs_free(&bufs); nghttp2_frame_headers_free(&frame.headers, mem); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); } void test_nghttp2_session_recv_eof(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.recv_callback2 = eof_recv_callback; nghttp2_session_client_new(&session, &callbacks, NULL); assert_int(NGHTTP2_ERR_EOF, ==, nghttp2_session_recv(session)); nghttp2_session_del(session); } void test_nghttp2_session_recv_data(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; uint8_t data[8092]; nghttp2_ssize rv; nghttp2_outbound_item *item; nghttp2_stream *stream; nghttp2_frame_hd hd; int i; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; callbacks.on_frame_recv_callback = on_frame_recv_callback; callbacks.on_frame_send_callback = on_frame_send_callback; nghttp2_session_client_new(&session, &callbacks, &ud); /* Create DATA frame with length 4KiB */ memset(data, 0, sizeof(data)); hd.length = 4096; hd.type = NGHTTP2_DATA; hd.flags = NGHTTP2_FLAG_NONE; hd.stream_id = 1; nghttp2_frame_pack_frame_hd(data, &hd); /* stream 1 is not opened, so it must be responded with connection error. This is not mandated by the spec */ ud.data_chunk_recv_cb_called = 0; ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, data, NGHTTP2_FRAME_HDLEN + 4096); assert_ptrdiff(NGHTTP2_FRAME_HDLEN + 4096, ==, rv); assert_int(0, ==, ud.data_chunk_recv_cb_called); assert_int(0, ==, ud.frame_recv_cb_called); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); nghttp2_session_del(session); nghttp2_session_client_new(&session, &callbacks, &ud); /* Create stream 1 with CLOSING state. DATA is ignored. */ stream = open_sent_stream2(session, 1, NGHTTP2_STREAM_CLOSING); /* Set initial window size 16383 to check stream flow control, isolating it from the connection flow control */ stream->local_window_size = 16383; ud.data_chunk_recv_cb_called = 0; ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, data, NGHTTP2_FRAME_HDLEN + 4096); assert_ptrdiff(NGHTTP2_FRAME_HDLEN + 4096, ==, rv); assert_int(0, ==, ud.data_chunk_recv_cb_called); assert_int(0, ==, ud.frame_recv_cb_called); item = nghttp2_session_get_next_ob_item(session); assert_null(item); /* This is normal case. DATA is acceptable. */ stream->state = NGHTTP2_STREAM_OPENED; ud.data_chunk_recv_cb_called = 0; ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, data, NGHTTP2_FRAME_HDLEN + 4096); assert_ptrdiff(NGHTTP2_FRAME_HDLEN + 4096, ==, rv); assert_int(1, ==, ud.data_chunk_recv_cb_called); assert_int(1, ==, ud.frame_recv_cb_called); assert_null(nghttp2_session_get_next_ob_item(session)); ud.data_chunk_recv_cb_called = 0; ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, data, NGHTTP2_FRAME_HDLEN + 4096); assert_ptrdiff(NGHTTP2_FRAME_HDLEN + 4096, ==, rv); /* Now we got data more than initial-window-size / 2, WINDOW_UPDATE must be queued */ assert_int(1, ==, ud.data_chunk_recv_cb_called); assert_int(1, ==, ud.frame_recv_cb_called); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_WINDOW_UPDATE, ==, item->frame.hd.type); assert_int32(1, ==, item->frame.window_update.hd.stream_id); assert_int(0, ==, nghttp2_session_send(session)); /* Set initial window size to 1MiB, so that we can check connection flow control individually */ stream->local_window_size = 1 << 20; /* Connection flow control takes into account DATA which is received in the error condition. We have received 4096 * 4 bytes of DATA. Additional 4 DATA frames, connection flow control will kick in. */ for (i = 0; i < 5; ++i) { rv = nghttp2_session_mem_recv2(session, data, NGHTTP2_FRAME_HDLEN + 4096); assert_ptrdiff(NGHTTP2_FRAME_HDLEN + 4096, ==, rv); } item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_WINDOW_UPDATE, ==, item->frame.hd.type); assert_int32(0, ==, item->frame.window_update.hd.stream_id); assert_int(0, ==, nghttp2_session_send(session)); /* Reception of DATA with stream ID = 0 causes connection error */ hd.length = 4096; hd.type = NGHTTP2_DATA; hd.flags = NGHTTP2_FLAG_NONE; hd.stream_id = 0; nghttp2_frame_pack_frame_hd(data, &hd); ud.data_chunk_recv_cb_called = 0; ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, data, NGHTTP2_FRAME_HDLEN + 4096); assert_ptrdiff(NGHTTP2_FRAME_HDLEN + 4096, ==, rv); assert_int(0, ==, ud.data_chunk_recv_cb_called); assert_int(0, ==, ud.frame_recv_cb_called); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_uint32(NGHTTP2_PROTOCOL_ERROR, ==, item->frame.goaway.error_code); nghttp2_session_del(session); /* Check window_update_queued flag in both session and stream */ nghttp2_session_server_new(&session, &callbacks, &ud); hd.length = 4096; hd.type = NGHTTP2_DATA; hd.flags = NGHTTP2_FLAG_NONE; hd.stream_id = 1; nghttp2_frame_pack_frame_hd(data, &hd); stream = open_recv_stream(session, 1); /* Send 32767 bytes of DATA. In our current flow control algorithm, it triggers first WINDOW_UPDATE of window_size_increment 32767. */ for (i = 0; i < 7; ++i) { rv = nghttp2_session_mem_recv2(session, data, NGHTTP2_FRAME_HDLEN + 4096); assert_ptrdiff(NGHTTP2_FRAME_HDLEN + 4096, ==, rv); } hd.length = 4095; nghttp2_frame_pack_frame_hd(data, &hd); rv = nghttp2_session_mem_recv2(session, data, NGHTTP2_FRAME_HDLEN + 4095); assert_ptrdiff(NGHTTP2_FRAME_HDLEN + 4095, ==, rv); /* Now 2 WINDOW_UPDATEs for session and stream should be queued. */ assert_int32(0, ==, stream->recv_window_size); assert_int32(0, ==, session->recv_window_size); assert_true(stream->window_update_queued); assert_true(session->window_update_queued); /* Then send 32768 bytes of DATA. Since we have not sent queued WINDOW_UDPATE frame, recv_window_size should not be decreased */ hd.length = 4096; nghttp2_frame_pack_frame_hd(data, &hd); for (i = 0; i < 8; ++i) { rv = nghttp2_session_mem_recv2(session, data, NGHTTP2_FRAME_HDLEN + 4096); assert_ptrdiff(NGHTTP2_FRAME_HDLEN + 4096, ==, rv); } /* WINDOW_UPDATE is blocked for session and stream, so recv_window_size must not be decreased. */ assert_int32(32768, ==, stream->recv_window_size); assert_int32(32768, ==, session->recv_window_size); assert_true(stream->window_update_queued); assert_true(session->window_update_queued); ud.frame_send_cb_called = 0; /* This sends queued WINDOW_UPDATES. And then check recv_window_size, and queue WINDOW_UPDATEs for both session and stream, and send them at once. */ assert_int(0, ==, nghttp2_session_send(session)); assert_int(4, ==, ud.frame_send_cb_called); assert_int32(0, ==, stream->recv_window_size); assert_int32(0, ==, session->recv_window_size); assert_false(stream->window_update_queued); assert_false(session->window_update_queued); nghttp2_session_del(session); } void test_nghttp2_session_recv_data_no_auto_flow_control(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; nghttp2_option *option; nghttp2_frame_hd hd; size_t padlen; uint8_t data[8192]; nghttp2_ssize rv; size_t sendlen; nghttp2_stream *stream; size_t i; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_frame_send_callback = on_frame_send_callback; nghttp2_option_new(&option); nghttp2_option_set_no_auto_window_update(option, 1); nghttp2_session_server_new2(&session, &callbacks, &ud, option); /* Create DATA frame with length 4KiB + 11 bytes padding*/ padlen = 11; memset(data, 0, sizeof(data)); hd.length = 4096 + 1 + padlen; hd.type = NGHTTP2_DATA; hd.flags = NGHTTP2_FLAG_PADDED; hd.stream_id = 1; nghttp2_frame_pack_frame_hd(data, &hd); data[NGHTTP2_FRAME_HDLEN] = (uint8_t)padlen; /* First create stream 1, then close it. Check that data is consumed for connection in this situation */ open_recv_stream(session, 1); /* Receive first 100 bytes */ sendlen = 100; rv = nghttp2_session_mem_recv2(session, data, sendlen); assert_ptrdiff((nghttp2_ssize)sendlen, ==, rv); /* We consumed pad length field (1 byte) */ assert_int32(1, ==, session->consumed_size); /* close stream here */ nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 1, NGHTTP2_NO_ERROR); nghttp2_session_send(session); /* stream 1 has been closed, and we disabled auto flow-control, so data must be immediately consumed for connection. */ rv = nghttp2_session_mem_recv2(session, data + sendlen, NGHTTP2_FRAME_HDLEN + hd.length - sendlen); assert_ptrdiff((nghttp2_ssize)(NGHTTP2_FRAME_HDLEN + hd.length - sendlen), ==, rv); /* We already consumed pad length field (1 byte), so do +1 here */ assert_int32((int32_t)(NGHTTP2_FRAME_HDLEN + hd.length - sendlen + 1), ==, session->consumed_size); nghttp2_session_del(session); /* Reuse DATA created previously. */ nghttp2_session_server_new2(&session, &callbacks, &ud, option); /* Now we are expecting final response header, which means receiving DATA for that stream is illegal. */ stream = open_recv_stream(session, 1); stream->http_flags |= NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE; rv = nghttp2_session_mem_recv2(session, data, NGHTTP2_FRAME_HDLEN + hd.length); assert_ptrdiff((nghttp2_ssize)(NGHTTP2_FRAME_HDLEN + hd.length), ==, rv); /* Session is going down because HTTP messaging rule was not honored. */ assert_uint8(NGHTTP2_GOAWAY, ==, nghttp2_session_get_next_ob_item(session)->frame.hd.type); nghttp2_session_del(session); /* Check window_update_queued flag in both session and stream */ nghttp2_session_server_new2(&session, &callbacks, &ud, option); stream = open_recv_stream(session, 1); hd.length = 4096; hd.type = NGHTTP2_DATA; hd.flags = NGHTTP2_FLAG_NONE; hd.stream_id = 1; nghttp2_frame_pack_frame_hd(data, &hd); /* Receive up to 65535 bytes of DATA */ for (i = 0; i < 15; ++i) { rv = nghttp2_session_mem_recv2(session, data, NGHTTP2_FRAME_HDLEN + 4096); assert_ptrdiff(NGHTTP2_FRAME_HDLEN + 4096, ==, rv); } hd.length = 4095; nghttp2_frame_pack_frame_hd(data, &hd); rv = nghttp2_session_mem_recv2(session, data, NGHTTP2_FRAME_HDLEN + 4095); assert_ptrdiff(NGHTTP2_FRAME_HDLEN + 4095, ==, rv); assert_int32(65535, ==, session->recv_window_size); assert_int32(65535, ==, stream->recv_window_size); /* The first call of nghttp2_session_consume_connection() will queue WINDOW_UPDATE. Next call does not. */ nghttp2_session_consume_connection(session, 32767); nghttp2_session_consume_connection(session, 32768); assert_int32(32768, ==, session->recv_window_size); assert_int32(65535, ==, stream->recv_window_size); assert_true(session->window_update_queued); assert_false(stream->window_update_queued); ud.frame_send_cb_called = 0; /* This will send WINDOW_UPDATE, and check whether we should send WINDOW_UPDATE, and queue and send it at once. */ assert_int(0, ==, nghttp2_session_send(session)); assert_int32(0, ==, session->recv_window_size); assert_int32(65535, ==, stream->recv_window_size); assert_false(session->window_update_queued); assert_false(stream->window_update_queued); assert_int(2, ==, ud.frame_send_cb_called); /* Do the same for stream */ nghttp2_session_consume_stream(session, 1, 32767); nghttp2_session_consume_stream(session, 1, 32768); assert_int32(0, ==, session->recv_window_size); assert_int32(32768, ==, stream->recv_window_size); assert_false(session->window_update_queued); assert_true(stream->window_update_queued); ud.frame_send_cb_called = 0; assert_int(0, ==, nghttp2_session_send(session)); assert_int32(0, ==, session->recv_window_size); assert_int32(0, ==, stream->recv_window_size); assert_false(session->window_update_queued); assert_false(stream->window_update_queued); assert_int(2, ==, ud.frame_send_cb_called); nghttp2_session_del(session); nghttp2_option_del(option); } void test_nghttp2_session_recv_continuation(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_nv *nva; size_t nvlen; nghttp2_frame frame; nghttp2_bufs bufs; nghttp2_buf *buf; nghttp2_ssize rv; my_user_data ud; nghttp2_hd_deflater deflater; uint8_t data[1024]; size_t datalen; nghttp2_frame_hd cont_hd; nghttp2_priority_spec pri_spec; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_header_callback = on_header_callback; callbacks.on_begin_headers_callback = on_begin_headers_callback; callbacks.on_begin_frame_callback = on_begin_frame_callback; nghttp2_session_server_new(&session, &callbacks, &ud); nghttp2_hd_deflate_init(&deflater, mem); /* Make 1 HEADERS and insert CONTINUATION header */ nvlen = ARRLEN(reqnv); nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_NONE, 1, NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); assert_ptrdiff(0, ==, rv); assert_size(0, <, nghttp2_bufs_len(&bufs)); /* make sure that all data is in the first buf */ buf = &bufs.head->buf; assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); nghttp2_frame_headers_free(&frame.headers, mem); /* HEADERS's payload is 1 byte */ memcpy(data, buf->pos, NGHTTP2_FRAME_HDLEN + 1); datalen = NGHTTP2_FRAME_HDLEN + 1; buf->pos += NGHTTP2_FRAME_HDLEN + 1; nghttp2_put_uint32be(data, (uint32_t)((1 << 8) + data[3])); /* First CONTINUATION, 2 bytes */ nghttp2_frame_hd_init(&cont_hd, 2, NGHTTP2_CONTINUATION, NGHTTP2_FLAG_NONE, 1); nghttp2_frame_pack_frame_hd(data + datalen, &cont_hd); datalen += NGHTTP2_FRAME_HDLEN; memcpy(data + datalen, buf->pos, cont_hd.length); datalen += cont_hd.length; buf->pos += cont_hd.length; /* Second CONTINUATION, rest of the bytes */ nghttp2_frame_hd_init(&cont_hd, nghttp2_buf_len(buf), NGHTTP2_CONTINUATION, NGHTTP2_FLAG_END_HEADERS, 1); nghttp2_frame_pack_frame_hd(data + datalen, &cont_hd); datalen += NGHTTP2_FRAME_HDLEN; memcpy(data + datalen, buf->pos, cont_hd.length); datalen += cont_hd.length; buf->pos += cont_hd.length; assert_size(0, ==, nghttp2_buf_len(buf)); ud.header_cb_called = 0; ud.begin_frame_cb_called = 0; rv = nghttp2_session_mem_recv2(session, data, datalen); assert_ptrdiff((nghttp2_ssize)datalen, ==, rv); assert_int(4, ==, ud.header_cb_called); assert_int(3, ==, ud.begin_frame_cb_called); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); /* HEADERS with padding followed by CONTINUATION */ nghttp2_session_server_new(&session, &callbacks, &ud); nghttp2_hd_deflate_init(&deflater, mem); nvlen = ARRLEN(reqnv); nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_NONE, 1, NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); nghttp2_bufs_reset(&bufs); rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); assert_ptrdiff(0, ==, rv); nghttp2_frame_headers_free(&frame.headers, mem); /* make sure that all data is in the first buf */ buf = &bufs.head->buf; assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); /* HEADERS payload is 3 byte (1 for padding field, 1 for padding) */ memcpy(data, buf->pos, NGHTTP2_FRAME_HDLEN); nghttp2_put_uint32be(data, (uint32_t)((3 << 8) + data[3])); data[4] |= NGHTTP2_FLAG_PADDED; /* padding field */ data[NGHTTP2_FRAME_HDLEN] = 1; data[NGHTTP2_FRAME_HDLEN + 1] = buf->pos[NGHTTP2_FRAME_HDLEN]; /* padding */ data[NGHTTP2_FRAME_HDLEN + 2] = 0; datalen = NGHTTP2_FRAME_HDLEN + 3; buf->pos += NGHTTP2_FRAME_HDLEN + 1; /* CONTINUATION, rest of the bytes */ nghttp2_frame_hd_init(&cont_hd, nghttp2_buf_len(buf), NGHTTP2_CONTINUATION, NGHTTP2_FLAG_END_HEADERS, 1); nghttp2_frame_pack_frame_hd(data + datalen, &cont_hd); datalen += NGHTTP2_FRAME_HDLEN; memcpy(data + datalen, buf->pos, cont_hd.length); datalen += cont_hd.length; buf->pos += cont_hd.length; assert_size(0, ==, nghttp2_buf_len(buf)); ud.header_cb_called = 0; ud.begin_frame_cb_called = 0; rv = nghttp2_session_mem_recv2(session, data, datalen); assert_ptrdiff((nghttp2_ssize)datalen, ==, rv); assert_int(4, ==, ud.header_cb_called); assert_int(2, ==, ud.begin_frame_cb_called); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); /* Expecting CONTINUATION, but get the other frame */ nghttp2_session_server_new(&session, &callbacks, &ud); nghttp2_hd_deflate_init(&deflater, mem); /* HEADERS without END_HEADERS flag */ nvlen = ARRLEN(reqnv); nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_NONE, 1, NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); nghttp2_bufs_reset(&bufs); rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); assert_ptrdiff(0, ==, rv); assert_size(0, <, nghttp2_bufs_len(&bufs)); nghttp2_frame_headers_free(&frame.headers, mem); /* make sure that all data is in the first buf */ buf = &bufs.head->buf; assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); memcpy(data, buf->pos, nghttp2_buf_len(buf)); datalen = nghttp2_buf_len(buf); /* Followed by PRIORITY */ nghttp2_priority_spec_default_init(&pri_spec); nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec); nghttp2_bufs_reset(&bufs); nghttp2_frame_pack_priority(&bufs, &frame.priority); assert_size(0, <, nghttp2_bufs_len(&bufs)); memcpy(data + datalen, buf->pos, nghttp2_buf_len(buf)); datalen += nghttp2_buf_len(buf); ud.begin_headers_cb_called = 0; rv = nghttp2_session_mem_recv2(session, data, datalen); assert_ptrdiff((nghttp2_ssize)datalen, ==, rv); assert_int(1, ==, ud.begin_headers_cb_called); assert_uint8(NGHTTP2_GOAWAY, ==, nghttp2_session_get_next_ob_item(session)->frame.hd.type); nghttp2_bufs_free(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); } void test_nghttp2_session_recv_headers_with_priority(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_nv *nva; size_t nvlen; nghttp2_frame frame; nghttp2_bufs bufs; nghttp2_buf *buf; nghttp2_ssize rv; my_user_data ud; nghttp2_hd_deflater deflater; nghttp2_outbound_item *item; nghttp2_priority_spec pri_spec; nghttp2_stream *stream; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_frame_recv_callback = on_frame_recv_callback; nghttp2_session_server_new(&session, &callbacks, &ud); nghttp2_hd_deflate_init(&deflater, mem); open_recv_stream(session, 1); /* With NGHTTP2_FLAG_PRIORITY without exclusive flag set */ nvlen = ARRLEN(reqnv); nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); nghttp2_priority_spec_init(&pri_spec, 1, 99, 0); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, 3, NGHTTP2_HCAT_HEADERS, &pri_spec, nva, nvlen); rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); assert_ptrdiff(0, ==, rv); assert_size(0, <, nghttp2_bufs_len(&bufs)); nghttp2_frame_headers_free(&frame.headers, mem); buf = &bufs.head->buf; assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf), ==, rv); assert_int(1, ==, ud.frame_recv_cb_called); stream = nghttp2_session_get_stream(session, 3); assert_not_null(stream); nghttp2_bufs_reset(&bufs); /* With NGHTTP2_FLAG_PRIORITY, but cut last 1 byte to make it invalid. */ nvlen = ARRLEN(reqnv); nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); nghttp2_priority_spec_init(&pri_spec, 0, 99, 0); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, 5, NGHTTP2_HCAT_HEADERS, &pri_spec, nva, nvlen); rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); assert_ptrdiff(0, ==, rv); assert_size(NGHTTP2_FRAME_HDLEN + 5, <, nghttp2_bufs_len(&bufs)); nghttp2_frame_headers_free(&frame.headers, mem); buf = &bufs.head->buf; /* Make payload shorter than required length to store priority group */ nghttp2_put_uint32be(buf->pos, (uint32_t)((4 << 8) + buf->pos[3])); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf), ==, rv); assert_int(0, ==, ud.frame_recv_cb_called); stream = nghttp2_session_get_stream(session, 5); assert_null(stream); item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_uint32(NGHTTP2_FRAME_SIZE_ERROR, ==, item->frame.goaway.error_code); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); /* Check dep_stream_id == stream_id */ nghttp2_session_server_new(&session, &callbacks, &ud); nghttp2_hd_deflate_init(&deflater, mem); nvlen = ARRLEN(reqnv); nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); nghttp2_priority_spec_init(&pri_spec, 1, 0, 0); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, 1, NGHTTP2_HCAT_HEADERS, &pri_spec, nva, nvlen); rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); assert_ptrdiff(0, ==, rv); assert_size(0, <, nghttp2_bufs_len(&bufs)); nghttp2_frame_headers_free(&frame.headers, mem); buf = &bufs.head->buf; assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf), ==, rv); assert_int(0, ==, ud.frame_recv_cb_called); stream = nghttp2_session_get_stream(session, 1); assert_null(stream); item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_uint32(NGHTTP2_PROTOCOL_ERROR, ==, item->frame.goaway.error_code); nghttp2_bufs_reset(&bufs); nghttp2_bufs_free(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); } void test_nghttp2_session_recv_headers_with_padding(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_bufs bufs; nghttp2_buf *buf; nghttp2_frame_hd hd; nghttp2_outbound_item *item; my_user_data ud; nghttp2_ssize rv; frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_frame_recv_callback = on_frame_recv_callback; callbacks.send_callback2 = null_send_callback; /* HEADERS: Wrong padding length */ nghttp2_session_server_new(&session, &callbacks, &ud); nghttp2_session_send(session); nghttp2_frame_hd_init( &hd, 10, NGHTTP2_HEADERS, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY | NGHTTP2_FLAG_PADDED, 1); buf = &bufs.head->buf; nghttp2_frame_pack_frame_hd(buf->last, &hd); buf->last += NGHTTP2_FRAME_HDLEN; /* padding is 6 bytes */ *buf->last++ = 5; /* priority field */ nghttp2_put_uint32be(buf->last, 3); buf->last += sizeof(uint32_t); *buf->last++ = 1; /* rest is garbage */ memset(buf->last, 0, 4); buf->last += 4; ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf), ==, rv); assert_int(0, ==, ud.frame_recv_cb_called); item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); nghttp2_bufs_reset(&bufs); nghttp2_session_del(session); /* PUSH_PROMISE: Wrong padding length */ nghttp2_session_client_new(&session, &callbacks, &ud); nghttp2_session_send(session); open_sent_stream(session, 1); nghttp2_frame_hd_init(&hd, 9, NGHTTP2_PUSH_PROMISE, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PADDED, 1); buf = &bufs.head->buf; nghttp2_frame_pack_frame_hd(buf->last, &hd); buf->last += NGHTTP2_FRAME_HDLEN; /* padding is 6 bytes */ *buf->last++ = 5; /* promised stream ID field */ nghttp2_put_uint32be(buf->last, 2); buf->last += sizeof(uint32_t); /* rest is garbage */ memset(buf->last, 0, 4); buf->last += 4; ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf), ==, rv); assert_int(0, ==, ud.frame_recv_cb_called); item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); nghttp2_bufs_free(&bufs); nghttp2_session_del(session); } static int response_on_begin_frame_callback(nghttp2_session *session, const nghttp2_frame_hd *hd, void *user_data) { int rv; (void)user_data; if (hd->type != NGHTTP2_HEADERS) { return 0; } rv = nghttp2_submit_response2(session, hd->stream_id, resnv, ARRLEN(resnv), NULL); assert_int(0, ==, rv); return 0; } void test_nghttp2_session_recv_headers_early_response(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_bufs bufs; nghttp2_buf *buf; nghttp2_hd_deflater deflater; nghttp2_mem *mem; nghttp2_nv *nva; size_t nvlen; nghttp2_frame frame; nghttp2_ssize rv; nghttp2_stream *stream; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_begin_frame_callback = response_on_begin_frame_callback; nghttp2_session_server_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); nvlen = ARRLEN(reqnv); nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, 1, NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen); rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); assert_ptrdiff(0, ==, rv); nghttp2_frame_headers_free(&frame.headers, mem); buf = &bufs.head->buf; /* Only receive 9 bytes headers, and invoke on_begin_frame_callback */ rv = nghttp2_session_mem_recv2(session, buf->pos, 9); assert_ptrdiff(9, ==, rv); rv = nghttp2_session_send(session); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, buf->pos + 9, nghttp2_buf_len(buf) - 9); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf) - 9, ==, rv); stream = nghttp2_session_get_stream_raw(session, 1); assert_null(stream); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); nghttp2_bufs_free(&bufs); } void test_nghttp2_session_recv_headers_for_closed_stream(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_nv *nva; size_t nvlen; nghttp2_frame frame; nghttp2_bufs bufs; nghttp2_buf *buf; nghttp2_ssize rv; my_user_data ud; nghttp2_hd_deflater deflater; nghttp2_stream *stream; nghttp2_mem *mem; const uint8_t *data; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_frame_recv_callback = on_frame_recv_callback; callbacks.on_header_callback = on_header_callback; nghttp2_session_server_new(&session, &callbacks, &ud); nghttp2_hd_deflate_init(&deflater, mem); /* Make sure that on_header callback never be invoked for closed stream */ nvlen = ARRLEN(reqnv); nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); assert_ptrdiff(0, ==, rv); assert_size(0, <, nghttp2_bufs_len(&bufs)); nghttp2_frame_headers_free(&frame.headers, mem); buf = &bufs.head->buf; assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); ud.header_cb_called = 0; ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf->pos, NGHTTP2_FRAME_HDLEN); assert_ptrdiff(NGHTTP2_FRAME_HDLEN, ==, rv); assert_int(0, ==, ud.header_cb_called); assert_int(0, ==, ud.frame_recv_cb_called); stream = nghttp2_session_get_stream(session, 1); assert_not_null(stream); rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 1, NGHTTP2_NO_ERROR); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_send2(session, &data); assert_ptrdiff(0, <, rv); stream = nghttp2_session_get_stream(session, 1); assert_null(stream); ud.header_cb_called = 0; ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf->pos + NGHTTP2_FRAME_HDLEN, nghttp2_buf_len(buf) - NGHTTP2_FRAME_HDLEN); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf) - NGHTTP2_FRAME_HDLEN, ==, rv); assert_int(0, ==, ud.header_cb_called); assert_int(0, ==, ud.frame_recv_cb_called); nghttp2_bufs_free(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); } void test_nghttp2_session_recv_headers_with_extpri(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_nv *nva; size_t nvlen; nghttp2_frame frame; nghttp2_bufs bufs; nghttp2_buf *buf; nghttp2_ssize rv; nghttp2_hd_deflater deflater; nghttp2_stream *stream; nghttp2_mem *mem; const nghttp2_nv extpri_reqnv[] = { MAKE_NV(":method", "GET"), MAKE_NV(":path", "/"), MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"), MAKE_NV("priority", "i,u=2"), }; nghttp2_settings_entry iv; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); nghttp2_session_server_new(&session, &callbacks, NULL); iv.settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES; iv.value = 1; nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1); nghttp2_hd_deflate_init(&deflater, mem); nvlen = ARRLEN(extpri_reqnv); nghttp2_nv_array_copy(&nva, extpri_reqnv, nvlen, mem); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); assert_ptrdiff(0, ==, rv); assert_size(0, <, nghttp2_bufs_len(&bufs)); nghttp2_frame_headers_free(&frame.headers, mem); buf = &bufs.head->buf; assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); stream = nghttp2_session_get_stream(session, 1); assert_uint32(2, ==, nghttp2_extpri_uint8_urgency(stream->extpri)); assert_true(nghttp2_extpri_uint8_inc(stream->extpri)); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); nghttp2_bufs_reset(&bufs); /* Client should ignore priority header field included in PUSH_PROMISE. */ nghttp2_session_client_new(&session, &callbacks, NULL); nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1); open_sent_stream(session, 1); nghttp2_hd_deflate_init(&deflater, mem); nvlen = ARRLEN(extpri_reqnv); nghttp2_nv_array_copy(&nva, extpri_reqnv, nvlen, mem); nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, 1, 2, nva, nvlen); rv = nghttp2_frame_pack_push_promise(&bufs, &frame.push_promise, &deflater); assert_ptrdiff(0, ==, rv); assert_size(0, <, nghttp2_bufs_len(&bufs)); nghttp2_frame_push_promise_free(&frame.push_promise, mem); buf = &bufs.head->buf; assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); stream = nghttp2_session_get_stream(session, 2); assert_uint32(NGHTTP2_EXTPRI_DEFAULT_URGENCY, ==, nghttp2_extpri_uint8_urgency(stream->http_extpri)); assert_uint32(NGHTTP2_EXTPRI_DEFAULT_URGENCY, ==, nghttp2_extpri_uint8_urgency(stream->extpri)); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); nghttp2_bufs_free(&bufs); } void test_nghttp2_session_server_recv_push_response(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_bufs bufs; nghttp2_buf *buf; nghttp2_ssize rv; my_user_data ud; nghttp2_mem *mem; nghttp2_frame frame; nghttp2_hd_deflater deflater; nghttp2_nv *nva; size_t nvlen; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; nghttp2_session_server_new(&session, &callbacks, &ud); nghttp2_hd_deflate_init(&deflater, mem); open_sent_stream2(session, 2, NGHTTP2_STREAM_RESERVED); nvlen = ARRLEN(resnv); nghttp2_nv_array_copy(&nva, resnv, nvlen, mem); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2, NGHTTP2_HCAT_HEADERS, &pri_spec_default, nva, nvlen); rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); assert_ptrdiff(0, ==, rv); assert_size(0, <, nghttp2_bufs_len(&bufs)); nghttp2_frame_headers_free(&frame.headers, mem); buf = &bufs.head->buf; ud.invalid_frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf), ==, rv); assert_int(1, ==, ud.invalid_frame_recv_cb_called); nghttp2_bufs_free(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); } void test_nghttp2_session_recv_premature_headers(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_bufs bufs; nghttp2_buf *buf; nghttp2_ssize rv; my_user_data ud; nghttp2_hd_deflater deflater; nghttp2_outbound_item *item; nghttp2_mem *mem; uint32_t payloadlen; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_server_new(&session, &callbacks, &ud); nghttp2_hd_deflate_init(&deflater, mem); pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv, ARRLEN(reqnv), mem); buf = &bufs.head->buf; /* Intentionally feed payload cutting last 1 byte off */ payloadlen = nghttp2_get_uint32(buf->pos) >> 8; nghttp2_put_uint32be(buf->pos, ((payloadlen - 1) << 8) + buf->pos[3]); rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf) - 1); assert_ptrdiff((nghttp2_ssize)(nghttp2_buf_len(buf) - 1), ==, rv); item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_RST_STREAM, ==, item->frame.hd.type); assert_uint32(NGHTTP2_COMPRESSION_ERROR, ==, item->frame.rst_stream.error_code); assert_int32(1, ==, item->frame.hd.stream_id); assert_int(0, ==, nghttp2_session_send(session)); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); /* Test for PUSH_PROMISE */ nghttp2_session_client_new(&session, &callbacks, &ud); nghttp2_hd_deflate_init(&deflater, mem); open_sent_stream3(session, 1, NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_STREAM_OPENING, NULL); rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2, reqnv, ARRLEN(reqnv), mem); assert_ptrdiff(0, ==, rv); buf = &bufs.head->buf; payloadlen = nghttp2_get_uint32(buf->pos) >> 8; /* Intentionally feed payload cutting last 1 byte off */ nghttp2_put_uint32be(buf->pos, ((payloadlen - 1) << 8) + buf->pos[3]); rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf) - 1); assert_ptrdiff((nghttp2_ssize)(nghttp2_buf_len(buf) - 1), ==, rv); item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_RST_STREAM, ==, item->frame.hd.type); assert_uint32(NGHTTP2_COMPRESSION_ERROR, ==, item->frame.rst_stream.error_code); assert_int32(2, ==, item->frame.hd.stream_id); assert_int(0, ==, nghttp2_session_send(session)); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); nghttp2_bufs_free(&bufs); } void test_nghttp2_session_recv_unknown_frame(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; uint8_t data[16384]; size_t datalen; nghttp2_frame_hd hd; nghttp2_ssize rv; nghttp2_outbound_item *item; nghttp2_option *option; nghttp2_frame_hd_init(&hd, 16000, 99, NGHTTP2_FLAG_NONE, 0); nghttp2_frame_pack_frame_hd(data, &hd); datalen = NGHTTP2_FRAME_HDLEN + hd.length; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_frame_recv_callback = on_frame_recv_callback; nghttp2_session_server_new(&session, &callbacks, &ud); ud.frame_recv_cb_called = 0; /* Unknown frame must be ignored */ rv = nghttp2_session_mem_recv2(session, data, datalen); assert_ptrdiff((nghttp2_ssize)datalen, ==, rv); assert_int(0, ==, ud.frame_recv_cb_called); assert_null(nghttp2_session_get_next_ob_item(session)); nghttp2_session_del(session); /* Receiving too many unknown frames */ nghttp2_session_server_new(&session, &callbacks, NULL); for (;;) { rv = nghttp2_session_mem_recv2(session, data, datalen); assert_ptrdiff((nghttp2_ssize)datalen, ==, rv); if (session->iframe.state == NGHTTP2_IB_IGN_ALL) { break; } } item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_uint32(NGHTTP2_ENHANCE_YOUR_CALM, ==, item->frame.goaway.error_code); nghttp2_session_del(session); /* With glitch rate limit option */ nghttp2_option_new(&option); nghttp2_option_set_glitch_rate_limit(option, 0, 0); nghttp2_session_server_new2(&session, &callbacks, NULL, option); rv = nghttp2_session_mem_recv2(session, data, datalen); assert_ptrdiff((nghttp2_ssize)datalen, ==, rv); assert_enum(nghttp2_inbound_state, NGHTTP2_IB_IGN_ALL, ==, session->iframe.state); item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_uint32(NGHTTP2_ENHANCE_YOUR_CALM, ==, item->frame.goaway.error_code); nghttp2_session_del(session); nghttp2_option_del(option); /* Receiving too many ALTSVC frames and client does not support them */ nghttp2_session_client_new(&session, &callbacks, NULL); nghttp2_frame_hd_init(&hd, 100, NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, 0); nghttp2_frame_pack_frame_hd(data, &hd); datalen = NGHTTP2_FRAME_HDLEN + hd.length; for (;;) { rv = nghttp2_session_mem_recv2(session, data, datalen); assert_ptrdiff((nghttp2_ssize)datalen, ==, rv); if (session->iframe.state == NGHTTP2_IB_IGN_ALL) { break; } } item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_uint32(NGHTTP2_ENHANCE_YOUR_CALM, ==, item->frame.goaway.error_code); nghttp2_session_del(session); /* Receiving too many ORIGIN frames and client does not support them */ nghttp2_session_client_new(&session, &callbacks, NULL); nghttp2_frame_hd_init(&hd, 100, NGHTTP2_ORIGIN, NGHTTP2_FLAG_NONE, 0); nghttp2_frame_pack_frame_hd(data, &hd); datalen = NGHTTP2_FRAME_HDLEN + hd.length; for (;;) { rv = nghttp2_session_mem_recv2(session, data, datalen); assert_ptrdiff((nghttp2_ssize)datalen, ==, rv); if (session->iframe.state == NGHTTP2_IB_IGN_ALL) { break; } } item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_uint32(NGHTTP2_ENHANCE_YOUR_CALM, ==, item->frame.goaway.error_code); nghttp2_session_del(session); /* Receiving too many PRIORITY_UPDATE frames and server does not support them */ nghttp2_session_server_new(&session, &callbacks, NULL); nghttp2_frame_hd_init(&hd, 100, NGHTTP2_PRIORITY_UPDATE, NGHTTP2_FLAG_NONE, 0); nghttp2_frame_pack_frame_hd(data, &hd); datalen = NGHTTP2_FRAME_HDLEN + hd.length; for (;;) { rv = nghttp2_session_mem_recv2(session, data, datalen); assert_ptrdiff((nghttp2_ssize)datalen, ==, rv); if (session->iframe.state == NGHTTP2_IB_IGN_ALL) { break; } } item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_uint32(NGHTTP2_ENHANCE_YOUR_CALM, ==, item->frame.goaway.error_code); nghttp2_session_del(session); } void test_nghttp2_session_recv_unexpected_continuation(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; uint8_t data[16384]; size_t datalen; nghttp2_frame_hd hd; nghttp2_ssize rv; nghttp2_outbound_item *item; nghttp2_frame_hd_init(&hd, 16000, NGHTTP2_CONTINUATION, NGHTTP2_FLAG_END_HEADERS, 1); nghttp2_frame_pack_frame_hd(data, &hd); datalen = NGHTTP2_FRAME_HDLEN + hd.length; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_frame_recv_callback = on_frame_recv_callback; nghttp2_session_server_new(&session, &callbacks, &ud); open_recv_stream(session, 1); ud.frame_recv_cb_called = 0; /* unexpected CONTINUATION must be treated as connection error */ rv = nghttp2_session_mem_recv2(session, data, datalen); assert_ptrdiff((nghttp2_ssize)datalen, ==, rv); assert_int(0, ==, ud.frame_recv_cb_called); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); nghttp2_session_del(session); } void test_nghttp2_session_recv_settings_header_table_size(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_frame frame; nghttp2_bufs bufs; nghttp2_buf *buf; nghttp2_ssize rv; my_user_data ud; nghttp2_settings_entry iv[3]; nghttp2_nv nv = MAKE_NV(":authority", "example.org"); nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_frame_recv_callback = on_frame_recv_callback; callbacks.send_callback2 = null_send_callback; nghttp2_session_client_new(&session, &callbacks, &ud); iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[0].value = 3000; iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; iv[1].value = 16384; nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 2), 2); rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); assert_ptrdiff(0, ==, rv); assert_size(0, <, nghttp2_bufs_len(&bufs)); nghttp2_frame_settings_free(&frame.settings, mem); buf = &bufs.head->buf; assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf), ==, rv); assert_int(1, ==, ud.frame_recv_cb_called); assert_uint32(3000, ==, session->remote_settings.header_table_size); assert_uint32(16384, ==, session->remote_settings.initial_window_size); nghttp2_bufs_reset(&bufs); /* 2 SETTINGS_HEADER_TABLE_SIZE */ iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[0].value = 3001; iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; iv[1].value = 16383; iv[2].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[2].value = 3001; nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 3), 3); rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); assert_ptrdiff(0, ==, rv); assert_size(0, <, nghttp2_bufs_len(&bufs)); nghttp2_frame_settings_free(&frame.settings, mem); buf = &bufs.head->buf; assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)(nghttp2_buf_len(buf)), ==, rv); assert_int(1, ==, ud.frame_recv_cb_called); assert_uint32(3001, ==, session->remote_settings.header_table_size); assert_uint32(16383, ==, session->remote_settings.initial_window_size); nghttp2_bufs_reset(&bufs); /* 2 SETTINGS_HEADER_TABLE_SIZE; first entry clears dynamic header table. */ nghttp2_submit_request2(session, NULL, &nv, 1, NULL, NULL); nghttp2_session_send(session); assert_size(0, <, session->hd_deflater.ctx.hd_table.len); iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[0].value = 0; iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; iv[1].value = 16382; iv[2].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[2].value = 4096; nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 3), 3); rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); assert_ptrdiff(0, ==, rv); assert_size(0, <, nghttp2_bufs_len(&bufs)); nghttp2_frame_settings_free(&frame.settings, mem); buf = &bufs.head->buf; assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf), ==, rv); assert_int(1, ==, ud.frame_recv_cb_called); assert_uint32(4096, ==, session->remote_settings.header_table_size); assert_uint32(16382, ==, session->remote_settings.initial_window_size); assert_size(0, ==, session->hd_deflater.ctx.hd_table.len); nghttp2_bufs_reset(&bufs); /* 2 SETTINGS_HEADER_TABLE_SIZE; second entry clears dynamic header table. */ nghttp2_submit_request2(session, NULL, &nv, 1, NULL, NULL); nghttp2_session_send(session); assert_size(0, <, session->hd_deflater.ctx.hd_table.len); iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[0].value = 3000; iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; iv[1].value = 16381; iv[2].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[2].value = 0; nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 3), 3); rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); assert_ptrdiff(0, ==, rv); assert_size(0, <, nghttp2_bufs_len(&bufs)); nghttp2_frame_settings_free(&frame.settings, mem); buf = &bufs.head->buf; assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf), ==, rv); assert_int(1, ==, ud.frame_recv_cb_called); assert_uint32(0, ==, session->remote_settings.header_table_size); assert_uint32(16381, ==, session->remote_settings.initial_window_size); assert_size(0, ==, session->hd_deflater.ctx.hd_table.len); nghttp2_bufs_reset(&bufs); nghttp2_bufs_free(&bufs); nghttp2_session_del(session); } void test_nghttp2_session_recv_too_large_frame_length(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; uint8_t buf[NGHTTP2_FRAME_HDLEN]; nghttp2_outbound_item *item; nghttp2_frame_hd hd; /* Initial max frame size is NGHTTP2_MAX_FRAME_SIZE_MIN */ nghttp2_frame_hd_init(&hd, NGHTTP2_MAX_FRAME_SIZE_MIN + 1, NGHTTP2_HEADERS, NGHTTP2_FLAG_NONE, 1); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); nghttp2_session_server_new(&session, &callbacks, NULL); nghttp2_frame_pack_frame_hd(buf, &hd); assert_ptrdiff(sizeof(buf), ==, nghttp2_session_mem_recv2(session, buf, sizeof(buf))); item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); nghttp2_session_del(session); } void test_nghttp2_session_recv_extension(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; nghttp2_buf buf; nghttp2_frame_hd hd; nghttp2_mem *mem; const char data[] = "Hello World!"; nghttp2_ssize rv; nghttp2_option *option; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_extension_chunk_recv_callback = on_extension_chunk_recv_callback; callbacks.unpack_extension_callback = unpack_extension_callback; callbacks.on_frame_recv_callback = on_frame_recv_callback; nghttp2_option_new(&option); nghttp2_option_set_user_recv_extension_type(option, 111); nghttp2_buf_init2(&ud.scratchbuf, 4096, mem); nghttp2_buf_init2(&buf, 4096, mem); nghttp2_frame_hd_init(&hd, sizeof(data), 111, 0xab, 1000000007); nghttp2_frame_pack_frame_hd(buf.last, &hd); buf.last += NGHTTP2_FRAME_HDLEN; buf.last = nghttp2_cpymem(buf.last, data, sizeof(data)); nghttp2_session_client_new2(&session, &callbacks, &ud, option); nghttp2_frame_hd_init(&ud.recv_frame_hd, 0, 0, 0, 0); rv = nghttp2_session_mem_recv2(session, buf.pos, nghttp2_buf_len(&buf)); assert_size(NGHTTP2_FRAME_HDLEN + hd.length, ==, (size_t)rv); assert_uint8(111, ==, ud.recv_frame_hd.type); assert_uint8(0xab, ==, ud.recv_frame_hd.flags); assert_int32(1000000007, ==, ud.recv_frame_hd.stream_id); assert_memory_equal(sizeof(data), data, ud.scratchbuf.pos); nghttp2_session_del(session); /* cancel in on_extension_chunk_recv_callback */ nghttp2_buf_reset(&ud.scratchbuf); callbacks.on_extension_chunk_recv_callback = cancel_on_extension_chunk_recv_callback; nghttp2_session_server_new2(&session, &callbacks, &ud, option); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf.pos, nghttp2_buf_len(&buf)); assert_size(NGHTTP2_FRAME_HDLEN + hd.length, ==, (size_t)rv); assert_int(0, ==, ud.frame_recv_cb_called); nghttp2_session_del(session); /* cancel in unpack_extension_callback */ nghttp2_buf_reset(&ud.scratchbuf); callbacks.on_extension_chunk_recv_callback = on_extension_chunk_recv_callback; callbacks.unpack_extension_callback = cancel_unpack_extension_callback; nghttp2_session_server_new2(&session, &callbacks, &ud, option); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf.pos, nghttp2_buf_len(&buf)); assert_size(NGHTTP2_FRAME_HDLEN + hd.length, ==, (size_t)rv); assert_int(0, ==, ud.frame_recv_cb_called); nghttp2_session_del(session); nghttp2_buf_free(&buf, mem); nghttp2_buf_free(&ud.scratchbuf, mem); nghttp2_option_del(option); } void test_nghttp2_session_recv_altsvc(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; nghttp2_buf buf; nghttp2_frame_hd hd; nghttp2_mem *mem; nghttp2_ssize rv; nghttp2_option *option; nghttp2_outbound_item *item; static const uint8_t origin[] = "nghttp2.org"; static const uint8_t field_value[] = "h2=\":443\""; mem = nghttp2_mem_default(); nghttp2_buf_init2(&buf, NGHTTP2_FRAME_HDLEN + NGHTTP2_MAX_FRAME_SIZE_MIN, mem); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_frame_recv_callback = on_frame_recv_callback; callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; nghttp2_option_new(&option); nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_ALTSVC); nghttp2_session_client_new2(&session, &callbacks, &ud, option); nghttp2_frame_hd_init(&hd, 2 + sizeof(origin) - 1 + sizeof(field_value) - 1, NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, 0); nghttp2_frame_pack_frame_hd(buf.last, &hd); buf.last += NGHTTP2_FRAME_HDLEN; nghttp2_put_uint16be(buf.last, sizeof(origin) - 1); buf.last += 2; buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1); buf.last = nghttp2_cpymem(buf.last, field_value, sizeof(field_value) - 1); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf.pos, nghttp2_buf_len(&buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&buf), ==, rv); assert_int(1, ==, ud.frame_recv_cb_called); assert_uint8(NGHTTP2_ALTSVC, ==, ud.recv_frame_hd.type); assert_uint8(NGHTTP2_FLAG_NONE, ==, ud.recv_frame_hd.flags); assert_int32(0, ==, ud.recv_frame_hd.stream_id); nghttp2_session_del(session); /* size of origin is larger than frame length */ nghttp2_buf_reset(&buf); nghttp2_session_client_new2(&session, &callbacks, &ud, option); nghttp2_frame_hd_init(&hd, 2 + sizeof(origin) - 1 - 1, NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, 0); nghttp2_frame_pack_frame_hd(buf.last, &hd); buf.last += NGHTTP2_FRAME_HDLEN; nghttp2_put_uint16be(buf.last, sizeof(origin) - 1); buf.last += 2; buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1 - 1); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf.pos, nghttp2_buf_len(&buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&buf), ==, rv); assert_int(0, ==, ud.frame_recv_cb_called); nghttp2_session_del(session); /* zero-length value */ nghttp2_buf_reset(&buf); nghttp2_session_client_new2(&session, &callbacks, &ud, option); nghttp2_frame_hd_init(&hd, 2 + sizeof(origin) - 1, NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, 0); nghttp2_frame_pack_frame_hd(buf.last, &hd); buf.last += NGHTTP2_FRAME_HDLEN; nghttp2_put_uint16be(buf.last, sizeof(origin) - 1); buf.last += 2; buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1); ud.invalid_frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf.pos, nghttp2_buf_len(&buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&buf), ==, rv); assert_int(1, ==, ud.invalid_frame_recv_cb_called); nghttp2_session_del(session); /* non-empty origin to a stream other than 0 */ nghttp2_buf_reset(&buf); nghttp2_session_client_new2(&session, &callbacks, &ud, option); open_sent_stream(session, 1); nghttp2_frame_hd_init(&hd, 2 + sizeof(origin) - 1 + sizeof(field_value) - 1, NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, 1); nghttp2_frame_pack_frame_hd(buf.last, &hd); buf.last += NGHTTP2_FRAME_HDLEN; nghttp2_put_uint16be(buf.last, sizeof(origin) - 1); buf.last += 2; buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1); buf.last = nghttp2_cpymem(buf.last, field_value, sizeof(field_value) - 1); ud.invalid_frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf.pos, nghttp2_buf_len(&buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&buf), ==, rv); assert_int(1, ==, ud.invalid_frame_recv_cb_called); nghttp2_session_del(session); /* empty origin to stream 0 */ nghttp2_buf_reset(&buf); nghttp2_session_client_new2(&session, &callbacks, &ud, option); nghttp2_frame_hd_init(&hd, 2 + sizeof(field_value) - 1, NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, 0); nghttp2_frame_pack_frame_hd(buf.last, &hd); buf.last += NGHTTP2_FRAME_HDLEN; nghttp2_put_uint16be(buf.last, 0); buf.last += 2; buf.last = nghttp2_cpymem(buf.last, field_value, sizeof(field_value) - 1); ud.invalid_frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf.pos, nghttp2_buf_len(&buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&buf), ==, rv); assert_int(1, ==, ud.invalid_frame_recv_cb_called); nghttp2_session_del(session); /* send large frame (16KiB) */ nghttp2_buf_reset(&buf); nghttp2_session_client_new2(&session, &callbacks, &ud, option); nghttp2_frame_hd_init(&hd, NGHTTP2_MAX_FRAME_SIZE_MIN, NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, 0); nghttp2_frame_pack_frame_hd(buf.last, &hd); buf.last += NGHTTP2_FRAME_HDLEN; nghttp2_put_uint16be(buf.last, sizeof(origin) - 1); buf.last += 2; buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1); memset(buf.last, 0, nghttp2_buf_avail(&buf)); buf.last += nghttp2_buf_avail(&buf); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf.pos, nghttp2_buf_len(&buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&buf), ==, rv); assert_int(1, ==, ud.frame_recv_cb_called); assert_uint8(NGHTTP2_ALTSVC, ==, ud.recv_frame_hd.type); assert_size(NGHTTP2_MAX_FRAME_SIZE_MIN, ==, ud.recv_frame_hd.length); nghttp2_session_del(session); /* send too large frame */ nghttp2_buf_reset(&buf); nghttp2_session_client_new2(&session, &callbacks, &ud, option); session->local_settings.max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN - 1; nghttp2_frame_hd_init(&hd, NGHTTP2_MAX_FRAME_SIZE_MIN + 1, NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, 0); nghttp2_frame_pack_frame_hd(buf.last, &hd); buf.last += NGHTTP2_FRAME_HDLEN; nghttp2_put_uint16be(buf.last, sizeof(origin) - 1); buf.last += 2; buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1); memset(buf.last, 0, nghttp2_buf_avail(&buf)); buf.last += nghttp2_buf_avail(&buf); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf.pos, nghttp2_buf_len(&buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&buf), ==, rv); assert_int(0, ==, ud.frame_recv_cb_called); nghttp2_session_del(session); /* received by server */ nghttp2_buf_reset(&buf); nghttp2_session_server_new2(&session, &callbacks, &ud, option); nghttp2_frame_hd_init(&hd, 2 + sizeof(origin) - 1 + sizeof(field_value) - 1, NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, 0); nghttp2_frame_pack_frame_hd(buf.last, &hd); buf.last += NGHTTP2_FRAME_HDLEN; nghttp2_put_uint16be(buf.last, sizeof(origin) - 1); buf.last += 2; buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1); buf.last = nghttp2_cpymem(buf.last, field_value, sizeof(field_value) - 1); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf.pos, nghttp2_buf_len(&buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&buf), ==, rv); assert_int(0, ==, ud.frame_recv_cb_called); nghttp2_session_del(session); /* Server receives too many ALTSVC frames */ nghttp2_buf_reset(&buf); nghttp2_session_server_new2(&session, &callbacks, &ud, option); nghttp2_frame_hd_init(&hd, 2 + sizeof(origin) - 1 + sizeof(field_value) - 1, NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, 0); nghttp2_frame_pack_frame_hd(buf.last, &hd); buf.last += NGHTTP2_FRAME_HDLEN; nghttp2_put_uint16be(buf.last, sizeof(origin) - 1); buf.last += 2; buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1); buf.last = nghttp2_cpymem(buf.last, field_value, sizeof(field_value) - 1); for (;;) { rv = nghttp2_session_mem_recv2(session, buf.pos, nghttp2_buf_len(&buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&buf), ==, rv); if (session->iframe.state == NGHTTP2_IB_IGN_ALL) { break; } } item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_uint32(NGHTTP2_ENHANCE_YOUR_CALM, ==, item->frame.goaway.error_code); nghttp2_session_del(session); nghttp2_buf_free(&buf, mem); nghttp2_option_del(option); } void test_nghttp2_session_recv_origin(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; nghttp2_bufs bufs; nghttp2_ssize rv; nghttp2_option *option; nghttp2_extension frame; nghttp2_ext_origin origin; nghttp2_origin_entry ov; nghttp2_outbound_item *item; static const uint8_t nghttp2[] = "https://nghttp2.org"; frame_pack_bufs_init(&bufs); frame.payload = &origin; ov.origin = (uint8_t *)nghttp2; ov.origin_len = sizeof(nghttp2) - 1; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_frame_recv_callback = on_frame_recv_callback; nghttp2_option_new(&option); nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_ORIGIN); nghttp2_session_client_new2(&session, &callbacks, &ud, option); nghttp2_frame_origin_init(&frame, &ov, 1); rv = nghttp2_frame_pack_origin(&bufs, &frame); assert_ptrdiff(0, ==, rv); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, rv); assert_int(1, ==, ud.frame_recv_cb_called); assert_uint8(NGHTTP2_ORIGIN, ==, ud.recv_frame_hd.type); assert_uint8(NGHTTP2_FLAG_NONE, ==, ud.recv_frame_hd.flags); assert_int32(0, ==, ud.recv_frame_hd.stream_id); nghttp2_session_del(session); nghttp2_bufs_reset(&bufs); /* The length of origin is larger than payload length. */ nghttp2_session_client_new2(&session, &callbacks, &ud, option); nghttp2_frame_origin_init(&frame, &ov, 1); rv = nghttp2_frame_pack_origin(&bufs, &frame); assert_ptrdiff(0, ==, rv); nghttp2_put_uint16be(bufs.head->buf.pos + NGHTTP2_FRAME_HDLEN, (uint16_t)sizeof(nghttp2)); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, rv); assert_int(0, ==, ud.frame_recv_cb_called); nghttp2_session_del(session); nghttp2_bufs_reset(&bufs); /* A frame should be ignored if it is sent to a stream other than stream 0. */ nghttp2_session_client_new2(&session, &callbacks, &ud, option); nghttp2_frame_origin_init(&frame, &ov, 1); frame.hd.stream_id = 1; rv = nghttp2_frame_pack_origin(&bufs, &frame); assert_ptrdiff(0, ==, rv); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, rv); assert_int(0, ==, ud.frame_recv_cb_called); nghttp2_session_del(session); nghttp2_bufs_reset(&bufs); /* A frame should be ignored if the reserved flag is set */ nghttp2_session_client_new2(&session, &callbacks, &ud, option); nghttp2_frame_origin_init(&frame, &ov, 1); frame.hd.flags = 0xf0; rv = nghttp2_frame_pack_origin(&bufs, &frame); assert_ptrdiff(0, ==, rv); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, rv); assert_int(0, ==, ud.frame_recv_cb_called); nghttp2_session_del(session); nghttp2_bufs_reset(&bufs); /* A frame should be ignored if it is received by a server. */ nghttp2_session_server_new2(&session, &callbacks, &ud, option); nghttp2_frame_origin_init(&frame, &ov, 1); rv = nghttp2_frame_pack_origin(&bufs, &frame); assert_ptrdiff(0, ==, rv); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, rv); assert_int(0, ==, ud.frame_recv_cb_called); nghttp2_session_del(session); nghttp2_bufs_reset(&bufs); /* Server receives too many ORIGIN frames. */ nghttp2_session_server_new2(&session, &callbacks, &ud, option); nghttp2_frame_origin_init(&frame, &ov, 1); rv = nghttp2_frame_pack_origin(&bufs, &frame); assert_ptrdiff(0, ==, rv); for (;;) { rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, rv); if (session->iframe.state == NGHTTP2_IB_IGN_ALL) { break; } } item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_uint32(NGHTTP2_ENHANCE_YOUR_CALM, ==, item->frame.goaway.error_code); nghttp2_session_del(session); nghttp2_bufs_reset(&bufs); /* Receiving empty ORIGIN frame */ nghttp2_session_client_new2(&session, &callbacks, &ud, option); nghttp2_frame_origin_init(&frame, NULL, 0); rv = nghttp2_frame_pack_origin(&bufs, &frame); assert_ptrdiff(0, ==, rv); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, rv); assert_int(1, ==, ud.frame_recv_cb_called); assert_uint8(NGHTTP2_ORIGIN, ==, ud.recv_frame_hd.type); nghttp2_session_del(session); nghttp2_option_del(option); nghttp2_bufs_free(&bufs); } void test_nghttp2_session_recv_priority_update(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; nghttp2_bufs bufs; nghttp2_ssize rv; nghttp2_option *option; nghttp2_extension frame; nghttp2_ext_priority_update priority_update; nghttp2_stream *stream; nghttp2_hd_deflater deflater; nghttp2_mem *mem; uint8_t large_field_value[sizeof(session->iframe.raw_sbuf) + 1]; nghttp2_outbound_item *item; size_t i; int32_t stream_id; static const uint8_t field_value[] = "u=2,i"; mem = nghttp2_mem_default(); memset(large_field_value, ' ', sizeof(large_field_value)); memcpy(large_field_value, field_value, sizeof(field_value) - 1); frame_pack_bufs_init(&bufs); frame.payload = &priority_update; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_frame_recv_callback = on_frame_recv_callback; nghttp2_option_new(&option); nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_PRIORITY_UPDATE); nghttp2_session_server_new2(&session, &callbacks, &ud, option); session->pending_no_rfc7540_priorities = 1; nghttp2_frame_priority_update_init(&frame, 1, (uint8_t *)field_value, sizeof(field_value) - 1); nghttp2_frame_pack_priority_update(&bufs, &frame); open_recv_stream(session, 1); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, rv); assert_int(1, ==, ud.frame_recv_cb_called); assert_uint8(NGHTTP2_PRIORITY_UPDATE, ==, ud.recv_frame_hd.type); assert_uint8(NGHTTP2_FLAG_NONE, ==, ud.recv_frame_hd.flags); assert_int32(0, ==, ud.recv_frame_hd.stream_id); stream = nghttp2_session_get_stream_raw(session, 1); assert_uint32(2, ==, nghttp2_extpri_uint8_urgency(stream->extpri)); assert_true(nghttp2_extpri_uint8_inc(stream->extpri)); nghttp2_session_del(session); nghttp2_bufs_reset(&bufs); /* Check that priority which is received in idle state is retained. */ nghttp2_session_server_new2(&session, &callbacks, &ud, option); session->pending_no_rfc7540_priorities = 1; nghttp2_frame_priority_update_init(&frame, 1, (uint8_t *)field_value, sizeof(field_value) - 1); nghttp2_frame_pack_priority_update(&bufs, &frame); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, rv); assert_int(1, ==, ud.frame_recv_cb_called); assert_uint8(NGHTTP2_PRIORITY_UPDATE, ==, ud.recv_frame_hd.type); assert_uint8(NGHTTP2_FLAG_NONE, ==, ud.recv_frame_hd.flags); assert_int32(0, ==, ud.recv_frame_hd.stream_id); stream = nghttp2_session_get_stream_raw(session, 1); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_IDLE, ==, stream->state); assert_uint32(2, ==, nghttp2_extpri_uint8_urgency(stream->extpri)); assert_true(nghttp2_extpri_uint8_inc(stream->extpri)); nghttp2_hd_deflate_init(&deflater, mem); nghttp2_bufs_reset(&bufs); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv, ARRLEN(reqnv), mem); assert_ptrdiff(0, ==, rv); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, rv); assert_int(1, ==, ud.frame_recv_cb_called); assert_uint8(NGHTTP2_HEADERS, ==, ud.recv_frame_hd.type); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_OPENING, ==, stream->state); assert_uint32(2, ==, nghttp2_extpri_uint8_urgency(stream->extpri)); assert_true(nghttp2_extpri_uint8_inc(stream->extpri)); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); nghttp2_bufs_reset(&bufs); /* PRIORITY_UPDATE with too large field_value is discarded */ nghttp2_session_server_new2(&session, &callbacks, &ud, option); session->pending_no_rfc7540_priorities = 1; nghttp2_frame_priority_update_init(&frame, 1, large_field_value, sizeof(large_field_value)); nghttp2_frame_pack_priority_update(&bufs, &frame); open_recv_stream(session, 1); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, rv); assert_int(0, ==, ud.frame_recv_cb_called); stream = nghttp2_session_get_stream_raw(session, 1); assert_uint32(NGHTTP2_EXTPRI_DEFAULT_URGENCY, ==, stream->extpri); nghttp2_session_del(session); nghttp2_bufs_reset(&bufs); /* Connection error if client receives PRIORITY_UPDATE. */ nghttp2_session_client_new2(&session, &callbacks, &ud, option); session->pending_no_rfc7540_priorities = 1; nghttp2_frame_priority_update_init(&frame, 1, (uint8_t *)field_value, sizeof(field_value) - 1); nghttp2_frame_pack_priority_update(&bufs, &frame); open_sent_stream(session, 1); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, rv); assert_int(0, ==, ud.frame_recv_cb_called); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_uint32(NGHTTP2_PROTOCOL_ERROR, ==, item->frame.goaway.error_code); nghttp2_session_del(session); nghttp2_bufs_reset(&bufs); /* The number of idle streams exceeds the maximum. */ nghttp2_session_server_new2(&session, &callbacks, &ud, option); session->pending_no_rfc7540_priorities = 1; session->local_settings.max_concurrent_streams = 100; for (i = 0; i < 101; ++i) { stream_id = (int32_t)(i * 2 + 1); nghttp2_frame_priority_update_init( &frame, stream_id, (uint8_t *)field_value, sizeof(field_value) - 1); nghttp2_frame_pack_priority_update(&bufs, &frame); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); if (i < 100) { assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, rv); assert_int(1, ==, ud.frame_recv_cb_called); assert_uint8(NGHTTP2_PRIORITY_UPDATE, ==, ud.recv_frame_hd.type); } else { assert_int(0, ==, ud.frame_recv_cb_called); } nghttp2_bufs_reset(&bufs); } item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_uint32(NGHTTP2_PROTOCOL_ERROR, ==, item->frame.goaway.error_code); nghttp2_session_del(session); nghttp2_option_del(option); nghttp2_bufs_free(&bufs); } void test_nghttp2_session_continue(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data user_data; const nghttp2_nv nv1[] = {MAKE_NV(":method", "GET"), MAKE_NV(":path", "/")}; const nghttp2_nv nv2[] = {MAKE_NV("user-agent", "nghttp2/1.0.0"), MAKE_NV("alpha", "bravo")}; nghttp2_bufs bufs; nghttp2_buf *buf; size_t framelen1, framelen2; nghttp2_ssize rv; uint8_t buffer[4096]; nghttp2_buf databuf; nghttp2_frame frame; nghttp2_nv *nva; size_t nvlen; const nghttp2_frame *recv_frame; nghttp2_frame_hd data_hd; nghttp2_hd_deflater deflater; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nghttp2_buf_wrap_init(&databuf, buffer, sizeof(buffer)); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_frame_recv_callback = on_frame_recv_callback; callbacks.on_data_chunk_recv_callback = pause_on_data_chunk_recv_callback; callbacks.on_header_callback = pause_on_header_callback; callbacks.on_begin_headers_callback = on_begin_headers_callback; nghttp2_session_server_new(&session, &callbacks, &user_data); /* disable strict HTTP layering checks */ session->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING; nghttp2_hd_deflate_init(&deflater, mem); /* Make 2 HEADERS frames */ nvlen = ARRLEN(nv1); nghttp2_nv_array_copy(&nva, nv1, nvlen, mem); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); assert_ptrdiff(0, ==, rv); assert_size(0, <, nghttp2_bufs_len(&bufs)); nghttp2_frame_headers_free(&frame.headers, mem); buf = &bufs.head->buf; assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); framelen1 = nghttp2_buf_len(buf); databuf.last = nghttp2_cpymem(databuf.last, buf->pos, nghttp2_buf_len(buf)); nvlen = ARRLEN(nv2); nghttp2_nv_array_copy(&nva, nv2, nvlen, mem); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3, NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); nghttp2_bufs_reset(&bufs); rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); assert_ptrdiff(0, ==, rv); assert_size(0, <, nghttp2_bufs_len(&bufs)); nghttp2_frame_headers_free(&frame.headers, mem); assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); framelen2 = nghttp2_buf_len(buf); databuf.last = nghttp2_cpymem(databuf.last, buf->pos, nghttp2_buf_len(buf)); /* Receive 1st HEADERS and pause */ user_data.begin_headers_cb_called = 0; user_data.header_cb_called = 0; rv = nghttp2_session_mem_recv2(session, databuf.pos, nghttp2_buf_len(&databuf)); assert_ptrdiff(0, <=, rv); databuf.pos += rv; recv_frame = user_data.frame; assert_uint8(NGHTTP2_HEADERS, ==, recv_frame->hd.type); assert_size(framelen1 - NGHTTP2_FRAME_HDLEN, ==, recv_frame->hd.length); assert_int(1, ==, user_data.begin_headers_cb_called); assert_int(1, ==, user_data.header_cb_called); assert_true(nghttp2_nv_equal(&nv1[0], &user_data.nv)); /* get 2nd header field */ user_data.begin_headers_cb_called = 0; user_data.header_cb_called = 0; rv = nghttp2_session_mem_recv2(session, databuf.pos, nghttp2_buf_len(&databuf)); assert_ptrdiff(0, <=, rv); databuf.pos += rv; assert_int(0, ==, user_data.begin_headers_cb_called); assert_int(1, ==, user_data.header_cb_called); assert_true(nghttp2_nv_equal(&nv1[1], &user_data.nv)); /* will call end_headers_callback and receive 2nd HEADERS and pause */ user_data.begin_headers_cb_called = 0; user_data.header_cb_called = 0; rv = nghttp2_session_mem_recv2(session, databuf.pos, nghttp2_buf_len(&databuf)); assert_ptrdiff(0, <=, rv); databuf.pos += rv; recv_frame = user_data.frame; assert_uint8(NGHTTP2_HEADERS, ==, recv_frame->hd.type); assert_size(framelen2 - NGHTTP2_FRAME_HDLEN, ==, recv_frame->hd.length); assert_int(1, ==, user_data.begin_headers_cb_called); assert_int(1, ==, user_data.header_cb_called); assert_true(nghttp2_nv_equal(&nv2[0], &user_data.nv)); /* get 2nd header field */ user_data.begin_headers_cb_called = 0; user_data.header_cb_called = 0; rv = nghttp2_session_mem_recv2(session, databuf.pos, nghttp2_buf_len(&databuf)); assert_ptrdiff(0, <=, rv); databuf.pos += rv; assert_int(0, ==, user_data.begin_headers_cb_called); assert_int(1, ==, user_data.header_cb_called); assert_true(nghttp2_nv_equal(&nv2[1], &user_data.nv)); /* No input data, frame_recv_callback is called */ user_data.begin_headers_cb_called = 0; user_data.header_cb_called = 0; user_data.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, databuf.pos, nghttp2_buf_len(&databuf)); assert_ptrdiff(0, <=, rv); databuf.pos += rv; assert_int(0, ==, user_data.begin_headers_cb_called); assert_int(0, ==, user_data.header_cb_called); assert_int(1, ==, user_data.frame_recv_cb_called); /* Receive DATA */ nghttp2_frame_hd_init(&data_hd, 16, NGHTTP2_DATA, NGHTTP2_FLAG_NONE, 1); nghttp2_buf_reset(&databuf); nghttp2_frame_pack_frame_hd(databuf.pos, &data_hd); /* Intentionally specify larger buffer size to see pause is kicked in. */ databuf.last = databuf.end; user_data.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, databuf.pos, nghttp2_buf_len(&databuf)); assert_ptrdiff(16 + NGHTTP2_FRAME_HDLEN, ==, rv); assert_int(0, ==, user_data.frame_recv_cb_called); /* Next nghttp2_session_mem_recv2 invokes on_frame_recv_callback and pause again in on_data_chunk_recv_callback since we pass same DATA frame. */ user_data.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, databuf.pos, nghttp2_buf_len(&databuf)); assert_ptrdiff(16 + NGHTTP2_FRAME_HDLEN, ==, rv); assert_int(1, ==, user_data.frame_recv_cb_called); /* And finally call on_frame_recv_callback with 0 size input */ user_data.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, NULL, 0); assert_ptrdiff(0, ==, rv); assert_int(1, ==, user_data.frame_recv_cb_called); nghttp2_bufs_free(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); } void test_nghttp2_session_add_frame(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; accumulator acc; my_user_data user_data; nghttp2_outbound_item *item; nghttp2_frame *frame; nghttp2_nv *nva; size_t nvlen; nghttp2_mem *mem; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = accumulator_send_callback; acc.length = 0; user_data.acc = &acc; assert_int(0, ==, nghttp2_session_client_new(&session, &callbacks, &user_data)); item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); nghttp2_outbound_item_init(item); frame = &item->frame; nvlen = ARRLEN(reqnv); nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); nghttp2_frame_headers_init( &frame->headers, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, (int32_t)session->next_stream_id, NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen); session->next_stream_id += 2; assert_int(0, ==, nghttp2_session_add_item(session, item)); assert_not_null(nghttp2_outbound_queue_top(&session->ob_syn)); assert_int(0, ==, nghttp2_session_send(session)); assert_uint8(NGHTTP2_HEADERS, ==, acc.buf[3]); assert_uint8((NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY), ==, acc.buf[4]); /* check stream id */ assert_uint32(1, ==, nghttp2_get_uint32(&acc.buf[5])); nghttp2_session_del(session); } void test_nghttp2_session_on_request_headers_received(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data user_data; nghttp2_frame frame; nghttp2_stream *stream; int32_t stream_id = 1; nghttp2_nv malformed_nva[] = {MAKE_NV(":path", "\x01")}; nghttp2_nv *nva; size_t nvlen; nghttp2_priority_spec pri_spec; nghttp2_mem *mem; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_begin_headers_callback = on_begin_headers_callback; callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; nghttp2_session_server_new(&session, &callbacks, &user_data); nghttp2_priority_spec_init(&pri_spec, 0, 255, 0); nghttp2_frame_headers_init( &frame.headers, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, stream_id, NGHTTP2_HCAT_REQUEST, &pri_spec, NULL, 0); user_data.begin_headers_cb_called = 0; user_data.invalid_frame_recv_cb_called = 0; assert_int(0, ==, nghttp2_session_on_request_headers_received(session, &frame)); assert_int(1, ==, user_data.begin_headers_cb_called); stream = nghttp2_session_get_stream(session, stream_id); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_OPENING, ==, stream->state); nghttp2_frame_headers_free(&frame.headers, mem); /* More than un-ACKed max concurrent streams leads REFUSED_STREAM */ session->pending_local_max_concurrent_stream = 1; nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, 3, NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); user_data.invalid_frame_recv_cb_called = 0; assert_int(NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_request_headers_received(session, &frame)); assert_int(1, ==, user_data.invalid_frame_recv_cb_called); assert_false(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); nghttp2_frame_headers_free(&frame.headers, mem); session->local_settings.max_concurrent_streams = NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS; /* Stream ID less than or equal to the previously received request HEADERS is just ignored due to race condition */ nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, 3, NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); user_data.invalid_frame_recv_cb_called = 0; assert_int(NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_request_headers_received(session, &frame)); assert_int(0, ==, user_data.invalid_frame_recv_cb_called); assert_false(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); nghttp2_frame_headers_free(&frame.headers, mem); /* Stream ID is our side and it is idle stream ID, then treat it as connection error */ nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, 2, NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); user_data.invalid_frame_recv_cb_called = 0; assert_int(NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_request_headers_received(session, &frame)); assert_int(1, ==, user_data.invalid_frame_recv_cb_called); assert_true(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); nghttp2_frame_headers_free(&frame.headers, mem); nghttp2_session_del(session); /* Check malformed headers. The library accept it. */ nghttp2_session_server_new(&session, &callbacks, &user_data); nvlen = ARRLEN(malformed_nva); nghttp2_nv_array_copy(&nva, malformed_nva, nvlen, mem); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, 1, NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); user_data.begin_headers_cb_called = 0; user_data.invalid_frame_recv_cb_called = 0; assert_int(0, ==, nghttp2_session_on_request_headers_received(session, &frame)); assert_int(1, ==, user_data.begin_headers_cb_called); assert_int(0, ==, user_data.invalid_frame_recv_cb_called); nghttp2_frame_headers_free(&frame.headers, mem); nghttp2_session_del(session); /* Check client side */ nghttp2_session_client_new(&session, &callbacks, &user_data); /* Receiving peer's idle stream ID is subject to connection error */ nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2, NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); user_data.invalid_frame_recv_cb_called = 0; assert_int(NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_request_headers_received(session, &frame)); assert_int(1, ==, user_data.invalid_frame_recv_cb_called); assert_true(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); nghttp2_frame_headers_free(&frame.headers, mem); nghttp2_session_del(session); nghttp2_session_client_new(&session, &callbacks, &user_data); /* Receiving our's idle stream ID is subject to connection error */ nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); user_data.invalid_frame_recv_cb_called = 0; assert_int(NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_request_headers_received(session, &frame)); assert_int(1, ==, user_data.invalid_frame_recv_cb_called); assert_true(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); nghttp2_frame_headers_free(&frame.headers, mem); nghttp2_session_del(session); nghttp2_session_client_new(&session, &callbacks, &user_data); session->next_stream_id = 5; session->last_sent_stream_id = 3; /* Stream ID which is not idle and not in stream map is just ignored */ nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3, NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); user_data.invalid_frame_recv_cb_called = 0; assert_int(NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_request_headers_received(session, &frame)); assert_int(0, ==, user_data.invalid_frame_recv_cb_called); assert_false(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); nghttp2_frame_headers_free(&frame.headers, mem); nghttp2_session_del(session); nghttp2_session_server_new(&session, &callbacks, &user_data); /* Stream ID which is equal to local_last_stream_id is ok. */ session->local_last_stream_id = 3; nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3, NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); assert_int(0, ==, nghttp2_session_on_request_headers_received(session, &frame)); nghttp2_frame_headers_free(&frame.headers, mem); /* If GOAWAY has been sent, new stream is ignored */ nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 5, NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); session->goaway_flags |= NGHTTP2_GOAWAY_SENT; user_data.invalid_frame_recv_cb_called = 0; assert_int(NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_request_headers_received(session, &frame)); assert_int(0, ==, user_data.invalid_frame_recv_cb_called); assert_false(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); nghttp2_frame_headers_free(&frame.headers, mem); nghttp2_session_del(session); nghttp2_session_server_new(&session, &callbacks, &user_data); /* HEADERS to closed stream */ stream = open_recv_stream(session, 1); nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); nghttp2_session_close_stream(session, 1, NGHTTP2_NO_ERROR); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); assert_int(NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_request_headers_received(session, &frame)); nghttp2_frame_headers_free(&frame.headers, mem); nghttp2_session_del(session); } void test_nghttp2_session_on_response_headers_received(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data user_data; nghttp2_frame frame; nghttp2_stream *stream; nghttp2_mem *mem; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_begin_headers_callback = on_begin_headers_callback; callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; nghttp2_session_client_new(&session, &callbacks, &user_data); stream = open_sent_stream2(session, 1, NGHTTP2_STREAM_OPENING); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); user_data.begin_headers_cb_called = 0; user_data.invalid_frame_recv_cb_called = 0; assert_int( 0, ==, nghttp2_session_on_response_headers_received(session, &frame, stream)); assert_int(1, ==, user_data.begin_headers_cb_called); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_OPENED, ==, stream->state); nghttp2_frame_headers_free(&frame.headers, mem); nghttp2_session_del(session); } void test_nghttp2_session_on_headers_received(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data user_data; nghttp2_frame frame; nghttp2_stream *stream; nghttp2_mem *mem; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_begin_headers_callback = on_begin_headers_callback; callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; nghttp2_session_client_new(&session, &callbacks, &user_data); stream = open_sent_stream2(session, 1, NGHTTP2_STREAM_OPENED); nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); user_data.begin_headers_cb_called = 0; user_data.invalid_frame_recv_cb_called = 0; assert_int(0, ==, nghttp2_session_on_headers_received(session, &frame, stream)); assert_int(1, ==, user_data.begin_headers_cb_called); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_OPENED, ==, stream->state); /* stream closed */ frame.hd.flags |= NGHTTP2_FLAG_END_STREAM; assert_int(0, ==, nghttp2_session_on_headers_received(session, &frame, stream)); assert_int(2, ==, user_data.begin_headers_cb_called); /* Check to see when NGHTTP2_STREAM_CLOSING, incoming HEADERS is discarded. */ stream = open_sent_stream2(session, 3, NGHTTP2_STREAM_CLOSING); frame.hd.stream_id = 3; frame.hd.flags = NGHTTP2_FLAG_END_HEADERS; assert_int(NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_headers_received(session, &frame, stream)); /* See no counters are updated */ assert_int(2, ==, user_data.begin_headers_cb_called); assert_int(0, ==, user_data.invalid_frame_recv_cb_called); /* Server initiated stream */ stream = open_recv_stream(session, 2); frame.hd.flags = NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM; frame.hd.stream_id = 2; assert_int(0, ==, nghttp2_session_on_headers_received(session, &frame, stream)); assert_int(3, ==, user_data.begin_headers_cb_called); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_OPENED, ==, stream->state); nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); /* Further reception of HEADERS is subject to stream error */ assert_int(NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_headers_received(session, &frame, stream)); assert_int(1, ==, user_data.invalid_frame_recv_cb_called); nghttp2_frame_headers_free(&frame.headers, mem); nghttp2_session_del(session); } void test_nghttp2_session_on_push_response_headers_received(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data user_data; nghttp2_frame frame; nghttp2_stream *stream; nghttp2_outbound_item *item; nghttp2_mem *mem; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_begin_headers_callback = on_begin_headers_callback; callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; nghttp2_session_client_new(&session, &callbacks, &user_data); stream = open_recv_stream2(session, 2, NGHTTP2_STREAM_RESERVED); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2, NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); /* nghttp2_session_on_push_response_headers_received assumes stream's state is NGHTTP2_STREAM_RESERVED and session->server is 0. */ user_data.begin_headers_cb_called = 0; user_data.invalid_frame_recv_cb_called = 0; assert_size(1, ==, session->num_incoming_reserved_streams); assert_int( 0, ==, nghttp2_session_on_push_response_headers_received(session, &frame, stream)); assert_int(1, ==, user_data.begin_headers_cb_called); assert_size(0, ==, session->num_incoming_reserved_streams); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_OPENED, ==, stream->state); assert_size(1, ==, session->num_incoming_streams); assert_false(stream->flags & NGHTTP2_STREAM_FLAG_PUSH); /* If un-ACKed max concurrent streams limit is exceeded, RST_STREAMed */ session->pending_local_max_concurrent_stream = 1; stream = open_recv_stream2(session, 4, NGHTTP2_STREAM_RESERVED); frame.hd.stream_id = 4; assert_int( NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_push_response_headers_received(session, &frame, stream)); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_RST_STREAM, ==, item->frame.hd.type); assert_uint32(NGHTTP2_REFUSED_STREAM, ==, item->frame.rst_stream.error_code); assert_size(1, ==, session->num_incoming_streams); assert_size(1, ==, session->num_incoming_reserved_streams); assert_int(0, ==, nghttp2_session_send(session)); assert_size(1, ==, session->num_incoming_streams); /* If ACKed max concurrent streams limit is exceeded, GOAWAY is issued */ session->local_settings.max_concurrent_streams = 1; stream = open_recv_stream2(session, 6, NGHTTP2_STREAM_RESERVED); frame.hd.stream_id = 6; assert_int( NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_push_response_headers_received(session, &frame, stream)); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_uint32(NGHTTP2_PROTOCOL_ERROR, ==, item->frame.goaway.error_code); assert_size(1, ==, session->num_incoming_streams); assert_size(1, ==, session->num_incoming_reserved_streams); nghttp2_frame_headers_free(&frame.headers, mem); nghttp2_session_del(session); } void test_nghttp2_session_on_rst_stream_received(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data user_data; nghttp2_frame frame; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); nghttp2_session_server_new(&session, &callbacks, &user_data); open_recv_stream(session, 1); nghttp2_frame_rst_stream_init(&frame.rst_stream, 1, NGHTTP2_PROTOCOL_ERROR); assert_int(0, ==, nghttp2_session_on_rst_stream_received(session, &frame)); assert_null(nghttp2_session_get_stream(session, 1)); nghttp2_frame_rst_stream_free(&frame.rst_stream); nghttp2_session_del(session); } void test_nghttp2_session_on_settings_received(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data user_data; nghttp2_stream *stream1, *stream2; nghttp2_frame frame; const size_t niv = 5; nghttp2_settings_entry iv[255]; nghttp2_outbound_item *item; nghttp2_nv nv = MAKE_NV(":authority", "example.org"); nghttp2_mem *mem; nghttp2_option *option; uint8_t data[2048]; nghttp2_frame_hd hd; int rv; nghttp2_ssize nread; nghttp2_stream *stream; mem = nghttp2_mem_default(); iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; iv[0].value = 50; iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; iv[1].value = 1000000009; iv[2].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; iv[2].value = 64 * 1024; iv[3].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[3].value = 1024; iv[4].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; iv[4].value = 0; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_client_new(&session, &callbacks, &user_data); session->remote_settings.initial_window_size = 16 * 1024; stream1 = open_sent_stream(session, 1); stream2 = open_recv_stream(session, 2); /* Set window size for each streams and will see how settings updates these values */ stream1->remote_window_size = 16 * 1024; stream2->remote_window_size = -48 * 1024; nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, niv), niv); assert_int(0, ==, nghttp2_session_on_settings_received(session, &frame, 0)); assert_uint32(1000000009, ==, session->remote_settings.max_concurrent_streams); assert_uint32(64 * 1024, ==, session->remote_settings.initial_window_size); assert_uint32(1024, ==, session->remote_settings.header_table_size); assert_uint32(0, ==, session->remote_settings.enable_push); assert_int32(64 * 1024, ==, stream1->remote_window_size); assert_int32(0, ==, stream2->remote_window_size); frame.settings.iv[2].value = 16 * 1024; assert_int(0, ==, nghttp2_session_on_settings_received(session, &frame, 0)); assert_int32(16 * 1024, ==, stream1->remote_window_size); assert_int32(-48 * 1024, ==, stream2->remote_window_size); assert_int32( 16 * 1024, ==, nghttp2_session_get_stream_remote_window_size(session, stream1->stream_id)); assert_int32( 0, ==, nghttp2_session_get_stream_remote_window_size(session, stream2->stream_id)); nghttp2_frame_settings_free(&frame.settings, mem); nghttp2_session_del(session); /* Check ACK with niv > 0 */ nghttp2_session_server_new(&session, &callbacks, NULL); nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_ACK, dup_iv(iv, 1), 1); assert_int(0, ==, nghttp2_session_on_settings_received(session, &frame, 0)); item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); nghttp2_frame_settings_free(&frame.settings, mem); nghttp2_session_del(session); /* Check ACK against no inflight SETTINGS */ nghttp2_session_server_new(&session, &callbacks, NULL); nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_ACK, NULL, 0); assert_int(0, ==, nghttp2_session_on_settings_received(session, &frame, 0)); item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); nghttp2_frame_settings_free(&frame.settings, mem); nghttp2_session_del(session); /* Check that 2 SETTINGS_HEADER_TABLE_SIZE 0 and 4096 are included and header table size is once cleared to 0. */ nghttp2_session_client_new(&session, &callbacks, NULL); nghttp2_submit_request2(session, NULL, &nv, 1, NULL, NULL); nghttp2_session_send(session); assert_size(0, <, session->hd_deflater.ctx.hd_table.len); iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[0].value = 0; iv[1].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[1].value = 2048; nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 2), 2); assert_int(0, ==, nghttp2_session_on_settings_received(session, &frame, 0)); assert_size(0, ==, session->hd_deflater.ctx.hd_table.len); assert_size(2048, ==, session->hd_deflater.ctx.hd_table_bufsize_max); assert_uint32(2048, ==, session->remote_settings.header_table_size); nghttp2_frame_settings_free(&frame.settings, mem); nghttp2_session_del(session); /* Check that remote SETTINGS_MAX_CONCURRENT_STREAMS is set to a value set by nghttp2_option_set_peer_max_concurrent_streams() and reset to the default value (unlimited) after receiving initial SETTINGS frame from the peer. */ nghttp2_option_new(&option); nghttp2_option_set_peer_max_concurrent_streams(option, 1000); nghttp2_session_client_new2(&session, &callbacks, NULL, option); assert_uint32(1000, ==, session->remote_settings.max_concurrent_streams); nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, NULL, 0); assert_int(0, ==, nghttp2_session_on_settings_received(session, &frame, 0)); assert_uint32(NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS, ==, session->remote_settings.max_concurrent_streams); nghttp2_frame_settings_free(&frame.settings, mem); nghttp2_session_del(session); nghttp2_option_del(option); /* Check too large SETTINGS_MAX_FRAME_SIZE */ nghttp2_session_server_new(&session, &callbacks, NULL); iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MAX + 1; nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 1), 1); assert_int(0, ==, nghttp2_session_on_settings_received(session, &frame, 0)); item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); nghttp2_frame_settings_free(&frame.settings, mem); nghttp2_session_del(session); /* Check the case where stream window size overflows */ nghttp2_session_server_new(&session, &callbacks, NULL); stream1 = open_recv_stream(session, 1); /* This will increment window size by 1 */ nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 1, 1); assert_int(0, ==, nghttp2_session_on_window_update_received(session, &frame)); nghttp2_frame_window_update_free(&frame.window_update); iv[0].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; iv[0].value = NGHTTP2_MAX_WINDOW_SIZE; nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 1), 1); /* Now window size gets NGHTTP2_MAX_WINDOW_SIZE + 1, which is unacceptable situation in protocol spec. */ assert_int(0, ==, nghttp2_session_on_settings_received(session, &frame, 0)); nghttp2_frame_settings_free(&frame.settings, mem); item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); nghttp2_session_del(session); /* It is invalid that peer disables ENABLE_CONNECT_PROTOCOL once it has been enabled. */ nghttp2_session_client_new(&session, &callbacks, NULL); session->remote_settings.enable_connect_protocol = 1; iv[0].settings_id = NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL; iv[0].value = 0; nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 1), 1); assert_int(0, ==, nghttp2_session_on_settings_received(session, &frame, 0)); nghttp2_frame_settings_free(&frame.settings, mem); item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); nghttp2_session_del(session); /* Should send WINDOW_UPDATE with no_auto_window_update option on if the initial window size is decreased and becomes smaller than or equal to the amount of data that has already received. */ nghttp2_option_new(&option); nghttp2_option_set_no_auto_window_update(option, 1); nghttp2_session_server_new2(&session, &callbacks, NULL, option); iv[0].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; iv[0].value = 1024; rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1); assert_int(0, ==, rv); rv = nghttp2_session_send(session); assert_int(0, ==, rv); stream = open_recv_stream(session, 1); memset(data, 0, sizeof(data)); hd.length = 1024; hd.type = NGHTTP2_DATA; hd.flags = NGHTTP2_FLAG_NONE; hd.stream_id = 1; nghttp2_frame_pack_frame_hd(data, &hd); nread = nghttp2_session_mem_recv2(session, data, NGHTTP2_FRAME_HDLEN + hd.length); assert_ptrdiff((nghttp2_ssize)(NGHTTP2_FRAME_HDLEN + hd.length), ==, nread); rv = nghttp2_session_consume(session, 1, hd.length); assert_int(0, ==, rv); assert_int32((int32_t)hd.length, ==, stream->recv_window_size); assert_int32((int32_t)hd.length, ==, stream->consumed_size); nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_ACK, NULL, 0); rv = nghttp2_session_on_settings_received(session, &frame, 0); assert_int(0, ==, rv); assert_int32(1024, ==, stream->local_window_size); assert_int32(0, ==, stream->recv_window_size); assert_int32(0, ==, stream->consumed_size); item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_WINDOW_UPDATE, ==, item->frame.hd.type); assert_int32((int32_t)hd.length, ==, item->frame.window_update.window_size_increment); nghttp2_session_del(session); nghttp2_option_del(option); /* It is invalid to change SETTINGS_NO_RFC7540_PRIORITIES in the following SETTINGS. */ nghttp2_session_client_new(&session, &callbacks, NULL); session->remote_settings.no_rfc7540_priorities = 1; iv[0].settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES; iv[0].value = 0; nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 1), 1); assert_int(0, ==, nghttp2_session_on_settings_received(session, &frame, 0)); nghttp2_frame_settings_free(&frame.settings, mem); item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); nghttp2_session_del(session); } void test_nghttp2_session_on_push_promise_received(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data user_data; nghttp2_frame frame; nghttp2_stream *stream, *promised_stream; nghttp2_outbound_item *item; nghttp2_nv malformed_nva[] = {MAKE_NV(":path", "\x01")}; nghttp2_nv *nva; size_t nvlen; nghttp2_mem *mem; nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_ENABLE_PUSH, 0}; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_begin_headers_callback = on_begin_headers_callback; callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; nghttp2_session_client_new(&session, &callbacks, &user_data); stream = open_sent_stream(session, 1); nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, 1, 2, NULL, 0); user_data.begin_headers_cb_called = 0; user_data.invalid_frame_recv_cb_called = 0; assert_int(0, ==, nghttp2_session_on_push_promise_received(session, &frame)); assert_int(1, ==, user_data.begin_headers_cb_called); assert_size(1, ==, session->num_incoming_reserved_streams); promised_stream = nghttp2_session_get_stream(session, 2); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_RESERVED, ==, promised_stream->state); assert_int32(2, ==, session->last_recv_stream_id); /* Attempt to PUSH_PROMISE against half close (remote) */ nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); frame.push_promise.promised_stream_id = 4; user_data.begin_headers_cb_called = 0; user_data.invalid_frame_recv_cb_called = 0; assert_int(NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_push_promise_received(session, &frame)); assert_int(0, ==, user_data.begin_headers_cb_called); assert_int(1, ==, user_data.invalid_frame_recv_cb_called); assert_size(1, ==, session->num_incoming_reserved_streams); assert_null(nghttp2_session_get_stream(session, 4)); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_uint32(NGHTTP2_STREAM_CLOSED, ==, item->frame.goaway.error_code); assert_int(0, ==, nghttp2_session_send(session)); assert_int32(4, ==, session->last_recv_stream_id); nghttp2_session_del(session); nghttp2_session_client_new(&session, &callbacks, &user_data); stream = open_sent_stream(session, 1); /* Attempt to PUSH_PROMISE against stream in closing state */ stream->state = NGHTTP2_STREAM_CLOSING; frame.push_promise.promised_stream_id = 6; user_data.begin_headers_cb_called = 0; user_data.invalid_frame_recv_cb_called = 0; assert_int(NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_push_promise_received(session, &frame)); assert_int(0, ==, user_data.begin_headers_cb_called); assert_size(0, ==, session->num_incoming_reserved_streams); assert_null(nghttp2_session_get_stream(session, 6)); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_RST_STREAM, ==, item->frame.hd.type); assert_int32(6, ==, item->frame.hd.stream_id); assert_uint32(NGHTTP2_CANCEL, ==, item->frame.rst_stream.error_code); assert_int(0, ==, nghttp2_session_send(session)); /* Attempt to PUSH_PROMISE against idle stream */ frame.hd.stream_id = 3; frame.push_promise.promised_stream_id = 8; user_data.begin_headers_cb_called = 0; user_data.invalid_frame_recv_cb_called = 0; assert_int(NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_push_promise_received(session, &frame)); assert_int(0, ==, user_data.begin_headers_cb_called); assert_size(0, ==, session->num_incoming_reserved_streams); assert_null(nghttp2_session_get_stream(session, 8)); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_int32(0, ==, item->frame.hd.stream_id); assert_uint32(NGHTTP2_PROTOCOL_ERROR, ==, item->frame.goaway.error_code); assert_int(0, ==, nghttp2_session_send(session)); nghttp2_session_del(session); nghttp2_session_client_new(&session, &callbacks, &user_data); stream = open_sent_stream(session, 1); /* Same ID twice */ frame.hd.stream_id = 1; frame.push_promise.promised_stream_id = 2; user_data.begin_headers_cb_called = 0; user_data.invalid_frame_recv_cb_called = 0; assert_int(0, ==, nghttp2_session_on_push_promise_received(session, &frame)); assert_int(1, ==, user_data.begin_headers_cb_called); assert_size(1, ==, session->num_incoming_reserved_streams); assert_not_null(nghttp2_session_get_stream(session, 2)); user_data.begin_headers_cb_called = 0; user_data.invalid_frame_recv_cb_called = 0; assert_int(NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_push_promise_received(session, &frame)); assert_int(0, ==, user_data.begin_headers_cb_called); assert_size(1, ==, session->num_incoming_reserved_streams); assert_null(nghttp2_session_get_stream(session, 8)); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_uint32(NGHTTP2_PROTOCOL_ERROR, ==, item->frame.goaway.error_code); assert_int(0, ==, nghttp2_session_send(session)); /* After GOAWAY, PUSH_PROMISE will be discarded */ frame.push_promise.promised_stream_id = 10; user_data.begin_headers_cb_called = 0; user_data.invalid_frame_recv_cb_called = 0; assert_int(NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_push_promise_received(session, &frame)); assert_int(0, ==, user_data.begin_headers_cb_called); assert_size(1, ==, session->num_incoming_reserved_streams); assert_null(nghttp2_session_get_stream(session, 10)); assert_null(nghttp2_session_get_next_ob_item(session)); nghttp2_frame_push_promise_free(&frame.push_promise, mem); nghttp2_session_del(session); nghttp2_session_client_new(&session, &callbacks, &user_data); open_recv_stream2(session, 2, NGHTTP2_STREAM_RESERVED); /* Attempt to PUSH_PROMISE against reserved (remote) stream */ nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, 2, 4, NULL, 0); user_data.begin_headers_cb_called = 0; user_data.invalid_frame_recv_cb_called = 0; assert_int(NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_push_promise_received(session, &frame)); assert_int(0, ==, user_data.begin_headers_cb_called); assert_int(1, ==, user_data.invalid_frame_recv_cb_called); assert_size(1, ==, session->num_incoming_reserved_streams); nghttp2_frame_push_promise_free(&frame.push_promise, mem); nghttp2_session_del(session); /* Disable PUSH */ nghttp2_session_client_new(&session, &callbacks, &user_data); open_sent_stream(session, 1); session->local_settings.enable_push = 0; nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, 1, 2, NULL, 0); user_data.begin_headers_cb_called = 0; user_data.invalid_frame_recv_cb_called = 0; assert_int(NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_push_promise_received(session, &frame)); assert_int(0, ==, user_data.begin_headers_cb_called); assert_int(1, ==, user_data.invalid_frame_recv_cb_called); assert_size(0, ==, session->num_incoming_reserved_streams); nghttp2_frame_push_promise_free(&frame.push_promise, mem); nghttp2_session_del(session); /* Check malformed headers. We accept malformed headers */ nghttp2_session_client_new(&session, &callbacks, &user_data); open_sent_stream(session, 1); nvlen = ARRLEN(malformed_nva); nghttp2_nv_array_copy(&nva, malformed_nva, nvlen, mem); nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, 1, 2, nva, nvlen); user_data.begin_headers_cb_called = 0; user_data.invalid_frame_recv_cb_called = 0; assert_int(0, ==, nghttp2_session_on_push_promise_received(session, &frame)); assert_int(1, ==, user_data.begin_headers_cb_called); assert_int(0, ==, user_data.invalid_frame_recv_cb_called); nghttp2_frame_push_promise_free(&frame.push_promise, mem); nghttp2_session_del(session); /* If local_settings.enable_push = 0 is pending, but not acked from peer, incoming PUSH_PROMISE is rejected */ nghttp2_session_client_new(&session, &callbacks, &user_data); open_sent_stream(session, 1); /* Submit settings with ENABLE_PUSH = 0 (thus disabling push) */ nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1); nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, 1, 2, NULL, 0); assert_int(NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_push_promise_received(session, &frame)); assert_size(0, ==, session->num_incoming_reserved_streams); nghttp2_frame_push_promise_free(&frame.push_promise, mem); nghttp2_session_del(session); /* Check max_incoming_reserved_streams */ nghttp2_session_client_new(&session, &callbacks, &user_data); session->max_incoming_reserved_streams = 1; open_sent_stream(session, 1); open_recv_stream2(session, 2, NGHTTP2_STREAM_RESERVED); assert_size(1, ==, session->num_incoming_reserved_streams); nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, 1, 4, NULL, 0); assert_int(NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_push_promise_received(session, &frame)); assert_size(1, ==, session->num_incoming_reserved_streams); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_RST_STREAM, ==, item->frame.hd.type); assert_uint32(NGHTTP2_CANCEL, ==, item->frame.rst_stream.error_code); nghttp2_frame_push_promise_free(&frame.push_promise, mem); nghttp2_session_del(session); } void test_nghttp2_session_on_ping_received(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data user_data; nghttp2_frame frame; nghttp2_outbound_item *top; const uint8_t opaque_data[] = "01234567"; nghttp2_option *option; user_data.frame_recv_cb_called = 0; user_data.invalid_frame_recv_cb_called = 0; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_frame_recv_callback = on_frame_recv_callback; callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; nghttp2_session_client_new(&session, &callbacks, &user_data); nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_ACK, opaque_data); assert_int(0, ==, nghttp2_session_on_ping_received(session, &frame)); assert_int(1, ==, user_data.frame_recv_cb_called); /* Since this ping frame has ACK flag set, no further action is performed. */ assert_null(nghttp2_outbound_queue_top(&session->ob_urgent)); /* Clear the flag, and receive it again */ frame.hd.flags = NGHTTP2_FLAG_NONE; assert_int(0, ==, nghttp2_session_on_ping_received(session, &frame)); assert_int(2, ==, user_data.frame_recv_cb_called); top = nghttp2_outbound_queue_top(&session->ob_urgent); assert_uint8(NGHTTP2_PING, ==, top->frame.hd.type); assert_uint8(NGHTTP2_FLAG_ACK, ==, top->frame.hd.flags); assert_memory_equal(8, opaque_data, top->frame.ping.opaque_data); nghttp2_frame_ping_free(&frame.ping); nghttp2_session_del(session); /* Use nghttp2_option_set_no_auto_ping_ack() */ nghttp2_option_new(&option); nghttp2_option_set_no_auto_ping_ack(option, 1); nghttp2_session_server_new2(&session, &callbacks, &user_data, option); nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL); user_data.frame_recv_cb_called = 0; assert_int(0, ==, nghttp2_session_on_ping_received(session, &frame)); assert_int(1, ==, user_data.frame_recv_cb_called); assert_null(nghttp2_outbound_queue_top(&session->ob_urgent)); nghttp2_frame_ping_free(&frame.ping); nghttp2_session_del(session); nghttp2_option_del(option); } void test_nghttp2_session_on_goaway_received(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data user_data; nghttp2_frame frame; int i; nghttp2_mem *mem; const uint8_t *data; nghttp2_ssize datalen; mem = nghttp2_mem_default(); user_data.frame_recv_cb_called = 0; user_data.invalid_frame_recv_cb_called = 0; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_frame_recv_callback = on_frame_recv_callback; callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; callbacks.on_stream_close_callback = on_stream_close_callback; nghttp2_session_client_new(&session, &callbacks, &user_data); for (i = 1; i <= 7; ++i) { if (nghttp2_session_is_my_stream_id(session, i)) { open_sent_stream(session, i); } else { open_recv_stream(session, i); } } nghttp2_frame_goaway_init(&frame.goaway, 3, NGHTTP2_PROTOCOL_ERROR, NULL, 0); user_data.stream_close_cb_called = 0; assert_int(0, ==, nghttp2_session_on_goaway_received(session, &frame)); assert_int(1, ==, user_data.frame_recv_cb_called); assert_int32(3, ==, session->remote_last_stream_id); /* on_stream_close should be callsed for 2 times (stream 5 and 7) */ assert_int(2, ==, user_data.stream_close_cb_called); assert_not_null(nghttp2_session_get_stream(session, 1)); assert_not_null(nghttp2_session_get_stream(session, 2)); assert_not_null(nghttp2_session_get_stream(session, 3)); assert_not_null(nghttp2_session_get_stream(session, 4)); assert_null(nghttp2_session_get_stream(session, 5)); assert_not_null(nghttp2_session_get_stream(session, 6)); assert_null(nghttp2_session_get_stream(session, 7)); nghttp2_frame_goaway_free(&frame.goaway, mem); nghttp2_session_del(session); /* Make sure that no memory leak when stream_close callback fails with a fatal error */ memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_stream_close_callback = fatal_error_on_stream_close_callback; memset(&user_data, 0, sizeof(user_data)); nghttp2_session_client_new(&session, &callbacks, &user_data); nghttp2_frame_goaway_init(&frame.goaway, 0, NGHTTP2_NO_ERROR, NULL, 0); assert_int(0, ==, nghttp2_session_on_goaway_received(session, &frame)); nghttp2_submit_request2(session, NULL, reqnv, ARRLEN(reqnv), NULL, NULL); datalen = nghttp2_session_mem_send2(session, &data); assert_ptrdiff(NGHTTP2_ERR_CALLBACK_FAILURE, ==, datalen); assert_int(1, ==, user_data.stream_close_cb_called); nghttp2_frame_goaway_free(&frame.goaway, mem); nghttp2_session_del(session); } void test_nghttp2_session_on_window_update_received(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data user_data; nghttp2_frame frame; nghttp2_stream *stream; nghttp2_outbound_item *data_item; nghttp2_mem *mem; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_frame_recv_callback = on_frame_recv_callback; callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; user_data.frame_recv_cb_called = 0; user_data.invalid_frame_recv_cb_called = 0; nghttp2_session_client_new(&session, &callbacks, &user_data); stream = open_sent_stream(session, 1); data_item = create_data_ob_item(mem); nghttp2_stream_attach_item(stream, data_item); nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 1, 16 * 1024); assert_int(0, ==, nghttp2_session_on_window_update_received(session, &frame)); assert_int(1, ==, user_data.frame_recv_cb_called); assert_int32(NGHTTP2_INITIAL_WINDOW_SIZE + 16 * 1024, ==, stream->remote_window_size); nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); assert_int(0, ==, nghttp2_session_on_window_update_received(session, &frame)); assert_int(2, ==, user_data.frame_recv_cb_called); assert_int32(NGHTTP2_INITIAL_WINDOW_SIZE + 16 * 1024 * 2, ==, stream->remote_window_size); assert_false(stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL); nghttp2_frame_window_update_free(&frame.window_update); /* Receiving WINDOW_UPDATE on reserved (remote) stream is a connection error */ open_recv_stream2(session, 2, NGHTTP2_STREAM_RESERVED); nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 2, 4096); assert_false(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); assert_int(0, ==, nghttp2_session_on_window_update_received(session, &frame)); assert_true(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); nghttp2_frame_window_update_free(&frame.window_update); nghttp2_session_del(session); /* Receiving WINDOW_UPDATE on reserved (local) stream is allowed */ nghttp2_session_server_new(&session, &callbacks, &user_data); stream = open_sent_stream2(session, 2, NGHTTP2_STREAM_RESERVED); nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 2, 4096); assert_int(0, ==, nghttp2_session_on_window_update_received(session, &frame)); assert_false(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); assert_int32(NGHTTP2_INITIAL_WINDOW_SIZE + 4096, ==, stream->remote_window_size); nghttp2_frame_window_update_free(&frame.window_update); nghttp2_session_del(session); } void test_nghttp2_session_on_data_received(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data user_data; nghttp2_outbound_item *top; nghttp2_stream *stream; nghttp2_frame frame; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); nghttp2_session_client_new(&session, &callbacks, &user_data); stream = open_recv_stream(session, 2); nghttp2_frame_hd_init(&frame.hd, 4096, NGHTTP2_DATA, NGHTTP2_FLAG_NONE, 2); assert_int(0, ==, nghttp2_session_on_data_received(session, &frame)); assert_uint8(0, ==, stream->shut_flags); frame.hd.flags = NGHTTP2_FLAG_END_STREAM; assert_int(0, ==, nghttp2_session_on_data_received(session, &frame)); assert_uint8(NGHTTP2_SHUT_RD, ==, stream->shut_flags); /* If NGHTTP2_STREAM_CLOSING state, DATA frame is discarded. */ open_sent_stream2(session, 1, NGHTTP2_STREAM_CLOSING); frame.hd.flags = NGHTTP2_FLAG_NONE; frame.hd.stream_id = 1; assert_int(0, ==, nghttp2_session_on_data_received(session, &frame)); assert_null(nghttp2_outbound_queue_top(&session->ob_reg)); /* Check INVALID_STREAM case: DATA frame with stream ID which does not exist. */ frame.hd.stream_id = 3; assert_int(0, ==, nghttp2_session_on_data_received(session, &frame)); top = nghttp2_outbound_queue_top(&session->ob_reg); /* DATA against nonexistent stream is just ignored for now. */ assert_null(top); nghttp2_session_del(session); } void test_nghttp2_session_on_data_received_fail_fast(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; uint8_t buf[9]; nghttp2_stream *stream; nghttp2_frame_hd hd; nghttp2_outbound_item *item; memset(&callbacks, 0, sizeof(callbacks)); nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_NONE, 1); nghttp2_frame_pack_frame_hd(buf, &hd); nghttp2_session_server_new(&session, &callbacks, NULL); /* DATA to closed (remote) */ stream = open_recv_stream(session, 1); nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); assert_ptrdiff((nghttp2_ssize)sizeof(buf), ==, nghttp2_session_mem_recv2(session, buf, sizeof(buf))); item = nghttp2_session_get_next_ob_item(session); assert_not_null(item); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); nghttp2_session_del(session); nghttp2_session_server_new(&session, &callbacks, NULL); /* DATA to closed stream with explicit closed (remote) */ stream = open_recv_stream(session, 1); nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); nghttp2_session_close_stream(session, 1, NGHTTP2_NO_ERROR); assert_ptrdiff((nghttp2_ssize)sizeof(buf), ==, nghttp2_session_mem_recv2(session, buf, sizeof(buf))); item = nghttp2_session_get_next_ob_item(session); assert_null(item); nghttp2_session_del(session); } void test_nghttp2_session_on_altsvc_received(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; nghttp2_frame frame; nghttp2_option *option; uint8_t origin[] = "nghttp2.org"; uint8_t field_value[] = "h2=\":443\""; int rv; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_frame_recv_callback = on_frame_recv_callback; nghttp2_option_new(&option); nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_ALTSVC); nghttp2_session_client_new2(&session, &callbacks, &ud, option); frame.ext.payload = &session->iframe.ext_frame_payload; /* We just pass the strings without making a copy. This is OK, since we never call nghttp2_frame_altsvc_free(). */ nghttp2_frame_altsvc_init(&frame.ext, 0, origin, sizeof(origin) - 1, field_value, sizeof(field_value) - 1); ud.frame_recv_cb_called = 0; rv = nghttp2_session_on_altsvc_received(session, &frame); assert_int(0, ==, rv); assert_int(1, ==, ud.frame_recv_cb_called); nghttp2_session_del(session); /* Receiving empty origin with stream ID == 0 */ nghttp2_session_client_new2(&session, &callbacks, &ud, option); frame.ext.payload = &session->iframe.ext_frame_payload; nghttp2_frame_altsvc_init(&frame.ext, 0, origin, 0, field_value, sizeof(field_value) - 1); ud.frame_recv_cb_called = 0; rv = nghttp2_session_on_altsvc_received(session, &frame); assert_int(0, ==, rv); assert_int(0, ==, ud.frame_recv_cb_called); nghttp2_session_del(session); /* Receiving non-empty origin with stream ID != 0 */ nghttp2_session_client_new2(&session, &callbacks, &ud, option); frame.ext.payload = &session->iframe.ext_frame_payload; open_sent_stream(session, 1); nghttp2_frame_altsvc_init(&frame.ext, 1, origin, sizeof(origin) - 1, field_value, sizeof(field_value) - 1); ud.frame_recv_cb_called = 0; rv = nghttp2_session_on_altsvc_received(session, &frame); assert_int(0, ==, rv); assert_int(0, ==, ud.frame_recv_cb_called); nghttp2_session_del(session); /* Receiving empty origin with stream ID != 0; this is OK */ nghttp2_session_client_new2(&session, &callbacks, &ud, option); frame.ext.payload = &session->iframe.ext_frame_payload; open_sent_stream(session, 1); nghttp2_frame_altsvc_init(&frame.ext, 1, origin, 0, field_value, sizeof(field_value) - 1); ud.frame_recv_cb_called = 0; rv = nghttp2_session_on_altsvc_received(session, &frame); assert_int(0, ==, rv); assert_int(1, ==, ud.frame_recv_cb_called); nghttp2_session_del(session); /* Stream does not exist; ALTSVC will be ignored. */ nghttp2_session_client_new2(&session, &callbacks, &ud, option); frame.ext.payload = &session->iframe.ext_frame_payload; nghttp2_frame_altsvc_init(&frame.ext, 1, origin, 0, field_value, sizeof(field_value) - 1); ud.frame_recv_cb_called = 0; rv = nghttp2_session_on_altsvc_received(session, &frame); assert_int(0, ==, rv); assert_int(0, ==, ud.frame_recv_cb_called); nghttp2_session_del(session); nghttp2_option_del(option); } void test_nghttp2_session_send_headers_start_stream(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_outbound_item *item; nghttp2_frame *frame; nghttp2_stream *stream; nghttp2_mem *mem; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_client_new(&session, &callbacks, NULL); item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); nghttp2_outbound_item_init(item); frame = &item->frame; nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS, (int32_t)session->next_stream_id, NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); session->next_stream_id += 2; nghttp2_session_add_item(session, item); assert_int(0, ==, nghttp2_session_send(session)); stream = nghttp2_session_get_stream(session, 1); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_OPENING, ==, stream->state); nghttp2_session_del(session); } void test_nghttp2_session_send_headers_reply(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_outbound_item *item; nghttp2_frame *frame; nghttp2_stream *stream; nghttp2_mem *mem; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; assert_int(0, ==, nghttp2_session_server_new(&session, &callbacks, NULL)); open_recv_stream2(session, 1, NGHTTP2_STREAM_OPENING); item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); nghttp2_outbound_item_init(item); frame = &item->frame; nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS, 1, NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); nghttp2_session_add_item(session, item); assert_int(0, ==, nghttp2_session_send(session)); stream = nghttp2_session_get_stream(session, 1); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_OPENED, ==, stream->state); nghttp2_session_del(session); } void test_nghttp2_session_send_headers_frame_size_error(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_outbound_item *item; nghttp2_frame *frame; nghttp2_nv *nva; size_t nvlen; size_t vallen = NGHTTP2_HD_MAX_NV; nghttp2_nv nv[28]; size_t nnv = ARRLEN(nv); size_t i; my_user_data ud; nghttp2_mem *mem; mem = nghttp2_mem_default(); for (i = 0; i < nnv; ++i) { nv[i].name = (uint8_t *)"header"; nv[i].namelen = strlen((const char *)nv[i].name); nv[i].value = mem->malloc(vallen + 1, NULL); memset(nv[i].value, '0' + (int)i, vallen); nv[i].value[vallen] = '\0'; nv[i].valuelen = vallen; nv[i].flags = NGHTTP2_NV_FLAG_NONE; } memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_frame_not_send_callback = on_frame_not_send_callback; nghttp2_session_client_new(&session, &callbacks, &ud); nvlen = nnv; nghttp2_nv_array_copy(&nva, nv, nvlen, mem); item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); nghttp2_outbound_item_init(item); frame = &item->frame; nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS, (int32_t)session->next_stream_id, NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen); session->next_stream_id += 2; nghttp2_session_add_item(session, item); ud.frame_not_send_cb_called = 0; assert_int(0, ==, nghttp2_session_send(session)); assert_int(1, ==, ud.frame_not_send_cb_called); assert_uint8(NGHTTP2_HEADERS, ==, ud.not_sent_frame_type); assert_int(NGHTTP2_ERR_FRAME_SIZE_ERROR, ==, ud.not_sent_error); for (i = 0; i < nnv; ++i) { mem->free(nv[i].value, NULL); } nghttp2_session_del(session); } void test_nghttp2_session_send_headers_push_reply(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_outbound_item *item; nghttp2_frame *frame; nghttp2_stream *stream; nghttp2_mem *mem; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; assert_int(0, ==, nghttp2_session_server_new(&session, &callbacks, NULL)); open_sent_stream2(session, 2, NGHTTP2_STREAM_RESERVED); item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); nghttp2_outbound_item_init(item); frame = &item->frame; nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS, 2, NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); nghttp2_session_add_item(session, item); assert_size(0, ==, session->num_outgoing_streams); assert_int(0, ==, nghttp2_session_send(session)); assert_size(1, ==, session->num_outgoing_streams); stream = nghttp2_session_get_stream(session, 2); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_OPENED, ==, stream->state); assert_false(stream->flags & NGHTTP2_STREAM_FLAG_PUSH); nghttp2_session_del(session); } void test_nghttp2_session_send_rst_stream(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data user_data; nghttp2_outbound_item *item; nghttp2_frame *frame; nghttp2_mem *mem; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_client_new(&session, &callbacks, &user_data); open_sent_stream(session, 1); item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); nghttp2_outbound_item_init(item); frame = &item->frame; nghttp2_frame_rst_stream_init(&frame->rst_stream, 1, NGHTTP2_PROTOCOL_ERROR); nghttp2_session_add_item(session, item); assert_int(0, ==, nghttp2_session_send(session)); assert_null(nghttp2_session_get_stream(session, 1)); nghttp2_session_del(session); } void test_nghttp2_session_send_push_promise(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_outbound_item *item; nghttp2_frame *frame; nghttp2_stream *stream; nghttp2_settings_entry iv; my_user_data ud; nghttp2_mem *mem; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_frame_not_send_callback = on_frame_not_send_callback; nghttp2_session_server_new(&session, &callbacks, &ud); open_recv_stream(session, 1); item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); nghttp2_outbound_item_init(item); frame = &item->frame; nghttp2_frame_push_promise_init(&frame->push_promise, NGHTTP2_FLAG_END_HEADERS, 1, (int32_t)session->next_stream_id, NULL, 0); session->next_stream_id += 2; nghttp2_session_add_item(session, item); assert_int(0, ==, nghttp2_session_send(session)); stream = nghttp2_session_get_stream(session, 2); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_RESERVED, ==, stream->state); /* Received ENABLE_PUSH = 0 */ iv.settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; iv.value = 0; frame = mem->malloc(sizeof(nghttp2_frame), NULL); nghttp2_frame_settings_init(&frame->settings, NGHTTP2_FLAG_NONE, dup_iv(&iv, 1), 1); nghttp2_session_on_settings_received(session, frame, 1); nghttp2_frame_settings_free(&frame->settings, mem); mem->free(frame, NULL); item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); nghttp2_outbound_item_init(item); frame = &item->frame; nghttp2_frame_push_promise_init(&frame->push_promise, NGHTTP2_FLAG_END_HEADERS, 1, -1, NULL, 0); nghttp2_session_add_item(session, item); ud.frame_not_send_cb_called = 0; assert_int(0, ==, nghttp2_session_send(session)); assert_int(1, ==, ud.frame_not_send_cb_called); assert_uint8(NGHTTP2_PUSH_PROMISE, ==, ud.not_sent_frame_type); assert_int(NGHTTP2_ERR_PUSH_DISABLED, ==, ud.not_sent_error); nghttp2_session_del(session); /* PUSH_PROMISE from client is error */ nghttp2_session_client_new(&session, &callbacks, &ud); open_sent_stream(session, 1); item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); nghttp2_outbound_item_init(item); frame = &item->frame; nghttp2_frame_push_promise_init(&frame->push_promise, NGHTTP2_FLAG_END_HEADERS, 1, -1, NULL, 0); nghttp2_session_add_item(session, item); assert_int(0, ==, nghttp2_session_send(session)); assert_null(nghttp2_session_get_stream(session, 3)); nghttp2_session_del(session); } void test_nghttp2_session_is_my_stream_id(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); nghttp2_session_server_new(&session, &callbacks, NULL); assert_false(nghttp2_session_is_my_stream_id(session, 0)); assert_false(nghttp2_session_is_my_stream_id(session, 1)); assert_true(nghttp2_session_is_my_stream_id(session, 2)); nghttp2_session_del(session); nghttp2_session_client_new(&session, &callbacks, NULL); assert_false(nghttp2_session_is_my_stream_id(session, 0)); assert_true(nghttp2_session_is_my_stream_id(session, 1)); assert_false(nghttp2_session_is_my_stream_id(session, 2)); nghttp2_session_del(session); } void test_nghttp2_session_upgrade2(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; uint8_t settings_payload[128]; size_t settings_payloadlen; nghttp2_settings_entry iv[16]; nghttp2_stream *stream; nghttp2_outbound_item *item; nghttp2_ssize rv; nghttp2_bufs bufs; nghttp2_buf *buf; nghttp2_hd_deflater deflater; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; iv[0].value = 1; iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; iv[1].value = 4095; settings_payloadlen = (size_t)nghttp2_pack_settings_payload2( settings_payload, sizeof(settings_payload), iv, 2); /* Check client side */ nghttp2_session_client_new(&session, &callbacks, NULL); assert_int(0, ==, nghttp2_session_upgrade2(session, settings_payload, settings_payloadlen, 0, &callbacks)); assert_int32(1, ==, session->last_sent_stream_id); stream = nghttp2_session_get_stream(session, 1); assert_not_null(stream); assert_ptr_equal(&callbacks, stream->stream_user_data); assert_uint8(NGHTTP2_SHUT_WR, ==, stream->shut_flags); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_SETTINGS, ==, item->frame.hd.type); assert_size(2, ==, item->frame.settings.niv); assert_int32(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, ==, item->frame.settings.iv[0].settings_id); assert_uint32(1, ==, item->frame.settings.iv[0].value); assert_int32(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, ==, item->frame.settings.iv[1].settings_id); assert_uint32(4095, ==, item->frame.settings.iv[1].value); /* Call nghttp2_session_upgrade2() again is error */ assert_int(NGHTTP2_ERR_PROTO, ==, nghttp2_session_upgrade2(session, settings_payload, settings_payloadlen, 0, &callbacks)); nghttp2_session_del(session); /* Make sure that response from server can be received */ nghttp2_session_client_new(&session, &callbacks, NULL); assert_int(0, ==, nghttp2_session_upgrade2(session, settings_payload, settings_payloadlen, 0, &callbacks)); stream = nghttp2_session_get_stream(session, 1); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_OPENING, ==, stream->state); nghttp2_hd_deflate_init(&deflater, mem); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, resnv, ARRLEN(resnv), mem); assert_ptrdiff(0, ==, rv); buf = &bufs.head->buf; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf), ==, rv); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_OPENED, ==, stream->state); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); nghttp2_bufs_reset(&bufs); /* Check server side */ nghttp2_session_server_new(&session, &callbacks, NULL); assert_int(0, ==, nghttp2_session_upgrade2(session, settings_payload, settings_payloadlen, 0, &callbacks)); assert_int32(1, ==, session->last_recv_stream_id); stream = nghttp2_session_get_stream(session, 1); assert_not_null(stream); assert_null(stream->stream_user_data); assert_uint8(NGHTTP2_SHUT_RD, ==, stream->shut_flags); assert_null(nghttp2_session_get_next_ob_item(session)); assert_uint32(1, ==, session->remote_settings.max_concurrent_streams); assert_uint32(4095, ==, session->remote_settings.initial_window_size); /* Call nghttp2_session_upgrade2() again is error */ assert_int(NGHTTP2_ERR_PROTO, ==, nghttp2_session_upgrade2(session, settings_payload, settings_payloadlen, 0, &callbacks)); nghttp2_session_del(session); /* Empty SETTINGS is OK */ settings_payloadlen = (size_t)nghttp2_pack_settings_payload2( settings_payload, sizeof(settings_payload), NULL, 0); nghttp2_session_client_new(&session, &callbacks, NULL); assert_int(0, ==, nghttp2_session_upgrade2(session, settings_payload, settings_payloadlen, 0, NULL)); nghttp2_session_del(session); nghttp2_bufs_free(&bufs); } void test_nghttp2_submit_data(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_data_provider2 data_prd; my_user_data ud; nghttp2_frame *frame; nghttp2_frame_hd hd; nghttp2_active_outbound_item *aob; nghttp2_bufs *framebufs; nghttp2_buf *buf; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = block_count_send_callback; data_prd.read_callback = fixed_length_data_source_read_callback; ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 2; assert_int(0, ==, nghttp2_session_client_new(&session, &callbacks, &ud)); aob = &session->aob; framebufs = &aob->framebufs; open_sent_stream(session, 1); assert_int( 0, ==, nghttp2_submit_data2(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd)); ud.block_count = 0; assert_int(0, ==, nghttp2_session_send(session)); frame = &aob->item->frame; buf = &framebufs->head->buf; nghttp2_frame_unpack_frame_hd(&hd, buf->pos); assert_uint8(NGHTTP2_FLAG_NONE, ==, hd.flags); assert_uint8(NGHTTP2_FLAG_NONE, ==, frame->hd.flags); /* aux_data.data.flags has these flags */ assert_uint8(NGHTTP2_FLAG_END_STREAM, ==, aob->item->aux_data.data.flags); nghttp2_session_del(session); } void test_nghttp2_submit_data_read_length_too_large(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_data_provider2 data_prd; my_user_data ud; nghttp2_frame *frame; nghttp2_frame_hd hd; nghttp2_active_outbound_item *aob; nghttp2_bufs *framebufs; nghttp2_buf *buf; size_t payloadlen; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = block_count_send_callback; callbacks.read_length_callback2 = too_large_data_source_length_callback; data_prd.read_callback = fixed_length_data_source_read_callback; ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 2; assert_int(0, ==, nghttp2_session_client_new(&session, &callbacks, &ud)); aob = &session->aob; framebufs = &aob->framebufs; open_sent_stream(session, 1); assert_int( 0, ==, nghttp2_submit_data2(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd)); ud.block_count = 0; assert_int(0, ==, nghttp2_session_send(session)); frame = &aob->item->frame; buf = &framebufs->head->buf; nghttp2_frame_unpack_frame_hd(&hd, buf->pos); assert_uint8(NGHTTP2_FLAG_NONE, ==, hd.flags); assert_uint8(NGHTTP2_FLAG_NONE, ==, frame->hd.flags); assert_size(16384, ==, hd.length); /* aux_data.data.flags has these flags */ assert_uint8(NGHTTP2_FLAG_END_STREAM, ==, aob->item->aux_data.data.flags); nghttp2_session_del(session); /* Check that buffers are expanded */ assert_int(0, ==, nghttp2_session_client_new(&session, &callbacks, &ud)); ud.data_source_length = NGHTTP2_MAX_FRAME_SIZE_MAX; session->remote_settings.max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MAX; open_sent_stream(session, 1); assert_int( 0, ==, nghttp2_submit_data2(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd)); ud.block_count = 0; assert_int(0, ==, nghttp2_session_send(session)); aob = &session->aob; frame = &aob->item->frame; framebufs = &aob->framebufs; buf = &framebufs->head->buf; nghttp2_frame_unpack_frame_hd(&hd, buf->pos); payloadlen = nghttp2_min_size(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE, NGHTTP2_INITIAL_WINDOW_SIZE); assert_size(NGHTTP2_FRAME_HDLEN + 1 + payloadlen, ==, (size_t)nghttp2_buf_cap(buf)); assert_uint8(NGHTTP2_FLAG_NONE, ==, hd.flags); assert_uint8(NGHTTP2_FLAG_NONE, ==, frame->hd.flags); assert_size(payloadlen, ==, hd.length); /* aux_data.data.flags has these flags */ assert_uint8(NGHTTP2_FLAG_END_STREAM, ==, aob->item->aux_data.data.flags); nghttp2_session_del(session); } void test_nghttp2_submit_data_read_length_smallest(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_data_provider2 data_prd; my_user_data ud; nghttp2_frame *frame; nghttp2_frame_hd hd; nghttp2_active_outbound_item *aob; nghttp2_bufs *framebufs; nghttp2_buf *buf; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = block_count_send_callback; callbacks.read_length_callback2 = smallest_length_data_source_length_callback; data_prd.read_callback = fixed_length_data_source_read_callback; ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 2; assert_int(0, ==, nghttp2_session_client_new(&session, &callbacks, &ud)); aob = &session->aob; framebufs = &aob->framebufs; open_sent_stream(session, 1); assert_int( 0, ==, nghttp2_submit_data2(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd)); ud.block_count = 0; assert_int(0, ==, nghttp2_session_send(session)); frame = &aob->item->frame; buf = &framebufs->head->buf; nghttp2_frame_unpack_frame_hd(&hd, buf->pos); assert_uint8(NGHTTP2_FLAG_NONE, ==, hd.flags); assert_uint8(NGHTTP2_FLAG_NONE, ==, frame->hd.flags); assert_size(1, ==, hd.length); /* aux_data.data.flags has these flags */ assert_uint8(NGHTTP2_FLAG_END_STREAM, ==, aob->item->aux_data.data.flags); nghttp2_session_del(session); } static nghttp2_ssize submit_data_twice_data_source_read_callback( nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len, uint32_t *data_flags, nghttp2_data_source *source, void *user_data) { (void)session; (void)stream_id; (void)buf; (void)source; (void)user_data; *data_flags |= NGHTTP2_DATA_FLAG_EOF; return (nghttp2_ssize)nghttp2_min_size(len, 16); } static int submit_data_twice_on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { static int called = 0; int rv; nghttp2_data_provider2 data_prd; (void)user_data; if (called == 0) { called = 1; data_prd.read_callback = submit_data_twice_data_source_read_callback; rv = nghttp2_submit_data2(session, NGHTTP2_FLAG_END_STREAM, frame->hd.stream_id, &data_prd); assert_int(0, ==, rv); } return 0; } void test_nghttp2_submit_data_twice(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_data_provider2 data_prd; my_user_data ud; accumulator acc; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = accumulator_send_callback; callbacks.on_frame_send_callback = submit_data_twice_on_frame_send_callback; data_prd.read_callback = submit_data_twice_data_source_read_callback; acc.length = 0; ud.acc = &acc; assert_int(0, ==, nghttp2_session_client_new(&session, &callbacks, &ud)); open_sent_stream(session, 1); assert_int(0, ==, nghttp2_submit_data2(session, NGHTTP2_FLAG_NONE, 1, &data_prd)); assert_int(0, ==, nghttp2_session_send(session)); /* We should have sent 2 DATA frame with 16 bytes payload each */ assert_size(NGHTTP2_FRAME_HDLEN * 2 + 16 * 2, ==, acc.length); nghttp2_session_del(session); } void test_nghttp2_submit_request_with_data(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_data_provider2 data_prd; my_user_data ud; nghttp2_outbound_item *item; nghttp2_mem *mem; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; data_prd.read_callback = fixed_length_data_source_read_callback; ud.data_source_length = 64 * 1024 - 1; assert_int(0, ==, nghttp2_session_client_new(&session, &callbacks, &ud)); assert_int32(1, ==, nghttp2_submit_request2(session, NULL, reqnv, ARRLEN(reqnv), &data_prd, NULL)); item = nghttp2_session_get_next_ob_item(session); assert_size(ARRLEN(reqnv), ==, item->frame.headers.nvlen); assert_nv_equal(reqnv, item->frame.headers.nva, item->frame.headers.nvlen, mem); assert_int(0, ==, nghttp2_session_send(session)); assert_size(0, ==, ud.data_source_length); nghttp2_session_del(session); /* nghttp2_submit_request2() with server session is error */ nghttp2_session_server_new(&session, &callbacks, NULL); assert_int32( NGHTTP2_ERR_PROTO, ==, nghttp2_submit_request2(session, NULL, reqnv, ARRLEN(reqnv), NULL, NULL)); nghttp2_session_del(session); } void test_nghttp2_submit_request_without_data(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; accumulator acc; nghttp2_data_provider2 data_prd = {{-1}, NULL}; nghttp2_outbound_item *item; my_user_data ud; nghttp2_frame frame; nghttp2_hd_inflater inflater; nva_out out; nghttp2_bufs bufs; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nva_out_init(&out); acc.length = 0; ud.acc = &acc; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = accumulator_send_callback; assert_int(0, ==, nghttp2_session_client_new(&session, &callbacks, &ud)); nghttp2_hd_inflate_init(&inflater, mem); assert_int32(1, ==, nghttp2_submit_request2(session, NULL, reqnv, ARRLEN(reqnv), &data_prd, NULL)); item = nghttp2_session_get_next_ob_item(session); assert_size(ARRLEN(reqnv), ==, item->frame.headers.nvlen); assert_nv_equal(reqnv, item->frame.headers.nva, item->frame.headers.nvlen, mem); assert_true(item->frame.hd.flags & NGHTTP2_FLAG_END_STREAM); assert_int(0, ==, nghttp2_session_send(session)); assert_int(0, ==, unpack_frame(&frame, acc.buf, acc.length)); nghttp2_bufs_add(&bufs, acc.buf, acc.length); inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem); assert_size(ARRLEN(reqnv), ==, out.nvlen); assert_nv_equal(reqnv, out.nva, out.nvlen, mem); nghttp2_frame_headers_free(&frame.headers, mem); nva_out_reset(&out, mem); nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); nghttp2_session_del(session); } void test_nghttp2_submit_response_with_data(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_data_provider2 data_prd; my_user_data ud; nghttp2_outbound_item *item; nghttp2_mem *mem; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; data_prd.read_callback = fixed_length_data_source_read_callback; ud.data_source_length = 64 * 1024 - 1; assert_int(0, ==, nghttp2_session_server_new(&session, &callbacks, &ud)); open_recv_stream2(session, 1, NGHTTP2_STREAM_OPENING); assert_int( 0, ==, nghttp2_submit_response2(session, 1, resnv, ARRLEN(resnv), &data_prd)); item = nghttp2_session_get_next_ob_item(session); assert_size(ARRLEN(resnv), ==, item->frame.headers.nvlen); assert_nv_equal(resnv, item->frame.headers.nva, item->frame.headers.nvlen, mem); assert_int(0, ==, nghttp2_session_send(session)); assert_size(0, ==, ud.data_source_length); nghttp2_session_del(session); /* Various error cases */ nghttp2_session_client_new(&session, &callbacks, NULL); /* Calling nghttp2_submit_response2() with client session is error */ assert_int(NGHTTP2_ERR_PROTO, ==, nghttp2_submit_response2(session, 1, resnv, ARRLEN(resnv), NULL)); /* Stream ID <= 0 is error */ assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, nghttp2_submit_response2(session, 0, resnv, ARRLEN(resnv), NULL)); nghttp2_session_del(session); } void test_nghttp2_submit_response_without_data(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; accumulator acc; nghttp2_data_provider2 data_prd = {{-1}, NULL}; nghttp2_outbound_item *item; my_user_data ud; nghttp2_frame frame; nghttp2_hd_inflater inflater; nva_out out; nghttp2_bufs bufs; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nva_out_init(&out); acc.length = 0; ud.acc = &acc; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = accumulator_send_callback; assert_int(0, ==, nghttp2_session_server_new(&session, &callbacks, &ud)); nghttp2_hd_inflate_init(&inflater, mem); open_recv_stream2(session, 1, NGHTTP2_STREAM_OPENING); assert_int( 0, ==, nghttp2_submit_response2(session, 1, resnv, ARRLEN(resnv), &data_prd)); item = nghttp2_session_get_next_ob_item(session); assert_size(ARRLEN(resnv), ==, item->frame.headers.nvlen); assert_nv_equal(resnv, item->frame.headers.nva, item->frame.headers.nvlen, mem); assert_true(item->frame.hd.flags & NGHTTP2_FLAG_END_STREAM); assert_int(0, ==, nghttp2_session_send(session)); assert_int(0, ==, unpack_frame(&frame, acc.buf, acc.length)); nghttp2_bufs_add(&bufs, acc.buf, acc.length); inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem); assert_size(ARRLEN(resnv), ==, out.nvlen); assert_nv_equal(resnv, out.nva, out.nvlen, mem); nva_out_reset(&out, mem); nghttp2_bufs_free(&bufs); nghttp2_frame_headers_free(&frame.headers, mem); nghttp2_hd_inflate_free(&inflater); nghttp2_session_del(session); } void test_nghttp2_submit_response_push_response(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_frame_not_send_callback = on_frame_not_send_callback; nghttp2_session_server_new(&session, &callbacks, &ud); open_sent_stream2(session, 2, NGHTTP2_STREAM_RESERVED); session->goaway_flags |= NGHTTP2_GOAWAY_RECV; assert_int(0, ==, nghttp2_submit_response2(session, 2, resnv, ARRLEN(resnv), NULL)); ud.frame_not_send_cb_called = 0; assert_int(0, ==, nghttp2_session_send(session)); assert_int(1, ==, ud.frame_not_send_cb_called); nghttp2_session_del(session); } void test_nghttp2_submit_trailer(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; accumulator acc; nghttp2_data_provider2 data_prd; nghttp2_outbound_item *item; my_user_data ud; nghttp2_frame frame; nghttp2_hd_inflater inflater; nva_out out; nghttp2_bufs bufs; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); data_prd.read_callback = no_end_stream_data_source_read_callback; nva_out_init(&out); acc.length = 0; ud.acc = &acc; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; assert_int(0, ==, nghttp2_session_server_new(&session, &callbacks, &ud)); nghttp2_hd_inflate_init(&inflater, mem); open_recv_stream2(session, 1, NGHTTP2_STREAM_OPENING); assert_int( 0, ==, nghttp2_submit_response2(session, 1, resnv, ARRLEN(resnv), &data_prd)); assert_int(0, ==, nghttp2_session_send(session)); assert_int(0, ==, nghttp2_submit_trailer(session, 1, trailernv, ARRLEN(trailernv))); session->callbacks.send_callback2 = accumulator_send_callback; item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_HEADERS, ==, item->frame.hd.type); assert_enum(nghttp2_headers_category, NGHTTP2_HCAT_HEADERS, ==, item->frame.headers.cat); assert_true(item->frame.hd.flags & NGHTTP2_FLAG_END_STREAM); assert_int(0, ==, nghttp2_session_send(session)); assert_int(0, ==, unpack_frame(&frame, acc.buf, acc.length)); nghttp2_bufs_add(&bufs, acc.buf, acc.length); inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem); assert_size(ARRLEN(trailernv), ==, out.nvlen); assert_nv_equal(trailernv, out.nva, out.nvlen, mem); nva_out_reset(&out, mem); nghttp2_bufs_free(&bufs); nghttp2_frame_headers_free(&frame.headers, mem); nghttp2_hd_inflate_free(&inflater); nghttp2_session_del(session); /* Specifying stream ID <= 0 is error */ nghttp2_session_server_new(&session, &callbacks, NULL); open_recv_stream(session, 1); assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, nghttp2_submit_trailer(session, 0, trailernv, ARRLEN(trailernv))); assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, nghttp2_submit_trailer(session, -1, trailernv, ARRLEN(trailernv))); nghttp2_session_del(session); } void test_nghttp2_submit_headers_start_stream(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_outbound_item *item; nghttp2_mem *mem; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); assert_int(0, ==, nghttp2_session_client_new(&session, &callbacks, NULL)); assert_int32(1, ==, nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1, NULL, reqnv, ARRLEN(reqnv), NULL)); item = nghttp2_session_get_next_ob_item(session); assert_size(ARRLEN(reqnv), ==, item->frame.headers.nvlen); assert_nv_equal(reqnv, item->frame.headers.nva, item->frame.headers.nvlen, mem); assert_uint8((NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM), ==, item->frame.hd.flags); assert_false(item->frame.hd.flags & NGHTTP2_FLAG_PRIORITY); nghttp2_session_del(session); } void test_nghttp2_submit_headers_reply(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; nghttp2_outbound_item *item; nghttp2_stream *stream; nghttp2_mem *mem; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_frame_send_callback = on_frame_send_callback; assert_int(0, ==, nghttp2_session_server_new(&session, &callbacks, &ud)); assert_int32(0, ==, nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, NULL, resnv, ARRLEN(resnv), NULL)); item = nghttp2_session_get_next_ob_item(session); assert_size(ARRLEN(resnv), ==, item->frame.headers.nvlen); assert_nv_equal(resnv, item->frame.headers.nva, item->frame.headers.nvlen, mem); assert_uint8((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS), ==, item->frame.hd.flags); ud.frame_send_cb_called = 0; ud.sent_frame_type = 0; /* The transimission will be canceled because the stream 1 is not open. */ assert_int(0, ==, nghttp2_session_send(session)); assert_int(0, ==, ud.frame_send_cb_called); stream = open_recv_stream2(session, 1, NGHTTP2_STREAM_OPENING); assert_int32(0, ==, nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, NULL, resnv, ARRLEN(resnv), NULL)); assert_int(0, ==, nghttp2_session_send(session)); assert_int(1, ==, ud.frame_send_cb_called); assert_uint8(NGHTTP2_HEADERS, ==, ud.sent_frame_type); assert_true(stream->shut_flags & NGHTTP2_SHUT_WR); nghttp2_session_del(session); } void test_nghttp2_submit_headers_push_reply(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; nghttp2_stream *stream; int foo; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_frame_send_callback = on_frame_send_callback; assert_int(0, ==, nghttp2_session_server_new(&session, &callbacks, &ud)); stream = open_sent_stream2(session, 2, NGHTTP2_STREAM_RESERVED); assert_int32(0, ==, nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 2, NULL, resnv, ARRLEN(resnv), &foo)); ud.frame_send_cb_called = 0; ud.sent_frame_type = 0; assert_int(0, ==, nghttp2_session_send(session)); assert_int(1, ==, ud.frame_send_cb_called); assert_uint8(NGHTTP2_HEADERS, ==, ud.sent_frame_type); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_OPENED, ==, stream->state); assert_ptr_equal(&foo, stream->stream_user_data); nghttp2_session_del(session); /* Sending HEADERS from client against stream in reserved state is error */ assert_int(0, ==, nghttp2_session_client_new(&session, &callbacks, &ud)); open_recv_stream2(session, 2, NGHTTP2_STREAM_RESERVED); assert_int32(0, ==, nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 2, NULL, reqnv, ARRLEN(reqnv), NULL)); ud.frame_send_cb_called = 0; ud.sent_frame_type = 0; assert_int(0, ==, nghttp2_session_send(session)); assert_int(0, ==, ud.frame_send_cb_called); nghttp2_session_del(session); } void test_nghttp2_submit_headers(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; nghttp2_outbound_item *item; nghttp2_stream *stream; accumulator acc; nghttp2_frame frame; nghttp2_hd_inflater inflater; nva_out out; nghttp2_bufs bufs; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nva_out_init(&out); acc.length = 0; ud.acc = &acc; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = accumulator_send_callback; callbacks.on_frame_send_callback = on_frame_send_callback; assert_int(0, ==, nghttp2_session_client_new(&session, &callbacks, &ud)); nghttp2_hd_inflate_init(&inflater, mem); assert_int32(0, ==, nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, NULL, reqnv, ARRLEN(reqnv), NULL)); item = nghttp2_session_get_next_ob_item(session); assert_size(ARRLEN(reqnv), ==, item->frame.headers.nvlen); assert_nv_equal(reqnv, item->frame.headers.nva, item->frame.headers.nvlen, mem); assert_uint8((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS), ==, item->frame.hd.flags); ud.frame_send_cb_called = 0; ud.sent_frame_type = 0; /* The transimission will be canceled because the stream 1 is not open. */ assert_int(0, ==, nghttp2_session_send(session)); assert_int(0, ==, ud.frame_send_cb_called); stream = open_sent_stream(session, 1); assert_int32(0, ==, nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, NULL, reqnv, ARRLEN(reqnv), NULL)); assert_int(0, ==, nghttp2_session_send(session)); assert_int(1, ==, ud.frame_send_cb_called); assert_uint8(NGHTTP2_HEADERS, ==, ud.sent_frame_type); assert_true(stream->shut_flags & NGHTTP2_SHUT_WR); assert_int(0, ==, unpack_frame(&frame, acc.buf, acc.length)); nghttp2_bufs_add(&bufs, acc.buf, acc.length); inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem); assert_size(ARRLEN(reqnv), ==, out.nvlen); assert_nv_equal(reqnv, out.nva, out.nvlen, mem); nva_out_reset(&out, mem); nghttp2_bufs_free(&bufs); nghttp2_frame_headers_free(&frame.headers, mem); nghttp2_hd_inflate_free(&inflater); nghttp2_session_del(session); /* Error cases with invalid stream ID */ nghttp2_session_server_new(&session, &callbacks, NULL); /* Sending nghttp2_submit_headers() with stream_id == 1 and server session is error */ assert_int32(NGHTTP2_ERR_PROTO, ==, nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1, NULL, reqnv, ARRLEN(reqnv), NULL)); /* Sending stream ID <= 0 is error */ assert_int32(NGHTTP2_ERR_INVALID_ARGUMENT, ==, nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 0, NULL, resnv, ARRLEN(resnv), NULL)); nghttp2_session_del(session); } void test_nghttp2_submit_headers_continuation(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_nv nv[] = { MAKE_NV("h1", ""), MAKE_NV("h1", ""), MAKE_NV("h1", ""), MAKE_NV("h1", ""), MAKE_NV("h1", ""), MAKE_NV("h1", ""), MAKE_NV("h1", ""), }; nghttp2_outbound_item *item; uint8_t data[4096]; size_t i; my_user_data ud; memset(data, '0', sizeof(data)); for (i = 0; i < ARRLEN(nv); ++i) { nv[i].valuelen = sizeof(data); nv[i].value = data; } memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_frame_send_callback = on_frame_send_callback; assert_int(0, ==, nghttp2_session_client_new(&session, &callbacks, &ud)); assert_int32(1, ==, nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1, NULL, nv, ARRLEN(nv), NULL)); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_HEADERS, ==, item->frame.hd.type); assert_uint8((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS), ==, item->frame.hd.flags); assert_false(item->frame.hd.flags & NGHTTP2_FLAG_PRIORITY); ud.frame_send_cb_called = 0; assert_int(0, ==, nghttp2_session_send(session)); assert_int(1, ==, ud.frame_send_cb_called); nghttp2_session_del(session); } void test_nghttp2_submit_headers_continuation_extra_large(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_nv nv[] = { MAKE_NV("h1", ""), MAKE_NV("h1", ""), MAKE_NV("h1", ""), MAKE_NV("h1", ""), MAKE_NV("h1", ""), MAKE_NV("h1", ""), }; nghttp2_outbound_item *item; uint8_t data[16384]; size_t i; my_user_data ud; nghttp2_option *opt; memset(data, '0', sizeof(data)); for (i = 0; i < ARRLEN(nv); ++i) { nv[i].valuelen = sizeof(data); nv[i].value = data; } memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_frame_send_callback = on_frame_send_callback; /* The default size of max send header block length is too small to send these header fields. Expand it. */ nghttp2_option_new(&opt); nghttp2_option_set_max_send_header_block_length(opt, 102400); assert_int(0, ==, nghttp2_session_client_new2(&session, &callbacks, &ud, opt)); assert_int32(1, ==, nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1, NULL, nv, ARRLEN(nv), NULL)); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_HEADERS, ==, item->frame.hd.type); assert_uint8((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS), ==, item->frame.hd.flags); assert_false(item->frame.hd.flags & NGHTTP2_FLAG_PRIORITY); ud.frame_send_cb_called = 0; assert_int(0, ==, nghttp2_session_send(session)); assert_int(1, ==, ud.frame_send_cb_called); nghttp2_session_del(session); nghttp2_option_del(opt); } void test_nghttp2_submit_settings(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; nghttp2_outbound_item *item; nghttp2_frame *frame; nghttp2_settings_entry iv[7]; nghttp2_frame ack_frame; const int32_t UNKNOWN_ID = 1000000007; nghttp2_mem *mem; mem = nghttp2_mem_default(); iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; iv[0].value = 5; iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; iv[1].value = 16 * 1024; iv[2].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; iv[2].value = 50; iv[3].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[3].value = 111; iv[4].settings_id = UNKNOWN_ID; iv[4].value = 999; iv[5].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[5].value = 1023; iv[6].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; iv[6].value = (uint32_t)NGHTTP2_MAX_WINDOW_SIZE + 1; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_frame_send_callback = on_frame_send_callback; nghttp2_session_server_new(&session, &callbacks, &ud); assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 7)); /* Make sure that local settings are not changed */ assert_uint32(NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS, ==, session->local_settings.max_concurrent_streams); assert_uint32(NGHTTP2_INITIAL_WINDOW_SIZE, ==, session->local_settings.initial_window_size); /* Now sends without 6th one */ assert_int(0, ==, nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 6)); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_SETTINGS, ==, item->frame.hd.type); frame = &item->frame; assert_size(6, ==, frame->settings.niv); assert_uint32(5, ==, frame->settings.iv[0].value); assert_int32(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, ==, frame->settings.iv[0].settings_id); assert_uint32(16 * 1024, ==, frame->settings.iv[1].value); assert_int32(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, ==, frame->settings.iv[1].settings_id); assert_int32(UNKNOWN_ID, ==, frame->settings.iv[4].settings_id); assert_uint32(999, ==, frame->settings.iv[4].value); ud.frame_send_cb_called = 0; assert_int(0, ==, nghttp2_session_send(session)); assert_int(1, ==, ud.frame_send_cb_called); assert_uint32(50, ==, session->pending_local_max_concurrent_stream); /* before receiving SETTINGS ACK, local settings have still default values */ assert_uint32(NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS, ==, nghttp2_session_get_local_settings( session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS)); assert_uint32(NGHTTP2_INITIAL_WINDOW_SIZE, ==, nghttp2_session_get_local_settings( session, NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE)); nghttp2_frame_settings_init(&ack_frame.settings, NGHTTP2_FLAG_ACK, NULL, 0); assert_int(0, ==, nghttp2_session_on_settings_received(session, &ack_frame, 0)); nghttp2_frame_settings_free(&ack_frame.settings, mem); assert_uint32(16 * 1024, ==, session->local_settings.initial_window_size); assert_size(111, ==, session->hd_inflater.ctx.hd_table_bufsize_max); assert_size(111, ==, session->hd_inflater.min_hd_table_bufsize_max); assert_uint32(50, ==, session->local_settings.max_concurrent_streams); assert_uint32(50, ==, nghttp2_session_get_local_settings( session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS)); assert_uint32(16 * 1024, ==, nghttp2_session_get_local_settings( session, NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE)); /* We just keep the last seen value */ assert_uint32(50, ==, session->pending_local_max_concurrent_stream); nghttp2_session_del(session); /* Bail out if there are contradicting SETTINGS_NO_RFC7540_PRIORITIES in one SETTINGS. */ nghttp2_session_server_new(&session, &callbacks, &ud); iv[0].settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES; iv[0].value = 1; iv[1].settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES; iv[1].value = 0; assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 2)); nghttp2_session_del(session); /* Attempt to change SETTINGS_NO_RFC7540_PRIORITIES in the 2nd SETTINGS. */ nghttp2_session_server_new(&session, &callbacks, &ud); iv[0].settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES; iv[0].value = 1; assert_int(0, ==, nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1)); iv[0].settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES; iv[0].value = 0; assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1)); nghttp2_session_del(session); } void test_nghttp2_submit_settings_update_local_window_size(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_outbound_item *item; nghttp2_settings_entry iv[4]; nghttp2_stream *stream; nghttp2_frame ack_frame; nghttp2_mem *mem; nghttp2_option *option; mem = nghttp2_mem_default(); nghttp2_frame_settings_init(&ack_frame.settings, NGHTTP2_FLAG_ACK, NULL, 0); iv[0].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; iv[0].value = 16 * 1024; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_server_new(&session, &callbacks, NULL); stream = open_recv_stream(session, 1); stream->local_window_size = NGHTTP2_INITIAL_WINDOW_SIZE + 100; stream->recv_window_size = 32768; open_recv_stream(session, 3); assert_int(0, ==, nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1)); assert_int(0, ==, nghttp2_session_send(session)); assert_int(0, ==, nghttp2_session_on_settings_received(session, &ack_frame, 0)); stream = nghttp2_session_get_stream(session, 1); assert_int32(0, ==, stream->recv_window_size); assert_int32(16 * 1024 + 100, ==, stream->local_window_size); stream = nghttp2_session_get_stream(session, 3); assert_int32(16 * 1024, ==, stream->local_window_size); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_WINDOW_UPDATE, ==, item->frame.hd.type); assert_int32(32768, ==, item->frame.window_update.window_size_increment); nghttp2_session_del(session); /* Without auto-window update */ nghttp2_option_new(&option); nghttp2_option_set_no_auto_window_update(option, 1); nghttp2_session_server_new2(&session, &callbacks, NULL, option); nghttp2_option_del(option); stream = open_recv_stream(session, 1); stream->local_window_size = NGHTTP2_INITIAL_WINDOW_SIZE + 100; stream->recv_window_size = 32768; assert_int(0, ==, nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1)); assert_int(0, ==, nghttp2_session_send(session)); assert_int(0, ==, nghttp2_session_on_settings_received(session, &ack_frame, 0)); stream = nghttp2_session_get_stream(session, 1); assert_int32(32768, ==, stream->recv_window_size); assert_int32(16 * 1024 + 100, ==, stream->local_window_size); /* Check that we can handle the case where local_window_size < recv_window_size */ assert_int32(0, ==, nghttp2_session_get_stream_local_window_size(session, 1)); nghttp2_session_del(session); /* Check overflow case */ iv[0].value = 128 * 1024; nghttp2_session_server_new(&session, &callbacks, NULL); stream = open_recv_stream(session, 1); stream->local_window_size = NGHTTP2_MAX_WINDOW_SIZE; assert_int(0, ==, nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1)); assert_int(0, ==, nghttp2_session_send(session)); assert_int(0, ==, nghttp2_session_on_settings_received(session, &ack_frame, 0)); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_uint32(NGHTTP2_FLOW_CONTROL_ERROR, ==, item->frame.goaway.error_code); nghttp2_session_del(session); nghttp2_frame_settings_free(&ack_frame.settings, mem); } void test_nghttp2_submit_settings_multiple_times(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_settings_entry iv[4]; nghttp2_frame frame; nghttp2_inflight_settings *inflight_settings; memset(&callbacks, 0, sizeof(callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_client_new(&session, &callbacks, NULL); /* first SETTINGS */ iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; iv[0].value = 100; iv[1].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; iv[1].value = 0; assert_int(0, ==, nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 2)); inflight_settings = session->inflight_settings_head; assert_not_null(inflight_settings); assert_int32(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, ==, inflight_settings->iv[0].settings_id); assert_uint32(100, ==, inflight_settings->iv[0].value); assert_size(2, ==, inflight_settings->niv); assert_null(inflight_settings->next); assert_uint32(100, ==, session->pending_local_max_concurrent_stream); assert_uint8(0, ==, session->pending_enable_push); /* second SETTINGS */ iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; iv[0].value = 99; assert_int(0, ==, nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1)); inflight_settings = session->inflight_settings_head->next; assert_not_null(inflight_settings); assert_int32(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, ==, inflight_settings->iv[0].settings_id); assert_uint32(99, ==, inflight_settings->iv[0].value); assert_size(1, ==, inflight_settings->niv); assert_null(inflight_settings->next); assert_uint32(99, ==, session->pending_local_max_concurrent_stream); assert_uint8(0, ==, session->pending_enable_push); nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_ACK, NULL, 0); /* receive SETTINGS ACK */ assert_int(0, ==, nghttp2_session_on_settings_received(session, &frame, 0)); inflight_settings = session->inflight_settings_head; /* first inflight SETTINGS was removed */ assert_not_null(inflight_settings); assert_int32(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, ==, inflight_settings->iv[0].settings_id); assert_uint32(99, ==, inflight_settings->iv[0].value); assert_size(1, ==, inflight_settings->niv); assert_null(inflight_settings->next); assert_uint32(100, ==, session->local_settings.max_concurrent_streams); /* receive SETTINGS ACK again */ assert_int(0, ==, nghttp2_session_on_settings_received(session, &frame, 0)); assert_null(session->inflight_settings_head); assert_uint32(99, ==, session->local_settings.max_concurrent_streams); nghttp2_session_del(session); } void test_nghttp2_submit_push_promise(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; nghttp2_stream *stream; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_frame_send_callback = on_frame_send_callback; callbacks.on_frame_not_send_callback = on_frame_not_send_callback; assert_int(0, ==, nghttp2_session_server_new(&session, &callbacks, &ud)); open_recv_stream(session, 1); assert_int32(2, ==, nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 1, reqnv, ARRLEN(reqnv), &ud)); stream = nghttp2_session_get_stream(session, 2); assert_not_null(stream); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_RESERVED, ==, stream->state); assert_ptr_equal(&ud, nghttp2_session_get_stream_user_data(session, 2)); ud.frame_send_cb_called = 0; ud.sent_frame_type = 0; assert_int(0, ==, nghttp2_session_send(session)); assert_int(1, ==, ud.frame_send_cb_called); assert_uint8(NGHTTP2_PUSH_PROMISE, ==, ud.sent_frame_type); stream = nghttp2_session_get_stream(session, 2); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_RESERVED, ==, stream->state); assert_ptr_equal(&ud, nghttp2_session_get_stream_user_data(session, 2)); /* submit PUSH_PROMISE while associated stream is not opened */ assert_int32(NGHTTP2_ERR_STREAM_CLOSED, ==, nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 3, reqnv, ARRLEN(reqnv), NULL)); /* Stream ID <= 0 is error */ assert_int32(NGHTTP2_ERR_INVALID_ARGUMENT, ==, nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 0, reqnv, ARRLEN(reqnv), NULL)); nghttp2_session_del(session); } void test_nghttp2_submit_window_update(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; nghttp2_outbound_item *item; nghttp2_stream *stream; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_client_new(&session, &callbacks, &ud); stream = open_recv_stream(session, 2); stream->recv_window_size = 4096; assert_int(0, ==, nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 1024)); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_WINDOW_UPDATE, ==, item->frame.hd.type); assert_int32(1024, ==, item->frame.window_update.window_size_increment); assert_int(0, ==, nghttp2_session_send(session)); assert_int32(3072, ==, stream->recv_window_size); assert_int(0, ==, nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 4096)); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_WINDOW_UPDATE, ==, item->frame.hd.type); assert_int32(4096, ==, item->frame.window_update.window_size_increment); assert_int(0, ==, nghttp2_session_send(session)); assert_int32(0, ==, stream->recv_window_size); assert_int(0, ==, nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 4096)); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_WINDOW_UPDATE, ==, item->frame.hd.type); assert_int32(4096, ==, item->frame.window_update.window_size_increment); assert_int(0, ==, nghttp2_session_send(session)); assert_int32(0, ==, stream->recv_window_size); assert_int(0, ==, nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 0)); /* It is ok if stream is closed or does not exist at the call time */ assert_int(0, ==, nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 4, 4096)); nghttp2_session_del(session); } void test_nghttp2_submit_window_update_local_window_size(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_outbound_item *item; nghttp2_stream *stream; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_client_new(&session, &callbacks, NULL); stream = open_recv_stream(session, 2); stream->recv_window_size = 4096; assert_int(0, ==, nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, stream->recv_window_size + 1)); assert_int32(NGHTTP2_INITIAL_WINDOW_SIZE + 1, ==, stream->local_window_size); assert_int32(0, ==, stream->recv_window_size); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_WINDOW_UPDATE, ==, item->frame.hd.type); assert_int32(4097, ==, item->frame.window_update.window_size_increment); assert_int(0, ==, nghttp2_session_send(session)); /* Let's decrement local window size */ stream->recv_window_size = 4096; assert_int(0, ==, nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, -stream->local_window_size / 2)); assert_int32(32768, ==, stream->local_window_size); assert_int32(-28672, ==, stream->recv_window_size); assert_int32(32768, ==, stream->recv_reduction); item = nghttp2_session_get_next_ob_item(session); assert_null(item); /* Increase local window size */ assert_int( 0, ==, nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 16384)); assert_int32(49152, ==, stream->local_window_size); assert_int32(-12288, ==, stream->recv_window_size); assert_int32(16384, ==, stream->recv_reduction); assert_null(nghttp2_session_get_next_ob_item(session)); assert_int(NGHTTP2_ERR_FLOW_CONTROL, ==, nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, NGHTTP2_MAX_WINDOW_SIZE)); assert_int(0, ==, nghttp2_session_send(session)); /* Check connection-level flow control */ session->recv_window_size = 4096; assert_int(0, ==, nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, session->recv_window_size + 1)); assert_int32(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1, ==, session->local_window_size); assert_int32(0, ==, session->recv_window_size); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_WINDOW_UPDATE, ==, item->frame.hd.type); assert_int32(4097, ==, item->frame.window_update.window_size_increment); assert_int(0, ==, nghttp2_session_send(session)); /* Go decrement part */ session->recv_window_size = 4096; assert_int(0, ==, nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, -session->local_window_size / 2)); assert_int32(32768, ==, session->local_window_size); assert_int32(-28672, ==, session->recv_window_size); assert_int32(32768, ==, session->recv_reduction); item = nghttp2_session_get_next_ob_item(session); assert_null(item); /* Increase local window size */ assert_int( 0, ==, nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, 16384)); assert_int32(49152, ==, session->local_window_size); assert_int32(-12288, ==, session->recv_window_size); assert_int32(16384, ==, session->recv_reduction); assert_null(nghttp2_session_get_next_ob_item(session)); assert_int(NGHTTP2_ERR_FLOW_CONTROL, ==, nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, NGHTTP2_MAX_WINDOW_SIZE)); nghttp2_session_del(session); } void test_nghttp2_submit_shutdown_notice(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_frame_send_callback = on_frame_send_callback; callbacks.on_frame_not_send_callback = on_frame_not_send_callback; nghttp2_session_server_new(&session, &callbacks, &ud); assert_int(0, ==, nghttp2_submit_shutdown_notice(session)); ud.frame_send_cb_called = 0; nghttp2_session_send(session); assert_int(1, ==, ud.frame_send_cb_called); assert_uint8(NGHTTP2_GOAWAY, ==, ud.sent_frame_type); assert_int32((1u << 31) - 1, ==, session->local_last_stream_id); /* After another GOAWAY, nghttp2_submit_shutdown_notice() is noop. */ assert_int(0, ==, nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR)); ud.frame_send_cb_called = 0; nghttp2_session_send(session); assert_int(1, ==, ud.frame_send_cb_called); assert_uint8(NGHTTP2_GOAWAY, ==, ud.sent_frame_type); assert_int32(0, ==, session->local_last_stream_id); assert_int(0, ==, nghttp2_submit_shutdown_notice(session)); ud.frame_send_cb_called = 0; ud.frame_not_send_cb_called = 0; nghttp2_session_send(session); assert_int(0, ==, ud.frame_send_cb_called); assert_int(0, ==, ud.frame_not_send_cb_called); nghttp2_session_del(session); /* Using nghttp2_submit_shutdown_notice() with client side session is error */ nghttp2_session_client_new(&session, &callbacks, NULL); assert_int(NGHTTP2_ERR_INVALID_STATE, ==, nghttp2_submit_shutdown_notice(session)); nghttp2_session_del(session); } void test_nghttp2_submit_invalid_nv(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_nv empty_name_nv[] = {MAKE_NV("Version", "HTTP/1.1"), MAKE_NV("", "empty name")}; /* Now invalid header name/value pair in HTTP/1.1 is accepted in nghttp2 */ memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); assert_int(0, ==, nghttp2_session_server_new(&session, &callbacks, NULL)); /* nghttp2_submit_response */ assert_int(0, ==, nghttp2_submit_response2(session, 2, empty_name_nv, ARRLEN(empty_name_nv), NULL)); /* nghttp2_submit_push_promise */ open_recv_stream(session, 1); assert_int32(0, <, nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 1, empty_name_nv, ARRLEN(empty_name_nv), NULL)); nghttp2_session_del(session); assert_int(0, ==, nghttp2_session_client_new(&session, &callbacks, NULL)); /* nghttp2_submit_request */ assert_int32(0, <, nghttp2_submit_request2(session, NULL, empty_name_nv, ARRLEN(empty_name_nv), NULL, NULL)); /* nghttp2_submit_headers */ assert_int32(0, <, nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1, NULL, empty_name_nv, ARRLEN(empty_name_nv), NULL)); nghttp2_session_del(session); } void test_nghttp2_submit_extension(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; accumulator acc; nghttp2_mem *mem; const char data[] = "Hello World!"; size_t len; int32_t stream_id; int rv; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.pack_extension_callback2 = pack_extension_callback; callbacks.send_callback2 = accumulator_send_callback; nghttp2_buf_init2(&ud.scratchbuf, 4096, mem); nghttp2_session_client_new(&session, &callbacks, &ud); ud.scratchbuf.last = nghttp2_cpymem(ud.scratchbuf.last, data, sizeof(data)); ud.acc = &acc; rv = nghttp2_submit_extension(session, 211, 0x01, 3, &ud.scratchbuf); assert_int(0, ==, rv); acc.length = 0; rv = nghttp2_session_send(session); assert_int(0, ==, rv); assert_size(NGHTTP2_FRAME_HDLEN + sizeof(data), ==, acc.length); len = nghttp2_get_uint32(acc.buf) >> 8; assert_size(sizeof(data), ==, len); assert_uint8(211, ==, acc.buf[3]); assert_uint8(0x01, ==, acc.buf[4]); stream_id = (int32_t)nghttp2_get_uint32(acc.buf + 5); assert_int32(3, ==, stream_id); assert_memory_equal(sizeof(data), data, &acc.buf[NGHTTP2_FRAME_HDLEN]); nghttp2_session_del(session); /* submitting standard HTTP/2 frame is error */ nghttp2_session_server_new(&session, &callbacks, &ud); rv = nghttp2_submit_extension(session, NGHTTP2_GOAWAY, NGHTTP2_FLAG_NONE, 0, NULL); assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, rv); nghttp2_session_del(session); nghttp2_buf_free(&ud.scratchbuf, mem); } void test_nghttp2_submit_altsvc(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; int rv; nghttp2_ssize len; const uint8_t *data; nghttp2_frame_hd hd; size_t origin_len; const uint8_t origin[] = "nghttp2.org"; const uint8_t field_value[] = "h2=\":443\""; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); nghttp2_session_server_new(&session, &callbacks, &ud); rv = nghttp2_submit_altsvc(session, NGHTTP2_FLAG_NONE, 0, origin, sizeof(origin) - 1, field_value, sizeof(field_value) - 1); assert_int(0, ==, rv); ud.frame_send_cb_called = 0; len = nghttp2_session_mem_send2(session, &data); assert_ptrdiff(NGHTTP2_FRAME_HDLEN + 2 + sizeof(origin) - 1 + sizeof(field_value) - 1, ==, len); nghttp2_frame_unpack_frame_hd(&hd, data); assert_size(2 + sizeof(origin) - 1 + sizeof(field_value) - 1, ==, hd.length); assert_uint8(NGHTTP2_ALTSVC, ==, hd.type); assert_uint8(NGHTTP2_FLAG_NONE, ==, hd.flags); origin_len = nghttp2_get_uint16(data + NGHTTP2_FRAME_HDLEN); assert_size(sizeof(origin) - 1, ==, origin_len); assert_memory_equal(sizeof(origin) - 1, origin, data + NGHTTP2_FRAME_HDLEN + 2); assert_memory_equal(hd.length - (sizeof(origin) - 1) - 2, field_value, data + NGHTTP2_FRAME_HDLEN + 2 + sizeof(origin) - 1); /* submitting empty origin with stream_id == 0 is error */ rv = nghttp2_submit_altsvc(session, NGHTTP2_FLAG_NONE, 0, NULL, 0, field_value, sizeof(field_value) - 1); assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, rv); /* submitting non-empty origin with stream_id != 0 is error */ rv = nghttp2_submit_altsvc(session, NGHTTP2_FLAG_NONE, 1, origin, sizeof(origin) - 1, field_value, sizeof(field_value) - 1); assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, rv); nghttp2_session_del(session); /* submitting from client side session is error */ nghttp2_session_client_new(&session, &callbacks, NULL); rv = nghttp2_submit_altsvc(session, NGHTTP2_FLAG_NONE, 0, origin, sizeof(origin) - 1, field_value, sizeof(field_value) - 1); assert_int(NGHTTP2_ERR_INVALID_STATE, ==, rv); nghttp2_session_del(session); } void test_nghttp2_submit_origin(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; int rv; nghttp2_ssize len; const uint8_t *data; static const uint8_t nghttp2[] = "https://nghttp2.org"; static const uint8_t examples[] = "https://examples.com"; static const nghttp2_origin_entry ov[] = { { (uint8_t *)nghttp2, sizeof(nghttp2) - 1, }, { (uint8_t *)examples, sizeof(examples) - 1, }, }; nghttp2_frame frame; nghttp2_ext_origin origin; nghttp2_mem *mem; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_frame_send_callback = on_frame_send_callback; frame.ext.payload = &origin; nghttp2_session_server_new(&session, &callbacks, &ud); rv = nghttp2_submit_origin(session, NGHTTP2_FLAG_NONE, ov, 2); assert_int(0, ==, rv); ud.frame_send_cb_called = 0; len = nghttp2_session_mem_send2(session, &data); assert_ptrdiff(0, <, len); assert_int(1, ==, ud.frame_send_cb_called); nghttp2_frame_unpack_frame_hd(&frame.hd, data); rv = nghttp2_frame_unpack_origin_payload(&frame.ext, data + NGHTTP2_FRAME_HDLEN, (size_t)len - NGHTTP2_FRAME_HDLEN, mem); assert_int(0, ==, rv); assert_int32(0, ==, frame.hd.stream_id); assert_uint8(NGHTTP2_ORIGIN, ==, frame.hd.type); assert_size(2, ==, origin.nov); assert_memory_equal(sizeof(nghttp2) - 1, nghttp2, origin.ov[0].origin); assert_size(sizeof(nghttp2) - 1, ==, origin.ov[0].origin_len); assert_memory_equal(sizeof(examples) - 1, examples, origin.ov[1].origin); assert_size(sizeof(examples) - 1, ==, origin.ov[1].origin_len); nghttp2_frame_origin_free(&frame.ext, mem); nghttp2_session_del(session); /* Submitting ORIGIN frame from client session is error */ nghttp2_session_client_new(&session, &callbacks, NULL); rv = nghttp2_submit_origin(session, NGHTTP2_FLAG_NONE, ov, 1); assert_int(NGHTTP2_ERR_INVALID_STATE, ==, rv); nghttp2_session_del(session); /* Submitting empty ORIGIN frame */ nghttp2_session_server_new(&session, &callbacks, &ud); rv = nghttp2_submit_origin(session, NGHTTP2_FLAG_NONE, NULL, 0); assert_int(0, ==, rv); ud.frame_send_cb_called = 0; len = nghttp2_session_mem_send2(session, &data); assert_ptrdiff(NGHTTP2_FRAME_HDLEN, ==, len); assert_int(1, ==, ud.frame_send_cb_called); nghttp2_frame_unpack_frame_hd(&frame.hd, data); assert_uint8(NGHTTP2_ORIGIN, ==, frame.hd.type); nghttp2_session_del(session); } void test_nghttp2_submit_priority_update(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; const uint8_t field_value[] = "i"; my_user_data ud; const uint8_t *data; int rv; nghttp2_frame frame; nghttp2_ext_priority_update priority_update; nghttp2_ssize len; int32_t stream_id; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_frame_send_callback = on_frame_send_callback; nghttp2_session_client_new(&session, &callbacks, &ud); session->pending_no_rfc7540_priorities = 1; stream_id = nghttp2_submit_request2(session, NULL, reqnv, ARRLEN(reqnv), NULL, NULL); assert_int32(1, ==, stream_id); len = nghttp2_session_mem_send2(session, &data); assert_ptrdiff(0, <, len); rv = nghttp2_submit_priority_update(session, NGHTTP2_FLAG_NONE, stream_id, field_value, sizeof(field_value) - 1); assert_int(0, ==, rv); frame.ext.payload = &priority_update; ud.frame_send_cb_called = 0; len = nghttp2_session_mem_send2(session, &data); assert_ptrdiff(0, <, len); assert_int(1, ==, ud.frame_send_cb_called); nghttp2_frame_unpack_frame_hd(&frame.hd, data); nghttp2_frame_unpack_priority_update_payload( &frame.ext, (uint8_t *)(data + NGHTTP2_FRAME_HDLEN), (size_t)len - NGHTTP2_FRAME_HDLEN); assert_int32(0, ==, frame.hd.stream_id); assert_uint8(NGHTTP2_PRIORITY_UPDATE, ==, frame.hd.type); assert_int32(stream_id, ==, priority_update.stream_id); assert_size(sizeof(field_value) - 1, ==, priority_update.field_value_len); assert_memory_equal(sizeof(field_value) - 1, field_value, priority_update.field_value); nghttp2_session_del(session); /* Submitting PRIORITY_UPDATE frame from server session is error */ nghttp2_session_server_new(&session, &callbacks, &ud); open_recv_stream(session, 1); rv = nghttp2_submit_priority_update(session, NGHTTP2_FLAG_NONE, 1, field_value, sizeof(field_value) - 1); assert_int(NGHTTP2_ERR_INVALID_STATE, ==, rv); nghttp2_session_del(session); /* Submitting PRIORITY_UPDATE with empty field_value */ nghttp2_session_client_new(&session, &callbacks, &ud); stream_id = nghttp2_submit_request2(session, NULL, reqnv, ARRLEN(reqnv), NULL, NULL); assert_int32(1, ==, stream_id); len = nghttp2_session_mem_send2(session, &data); assert_ptrdiff(0, <, len); rv = nghttp2_submit_priority_update(session, NGHTTP2_FLAG_NONE, stream_id, NULL, 0); assert_int(0, ==, rv); frame.ext.payload = &priority_update; len = nghttp2_session_mem_send2(session, &data); assert_ptrdiff(0, <, len); nghttp2_frame_unpack_frame_hd(&frame.hd, data); nghttp2_frame_unpack_priority_update_payload( &frame.ext, (uint8_t *)(data + NGHTTP2_FRAME_HDLEN), (size_t)len - NGHTTP2_FRAME_HDLEN); assert_int32(0, ==, frame.hd.stream_id); assert_uint8(NGHTTP2_PRIORITY_UPDATE, ==, frame.hd.type); assert_int32(stream_id, ==, priority_update.stream_id); assert_size(0, ==, priority_update.field_value_len); assert_null(priority_update.field_value); nghttp2_session_del(session); } void test_nghttp2_submit_rst_stream(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_outbound_item *item; int rv; int32_t stream_id; nghttp2_ssize datalen; const uint8_t *data; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); /* Sending RST_STREAM to idle stream (local) is ignored */ nghttp2_session_client_new(&session, &callbacks, NULL); rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 1, NGHTTP2_NO_ERROR); assert_int(0, ==, rv); item = nghttp2_outbound_queue_top(&session->ob_reg); assert_null(item); nghttp2_session_del(session); /* Sending RST_STREAM to idle stream (remote) is ignored */ nghttp2_session_client_new(&session, &callbacks, NULL); rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 2, NGHTTP2_NO_ERROR); assert_int(0, ==, rv); item = nghttp2_outbound_queue_top(&session->ob_reg); assert_null(item); nghttp2_session_del(session); /* Sending RST_STREAM to non-idle stream (local) */ nghttp2_session_client_new(&session, &callbacks, NULL); open_sent_stream(session, 1); rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 1, NGHTTP2_NO_ERROR); assert_int(0, ==, rv); item = nghttp2_outbound_queue_top(&session->ob_reg); assert_not_null(item); assert_uint8(NGHTTP2_RST_STREAM, ==, item->frame.hd.type); assert_int32(1, ==, item->frame.hd.stream_id); nghttp2_session_del(session); /* Sending RST_STREAM to non-idle stream (remote) */ nghttp2_session_client_new(&session, &callbacks, NULL); open_recv_stream(session, 2); rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 2, NGHTTP2_NO_ERROR); assert_int(0, ==, rv); item = nghttp2_outbound_queue_top(&session->ob_reg); assert_not_null(item); assert_uint8(NGHTTP2_RST_STREAM, ==, item->frame.hd.type); assert_int32(2, ==, item->frame.hd.stream_id); nghttp2_session_del(session); /* Sending RST_STREAM to pending stream */ nghttp2_session_client_new(&session, &callbacks, NULL); stream_id = nghttp2_submit_request2(session, NULL, reqnv, ARRLEN(reqnv), NULL, NULL); assert_int32(0, <, stream_id); item = nghttp2_outbound_queue_top(&session->ob_syn); assert_not_null(item); assert_uint8(NGHTTP2_HEADERS, ==, item->frame.hd.type); assert_false(item->aux_data.headers.canceled); rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id, NGHTTP2_NO_ERROR); assert_int(0, ==, rv); item = nghttp2_outbound_queue_top(&session->ob_syn); assert_not_null(item); assert_uint8(NGHTTP2_HEADERS, ==, item->frame.hd.type); assert_true(item->aux_data.headers.canceled); nghttp2_session_del(session); /* Sending RST_STREAM to closed stream */ nghttp2_session_client_new(&session, &callbacks, NULL); open_sent_stream(session, 1); rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 1, NGHTTP2_NO_ERROR); assert_int(0, ==, rv); item = nghttp2_outbound_queue_top(&session->ob_reg); assert_not_null(item); assert_uint8(NGHTTP2_RST_STREAM, ==, item->frame.hd.type); assert_int32(1, ==, item->frame.hd.stream_id); datalen = nghttp2_session_mem_send2(session, &data); assert_ptrdiff(0, <, datalen); item = nghttp2_outbound_queue_top(&session->ob_reg); assert_null(item); rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 1, NGHTTP2_NO_ERROR); assert_int(0, ==, rv); item = nghttp2_outbound_queue_top(&session->ob_reg); assert_null(item); nghttp2_session_del(session); } void test_nghttp2_session_open_stream(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_stream *stream; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); nghttp2_session_server_new(&session, &callbacks, NULL); stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_STREAM_OPENED, NULL); assert_size(1, ==, session->num_incoming_streams); assert_size(0, ==, session->num_outgoing_streams); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_OPENED, ==, stream->state); assert_uint8(NGHTTP2_SHUT_NONE, ==, stream->shut_flags); stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_STREAM_OPENING, NULL); assert_size(1, ==, session->num_incoming_streams); assert_size(1, ==, session->num_outgoing_streams); assert_uint8(NGHTTP2_SHUT_NONE, ==, stream->shut_flags); stream = nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_STREAM_RESERVED, NULL); assert_size(1, ==, session->num_incoming_streams); assert_size(1, ==, session->num_outgoing_streams); assert_uint8(NGHTTP2_SHUT_RD, ==, stream->shut_flags); stream = nghttp2_session_open_stream(session, 3, NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_STREAM_OPENED, NULL); /* Dependency to idle stream */ stream = nghttp2_session_open_stream(session, 5, NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_STREAM_OPENED, NULL); /* Dependency to closed stream which is not in dependency tree */ session->last_recv_stream_id = 7; stream = nghttp2_session_open_stream(session, 9, NGHTTP2_FLAG_NONE, NGHTTP2_STREAM_OPENED, NULL); nghttp2_session_del(session); nghttp2_session_client_new(&session, &callbacks, NULL); stream = nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_STREAM_RESERVED, NULL); assert_size(0, ==, session->num_incoming_streams); assert_size(0, ==, session->num_outgoing_streams); assert_uint8(NGHTTP2_SHUT_WR, ==, stream->shut_flags); nghttp2_session_del(session); } void test_nghttp2_session_get_next_ob_item(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_priority_spec pri_spec; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_client_new(&session, &callbacks, NULL); session->remote_settings.max_concurrent_streams = 2; assert_null(nghttp2_session_get_next_ob_item(session)); nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL); assert_uint8(NGHTTP2_PING, ==, nghttp2_session_get_next_ob_item(session)->frame.hd.type); assert_int32(1, ==, nghttp2_submit_request2(session, NULL, NULL, 0, NULL, NULL)); assert_uint8(NGHTTP2_PING, ==, nghttp2_session_get_next_ob_item(session)->frame.hd.type); assert_int(0, ==, nghttp2_session_send(session)); assert_null(nghttp2_session_get_next_ob_item(session)); /* Incoming stream does not affect the number of outgoing max concurrent streams. */ open_recv_stream(session, 2); nghttp2_priority_spec_init(&pri_spec, 0, NGHTTP2_MAX_WEIGHT, 0); assert_int(3, ==, nghttp2_submit_request2(session, &pri_spec, NULL, 0, NULL, NULL)); assert_uint8(NGHTTP2_HEADERS, ==, nghttp2_session_get_next_ob_item(session)->frame.hd.type); assert_int(0, ==, nghttp2_session_send(session)); assert_int(5, ==, nghttp2_submit_request2(session, &pri_spec, NULL, 0, NULL, NULL)); assert_null(nghttp2_session_get_next_ob_item(session)); session->remote_settings.max_concurrent_streams = 3; assert_uint8(NGHTTP2_HEADERS, ==, nghttp2_session_get_next_ob_item(session)->frame.hd.type); nghttp2_session_del(session); /* Check that push reply HEADERS are queued into ob_ss_pq */ nghttp2_session_server_new(&session, &callbacks, NULL); session->remote_settings.max_concurrent_streams = 0; open_sent_stream2(session, 2, NGHTTP2_STREAM_RESERVED); assert_int32(0, ==, nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 2, NULL, NULL, 0, NULL)); assert_null(nghttp2_session_get_next_ob_item(session)); assert_size(1, ==, nghttp2_outbound_queue_size(&session->ob_syn)); nghttp2_session_del(session); } void test_nghttp2_session_pop_next_ob_item(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_outbound_item *item; nghttp2_priority_spec pri_spec; nghttp2_mem *mem; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_client_new(&session, &callbacks, NULL); session->remote_settings.max_concurrent_streams = 1; assert_null(nghttp2_session_pop_next_ob_item(session)); nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL); nghttp2_priority_spec_init(&pri_spec, 0, 254, 0); nghttp2_submit_request2(session, &pri_spec, NULL, 0, NULL, NULL); item = nghttp2_session_pop_next_ob_item(session); assert_uint8(NGHTTP2_PING, ==, item->frame.hd.type); nghttp2_outbound_item_free(item, mem); mem->free(item, NULL); item = nghttp2_session_pop_next_ob_item(session); assert_uint8(NGHTTP2_HEADERS, ==, item->frame.hd.type); nghttp2_outbound_item_free(item, mem); mem->free(item, NULL); assert_null(nghttp2_session_pop_next_ob_item(session)); /* Incoming stream does not affect the number of outgoing max concurrent streams. */ open_recv_stream(session, 4); /* In-flight outgoing stream */ open_sent_stream(session, 1); nghttp2_priority_spec_init(&pri_spec, 0, NGHTTP2_MAX_WEIGHT, 0); nghttp2_submit_request2(session, &pri_spec, NULL, 0, NULL, NULL); assert_null(nghttp2_session_pop_next_ob_item(session)); session->remote_settings.max_concurrent_streams = 2; item = nghttp2_session_pop_next_ob_item(session); assert_uint8(NGHTTP2_HEADERS, ==, item->frame.hd.type); nghttp2_outbound_item_free(item, mem); mem->free(item, NULL); nghttp2_session_del(session); /* Check that push reply HEADERS are queued into ob_ss_pq */ nghttp2_session_server_new(&session, &callbacks, NULL); session->remote_settings.max_concurrent_streams = 0; open_sent_stream2(session, 2, NGHTTP2_STREAM_RESERVED); assert_int32(0, ==, nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 2, NULL, NULL, 0, NULL)); assert_null(nghttp2_session_pop_next_ob_item(session)); assert_size(1, ==, nghttp2_outbound_queue_size(&session->ob_syn)); nghttp2_session_del(session); } void test_nghttp2_session_reply_fail(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_data_provider2 data_prd; my_user_data ud; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = fail_send_callback; data_prd.read_callback = fixed_length_data_source_read_callback; ud.data_source_length = 4 * 1024; assert_int(0, ==, nghttp2_session_server_new(&session, &callbacks, &ud)); open_recv_stream2(session, 1, NGHTTP2_STREAM_OPENING); assert_int(0, ==, nghttp2_submit_response2(session, 1, NULL, 0, &data_prd)); assert_int(NGHTTP2_ERR_CALLBACK_FAILURE, ==, nghttp2_session_send(session)); nghttp2_session_del(session); } void test_nghttp2_session_max_concurrent_streams(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_frame frame; nghttp2_outbound_item *item; nghttp2_mem *mem; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_server_new(&session, &callbacks, NULL); open_recv_stream(session, 1); /* Check un-ACKed SETTINGS_MAX_CONCURRENT_STREAMS */ nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3, NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); session->pending_local_max_concurrent_stream = 1; assert_int(NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_request_headers_received(session, &frame)); item = nghttp2_outbound_queue_top(&session->ob_reg); assert_uint8(NGHTTP2_RST_STREAM, ==, item->frame.hd.type); assert_uint32(NGHTTP2_REFUSED_STREAM, ==, item->frame.rst_stream.error_code); assert_int(0, ==, nghttp2_session_send(session)); /* Check ACKed SETTINGS_MAX_CONCURRENT_STREAMS */ session->local_settings.max_concurrent_streams = 1; frame.hd.stream_id = 5; assert_int(NGHTTP2_ERR_IGN_HEADER_BLOCK, ==, nghttp2_session_on_request_headers_received(session, &frame)); item = nghttp2_outbound_queue_top(&session->ob_reg); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_uint32(NGHTTP2_PROTOCOL_ERROR, ==, item->frame.goaway.error_code); nghttp2_frame_headers_free(&frame.headers, mem); nghttp2_session_del(session); } void test_nghttp2_session_stop_data_with_rst_stream(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; nghttp2_data_provider2 data_prd; nghttp2_frame frame; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_frame_send_callback = on_frame_send_callback; callbacks.send_callback2 = block_count_send_callback; data_prd.read_callback = fixed_length_data_source_read_callback; ud.frame_send_cb_called = 0; ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 4; nghttp2_session_server_new(&session, &callbacks, &ud); open_recv_stream2(session, 1, NGHTTP2_STREAM_OPENING); nghttp2_submit_response2(session, 1, NULL, 0, &data_prd); ud.block_count = 2; /* Sends response HEADERS + DATA[0] */ assert_int(0, ==, nghttp2_session_send(session)); assert_uint8(NGHTTP2_DATA, ==, ud.sent_frame_type); /* data for DATA[1] is read from data_prd but it is not sent */ assert_size(ud.data_source_length, ==, NGHTTP2_DATA_PAYLOADLEN * 2); nghttp2_frame_rst_stream_init(&frame.rst_stream, 1, NGHTTP2_CANCEL); assert_int(0, ==, nghttp2_session_on_rst_stream_received(session, &frame)); nghttp2_frame_rst_stream_free(&frame.rst_stream); /* Big enough number to send all DATA frames potentially. */ ud.block_count = 100; /* Nothing will be sent in the following call. */ assert_int(0, ==, nghttp2_session_send(session)); /* With RST_STREAM, stream is canceled and further DATA on that stream are not sent. */ assert_size(ud.data_source_length, ==, NGHTTP2_DATA_PAYLOADLEN * 2); assert_null(nghttp2_session_get_stream(session, 1)); nghttp2_session_del(session); } void test_nghttp2_session_defer_data(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; nghttp2_data_provider2 data_prd; nghttp2_outbound_item *item; nghttp2_stream *stream; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_frame_send_callback = on_frame_send_callback; callbacks.send_callback2 = block_count_send_callback; data_prd.read_callback = defer_data_source_read_callback; ud.frame_send_cb_called = 0; ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 4; nghttp2_session_server_new(&session, &callbacks, &ud); stream = open_recv_stream2(session, 1, NGHTTP2_STREAM_OPENING); session->remote_window_size = 1 << 20; stream->remote_window_size = 1 << 20; nghttp2_submit_response2(session, 1, NULL, 0, &data_prd); ud.block_count = 1; /* Sends HEADERS reply */ assert_int(0, ==, nghttp2_session_send(session)); assert_uint8(NGHTTP2_HEADERS, ==, ud.sent_frame_type); /* No data is read */ assert_size(ud.data_source_length, ==, NGHTTP2_DATA_PAYLOADLEN * 4); ud.block_count = 1; nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL); /* Sends PING */ assert_int(0, ==, nghttp2_session_send(session)); assert_uint8(NGHTTP2_PING, ==, ud.sent_frame_type); /* Resume deferred DATA */ assert_int(0, ==, nghttp2_session_resume_data(session, 1)); item = stream->item; item->aux_data.data.dpw.data_prd.v1.read_callback = fixed_length_data_source_read_callback; ud.block_count = 1; /* Reads 2 DATA chunks */ assert_int(0, ==, nghttp2_session_send(session)); assert_size(ud.data_source_length, ==, NGHTTP2_DATA_PAYLOADLEN * 2); /* Deferred again */ item->aux_data.data.dpw.data_prd.v1.read_callback = defer_data_source_read_callback; /* This is needed since 16KiB block is already read and waiting to be sent. No read_callback invocation. */ ud.block_count = 1; assert_int(0, ==, nghttp2_session_send(session)); assert_size(ud.data_source_length, ==, NGHTTP2_DATA_PAYLOADLEN * 2); /* Resume deferred DATA */ assert_int(0, ==, nghttp2_session_resume_data(session, 1)); item->aux_data.data.dpw.data_prd.v1.read_callback = fixed_length_data_source_read_callback; ud.block_count = 1; /* Reads 2 16KiB blocks */ assert_int(0, ==, nghttp2_session_send(session)); assert_size(ud.data_source_length, ==, 0); nghttp2_session_del(session); } void test_nghttp2_session_flow_control(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; nghttp2_data_provider2 data_prd; nghttp2_frame frame; nghttp2_stream *stream; int32_t new_initial_window_size; nghttp2_settings_entry iv[1]; nghttp2_frame settings_frame; nghttp2_mem *mem; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = fixed_bytes_send_callback; callbacks.on_frame_send_callback = on_frame_send_callback; data_prd.read_callback = fixed_length_data_source_read_callback; ud.frame_send_cb_called = 0; ud.data_source_length = 128 * 1024; /* Use smaller emission count so that we can check outbound flow control window calculation is correct. */ ud.fixed_sendlen = 2 * 1024; /* Initial window size to 64KiB - 1*/ nghttp2_session_client_new(&session, &callbacks, &ud); /* Change it to 64KiB for easy calculation */ session->remote_window_size = 64 * 1024; session->remote_settings.initial_window_size = 64 * 1024; nghttp2_submit_request2(session, NULL, NULL, 0, &data_prd, NULL); /* Sends 64KiB - 1 data */ assert_int(0, ==, nghttp2_session_send(session)); assert_size(64 * 1024, ==, ud.data_source_length); /* Back 32KiB in stream window */ nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 1, 32 * 1024); nghttp2_session_on_window_update_received(session, &frame); /* Send nothing because of connection-level window */ assert_int(0, ==, nghttp2_session_send(session)); assert_size(64 * 1024, ==, ud.data_source_length); /* Back 32KiB in connection-level window */ frame.hd.stream_id = 0; nghttp2_session_on_window_update_received(session, &frame); /* Sends another 32KiB data */ assert_int(0, ==, nghttp2_session_send(session)); assert_size(32 * 1024, ==, ud.data_source_length); stream = nghttp2_session_get_stream(session, 1); /* Change initial window size to 16KiB. The window_size becomes negative. */ new_initial_window_size = 16 * 1024; stream->remote_window_size = new_initial_window_size - ((int32_t)session->remote_settings.initial_window_size - stream->remote_window_size); session->remote_settings.initial_window_size = (uint32_t)new_initial_window_size; assert_int32(-48 * 1024, ==, stream->remote_window_size); /* Back 48KiB to stream window */ frame.hd.stream_id = 1; frame.window_update.window_size_increment = 48 * 1024; nghttp2_session_on_window_update_received(session, &frame); /* Nothing is sent because window_size is 0 */ assert_int(0, ==, nghttp2_session_send(session)); assert_size(32 * 1024, ==, ud.data_source_length); /* Back 16KiB in stream window */ frame.hd.stream_id = 1; frame.window_update.window_size_increment = 16 * 1024; nghttp2_session_on_window_update_received(session, &frame); /* Back 24KiB in connection-level window */ frame.hd.stream_id = 0; frame.window_update.window_size_increment = 24 * 1024; nghttp2_session_on_window_update_received(session, &frame); /* Sends another 16KiB data */ assert_int(0, ==, nghttp2_session_send(session)); assert_size(16 * 1024, ==, ud.data_source_length); /* Increase initial window size to 32KiB */ iv[0].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; iv[0].value = 32 * 1024; nghttp2_frame_settings_init(&settings_frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 1), 1); nghttp2_session_on_settings_received(session, &settings_frame, 1); nghttp2_frame_settings_free(&settings_frame.settings, mem); /* Sends another 8KiB data */ assert_int(0, ==, nghttp2_session_send(session)); assert_size(8 * 1024, ==, ud.data_source_length); /* Back 8KiB in connection-level window */ frame.hd.stream_id = 0; frame.window_update.window_size_increment = 8 * 1024; nghttp2_session_on_window_update_received(session, &frame); /* Sends last 8KiB data */ assert_int(0, ==, nghttp2_session_send(session)); assert_size(0, ==, ud.data_source_length); assert_true(nghttp2_session_get_stream(session, 1)->shut_flags & NGHTTP2_SHUT_WR); nghttp2_frame_window_update_free(&frame.window_update); nghttp2_session_del(session); } void test_nghttp2_session_flow_control_data_recv(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; uint8_t data[64 * 1024 + 16]; nghttp2_frame_hd hd; nghttp2_outbound_item *item; nghttp2_stream *stream; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; /* Initial window size to 64KiB - 1*/ nghttp2_session_client_new(&session, &callbacks, NULL); stream = open_sent_stream(session, 1); nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); session->local_window_size = NGHTTP2_MAX_PAYLOADLEN; stream->local_window_size = NGHTTP2_MAX_PAYLOADLEN; /* Create DATA frame */ memset(data, 0, sizeof(data)); nghttp2_frame_hd_init(&hd, NGHTTP2_MAX_PAYLOADLEN, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 1); nghttp2_frame_pack_frame_hd(data, &hd); assert_ptrdiff( NGHTTP2_MAX_PAYLOADLEN + NGHTTP2_FRAME_HDLEN, ==, nghttp2_session_mem_recv2(session, data, NGHTTP2_MAX_PAYLOADLEN + NGHTTP2_FRAME_HDLEN)); item = nghttp2_session_get_next_ob_item(session); /* Since this is the last frame, stream-level WINDOW_UPDATE is not issued, but connection-level is. */ assert_uint8(NGHTTP2_WINDOW_UPDATE, ==, item->frame.hd.type); assert_int32(0, ==, item->frame.hd.stream_id); assert_int32(NGHTTP2_MAX_PAYLOADLEN, ==, item->frame.window_update.window_size_increment); assert_int(0, ==, nghttp2_session_send(session)); /* Receive DATA for closed stream. They are still subject to under connection-level flow control, since this situation arises when RST_STREAM is issued by the remote, but the local side keeps sending DATA frames. Without calculating connection-level window, the subsequent flow control gets confused. */ assert_ptrdiff( NGHTTP2_MAX_PAYLOADLEN + NGHTTP2_FRAME_HDLEN, ==, nghttp2_session_mem_recv2(session, data, NGHTTP2_MAX_PAYLOADLEN + NGHTTP2_FRAME_HDLEN)); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_WINDOW_UPDATE, ==, item->frame.hd.type); assert_int32(0, ==, item->frame.hd.stream_id); assert_int32(NGHTTP2_MAX_PAYLOADLEN, ==, item->frame.window_update.window_size_increment); nghttp2_session_del(session); } void test_nghttp2_session_flow_control_data_with_padding_recv(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; uint8_t data[1024]; nghttp2_frame_hd hd; nghttp2_stream *stream; nghttp2_option *option; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_option_new(&option); /* Disable auto window update so that we can check padding is consumed automatically */ nghttp2_option_set_no_auto_window_update(option, 1); /* Initial window size to 64KiB - 1*/ nghttp2_session_client_new2(&session, &callbacks, NULL, option); nghttp2_option_del(option); stream = open_sent_stream(session, 1); /* Create DATA frame */ memset(data, 0, sizeof(data)); nghttp2_frame_hd_init(&hd, 357, NGHTTP2_DATA, NGHTTP2_FLAG_PADDED, 1); nghttp2_frame_pack_frame_hd(data, &hd); /* Set Pad Length field, which itself is padding */ data[NGHTTP2_FRAME_HDLEN] = 255; assert_ptrdiff( (nghttp2_ssize)(NGHTTP2_FRAME_HDLEN + hd.length), ==, nghttp2_session_mem_recv2(session, data, NGHTTP2_FRAME_HDLEN + hd.length)); assert_int32((int32_t)hd.length, ==, session->recv_window_size); assert_int32((int32_t)hd.length, ==, stream->recv_window_size); assert_int32(256, ==, session->consumed_size); assert_int32(256, ==, stream->consumed_size); assert_int32(357, ==, session->recv_window_size); assert_int32(357, ==, stream->recv_window_size); /* Receive the same DATA frame, but in 2 parts: first 9 + 1 + 102 bytes which includes 1st padding byte, and remainder */ assert_ptrdiff( (nghttp2_ssize)(NGHTTP2_FRAME_HDLEN + 103), ==, nghttp2_session_mem_recv2(session, data, NGHTTP2_FRAME_HDLEN + 103)); assert_int32(258, ==, session->consumed_size); assert_int32(258, ==, stream->consumed_size); assert_int32(460, ==, session->recv_window_size); assert_int32(460, ==, stream->recv_window_size); /* 357 - 103 = 254 bytes left */ assert_ptrdiff(254, ==, nghttp2_session_mem_recv2(session, data, 254)); assert_int32(512, ==, session->consumed_size); assert_int32(512, ==, stream->consumed_size); assert_int32(714, ==, session->recv_window_size); assert_int32(714, ==, stream->recv_window_size); /* Receive the same DATA frame, but in 2 parts: first 9 = 1 + 101 bytes which only includes data without padding, 2nd part is padding only */ assert_ptrdiff( (nghttp2_ssize)(NGHTTP2_FRAME_HDLEN + 102), ==, nghttp2_session_mem_recv2(session, data, NGHTTP2_FRAME_HDLEN + 102)); assert_int32(513, ==, session->consumed_size); assert_int32(513, ==, stream->consumed_size); assert_int32(816, ==, session->recv_window_size); assert_int32(816, ==, stream->recv_window_size); /* 357 - 102 = 255 bytes left */ assert_ptrdiff(255, ==, nghttp2_session_mem_recv2(session, data, 255)); assert_int32(768, ==, session->consumed_size); assert_int32(768, ==, stream->consumed_size); assert_int32(1071, ==, session->recv_window_size); assert_int32(1071, ==, stream->recv_window_size); /* Receive the same DATA frame, but in 2 parts: first 9 = 1 + 50 bytes which includes byte up to middle of data, 2nd part is the remainder */ assert_ptrdiff( (nghttp2_ssize)(NGHTTP2_FRAME_HDLEN + 51), ==, nghttp2_session_mem_recv2(session, data, NGHTTP2_FRAME_HDLEN + 51)); assert_int32(769, ==, session->consumed_size); assert_int32(769, ==, stream->consumed_size); assert_int32(1122, ==, session->recv_window_size); assert_int32(1122, ==, stream->recv_window_size); /* 357 - 51 = 306 bytes left */ assert_ptrdiff(306, ==, nghttp2_session_mem_recv2(session, data, 306)); assert_int32(1024, ==, session->consumed_size); assert_int32(1024, ==, stream->consumed_size); assert_int32(1428, ==, session->recv_window_size); assert_int32(1428, ==, stream->recv_window_size); nghttp2_session_del(session); } void test_nghttp2_session_data_read_temporal_failure(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; nghttp2_data_provider2 data_prd; nghttp2_frame frame; nghttp2_stream *stream; size_t data_size = 128 * 1024; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_frame_send_callback = on_frame_send_callback; data_prd.read_callback = fixed_length_data_source_read_callback; ud.data_source_length = data_size; /* Initial window size is 64KiB - 1 */ nghttp2_session_client_new(&session, &callbacks, &ud); nghttp2_submit_request2(session, NULL, NULL, 0, &data_prd, NULL); /* Sends NGHTTP2_INITIAL_WINDOW_SIZE data, assuming, it is equal to or smaller than NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE */ assert_int(0, ==, nghttp2_session_send(session)); assert_size(data_size - NGHTTP2_INITIAL_WINDOW_SIZE, ==, ud.data_source_length); stream = nghttp2_session_get_stream(session, 1); assert_uint8(NGHTTP2_DATA, ==, stream->item->frame.hd.type); stream->item->aux_data.data.dpw.data_prd.v1.read_callback = temporal_failure_data_source_read_callback; /* Back NGHTTP2_INITIAL_WINDOW_SIZE to both connection-level and stream-wise window */ nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 1, NGHTTP2_INITIAL_WINDOW_SIZE); nghttp2_session_on_window_update_received(session, &frame); frame.hd.stream_id = 0; nghttp2_session_on_window_update_received(session, &frame); nghttp2_frame_window_update_free(&frame.window_update); /* Sending data will fail (soft fail) and treated as stream error */ ud.frame_send_cb_called = 0; assert_int(0, ==, nghttp2_session_send(session)); assert_size(data_size - NGHTTP2_INITIAL_WINDOW_SIZE, ==, ud.data_source_length); assert_int(1, ==, ud.frame_send_cb_called); assert_uint8(NGHTTP2_RST_STREAM, ==, ud.sent_frame_type); data_prd.read_callback = fail_data_source_read_callback; nghttp2_submit_request2(session, NULL, NULL, 0, &data_prd, NULL); /* Sending data will fail (hard fail) and session tear down */ assert_int(NGHTTP2_ERR_CALLBACK_FAILURE, ==, nghttp2_session_send(session)); nghttp2_session_del(session); } void test_nghttp2_session_on_stream_close(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data user_data; nghttp2_stream *stream; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_stream_close_callback = on_stream_close_callback; user_data.stream_close_cb_called = 0; nghttp2_session_client_new(&session, &callbacks, &user_data); stream = open_sent_stream3(session, 1, NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_STREAM_OPENED, &user_data); assert_not_null(stream); assert_int(0, ==, nghttp2_session_close_stream(session, 1, NGHTTP2_NO_ERROR)); assert_int(1, ==, user_data.stream_close_cb_called); nghttp2_session_del(session); } void test_nghttp2_session_on_ctrl_not_send(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data user_data; nghttp2_stream *stream; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_frame_not_send_callback = on_frame_not_send_callback; callbacks.send_callback2 = null_send_callback; user_data.frame_not_send_cb_called = 0; user_data.not_sent_frame_type = 0; user_data.not_sent_error = 0; nghttp2_session_server_new(&session, &callbacks, &user_data); stream = open_recv_stream3(session, 1, NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_STREAM_OPENING, &user_data); /* Check response HEADERS */ /* Send bogus stream ID */ assert_int32(0, ==, nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 3, NULL, NULL, 0, NULL)); assert_int(0, ==, nghttp2_session_send(session)); assert_int(1, ==, user_data.frame_not_send_cb_called); assert_uint8(NGHTTP2_HEADERS, ==, user_data.not_sent_frame_type); assert_int(NGHTTP2_ERR_STREAM_CLOSED, ==, user_data.not_sent_error); user_data.frame_not_send_cb_called = 0; /* Shutdown transmission */ stream->shut_flags |= NGHTTP2_SHUT_WR; assert_int32(0, ==, nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, NULL, NULL, 0, NULL)); assert_int(0, ==, nghttp2_session_send(session)); assert_int(1, ==, user_data.frame_not_send_cb_called); assert_uint8(NGHTTP2_HEADERS, ==, user_data.not_sent_frame_type); assert_int(NGHTTP2_ERR_STREAM_SHUT_WR, ==, user_data.not_sent_error); stream->shut_flags = NGHTTP2_SHUT_NONE; user_data.frame_not_send_cb_called = 0; /* Queue RST_STREAM */ assert_int32(0, ==, nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, NULL, NULL, 0, NULL)); assert_int(0, ==, nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 1, NGHTTP2_INTERNAL_ERROR)); assert_int(0, ==, nghttp2_session_send(session)); assert_int(1, ==, user_data.frame_not_send_cb_called); assert_uint8(NGHTTP2_HEADERS, ==, user_data.not_sent_frame_type); assert_int(NGHTTP2_ERR_STREAM_CLOSING, ==, user_data.not_sent_error); nghttp2_session_del(session); /* Check request HEADERS */ user_data.frame_not_send_cb_called = 0; assert_int(0, ==, nghttp2_session_client_new(&session, &callbacks, &user_data)); /* Maximum Stream ID is reached */ session->next_stream_id = (1u << 31) + 1; assert_int32(NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE, ==, nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1, NULL, NULL, 0, NULL)); user_data.frame_not_send_cb_called = 0; /* GOAWAY received */ session->goaway_flags |= NGHTTP2_GOAWAY_RECV; session->next_stream_id = 9; assert_int32(0, <, nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1, NULL, NULL, 0, NULL)); assert_int(0, ==, nghttp2_session_send(session)); assert_int(1, ==, user_data.frame_not_send_cb_called); assert_uint8(NGHTTP2_HEADERS, ==, user_data.not_sent_frame_type); assert_int(NGHTTP2_ERR_START_STREAM_NOT_ALLOWED, ==, user_data.not_sent_error); nghttp2_session_del(session); } void test_nghttp2_session_get_outbound_queue_size(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); assert_int(0, ==, nghttp2_session_client_new(&session, &callbacks, NULL)); assert_size(0, ==, nghttp2_session_get_outbound_queue_size(session)); assert_int(0, ==, nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL)); assert_size(1, ==, nghttp2_session_get_outbound_queue_size(session)); assert_int(0, ==, nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, 2, NGHTTP2_NO_ERROR, NULL, 0)); assert_size(2, ==, nghttp2_session_get_outbound_queue_size(session)); nghttp2_session_del(session); } void test_nghttp2_session_get_effective_local_window_size(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_stream *stream; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); assert_int(0, ==, nghttp2_session_client_new(&session, &callbacks, NULL)); stream = open_sent_stream(session, 1); assert_int32(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE, ==, nghttp2_session_get_effective_local_window_size(session)); assert_int32(0, ==, nghttp2_session_get_effective_recv_data_length(session)); assert_int32( NGHTTP2_INITIAL_WINDOW_SIZE, ==, nghttp2_session_get_stream_effective_local_window_size(session, 1)); assert_int32( 0, ==, nghttp2_session_get_stream_effective_recv_data_length(session, 1)); /* Check connection flow control */ session->recv_window_size = 100; nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, 1100); assert_int32(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1000, ==, nghttp2_session_get_effective_local_window_size(session)); assert_int32(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1000, ==, nghttp2_session_get_local_window_size(session)); assert_int32(0, ==, nghttp2_session_get_effective_recv_data_length(session)); nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, -50); /* Now session->recv_window_size = -50 */ assert_int32(-50, ==, session->recv_window_size); assert_int32(50, ==, session->recv_reduction); assert_int32(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 950, ==, nghttp2_session_get_effective_local_window_size(session)); assert_int32(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1000, ==, nghttp2_session_get_local_window_size(session)); assert_int32(0, ==, nghttp2_session_get_effective_recv_data_length(session)); session->recv_window_size += 50; /* Now session->recv_window_size = 0 */ assert_int32(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 950, ==, nghttp2_session_get_local_window_size(session)); nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, 100); assert_int32(50, ==, session->recv_window_size); assert_int32(0, ==, session->recv_reduction); assert_int32(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1050, ==, nghttp2_session_get_effective_local_window_size(session)); assert_int32(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1000, ==, nghttp2_session_get_local_window_size(session)); assert_int32(50, ==, nghttp2_session_get_effective_recv_data_length(session)); /* Check stream flow control */ stream->recv_window_size = 100; nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 1, 1100); assert_int32( NGHTTP2_INITIAL_WINDOW_SIZE + 1000, ==, nghttp2_session_get_stream_effective_local_window_size(session, 1)); assert_int32(NGHTTP2_INITIAL_WINDOW_SIZE + 1000, ==, nghttp2_session_get_stream_local_window_size(session, 1)); assert_int32( 0, ==, nghttp2_session_get_stream_effective_recv_data_length(session, 1)); nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 1, -50); /* Now stream->recv_window_size = -50 */ assert_int32( NGHTTP2_INITIAL_WINDOW_SIZE + 950, ==, nghttp2_session_get_stream_effective_local_window_size(session, 1)); assert_int32(NGHTTP2_INITIAL_WINDOW_SIZE + 1000, ==, nghttp2_session_get_stream_local_window_size(session, 1)); assert_int32( 0, ==, nghttp2_session_get_stream_effective_recv_data_length(session, 1)); stream->recv_window_size += 50; /* Now stream->recv_window_size = 0 */ nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 1, 100); assert_int32( NGHTTP2_INITIAL_WINDOW_SIZE + 1050, ==, nghttp2_session_get_stream_effective_local_window_size(session, 1)); assert_int32(NGHTTP2_INITIAL_WINDOW_SIZE + 1000, ==, nghttp2_session_get_stream_local_window_size(session, 1)); assert_int32( 50, ==, nghttp2_session_get_stream_effective_recv_data_length(session, 1)); nghttp2_session_del(session); } void test_nghttp2_session_set_option(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_option *option; nghttp2_hd_deflater *deflater; int rv; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; /* Test for nghttp2_option_set_no_auto_window_update */ nghttp2_option_new(&option); nghttp2_option_set_no_auto_window_update(option, 1); nghttp2_session_client_new2(&session, &callbacks, NULL, option); assert_true(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE); nghttp2_session_del(session); nghttp2_option_del(option); /* Test for nghttp2_option_set_peer_max_concurrent_streams */ nghttp2_option_new(&option); nghttp2_option_set_peer_max_concurrent_streams(option, 100); nghttp2_session_client_new2(&session, &callbacks, NULL, option); assert_uint32(100, ==, session->remote_settings.max_concurrent_streams); nghttp2_session_del(session); nghttp2_option_del(option); /* Test for nghttp2_option_set_max_reserved_remote_streams */ nghttp2_option_new(&option); nghttp2_option_set_max_reserved_remote_streams(option, 99); nghttp2_session_client_new2(&session, &callbacks, NULL, option); assert_size(99, ==, session->max_incoming_reserved_streams); nghttp2_session_del(session); nghttp2_option_del(option); /* Test for nghttp2_option_set_no_auto_ping_ack */ nghttp2_option_new(&option); nghttp2_option_set_no_auto_ping_ack(option, 1); nghttp2_session_client_new2(&session, &callbacks, NULL, option); assert_true(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_PING_ACK); nghttp2_session_del(session); nghttp2_option_del(option); /* Test for nghttp2_option_set_max_deflate_dynamic_table_size */ nghttp2_option_new(&option); nghttp2_option_set_max_deflate_dynamic_table_size(option, 0); nghttp2_session_client_new2(&session, &callbacks, NULL, option); deflater = &session->hd_deflater; rv = nghttp2_submit_request2(session, NULL, reqnv, ARRLEN(reqnv), NULL, NULL); assert_int(1, ==, rv); rv = nghttp2_session_send(session); assert_int(0, ==, rv); assert_size(0, ==, deflater->deflate_hd_table_bufsize_max); assert_size(0, ==, deflater->ctx.hd_table_bufsize); nghttp2_session_del(session); nghttp2_option_del(option); } void test_nghttp2_session_data_backoff_by_high_pri_frame(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; nghttp2_data_provider2 data_prd; nghttp2_stream *stream; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = block_count_send_callback; callbacks.on_frame_send_callback = on_frame_send_callback; data_prd.read_callback = fixed_length_data_source_read_callback; ud.frame_send_cb_called = 0; ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 4; nghttp2_session_client_new(&session, &callbacks, &ud); nghttp2_submit_request2(session, NULL, NULL, 0, &data_prd, NULL); session->remote_window_size = 1 << 20; ud.block_count = 2; /* Sends request HEADERS + DATA[0] */ assert_int(0, ==, nghttp2_session_send(session)); stream = nghttp2_session_get_stream(session, 1); stream->remote_window_size = 1 << 20; assert_uint8(NGHTTP2_DATA, ==, ud.sent_frame_type); /* data for DATA[1] is read from data_prd but it is not sent */ assert_size(ud.data_source_length, ==, NGHTTP2_DATA_PAYLOADLEN * 2); nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL); ud.block_count = 2; /* Sends DATA[1] + PING, PING is interleaved in DATA sequence */ assert_int(0, ==, nghttp2_session_send(session)); assert_uint8(NGHTTP2_PING, ==, ud.sent_frame_type); /* data for DATA[2] is read from data_prd but it is not sent */ assert_size(ud.data_source_length, ==, NGHTTP2_DATA_PAYLOADLEN); ud.block_count = 2; /* Sends DATA[2..3] */ assert_int(0, ==, nghttp2_session_send(session)); assert_true(stream->shut_flags & NGHTTP2_SHUT_WR); nghttp2_session_del(session); } static void check_session_recv_data_with_padding(nghttp2_bufs *bufs, size_t datalen, nghttp2_mem *mem) { nghttp2_session *session; my_user_data ud; nghttp2_session_callbacks callbacks; uint8_t *in; size_t inlen; memset(&callbacks, 0, sizeof(callbacks)); callbacks.on_frame_recv_callback = on_frame_recv_callback; callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; nghttp2_session_server_new(&session, &callbacks, &ud); open_recv_stream(session, 1); inlen = (size_t)nghttp2_bufs_remove(bufs, &in); ud.frame_recv_cb_called = 0; ud.data_chunk_len = 0; assert_ptrdiff((nghttp2_ssize)inlen, ==, nghttp2_session_mem_recv2(session, in, inlen)); assert_int(1, ==, ud.frame_recv_cb_called); assert_size(datalen, ==, ud.data_chunk_len); mem->free(in, NULL); nghttp2_session_del(session); } void test_nghttp2_session_pack_data_with_padding(void) { nghttp2_session *session; my_user_data ud; nghttp2_session_callbacks callbacks; nghttp2_data_provider2 data_prd; nghttp2_frame *frame; size_t datalen = 55; nghttp2_mem *mem; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(callbacks)); callbacks.send_callback2 = block_count_send_callback; callbacks.on_frame_send_callback = on_frame_send_callback; callbacks.select_padding_callback2 = select_padding_callback; data_prd.read_callback = fixed_length_data_source_read_callback; nghttp2_session_client_new(&session, &callbacks, &ud); ud.padlen = 63; nghttp2_submit_request2(session, NULL, NULL, 0, &data_prd, NULL); ud.block_count = 1; ud.data_source_length = datalen; /* Sends HEADERS */ assert_int(0, ==, nghttp2_session_send(session)); assert_uint8(NGHTTP2_HEADERS, ==, ud.sent_frame_type); frame = &session->aob.item->frame; assert_size(ud.padlen, ==, frame->data.padlen); assert_true(frame->hd.flags & NGHTTP2_FLAG_PADDED); /* Check reception of this DATA frame */ check_session_recv_data_with_padding(&session->aob.framebufs, datalen, mem); nghttp2_session_del(session); } void test_nghttp2_session_pack_headers_with_padding(void) { nghttp2_session *session, *sv_session; accumulator acc; my_user_data ud; nghttp2_session_callbacks callbacks; memset(&callbacks, 0, sizeof(callbacks)); callbacks.send_callback2 = accumulator_send_callback; callbacks.on_frame_send_callback = on_frame_send_callback; callbacks.select_padding_callback2 = select_padding_callback; callbacks.on_frame_recv_callback = on_frame_recv_callback; acc.length = 0; ud.acc = &acc; nghttp2_session_client_new(&session, &callbacks, &ud); nghttp2_session_server_new(&sv_session, &callbacks, &ud); ud.padlen = 163; assert_int32( 1, ==, nghttp2_submit_request2(session, NULL, reqnv, ARRLEN(reqnv), NULL, NULL)); assert_int(0, ==, nghttp2_session_send(session)); assert_size(NGHTTP2_MAX_PAYLOADLEN, >, acc.length); ud.frame_recv_cb_called = 0; assert_ptrdiff((nghttp2_ssize)acc.length, ==, nghttp2_session_mem_recv2(sv_session, acc.buf, acc.length)); assert_int(1, ==, ud.frame_recv_cb_called); assert_null(nghttp2_session_get_next_ob_item(sv_session)); nghttp2_session_del(sv_session); nghttp2_session_del(session); } void test_nghttp2_pack_settings_payload(void) { nghttp2_settings_entry iv[2]; uint8_t buf[64]; nghttp2_ssize len; nghttp2_settings_entry *resiv; size_t resniv; nghttp2_mem *mem; mem = nghttp2_mem_default(); iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[0].value = 1023; iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; iv[1].value = 4095; len = nghttp2_pack_settings_payload2(buf, sizeof(buf), iv, 2); assert_ptrdiff(2 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH, ==, len); assert_int(0, ==, nghttp2_frame_unpack_settings_payload2(&resiv, &resniv, buf, (size_t)len, mem)); assert_size(2, ==, resniv); assert_int32(NGHTTP2_SETTINGS_HEADER_TABLE_SIZE, ==, resiv[0].settings_id); assert_uint32(1023, ==, resiv[0].value); assert_int32(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, ==, resiv[1].settings_id); assert_uint32(4095, ==, resiv[1].value); mem->free(resiv, NULL); len = nghttp2_pack_settings_payload2(buf, 9 /* too small */, iv, 2); assert_ptrdiff(NGHTTP2_ERR_INSUFF_BUFSIZE, ==, len); } void test_nghttp2_session_stream_get_state(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_mem *mem; nghttp2_hd_deflater deflater; nghttp2_bufs bufs; nghttp2_buf *buf; nghttp2_stream *stream; nghttp2_ssize rv; nghttp2_data_provider2 data_prd; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&data_prd, 0, sizeof(data_prd)); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_server_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); assert_enum( nghttp2_stream_proto_state, NGHTTP2_STREAM_STATE_IDLE, ==, nghttp2_stream_get_state(nghttp2_session_get_root_stream(session))); /* stream 1 HEADERS; without END_STREAM flag set */ pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv, ARRLEN(reqnv), mem); buf = &bufs.head->buf; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf), ==, rv); stream = nghttp2_session_find_stream(session, 1); assert_not_null(stream); assert_int32(1, ==, stream->stream_id); assert_enum(nghttp2_stream_proto_state, NGHTTP2_STREAM_STATE_OPEN, ==, nghttp2_stream_get_state(stream)); nghttp2_bufs_reset(&bufs); /* stream 3 HEADERS; with END_STREAM flag set */ pack_headers(&bufs, &deflater, 3, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, reqnv, ARRLEN(reqnv), mem); buf = &bufs.head->buf; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf), ==, rv); stream = nghttp2_session_find_stream(session, 3); assert_not_null(stream); assert_int32(3, ==, stream->stream_id); assert_enum(nghttp2_stream_proto_state, NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE, ==, nghttp2_stream_get_state(stream)); nghttp2_bufs_reset(&bufs); /* Respond to stream 1 */ nghttp2_submit_response2(session, 1, resnv, ARRLEN(resnv), NULL); rv = nghttp2_session_send(session); assert_ptrdiff(0, ==, rv); stream = nghttp2_session_find_stream(session, 1); assert_enum(nghttp2_stream_proto_state, NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL, ==, nghttp2_stream_get_state(stream)); /* Respond to stream 3 */ nghttp2_submit_response2(session, 3, resnv, ARRLEN(resnv), NULL); rv = nghttp2_session_send(session); assert_ptrdiff(0, ==, rv); stream = nghttp2_session_find_stream(session, 3); assert_null(stream); /* stream 5 HEADERS; with END_STREAM flag set */ pack_headers(&bufs, &deflater, 5, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, reqnv, ARRLEN(reqnv), mem); buf = &bufs.head->buf; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf), ==, rv); nghttp2_bufs_reset(&bufs); /* Push stream 2 associated to stream 5 */ rv = nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 5, reqnv, ARRLEN(reqnv), NULL); assert_ptrdiff(2, ==, rv); rv = nghttp2_session_send(session); assert_ptrdiff(0, ==, rv); stream = nghttp2_session_find_stream(session, 2); assert_enum(nghttp2_stream_proto_state, NGHTTP2_STREAM_STATE_RESERVED_LOCAL, ==, nghttp2_stream_get_state(stream)); /* Send response to push stream 2 with END_STREAM set */ nghttp2_submit_response2(session, 2, resnv, ARRLEN(resnv), NULL); rv = nghttp2_session_send(session); assert_ptrdiff(0, ==, rv); stream = nghttp2_session_find_stream(session, 2); /* At server, pushed stream object is not retained after closed */ assert_null(stream); /* Push stream 4 associated to stream 5 */ rv = nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 5, reqnv, ARRLEN(reqnv), NULL); assert_ptrdiff(4, ==, rv); rv = nghttp2_session_send(session); assert_ptrdiff(0, ==, rv); stream = nghttp2_session_find_stream(session, 4); assert_enum(nghttp2_stream_proto_state, NGHTTP2_STREAM_STATE_RESERVED_LOCAL, ==, nghttp2_stream_get_state(stream)); /* Send response to push stream 4 without closing */ data_prd.read_callback = defer_data_source_read_callback; nghttp2_submit_response2(session, 4, resnv, ARRLEN(resnv), &data_prd); rv = nghttp2_session_send(session); assert_ptrdiff(0, ==, rv); stream = nghttp2_session_find_stream(session, 4); assert_enum(nghttp2_stream_proto_state, NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE, ==, nghttp2_stream_get_state(stream)); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); /* Test for client side */ nghttp2_session_client_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); nghttp2_submit_request2(session, NULL, reqnv, ARRLEN(reqnv), NULL, NULL); rv = nghttp2_session_send(session); assert_ptrdiff(0, ==, rv); /* Receive PUSH_PROMISE 2 associated to stream 1 */ pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2, reqnv, ARRLEN(reqnv), mem); buf = &bufs.head->buf; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf), ==, rv); stream = nghttp2_session_find_stream(session, 2); assert_enum(nghttp2_stream_proto_state, NGHTTP2_STREAM_STATE_RESERVED_REMOTE, ==, nghttp2_stream_get_state(stream)); nghttp2_bufs_reset(&bufs); /* Receive push response for stream 2 without END_STREAM set */ pack_headers(&bufs, &deflater, 2, NGHTTP2_FLAG_END_HEADERS, resnv, ARRLEN(resnv), mem); buf = &bufs.head->buf; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf), ==, rv); stream = nghttp2_session_find_stream(session, 2); assert_enum(nghttp2_stream_proto_state, NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL, ==, nghttp2_stream_get_state(stream)); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); nghttp2_bufs_free(&bufs); } void test_nghttp2_session_find_stream(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_stream *stream; memset(&callbacks, 0, sizeof(callbacks)); nghttp2_session_server_new(&session, &callbacks, NULL); open_recv_stream(session, 1); stream = nghttp2_session_find_stream(session, 1); assert_not_null(stream); assert_int32(1, ==, stream->stream_id); stream = nghttp2_session_find_stream(session, 0); assert_not_null(stream); assert_int32(0, ==, stream->stream_id); stream = nghttp2_session_find_stream(session, 2); assert_null(stream); nghttp2_session_del(session); } void test_nghttp2_session_graceful_shutdown(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; memset(&callbacks, 0, sizeof(callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_frame_send_callback = on_frame_send_callback; callbacks.on_stream_close_callback = on_stream_close_callback; nghttp2_session_server_new(&session, &callbacks, &ud); open_recv_stream(session, 301); open_sent_stream(session, 302); open_recv_stream(session, 309); open_recv_stream(session, 311); open_recv_stream(session, 319); assert_int(0, ==, nghttp2_submit_shutdown_notice(session)); ud.frame_send_cb_called = 0; assert_int(0, ==, nghttp2_session_send(session)); assert_int(1, ==, ud.frame_send_cb_called); assert_int32((1u << 31) - 1, ==, session->local_last_stream_id); assert_int(0, ==, nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, 311, NGHTTP2_NO_ERROR, NULL, 0)); ud.frame_send_cb_called = 0; ud.stream_close_cb_called = 0; assert_int(0, ==, nghttp2_session_send(session)); assert_int(1, ==, ud.frame_send_cb_called); assert_int32(311, ==, session->local_last_stream_id); assert_int(1, ==, ud.stream_close_cb_called); assert_int( 0, ==, nghttp2_session_terminate_session2(session, 301, NGHTTP2_NO_ERROR)); ud.frame_send_cb_called = 0; ud.stream_close_cb_called = 0; assert_int(0, ==, nghttp2_session_send(session)); assert_int(1, ==, ud.frame_send_cb_called); assert_int32(301, ==, session->local_last_stream_id); assert_int(2, ==, ud.stream_close_cb_called); assert_not_null(nghttp2_session_get_stream(session, 301)); assert_not_null(nghttp2_session_get_stream(session, 302)); assert_null(nghttp2_session_get_stream(session, 309)); assert_null(nghttp2_session_get_stream(session, 311)); assert_null(nghttp2_session_get_stream(session, 319)); nghttp2_session_del(session); } void test_nghttp2_session_on_header_temporal_failure(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; nghttp2_bufs bufs; nghttp2_buf *buf; nghttp2_hd_deflater deflater; nghttp2_nv nv[] = {MAKE_NV("alpha", "bravo"), MAKE_NV("charlie", "delta")}; nghttp2_nv *nva; size_t hdpos; nghttp2_ssize rv; nghttp2_frame frame; nghttp2_frame_hd hd; nghttp2_outbound_item *item; nghttp2_mem *mem; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(callbacks)); callbacks.on_header_callback = temporal_failure_on_header_callback; nghttp2_session_server_new(&session, &callbacks, &ud); frame_pack_bufs_init(&bufs); nghttp2_hd_deflate_init(&deflater, mem); nghttp2_nv_array_copy(&nva, reqnv, ARRLEN(reqnv), mem); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_STREAM, 1, NGHTTP2_HCAT_REQUEST, NULL, nva, ARRLEN(reqnv)); nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); nghttp2_frame_headers_free(&frame.headers, mem); /* We are going to create CONTINUATION. First serialize header block, and then frame header. */ hdpos = nghttp2_bufs_len(&bufs); buf = &bufs.head->buf; buf->last += NGHTTP2_FRAME_HDLEN; nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, &nv[1], 1); nghttp2_frame_hd_init(&hd, nghttp2_bufs_len(&bufs) - hdpos - NGHTTP2_FRAME_HDLEN, NGHTTP2_CONTINUATION, NGHTTP2_FLAG_END_HEADERS, 1); nghttp2_frame_pack_frame_hd(&buf->pos[hdpos], &hd); ud.header_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, rv); assert_int(1, ==, ud.header_cb_called); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_RST_STREAM, ==, item->frame.hd.type); assert_int32(1, ==, item->frame.hd.stream_id); /* Make sure no header decompression error occurred */ assert_uint8(NGHTTP2_GOAWAY_NONE, ==, session->goaway_flags); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); nghttp2_bufs_reset(&bufs); /* Check for PUSH_PROMISE */ nghttp2_hd_deflate_init(&deflater, mem); nghttp2_session_client_new(&session, &callbacks, &ud); open_sent_stream(session, 1); rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2, reqnv, ARRLEN(reqnv), mem); assert_ptrdiff(0, ==, rv); ud.header_cb_called = 0; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, rv); assert_int(1, ==, ud.header_cb_called); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_RST_STREAM, ==, item->frame.hd.type); assert_int32(2, ==, item->frame.hd.stream_id); assert_uint32(NGHTTP2_INTERNAL_ERROR, ==, item->frame.rst_stream.error_code); nghttp2_session_del(session); nghttp2_hd_deflate_free(&deflater); nghttp2_bufs_free(&bufs); } void test_nghttp2_session_recv_client_magic(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_ssize rv; nghttp2_frame ping_frame; uint8_t buf[16]; /* enable global nghttp2_enable_strict_preface here */ nghttp2_enable_strict_preface = 1; memset(&callbacks, 0, sizeof(callbacks)); /* Check success case */ nghttp2_session_server_new(&session, &callbacks, NULL); rv = nghttp2_session_mem_recv2(session, (const uint8_t *)NGHTTP2_CLIENT_MAGIC, NGHTTP2_CLIENT_MAGIC_LEN); assert_ptrdiff(NGHTTP2_CLIENT_MAGIC_LEN, ==, rv); assert_enum(nghttp2_inbound_state, NGHTTP2_IB_READ_FIRST_SETTINGS, ==, session->iframe.state); /* Receiving PING is error because we want SETTINGS. */ nghttp2_frame_ping_init(&ping_frame.ping, NGHTTP2_FLAG_NONE, NULL); nghttp2_frame_pack_frame_hd(buf, &ping_frame.ping.hd); rv = nghttp2_session_mem_recv2(session, buf, NGHTTP2_FRAME_HDLEN); assert_ptrdiff(NGHTTP2_FRAME_HDLEN, ==, rv); assert_enum(nghttp2_inbound_state, NGHTTP2_IB_IGN_ALL, ==, session->iframe.state); assert_size(0, ==, session->iframe.payloadleft); nghttp2_frame_ping_free(&ping_frame.ping); nghttp2_session_del(session); /* Check bad case */ nghttp2_session_server_new(&session, &callbacks, NULL); /* Feed magic with one byte less */ rv = nghttp2_session_mem_recv2(session, (const uint8_t *)NGHTTP2_CLIENT_MAGIC, NGHTTP2_CLIENT_MAGIC_LEN - 1); assert_ptrdiff(NGHTTP2_CLIENT_MAGIC_LEN - 1, ==, rv); assert_enum(nghttp2_inbound_state, NGHTTP2_IB_READ_CLIENT_MAGIC, ==, session->iframe.state); assert_size(1, ==, session->iframe.payloadleft); rv = nghttp2_session_mem_recv2(session, (const uint8_t *)"\0", 1); assert_ptrdiff(NGHTTP2_ERR_BAD_CLIENT_MAGIC, ==, rv); nghttp2_session_del(session); /* disable global nghttp2_enable_strict_preface here */ nghttp2_enable_strict_preface = 0; } void test_nghttp2_session_delete_data_item(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_data_provider2 prd; memset(&callbacks, 0, sizeof(callbacks)); nghttp2_session_server_new(&session, &callbacks, NULL); open_recv_stream(session, 1); open_recv_stream(session, 3); /* We don't care about these members, since we won't send data */ prd.source.ptr = NULL; prd.read_callback = fail_data_source_read_callback; assert_int(0, ==, nghttp2_submit_data2(session, NGHTTP2_FLAG_NONE, 1, &prd)); assert_int(0, ==, nghttp2_submit_data2(session, NGHTTP2_FLAG_NONE, 3, &prd)); nghttp2_session_del(session); } void test_nghttp2_session_open_idle_stream(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_stream *stream; nghttp2_stream *opened_stream; nghttp2_frame frame; nghttp2_ext_priority_update priority_update; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); nghttp2_session_server_new(&session, &callbacks, NULL); frame.ext.payload = &priority_update; nghttp2_frame_priority_update_init(&frame.ext, 1, (uint8_t *)"u=3", strlen("u=3")); assert_int(0, ==, nghttp2_session_on_priority_update_received(session, &frame)); stream = nghttp2_session_get_stream_raw(session, 1); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_IDLE, ==, stream->state); assert_null(stream->closed_next); assert_size(1, ==, session->num_idle_streams); opened_stream = open_recv_stream2(session, 1, NGHTTP2_STREAM_OPENING); assert_ptr_equal(stream, opened_stream); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_OPENING, ==, stream->state); assert_size(0, ==, session->num_idle_streams); nghttp2_frame_priority_free(&frame.priority); nghttp2_session_del(session); /* No RFC 7540 priorities */ nghttp2_session_server_new(&session, &callbacks, NULL); session->pending_no_rfc7540_priorities = 1; frame.ext.payload = &priority_update; nghttp2_frame_priority_update_init(&frame.ext, 1, (uint8_t *)"u=3", strlen("u=3")); assert_int(0, ==, nghttp2_session_on_priority_update_received(session, &frame)); stream = nghttp2_session_get_stream_raw(session, 1); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_IDLE, ==, stream->state); assert_null(stream->closed_next); assert_size(1, ==, session->num_idle_streams); opened_stream = open_recv_stream2(session, 1, NGHTTP2_STREAM_OPENING); assert_ptr_equal(stream, opened_stream); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_OPENING, ==, stream->state); assert_size(0, ==, session->num_idle_streams); nghttp2_frame_priority_free(&frame.priority); nghttp2_session_del(session); } void test_nghttp2_session_cancel_reserved_remote(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_stream *stream; nghttp2_frame frame; nghttp2_nv *nva; size_t nvlen; nghttp2_hd_deflater deflater; nghttp2_mem *mem; nghttp2_bufs bufs; nghttp2_ssize rv; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_client_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); stream = open_recv_stream2(session, 2, NGHTTP2_STREAM_RESERVED); nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 2, NGHTTP2_CANCEL); assert_enum(nghttp2_stream_state, NGHTTP2_STREAM_CLOSING, ==, stream->state); assert_int(0, ==, nghttp2_session_send(session)); nvlen = ARRLEN(resnv); nghttp2_nv_array_copy(&nva, resnv, nvlen, mem); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2, NGHTTP2_HCAT_PUSH_RESPONSE, NULL, nva, nvlen); rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); /* stream is not dangling, so assign NULL */ stream = NULL; /* No RST_STREAM or GOAWAY is generated since stream should be in NGHTTP2_STREAM_CLOSING and push response should be ignored. */ assert_size(0, ==, nghttp2_outbound_queue_size(&session->ob_reg)); /* Check that we can receive push response HEADERS while RST_STREAM is just queued. */ open_recv_stream2(session, 4, NGHTTP2_STREAM_RESERVED); nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 4, NGHTTP2_CANCEL); nghttp2_bufs_reset(&bufs); frame.hd.stream_id = 4; rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); assert_size(1, ==, nghttp2_outbound_queue_size(&session->ob_reg)); nghttp2_frame_headers_free(&frame.headers, mem); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); nghttp2_bufs_free(&bufs); } void test_nghttp2_session_reset_pending_headers(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_stream *stream; int32_t stream_id; my_user_data ud; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_frame_send_callback = on_frame_send_callback; callbacks.on_frame_not_send_callback = on_frame_not_send_callback; callbacks.on_stream_close_callback = on_stream_close_callback; nghttp2_session_client_new(&session, &callbacks, &ud); stream_id = nghttp2_submit_request2(session, NULL, NULL, 0, NULL, NULL); assert_int32(1, <=, stream_id); nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id, NGHTTP2_CANCEL); session->remote_settings.max_concurrent_streams = 0; /* RST_STREAM cancels pending HEADERS and is not actually sent. */ ud.frame_send_cb_called = 0; assert_int(0, ==, nghttp2_session_send(session)); assert_int(0, ==, ud.frame_send_cb_called); stream = nghttp2_session_get_stream(session, stream_id); assert_null(stream); /* See HEADERS is not sent. on_stream_close is called just like transmission failure. */ session->remote_settings.max_concurrent_streams = 1; ud.frame_not_send_cb_called = 0; ud.stream_close_error_code = 0; assert_int(0, ==, nghttp2_session_send(session)); assert_int(1, ==, ud.frame_not_send_cb_called); assert_uint8(NGHTTP2_HEADERS, ==, ud.not_sent_frame_type); assert_uint32(NGHTTP2_CANCEL, ==, ud.stream_close_error_code); stream = nghttp2_session_get_stream(session, stream_id); assert_null(stream); nghttp2_session_del(session); } void test_nghttp2_session_send_data_callback(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_data_provider2 data_prd; my_user_data ud; accumulator acc; nghttp2_frame_hd hd; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = accumulator_send_callback; callbacks.send_data_callback = send_data_callback; data_prd.read_callback = no_copy_data_source_read_callback; acc.length = 0; ud.acc = &acc; ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 2; nghttp2_session_client_new(&session, &callbacks, &ud); open_sent_stream(session, 1); nghttp2_submit_data2(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd); assert_int(0, ==, nghttp2_session_send(session)); assert_size((NGHTTP2_FRAME_HDLEN + NGHTTP2_DATA_PAYLOADLEN) * 2, ==, acc.length); nghttp2_frame_unpack_frame_hd(&hd, acc.buf); assert_size(16384, ==, hd.length); assert_uint8(NGHTTP2_DATA, ==, hd.type); assert_uint8(NGHTTP2_FLAG_NONE, ==, hd.flags); nghttp2_frame_unpack_frame_hd(&hd, acc.buf + NGHTTP2_FRAME_HDLEN + hd.length); assert_size(16384, ==, hd.length); assert_uint8(NGHTTP2_DATA, ==, hd.type); assert_uint8(NGHTTP2_FLAG_END_STREAM, ==, hd.flags); nghttp2_session_del(session); } void test_nghttp2_session_on_begin_headers_temporal_failure(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; nghttp2_bufs bufs; nghttp2_mem *mem; nghttp2_ssize rv; nghttp2_hd_deflater deflater; nghttp2_outbound_item *item; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nghttp2_hd_deflate_init(&deflater, mem); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_begin_headers_callback = temporal_failure_on_begin_headers_callback; callbacks.on_header_callback = on_header_callback; callbacks.on_frame_recv_callback = on_frame_recv_callback; callbacks.send_callback2 = null_send_callback; nghttp2_session_server_new(&session, &callbacks, &ud); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv, ARRLEN(reqnv), mem); assert_ptrdiff(0, ==, rv); ud.header_cb_called = 0; ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, rv); assert_int(0, ==, ud.header_cb_called); assert_int(0, ==, ud.frame_recv_cb_called); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_RST_STREAM, ==, item->frame.hd.type); assert_int32(1, ==, item->frame.hd.stream_id); assert_uint32(NGHTTP2_INTERNAL_ERROR, ==, item->frame.rst_stream.error_code); nghttp2_session_del(session); nghttp2_hd_deflate_free(&deflater); nghttp2_bufs_reset(&bufs); /* check for PUSH_PROMISE */ nghttp2_hd_deflate_init(&deflater, mem); nghttp2_session_client_new(&session, &callbacks, &ud); open_sent_stream(session, 1); rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2, reqnv, ARRLEN(reqnv), mem); assert_ptrdiff(0, ==, rv); ud.header_cb_called = 0; ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, rv); assert_int(0, ==, ud.header_cb_called); assert_int(0, ==, ud.frame_recv_cb_called); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_RST_STREAM, ==, item->frame.hd.type); assert_int32(2, ==, item->frame.hd.stream_id); assert_uint32(NGHTTP2_INTERNAL_ERROR, ==, item->frame.rst_stream.error_code); nghttp2_session_del(session); nghttp2_hd_deflate_free(&deflater); nghttp2_bufs_free(&bufs); } void test_nghttp2_session_defer_then_close(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_data_provider2 prd; int rv; const uint8_t *datap; nghttp2_ssize datalen; nghttp2_frame frame; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_client_new(&session, &callbacks, NULL); prd.read_callback = defer_data_source_read_callback; rv = nghttp2_submit_request2(session, NULL, reqnv, ARRLEN(reqnv), &prd, NULL); assert_ptrdiff(0, <, rv); /* This sends HEADERS */ datalen = nghttp2_session_mem_send2(session, &datap); assert_ptrdiff(0, <, datalen); /* This makes DATA item deferred */ datalen = nghttp2_session_mem_send2(session, &datap); assert_ptrdiff(0, ==, datalen); nghttp2_frame_rst_stream_init(&frame.rst_stream, 1, NGHTTP2_CANCEL); /* Assertion failure; GH-264 */ rv = nghttp2_session_on_rst_stream_received(session, &frame); assert_int(0, ==, rv); nghttp2_session_del(session); } static int submit_response_on_stream_close(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *user_data) { nghttp2_data_provider2 data_prd; (void)error_code; (void)user_data; data_prd.read_callback = temporal_failure_data_source_read_callback; // Attempt to submit response or data to the stream being closed switch (stream_id) { case 1: assert_int(0, ==, nghttp2_submit_response2(session, stream_id, resnv, ARRLEN(resnv), &data_prd)); break; case 3: assert_int( 0, ==, nghttp2_submit_data2(session, NGHTTP2_FLAG_NONE, stream_id, &data_prd)); break; } return 0; } void test_nghttp2_session_detach_item_from_closed_stream(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; memset(&callbacks, 0, sizeof(callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_stream_close_callback = submit_response_on_stream_close; nghttp2_session_server_new(&session, &callbacks, NULL); open_recv_stream(session, 1); open_recv_stream(session, 3); nghttp2_session_close_stream(session, 1, NGHTTP2_NO_ERROR); nghttp2_session_close_stream(session, 3, NGHTTP2_NO_ERROR); assert_int(0, ==, nghttp2_session_send(session)); nghttp2_session_del(session); /* No RFC 7540 priorities */ nghttp2_session_server_new(&session, &callbacks, NULL); session->pending_no_rfc7540_priorities = 1; open_recv_stream(session, 1); open_recv_stream(session, 3); nghttp2_session_close_stream(session, 1, NGHTTP2_NO_ERROR); nghttp2_session_close_stream(session, 3, NGHTTP2_NO_ERROR); assert_int(0, ==, nghttp2_session_send(session)); nghttp2_session_del(session); } void test_nghttp2_session_flooding(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_bufs bufs; nghttp2_buf *buf; nghttp2_frame frame; nghttp2_mem *mem; size_t i; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(callbacks)); /* PING ACK */ nghttp2_session_server_new(&session, &callbacks, NULL); nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL); nghttp2_frame_pack_ping(&bufs, &frame.ping); nghttp2_frame_ping_free(&frame.ping); buf = &bufs.head->buf; for (i = 0; i < NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM; ++i) { assert_ptrdiff( (nghttp2_ssize)nghttp2_buf_len(buf), ==, nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf))); } assert_ptrdiff( NGHTTP2_ERR_FLOODED, ==, nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf))); nghttp2_session_del(session); /* SETTINGS ACK */ nghttp2_bufs_reset(&bufs); nghttp2_session_server_new(&session, &callbacks, NULL); nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, NULL, 0); nghttp2_frame_pack_settings(&bufs, &frame.settings); nghttp2_frame_settings_free(&frame.settings, mem); buf = &bufs.head->buf; for (i = 0; i < NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM; ++i) { assert_ptrdiff( (nghttp2_ssize)nghttp2_buf_len(buf), ==, nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf))); } assert_ptrdiff( NGHTTP2_ERR_FLOODED, ==, nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf))); nghttp2_session_del(session); nghttp2_bufs_free(&bufs); } void test_nghttp2_session_change_extpri_stream_priority(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_bufs bufs; nghttp2_buf *buf; nghttp2_ssize rv; nghttp2_option *option; nghttp2_extension frame; nghttp2_ext_priority_update priority_update; nghttp2_extpri extpri, nextpri; nghttp2_stream *stream; static const uint8_t field_value[] = "u=2"; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); frame_pack_bufs_init(&bufs); nghttp2_option_new(&option); nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_PRIORITY_UPDATE); nghttp2_session_server_new2(&session, &callbacks, NULL, option); session->pending_no_rfc7540_priorities = 1; open_recv_stream(session, 1); extpri.urgency = NGHTTP2_EXTPRI_URGENCY_LOW + 1; extpri.inc = 1; rv = nghttp2_session_change_extpri_stream_priority( session, 1, &extpri, /* ignore_client_signal = */ 0); assert_ptrdiff(0, ==, rv); stream = nghttp2_session_get_stream(session, 1); assert_uint32(NGHTTP2_EXTPRI_URGENCY_LOW, ==, nghttp2_extpri_uint8_urgency(stream->extpri)); assert_true(nghttp2_extpri_uint8_inc(stream->extpri)); rv = nghttp2_session_get_extpri_stream_priority(session, &nextpri, 1); assert_ptrdiff(0, ==, rv); assert_uint32(NGHTTP2_EXTPRI_URGENCY_LOW, ==, nextpri.urgency); assert_true(nextpri.inc); /* Client can still update stream priority. */ frame.payload = &priority_update; nghttp2_frame_priority_update_init(&frame, 1, (uint8_t *)field_value, sizeof(field_value) - 1); nghttp2_frame_pack_priority_update(&bufs, &frame); buf = &bufs.head->buf; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf), ==, rv); assert_uint8(2, ==, stream->extpri); /* Start to ignore client priority signal for this stream. */ rv = nghttp2_session_change_extpri_stream_priority( session, 1, &extpri, /* ignore_client_signal = */ 1); assert_ptrdiff(0, ==, rv); stream = nghttp2_session_get_stream(session, 1); assert_uint32(NGHTTP2_EXTPRI_URGENCY_LOW, ==, nghttp2_extpri_uint8_urgency(stream->extpri)); assert_true(nghttp2_extpri_uint8_inc(stream->extpri)); buf = &bufs.head->buf; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf), ==, rv); assert_uint32(NGHTTP2_EXTPRI_URGENCY_LOW, ==, nghttp2_extpri_uint8_urgency(stream->extpri)); assert_true(nghttp2_extpri_uint8_inc(stream->extpri)); nghttp2_session_del(session); nghttp2_option_del(option); nghttp2_bufs_free(&bufs); } void test_nghttp2_session_set_local_window_size(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_outbound_item *item; nghttp2_stream *stream; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_client_new(&session, &callbacks, NULL); stream = open_sent_stream(session, 1); stream->recv_window_size = 4096; assert_int(0, ==, nghttp2_session_set_local_window_size(session, NGHTTP2_FLAG_NONE, 1, 65536)); assert_int32(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1, ==, stream->local_window_size); assert_int32(4096, ==, stream->recv_window_size); assert_int32(65536 - 4096, ==, nghttp2_session_get_stream_local_window_size(session, 1)); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_WINDOW_UPDATE, ==, item->frame.hd.type); assert_int32(1, ==, item->frame.window_update.hd.stream_id); assert_int32(1, ==, item->frame.window_update.window_size_increment); assert_int(0, ==, nghttp2_session_send(session)); /* Go decrement part */ assert_int(0, ==, nghttp2_session_set_local_window_size(session, NGHTTP2_FLAG_NONE, 1, 32768)); assert_int32(32768, ==, stream->local_window_size); assert_int32(-28672, ==, stream->recv_window_size); assert_int32(32768, ==, stream->recv_reduction); assert_int32(65536 - 4096, ==, nghttp2_session_get_stream_local_window_size(session, 1)); item = nghttp2_session_get_next_ob_item(session); assert_null(item); /* Increase local window size */ assert_int(0, ==, nghttp2_session_set_local_window_size(session, NGHTTP2_FLAG_NONE, 1, 49152)); assert_int32(49152, ==, stream->local_window_size); assert_int32(-12288, ==, stream->recv_window_size); assert_int32(16384, ==, stream->recv_reduction); assert_int32(65536 - 4096, ==, nghttp2_session_get_stream_local_window_size(session, 1)); assert_null(nghttp2_session_get_next_ob_item(session)); /* Increase local window again */ assert_int(0, ==, nghttp2_session_set_local_window_size(session, NGHTTP2_FLAG_NONE, 1, 65537)); assert_int32(65537, ==, stream->local_window_size); assert_int32(4096, ==, stream->recv_window_size); assert_int32(0, ==, stream->recv_reduction); assert_int32(65537 - 4096, ==, nghttp2_session_get_stream_local_window_size(session, 1)); item = nghttp2_session_get_next_ob_item(session); assert_int32(1, ==, item->frame.window_update.window_size_increment); assert_int(0, ==, nghttp2_session_send(session)); /* Check connection-level flow control */ session->recv_window_size = 4096; assert_int(0, ==, nghttp2_session_set_local_window_size(session, NGHTTP2_FLAG_NONE, 0, 65536)); assert_int32(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1, ==, session->local_window_size); assert_int32(4096, ==, session->recv_window_size); assert_int32(65536 - 4096, ==, nghttp2_session_get_local_window_size(session)); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_WINDOW_UPDATE, ==, item->frame.hd.type); assert_int32(0, ==, item->frame.window_update.hd.stream_id); assert_int32(1, ==, item->frame.window_update.window_size_increment); assert_int(0, ==, nghttp2_session_send(session)); /* Go decrement part */ assert_int(0, ==, nghttp2_session_set_local_window_size(session, NGHTTP2_FLAG_NONE, 0, 32768)); assert_int32(32768, ==, session->local_window_size); assert_int32(-28672, ==, session->recv_window_size); assert_int32(32768, ==, session->recv_reduction); assert_int32(65536 - 4096, ==, nghttp2_session_get_local_window_size(session)); item = nghttp2_session_get_next_ob_item(session); assert_null(item); /* Increase local window size */ assert_int(0, ==, nghttp2_session_set_local_window_size(session, NGHTTP2_FLAG_NONE, 0, 49152)); assert_int32(49152, ==, session->local_window_size); assert_int32(-12288, ==, session->recv_window_size); assert_int32(16384, ==, session->recv_reduction); assert_int32(65536 - 4096, ==, nghttp2_session_get_local_window_size(session)); assert_null(nghttp2_session_get_next_ob_item(session)); /* Increase local window again */ assert_int(0, ==, nghttp2_session_set_local_window_size(session, NGHTTP2_FLAG_NONE, 0, 65537)); assert_int32(65537, ==, session->local_window_size); assert_int32(4096, ==, session->recv_window_size); assert_int32(0, ==, session->recv_reduction); assert_int32(65537 - 4096, ==, nghttp2_session_get_local_window_size(session)); item = nghttp2_session_get_next_ob_item(session); assert_int32(1, ==, item->frame.window_update.window_size_increment); assert_int(0, ==, nghttp2_session_send(session)); nghttp2_session_del(session); /* Make sure that nghttp2_session_set_local_window_size submits WINDOW_UPDATE if necessary to increase stream-level window. */ nghttp2_session_client_new(&session, &callbacks, NULL); stream = open_sent_stream(session, 1); stream->recv_window_size = NGHTTP2_INITIAL_WINDOW_SIZE; assert_int( 0, ==, nghttp2_session_set_local_window_size(session, NGHTTP2_FLAG_NONE, 1, 0)); assert_int32(0, ==, stream->recv_window_size); assert_int32(0, ==, nghttp2_session_get_stream_local_window_size(session, 1)); /* This should submit WINDOW_UPDATE frame because stream-level receiving window is now full. */ assert_int(0, ==, nghttp2_session_set_local_window_size( session, NGHTTP2_FLAG_NONE, 1, NGHTTP2_INITIAL_WINDOW_SIZE)); assert_int32(0, ==, stream->recv_window_size); assert_int32(NGHTTP2_INITIAL_WINDOW_SIZE, ==, nghttp2_session_get_stream_local_window_size(session, 1)); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_WINDOW_UPDATE, ==, item->frame.hd.type); assert_int32(1, ==, item->frame.hd.stream_id); assert_int32(NGHTTP2_INITIAL_WINDOW_SIZE, ==, item->frame.window_update.window_size_increment); nghttp2_session_del(session); /* Make sure that nghttp2_session_set_local_window_size submits WINDOW_UPDATE if necessary to increase connection-level window. */ nghttp2_session_client_new(&session, &callbacks, NULL); session->recv_window_size = NGHTTP2_INITIAL_WINDOW_SIZE; assert_int( 0, ==, nghttp2_session_set_local_window_size(session, NGHTTP2_FLAG_NONE, 0, 0)); assert_int32(0, ==, session->recv_window_size); assert_int32(0, ==, nghttp2_session_get_local_window_size(session)); /* This should submit WINDOW_UPDATE frame because connection-level receiving window is now full. */ assert_int(0, ==, nghttp2_session_set_local_window_size( session, NGHTTP2_FLAG_NONE, 0, NGHTTP2_INITIAL_WINDOW_SIZE)); assert_int32(0, ==, session->recv_window_size); assert_int32(NGHTTP2_INITIAL_WINDOW_SIZE, ==, nghttp2_session_get_local_window_size(session)); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_WINDOW_UPDATE, ==, item->frame.hd.type); assert_int32(0, ==, item->frame.hd.stream_id); assert_int32(NGHTTP2_INITIAL_WINDOW_SIZE, ==, item->frame.window_update.window_size_increment); nghttp2_session_del(session); } void test_nghttp2_session_cancel_from_before_frame_send(void) { int rv; nghttp2_session *session; nghttp2_session_callbacks callbacks; my_user_data ud; nghttp2_settings_entry iv; nghttp2_data_provider2 data_prd; int32_t stream_id; nghttp2_stream *stream; memset(&callbacks, 0, sizeof(callbacks)); callbacks.before_frame_send_callback = cancel_before_frame_send_callback; callbacks.on_frame_not_send_callback = on_frame_not_send_callback; callbacks.send_callback2 = null_send_callback; nghttp2_session_client_new(&session, &callbacks, &ud); iv.settings_id = 0; iv.value = 1000000009; rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1); assert_int(0, ==, rv); ud.frame_send_cb_called = 0; ud.before_frame_send_cb_called = 0; ud.frame_not_send_cb_called = 0; rv = nghttp2_session_send(session); assert_int(0, ==, rv); assert_int(0, ==, ud.frame_send_cb_called); assert_int(1, ==, ud.before_frame_send_cb_called); assert_int(1, ==, ud.frame_not_send_cb_called); data_prd.source.ptr = NULL; data_prd.read_callback = temporal_failure_data_source_read_callback; stream_id = nghttp2_submit_request2(session, NULL, reqnv, ARRLEN(reqnv), &data_prd, NULL); assert_int32(0, <, stream_id); ud.frame_send_cb_called = 0; ud.before_frame_send_cb_called = 0; ud.frame_not_send_cb_called = 0; rv = nghttp2_session_send(session); assert_int(0, ==, rv); assert_int(0, ==, ud.frame_send_cb_called); assert_int(1, ==, ud.before_frame_send_cb_called); assert_int(1, ==, ud.frame_not_send_cb_called); stream = nghttp2_session_get_stream_raw(session, stream_id); assert_null(stream); nghttp2_session_del(session); nghttp2_session_server_new(&session, &callbacks, &ud); open_recv_stream(session, 1); stream_id = nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 1, reqnv, ARRLEN(reqnv), NULL); assert_int32(0, <, stream_id); ud.frame_send_cb_called = 0; ud.before_frame_send_cb_called = 0; ud.frame_not_send_cb_called = 0; rv = nghttp2_session_send(session); assert_int(0, ==, rv); assert_int(0, ==, ud.frame_send_cb_called); assert_int(1, ==, ud.before_frame_send_cb_called); assert_int(1, ==, ud.frame_not_send_cb_called); stream = nghttp2_session_get_stream_raw(session, stream_id); assert_null(stream); nghttp2_session_del(session); } void test_nghttp2_session_too_many_settings(void) { nghttp2_session *session; nghttp2_option *option; nghttp2_session_callbacks callbacks; nghttp2_frame frame; nghttp2_bufs bufs; nghttp2_buf *buf; nghttp2_ssize rv; my_user_data ud; nghttp2_settings_entry iv[3]; nghttp2_mem *mem; nghttp2_outbound_item *item; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_frame_recv_callback = on_frame_recv_callback; callbacks.send_callback2 = null_send_callback; nghttp2_option_new(&option); nghttp2_option_set_max_settings(option, 1); nghttp2_session_client_new2(&session, &callbacks, &ud, option); assert_size(1, ==, session->max_settings); nghttp2_option_del(option); iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[0].value = 3000; iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; iv[1].value = 16384; nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 2), 2); rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); assert_ptrdiff(0, ==, rv); assert_size(0, <, nghttp2_bufs_len(&bufs)); nghttp2_frame_settings_free(&frame.settings, mem); buf = &bufs.head->buf; assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf), ==, rv); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); nghttp2_bufs_reset(&bufs); nghttp2_bufs_free(&bufs); nghttp2_session_del(session); } static void prepare_session_removed_closed_stream(nghttp2_session *session, nghttp2_hd_deflater *deflater) { int rv; nghttp2_settings_entry iv; nghttp2_bufs bufs; nghttp2_mem *mem; nghttp2_ssize nread; int i; nghttp2_stream *stream; nghttp2_frame_hd hd; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); iv.settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; iv.value = 2; rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1); assert_int(0, ==, rv); rv = nghttp2_session_send(session); assert_int(0, ==, rv); for (i = 1; i <= 3; i += 2) { rv = pack_headers(&bufs, deflater, i, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, reqnv, ARRLEN(reqnv), mem); assert_int(0, ==, rv); nread = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, nread); nghttp2_bufs_reset(&bufs); } nghttp2_session_close_stream(session, 3, NGHTTP2_NO_ERROR); rv = pack_headers(&bufs, deflater, 5, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, reqnv, ARRLEN(reqnv), mem); assert_int(0, ==, rv); /* Receiving stream 5 will erase stream 3 from closed stream list */ nread = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, nread); stream = nghttp2_session_get_stream_raw(session, 3); assert_null(stream); /* Since the current max concurrent streams is NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS, receiving frame on stream 3 is ignored. */ nghttp2_bufs_reset(&bufs); rv = pack_headers(&bufs, deflater, 3, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, trailernv, ARRLEN(trailernv), mem); assert_int(0, ==, rv); nread = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, nread); assert_null(nghttp2_session_get_next_ob_item(session)); nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_NONE, 3); nghttp2_bufs_reset(&bufs); nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); bufs.head->buf.last += NGHTTP2_FRAME_HDLEN; nread = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, nread); assert_null(nghttp2_session_get_next_ob_item(session)); /* Now server receives SETTINGS ACK */ nghttp2_frame_hd_init(&hd, 0, NGHTTP2_SETTINGS, NGHTTP2_FLAG_ACK, 0); nghttp2_bufs_reset(&bufs); nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); bufs.head->buf.last += NGHTTP2_FRAME_HDLEN; nread = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, nread); nghttp2_bufs_free(&bufs); } void test_nghttp2_session_removed_closed_stream(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; int rv; nghttp2_hd_deflater deflater; nghttp2_bufs bufs; nghttp2_mem *mem; nghttp2_ssize nread; nghttp2_frame_hd hd; nghttp2_outbound_item *item; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_server_new(&session, &callbacks, NULL); /* Now local max concurrent streams is still unlimited, pending max concurrent streams is now 2. */ nghttp2_hd_deflate_init(&deflater, mem); prepare_session_removed_closed_stream(session, &deflater); /* Now current max concurrent streams is 2. Receiving frame on stream 3 is ignored because we have no stream object for stream 3. */ nghttp2_bufs_reset(&bufs); rv = pack_headers(&bufs, &deflater, 3, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, trailernv, ARRLEN(trailernv), mem); assert_int(0, ==, rv); nread = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, nread); item = nghttp2_session_get_next_ob_item(session); assert_null(item); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); nghttp2_session_server_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); /* Same setup, and then receive DATA instead of HEADERS */ prepare_session_removed_closed_stream(session, &deflater); nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_NONE, 3); nghttp2_bufs_reset(&bufs); nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); bufs.head->buf.last += NGHTTP2_FRAME_HDLEN; nread = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_bufs_len(&bufs)); assert_ptrdiff((nghttp2_ssize)nghttp2_bufs_len(&bufs), ==, nread); item = nghttp2_session_get_next_ob_item(session); assert_null(item); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); nghttp2_bufs_free(&bufs); } static nghttp2_ssize pause_once_data_source_read_callback( nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len, uint32_t *data_flags, nghttp2_data_source *source, void *user_data) { my_user_data *ud = user_data; if (ud->data_source_read_cb_paused == 0) { ++ud->data_source_read_cb_paused; return NGHTTP2_ERR_PAUSE; } return fixed_length_data_source_read_callback(session, stream_id, buf, len, data_flags, source, user_data); } void test_nghttp2_session_pause_data(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_data_provider2 data_prd; my_user_data ud; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_frame_send_callback = on_frame_send_callback; data_prd.read_callback = pause_once_data_source_read_callback; ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN; nghttp2_session_server_new(&session, &callbacks, &ud); open_recv_stream(session, 1); assert_int( 0, ==, nghttp2_submit_data2(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd)); ud.frame_send_cb_called = 0; ud.data_source_read_cb_paused = 0; assert_int(0, ==, nghttp2_session_send(session)); assert_int(0, ==, ud.frame_send_cb_called); assert_null(session->aob.item); assert_int(0, ==, nghttp2_session_send(session)); assert_int(1, ==, ud.frame_send_cb_called); assert_uint8(NGHTTP2_DATA, ==, ud.sent_frame_type); assert_null(nghttp2_session_get_next_ob_item(session)); nghttp2_session_del(session); } void test_nghttp2_session_no_closed_streams(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_option *option; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); nghttp2_option_new(&option); nghttp2_option_set_no_closed_streams(option, 1); nghttp2_session_server_new2(&session, &callbacks, NULL, option); open_recv_stream(session, 1); nghttp2_session_close_stream(session, 1, NGHTTP2_NO_ERROR); assert_size(0, ==, session->num_closed_streams); nghttp2_session_del(session); nghttp2_option_del(option); } void test_nghttp2_session_set_stream_user_data(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; int32_t stream_id; int user_data1, user_data2; int rv; const uint8_t *datap; nghttp2_ssize datalen; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); nghttp2_session_client_new(&session, &callbacks, NULL); stream_id = nghttp2_submit_request2(session, NULL, reqnv, ARRLEN(reqnv), NULL, &user_data1); rv = nghttp2_session_set_stream_user_data(session, stream_id, &user_data2); assert_int(0, ==, rv); datalen = nghttp2_session_mem_send2(session, &datap); assert_ptrdiff(0, <, datalen); assert_ptr_equal(&user_data2, nghttp2_session_get_stream_user_data(session, stream_id)); assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, nghttp2_session_set_stream_user_data(session, 2, NULL)); nghttp2_session_del(session); } void test_nghttp2_session_no_rfc7540_priorities(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_data_provider2 data_prd; my_user_data ud; nghttp2_outbound_item *item; nghttp2_mem *mem; nghttp2_settings_entry iv; nghttp2_priority_spec pri_spec; mem = nghttp2_mem_default(); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; /* Do not use a dependency tree if SETTINGS_NO_RFC7540_PRIORITIES = 1. */ data_prd.read_callback = fixed_length_data_source_read_callback; ud.data_source_length = 128 * 1024; assert_int(0, ==, nghttp2_session_server_new(&session, &callbacks, &ud)); iv.settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES; iv.value = 1; assert_int(0, ==, nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1)); assert_int(0, ==, nghttp2_session_send(session)); open_recv_stream2(session, 1, NGHTTP2_STREAM_OPENING); assert_int( 0, ==, nghttp2_submit_response2(session, 1, resnv, ARRLEN(resnv), &data_prd)); item = nghttp2_session_get_next_ob_item(session); assert_size(ARRLEN(resnv), ==, item->frame.headers.nvlen); assert_nv_equal(resnv, item->frame.headers.nva, item->frame.headers.nvlen, mem); assert_int(0, ==, nghttp2_session_send(session)); assert_size( 1, ==, nghttp2_pq_size(&session->sched[NGHTTP2_EXTPRI_DEFAULT_URGENCY].ob_data)); nghttp2_session_del(session); /* Priorities are defaulted */ assert_int(0, ==, nghttp2_session_client_new(&session, &callbacks, NULL)); iv.settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES; iv.value = 1; assert_int(0, ==, nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1)); session->remote_settings.no_rfc7540_priorities = 1; pri_spec.stream_id = 5; pri_spec.weight = 111; pri_spec.exclusive = 1; assert_int32(1, ==, nghttp2_submit_request2(session, &pri_spec, reqnv, ARRLEN(reqnv), NULL, NULL)); item = nghttp2_outbound_queue_top(&session->ob_syn); assert_not_null(item); assert_uint8(NGHTTP2_HEADERS, ==, item->frame.hd.type); assert_true( nghttp2_priority_spec_check_default(&item->frame.headers.pri_spec)); nghttp2_session_del(session); } void test_nghttp2_session_stream_reset_ratelim(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_frame frame; nghttp2_ssize rv; nghttp2_bufs bufs; nghttp2_buf *buf; nghttp2_mem *mem; size_t i; nghttp2_hd_deflater deflater; size_t nvlen; nghttp2_nv *nva; int32_t stream_id; nghttp2_outbound_item *item; nghttp2_option *option; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_option_new(&option); nghttp2_option_set_stream_reset_rate_limit( option, NGHTTP2_DEFAULT_STREAM_RESET_BURST, 0); nghttp2_session_server_new2(&session, &callbacks, NULL, option); nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, NULL, 0); rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); assert_ptrdiff(0, ==, rv); nghttp2_frame_settings_free(&frame.settings, mem); buf = &bufs.head->buf; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf), ==, rv); /* Send SETTINGS ACK */ rv = nghttp2_session_send(session); assert_ptrdiff(0, ==, rv); nghttp2_hd_deflate_init(&deflater, mem); for (i = 0; i < NGHTTP2_DEFAULT_STREAM_RESET_BURST + 2; ++i) { stream_id = (int32_t)(i * 2 + 1); nghttp2_bufs_reset(&bufs); /* HEADERS */ nvlen = ARRLEN(reqnv); nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, stream_id, NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); assert_ptrdiff(0, ==, rv); nghttp2_frame_headers_free(&frame.headers, mem); buf = &bufs.head->buf; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf), ==, rv); nghttp2_bufs_reset(&bufs); /* RST_STREAM */ nghttp2_frame_rst_stream_init(&frame.rst_stream, stream_id, NGHTTP2_NO_ERROR); nghttp2_frame_pack_rst_stream(&bufs, &frame.rst_stream); nghttp2_frame_rst_stream_free(&frame.rst_stream); buf = &bufs.head->buf; rv = nghttp2_session_mem_recv2(session, buf->pos, nghttp2_buf_len(buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(buf), ==, rv); if (i < NGHTTP2_DEFAULT_STREAM_RESET_BURST) { assert_size(0, ==, nghttp2_outbound_queue_size(&session->ob_reg)); continue; } assert_size(1, ==, nghttp2_outbound_queue_size(&session->ob_reg)); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_int32(NGHTTP2_DEFAULT_STREAM_RESET_BURST * 2 + 1, ==, item->frame.goaway.last_stream_id); } nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); nghttp2_bufs_free(&bufs); nghttp2_option_del(option); } typedef struct check_http_opts { int server; int connect_protocol; } check_http_opts; static void check_http_opts_reset(check_http_opts *opts) { memset(opts, 0, sizeof(*opts)); } static void check_nghttp2_http_recv_headers_fail(check_http_opts opts, int stream_state, const nghttp2_nv *nva, size_t nvlen) { nghttp2_mem *mem; nghttp2_ssize rv; nghttp2_outbound_item *item; nghttp2_bufs bufs; my_user_data ud; nghttp2_session *session; nghttp2_hd_deflater deflater; nghttp2_session_callbacks callbacks; int32_t stream_id = 1; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_frame_recv_callback = on_frame_recv_callback; callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; if (opts.server) { nghttp2_session_server_new(&session, &callbacks, &ud); } else { nghttp2_session_client_new(&session, &callbacks, &ud); } if (opts.connect_protocol) { session->pending_enable_connect_protocol = 1; } nghttp2_hd_deflate_init(&deflater, mem); if (stream_state != -1) { if (nghttp2_session_is_my_stream_id(session, stream_id)) { open_sent_stream2(session, stream_id, (nghttp2_stream_state)stream_state); } else { open_recv_stream2(session, stream_id, (nghttp2_stream_state)stream_state); } } rv = pack_headers(&bufs, &deflater, stream_id, NGHTTP2_FLAG_END_HEADERS, nva, nvlen, mem); assert_ptrdiff(0, ==, rv); ud.invalid_frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_int(0, ==, nghttp2_session_send(session)); nghttp2_bufs_free(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); } static void check_nghttp2_http_recv_headers_ok(check_http_opts opts, int stream_state, const nghttp2_nv *nva, size_t nvlen) { nghttp2_mem *mem; nghttp2_ssize rv; nghttp2_bufs bufs; my_user_data ud; nghttp2_session *session; nghttp2_hd_deflater deflater; nghttp2_session_callbacks callbacks; int32_t stream_id = 1; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_frame_recv_callback = on_frame_recv_callback; callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; if (opts.server) { nghttp2_session_server_new(&session, &callbacks, &ud); } else { nghttp2_session_client_new(&session, &callbacks, &ud); } if (opts.connect_protocol) { session->pending_enable_connect_protocol = 1; } nghttp2_hd_deflate_init(&deflater, mem); if (stream_state != -1) { if (nghttp2_session_is_my_stream_id(session, stream_id)) { open_sent_stream2(session, stream_id, (nghttp2_stream_state)stream_state); } else { open_recv_stream2(session, stream_id, (nghttp2_stream_state)stream_state); } } rv = pack_headers(&bufs, &deflater, stream_id, NGHTTP2_FLAG_END_HEADERS, nva, nvlen, mem); assert_ptrdiff(0, ==, rv); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); assert_null(nghttp2_session_get_next_ob_item(session)); assert_int(1, ==, ud.frame_recv_cb_called); nghttp2_bufs_free(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); } void test_nghttp2_http_mandatory_headers(void) { /* test case for response */ const nghttp2_nv nostatus_resnv[] = {MAKE_NV("server", "foo")}; const nghttp2_nv dupstatus_resnv[] = {MAKE_NV(":status", "200"), MAKE_NV(":status", "200")}; const nghttp2_nv badpseudo_resnv[] = {MAKE_NV(":status", "200"), MAKE_NV(":scheme", "https")}; const nghttp2_nv latepseudo_resnv[] = {MAKE_NV("server", "foo"), MAKE_NV(":status", "200")}; const nghttp2_nv badstatus_resnv[] = {MAKE_NV(":status", "2000")}; const nghttp2_nv badcl_resnv[] = {MAKE_NV(":status", "200"), MAKE_NV("content-length", "-1")}; const nghttp2_nv dupcl_resnv[] = {MAKE_NV(":status", "200"), MAKE_NV("content-length", "0"), MAKE_NV("content-length", "0")}; const nghttp2_nv badhd_resnv[] = {MAKE_NV(":status", "200"), MAKE_NV("connection", "close")}; const nghttp2_nv cl1xx_resnv[] = {MAKE_NV(":status", "100"), MAKE_NV("content-length", "0")}; const nghttp2_nv cl204_resnv[] = {MAKE_NV(":status", "204"), MAKE_NV("content-length", "0")}; const nghttp2_nv clnonzero204_resnv[] = {MAKE_NV(":status", "204"), MAKE_NV("content-length", "100")}; const nghttp2_nv status101_resnv[] = {MAKE_NV(":status", "101")}; const nghttp2_nv unexpectedhost_resnv[] = {MAKE_NV(":status", "200"), MAKE_NV("host", "/localhost")}; /* test case for request */ const nghttp2_nv nopath_reqnv[] = {MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"), MAKE_NV(":authority", "localhost")}; const nghttp2_nv earlyconnect_reqnv[] = { MAKE_NV(":method", "CONNECT"), MAKE_NV(":scheme", "https"), MAKE_NV(":path", "/"), MAKE_NV(":authority", "localhost")}; const nghttp2_nv lateconnect_reqnv[] = { MAKE_NV(":scheme", "https"), MAKE_NV(":path", "/"), MAKE_NV(":method", "CONNECT"), MAKE_NV(":authority", "localhost")}; const nghttp2_nv duppath_reqnv[] = { MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"), MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"), MAKE_NV(":path", "/")}; const nghttp2_nv badcl_reqnv[] = { MAKE_NV(":scheme", "https"), MAKE_NV(":method", "POST"), MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"), MAKE_NV("content-length", "-1")}; const nghttp2_nv dupcl_reqnv[] = { MAKE_NV(":scheme", "https"), MAKE_NV(":method", "POST"), MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"), MAKE_NV("content-length", "0"), MAKE_NV("content-length", "0")}; const nghttp2_nv badhd_reqnv[] = { MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"), MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"), MAKE_NV("connection", "close")}; const nghttp2_nv badauthority_reqnv[] = { MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"), MAKE_NV(":authority", "\x0d\x0alocalhost"), MAKE_NV(":path", "/")}; const nghttp2_nv badhdbtw_reqnv[] = { MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"), MAKE_NV("foo", "\x0d\x0a"), MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/")}; const nghttp2_nv asteriskget1_reqnv[] = { MAKE_NV(":path", "*"), MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"), MAKE_NV(":method", "GET")}; const nghttp2_nv asteriskget2_reqnv[] = { MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"), MAKE_NV(":method", "GET"), MAKE_NV(":path", "*")}; const nghttp2_nv asteriskoptions1_reqnv[] = { MAKE_NV(":path", "*"), MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"), MAKE_NV(":method", "OPTIONS")}; const nghttp2_nv asteriskoptions2_reqnv[] = { MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"), MAKE_NV(":method", "OPTIONS"), MAKE_NV(":path", "*")}; const nghttp2_nv connectproto_reqnv[] = { MAKE_NV(":scheme", "https"), MAKE_NV(":path", "/"), MAKE_NV(":method", "CONNECT"), MAKE_NV(":authority", "localhost"), MAKE_NV(":protocol", "websocket")}; const nghttp2_nv connectprotoget_reqnv[] = { MAKE_NV(":scheme", "https"), MAKE_NV(":path", "/"), MAKE_NV(":method", "GET"), MAKE_NV(":authority", "localhost"), MAKE_NV(":protocol", "websocket")}; const nghttp2_nv connectprotonopath_reqnv[] = { MAKE_NV(":scheme", "https"), MAKE_NV(":method", "CONNECT"), MAKE_NV(":authority", "localhost"), MAKE_NV(":protocol", "websocket")}; const nghttp2_nv connectprotonoauth_reqnv[] = { MAKE_NV(":scheme", "http"), MAKE_NV(":path", "/"), MAKE_NV(":method", "CONNECT"), MAKE_NV("host", "localhost"), MAKE_NV(":protocol", "websocket")}; const nghttp2_nv regularconnect_reqnv[] = { MAKE_NV(":method", "CONNECT"), MAKE_NV(":authority", "localhost")}; check_http_opts opts; check_http_opts_reset(&opts); /* response header lacks :status */ check_nghttp2_http_recv_headers_fail(opts, NGHTTP2_STREAM_OPENING, nostatus_resnv, ARRLEN(nostatus_resnv)); /* response header has 2 :status */ check_nghttp2_http_recv_headers_fail( opts, NGHTTP2_STREAM_OPENING, dupstatus_resnv, ARRLEN(dupstatus_resnv)); /* response header has bad pseudo header :scheme */ check_nghttp2_http_recv_headers_fail( opts, NGHTTP2_STREAM_OPENING, badpseudo_resnv, ARRLEN(badpseudo_resnv)); /* response header has :status after regular header field */ check_nghttp2_http_recv_headers_fail( opts, NGHTTP2_STREAM_OPENING, latepseudo_resnv, ARRLEN(latepseudo_resnv)); /* response header has bad status code */ check_nghttp2_http_recv_headers_fail( opts, NGHTTP2_STREAM_OPENING, badstatus_resnv, ARRLEN(badstatus_resnv)); /* response header has bad content-length */ check_nghttp2_http_recv_headers_fail(opts, NGHTTP2_STREAM_OPENING, badcl_resnv, ARRLEN(badcl_resnv)); /* response header has multiple content-length */ check_nghttp2_http_recv_headers_fail(opts, NGHTTP2_STREAM_OPENING, dupcl_resnv, ARRLEN(dupcl_resnv)); /* response header has disallowed header field */ check_nghttp2_http_recv_headers_fail(opts, NGHTTP2_STREAM_OPENING, badhd_resnv, ARRLEN(badhd_resnv)); /* response header has content-length with 100 status code */ check_nghttp2_http_recv_headers_fail(opts, NGHTTP2_STREAM_OPENING, cl1xx_resnv, ARRLEN(cl1xx_resnv)); /* response header has 0 content-length with 204 status code */ check_nghttp2_http_recv_headers_ok(opts, NGHTTP2_STREAM_OPENING, cl204_resnv, ARRLEN(cl204_resnv)); /* response header has nonzero content-length with 204 status code */ check_nghttp2_http_recv_headers_fail(opts, NGHTTP2_STREAM_OPENING, clnonzero204_resnv, ARRLEN(clnonzero204_resnv)); /* status code 101 should not be used in HTTP/2 because it is used for HTTP Upgrade which HTTP/2 removes. */ check_nghttp2_http_recv_headers_fail( opts, NGHTTP2_STREAM_OPENING, status101_resnv, ARRLEN(status101_resnv)); /* Specific characters check for host field in response header should not be done as its use is undefined. */ check_nghttp2_http_recv_headers_ok(opts, NGHTTP2_STREAM_OPENING, unexpectedhost_resnv, ARRLEN(unexpectedhost_resnv)); /* check server side */ check_http_opts_reset(&opts); opts.server = 1; /* request header has no :path */ check_nghttp2_http_recv_headers_fail(opts, -1, nopath_reqnv, ARRLEN(nopath_reqnv)); /* request header has CONNECT method, but followed by :path */ check_nghttp2_http_recv_headers_fail(opts, -1, earlyconnect_reqnv, ARRLEN(earlyconnect_reqnv)); /* request header has CONNECT method following :path */ check_nghttp2_http_recv_headers_fail(opts, -1, lateconnect_reqnv, ARRLEN(lateconnect_reqnv)); /* request header has multiple :path */ check_nghttp2_http_recv_headers_fail(opts, -1, duppath_reqnv, ARRLEN(duppath_reqnv)); /* request header has bad content-length */ check_nghttp2_http_recv_headers_fail(opts, -1, badcl_reqnv, ARRLEN(badcl_reqnv)); /* request header has multiple content-length */ check_nghttp2_http_recv_headers_fail(opts, -1, dupcl_reqnv, ARRLEN(dupcl_reqnv)); /* request header has disallowed header field */ check_nghttp2_http_recv_headers_fail(opts, -1, badhd_reqnv, ARRLEN(badhd_reqnv)); /* request header has :authority header field containing illegal characters */ check_nghttp2_http_recv_headers_fail(opts, -1, badauthority_reqnv, ARRLEN(badauthority_reqnv)); /* request header has regular header field containing illegal character before all mandatory header fields are seen. */ check_nghttp2_http_recv_headers_fail(opts, -1, badhdbtw_reqnv, ARRLEN(badhdbtw_reqnv)); /* request header has "*" in :path header field while method is GET. :path is received before :method */ check_nghttp2_http_recv_headers_fail(opts, -1, asteriskget1_reqnv, ARRLEN(asteriskget1_reqnv)); /* request header has "*" in :path header field while method is GET. :method is received before :path */ check_nghttp2_http_recv_headers_fail(opts, -1, asteriskget2_reqnv, ARRLEN(asteriskget2_reqnv)); /* OPTIONS method can include "*" in :path header field. :path is received before :method. */ check_nghttp2_http_recv_headers_ok(opts, -1, asteriskoptions1_reqnv, ARRLEN(asteriskoptions1_reqnv)); /* OPTIONS method can include "*" in :path header field. :method is received before :path. */ check_nghttp2_http_recv_headers_ok(opts, -1, asteriskoptions2_reqnv, ARRLEN(asteriskoptions2_reqnv)); /* :protocol is not allowed unless it is enabled by the local endpoint. */ check_nghttp2_http_recv_headers_fail(opts, -1, connectproto_reqnv, ARRLEN(connectproto_reqnv)); /* enable SETTINGS_CONNECT_PROTOCOL */ check_http_opts_reset(&opts); opts.server = 1; opts.connect_protocol = 1; /* :protocol is allowed if SETTINGS_CONNECT_PROTOCOL is enabled by the local endpoint. */ check_nghttp2_http_recv_headers_ok(opts, -1, connectproto_reqnv, ARRLEN(connectproto_reqnv)); /* :protocol is only allowed with CONNECT method. */ check_nghttp2_http_recv_headers_fail(opts, -1, connectprotoget_reqnv, ARRLEN(connectprotoget_reqnv)); /* CONNECT method with :protocol requires :path. */ check_nghttp2_http_recv_headers_fail(opts, -1, connectprotonopath_reqnv, ARRLEN(connectprotonopath_reqnv)); /* CONNECT method with :protocol requires :authority. */ check_nghttp2_http_recv_headers_fail(opts, -1, connectprotonoauth_reqnv, ARRLEN(connectprotonoauth_reqnv)); /* regular CONNECT method should succeed with SETTINGS_CONNECT_PROTOCOL */ check_nghttp2_http_recv_headers_ok(opts, -1, regularconnect_reqnv, ARRLEN(regularconnect_reqnv)); } void test_nghttp2_http_content_length(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_hd_deflater deflater; nghttp2_mem *mem; nghttp2_bufs bufs; nghttp2_ssize rv; nghttp2_stream *stream; const nghttp2_nv cl_resnv[] = {MAKE_NV(":status", "200"), MAKE_NV("te", "trailers"), MAKE_NV("content-length", "9000000000")}; const nghttp2_nv cl_reqnv[] = { MAKE_NV(":path", "/"), MAKE_NV(":method", "PUT"), MAKE_NV(":scheme", "https"), MAKE_NV("te", "trailers"), MAKE_NV("host", "localhost"), MAKE_NV("content-length", "9000000000")}; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_client_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); stream = open_sent_stream2(session, 1, NGHTTP2_STREAM_OPENING); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, cl_resnv, ARRLEN(cl_resnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); assert_null(nghttp2_session_get_next_ob_item(session)); assert_int64(9000000000LL, ==, stream->content_length); assert_int16(200, ==, stream->status_code); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); nghttp2_bufs_reset(&bufs); /* check server side */ nghttp2_session_server_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, cl_reqnv, ARRLEN(cl_reqnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); stream = nghttp2_session_get_stream(session, 1); assert_null(nghttp2_session_get_next_ob_item(session)); assert_int64(9000000000LL, ==, stream->content_length); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); nghttp2_bufs_free(&bufs); } void test_nghttp2_http_content_length_mismatch(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_hd_deflater deflater; nghttp2_mem *mem; nghttp2_bufs bufs; nghttp2_ssize rv; const nghttp2_nv cl_reqnv[] = { MAKE_NV(":path", "/"), MAKE_NV(":method", "PUT"), MAKE_NV(":authority", "localhost"), MAKE_NV(":scheme", "https"), MAKE_NV("content-length", "20")}; const nghttp2_nv cl_resnv[] = {MAKE_NV(":status", "200"), MAKE_NV("content-length", "20")}; nghttp2_outbound_item *item; nghttp2_frame_hd hd; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_server_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); /* header says content-length: 20, but HEADERS has END_STREAM flag set */ rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, cl_reqnv, ARRLEN(cl_reqnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_int(0, ==, nghttp2_session_send(session)); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); /* header says content-length: 20, but DATA has 0 byte */ nghttp2_session_server_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, cl_reqnv, ARRLEN(cl_reqnv), mem); assert_ptrdiff(0, ==, rv); nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 1); nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); bufs.head->buf.last += NGHTTP2_FRAME_HDLEN; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_int(0, ==, nghttp2_session_send(session)); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); /* header says content-length: 20, but DATA has 21 bytes */ nghttp2_session_server_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, cl_reqnv, ARRLEN(cl_reqnv), mem); assert_ptrdiff(0, ==, rv); nghttp2_frame_hd_init(&hd, 21, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 1); nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); bufs.head->buf.last += NGHTTP2_FRAME_HDLEN + 21; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_int(0, ==, nghttp2_session_send(session)); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); /* Check for client */ nghttp2_session_client_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); /* header says content-length: 20, but HEADERS has END_STREAM flag set */ nghttp2_submit_request2(session, NULL, reqnv, ARRLEN(reqnv), NULL, NULL); assert_int(0, ==, nghttp2_session_send(session)); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, cl_resnv, ARRLEN(cl_resnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_not_null(nghttp2_session_get_stream(session, 1)); assert_int(0, ==, nghttp2_session_send(session)); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); /* header says content-length: 20, but DATA has 0 byte */ nghttp2_session_client_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); nghttp2_submit_request2(session, NULL, reqnv, ARRLEN(reqnv), NULL, NULL); assert_int(0, ==, nghttp2_session_send(session)); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, cl_resnv, ARRLEN(cl_resnv), mem); assert_ptrdiff(0, ==, rv); nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 1); nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); bufs.head->buf.last += NGHTTP2_FRAME_HDLEN; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_not_null(nghttp2_session_get_stream(session, 1)); assert_int(0, ==, nghttp2_session_send(session)); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); /* header says content-length: 20, but DATA has 21 bytes */ nghttp2_session_client_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); nghttp2_submit_request2(session, NULL, reqnv, ARRLEN(reqnv), NULL, NULL); assert_int(0, ==, nghttp2_session_send(session)); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, cl_resnv, ARRLEN(cl_resnv), mem); assert_ptrdiff(0, ==, rv); nghttp2_frame_hd_init(&hd, 21, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 1); nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); bufs.head->buf.last += NGHTTP2_FRAME_HDLEN + 21; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_not_null(nghttp2_session_get_stream(session, 1)); assert_int(0, ==, nghttp2_session_send(session)); nghttp2_bufs_reset(&bufs); nghttp2_bufs_free(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); } void test_nghttp2_http_non_final_response(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_hd_deflater deflater; nghttp2_mem *mem; nghttp2_bufs bufs; nghttp2_ssize rv; const nghttp2_nv nonfinal_resnv[] = { MAKE_NV(":status", "100"), }; nghttp2_outbound_item *item; nghttp2_frame_hd hd; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_client_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); /* non-final HEADERS with END_STREAM is illegal */ open_sent_stream2(session, 1, NGHTTP2_STREAM_OPENING); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, nonfinal_resnv, ARRLEN(nonfinal_resnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_int(0, ==, nghttp2_session_send(session)); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); /* non-final HEADERS followed by non-empty DATA is illegal */ nghttp2_session_client_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); open_sent_stream2(session, 1, NGHTTP2_STREAM_OPENING); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, nonfinal_resnv, ARRLEN(nonfinal_resnv), mem); assert_ptrdiff(0, ==, rv); nghttp2_frame_hd_init(&hd, 10, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 1); nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); bufs.head->buf.last += NGHTTP2_FRAME_HDLEN + 10; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_int(0, ==, nghttp2_session_send(session)); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); /* non-final HEADERS followed by empty DATA (without END_STREAM) is ok */ nghttp2_session_client_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); open_sent_stream2(session, 1, NGHTTP2_STREAM_OPENING); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, nonfinal_resnv, ARRLEN(nonfinal_resnv), mem); assert_ptrdiff(0, ==, rv); nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_NONE, 1); nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); bufs.head->buf.last += NGHTTP2_FRAME_HDLEN; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); assert_null(nghttp2_session_get_next_ob_item(session)); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); /* non-final HEADERS followed by empty DATA (with END_STREAM) is illegal */ nghttp2_session_client_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); open_sent_stream2(session, 1, NGHTTP2_STREAM_OPENING); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, nonfinal_resnv, ARRLEN(nonfinal_resnv), mem); assert_ptrdiff(0, ==, rv); nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 1); nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); bufs.head->buf.last += NGHTTP2_FRAME_HDLEN; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_int(0, ==, nghttp2_session_send(session)); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); /* non-final HEADERS followed by final HEADERS is OK */ nghttp2_session_client_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); open_sent_stream2(session, 1, NGHTTP2_STREAM_OPENING); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, nonfinal_resnv, ARRLEN(nonfinal_resnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); nghttp2_bufs_reset(&bufs); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, resnv, ARRLEN(resnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); assert_null(nghttp2_session_get_next_ob_item(session)); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); nghttp2_bufs_free(&bufs); } void test_nghttp2_http_trailer_headers(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_hd_deflater deflater; nghttp2_mem *mem; nghttp2_bufs bufs; nghttp2_ssize rv; const nghttp2_nv trailer_reqnv[] = { MAKE_NV("foo", "bar"), }; nghttp2_outbound_item *item; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_server_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); /* good trailer header */ rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv, ARRLEN(reqnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); nghttp2_bufs_reset(&bufs); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, trailer_reqnv, ARRLEN(trailer_reqnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); assert_null(nghttp2_session_get_next_ob_item(session)); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); /* trailer header without END_STREAM is illegal */ nghttp2_session_server_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv, ARRLEN(reqnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); nghttp2_bufs_reset(&bufs); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, trailer_reqnv, ARRLEN(trailer_reqnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_int(0, ==, nghttp2_session_send(session)); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); /* trailer header including pseudo header field is illegal */ nghttp2_session_server_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv, ARRLEN(reqnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); nghttp2_bufs_reset(&bufs); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv, ARRLEN(reqnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); assert_int(0, ==, nghttp2_session_send(session)); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); nghttp2_bufs_free(&bufs); } void test_nghttp2_http_ignore_regular_header(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_hd_deflater deflater; nghttp2_mem *mem; nghttp2_bufs bufs; nghttp2_ssize rv; my_user_data ud; const nghttp2_nv bad_reqnv[] = { MAKE_NV(":authority", "localhost"), MAKE_NV(":scheme", "https"), MAKE_NV(":path", "/"), MAKE_NV(":method", "GET"), MAKE_NV("foo", "\x0zzz"), MAKE_NV("bar", "buzz"), }; const nghttp2_nv bad_ansnv[] = { MAKE_NV(":authority", "localhost"), MAKE_NV(":scheme", "https"), MAKE_NV(":path", "/"), MAKE_NV(":method", "GET"), MAKE_NV("bar", "buzz")}; size_t proclen; size_t i; nghttp2_outbound_item *item; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; callbacks.on_header_callback = pause_on_header_callback; nghttp2_session_server_new(&session, &callbacks, &ud); nghttp2_hd_deflate_init(&deflater, mem); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, bad_reqnv, ARRLEN(bad_reqnv), mem); assert_ptrdiff(0, ==, rv); nghttp2_hd_deflate_free(&deflater); proclen = 0; for (i = 0; i < 4; ++i) { rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos + proclen, nghttp2_buf_len(&bufs.head->buf) - proclen); assert_ptrdiff(0, <, rv); proclen += (size_t)rv; assert_true(nghttp2_nv_equal(&bad_ansnv[i], &ud.nv)); } rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos + proclen, nghttp2_buf_len(&bufs.head->buf) - proclen); assert_ptrdiff(0, <, rv); /* Without on_invalid_frame_recv_callback2, bad header is ignored. */ item = nghttp2_session_get_next_ob_item(session); assert_null(item); proclen += (size_t)rv; assert_size(nghttp2_buf_len(&bufs.head->buf), ==, proclen); nghttp2_session_del(session); /* use on_invalid_header_callback */ callbacks.on_invalid_header_callback = pause_on_invalid_header_callback; nghttp2_session_server_new(&session, &callbacks, &ud); proclen = 0; ud.invalid_header_cb_called = 0; for (i = 0; i < 4; ++i) { rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos + proclen, nghttp2_buf_len(&bufs.head->buf) - proclen); assert_ptrdiff(0, <, rv); proclen += (size_t)rv; assert_true(nghttp2_nv_equal(&bad_ansnv[i], &ud.nv)); } assert_int(0, ==, ud.invalid_header_cb_called); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos + proclen, nghttp2_buf_len(&bufs.head->buf) - proclen); assert_ptrdiff(0, <, rv); assert_int(1, ==, ud.invalid_header_cb_called); assert_true(nghttp2_nv_equal(&bad_reqnv[4], &ud.nv)); proclen += (size_t)rv; rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos + proclen, nghttp2_buf_len(&bufs.head->buf) - proclen); assert_ptrdiff(0, <, rv); assert_true(nghttp2_nv_equal(&bad_ansnv[4], &ud.nv)); nghttp2_session_del(session); /* make sure that we can reset stream from on_invalid_header_callback */ callbacks.on_header_callback = on_header_callback; callbacks.on_invalid_header_callback = reset_on_invalid_header_callback; nghttp2_session_server_new(&session, &callbacks, &ud); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_RST_STREAM, ==, item->frame.hd.type); assert_int32(1, ==, item->frame.hd.stream_id); nghttp2_session_del(session); nghttp2_bufs_free(&bufs); } void test_nghttp2_http_ignore_content_length(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_hd_deflater deflater; nghttp2_mem *mem; nghttp2_bufs bufs; nghttp2_ssize rv; const nghttp2_nv cl_resnv[] = {MAKE_NV(":status", "304"), MAKE_NV("content-length", "20")}; const nghttp2_nv conn_reqnv[] = {MAKE_NV(":authority", "localhost"), MAKE_NV(":method", "CONNECT"), MAKE_NV("content-length", "999999")}; const nghttp2_nv conn_cl_resnv[] = {MAKE_NV(":status", "200"), MAKE_NV("content-length", "0")}; nghttp2_stream *stream; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_client_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); /* If status 304, content-length must be ignored */ open_sent_stream2(session, 1, NGHTTP2_STREAM_OPENING); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, cl_resnv, ARRLEN(cl_resnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); assert_null(nghttp2_session_get_next_ob_item(session)); nghttp2_bufs_reset(&bufs); /* Content-Length in 200 response to CONNECT is ignored */ stream = open_sent_stream2(session, 3, NGHTTP2_STREAM_OPENING); stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT; rv = pack_headers(&bufs, &deflater, 3, NGHTTP2_FLAG_END_HEADERS, conn_cl_resnv, ARRLEN(conn_cl_resnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); assert_null(nghttp2_session_get_next_ob_item(session)); assert_int64(-1, ==, stream->content_length); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); /* If request method is CONNECT, content-length must be ignored */ nghttp2_session_server_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, conn_reqnv, ARRLEN(conn_reqnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); assert_null(nghttp2_session_get_next_ob_item(session)); stream = nghttp2_session_get_stream(session, 1); assert_int64(-1, ==, stream->content_length); assert_true(stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); nghttp2_bufs_free(&bufs); } void test_nghttp2_http_record_request_method(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; const nghttp2_nv conn_reqnv[] = {MAKE_NV(":method", "CONNECT"), MAKE_NV(":authority", "localhost")}; const nghttp2_nv conn_resnv[] = {MAKE_NV(":status", "200"), MAKE_NV("content-length", "9999")}; nghttp2_stream *stream; nghttp2_ssize rv; nghttp2_bufs bufs; nghttp2_hd_deflater deflater; nghttp2_mem *mem; nghttp2_outbound_item *item; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_client_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); assert_int32(1, ==, nghttp2_submit_request2(session, NULL, conn_reqnv, ARRLEN(conn_reqnv), NULL, NULL)); assert_int(0, ==, nghttp2_session_send(session)); stream = nghttp2_session_get_stream(session, 1); assert_uint32(NGHTTP2_HTTP_FLAG_METH_CONNECT, ==, stream->http_flags); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, conn_resnv, ARRLEN(conn_resnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); assert_true(NGHTTP2_HTTP_FLAG_METH_CONNECT & stream->http_flags); assert_int64(-1, ==, stream->content_length); /* content-length is ignored in 200 response to a CONNECT request */ item = nghttp2_session_get_next_ob_item(session); assert_null(item); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); nghttp2_bufs_free(&bufs); } void test_nghttp2_http_push_promise(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_hd_deflater deflater; nghttp2_mem *mem; nghttp2_bufs bufs; nghttp2_ssize rv; nghttp2_stream *stream; const nghttp2_nv bad_reqnv[] = {MAKE_NV(":method", "GET")}; nghttp2_outbound_item *item; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; /* good PUSH_PROMISE case */ nghttp2_session_client_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); open_sent_stream2(session, 1, NGHTTP2_STREAM_OPENING); rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2, reqnv, ARRLEN(reqnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); assert_null(nghttp2_session_get_next_ob_item(session)); stream = nghttp2_session_get_stream(session, 2); assert_not_null(stream); nghttp2_bufs_reset(&bufs); rv = pack_headers(&bufs, &deflater, 2, NGHTTP2_FLAG_END_HEADERS, resnv, ARRLEN(resnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); assert_null(nghttp2_session_get_next_ob_item(session)); assert_int16(200, ==, stream->status_code); nghttp2_bufs_reset(&bufs); /* PUSH_PROMISE lacks mandatory header */ rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 4, bad_reqnv, ARRLEN(bad_reqnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_GOAWAY, ==, item->frame.hd.type); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); nghttp2_bufs_free(&bufs); } void test_nghttp2_http_head_method_upgrade_workaround(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; const nghttp2_nv cl_resnv[] = {MAKE_NV(":status", "200"), MAKE_NV("content-length", "1000000007")}; nghttp2_bufs bufs; nghttp2_hd_deflater deflater; nghttp2_mem *mem; nghttp2_ssize rv; nghttp2_stream *stream; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; nghttp2_session_client_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); nghttp2_session_upgrade(session, NULL, 0, NULL); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, cl_resnv, ARRLEN(cl_resnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); stream = nghttp2_session_get_stream(session, 1); assert_int64(-1, ==, stream->content_length); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); nghttp2_bufs_free(&bufs); } void test_nghttp2_http_no_rfc9113_leading_and_trailing_ws_validation(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_hd_deflater deflater; nghttp2_mem *mem; nghttp2_bufs bufs; nghttp2_ssize rv; const nghttp2_nv ws_reqnv[] = { MAKE_NV(":path", "/"), MAKE_NV(":method", "GET"), MAKE_NV(":authority", "localhost"), MAKE_NV(":scheme", "https"), MAKE_NV("foo", "bar "), }; nghttp2_outbound_item *item; nghttp2_option *option; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; /* By default, the leading and trailing white spaces validation is enabled as per RFC 9113. Without on_invalid_header_callback2, they are ignored. */ nghttp2_session_server_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, ws_reqnv, ARRLEN(ws_reqnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); item = nghttp2_session_get_next_ob_item(session); assert_null(item); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); /* Specify on_invalid_header_callback2. */ callbacks.on_invalid_header_callback2 = term_on_invalid_header_callback2; nghttp2_session_server_new(&session, &callbacks, NULL); nghttp2_hd_deflate_init(&deflater, mem); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, ws_reqnv, ARRLEN(ws_reqnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); item = nghttp2_session_get_next_ob_item(session); assert_uint8(NGHTTP2_RST_STREAM, ==, item->frame.hd.type); assert_int(0, ==, nghttp2_session_send(session)); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); /* Turn off the validation */ nghttp2_option_new(&option); nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(option, 1); nghttp2_session_server_new2(&session, &callbacks, NULL, option); nghttp2_hd_deflate_init(&deflater, mem); rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, ws_reqnv, ARRLEN(ws_reqnv), mem); assert_ptrdiff(0, ==, rv); rv = nghttp2_session_mem_recv2(session, bufs.head->buf.pos, nghttp2_buf_len(&bufs.head->buf)); assert_ptrdiff((nghttp2_ssize)nghttp2_buf_len(&bufs.head->buf), ==, rv); item = nghttp2_session_get_next_ob_item(session); assert_null(item); nghttp2_bufs_reset(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); nghttp2_option_del(option); nghttp2_bufs_free(&bufs); } nghttp2-1.68.0/tests/PaxHeaders/nghttp2_buf_test.c0000644000000000000000000000013215077107271017050 xustar0030 mtime=1761382073.006444079 30 atime=1761382080.107411476 30 ctime=1761382108.100303272 nghttp2-1.68.0/tests/nghttp2_buf_test.c0000644000175100017510000002241315077107271017442 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_buf_test.h" #include #include "munit.h" #include "nghttp2_buf.h" #include "nghttp2_test_helper.h" static const MunitTest tests[] = { munit_void_test(test_nghttp2_bufs_add), munit_void_test(test_nghttp2_bufs_add_stack_buffer_overflow_bug), munit_void_test(test_nghttp2_bufs_addb), munit_void_test(test_nghttp2_bufs_orb), munit_void_test(test_nghttp2_bufs_remove), munit_void_test(test_nghttp2_bufs_reset), munit_void_test(test_nghttp2_bufs_advance), munit_void_test(test_nghttp2_bufs_next_present), munit_void_test(test_nghttp2_bufs_realloc), munit_test_end(), }; const MunitSuite buf_suite = { "/buf", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE, }; void test_nghttp2_bufs_add(void) { int rv; nghttp2_bufs bufs; uint8_t data[2048]; nghttp2_mem *mem; mem = nghttp2_mem_default(); rv = nghttp2_bufs_init(&bufs, 1000, 3, mem); assert_int(0, ==, rv); assert_ptr_equal(bufs.cur->buf.pos, bufs.cur->buf.last); rv = nghttp2_bufs_add(&bufs, data, 493); assert_int(0, ==, rv); assert_size(493, ==, nghttp2_buf_len(&bufs.cur->buf)); assert_size(493, ==, nghttp2_bufs_len(&bufs)); assert_size(507, ==, nghttp2_bufs_cur_avail(&bufs)); rv = nghttp2_bufs_add(&bufs, data, 507); assert_int(0, ==, rv); assert_size(1000, ==, nghttp2_buf_len(&bufs.cur->buf)); assert_size(1000, ==, nghttp2_bufs_len(&bufs)); assert_ptr_equal(bufs.cur, bufs.head); rv = nghttp2_bufs_add(&bufs, data, 1); assert_int(0, ==, rv); assert_size(1, ==, nghttp2_buf_len(&bufs.cur->buf)); assert_size(1001, ==, nghttp2_bufs_len(&bufs)); assert_ptr_equal(bufs.cur, bufs.head->next); nghttp2_bufs_free(&bufs); } /* Test for GH-232, stack-buffer-overflow */ void test_nghttp2_bufs_add_stack_buffer_overflow_bug(void) { int rv; nghttp2_bufs bufs; uint8_t data[1024]; nghttp2_mem *mem; mem = nghttp2_mem_default(); rv = nghttp2_bufs_init(&bufs, 100, 200, mem); assert_int(0, ==, rv); rv = nghttp2_bufs_add(&bufs, data, sizeof(data)); assert_int(0, ==, rv); assert_size(sizeof(data), ==, nghttp2_bufs_len(&bufs)); nghttp2_bufs_free(&bufs); } void test_nghttp2_bufs_addb(void) { int rv; nghttp2_bufs bufs; size_t i; nghttp2_mem *mem; mem = nghttp2_mem_default(); rv = nghttp2_bufs_init(&bufs, 1000, 3, mem); assert_int(0, ==, rv); rv = nghttp2_bufs_addb(&bufs, 14); assert_int(0, ==, rv); assert_size(1, ==, nghttp2_buf_len(&bufs.cur->buf)); assert_size(1, ==, nghttp2_bufs_len(&bufs)); assert_uint8(14, ==, *bufs.cur->buf.pos); for (i = 0; i < 999; ++i) { rv = nghttp2_bufs_addb(&bufs, 254); assert_int(0, ==, rv); assert_size(i + 2, ==, nghttp2_buf_len(&bufs.cur->buf)); assert_size(i + 2, ==, nghttp2_bufs_len(&bufs)); assert_uint8(254, ==, *(bufs.cur->buf.last - 1)); assert_ptr_equal(bufs.cur, bufs.head); } rv = nghttp2_bufs_addb(&bufs, 253); assert_int(0, ==, rv); assert_size(1, ==, nghttp2_buf_len(&bufs.cur->buf)); assert_size(1001, ==, nghttp2_bufs_len(&bufs)); assert_uint8(253, ==, *(bufs.cur->buf.last - 1)); assert_ptr_equal(bufs.cur, bufs.head->next); rv = nghttp2_bufs_addb_hold(&bufs, 15); assert_int(0, ==, rv); assert_size(1, ==, nghttp2_buf_len(&bufs.cur->buf)); assert_size(1001, ==, nghttp2_bufs_len(&bufs)); assert_uint8(15, ==, *(bufs.cur->buf.last)); /* test fast version */ nghttp2_bufs_fast_addb(&bufs, 240); assert_size(2, ==, nghttp2_buf_len(&bufs.cur->buf)); assert_size(1002, ==, nghttp2_bufs_len(&bufs)); assert_uint8(240, ==, *(bufs.cur->buf.last - 1)); nghttp2_bufs_fast_addb_hold(&bufs, 113); assert_size(2, ==, nghttp2_buf_len(&bufs.cur->buf)); assert_size(1002, ==, nghttp2_bufs_len(&bufs)); assert_uint8(113, ==, *(bufs.cur->buf.last)); /* addb_hold when last == end */ bufs.cur->buf.last = bufs.cur->buf.end; rv = nghttp2_bufs_addb_hold(&bufs, 19); assert_int(0, ==, rv); assert_size(0, ==, nghttp2_buf_len(&bufs.cur->buf)); assert_size(2000, ==, nghttp2_bufs_len(&bufs)); assert_uint8(19, ==, *(bufs.cur->buf.last)); nghttp2_bufs_free(&bufs); } void test_nghttp2_bufs_orb(void) { int rv; nghttp2_bufs bufs; nghttp2_mem *mem; mem = nghttp2_mem_default(); rv = nghttp2_bufs_init(&bufs, 1000, 3, mem); assert_int(0, ==, rv); *(bufs.cur->buf.last) = 0; rv = nghttp2_bufs_orb_hold(&bufs, 15); assert_int(0, ==, rv); assert_size(0, ==, nghttp2_buf_len(&bufs.cur->buf)); assert_size(0, ==, nghttp2_bufs_len(&bufs)); assert_uint8(15, ==, *(bufs.cur->buf.last)); rv = nghttp2_bufs_orb(&bufs, 240); assert_int(0, ==, rv); assert_size(1, ==, nghttp2_buf_len(&bufs.cur->buf)); assert_size(1, ==, nghttp2_bufs_len(&bufs)); assert_uint8(255, ==, *(bufs.cur->buf.last - 1)); *(bufs.cur->buf.last) = 0; nghttp2_bufs_fast_orb_hold(&bufs, 240); assert_uint8(240, ==, *(bufs.cur->buf.last)); nghttp2_bufs_fast_orb(&bufs, 15); assert_uint8(255, ==, *(bufs.cur->buf.last - 1)); nghttp2_bufs_free(&bufs); } void test_nghttp2_bufs_remove(void) { int rv; nghttp2_bufs bufs; nghttp2_buf_chain *chain; int i; uint8_t *out; nghttp2_ssize outlen; nghttp2_mem *mem; mem = nghttp2_mem_default(); rv = nghttp2_bufs_init(&bufs, 1000, 3, mem); assert_int(0, ==, rv); nghttp2_buf_shift_right(&bufs.cur->buf, 10); rv = nghttp2_bufs_add(&bufs, "hello ", 6); assert_int(0, ==, rv); for (i = 0; i < 2; ++i) { chain = bufs.cur; rv = nghttp2_bufs_advance(&bufs); assert_int(0, ==, rv); assert_ptr_equal(chain->next, bufs.cur); } rv = nghttp2_bufs_add(&bufs, "world", 5); assert_int(0, ==, rv); outlen = nghttp2_bufs_remove(&bufs, &out); assert_ptrdiff(11, ==, outlen); assert_memory_equal((size_t)outlen, "hello world", out); assert_size(11, ==, nghttp2_bufs_len(&bufs)); mem->free(out, NULL); nghttp2_bufs_free(&bufs); } void test_nghttp2_bufs_reset(void) { int rv; nghttp2_bufs bufs; nghttp2_buf_chain *ci; size_t offset = 9; nghttp2_mem *mem; mem = nghttp2_mem_default(); rv = nghttp2_bufs_init3(&bufs, 250, 3, 1, offset, mem); assert_int(0, ==, rv); rv = nghttp2_bufs_add(&bufs, "foo", 3); assert_int(0, ==, rv); rv = nghttp2_bufs_advance(&bufs); assert_int(0, ==, rv); rv = nghttp2_bufs_add(&bufs, "bar", 3); assert_int(0, ==, rv); assert_size(6, ==, nghttp2_bufs_len(&bufs)); nghttp2_bufs_reset(&bufs); assert_size(0, ==, nghttp2_bufs_len(&bufs)); assert_ptr_equal(bufs.cur, bufs.head); for (ci = bufs.head; ci; ci = ci->next) { assert_ptrdiff((ptrdiff_t)offset, ==, ci->buf.pos - ci->buf.begin); assert_ptr_equal(ci->buf.pos, ci->buf.last); } assert_null(bufs.head->next); nghttp2_bufs_free(&bufs); } void test_nghttp2_bufs_advance(void) { int rv; nghttp2_bufs bufs; int i; nghttp2_mem *mem; mem = nghttp2_mem_default(); rv = nghttp2_bufs_init(&bufs, 250, 3, mem); assert_int(0, ==, rv); for (i = 0; i < 2; ++i) { rv = nghttp2_bufs_advance(&bufs); assert_int(0, ==, rv); } rv = nghttp2_bufs_advance(&bufs); assert_int(NGHTTP2_ERR_BUFFER_ERROR, ==, rv); nghttp2_bufs_free(&bufs); } void test_nghttp2_bufs_next_present(void) { int rv; nghttp2_bufs bufs; nghttp2_mem *mem; mem = nghttp2_mem_default(); rv = nghttp2_bufs_init(&bufs, 250, 3, mem); assert_int(0, ==, rv); assert_false(nghttp2_bufs_next_present(&bufs)); rv = nghttp2_bufs_advance(&bufs); assert_int(0, ==, rv); nghttp2_bufs_rewind(&bufs); assert_false(nghttp2_bufs_next_present(&bufs)); bufs.cur = bufs.head->next; rv = nghttp2_bufs_addb(&bufs, 1); assert_int(0, ==, rv); nghttp2_bufs_rewind(&bufs); assert_true(nghttp2_bufs_next_present(&bufs)); nghttp2_bufs_free(&bufs); } void test_nghttp2_bufs_realloc(void) { int rv; nghttp2_bufs bufs; nghttp2_mem *mem; mem = nghttp2_mem_default(); rv = nghttp2_bufs_init3(&bufs, 266, 3, 1, 10, mem); assert_int(0, ==, rv); /* Create new buffer to see that these buffers are deallocated on realloc */ rv = nghttp2_bufs_advance(&bufs); assert_int(0, ==, rv); rv = nghttp2_bufs_realloc(&bufs, 522); assert_int(0, ==, rv); assert_size(512, ==, nghttp2_bufs_cur_avail(&bufs)); rv = nghttp2_bufs_realloc(&bufs, 9); assert_int(NGHTTP2_ERR_INVALID_ARGUMENT, ==, rv); nghttp2_bufs_free(&bufs); } nghttp2-1.68.0/tests/PaxHeaders/nghttp2_pq_test.h0000644000000000000000000000013215077107271016721 xustar0030 mtime=1761382073.007444074 30 atime=1761382080.107411476 30 ctime=1761382108.069303361 nghttp2-1.68.0/tests/nghttp2_pq_test.h0000644000175100017510000000300015077107271017302 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_PQ_TEST_H #define NGHTTP2_PQ_TEST_H #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" extern const MunitSuite pq_suite; munit_void_test_decl(test_nghttp2_pq) munit_void_test_decl(test_nghttp2_pq_update) munit_void_test_decl(test_nghttp2_pq_remove) #endif /* NGHTTP2_PQ_TEST_H */ nghttp2-1.68.0/tests/PaxHeaders/main.c0000644000000000000000000000013015077107271014511 xustar0030 mtime=1761382073.004444088 29 atime=1761382080.10641148 29 ctime=1761382108.08730331 nghttp2-1.68.0/tests/main.c0000644000175100017510000000431315077107271015104 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #include "munit.h" /* include test cases' include files here */ #include "nghttp2_pq_test.h" #include "nghttp2_map_test.h" #include "nghttp2_queue_test.h" #include "nghttp2_session_test.h" #include "nghttp2_frame_test.h" #include "nghttp2_stream_test.h" #include "nghttp2_hd_test.h" #include "nghttp2_alpn_test.h" #include "nghttp2_helper_test.h" #include "nghttp2_buf_test.h" #include "nghttp2_http_test.h" #include "nghttp2_extpri_test.h" #include "nghttp2_ratelim_test.h" extern int nghttp2_enable_strict_preface; int main(int argc, char *argv[]) { const MunitSuite suites[] = { pq_suite, map_suite, queue_suite, frame_suite, session_suite, hd_suite, alpn_suite, helper_suite, buf_suite, http_suite, extpri_suite, ratelim_suite, {NULL, NULL, NULL, 0, MUNIT_SUITE_OPTION_NONE}, }; const MunitSuite suite = { "", NULL, suites, 1, MUNIT_SUITE_OPTION_NONE, }; nghttp2_enable_strict_preface = 0; return munit_suite_main(&suite, NULL, argc, argv); } nghttp2-1.68.0/tests/PaxHeaders/nghttp2_test_helper.c0000644000000000000000000000013215077107271017553 xustar0030 mtime=1761382073.008444069 30 atime=1761382080.108411471 30 ctime=1761382108.064303376 nghttp2-1.68.0/tests/nghttp2_test_helper.c0000644000175100017510000002424615077107271020153 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_test_helper.h" #include #include #include "nghttp2_helper.h" #include "nghttp2_priority_spec.h" int unpack_framebuf(nghttp2_frame *frame, nghttp2_bufs *bufs) { nghttp2_buf *buf; /* Assuming we have required data in first buffer. We don't decode header block so, we don't mind its space */ buf = &bufs->head->buf; return unpack_frame(frame, buf->pos, nghttp2_buf_len(buf)); } int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len) { int rv = 0; const uint8_t *payload = in + NGHTTP2_FRAME_HDLEN; size_t payloadlen = len - NGHTTP2_FRAME_HDLEN; size_t payloadoff; nghttp2_mem *mem; mem = nghttp2_mem_default(); nghttp2_frame_unpack_frame_hd(&frame->hd, in); switch (frame->hd.type) { case NGHTTP2_HEADERS: payloadoff = ((frame->hd.flags & NGHTTP2_FLAG_PADDED) > 0); nghttp2_frame_unpack_headers_payload(&frame->headers, payload + payloadoff); break; case NGHTTP2_PRIORITY: nghttp2_frame_unpack_priority_payload(&frame->priority, payload); break; case NGHTTP2_RST_STREAM: nghttp2_frame_unpack_rst_stream_payload(&frame->rst_stream, payload); break; case NGHTTP2_SETTINGS: rv = nghttp2_frame_unpack_settings_payload2( &frame->settings.iv, &frame->settings.niv, payload, payloadlen, mem); break; case NGHTTP2_PUSH_PROMISE: nghttp2_frame_unpack_push_promise_payload(&frame->push_promise, payload); break; case NGHTTP2_PING: nghttp2_frame_unpack_ping_payload(&frame->ping, payload); break; case NGHTTP2_GOAWAY: nghttp2_frame_unpack_goaway_payload2(&frame->goaway, payload, payloadlen, mem); break; case NGHTTP2_WINDOW_UPDATE: nghttp2_frame_unpack_window_update_payload(&frame->window_update, payload); break; case NGHTTP2_ALTSVC: assert(payloadlen > 2); nghttp2_frame_unpack_altsvc_payload2(&frame->ext, payload, payloadlen, mem); break; case NGHTTP2_ORIGIN: rv = nghttp2_frame_unpack_origin_payload(&frame->ext, payload, payloadlen, mem); break; case NGHTTP2_PRIORITY_UPDATE: assert(payloadlen >= 4); nghttp2_frame_unpack_priority_update_payload( &frame->ext, (uint8_t *)payload, payloadlen); break; default: /* Must not be reachable */ assert(0); } return rv; } int strmemeq(const char *a, const uint8_t *b, size_t bn) { const uint8_t *c; if (!a || !b) { return 0; } c = b + bn; for (; *a && b != c && *a == *b; ++a, ++b) ; return !*a && b == c; } int nvnameeq(const char *a, nghttp2_nv *nv) { return strmemeq(a, nv->name, nv->namelen); } int nvvalueeq(const char *a, nghttp2_nv *nv) { return strmemeq(a, nv->value, nv->valuelen); } void nva_out_init(nva_out *out) { memset(out->nva, 0, sizeof(out->nva)); out->nvlen = 0; } void nva_out_reset(nva_out *out, nghttp2_mem *mem) { size_t i; for (i = 0; i < out->nvlen; ++i) { mem->free(out->nva[i].name, NULL); mem->free(out->nva[i].value, NULL); } memset(out->nva, 0, sizeof(out->nva)); out->nvlen = 0; } void add_out(nva_out *out, nghttp2_nv *nv, nghttp2_mem *mem) { nghttp2_nv *onv = &out->nva[out->nvlen]; if (nv->namelen) { onv->name = mem->malloc(nv->namelen, NULL); memcpy(onv->name, nv->name, nv->namelen); } else { onv->name = NULL; } if (nv->valuelen) { onv->value = mem->malloc(nv->valuelen, NULL); memcpy(onv->value, nv->value, nv->valuelen); } else { onv->value = NULL; } onv->namelen = nv->namelen; onv->valuelen = nv->valuelen; onv->flags = nv->flags; ++out->nvlen; } nghttp2_ssize inflate_hd(nghttp2_hd_inflater *inflater, nva_out *out, nghttp2_bufs *bufs, size_t offset, nghttp2_mem *mem) { nghttp2_ssize rv; nghttp2_nv nv; int inflate_flags; nghttp2_buf_chain *ci; nghttp2_buf *buf; nghttp2_buf bp; int fin; size_t processed; processed = 0; for (ci = bufs->head; ci; ci = ci->next) { buf = &ci->buf; fin = nghttp2_buf_len(buf) == 0 || ci->next == NULL; bp = *buf; if (offset) { size_t n; n = nghttp2_min_size(offset, nghttp2_buf_len(&bp)); bp.pos += n; offset -= n; } for (;;) { inflate_flags = 0; rv = nghttp2_hd_inflate_hd3(inflater, &nv, &inflate_flags, bp.pos, nghttp2_buf_len(&bp), fin); if (rv < 0) { return rv; } bp.pos += rv; processed += (size_t)rv; if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { if (out) { add_out(out, &nv, mem); } } if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { break; } if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && nghttp2_buf_len(&bp) == 0) { break; } } } nghttp2_hd_inflate_end_headers(inflater); return (nghttp2_ssize)processed; } int pack_headers(nghttp2_bufs *bufs, nghttp2_hd_deflater *deflater, int32_t stream_id, uint8_t flags, const nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem) { nghttp2_nv *dnva; nghttp2_frame frame; int rv; nghttp2_nv_array_copy(&dnva, nva, nvlen, mem); nghttp2_frame_headers_init(&frame.headers, flags, stream_id, NGHTTP2_HCAT_HEADERS, NULL, dnva, nvlen); rv = nghttp2_frame_pack_headers(bufs, &frame.headers, deflater); nghttp2_frame_headers_free(&frame.headers, mem); return rv; } int pack_push_promise(nghttp2_bufs *bufs, nghttp2_hd_deflater *deflater, int32_t stream_id, uint8_t flags, int32_t promised_stream_id, const nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem) { nghttp2_nv *dnva; nghttp2_frame frame; int rv; nghttp2_nv_array_copy(&dnva, nva, nvlen, mem); nghttp2_frame_push_promise_init(&frame.push_promise, flags, stream_id, promised_stream_id, dnva, nvlen); rv = nghttp2_frame_pack_push_promise(bufs, &frame.push_promise, deflater); nghttp2_frame_push_promise_free(&frame.push_promise, mem); return rv; } int frame_pack_bufs_init(nghttp2_bufs *bufs) { /* 1 for Pad Length */ return nghttp2_bufs_init2(bufs, 4096, 16, NGHTTP2_FRAME_HDLEN + 1, nghttp2_mem_default()); } void bufs_large_init(nghttp2_bufs *bufs, size_t chunk_size) { /* 1 for Pad Length */ nghttp2_bufs_init2(bufs, chunk_size, 16, NGHTTP2_FRAME_HDLEN + 1, nghttp2_mem_default()); } nghttp2_stream *open_stream(nghttp2_session *session, int32_t stream_id) { return nghttp2_session_open_stream( session, stream_id, NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_STREAM_OPENED, NULL); } nghttp2_outbound_item *create_data_ob_item(nghttp2_mem *mem) { nghttp2_outbound_item *item; item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); memset(item, 0, sizeof(nghttp2_outbound_item)); return item; } nghttp2_stream *open_sent_stream(nghttp2_session *session, int32_t stream_id) { return open_sent_stream3(session, stream_id, NGHTTP2_FLAG_NONE, NGHTTP2_STREAM_OPENED, NULL); } nghttp2_stream *open_sent_stream2(nghttp2_session *session, int32_t stream_id, nghttp2_stream_state initial_state) { return open_sent_stream3(session, stream_id, NGHTTP2_FLAG_NONE, initial_state, NULL); } nghttp2_stream *open_sent_stream3(nghttp2_session *session, int32_t stream_id, uint8_t flags, nghttp2_stream_state initial_state, void *stream_user_data) { nghttp2_stream *stream; assert(nghttp2_session_is_my_stream_id(session, stream_id)); stream = nghttp2_session_open_stream(session, stream_id, flags, initial_state, stream_user_data); session->last_sent_stream_id = nghttp2_max_int32(session->last_sent_stream_id, stream_id); session->next_stream_id = nghttp2_max_uint32(session->next_stream_id, (uint32_t)stream_id + 2); return stream; } nghttp2_stream *open_recv_stream(nghttp2_session *session, int32_t stream_id) { return open_recv_stream3(session, stream_id, NGHTTP2_FLAG_NONE, NGHTTP2_STREAM_OPENED, NULL); } nghttp2_stream *open_recv_stream2(nghttp2_session *session, int32_t stream_id, nghttp2_stream_state initial_state) { return open_recv_stream3(session, stream_id, NGHTTP2_FLAG_NONE, initial_state, NULL); } nghttp2_stream *open_recv_stream3(nghttp2_session *session, int32_t stream_id, uint8_t flags, nghttp2_stream_state initial_state, void *stream_user_data) { nghttp2_stream *stream; assert(!nghttp2_session_is_my_stream_id(session, stream_id)); stream = nghttp2_session_open_stream(session, stream_id, flags, initial_state, stream_user_data); session->last_recv_stream_id = nghttp2_max_int32(session->last_recv_stream_id, stream_id); return stream; } nghttp2-1.68.0/tests/PaxHeaders/nghttp2_hd_test.h0000644000000000000000000000013215077107271016674 xustar0030 mtime=1761382073.006444079 30 atime=1761382080.107411476 30 ctime=1761382108.077303338 nghttp2-1.68.0/tests/nghttp2_hd_test.h0000644000175100017510000000514715077107271017273 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_HD_TEST_H #define NGHTTP2_HD_TEST_H #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" extern const MunitSuite hd_suite; munit_void_test_decl(test_nghttp2_hd_deflate) munit_void_test_decl(test_nghttp2_hd_deflate_same_indexed_repr) munit_void_test_decl(test_nghttp2_hd_inflate_indexed) munit_void_test_decl(test_nghttp2_hd_inflate_indname_noinc) munit_void_test_decl(test_nghttp2_hd_inflate_indname_inc) munit_void_test_decl(test_nghttp2_hd_inflate_indname_inc_eviction) munit_void_test_decl(test_nghttp2_hd_inflate_newname_noinc) munit_void_test_decl(test_nghttp2_hd_inflate_newname_inc) munit_void_test_decl(test_nghttp2_hd_inflate_clearall_inc) munit_void_test_decl(test_nghttp2_hd_inflate_zero_length_huffman) munit_void_test_decl(test_nghttp2_hd_inflate_expect_table_size_update) munit_void_test_decl(test_nghttp2_hd_inflate_unexpected_table_size_update) munit_void_test_decl(test_nghttp2_hd_ringbuf_reserve) munit_void_test_decl(test_nghttp2_hd_change_table_size) munit_void_test_decl(test_nghttp2_hd_deflate_inflate) munit_void_test_decl(test_nghttp2_hd_no_index) munit_void_test_decl(test_nghttp2_hd_deflate_bound) munit_void_test_decl(test_nghttp2_hd_public_api) munit_void_test_decl(test_nghttp2_hd_deflate_hd_vec) munit_void_test_decl(test_nghttp2_hd_decode_length) munit_void_test_decl(test_nghttp2_hd_huff_encode) munit_void_test_decl(test_nghttp2_hd_huff_decode) #endif /* NGHTTP2_HD_TEST_H */ nghttp2-1.68.0/tests/PaxHeaders/failmalloc_test.c0000644000000000000000000000013015077107271016727 xustar0030 mtime=1761382073.004444088 29 atime=1761382080.10641148 29 ctime=1761382108.05930339 nghttp2-1.68.0/tests/failmalloc_test.c0000644000175100017510000004063615077107271017332 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012, 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "failmalloc_test.h" #include #include #include "munit.h" #include "nghttp2_session.h" #include "nghttp2_stream.h" #include "nghttp2_frame.h" #include "nghttp2_helper.h" #include "malloc_wrapper.h" #include "nghttp2_test_helper.h" static const MunitTest tests[] = { munit_void_test(test_nghttp2_session_send), munit_void_test(test_nghttp2_session_send_server), munit_void_test(test_nghttp2_session_recv), munit_void_test(test_nghttp2_frame), munit_void_test(test_nghttp2_hd), munit_test_end(), }; const MunitSuite failmalloc_suite = { "/failmalloc", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE, }; typedef struct { uint8_t data[8192]; uint8_t *datamark, *datalimit; } data_feed; typedef struct { data_feed *df; size_t data_source_length; } my_user_data; static void data_feed_init(data_feed *df, nghttp2_bufs *bufs) { nghttp2_buf *buf; size_t data_length; buf = &bufs->head->buf; data_length = nghttp2_buf_len(buf); assert(data_length <= sizeof(df->data)); memcpy(df->data, buf->pos, data_length); df->datamark = df->data; df->datalimit = df->data + data_length; } static nghttp2_ssize null_send_callback(nghttp2_session *session, const uint8_t *data, size_t len, int flags, void *user_data) { (void)session; (void)data; (void)flags; (void)user_data; return (nghttp2_ssize)len; } static nghttp2_ssize data_feed_recv_callback(nghttp2_session *session, uint8_t *data, size_t len, int flags, void *user_data) { data_feed *df = ((my_user_data *)user_data)->df; size_t avail = (size_t)(df->datalimit - df->datamark); size_t wlen = nghttp2_min_size(avail, len); (void)session; (void)flags; memcpy(data, df->datamark, wlen); df->datamark += wlen; return (nghttp2_ssize)wlen; } static nghttp2_ssize fixed_length_data_source_read_callback( nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len, uint32_t *data_flags, nghttp2_data_source *source, void *user_data) { my_user_data *ud = (my_user_data *)user_data; size_t wlen; (void)session; (void)stream_id; (void)buf; (void)source; if (len < ud->data_source_length) { wlen = len; } else { wlen = ud->data_source_length; } ud->data_source_length -= wlen; if (ud->data_source_length == 0) { *data_flags = NGHTTP2_DATA_FLAG_EOF; } return (nghttp2_ssize)wlen; } #define TEST_FAILMALLOC_RUN(FUN) \ do { \ int nmalloc, i; \ \ nghttp2_failmalloc = 0; \ nghttp2_nmalloc = 0; \ FUN(); \ nmalloc = nghttp2_nmalloc; \ \ nghttp2_failmalloc = 1; \ for (i = 0; i < nmalloc; ++i) { \ nghttp2_nmalloc = 0; \ nghttp2_failstart = i; \ /* printf("i=%zu\n", i); */ \ FUN(); \ /* printf("nmalloc=%d\n", nghttp2_nmalloc); */ \ } \ nghttp2_failmalloc = 0; \ } while (0) static void run_nghttp2_session_send(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_nv nv[] = {MAKE_NV(":host", "example.org"), MAKE_NV(":scheme", "https")}; nghttp2_data_provider2 data_prd; nghttp2_settings_entry iv[2]; my_user_data ud; int rv; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback2 = null_send_callback; data_prd.read_callback = fixed_length_data_source_read_callback; ud.data_source_length = 64 * 1024; iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[0].value = 4096; iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; iv[1].value = 100; rv = nghttp2_session_client_new3(&session, &callbacks, &ud, NULL, nghttp2_mem_fm()); if (rv != 0) { goto client_new_fail; } rv = nghttp2_submit_request2(session, NULL, nv, ARRLEN(nv), &data_prd, NULL); if (rv < 0) { goto fail; } rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1, NULL, nv, ARRLEN(nv), NULL); if (rv < 0) { goto fail; } rv = nghttp2_session_send(session); if (rv != 0) { goto fail; } /* The HEADERS submitted by the previous nghttp2_submit_headers will have stream ID 3. Send HEADERS to that stream. */ rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 3, NULL, nv, ARRLEN(nv), NULL); if (rv != 0) { goto fail; } rv = nghttp2_submit_data2(session, NGHTTP2_FLAG_END_STREAM, 3, &data_prd); if (rv != 0) { goto fail; } rv = nghttp2_session_send(session); if (rv != 0) { goto fail; } rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 3, NGHTTP2_CANCEL); if (rv != 0) { goto fail; } rv = nghttp2_session_send(session); if (rv != 0) { goto fail; } rv = nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL); if (rv != 0) { goto fail; } rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 2); if (rv != 0) { goto fail; } rv = nghttp2_session_send(session); if (rv != 0) { goto fail; } rv = nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, 100, NGHTTP2_NO_ERROR, NULL, 0); if (rv != 0) { goto fail; } rv = nghttp2_session_send(session); if (rv != 0) { goto fail; } fail: nghttp2_session_del(session); client_new_fail:; } void test_nghttp2_session_send(void) { TEST_FAILMALLOC_RUN(run_nghttp2_session_send); } static void run_nghttp2_session_send_server(void) { nghttp2_session *session; nghttp2_session_callbacks *callbacks; int rv; const uint8_t *txdata; nghttp2_ssize txdatalen; const uint8_t origin[] = "nghttp2.org"; const uint8_t altsvc_field_value[] = "h2=\":443\""; static const uint8_t nghttp2[] = "https://nghttp2.org"; static const nghttp2_origin_entry ov = { (uint8_t *)nghttp2, sizeof(nghttp2) - 1, }; rv = nghttp2_session_callbacks_new(&callbacks); if (rv != 0) { return; } rv = nghttp2_session_server_new3(&session, callbacks, NULL, NULL, nghttp2_mem_fm()); nghttp2_session_callbacks_del(callbacks); if (rv != 0) { return; } rv = nghttp2_submit_altsvc(session, NGHTTP2_FLAG_NONE, 0, origin, sizeof(origin) - 1, altsvc_field_value, sizeof(altsvc_field_value) - 1); if (rv != 0) { goto fail; } rv = nghttp2_submit_origin(session, NGHTTP2_FLAG_NONE, &ov, 1); if (rv != 0) { goto fail; } txdatalen = nghttp2_session_mem_send2(session, &txdata); if (txdatalen < 0) { goto fail; } fail: nghttp2_session_del(session); } void test_nghttp2_session_send_server(void) { TEST_FAILMALLOC_RUN(run_nghttp2_session_send_server); } static void run_nghttp2_session_recv(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_hd_deflater deflater; nghttp2_frame frame; nghttp2_bufs bufs; nghttp2_nv nv[] = { MAKE_NV(":method", "GET"), MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"), MAKE_NV(":path", "/"), }; nghttp2_settings_entry iv[2]; my_user_data ud; data_feed df; int rv; nghttp2_nv *nva; size_t nvlen; rv = frame_pack_bufs_init(&bufs); if (rv != 0) { return; } memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.recv_callback2 = data_feed_recv_callback; ud.df = &df; nghttp2_failmalloc_pause(); nghttp2_hd_deflate_init(&deflater, nghttp2_mem_fm()); nghttp2_session_server_new3(&session, &callbacks, &ud, NULL, nghttp2_mem_fm()); /* Client preface */ nghttp2_bufs_add(&bufs, NGHTTP2_CLIENT_MAGIC, NGHTTP2_CLIENT_MAGIC_LEN); data_feed_init(&df, &bufs); nghttp2_bufs_reset(&bufs); nghttp2_failmalloc_unpause(); rv = nghttp2_session_recv(session); if (rv != 0) { goto fail; } nghttp2_failmalloc_pause(); /* SETTINGS */ iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[0].value = 4096; iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; iv[1].value = 100; nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, nghttp2_frame_iv_copy(iv, 2, nghttp2_mem_fm()), 2); nghttp2_frame_pack_settings(&bufs, &frame.settings); nghttp2_frame_settings_free(&frame.settings, nghttp2_mem_fm()); data_feed_init(&df, &bufs); nghttp2_bufs_reset(&bufs); nghttp2_failmalloc_unpause(); rv = nghttp2_session_recv(session); if (rv != 0) { goto fail; } nghttp2_failmalloc_pause(); /* HEADERS */ nvlen = ARRLEN(nv); nghttp2_nv_array_copy(&nva, nv, nvlen, nghttp2_mem_fm()); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_STREAM, 1, NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen); nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); nghttp2_frame_headers_free(&frame.headers, nghttp2_mem_fm()); data_feed_init(&df, &bufs); nghttp2_bufs_reset(&bufs); nghttp2_failmalloc_unpause(); rv = nghttp2_session_recv(session); if (rv != 0) { goto fail; } /* PING */ nghttp2_failmalloc_pause(); nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL); nghttp2_frame_pack_ping(&bufs, &frame.ping); nghttp2_frame_ping_free(&frame.ping); data_feed_init(&df, &bufs); nghttp2_bufs_reset(&bufs); nghttp2_failmalloc_unpause(); rv = nghttp2_session_recv(session); if (rv != 0) { goto fail; } /* RST_STREAM */ nghttp2_failmalloc_pause(); nghttp2_frame_rst_stream_init(&frame.rst_stream, 1, NGHTTP2_PROTOCOL_ERROR); nghttp2_frame_pack_rst_stream(&bufs, &frame.rst_stream); nghttp2_frame_rst_stream_free(&frame.rst_stream); nghttp2_bufs_reset(&bufs); nghttp2_failmalloc_unpause(); rv = nghttp2_session_recv(session); if (rv != 0) { goto fail; } fail: nghttp2_bufs_free(&bufs); nghttp2_session_del(session); nghttp2_hd_deflate_free(&deflater); } void test_nghttp2_session_recv(void) { TEST_FAILMALLOC_RUN(run_nghttp2_session_recv); } static void run_nghttp2_frame_pack_headers(void) { nghttp2_hd_deflater deflater; nghttp2_hd_inflater inflater; nghttp2_frame frame, oframe; nghttp2_bufs bufs; nghttp2_nv nv[] = {MAKE_NV(":host", "example.org"), MAKE_NV(":scheme", "https")}; int rv; nghttp2_nv *nva; size_t nvlen; rv = frame_pack_bufs_init(&bufs); if (rv != 0) { return; } rv = nghttp2_hd_deflate_init(&deflater, nghttp2_mem_fm()); if (rv != 0) { goto deflate_init_fail; } rv = nghttp2_hd_inflate_init(&inflater, nghttp2_mem_fm()); if (rv != 0) { goto inflate_init_fail; } nvlen = ARRLEN(nv); rv = nghttp2_nv_array_copy(&nva, nv, nvlen, nghttp2_mem_fm()); if (rv < 0) { goto nv_copy_fail; } nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_STREAM, 1, NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen); rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); if (rv != 0) { goto fail; } rv = unpack_framebuf(&oframe, &bufs); if (rv != 0) { goto fail; } nghttp2_frame_headers_free(&oframe.headers, nghttp2_mem_fm()); fail: nghttp2_frame_headers_free(&frame.headers, nghttp2_mem_fm()); nv_copy_fail: nghttp2_hd_inflate_free(&inflater); inflate_init_fail: nghttp2_hd_deflate_free(&deflater); deflate_init_fail: nghttp2_bufs_free(&bufs); } static void run_nghttp2_frame_pack_settings(void) { nghttp2_frame frame, oframe; nghttp2_bufs bufs; nghttp2_buf *buf; nghttp2_settings_entry iv[2], *iv_copy; int rv; rv = frame_pack_bufs_init(&bufs); if (rv != 0) { return; } iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[0].value = 4096; iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; iv[1].value = 100; iv_copy = nghttp2_frame_iv_copy(iv, 2, nghttp2_mem_fm()); if (iv_copy == NULL) { goto iv_copy_fail; } nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, iv_copy, 2); rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); if (rv != 0) { goto fail; } buf = &bufs.head->buf; rv = nghttp2_frame_unpack_settings_payload2( &oframe.settings.iv, &oframe.settings.niv, buf->pos + NGHTTP2_FRAME_HDLEN, nghttp2_buf_len(buf) - NGHTTP2_FRAME_HDLEN, nghttp2_mem_fm()); if (rv != 0) { goto fail; } nghttp2_frame_settings_free(&oframe.settings, nghttp2_mem_fm()); fail: nghttp2_frame_settings_free(&frame.settings, nghttp2_mem_fm()); iv_copy_fail: nghttp2_bufs_free(&bufs); } void test_nghttp2_frame(void) { TEST_FAILMALLOC_RUN(run_nghttp2_frame_pack_headers); TEST_FAILMALLOC_RUN(run_nghttp2_frame_pack_settings); } static int deflate_inflate(nghttp2_hd_deflater *deflater, nghttp2_hd_inflater *inflater, nghttp2_bufs *bufs, nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem) { int rv; rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, nva, nvlen); if (rv != 0) { return rv; } rv = (int)inflate_hd(inflater, NULL, bufs, 0, mem); if (rv < 0) { return rv; } nghttp2_bufs_reset(bufs); return 0; } static void run_nghttp2_hd(void) { nghttp2_hd_deflater deflater; nghttp2_hd_inflater inflater; nghttp2_bufs bufs; int rv; nghttp2_nv nva1[] = { MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"), MAKE_NV(":path", "/slashdot"), MAKE_NV("accept-encoding", "gzip, deflate"), MAKE_NV("foo", "bar")}; nghttp2_nv nva2[] = { MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"), MAKE_NV(":path", "/style.css"), MAKE_NV("cookie", "nghttp2=FTW"), MAKE_NV("foo", "bar2")}; rv = frame_pack_bufs_init(&bufs); if (rv != 0) { return; } rv = nghttp2_hd_deflate_init(&deflater, nghttp2_mem_fm()); if (rv != 0) { goto deflate_init_fail; } rv = nghttp2_hd_inflate_init(&inflater, nghttp2_mem_fm()); if (rv != 0) { goto inflate_init_fail; } rv = deflate_inflate(&deflater, &inflater, &bufs, nva1, ARRLEN(nva1), nghttp2_mem_fm()); if (rv != 0) { goto deflate_hd_fail; } rv = deflate_inflate(&deflater, &inflater, &bufs, nva2, ARRLEN(nva2), nghttp2_mem_fm()); if (rv != 0) { goto deflate_hd_fail; } deflate_hd_fail: nghttp2_hd_inflate_free(&inflater); inflate_init_fail: nghttp2_hd_deflate_free(&deflater); deflate_init_fail: nghttp2_bufs_free(&bufs); } void test_nghttp2_hd(void) { TEST_FAILMALLOC_RUN(run_nghttp2_hd); } nghttp2-1.68.0/tests/PaxHeaders/nghttp2_frame_test.c0000644000000000000000000000013215077107271017366 xustar0030 mtime=1761382073.006444079 30 atime=1761382080.107411476 30 ctime=1761382108.092303295 nghttp2-1.68.0/tests/nghttp2_frame_test.c0000644000175100017510000005740015077107271017764 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_frame_test.h" #include #include #include "munit.h" #include "nghttp2_frame.h" #include "nghttp2_helper.h" #include "nghttp2_test_helper.h" #include "nghttp2_priority_spec.h" static MunitTest tests[] = { munit_void_test(test_nghttp2_frame_pack_headers), munit_void_test(test_nghttp2_frame_pack_headers_frame_too_large), munit_void_test(test_nghttp2_frame_pack_priority), munit_void_test(test_nghttp2_frame_pack_rst_stream), munit_void_test(test_nghttp2_frame_pack_settings), munit_void_test(test_nghttp2_frame_pack_push_promise), munit_void_test(test_nghttp2_frame_pack_ping), munit_void_test(test_nghttp2_frame_pack_goaway), munit_void_test(test_nghttp2_frame_pack_window_update), munit_void_test(test_nghttp2_frame_pack_altsvc), munit_void_test(test_nghttp2_frame_pack_origin), munit_void_test(test_nghttp2_frame_pack_priority_update), munit_void_test(test_nghttp2_nv_array_copy), munit_void_test(test_nghttp2_iv_check), munit_test_end(), }; const MunitSuite frame_suite = { "/frame", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE, }; static nghttp2_nv make_nv(const char *name, const char *value) { nghttp2_nv nv; nv.name = (uint8_t *)name; nv.value = (uint8_t *)value; nv.namelen = strlen(name); nv.valuelen = strlen(value); nv.flags = NGHTTP2_NV_FLAG_NONE; return nv; } #define HEADERS_LENGTH 7 static nghttp2_nv *headers(nghttp2_mem *mem) { nghttp2_nv *nva = mem->malloc(sizeof(nghttp2_nv) * HEADERS_LENGTH, NULL); nva[0] = make_nv("method", "GET"); nva[1] = make_nv("scheme", "https"); nva[2] = make_nv("url", "/"); nva[3] = make_nv("x-head", "foo"); nva[4] = make_nv("x-head", "bar"); nva[5] = make_nv("version", "HTTP/1.1"); nva[6] = make_nv("x-empty", ""); return nva; } static void check_frame_header(size_t length, uint8_t type, uint8_t flags, int32_t stream_id, nghttp2_frame_hd *hd) { assert_size(length, ==, hd->length); assert_uint8(type, ==, hd->type); assert_uint8(flags, ==, hd->flags); assert_int32(stream_id, ==, hd->stream_id); assert_uint8(0, ==, hd->reserved); } void test_nghttp2_frame_pack_headers(void) { nghttp2_hd_deflater deflater; nghttp2_hd_inflater inflater; nghttp2_headers frame, oframe; nghttp2_bufs bufs; nghttp2_nv *nva; nghttp2_priority_spec pri_spec; size_t nvlen; nva_out out; size_t hdblocklen; int rv; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nva_out_init(&out); nghttp2_hd_deflate_init(&deflater, mem); nghttp2_hd_inflate_init(&inflater, mem); nva = headers(mem); nvlen = HEADERS_LENGTH; nghttp2_priority_spec_default_init(&pri_spec); nghttp2_frame_headers_init( &frame, NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS, 1000000007, NGHTTP2_HCAT_REQUEST, &pri_spec, nva, nvlen); rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater); nghttp2_bufs_rewind(&bufs); assert_int(0, ==, rv); assert_size(0, <, nghttp2_bufs_len(&bufs)); assert_int(0, ==, unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); check_frame_header( nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN, NGHTTP2_HEADERS, NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS, 1000000007, &oframe.hd); /* We did not include PRIORITY flag */ assert_int32(NGHTTP2_DEFAULT_WEIGHT, ==, oframe.pri_spec.weight); hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN; assert_ptrdiff((nghttp2_ssize)hdblocklen, ==, inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem)); assert_size(7, ==, out.nvlen); assert_true(nvnameeq("method", &out.nva[0])); assert_true(nvvalueeq("GET", &out.nva[0])); nghttp2_frame_headers_free(&oframe, mem); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); memset(&oframe, 0, sizeof(oframe)); /* Next, include NGHTTP2_FLAG_PRIORITY */ nghttp2_priority_spec_init(&frame.pri_spec, 1000000009, 12, 1); frame.hd.flags |= NGHTTP2_FLAG_PRIORITY; rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater); assert_int(0, ==, rv); assert_size(0, <, nghttp2_bufs_len(&bufs)); assert_int(0, ==, unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); check_frame_header( nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN, NGHTTP2_HEADERS, NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, 1000000007, &oframe.hd); assert_int32(1000000009, ==, oframe.pri_spec.stream_id); assert_int32(12, ==, oframe.pri_spec.weight); assert_true(oframe.pri_spec.exclusive); hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN - nghttp2_frame_priority_len(oframe.hd.flags); assert_ptrdiff((nghttp2_ssize)hdblocklen, ==, inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN + nghttp2_frame_priority_len(oframe.hd.flags), mem)); nghttp2_nv_array_sort(out.nva, out.nvlen); assert_true(nvnameeq("method", &out.nva[0])); nghttp2_frame_headers_free(&oframe, mem); nva_out_reset(&out, mem); nghttp2_bufs_reset(&bufs); nghttp2_bufs_free(&bufs); nghttp2_frame_headers_free(&frame, mem); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); } void test_nghttp2_frame_pack_headers_frame_too_large(void) { nghttp2_hd_deflater deflater; nghttp2_headers frame; nghttp2_bufs bufs; nghttp2_nv *nva; size_t big_vallen = NGHTTP2_HD_MAX_NV; nghttp2_nv big_hds[16]; size_t big_hdslen = ARRLEN(big_hds); size_t i; int rv; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); for (i = 0; i < big_hdslen; ++i) { big_hds[i].name = (uint8_t *)"header"; big_hds[i].value = mem->malloc(big_vallen + 1, NULL); memset(big_hds[i].value, '0' + (int)i, big_vallen); big_hds[i].value[big_vallen] = '\0'; big_hds[i].namelen = strlen((char *)big_hds[i].name); big_hds[i].valuelen = big_vallen; big_hds[i].flags = NGHTTP2_NV_FLAG_NONE; } nghttp2_nv_array_copy(&nva, big_hds, big_hdslen, mem); nghttp2_hd_deflate_init(&deflater, mem); nghttp2_frame_headers_init( &frame, NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS, 1000000007, NGHTTP2_HCAT_REQUEST, NULL, nva, big_hdslen); rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater); assert_int(NGHTTP2_ERR_HEADER_COMP, ==, rv); nghttp2_frame_headers_free(&frame, mem); nghttp2_bufs_free(&bufs); for (i = 0; i < big_hdslen; ++i) { mem->free(big_hds[i].value, NULL); } nghttp2_hd_deflate_free(&deflater); } void test_nghttp2_frame_pack_priority(void) { nghttp2_priority frame, oframe; nghttp2_bufs bufs; nghttp2_priority_spec pri_spec; frame_pack_bufs_init(&bufs); /* First, pack priority with priority group and weight */ nghttp2_priority_spec_init(&pri_spec, 1000000009, 12, 1); nghttp2_frame_priority_init(&frame, 1000000007, &pri_spec); nghttp2_frame_pack_priority(&bufs, &frame); assert_size(NGHTTP2_FRAME_HDLEN + 5, ==, nghttp2_bufs_len(&bufs)); assert_int(0, ==, unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); check_frame_header(5, NGHTTP2_PRIORITY, NGHTTP2_FLAG_NONE, 1000000007, &oframe.hd); assert_int32(1000000009, ==, oframe.pri_spec.stream_id); assert_int32(12, ==, oframe.pri_spec.weight); assert_true(oframe.pri_spec.exclusive); nghttp2_frame_priority_free(&oframe); nghttp2_bufs_reset(&bufs); nghttp2_bufs_free(&bufs); nghttp2_frame_priority_free(&frame); } void test_nghttp2_frame_pack_rst_stream(void) { nghttp2_rst_stream frame, oframe; nghttp2_bufs bufs; frame_pack_bufs_init(&bufs); nghttp2_frame_rst_stream_init(&frame, 1000000007, NGHTTP2_PROTOCOL_ERROR); nghttp2_frame_pack_rst_stream(&bufs, &frame); assert_size(NGHTTP2_FRAME_HDLEN + 4, ==, nghttp2_bufs_len(&bufs)); assert_int(0, ==, unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); check_frame_header(4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, 1000000007, &oframe.hd); assert_uint32(NGHTTP2_PROTOCOL_ERROR, ==, oframe.error_code); nghttp2_frame_rst_stream_free(&oframe); nghttp2_bufs_reset(&bufs); /* Unknown error code is passed to callback as is */ frame.error_code = 1000000009; nghttp2_frame_pack_rst_stream(&bufs, &frame); assert_int(0, ==, unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); check_frame_header(4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, 1000000007, &oframe.hd); assert_uint32(1000000009, ==, oframe.error_code); nghttp2_frame_rst_stream_free(&oframe); nghttp2_frame_rst_stream_free(&frame); nghttp2_bufs_free(&bufs); } void test_nghttp2_frame_pack_settings(void) { nghttp2_settings frame, oframe; nghttp2_bufs bufs; int i; int rv; nghttp2_settings_entry iv[] = {{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 256}, {NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, 16384}, {NGHTTP2_SETTINGS_HEADER_TABLE_SIZE, 4096}}; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nghttp2_frame_settings_init(&frame, NGHTTP2_FLAG_NONE, nghttp2_frame_iv_copy(iv, 3, mem), 3); rv = nghttp2_frame_pack_settings(&bufs, &frame); assert_int(0, ==, rv); assert_size(NGHTTP2_FRAME_HDLEN + 3 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH, ==, nghttp2_bufs_len(&bufs)); assert_int(0, ==, unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); check_frame_header(3 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH, NGHTTP2_SETTINGS, NGHTTP2_FLAG_NONE, 0, &oframe.hd); assert_size(3, ==, oframe.niv); for (i = 0; i < 3; ++i) { assert_int32(iv[i].settings_id, ==, oframe.iv[i].settings_id); assert_uint32(iv[i].value, ==, oframe.iv[i].value); } nghttp2_bufs_free(&bufs); nghttp2_frame_settings_free(&frame, mem); nghttp2_frame_settings_free(&oframe, mem); } void test_nghttp2_frame_pack_push_promise(void) { nghttp2_hd_deflater deflater; nghttp2_hd_inflater inflater; nghttp2_push_promise frame, oframe; nghttp2_bufs bufs; nghttp2_nv *nva; size_t nvlen; nva_out out; size_t hdblocklen; int rv; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); nva_out_init(&out); nghttp2_hd_deflate_init(&deflater, mem); nghttp2_hd_inflate_init(&inflater, mem); nva = headers(mem); nvlen = HEADERS_LENGTH; nghttp2_frame_push_promise_init(&frame, NGHTTP2_FLAG_END_HEADERS, 1000000007, (1U << 31) - 1, nva, nvlen); rv = nghttp2_frame_pack_push_promise(&bufs, &frame, &deflater); assert_int(0, ==, rv); assert_size(0, <, nghttp2_bufs_len(&bufs)); assert_int(0, ==, unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); check_frame_header(nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN, NGHTTP2_PUSH_PROMISE, NGHTTP2_FLAG_END_HEADERS, 1000000007, &oframe.hd); assert_int32((1U << 31) - 1, ==, oframe.promised_stream_id); hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN - 4; assert_ptrdiff( (nghttp2_ssize)hdblocklen, ==, inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN + 4, mem)); assert_size(7, ==, out.nvlen); assert_true(nvnameeq("method", &out.nva[0])); assert_true(nvvalueeq("GET", &out.nva[0])); nva_out_reset(&out, mem); nghttp2_bufs_free(&bufs); nghttp2_frame_push_promise_free(&oframe, mem); nghttp2_frame_push_promise_free(&frame, mem); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); } void test_nghttp2_frame_pack_ping(void) { nghttp2_ping frame, oframe; nghttp2_bufs bufs; const uint8_t opaque_data[] = "01234567"; frame_pack_bufs_init(&bufs); nghttp2_frame_ping_init(&frame, NGHTTP2_FLAG_ACK, opaque_data); nghttp2_frame_pack_ping(&bufs, &frame); assert_size(NGHTTP2_FRAME_HDLEN + 8, ==, nghttp2_bufs_len(&bufs)); assert_int(0, ==, unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); check_frame_header(8, NGHTTP2_PING, NGHTTP2_FLAG_ACK, 0, &oframe.hd); assert_memory_equal(sizeof(opaque_data) - 1, opaque_data, oframe.opaque_data); nghttp2_bufs_free(&bufs); nghttp2_frame_ping_free(&oframe); nghttp2_frame_ping_free(&frame); } void test_nghttp2_frame_pack_goaway(void) { nghttp2_goaway frame, oframe; nghttp2_bufs bufs; size_t opaque_data_len = 16; uint8_t *opaque_data; int rv; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); opaque_data = mem->malloc(opaque_data_len, NULL); memcpy(opaque_data, "0123456789abcdef", opaque_data_len); nghttp2_frame_goaway_init(&frame, 1000000007, NGHTTP2_PROTOCOL_ERROR, opaque_data, opaque_data_len); rv = nghttp2_frame_pack_goaway(&bufs, &frame); assert_int(0, ==, rv); assert_size(NGHTTP2_FRAME_HDLEN + 8 + opaque_data_len, ==, nghttp2_bufs_len(&bufs)); assert_int(0, ==, unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); check_frame_header(24, NGHTTP2_GOAWAY, NGHTTP2_FLAG_NONE, 0, &oframe.hd); assert_int32(1000000007, ==, oframe.last_stream_id); assert_uint32(NGHTTP2_PROTOCOL_ERROR, ==, oframe.error_code); assert_size(opaque_data_len, ==, oframe.opaque_data_len); assert_memory_equal(opaque_data_len, opaque_data, oframe.opaque_data); nghttp2_frame_goaway_free(&oframe, mem); nghttp2_bufs_reset(&bufs); /* Unknown error code is passed to callback as is */ frame.error_code = 1000000009; rv = nghttp2_frame_pack_goaway(&bufs, &frame); assert_int(0, ==, rv); assert_int(0, ==, unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); check_frame_header(24, NGHTTP2_GOAWAY, NGHTTP2_FLAG_NONE, 0, &oframe.hd); assert_uint32(1000000009, ==, oframe.error_code); nghttp2_frame_goaway_free(&oframe, mem); nghttp2_frame_goaway_free(&frame, mem); nghttp2_bufs_free(&bufs); } void test_nghttp2_frame_pack_window_update(void) { nghttp2_window_update frame, oframe; nghttp2_bufs bufs; frame_pack_bufs_init(&bufs); nghttp2_frame_window_update_init(&frame, NGHTTP2_FLAG_NONE, 1000000007, 4096); nghttp2_frame_pack_window_update(&bufs, &frame); assert_size(NGHTTP2_FRAME_HDLEN + 4, ==, nghttp2_bufs_len(&bufs)); assert_int(0, ==, unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); check_frame_header(4, NGHTTP2_WINDOW_UPDATE, NGHTTP2_FLAG_NONE, 1000000007, &oframe.hd); assert_int32(4096, ==, oframe.window_size_increment); nghttp2_bufs_free(&bufs); nghttp2_frame_window_update_free(&oframe); nghttp2_frame_window_update_free(&frame); } void test_nghttp2_frame_pack_altsvc(void) { nghttp2_extension frame, oframe; nghttp2_ext_altsvc altsvc, oaltsvc; nghttp2_bufs bufs; int rv; size_t payloadlen; static const uint8_t origin[] = "nghttp2.org"; static const uint8_t field_value[] = "h2=\":443\""; nghttp2_buf buf; uint8_t *rawbuf; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); frame.payload = &altsvc; oframe.payload = &oaltsvc; rawbuf = nghttp2_mem_malloc(mem, 32); nghttp2_buf_wrap_init(&buf, rawbuf, 32); buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1); buf.last = nghttp2_cpymem(buf.last, field_value, sizeof(field_value) - 1); nghttp2_frame_altsvc_init(&frame, 1000000007, buf.pos, sizeof(origin) - 1, buf.pos + sizeof(origin) - 1, sizeof(field_value) - 1); payloadlen = 2 + sizeof(origin) - 1 + sizeof(field_value) - 1; nghttp2_frame_pack_altsvc(&bufs, &frame); assert_size(NGHTTP2_FRAME_HDLEN + payloadlen, ==, nghttp2_bufs_len(&bufs)); rv = unpack_framebuf((nghttp2_frame *)&oframe, &bufs); assert_int(0, ==, rv); check_frame_header(payloadlen, NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, 1000000007, &oframe.hd); assert_size(sizeof(origin) - 1, ==, oaltsvc.origin_len); assert_memory_equal(sizeof(origin) - 1, origin, oaltsvc.origin); assert_size(sizeof(field_value) - 1, ==, oaltsvc.field_value_len); assert_memory_equal(sizeof(field_value) - 1, field_value, oaltsvc.field_value); nghttp2_frame_altsvc_free(&oframe, mem); nghttp2_frame_altsvc_free(&frame, mem); nghttp2_bufs_free(&bufs); } void test_nghttp2_frame_pack_origin(void) { nghttp2_extension frame, oframe; nghttp2_ext_origin origin, oorigin; nghttp2_bufs bufs; nghttp2_buf *buf; int rv; size_t payloadlen; static const uint8_t example[] = "https://example.com"; static const uint8_t nghttp2[] = "https://nghttp2.org"; nghttp2_origin_entry ov[] = { { (uint8_t *)example, sizeof(example) - 1, }, { NULL, 0, }, { (uint8_t *)nghttp2, sizeof(nghttp2) - 1, }, }; nghttp2_mem *mem; mem = nghttp2_mem_default(); frame_pack_bufs_init(&bufs); frame.payload = &origin; oframe.payload = &oorigin; nghttp2_frame_origin_init(&frame, ov, 3); payloadlen = 2 + sizeof(example) - 1 + 2 + 2 + sizeof(nghttp2) - 1; rv = nghttp2_frame_pack_origin(&bufs, &frame); assert_int(0, ==, rv); assert_size(NGHTTP2_FRAME_HDLEN + payloadlen, ==, nghttp2_bufs_len(&bufs)); rv = unpack_framebuf((nghttp2_frame *)&oframe, &bufs); assert_int(0, ==, rv); check_frame_header(payloadlen, NGHTTP2_ORIGIN, NGHTTP2_FLAG_NONE, 0, &oframe.hd); assert_size(2, ==, oorigin.nov); assert_size(sizeof(example) - 1, ==, oorigin.ov[0].origin_len); assert_memory_equal(sizeof(example) - 1, example, oorigin.ov[0].origin); assert_size(sizeof(nghttp2) - 1, ==, oorigin.ov[1].origin_len); assert_memory_equal(sizeof(nghttp2) - 1, nghttp2, oorigin.ov[1].origin); nghttp2_frame_origin_free(&oframe, mem); /* Check the case where origin length is too large */ buf = &bufs.head->buf; nghttp2_put_uint16be(buf->pos + NGHTTP2_FRAME_HDLEN, (uint16_t)(payloadlen - 1)); rv = unpack_framebuf((nghttp2_frame *)&oframe, &bufs); assert_int(NGHTTP2_ERR_FRAME_SIZE_ERROR, ==, rv); nghttp2_bufs_reset(&bufs); memset(&oframe, 0, sizeof(oframe)); memset(&oorigin, 0, sizeof(oorigin)); oframe.payload = &oorigin; /* Empty ORIGIN frame */ nghttp2_frame_origin_init(&frame, NULL, 0); rv = nghttp2_frame_pack_origin(&bufs, &frame); assert_int(0, ==, rv); assert_size(NGHTTP2_FRAME_HDLEN, ==, nghttp2_bufs_len(&bufs)); rv = unpack_framebuf((nghttp2_frame *)&oframe, &bufs); assert_int(0, ==, rv); check_frame_header(0, NGHTTP2_ORIGIN, NGHTTP2_FLAG_NONE, 0, &oframe.hd); assert_size(0, ==, oorigin.nov); assert_null(oorigin.ov); nghttp2_frame_origin_free(&oframe, mem); nghttp2_bufs_free(&bufs); } void test_nghttp2_frame_pack_priority_update(void) { nghttp2_extension frame, oframe; nghttp2_ext_priority_update priority_update, opriority_update; nghttp2_bufs bufs; int rv; size_t payloadlen; static const uint8_t field_value[] = "i,u=0"; frame_pack_bufs_init(&bufs); frame.payload = &priority_update; oframe.payload = &opriority_update; nghttp2_frame_priority_update_init(&frame, 1000000007, (uint8_t *)field_value, sizeof(field_value) - 1); payloadlen = 4 + sizeof(field_value) - 1; nghttp2_frame_pack_priority_update(&bufs, &frame); assert_size(NGHTTP2_FRAME_HDLEN + payloadlen, ==, nghttp2_bufs_len(&bufs)); rv = unpack_framebuf((nghttp2_frame *)&oframe, &bufs); assert_int(0, ==, rv); check_frame_header(payloadlen, NGHTTP2_PRIORITY_UPDATE, NGHTTP2_FLAG_NONE, 0, &oframe.hd); assert_size(sizeof(field_value) - 1, ==, opriority_update.field_value_len); assert_memory_equal(sizeof(field_value) - 1, field_value, opriority_update.field_value); nghttp2_bufs_free(&bufs); } void test_nghttp2_nv_array_copy(void) { nghttp2_nv *nva; int rv; nghttp2_nv emptynv[] = {MAKE_NV("", ""), MAKE_NV("", "")}; nghttp2_nv nv[] = {MAKE_NV("alpha", "bravo"), MAKE_NV("charlie", "delta")}; nghttp2_nv bignv; nghttp2_mem *mem; mem = nghttp2_mem_default(); bignv.name = (uint8_t *)"echo"; bignv.namelen = strlen("echo"); bignv.valuelen = (1 << 14) - 1; bignv.value = mem->malloc(bignv.valuelen, NULL); bignv.flags = NGHTTP2_NV_FLAG_NONE; memset(bignv.value, '0', bignv.valuelen); rv = nghttp2_nv_array_copy(&nva, NULL, 0, mem); assert_int(0, ==, rv); assert_null(nva); rv = nghttp2_nv_array_copy(&nva, emptynv, ARRLEN(emptynv), mem); assert_int(0, ==, rv); assert_size(0, ==, nva[0].namelen); assert_size(0, ==, nva[0].valuelen); assert_size(0, ==, nva[1].namelen); assert_size(0, ==, nva[1].valuelen); nghttp2_nv_array_del(nva, mem); rv = nghttp2_nv_array_copy(&nva, nv, ARRLEN(nv), mem); assert_int(0, ==, rv); assert_size(5, ==, nva[0].namelen); assert_memory_equal(5, "alpha", nva[0].name); assert_size(5, ==, nva[0].valuelen); assert_memory_equal(5, "bravo", nva[0].value); assert_size(7, ==, nva[1].namelen); assert_memory_equal(7, "charlie", nva[1].name); assert_size(5, ==, nva[1].valuelen); assert_memory_equal(5, "delta", nva[1].value); nghttp2_nv_array_del(nva, mem); /* Large header field is acceptable */ rv = nghttp2_nv_array_copy(&nva, &bignv, 1, mem); assert_int(0, ==, rv); nghttp2_nv_array_del(nva, mem); mem->free(bignv.value, NULL); } void test_nghttp2_iv_check(void) { nghttp2_settings_entry iv[5]; iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; iv[0].value = 100; iv[1].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[1].value = 1024; assert_true(nghttp2_iv_check(iv, 2)); iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; iv[1].value = NGHTTP2_MAX_WINDOW_SIZE; assert_true(nghttp2_iv_check(iv, 2)); /* Too large window size */ iv[1].value = (uint32_t)NGHTTP2_MAX_WINDOW_SIZE + 1; assert_false(nghttp2_iv_check(iv, 2)); /* ENABLE_PUSH only allows 0 or 1 */ iv[1].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; iv[1].value = 0; assert_true(nghttp2_iv_check(iv, 2)); iv[1].value = 1; assert_true(nghttp2_iv_check(iv, 2)); iv[1].value = 3; assert_false(nghttp2_iv_check(iv, 2)); /* Undefined SETTINGS ID is allowed */ iv[1].settings_id = 1000000009; iv[1].value = 0; assert_true(nghttp2_iv_check(iv, 2)); /* Full size SETTINGS_HEADER_TABLE_SIZE (UINT32_MAX) must be accepted */ iv[1].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[1].value = UINT32_MAX; assert_true(nghttp2_iv_check(iv, 2)); /* Too small SETTINGS_MAX_FRAME_SIZE */ iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MIN - 1; assert_false(nghttp2_iv_check(iv, 1)); /* Too large SETTINGS_MAX_FRAME_SIZE */ iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MAX + 1; assert_false(nghttp2_iv_check(iv, 1)); /* Max and min SETTINGS_MAX_FRAME_SIZE */ iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MIN; iv[1].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; iv[1].value = NGHTTP2_MAX_FRAME_SIZE_MAX; assert_true(nghttp2_iv_check(iv, 2)); } nghttp2-1.68.0/tests/PaxHeaders/malloc_wrapper.c0000644000000000000000000000013115077107271016575 xustar0030 mtime=1761382073.005444083 29 atime=1761382080.10641148 30 ctime=1761382108.062303382 nghttp2-1.68.0/tests/malloc_wrapper.c0000644000175100017510000000544615077107271017177 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "malloc_wrapper.h" int nghttp2_failmalloc = 0; int nghttp2_failstart = 0; int nghttp2_countmalloc = 1; int nghttp2_nmalloc = 0; #define CHECK_PREREQ \ do { \ if (nghttp2_failmalloc && nghttp2_nmalloc >= nghttp2_failstart) { \ return NULL; \ } \ if (nghttp2_countmalloc) { \ ++nghttp2_nmalloc; \ } \ } while (0) static void *my_malloc(size_t size, void *mud) { (void)mud; CHECK_PREREQ; return malloc(size); } static void my_free(void *ptr, void *mud) { (void)mud; free(ptr); } static void *my_calloc(size_t nmemb, size_t size, void *mud) { (void)mud; CHECK_PREREQ; return calloc(nmemb, size); } static void *my_realloc(void *ptr, size_t size, void *mud) { (void)mud; CHECK_PREREQ; return realloc(ptr, size); } static nghttp2_mem mem = {NULL, my_malloc, my_free, my_calloc, my_realloc}; nghttp2_mem *nghttp2_mem_fm(void) { return &mem; } static int failmalloc_bk, countmalloc_bk; void nghttp2_failmalloc_pause(void) { failmalloc_bk = nghttp2_failmalloc; countmalloc_bk = nghttp2_countmalloc; nghttp2_failmalloc = 0; nghttp2_countmalloc = 0; } void nghttp2_failmalloc_unpause(void) { nghttp2_failmalloc = failmalloc_bk; nghttp2_countmalloc = countmalloc_bk; } nghttp2-1.68.0/tests/PaxHeaders/nghttp2_frame_test.h0000644000000000000000000000013215077107271017373 xustar0030 mtime=1761382073.006444079 30 atime=1761382080.107411476 30 ctime=1761382108.074303347 nghttp2-1.68.0/tests/nghttp2_frame_test.h0000644000175100017510000000422315077107271017764 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_FRAME_TEST_H #define NGHTTP2_FRAME_TEST_H #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" extern const MunitSuite frame_suite; munit_void_test_decl(test_nghttp2_frame_pack_headers) munit_void_test_decl(test_nghttp2_frame_pack_headers_frame_too_large) munit_void_test_decl(test_nghttp2_frame_pack_priority) munit_void_test_decl(test_nghttp2_frame_pack_rst_stream) munit_void_test_decl(test_nghttp2_frame_pack_settings) munit_void_test_decl(test_nghttp2_frame_pack_push_promise) munit_void_test_decl(test_nghttp2_frame_pack_ping) munit_void_test_decl(test_nghttp2_frame_pack_goaway) munit_void_test_decl(test_nghttp2_frame_pack_window_update) munit_void_test_decl(test_nghttp2_frame_pack_altsvc) munit_void_test_decl(test_nghttp2_frame_pack_origin) munit_void_test_decl(test_nghttp2_frame_pack_priority_update) munit_void_test_decl(test_nghttp2_nv_array_copy) munit_void_test_decl(test_nghttp2_iv_check) #endif /* NGHTTP2_FRAME_TEST_H */ nghttp2-1.68.0/tests/PaxHeaders/Makefile.am0000644000000000000000000000013115077107271015456 xustar0030 mtime=1761382073.002444097 29 atime=1761382080.10641148 30 ctime=1761382108.054303405 nghttp2-1.68.0/tests/Makefile.am0000644000175100017510000000561415077107271016055 0ustar00runnerrunner# nghttp2 - HTTP/2 C Library # Copyright (c) 2012 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SUBDIRS = testdata EXTRA_DIST = CMakeLists.txt munit/COPYING check_PROGRAMS = main if ENABLE_FAILMALLOC check_PROGRAMS += failmalloc endif # ENABLE_FAILMALLOC OBJECTS = main.c nghttp2_pq_test.c nghttp2_map_test.c nghttp2_queue_test.c \ nghttp2_test_helper.c \ nghttp2_frame_test.c \ nghttp2_stream_test.c \ nghttp2_session_test.c \ nghttp2_hd_test.c \ nghttp2_alpn_test.c \ nghttp2_helper_test.c \ nghttp2_buf_test.c \ nghttp2_http_test.c \ nghttp2_extpri_test.c \ nghttp2_ratelim_test.c \ munit/munit.c HFILES = nghttp2_pq_test.h nghttp2_map_test.h nghttp2_queue_test.h \ nghttp2_session_test.h \ nghttp2_frame_test.h nghttp2_stream_test.h nghttp2_hd_test.h \ nghttp2_alpn_test.h nghttp2_helper_test.h \ nghttp2_assertion.h \ nghttp2_test_helper.h \ nghttp2_buf_test.h \ nghttp2_http_test.h \ nghttp2_extpri_test.h \ nghttp2_ratelim_test.h \ munit/munit.h main_SOURCES = $(HFILES) $(OBJECTS) if ENABLE_STATIC main_LDADD = ${top_builddir}/lib/libnghttp2.la else # With static lib disabled and symbol hiding enabled, we have to link object # files directly because the tests use symbols not included in public API. main_LDADD = ${top_builddir}/lib/.libs/*.o endif main_LDADD += @TESTLDADD@ main_LDFLAGS = -static if ENABLE_FAILMALLOC failmalloc_SOURCES = failmalloc.c failmalloc_test.c failmalloc_test.h \ malloc_wrapper.c malloc_wrapper.h \ nghttp2_test_helper.c nghttp2_test_helper.h \ munit/munit.c munit/munit.h failmalloc_LDADD = $(main_LDADD) failmalloc_LDFLAGS = $(main_LDFLAGS) endif # ENABLE_FAILMALLOC AM_CFLAGS = $(WARNCFLAGS) \ -I${top_srcdir}/lib \ -I${top_srcdir}/lib/includes \ -I${top_srcdir}/tests/munit \ -I${top_builddir}/lib/includes \ -DBUILDING_NGHTTP2 \ -DNGHTTP2_STATICLIB \ @DEFS@ TESTS = main if ENABLE_FAILMALLOC TESTS += failmalloc endif # ENABLE_FAILMALLOC nghttp2-1.68.0/tests/PaxHeaders/testdata0000644000000000000000000000013215077107334015157 xustar0030 mtime=1761382108.139303159 30 atime=1761382109.800298358 30 ctime=1761382108.139303159 nghttp2-1.68.0/tests/testdata/0000755000175100017510000000000015077107334015624 5ustar00runnerrunnernghttp2-1.68.0/tests/testdata/PaxHeaders/cacert.pem0000644000000000000000000000013215077107271017200 xustar0030 mtime=1761382073.008444069 30 atime=1761382080.108411471 30 ctime=1761382108.136303168 nghttp2-1.68.0/tests/testdata/cacert.pem0000644000175100017510000000145215077107271017572 0ustar00runnerrunner-----BEGIN CERTIFICATE----- MIICKTCCAdOgAwIBAgIJAIsolheWrwMZMA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV BAYTAlVTMQswCQYDVQQIDAJDQTENMAsGA1UEBwwEQ2l0eTESMBAGA1UECgwJU3Bk eSBUZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHTAbBgkqhkiG9w0BCQEWDnNwZHlA bG9jYWxob3N0MB4XDTEyMDMwMTE5MTI0NVoXDTIzMDUxOTE5MTI0NVowcDELMAkG A1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ0wCwYDVQQHDARDaXR5MRIwEAYDVQQKDAlT cGR5IFRlc3QxEjAQBgNVBAMMCWxvY2FsaG9zdDEdMBsGCSqGSIb3DQEJARYOc3Bk eUBsb2NhbGhvc3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAw/2MgzAdlJDm29qH ZlAibgs9mH+8keOtsRrb4B1PiCcZoHvN9eCVZ4WnzT+0zhHF+nO3YfwVFVC3w7TF 7fLB3QIDAQABo1AwTjAdBgNVHQ4EFgQUVP2Jw9RX6BB76aV5x2qk5qsrAIQwHwYD VR0jBBgwFoAUVP2Jw9RX6BB76aV5x2qk5qsrAIQwDAYDVR0TBAUwAwEB/zANBgkq hkiG9w0BAQUFAANBAKd9M5FzQLEZW1KPe9/XNZlgxZ2g3EC5Krxo5I4Ul3MnIYS9 u4K8t/iprhgOzjFH6+8LVk9v0Za+gU+K43CpUo4= -----END CERTIFICATE----- nghttp2-1.68.0/tests/testdata/PaxHeaders/index.html0000644000000000000000000000013215077107271017231 xustar0030 mtime=1761382073.008444069 30 atime=1761382080.108411471 30 ctime=1761382108.138303162 nghttp2-1.68.0/tests/testdata/index.html0000644000175100017510000000004015077107271017613 0ustar00runnerrunnersmall nghttp2-1.68.0/tests/testdata/PaxHeaders/privkey.pem0000644000000000000000000000013215077107271017430 xustar0030 mtime=1761382073.008444069 30 atime=1761382080.108411471 30 ctime=1761382108.139303159 nghttp2-1.68.0/tests/testdata/privkey.pem0000644000175100017510000000076115077107271020024 0ustar00runnerrunner-----BEGIN RSA PRIVATE KEY----- MIIBOwIBAAJBAMP9jIMwHZSQ5tvah2ZQIm4LPZh/vJHjrbEa2+AdT4gnGaB7zfXg lWeFp80/tM4Rxfpzt2H8FRVQt8O0xe3ywd0CAwEAAQJBAIQ8PGP/QNYOdlT8OsLj aneJCgQsm1Rro7ONBbFO1WxslvA6+uJsx4Rs8zLiS8cyqmJ/lmGa7zhwYSOvFQPa XgECIQDgIcgM/2C67peTm1diKKIoGVVKFCfdRi+Dje6mTl2TQQIhAN/bcFWbG73j cUVlIsr9Wk1dJzjPPWKeyirF1qd/WbOdAiEApTsCOeLCssxV3jF02B5QfPNAFx6I zO2C9Z7awque/IECIGCHW3VOoTPMs7dc2Rf3D810cclJdArmtf6juOAZRjDxAiBS AC+H685IBJ99N5nCbF9NWYIVSkuiKVQ8POYVZX+0Jg== -----END RSA PRIVATE KEY----- nghttp2-1.68.0/tests/testdata/PaxHeaders/Makefile.in0000644000000000000000000000013115077107305017276 xustar0030 mtime=1761382085.810386125 29 atime=1761382103.84732081 30 ctime=1761382108.135303171 nghttp2-1.68.0/tests/testdata/Makefile.in0000644000175100017510000003742015077107305017675 0ustar00runnerrunner# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # nghttp2 - HTTP/2 C Library # Copyright (c) 2012 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = tests/testdata ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPLDFLAGS = @APPLDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BPFCFLAGS = @BPFCFLAGS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CXX = @CXX@ CXX1XCXXFLAGS = @CXX1XCXXFLAGS@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXTRABPFCFLAGS = @EXTRABPFCFLAGS@ EXTRACFLAG = @EXTRACFLAG@ EXTRA_DEFS = @EXTRA_DEFS@ FGREP = @FGREP@ FILECMD = @FILECMD@ GREP = @GREP@ HAVE_CXX20 = @HAVE_CXX20@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ JANSSON_CFLAGS = @JANSSON_CFLAGS@ JANSSON_LIBS = @JANSSON_LIBS@ JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ JEMALLOC_LIBS = @JEMALLOC_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBBPF_CFLAGS = @LIBBPF_CFLAGS@ LIBBPF_LIBS = @LIBBPF_LIBS@ LIBBROTLIDEC_CFLAGS = @LIBBROTLIDEC_CFLAGS@ LIBBROTLIDEC_LIBS = @LIBBROTLIDEC_LIBS@ LIBBROTLIENC_CFLAGS = @LIBBROTLIENC_CFLAGS@ LIBBROTLIENC_LIBS = @LIBBROTLIENC_LIBS@ LIBCARES_CFLAGS = @LIBCARES_CFLAGS@ LIBCARES_LIBS = @LIBCARES_LIBS@ LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ LIBEV_CFLAGS = @LIBEV_CFLAGS@ LIBEV_LIBS = @LIBEV_LIBS@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@ LIBMRUBY_LIBS = @LIBMRUBY_LIBS@ LIBNGHTTP3_CFLAGS = @LIBNGHTTP3_CFLAGS@ LIBNGHTTP3_LIBS = @LIBNGHTTP3_LIBS@ LIBNGTCP2_CFLAGS = @LIBNGTCP2_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS = @LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_LIBS = @LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS = @LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS@ LIBNGTCP2_CRYPTO_LIBRESSL_LIBS = @LIBNGTCP2_CRYPTO_LIBRESSL_LIBS@ LIBNGTCP2_CRYPTO_OSSL_CFLAGS = @LIBNGTCP2_CRYPTO_OSSL_CFLAGS@ LIBNGTCP2_CRYPTO_OSSL_LIBS = @LIBNGTCP2_CRYPTO_OSSL_LIBS@ LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS = @LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ LIBNGTCP2_CRYPTO_QUICTLS_LIBS = @LIBNGTCP2_CRYPTO_QUICTLS_LIBS@ LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS = @LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS@ LIBNGTCP2_CRYPTO_WOLFSSL_LIBS = @LIBNGTCP2_CRYPTO_WOLFSSL_LIBS@ LIBNGTCP2_LIBS = @LIBNGTCP2_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBTOOL_LDFLAGS = @LIBTOOL_LDFLAGS@ LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ LIBXML2_LIBS = @LIBXML2_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_AGE = @LT_AGE@ LT_CURRENT = @LT_CURRENT@ LT_REVISION = @LT_REVISION@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ SYSTEMD_LIBS = @SYSTEMD_LIBS@ TESTLDADD = @TESTLDADD@ VERSION = @VERSION@ WARNCFLAGS = @WARNCFLAGS@ WARNCXXFLAGS = @WARNCXXFLAGS@ WOLFSSL_CFLAGS = @WOLFSSL_CFLAGS@ WOLFSSL_LIBS = @WOLFSSL_LIBS@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. EXTRA_DIST = cacert.pem index.html privkey.pem all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tests/testdata/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu tests/testdata/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nghttp2-1.68.0/tests/testdata/PaxHeaders/Makefile.am0000644000000000000000000000013215077107271017270 xustar0030 mtime=1761382073.008444069 30 atime=1761382080.108411471 30 ctime=1761382108.134303173 nghttp2-1.68.0/tests/testdata/Makefile.am0000644000175100017510000000223015077107271017655 0ustar00runnerrunner# nghttp2 - HTTP/2 C Library # Copyright (c) 2012 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. EXTRA_DIST = cacert.pem index.html privkey.pem nghttp2-1.68.0/PaxHeaders/cmake0000644000000000000000000000013215077107333013263 xustar0030 mtime=1761382107.879303911 30 atime=1761382109.800298358 30 ctime=1761382107.879303911 nghttp2-1.68.0/cmake/0000755000175100017510000000000015077107333013730 5ustar00runnerrunnernghttp2-1.68.0/cmake/PaxHeaders/PickyWarningsC.cmake0000644000000000000000000000013215077107270017235 xustar0030 mtime=1761382072.962444282 30 atime=1761382104.893316202 30 ctime=1761382107.878303913 nghttp2-1.68.0/cmake/PickyWarningsC.cmake0000644000175100017510000001537615077107270017641 0ustar00runnerrunner# nghttp2 # # Copyright (c) 2023 nghttp2 contributors # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # C include(CheckCCompilerFlag) if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER_ID MATCHES "Clang") # https://clang.llvm.org/docs/DiagnosticsReference.html # https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html # WPICKY_ENABLE = Options we want to enable as-is. # WPICKY_DETECT = Options we want to test first and enable if available. # Prefer the -Wextra alias with clang. if(CMAKE_C_COMPILER_ID MATCHES "Clang") set(WPICKY_ENABLE "-Wextra") else() set(WPICKY_ENABLE "-W") endif() list(APPEND WPICKY_ENABLE -Wall ) # ---------------------------------- # Add new options here, if in doubt: # ---------------------------------- set(WPICKY_DETECT ) # Assume these options always exist with both clang and gcc. # Require clang 3.0 / gcc 2.95 or later. list(APPEND WPICKY_ENABLE -Wconversion # clang 3.0 gcc 2.95 -Winline # clang 1.0 gcc 1.0 -Wmissing-declarations # clang 1.0 gcc 2.7 -Wmissing-prototypes # clang 1.0 gcc 1.0 -Wnested-externs # clang 1.0 gcc 2.7 -Wpointer-arith # clang 1.0 gcc 1.4 -Wshadow # clang 1.0 gcc 2.95 -Wundef # clang 1.0 gcc 2.95 -Wwrite-strings # clang 1.0 gcc 1.4 ) # Always enable with clang, version dependent with gcc set(WPICKY_COMMON_OLD -Waddress # clang 3.0 gcc 4.3 -Wattributes # clang 3.0 gcc 4.1 -Wcast-align # clang 1.0 gcc 4.2 -Wdeclaration-after-statement # clang 1.0 gcc 3.4 -Wdiv-by-zero # clang 3.0 gcc 4.1 -Wempty-body # clang 3.0 gcc 4.3 -Wendif-labels # clang 1.0 gcc 3.3 -Wfloat-equal # clang 1.0 gcc 2.96 (3.0) -Wformat-nonliteral # clang 3.0 gcc 4.1 -Wformat-security # clang 3.0 gcc 4.1 -Wmissing-field-initializers # clang 3.0 gcc 4.1 -Wmissing-noreturn # clang 3.0 gcc 4.1 -Wno-format-nonliteral # clang 1.0 gcc 2.96 (3.0) # This is required because we pass format string as "const char*" # -Wpadded # clang 3.0 gcc 4.1 # Not used because we cannot change public structs -Wredundant-decls # clang 3.0 gcc 4.1 -Wsign-conversion # clang 3.0 gcc 4.3 -Wstrict-prototypes # clang 1.0 gcc 3.3 # -Wswitch-enum # clang 3.0 gcc 4.1 # Not used because this basically disallows default case -Wunreachable-code # clang 3.0 gcc 4.1 -Wunused-parameter # clang 3.0 gcc 4.1 -Wvla # clang 2.8 gcc 4.3 ) set(WPICKY_COMMON -Wpragmas # clang 3.5 gcc 4.1 appleclang 6.0 ) if(CMAKE_C_COMPILER_ID MATCHES "Clang") list(APPEND WPICKY_ENABLE ${WPICKY_COMMON_OLD} -Wshorten-64-to-32 # clang 1.0 -Wlanguage-extension-token # clang 3.0 ) # Enable based on compiler version if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.6) OR (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.3)) list(APPEND WPICKY_ENABLE ${WPICKY_COMMON} -Wunreachable-code-break # clang 3.5 appleclang 6.0 -Wheader-guard # clang 3.4 appleclang 5.1 ) endif() if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.9) OR (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 8.3)) list(APPEND WPICKY_ENABLE -Wmissing-variable-declarations # clang 3.2 appleclang 4.6 ) endif() if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0) OR (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 9.4)) list(APPEND WPICKY_ENABLE ) endif() if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 7.0) OR (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 10.3)) list(APPEND WPICKY_ENABLE ) endif() else() # gcc list(APPEND WPICKY_DETECT ${WPICKY_COMMON} ) # Enable based on compiler version if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.3) list(APPEND WPICKY_ENABLE ${WPICKY_COMMON_OLD} -Wclobbered # gcc 4.3 ) endif() endif() # unset(_wpicky) foreach(_CCOPT IN LISTS WPICKY_ENABLE) set(_wpicky "${_wpicky} ${_CCOPT}") endforeach() foreach(_CCOPT IN LISTS WPICKY_DETECT) # surprisingly, CHECK_C_COMPILER_FLAG needs a new variable to store each new # test result in. string(MAKE_C_IDENTIFIER "OPT${_CCOPT}" _optvarname) # GCC only warns about unknown -Wno- options if there are also other diagnostic messages, # so test for the positive form instead string(REPLACE "-Wno-" "-W" _CCOPT_ON "${_CCOPT}") check_c_compiler_flag(${_CCOPT_ON} ${_optvarname}) if(${_optvarname}) set(_wpicky "${_wpicky} ${_CCOPT}") endif() endforeach() set(WARNCFLAGS "${WARNCFLAGS} ${_wpicky}") endif() nghttp2-1.68.0/cmake/PaxHeaders/FindLibngtcp2_crypto_wolfssl.cmake0000644000000000000000000000013115077107270022137 xustar0030 mtime=1761382072.962444282 29 atime=1761382104.88931622 30 ctime=1761382107.874303925 nghttp2-1.68.0/cmake/FindLibngtcp2_crypto_wolfssl.cmake0000644000175100017510000000361615077107270022536 0ustar00runnerrunner# - Try to find libngtcp2_crypto_wolfssl # Once done this will define # LIBNGTCP2_CRYPTO_WOLFSSL_FOUND - System has libngtcp2_crypto_wolfssl # LIBNGTCP2_CRYPTO_WOLFSSL_INCLUDE_DIRS - The libngtcp2_crypto_wolfssl include directories # LIBNGTCP2_CRYPTO_WOLFSSL_LIBRARIES - The libraries needed to use libngtcp2_crypto_wolfssl find_package(PkgConfig QUIET) pkg_check_modules(PC_LIBNGTCP2_CRYPTO_WOLFSSL QUIET libngtcp2_crypto_wolfssl) find_path(LIBNGTCP2_CRYPTO_WOLFSSL_INCLUDE_DIR NAMES ngtcp2/ngtcp2_crypto_wolfssl.h HINTS ${PC_LIBNGTCP2_CRYPTO_WOLFSSL_INCLUDE_DIRS} ) find_library(LIBNGTCP2_CRYPTO_WOLFSSL_LIBRARY NAMES ngtcp2_crypto_wolfssl HINTS ${PC_LIBNGTCP2_CRYPTO_WOLFSSL_LIBRARY_DIRS} ) if(LIBNGTCP2_CRYPTO_WOLFSSL_INCLUDE_DIR) set(_version_regex "^#define[ \t]+NGTCP2_VERSION[ \t]+\"([^\"]+)\".*") file(STRINGS "${LIBNGTCP2_CRYPTO_WOLFSSL_INCLUDE_DIR}/ngtcp2/version.h" LIBNGTCP2_CRYPTO_WOLFSSL_VERSION REGEX "${_version_regex}") string(REGEX REPLACE "${_version_regex}" "\\1" LIBNGTCP2_CRYPTO_WOLFSSL_VERSION "${LIBNGTCP2_CRYPTO_WOLFSSL_VERSION}") unset(_version_regex) endif() include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set # LIBNGTCP2_CRYPTO_WOLFSSL_FOUND to TRUE if all listed variables are # TRUE and the requested version matches. find_package_handle_standard_args(Libngtcp2_crypto_wolfssl REQUIRED_VARS LIBNGTCP2_CRYPTO_WOLFSSL_LIBRARY LIBNGTCP2_CRYPTO_WOLFSSL_INCLUDE_DIR VERSION_VAR LIBNGTCP2_CRYPTO_WOLFSSL_VERSION) if(LIBNGTCP2_CRYPTO_WOLFSSL_FOUND) set(LIBNGTCP2_CRYPTO_WOLFSSL_LIBRARIES ${LIBNGTCP2_CRYPTO_WOLFSSL_LIBRARY}) set(LIBNGTCP2_CRYPTO_WOLFSSL_INCLUDE_DIRS ${LIBNGTCP2_CRYPTO_WOLFSSL_INCLUDE_DIR}) endif() mark_as_advanced(LIBNGTCP2_CRYPTO_WOLFSSL_INCLUDE_DIR LIBNGTCP2_CRYPTO_WOLFSSL_LIBRARY) nghttp2-1.68.0/cmake/PaxHeaders/Version.cmake0000644000000000000000000000013215077107270015767 xustar0030 mtime=1761382072.962444282 30 atime=1761382104.875316281 30 ctime=1761382107.860303966 nghttp2-1.68.0/cmake/Version.cmake0000644000175100017510000000073615077107270016365 0ustar00runnerrunner# Converts a version such as 1.2.255 to 0x0102ff function(HexVersion version_hex_var major minor patch) math(EXPR version_dec "${major} * 256 * 256 + ${minor} * 256 + ${patch}") set(version_hex "0x") foreach(i RANGE 5 0 -1) math(EXPR num "(${version_dec} >> (4 * ${i})) & 15") string(SUBSTRING "0123456789abcdef" ${num} 1 num_hex) set(version_hex "${version_hex}${num_hex}") endforeach() set(${version_hex_var} "${version_hex}" PARENT_SCOPE) endfunction() nghttp2-1.68.0/cmake/PaxHeaders/FindJansson.cmake0000644000000000000000000000013215077107270016556 xustar0030 mtime=1761382072.962444282 30 atime=1761382104.877316273 30 ctime=1761382107.863303957 nghttp2-1.68.0/cmake/FindJansson.cmake0000644000175100017510000000246715077107270017157 0ustar00runnerrunner# - Try to find jansson # Once done this will define # JANSSON_FOUND - System has jansson # JANSSON_INCLUDE_DIRS - The jansson include directories # JANSSON_LIBRARIES - The libraries needed to use jansson find_package(PkgConfig QUIET) pkg_check_modules(PC_JANSSON QUIET jansson) find_path(JANSSON_INCLUDE_DIR NAMES jansson.h HINTS ${PC_JANSSON_INCLUDE_DIRS} ) find_library(JANSSON_LIBRARY NAMES jansson HINTS ${PC_JANSSON_LIBRARY_DIRS} ) if(JANSSON_INCLUDE_DIR) set(_version_regex "^#define[ \t]+JANSSON_VERSION[ \t]+\"([^\"]+)\".*") file(STRINGS "${JANSSON_INCLUDE_DIR}/jansson.h" JANSSON_VERSION REGEX "${_version_regex}") string(REGEX REPLACE "${_version_regex}" "\\1" JANSSON_VERSION "${JANSSON_VERSION}") unset(_version_regex) endif() include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set JANSSON_FOUND to TRUE # if all listed variables are TRUE and the requested version matches. find_package_handle_standard_args(Jansson REQUIRED_VARS JANSSON_LIBRARY JANSSON_INCLUDE_DIR VERSION_VAR JANSSON_VERSION) if(JANSSON_FOUND) set(JANSSON_LIBRARIES ${JANSSON_LIBRARY}) set(JANSSON_INCLUDE_DIRS ${JANSSON_INCLUDE_DIR}) endif() mark_as_advanced(JANSSON_INCLUDE_DIR JANSSON_LIBRARY) nghttp2-1.68.0/cmake/PaxHeaders/FindLibevent.cmake0000644000000000000000000000013215077107270016713 xustar0030 mtime=1761382072.962444282 30 atime=1761382104.876316277 30 ctime=1761382107.861303963 nghttp2-1.68.0/cmake/FindLibevent.cmake0000644000175100017510000000660215077107270017307 0ustar00runnerrunner# - Try to find libevent #.rst # FindLibevent # ------------ # # Find Libevent include directories and libraries. Invoke as:: # # find_package(Libevent # [version] [EXACT] # Minimum or exact version # [REQUIRED] # Fail if Libevent is not found # [COMPONENT ...]) # Libraries to look for # # Valid components are one or more of:: libevent core extra pthreads openssl. # Note that 'libevent' contains both core and extra. You must specify one of # them for the other components. # # This module will define the following variables:: # # LIBEVENT_FOUND - True if headers and requested libraries were found # LIBEVENT_INCLUDE_DIRS - Libevent include directories # LIBEVENT_LIBRARIES - Libevent libraries to be linked # LIBEVENT__FOUND - Component was found ( is uppercase) # LIBEVENT__LIBRARY - Library to be linked for Libevent component . find_package(PkgConfig QUIET) pkg_check_modules(PC_LIBEVENT QUIET libevent) # Look for the Libevent 2.0 or 1.4 headers find_path(LIBEVENT_INCLUDE_DIR NAMES event2/event-config.h event-config.h HINTS ${PC_LIBEVENT_INCLUDE_DIRS} ) if(LIBEVENT_INCLUDE_DIR) set(_version_regex "^#define[ \t]+_EVENT_VERSION[ \t]+\"([^\"]+)\".*") if(EXISTS "${LIBEVENT_INCLUDE_DIR}/event2/event-config.h") # Libevent 2.0 file(STRINGS "${LIBEVENT_INCLUDE_DIR}/event2/event-config.h" LIBEVENT_VERSION REGEX "${_version_regex}") if("${LIBEVENT_VERSION}" STREQUAL "") set(LIBEVENT_VERSION ${PC_LIBEVENT_VERSION}) endif() else() # Libevent 1.4 file(STRINGS "${LIBEVENT_INCLUDE_DIR}/event-config.h" LIBEVENT_VERSION REGEX "${_version_regex}") endif() string(REGEX REPLACE "${_version_regex}" "\\1" LIBEVENT_VERSION "${LIBEVENT_VERSION}") unset(_version_regex) endif() set(_LIBEVENT_REQUIRED_VARS) foreach(COMPONENT ${Libevent_FIND_COMPONENTS}) set(_LIBEVENT_LIBNAME libevent) # Note: compare two variables to avoid a CMP0054 policy warning if(COMPONENT STREQUAL _LIBEVENT_LIBNAME) set(_LIBEVENT_LIBNAME event) else() set(_LIBEVENT_LIBNAME "event_${COMPONENT}") endif() string(TOUPPER "${COMPONENT}" COMPONENT_UPPER) find_library(LIBEVENT_${COMPONENT_UPPER}_LIBRARY NAMES ${_LIBEVENT_LIBNAME} HINTS ${PC_LIBEVENT_LIBRARY_DIRS} ) if(LIBEVENT_${COMPONENT_UPPER}_LIBRARY) set(Libevent_${COMPONENT}_FOUND 1) endif() list(APPEND _LIBEVENT_REQUIRED_VARS LIBEVENT_${COMPONENT_UPPER}_LIBRARY) endforeach() unset(_LIBEVENT_LIBNAME) include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBEVENT_FOUND to TRUE # if all listed variables are TRUE and the requested version matches. find_package_handle_standard_args(Libevent REQUIRED_VARS ${_LIBEVENT_REQUIRED_VARS} LIBEVENT_INCLUDE_DIR VERSION_VAR LIBEVENT_VERSION HANDLE_COMPONENTS) if(LIBEVENT_FOUND) set(LIBEVENT_INCLUDE_DIRS ${LIBEVENT_INCLUDE_DIR}) set(LIBEVENT_LIBRARIES) foreach(COMPONENT ${Libevent_FIND_COMPONENTS}) string(TOUPPER "${COMPONENT}" COMPONENT_UPPER) list(APPEND LIBEVENT_LIBRARIES ${LIBEVENT_${COMPONENT_UPPER}_LIBRARY}) set(LIBEVENT_${COMPONENT_UPPER}_FOUND ${Libevent_${COMPONENT}_FOUND}) endforeach() endif() mark_as_advanced(LIBEVENT_INCLUDE_DIR ${_LIBEVENT_REQUIRED_VARS}) unset(_LIBEVENT_REQUIRED_VARS) nghttp2-1.68.0/cmake/PaxHeaders/FindLibngtcp2_crypto_quictls.cmake0000644000000000000000000000013215077107270022133 xustar0030 mtime=1761382072.962444282 30 atime=1761382104.885316238 30 ctime=1761382107.870303937 nghttp2-1.68.0/cmake/FindLibngtcp2_crypto_quictls.cmake0000644000175100017510000000361615077107270022531 0ustar00runnerrunner# - Try to find libngtcp2_crypto_quictls # Once done this will define # LIBNGTCP2_CRYPTO_QUICTLS_FOUND - System has libngtcp2_crypto_quictls # LIBNGTCP2_CRYPTO_QUICTLS_INCLUDE_DIRS - The libngtcp2_crypto_quictls include directories # LIBNGTCP2_CRYPTO_QUICTLS_LIBRARIES - The libraries needed to use libngtcp2_crypto_quictls find_package(PkgConfig QUIET) pkg_check_modules(PC_LIBNGTCP2_CRYPTO_QUICTLS QUIET libngtcp2_crypto_quictls) find_path(LIBNGTCP2_CRYPTO_QUICTLS_INCLUDE_DIR NAMES ngtcp2/ngtcp2_crypto_quictls.h HINTS ${PC_LIBNGTCP2_CRYPTO_QUICTLS_INCLUDE_DIRS} ) find_library(LIBNGTCP2_CRYPTO_QUICTLS_LIBRARY NAMES ngtcp2_crypto_quictls HINTS ${PC_LIBNGTCP2_CRYPTO_QUICTLS_LIBRARY_DIRS} ) if(LIBNGTCP2_CRYPTO_QUICTLS_INCLUDE_DIR) set(_version_regex "^#define[ \t]+NGTCP2_VERSION[ \t]+\"([^\"]+)\".*") file(STRINGS "${LIBNGTCP2_CRYPTO_QUICTLS_INCLUDE_DIR}/ngtcp2/version.h" LIBNGTCP2_CRYPTO_QUICTLS_VERSION REGEX "${_version_regex}") string(REGEX REPLACE "${_version_regex}" "\\1" LIBNGTCP2_CRYPTO_QUICTLS_VERSION "${LIBNGTCP2_CRYPTO_QUICTLS_VERSION}") unset(_version_regex) endif() include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set # LIBNGTCP2_CRYPTO_QUICTLS_FOUND to TRUE if all listed variables are # TRUE and the requested version matches. find_package_handle_standard_args(Libngtcp2_crypto_quictls REQUIRED_VARS LIBNGTCP2_CRYPTO_QUICTLS_LIBRARY LIBNGTCP2_CRYPTO_QUICTLS_INCLUDE_DIR VERSION_VAR LIBNGTCP2_CRYPTO_QUICTLS_VERSION) if(LIBNGTCP2_CRYPTO_QUICTLS_FOUND) set(LIBNGTCP2_CRYPTO_QUICTLS_LIBRARIES ${LIBNGTCP2_CRYPTO_QUICTLS_LIBRARY}) set(LIBNGTCP2_CRYPTO_QUICTLS_INCLUDE_DIRS ${LIBNGTCP2_CRYPTO_QUICTLS_INCLUDE_DIR}) endif() mark_as_advanced(LIBNGTCP2_CRYPTO_QUICTLS_INCLUDE_DIR LIBNGTCP2_CRYPTO_QUICTLS_LIBRARY) nghttp2-1.68.0/cmake/PaxHeaders/FindJemalloc.cmake0000644000000000000000000000013215077107270016671 xustar0030 mtime=1761382072.962444282 30 atime=1761382104.872316295 30 ctime=1761382107.857303974 nghttp2-1.68.0/cmake/FindJemalloc.cmake0000644000175100017510000000255115077107270017264 0ustar00runnerrunner# - Try to find jemalloc # Once done this will define # JEMALLOC_FOUND - System has jemalloc # JEMALLOC_INCLUDE_DIRS - The jemalloc include directories # JEMALLOC_LIBRARIES - The libraries needed to use jemalloc find_package(PkgConfig QUIET) pkg_check_modules(PC_JEMALLOC QUIET jemalloc) find_path(JEMALLOC_INCLUDE_DIR NAMES jemalloc/jemalloc.h HINTS ${PC_JEMALLOC_INCLUDE_DIRS} ) find_library(JEMALLOC_LIBRARY NAMES jemalloc HINTS ${PC_JEMALLOC_LIBRARY_DIRS} ) if(JEMALLOC_INCLUDE_DIR) set(_version_regex "^#define[ \t]+JEMALLOC_VERSION[ \t]+\"([^\"]+)\".*") file(STRINGS "${JEMALLOC_INCLUDE_DIR}/jemalloc/jemalloc.h" JEMALLOC_VERSION REGEX "${_version_regex}") string(REGEX REPLACE "${_version_regex}" "\\1" JEMALLOC_VERSION "${JEMALLOC_VERSION}") unset(_version_regex) endif() include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set JEMALLOC_FOUND to TRUE # if all listed variables are TRUE and the requested version matches. find_package_handle_standard_args(Jemalloc REQUIRED_VARS JEMALLOC_LIBRARY JEMALLOC_INCLUDE_DIR VERSION_VAR JEMALLOC_VERSION) if(JEMALLOC_FOUND) set(JEMALLOC_LIBRARIES ${JEMALLOC_LIBRARY}) set(JEMALLOC_INCLUDE_DIRS ${JEMALLOC_INCLUDE_DIR}) endif() mark_as_advanced(JEMALLOC_INCLUDE_DIR JEMALLOC_LIBRARY) nghttp2-1.68.0/cmake/PaxHeaders/FindLibbrotlienc.cmake0000644000000000000000000000013215077107270017553 xustar0030 mtime=1761382072.962444282 30 atime=1761382104.886316233 30 ctime=1761382107.872303931 nghttp2-1.68.0/cmake/FindLibbrotlienc.cmake0000644000175100017510000000233115077107270020142 0ustar00runnerrunner# - Try to find libbrotlienc # Once done this will define # LIBBROTLIENC_FOUND - System has libbrotlienc # LIBBROTLIENC_INCLUDE_DIRS - The libbrotlienc include directories # LIBBROTLIENC_LIBRARIES - The libraries needed to use libbrotlienc find_package(PkgConfig QUIET) pkg_check_modules(PC_LIBBROTLIENC QUIET libbrotlienc) find_path(LIBBROTLIENC_INCLUDE_DIR NAMES brotli/encode.h HINTS ${PC_LIBBROTLIENC_INCLUDE_DIRS} ) find_library(LIBBROTLIENC_LIBRARY NAMES brotlienc HINTS ${PC_LIBBROTLIENC_LIBRARY_DIRS} ) if(PC_LIBBROTLIENC_FOUND) set(LIBBROTLIENC_VERSION ${PC_LIBBROTLIENC_VERSION}) endif() include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBBROTLIENC_FOUND # to TRUE if all listed variables are TRUE and the requested version # matches. find_package_handle_standard_args(Libbrotlienc REQUIRED_VARS LIBBROTLIENC_LIBRARY LIBBROTLIENC_INCLUDE_DIR VERSION_VAR LIBBROTLIENC_VERSION) if(LIBBROTLIENC_FOUND) set(LIBBROTLIENC_LIBRARIES ${LIBBROTLIENC_LIBRARY}) set(LIBBROTLIENC_INCLUDE_DIRS ${LIBBROTLIENC_INCLUDE_DIR}) endif() mark_as_advanced(LIBBROTLIENC_INCLUDE_DIR LIBBROTLIENC_LIBRARY) nghttp2-1.68.0/cmake/PaxHeaders/PickyWarningsCXX.cmake0000644000000000000000000000013215077107270017515 xustar0030 mtime=1761382072.962444282 30 atime=1761382104.894316198 30 ctime=1761382107.879303911 nghttp2-1.68.0/cmake/PickyWarningsCXX.cmake0000644000175100017510000001036715077107270020114 0ustar00runnerrunner# nghttp2 # # Copyright (c) 2023 nghttp2 contributors # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # C++ include(CheckCXXCompilerFlag) if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") # https://clang.llvm.org/docs/DiagnosticsReference.html # https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html # WPICKY_ENABLE = Options we want to enable as-is. # WPICKY_DETECT = Options we want to test first and enable if available. set(WPICKY_ENABLE "-Wall") # ---------------------------------- # Add new options here, if in doubt: # ---------------------------------- set(WPICKY_DETECT ) # Assume these options always exist with both clang and gcc. # Require clang 3.0 / gcc 2.95 or later. list(APPEND WPICKY_ENABLE ) # Always enable with clang, version dependent with gcc set(WPICKY_COMMON_OLD -Wformat-security # clang 3.0 gcc 4.1 ) set(WPICKY_COMMON ) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") list(APPEND WPICKY_ENABLE ${WPICKY_COMMON_OLD} ) list(APPEND WPICKY_ENABLE # clang++-18 warns this when building with wolfSSL >= v5.7.6-stable. -Wno-extern-c-compat ) # Enable based on compiler version if((CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.6) OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.3)) list(APPEND WPICKY_ENABLE ${WPICKY_COMMON} ) endif() if((CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.9) OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.3)) list(APPEND WPICKY_ENABLE ) endif() if((CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.4)) list(APPEND WPICKY_ENABLE ) endif() if((CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0) OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.3)) list(APPEND WPICKY_ENABLE ) endif() else() # gcc list(APPEND WPICKY_DETECT ${WPICKY_COMMON} ) # Enable based on compiler version if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.3) list(APPEND WPICKY_ENABLE ${WPICKY_COMMON_OLD} ) endif() endif() # unset(_wpicky) foreach(_CCOPT IN LISTS WPICKY_ENABLE) set(_wpicky "${_wpicky} ${_CCOPT}") endforeach() foreach(_CCOPT IN LISTS WPICKY_DETECT) # surprisingly, CHECK_CXX_COMPILER_FLAG needs a new variable to store each new # test result in. string(MAKE_C_IDENTIFIER "OPT${_CCOPT}" _optvarname) # GCC only warns about unknown -Wno- options if there are also other diagnostic messages, # so test for the positive form instead string(REPLACE "-Wno-" "-W" _CCOPT_ON "${_CCOPT}") check_cxx_compiler_flag(${_CCOPT_ON} ${_optvarname}) if(${_optvarname}) set(_wpicky "${_wpicky} ${_CCOPT}") endif() endforeach() set(WARNCXXFLAGS "${WARNCXXFLAGS} ${_wpicky}") endif() nghttp2-1.68.0/cmake/PaxHeaders/FindWolfSSL.cmake0000644000000000000000000000013215077107270016434 xustar0030 mtime=1761382072.962444282 30 atime=1761382104.891316211 30 ctime=1761382107.877303916 nghttp2-1.68.0/cmake/FindWolfSSL.cmake0000644000175100017510000000251715077107270017031 0ustar00runnerrunner# - Try to find wolfssl # Once done this will define # WOLFSSL_FOUND - System has wolfssl # WOLFSSL_INCLUDE_DIRS - The wolfssl include directories # WOLFSSL_LIBRARIES - The libraries needed to use wolfssl find_package(PkgConfig QUIET) pkg_check_modules(PC_WOLFSSL QUIET wolfssl) find_path(WOLFSSL_INCLUDE_DIR NAMES wolfssl/ssl.h HINTS ${PC_WOLFSSL_INCLUDE_DIRS} ) find_library(WOLFSSL_LIBRARY NAMES wolfssl HINTS ${PC_WOLFSSL_LIBRARY_DIRS} ) if(WOLFSSL_INCLUDE_DIR) set(_version_regex "^#define[ \t]+LIBWOLFSSL_VERSION_STRING[ \t]+\"([^\"]+)\".*") file(STRINGS "${WOLFSSL_INCLUDE_DIR}/wolfssl/version.h" WOLFSSL_VERSION REGEX "${_version_regex}") string(REGEX REPLACE "${_version_regex}" "\\1" WOLFSSL_VERSION "${WOLFSSL_VERSION}") unset(_version_regex) endif() include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set WOLFSSL_FOUND # to TRUE if all listed variables are TRUE and the requested version # matches. find_package_handle_standard_args(WolfSSL REQUIRED_VARS WOLFSSL_LIBRARY WOLFSSL_INCLUDE_DIR VERSION_VAR WOLFSSL_VERSION) if(WOLFSSL_FOUND) set(WOLFSSL_LIBRARIES ${WOLFSSL_LIBRARY}) set(WOLFSSL_INCLUDE_DIRS ${WOLFSSL_INCLUDE_DIR}) endif() mark_as_advanced(WOLFSSL_INCLUDE_DIR WOLFSSL_LIBRARY) nghttp2-1.68.0/cmake/PaxHeaders/FindSystemd.cmake0000644000000000000000000000013215077107270016573 xustar0030 mtime=1761382072.962444282 30 atime=1761382104.880316259 30 ctime=1761382107.865303951 nghttp2-1.68.0/cmake/FindSystemd.cmake0000644000175100017510000000146515077107270017171 0ustar00runnerrunner# - Try to find systemd # Once done this will define # SYSTEMD_FOUND - System has systemd # SYSTEMD_INCLUDE_DIRS - The systemd include directories # SYSTEMD_LIBRARIES - The libraries needed to use systemd include(FeatureSummary) set_package_properties(Systemd PROPERTIES URL "http://freedesktop.org/wiki/Software/systemd/" DESCRIPTION "System and Service Manager") find_package(PkgConfig QUIET) pkg_check_modules(PC_SYSTEMD QUIET libsystemd) find_library(SYSTEMD_LIBRARIES NAMES systemd ${PC_SYSTEMD_LIBRARY_DIRS}) find_path(SYSTEMD_INCLUDE_DIRS systemd/sd-login.h HINTS ${PC_SYSTEMD_INCLUDE_DIRS}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Systemd DEFAULT_MSG SYSTEMD_INCLUDE_DIRS SYSTEMD_LIBRARIES) mark_as_advanced(SYSTEMD_INCLUDE_DIRS SYSTEMD_LIBRARIES) nghttp2-1.68.0/cmake/PaxHeaders/FindLibnghttp3.cmake0000644000000000000000000000013215077107270017161 xustar0030 mtime=1761382072.962444282 30 atime=1761382104.882316251 30 ctime=1761382107.868303942 nghttp2-1.68.0/cmake/FindLibnghttp3.cmake0000644000175100017510000000264315077107270017556 0ustar00runnerrunner# - Try to find libnghttp3 # Once done this will define # LIBNGHTTP3_FOUND - System has libnghttp3 # LIBNGHTTP3_INCLUDE_DIRS - The libnghttp3 include directories # LIBNGHTTP3_LIBRARIES - The libraries needed to use libnghttp3 find_package(PkgConfig QUIET) pkg_check_modules(PC_LIBNGHTTP3 QUIET libnghttp3) find_path(LIBNGHTTP3_INCLUDE_DIR NAMES nghttp3/nghttp3.h HINTS ${PC_LIBNGHTTP3_INCLUDE_DIRS} ) find_library(LIBNGHTTP3_LIBRARY NAMES nghttp3 HINTS ${PC_LIBNGHTTP3_LIBRARY_DIRS} ) if(LIBNGHTTP3_INCLUDE_DIR) set(_version_regex "^#define[ \t]+NGHTTP3_VERSION[ \t]+\"([^\"]+)\".*") file(STRINGS "${LIBNGHTTP3_INCLUDE_DIR}/nghttp3/version.h" LIBNGHTTP3_VERSION REGEX "${_version_regex}") string(REGEX REPLACE "${_version_regex}" "\\1" LIBNGHTTP3_VERSION "${LIBNGHTTP3_VERSION}") unset(_version_regex) endif() include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBNGHTTP3_FOUND # to TRUE if all listed variables are TRUE and the requested version # matches. find_package_handle_standard_args(Libnghttp3 REQUIRED_VARS LIBNGHTTP3_LIBRARY LIBNGHTTP3_INCLUDE_DIR VERSION_VAR LIBNGHTTP3_VERSION) if(LIBNGHTTP3_FOUND) set(LIBNGHTTP3_LIBRARIES ${LIBNGHTTP3_LIBRARY}) set(LIBNGHTTP3_INCLUDE_DIRS ${LIBNGHTTP3_INCLUDE_DIR}) endif() mark_as_advanced(LIBNGHTTP3_INCLUDE_DIR LIBNGHTTP3_LIBRARY) nghttp2-1.68.0/cmake/PaxHeaders/FindLibbpf.cmake0000644000000000000000000000013215077107270016341 xustar0030 mtime=1761382072.962444282 30 atime=1761382104.881316255 30 ctime=1761382107.866303948 nghttp2-1.68.0/cmake/FindLibbpf.cmake0000644000175100017510000000173515077107270016737 0ustar00runnerrunner# - Try to find libbpf # Once done this will define # LIBBPF_FOUND - System has libbpf # LIBBPF_INCLUDE_DIRS - The libbpf include directories # LIBBPF_LIBRARIES - The libraries needed to use libbpf find_package(PkgConfig QUIET) pkg_check_modules(PC_LIBBPF QUIET libbpf) find_path(LIBBPF_INCLUDE_DIR NAMES bpf/bpf.h HINTS ${PC_LIBBPF_INCLUDE_DIRS} ) find_library(LIBBPF_LIBRARY NAMES bpf HINTS ${PC_LIBBPF_LIBRARY_DIRS} ) include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBBPF_FOUND # to TRUE if all listed variables are TRUE and the requested version # matches. find_package_handle_standard_args(Libbpf REQUIRED_VARS LIBBPF_LIBRARY LIBBPF_INCLUDE_DIR VERSION_VAR LIBBPF_VERSION) if(LIBBPF_FOUND) set(LIBBPF_LIBRARIES ${LIBBPF_LIBRARY}) set(LIBBPF_INCLUDE_DIRS ${LIBBPF_INCLUDE_DIR}) endif() mark_as_advanced(LIBBPF_INCLUDE_DIR LIBBPF_LIBRARY) nghttp2-1.68.0/cmake/PaxHeaders/FindLibngtcp2_crypto_ossl.cmake0000644000000000000000000000013215077107270021427 xustar0030 mtime=1761382072.962444282 30 atime=1761382104.890316215 30 ctime=1761382107.875303922 nghttp2-1.68.0/cmake/FindLibngtcp2_crypto_ossl.cmake0000644000175100017510000000345615077107270022027 0ustar00runnerrunner# - Try to find libngtcp2_crypto_ossl # Once done this will define # LIBNGTCP2_CRYPTO_OSSL_FOUND - System has libngtcp2_crypto_ossl # LIBNGTCP2_CRYPTO_OSSL_INCLUDE_DIRS - The libngtcp2_crypto_ossl include directories # LIBNGTCP2_CRYPTO_OSSL_LIBRARIES - The libraries needed to use libngtcp2_crypto_ossl find_package(PkgConfig QUIET) pkg_check_modules(PC_LIBNGTCP2_CRYPTO_OSSL QUIET libngtcp2_crypto_ossl) find_path(LIBNGTCP2_CRYPTO_OSSL_INCLUDE_DIR NAMES ngtcp2/ngtcp2_crypto_ossl.h HINTS ${PC_LIBNGTCP2_CRYPTO_OSSL_INCLUDE_DIRS} ) find_library(LIBNGTCP2_CRYPTO_OSSL_LIBRARY NAMES ngtcp2_crypto_ossl HINTS ${PC_LIBNGTCP2_CRYPTO_OSSL_LIBRARY_DIRS} ) if(LIBNGTCP2_CRYPTO_OSSL_INCLUDE_DIR) set(_version_regex "^#define[ \t]+NGTCP2_VERSION[ \t]+\"([^\"]+)\".*") file(STRINGS "${LIBNGTCP2_CRYPTO_OSSL_INCLUDE_DIR}/ngtcp2/version.h" LIBNGTCP2_CRYPTO_OSSL_VERSION REGEX "${_version_regex}") string(REGEX REPLACE "${_version_regex}" "\\1" LIBNGTCP2_CRYPTO_OSSL_VERSION "${LIBNGTCP2_CRYPTO_OSSL_VERSION}") unset(_version_regex) endif() include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set # LIBNGTCP2_CRYPTO_OSSL_FOUND to TRUE if all listed variables are # TRUE and the requested version matches. find_package_handle_standard_args(Libngtcp2_crypto_ossl REQUIRED_VARS LIBNGTCP2_CRYPTO_OSSL_LIBRARY LIBNGTCP2_CRYPTO_OSSL_INCLUDE_DIR VERSION_VAR LIBNGTCP2_CRYPTO_OSSL_VERSION) if(LIBNGTCP2_CRYPTO_OSSL_FOUND) set(LIBNGTCP2_CRYPTO_OSSL_LIBRARIES ${LIBNGTCP2_CRYPTO_OSSL_LIBRARY}) set(LIBNGTCP2_CRYPTO_OSSL_INCLUDE_DIRS ${LIBNGTCP2_CRYPTO_OSSL_INCLUDE_DIR}) endif() mark_as_advanced(LIBNGTCP2_CRYPTO_OSSL_INCLUDE_DIR LIBNGTCP2_CRYPTO_OSSL_LIBRARY) nghttp2-1.68.0/cmake/PaxHeaders/ExtractValidFlags.cmake0000644000000000000000000000013215077107270017711 xustar0030 mtime=1761382072.962444282 30 atime=1761382104.871316299 30 ctime=1761382107.856303977 nghttp2-1.68.0/cmake/ExtractValidFlags.cmake0000644000175100017510000000174015077107270020303 0ustar00runnerrunner# Convenience function that checks the availability of certain # C or C++ compiler flags and returns valid ones as a string. include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) function(extract_valid_c_flags varname) set(valid_flags) foreach(flag IN LISTS ARGN) string(REGEX REPLACE "[^a-zA-Z0-9_]+" "_" flag_var ${flag}) set(flag_var "C_FLAG_${flag_var}") check_c_compiler_flag("${flag}" "${flag_var}") if(${flag_var}) set(valid_flags "${valid_flags} ${flag}") endif() endforeach() set(${varname} "${valid_flags}" PARENT_SCOPE) endfunction() function(extract_valid_cxx_flags varname) set(valid_flags) foreach(flag IN LISTS ARGN) string(REGEX REPLACE "[^a-zA-Z0-9_]+" "_" flag_var ${flag}) set(flag_var "CXX_FLAG_${flag_var}") check_cxx_compiler_flag("${flag}" "${flag_var}") if(${flag_var}) set(valid_flags "${valid_flags} ${flag}") endif() endforeach() set(${varname} "${valid_flags}" PARENT_SCOPE) endfunction() nghttp2-1.68.0/cmake/PaxHeaders/FindLibcares.cmake0000644000000000000000000000013215077107270016667 xustar0030 mtime=1761382072.962444282 30 atime=1761382104.879316264 30 ctime=1761382107.864303954 nghttp2-1.68.0/cmake/FindLibcares.cmake0000644000175100017510000000327215077107270017263 0ustar00runnerrunner# - Try to find libcares # Once done this will define # LIBCARES_FOUND - System has libcares # LIBCARES_INCLUDE_DIRS - The libcares include directories # LIBCARES_LIBRARIES - The libraries needed to use libcares find_package(PkgConfig QUIET) pkg_check_modules(PC_LIBCARES QUIET libcares) find_path(LIBCARES_INCLUDE_DIR NAMES ares.h HINTS ${PC_LIBCARES_INCLUDE_DIRS} ) find_library(LIBCARES_LIBRARY NAMES cares HINTS ${PC_LIBCARES_LIBRARY_DIRS} ) if(LIBCARES_INCLUDE_DIR) file(READ "${LIBCARES_INCLUDE_DIR}/ares_version.h" _ares_version_h) string(REGEX REPLACE ".*#define[ \t]+ARES_VERSION_MAJOR[ \t]+([0-9]+).*" "\\1" _ares_version_major ${_ares_version_h}) string(REGEX REPLACE ".*#define[ \t]+ARES_VERSION_MINOR[ \t]+([0-9]+).*" "\\1" _ares_version_minor ${_ares_version_h}) string(REGEX REPLACE ".*#define[ \t]+ARES_VERSION_PATCH[ \t]+([0-9]+).*" "\\1" _ares_version_patch ${_ares_version_h}) set(LIBCARES_VERSION "${_ares_version_major}.${_ares_version_minor}.${_ares_version_patch}") unset(_ares_version_patch) unset(_ares_version_minor) unset(_ares_version_major) unset(_ares_version_h) endif() include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBCARES_FOUND to TRUE # if all listed variables are TRUE and the requested version matches. find_package_handle_standard_args(Libcares REQUIRED_VARS LIBCARES_LIBRARY LIBCARES_INCLUDE_DIR VERSION_VAR LIBCARES_VERSION) if(LIBCARES_FOUND) set(LIBCARES_LIBRARIES ${LIBCARES_LIBRARY}) set(LIBCARES_INCLUDE_DIRS ${LIBCARES_INCLUDE_DIR}) endif() mark_as_advanced(LIBCARES_INCLUDE_DIR LIBCARES_LIBRARY) nghttp2-1.68.0/cmake/PaxHeaders/FindLibev.cmake0000644000000000000000000000013215077107270016204 xustar0030 mtime=1761382072.962444282 30 atime=1761382104.874316286 30 ctime=1761382107.859303968 nghttp2-1.68.0/cmake/FindLibev.cmake0000644000175100017510000000254315077107270016600 0ustar00runnerrunner# - Try to find libev # Once done this will define # LIBEV_FOUND - System has libev # LIBEV_INCLUDE_DIRS - The libev include directories # LIBEV_LIBRARIES - The libraries needed to use libev find_path(LIBEV_INCLUDE_DIR NAMES ev.h ) find_library(LIBEV_LIBRARY NAMES ev ) if(LIBEV_INCLUDE_DIR) file(STRINGS "${LIBEV_INCLUDE_DIR}/ev.h" LIBEV_VERSION_MAJOR REGEX "^#define[ \t]+EV_VERSION_MAJOR[ \t]+[0-9]+") file(STRINGS "${LIBEV_INCLUDE_DIR}/ev.h" LIBEV_VERSION_MINOR REGEX "^#define[ \t]+EV_VERSION_MINOR[ \t]+[0-9]+") string(REGEX REPLACE "[^0-9]+" "" LIBEV_VERSION_MAJOR "${LIBEV_VERSION_MAJOR}") string(REGEX REPLACE "[^0-9]+" "" LIBEV_VERSION_MINOR "${LIBEV_VERSION_MINOR}") set(LIBEV_VERSION "${LIBEV_VERSION_MAJOR}.${LIBEV_VERSION_MINOR}") unset(LIBEV_VERSION_MINOR) unset(LIBEV_VERSION_MAJOR) endif() include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBEV_FOUND to TRUE # if all listed variables are TRUE and the requested version matches. find_package_handle_standard_args(Libev REQUIRED_VARS LIBEV_LIBRARY LIBEV_INCLUDE_DIR VERSION_VAR LIBEV_VERSION) if(LIBEV_FOUND) set(LIBEV_LIBRARIES ${LIBEV_LIBRARY}) set(LIBEV_INCLUDE_DIRS ${LIBEV_INCLUDE_DIR}) endif() mark_as_advanced(LIBEV_INCLUDE_DIR LIBEV_LIBRARY) nghttp2-1.68.0/cmake/PaxHeaders/FindLibngtcp2.cmake0000644000000000000000000000013215077107270016767 xustar0030 mtime=1761382072.962444282 30 atime=1761382104.884316242 30 ctime=1761382107.869303939 nghttp2-1.68.0/cmake/FindLibngtcp2.cmake0000644000175100017510000000260015077107270017355 0ustar00runnerrunner# - Try to find libngtcp2 # Once done this will define # LIBNGTCP2_FOUND - System has libngtcp2 # LIBNGTCP2_INCLUDE_DIRS - The libngtcp2 include directories # LIBNGTCP2_LIBRARIES - The libraries needed to use libngtcp2 find_package(PkgConfig QUIET) pkg_check_modules(PC_LIBNGTCP2 QUIET libngtcp2) find_path(LIBNGTCP2_INCLUDE_DIR NAMES ngtcp2/ngtcp2.h HINTS ${PC_LIBNGTCP2_INCLUDE_DIRS} ) find_library(LIBNGTCP2_LIBRARY NAMES ngtcp2 HINTS ${PC_LIBNGTCP2_LIBRARY_DIRS} ) if(LIBNGTCP2_INCLUDE_DIR) set(_version_regex "^#define[ \t]+NGTCP2_VERSION[ \t]+\"([^\"]+)\".*") file(STRINGS "${LIBNGTCP2_INCLUDE_DIR}/ngtcp2/version.h" LIBNGTCP2_VERSION REGEX "${_version_regex}") string(REGEX REPLACE "${_version_regex}" "\\1" LIBNGTCP2_VERSION "${LIBNGTCP2_VERSION}") unset(_version_regex) endif() include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBNGTCP2_FOUND # to TRUE if all listed variables are TRUE and the requested version # matches. find_package_handle_standard_args(Libngtcp2 REQUIRED_VARS LIBNGTCP2_LIBRARY LIBNGTCP2_INCLUDE_DIR VERSION_VAR LIBNGTCP2_VERSION) if(LIBNGTCP2_FOUND) set(LIBNGTCP2_LIBRARIES ${LIBNGTCP2_LIBRARY}) set(LIBNGTCP2_INCLUDE_DIRS ${LIBNGTCP2_INCLUDE_DIR}) endif() mark_as_advanced(LIBNGTCP2_INCLUDE_DIR LIBNGTCP2_LIBRARY) nghttp2-1.68.0/cmake/PaxHeaders/FindLibbrotlidec.cmake0000644000000000000000000000013215077107270017541 xustar0030 mtime=1761382072.962444282 30 atime=1761382104.887316229 30 ctime=1761382107.873303928 nghttp2-1.68.0/cmake/FindLibbrotlidec.cmake0000644000175100017510000000233115077107270020130 0ustar00runnerrunner# - Try to find libbrotlidec # Once done this will define # LIBBROTLIDEC_FOUND - System has libbrotlidec # LIBBROTLIDEC_INCLUDE_DIRS - The libbrotlidec include directories # LIBBROTLIDEC_LIBRARIES - The libraries needed to use libbrotlidec find_package(PkgConfig QUIET) pkg_check_modules(PC_LIBBROTLIDEC QUIET libbrotlidec) find_path(LIBBROTLIDEC_INCLUDE_DIR NAMES brotli/decode.h HINTS ${PC_LIBBROTLIDEC_INCLUDE_DIRS} ) find_library(LIBBROTLIDEC_LIBRARY NAMES brotlidec HINTS ${PC_LIBBROTLIDEC_LIBRARY_DIRS} ) if(PC_LIBBROTLIDEC_FOUND) set(LIBBROTLIDEC_VERSION ${PC_LIBBROTLIDEC_VERSION}) endif() include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBBROTLIDEC_FOUND # to TRUE if all listed variables are TRUE and the requested version # matches. find_package_handle_standard_args(Libbrotlidec REQUIRED_VARS LIBBROTLIDEC_LIBRARY LIBBROTLIDEC_INCLUDE_DIR VERSION_VAR LIBBROTLIDEC_VERSION) if(LIBBROTLIDEC_FOUND) set(LIBBROTLIDEC_LIBRARIES ${LIBBROTLIDEC_LIBRARY}) set(LIBBROTLIDEC_INCLUDE_DIRS ${LIBBROTLIDEC_INCLUDE_DIR}) endif() mark_as_advanced(LIBBROTLIDEC_INCLUDE_DIR LIBBROTLIDEC_LIBRARY) nghttp2-1.68.0/PaxHeaders/Makefile.am0000644000000000000000000000013015077107270014312 xustar0030 mtime=1761382072.961444287 29 atime=1761382080.39941014 29 ctime=1761382107.81030411 nghttp2-1.68.0/Makefile.am0000644000175100017510000000460015077107270014704 0ustar00runnerrunner# nghttp2 - HTTP/2 C Library # Copyright (c) 2012 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SUBDIRS = lib tests third-party src bpf examples integration-tests \ doc contrib ACLOCAL_AMFLAGS = -I m4 dist_doc_DATA = README.rst EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-env \ Dockerfile.android \ cmakeconfig.h.in \ CMakeLists.txt \ CMakeOptions.txt \ cmake/ExtractValidFlags.cmake \ cmake/FindJemalloc.cmake \ cmake/FindLibev.cmake \ cmake/Version.cmake \ cmake/FindLibevent.cmake \ cmake/FindJansson.cmake \ cmake/FindLibcares.cmake \ cmake/FindSystemd.cmake \ cmake/FindLibbpf.cmake \ cmake/FindLibnghttp3.cmake \ cmake/FindLibngtcp2.cmake \ cmake/FindLibngtcp2_crypto_quictls.cmake \ cmake/FindLibbrotlienc.cmake \ cmake/FindLibbrotlidec.cmake \ cmake/FindLibngtcp2_crypto_wolfssl.cmake \ cmake/FindLibngtcp2_crypto_ossl.cmake \ cmake/FindWolfSSL.cmake \ cmake/PickyWarningsC.cmake \ cmake/PickyWarningsCXX.cmake .PHONY: clang-format # Format source files using clang-format. Don't format source files # under third-party directory since we are not responsible for their # coding style. clang-format: CLANGFORMAT=`git config --get clangformat.binary`; \ test -z $${CLANGFORMAT} && CLANGFORMAT="clang-format"; \ $${CLANGFORMAT} -i lib/*.{c,h} lib/includes/nghttp2/*.h \ src/*.{c,cc,h} examples/*.c \ tests/*.{c,h} bpf/*.c fuzz/*.cc nghttp2-1.68.0/PaxHeaders/depcomp0000644000000000000000000000013115077107305013630 xustar0029 mtime=1761382085.52438735 30 atime=1761382094.990346216 30 ctime=1761382107.915303806 nghttp2-1.68.0/depcomp0000755000175100017510000005602015077107305014227 0ustar00runnerrunner#! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2018-03-07.03; # UTC # Copyright (C) 1999-2021 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by 'PROGRAMS ARGS'. object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. libtool Whether libtool is used (yes/no). Report bugs to . EOF exit $? ;; -v | --v*) echo "depcomp $scriptversion" exit $? ;; esac # Get the directory component of the given path, and save it in the # global variables '$dir'. Note that this directory component will # be either empty or ending with a '/' character. This is deliberate. set_dir_from () { case $1 in */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; *) dir=;; esac } # Get the suffix-stripped basename of the given path, and save it the # global variable '$base'. set_base_from () { base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` } # If no dependency file was actually created by the compiler invocation, # we still have to create a dummy depfile, to avoid errors with the # Makefile "include basename.Plo" scheme. make_dummy_depfile () { echo "#dummy" > "$depfile" } # Factor out some common post-processing of the generated depfile. # Requires the auxiliary global variable '$tmpdepfile' to be set. aix_post_process_depfile () { # If the compiler actually managed to produce a dependency file, # post-process it. if test -f "$tmpdepfile"; then # Each line is of the form 'foo.o: dependency.h'. # Do two passes, one to just change these to # $object: dependency.h # and one to simply output # dependency.h: # which is needed to avoid the deleted-header problem. { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" } > "$depfile" rm -f "$tmpdepfile" else make_dummy_depfile fi } # A tabulation character. tab=' ' # A newline character. nl=' ' # Character ranges might be problematic outside the C locale. # These definitions help. upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ lower=abcdefghijklmnopqrstuvwxyz digits=0123456789 alpha=${upper}${lower} if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Avoid interferences from the environment. gccflag= dashmflag= # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then # This is just like msvc7 but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvc7 fi if test "$depmode" = xlc; then # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. gccflag=-qmakedep=gcc,-MF depmode=gcc fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. ## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. ## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). Also, it might not be ## supported by the other compilers which use the 'gcc' depmode. ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The second -e expression handles DOS-style file names with drive # letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. ## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" ;; xlc) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done aix_post_process_depfile ;; tcc) # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 # FIXME: That version still under development at the moment of writing. # Make that this statement remains true also for stable, released # versions. # It will wrap lines (doesn't matter whether long or short) with a # trailing '\', as in: # # foo.o : \ # foo.c \ # foo.h \ # # It will put a trailing '\' even on the last line, and will use leading # spaces rather than leading tabs (at least since its commit 0394caf7 # "Emit spaces for -MD"). "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. # We have to change lines of the first kind to '$object: \'. sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" # And for each line of the second kind, we have to emit a 'dep.h:' # dummy dependency, to avoid the deleted-header problem. sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; ## The order of this option in the case statement is important, since the ## shell code in configure will try each of these formats in the order ## listed in this file. A plain '-MD' option would be understood by many ## compilers, so we must ensure this comes after the gcc and icc options. pgcc) # Portland's C compiler understands '-MD'. # Will always output deps to 'file.d' where file is the root name of the # source file under compilation, even if file resides in a subdirectory. # The object file name does not affect the name of the '.d' file. # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... set_dir_from "$object" # Use the source, not the object, to determine the base name, since # that's sadly what pgcc will do too. set_base_from "$source" tmpdepfile=$base.d # For projects that build the same source file twice into different object # files, the pgcc approach of using the *source* file root name can cause # problems in parallel builds. Use a locking strategy to avoid stomping on # the same $tmpdepfile. lockdir=$base.d-lock trap " echo '$0: caught signal, cleaning up...' >&2 rmdir '$lockdir' exit 1 " 1 2 13 15 numtries=100 i=$numtries while test $i -gt 0; do # mkdir is a portable test-and-set. if mkdir "$lockdir" 2>/dev/null; then # This process acquired the lock. "$@" -MD stat=$? # Release the lock. rmdir "$lockdir" break else # If the lock is being held by a different process, wait # until the winning process is done or we timeout. while test -d "$lockdir" && test $i -gt 0; do sleep 1 i=`expr $i - 1` done fi i=`expr $i - 1` done trap - 1 2 13 15 if test $i -le 0; then echo "$0: failed to acquire lock after $numtries attempts" >&2 echo "$0: check lockdir '$lockdir'" >&2 exit 1 fi if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in 'foo.d' instead, so we check for that too. # Subdirectories are respected. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then # Libtool generates 2 separate objects for the 2 libraries. These # two compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir$base.o.d # libtool 1.5 tmpdepfile2=$dir.libs/$base.o.d # Likewise. tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d "$@" -MD fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done # Same post-processing that is required for AIX mode. aix_post_process_depfile ;; msvc7) if test "$libtool" = yes; then showIncludes=-Wc,-showIncludes else showIncludes=-showIncludes fi "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The first sed program below extracts the file names and escapes # backslashes for cygpath. The second sed program outputs the file # name when reading, but also accumulates all include files in the # hold buffer in order to output them again at the end. This only # works with sed implementations that can handle large buffers. sed < "$tmpdepfile" -n ' /^Note: including file: *\(.*\)/ { s//\1/ s/\\/\\\\/g p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { s/.*/'"$tab"'/ G p }' >> "$depfile" echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; msvc7msys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this sed invocation # correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -arch) eat=yes ;; -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix=`echo "$object" | sed 's/^.*\././'` touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process the last invocation # correctly. Breaking it into two sed invocations is a workaround. sed '1,2d' "$tmpdepfile" \ | tr ' ' "$nl" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E \ | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: nghttp2-1.68.0/PaxHeaders/aclocal.m40000644000000000000000000000013015077107303014113 xustar0030 mtime=1761382083.972393997 29 atime=1761382084.04639368 29 ctime=1761382107.82430407 nghttp2-1.68.0/aclocal.m40000644000175100017510000020771015077107303014514 0ustar00runnerrunner# generated automatically by aclocal 1.16.5 -*- Autoconf -*- # Copyright (C) 1996-2021 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.71],, [m4_warning([this file was generated for autoconf 2.71. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) # pkg.m4 - Macros to locate and use pkg-config. -*- Autoconf -*- # serial 12 (pkg-config-0.29.2) dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA dnl 02111-1307, USA. dnl dnl As a special exception to the GNU General Public License, if you dnl distribute this file as part of a program that contains a dnl configuration script generated by Autoconf, you may include it under dnl the same distribution terms that you use for the rest of that dnl program. dnl PKG_PREREQ(MIN-VERSION) dnl ----------------------- dnl Since: 0.29 dnl dnl Verify that the version of the pkg-config macros are at least dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's dnl installed version of pkg-config, this checks the developer's version dnl of pkg.m4 when generating configure. dnl dnl To ensure that this macro is defined, also add: dnl m4_ifndef([PKG_PREREQ], dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], [m4_define([PKG_MACROS_VERSION], [0.29.2]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) dnl ---------------------------------- dnl Since: 0.16 dnl dnl Search for the pkg-config tool and set the PKG_CONFIG variable to dnl first found in the path. Checks that the version of pkg-config found dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is dnl used since that's the first version where most current features of dnl pkg-config existed. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])dnl PKG_PROG_PKG_CONFIG dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------------------------------- dnl Since: 0.18 dnl dnl Check to see whether a particular set of modules exists. Similar to dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) dnl only at the first occurrence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) dnl --------------------------------------------- dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting dnl pkg_failed based on the result. m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])dnl _PKG_CONFIG dnl _PKG_SHORT_ERRORS_SUPPORTED dnl --------------------------- dnl Internal check to see if pkg-config supports short errors. AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])dnl _PKG_SHORT_ERRORS_SUPPORTED dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl -------------------------------------------------------------- dnl Since: 0.4.0 dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES might not happen, you should be sure to include an dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $2]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])dnl PKG_CHECK_MODULES dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl --------------------------------------------------------------------- dnl Since: 0.29 dnl dnl Checks for existence of MODULES and gathers its build flags with dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags dnl and VARIABLE-PREFIX_LIBS from --libs. dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to dnl include an explicit call to PKG_PROG_PKG_CONFIG in your dnl configure.ac. AC_DEFUN([PKG_CHECK_MODULES_STATIC], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl _save_PKG_CONFIG=$PKG_CONFIG PKG_CONFIG="$PKG_CONFIG --static" PKG_CHECK_MODULES($@) PKG_CONFIG=$_save_PKG_CONFIG[]dnl ])dnl PKG_CHECK_MODULES_STATIC dnl PKG_INSTALLDIR([DIRECTORY]) dnl ------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable pkgconfigdir as the location where a module dnl should install pkg-config .pc files. By default the directory is dnl $libdir/pkgconfig, but the default can be changed by passing dnl DIRECTORY. The user can override through the --with-pkgconfigdir dnl parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, [with_pkgconfigdir=]pkg_default) AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_INSTALLDIR dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) dnl -------------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable noarch_pkgconfigdir as the location where a dnl module should install arch-independent pkg-config .pc files. By dnl default the directory is $datadir/pkgconfig, but the default can be dnl changed by passing DIRECTORY. The user can override through the dnl --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, [with_noarch_pkgconfigdir=]pkg_default) AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_NOARCH_INSTALLDIR dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------- dnl Since: 0.28 dnl dnl Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES, dnl [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND], dnl [DESCRIPTION], [DEFAULT]) dnl ------------------------------------------ dnl dnl Prepare a "--with-" configure option using the lowercase dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and dnl PKG_CHECK_MODULES in a single macro. AC_DEFUN([PKG_WITH_MODULES], [ m4_pushdef([with_arg], m4_tolower([$1])) m4_pushdef([description], [m4_default([$5], [build with ]with_arg[ support])]) m4_pushdef([def_arg], [m4_default([$6], [auto])]) m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes]) m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no]) m4_case(def_arg, [yes],[m4_pushdef([with_without], [--without-]with_arg)], [m4_pushdef([with_without],[--with-]with_arg)]) AC_ARG_WITH(with_arg, AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),, [AS_TR_SH([with_]with_arg)=def_arg]) AS_CASE([$AS_TR_SH([with_]with_arg)], [yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)], [auto],[PKG_CHECK_MODULES([$1],[$2], [m4_n([def_action_if_found]) $3], [m4_n([def_action_if_not_found]) $4])]) m4_popdef([with_arg]) m4_popdef([description]) m4_popdef([def_arg]) ])dnl PKG_WITH_MODULES dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES, dnl [DESCRIPTION], [DEFAULT]) dnl ----------------------------------------------- dnl dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES dnl check._[VARIABLE-PREFIX] is exported as make variable. AC_DEFUN([PKG_HAVE_WITH_MODULES], [ PKG_WITH_MODULES([$1],[$2],,,[$3],[$4]) AM_CONDITIONAL([HAVE_][$1], [test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"]) ])dnl PKG_HAVE_WITH_MODULES dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES, dnl [DESCRIPTION], [DEFAULT]) dnl ------------------------------------------------------ dnl dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make dnl and preprocessor variable. AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES], [ PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4]) AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"], [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])]) ])dnl PKG_HAVE_DEFINE_WITH_MODULES # Copyright (C) 2002-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.16' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.16.5], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.16.5])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to # '$srcdir', '$srcdir/..', or '$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is '.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Copyright (C) 1999-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], [$1], [CXX], [depcc="$CXX" am_compiler_list=], [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], [$1], [UPC], [depcc="$UPC" am_compiler_list=], [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_$1_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi am__universal=false m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac]) for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_$1_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) AM_CONDITIONAL([am__fastdep$1], [ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES. AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE([dependency-tracking], [dnl AS_HELP_STRING( [--enable-dependency-tracking], [do not reject slow dependency extractors]) AS_HELP_STRING( [--disable-dependency-tracking], [speeds up one-time build])]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl AC_SUBST([am__nodep])dnl _AM_SUBST_NOTMAKE([am__nodep])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. # TODO: see whether this extra hack can be removed once we start # requiring Autoconf 2.70 or later. AS_CASE([$CONFIG_FILES], [*\'*], [eval set x "$CONFIG_FILES"], [*], [set x $CONFIG_FILES]) shift # Used to flag and report bootstrapping failures. am_rc=0 for am_mf do # Strip MF so we end up with the name of the file. am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile which includes # dependency-tracking related rules and includes. # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ || continue am_dirpart=`AS_DIRNAME(["$am_mf"])` am_filepart=`AS_BASENAME(["$am_mf"])` AM_RUN_LOG([cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles]) || am_rc=$? done if test $am_rc -ne 0; then AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments for automatic dependency tracking. If GNU make was not used, consider re-running the configure script with MAKE="gmake" (or whatever is necessary). You can also try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking).]) fi AS_UNSET([am_dirpart]) AS_UNSET([am_filepart]) AS_UNSET([am_mf]) AS_UNSET([am_rc]) rm -f conftest-deps.mk } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS # AM_OUTPUT_DEPENDENCY_COMMANDS # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking is enabled. # This creates each '.Po' and '.Plo' makefile fragment that we'll need in # order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC]) [_AM_PROG_CC_C_O ]) # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.65])dnl m4_ifdef([_$0_ALREADY_INIT], [m4_fatal([$0 expanded multiple times ]m4_defn([_$0_ALREADY_INIT]))], [m4_define([_$0_ALREADY_INIT], m4_expansion_stack)])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [AC_DIAGNOSE([obsolete], [$0: two- and three-arguments forms are deprecated.]) m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if( m4_ifset([AC_PACKAGE_NAME], [ok]):m4_ifset([AC_PACKAGE_VERSION], [ok]), [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) AM_MISSING_PROG([AUTOCONF], [autoconf]) AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) AM_MISSING_PROG([AUTOHEADER], [autoheader]) AM_MISSING_PROG([MAKEINFO], [makeinfo]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES([CC])], [m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES([CXX])], [m4_define([AC_PROG_CXX], m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES([OBJC])], [m4_define([AC_PROG_OBJC], m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], [_AM_DEPENDENCIES([OBJCXX])], [m4_define([AC_PROG_OBJCXX], m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl ]) # Variables for tags utilities; see am/tags.am if test -z "$CTAGS"; then CTAGS=ctags fi AC_SUBST([CTAGS]) if test -z "$ETAGS"; then ETAGS=etags fi AC_SUBST([ETAGS]) if test -z "$CSCOPE"; then CSCOPE=cscope fi AC_SUBST([CSCOPE]) AC_REQUIRE([AM_SILENT_RULES])dnl dnl The testsuite driver may need to know about EXEEXT, so add the dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) fi fi dnl The trailing newline in this macro's definition is deliberate, for dnl backward compatibility and to allow trailing 'dnl'-style comments dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. ]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST([install_sh])]) # Copyright (C) 2003-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MAKE_INCLUDE() # ----------------- # Check whether make has an 'include' directive that can support all # the idioms we need for our automatic dependency tracking code. AC_DEFUN([AM_MAKE_INCLUDE], [AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive]) cat > confinc.mk << 'END' am__doit: @echo this is the am__doit target >confinc.out .PHONY: am__doit END am__include="#" am__quote= # BSD make does it like this. echo '.include "confinc.mk" # ignored' > confmf.BSD # Other make implementations (GNU, Solaris 10, AIX) do it like this. echo 'include confinc.mk # ignored' > confmf.GNU _am_result=no for s in GNU BSD; do AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out]) AS_CASE([$?:`cat confinc.out 2>/dev/null`], ['0:this is the am__doit target'], [AS_CASE([$s], [BSD], [am__include='.include' am__quote='"'], [am__include='include' am__quote=''])]) if test "$am__include" != "#"; then _am_result="yes ($s style)" break fi done rm -f confinc.* confmf.* AC_MSG_RESULT([${_am_result}]) AC_SUBST([am__include])]) AC_SUBST([am__quote])]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it is modern enough. # If it is, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then MISSING="\${SHELL} '$am_aux_dir/missing'" fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= AC_MSG_WARN(['missing' script is too old or missing]) fi ]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # -------------------- # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), [1])]) # _AM_SET_OPTIONS(OPTIONS) # ------------------------ # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Copyright (C) 1999-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_CC_C_O # --------------- # Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC # to automatically call this. AC_DEFUN([_AM_PROG_CC_C_O], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl AC_LANG_PUSH([C])dnl AC_CACHE_CHECK( [whether $CC understands -c and -o together], [am_cv_prog_cc_c_o], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i]) if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) # Copyright (C) 1999-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # --------------------------------------------------------------------------- # Adds support for distributing Python modules and packages. To # install modules, copy them to $(pythondir), using the python_PYTHON # automake variable. To install a package with the same name as the # automake package, install to $(pkgpythondir), or use the # pkgpython_PYTHON automake variable. # # The variables $(pyexecdir) and $(pkgpyexecdir) are provided as # locations to install python extension modules (shared libraries). # Another macro is required to find the appropriate flags to compile # extension modules. # # If your package is configured with a different prefix to python, # users will have to add the install directory to the PYTHONPATH # environment variable, or create a .pth file (see the python # documentation for details). # # If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will # cause an error if the version of python installed on the system # doesn't meet the requirement. MINIMUM-VERSION should consist of # numbers and dots only. AC_DEFUN([AM_PATH_PYTHON], [ dnl Find a Python interpreter. Python versions prior to 2.0 are not dnl supported. (2.0 was released on October 16, 2000). m4_define_default([_AM_PYTHON_INTERPRETER_LIST], [python python2 python3 dnl python3.11 python3.10 dnl python3.9 python3.8 python3.7 python3.6 python3.5 python3.4 python3.3 dnl python3.2 python3.1 python3.0 dnl python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 dnl python2.0]) AC_ARG_VAR([PYTHON], [the Python interpreter]) m4_if([$1],[],[ dnl No version check is needed. # Find any Python interpreter. if test -z "$PYTHON"; then AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST, :) fi am_display_PYTHON=python ], [ dnl A version check is needed. if test -n "$PYTHON"; then # If the user set $PYTHON, use it and don't search something else. AC_MSG_CHECKING([whether $PYTHON version is >= $1]) AM_PYTHON_CHECK_VERSION([$PYTHON], [$1], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) AC_MSG_ERROR([Python interpreter is too old])]) am_display_PYTHON=$PYTHON else # Otherwise, try each interpreter until we find one that satisfies # VERSION. AC_CACHE_CHECK([for a Python interpreter with version >= $1], [am_cv_pathless_PYTHON],[ for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do test "$am_cv_pathless_PYTHON" = none && break AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break]) done]) # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. if test "$am_cv_pathless_PYTHON" = none; then PYTHON=: else AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON]) fi am_display_PYTHON=$am_cv_pathless_PYTHON fi ]) if test "$PYTHON" = :; then dnl Run any user-specified action, or abort. m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])]) else dnl Query Python for its version number. Although site.py simply uses dnl sys.version[:3], printing that failed with Python 3.10, since the dnl trailing zero was eliminated. So now we output just the major dnl and minor version numbers, as numbers. Apparently the tertiary dnl version is not of interest. dnl AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version], [am_cv_python_version=`$PYTHON -c "import sys; print ('%u.%u' % sys.version_info[[:2]])"`]) AC_SUBST([PYTHON_VERSION], [$am_cv_python_version]) dnl At times, e.g., when building shared libraries, you may want dnl to know which OS platform Python thinks this is. dnl AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform], [am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"`]) AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform]) dnl emacs-page dnl If --with-python-sys-prefix is given, use the values of sys.prefix dnl and sys.exec_prefix for the corresponding values of PYTHON_PREFIX dnl and PYTHON_EXEC_PREFIX. Otherwise, use the GNU ${prefix} and dnl ${exec_prefix} variables. dnl dnl The two are made distinct variables so they can be overridden if dnl need be, although general consensus is that you shouldn't need dnl this separation. dnl dnl Also allow directly setting the prefixes via configure options, dnl overriding any default. dnl if test "x$prefix" = xNONE; then am__usable_prefix=$ac_default_prefix else am__usable_prefix=$prefix fi # Allow user to request using sys.* values from Python, # instead of the GNU $prefix values. AC_ARG_WITH([python-sys-prefix], [AS_HELP_STRING([--with-python-sys-prefix], [use Python's sys.prefix and sys.exec_prefix values])], [am_use_python_sys=:], [am_use_python_sys=false]) # Allow user to override whatever the default Python prefix is. AC_ARG_WITH([python_prefix], [AS_HELP_STRING([--with-python_prefix], [override the default PYTHON_PREFIX])], [am_python_prefix_subst=$withval am_cv_python_prefix=$withval AC_MSG_CHECKING([for explicit $am_display_PYTHON prefix]) AC_MSG_RESULT([$am_cv_python_prefix])], [ if $am_use_python_sys; then # using python sys.prefix value, not GNU AC_CACHE_CHECK([for python default $am_display_PYTHON prefix], [am_cv_python_prefix], [am_cv_python_prefix=`$PYTHON -c "import sys; sys.stdout.write(sys.prefix)"`]) dnl If sys.prefix is a subdir of $prefix, replace the literal value of dnl $prefix with a variable reference so it can be overridden. case $am_cv_python_prefix in $am__usable_prefix*) am__strip_prefix=`echo "$am__usable_prefix" | sed 's|.|.|g'` am_python_prefix_subst=`echo "$am_cv_python_prefix" | sed "s,^$am__strip_prefix,\\${prefix},"` ;; *) am_python_prefix_subst=$am_cv_python_prefix ;; esac else # using GNU prefix value, not python sys.prefix am_python_prefix_subst='${prefix}' am_python_prefix=$am_python_prefix_subst AC_MSG_CHECKING([for GNU default $am_display_PYTHON prefix]) AC_MSG_RESULT([$am_python_prefix]) fi]) # Substituting python_prefix_subst value. AC_SUBST([PYTHON_PREFIX], [$am_python_prefix_subst]) # emacs-page Now do it all over again for Python exec_prefix, but with yet # another conditional: fall back to regular prefix if that was specified. AC_ARG_WITH([python_exec_prefix], [AS_HELP_STRING([--with-python_exec_prefix], [override the default PYTHON_EXEC_PREFIX])], [am_python_exec_prefix_subst=$withval am_cv_python_exec_prefix=$withval AC_MSG_CHECKING([for explicit $am_display_PYTHON exec_prefix]) AC_MSG_RESULT([$am_cv_python_exec_prefix])], [ # no explicit --with-python_exec_prefix, but if # --with-python_prefix was given, use its value for python_exec_prefix too. AS_IF([test -n "$with_python_prefix"], [am_python_exec_prefix_subst=$with_python_prefix am_cv_python_exec_prefix=$with_python_prefix AC_MSG_CHECKING([for python_prefix-given $am_display_PYTHON exec_prefix]) AC_MSG_RESULT([$am_cv_python_exec_prefix])], [ # Set am__usable_exec_prefix whether using GNU or Python values, # since we use that variable for pyexecdir. if test "x$exec_prefix" = xNONE; then am__usable_exec_prefix=$am__usable_prefix else am__usable_exec_prefix=$exec_prefix fi # if $am_use_python_sys; then # using python sys.exec_prefix, not GNU AC_CACHE_CHECK([for python default $am_display_PYTHON exec_prefix], [am_cv_python_exec_prefix], [am_cv_python_exec_prefix=`$PYTHON -c "import sys; sys.stdout.write(sys.exec_prefix)"`]) dnl If sys.exec_prefix is a subdir of $exec_prefix, replace the dnl literal value of $exec_prefix with a variable reference so it can dnl be overridden. case $am_cv_python_exec_prefix in $am__usable_exec_prefix*) am__strip_prefix=`echo "$am__usable_exec_prefix" | sed 's|.|.|g'` am_python_exec_prefix_subst=`echo "$am_cv_python_exec_prefix" | sed "s,^$am__strip_prefix,\\${exec_prefix},"` ;; *) am_python_exec_prefix_subst=$am_cv_python_exec_prefix ;; esac else # using GNU $exec_prefix, not python sys.exec_prefix am_python_exec_prefix_subst='${exec_prefix}' am_python_exec_prefix=$am_python_exec_prefix_subst AC_MSG_CHECKING([for GNU default $am_display_PYTHON exec_prefix]) AC_MSG_RESULT([$am_python_exec_prefix]) fi])]) # Substituting python_exec_prefix_subst. AC_SUBST([PYTHON_EXEC_PREFIX], [$am_python_exec_prefix_subst]) # Factor out some code duplication into this shell variable. am_python_setup_sysconfig="\ import sys # Prefer sysconfig over distutils.sysconfig, for better compatibility # with python 3.x. See automake bug#10227. try: import sysconfig except ImportError: can_use_sysconfig = 0 else: can_use_sysconfig = 1 # Can't use sysconfig in CPython 2.7, since it's broken in virtualenvs: # try: from platform import python_implementation if python_implementation() == 'CPython' and sys.version[[:3]] == '2.7': can_use_sysconfig = 0 except ImportError: pass" dnl emacs-page Set up 4 directories: dnl 1. pythondir: where to install python scripts. This is the dnl site-packages directory, not the python standard library dnl directory like in previous automake betas. This behavior dnl is more consistent with lispdir.m4 for example. dnl Query distutils for this directory. dnl AC_CACHE_CHECK([for $am_display_PYTHON script directory (pythondir)], [am_cv_python_pythondir], [if test "x$am_cv_python_prefix" = x; then am_py_prefix=$am__usable_prefix else am_py_prefix=$am_cv_python_prefix fi am_cv_python_pythondir=`$PYTHON -c " $am_python_setup_sysconfig if can_use_sysconfig: if hasattr(sysconfig, 'get_default_scheme'): scheme = sysconfig.get_default_scheme() else: scheme = sysconfig._get_default_scheme() if scheme == 'posix_local': # Debian's default scheme installs to /usr/local/ but we want to find headers in /usr/ scheme = 'posix_prefix' sitedir = sysconfig.get_path('purelib', scheme, vars={'base':'$am_py_prefix'}) else: from distutils import sysconfig sitedir = sysconfig.get_python_lib(0, 0, prefix='$am_py_prefix') sys.stdout.write(sitedir)"` # case $am_cv_python_pythondir in $am_py_prefix*) am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'` am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,\\${PYTHON_PREFIX},"` ;; *) case $am_py_prefix in /usr|/System*) ;; *) am_cv_python_pythondir="\${PYTHON_PREFIX}/lib/python$PYTHON_VERSION/site-packages" ;; esac ;; esac ]) AC_SUBST([pythondir], [$am_cv_python_pythondir]) dnl 2. pkgpythondir: $PACKAGE directory under pythondir. Was dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is dnl more consistent with the rest of automake. dnl AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE]) dnl 3. pyexecdir: directory for installing python extension modules dnl (shared libraries). dnl Query distutils for this directory. dnl AC_CACHE_CHECK([for $am_display_PYTHON extension module directory (pyexecdir)], [am_cv_python_pyexecdir], [if test "x$am_cv_python_exec_prefix" = x; then am_py_exec_prefix=$am__usable_exec_prefix else am_py_exec_prefix=$am_cv_python_exec_prefix fi am_cv_python_pyexecdir=`$PYTHON -c " $am_python_setup_sysconfig if can_use_sysconfig: if hasattr(sysconfig, 'get_default_scheme'): scheme = sysconfig.get_default_scheme() else: scheme = sysconfig._get_default_scheme() if scheme == 'posix_local': # Debian's default scheme installs to /usr/local/ but we want to find headers in /usr/ scheme = 'posix_prefix' sitedir = sysconfig.get_path('platlib', scheme, vars={'platbase':'$am_py_exec_prefix'}) else: from distutils import sysconfig sitedir = sysconfig.get_python_lib(1, 0, prefix='$am_py_exec_prefix') sys.stdout.write(sitedir)"` # case $am_cv_python_pyexecdir in $am_py_exec_prefix*) am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'` am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,\\${PYTHON_EXEC_PREFIX},"` ;; *) case $am_py_exec_prefix in /usr|/System*) ;; *) am_cv_python_pyexecdir="\${PYTHON_EXEC_PREFIX}/lib/python$PYTHON_VERSION/site-packages" ;; esac ;; esac ]) AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir]) dnl 4. pkgpyexecdir: $(pyexecdir)/$(PACKAGE) dnl AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE]) dnl Run any user-specified action. $2 fi ]) # AM_PYTHON_CHECK_VERSION(PROG, VERSION, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) # --------------------------------------------------------------------------- # Run ACTION-IF-TRUE if the Python interpreter PROG has version >= VERSION. # Run ACTION-IF-FALSE otherwise. # This test uses sys.hexversion instead of the string equivalent (first # word of sys.version), in order to cope with versions such as 2.2c1. # This supports Python 2.0 or higher. (2.0 was released on October 16, 2000). AC_DEFUN([AM_PYTHON_CHECK_VERSION], [prog="import sys # split strings by '.' and convert to numeric. Append some zeros # because we need at least 4 digits for the hex conversion. # map returns an iterator in Python 3.0 and a list in 2.x minver = list(map(int, '$2'.split('.'))) + [[0, 0, 0]] minverhex = 0 # xrange is not present in Python 3.0 and range returns an iterator for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[[i]] sys.exit(sys.hexversion < minverhex)" AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])]) # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_RUN_LOG(COMMAND) # ------------------- # Run COMMAND, save the exit status in ac_status, and log it. # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi if test "$[2]" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT([yes]) # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi AC_CONFIG_COMMANDS_PRE( [AC_MSG_CHECKING([that generated files are newer than configure]) if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi AC_MSG_RESULT([done])]) rm -f conftest.file ]) # Copyright (C) 2009-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # ("yes" being less verbose, "no" or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [dnl AS_HELP_STRING( [--enable-silent-rules], [less verbose build output (undo: "make V=1")]) AS_HELP_STRING( [--disable-silent-rules], [verbose build output (undo: "make V=0")])dnl ]) case $enable_silent_rules in @%:@ ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac dnl dnl A few 'make' implementations (e.g., NonStop OS and NextStep) dnl do not support nested variable expansions. dnl See automake bug#9928 and bug#10237. am_make=${MAKE-make} AC_CACHE_CHECK([whether $am_make supports nested variables], [am_cv_make_support_nested_variables], [if AS_ECHO([['TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi]) if test $am_cv_make_support_nested_variables = yes; then dnl Using '$V' instead of '$(V)' breaks IRIX make. AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AC_SUBST([AM_V])dnl AM_SUBST_NOTMAKE([AM_V])dnl AC_SUBST([AM_DEFAULT_V])dnl AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor 'install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in "make install-strip", and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # -------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar # AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' m4_if([$1], [v7], [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar], [# The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) if test $am_uid -le $am_max_uid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) if test $am_gid -le $am_max_gid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi], [pax], [], [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_$1-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR m4_include([m4/ax_check_compile_flag.m4]) m4_include([m4/ax_cxx_compile_stdcxx.m4]) m4_include([m4/libtool.m4]) m4_include([m4/ltoptions.m4]) m4_include([m4/ltsugar.m4]) m4_include([m4/ltversion.m4]) m4_include([m4/lt~obsolete.m4]) nghttp2-1.68.0/PaxHeaders/bpf0000644000000000000000000000013215077107335012754 xustar0030 mtime=1761382109.348299664 30 atime=1761382109.800298358 30 ctime=1761382109.348299664 nghttp2-1.68.0/bpf/0000755000175100017510000000000015077107335013421 5ustar00runnerrunnernghttp2-1.68.0/bpf/PaxHeaders/reuseport_kern.c0000644000000000000000000000013215077107270016242 xustar0030 mtime=1761382072.961444287 30 atime=1761382106.350309784 30 ctime=1761382109.348299664 nghttp2-1.68.0/bpf/reuseport_kern.c0000644000175100017510000004143715077107270016643 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2021 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include /* * How to compile: * * clang-12 -O2 -Wall -target bpf -g -c reuseport_kern.c -o reuseport_kern.o \ * -I/path/to/kernel/include * * See * https://www.kernel.org/doc/Documentation/kbuild/headers_install.txt * how to install kernel header files. */ /* AES_CBC_decrypt_buffer: https://github.com/kokke/tiny-AES-c License is Public Domain. Commit hash: 12e7744b4919e9d55de75b7ab566326a1c8e7a67 */ #define AES_keyExpSize 176 struct AES_ctx { __u8 RoundKey[AES_keyExpSize]; }; /* The number of columns comprising a state in AES. This is a constant in AES. Value=4 */ #define Nb 4 #define Nr 10 /* The number of rounds in AES Cipher. */ /* state - array holding the intermediate results during decryption. */ typedef __u8 state_t[4][4]; /* The lookup-tables are marked const so they can be placed in read-only storage instead of RAM The numbers below can be computed dynamically trading ROM for RAM - This can be useful in (embedded) bootloader applications, where ROM is often limited. */ static const __u8 rsbox[256] = { 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d}; /* This function adds the round key to state. The round key is added to the state by an XOR function. */ static void AddRoundKey(__u8 round, state_t *state, const __u8 *RoundKey) { __u8 i, j; for (i = 0; i < 4; ++i) { for (j = 0; j < 4; ++j) { (*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j]; } } } static __u8 xtime(__u8 x) { return ((x << 1) ^ (((x >> 7) & 1) * 0x1b)); } #define Multiply(x, y) \ (((y & 1) * x) ^ ((y >> 1 & 1) * xtime(x)) ^ \ ((y >> 2 & 1) * xtime(xtime(x))) ^ \ ((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^ \ ((y >> 4 & 1) * xtime(xtime(xtime(xtime(x)))))) #define getSBoxInvert(num) (rsbox[(num)]) /* MixColumns function mixes the columns of the state matrix. The method used to multiply may be difficult to understand for the inexperienced. Please use the references to gain more information. */ static void InvMixColumns(state_t *state) { int i; __u8 a, b, c, d; for (i = 0; i < 4; ++i) { a = (*state)[i][0]; b = (*state)[i][1]; c = (*state)[i][2]; d = (*state)[i][3]; (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09); (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d); (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b); (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e); } } extern __u32 LINUX_KERNEL_VERSION __kconfig; /* The SubBytes Function Substitutes the values in the state matrix with values in an S-box. */ static void InvSubBytes(state_t *state) { __u8 i, j; if (LINUX_KERNEL_VERSION < KERNEL_VERSION(5, 10, 0)) { for (i = 0; i < 4; ++i) { for (j = 0; j < 4; ++j) { /* Ubuntu 20.04 LTS kernel 5.4.0 needs this workaround otherwise "math between map_value pointer and register with unbounded min value is not allowed". 5.10.0 is a kernel version that works but it might not be the minimum version. */ __u8 k = (*state)[j][i]; (*state)[j][i] = k ? getSBoxInvert(k) : getSBoxInvert(0); } } } else { for (i = 0; i < 4; ++i) { for (j = 0; j < 4; ++j) { (*state)[j][i] = getSBoxInvert((*state)[j][i]); } } } } static void InvShiftRows(state_t *state) { __u8 temp; /* Rotate first row 1 columns to right */ temp = (*state)[3][1]; (*state)[3][1] = (*state)[2][1]; (*state)[2][1] = (*state)[1][1]; (*state)[1][1] = (*state)[0][1]; (*state)[0][1] = temp; /* Rotate second row 2 columns to right */ temp = (*state)[0][2]; (*state)[0][2] = (*state)[2][2]; (*state)[2][2] = temp; temp = (*state)[1][2]; (*state)[1][2] = (*state)[3][2]; (*state)[3][2] = temp; /* Rotate third row 3 columns to right */ temp = (*state)[0][3]; (*state)[0][3] = (*state)[1][3]; (*state)[1][3] = (*state)[2][3]; (*state)[2][3] = (*state)[3][3]; (*state)[3][3] = temp; } static void InvCipher(state_t *state, const __u8 *RoundKey) { /* Add the First round key to the state before starting the rounds. */ AddRoundKey(Nr, state, RoundKey); /* There will be Nr rounds. The first Nr-1 rounds are identical. These Nr rounds are executed in the loop below. Last one without InvMixColumn() */ InvShiftRows(state); InvSubBytes(state); AddRoundKey(Nr - 1, state, RoundKey); InvMixColumns(state); InvShiftRows(state); InvSubBytes(state); AddRoundKey(Nr - 2, state, RoundKey); InvMixColumns(state); InvShiftRows(state); InvSubBytes(state); AddRoundKey(Nr - 3, state, RoundKey); InvMixColumns(state); InvShiftRows(state); InvSubBytes(state); AddRoundKey(Nr - 4, state, RoundKey); InvMixColumns(state); InvShiftRows(state); InvSubBytes(state); AddRoundKey(Nr - 5, state, RoundKey); InvMixColumns(state); InvShiftRows(state); InvSubBytes(state); AddRoundKey(Nr - 6, state, RoundKey); InvMixColumns(state); InvShiftRows(state); InvSubBytes(state); AddRoundKey(Nr - 7, state, RoundKey); InvMixColumns(state); InvShiftRows(state); InvSubBytes(state); AddRoundKey(Nr - 8, state, RoundKey); InvMixColumns(state); InvShiftRows(state); InvSubBytes(state); AddRoundKey(Nr - 9, state, RoundKey); InvMixColumns(state); InvShiftRows(state); InvSubBytes(state); AddRoundKey(Nr - 10, state, RoundKey); } static void AES_ECB_decrypt(const struct AES_ctx *ctx, __u8 *buf) { /* The next function call decrypts the PlainText with the Key using AES algorithm. */ InvCipher((state_t *)buf, ctx->RoundKey); } /* rol32: From linux kernel source code */ /** * rol32 - rotate a 32-bit value left * @word: value to rotate * @shift: bits to roll */ static inline __u32 rol32(__u32 word, unsigned int shift) { return (word << shift) | (word >> ((-shift) & 31)); } /* jhash.h: Jenkins hash support. * * Copyright (C) 2006. Bob Jenkins (bob_jenkins@burtleburtle.net) * * https://burtleburtle.net/bob/hash/ * * These are the credits from Bob's sources: * * lookup3.c, by Bob Jenkins, May 2006, Public Domain. * * These are functions for producing 32-bit hashes for hash table lookup. * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() * are externally useful functions. Routines to test the hash are included * if SELF_TEST is defined. You can use this free for any purpose. It's in * the public domain. It has no warranty. * * Copyright (C) 2009-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) * * I've modified Bob's hash to be useful in the Linux kernel, and * any bugs present are my fault. * Jozsef */ /* __jhash_final - final mixing of 3 32-bit values (a,b,c) into c */ #define __jhash_final(a, b, c) \ { \ c ^= b; \ c -= rol32(b, 14); \ a ^= c; \ a -= rol32(c, 11); \ b ^= a; \ b -= rol32(a, 25); \ c ^= b; \ c -= rol32(b, 16); \ a ^= c; \ a -= rol32(c, 4); \ b ^= a; \ b -= rol32(a, 14); \ c ^= b; \ c -= rol32(b, 24); \ } /* __jhash_nwords - hash exactly 3, 2 or 1 word(s) */ static inline __u32 __jhash_nwords(__u32 a, __u32 b, __u32 c, __u32 initval) { a += initval; b += initval; c += initval; __jhash_final(a, b, c); return c; } /* An arbitrary initial parameter */ #define JHASH_INITVAL 0xdeadbeef static inline __u32 jhash_2words(__u32 a, __u32 b, __u32 initval) { return __jhash_nwords(a, b, 0, initval + JHASH_INITVAL + (2 << 2)); } struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 255); __type(key, __u64); __type(value, __u32); } worker_id_map SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_REUSEPORT_SOCKARRAY); __uint(max_entries, 255); __type(key, __u32); __type(value, __u32); } reuseport_array SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_ARRAY); __uint(max_entries, 1); __type(key, __u32); __type(value, __u64); } sk_info SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_ARRAY); __uint(max_entries, 1); __type(key, __u32); __type(value, struct AES_ctx); } aes_key SEC(".maps"); typedef struct quic_hd { __u8 *dcid; __u32 dcidlen; __u32 dcid_offset; __u8 type; } quic_hd; #define SV_DCIDLEN 17 #define MAX_DCIDLEN 20 #define MIN_DCIDLEN 8 #define WORKER_IDLEN 8 #define WORKER_ID_OFFSET 1 enum { NGTCP2_PKT_INITIAL = 0x0, NGTCP2_PKT_0RTT = 0x1, NGTCP2_PKT_HANDSHAKE = 0x2, NGTCP2_PKT_SHORT = 0x40, }; static inline int parse_quic(quic_hd *qhd, __u8 *data, __u8 *data_end) { __u8 *p; __u64 dcidlen; if (*data & 0x80) { p = data + 1 + 4; /* Do not check the actual DCID length because we might not buffer entire DCID here. */ dcidlen = *p; if (dcidlen > MAX_DCIDLEN || dcidlen < MIN_DCIDLEN) { return -1; } ++p; qhd->type = (*data & 0x30) >> 4; qhd->dcid = p; qhd->dcidlen = dcidlen; qhd->dcid_offset = 6; } else { qhd->type = NGTCP2_PKT_SHORT; qhd->dcid = data + 1; qhd->dcidlen = SV_DCIDLEN; qhd->dcid_offset = 1; } return 0; } static __u32 hash(const __u8 *data, __u32 datalen, __u32 initval) { __u32 a, b; a = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; b = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7]; return jhash_2words(a, b, initval); } static __u32 sk_index_from_dcid(const quic_hd *qhd, const struct sk_reuseport_md *reuse_md, __u64 num_socks) { __u32 len = qhd->dcidlen; __u32 h = reuse_md->hash; __u8 hbuf[8]; if (len > 16) { __builtin_memset(hbuf, 0, sizeof(hbuf)); switch (len) { case 20: __builtin_memcpy(hbuf, qhd->dcid + 16, 4); break; case 19: __builtin_memcpy(hbuf, qhd->dcid + 16, 3); break; case 18: __builtin_memcpy(hbuf, qhd->dcid + 16, 2); break; case 17: __builtin_memcpy(hbuf, qhd->dcid + 16, 1); break; } h = hash(hbuf, sizeof(hbuf), h); len = 16; } if (len > 8) { __builtin_memset(hbuf, 0, sizeof(hbuf)); switch (len) { case 16: __builtin_memcpy(hbuf, qhd->dcid + 8, 8); break; case 15: __builtin_memcpy(hbuf, qhd->dcid + 8, 7); break; case 14: __builtin_memcpy(hbuf, qhd->dcid + 8, 6); break; case 13: __builtin_memcpy(hbuf, qhd->dcid + 8, 5); break; case 12: __builtin_memcpy(hbuf, qhd->dcid + 8, 4); break; case 11: __builtin_memcpy(hbuf, qhd->dcid + 8, 3); break; case 10: __builtin_memcpy(hbuf, qhd->dcid + 8, 2); break; case 9: __builtin_memcpy(hbuf, qhd->dcid + 8, 1); break; } h = hash(hbuf, sizeof(hbuf), h); len = 8; } return hash(qhd->dcid, len, h) % num_socks; } SEC("sk_reuseport") int select_reuseport(struct sk_reuseport_md *reuse_md) { __u32 sk_index, *psk_index; __u64 *pnum_socks; __u32 zero = 0; int rv; quic_hd qhd; __u8 qpktbuf[6 + MAX_DCIDLEN]; struct AES_ctx *aes_ctx; __u8 *worker_id; __u16 remote_port; __u8 *data = reuse_md->data; /* Packets less than 22 bytes never be a valid QUIC packet. */ if (reuse_md->len < sizeof(struct udphdr) + 22) { return SK_DROP; } if (reuse_md->data + sizeof(struct udphdr) > reuse_md->data_end) { return SK_DROP; } remote_port = (data[0] << 8) + data[1]; switch (remote_port) { case 1900: case 5353: case 11211: case 20800: case 27015: return SK_DROP; default: if (remote_port < 1024) { return SK_DROP; } } if (bpf_skb_load_bytes(reuse_md, sizeof(struct udphdr), qpktbuf, sizeof(qpktbuf)) != 0) { return SK_DROP; } pnum_socks = bpf_map_lookup_elem(&sk_info, &zero); if (pnum_socks == NULL) { return SK_DROP; } aes_ctx = bpf_map_lookup_elem(&aes_key, &zero); if (aes_ctx == NULL) { return SK_DROP; } rv = parse_quic(&qhd, qpktbuf, qpktbuf + sizeof(qpktbuf)); if (rv != 0) { return SK_DROP; } switch (qhd.type) { case NGTCP2_PKT_INITIAL: case NGTCP2_PKT_0RTT: if (qhd.dcidlen == SV_DCIDLEN) { worker_id = qhd.dcid + WORKER_ID_OFFSET; AES_ECB_decrypt(aes_ctx, worker_id); psk_index = bpf_map_lookup_elem(&worker_id_map, worker_id); if (psk_index != NULL) { sk_index = *psk_index; break; } } sk_index = sk_index_from_dcid(&qhd, reuse_md, *pnum_socks); break; case NGTCP2_PKT_HANDSHAKE: case NGTCP2_PKT_SHORT: if (qhd.dcidlen != SV_DCIDLEN) { return SK_DROP; } worker_id = qhd.dcid + WORKER_ID_OFFSET; AES_ECB_decrypt(aes_ctx, worker_id); psk_index = bpf_map_lookup_elem(&worker_id_map, worker_id); if (psk_index == NULL) { sk_index = sk_index_from_dcid(&qhd, reuse_md, *pnum_socks); break; } sk_index = *psk_index; break; default: return SK_DROP; } rv = bpf_sk_select_reuseport(reuse_md, &reuseport_array, &sk_index, 0); if (rv != 0) { return SK_DROP; } return SK_PASS; } nghttp2-1.68.0/bpf/PaxHeaders/CMakeLists.txt0000644000000000000000000000013115077107270015566 xustar0030 mtime=1761382072.961444287 30 atime=1761382106.349309788 29 ctime=1761382109.34629967 nghttp2-1.68.0/bpf/CMakeLists.txt0000644000175100017510000000106715077107270016163 0ustar00runnerrunnerif(LIBBPF_FOUND) add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/reuseport_kern.o" COMMAND ${CMAKE_C_COMPILER} ${BPFCFLAGS} ${EXTRABPFCFLAGS} -I${LIBBPF_INCLUDE_DIRS} -target bpf -c reuseport_kern.c -o "${CMAKE_CURRENT_BINARY_DIR}/reuseport_kern.o" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" VERBATIM) add_custom_target(bpf ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/reuseport_kern.o" VERBATIM) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/reuseport_kern.o" DESTINATION "${CMAKE_INSTALL_LIBDIR}/${CMAKE_PROJECT_NAME}") endif() nghttp2-1.68.0/bpf/PaxHeaders/Makefile.in0000644000000000000000000000013115077107305015072 xustar0030 mtime=1761382085.456387641 29 atime=1761382103.90632055 30 ctime=1761382109.345299673 nghttp2-1.68.0/bpf/Makefile.in0000644000175100017510000004467115077107305015477 0ustar00runnerrunner# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # nghttp2 - HTTP/2 C Library # Copyright (c) 2021 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = bpf ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(bpf_pkglibdir)" DATA = $(bpf_pkglib_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPLDFLAGS = @APPLDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BPFCFLAGS = @BPFCFLAGS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CXX = @CXX@ CXX1XCXXFLAGS = @CXX1XCXXFLAGS@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXTRABPFCFLAGS = @EXTRABPFCFLAGS@ EXTRACFLAG = @EXTRACFLAG@ EXTRA_DEFS = @EXTRA_DEFS@ FGREP = @FGREP@ FILECMD = @FILECMD@ GREP = @GREP@ HAVE_CXX20 = @HAVE_CXX20@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ JANSSON_CFLAGS = @JANSSON_CFLAGS@ JANSSON_LIBS = @JANSSON_LIBS@ JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ JEMALLOC_LIBS = @JEMALLOC_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBBPF_CFLAGS = @LIBBPF_CFLAGS@ LIBBPF_LIBS = @LIBBPF_LIBS@ LIBBROTLIDEC_CFLAGS = @LIBBROTLIDEC_CFLAGS@ LIBBROTLIDEC_LIBS = @LIBBROTLIDEC_LIBS@ LIBBROTLIENC_CFLAGS = @LIBBROTLIENC_CFLAGS@ LIBBROTLIENC_LIBS = @LIBBROTLIENC_LIBS@ LIBCARES_CFLAGS = @LIBCARES_CFLAGS@ LIBCARES_LIBS = @LIBCARES_LIBS@ LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ LIBEV_CFLAGS = @LIBEV_CFLAGS@ LIBEV_LIBS = @LIBEV_LIBS@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@ LIBMRUBY_LIBS = @LIBMRUBY_LIBS@ LIBNGHTTP3_CFLAGS = @LIBNGHTTP3_CFLAGS@ LIBNGHTTP3_LIBS = @LIBNGHTTP3_LIBS@ LIBNGTCP2_CFLAGS = @LIBNGTCP2_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS = @LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_LIBS = @LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS = @LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS@ LIBNGTCP2_CRYPTO_LIBRESSL_LIBS = @LIBNGTCP2_CRYPTO_LIBRESSL_LIBS@ LIBNGTCP2_CRYPTO_OSSL_CFLAGS = @LIBNGTCP2_CRYPTO_OSSL_CFLAGS@ LIBNGTCP2_CRYPTO_OSSL_LIBS = @LIBNGTCP2_CRYPTO_OSSL_LIBS@ LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS = @LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ LIBNGTCP2_CRYPTO_QUICTLS_LIBS = @LIBNGTCP2_CRYPTO_QUICTLS_LIBS@ LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS = @LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS@ LIBNGTCP2_CRYPTO_WOLFSSL_LIBS = @LIBNGTCP2_CRYPTO_WOLFSSL_LIBS@ LIBNGTCP2_LIBS = @LIBNGTCP2_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBTOOL_LDFLAGS = @LIBTOOL_LDFLAGS@ LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ LIBXML2_LIBS = @LIBXML2_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_AGE = @LT_AGE@ LT_CURRENT = @LT_CURRENT@ LT_REVISION = @LT_REVISION@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ SYSTEMD_LIBS = @SYSTEMD_LIBS@ TESTLDADD = @TESTLDADD@ VERSION = @VERSION@ WARNCFLAGS = @WARNCFLAGS@ WARNCXXFLAGS = @WARNCXXFLAGS@ WOLFSSL_CFLAGS = @WOLFSSL_CFLAGS@ WOLFSSL_LIBS = @WOLFSSL_LIBS@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ EXTRA_DIST = CMakeLists.txt reuseport_kern.c @HAVE_LIBBPF_TRUE@bpf_pkglibdir = $(pkglibdir) @HAVE_LIBBPF_TRUE@bpf_pkglib_DATA = reuseport_kern.o all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu bpf/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu bpf/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-bpf_pkglibDATA: $(bpf_pkglib_DATA) @$(NORMAL_INSTALL) @list='$(bpf_pkglib_DATA)'; test -n "$(bpf_pkglibdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bpf_pkglibdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bpf_pkglibdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(bpf_pkglibdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(bpf_pkglibdir)" || exit $$?; \ done uninstall-bpf_pkglibDATA: @$(NORMAL_UNINSTALL) @list='$(bpf_pkglib_DATA)'; test -n "$(bpf_pkglibdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(bpf_pkglibdir)'; $(am__uninstall_files_from_dir) tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(DATA) installdirs: for dir in "$(DESTDIR)$(bpf_pkglibdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." @HAVE_LIBBPF_FALSE@clean-local: clean: clean-am clean-am: clean-generic clean-libtool clean-local mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-bpf_pkglibDATA install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-bpf_pkglibDATA .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ clean-local cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am info info-am \ install install-am install-bpf_pkglibDATA install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ uninstall-am uninstall-bpf_pkglibDATA .PRECIOUS: Makefile @HAVE_LIBBPF_TRUE@all: $(builddir)/reuseport_kern.o @HAVE_LIBBPF_TRUE@$(builddir)/reuseport_kern.o: reuseport_kern.c @HAVE_LIBBPF_TRUE@ $(CC) @LIBBPF_CFLAGS@ @BPFCFLAGS@ @EXTRABPFCFLAGS@ \ @HAVE_LIBBPF_TRUE@ -target bpf -c $< -o $@ @HAVE_LIBBPF_TRUE@clean-local: @HAVE_LIBBPF_TRUE@ -rm -f reuseport_kern.o # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nghttp2-1.68.0/bpf/PaxHeaders/Makefile.am0000644000000000000000000000013215077107270015063 xustar0030 mtime=1761382072.961444287 30 atime=1761382085.445387688 30 ctime=1761382109.344299676 nghttp2-1.68.0/bpf/Makefile.am0000644000175100017510000000270615077107270015460 0ustar00runnerrunner# nghttp2 - HTTP/2 C Library # Copyright (c) 2021 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. EXTRA_DIST = CMakeLists.txt reuseport_kern.c if HAVE_LIBBPF bpf_pkglibdir = $(pkglibdir) bpf_pkglib_DATA = reuseport_kern.o all: $(builddir)/reuseport_kern.o $(builddir)/reuseport_kern.o: reuseport_kern.c $(CC) @LIBBPF_CFLAGS@ @BPFCFLAGS@ @EXTRABPFCFLAGS@ \ -target bpf -c $< -o $@ clean-local: -rm -f reuseport_kern.o endif # HAVE_LIBBPF nghttp2-1.68.0/PaxHeaders/compile0000644000000000000000000000013215077107305013632 xustar0030 mtime=1761382085.313388253 30 atime=1761382104.853316378 30 ctime=1761382107.838304029 nghttp2-1.68.0/compile0000755000175100017510000001635015077107305014232 0ustar00runnerrunner#! /bin/sh # Wrapper for compilers which do not understand '-c -o'. scriptversion=2018-03-07.03; # UTC # Copyright (C) 1999-2021 Free Software Foundation, Inc. # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' # We need space, tab and new line, in precisely that order. Quoting is # there to prevent tools from complaining about whitespace usage. IFS=" "" $nl" file_conv= # func_file_conv build_file lazy # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. If the determined conversion # type is listed in (the comma separated) LAZY, no conversion will # take place. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN* | MSYS*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv/,$2, in *,$file_conv,*) ;; mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin/* | msys/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_cl_dashL linkdir # Make cl look for libraries in LINKDIR func_cl_dashL () { func_file_conv "$1" if test -z "$lib_path"; then lib_path=$file else lib_path="$lib_path;$file" fi linker_opts="$linker_opts -LIBPATH:$file" } # func_cl_dashl library # Do a library search-path lookup for cl func_cl_dashl () { lib=$1 found=no save_IFS=$IFS IFS=';' for dir in $lib_path $LIB do IFS=$save_IFS if $shared && test -f "$dir/$lib.dll.lib"; then found=yes lib=$dir/$lib.dll.lib break fi if test -f "$dir/$lib.lib"; then found=yes lib=$dir/$lib.lib break fi if test -f "$dir/lib$lib.a"; then found=yes lib=$dir/lib$lib.a break fi done IFS=$save_IFS if test "$found" != yes; then lib=$lib.lib fi } # func_cl_wrapper cl arg... # Adjust compile command to suit cl func_cl_wrapper () { # Assume a capable shell lib_path= shared=: linker_opts= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. eat=1 case $2 in *.o | *.[oO][bB][jJ]) func_file_conv "$2" set x "$@" -Fo"$file" shift ;; *) func_file_conv "$2" set x "$@" -Fe"$file" shift ;; esac ;; -I) eat=1 func_file_conv "$2" mingw set x "$@" -I"$file" shift ;; -I*) func_file_conv "${1#-I}" mingw set x "$@" -I"$file" shift ;; -l) eat=1 func_cl_dashl "$2" set x "$@" "$lib" shift ;; -l*) func_cl_dashl "${1#-l}" set x "$@" "$lib" shift ;; -L) eat=1 func_cl_dashL "$2" ;; -L*) func_cl_dashL "${1#-L}" ;; -static) shared=false ;; -Wl,*) arg=${1#-Wl,} save_ifs="$IFS"; IFS=',' for flag in $arg; do IFS="$save_ifs" linker_opts="$linker_opts $flag" done IFS="$save_ifs" ;; -Xlinker) eat=1 linker_opts="$linker_opts $2" ;; -*) set x "$@" "$1" shift ;; *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) func_file_conv "$1" set x "$@" -Tp"$file" shift ;; *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) func_file_conv "$1" mingw set x "$@" "$file" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -n "$linker_opts"; then linker_opts="-link$linker_opts" fi exec "$@" $linker_opts exit 1 } eat= case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] Wrapper for compilers which do not understand '-c -o'. Remove '-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the right script to run: please start by reading the file 'INSTALL'. Report bugs to . EOF exit $? ;; -v | --v*) echo "compile $scriptversion" exit $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac ofile= cfile= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. # So we strip '-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) ofile=$2 ;; *) set x "$@" -o "$2" shift ;; esac ;; *.c) cfile=$1 set x "$@" "$1" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -z "$ofile" || test -z "$cfile"; then # If no '-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # '.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. # Note: use '[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir '$lockdir'; exit 1" 1 2 15 # Run the compile. "$@" ret=$? if test -f "$cofile"; then test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: nghttp2-1.68.0/PaxHeaders/src0000644000000000000000000000013215077107335012774 xustar0030 mtime=1761382109.286299844 30 atime=1761382109.800298358 30 ctime=1761382109.286299844 nghttp2-1.68.0/src/0000755000175100017510000000000015077107335013441 5ustar00runnerrunnernghttp2-1.68.0/src/PaxHeaders/ssl_compat.h0000644000000000000000000000013215077107271015365 xustar0030 mtime=1761382073.000444106 30 atime=1761382080.105411485 30 ctime=1761382109.072300462 nghttp2-1.68.0/src/ssl_compat.h0000644000175100017510000001046115077107271015757 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SSL_COMPAT_H #define SSL_COMPAT_H #include "nghttp2_config.h" #include #ifdef HAVE_WOLFSSL # include # include # define NGHTTP2_OPENSSL_IS_WOLFSSL using nghttp2_ssl_op_type = long; using nghttp2_ssl_proto_version_type = int; using nghttp2_ssl_key_length_type = int; using nghttp2_ssl_stack_index_type = int; using nghttp2_ssl_timeout_type = uint32_t; using nghttp2_ssl_rand_length_type = int; using nghttp2_ssl_verify_host_length_type = unsigned int; inline constexpr auto NGHTTP2_CERT_TYPE_ECDSA = ECDSAk; inline constexpr auto NGHTTP2_CERT_TYPE_ML_DSA_44 = ML_DSA_LEVEL2k; inline constexpr auto NGHTTP2_CERT_TYPE_ML_DSA_65 = ML_DSA_LEVEL3k; inline constexpr auto NGHTTP2_CERT_TYPE_ML_DSA_87 = ML_DSA_LEVEL5k; #else // !defined(HAVE_WOLFSSL) # include # ifdef LIBRESSL_VERSION_NUMBER # define NGHTTP2_OPENSSL_IS_LIBRESSL using nghttp2_ssl_op_type = long; using nghttp2_ssl_proto_version_type = uint16_t; using nghttp2_ssl_key_length_type = int; using nghttp2_ssl_stack_index_type = int; using nghttp2_ssl_timeout_type = long; using nghttp2_ssl_rand_length_type = int; using nghttp2_ssl_verify_host_length_type = size_t; # endif // !defined(LIBRESSL_VERSION_NUMBER) # if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) # define NGHTTP2_OPENSSL_IS_BORINGSSL using nghttp2_ssl_op_type = uint32_t; using nghttp2_ssl_proto_version_type = uint16_t; using nghttp2_ssl_key_length_type = size_t; using nghttp2_ssl_stack_index_type = size_t; using nghttp2_ssl_timeout_type = uint32_t; using nghttp2_ssl_rand_length_type = size_t; using nghttp2_ssl_verify_host_length_type = size_t; # endif // defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) # if !defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && \ !defined(NGHTTP2_OPENSSL_IS_LIBRESSL) # define NGHTTP2_GENUINE_OPENSSL # endif // !defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && // !defined(NGHTTP2_OPENSSL_IS_LIBRESSL) # ifdef NGHTTP2_GENUINE_OPENSSL # define OPENSSL_3_0_0_API (OPENSSL_VERSION_NUMBER >= 0x30000000L) # define OPENSSL_3_5_0_API (OPENSSL_VERSION_NUMBER >= 0x30500000L) # if OPENSSL_VERSION_NUMBER >= 0x30000000L using nghttp2_ssl_op_type = uint64_t; # else // OPENSSL_VERSION_NUMBER < 0x30000000L using nghttp2_ssl_op_type = unsigned long; # endif // OPENSSL_VERSION_NUMBER < 0x30000000L using nghttp2_ssl_proto_version_type = long; using nghttp2_ssl_key_length_type = int; using nghttp2_ssl_stack_index_type = int; using nghttp2_ssl_timeout_type = long; using nghttp2_ssl_rand_length_type = int; using nghttp2_ssl_verify_host_length_type = size_t; # else // !defined(NGHTTP2_GENUINE_OPENSSL) # define OPENSSL_3_0_0_API 0 # define OPENSSL_3_5_0_API 0 # endif // !defined(NGHTTP2_GENUINE_OPENSSL) inline constexpr auto NGHTTP2_CERT_TYPE_ECDSA = EVP_PKEY_EC; # if OPENSSL_3_5_0_API inline constexpr auto NGHTTP2_CERT_TYPE_ML_DSA_44 = EVP_PKEY_ML_DSA_44; inline constexpr auto NGHTTP2_CERT_TYPE_ML_DSA_65 = EVP_PKEY_ML_DSA_65; inline constexpr auto NGHTTP2_CERT_TYPE_ML_DSA_87 = EVP_PKEY_ML_DSA_87; # endif // OPENSSL_3_5_0_API #endif // !defined(HAVE_WOLFSSL) #endif // !defined(SSL_COMPAT_H) nghttp2-1.68.0/src/PaxHeaders/buffer.h0000644000000000000000000000013115077107270014470 xustar0029 mtime=1761382072.98444418 30 atime=1761382106.172310568 30 ctime=1761382109.168300184 nghttp2-1.68.0/src/buffer.h0000644000175100017510000000540015077107270015060 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef BUFFER_H #define BUFFER_H #include "nghttp2_config.h" #include #include "template.h" namespace nghttp2 { template struct Buffer { constexpr Buffer() noexcept : pos(buf), last(pos) {} // Returns the number of bytes to read. constexpr size_t rleft() const noexcept { return as_unsigned(last - pos); } // Returns the number of bytes this buffer can store. constexpr size_t wleft() const noexcept { return as_unsigned(&buf[N] - last); } // Writes up to min(wleft(), |count|) bytes from buffer pointed by // |src|. Returns number of bytes written. constexpr size_t write(const void *src, size_t count) { count = std::min(count, wleft()); auto p = static_cast(src); last = std::ranges::copy_n(p, as_signed(count), last).out; return count; } constexpr size_t write(size_t count) { count = std::min(count, wleft()); last += count; return count; } // Drains min(rleft(), |count|) bytes from start of the buffer. constexpr size_t drain(size_t count) { count = std::min(count, rleft()); pos += count; return count; } constexpr size_t drain_reset(size_t count) { count = std::min(count, rleft()); last = std::ranges::copy(pos + count, last, buf).out; pos = buf; return count; } constexpr void reset() noexcept { pos = last = buf; } constexpr uint8_t *begin() noexcept { return buf; } constexpr uint8_t &operator[](size_t n) { return buf[n]; } constexpr const uint8_t &operator[](size_t n) const { return buf[n]; } uint8_t buf[N]; uint8_t *pos, *last; }; } // namespace nghttp2 #endif // !defined(BUFFER_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_live_check.h0000644000000000000000000000013215077107270016540 xustar0030 mtime=1761382072.996444125 30 atime=1761382106.130310753 30 ctime=1761382109.127300303 nghttp2-1.68.0/src/shrpx_live_check.h0000644000175100017510000000730015077107270017130 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_LIVE_CHECK_H #define SHRPX_LIVE_CHECK_H #include "shrpx.h" #include #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include #include #include "shrpx_connection.h" namespace shrpx { class Worker; struct DownstreamAddr; struct DNSQuery; class LiveCheck { public: LiveCheck(struct ev_loop *loop, SSL_CTX *ssl_ctx, Worker *worker, DownstreamAddr *addr, std::mt19937 &gen); ~LiveCheck(); void disconnect(); void on_success(); void on_failure(); int initiate_connection(); // Schedules next connection attempt void schedule(); // Low level I/O operation callback; they are called from do_read() // or do_write(). int noop(); int connected(); int tls_handshake(); int read_tls(); int write_tls(); int read_clear(); int write_clear(); int do_read(); int do_write(); // These functions are used to feed / extract data to // nghttp2_session object. int on_read(const uint8_t *data, size_t len); int on_write(); // Call this function when HTTP/2 connection was established. We // don't call this function for HTTP/1 at the moment. int connection_made(); void start_settings_timer(); void stop_settings_timer(); // Call this function when SETTINGS ACK was received from server. void settings_ack_received(); void signal_write(); private: Connection conn_; DefaultMemchunks wb_; std::mt19937 &gen_; ev_timer backoff_timer_; ev_timer settings_timer_; std::function read_, write_; Worker *worker_; // nullptr if no TLS is configured SSL_CTX *ssl_ctx_; // Address of remote endpoint DownstreamAddr *addr_; nghttp2_session *session_; // Actual remote address used to contact backend. This is initially // nullptr, and may point to either &addr_->addr, or // resolved_addr_.get(). const Address *raddr_; // Resolved IP address if dns parameter is used std::unique_ptr
resolved_addr_; std::unique_ptr dns_query_; // The number of successful connect attempt in a row. size_t success_count_; // The number of unsuccessful connect attempt in a row. size_t fail_count_; // true when SETTINGS ACK has been received from server. bool settings_ack_received_; // true when GOAWAY has been queued. bool session_closing_; }; } // namespace shrpx #endif // !defined(SHRPX_LIVE_CHECK_H) nghttp2-1.68.0/src/PaxHeaders/app_helper.h0000644000000000000000000000013115077107270015336 xustar0029 mtime=1761382072.98444418 30 atime=1761382106.072311009 30 ctime=1761382109.068300474 nghttp2-1.68.0/src/app_helper.h0000644000175100017510000000661015077107270015732 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef APP_HELPER_H #define APP_HELPER_H #include "nghttp2_config.h" #include #include #ifdef HAVE_SYS_TIME_H # include #endif // defined(HAVE_SYS_TIME_H) #include #include #include namespace nghttp2 { int verbose_on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *user_data); int verbose_on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data); int verbose_on_invalid_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, void *user_data); int verbose_on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data); int verbose_on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data); int verbose_error_callback(nghttp2_session *session, int lib_error_code, const char *msg, size_t len, void *user_data); // Returns difference between |a| and |b| in milliseconds, assuming // |a| is more recent than |b|. template std::chrono::milliseconds time_delta(const TimePoint &a, const TimePoint &b) { return std::chrono::duration_cast(a - b); } // Resets timer void reset_timer(); // Returns the duration since timer reset. std::chrono::milliseconds get_timer(); // Returns current time point. std::chrono::steady_clock::time_point get_time(); void print_timer(); // Setting true will print characters with ANSI color escape codes // when printing HTTP2 frames. This function changes a static // variable. void set_color_output(bool f); // Set output file when printing HTTP2 frames. By default, stdout is // used. void set_output(FILE *file); } // namespace nghttp2 #endif // !defined(APP_HELPER_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_http2_downstream_connection.cc0000644000000000000000000000013215077107270022325 xustar0030 mtime=1761382072.994444134 30 atime=1761382106.103310872 30 ctime=1761382109.099300384 nghttp2-1.68.0/src/shrpx_http2_downstream_connection.cc0000644000175100017510000004513615077107270022726 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_http2_downstream_connection.h" #ifdef HAVE_UNISTD_H # include #endif // defined(HAVE_UNISTD_H) #include "llhttp.h" #include "shrpx_client_handler.h" #include "shrpx_upstream.h" #include "shrpx_downstream.h" #include "shrpx_config.h" #include "shrpx_error.h" #include "shrpx_http.h" #include "shrpx_http2_session.h" #include "shrpx_worker.h" #include "shrpx_log.h" #include "http2.h" #include "util.h" #include "ssl_compat.h" using namespace nghttp2; namespace shrpx { Http2DownstreamConnection::Http2DownstreamConnection(Http2Session *http2session) : dlnext(nullptr), dlprev(nullptr), http2session_(http2session), sd_(nullptr) {} Http2DownstreamConnection::~Http2DownstreamConnection() { if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "Deleting"; } if (downstream_) { downstream_->disable_downstream_rtimer(); downstream_->disable_downstream_wtimer(); uint32_t error_code; if (downstream_->get_request_state() == DownstreamState::STREAM_CLOSED && downstream_->get_upgraded()) { // For upgraded connection, send NO_ERROR. Should we consider // request states other than DownstreamState::STREAM_CLOSED ? error_code = NGHTTP2_NO_ERROR; } else { error_code = NGHTTP2_INTERNAL_ERROR; } if (http2session_->get_state() == Http2SessionState::CONNECTED && downstream_->get_downstream_stream_id() != -1) { submit_rst_stream(downstream_, error_code); auto &resp = downstream_->response(); http2session_->consume( static_cast(downstream_->get_downstream_stream_id()), resp.unconsumed_body_length); resp.unconsumed_body_length = 0; http2session_->signal_write(); } } http2session_->remove_downstream_connection(this); if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "Deleted"; } } int Http2DownstreamConnection::attach_downstream(Downstream *downstream) { if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream; } http2session_->add_downstream_connection(this); http2session_->signal_write(); downstream_ = downstream; downstream_->reset_downstream_rtimer(); auto &req = downstream_->request(); // HTTP/2 disables HTTP Upgrade. if (req.method != HTTP_CONNECT && req.connect_proto == ConnectProto::NONE) { req.upgrade_request = false; } return 0; } void Http2DownstreamConnection::detach_downstream(Downstream *downstream) { if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream; } auto &resp = downstream_->response(); if (downstream_->get_downstream_stream_id() != -1) { if (submit_rst_stream(downstream) == 0) { http2session_->signal_write(); } http2session_->consume( static_cast(downstream_->get_downstream_stream_id()), resp.unconsumed_body_length); resp.unconsumed_body_length = 0; http2session_->signal_write(); } downstream->disable_downstream_rtimer(); downstream->disable_downstream_wtimer(); downstream_ = nullptr; } int Http2DownstreamConnection::submit_rst_stream(Downstream *downstream, uint32_t error_code) { int rv = -1; if (http2session_->get_state() == Http2SessionState::CONNECTED && downstream->get_downstream_stream_id() != -1) { switch (downstream->get_response_state()) { case DownstreamState::MSG_RESET: case DownstreamState::MSG_BAD_HEADER: case DownstreamState::MSG_COMPLETE: break; default: if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream << ", stream_id=" << downstream->get_downstream_stream_id() << ", error_code=" << error_code; } rv = http2session_->submit_rst_stream( static_cast(downstream->get_downstream_stream_id()), error_code); } } return rv; } namespace { nghttp2_ssize http2_data_read_callback(nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *user_data) { int rv; auto sd = static_cast( nghttp2_session_get_stream_user_data(session, stream_id)); if (!sd || !sd->dconn) { return NGHTTP2_ERR_DEFERRED; } auto dconn = sd->dconn; auto downstream = dconn->get_downstream(); if (!downstream) { // In this case, RST_STREAM should have been issued. But depending // on the priority, DATA frame may come first. return NGHTTP2_ERR_DEFERRED; } const auto &req = downstream->request(); auto input = downstream->get_request_buf(); auto nread = std::min(input->rleft(), length); auto input_empty = input->rleft() == nread; *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY; if (input_empty && downstream->get_request_state() == DownstreamState::MSG_COMPLETE && // If connection is upgraded, don't set EOF flag, since HTTP/1 // will set MSG_COMPLETE to request state after upgrade response // header is seen. (!req.upgrade_request || (downstream->get_response_state() == DownstreamState::HEADER_COMPLETE && !downstream->get_upgraded()))) { *data_flags |= NGHTTP2_DATA_FLAG_EOF; const auto &trailers = req.fs.trailers(); if (!trailers.empty()) { std::vector nva; nva.reserve(trailers.size()); http2::copy_headers_to_nva_nocopy(nva, trailers, http2::HDOP_STRIP_ALL); if (!nva.empty()) { rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size()); if (rv != 0) { if (nghttp2_is_fatal(rv)) { return NGHTTP2_ERR_CALLBACK_FAILURE; } } else { *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; } } } } if (nread == 0 && (*data_flags & NGHTTP2_DATA_FLAG_EOF) == 0) { downstream->disable_downstream_wtimer(); return NGHTTP2_ERR_DEFERRED; } return as_signed(nread); } } // namespace int Http2DownstreamConnection::push_request_headers() { int rv; if (!downstream_) { return 0; } if (!http2session_->can_push_request(downstream_)) { // The HTTP2 session to the backend has not been established or // connection is now being checked. This function will be called // again just after it is established. downstream_->set_request_pending(true); http2session_->start_checking_connection(); return 0; } downstream_->set_request_pending(false); const auto &req = downstream_->request(); if (req.connect_proto != ConnectProto::NONE && !http2session_->get_allow_connect_proto()) { return -1; } auto &balloc = downstream_->get_block_allocator(); auto config = get_config(); auto &httpconf = config->http; auto &http2conf = config->http2; auto no_host_rewrite = httpconf.no_host_rewrite || config->http2_proxy || req.regular_connect_method(); // http2session_ has already in CONNECTED state, so we can get // addr_idx here. // For HTTP/1.0 request, there is no authority in request. In that // case, we use backend server's host nonetheless. auto authority = http2session_->get_addr()->hostport; if (no_host_rewrite && !req.authority.empty()) { authority = req.authority; } downstream_->set_request_downstream_host(authority); size_t num_cookies = 0; if (!http2conf.no_cookie_crumbling) { num_cookies = downstream_->count_crumble_request_cookie(); } // 11 means: // 1. :method // 2. :scheme // 3. :path // 4. :authority (or host) // 5. :protocol (optional) // 6. via (optional) // 7. x-forwarded-for (optional) // 8. x-forwarded-proto (optional) // 9. te (optional) // 10. forwarded (optional) // 11. early-data (optional) auto nva = std::vector(); nva.reserve(req.fs.headers().size() + 11 + num_cookies + httpconf.add_request_headers.size()); if (req.connect_proto == ConnectProto::WEBSOCKET) { nva.push_back(http2::make_field(":method"sv, "CONNECT"sv)); nva.push_back(http2::make_field(":protocol"sv, "websocket"sv)); } else { nva.push_back( http2::make_field(":method"sv, http2::to_method_string(req.method))); } if (!req.regular_connect_method()) { assert(!req.scheme.empty()); auto addr = http2session_->get_addr(); assert(addr); // We will handle more protocol scheme upgrade in the future. if (addr->tls && addr->upgrade_scheme && req.scheme == "http"sv) { nva.push_back(http2::make_field(":scheme"sv, "https"sv)); } else { nva.push_back(http2::make_field(":scheme"sv, req.scheme)); } if (req.method == HTTP_OPTIONS && req.path.empty()) { nva.push_back(http2::make_field(":path"sv, "*"sv)); } else { nva.push_back(http2::make_field(":path"sv, req.path)); } if (!req.no_authority || req.connect_proto != ConnectProto::NONE) { nva.push_back(http2::make_field(":authority"sv, authority)); } else { nva.push_back(http2::make_field("host"sv, authority)); } } else { nva.push_back(http2::make_field(":authority"sv, authority)); } auto &fwdconf = httpconf.forwarded; auto &xffconf = httpconf.xff; auto &xfpconf = httpconf.xfp; auto &earlydataconf = httpconf.early_data; uint32_t build_flags = (fwdconf.strip_incoming ? http2::HDOP_STRIP_FORWARDED : 0) | (xffconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_FOR : 0) | (xfpconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_PROTO : 0) | (earlydataconf.strip_incoming ? http2::HDOP_STRIP_EARLY_DATA : 0) | http2::HDOP_STRIP_SEC_WEBSOCKET_KEY; http2::copy_headers_to_nva_nocopy(nva, req.fs.headers(), build_flags); if (!http2conf.no_cookie_crumbling) { downstream_->crumble_request_cookie(nva); } auto upstream = downstream_->get_upstream(); auto handler = upstream->get_client_handler(); #if defined(NGHTTP2_GENUINE_OPENSSL) || \ defined(NGHTTP2_OPENSSL_IS_BORINGSSL) || defined(NGHTTP2_OPENSSL_IS_WOLFSSL) auto conn = handler->get_connection(); if (conn->tls.ssl && !SSL_is_init_finished(conn->tls.ssl)) { nva.push_back(http2::make_field("early-data"sv, "1"sv)); } #endif // defined(NGHTTP2_GENUINE_OPENSSL) || // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) || // defined(NGHTTP2_OPENSSL_IS_WOLFSSL) auto fwd = fwdconf.strip_incoming ? nullptr : req.fs.header(http2::HD_FORWARDED); if (fwdconf.params) { auto params = fwdconf.params; if (config->http2_proxy || req.regular_connect_method()) { params &= static_cast(~FORWARDED_PROTO); } auto value = http::create_forwarded( balloc, params, handler->get_forwarded_by(), handler->get_forwarded_for(), req.authority, req.scheme); if (fwd || !value.empty()) { if (fwd) { if (value.empty()) { value = fwd->value; } else { value = concat_string_ref(balloc, fwd->value, ", "sv, value); } } nva.push_back(http2::make_field("forwarded"sv, value)); } } else if (fwd) { nva.push_back(http2::make_field("forwarded"sv, fwd->value)); } auto xff = xffconf.strip_incoming ? nullptr : req.fs.header(http2::HD_X_FORWARDED_FOR); if (xffconf.add) { std::string_view xff_value; const auto &addr = upstream->get_client_handler()->get_ipaddr(); if (xff) { xff_value = concat_string_ref(balloc, xff->value, ", "sv, addr); } else { xff_value = addr; } nva.push_back(http2::make_field("x-forwarded-for"sv, xff_value)); } else if (xff) { nva.push_back(http2::make_field("x-forwarded-for"sv, xff->value)); } if (!config->http2_proxy && !req.regular_connect_method()) { auto xfp = xfpconf.strip_incoming ? nullptr : req.fs.header(http2::HD_X_FORWARDED_PROTO); if (xfpconf.add) { std::string_view xfp_value; // We use same protocol with :scheme header field if (xfp) { xfp_value = concat_string_ref(balloc, xfp->value, ", "sv, req.scheme); } else { xfp_value = req.scheme; } nva.push_back(http2::make_field("x-forwarded-proto"sv, xfp_value)); } else if (xfp) { nva.push_back(http2::make_field("x-forwarded-proto"sv, xfp->value)); } } auto via = req.fs.header(http2::HD_VIA); if (httpconf.no_via) { if (via) { nva.push_back(http2::make_field("via"sv, (*via).value)); } } else { size_t vialen = 16; if (via) { vialen += via->value.size() + 2; } auto iov = make_byte_ref(balloc, vialen + 1); auto p = std::ranges::begin(iov); if (via) { p = std::ranges::copy(via->value, p).out; p = std::ranges::copy(", "sv, p).out; } p = http::create_via_header_value(p, req.http_major, req.http_minor); *p = '\0'; nva.push_back( http2::make_field("via"sv, as_string_view(std::ranges::begin(iov), p))); } auto te = req.fs.header(http2::HD_TE); // HTTP/1 upstream request can contain keyword other than // "trailers". We just forward "trailers". // TODO more strict handling required here. if (te && http2::contains_trailers(te->value)) { nva.push_back(http2::make_field("te"sv, "trailers"sv)); } for (auto &p : httpconf.add_request_headers) { nva.push_back(http2::make_field(p.name, p.value)); } if (LOG_ENABLED(INFO)) { std::stringstream ss; for (auto &nv : nva) { auto name = as_string_view(nv.name, nv.namelen); if ("authorization"sv == name) { ss << TTY_HTTP_HD << name << TTY_RST << ": \n"; continue; } ss << TTY_HTTP_HD << name << TTY_RST << ": " << as_string_view(nv.value, nv.valuelen) << "\n"; } DCLOG(INFO, this) << "HTTP request headers\n" << ss.str(); } auto transfer_encoding = req.fs.header(http2::HD_TRANSFER_ENCODING); nghttp2_data_provider2 *data_prdptr = nullptr; nghttp2_data_provider2 data_prd; // Add body as long as transfer-encoding is given even if // req.fs.content_length == 0 to forward trailer fields. if (req.method == HTTP_CONNECT || req.connect_proto != ConnectProto::NONE || transfer_encoding || req.fs.content_length > 0 || req.http2_expect_body) { // Request-body is expected. data_prd = {{}, http2_data_read_callback}; data_prdptr = &data_prd; } rv = http2session_->submit_request(this, nva.data(), nva.size(), data_prdptr); if (rv != 0) { DCLOG(FATAL, this) << "nghttp2_submit_request() failed"; return -1; } if (data_prdptr) { downstream_->reset_downstream_wtimer(); } http2session_->signal_write(); return 0; } int Http2DownstreamConnection::push_upload_data_chunk(const uint8_t *data, size_t datalen) { if (!downstream_->get_request_header_sent()) { auto output = downstream_->get_blocked_request_buf(); auto &req = downstream_->request(); output->append(data, datalen); req.unconsumed_body_length += datalen; return 0; } int rv; auto output = downstream_->get_request_buf(); output->append(data, datalen); if (downstream_->get_downstream_stream_id() != -1) { rv = http2session_->resume_data(this); if (rv != 0) { return -1; } downstream_->ensure_downstream_wtimer(); http2session_->signal_write(); } return 0; } int Http2DownstreamConnection::end_upload_data() { if (!downstream_->get_request_header_sent()) { downstream_->set_blocked_request_data_eof(true); return 0; } int rv; if (downstream_->get_downstream_stream_id() != -1) { rv = http2session_->resume_data(this); if (rv != 0) { return -1; } downstream_->ensure_downstream_wtimer(); http2session_->signal_write(); } return 0; } int Http2DownstreamConnection::resume_read(IOCtrlReason reason, size_t consumed) { int rv; if (http2session_->get_state() != Http2SessionState::CONNECTED) { return 0; } if (!downstream_ || downstream_->get_downstream_stream_id() == -1) { return 0; } if (consumed > 0) { rv = http2session_->consume( static_cast(downstream_->get_downstream_stream_id()), consumed); if (rv != 0) { return -1; } auto &resp = downstream_->response(); resp.unconsumed_body_length -= consumed; http2session_->signal_write(); } return 0; } int Http2DownstreamConnection::on_read() { return 0; } int Http2DownstreamConnection::on_write() { return 0; } void Http2DownstreamConnection::attach_stream_data(StreamData *sd) { // It is possible sd->dconn is not NULL. sd is detached when // on_stream_close_callback. Before that, after MSG_COMPLETE is set // to Downstream::set_response_state(), upstream's readcb is called // and execution path eventually could reach here. Since the // response was already handled, we just detach sd. detach_stream_data(); sd_ = sd; sd_->dconn = this; } StreamData *Http2DownstreamConnection::detach_stream_data() { if (sd_) { auto sd = sd_; sd_ = nullptr; sd->dconn = nullptr; return sd; } return nullptr; } int Http2DownstreamConnection::on_timeout() { if (!downstream_) { return 0; } return submit_rst_stream(downstream_, NGHTTP2_NO_ERROR); } const std::shared_ptr & Http2DownstreamConnection::get_downstream_addr_group() const { return http2session_->get_downstream_addr_group(); } DownstreamAddr *Http2DownstreamConnection::get_addr() const { return nullptr; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/template.h0000644000000000000000000000013215077107271015034 xustar0030 mtime=1761382073.000444106 30 atime=1761382080.105411485 30 ctime=1761382109.170300179 nghttp2-1.68.0/src/template.h0000644000175100017510000003071415077107271015431 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef TEMPLATE_H #define TEMPLATE_H #include "nghttp2_config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace nghttp2 { template [[nodiscard]] constexpr auto as_unsigned(T n) noexcept { return static_cast>(n); } template [[nodiscard]] constexpr auto as_signed(T n) noexcept { return static_cast>(n); } template constexpr size_t array_size(T (&)[N]) { return N; } template constexpr size_t str_size(T (&)[N]) { return N - 1; } // inspired by , but our // template can take functions returning other than void. template struct Defer { Defer(F &&f, T &&...t) : f(std::bind(std::forward(f), std::forward(t)...)) {} Defer(Defer &&o) noexcept : f(std::move(o.f)) {} ~Defer() { f(); } using ResultType = std::invoke_result_t; std::function f; }; template Defer defer(F &&f, T &&...t) { return Defer(std::forward(f), std::forward(t)...); } template bool test_flags(T t, F flags) { return (t & flags) == flags; } // doubly linked list of element T*. T must have field T *dlprev and // T *dlnext, which point to previous element and next element in the // list respectively. template struct DList { DList() : head(nullptr), tail(nullptr), len(0) {} DList(const DList &) = delete; DList &operator=(const DList &) = delete; DList(DList &&other) noexcept : head{std::exchange(other.head, nullptr)}, tail{std::exchange(other.tail, nullptr)}, len{std::exchange(other.len, 0)} {} DList &operator=(DList &&other) noexcept { if (this == &other) { return *this; } head = std::exchange(other.head, nullptr); tail = std::exchange(other.tail, nullptr); len = std::exchange(other.len, 0); return *this; } void append(T *t) { ++len; if (tail) { tail->dlnext = t; t->dlprev = tail; tail = t; return; } head = tail = t; } void remove(T *t) { --len; auto p = t->dlprev; auto n = t->dlnext; if (p) { p->dlnext = n; } if (head == t) { head = n; } if (n) { n->dlprev = p; } if (tail == t) { tail = p; } t->dlprev = t->dlnext = nullptr; } bool empty() const { return head == nullptr; } size_t size() const { return len; } T *head, *tail; size_t len; }; template void dlist_delete_all(DList &dl) { for (auto e = dl.head; e;) { auto next = e->dlnext; delete e; e = next; } } // User-defined literals for K, M, and G (powers of 1024) constexpr unsigned long long operator"" _k(unsigned long long k) { return k * 1024; } constexpr unsigned long long operator"" _m(unsigned long long m) { return m * 1024 * 1024; } constexpr unsigned long long operator"" _g(unsigned long long g) { return g * 1024 * 1024 * 1024; } // User-defined literals for time, converted into double in seconds // hours constexpr double operator"" _h(unsigned long long h) { return static_cast(h * 60 * 60); } // minutes constexpr double operator"" _min(unsigned long long min) { return static_cast(min * 60); } // seconds constexpr double operator"" _s(unsigned long long s) { return static_cast(s); } // milliseconds constexpr double operator"" _ms(unsigned long long ms) { return static_cast(ms) / 1000.; } // ImmutableString represents string that is immutable unlike // std::string. It has c_str() and size() functions to mimic // std::string. It manages buffer by itself. Just like std::string, // c_str() returns NULL-terminated string, but NULL character may // appear before the final terminal NULL. class ImmutableString { public: using traits_type = std::char_traits; using value_type = traits_type::char_type; using allocator_type = std::allocator; using size_type = std::allocator_traits::size_type; using difference_type = std::allocator_traits::difference_type; using const_reference = const value_type &; using const_pointer = const value_type *; using const_iterator = const_pointer; using const_reverse_iterator = std::reverse_iterator; constexpr ImmutableString() noexcept : len(0), base("") {} constexpr ImmutableString(const char *s, size_t slen) : len(slen), base(copystr(s, s + len)) {} constexpr explicit ImmutableString(const char *s) : len(traits_type::length(s)), base(copystr(s, s + len)) {} ImmutableString(std::nullptr_t) = delete; template requires(std::is_same_v, value_type> && !std::is_same_v, ImmutableString> && !std::is_array_v>) constexpr explicit ImmutableString(R &&r) : len(std::ranges::size(r)), base(copystr(std::forward(r))) {} template requires(std::is_same_v, value_type>) constexpr ImmutableString(I first, I last) : len(as_unsigned(std::ranges::distance(first, last))), base(copystr(std::move(first), std::move(last))) {} constexpr ImmutableString(const ImmutableString &other) : len(other.len), base(copystr(other)) {} constexpr ImmutableString(ImmutableString &&other) noexcept : len{std::exchange(other.len, 0)}, base{std::exchange(other.base, "")} {} constexpr ~ImmutableString() { if (len) { delete[] base; } } constexpr ImmutableString &operator=(const ImmutableString &other) { if (this == &other) { return *this; } if (len) { delete[] base; } len = other.len; base = copystr(other); return *this; } constexpr ImmutableString &operator=(ImmutableString &&other) noexcept { if (this == &other) { return *this; } if (len) { delete[] base; } len = std::exchange(other.len, 0); base = std::exchange(other.base, ""); return *this; } constexpr const_iterator begin() const noexcept { return base; } constexpr const_iterator cbegin() const noexcept { return base; } constexpr const_iterator end() const noexcept { return base + len; } constexpr const_iterator cend() const noexcept { return base + len; } constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator{base + len}; } constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{base + len}; } constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator{base}; } constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator{base}; } constexpr const_pointer c_str() const noexcept { return base; } constexpr const_pointer data() const noexcept { return base; } constexpr size_type size() const noexcept { return len; } constexpr bool empty() const noexcept { return len == 0; } constexpr const_reference operator[](size_type pos) const noexcept { return *(base + pos); } private: template constexpr const char *copystr(I first, I last) { auto len = static_cast(std::ranges::distance(first, last)); if (len == 0) { return ""; } auto res = new char[len + 1]; *std::ranges::copy(first, last, res).out = '\0'; return res; } template requires(!std::is_array_v>) constexpr const char *copystr(R &&r) { return copystr(std::ranges::begin(r), std::ranges::end(r)); } size_type len; const char *base; }; inline bool operator==(const ImmutableString &lhs, const ImmutableString &rhs) { return std::ranges::equal(lhs, rhs); } inline std::ostream &operator<<(std::ostream &o, const ImmutableString &s) { return o.write(s.c_str(), static_cast(s.size())); } inline std::string &operator+=(std::string &lhs, const ImmutableString &rhs) { lhs.append(rhs.c_str(), rhs.size()); return lhs; } inline bool operator==(const ImmutableString &lhs, const std::string_view &rhs) { return std::ranges::equal(lhs, rhs); } inline std::strong_ordering operator<=>(const ImmutableString &lhs, const ImmutableString &rhs) noexcept { return std::string_view{lhs.data(), lhs.size()} <=> std::string_view{rhs.data(), rhs.size()}; } constexpr ImmutableString operator""_is(const char *str, size_t len) { return {str, len}; } template [[nodiscard]] std::span< const uint8_t, N == std::dynamic_extent ? std::dynamic_extent : N * sizeof(T)> as_uint8_span(std::span s) noexcept { return std::span{ reinterpret_cast(s.data()), s.size_bytes()}; } template [[nodiscard]] std::span as_writable_uint8_span(std::span s) noexcept { return std::span{ reinterpret_cast(s.data()), s.size_bytes()}; } template requires(std::ranges::contiguous_range && std::ranges::sized_range && std::ranges::borrowed_range && !std::is_array_v> && sizeof(std::ranges::range_value_t) == sizeof(std::string_view::value_type)) [[nodiscard]] std::string_view as_string_view(R &&r) { return std::string_view{ reinterpret_cast(std::ranges::data(r)), std::ranges::size(r)}; } // Returns std::string_view over a given range [|first|, |last|). template requires(sizeof(std::iter_value_t) == sizeof(std::string_view::value_type)) [[nodiscard]] std::string_view as_string_view(I first, I last) { return std::string_view{ reinterpret_cast(std::to_address(first)), static_cast(std::ranges::distance(first, last))}; } // Returns std::string_view over a given range [|first|, |first| + |n|). template requires(sizeof(std::iter_value_t) == sizeof(std::string_view::value_type)) [[nodiscard]] std::string_view as_string_view(I first, size_t n) { return std::string_view{ reinterpret_cast(std::to_address(first)), n}; } inline int run_app(std::function app, int argc, char **argv) { try { return app(argc, argv); } catch (const std::bad_alloc &) { fputs("Out of memory\n", stderr); } catch (const std::exception &x) { fprintf(stderr, "Caught %s:\n%s\n", typeid(x).name(), x.what()); } catch (...) { fputs("Unknown exception caught\n", stderr); } return EXIT_FAILURE; } } // namespace nghttp2 #endif // !defined(TEMPLATE_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_worker.cc0000644000000000000000000000013215077107271016114 xustar0030 mtime=1761382073.000444106 30 atime=1761382080.105411485 30 ctime=1761382109.118300329 nghttp2-1.68.0/src/shrpx_worker.cc0000644000175100017510000014625715077107271016523 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_worker.h" #ifdef HAVE_UNISTD_H # include #endif // defined(HAVE_UNISTD_H) #include #include #include #include #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #ifdef HAVE_LIBBPF # include # include #endif // defined(HAVE_LIBBPF) #include "shrpx_tls.h" #include "shrpx_log.h" #include "shrpx_client_handler.h" #include "shrpx_http2_session.h" #include "shrpx_log_config.h" #ifdef HAVE_MRUBY # include "shrpx_mruby.h" #endif // defined(HAVE_MRUBY) #ifdef ENABLE_HTTP3 # include "shrpx_quic_listener.h" #endif // defined(ENABLE_HTTP3) #include "shrpx_connection_handler.h" #include "shrpx_accept_handler.h" #include "util.h" #include "template.h" #include "xsi_strerror.h" namespace shrpx { #ifndef _KERNEL_FASTOPEN # define _KERNEL_FASTOPEN // conditional define for TCP_FASTOPEN mostly on ubuntu # ifndef TCP_FASTOPEN # define TCP_FASTOPEN 23 # endif // !defined(TCP_FASTOPEN) #endif // !defined(_KERNEL_FASTOPEN) namespace { void eventcb(struct ev_loop *loop, ev_async *w, int revents) { auto worker = static_cast(w->data); worker->process_events(); } } // namespace namespace { void mcpool_clear_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto worker = static_cast(w->data); if (worker->get_worker_stat()->num_connections != 0) { return; } auto mcpool = worker->get_mcpool(); if (mcpool->freelistsize == mcpool->poolsize) { worker->get_mcpool()->clear(); } } } // namespace namespace { void proc_wev_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto worker = static_cast(w->data); worker->process_events(); } } // namespace namespace { void disable_listener_cb(struct ev_loop *loop, ev_timer *w, int revent) { auto worker = static_cast(w->data); // If we are in graceful shutdown period, we must not enable // acceptors again. if (worker->get_graceful_shutdown()) { return; } worker->enable_listener(); } } // namespace DownstreamAddrGroup::DownstreamAddrGroup() : retired{false} {} DownstreamAddrGroup::~DownstreamAddrGroup() {} // DownstreamKey is used to index SharedDownstreamAddr in order to // find the same configuration. using DownstreamKey = std::tuple< std::vector>, bool, SessionAffinity, std::string_view, std::string_view, SessionAffinityCookieSecure, SessionAffinityCookieStickiness, ev_tstamp, ev_tstamp, std::string_view, bool>; namespace { DownstreamKey create_downstream_key(const std::shared_ptr &shared_addr, const std::string_view &mruby_file) { DownstreamKey dkey; auto &addrs = std::get<0>(dkey); addrs.resize(shared_addr->addrs.size()); auto p = std::ranges::begin(addrs); for (auto &a : shared_addr->addrs) { std::get<0>(*p) = a.host; std::get<1>(*p) = a.sni; std::get<2>(*p) = a.group; std::get<3>(*p) = a.fall; std::get<4>(*p) = a.rise; std::get<5>(*p) = a.proto; std::get<6>(*p) = a.port; std::get<7>(*p) = a.weight; std::get<8>(*p) = a.group_weight; std::get<9>(*p) = a.host_unix; std::get<10>(*p) = a.tls; std::get<11>(*p) = a.dns; std::get<12>(*p) = a.upgrade_scheme; ++p; } std::ranges::sort(addrs); std::get<1>(dkey) = shared_addr->redirect_if_not_tls; auto &affinity = shared_addr->affinity; std::get<2>(dkey) = affinity.type; std::get<3>(dkey) = affinity.cookie.name; std::get<4>(dkey) = affinity.cookie.path; std::get<5>(dkey) = affinity.cookie.secure; std::get<6>(dkey) = affinity.cookie.stickiness; auto &timeout = shared_addr->timeout; std::get<7>(dkey) = timeout.read; std::get<8>(dkey) = timeout.write; std::get<9>(dkey) = mruby_file; std::get<10>(dkey) = shared_addr->dnf; return dkey; } } // namespace Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, tls::CertLookupTree *cert_tree, #ifdef ENABLE_HTTP3 SSL_CTX *quic_sv_ssl_ctx, tls::CertLookupTree *quic_cert_tree, WorkerID wid, #endif // defined(ENABLE_HTTP3) size_t index, const std::shared_ptr &ticket_keys, ConnectionHandler *conn_handler, std::shared_ptr downstreamconf) : index_{index}, randgen_(util::make_mt19937()), worker_stat_{}, dns_tracker_(loop, get_config()->conn.downstream->family), upstream_addrs_{get_config()->conn.listener.addrs}, #ifdef ENABLE_HTTP3 worker_id_{std::move(wid)}, quic_upstream_addrs_{get_config()->conn.quic_listener.addrs}, #endif // defined(ENABLE_HTTP3) loop_(loop), sv_ssl_ctx_(sv_ssl_ctx), cl_ssl_ctx_(cl_ssl_ctx), cert_tree_(cert_tree), conn_handler_(conn_handler), #ifdef ENABLE_HTTP3 quic_sv_ssl_ctx_{quic_sv_ssl_ctx}, quic_cert_tree_{quic_cert_tree}, quic_conn_handler_{this}, #endif // defined(ENABLE_HTTP3) ticket_keys_(ticket_keys), connect_blocker_( std::make_unique(randgen_, loop_, nullptr, nullptr)), graceful_shutdown_(false) { ev_async_init(&w_, eventcb); w_.data = this; ev_async_start(loop_, &w_); ev_timer_init(&mcpool_clear_timer_, mcpool_clear_cb, 0., 0.); mcpool_clear_timer_.data = this; ev_timer_init(&proc_wev_timer_, proc_wev_cb, 0., 0.); proc_wev_timer_.data = this; ev_timer_init(&disable_listener_timer_, disable_listener_cb, 0., 0.); disable_listener_timer_.data = this; replace_downstream_config(std::move(downstreamconf)); } namespace { void ensure_enqueue_addr( std::priority_queue, WeightGroupEntryGreater> &wgpq, WeightGroup *wg, DownstreamAddr *addr) { uint32_t cycle; if (!wg->pq.empty()) { auto &top = wg->pq.top(); cycle = top.cycle; } else { cycle = 0; } addr->cycle = cycle; addr->pending_penalty = 0; wg->pq.push(DownstreamAddrEntry{addr, addr->seq, addr->cycle}); addr->queued = true; if (!wg->queued) { if (!wgpq.empty()) { auto &top = wgpq.top(); cycle = top.cycle; } else { cycle = 0; } wg->cycle = cycle; wg->pending_penalty = 0; wgpq.push(WeightGroupEntry{wg, wg->seq, wg->cycle}); wg->queued = true; } } } // namespace void Worker::replace_downstream_config( std::shared_ptr downstreamconf) { for (auto &g : downstream_addr_groups_) { g->retired = true; auto &shared_addr = g->shared_addr; for (auto &addr : shared_addr->addrs) { addr.dconn_pool->remove_all(); } } downstreamconf_ = downstreamconf; // Making a copy is much faster with multiple thread on // backendconfig API call. auto groups = downstreamconf->addr_groups; auto old_addr_groups = std::exchange( downstream_addr_groups_, std::vector>(groups.size())); std::map addr_groups_indexer; #ifdef HAVE_MRUBY // TODO It is a bit less efficient because // mruby::create_mruby_context returns std::unique_ptr and we cannot // use std::make_shared. std::unordered_map> shared_mruby_ctxs; #endif // defined(HAVE_MRUBY) auto old_addr_group_it = std::ranges::begin(old_addr_groups); for (size_t i = 0; i < groups.size(); ++i) { auto &src = groups[i]; auto &dst = downstream_addr_groups_[i]; dst = std::make_shared(); dst->pattern = ImmutableString{src.pattern}; for (; old_addr_group_it != std::ranges::end(old_addr_groups) && (*old_addr_group_it)->pattern < dst->pattern; ++old_addr_group_it) ; auto shared_addr = std::make_shared(); shared_addr->addrs.resize(src.addrs.size()); shared_addr->affinity.type = src.affinity.type; if (src.affinity.type == SessionAffinity::COOKIE) { shared_addr->affinity.cookie.name = make_string_ref(shared_addr->balloc, src.affinity.cookie.name); if (!src.affinity.cookie.path.empty()) { shared_addr->affinity.cookie.path = make_string_ref(shared_addr->balloc, src.affinity.cookie.path); } shared_addr->affinity.cookie.secure = src.affinity.cookie.secure; shared_addr->affinity.cookie.stickiness = src.affinity.cookie.stickiness; } shared_addr->affinity_hash = src.affinity_hash; shared_addr->affinity_hash_map = src.affinity_hash_map; shared_addr->redirect_if_not_tls = src.redirect_if_not_tls; shared_addr->dnf = src.dnf; shared_addr->timeout.read = src.timeout.read; shared_addr->timeout.write = src.timeout.write; for (size_t j = 0; j < src.addrs.size(); ++j) { auto &src_addr = src.addrs[j]; auto &dst_addr = shared_addr->addrs[j]; dst_addr.addr = src_addr.addr; dst_addr.host = make_string_ref(shared_addr->balloc, src_addr.host); dst_addr.hostport = make_string_ref(shared_addr->balloc, src_addr.hostport); dst_addr.port = src_addr.port; dst_addr.host_unix = src_addr.host_unix; dst_addr.weight = src_addr.weight; dst_addr.group = make_string_ref(shared_addr->balloc, src_addr.group); dst_addr.group_weight = src_addr.group_weight; dst_addr.affinity_hash = src_addr.affinity_hash; dst_addr.proto = src_addr.proto; dst_addr.tls = src_addr.tls; dst_addr.sni = make_string_ref(shared_addr->balloc, src_addr.sni); dst_addr.fall = src_addr.fall; dst_addr.rise = src_addr.rise; dst_addr.dns = src_addr.dns; dst_addr.upgrade_scheme = src_addr.upgrade_scheme; } #ifdef HAVE_MRUBY auto mruby_ctx_it = shared_mruby_ctxs.find(src.mruby_file); if (mruby_ctx_it == std::ranges::end(shared_mruby_ctxs)) { shared_addr->mruby_ctx = mruby::create_mruby_context(src.mruby_file); assert(shared_addr->mruby_ctx); shared_mruby_ctxs.emplace(src.mruby_file, shared_addr->mruby_ctx); } else { shared_addr->mruby_ctx = (*mruby_ctx_it).second; } #endif // defined(HAVE_MRUBY) // share the connection if patterns have the same set of backend // addresses. auto dkey = create_downstream_key(shared_addr, src.mruby_file); auto it = addr_groups_indexer.find(dkey); if (it == std::ranges::end(addr_groups_indexer)) { auto shared_addr_ptr = shared_addr.get(); for (auto &addr : shared_addr->addrs) { addr.connect_blocker = std::make_unique( randgen_, loop_, nullptr, [shared_addr_ptr, &addr]() { if (!addr.queued) { if (!addr.wg) { return; } ensure_enqueue_addr(shared_addr_ptr->pq, addr.wg, &addr); } }); addr.live_check = std::make_unique(loop_, cl_ssl_ctx_, this, &addr, randgen_); } size_t seq = 0; for (auto &addr : shared_addr->addrs) { addr.dconn_pool = std::make_unique(); addr.seq = seq++; } util::shuffle(shared_addr->addrs, randgen_, [](auto i, auto j) { std::swap((*i).seq, (*j).seq); }); if (shared_addr->affinity.type == SessionAffinity::NONE) { std::unordered_map wgs; size_t num_wgs = 0; for (auto &addr : shared_addr->addrs) { if (!wgs.contains(addr.group)) { ++num_wgs; wgs.emplace(addr.group, nullptr); } } shared_addr->wgs = std::vector(num_wgs); for (auto &addr : shared_addr->addrs) { auto &wg = wgs[addr.group]; if (wg == nullptr) { wg = &shared_addr->wgs[--num_wgs]; wg->name = addr.group; wg->seq = num_wgs; } wg->weight = addr.group_weight; wg->pq.push(DownstreamAddrEntry{&addr, addr.seq, addr.cycle}); addr.queued = true; addr.wg = wg; } assert(num_wgs == 0); auto copy_cycle = old_addr_group_it != std::ranges::end(old_addr_groups) && (*old_addr_group_it)->pattern == dst->pattern && (*old_addr_group_it)->shared_addr->affinity.type == SessionAffinity::NONE && std::ranges::equal(shared_addr->wgs, (*old_addr_group_it)->shared_addr->wgs, [](const auto &a, const auto &b) { return a.name == b.name && a.weight == b.weight; }); for (size_t i = 0; i < shared_addr->wgs.size(); ++i) { auto &wg = shared_addr->wgs[i]; if (copy_cycle) { wg.cycle = (*old_addr_group_it)->shared_addr->wgs[i].cycle; } shared_addr->pq.push(WeightGroupEntry{&wg, wg.seq, wg.cycle}); wg.queued = true; } } dst->shared_addr = std::move(shared_addr); addr_groups_indexer.emplace(std::move(dkey), i); } else { auto &g = *(std::ranges::begin(downstream_addr_groups_) + as_signed((*it).second)); if (LOG_ENABLED(INFO)) { LOG(INFO) << dst->pattern << " shares the same backend group with " << g->pattern; } dst->shared_addr = g->shared_addr; } } } Worker::~Worker() { ev_async_stop(loop_, &w_); ev_timer_stop(loop_, &mcpool_clear_timer_); ev_timer_stop(loop_, &proc_wev_timer_); ev_timer_stop(loop_, &disable_listener_timer_); } void Worker::schedule_clear_mcpool() { // libev manual says: "If the watcher is already active nothing will // happen." Since we don't change any timeout here, we don't have // to worry about querying ev_is_active. ev_timer_start(loop_, &mcpool_clear_timer_); } void Worker::wait() { #ifndef NOTHREADS fut_.get(); #endif // !defined(NOTHREADS) } void Worker::run_async() { #ifndef NOTHREADS fut_ = std::async(std::launch::async, [this] { (void)reopen_log_files(get_config()->logging); ev_run(loop_); # ifdef NGHTTP2_OPENSSL_IS_WOLFSSL wc_ecc_fp_free(); # endif // defined(NGHTTP2_OPENSSL_IS_WOLFSSL) }); #endif // !defined(NOTHREADS) } void Worker::send(WorkerEvent event) { { std::lock_guard g(m_); q_.emplace_back(std::move(event)); } ev_async_send(loop_, &w_); } void Worker::process_events() { WorkerEvent wev; { std::lock_guard g(m_); // Process event one at a time. if (q_.empty()) { ev_timer_stop(loop_, &proc_wev_timer_); return; } wev = std::move(q_.front()); q_.pop_front(); } ev_timer_start(loop_, &proc_wev_timer_); auto config = get_config(); switch (wev.type) { case WorkerEventType::REOPEN_LOG: WLOG(NOTICE, this) << "Reopening log files: worker process (thread " << this << ")"; reopen_log_files(config->logging); break; case WorkerEventType::GRACEFUL_SHUTDOWN: WLOG(NOTICE, this) << "Graceful shutdown commencing"; graceful_shutdown_ = true; accept_pending_connection(); delete_listener(); if (worker_stat_.num_connections == 0 && worker_stat_.num_close_waits == 0) { ev_break(loop_); return; } break; case WorkerEventType::REPLACE_DOWNSTREAM: WLOG(NOTICE, this) << "Replace downstream"; replace_downstream_config(wev.downstreamconf); break; #ifdef ENABLE_HTTP3 case WorkerEventType::QUIC_PKT_FORWARD: { const UpstreamAddr *faddr; if (wev.quic_pkt->upstream_addr_index == static_cast(-1)) { faddr = find_quic_upstream_addr(wev.quic_pkt->local_addr); if (faddr == nullptr) { LOG(ERROR) << "No suitable upstream address found"; break; } } else if (quic_upstream_addrs_.size() <= wev.quic_pkt->upstream_addr_index) { LOG(ERROR) << "upstream_addr_index is too large"; break; } else { faddr = &quic_upstream_addrs_[wev.quic_pkt->upstream_addr_index]; } quic_conn_handler_.handle_packet(faddr, wev.quic_pkt->remote_addr, wev.quic_pkt->local_addr, wev.quic_pkt->pi, wev.quic_pkt->data); break; } #endif // defined(ENABLE_HTTP3) default: if (LOG_ENABLED(INFO)) { WLOG(INFO, this) << "unknown event type " << static_cast(wev.type); } } } void Worker::enable_listener() { if (LOG_ENABLED(INFO)) { WLOG(INFO, this) << "Enable listeners"; } for (auto &a : listeners_) { a->enable(); } } void Worker::disable_listener() { if (LOG_ENABLED(INFO)) { WLOG(INFO, this) << "Disable listeners"; } for (auto &a : listeners_) { a->disable(); } } void Worker::sleep_listener(ev_tstamp t) { if (t == 0. || ev_is_active(&disable_listener_timer_)) { return; } disable_listener(); ev_timer_set(&disable_listener_timer_, t, 0.); ev_timer_start(loop_, &disable_listener_timer_); } tls::CertLookupTree *Worker::get_cert_lookup_tree() const { return cert_tree_; } #ifdef ENABLE_HTTP3 tls::CertLookupTree *Worker::get_quic_cert_lookup_tree() const { return quic_cert_tree_; } #endif // defined(ENABLE_HTTP3) std::shared_ptr Worker::get_ticket_keys() { #ifdef HAVE_ATOMIC_STD_SHARED_PTR return ticket_keys_.load(std::memory_order_acquire); #else // !defined(HAVE_ATOMIC_STD_SHARED_PTR) std::lock_guard g(ticket_keys_m_); return ticket_keys_; #endif // !defined(HAVE_ATOMIC_STD_SHARED_PTR) } void Worker::set_ticket_keys(std::shared_ptr ticket_keys) { #ifdef HAVE_ATOMIC_STD_SHARED_PTR // This is single writer ticket_keys_.store(std::move(ticket_keys), std::memory_order_release); #else // !defined(HAVE_ATOMIC_STD_SHARED_PTR) std::lock_guard g(ticket_keys_m_); ticket_keys_ = std::move(ticket_keys); #endif // !defined(HAVE_ATOMIC_STD_SHARED_PTR) } WorkerStat *Worker::get_worker_stat() { return &worker_stat_; } struct ev_loop *Worker::get_loop() const { return loop_; } SSL_CTX *Worker::get_sv_ssl_ctx() const { return sv_ssl_ctx_; } SSL_CTX *Worker::get_cl_ssl_ctx() const { return cl_ssl_ctx_; } #ifdef ENABLE_HTTP3 SSL_CTX *Worker::get_quic_sv_ssl_ctx() const { return quic_sv_ssl_ctx_; } #endif // defined(ENABLE_HTTP3) void Worker::set_graceful_shutdown(bool f) { graceful_shutdown_ = f; } bool Worker::get_graceful_shutdown() const { return graceful_shutdown_; } MemchunkPool *Worker::get_mcpool() { return &mcpool_; } std::mt19937 &Worker::get_randgen() { return randgen_; } #ifdef HAVE_MRUBY int Worker::create_mruby_context() { mruby_ctx_ = mruby::create_mruby_context(get_config()->mruby_file); if (!mruby_ctx_) { return -1; } return 0; } mruby::MRubyContext *Worker::get_mruby_context() const { return mruby_ctx_.get(); } #endif // defined(HAVE_MRUBY) std::vector> & Worker::get_downstream_addr_groups() { return downstream_addr_groups_; } ConnectBlocker *Worker::get_connect_blocker() const { return connect_blocker_.get(); } const DownstreamConfig *Worker::get_downstream_config() const { return downstreamconf_.get(); } ConnectionHandler *Worker::get_connection_handler() const { return conn_handler_; } int Worker::setup_server_socket() { auto config = get_config(); auto &apiconf = config->api; auto api_isolation = apiconf.enabled && !config->single_thread; for (auto &addr : upstream_addrs_) { if (api_isolation) { if (addr.alt_mode == UpstreamAltMode::API) { if (index_ != 0) { continue; } } else if (index_ == 0) { continue; } } if (addr.host_unix) { // Copy file descriptor because AcceptHandler destructor closes // addr.fd. addr.fd = dup(addr.fd); if (addr.fd == -1) { return -1; } util::make_socket_closeonexec(addr.fd); } else if (create_tcp_server_socket(addr) != 0) { return -1; } listeners_.emplace_back(std::make_unique(this, &addr)); } return 0; } void Worker::delete_listener() { listeners_.clear(); } void Worker::accept_pending_connection() { for (auto &l : listeners_) { l->accept_connection(); } } int Worker::create_tcp_server_socket(UpstreamAddr &faddr) { std::array errbuf; int fd = -1; int rv; auto &listenerconf = get_config()->conn.listener; auto service = util::utos(faddr.port); addrinfo hints{ .ai_flags = AI_PASSIVE #ifdef AI_ADDRCONFIG | AI_ADDRCONFIG #endif // defined(AI_ADDRCONFIG) , .ai_family = faddr.family, .ai_socktype = SOCK_STREAM, }; auto node = faddr.host == "*"sv ? nullptr : faddr.host.data(); addrinfo *res, *rp; rv = getaddrinfo(node, service.c_str(), &hints, &res); #ifdef AI_ADDRCONFIG if (rv != 0) { // Retry without AI_ADDRCONFIG hints.ai_flags &= ~AI_ADDRCONFIG; rv = getaddrinfo(node, service.c_str(), &hints, &res); } #endif // defined(AI_ADDRCONFIG) if (rv != 0) { LOG(FATAL) << "Unable to get IPv" << (faddr.family == AF_INET ? "4" : "6") << " address for " << faddr.host << ", port " << faddr.port << ": " << gai_strerror(rv); return -1; } auto res_d = defer(freeaddrinfo, res); std::array host; for (rp = res; rp; rp = rp->ai_next) { rv = getnameinfo(rp->ai_addr, rp->ai_addrlen, host.data(), host.size(), nullptr, 0, NI_NUMERICHOST); if (rv != 0) { LOG(WARN) << "getnameinfo() failed: " << gai_strerror(rv); continue; } #ifdef SOCK_NONBLOCK fd = socket(rp->ai_family, rp->ai_socktype | SOCK_NONBLOCK | SOCK_CLOEXEC, rp->ai_protocol); if (fd == -1) { auto error = errno; LOG(WARN) << "socket() syscall failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); continue; } #else // !defined(SOCK_NONBLOCK) fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (fd == -1) { auto error = errno; LOG(WARN) << "socket() syscall failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); continue; } util::make_socket_nonblocking(fd); util::make_socket_closeonexec(fd); #endif // !defined(SOCK_NONBLOCK) int val = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, static_cast(sizeof(val))) == -1) { auto error = errno; LOG(WARN) << "Failed to set SO_REUSEADDR option to listener socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, static_cast(sizeof(val))) == -1) { auto error = errno; LOG(WARN) << "Failed to set SO_REUSEPORT option to listener socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } #ifdef IPV6_V6ONLY if (faddr.family == AF_INET6) { if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, static_cast(sizeof(val))) == -1) { auto error = errno; LOG(WARN) << "Failed to set IPV6_V6ONLY option to listener socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } } #endif // defined(IPV6_V6ONLY) #ifdef TCP_DEFER_ACCEPT val = 3; if (setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &val, static_cast(sizeof(val))) == -1) { auto error = errno; LOG(WARN) << "Failed to set TCP_DEFER_ACCEPT option to listener socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); } #endif // defined(TCP_DEFER_ACCEPT) // When we are executing new binary, and the old binary did not // bind privileged port (< 1024) for some reason, binding to those // ports will fail with permission denied error. if (bind(fd, rp->ai_addr, rp->ai_addrlen) == -1) { auto error = errno; LOG(WARN) << "bind() syscall failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } if (listenerconf.fastopen > 0) { val = listenerconf.fastopen; if (setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN, &val, static_cast(sizeof(val))) == -1) { auto error = errno; LOG(WARN) << "Failed to set TCP_FASTOPEN option to listener socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); } } if (listen(fd, listenerconf.backlog) == -1) { auto error = errno; LOG(WARN) << "listen() syscall failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } break; } if (!rp) { LOG(FATAL) << "Listening " << (faddr.family == AF_INET ? "IPv4" : "IPv6") << " socket failed"; return -1; } faddr.fd = fd; faddr.hostport = util::make_http_hostport( mod_config()->balloc, std::string_view{host.data()}, faddr.port); LOG(NOTICE) << "Listening on " << faddr.hostport << (faddr.tls ? ", tls" : ""); return 0; } #ifdef ENABLE_HTTP3 QUICConnectionHandler *Worker::get_quic_connection_handler() { return &quic_conn_handler_; } #endif // defined(ENABLE_HTTP3) DNSTracker *Worker::get_dns_tracker() { return &dns_tracker_; } #ifdef ENABLE_HTTP3 # ifdef HAVE_LIBBPF bool Worker::should_attach_bpf() const { auto config = get_config(); auto &quicconf = config->quic; auto &apiconf = config->api; if (quicconf.bpf.disabled) { return false; } if (!config->single_thread && apiconf.enabled) { return index_ == 1; } return index_ == 0; } bool Worker::should_update_bpf_map() const { auto config = get_config(); auto &quicconf = config->quic; return !quicconf.bpf.disabled; } uint32_t Worker::compute_sk_index() const { auto config = get_config(); auto &apiconf = config->api; if (!config->single_thread && apiconf.enabled) { return static_cast(index_ - 1); } return static_cast(index_); } # endif // defined(HAVE_LIBBPF) int Worker::setup_quic_server_socket() { size_t n = 0; for (auto &addr : quic_upstream_addrs_) { assert(!addr.host_unix); if (create_quic_server_socket(addr) != 0) { return -1; } // Make sure that each endpoint has a unique address. for (size_t i = 0; i < n; ++i) { const auto &a = quic_upstream_addrs_[i]; if (addr.hostport == a.hostport) { LOG(FATAL) << "QUIC frontend endpoint must be unique: a duplicate found for " << addr.hostport; return -1; } } ++n; quic_listeners_.emplace_back(std::make_unique(&addr, this)); } return 0; } # ifdef HAVE_LIBBPF namespace { // https://github.com/kokke/tiny-AES-c // // License is Public Domain. // Commit hash: 12e7744b4919e9d55de75b7ab566326a1c8e7a67 // The number of columns comprising a state in AES. This is a constant // in AES. Value=4 # define Nb 4 # define Nk 4 // The number of 32 bit words in a key. # define Nr 10 // The number of rounds in AES Cipher. // The lookup-tables are marked const so they can be placed in // read-only storage instead of RAM The numbers below can be computed // dynamically trading ROM for RAM - This can be useful in (embedded) // bootloader applications, where ROM is often limited. const uint8_t sbox[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16}; # define getSBoxValue(num) (sbox[(num)]) // The round constant word array, Rcon[i], contains the values given // by x to the power (i-1) being powers of x (x is denoted as {02}) in // the field GF(2^8) const uint8_t Rcon[11] = {0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36}; // This function produces Nb(Nr+1) round keys. The round keys are used // in each round to decrypt the states. void KeyExpansion(uint8_t *RoundKey, const uint8_t *Key) { unsigned i, j, k; uint8_t tempa[4]; // Used for the column/row operations // The first round key is the key itself. for (i = 0; i < Nk; ++i) { RoundKey[(i * 4) + 0] = Key[(i * 4) + 0]; RoundKey[(i * 4) + 1] = Key[(i * 4) + 1]; RoundKey[(i * 4) + 2] = Key[(i * 4) + 2]; RoundKey[(i * 4) + 3] = Key[(i * 4) + 3]; } // All other round keys are found from the previous round keys. for (i = Nk; i < Nb * (Nr + 1); ++i) { { k = (i - 1) * 4; tempa[0] = RoundKey[k + 0]; tempa[1] = RoundKey[k + 1]; tempa[2] = RoundKey[k + 2]; tempa[3] = RoundKey[k + 3]; } if (i % Nk == 0) { // This function shifts the 4 bytes in a word to the left once. // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] // Function RotWord() { const uint8_t u8tmp = tempa[0]; tempa[0] = tempa[1]; tempa[1] = tempa[2]; tempa[2] = tempa[3]; tempa[3] = u8tmp; } // SubWord() is a function that takes a four-byte input word and // applies the S-box to each of the four bytes to produce an // output word. // Function Subword() { tempa[0] = getSBoxValue(tempa[0]); tempa[1] = getSBoxValue(tempa[1]); tempa[2] = getSBoxValue(tempa[2]); tempa[3] = getSBoxValue(tempa[3]); } tempa[0] = tempa[0] ^ Rcon[i / Nk]; } j = i * 4; k = (i - Nk) * 4; RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0]; RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1]; RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2]; RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3]; } } } // namespace # endif // defined(HAVE_LIBBPF) int Worker::create_quic_server_socket(UpstreamAddr &faddr) { std::array errbuf; int fd = -1; int rv; auto service = util::utos(faddr.port); addrinfo hints{ .ai_flags = AI_PASSIVE # ifdef AI_ADDRCONFIG | AI_ADDRCONFIG # endif // defined(AI_ADDRCONFIG) , .ai_family = faddr.family, .ai_socktype = SOCK_DGRAM, }; auto node = faddr.host == "*"sv ? nullptr : faddr.host.data(); addrinfo *res, *rp; rv = getaddrinfo(node, service.c_str(), &hints, &res); # ifdef AI_ADDRCONFIG if (rv != 0) { // Retry without AI_ADDRCONFIG hints.ai_flags &= ~AI_ADDRCONFIG; rv = getaddrinfo(node, service.c_str(), &hints, &res); } # endif // defined(AI_ADDRCONFIG) if (rv != 0) { LOG(FATAL) << "Unable to get IPv" << (faddr.family == AF_INET ? "4" : "6") << " address for " << faddr.host << ", port " << faddr.port << ": " << gai_strerror(rv); return -1; } auto res_d = defer(freeaddrinfo, res); std::array host; for (rp = res; rp; rp = rp->ai_next) { rv = getnameinfo(rp->ai_addr, rp->ai_addrlen, host.data(), host.size(), nullptr, 0, NI_NUMERICHOST); if (rv != 0) { LOG(WARN) << "getnameinfo() failed: " << gai_strerror(rv); continue; } # ifdef SOCK_NONBLOCK fd = socket(rp->ai_family, rp->ai_socktype | SOCK_NONBLOCK | SOCK_CLOEXEC, rp->ai_protocol); if (fd == -1) { auto error = errno; LOG(WARN) << "socket() syscall failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); continue; } # else // !defined(SOCK_NONBLOCK) fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (fd == -1) { auto error = errno; LOG(WARN) << "socket() syscall failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); continue; } util::make_socket_nonblocking(fd); util::make_socket_closeonexec(fd); # endif // !defined(SOCK_NONBLOCK) int val = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, static_cast(sizeof(val))) == -1) { auto error = errno; LOG(WARN) << "Failed to set SO_REUSEADDR option to listener socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, static_cast(sizeof(val))) == -1) { auto error = errno; LOG(WARN) << "Failed to set SO_REUSEPORT option to listener socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } if (faddr.family == AF_INET6) { # ifdef IPV6_V6ONLY if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, static_cast(sizeof(val))) == -1) { auto error = errno; LOG(WARN) << "Failed to set IPV6_V6ONLY option to listener socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } # endif // defined(IPV6_V6ONLY) if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, static_cast(sizeof(val))) == -1) { auto error = errno; LOG(WARN) << "Failed to set IPV6_RECVPKTINFO option to listener socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVTCLASS, &val, static_cast(sizeof(val))) == -1) { auto error = errno; LOG(WARN) << "Failed to set IPV6_RECVTCLASS option to listener socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } # if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_PROBE) int mtu_disc = IPV6_PMTUDISC_PROBE; if (setsockopt(fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &mtu_disc, static_cast(sizeof(mtu_disc))) == -1) { auto error = errno; LOG(WARN) << "Failed to set IPV6_MTU_DISCOVER option to listener socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } # endif // defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_PROBE) } else { if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &val, static_cast(sizeof(val))) == -1) { auto error = errno; LOG(WARN) << "Failed to set IP_PKTINFO option to listener socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } if (setsockopt(fd, IPPROTO_IP, IP_RECVTOS, &val, static_cast(sizeof(val))) == -1) { auto error = errno; LOG(WARN) << "Failed to set IP_RECVTOS option to listener socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } # if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_PROBE) int mtu_disc = IP_PMTUDISC_PROBE; if (setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &mtu_disc, static_cast(sizeof(mtu_disc))) == -1) { auto error = errno; LOG(WARN) << "Failed to set IP_MTU_DISCOVER option to listener socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } # endif // defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_PROBE) } # ifdef UDP_GRO if (setsockopt(fd, IPPROTO_UDP, UDP_GRO, &val, sizeof(val)) == -1) { auto error = errno; LOG(WARN) << "Failed to set UDP_GRO option to listener socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } # endif // defined(UDP_GRO) if (bind(fd, rp->ai_addr, rp->ai_addrlen) == -1) { auto error = errno; LOG(WARN) << "bind() syscall failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } # ifdef HAVE_LIBBPF auto config = get_config(); auto &quic_bpf_refs = conn_handler_->get_quic_bpf_refs(); if (should_attach_bpf()) { auto &bpfconf = config->quic.bpf; auto obj = bpf_object__open_file(bpfconf.prog_file.data(), nullptr); if (!obj) { auto error = errno; LOG(FATAL) << "Failed to open bpf object file: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); return -1; } rv = bpf_object__load(obj); if (rv != 0) { auto error = errno; LOG(FATAL) << "Failed to load bpf object file: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); return -1; } auto prog = bpf_object__find_program_by_name(obj, "select_reuseport"); if (!prog) { auto error = errno; LOG(FATAL) << "Failed to find sk_reuseport program: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); return -1; } auto &ref = quic_bpf_refs[faddr.index]; ref.obj = obj; ref.reuseport_array = bpf_object__find_map_by_name(obj, "reuseport_array"); if (!ref.reuseport_array) { auto error = errno; LOG(FATAL) << "Failed to get reuseport_array: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); return -1; } ref.worker_id_map = bpf_object__find_map_by_name(obj, "worker_id_map"); if (!ref.worker_id_map) { auto error = errno; LOG(FATAL) << "Failed to get worker_id_map: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); return -1; } auto sk_info = bpf_object__find_map_by_name(obj, "sk_info"); if (!sk_info) { auto error = errno; LOG(FATAL) << "Failed to get sk_info: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); return -1; } constexpr uint32_t zero = 0; uint64_t num_socks = config->num_worker; rv = bpf_map__update_elem(sk_info, &zero, sizeof(zero), &num_socks, sizeof(num_socks), BPF_ANY); if (rv != 0) { auto error = errno; LOG(FATAL) << "Failed to update sk_info: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); return -1; } auto &qkms = conn_handler_->get_quic_keying_materials(); auto &qkm = qkms->keying_materials.front(); auto aes_key = bpf_object__find_map_by_name(obj, "aes_key"); if (!aes_key) { auto error = errno; LOG(FATAL) << "Failed to get aes_key: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); return -1; } constexpr size_t expanded_aes_keylen = 176; std::array aes_exp_key; KeyExpansion(aes_exp_key.data(), qkm.cid_encryption_key.data()); rv = bpf_map__update_elem(aes_key, &zero, sizeof(zero), aes_exp_key.data(), aes_exp_key.size(), BPF_ANY); if (rv != 0) { auto error = errno; LOG(FATAL) << "Failed to update aes_key: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); return -1; } auto prog_fd = bpf_program__fd(prog); if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, &prog_fd, static_cast(sizeof(prog_fd))) == -1) { LOG(FATAL) << "Failed to attach bpf program: " << xsi_strerror(errno, errbuf.data(), errbuf.size()); close(fd); return -1; } } if (should_update_bpf_map()) { const auto &ref = quic_bpf_refs[faddr.index]; auto sk_index = compute_sk_index(); rv = bpf_map__update_elem(ref.reuseport_array, &sk_index, sizeof(sk_index), &fd, sizeof(fd), BPF_NOEXIST); if (rv != 0) { auto error = errno; LOG(FATAL) << "Failed to update reuseport_array: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); return -1; } rv = bpf_map__update_elem(ref.worker_id_map, &worker_id_, sizeof(worker_id_), &sk_index, sizeof(sk_index), BPF_NOEXIST); if (rv != 0) { auto error = errno; LOG(FATAL) << "Failed to update worker_id_map: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); return -1; } } # endif // defined(HAVE_LIBBPF) break; } if (!rp) { LOG(FATAL) << "Listening " << (faddr.family == AF_INET ? "IPv4" : "IPv6") << " socket failed"; return -1; } faddr.fd = fd; faddr.hostport = util::make_http_hostport( mod_config()->balloc, std::string_view{host.data()}, faddr.port); memcpy(&faddr.sockaddr, rp->ai_addr, rp->ai_addrlen); switch (faddr.family) { case AF_INET: { static constexpr auto inaddr_any = INADDR_ANY; faddr.sockaddr_any = memcmp(&inaddr_any, &faddr.sockaddr.in.sin_addr, sizeof(inaddr_any)) == 0; break; } case AF_INET6: { static constexpr in6_addr in6addr_any = IN6ADDR_ANY_INIT; faddr.sockaddr_any = memcmp(&in6addr_any, &faddr.sockaddr.in6.sin6_addr, sizeof(in6addr_any)) == 0; break; } default: assert(0); } LOG(NOTICE) << "Listening on " << faddr.hostport << ", quic"; return 0; } const WorkerID &Worker::get_worker_id() const { return worker_id_; } const UpstreamAddr *Worker::find_quic_upstream_addr(const Address &local_addr) { const UpstreamAddr *fallback_faddr = nullptr; for (auto &faddr : quic_upstream_addrs_) { if (local_addr.su.sa.sa_family != faddr.family) { continue; } switch (faddr.family) { case AF_INET: { const auto &addr = faddr.sockaddr.in; if (local_addr.su.in.sin_port != addr.sin_port) { continue; } if (memcmp(&local_addr.su.in.sin_addr, &addr.sin_addr, sizeof(addr.sin_addr)) == 0) { return &faddr; } break; } case AF_INET6: { const auto &addr = faddr.sockaddr.in6; if (local_addr.su.in6.sin6_port != addr.sin6_port) { continue; } if (memcmp(&local_addr.su.in6.sin6_addr, &addr.sin6_addr, sizeof(addr.sin6_addr)) == 0) { return &faddr; } break; } } if (faddr.sockaddr_any) { fallback_faddr = &faddr; } } return fallback_faddr; } #endif // defined(ENABLE_HTTP3) namespace { size_t match_downstream_addr_group_host( const RouterConfig &routerconf, const std::string_view &host, const std::string_view &path, const std::vector> &groups, size_t catch_all, BlockAllocator &balloc) { const auto &router = routerconf.router; const auto &rev_wildcard_router = routerconf.rev_wildcard_router; const auto &wildcard_patterns = routerconf.wildcard_patterns; if (LOG_ENABLED(INFO)) { LOG(INFO) << "Perform mapping selection, using host=" << host << ", path=" << path; } auto group = router.match(host, path); if (group != -1) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "Found pattern with query " << host << path << ", matched pattern=" << groups[as_unsigned(group)]->pattern; } return as_unsigned(group); } if (!wildcard_patterns.empty() && !host.empty()) { auto rev_host_src = make_byte_ref(balloc, host.size() - 1); auto rev_host = as_string_view(std::ranges::begin(rev_host_src), std::ranges::reverse_copy(std::ranges::begin(host) + 1, std::ranges::end(host), std::ranges::begin(rev_host_src)) .out); ssize_t best_group = -1; const RNode *last_node = nullptr; for (;;) { size_t nread = 0; auto wcidx = rev_wildcard_router.match_prefix(&nread, &last_node, rev_host); if (wcidx == -1) { break; } rev_host = std::string_view{std::ranges::begin(rev_host) + nread, std::ranges::end(rev_host)}; auto &wc = wildcard_patterns[as_unsigned(wcidx)]; auto group = wc.router.match(""sv, path); if (group != -1) { // We sorted wildcard_patterns in a way that first match is the // longest host pattern. if (LOG_ENABLED(INFO)) { LOG(INFO) << "Found wildcard pattern with query " << host << path << ", matched pattern=" << groups[as_unsigned(group)]->pattern; } best_group = group; } } if (best_group != -1) { return as_unsigned(best_group); } } group = router.match(""sv, path); if (group != -1) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "Found pattern with query " << path << ", matched pattern=" << groups[as_unsigned(group)]->pattern; } return as_unsigned(group); } if (LOG_ENABLED(INFO)) { LOG(INFO) << "None match. Use catch-all pattern"; } return catch_all; } } // namespace size_t match_downstream_addr_group( const RouterConfig &routerconf, const std::string_view &hostport, const std::string_view &raw_path, const std::vector> &groups, size_t catch_all, BlockAllocator &balloc) { if (util::contains(hostport, '/')) { // We use '/' specially, and if '/' is included in host, it breaks // our code. Select catch-all case. return catch_all; } auto fragment = std::ranges::find(raw_path, '#'); auto query = std::ranges::find(std::ranges::begin(raw_path), fragment, '?'); auto path = std::string_view{std::ranges::begin(raw_path), query}; if (path.empty() || path[0] != '/') { path = "/"sv; } if (hostport.empty()) { return match_downstream_addr_group_host(routerconf, hostport, path, groups, catch_all, balloc); } std::string_view host; if (hostport[0] == '[') { // assume this is IPv6 numeric address auto p = std::ranges::find(hostport, ']'); if (p == std::ranges::end(hostport)) { return catch_all; } if (p + 1 < std::ranges::end(hostport) && *(p + 1) != ':') { return catch_all; } host = std::string_view{std::ranges::begin(hostport), p + 1}; } else { auto p = std::ranges::find(hostport, ':'); if (p == std::ranges::begin(hostport)) { return catch_all; } host = std::string_view{std::ranges::begin(hostport), p}; } if (std::ranges::find_if(host, [](char c) { return 'A' <= c && c <= 'Z'; }) != std::ranges::end(host)) { auto low_host = make_byte_ref(balloc, host.size() + 1); auto ep = util::tolower(host, std::ranges::begin(low_host)); *ep = '\0'; host = as_string_view(std::ranges::begin(low_host), ep); } return match_downstream_addr_group_host(routerconf, host, path, groups, catch_all, balloc); } void downstream_failure(DownstreamAddr *addr, const Address *raddr) { const auto &connect_blocker = addr->connect_blocker; if (connect_blocker->in_offline()) { return; } connect_blocker->on_failure(); if (addr->fall == 0) { return; } auto fail_count = connect_blocker->get_fail_count(); if (fail_count >= addr->fall) { if (raddr) { LOG(WARN) << "Could not connect to " << util::to_numeric_addr(raddr) << " " << fail_count << " times in a row; considered as offline"; } else { LOG(WARN) << "Could not connect to " << addr->host << ":" << addr->port << " " << fail_count << " times in a row; considered as offline"; } connect_blocker->offline(); if (addr->rise) { addr->live_check->schedule(); } } } int Worker::handle_connection(int fd, sockaddr *addr, socklen_t addrlen, const UpstreamAddr *faddr) { if (LOG_ENABLED(INFO)) { LLOG(INFO, this) << "Accepted connection from " << util::numeric_name(addr, addrlen) << ", fd=" << fd; } auto config = get_config(); auto max_conns = config->conn.upstream.worker_connections; if (worker_stat_.num_connections >= max_conns) { if (LOG_ENABLED(INFO)) { WLOG(INFO, this) << "Too many connections >= " << max_conns; } close(fd); return -1; } auto client_handler = tls::accept_connection(this, fd, addr, addrlen, faddr); if (!client_handler) { if (LOG_ENABLED(INFO)) { WLOG(ERROR, this) << "ClientHandler creation failed"; } close(fd); return -1; } if (LOG_ENABLED(INFO)) { WLOG(INFO, this) << "CLIENT_HANDLER:" << client_handler << " created"; } return 0; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_io_control.cc0000644000000000000000000000013215077107270016751 xustar0030 mtime=1761382072.995444129 30 atime=1761382106.116310815 30 ctime=1761382109.112300346 nghttp2-1.68.0/src/shrpx_io_control.cc0000644000175100017510000000341615077107270017345 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_io_control.h" #include #include "shrpx_rate_limit.h" #include "util.h" using namespace nghttp2; namespace shrpx { IOControl::IOControl(RateLimit *lim) : lim_(lim), rdbits_(0) {} IOControl::~IOControl() {} void IOControl::pause_read(IOCtrlReason reason) { rdbits_ |= reason; if (lim_) { lim_->stopw(); } } bool IOControl::resume_read(IOCtrlReason reason) { rdbits_ &= ~reason; if (rdbits_ == 0) { if (lim_) { lim_->startw(); } return true; } return false; } void IOControl::force_resume_read() { rdbits_ = 0; if (lim_) { lim_->startw(); } } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/buffer_test.h0000644000000000000000000000013115077107270015527 xustar0029 mtime=1761382072.98444418 30 atime=1761382106.270310136 30 ctime=1761382109.267299898 nghttp2-1.68.0/src/buffer_test.h0000644000175100017510000000273215077107270016124 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef BUFFER_TEST_H #define BUFFER_TEST_H #ifdef HAVE_CONFIG_H # include #endif // defined(HAVE_CONFIG_H) #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" namespace nghttp2 { extern const MunitSuite buffer_suite; munit_void_test_decl(test_buffer_write) } // namespace nghttp2 #endif // !defined(BUFFER_TEST_H) nghttp2-1.68.0/src/PaxHeaders/h2load_session.h0000644000000000000000000000013215077107270016134 xustar0030 mtime=1761382072.986444171 30 atime=1761382106.214310383 30 ctime=1761382109.210300063 nghttp2-1.68.0/src/h2load_session.h0000644000175100017510000000402115077107270016521 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef H2LOAD_SESSION_H #define H2LOAD_SESSION_H #include "nghttp2_config.h" #include #include #include "h2load.h" namespace h2load { class Session { public: virtual ~Session() {} // Called when the connection was made. virtual void on_connect() = 0; // Called when one request must be issued. virtual int submit_request() = 0; // Called when incoming bytes are available. The subclass has to // return the number of bytes read. virtual int on_read(const uint8_t *data, size_t len) = 0; // Called when write is available. Returns 0 on success, otherwise // return -1. virtual int on_write() = 0; // Called when the underlying session must be terminated. virtual void terminate() = 0; // Return the maximum concurrency per connection virtual size_t max_concurrent_streams() = 0; }; } // namespace h2load #endif // !defined(H2LOAD_SESSION_H) nghttp2-1.68.0/src/PaxHeaders/h2load.h0000644000000000000000000000013215077107270014371 xustar0030 mtime=1761382072.985444176 30 atime=1761382106.213310387 30 ctime=1761382109.209300066 nghttp2-1.68.0/src/h2load.h0000644000175100017510000004132415077107270014765 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef H2LOAD_H #define H2LOAD_H #include "nghttp2_config.h" #include #ifdef HAVE_SYS_SOCKET_H # include #endif // defined(HAVE_SYS_SOCKET_H) #ifdef HAVE_NETDB_H # include #endif // defined(HAVE_NETDB_H) #include #include #include #include #include #include #include #include #define NGHTTP2_NO_SSIZE_T #include #ifdef ENABLE_HTTP3 # include # include #endif // defined(ENABLE_HTTP3) #include #include "ssl_compat.h" #if defined(ENABLE_HTTP3) && OPENSSL_3_5_0_API # include #endif // defined(ENABLE_HTTP3) && OPENSSL_3_5_0_API #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include "http2.h" #include "memchunk.h" #include "template.h" using namespace nghttp2; namespace h2load { inline constexpr auto BACKOFF_WRITE_BUFFER_THRES = 16_k; class Session; struct Worker; struct Config { std::vector> nva; std::vector h1reqs; std::vector timings; nghttp2::Headers custom_headers; std::string scheme; std::string host; std::string connect_to_host; std::string ifile; std::string ciphers; std::string tls13_ciphers; // supported groups (or curves). std::string groups; // length of upload data int64_t data_length; // memory mapped upload data uint8_t *data; addrinfo *addrs; size_t nreqs; size_t nclients; size_t nthreads; // The maximum number of concurrent streams per session. size_t max_concurrent_streams; size_t window_bits; size_t connection_window_bits; size_t max_frame_size; // rate at which connections should be made size_t rate; ev_tstamp rate_period; // amount of time for main measurements in timing-based test ev_tstamp duration; // amount of time to wait before starting measurements in timing-based test ev_tstamp warm_up_time; // amount of time to wait for activity on a given connection ev_tstamp conn_active_timeout; // amount of time to wait after the last request is made on a connection ev_tstamp conn_inactivity_timeout; enum { PROTO_HTTP2, PROTO_HTTP1_1 } no_tls_proto; uint32_t header_table_size; uint32_t encoder_header_table_size; // file descriptor for upload data int data_fd; // file descriptor to write per-request stats to. int log_fd; // base file name of qlog output files std::string qlog_file_base; uint16_t port; uint16_t default_port; uint16_t connect_to_port; bool verbose; bool timing_script; std::string base_uri; // true if UNIX domain socket is used. In this case, base_uri is // not used in usual way. bool base_uri_unix; // used when UNIX domain socket is used (base_uri_unix is true). sockaddr_un unix_addr; // list of supported ALPN protocol strings in the order of // preference. std::vector alpn_list; // The number of request per second for each client. double rps; // Disables GSO for UDP connections. bool no_udp_gso; // The maximum UDP datagram payload size to send. size_t max_udp_payload_size; // Enable ktls. bool ktls; // sni is the value sent in TLS SNI, overriding DNS name of the // remote host. std::string sni; Config(); ~Config(); bool is_rate_mode() const; bool is_timing_based_mode() const; bool has_base_uri() const; bool rps_enabled() const; bool is_quic() const; }; struct RequestStat { // time point when request was sent std::chrono::steady_clock::time_point request_time; // same, but in wall clock reference frame std::chrono::system_clock::time_point request_wall_time; // time point when stream was closed std::chrono::steady_clock::time_point stream_close_time; // upload data length sent so far int64_t data_offset; // HTTP status code int status; // true if stream was successfully closed. This means stream was // not reset, but it does not mean HTTP level error (e.g., 404). bool completed; }; struct ClientStat { // time client started (i.e., first connect starts) std::chrono::steady_clock::time_point client_start_time; // time client end (i.e., client somehow processed all requests it // is responsible for, and disconnected) std::chrono::steady_clock::time_point client_end_time; // The number of requests completed successful, but not necessarily // means successful HTTP status code. size_t req_success; // The following 3 numbers are overwritten each time when connection // is made. // time connect starts std::chrono::steady_clock::time_point connect_start_time; // time to connect std::chrono::steady_clock::time_point connect_time; // time to first byte (TTFB) std::chrono::steady_clock::time_point ttfb; }; struct SDStat { // min, max, mean and sd (standard deviation) double min, max, mean, sd; // percentage of samples inside mean -/+ sd double within_sd; }; struct SDStats { // time for request SDStat request; // time for connect SDStat connect; // time to first byte (TTFB) SDStat ttfb; // request per second for each client SDStat rps; }; struct Stats { Stats(size_t req_todo, size_t nclients); // The total number of requests size_t req_todo; // The number of requests issued so far size_t req_started; // The number of requests finished size_t req_done; // The number of requests completed successful, but not necessarily // means successful HTTP status code. size_t req_success; // The number of requests marked as success. HTTP status code is // also considered as success. This is subset of req_done. size_t req_status_success; // The number of requests failed. This is subset of req_done. size_t req_failed; // The number of requests failed due to network errors. This is // subset of req_failed. size_t req_error; // The number of requests that failed due to timeout. size_t req_timedout; // The number of bytes received on the "wire". If SSL/TLS is used, // this is the number of decrypted bytes the application received. int64_t bytes_total; // The number of bytes received for header fields. This is // compressed version. int64_t bytes_head; // The number of bytes received for header fields after they are // decompressed. int64_t bytes_head_decomp; // The number of bytes received in DATA frame. int64_t bytes_body; // The number of each HTTP status category, status[i] is status code // in the range [i*100, (i+1)*100). std::array status; // The statistics per request std::vector req_stats; // The statistics per client std::vector client_stats; // The number of UDP datagrams received. size_t udp_dgram_recv; // The number of UDP datagrams sent. size_t udp_dgram_sent; }; enum ClientState { CLIENT_IDLE, CLIENT_CONNECTED }; // This type tells whether the client is in warmup phase or not or is over enum class Phase { INITIAL_IDLE, // Initial idle state before warm-up phase WARM_UP, // Warm up phase when no measurements are done MAIN_DURATION, // Main measurement phase; if timing-based // test is not run, this is the default phase DURATION_OVER // This phase occurs after the measurements are over }; struct Client; // We use reservoir sampling method struct Sampling { // maximum number of samples size_t max_samples; // number of samples seen, including discarded samples. size_t n; }; struct Worker { MemchunkPool mcpool; std::mt19937 randgen; Stats stats; Sampling request_times_smp; Sampling client_smp; struct ev_loop *loop; SSL_CTX *ssl_ctx; Config *config; size_t progress_interval; uint32_t id; bool tls_info_report_done; bool app_info_report_done; size_t nconns_made; // number of clients this worker handles size_t nclients; // number of requests each client issues size_t nreqs_per_client; // at most nreqs_rem clients get an extra request size_t nreqs_rem; size_t rate; // maximum number of samples in this worker thread size_t max_samples; ev_timer timeout_watcher; // The next client ID this worker assigns uint32_t next_client_id; // Keeps track of the current phase (for timing-based experiment) for the // worker Phase current_phase; // We need to keep track of the clients in order to stop them when needed std::vector clients; // This is only active when there is not a bounded number of requests // specified ev_timer duration_watcher; ev_timer warmup_watcher; Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t nreq_todo, size_t nclients, size_t rate, size_t max_samples, Config *config); ~Worker(); Worker(Worker &&o) = default; void run(); void sample_req_stat(RequestStat *req_stat); void sample_client_stat(ClientStat *cstat); void report_progress(); void report_rate_progress(); // This function calls the destructors of all the clients. void stop_all_clients(); // This function frees a client from the list of clients for this Worker. void free_client(Client *); }; struct Stream { RequestStat req_stat; int status_success; Stream(); }; struct Client { DefaultMemchunks wb; std::unordered_map streams; ClientStat cstat; std::unique_ptr session; ev_io wev; ev_io rev; std::function readfn, writefn; Worker *worker; SSL *ssl; #ifdef ENABLE_HTTP3 struct { ngtcp2_crypto_conn_ref conn_ref; ev_timer pkt_timer; ngtcp2_conn *conn; ngtcp2_ccerr last_error; # if OPENSSL_3_5_0_API ngtcp2_crypto_ossl_ctx *ossl_ctx; # endif // OPENSSL_3_5_0_API bool close_requested; FILE *qlog_file; struct { bool send_blocked; struct { Address remote_addr; std::span data; size_t gso_size; } blocked; std::unique_ptr data; bool no_gso; } tx; } quic; #endif // defined(ENABLE_HTTP3) ev_timer request_timeout_watcher; addrinfo *next_addr; // Address for the current address. When try_new_connection() is // used and current_addr is not nullptr, it is used instead of // trying next address though next_addr. To try new address, set // nullptr to current_addr before calling connect(). addrinfo *current_addr; size_t reqidx; ClientState state; // The number of requests this client has to issue. size_t req_todo; // The number of requests left to issue size_t req_left; // The number of requests currently have started, but not abandoned // or finished. size_t req_inflight; // The number of requests this client has issued so far. size_t req_started; // The number of requests this client has done so far. size_t req_done; // The client id per worker uint32_t id; int fd; Address local_addr; ev_timer conn_active_watcher; ev_timer conn_inactivity_watcher; std::string selected_proto; bool new_connection_requested; // true if the current connection will be closed, and no more new // request cannot be processed. bool final; // rps_watcher is a timer to invoke callback periodically to // generate a new request. ev_timer rps_watcher; // The timestamp that starts the period which contributes to the // next request generation. std::chrono::steady_clock::time_point rps_duration_started; // The number of requests allowed by rps, but limited by stream // concurrency. size_t rps_req_pending; // The number of in-flight streams. req_inflight has similar value // but it only measures requests made during Phase::MAIN_DURATION. // rps_req_inflight measures the number of requests in all phases, // and it is only used if --rps is given. size_t rps_req_inflight; enum { ERR_CONNECT_FAIL = -100 }; Client(uint32_t id, Worker *worker, size_t req_todo); ~Client(); int make_socket(addrinfo *addr); int connect(); void disconnect(); void fail(); // Call this function when do_read() returns -1. This function // tries to connect to the remote host again if it is requested. If // so, this function returns 0, and this object should be retained. // Otherwise, this function returns -1, and this object should be // deleted. int try_again_or_fail(); void timeout(); void restart_timeout(); int submit_request(); void process_request_failure(); void process_timedout_streams(); void process_abandoned_streams(); void report_tls_info(); void report_app_info(); void terminate_session(); // Asks client to create new connection, instead of just fail. void try_new_connection(); int do_read(); int do_write(); // low-level I/O callback functions called by do_read/do_write int connected(); int read_clear(); int write_clear(); int tls_handshake(); int read_tls(); int write_tls(); int on_read(const uint8_t *data, size_t len); int on_write(); int connection_made(); void on_request(int64_t stream_id); void on_header(int64_t stream_id, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen); void on_status_code(int64_t stream_id, uint16_t status); // |success| == true means that the request/response was exchanged // |successfully, but it does not mean response carried successful // |HTTP status code. void on_stream_close(int64_t stream_id, bool success, bool final = false); // Returns RequestStat for |stream_id|. This function must be // called after on_request(stream_id), and before // on_stream_close(stream_id, ...). Otherwise, this will return // nullptr. RequestStat *get_req_stat(int64_t stream_id); void record_request_time(RequestStat *req_stat); void record_connect_start_time(); void record_connect_time(); void record_ttfb(); void clear_connect_times(); void record_client_start_time(); void record_client_end_time(); void signal_write(); #ifdef ENABLE_HTTP3 // QUIC int quic_init(const sockaddr *local_addr, socklen_t local_addrlen, const sockaddr *remote_addr, socklen_t remote_addrlen); void quic_free(); int read_quic(); int write_quic(); ngtcp2_ssize write_quic_pkt(ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_tstamp ts); std::span write_udp(const sockaddr *addr, socklen_t addrlen, std::span data, size_t gso_size); void write_udp_or_blocked(const ngtcp2_path &path, std::span data, size_t gso_size); void on_send_blocked(const ngtcp2_addr &remote_addr, std::span data, size_t gso_size); int send_blocked_packet(); void quic_close_connection(); int quic_handshake_completed(); int quic_recv_stream_data(uint32_t flags, int64_t stream_id, const uint8_t *data, size_t datalen); int quic_acked_stream_data_offset(int64_t stream_id, size_t datalen); int quic_stream_close(int64_t stream_id, uint64_t app_error_code); int quic_stream_reset(int64_t stream_id, uint64_t app_error_code); int quic_stream_stop_sending(int64_t stream_id, uint64_t app_error_code); int quic_extend_max_local_streams(); int quic_extend_max_stream_data(int64_t stream_id); int quic_write_client_handshake(ngtcp2_encryption_level level, const uint8_t *data, size_t datalen); int quic_pkt_timeout(); void quic_restart_pkt_timer(); void quic_write_qlog(const void *data, size_t datalen); int quic_make_http3_session(); #endif // defined(ENABLE_HTTP3) }; } // namespace h2load #endif // !defined(H2LOAD_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_downstream_test.cc0000644000000000000000000000013215077107270020024 xustar0030 mtime=1761382072.993444139 30 atime=1761382106.248310233 30 ctime=1761382109.245299962 nghttp2-1.68.0/src/shrpx_downstream_test.cc0000644000175100017510000001634415077107270020424 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_downstream_test.h" #include #include "munitxx.h" #include "shrpx_downstream.h" using namespace std::literals; namespace shrpx { namespace { const MunitTest tests[]{ munit_void_test(test_downstream_field_store_append_last_header), munit_void_test(test_downstream_field_store_header), munit_void_test(test_downstream_crumble_request_cookie), munit_void_test(test_downstream_assemble_request_cookie), munit_void_test(test_downstream_rewrite_location_response_header), munit_void_test(test_downstream_supports_non_final_response), munit_void_test(test_downstream_find_affinity_cookie), munit_test_end(), }; } // namespace const MunitSuite downstream_suite{ "/downstream", tests, nullptr, 1, MUNIT_SUITE_OPTION_NONE, }; void test_downstream_field_store_append_last_header(void) { BlockAllocator balloc(16, 16); FieldStore fs(balloc, 0); fs.alloc_add_header_name("alpha"sv); auto bravo = "BRAVO"sv; fs.append_last_header_key(bravo); // Add more characters so that relloc occurs auto golf = "golF0123456789"sv; fs.append_last_header_key(golf); auto charlie = "Charlie"sv; fs.append_last_header_value(charlie); auto delta = "deltA"sv; fs.append_last_header_value(delta); // Add more characters so that relloc occurs auto echo = "echo0123456789"sv; fs.append_last_header_value(echo); fs.add_header_token("echo"sv, "foxtrot"sv, false, -1); auto ans = HeaderRefs{{"alphabravogolf0123456789"sv, "CharliedeltAecho0123456789"sv}, {"echo"sv, "foxtrot"sv}}; assert_true(ans == fs.headers()); } void test_downstream_field_store_header(void) { BlockAllocator balloc(16, 16); FieldStore fs(balloc, 0); fs.add_header_token("alpha"sv, "0"sv, false, -1); fs.add_header_token(":authority"sv, "1"sv, false, http2::HD__AUTHORITY); fs.add_header_token("content-length"sv, "2"sv, false, http2::HD_CONTENT_LENGTH); // By token assert_true(HeaderRef(":authority"sv, "1"sv) == *fs.header(http2::HD__AUTHORITY)); assert_null(fs.header(http2::HD__METHOD)); // By name assert_true(HeaderRef("alpha"sv, "0"sv) == *fs.header("alpha"sv)); assert_null(fs.header("bravo"sv)); } void test_downstream_crumble_request_cookie(void) { Downstream d(nullptr, nullptr, 0); auto &req = d.request(); req.fs.add_header_token(":method"sv, "get"sv, false, -1); req.fs.add_header_token(":path"sv, "/"sv, false, -1); req.fs.add_header_token("cookie"sv, "alpha; bravo; ; ;; charlie;;"sv, true, http2::HD_COOKIE); req.fs.add_header_token("cookie"sv, ";delta"sv, false, http2::HD_COOKIE); req.fs.add_header_token("cookie"sv, "echo"sv, false, http2::HD_COOKIE); std::vector nva; d.crumble_request_cookie(nva); auto num_cookies = d.count_crumble_request_cookie(); assert_size(5, ==, nva.size()); assert_size(5, ==, num_cookies); HeaderRefs cookies; std::ranges::transform(nva, std::back_inserter(cookies), [](const auto &nv) { return HeaderRef(as_string_view(nv.name, nv.namelen), as_string_view(nv.value, nv.valuelen), nv.flags & NGHTTP2_NV_FLAG_NO_INDEX); }); HeaderRefs ans = {{"cookie"sv, "alpha"sv}, {"cookie"sv, "bravo"sv}, {"cookie"sv, "charlie"sv}, {"cookie"sv, "delta"sv}, {"cookie"sv, "echo"sv}}; assert_true(ans == cookies); assert_true(cookies[0].no_index); assert_true(cookies[1].no_index); assert_true(cookies[2].no_index); } void test_downstream_assemble_request_cookie(void) { Downstream d(nullptr, nullptr, 0); auto &req = d.request(); req.fs.add_header_token(":method"sv, "get"sv, false, -1); req.fs.add_header_token(":path"sv, "/"sv, false, -1); req.fs.add_header_token("cookie"sv, "alpha"sv, false, http2::HD_COOKIE); req.fs.add_header_token("cookie"sv, "bravo;"sv, false, http2::HD_COOKIE); req.fs.add_header_token("cookie"sv, "charlie; "sv, false, http2::HD_COOKIE); req.fs.add_header_token("cookie"sv, "delta;;"sv, false, http2::HD_COOKIE); assert_stdsv_equal("alpha; bravo; charlie; delta"sv, d.assemble_request_cookie()); } void test_downstream_rewrite_location_response_header(void) { Downstream d(nullptr, nullptr, 0); auto &req = d.request(); auto &resp = d.response(); d.set_request_downstream_host("localhost2"sv); req.authority = "localhost:8443"sv; resp.fs.add_header_token("location"sv, "http://localhost2:3000/"sv, false, http2::HD_LOCATION); d.rewrite_location_response_header("https"sv); auto location = resp.fs.header(http2::HD_LOCATION); assert_stdsv_equal("https://localhost:8443/"sv, (*location).value); } void test_downstream_supports_non_final_response(void) { Downstream d(nullptr, nullptr, 0); auto &req = d.request(); req.http_major = 3; req.http_minor = 0; assert_true(d.supports_non_final_response()); req.http_major = 2; req.http_minor = 0; assert_true(d.supports_non_final_response()); req.http_major = 1; req.http_minor = 1; assert_true(d.supports_non_final_response()); req.http_major = 1; req.http_minor = 0; assert_false(d.supports_non_final_response()); req.http_major = 0; req.http_minor = 9; assert_false(d.supports_non_final_response()); } void test_downstream_find_affinity_cookie(void) { Downstream d(nullptr, nullptr, 0); auto &req = d.request(); req.fs.add_header_token("cookie"sv, ""sv, false, http2::HD_COOKIE); req.fs.add_header_token("cookie"sv, "a=b;;c=d"sv, false, http2::HD_COOKIE); req.fs.add_header_token("content-length"sv, "599"sv, false, http2::HD_CONTENT_LENGTH); req.fs.add_header_token("cookie"sv, "lb=deadbeef;LB=f1f2f3f4"sv, false, http2::HD_COOKIE); req.fs.add_header_token("cookie"sv, "short=e1e2e3e"sv, false, http2::HD_COOKIE); uint32_t aff; aff = d.find_affinity_cookie("lb"sv); assert_uint32(0xdeadbeef, ==, aff); aff = d.find_affinity_cookie("LB"sv); assert_uint32(0xf1f2f3f4, ==, aff); aff = d.find_affinity_cookie("short"sv); assert_uint32(0, ==, aff); } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_log_config.h0000644000000000000000000000013215077107270016552 xustar0030 mtime=1761382072.996444125 30 atime=1761382106.125310775 30 ctime=1761382109.122300317 nghttp2-1.68.0/src/shrpx_log_config.h0000644000175100017510000000504615077107270017147 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_LOG_CONFIG_H #define SHRPX_LOG_CONFIG_H #include "shrpx.h" #include #include #include "template.h" using namespace nghttp2; namespace shrpx { struct Timestamp { Timestamp(const std::chrono::system_clock::time_point &tp); std::array time_local_buf; std::array time_iso8601_buf; std::array time_http_buf; std::string_view time_local; std::string_view time_iso8601; std::string_view time_http; }; struct LogConfig { std::chrono::system_clock::time_point time_str_updated; std::shared_ptr tstamp; std::string thread_id; pid_t pid; int accesslog_fd; int errorlog_fd; // true if errorlog_fd is referring to a terminal. bool errorlog_tty; LogConfig(); // Updates time stamp if difference between time_str_updated and now // is 1 or more milliseconds. void update_tstamp_millis(const std::chrono::system_clock::time_point &now); // Updates time stamp if difference between time_str_updated and // now, converted to time_t, is 1 or more seconds. void update_tstamp(const std::chrono::system_clock::time_point &now); }; // We need LogConfig per thread to avoid data race around opening file // descriptor for log files. LogConfig *log_config(); } // namespace shrpx #endif // !defined(SHRPX_LOG_CONFIG_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_mruby_module_request.cc0000644000000000000000000000013115077107270021054 xustar0029 mtime=1761382072.99744412 30 atime=1761382106.187310502 30 ctime=1761382109.183300141 nghttp2-1.68.0/src/shrpx_mruby_module_request.cc0000644000175100017510000002576315077107270021462 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_mruby_module_request.h" #include #include #include #include #include "shrpx_downstream.h" #include "shrpx_upstream.h" #include "shrpx_client_handler.h" #include "shrpx_mruby.h" #include "shrpx_mruby_module.h" #include "shrpx_log.h" #include "util.h" #include "http2.h" namespace shrpx { namespace mruby { namespace { mrb_value request_init(mrb_state *mrb, mrb_value self) { return self; } } // namespace namespace { mrb_value request_get_http_version_major(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; const auto &req = downstream->request(); return mrb_fixnum_value(req.http_major); } } // namespace namespace { mrb_value request_get_http_version_minor(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; const auto &req = downstream->request(); return mrb_fixnum_value(req.http_minor); } } // namespace namespace { mrb_value request_get_method(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; const auto &req = downstream->request(); auto method = http2::to_method_string(req.method); return mrb_str_new(mrb, method.data(), static_cast(method.size())); } } // namespace namespace { mrb_value request_set_method(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto &req = downstream->request(); check_phase(mrb, data->phase, PHASE_REQUEST); const char *method; mrb_int n; mrb_get_args(mrb, "s", &method, &n); if (n == 0) { mrb_raise(mrb, E_RUNTIME_ERROR, "method must not be empty string"); } auto token = http2::lookup_method_token( std::string_view{method, static_cast(n)}); if (token == -1) { mrb_raise(mrb, E_RUNTIME_ERROR, "method not supported"); } req.method = token; return self; } } // namespace namespace { mrb_value request_get_authority(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; const auto &req = downstream->request(); return mrb_str_new(mrb, req.authority.data(), static_cast(req.authority.size())); } } // namespace namespace { mrb_value request_set_authority(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto &req = downstream->request(); auto &balloc = downstream->get_block_allocator(); check_phase(mrb, data->phase, PHASE_REQUEST); const char *authority; mrb_int n; mrb_get_args(mrb, "s", &authority, &n); if (n == 0) { mrb_raise(mrb, E_RUNTIME_ERROR, "authority must not be empty string"); } req.authority = make_string_ref( balloc, std::string_view{authority, static_cast(n)}); return self; } } // namespace namespace { mrb_value request_get_scheme(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; const auto &req = downstream->request(); return mrb_str_new(mrb, req.scheme.data(), static_cast(req.scheme.size())); } } // namespace namespace { mrb_value request_set_scheme(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto &req = downstream->request(); auto &balloc = downstream->get_block_allocator(); check_phase(mrb, data->phase, PHASE_REQUEST); const char *scheme; mrb_int n; mrb_get_args(mrb, "s", &scheme, &n); if (n == 0) { mrb_raise(mrb, E_RUNTIME_ERROR, "scheme must not be empty string"); } req.scheme = make_string_ref(balloc, std::string_view{scheme, static_cast(n)}); return self; } } // namespace namespace { mrb_value request_get_path(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; const auto &req = downstream->request(); return mrb_str_new(mrb, req.path.data(), static_cast(req.path.size())); } } // namespace namespace { mrb_value request_set_path(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto &req = downstream->request(); auto &balloc = downstream->get_block_allocator(); check_phase(mrb, data->phase, PHASE_REQUEST); const char *path; mrb_int pathlen; mrb_get_args(mrb, "s", &path, &pathlen); req.path = make_string_ref( balloc, std::string_view{path, static_cast(pathlen)}); return self; } } // namespace namespace { mrb_value request_get_headers(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; const auto &req = downstream->request(); return create_headers_hash(mrb, req.fs.headers()); } } // namespace namespace { mrb_value request_mod_header(mrb_state *mrb, mrb_value self, bool repl) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto &req = downstream->request(); auto &balloc = downstream->get_block_allocator(); check_phase(mrb, data->phase, PHASE_REQUEST); mrb_value key, values; mrb_get_args(mrb, "So", &key, &values); if (RSTRING_LEN(key) == 0) { mrb_raise(mrb, E_RUNTIME_ERROR, "empty key is not allowed"); } auto ai = mrb_gc_arena_save(mrb); key = mrb_funcall(mrb, key, "downcase", 0); auto keyref = make_string_ref( balloc, std::string_view{RSTRING_PTR(key), static_cast(RSTRING_LEN(key))}); mrb_gc_arena_restore(mrb, ai); auto token = http2::lookup_token(keyref); if (repl) { size_t p = 0; auto &headers = req.fs.headers(); for (size_t i = 0; i < headers.size(); ++i) { auto &kv = headers[i]; if (kv.name == keyref) { continue; } if (i != p) { headers[p] = std::move(kv); } ++p; } headers.resize(p); } if (mrb_array_p(values)) { auto n = RARRAY_LEN(values); for (int i = 0; i < n; ++i) { auto value = mrb_ary_ref(mrb, values, i); if (!mrb_string_p(value)) { mrb_raise(mrb, E_RUNTIME_ERROR, "value must be string"); } req.fs.add_header_token( keyref, make_string_ref( balloc, std::string_view{RSTRING_PTR(value), static_cast(RSTRING_LEN(value))}), false, token); } } else if (mrb_string_p(values)) { req.fs.add_header_token( keyref, make_string_ref( balloc, std::string_view{RSTRING_PTR(values), static_cast(RSTRING_LEN(values))}), false, token); } else { mrb_raise(mrb, E_RUNTIME_ERROR, "value must be string"); } return mrb_nil_value(); } } // namespace namespace { mrb_value request_set_header(mrb_state *mrb, mrb_value self) { return request_mod_header(mrb, self, true); } } // namespace namespace { mrb_value request_add_header(mrb_state *mrb, mrb_value self) { return request_mod_header(mrb, self, false); } } // namespace namespace { mrb_value request_clear_headers(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto &req = downstream->request(); check_phase(mrb, data->phase, PHASE_REQUEST); req.fs.clear_headers(); return mrb_nil_value(); } } // namespace namespace { mrb_value request_push(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto upstream = downstream->get_upstream(); const char *uri; mrb_int len; mrb_get_args(mrb, "s", &uri, &len); upstream->initiate_push(downstream, std::string_view{uri, static_cast(len)}); return mrb_nil_value(); } } // namespace void init_request_class(mrb_state *mrb, RClass *module) { auto request_class = mrb_define_class_under(mrb, module, "Request", mrb->object_class); mrb_define_method(mrb, request_class, "initialize", request_init, MRB_ARGS_NONE()); mrb_define_method(mrb, request_class, "http_version_major", request_get_http_version_major, MRB_ARGS_NONE()); mrb_define_method(mrb, request_class, "http_version_minor", request_get_http_version_minor, MRB_ARGS_NONE()); mrb_define_method(mrb, request_class, "method", request_get_method, MRB_ARGS_NONE()); mrb_define_method(mrb, request_class, "method=", request_set_method, MRB_ARGS_REQ(1)); mrb_define_method(mrb, request_class, "authority", request_get_authority, MRB_ARGS_NONE()); mrb_define_method(mrb, request_class, "authority=", request_set_authority, MRB_ARGS_REQ(1)); mrb_define_method(mrb, request_class, "scheme", request_get_scheme, MRB_ARGS_NONE()); mrb_define_method(mrb, request_class, "scheme=", request_set_scheme, MRB_ARGS_REQ(1)); mrb_define_method(mrb, request_class, "path", request_get_path, MRB_ARGS_NONE()); mrb_define_method(mrb, request_class, "path=", request_set_path, MRB_ARGS_REQ(1)); mrb_define_method(mrb, request_class, "headers", request_get_headers, MRB_ARGS_NONE()); mrb_define_method(mrb, request_class, "add_header", request_add_header, MRB_ARGS_REQ(2)); mrb_define_method(mrb, request_class, "set_header", request_set_header, MRB_ARGS_REQ(2)); mrb_define_method(mrb, request_class, "clear_headers", request_clear_headers, MRB_ARGS_NONE()); mrb_define_method(mrb, request_class, "push", request_push, MRB_ARGS_REQ(1)); } } // namespace mruby } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/h2load_http3_session.h0000644000000000000000000000013115077107270017255 xustar0030 mtime=1761382072.986444171 30 atime=1761382106.222310348 29 ctime=1761382109.21830004 nghttp2-1.68.0/src/h2load_http3_session.h0000644000175100017510000000575215077107270017657 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2019 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef H2LOAD_HTTP3_SESSION_H #define H2LOAD_HTTP3_SESSION_H #include "h2load_session.h" #include namespace h2load { struct Client; class Http3Session : public Session { public: Http3Session(Client *client); virtual ~Http3Session(); virtual void on_connect(); virtual int submit_request(); virtual int on_read(const uint8_t *data, size_t len); virtual int on_write(); virtual void terminate(); virtual size_t max_concurrent_streams(); int init_conn(); int stream_close(int64_t stream_id, uint64_t app_error_code); int end_stream(int64_t stream_id); void recv_data(int64_t stream_id, const uint8_t *data, size_t datalen); void consume(int64_t stream_id, size_t nconsumed); void begin_headers(int64_t stream_id); void recv_header(int64_t stream_id, const nghttp3_vec *name, const nghttp3_vec *value); int stop_sending(int64_t stream_id, uint64_t app_error_code); int reset_stream(int64_t stream_id, uint64_t app_error_code); int close_stream(int64_t stream_id, uint64_t app_error_code); int shutdown_stream_read(int64_t stream_id); int extend_max_local_streams(); int64_t submit_request_internal(); ssize_t read_stream(uint32_t flags, int64_t stream_id, const uint8_t *data, size_t datalen); ssize_t write_stream(int64_t &stream_id, int &fin, nghttp3_vec *vec, size_t veccnt); void block_stream(int64_t stream_id); int unblock_stream(int64_t stream_id); void shutdown_stream_write(int64_t stream_id); int add_write_offset(int64_t stream_id, size_t ndatalen); int add_ack_offset(int64_t stream_id, size_t datalen); void read_data(nghttp3_vec *vec, size_t veccnt, uint32_t *pflags); private: Client *client_; nghttp3_conn *conn_; size_t npending_request_; size_t reqidx_; }; } // namespace h2load #endif // H2LOAD_HTTP3_SESSION_H nghttp2-1.68.0/src/PaxHeaders/shrpx_quic_connection_handler.h0000644000000000000000000000013215077107270021321 xustar0030 mtime=1761382072.998444116 30 atime=1761382106.199310449 30 ctime=1761382109.194300109 nghttp2-1.68.0/src/shrpx_quic_connection_handler.h0000644000175100017510000001277115077107270021721 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2021 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_QUIC_CONNECTION_HANDLER_H #define SHRPX_QUIC_CONNECTION_HANDLER_H #include "shrpx.h" #include #include #include #include #include #include #include "shrpx_quic.h" #include "network.h" using namespace nghttp2; namespace shrpx { struct UpstreamAddr; class ClientHandler; class Worker; // CloseWait handles packets received in close-wait (draining or // closing period). struct CloseWait { CloseWait(Worker *worker, std::vector scids, std::vector pkt, ev_tstamp period); ~CloseWait(); int handle_packet(const UpstreamAddr *faddr, const Address &remote_addr, const Address &local_addr, const ngtcp2_pkt_info &pi, std::span data); Worker *worker; // Source Connection IDs of the connection. std::vector scids; // QUIC packet which is sent in response to the incoming packet. It // might be empty. std::vector pkt; // Close-wait (draining or closing period) timer. ev_timer timer; // The number of bytes received during close-wait period. size_t bytes_recv; // The number of bytes sent during close-wait period. size_t bytes_sent; // The number of packets received during close-wait period. size_t num_pkts_recv; // If the number of packets received reaches this number, send a // QUIC packet. size_t next_pkts_recv; }; class QUICConnectionHandler { public: QUICConnectionHandler(Worker *worker); ~QUICConnectionHandler(); int handle_packet(const UpstreamAddr *faddr, const Address &remote_addr, const Address &local_addr, const ngtcp2_pkt_info &pi, std::span data); // Send Retry packet. |ini_dcid| is the destination Connection ID // which appeared in Client Initial packet. |ini_scid| is the // source Connection ID which appeared in Client Initial packet. int send_retry(const UpstreamAddr *faddr, uint32_t version, std::span ini_dcid, std::span ini_scid, const Address &remote_addr, const Address &local_addr, size_t max_pktlen); // Send Version Negotiation packet. |ini_dcid| is the destination // Connection ID which appeared in Client Initial packet. // |ini_scid| is the source Connection ID which appeared in Client // Initial packet. int send_version_negotiation(const UpstreamAddr *faddr, uint32_t version, std::span ini_dcid, std::span ini_scid, const Address &remote_addr, const Address &local_addr); int send_stateless_reset(const UpstreamAddr *faddr, size_t pktlen, std::span dcid, const Address &remote_addr, const Address &local_addr); // Send Initial CONNECTION_CLOSE. |ini_dcid| is the destination // Connection ID which appeared in Client Initial packet. // |ini_scid| is the source Connection ID which appeared in Client // Initial packet. int send_connection_close(const UpstreamAddr *faddr, uint32_t version, const ngtcp2_cid &ini_dcid, const ngtcp2_cid &ini_scid, const Address &remote_addr, const Address &local_addr, uint64_t error_code, size_t max_pktlen); ClientHandler * handle_new_connection(const UpstreamAddr *faddr, const Address &remote_addr, const Address &local_addr, const ngtcp2_pkt_hd &hd, const ngtcp2_cid *odcid, std::span token, ngtcp2_token_type token_type); void add_connection_id(const ngtcp2_cid &cid, ClientHandler *handler); void remove_connection_id(const ngtcp2_cid &cid); void add_close_wait(CloseWait *cw); void remove_close_wait(const CloseWait *cw); void on_stateless_reset_bucket_regen(); private: Worker *worker_; std::unordered_map connections_; std::unordered_map close_waits_; ev_timer stateless_reset_bucket_regen_timer_; size_t stateless_reset_bucket_; }; } // namespace shrpx #endif // !defined(SHRPX_QUIC_CONNECTION_HANDLER_H) nghttp2-1.68.0/src/PaxHeaders/comp_helper.h0000644000000000000000000000013115077107270015514 xustar0029 mtime=1761382072.98444418 30 atime=1761382106.210310401 30 ctime=1761382109.206300075 nghttp2-1.68.0/src/comp_helper.h0000644000175100017510000000355115077107270016111 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_COMP_HELPER_H #define NGHTTP2_COMP_HELPER_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include #include #ifdef __cplusplus extern "C" { #endif /* defined(__cplusplus) */ json_t *dump_deflate_header_table(nghttp2_hd_deflater *deflater); json_t *dump_inflate_header_table(nghttp2_hd_inflater *inflater); json_t *dump_header(const uint8_t *name, size_t namelen, const uint8_t *value, size_t vlauelen); json_t *dump_headers(const nghttp2_nv *nva, size_t nvlen); void output_json_header(void); void output_json_footer(void); #ifdef __cplusplus } #endif /* defined(__cplusplus) */ #endif /* !defined(NGHTTP2_COMP_HELPER_H) */ nghttp2-1.68.0/src/PaxHeaders/shrpx_https_upstream.h0000644000000000000000000000013115077107270017525 xustar0030 mtime=1761382072.995444129 30 atime=1761382106.094310912 29 ctime=1761382109.09030041 nghttp2-1.68.0/src/shrpx_https_upstream.h0000644000175100017510000001015715077107270020122 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_HTTPS_UPSTREAM_H #define SHRPX_HTTPS_UPSTREAM_H #include "shrpx.h" #include #include #include "llhttp.h" #include "shrpx_upstream.h" #include "memchunk.h" using namespace nghttp2; namespace shrpx { class ClientHandler; class HttpsUpstream : public Upstream { public: HttpsUpstream(ClientHandler *handler); virtual ~HttpsUpstream(); virtual int on_read(); virtual int on_write(); virtual int on_event(); virtual int on_downstream_abort_request(Downstream *downstream, unsigned int status_code); virtual int on_downstream_abort_request_with_https_redirect(Downstream *downstream); virtual ClientHandler *get_client_handler() const; virtual int downstream_read(DownstreamConnection *dconn); virtual int downstream_write(DownstreamConnection *dconn); virtual int downstream_eof(DownstreamConnection *dconn); virtual int downstream_error(DownstreamConnection *dconn, int events); void attach_downstream(std::unique_ptr downstream); void delete_downstream(); Downstream *get_downstream() const; std::unique_ptr pop_downstream(); void error_reply(unsigned int status_code); virtual void pause_read(IOCtrlReason reason); virtual int resume_read(IOCtrlReason reason, Downstream *downstream, size_t consumed); virtual int on_downstream_header_complete(Downstream *downstream); virtual int on_downstream_body(Downstream *downstream, const uint8_t *data, size_t len, bool flush); virtual int on_downstream_body_complete(Downstream *downstream); virtual void on_handler_delete(); virtual int on_downstream_reset(Downstream *downstream, bool no_retry); virtual int send_reply(Downstream *downstream, const uint8_t *body, size_t bodylen); virtual int initiate_push(Downstream *downstream, const std::string_view &uri); virtual int response_riovec(struct iovec *iov, int iovcnt) const; virtual void response_drain(size_t n); virtual bool response_empty() const; virtual Downstream *on_downstream_push_promise(Downstream *downstream, int32_t promised_stream_id); virtual int on_downstream_push_promise_complete(Downstream *downstream, Downstream *promised_downstream); virtual bool push_enabled() const; virtual void cancel_premature_downstream(Downstream *promised_downstream); void reset_current_header_length(); void log_response_headers(DefaultMemchunks *buf) const; int redirect_to_https(Downstream *downstream); // Called when new request has started. void on_start_request(); private: ClientHandler *handler_; llhttp_t htp_; size_t current_header_length_; std::unique_ptr downstream_; IOControl ioctrl_; // The number of requests seen so far. size_t num_requests_; }; } // namespace shrpx #endif // !defined(SHRPX_HTTPS_UPSTREAM_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_downstream_connection_pool.h0000644000000000000000000000013115077107270022076 xustar0030 mtime=1761382072.993444139 29 atime=1761382106.13331074 30 ctime=1761382109.129300297 nghttp2-1.68.0/src/shrpx_downstream_connection_pool.h0000644000175100017510000000346015077107270022472 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_DOWNSTREAM_CONNECTION_POOL_H #define SHRPX_DOWNSTREAM_CONNECTION_POOL_H #include "shrpx.h" #include #include namespace shrpx { class DownstreamConnection; class DownstreamConnectionPool { public: DownstreamConnectionPool(); ~DownstreamConnectionPool(); void add_downstream_connection(std::unique_ptr dconn); std::unique_ptr pop_downstream_connection(); void remove_downstream_connection(DownstreamConnection *dconn); void remove_all(); private: std::unordered_set pool_; }; } // namespace shrpx #endif // !defined(SHRPX_DOWNSTREAM_CONNECTION_POOL_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_https_upstream.cc0000644000000000000000000000013215077107270017664 xustar0030 mtime=1761382072.995444129 30 atime=1761382106.093310916 30 ctime=1761382109.089300413 nghttp2-1.68.0/src/shrpx_https_upstream.cc0000644000175100017510000012741715077107270020270 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_https_upstream.h" #include #include #include "shrpx_client_handler.h" #include "shrpx_downstream.h" #include "shrpx_downstream_connection.h" #include "shrpx_http.h" #include "shrpx_config.h" #include "shrpx_error.h" #include "shrpx_log_config.h" #include "shrpx_worker.h" #include "shrpx_http2_session.h" #include "shrpx_log.h" #ifdef HAVE_MRUBY # include "shrpx_mruby.h" #endif // defined(HAVE_MRUBY) #include "http2.h" #include "util.h" #include "template.h" #include "base64.h" #include "urlparse.h" using namespace nghttp2; namespace shrpx { namespace { int htp_msg_begin(llhttp_t *htp); int htp_uricb(llhttp_t *htp, const char *data, size_t len); int htp_hdr_keycb(llhttp_t *htp, const char *data, size_t len); int htp_hdr_valcb(llhttp_t *htp, const char *data, size_t len); int htp_hdrs_completecb(llhttp_t *htp); int htp_bodycb(llhttp_t *htp, const char *data, size_t len); int htp_msg_completecb(llhttp_t *htp); } // namespace namespace { constexpr llhttp_settings_t htp_hooks = { .on_message_begin = htp_msg_begin, .on_url = htp_uricb, .on_header_field = htp_hdr_keycb, .on_header_value = htp_hdr_valcb, .on_headers_complete = htp_hdrs_completecb, .on_body = htp_bodycb, .on_message_complete = htp_msg_completecb, }; } // namespace HttpsUpstream::HttpsUpstream(ClientHandler *handler) : handler_(handler), current_header_length_(0), ioctrl_(handler->get_rlimit()), num_requests_(0) { llhttp_init(&htp_, HTTP_REQUEST, &htp_hooks); htp_.data = this; } HttpsUpstream::~HttpsUpstream() {} void HttpsUpstream::reset_current_header_length() { current_header_length_ = 0; } void HttpsUpstream::on_start_request() { if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "HTTP request started"; } reset_current_header_length(); auto downstream = std::make_unique(this, handler_->get_mcpool(), 0); attach_downstream(std::move(downstream)); auto &httpconf = get_config()->http; handler_->reset_upstream_read_timeout(httpconf.timeout.header); ++num_requests_; } namespace { int htp_msg_begin(llhttp_t *htp) { auto upstream = static_cast(htp->data); upstream->on_start_request(); return 0; } } // namespace namespace { int htp_uricb(llhttp_t *htp, const char *data, size_t len) { auto upstream = static_cast(htp->data); auto downstream = upstream->get_downstream(); auto &req = downstream->request(); auto &balloc = downstream->get_block_allocator(); // We happen to have the same value for method token. req.method = htp->method; if (req.fs.buffer_size() + len > get_config()->http.request_header_field_buffer) { if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "Too large URI size=" << req.fs.buffer_size() + len; } assert(downstream->get_request_state() == DownstreamState::INITIAL); downstream->set_request_state( DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE); llhttp_set_error_reason(htp, "too long request URI"); return HPE_USER; } req.fs.add_extra_buffer_size(len); if (req.method == HTTP_CONNECT) { req.authority = concat_string_ref(balloc, req.authority, std::string_view{data, len}); } else { req.path = concat_string_ref(balloc, req.path, std::string_view{data, len}); } return 0; } } // namespace namespace { int htp_hdr_keycb(llhttp_t *htp, const char *data, size_t len) { auto upstream = static_cast(htp->data); auto downstream = upstream->get_downstream(); auto &req = downstream->request(); auto &httpconf = get_config()->http; if (req.fs.buffer_size() + len > httpconf.request_header_field_buffer) { if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "Too large header block size=" << req.fs.buffer_size() + len; } if (downstream->get_request_state() == DownstreamState::INITIAL) { downstream->set_request_state( DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE); } llhttp_set_error_reason(htp, "too large header"); return HPE_USER; } auto name = std::string_view{data, len}; if (downstream->get_request_state() == DownstreamState::INITIAL) { if (req.fs.header_key_prev()) { req.fs.append_last_header_key(name); } else { if (req.fs.num_fields() >= httpconf.max_request_header_fields) { if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "Too many header field num=" << req.fs.num_fields() + 1; } downstream->set_request_state( DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE); llhttp_set_error_reason(htp, "too many headers"); return HPE_USER; } req.fs.alloc_add_header_name(name); } } else { // trailer part if (req.fs.trailer_key_prev()) { req.fs.append_last_trailer_key(name); } else { if (req.fs.num_fields() >= httpconf.max_request_header_fields) { if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "Too many header field num=" << req.fs.num_fields() + 1; } llhttp_set_error_reason(htp, "too many headers"); return HPE_USER; } req.fs.alloc_add_trailer_name(name); } } return 0; } } // namespace namespace { int htp_hdr_valcb(llhttp_t *htp, const char *data, size_t len) { auto upstream = static_cast(htp->data); auto downstream = upstream->get_downstream(); auto &req = downstream->request(); if (req.fs.buffer_size() + len > get_config()->http.request_header_field_buffer) { if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "Too large header block size=" << req.fs.buffer_size() + len; } if (downstream->get_request_state() == DownstreamState::INITIAL) { downstream->set_request_state( DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE); } llhttp_set_error_reason(htp, "too large header"); return HPE_USER; } auto value = std::string_view{data, len}; if (downstream->get_request_state() == DownstreamState::INITIAL) { req.fs.append_last_header_value(value); } else { req.fs.append_last_trailer_value(value); } return 0; } } // namespace namespace { void rewrite_request_host_path_from_uri(BlockAllocator &balloc, Request &req, const std::string_view &uri, urlparse_url &u) { assert(u.field_set & (1 << URLPARSE_HOST)); // As per https://tools.ietf.org/html/rfc7230#section-5.4, we // rewrite host header field with authority component. auto authority = util::get_uri_field(uri.data(), u, URLPARSE_HOST); // TODO properly check IPv6 numeric address auto ipv6 = util::contains(authority, ':'); auto authoritylen = authority.size(); if (ipv6) { authoritylen += 2; } if (u.field_set & (1 << URLPARSE_PORT)) { authoritylen += 1 + str_size("65535"); } if (authoritylen > authority.size()) { auto iovec = make_byte_ref(balloc, authoritylen + 1); auto p = std::ranges::begin(iovec); if (ipv6) { *p++ = '['; } p = std::ranges::copy(authority, p).out; if (ipv6) { *p++ = ']'; } if (u.field_set & (1 << URLPARSE_PORT)) { *p++ = ':'; p = util::utos(u.port, p); } *p = '\0'; req.authority = as_string_view(std::ranges::begin(iovec), p); } else { req.authority = authority; } req.scheme = util::get_uri_field(uri.data(), u, URLPARSE_SCHEMA); std::string_view path; if (u.field_set & (1 << URLPARSE_PATH)) { path = util::get_uri_field(uri.data(), u, URLPARSE_PATH); } else if (req.method == HTTP_OPTIONS) { // Server-wide OPTIONS takes following form in proxy request: // // OPTIONS http://example.org HTTP/1.1 // // Notice that no slash after authority. See // http://tools.ietf.org/html/rfc7230#section-5.3.4 req.path = ""sv; // we ignore query component here return; } else { path = "/"sv; } if (u.field_set & (1 << URLPARSE_QUERY)) { auto &fdata = u.field_data[URLPARSE_QUERY]; if (u.field_set & (1 << URLPARSE_PATH)) { auto q = util::get_uri_field(uri.data(), u, URLPARSE_QUERY); path = std::string_view{std::ranges::begin(path), std::ranges::end(q)}; } else { path = concat_string_ref(balloc, path, "?"sv, std::string_view{&uri[fdata.off], fdata.len}); } } req.path = http2::rewrite_clean_path(balloc, path); } } // namespace namespace { int htp_hdrs_completecb(llhttp_t *htp) { int rv; auto upstream = static_cast(htp->data); if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "HTTP request headers completed"; } auto handler = upstream->get_client_handler(); auto downstream = upstream->get_downstream(); auto &req = downstream->request(); auto &balloc = downstream->get_block_allocator(); for (auto &kv : req.fs.headers()) { kv.value = util::rstrip(balloc, kv.value); if (kv.token == http2::HD_TRANSFER_ENCODING && !http2::check_transfer_encoding(kv.value)) { return -1; } } auto lgconf = log_config(); lgconf->update_tstamp(std::chrono::system_clock::now()); req.tstamp = lgconf->tstamp; req.http_major = htp->http_major; req.http_minor = htp->http_minor; req.connection_close = !llhttp_should_keep_alive(htp); handler->stop_read_timer(); auto method = req.method; if (LOG_ENABLED(INFO)) { std::stringstream ss; ss << http2::to_method_string(method) << " " << (method == HTTP_CONNECT ? req.authority : req.path) << " " << "HTTP/" << req.http_major << "." << req.http_minor << "\n"; for (const auto &kv : req.fs.headers()) { if (kv.name == "authorization"sv) { ss << TTY_HTTP_HD << kv.name << TTY_RST << ": \n"; continue; } ss << TTY_HTTP_HD << kv.name << TTY_RST << ": " << kv.value << "\n"; } ULOG(INFO, upstream) << "HTTP request headers\n" << ss.str(); } // set content-length if method is not CONNECT, and no // transfer-encoding is given. If transfer-encoding is given, leave // req.fs.content_length to -1. if (method != HTTP_CONNECT && !req.fs.header(http2::HD_TRANSFER_ENCODING)) { // llhttp sets 0 to htp->content_length if there is no // content-length header field. If we don't have both // transfer-encoding and content-length header field, we assume // that there is no request body. if (htp->content_length > std::numeric_limits::max()) { return -1; } req.fs.content_length = static_cast(htp->content_length); } auto host = req.fs.header(http2::HD_HOST); if (req.http_major > 1 || req.http_minor > 1) { req.http_major = 1; req.http_minor = 1; return -1; } if (req.http_major == 1 && req.http_minor == 1 && !host) { return -1; } if (host) { const auto &value = host->value; // Not allow at least '"' or '\' in host. They are illegal in // authority component, also they cause headaches when we put them // in quoted-string. if (std::ranges::find_if(value, [](char c) { return c == '"' || c == '\\'; }) != std::ranges::end(value)) { return -1; } } downstream->inspect_http1_request(); if (htp->flags & F_CHUNKED) { downstream->set_chunked_request(true); } auto transfer_encoding = req.fs.header(http2::HD_TRANSFER_ENCODING); if (transfer_encoding && http2::legacy_http1(req.http_major, req.http_minor)) { return -1; } auto faddr = handler->get_upstream_addr(); auto config = get_config(); if (method != HTTP_CONNECT) { urlparse_url u; rv = urlparse_parse_url(req.path.data(), req.path.size(), 0, &u); if (rv != 0) { // Expect to respond with 400 bad request return -1; } // checking URLPARSE_HOST could be redundant, but just in case ... if (!(u.field_set & (1 << URLPARSE_SCHEMA)) || !(u.field_set & (1 << URLPARSE_HOST))) { req.no_authority = true; if (method == HTTP_OPTIONS && req.path == "*"sv) { req.path = ""sv; } else { req.path = http2::rewrite_clean_path(balloc, req.path); } if (host) { req.authority = host->value; } if (handler->get_ssl()) { req.scheme = "https"sv; } else { req.scheme = "http"sv; } } else { rewrite_request_host_path_from_uri(balloc, req, req.path, u); } } downstream->set_request_state(DownstreamState::HEADER_COMPLETE); auto &resp = downstream->response(); if (config->http.require_http_scheme && !http::check_http_scheme(req.scheme, handler->get_ssl() != nullptr)) { resp.http_status = 400; return -1; } #ifdef HAVE_MRUBY auto worker = handler->get_worker(); auto mruby_ctx = worker->get_mruby_context(); if (mruby_ctx->run_on_request_proc(downstream) != 0) { resp.http_status = 500; return -1; } #endif // defined(HAVE_MRUBY) // mruby hook may change method value if (req.no_authority && config->http2_proxy && faddr->alt_mode == UpstreamAltMode::NONE) { // Request URI should be absolute-form for client proxy mode return -1; } if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { return 0; } #ifdef HAVE_MRUBY DownstreamConnection *dconn_ptr; #endif // defined(HAVE_MRUBY) for (;;) { auto dconn = handler->get_downstream_connection(rv, downstream); if (!dconn) { if (rv == SHRPX_ERR_TLS_REQUIRED) { upstream->redirect_to_https(downstream); } downstream->set_request_state(DownstreamState::CONNECT_FAIL); return -1; } #ifdef HAVE_MRUBY dconn_ptr = dconn.get(); #endif // defined(HAVE_MRUBY) if (downstream->attach_downstream_connection(std::move(dconn)) == 0) { break; } } #ifdef HAVE_MRUBY const auto &group = dconn_ptr->get_downstream_addr_group(); if (group) { const auto &dmruby_ctx = group->shared_addr->mruby_ctx; if (dmruby_ctx->run_on_request_proc(downstream) != 0) { resp.http_status = 500; return -1; } if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { return 0; } } #endif // defined(HAVE_MRUBY) rv = downstream->push_request_headers(); if (rv != 0) { return -1; } if (faddr->alt_mode != UpstreamAltMode::NONE) { // Normally, we forward expect: 100-continue to backend server, // and let them decide whether responds with 100 Continue or not. // For alternative mode, we have no backend, so just send 100 // Continue here to make the client happy. if (downstream->get_expect_100_continue()) { auto output = downstream->get_response_buf(); static constexpr auto res = "HTTP/1.1 100 Continue\r\n\r\n"sv; output->append(res); handler->signal_write(); } } return 0; } } // namespace namespace { int htp_bodycb(llhttp_t *htp, const char *data, size_t len) { int rv; auto upstream = static_cast(htp->data); auto downstream = upstream->get_downstream(); rv = downstream->push_upload_data_chunk( reinterpret_cast(data), len); if (rv != 0) { // Ignore error if response has been completed. We will end up in // htp_msg_completecb, and request will end gracefully. if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { return 0; } llhttp_set_error_reason(htp, "could not process request body"); return HPE_USER; } return 0; } } // namespace namespace { int htp_msg_completecb(llhttp_t *htp) { int rv; auto upstream = static_cast(htp->data); if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "HTTP request completed"; } auto handler = upstream->get_client_handler(); auto downstream = upstream->get_downstream(); auto &req = downstream->request(); auto &balloc = downstream->get_block_allocator(); for (auto &kv : req.fs.trailers()) { kv.value = util::rstrip(balloc, kv.value); } downstream->set_request_state(DownstreamState::MSG_COMPLETE); rv = downstream->end_upload_data(); if (rv != 0) { if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { // Here both response and request were completed. One of the // reason why end_upload_data() failed is when we sent response // in request phase hook. We only delete and proceed to the // next request handling (if we don't close the connection). We // first pause parser here just as we normally do, and call // signal_write() to run on_write(). return HPE_PAUSED; } return -1; } if (handler->get_http2_upgrade_allowed() && downstream->get_http2_upgrade_request() && handler->perform_http2_upgrade(upstream) != 0) { if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "HTTP Upgrade to HTTP/2 failed"; } } // Stop further processing to complete this request return HPE_PAUSED; } } // namespace // on_read() does not consume all available data in input buffer if // one http request is fully received. int HttpsUpstream::on_read() { auto rb = handler_->get_rb(); auto rlimit = handler_->get_rlimit(); auto downstream = get_downstream(); if (rb->rleft() == 0 || handler_->get_should_close_after_write()) { return 0; } // downstream can be nullptr here, because it is initialized in the // callback chain called by llhttp_execute() if (downstream && downstream->get_upgraded()) { auto rv = downstream->push_upload_data_chunk(rb->pos(), rb->rleft()); if (rv != 0) { return -1; } rb->reset(); rlimit->startw(); if (downstream->request_buf_full()) { if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "Downstream request buf is full"; } pause_read(SHRPX_NO_BUFFER); return 0; } return 0; } if (downstream) { // To avoid reading next pipelined request switch (downstream->get_request_state()) { case DownstreamState::INITIAL: case DownstreamState::HEADER_COMPLETE: break; default: return 0; } } // llhttp_execute() does nothing once it entered error state. auto htperr = llhttp_execute(&htp_, reinterpret_cast(rb->pos()), rb->rleft()); if (htperr == HPE_PAUSED_UPGRADE && rb->pos() == reinterpret_cast(llhttp_get_error_pos(&htp_))) { llhttp_resume_after_upgrade(&htp_); htperr = llhttp_execute(&htp_, reinterpret_cast(rb->pos()), rb->rleft()); } auto nread = htperr == HPE_OK ? rb->rleft() : as_unsigned(reinterpret_cast( llhttp_get_error_pos(&htp_)) - rb->pos()); rb->drain(nread); rlimit->startw(); // Well, actually header length + some body bytes current_header_length_ += nread; // Get downstream again because it may be initialized in http parser // execution downstream = get_downstream(); if (htperr == HPE_PAUSED) { // We may pause parser in htp_msg_completecb when both side are // completed. Signal write, so that we can run on_write(). if (downstream && downstream->get_request_state() == DownstreamState::MSG_COMPLETE && downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { handler_->signal_write(); } return 0; } if (htperr != HPE_OK) { if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "HTTP parse failure: " << "(" << llhttp_errno_name(htperr) << ") " << llhttp_get_error_reason(&htp_); } if (downstream && downstream->get_response_state() != DownstreamState::INITIAL) { handler_->set_should_close_after_write(true); handler_->signal_write(); return 0; } unsigned int status_code; if (htperr == HPE_INVALID_METHOD) { status_code = 501; } else if (downstream) { status_code = downstream->response().http_status; if (status_code == 0) { if (downstream->get_request_state() == DownstreamState::CONNECT_FAIL) { status_code = 502; } else if (downstream->get_request_state() == DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE) { status_code = 431; } else { status_code = 400; } } } else { status_code = 400; } error_reply(status_code); handler_->signal_write(); return 0; } // downstream can be NULL here. if (downstream && downstream->request_buf_full()) { if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "Downstream request buffer is full"; } pause_read(SHRPX_NO_BUFFER); return 0; } return 0; } int HttpsUpstream::on_write() { auto downstream = get_downstream(); if (!downstream) { return 0; } auto output = downstream->get_response_buf(); const auto &resp = downstream->response(); if (output->rleft() > 0) { return 0; } // We need to postpone detachment until all data are sent so that // we can notify nghttp2 library all data consumed. if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { if (downstream->can_detach_downstream_connection()) { // Keep-alive downstream->detach_downstream_connection(); } else { // Connection close downstream->pop_downstream_connection(); // dconn was deleted } // We need this if response ends before request. if (downstream->get_request_state() == DownstreamState::MSG_COMPLETE) { delete_downstream(); if (handler_->get_should_close_after_write()) { return 0; } auto &upstreamconf = get_config()->conn.upstream; handler_->reset_upstream_read_timeout(upstreamconf.timeout.idle); return resume_read(SHRPX_NO_BUFFER, nullptr, 0); } else { // If the request is not complete, close the connection. delete_downstream(); handler_->set_should_close_after_write(true); return 0; } } return downstream->resume_read(SHRPX_NO_BUFFER, resp.unconsumed_body_length); } int HttpsUpstream::on_event() { return 0; } ClientHandler *HttpsUpstream::get_client_handler() const { return handler_; } void HttpsUpstream::pause_read(IOCtrlReason reason) { ioctrl_.pause_read(reason); } int HttpsUpstream::resume_read(IOCtrlReason reason, Downstream *downstream, size_t consumed) { // downstream could be nullptr if (downstream && downstream->request_buf_full()) { return 0; } if (ioctrl_.resume_read(reason)) { // Process remaining data in input buffer here because these bytes // are not notified by readcb until new data arrive. llhttp_resume(&htp_); auto conn = handler_->get_connection(); ev_feed_event(conn->loop, &conn->rev, EV_READ); return 0; } return 0; } int HttpsUpstream::downstream_read(DownstreamConnection *dconn) { auto downstream = dconn->get_downstream(); int rv; rv = downstream->on_read(); if (rv == SHRPX_ERR_EOF) { if (downstream->get_request_header_sent()) { return downstream_eof(dconn); } return SHRPX_ERR_RETRY; } if (rv == SHRPX_ERR_DCONN_CANCELED) { downstream->pop_downstream_connection(); goto end; } if (rv < 0) { return downstream_error(dconn, Downstream::EVENT_ERROR); } if (downstream->get_response_state() == DownstreamState::MSG_RESET) { return -1; } if (downstream->get_response_state() == DownstreamState::MSG_BAD_HEADER) { error_reply(502); downstream->pop_downstream_connection(); goto end; } if (downstream->can_detach_downstream_connection()) { // Keep-alive downstream->detach_downstream_connection(); } end: handler_->signal_write(); return 0; } int HttpsUpstream::downstream_write(DownstreamConnection *dconn) { int rv; rv = dconn->on_write(); if (rv == SHRPX_ERR_NETWORK) { return downstream_error(dconn, Downstream::EVENT_ERROR); } if (rv != 0) { return rv; } return 0; } int HttpsUpstream::downstream_eof(DownstreamConnection *dconn) { auto downstream = dconn->get_downstream(); if (LOG_ENABLED(INFO)) { DCLOG(INFO, dconn) << "EOF"; } if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { goto end; } if (downstream->get_response_state() == DownstreamState::HEADER_COMPLETE) { // Server may indicate the end of the request by EOF if (LOG_ENABLED(INFO)) { DCLOG(INFO, dconn) << "The end of the response body was indicated by " << "EOF"; } on_downstream_body_complete(downstream); downstream->set_response_state(DownstreamState::MSG_COMPLETE); downstream->pop_downstream_connection(); goto end; } if (downstream->get_response_state() == DownstreamState::INITIAL) { // we did not send any response headers, so we can reply error // message. if (LOG_ENABLED(INFO)) { DCLOG(INFO, dconn) << "Return error reply"; } error_reply(502); downstream->pop_downstream_connection(); goto end; } // Otherwise, we don't know how to recover from this situation. Just // drop connection. return -1; end: handler_->signal_write(); return 0; } int HttpsUpstream::downstream_error(DownstreamConnection *dconn, int events) { auto downstream = dconn->get_downstream(); if (LOG_ENABLED(INFO)) { if (events & Downstream::EVENT_ERROR) { DCLOG(INFO, dconn) << "Network error/general error"; } else { DCLOG(INFO, dconn) << "Timeout"; } } if (downstream->get_response_state() != DownstreamState::INITIAL) { return -1; } unsigned int status; if (events & Downstream::EVENT_TIMEOUT) { if (downstream->get_request_header_sent()) { status = 504; } else { status = 408; } } else { status = 502; } error_reply(status); downstream->pop_downstream_connection(); handler_->signal_write(); return 0; } int HttpsUpstream::send_reply(Downstream *downstream, const uint8_t *body, size_t bodylen) { const auto &req = downstream->request(); auto &resp = downstream->response(); auto &balloc = downstream->get_block_allocator(); auto config = get_config(); auto &httpconf = config->http; auto connection_close = false; auto worker = handler_->get_worker(); if (httpconf.max_requests <= num_requests_ || worker->get_graceful_shutdown()) { resp.fs.add_header_token("connection"sv, "close"sv, false, http2::HD_CONNECTION); connection_close = true; } else if (req.http_major <= 0 || (req.http_major == 1 && req.http_minor == 0)) { connection_close = true; } else { auto c = resp.fs.header(http2::HD_CONNECTION); if (c && util::strieq("close"sv, c->value)) { connection_close = true; } } if (connection_close) { resp.connection_close = true; handler_->set_should_close_after_write(true); } auto output = downstream->get_response_buf(); output->append("HTTP/1.1 "sv); output->append(http2::stringify_status(balloc, resp.http_status)); output->append(' '); output->append(http2::get_reason_phrase(resp.http_status)); output->append("\r\n"sv); for (auto &kv : resp.fs.headers()) { if (kv.name.empty() || kv.name[0] == ':') { continue; } http2::capitalize(output, kv.name); output->append(": "sv); output->append(kv.value); output->append("\r\n"sv); } if (!resp.fs.header(http2::HD_SERVER)) { output->append("Server: "sv); output->append(config->http.server_name); output->append("\r\n"sv); } for (auto &p : httpconf.add_response_headers) { output->append(p.name); output->append(": "sv); output->append(p.value); output->append("\r\n"sv); } output->append("\r\n"sv); if (req.method != HTTP_HEAD) { output->append(body, bodylen); downstream->response_sent_body_length += bodylen; } downstream->set_response_state(DownstreamState::MSG_COMPLETE); return 0; } void HttpsUpstream::error_reply(unsigned int status_code) { auto downstream = get_downstream(); if (!downstream) { attach_downstream( std::make_unique(this, handler_->get_mcpool(), 1)); downstream = get_downstream(); } auto &resp = downstream->response(); auto &balloc = downstream->get_block_allocator(); auto html = http::create_error_html(balloc, status_code); resp.http_status = status_code; // we are going to close connection for both frontend and backend in // error condition. This is safest option. resp.connection_close = true; handler_->set_should_close_after_write(true); auto output = downstream->get_response_buf(); output->append("HTTP/1.1 "sv); output->append(http2::stringify_status(balloc, status_code)); output->append(' '); output->append(http2::get_reason_phrase(status_code)); output->append("\r\nServer: "sv); output->append(get_config()->http.server_name); output->append("\r\nContent-Length: "sv); output->append(std::numeric_limits::digits10 + 1, std::bind_front(util::UIntFormatter{}, html.size())); output->append("\r\nDate: "sv); auto lgconf = log_config(); lgconf->update_tstamp(std::chrono::system_clock::now()); output->append(lgconf->tstamp->time_http); output->append("\r\nContent-Type: text/html; " "charset=UTF-8\r\nConnection: close\r\n\r\n"sv); const auto &req = downstream->request(); if (req.method != HTTP_HEAD) { output->append(html); downstream->response_sent_body_length += html.size(); } downstream->set_response_state(DownstreamState::MSG_COMPLETE); } void HttpsUpstream::attach_downstream(std::unique_ptr downstream) { assert(!downstream_); downstream_ = std::move(downstream); } void HttpsUpstream::delete_downstream() { if (downstream_ && downstream_->accesslog_ready()) { handler_->write_accesslog(downstream_.get()); } downstream_.reset(); } Downstream *HttpsUpstream::get_downstream() const { return downstream_.get(); } std::unique_ptr HttpsUpstream::pop_downstream() { return std::unique_ptr(downstream_.release()); } int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { if (LOG_ENABLED(INFO)) { if (downstream->get_non_final_response()) { DLOG(INFO, downstream) << "HTTP non-final response header"; } else { DLOG(INFO, downstream) << "HTTP response header completed"; } } const auto &req = downstream->request(); auto &resp = downstream->response(); auto &balloc = downstream->get_block_allocator(); auto dconn = downstream->get_downstream_connection(); // dconn might be nullptr if this is non-final response from mruby. if (downstream->get_non_final_response() && !downstream->supports_non_final_response()) { resp.fs.clear_headers(); return 0; } #ifdef HAVE_MRUBY if (!downstream->get_non_final_response()) { assert(dconn); const auto &group = dconn->get_downstream_addr_group(); if (group) { const auto &dmruby_ctx = group->shared_addr->mruby_ctx; if (dmruby_ctx->run_on_response_proc(downstream) != 0) { error_reply(500); return -1; } if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { return -1; } } auto worker = handler_->get_worker(); auto mruby_ctx = worker->get_mruby_context(); if (mruby_ctx->run_on_response_proc(downstream) != 0) { error_reply(500); return -1; } if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { return -1; } } #endif // defined(HAVE_MRUBY) auto connect_method = req.method == HTTP_CONNECT; auto buf = downstream->get_response_buf(); buf->append("HTTP/"sv); buf->append('0' + static_cast(req.http_major)); buf->append('.'); buf->append('0' + static_cast(req.http_minor)); buf->append(' '); if (req.connect_proto != ConnectProto::NONE && downstream->get_upgraded()) { buf->append("101 Switching Protocols"sv); } else { buf->append(http2::stringify_status(balloc, resp.http_status)); buf->append(' '); buf->append(http2::get_reason_phrase(resp.http_status)); } buf->append("\r\n"sv); auto config = get_config(); auto &httpconf = config->http; if (!config->http2_proxy && !httpconf.no_location_rewrite) { downstream->rewrite_location_response_header( get_client_handler()->get_upstream_scheme()); } if (downstream->get_non_final_response()) { http2::build_http1_headers_from_headers(buf, resp.fs.headers(), http2::HDOP_STRIP_ALL); buf->append("\r\n"sv); if (LOG_ENABLED(INFO)) { log_response_headers(buf); } resp.fs.clear_headers(); return 0; } auto build_flags = static_cast((http2::HDOP_STRIP_ALL & ~http2::HDOP_STRIP_VIA) | (!http2::legacy_http1(req.http_major, req.http_minor) ? 0 : http2::HDOP_STRIP_TRANSFER_ENCODING)); http2::build_http1_headers_from_headers(buf, resp.fs.headers(), build_flags); auto worker = handler_->get_worker(); // after graceful shutdown commenced, add connection: close header // field. If CONNECT request failed, close the connection. if (httpconf.max_requests <= num_requests_ || worker->get_graceful_shutdown() || (connect_method && !downstream->get_upgraded())) { resp.connection_close = true; } // We check downstream->get_response_connection_close() in case when // the Content-Length is not available. if (!req.connection_close && !resp.connection_close) { if (req.http_major <= 0 || req.http_minor <= 0) { // We add this header for HTTP/1.0 or HTTP/0.9 clients buf->append("Connection: Keep-Alive\r\n"sv); } } else if (!downstream->get_upgraded()) { buf->append("Connection: close\r\n"sv); } if (!connect_method && downstream->get_upgraded()) { if (req.connect_proto == ConnectProto::WEBSOCKET && resp.http_status / 100 == 2) { buf->append("Upgrade: websocket\r\nConnection: Upgrade\r\n"sv); auto key = req.fs.header(http2::HD_SEC_WEBSOCKET_KEY); if (!key || key->value.size() != base64::encode_length(16)) { return -1; } std::array out; auto accept = http2::make_websocket_accept_token(out.data(), key->value); if (accept.empty()) { return -1; } buf->append("Sec-WebSocket-Accept: "sv); buf->append(accept); buf->append("\r\n"sv); } else { auto connection = resp.fs.header(http2::HD_CONNECTION); if (connection) { buf->append("Connection: "sv); buf->append((*connection).value); buf->append("\r\n"sv); } auto upgrade = resp.fs.header(http2::HD_UPGRADE); if (upgrade) { buf->append("Upgrade: "sv); buf->append((*upgrade).value); buf->append("\r\n"sv); } } } if (!resp.fs.header(http2::HD_ALT_SVC)) { // We won't change or alter alt-svc from backend for now if (!httpconf.altsvcs.empty()) { buf->append("Alt-Svc: "sv); buf->append(httpconf.altsvc_header_value); buf->append("\r\n"sv); } } if (!config->http2_proxy && !httpconf.no_server_rewrite) { buf->append("Server: "sv); buf->append(httpconf.server_name); buf->append("\r\n"sv); } else { auto server = resp.fs.header(http2::HD_SERVER); if (server) { buf->append("Server: "sv); buf->append((*server).value); buf->append("\r\n"sv); } } if (req.method != HTTP_CONNECT || !downstream->get_upgraded()) { auto affinity_cookie = downstream->get_affinity_cookie_to_send(); if (affinity_cookie) { auto &group = dconn->get_downstream_addr_group(); auto &shared_addr = group->shared_addr; auto &cookieconf = shared_addr->affinity.cookie; auto secure = http::require_cookie_secure_attribute(cookieconf.secure, req.scheme); auto cookie_str = http::create_affinity_cookie( balloc, cookieconf.name, affinity_cookie, cookieconf.path, secure); buf->append("Set-Cookie: "sv); buf->append(cookie_str); buf->append("\r\n"sv); } } auto via = resp.fs.header(http2::HD_VIA); if (httpconf.no_via) { if (via) { buf->append("Via: "sv); buf->append((*via).value); buf->append("\r\n"sv); } } else { buf->append("Via: "sv); if (via) { buf->append((*via).value); buf->append(", "sv); } buf->append(16, std::bind_front(http::ViaValueGenerator{}, resp.http_major, resp.http_minor)); buf->append("\r\n"sv); } for (auto &p : httpconf.add_response_headers) { buf->append(p.name); buf->append(": "sv); buf->append(p.value); buf->append("\r\n"sv); } buf->append("\r\n"sv); if (LOG_ENABLED(INFO)) { log_response_headers(buf); } return 0; } int HttpsUpstream::on_downstream_body(Downstream *downstream, const uint8_t *data, size_t len, bool flush) { if (len == 0) { return 0; } auto output = downstream->get_response_buf(); if (downstream->get_chunked_response()) { output->append(sizeof(len) * 2, std::bind_front(util::CompactHexFormatter{}, len)); output->append("\r\n"sv); } output->append(data, len); downstream->response_sent_body_length += len; if (downstream->get_chunked_response()) { output->append("\r\n"sv); } return 0; } int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) { const auto &req = downstream->request(); auto &resp = downstream->response(); if (downstream->get_chunked_response()) { auto output = downstream->get_response_buf(); const auto &trailers = resp.fs.trailers(); if (trailers.empty()) { output->append("0\r\n\r\n"sv); } else { output->append("0\r\n"sv); http2::build_http1_headers_from_headers(output, trailers, http2::HDOP_STRIP_ALL); output->append("\r\n"sv); } } if (LOG_ENABLED(INFO)) { DLOG(INFO, downstream) << "HTTP response completed"; } if (!downstream->validate_response_recv_body_length()) { resp.connection_close = true; } if (req.connection_close || resp.connection_close || // To avoid to stall upload body downstream->get_request_state() != DownstreamState::MSG_COMPLETE) { auto handler = get_client_handler(); handler->set_should_close_after_write(true); } return 0; } int HttpsUpstream::on_downstream_abort_request(Downstream *downstream, unsigned int status_code) { error_reply(status_code); handler_->signal_write(); return 0; } int HttpsUpstream::on_downstream_abort_request_with_https_redirect( Downstream *downstream) { redirect_to_https(downstream); handler_->signal_write(); return 0; } int HttpsUpstream::redirect_to_https(Downstream *downstream) { auto &req = downstream->request(); if (req.method == HTTP_CONNECT || req.scheme != "http"sv || req.authority.empty()) { error_reply(400); return 0; } auto authority = util::extract_host(req.authority); if (authority.empty()) { error_reply(400); return 0; } auto &balloc = downstream->get_block_allocator(); auto config = get_config(); auto &httpconf = config->http; std::string_view loc; if (httpconf.redirect_https_port == "443"sv) { loc = concat_string_ref(balloc, "https://"sv, authority, req.path); } else { loc = concat_string_ref(balloc, "https://"sv, authority, ":"sv, httpconf.redirect_https_port, req.path); } auto &resp = downstream->response(); resp.http_status = 308; resp.fs.add_header_token("location"sv, loc, false, http2::HD_LOCATION); resp.fs.add_header_token("connection"sv, "close"sv, false, http2::HD_CONNECTION); return send_reply(downstream, nullptr, 0); } void HttpsUpstream::log_response_headers(DefaultMemchunks *buf) const { std::string nhdrs; for (auto chunk = buf->head; chunk; chunk = chunk->next) { nhdrs.append(chunk->pos, chunk->last); } if (log_config()->errorlog_tty) { nhdrs = http::colorize_headers(nhdrs); } ULOG(INFO, this) << "HTTP response headers\n" << nhdrs; } void HttpsUpstream::on_handler_delete() { if (downstream_ && downstream_->accesslog_ready()) { handler_->write_accesslog(downstream_.get()); } } int HttpsUpstream::on_downstream_reset(Downstream *downstream, bool no_retry) { int rv; std::unique_ptr dconn; assert(downstream == downstream_.get()); downstream_->pop_downstream_connection(); if (!downstream_->request_submission_ready()) { switch (downstream_->get_response_state()) { case DownstreamState::MSG_COMPLETE: // We have got all response body already. Send it off. return 0; case DownstreamState::INITIAL: if (on_downstream_abort_request(downstream_.get(), 502) != 0) { return -1; } return 0; default: break; } // Return error so that caller can delete handler return -1; } downstream_->add_retry(); rv = 0; if (no_retry || downstream_->no_more_retry()) { goto fail; } for (;;) { auto dconn = handler_->get_downstream_connection(rv, downstream_.get()); if (!dconn) { goto fail; } rv = downstream_->attach_downstream_connection(std::move(dconn)); if (rv == 0) { break; } } rv = downstream_->push_request_headers(); if (rv != 0) { goto fail; } return 0; fail: if (rv == SHRPX_ERR_TLS_REQUIRED) { rv = on_downstream_abort_request_with_https_redirect(downstream); } else { rv = on_downstream_abort_request(downstream_.get(), 502); } if (rv != 0) { return -1; } downstream_->pop_downstream_connection(); return 0; } int HttpsUpstream::initiate_push(Downstream *downstream, const std::string_view &uri) { return 0; } int HttpsUpstream::response_riovec(struct iovec *iov, int iovcnt) const { if (!downstream_) { return 0; } auto buf = downstream_->get_response_buf(); return buf->riovec(iov, iovcnt); } void HttpsUpstream::response_drain(size_t n) { if (!downstream_) { return; } auto buf = downstream_->get_response_buf(); buf->drain(n); } bool HttpsUpstream::response_empty() const { if (!downstream_) { return true; } auto buf = downstream_->get_response_buf(); return buf->rleft() == 0; } Downstream * HttpsUpstream::on_downstream_push_promise(Downstream *downstream, int32_t promised_stream_id) { return nullptr; } int HttpsUpstream::on_downstream_push_promise_complete( Downstream *downstream, Downstream *promised_downstream) { return -1; } bool HttpsUpstream::push_enabled() const { return false; } void HttpsUpstream::cancel_premature_downstream( Downstream *promised_downstream) {} } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/HttpServer.h0000644000000000000000000000013215077107270015326 xustar0030 mtime=1761382072.983444185 30 atime=1761382106.240310268 30 ctime=1761382109.237299985 nghttp2-1.68.0/src/HttpServer.h0000644000175100017510000001554115077107270015724 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef HTTP_SERVER_H #define HTTP_SERVER_H #include "nghttp2_config.h" #include #include #include #include #include #include #include #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include #define NGHTTP2_NO_SSIZE_T #include #include "http2.h" #include "buffer.h" #include "template.h" #include "allocator.h" namespace nghttp2 { struct Config { std::unordered_map> push; std::unordered_map mime_types; Headers trailer; std::string trailer_names; std::string htdocs; std::string host; std::string private_key_file; std::string cert_file; std::string dh_param_file; std::string address; std::string mime_types_file; std::string_view groups; ev_tstamp stream_read_timeout; ev_tstamp stream_write_timeout; void *data_ptr; size_t padding; size_t num_worker; size_t max_concurrent_streams; ssize_t header_table_size; ssize_t encoder_header_table_size; int window_bits; int connection_window_bits; uint16_t port; bool verbose; bool daemon; bool verify_client; bool no_tls; bool error_gzip; bool early_response; bool hexdump; bool echo_upload; bool no_content_length; bool ktls; Config(); ~Config(); }; class Http2Handler; struct FileEntry { FileEntry(std::string path, int64_t length, int64_t mtime, int fd, const std::string *content_type, const std::chrono::steady_clock::time_point &last_valid, bool stale = false) : path(std::move(path)), length(length), mtime(mtime), last_valid(last_valid), content_type(content_type), dlnext(nullptr), dlprev(nullptr), fd(fd), usecount(1), stale(stale) {} std::string path; std::unordered_multimap>::iterator it; int64_t length; int64_t mtime; std::chrono::steady_clock::time_point last_valid; const std::string *content_type; FileEntry *dlnext, *dlprev; int fd; int usecount; bool stale; }; struct RequestHeader { std::string_view method; std::string_view scheme; std::string_view authority; std::string_view host; std::string_view path; std::string_view ims; std::string_view expect; struct { nghttp2_rcbuf *method; nghttp2_rcbuf *scheme; nghttp2_rcbuf *authority; nghttp2_rcbuf *host; nghttp2_rcbuf *path; nghttp2_rcbuf *ims; nghttp2_rcbuf *expect; } rcbuf; }; struct Stream { BlockAllocator balloc; RequestHeader header; Http2Handler *handler; FileEntry *file_ent; ev_timer rtimer; ev_timer wtimer; int64_t body_length; int64_t body_offset; // Total amount of bytes (sum of name and value length) used in // headers. size_t header_buffer_size; int32_t stream_id; bool echo_upload; Stream(Http2Handler *handler, int32_t stream_id); ~Stream(); }; class Sessions; class Http2Handler { public: Http2Handler(Sessions *sessions, int fd, SSL *ssl, int64_t session_id); ~Http2Handler(); void remove_self(); void start_settings_timer(); int on_read(); int on_write(); int connection_made(); int verify_alpn_result(); int submit_file_response(const std::string_view &status, Stream *stream, time_t last_modified, off_t file_length, const std::string *content_type, nghttp2_data_provider2 *data_prd); int submit_response(const std::string_view &status, int32_t stream_id, nghttp2_data_provider2 *data_prd); int submit_response(const std::string_view &status, int32_t stream_id, const HeaderRefs &headers, nghttp2_data_provider2 *data_prd); int submit_non_final_response(const std::string &status, int32_t stream_id); int submit_push_promise(Stream *stream, const std::string_view &push_path); int submit_rst_stream(Stream *stream, uint32_t error_code); void add_stream(int32_t stream_id, std::unique_ptr stream); void remove_stream(int32_t stream_id); Stream *get_stream(int32_t stream_id); int64_t session_id() const; Sessions *get_sessions() const; const Config *get_config() const; void remove_settings_timer(); void terminate_session(uint32_t error_code); int fill_wb(); int read_clear(); int write_clear(); int tls_handshake(); int read_tls(); int write_tls(); struct ev_loop *get_loop() const; using WriteBuf = Buffer<64_k>; WriteBuf *get_wb(); private: ev_io wev_; ev_io rev_; ev_timer settings_timerev_; std::unordered_map> id2stream_; WriteBuf wb_; std::function read_, write_; int64_t session_id_; nghttp2_session *session_; Sessions *sessions_; SSL *ssl_; const uint8_t *data_pending_; size_t data_pendinglen_; int fd_; }; struct StatusPage { std::string status; FileEntry file_ent; }; class HttpServer { public: HttpServer(const Config *config); int listen(); int run(); const Config *get_config() const; const StatusPage *get_status_page(int status) const; private: std::vector status_pages_; const Config *config_; }; nghttp2_ssize file_read_callback(nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *user_data); } // namespace nghttp2 #endif // !defined(HTTP_SERVER_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_connection_handler.cc0000644000000000000000000000013215077107270020436 xustar0030 mtime=1761382072.992444143 30 atime=1761382106.084310955 30 ctime=1761382109.079300442 nghttp2-1.68.0/src/shrpx_connection_handler.cc0000644000175100017510000005622715077107270021042 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_connection_handler.h" #ifdef HAVE_UNISTD_H # include #endif // defined(HAVE_UNISTD_H) #include #include #include #include #include #include "shrpx_client_handler.h" #include "shrpx_tls.h" #include "shrpx_worker.h" #include "shrpx_config.h" #include "shrpx_http2_session.h" #include "shrpx_connect_blocker.h" #include "shrpx_downstream_connection.h" #include "shrpx_memcached_dispatcher.h" #include "shrpx_signal.h" #include "shrpx_log.h" #include "xsi_strerror.h" #include "util.h" #include "template.h" #include "ssl_compat.h" using namespace nghttp2; namespace shrpx { namespace { void thread_join_async_cb(struct ev_loop *loop, ev_async *w, int revent) { ev_break(loop); } } // namespace namespace { void serial_event_async_cb(struct ev_loop *loop, ev_async *w, int revent) { auto h = static_cast(w->data); h->handle_serial_event(); } } // namespace ConnectionHandler::ConnectionHandler(struct ev_loop *loop, std::mt19937 &gen) : #ifdef ENABLE_HTTP3 quic_ipc_fd_(-1), #endif // defined(ENABLE_HTTP3) gen_(gen), single_worker_(nullptr), loop_(loop), #ifdef HAVE_NEVERBLEED nb_(nullptr), #endif // defined(HAVE_NEVERBLEED) tls_ticket_key_memcached_get_retry_count_(0), tls_ticket_key_memcached_fail_count_(0), worker_round_robin_cnt_(get_config()->api.enabled ? 1 : 0), graceful_shutdown_(false) { ev_async_init(&thread_join_asyncev_, thread_join_async_cb); ev_async_init(&serial_event_asyncev_, serial_event_async_cb); serial_event_asyncev_.data = this; ev_async_start(loop_, &serial_event_asyncev_); } ConnectionHandler::~ConnectionHandler() { ev_async_stop(loop_, &serial_event_asyncev_); ev_async_stop(loop_, &thread_join_asyncev_); #ifdef ENABLE_HTTP3 for (auto ssl_ctx : quic_all_ssl_ctx_) { if (ssl_ctx == nullptr) { continue; } auto tls_ctx_data = static_cast(SSL_CTX_get_app_data(ssl_ctx)); delete tls_ctx_data; SSL_CTX_free(ssl_ctx); } #endif // defined(ENABLE_HTTP3) for (auto ssl_ctx : all_ssl_ctx_) { auto tls_ctx_data = static_cast(SSL_CTX_get_app_data(ssl_ctx)); delete tls_ctx_data; SSL_CTX_free(ssl_ctx); } // Free workers before destroying ev_loop workers_.clear(); for (auto loop : worker_loops_) { ev_loop_destroy(loop); } } void ConnectionHandler::set_ticket_keys_to_worker( const std::shared_ptr &ticket_keys) { for (auto &worker : workers_) { worker->set_ticket_keys(ticket_keys); } } void ConnectionHandler::worker_reopen_log_files() { for (auto &worker : workers_) { worker->send(WorkerEvent{ .type = WorkerEventType::REOPEN_LOG, }); } } void ConnectionHandler::worker_replace_downstream( std::shared_ptr downstreamconf) { for (auto &worker : workers_) { worker->send(WorkerEvent{ .type = WorkerEventType::REPLACE_DOWNSTREAM, .downstreamconf = downstreamconf, }); } } int ConnectionHandler::create_single_worker() { cert_tree_ = tls::create_cert_lookup_tree(); auto sv_ssl_ctx = tls::setup_server_ssl_context( all_ssl_ctx_, indexed_ssl_ctx_, cert_tree_.get() #ifdef HAVE_NEVERBLEED , nb_ #endif // defined(HAVE_NEVERBLEED) ); #ifdef ENABLE_HTTP3 quic_cert_tree_ = tls::create_cert_lookup_tree(); auto quic_sv_ssl_ctx = tls::setup_quic_server_ssl_context( quic_all_ssl_ctx_, quic_indexed_ssl_ctx_, quic_cert_tree_.get() # ifdef HAVE_NEVERBLEED , nb_ # endif // defined(HAVE_NEVERBLEED) ); #endif // defined(ENABLE_HTTP3) auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context( #ifdef HAVE_NEVERBLEED nb_ #endif // defined(HAVE_NEVERBLEED) ); if (cl_ssl_ctx) { all_ssl_ctx_.push_back(cl_ssl_ctx); #ifdef ENABLE_HTTP3 quic_all_ssl_ctx_.push_back(nullptr); #endif // defined(ENABLE_HTTP3) } auto config = get_config(); #if defined(ENABLE_HTTP3) && defined(HAVE_LIBBPF) quic_bpf_refs_.resize(config->conn.quic_listener.addrs.size()); #endif // defined(ENABLE_HTTP3) && defined(HAVE_LIBBPF) #ifdef ENABLE_HTTP3 assert(worker_ids_.size() == 1); const auto &wid = worker_ids_[0]; #endif // defined(ENABLE_HTTP3) single_worker_ = std::make_unique( loop_, sv_ssl_ctx, cl_ssl_ctx, cert_tree_.get(), #ifdef ENABLE_HTTP3 quic_sv_ssl_ctx, quic_cert_tree_.get(), wid, #endif // defined(ENABLE_HTTP3) /* index = */ 0, ticket_keys_, this, config->conn.downstream); #ifdef HAVE_MRUBY if (single_worker_->create_mruby_context() != 0) { return -1; } #endif // defined(HAVE_MRUBY) if (single_worker_->setup_server_socket() != 0) { return -1; } #ifdef ENABLE_HTTP3 if (single_worker_->setup_quic_server_socket() != 0) { return -1; } #endif // defined(ENABLE_HTTP3) return 0; } int ConnectionHandler::create_worker_thread(size_t num) { #ifndef NOTHREADS assert(workers_.size() == 0); cert_tree_ = tls::create_cert_lookup_tree(); auto sv_ssl_ctx = tls::setup_server_ssl_context( all_ssl_ctx_, indexed_ssl_ctx_, cert_tree_.get() # ifdef HAVE_NEVERBLEED , nb_ # endif // defined(HAVE_NEVERBLEED) ); # ifdef ENABLE_HTTP3 quic_cert_tree_ = tls::create_cert_lookup_tree(); auto quic_sv_ssl_ctx = tls::setup_quic_server_ssl_context( quic_all_ssl_ctx_, quic_indexed_ssl_ctx_, quic_cert_tree_.get() # ifdef HAVE_NEVERBLEED , nb_ # endif // defined(HAVE_NEVERBLEED) ); # endif // defined(ENABLE_HTTP3) auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context( # ifdef HAVE_NEVERBLEED nb_ # endif // defined(HAVE_NEVERBLEED) ); if (cl_ssl_ctx) { all_ssl_ctx_.push_back(cl_ssl_ctx); # ifdef ENABLE_HTTP3 quic_all_ssl_ctx_.push_back(nullptr); # endif // defined(ENABLE_HTTP3) } auto config = get_config(); auto &apiconf = config->api; # if defined(ENABLE_HTTP3) && defined(HAVE_LIBBPF) quic_bpf_refs_.resize(config->conn.quic_listener.addrs.size()); # endif // defined(ENABLE_HTTP3) && defined(HAVE_LIBBPF) // We have dedicated worker for API request processing. if (apiconf.enabled) { ++num; } # ifdef ENABLE_HTTP3 assert(worker_ids_.size() == num); # endif // defined(ENABLE_HTTP3) for (size_t i = 0; i < num; ++i) { auto loop = ev_loop_new(config->ev_loop_flags); # ifdef ENABLE_HTTP3 const auto &wid = worker_ids_[i]; # endif // defined(ENABLE_HTTP3) auto worker = std::make_unique(loop, sv_ssl_ctx, cl_ssl_ctx, cert_tree_.get(), # ifdef ENABLE_HTTP3 quic_sv_ssl_ctx, quic_cert_tree_.get(), wid, # endif // defined(ENABLE_HTTP3) i, ticket_keys_, this, config->conn.downstream); # ifdef HAVE_MRUBY if (worker->create_mruby_context() != 0) { return -1; } # endif // defined(HAVE_MRUBY) if (worker->setup_server_socket() != 0) { return -1; } # ifdef ENABLE_HTTP3 if ((!apiconf.enabled || i != 0) && worker->setup_quic_server_socket() != 0) { return -1; } # endif // defined(ENABLE_HTTP3) workers_.push_back(std::move(worker)); worker_loops_.push_back(loop); LLOG(NOTICE, this) << "Created worker thread #" << workers_.size() - 1; } for (auto &worker : workers_) { worker->run_async(); } #endif // !defined(NOTHREADS) return 0; } void ConnectionHandler::join_worker() { #ifndef NOTHREADS int n = 0; if (LOG_ENABLED(INFO)) { LLOG(INFO, this) << "Waiting for worker thread to join: n=" << workers_.size(); } for (auto &worker : workers_) { worker->wait(); if (LOG_ENABLED(INFO)) { LLOG(INFO, this) << "Thread #" << n << " joined"; } ++n; } #endif // !defined(NOTHREADS) } void ConnectionHandler::graceful_shutdown_worker() { if (single_worker_) { return; } if (LOG_ENABLED(INFO)) { LLOG(INFO, this) << "Sending graceful shutdown signal to worker"; } for (auto &worker : workers_) { worker->send(WorkerEvent{ .type = WorkerEventType::GRACEFUL_SHUTDOWN, }); } #ifndef NOTHREADS ev_async_start(loop_, &thread_join_asyncev_); thread_join_fut_ = std::async(std::launch::async, [this]() { (void)reopen_log_files(get_config()->logging); join_worker(); ev_async_send(get_loop(), &thread_join_asyncev_); }); #endif // !defined(NOTHREADS) } struct ev_loop *ConnectionHandler::get_loop() const { return loop_; } Worker *ConnectionHandler::get_single_worker() const { return single_worker_.get(); } void ConnectionHandler::set_ticket_keys( std::shared_ptr ticket_keys) { ticket_keys_ = std::move(ticket_keys); if (single_worker_) { single_worker_->set_ticket_keys(ticket_keys_); } } const std::shared_ptr &ConnectionHandler::get_ticket_keys() const { return ticket_keys_; } void ConnectionHandler::set_graceful_shutdown(bool f) { graceful_shutdown_ = f; if (single_worker_) { single_worker_->set_graceful_shutdown(f); } } bool ConnectionHandler::get_graceful_shutdown() const { return graceful_shutdown_; } void ConnectionHandler::set_tls_ticket_key_memcached_dispatcher( std::unique_ptr dispatcher) { tls_ticket_key_memcached_dispatcher_ = std::move(dispatcher); } MemcachedDispatcher * ConnectionHandler::get_tls_ticket_key_memcached_dispatcher() const { return tls_ticket_key_memcached_dispatcher_.get(); } // Use the similar backoff algorithm described in // https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md namespace { constexpr size_t MAX_BACKOFF_EXP = 10; constexpr auto MULTIPLIER = 3.2; constexpr auto JITTER = 0.2; } // namespace void ConnectionHandler::on_tls_ticket_key_network_error(ev_timer *w) { if (++tls_ticket_key_memcached_get_retry_count_ >= get_config()->tls.ticket.memcached.max_retry) { LOG(WARN) << "Memcached: tls ticket get retry all failed " << tls_ticket_key_memcached_get_retry_count_ << " times."; on_tls_ticket_key_not_found(w); return; } auto base_backoff = util::int_pow( MULTIPLIER, std::min(MAX_BACKOFF_EXP, tls_ticket_key_memcached_get_retry_count_)); auto dist = std::uniform_real_distribution<>(-JITTER * base_backoff, JITTER * base_backoff); auto backoff = base_backoff + dist(gen_); LOG(WARN) << "Memcached: tls ticket get failed due to network error, retrying in " << backoff << " seconds"; ev_timer_set(w, backoff, 0.); ev_timer_start(loop_, w); } void ConnectionHandler::on_tls_ticket_key_not_found(ev_timer *w) { tls_ticket_key_memcached_get_retry_count_ = 0; if (++tls_ticket_key_memcached_fail_count_ >= get_config()->tls.ticket.memcached.max_fail) { LOG(WARN) << "Memcached: could not get tls ticket; disable tls ticket"; tls_ticket_key_memcached_fail_count_ = 0; set_ticket_keys(nullptr); set_ticket_keys_to_worker(nullptr); } LOG(WARN) << "Memcached: tls ticket get failed, schedule next"; schedule_next_tls_ticket_key_memcached_get(w); } void ConnectionHandler::on_tls_ticket_key_get_success( const std::shared_ptr &ticket_keys, ev_timer *w) { LOG(NOTICE) << "Memcached: tls ticket get success"; tls_ticket_key_memcached_get_retry_count_ = 0; tls_ticket_key_memcached_fail_count_ = 0; schedule_next_tls_ticket_key_memcached_get(w); if (!ticket_keys || ticket_keys->keys.empty()) { LOG(WARN) << "Memcached: tls ticket keys are empty; tls ticket disabled"; set_ticket_keys(nullptr); set_ticket_keys_to_worker(nullptr); return; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "ticket keys get done"; LOG(INFO) << 0 << " enc+dec: " << util::format_hex(ticket_keys->keys[0].data.name); for (size_t i = 1; i < ticket_keys->keys.size(); ++i) { auto &key = ticket_keys->keys[i]; LOG(INFO) << i << " dec: " << util::format_hex(key.data.name); } } set_ticket_keys(ticket_keys); set_ticket_keys_to_worker(ticket_keys); } void ConnectionHandler::schedule_next_tls_ticket_key_memcached_get( ev_timer *w) { ev_timer_set(w, get_config()->tls.ticket.memcached.interval, 0.); ev_timer_start(loop_, w); } SSL_CTX *ConnectionHandler::create_tls_ticket_key_memcached_ssl_ctx() { auto config = get_config(); auto &tlsconf = config->tls; auto &memcachedconf = config->tls.ticket.memcached; auto ssl_ctx = tls::create_ssl_client_context( #ifdef HAVE_NEVERBLEED nb_, #endif // defined(HAVE_NEVERBLEED) tlsconf.cacert, memcachedconf.cert_file, memcachedconf.private_key_file); all_ssl_ctx_.push_back(ssl_ctx); #ifdef ENABLE_HTTP3 quic_all_ssl_ctx_.push_back(nullptr); #endif // defined(ENABLE_HTTP3) return ssl_ctx; } #ifdef HAVE_NEVERBLEED void ConnectionHandler::set_neverbleed(neverbleed_t *nb) { nb_ = nb; } #endif // defined(HAVE_NEVERBLEED) void ConnectionHandler::handle_serial_event() { std::vector q; { std::lock_guard g(serial_event_mu_); q.swap(serial_events_); } for (auto &sev : q) { switch (sev.type) { case SerialEventType::REPLACE_DOWNSTREAM: // Mmake sure that none of worker uses // get_config()->conn.downstream mod_config()->conn.downstream = sev.downstreamconf; if (single_worker_) { single_worker_->replace_downstream_config(sev.downstreamconf); break; } worker_replace_downstream(sev.downstreamconf); break; default: break; } } } void ConnectionHandler::send_replace_downstream( const std::shared_ptr &downstreamconf) { send_serial_event( SerialEvent(SerialEventType::REPLACE_DOWNSTREAM, downstreamconf)); } void ConnectionHandler::send_serial_event(SerialEvent ev) { { std::lock_guard g(serial_event_mu_); serial_events_.push_back(std::move(ev)); } ev_async_send(loop_, &serial_event_asyncev_); } SSL_CTX *ConnectionHandler::get_ssl_ctx(size_t idx) const { return all_ssl_ctx_[idx]; } const std::vector & ConnectionHandler::get_indexed_ssl_ctx(size_t idx) const { return indexed_ssl_ctx_[idx]; } #ifdef ENABLE_HTTP3 const std::vector & ConnectionHandler::get_quic_indexed_ssl_ctx(size_t idx) const { return quic_indexed_ssl_ctx_[idx]; } #endif // defined(ENABLE_HTTP3) #ifdef ENABLE_HTTP3 int ConnectionHandler::forward_quic_packet(const UpstreamAddr *faddr, const Address &remote_addr, const Address &local_addr, const ngtcp2_pkt_info &pi, const WorkerID &wid, std::span data) { assert(!get_config()->single_thread); auto worker = find_worker(wid); if (worker == nullptr) { return -1; } worker->send(WorkerEvent{ .type = WorkerEventType::QUIC_PKT_FORWARD, .quic_pkt = std::make_unique(faddr->index, remote_addr, local_addr, pi, data), }); return 0; } void ConnectionHandler::set_quic_keying_materials( std::shared_ptr qkms) { quic_keying_materials_ = std::move(qkms); } const std::shared_ptr & ConnectionHandler::get_quic_keying_materials() const { return quic_keying_materials_; } void ConnectionHandler::set_worker_ids(std::vector worker_ids) { worker_ids_ = std::move(worker_ids); } namespace { ssize_t find_worker_index(const std::vector &worker_ids, const WorkerID &wid) { assert(!worker_ids.empty()); if (wid.server != worker_ids[0].server || wid.worker_process != worker_ids[0].worker_process || wid.thread >= worker_ids.size()) { return -1; } return wid.thread; } } // namespace Worker *ConnectionHandler::find_worker(const WorkerID &wid) const { auto idx = find_worker_index(worker_ids_, wid); if (idx == -1) { return nullptr; } return workers_[as_unsigned(idx)].get(); } QUICLingeringWorkerProcess * ConnectionHandler::match_quic_lingering_worker_process_worker_id( const WorkerID &wid) { for (auto &lwps : quic_lingering_worker_processes_) { if (find_worker_index(lwps.worker_ids, wid) != -1) { return &lwps; } } return nullptr; } # ifdef HAVE_LIBBPF std::vector &ConnectionHandler::get_quic_bpf_refs() { return quic_bpf_refs_; } void ConnectionHandler::unload_bpf_objects() { LOG(NOTICE) << "Unloading BPF objects"; for (auto &ref : quic_bpf_refs_) { if (ref.obj == nullptr) { continue; } bpf_object__close(ref.obj); ref.obj = nullptr; } } # endif // defined(HAVE_LIBBPF) void ConnectionHandler::set_quic_ipc_fd(int fd) { quic_ipc_fd_ = fd; } void ConnectionHandler::set_quic_lingering_worker_processes( const std::vector &quic_lwps) { quic_lingering_worker_processes_ = quic_lwps; } int ConnectionHandler::forward_quic_packet_to_lingering_worker_process( QUICLingeringWorkerProcess *quic_lwp, const Address &remote_addr, const Address &local_addr, const ngtcp2_pkt_info &pi, std::span data) { std::array header; assert(header.size() >= 1 + 1 + 1 + 1 + sizeof(sockaddr_storage) * 2); assert(remote_addr.len > 0); assert(local_addr.len > 0); auto p = header.data(); *p++ = static_cast(QUICIPCType::DGRAM_FORWARD); *p++ = static_cast(remote_addr.len - 1); p = std::ranges::copy_n(reinterpret_cast(&remote_addr.su), as_signed(remote_addr.len), p) .out; *p++ = static_cast(local_addr.len - 1); p = std::ranges::copy_n(reinterpret_cast(&local_addr.su), as_signed(local_addr.len), p) .out; *p++ = pi.ecn; iovec msg_iov[] = { { .iov_base = header.data(), .iov_len = static_cast(p - header.data()), }, { .iov_base = const_cast(data.data()), .iov_len = data.size(), }, }; msghdr msg{ .msg_iov = msg_iov, .msg_iovlen = array_size(msg_iov), }; ssize_t nwrite; while ((nwrite = sendmsg(quic_lwp->quic_ipc_fd, &msg, 0)) == -1 && errno == EINTR) ; if (nwrite == -1) { std::array errbuf; auto error = errno; LOG(ERROR) << "Failed to send QUIC IPC message: " << xsi_strerror(error, errbuf.data(), errbuf.size()); return -1; } return 0; } int ConnectionHandler::quic_ipc_read() { std::array buf; ssize_t nread; while ((nread = recv(quic_ipc_fd_, buf.data(), buf.size(), 0)) == -1 && errno == EINTR) ; if (nread == -1) { std::array errbuf; auto error = errno; LOG(ERROR) << "Failed to read data from QUIC IPC channel: " << xsi_strerror(error, errbuf.data(), errbuf.size()); return -1; } if (nread == 0) { return 0; } size_t len = 1 + 1 + 1 + 1; // Wire format: // TYPE(1) REMOTE_ADDRLEN(1) REMOTE_ADDR(N) LOCAL_ADDRLEN(1) LOCAL_ADDR(N) // ECN(1) DGRAM_PAYLOAD(N) // // When encoding, REMOTE_ADDRLEN and LOCAL_ADDRLEN are decremented // by 1. if (static_cast(nread) < len) { return 0; } auto p = buf.data(); if (*p != static_cast(QUICIPCType::DGRAM_FORWARD)) { LOG(ERROR) << "Unknown QUICIPCType: " << static_cast(*p); return -1; } ++p; auto pkt = std::make_unique(); auto remote_addrlen = static_cast(*p++) + 1; if (remote_addrlen > sizeof(sockaddr_storage)) { LOG(ERROR) << "The length of remote address is too large: " << remote_addrlen; return -1; } len += remote_addrlen; if (static_cast(nread) < len) { LOG(ERROR) << "Insufficient QUIC IPC message length"; return -1; } pkt->remote_addr.len = remote_addrlen; memcpy(&pkt->remote_addr.su, p, remote_addrlen); p += remote_addrlen; auto local_addrlen = static_cast(*p++) + 1; if (local_addrlen > sizeof(sockaddr_storage)) { LOG(ERROR) << "The length of local address is too large: " << local_addrlen; return -1; } len += local_addrlen; if (static_cast(nread) < len) { LOG(ERROR) << "Insufficient QUIC IPC message length"; return -1; } pkt->local_addr.len = local_addrlen; memcpy(&pkt->local_addr.su, p, local_addrlen); p += local_addrlen; pkt->pi.ecn = *p++; auto datalen = static_cast(nread - (p - buf.data())); pkt->data.assign(p, p + datalen); // At the moment, UpstreamAddr index is unknown. pkt->upstream_addr_index = static_cast(-1); ngtcp2_version_cid vc; auto rv = ngtcp2_pkt_decode_version_cid(&vc, p, datalen, SHRPX_QUIC_SCIDLEN); if (rv < 0) { LOG(ERROR) << "ngtcp2_pkt_decode_version_cid: " << ngtcp2_strerror(rv); return -1; } if (vc.dcidlen != SHRPX_QUIC_SCIDLEN) { LOG(ERROR) << "DCID length is invalid"; return -1; } if (single_worker_) { auto faddr = single_worker_->find_quic_upstream_addr(pkt->local_addr); if (faddr == nullptr) { LOG(ERROR) << "No suitable upstream address found"; return 0; } auto quic_conn_handler = single_worker_->get_quic_connection_handler(); // Ignore return value quic_conn_handler->handle_packet(faddr, pkt->remote_addr, pkt->local_addr, pkt->pi, pkt->data); return 0; } auto &qkm = quic_keying_materials_->keying_materials.front(); ConnectionID decrypted_dcid; if (decrypt_quic_connection_id(decrypted_dcid, vc.dcid + SHRPX_QUIC_CID_WORKER_ID_OFFSET, qkm.cid_decryption_ctx) != 0) { return -1; } auto worker = find_worker(decrypted_dcid.worker); if (worker == nullptr) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "No worker to match Worker ID"; } return 0; } WorkerEvent wev{ .type = WorkerEventType::QUIC_PKT_FORWARD, .quic_pkt = std::move(pkt), }; worker->send(std::move(wev)); return 0; } #endif // defined(ENABLE_HTTP3) } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_router.cc0000644000000000000000000000013215077107270016122 xustar0030 mtime=1761382072.998444116 30 atime=1761382106.154310647 30 ctime=1761382109.150300237 nghttp2-1.68.0/src/shrpx_router.cc0000644000175100017510000002507015077107270016516 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_router.h" #include #include "shrpx_config.h" #include "shrpx_log.h" namespace shrpx { RNode::RNode() : index(-1), wildcard_index(-1) {} RNode::RNode(const std::string_view &s, ssize_t index, ssize_t wildcard_index) : s(s), index(index), wildcard_index(wildcard_index) {} Router::Router() : balloc_(1024, 1024), root_{} {} Router::~Router() {} namespace { char first_byte(const std::unique_ptr &node) { return node->s[0]; } } // namespace namespace { RNode *find_next_node(const RNode *node, char c) { auto itr = std::ranges::lower_bound(node->next, c, {}, first_byte); if (itr == std::ranges::end(node->next) || (*itr)->s[0] != c) { return nullptr; } return (*itr).get(); } } // namespace namespace { void add_next_node(RNode *node, std::unique_ptr new_node) { auto itr = std::ranges::lower_bound(node->next, new_node->s[0], {}, first_byte); node->next.insert(itr, std::move(new_node)); } } // namespace void Router::add_node(RNode *node, const std::string_view &pattern, ssize_t index, ssize_t wildcard_index) { auto pat = make_string_ref(balloc_, pattern); auto new_node = std::make_unique(pat, index, wildcard_index); add_next_node(node, std::move(new_node)); } size_t Router::add_route(const std::string_view &pattern, size_t idx, bool wildcard) { ssize_t index = -1, wildcard_index = -1; if (wildcard) { wildcard_index = as_signed(idx); } else { index = as_signed(idx); } auto node = &root_; size_t i = 0; for (;;) { auto next_node = find_next_node(node, pattern[i]); if (next_node == nullptr) { add_node(node, pattern.substr(i), index, wildcard_index); return idx; } node = next_node; auto slen = pattern.size() - i; auto s = pattern.data() + i; auto n = std::min(node->s.size(), slen); size_t j; for (j = 0; j < n && node->s[j] == s[j]; ++j) ; if (j == n) { // The common prefix was matched if (slen == node->s.size()) { // Complete match if (index != -1) { if (node->index != -1) { // Return the existing index for duplicates. return as_unsigned(node->index); } node->index = index; return idx; } assert(wildcard_index != -1); if (node->wildcard_index != -1) { return as_unsigned(node->wildcard_index); } node->wildcard_index = wildcard_index; return idx; } if (slen > node->s.size()) { // We still have pattern to add i += j; continue; } } if (node->s.size() > j) { // node must be split into 2 nodes. new_node is now the child // of node. auto new_node = std::make_unique(node->s.substr(j), node->index, node->wildcard_index); std::swap(node->next, new_node->next); node->s = node->s.substr(0, j); node->index = -1; node->wildcard_index = -1; add_next_node(node, std::move(new_node)); if (slen == j) { node->index = index; node->wildcard_index = wildcard_index; return idx; } } i += j; assert(pattern.size() > i); add_node(node, pattern.substr(i), index, wildcard_index); return idx; } } namespace { const RNode *match_complete(size_t *offset, const RNode *node, const char *first, const char *last) { *offset = 0; if (first == last) { return node; } auto p = first; for (;;) { auto next_node = find_next_node(node, *p); if (next_node == nullptr) { return nullptr; } node = next_node; auto n = std::min(node->s.size(), static_cast(last - p)); if (memcmp(node->s.data(), p, n) != 0) { return nullptr; } p += n; if (p == last) { *offset = n; return node; } } } } // namespace namespace { const RNode *match_partial(bool *pattern_is_wildcard, const RNode *node, size_t offset, const char *first, const char *last) { *pattern_is_wildcard = false; if (first == last) { if (node->s.size() == offset) { return node; } return nullptr; } auto p = first; const RNode *found_node = nullptr; if (offset > 0) { auto n = std::min(node->s.size() - offset, static_cast(last - first)); if (memcmp(node->s.data() + offset, first, n) != 0) { return nullptr; } p += n; if (p == last) { if (node->s.size() == offset + n) { if (node->index != -1) { return node; } // The last '/' handling, see below. node = find_next_node(node, '/'); if (node != nullptr && node->index != -1 && node->s.size() == 1) { return node; } return nullptr; } // The last '/' handling, see below. if (node->index != -1 && offset + n + 1 == node->s.size() && node->s[node->s.size() - 1] == '/') { return node; } return nullptr; } if (node->wildcard_index != -1) { found_node = node; *pattern_is_wildcard = true; } else if (node->index != -1 && node->s[node->s.size() - 1] == '/') { found_node = node; *pattern_is_wildcard = false; } assert(node->s.size() == offset + n); } for (;;) { auto next_node = find_next_node(node, *p); if (next_node == nullptr) { return found_node; } node = next_node; auto n = std::min(node->s.size(), static_cast(last - p)); if (memcmp(node->s.data(), p, n) != 0) { return found_node; } p += n; if (p == last) { if (node->s.size() == n) { // Complete match with this node if (node->index != -1) { *pattern_is_wildcard = false; return node; } // The last '/' handling, see below. node = find_next_node(node, '/'); if (node != nullptr && node->index != -1 && node->s.size() == 1) { *pattern_is_wildcard = false; return node; } return found_node; } // We allow match without trailing "/" at the end of pattern. // So, if pattern ends with '/', and pattern and path matches // without that slash, we consider they match to deal with // request to the directory without trailing slash. That is if // pattern is "/foo/" and path is "/foo", we consider they // match. if (node->index != -1 && n + 1 == node->s.size() && node->s[n] == '/') { *pattern_is_wildcard = false; return node; } return found_node; } if (node->wildcard_index != -1) { found_node = node; *pattern_is_wildcard = true; } else if (node->index != -1 && node->s[node->s.size() - 1] == '/') { // This is the case when pattern which ends with "/" is included // in query. found_node = node; *pattern_is_wildcard = false; } assert(node->s.size() == n); } } } // namespace ssize_t Router::match(const std::string_view &host, const std::string_view &path) const { const RNode *node; size_t offset; node = match_complete(&offset, &root_, std::ranges::begin(host), std::ranges::end(host)); if (node == nullptr) { return -1; } bool pattern_is_wildcard; node = match_partial(&pattern_is_wildcard, node, offset, std::ranges::begin(path), std::ranges::end(path)); if (node == nullptr || node == &root_) { return -1; } return pattern_is_wildcard ? node->wildcard_index : node->index; } ssize_t Router::match(const std::string_view &s) const { const RNode *node; size_t offset; node = match_complete(&offset, &root_, std::ranges::begin(s), std::ranges::end(s)); if (node == nullptr) { return -1; } if (node->s.size() != offset) { return -1; } return node->index; } namespace { const RNode *match_prefix(size_t *nread, const RNode *node, const char *first, const char *last) { if (first == last) { return nullptr; } auto p = first; for (;;) { auto next_node = find_next_node(node, *p); if (next_node == nullptr) { return nullptr; } node = next_node; auto n = std::min(node->s.size(), static_cast(last - p)); if (memcmp(node->s.data(), p, n) != 0) { return nullptr; } p += n; if (p != last) { if (node->index != -1) { *nread = as_unsigned(p - first); return node; } continue; } if (node->s.size() == n) { *nread = as_unsigned(p - first); return node; } return nullptr; } } } // namespace ssize_t Router::match_prefix(size_t *nread, const RNode **last_node, const std::string_view &s) const { if (*last_node == nullptr) { *last_node = &root_; } auto node = ::shrpx::match_prefix(nread, *last_node, std::ranges::begin(s), std::ranges::end(s)); if (node == nullptr) { return -1; } *last_node = node; return node->index; } namespace { void dump_node(const RNode *node, int depth) { fprintf(stderr, "%*ss='%.*s', len=%zu, index=%zd\n", depth, "", static_cast(node->s.size()), node->s.data(), node->s.size(), node->index); for (auto &nd : node->next) { dump_node(nd.get(), depth + 4); } } } // namespace void Router::dump() const { dump_node(&root_, 0); } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_config.h0000644000000000000000000000013215077107270015711 xustar0030 mtime=1761382072.991444148 30 atime=1761382106.078310982 30 ctime=1761382109.074300456 nghttp2-1.68.0/src/shrpx_config.h0000644000175100017510000014427315077107270016314 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_CONFIG_H #define SHRPX_CONFIG_H #include "shrpx.h" #include #ifdef HAVE_SYS_SOCKET_H # include #endif // defined(HAVE_SYS_SOCKET_H) #include #ifdef HAVE_NETINET_IN_H # include #endif // defined(HAVE_NETINET_IN_H) #ifdef HAVE_ARPA_INET_H # include #endif // defined(HAVE_ARPA_INET_H) #include #include #include #include #include #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include #include #include "shrpx_log.h" #include "shrpx_router.h" #ifdef ENABLE_HTTP3 # include "shrpx_quic.h" #endif // defined(ENABLE_HTTP3) #include "template.h" #include "http2.h" #include "network.h" #include "allocator.h" using namespace nghttp2; namespace shrpx { struct LogFragment; class ConnectBlocker; class Http2Session; namespace tls { class CertLookupTree; } // namespace tls inline constexpr auto SHRPX_OPT_PRIVATE_KEY_FILE = "private-key-file"sv; inline constexpr auto SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE = "private-key-passwd-file"sv; inline constexpr auto SHRPX_OPT_CERTIFICATE_FILE = "certificate-file"sv; inline constexpr auto SHRPX_OPT_DH_PARAM_FILE = "dh-param-file"sv; inline constexpr auto SHRPX_OPT_SUBCERT = "subcert"sv; inline constexpr auto SHRPX_OPT_BACKEND = "backend"sv; inline constexpr auto SHRPX_OPT_FRONTEND = "frontend"sv; inline constexpr auto SHRPX_OPT_WORKERS = "workers"sv; inline constexpr auto SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS = "http2-max-concurrent-streams"sv; inline constexpr auto SHRPX_OPT_LOG_LEVEL = "log-level"sv; inline constexpr auto SHRPX_OPT_DAEMON = "daemon"sv; inline constexpr auto SHRPX_OPT_HTTP2_PROXY = "http2-proxy"sv; inline constexpr auto SHRPX_OPT_HTTP2_BRIDGE = "http2-bridge"sv; inline constexpr auto SHRPX_OPT_CLIENT_PROXY = "client-proxy"sv; inline constexpr auto SHRPX_OPT_ADD_X_FORWARDED_FOR = "add-x-forwarded-for"sv; inline constexpr auto SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR = "strip-incoming-x-forwarded-for"sv; inline constexpr auto SHRPX_OPT_NO_VIA = "no-via"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT = "frontend-http2-read-timeout"sv; inline constexpr auto SHRPX_OPT_FRONTEND_READ_TIMEOUT = "frontend-read-timeout"sv; inline constexpr auto SHRPX_OPT_FRONTEND_WRITE_TIMEOUT = "frontend-write-timeout"sv; inline constexpr auto SHRPX_OPT_BACKEND_READ_TIMEOUT = "backend-read-timeout"sv; inline constexpr auto SHRPX_OPT_BACKEND_WRITE_TIMEOUT = "backend-write-timeout"sv; inline constexpr auto SHRPX_OPT_STREAM_READ_TIMEOUT = "stream-read-timeout"sv; inline constexpr auto SHRPX_OPT_STREAM_WRITE_TIMEOUT = "stream-write-timeout"sv; inline constexpr auto SHRPX_OPT_ACCESSLOG_FILE = "accesslog-file"sv; inline constexpr auto SHRPX_OPT_ACCESSLOG_SYSLOG = "accesslog-syslog"sv; inline constexpr auto SHRPX_OPT_ACCESSLOG_FORMAT = "accesslog-format"sv; inline constexpr auto SHRPX_OPT_ERRORLOG_FILE = "errorlog-file"sv; inline constexpr auto SHRPX_OPT_ERRORLOG_SYSLOG = "errorlog-syslog"sv; inline constexpr auto SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT = "backend-keep-alive-timeout"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS = "frontend-http2-window-bits"sv; inline constexpr auto SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS = "backend-http2-window-bits"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS = "frontend-http2-connection-window-bits"sv; inline constexpr auto SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS = "backend-http2-connection-window-bits"sv; inline constexpr auto SHRPX_OPT_FRONTEND_NO_TLS = "frontend-no-tls"sv; inline constexpr auto SHRPX_OPT_BACKEND_NO_TLS = "backend-no-tls"sv; inline constexpr auto SHRPX_OPT_BACKEND_TLS_SNI_FIELD = "backend-tls-sni-field"sv; inline constexpr auto SHRPX_OPT_PID_FILE = "pid-file"sv; inline constexpr auto SHRPX_OPT_USER = "user"sv; inline constexpr auto SHRPX_OPT_SYSLOG_FACILITY = "syslog-facility"sv; inline constexpr auto SHRPX_OPT_BACKLOG = "backlog"sv; inline constexpr auto SHRPX_OPT_CIPHERS = "ciphers"sv; inline constexpr auto SHRPX_OPT_CLIENT = "client"sv; inline constexpr auto SHRPX_OPT_INSECURE = "insecure"sv; inline constexpr auto SHRPX_OPT_CACERT = "cacert"sv; inline constexpr auto SHRPX_OPT_BACKEND_IPV4 = "backend-ipv4"sv; inline constexpr auto SHRPX_OPT_BACKEND_IPV6 = "backend-ipv6"sv; inline constexpr auto SHRPX_OPT_BACKEND_HTTP_PROXY_URI = "backend-http-proxy-uri"sv; inline constexpr auto SHRPX_OPT_READ_RATE = "read-rate"sv; inline constexpr auto SHRPX_OPT_READ_BURST = "read-burst"sv; inline constexpr auto SHRPX_OPT_WRITE_RATE = "write-rate"sv; inline constexpr auto SHRPX_OPT_WRITE_BURST = "write-burst"sv; inline constexpr auto SHRPX_OPT_WORKER_READ_RATE = "worker-read-rate"sv; inline constexpr auto SHRPX_OPT_WORKER_READ_BURST = "worker-read-burst"sv; inline constexpr auto SHRPX_OPT_WORKER_WRITE_RATE = "worker-write-rate"sv; inline constexpr auto SHRPX_OPT_WORKER_WRITE_BURST = "worker-write-burst"sv; inline constexpr auto SHRPX_OPT_NPN_LIST = "npn-list"sv; inline constexpr auto SHRPX_OPT_TLS_PROTO_LIST = "tls-proto-list"sv; inline constexpr auto SHRPX_OPT_VERIFY_CLIENT = "verify-client"sv; inline constexpr auto SHRPX_OPT_VERIFY_CLIENT_CACERT = "verify-client-cacert"sv; inline constexpr auto SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE = "client-private-key-file"sv; inline constexpr auto SHRPX_OPT_CLIENT_CERT_FILE = "client-cert-file"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER = "frontend-http2-dump-request-header"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER = "frontend-http2-dump-response-header"sv; inline constexpr auto SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING = "http2-no-cookie-crumbling"sv; inline constexpr auto SHRPX_OPT_FRONTEND_FRAME_DEBUG = "frontend-frame-debug"sv; inline constexpr auto SHRPX_OPT_PADDING = "padding"sv; inline constexpr auto SHRPX_OPT_ALTSVC = "altsvc"sv; inline constexpr auto SHRPX_OPT_ADD_REQUEST_HEADER = "add-request-header"sv; inline constexpr auto SHRPX_OPT_ADD_RESPONSE_HEADER = "add-response-header"sv; inline constexpr auto SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS = "worker-frontend-connections"sv; inline constexpr auto SHRPX_OPT_NO_LOCATION_REWRITE = "no-location-rewrite"sv; inline constexpr auto SHRPX_OPT_NO_HOST_REWRITE = "no-host-rewrite"sv; inline constexpr auto SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST = "backend-http1-connections-per-host"sv; inline constexpr auto SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND = "backend-http1-connections-per-frontend"sv; inline constexpr auto SHRPX_OPT_LISTENER_DISABLE_TIMEOUT = "listener-disable-timeout"sv; inline constexpr auto SHRPX_OPT_TLS_TICKET_KEY_FILE = "tls-ticket-key-file"sv; inline constexpr auto SHRPX_OPT_RLIMIT_NOFILE = "rlimit-nofile"sv; inline constexpr auto SHRPX_OPT_BACKEND_REQUEST_BUFFER = "backend-request-buffer"sv; inline constexpr auto SHRPX_OPT_BACKEND_RESPONSE_BUFFER = "backend-response-buffer"sv; inline constexpr auto SHRPX_OPT_NO_SERVER_PUSH = "no-server-push"sv; inline constexpr auto SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER = "backend-http2-connections-per-worker"sv; inline constexpr auto SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE = "fetch-ocsp-response-file"sv; inline constexpr auto SHRPX_OPT_OCSP_UPDATE_INTERVAL = "ocsp-update-interval"sv; inline constexpr auto SHRPX_OPT_NO_OCSP = "no-ocsp"sv; inline constexpr auto SHRPX_OPT_HEADER_FIELD_BUFFER = "header-field-buffer"sv; inline constexpr auto SHRPX_OPT_MAX_HEADER_FIELDS = "max-header-fields"sv; inline constexpr auto SHRPX_OPT_INCLUDE = "include"sv; inline constexpr auto SHRPX_OPT_TLS_TICKET_KEY_CIPHER = "tls-ticket-key-cipher"sv; inline constexpr auto SHRPX_OPT_HOST_REWRITE = "host-rewrite"sv; inline constexpr auto SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED = "tls-session-cache-memcached"sv; inline constexpr auto SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED = "tls-ticket-key-memcached"sv; inline constexpr auto SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_INTERVAL = "tls-ticket-key-memcached-interval"sv; inline constexpr auto SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY = "tls-ticket-key-memcached-max-retry"sv; inline constexpr auto SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL = "tls-ticket-key-memcached-max-fail"sv; inline constexpr auto SHRPX_OPT_MRUBY_FILE = "mruby-file"sv; inline constexpr auto SHRPX_OPT_ACCEPT_PROXY_PROTOCOL = "accept-proxy-protocol"sv; inline constexpr auto SHRPX_OPT_FASTOPEN = "fastopen"sv; inline constexpr auto SHRPX_OPT_TLS_DYN_REC_WARMUP_THRESHOLD = "tls-dyn-rec-warmup-threshold"sv; inline constexpr auto SHRPX_OPT_TLS_DYN_REC_IDLE_TIMEOUT = "tls-dyn-rec-idle-timeout"sv; inline constexpr auto SHRPX_OPT_ADD_FORWARDED = "add-forwarded"sv; inline constexpr auto SHRPX_OPT_STRIP_INCOMING_FORWARDED = "strip-incoming-forwarded"sv; inline constexpr auto SHRPX_OPT_FORWARDED_BY = "forwarded-by"sv; inline constexpr auto SHRPX_OPT_FORWARDED_FOR = "forwarded-for"sv; inline constexpr auto SHRPX_OPT_REQUEST_HEADER_FIELD_BUFFER = "request-header-field-buffer"sv; inline constexpr auto SHRPX_OPT_MAX_REQUEST_HEADER_FIELDS = "max-request-header-fields"sv; inline constexpr auto SHRPX_OPT_RESPONSE_HEADER_FIELD_BUFFER = "response-header-field-buffer"sv; inline constexpr auto SHRPX_OPT_MAX_RESPONSE_HEADER_FIELDS = "max-response-header-fields"sv; inline constexpr auto SHRPX_OPT_NO_HTTP2_CIPHER_BLOCK_LIST = "no-http2-cipher-block-list"sv; inline constexpr auto SHRPX_OPT_NO_HTTP2_CIPHER_BLACK_LIST = "no-http2-cipher-black-list"sv; inline constexpr auto SHRPX_OPT_BACKEND_HTTP1_TLS = "backend-http1-tls"sv; inline constexpr auto SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_TLS = "tls-session-cache-memcached-tls"sv; inline constexpr auto SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE = "tls-session-cache-memcached-cert-file"sv; inline constexpr auto SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE = "tls-session-cache-memcached-private-key-file"sv; inline constexpr auto SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY = "tls-session-cache-memcached-address-family"sv; inline constexpr auto SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_TLS = "tls-ticket-key-memcached-tls"sv; inline constexpr auto SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_CERT_FILE = "tls-ticket-key-memcached-cert-file"sv; inline constexpr auto SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE = "tls-ticket-key-memcached-private-key-file"sv; inline constexpr auto SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY = "tls-ticket-key-memcached-address-family"sv; inline constexpr auto SHRPX_OPT_BACKEND_ADDRESS_FAMILY = "backend-address-family"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS = "frontend-http2-max-concurrent-streams"sv; inline constexpr auto SHRPX_OPT_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS = "backend-http2-max-concurrent-streams"sv; inline constexpr auto SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND = "backend-connections-per-frontend"sv; inline constexpr auto SHRPX_OPT_BACKEND_TLS = "backend-tls"sv; inline constexpr auto SHRPX_OPT_BACKEND_CONNECTIONS_PER_HOST = "backend-connections-per-host"sv; inline constexpr auto SHRPX_OPT_ERROR_PAGE = "error-page"sv; inline constexpr auto SHRPX_OPT_NO_KQUEUE = "no-kqueue"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HTTP2_SETTINGS_TIMEOUT = "frontend-http2-settings-timeout"sv; inline constexpr auto SHRPX_OPT_BACKEND_HTTP2_SETTINGS_TIMEOUT = "backend-http2-settings-timeout"sv; inline constexpr auto SHRPX_OPT_API_MAX_REQUEST_BODY = "api-max-request-body"sv; inline constexpr auto SHRPX_OPT_BACKEND_MAX_BACKOFF = "backend-max-backoff"sv; inline constexpr auto SHRPX_OPT_SERVER_NAME = "server-name"sv; inline constexpr auto SHRPX_OPT_NO_SERVER_REWRITE = "no-server-rewrite"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE = "frontend-http2-optimize-write-buffer-size"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE = "frontend-http2-optimize-window-size"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HTTP2_WINDOW_SIZE = "frontend-http2-window-size"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE = "frontend-http2-connection-window-size"sv; inline constexpr auto SHRPX_OPT_BACKEND_HTTP2_WINDOW_SIZE = "backend-http2-window-size"sv; inline constexpr auto SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE = "backend-http2-connection-window-size"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE = "frontend-http2-encoder-dynamic-table-size"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE = "frontend-http2-decoder-dynamic-table-size"sv; inline constexpr auto SHRPX_OPT_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE = "backend-http2-encoder-dynamic-table-size"sv; inline constexpr auto SHRPX_OPT_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE = "backend-http2-decoder-dynamic-table-size"sv; inline constexpr auto SHRPX_OPT_ECDH_CURVES = "ecdh-curves"sv; inline constexpr auto SHRPX_OPT_TLS_SCT_DIR = "tls-sct-dir"sv; inline constexpr auto SHRPX_OPT_BACKEND_CONNECT_TIMEOUT = "backend-connect-timeout"sv; inline constexpr auto SHRPX_OPT_DNS_CACHE_TIMEOUT = "dns-cache-timeout"sv; inline constexpr auto SHRPX_OPT_DNS_LOOKUP_TIMEOUT = "dns-lookup-timeout"sv; inline constexpr auto SHRPX_OPT_DNS_MAX_TRY = "dns-max-try"sv; inline constexpr auto SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT = "frontend-keep-alive-timeout"sv; inline constexpr auto SHRPX_OPT_PSK_SECRETS = "psk-secrets"sv; inline constexpr auto SHRPX_OPT_CLIENT_PSK_SECRETS = "client-psk-secrets"sv; inline constexpr auto SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLOCK_LIST = "client-no-http2-cipher-block-list"sv; inline constexpr auto SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST = "client-no-http2-cipher-black-list"sv; inline constexpr auto SHRPX_OPT_CLIENT_CIPHERS = "client-ciphers"sv; inline constexpr auto SHRPX_OPT_ACCESSLOG_WRITE_EARLY = "accesslog-write-early"sv; inline constexpr auto SHRPX_OPT_TLS_MIN_PROTO_VERSION = "tls-min-proto-version"sv; inline constexpr auto SHRPX_OPT_TLS_MAX_PROTO_VERSION = "tls-max-proto-version"sv; inline constexpr auto SHRPX_OPT_REDIRECT_HTTPS_PORT = "redirect-https-port"sv; inline constexpr auto SHRPX_OPT_FRONTEND_MAX_REQUESTS = "frontend-max-requests"sv; inline constexpr auto SHRPX_OPT_SINGLE_THREAD = "single-thread"sv; inline constexpr auto SHRPX_OPT_SINGLE_PROCESS = "single-process"sv; inline constexpr auto SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO = "no-add-x-forwarded-proto"sv; inline constexpr auto SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO = "no-strip-incoming-x-forwarded-proto"sv; inline constexpr auto SHRPX_OPT_OCSP_STARTUP = "ocsp-startup"sv; inline constexpr auto SHRPX_OPT_NO_VERIFY_OCSP = "no-verify-ocsp"sv; inline constexpr auto SHRPX_OPT_VERIFY_CLIENT_TOLERATE_EXPIRED = "verify-client-tolerate-expired"sv; inline constexpr auto SHRPX_OPT_IGNORE_PER_PATTERN_MRUBY_ERROR = "ignore-per-pattern-mruby-error"sv; inline constexpr auto SHRPX_OPT_TLS_NO_POSTPONE_EARLY_DATA = "tls-no-postpone-early-data"sv; inline constexpr auto SHRPX_OPT_TLS_MAX_EARLY_DATA = "tls-max-early-data"sv; inline constexpr auto SHRPX_OPT_TLS13_CIPHERS = "tls13-ciphers"sv; inline constexpr auto SHRPX_OPT_TLS13_CLIENT_CIPHERS = "tls13-client-ciphers"sv; inline constexpr auto SHRPX_OPT_NO_STRIP_INCOMING_EARLY_DATA = "no-strip-incoming-early-data"sv; inline constexpr auto SHRPX_OPT_QUIC_BPF_PROGRAM_FILE = "quic-bpf-program-file"sv; inline constexpr auto SHRPX_OPT_NO_QUIC_BPF = "no-quic-bpf"sv; inline constexpr auto SHRPX_OPT_HTTP2_ALTSVC = "http2-altsvc"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HTTP3_READ_TIMEOUT = "frontend-http3-read-timeout"sv; inline constexpr auto SHRPX_OPT_FRONTEND_QUIC_IDLE_TIMEOUT = "frontend-quic-idle-timeout"sv; inline constexpr auto SHRPX_OPT_FRONTEND_QUIC_DEBUG_LOG = "frontend-quic-debug-log"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HTTP3_WINDOW_SIZE = "frontend-http3-window-size"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE = "frontend-http3-connection-window-size"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HTTP3_MAX_WINDOW_SIZE = "frontend-http3-max-window-size"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE = "frontend-http3-max-connection-window-size"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS = "frontend-http3-max-concurrent-streams"sv; inline constexpr auto SHRPX_OPT_FRONTEND_QUIC_EARLY_DATA = "frontend-quic-early-data"sv; inline constexpr auto SHRPX_OPT_FRONTEND_QUIC_QLOG_DIR = "frontend-quic-qlog-dir"sv; inline constexpr auto SHRPX_OPT_FRONTEND_QUIC_REQUIRE_TOKEN = "frontend-quic-require-token"sv; inline constexpr auto SHRPX_OPT_FRONTEND_QUIC_CONGESTION_CONTROLLER = "frontend-quic-congestion-controller"sv; inline constexpr auto SHRPX_OPT_QUIC_SERVER_ID = "quic-server-id"sv; inline constexpr auto SHRPX_OPT_FRONTEND_QUIC_SECRET_FILE = "frontend-quic-secret-file"sv; inline constexpr auto SHRPX_OPT_RLIMIT_MEMLOCK = "rlimit-memlock"sv; inline constexpr auto SHRPX_OPT_MAX_WORKER_PROCESSES = "max-worker-processes"sv; inline constexpr auto SHRPX_OPT_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD = "worker-process-grace-shutdown-period"sv; inline constexpr auto SHRPX_OPT_FRONTEND_QUIC_INITIAL_RTT = "frontend-quic-initial-rtt"sv; inline constexpr auto SHRPX_OPT_REQUIRE_HTTP_SCHEME = "require-http-scheme"sv; inline constexpr auto SHRPX_OPT_TLS_KTLS = "tls-ktls"sv; inline constexpr auto SHRPX_OPT_ALPN_LIST = "alpn-list"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HEADER_TIMEOUT = "frontend-header-timeout"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HTTP2_IDLE_TIMEOUT = "frontend-http2-idle-timeout"sv; inline constexpr auto SHRPX_OPT_FRONTEND_HTTP3_IDLE_TIMEOUT = "frontend-http3-idle-timeout"sv; inline constexpr auto SHRPX_OPT_GROUPS = "groups"sv; inline constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8; inline constexpr auto DEFAULT_DOWNSTREAM_HOST = "127.0.0.1"sv; inline constexpr int16_t DEFAULT_DOWNSTREAM_PORT = 80; enum class Proto { NONE, HTTP1, HTTP2, HTTP3, MEMCACHED, }; enum class SessionAffinity { // No session affinity NONE, // Client IP affinity IP, // Cookie based affinity COOKIE, }; enum class SessionAffinityCookieSecure { // Secure attribute of session affinity cookie is determined by the // request scheme. AUTO, // Secure attribute of session affinity cookie is always set. YES, // Secure attribute of session affinity cookie is always unset. NO, }; enum class SessionAffinityCookieStickiness { // Backend server might be changed when an existing backend server // is removed, or new backend server is added. LOOSE, // Backend server might be changed when a designated backend server // is removed, but adding new backend server does not cause // breakage. STRICT, }; struct AffinityConfig { // Type of session affinity. SessionAffinity type; struct { // Name of a cookie to use. std::string_view name; // Path which a cookie is applied to. std::string_view path; // Secure attribute SessionAffinityCookieSecure secure; // Affinity Stickiness SessionAffinityCookieStickiness stickiness; } cookie; }; enum shrpx_forwarded_param { FORWARDED_NONE = 0, FORWARDED_BY = 0x1, FORWARDED_FOR = 0x2, FORWARDED_HOST = 0x4, FORWARDED_PROTO = 0x8, }; enum class ForwardedNode { OBFUSCATED, IP, }; struct AltSvc { std::string_view protocol_id, host, origin, service, params; uint16_t port; }; enum class UpstreamAltMode { // No alternative mode NONE, // API processing mode API, // Health monitor mode HEALTHMON, }; struct UpstreamAddr { // The unique index of this address. size_t index; // The frontend address (e.g., FQDN, hostname, IP address). If // |host_unix| is true, this is UNIX domain socket path. This must // be NULL terminated string. std::string_view host; // For TCP socket, this is :. For IPv6 address, // address is surrounded by square brackets. If socket is UNIX // domain socket, this is "localhost". std::string_view hostport; // Binary representation of this address. Only filled if quic is // true. sockaddr_union sockaddr; // frontend port. 0 if |host_unix| is true. uint16_t port; // For TCP socket, this is either AF_INET or AF_INET6. For UNIX // domain socket, this is 0. int family; // Alternate mode UpstreamAltMode alt_mode; // true if |host| contains UNIX domain socket path. bool host_unix; // true if TLS is enabled. bool tls; // true if SNI host should be used as a host when selecting backend // server. bool sni_fwd; // true if client is supposed to send PROXY protocol v1 header. bool accept_proxy_protocol; bool quic; // true if sockaddr contains wildcard address. bool sockaddr_any; int fd; }; struct DownstreamAddrConfig { // Resolved address if |dns| is false Address addr; // backend address. If |host_unix| is true, this is UNIX domain // socket path. This must be NULL terminated string. std::string_view host; // :. This does not treat 80 and 443 specially. If // |host_unix| is true, this is "localhost". std::string_view hostport; // hostname sent as SNI field std::string_view sni; // name of group which this address belongs to. std::string_view group; size_t fall; size_t rise; // weight of this address inside a weight group. Its range is [1, // 256], inclusive. uint32_t weight; // weight of the weight group. Its range is [1, 256], inclusive. uint32_t group_weight; // affinity hash for this address. It is assigned when strict // stickiness is enabled. uint32_t affinity_hash; // Application protocol used in this group Proto proto; // backend port. 0 if |host_unix| is true. uint16_t port; // true if |host| contains UNIX domain socket path. bool host_unix; bool tls; // true if dynamic DNS is enabled bool dns; // true if :scheme pseudo header field should be upgraded to secure // variant (e.g., "https") when forwarding request to a backend // connected by TLS connection. bool upgrade_scheme; // true if a request should not be forwarded to a backend. bool dnf; }; // Mapping hash to idx which is an index into // DownstreamAddrGroupConfig::addrs. struct AffinityHash { AffinityHash(size_t idx, uint32_t hash) : idx(idx), hash(hash) {} size_t idx; uint32_t hash; }; struct DownstreamAddrGroupConfig { DownstreamAddrGroupConfig(const std::string_view &pattern) : pattern(pattern), affinity{SessionAffinity::NONE}, redirect_if_not_tls(false), dnf{false}, timeout{} {} std::string_view pattern; std::string_view mruby_file; std::vector addrs; // Bunch of session affinity hash. Only used if affinity == // SessionAffinity::IP. std::vector affinity_hash; // Maps affinity hash of each DownstreamAddrConfig to its index in // addrs. It is only assigned when strict stickiness is enabled. std::unordered_map affinity_hash_map; // Cookie based session affinity configuration. AffinityConfig affinity; // true if this group requires that client connection must be TLS, // and the request must be redirected to https URI. bool redirect_if_not_tls; // true if a request should not be forwarded to a backend. bool dnf; // Timeouts for backend connection. struct { ev_tstamp read; ev_tstamp write; } timeout; }; struct TicketKey { const EVP_CIPHER *cipher; const EVP_MD *hmac; size_t hmac_keylen; struct { // name of this ticket configuration std::array name; // encryption key for |cipher| std::array enc_key; // hmac key for |hmac| std::array hmac_key; } data; }; struct TicketKeys { ~TicketKeys(); std::vector keys; }; struct TLSCertificate { TLSCertificate(std::string_view private_key_file, std::string_view cert_file, std::vector sct_data) : private_key_file(std::move(private_key_file)), cert_file(std::move(cert_file)), sct_data(std::move(sct_data)) {} std::string_view private_key_file; std::string_view cert_file; std::vector sct_data; }; #ifdef ENABLE_HTTP3 struct QUICKeyingMaterial { QUICKeyingMaterial() noexcept = default; QUICKeyingMaterial(QUICKeyingMaterial &&other) noexcept; ~QUICKeyingMaterial() noexcept; QUICKeyingMaterial &operator=(QUICKeyingMaterial &&other) noexcept; EVP_CIPHER_CTX *cid_encryption_ctx; EVP_CIPHER_CTX *cid_decryption_ctx; std::array reserved; std::array secret; std::array salt; std::array cid_encryption_key; // Identifier of this keying material. Only the first 2 bits are // used. uint8_t id; }; struct QUICKeyingMaterials { std::vector keying_materials; }; #endif // defined(ENABLE_HTTP3) struct HttpProxy { Address addr; // host in http proxy URI std::string_view host; // userinfo in http proxy URI, not percent-encoded form std::string_view userinfo; // port in http proxy URI uint16_t port; }; struct TLSConfig { // RFC 5077 Session ticket related configurations struct { struct { Address addr; uint16_t port; // Hostname of memcached server. This is also used as SNI field // if TLS is enabled. std::string_view host; // Client private key and certificate for authentication std::string_view private_key_file; std::string_view cert_file; ev_tstamp interval; // Maximum number of retries when getting TLS ticket key from // mamcached, due to network error. size_t max_retry; // Maximum number of consecutive error from memcached, when this // limit reached, TLS ticket is disabled. size_t max_fail; // Address family of memcached connection. One of either // AF_INET, AF_INET6 or AF_UNSPEC. int family; bool tls; } memcached; std::vector files; const EVP_CIPHER *cipher; // true if --tls-ticket-key-cipher is used bool cipher_given; } ticket; // Dynamic record sizing configurations struct { size_t warmup_threshold; ev_tstamp idle_timeout; } dyn_rec; // Client verification configurations struct { // Path to file containing CA certificate solely used for client // certificate validation std::string_view cacert; bool enabled; // true if we accept an expired client certificate. bool tolerate_expired; } client_verify; // Client (backend connection) TLS configuration. struct { // Client PSK configuration struct { // identity must be NULL terminated string. std::string_view identity; std::string_view secret; } psk; std::string_view private_key_file; std::string_view cert_file; std::string_view ciphers; std::string_view tls13_ciphers; bool no_http2_cipher_block_list; } client; // PSK secrets. The key is identity, and the associated value is // its secret. std::unordered_map psk_secrets; // The list of additional TLS certificate pair std::vector subcerts; std::vector alpn_prefs; // list of supported ALPN protocol strings in the order of // preference. std::vector alpn_list; // list of supported SSL/TLS protocol strings. std::vector tls_proto_list; std::vector sct_data; // Bit mask to disable SSL/TLS protocol versions. This will be // passed to SSL_CTX_set_options(). nghttp2_ssl_op_type tls_proto_mask; std::string_view backend_sni_name; std::chrono::seconds session_timeout; std::string_view private_key_file; std::string_view private_key_passwd; std::string_view cert_file; std::string_view dh_param_file; std::string_view ciphers; std::string_view tls13_ciphers; std::string_view groups; std::string_view cacert; // The maximum amount of 0-RTT data that server accepts. uint32_t max_early_data; // The minimum and maximum TLS version. These values are defined in // OpenSSL header file. int min_proto_version; int max_proto_version; bool insecure; bool no_http2_cipher_block_list; // true if forwarding requests included in TLS early data should not // be postponed until TLS handshake finishes. bool no_postpone_early_data; bool ktls; }; #ifdef ENABLE_HTTP3 struct QUICConfig { struct { struct { ev_tstamp idle; } timeout; struct { bool log; } debug; struct { std::string_view dir; } qlog; ngtcp2_cc_algo congestion_controller; bool early_data; bool require_token; std::string_view secret_file; ev_tstamp initial_rtt; } upstream; struct { std::string_view prog_file; bool disabled; } bpf; uint32_t server_id; }; struct Http3Config { struct { size_t max_concurrent_streams; int32_t window_size; int32_t connection_window_size; int32_t max_window_size; int32_t max_connection_window_size; } upstream; }; #endif // defined(ENABLE_HTTP3) // custom error page struct ErrorPage { // not NULL-terminated std::vector content; // 0 is special value, and it matches all HTTP status code. unsigned int http_status; }; struct HttpConfig { struct { // obfuscated value used in "by" parameter of Forwarded header // field. This is only used when user defined static obfuscated // string is provided. std::string_view by_obfuscated; // bitwise-OR of one or more of shrpx_forwarded_param values. uint32_t params; // type of value recorded in "by" parameter of Forwarded header // field. ForwardedNode by_node_type; // type of value recorded in "for" parameter of Forwarded header // field. ForwardedNode for_node_type; bool strip_incoming; } forwarded; struct { bool add; bool strip_incoming; } xff; struct { bool add; bool strip_incoming; } xfp; struct { bool strip_incoming; } early_data; struct { ev_tstamp header; } timeout; std::vector altsvcs; // altsvcs serialized in a wire format. std::string_view altsvc_header_value; std::vector http2_altsvcs; // http2_altsvcs serialized in a wire format. std::string_view http2_altsvc_header_value; std::vector error_pages; HeaderRefs add_request_headers; HeaderRefs add_response_headers; std::string_view server_name; // Port number which appears in Location header field when https // redirect is made. std::string_view redirect_https_port; size_t request_header_field_buffer; size_t max_request_header_fields; size_t response_header_field_buffer; size_t max_response_header_fields; size_t max_requests; bool no_via; bool no_location_rewrite; bool no_host_rewrite; bool no_server_rewrite; bool require_http_scheme; }; struct Http2Config { struct { struct { struct { std::string_view request_header_file; std::string_view response_header_file; FILE *request_header; FILE *response_header; } dump; bool frame_debug; } debug; struct { ev_tstamp settings; } timeout; nghttp2_option *option; nghttp2_option *alt_mode_option; nghttp2_session_callbacks *callbacks; size_t max_concurrent_streams; size_t encoder_dynamic_table_size; size_t decoder_dynamic_table_size; int32_t window_size; int32_t connection_window_size; bool optimize_write_buffer_size; bool optimize_window_size; } upstream; struct { struct { ev_tstamp settings; } timeout; nghttp2_option *option; nghttp2_session_callbacks *callbacks; size_t encoder_dynamic_table_size; size_t decoder_dynamic_table_size; int32_t window_size; int32_t connection_window_size; size_t max_concurrent_streams; } downstream; struct { ev_tstamp stream_read; ev_tstamp stream_write; } timeout; bool no_cookie_crumbling; bool no_server_push; }; struct LoggingConfig { struct { std::vector format; std::string_view file; // Send accesslog to syslog, ignoring accesslog_file. bool syslog; // Write accesslog when response headers are received from // backend, rather than response body is received and sent. bool write_early; } access; struct { std::string_view file; // Send errorlog to syslog, ignoring errorlog_file. bool syslog; } error; int syslog_facility; int severity; }; struct RateLimitConfig { size_t rate; size_t burst; }; // Wildcard host pattern routing. We strips left most '*' from host // field. router includes all path patterns sharing the same wildcard // host. struct WildcardPattern { WildcardPattern(const std::string_view &host) : host(host) {} // This might not be NULL terminated. Currently it is only used for // comparison. std::string_view host; Router router; }; // Configuration to select backend to forward request struct RouterConfig { Router router; // Router for reversed wildcard hosts. Since this router has // wildcard hosts reversed without '*', one should call match() // function with reversed host stripping last character. This is // because we require at least one character must match for '*'. // The index stored in this router is index of wildcard_patterns. Router rev_wildcard_router; std::vector wildcard_patterns; }; struct DownstreamConfig { DownstreamConfig() : balloc(1024, 1024), timeout{}, addr_group_catch_all{0}, connections_per_host{0}, connections_per_frontend{0}, request_buffer_size{0}, response_buffer_size{0}, family{0} {} DownstreamConfig(const DownstreamConfig &) = delete; DownstreamConfig(DownstreamConfig &&) = delete; DownstreamConfig &operator=(const DownstreamConfig &) = delete; DownstreamConfig &operator=(DownstreamConfig &&) = delete; // Allocator to allocate memory for Downstream configuration. Since // we may swap around DownstreamConfig in arbitrary times with API // calls, we should use their own allocator instead of per Config // allocator. BlockAllocator balloc; struct { ev_tstamp read; ev_tstamp write; ev_tstamp idle_read; ev_tstamp connect; // The maximum backoff while checking health check for offline // backend or while detaching failed backend from load balancing // group temporarily. ev_tstamp max_backoff; } timeout; RouterConfig router; std::vector addr_groups; // The index of catch-all group in downstream_addr_groups. size_t addr_group_catch_all; size_t connections_per_host; size_t connections_per_frontend; size_t request_buffer_size; size_t response_buffer_size; // Address family of backend connection. One of either AF_INET, // AF_INET6 or AF_UNSPEC. This is ignored if backend connection // is made via Unix domain socket. int family; }; struct ConnectionConfig { struct { struct { ev_tstamp sleep; } timeout; // address of frontend acceptors std::vector addrs; int backlog; // TCP fastopen. If this is positive, it is passed to // setsockopt() along with TCP_FASTOPEN. int fastopen; } listener; #ifdef ENABLE_HTTP3 struct { std::vector addrs; } quic_listener; #endif // defined(ENABLE_HTTP3) struct { struct { ev_tstamp http2_idle; ev_tstamp http3_idle; ev_tstamp write; ev_tstamp idle; } timeout; struct { RateLimitConfig read; RateLimitConfig write; } ratelimit; size_t worker_connections; // Deprecated. See UpstreamAddr.accept_proxy_protocol. bool accept_proxy_protocol; } upstream; std::shared_ptr downstream; }; struct APIConfig { // Maximum request body size for one API request size_t max_request_body; // true if at least one of UpstreamAddr has api enabled bool enabled; }; struct DNSConfig { struct { ev_tstamp cache; ev_tstamp lookup; } timeout; // The number of tries name resolver makes before abandoning // request. size_t max_try; }; struct Config { Config() : balloc(4096, 4096), downstream_http_proxy{}, http{}, http2{}, tls{}, #ifdef ENABLE_HTTP3 quic{}, #endif // defined(ENABLE_HTTP3) logging{}, conn{}, api{}, dns{}, config_revision{0}, num_worker{0}, padding{0}, rlimit_nofile{0}, rlimit_memlock{0}, uid{0}, gid{0}, pid{0}, verbose{false}, daemon{false}, http2_proxy{false}, single_process{false}, single_thread{false}, ignore_per_pattern_mruby_error{false}, ev_loop_flags{0}, max_worker_processes{0}, worker_process_grace_shutdown_period{0.} { } ~Config(); Config(Config &&) = delete; Config(const Config &&) = delete; Config &operator=(Config &&) = delete; Config &operator=(const Config &&) = delete; // Allocator to allocate memory for this object except for // DownstreamConfig. Currently, it is used to allocate memory for // strings. BlockAllocator balloc; HttpProxy downstream_http_proxy; HttpConfig http; Http2Config http2; TLSConfig tls; #ifdef ENABLE_HTTP3 QUICConfig quic; Http3Config http3; #endif // defined(ENABLE_HTTP3) LoggingConfig logging; ConnectionConfig conn; APIConfig api; DNSConfig dns; std::string_view pid_file; std::string_view conf_path; std::string_view user; std::string_view mruby_file; // The revision of configuration which is opaque string, and changes // on each configuration reloading. This does not change on // backendconfig API call. This value is returned in health check // as "nghttpx-conf-rev" response header field. The external // program can check this value to know whether reloading has // completed or not. uint64_t config_revision; size_t num_worker; size_t padding; size_t rlimit_nofile; size_t rlimit_memlock; uid_t uid; gid_t gid; pid_t pid; bool verbose; bool daemon; bool http2_proxy; // Run nghttpx in single process mode. With this mode, signal // handling is omitted. bool single_process; bool single_thread; // Ignore mruby compile error for per-pattern mruby script. bool ignore_per_pattern_mruby_error; // flags passed to ev_default_loop() and ev_loop_new() uint32_t ev_loop_flags; size_t max_worker_processes; ev_tstamp worker_process_grace_shutdown_period; }; const Config *get_config(); Config *mod_config(); // Replaces the current config with given |new_config|. The old config is // returned. std::unique_ptr replace_config(std::unique_ptr new_config); void create_config(); // generated by gennghttpxfun.py enum { SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL, SHRPX_OPTID_ACCESSLOG_FILE, SHRPX_OPTID_ACCESSLOG_FORMAT, SHRPX_OPTID_ACCESSLOG_SYSLOG, SHRPX_OPTID_ACCESSLOG_WRITE_EARLY, SHRPX_OPTID_ADD_FORWARDED, SHRPX_OPTID_ADD_REQUEST_HEADER, SHRPX_OPTID_ADD_RESPONSE_HEADER, SHRPX_OPTID_ADD_X_FORWARDED_FOR, SHRPX_OPTID_ALPN_LIST, SHRPX_OPTID_ALTSVC, SHRPX_OPTID_API_MAX_REQUEST_BODY, SHRPX_OPTID_BACKEND, SHRPX_OPTID_BACKEND_ADDRESS_FAMILY, SHRPX_OPTID_BACKEND_CONNECT_TIMEOUT, SHRPX_OPTID_BACKEND_CONNECTIONS_PER_FRONTEND, SHRPX_OPTID_BACKEND_CONNECTIONS_PER_HOST, SHRPX_OPTID_BACKEND_HTTP_PROXY_URI, SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND, SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_HOST, SHRPX_OPTID_BACKEND_HTTP1_TLS, SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_BITS, SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE, SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER, SHRPX_OPTID_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE, SHRPX_OPTID_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE, SHRPX_OPTID_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS, SHRPX_OPTID_BACKEND_HTTP2_SETTINGS_TIMEOUT, SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS, SHRPX_OPTID_BACKEND_HTTP2_WINDOW_SIZE, SHRPX_OPTID_BACKEND_IPV4, SHRPX_OPTID_BACKEND_IPV6, SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT, SHRPX_OPTID_BACKEND_MAX_BACKOFF, SHRPX_OPTID_BACKEND_NO_TLS, SHRPX_OPTID_BACKEND_READ_TIMEOUT, SHRPX_OPTID_BACKEND_REQUEST_BUFFER, SHRPX_OPTID_BACKEND_RESPONSE_BUFFER, SHRPX_OPTID_BACKEND_TLS, SHRPX_OPTID_BACKEND_TLS_SNI_FIELD, SHRPX_OPTID_BACKEND_WRITE_TIMEOUT, SHRPX_OPTID_BACKLOG, SHRPX_OPTID_CACERT, SHRPX_OPTID_CERTIFICATE_FILE, SHRPX_OPTID_CIPHERS, SHRPX_OPTID_CLIENT, SHRPX_OPTID_CLIENT_CERT_FILE, SHRPX_OPTID_CLIENT_CIPHERS, SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST, SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLOCK_LIST, SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE, SHRPX_OPTID_CLIENT_PROXY, SHRPX_OPTID_CLIENT_PSK_SECRETS, SHRPX_OPTID_CONF, SHRPX_OPTID_DAEMON, SHRPX_OPTID_DH_PARAM_FILE, SHRPX_OPTID_DNS_CACHE_TIMEOUT, SHRPX_OPTID_DNS_LOOKUP_TIMEOUT, SHRPX_OPTID_DNS_MAX_TRY, SHRPX_OPTID_ECDH_CURVES, SHRPX_OPTID_ERROR_PAGE, SHRPX_OPTID_ERRORLOG_FILE, SHRPX_OPTID_ERRORLOG_SYSLOG, SHRPX_OPTID_FASTOPEN, SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE, SHRPX_OPTID_FORWARDED_BY, SHRPX_OPTID_FORWARDED_FOR, SHRPX_OPTID_FRONTEND, SHRPX_OPTID_FRONTEND_FRAME_DEBUG, SHRPX_OPTID_FRONTEND_HEADER_TIMEOUT, SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS, SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE, SHRPX_OPTID_FRONTEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE, SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER, SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER, SHRPX_OPTID_FRONTEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE, SHRPX_OPTID_FRONTEND_HTTP2_IDLE_TIMEOUT, SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS, SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE, SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE, SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT, SHRPX_OPTID_FRONTEND_HTTP2_SETTINGS_TIMEOUT, SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS, SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE, SHRPX_OPTID_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE, SHRPX_OPTID_FRONTEND_HTTP3_IDLE_TIMEOUT, SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS, SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE, SHRPX_OPTID_FRONTEND_HTTP3_MAX_WINDOW_SIZE, SHRPX_OPTID_FRONTEND_HTTP3_READ_TIMEOUT, SHRPX_OPTID_FRONTEND_HTTP3_WINDOW_SIZE, SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT, SHRPX_OPTID_FRONTEND_MAX_REQUESTS, SHRPX_OPTID_FRONTEND_NO_TLS, SHRPX_OPTID_FRONTEND_QUIC_CONGESTION_CONTROLLER, SHRPX_OPTID_FRONTEND_QUIC_DEBUG_LOG, SHRPX_OPTID_FRONTEND_QUIC_EARLY_DATA, SHRPX_OPTID_FRONTEND_QUIC_IDLE_TIMEOUT, SHRPX_OPTID_FRONTEND_QUIC_INITIAL_RTT, SHRPX_OPTID_FRONTEND_QUIC_QLOG_DIR, SHRPX_OPTID_FRONTEND_QUIC_REQUIRE_TOKEN, SHRPX_OPTID_FRONTEND_QUIC_SECRET_FILE, SHRPX_OPTID_FRONTEND_READ_TIMEOUT, SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT, SHRPX_OPTID_GROUPS, SHRPX_OPTID_HEADER_FIELD_BUFFER, SHRPX_OPTID_HOST_REWRITE, SHRPX_OPTID_HTTP2_ALTSVC, SHRPX_OPTID_HTTP2_BRIDGE, SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS, SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING, SHRPX_OPTID_HTTP2_PROXY, SHRPX_OPTID_IGNORE_PER_PATTERN_MRUBY_ERROR, SHRPX_OPTID_INCLUDE, SHRPX_OPTID_INSECURE, SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT, SHRPX_OPTID_LOG_LEVEL, SHRPX_OPTID_MAX_HEADER_FIELDS, SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS, SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS, SHRPX_OPTID_MAX_WORKER_PROCESSES, SHRPX_OPTID_MRUBY_FILE, SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO, SHRPX_OPTID_NO_HOST_REWRITE, SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST, SHRPX_OPTID_NO_HTTP2_CIPHER_BLOCK_LIST, SHRPX_OPTID_NO_KQUEUE, SHRPX_OPTID_NO_LOCATION_REWRITE, SHRPX_OPTID_NO_OCSP, SHRPX_OPTID_NO_QUIC_BPF, SHRPX_OPTID_NO_SERVER_PUSH, SHRPX_OPTID_NO_SERVER_REWRITE, SHRPX_OPTID_NO_STRIP_INCOMING_EARLY_DATA, SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO, SHRPX_OPTID_NO_VERIFY_OCSP, SHRPX_OPTID_NO_VIA, SHRPX_OPTID_NPN_LIST, SHRPX_OPTID_OCSP_STARTUP, SHRPX_OPTID_OCSP_UPDATE_INTERVAL, SHRPX_OPTID_PADDING, SHRPX_OPTID_PID_FILE, SHRPX_OPTID_PRIVATE_KEY_FILE, SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE, SHRPX_OPTID_PSK_SECRETS, SHRPX_OPTID_QUIC_BPF_PROGRAM_FILE, SHRPX_OPTID_QUIC_SERVER_ID, SHRPX_OPTID_READ_BURST, SHRPX_OPTID_READ_RATE, SHRPX_OPTID_REDIRECT_HTTPS_PORT, SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER, SHRPX_OPTID_REQUIRE_HTTP_SCHEME, SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER, SHRPX_OPTID_RLIMIT_MEMLOCK, SHRPX_OPTID_RLIMIT_NOFILE, SHRPX_OPTID_SERVER_NAME, SHRPX_OPTID_SINGLE_PROCESS, SHRPX_OPTID_SINGLE_THREAD, SHRPX_OPTID_STREAM_READ_TIMEOUT, SHRPX_OPTID_STREAM_WRITE_TIMEOUT, SHRPX_OPTID_STRIP_INCOMING_FORWARDED, SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR, SHRPX_OPTID_SUBCERT, SHRPX_OPTID_SYSLOG_FACILITY, SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT, SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD, SHRPX_OPTID_TLS_KTLS, SHRPX_OPTID_TLS_MAX_EARLY_DATA, SHRPX_OPTID_TLS_MAX_PROTO_VERSION, SHRPX_OPTID_TLS_MIN_PROTO_VERSION, SHRPX_OPTID_TLS_NO_POSTPONE_EARLY_DATA, SHRPX_OPTID_TLS_PROTO_LIST, SHRPX_OPTID_TLS_SCT_DIR, SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED, SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY, SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE, SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE, SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_TLS, SHRPX_OPTID_TLS_TICKET_KEY_CIPHER, SHRPX_OPTID_TLS_TICKET_KEY_FILE, SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED, SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY, SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE, SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL, SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL, SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY, SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE, SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS, SHRPX_OPTID_TLS13_CIPHERS, SHRPX_OPTID_TLS13_CLIENT_CIPHERS, SHRPX_OPTID_USER, SHRPX_OPTID_VERIFY_CLIENT, SHRPX_OPTID_VERIFY_CLIENT_CACERT, SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED, SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS, SHRPX_OPTID_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD, SHRPX_OPTID_WORKER_READ_BURST, SHRPX_OPTID_WORKER_READ_RATE, SHRPX_OPTID_WORKER_WRITE_BURST, SHRPX_OPTID_WORKER_WRITE_RATE, SHRPX_OPTID_WORKERS, SHRPX_OPTID_WRITE_BURST, SHRPX_OPTID_WRITE_RATE, SHRPX_OPTID_MAXIDX, }; // Looks up token for given option name |name|. int option_lookup_token(const std::string_view &name); // Parses option name |opt| and value |optarg|. The results are // stored into the object pointed by |config|. This function returns 0 // if it succeeds, or -1. The |included_set| contains the all paths // already included while processing this configuration, to avoid loop // in --include option. The |pattern_addr_indexer| contains a pair of // pattern of backend, and its index in DownstreamConfig::addr_groups. // It is introduced to speed up loading configuration file with lots // of backends. int parse_config( Config *config, const std::string_view &opt, const std::string_view &optarg, std::unordered_set &included_set, std::unordered_map &pattern_addr_indexer); // Similar to parse_config() above, but additional |optid| which // should be the return value of option_lookup_token(opt). int parse_config( Config *config, int optid, const std::string_view &opt, const std::string_view &optarg, std::unordered_set &included_set, std::unordered_map &pattern_addr_indexer); // Loads configurations from |filename| and stores them in |config|. // This function returns 0 if it succeeds, or -1. See parse_config() // for |include_set|. int load_config( Config *config, const char *filename, std::unordered_set &include_set, std::unordered_map &pattern_addr_indexer); // Parses header field in |optarg|. We expect header field is formed // like "NAME: VALUE". We require that NAME is non empty string. ":" // is allowed at the start of the NAME, but NAME == ":" is not // allowed. This function returns pair of NAME and VALUE. HeaderRefs::value_type parse_header(BlockAllocator &balloc, const std::string_view &optarg); std::vector parse_log_format(BlockAllocator &balloc, const std::string_view &optarg); // Returns string for syslog |facility|. std::string_view str_syslog_facility(int facility); // Returns integer value of syslog |facility| string. int int_syslog_facility(const std::string_view &strfacility); FILE *open_file_for_write(const char *filename); // Reads TLS ticket key file in |files| and returns TicketKey which // stores read key data. The given |cipher| and |hmac| determine the // expected file size. This function returns TicketKey if it // succeeds, or nullptr. std::unique_ptr read_tls_ticket_key_file(const std::vector &files, const EVP_CIPHER *cipher, const EVP_MD *hmac); #ifdef ENABLE_HTTP3 std::shared_ptr read_quic_secret_file(const std::string_view &path); #endif // defined(ENABLE_HTTP3) // Returns string representation of |proto|. std::string_view strproto(Proto proto); int configure_downstream_group(Config *config, bool http2_proxy, bool numeric_addr_only, const TLSConfig &tlsconf); int resolve_hostname(Address *addr, const char *hostname, uint16_t port, int family, int additional_flags = 0); } // namespace shrpx #endif // !defined(SHRPX_CONFIG_H) nghttp2-1.68.0/src/PaxHeaders/app_helper.cc0000644000000000000000000000013215077107270015475 xustar0030 mtime=1761382072.983444185 30 atime=1761382106.071311013 30 ctime=1761382109.067300476 nghttp2-1.68.0/src/app_helper.cc0000644000175100017510000003306015077107270016067 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #ifdef HAVE_SYS_SOCKET_H # include #endif // defined(HAVE_SYS_SOCKET_H) #ifdef HAVE_NETDB_H # include #endif // defined(HAVE_NETDB_H) #ifdef HAVE_UNISTD_H # include #endif // defined(HAVE_UNISTD_H) #ifdef HAVE_FCNTL_H # include #endif // defined(HAVE_FCNTL_H) #ifdef HAVE_NETINET_IN_H # include #endif // defined(HAVE_NETINET_IN_H) #include #include #include #include #include #include #include #include #include #include #include #include #include "app_helper.h" #include "util.h" #include "http2.h" #include "template.h" namespace nghttp2 { namespace { const char *strsettingsid(int32_t id) { switch (id) { case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: return "SETTINGS_HEADER_TABLE_SIZE"; case NGHTTP2_SETTINGS_ENABLE_PUSH: return "SETTINGS_ENABLE_PUSH"; case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: return "SETTINGS_MAX_CONCURRENT_STREAMS"; case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: return "SETTINGS_INITIAL_WINDOW_SIZE"; case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: return "SETTINGS_MAX_FRAME_SIZE"; case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: return "SETTINGS_MAX_HEADER_LIST_SIZE"; case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: return "SETTINGS_ENABLE_CONNECT_PROTOCOL"; case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES: return "SETTINGS_NO_RFC7540_PRIORITIES"; default: return "UNKNOWN"; } } } // namespace namespace { std::string strframetype(uint8_t type) { switch (type) { case NGHTTP2_DATA: return "DATA"; case NGHTTP2_HEADERS: return "HEADERS"; case NGHTTP2_PRIORITY: return "PRIORITY"; case NGHTTP2_RST_STREAM: return "RST_STREAM"; case NGHTTP2_SETTINGS: return "SETTINGS"; case NGHTTP2_PUSH_PROMISE: return "PUSH_PROMISE"; case NGHTTP2_PING: return "PING"; case NGHTTP2_GOAWAY: return "GOAWAY"; case NGHTTP2_WINDOW_UPDATE: return "WINDOW_UPDATE"; case NGHTTP2_ALTSVC: return "ALTSVC"; case NGHTTP2_ORIGIN: return "ORIGIN"; case NGHTTP2_PRIORITY_UPDATE: return "PRIORITY_UPDATE"; } std::string s = "extension(0x"; s += util::format_hex(std::span{&type, 1}); s += ')'; return s; } } // namespace namespace { bool color_output = false; } // namespace void set_color_output(bool f) { color_output = f; } namespace { FILE *outfile = stdout; } // namespace void set_output(FILE *file) { outfile = file; } namespace { void print_frame_attr_indent() { fprintf(outfile, " "); } } // namespace namespace { const char *ansi_esc(const char *code) { return color_output ? code : ""; } } // namespace namespace { const char *ansi_escend() { return color_output ? "\033[0m" : ""; } } // namespace namespace { void print_nv(nghttp2_nv *nv) { fprintf(outfile, "%s%s%s: %s\n", ansi_esc("\033[1;34m"), nv->name, ansi_escend(), nv->value); } } // namespace namespace { void print_nv(nghttp2_nv *nva, size_t nvlen) { auto end = nva + nvlen; for (; nva != end; ++nva) { print_frame_attr_indent(); print_nv(nva); } } } // namespace void print_timer() { auto millis = get_timer(); fprintf(outfile, "%s[%3ld.%03ld]%s", ansi_esc("\033[33m"), (long int)(millis.count() / 1000), (long int)(millis.count() % 1000), ansi_escend()); } namespace { void print_frame_hd(const nghttp2_frame_hd &hd) { fprintf(outfile, "\n", hd.length, hd.flags, hd.stream_id); } } // namespace namespace { void print_flags(const nghttp2_frame_hd &hd) { std::string s; switch (hd.type) { case NGHTTP2_DATA: if (hd.flags & NGHTTP2_FLAG_END_STREAM) { s += "END_STREAM"; } if (hd.flags & NGHTTP2_FLAG_PADDED) { if (!s.empty()) { s += " | "; } s += "PADDED"; } break; case NGHTTP2_HEADERS: if (hd.flags & NGHTTP2_FLAG_END_STREAM) { s += "END_STREAM"; } if (hd.flags & NGHTTP2_FLAG_END_HEADERS) { if (!s.empty()) { s += " | "; } s += "END_HEADERS"; } if (hd.flags & NGHTTP2_FLAG_PADDED) { if (!s.empty()) { s += " | "; } s += "PADDED"; } if (hd.flags & NGHTTP2_FLAG_PRIORITY) { if (!s.empty()) { s += " | "; } s += "PRIORITY"; } break; case NGHTTP2_PRIORITY: break; case NGHTTP2_SETTINGS: if (hd.flags & NGHTTP2_FLAG_ACK) { s += "ACK"; } break; case NGHTTP2_PUSH_PROMISE: if (hd.flags & NGHTTP2_FLAG_END_HEADERS) { s += "END_HEADERS"; } if (hd.flags & NGHTTP2_FLAG_PADDED) { if (!s.empty()) { s += " | "; } s += "PADDED"; } break; case NGHTTP2_PING: if (hd.flags & NGHTTP2_FLAG_ACK) { s += "ACK"; } break; } fprintf(outfile, "; %s\n", s.c_str()); } } // namespace enum print_type { PRINT_SEND, PRINT_RECV }; namespace { const char *frame_name_ansi_esc(print_type ptype) { return ansi_esc(ptype == PRINT_SEND ? "\033[1;35m" : "\033[1;36m"); } } // namespace namespace { void print_frame(print_type ptype, const nghttp2_frame *frame) { fprintf(outfile, "%s%s%s frame ", frame_name_ansi_esc(ptype), strframetype(frame->hd.type).c_str(), ansi_escend()); print_frame_hd(frame->hd); if (frame->hd.flags) { print_frame_attr_indent(); print_flags(frame->hd); } switch (frame->hd.type) { case NGHTTP2_DATA: if (frame->data.padlen > 0) { print_frame_attr_indent(); fprintf(outfile, "(padlen=%zu)\n", frame->data.padlen); } break; case NGHTTP2_HEADERS: print_frame_attr_indent(); fprintf(outfile, "(padlen=%zu", frame->headers.padlen); if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { fprintf(outfile, ", dep_stream_id=%d, weight=%u, exclusive=%d", frame->headers.pri_spec.stream_id, frame->headers.pri_spec.weight, frame->headers.pri_spec.exclusive); } fprintf(outfile, ")\n"); switch (frame->headers.cat) { case NGHTTP2_HCAT_REQUEST: print_frame_attr_indent(); fprintf(outfile, "; Open new stream\n"); break; case NGHTTP2_HCAT_RESPONSE: print_frame_attr_indent(); fprintf(outfile, "; First response header\n"); break; case NGHTTP2_HCAT_PUSH_RESPONSE: print_frame_attr_indent(); fprintf(outfile, "; First push response header\n"); break; default: break; } print_nv(frame->headers.nva, frame->headers.nvlen); break; case NGHTTP2_PRIORITY: print_frame_attr_indent(); fprintf(outfile, "(dep_stream_id=%d, weight=%u, exclusive=%d)\n", frame->priority.pri_spec.stream_id, frame->priority.pri_spec.weight, frame->priority.pri_spec.exclusive); break; case NGHTTP2_RST_STREAM: print_frame_attr_indent(); fprintf(outfile, "(error_code=%s(0x%02x))\n", nghttp2_http2_strerror(frame->rst_stream.error_code), frame->rst_stream.error_code); break; case NGHTTP2_SETTINGS: print_frame_attr_indent(); fprintf(outfile, "(niv=%lu)\n", static_cast(frame->settings.niv)); for (size_t i = 0; i < frame->settings.niv; ++i) { print_frame_attr_indent(); fprintf(outfile, "[%s(0x%02x):%u]\n", strsettingsid(frame->settings.iv[i].settings_id), frame->settings.iv[i].settings_id, frame->settings.iv[i].value); } break; case NGHTTP2_PUSH_PROMISE: print_frame_attr_indent(); fprintf(outfile, "(padlen=%zu, promised_stream_id=%d)\n", frame->push_promise.padlen, frame->push_promise.promised_stream_id); print_nv(frame->push_promise.nva, frame->push_promise.nvlen); break; case NGHTTP2_PING: print_frame_attr_indent(); fprintf(outfile, "(opaque_data=%s)\n", util::format_hex(std::span{frame->ping.opaque_data}).c_str()); break; case NGHTTP2_GOAWAY: print_frame_attr_indent(); fprintf( outfile, "(last_stream_id=%d, error_code=%s(0x%02x), " "opaque_data(%u)=[%s])\n", frame->goaway.last_stream_id, nghttp2_http2_strerror(frame->goaway.error_code), frame->goaway.error_code, static_cast(frame->goaway.opaque_data_len), util::ascii_dump(frame->goaway.opaque_data, frame->goaway.opaque_data_len) .c_str()); break; case NGHTTP2_WINDOW_UPDATE: print_frame_attr_indent(); fprintf(outfile, "(window_size_increment=%d)\n", frame->window_update.window_size_increment); break; case NGHTTP2_ALTSVC: { auto altsvc = static_cast(frame->ext.payload); print_frame_attr_indent(); fprintf(outfile, "(origin=[%.*s], altsvc_field_value=[%.*s])\n", static_cast(altsvc->origin_len), altsvc->origin, static_cast(altsvc->field_value_len), altsvc->field_value); break; } case NGHTTP2_ORIGIN: { auto origin = static_cast(frame->ext.payload); for (size_t i = 0; i < origin->nov; ++i) { auto ent = &origin->ov[i]; print_frame_attr_indent(); fprintf(outfile, "[%.*s]\n", (int)ent->origin_len, ent->origin); } break; } case NGHTTP2_PRIORITY_UPDATE: { auto priority_update = static_cast(frame->ext.payload); print_frame_attr_indent(); fprintf(outfile, "(prioritized_stream_id=%d, priority_field_value=[%.*s])\n", priority_update->stream_id, static_cast(priority_update->field_value_len), priority_update->field_value); break; } default: break; } } } // namespace int verbose_on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *user_data) { nghttp2_nv nv = {const_cast(name), const_cast(value), namelen, valuelen}; print_timer(); fprintf(outfile, " recv (stream_id=%d", frame->hd.stream_id); if (flags & NGHTTP2_NV_FLAG_NO_INDEX) { fprintf(outfile, ", sensitive"); } fprintf(outfile, ") "); print_nv(&nv); fflush(outfile); return 0; } int verbose_on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { print_timer(); fprintf(outfile, " recv "); print_frame(PRINT_RECV, frame); fflush(outfile); return 0; } int verbose_on_invalid_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, void *user_data) { print_timer(); fprintf(outfile, " [INVALID; error=%s] recv ", nghttp2_strerror(lib_error_code)); print_frame(PRINT_RECV, frame); fflush(outfile); return 0; } int verbose_on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { print_timer(); fprintf(outfile, " send "); print_frame(PRINT_SEND, frame); fflush(outfile); return 0; } int verbose_on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data) { print_timer(); auto srecv = nghttp2_session_get_stream_effective_recv_data_length(session, stream_id); auto crecv = nghttp2_session_get_effective_recv_data_length(session); fprintf(outfile, " recv (stream_id=%d, length=%zu, srecv=%d, crecv=%d) DATA\n", stream_id, len, srecv, crecv); fflush(outfile); return 0; } int verbose_error_callback(nghttp2_session *session, int lib_error_code, const char *msg, size_t len, void *user_data) { print_timer(); fprintf(outfile, " [ERROR] %.*s\n", (int)len, msg); fflush(outfile); return 0; } namespace { std::chrono::steady_clock::time_point base_tv; } // namespace void reset_timer() { base_tv = std::chrono::steady_clock::now(); } std::chrono::milliseconds get_timer() { return time_delta(std::chrono::steady_clock::now(), base_tv); } std::chrono::steady_clock::time_point get_time() { return std::chrono::steady_clock::now(); } } // namespace nghttp2 nghttp2-1.68.0/src/PaxHeaders/nghttp2_gzip_test.h0000644000000000000000000000013215077107270016676 xustar0030 mtime=1761382072.989444157 30 atime=1761382106.267310149 30 ctime=1761382109.264299907 nghttp2-1.68.0/src/nghttp2_gzip_test.h0000644000175100017510000000310615077107270017266 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_GZIP_TEST_H #define NGHTTP2_GZIP_TEST_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #ifdef __cplusplus extern "C" { #endif /* defined(__cplusplus) */ #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" extern const MunitSuite gzip_suite; munit_void_test_decl(test_nghttp2_gzip_inflate) #ifdef __cplusplus } #endif /* defined(__cplusplus) */ #endif /* !defined(NGHTTP2_GZIP_TEST_H) */ nghttp2-1.68.0/src/PaxHeaders/shrpx_quic.h0000644000000000000000000000013215077107270015405 xustar0030 mtime=1761382072.998444116 30 atime=1761382106.193310475 30 ctime=1761382109.189300124 nghttp2-1.68.0/src/shrpx_quic.h0000644000175100017510000001436115077107270016002 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2021 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_QUIC_H #define SHRPX_QUIC_H #include "shrpx.h" #include #include #include #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include #include "siphash.h" #include "template.h" #include "network.h" using namespace nghttp2; namespace shrpx { std::span generate_siphash_key(); } // namespace shrpx namespace std { template <> struct hash { hash() { std::ranges::copy(shrpx::generate_siphash_key(), std::ranges::begin(key)); } std::size_t operator()(const ngtcp2_cid &cid) const noexcept { return static_cast(siphash24(key, {cid.data, cid.datalen})); } std::array key; }; } // namespace std bool operator==(const ngtcp2_cid &lhs, const ngtcp2_cid &rhs); namespace shrpx { struct UpstreamAddr; struct QUICKeyingMaterials; struct QUICKeyingMaterial; inline constexpr size_t SHRPX_QUIC_CID_WORKER_ID_OFFSET = 1; inline constexpr size_t SHRPX_QUIC_SERVER_IDLEN = 4; inline constexpr size_t SHRPX_QUIC_SOCK_IDLEN = 4; inline constexpr size_t SHRPX_QUIC_WORKER_IDLEN = SHRPX_QUIC_SERVER_IDLEN + SHRPX_QUIC_SOCK_IDLEN; inline constexpr size_t SHRPX_QUIC_CLIENT_IDLEN = 8; inline constexpr size_t SHRPX_QUIC_DECRYPTED_DCIDLEN = SHRPX_QUIC_WORKER_IDLEN + SHRPX_QUIC_CLIENT_IDLEN; inline constexpr size_t SHRPX_QUIC_SCIDLEN = SHRPX_QUIC_CID_WORKER_ID_OFFSET + SHRPX_QUIC_DECRYPTED_DCIDLEN; inline constexpr size_t SHRPX_QUIC_CID_ENCRYPTION_KEYLEN = 16; inline constexpr size_t SHRPX_QUIC_CONN_CLOSE_PKTLEN = 256; inline constexpr size_t SHRPX_QUIC_STATELESS_RESET_BURST = 100; inline constexpr size_t SHRPX_QUIC_SECRET_RESERVEDLEN = 4; inline constexpr size_t SHRPX_QUIC_SECRETLEN = 32; inline constexpr size_t SHRPX_QUIC_SALTLEN = 32; inline constexpr uint8_t SHRPX_QUIC_DCID_KM_ID_MASK = 0xe0; struct WorkerID { union { struct { uint32_t server; uint16_t worker_process; uint16_t thread; }; uint64_t worker; }; }; static_assert(sizeof(WorkerID) == SHRPX_QUIC_WORKER_IDLEN, "WorkerID length assertion failure"); inline bool operator==(const WorkerID &lhd, const WorkerID &rhd) { return lhd.worker == rhd.worker; } inline bool operator!=(const WorkerID &lhd, const WorkerID &rhd) { return lhd.worker != rhd.worker; } struct ConnectionID { WorkerID worker; uint64_t client; }; ngtcp2_tstamp quic_timestamp(); int quic_send_packet(const UpstreamAddr *faddr, const sockaddr *remote_sa, socklen_t remote_salen, const sockaddr *local_sa, socklen_t local_salen, const ngtcp2_pkt_info &pi, std::span data, size_t gso_size); int generate_quic_retry_connection_id(ngtcp2_cid &cid, uint32_t server_id, uint8_t km_id, EVP_CIPHER_CTX *ctx); int generate_quic_connection_id(ngtcp2_cid &cid, const WorkerID &wid, uint8_t km_id, EVP_CIPHER_CTX *ctx); int encrypt_quic_connection_id(uint8_t *dest, const uint8_t *src, EVP_CIPHER_CTX *ctx); int decrypt_quic_connection_id(ConnectionID &dest, const uint8_t *src, EVP_CIPHER_CTX *ctx); int generate_quic_hashed_connection_id(ngtcp2_cid &dest, const Address &remote_addr, const Address &local_addr, const ngtcp2_cid &cid); int generate_quic_stateless_reset_token(uint8_t *token, const ngtcp2_cid &cid, const uint8_t *secret, size_t secretlen); std::optional> generate_retry_token(std::span token, uint32_t version, const sockaddr *sa, socklen_t salen, const ngtcp2_cid &retry_scid, const ngtcp2_cid &odcid, std::span secret); int verify_retry_token(ngtcp2_cid &odcid, std::span token, uint32_t version, const ngtcp2_cid &dcid, const sockaddr *sa, socklen_t salen, std::span secret); std::optional> generate_token(std::span token, const sockaddr *sa, size_t salen, std::span secret, uint8_t km_id); int verify_token(std::span token, const sockaddr *sa, socklen_t salen, std::span secret); int generate_quic_connection_id_encryption_key(std::span key, std::span secret, std::span salt); const QUICKeyingMaterial * select_quic_keying_material(const QUICKeyingMaterials &qkms, uint8_t km_id); } // namespace shrpx #endif // !defined(SHRPX_QUIC_H) nghttp2-1.68.0/src/PaxHeaders/nghttp2_gzip.h0000644000000000000000000000013215077107270015637 xustar0030 mtime=1761382072.989444157 30 atime=1761382106.230310312 30 ctime=1761382109.226300017 nghttp2-1.68.0/src/nghttp2_gzip.h0000644000175100017510000000745215077107270016237 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_GZIP_H #define NGHTTP2_GZIP_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include #include #ifdef __cplusplus extern "C" { #endif /* defined(__cplusplus) */ /** * @struct * * The gzip stream to inflate data. */ typedef struct { z_stream zst; int8_t finished; } nghttp2_gzip; /** * @function * * A helper function to set up a per request gzip stream to inflate * data. * * This function returns 0 if it succeeds, or -1. */ int nghttp2_gzip_inflate_new(nghttp2_gzip **inflater_ptr); /** * @function * * Frees the inflate stream. The |inflater| may be ``NULL``. */ void nghttp2_gzip_inflate_del(nghttp2_gzip *inflater); /** * @function * * Inflates data in |in| with the length |*inlen_ptr| and stores the * inflated data to |out| which has allocated size at least * |*outlen_ptr|. On return, |*outlen_ptr| is updated to represent * the number of data written in |out|. Similarly, |*inlen_ptr| is * updated to represent the number of input bytes processed. * * This function returns 0 if it succeeds, or -1. * * The example follows:: * * void on_data_chunk_recv_callback(nghttp2_session *session, * uint8_t flags, * int32_t stream_id, * const uint8_t *data, size_t len, * void *user_data) * { * ... * req = nghttp2_session_get_stream_user_data(session, stream_id); * nghttp2_gzip *inflater = req->inflater; * while(len > 0) { * uint8_t out[MAX_OUTLEN]; * size_t outlen = MAX_OUTLEN; * size_t tlen = len; * int rv; * rv = nghttp2_gzip_inflate(inflater, out, &outlen, data, &tlen); * if(rv != 0) { * nghttp2_submit_rst_stream(session, stream_id, * NGHTTP2_INTERNAL_ERROR); * break; * } * ... Do stuff ... * data += tlen; * len -= tlen; * } * .... * } */ int nghttp2_gzip_inflate(nghttp2_gzip *inflater, uint8_t *out, size_t *outlen_ptr, const uint8_t *in, size_t *inlen_ptr); /** * @function * * Returns nonzero if |inflater| sees the end of deflate stream. * After this function returns nonzero, `nghttp2_gzip_inflate()` with * |inflater| gets to return error. */ int nghttp2_gzip_inflate_finished(nghttp2_gzip *inflater); #ifdef __cplusplus } #endif /* defined(__cplusplus) */ #endif /* !defined(NGHTTP2_GZIP_H) */ nghttp2-1.68.0/src/PaxHeaders/shrpx_router_test.h0000644000000000000000000000013015077107270017021 xustar0030 mtime=1761382072.999444111 29 atime=1761382106.26031018 29 ctime=1761382109.25629993 nghttp2-1.68.0/src/shrpx_router_test.h0000644000175100017510000000313215077107270017412 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_ROUTER_TEST_H #define SHRPX_ROUTER_TEST_H #ifdef HAVE_CONFIG_H # include #endif // defined(HAVE_CONFIG_H) #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" namespace shrpx { extern const MunitSuite router_suite; munit_void_test_decl(test_shrpx_router_match) munit_void_test_decl(test_shrpx_router_match_wildcard) munit_void_test_decl(test_shrpx_router_match_prefix) } // namespace shrpx #endif // !defined(SHRPX_ROUTER_TEST_H) nghttp2-1.68.0/src/PaxHeaders/http2.cc0000644000000000000000000000013215077107270014417 xustar0030 mtime=1761382072.987444166 30 atime=1761382106.064311044 30 ctime=1761382109.060300497 nghttp2-1.68.0/src/http2.cc0000644000175100017510000014144715077107270015022 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "http2.h" #include "llhttp.h" #include "util.h" using namespace std::literals; namespace nghttp2 { namespace http2 { std::string_view get_reason_phrase(unsigned int status_code) { switch (status_code) { case 100: return "Continue"sv; case 101: return "Switching Protocols"sv; case 103: return "Early Hints"sv; case 200: return "OK"sv; case 201: return "Created"sv; case 202: return "Accepted"sv; case 203: return "Non-Authoritative Information"sv; case 204: return "No Content"sv; case 205: return "Reset Content"sv; case 206: return "Partial Content"sv; case 300: return "Multiple Choices"sv; case 301: return "Moved Permanently"sv; case 302: return "Found"sv; case 303: return "See Other"sv; case 304: return "Not Modified"sv; case 305: return "Use Proxy"sv; // case 306: return "(Unused)"sv; case 307: return "Temporary Redirect"sv; case 308: return "Permanent Redirect"sv; case 400: return "Bad Request"sv; case 401: return "Unauthorized"sv; case 402: return "Payment Required"sv; case 403: return "Forbidden"sv; case 404: return "Not Found"sv; case 405: return "Method Not Allowed"sv; case 406: return "Not Acceptable"sv; case 407: return "Proxy Authentication Required"sv; case 408: return "Request Timeout"sv; case 409: return "Conflict"sv; case 410: return "Gone"sv; case 411: return "Length Required"sv; case 412: return "Precondition Failed"sv; case 413: return "Payload Too Large"sv; case 414: return "URI Too Long"sv; case 415: return "Unsupported Media Type"sv; case 416: return "Requested Range Not Satisfiable"sv; case 417: return "Expectation Failed"sv; case 421: return "Misdirected Request"sv; case 425: // https://tools.ietf.org/html/rfc8470 return "Too Early"sv; case 426: return "Upgrade Required"sv; case 428: return "Precondition Required"sv; case 429: return "Too Many Requests"sv; case 431: return "Request Header Fields Too Large"sv; case 451: return "Unavailable For Legal Reasons"sv; case 500: return "Internal Server Error"sv; case 501: return "Not Implemented"sv; case 502: return "Bad Gateway"sv; case 503: return "Service Unavailable"sv; case 504: return "Gateway Timeout"sv; case 505: return "HTTP Version Not Supported"sv; case 511: return "Network Authentication Required"sv; default: return ""sv; } } std::string_view stringify_status(BlockAllocator &balloc, unsigned int status_code) { switch (status_code) { case 100: return "100"sv; case 101: return "101"sv; case 103: return "103"sv; case 200: return "200"sv; case 201: return "201"sv; case 202: return "202"sv; case 203: return "203"sv; case 204: return "204"sv; case 205: return "205"sv; case 206: return "206"sv; case 300: return "300"sv; case 301: return "301"sv; case 302: return "302"sv; case 303: return "303"sv; case 304: return "304"sv; case 305: return "305"sv; // case 306: return "306"sv; case 307: return "307"sv; case 308: return "308"sv; case 400: return "400"sv; case 401: return "401"sv; case 402: return "402"sv; case 403: return "403"sv; case 404: return "404"sv; case 405: return "405"sv; case 406: return "406"sv; case 407: return "407"sv; case 408: return "408"sv; case 409: return "409"sv; case 410: return "410"sv; case 411: return "411"sv; case 412: return "412"sv; case 413: return "413"sv; case 414: return "414"sv; case 415: return "415"sv; case 416: return "416"sv; case 417: return "417"sv; case 421: return "421"sv; case 426: return "426"sv; case 428: return "428"sv; case 429: return "429"sv; case 431: return "431"sv; case 451: return "451"sv; case 500: return "500"sv; case 501: return "501"sv; case 502: return "502"sv; case 503: return "503"sv; case 504: return "504"sv; case 505: return "505"sv; case 511: return "511"sv; default: return util::make_string_ref_uint(balloc, status_code); } } struct Capitalizer { template requires(std::indirectly_writable) constexpr O operator()(const std::string_view &s, O result) noexcept { using result_type = std::iter_value_t; *result++ = static_cast(util::upcase(s[0])); for (size_t i = 1; i < s.size(); ++i) { if (s[i - 1] == '-') { *result++ = static_cast(util::upcase(s[i])); } else { *result++ = static_cast(s[i]); } } return result; } }; namespace { void capitalize_long(DefaultMemchunks *buf, const std::string_view &s) { buf->append(util::upcase(s[0])); auto it = std::ranges::begin(s) + 1; for (; it != std::ranges::end(s);) { auto p = std::ranges::find(it, std::ranges::end(s), '-'); p = std::ranges::find_if(p, std::ranges::end(s), [](auto c) { return c != '-'; }); buf->append(it, p); if (p == std::ranges::end(s)) { return; } buf->append(util::upcase(*p)); it = p + 1; } } } // namespace void capitalize(DefaultMemchunks *buf, const std::string_view &s) { assert(!s.empty()); constexpr size_t max_namelen = 32; if (s.size() > max_namelen) { capitalize_long(buf, s); return; } buf->append(s.size(), std::bind_front(Capitalizer{}, s)); } Headers::value_type to_header(const std::string_view &name, const std::string_view &value, bool no_index, int32_t token) { return Header(std::string{std::ranges::begin(name), std::ranges::end(name)}, std::string{std::ranges::begin(value), std::ranges::end(value)}, no_index, token); } void add_header(Headers &nva, const std::string_view &name, const std::string_view &value, bool no_index, int32_t token) { nva.push_back(to_header(name, value, no_index, token)); } const Headers::value_type *get_header(const Headers &nva, const std::string_view &name) { const Headers::value_type *res = nullptr; for (auto &nv : nva) { if (nv.name == name) { res = &nv; } } return res; } bool non_empty_value(const HeaderRefs::value_type *nv) { return nv && !nv->value.empty(); } namespace { void copy_headers_to_nva_internal(std::vector &nva, const HeaderRefs &headers, uint8_t nv_flags, uint32_t flags) { auto it_forwarded = std::ranges::end(headers); auto it_xff = std::ranges::end(headers); auto it_xfp = std::ranges::end(headers); auto it_via = std::ranges::end(headers); for (auto it = std::ranges::begin(headers); it != std::ranges::end(headers); ++it) { auto kv = &(*it); if (kv->name.empty() || kv->name[0] == ':') { continue; } switch (kv->token) { case HD_COOKIE: case HD_CONNECTION: case HD_HOST: case HD_HTTP2_SETTINGS: case HD_KEEP_ALIVE: case HD_PROXY_CONNECTION: case HD_SERVER: case HD_TE: case HD_TRANSFER_ENCODING: case HD_UPGRADE: continue; case HD_EARLY_DATA: if (flags & HDOP_STRIP_EARLY_DATA) { continue; } break; case HD_SEC_WEBSOCKET_ACCEPT: if (flags & HDOP_STRIP_SEC_WEBSOCKET_ACCEPT) { continue; } break; case HD_SEC_WEBSOCKET_KEY: if (flags & HDOP_STRIP_SEC_WEBSOCKET_KEY) { continue; } break; case HD_FORWARDED: if (flags & HDOP_STRIP_FORWARDED) { continue; } if (it_forwarded == std::ranges::end(headers)) { it_forwarded = it; continue; } kv = &(*it_forwarded); it_forwarded = it; break; case HD_X_FORWARDED_FOR: if (flags & HDOP_STRIP_X_FORWARDED_FOR) { continue; } if (it_xff == std::ranges::end(headers)) { it_xff = it; continue; } kv = &(*it_xff); it_xff = it; break; case HD_X_FORWARDED_PROTO: if (flags & HDOP_STRIP_X_FORWARDED_PROTO) { continue; } if (it_xfp == std::ranges::end(headers)) { it_xfp = it; continue; } kv = &(*it_xfp); it_xfp = it; break; case HD_VIA: if (flags & HDOP_STRIP_VIA) { continue; } if (it_via == std::ranges::end(headers)) { it_via = it; continue; } kv = &(*it_via); it_via = it; break; } nva.push_back( make_field_flags(kv->name, kv->value, nv_flags | no_index(kv->no_index))); } } } // namespace void copy_headers_to_nva(std::vector &nva, const HeaderRefs &headers, uint32_t flags) { copy_headers_to_nva_internal(nva, headers, NGHTTP2_NV_FLAG_NONE, flags); } void copy_headers_to_nva_nocopy(std::vector &nva, const HeaderRefs &headers, uint32_t flags) { copy_headers_to_nva_internal( nva, headers, NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE, flags); } void build_http1_headers_from_headers(DefaultMemchunks *buf, const HeaderRefs &headers, uint32_t flags) { auto it_forwarded = std::ranges::end(headers); auto it_xff = std::ranges::end(headers); auto it_xfp = std::ranges::end(headers); auto it_via = std::ranges::end(headers); for (auto it = std::ranges::begin(headers); it != std::ranges::end(headers); ++it) { auto kv = &(*it); if (kv->name.empty() || kv->name[0] == ':') { continue; } switch (kv->token) { case HD_CONNECTION: case HD_COOKIE: case HD_HOST: case HD_HTTP2_SETTINGS: case HD_KEEP_ALIVE: case HD_PROXY_CONNECTION: case HD_SERVER: case HD_UPGRADE: continue; case HD_EARLY_DATA: if (flags & HDOP_STRIP_EARLY_DATA) { continue; } break; case HD_TRANSFER_ENCODING: if (flags & HDOP_STRIP_TRANSFER_ENCODING) { continue; } break; case HD_FORWARDED: if (flags & HDOP_STRIP_FORWARDED) { continue; } if (it_forwarded == std::ranges::end(headers)) { it_forwarded = it; continue; } kv = &(*it_forwarded); it_forwarded = it; break; case HD_X_FORWARDED_FOR: if (flags & HDOP_STRIP_X_FORWARDED_FOR) { continue; } if (it_xff == std::ranges::end(headers)) { it_xff = it; continue; } kv = &(*it_xff); it_xff = it; break; case HD_X_FORWARDED_PROTO: if (flags & HDOP_STRIP_X_FORWARDED_PROTO) { continue; } if (it_xfp == std::ranges::end(headers)) { it_xfp = it; continue; } kv = &(*it_xfp); it_xfp = it; break; case HD_VIA: if (flags & HDOP_STRIP_VIA) { continue; } if (it_via == std::ranges::end(headers)) { it_via = it; continue; } kv = &(*it_via); it_via = it; break; } capitalize(buf, kv->name); buf->append(": "sv); buf->append(kv->value); buf->append("\r\n"sv); } } int32_t determine_window_update_transmission(nghttp2_session *session, int32_t stream_id) { int32_t recv_length, window_size; if (stream_id == 0) { recv_length = nghttp2_session_get_effective_recv_data_length(session); window_size = nghttp2_session_get_effective_local_window_size(session); } else { recv_length = nghttp2_session_get_stream_effective_recv_data_length(session, stream_id); window_size = nghttp2_session_get_stream_effective_local_window_size( session, stream_id); } if (recv_length != -1 && window_size != -1) { if (recv_length >= window_size / 2) { return recv_length; } } return -1; } void dump_nv(FILE *out, const nghttp2_nv *nva, size_t nvlen) { auto end = nva + nvlen; for (; nva != end; ++nva) { fprintf(out, "%s: %s\n", nva->name, nva->value); } fputc('\n', out); fflush(out); } void dump_nv(FILE *out, const HeaderRefs &nva) { for (auto &nv : nva) { fprintf(out, "%s: %s\n", nv.name.data(), nv.value.data()); } fputc('\n', out); fflush(out); } void erase_header(HeaderRef *hd) { hd->name = ""sv; hd->token = -1; } std::string_view rewrite_location_uri(BlockAllocator &balloc, const std::string_view &uri, const urlparse_url &u, const std::string_view &match_host, const std::string_view &request_authority, const std::string_view &upstream_scheme) { // We just rewrite scheme and authority. if ((u.field_set & (1 << URLPARSE_HOST)) == 0) { return ""sv; } auto field = &u.field_data[URLPARSE_HOST]; if (!util::starts_with(match_host, std::string_view{&uri[field->off], field->len}) || (match_host.size() != field->len && match_host[field->len] != ':')) { return ""sv; } size_t len = 0; if (!request_authority.empty()) { len += upstream_scheme.size() + str_size("://") + request_authority.size(); } if (u.field_set & (1 << URLPARSE_PATH)) { field = &u.field_data[URLPARSE_PATH]; len += field->len; } if (u.field_set & (1 << URLPARSE_QUERY)) { field = &u.field_data[URLPARSE_QUERY]; len += 1 + field->len; } if (u.field_set & (1 << URLPARSE_FRAGMENT)) { field = &u.field_data[URLPARSE_FRAGMENT]; len += 1 + field->len; } auto iov = make_byte_ref(balloc, len + 1); auto p = std::ranges::begin(iov); if (!request_authority.empty()) { p = std::ranges::copy(upstream_scheme, p).out; p = std::ranges::copy("://"sv, p).out; p = std::ranges::copy(request_authority, p).out; } if (u.field_set & (1 << URLPARSE_PATH)) { field = &u.field_data[URLPARSE_PATH]; p = std::ranges::copy_n(&uri[field->off], field->len, p).out; } if (u.field_set & (1 << URLPARSE_QUERY)) { field = &u.field_data[URLPARSE_QUERY]; *p++ = '?'; p = std::ranges::copy_n(&uri[field->off], field->len, p).out; } if (u.field_set & (1 << URLPARSE_FRAGMENT)) { field = &u.field_data[URLPARSE_FRAGMENT]; *p++ = '#'; p = std::ranges::copy_n(&uri[field->off], field->len, p).out; } *p = '\0'; return as_string_view(std::ranges::begin(iov), p); } int parse_http_status_code(const std::string_view &src) { if (src.size() != 3) { return -1; } int status = 0; for (auto c : src) { if (!isdigit(c)) { return -1; } status *= 10; status += c - '0'; } if (status < 100) { return -1; } return status; } // This function was generated by genheaderfunc.py. Inspired by h2o // header lookup. https://github.com/h2o/h2o int lookup_token(const std::string_view &name) { switch (name.size()) { case 2: switch (name[1]) { case 'e': if (util::streq("t"sv, name.substr(0, 1))) { return HD_TE; } break; } break; case 3: switch (name[2]) { case 'a': if (util::streq("vi"sv, name.substr(0, 2))) { return HD_VIA; } break; } break; case 4: switch (name[3]) { case 'e': if (util::streq("dat"sv, name.substr(0, 3))) { return HD_DATE; } break; case 'k': if (util::streq("lin"sv, name.substr(0, 3))) { return HD_LINK; } break; case 't': if (util::streq("hos"sv, name.substr(0, 3))) { return HD_HOST; } break; } break; case 5: switch (name[4]) { case 'h': if (util::streq(":pat"sv, name.substr(0, 4))) { return HD__PATH; } break; case 't': if (util::streq(":hos"sv, name.substr(0, 4))) { return HD__HOST; } break; } break; case 6: switch (name[5]) { case 'e': if (util::streq("cooki"sv, name.substr(0, 5))) { return HD_COOKIE; } break; case 'r': if (util::streq("serve"sv, name.substr(0, 5))) { return HD_SERVER; } break; case 't': if (util::streq("expec"sv, name.substr(0, 5))) { return HD_EXPECT; } break; } break; case 7: switch (name[6]) { case 'c': if (util::streq("alt-sv"sv, name.substr(0, 6))) { return HD_ALT_SVC; } break; case 'd': if (util::streq(":metho"sv, name.substr(0, 6))) { return HD__METHOD; } break; case 'e': if (util::streq(":schem"sv, name.substr(0, 6))) { return HD__SCHEME; } if (util::streq("upgrad"sv, name.substr(0, 6))) { return HD_UPGRADE; } break; case 'r': if (util::streq("traile"sv, name.substr(0, 6))) { return HD_TRAILER; } break; case 's': if (util::streq(":statu"sv, name.substr(0, 6))) { return HD__STATUS; } break; } break; case 8: switch (name[7]) { case 'n': if (util::streq("locatio"sv, name.substr(0, 7))) { return HD_LOCATION; } break; case 'y': if (util::streq("priorit"sv, name.substr(0, 7))) { return HD_PRIORITY; } break; } break; case 9: switch (name[8]) { case 'd': if (util::streq("forwarde"sv, name.substr(0, 8))) { return HD_FORWARDED; } break; case 'l': if (util::streq(":protoco"sv, name.substr(0, 8))) { return HD__PROTOCOL; } break; } break; case 10: switch (name[9]) { case 'a': if (util::streq("early-dat"sv, name.substr(0, 9))) { return HD_EARLY_DATA; } break; case 'e': if (util::streq("keep-aliv"sv, name.substr(0, 9))) { return HD_KEEP_ALIVE; } break; case 'n': if (util::streq("connectio"sv, name.substr(0, 9))) { return HD_CONNECTION; } break; case 't': if (util::streq("user-agen"sv, name.substr(0, 9))) { return HD_USER_AGENT; } break; case 'y': if (util::streq(":authorit"sv, name.substr(0, 9))) { return HD__AUTHORITY; } break; } break; case 12: switch (name[11]) { case 'e': if (util::streq("content-typ"sv, name.substr(0, 11))) { return HD_CONTENT_TYPE; } break; } break; case 13: switch (name[12]) { case 'l': if (util::streq("cache-contro"sv, name.substr(0, 12))) { return HD_CACHE_CONTROL; } break; } break; case 14: switch (name[13]) { case 'h': if (util::streq("content-lengt"sv, name.substr(0, 13))) { return HD_CONTENT_LENGTH; } break; case 's': if (util::streq("http2-setting"sv, name.substr(0, 13))) { return HD_HTTP2_SETTINGS; } break; } break; case 15: switch (name[14]) { case 'e': if (util::streq("accept-languag"sv, name.substr(0, 14))) { return HD_ACCEPT_LANGUAGE; } break; case 'g': if (util::streq("accept-encodin"sv, name.substr(0, 14))) { return HD_ACCEPT_ENCODING; } break; case 'r': if (util::streq("x-forwarded-fo"sv, name.substr(0, 14))) { return HD_X_FORWARDED_FOR; } break; } break; case 16: switch (name[15]) { case 'n': if (util::streq("proxy-connectio"sv, name.substr(0, 15))) { return HD_PROXY_CONNECTION; } break; } break; case 17: switch (name[16]) { case 'e': if (util::streq("if-modified-sinc"sv, name.substr(0, 16))) { return HD_IF_MODIFIED_SINCE; } break; case 'g': if (util::streq("transfer-encodin"sv, name.substr(0, 16))) { return HD_TRANSFER_ENCODING; } break; case 'o': if (util::streq("x-forwarded-prot"sv, name.substr(0, 16))) { return HD_X_FORWARDED_PROTO; } break; case 'y': if (util::streq("sec-websocket-ke"sv, name.substr(0, 16))) { return HD_SEC_WEBSOCKET_KEY; } break; } break; case 20: switch (name[19]) { case 't': if (util::streq("sec-websocket-accep"sv, name.substr(0, 19))) { return HD_SEC_WEBSOCKET_ACCEPT; } break; } break; } return -1; } void init_hdidx(HeaderIndex &hdidx) { std::ranges::fill(hdidx, -1); } void index_header(HeaderIndex &hdidx, int32_t token, size_t idx) { if (token == -1) { return; } assert(token < HD_MAXIDX); hdidx[static_cast(token)] = static_cast(idx); } namespace { template InputIt skip_lws(InputIt first, InputIt last) { for (; first != last; ++first) { switch (*first) { case ' ': case '\t': continue; default: return first; } } return first; } } // namespace namespace { template InputIt skip_to_next_field(InputIt first, InputIt last) { for (; first != last; ++first) { switch (*first) { case ' ': case '\t': case ',': continue; default: return first; } } return first; } } // namespace namespace { // Skip to the right dquote ('"'), handling backslash escapes. // Returns |last| if input is not terminated with '"'. template InputIt skip_to_right_dquote(InputIt first, InputIt last) { for (; first != last;) { switch (*first) { case '"': return first; // quoted-pair case '\\': ++first; if (first == last) { return first; } switch (*first) { case '\t': case ' ': break; default: if ((0x21 <= *first && *first <= 0x7e) /* VCHAR */ || 0x80 <= *first /* obs-text */) { break; } return last; } break; // qdtext case '\t': case ' ': case '!': break; default: if ((0x23 <= *first && *first <= 0x5b) || (0x5d <= *first && *first <= 0x7e)) { break; } return last; } ++first; } return first; } } // namespace namespace { // Returns true if link-param does not match pattern |pat| of length // |patlen| or it has empty value (""). |pat| should be parmname // followed by "=". bool check_link_param_empty(const std::string_view &s, const std::string_view &pat) { return s.size() < pat.size() || !std::ranges::equal(s.substr(0, pat.size()), pat, util::CaseCmp()) || (s.size() >= pat.size() + 2 && // we only accept URI if pat is followed by "" // (e.g., loadpolicy="") here. s[pat.size()] == '"' && s[pat.size() + 1] == '"'); } } // namespace namespace { // Returns true if link-param consists of only parmname, and it // matches string [pat, pat + patlen). bool check_link_param_without_value(const std::string_view &s, const std::string_view &pat) { if (s.size() < pat.size()) { return false; } if (s.size() == pat.size()) { return std::ranges::equal(s, pat, util::CaseCmp()); } switch (s[pat.size()]) { case ';': case ',': return std::ranges::equal(s.substr(0, pat.size()), pat, util::CaseCmp()); } return false; } } // namespace namespace { std::pair parse_next_link_header_once(const char *first, const char *last) { first = skip_to_next_field(first, last); if (first == last || *first != '<') { return {{""sv}, last}; } auto url_first = ++first; first = std::ranges::find(first, last, '>'); if (first == last) { return {{""sv}, first}; } auto url_last = first++; if (first == last) { return {{""sv}, first}; } // we expect ';' or ',' here switch (*first) { case ',': return {{""sv}, ++first}; case ';': ++first; break; default: return {{""sv}, last}; } auto ok = false; auto ign = false; for (;;) { first = skip_lws(first, last); if (first == last) { return {{""sv}, first}; } // we expect link-param if (!ign) { if (!ok) { // rel can take several relations using quoted form. static constexpr auto PLP = "rel=\""sv; static constexpr auto PLT = "preload"sv; if (first + PLP.size() < last && *(first + PLP.size() - 1) == '"' && std::ranges::equal(PLP, std::string_view{first, PLP.size()}, util::CaseCmp())) { // we have to search preload in whitespace separated list: // rel="preload something http://example.org/foo" first += PLP.size(); auto start = first; for (; first != last;) { if (*first != ' ' && *first != '"') { ++first; continue; } if (start == first) { return {{""sv}, last}; } if (!ok && start + PLT.size() == first && std::ranges::equal(PLT, std::string_view{start, PLT.size()}, util::CaseCmp())) { ok = true; } if (*first == '"') { break; } first = skip_lws(first, last); start = first; } if (first == last) { return {{""sv}, last}; } assert(*first == '"'); ++first; if (first == last || *first == ',') { goto almost_done; } if (*first == ';') { ++first; // parse next link-param continue; } return {{""sv}, last}; } } // we are only interested in rel=preload parameter. Others are // simply skipped. static constexpr auto PL = "rel=preload"sv; if (first + PL.size() == last) { if (std::ranges::equal(PL, std::string_view{first, PL.size()}, util::CaseCmp())) { // ok = true; // this is the end of sequence return {{{url_first, url_last}}, last}; } } else if (first + PL.size() + 1 <= last) { switch (*(first + PL.size())) { case ',': if (!std::ranges::equal(PL, std::string_view{first, PL.size()}, util::CaseCmp())) { break; } // ok = true; // skip including ',' first += PL.size() + 1; return {{{url_first, url_last}}, first}; case ';': if (!std::ranges::equal(PL, std::string_view{first, PL.size()}, util::CaseCmp())) { break; } ok = true; // skip including ';' first += PL.size() + 1; // continue parse next link-param continue; } } // we have to reject URI if we have nonempty anchor parameter. if (!ign && !check_link_param_empty({first, last}, "anchor="sv)) { ign = true; } // reject URI if we have non-empty loadpolicy. This could be // tightened up to just pick up "next" or "insert". if (!ign && !check_link_param_empty({first, last}, "loadpolicy="sv)) { ign = true; } // reject URI if we have nopush attribute. if (!ign && check_link_param_without_value({first, last}, "nopush"sv)) { ign = true; } } auto param_first = first; for (; first != last;) { if (util::in_attr_char(*first)) { ++first; continue; } // '*' is only allowed at the end of parameter name and must be // followed by '=' if (last - first >= 2 && first != param_first) { if (*first == '*' && *(first + 1) == '=') { ++first; break; } } if (*first == '=' || *first == ';' || *first == ',') { break; } return {{""sv}, last}; } if (param_first == first) { // empty parmname return {{""sv}, last}; } // link-param without value is acceptable (see link-extension) if // it is not followed by '=' if (first == last || *first == ',') { goto almost_done; } if (*first == ';') { ++first; // parse next link-param continue; } // now parsing link-param value assert(*first == '='); ++first; if (first == last) { // empty value is not acceptable return {{""sv}, first}; } if (*first == '"') { // quoted-string first = skip_to_right_dquote(first + 1, last); if (first == last) { return {{""sv}, first}; } ++first; if (first == last || *first == ',') { goto almost_done; } if (*first == ';') { ++first; // parse next link-param continue; } return {{""sv}, last}; } // not quoted-string, skip to next ',' or ';' if (*first == ',' || *first == ';') { // empty value return {{""sv}, last}; } for (; first != last; ++first) { if (*first == ',' || *first == ';') { break; } } if (first == last || *first == ',') { goto almost_done; } assert(*first == ';'); ++first; // parse next link-param } almost_done: assert(first == last || *first == ','); if (first != last) { ++first; } if (ok && !ign) { return {{{url_first, url_last}}, first}; } return {{""sv}, first}; } } // namespace std::vector parse_link_header(const std::string_view &src) { std::vector res; for (auto first = std::ranges::begin(src); first != std::ranges::end(src);) { auto rv = parse_next_link_header_once(first, std::ranges::end(src)); first = rv.second; auto &link = rv.first; if (!link.uri.empty()) { res.push_back(link); } } return res; } std::string path_join(const std::string_view &base_path, const std::string_view &base_query, const std::string_view &rel_path, const std::string_view &rel_query) { BlockAllocator balloc(1024, 1024); return std::string{ path_join(balloc, base_path, base_query, rel_path, rel_query)}; } bool expect_response_body(uint32_t status_code) { return status_code == 101 || (status_code / 100 != 1 && status_code != 304 && status_code != 204); } bool expect_response_body(const std::string &method, uint32_t status_code) { return method != "HEAD" && expect_response_body(status_code); } bool expect_response_body(int method_token, uint32_t status_code) { return method_token != HTTP_HEAD && expect_response_body(status_code); } // This function was generated by genmethodfunc.py. int lookup_method_token(const std::string_view &name) { switch (name.size()) { case 3: switch (name[2]) { case 'L': if (util::streq("AC"sv, name.substr(0, 2))) { return HTTP_ACL; } break; case 'T': if (util::streq("GE"sv, name.substr(0, 2))) { return HTTP_GET; } if (util::streq("PU"sv, name.substr(0, 2))) { return HTTP_PUT; } break; } break; case 4: switch (name[3]) { case 'D': if (util::streq("BIN"sv, name.substr(0, 3))) { return HTTP_BIND; } if (util::streq("HEA"sv, name.substr(0, 3))) { return HTTP_HEAD; } break; case 'E': if (util::streq("MOV"sv, name.substr(0, 3))) { return HTTP_MOVE; } break; case 'K': if (util::streq("LIN"sv, name.substr(0, 3))) { return HTTP_LINK; } if (util::streq("LOC"sv, name.substr(0, 3))) { return HTTP_LOCK; } break; case 'T': if (util::streq("POS"sv, name.substr(0, 3))) { return HTTP_POST; } break; case 'Y': if (util::streq("COP"sv, name.substr(0, 3))) { return HTTP_COPY; } break; } break; case 5: switch (name[4]) { case 'E': if (util::streq("MERG"sv, name.substr(0, 4))) { return HTTP_MERGE; } if (util::streq("PURG"sv, name.substr(0, 4))) { return HTTP_PURGE; } if (util::streq("TRAC"sv, name.substr(0, 4))) { return HTTP_TRACE; } break; case 'H': if (util::streq("PATC"sv, name.substr(0, 4))) { return HTTP_PATCH; } break; case 'L': if (util::streq("MKCO"sv, name.substr(0, 4))) { return HTTP_MKCOL; } break; } break; case 6: switch (name[5]) { case 'D': if (util::streq("REBIN"sv, name.substr(0, 5))) { return HTTP_REBIND; } if (util::streq("UNBIN"sv, name.substr(0, 5))) { return HTTP_UNBIND; } break; case 'E': if (util::streq("DELET"sv, name.substr(0, 5))) { return HTTP_DELETE; } if (util::streq("SOURC"sv, name.substr(0, 5))) { return HTTP_SOURCE; } break; case 'H': if (util::streq("SEARC"sv, name.substr(0, 5))) { return HTTP_SEARCH; } break; case 'K': if (util::streq("UNLIN"sv, name.substr(0, 5))) { return HTTP_UNLINK; } if (util::streq("UNLOC"sv, name.substr(0, 5))) { return HTTP_UNLOCK; } break; case 'T': if (util::streq("REPOR"sv, name.substr(0, 5))) { return HTTP_REPORT; } break; case 'Y': if (util::streq("NOTIF"sv, name.substr(0, 5))) { return HTTP_NOTIFY; } break; } break; case 7: switch (name[6]) { case 'H': if (util::streq("MSEARC"sv, name.substr(0, 6))) { return HTTP_MSEARCH; } break; case 'S': if (util::streq("OPTION"sv, name.substr(0, 6))) { return HTTP_OPTIONS; } break; case 'T': if (util::streq("CONNEC"sv, name.substr(0, 6))) { return HTTP_CONNECT; } break; } break; case 8: switch (name[7]) { case 'D': if (util::streq("PROPFIN"sv, name.substr(0, 7))) { return HTTP_PROPFIND; } break; case 'T': if (util::streq("CHECKOU"sv, name.substr(0, 7))) { return HTTP_CHECKOUT; } break; } break; case 9: switch (name[8]) { case 'E': if (util::streq("SUBSCRIB"sv, name.substr(0, 8))) { return HTTP_SUBSCRIBE; } break; case 'H': if (util::streq("PROPPATC"sv, name.substr(0, 8))) { return HTTP_PROPPATCH; } break; } break; case 10: switch (name[9]) { case 'R': if (util::streq("MKCALENDA"sv, name.substr(0, 9))) { return HTTP_MKCALENDAR; } break; case 'Y': if (util::streq("MKACTIVIT"sv, name.substr(0, 9))) { return HTTP_MKACTIVITY; } break; } break; case 11: switch (name[10]) { case 'E': if (util::streq("UNSUBSCRIB"sv, name.substr(0, 10))) { return HTTP_UNSUBSCRIBE; } break; } break; } return -1; } std::string_view to_method_string(int method_token) { // we happened to use same value for method with llhttp. return std::string_view{ llhttp_method_name(static_cast(method_token))}; } std::string_view get_pure_path_component(const std::string_view &uri) { int rv; urlparse_url u; rv = urlparse_parse_url(uri.data(), uri.size(), 0, &u); if (rv != 0) { return ""sv; } if (u.field_set & (1 << URLPARSE_PATH)) { auto &f = u.field_data[URLPARSE_PATH]; return std::string_view{uri.data() + f.off, f.len}; } return "/"sv; } int construct_push_component(BlockAllocator &balloc, std::string_view &scheme, std::string_view &authority, std::string_view &path, const std::string_view &base, const std::string_view &uri) { int rv; std::string_view rel, relq; if (uri.size() == 0) { return -1; } urlparse_url u; rv = urlparse_parse_url(uri.data(), uri.size(), 0, &u); if (rv != 0) { if (uri[0] == '/') { return -1; } // treat link_url as relative URI. auto end = std::ranges::find(uri, '#'); auto q = std::ranges::find(std::ranges::begin(uri), end, '?'); rel = std::string_view{std::ranges::begin(uri), q}; if (q != end) { relq = std::string_view{q + 1, std::ranges::end(uri)}; } } else { if (u.field_set & (1 << URLPARSE_SCHEMA)) { scheme = util::get_uri_field(uri.data(), u, URLPARSE_SCHEMA); } if (u.field_set & (1 << URLPARSE_HOST)) { auto auth = util::get_uri_field(uri.data(), u, URLPARSE_HOST); auto len = auth.size(); auto port_exists = u.field_set & (1 << URLPARSE_PORT); if (port_exists) { len += 1 + str_size("65535"); } auto iov = make_byte_ref(balloc, len + 1); auto p = std::ranges::begin(iov); p = std::ranges::copy(auth, p).out; if (port_exists) { *p++ = ':'; p = util::utos(u.port, p); } *p = '\0'; authority = as_string_view(std::ranges::begin(iov), p); } if (u.field_set & (1 << URLPARSE_PATH)) { auto &f = u.field_data[URLPARSE_PATH]; rel = std::string_view{uri.data() + f.off, f.len}; } else { rel = "/"sv; } if (u.field_set & (1 << URLPARSE_QUERY)) { auto &f = u.field_data[URLPARSE_QUERY]; relq = std::string_view{uri.data() + f.off, f.len}; } } path = path_join(balloc, base, ""sv, rel, relq); return 0; } namespace { template InputIt eat_file(InputIt first, InputIt last) { if (first == last) { *first++ = '/'; return first; } if (*(last - 1) == '/') { return last; } auto p = last; for (; p != first && *(p - 1) != '/'; --p) ; if (p == first) { // this should not happened in normal case, where we expect path // starts with '/' *first++ = '/'; return first; } return p; } } // namespace namespace { template InputIt eat_dir(InputIt first, InputIt last) { auto p = eat_file(first, last); --p; assert(*p == '/'); return eat_file(first, p); } } // namespace std::string_view path_join(BlockAllocator &balloc, const std::string_view &base_path, const std::string_view &base_query, const std::string_view &rel_path, const std::string_view &rel_query) { auto res = make_byte_ref(balloc, std::max(static_cast(1), base_path.size()) + rel_path.size() + 1 + std::max(base_query.size(), rel_query.size()) + 1); auto p = std::ranges::begin(res); if (rel_path.empty()) { if (base_path.empty()) { *p++ = '/'; } else { p = std::ranges::copy(base_path, p).out; } if (rel_query.empty()) { if (!base_query.empty()) { *p++ = '?'; p = std::ranges::copy(base_query, p).out; } *p = '\0'; return as_string_view(std::ranges::begin(res), p); } *p++ = '?'; p = std::ranges::copy(rel_query, p).out; *p = '\0'; return as_string_view(std::ranges::begin(res), p); } auto first = std::ranges::begin(rel_path); auto last = std::ranges::end(rel_path); if (rel_path[0] == '/') { *p++ = '/'; ++first; for (; first != last && *first == '/'; ++first) ; } else if (base_path.empty()) { *p++ = '/'; } else { p = std::ranges::copy(base_path, p).out; } for (; first != last;) { if (*first == '.') { if (first + 1 == last) { if (*(p - 1) != '/') { p = eat_file(std::ranges::begin(res), p); } break; } if (*(first + 1) == '/') { if (*(p - 1) != '/') { p = eat_file(std::ranges::begin(res), p); } first += 2; continue; } if (*(first + 1) == '.') { if (first + 2 == last) { p = eat_dir(std::ranges::begin(res), p); break; } if (*(first + 2) == '/') { p = eat_dir(std::ranges::begin(res), p); first += 3; continue; } } } if (*(p - 1) != '/') { p = eat_file(std::ranges::begin(res), p); } auto slash = std::ranges::find(first, last, '/'); if (slash == last) { p = std::ranges::copy(first, last, p).out; break; } p = std::ranges::copy(first, slash + 1, p).out; first = slash + 1; for (; first != last && *first == '/'; ++first) ; } if (!rel_query.empty()) { *p++ = '?'; p = std::ranges::copy(rel_query, p).out; } *p = '\0'; return as_string_view(std::ranges::begin(res), p); } std::string_view normalize_path(BlockAllocator &balloc, const std::string_view &path, const std::string_view &query) { // First, decode %XX for unreserved characters, then do // http2::path_join // We won't find %XX if length is less than 3. if (path.size() < 3 || std::ranges::find(path, '%') == std::ranges::end(path)) { return path_join(balloc, ""sv, ""sv, path, query); } // includes last terminal NULL. auto result = make_byte_ref(balloc, path.size() + 1); auto p = std::ranges::begin(result); auto it = std::ranges::begin(path); for (; it + 2 < std::ranges::end(path);) { if (*it == '%') { if (util::is_hex_digit(*(it + 1)) && util::is_hex_digit(*(it + 2))) { auto c = static_cast((util::hex_to_uint(*(it + 1)) << 4) + util::hex_to_uint(*(it + 2))); if (util::in_rfc3986_unreserved_chars(c)) { *p++ = as_unsigned(c); it += 3; continue; } *p++ = '%'; *p++ = as_unsigned(util::upcase(*(it + 1))); *p++ = as_unsigned(util::upcase(*(it + 2))); it += 3; continue; } } *p++ = as_unsigned(*it++); } p = std::ranges::copy(it, std::ranges::end(path), p).out; *p = '\0'; return path_join(balloc, ""sv, ""sv, as_string_view(std::ranges::begin(result), p), query); } std::string_view normalize_path_colon(BlockAllocator &balloc, const std::string_view &path, const std::string_view &query) { // First, decode %XX for unreserved characters and ':', then do // http2::path_join // We won't find %XX if length is less than 3. if (path.size() < 3 || std::ranges::find(path, '%') == std::ranges::end(path)) { return path_join(balloc, ""sv, ""sv, path, query); } // includes last terminal NULL. auto result = make_byte_ref(balloc, path.size() + 1); auto p = std::ranges::begin(result); auto it = std::ranges::begin(path); for (; it + 2 < std::ranges::end(path);) { if (*it == '%') { if (util::is_hex_digit(*(it + 1)) && util::is_hex_digit(*(it + 2))) { auto c = static_cast((util::hex_to_uint(*(it + 1)) << 4) + util::hex_to_uint(*(it + 2))); if (util::in_rfc3986_unreserved_chars(c) || c == ':') { *p++ = as_unsigned(c); it += 3; continue; } *p++ = '%'; *p++ = as_unsigned(util::upcase(*(it + 1))); *p++ = as_unsigned(util::upcase(*(it + 2))); it += 3; continue; } } *p++ = as_unsigned(*it++); } p = std::ranges::copy(it, std::ranges::end(path), p).out; *p = '\0'; return path_join(balloc, ""sv, ""sv, as_string_view(std::ranges::begin(result), p), query); } std::string normalize_path(const std::string_view &path, const std::string_view &query) { BlockAllocator balloc(1024, 1024); return std::string{normalize_path(balloc, path, query)}; } std::string_view rewrite_clean_path(BlockAllocator &balloc, const std::string_view &src) { if (src.empty() || src[0] != '/') { return src; } // probably, not necessary most of the case, but just in case. auto fragment = std::ranges::find(src, '#'); auto raw_query = std::ranges::find(std::ranges::begin(src), fragment, '?'); auto query = raw_query; if (query != fragment) { ++query; } return normalize_path(balloc, std::string_view{std::ranges::begin(src), raw_query}, std::string_view{query, fragment}); } bool contains_trailers(const std::string_view &s) { constexpr auto trailers = "trailers"sv; for (auto p = std::ranges::begin(s), end = std::ranges::end(s);; ++p) { p = std::ranges::find_if(p, end, [](char c) { return c != ' ' && c != '\t'; }); if (p == end || static_cast(end - p) < trailers.size()) { return false; } if (util::strieq(trailers, std::string_view{p, p + trailers.size()})) { // Make sure that there is no character other than white spaces // before next "," or end of string. p = std::ranges::find_if(p + trailers.size(), end, [](char c) { return c != ' ' && c != '\t'; }); if (p == end || *p == ',') { return true; } } // Skip to next ",". p = std::ranges::find_if(p, end, [](char c) { return c == ','; }); if (p == end) { return false; } } } std::string_view make_websocket_accept_token(uint8_t *dest, const std::string_view &key) { static constexpr auto magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"sv; std::array s; auto p = std::ranges::copy(key, std::ranges::begin(s)).out; std::ranges::copy(magic, p); std::array h; if (util::sha1(h.data(), as_string_view(s)) != 0) { return ""sv; } return as_string_view(dest, base64::encode(h, dest)); } bool legacy_http1(int major, int minor) { return major <= 0 || (major == 1 && minor == 0); } bool check_transfer_encoding(const std::string_view &s) { if (s.empty()) { return false; } auto it = std::ranges::begin(s); for (;;) { // token if (!util::in_token(*it)) { return false; } ++it; for (; it != std::ranges::end(s) && util::in_token(*it); ++it) ; if (it == std::ranges::end(s)) { return true; } for (;;) { // OWS it = skip_lws(it, std::ranges::end(s)); if (it == std::ranges::end(s)) { return false; } if (*it == ',') { ++it; it = skip_lws(it, std::ranges::end(s)); if (it == std::ranges::end(s)) { return false; } break; } if (*it != ';') { return false; } ++it; // transfer-parameter follows // OWS it = skip_lws(it, std::ranges::end(s)); if (it == std::ranges::end(s)) { return false; } // token if (!util::in_token(*it)) { return false; } ++it; for (; it != std::ranges::end(s) && util::in_token(*it); ++it) ; if (it == std::ranges::end(s)) { return false; } // No BWS allowed if (*it != '=') { return false; } ++it; if (util::in_token(*it)) { // token ++it; for (; it != std::ranges::end(s) && util::in_token(*it); ++it) ; } else if (*it == '"') { // quoted-string ++it; it = skip_to_right_dquote(it, std::ranges::end(s)); if (it == std::ranges::end(s)) { return false; } ++it; } else { return false; } if (it == std::ranges::end(s)) { return true; } } } } std::string encode_extpri(const nghttp2_extpri &extpri) { std::string res = "u="; res += static_cast(extpri.urgency) + '0'; if (extpri.inc) { res += ",i"; } return res; } } // namespace http2 } // namespace nghttp2 nghttp2-1.68.0/src/PaxHeaders/nghttp2_gzip.c0000644000000000000000000000013215077107270015632 xustar0030 mtime=1761382072.989444157 30 atime=1761382106.227310326 30 ctime=1761382109.223300025 nghttp2-1.68.0/src/nghttp2_gzip.c0000644000175100017510000000505415077107270016226 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_gzip.h" #include int nghttp2_gzip_inflate_new(nghttp2_gzip **inflater_ptr) { int rv; *inflater_ptr = calloc(1, sizeof(nghttp2_gzip)); if (*inflater_ptr == NULL) { return -1; } rv = inflateInit2(&(*inflater_ptr)->zst, 47); if (rv != Z_OK) { free(*inflater_ptr); return -1; } return 0; } void nghttp2_gzip_inflate_del(nghttp2_gzip *inflater) { if (inflater != NULL) { inflateEnd(&inflater->zst); free(inflater); } } int nghttp2_gzip_inflate(nghttp2_gzip *inflater, uint8_t *out, size_t *outlen_ptr, const uint8_t *in, size_t *inlen_ptr) { int rv; if (inflater->finished) { return -1; } inflater->zst.avail_in = (unsigned int)*inlen_ptr; inflater->zst.next_in = (unsigned char *)in; inflater->zst.avail_out = (unsigned int)*outlen_ptr; inflater->zst.next_out = out; rv = inflate(&inflater->zst, Z_NO_FLUSH); *inlen_ptr -= inflater->zst.avail_in; *outlen_ptr -= inflater->zst.avail_out; switch (rv) { case Z_STREAM_END: inflater->finished = 1; /* FALL THROUGH */ case Z_OK: case Z_BUF_ERROR: return 0; case Z_DATA_ERROR: case Z_STREAM_ERROR: case Z_NEED_DICT: case Z_MEM_ERROR: return -1; default: assert(0); /* We need this for some compilers */ return 0; } } int nghttp2_gzip_inflate_finished(nghttp2_gzip *inflater) { return inflater->finished; } nghttp2-1.68.0/src/PaxHeaders/shrpx_dns_resolver.h0000644000000000000000000000013215077107270017151 xustar0030 mtime=1761382072.992444143 30 atime=1761382106.165310599 30 ctime=1761382109.161300205 nghttp2-1.68.0/src/shrpx_dns_resolver.h0000644000175100017510000000744515077107270017553 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_DNS_RESOLVER_H #define SHRPX_DNS_RESOLVER_H #include "shrpx.h" #include #include #include #include #include #include "template.h" #include "network.h" using namespace nghttp2; namespace shrpx { enum class DNSResolverStatus { // Resolver is in initial status IDLE, // Resolver is currently resolving host name RUNNING, // Resolver successfully resolved host name OK, // Resolver failed to resolve host name ERROR, }; // Callback function called when host name lookup is finished. // |status| is either DNSResolverStatus::OK, or // DNSResolverStatus::ERROR. If |status| is DNSResolverStatus::OK, // |result| points to the resolved address. Note that port portion of // |result| is undefined, and must be initialized by application. // This callback function is not called if name lookup finishes in // DNSResolver::resolve() completely. In this case, application // should call DNSResolver::get_status() to get current status and // result. In other words, callback is called if get_status() returns // DNSResolverStatus::RUNNING. using CompleteCb = std::function; // DNSResolver is asynchronous name resolver, backed by c-ares // library. class DNSResolver { public: DNSResolver(struct ev_loop *loop); ~DNSResolver(); // Starts resolving hostname |name|. int resolve(const std::string_view &name, int family); // Returns status. If status_ is DNSResolverStatus::SUCCESS && // |result| is not nullptr, |*result| is filled. DNSResolverStatus get_status(Address *result) const; // Sets callback function when name lookup finishes. The callback // function is called in a way that it can destroy this DNSResolver. void set_complete_cb(CompleteCb cb); CompleteCb get_complete_cb() const; // Calls these functions when read/write event occurred respectively. int on_read(int fd); int on_write(int fd); int on_timeout(); // Calls this function when DNS query finished. void on_result(int status, ares_addrinfo *result); void reset_timeout(); void start_rev(int fd); void stop_rev(int fd); void start_wev(int fd); void stop_wev(int fd); private: int handle_event(int rfd, int wfd); std::vector> revs_, wevs_; Address result_; CompleteCb completeCb_; ev_timer timer_; std::string_view name_; struct ev_loop *loop_; // ares_channel is pointer type ares_channel channel_; // AF_INET or AF_INET6. AF_INET for A record lookup, and AF_INET6 // for AAAA record lookup. int family_; DNSResolverStatus status_; }; } // namespace shrpx #endif // !defined(SHRPX_DNS_RESOLVER_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_router_test.cc0000644000000000000000000000013215077107270017161 xustar0030 mtime=1761382072.999444111 30 atime=1761382106.258310189 30 ctime=1761382109.255299933 nghttp2-1.68.0/src/shrpx_router_test.cc0000644000175100017510000001120215077107270017545 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_router_test.h" #include "munitxx.h" #include "shrpx_router.h" using namespace std::literals; namespace shrpx { namespace { const MunitTest tests[]{ munit_void_test(test_shrpx_router_match), munit_void_test(test_shrpx_router_match_wildcard), munit_void_test(test_shrpx_router_match_prefix), munit_test_end(), }; } // namespace const MunitSuite router_suite{ "/router", tests, nullptr, 1, MUNIT_SUITE_OPTION_NONE, }; struct Pattern { std::string_view pattern; size_t idx; bool wildcard; }; void test_shrpx_router_match(void) { auto patterns = std::vector{ {"nghttp2.org/"sv, 0}, {"nghttp2.org/alpha"sv, 1}, {"nghttp2.org/alpha/"sv, 2}, {"nghttp2.org/alpha/bravo/"sv, 3}, {"www.nghttp2.org/alpha/"sv, 4}, {"/alpha"sv, 5}, {"example.com/alpha/"sv, 6}, {"nghttp2.org/alpha/bravo2/"sv, 7}, {"www2.nghttp2.org/alpha/"sv, 8}, {"www2.nghttp2.org/alpha2/"sv, 9}, }; Router router; for (auto &p : patterns) { router.add_route(p.pattern, p.idx); } ssize_t idx; idx = router.match("nghttp2.org"sv, "/"sv); assert_ssize(0, ==, idx); idx = router.match("nghttp2.org"sv, "/alpha"sv); assert_ssize(1, ==, idx); idx = router.match("nghttp2.org"sv, "/alpha/"sv); assert_ssize(2, ==, idx); idx = router.match("nghttp2.org"sv, "/alpha/charlie"sv); assert_ssize(2, ==, idx); idx = router.match("nghttp2.org"sv, "/alpha/bravo/"sv); assert_ssize(3, ==, idx); // matches pattern when last '/' is missing in path idx = router.match("nghttp2.org"sv, "/alpha/bravo"sv); assert_ssize(3, ==, idx); idx = router.match("www2.nghttp2.org"sv, "/alpha"sv); assert_ssize(8, ==, idx); idx = router.match(""sv, "/alpha"sv); assert_ssize(5, ==, idx); } void test_shrpx_router_match_wildcard(void) { constexpr auto patterns = std::to_array({ {"nghttp2.org/"sv, 0}, {"nghttp2.org/"sv, 1, true}, {"nghttp2.org/alpha/"sv, 2}, {"nghttp2.org/alpha/"sv, 3, true}, {"nghttp2.org/bravo"sv, 4}, {"nghttp2.org/bravo"sv, 5, true}, }); Router router; for (auto &p : patterns) { router.add_route(p.pattern, p.idx, p.wildcard); } assert_ssize(0, ==, router.match("nghttp2.org"sv, "/"sv)); assert_ssize(1, ==, router.match("nghttp2.org"sv, "/a"sv)); assert_ssize(1, ==, router.match("nghttp2.org"sv, "/charlie"sv)); assert_ssize(2, ==, router.match("nghttp2.org"sv, "/alpha"sv)); assert_ssize(2, ==, router.match("nghttp2.org"sv, "/alpha/"sv)); assert_ssize(3, ==, router.match("nghttp2.org"sv, "/alpha/b"sv)); assert_ssize(4, ==, router.match("nghttp2.org"sv, "/bravo"sv)); assert_ssize(5, ==, router.match("nghttp2.org"sv, "/bravocharlie"sv)); assert_ssize(5, ==, router.match("nghttp2.org"sv, "/bravo/"sv)); } void test_shrpx_router_match_prefix(void) { auto patterns = std::vector{ {"gro.2ptthgn."sv, 0}, {"gro.2ptthgn.www."sv, 1}, {"gro.2ptthgn.gmi."sv, 2}, {"gro.2ptthgn.gmi.ahpla."sv, 3}, }; Router router; for (auto &p : patterns) { router.add_route(p.pattern, p.idx); } ssize_t idx; const RNode *node; size_t nread; node = nullptr; idx = router.match_prefix(&nread, &node, "gro.2ptthgn.gmi.ahpla.ovarb"sv); assert_ssize(0, ==, idx); assert_size(12, ==, nread); idx = router.match_prefix(&nread, &node, "gmi.ahpla.ovarb"sv); assert_ssize(2, ==, idx); assert_size(4, ==, nread); idx = router.match_prefix(&nread, &node, "ahpla.ovarb"sv); assert_ssize(3, ==, idx); assert_size(6, ==, nread); } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_log.cc0000644000000000000000000000013215077107270015363 xustar0030 mtime=1761382072.996444125 30 atime=1761382106.111310837 30 ctime=1761382109.107300361 nghttp2-1.68.0/src/shrpx_log.cc0000644000175100017510000006240415077107270015761 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_log.h" #ifdef HAVE_SYSLOG_H # include #endif // defined(HAVE_SYSLOG_H) #ifdef HAVE_UNISTD_H # include #endif // defined(HAVE_UNISTD_H) #ifdef HAVE_INTTYPES_H # include #endif // defined(HAVE_INTTYPES_H) #include #include #ifdef HAVE_FCNTL_H # include #endif // defined(HAVE_FCNTL_H) #include #include #include #include #include #include #include #include "shrpx_config.h" #include "shrpx_downstream.h" #include "shrpx_worker.h" #include "util.h" #include "template.h" using namespace nghttp2; namespace shrpx { namespace { constexpr std::string_view SEVERITY_STR[] = {"INFO"sv, "NOTICE"sv, "WARN"sv, "ERROR"sv, "FATAL"sv}; } // namespace namespace { constexpr std::string_view SEVERITY_COLOR[] = { "\033[1;32m"sv, // INFO "\033[1;36m"sv, // NOTICE "\033[1;33m"sv, // WARN "\033[1;31m"sv, // ERROR "\033[1;35m"sv, // FATAL }; } // namespace namespace { LogBuffer *get_logbuf() { static thread_local LogBuffer logbuf; return &logbuf; } } // namespace int Log::severity_thres_ = NOTICE; void Log::set_severity_level(int severity) { severity_thres_ = severity; } int Log::get_severity_level_by_name(const std::string_view &name) { for (size_t i = 0, max = array_size(SEVERITY_STR); i < max; ++i) { if (name == SEVERITY_STR[i]) { return static_cast(i); } } return -1; } int severity_to_syslog_level(int severity) { switch (severity) { case (INFO): return LOG_INFO; case (NOTICE): return LOG_NOTICE; case (WARN): return LOG_WARNING; case (ERROR): return LOG_ERR; case (FATAL): return LOG_CRIT; default: return -1; } } Log::Log(int severity, const char *filename, int linenum) : buf_(*get_logbuf()), begin_(buf_.data()), end_(begin_ + buf_.size()), last_(begin_), filename_(filename), flags_(0), severity_(severity), linenum_(linenum), full_(false) { auto config = get_config(); if (!config) { full_ = true; return; } auto lgconf = log_config(); auto &errorconf = config->logging.error; if (!log_enabled(severity_) || (lgconf->errorlog_fd == -1 && !errorconf.syslog)) { full_ = true; return; } if (errorconf.syslog) { *last_++ = '['; last_ = std::ranges::copy(SEVERITY_STR[severity_], last_).out; last_ = std::ranges::copy("] "sv, last_).out; return; } auto tty = lgconf->errorlog_tty; lgconf->update_tstamp_millis(std::chrono::system_clock::now()); // Error log format: // (:) last_ = std::ranges::copy(lgconf->tstamp->time_iso8601, last_).out; *last_++ = ' '; last_ = util::utos(as_unsigned(config->pid), last_); *last_++ = ' '; last_ = util::utos(as_unsigned(lgconf->pid), last_); *last_++ = ' '; last_ = std::ranges::copy(lgconf->thread_id, last_).out; *last_++ = ' '; if (tty) { last_ = std::ranges::copy(SEVERITY_COLOR[severity_], last_).out; } last_ = std::ranges::copy(SEVERITY_STR[severity_], last_).out; if (tty) { last_ = std::ranges::copy("\033[0m"sv, last_).out; } last_ = std::ranges::copy(" ("sv, last_).out; last_ = std::ranges::copy(filename_, last_).out; *last_++ = ':'; last_ = util::utos(as_unsigned(linenum_), last_); last_ = std::ranges::copy(") "sv, last_).out; } Log::~Log() { if (last_ == begin_) { return; } auto config = get_config(); auto &errorconf = config->logging.error; if (errorconf.syslog) { if (severity_ != NOTICE && wleft() >= " ("sv.size() + filename_.size() + /* : */ 1 + std::numeric_limits::digits10 + 1 + /* ) */ 1) { last_ = std::ranges::copy(" ("sv, last_).out; last_ = std::ranges::copy(filename_, last_).out; *last_++ = ':'; last_ = util::utos(as_unsigned(linenum_), last_); *last_++ = ')'; } *last_ = '\0'; syslog(severity_to_syslog_level(severity_), "%s", begin_); return; } auto lgconf = log_config(); *last_++ = '\n'; while (write(lgconf->errorlog_fd, begin_, rleft()) == -1 && errno == EINTR) ; } Log &Log::operator<<(const std::string &s) { write_seq(s); return *this; } Log &Log::operator<<(const std::string_view &s) { write_seq(s); return *this; } Log &Log::operator<<(const char *s) { write_seq(std::string_view{s}); return *this; } Log &Log::operator<<(const ImmutableString &s) { write_seq(s); return *this; } Log &Log::operator<<(double n) { if (full_) { return *this; } auto left = wleft(); auto rv = snprintf(reinterpret_cast(last_), left, "%.9f", n); if (rv > static_cast(left)) { full_ = true; return *this; } last_ += rv; update_full(); return *this; } Log &Log::operator<<(long double n) { if (full_) { return *this; } auto left = wleft(); auto rv = snprintf(reinterpret_cast(last_), left, "%.9Lf", n); if (rv > static_cast(left)) { full_ = true; return *this; } last_ += rv; update_full(); return *this; } Log &Log::operator<<(bool n) { if (full_) { return *this; } *last_++ = n ? '1' : '0'; update_full(); return *this; } Log &Log::operator<<(const void *p) { if (full_) { return *this; } write_hex(reinterpret_cast(p)); return *this; } namespace log { void hex(Log &log) { log.set_flags(Log::fmt_hex); } void dec(Log &log) { log.set_flags(Log::fmt_dec); } } // namespace log namespace { template requires(!std::is_array_v>) std::span copy(R &&src, std::span dest) { auto nwrite = std::min(std::ranges::size(src), std::ranges::size(dest)); std::ranges::copy(std::views::take(src, as_signed(nwrite)), std::ranges::begin(dest)); return dest.subspan(nwrite); } } // namespace namespace { std::span copy(const char *src, std::span dest) { return copy(std::string_view{src}, dest); } } // namespace namespace { std::span copy(char c, std::span dest) { if (dest.empty()) { return dest; } dest[0] = c; return dest.subspan(1); } } // namespace namespace { std::span copy_hex_low(std::span src, std::span dest) { auto n = std::min(dest.size(), src.size() * 2) / 2; auto d = util::format_hex(src.first(n), std::ranges::begin(dest)); if (n < src.size()) { return {d, d}; } return {d, std::ranges::end(dest)}; } } // namespace namespace { template std::span copy(T n, std::span dest) { if (dest.size() < std::numeric_limits::digits10 + 1) { return dest.first(0); } return {util::utos(n, std::ranges::begin(dest)), std::ranges::end(dest)}; } } // namespace namespace { // 1 means that character must be escaped as "\xNN", where NN is ascii // code of the character in hex notation. constexpr uint8_t ESCAPE_TBL[] = { 1 /* NUL */, 1 /* SOH */, 1 /* STX */, 1 /* ETX */, 1 /* EOT */, 1 /* ENQ */, 1 /* ACK */, 1 /* BEL */, 1 /* BS */, 1 /* HT */, 1 /* LF */, 1 /* VT */, 1 /* FF */, 1 /* CR */, 1 /* SO */, 1 /* SI */, 1 /* DLE */, 1 /* DC1 */, 1 /* DC2 */, 1 /* DC3 */, 1 /* DC4 */, 1 /* NAK */, 1 /* SYN */, 1 /* ETB */, 1 /* CAN */, 1 /* EM */, 1 /* SUB */, 1 /* ESC */, 1 /* FS */, 1 /* GS */, 1 /* RS */, 1 /* US */, 0 /* SPC */, 0 /* ! */, 1 /* " */, 0 /* # */, 0 /* $ */, 0 /* % */, 0 /* & */, 0 /* ' */, 0 /* ( */, 0 /* ) */, 0 /* * */, 0 /* + */, 0 /* , */, 0 /* - */, 0 /* . */, 0 /* / */, 0 /* 0 */, 0 /* 1 */, 0 /* 2 */, 0 /* 3 */, 0 /* 4 */, 0 /* 5 */, 0 /* 6 */, 0 /* 7 */, 0 /* 8 */, 0 /* 9 */, 0 /* : */, 0 /* ; */, 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */, 0 /* A */, 0 /* B */, 0 /* C */, 0 /* D */, 0 /* E */, 0 /* F */, 0 /* G */, 0 /* H */, 0 /* I */, 0 /* J */, 0 /* K */, 0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */, 0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */, 0 /* T */, 0 /* U */, 0 /* V */, 0 /* W */, 0 /* X */, 0 /* Y */, 0 /* Z */, 0 /* [ */, 1 /* \ */, 0 /* ] */, 0 /* ^ */, 0 /* _ */, 0 /* ` */, 0 /* a */, 0 /* b */, 0 /* c */, 0 /* d */, 0 /* e */, 0 /* f */, 0 /* g */, 0 /* h */, 0 /* i */, 0 /* j */, 0 /* k */, 0 /* l */, 0 /* m */, 0 /* n */, 0 /* o */, 0 /* p */, 0 /* q */, 0 /* r */, 0 /* s */, 0 /* t */, 0 /* u */, 0 /* v */, 0 /* w */, 0 /* x */, 0 /* y */, 0 /* z */, 0 /* { */, 0 /* | */, 0 /* } */, 0 /* ~ */, 1 /* DEL */, 1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */, 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */, 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */, 1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */, 1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */, 1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */, 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */, 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */, 1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */, 1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */, 1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */, 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */, 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */, 1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */, 1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */, 1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */, 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */, 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */, 1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */, 1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */, 1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */, 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */, 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */, 1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */, 1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */, 1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */, 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */, 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */, 1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */, 1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */, 1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */, 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */, }; } // namespace namespace { std::span copy_escape(const std::string_view &src, std::span dest) { auto safe_first = std::ranges::begin(src); for (auto p = safe_first; p != std::ranges::end(src) && !dest.empty(); ++p) { auto c = as_unsigned(*p); if (!ESCAPE_TBL[c]) { continue; } auto n = std::min(std::ranges::size(dest), as_unsigned(std::ranges::distance(safe_first, p))); std::ranges::copy_n(safe_first, as_signed(n), std::ranges::begin(dest)); dest = dest.subspan(n); if (dest.size() < 4) { return dest.first(0); } dest[0] = '\\'; dest[1] = 'x'; util::format_hex(c, std::ranges::begin(dest) + 2); dest = dest.subspan(4); safe_first = p + 1; } auto n = std::min( std::ranges::size(dest), as_unsigned(std::ranges::distance(safe_first, std::ranges::end(src)))); std::ranges::copy_n(safe_first, as_signed(n), std::ranges::begin(dest)); return dest.subspan(n); } } // namespace namespace { // Construct absolute request URI from |Request|, mainly to log // request URI for proxy request (HTTP/2 proxy or client proxy). This // is mostly same routine found in // HttpDownstreamConnection::push_request_headers(), but vastly // simplified since we only care about absolute URI. std::string_view construct_absolute_request_uri(BlockAllocator &balloc, const Request &req) { if (req.authority.empty()) { return req.path; } auto len = req.authority.size() + req.path.size(); if (req.scheme.empty()) { len += str_size("http://"); } else { len += req.scheme.size() + str_size("://"); } auto iov = make_byte_ref(balloc, len + 1); auto p = std::ranges::begin(iov); if (req.scheme.empty()) { // We may have to log the request which lacks scheme (e.g., // http/1.1 with origin form). p = std::ranges::copy("http://"sv, p).out; } else { p = std::ranges::copy(req.scheme, p).out; p = std::ranges::copy("://"sv, p).out; } p = std::ranges::copy(req.authority, p).out; p = std::ranges::copy(req.path, p).out; *p = '\0'; return as_string_view(std::ranges::begin(iov), p); } } // namespace void upstream_accesslog(const std::vector &lfv, const LogSpec &lgsp) { auto config = get_config(); auto lgconf = log_config(); auto &accessconf = get_config()->logging.access; if (lgconf->accesslog_fd == -1 && !accessconf.syslog) { return; } std::array buf; auto downstream = lgsp.downstream; const auto &req = downstream->request(); const auto &resp = downstream->response(); const auto &tstamp = req.tstamp; auto &balloc = downstream->get_block_allocator(); auto downstream_addr = downstream->get_addr(); auto method = req.method == -1 ? ""sv : http2::to_method_string(req.method); auto path = req.method == HTTP_CONNECT ? req.authority : config->http2_proxy ? construct_absolute_request_uri(balloc, req) : req.path.empty() ? req.method == HTTP_OPTIONS ? "*"sv : "-"sv : req.path; auto path_without_query = req.method == HTTP_CONNECT ? path : std::string_view{std::ranges::begin(path), std::ranges::find(path, '?')}; auto p = std::span{buf}.first(buf.size() - 2); for (auto &lf : lfv) { switch (lf.type) { case LogFragmentType::LITERAL: p = copy(lf.value, p); break; case LogFragmentType::REMOTE_ADDR: p = copy(lgsp.remote_addr, p); break; case LogFragmentType::TIME_LOCAL: p = copy(tstamp->time_local, p); break; case LogFragmentType::TIME_ISO8601: p = copy(tstamp->time_iso8601, p); break; case LogFragmentType::REQUEST: p = copy(method, p); p = copy(' ', p); p = copy_escape(path, p); p = copy(" HTTP/"sv, p); p = copy(as_unsigned(req.http_major), p); if (req.http_major < 2) { p = copy('.', p); p = copy(as_unsigned(req.http_minor), p); } break; case LogFragmentType::METHOD: p = copy(method, p); break; case LogFragmentType::PATH: p = copy_escape(path, p); break; case LogFragmentType::PATH_WITHOUT_QUERY: p = copy_escape(path_without_query, p); break; case LogFragmentType::PROTOCOL_VERSION: p = copy("HTTP/"sv, p); p = copy(as_unsigned(req.http_major), p); if (req.http_major < 2) { p = copy('.', p); p = copy(as_unsigned(req.http_minor), p); } break; case LogFragmentType::STATUS: p = copy(resp.http_status, p); break; case LogFragmentType::BODY_BYTES_SENT: p = copy(as_unsigned(downstream->response_sent_body_length), p); break; case LogFragmentType::HTTP: { auto hd = req.fs.header(lf.value); if (hd) { p = copy_escape((*hd).value, p); break; } p = copy('-', p); break; } case LogFragmentType::AUTHORITY: if (!req.authority.empty()) { p = copy(req.authority, p); break; } p = copy('-', p); break; case LogFragmentType::REMOTE_PORT: p = copy(lgsp.remote_port, p); break; case LogFragmentType::SERVER_PORT: p = copy(lgsp.server_port, p); break; case LogFragmentType::REQUEST_TIME: { auto t = std::chrono::duration_cast( lgsp.request_end_time - downstream->get_request_start_time()) .count(); p = copy(as_unsigned(t / 1000), p); p = copy('.', p); auto frac = t % 1000; if (frac < 100) { auto n = static_cast(frac < 10 ? 2 : 1); p = copy(std::string_view{"000", n}, p); } p = copy(as_unsigned(frac), p); break; } case LogFragmentType::PID: p = copy(as_unsigned(lgsp.pid), p); break; case LogFragmentType::ALPN: p = copy_escape(lgsp.alpn, p); break; case LogFragmentType::TLS_CIPHER: if (!lgsp.ssl) { p = copy('-', p); break; } p = copy(SSL_get_cipher_name(lgsp.ssl), p); break; case LogFragmentType::TLS_PROTOCOL: if (!lgsp.ssl) { p = copy('-', p); break; } p = copy(nghttp2::tls::get_tls_protocol(lgsp.ssl), p); break; case LogFragmentType::TLS_SESSION_ID: { auto session = SSL_get_session(lgsp.ssl); if (!session) { p = copy('-', p); break; } unsigned int session_id_length = 0; auto session_id = SSL_SESSION_get_id(session, &session_id_length); if (session_id_length == 0) { p = copy('-', p); break; } p = copy_hex_low({session_id, session_id_length}, p); break; } case LogFragmentType::TLS_SESSION_REUSED: if (!lgsp.ssl) { p = copy('-', p); break; } p = copy(SSL_session_reused(lgsp.ssl) ? 'r' : '.', p); break; case LogFragmentType::TLS_SNI: if (lgsp.sni.empty()) { p = copy('-', p); break; } p = copy_escape(lgsp.sni, p); break; case LogFragmentType::TLS_CLIENT_FINGERPRINT_SHA1: case LogFragmentType::TLS_CLIENT_FINGERPRINT_SHA256: { if (!lgsp.ssl) { p = copy('-', p); break; } #if OPENSSL_3_0_0_API auto x = SSL_get0_peer_certificate(lgsp.ssl); #else // !OPENSSL_3_0_0_API auto x = SSL_get_peer_certificate(lgsp.ssl); #endif // !OPENSSL_3_0_0_API if (!x) { p = copy('-', p); break; } std::array buf; auto len = tls::get_x509_fingerprint( buf.data(), buf.size(), x, lf.type == LogFragmentType::TLS_CLIENT_FINGERPRINT_SHA256 ? EVP_sha256() : EVP_sha1()); #if !OPENSSL_3_0_0_API X509_free(x); #endif // !OPENSSL_3_0_0_API if (len <= 0) { p = copy('-', p); break; } p = copy_hex_low({buf.data(), static_cast(len)}, p); break; } case LogFragmentType::TLS_CLIENT_ISSUER_NAME: case LogFragmentType::TLS_CLIENT_SUBJECT_NAME: { if (!lgsp.ssl) { p = copy('-', p); break; } #if OPENSSL_3_0_0_API auto x = SSL_get0_peer_certificate(lgsp.ssl); #else // !OPENSSL_3_0_0_API auto x = SSL_get_peer_certificate(lgsp.ssl); #endif // !OPENSSL_3_0_0_API if (!x) { p = copy('-', p); break; } auto name = lf.type == LogFragmentType::TLS_CLIENT_ISSUER_NAME ? tls::get_x509_issuer_name(balloc, x) : tls::get_x509_subject_name(balloc, x); #if !OPENSSL_3_0_0_API X509_free(x); #endif // !OPENSSL_3_0_0_API if (name.empty()) { p = copy('-', p); break; } p = copy(name, p); break; } case LogFragmentType::TLS_CLIENT_SERIAL: { if (!lgsp.ssl) { p = copy('-', p); break; } #if OPENSSL_3_0_0_API auto x = SSL_get0_peer_certificate(lgsp.ssl); #else // !OPENSSL_3_0_0_API auto x = SSL_get_peer_certificate(lgsp.ssl); #endif // !OPENSSL_3_0_0_API if (!x) { p = copy('-', p); break; } auto sn = tls::get_x509_serial(balloc, x); #if !OPENSSL_3_0_0_API X509_free(x); #endif // !OPENSSL_3_0_0_API if (sn.empty()) { p = copy('-', p); break; } p = copy(sn, p); break; } case LogFragmentType::BACKEND_HOST: if (!downstream_addr) { p = copy('-', p); break; } p = copy(downstream_addr->host, p); break; case LogFragmentType::BACKEND_PORT: if (!downstream_addr) { p = copy('-', p); break; } p = copy(downstream_addr->port, p); break; case LogFragmentType::NONE: break; default: break; } } if (accessconf.syslog) { p[0] = '\0'; syslog(LOG_INFO, "%s", buf.data()); return; } p[0] = '\n'; p = p.subspan(1); auto nwrite = as_unsigned(std::ranges::distance( std::ranges::begin(std::span{buf}), std::ranges::begin(p))); while (write(lgconf->accesslog_fd, buf.data(), nwrite) == -1 && errno == EINTR) ; } int reopen_log_files(const LoggingConfig &loggingconf) { int res = 0; int new_accesslog_fd = -1; int new_errorlog_fd = -1; auto lgconf = log_config(); auto &accessconf = loggingconf.access; auto &errorconf = loggingconf.error; if (!accessconf.syslog && !accessconf.file.empty()) { new_accesslog_fd = open_log_file(accessconf.file.data()); if (new_accesslog_fd == -1) { LOG(ERROR) << "Failed to open accesslog file " << accessconf.file; res = -1; } } if (!errorconf.syslog && !errorconf.file.empty()) { new_errorlog_fd = open_log_file(errorconf.file.data()); if (new_errorlog_fd == -1) { if (lgconf->errorlog_fd != -1) { LOG(ERROR) << "Failed to open errorlog file " << errorconf.file; } else { std::cerr << "Failed to open errorlog file " << errorconf.file << std::endl; } res = -1; } } close_log_file(lgconf->accesslog_fd); close_log_file(lgconf->errorlog_fd); lgconf->accesslog_fd = new_accesslog_fd; lgconf->errorlog_fd = new_errorlog_fd; lgconf->errorlog_tty = (new_errorlog_fd == -1) ? false : isatty(new_errorlog_fd); return res; } void log_chld(pid_t pid, int rstatus, const char *msg) { std::string signalstr; if (WIFSIGNALED(rstatus)) { signalstr += "; signal "; auto sig = WTERMSIG(rstatus); auto s = strsignal(sig); if (s) { signalstr += s; signalstr += '('; } else { signalstr += "UNKNOWN("; } signalstr += util::utos(as_unsigned(sig)); signalstr += ')'; } LOG(NOTICE) << msg << ": [" << pid << "] exited " << (WIFEXITED(rstatus) ? "normally" : "abnormally") << " with status " << log::hex << rstatus << log::dec << "; exit status " << (WIFEXITED(rstatus) ? WEXITSTATUS(rstatus) : 0) << (signalstr.empty() ? "" : signalstr.c_str()); } void redirect_stderr_to_errorlog(const LoggingConfig &loggingconf) { auto lgconf = log_config(); auto &errorconf = loggingconf.error; if (errorconf.syslog || lgconf->errorlog_fd == -1) { return; } dup2(lgconf->errorlog_fd, STDERR_FILENO); } namespace { int STDERR_COPY = -1; int STDOUT_COPY = -1; } // namespace void store_original_fds() { // consider dup'ing stdout too STDERR_COPY = dup(STDERR_FILENO); STDOUT_COPY = STDOUT_FILENO; // no race here, since it is called early util::make_socket_closeonexec(STDERR_COPY); } void restore_original_fds() { dup2(STDERR_COPY, STDERR_FILENO); } void close_log_file(int &fd) { if (fd != STDERR_COPY && fd != STDOUT_COPY && fd != -1) { close(fd); } fd = -1; } int open_log_file(const char *path) { if (strcmp(path, "/dev/stdout") == 0 || strcmp(path, "/proc/self/fd/1") == 0) { return STDOUT_COPY; } if (strcmp(path, "/dev/stderr") == 0 || strcmp(path, "/proc/self/fd/2") == 0) { return STDERR_COPY; } #ifdef O_CLOEXEC auto fd = open(path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP); #else // !defined(O_CLOEXEC) auto fd = open(path, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP); // We get race condition if execve is called at the same time. if (fd != -1) { util::make_socket_closeonexec(fd); } #endif // !defined(O_CLOEXEC) if (fd == -1) { return -1; } return fd; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/http2.h0000644000000000000000000000013215077107270014261 xustar0030 mtime=1761382072.987444166 30 atime=1761382106.065311039 30 ctime=1761382109.061300494 nghttp2-1.68.0/src/http2.h0000644000175100017510000004146115077107270014657 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef HTTP2_H #define HTTP2_H #include "nghttp2_config.h" #include #include #include #include #include #include #include "urlparse.h" #include "util.h" #include "memchunk.h" #include "template.h" #include "allocator.h" #include "base64.h" namespace nghttp2 { struct Header { Header(std::string name, std::string value, bool no_index = false, int32_t token = -1) : name(std::move(name)), value(std::move(value)), token(token), no_index(no_index) {} Header() : token(-1), no_index(false) {} bool operator==(const Header &other) const { return name == other.name && value == other.value; } bool operator<(const Header &rhs) const { return name < rhs.name || (name == rhs.name && value < rhs.value); } std::string name; std::string value; int32_t token; bool no_index; }; struct HeaderRef { HeaderRef(const std::string_view &name, const std::string_view &value, bool no_index = false, int32_t token = -1) : name(name), value(value), token(token), no_index(no_index) {} HeaderRef() : token(-1), no_index(false) {} bool operator==(const HeaderRef &other) const { return name == other.name && value == other.value; } bool operator<(const HeaderRef &rhs) const { return name < rhs.name || (name == rhs.name && value < rhs.value); } std::string_view name; std::string_view value; int32_t token; bool no_index; }; using Headers = std::vector
; using HeaderRefs = std::vector; namespace http2 { // Returns reason-phrase for given |status code|. If there is no // known reason-phrase for the given code, returns empty string. std::string_view get_reason_phrase(unsigned int status_code); // Returns string version of |status_code|. (e.g., "404") std::string_view stringify_status(BlockAllocator &balloc, unsigned int status_code); void capitalize(DefaultMemchunks *buf, const std::string_view &s); Headers::value_type to_header(const std::string_view &name, const std::string_view &value, bool no_index, int32_t token); // Add name/value pairs to |nva|. If |no_index| is true, this // name/value pair won't be indexed when it is forwarded to the next // hop. void add_header(Headers &nva, const std::string_view &name, const std::string_view &value, bool no_index, int32_t token); // Returns pointer to the entry in |nva| which has name |name|. If // more than one entries which have the name |name|, last occurrence // in |nva| is returned. If no such entry exist, returns nullptr. const Headers::value_type *get_header(const Headers &nva, const std::string_view &name); // Returns true if the value of |nv| is not empty. bool non_empty_value(const HeaderRefs::value_type *nv); // Create nghttp2_nv from |name|, |value| and |flags|. inline nghttp2_nv make_field_flags(const std::string_view &name, const std::string_view &value, uint8_t flags = NGHTTP2_NV_FLAG_NONE) { auto ns = as_uint8_span(std::span{name}); auto vs = as_uint8_span(std::span{value}); return {const_cast(ns.data()), const_cast(vs.data()), ns.size(), vs.size(), flags}; } // Creates nghttp2_nv from |name|, |value| and |flags|. nghttp2 // library does not copy them. inline nghttp2_nv make_field(const std::string_view &name, const std::string_view &value, uint8_t flags = NGHTTP2_NV_FLAG_NONE) { return make_field_flags(name, value, static_cast(NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE | flags)); } // Creates nghttp2_nv from |name|, |value| and |flags|. nghttp2 // library copies |value| unless |flags| includes // NGHTTP2_NV_FLAG_NO_COPY_VALUE. inline nghttp2_nv make_field_v(const std::string_view &name, const std::string_view &value, uint8_t flags = NGHTTP2_NV_FLAG_NONE) { return make_field_flags( name, value, static_cast(NGHTTP2_NV_FLAG_NO_COPY_NAME | flags)); } // Creates nghttp2_nv from |name|, |value| and |flags|. nghttp2 // library copies |name| and |value| unless |flags| includes // NGHTTP2_NV_FLAG_NO_COPY_NAME or NGHTTP2_NV_FLAG_NO_COPY_VALUE. inline nghttp2_nv make_field_nv(const std::string_view &name, const std::string_view &value, uint8_t flags = NGHTTP2_NV_FLAG_NONE) { return make_field_flags(name, value, flags); } // Returns NGHTTP2_NV_FLAG_NO_INDEX if |no_index| is true, otherwise // NGHTTP2_NV_FLAG_NONE. inline uint8_t no_index(bool no_index) { return no_index ? NGHTTP2_NV_FLAG_NO_INDEX : NGHTTP2_NV_FLAG_NONE; } enum HeaderBuildOp { HDOP_NONE, // Forwarded header fields must be stripped. If this flag is not // set, all Forwarded header fields other than last one are added. HDOP_STRIP_FORWARDED = 1, // X-Forwarded-For header fields must be stripped. If this flag is // not set, all X-Forwarded-For header fields other than last one // are added. HDOP_STRIP_X_FORWARDED_FOR = 1 << 1, // X-Forwarded-Proto header fields must be stripped. If this flag // is not set, all X-Forwarded-Proto header fields other than last // one are added. HDOP_STRIP_X_FORWARDED_PROTO = 1 << 2, // Via header fields must be stripped. If this flag is not set, all // Via header fields other than last one are added. HDOP_STRIP_VIA = 1 << 3, // Early-Data header fields must be stripped. If this flag is not // set, all Early-Data header fields are added. HDOP_STRIP_EARLY_DATA = 1 << 4, // Strip above all header fields. HDOP_STRIP_ALL = HDOP_STRIP_FORWARDED | HDOP_STRIP_X_FORWARDED_FOR | HDOP_STRIP_X_FORWARDED_PROTO | HDOP_STRIP_VIA | HDOP_STRIP_EARLY_DATA, // Sec-WebSocket-Accept header field must be stripped. If this flag // is not set, all Sec-WebSocket-Accept header fields are added. HDOP_STRIP_SEC_WEBSOCKET_ACCEPT = 1 << 5, // Sec-WebSocket-Key header field must be stripped. If this flag is // not set, all Sec-WebSocket-Key header fields are added. HDOP_STRIP_SEC_WEBSOCKET_KEY = 1 << 6, // Transfer-Encoding header field must be stripped. If this flag is // not set, all Transfer-Encoding header fields are added. HDOP_STRIP_TRANSFER_ENCODING = 1 << 7, }; // Appends headers in |headers| to |nv|. |headers| must be indexed // before this call (its element's token field is assigned). Certain // headers, including disallowed headers in HTTP/2 spec and headers // which require special handling (i.e. via), are not copied. |flags| // is one or more of HeaderBuildOp flags. They tell function that // certain header fields should not be added. void copy_headers_to_nva(std::vector &nva, const HeaderRefs &headers, uint32_t flags); // Just like copy_headers_to_nva(), but this adds // NGHTTP2_NV_FLAG_NO_COPY_NAME and NGHTTP2_NV_FLAG_NO_COPY_VALUE. void copy_headers_to_nva_nocopy(std::vector &nva, const HeaderRefs &headers, uint32_t flags); // Appends HTTP/1.1 style header lines to |buf| from headers in // |headers|. |headers| must be indexed before this call (its // element's token field is assigned). Certain headers, which // requires special handling (i.e. via and cookie), are not appended. // |flags| is one or more of HeaderBuildOp flags. They tell function // that certain header fields should not be added. void build_http1_headers_from_headers(DefaultMemchunks *buf, const HeaderRefs &headers, uint32_t flags); // Return positive window_size_increment if WINDOW_UPDATE should be // sent for the stream |stream_id|. If |stream_id| == 0, this function // determines the necessity of the WINDOW_UPDATE for a connection. // // If the function determines WINDOW_UPDATE is not necessary at the // moment, it returns -1. int32_t determine_window_update_transmission(nghttp2_session *session, int32_t stream_id); // Dumps name/value pairs in |nva| of length |nvlen| to |out|. void dump_nv(FILE *out, const nghttp2_nv *nva, size_t nvlen); // Dumps name/value pairs in |nva| to |out|. void dump_nv(FILE *out, const HeaderRefs &nva); // Ereases header in |hd|. void erase_header(HeaderRef *hd); // Rewrites redirection URI which usually appears in location header // field. The |uri| is the URI in the location header field. The |u| // stores the result of parsed |uri|. The |request_authority| is the // host or :authority header field value in the request. The // |upstream_scheme| is either "https" or "http" in the upstream // interface. Rewrite is done only if location header field value // contains |match_host| as host excluding port. The |match_host| and // |request_authority| could be different. If |request_authority| is // empty, strip authority. // // This function returns the new rewritten URI on success. If the // location URI is not subject to the rewrite, this function returns // empty string. std::string_view rewrite_location_uri(BlockAllocator &balloc, const std::string_view &uri, const urlparse_url &u, const std::string_view &match_host, const std::string_view &request_authority, const std::string_view &upstream_scheme); // Returns parsed HTTP status code. Returns -1 on failure. int parse_http_status_code(const std::string_view &src); // Header fields to be indexed, except HD_MAXIDX which is convenient // member to get maximum value. // // generated by genheaderfunc.py enum { HD__AUTHORITY, HD__HOST, HD__METHOD, HD__PATH, HD__PROTOCOL, HD__SCHEME, HD__STATUS, HD_ACCEPT_ENCODING, HD_ACCEPT_LANGUAGE, HD_ALT_SVC, HD_CACHE_CONTROL, HD_CONNECTION, HD_CONTENT_LENGTH, HD_CONTENT_TYPE, HD_COOKIE, HD_DATE, HD_EARLY_DATA, HD_EXPECT, HD_FORWARDED, HD_HOST, HD_HTTP2_SETTINGS, HD_IF_MODIFIED_SINCE, HD_KEEP_ALIVE, HD_LINK, HD_LOCATION, HD_PRIORITY, HD_PROXY_CONNECTION, HD_SEC_WEBSOCKET_ACCEPT, HD_SEC_WEBSOCKET_KEY, HD_SERVER, HD_TE, HD_TRAILER, HD_TRANSFER_ENCODING, HD_UPGRADE, HD_USER_AGENT, HD_VIA, HD_X_FORWARDED_FOR, HD_X_FORWARDED_PROTO, HD_MAXIDX, }; using HeaderIndex = std::array; // Looks up header token for header name |name|. Only headers we are // interested in are tokenized. If header name cannot be tokenized, // returns -1. int lookup_token(const std::string_view &name); // Initializes |hdidx|, header index. The |hdidx| must point to the // array containing at least HD_MAXIDX elements. void init_hdidx(HeaderIndex &hdidx); // Indexes header |token| using index |idx|. void index_header(HeaderIndex &hdidx, int32_t token, size_t idx); struct LinkHeader { // The region of URI. This might not be NULL-terminated. std::string_view uri; }; // Returns next URI-reference in Link header field value |src|. If no // URI-reference found after searching all input, returned uri field // is empty. This imply that empty URI-reference is ignored during // parsing. std::vector parse_link_header(const std::string_view &src); // Constructs path by combining base path |base_path| with another // path |rel_path|. The base path and another path can have optional // query component. This function assumes |base_path| is normalized. // In other words, it does not contain ".." or "." path components // and starts with "/" if it is not empty. std::string path_join(const std::string_view &base, const std::string_view &base_query, const std::string_view &rel_path, const std::string_view &rel_query); std::string_view path_join(BlockAllocator &balloc, const std::string_view &base_path, const std::string_view &base_query, const std::string_view &rel_path, const std::string_view &rel_query); // true if response has body, taking into account the request method // and status code. bool expect_response_body(const std::string &method, uint32_t status_code); bool expect_response_body(int method_token, uint32_t status_code); // true if response has body, taking into account status code only. bool expect_response_body(uint32_t status_code); // Looks up method token for method name |name|. Only methods defined // in llhttp.h (llhttp_method) are tokenized. If method name cannot // be tokenized, returns -1. int lookup_method_token(const std::string_view &name); // Returns string representation of |method_token|. This is wrapper // around llhttp_method_name from llhttp. If |method_token| is // unknown, program aborts. The returned std::string_view is guaranteed to // be NULL-terminated. std::string_view to_method_string(int method_token); std::string_view normalize_path(BlockAllocator &balloc, const std::string_view &path, const std::string_view &query); // normalize_path_colon is like normalize_path, but it additionally // does percent-decoding %3A in order to workaround the issue that ':' // cannot be included in backend pattern. std::string_view normalize_path_colon(BlockAllocator &balloc, const std::string_view &path, const std::string_view &query); std::string normalize_path(const std::string_view &path, const std::string_view &query); std::string_view rewrite_clean_path(BlockAllocator &balloc, const std::string_view &src); // Returns path component of |uri|. The returned path does not // include query component. This function returns empty string if it // fails. std::string_view get_pure_path_component(const std::string_view &uri); // Deduces scheme, authority and path from given |uri|, and stores // them in |scheme|, |authority|, and |path| respectively. If |uri| // is relative path, path resolution takes place using path given in // |base| of length |baselen|. This function returns 0 if it // succeeds, or -1. int construct_push_component(BlockAllocator &balloc, std::string_view &scheme, std::string_view &authority, std::string_view &path, const std::string_view &base, const std::string_view &uri); // Returns true if te header field value |s| contains "trailers". bool contains_trailers(const std::string_view &s); // Creates Sec-WebSocket-Accept value for |key|. The capacity of // buffer pointed by |dest| must have at least 24 bytes (base64 // encoded length of 16 bytes data). It returns empty string in case // of error. std::string_view make_websocket_accept_token(uint8_t *dest, const std::string_view &key); // Returns true if HTTP version represents pre-HTTP/1.1 (e.g., // HTTP/0.9 or HTTP/1.0). bool legacy_http1(int major, int minor); // Returns true if transfer-encoding field value |s| conforms RFC // strictly. This function does not allow empty value, BWS, and empty // list elements. bool check_transfer_encoding(const std::string_view &s); // Encodes |extpri| in the wire format. std::string encode_extpri(const nghttp2_extpri &extpri); } // namespace http2 } // namespace nghttp2 #endif // !defined(HTTP2_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_connect_blocker.h0000644000000000000000000000013215077107270017576 xustar0030 mtime=1761382072.991444148 30 atime=1761382106.128310762 30 ctime=1761382109.124300312 nghttp2-1.68.0/src/shrpx_connect_blocker.h0000644000175100017510000000523615077107270020174 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_CONNECT_BLOCKER_H #define SHRPX_CONNECT_BLOCKER_H #include "shrpx.h" #include #include #include namespace shrpx { class ConnectBlocker { public: ConnectBlocker(std::mt19937 &gen, struct ev_loop *loop, std::function block_func, std::function unblock_func); ~ConnectBlocker(); // Returns true if making connection is not allowed. bool blocked() const; // Call this function if connect operation succeeded. This will // reset sleep_ to minimum value. void on_success(); // Call this function if connect operations failed. This will start // timer and blocks connection establishment with exponential // backoff. void on_failure(); size_t get_fail_count() const; // Peer is now considered offline. This effectively means that the // connection is blocked until online() is called. void offline(); // Peer is now considered online void online(); // Returns true if peer is considered offline. bool in_offline() const; void call_block_func(); void call_unblock_func(); private: std::mt19937 &gen_; // Called when blocking is started std::function block_func_; // Called when unblocked std::function unblock_func_; ev_timer timer_; struct ev_loop *loop_; // The number of consecutive connection failure. Reset to 0 on // success. size_t fail_count_; // true if peer is considered offline. bool offline_; }; } // namespace shrpx #endif // !defined(SHRPX_CONNECT_BLOCKER_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_client_handler.h0000644000000000000000000000013115077107270017416 xustar0030 mtime=1761382072.990444153 30 atime=1761382106.088310938 29 ctime=1761382109.08330043 nghttp2-1.68.0/src/shrpx_client_handler.h0000644000175100017510000002021015077107270020002 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_CLIENT_HANDLER_H #define SHRPX_CLIENT_HANDLER_H #include "shrpx.h" #include #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include "shrpx_rate_limit.h" #include "shrpx_connection.h" #include "buffer.h" #include "memchunk.h" #include "allocator.h" using namespace nghttp2; namespace shrpx { class Upstream; class DownstreamConnection; class HttpsUpstream; class ConnectBlocker; class DownstreamConnectionPool; class Worker; class Downstream; struct WorkerStat; struct DownstreamAddrGroup; struct SharedDownstreamAddr; struct DownstreamAddr; #ifdef ENABLE_HTTP3 class Http3Upstream; #endif // defined(ENABLE_HTTP3) class ClientHandler { public: ClientHandler(Worker *worker, int fd, SSL *ssl, const std::string_view &ipaddr, const std::string_view &port, int family, const UpstreamAddr *faddr); ~ClientHandler(); int noop(); // Performs clear text I/O int read_clear(); int write_clear(); // Specialized for PROXY-protocol use; peek data from socket. int proxy_protocol_peek_clear(); // Performs TLS handshake int tls_handshake(); // Performs TLS I/O int read_tls(); int write_tls(); int upstream_noop(); int upstream_read(); int upstream_http2_connhd_read(); int upstream_http1_connhd_read(); int upstream_write(); int proxy_protocol_read(); int proxy_protocol_v2_read(); int on_proxy_protocol_finish(); // Performs I/O operation. Internally calls on_read()/on_write(). int do_read(); int do_write(); // Processes buffers. No underlying I/O operation will be done. int on_read(); int on_write(); struct ev_loop *get_loop() const; void reset_upstream_read_timeout(ev_tstamp t); void reset_upstream_write_timeout(ev_tstamp t); int validate_next_proto(); const std::string_view &get_ipaddr() const; bool get_should_close_after_write() const; void set_should_close_after_write(bool f); Upstream *get_upstream(); void pool_downstream_connection(std::unique_ptr dconn); void remove_downstream_connection(DownstreamConnection *dconn); DownstreamAddr *get_downstream_addr(int &err, DownstreamAddrGroup *group, Downstream *downstream); // Returns DownstreamConnection object based on request path. This // function returns non-null DownstreamConnection, and assigns 0 to // |err| if it succeeds, or returns nullptr, and assigns negative // error code to |err|. std::unique_ptr get_downstream_connection(int &err, Downstream *downstream); MemchunkPool *get_mcpool(); SSL *get_ssl() const; // Call this function when HTTP/2 connection header is received at // the start of the connection. void direct_http2_upgrade(); // Performs HTTP/2 Upgrade from the connection managed by // |http|. If this function fails, the connection must be // terminated. This function returns 0 if it succeeds, or -1. int perform_http2_upgrade(HttpsUpstream *http); bool get_http2_upgrade_allowed() const; // Returns upstream scheme, either "http" or "https" std::string_view get_upstream_scheme() const; void start_immediate_shutdown(); // Writes upstream accesslog using |downstream|. The |downstream| // must not be nullptr. void write_accesslog(Downstream *downstream); Worker *get_worker() const; // Initializes forwarded_for_. void init_forwarded_for(int family, const std::string_view &ipaddr); using ReadBuf = DefaultMemchunkBuffer; ReadBuf *get_rb(); RateLimit *get_rlimit(); RateLimit *get_wlimit(); void signal_write(); ev_io *get_wev(); void setup_upstream_io_callback(); #ifdef ENABLE_HTTP3 void setup_http3_upstream(std::unique_ptr &&upstream); int read_quic(const UpstreamAddr *faddr, const Address &remote_addr, const Address &local_addr, const ngtcp2_pkt_info &pi, std::span data); int write_quic(); #endif // defined(ENABLE_HTTP3) // Returns string suitable for use in "by" parameter of Forwarded // header field. std::string_view get_forwarded_by() const; // Returns string suitable for use in "for" parameter of Forwarded // header field. std::string_view get_forwarded_for() const; Http2Session * get_http2_session(const std::shared_ptr &group, DownstreamAddr *addr); // Returns an affinity cookie value for |downstream|. |cookie_name| // is used to inspect cookie header field in request header fields. uint32_t get_affinity_cookie(Downstream *downstream, const std::string_view &cookie_name); DownstreamAddr *get_downstream_addr_strict_affinity( int &err, const std::shared_ptr &shared_addr, Downstream *downstream); const UpstreamAddr *get_upstream_addr() const; void repeat_read_timer(); void stop_read_timer(); Connection *get_connection(); // Stores |sni| which is TLS SNI extension value client sent in this // connection. void set_tls_sni(const std::string_view &sni); // Returns TLS SNI extension value client sent in this connection. std::string_view get_tls_sni() const; // Returns ALPN negotiated in this connection. std::string_view get_alpn() const; BlockAllocator &get_block_allocator(); void set_alpn_from_conn(); void set_local_hostport(const sockaddr *addr, socklen_t addrlen); private: // Allocator to allocate memory for connection-wide objects. Make // sure that the allocations must be bounded, and not proportional // to the number of requests. BlockAllocator balloc_; DefaultMemchunkBuffer rb_; Connection conn_; ev_timer reneg_shutdown_timer_; std::unique_ptr upstream_; // IP address of client. If UNIX domain socket is used, this is // "localhost". std::string_view ipaddr_; std::string_view port_; // The ALPN identifier negotiated for this connection. std::string_view alpn_; // The client address used in "for" parameter of Forwarded header // field. std::string_view forwarded_for_; // lowercased TLS SNI which client sent. std::string_view sni_; // The host and port of local address where the connection is // accepted. For QUIC connection, the local address may change due // to client address migration, but this value stays the same for // now. std::string_view local_hostport_; std::function read_, write_; std::function on_read_, on_write_; // Address of frontend listening socket const UpstreamAddr *faddr_; Worker *worker_; // The number of bytes of HTTP/2 client connection header to read size_t left_connhd_len_; // hash for session affinity using client IP uint32_t affinity_hash_; bool should_close_after_write_; // true if affinity_hash_ is computed bool affinity_hash_computed_; }; } // namespace shrpx #endif // !defined(SHRPX_CLIENT_HANDLER_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_mruby.h0000644000000000000000000000013115077107270015601 xustar0029 mtime=1761382072.99744412 30 atime=1761382106.181310528 30 ctime=1761382109.176300161 nghttp2-1.68.0/src/shrpx_mruby.h0000644000175100017510000000450315077107270016174 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_MRUBY_H #define SHRPX_MRUBY_H #include "shrpx.h" #include #include #include #include "template.h" using namespace nghttp2; namespace shrpx { class Downstream; namespace mruby { class MRubyContext { public: MRubyContext(mrb_state *mrb, mrb_value app, mrb_value env); ~MRubyContext(); int run_on_request_proc(Downstream *downstream); int run_on_response_proc(Downstream *downstream); int run_app(Downstream *downstream, int phase); void delete_downstream(Downstream *downstream); private: mrb_state *mrb_; mrb_value app_; mrb_value env_; }; enum { PHASE_NONE = 0, PHASE_REQUEST = 1, PHASE_RESPONSE = 1 << 1, }; struct MRubyAssocData { Downstream *downstream; int phase; }; RProc *compile(mrb_state *mrb, const std::string_view &filename); std::unique_ptr create_mruby_context(const std::string_view &filename); // Return interned |ptr|. mrb_sym intern_ptr(mrb_state *mrb, void *ptr); // Checks that |phase| is set in |phase_mask|. If not set, raise // exception. void check_phase(mrb_state *mrb, int phase, int phase_mask); } // namespace mruby } // namespace shrpx #endif // !defined(SHRPX_MRUBY_H) nghttp2-1.68.0/src/PaxHeaders/HttpServer.cc0000644000000000000000000000013215077107270015464 xustar0030 mtime=1761382072.983444185 30 atime=1761382106.239310273 30 ctime=1761382109.235299991 nghttp2-1.68.0/src/HttpServer.cc0000644000175100017510000017265115077107270016070 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "HttpServer.h" #include #ifdef HAVE_SYS_SOCKET_H # include #endif // defined(HAVE_SYS_SOCKET_H) #ifdef HAVE_NETDB_H # include #endif // defined(HAVE_NETDB_H) #ifdef HAVE_UNISTD_H # include #endif // defined(HAVE_UNISTD_H) #ifdef HAVE_FCNTL_H # include #endif // defined(HAVE_FCNTL_H) #ifdef HAVE_NETINET_IN_H # include #endif // defined(HAVE_NETINET_IN_H) #include #ifdef HAVE_ARPA_INET_H # include #endif // defined(HAVE_ARPA_INET_H) #include #include #include #include #include #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include # include # if OPENSSL_3_0_0_API # include # endif // OPENSSL_3_0_0_API #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include #include "app_helper.h" #include "http2.h" #include "util.h" #include "tls.h" #include "template.h" #ifndef O_BINARY # define O_BINARY (0) #endif // !defined(O_BINARY) using namespace std::chrono_literals; using namespace std::string_literals; namespace nghttp2 { namespace { // TODO could be constexpr constexpr auto DEFAULT_HTML = "index.html"sv; constexpr auto NGHTTPD_SERVER = "nghttpd nghttp2/" NGHTTP2_VERSION ""sv; } // namespace namespace { void delete_handler(Http2Handler *handler) { handler->remove_self(); delete handler; } } // namespace namespace { void print_session_id(int64_t id) { std::cout << "[id=" << id << "] "; } } // namespace Config::Config() : mime_types_file("/etc/mime.types"), groups("X25519:P-256:P-384:P-521"sv), stream_read_timeout(1_min), stream_write_timeout(1_min), data_ptr(nullptr), padding(0), num_worker(1), max_concurrent_streams(100), header_table_size(-1), encoder_header_table_size(-1), window_bits(-1), connection_window_bits(-1), port(0), verbose(false), daemon(false), verify_client(false), no_tls(false), error_gzip(false), early_response(false), hexdump(false), echo_upload(false), no_content_length(false), ktls(false) {} Config::~Config() {} namespace { void stream_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { int rv; auto stream = static_cast(w->data); auto hd = stream->handler; auto config = hd->get_config(); ev_timer_stop(hd->get_loop(), &stream->rtimer); ev_timer_stop(hd->get_loop(), &stream->wtimer); if (config->verbose) { print_session_id(hd->session_id()); print_timer(); std::cout << " timeout stream_id=" << stream->stream_id << std::endl; } hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); rv = hd->on_write(); if (rv == -1) { delete_handler(hd); } } } // namespace namespace { void add_stream_read_timeout(Stream *stream) { auto hd = stream->handler; ev_timer_again(hd->get_loop(), &stream->rtimer); } } // namespace namespace { void add_stream_read_timeout_if_pending(Stream *stream) { auto hd = stream->handler; if (ev_is_active(&stream->rtimer)) { ev_timer_again(hd->get_loop(), &stream->rtimer); } } } // namespace namespace { void add_stream_write_timeout(Stream *stream) { auto hd = stream->handler; ev_timer_again(hd->get_loop(), &stream->wtimer); } } // namespace namespace { void remove_stream_read_timeout(Stream *stream) { auto hd = stream->handler; ev_timer_stop(hd->get_loop(), &stream->rtimer); } } // namespace namespace { void remove_stream_write_timeout(Stream *stream) { auto hd = stream->handler; ev_timer_stop(hd->get_loop(), &stream->wtimer); } } // namespace namespace { void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config); } // namespace namespace { constexpr ev_tstamp RELEASE_FD_TIMEOUT = 2.; } // namespace namespace { void release_fd_cb(struct ev_loop *loop, ev_timer *w, int revents); } // namespace namespace { constexpr auto FILE_ENTRY_MAX_AGE = 10s; } // namespace namespace { constexpr size_t FILE_ENTRY_EVICT_THRES = 2048; } // namespace namespace { bool need_validation_file_entry( const FileEntry *ent, const std::chrono::steady_clock::time_point &now) { return ent->last_valid + FILE_ENTRY_MAX_AGE < now; } } // namespace namespace { bool validate_file_entry(FileEntry *ent, const std::chrono::steady_clock::time_point &now) { struct stat stbuf; int rv; rv = fstat(ent->fd, &stbuf); if (rv != 0) { ent->stale = true; return false; } if (stbuf.st_nlink == 0 || ent->mtime != stbuf.st_mtime) { ent->stale = true; return false; } ent->mtime = stbuf.st_mtime; ent->last_valid = now; return true; } } // namespace class Sessions { public: Sessions(HttpServer *sv, struct ev_loop *loop, const Config *config, SSL_CTX *ssl_ctx) : sv_(sv), loop_(loop), config_(config), ssl_ctx_(ssl_ctx), callbacks_(nullptr), option_(nullptr), next_session_id_(1), tstamp_cached_(ev_now(loop)), cached_date_( util::format_http_date(std::chrono::system_clock::from_time_t( static_cast(tstamp_cached_)))) { nghttp2_session_callbacks_new(&callbacks_); fill_callback(callbacks_, config_); nghttp2_option_new(&option_); if (config_->encoder_header_table_size != -1) { nghttp2_option_set_max_deflate_dynamic_table_size( option_, as_unsigned(config_->encoder_header_table_size)); } ev_timer_init(&release_fd_timer_, release_fd_cb, 0., RELEASE_FD_TIMEOUT); release_fd_timer_.data = this; } ~Sessions() { ev_timer_stop(loop_, &release_fd_timer_); for (auto handler : handlers_) { delete handler; } nghttp2_option_del(option_); nghttp2_session_callbacks_del(callbacks_); } void add_handler(Http2Handler *handler) { handlers_.insert(handler); } void remove_handler(Http2Handler *handler) { handlers_.erase(handler); if (handlers_.empty() && !fd_cache_.empty()) { ev_timer_again(loop_, &release_fd_timer_); } } SSL_CTX *get_ssl_ctx() const { return ssl_ctx_; } SSL *ssl_session_new(int fd) { SSL *ssl = SSL_new(ssl_ctx_); if (!ssl) { std::cerr << "SSL_new() failed" << std::endl; return nullptr; } if (SSL_set_fd(ssl, fd) == 0) { std::cerr << "SSL_set_fd() failed" << std::endl; SSL_free(ssl); return nullptr; } return ssl; } const Config *get_config() const { return config_; } struct ev_loop *get_loop() const { return loop_; } int64_t get_next_session_id() { auto session_id = next_session_id_; if (next_session_id_ == std::numeric_limits::max()) { next_session_id_ = 1; } else { ++next_session_id_; } return session_id; } const nghttp2_session_callbacks *get_callbacks() const { return callbacks_; } const nghttp2_option *get_option() const { return option_; } void accept_connection(int fd) { util::make_socket_nodelay(fd); SSL *ssl = nullptr; if (ssl_ctx_) { ssl = ssl_session_new(fd); if (!ssl) { close(fd); return; } } auto handler = std::make_unique(this, fd, ssl, get_next_session_id()); if (!ssl) { if (handler->connection_made() != 0) { return; } } add_handler(handler.release()); } void update_cached_date() { cached_date_ = util::format_http_date(std::chrono::system_clock::from_time_t( static_cast(tstamp_cached_))); } const std::string &get_cached_date() { auto t = ev_now(loop_); if (t != tstamp_cached_) { tstamp_cached_ = t; update_cached_date(); } return cached_date_; } FileEntry *get_cached_fd(const std::string &path) { auto range = fd_cache_.equal_range(path); if (range.first == range.second) { return nullptr; } auto now = std::chrono::steady_clock::now(); for (auto it = range.first; it != range.second;) { auto &ent = (*it).second; if (ent->stale) { ++it; continue; } if (need_validation_file_entry(ent.get(), now) && !validate_file_entry(ent.get(), now)) { if (ent->usecount == 0) { fd_cache_lru_.remove(ent.get()); close(ent->fd); it = fd_cache_.erase(it); continue; } ++it; continue; } fd_cache_lru_.remove(ent.get()); fd_cache_lru_.append(ent.get()); ++ent->usecount; return ent.get(); } return nullptr; } FileEntry *cache_fd(const std::string &path, const FileEntry &ent) { auto rv = fd_cache_.emplace(path, std::make_unique(ent)); auto &res = (*rv).second; res->it = rv; fd_cache_lru_.append(res.get()); while (fd_cache_.size() > FILE_ENTRY_EVICT_THRES) { auto ent = fd_cache_lru_.head; if (ent->usecount) { break; } fd_cache_lru_.remove(ent); close(ent->fd); fd_cache_.erase(ent->it); } return res.get(); } void release_fd(FileEntry *target) { --target->usecount; if (target->usecount == 0 && target->stale) { fd_cache_lru_.remove(target); close(target->fd); fd_cache_.erase(target->it); return; } // We use timer to close file descriptor and delete the entry from // cache. The timer will be started when there is no handler. } void release_unused_fd() { for (auto i = std::ranges::begin(fd_cache_); i != std::ranges::end(fd_cache_);) { auto &ent = (*i).second; if (ent->usecount != 0) { ++i; continue; } fd_cache_lru_.remove(ent.get()); close(ent->fd); i = fd_cache_.erase(i); } } const HttpServer *get_server() const { return sv_; } bool handlers_empty() const { return handlers_.empty(); } private: std::unordered_set handlers_; // cache for file descriptors to read file. std::unordered_multimap> fd_cache_; DList fd_cache_lru_; HttpServer *sv_; struct ev_loop *loop_; const Config *config_; SSL_CTX *ssl_ctx_; nghttp2_session_callbacks *callbacks_; nghttp2_option *option_; ev_timer release_fd_timer_; int64_t next_session_id_; ev_tstamp tstamp_cached_; std::string cached_date_; }; namespace { void release_fd_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto sessions = static_cast(w->data); ev_timer_stop(loop, w); if (!sessions->handlers_empty()) { return; } sessions->release_unused_fd(); } } // namespace Stream::Stream(Http2Handler *handler, int32_t stream_id) : balloc(1024, 1024), header{}, handler(handler), file_ent(nullptr), body_length(0), body_offset(0), header_buffer_size(0), stream_id(stream_id), echo_upload(false) { auto config = handler->get_config(); ev_timer_init(&rtimer, stream_timeout_cb, 0., config->stream_read_timeout); ev_timer_init(&wtimer, stream_timeout_cb, 0., config->stream_write_timeout); rtimer.data = this; wtimer.data = this; } Stream::~Stream() { if (file_ent != nullptr) { auto sessions = handler->get_sessions(); sessions->release_fd(file_ent); } auto &rcbuf = header.rcbuf; nghttp2_rcbuf_decref(rcbuf.method); nghttp2_rcbuf_decref(rcbuf.scheme); nghttp2_rcbuf_decref(rcbuf.authority); nghttp2_rcbuf_decref(rcbuf.host); nghttp2_rcbuf_decref(rcbuf.path); nghttp2_rcbuf_decref(rcbuf.ims); nghttp2_rcbuf_decref(rcbuf.expect); auto loop = handler->get_loop(); ev_timer_stop(loop, &rtimer); ev_timer_stop(loop, &wtimer); } namespace { void on_session_closed(Http2Handler *hd, int64_t session_id) { if (hd->get_config()->verbose) { print_session_id(session_id); print_timer(); std::cout << " closed" << std::endl; } } } // namespace namespace { void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { int rv; auto hd = static_cast(w->data); hd->terminate_session(NGHTTP2_SETTINGS_TIMEOUT); rv = hd->on_write(); if (rv == -1) { delete_handler(hd); } } } // namespace namespace { void readcb(struct ev_loop *loop, ev_io *w, int revents) { int rv; auto handler = static_cast(w->data); rv = handler->on_read(); if (rv == -1) { delete_handler(handler); } } } // namespace namespace { void writecb(struct ev_loop *loop, ev_io *w, int revents) { int rv; auto handler = static_cast(w->data); rv = handler->on_write(); if (rv == -1) { delete_handler(handler); } } } // namespace Http2Handler::Http2Handler(Sessions *sessions, int fd, SSL *ssl, int64_t session_id) : session_id_(session_id), session_(nullptr), sessions_(sessions), ssl_(ssl), data_pending_(nullptr), data_pendinglen_(0), fd_(fd) { ev_timer_init(&settings_timerev_, settings_timeout_cb, 10., 0.); ev_io_init(&wev_, writecb, fd, EV_WRITE); ev_io_init(&rev_, readcb, fd, EV_READ); settings_timerev_.data = this; wev_.data = this; rev_.data = this; auto loop = sessions_->get_loop(); ev_io_start(loop, &rev_); if (ssl) { SSL_set_accept_state(ssl); read_ = &Http2Handler::tls_handshake; write_ = &Http2Handler::tls_handshake; } else { read_ = &Http2Handler::read_clear; write_ = &Http2Handler::write_clear; } } Http2Handler::~Http2Handler() { on_session_closed(this, session_id_); nghttp2_session_del(session_); if (ssl_) { SSL_set_shutdown(ssl_, SSL_get_shutdown(ssl_) | SSL_RECEIVED_SHUTDOWN); ERR_clear_error(); SSL_shutdown(ssl_); } auto loop = sessions_->get_loop(); ev_timer_stop(loop, &settings_timerev_); ev_io_stop(loop, &rev_); ev_io_stop(loop, &wev_); if (ssl_) { SSL_free(ssl_); } shutdown(fd_, SHUT_WR); close(fd_); } void Http2Handler::remove_self() { sessions_->remove_handler(this); } struct ev_loop *Http2Handler::get_loop() const { return sessions_->get_loop(); } Http2Handler::WriteBuf *Http2Handler::get_wb() { return &wb_; } void Http2Handler::start_settings_timer() { ev_timer_start(sessions_->get_loop(), &settings_timerev_); } int Http2Handler::fill_wb() { if (data_pending_) { auto n = std::min(wb_.wleft(), data_pendinglen_); wb_.write(data_pending_, n); if (n < data_pendinglen_) { data_pending_ += n; data_pendinglen_ -= n; return 0; } data_pending_ = nullptr; data_pendinglen_ = 0; } for (;;) { const uint8_t *data; auto datalen = nghttp2_session_mem_send2(session_, &data); if (datalen < 0) { std::cerr << "nghttp2_session_mem_send2() returned error: " << nghttp2_strerror(static_cast(datalen)) << std::endl; return -1; } if (datalen == 0) { break; } auto n = wb_.write(data, as_unsigned(datalen)); if (n < static_cast(datalen)) { data_pending_ = data + n; data_pendinglen_ = as_unsigned(datalen) - n; break; } } return 0; } int Http2Handler::read_clear() { std::array buf; ssize_t nread; while ((nread = read(fd_, buf.data(), buf.size())) == -1 && errno == EINTR) ; if (nread == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return write_(*this); } return -1; } if (nread == 0) { return -1; } if (get_config()->hexdump) { util::hexdump(stdout, buf.data(), as_unsigned(nread)); } auto nrecv = nghttp2_session_mem_recv2(session_, buf.data(), as_unsigned(nread)); if (nrecv < 0) { if (nrecv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) { std::cerr << "nghttp2_session_mem_recv2() returned error: " << nghttp2_strerror(static_cast(nrecv)) << std::endl; } return -1; } return write_(*this); } int Http2Handler::write_clear() { auto loop = sessions_->get_loop(); for (;;) { if (wb_.rleft() > 0) { ssize_t nwrite; while ((nwrite = write(fd_, wb_.pos, wb_.rleft())) == -1 && errno == EINTR) ; if (nwrite == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { ev_io_start(loop, &wev_); return 0; } return -1; } wb_.drain(as_unsigned(nwrite)); continue; } wb_.reset(); if (fill_wb() != 0) { return -1; } if (wb_.rleft() == 0) { break; } } if (wb_.rleft() == 0) { ev_io_stop(loop, &wev_); } else { ev_io_start(loop, &wev_); } if (nghttp2_session_want_read(session_) == 0 && nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) { return -1; } return 0; } int Http2Handler::tls_handshake() { ev_io_stop(sessions_->get_loop(), &wev_); ERR_clear_error(); auto rv = SSL_do_handshake(ssl_); if (rv <= 0) { auto err = SSL_get_error(ssl_, rv); switch (err) { case SSL_ERROR_WANT_READ: return 0; case SSL_ERROR_WANT_WRITE: ev_io_start(sessions_->get_loop(), &wev_); return 0; default: return -1; } } if (sessions_->get_config()->verbose) { std::cerr << "SSL/TLS handshake completed" << std::endl; } if (verify_alpn_result() != 0) { return -1; } read_ = &Http2Handler::read_tls; write_ = &Http2Handler::write_tls; if (connection_made() != 0) { return -1; } if (sessions_->get_config()->verbose) { if (SSL_session_reused(ssl_)) { std::cerr << "SSL/TLS session reused" << std::endl; } } return 0; } int Http2Handler::read_tls() { std::array buf; ERR_clear_error(); for (;;) { auto rv = SSL_read(ssl_, buf.data(), buf.size()); if (rv <= 0) { auto err = SSL_get_error(ssl_, rv); switch (err) { case SSL_ERROR_WANT_READ: return write_(*this); case SSL_ERROR_WANT_WRITE: // renegotiation started return -1; default: return -1; } } auto nread = static_cast(rv); if (get_config()->hexdump) { util::hexdump(stdout, buf.data(), nread); } auto nrecv = nghttp2_session_mem_recv2(session_, buf.data(), nread); if (nrecv < 0) { if (nrecv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) { std::cerr << "nghttp2_session_mem_recv2() returned error: " << nghttp2_strerror(static_cast(nrecv)) << std::endl; } return -1; } if (SSL_pending(ssl_) == 0) { break; } } return write_(*this); } int Http2Handler::write_tls() { auto loop = sessions_->get_loop(); ERR_clear_error(); for (;;) { if (wb_.rleft() > 0) { auto rv = SSL_write(ssl_, wb_.pos, static_cast(wb_.rleft())); if (rv <= 0) { auto err = SSL_get_error(ssl_, rv); switch (err) { case SSL_ERROR_WANT_READ: // renegotiation started return -1; case SSL_ERROR_WANT_WRITE: ev_io_start(sessions_->get_loop(), &wev_); return 0; default: return -1; } } wb_.drain(static_cast(rv)); continue; } wb_.reset(); if (fill_wb() != 0) { return -1; } if (wb_.rleft() == 0) { break; } } if (wb_.rleft() == 0) { ev_io_stop(loop, &wev_); } else { ev_io_start(loop, &wev_); } if (nghttp2_session_want_read(session_) == 0 && nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) { return -1; } return 0; } int Http2Handler::on_read() { return read_(*this); } int Http2Handler::on_write() { return write_(*this); } int Http2Handler::connection_made() { int r; r = nghttp2_session_server_new2(&session_, sessions_->get_callbacks(), this, sessions_->get_option()); if (r != 0) { return r; } auto config = sessions_->get_config(); std::array entry; size_t niv = 2; entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; entry[0].value = static_cast(config->max_concurrent_streams); entry[1].settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES; entry[1].value = 1; if (config->header_table_size >= 0) { entry[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; entry[niv].value = static_cast(config->header_table_size); ++niv; } if (config->window_bits != -1) { entry[niv].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; entry[niv].value = (1 << config->window_bits) - 1; ++niv; } r = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), niv); if (r != 0) { return r; } if (config->connection_window_bits != -1) { r = nghttp2_session_set_local_window_size( session_, NGHTTP2_FLAG_NONE, 0, (1 << config->connection_window_bits) - 1); if (r != 0) { return r; } } if (ssl_ && !nghttp2::tls::check_http2_requirement(ssl_)) { terminate_session(NGHTTP2_INADEQUATE_SECURITY); } return on_write(); } int Http2Handler::verify_alpn_result() { const unsigned char *next_proto = nullptr; unsigned int next_proto_len; // Check the negotiated protocol in ALPN SSL_get0_alpn_selected(ssl_, &next_proto, &next_proto_len); if (next_proto) { auto proto = as_string_view(next_proto, next_proto_len); if (sessions_->get_config()->verbose) { std::cout << "The negotiated protocol: " << proto << std::endl; } if (util::check_h2_is_selected(proto)) { return 0; } } if (sessions_->get_config()->verbose) { std::cerr << "Client did not advertise HTTP/2 protocol." << " (nghttp2 expects " << NGHTTP2_PROTO_VERSION_ID << ")" << std::endl; } return -1; } int Http2Handler::submit_file_response(const std::string_view &status, Stream *stream, time_t last_modified, off_t file_length, const std::string *content_type, nghttp2_data_provider2 *data_prd) { std::string last_modified_str; auto nva = std::to_array({ http2::make_field(":status"sv, status), http2::make_field("server"sv, NGHTTPD_SERVER), http2::make_field("cache-control"sv, "max-age=3600"sv), http2::make_field_v("date"sv, sessions_->get_cached_date()), {}, {}, {}, {}, }); size_t nvlen = 4; if (!get_config()->no_content_length) { nva[nvlen++] = http2::make_field( "content-length"sv, util::make_string_ref_uint(stream->balloc, as_unsigned(file_length))); } if (last_modified != 0) { last_modified_str = util::format_http_date( std::chrono::system_clock::from_time_t(last_modified)); nva[nvlen++] = http2::make_field_v("last-modified"sv, last_modified_str); } if (content_type) { nva[nvlen++] = http2::make_field_v("content-type"sv, *content_type); } auto &trailer_names = get_config()->trailer_names; if (!trailer_names.empty()) { nva[nvlen++] = http2::make_field("trailer"sv, trailer_names); } return nghttp2_submit_response2(session_, stream->stream_id, nva.data(), nvlen, data_prd); } int Http2Handler::submit_response(const std::string_view &status, int32_t stream_id, const HeaderRefs &headers, nghttp2_data_provider2 *data_prd) { auto nva = std::vector(); nva.reserve(4 + headers.size()); nva.push_back(http2::make_field(":status"sv, status)); nva.push_back(http2::make_field("server"sv, NGHTTPD_SERVER)); nva.push_back(http2::make_field_v("date"sv, sessions_->get_cached_date())); if (data_prd) { auto &trailer_names = get_config()->trailer_names; if (!trailer_names.empty()) { nva.push_back(http2::make_field("trailer"sv, trailer_names)); } } for (auto &nv : headers) { nva.push_back( http2::make_field(nv.name, nv.value, http2::no_index(nv.no_index))); } int r = nghttp2_submit_response2(session_, stream_id, nva.data(), nva.size(), data_prd); return r; } int Http2Handler::submit_response(const std::string_view &status, int32_t stream_id, nghttp2_data_provider2 *data_prd) { auto nva = std::to_array({ http2::make_field(":status"sv, status), http2::make_field("server"sv, NGHTTPD_SERVER), http2::make_field_v("date"sv, sessions_->get_cached_date()), {}, }); size_t nvlen = 3; if (data_prd) { auto &trailer_names = get_config()->trailer_names; if (!trailer_names.empty()) { nva[nvlen++] = http2::make_field("trailer"sv, trailer_names); } } return nghttp2_submit_response2(session_, stream_id, nva.data(), nvlen, data_prd); } int Http2Handler::submit_non_final_response(const std::string &status, int32_t stream_id) { auto nva = std::to_array({http2::make_field_v(":status"sv, status)}); return nghttp2_submit_headers(session_, NGHTTP2_FLAG_NONE, stream_id, nullptr, nva.data(), nva.size(), nullptr); } int Http2Handler::submit_push_promise(Stream *stream, const std::string_view &push_path) { auto authority = stream->header.authority; if (authority.empty()) { authority = stream->header.host; } auto scheme = get_config()->no_tls ? "http"sv : "https"sv; auto nva = std::to_array({http2::make_field(":method"sv, "GET"sv), http2::make_field(":path"sv, push_path), http2::make_field(":scheme"sv, scheme), http2::make_field(":authority"sv, authority)}); auto promised_stream_id = nghttp2_submit_push_promise( session_, NGHTTP2_FLAG_END_HEADERS, stream->stream_id, nva.data(), nva.size(), nullptr); if (promised_stream_id < 0) { return promised_stream_id; } auto promised_stream = std::make_unique(this, promised_stream_id); auto &promised_header = promised_stream->header; promised_header.method = "GET"sv; promised_header.path = push_path; promised_header.scheme = scheme; promised_header.authority = make_string_ref(promised_stream->balloc, authority); add_stream(promised_stream_id, std::move(promised_stream)); return 0; } int Http2Handler::submit_rst_stream(Stream *stream, uint32_t error_code) { remove_stream_read_timeout(stream); remove_stream_write_timeout(stream); return nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, stream->stream_id, error_code); } void Http2Handler::add_stream(int32_t stream_id, std::unique_ptr stream) { id2stream_[stream_id] = std::move(stream); } void Http2Handler::remove_stream(int32_t stream_id) { id2stream_.erase(stream_id); } Stream *Http2Handler::get_stream(int32_t stream_id) { auto itr = id2stream_.find(stream_id); if (itr == std::ranges::end(id2stream_)) { return nullptr; } else { return (*itr).second.get(); } } int64_t Http2Handler::session_id() const { return session_id_; } Sessions *Http2Handler::get_sessions() const { return sessions_; } const Config *Http2Handler::get_config() const { return sessions_->get_config(); } void Http2Handler::remove_settings_timer() { ev_timer_stop(sessions_->get_loop(), &settings_timerev_); } void Http2Handler::terminate_session(uint32_t error_code) { nghttp2_session_terminate_session(session_, error_code); } nghttp2_ssize file_read_callback(nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *user_data) { int rv; auto hd = static_cast(user_data); auto stream = hd->get_stream(stream_id); auto nread = std::min(stream->body_length - stream->body_offset, static_cast(length)); *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY; if (nread == 0 || stream->body_length == stream->body_offset + nread) { *data_flags |= NGHTTP2_DATA_FLAG_EOF; auto config = hd->get_config(); if (!config->trailer.empty()) { std::vector nva; nva.reserve(config->trailer.size()); for (auto &kv : config->trailer) { nva.push_back(http2::make_field_nv(kv.name, kv.value, http2::no_index(kv.no_index))); } rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size()); if (rv != 0) { if (nghttp2_is_fatal(rv)) { return NGHTTP2_ERR_CALLBACK_FAILURE; } } else { *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; } } if (nghttp2_session_get_stream_remote_close(session, stream_id) == 0) { remove_stream_read_timeout(stream); remove_stream_write_timeout(stream); hd->submit_rst_stream(stream, NGHTTP2_NO_ERROR); } } return nread; } namespace { void prepare_status_response(Stream *stream, Http2Handler *hd, int status) { auto sessions = hd->get_sessions(); auto status_page = sessions->get_server()->get_status_page(status); auto file_ent = &status_page->file_ent; // we don't set stream->file_ent since we don't want to expire it. stream->body_length = file_ent->length; nghttp2_data_provider2 data_prd; data_prd.source.fd = file_ent->fd; data_prd.read_callback = file_read_callback; HeaderRefs headers; headers.reserve(2); headers.emplace_back("content-type"sv, "text/html; charset=UTF-8"sv); headers.emplace_back( "content-length"sv, util::make_string_ref_uint(stream->balloc, as_unsigned(file_ent->length))); hd->submit_response(status_page->status, stream->stream_id, headers, &data_prd); } } // namespace namespace { void prepare_echo_response(Stream *stream, Http2Handler *hd) { auto length = lseek(stream->file_ent->fd, 0, SEEK_END); if (length == -1) { hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); return; } stream->body_length = length; if (lseek(stream->file_ent->fd, 0, SEEK_SET) == -1) { hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); return; } nghttp2_data_provider2 data_prd; data_prd.source.fd = stream->file_ent->fd; data_prd.read_callback = file_read_callback; HeaderRefs headers; headers.emplace_back("nghttpd-response"sv, "echo"sv); if (!hd->get_config()->no_content_length) { headers.emplace_back( "content-length"sv, util::make_string_ref_uint(stream->balloc, as_unsigned(length))); } hd->submit_response("200"sv, stream->stream_id, headers, &data_prd); } } // namespace namespace { bool prepare_upload_temp_store(Stream *stream, Http2Handler *hd) { auto sessions = hd->get_sessions(); char tempfn[] = "/tmp/nghttpd.temp.XXXXXX"; auto fd = mkstemp(tempfn); if (fd == -1) { return false; } unlink(tempfn); // Ordinary request never start with "echo:". The length is 0 for // now. We will update it when we get whole request body. auto path = std::string("echo:") + tempfn; stream->file_ent = sessions->cache_fd(path, FileEntry(path, 0, 0, fd, nullptr, {}, true)); stream->echo_upload = true; return true; } } // namespace namespace { void prepare_redirect_response(Stream *stream, Http2Handler *hd, const std::string_view &path, int status) { auto scheme = stream->header.scheme; auto authority = stream->header.authority; if (authority.empty()) { authority = stream->header.host; } auto location = concat_string_ref(stream->balloc, scheme, "://"sv, authority, path); auto headers = HeaderRefs{{"location"sv, location}}; auto sessions = hd->get_sessions(); auto status_page = sessions->get_server()->get_status_page(status); hd->submit_response(status_page->status, stream->stream_id, headers, nullptr); } } // namespace namespace { void prepare_response(Stream *stream, Http2Handler *hd, bool allow_push = true) { int rv; auto reqpath = stream->header.path; if (reqpath.empty()) { prepare_status_response(stream, hd, 405); return; } auto ims = stream->header.ims; time_t last_mod = 0; bool last_mod_found = false; if (!ims.empty()) { last_mod_found = true; last_mod = util::parse_http_date(ims); } std::string_view raw_path, raw_query; auto query_pos = std::ranges::find(reqpath, '?'); if (query_pos != std::ranges::end(reqpath)) { // Do not response to this request to allow clients to test timeouts. if ("nghttpd_do_not_respond_to_req=yes"sv == std::string_view{query_pos, std::ranges::end(reqpath)}) { return; } raw_path = std::string_view{std::ranges::begin(reqpath), query_pos}; raw_query = std::string_view{query_pos, std::ranges::end(reqpath)}; } else { raw_path = reqpath; } auto sessions = hd->get_sessions(); std::string_view path; if (util::contains(raw_path, '%')) { path = util::percent_decode(stream->balloc, raw_path); } else { path = raw_path; } path = http2::path_join(stream->balloc, ""sv, ""sv, path, ""sv); if (util::contains(path, '\\')) { if (stream->file_ent) { sessions->release_fd(stream->file_ent); stream->file_ent = nullptr; } prepare_status_response(stream, hd, 404); return; } if (!hd->get_config()->push.empty()) { auto push_itr = hd->get_config()->push.find(std::string{path}); if (allow_push && push_itr != std::ranges::end(hd->get_config()->push)) { for (auto &push_path : (*push_itr).second) { rv = hd->submit_push_promise(stream, push_path); if (rv != 0) { std::cerr << "nghttp2_submit_push_promise() returned error: " << nghttp2_strerror(rv) << std::endl; } } } } std::string file_path; { auto len = hd->get_config()->htdocs.size() + path.size(); auto trailing_slash = path[path.size() - 1] == '/'; if (trailing_slash) { len += DEFAULT_HTML.size(); } file_path.resize(len); auto p = &file_path[0]; auto &htdocs = hd->get_config()->htdocs; p = std::ranges::copy(htdocs, p).out; p = std::ranges::copy(path, p).out; if (trailing_slash) { std::ranges::copy(DEFAULT_HTML, p); } } if (stream->echo_upload) { assert(stream->file_ent); prepare_echo_response(stream, hd); return; } auto file_ent = sessions->get_cached_fd(file_path); if (file_ent == nullptr) { int file = open(file_path.c_str(), O_RDONLY | O_BINARY); if (file == -1) { prepare_status_response(stream, hd, 404); return; } struct stat buf; if (fstat(file, &buf) == -1) { close(file); prepare_status_response(stream, hd, 404); return; } if (buf.st_mode & S_IFDIR) { close(file); auto reqpath = concat_string_ref(stream->balloc, raw_path, "/"sv, raw_query); prepare_redirect_response(stream, hd, reqpath, 301); return; } const std::string *content_type = nullptr; auto ext = file_path.c_str() + file_path.size() - 1; for (; file_path.c_str() < ext && *ext != '.' && *ext != '/'; --ext) ; if (*ext == '.') { ++ext; const auto &mime_types = hd->get_config()->mime_types; auto content_type_itr = mime_types.find(ext); if (content_type_itr != std::ranges::end(mime_types)) { content_type = &(*content_type_itr).second; } } file_ent = sessions->cache_fd( file_path, FileEntry(file_path, buf.st_size, buf.st_mtime, file, content_type, std::chrono::steady_clock::now())); } stream->file_ent = file_ent; if (last_mod_found && file_ent->mtime <= last_mod) { hd->submit_response("304"sv, stream->stream_id, nullptr); return; } auto method = stream->header.method; if (method == "HEAD"sv) { hd->submit_file_response("200"sv, stream, file_ent->mtime, file_ent->length, file_ent->content_type, nullptr); return; } stream->body_length = file_ent->length; nghttp2_data_provider2 data_prd; data_prd.source.fd = file_ent->fd; data_prd.read_callback = file_read_callback; hd->submit_file_response("200"sv, stream, file_ent->mtime, file_ent->length, file_ent->content_type, &data_prd); } } // namespace namespace { int on_header_callback2(nghttp2_session *session, const nghttp2_frame *frame, nghttp2_rcbuf *name, nghttp2_rcbuf *value, uint8_t flags, void *user_data) { auto hd = static_cast(user_data); auto namebuf = nghttp2_rcbuf_get_buf(name); auto valuebuf = nghttp2_rcbuf_get_buf(value); if (hd->get_config()->verbose) { print_session_id(hd->session_id()); verbose_on_header_callback(session, frame, namebuf.base, namebuf.len, valuebuf.base, valuebuf.len, flags, user_data); } if (frame->hd.type != NGHTTP2_HEADERS || frame->headers.cat != NGHTTP2_HCAT_REQUEST) { return 0; } auto stream = hd->get_stream(frame->hd.stream_id); if (!stream) { return 0; } if (stream->header_buffer_size + namebuf.len + valuebuf.len > 64_k) { hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); return 0; } stream->header_buffer_size += namebuf.len + valuebuf.len; auto token = http2::lookup_token(as_string_view(namebuf.base, namebuf.len)); auto &header = stream->header; switch (token) { case http2::HD__METHOD: header.method = as_string_view(valuebuf.base, valuebuf.len); header.rcbuf.method = value; nghttp2_rcbuf_incref(value); break; case http2::HD__SCHEME: header.scheme = as_string_view(valuebuf.base, valuebuf.len); header.rcbuf.scheme = value; nghttp2_rcbuf_incref(value); break; case http2::HD__AUTHORITY: header.authority = as_string_view(valuebuf.base, valuebuf.len); header.rcbuf.authority = value; nghttp2_rcbuf_incref(value); break; case http2::HD_HOST: header.host = as_string_view(valuebuf.base, valuebuf.len); header.rcbuf.host = value; nghttp2_rcbuf_incref(value); break; case http2::HD__PATH: header.path = as_string_view(valuebuf.base, valuebuf.len); header.rcbuf.path = value; nghttp2_rcbuf_incref(value); break; case http2::HD_IF_MODIFIED_SINCE: header.ims = as_string_view(valuebuf.base, valuebuf.len); header.rcbuf.ims = value; nghttp2_rcbuf_incref(value); break; case http2::HD_EXPECT: header.expect = as_string_view(valuebuf.base, valuebuf.len); header.rcbuf.expect = value; nghttp2_rcbuf_incref(value); break; } return 0; } } // namespace namespace { int on_begin_headers_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { auto hd = static_cast(user_data); if (frame->hd.type != NGHTTP2_HEADERS || frame->headers.cat != NGHTTP2_HCAT_REQUEST) { return 0; } auto stream = std::make_unique(hd, frame->hd.stream_id); add_stream_read_timeout(stream.get()); hd->add_stream(frame->hd.stream_id, std::move(stream)); return 0; } } // namespace namespace { int hd_on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { auto hd = static_cast(user_data); if (hd->get_config()->verbose) { print_session_id(hd->session_id()); verbose_on_frame_recv_callback(session, frame, user_data); } switch (frame->hd.type) { case NGHTTP2_DATA: { // TODO Handle POST auto stream = hd->get_stream(frame->hd.stream_id); if (!stream) { return 0; } if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { remove_stream_read_timeout(stream); if (stream->echo_upload || !hd->get_config()->early_response) { prepare_response(stream, hd); } } else { add_stream_read_timeout(stream); } break; } case NGHTTP2_HEADERS: { auto stream = hd->get_stream(frame->hd.stream_id); if (!stream) { return 0; } if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { auto expect100 = stream->header.expect; if (util::strieq("100-continue"sv, expect100)) { hd->submit_non_final_response("100", frame->hd.stream_id); } auto method = stream->header.method; if (hd->get_config()->echo_upload && (method == "POST"sv || method == "PUT"sv)) { if (!prepare_upload_temp_store(stream, hd)) { hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); return 0; } } else if (hd->get_config()->early_response) { prepare_response(stream, hd); } } if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { remove_stream_read_timeout(stream); if (stream->echo_upload || !hd->get_config()->early_response) { prepare_response(stream, hd); } } else { add_stream_read_timeout(stream); } break; } case NGHTTP2_SETTINGS: if (frame->hd.flags & NGHTTP2_FLAG_ACK) { hd->remove_settings_timer(); } break; default: break; } return 0; } } // namespace namespace { int hd_on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { auto hd = static_cast(user_data); if (hd->get_config()->verbose) { print_session_id(hd->session_id()); verbose_on_frame_send_callback(session, frame, user_data); } switch (frame->hd.type) { case NGHTTP2_DATA: case NGHTTP2_HEADERS: { auto stream = hd->get_stream(frame->hd.stream_id); if (!stream) { return 0; } if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { remove_stream_write_timeout(stream); } else if (std::min(nghttp2_session_get_stream_remote_window_size( session, frame->hd.stream_id), nghttp2_session_get_remote_window_size(session)) <= 0) { // If stream is blocked by flow control, enable write timeout. add_stream_read_timeout_if_pending(stream); add_stream_write_timeout(stream); } else { add_stream_read_timeout_if_pending(stream); remove_stream_write_timeout(stream); } break; } case NGHTTP2_SETTINGS: { if (frame->hd.flags & NGHTTP2_FLAG_ACK) { return 0; } hd->start_settings_timer(); break; } case NGHTTP2_PUSH_PROMISE: { auto promised_stream_id = frame->push_promise.promised_stream_id; auto promised_stream = hd->get_stream(promised_stream_id); auto stream = hd->get_stream(frame->hd.stream_id); if (!stream || !promised_stream) { return 0; } add_stream_read_timeout_if_pending(stream); add_stream_write_timeout(stream); prepare_response(promised_stream, hd, /*allow_push */ false); } } return 0; } } // namespace namespace { int send_data_callback(nghttp2_session *session, nghttp2_frame *frame, const uint8_t *framehd, size_t length, nghttp2_data_source *source, void *user_data) { auto hd = static_cast(user_data); auto wb = hd->get_wb(); auto padlen = frame->data.padlen; auto stream = hd->get_stream(frame->hd.stream_id); if (wb->wleft() < 9 + length + padlen) { return NGHTTP2_ERR_WOULDBLOCK; } int fd = source->fd; auto p = wb->last; p = std::ranges::copy_n(framehd, 9, p).out; if (padlen) { *p++ = static_cast(padlen - 1); } while (length) { ssize_t nread; while ((nread = pread(fd, p, length, stream->body_offset)) == -1 && errno == EINTR) ; if (nread == -1) { remove_stream_read_timeout(stream); remove_stream_write_timeout(stream); return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } stream->body_offset += nread; length -= as_unsigned(nread); p += nread; } if (padlen) { std::ranges::fill(p, p + padlen - 1, 0); p += padlen - 1; } wb->last = p; return 0; } } // namespace namespace { nghttp2_ssize select_padding_callback(nghttp2_session *session, const nghttp2_frame *frame, size_t max_payload, void *user_data) { auto hd = static_cast(user_data); return as_signed( std::min(max_payload, frame->hd.length + hd->get_config()->padding)); } } // namespace namespace { int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data) { auto hd = static_cast(user_data); auto stream = hd->get_stream(stream_id); if (!stream) { return 0; } if (stream->echo_upload) { assert(stream->file_ent); while (len) { ssize_t n; while ((n = write(stream->file_ent->fd, data, len)) == -1 && errno == EINTR) ; if (n == -1) { hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); return 0; } len -= as_unsigned(n); data += n; } } // TODO Handle POST add_stream_read_timeout(stream); return 0; } } // namespace namespace { int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *user_data) { auto hd = static_cast(user_data); hd->remove_stream(stream_id); if (hd->get_config()->verbose) { print_session_id(hd->session_id()); print_timer(); printf(" stream_id=%d closed\n", stream_id); fflush(stdout); } return 0; } } // namespace namespace { void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config) { nghttp2_session_callbacks_set_on_stream_close_callback( callbacks, on_stream_close_callback); nghttp2_session_callbacks_set_on_frame_recv_callback( callbacks, hd_on_frame_recv_callback); nghttp2_session_callbacks_set_on_frame_send_callback( callbacks, hd_on_frame_send_callback); if (config->verbose) { nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( callbacks, verbose_on_invalid_frame_recv_callback); nghttp2_session_callbacks_set_error_callback2(callbacks, verbose_error_callback); } nghttp2_session_callbacks_set_on_data_chunk_recv_callback( callbacks, on_data_chunk_recv_callback); nghttp2_session_callbacks_set_on_header_callback2(callbacks, on_header_callback2); nghttp2_session_callbacks_set_on_begin_headers_callback( callbacks, on_begin_headers_callback); nghttp2_session_callbacks_set_send_data_callback(callbacks, send_data_callback); if (config->padding) { nghttp2_session_callbacks_set_select_padding_callback2( callbacks, select_padding_callback); } nghttp2_session_callbacks_set_rand_callback(callbacks, util::secure_random); } } // namespace struct ClientInfo { int fd; }; struct Worker { std::unique_ptr sessions; ev_async w; // protects q std::mutex m; std::deque q; }; namespace { void worker_acceptcb(struct ev_loop *loop, ev_async *w, int revents) { auto worker = static_cast(w->data); auto &sessions = worker->sessions; std::deque q; { std::lock_guard lock(worker->m); q.swap(worker->q); } for (const auto &c : q) { sessions->accept_connection(c.fd); } } } // namespace namespace { void run_worker(Worker *worker) { auto loop = worker->sessions->get_loop(); ev_run(loop, 0); #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL wc_ecc_fp_free(); #endif // defined(NGHTTP2_OPENSSL_IS_WOLFSSL) } } // namespace namespace { unsigned int get_ev_loop_flags() { if (ev_supported_backends() & ~ev_recommended_backends() & EVBACKEND_KQUEUE) { return ev_recommended_backends() | EVBACKEND_KQUEUE; } return 0; } } // namespace class AcceptHandler { public: AcceptHandler(HttpServer *sv, Sessions *sessions, const Config *config) : sessions_(sessions), config_(config), next_worker_(0) { if (config_->num_worker == 1) { return; } for (size_t i = 0; i < config_->num_worker; ++i) { if (config_->verbose) { std::cerr << "spawning thread #" << i << std::endl; } auto worker = std::make_unique(); auto loop = ev_loop_new(get_ev_loop_flags()); worker->sessions = std::make_unique(sv, loop, config_, sessions_->get_ssl_ctx()); ev_async_init(&worker->w, worker_acceptcb); worker->w.data = worker.get(); ev_async_start(loop, &worker->w); auto t = std::thread(run_worker, worker.get()); t.detach(); workers_.push_back(std::move(worker)); } } void accept_connection(int fd) { if (config_->num_worker == 1) { sessions_->accept_connection(fd); return; } // Dispatch client to the one of the worker threads, in a round // robin manner. auto &worker = workers_[next_worker_]; if (next_worker_ == config_->num_worker - 1) { next_worker_ = 0; } else { ++next_worker_; } { std::lock_guard lock(worker->m); worker->q.push_back({fd}); } ev_async_send(worker->sessions->get_loop(), &worker->w); } private: std::vector> workers_; Sessions *sessions_; const Config *config_; // In multi threading mode, this points to the next thread that // client will be dispatched. size_t next_worker_; }; namespace { void acceptcb(struct ev_loop *loop, ev_io *w, int revents); } // namespace class ListenEventHandler { public: ListenEventHandler(Sessions *sessions, int fd, std::shared_ptr acceptor) : acceptor_(std::move(acceptor)), sessions_(sessions), fd_(fd) { ev_io_init(&w_, acceptcb, fd, EV_READ); w_.data = this; ev_io_start(sessions_->get_loop(), &w_); } void accept_connection() { for (;;) { #ifdef HAVE_ACCEPT4 auto fd = accept4(fd_, nullptr, nullptr, SOCK_NONBLOCK); #else // !defined(HAVE_ACCEPT4) auto fd = accept(fd_, nullptr, nullptr); #endif // !defined(HAVE_ACCEPT4) if (fd == -1) { break; } #ifndef HAVE_ACCEPT4 util::make_socket_nonblocking(fd); #endif // !defined(HAVE_ACCEPT4) acceptor_->accept_connection(fd); } } private: ev_io w_; std::shared_ptr acceptor_; Sessions *sessions_; int fd_; }; namespace { void acceptcb(struct ev_loop *loop, ev_io *w, int revents) { auto handler = static_cast(w->data); handler->accept_connection(); } } // namespace namespace { FileEntry make_status_body(uint32_t status, uint16_t port) { BlockAllocator balloc(1024, 1024); auto status_string = http2::stringify_status(balloc, status); auto reason_pharase = http2::get_reason_phrase(status); std::string body; body = ""; body += status_string; body += ' '; body += reason_pharase; body += "

"; body += status_string; body += ' '; body += reason_pharase; body += "


"; body += NGHTTPD_SERVER; body += " at port "; body += util::utos(port); body += "
"; body += ""; char tempfn[] = "/tmp/nghttpd.temp.XXXXXX"; int fd = mkstemp(tempfn); if (fd == -1) { auto error = errno; std::cerr << "Could not open status response body file: errno=" << error; assert(0); } unlink(tempfn); ssize_t nwrite; while ((nwrite = write(fd, body.c_str(), body.size())) == -1 && errno == EINTR) ; if (nwrite == -1) { auto error = errno; std::cerr << "Could not write status response body into file: errno=" << error; assert(0); } return FileEntry(util::utos(status), nwrite, 0, fd, nullptr, {}); } } // namespace // index into HttpServer::status_pages_ enum { IDX_200, IDX_301, IDX_400, IDX_404, IDX_405, }; HttpServer::HttpServer(const Config *config) : config_(config) { status_pages_ = std::vector{ {"200", make_status_body(200, config_->port)}, {"301", make_status_body(301, config_->port)}, {"400", make_status_body(400, config_->port)}, {"404", make_status_body(404, config_->port)}, {"405", make_status_body(405, config_->port)}, }; } namespace { int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { // We don't verify the client certificate. Just request it for the // testing purpose. return 1; } } // namespace namespace { int start_listen(HttpServer *sv, struct ev_loop *loop, Sessions *sessions, const Config *config) { int r; bool ok = false; const char *addr = nullptr; std::shared_ptr acceptor; auto service = util::utos(config->port); addrinfo hints{ .ai_flags = AI_PASSIVE #ifdef AI_ADDRCONFIG | AI_ADDRCONFIG #endif // defined(AI_ADDRCONFIG) , .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, }; if (!config->address.empty()) { addr = config->address.c_str(); } addrinfo *res, *rp; r = getaddrinfo(addr, service.c_str(), &hints, &res); if (r != 0) { std::cerr << "getaddrinfo() failed: " << gai_strerror(r) << std::endl; return -1; } for (rp = res; rp; rp = rp->ai_next) { int fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (fd == -1) { continue; } int val = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, static_cast(sizeof(val))) == -1) { close(fd); continue; } (void)util::make_socket_nonblocking(fd); #ifdef IPV6_V6ONLY if (rp->ai_family == AF_INET6) { if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, static_cast(sizeof(val))) == -1) { close(fd); continue; } } #endif // defined(IPV6_V6ONLY) if (bind(fd, rp->ai_addr, rp->ai_addrlen) == 0 && listen(fd, 1000) == 0) { if (!acceptor) { acceptor = std::make_shared(sv, sessions, config); } new ListenEventHandler(sessions, fd, acceptor); if (config->verbose) { std::string s = util::numeric_name(rp->ai_addr, rp->ai_addrlen); std::cout << (rp->ai_family == AF_INET ? "IPv4" : "IPv6") << ": listen " << s << ":" << config->port << std::endl; } ok = true; continue; } else { std::cerr << strerror(errno) << std::endl; } close(fd); } freeaddrinfo(res); if (!ok) { return -1; } return 0; } } // namespace namespace { int alpn_select_proto_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { auto config = static_cast(arg)->get_config(); if (config->verbose) { std::cout << "[ALPN] client offers:" << std::endl; } if (config->verbose) { for (unsigned int i = 0; i < inlen; i += in[i] + 1) { std::cout << " * "; std::cout.write(reinterpret_cast(&in[i + 1]), in[i]); std::cout << std::endl; } } if (!util::select_h2(out, outlen, in, inlen)) { return SSL_TLSEXT_ERR_NOACK; } return SSL_TLSEXT_ERR_OK; } } // namespace int HttpServer::run() { SSL_CTX *ssl_ctx = nullptr; if (!config_->no_tls) { ssl_ctx = SSL_CTX_new(TLS_server_method()); if (!ssl_ctx) { std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl; return -1; } auto ssl_opts = static_cast( (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_TICKET | SSL_OP_CIPHER_SERVER_PREFERENCE); #ifdef SSL_OP_ENABLE_KTLS if (config_->ktls) { ssl_opts |= SSL_OP_ENABLE_KTLS; } #endif // defined(SSL_OP_ENABLE_KTLS) SSL_CTX_set_options(ssl_ctx, ssl_opts); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); if (nghttp2::tls::ssl_ctx_set_proto_versions( ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION, nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) { std::cerr << "Could not set TLS versions" << std::endl; return -1; } if (SSL_CTX_set_cipher_list(ssl_ctx, tls::DEFAULT_CIPHER_LIST.data()) == 0) { std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl; return -1; } #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL if (SSL_CTX_set_ciphersuites(ssl_ctx, tls::DEFAULT_TLS13_CIPHER_LIST.data()) == 0) { std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl; return -1; } #endif // defined(NGHTTP2_OPENSSL_IS_WOLFSSL) const unsigned char sid_ctx[] = "nghttpd"; SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx) - 1); SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER); if (SSL_CTX_set1_groups_list(ssl_ctx, config_->groups.data()) != 1) { std::cerr << "SSL_CTX_set1_groups_list failed: " << ERR_error_string(ERR_get_error(), nullptr); return -1; } if (!config_->dh_param_file.empty()) { // Read DH parameters from file auto bio = BIO_new_file(config_->dh_param_file.c_str(), "rb"); if (bio == nullptr) { std::cerr << "BIO_new_file() failed: " << ERR_error_string(ERR_get_error(), nullptr) << std::endl; return -1; } #if OPENSSL_3_0_0_API EVP_PKEY *dh = nullptr; auto dctx = OSSL_DECODER_CTX_new_for_pkey( &dh, "PEM", nullptr, "DH", OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS, nullptr, nullptr); if (!OSSL_DECODER_from_bio(dctx, bio)) { std::cerr << "OSSL_DECODER_from_bio() failed: " << ERR_error_string(ERR_get_error(), nullptr) << std::endl; return -1; } if (SSL_CTX_set0_tmp_dh_pkey(ssl_ctx, dh) != 1) { std::cerr << "SSL_CTX_set0_tmp_dh_pkey failed: " << ERR_error_string(ERR_get_error(), nullptr) << std::endl; return -1; } #else // !OPENSSL_3_0_0_API auto dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr); if (dh == nullptr) { std::cerr << "PEM_read_bio_DHparams() failed: " << ERR_error_string(ERR_get_error(), nullptr) << std::endl; return -1; } SSL_CTX_set_tmp_dh(ssl_ctx, dh); DH_free(dh); #endif // !OPENSSL_3_0_0_API BIO_free(bio); } if (SSL_CTX_use_PrivateKey_file(ssl_ctx, config_->private_key_file.c_str(), SSL_FILETYPE_PEM) != 1) { std::cerr << "SSL_CTX_use_PrivateKey_file failed." << std::endl; return -1; } if (SSL_CTX_use_certificate_chain_file(ssl_ctx, config_->cert_file.c_str()) != 1) { std::cerr << "SSL_CTX_use_certificate_file failed." << std::endl; return -1; } if (SSL_CTX_check_private_key(ssl_ctx) != 1) { std::cerr << "SSL_CTX_check_private_key failed." << std::endl; return -1; } if (config_->verify_client) { SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback); } // ALPN selection callback SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, this); #if defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && defined(HAVE_LIBBROTLI) if (!SSL_CTX_add_cert_compression_alg( ssl_ctx, nghttp2::tls::CERTIFICATE_COMPRESSION_ALGO_BROTLI, nghttp2::tls::cert_compress, nghttp2::tls::cert_decompress)) { std::cerr << "SSL_CTX_add_cert_compression_alg failed." << std::endl; return -1; } #endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && // defined(HAVE_LIBBROTLI) if (tls::setup_keylog_callback(ssl_ctx) != 0) { std::cerr << "Failed to setup keylog" << std::endl; return -1; } } auto loop = EV_DEFAULT; Sessions sessions(this, loop, config_, ssl_ctx); if (start_listen(this, loop, &sessions, config_) != 0) { std::cerr << "Could not listen" << std::endl; if (ssl_ctx) { SSL_CTX_free(ssl_ctx); } return -1; } ev_run(loop, 0); SSL_CTX_free(ssl_ctx); return 0; } const Config *HttpServer::get_config() const { return config_; } const StatusPage *HttpServer::get_status_page(int status) const { switch (status) { case 200: return &status_pages_[IDX_200]; case 301: return &status_pages_[IDX_301]; case 400: return &status_pages_[IDX_400]; case 404: return &status_pages_[IDX_404]; case 405: return &status_pages_[IDX_405]; default: assert(0); } return nullptr; } } // namespace nghttp2 nghttp2-1.68.0/src/PaxHeaders/shrpx_client_handler.cc0000644000000000000000000000013215077107270017555 xustar0030 mtime=1761382072.990444153 30 atime=1761382106.086310947 30 ctime=1761382109.082300433 nghttp2-1.68.0/src/shrpx_client_handler.cc0000644000175100017510000013031715077107270020152 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_client_handler.h" #ifdef HAVE_UNISTD_H # include #endif // defined(HAVE_UNISTD_H) #ifdef HAVE_SYS_SOCKET_H # include #endif // defined(HAVE_SYS_SOCKET_H) #ifdef HAVE_NETDB_H # include #endif // defined(HAVE_NETDB_H) #include #include #include "shrpx_upstream.h" #include "shrpx_http2_upstream.h" #include "shrpx_https_upstream.h" #include "shrpx_config.h" #include "shrpx_http_downstream_connection.h" #include "shrpx_http2_downstream_connection.h" #include "shrpx_tls.h" #include "shrpx_worker.h" #include "shrpx_downstream_connection_pool.h" #include "shrpx_downstream.h" #include "shrpx_http2_session.h" #include "shrpx_connect_blocker.h" #include "shrpx_api_downstream_connection.h" #include "shrpx_health_monitor_downstream_connection.h" #include "shrpx_null_downstream_connection.h" #ifdef ENABLE_HTTP3 # include "shrpx_http3_upstream.h" #endif // defined(ENABLE_HTTP3) #include "shrpx_log.h" #include "util.h" #include "template.h" #include "tls.h" using namespace nghttp2; namespace shrpx { namespace { void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { auto conn = static_cast(w->data); auto handler = static_cast(conn->data); if (LOG_ENABLED(INFO)) { CLOG(INFO, handler) << "Time out"; } delete handler; } } // namespace namespace { void shutdowncb(struct ev_loop *loop, ev_timer *w, int revents) { auto handler = static_cast(w->data); if (LOG_ENABLED(INFO)) { CLOG(INFO, handler) << "Close connection due to TLS renegotiation"; } delete handler; } } // namespace namespace { void readcb(struct ev_loop *loop, ev_io *w, int revents) { auto conn = static_cast(w->data); auto handler = static_cast(conn->data); if (handler->do_read() != 0) { delete handler; return; } } } // namespace namespace { void writecb(struct ev_loop *loop, ev_io *w, int revents) { auto conn = static_cast(w->data); auto handler = static_cast(conn->data); if (handler->do_write() != 0) { delete handler; return; } } } // namespace int ClientHandler::noop() { return 0; } int ClientHandler::read_clear() { auto should_break = false; rb_.ensure_chunk(); for (;;) { if (rb_.rleft() && on_read() != 0) { return -1; } if (rb_.rleft() == 0) { rb_.reset(); } else if (rb_.wleft() == 0) { conn_.rlimit.stopw(); return 0; } if (!ev_is_active(&conn_.rev) || should_break) { return 0; } auto nread = conn_.read_clear(rb_.last(), rb_.wleft()); if (nread == 0) { if (rb_.rleft() == 0) { rb_.release_chunk(); } return 0; } if (nread < 0) { return -1; } rb_.write(as_unsigned(nread)); should_break = true; } } int ClientHandler::write_clear() { std::array iov; for (;;) { if (on_write() != 0) { return -1; } auto iovcnt = upstream_->response_riovec(iov.data(), iov.size()); if (iovcnt == 0) { break; } auto nwrite = conn_.writev_clear(iov.data(), iovcnt); if (nwrite < 0) { return -1; } if (nwrite == 0) { return 0; } upstream_->response_drain(as_unsigned(nwrite)); } conn_.wlimit.stopw(); ev_timer_stop(conn_.loop, &conn_.wt); return 0; } int ClientHandler::proxy_protocol_peek_clear() { rb_.ensure_chunk(); assert(rb_.rleft() == 0); auto nread = conn_.peek_clear(rb_.last(), rb_.wleft()); if (nread < 0) { return -1; } if (nread == 0) { return 0; } if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol: Peek " << nread << " bytes from socket"; } rb_.write(as_unsigned(nread)); if (on_read() != 0) { return -1; } rb_.reset(); return 0; } int ClientHandler::tls_handshake() { ev_timer_again(conn_.loop, &conn_.rt); ERR_clear_error(); auto rv = conn_.tls_handshake(); if (rv == SHRPX_ERR_INPROGRESS) { return 0; } if (rv < 0) { return -1; } if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "SSL/TLS handshake completed"; } if (validate_next_proto() != 0) { return -1; } read_ = &ClientHandler::read_tls; write_ = &ClientHandler::write_tls; return 0; } int ClientHandler::read_tls() { auto should_break = false; ERR_clear_error(); rb_.ensure_chunk(); for (;;) { // we should process buffered data first before we read EOF. if (rb_.rleft() && on_read() != 0) { return -1; } if (rb_.rleft() == 0) { rb_.reset(); } else if (rb_.wleft() == 0) { conn_.rlimit.stopw(); return 0; } if (!ev_is_active(&conn_.rev) || should_break) { return 0; } auto nread = conn_.read_tls(rb_.last(), rb_.wleft()); if (nread == 0) { if (rb_.rleft() == 0) { rb_.release_chunk(); } return 0; } if (nread < 0) { return -1; } rb_.write(as_unsigned(nread)); should_break = true; } } int ClientHandler::write_tls() { struct iovec iov; ERR_clear_error(); if (on_write() != 0) { return -1; } auto iovcnt = upstream_->response_riovec(&iov, 1); if (iovcnt == 0) { conn_.start_tls_write_idle(); conn_.wlimit.stopw(); ev_timer_stop(conn_.loop, &conn_.wt); return 0; } for (;;) { auto nwrite = conn_.write_tls(iov.iov_base, iov.iov_len); if (nwrite < 0) { return -1; } if (nwrite == 0) { return 0; } upstream_->response_drain(as_unsigned(nwrite)); iovcnt = upstream_->response_riovec(&iov, 1); if (iovcnt == 0) { return 0; } } } #ifdef ENABLE_HTTP3 int ClientHandler::read_quic(const UpstreamAddr *faddr, const Address &remote_addr, const Address &local_addr, const ngtcp2_pkt_info &pi, std::span data) { // Rate limiting is implemented by dropping an incoming packet. auto &rlimit = conn_.rlimit; if (rlimit.avail() < data.size()) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "Dropped QUIC packet of " << data.size() << " bytes due to rate limiting"; } return 0; } rlimit.drain(data.size()); auto upstream = static_cast(upstream_.get()); return upstream->on_read(faddr, remote_addr, local_addr, pi, data); } int ClientHandler::write_quic() { return upstream_->on_write(); } #endif // defined(ENABLE_HTTP3) int ClientHandler::upstream_noop() { return 0; } int ClientHandler::upstream_read() { assert(upstream_); if (upstream_->on_read() != 0) { return -1; } return 0; } int ClientHandler::upstream_write() { assert(upstream_); if (upstream_->on_write() != 0) { return -1; } if (get_should_close_after_write() && upstream_->response_empty()) { return -1; } return 0; } int ClientHandler::upstream_http2_connhd_read() { auto nread = std::min(left_connhd_len_, rb_.rleft()); if (memcmp(&NGHTTP2_CLIENT_MAGIC[NGHTTP2_CLIENT_MAGIC_LEN - left_connhd_len_], rb_.pos(), nread) != 0) { // There is no downgrade path here. Just drop the connection. if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "invalid client connection header"; } return -1; } left_connhd_len_ -= nread; rb_.drain(nread); conn_.rlimit.startw(); if (left_connhd_len_ == 0) { on_read_ = &ClientHandler::upstream_read; // Run on_read to process data left in buffer since they are not // notified further if (on_read() != 0) { return -1; } return 0; } return 0; } int ClientHandler::upstream_http1_connhd_read() { auto nread = std::min(left_connhd_len_, rb_.rleft()); if (memcmp(&NGHTTP2_CLIENT_MAGIC[NGHTTP2_CLIENT_MAGIC_LEN - left_connhd_len_], rb_.pos(), nread) != 0) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "This is HTTP/1.1 connection, " << "but may be upgraded to HTTP/2 later."; } // Reset header length for later HTTP/2 upgrade left_connhd_len_ = NGHTTP2_CLIENT_MAGIC_LEN; on_read_ = &ClientHandler::upstream_read; on_write_ = &ClientHandler::upstream_write; if (on_read() != 0) { return -1; } return 0; } left_connhd_len_ -= nread; rb_.drain(nread); conn_.rlimit.startw(); if (left_connhd_len_ == 0) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "direct HTTP/2 connection"; } direct_http2_upgrade(); on_read_ = &ClientHandler::upstream_read; on_write_ = &ClientHandler::upstream_write; // Run on_read to process data left in buffer since they are not // notified further if (on_read() != 0) { return -1; } return 0; } return 0; } ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl, const std::string_view &ipaddr, const std::string_view &port, int family, const UpstreamAddr *faddr) : // We use balloc_ for TLS session ID (64), ipaddr (IPv6) (39), // port (5), forwarded-for (IPv6) (41), alpn (5), proxyproto // ipaddr (15), proxyproto port (5), sni (32, estimated). we // need terminal NULL byte for each. We also require 8 bytes // header for each allocation. We align at 16 bytes boundary, // so the required space is 64 + 48 + 16 + 48 + 16 + 16 + 16 + // 32 + 8 + 8 * 8 = 328. balloc_(512, 512), rb_(worker->get_mcpool()), conn_(worker->get_loop(), fd, ssl, worker->get_mcpool(), get_config()->conn.upstream.timeout.write, get_config()->conn.upstream.timeout.idle, get_config()->conn.upstream.ratelimit.write, get_config()->conn.upstream.ratelimit.read, writecb, readcb, timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold, get_config()->tls.dyn_rec.idle_timeout, faddr->quic ? Proto::HTTP3 : Proto::NONE), ipaddr_(make_string_ref(balloc_, ipaddr)), port_(make_string_ref(balloc_, port)), faddr_(faddr), worker_(worker), left_connhd_len_(NGHTTP2_CLIENT_MAGIC_LEN), affinity_hash_(0), should_close_after_write_(false), affinity_hash_computed_(false) { ++worker_->get_worker_stat()->num_connections; ev_timer_init(&reneg_shutdown_timer_, shutdowncb, 0., 0.); reneg_shutdown_timer_.data = this; if (!faddr->quic) { conn_.rlimit.startw(); } ev_timer_again(conn_.loop, &conn_.rt); auto config = get_config(); if (!faddr->quic) { if (faddr_->accept_proxy_protocol || config->conn.upstream.accept_proxy_protocol) { read_ = &ClientHandler::proxy_protocol_peek_clear; write_ = &ClientHandler::noop; on_read_ = &ClientHandler::proxy_protocol_read; on_write_ = &ClientHandler::upstream_noop; } else { setup_upstream_io_callback(); } } auto &fwdconf = config->http.forwarded; if (fwdconf.params & FORWARDED_FOR) { if (fwdconf.for_node_type == ForwardedNode::OBFUSCATED) { // 1 for '_' auto len = SHRPX_OBFUSCATED_NODE_LENGTH + 1; // 1 for terminating NUL. auto buf = make_byte_ref(balloc_, len + 1); auto p = std::ranges::begin(buf); *p++ = '_'; p = util::random_alpha_digit(p, p + SHRPX_OBFUSCATED_NODE_LENGTH, worker_->get_randgen()); *p = '\0'; forwarded_for_ = as_string_view(std::ranges::begin(buf), p); } else { init_forwarded_for(family, ipaddr_); } } } void ClientHandler::init_forwarded_for(int family, const std::string_view &ipaddr) { if (family == AF_INET6) { // 2 for '[' and ']' auto len = 2 + ipaddr.size(); // 1 for terminating NUL. auto buf = make_byte_ref(balloc_, len + 1); auto p = std::ranges::begin(buf); *p++ = '['; p = std::ranges::copy(ipaddr, p).out; *p++ = ']'; *p = '\0'; forwarded_for_ = as_string_view(std::ranges::begin(buf), p); } else { // family == AF_INET or family == AF_UNIX forwarded_for_ = ipaddr; } } void ClientHandler::setup_upstream_io_callback() { if (conn_.tls.ssl) { conn_.prepare_server_handshake(); read_ = write_ = &ClientHandler::tls_handshake; on_read_ = &ClientHandler::upstream_noop; on_write_ = &ClientHandler::upstream_write; } else { // For non-TLS version, first create HttpsUpstream. It may be // upgraded to HTTP/2 through HTTP Upgrade or direct HTTP/2 // connection. upstream_ = std::make_unique(this); alpn_ = "http/1.1"sv; read_ = &ClientHandler::read_clear; write_ = &ClientHandler::write_clear; on_read_ = &ClientHandler::upstream_http1_connhd_read; on_write_ = &ClientHandler::upstream_noop; } } #ifdef ENABLE_HTTP3 void ClientHandler::setup_http3_upstream( std::unique_ptr &&upstream) { upstream_ = std::move(upstream); write_ = &ClientHandler::write_quic; auto config = get_config(); reset_upstream_read_timeout(config->conn.upstream.timeout.http3_idle); } #endif // defined(ENABLE_HTTP3) ClientHandler::~ClientHandler() { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "Deleting"; } if (upstream_) { upstream_->on_handler_delete(); } auto worker_stat = worker_->get_worker_stat(); --worker_stat->num_connections; if (worker_stat->num_connections == 0) { worker_->schedule_clear_mcpool(); } ev_timer_stop(conn_.loop, &reneg_shutdown_timer_); // TODO If backend is http/2, and it is in CONNECTED state, signal // it and make it loopbreak when output is zero. if (worker_->get_graceful_shutdown() && worker_stat->num_connections == 0 && worker_stat->num_close_waits == 0) { ev_break(conn_.loop); } if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "Deleted"; } } Upstream *ClientHandler::get_upstream() { return upstream_.get(); } struct ev_loop *ClientHandler::get_loop() const { return conn_.loop; } void ClientHandler::reset_upstream_read_timeout(ev_tstamp t) { conn_.rt.repeat = t; ev_timer_again(conn_.loop, &conn_.rt); } void ClientHandler::reset_upstream_write_timeout(ev_tstamp t) { conn_.wt.repeat = t; ev_timer_again(conn_.loop, &conn_.wt); } void ClientHandler::repeat_read_timer() { ev_timer_again(conn_.loop, &conn_.rt); } void ClientHandler::stop_read_timer() { ev_timer_stop(conn_.loop, &conn_.rt); } int ClientHandler::validate_next_proto() { const unsigned char *next_proto = nullptr; unsigned int next_proto_len = 0; // First set callback for catch all cases on_read_ = &ClientHandler::upstream_read; SSL_get0_alpn_selected(conn_.tls.ssl, &next_proto, &next_proto_len); std::string_view proto; if (next_proto) { proto = as_string_view(next_proto, next_proto_len); if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "The negotiated next protocol: " << proto; } } else { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "No protocol negotiated. Fallback to HTTP/1.1"; } proto = "http/1.1"sv; } if (!tls::in_proto_list(get_config()->tls.alpn_list, proto)) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "The negotiated protocol is not supported: " << proto; } return -1; } if (util::check_h2_is_selected(proto)) { on_read_ = &ClientHandler::upstream_http2_connhd_read; auto http2_upstream = std::make_unique(this); upstream_ = std::move(http2_upstream); alpn_ = make_string_ref(balloc_, proto); // At this point, input buffer is already filled with some bytes. // The read callback is not called until new data come. So consume // input buffer here. if (on_read() != 0) { return -1; } return 0; } if (proto == "http/1.1"sv) { upstream_ = std::make_unique(this); alpn_ = "http/1.1"sv; // At this point, input buffer is already filled with some bytes. // The read callback is not called until new data come. So consume // input buffer here. if (on_read() != 0) { return -1; } return 0; } if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "The negotiated protocol is not supported"; } return -1; } int ClientHandler::do_read() { return read_(*this); } int ClientHandler::do_write() { return write_(*this); } int ClientHandler::on_read() { if (rb_.chunk_avail()) { auto rv = on_read_(*this); if (rv != 0) { return rv; } } conn_.handle_tls_pending_read(); return 0; } int ClientHandler::on_write() { return on_write_(*this); } const std::string_view &ClientHandler::get_ipaddr() const { return ipaddr_; } bool ClientHandler::get_should_close_after_write() const { return should_close_after_write_; } void ClientHandler::set_should_close_after_write(bool f) { should_close_after_write_ = f; } void ClientHandler::pool_downstream_connection( std::unique_ptr dconn) { if (!dconn->poolable()) { return; } dconn->set_client_handler(nullptr); auto &group = dconn->get_downstream_addr_group(); if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "Pooling downstream connection DCONN:" << dconn.get() << " in group " << group; } auto addr = dconn->get_addr(); auto &dconn_pool = addr->dconn_pool; dconn_pool->add_downstream_connection(std::move(dconn)); } namespace { // Computes 32bits hash for session affinity for IP address |ip|. uint32_t compute_affinity_from_ip(const std::string_view &ip) { int rv; std::array buf; rv = util::sha256(buf.data(), ip); if (rv != 0) { // Not sure when sha256 failed. Just fall back to another // function. return util::hash32(ip); } return (static_cast(buf[0]) << 24) | (static_cast(buf[1]) << 16) | (static_cast(buf[2]) << 8) | static_cast(buf[3]); } } // namespace Http2Session *ClientHandler::get_http2_session( const std::shared_ptr &group, DownstreamAddr *addr) { auto &shared_addr = group->shared_addr; if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "Selected DownstreamAddr=" << addr << ", index=" << (addr - shared_addr->addrs.data()); } for (auto session = addr->http2_extra_freelist.head; session;) { auto next = session->dlnext; if (session->max_concurrency_reached(0)) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "Maximum streams have been reached for Http2Session(" << session << "). Skip it"; } session->remove_from_freelist(); session = next; continue; } if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "Use Http2Session " << session << " from http2_extra_freelist"; } if (session->max_concurrency_reached(1)) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "Maximum streams are reached for Http2Session(" << session << ")."; } session->remove_from_freelist(); } return session; } auto session = new Http2Session(conn_.loop, worker_->get_cl_ssl_ctx(), worker_, group, addr); if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "Create new Http2Session " << session; } session->add_to_extra_freelist(); return session; } uint32_t ClientHandler::get_affinity_cookie(Downstream *downstream, const std::string_view &cookie_name) { auto h = downstream->find_affinity_cookie(cookie_name); if (h) { return h; } auto d = std::uniform_int_distribution(1); auto rh = d(worker_->get_randgen()); h = util::hash32(std::string_view{reinterpret_cast(&rh), sizeof(rh)}); downstream->renew_affinity_cookie(h); return h; } namespace { void reschedule_addr( std::priority_queue, DownstreamAddrEntryGreater> &pq, DownstreamAddr *addr) { auto penalty = MAX_DOWNSTREAM_ADDR_WEIGHT + addr->pending_penalty; addr->cycle += penalty / addr->weight; addr->pending_penalty = penalty % addr->weight; pq.push(DownstreamAddrEntry{addr, addr->seq, addr->cycle}); addr->queued = true; } } // namespace namespace { void reschedule_wg( std::priority_queue, WeightGroupEntryGreater> &pq, WeightGroup *wg) { auto penalty = MAX_DOWNSTREAM_ADDR_WEIGHT + wg->pending_penalty; wg->cycle += penalty / wg->weight; wg->pending_penalty = penalty % wg->weight; pq.push(WeightGroupEntry{wg, wg->seq, wg->cycle}); wg->queued = true; } } // namespace DownstreamAddr *ClientHandler::get_downstream_addr(int &err, DownstreamAddrGroup *group, Downstream *downstream) { err = 0; switch (faddr_->alt_mode) { case UpstreamAltMode::API: case UpstreamAltMode::HEALTHMON: assert(0); default: break; } auto &shared_addr = group->shared_addr; if (shared_addr->affinity.type != SessionAffinity::NONE) { uint32_t hash; switch (shared_addr->affinity.type) { case SessionAffinity::IP: if (!affinity_hash_computed_) { affinity_hash_ = compute_affinity_from_ip(ipaddr_); affinity_hash_computed_ = true; } hash = affinity_hash_; break; case SessionAffinity::COOKIE: if (shared_addr->affinity.cookie.stickiness == SessionAffinityCookieStickiness::STRICT) { return get_downstream_addr_strict_affinity(err, shared_addr, downstream); } hash = get_affinity_cookie(downstream, shared_addr->affinity.cookie.name); break; default: assert(0); } const auto &affinity_hash = shared_addr->affinity_hash; auto it = std::ranges::lower_bound(affinity_hash, hash, {}, &AffinityHash::hash); if (it == std::ranges::end(affinity_hash)) { it = std::ranges::begin(affinity_hash); } auto aff_idx = static_cast( std::ranges::distance(std::ranges::begin(affinity_hash), it)); auto idx = (*it).idx; auto addr = &shared_addr->addrs[idx]; if (addr->connect_blocker->blocked()) { size_t i; for (i = aff_idx + 1; i != aff_idx; ++i) { if (i == shared_addr->affinity_hash.size()) { i = 0; } addr = &shared_addr->addrs[shared_addr->affinity_hash[i].idx]; if (addr->connect_blocker->blocked()) { continue; } break; } if (i == aff_idx) { err = -1; return nullptr; } } return addr; } auto &wgpq = shared_addr->pq; for (;;) { if (wgpq.empty()) { CLOG(INFO, this) << "No working downstream address found"; err = -1; return nullptr; } auto wg = wgpq.top().wg; wgpq.pop(); wg->queued = false; for (;;) { if (wg->pq.empty()) { break; } auto addr = wg->pq.top().addr; wg->pq.pop(); addr->queued = false; if (addr->connect_blocker->blocked()) { continue; } reschedule_addr(wg->pq, addr); reschedule_wg(wgpq, wg); return addr; } } } DownstreamAddr *ClientHandler::get_downstream_addr_strict_affinity( int &err, const std::shared_ptr &shared_addr, Downstream *downstream) { const auto &affinity_hash = shared_addr->affinity_hash; auto h = downstream->find_affinity_cookie(shared_addr->affinity.cookie.name); if (h) { auto it = shared_addr->affinity_hash_map.find(h); if (it != std::ranges::end(shared_addr->affinity_hash_map)) { auto addr = &shared_addr->addrs[(*it).second]; if (!addr->connect_blocker->blocked()) { return addr; } } } else { auto d = std::uniform_int_distribution(1); auto rh = d(worker_->get_randgen()); h = util::hash32(std::string_view{reinterpret_cast(&rh), sizeof(rh)}); } // Client is not bound to a particular backend, or the bound backend // is not found, or is blocked. Find new backend using h. Using // existing h allows us to find new server in a deterministic way. // It is preferable because multiple concurrent requests with the // stale cookie might be in-flight. auto it = std::ranges::lower_bound(affinity_hash, h, {}, &AffinityHash::hash); if (it == std::ranges::end(affinity_hash)) { it = std::ranges::begin(affinity_hash); } auto aff_idx = static_cast( std::ranges::distance(std::ranges::begin(affinity_hash), it)); auto idx = (*it).idx; auto addr = &shared_addr->addrs[idx]; if (addr->connect_blocker->blocked()) { size_t i; for (i = aff_idx + 1; i != aff_idx; ++i) { if (i == shared_addr->affinity_hash.size()) { i = 0; } addr = &shared_addr->addrs[shared_addr->affinity_hash[i].idx]; if (addr->connect_blocker->blocked()) { continue; } break; } if (i == aff_idx) { err = -1; return nullptr; } } downstream->renew_affinity_cookie(addr->affinity_hash); return addr; } std::unique_ptr ClientHandler::get_downstream_connection(int &err, Downstream *downstream) { size_t group_idx; auto &downstreamconf = *worker_->get_downstream_config(); auto &routerconf = downstreamconf.router; auto catch_all = downstreamconf.addr_group_catch_all; auto &groups = worker_->get_downstream_addr_groups(); auto &req = downstream->request(); err = 0; switch (faddr_->alt_mode) { case UpstreamAltMode::API: { auto dconn = std::make_unique(worker_); dconn->set_client_handler(this); return dconn; } case UpstreamAltMode::HEALTHMON: { auto dconn = std::make_unique(); dconn->set_client_handler(this); return dconn; } default: break; } auto &balloc = downstream->get_block_allocator(); std::string_view authority, path; if (req.forwarded_once) { if (groups.size() != 1) { authority = req.orig_authority; path = req.orig_path; } } else { if (faddr_->sni_fwd) { authority = sni_; } else if (!req.authority.empty()) { authority = req.authority; } else { auto h = req.fs.header(http2::HD_HOST); if (h) { authority = h->value; } } // CONNECT method does not have path. But we requires path in // host-path mapping. As workaround, we assume that path is // "/". if (!req.regular_connect_method()) { path = req.path; } // Cache the authority and path used for the first-time backend // selection because per-pattern mruby script can change them. req.orig_authority = authority; req.orig_path = path; req.forwarded_once = true; } // Fast path. If we have one group, it must be catch-all group. if (groups.size() == 1) { group_idx = 0; } else { group_idx = match_downstream_addr_group(routerconf, authority, path, groups, catch_all, balloc); } if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "Downstream address group_idx: " << group_idx; } if (groups[group_idx]->shared_addr->redirect_if_not_tls && !conn_.tls.ssl) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "Downstream address group " << group_idx << " requires frontend TLS connection."; } err = SHRPX_ERR_TLS_REQUIRED; return nullptr; } auto &group = groups[group_idx]; if (group->shared_addr->dnf) { auto dconn = std::make_unique(group); dconn->set_client_handler(this); return dconn; } auto addr = get_downstream_addr(err, group.get(), downstream); if (addr == nullptr) { return nullptr; } if (addr->proto == Proto::HTTP1) { auto dconn = addr->dconn_pool->pop_downstream_connection(); if (dconn) { dconn->set_client_handler(this); return dconn; } if (worker_->get_connect_blocker()->blocked()) { if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "Worker wide backend connection was blocked temporarily"; } return nullptr; } if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "Downstream connection pool is empty." << " Create new one"; } dconn = std::make_unique(group, addr, conn_.loop, worker_); dconn->set_client_handler(this); return dconn; } if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "Downstream connection pool is empty." << " Create new one"; } auto http2session = get_http2_session(group, addr); auto dconn = std::make_unique(http2session); dconn->set_client_handler(this); return dconn; } MemchunkPool *ClientHandler::get_mcpool() { return worker_->get_mcpool(); } SSL *ClientHandler::get_ssl() const { return conn_.tls.ssl; } void ClientHandler::direct_http2_upgrade() { upstream_ = std::make_unique(this); alpn_ = NGHTTP2_CLEARTEXT_PROTO_VERSION_ID ""sv; on_read_ = &ClientHandler::upstream_read; write_ = &ClientHandler::write_clear; } int ClientHandler::perform_http2_upgrade(HttpsUpstream *http) { auto upstream = std::make_unique(this); auto output = upstream->get_response_buf(); // We might have written non-final header in response_buf, in this // case, response_state is still INITIAL. If this non-final header // and upgrade header fit in output buffer, do upgrade. Otherwise, // to avoid to send this non-final header as response body in HTTP/2 // upstream, fail upgrade. auto downstream = http->get_downstream(); auto input = downstream->get_response_buf(); if (upstream->upgrade_upstream(http) != 0) { return -1; } // http pointer is now owned by upstream. upstream_.release(); // TODO We might get other version id in HTTP2-settings, if we // support aliasing for h2, but we just use library default for now. alpn_ = NGHTTP2_CLEARTEXT_PROTO_VERSION_ID ""sv; on_read_ = &ClientHandler::upstream_http2_connhd_read; write_ = &ClientHandler::write_clear; input->remove(*output, input->rleft()); static constexpr auto res = "HTTP/1.1 101 Switching Protocols\r\n" "Connection: Upgrade\r\n" "Upgrade: " NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "\r\n" "\r\n"sv; output->append(res); upstream_ = std::move(upstream); signal_write(); return 0; } bool ClientHandler::get_http2_upgrade_allowed() const { return !conn_.tls.ssl; } std::string_view ClientHandler::get_upstream_scheme() const { if (conn_.tls.ssl) { return "https"sv; } else { return "http"sv; } } void ClientHandler::start_immediate_shutdown() { ev_timer_start(conn_.loop, &reneg_shutdown_timer_); } void ClientHandler::write_accesslog(Downstream *downstream) { auto &req = downstream->request(); auto config = get_config(); if (!req.tstamp) { auto lgconf = log_config(); lgconf->update_tstamp(std::chrono::system_clock::now()); req.tstamp = lgconf->tstamp; } upstream_accesslog( config->logging.access.format, LogSpec{ downstream, ipaddr_, alpn_, sni_, conn_.tls.ssl, std::chrono::high_resolution_clock::now(), // request_end_time port_, faddr_->port, config->pid, }); } ClientHandler::ReadBuf *ClientHandler::get_rb() { return &rb_; } void ClientHandler::signal_write() { conn_.wlimit.startw(); } RateLimit *ClientHandler::get_rlimit() { return &conn_.rlimit; } RateLimit *ClientHandler::get_wlimit() { return &conn_.wlimit; } ev_io *ClientHandler::get_wev() { return &conn_.wev; } Worker *ClientHandler::get_worker() const { return worker_; } namespace { ssize_t parse_proxy_line_port(const uint8_t *first, const uint8_t *last) { auto p = first; int32_t port = 0; if (p == last) { return -1; } if (*p == '0') { if (p + 1 != last && util::is_digit(as_signed(*(p + 1)))) { return -1; } return 1; } for (; p != last && util::is_digit(as_signed(*p)); ++p) { port *= 10; port += *p - '0'; if (port > 65535) { return -1; } } return p - first; } } // namespace int ClientHandler::on_proxy_protocol_finish() { auto len = as_unsigned(rb_.pos() - rb_.begin()); assert(len); if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol: Draining " << len << " bytes from socket"; } rb_.reset(); if (conn_.read_nolim_clear(rb_.pos(), len) < 0) { return -1; } rb_.reset(); setup_upstream_io_callback(); return 0; } namespace { // PROXY-protocol v2 header signature constexpr uint8_t PROXY_PROTO_V2_SIG[] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"; // PROXY-protocol v2 header length constexpr size_t PROXY_PROTO_V2_HDLEN = str_size(PROXY_PROTO_V2_SIG) + /* ver_cmd(1) + fam(1) + len(2) = */ 4; } // namespace // http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt int ClientHandler::proxy_protocol_read() { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol: Started"; } auto first = rb_.pos(); if (rb_.rleft() >= PROXY_PROTO_V2_HDLEN && (*(first + str_size(PROXY_PROTO_V2_SIG)) & 0xf0) == 0x20) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol: Detected v2 header signature"; } return proxy_protocol_v2_read(); } // NULL character really destroys functions which expects NULL // terminated string. We won't expect it in PROXY protocol line, so // find it here. auto chrs = std::to_array({'\n', '\0'}); constexpr size_t MAX_PROXY_LINELEN = 107; auto bufend = rb_.pos() + std::min(MAX_PROXY_LINELEN, rb_.rleft()); auto end = std::ranges::find_first_of( rb_.pos(), bufend, std::ranges::begin(chrs), std::ranges::end(chrs)); if (end == bufend || *end == '\0' || end == rb_.pos() || *(end - 1) != '\r') { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v1: No ending CR LF sequence found"; } return -1; } --end; static constexpr auto HEADER = "PROXY "sv; if (static_cast(end - rb_.pos()) < HEADER.size()) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v1: PROXY version 1 ID not found"; } return -1; } if (HEADER != as_string_view(rb_.pos(), HEADER.size())) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v1: Bad PROXY protocol version 1 ID"; } return -1; } rb_.drain(HEADER.size()); int family; if (rb_.pos()[0] == 'T') { if (end - rb_.pos() < 5) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v1: INET protocol family not found"; } return -1; } if (rb_.pos()[1] != 'C' || rb_.pos()[2] != 'P') { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v1: Unknown INET protocol family"; } return -1; } switch (rb_.pos()[3]) { case '4': family = AF_INET; break; case '6': family = AF_INET6; break; default: if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v1: Unknown INET protocol family"; } return -1; } rb_.drain(5); } else { if (end - rb_.pos() < 7) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v1: INET protocol family not found"; } return -1; } if ("UNKNOWN"sv != as_string_view(rb_.pos(), 7)) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v1: Unknown INET protocol family"; } return -1; } rb_.drain(as_unsigned(end + 2 - rb_.pos())); return on_proxy_protocol_finish(); } // source address auto token_end = std::ranges::find(rb_.pos(), end, ' '); if (token_end == end) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v1: Source address not found"; } return -1; } *token_end = '\0'; if (!util::numeric_host(reinterpret_cast(rb_.pos()), family)) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v1: Invalid source address"; } return -1; } auto src_addr = rb_.pos(); auto src_addrlen = token_end - rb_.pos(); rb_.drain(as_unsigned(token_end - rb_.pos() + 1)); // destination address token_end = std::ranges::find(rb_.pos(), end, ' '); if (token_end == end) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v1: Destination address not found"; } return -1; } *token_end = '\0'; if (!util::numeric_host(reinterpret_cast(rb_.pos()), family)) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v1: Invalid destination address"; } return -1; } // Currently we don't use destination address rb_.drain(as_unsigned(token_end - rb_.pos() + 1)); // source port auto n = parse_proxy_line_port(rb_.pos(), end); if (n <= 0 || *(rb_.pos() + n) != ' ') { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v1: Invalid source port"; } return -1; } rb_.pos()[n] = '\0'; auto src_port = rb_.pos(); auto src_portlen = n; rb_.drain(as_unsigned(n + 1)); // destination port n = parse_proxy_line_port(rb_.pos(), end); if (n <= 0 || rb_.pos() + n != end) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v1: Invalid destination port"; } return -1; } // Currently we don't use destination port rb_.drain(as_unsigned(end + 2 - rb_.pos())); ipaddr_ = make_string_ref( balloc_, as_string_view(src_addr, static_cast(src_addrlen))); port_ = make_string_ref( balloc_, as_string_view(src_port, static_cast(src_portlen))); if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v1: Finished, " << (rb_.pos() - first) << " bytes read"; } auto config = get_config(); auto &fwdconf = config->http.forwarded; if ((fwdconf.params & FORWARDED_FOR) && fwdconf.for_node_type == ForwardedNode::IP) { init_forwarded_for(family, ipaddr_); } return on_proxy_protocol_finish(); } int ClientHandler::proxy_protocol_v2_read() { // Assume that first str_size(PROXY_PROTO_V2_SIG) octets match v2 // protocol signature and followed by the bytes which indicates v2. assert(rb_.rleft() >= PROXY_PROTO_V2_HDLEN); auto p = rb_.pos() + str_size(PROXY_PROTO_V2_SIG); assert(((*p) & 0xf0) == 0x20); enum { LOCAL, PROXY } cmd; auto cmd_bits = (*p++) & 0xf; switch (cmd_bits) { case 0x0: cmd = LOCAL; break; case 0x01: cmd = PROXY; break; default: if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v2: Unknown command " << log::hex << cmd_bits; } return -1; } auto fam = *p++; uint16_t len; memcpy(&len, p, sizeof(len)); len = ntohs(len); p += sizeof(len); if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v2: Detected family=" << log::hex << fam << ", len=" << log::dec << len; } if (rb_.last() - p < len) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v2: Prematurely truncated header block; require " << len << " bytes, " << rb_.last() - p << " bytes left"; } return -1; } int family; std::array src_addr, dst_addr; size_t addrlen; switch (fam) { case 0x11: case 0x12: if (len < 12) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v2: Too short AF_INET addresses"; } return -1; } family = AF_INET; addrlen = 4; break; case 0x21: case 0x22: if (len < 36) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v2: Too short AF_INET6 addresses"; } return -1; } family = AF_INET6; addrlen = 16; break; case 0x31: case 0x32: if (len < 216) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v2: Too short AF_UNIX addresses"; } return -1; } // fall through case 0x00: { // UNSPEC and UNIX are just ignored. if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v2: Ignore combination of address " "family and protocol " << log::hex << fam; } rb_.drain(PROXY_PROTO_V2_HDLEN + len); return on_proxy_protocol_finish(); } default: if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v2: Unknown combination of address " "family and protocol " << log::hex << fam; } return -1; } if (cmd != PROXY) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v2: Ignore non-PROXY command"; } rb_.drain(PROXY_PROTO_V2_HDLEN + len); return on_proxy_protocol_finish(); } if (inet_ntop(family, p, src_addr.data(), src_addr.size()) == nullptr) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v2: Unable to parse source address"; } return -1; } p += addrlen; if (inet_ntop(family, p, dst_addr.data(), dst_addr.size()) == nullptr) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v2: Unable to parse destination address"; } return -1; } p += addrlen; uint16_t src_port; memcpy(&src_port, p, sizeof(src_port)); src_port = ntohs(src_port); // We don't use destination port. p += 4; ipaddr_ = make_string_ref(balloc_, std::string_view{src_addr.data()}); port_ = util::make_string_ref_uint(balloc_, src_port); if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "PROXY-protocol-v2: Finished reading proxy addresses, " << p - rb_.pos() << " bytes read, " << PROXY_PROTO_V2_HDLEN + len - as_unsigned(p - rb_.pos()) << " bytes left"; } auto config = get_config(); auto &fwdconf = config->http.forwarded; if ((fwdconf.params & FORWARDED_FOR) && fwdconf.for_node_type == ForwardedNode::IP) { init_forwarded_for(family, ipaddr_); } rb_.drain(PROXY_PROTO_V2_HDLEN + len); return on_proxy_protocol_finish(); } std::string_view ClientHandler::get_forwarded_by() const { auto &fwdconf = get_config()->http.forwarded; if (fwdconf.by_node_type == ForwardedNode::OBFUSCATED) { return fwdconf.by_obfuscated; } return local_hostport_; } std::string_view ClientHandler::get_forwarded_for() const { return forwarded_for_; } const UpstreamAddr *ClientHandler::get_upstream_addr() const { return faddr_; } Connection *ClientHandler::get_connection() { return &conn_; } void ClientHandler::set_tls_sni(const std::string_view &sni) { sni_ = make_string_ref(balloc_, sni); } std::string_view ClientHandler::get_tls_sni() const { return sni_; } std::string_view ClientHandler::get_alpn() const { return alpn_; } BlockAllocator &ClientHandler::get_block_allocator() { return balloc_; } void ClientHandler::set_alpn_from_conn() { const unsigned char *alpn; unsigned int alpnlen; SSL_get0_alpn_selected(conn_.tls.ssl, &alpn, &alpnlen); alpn_ = make_string_ref(balloc_, as_string_view(alpn, alpnlen)); } void ClientHandler::set_local_hostport(const sockaddr *addr, socklen_t addrlen) { std::array host; if (getnameinfo(addr, addrlen, host.data(), host.size(), nullptr, 0, NI_NUMERICHOST) != 0) { return; } local_hostport_ = util::make_hostport(balloc_, host.data(), faddr_->port); } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/h2load_http2_session.cc0000644000000000000000000000013215077107270017413 xustar0030 mtime=1761382072.985444176 30 atime=1761382106.216310374 30 ctime=1761382109.212300057 nghttp2-1.68.0/src/h2load_http2_session.cc0000644000175100017510000002230115077107270020001 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "h2load_http2_session.h" #include #include #include #include "h2load.h" #include "util.h" #include "template.h" using namespace nghttp2; namespace h2load { Http2Session::Http2Session(Client *client) : client_(client), session_(nullptr) {} Http2Session::~Http2Session() { nghttp2_session_del(session_); } namespace { int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *user_data) { auto client = static_cast(user_data); if (frame->hd.type != NGHTTP2_HEADERS) { return 0; } client->on_header(frame->hd.stream_id, name, namelen, value, valuelen); client->worker->stats.bytes_head_decomp += namelen + valuelen; if (client->worker->config->verbose) { std::cout << "[stream_id=" << frame->hd.stream_id << "] "; std::cout.write(reinterpret_cast(name), static_cast(namelen)); std::cout << ": "; std::cout.write(reinterpret_cast(value), static_cast(valuelen)); std::cout << "\n"; } return 0; } } // namespace namespace { int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { auto client = static_cast(user_data); switch (frame->hd.type) { case NGHTTP2_HEADERS: client->worker->stats.bytes_head += frame->hd.length - frame->headers.padlen - ((frame->hd.flags & NGHTTP2_FLAG_PRIORITY) ? 5 : 0); // fall through case NGHTTP2_DATA: if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { client->record_ttfb(); } break; } return 0; } } // namespace namespace { int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data) { auto client = static_cast(user_data); client->record_ttfb(); client->worker->stats.bytes_body += len; return 0; } } // namespace namespace { int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *user_data) { auto client = static_cast(user_data); client->on_stream_close(stream_id, error_code == NGHTTP2_NO_ERROR); return 0; } } // namespace namespace { int before_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { if (frame->hd.type != NGHTTP2_HEADERS || frame->headers.cat != NGHTTP2_HCAT_REQUEST) { return 0; } auto client = static_cast(user_data); auto req_stat = client->get_req_stat(frame->hd.stream_id); assert(req_stat); client->record_request_time(req_stat); return 0; } } // namespace namespace { nghttp2_ssize file_read_callback(nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *user_data) { auto client = static_cast(user_data); auto config = client->worker->config; auto req_stat = client->get_req_stat(stream_id); assert(req_stat); ssize_t nread; while ((nread = pread(config->data_fd, buf, length, req_stat->data_offset)) == -1 && errno == EINTR) ; if (nread == -1) { return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } req_stat->data_offset += nread; if (req_stat->data_offset == config->data_length) { *data_flags |= NGHTTP2_DATA_FLAG_EOF; return nread; } if (req_stat->data_offset > config->data_length || nread == 0) { return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } return nread; } } // namespace namespace { nghttp2_ssize send_callback(nghttp2_session *session, const uint8_t *data, size_t length, int flags, void *user_data) { auto client = static_cast(user_data); auto &wb = client->wb; if (wb.rleft() >= BACKOFF_WRITE_BUFFER_THRES) { return NGHTTP2_ERR_WOULDBLOCK; } wb.append(data, length); return as_signed(length); } } // namespace void Http2Session::on_connect() { int rv; // This is required with --disable-assert. (void)rv; nghttp2_session_callbacks *callbacks; nghttp2_session_callbacks_new(&callbacks); auto callbacks_deleter = defer(nghttp2_session_callbacks_del, callbacks); nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, on_frame_recv_callback); nghttp2_session_callbacks_set_on_data_chunk_recv_callback( callbacks, on_data_chunk_recv_callback); nghttp2_session_callbacks_set_on_stream_close_callback( callbacks, on_stream_close_callback); nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header_callback); nghttp2_session_callbacks_set_before_frame_send_callback( callbacks, before_frame_send_callback); nghttp2_session_callbacks_set_send_callback2(callbacks, send_callback); nghttp2_session_callbacks_set_rand_callback(callbacks, util::secure_random); nghttp2_option *opt; rv = nghttp2_option_new(&opt); assert(rv == 0); auto config = client_->worker->config; if (config->encoder_header_table_size != NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) { nghttp2_option_set_max_deflate_dynamic_table_size( opt, config->encoder_header_table_size); } nghttp2_session_client_new2(&session_, callbacks, client_, opt); nghttp2_option_del(opt); std::array iv; size_t niv = 2; iv[0].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; iv[0].value = 0; iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; iv[1].value = (1 << config->window_bits) - 1; if (config->header_table_size != NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) { iv[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[niv].value = config->header_table_size; ++niv; } if (config->max_frame_size != 16_k) { iv[niv].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; iv[niv].value = static_cast(config->max_frame_size); ++niv; } rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, iv.data(), niv); assert(rv == 0); auto connection_window = (1 << config->connection_window_bits) - 1; nghttp2_session_set_local_window_size(session_, NGHTTP2_FLAG_NONE, 0, connection_window); client_->signal_write(); } int Http2Session::submit_request() { if (nghttp2_session_check_request_allowed(session_) == 0) { return -1; } auto config = client_->worker->config; auto &nva = config->nva[client_->reqidx++]; if (client_->reqidx == config->nva.size()) { client_->reqidx = 0; } nghttp2_data_provider2 prd{{0}, file_read_callback}; auto stream_id = nghttp2_submit_request2(session_, nullptr, nva.data(), nva.size(), config->data_fd == -1 ? nullptr : &prd, nullptr); if (stream_id < 0) { return -1; } client_->on_request(stream_id); return 0; } int Http2Session::on_read(const uint8_t *data, size_t len) { auto rv = nghttp2_session_mem_recv2(session_, data, len); if (rv < 0) { return -1; } assert(static_cast(rv) == len); if (nghttp2_session_want_read(session_) == 0 && nghttp2_session_want_write(session_) == 0 && client_->wb.rleft() == 0) { return -1; } client_->signal_write(); return 0; } int Http2Session::on_write() { auto rv = nghttp2_session_send(session_); if (rv != 0) { return -1; } if (nghttp2_session_want_read(session_) == 0 && nghttp2_session_want_write(session_) == 0 && client_->wb.rleft() == 0) { return -1; } return 0; } void Http2Session::terminate() { nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR); } size_t Http2Session::max_concurrent_streams() { return client_->worker->config->max_concurrent_streams; } } // namespace h2load nghttp2-1.68.0/src/PaxHeaders/shrpx_quic_listener.cc0000644000000000000000000000013215077107270017450 xustar0030 mtime=1761382072.998444116 30 atime=1761382106.195310467 30 ctime=1761382109.190300121 nghttp2-1.68.0/src/shrpx_quic_listener.cc0000644000175100017510000001003215077107270020034 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2021 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_quic_listener.h" #include "shrpx_worker.h" #include "shrpx_config.h" #include "shrpx_log.h" namespace shrpx { namespace { void readcb(struct ev_loop *loop, ev_io *w, int revent) { auto l = static_cast(w->data); l->on_read(); } } // namespace QUICListener::QUICListener(const UpstreamAddr *faddr, Worker *worker) : faddr_{faddr}, worker_{worker} { ev_io_init(&rev_, readcb, faddr_->fd, EV_READ); ev_set_priority(&rev_, EV_MAXPRI); rev_.data = this; ev_io_start(worker_->get_loop(), &rev_); } QUICListener::~QUICListener() { ev_io_stop(worker_->get_loop(), &rev_); close(faddr_->fd); } void QUICListener::on_read() { Address remote_addr; std::array buf; size_t pktcnt = 0; iovec msg_iov{ .iov_base = buf.data(), .iov_len = buf.size(), }; uint8_t msg_ctrl[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(in6_pktinfo)) + CMSG_SPACE(sizeof(int))]; msghdr msg{ .msg_name = &remote_addr.su, .msg_iov = &msg_iov, .msg_iovlen = 1, .msg_control = msg_ctrl, }; auto quic_conn_handler = worker_->get_quic_connection_handler(); for (; pktcnt < 64;) { msg.msg_namelen = sizeof(remote_addr.su); msg.msg_controllen = sizeof(msg_ctrl); auto nread = recvmsg(faddr_->fd, &msg, 0); if (nread == -1) { return; } // Packets less than 21 bytes never be a valid QUIC packet. if (nread < 21) { ++pktcnt; continue; } if (util::quic_prohibited_port(util::get_port(&remote_addr.su))) { ++pktcnt; continue; } Address local_addr{}; if (util::msghdr_get_local_addr(local_addr, &msg, remote_addr.su.storage.ss_family) != 0) { ++pktcnt; continue; } util::set_port(local_addr, faddr_->port); ngtcp2_pkt_info pi{ .ecn = util::msghdr_get_ecn(&msg, remote_addr.su.storage.ss_family), }; auto gso_size = util::msghdr_get_udp_gro(&msg); if (gso_size == 0) { gso_size = static_cast(nread); } auto data = std::span{buf}.first(as_unsigned(nread)); for (;;) { auto datalen = std::min(data.size(), gso_size); ++pktcnt; remote_addr.len = msg.msg_namelen; if (LOG_ENABLED(INFO)) { LOG(INFO) << "QUIC received packet: local=" << util::to_numeric_addr(&local_addr) << " remote=" << util::to_numeric_addr(&remote_addr) << " ecn=" << log::hex << pi.ecn << log::dec << " " << datalen << " bytes"; } // Packets less than 21 bytes never be a valid QUIC packet. if (datalen < 21) { break; } quic_conn_handler->handle_packet(faddr_, remote_addr, local_addr, pi, data.first(datalen)); data = data.subspan(datalen); if (data.empty()) { break; } } } } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_downstream_connection.h0000644000000000000000000000013215077107270021046 xustar0030 mtime=1761382072.992444143 30 atime=1761382106.099310889 30 ctime=1761382109.095300395 nghttp2-1.68.0/src/shrpx_downstream_connection.h0000644000175100017510000000511415077107270021437 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_DOWNSTREAM_CONNECTION_H #define SHRPX_DOWNSTREAM_CONNECTION_H #include "shrpx.h" #include #include "shrpx_io_control.h" namespace shrpx { class ClientHandler; class Upstream; class Downstream; struct DownstreamAddrGroup; struct DownstreamAddr; class DownstreamConnection { public: DownstreamConnection(); virtual ~DownstreamConnection(); virtual int attach_downstream(Downstream *downstream) = 0; virtual void detach_downstream(Downstream *downstream) = 0; virtual int push_request_headers() = 0; virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen) = 0; virtual int end_upload_data() = 0; virtual void pause_read(IOCtrlReason reason) = 0; virtual int resume_read(IOCtrlReason reason, size_t consumed) = 0; virtual void force_resume_read() = 0; virtual int on_read() = 0; virtual int on_write() = 0; virtual int on_timeout() { return 0; } virtual void on_upstream_change(Upstream *upstream) = 0; // true if this object is poolable. virtual bool poolable() const = 0; virtual const std::shared_ptr & get_downstream_addr_group() const = 0; virtual DownstreamAddr *get_addr() const = 0; void set_client_handler(ClientHandler *client_handler); ClientHandler *get_client_handler(); Downstream *get_downstream(); protected: ClientHandler *client_handler_; Downstream *downstream_; }; } // namespace shrpx #endif // !defined(SHRPX_DOWNSTREAM_CONNECTION_H) nghttp2-1.68.0/src/PaxHeaders/inflatehd.cc0000644000000000000000000000013115077107270015313 xustar0030 mtime=1761382072.988444162 29 atime=1761382106.22631033 30 ctime=1761382109.222300028 nghttp2-1.68.0/src/inflatehd.cc0000644000175100017510000001740315077107270015711 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifdef HAVE_CONFIG_H # include #endif // defined(HAVE_CONFIG_H) #ifdef HAVE_UNISTD_H # include #endif // defined(HAVE_UNISTD_H) #include #include #include #include #include #include #include #include #include #define NGHTTP2_NO_SSIZE_T #include #include "template.h" #include "comp_helper.h" namespace nghttp2 { typedef struct { int dump_header_table; } inflate_config; static inflate_config config; static uint8_t to_ud(char c) { if (c >= 'A' && c <= 'Z') { return static_cast(c - 'A' + 10); } else if (c >= 'a' && c <= 'z') { return static_cast(c - 'a' + 10); } else { return static_cast(c - '0'); } } static void decode_hex(uint8_t *dest, const char *src, size_t len) { size_t i; for (i = 0; i < len; i += 2) { *dest++ = static_cast(to_ud(src[i]) << 4 | to_ud(src[i + 1])); } } static void to_json(nghttp2_hd_inflater *inflater, json_t *headers, json_t *wire, int seq, size_t old_settings_table_size) { auto obj = json_object(); json_object_set_new(obj, "seq", json_integer(seq)); json_object_set(obj, "wire", wire); json_object_set(obj, "headers", headers); auto max_dyn_table_size = nghttp2_hd_inflate_get_max_dynamic_table_size(inflater); if (old_settings_table_size != max_dyn_table_size) { json_object_set_new( obj, "header_table_size", json_integer(static_cast(max_dyn_table_size))); } if (config.dump_header_table) { json_object_set_new(obj, "header_table", dump_inflate_header_table(inflater)); } json_dumpf(obj, stdout, JSON_INDENT(2) | JSON_PRESERVE_ORDER); json_decref(obj); printf("\n"); } static int inflate_hd(json_t *obj, nghttp2_hd_inflater *inflater, int seq) { nghttp2_nv nv; int inflate_flags; size_t old_settings_table_size = nghttp2_hd_inflate_get_max_dynamic_table_size(inflater); auto wire = json_object_get(obj, "wire"); if (wire == nullptr) { fprintf(stderr, "'wire' key is missing at %d\n", seq); return -1; } if (!json_is_string(wire)) { fprintf(stderr, "'wire' value is not string at %d\n", seq); return -1; } auto table_size = json_object_get(obj, "header_table_size"); if (table_size) { if (!json_is_integer(table_size)) { fprintf(stderr, "The value of 'header_table_size key' is not integer at %d\n", seq); return -1; } auto rv = nghttp2_hd_inflate_change_table_size( inflater, static_cast(json_integer_value(table_size))); if (rv != 0) { fprintf(stderr, "nghttp2_hd_change_table_size() failed with error %s at %d\n", nghttp2_strerror(rv), seq); return -1; } } auto inputlen = strlen(json_string_value(wire)); if (inputlen & 1) { fprintf(stderr, "Badly formatted output value at %d\n", seq); exit(EXIT_FAILURE); } auto buflen = inputlen / 2; auto buf = std::vector(buflen); decode_hex(buf.data(), json_string_value(wire), inputlen); auto headers = json_array(); auto p = buf.data(); for (;;) { inflate_flags = 0; auto rv = nghttp2_hd_inflate_hd3(inflater, &nv, &inflate_flags, p, buflen, 1); if (rv < 0) { fprintf(stderr, "inflate failed with error code %zd at %d\n", rv, seq); exit(EXIT_FAILURE); } p += rv; buflen -= as_unsigned(rv); if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { json_array_append_new( headers, dump_header(nv.name, nv.namelen, nv.value, nv.valuelen)); } if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { break; } } assert(buflen == 0); nghttp2_hd_inflate_end_headers(inflater); to_json(inflater, headers, wire, seq, old_settings_table_size); json_decref(headers); return 0; } static int perform(void) { nghttp2_hd_inflater *inflater = nullptr; json_error_t error; auto json = json_loadf(stdin, 0, &error); if (json == nullptr) { fprintf(stderr, "JSON loading failed\n"); exit(EXIT_FAILURE); } auto cases = json_object_get(json, "cases"); if (cases == nullptr) { fprintf(stderr, "Missing 'cases' key in root object\n"); exit(EXIT_FAILURE); } if (!json_is_array(cases)) { fprintf(stderr, "'cases' must be JSON array\n"); exit(EXIT_FAILURE); } nghttp2_hd_inflate_new(&inflater); output_json_header(); auto len = json_array_size(cases); for (size_t i = 0; i < len; ++i) { auto obj = json_array_get(cases, i); if (!json_is_object(obj)) { fprintf(stderr, "Unexpected JSON type at %zu. It should be object.\n", i); continue; } if (inflate_hd(obj, inflater, static_cast(i)) != 0) { continue; } if (i + 1 < len) { printf(",\n"); } } output_json_footer(); nghttp2_hd_inflate_del(inflater); json_decref(json); return 0; } static void print_help(void) { std::cout << R"(HPACK HTTP/2 header decoder Usage: inflatehd [OPTIONS] < INPUT Reads JSON data from stdin and outputs inflated name/value pairs in JSON. The root JSON object must contain "context" key, which indicates which compression context is used. If it is "request", request compression context is used. Otherwise, response compression context is used. The value of "cases" key contains the sequence of compressed header block. They share the same compression context and are processed in the order they appear. Each item in the sequence is a JSON object and it must have at least "wire" key. Its value is a string containing compressed header block in hex string. Example: { "context": "request", "cases": [ { "wire": "0284f77778ff" }, { "wire": "0185fafd3c3c7f81" } ] } The output of this program can be used as input for deflatehd. OPTIONS: -d, --dump-header-table Output dynamic header table.)" << std::endl; ; } constexpr static struct option long_options[] = { {"dump-header-table", no_argument, nullptr, 'd'}, {nullptr, 0, nullptr, 0}}; int main(int argc, char **argv) { config.dump_header_table = 0; while (1) { int option_index = 0; int c = getopt_long(argc, argv, "dh", long_options, &option_index); if (c == -1) { break; } switch (c) { case 'h': print_help(); exit(EXIT_SUCCESS); case 'd': // --dump-header-table config.dump_header_table = 1; break; case '?': exit(EXIT_FAILURE); default: break; } } perform(); return 0; } } // namespace nghttp2 int main(int argc, char **argv) { return nghttp2::run_app(nghttp2::main, argc, argv); } nghttp2-1.68.0/src/PaxHeaders/shrpx_mruby_module_env.cc0000644000000000000000000000013015077107270020153 xustar0029 mtime=1761382072.99744412 30 atime=1761382106.184310515 29 ctime=1761382109.18030015 nghttp2-1.68.0/src/shrpx_mruby_module_env.cc0000644000175100017510000003666015077107270020560 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_mruby_module_env.h" #include #include #include #include "shrpx_downstream.h" #include "shrpx_upstream.h" #include "shrpx_client_handler.h" #include "shrpx_mruby.h" #include "shrpx_mruby_module.h" #include "shrpx_log.h" #include "shrpx_tls.h" namespace shrpx { namespace mruby { namespace { mrb_value env_init(mrb_state *mrb, mrb_value self) { return self; } } // namespace namespace { mrb_value env_get_req(mrb_state *mrb, mrb_value self) { return mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "req")); } } // namespace namespace { mrb_value env_get_resp(mrb_state *mrb, mrb_value self) { return mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "resp")); } } // namespace namespace { mrb_value env_get_ctx(mrb_state *mrb, mrb_value self) { auto data = reinterpret_cast(mrb->ud); auto downstream = data->downstream; auto dsym = intern_ptr(mrb, downstream); auto ctx = mrb_iv_get(mrb, self, dsym); if (mrb_nil_p(ctx)) { ctx = mrb_hash_new(mrb); mrb_iv_set(mrb, self, dsym, ctx); } return ctx; } } // namespace namespace { mrb_value env_get_phase(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); return mrb_fixnum_value(data->phase); } } // namespace namespace { mrb_value env_get_remote_addr(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); auto &ipaddr = handler->get_ipaddr(); return mrb_str_new(mrb, ipaddr.data(), static_cast(ipaddr.size())); } } // namespace namespace { mrb_value env_get_server_port(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); auto faddr = handler->get_upstream_addr(); return mrb_fixnum_value(faddr->port); } } // namespace namespace { mrb_value env_get_server_addr(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); auto faddr = handler->get_upstream_addr(); return mrb_str_new(mrb, faddr->host.data(), static_cast(faddr->host.size())); } } // namespace namespace { mrb_value env_get_tls_used(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); return handler->get_ssl() ? mrb_true_value() : mrb_false_value(); } } // namespace namespace { mrb_value env_get_tls_sni(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); auto sni = handler->get_tls_sni(); return mrb_str_new(mrb, sni.data(), static_cast(sni.size())); } } // namespace namespace { mrb_value env_get_tls_client_fingerprint_md(mrb_state *mrb, const EVP_MD *md) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); auto ssl = handler->get_ssl(); if (!ssl) { return mrb_str_new_static(mrb, "", 0); } #if OPENSSL_3_0_0_API auto x = SSL_get0_peer_certificate(ssl); #else // !OPENSSL_3_0_0_API auto x = SSL_get_peer_certificate(ssl); #endif // !OPENSSL_3_0_0_API if (!x) { return mrb_str_new_static(mrb, "", 0); } // Currently the largest hash value is SHA-256, which is 32 bytes. std::array buf; auto slen = tls::get_x509_fingerprint(buf.data(), buf.size(), x, md); #if !OPENSSL_3_0_0_API X509_free(x); #endif // !OPENSSL_3_0_0_API if (slen == -1) { mrb_raise(mrb, E_RUNTIME_ERROR, "could not compute client fingerprint"); } auto &balloc = downstream->get_block_allocator(); auto f = util::format_hex(balloc, std::span{buf.data(), static_cast(slen)}); return mrb_str_new(mrb, f.data(), static_cast(f.size())); } } // namespace namespace { mrb_value env_get_tls_client_fingerprint_sha256(mrb_state *mrb, mrb_value self) { return env_get_tls_client_fingerprint_md(mrb, EVP_sha256()); } } // namespace namespace { mrb_value env_get_tls_client_fingerprint_sha1(mrb_state *mrb, mrb_value self) { return env_get_tls_client_fingerprint_md(mrb, EVP_sha1()); } } // namespace namespace { mrb_value env_get_tls_client_subject_name(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); auto ssl = handler->get_ssl(); if (!ssl) { return mrb_str_new_static(mrb, "", 0); } #if OPENSSL_3_0_0_API auto x = SSL_get0_peer_certificate(ssl); #else // !OPENSSL_3_0_0_API auto x = SSL_get_peer_certificate(ssl); #endif // !OPENSSL_3_0_0_API if (!x) { return mrb_str_new_static(mrb, "", 0); } auto &balloc = downstream->get_block_allocator(); auto name = tls::get_x509_subject_name(balloc, x); #if !OPENSSL_3_0_0_API X509_free(x); #endif // !OPENSSL_3_0_0_API return mrb_str_new(mrb, name.data(), static_cast(name.size())); } } // namespace namespace { mrb_value env_get_tls_client_issuer_name(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); auto ssl = handler->get_ssl(); if (!ssl) { return mrb_str_new_static(mrb, "", 0); } #if OPENSSL_3_0_0_API auto x = SSL_get0_peer_certificate(ssl); #else // !OPENSSL_3_0_0_API auto x = SSL_get_peer_certificate(ssl); #endif // !OPENSSL_3_0_0_API if (!x) { return mrb_str_new_static(mrb, "", 0); } auto &balloc = downstream->get_block_allocator(); auto name = tls::get_x509_issuer_name(balloc, x); #if !OPENSSL_3_0_0_API X509_free(x); #endif // !OPENSSL_3_0_0_API return mrb_str_new(mrb, name.data(), static_cast(name.size())); } } // namespace namespace { mrb_value env_get_tls_client_serial(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); auto ssl = handler->get_ssl(); if (!ssl) { return mrb_str_new_static(mrb, "", 0); } #if OPENSSL_3_0_0_API auto x = SSL_get0_peer_certificate(ssl); #else // !OPENSSL_3_0_0_API auto x = SSL_get_peer_certificate(ssl); #endif // !OPENSSL_3_0_0_API if (!x) { return mrb_str_new_static(mrb, "", 0); } auto &balloc = downstream->get_block_allocator(); auto sn = tls::get_x509_serial(balloc, x); #if !OPENSSL_3_0_0_API X509_free(x); #endif // !OPENSSL_3_0_0_API return mrb_str_new(mrb, sn.data(), static_cast(sn.size())); } } // namespace namespace { mrb_value env_get_tls_client_not_before(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); auto ssl = handler->get_ssl(); if (!ssl) { return mrb_fixnum_value(0); } #if OPENSSL_3_0_0_API auto x = SSL_get0_peer_certificate(ssl); #else // !OPENSSL_3_0_0_API auto x = SSL_get_peer_certificate(ssl); #endif // !OPENSSL_3_0_0_API if (!x) { return mrb_fixnum_value(0); } time_t t; if (tls::get_x509_not_before(t, x) != 0) { t = 0; } #if !OPENSSL_3_0_0_API X509_free(x); #endif // !OPENSSL_3_0_0_API return mrb_fixnum_value(t); } } // namespace namespace { mrb_value env_get_tls_client_not_after(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); auto ssl = handler->get_ssl(); if (!ssl) { return mrb_fixnum_value(0); } #if OPENSSL_3_0_0_API auto x = SSL_get0_peer_certificate(ssl); #else // !OPENSSL_3_0_0_API auto x = SSL_get_peer_certificate(ssl); #endif // !OPENSSL_3_0_0_API if (!x) { return mrb_fixnum_value(0); } time_t t; if (tls::get_x509_not_after(t, x) != 0) { t = 0; } #if !OPENSSL_3_0_0_API X509_free(x); #endif // !OPENSSL_3_0_0_API return mrb_fixnum_value(t); } } // namespace namespace { mrb_value env_get_tls_cipher(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); auto ssl = handler->get_ssl(); if (!ssl) { return mrb_str_new_static(mrb, "", 0); } return mrb_str_new_cstr(mrb, SSL_get_cipher_name(ssl)); } } // namespace namespace { mrb_value env_get_tls_protocol(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); auto ssl = handler->get_ssl(); if (!ssl) { return mrb_str_new_static(mrb, "", 0); } auto proto = nghttp2::tls::get_tls_protocol(ssl); return mrb_str_new(mrb, proto.data(), static_cast(proto.size())); } } // namespace namespace { mrb_value env_get_tls_session_id(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); auto ssl = handler->get_ssl(); if (!ssl) { return mrb_str_new_static(mrb, "", 0); } auto session = SSL_get_session(ssl); if (!session) { return mrb_str_new_static(mrb, "", 0); } unsigned int session_id_length = 0; auto session_id = SSL_SESSION_get_id(session, &session_id_length); auto &balloc = downstream->get_block_allocator(); auto id = util::format_hex(balloc, std::span{session_id, session_id_length}); return mrb_str_new(mrb, id.data(), static_cast(id.size())); } } // namespace namespace { mrb_value env_get_tls_session_reused(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); auto ssl = handler->get_ssl(); if (!ssl) { return mrb_false_value(); } return SSL_session_reused(ssl) ? mrb_true_value() : mrb_false_value(); } } // namespace namespace { mrb_value env_get_alpn(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); auto alpn = handler->get_alpn(); return mrb_str_new(mrb, alpn.data(), static_cast(alpn.size())); } } // namespace namespace { mrb_value env_get_tls_handshake_finished(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); auto conn = handler->get_connection(); return SSL_is_init_finished(conn->tls.ssl) ? mrb_true_value() : mrb_false_value(); } } // namespace void init_env_class(mrb_state *mrb, RClass *module) { auto env_class = mrb_define_class_under(mrb, module, "Env", mrb->object_class); mrb_define_method(mrb, env_class, "initialize", env_init, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "req", env_get_req, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "resp", env_get_resp, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "ctx", env_get_ctx, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "phase", env_get_phase, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "remote_addr", env_get_remote_addr, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "server_addr", env_get_server_addr, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "server_port", env_get_server_port, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "tls_used", env_get_tls_used, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "tls_sni", env_get_tls_sni, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "tls_client_fingerprint_sha256", env_get_tls_client_fingerprint_sha256, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "tls_client_fingerprint_sha1", env_get_tls_client_fingerprint_sha1, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "tls_client_issuer_name", env_get_tls_client_issuer_name, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "tls_client_subject_name", env_get_tls_client_subject_name, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "tls_client_serial", env_get_tls_client_serial, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "tls_client_not_before", env_get_tls_client_not_before, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "tls_client_not_after", env_get_tls_client_not_after, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "tls_cipher", env_get_tls_cipher, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "tls_protocol", env_get_tls_protocol, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "tls_session_id", env_get_tls_session_id, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "tls_session_reused", env_get_tls_session_reused, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "alpn", env_get_alpn, MRB_ARGS_NONE()); mrb_define_method(mrb, env_class, "tls_handshake_finished", env_get_tls_handshake_finished, MRB_ARGS_NONE()); } } // namespace mruby } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_mruby_module_env.h0000644000000000000000000000013115077107270020016 xustar0029 mtime=1761382072.99744412 30 atime=1761382106.186310506 30 ctime=1761382109.181300147 nghttp2-1.68.0/src/shrpx_mruby_module_env.h0000644000175100017510000000265015077107270020412 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_MRUBY_MODULE_ENV_H #define SHRPX_MRUBY_MODULE_ENV_H #include "shrpx.h" #include namespace shrpx { namespace mruby { void init_env_class(mrb_state *mrb, RClass *module); } // namespace mruby } // namespace shrpx #endif // !defined(SHRPX_MRUBY_MODULE_ENV_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_null_downstream_connection.cc0000644000000000000000000000013215077107270022236 xustar0030 mtime=1761382072.998444116 30 atime=1761382106.161310616 30 ctime=1761382109.157300216 nghttp2-1.68.0/src/shrpx_null_downstream_connection.cc0000644000175100017510000000560015077107270022627 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2021 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_null_downstream_connection.h" #include "shrpx_upstream.h" #include "shrpx_downstream.h" #include "shrpx_log.h" namespace shrpx { NullDownstreamConnection::NullDownstreamConnection( const std::shared_ptr &group) : group_(group) {} NullDownstreamConnection::~NullDownstreamConnection() {} int NullDownstreamConnection::attach_downstream(Downstream *downstream) { if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream; } downstream_ = downstream; return 0; } void NullDownstreamConnection::detach_downstream(Downstream *downstream) { if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream; } downstream_ = nullptr; } int NullDownstreamConnection::push_request_headers() { return 0; } int NullDownstreamConnection::push_upload_data_chunk(const uint8_t *data, size_t datalen) { return 0; } int NullDownstreamConnection::end_upload_data() { return 0; } void NullDownstreamConnection::pause_read(IOCtrlReason reason) {} int NullDownstreamConnection::resume_read(IOCtrlReason reason, size_t consumed) { return 0; } void NullDownstreamConnection::force_resume_read() {} int NullDownstreamConnection::on_read() { return 0; } int NullDownstreamConnection::on_write() { return 0; } void NullDownstreamConnection::on_upstream_change(Upstream *upstream) {} bool NullDownstreamConnection::poolable() const { return false; } const std::shared_ptr & NullDownstreamConnection::get_downstream_addr_group() const { return group_; } DownstreamAddr *NullDownstreamConnection::get_addr() const { return nullptr; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/xsi_strerror.h0000644000000000000000000000013115077107271015765 xustar0030 mtime=1761382073.002444097 29 atime=1761382080.10641148 30 ctime=1761382109.174300167 nghttp2-1.68.0/src/xsi_strerror.h0000644000175100017510000000372115077107271016361 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef XSI_STRERROR_H #define XSI_STRERROR_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include #ifdef __cplusplus extern "C" { #endif /* defined(__cplusplus) */ /* Looks like error message is quite small, but we really don't know how much longer they become. */ #define STRERROR_BUFSIZE 256 /* * Returns description of error denoted by |errnum|. The description * is written in |buf| of length |buflen| including terminal NULL. If * there is an error, including the case that buffer space is not * sufficient to include error message, and |buflen| > 0, empty string * is written to |buf|. This function returns |buf|. */ char *xsi_strerror(int errnum, char *buf, size_t buflen); #ifdef __cplusplus } #endif /* defined(__cplusplus) */ #endif /* !defined(XSI_STRERROR_H) */ nghttp2-1.68.0/src/PaxHeaders/shrpx_worker_test.h0000644000000000000000000000013215077107271017015 xustar0030 mtime=1761382073.000444106 30 atime=1761382080.105411485 30 ctime=1761382109.251299945 nghttp2-1.68.0/src/shrpx_worker_test.h0000644000175100017510000000300415077107271017402 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_WORKER_TEST_H #define SHRPX_WORKER_TEST_H #ifdef HAVE_CONFIG_H # include #endif // defined(HAVE_CONFIG_H) #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" namespace shrpx { extern const MunitSuite worker_suite; munit_void_test_decl(test_shrpx_worker_match_downstream_addr_group) } // namespace shrpx #endif // !defined(SHRPX_WORKER_TEST_H) nghttp2-1.68.0/src/PaxHeaders/xsi_strerror.c0000644000000000000000000000013015077107271015757 xustar0030 mtime=1761382073.002444097 29 atime=1761382080.10641148 29 ctime=1761382109.17330017 nghttp2-1.68.0/src/xsi_strerror.c0000644000175100017510000000314015077107271016347 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "xsi_strerror.h" /* Make sure that we get XSI-compliant version of strerror_r */ #ifdef _POSIX_C_SOURCE # undef _POSIX_C_SOURCE #endif /* defined(_POSIX_C_SOURCE) */ #ifdef _GNU_SOURCE # undef _GNU_SOURCE #endif /* defined(_GNU_SOURCE) */ #include char *xsi_strerror(int errnum, char *buf, size_t buflen) { int rv; rv = strerror_r(errnum, buf, buflen); if (rv != 0) { if (buflen > 0) { buf[0] = '\0'; } } return buf; } nghttp2-1.68.0/src/PaxHeaders/nghttp.cc0000644000000000000000000000013215077107270014662 xustar0030 mtime=1761382072.988444162 30 atime=1761382106.232310304 30 ctime=1761382109.229300008 nghttp2-1.68.0/src/nghttp.cc0000644000175100017510000025654615077107270015274 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp.h" #include #ifdef HAVE_UNISTD_H # include #endif // defined(HAVE_UNISTD_H) #ifdef HAVE_FCNTL_H # include #endif // defined(HAVE_FCNTL_H) #ifdef HAVE_NETINET_IN_H # include #endif // defined(HAVE_NETINET_IN_H) #include #include #include #include #include #include #include #include #include #include #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #ifdef HAVE_JANSSON # include #endif // defined(HAVE_JANSSON) #include "app_helper.h" #include "HtmlParser.h" #include "util.h" #include "base64.h" #include "tls.h" #include "template.h" #ifndef O_BINARY # define O_BINARY (0) #endif // !defined(O_BINARY) namespace nghttp2 { Config::Config() : header_table_size(-1), min_header_table_size(std::numeric_limits::max()), encoder_header_table_size(-1), padding(0), max_concurrent_streams(100), peer_max_concurrent_streams(100), multiply(1), timeout(0.), window_bits(-1), connection_window_bits(-1), verbose(0), port_override(0), null_out(false), remote_name(false), get_assets(false), stat(false), upgrade(false), continuation(false), no_content_length(false), hexdump(false), no_push(false), expect_continue(false), verify_peer(true), ktls(false) { nghttp2_option_new(&http2_option); nghttp2_option_set_peer_max_concurrent_streams( http2_option, static_cast(peer_max_concurrent_streams)); nghttp2_option_set_builtin_recv_extension_type(http2_option, NGHTTP2_ALTSVC); nghttp2_option_set_builtin_recv_extension_type(http2_option, NGHTTP2_ORIGIN); } Config::~Config() { nghttp2_option_del(http2_option); } namespace { Config config; } // namespace namespace { void print_protocol_nego_error() { std::cerr << "[ERROR] HTTP/2 protocol was not selected." << " (nghttp2 expects " << NGHTTP2_PROTO_VERSION_ID << ")" << std::endl; } } // namespace namespace { std::string strip_fragment(const char *raw_uri) { const char *end; for (end = raw_uri; *end && *end != '#'; ++end) ; return std::string(raw_uri, end); } } // namespace Request::Request(const std::string &uri, const urlparse_url &u, const nghttp2_data_provider2 *data_prd, int64_t data_length, const nghttp2_extpri &extpri, int level) : uri(uri), u(u), extpri(extpri), data_length(data_length), data_offset(0), response_len(0), inflater(nullptr), data_prd(data_prd), header_buffer_size(0), stream_id(-1), status(0), level(level), expect_final_response(false) { http2::init_hdidx(res_hdidx); http2::init_hdidx(req_hdidx); } Request::~Request() { nghttp2_gzip_inflate_del(inflater); } void Request::init_inflater() { int rv; // This is required with --disable-assert. (void)rv; rv = nghttp2_gzip_inflate_new(&inflater); assert(rv == 0); } std::string_view Request::get_real_scheme() const { return config.scheme_override.empty() ? util::get_uri_field(uri.c_str(), u, URLPARSE_SCHEMA) : std::string_view{config.scheme_override}; } std::string_view Request::get_real_host() const { return config.host_override.empty() ? util::get_uri_field(uri.c_str(), u, URLPARSE_HOST) : std::string_view{config.host_override}; } uint16_t Request::get_real_port() const { auto scheme = get_real_scheme(); return config.host_override.empty() ? util::has_uri_field(u, URLPARSE_PORT) ? u.port : scheme == "https"sv ? 443 : 80 : config.port_override == 0 ? scheme == "https"sv ? 443 : 80 : config.port_override; } void Request::init_html_parser() { // We crawl HTML using overridden scheme, host, and port. auto scheme = get_real_scheme(); auto host = get_real_host(); auto port = get_real_port(); auto ipv6_lit = util::contains(host, ':'); auto base_uri = std::string{scheme}; base_uri += "://"; if (ipv6_lit) { base_uri += '['; } base_uri += host; if (ipv6_lit) { base_uri += ']'; } if (!((scheme == "https"sv && port == 443) || (scheme == "http"sv && port == 80))) { base_uri += ':'; base_uri += util::utos(port); } base_uri += util::get_uri_field(uri.c_str(), u, URLPARSE_PATH); if (util::has_uri_field(u, URLPARSE_QUERY)) { base_uri += '?'; base_uri += util::get_uri_field(uri.c_str(), u, URLPARSE_QUERY); } html_parser = std::make_unique(base_uri); } int Request::update_html_parser(const uint8_t *data, size_t len, int fin) { if (!html_parser) { return 0; } return html_parser->parse_chunk(reinterpret_cast(data), len, fin); } std::string Request::make_reqpath() const { auto path = util::has_uri_field(u, URLPARSE_PATH) ? std::string{util::get_uri_field(uri.c_str(), u, URLPARSE_PATH)} : "/"s; if (util::has_uri_field(u, URLPARSE_QUERY)) { path += '?'; path.append(uri.c_str() + u.field_data[URLPARSE_QUERY].off, u.field_data[URLPARSE_QUERY].len); } return path; } namespace { // Perform special handling |host| if it is IPv6 literal and includes // zone ID per RFC 6874. std::string decode_host(const std::string_view &host) { auto zone_start = std::ranges::find(host, '%'); if (zone_start == std::ranges::end(host) || !util::ipv6_numeric_addr( std::string(std::ranges::begin(host), zone_start).c_str())) { return std::string{host}; } // case: ::1% if (zone_start + 1 == std::ranges::end(host)) { return {host.data(), host.size() - 1}; } // case: ::1%12 or ::1%1 if (zone_start + 3 >= std::ranges::end(host)) { return std::string{host}; } // If we see "%25", followed by more characters, then decode %25 as // '%'. auto zone_id_src = (*(zone_start + 1) == '2' && *(zone_start + 2) == '5') ? zone_start + 3 : zone_start + 1; auto zone_id = util::percent_decode(zone_id_src, std::ranges::end(host)); auto res = std::string(std::ranges::begin(host), zone_start + 1); res += zone_id; return res; } } // namespace namespace { nghttp2_extpri resolve_pri(int res_type) { switch (res_type) { case REQ_CSS: case REQ_JS: return { .urgency = 0, }; case REQ_UNBLOCK_JS: return { .urgency = 1, }; case REQ_IMG: return { .urgency = NGHTTP2_EXTPRI_DEFAULT_URGENCY, .inc = 1, }; default: return { .urgency = NGHTTP2_EXTPRI_DEFAULT_URGENCY, }; } } } // namespace bool Request::is_ipv6_literal_addr() const { if (util::has_uri_field(u, URLPARSE_HOST)) { return memchr(uri.c_str() + u.field_data[URLPARSE_HOST].off, ':', u.field_data[URLPARSE_HOST].len); } else { return false; } } Headers::value_type *Request::get_res_header(int32_t token) { auto idx = res_hdidx[static_cast(token)]; if (idx == -1) { return nullptr; } return &res_nva[static_cast(idx)]; } Headers::value_type *Request::get_req_header(int32_t token) { auto idx = req_hdidx[static_cast(token)]; if (idx == -1) { return nullptr; } return &req_nva[static_cast(idx)]; } void Request::record_request_start_time() { timing.state = RequestState::ON_REQUEST; timing.request_start_time = get_time(); } void Request::record_response_start_time() { timing.state = RequestState::ON_RESPONSE; timing.response_start_time = get_time(); } void Request::record_response_end_time() { timing.state = RequestState::ON_COMPLETE; timing.response_end_time = get_time(); } namespace { void continue_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto client = static_cast(ev_userdata(loop)); auto req = static_cast(w->data); int error; error = nghttp2_submit_data2(client->session, NGHTTP2_FLAG_END_STREAM, req->stream_id, req->data_prd); if (error) { std::cerr << "[ERROR] nghttp2_submit_data2() returned error: " << nghttp2_strerror(error) << std::endl; nghttp2_submit_rst_stream(client->session, NGHTTP2_FLAG_NONE, req->stream_id, NGHTTP2_INTERNAL_ERROR); } client->signal_write(); } } // namespace ContinueTimer::ContinueTimer(struct ev_loop *loop, Request *req) : loop(loop) { ev_timer_init(&timer, continue_timeout_cb, 1., 0.); timer.data = req; } ContinueTimer::~ContinueTimer() { stop(); } void ContinueTimer::start() { ev_timer_start(loop, &timer); } void ContinueTimer::stop() { ev_timer_stop(loop, &timer); } void ContinueTimer::dispatch_continue() { // Only dispatch the timeout callback if it hasn't already been called. if (ev_is_active(&timer)) { ev_feed_event(loop, &timer, 0); } } namespace { int htp_msg_begincb(llhttp_t *htp) { if (config.verbose) { print_timer(); std::cout << " HTTP Upgrade response" << std::endl; } return 0; } } // namespace namespace { int htp_msg_completecb(llhttp_t *htp) { auto client = static_cast(htp->data); client->upgrade_response_status_code = htp->status_code; client->upgrade_response_complete = true; return 0; } } // namespace namespace { constexpr llhttp_settings_t htp_hooks = { .on_message_begin = htp_msg_begincb, .on_message_complete = htp_msg_completecb, }; } // namespace namespace { int submit_request(HttpClient *client, const Headers &headers, Request *req) { auto scheme = util::get_uri_field(req->uri.c_str(), req->u, URLPARSE_SCHEMA); auto build_headers = Headers{{":method", req->data_prd ? "POST" : "GET"}, {":path", req->make_reqpath()}, {":scheme", std::string{scheme}}, {":authority", client->hostport}, {"priority", http2::encode_extpri(req->extpri)}, {"accept", "*/*"}, {"accept-encoding", "gzip, deflate"}, {"user-agent", "nghttp2/" NGHTTP2_VERSION}}; bool expect_continue = false; if (config.continuation) { for (size_t i = 0; i < 6; ++i) { build_headers.emplace_back("continuation-test-" + util::utos(i + 1), std::string(4_k, '-')); } } auto num_initial_headers = build_headers.size(); if (req->data_prd) { if (!config.no_content_length) { build_headers.emplace_back("content-length", util::utos(as_unsigned(req->data_length))); } if (config.expect_continue) { expect_continue = true; build_headers.emplace_back("expect", "100-continue"); } } for (auto &kv : headers) { size_t i; for (i = 0; i < num_initial_headers; ++i) { if (kv.name == build_headers[i].name) { build_headers[i].value = kv.value; break; } } if (i < num_initial_headers) { continue; } build_headers.emplace_back(kv.name, kv.value, kv.no_index); } auto nva = std::vector(); nva.reserve(build_headers.size()); for (auto &kv : build_headers) { nva.push_back( http2::make_field_nv(kv.name, kv.value, http2::no_index(kv.no_index))); } auto method = http2::get_header(build_headers, ":method"sv); assert(method); req->method = method->value; std::string trailer_names; if (!config.trailer.empty()) { trailer_names = config.trailer[0].name; for (size_t i = 1; i < config.trailer.size(); ++i) { trailer_names += ", "; trailer_names += config.trailer[i].name; } nva.push_back(http2::make_field_v("trailer"sv, trailer_names)); } int32_t stream_id; if (expect_continue) { stream_id = nghttp2_submit_headers(client->session, 0, -1, nullptr, nva.data(), nva.size(), req); } else { stream_id = nghttp2_submit_request2(client->session, nullptr, nva.data(), nva.size(), req->data_prd, req); } if (stream_id < 0) { std::cerr << "[ERROR] nghttp2_submit_" << (expect_continue ? "headers" : "request2") << "() returned error: " << nghttp2_strerror(stream_id) << std::endl; return -1; } req->stream_id = stream_id; client->request_done(req); req->req_nva = std::move(build_headers); if (expect_continue) { auto timer = std::make_unique(client->loop, req); req->continue_timer = std::move(timer); } return 0; } } // namespace namespace { void readcb(struct ev_loop *loop, ev_io *w, int revents) { auto client = static_cast(w->data); if (client->do_read() != 0) { client->disconnect(); } } } // namespace namespace { void writecb(struct ev_loop *loop, ev_io *w, int revents) { auto client = static_cast(w->data); auto rv = client->do_write(); if (rv == HttpClient::ERR_CONNECT_FAIL) { client->connect_fail(); return; } if (rv != 0) { client->disconnect(); } } } // namespace namespace { void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { auto client = static_cast(w->data); std::cerr << "[ERROR] Timeout" << std::endl; client->disconnect(); } } // namespace namespace { void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto client = static_cast(w->data); ev_timer_stop(loop, w); nghttp2_session_terminate_session(client->session, NGHTTP2_SETTINGS_TIMEOUT); client->signal_write(); } } // namespace HttpClient::HttpClient(const nghttp2_session_callbacks *callbacks, struct ev_loop *loop, SSL_CTX *ssl_ctx) : wb(&mcpool), session(nullptr), callbacks(callbacks), loop(loop), ssl_ctx(ssl_ctx), ssl(nullptr), addrs(nullptr), next_addr(nullptr), cur_addr(nullptr), complete(0), success(0), settings_payloadlen(0), state(ClientState::IDLE), upgrade_response_status_code(0), fd(-1), upgrade_response_complete(false) { ev_io_init(&wev, writecb, 0, EV_WRITE); ev_io_init(&rev, readcb, 0, EV_READ); wev.data = this; rev.data = this; ev_timer_init(&wt, timeoutcb, 0., config.timeout); ev_timer_init(&rt, timeoutcb, 0., config.timeout); wt.data = this; rt.data = this; ev_timer_init(&settings_timer, settings_timeout_cb, 0., 10.); settings_timer.data = this; } HttpClient::~HttpClient() { disconnect(); if (addrs) { freeaddrinfo(addrs); addrs = nullptr; next_addr = nullptr; } } bool HttpClient::need_upgrade() const { return config.upgrade && scheme == "http"; } int HttpClient::resolve_host(const std::string &host, uint16_t port) { int rv; this->host = host; addrinfo hints{ .ai_flags = AI_ADDRCONFIG, .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, }; rv = getaddrinfo(host.c_str(), util::utos(port).c_str(), &hints, &addrs); if (rv != 0) { std::cerr << "[ERROR] getaddrinfo() failed: " << gai_strerror(rv) << std::endl; return -1; } if (addrs == nullptr) { std::cerr << "[ERROR] No address returned" << std::endl; return -1; } next_addr = addrs; return 0; } namespace { // Just returns 1 to continue handshake. int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; } } // namespace int HttpClient::initiate_connection() { int rv; cur_addr = nullptr; while (next_addr) { cur_addr = next_addr; next_addr = next_addr->ai_next; fd = util::create_nonblock_socket(cur_addr->ai_family); if (fd == -1) { continue; } if (ssl_ctx) { // We are establishing TLS connection. ssl = SSL_new(ssl_ctx); if (!ssl) { std::cerr << "[ERROR] SSL_new() failed: " << ERR_error_string(ERR_get_error(), nullptr) << std::endl; return -1; } SSL_set_connect_state(ssl); // If the user overrode the :authority or host header, use that // value for the SNI extension const auto &host_string = config.host_override.empty() ? host : config.host_override; auto param = SSL_get0_param(ssl); X509_VERIFY_PARAM_set_hostflags(param, 0); X509_VERIFY_PARAM_set1_host( param, host_string.c_str(), static_cast(host_string.size())); SSL_set_verify(ssl, SSL_VERIFY_PEER, verify_cb); if (!util::numeric_host(host_string.c_str())) { SSL_set_tlsext_host_name(ssl, host_string.c_str()); } } rv = connect(fd, cur_addr->ai_addr, cur_addr->ai_addrlen); if (rv != 0 && errno != EINPROGRESS) { if (ssl) { SSL_free(ssl); ssl = nullptr; } close(fd); fd = -1; continue; } break; } if (fd == -1) { return -1; } writefn = &HttpClient::connected; if (need_upgrade()) { on_readfn = &HttpClient::on_upgrade_read; on_writefn = &HttpClient::on_upgrade_connect; } else { on_readfn = &HttpClient::on_read; on_writefn = &HttpClient::on_write; } ev_io_set(&rev, fd, EV_READ); ev_io_set(&wev, fd, EV_WRITE); ev_io_start(loop, &wev); ev_timer_again(loop, &wt); return 0; } void HttpClient::disconnect() { state = ClientState::IDLE; for (auto &req : reqvec) { if (req->continue_timer) { req->continue_timer->stop(); } } ev_timer_stop(loop, &settings_timer); ev_timer_stop(loop, &rt); ev_timer_stop(loop, &wt); ev_io_stop(loop, &rev); ev_io_stop(loop, &wev); nghttp2_session_del(session); session = nullptr; if (ssl) { SSL_set_shutdown(ssl, SSL_get_shutdown(ssl) | SSL_RECEIVED_SHUTDOWN); ERR_clear_error(); SSL_shutdown(ssl); SSL_free(ssl); ssl = nullptr; } if (fd != -1) { shutdown(fd, SHUT_WR); close(fd); fd = -1; } } int HttpClient::read_clear() { ev_timer_again(loop, &rt); std::array buf; for (;;) { ssize_t nread; while ((nread = read(fd, buf.data(), buf.size())) == -1 && errno == EINTR) ; if (nread == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return 0; } return -1; } if (nread == 0) { return -1; } if (on_readfn(*this, buf.data(), as_unsigned(nread)) != 0) { return -1; } } return 0; } int HttpClient::write_clear() { ev_timer_again(loop, &rt); std::array iov; for (;;) { if (on_writefn(*this) != 0) { return -1; } auto iovcnt = wb.riovec(iov.data(), iov.size()); if (iovcnt == 0) { break; } ssize_t nwrite; while ((nwrite = writev(fd, iov.data(), iovcnt)) == -1 && errno == EINTR) ; if (nwrite == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { ev_io_start(loop, &wev); ev_timer_again(loop, &wt); return 0; } return -1; } wb.drain(as_unsigned(nwrite)); } ev_io_stop(loop, &wev); ev_timer_stop(loop, &wt); return 0; } int HttpClient::noop() { return 0; } void HttpClient::connect_fail() { if (state == ClientState::IDLE) { std::cerr << "[ERROR] Could not connect to the address " << util::numeric_name(cur_addr->ai_addr, cur_addr->ai_addrlen) << std::endl; } auto cur_state = state; disconnect(); if (cur_state == ClientState::IDLE) { if (initiate_connection() == 0) { std::cerr << "Trying next address " << util::numeric_name(cur_addr->ai_addr, cur_addr->ai_addrlen) << std::endl; } } } int HttpClient::connected() { if (!util::check_socket_connected(fd)) { return ERR_CONNECT_FAIL; } if (config.verbose) { print_timer(); std::cout << " Connected" << std::endl; } state = ClientState::CONNECTED; ev_io_start(loop, &rev); ev_io_stop(loop, &wev); ev_timer_again(loop, &rt); ev_timer_stop(loop, &wt); if (ssl) { SSL_set_fd(ssl, fd); readfn = &HttpClient::tls_handshake; writefn = &HttpClient::tls_handshake; return do_write(); } readfn = &HttpClient::read_clear; writefn = &HttpClient::write_clear; if (need_upgrade()) { htp = std::make_unique(); llhttp_init(htp.get(), HTTP_RESPONSE, &htp_hooks); htp->data = this; return do_write(); } if (connection_made() != 0) { return -1; } return 0; } namespace { size_t populate_settings(nghttp2_settings_entry *iv) { size_t niv = 3; iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; iv[0].value = static_cast(config.max_concurrent_streams); iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; if (config.window_bits != -1) { iv[1].value = (1 << config.window_bits) - 1; } else { iv[1].value = NGHTTP2_INITIAL_WINDOW_SIZE; } iv[2].settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES; iv[2].value = 1; if (config.header_table_size >= 0) { if (config.min_header_table_size < config.header_table_size) { iv[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[niv].value = static_cast(config.min_header_table_size); ++niv; } iv[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[niv].value = static_cast(config.header_table_size); ++niv; } if (config.no_push) { iv[niv].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; iv[niv].value = 0; ++niv; } return niv; } } // namespace int HttpClient::on_upgrade_connect() { nghttp2_ssize rv; record_connect_end_time(); assert(!reqvec.empty()); std::array iv; size_t niv = populate_settings(iv.data()); assert(settings_payload.size() >= 8 * niv); rv = nghttp2_pack_settings_payload2(settings_payload.data(), settings_payload.size(), iv.data(), niv); if (rv < 0) { return -1; } settings_payloadlen = as_unsigned(rv); auto token68 = base64::encode(std::span{settings_payload.data(), settings_payloadlen}); util::to_token68(token68); std::string req; if (reqvec[0]->data_prd) { // If the request contains upload data, use OPTIONS * to upgrade req = "OPTIONS *"; } else { auto meth = std::ranges::find_if( config.headers, [](const auto &kv) { return ":method"sv == kv.name; }); if (meth == std::ranges::end(config.headers)) { req = "GET "; reqvec[0]->method = "GET"; } else { req = (*meth).value; req += ' '; reqvec[0]->method = (*meth).value; } req += reqvec[0]->make_reqpath(); } auto headers = Headers{{"host", hostport}, {"connection", "Upgrade, HTTP2-Settings"}, {"upgrade", NGHTTP2_CLEARTEXT_PROTO_VERSION_ID}, {"http2-settings", std::move(token68)}, {"accept", "*/*"}, {"user-agent", "nghttp2/" NGHTTP2_VERSION}}; auto initial_headerslen = headers.size(); if (!reqvec[0]->data_prd) { headers.emplace_back("priority", http2::encode_extpri(reqvec[0]->extpri)); ++initial_headerslen; } for (auto &kv : config.headers) { size_t i; if (kv.name.empty() || kv.name[0] == ':') { continue; } for (i = 0; i < initial_headerslen; ++i) { if (kv.name == headers[i].name) { headers[i].value = kv.value; break; } } if (i < initial_headerslen) { continue; } headers.emplace_back(kv.name, kv.value, kv.no_index); } req += " HTTP/1.1\r\n"; for (auto &kv : headers) { req += kv.name; req += ": "; req += kv.value; req += "\r\n"; } req += "\r\n"; wb.append(req); if (config.verbose) { print_timer(); std::cout << " HTTP Upgrade request\n" << req << std::endl; } if (!reqvec[0]->data_prd) { // record request time if this is a part of real request. reqvec[0]->record_request_start_time(); reqvec[0]->req_nva = std::move(headers); } on_writefn = &HttpClient::noop; signal_write(); return 0; } int HttpClient::on_upgrade_read(const uint8_t *data, size_t len) { int rv; auto htperr = llhttp_execute(htp.get(), reinterpret_cast(data), len); auto nread = htperr == HPE_OK ? len : static_cast(reinterpret_cast( llhttp_get_error_pos(htp.get())) - data); if (config.verbose) { std::cout.write(reinterpret_cast(data), static_cast(nread)); } if (htperr != HPE_OK && htperr != HPE_PAUSED_UPGRADE) { std::cerr << "[ERROR] Failed to parse HTTP Upgrade response header: " << "(" << llhttp_errno_name(htperr) << ") " << llhttp_get_error_reason(htp.get()) << std::endl; return -1; } if (!upgrade_response_complete) { return 0; } if (config.verbose) { std::cout << std::endl; } if (upgrade_response_status_code != 101) { std::cerr << "[ERROR] HTTP Upgrade failed" << std::endl; return -1; } if (config.verbose) { print_timer(); std::cout << " HTTP Upgrade success" << std::endl; } on_readfn = &HttpClient::on_read; on_writefn = &HttpClient::on_write; rv = connection_made(); if (rv != 0) { return rv; } // Read remaining data in the buffer because it is not notified // callback anymore. rv = on_readfn(*this, data + nread, len - nread); if (rv != 0) { return rv; } return 0; } int HttpClient::do_read() { return readfn(*this); } int HttpClient::do_write() { return writefn(*this); } int HttpClient::connection_made() { int rv; if (!need_upgrade()) { record_connect_end_time(); } if (ssl) { // Check ALPN result const unsigned char *next_proto = nullptr; unsigned int next_proto_len; SSL_get0_alpn_selected(ssl, &next_proto, &next_proto_len); if (next_proto) { auto proto = as_string_view(next_proto, next_proto_len); if (config.verbose) { std::cout << "The negotiated protocol: " << proto << std::endl; } if (!util::check_h2_is_selected(proto)) { next_proto = nullptr; } } if (!next_proto) { print_protocol_nego_error(); return -1; } } rv = nghttp2_session_client_new2(&session, callbacks, this, config.http2_option); if (rv != 0) { return -1; } if (need_upgrade()) { // Adjust stream user-data depending on the existence of upload // data Request *stream_user_data = nullptr; if (!reqvec[0]->data_prd) { stream_user_data = reqvec[0].get(); } // If HEAD is used, that is only when user specified it with -H // option. auto head_request = stream_user_data && stream_user_data->method == "HEAD"; rv = nghttp2_session_upgrade2(session, settings_payload.data(), settings_payloadlen, head_request, stream_user_data); if (rv != 0) { std::cerr << "[ERROR] nghttp2_session_upgrade() returned error: " << nghttp2_strerror(rv) << std::endl; return -1; } if (stream_user_data) { stream_user_data->stream_id = 1; request_done(stream_user_data); } } // If upgrade succeeds, the SETTINGS value sent with // HTTP2-Settings header field has already been submitted to // session object. if (!need_upgrade()) { std::array iv; auto niv = populate_settings(iv.data()); rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv.data(), niv); if (rv != 0) { return -1; } } ev_timer_again(loop, &settings_timer); if (config.connection_window_bits != -1) { int32_t window_size = (1 << config.connection_window_bits) - 1; rv = nghttp2_session_set_local_window_size(session, NGHTTP2_FLAG_NONE, 0, window_size); if (rv != 0) { return -1; } } // Adjust first request depending on the existence of the upload // data for (auto i = std::ranges::begin(reqvec) + (need_upgrade() && !reqvec[0]->data_prd); i != std::ranges::end(reqvec); ++i) { if (submit_request(this, config.headers, (*i).get()) != 0) { return -1; } } signal_write(); return 0; } int HttpClient::on_read(const uint8_t *data, size_t len) { if (config.hexdump) { util::hexdump(stdout, data, len); } auto rv = nghttp2_session_mem_recv2(session, data, len); if (rv < 0) { std::cerr << "[ERROR] nghttp2_session_mem_recv2() returned error: " << nghttp2_strerror(static_cast(rv)) << std::endl; return -1; } assert(static_cast(rv) == len); if (nghttp2_session_want_read(session) == 0 && nghttp2_session_want_write(session) == 0 && wb.rleft() == 0) { return -1; } signal_write(); return 0; } int HttpClient::on_write() { for (;;) { if (wb.rleft() >= 16384) { return 0; } const uint8_t *data; auto len = nghttp2_session_mem_send2(session, &data); if (len < 0) { std::cerr << "[ERROR] nghttp2_session_send2() returned error: " << nghttp2_strerror(static_cast(len)) << std::endl; return -1; } if (len == 0) { break; } wb.append(data, as_unsigned(len)); } if (nghttp2_session_want_read(session) == 0 && nghttp2_session_want_write(session) == 0 && wb.rleft() == 0) { return -1; } return 0; } int HttpClient::tls_handshake() { ev_timer_again(loop, &rt); ERR_clear_error(); auto rv = SSL_do_handshake(ssl); if (rv <= 0) { auto err = SSL_get_error(ssl, rv); switch (err) { case SSL_ERROR_WANT_READ: ev_io_stop(loop, &wev); ev_timer_stop(loop, &wt); return 0; case SSL_ERROR_WANT_WRITE: ev_io_start(loop, &wev); ev_timer_again(loop, &wt); return 0; default: return -1; } } ev_io_stop(loop, &wev); ev_timer_stop(loop, &wt); readfn = &HttpClient::read_tls; writefn = &HttpClient::write_tls; if (config.verify_peer) { auto verify_res = SSL_get_verify_result(ssl); if (verify_res != X509_V_OK) { std::cerr << "[WARNING] Certificate verification failed: " << X509_verify_cert_error_string(verify_res) << std::endl; } } if (connection_made() != 0) { return -1; } return 0; } int HttpClient::read_tls() { ev_timer_again(loop, &rt); ERR_clear_error(); std::array buf; for (;;) { auto rv = SSL_read(ssl, buf.data(), buf.size()); if (rv <= 0) { auto err = SSL_get_error(ssl, rv); switch (err) { case SSL_ERROR_WANT_READ: return 0; case SSL_ERROR_WANT_WRITE: // renegotiation started return -1; default: return -1; } } if (on_readfn(*this, buf.data(), static_cast(rv)) != 0) { return -1; } } } int HttpClient::write_tls() { ev_timer_again(loop, &rt); ERR_clear_error(); struct iovec iov; for (;;) { if (on_writefn(*this) != 0) { return -1; } auto iovcnt = wb.riovec(&iov, 1); if (iovcnt == 0) { break; } auto rv = SSL_write(ssl, iov.iov_base, static_cast(iov.iov_len)); if (rv <= 0) { auto err = SSL_get_error(ssl, rv); switch (err) { case SSL_ERROR_WANT_READ: // renegotiation started return -1; case SSL_ERROR_WANT_WRITE: ev_io_start(loop, &wev); ev_timer_again(loop, &wt); return 0; default: return -1; } } wb.drain(static_cast(rv)); } ev_io_stop(loop, &wev); ev_timer_stop(loop, &wt); return 0; } void HttpClient::signal_write() { ev_io_start(loop, &wev); } bool HttpClient::all_requests_processed() const { return complete == reqvec.size(); } void HttpClient::update_hostport() { if (reqvec.empty()) { return; } scheme = util::get_uri_field(reqvec[0]->uri.c_str(), reqvec[0]->u, URLPARSE_SCHEMA); std::stringstream ss; if (reqvec[0]->is_ipv6_literal_addr()) { // we may have zone ID, which must start with "%25", or "%". RFC // 6874 defines "%25" only, and just "%" is allowed for just // convenience to end-user input. auto host = util::get_uri_field(reqvec[0]->uri.c_str(), reqvec[0]->u, URLPARSE_HOST); auto end = std::ranges::find(host, '%'); ss << "["; ss.write(host.data(), end - std::ranges::begin(host)); ss << "]"; } else { util::write_uri_field(ss, reqvec[0]->uri.c_str(), reqvec[0]->u, URLPARSE_HOST); } if (util::has_uri_field(reqvec[0]->u, URLPARSE_PORT) && reqvec[0]->u.port != util::get_default_port(reqvec[0]->uri.c_str(), reqvec[0]->u)) { ss << ":" << reqvec[0]->u.port; } hostport = ss.str(); } bool HttpClient::add_request(const std::string &uri, const nghttp2_data_provider2 *data_prd, int64_t data_length, const nghttp2_extpri &extpri, int level) { urlparse_url u; if (urlparse_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { return false; } if (path_cache.contains(uri)) { return false; } if (config.multiply == 1) { path_cache.insert(uri); } reqvec.push_back( std::make_unique(uri, u, data_prd, data_length, extpri, level)); return true; } void HttpClient::record_start_time() { timing.system_start_time = std::chrono::system_clock::now(); timing.start_time = get_time(); } void HttpClient::record_domain_lookup_end_time() { timing.domain_lookup_end_time = get_time(); } void HttpClient::record_connect_end_time() { timing.connect_end_time = get_time(); } void HttpClient::request_done(Request *req) { if (req->stream_id % 2 == 0) { return; } } #ifdef HAVE_JANSSON void HttpClient::output_har(FILE *outfile) { static auto PAGE_ID = "page_0"; auto root = json_object(); auto log = json_object(); json_object_set_new(root, "log", log); json_object_set_new(log, "version", json_string("1.2")); auto creator = json_object(); json_object_set_new(log, "creator", creator); json_object_set_new(creator, "name", json_string("nghttp")); json_object_set_new(creator, "version", json_string(NGHTTP2_VERSION)); auto pages = json_array(); json_object_set_new(log, "pages", pages); auto page = json_object(); json_array_append_new(pages, page); json_object_set_new( page, "startedDateTime", json_string(util::format_iso8601(timing.system_start_time).c_str())); json_object_set_new(page, "id", json_string(PAGE_ID)); json_object_set_new(page, "title", json_string("")); json_object_set_new(page, "pageTimings", json_object()); auto entries = json_array(); json_object_set_new(log, "entries", entries); auto dns_delta = static_cast(std::chrono::duration_cast( timing.domain_lookup_end_time - timing.start_time) .count()) / 1000.0; auto connect_delta = static_cast( std::chrono::duration_cast( timing.connect_end_time - timing.domain_lookup_end_time) .count()) / 1000.0; for (size_t i = 0; i < reqvec.size(); ++i) { auto &req = reqvec[i]; if (req->timing.state != RequestState::ON_COMPLETE) { continue; } auto entry = json_object(); json_array_append_new(entries, entry); auto &req_timing = req->timing; auto request_time = (i == 0) ? timing.system_start_time : timing.system_start_time + std::chrono::duration_cast( req_timing.request_start_time - timing.start_time); auto wait_delta = static_cast( std::chrono::duration_cast( req_timing.response_start_time - req_timing.request_start_time) .count()) / 1000.0; auto receive_delta = static_cast( std::chrono::duration_cast( req_timing.response_end_time - req_timing.response_start_time) .count()) / 1000.0; auto time_sum = static_cast( std::chrono::duration_cast( (i == 0) ? (req_timing.response_end_time - timing.start_time) : (req_timing.response_end_time - req_timing.request_start_time)) .count()) / 1000.0; json_object_set_new( entry, "startedDateTime", json_string(util::format_iso8601(request_time).c_str())); json_object_set_new(entry, "time", json_real(time_sum)); auto pushed = req->stream_id % 2 == 0; json_object_set_new(entry, "comment", json_string(pushed ? "Pushed Object" : "")); auto request = json_object(); json_object_set_new(entry, "request", request); auto req_headers = json_array(); json_object_set_new(request, "headers", req_headers); for (auto &nv : req->req_nva) { auto hd = json_object(); json_array_append_new(req_headers, hd); json_object_set_new(hd, "name", json_string(nv.name.c_str())); json_object_set_new(hd, "value", json_string(nv.value.c_str())); } json_object_set_new(request, "method", json_string(req->method.c_str())); json_object_set_new(request, "url", json_string(req->uri.c_str())); json_object_set_new(request, "httpVersion", json_string("HTTP/2.0")); json_object_set_new(request, "cookies", json_array()); json_object_set_new(request, "queryString", json_array()); json_object_set_new(request, "headersSize", json_integer(-1)); json_object_set_new(request, "bodySize", json_integer(-1)); auto response = json_object(); json_object_set_new(entry, "response", response); auto res_headers = json_array(); json_object_set_new(response, "headers", res_headers); for (auto &nv : req->res_nva) { auto hd = json_object(); json_array_append_new(res_headers, hd); json_object_set_new(hd, "name", json_string(nv.name.c_str())); json_object_set_new(hd, "value", json_string(nv.value.c_str())); } json_object_set_new(response, "status", json_integer(req->status)); json_object_set_new(response, "statusText", json_string("")); json_object_set_new(response, "httpVersion", json_string("HTTP/2.0")); json_object_set_new(response, "cookies", json_array()); auto content = json_object(); json_object_set_new(response, "content", content); json_object_set_new(content, "size", json_integer(req->response_len)); auto content_type_ptr = http2::get_header(req->res_nva, "content-type"sv); const char *content_type = ""; if (content_type_ptr) { content_type = content_type_ptr->value.c_str(); } json_object_set_new(content, "mimeType", json_string(content_type)); json_object_set_new(response, "redirectURL", json_string("")); json_object_set_new(response, "headersSize", json_integer(-1)); json_object_set_new(response, "bodySize", json_integer(-1)); json_object_set_new(entry, "cache", json_object()); auto timings = json_object(); json_object_set_new(entry, "timings", timings); auto dns_timing = (i == 0) ? dns_delta : 0; auto connect_timing = (i == 0) ? connect_delta : 0; json_object_set_new(timings, "dns", json_real(dns_timing)); json_object_set_new(timings, "connect", json_real(connect_timing)); json_object_set_new(timings, "blocked", json_real(0.0)); json_object_set_new(timings, "send", json_real(0.0)); json_object_set_new(timings, "wait", json_real(wait_delta)); json_object_set_new(timings, "receive", json_real(receive_delta)); json_object_set_new(entry, "pageref", json_string(PAGE_ID)); json_object_set_new( entry, "connection", json_string(util::utos(as_unsigned(req->stream_id)).c_str())); } json_dumpf(root, outfile, JSON_PRESERVE_ORDER | JSON_INDENT(2)); json_decref(root); } #endif // defined(HAVE_JANSSON) namespace { void update_html_parser(HttpClient *client, Request *req, const uint8_t *data, size_t len, int fin) { if (!req->html_parser) { return; } req->update_html_parser(data, len, fin); auto scheme = req->get_real_scheme(); auto host = req->get_real_host(); auto port = req->get_real_port(); for (auto &p : req->html_parser->get_links()) { auto uri = strip_fragment(p.first.c_str()); auto res_type = p.second; urlparse_url u; if (urlparse_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { continue; } if (!util::fieldeq(uri.c_str(), u, URLPARSE_SCHEMA, scheme) || !util::fieldeq(uri.c_str(), u, URLPARSE_HOST, host)) { continue; } auto link_port = util::has_uri_field(u, URLPARSE_PORT) ? u.port : scheme == "https"sv ? 443 : 80; if (port != link_port) { continue; } // No POST data for assets auto extpri = resolve_pri(res_type); if (client->add_request(uri, nullptr, 0, extpri, req->level + 1)) { submit_request(client, config.headers, client->reqvec.back().get()); } } req->html_parser->clear_links(); } } // namespace namespace { HttpClient *get_client(void *user_data) { return static_cast(user_data); } } // namespace namespace { int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data) { auto client = get_client(user_data); auto req = static_cast( nghttp2_session_get_stream_user_data(session, stream_id)); if (!req) { return 0; } if (config.verbose >= 2) { verbose_on_data_chunk_recv_callback(session, flags, stream_id, data, len, user_data); } req->response_len += len; if (req->inflater) { while (len > 0) { const size_t MAX_OUTLEN = 4_k; std::array out; size_t outlen = MAX_OUTLEN; size_t tlen = len; int rv = nghttp2_gzip_inflate(req->inflater, out.data(), &outlen, data, &tlen); if (rv != 0) { nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id, NGHTTP2_INTERNAL_ERROR); break; } if (!config.null_out) { std::cout.write(reinterpret_cast(out.data()), static_cast(outlen)); } update_html_parser(client, req, out.data(), outlen, 0); data += tlen; len -= tlen; } return 0; } if (!config.null_out) { std::cout.write(reinterpret_cast(data), static_cast(len)); } update_html_parser(client, req, data, len, 0); return 0; } } // namespace namespace { nghttp2_ssize select_padding_callback(nghttp2_session *session, const nghttp2_frame *frame, size_t max_payload, void *user_data) { return as_signed(std::min(max_payload, frame->hd.length + config.padding)); } } // namespace namespace { void check_response_header(nghttp2_session *session, Request *req) { bool gzip = false; req->expect_final_response = false; auto status_hd = req->get_res_header(http2::HD__STATUS); if (!status_hd) { nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id, NGHTTP2_PROTOCOL_ERROR); return; } auto status = http2::parse_http_status_code(status_hd->value); if (status == -1) { nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id, NGHTTP2_PROTOCOL_ERROR); return; } req->status = status; for (auto &nv : req->res_nva) { if ("content-encoding" == nv.name) { gzip = util::strieq("gzip"sv, nv.value) || util::strieq("deflate"sv, nv.value); continue; } } if (req->status / 100 == 1) { if (req->continue_timer && (req->status == 100)) { // If the request is waiting for a 100 Continue, complete the handshake. req->continue_timer->dispatch_continue(); } req->expect_final_response = true; req->status = 0; req->res_nva.clear(); http2::init_hdidx(req->res_hdidx); return; } else if (req->continue_timer) { // A final response stops any pending Expect/Continue handshake. req->continue_timer->stop(); } if (gzip) { if (!req->inflater) { req->init_inflater(); } } if (config.get_assets && req->level == 0) { if (!req->html_parser) { req->init_html_parser(); } } } } // namespace namespace { int on_begin_headers_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { auto client = get_client(user_data); switch (frame->hd.type) { case NGHTTP2_HEADERS: { auto req = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); if (!req) { break; } switch (frame->headers.cat) { case NGHTTP2_HCAT_RESPONSE: case NGHTTP2_HCAT_PUSH_RESPONSE: req->record_response_start_time(); break; default: break; } break; } case NGHTTP2_PUSH_PROMISE: { auto stream_id = frame->push_promise.promised_stream_id; nghttp2_extpri extpri{ .urgency = NGHTTP2_EXTPRI_DEFAULT_URGENCY, }; auto req = std::make_unique("", urlparse_url{}, nullptr, 0, extpri); req->stream_id = stream_id; nghttp2_session_set_stream_user_data(session, stream_id, req.get()); client->request_done(req.get()); req->record_request_start_time(); client->reqvec.push_back(std::move(req)); break; } } return 0; } } // namespace namespace { int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *user_data) { if (config.verbose) { verbose_on_header_callback(session, frame, name, namelen, value, valuelen, flags, user_data); } switch (frame->hd.type) { case NGHTTP2_HEADERS: { auto req = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); if (!req) { break; } /* ignore trailer header */ if (frame->headers.cat == NGHTTP2_HCAT_HEADERS && !req->expect_final_response) { break; } if (req->header_buffer_size + namelen + valuelen > 64_k) { nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR); return 0; } req->header_buffer_size += namelen + valuelen; auto nameref = as_string_view(name, namelen); auto valueref = as_string_view(value, valuelen); auto token = http2::lookup_token(nameref); http2::index_header(req->res_hdidx, token, req->res_nva.size()); http2::add_header(req->res_nva, nameref, valueref, flags & NGHTTP2_NV_FLAG_NO_INDEX, token); break; } case NGHTTP2_PUSH_PROMISE: { auto req = static_cast(nghttp2_session_get_stream_user_data( session, frame->push_promise.promised_stream_id)); if (!req) { break; } if (req->header_buffer_size + namelen + valuelen > 64_k) { nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->push_promise.promised_stream_id, NGHTTP2_INTERNAL_ERROR); return 0; } req->header_buffer_size += namelen + valuelen; auto nameref = as_string_view(name, namelen); auto valueref = as_string_view(value, valuelen); auto token = http2::lookup_token(nameref); http2::index_header(req->req_hdidx, token, req->req_nva.size()); http2::add_header(req->req_nva, nameref, valueref, flags & NGHTTP2_NV_FLAG_NO_INDEX, token); break; } } return 0; } } // namespace namespace { int on_frame_recv_callback2(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { int rv = 0; if (config.verbose) { verbose_on_frame_recv_callback(session, frame, user_data); } auto client = get_client(user_data); switch (frame->hd.type) { case NGHTTP2_DATA: { auto req = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); if (!req) { return 0; ; } if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { req->record_response_end_time(); ++client->success; } break; } case NGHTTP2_HEADERS: { auto req = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); // If this is the HTTP Upgrade with OPTIONS method to avoid POST, // req is nullptr. if (!req) { return 0; ; } switch (frame->headers.cat) { case NGHTTP2_HCAT_RESPONSE: case NGHTTP2_HCAT_PUSH_RESPONSE: check_response_header(session, req); break; case NGHTTP2_HCAT_HEADERS: if (req->expect_final_response) { check_response_header(session, req); break; } if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR); return 0; } break; default: assert(0); } if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { req->record_response_end_time(); ++client->success; } break; } case NGHTTP2_SETTINGS: if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { break; } ev_timer_stop(client->loop, &client->settings_timer); break; case NGHTTP2_PUSH_PROMISE: { auto req = static_cast(nghttp2_session_get_stream_user_data( session, frame->push_promise.promised_stream_id)); if (!req) { break; } // Reset for response header field reception req->header_buffer_size = 0; auto scheme = req->get_req_header(http2::HD__SCHEME); auto authority = req->get_req_header(http2::HD__AUTHORITY); auto path = req->get_req_header(http2::HD__PATH); if (!authority) { authority = req->get_req_header(http2::HD_HOST); } // libnghttp2 guarantees :scheme, :method, :path and (:authority | // host) exist and non-empty. if (path->value[0] != '/') { nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->push_promise.promised_stream_id, NGHTTP2_PROTOCOL_ERROR); break; } std::string uri = scheme->value; uri += "://"; uri += authority->value; uri += path->value; urlparse_url u; if (urlparse_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->push_promise.promised_stream_id, NGHTTP2_PROTOCOL_ERROR); break; } req->uri = uri; req->u = u; if (client->path_cache.contains(uri)) { nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->push_promise.promised_stream_id, NGHTTP2_CANCEL); break; } if (config.multiply == 1) { client->path_cache.emplace(std::move(uri)); } break; } } return rv; } } // namespace namespace { int before_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { if (frame->hd.type != NGHTTP2_HEADERS || frame->headers.cat != NGHTTP2_HCAT_REQUEST) { return 0; } auto req = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); assert(req); req->record_request_start_time(); return 0; } } // namespace namespace { int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { if (config.verbose) { verbose_on_frame_send_callback(session, frame, user_data); } if (frame->hd.type != NGHTTP2_HEADERS || frame->headers.cat != NGHTTP2_HCAT_REQUEST) { return 0; } auto req = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); if (!req) { return 0; } // If this request is using Expect/Continue, start its ContinueTimer. if (req->continue_timer) { req->continue_timer->start(); } return 0; } } // namespace namespace { int on_frame_not_send_callback(nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, void *user_data) { if (frame->hd.type != NGHTTP2_HEADERS || frame->headers.cat != NGHTTP2_HCAT_REQUEST) { return 0; } auto req = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); if (!req) { return 0; } std::cerr << "[ERROR] request " << req->uri << " failed: " << nghttp2_strerror(lib_error_code) << std::endl; return 0; } } // namespace namespace { int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *user_data) { auto client = get_client(user_data); auto req = static_cast( nghttp2_session_get_stream_user_data(session, stream_id)); if (!req) { return 0; } // If this request is using Expect/Continue, stop its ContinueTimer. if (req->continue_timer) { req->continue_timer->stop(); } update_html_parser(client, req, nullptr, 0, 1); ++client->complete; if (client->all_requests_processed()) { nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); } return 0; } } // namespace struct RequestResult { std::chrono::microseconds time; }; namespace { void print_stats(const HttpClient &client) { std::cout << "***** Statistics *****" << std::endl; std::vector reqs; reqs.reserve(client.reqvec.size()); for (const auto &req : client.reqvec) { if (req->timing.state == RequestState::ON_COMPLETE) { reqs.push_back(req.get()); } } std::ranges::sort(reqs, [](const Request *lhs, const Request *rhs) { const auto <iming = lhs->timing; const auto &rtiming = rhs->timing; return ltiming.response_end_time < rtiming.response_end_time || (ltiming.response_end_time == rtiming.response_end_time && ltiming.request_start_time < rtiming.request_start_time); }); std::cout << R"( Request timing: responseEnd: the time when last byte of response was received relative to connectEnd requestStart: the time just before first byte of request was sent relative to connectEnd. If '*' is shown, this was pushed by server. process: responseEnd - requestStart code: HTTP status code size: number of bytes received as response body without inflation. URI: request URI see http://www.w3.org/TR/resource-timing/#processing-model sorted by 'complete' id responseEnd requestStart process code size request path)" << std::endl; const auto &base = client.timing.connect_end_time; for (const auto &req : reqs) { auto response_end = std::chrono::duration_cast( req->timing.response_end_time - base); auto request_start = std::chrono::duration_cast( req->timing.request_start_time - base); auto total = std::chrono::duration_cast( req->timing.response_end_time - req->timing.request_start_time); auto pushed = req->stream_id % 2 == 0; std::cout << std::setw(3) << req->stream_id << " " << std::setw(11) << ("+" + util::format_duration(response_end)) << " " << (pushed ? "*" : " ") << std::setw(11) << ("+" + util::format_duration(request_start)) << " " << std::setw(8) << util::format_duration(total) << " " << std::setw(4) << req->status << " " << std::setw(4) << util::utos_unit(as_unsigned(req->response_len)) << " " << req->make_reqpath() << std::endl; } } } // namespace namespace { int communicate( const std::string &scheme, const std::string &host, uint16_t port, std::vector< std::tuple> requests, const nghttp2_session_callbacks *callbacks) { int result = 0; auto loop = EV_DEFAULT; SSL_CTX *ssl_ctx = nullptr; if (scheme == "https") { ssl_ctx = SSL_CTX_new(TLS_client_method()); if (!ssl_ctx) { std::cerr << "[ERROR] Failed to create SSL_CTX: " << ERR_error_string(ERR_get_error(), nullptr) << std::endl; result = -1; goto fin; } auto ssl_opts = static_cast( (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); #ifdef SSL_OP_ENABLE_KTLS if (config.ktls) { ssl_opts |= SSL_OP_ENABLE_KTLS; } #endif // defined(SSL_OP_ENABLE_KTLS) SSL_CTX_set_options(ssl_ctx, ssl_opts); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); if (SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) { std::cerr << "[WARNING] Could not load system trusted CA certificates: " << ERR_error_string(ERR_get_error(), nullptr) << std::endl; } if (nghttp2::tls::ssl_ctx_set_proto_versions( ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION, nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) { std::cerr << "[ERROR] Could not set TLS versions" << std::endl; result = -1; goto fin; } if (SSL_CTX_set_cipher_list(ssl_ctx, tls::DEFAULT_CIPHER_LIST.data()) == 0) { std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr) << std::endl; result = -1; goto fin; } #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL if (SSL_CTX_set_ciphersuites(ssl_ctx, tls::DEFAULT_TLS13_CIPHER_LIST.data()) == 0) { std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr) << std::endl; result = -1; goto fin; } #endif // defined(NGHTTP2_OPENSSL_IS_WOLFSSL) if (!config.keyfile.empty()) { if (SSL_CTX_use_PrivateKey_file(ssl_ctx, config.keyfile.c_str(), SSL_FILETYPE_PEM) != 1) { std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr) << std::endl; result = -1; goto fin; } } if (!config.certfile.empty()) { if (SSL_CTX_use_certificate_chain_file(ssl_ctx, config.certfile.c_str()) != 1) { std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr) << std::endl; result = -1; goto fin; } } SSL_CTX_set_alpn_protos( ssl_ctx, reinterpret_cast(NGHTTP2_H2_ALPN.data()), NGHTTP2_H2_ALPN.size()); #if defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && defined(HAVE_LIBBROTLI) if (!SSL_CTX_add_cert_compression_alg( ssl_ctx, nghttp2::tls::CERTIFICATE_COMPRESSION_ALGO_BROTLI, nghttp2::tls::cert_compress, nghttp2::tls::cert_decompress)) { std::cerr << "[ERROR] SSL_CTX_add_cert_compression_alg failed." << std::endl; result = -1; goto fin; } #endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && // defined(HAVE_LIBBROTLI) if (tls::setup_keylog_callback(ssl_ctx) != 0) { std::cerr << "[ERROR] Failed to setup keylog" << std::endl; result = -1; goto fin; } } { HttpClient client{callbacks, loop, ssl_ctx}; for (auto &req : requests) { for (int i = 0; i < config.multiply; ++i) { client.add_request(std::get<0>(req), std::get<1>(req), std::get<2>(req), std::get<3>(req)); } } client.update_hostport(); client.record_start_time(); if (client.resolve_host(host, port) != 0) { goto fin; } client.record_domain_lookup_end_time(); if (client.initiate_connection() != 0) { std::cerr << "[ERROR] Could not connect to " << host << ", port " << port << std::endl; goto fin; } ev_set_userdata(loop, &client); ev_run(loop, 0); ev_set_userdata(loop, nullptr); #ifdef HAVE_JANSSON if (!config.harfile.empty()) { FILE *outfile; if (config.harfile == "-") { outfile = stdout; } else { outfile = fopen(config.harfile.c_str(), "wb"); } if (outfile) { client.output_har(outfile); if (outfile != stdout) { fclose(outfile); } } else { std::cerr << "Cannot open file " << config.harfile << ". " << "har file could not be created." << std::endl; } } #endif // defined(HAVE_JANSSON) if (client.success != client.reqvec.size()) { std::cerr << "Some requests were not processed. total=" << client.reqvec.size() << ", processed=" << client.success << std::endl; } if (config.stat) { print_stats(client); } } fin: if (ssl_ctx) { SSL_CTX_free(ssl_ctx); } return result; } } // namespace namespace { nghttp2_ssize file_read_callback(nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *user_data) { int rv; auto req = static_cast( nghttp2_session_get_stream_user_data(session, stream_id)); assert(req); int fd = source->fd; ssize_t nread; while ((nread = pread(fd, buf, length, req->data_offset)) == -1 && errno == EINTR) ; if (nread == -1) { return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } req->data_offset += nread; if (req->data_offset == req->data_length) { *data_flags |= NGHTTP2_DATA_FLAG_EOF; if (!config.trailer.empty()) { std::vector nva; nva.reserve(config.trailer.size()); for (auto &kv : config.trailer) { nva.push_back(http2::make_field_nv(kv.name, kv.value, http2::no_index(kv.no_index))); } rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size()); if (rv != 0) { if (nghttp2_is_fatal(rv)) { return NGHTTP2_ERR_CALLBACK_FAILURE; } } else { *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; } } return static_cast(nread); } if (req->data_offset > req->data_length || nread == 0) { return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } return static_cast(nread); } } // namespace namespace { int run(char **uris, int n) { nghttp2_session_callbacks *callbacks; nghttp2_session_callbacks_new(&callbacks); auto cbsdel = defer(nghttp2_session_callbacks_del, callbacks); nghttp2_session_callbacks_set_on_stream_close_callback( callbacks, on_stream_close_callback); nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, on_frame_recv_callback2); if (config.verbose) { nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( callbacks, verbose_on_invalid_frame_recv_callback); nghttp2_session_callbacks_set_error_callback2(callbacks, verbose_error_callback); } nghttp2_session_callbacks_set_on_data_chunk_recv_callback( callbacks, on_data_chunk_recv_callback); nghttp2_session_callbacks_set_on_begin_headers_callback( callbacks, on_begin_headers_callback); nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header_callback); nghttp2_session_callbacks_set_before_frame_send_callback( callbacks, before_frame_send_callback); nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, on_frame_send_callback); nghttp2_session_callbacks_set_on_frame_not_send_callback( callbacks, on_frame_not_send_callback); if (config.padding) { nghttp2_session_callbacks_set_select_padding_callback2( callbacks, select_padding_callback); } nghttp2_session_callbacks_set_rand_callback(callbacks, util::secure_random); std::string prev_scheme; std::string prev_host; uint16_t prev_port = 0; int failures = 0; int data_fd = -1; nghttp2_data_provider2 data_prd; struct stat data_stat{}; if (!config.datafile.empty()) { if (config.datafile == "-") { if (fstat(0, &data_stat) == 0 && (data_stat.st_mode & S_IFMT) == S_IFREG) { // use STDIN if it is a regular file data_fd = 0; } else { // copy the contents of STDIN to a temporary file char tempfn[] = "/tmp/nghttp.temp.XXXXXX"; data_fd = mkstemp(tempfn); if (data_fd == -1) { std::cerr << "[ERROR] Could not create a temporary file in /tmp" << std::endl; return 1; } if (unlink(tempfn) != 0) { std::cerr << "[WARNING] failed to unlink temporary file:" << tempfn << std::endl; } while (1) { std::array buf; ssize_t rret, wret; while ((rret = read(0, buf.data(), buf.size())) == -1 && errno == EINTR) ; if (rret == 0) break; if (rret == -1) { std::cerr << "[ERROR] I/O error while reading from STDIN" << std::endl; return 1; } while ((wret = write(data_fd, buf.data(), as_unsigned(rret))) == -1 && errno == EINTR) ; if (wret != rret) { std::cerr << "[ERROR] I/O error while writing to temporary file" << std::endl; return 1; } } if (fstat(data_fd, &data_stat) == -1) { close(data_fd); std::cerr << "[ERROR] Could not stat temporary file" << std::endl; return 1; } } } else { data_fd = open(config.datafile.c_str(), O_RDONLY | O_BINARY); if (data_fd == -1) { std::cerr << "[ERROR] Could not open file " << config.datafile << std::endl; return 1; } if (fstat(data_fd, &data_stat) == -1) { close(data_fd); std::cerr << "[ERROR] Could not stat file " << config.datafile << std::endl; return 1; } } data_prd.source.fd = data_fd; data_prd.read_callback = file_read_callback; } std::vector< std::tuple> requests; size_t next_extpri_idx = 0; for (int i = 0; i < n; ++i) { urlparse_url u; auto uri = strip_fragment(uris[i]); if (urlparse_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { ++next_extpri_idx; std::cerr << "[ERROR] Could not parse URI " << uri << std::endl; continue; } if (!util::has_uri_field(u, URLPARSE_SCHEMA)) { ++next_extpri_idx; std::cerr << "[ERROR] URI " << uri << " does not have scheme part" << std::endl; continue; } auto port = util::has_uri_field(u, URLPARSE_PORT) ? u.port : util::get_default_port(uri.c_str(), u); auto host = decode_host(util::get_uri_field(uri.c_str(), u, URLPARSE_HOST)); if (!util::fieldeq(uri.c_str(), u, URLPARSE_SCHEMA, prev_scheme.c_str()) || host != prev_host || port != prev_port) { if (!requests.empty()) { if (communicate(prev_scheme, prev_host, prev_port, std::move(requests), callbacks) != 0) { ++failures; } requests.clear(); } prev_scheme = util::get_uri_field(uri.c_str(), u, URLPARSE_SCHEMA); prev_host = std::move(host); prev_port = port; } requests.emplace_back(uri, data_fd == -1 ? nullptr : &data_prd, data_stat.st_size, config.extpris[next_extpri_idx++]); } if (!requests.empty()) { if (communicate(prev_scheme, prev_host, prev_port, std::move(requests), callbacks) != 0) { ++failures; } } return failures; } } // namespace namespace { void print_version(std::ostream &out) { out << "nghttp nghttp2/" NGHTTP2_VERSION << std::endl; } } // namespace namespace { void print_usage(std::ostream &out) { out << R"(Usage: nghttp [OPTIONS]... ... HTTP/2 client)" << std::endl; } } // namespace namespace { void print_help(std::ostream &out) { print_usage(out); out << R"( Specify URI to access. Options: -v, --verbose Print debug information such as reception and transmission of frames and name/value pairs. Specifying this option multiple times increases verbosity. -n, --null-out Discard downloaded data. -O, --remote-name Save download data in the current directory. The filename is derived from URI. If URI ends with '/', 'index.html' is used as a filename. Not implemented yet. -t, --timeout= Timeout each request after . Set 0 to disable timeout. -w, --window-bits= Sets the stream level initial window size to 2**-1. -W, --connection-window-bits= Sets the connection level initial window size to 2**-1. -a, --get-assets Download assets such as stylesheets, images and script files linked from the downloaded resource. Only links whose origins are the same with the linking resource will be downloaded. nghttp prioritizes resources using HTTP/2 dependency based priority. The priority order, from highest to lowest, is html itself, css, javascript and images. -s, --stat Print statistics. -H, --header=
Add a header to the requests. Example: -H':method: PUT' --trailer=
Add a trailer header to the requests.
must not include pseudo header field (header field name starting with ':'). To send trailer, one must use -d option to send request body. Example: --trailer 'foo: bar'. --cert= Use the specified client certificate file. The file must be in PEM format. --key= Use the client private key file. The file must be in PEM format. -d, --data= Post FILE to server. If '-' is given, data will be read from stdin. -m, --multiply= Request each URI times. By default, same URI is not requested twice. This option disables it too. -u, --upgrade Perform HTTP Upgrade for HTTP/2. This option is ignored if the request URI has https scheme. If -d is used, the HTTP upgrade request is performed with OPTIONS method. --extpri= Sets RFC 9218 priority of given URI. must be the wire format of priority header field (e.g., "u=3,i"). This option can be used multiple times, and N-th --extpri option sets priority of N-th URI in the command line. If the number of this option is less than the number of URI, the last option value is repeated. If there is no --extpri option, urgency is 3, and incremental is false. -M, --peer-max-concurrent-streams= Use as SETTINGS_MAX_CONCURRENT_STREAMS value of remote endpoint as if it is received in SETTINGS frame. Default: 100 -c, --header-table-size= Specify decoder header table size. If this option is used multiple times, and the minimum value among the given values except for last one is strictly less than the last value, that minimum value is set in SETTINGS frame payload before the last value, to simulate multiple header table size change. --encoder-header-table-size= Specify encoder header table size. The decoder (server) specifies the maximum dynamic table size it accepts. Then the negotiated dynamic table size is the minimum of this option value and the value which server specified. -b, --padding= Add at most bytes to a frame payload as padding. Specify 0 to disable padding. -r, --har= Output HTTP transactions in HAR format. If '-' is given, data is written to stdout. --color Force colored log output. --continuation Send large header to test CONTINUATION. --no-content-length Don't send content-length header field. --hexdump Display the incoming traffic in hexadecimal (Canonical hex+ASCII display). If SSL/TLS is used, decrypted data are used. --no-push Disable server push. --max-concurrent-streams= The number of concurrent pushed streams this client accepts. --expect-continue Perform an Expect/Continue handshake: wait to send DATA (up to a short timeout) until the server sends a 100 Continue interim response. This option is ignored unless combined with the -d option. -y, --no-verify-peer Suppress warning on server certificate verification failure. --ktls Enable ktls. --version Display version information and exit. -h, --help Display this help and exit. -- The argument is an integer and an optional unit (e.g., 10K is 10 * 1024). Units are K, M and G (powers of 1024). The argument is an integer and an optional unit (e.g., 1s is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms (hours, minutes, seconds and milliseconds, respectively). If a unit is omitted, a second is used as unit.)" << std::endl; } } // namespace int main(int argc, char **argv) { bool color = false; while (1) { static int flag = 0; constexpr static option long_options[] = { {"verbose", no_argument, nullptr, 'v'}, {"null-out", no_argument, nullptr, 'n'}, {"remote-name", no_argument, nullptr, 'O'}, {"timeout", required_argument, nullptr, 't'}, {"window-bits", required_argument, nullptr, 'w'}, {"connection-window-bits", required_argument, nullptr, 'W'}, {"get-assets", no_argument, nullptr, 'a'}, {"stat", no_argument, nullptr, 's'}, {"help", no_argument, nullptr, 'h'}, {"header", required_argument, nullptr, 'H'}, {"data", required_argument, nullptr, 'd'}, {"multiply", required_argument, nullptr, 'm'}, {"upgrade", no_argument, nullptr, 'u'}, {"weight", required_argument, nullptr, 'p'}, {"peer-max-concurrent-streams", required_argument, nullptr, 'M'}, {"header-table-size", required_argument, nullptr, 'c'}, {"padding", required_argument, nullptr, 'b'}, {"har", required_argument, nullptr, 'r'}, {"no-verify-peer", no_argument, nullptr, 'y'}, {"cert", required_argument, &flag, 1}, {"key", required_argument, &flag, 2}, {"color", no_argument, &flag, 3}, {"continuation", no_argument, &flag, 4}, {"version", no_argument, &flag, 5}, {"no-content-length", no_argument, &flag, 6}, {"no-dep", no_argument, &flag, 7}, {"trailer", required_argument, &flag, 9}, {"hexdump", no_argument, &flag, 10}, {"no-push", no_argument, &flag, 11}, {"max-concurrent-streams", required_argument, &flag, 12}, {"expect-continue", no_argument, &flag, 13}, {"encoder-header-table-size", required_argument, &flag, 14}, {"ktls", no_argument, &flag, 15}, {"no-rfc7540-pri", no_argument, &flag, 16}, {"extpri", required_argument, &flag, 17}, {nullptr, 0, nullptr, 0}}; int option_index = 0; int c = getopt_long(argc, argv, "M:Oab:c:d:m:np:r:hH:vst:uw:yW:", long_options, &option_index); if (c == -1) { break; } switch (c) { case 'M': { // peer-max-concurrent-streams option auto n = util::parse_uint(optarg); if (!n) { std::cerr << "-M: Bad option value: " << optarg << std::endl; exit(EXIT_FAILURE); } config.peer_max_concurrent_streams = static_cast(*n); break; } case 'O': config.remote_name = true; break; case 'h': print_help(std::cout); exit(EXIT_SUCCESS); case 'b': { auto n = util::parse_uint(optarg); if (!n) { std::cerr << "-b: Bad option value: " << optarg << std::endl; exit(EXIT_FAILURE); } config.padding = static_cast(*n); break; } case 'n': config.null_out = true; break; case 'p': std::cerr << "[WARNING]: --weight option has been deprecated." << std::endl; break; case 'r': #ifdef HAVE_JANSSON config.harfile = optarg; #else // !defined(HAVE_JANSSON) std::cerr << "[WARNING]: -r, --har option is ignored because\n" << "the binary was not compiled with libjansson." << std::endl; #endif // !defined(HAVE_JANSSON) break; case 'v': ++config.verbose; break; case 't': { auto d = util::parse_duration_with_unit(optarg); if (!d) { std::cerr << "-t: bad timeout value: " << optarg << std::endl; exit(EXIT_FAILURE); } config.timeout = *d; break; } case 'u': config.upgrade = true; break; case 'w': case 'W': { auto n = util::parse_uint(optarg); if (!n || n > 30) { std::cerr << "-" << static_cast(c) << ": specify the integer in the range [0, 30], inclusive" << std::endl; exit(EXIT_FAILURE); } if (c == 'w') { config.window_bits = static_cast(*n); } else { config.connection_window_bits = static_cast(*n); } break; } case 'H': { char *header = optarg; // Skip first possible ':' in the header name auto name_end = strchr(optarg + 1, ':'); if (!name_end || (header[0] == ':' && header + 1 == name_end)) { std::cerr << "-H: invalid header: " << optarg << std::endl; exit(EXIT_FAILURE); } *name_end = 0; auto value = name_end + 1; while (isspace(*value)) { value++; } if (*value == 0) { // This could also be a valid case for suppressing a header // similar to curl std::cerr << "-H: invalid header - value missing: " << optarg << std::endl; exit(EXIT_FAILURE); } util::tolower(header, name_end, header); config.headers.emplace_back(header, value, false); break; } case 'a': #ifdef HAVE_LIBXML2 config.get_assets = true; #else // !defined(HAVE_LIBXML2) std::cerr << "[WARNING]: -a, --get-assets option is ignored because\n" << "the binary was not compiled with libxml2." << std::endl; #endif // !defined(HAVE_LIBXML2) break; case 's': config.stat = true; break; case 'd': config.datafile = optarg; break; case 'm': { auto n = util::parse_uint(optarg); if (!n) { std::cerr << "-m: Bad option value: " << optarg << std::endl; exit(EXIT_FAILURE); } config.multiply = static_cast(*n); break; } case 'c': { auto n = util::parse_uint_with_unit(optarg); if (!n) { std::cerr << "-c: Bad option value: " << optarg << std::endl; exit(EXIT_FAILURE); } if (n > std::numeric_limits::max()) { std::cerr << "-c: Value too large. It should be less than or equal to " << std::numeric_limits::max() << std::endl; exit(EXIT_FAILURE); } config.header_table_size = *n; config.min_header_table_size = std::min(config.min_header_table_size, *n); break; } case 'y': config.verify_peer = false; break; case '?': util::show_candidates(argv[optind - 1], long_options); exit(EXIT_FAILURE); case 0: switch (flag) { case 1: // cert option config.certfile = optarg; break; case 2: // key option config.keyfile = optarg; break; case 3: // color option color = true; break; case 4: // continuation option config.continuation = true; break; case 5: // version option print_version(std::cout); exit(EXIT_SUCCESS); case 6: // no-content-length option config.no_content_length = true; break; case 7: // no-dep option std::cerr << "[WARNING]: --no-dep option has been deprecated." << std::endl; break; case 9: { // trailer option auto header = optarg; auto name_end = strchr(optarg, ':'); if (!name_end) { std::cerr << "--trailer: invalid header: " << optarg << std::endl; exit(EXIT_FAILURE); } *name_end = 0; auto value = name_end + 1; while (isspace(*value)) { value++; } if (*value == 0) { // This could also be a valid case for suppressing a header // similar to curl std::cerr << "--trailer: invalid header - value missing: " << optarg << std::endl; exit(EXIT_FAILURE); } util::tolower(header, name_end, header); config.trailer.emplace_back(header, value, false); break; } case 10: // hexdump option config.hexdump = true; break; case 11: // no-push option config.no_push = true; break; case 12: { // max-concurrent-streams option auto n = util::parse_uint(optarg); if (!n) { std::cerr << "--max-concurrent-streams: Bad option value: " << optarg << std::endl; exit(EXIT_FAILURE); } config.max_concurrent_streams = static_cast(*n); break; } case 13: // expect-continue option config.expect_continue = true; break; case 14: { // encoder-header-table-size option auto n = util::parse_uint_with_unit(optarg); if (!n) { std::cerr << "--encoder-header-table-size: Bad option value: " << optarg << std::endl; exit(EXIT_FAILURE); } if (n > std::numeric_limits::max()) { std::cerr << "--encoder-header-table-size: Value too large. It " "should be less than or equal to " << std::numeric_limits::max() << std::endl; exit(EXIT_FAILURE); } config.encoder_header_table_size = *n; break; } case 15: // ktls option config.ktls = true; break; case 16: // no-rfc7540-pri option std::cerr << "[WARNING]: --no-rfc7540-pri option has been deprecated." << std::endl; break; case 17: { // extpri option nghttp2_extpri pri{ .urgency = NGHTTP2_EXTPRI_DEFAULT_URGENCY, }; if (nghttp2_extpri_parse_priority( &pri, reinterpret_cast(optarg), strlen(optarg)) != 0) { std::cerr << "--extpri: Bad option value: " << optarg << std::endl; exit(EXIT_FAILURE); } config.extpris.emplace_back(std::move(pri)); break; } } break; default: break; } } nghttp2_extpri extpri_to_fill{ .urgency = NGHTTP2_EXTPRI_DEFAULT_URGENCY, }; if (!config.extpris.empty()) { extpri_to_fill = config.extpris.back(); } config.extpris.insert(std::ranges::end(config.extpris), static_cast(argc - optind), extpri_to_fill); // Find scheme overridden by extra header fields. auto scheme_it = std::ranges::find_if( config.headers, [](const Header &nv) { return nv.name == ":scheme"; }); if (scheme_it != std::ranges::end(config.headers)) { config.scheme_override = (*scheme_it).value; } // Find host and port overridden by extra header fields. auto authority_it = std::ranges::find_if( config.headers, [](const Header &nv) { return nv.name == ":authority"; }); if (authority_it == std::ranges::end(config.headers)) { authority_it = std::ranges::find_if( config.headers, [](const Header &nv) { return nv.name == "host"; }); } if (authority_it != std::ranges::end(config.headers)) { // authority_it may looks like "host:port". auto uri = "https://" + (*authority_it).value; urlparse_url u; if (urlparse_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { std::cerr << "[ERROR] Could not parse authority in " << (*authority_it).name << ": " << (*authority_it).value << std::endl; exit(EXIT_FAILURE); } config.host_override = util::get_uri_field(uri.c_str(), u, URLPARSE_HOST); if (util::has_uri_field(u, URLPARSE_PORT)) { config.port_override = u.port; } } set_color_output(color || isatty(fileno(stdout))); nghttp2_option_set_peer_max_concurrent_streams( config.http2_option, static_cast(config.peer_max_concurrent_streams)); if (config.encoder_header_table_size != -1) { nghttp2_option_set_max_deflate_dynamic_table_size( config.http2_option, static_cast(config.encoder_header_table_size)); } struct sigaction act{}; act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, nullptr); reset_timer(); return run(argv + optind, argc - optind); } } // namespace nghttp2 int main(int argc, char **argv) { return nghttp2::run_app(nghttp2::main, argc, argv); } nghttp2-1.68.0/src/PaxHeaders/h2load_quic.cc0000644000000000000000000000013215077107270015550 xustar0030 mtime=1761382072.986444171 30 atime=1761382106.223310343 30 ctime=1761382109.219300037 nghttp2-1.68.0/src/h2load_quic.cc0000644000175100017510000005307715077107270016154 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2019 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "h2load_quic.h" #include #include #if defined(HAVE_LIBNGTCP2_CRYPTO_QUICTLS) || \ defined(HAVE_LIBNGTCP2_CRYPTO_LIBRESSL) # include #endif // defined(HAVE_LIBNGTCP2_CRYPTO_QUICTLS) || // defined(HAVE_LIBNGTCP2_CRYPTO_LIBRESSL) #ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL # include #endif // defined(HAVE_LIBNGTCP2_CRYPTO_BORINGSSL) #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include "h2load_http3_session.h" namespace h2load { namespace { int handshake_completed(ngtcp2_conn *conn, void *user_data) { auto c = static_cast(user_data); if (c->quic_handshake_completed() != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Client::quic_handshake_completed() { return connection_made(); } namespace { int recv_stream_data(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id, uint64_t offset, const uint8_t *data, size_t datalen, void *user_data, void *stream_user_data) { auto c = static_cast(user_data); if (c->quic_recv_stream_data(flags, stream_id, data, datalen) != 0) { // TODO Better to do this gracefully rather than // NGTCP2_ERR_CALLBACK_FAILURE. Perhaps, call // ngtcp2_conn_write_application_close() ? return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Client::quic_recv_stream_data(uint32_t flags, int64_t stream_id, const uint8_t *data, size_t datalen) { if (worker->current_phase == Phase::MAIN_DURATION) { worker->stats.bytes_total += datalen; } auto s = static_cast(session.get()); auto nconsumed = s->read_stream(flags, stream_id, data, datalen); if (nconsumed == -1) { return -1; } ngtcp2_conn_extend_max_stream_offset(quic.conn, stream_id, static_cast(nconsumed)); ngtcp2_conn_extend_max_offset(quic.conn, static_cast(nconsumed)); return 0; } namespace { int acked_stream_data_offset(ngtcp2_conn *conn, int64_t stream_id, uint64_t offset, uint64_t datalen, void *user_data, void *stream_user_data) { auto c = static_cast(user_data); if (c->quic_acked_stream_data_offset(stream_id, datalen) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Client::quic_acked_stream_data_offset(int64_t stream_id, size_t datalen) { auto s = static_cast(session.get()); if (s->add_ack_offset(stream_id, datalen) != 0) { return -1; } return 0; } namespace { int stream_close(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id, uint64_t app_error_code, void *user_data, void *stream_user_data) { auto c = static_cast(user_data); if (!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) { app_error_code = NGHTTP3_H3_NO_ERROR; } if (c->quic_stream_close(stream_id, app_error_code) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Client::quic_stream_close(int64_t stream_id, uint64_t app_error_code) { auto s = static_cast(session.get()); if (s->close_stream(stream_id, app_error_code) != 0) { return -1; } return 0; } namespace { int stream_reset(ngtcp2_conn *conn, int64_t stream_id, uint64_t final_size, uint64_t app_error_code, void *user_data, void *stream_user_data) { auto c = static_cast(user_data); if (c->quic_stream_reset(stream_id, app_error_code) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Client::quic_stream_reset(int64_t stream_id, uint64_t app_error_code) { auto s = static_cast(session.get()); if (s->shutdown_stream_read(stream_id) != 0) { return -1; } return 0; } namespace { int stream_stop_sending(ngtcp2_conn *conn, int64_t stream_id, uint64_t app_error_code, void *user_data, void *stream_user_data) { auto c = static_cast(user_data); if (c->quic_stream_stop_sending(stream_id, app_error_code) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Client::quic_stream_stop_sending(int64_t stream_id, uint64_t app_error_code) { auto s = static_cast(session.get()); if (s->shutdown_stream_read(stream_id) != 0) { return -1; } return 0; } namespace { int extend_max_local_streams_bidi(ngtcp2_conn *conn, uint64_t max_streams, void *user_data) { auto c = static_cast(user_data); if (c->quic_extend_max_local_streams() != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Client::quic_extend_max_local_streams() { auto s = static_cast(session.get()); if (s->extend_max_local_streams() != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } namespace { int extend_max_stream_data(ngtcp2_conn *conn, int64_t stream_id, uint64_t max_data, void *user_data, void *stream_user_data) { auto c = static_cast(user_data); if (c->quic_extend_max_stream_data(stream_id) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Client::quic_extend_max_stream_data(int64_t stream_id) { auto s = static_cast(session.get()); if (s->unblock_stream(stream_id) != 0) { return -1; } return 0; } namespace { int get_new_connection_id(ngtcp2_conn *conn, ngtcp2_cid *cid, uint8_t *token, size_t cidlen, void *user_data) { if (RAND_bytes(cid->data, static_cast(cidlen)) != 1) { return NGTCP2_ERR_CALLBACK_FAILURE; } cid->datalen = cidlen; if (RAND_bytes(token, NGTCP2_STATELESS_RESET_TOKENLEN) != 1) { return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } } // namespace namespace { void debug_log_printf(void *user_data, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); } } // namespace namespace { int generate_cid(ngtcp2_cid &dest) { dest.datalen = 8; if (RAND_bytes(dest.data, static_cast( dest.datalen)) != 1) { return -1; } return 0; } } // namespace namespace { ngtcp2_tstamp quic_timestamp() { return static_cast( std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()) .count()); } } // namespace // qlog write callback -- excerpted from ngtcp2/examples/client_base.cc namespace { void qlog_write_cb(void *user_data, uint32_t flags, const void *data, size_t datalen) { auto c = static_cast(user_data); c->quic_write_qlog(data, datalen); } } // namespace void Client::quic_write_qlog(const void *data, size_t datalen) { assert(quic.qlog_file != nullptr); fwrite(data, 1, datalen, quic.qlog_file); } namespace { void rand(uint8_t *dest, size_t destlen, const ngtcp2_rand_ctx *rand_ctx) { auto rv = RAND_bytes(dest, static_cast(destlen)); if (rv != 1) { assert(0); abort(); } } } // namespace namespace { int recv_rx_key(ngtcp2_conn *conn, ngtcp2_encryption_level level, void *user_data) { if (level != NGTCP2_ENCRYPTION_LEVEL_1RTT) { return 0; } auto c = static_cast(user_data); if (c->quic_make_http3_session() != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Client::quic_make_http3_session() { auto s = std::make_unique(this); if (s->init_conn() == -1) { return -1; } session = std::move(s); return 0; } namespace { ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) { auto c = static_cast(conn_ref->user_data); return c->quic.conn; } } // namespace int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen, const sockaddr *remote_addr, socklen_t remote_addrlen) { int rv; if (!ssl) { ssl = SSL_new(worker->ssl_ctx); quic.conn_ref.get_conn = get_conn; quic.conn_ref.user_data = this; SSL_set_app_data(ssl, &quic.conn_ref); SSL_set_connect_state(ssl); #if OPENSSL_3_5_0_API if (ngtcp2_crypto_ossl_configure_client_session(ssl) != 0) { std::cerr << "ngtcp2_crypto_ossl_configure_client_session failed" << std::endl; return -1; } rv = ngtcp2_crypto_ossl_ctx_new(&quic.ossl_ctx, ssl); if (rv != 0) { std::cerr << "ngtcp2_crypto_ossl_ctx_new failed with error code " << rv << std::endl; return -1; } #else // !OPENSSL_3_5_0_API SSL_set_quic_use_legacy_codepoint(ssl, 0); #endif // !OPENSSL_3_5_0_API } auto callbacks = ngtcp2_callbacks{ .client_initial = ngtcp2_crypto_client_initial_cb, .recv_crypto_data = ngtcp2_crypto_recv_crypto_data_cb, .handshake_completed = h2load::handshake_completed, .encrypt = ngtcp2_crypto_encrypt_cb, .decrypt = ngtcp2_crypto_decrypt_cb, .hp_mask = ngtcp2_crypto_hp_mask_cb, .recv_stream_data = h2load::recv_stream_data, .acked_stream_data_offset = h2load::acked_stream_data_offset, .stream_close = h2load::stream_close, .recv_retry = ngtcp2_crypto_recv_retry_cb, .extend_max_local_streams_bidi = h2load::extend_max_local_streams_bidi, .rand = h2load::rand, .get_new_connection_id = get_new_connection_id, .update_key = ngtcp2_crypto_update_key_cb, .stream_reset = h2load::stream_reset, .extend_max_stream_data = h2load::extend_max_stream_data, .delete_crypto_aead_ctx = ngtcp2_crypto_delete_crypto_aead_ctx_cb, .delete_crypto_cipher_ctx = ngtcp2_crypto_delete_crypto_cipher_ctx_cb, .get_path_challenge_data = ngtcp2_crypto_get_path_challenge_data_cb, .stream_stop_sending = h2load::stream_stop_sending, .recv_rx_key = h2load::recv_rx_key, }; ngtcp2_cid scid, dcid; if (generate_cid(scid) != 0) { return -1; } if (generate_cid(dcid) != 0) { return -1; } auto config = worker->config; ngtcp2_settings settings; ngtcp2_settings_default(&settings); if (config->verbose) { settings.log_printf = debug_log_printf; } settings.initial_ts = quic_timestamp(); settings.rand_ctx.native_handle = &worker->randgen; if (!config->qlog_file_base.empty()) { assert(quic.qlog_file == nullptr); auto path = config->qlog_file_base; path += '.'; path += util::utos(worker->id); path += '.'; path += util::utos(id); path += ".sqlog"; quic.qlog_file = fopen(path.c_str(), "w"); if (quic.qlog_file == nullptr) { std::cerr << "Failed to open a qlog file: " << path << std::endl; return -1; } settings.qlog_write = qlog_write_cb; } if (config->max_udp_payload_size) { settings.max_tx_udp_payload_size = config->max_udp_payload_size; settings.no_tx_udp_payload_size_shaping = 1; } ngtcp2_transport_params params; ngtcp2_transport_params_default(¶ms); auto max_stream_data = static_cast( std::min((1 << 26) - 1, (1 << config->window_bits) - 1)); params.initial_max_stream_data_bidi_local = max_stream_data; params.initial_max_stream_data_uni = max_stream_data; params.initial_max_data = (1 << config->connection_window_bits) - 1; params.initial_max_streams_bidi = 0; params.initial_max_streams_uni = 100; params.max_idle_timeout = 30 * NGTCP2_SECONDS; auto path = ngtcp2_path{ { const_cast(local_addr), local_addrlen, }, { const_cast(remote_addr), remote_addrlen, }, }; assert(config->alpn_list.size()); uint32_t quic_version; if (config->alpn_list[0] == NGHTTP3_ALPN_H3) { quic_version = NGTCP2_PROTO_VER_V1; } else { quic_version = NGTCP2_PROTO_VER_MIN; } rv = ngtcp2_conn_client_new(&quic.conn, &dcid, &scid, &path, quic_version, &callbacks, &settings, ¶ms, nullptr, this); if (rv != 0) { return -1; } #if OPENSSL_3_5_0_API ngtcp2_conn_set_tls_native_handle(quic.conn, quic.ossl_ctx); #else // !OPENSSL_3_5_0_API ngtcp2_conn_set_tls_native_handle(quic.conn, ssl); #endif // !OPENSSL_3_5_0_API return 0; } void Client::quic_free() { #if OPENSSL_3_5_0_API ngtcp2_crypto_ossl_ctx_del(quic.ossl_ctx); #endif // OPENSSL_3_5_0_API ngtcp2_conn_del(quic.conn); if (quic.qlog_file != nullptr) { fclose(quic.qlog_file); quic.qlog_file = nullptr; } } void Client::quic_close_connection() { if (!quic.conn) { return; } std::array buf; ngtcp2_path_storage ps; ngtcp2_path_storage_zero(&ps); auto nwrite = ngtcp2_conn_write_connection_close( quic.conn, &ps.path, nullptr, buf.data(), buf.size(), &quic.last_error, quic_timestamp()); if (nwrite <= 0) { return; } write_udp(reinterpret_cast(ps.path.remote.addr), ps.path.remote.addrlen, {buf.data(), static_cast(nwrite)}, as_unsigned(nwrite)); } int Client::quic_write_client_handshake(ngtcp2_encryption_level level, const uint8_t *data, size_t datalen) { int rv; assert(level < 2); rv = ngtcp2_conn_submit_crypto_data(quic.conn, level, data, datalen); if (rv != 0) { std::cerr << "ngtcp2_conn_submit_crypto_data: " << ngtcp2_strerror(rv) << std::endl; return -1; } return 0; } void quic_pkt_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto c = static_cast(w->data); if (c->quic_pkt_timeout() != 0) { c->fail(); c->worker->free_client(c); delete c; return; } } int Client::quic_pkt_timeout() { int rv; auto now = quic_timestamp(); rv = ngtcp2_conn_handle_expiry(quic.conn, now); if (rv != 0) { ngtcp2_ccerr_set_liberr(&quic.last_error, rv, nullptr, 0); return -1; } signal_write(); return 0; } void Client::quic_restart_pkt_timer() { auto expiry = ngtcp2_conn_get_expiry(quic.conn); auto now = quic_timestamp(); auto t = expiry > now ? static_cast(expiry - now) / NGTCP2_SECONDS : 1e-9; quic.pkt_timer.repeat = t; ev_timer_again(worker->loop, &quic.pkt_timer); } int Client::read_quic() { std::array buf; sockaddr_union su; int rv; size_t pktcnt = 0; ngtcp2_pkt_info pi; iovec msg_iov{ .iov_base = buf.data(), .iov_len = buf.size(), }; uint8_t msg_ctrl[CMSG_SPACE(sizeof(int))]; msghdr msg{ .msg_name = &su, .msg_iov = &msg_iov, .msg_iovlen = 1, .msg_control = msg_ctrl, }; auto ts = quic_timestamp(); for (;;) { msg.msg_namelen = sizeof(su); msg.msg_controllen = sizeof(msg_ctrl); auto nread = recvmsg(fd, &msg, 0); if (nread == -1) { return 0; } auto gso_size = util::msghdr_get_udp_gro(&msg); if (gso_size == 0) { gso_size = static_cast(nread); } assert(quic.conn); if (gso_size) { worker->stats.udp_dgram_recv += (as_unsigned(nread) + gso_size - 1) / gso_size; } else { ++worker->stats.udp_dgram_recv; } auto path = ngtcp2_path{ { &local_addr.su.sa, local_addr.len, }, { &su.sa, msg.msg_namelen, }, }; auto data = buf.data(); for (;;) { auto datalen = std::min(static_cast(nread), gso_size); ++pktcnt; rv = ngtcp2_conn_read_pkt(quic.conn, &path, &pi, data, datalen, ts); if (rv != 0) { if (!quic.last_error.error_code) { if (rv == NGTCP2_ERR_CRYPTO) { ngtcp2_ccerr_set_tls_alert(&quic.last_error, ngtcp2_conn_get_tls_alert(quic.conn), nullptr, 0); } else { ngtcp2_ccerr_set_liberr(&quic.last_error, rv, nullptr, 0); } } return -1; } nread -= datalen; if (nread == 0) { break; } data += datalen; } if (pktcnt >= 100) { break; } } return 0; } namespace { ngtcp2_ssize write_pkt(ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_tstamp ts, void *user_data) { auto c = static_cast(user_data); return c->write_quic_pkt(path, pi, dest, destlen, ts); } } // namespace ngtcp2_ssize Client::write_quic_pkt(ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_tstamp ts) { std::array vec; auto s = static_cast(session.get()); for (;;) { int64_t stream_id = -1; int fin = 0; ssize_t sveccnt = 0; if (session && ngtcp2_conn_get_max_data_left(quic.conn)) { sveccnt = s->write_stream(stream_id, fin, vec.data(), vec.size()); if (sveccnt == -1) { return NGTCP2_ERR_CALLBACK_FAILURE; } } ngtcp2_ssize ndatalen; auto v = vec.data(); auto vcnt = static_cast(sveccnt); uint32_t flags = NGTCP2_WRITE_STREAM_FLAG_MORE | NGTCP2_WRITE_STREAM_FLAG_PADDING; if (fin) { flags |= NGTCP2_WRITE_STREAM_FLAG_FIN; } auto nwrite = ngtcp2_conn_writev_stream( quic.conn, path, nullptr, dest, destlen, &ndatalen, flags, stream_id, reinterpret_cast(v), vcnt, ts); if (nwrite < 0) { switch (nwrite) { case NGTCP2_ERR_STREAM_DATA_BLOCKED: assert(ndatalen == -1); s->block_stream(stream_id); continue; case NGTCP2_ERR_STREAM_SHUT_WR: assert(ndatalen == -1); s->shutdown_stream_write(stream_id); continue; case NGTCP2_ERR_WRITE_MORE: assert(ndatalen >= 0); if (s->add_write_offset(stream_id, as_unsigned(ndatalen)) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } continue; } ngtcp2_ccerr_set_liberr(&quic.last_error, static_cast(nwrite), nullptr, 0); return NGTCP2_ERR_CALLBACK_FAILURE; } if (ndatalen >= 0 && s->add_write_offset(stream_id, as_unsigned(ndatalen)) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } return nwrite; } } int Client::write_quic() { int rv; ev_io_stop(worker->loop, &wev); if (quic.close_requested) { return -1; } if (quic.tx.send_blocked) { rv = send_blocked_packet(); if (rv != 0) { return -1; } if (quic.tx.send_blocked) { return 0; } } auto txbuf = std::span{quic.tx.data.get(), QUIC_TX_DATALEN}; ngtcp2_path_storage ps; size_t gso_size = 0; ngtcp2_path_storage_zero(&ps); auto nwrite = ngtcp2_conn_write_aggregate_pkt( quic.conn, &ps.path, nullptr, txbuf.data(), txbuf.size(), &gso_size, h2load::write_pkt, quic_timestamp()); if (nwrite < 0) { return -1; } quic_restart_pkt_timer(); if (nwrite == 0) { return 0; } write_udp_or_blocked(ps.path, txbuf.first(static_cast(nwrite)), gso_size); return 0; } void Client::write_udp_or_blocked(const ngtcp2_path &path, std::span data, size_t gso_size) { auto rest = write_udp(path.remote.addr, path.remote.addrlen, data, gso_size); if (!rest.empty()) { on_send_blocked(path.remote, data, gso_size); } } void Client::on_send_blocked(const ngtcp2_addr &remote_addr, std::span data, size_t gso_size) { assert(!quic.tx.send_blocked); quic.tx.send_blocked = true; auto &p = quic.tx.blocked; memcpy(&p.remote_addr.su, remote_addr.addr, remote_addr.addrlen); p.remote_addr.len = remote_addr.addrlen; p.data = data; p.gso_size = gso_size; signal_write(); } int Client::send_blocked_packet() { assert(quic.tx.send_blocked); auto &p = quic.tx.blocked; auto rest = write_udp(&p.remote_addr.su.sa, p.remote_addr.len, p.data, p.gso_size); if (!rest.empty()) { p.data = rest; signal_write(); return 0; } quic.tx.send_blocked = false; return 0; } } // namespace h2load nghttp2-1.68.0/src/PaxHeaders/shrpx_dns_tracker.h0000644000000000000000000000013115077107270016742 xustar0030 mtime=1761382072.992444143 30 atime=1761382106.170310577 29 ctime=1761382109.16630019 nghttp2-1.68.0/src/shrpx_dns_tracker.h0000644000175100017510000001020715077107270017333 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_DNS_TRACKER_H #define SHRPX_DNS_TRACKER_H #include "shrpx.h" #include #include #include "shrpx_dual_dns_resolver.h" using namespace nghttp2; namespace shrpx { struct DNSQuery { DNSQuery(std::string_view host, CompleteCb cb) : host(std::move(host)), cb(std::move(cb)), dlnext(nullptr), dlprev(nullptr), status(DNSResolverStatus::IDLE), in_qlist(false) {} // Host name we lookup for. std::string_view host; // Callback function called when name lookup finished. This // callback is not called if name lookup finishes within // DNSTracker::resolve(). CompleteCb cb; DNSQuery *dlnext, *dlprev; DNSResolverStatus status; // true if this object is in linked list ResolverEntry::qlist. bool in_qlist; }; struct ResolverEntry { // Host name this entry lookups for. ImmutableString host; // DNS resolver. Only non-nullptr if status is // DNSResolverStatus::RUNNING. std::unique_ptr resolv; // DNSQuery interested in this name lookup result. The result is // notified to them all. DList qlist; // Use the same enum with DNSResolverStatus DNSResolverStatus status; // result and its expiry time Address result; // time point when cached result expires. std::chrono::steady_clock::time_point expiry; }; class DNSTracker { public: DNSTracker(struct ev_loop *loop, int family); ~DNSTracker(); // Lookups host name described in |dnsq|. If name lookup finishes // within this function (either it came from /etc/hosts, host name // is numeric, lookup result is cached, etc), it returns // DNSResolverStatus::OK or DNSResolverStatus::ERROR. If lookup is // successful, DNSResolverStatus::OK is returned, and |result| is // filled. If lookup failed, DNSResolverStatus::ERROR is returned. // If name lookup is being done background, it returns // DNSResolverStatus::RUNNING. Its completion is notified by // calling dnsq->cb. DNSResolverStatus resolve(Address *result, DNSQuery *dnsq); // Cancels name lookup requested by |dnsq|. void cancel(DNSQuery *dnsq); // Removes expired entries from ents_. void gc(); // Starts GC timer. void start_gc_timer(); private: ResolverEntry make_entry(std::unique_ptr resolv, ImmutableString host, DNSResolverStatus status, const Address *result); void update_entry(ResolverEntry &ent, std::unique_ptr resolv, DNSResolverStatus status, const Address *result); void add_to_qlist(ResolverEntry &ent, DNSQuery *dnsq); std::unordered_map ents_; // Periodically iterates ents_, and removes expired entries to avoid // excessive use of memory. Since only backend API can potentially // increase memory consumption, interval could be very long. ev_timer gc_timer_; struct ev_loop *loop_; // IP version preference. int family_; }; } // namespace shrpx #endif // !defined(SHRPX_DNS_TRACKER_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_memcached_connection.cc0000644000000000000000000000012715077107270020733 xustar0029 mtime=1761382072.99744412 28 atime=1761382106.1423107 30 ctime=1761382109.138300271 nghttp2-1.68.0/src/shrpx_memcached_connection.cc0000644000175100017510000004660115077107270021326 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_memcached_connection.h" #include #include #include #include "shrpx_memcached_request.h" #include "shrpx_memcached_result.h" #include "shrpx_config.h" #include "shrpx_tls.h" #include "shrpx_log.h" #include "util.h" namespace shrpx { namespace { void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { auto conn = static_cast(w->data); auto mconn = static_cast(conn->data); if (w == &conn->rt && !conn->expired_rt()) { return; } if (LOG_ENABLED(INFO)) { MCLOG(INFO, mconn) << "Time out"; } mconn->disconnect(); } } // namespace namespace { void readcb(struct ev_loop *loop, ev_io *w, int revents) { auto conn = static_cast(w->data); auto mconn = static_cast(conn->data); if (mconn->on_read() != 0) { mconn->reconnect_or_fail(); return; } } } // namespace namespace { void writecb(struct ev_loop *loop, ev_io *w, int revents) { auto conn = static_cast(w->data); auto mconn = static_cast(conn->data); if (mconn->on_write() != 0) { mconn->reconnect_or_fail(); return; } } } // namespace namespace { void connectcb(struct ev_loop *loop, ev_io *w, int revents) { auto conn = static_cast(w->data); auto mconn = static_cast(conn->data); if (mconn->connected() != 0) { mconn->disconnect(); return; } writecb(loop, w, revents); } } // namespace constexpr auto write_timeout = 10_s; constexpr auto read_timeout = 10_s; MemcachedConnection::MemcachedConnection(const Address *addr, struct ev_loop *loop, SSL_CTX *ssl_ctx, const std::string_view &sni_name, MemchunkPool *mcpool, std::mt19937 &gen) : conn_(loop, -1, nullptr, mcpool, write_timeout, read_timeout, {}, {}, connectcb, readcb, timeoutcb, this, 0, 0., Proto::MEMCACHED), do_read_(&MemcachedConnection::noop), do_write_(&MemcachedConnection::noop), sni_name_(sni_name), connect_blocker_( gen, loop, [] {}, [] {}), parse_state_{}, addr_(addr), ssl_ctx_(ssl_ctx), sendsum_(0), try_count_(0), connected_(false) {} MemcachedConnection::~MemcachedConnection() { conn_.disconnect(); } namespace { void clear_request(std::deque> &q) { for (auto &req : q) { if (req->cb) { req->cb(req.get(), MemcachedResult(MemcachedStatusCode::EXT_NETWORK_ERROR)); } } q.clear(); } } // namespace void MemcachedConnection::disconnect() { clear_request(recvq_); clear_request(sendq_); sendbufv_.clear(); sendsum_ = 0; parse_state_ = {}; connected_ = false; conn_.disconnect(); assert(recvbuf_.rleft() == 0); recvbuf_.reset(); do_read_ = do_write_ = &MemcachedConnection::noop; } int MemcachedConnection::initiate_connection() { assert(conn_.fd == -1); if (ssl_ctx_) { auto ssl = tls::create_ssl(ssl_ctx_); if (!ssl) { return -1; } conn_.set_ssl(ssl); conn_.tls.client_session_cache = &tls_session_cache_; } conn_.fd = util::create_nonblock_socket(addr_->su.storage.ss_family); if (conn_.fd == -1) { auto error = errno; MCLOG(WARN, this) << "socket() failed; errno=" << error; return -1; } int rv; rv = connect(conn_.fd, &addr_->su.sa, addr_->len); if (rv != 0 && errno != EINPROGRESS) { auto error = errno; MCLOG(WARN, this) << "connect() failed; errno=" << error; close(conn_.fd); conn_.fd = -1; return -1; } if (ssl_ctx_) { if (!util::numeric_host(sni_name_.data())) { SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name_.data()); } auto session = tls::reuse_tls_session(tls_session_cache_); if (session) { SSL_set_session(conn_.tls.ssl, session); SSL_SESSION_free(session); } conn_.prepare_client_handshake(); } if (LOG_ENABLED(INFO)) { MCLOG(INFO, this) << "Connecting to memcached server"; } ev_io_set(&conn_.wev, conn_.fd, EV_WRITE); ev_io_set(&conn_.rev, conn_.fd, EV_READ); ev_set_cb(&conn_.wev, connectcb); conn_.wlimit.startw(); ev_timer_again(conn_.loop, &conn_.wt); return 0; } int MemcachedConnection::connected() { auto sock_error = util::get_socket_error(conn_.fd); if (sock_error != 0) { MCLOG(WARN, this) << "memcached connect failed; addr=" << util::to_numeric_addr(addr_) << ": errno=" << sock_error; connect_blocker_.on_failure(); conn_.wlimit.stopw(); return -1; } if (LOG_ENABLED(INFO)) { MCLOG(INFO, this) << "connected to memcached server"; } conn_.rlimit.startw(); ev_set_cb(&conn_.wev, writecb); if (conn_.tls.ssl) { conn_.again_rt(); do_read_ = &MemcachedConnection::tls_handshake; do_write_ = &MemcachedConnection::tls_handshake; return 0; } ev_timer_stop(conn_.loop, &conn_.wt); connected_ = true; connect_blocker_.on_success(); do_read_ = &MemcachedConnection::read_clear; do_write_ = &MemcachedConnection::write_clear; return 0; } int MemcachedConnection::on_write() { return do_write_(*this); } int MemcachedConnection::on_read() { return do_read_(*this); } int MemcachedConnection::tls_handshake() { ERR_clear_error(); conn_.last_read = std::chrono::steady_clock::now(); auto rv = conn_.tls_handshake(); if (rv == SHRPX_ERR_INPROGRESS) { return 0; } if (rv < 0) { connect_blocker_.on_failure(); return rv; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "SSL/TLS handshake completed"; } auto &tlsconf = get_config()->tls; if (!tlsconf.insecure && tls::check_cert(conn_.tls.ssl, addr_, sni_name_) != 0) { connect_blocker_.on_failure(); return -1; } ev_timer_stop(conn_.loop, &conn_.rt); ev_timer_stop(conn_.loop, &conn_.wt); connected_ = true; connect_blocker_.on_success(); do_read_ = &MemcachedConnection::read_tls; do_write_ = &MemcachedConnection::write_tls; return on_write(); } int MemcachedConnection::write_tls() { if (!connected_) { return 0; } conn_.last_read = std::chrono::steady_clock::now(); std::array iov; std::array buf; for (; !sendq_.empty();) { auto iovcnt = fill_request_buffer(iov.data(), iov.size()); auto p = std::ranges::begin(buf); for (size_t i = 0; i < iovcnt; ++i) { auto &v = iov[i]; auto n = std::min(static_cast(std::ranges::end(buf) - p), v.iov_len); p = std::ranges::copy_n(static_cast(v.iov_base), as_signed(n), p) .out; if (p == std::ranges::end(buf)) { break; } } auto nwrite = conn_.write_tls(buf.data(), as_unsigned(p - std::ranges::begin(buf))); if (nwrite < 0) { return -1; } if (nwrite == 0) { return 0; } drain_send_queue(as_unsigned(nwrite)); } conn_.wlimit.stopw(); ev_timer_stop(conn_.loop, &conn_.wt); return 0; } int MemcachedConnection::read_tls() { if (!connected_) { return 0; } conn_.last_read = std::chrono::steady_clock::now(); for (;;) { auto nread = conn_.read_tls(recvbuf_.last, recvbuf_.wleft()); if (nread == 0) { return 0; } if (nread < 0) { return -1; } recvbuf_.write(as_unsigned(nread)); if (parse_packet() != 0) { return -1; } } return 0; } int MemcachedConnection::write_clear() { if (!connected_) { return 0; } conn_.last_read = std::chrono::steady_clock::now(); std::array iov; for (; !sendq_.empty();) { auto iovcnt = fill_request_buffer(iov.data(), iov.size()); auto nwrite = conn_.writev_clear(iov.data(), static_cast(iovcnt)); if (nwrite < 0) { return -1; } if (nwrite == 0) { return 0; } drain_send_queue(as_unsigned(nwrite)); } conn_.wlimit.stopw(); ev_timer_stop(conn_.loop, &conn_.wt); return 0; } int MemcachedConnection::read_clear() { if (!connected_) { return 0; } conn_.last_read = std::chrono::steady_clock::now(); for (;;) { auto nread = conn_.read_clear(recvbuf_.last, recvbuf_.wleft()); if (nread == 0) { return 0; } if (nread < 0) { return -1; } recvbuf_.write(as_unsigned(nread)); if (parse_packet() != 0) { return -1; } } return 0; } int MemcachedConnection::parse_packet() { auto in = recvbuf_.pos; for (;;) { auto busy = false; switch (parse_state_.state) { case MemcachedParseState::HEADER24: { if (recvbuf_.last - in < 24) { recvbuf_.drain_reset(as_unsigned(in - recvbuf_.pos)); return 0; } if (recvq_.empty()) { MCLOG(WARN, this) << "Response received, but there is no in-flight request."; return -1; } auto &req = recvq_.front(); if (*in != MEMCACHED_RES_MAGIC) { MCLOG(WARN, this) << "Response has bad magic: " << static_cast(*in); return -1; } ++in; parse_state_.op = static_cast(*in++); parse_state_.keylen = util::get_uint16(in); in += 2; parse_state_.extralen = *in++; // skip 1 byte reserved data type ++in; parse_state_.status_code = static_cast(util::get_uint16(in)); in += 2; parse_state_.totalbody = util::get_uint32(in); in += 4; // skip 4 bytes opaque in += 4; parse_state_.cas = util::get_uint64(in); in += 8; if (req->op != parse_state_.op) { MCLOG(WARN, this) << "opcode in response does not match to the request: want " << static_cast(req->op) << ", got " << static_cast(parse_state_.op); return -1; } if (parse_state_.keylen != 0) { MCLOG(WARN, this) << "zero length keylen expected: got " << parse_state_.keylen; return -1; } if (parse_state_.totalbody > 16_k) { MCLOG(WARN, this) << "totalbody is too large: got " << parse_state_.totalbody; return -1; } if (parse_state_.op == MemcachedOp::GET && parse_state_.status_code == MemcachedStatusCode::NO_ERROR && parse_state_.extralen == 0) { MCLOG(WARN, this) << "response for GET does not have extra"; return -1; } if (parse_state_.totalbody < parse_state_.keylen + parse_state_.extralen) { MCLOG(WARN, this) << "totalbody is too short: totalbody " << parse_state_.totalbody << ", want min " << parse_state_.keylen + parse_state_.extralen; return -1; } if (parse_state_.extralen) { parse_state_.state = MemcachedParseState::EXTRA; parse_state_.read_left = parse_state_.extralen; } else { parse_state_.state = MemcachedParseState::VALUE; parse_state_.read_left = parse_state_.totalbody - parse_state_.keylen - parse_state_.extralen; } busy = true; break; } case MemcachedParseState::EXTRA: { // We don't use extra for now. Just read and forget. auto n = std::min(static_cast(recvbuf_.last - in), parse_state_.read_left); parse_state_.read_left -= n; in += n; if (parse_state_.read_left) { recvbuf_.reset(); return 0; } parse_state_.state = MemcachedParseState::VALUE; // since we require keylen == 0, totalbody - extralen == // valuelen parse_state_.read_left = parse_state_.totalbody - parse_state_.keylen - parse_state_.extralen; busy = true; break; } case MemcachedParseState::VALUE: { auto n = std::min(static_cast(recvbuf_.last - in), parse_state_.read_left); parse_state_.value.insert(std::ranges::end(parse_state_.value), in, in + n); parse_state_.read_left -= n; in += n; if (parse_state_.read_left) { recvbuf_.reset(); return 0; } if (LOG_ENABLED(INFO)) { if (parse_state_.status_code != MemcachedStatusCode::NO_ERROR) { MCLOG(INFO, this) << "response returned error status: " << static_cast(parse_state_.status_code); } } // We require at least one complete response to clear try count. try_count_ = 0; auto req = std::move(recvq_.front()); recvq_.pop_front(); if (sendq_.empty() && recvq_.empty()) { ev_timer_stop(conn_.loop, &conn_.rt); } if (!req->canceled && req->cb) { req->cb(req.get(), MemcachedResult(parse_state_.status_code, std::move(parse_state_.value))); } parse_state_ = {}; break; } } if (!busy && in == recvbuf_.last) { break; } } assert(in == recvbuf_.last); recvbuf_.reset(); return 0; } #undef DEFAULT_WR_IOVCNT #define DEFAULT_WR_IOVCNT 128 #if defined(IOV_MAX) && IOV_MAX < DEFAULT_WR_IOVCNT # define MAX_WR_IOVCNT IOV_MAX #else // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT # define MAX_WR_IOVCNT DEFAULT_WR_IOVCNT #endif // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT size_t MemcachedConnection::fill_request_buffer(struct iovec *iov, size_t iovlen) { if (sendsum_ == 0) { for (auto &req : sendq_) { if (req->canceled) { continue; } if (serialized_size(req.get()) + sendsum_ > 1300) { break; } sendbufv_.emplace_back(); sendbufv_.back().req = req.get(); make_request(&sendbufv_.back(), req.get()); sendsum_ += sendbufv_.back().left(); } if (sendsum_ == 0) { sendq_.clear(); return 0; } } size_t iovcnt = 0; for (auto &buf : sendbufv_) { if (iovcnt + 2 > iovlen) { break; } auto req = buf.req; if (buf.headbuf.rleft()) { iov[iovcnt++] = {buf.headbuf.pos, buf.headbuf.rleft()}; } if (buf.send_value_left) { iov[iovcnt++] = {req->value.data() + req->value.size() - buf.send_value_left, buf.send_value_left}; } } return iovcnt; } void MemcachedConnection::drain_send_queue(size_t nwrite) { sendsum_ -= nwrite; while (nwrite > 0) { auto &buf = sendbufv_.front(); auto &req = sendq_.front(); if (req->canceled) { sendq_.pop_front(); continue; } assert(buf.req == req.get()); auto n = std::min(static_cast(nwrite), buf.headbuf.rleft()); buf.headbuf.drain(n); nwrite -= n; n = std::min(static_cast(nwrite), buf.send_value_left); buf.send_value_left -= n; nwrite -= n; if (buf.headbuf.rleft() || buf.send_value_left) { break; } sendbufv_.pop_front(); recvq_.push_back(std::move(sendq_.front())); sendq_.pop_front(); } // start read timer only when we wait for responses. if (recvq_.empty()) { ev_timer_stop(conn_.loop, &conn_.rt); } else if (!ev_is_active(&conn_.rt)) { conn_.again_rt(); } } size_t MemcachedConnection::serialized_size(MemcachedRequest *req) { switch (req->op) { case MemcachedOp::GET: return 24 + req->key.size(); case MemcachedOp::ADD: default: return 24 + 8 + req->key.size() + req->value.size(); } } void MemcachedConnection::make_request(MemcachedSendbuf *sendbuf, MemcachedRequest *req) { auto &headbuf = sendbuf->headbuf; std::ranges::fill(headbuf.buf, 0); headbuf[0] = MEMCACHED_REQ_MAGIC; headbuf[1] = static_cast(req->op); switch (req->op) { case MemcachedOp::GET: util::put_uint16be(&headbuf[2], static_cast(req->key.size())); util::put_uint32be(&headbuf[8], static_cast(req->key.size())); headbuf.write(24); break; case MemcachedOp::ADD: util::put_uint16be(&headbuf[2], static_cast(req->key.size())); headbuf[4] = 8; util::put_uint32be(&headbuf[8], static_cast(8 + req->key.size() + req->value.size())); util::put_uint32be(&headbuf[28], req->expiry); headbuf.write(32); break; } headbuf.write(req->key.c_str(), req->key.size()); sendbuf->send_value_left = req->value.size(); } int MemcachedConnection::add_request(std::unique_ptr req) { if (connect_blocker_.blocked()) { return -1; } sendq_.push_back(std::move(req)); if (connected_) { signal_write(); return 0; } if (conn_.fd == -1 && initiate_connection() != 0) { connect_blocker_.on_failure(); disconnect(); return -1; } return 0; } // TODO should we start write timer too? void MemcachedConnection::signal_write() { conn_.wlimit.startw(); } int MemcachedConnection::noop() { return 0; } void MemcachedConnection::reconnect_or_fail() { if (!connected_ || (recvq_.empty() && sendq_.empty())) { disconnect(); return; } constexpr size_t MAX_TRY_COUNT = 3; if (++try_count_ >= MAX_TRY_COUNT) { if (LOG_ENABLED(INFO)) { MCLOG(INFO, this) << "Tried " << MAX_TRY_COUNT << " times, and all failed. Aborting"; } try_count_ = 0; disconnect(); return; } std::vector> q; q.reserve(recvq_.size() + sendq_.size()); if (LOG_ENABLED(INFO)) { MCLOG(INFO, this) << "Retry connection, enqueue " << recvq_.size() + sendq_.size() << " request(s) again"; } q.insert(std::ranges::end(q), std::make_move_iterator(std::ranges::begin(recvq_)), std::make_move_iterator(std::ranges::end(recvq_))); q.insert(std::ranges::end(q), std::make_move_iterator(std::ranges::begin(sendq_)), std::make_move_iterator(std::ranges::end(sendq_))); recvq_.clear(); sendq_.clear(); disconnect(); sendq_.insert(std::ranges::end(sendq_), std::make_move_iterator(std::ranges::begin(q)), std::make_move_iterator(std::ranges::end(q))); if (initiate_connection() != 0) { connect_blocker_.on_failure(); disconnect(); return; } } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_connection.h0000644000000000000000000000013215077107270016603 xustar0030 mtime=1761382072.992444143 30 atime=1761382106.138310718 30 ctime=1761382109.134300283 nghttp2-1.68.0/src/shrpx_connection.h0000644000175100017510000001470015077107270017175 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_CONNECTION_H #define SHRPX_CONNECTION_H #include "shrpx_config.h" #include #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include #ifdef ENABLE_HTTP3 # include #endif // defined(ENABLE_HTTP3) #include "shrpx_rate_limit.h" #include "shrpx_error.h" #include "memchunk.h" namespace shrpx { namespace tls { struct TLSSessionCache; } // namespace tls struct TLSConnection { // Stores TLSv1.3 early data. DefaultMemchunks earlybuf; SSL *ssl; tls::TLSSessionCache *client_session_cache; std::chrono::steady_clock::time_point last_write_idle; size_t warmup_writelen; // length passed to SSL_write and SSL_read last time. This is // required since these functions require the exact same parameters // on non-blocking I/O. size_t last_writelen, last_readlen; bool initial_handshake_done; bool reneg_started; // true if ssl is prepared to do handshake as server. bool server_handshake; // true if ssl is initialized as server, and client requested // signed_certificate_timestamp extension. bool sct_requested; // true if TLSv1.3 early data has been completely received. Since // SSL_read_early_data acts like SSL_do_handshake, this field may be // true even if the negotiated TLS version is TLSv1.2 or earlier. // This value is also true if this is client side connection for // convenience. bool early_data_finish; }; struct TCPHint { size_t write_buffer_size; uint32_t rwin; }; template using EVCb = void (*)(struct ev_loop *, T *, int); using IOCb = EVCb; using TimerCb = EVCb; struct Connection { Connection(struct ev_loop *loop, int fd, SSL *ssl, MemchunkPool *mcpool, ev_tstamp write_timeout, ev_tstamp read_timeout, const RateLimitConfig &write_limit, const RateLimitConfig &read_limit, IOCb writecb, IOCb readcb, TimerCb timeoutcb, void *data, size_t tls_dyn_rec_warmup_threshold, ev_tstamp tls_dyn_rec_idle_timeout, Proto proto); ~Connection(); void disconnect(); void prepare_client_handshake(); void prepare_server_handshake(); int tls_handshake(); int write_tls_pending_handshake(); int check_http2_requirement(); // All write_* and writev_clear functions return number of bytes // written. If nothing cannot be written (e.g., there is no // allowance in RateLimit or underlying connection blocks), return // 0. SHRPX_ERR_NETWORK is returned in case of error. // // All read_* functions return number of bytes read. If nothing // cannot be read (e.g., there is no allowance in Ratelimit or // underlying connection blocks), return 0. SHRPX_ERR_EOF is // returned in case of EOF and no data was read. Otherwise // SHRPX_ERR_NETWORK is return in case of error. nghttp2_ssize write_tls(const void *data, size_t len); nghttp2_ssize read_tls(void *data, size_t len); size_t get_tls_write_limit(); // Updates the number of bytes written in warm up period. void update_tls_warmup_writelen(size_t n); // Tells there is no immediate write now. This triggers timer to // determine fallback to short record size mode. void start_tls_write_idle(); nghttp2_ssize write_clear(const void *data, size_t len); nghttp2_ssize writev_clear(struct iovec *iov, int iovcnt); nghttp2_ssize read_clear(void *data, size_t len); // Read at most |len| bytes of data from socket without rate limit. nghttp2_ssize read_nolim_clear(void *data, size_t len); // Peek at most |len| bytes of data from socket without rate limit. nghttp2_ssize peek_clear(void *data, size_t len); void handle_tls_pending_read(); void set_ssl(SSL *ssl); int get_tcp_hint(TCPHint *hint) const; // These functions are provided for read timer which is frequently // restarted. We do a trick to make a bit more efficient than just // calling ev_timer_again(). // Restarts read timer with timeout value |t|. void again_rt(ev_tstamp t); // Restarts read timer without changing timeout. void again_rt(); // Returns true if read timer expired. bool expired_rt(); #ifdef ENABLE_HTTP3 // This must be the first member of Connection. ngtcp2_crypto_conn_ref conn_ref; #endif // defined(ENABLE_HTTP3) TLSConnection tls; ev_io wev; ev_io rev; ev_timer wt; ev_timer rt; RateLimit wlimit; RateLimit rlimit; struct ev_loop *loop; void *data; int fd; size_t tls_dyn_rec_warmup_threshold; std::chrono::steady_clock::duration tls_dyn_rec_idle_timeout; // Application protocol used over the connection. This field is not // used in this object at the moment. The rest of the program may // use this value when it is useful. Proto proto; // The point of time when last read is observed. Note: since we use // |rt| as idle timer, the activity is not limited to read. std::chrono::steady_clock::time_point last_read; // Timeout for read timer |rt|. ev_tstamp read_timeout; }; #ifdef ENABLE_HTTP3 static_assert(std::is_standard_layout::value, "Connection is not standard layout"); #endif // defined(ENABLE_HTTP3) } // namespace shrpx #endif // !defined(SHRPX_CONNECTION_H) nghttp2-1.68.0/src/PaxHeaders/h2load_http3_session.cc0000644000000000000000000000013215077107270017414 xustar0030 mtime=1761382072.986444171 30 atime=1761382106.221310352 30 ctime=1761382109.217300043 nghttp2-1.68.0/src/h2load_http3_session.cc0000644000175100017510000003373615077107270020020 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2019 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "h2load_http3_session.h" #include #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include "h2load.h" namespace h2load { Http3Session::Http3Session(Client *client) : client_(client), conn_(nullptr), npending_request_(0), reqidx_(0) {} Http3Session::~Http3Session() { nghttp3_conn_del(conn_); } void Http3Session::on_connect() {} int Http3Session::submit_request() { if (npending_request_) { ++npending_request_; return 0; } auto config = client_->worker->config; reqidx_ = client_->reqidx; if (++client_->reqidx == config->nva.size()) { client_->reqidx = 0; } auto stream_id = submit_request_internal(); if (stream_id < 0) { if (stream_id == NGTCP2_ERR_STREAM_ID_BLOCKED) { ++npending_request_; return 0; } return -1; } return 0; } namespace { nghttp3_ssize read_data(nghttp3_conn *conn, int64_t stream_id, nghttp3_vec *vec, size_t veccnt, uint32_t *pflags, void *user_data, void *stream_user_data) { auto s = static_cast(user_data); s->read_data(vec, veccnt, pflags); return 1; } } // namespace void Http3Session::read_data(nghttp3_vec *vec, size_t veccnt, uint32_t *pflags) { assert(veccnt > 0); auto config = client_->worker->config; vec[0].base = config->data; vec[0].len = static_cast(config->data_length); *pflags |= NGHTTP3_DATA_FLAG_EOF; } int64_t Http3Session::submit_request_internal() { int rv; int64_t stream_id; auto config = client_->worker->config; auto &nva = config->nva[reqidx_]; rv = ngtcp2_conn_open_bidi_stream(client_->quic.conn, &stream_id, nullptr); if (rv != 0) { return rv; } nghttp3_data_reader dr{ .read_data = h2load::read_data, }; rv = nghttp3_conn_submit_request( conn_, stream_id, reinterpret_cast(nva.data()), nva.size(), config->data_fd == -1 ? nullptr : &dr, nullptr); if (rv != 0) { return rv; } client_->on_request(stream_id); auto req_stat = client_->get_req_stat(stream_id); assert(req_stat); client_->record_request_time(req_stat); return stream_id; } int Http3Session::on_read(const uint8_t *data, size_t len) { return -1; } int Http3Session::on_write() { return -1; } void Http3Session::terminate() {} size_t Http3Session::max_concurrent_streams() { return client_->worker->config->max_concurrent_streams; } namespace { int stream_close(nghttp3_conn *conn, int64_t stream_id, uint64_t app_error_code, void *user_data, void *stream_user_data) { auto s = static_cast(user_data); if (s->stream_close(stream_id, app_error_code) != 0) { return NGHTTP3_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Http3Session::stream_close(int64_t stream_id, uint64_t app_error_code) { if (!ngtcp2_is_bidi_stream(stream_id)) { assert(!ngtcp2_conn_is_local_stream(client_->quic.conn, stream_id)); ngtcp2_conn_extend_max_streams_uni(client_->quic.conn, 1); } client_->on_stream_close(stream_id, app_error_code == NGHTTP3_H3_NO_ERROR); return 0; } namespace { int end_stream(nghttp3_conn *conn, int64_t stream_id, void *user_data, void *stream_user_data) { auto s = static_cast(user_data); if (s->end_stream(stream_id) != 0) { return NGHTTP3_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Http3Session::end_stream(int64_t stream_id) { client_->record_ttfb(); return 0; } namespace { int recv_data(nghttp3_conn *conn, int64_t stream_id, const uint8_t *data, size_t datalen, void *user_data, void *stream_user_data) { auto s = static_cast(user_data); s->recv_data(stream_id, data, datalen); return 0; } } // namespace void Http3Session::recv_data(int64_t stream_id, const uint8_t *data, size_t datalen) { client_->record_ttfb(); client_->worker->stats.bytes_body += datalen; consume(stream_id, datalen); } namespace { int deferred_consume(nghttp3_conn *conn, int64_t stream_id, size_t nconsumed, void *user_data, void *stream_user_data) { auto s = static_cast(user_data); s->consume(stream_id, nconsumed); return 0; } } // namespace void Http3Session::consume(int64_t stream_id, size_t nconsumed) { ngtcp2_conn_extend_max_stream_offset(client_->quic.conn, stream_id, nconsumed); ngtcp2_conn_extend_max_offset(client_->quic.conn, nconsumed); } namespace { int begin_headers(nghttp3_conn *conn, int64_t stream_id, void *user_data, void *stream_user_data) { auto s = static_cast(user_data); s->begin_headers(stream_id); return 0; } } // namespace void Http3Session::begin_headers(int64_t stream_id) { auto payloadlen = nghttp3_conn_get_frame_payload_left(conn_, stream_id); assert(payloadlen > 0); client_->worker->stats.bytes_head += payloadlen; } namespace { int recv_header(nghttp3_conn *conn, int64_t stream_id, int32_t token, nghttp3_rcbuf *name, nghttp3_rcbuf *value, uint8_t flags, void *user_data, void *stream_user_data) { auto s = static_cast(user_data); auto k = nghttp3_rcbuf_get_buf(name); auto v = nghttp3_rcbuf_get_buf(value); s->recv_header(stream_id, &k, &v); return 0; } } // namespace void Http3Session::recv_header(int64_t stream_id, const nghttp3_vec *name, const nghttp3_vec *value) { client_->on_header(stream_id, name->base, name->len, value->base, value->len); client_->worker->stats.bytes_head_decomp += name->len + value->len; } namespace { int stop_sending(nghttp3_conn *conn, int64_t stream_id, uint64_t app_error_code, void *user_data, void *stream_user_data) { auto s = static_cast(user_data); if (s->stop_sending(stream_id, app_error_code) != 0) { return NGHTTP3_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Http3Session::stop_sending(int64_t stream_id, uint64_t app_error_code) { auto rv = ngtcp2_conn_shutdown_stream_read(client_->quic.conn, 0, stream_id, app_error_code); if (rv != 0) { std::cerr << "ngtcp2_conn_shutdown_stream_read: " << ngtcp2_strerror(rv) << std::endl; return -1; } return 0; } namespace { int reset_stream(nghttp3_conn *conn, int64_t stream_id, uint64_t app_error_code, void *user_data, void *stream_user_data) { auto s = static_cast(user_data); if (s->reset_stream(stream_id, app_error_code) != 0) { return NGHTTP3_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Http3Session::reset_stream(int64_t stream_id, uint64_t app_error_code) { auto rv = ngtcp2_conn_shutdown_stream_write(client_->quic.conn, 0, stream_id, app_error_code); if (rv != 0) { std::cerr << "ngtcp2_conn_shutdown_stream_write: " << ngtcp2_strerror(rv) << std::endl; return -1; } return 0; } int Http3Session::close_stream(int64_t stream_id, uint64_t app_error_code) { auto rv = nghttp3_conn_close_stream(conn_, stream_id, app_error_code); switch (rv) { case 0: return 0; case NGHTTP3_ERR_STREAM_NOT_FOUND: if (!ngtcp2_is_bidi_stream(stream_id)) { assert(!ngtcp2_conn_is_local_stream(client_->quic.conn, stream_id)); ngtcp2_conn_extend_max_streams_uni(client_->quic.conn, 1); } return 0; default: return -1; } } int Http3Session::shutdown_stream_read(int64_t stream_id) { auto rv = nghttp3_conn_shutdown_stream_read(conn_, stream_id); if (rv != 0) { return -1; } return 0; } int Http3Session::extend_max_local_streams() { auto config = client_->worker->config; for (; npending_request_; --npending_request_) { auto stream_id = submit_request_internal(); if (stream_id < 0) { if (stream_id == NGTCP2_ERR_STREAM_ID_BLOCKED) { return 0; } return -1; } if (++reqidx_ == config->nva.size()) { reqidx_ = 0; } } return 0; } namespace { void rand(uint8_t *dest, size_t destlen) { auto rv = RAND_bytes(dest, static_cast(destlen)); if (rv != 1) { assert(0); abort(); } } } // namespace int Http3Session::init_conn() { int rv; assert(conn_ == nullptr); if (ngtcp2_conn_get_streams_uni_left(client_->quic.conn) < 3) { return -1; } nghttp3_callbacks callbacks{ .stream_close = h2load::stream_close, .recv_data = h2load::recv_data, .deferred_consume = h2load::deferred_consume, .begin_headers = h2load::begin_headers, .recv_header = h2load::recv_header, .recv_trailer = h2load::recv_header, .stop_sending = h2load::stop_sending, .end_stream = h2load::end_stream, .reset_stream = h2load::reset_stream, .rand = h2load::rand, }; auto config = client_->worker->config; nghttp3_settings settings; nghttp3_settings_default(&settings); settings.qpack_max_dtable_capacity = config->header_table_size; settings.qpack_blocked_streams = 100; auto mem = nghttp3_mem_default(); rv = nghttp3_conn_client_new(&conn_, &callbacks, &settings, mem, this); if (rv != 0) { std::cerr << "nghttp3_conn_client_new: " << nghttp3_strerror(rv) << std::endl; return -1; } int64_t ctrl_stream_id; rv = ngtcp2_conn_open_uni_stream(client_->quic.conn, &ctrl_stream_id, nullptr); if (rv != 0) { std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv) << std::endl; return -1; } rv = nghttp3_conn_bind_control_stream(conn_, ctrl_stream_id); if (rv != 0) { std::cerr << "nghttp3_conn_bind_control_stream: " << nghttp3_strerror(rv) << std::endl; return -1; } int64_t qpack_enc_stream_id, qpack_dec_stream_id; rv = ngtcp2_conn_open_uni_stream(client_->quic.conn, &qpack_enc_stream_id, nullptr); if (rv != 0) { std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv) << std::endl; return -1; } rv = ngtcp2_conn_open_uni_stream(client_->quic.conn, &qpack_dec_stream_id, nullptr); if (rv != 0) { std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv) << std::endl; return -1; } rv = nghttp3_conn_bind_qpack_streams(conn_, qpack_enc_stream_id, qpack_dec_stream_id); if (rv != 0) { std::cerr << "nghttp3_conn_bind_qpack_streams: " << nghttp3_strerror(rv) << std::endl; return -1; } return 0; } ssize_t Http3Session::read_stream(uint32_t flags, int64_t stream_id, const uint8_t *data, size_t datalen) { auto nconsumed = nghttp3_conn_read_stream2( conn_, stream_id, data, datalen, flags & NGTCP2_STREAM_DATA_FLAG_FIN, ngtcp2_conn_get_timestamp(client_->quic.conn)); if (nconsumed < 0) { std::cerr << "nghttp3_conn_read_stream2: " << nghttp3_strerror(static_cast(nconsumed)) << std::endl; ngtcp2_ccerr_set_application_error( &client_->quic.last_error, nghttp3_err_infer_quic_app_error_code(static_cast(nconsumed)), nullptr, 0); return -1; } return nconsumed; } ssize_t Http3Session::write_stream(int64_t &stream_id, int &fin, nghttp3_vec *vec, size_t veccnt) { auto sveccnt = nghttp3_conn_writev_stream(conn_, &stream_id, &fin, vec, veccnt); if (sveccnt < 0) { ngtcp2_ccerr_set_application_error( &client_->quic.last_error, nghttp3_err_infer_quic_app_error_code(static_cast(sveccnt)), nullptr, 0); return -1; } return sveccnt; } void Http3Session::block_stream(int64_t stream_id) { nghttp3_conn_block_stream(conn_, stream_id); } int Http3Session::unblock_stream(int64_t stream_id) { if (nghttp3_conn_unblock_stream(conn_, stream_id) != 0) { return -1; } return 0; } void Http3Session::shutdown_stream_write(int64_t stream_id) { nghttp3_conn_shutdown_stream_write(conn_, stream_id); } int Http3Session::add_write_offset(int64_t stream_id, size_t ndatalen) { auto rv = nghttp3_conn_add_write_offset(conn_, stream_id, ndatalen); if (rv != 0) { ngtcp2_ccerr_set_application_error( &client_->quic.last_error, nghttp3_err_infer_quic_app_error_code(rv), nullptr, 0); return -1; } return 0; } int Http3Session::add_ack_offset(int64_t stream_id, size_t datalen) { auto rv = nghttp3_conn_add_ack_offset(conn_, stream_id, datalen); if (rv != 0) { ngtcp2_ccerr_set_application_error( &client_->quic.last_error, nghttp3_err_infer_quic_app_error_code(rv), nullptr, 0); return -1; } return 0; } } // namespace h2load nghttp2-1.68.0/src/PaxHeaders/nghttp2_config.h0000644000000000000000000000013115077107270016132 xustar0030 mtime=1761382072.989444157 30 atime=1761382106.229310317 29 ctime=1761382109.22530002 nghttp2-1.68.0/src/nghttp2_config.h0000644000175100017510000000245515077107270016531 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP2_CONFIG_H #define NGHTTP2_CONFIG_H #ifdef HAVE_CONFIG_H # include #endif // defined(HAVE_CONFIG_H) #endif // !defined(NGHTTP2_CONFIG_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_downstream_test.h0000644000000000000000000000013215077107270017666 xustar0030 mtime=1761382072.993444139 30 atime=1761382106.249310229 30 ctime=1761382109.246299959 nghttp2-1.68.0/src/shrpx_downstream_test.h0000644000175100017510000000361515077107270020263 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_DOWNSTREAM_TEST_H #define SHRPX_DOWNSTREAM_TEST_H #ifdef HAVE_CONFIG_H # include #endif // defined(HAVE_CONFIG_H) #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" namespace shrpx { extern const MunitSuite downstream_suite; munit_void_test_decl(test_downstream_field_store_append_last_header) munit_void_test_decl(test_downstream_field_store_header) munit_void_test_decl(test_downstream_crumble_request_cookie) munit_void_test_decl(test_downstream_assemble_request_cookie) munit_void_test_decl(test_downstream_rewrite_location_response_header) munit_void_test_decl(test_downstream_supports_non_final_response) munit_void_test_decl(test_downstream_find_affinity_cookie) } // namespace shrpx #endif // !defined(SHRPX_DOWNSTREAM_TEST_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_io_control.h0000644000000000000000000000013115077107270016612 xustar0030 mtime=1761382072.996444125 29 atime=1761382106.11731081 30 ctime=1761382109.114300341 nghttp2-1.68.0/src/shrpx_io_control.h0000644000175100017510000000343115077107270017204 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_IO_CONTROL_H #define SHRPX_IO_CONTROL_H #include "shrpx.h" #include #include #include #include "shrpx_rate_limit.h" namespace shrpx { enum IOCtrlReason { SHRPX_NO_BUFFER = 1 << 0, SHRPX_MSG_BLOCK = 1 << 1 }; class IOControl { public: IOControl(RateLimit *lim); ~IOControl(); void pause_read(IOCtrlReason reason); // Returns true if read operation is enabled after this call bool resume_read(IOCtrlReason reason); // Clear all pause flags and enable read void force_resume_read(); private: RateLimit *lim_; uint32_t rdbits_; }; } // namespace shrpx #endif // !defined(SHRPX_IO_CONTROL_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_tls.cc0000644000000000000000000000013215077107270015404 xustar0030 mtime=1761382072.999444111 30 atime=1761382106.119310801 30 ctime=1761382109.115300338 nghttp2-1.68.0/src/shrpx_tls.cc0000644000175100017510000022237715077107270016011 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_tls.h" #ifdef HAVE_SYS_SOCKET_H # include #endif // defined(HAVE_SYS_SOCKET_H) #ifdef HAVE_NETDB_H # include #endif // defined(HAVE_NETDB_H) #include #include #include #include #include #include #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include # include # include # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include # include # include # include # include # if OPENSSL_3_0_0_API # include # include # include # endif // OPENSSL_3_0_0_API #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #ifdef NGHTTP2_OPENSSL_IS_BORINGSSL # include #endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) #include #ifdef ENABLE_HTTP3 # include # include # if defined(HAVE_LIBNGTCP2_CRYPTO_QUICTLS) || \ defined(HAVE_LIBNGTCP2_CRYPTO_LIBRESSL) # include # endif // defined(HAVE_LIBNGTCP2_CRYPTO_QUICTLS) || // defined(HAVE_LIBNGTCP2_CRYPTO_LIBRESSL) # ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL # include # endif // defined(HAVE_LIBNGTCP2_CRYPTO_BORINGSSL) # ifdef HAVE_LIBNGTCP2_CRYPTO_WOLFSSL # include # endif // defined(HAVE_LIBNGTCP2_CRYPTO_WOLFSSL) #endif // defined(ENABLE_HTTP3) #ifdef HAVE_LIBBROTLI # include # include #endif // defined(HAVE_LIBBROTLI) #include "shrpx_log.h" #include "shrpx_client_handler.h" #include "shrpx_config.h" #include "shrpx_worker.h" #include "shrpx_downstream_connection_pool.h" #include "shrpx_http2_session.h" #include "shrpx_memcached_request.h" #include "shrpx_memcached_dispatcher.h" #include "shrpx_connection_handler.h" #ifdef ENABLE_HTTP3 # include "shrpx_http3_upstream.h" #endif // defined(ENABLE_HTTP3) #include "util.h" #include "tls.h" #include "template.h" #include "timegm.h" using namespace nghttp2; using namespace std::chrono_literals; namespace shrpx { namespace tls { namespace { int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { if (!preverify_ok) { int err = X509_STORE_CTX_get_error(ctx); int depth = X509_STORE_CTX_get_error_depth(ctx); if (err == X509_V_ERR_CERT_HAS_EXPIRED && depth == 0 && get_config()->tls.client_verify.tolerate_expired) { LOG(INFO) << "The client certificate has expired, but is accepted by " "configuration"; return 1; } LOG(ERROR) << "client certificate verify error:num=" << err << ":" << X509_verify_cert_error_string(err) << ":depth=" << depth; } return preverify_ok; } } // namespace int set_alpn_prefs(std::vector &out, const std::vector &protos) { size_t len = 0; for (const auto &proto : protos) { if (proto.size() > 255) { LOG(FATAL) << "Too long ALPN identifier: " << proto.size(); return -1; } len += 1 + proto.size(); } if (len > (1 << 16) - 1) { LOG(FATAL) << "Too long ALPN identifier list: " << len; return -1; } out.resize(len); auto ptr = out.data(); for (const auto &proto : protos) { *ptr++ = static_cast(proto.size()); ptr = std::ranges::copy(proto, ptr).out; } return 0; } namespace { int ssl_pem_passwd_cb(char *buf, int size, int rwflag, void *user_data) { auto config = static_cast(user_data); auto len = config->tls.private_key_passwd.size(); if (static_cast(size) < len + 1) { LOG(ERROR) << "ssl_pem_passwd_cb: buf is too small " << size; return 0; } // Copy string including last '\0'. memcpy(buf, config->tls.private_key_passwd.data(), len + 1); return static_cast(len); } } // namespace namespace { std::string_view get_servername(SSL *ssl) { auto rawhost = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); if (rawhost == nullptr) { return ""sv; } auto servername = std::string_view{rawhost}; // NI_MAXHOST includes terminal NULL. if (servername.empty() || servername.size() + 1 > NI_MAXHOST) { return ""sv; } return servername; } } // namespace namespace { void select_ssl_ctx(SSL *ssl, const std::string_view &servername) { auto conn = static_cast(SSL_get_app_data(ssl)); auto handler = static_cast(conn->data); auto worker = handler->get_worker(); std::array buf; auto end_buf = util::tolower(servername, std::ranges::begin(buf)); auto hostname = std::string_view{std::ranges::begin(buf), end_buf}; #ifdef ENABLE_HTTP3 auto cert_tree = conn->proto == Proto::HTTP3 ? worker->get_quic_cert_lookup_tree() : worker->get_cert_lookup_tree(); #else // !defined(ENABLE_HTTP3) auto cert_tree = worker->get_cert_lookup_tree(); #endif // !defined(ENABLE_HTTP3) auto idx = cert_tree->lookup(hostname); if (idx == -1) { return; } handler->set_tls_sni(hostname); auto conn_handler = worker->get_connection_handler(); #ifdef ENABLE_HTTP3 const auto &ssl_ctx_list = conn->proto == Proto::HTTP3 ? conn_handler->get_quic_indexed_ssl_ctx(as_unsigned(idx)) : conn_handler->get_indexed_ssl_ctx(as_unsigned(idx)); #else // !defined(ENABLE_HTTP3) const auto &ssl_ctx_list = conn_handler->get_indexed_ssl_ctx(as_unsigned(idx)); #endif // !defined(ENABLE_HTTP3) assert(!ssl_ctx_list.empty()); // fast path if (ssl_ctx_list.size() == 1) { SSL_set_SSL_CTX(ssl, ssl_ctx_list[0]); return; } auto ecdsa = false; auto mldsa = false; #ifdef NGHTTP2_GENUINE_OPENSSL auto num_sigalgs = SSL_get_sigalgs(ssl, 0, nullptr, nullptr, nullptr, nullptr, nullptr); for (idx = 0; idx < num_sigalgs; ++idx) { int signhash; SSL_get_sigalgs(ssl, static_cast(idx), nullptr, nullptr, &signhash, nullptr, nullptr); switch (signhash) { case NID_ecdsa_with_SHA256: case NID_ecdsa_with_SHA384: case NID_ecdsa_with_SHA512: ecdsa = true; break; # if OPENSSL_3_5_0_API case NID_ML_DSA_44: case NID_ML_DSA_65: case NID_ML_DSA_87: mldsa = true; break; # endif // OPENSSL_3_5_0_API } } #endif // defined(NGHTTP2_GENUINE_OPENSSL) #ifdef NGHTTP2_OPENSSL_IS_BORINGSSL const uint16_t *sigalgs; auto num_sigalgs = SSL_get0_peer_verify_algorithms(ssl, &sigalgs); for (size_t i = 0; i < num_sigalgs && !ecdsa; ++i) { switch (sigalgs[i]) { case SSL_SIGN_ECDSA_SECP256R1_SHA256: case SSL_SIGN_ECDSA_SECP384R1_SHA384: case SSL_SIGN_ECDSA_SECP521R1_SHA512: ecdsa = true; break; } } #endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL const uint8_t *sigalgs; uint16_t num_sigalgs; if (wolfSSL_get_client_suites_sigalgs(ssl, nullptr, nullptr, &sigalgs, &num_sigalgs) == WOLFSSL_SUCCESS) { for (size_t i = 0; i < num_sigalgs; i += 2) { int hashalgo; int sigalgo; if (wolfSSL_get_sigalg_info(sigalgs[i], sigalgs[i + 1], &hashalgo, &sigalgo) != 0) { continue; } switch (sigalgo) { case ECDSAk: ecdsa = true; break; case ML_DSA_LEVEL2k: case ML_DSA_LEVEL3k: case ML_DSA_LEVEL5k: mldsa = true; break; } } } #endif // defined(NGHTTP2_OPENSSL_IS_WOLFSSL) if (!ecdsa && !mldsa) { SSL_set_SSL_CTX(ssl, ssl_ctx_list[0]); return; } SSL_CTX *selected = nullptr; for (auto ssl_ctx : ssl_ctx_list) { auto tls_ctx_data = static_cast(SSL_CTX_get_app_data(ssl_ctx)); switch (tls_ctx_data->cert_type) { case NGHTTP2_CERT_TYPE_ECDSA: if (ecdsa && !selected) { selected = ssl_ctx; } break; #if OPENSSL_3_5_0_API || defined(NGHTTP2_OPENSSL_IS_WOLFSSL) case NGHTTP2_CERT_TYPE_ML_DSA_44: case NGHTTP2_CERT_TYPE_ML_DSA_65: case NGHTTP2_CERT_TYPE_ML_DSA_87: if (mldsa) { SSL_set_SSL_CTX(ssl, ssl_ctx); return; } break; #endif // OPENSSL_3_5_0_API || defined(NGHTTP2_OPENSSL_IS_WOLFSSL) } } if (selected) { SSL_set_SSL_CTX(ssl, selected); } else { SSL_set_SSL_CTX(ssl, ssl_ctx_list[0]); } return; } } // namespace namespace { // *al is set to SSL_AD_UNRECOGNIZED_NAME by openssl, so we don't have // to set it explicitly. int servername_callback(SSL *ssl, int *al, void *arg) { auto servername = get_servername(ssl); if (servername.empty()) { return SSL_TLSEXT_ERR_NOACK; } #if defined(NGHTTP2_GENUINE_OPENSSL) || defined(NGHTTP2_OPENSSL_IS_LIBRESSL) select_ssl_ctx(ssl, servername); #endif // defined(NGHTTP2_GENUINE_OPENSSL) || // defined(NGHTTP2_OPENSSL_IS_LIBRESSL) return SSL_TLSEXT_ERR_OK; } } // namespace #if defined(NGHTTP2_OPENSSL_IS_BORINGSSL) || defined(NGHTTP2_OPENSSL_IS_WOLFSSL) namespace { int cert_cb(SSL *ssl, void *arg) { auto servername = get_servername(ssl); if (!servername.empty()) { select_ssl_ctx(ssl, servername); } return 1; } } // namespace #endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) || // defined(NGHTTP2_OPENSSL_IS_WOLFSSL) namespace { int tls_session_client_new_cb(SSL *ssl, SSL_SESSION *session) { auto conn = static_cast(SSL_get_app_data(ssl)); if (conn->tls.client_session_cache == nullptr) { return 0; } try_cache_tls_session(conn->tls.client_session_cache, session, std::chrono::steady_clock::now()); return 0; } } // namespace namespace { int ticket_key_cb(SSL *ssl, unsigned char *key_name, unsigned char *iv, EVP_CIPHER_CTX *ctx, #if OPENSSL_3_0_0_API EVP_MAC_CTX *hctx, #else // !OPENSSL_3_0_0_API HMAC_CTX *hctx, #endif // !OPENSSL_3_0_0_API int enc) { auto conn = static_cast(SSL_get_app_data(ssl)); auto handler = static_cast(conn->data); auto worker = handler->get_worker(); auto ticket_keys = worker->get_ticket_keys(); if (!ticket_keys) { // No ticket keys available. return -1; } auto &keys = ticket_keys->keys; assert(!keys.empty()); if (enc) { if (RAND_bytes(iv, EVP_MAX_IV_LENGTH) == 0) { if (LOG_ENABLED(INFO)) { CLOG(INFO, handler) << "session ticket key: RAND_bytes failed"; } return -1; } auto &key = keys[0]; if (LOG_ENABLED(INFO)) { CLOG(INFO, handler) << "encrypt session ticket key: " << util::format_hex(key.data.name); } std::ranges::copy(key.data.name, key_name); EVP_EncryptInit_ex(ctx, get_config()->tls.ticket.cipher, nullptr, key.data.enc_key.data(), iv); #if OPENSSL_3_0_0_API auto params = std::to_array({ OSSL_PARAM_construct_octet_string( OSSL_MAC_PARAM_KEY, key.data.hmac_key.data(), key.hmac_keylen), OSSL_PARAM_construct_utf8_string( OSSL_MAC_PARAM_DIGEST, const_cast(EVP_MD_get0_name(key.hmac)), 0), OSSL_PARAM_construct_end(), }); if (!EVP_MAC_CTX_set_params(hctx, params.data())) { if (LOG_ENABLED(INFO)) { CLOG(INFO, handler) << "EVP_MAC_CTX_set_params failed"; } return -1; } #else // !OPENSSL_3_0_0_API HMAC_Init_ex(hctx, key.data.hmac_key.data(), static_cast(key.hmac_keylen), key.hmac, nullptr); #endif // !OPENSSL_3_0_0_API return 1; } size_t i; for (i = 0; i < keys.size(); ++i) { auto &key = keys[i]; if (std::ranges::equal(key.data.name, std::span{key_name, key.data.name.size()})) { break; } } if (i == keys.size()) { if (LOG_ENABLED(INFO)) { CLOG(INFO, handler) << "session ticket key " << util::format_hex(std::span{key_name, 16}) << " not found"; } return 0; } if (LOG_ENABLED(INFO)) { CLOG(INFO, handler) << "decrypt session ticket key: " << util::format_hex(std::span{key_name, 16}); } auto &key = keys[i]; #if OPENSSL_3_0_0_API auto params = std::to_array({ OSSL_PARAM_construct_octet_string( OSSL_MAC_PARAM_KEY, key.data.hmac_key.data(), key.hmac_keylen), OSSL_PARAM_construct_utf8_string( OSSL_MAC_PARAM_DIGEST, const_cast(EVP_MD_get0_name(key.hmac)), 0), OSSL_PARAM_construct_end(), }); if (!EVP_MAC_CTX_set_params(hctx, params.data())) { if (LOG_ENABLED(INFO)) { CLOG(INFO, handler) << "EVP_MAC_CTX_set_params failed"; } return -1; } #else // !OPENSSL_3_0_0_API HMAC_Init_ex(hctx, key.data.hmac_key.data(), static_cast(key.hmac_keylen), key.hmac, nullptr); #endif // !OPENSSL_3_0_0_API EVP_DecryptInit_ex(ctx, key.cipher, nullptr, key.data.enc_key.data(), iv); #ifdef TLS1_3_VERSION // If ticket_key_cb is not set, OpenSSL always renew ticket for // TLSv1.3. if (SSL_version(ssl) == TLS1_3_VERSION) { return 2; } #endif // defined(TLS1_3_VERSION) return i == 0 ? 1 : 2; } } // namespace namespace { void info_callback(const SSL *ssl, int where, int ret) { #ifdef TLS1_3_VERSION // TLSv1.3 has no renegotiation. if (SSL_version(ssl) == TLS1_3_VERSION) { return; } #endif // defined(TLS1_3_VERSION) // To mitigate possible DOS attack using lots of renegotiations, we // disable renegotiation. Since OpenSSL does not provide an easy way // to disable it, we check that renegotiation is started in this // callback. if (where & SSL_CB_HANDSHAKE_START) { auto conn = static_cast(SSL_get_app_data(ssl)); if (conn && conn->tls.initial_handshake_done) { auto handler = static_cast(conn->data); if (LOG_ENABLED(INFO)) { CLOG(INFO, handler) << "TLS renegotiation started"; } handler->start_immediate_shutdown(); } } } } // namespace namespace { int alpn_select_proto_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { // We assume that get_config()->alpn_list contains ALPN protocol // identifier sorted by preference order. So we just break when we // found the first overlap. for (const auto &alpn : get_config()->tls.alpn_list) { for (auto p = in, end = in + inlen; p < end;) { auto proto_id = p + 1; auto proto_len = *p; if (alpn.size() == proto_len && memcmp(alpn.data(), proto_id, alpn.size()) == 0) { *out = proto_id; *outlen = proto_len; return SSL_TLSEXT_ERR_OK; } p += 1 + proto_len; } } return SSL_TLSEXT_ERR_NOACK; } } // namespace #ifdef ENABLE_HTTP3 namespace { int quic_alpn_select_proto_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { static constexpr std::string_view alpnlist[] = { "h3"sv, "h3-29"sv, }; for (auto &alpn : alpnlist) { for (auto p = in, end = in + inlen; p < end;) { auto proto_id = p + 1; auto proto_len = *p; if (alpn.size() == proto_len && memcmp(alpn.data(), proto_id, alpn.size()) == 0) { *out = proto_id; *outlen = proto_len; return SSL_TLSEXT_ERR_OK; } p += 1 + proto_len; } } return SSL_TLSEXT_ERR_ALERT_FATAL; } } // namespace #endif // defined(ENABLE_HTTP3) #ifdef NGHTTP2_GENUINE_OPENSSL namespace { int sct_add_cb(SSL *ssl, unsigned int ext_type, unsigned int context, const unsigned char **out, size_t *outlen, X509 *x, size_t chainidx, int *al, void *add_arg) { assert(ext_type == TLSEXT_TYPE_signed_certificate_timestamp); auto conn = static_cast(SSL_get_app_data(ssl)); if (!conn->tls.sct_requested) { return 0; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "sct_add_cb is called, chainidx=" << chainidx << ", x=" << x << ", context=" << log::hex << context; } // We only have SCTs for leaf certificate. if (chainidx != 0) { return 0; } auto ssl_ctx = SSL_get_SSL_CTX(ssl); auto tls_ctx_data = static_cast(SSL_CTX_get_app_data(ssl_ctx)); *out = tls_ctx_data->sct_data.data(); *outlen = tls_ctx_data->sct_data.size(); return 1; } } // namespace namespace { void sct_free_cb(SSL *ssl, unsigned int ext_type, unsigned int context, const unsigned char *out, void *add_arg) { assert(ext_type == TLSEXT_TYPE_signed_certificate_timestamp); } } // namespace namespace { int sct_parse_cb(SSL *ssl, unsigned int ext_type, unsigned int context, const unsigned char *in, size_t inlen, X509 *x, size_t chainidx, int *al, void *parse_arg) { assert(ext_type == TLSEXT_TYPE_signed_certificate_timestamp); // client SHOULD send 0 length extension_data, but it is still // SHOULD, and not MUST. // For TLSv1.3 Certificate message, sct_add_cb is called even if // client has not sent signed_certificate_timestamp extension in its // ClientHello. Explicitly remember that client has included it // here. auto conn = static_cast(SSL_get_app_data(ssl)); conn->tls.sct_requested = true; return 1; } } // namespace #endif // defined(NGHTTP2_GENUINE_OPENSSL) #ifndef OPENSSL_NO_PSK namespace { unsigned int psk_server_cb(SSL *ssl, const char *identity, unsigned char *psk, unsigned int max_psk_len) { auto config = get_config(); auto &tlsconf = config->tls; auto it = tlsconf.psk_secrets.find(std::string_view{identity}); if (it == std::ranges::end(tlsconf.psk_secrets)) { return 0; } auto &secret = (*it).second; if (secret.size() > max_psk_len) { LOG(ERROR) << "The size of PSK secret is " << secret.size() << ", but the acceptable maximum size is" << max_psk_len; return 0; } std::ranges::copy(secret, psk); return static_cast(secret.size()); } } // namespace #endif // !defined(OPENSSL_NO_PSK) #ifndef OPENSSL_NO_PSK namespace { unsigned int psk_client_cb(SSL *ssl, const char *hint, char *identity_out, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len) { auto config = get_config(); auto &tlsconf = config->tls; auto &identity = tlsconf.client.psk.identity; auto &secret = tlsconf.client.psk.secret; if (identity.empty()) { return 0; } if (identity.size() + 1 > max_identity_len) { LOG(ERROR) << "The size of PSK identity is " << identity.size() << ", but the acceptable maximum size is " << max_identity_len; return 0; } if (secret.size() > max_psk_len) { LOG(ERROR) << "The size of PSK secret is " << secret.size() << ", but the acceptable maximum size is " << max_psk_len; return 0; } *std::ranges::copy(identity, identity_out).out = '\0'; std::ranges::copy(secret, psk); return static_cast(secret.size()); } } // namespace #endif // !defined(OPENSSL_NO_PSK) #if defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && defined(HAVE_LIBBROTLI) namespace { int cert_compress(SSL *ssl, CBB *out, const uint8_t *in, size_t in_len) { uint8_t *dest; size_t compressed_size = BrotliEncoderMaxCompressedSize(in_len); if (compressed_size == 0) { LOG(ERROR) << "BrotliEncoderMaxCompressedSize failed"; return 0; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "Maximum compressed size is " << compressed_size << " bytes against input " << in_len << " bytes"; } if (!CBB_reserve(out, &dest, compressed_size)) { LOG(ERROR) << "CBB_reserve failed"; return 0; } if (BrotliEncoderCompress(BROTLI_MAX_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_MODE_GENERIC, in_len, in, &compressed_size, dest) != BROTLI_TRUE) { LOG(ERROR) << "BrotliEncoderCompress failed"; return 0; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "BrotliEncoderCompress succeeded, produced " << compressed_size << " bytes, " << (in_len - compressed_size) * 100 / in_len << "% reduction"; } if (!CBB_did_write(out, compressed_size)) { LOG(ERROR) << "CBB_did_write failed"; return 0; } return 1; } int cert_decompress(SSL *ssl, CRYPTO_BUFFER **out, size_t uncompressed_len, const uint8_t *in, size_t in_len) { uint8_t *dest; auto buf = CRYPTO_BUFFER_alloc(&dest, uncompressed_len); auto len = uncompressed_len; if (BrotliDecoderDecompress(in_len, in, &len, dest) != BROTLI_DECODER_RESULT_SUCCESS) { LOG(ERROR) << "BrotliDecoderDecompress failed"; CRYPTO_BUFFER_free(buf); return 0; } if (uncompressed_len != len) { LOG(ERROR) << "Unexpected uncompressed length: expected " << uncompressed_len << " bytes, actual " << len << " bytes"; CRYPTO_BUFFER_free(buf); return 0; } *out = buf; return 1; } } // namespace #endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && // defined(HAVE_LIBBROTLI) struct TLSProtocol { std::string_view name; nghttp2_ssl_op_type mask; }; constexpr TLSProtocol TLS_PROTOS[] = { TLSProtocol{"TLSv1.2"sv, SSL_OP_NO_TLSv1_2}, }; nghttp2_ssl_op_type create_tls_proto_mask(const std::vector &tls_proto_list) { nghttp2_ssl_op_type res = 0; for (auto &supported : TLS_PROTOS) { auto ok = false; for (auto &name : tls_proto_list) { if (util::strieq(supported.name, name)) { ok = true; break; } } if (!ok) { res |= supported.mask; } } return res; } namespace { int get_cert_type(SSL_CTX *ssl_ctx) { auto cert = SSL_CTX_get0_certificate(ssl_ctx); #ifndef NGHTTP2_OPENSSL_IS_WOLFSSL auto pubkey = X509_get0_pubkey(cert); # if OPENSSL_3_5_0_API if (EVP_PKEY_is_a(pubkey, "ML-DSA-44")) { return EVP_PKEY_ML_DSA_44; } if (EVP_PKEY_is_a(pubkey, "ML-DSA-65")) { return EVP_PKEY_ML_DSA_65; } if (EVP_PKEY_is_a(pubkey, "ML-DSA-87")) { return EVP_PKEY_ML_DSA_87; } # endif // OPENSSL_3_5_0_API return EVP_PKEY_base_id(pubkey); #else // defined(NGHTTP2_OPENSSL_IS_WOLFSSL) return wolfSSL_X509_get_pubkey_type(cert); #endif // defined(NGHTTP2_OPENSSL_IS_WOLFSSL) } } // namespace SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file, const std::vector &sct_data #ifdef HAVE_NEVERBLEED , neverbleed_t *nb #endif // defined(HAVE_NEVERBLEED) ) { auto ssl_ctx = SSL_CTX_new(TLS_server_method()); if (!ssl_ctx) { LOG(FATAL) << ERR_error_string(ERR_get_error(), nullptr); DIE(); } auto ssl_opts = static_cast( (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_SINGLE_ECDH_USE | SSL_OP_SINGLE_DH_USE | SSL_OP_CIPHER_SERVER_PREFERENCE #ifdef NGHTTP2_GENUINE_OPENSSL // The reason for disabling built-in anti-replay in // OpenSSL is that it only works if client gets back // to the same server. The freshness check // described in // https://tools.ietf.org/html/rfc8446#section-8.3 // is still performed. | SSL_OP_NO_ANTI_REPLAY #endif // defined(NGHTTP2_GENUINE_OPENSSL) ); auto config = mod_config(); auto &tlsconf = config->tls; #ifdef SSL_OP_ENABLE_KTLS if (tlsconf.ktls) { ssl_opts |= SSL_OP_ENABLE_KTLS; } #endif // defined(SSL_OP_ENABLE_KTLS) SSL_CTX_set_options(ssl_ctx, ssl_opts | tlsconf.tls_proto_mask); if (nghttp2::tls::ssl_ctx_set_proto_versions( ssl_ctx, tlsconf.min_proto_version, tlsconf.max_proto_version) != 0) { LOG(FATAL) << "Could not set TLS protocol version"; DIE(); } const unsigned char sid_ctx[] = "shrpx"; SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx) - 1); SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER); SSL_CTX_set_timeout(ssl_ctx, static_cast( tlsconf.session_timeout.count())); if (SSL_CTX_set_cipher_list(ssl_ctx, tlsconf.ciphers.data()) == 0) { LOG(FATAL) << "SSL_CTX_set_cipher_list " << tlsconf.ciphers << " failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } #if defined(NGHTTP2_GENUINE_OPENSSL) || \ defined(NGHTTP2_OPENSSL_IS_LIBRESSL) || defined(NGHTTP2_OPENSSL_IS_WOLFSSL) if (SSL_CTX_set_ciphersuites(ssl_ctx, tlsconf.tls13_ciphers.data()) == 0) { LOG(FATAL) << "SSL_CTX_set_ciphersuites " << tlsconf.tls13_ciphers << " failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } #endif // defined(NGHTTP2_GENUINE_OPENSSL) || // defined(NGHTTP2_OPENSSL_IS_LIBRESSL) || // defined(NGHTTP2_OPENSSL_IS_WOLFSSL) if (SSL_CTX_set1_groups_list(ssl_ctx, tlsconf.groups.data()) != 1) { LOG(FATAL) << "SSL_CTX_set1_groups_list " << tlsconf.groups << " failed"; DIE(); } if (!tlsconf.dh_param_file.empty()) { // Read DH parameters from file auto bio = BIO_new_file(tlsconf.dh_param_file.data(), "rb"); if (bio == nullptr) { LOG(FATAL) << "BIO_new_file() failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } #if OPENSSL_3_0_0_API EVP_PKEY *dh = nullptr; auto dctx = OSSL_DECODER_CTX_new_for_pkey( &dh, "PEM", nullptr, "DH", OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS, nullptr, nullptr); if (!OSSL_DECODER_from_bio(dctx, bio)) { LOG(FATAL) << "OSSL_DECODER_from_bio() failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } if (SSL_CTX_set0_tmp_dh_pkey(ssl_ctx, dh) != 1) { LOG(FATAL) << "SSL_CTX_set0_tmp_dh_pkey failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } #else // !OPENSSL_3_0_0_API auto dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr); if (dh == nullptr) { LOG(FATAL) << "PEM_read_bio_DHparams() failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } SSL_CTX_set_tmp_dh(ssl_ctx, dh); DH_free(dh); #endif // !OPENSSL_3_0_0_API BIO_free(bio); } SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); if (SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) { LOG(WARN) << "Could not load system trusted ca certificates: " << ERR_error_string(ERR_get_error(), nullptr); } if (!tlsconf.cacert.empty()) { if (SSL_CTX_load_verify_locations(ssl_ctx, tlsconf.cacert.data(), nullptr) != 1) { LOG(FATAL) << "Could not load trusted ca certificates from " << tlsconf.cacert << ": " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } } if (!tlsconf.private_key_passwd.empty()) { SSL_CTX_set_default_passwd_cb(ssl_ctx, ssl_pem_passwd_cb); SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, config); } #ifndef HAVE_NEVERBLEED if (SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key_file, SSL_FILETYPE_PEM) != 1) { LOG(FATAL) << "SSL_CTX_use_PrivateKey_file failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } #else // defined(HAVE_NEVERBLEED) std::array errbuf; if (neverbleed_load_private_key_file(nb, ssl_ctx, private_key_file, errbuf.data()) != 1) { LOG(FATAL) << "neverbleed_load_private_key_file failed: " << errbuf.data(); DIE(); } #endif // defined(HAVE_NEVERBLEED) if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) { LOG(FATAL) << "SSL_CTX_use_certificate_file failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } if (SSL_CTX_check_private_key(ssl_ctx) != 1) { LOG(FATAL) << "SSL_CTX_check_private_key failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } if (tlsconf.client_verify.enabled) { if (!tlsconf.client_verify.cacert.empty()) { if (SSL_CTX_load_verify_locations( ssl_ctx, tlsconf.client_verify.cacert.data(), nullptr) != 1) { LOG(FATAL) << "Could not load trusted ca certificates from " << tlsconf.client_verify.cacert << ": " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } // It is heard that SSL_CTX_load_verify_locations() may leave // error even though it returns success. See // http://forum.nginx.org/read.php?29,242540 ERR_clear_error(); auto list = SSL_load_client_CA_file(tlsconf.client_verify.cacert.data()); if (!list) { LOG(FATAL) << "Could not load ca certificates from " << tlsconf.client_verify.cacert << ": " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } SSL_CTX_set_client_CA_list(ssl_ctx, list); } SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback); } SSL_CTX_set_tlsext_servername_callback(ssl_ctx, servername_callback); #if defined(NGHTTP2_OPENSSL_IS_BORINGSSL) || defined(NGHTTP2_OPENSSL_IS_WOLFSSL) SSL_CTX_set_cert_cb(ssl_ctx, cert_cb, nullptr); #endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) || // defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #if OPENSSL_3_0_0_API SSL_CTX_set_tlsext_ticket_key_evp_cb(ssl_ctx, ticket_key_cb); #else // !OPENSSL_3_0_0_API SSL_CTX_set_tlsext_ticket_key_cb(ssl_ctx, ticket_key_cb); #endif // !OPENSSL_3_0_0_API SSL_CTX_set_info_callback(ssl_ctx, info_callback); #ifdef NGHTTP2_OPENSSL_IS_BORINGSSL SSL_CTX_set_early_data_enabled(ssl_ctx, 1); #endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) // ALPN selection callback SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, nullptr); auto tls_ctx_data = new TLSContextData{ .sct_data = sct_data, .cert_type = get_cert_type(ssl_ctx), }; SSL_CTX_set_app_data(ssl_ctx, tls_ctx_data); #ifdef NGHTTP2_GENUINE_OPENSSL // SSL_extension_supported(TLSEXT_TYPE_signed_certificate_timestamp) // returns 1, which means OpenSSL internally handles it. But // OpenSSL handles signed_certificate_timestamp extension specially, // and it lets custom handler to process the extension. if (!sct_data.empty()) { // It is not entirely clear to me that SSL_EXT_CLIENT_HELLO is // required here. sct_parse_cb is called without // SSL_EXT_CLIENT_HELLO being set. But the passed context value // is SSL_EXT_CLIENT_HELLO. if (SSL_CTX_add_custom_ext( ssl_ctx, TLSEXT_TYPE_signed_certificate_timestamp, SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO | SSL_EXT_TLS1_3_CERTIFICATE | SSL_EXT_IGNORE_ON_RESUMPTION, sct_add_cb, sct_free_cb, nullptr, sct_parse_cb, nullptr) != 1) { LOG(FATAL) << "SSL_CTX_add_custom_ext failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } } #elif defined(NGHTTP2_OPENSSL_IS_BORINGSSL) if (!tls_ctx_data->sct_data.empty() && SSL_CTX_set_signed_cert_timestamp_list( ssl_ctx, tls_ctx_data->sct_data.data(), tls_ctx_data->sct_data.size()) != 1) { LOG(FATAL) << "SSL_CTX_set_signed_cert_timestamp_list failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } #endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) #if defined(NGHTTP2_GENUINE_OPENSSL) || \ (defined(NGHTTP2_OPENSSL_IS_WOLFSSL) && defined(WOLFSSL_EARLY_DATA)) if (SSL_CTX_set_max_early_data(ssl_ctx, tlsconf.max_early_data) != 1) { LOG(FATAL) << "SSL_CTX_set_max_early_data failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } #endif // defined(NGHTTP2_GENUINE_OPENSSL) || // (defined(NGHTTP2_OPENSSL_IS_WOLFSSL) && // defined(WOLFSSL_EARLY_DATA)) #ifdef NGHTTP2_GENUINE_OPENSSL if (SSL_CTX_set_recv_max_early_data(ssl_ctx, tlsconf.max_early_data) != 1) { LOG(FATAL) << "SSL_CTX_set_recv_max_early_data failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } #endif // defined(NGHTTP2_GENUINE_OPENSSL) #ifndef OPENSSL_NO_PSK SSL_CTX_set_psk_server_callback(ssl_ctx, psk_server_cb); #endif // !defined(OPENSSL_NO_PSK) #if defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && defined(HAVE_LIBBROTLI) if (!SSL_CTX_add_cert_compression_alg( ssl_ctx, nghttp2::tls::CERTIFICATE_COMPRESSION_ALGO_BROTLI, cert_compress, cert_decompress)) { LOG(FATAL) << "SSL_CTX_add_cert_compression_alg failed"; DIE(); } #endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && // defined(HAVE_LIBBROTLI) return ssl_ctx; } #ifdef ENABLE_HTTP3 SSL_CTX *create_quic_ssl_context(const char *private_key_file, const char *cert_file, const std::vector &sct_data # ifdef HAVE_NEVERBLEED , neverbleed_t *nb # endif // defined(HAVE_NEVERBLEED) ) { auto ssl_ctx = SSL_CTX_new(TLS_server_method()); if (!ssl_ctx) { LOG(FATAL) << ERR_error_string(ERR_get_error(), nullptr); DIE(); } constexpr auto ssl_opts = (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_SINGLE_ECDH_USE | SSL_OP_SINGLE_DH_USE | SSL_OP_CIPHER_SERVER_PREFERENCE # ifdef NGHTTP2_GENUINE_OPENSSL // The reason for disabling built-in anti-replay in OpenSSL is // that it only works if client gets back to the same server. // The freshness check described in // https://tools.ietf.org/html/rfc8446#section-8.3 is still // performed. | SSL_OP_NO_ANTI_REPLAY # endif // defined(NGHTTP2_GENUINE_OPENSSL) ; auto config = mod_config(); auto &tlsconf = config->tls; SSL_CTX_set_options(ssl_ctx, ssl_opts); # if defined(HAVE_LIBNGTCP2_CRYPTO_QUICTLS) || \ defined(HAVE_LIBNGTCP2_CRYPTO_LIBRESSL) if (ngtcp2_crypto_quictls_configure_server_context(ssl_ctx) != 0) { LOG(FATAL) << "ngtcp2_crypto_quictls_configure_server_context failed"; DIE(); } # endif // defined(HAVE_LIBNGTCP2_CRYPTO_QUICTLS) || // defined(HAVE_LIBNGTCP2_CRYPTO_LIBRESSL) # ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL if (ngtcp2_crypto_boringssl_configure_server_context(ssl_ctx) != 0) { LOG(FATAL) << "ngtcp2_crypto_boringssl_configure_server_context failed"; DIE(); } # endif // defined(HAVE_LIBNGTCP2_CRYPTO_BORINGSSL) # ifdef HAVE_LIBNGTCP2_CRYPTO_WOLFSSL if (ngtcp2_crypto_wolfssl_configure_server_context(ssl_ctx) != 0) { LOG(FATAL) << "ngtcp2_crypto_wolfssl_configure_server_context failed"; DIE(); } # endif // defined(HAVE_LIBNGTCP2_CRYPTO_WOLFSSL) const unsigned char sid_ctx[] = "shrpx"; SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx) - 1); SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_OFF); SSL_CTX_set_timeout(ssl_ctx, static_cast( tlsconf.session_timeout.count())); if (SSL_CTX_set_cipher_list(ssl_ctx, tlsconf.ciphers.data()) == 0) { LOG(FATAL) << "SSL_CTX_set_cipher_list " << tlsconf.ciphers << " failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } # if defined(NGHTTP2_GENUINE_OPENSSL) || \ defined(NGHTTP2_OPENSSL_IS_LIBRESSL) || \ defined(NGHTTP2_OPENSSL_IS_WOLFSSL) if (SSL_CTX_set_ciphersuites(ssl_ctx, tlsconf.tls13_ciphers.data()) == 0) { LOG(FATAL) << "SSL_CTX_set_ciphersuites " << tlsconf.tls13_ciphers << " failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } # endif // defined(NGHTTP2_GENUINE_OPENSSL) || // defined(NGHTTP2_OPENSSL_IS_LIBRESSL) || // defined(NGHTTP2_OPENSSL_IS_WOLFSSL) if (SSL_CTX_set1_groups_list(ssl_ctx, tlsconf.groups.data()) != 1) { LOG(FATAL) << "SSL_CTX_set1_groups_list " << tlsconf.groups << " failed"; DIE(); } if (!tlsconf.dh_param_file.empty()) { // Read DH parameters from file auto bio = BIO_new_file(tlsconf.dh_param_file.data(), "rb"); if (bio == nullptr) { LOG(FATAL) << "BIO_new_file() failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } # if OPENSSL_3_0_0_API EVP_PKEY *dh = nullptr; auto dctx = OSSL_DECODER_CTX_new_for_pkey( &dh, "PEM", nullptr, "DH", OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS, nullptr, nullptr); if (!OSSL_DECODER_from_bio(dctx, bio)) { LOG(FATAL) << "OSSL_DECODER_from_bio() failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } if (SSL_CTX_set0_tmp_dh_pkey(ssl_ctx, dh) != 1) { LOG(FATAL) << "SSL_CTX_set0_tmp_dh_pkey failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } # else // !OPENSSL_3_0_0_API auto dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr); if (dh == nullptr) { LOG(FATAL) << "PEM_read_bio_DHparams() failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } SSL_CTX_set_tmp_dh(ssl_ctx, dh); DH_free(dh); # endif // !OPENSSL_3_0_0_API BIO_free(bio); } SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); if (SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) { LOG(WARN) << "Could not load system trusted ca certificates: " << ERR_error_string(ERR_get_error(), nullptr); } if (!tlsconf.cacert.empty()) { if (SSL_CTX_load_verify_locations(ssl_ctx, tlsconf.cacert.data(), nullptr) != 1) { LOG(FATAL) << "Could not load trusted ca certificates from " << tlsconf.cacert << ": " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } } if (!tlsconf.private_key_passwd.empty()) { SSL_CTX_set_default_passwd_cb(ssl_ctx, ssl_pem_passwd_cb); SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, config); } # ifndef HAVE_NEVERBLEED if (SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key_file, SSL_FILETYPE_PEM) != 1) { LOG(FATAL) << "SSL_CTX_use_PrivateKey_file failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } # else // defined(HAVE_NEVERBLEED) std::array errbuf; if (neverbleed_load_private_key_file(nb, ssl_ctx, private_key_file, errbuf.data()) != 1) { LOG(FATAL) << "neverbleed_load_private_key_file failed: " << errbuf.data(); DIE(); } # endif // defined(HAVE_NEVERBLEED) if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) { LOG(FATAL) << "SSL_CTX_use_certificate_file failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } if (SSL_CTX_check_private_key(ssl_ctx) != 1) { LOG(FATAL) << "SSL_CTX_check_private_key failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } if (tlsconf.client_verify.enabled) { if (!tlsconf.client_verify.cacert.empty()) { if (SSL_CTX_load_verify_locations( ssl_ctx, tlsconf.client_verify.cacert.data(), nullptr) != 1) { LOG(FATAL) << "Could not load trusted ca certificates from " << tlsconf.client_verify.cacert << ": " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } // It is heard that SSL_CTX_load_verify_locations() may leave // error even though it returns success. See // http://forum.nginx.org/read.php?29,242540 ERR_clear_error(); auto list = SSL_load_client_CA_file(tlsconf.client_verify.cacert.data()); if (!list) { LOG(FATAL) << "Could not load ca certificates from " << tlsconf.client_verify.cacert << ": " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } SSL_CTX_set_client_CA_list(ssl_ctx, list); } SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback); } SSL_CTX_set_tlsext_servername_callback(ssl_ctx, servername_callback); # if defined(NGHTTP2_OPENSSL_IS_BORINGSSL) || \ defined(NGHTTP2_OPENSSL_IS_WOLFSSL) SSL_CTX_set_cert_cb(ssl_ctx, cert_cb, nullptr); # endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) || // defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # if OPENSSL_3_0_0_API SSL_CTX_set_tlsext_ticket_key_evp_cb(ssl_ctx, ticket_key_cb); # else // !OPENSSL_3_0_0_API SSL_CTX_set_tlsext_ticket_key_cb(ssl_ctx, ticket_key_cb); # endif // !OPENSSL_3_0_0_API // ALPN selection callback SSL_CTX_set_alpn_select_cb(ssl_ctx, quic_alpn_select_proto_cb, nullptr); auto tls_ctx_data = new TLSContextData{ .sct_data = sct_data, .cert_type = get_cert_type(ssl_ctx), }; SSL_CTX_set_app_data(ssl_ctx, tls_ctx_data); # ifdef NGHTTP2_GENUINE_OPENSSL // SSL_extension_supported(TLSEXT_TYPE_signed_certificate_timestamp) // returns 1, which means OpenSSL internally handles it. But // OpenSSL handles signed_certificate_timestamp extension specially, // and it lets custom handler to process the extension. if (!sct_data.empty()) { // It is not entirely clear to me that SSL_EXT_CLIENT_HELLO is // required here. sct_parse_cb is called without // SSL_EXT_CLIENT_HELLO being set. But the passed context value // is SSL_EXT_CLIENT_HELLO. if (SSL_CTX_add_custom_ext( ssl_ctx, TLSEXT_TYPE_signed_certificate_timestamp, SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO | SSL_EXT_TLS1_3_CERTIFICATE | SSL_EXT_IGNORE_ON_RESUMPTION, sct_add_cb, sct_free_cb, nullptr, sct_parse_cb, nullptr) != 1) { LOG(FATAL) << "SSL_CTX_add_custom_ext failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } } # elif defined(NGHTTP2_OPENSSL_IS_BORINGSSL) if (!tls_ctx_data->sct_data.empty() && SSL_CTX_set_signed_cert_timestamp_list( ssl_ctx, tls_ctx_data->sct_data.data(), tls_ctx_data->sct_data.size()) != 1) { LOG(FATAL) << "SSL_CTX_set_signed_cert_timestamp_list failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } # endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) # if defined(NGHTTP2_GENUINE_OPENSSL) || \ (defined(NGHTTP2_OPENSSL_IS_WOLFSSL) && defined(WOLFSSL_EARLY_DATA)) auto &quicconf = config->quic; if (quicconf.upstream.early_data && SSL_CTX_set_max_early_data(ssl_ctx, std::numeric_limits::max()) != 1) { LOG(FATAL) << "SSL_CTX_set_max_early_data failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } # endif // defined(NGHTTP2_GENUINE_OPENSSL) || // (defined(NGHTTP2_OPENSSL_IS_WOLFSSL) && // defined(WOLFSSL_EARLY_DATA)) # ifndef OPENSSL_NO_PSK SSL_CTX_set_psk_server_callback(ssl_ctx, psk_server_cb); # endif // !defined(OPENSSL_NO_PSK) # if defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && defined(HAVE_LIBBROTLI) if (!SSL_CTX_add_cert_compression_alg( ssl_ctx, nghttp2::tls::CERTIFICATE_COMPRESSION_ALGO_BROTLI, cert_compress, cert_decompress)) { LOG(FATAL) << "SSL_CTX_add_cert_compression_alg failed"; DIE(); } # endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && // defined(HAVE_LIBBROTLI) return ssl_ctx; } #endif // defined(ENABLE_HTTP3) SSL_CTX *create_ssl_client_context( #ifdef HAVE_NEVERBLEED neverbleed_t *nb, #endif // defined(HAVE_NEVERBLEED) const std::string_view &cacert, const std::string_view &cert_file, const std::string_view &private_key_file) { auto ssl_ctx = SSL_CTX_new(TLS_client_method()); if (!ssl_ctx) { LOG(FATAL) << ERR_error_string(ERR_get_error(), nullptr); DIE(); } auto ssl_opts = static_cast( (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); auto &tlsconf = get_config()->tls; #ifdef SSL_OP_ENABLE_KTLS if (tlsconf.ktls) { ssl_opts |= SSL_OP_ENABLE_KTLS; } #endif // defined(SSL_OP_ENABLE_KTLS) SSL_CTX_set_options(ssl_ctx, ssl_opts | tlsconf.tls_proto_mask); SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL_STORE); SSL_CTX_sess_set_new_cb(ssl_ctx, tls_session_client_new_cb); if (nghttp2::tls::ssl_ctx_set_proto_versions( ssl_ctx, tlsconf.min_proto_version, tlsconf.max_proto_version) != 0) { LOG(FATAL) << "Could not set TLS protocol version"; DIE(); } if (SSL_CTX_set_cipher_list(ssl_ctx, tlsconf.client.ciphers.data()) == 0) { LOG(FATAL) << "SSL_CTX_set_cipher_list " << tlsconf.client.ciphers << " failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } #if defined(NGHTTP2_GENUINE_OPENSSL) || \ defined(NGHTTP2_OPENSSL_IS_LIBRESSL) || defined(NGHTTP2_OPENSSL_IS_WOLFSSL) if (SSL_CTX_set_ciphersuites(ssl_ctx, tlsconf.client.tls13_ciphers.data()) == 0) { LOG(FATAL) << "SSL_CTX_set_ciphersuites " << tlsconf.client.tls13_ciphers << " failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } #endif // defined(NGHTTP2_GENUINE_OPENSSL) || // defined(NGHTTP2_OPENSSL_IS_LIBRESSL) || // defined(NGHTTP2_OPENSSL_IS_WOLFSSL) SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); if (SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) { LOG(WARN) << "Could not load system trusted ca certificates: " << ERR_error_string(ERR_get_error(), nullptr); } if (!cacert.empty()) { if (SSL_CTX_load_verify_locations(ssl_ctx, cacert.data(), nullptr) != 1) { LOG(FATAL) << "Could not load trusted ca certificates from " << cacert << ": " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } } if (!tlsconf.insecure) { SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, nullptr); } if (!cert_file.empty()) { if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file.data()) != 1) { LOG(FATAL) << "Could not load client certificate from " << cert_file << ": " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } } if (!private_key_file.empty()) { #ifndef HAVE_NEVERBLEED if (SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key_file.data(), SSL_FILETYPE_PEM) != 1) { LOG(FATAL) << "Could not load client private key from " << private_key_file << ": " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } #else // defined(HAVE_NEVERBLEED) std::array errbuf; if (neverbleed_load_private_key_file(nb, ssl_ctx, private_key_file.data(), errbuf.data()) != 1) { LOG(FATAL) << "neverbleed_load_private_key_file: could not load client " "private key from " << private_key_file << ": " << errbuf.data(); DIE(); } #endif // defined(HAVE_NEVERBLEED) } #ifndef OPENSSL_NO_PSK SSL_CTX_set_psk_client_callback(ssl_ctx, psk_client_cb); #endif // !defined(OPENSSL_NO_PSK) #if defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && defined(HAVE_LIBBROTLI) if (!SSL_CTX_add_cert_compression_alg( ssl_ctx, nghttp2::tls::CERTIFICATE_COMPRESSION_ALGO_BROTLI, cert_compress, cert_decompress)) { LOG(FATAL) << "SSL_CTX_add_cert_compression_alg failed"; DIE(); } #endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && // defined(HAVE_LIBBROTLI) return ssl_ctx; } SSL *create_ssl(SSL_CTX *ssl_ctx) { auto ssl = SSL_new(ssl_ctx); if (!ssl) { LOG(ERROR) << "SSL_new() failed: " << ERR_error_string(ERR_get_error(), nullptr); return nullptr; } return ssl; } ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr, socklen_t addrlen, const UpstreamAddr *faddr) { std::array host; std::array service; int rv; if (addr->sa_family == AF_UNIX) { *std::ranges::copy("localhost"sv, std::ranges::begin(host)).out = '\0'; service[0] = '\0'; } else { rv = getnameinfo(addr, addrlen, host.data(), host.size(), service.data(), service.size(), NI_NUMERICHOST | NI_NUMERICSERV); if (rv != 0) { LOG(ERROR) << "getnameinfo() failed: " << gai_strerror(rv); return nullptr; } rv = util::make_socket_nodelay(fd); if (rv == -1) { LOG(WARN) << "Setting option TCP_NODELAY failed: errno=" << errno; } } SSL *ssl = nullptr; if (faddr->tls) { auto ssl_ctx = worker->get_sv_ssl_ctx(); assert(ssl_ctx); ssl = create_ssl(ssl_ctx); if (!ssl) { return nullptr; } // Disable TLS session ticket if we don't have working ticket // keys. if (!worker->get_ticket_keys()) { SSL_set_options(ssl, SSL_OP_NO_TICKET); } } auto handler = new ClientHandler(worker, fd, ssl, std::string_view{host.data()}, std::string_view{service.data()}, addr->sa_family, faddr); auto config = get_config(); auto &fwdconf = config->http.forwarded; if (addr->sa_family != AF_UNIX && fwdconf.params & FORWARDED_BY) { sockaddr_union su; socklen_t sulen = sizeof(su); if (getsockname(fd, &su.sa, &sulen) == 0) { handler->set_local_hostport(&su.sa, sulen); } } return handler; } bool tls_hostname_match(const std::string_view &pattern, const std::string_view &hostname) { auto ptWildcard = std::ranges::find(pattern, '*'); if (ptWildcard == std::ranges::end(pattern)) { return util::strieq(pattern, hostname); } auto ptLeftLabelEnd = std::ranges::find(pattern, '.'); auto wildcardEnabled = true; // Do case-insensitive match. At least 2 dots are required to enable // wildcard match. Also wildcard must be in the left-most label. // Don't attempt to match a presented identifier where the wildcard // character is embedded within an A-label. if (ptLeftLabelEnd == std::ranges::end(pattern) || !util::contains(ptLeftLabelEnd + 1, std::ranges::end(pattern), '.') || ptLeftLabelEnd < ptWildcard || util::istarts_with(pattern, "xn--"sv)) { wildcardEnabled = false; } if (!wildcardEnabled) { return util::strieq(pattern, hostname); } auto hnLeftLabelEnd = std::ranges::find(hostname, '.'); if (hnLeftLabelEnd == std::ranges::end(hostname) || !util::strieq( std::string_view{ptLeftLabelEnd, std::ranges::end(pattern)}, std::string_view{hnLeftLabelEnd, std::ranges::end(hostname)})) { return false; } // Perform wildcard match. Here '*' must match at least one // character. if (hnLeftLabelEnd - std::ranges::begin(hostname) < ptLeftLabelEnd - std::ranges::begin(pattern)) { return false; } return util::istarts_with( std::string_view{std::ranges::begin(hostname), hnLeftLabelEnd}, std::string_view{std::ranges::begin(pattern), ptWildcard}) && util::iends_with( std::string_view{std::ranges::begin(hostname), hnLeftLabelEnd}, std::string_view{ptWildcard + 1, ptLeftLabelEnd}); } namespace { // if return value is not empty, std::string_view.data() must be freed // using OPENSSL_free(). std::string_view get_common_name(X509 *cert) { auto subjectname = X509_get_subject_name(cert); if (!subjectname) { LOG(WARN) << "Could not get X509 name object from the certificate."; return ""sv; } int lastpos = -1; for (;;) { lastpos = X509_NAME_get_index_by_NID(subjectname, NID_commonName, lastpos); if (lastpos == -1) { break; } auto entry = X509_NAME_get_entry(subjectname, lastpos); unsigned char *p; auto plen = ASN1_STRING_to_UTF8(&p, X509_NAME_ENTRY_get_data(entry)); if (plen < 0) { continue; } if (util::contains(p, p + plen, '\0')) { // Embedded NULL is not permitted. continue; } if (plen == 0) { LOG(WARN) << "X509 name is empty"; OPENSSL_free(p); continue; } return as_string_view(p, static_cast(plen)); } return ""sv; } } // namespace int verify_numeric_hostname(X509 *cert, const std::string_view &hostname, const Address *addr) { const void *saddr; size_t saddrlen; switch (addr->su.storage.ss_family) { case AF_INET: saddr = &addr->su.in.sin_addr; saddrlen = sizeof(addr->su.in.sin_addr); break; case AF_INET6: saddr = &addr->su.in6.sin6_addr; saddrlen = sizeof(addr->su.in6.sin6_addr); break; default: return -1; } auto altnames = static_cast( X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr)); if (altnames) { auto altnames_deleter = defer(GENERAL_NAMES_free, altnames); auto n = static_cast(sk_GENERAL_NAME_num(altnames)); auto ip_found = false; for (size_t i = 0; i < n; ++i) { auto altname = sk_GENERAL_NAME_value( altnames, static_cast(i)); if (altname->type != GEN_IPADD) { continue; } auto ip_addr = altname->d.iPAddress->data; if (!ip_addr) { continue; } auto ip_addrlen = static_cast(altname->d.iPAddress->length); ip_found = true; if (saddrlen == ip_addrlen && memcmp(saddr, ip_addr, ip_addrlen) == 0) { return 0; } } if (ip_found) { return -1; } } auto cn = get_common_name(cert); if (cn.empty()) { return -1; } // cn is not NULL terminated auto rv = hostname == cn; OPENSSL_free(const_cast(cn.data())); if (rv) { return 0; } return -1; } int verify_dns_hostname(X509 *cert, const std::string_view &hostname) { auto altnames = static_cast( X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr)); if (altnames) { auto dns_found = false; auto altnames_deleter = defer(GENERAL_NAMES_free, altnames); auto n = static_cast(sk_GENERAL_NAME_num(altnames)); for (size_t i = 0; i < n; ++i) { auto altname = sk_GENERAL_NAME_value( altnames, static_cast(i)); if (altname->type != GEN_DNS) { continue; } auto name = ASN1_STRING_get0_data(altname->d.ia5); if (!name) { continue; } auto len = ASN1_STRING_length(altname->d.ia5); if (len == 0) { continue; } if (util::contains(name, name + len, '\0')) { // Embedded NULL is not permitted. continue; } if (name[len - 1] == '.') { --len; if (len == 0) { continue; } } dns_found = true; if (tls_hostname_match(as_string_view(name, static_cast(len)), hostname)) { return 0; } } // RFC 6125, section 6.4.4. says that client MUST not seek a match // for CN if a dns dNSName is found. if (dns_found) { return -1; } } auto cn = get_common_name(cert); if (cn.empty()) { return -1; } if (cn[cn.size() - 1] == '.') { if (cn.size() == 1) { OPENSSL_free(const_cast(cn.data())); return -1; } cn = std::string_view{cn.data(), cn.size() - 1}; } auto rv = tls_hostname_match(cn, hostname); OPENSSL_free(const_cast(cn.data())); return rv ? 0 : -1; } namespace { int verify_hostname(X509 *cert, const std::string_view &hostname, const Address *addr) { if (util::numeric_host(hostname.data())) { return verify_numeric_hostname(cert, hostname, addr); } return verify_dns_hostname(cert, hostname); } } // namespace int check_cert(SSL *ssl, const Address *addr, const std::string_view &host) { #if OPENSSL_3_0_0_API auto cert = SSL_get0_peer_certificate(ssl); #else // !OPENSSL_3_0_0_API auto cert = SSL_get_peer_certificate(ssl); #endif // !OPENSSL_3_0_0_API if (!cert) { // By the protocol definition, TLS server always sends certificate // if it has. If certificate cannot be retrieved, authentication // without certificate is used, such as PSK. return 0; } #if !OPENSSL_3_0_0_API auto cert_deleter = defer(X509_free, cert); #endif // !OPENSSL_3_0_0_API if (verify_hostname(cert, host, addr) != 0) { LOG(ERROR) << "Certificate verification failed: hostname does not match"; return -1; } return 0; } int check_cert(SSL *ssl, const DownstreamAddr *addr, const Address *raddr) { auto hostname = addr->sni.empty() ? addr->host : addr->sni; return check_cert(ssl, raddr, hostname); } CertLookupTree::CertLookupTree() {} ssize_t CertLookupTree::add_cert(const std::string_view &hostname, size_t idx) { std::array buf; // NI_MAXHOST includes terminal NULL byte if (hostname.empty() || hostname.size() + 1 > buf.size()) { return -1; } auto wildcard_it = std::ranges::find(hostname, '*'); if (wildcard_it != std::ranges::end(hostname) && wildcard_it + 1 != std::ranges::end(hostname)) { auto wildcard_prefix = std::string_view{std::ranges::begin(hostname), wildcard_it}; auto wildcard_suffix = std::string_view{wildcard_it + 1, std::ranges::end(hostname)}; auto rev_suffix = std::string_view{ std::ranges::begin(buf), std::ranges::reverse_copy(wildcard_suffix, std::ranges::begin(buf)).out}; WildcardPattern *wpat; if (wildcard_patterns_.size() != rev_wildcard_router_.add_route(rev_suffix, wildcard_patterns_.size())) { auto wcidx = rev_wildcard_router_.match(rev_suffix); assert(wcidx != -1); wpat = &wildcard_patterns_[as_unsigned(wcidx)]; } else { wildcard_patterns_.emplace_back(); wpat = &wildcard_patterns_.back(); } auto rev_prefix = std::string_view{ std::ranges::begin(buf), std::ranges::reverse_copy(wildcard_prefix, std::ranges::begin(buf)).out}; for (auto &p : wpat->rev_prefix) { if (p.prefix == rev_prefix) { return as_signed(p.idx); } } wpat->rev_prefix.emplace_back(rev_prefix, idx); return as_signed(idx); } return as_signed(router_.add_route(hostname, idx)); } ssize_t CertLookupTree::lookup(const std::string_view &hostname) { std::array buf; // NI_MAXHOST includes terminal NULL byte if (hostname.empty() || hostname.size() + 1 > buf.size()) { return -1; } // Always prefer exact match auto idx = router_.match(hostname); if (idx != -1) { return idx; } if (wildcard_patterns_.empty()) { return -1; } ssize_t best_idx = -1; size_t best_prefixlen = 0; const RNode *last_node = nullptr; auto rev_host = std::string_view{ std::ranges::begin(buf), std::ranges::reverse_copy(hostname, std::ranges::begin(buf)).out}; for (;;) { size_t nread = 0; auto wcidx = rev_wildcard_router_.match_prefix(&nread, &last_node, rev_host); if (wcidx == -1) { return best_idx; } // '*' must match at least one byte if (nread == rev_host.size()) { return best_idx; } rev_host = std::string_view{std::ranges::begin(rev_host) + nread, std::ranges::end(rev_host)}; auto rev_prefix = std::string_view{std::ranges::begin(rev_host) + 1, std::ranges::end(rev_host)}; auto &wpat = wildcard_patterns_[as_unsigned(wcidx)]; for (auto &wprefix : wpat.rev_prefix) { if (!util::ends_with(rev_prefix, wprefix.prefix)) { continue; } auto prefixlen = wprefix.prefix.size() + as_unsigned(&rev_host[0] - &buf[0]); // Breaking a tie with longer suffix if (prefixlen < best_prefixlen) { continue; } best_idx = as_signed(wprefix.idx); best_prefixlen = prefixlen; } } } void CertLookupTree::dump() const { std::cerr << "exact:" << std::endl; router_.dump(); std::cerr << "wildcard suffix (reversed):" << std::endl; rev_wildcard_router_.dump(); } int cert_lookup_tree_add_ssl_ctx( CertLookupTree *lt, std::vector> &indexed_ssl_ctx, SSL_CTX *ssl_ctx) { std::array buf; auto cert = SSL_CTX_get0_certificate(ssl_ctx); auto altnames = static_cast( X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr)); if (altnames) { auto altnames_deleter = defer(GENERAL_NAMES_free, altnames); auto n = static_cast(sk_GENERAL_NAME_num(altnames)); auto dns_found = false; for (size_t i = 0; i < n; ++i) { auto altname = sk_GENERAL_NAME_value( altnames, static_cast(i)); if (altname->type != GEN_DNS) { continue; } auto name = ASN1_STRING_get0_data(altname->d.ia5); if (!name) { continue; } auto len = ASN1_STRING_length(altname->d.ia5); if (len == 0) { continue; } if (util::contains(name, name + len, '\0')) { // Embedded NULL is not permitted. continue; } if (name[len - 1] == '.') { --len; if (len == 0) { continue; } } dns_found = true; if (static_cast(len) + 1 > buf.size()) { continue; } auto end_buf = util::tolower(name, name + len, std::ranges::begin(buf)); auto idx = lt->add_cert(std::string_view{std::ranges::begin(buf), end_buf}, indexed_ssl_ctx.size()); if (idx == -1) { continue; } if (static_cast(idx) < indexed_ssl_ctx.size()) { indexed_ssl_ctx[as_unsigned(idx)].push_back(ssl_ctx); } else { assert(static_cast(idx) == indexed_ssl_ctx.size()); indexed_ssl_ctx.emplace_back(std::vector{ssl_ctx}); } } // Don't bother CN if we have dNSName. if (dns_found) { return 0; } } auto cn = get_common_name(cert); if (cn.empty()) { return 0; } if (cn[cn.size() - 1] == '.') { if (cn.size() == 1) { OPENSSL_free(const_cast(cn.data())); return 0; } cn = std::string_view{cn.data(), cn.size() - 1}; } auto end_buf = util::tolower(cn, std::ranges::begin(buf)); OPENSSL_free(const_cast(cn.data())); auto idx = lt->add_cert(std::string_view{std::ranges::begin(buf), end_buf}, indexed_ssl_ctx.size()); if (idx == -1) { return 0; } if (static_cast(idx) < indexed_ssl_ctx.size()) { indexed_ssl_ctx[as_unsigned(idx)].push_back(ssl_ctx); } else { assert(static_cast(idx) == indexed_ssl_ctx.size()); indexed_ssl_ctx.emplace_back(std::vector{ssl_ctx}); } return 0; } bool in_proto_list(const std::vector &protos, const std::string_view &needle) { for (auto &proto : protos) { if (proto == needle) { return true; } } return false; } bool upstream_tls_enabled(const ConnectionConfig &connconf) { #ifdef ENABLE_HTTP3 if (connconf.quic_listener.addrs.size()) { return true; } #endif // defined(ENABLE_HTTP3) const auto &faddrs = connconf.listener.addrs; return std::ranges::any_of( faddrs, [](const UpstreamAddr &faddr) { return faddr.tls; }); } X509 *load_certificate(const char *filename) { auto bio = BIO_new(BIO_s_file()); if (!bio) { fprintf(stderr, "BIO_new() failed\n"); return nullptr; } auto bio_deleter = defer(BIO_vfree, bio); if (!BIO_read_filename(bio, filename)) { fprintf(stderr, "Could not read certificate file '%s'\n", filename); return nullptr; } auto cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); if (!cert) { fprintf(stderr, "Could not read X509 structure from file '%s'\n", filename); return nullptr; } return cert; } SSL_CTX * setup_server_ssl_context(std::vector &all_ssl_ctx, std::vector> &indexed_ssl_ctx, CertLookupTree *cert_tree #ifdef HAVE_NEVERBLEED , neverbleed_t *nb #endif // defined(HAVE_NEVERBLEED) ) { auto config = get_config(); if (!upstream_tls_enabled(config->conn)) { return nullptr; } auto &tlsconf = config->tls; auto ssl_ctx = create_ssl_context(tlsconf.private_key_file.data(), tlsconf.cert_file.data(), tlsconf.sct_data #ifdef HAVE_NEVERBLEED , nb #endif // defined(HAVE_NEVERBLEED) ); all_ssl_ctx.push_back(ssl_ctx); assert(cert_tree); if (cert_lookup_tree_add_ssl_ctx(cert_tree, indexed_ssl_ctx, ssl_ctx) == -1) { LOG(FATAL) << "Failed to add default certificate."; DIE(); } for (auto &c : tlsconf.subcerts) { auto ssl_ctx = create_ssl_context(c.private_key_file.data(), c.cert_file.data(), c.sct_data #ifdef HAVE_NEVERBLEED , nb #endif // defined(HAVE_NEVERBLEED) ); all_ssl_ctx.push_back(ssl_ctx); if (cert_lookup_tree_add_ssl_ctx(cert_tree, indexed_ssl_ctx, ssl_ctx) == -1) { LOG(FATAL) << "Failed to add sub certificate."; DIE(); } } return ssl_ctx; } #ifdef ENABLE_HTTP3 SSL_CTX *setup_quic_server_ssl_context( std::vector &all_ssl_ctx, std::vector> &indexed_ssl_ctx, CertLookupTree *cert_tree # ifdef HAVE_NEVERBLEED , neverbleed_t *nb # endif // defined(HAVE_NEVERBLEED) ) { auto config = get_config(); if (!upstream_tls_enabled(config->conn)) { return nullptr; } auto &tlsconf = config->tls; auto ssl_ctx = create_quic_ssl_context( tlsconf.private_key_file.data(), tlsconf.cert_file.data(), tlsconf.sct_data # ifdef HAVE_NEVERBLEED , nb # endif // defined(HAVE_NEVERBLEED) ); all_ssl_ctx.push_back(ssl_ctx); assert(cert_tree); if (cert_lookup_tree_add_ssl_ctx(cert_tree, indexed_ssl_ctx, ssl_ctx) == -1) { LOG(FATAL) << "Failed to add default certificate."; DIE(); } for (auto &c : tlsconf.subcerts) { auto ssl_ctx = create_quic_ssl_context(c.private_key_file.data(), c.cert_file.data(), c.sct_data # ifdef HAVE_NEVERBLEED , nb # endif // defined(HAVE_NEVERBLEED) ); all_ssl_ctx.push_back(ssl_ctx); if (cert_lookup_tree_add_ssl_ctx(cert_tree, indexed_ssl_ctx, ssl_ctx) == -1) { LOG(FATAL) << "Failed to add sub certificate."; DIE(); } } return ssl_ctx; } #endif // defined(ENABLE_HTTP3) SSL_CTX *setup_downstream_client_ssl_context( #ifdef HAVE_NEVERBLEED neverbleed_t *nb #endif // defined(HAVE_NEVERBLEED) ) { auto &tlsconf = get_config()->tls; return create_ssl_client_context( #ifdef HAVE_NEVERBLEED nb, #endif // defined(HAVE_NEVERBLEED) tlsconf.cacert, tlsconf.client.cert_file, tlsconf.client.private_key_file); } void setup_downstream_http2_alpn(SSL *ssl) { // ALPN advertisement SSL_set_alpn_protos(ssl, reinterpret_cast(NGHTTP2_H2_ALPN.data()), NGHTTP2_H2_ALPN.size()); } void setup_downstream_http1_alpn(SSL *ssl) { // ALPN advertisement SSL_set_alpn_protos( ssl, reinterpret_cast(NGHTTP2_H1_1_ALPN.data()), NGHTTP2_H1_1_ALPN.size()); } std::unique_ptr create_cert_lookup_tree() { auto config = get_config(); if (!upstream_tls_enabled(config->conn)) { return nullptr; } return std::make_unique(); } namespace { std::vector serialize_ssl_session(SSL_SESSION *session) { auto len = static_cast(i2d_SSL_SESSION(session, nullptr)); auto buf = std::vector(len); auto p = buf.data(); i2d_SSL_SESSION(session, &p); return buf; } } // namespace void try_cache_tls_session(TLSSessionCache *cache, SSL_SESSION *session, const std::chrono::steady_clock::time_point &t) { if (cache->last_updated + 1min > t) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "Client session cache entry is still fresh."; } return; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "Update client cache entry " << "timestamp = " << t.time_since_epoch().count(); } cache->session_data = serialize_ssl_session(session); cache->last_updated = t; } SSL_SESSION *reuse_tls_session(const TLSSessionCache &cache) { if (cache.session_data.empty()) { return nullptr; } auto p = cache.session_data.data(); return d2i_SSL_SESSION(nullptr, &p, as_signed(cache.session_data.size())); } int proto_version_from_string(const std::string_view &v) { #ifdef TLS1_3_VERSION if (util::strieq("TLSv1.3"sv, v)) { return TLS1_3_VERSION; } #endif // defined(TLS1_3_VERSION) if (util::strieq("TLSv1.2"sv, v)) { return TLS1_2_VERSION; } return -1; } ssize_t get_x509_fingerprint(uint8_t *dst, size_t dstlen, const X509 *x, const EVP_MD *md) { auto len = static_cast(dstlen); if (X509_digest(x, md, dst, &len) != 1) { return -1; } return len; } namespace { std::string_view get_x509_name(BlockAllocator &balloc, X509_NAME *nm) { auto b = BIO_new(BIO_s_mem()); if (!b) { return ""sv; } auto b_deleter = defer(BIO_free, b); // Not documented, but it seems that X509_NAME_print_ex returns the // number of bytes written into b. auto slen = X509_NAME_print_ex(b, nm, 0, XN_FLAG_RFC2253); if (slen <= 0) { return ""sv; } auto iov = make_byte_ref(balloc, static_cast(slen) + 1); BIO_read(b, iov.data(), slen); iov[static_cast(slen)] = '\0'; return as_string_view(iov.first(static_cast(slen))); } } // namespace std::string_view get_x509_subject_name(BlockAllocator &balloc, X509 *x) { return get_x509_name(balloc, X509_get_subject_name(x)); } std::string_view get_x509_issuer_name(BlockAllocator &balloc, X509 *x) { return get_x509_name(balloc, X509_get_issuer_name(x)); } std::string_view get_x509_serial(BlockAllocator &balloc, X509 *x) { auto sn = X509_get_serialNumber(x); auto bn = BN_new(); auto bn_d = defer(BN_free, bn); if (!ASN1_INTEGER_to_BN(sn, bn) || BN_num_bytes(bn) > 20) { return ""sv; } std::array b; auto n = BN_bn2bin(bn, b.data()); assert(n <= 20); return util::format_hex(balloc, std::span{b.data(), static_cast(n)}); } namespace { // Performs conversion from |at| to time_t. The result is stored in // |t|. This function returns 0 if it succeeds, or -1. int time_t_from_asn1_time(time_t &t, const ASN1_TIME *at) { int rv; #if defined(NGHTTP2_GENUINE_OPENSSL) || \ defined(NGHTTP2_OPENSSL_IS_LIBRESSL) || defined(NGHTTP2_OPENSSL_IS_WOLFSSL) struct tm tm; rv = ASN1_TIME_to_tm(at, &tm); if (rv != 1) { return -1; } t = nghttp2_timegm(&tm); #else // !defined(NGHTTP2_GENUINE_OPENSSL) && // !defined(NGHTTP2_OPENSSL_IS_LIBRESSL) && // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) auto b = BIO_new(BIO_s_mem()); if (!b) { return -1; } auto bio_deleter = defer(BIO_free, b); rv = ASN1_TIME_print(b, at); if (rv != 1) { return -1; } char *s; auto slen = BIO_get_mem_data(b, &s); auto tt = util::parse_openssl_asn1_time_print( std::string_view{s, static_cast(slen)}); if (tt == 0) { return -1; } t = tt; #endif // !defined(NGHTTP2_GENUINE_OPENSSL) && // !defined(NGHTTP2_OPENSSL_IS_LIBRESSL) && // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) return 0; } } // namespace int get_x509_not_before(time_t &t, X509 *x) { auto at = X509_get0_notBefore(x); if (!at) { return -1; } return time_t_from_asn1_time(t, at); } int get_x509_not_after(time_t &t, X509 *x) { auto at = X509_get0_notAfter(x); if (!at) { return -1; } return time_t_from_asn1_time(t, at); } } // namespace tls } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/nghttpd.cc0000644000000000000000000000013215077107270015026 xustar0030 mtime=1761382072.989444157 30 atime=1761382106.238310277 30 ctime=1761382109.234299994 nghttp2-1.68.0/src/nghttpd.cc0000644000175100017510000003703215077107270015423 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_config.h" #ifdef HAVE_UNISTD_H # include #endif // defined(HAVE_UNISTD_H) #include #include #include #include #include #include #include #include #include #include "app_helper.h" #include "HttpServer.h" #include "util.h" #include "tls.h" namespace nghttp2 { namespace { int parse_push_config(Config &config, const char *optarg) { const char *eq = strchr(optarg, '='); if (eq == nullptr) { return -1; } auto &paths = config.push[std::string(optarg, eq)]; auto optarg_end = optarg + strlen(optarg); auto i = eq + 1; for (;;) { const char *j = strchr(i, ','); if (j == nullptr) { j = optarg_end; } paths.emplace_back(i, j); if (j == optarg_end) { break; } i = j; ++i; } return 0; } } // namespace namespace { void print_version(std::ostream &out) { out << "nghttpd nghttp2/" NGHTTP2_VERSION << std::endl; } } // namespace namespace { void print_usage(std::ostream &out) { out << "Usage: nghttpd [OPTION]... [ ]\n" << "HTTP/2 server" << std::endl; } } // namespace namespace { void print_help(std::ostream &out) { Config config; print_usage(out); out << R"( Specify listening port number. Set path to server's private key. Required unless --no-tls is specified. Set path to server's certificate. Required unless --no-tls is specified. Options: -a, --address= The address to bind to. If not specified the default IP address determined by getaddrinfo is used. -D, --daemon Run in a background. If -D is used, the current working directory is changed to '/'. Therefore if this option is used, -d option must be specified. -V, --verify-client The server sends a client certificate request. If the client did not return a certificate, the handshake is terminated. Currently, this option just requests a client certificate and does not verify it. -d, --htdocs= Specify document root. If this option is not specified, the document root is the current working directory. -v, --verbose Print debug information such as reception/ transmission of frames and name/value pairs. --no-tls Disable SSL/TLS. -c, --header-table-size= Specify decoder header table size. --encoder-header-table-size= Specify encoder header table size. The decoder (client) specifies the maximum dynamic table size it accepts. Then the negotiated dynamic table size is the minimum of this option value and the value which client specified. --color Force colored log output. -p, --push== Push resources s when is requested. This option can be used repeatedly to specify multiple push configurations. and s are relative to document root. See --htdocs option. Example: -p/=/foo.png -p/doc=/bar.css -b, --padding= Add at most bytes to a frame payload as padding. Specify 0 to disable padding. -m, --max-concurrent-streams= Set the maximum number of the concurrent streams in one HTTP/2 session. Default: )" << config.max_concurrent_streams << R"( -n, --workers= Set the number of worker threads. Default: 1 -e, --error-gzip Make error response gzipped. -w, --window-bits= Sets the stream level initial window size to 2**-1. -W, --connection-window-bits= Sets the connection level initial window size to 2**-1. --dh-param-file= Path to file that contains DH parameters in PEM format. Without this option, DHE cipher suites are not available. --early-response Start sending response when request HEADERS is received, rather than complete request is received. --trailer=
Add a trailer header to a response.
must not include pseudo header field (header field name starting with ':'). The trailer is sent only if a response has body part. Example: --trailer 'foo: bar'. --hexdump Display the incoming traffic in hexadecimal (Canonical hex+ASCII display). If SSL/TLS is used, decrypted data are used. --echo-upload Send back uploaded content if method is POST or PUT. --mime-types-file= Path to file that contains MIME media types and the extensions that represent them. Default: )" << config.mime_types_file << R"( --no-content-length Don't send content-length header field. --groups= Specify the supported groups. Default: )" << config.groups << R"( --ktls Enable ktls. --version Display version information and exit. -h, --help Display this help and exit. -- The argument is an integer and an optional unit (e.g., 10K is 10 * 1024). Units are K, M and G (powers of 1024).)" << std::endl; } } // namespace int main(int argc, char **argv) { Config config; bool color = false; auto mime_types_file_set_manually = false; while (1) { static int flag = 0; constexpr static option long_options[] = { {"address", required_argument, nullptr, 'a'}, {"daemon", no_argument, nullptr, 'D'}, {"htdocs", required_argument, nullptr, 'd'}, {"help", no_argument, nullptr, 'h'}, {"verbose", no_argument, nullptr, 'v'}, {"verify-client", no_argument, nullptr, 'V'}, {"header-table-size", required_argument, nullptr, 'c'}, {"push", required_argument, nullptr, 'p'}, {"padding", required_argument, nullptr, 'b'}, {"max-concurrent-streams", required_argument, nullptr, 'm'}, {"workers", required_argument, nullptr, 'n'}, {"error-gzip", no_argument, nullptr, 'e'}, {"window-bits", required_argument, nullptr, 'w'}, {"connection-window-bits", required_argument, nullptr, 'W'}, {"no-tls", no_argument, &flag, 1}, {"color", no_argument, &flag, 2}, {"version", no_argument, &flag, 3}, {"dh-param-file", required_argument, &flag, 4}, {"early-response", no_argument, &flag, 5}, {"trailer", required_argument, &flag, 6}, {"hexdump", no_argument, &flag, 7}, {"echo-upload", no_argument, &flag, 8}, {"mime-types-file", required_argument, &flag, 9}, {"no-content-length", no_argument, &flag, 10}, {"encoder-header-table-size", required_argument, &flag, 11}, {"ktls", no_argument, &flag, 12}, {"no-rfc7540-pri", no_argument, &flag, 13}, {"groups", required_argument, &flag, 14}, {nullptr, 0, nullptr, 0}}; int option_index = 0; int c = getopt_long(argc, argv, "DVb:c:d:ehm:n:p:va:w:W:", long_options, &option_index); if (c == -1) { break; } switch (c) { case 'a': config.address = optarg; break; case 'D': config.daemon = true; break; case 'V': config.verify_client = true; break; case 'b': { auto n = util::parse_uint(optarg); if (!n) { std::cerr << "-b: Bad option value: " << optarg << std::endl; exit(EXIT_FAILURE); } config.padding = static_cast(*n); break; } case 'd': config.htdocs = optarg; break; case 'e': config.error_gzip = true; break; case 'm': { // max-concurrent-streams option auto n = util::parse_uint(optarg); if (!n) { std::cerr << "-m: invalid argument: " << optarg << std::endl; exit(EXIT_FAILURE); } config.max_concurrent_streams = static_cast(*n); break; } case 'n': { #ifdef NOTHREADS std::cerr << "-n: WARNING: Threading disabled at build time, " << "no threads created." << std::endl; #else // !defined(NOTHREADS) auto n = util::parse_uint(optarg); if (!n) { std::cerr << "-n: Bad option value: " << optarg << std::endl; exit(EXIT_FAILURE); } config.num_worker = static_cast(*n); #endif // !defined(NOTHREADS) break; } case 'h': print_help(std::cout); exit(EXIT_SUCCESS); case 'v': config.verbose = true; break; case 'c': { auto n = util::parse_uint_with_unit(optarg); if (!n) { std::cerr << "-c: Bad option value: " << optarg << std::endl; exit(EXIT_FAILURE); } if (n > std::numeric_limits::max()) { std::cerr << "-c: Value too large. It should be less than or equal to " << std::numeric_limits::max() << std::endl; exit(EXIT_FAILURE); } config.header_table_size = *n; break; } case 'p': if (parse_push_config(config, optarg) != 0) { std::cerr << "-p: Bad option value: " << optarg << std::endl; } break; case 'w': case 'W': { auto n = util::parse_uint(optarg); if (!n || n > 30) { std::cerr << "-" << static_cast(c) << ": specify the integer in the range [0, 30], inclusive" << std::endl; exit(EXIT_FAILURE); } if (c == 'w') { config.window_bits = static_cast(*n); } else { config.connection_window_bits = static_cast(*n); } break; } case '?': util::show_candidates(argv[optind - 1], long_options); exit(EXIT_FAILURE); case 0: switch (flag) { case 1: // no-tls option config.no_tls = true; break; case 2: // color option color = true; break; case 3: // version print_version(std::cout); exit(EXIT_SUCCESS); case 4: // dh-param-file config.dh_param_file = optarg; break; case 5: // early-response config.early_response = true; break; case 6: { // trailer option auto header = optarg; auto name_end = strchr(optarg, ':'); if (!name_end) { std::cerr << "--trailer: invalid header: " << optarg << std::endl; exit(EXIT_FAILURE); } *name_end = 0; auto value = name_end + 1; while (isspace(*value)) { value++; } if (*value == 0) { // This could also be a valid case for suppressing a header // similar to curl std::cerr << "--trailer: invalid header - value missing: " << optarg << std::endl; exit(EXIT_FAILURE); } util::tolower(header, name_end, header); config.trailer.emplace_back(header, value, false); break; } case 7: // hexdump option config.hexdump = true; break; case 8: // echo-upload option config.echo_upload = true; break; case 9: // mime-types-file option mime_types_file_set_manually = true; config.mime_types_file = optarg; break; case 10: // no-content-length option config.no_content_length = true; break; case 11: { // encoder-header-table-size option auto n = util::parse_uint_with_unit(optarg); if (!n) { std::cerr << "--encoder-header-table-size: Bad option value: " << optarg << std::endl; exit(EXIT_FAILURE); } if (n > std::numeric_limits::max()) { std::cerr << "--encoder-header-table-size: Value too large. It " "should be less than or equal to " << std::numeric_limits::max() << std::endl; exit(EXIT_FAILURE); } config.encoder_header_table_size = *n; break; } case 12: // tls option config.ktls = true; break; case 13: // no-rfc7540-pri option std::cerr << "[WARNING]: --no-rfc7540-pri option has been deprecated." << std::endl; break; case 14: // groups option config.groups = optarg; break; } break; default: break; } } if (argc - optind < (config.no_tls ? 1 : 3)) { print_usage(std::cerr); std::cerr << "Too few arguments" << std::endl; exit(EXIT_FAILURE); } { auto portStr = argv[optind++]; auto n = util::parse_uint(portStr); if (!n || n > std::numeric_limits::max()) { std::cerr << ": Bad value: " << portStr << std::endl; exit(EXIT_FAILURE); } config.port = static_cast(*n); } if (!config.no_tls) { config.private_key_file = argv[optind++]; config.cert_file = argv[optind++]; } if (config.daemon) { if (config.htdocs.empty()) { print_usage(std::cerr); std::cerr << "-d option must be specified when -D is used." << std::endl; exit(EXIT_FAILURE); } if (util::daemonize(0, 0) == -1) { perror("daemon"); exit(EXIT_FAILURE); } } if (config.htdocs.empty()) { config.htdocs = "./"; } if (util::read_mime_types(config.mime_types, config.mime_types_file.c_str()) != 0) { if (mime_types_file_set_manually) { std::cerr << "--mime-types-file: Could not open mime types file: " << config.mime_types_file << std::endl; } } auto &trailer_names = config.trailer_names; for (auto &h : config.trailer) { trailer_names += h.name; trailer_names += ", "; } if (trailer_names.size() >= 2) { trailer_names.resize(trailer_names.size() - 2); } set_color_output(color || isatty(fileno(stdout))); struct sigaction act{}; act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, nullptr); reset_timer(); HttpServer server(&config); if (server.run() != 0) { exit(EXIT_FAILURE); } return 0; } } // namespace nghttp2 int main(int argc, char **argv) { return nghttp2::run_app(nghttp2::main, argc, argv); } nghttp2-1.68.0/src/PaxHeaders/shrpx_memcached_request.h0000644000000000000000000000013115077107270020121 xustar0029 mtime=1761382072.99744412 30 atime=1761382106.145310687 30 ctime=1761382109.141300262 nghttp2-1.68.0/src/shrpx_memcached_request.h0000644000175100017510000000337015077107270020515 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_MEMCACHED_REQUEST_H #define SHRPX_MEMCACHED_REQUEST_H #include "shrpx.h" #include #include #include #include "shrpx_memcached_result.h" namespace shrpx { enum class MemcachedOp : uint8_t { GET = 0x00, ADD = 0x02, }; struct MemcachedRequest; using MemcachedResultCallback = std::function; struct MemcachedRequest { std::string key; std::vector value; MemcachedResultCallback cb; uint32_t expiry; MemcachedOp op; bool canceled; }; } // namespace shrpx #endif // !defined(SHRPX_MEMCACHED_REQUEST_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_config.cc0000644000000000000000000000013215077107270016047 xustar0030 mtime=1761382072.991444148 30 atime=1761382106.077310986 30 ctime=1761382109.073300459 nghttp2-1.68.0/src/shrpx_config.cc0000644000175100017510000044070115077107270016445 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_config.h" #ifdef HAVE_PWD_H # include #endif // defined(HAVE_PWD_H) #ifdef HAVE_NETDB_H # include #endif // defined(HAVE_NETDB_H) #ifdef HAVE_SYSLOG_H # include #endif // defined(HAVE_SYSLOG_H) #include #include #ifdef HAVE_FCNTL_H # include #endif // defined(HAVE_FCNTL_H) #ifdef HAVE_UNISTD_H # include #endif // defined(HAVE_UNISTD_H) #include #include #include #include #include #include #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include #include "urlparse.h" #include "shrpx_log.h" #include "shrpx_tls.h" #include "shrpx_http.h" #ifdef HAVE_MRUBY # include "shrpx_mruby.h" #endif // defined(HAVE_MRUBY) #include "util.h" #include "base64.h" #include "ssl_compat.h" #include "xsi_strerror.h" #ifndef AI_NUMERICSERV # define AI_NUMERICSERV 0 #endif // !defined(AI_NUMERICSERV) namespace shrpx { namespace { Config *config; } // namespace constexpr auto SHRPX_UNIX_PATH_PREFIX = "unix:"sv; const Config *get_config() { return config; } Config *mod_config() { return config; } std::unique_ptr replace_config(std::unique_ptr another) { auto p = config; config = another.release(); return std::unique_ptr(p); } void create_config() { config = new Config(); } Config::~Config() { auto &upstreamconf = http2.upstream; nghttp2_option_del(upstreamconf.option); nghttp2_option_del(upstreamconf.alt_mode_option); nghttp2_session_callbacks_del(upstreamconf.callbacks); auto &downstreamconf = http2.downstream; nghttp2_option_del(downstreamconf.option); nghttp2_session_callbacks_del(downstreamconf.callbacks); auto &dumpconf = http2.upstream.debug.dump; if (dumpconf.request_header) { fclose(dumpconf.request_header); } if (dumpconf.response_header) { fclose(dumpconf.response_header); } } TicketKeys::~TicketKeys() { /* Erase keys from memory */ for (auto &key : keys) { memset(&key, 0, sizeof(key)); } } struct HostPort { std::string_view host; uint16_t port; }; namespace { std::optional split_host_port(BlockAllocator &balloc, const std::string_view &hostport, const std::string_view &opt) { // host and port in |hostport| is separated by single ','. auto sep = std::ranges::find(hostport, ','); if (sep == std::ranges::end(hostport)) { LOG(ERROR) << opt << ": Invalid host, port: " << hostport; return {}; } auto len = as_unsigned(sep - std::ranges::begin(hostport)); if (NI_MAXHOST < len + 1) { LOG(ERROR) << opt << ": Hostname too long: " << hostport; return {}; } auto portstr = std::string_view{sep + 1, std::ranges::end(hostport)}; auto d = util::parse_uint(portstr); if (!d || 1 > d || d > std::numeric_limits::max()) { LOG(ERROR) << opt << ": Port is invalid: " << portstr; return {}; } return HostPort{ .host = make_string_ref(balloc, std::ranges::begin(hostport), sep), .port = static_cast(*d), }; } } // namespace namespace { bool is_secure(const std::string_view &filename) { struct stat buf; int rv = stat(filename.data(), &buf); if (rv == 0) { if ((buf.st_mode & S_IRWXU) && !(buf.st_mode & S_IRWXG) && !(buf.st_mode & S_IRWXO)) { return true; } } return false; } } // namespace std::unique_ptr read_tls_ticket_key_file(const std::vector &files, const EVP_CIPHER *cipher, const EVP_MD *hmac) { auto ticket_keys = std::make_unique(); auto &keys = ticket_keys->keys; keys.resize(files.size()); auto enc_keylen = static_cast(EVP_CIPHER_key_length(cipher)); auto hmac_keylen = static_cast(EVP_MD_size(hmac)); if (cipher == EVP_aes_128_cbc()) { // backward compatibility, as a legacy of using same file format // with nginx and apache. hmac_keylen = 16; } auto expectedlen = keys[0].data.name.size() + enc_keylen + hmac_keylen; std::array buf; assert(buf.size() >= expectedlen); size_t i = 0; for (auto &file : files) { struct stat fst{}; if (stat(file.data(), &fst) == -1) { auto error = errno; LOG(ERROR) << "tls-ticket-key-file: could not stat file " << file << ", errno=" << error; return nullptr; } if (static_cast(fst.st_size) != expectedlen) { LOG(ERROR) << "tls-ticket-key-file: the expected file size is " << expectedlen << ", the actual file size is " << fst.st_size; return nullptr; } std::ifstream f(file.data()); if (!f) { LOG(ERROR) << "tls-ticket-key-file: could not open file " << file; return nullptr; } f.read(buf.data(), static_cast(expectedlen)); if (static_cast(f.gcount()) != expectedlen) { LOG(ERROR) << "tls-ticket-key-file: want to read " << expectedlen << " bytes but only read " << f.gcount() << " bytes from " << file; return nullptr; } auto &key = keys[i++]; key.cipher = cipher; key.hmac = hmac; key.hmac_keylen = hmac_keylen; if (LOG_ENABLED(INFO)) { LOG(INFO) << "enc_keylen=" << enc_keylen << ", hmac_keylen=" << key.hmac_keylen; } auto p = std::ranges::begin(buf); p = std::ranges::copy_n(p, as_signed(key.data.name.size()), std::ranges::begin(key.data.name)) .in; p = std::ranges::copy_n(p, as_signed(enc_keylen), std::ranges::begin(key.data.enc_key)) .in; std::ranges::copy_n(p, as_signed(hmac_keylen), std::ranges::begin(key.data.hmac_key)); if (LOG_ENABLED(INFO)) { LOG(INFO) << "session ticket key: " << util::format_hex(key.data.name); } } return ticket_keys; } #ifdef ENABLE_HTTP3 std::shared_ptr read_quic_secret_file(const std::string_view &path) { constexpr size_t expectedlen = SHRPX_QUIC_SECRET_RESERVEDLEN + SHRPX_QUIC_SECRETLEN + SHRPX_QUIC_SALTLEN; auto qkms = std::make_shared(); auto &kms = qkms->keying_materials; std::ifstream f(path.data()); if (!f) { LOG(ERROR) << "frontend-quic-secret-file: could not open file " << path; return nullptr; } std::string line; while (std::getline(f, line)) { if (line.empty() || line[0] == '#') { continue; } auto s = std::string_view{line}; if (s.size() != expectedlen * 2 || !util::is_hex_string(s)) { LOG(ERROR) << "frontend-quic-secret-file: each line must be a " << expectedlen * 2 << " bytes hex encoded string"; return nullptr; } kms.emplace_back(); auto &qkm = kms.back(); auto p = std::ranges::begin(s); util::decode_hex(p, p + qkm.reserved.size(), std::ranges::begin(qkm.reserved)); p += qkm.reserved.size() * 2; util::decode_hex(p, p + qkm.secret.size(), std::ranges::begin(qkm.secret)); p += qkm.secret.size() * 2; util::decode_hex(p, p + qkm.salt.size(), std::ranges::begin(qkm.salt)); p += qkm.salt.size() * 2; assert(static_cast(p - std::ranges::begin(s)) == expectedlen * 2); qkm.id = qkm.reserved[0] & SHRPX_QUIC_DCID_KM_ID_MASK; if (kms.size() == 8) { break; } } if (f.bad() || (!f.eof() && f.fail())) { LOG(ERROR) << "frontend-quic-secret-file: error occurred while reading file " << path; return nullptr; } if (kms.empty()) { LOG(WARN) << "frontend-quic-secret-file: no keying materials are present in file " << path; return nullptr; } return qkms; } #endif // defined(ENABLE_HTTP3) FILE *open_file_for_write(const char *filename) { std::array errbuf; #ifdef O_CLOEXEC auto fd = open(filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); #else // !defined(O_CLOEXEC) auto fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); // We get race condition if execve is called at the same time. if (fd != -1) { util::make_socket_closeonexec(fd); } #endif // !defined(O_CLOEXEC) if (fd == -1) { auto error = errno; LOG(ERROR) << "Failed to open " << filename << " for writing. Cause: " << xsi_strerror(error, errbuf.data(), errbuf.size()); return nullptr; } auto f = fdopen(fd, "wb"); if (f == nullptr) { auto error = errno; LOG(ERROR) << "Failed to open " << filename << " for writing. Cause: " << xsi_strerror(error, errbuf.data(), errbuf.size()); return nullptr; } return f; } namespace { // Read passwd from |filename| std::string read_passwd_from_file(const std::string_view &opt, const std::string_view &filename) { std::string line; if (!is_secure(filename)) { LOG(ERROR) << opt << ": Private key passwd file " << filename << " has insecure mode."; return line; } std::ifstream in(filename.data(), std::ios::binary); if (!in) { LOG(ERROR) << opt << ": Could not open key passwd file " << filename; return line; } std::getline(in, line); return line; } } // namespace HeaderRefs::value_type parse_header(BlockAllocator &balloc, const std::string_view &optarg) { auto colon = std::ranges::find(optarg, ':'); if (colon == std::ranges::end(optarg) || colon == std::ranges::begin(optarg)) { return {}; } auto value = colon + 1; for (; *value == '\t' || *value == ' '; ++value) ; auto name_iov = make_byte_ref( balloc, as_unsigned(std::ranges::distance(std::ranges::begin(optarg), colon) + 1)); auto p = util::tolower(std::ranges::begin(optarg), colon, std::ranges::begin(name_iov)); *p = '\0'; auto nv = HeaderRef( as_string_view(std::ranges::begin(name_iov), p), make_string_ref(balloc, std::string_view{value, std::ranges::end(optarg)})); if (!nghttp2_check_header_name( reinterpret_cast(nv.name.data()), nv.name.size()) || !nghttp2_check_header_value_rfc9113( reinterpret_cast(nv.value.data()), nv.value.size())) { return {}; } return nv; } template int parse_uint(T *dest, const std::string_view &opt, const std::string_view &optarg) { auto val = util::parse_uint(optarg); if (!val) { LOG(ERROR) << opt << ": bad value. Specify an integer >= 0."; return -1; } *dest = static_cast(*val); return 0; } namespace { template int parse_uint_with_unit(T *dest, const std::string_view &opt, const std::string_view &optarg) { auto n = util::parse_uint_with_unit(optarg); if (!n) { LOG(ERROR) << opt << ": bad value: '" << optarg << "'"; return -1; } if constexpr (!std::is_same_v) { if (static_cast(std::numeric_limits::max()) < static_cast(*n)) { LOG(ERROR) << opt << ": too large. The value should be less than or equal to " << std::numeric_limits::max(); return -1; } } *dest = static_cast(*n); return 0; } } // namespace namespace { int parse_altsvc(AltSvc &altsvc, const std::string_view &opt, const std::string_view &optarg) { // PROTOID, PORT, HOST, ORIGIN, PARAMS. auto tokens = util::split_str(optarg, ',', 5); if (tokens.size() < 2) { // Requires at least protocol_id and port LOG(ERROR) << opt << ": too few parameters: " << optarg; return -1; } int port; if (parse_uint(&port, opt, tokens[1]) != 0) { return -1; } if (port < 1 || port > static_cast(std::numeric_limits::max())) { LOG(ERROR) << opt << ": port is invalid: " << tokens[1]; return -1; } altsvc.protocol_id = make_string_ref(config->balloc, tokens[0]); altsvc.port = static_cast(port); altsvc.service = make_string_ref(config->balloc, tokens[1]); if (tokens.size() > 2) { if (!tokens[2].empty()) { altsvc.host = make_string_ref(config->balloc, tokens[2]); } if (tokens.size() > 3) { if (!tokens[3].empty()) { altsvc.origin = make_string_ref(config->balloc, tokens[3]); } if (tokens.size() > 4) { if (!tokens[4].empty()) { altsvc.params = make_string_ref(config->balloc, tokens[4]); } } } } return 0; } } // namespace namespace { // generated by gennghttpxfun.py LogFragmentType log_var_lookup_token(const std::string_view &name) { switch (name.size()) { case 3: switch (name[2]) { case 'd': if (util::strieq("pi"sv, name.substr(0, 2))) { return LogFragmentType::PID; } break; } break; case 4: switch (name[3]) { case 'h': if (util::strieq("pat"sv, name.substr(0, 3))) { return LogFragmentType::PATH; } break; case 'n': if (util::strieq("alp"sv, name.substr(0, 3))) { return LogFragmentType::ALPN; } break; } break; case 6: switch (name[5]) { case 'd': if (util::strieq("metho"sv, name.substr(0, 5))) { return LogFragmentType::METHOD; } break; case 's': if (util::strieq("statu"sv, name.substr(0, 5))) { return LogFragmentType::STATUS; } break; } break; case 7: switch (name[6]) { case 'i': if (util::strieq("tls_sn"sv, name.substr(0, 6))) { return LogFragmentType::TLS_SNI; } break; case 't': if (util::strieq("reques"sv, name.substr(0, 6))) { return LogFragmentType::REQUEST; } break; } break; case 10: switch (name[9]) { case 'l': if (util::strieq("time_loca"sv, name.substr(0, 9))) { return LogFragmentType::TIME_LOCAL; } break; case 'r': if (util::strieq("ssl_ciphe"sv, name.substr(0, 9))) { return LogFragmentType::SSL_CIPHER; } if (util::strieq("tls_ciphe"sv, name.substr(0, 9))) { return LogFragmentType::TLS_CIPHER; } break; } break; case 11: switch (name[10]) { case 'r': if (util::strieq("remote_add"sv, name.substr(0, 10))) { return LogFragmentType::REMOTE_ADDR; } break; case 't': if (util::strieq("remote_por"sv, name.substr(0, 10))) { return LogFragmentType::REMOTE_PORT; } if (util::strieq("server_por"sv, name.substr(0, 10))) { return LogFragmentType::SERVER_PORT; } break; } break; case 12: switch (name[11]) { case '1': if (util::strieq("time_iso860"sv, name.substr(0, 11))) { return LogFragmentType::TIME_ISO8601; } break; case 'e': if (util::strieq("request_tim"sv, name.substr(0, 11))) { return LogFragmentType::REQUEST_TIME; } break; case 'l': if (util::strieq("ssl_protoco"sv, name.substr(0, 11))) { return LogFragmentType::SSL_PROTOCOL; } if (util::strieq("tls_protoco"sv, name.substr(0, 11))) { return LogFragmentType::TLS_PROTOCOL; } break; case 't': if (util::strieq("backend_hos"sv, name.substr(0, 11))) { return LogFragmentType::BACKEND_HOST; } if (util::strieq("backend_por"sv, name.substr(0, 11))) { return LogFragmentType::BACKEND_PORT; } break; } break; case 14: switch (name[13]) { case 'd': if (util::strieq("ssl_session_i"sv, name.substr(0, 13))) { return LogFragmentType::SSL_SESSION_ID; } if (util::strieq("tls_session_i"sv, name.substr(0, 13))) { return LogFragmentType::TLS_SESSION_ID; } break; } break; case 15: switch (name[14]) { case 't': if (util::strieq("body_bytes_sen"sv, name.substr(0, 14))) { return LogFragmentType::BODY_BYTES_SENT; } break; } break; case 16: switch (name[15]) { case 'n': if (util::strieq("protocol_versio"sv, name.substr(0, 15))) { return LogFragmentType::PROTOCOL_VERSION; } break; } break; case 17: switch (name[16]) { case 'l': if (util::strieq("tls_client_seria"sv, name.substr(0, 16))) { return LogFragmentType::TLS_CLIENT_SERIAL; } break; } break; case 18: switch (name[17]) { case 'd': if (util::strieq("ssl_session_reuse"sv, name.substr(0, 17))) { return LogFragmentType::SSL_SESSION_REUSED; } if (util::strieq("tls_session_reuse"sv, name.substr(0, 17))) { return LogFragmentType::TLS_SESSION_REUSED; } break; case 'y': if (util::strieq("path_without_quer"sv, name.substr(0, 17))) { return LogFragmentType::PATH_WITHOUT_QUERY; } break; } break; case 22: switch (name[21]) { case 'e': if (util::strieq("tls_client_issuer_nam"sv, name.substr(0, 21))) { return LogFragmentType::TLS_CLIENT_ISSUER_NAME; } break; } break; case 23: switch (name[22]) { case 'e': if (util::strieq("tls_client_subject_nam"sv, name.substr(0, 22))) { return LogFragmentType::TLS_CLIENT_SUBJECT_NAME; } break; } break; case 27: switch (name[26]) { case '1': if (util::strieq("tls_client_fingerprint_sha"sv, name.substr(0, 26))) { return LogFragmentType::TLS_CLIENT_FINGERPRINT_SHA1; } break; } break; case 29: switch (name[28]) { case '6': if (util::strieq("tls_client_fingerprint_sha25"sv, name.substr(0, 28))) { return LogFragmentType::TLS_CLIENT_FINGERPRINT_SHA256; } break; } break; } return LogFragmentType::NONE; } } // namespace namespace { bool var_token(char c) { return util::is_alpha(c) || util::is_digit(c) || c == '_'; } } // namespace std::vector parse_log_format(BlockAllocator &balloc, const std::string_view &optarg) { auto literal_start = std::ranges::begin(optarg); auto p = literal_start; auto eop = std::ranges::end(optarg); auto res = std::vector(); for (; p != eop;) { if (*p != '$') { ++p; continue; } auto var_start = p; ++p; std::string_view var_name; if (p != eop && *p == '{') { auto var_name_start = ++p; for (; p != eop && var_token(*p); ++p) ; if (p == eop || *p != '}') { LOG(WARN) << "Missing '}' after " << std::string_view{var_start, p}; continue; } var_name = std::string_view{var_name_start, p}; ++p; } else { auto var_name_start = p; for (; p != eop && var_token(*p); ++p) ; var_name = std::string_view{var_name_start, p}; } auto value = std::ranges::begin(var_name); auto type = log_var_lookup_token(var_name); if (type == LogFragmentType::NONE) { if (util::istarts_with(var_name, "http_"sv)) { if ("host"sv == var_name.substr(str_size("http_"))) { // Special handling of host header field. We will use // :authority header field if host header is missing. This // is a typical case in HTTP/2. type = LogFragmentType::AUTHORITY; } else { type = LogFragmentType::HTTP; value += str_size("http_"); } } else { LOG(WARN) << "Unrecognized log format variable: " << var_name; continue; } } if (literal_start < var_start) { res.emplace_back( LogFragmentType::LITERAL, make_string_ref(balloc, std::string_view{literal_start, var_start})); } literal_start = p; if (value == std::ranges::begin(var_name)) { res.emplace_back(type); continue; } { auto iov = make_byte_ref( balloc, as_unsigned(std::ranges::distance(value, std::ranges::end(var_name)) + 1)); auto p = std::ranges::transform(value, std::ranges::end(var_name), std::ranges::begin(iov), [](auto c) { return c == '_' ? '-' : c; }) .out; *p = '\0'; res.emplace_back(type, as_string_view(std::ranges::begin(iov), p)); } } if (literal_start != eop) { res.emplace_back( LogFragmentType::LITERAL, make_string_ref(balloc, std::string_view{literal_start, eop})); } return res; } namespace { int parse_address_family(int *dest, const std::string_view &opt, const std::string_view &optarg) { if (util::strieq("auto"sv, optarg)) { *dest = AF_UNSPEC; return 0; } if (util::strieq("IPv4"sv, optarg)) { *dest = AF_INET; return 0; } if (util::strieq("IPv6"sv, optarg)) { *dest = AF_INET6; return 0; } LOG(ERROR) << opt << ": bad value: '" << optarg << "'"; return -1; } } // namespace namespace { int parse_duration(ev_tstamp *dest, const std::string_view &opt, const std::string_view &optarg) { auto t = util::parse_duration_with_unit(optarg); if (!t) { LOG(ERROR) << opt << ": bad value: '" << optarg << "'"; return -1; } *dest = *t; return 0; } } // namespace namespace { int parse_tls_proto_version(int &dest, const std::string_view &opt, const std::string_view &optarg) { auto v = tls::proto_version_from_string(optarg); if (v == -1) { LOG(ERROR) << opt << ": invalid TLS protocol version: " << optarg; return -1; } dest = v; return 0; } } // namespace struct MemcachedConnectionParams { bool tls; }; namespace { // Parses memcached connection configuration parameter |src_params|, // and stores parsed results into |out|. This function returns 0 if // it succeeds, or -1. int parse_memcached_connection_params(MemcachedConnectionParams &out, const std::string_view &src_params, const std::string_view &opt) { auto last = std::ranges::end(src_params); for (auto first = std::ranges::begin(src_params); first != last;) { auto end = std::ranges::find(first, last, ';'); auto param = std::string_view{first, end}; if (util::strieq("tls"sv, param)) { out.tls = true; } else if (util::strieq("no-tls"sv, param)) { out.tls = false; } else if (!param.empty()) { LOG(ERROR) << opt << ": " << param << ": unknown keyword"; return -1; } if (end == last) { break; } first = end + 1; } return 0; } } // namespace struct UpstreamParams { UpstreamAltMode alt_mode; bool tls; bool sni_fwd; bool proxyproto; bool quic; }; namespace { // Parses upstream configuration parameter |src_params|, and stores // parsed results into |out|. This function returns 0 if it succeeds, // or -1. int parse_upstream_params(UpstreamParams &out, const std::string_view &src_params) { auto last = std::ranges::end(src_params); for (auto first = std::ranges::begin(src_params); first != last;) { auto end = std::ranges::find(first, last, ';'); auto param = std::string_view{first, end}; if (util::strieq("tls"sv, param)) { out.tls = true; } else if (util::strieq("sni-fwd"sv, param)) { out.sni_fwd = true; } else if (util::strieq("no-tls"sv, param)) { out.tls = false; } else if (util::strieq("api"sv, param)) { if (out.alt_mode != UpstreamAltMode::NONE && out.alt_mode != UpstreamAltMode::API) { LOG(ERROR) << "frontend: api and healthmon are mutually exclusive"; return -1; } out.alt_mode = UpstreamAltMode::API; } else if (util::strieq("healthmon"sv, param)) { if (out.alt_mode != UpstreamAltMode::NONE && out.alt_mode != UpstreamAltMode::HEALTHMON) { LOG(ERROR) << "frontend: api and healthmon are mutually exclusive"; return -1; } out.alt_mode = UpstreamAltMode::HEALTHMON; } else if (util::strieq("proxyproto"sv, param)) { out.proxyproto = true; } else if (util::strieq("quic"sv, param)) { #ifdef ENABLE_HTTP3 out.quic = true; #else // !defined(ENABLE_HTTP3) LOG(ERROR) << "quic: QUIC is disabled at compile time"; return -1; #endif // !defined(ENABLE_HTTP3) } else if (!param.empty()) { LOG(ERROR) << "frontend: " << param << ": unknown keyword"; return -1; } if (end == last) { break; } first = end + 1; } return 0; } } // namespace struct DownstreamParams { std::string_view sni; std::string_view mruby; std::string_view group; AffinityConfig affinity; ev_tstamp read_timeout; ev_tstamp write_timeout; size_t fall; size_t rise; uint32_t weight; uint32_t group_weight; Proto proto; bool tls; bool dns; bool redirect_if_not_tls; bool upgrade_scheme; bool dnf; }; namespace { // Parses |value| of parameter named |name| as duration. This // function returns 0 if it succeeds and the parsed value is assigned // to |dest|, or -1. int parse_downstream_param_duration(ev_tstamp &dest, const std::string_view &name, const std::string_view &value) { auto t = util::parse_duration_with_unit(value); if (!t) { LOG(ERROR) << "backend: " << name << ": bad value: '" << value << "'"; return -1; } dest = *t; return 0; } } // namespace namespace { // Parses downstream configuration parameter |src_params|, and stores // parsed results into |out|. This function returns 0 if it succeeds, // or -1. int parse_downstream_params(DownstreamParams &out, const std::string_view &src_params) { auto last = std::ranges::end(src_params); for (auto first = std::ranges::begin(src_params); first != last;) { auto end = std::ranges::find(first, last, ';'); auto param = std::string_view{first, end}; if (util::istarts_with(param, "proto="sv)) { auto protostr = std::string_view{first + str_size("proto="), end}; if (protostr.empty()) { LOG(ERROR) << "backend: proto: protocol is empty"; return -1; } if ("h2"sv == protostr) { out.proto = Proto::HTTP2; } else if ("http/1.1"sv == protostr) { out.proto = Proto::HTTP1; } else { LOG(ERROR) << "backend: proto: unknown protocol " << protostr; return -1; } } else if (util::istarts_with(param, "fall="sv)) { auto valstr = std::string_view{first + str_size("fall="), end}; if (valstr.empty()) { LOG(ERROR) << "backend: fall: non-negative integer is expected"; return -1; } auto n = util::parse_uint(valstr); if (!n) { LOG(ERROR) << "backend: fall: non-negative integer is expected"; return -1; } out.fall = static_cast(*n); } else if (util::istarts_with(param, "rise="sv)) { auto valstr = std::string_view{first + str_size("rise="), end}; if (valstr.empty()) { LOG(ERROR) << "backend: rise: non-negative integer is expected"; return -1; } auto n = util::parse_uint(valstr); if (!n) { LOG(ERROR) << "backend: rise: non-negative integer is expected"; return -1; } out.rise = static_cast(*n); } else if (util::strieq("tls"sv, param)) { out.tls = true; } else if (util::strieq("no-tls"sv, param)) { out.tls = false; } else if (util::istarts_with(param, "sni="sv)) { out.sni = std::string_view{first + str_size("sni="), end}; } else if (util::istarts_with(param, "affinity="sv)) { auto valstr = std::string_view{first + str_size("affinity="), end}; if (util::strieq("none"sv, valstr)) { out.affinity.type = SessionAffinity::NONE; } else if (util::strieq("ip"sv, valstr)) { out.affinity.type = SessionAffinity::IP; } else if (util::strieq("cookie"sv, valstr)) { out.affinity.type = SessionAffinity::COOKIE; } else { LOG(ERROR) << "backend: affinity: value must be one of none, ip, and cookie"; return -1; } } else if (util::istarts_with(param, "affinity-cookie-name="sv)) { auto val = std::string_view{first + str_size("affinity-cookie-name="), end}; if (val.empty()) { LOG(ERROR) << "backend: affinity-cookie-name: non empty string is expected"; return -1; } out.affinity.cookie.name = val; } else if (util::istarts_with(param, "affinity-cookie-path="sv)) { out.affinity.cookie.path = std::string_view{first + str_size("affinity-cookie-path="), end}; } else if (util::istarts_with(param, "affinity-cookie-secure="sv)) { auto valstr = std::string_view{first + str_size("affinity-cookie-secure="), end}; if (util::strieq("auto"sv, valstr)) { out.affinity.cookie.secure = SessionAffinityCookieSecure::AUTO; } else if (util::strieq("yes"sv, valstr)) { out.affinity.cookie.secure = SessionAffinityCookieSecure::YES; } else if (util::strieq("no"sv, valstr)) { out.affinity.cookie.secure = SessionAffinityCookieSecure::NO; } else { LOG(ERROR) << "backend: affinity-cookie-secure: value must be one of " "auto, yes, and no"; return -1; } } else if (util::istarts_with(param, "affinity-cookie-stickiness="sv)) { auto valstr = std::string_view{first + str_size("affinity-cookie-stickiness="), end}; if (util::strieq("loose"sv, valstr)) { out.affinity.cookie.stickiness = SessionAffinityCookieStickiness::LOOSE; } else if (util::strieq("strict"sv, valstr)) { out.affinity.cookie.stickiness = SessionAffinityCookieStickiness::STRICT; } else { LOG(ERROR) << "backend: affinity-cookie-stickiness: value must be " "either loose or strict"; return -1; } } else if (util::strieq("dns"sv, param)) { out.dns = true; } else if (util::strieq("redirect-if-not-tls"sv, param)) { out.redirect_if_not_tls = true; } else if (util::strieq("upgrade-scheme"sv, param)) { out.upgrade_scheme = true; } else if (util::istarts_with(param, "mruby="sv)) { auto valstr = std::string_view{first + str_size("mruby="), end}; out.mruby = valstr; } else if (util::istarts_with(param, "read-timeout="sv)) { if (parse_downstream_param_duration( out.read_timeout, "read-timeout"sv, std::string_view{first + str_size("read-timeout="), end}) == -1) { return -1; } } else if (util::istarts_with(param, "write-timeout="sv)) { if (parse_downstream_param_duration( out.write_timeout, "write-timeout"sv, std::string_view{first + str_size("write-timeout="), end}) == -1) { return -1; } } else if (util::istarts_with(param, "weight="sv)) { auto valstr = std::string_view{first + str_size("weight="), end}; if (valstr.empty()) { LOG(ERROR) << "backend: weight: non-negative integer [1, 256] is expected"; return -1; } auto n = util::parse_uint(valstr); if (!n || (n < 1 || n > 256)) { LOG(ERROR) << "backend: weight: non-negative integer [1, 256] is expected"; return -1; } out.weight = static_cast(*n); } else if (util::istarts_with(param, "group="sv)) { auto valstr = std::string_view{first + str_size("group="), end}; if (valstr.empty()) { LOG(ERROR) << "backend: group: empty string is not allowed"; return -1; } out.group = valstr; } else if (util::istarts_with(param, "group-weight="sv)) { auto valstr = std::string_view{first + str_size("group-weight="), end}; if (valstr.empty()) { LOG(ERROR) << "backend: group-weight: non-negative integer [1, 256] is " "expected"; return -1; } auto n = util::parse_uint(valstr); if (!n || (n < 1 || n > 256)) { LOG(ERROR) << "backend: group-weight: non-negative integer [1, 256] is " "expected"; return -1; } out.group_weight = static_cast(*n); } else if (util::strieq("dnf"sv, param)) { out.dnf = true; } else if (!param.empty()) { LOG(ERROR) << "backend: " << param << ": unknown keyword"; return -1; } if (end == last) { break; } first = end + 1; } return 0; } } // namespace namespace { // Parses host-path mapping patterns in |src_pattern|, and stores // mappings in config. We will store each host-path pattern found in // |src| with |addr|. |addr| will be copied accordingly. Also we // make a group based on the pattern. The "/" pattern is considered // as catch-all. We also parse protocol specified in |src_proto|. // // This function returns 0 if it succeeds, or -1. int parse_mapping( Config *config, DownstreamAddrConfig &addr, std::unordered_map &pattern_addr_indexer, const std::string_view &src_pattern, const std::string_view &src_params) { // This returns at least 1 element (it could be empty string). We // will append '/' to all patterns, so it becomes catch-all pattern. auto mapping = util::split_str(src_pattern, ':'); assert(!mapping.empty()); auto &downstreamconf = *config->conn.downstream; auto &addr_groups = downstreamconf.addr_groups; DownstreamParams params{ .weight = 1, .proto = Proto::HTTP1, }; if (parse_downstream_params(params, src_params) != 0) { return -1; } if (addr.host_unix && params.dns) { LOG(ERROR) << "backend: dns: cannot be used for UNIX domain socket"; return -1; } if (params.affinity.type == SessionAffinity::COOKIE && params.affinity.cookie.name.empty()) { LOG(ERROR) << "backend: affinity-cookie-name is mandatory if " "affinity=cookie is specified"; return -1; } addr.fall = params.fall; addr.rise = params.rise; addr.weight = params.weight; addr.group = make_string_ref(downstreamconf.balloc, params.group); addr.group_weight = params.group_weight; addr.proto = params.proto; addr.tls = params.tls; addr.sni = make_string_ref(downstreamconf.balloc, params.sni); addr.dns = params.dns; addr.upgrade_scheme = params.upgrade_scheme; addr.dnf = params.dnf; for (const auto &raw_pattern : mapping) { std::string_view pattern; auto slash = std::ranges::find(raw_pattern, '/'); if (slash == std::ranges::end(raw_pattern)) { // This effectively makes empty pattern to "/". 2 for '/' and // terminal NULL character. auto iov = make_byte_ref(downstreamconf.balloc, raw_pattern.size() + 2); auto p = util::tolower(raw_pattern, std::ranges::begin(iov)); *p++ = '/'; *p = '\0'; pattern = as_string_view(std::ranges::begin(iov), p); } else { auto path = http2::normalize_path_colon( downstreamconf.balloc, std::string_view{slash, std::ranges::end(raw_pattern)}, ""sv); auto iov = make_byte_ref(downstreamconf.balloc, as_unsigned(std::ranges::distance( std::ranges::begin(raw_pattern), slash)) + path.size() + 1); auto p = util::tolower(std::ranges::begin(raw_pattern), slash, std::ranges::begin(iov)); p = std::ranges::copy(path, p).out; *p = '\0'; pattern = as_string_view(std::ranges::begin(iov), p); } auto it = pattern_addr_indexer.find(pattern); if (it != std::ranges::end(pattern_addr_indexer)) { auto &g = addr_groups[(*it).second]; // Last value wins if we have multiple different affinity // value under one group. if (params.affinity.type != SessionAffinity::NONE) { if (g.affinity.type == SessionAffinity::NONE) { g.affinity.type = params.affinity.type; if (params.affinity.type == SessionAffinity::COOKIE) { g.affinity.cookie.name = make_string_ref( downstreamconf.balloc, params.affinity.cookie.name); if (!params.affinity.cookie.path.empty()) { g.affinity.cookie.path = make_string_ref( downstreamconf.balloc, params.affinity.cookie.path); } g.affinity.cookie.secure = params.affinity.cookie.secure; g.affinity.cookie.stickiness = params.affinity.cookie.stickiness; } } else if (g.affinity.type != params.affinity.type || g.affinity.cookie.name != params.affinity.cookie.name || g.affinity.cookie.path != params.affinity.cookie.path || g.affinity.cookie.secure != params.affinity.cookie.secure || g.affinity.cookie.stickiness != params.affinity.cookie.stickiness) { LOG(ERROR) << "backend: affinity: multiple different affinity " "configurations found in a single group"; return -1; } } // If at least one backend requires frontend TLS connection, // enable it for all backends sharing the same pattern. if (params.redirect_if_not_tls) { g.redirect_if_not_tls = true; } // All backends in the same group must have the same mruby path. // If some backends do not specify mruby file, and there is at // least one backend with mruby file, it is used for all // backends in the group. if (!params.mruby.empty()) { if (g.mruby_file.empty()) { g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby); } else if (g.mruby_file != params.mruby) { LOG(ERROR) << "backend: mruby: multiple different mruby file found " "in a single group"; return -1; } } // All backends in the same group must have the same read/write // timeout. If some backends do not specify read/write timeout, // and there is at least one backend with read/write timeout, it // is used for all backends in the group. if (params.read_timeout > 1e-9) { if (g.timeout.read < 1e-9) { g.timeout.read = params.read_timeout; } else if (fabs(g.timeout.read - params.read_timeout) > 1e-9) { LOG(ERROR) << "backend: read-timeout: multiple different read-timeout " "found in a single group"; return -1; } } if (params.write_timeout > 1e-9) { if (g.timeout.write < 1e-9) { g.timeout.write = params.write_timeout; } else if (fabs(g.timeout.write - params.write_timeout) > 1e-9) { LOG(ERROR) << "backend: write-timeout: multiple different " "write-timeout found in a single group"; return -1; } } // All backends in the same group must have the same dnf // setting. If some backends do not specify dnf, and there is // at least one backend with dnf, it is used for all backends in // the group. In general, multiple backends are not necessary // for dnf because there is no need for load balancing. if (params.dnf) { g.dnf = true; } g.addrs.push_back(addr); continue; } auto idx = addr_groups.size(); pattern_addr_indexer.emplace(pattern, idx); addr_groups.emplace_back(pattern); auto &g = addr_groups.back(); g.addrs.push_back(addr); g.affinity.type = params.affinity.type; if (params.affinity.type == SessionAffinity::COOKIE) { g.affinity.cookie.name = make_string_ref(downstreamconf.balloc, params.affinity.cookie.name); if (!params.affinity.cookie.path.empty()) { g.affinity.cookie.path = make_string_ref(downstreamconf.balloc, params.affinity.cookie.path); } g.affinity.cookie.secure = params.affinity.cookie.secure; g.affinity.cookie.stickiness = params.affinity.cookie.stickiness; } g.redirect_if_not_tls = params.redirect_if_not_tls; g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby); g.timeout.read = params.read_timeout; g.timeout.write = params.write_timeout; g.dnf = params.dnf; } return 0; } } // namespace namespace { ForwardedNode parse_forwarded_node_type(const std::string_view &optarg) { if (util::strieq("obfuscated"sv, optarg)) { return ForwardedNode::OBFUSCATED; } if (util::strieq("ip"sv, optarg)) { return ForwardedNode::IP; } if (optarg.size() < 2 || optarg[0] != '_') { return static_cast(-1); } if (std::ranges::find_if_not(optarg, [](auto c) { return util::is_alpha(c) || util::is_digit(c) || c == '.' || c == '_' || c == '-'; }) != std::ranges::end(optarg)) { return static_cast(-1); } return ForwardedNode::OBFUSCATED; } } // namespace namespace { int parse_error_page(std::vector &error_pages, const std::string_view &opt, const std::string_view &optarg) { std::array errbuf; auto eq = std::ranges::find(optarg, '='); if (eq == std::ranges::end(optarg) || eq + 1 == std::ranges::end(optarg)) { LOG(ERROR) << opt << ": bad value: '" << optarg << "'"; return -1; } auto codestr = std::string_view{std::ranges::begin(optarg), eq}; unsigned int code; if (codestr == "*"sv) { code = 0; } else { auto n = util::parse_uint(codestr); if (!n || n < 400 || n > 599) { LOG(ERROR) << opt << ": bad code: '" << codestr << "'"; return -1; } code = static_cast(*n); } auto path = std::string_view{eq + 1, std::ranges::end(optarg)}; std::vector content; auto fd = open(path.data(), O_RDONLY); if (fd == -1) { auto error = errno; LOG(ERROR) << opt << ": " << optarg << ": " << xsi_strerror(error, errbuf.data(), errbuf.size()); return -1; } auto fd_closer = defer(close, fd); std::array buf; for (;;) { auto n = read(fd, buf.data(), buf.size()); if (n == -1) { auto error = errno; LOG(ERROR) << opt << ": " << optarg << ": " << xsi_strerror(error, errbuf.data(), errbuf.size()); return -1; } if (n == 0) { break; } content.insert(std::ranges::end(content), std::ranges::begin(buf), std::ranges::begin(buf) + n); } error_pages.push_back(ErrorPage{std::move(content), code}); return 0; } } // namespace namespace { // Maximum size of SCT extension payload length. constexpr size_t MAX_SCT_EXT_LEN = 16_k; } // namespace struct SubcertParams { std::string_view sct_dir; }; namespace { // Parses subcert parameter |src_params|, and stores parsed results // into |out|. This function returns 0 if it succeeds, or -1. int parse_subcert_params(SubcertParams &out, const std::string_view &src_params) { auto last = std::ranges::end(src_params); for (auto first = std::ranges::begin(src_params); first != last;) { auto end = std::ranges::find(first, last, ';'); auto param = std::string_view{first, end}; if (util::istarts_with(param, "sct-dir="sv)) { #if defined(NGHTTP2_GENUINE_OPENSSL) || defined(NGHTTP2_OPENSSL_IS_BORINGSSL) auto sct_dir = std::string_view{std::ranges::begin(param) + str_size("sct-dir="), std::ranges::end(param)}; if (sct_dir.empty()) { LOG(ERROR) << "subcert: " << param << ": empty sct-dir"; return -1; } out.sct_dir = sct_dir; #else // !defined(NGHTTP2_GENUINE_OPENSSL) && // !defined(NGHTTP2_OPENSSL_IS_BORINGSSL) LOG(WARN) << "subcert: sct-dir is ignored because underlying TLS library " "does not support SCT"; #endif // !defined(NGHTTP2_GENUINE_OPENSSL) && // !defined(NGHTTP2_OPENSSL_IS_BORINGSSL) } else if (!param.empty()) { LOG(ERROR) << "subcert: " << param << ": unknown keyword"; return -1; } if (end == last) { break; } first = end + 1; } return 0; } } // namespace namespace { // Reads *.sct files from directory denoted by |dir_path|. |dir_path| // must be NULL-terminated string. int read_tls_sct_from_dir(std::vector &dst, const std::string_view &opt, const std::string_view &dir_path) { std::array errbuf; auto dir = opendir(dir_path.data()); if (dir == nullptr) { auto error = errno; LOG(ERROR) << opt << ": " << dir_path << ": " << xsi_strerror(error, errbuf.data(), errbuf.size()); return -1; } auto closer = defer(closedir, dir); // 2 bytes total length field auto len_idx = dst.size(); dst.insert(std::ranges::end(dst), 2, 0); for (;;) { errno = 0; auto ent = readdir(dir); if (ent == nullptr) { if (errno != 0) { auto error = errno; LOG(ERROR) << opt << ": failed to read directory " << dir_path << ": " << xsi_strerror(error, errbuf.data(), errbuf.size()); return -1; } break; } auto name = std::string_view{ent->d_name}; if (name[0] == '.' || !util::iends_with(name, ".sct"sv)) { continue; } std::string path; path.resize(dir_path.size() + 1 + name.size()); { auto p = std::ranges::begin(path); p = std::ranges::copy(dir_path, p).out; *p++ = '/'; std::ranges::copy(name, p); } auto fd = open(path.c_str(), O_RDONLY); if (fd == -1) { auto error = errno; LOG(ERROR) << opt << ": failed to read SCT from " << path << ": " << xsi_strerror(error, errbuf.data(), errbuf.size()); return -1; } auto closer = defer(close, fd); // 2 bytes length field for this SCT. auto len_idx = dst.size(); dst.insert(std::ranges::end(dst), 2, 0); // *.sct file tends to be small; around 110+ bytes. std::array buf; for (;;) { ssize_t nread; while ((nread = read(fd, buf.data(), buf.size())) == -1 && errno == EINTR) ; if (nread == -1) { auto error = errno; LOG(ERROR) << opt << ": failed to read SCT data from " << path << ": " << xsi_strerror(error, errbuf.data(), errbuf.size()); return -1; } if (nread == 0) { break; } dst.insert(std::ranges::end(dst), std::ranges::begin(buf), std::ranges::begin(buf) + nread); if (dst.size() > MAX_SCT_EXT_LEN) { LOG(ERROR) << opt << ": the concatenated SCT data from " << dir_path << " is too large. Max " << MAX_SCT_EXT_LEN; return -1; } } auto len = dst.size() - len_idx - 2; if (len == 0) { dst.resize(dst.size() - 2); continue; } dst[len_idx] = static_cast(len >> 8); dst[len_idx + 1] = static_cast(len); } auto len = dst.size() - len_idx - 2; if (len == 0) { dst.resize(dst.size() - 2); return 0; } dst[len_idx] = static_cast(len >> 8); dst[len_idx + 1] = static_cast(len); return 0; } } // namespace #ifndef OPENSSL_NO_PSK namespace { // Reads PSK secrets from path, and parses each line. The result is // directly stored into config->tls.psk_secrets. This function // returns 0 if it succeeds, or -1. int parse_psk_secrets(Config *config, const std::string_view &path) { auto &tlsconf = config->tls; std::ifstream f(path.data(), std::ios::binary); if (!f) { LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": could not open file " << path; return -1; } size_t lineno = 0; std::string line; while (std::getline(f, line)) { ++lineno; if (line.empty() || line[0] == '#') { continue; } auto sep_it = std::ranges::find(line, ':'); if (sep_it == std::ranges::end(line)) { LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": could not fine separator at line " << lineno; return -1; } if (sep_it == std::ranges::begin(line)) { LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": empty identity at line " << lineno; return -1; } if (sep_it + 1 == std::ranges::end(line)) { LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": empty secret at line " << lineno; return -1; } if (!util::is_hex_string(sep_it + 1, std::ranges::end(line))) { LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": secret must be hex string at line " << lineno; return -1; } auto identity = make_string_ref( config->balloc, std::string_view{std::ranges::begin(line), sep_it}); auto secret = as_string_view( util::decode_hex(config->balloc, sep_it + 1, std::ranges::end(line))); auto rv = tlsconf.psk_secrets.emplace(identity, secret); if (!rv.second) { LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": identity has already been registered at line " << lineno; return -1; } } return 0; } } // namespace #endif // !defined(OPENSSL_NO_PSK) #ifndef OPENSSL_NO_PSK namespace { // Reads PSK secrets from path, and parses each line. The result is // directly stored into config->tls.client.psk. This function returns // 0 if it succeeds, or -1. int parse_client_psk_secrets(Config *config, const std::string_view &path) { auto &tlsconf = config->tls; std::ifstream f(path.data(), std::ios::binary); if (!f) { LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": could not open file " << path; return -1; } size_t lineno = 0; std::string line; while (std::getline(f, line)) { ++lineno; if (line.empty() || line[0] == '#') { continue; } auto sep_it = std::ranges::find(line, ':'); if (sep_it == std::ranges::end(line)) { LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": could not find separator at line " << lineno; return -1; } if (sep_it == std::ranges::begin(line)) { LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": empty identity at line " << lineno; return -1; } if (sep_it + 1 == std::ranges::end(line)) { LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": empty secret at line " << lineno; return -1; } if (!util::is_hex_string(sep_it + 1, std::ranges::end(line))) { LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": secret must be hex string at line " << lineno; return -1; } tlsconf.client.psk.identity = make_string_ref( config->balloc, std::string_view{std::ranges::begin(line), sep_it}); tlsconf.client.psk.secret = as_string_view( util::decode_hex(config->balloc, sep_it + 1, std::ranges::end(line))); return 0; } return 0; } } // namespace #endif // !defined(OPENSSL_NO_PSK) // generated by gennghttpxfun.py int option_lookup_token(const std::string_view &name) { switch (name.size()) { case 4: switch (name[3]) { case 'f': if (util::strieq("con"sv, name.substr(0, 3))) { return SHRPX_OPTID_CONF; } break; case 'r': if (util::strieq("use"sv, name.substr(0, 3))) { return SHRPX_OPTID_USER; } break; } break; case 6: switch (name[5]) { case 'a': if (util::strieq("no-vi"sv, name.substr(0, 5))) { return SHRPX_OPTID_NO_VIA; } break; case 'c': if (util::strieq("altsv"sv, name.substr(0, 5))) { return SHRPX_OPTID_ALTSVC; } break; case 'n': if (util::strieq("daemo"sv, name.substr(0, 5))) { return SHRPX_OPTID_DAEMON; } break; case 's': if (util::strieq("group"sv, name.substr(0, 5))) { return SHRPX_OPTID_GROUPS; } break; case 't': if (util::strieq("cacer"sv, name.substr(0, 5))) { return SHRPX_OPTID_CACERT; } if (util::strieq("clien"sv, name.substr(0, 5))) { return SHRPX_OPTID_CLIENT; } break; } break; case 7: switch (name[6]) { case 'd': if (util::strieq("backen"sv, name.substr(0, 6))) { return SHRPX_OPTID_BACKEND; } break; case 'e': if (util::strieq("includ"sv, name.substr(0, 6))) { return SHRPX_OPTID_INCLUDE; } break; case 'g': if (util::strieq("backlo"sv, name.substr(0, 6))) { return SHRPX_OPTID_BACKLOG; } if (util::strieq("paddin"sv, name.substr(0, 6))) { return SHRPX_OPTID_PADDING; } break; case 'p': if (util::strieq("no-ocs"sv, name.substr(0, 6))) { return SHRPX_OPTID_NO_OCSP; } break; case 's': if (util::strieq("cipher"sv, name.substr(0, 6))) { return SHRPX_OPTID_CIPHERS; } if (util::strieq("worker"sv, name.substr(0, 6))) { return SHRPX_OPTID_WORKERS; } break; case 't': if (util::strieq("subcer"sv, name.substr(0, 6))) { return SHRPX_OPTID_SUBCERT; } break; } break; case 8: switch (name[7]) { case 'd': if (util::strieq("fronten"sv, name.substr(0, 7))) { return SHRPX_OPTID_FRONTEND; } break; case 'e': if (util::strieq("insecur"sv, name.substr(0, 7))) { return SHRPX_OPTID_INSECURE; } if (util::strieq("pid-fil"sv, name.substr(0, 7))) { return SHRPX_OPTID_PID_FILE; } break; case 'n': if (util::strieq("fastope"sv, name.substr(0, 7))) { return SHRPX_OPTID_FASTOPEN; } break; case 's': if (util::strieq("tls-ktl"sv, name.substr(0, 7))) { return SHRPX_OPTID_TLS_KTLS; } break; case 't': if (util::strieq("npn-lis"sv, name.substr(0, 7))) { return SHRPX_OPTID_NPN_LIST; } break; } break; case 9: switch (name[8]) { case 'e': if (util::strieq("no-kqueu"sv, name.substr(0, 8))) { return SHRPX_OPTID_NO_KQUEUE; } if (util::strieq("read-rat"sv, name.substr(0, 8))) { return SHRPX_OPTID_READ_RATE; } break; case 'l': if (util::strieq("log-leve"sv, name.substr(0, 8))) { return SHRPX_OPTID_LOG_LEVEL; } break; case 't': if (util::strieq("alpn-lis"sv, name.substr(0, 8))) { return SHRPX_OPTID_ALPN_LIST; } break; } break; case 10: switch (name[9]) { case 'e': if (util::strieq("error-pag"sv, name.substr(0, 9))) { return SHRPX_OPTID_ERROR_PAGE; } if (util::strieq("mruby-fil"sv, name.substr(0, 9))) { return SHRPX_OPTID_MRUBY_FILE; } if (util::strieq("write-rat"sv, name.substr(0, 9))) { return SHRPX_OPTID_WRITE_RATE; } break; case 't': if (util::strieq("read-burs"sv, name.substr(0, 9))) { return SHRPX_OPTID_READ_BURST; } break; } break; case 11: switch (name[10]) { case 'e': if (util::strieq("server-nam"sv, name.substr(0, 10))) { return SHRPX_OPTID_SERVER_NAME; } break; case 'f': if (util::strieq("no-quic-bp"sv, name.substr(0, 10))) { return SHRPX_OPTID_NO_QUIC_BPF; } break; case 'r': if (util::strieq("tls-sct-di"sv, name.substr(0, 10))) { return SHRPX_OPTID_TLS_SCT_DIR; } break; case 's': if (util::strieq("backend-tl"sv, name.substr(0, 10))) { return SHRPX_OPTID_BACKEND_TLS; } if (util::strieq("ecdh-curve"sv, name.substr(0, 10))) { return SHRPX_OPTID_ECDH_CURVES; } if (util::strieq("psk-secret"sv, name.substr(0, 10))) { return SHRPX_OPTID_PSK_SECRETS; } break; case 't': if (util::strieq("write-burs"sv, name.substr(0, 10))) { return SHRPX_OPTID_WRITE_BURST; } break; case 'y': if (util::strieq("dns-max-tr"sv, name.substr(0, 10))) { return SHRPX_OPTID_DNS_MAX_TRY; } if (util::strieq("http2-prox"sv, name.substr(0, 10))) { return SHRPX_OPTID_HTTP2_PROXY; } break; } break; case 12: switch (name[11]) { case '4': if (util::strieq("backend-ipv"sv, name.substr(0, 11))) { return SHRPX_OPTID_BACKEND_IPV4; } break; case '6': if (util::strieq("backend-ipv"sv, name.substr(0, 11))) { return SHRPX_OPTID_BACKEND_IPV6; } break; case 'c': if (util::strieq("http2-altsv"sv, name.substr(0, 11))) { return SHRPX_OPTID_HTTP2_ALTSVC; } break; case 'e': if (util::strieq("host-rewrit"sv, name.substr(0, 11))) { return SHRPX_OPTID_HOST_REWRITE; } if (util::strieq("http2-bridg"sv, name.substr(0, 11))) { return SHRPX_OPTID_HTTP2_BRIDGE; } break; case 'p': if (util::strieq("ocsp-startu"sv, name.substr(0, 11))) { return SHRPX_OPTID_OCSP_STARTUP; } break; case 'y': if (util::strieq("client-prox"sv, name.substr(0, 11))) { return SHRPX_OPTID_CLIENT_PROXY; } if (util::strieq("forwarded-b"sv, name.substr(0, 11))) { return SHRPX_OPTID_FORWARDED_BY; } break; } break; case 13: switch (name[12]) { case 'd': if (util::strieq("add-forwarde"sv, name.substr(0, 12))) { return SHRPX_OPTID_ADD_FORWARDED; } if (util::strieq("single-threa"sv, name.substr(0, 12))) { return SHRPX_OPTID_SINGLE_THREAD; } break; case 'e': if (util::strieq("dh-param-fil"sv, name.substr(0, 12))) { return SHRPX_OPTID_DH_PARAM_FILE; } if (util::strieq("errorlog-fil"sv, name.substr(0, 12))) { return SHRPX_OPTID_ERRORLOG_FILE; } if (util::strieq("rlimit-nofil"sv, name.substr(0, 12))) { return SHRPX_OPTID_RLIMIT_NOFILE; } break; case 'r': if (util::strieq("forwarded-fo"sv, name.substr(0, 12))) { return SHRPX_OPTID_FORWARDED_FOR; } break; case 's': if (util::strieq("tls13-cipher"sv, name.substr(0, 12))) { return SHRPX_OPTID_TLS13_CIPHERS; } break; case 't': if (util::strieq("verify-clien"sv, name.substr(0, 12))) { return SHRPX_OPTID_VERIFY_CLIENT; } break; } break; case 14: switch (name[13]) { case 'd': if (util::strieq("quic-server-i"sv, name.substr(0, 13))) { return SHRPX_OPTID_QUIC_SERVER_ID; } break; case 'e': if (util::strieq("accesslog-fil"sv, name.substr(0, 13))) { return SHRPX_OPTID_ACCESSLOG_FILE; } break; case 'h': if (util::strieq("no-server-pus"sv, name.substr(0, 13))) { return SHRPX_OPTID_NO_SERVER_PUSH; } break; case 'k': if (util::strieq("rlimit-memloc"sv, name.substr(0, 13))) { return SHRPX_OPTID_RLIMIT_MEMLOCK; } break; case 'p': if (util::strieq("no-verify-ocs"sv, name.substr(0, 13))) { return SHRPX_OPTID_NO_VERIFY_OCSP; } break; case 's': if (util::strieq("backend-no-tl"sv, name.substr(0, 13))) { return SHRPX_OPTID_BACKEND_NO_TLS; } if (util::strieq("client-cipher"sv, name.substr(0, 13))) { return SHRPX_OPTID_CLIENT_CIPHERS; } if (util::strieq("single-proces"sv, name.substr(0, 13))) { return SHRPX_OPTID_SINGLE_PROCESS; } break; case 't': if (util::strieq("tls-proto-lis"sv, name.substr(0, 13))) { return SHRPX_OPTID_TLS_PROTO_LIST; } break; } break; case 15: switch (name[14]) { case 'e': if (util::strieq("no-host-rewrit"sv, name.substr(0, 14))) { return SHRPX_OPTID_NO_HOST_REWRITE; } break; case 'g': if (util::strieq("errorlog-syslo"sv, name.substr(0, 14))) { return SHRPX_OPTID_ERRORLOG_SYSLOG; } break; case 's': if (util::strieq("frontend-no-tl"sv, name.substr(0, 14))) { return SHRPX_OPTID_FRONTEND_NO_TLS; } break; case 'y': if (util::strieq("syslog-facilit"sv, name.substr(0, 14))) { return SHRPX_OPTID_SYSLOG_FACILITY; } break; } break; case 16: switch (name[15]) { case 'e': if (util::strieq("certificate-fil"sv, name.substr(0, 15))) { return SHRPX_OPTID_CERTIFICATE_FILE; } if (util::strieq("client-cert-fil"sv, name.substr(0, 15))) { return SHRPX_OPTID_CLIENT_CERT_FILE; } if (util::strieq("private-key-fil"sv, name.substr(0, 15))) { return SHRPX_OPTID_PRIVATE_KEY_FILE; } if (util::strieq("worker-read-rat"sv, name.substr(0, 15))) { return SHRPX_OPTID_WORKER_READ_RATE; } break; case 'g': if (util::strieq("accesslog-syslo"sv, name.substr(0, 15))) { return SHRPX_OPTID_ACCESSLOG_SYSLOG; } break; case 't': if (util::strieq("accesslog-forma"sv, name.substr(0, 15))) { return SHRPX_OPTID_ACCESSLOG_FORMAT; } break; } break; case 17: switch (name[16]) { case 'e': if (util::strieq("no-server-rewrit"sv, name.substr(0, 16))) { return SHRPX_OPTID_NO_SERVER_REWRITE; } if (util::strieq("worker-write-rat"sv, name.substr(0, 16))) { return SHRPX_OPTID_WORKER_WRITE_RATE; } break; case 's': if (util::strieq("backend-http1-tl"sv, name.substr(0, 16))) { return SHRPX_OPTID_BACKEND_HTTP1_TLS; } if (util::strieq("max-header-field"sv, name.substr(0, 16))) { return SHRPX_OPTID_MAX_HEADER_FIELDS; } break; case 't': if (util::strieq("dns-cache-timeou"sv, name.substr(0, 16))) { return SHRPX_OPTID_DNS_CACHE_TIMEOUT; } if (util::strieq("worker-read-burs"sv, name.substr(0, 16))) { return SHRPX_OPTID_WORKER_READ_BURST; } break; } break; case 18: switch (name[17]) { case 'a': if (util::strieq("tls-max-early-dat"sv, name.substr(0, 17))) { return SHRPX_OPTID_TLS_MAX_EARLY_DATA; } break; case 'r': if (util::strieq("add-request-heade"sv, name.substr(0, 17))) { return SHRPX_OPTID_ADD_REQUEST_HEADER; } break; case 's': if (util::strieq("client-psk-secret"sv, name.substr(0, 17))) { return SHRPX_OPTID_CLIENT_PSK_SECRETS; } break; case 't': if (util::strieq("dns-lookup-timeou"sv, name.substr(0, 17))) { return SHRPX_OPTID_DNS_LOOKUP_TIMEOUT; } if (util::strieq("worker-write-burs"sv, name.substr(0, 17))) { return SHRPX_OPTID_WORKER_WRITE_BURST; } break; } break; case 19: switch (name[18]) { case 'e': if (util::strieq("no-location-rewrit"sv, name.substr(0, 18))) { return SHRPX_OPTID_NO_LOCATION_REWRITE; } if (util::strieq("require-http-schem"sv, name.substr(0, 18))) { return SHRPX_OPTID_REQUIRE_HTTP_SCHEME; } if (util::strieq("tls-ticket-key-fil"sv, name.substr(0, 18))) { return SHRPX_OPTID_TLS_TICKET_KEY_FILE; } break; case 'f': if (util::strieq("backend-max-backof"sv, name.substr(0, 18))) { return SHRPX_OPTID_BACKEND_MAX_BACKOFF; } break; case 'r': if (util::strieq("add-response-heade"sv, name.substr(0, 18))) { return SHRPX_OPTID_ADD_RESPONSE_HEADER; } if (util::strieq("add-x-forwarded-fo"sv, name.substr(0, 18))) { return SHRPX_OPTID_ADD_X_FORWARDED_FOR; } if (util::strieq("header-field-buffe"sv, name.substr(0, 18))) { return SHRPX_OPTID_HEADER_FIELD_BUFFER; } break; case 't': if (util::strieq("redirect-https-por"sv, name.substr(0, 18))) { return SHRPX_OPTID_REDIRECT_HTTPS_PORT; } if (util::strieq("stream-read-timeou"sv, name.substr(0, 18))) { return SHRPX_OPTID_STREAM_READ_TIMEOUT; } break; } break; case 20: switch (name[19]) { case 'g': if (util::strieq("frontend-frame-debu"sv, name.substr(0, 19))) { return SHRPX_OPTID_FRONTEND_FRAME_DEBUG; } break; case 'l': if (util::strieq("ocsp-update-interva"sv, name.substr(0, 19))) { return SHRPX_OPTID_OCSP_UPDATE_INTERVAL; } break; case 's': if (util::strieq("max-worker-processe"sv, name.substr(0, 19))) { return SHRPX_OPTID_MAX_WORKER_PROCESSES; } if (util::strieq("tls13-client-cipher"sv, name.substr(0, 19))) { return SHRPX_OPTID_TLS13_CLIENT_CIPHERS; } break; case 't': if (util::strieq("backend-read-timeou"sv, name.substr(0, 19))) { return SHRPX_OPTID_BACKEND_READ_TIMEOUT; } if (util::strieq("stream-write-timeou"sv, name.substr(0, 19))) { return SHRPX_OPTID_STREAM_WRITE_TIMEOUT; } if (util::strieq("verify-client-cacer"sv, name.substr(0, 19))) { return SHRPX_OPTID_VERIFY_CLIENT_CACERT; } break; case 'y': if (util::strieq("api-max-request-bod"sv, name.substr(0, 19))) { return SHRPX_OPTID_API_MAX_REQUEST_BODY; } break; } break; case 21: switch (name[20]) { case 'd': if (util::strieq("backend-tls-sni-fiel"sv, name.substr(0, 20))) { return SHRPX_OPTID_BACKEND_TLS_SNI_FIELD; } break; case 'e': if (util::strieq("quic-bpf-program-fil"sv, name.substr(0, 20))) { return SHRPX_OPTID_QUIC_BPF_PROGRAM_FILE; } break; case 'l': if (util::strieq("accept-proxy-protoco"sv, name.substr(0, 20))) { return SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL; } break; case 'n': if (util::strieq("tls-max-proto-versio"sv, name.substr(0, 20))) { return SHRPX_OPTID_TLS_MAX_PROTO_VERSION; } if (util::strieq("tls-min-proto-versio"sv, name.substr(0, 20))) { return SHRPX_OPTID_TLS_MIN_PROTO_VERSION; } break; case 'r': if (util::strieq("tls-ticket-key-ciphe"sv, name.substr(0, 20))) { return SHRPX_OPTID_TLS_TICKET_KEY_CIPHER; } break; case 's': if (util::strieq("frontend-max-request"sv, name.substr(0, 20))) { return SHRPX_OPTID_FRONTEND_MAX_REQUESTS; } break; case 't': if (util::strieq("backend-write-timeou"sv, name.substr(0, 20))) { return SHRPX_OPTID_BACKEND_WRITE_TIMEOUT; } if (util::strieq("frontend-read-timeou"sv, name.substr(0, 20))) { return SHRPX_OPTID_FRONTEND_READ_TIMEOUT; } break; case 'y': if (util::strieq("accesslog-write-earl"sv, name.substr(0, 20))) { return SHRPX_OPTID_ACCESSLOG_WRITE_EARLY; } break; } break; case 22: switch (name[21]) { case 'i': if (util::strieq("backend-http-proxy-ur"sv, name.substr(0, 21))) { return SHRPX_OPTID_BACKEND_HTTP_PROXY_URI; } break; case 'r': if (util::strieq("backend-request-buffe"sv, name.substr(0, 21))) { return SHRPX_OPTID_BACKEND_REQUEST_BUFFER; } if (util::strieq("frontend-quic-qlog-di"sv, name.substr(0, 21))) { return SHRPX_OPTID_FRONTEND_QUIC_QLOG_DIR; } break; case 't': if (util::strieq("frontend-write-timeou"sv, name.substr(0, 21))) { return SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT; } break; case 'y': if (util::strieq("backend-address-famil"sv, name.substr(0, 21))) { return SHRPX_OPTID_BACKEND_ADDRESS_FAMILY; } break; } break; case 23: switch (name[22]) { case 'e': if (util::strieq("client-private-key-fil"sv, name.substr(0, 22))) { return SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE; } if (util::strieq("private-key-passwd-fil"sv, name.substr(0, 22))) { return SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE; } break; case 'g': if (util::strieq("frontend-quic-debug-lo"sv, name.substr(0, 22))) { return SHRPX_OPTID_FRONTEND_QUIC_DEBUG_LOG; } break; case 'r': if (util::strieq("backend-response-buffe"sv, name.substr(0, 22))) { return SHRPX_OPTID_BACKEND_RESPONSE_BUFFER; } break; case 't': if (util::strieq("backend-connect-timeou"sv, name.substr(0, 22))) { return SHRPX_OPTID_BACKEND_CONNECT_TIMEOUT; } if (util::strieq("frontend-header-timeou"sv, name.substr(0, 22))) { return SHRPX_OPTID_FRONTEND_HEADER_TIMEOUT; } break; } break; case 24: switch (name[23]) { case 'a': if (util::strieq("frontend-quic-early-dat"sv, name.substr(0, 23))) { return SHRPX_OPTID_FRONTEND_QUIC_EARLY_DATA; } break; case 'd': if (util::strieq("strip-incoming-forwarde"sv, name.substr(0, 23))) { return SHRPX_OPTID_STRIP_INCOMING_FORWARDED; } if (util::strieq("tls-ticket-key-memcache"sv, name.substr(0, 23))) { return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED; } break; case 'e': if (util::strieq("fetch-ocsp-response-fil"sv, name.substr(0, 23))) { return SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE; } break; case 'o': if (util::strieq("no-add-x-forwarded-prot"sv, name.substr(0, 23))) { return SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO; } break; case 't': if (util::strieq("listener-disable-timeou"sv, name.substr(0, 23))) { return SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT; } if (util::strieq("tls-dyn-rec-idle-timeou"sv, name.substr(0, 23))) { return SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT; } break; } break; case 25: switch (name[24]) { case 'e': if (util::strieq("backend-http2-window-siz"sv, name.substr(0, 24))) { return SHRPX_OPTID_BACKEND_HTTP2_WINDOW_SIZE; } if (util::strieq("frontend-quic-secret-fil"sv, name.substr(0, 24))) { return SHRPX_OPTID_FRONTEND_QUIC_SECRET_FILE; } break; case 'g': if (util::strieq("http2-no-cookie-crumblin"sv, name.substr(0, 24))) { return SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING; } break; case 's': if (util::strieq("backend-http2-window-bit"sv, name.substr(0, 24))) { return SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS; } if (util::strieq("max-request-header-field"sv, name.substr(0, 24))) { return SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS; } break; case 't': if (util::strieq("frontend-quic-initial-rt"sv, name.substr(0, 24))) { return SHRPX_OPTID_FRONTEND_QUIC_INITIAL_RTT; } break; } break; case 26: switch (name[25]) { case 'a': if (util::strieq("tls-no-postpone-early-dat"sv, name.substr(0, 25))) { return SHRPX_OPTID_TLS_NO_POSTPONE_EARLY_DATA; } break; case 'e': if (util::strieq("frontend-http2-window-siz"sv, name.substr(0, 25))) { return SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE; } if (util::strieq("frontend-http3-window-siz"sv, name.substr(0, 25))) { return SHRPX_OPTID_FRONTEND_HTTP3_WINDOW_SIZE; } break; case 's': if (util::strieq("frontend-http2-window-bit"sv, name.substr(0, 25))) { return SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS; } if (util::strieq("max-response-header-field"sv, name.substr(0, 25))) { return SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS; } break; case 't': if (util::strieq("backend-keep-alive-timeou"sv, name.substr(0, 25))) { return SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT; } if (util::strieq("frontend-quic-idle-timeou"sv, name.substr(0, 25))) { return SHRPX_OPTID_FRONTEND_QUIC_IDLE_TIMEOUT; } if (util::strieq("no-http2-cipher-black-lis"sv, name.substr(0, 25))) { return SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST; } if (util::strieq("no-http2-cipher-block-lis"sv, name.substr(0, 25))) { return SHRPX_OPTID_NO_HTTP2_CIPHER_BLOCK_LIST; } break; } break; case 27: switch (name[26]) { case 'd': if (util::strieq("tls-session-cache-memcache"sv, name.substr(0, 26))) { return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED; } break; case 'n': if (util::strieq("frontend-quic-require-toke"sv, name.substr(0, 26))) { return SHRPX_OPTID_FRONTEND_QUIC_REQUIRE_TOKEN; } break; case 'r': if (util::strieq("request-header-field-buffe"sv, name.substr(0, 26))) { return SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER; } break; case 's': if (util::strieq("worker-frontend-connection"sv, name.substr(0, 26))) { return SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS; } break; case 't': if (util::strieq("frontend-http2-idle-timeou"sv, name.substr(0, 26))) { return SHRPX_OPTID_FRONTEND_HTTP2_IDLE_TIMEOUT; } if (util::strieq("frontend-http2-read-timeou"sv, name.substr(0, 26))) { return SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT; } if (util::strieq("frontend-http3-idle-timeou"sv, name.substr(0, 26))) { return SHRPX_OPTID_FRONTEND_HTTP3_IDLE_TIMEOUT; } if (util::strieq("frontend-http3-read-timeou"sv, name.substr(0, 26))) { return SHRPX_OPTID_FRONTEND_HTTP3_READ_TIMEOUT; } if (util::strieq("frontend-keep-alive-timeou"sv, name.substr(0, 26))) { return SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT; } break; } break; case 28: switch (name[27]) { case 'a': if (util::strieq("no-strip-incoming-early-dat"sv, name.substr(0, 27))) { return SHRPX_OPTID_NO_STRIP_INCOMING_EARLY_DATA; } break; case 'd': if (util::strieq("tls-dyn-rec-warmup-threshol"sv, name.substr(0, 27))) { return SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD; } break; case 'r': if (util::strieq("response-header-field-buffe"sv, name.substr(0, 27))) { return SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER; } break; case 's': if (util::strieq("http2-max-concurrent-stream"sv, name.substr(0, 27))) { return SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS; } if (util::strieq("tls-ticket-key-memcached-tl"sv, name.substr(0, 27))) { return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS; } break; case 't': if (util::strieq("backend-connections-per-hos"sv, name.substr(0, 27))) { return SHRPX_OPTID_BACKEND_CONNECTIONS_PER_HOST; } break; } break; case 30: switch (name[29]) { case 'd': if (util::strieq("verify-client-tolerate-expire"sv, name.substr(0, 29))) { return SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED; } break; case 'e': if (util::strieq("frontend-http3-max-window-siz"sv, name.substr(0, 29))) { return SHRPX_OPTID_FRONTEND_HTTP3_MAX_WINDOW_SIZE; } break; case 'r': if (util::strieq("ignore-per-pattern-mruby-erro"sv, name.substr(0, 29))) { return SHRPX_OPTID_IGNORE_PER_PATTERN_MRUBY_ERROR; } if (util::strieq("strip-incoming-x-forwarded-fo"sv, name.substr(0, 29))) { return SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR; } break; case 't': if (util::strieq("backend-http2-settings-timeou"sv, name.substr(0, 29))) { return SHRPX_OPTID_BACKEND_HTTP2_SETTINGS_TIMEOUT; } break; } break; case 31: switch (name[30]) { case 's': if (util::strieq("tls-session-cache-memcached-tl"sv, name.substr(0, 30))) { return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_TLS; } break; case 't': if (util::strieq("frontend-http2-settings-timeou"sv, name.substr(0, 30))) { return SHRPX_OPTID_FRONTEND_HTTP2_SETTINGS_TIMEOUT; } break; } break; case 32: switch (name[31]) { case 'd': if (util::strieq("backend-connections-per-fronten"sv, name.substr(0, 31))) { return SHRPX_OPTID_BACKEND_CONNECTIONS_PER_FRONTEND; } break; } break; case 33: switch (name[32]) { case 'l': if (util::strieq("tls-ticket-key-memcached-interva"sv, name.substr(0, 32))) { return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL; } if (util::strieq("tls-ticket-key-memcached-max-fai"sv, name.substr(0, 32))) { return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL; } break; case 't': if (util::strieq("client-no-http2-cipher-black-lis"sv, name.substr(0, 32))) { return SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST; } if (util::strieq("client-no-http2-cipher-block-lis"sv, name.substr(0, 32))) { return SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLOCK_LIST; } break; } break; case 34: switch (name[33]) { case 'e': if (util::strieq("tls-ticket-key-memcached-cert-fil"sv, name.substr(0, 33))) { return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE; } break; case 'r': if (util::strieq("frontend-http2-dump-request-heade"sv, name.substr(0, 33))) { return SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER; } break; case 't': if (util::strieq("backend-http1-connections-per-hos"sv, name.substr(0, 33))) { return SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_HOST; } break; case 'y': if (util::strieq("tls-ticket-key-memcached-max-retr"sv, name.substr(0, 33))) { return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY; } break; } break; case 35: switch (name[34]) { case 'e': if (util::strieq("frontend-http2-optimize-window-siz"sv, name.substr(0, 34))) { return SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE; } break; case 'o': if (util::strieq("no-strip-incoming-x-forwarded-prot"sv, name.substr(0, 34))) { return SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO; } break; case 'r': if (util::strieq("frontend-http2-dump-response-heade"sv, name.substr(0, 34))) { return SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER; } if (util::strieq("frontend-quic-congestion-controlle"sv, name.substr(0, 34))) { return SHRPX_OPTID_FRONTEND_QUIC_CONGESTION_CONTROLLER; } break; } break; case 36: switch (name[35]) { case 'd': if (util::strieq("worker-process-grace-shutdown-perio"sv, name.substr(0, 35))) { return SHRPX_OPTID_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD; } break; case 'e': if (util::strieq("backend-http2-connection-window-siz"sv, name.substr(0, 35))) { return SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE; } break; case 'r': if (util::strieq("backend-http2-connections-per-worke"sv, name.substr(0, 35))) { return SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER; } break; case 's': if (util::strieq("backend-http2-connection-window-bit"sv, name.substr(0, 35))) { return SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_BITS; } if (util::strieq("backend-http2-max-concurrent-stream"sv, name.substr(0, 35))) { return SHRPX_OPTID_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS; } break; } break; case 37: switch (name[36]) { case 'e': if (util::strieq("frontend-http2-connection-window-siz"sv, name.substr(0, 36))) { return SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE; } if (util::strieq("frontend-http3-connection-window-siz"sv, name.substr(0, 36))) { return SHRPX_OPTID_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE; } if (util::strieq("tls-session-cache-memcached-cert-fil"sv, name.substr(0, 36))) { return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE; } break; case 's': if (util::strieq("frontend-http2-connection-window-bit"sv, name.substr(0, 36))) { return SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS; } if (util::strieq("frontend-http2-max-concurrent-stream"sv, name.substr(0, 36))) { return SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS; } if (util::strieq("frontend-http3-max-concurrent-stream"sv, name.substr(0, 36))) { return SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS; } break; } break; case 38: switch (name[37]) { case 'd': if (util::strieq("backend-http1-connections-per-fronten"sv, name.substr(0, 37))) { return SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND; } break; } break; case 39: switch (name[38]) { case 'y': if (util::strieq("tls-ticket-key-memcached-address-famil"sv, name.substr(0, 38))) { return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY; } break; } break; case 40: switch (name[39]) { case 'e': if (util::strieq("backend-http2-decoder-dynamic-table-siz"sv, name.substr(0, 39))) { return SHRPX_OPTID_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE; } if (util::strieq("backend-http2-encoder-dynamic-table-siz"sv, name.substr(0, 39))) { return SHRPX_OPTID_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE; } break; } break; case 41: switch (name[40]) { case 'e': if (util::strieq("frontend-http2-decoder-dynamic-table-siz"sv, name.substr(0, 40))) { return SHRPX_OPTID_FRONTEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE; } if (util::strieq("frontend-http2-encoder-dynamic-table-siz"sv, name.substr(0, 40))) { return SHRPX_OPTID_FRONTEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE; } if (util::strieq("frontend-http2-optimize-write-buffer-siz"sv, name.substr(0, 40))) { return SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE; } if (util::strieq("frontend-http3-max-connection-window-siz"sv, name.substr(0, 40))) { return SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE; } if (util::strieq("tls-ticket-key-memcached-private-key-fil"sv, name.substr(0, 40))) { return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE; } break; } break; case 42: switch (name[41]) { case 'y': if (util::strieq("tls-session-cache-memcached-address-famil"sv, name.substr(0, 41))) { return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY; } break; } break; case 44: switch (name[43]) { case 'e': if (util::strieq("tls-session-cache-memcached-private-key-fil"sv, name.substr(0, 43))) { return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE; } break; } break; } return -1; } int parse_config( Config *config, const std::string_view &opt, const std::string_view &optarg, std::unordered_set &included_set, std::unordered_map &pattern_addr_indexer) { auto optid = option_lookup_token(opt); return parse_config(config, optid, opt, optarg, included_set, pattern_addr_indexer); } int parse_config( Config *config, int optid, const std::string_view &opt, const std::string_view &optarg, std::unordered_set &included_set, std::unordered_map &pattern_addr_indexer) { std::array errbuf; switch (optid) { case SHRPX_OPTID_BACKEND: { auto &downstreamconf = *config->conn.downstream; auto addr_end = std::ranges::find(optarg, ';'); DownstreamAddrConfig addr{}; if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) { auto path = std::ranges::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size(); addr.host = make_string_ref(downstreamconf.balloc, std::string_view{path, addr_end}); addr.host_unix = true; } else { auto hp = split_host_port( downstreamconf.balloc, std::string_view{std::ranges::begin(optarg), addr_end}, opt); if (!hp) { return -1; } addr.host = std::move(hp->host); addr.port = hp->port; } auto mapping = addr_end == std::ranges::end(optarg) ? addr_end : addr_end + 1; auto mapping_end = std::ranges::find(mapping, std::ranges::end(optarg), ';'); auto params = mapping_end == std::ranges::end(optarg) ? mapping_end : mapping_end + 1; if (parse_mapping(config, addr, pattern_addr_indexer, std::string_view{mapping, mapping_end}, std::string_view{params, std::ranges::end(optarg)}) != 0) { return -1; } return 0; } case SHRPX_OPTID_FRONTEND: { auto &apiconf = config->api; auto addr_end = std::ranges::find(optarg, ';'); auto src_params = std::string_view{addr_end, std::ranges::end(optarg)}; UpstreamParams params{ .tls = true, }; if (parse_upstream_params(params, src_params) != 0) { return -1; } if (params.sni_fwd && !params.tls) { LOG(ERROR) << "frontend: sni_fwd requires tls"; return -1; } if (params.quic) { if (params.alt_mode != UpstreamAltMode::NONE) { LOG(ERROR) << "frontend: api or healthmon cannot be used with quic"; return -1; } if (!params.tls) { LOG(ERROR) << "frontend: quic requires TLS"; return -1; } } UpstreamAddr addr{ .alt_mode = params.alt_mode, .tls = params.tls, .sni_fwd = params.sni_fwd, .accept_proxy_protocol = params.proxyproto, .quic = params.quic, .fd = -1, }; if (addr.alt_mode == UpstreamAltMode::API) { apiconf.enabled = true; } #ifdef ENABLE_HTTP3 auto &addrs = params.quic ? config->conn.quic_listener.addrs : config->conn.listener.addrs; #else // !defined(ENABLE_HTTP3) auto &addrs = config->conn.listener.addrs; #endif // !defined(ENABLE_HTTP3) if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) { if (addr.quic) { LOG(ERROR) << "frontend: quic cannot be used on UNIX domain socket"; return -1; } auto path = std::ranges::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size(); addr.host = make_string_ref(config->balloc, std::string_view{path, addr_end}); addr.host_unix = true; addr.index = addrs.size(); addrs.push_back(std::move(addr)); return 0; } auto hp = split_host_port( config->balloc, std::string_view{std::ranges::begin(optarg), addr_end}, opt); if (!hp) { return -1; } addr.host = std::move(hp->host); addr.port = hp->port; if (util::numeric_host(addr.host.data(), AF_INET)) { addr.family = AF_INET; addr.index = addrs.size(); addrs.push_back(std::move(addr)); return 0; } if (util::numeric_host(addr.host.data(), AF_INET6)) { addr.family = AF_INET6; addr.index = addrs.size(); addrs.push_back(std::move(addr)); return 0; } addr.family = AF_INET; addr.index = addrs.size(); addrs.push_back(addr); addr.family = AF_INET6; addr.index = addrs.size(); addrs.push_back(std::move(addr)); return 0; } case SHRPX_OPTID_WORKERS: { #ifdef NOTHREADS LOG(WARN) << "Threading disabled at build time, no threads created."; return 0; #else // !defined(NOTHREADS) size_t n; if (parse_uint(&n, opt, optarg) != 0) { return -1; } if (n > 65530) { LOG(ERROR) << opt << ": the number of workers must not exceed 65530"; return -1; } config->num_worker = n; return 0; #endif // !defined(NOTHREADS) } case SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS: { LOG(WARN) << opt << ": deprecated. Use " << SHRPX_OPT_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS << " and " << SHRPX_OPT_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS << " instead."; size_t n; if (parse_uint(&n, opt, optarg) != 0) { return -1; } auto &http2conf = config->http2; http2conf.upstream.max_concurrent_streams = n; http2conf.downstream.max_concurrent_streams = n; return 0; } case SHRPX_OPTID_LOG_LEVEL: { auto level = Log::get_severity_level_by_name(optarg); if (level == -1) { LOG(ERROR) << opt << ": Invalid severity level: " << optarg; return -1; } config->logging.severity = level; return 0; } case SHRPX_OPTID_DAEMON: config->daemon = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_HTTP2_PROXY: config->http2_proxy = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_HTTP2_BRIDGE: LOG(ERROR) << opt << ": deprecated. Use backend=,;;proto=h2;tls"; return -1; case SHRPX_OPTID_CLIENT_PROXY: LOG(ERROR) << opt << ": deprecated. Use http2-proxy, frontend=,;no-tls " "and backend=,;;proto=h2;tls"; return -1; case SHRPX_OPTID_ADD_X_FORWARDED_FOR: config->http.xff.add = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR: config->http.xff.strip_incoming = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_NO_VIA: config->http.no_via = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT: LOG(WARN) << opt << ": deprecated. Use frontend-http2-idle-timeout"; // fall through case SHRPX_OPTID_FRONTEND_HTTP2_IDLE_TIMEOUT: return parse_duration(&config->conn.upstream.timeout.http2_idle, opt, optarg); case SHRPX_OPTID_FRONTEND_READ_TIMEOUT: LOG(WARN) << opt << ": deprecated. Use frontend-header-timeout"; return 0; case SHRPX_OPTID_FRONTEND_HEADER_TIMEOUT: return parse_duration(&config->http.timeout.header, opt, optarg); case SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT: return parse_duration(&config->conn.upstream.timeout.write, opt, optarg); case SHRPX_OPTID_BACKEND_READ_TIMEOUT: return parse_duration(&config->conn.downstream->timeout.read, opt, optarg); case SHRPX_OPTID_BACKEND_WRITE_TIMEOUT: return parse_duration(&config->conn.downstream->timeout.write, opt, optarg); case SHRPX_OPTID_BACKEND_CONNECT_TIMEOUT: return parse_duration(&config->conn.downstream->timeout.connect, opt, optarg); case SHRPX_OPTID_STREAM_READ_TIMEOUT: return parse_duration(&config->http2.timeout.stream_read, opt, optarg); case SHRPX_OPTID_STREAM_WRITE_TIMEOUT: return parse_duration(&config->http2.timeout.stream_write, opt, optarg); case SHRPX_OPTID_ACCESSLOG_FILE: config->logging.access.file = make_string_ref(config->balloc, optarg); return 0; case SHRPX_OPTID_ACCESSLOG_SYSLOG: config->logging.access.syslog = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_ACCESSLOG_FORMAT: config->logging.access.format = parse_log_format(config->balloc, optarg); return 0; case SHRPX_OPTID_ERRORLOG_FILE: config->logging.error.file = make_string_ref(config->balloc, optarg); return 0; case SHRPX_OPTID_ERRORLOG_SYSLOG: config->logging.error.syslog = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_FASTOPEN: return parse_uint(&config->conn.listener.fastopen, opt, optarg); case SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT: return parse_duration(&config->conn.downstream->timeout.idle_read, opt, optarg); case SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS: case SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS: { LOG(WARN) << opt << ": deprecated. Use " << (optid == SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS ? SHRPX_OPT_FRONTEND_HTTP2_WINDOW_SIZE : SHRPX_OPT_BACKEND_HTTP2_WINDOW_SIZE); int32_t *resp; if (optid == SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS) { resp = &config->http2.upstream.window_size; } else { resp = &config->http2.downstream.window_size; } errno = 0; int n; if (parse_uint(&n, opt, optarg) != 0) { return -1; } if (n >= 31) { LOG(ERROR) << opt << ": specify the integer in the range [0, 30], inclusive"; return -1; } // Make 16 bits to the HTTP/2 default 64KiB - 1. This is the same // behaviour of previous code. *resp = (1 << n) - 1; return 0; } case SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS: case SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_BITS: { LOG(WARN) << opt << ": deprecated. Use " << (optid == SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS ? SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE : SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE); int32_t *resp; if (optid == SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS) { resp = &config->http2.upstream.connection_window_size; } else { resp = &config->http2.downstream.connection_window_size; } errno = 0; int n; if (parse_uint(&n, opt, optarg) != 0) { return -1; } if (n < 16 || n >= 31) { LOG(ERROR) << opt << ": specify the integer in the range [16, 30], inclusive"; return -1; } *resp = (1 << n) - 1; return 0; } case SHRPX_OPTID_FRONTEND_NO_TLS: LOG(WARN) << opt << ": deprecated. Use no-tls keyword in " << SHRPX_OPT_FRONTEND; return 0; case SHRPX_OPTID_BACKEND_NO_TLS: LOG(WARN) << opt << ": deprecated. backend connection is not encrypted by " "default. See also " << SHRPX_OPT_BACKEND_TLS; return 0; case SHRPX_OPTID_BACKEND_TLS_SNI_FIELD: LOG(WARN) << opt << ": deprecated. Use sni keyword in --backend option. " "For now, all sni values of all backends are " "overridden by the given value " << optarg; config->tls.backend_sni_name = make_string_ref(config->balloc, optarg); return 0; case SHRPX_OPTID_PID_FILE: config->pid_file = make_string_ref(config->balloc, optarg); return 0; case SHRPX_OPTID_USER: { auto pwd = getpwnam(optarg.data()); if (!pwd) { LOG(ERROR) << opt << ": failed to get uid from " << optarg << ": " << xsi_strerror(errno, errbuf.data(), errbuf.size()); return -1; } config->user = make_string_ref(config->balloc, std::string_view{pwd->pw_name}); config->uid = pwd->pw_uid; config->gid = pwd->pw_gid; return 0; } case SHRPX_OPTID_PRIVATE_KEY_FILE: config->tls.private_key_file = make_string_ref(config->balloc, optarg); return 0; case SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE: { auto passwd = read_passwd_from_file(opt, optarg); if (passwd.empty()) { LOG(ERROR) << opt << ": Couldn't read key file's passwd from " << optarg; return -1; } config->tls.private_key_passwd = make_string_ref(config->balloc, passwd); return 0; } case SHRPX_OPTID_CERTIFICATE_FILE: config->tls.cert_file = make_string_ref(config->balloc, optarg); return 0; case SHRPX_OPTID_DH_PARAM_FILE: config->tls.dh_param_file = make_string_ref(config->balloc, optarg); return 0; case SHRPX_OPTID_SUBCERT: { auto end_keys = std::ranges::find(optarg, ';'); auto src_params = std::string_view{end_keys, std::ranges::end(optarg)}; SubcertParams params; if (parse_subcert_params(params, src_params) != 0) { return -1; } std::vector sct_data; if (!params.sct_dir.empty()) { // Make sure that dir_path is NULL terminated string. if (read_tls_sct_from_dir(sct_data, opt, std::string{params.sct_dir}) != 0) { return -1; } } // Private Key file and certificate file separated by ':'. auto sp = std::ranges::find(std::ranges::begin(optarg), end_keys, ':'); if (sp == end_keys) { LOG(ERROR) << opt << ": missing ':' in " << std::string_view{std::ranges::begin(optarg), end_keys}; return -1; } auto private_key_file = std::string_view{std::ranges::begin(optarg), sp}; if (private_key_file.empty()) { LOG(ERROR) << opt << ": missing private key file: " << std::string_view{std::ranges::begin(optarg), end_keys}; return -1; } auto cert_file = std::string_view{sp + 1, end_keys}; if (cert_file.empty()) { LOG(ERROR) << opt << ": missing certificate file: " << std::string_view{std::ranges::begin(optarg), end_keys}; return -1; } config->tls.subcerts.emplace_back( make_string_ref(config->balloc, private_key_file), make_string_ref(config->balloc, cert_file), std::move(sct_data)); return 0; } case SHRPX_OPTID_SYSLOG_FACILITY: { int facility = int_syslog_facility(optarg); if (facility == -1) { LOG(ERROR) << opt << ": Unknown syslog facility: " << optarg; return -1; } config->logging.syslog_facility = facility; return 0; } case SHRPX_OPTID_BACKLOG: return parse_uint(&config->conn.listener.backlog, opt, optarg); case SHRPX_OPTID_CIPHERS: config->tls.ciphers = make_string_ref(config->balloc, optarg); return 0; case SHRPX_OPTID_TLS13_CIPHERS: config->tls.tls13_ciphers = make_string_ref(config->balloc, optarg); return 0; case SHRPX_OPTID_CLIENT: LOG(ERROR) << opt << ": deprecated. Use frontend=,;no-tls, " "backend=,;;proto=h2;tls"; return -1; case SHRPX_OPTID_INSECURE: config->tls.insecure = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_CACERT: config->tls.cacert = make_string_ref(config->balloc, optarg); return 0; case SHRPX_OPTID_BACKEND_IPV4: LOG(WARN) << opt << ": deprecated. Use backend-address-family=IPv4 instead."; config->conn.downstream->family = AF_INET; return 0; case SHRPX_OPTID_BACKEND_IPV6: LOG(WARN) << opt << ": deprecated. Use backend-address-family=IPv6 instead."; config->conn.downstream->family = AF_INET6; return 0; case SHRPX_OPTID_BACKEND_HTTP_PROXY_URI: { auto &proxy = config->downstream_http_proxy; // Reset here so that multiple option occurrence does not merge // the results. proxy = {}; // parse URI and get hostname, port and optionally userinfo. urlparse_url u; int rv = urlparse_parse_url(optarg.data(), optarg.size(), 0, &u); if (rv == 0) { if (u.field_set & URLPARSE_USERINFO) { auto uf = util::get_uri_field(optarg.data(), u, URLPARSE_USERINFO); // Surprisingly, u.field_set & URLPARSE_USERINFO is nonzero even if // userinfo component is empty string. if (!uf.empty()) { proxy.userinfo = util::percent_decode(config->balloc, uf); } } if (u.field_set & URLPARSE_HOST) { proxy.host = make_string_ref( config->balloc, util::get_uri_field(optarg.data(), u, URLPARSE_HOST)); } else { LOG(ERROR) << opt << ": no hostname specified"; return -1; } if (u.field_set & URLPARSE_PORT) { proxy.port = u.port; } else { LOG(ERROR) << opt << ": no port specified"; return -1; } } else { LOG(ERROR) << opt << ": parse error"; return -1; } return 0; } case SHRPX_OPTID_READ_RATE: return parse_uint_with_unit(&config->conn.upstream.ratelimit.read.rate, opt, optarg); case SHRPX_OPTID_READ_BURST: return parse_uint_with_unit(&config->conn.upstream.ratelimit.read.burst, opt, optarg); case SHRPX_OPTID_WRITE_RATE: return parse_uint_with_unit(&config->conn.upstream.ratelimit.write.rate, opt, optarg); case SHRPX_OPTID_WRITE_BURST: return parse_uint_with_unit(&config->conn.upstream.ratelimit.write.burst, opt, optarg); case SHRPX_OPTID_WORKER_READ_RATE: LOG(WARN) << opt << ": not implemented yet"; return 0; case SHRPX_OPTID_WORKER_READ_BURST: LOG(WARN) << opt << ": not implemented yet"; return 0; case SHRPX_OPTID_WORKER_WRITE_RATE: LOG(WARN) << opt << ": not implemented yet"; return 0; case SHRPX_OPTID_WORKER_WRITE_BURST: LOG(WARN) << opt << ": not implemented yet"; return 0; case SHRPX_OPTID_TLS_PROTO_LIST: { LOG(WARN) << opt << ": deprecated. Use tls-min-proto-version and " "tls-max-proto-version instead."; auto list = util::split_str(optarg, ','); config->tls.tls_proto_list.resize(list.size()); for (size_t i = 0; i < list.size(); ++i) { config->tls.tls_proto_list[i] = make_string_ref(config->balloc, list[i]); } return 0; } case SHRPX_OPTID_VERIFY_CLIENT: config->tls.client_verify.enabled = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_VERIFY_CLIENT_CACERT: config->tls.client_verify.cacert = make_string_ref(config->balloc, optarg); return 0; case SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE: config->tls.client.private_key_file = make_string_ref(config->balloc, optarg); return 0; case SHRPX_OPTID_CLIENT_CERT_FILE: config->tls.client.cert_file = make_string_ref(config->balloc, optarg); return 0; case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER: config->http2.upstream.debug.dump.request_header_file = make_string_ref(config->balloc, optarg); return 0; case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER: config->http2.upstream.debug.dump.response_header_file = make_string_ref(config->balloc, optarg); return 0; case SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING: config->http2.no_cookie_crumbling = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_FRONTEND_FRAME_DEBUG: config->http2.upstream.debug.frame_debug = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_PADDING: return parse_uint(&config->padding, opt, optarg); case SHRPX_OPTID_ALTSVC: { AltSvc altsvc{}; if (parse_altsvc(altsvc, opt, optarg) != 0) { return -1; } config->http.altsvcs.push_back(std::move(altsvc)); return 0; } case SHRPX_OPTID_ADD_REQUEST_HEADER: case SHRPX_OPTID_ADD_RESPONSE_HEADER: { auto p = parse_header(config->balloc, optarg); if (p.name.empty()) { LOG(ERROR) << opt << ": invalid header field: " << optarg; return -1; } if (optid == SHRPX_OPTID_ADD_REQUEST_HEADER) { config->http.add_request_headers.push_back(std::move(p)); } else { config->http.add_response_headers.push_back(std::move(p)); } return 0; } case SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS: return parse_uint(&config->conn.upstream.worker_connections, opt, optarg); case SHRPX_OPTID_NO_LOCATION_REWRITE: config->http.no_location_rewrite = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_NO_HOST_REWRITE: LOG(WARN) << SHRPX_OPT_NO_HOST_REWRITE << ": deprecated. :authority and host header fields are NOT " "altered by default. To rewrite these headers, use " "--host-rewrite option."; return 0; case SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_HOST: LOG(WARN) << opt << ": deprecated. Use backend-connections-per-host instead."; // fall through case SHRPX_OPTID_BACKEND_CONNECTIONS_PER_HOST: { int n; if (parse_uint(&n, opt, optarg) != 0) { return -1; } if (n == 0) { LOG(ERROR) << opt << ": specify an integer strictly more than 0"; return -1; } config->conn.downstream->connections_per_host = static_cast(n); return 0; } case SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND: LOG(WARN) << opt << ": deprecated. Use " << SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND << " instead."; // fall through case SHRPX_OPTID_BACKEND_CONNECTIONS_PER_FRONTEND: return parse_uint(&config->conn.downstream->connections_per_frontend, opt, optarg); case SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT: return parse_duration(&config->conn.listener.timeout.sleep, opt, optarg); case SHRPX_OPTID_TLS_TICKET_KEY_FILE: config->tls.ticket.files.emplace_back( make_string_ref(config->balloc, optarg)); return 0; case SHRPX_OPTID_RLIMIT_NOFILE: { int n; if (parse_uint(&n, opt, optarg) != 0) { return -1; } if (n < 0) { LOG(ERROR) << opt << ": specify the integer more than or equal to 0"; return -1; } config->rlimit_nofile = static_cast(n); return 0; } case SHRPX_OPTID_BACKEND_REQUEST_BUFFER: case SHRPX_OPTID_BACKEND_RESPONSE_BUFFER: { size_t n; if (parse_uint_with_unit(&n, opt, optarg) != 0) { return -1; } if (n == 0) { LOG(ERROR) << opt << ": specify an integer strictly more than 0"; return -1; } if (optid == SHRPX_OPTID_BACKEND_REQUEST_BUFFER) { config->conn.downstream->request_buffer_size = n; } else { config->conn.downstream->response_buffer_size = n; } return 0; } case SHRPX_OPTID_NO_SERVER_PUSH: config->http2.no_server_push = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER: LOG(WARN) << opt << ": deprecated."; return 0; case SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE: LOG(WARN) << opt << ": deprecated. It has no effect"; return 0; case SHRPX_OPTID_OCSP_UPDATE_INTERVAL: LOG(WARN) << opt << ": deprecated. It has no effect"; return 0; case SHRPX_OPTID_NO_OCSP: LOG(WARN) << opt << ": deprecated. It has no effect"; return 0; case SHRPX_OPTID_HEADER_FIELD_BUFFER: LOG(WARN) << opt << ": deprecated. Use request-header-field-buffer instead."; // fall through case SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER: return parse_uint_with_unit(&config->http.request_header_field_buffer, opt, optarg); case SHRPX_OPTID_MAX_HEADER_FIELDS: LOG(WARN) << opt << ": deprecated. Use max-request-header-fields instead."; // fall through case SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS: return parse_uint(&config->http.max_request_header_fields, opt, optarg); case SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER: return parse_uint_with_unit(&config->http.response_header_field_buffer, opt, optarg); case SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS: return parse_uint(&config->http.max_response_header_fields, opt, optarg); case SHRPX_OPTID_INCLUDE: { if (included_set.contains(optarg)) { LOG(ERROR) << opt << ": " << optarg << " has already been included"; return -1; } included_set.insert(optarg); auto rv = load_config(config, optarg.data(), included_set, pattern_addr_indexer); included_set.erase(optarg); if (rv != 0) { return -1; } return 0; } case SHRPX_OPTID_TLS_TICKET_KEY_CIPHER: if (util::strieq("aes-128-cbc"sv, optarg)) { config->tls.ticket.cipher = EVP_aes_128_cbc(); } else if (util::strieq("aes-256-cbc"sv, optarg)) { config->tls.ticket.cipher = EVP_aes_256_cbc(); } else { LOG(ERROR) << opt << ": unsupported cipher for ticket encryption: " << optarg; return -1; } config->tls.ticket.cipher_given = true; return 0; case SHRPX_OPTID_HOST_REWRITE: config->http.no_host_rewrite = !util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED: LOG(WARN) << opt << ": deprecated. It has no effect"; return 0; case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED: { auto addr_end = std::ranges::find(optarg, ';'); auto src_params = std::string_view{addr_end, std::ranges::end(optarg)}; MemcachedConnectionParams params{}; if (parse_memcached_connection_params(params, src_params, opt) != 0) { return -1; } auto hp = split_host_port( config->balloc, std::string_view{std::ranges::begin(optarg), addr_end}, opt); if (!hp) { return -1; } auto &memcachedconf = config->tls.ticket.memcached; memcachedconf.host = std::move(hp->host); memcachedconf.port = hp->port; memcachedconf.tls = params.tls; return 0; } case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL: return parse_duration(&config->tls.ticket.memcached.interval, opt, optarg); case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY: { int n; if (parse_uint(&n, opt, optarg) != 0) { return -1; } if (n > 30) { LOG(ERROR) << opt << ": must be smaller than or equal to 30"; return -1; } config->tls.ticket.memcached.max_retry = static_cast(n); return 0; } case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL: return parse_uint(&config->tls.ticket.memcached.max_fail, opt, optarg); case SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD: { size_t n; if (parse_uint_with_unit(&n, opt, optarg) != 0) { return -1; } config->tls.dyn_rec.warmup_threshold = n; return 0; } case SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT: return parse_duration(&config->tls.dyn_rec.idle_timeout, opt, optarg); case SHRPX_OPTID_MRUBY_FILE: #ifdef HAVE_MRUBY config->mruby_file = make_string_ref(config->balloc, optarg); #else // !defined(HAVE_MRUBY) LOG(WARN) << opt << ": ignored because mruby support is disabled at build time."; #endif // !defined(HAVE_MRUBY) return 0; case SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL: LOG(WARN) << opt << ": deprecated. Use proxyproto keyword in " << SHRPX_OPT_FRONTEND << " instead."; config->conn.upstream.accept_proxy_protocol = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_ADD_FORWARDED: { auto &fwdconf = config->http.forwarded; fwdconf.params = FORWARDED_NONE; for (const auto ¶m : util::split_str(optarg, ',')) { if (util::strieq("by"sv, param)) { fwdconf.params |= FORWARDED_BY; continue; } if (util::strieq("for"sv, param)) { fwdconf.params |= FORWARDED_FOR; continue; } if (util::strieq("host"sv, param)) { fwdconf.params |= FORWARDED_HOST; continue; } if (util::strieq("proto"sv, param)) { fwdconf.params |= FORWARDED_PROTO; continue; } LOG(ERROR) << opt << ": unknown parameter " << optarg; return -1; } return 0; } case SHRPX_OPTID_STRIP_INCOMING_FORWARDED: config->http.forwarded.strip_incoming = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_FORWARDED_BY: case SHRPX_OPTID_FORWARDED_FOR: { auto type = parse_forwarded_node_type(optarg); if (type == static_cast(-1) || (optid == SHRPX_OPTID_FORWARDED_FOR && optarg[0] == '_')) { LOG(ERROR) << opt << ": unknown node type or illegal obfuscated string " << optarg; return -1; } auto &fwdconf = config->http.forwarded; switch (optid) { case SHRPX_OPTID_FORWARDED_BY: fwdconf.by_node_type = type; if (optarg[0] == '_') { fwdconf.by_obfuscated = make_string_ref(config->balloc, optarg); } else { fwdconf.by_obfuscated = ""sv; } break; case SHRPX_OPTID_FORWARDED_FOR: fwdconf.for_node_type = type; break; } return 0; } case SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST: LOG(WARN) << opt << ": deprecated. Use " << SHRPX_OPT_NO_HTTP2_CIPHER_BLOCK_LIST << " instead."; // fall through case SHRPX_OPTID_NO_HTTP2_CIPHER_BLOCK_LIST: config->tls.no_http2_cipher_block_list = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_BACKEND_HTTP1_TLS: case SHRPX_OPTID_BACKEND_TLS: LOG(WARN) << opt << ": deprecated. Use tls keyword in " << SHRPX_OPT_BACKEND << " instead."; return 0; case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_TLS: LOG(WARN) << opt << ": deprecated. It has no effect"; return 0; case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE: LOG(WARN) << opt << ": deprecated. It has no effect"; return 0; case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE: LOG(WARN) << opt << ": deprecated. It has no effect"; return 0; case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS: LOG(WARN) << opt << ": deprecated. Use tls keyword in " << SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED; return 0; case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE: config->tls.ticket.memcached.cert_file = make_string_ref(config->balloc, optarg); return 0; case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE: config->tls.ticket.memcached.private_key_file = make_string_ref(config->balloc, optarg); return 0; case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY: return parse_address_family(&config->tls.ticket.memcached.family, opt, optarg); case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY: LOG(WARN) << opt << ": deprecated. It has no effect"; return 0; case SHRPX_OPTID_BACKEND_ADDRESS_FAMILY: return parse_address_family(&config->conn.downstream->family, opt, optarg); case SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS: return parse_uint(&config->http2.upstream.max_concurrent_streams, opt, optarg); case SHRPX_OPTID_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS: return parse_uint(&config->http2.downstream.max_concurrent_streams, opt, optarg); case SHRPX_OPTID_ERROR_PAGE: return parse_error_page(config->http.error_pages, opt, optarg); case SHRPX_OPTID_NO_KQUEUE: if ((ev_supported_backends() & EVBACKEND_KQUEUE) == 0) { LOG(WARN) << opt << ": kqueue is not supported on this platform"; return 0; } config->ev_loop_flags = ev_recommended_backends() & static_cast(~EVBACKEND_KQUEUE); return 0; case SHRPX_OPTID_FRONTEND_HTTP2_SETTINGS_TIMEOUT: return parse_duration(&config->http2.upstream.timeout.settings, opt, optarg); case SHRPX_OPTID_BACKEND_HTTP2_SETTINGS_TIMEOUT: return parse_duration(&config->http2.downstream.timeout.settings, opt, optarg); case SHRPX_OPTID_API_MAX_REQUEST_BODY: return parse_uint_with_unit(&config->api.max_request_body, opt, optarg); case SHRPX_OPTID_BACKEND_MAX_BACKOFF: return parse_duration(&config->conn.downstream->timeout.max_backoff, opt, optarg); case SHRPX_OPTID_SERVER_NAME: config->http.server_name = make_string_ref(config->balloc, optarg); return 0; case SHRPX_OPTID_NO_SERVER_REWRITE: config->http.no_server_rewrite = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE: config->http2.upstream.optimize_write_buffer_size = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE: config->http2.upstream.optimize_window_size = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE: if (parse_uint_with_unit(&config->http2.upstream.window_size, opt, optarg) != 0) { return -1; } return 0; case SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE: if (parse_uint_with_unit(&config->http2.upstream.connection_window_size, opt, optarg) != 0) { return -1; } return 0; case SHRPX_OPTID_BACKEND_HTTP2_WINDOW_SIZE: if (parse_uint_with_unit(&config->http2.downstream.window_size, opt, optarg) != 0) { return -1; } return 0; case SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE: if (parse_uint_with_unit(&config->http2.downstream.connection_window_size, opt, optarg) != 0) { return -1; } return 0; case SHRPX_OPTID_FRONTEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE: if (parse_uint_with_unit(&config->http2.upstream.encoder_dynamic_table_size, opt, optarg) != 0) { return -1; } nghttp2_option_set_max_deflate_dynamic_table_size( config->http2.upstream.option, config->http2.upstream.encoder_dynamic_table_size); nghttp2_option_set_max_deflate_dynamic_table_size( config->http2.upstream.alt_mode_option, config->http2.upstream.encoder_dynamic_table_size); return 0; case SHRPX_OPTID_FRONTEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE: return parse_uint_with_unit( &config->http2.upstream.decoder_dynamic_table_size, opt, optarg); case SHRPX_OPTID_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE: if (parse_uint_with_unit( &config->http2.downstream.encoder_dynamic_table_size, opt, optarg) != 0) { return -1; } nghttp2_option_set_max_deflate_dynamic_table_size( config->http2.downstream.option, config->http2.downstream.encoder_dynamic_table_size); return 0; case SHRPX_OPTID_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE: return parse_uint_with_unit( &config->http2.downstream.decoder_dynamic_table_size, opt, optarg); case SHRPX_OPTID_ECDH_CURVES: LOG(WARN) << opt << ": deprecated. Use " << SHRPX_OPT_GROUPS << " instead."; // fall through case SHRPX_OPTID_GROUPS: config->tls.groups = make_string_ref(config->balloc, optarg); return 0; case SHRPX_OPTID_TLS_SCT_DIR: #if defined(NGHTTP2_GENUINE_OPENSSL) || defined(NGHTTP2_OPENSSL_IS_BORINGSSL) return read_tls_sct_from_dir(config->tls.sct_data, opt, optarg); #else // !defined(NGHTTP2_GENUINE_OPENSSL) && // !defined(NGHTTP2_OPENSSL_IS_BORINGSSL) LOG(WARN) << opt << ": ignored because underlying TLS library does not support SCT"; return 0; #endif // !defined(NGHTTP2_GENUINE_OPENSSL) && // !defined(NGHTTP2_OPENSSL_IS_BORINGSSL) case SHRPX_OPTID_DNS_CACHE_TIMEOUT: return parse_duration(&config->dns.timeout.cache, opt, optarg); case SHRPX_OPTID_DNS_LOOKUP_TIMEOUT: return parse_duration(&config->dns.timeout.lookup, opt, optarg); case SHRPX_OPTID_DNS_MAX_TRY: { int n; if (parse_uint(&n, opt, optarg) != 0) { return -1; } if (n > 5) { LOG(ERROR) << opt << ": must be smaller than or equal to 5"; return -1; } config->dns.max_try = static_cast(n); return 0; } case SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT: return parse_duration(&config->conn.upstream.timeout.idle, opt, optarg); case SHRPX_OPTID_PSK_SECRETS: #ifndef OPENSSL_NO_PSK return parse_psk_secrets(config, optarg); #else // defined(OPENSSL_NO_PSK) LOG(WARN) << opt << ": ignored because underlying TLS library does not support PSK"; return 0; #endif // defined(OPENSSL_NO_PSK) case SHRPX_OPTID_CLIENT_PSK_SECRETS: #ifndef OPENSSL_NO_PSK return parse_client_psk_secrets(config, optarg); #else // defined(OPENSSL_NO_PSK) LOG(WARN) << opt << ": ignored because underlying TLS library does not support PSK"; return 0; #endif // defined(OPENSSL_NO_PSK) case SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST: LOG(WARN) << opt << ": deprecated. Use " << SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLOCK_LIST << " instead."; // fall through case SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLOCK_LIST: config->tls.client.no_http2_cipher_block_list = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_CLIENT_CIPHERS: config->tls.client.ciphers = make_string_ref(config->balloc, optarg); return 0; case SHRPX_OPTID_TLS13_CLIENT_CIPHERS: config->tls.client.tls13_ciphers = make_string_ref(config->balloc, optarg); return 0; case SHRPX_OPTID_ACCESSLOG_WRITE_EARLY: config->logging.access.write_early = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_TLS_MIN_PROTO_VERSION: return parse_tls_proto_version(config->tls.min_proto_version, opt, optarg); case SHRPX_OPTID_TLS_MAX_PROTO_VERSION: return parse_tls_proto_version(config->tls.max_proto_version, opt, optarg); case SHRPX_OPTID_REDIRECT_HTTPS_PORT: { auto n = util::parse_uint(optarg); if (!n || n < 0 || n > 65535) { LOG(ERROR) << opt << ": bad value. Specify an integer in the range [0, " "65535], inclusive"; return -1; } config->http.redirect_https_port = make_string_ref(config->balloc, optarg); return 0; } case SHRPX_OPTID_FRONTEND_MAX_REQUESTS: return parse_uint(&config->http.max_requests, opt, optarg); case SHRPX_OPTID_SINGLE_THREAD: config->single_thread = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_SINGLE_PROCESS: config->single_process = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO: config->http.xfp.add = !util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO: config->http.xfp.strip_incoming = !util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_OCSP_STARTUP: LOG(WARN) << opt << ": deprecated. It has no effect"; return 0; case SHRPX_OPTID_NO_VERIFY_OCSP: LOG(WARN) << opt << ": deprecated. It has no effect"; return 0; case SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED: config->tls.client_verify.tolerate_expired = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_IGNORE_PER_PATTERN_MRUBY_ERROR: config->ignore_per_pattern_mruby_error = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_TLS_NO_POSTPONE_EARLY_DATA: config->tls.no_postpone_early_data = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_TLS_MAX_EARLY_DATA: { return parse_uint_with_unit(&config->tls.max_early_data, opt, optarg); } case SHRPX_OPTID_NO_STRIP_INCOMING_EARLY_DATA: config->http.early_data.strip_incoming = !util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_QUIC_BPF_PROGRAM_FILE: #ifdef ENABLE_HTTP3 config->quic.bpf.prog_file = make_string_ref(config->balloc, optarg); #endif // defined(ENABLE_HTTP3) return 0; case SHRPX_OPTID_NO_QUIC_BPF: #ifdef ENABLE_HTTP3 config->quic.bpf.disabled = util::strieq("yes"sv, optarg); #endif // defined(ENABLE_HTTP3) return 0; case SHRPX_OPTID_HTTP2_ALTSVC: { AltSvc altsvc{}; if (parse_altsvc(altsvc, opt, optarg) != 0) { return -1; } config->http.http2_altsvcs.push_back(std::move(altsvc)); return 0; } case SHRPX_OPTID_FRONTEND_HTTP3_READ_TIMEOUT: LOG(WARN) << opt << ": deprecated. Use frontend-http3-idle-timeout"; // fall through case SHRPX_OPTID_FRONTEND_HTTP3_IDLE_TIMEOUT: #ifdef ENABLE_HTTP3 return parse_duration(&config->conn.upstream.timeout.http3_idle, opt, optarg); #else // !defined(ENABLE_HTTP3) return 0; #endif // !defined(ENABLE_HTTP3) case SHRPX_OPTID_FRONTEND_QUIC_IDLE_TIMEOUT: #ifdef ENABLE_HTTP3 return parse_duration(&config->quic.upstream.timeout.idle, opt, optarg); #else // !defined(ENABLE_HTTP3) return 0; #endif // !defined(ENABLE_HTTP3) case SHRPX_OPTID_FRONTEND_QUIC_DEBUG_LOG: #ifdef ENABLE_HTTP3 config->quic.upstream.debug.log = util::strieq("yes"sv, optarg); #endif // defined(ENABLE_HTTP3) return 0; case SHRPX_OPTID_FRONTEND_HTTP3_WINDOW_SIZE: #ifdef ENABLE_HTTP3 if (parse_uint_with_unit(&config->http3.upstream.window_size, opt, optarg) != 0) { return -1; } #endif // defined(ENABLE_HTTP3) return 0; case SHRPX_OPTID_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE: #ifdef ENABLE_HTTP3 if (parse_uint_with_unit(&config->http3.upstream.connection_window_size, opt, optarg) != 0) { return -1; } #endif // defined(ENABLE_HTTP3) return 0; case SHRPX_OPTID_FRONTEND_HTTP3_MAX_WINDOW_SIZE: #ifdef ENABLE_HTTP3 if (parse_uint_with_unit(&config->http3.upstream.max_window_size, opt, optarg) != 0) { return -1; } #endif // defined(ENABLE_HTTP3) return 0; case SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE: #ifdef ENABLE_HTTP3 if (parse_uint_with_unit(&config->http3.upstream.max_connection_window_size, opt, optarg) != 0) { return -1; } #endif // defined(ENABLE_HTTP3) return 0; case SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS: #ifdef ENABLE_HTTP3 return parse_uint(&config->http3.upstream.max_concurrent_streams, opt, optarg); #else // !defined(ENABLE_HTTP3) return 0; #endif // !defined(ENABLE_HTTP3) case SHRPX_OPTID_FRONTEND_QUIC_EARLY_DATA: #ifdef ENABLE_HTTP3 config->quic.upstream.early_data = util::strieq("yes"sv, optarg); #endif // defined(ENABLE_HTTP3) return 0; case SHRPX_OPTID_FRONTEND_QUIC_QLOG_DIR: #ifdef ENABLE_HTTP3 config->quic.upstream.qlog.dir = make_string_ref(config->balloc, optarg); #endif // defined(ENABLE_HTTP3) return 0; case SHRPX_OPTID_FRONTEND_QUIC_REQUIRE_TOKEN: #ifdef ENABLE_HTTP3 config->quic.upstream.require_token = util::strieq("yes"sv, optarg); #endif // defined(ENABLE_HTTP3) return 0; case SHRPX_OPTID_FRONTEND_QUIC_CONGESTION_CONTROLLER: #ifdef ENABLE_HTTP3 if (util::strieq("cubic"sv, optarg)) { config->quic.upstream.congestion_controller = NGTCP2_CC_ALGO_CUBIC; } else if (util::strieq("bbr"sv, optarg)) { config->quic.upstream.congestion_controller = NGTCP2_CC_ALGO_BBR; } else { LOG(ERROR) << opt << ": must be either cubic or bbr"; return -1; } #endif // defined(ENABLE_HTTP3) return 0; case SHRPX_OPTID_QUIC_SERVER_ID: #ifdef ENABLE_HTTP3 if (optarg.size() != sizeof(config->quic.server_id) * 2 || !util::is_hex_string(optarg)) { LOG(ERROR) << opt << ": must be a hex-string"; return -1; } util::decode_hex(optarg, reinterpret_cast(&config->quic.server_id)); #endif // defined(ENABLE_HTTP3) return 0; case SHRPX_OPTID_FRONTEND_QUIC_SECRET_FILE: #ifdef ENABLE_HTTP3 config->quic.upstream.secret_file = make_string_ref(config->balloc, optarg); #endif // defined(ENABLE_HTTP3) return 0; case SHRPX_OPTID_RLIMIT_MEMLOCK: { int n; if (parse_uint(&n, opt, optarg) != 0) { return -1; } if (n < 0) { LOG(ERROR) << opt << ": specify the integer more than or equal to 0"; return -1; } config->rlimit_memlock = static_cast(n); return 0; } case SHRPX_OPTID_MAX_WORKER_PROCESSES: return parse_uint(&config->max_worker_processes, opt, optarg); case SHRPX_OPTID_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD: return parse_duration(&config->worker_process_grace_shutdown_period, opt, optarg); case SHRPX_OPTID_FRONTEND_QUIC_INITIAL_RTT: { #ifdef ENABLE_HTTP3 return parse_duration(&config->quic.upstream.initial_rtt, opt, optarg); #endif // defined(ENABLE_HTTP3) return 0; } case SHRPX_OPTID_REQUIRE_HTTP_SCHEME: config->http.require_http_scheme = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_TLS_KTLS: config->tls.ktls = util::strieq("yes"sv, optarg); return 0; case SHRPX_OPTID_NPN_LIST: LOG(WARN) << opt << ": deprecated. Use alpn-list instead."; // fall through case SHRPX_OPTID_ALPN_LIST: { auto list = util::split_str(optarg, ','); config->tls.alpn_list.resize(list.size()); for (size_t i = 0; i < list.size(); ++i) { config->tls.alpn_list[i] = make_string_ref(config->balloc, list[i]); } return 0; } case SHRPX_OPTID_CONF: LOG(WARN) << "conf: ignored"; return 0; } LOG(ERROR) << "Unknown option: " << opt; return -1; } int load_config( Config *config, const char *filename, std::unordered_set &include_set, std::unordered_map &pattern_addr_indexer) { std::ifstream in(filename, std::ios::binary); if (!in) { LOG(ERROR) << "Could not open config file " << filename; return -1; } std::string line; int linenum = 0; while (std::getline(in, line)) { ++linenum; if (line.empty() || line[0] == '#') { continue; } auto eq = std::ranges::find(line, '='); if (eq == std::ranges::end(line)) { LOG(ERROR) << "Bad configuration format in " << filename << " at line " << linenum; return -1; } *eq = '\0'; if (parse_config(config, std::string_view{std::ranges::begin(line), eq}, std::string_view{eq + 1, std::ranges::end(line)}, include_set, pattern_addr_indexer) != 0) { return -1; } } if (in.bad() || (!in.eof() && in.fail())) { LOG(ERROR) << "Could not read the configuration file " << filename; return -1; } return 0; } std::string_view str_syslog_facility(int facility) { switch (facility) { case (LOG_AUTH): return "auth"sv; #ifdef LOG_AUTHPRIV case (LOG_AUTHPRIV): return "authpriv"sv; #endif // defined(LOG_AUTHPRIV) case (LOG_CRON): return "cron"sv; case (LOG_DAEMON): return "daemon"sv; #ifdef LOG_FTP case (LOG_FTP): return "ftp"sv; #endif // defined(LOG_FTP) case (LOG_KERN): return "kern"sv; case (LOG_LOCAL0): return "local0"sv; case (LOG_LOCAL1): return "local1"sv; case (LOG_LOCAL2): return "local2"sv; case (LOG_LOCAL3): return "local3"sv; case (LOG_LOCAL4): return "local4"sv; case (LOG_LOCAL5): return "local5"sv; case (LOG_LOCAL6): return "local6"sv; case (LOG_LOCAL7): return "local7"sv; case (LOG_LPR): return "lpr"sv; case (LOG_MAIL): return "mail"sv; case (LOG_SYSLOG): return "syslog"sv; case (LOG_USER): return "user"sv; case (LOG_UUCP): return "uucp"sv; default: return "(unknown)"sv; } } int int_syslog_facility(const std::string_view &strfacility) { if (util::strieq("auth"sv, strfacility)) { return LOG_AUTH; } #ifdef LOG_AUTHPRIV if (util::strieq("authpriv"sv, strfacility)) { return LOG_AUTHPRIV; } #endif // defined(LOG_AUTHPRIV) if (util::strieq("cron"sv, strfacility)) { return LOG_CRON; } if (util::strieq("daemon"sv, strfacility)) { return LOG_DAEMON; } #ifdef LOG_FTP if (util::strieq("ftp"sv, strfacility)) { return LOG_FTP; } #endif // defined(LOG_FTP) if (util::strieq("kern"sv, strfacility)) { return LOG_KERN; } if (util::strieq("local0"sv, strfacility)) { return LOG_LOCAL0; } if (util::strieq("local1"sv, strfacility)) { return LOG_LOCAL1; } if (util::strieq("local2"sv, strfacility)) { return LOG_LOCAL2; } if (util::strieq("local3"sv, strfacility)) { return LOG_LOCAL3; } if (util::strieq("local4"sv, strfacility)) { return LOG_LOCAL4; } if (util::strieq("local5"sv, strfacility)) { return LOG_LOCAL5; } if (util::strieq("local6"sv, strfacility)) { return LOG_LOCAL6; } if (util::strieq("local7"sv, strfacility)) { return LOG_LOCAL7; } if (util::strieq("lpr"sv, strfacility)) { return LOG_LPR; } if (util::strieq("mail"sv, strfacility)) { return LOG_MAIL; } if (util::strieq("news"sv, strfacility)) { return LOG_NEWS; } if (util::strieq("syslog"sv, strfacility)) { return LOG_SYSLOG; } if (util::strieq("user"sv, strfacility)) { return LOG_USER; } if (util::strieq("uucp"sv, strfacility)) { return LOG_UUCP; } return -1; } std::string_view strproto(Proto proto) { switch (proto) { case Proto::NONE: return "none"sv; case Proto::HTTP1: return "http/1.1"sv; case Proto::HTTP2: return "h2"sv; case Proto::HTTP3: return "h3"sv; case Proto::MEMCACHED: return "memcached"sv; } // gcc needs this. assert(0); abort(); } namespace { // Consistent hashing method described in // https://github.com/RJ/ketama. Generate 160 32-bit hashes per |s|, // which is usually backend address. The each hash is associated to // index of backend address. When all hashes for every backend // address are calculated, sort it in ascending order of hash. To // choose the index, compute 32-bit hash based on client IP address, // and do lower bound search in the array. The returned index is the // backend to use. int compute_affinity_hash(std::vector &res, size_t idx, const std::string_view &s) { int rv; std::array buf; for (auto i = 0; i < 20; ++i) { auto t = std::string{s}; t += static_cast(i); rv = util::sha256(buf.data(), t); if (rv != 0) { return -1; } for (size_t i = 0; i < 8; ++i) { auto h = (static_cast(buf[4 * i]) << 24) | (static_cast(buf[4 * i + 1]) << 16) | (static_cast(buf[4 * i + 2]) << 8) | static_cast(buf[4 * i + 3]); res.emplace_back(idx, h); } } return 0; } } // namespace // Configures the following member in |config|: // conn.downstream_router, conn.downstream.addr_groups, // conn.downstream.addr_group_catch_all. int configure_downstream_group(Config *config, bool http2_proxy, bool numeric_addr_only, const TLSConfig &tlsconf) { int rv; auto &downstreamconf = *config->conn.downstream; auto &addr_groups = downstreamconf.addr_groups; auto &routerconf = downstreamconf.router; auto &router = routerconf.router; auto &rw_router = routerconf.rev_wildcard_router; auto &wildcard_patterns = routerconf.wildcard_patterns; if (addr_groups.empty()) { DownstreamAddrConfig addr{ .host = DEFAULT_DOWNSTREAM_HOST, .weight = 1, .group_weight = 1, .proto = Proto::HTTP1, .port = DEFAULT_DOWNSTREAM_PORT, }; DownstreamAddrGroupConfig g("/"sv); g.addrs.push_back(std::move(addr)); router.add_route(g.pattern, addr_groups.size()); addr_groups.push_back(std::move(g)); } // backward compatibility: override all SNI fields with the option // value --backend-tls-sni-field if (!tlsconf.backend_sni_name.empty()) { auto &sni = tlsconf.backend_sni_name; for (auto &addr_group : addr_groups) { for (auto &addr : addr_group.addrs) { addr.sni = sni; } } } if (LOG_ENABLED(INFO)) { LOG(INFO) << "Resolving backend address"; } // Sort by pattern so that later we can compare the old and new // backends efficiently in Worker::replace_downstream_config. std::ranges::sort(addr_groups, [](const auto &lhs, const auto &rhs) { return lhs.pattern < rhs.pattern; }); for (size_t idx = 0; idx < addr_groups.size(); ++idx) { auto &g = addr_groups[idx]; // Sort by group so that later we can see the group in the // particular order in Worker::replace_downstream_config. std::ranges::sort(g.addrs, [](const auto &lhs, const auto &rhs) { return lhs.group < rhs.group; }); if (g.pattern[0] == '*') { // wildcard pattern auto path_first = std::ranges::find(g.pattern, '/'); auto host = std::string_view{std::ranges::begin(g.pattern) + 1, path_first}; auto path = std::string_view{path_first, std::ranges::end(g.pattern)}; auto path_is_wildcard = false; if (path[path.size() - 1] == '*') { path = path.substr(0, path.size() - 1); path_is_wildcard = true; } auto it = std::ranges::find_if( wildcard_patterns, [&host](const WildcardPattern &wp) { return wp.host == host; }); if (it == std::ranges::end(wildcard_patterns)) { wildcard_patterns.emplace_back(host); auto &router = wildcard_patterns.back().router; router.add_route(path, idx, path_is_wildcard); auto iov = make_byte_ref(downstreamconf.balloc, host.size() + 1); auto p = std::ranges::reverse_copy(host, std::ranges::begin(iov)).out; *p = '\0'; auto rev_host = as_string_view(std::ranges::begin(iov), p); rw_router.add_route(rev_host, wildcard_patterns.size() - 1); } else { (*it).router.add_route(path, idx, path_is_wildcard); } continue; } auto path_is_wildcard = false; auto pattern = g.pattern; if (pattern[pattern.size() - 1] == '*') { pattern = pattern.substr(0, pattern.size() - 1); path_is_wildcard = true; } router.add_route(pattern, idx, path_is_wildcard); } ssize_t catch_all_group = -1; for (size_t i = 0; i < addr_groups.size(); ++i) { auto &g = addr_groups[i]; if (g.pattern == "/"sv) { catch_all_group = as_signed(i); } if (LOG_ENABLED(INFO)) { LOG(INFO) << "Host-path pattern: group " << i << ": '" << g.pattern << "'"; for (auto &addr : g.addrs) { LOG(INFO) << "group " << i << " -> " << addr.host.data() << (addr.host_unix ? "" : ":" + util::utos(addr.port)) << ", proto=" << strproto(addr.proto) << (addr.tls ? ", tls" : ""); } } #ifdef HAVE_MRUBY // Try compile mruby script and catch compile error early. if (!g.mruby_file.empty()) { if (mruby::create_mruby_context(g.mruby_file) == nullptr) { LOG(config->ignore_per_pattern_mruby_error ? ERROR : FATAL) << "backend: Could not compile mruby file for pattern " << g.pattern; if (!config->ignore_per_pattern_mruby_error) { return -1; } g.mruby_file = ""sv; } } #endif // defined(HAVE_MRUBY) } #ifdef HAVE_MRUBY // Try compile mruby script (--mruby-file) here to catch compile // error early. if (!config->mruby_file.empty()) { if (mruby::create_mruby_context(config->mruby_file) == nullptr) { LOG(FATAL) << "mruby-file: Could not compile mruby file"; return -1; } } #endif // defined(HAVE_MRUBY) if (catch_all_group == -1) { LOG(FATAL) << "backend: No catch-all backend address is configured"; return -1; } downstreamconf.addr_group_catch_all = as_unsigned(catch_all_group); if (LOG_ENABLED(INFO)) { LOG(INFO) << "Catch-all pattern is group " << catch_all_group; } auto resolve_flags = numeric_addr_only ? AI_NUMERICHOST | AI_NUMERICSERV : 0; std::array hostport_buf; for (auto &g : addr_groups) { std::unordered_map wgchk; for (auto &addr : g.addrs) { if (addr.group_weight) { auto it = wgchk.find(addr.group); if (it == std::ranges::end(wgchk)) { wgchk.emplace(addr.group, addr.group_weight); } else if ((*it).second != addr.group_weight) { LOG(FATAL) << "backend: inconsistent group-weight for a single group"; return -1; } } if (addr.host_unix) { // for AF_UNIX socket, we use "localhost" as host for backend // hostport. This is used as Host header field to backend and // not going to be passed to any syscalls. addr.hostport = "localhost"sv; auto path = addr.host.data(); auto pathlen = addr.host.size(); if (pathlen + 1 > sizeof(addr.addr.su.un.sun_path)) { LOG(FATAL) << "UNIX domain socket path " << path << " is too long > " << sizeof(addr.addr.su.un.sun_path); return -1; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "Use UNIX domain socket path " << path << " for backend connection"; } addr.addr.su.un.sun_family = AF_UNIX; // copy path including terminal NULL std::ranges::copy_n(path, as_signed(pathlen + 1), addr.addr.su.un.sun_path); addr.addr.len = sizeof(addr.addr.su.un); continue; } addr.hostport = util::make_http_hostport(downstreamconf.balloc, addr.host, addr.port); auto hostport = util::make_hostport(addr.host, addr.port, std::ranges::begin(hostport_buf)); if (!addr.dns) { if (resolve_hostname(&addr.addr, addr.host.data(), addr.port, downstreamconf.family, resolve_flags) == -1) { LOG(FATAL) << "Resolving backend address failed: " << hostport; return -1; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "Resolved backend address: " << hostport << " -> " << util::to_numeric_addr(&addr.addr); } } else { LOG(INFO) << "Resolving backend address " << hostport << " takes place dynamically"; } } for (auto &addr : g.addrs) { if (addr.group_weight == 0) { auto it = wgchk.find(addr.group); if (it == std::ranges::end(wgchk)) { addr.group_weight = 1; } else { addr.group_weight = (*it).second; } } } if (g.affinity.type != SessionAffinity::NONE) { size_t idx = 0; for (auto &addr : g.addrs) { std::string_view key; if (addr.dns) { if (addr.host_unix) { key = addr.host; } else { key = addr.hostport; } } else { key = std::string_view{reinterpret_cast(&addr.addr.su), addr.addr.len}; } rv = compute_affinity_hash(g.affinity_hash, idx, key); if (rv != 0) { return -1; } if (g.affinity.cookie.stickiness == SessionAffinityCookieStickiness::STRICT) { addr.affinity_hash = util::hash32(key); g.affinity_hash_map.emplace(addr.affinity_hash, idx); } ++idx; } std::ranges::sort(g.affinity_hash, [](const auto &lhs, const auto &rhs) { return lhs.hash < rhs.hash; }); } auto &timeout = g.timeout; if (timeout.read < 1e-9) { timeout.read = downstreamconf.timeout.read; } if (timeout.write < 1e-9) { timeout.write = downstreamconf.timeout.write; } } return 0; } int resolve_hostname(Address *addr, const char *hostname, uint16_t port, int family, int additional_flags) { int rv; auto service = util::utos(port); addrinfo hints{ .ai_flags = additional_flags #ifdef AI_ADDRCONFIG | AI_ADDRCONFIG #endif // defined(AI_ADDRCONFIG) , .ai_family = family, .ai_socktype = SOCK_STREAM, }; addrinfo *res; rv = getaddrinfo(hostname, service.c_str(), &hints, &res); #ifdef AI_ADDRCONFIG if (rv != 0) { // Retry without AI_ADDRCONFIG hints.ai_flags &= ~AI_ADDRCONFIG; rv = getaddrinfo(hostname, service.c_str(), &hints, &res); } #endif // defined(AI_ADDRCONFIG) if (rv != 0) { LOG(FATAL) << "Unable to resolve address for " << hostname << ": " << gai_strerror(rv); return -1; } auto res_d = defer(freeaddrinfo, res); std::array host; rv = getnameinfo(res->ai_addr, res->ai_addrlen, host.data(), host.size(), nullptr, 0, NI_NUMERICHOST); if (rv != 0) { LOG(FATAL) << "Address resolution for " << hostname << " failed: " << gai_strerror(rv); return -1; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "Address resolution for " << hostname << " succeeded: " << host.data(); } memcpy(&addr->su, res->ai_addr, res->ai_addrlen); addr->len = res->ai_addrlen; return 0; } #ifdef ENABLE_HTTP3 QUICKeyingMaterial::QUICKeyingMaterial(QUICKeyingMaterial &&other) noexcept : cid_encryption_ctx{std::exchange(other.cid_encryption_ctx, nullptr)}, cid_decryption_ctx{std::exchange(other.cid_decryption_ctx, nullptr)}, reserved{other.reserved}, secret{other.secret}, salt{other.salt}, cid_encryption_key{other.cid_encryption_key}, id{other.id} {} QUICKeyingMaterial::~QUICKeyingMaterial() noexcept { if (cid_encryption_ctx) { EVP_CIPHER_CTX_free(cid_encryption_ctx); } if (cid_decryption_ctx) { EVP_CIPHER_CTX_free(cid_decryption_ctx); } } QUICKeyingMaterial & QUICKeyingMaterial::operator=(QUICKeyingMaterial &&other) noexcept { cid_encryption_ctx = std::exchange(other.cid_encryption_ctx, nullptr); cid_decryption_ctx = std::exchange(other.cid_decryption_ctx, nullptr); reserved = other.reserved; secret = other.secret; salt = other.salt; cid_encryption_key = other.cid_encryption_key; id = other.id; return *this; } #endif // defined(ENABLE_HTTP3) } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_config_test.h0000644000000000000000000000013115077107270016747 xustar0030 mtime=1761382072.991444148 30 atime=1761382106.252310215 29 ctime=1761382109.24929995 nghttp2-1.68.0/src/shrpx_config_test.h0000644000175100017510000000327015077107270017342 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_CONFIG_TEST_H #define SHRPX_CONFIG_TEST_H #ifdef HAVE_CONFIG_H # include #endif // defined(HAVE_CONFIG_H) #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" namespace shrpx { extern const MunitSuite config_suite; munit_void_test_decl(test_shrpx_config_parse_header) munit_void_test_decl(test_shrpx_config_parse_log_format) munit_void_test_decl(test_shrpx_config_read_tls_ticket_key_file) munit_void_test_decl(test_shrpx_config_read_tls_ticket_key_file_aes_256) } // namespace shrpx #endif // !defined(SHRPX_CONFIG_TEST_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_accept_handler.cc0000644000000000000000000000013215077107270017536 xustar0030 mtime=1761382072.990444153 30 atime=1761382106.081310969 30 ctime=1761382109.077300448 nghttp2-1.68.0/src/shrpx_accept_handler.cc0000644000175100017510000000631715077107270020135 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_accept_handler.h" #ifdef HAVE_UNISTD_H # include #endif // defined(HAVE_UNISTD_H) #include #include "shrpx_connection_handler.h" #include "shrpx_config.h" #include "shrpx_log.h" #include "shrpx_worker.h" #include "util.h" using namespace nghttp2; namespace shrpx { namespace { void acceptcb(struct ev_loop *loop, ev_io *w, int revent) { auto h = static_cast(w->data); h->accept_connection(); } } // namespace AcceptHandler::AcceptHandler(Worker *worker, const UpstreamAddr *faddr) : worker_(worker), faddr_(faddr) { ev_io_init(&wev_, acceptcb, faddr_->fd, EV_READ); wev_.data = this; ev_io_start(worker_->get_loop(), &wev_); } AcceptHandler::~AcceptHandler() { ev_io_stop(worker_->get_loop(), &wev_); close(faddr_->fd); } void AcceptHandler::accept_connection() { sockaddr_union sockaddr; socklen_t addrlen = sizeof(sockaddr); #ifdef HAVE_ACCEPT4 auto cfd = accept4(faddr_->fd, &sockaddr.sa, &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC); #else // !defined(HAVE_ACCEPT4) auto cfd = accept(faddr_->fd, &sockaddr.sa, &addrlen); #endif // !defined(HAVE_ACCEPT4) if (cfd == -1) { switch (errno) { case EINTR: case ENETDOWN: case EPROTO: case ENOPROTOOPT: case EHOSTDOWN: #ifdef ENONET case ENONET: #endif // defined(ENONET) case EHOSTUNREACH: case EOPNOTSUPP: case ENETUNREACH: return; case EMFILE: case ENFILE: LOG(WARN) << "acceptor: running out file descriptor; disable acceptor " "temporarily"; worker_->sleep_listener(get_config()->conn.listener.timeout.sleep); return; default: return; } } #ifndef HAVE_ACCEPT4 util::make_socket_nonblocking(cfd); util::make_socket_closeonexec(cfd); #endif // !defined(HAVE_ACCEPT4) worker_->handle_connection(cfd, &sockaddr.sa, addrlen, faddr_); } void AcceptHandler::enable() { ev_io_start(worker_->get_loop(), &wev_); } void AcceptHandler::disable() { ev_io_stop(worker_->get_loop(), &wev_); } int AcceptHandler::get_fd() const { return faddr_->fd; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_dual_dns_resolver.cc0000644000000000000000000000013215077107270020314 xustar0030 mtime=1761382072.993444139 30 atime=1761382106.166310594 30 ctime=1761382109.162300202 nghttp2-1.68.0/src/shrpx_dual_dns_resolver.cc0000644000175100017510000000562215077107270020711 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_dual_dns_resolver.h" namespace shrpx { DualDNSResolver::DualDNSResolver(struct ev_loop *loop, int family) : family_(family), resolv4_(loop), resolv6_(loop) { auto cb = [this](DNSResolverStatus, const Address *) { Address result; auto status = this->get_status(&result); switch (status) { case DNSResolverStatus::ERROR: case DNSResolverStatus::OK: break; default: return; } auto cb = this->get_complete_cb(); cb(status, &result); }; if (family_ == AF_UNSPEC || family_ == AF_INET) { resolv4_.set_complete_cb(cb); } if (family_ == AF_UNSPEC || family_ == AF_INET6) { resolv6_.set_complete_cb(cb); } } int DualDNSResolver::resolve(const std::string_view &host) { int rv4 = 0, rv6 = 0; if (family_ == AF_UNSPEC || family_ == AF_INET) { rv4 = resolv4_.resolve(host, AF_INET); } if (family_ == AF_UNSPEC || family_ == AF_INET6) { rv6 = resolv6_.resolve(host, AF_INET6); } if (rv4 != 0 && rv6 != 0) { return -1; } return 0; } CompleteCb DualDNSResolver::get_complete_cb() const { return complete_cb_; } void DualDNSResolver::set_complete_cb(CompleteCb cb) { complete_cb_ = cb; } DNSResolverStatus DualDNSResolver::get_status(Address *result) const { auto rv6 = resolv6_.get_status(result); if (rv6 == DNSResolverStatus::OK) { return DNSResolverStatus::OK; } auto rv4 = resolv4_.get_status(result); if (rv4 == DNSResolverStatus::OK) { return DNSResolverStatus::OK; } if (rv4 == DNSResolverStatus::RUNNING || rv6 == DNSResolverStatus::RUNNING) { return DNSResolverStatus::RUNNING; } if (rv4 == DNSResolverStatus::ERROR || rv6 == DNSResolverStatus::ERROR) { return DNSResolverStatus::ERROR; } return DNSResolverStatus::IDLE; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/nghttp2_gzip_test.c0000644000000000000000000000013115077107270016670 xustar0030 mtime=1761382072.989444157 30 atime=1761382106.266310154 29 ctime=1761382109.26329991 nghttp2-1.68.0/src/nghttp2_gzip_test.c0000644000175100017510000000752015077107270017265 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp2_gzip_test.h" #include #include #include "munit.h" #include #include "nghttp2_gzip.h" static const MunitTest tests[] = { munit_void_test(test_nghttp2_gzip_inflate), munit_test_end(), }; const MunitSuite gzip_suite = { "/gzip", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE, }; static size_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen) { int rv; z_stream zst = {0}; rv = deflateInit(&zst, Z_DEFAULT_COMPRESSION); assert_int(Z_OK, ==, rv); zst.avail_in = (unsigned int)inlen; zst.next_in = (uint8_t *)in; zst.avail_out = (unsigned int)outlen; zst.next_out = out; rv = deflate(&zst, Z_SYNC_FLUSH); assert_int(Z_OK, ==, rv); deflateEnd(&zst); return outlen - zst.avail_out; } static const char input[] = "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND " "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF " "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND " "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE " "LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION " "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION " "WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."; void test_nghttp2_gzip_inflate(void) { nghttp2_gzip *inflater; uint8_t in[4096], out[4096], *inptr; size_t inlen = sizeof(in); size_t inproclen, outproclen; const char *inputptr = input; inlen = deflate_data(in, inlen, (const uint8_t *)input, sizeof(input) - 1); assert_int(0, ==, nghttp2_gzip_inflate_new(&inflater)); /* First 16 bytes */ inptr = in; inproclen = inlen; outproclen = 16; assert_int( 0, ==, nghttp2_gzip_inflate(inflater, out, &outproclen, inptr, &inproclen)); assert_size(16, ==, outproclen); assert_size(0, <, inproclen); assert_memory_equal(outproclen, inputptr, out); /* Next 32 bytes */ inptr += inproclen; inlen -= inproclen; inproclen = inlen; inputptr += outproclen; outproclen = 32; assert_int( 0, ==, nghttp2_gzip_inflate(inflater, out, &outproclen, inptr, &inproclen)); assert_size(32, ==, outproclen); assert_size(0, <, inproclen); assert_memory_equal(outproclen, inputptr, out); /* Rest */ inptr += inproclen; inlen -= inproclen; inproclen = inlen; inputptr += outproclen; outproclen = sizeof(out); assert_int( 0, ==, nghttp2_gzip_inflate(inflater, out, &outproclen, inptr, &inproclen)); assert_size(sizeof(input) - 49, ==, outproclen); assert_size(0, <, inproclen); assert_memory_equal(outproclen, inputptr, out); inlen -= inproclen; assert_size(0, ==, inlen); nghttp2_gzip_inflate_del(inflater); } nghttp2-1.68.0/src/PaxHeaders/siphash_test.h0000644000000000000000000000013215077107271015717 xustar0030 mtime=1761382073.000444106 30 atime=1761382080.105411485 30 ctime=1761382109.279299864 nghttp2-1.68.0/src/siphash_test.h0000644000175100017510000000273215077107271016313 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2025 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SIPHASH_TEST_H #define SIPHASH_TEST_H #ifdef HAVE_CONFIG_H # include #endif // defined(HAVE_CONFIG_H) #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" namespace nghttp2 { extern const MunitSuite siphash_suite; munit_void_test_decl(test_siphash) } // namespace nghttp2 #endif // !defined(SIPHASH_TEST_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_live_check.cc0000644000000000000000000000013215077107270016676 xustar0030 mtime=1761382072.996444125 30 atime=1761382106.129310757 30 ctime=1761382109.125300309 nghttp2-1.68.0/src/shrpx_live_check.cc0000644000175100017510000004360015077107270017271 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_live_check.h" #include "shrpx_worker.h" #include "shrpx_connect_blocker.h" #include "shrpx_tls.h" #include "shrpx_log.h" namespace shrpx { namespace { constexpr size_t MAX_BUFFER_SIZE = 4_k; } // namespace namespace { void readcb(struct ev_loop *loop, ev_io *w, int revents) { int rv; auto conn = static_cast(w->data); auto live_check = static_cast(conn->data); rv = live_check->do_read(); if (rv != 0) { live_check->on_failure(); return; } } } // namespace namespace { void writecb(struct ev_loop *loop, ev_io *w, int revents) { int rv; auto conn = static_cast(w->data); auto live_check = static_cast(conn->data); rv = live_check->do_write(); if (rv != 0) { live_check->on_failure(); return; } } } // namespace namespace { void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { auto conn = static_cast(w->data); auto live_check = static_cast(conn->data); if (w == &conn->rt && !conn->expired_rt()) { return; } live_check->on_failure(); } } // namespace namespace { void backoff_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { int rv; auto live_check = static_cast(w->data); rv = live_check->initiate_connection(); if (rv != 0) { live_check->on_failure(); return; } } } // namespace namespace { void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto live_check = static_cast(w->data); if (LOG_ENABLED(INFO)) { LOG(INFO) << "SETTINGS timeout"; } live_check->on_failure(); } } // namespace LiveCheck::LiveCheck(struct ev_loop *loop, SSL_CTX *ssl_ctx, Worker *worker, DownstreamAddr *addr, std::mt19937 &gen) : conn_(loop, -1, nullptr, worker->get_mcpool(), worker->get_downstream_config()->timeout.write, worker->get_downstream_config()->timeout.read, {}, {}, writecb, readcb, timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold, get_config()->tls.dyn_rec.idle_timeout, Proto::NONE), wb_(worker->get_mcpool()), gen_(gen), read_(&LiveCheck::noop), write_(&LiveCheck::noop), worker_(worker), ssl_ctx_(ssl_ctx), addr_(addr), session_(nullptr), raddr_(nullptr), success_count_(0), fail_count_(0), settings_ack_received_(false), session_closing_(false) { ev_timer_init(&backoff_timer_, backoff_timeoutcb, 0., 0.); backoff_timer_.data = this; // SETTINGS ACK must be received in a short timeout. Otherwise, we // assume that connection is broken. ev_timer_init(&settings_timer_, settings_timeout_cb, 0., 0.); settings_timer_.data = this; } LiveCheck::~LiveCheck() { disconnect(); ev_timer_stop(conn_.loop, &backoff_timer_); } void LiveCheck::disconnect() { if (dns_query_) { auto dns_tracker = worker_->get_dns_tracker(); dns_tracker->cancel(dns_query_.get()); } dns_query_.reset(); // We can reuse resolved_addr_ raddr_ = nullptr; conn_.rlimit.stopw(); conn_.wlimit.stopw(); ev_timer_stop(conn_.loop, &settings_timer_); read_ = write_ = &LiveCheck::noop; conn_.disconnect(); nghttp2_session_del(session_); session_ = nullptr; settings_ack_received_ = false; session_closing_ = false; wb_.reset(); } // Use the similar backoff algorithm described in // https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md namespace { constexpr size_t MAX_BACKOFF_EXP = 10; constexpr auto MULTIPLIER = 1.6; constexpr auto JITTER = 0.2; } // namespace void LiveCheck::schedule() { auto base_backoff = util::int_pow(MULTIPLIER, std::min(fail_count_, MAX_BACKOFF_EXP)); auto dist = std::uniform_real_distribution<>(-JITTER * base_backoff, JITTER * base_backoff); auto &downstreamconf = *get_config()->conn.downstream; auto backoff = std::min(downstreamconf.timeout.max_backoff, base_backoff + dist(gen_)); ev_timer_set(&backoff_timer_, backoff, 0.); ev_timer_start(conn_.loop, &backoff_timer_); } int LiveCheck::do_read() { return read_(*this); } int LiveCheck::do_write() { return write_(*this); } int LiveCheck::initiate_connection() { int rv; auto worker_blocker = worker_->get_connect_blocker(); if (worker_blocker->blocked()) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "Worker wide backend connection was blocked temporarily"; } return -1; } if (!dns_query_ && addr_->tls) { assert(ssl_ctx_); auto ssl = tls::create_ssl(ssl_ctx_); if (!ssl) { return -1; } switch (addr_->proto) { case Proto::HTTP1: tls::setup_downstream_http1_alpn(ssl); break; case Proto::HTTP2: tls::setup_downstream_http2_alpn(ssl); break; default: assert(0); } conn_.set_ssl(ssl); conn_.tls.client_session_cache = &addr_->tls_session_cache; } if (addr_->dns) { if (!dns_query_) { auto dns_query = std::make_unique( addr_->host, [this](DNSResolverStatus status, const Address *result) { int rv; if (status == DNSResolverStatus::OK) { *this->resolved_addr_ = *result; } rv = this->initiate_connection(); if (rv != 0) { this->on_failure(); } }); auto dns_tracker = worker_->get_dns_tracker(); if (!resolved_addr_) { resolved_addr_ = std::make_unique
(); } switch (dns_tracker->resolve(resolved_addr_.get(), dns_query.get())) { case DNSResolverStatus::ERROR: return -1; case DNSResolverStatus::RUNNING: dns_query_ = std::move(dns_query); return 0; case DNSResolverStatus::OK: break; default: assert(0); } } else { switch (dns_query_->status) { case DNSResolverStatus::ERROR: dns_query_.reset(); return -1; case DNSResolverStatus::OK: dns_query_.reset(); break; default: assert(0); } } util::set_port(*resolved_addr_, addr_->port); raddr_ = resolved_addr_.get(); } else { raddr_ = &addr_->addr; } conn_.fd = util::create_nonblock_socket(raddr_->su.storage.ss_family); if (conn_.fd == -1) { auto error = errno; LOG(WARN) << "socket() failed; addr=" << util::to_numeric_addr(raddr_) << ", errno=" << error; return -1; } rv = connect(conn_.fd, &raddr_->su.sa, raddr_->len); if (rv != 0 && errno != EINPROGRESS) { auto error = errno; LOG(WARN) << "connect() failed; addr=" << util::to_numeric_addr(raddr_) << ", errno=" << error; close(conn_.fd); conn_.fd = -1; return -1; } if (addr_->tls) { auto sni_name = addr_->sni.empty() ? addr_->host : addr_->sni; if (!util::numeric_host(sni_name.data())) { SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name.data()); } auto session = tls::reuse_tls_session(addr_->tls_session_cache); if (session) { SSL_set_session(conn_.tls.ssl, session); SSL_SESSION_free(session); } conn_.prepare_client_handshake(); } write_ = &LiveCheck::connected; ev_io_set(&conn_.wev, conn_.fd, EV_WRITE); ev_io_set(&conn_.rev, conn_.fd, EV_READ); conn_.wlimit.startw(); auto &downstreamconf = *get_config()->conn.downstream; conn_.wt.repeat = downstreamconf.timeout.connect; ev_timer_again(conn_.loop, &conn_.wt); return 0; } int LiveCheck::connected() { auto sock_error = util::get_socket_error(conn_.fd); if (sock_error != 0) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "Backend connect failed; addr=" << util::to_numeric_addr(raddr_) << ": errno=" << sock_error; } return -1; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "Connection established"; } auto &downstreamconf = *get_config()->conn.downstream; // Reset timeout for write. Previously, we set timeout for connect. conn_.wt.repeat = downstreamconf.timeout.write; ev_timer_again(conn_.loop, &conn_.wt); conn_.rlimit.startw(); conn_.again_rt(); if (conn_.tls.ssl) { read_ = &LiveCheck::tls_handshake; write_ = &LiveCheck::tls_handshake; return do_write(); } if (addr_->proto == Proto::HTTP2) { // For HTTP/2, we try to read SETTINGS ACK from server to make // sure it is really alive, and serving HTTP/2. read_ = &LiveCheck::read_clear; write_ = &LiveCheck::write_clear; if (connection_made() != 0) { return -1; } return 0; } on_success(); return 0; } int LiveCheck::tls_handshake() { conn_.last_read = std::chrono::steady_clock::now(); ERR_clear_error(); auto rv = conn_.tls_handshake(); if (rv == SHRPX_ERR_INPROGRESS) { return 0; } if (rv < 0) { return rv; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "SSL/TLS handshake completed"; } if (!get_config()->tls.insecure && tls::check_cert(conn_.tls.ssl, addr_, raddr_) != 0) { return -1; } // Check negotiated ALPN const unsigned char *next_proto = nullptr; unsigned int next_proto_len = 0; SSL_get0_alpn_selected(conn_.tls.ssl, &next_proto, &next_proto_len); auto proto = as_string_view(next_proto, next_proto_len); switch (addr_->proto) { case Proto::HTTP1: if (proto.empty() || proto == "http/1.1"sv) { break; } return -1; case Proto::HTTP2: if (util::check_h2_is_selected(proto)) { // For HTTP/2, we try to read SETTINGS ACK from server to make // sure it is really alive, and serving HTTP/2. read_ = &LiveCheck::read_tls; write_ = &LiveCheck::write_tls; if (connection_made() != 0) { return -1; } return 0; } return -1; default: break; } on_success(); return 0; } int LiveCheck::read_tls() { conn_.last_read = std::chrono::steady_clock::now(); std::array buf; ERR_clear_error(); for (;;) { auto nread = conn_.read_tls(buf.data(), buf.size()); if (nread == 0) { return 0; } if (nread < 0) { return static_cast(nread); } if (on_read(buf.data(), as_unsigned(nread)) != 0) { return -1; } } } int LiveCheck::write_tls() { conn_.last_read = std::chrono::steady_clock::now(); ERR_clear_error(); struct iovec iov; for (;;) { if (wb_.rleft() > 0) { auto iovcnt = wb_.riovec(&iov, 1); if (iovcnt != 1) { assert(0); return -1; } auto nwrite = conn_.write_tls(iov.iov_base, iov.iov_len); if (nwrite == 0) { return 0; } if (nwrite < 0) { return static_cast(nwrite); } wb_.drain(as_unsigned(nwrite)); continue; } if (on_write() != 0) { return -1; } if (wb_.rleft() == 0) { conn_.start_tls_write_idle(); break; } } conn_.wlimit.stopw(); ev_timer_stop(conn_.loop, &conn_.wt); if (settings_ack_received_) { on_success(); } return 0; } int LiveCheck::read_clear() { conn_.last_read = std::chrono::steady_clock::now(); std::array buf; for (;;) { auto nread = conn_.read_clear(buf.data(), buf.size()); if (nread == 0) { return 0; } if (nread < 0) { return static_cast(nread); } if (on_read(buf.data(), as_unsigned(nread)) != 0) { return -1; } } } int LiveCheck::write_clear() { conn_.last_read = std::chrono::steady_clock::now(); struct iovec iov; for (;;) { if (wb_.rleft() > 0) { auto iovcnt = wb_.riovec(&iov, 1); if (iovcnt != 1) { assert(0); return -1; } auto nwrite = conn_.write_clear(iov.iov_base, iov.iov_len); if (nwrite == 0) { return 0; } if (nwrite < 0) { return static_cast(nwrite); } wb_.drain(as_unsigned(nwrite)); continue; } if (on_write() != 0) { return -1; } if (wb_.rleft() == 0) { break; } } conn_.wlimit.stopw(); ev_timer_stop(conn_.loop, &conn_.wt); if (settings_ack_received_) { on_success(); } return 0; } int LiveCheck::on_read(const uint8_t *data, size_t len) { auto rv = nghttp2_session_mem_recv2(session_, data, len); if (rv < 0) { LOG(ERROR) << "nghttp2_session_mem_recv2() returned error: " << nghttp2_strerror(static_cast(rv)); return -1; } if (settings_ack_received_ && !session_closing_) { session_closing_ = true; auto rv = nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR); if (rv != 0) { return -1; } } if (nghttp2_session_want_read(session_) == 0 && nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "No more read/write for this session"; } // If we have SETTINGS ACK already, we treat this success. if (settings_ack_received_) { return 0; } return -1; } signal_write(); return 0; } int LiveCheck::on_write() { for (;;) { const uint8_t *data; auto datalen = nghttp2_session_mem_send2(session_, &data); if (datalen < 0) { LOG(ERROR) << "nghttp2_session_mem_send2() returned error: " << nghttp2_strerror(static_cast(datalen)); return -1; } if (datalen == 0) { break; } wb_.append(data, as_unsigned(datalen)); if (wb_.rleft() >= MAX_BUFFER_SIZE) { break; } } if (nghttp2_session_want_read(session_) == 0 && nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "No more read/write for this session"; } if (settings_ack_received_) { return 0; } return -1; } return 0; } void LiveCheck::on_failure() { ++fail_count_; if (LOG_ENABLED(INFO)) { LOG(INFO) << "Liveness check for " << addr_->host << ":" << addr_->port << " failed " << fail_count_ << " time(s) in a row"; } disconnect(); schedule(); } void LiveCheck::on_success() { ++success_count_; fail_count_ = 0; if (LOG_ENABLED(INFO)) { LOG(INFO) << "Liveness check for " << addr_->host << ":" << addr_->port << " succeeded " << success_count_ << " time(s) in a row"; } if (success_count_ < addr_->rise) { disconnect(); schedule(); return; } LOG(NOTICE) << util::to_numeric_addr(&addr_->addr) << " is considered online"; addr_->connect_blocker->online(); success_count_ = 0; fail_count_ = 0; disconnect(); } int LiveCheck::noop() { return 0; } void LiveCheck::start_settings_timer() { auto &downstreamconf = get_config()->http2.downstream; ev_timer_set(&settings_timer_, downstreamconf.timeout.settings, 0.); ev_timer_start(conn_.loop, &settings_timer_); } void LiveCheck::stop_settings_timer() { ev_timer_stop(conn_.loop, &settings_timer_); } void LiveCheck::settings_ack_received() { settings_ack_received_ = true; } namespace { int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { auto live_check = static_cast(user_data); if (frame->hd.type != NGHTTP2_SETTINGS || (frame->hd.flags & NGHTTP2_FLAG_ACK)) { return 0; } live_check->start_settings_timer(); return 0; } } // namespace namespace { int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { auto live_check = static_cast(user_data); if (frame->hd.type != NGHTTP2_SETTINGS || (frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { return 0; } live_check->stop_settings_timer(); live_check->settings_ack_received(); return 0; } } // namespace int LiveCheck::connection_made() { int rv; nghttp2_session_callbacks *callbacks; rv = nghttp2_session_callbacks_new(&callbacks); if (rv != 0) { return -1; } nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, on_frame_send_callback); nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, on_frame_recv_callback); nghttp2_session_callbacks_set_rand_callback(callbacks, util::secure_random); rv = nghttp2_session_client_new(&session_, callbacks, this); nghttp2_session_callbacks_del(callbacks); if (rv != 0) { return -1; } rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, nullptr, 0); if (rv != 0) { return -1; } auto must_terminate = addr_->tls && !nghttp2::tls::check_http2_requirement(conn_.tls.ssl); if (must_terminate) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "TLSv1.2 was not negotiated. HTTP/2 must not be negotiated."; } rv = nghttp2_session_terminate_session(session_, NGHTTP2_INADEQUATE_SECURITY); if (rv != 0) { return -1; } } signal_write(); return 0; } void LiveCheck::signal_write() { conn_.wlimit.startw(); } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_error.h0000644000000000000000000000013115077107270015574 xustar0030 mtime=1761382072.993444139 30 atime=1761382106.080310973 29 ctime=1761382109.07630045 nghttp2-1.68.0/src/shrpx_error.h0000644000175100017510000000310715077107270016166 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_ERROR_H #define SHRPX_ERROR_H #include "shrpx.h" namespace shrpx { // Deprecated, do not use. enum ErrorCode { SHRPX_ERR_SUCCESS = 0, SHRPX_ERR_ERROR = -1, SHRPX_ERR_NETWORK = -100, SHRPX_ERR_EOF = -101, SHRPX_ERR_INPROGRESS = -102, SHRPX_ERR_DCONN_CANCELED = -103, SHRPX_ERR_RETRY = -104, SHRPX_ERR_TLS_REQUIRED = -105, SHRPX_ERR_SEND_BLOCKED = -106, }; } // namespace shrpx #endif // !defined(SHRPX_ERROR_H) nghttp2-1.68.0/src/PaxHeaders/buffer_test.cc0000644000000000000000000000013115077107270015665 xustar0029 mtime=1761382072.98444418 30 atime=1761382106.269310141 30 ctime=1761382109.266299901 nghttp2-1.68.0/src/buffer_test.cc0000644000175100017510000000467615077107270016273 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "buffer_test.h" #include #include #include #include "munitxx.h" #include #include "buffer.h" namespace nghttp2 { namespace { const MunitTest tests[]{ munit_void_test(test_buffer_write), munit_test_end(), }; } // namespace const MunitSuite buffer_suite{ "/buffer", tests, nullptr, 1, MUNIT_SUITE_OPTION_NONE, }; void test_buffer_write(void) { Buffer<16> b; assert_size(0, ==, b.rleft()); assert_size(16, ==, b.wleft()); b.write("012", 3); assert_size(3, ==, b.rleft()); assert_size(13, ==, b.wleft()); assert_ptr_equal(b.pos, std::ranges::begin(b.buf)); b.drain(3); assert_size(0, ==, b.rleft()); assert_size(13, ==, b.wleft()); assert_ptrdiff(3, ==, b.pos - std::ranges::begin(b.buf)); auto n = b.write("0123456789ABCDEF", 16); assert_size(13, ==, n); assert_size(13, ==, b.rleft()); assert_size(0, ==, b.wleft()); assert_ptrdiff(3, ==, b.pos - std::ranges::begin(b.buf)); assert_memory_equal(13, b.pos, "0123456789ABC"); b.reset(); assert_size(0, ==, b.rleft()); assert_size(16, ==, b.wleft()); assert_ptr_equal(b.pos, std::ranges::begin(b.buf)); b.write(5); assert_size(5, ==, b.rleft()); assert_size(11, ==, b.wleft()); assert_ptr_equal(b.pos, std::ranges::begin(b.buf)); } } // namespace nghttp2 nghttp2-1.68.0/src/PaxHeaders/shrpx_signal.h0000644000000000000000000000013215077107270015721 xustar0030 mtime=1761382072.999444111 30 atime=1761382106.152310656 30 ctime=1761382109.148300242 nghttp2-1.68.0/src/shrpx_signal.h0000644000175100017510000000431315077107270016312 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_SIGNAL_H #define SHRPX_SIGNAL_H #include "shrpx.h" #include namespace shrpx { inline constexpr int REOPEN_LOG_SIGNAL = SIGUSR1; inline constexpr int EXEC_BINARY_SIGNAL = SIGUSR2; inline constexpr int GRACEFUL_SHUTDOWN_SIGNAL = SIGQUIT; inline constexpr int RELOAD_SIGNAL = SIGHUP; // Blocks all signals. The previous signal mask is stored into // |oldset| if it is not nullptr. This function returns 0 if it // succeeds, or -1. The errno will indicate the error. int shrpx_signal_block_all(sigset_t *oldset); // Unblocks all signals. This function returns 0 if it succeeds, or // -1. The errno will indicate the error. int shrpx_signal_unblock_all(); // Sets signal mask |set|. This function returns 0 if it succeeds, or // -1. The errno will indicate the error. int shrpx_signal_set(sigset_t *set); int shrpx_signal_set_main_proc_ign_handler(); int shrpx_signal_unset_main_proc_ign_handler(); int shrpx_signal_set_worker_proc_ign_handler(); int shrpx_signal_unset_worker_proc_ign_handler(); } // namespace shrpx #endif // !defined(SHRPX_SIGNAL_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_http3_upstream.h0000644000000000000000000000013115077107270017425 xustar0030 mtime=1761382072.995444129 29 atime=1761382106.20131044 30 ctime=1761382109.197300101 nghttp2-1.68.0/src/shrpx_http3_upstream.h0000644000175100017510000001745315077107270020030 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2021 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_HTTP3_UPSTREAM_H #define SHRPX_HTTP3_UPSTREAM_H #include "shrpx.h" #include #include #include "shrpx_upstream.h" #include "shrpx_downstream_queue.h" #include "network.h" #include "ssl_compat.h" #if defined(ENABLE_HTTP3) && OPENSSL_3_5_0_API # include #endif // defined(ENABLE_HTTP3) && OPENSSL_3_5_0_API using namespace nghttp2; namespace shrpx { struct UpstreamAddr; class Http3Upstream : public Upstream { public: Http3Upstream(ClientHandler *handler); virtual ~Http3Upstream(); virtual int on_read(); virtual int on_write(); virtual int on_timeout(Downstream *downstream); virtual int on_downstream_abort_request(Downstream *downstream, unsigned int status_code); virtual int on_downstream_abort_request_with_https_redirect(Downstream *downstream); virtual int downstream_read(DownstreamConnection *dconn); virtual int downstream_write(DownstreamConnection *dconn); virtual int downstream_eof(DownstreamConnection *dconn); virtual int downstream_error(DownstreamConnection *dconn, int events); virtual ClientHandler *get_client_handler() const; virtual int on_downstream_header_complete(Downstream *downstream); virtual int on_downstream_body(Downstream *downstream, const uint8_t *data, size_t len, bool flush); virtual int on_downstream_body_complete(Downstream *downstream); virtual void on_handler_delete(); virtual int on_downstream_reset(Downstream *downstream, bool no_retry); virtual void pause_read(IOCtrlReason reason); virtual int resume_read(IOCtrlReason reason, Downstream *downstream, size_t consumed); virtual int send_reply(Downstream *downstream, const uint8_t *body, size_t bodylen); virtual int initiate_push(Downstream *downstream, const std::string_view &uri); virtual int response_riovec(struct iovec *iov, int iovcnt) const; virtual void response_drain(size_t n); virtual bool response_empty() const; virtual Downstream *on_downstream_push_promise(Downstream *downstream, int32_t promised_stream_id); virtual int on_downstream_push_promise_complete(Downstream *downstream, Downstream *promised_downstream); virtual bool push_enabled() const; virtual void cancel_premature_downstream(Downstream *promised_downstream); int init(const UpstreamAddr *faddr, const Address &remote_addr, const Address &local_addr, const ngtcp2_pkt_hd &initial_hd, const ngtcp2_cid *odcid, std::span token, ngtcp2_token_type token_type); int on_read(const UpstreamAddr *faddr, const Address &remote_addr, const Address &local_addr, const ngtcp2_pkt_info &pi, std::span data); int write_streams(); ngtcp2_ssize write_pkt(ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_tstamp ts); int handle_error(); int handle_expiry(); void reset_timer(); int setup_httpconn(); void add_pending_downstream(std::unique_ptr downstream); int recv_stream_data(uint32_t flags, int64_t stream_id, std::span data); int acked_stream_data_offset(int64_t stream_id, uint64_t datalen); int extend_max_stream_data(int64_t stream_id); void extend_max_remote_streams_bidi(uint64_t max_streams); int error_reply(Downstream *downstream, unsigned int status_code); void http_begin_request_headers(int64_t stream_id); int http_recv_request_header(Downstream *downstream, int32_t token, nghttp3_rcbuf *name, nghttp3_rcbuf *value, uint8_t flags, bool trailer); int http_end_request_headers(Downstream *downstream, int fin); int http_end_stream(Downstream *downstream); void start_downstream(Downstream *downstream); void initiate_downstream(Downstream *downstream); int shutdown_stream(Downstream *downstream, uint64_t app_error_code); int shutdown_stream_read(int64_t stream_id, uint64_t app_error_code); int http_stream_close(Downstream *downstream, uint64_t app_error_code); void consume(int64_t stream_id, size_t nconsumed); void remove_downstream(Downstream *downstream); int stream_close(int64_t stream_id, uint64_t app_error_code); void log_response_headers(Downstream *downstream, const std::vector &nva) const; int http_acked_stream_data(Downstream *downstream, uint64_t datalen); int http_shutdown_stream_read(int64_t stream_id); int http_reset_stream(int64_t stream_id, uint64_t app_error_code); int http_stop_sending(int64_t stream_id, uint64_t app_error_code); int http_recv_data(Downstream *downstream, std::span data); int handshake_completed(); int check_shutdown(); int start_graceful_shutdown(); int submit_goaway(); std::pair, int> send_packet(const UpstreamAddr *faddr, const sockaddr *remote_sa, socklen_t remote_salen, const sockaddr *local_sa, socklen_t local_salen, const ngtcp2_pkt_info &pi, std::span data, size_t gso_size); void send_packet(const ngtcp2_path &path, const ngtcp2_pkt_info &pi, const std::span data, size_t gso_size); void qlog_write(const void *data, size_t datalen, bool fin); int open_qlog_file(const std::string_view &dir, const ngtcp2_cid &scid) const; void on_send_blocked(const ngtcp2_path &path, const ngtcp2_pkt_info &pi, std::span data, size_t gso_size); int send_blocked_packet(); void signal_write_upstream_addr(const UpstreamAddr *faddr); ngtcp2_conn *get_conn() const; int send_new_token(const ngtcp2_addr *remote_addr); private: ClientHandler *handler_; ev_timer timer_; ev_timer shutdown_timer_; ev_prepare prep_; int qlog_fd_; ngtcp2_cid hashed_scid_; ngtcp2_conn *conn_; ngtcp2_ccerr last_error_; #if OPENSSL_3_5_0_API ngtcp2_crypto_ossl_ctx *ossl_ctx_; #endif // OPENSSL_3_5_0_API nghttp3_conn *httpconn_; DownstreamQueue downstream_queue_; std::vector conn_close_; struct { bool send_blocked; // blocked field is effective only when send_blocked is true. struct { const UpstreamAddr *faddr; Address local_addr; Address remote_addr; ngtcp2_pkt_info pi; std::span data; size_t gso_size; } blocked; std::unique_ptr data; bool no_gso; } tx_; }; } // namespace shrpx #endif // SHRPX_HTTP3_UPSTREAM_H nghttp2-1.68.0/src/PaxHeaders/memchunk_test.h0000644000000000000000000000013115077107270016065 xustar0030 mtime=1761382072.988444162 30 atime=1761382106.272310127 29 ctime=1761382109.27029989 nghttp2-1.68.0/src/memchunk_test.h0000644000175100017510000000344015077107270016457 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MEMCHUNK_TEST_H #define MEMCHUNK_TEST_H #ifdef HAVE_CONFIG_H # include #endif // defined(HAVE_CONFIG_H) #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" namespace nghttp2 { extern const MunitSuite memchunk_suite; munit_void_test_decl(test_pool_recycle) munit_void_test_decl(test_memchunks_append) munit_void_test_decl(test_memchunks_drain) munit_void_test_decl(test_memchunks_riovec) munit_void_test_decl(test_memchunks_recycle) munit_void_test_decl(test_memchunks_reset) munit_void_test_decl(test_memchunks_reserve) munit_void_test_decl(test_memchunkbuffer_drain_reset) } // namespace nghttp2 #endif // !defined(MEMCHUNK_TEST_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_http.h0000644000000000000000000000013215077107270015423 xustar0030 mtime=1761382072.993444139 30 atime=1761382106.115310819 30 ctime=1761382109.111300349 nghttp2-1.68.0/src/shrpx_http.h0000644000175100017510000001020415077107270016010 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_HTTP_H #define SHRPX_HTTP_H #include "shrpx.h" #include #include #include #include "shrpx_config.h" #include "util.h" #include "allocator.h" using namespace nghttp2; using namespace std::literals; namespace shrpx { namespace http { std::string_view create_error_html(BlockAllocator &balloc, unsigned int status_code); struct ViaValueGenerator { template requires(std::indirectly_writable) constexpr O operator()(int major, int minor, O result) { using result_type = std::iter_value_t; *result++ = static_cast(major + '0'); if (major < 2) { *result++ = '.'; *result++ = static_cast(minor + '0'); } return std::ranges::copy(" nghttpx"sv, result).out; } }; template OutputIt create_via_header_value(OutputIt dst, int major, int minor) { return ViaValueGenerator{}(major, minor, std::move(dst)); } // Returns generated RFC 7239 Forwarded header field value. The // |params| is bitwise-OR of zero or more of shrpx_forwarded_param // defined in shrpx_config.h. std::string_view create_forwarded(BlockAllocator &balloc, uint32_t params, const std::string_view &node_by, const std::string_view &node_for, const std::string_view &host, const std::string_view &proto); // Adds ANSI color codes to HTTP headers |hdrs|. std::string colorize_headers(const std::string_view &hdrs); nghttp2_ssize select_padding_callback(nghttp2_session *session, const nghttp2_frame *frame, size_t max_payload, void *user_data); // Creates set-cookie-string for cookie based affinity. If |path| is // not empty, "; " is added. If |secure| is true, "; Secure" is // added. std::string_view create_affinity_cookie(BlockAllocator &balloc, const std::string_view &name, uint32_t affinity_cookie, const std::string_view &path, bool secure); // Returns true if |secure| indicates that Secure attribute should be // set. bool require_cookie_secure_attribute(SessionAffinityCookieSecure secure, const std::string_view &scheme); // Returns RFC 7838 alt-svc header field value. std::string_view create_altsvc_header_value(BlockAllocator &balloc, const std::vector &altsvcs); // Returns true if either of the following conditions holds: // - scheme is https and encrypted is true // - scheme is http and encrypted is false // Otherwise returns false. bool check_http_scheme(const std::string_view &scheme, bool encrypted); } // namespace http } // namespace shrpx #endif // !defined(SHRPX_HTTP_H) nghttp2-1.68.0/src/PaxHeaders/template_test.h0000644000000000000000000000013115077107271016072 xustar0030 mtime=1761382073.001444102 29 atime=1761382080.10641148 30 ctime=1761382109.272299884 nghttp2-1.68.0/src/template_test.h0000644000175100017510000000312415077107271016463 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef TEMPLATE_TEST_H #define TEMPLATE_TEST_H #ifdef HAVE_CONFIG_H # include #endif // defined(HAVE_CONFIG_H) #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" namespace nghttp2 { extern const MunitSuite template_suite; munit_void_test_decl(test_template_immutable_string) munit_void_test_decl(test_template_as_uint8_span) munit_void_test_decl(test_template_as_string_view) } // namespace nghttp2 #endif // !defined(TEMPLATE_TEST_H) nghttp2-1.68.0/src/PaxHeaders/allocator.h0000644000000000000000000000013215077107270015200 xustar0030 mtime=1761382072.983444185 30 atime=1761382106.175310555 30 ctime=1761382109.171300176 nghttp2-1.68.0/src/allocator.h0000644000175100017510000002402315077107270015571 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef ALLOCATOR_H #define ALLOCATOR_H #include "nghttp2_config.h" #ifndef _WIN32 # include #endif // !defined(_WIN32) #include #include #include #include #include "template.h" namespace nghttp2 { struct MemBlock { // The next MemBlock to chain them. This is for book keeping // purpose to free them later. MemBlock *next; // begin is the pointer to the beginning of buffer. last is the // location of next write. end is the one beyond of the end of the // buffer. uint8_t *begin, *last, *end; }; static_assert((sizeof(MemBlock) & 0xf) == 0); struct ChunkHead { union { size_t size; uint64_t pad1; }; uint64_t pad2; }; static_assert(sizeof(ChunkHead) == 16); // BlockAllocator allocates memory block with given size at once, and // cuts the region from it when allocation is requested. If the // requested size is larger than given threshold (plus small internal // overhead), it will be allocated in a distinct buffer on demand. // The |isolation_threshold| must be less than or equal to // |block_size|. struct BlockAllocator { BlockAllocator(size_t block_size, size_t isolation_threshold) : retain(nullptr), head(nullptr), block_size(block_size), isolation_threshold(std::min(block_size, isolation_threshold)) { assert(isolation_threshold <= block_size); } ~BlockAllocator() { reset(); } BlockAllocator(BlockAllocator &&other) noexcept : retain{std::exchange(other.retain, nullptr)}, head{std::exchange(other.head, nullptr)}, block_size(other.block_size), isolation_threshold(other.isolation_threshold) {} BlockAllocator &operator=(BlockAllocator &&other) noexcept { reset(); retain = std::exchange(other.retain, nullptr); head = std::exchange(other.head, nullptr); block_size = other.block_size; isolation_threshold = other.isolation_threshold; return *this; } BlockAllocator(const BlockAllocator &) = delete; BlockAllocator &operator=(const BlockAllocator &) = delete; void reset() { for (auto mb = retain; mb;) { auto next = mb->next; operator delete[](reinterpret_cast(mb), std::align_val_t(16)); mb = next; } retain = nullptr; head = nullptr; } MemBlock *alloc_mem_block(size_t size) { auto block = new (std::align_val_t(16)) uint8_t[sizeof(MemBlock) + size]; auto mb = reinterpret_cast(block); mb->next = retain; mb->begin = mb->last = reinterpret_cast( (reinterpret_cast(block + sizeof(MemBlock)) + 0xf) & ~0xf); mb->end = mb->begin + size; retain = mb; return mb; } constexpr size_t alloc_unit(size_t size) { return sizeof(ChunkHead) + size; } void *alloc(size_t size) { auto au = alloc_unit(size); if (au >= isolation_threshold) { size = std::max(static_cast(16), size); // We will store the allocated size in size_t field. auto mb = alloc_mem_block(alloc_unit(size)); auto ch = reinterpret_cast(mb->begin); ch->size = size; mb->last = mb->end; return mb->begin + sizeof(ChunkHead); } if (!head || static_cast(head->end - head->last) < au) { head = alloc_mem_block(block_size); } // We will store the allocated size in size_t field. auto res = head->last + sizeof(ChunkHead); auto ch = reinterpret_cast(head->last); ch->size = size; head->last = reinterpret_cast( (reinterpret_cast(res + size) + 0xf) & ~0xf); return res; } // Returns allocated size for memory pointed by |ptr|. We assume // that |ptr| was returned from alloc() or realloc(). size_t get_alloc_length(void *ptr) { return reinterpret_cast(static_cast(ptr) - sizeof(ChunkHead)) ->size; } // Allocates memory of at least |size| bytes. If |ptr| is nullptr, // this is equivalent to alloc(size). If |ptr| is not nullptr, // obtain the allocated size for |ptr|, assuming that |ptr| was // returned from alloc() or realloc(). If the allocated size is // greater than or equal to size, |ptr| is returned. Otherwise, // allocates at least |size| bytes of memory, and the original // content pointed by |ptr| is copied to the newly allocated memory. void *realloc(void *ptr, size_t size) { if (!ptr) { return alloc(size); } auto alloclen = get_alloc_length(ptr); auto p = reinterpret_cast(ptr); if (size <= alloclen) { return ptr; } auto nalloclen = std::max(size + 1, alloclen * 2); auto res = alloc(nalloclen); std::ranges::copy_n(p, as_signed(alloclen), static_cast(res)); return res; } // This holds live memory block to free them in dtor. MemBlock *retain; // Current memory block to use. MemBlock *head; // size of single memory block size_t block_size; // if allocation greater or equal to isolation_threshold bytes is // requested, allocate dedicated block. size_t isolation_threshold; }; // Makes a copy of a range [|first|, |last|). The resulting string // will be NULL-terminated. template std::string_view make_string_ref(BlockAllocator &alloc, I first, I last) { auto dst = static_cast( alloc.alloc(static_cast(std::ranges::distance(first, last) + 1))); auto p = std::ranges::copy(first, last, dst).out; *p = '\0'; return std::string_view{dst, p}; } // Makes a copy of |r| as std::string_view. The resulting string will be // NULL-terminated. template requires(!std::is_array_v>) std::string_view make_string_ref(BlockAllocator &alloc, R &&r) { return make_string_ref(alloc, std::ranges::begin(r), std::ranges::end(r)); } // private function used in concat_string_ref. this is the base // function of concat_string_ref_count(). constexpr size_t concat_string_ref_count(size_t acc) { return acc; } // private function used in concat_string_ref. This function counts // the sum of length of given arguments. The calculated length is // accumulated, and passed to the next function. template requires(!std::is_array_v>) constexpr size_t concat_string_ref_count(size_t acc, R &&r, Args &&...args) { return concat_string_ref_count(acc + std::ranges::size(r), args...); } // private function used in concat_string_ref. this is the base // function of concat_string_ref_copy(). inline uint8_t *concat_string_ref_copy(uint8_t *p) { return p; } // private function used in concat_string_ref. This function copies // given strings into |p|. |p| is incremented by the copied length, // and returned. In the end, return value points to the location one // beyond the last byte written. template requires(!std::is_array_v>) uint8_t *concat_string_ref_copy(uint8_t *p, R &&r, Args &&...args) { return concat_string_ref_copy(std::ranges::copy(std::forward(r), p).out, std::forward(args)...); } // Returns the string which is the concatenation of |args| in the // given order. The resulting string will be NULL-terminated. template std::string_view concat_string_ref(BlockAllocator &alloc, Args &&...args) { auto len = concat_string_ref_count(0, args...); auto dst = static_cast(alloc.alloc(len + 1)); auto p = dst; p = concat_string_ref_copy(p, std::forward(args)...); *p = '\0'; return as_string_view(dst, p); } // Returns the string which is the concatenation of |value| and |args| // in the given order. The resulting string will be NULL-terminated. // This function assumes that the pointer value value.c_str() was // obtained from alloc.alloc() or alloc.realloc(), and attempts to use // unused memory region by using alloc.realloc(). If value is empty, // then just call concat_string_ref(). template std::string_view realloc_concat_string_ref(BlockAllocator &alloc, const std::string_view &value, Args &&...args) { if (value.empty()) { return concat_string_ref(alloc, std::forward(args)...); } auto len = value.size() + concat_string_ref_count(0, args...); auto dst = static_cast(alloc.realloc( const_cast(reinterpret_cast(value.data())), len + 1)); auto p = dst + value.size(); p = concat_string_ref_copy(p, std::forward(args)...); *p = '\0'; return as_string_view(dst, p); } // Makes an uninitialized buffer with given size. inline std::span make_byte_ref(BlockAllocator &alloc, size_t size) { return {static_cast(alloc.alloc(size)), size}; } } // namespace nghttp2 #endif // !defined(ALLOCATOR_H) nghttp2-1.68.0/src/PaxHeaders/HtmlParser.cc0000644000000000000000000000013215077107270015437 xustar0030 mtime=1761382072.983444185 30 atime=1761382106.235310291 30 ctime=1761382109.231300002 nghttp2-1.68.0/src/HtmlParser.cc0000644000175100017510000001554315077107270016037 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "HtmlParser.h" #include #include "util.h" namespace nghttp2 { ParserData::ParserData(const std::string &base_uri) : base_uri(base_uri), inside_head(0) {} HtmlParser::HtmlParser(const std::string &base_uri) : base_uri_(base_uri), parser_ctx_(nullptr), parser_data_(base_uri) {} HtmlParser::~HtmlParser() { htmlFreeParserCtxt(parser_ctx_); } namespace { std::string_view get_attr(const xmlChar **attrs, const std::string_view &name) { if (attrs == nullptr) { return ""sv; } for (; *attrs; attrs += 2) { if (util::strieq(std::string_view{reinterpret_cast(attrs[0])}, name)) { return std::string_view{reinterpret_cast(attrs[1])}; } } return ""sv; } } // namespace namespace { ResourceType get_resource_type_for_preload_as(const std::string_view &attribute_value) { if (util::strieq("image"sv, attribute_value)) { return REQ_IMG; } else if (util::strieq("style"sv, attribute_value)) { return REQ_CSS; } else if (util::strieq("script"sv, attribute_value)) { return REQ_UNBLOCK_JS; } else { return REQ_OTHERS; } } } // namespace namespace { void add_link(ParserData *parser_data, const std::string_view &uri, ResourceType res_type) { auto u = xmlBuildURI( reinterpret_cast(uri.data()), reinterpret_cast(parser_data->base_uri.c_str())); if (u) { parser_data->links.push_back( std::make_pair(reinterpret_cast(u), res_type)); xmlFree(u); } } } // namespace namespace { void start_element_func(void *user_data, const xmlChar *src_name, const xmlChar **attrs) { auto parser_data = static_cast(user_data); auto name = std::string_view{reinterpret_cast(src_name)}; if (util::strieq("head"sv, name)) { ++parser_data->inside_head; } if (util::strieq("link"sv, name)) { auto rel_attr = get_attr(attrs, "rel"sv); auto href_attr = get_attr(attrs, "href"sv); if (rel_attr.empty() || href_attr.empty()) { return; } if (util::strieq("shortcut icon"sv, rel_attr)) { add_link(parser_data, href_attr, REQ_OTHERS); } else if (util::strieq("stylesheet"sv, rel_attr)) { add_link(parser_data, href_attr, REQ_CSS); } else if (util::strieq("preload"sv, rel_attr)) { auto as_attr = get_attr(attrs, "as"sv); if (as_attr.empty()) { return; } add_link(parser_data, href_attr, get_resource_type_for_preload_as(as_attr)); } } else if (util::strieq("img"sv, name)) { auto src_attr = get_attr(attrs, "src"sv); if (src_attr.empty()) { return; } add_link(parser_data, src_attr, REQ_IMG); } else if (util::strieq("script"sv, name)) { auto src_attr = get_attr(attrs, "src"sv); if (src_attr.empty()) { return; } if (parser_data->inside_head) { add_link(parser_data, src_attr, REQ_JS); } else { add_link(parser_data, src_attr, REQ_UNBLOCK_JS); } } } } // namespace namespace { void end_element_func(void *user_data, const xmlChar *name) { auto parser_data = static_cast(user_data); if (util::strieq("head"sv, std::string_view{reinterpret_cast(name)})) { --parser_data->inside_head; } } } // namespace namespace { xmlSAXHandler saxHandler = { nullptr, // internalSubsetSAXFunc nullptr, // isStandaloneSAXFunc nullptr, // hasInternalSubsetSAXFunc nullptr, // hasExternalSubsetSAXFunc nullptr, // resolveEntitySAXFunc nullptr, // getEntitySAXFunc nullptr, // entityDeclSAXFunc nullptr, // notationDeclSAXFunc nullptr, // attributeDeclSAXFunc nullptr, // elementDeclSAXFunc nullptr, // unparsedEntityDeclSAXFunc nullptr, // setDocumentLocatorSAXFunc nullptr, // startDocumentSAXFunc nullptr, // endDocumentSAXFunc &start_element_func, // startElementSAXFunc &end_element_func, // endElementSAXFunc nullptr, // referenceSAXFunc nullptr, // charactersSAXFunc nullptr, // ignorableWhitespaceSAXFunc nullptr, // processingInstructionSAXFunc nullptr, // commentSAXFunc nullptr, // warningSAXFunc nullptr, // errorSAXFunc nullptr, // fatalErrorSAXFunc nullptr, // getParameterEntitySAXFunc nullptr, // cdataBlockSAXFunc nullptr, // externalSubsetSAXFunc 0, // unsigned int initialized nullptr, // void * _private nullptr, // startElementNsSAX2Func nullptr, // endElementNsSAX2Func nullptr, // xmlStructuredErrorFunc }; } // namespace int HtmlParser::parse_chunk(const char *chunk, size_t size, int fin) { if (!parser_ctx_) { parser_ctx_ = htmlCreatePushParserCtxt( &saxHandler, &parser_data_, chunk, static_cast(size), base_uri_.c_str(), XML_CHAR_ENCODING_NONE); if (!parser_ctx_) { return -1; } else { if (fin) { return parse_chunk_internal(nullptr, 0, fin); } else { return 0; } } } else { return parse_chunk_internal(chunk, size, fin); } } int HtmlParser::parse_chunk_internal(const char *chunk, size_t size, int fin) { int rv = htmlParseChunk(parser_ctx_, chunk, static_cast(size), fin); if (rv == 0) { return 0; } else { return -1; } } const std::vector> & HtmlParser::get_links() const { return parser_data_.links; } void HtmlParser::clear_links() { parser_data_.links.clear(); } } // namespace nghttp2 nghttp2-1.68.0/src/PaxHeaders/h2load_http1_session.cc0000644000000000000000000000013215077107270017412 xustar0030 mtime=1761382072.985444176 30 atime=1761382106.218310365 30 ctime=1761382109.214300051 nghttp2-1.68.0/src/h2load_http1_session.cc0000644000175100017510000001676615077107270020022 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 British Broadcasting Corporation * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "h2load_http1_session.h" #include #include #include "h2load.h" #include "util.h" #include "template.h" #include #include using namespace nghttp2; namespace h2load { namespace { // HTTP response message begin int htp_msg_begincb(llhttp_t *htp) { auto session = static_cast(htp->data); if (session->stream_resp_counter_ > session->stream_req_counter_) { return -1; } return 0; } } // namespace namespace { // HTTP response status code int htp_statuscb(llhttp_t *htp, const char *at, size_t length) { auto session = static_cast(htp->data); auto client = session->get_client(); if (htp->status_code / 100 == 1) { return 0; } client->on_status_code(session->stream_resp_counter_, htp->status_code); return 0; } } // namespace namespace { // HTTP response message complete int htp_msg_completecb(llhttp_t *htp) { auto session = static_cast(htp->data); auto client = session->get_client(); if (htp->status_code / 100 == 1) { return 0; } client->final = llhttp_should_keep_alive(htp) == 0; auto req_stat = client->get_req_stat(session->stream_resp_counter_); assert(req_stat); auto config = client->worker->config; if (req_stat->data_offset >= config->data_length) { client->on_stream_close(session->stream_resp_counter_, true, client->final); } session->stream_resp_counter_ += 2; if (client->final) { session->stream_req_counter_ = session->stream_resp_counter_; // Connection is going down. If we have still request to do, // create new connection and keep on doing the job. if (client->req_left) { client->try_new_connection(); } return HPE_PAUSED; } return 0; } } // namespace namespace { int htp_hdr_keycb(llhttp_t *htp, const char *data, size_t len) { auto session = static_cast(htp->data); auto client = session->get_client(); client->worker->stats.bytes_head += len; client->worker->stats.bytes_head_decomp += len; return 0; } } // namespace namespace { int htp_hdr_valcb(llhttp_t *htp, const char *data, size_t len) { auto session = static_cast(htp->data); auto client = session->get_client(); client->worker->stats.bytes_head += len; client->worker->stats.bytes_head_decomp += len; return 0; } } // namespace namespace { int htp_hdrs_completecb(llhttp_t *htp) { return !http2::expect_response_body(htp->status_code); } } // namespace namespace { int htp_body_cb(llhttp_t *htp, const char *data, size_t len) { auto session = static_cast(htp->data); auto client = session->get_client(); client->record_ttfb(); client->worker->stats.bytes_body += len; return 0; } } // namespace namespace { constexpr llhttp_settings_t htp_hooks = { .on_message_begin = htp_msg_begincb, .on_status = htp_statuscb, .on_header_field = htp_hdr_keycb, .on_header_value = htp_hdr_valcb, .on_headers_complete = htp_hdrs_completecb, .on_body = htp_body_cb, .on_message_complete = htp_msg_completecb, }; } // namespace Http1Session::Http1Session(Client *client) : stream_req_counter_(1), stream_resp_counter_(1), client_(client), htp_(), complete_(false) { llhttp_init(&htp_, HTTP_RESPONSE, &htp_hooks); htp_.data = this; } Http1Session::~Http1Session() {} void Http1Session::on_connect() { client_->signal_write(); } int Http1Session::submit_request() { auto config = client_->worker->config; const auto &req = config->h1reqs[client_->reqidx]; client_->reqidx++; if (client_->reqidx == config->h1reqs.size()) { client_->reqidx = 0; } client_->on_request(stream_req_counter_); auto req_stat = client_->get_req_stat(stream_req_counter_); client_->record_request_time(req_stat); client_->wb.append(req); if (config->data_fd == -1 || config->data_length == 0) { // increment for next request stream_req_counter_ += 2; return 0; } return on_write(); } int Http1Session::on_read(const uint8_t *data, size_t len) { auto htperr = llhttp_execute(&htp_, reinterpret_cast(data), len); auto nread = htperr == HPE_OK ? len : static_cast(reinterpret_cast( llhttp_get_error_pos(&htp_)) - data); if (client_->worker->config->verbose) { std::cout.write(reinterpret_cast(data), static_cast(nread)); } if (htperr == HPE_PAUSED) { // pause is done only when connection: close is requested return -1; } if (htperr != HPE_OK) { std::cerr << "[ERROR] HTTP parse error: " << "(" << llhttp_errno_name(htperr) << ") " << llhttp_get_error_reason(&htp_) << std::endl; return -1; } return 0; } int Http1Session::on_write() { if (complete_) { return -1; } auto config = client_->worker->config; auto req_stat = client_->get_req_stat(stream_req_counter_); if (!req_stat) { return 0; } if (req_stat->data_offset < config->data_length) { auto req_stat = client_->get_req_stat(stream_req_counter_); auto &wb = client_->wb; // TODO unfortunately, wb has no interface to use with read(2) // family functions. std::array buf; ssize_t nread; while ((nread = pread(config->data_fd, buf.data(), buf.size(), req_stat->data_offset)) == -1 && errno == EINTR) ; if (nread == -1) { return -1; } req_stat->data_offset += nread; wb.append(buf.data(), as_unsigned(nread)); if (client_->worker->config->verbose) { std::cout << "[send " << nread << " byte(s)]" << std::endl; } if (req_stat->data_offset == config->data_length) { // increment for next request stream_req_counter_ += 2; if (stream_resp_counter_ == stream_req_counter_) { // Response has already been received client_->on_stream_close(stream_resp_counter_ - 2, true, client_->final); } } } return 0; } void Http1Session::terminate() { complete_ = true; } Client *Http1Session::get_client() { return client_; } size_t Http1Session::max_concurrent_streams() { auto config = client_->worker->config; return config->data_fd == -1 ? config->max_concurrent_streams : 1; } } // namespace h2load nghttp2-1.68.0/src/PaxHeaders/h2load.cc0000644000000000000000000000013215077107270014527 xustar0030 mtime=1761382072.985444176 30 atime=1761382106.211310396 30 ctime=1761382109.208300069 nghttp2-1.68.0/src/h2load.cc0000644000175100017510000030677115077107270015135 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "h2load.h" #include #include #ifdef HAVE_NETINET_IN_H # include #endif // defined(HAVE_NETINET_IN_H) #include #include #ifdef HAVE_FCNTL_H # include #endif // defined(HAVE_FCNTL_H) #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #ifdef ENABLE_HTTP3 # if defined(HAVE_LIBNGTCP2_CRYPTO_QUICTLS) || \ defined(HAVE_LIBNGTCP2_CRYPTO_LIBRESSL) # include # endif // defined(HAVE_LIBNGTCP2_CRYPTO_QUICTLS) || // defined(HAVE_LIBNGTCP2_CRYPTO_LIBRESSL) # ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL # include # endif // defined(HAVE_LIBNGTCP2_CRYPTO_BORINGSSL) # ifdef HAVE_LIBNGTCP2_CRYPTO_WOLFSSL # include # endif // defined(HAVE_LIBNGTCP2_CRYPTO_WOLFSSL) # ifdef HAVE_LIBNGTCP2_CRYPTO_OSSL # include # endif // defined(HAVE_LIBNGTCP2_CRYPTO_OSSL) #endif // defined(ENABLE_HTTP3) #include "urlparse.h" #include "h2load_http1_session.h" #include "h2load_http2_session.h" #ifdef ENABLE_HTTP3 # include "h2load_http3_session.h" # include "h2load_quic.h" #endif // defined(ENABLE_HTTP3) #include "tls.h" #include "http2.h" #include "util.h" #include "template.h" #ifndef O_BINARY # define O_BINARY (0) #endif // !defined(O_BINARY) using namespace nghttp2; namespace h2load { namespace { bool recorded(const std::chrono::steady_clock::time_point &t) { return std::chrono::steady_clock::duration::zero() != t.time_since_epoch(); } } // namespace Config::Config() : ciphers(tls::DEFAULT_CIPHER_LIST), tls13_ciphers("TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_" "CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256"), groups("X25519:P-256:P-384:P-521"), data_length(-1), data(nullptr), addrs(nullptr), nreqs(1), nclients(1), nthreads(1), max_concurrent_streams(1), window_bits(30), connection_window_bits(30), max_frame_size(16_k), rate(0), rate_period(1.0), duration(0.0), warm_up_time(0.0), conn_active_timeout(0.), conn_inactivity_timeout(0.), no_tls_proto(PROTO_HTTP2), header_table_size(4_k), encoder_header_table_size(4_k), data_fd(-1), log_fd(-1), qlog_file_base(), port(0), default_port(0), connect_to_port(0), verbose(false), timing_script(false), base_uri_unix(false), unix_addr{}, rps(0.), no_udp_gso(false), max_udp_payload_size(0), ktls(false) {} Config::~Config() { if (addrs) { if (base_uri_unix) { delete addrs; } else { freeaddrinfo(addrs); } } if (data_fd != -1) { close(data_fd); } } bool Config::is_rate_mode() const { return (this->rate != 0); } bool Config::is_timing_based_mode() const { return (this->duration > 0); } bool Config::has_base_uri() const { return (!this->base_uri.empty()); } bool Config::rps_enabled() const { return this->rps > 0.0; } bool Config::is_quic() const { #ifdef ENABLE_HTTP3 return !alpn_list.empty() && (alpn_list[0] == NGHTTP3_ALPN_H3 || alpn_list[0] == "\x5h3-29"); #else // !defined(ENABLE_HTTP3) return false; #endif // !defined(ENABLE_HTTP3) } Config config; namespace { constexpr size_t MAX_SAMPLES = 1000000; } // namespace Stats::Stats(size_t req_todo, size_t nclients) : req_todo(req_todo), req_started(0), req_done(0), req_success(0), req_status_success(0), req_failed(0), req_error(0), req_timedout(0), bytes_total(0), bytes_head(0), bytes_head_decomp(0), bytes_body(0), status(), udp_dgram_recv(0), udp_dgram_sent(0) {} Stream::Stream() : req_stat{}, status_success(-1) {} namespace { std::random_device rd; } // namespace namespace { std::mt19937 gen(rd()); } // namespace namespace { void sampling_init(Sampling &smp, size_t max_samples) { smp.n = 0; smp.max_samples = max_samples; } } // namespace namespace { void writecb(struct ev_loop *loop, ev_io *w, int revents) { auto client = static_cast(w->data); client->restart_timeout(); auto rv = client->do_write(); if (rv == Client::ERR_CONNECT_FAIL) { client->disconnect(); // Try next address client->current_addr = nullptr; rv = client->connect(); if (rv != 0) { client->fail(); client->worker->free_client(client); delete client; return; } return; } if (rv != 0) { client->fail(); client->worker->free_client(client); delete client; } } } // namespace namespace { void readcb(struct ev_loop *loop, ev_io *w, int revents) { auto client = static_cast(w->data); client->restart_timeout(); if (client->do_read() != 0) { if (client->try_again_or_fail() == 0) { return; } client->worker->free_client(client); delete client; return; } client->signal_write(); } } // namespace namespace { // Called every rate_period when rate mode is being used void rate_period_timeout_w_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto worker = static_cast(w->data); auto nclients_per_second = worker->rate; auto conns_remaining = worker->nclients - worker->nconns_made; auto nclients = std::min(nclients_per_second, conns_remaining); for (size_t i = 0; i < nclients; ++i) { auto req_todo = worker->nreqs_per_client; if (worker->nreqs_rem > 0) { ++req_todo; --worker->nreqs_rem; } auto client = std::make_unique(worker->next_client_id++, worker, req_todo); ++worker->nconns_made; if (client->connect() != 0) { std::cerr << "client could not connect to host" << std::endl; client->fail(); } else { if (worker->config->is_timing_based_mode()) { worker->clients.push_back(client.release()); } else { client.release(); } } worker->report_rate_progress(); } if (!worker->config->is_timing_based_mode()) { if (worker->nconns_made >= worker->nclients) { ev_timer_stop(worker->loop, w); } } else { // To check whether all created clients are pushed correctly assert(worker->nclients == worker->clients.size()); } } } // namespace namespace { // Called when the duration for infinite number of requests are over void duration_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto worker = static_cast(w->data); worker->current_phase = Phase::DURATION_OVER; std::cout << "Main benchmark duration is over for thread #" << worker->id << ". Stopping all clients." << std::endl; worker->stop_all_clients(); std::cout << "Stopped all clients for thread #" << worker->id << std::endl; } } // namespace namespace { // Called when the warmup duration for infinite number of requests are over void warmup_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto worker = static_cast(w->data); std::cout << "Warm-up phase is over for thread #" << worker->id << "." << std::endl; std::cout << "Main benchmark duration is started for thread #" << worker->id << "." << std::endl; assert(worker->stats.req_started == 0); assert(worker->stats.req_done == 0); for (auto client : worker->clients) { if (client) { assert(client->req_todo == 0); assert(client->req_left == 1); assert(client->req_inflight == 0); assert(client->req_started == 0); assert(client->req_done == 0); client->record_client_start_time(); client->clear_connect_times(); client->record_connect_start_time(); } } worker->current_phase = Phase::MAIN_DURATION; ev_timer_start(worker->loop, &worker->duration_watcher); } } // namespace namespace { void rps_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto client = static_cast(w->data); auto &session = client->session; assert(!config.timing_script); if (client->req_left == 0) { ev_timer_stop(loop, w); return; } auto now = std::chrono::steady_clock::now(); auto d = now - client->rps_duration_started; auto n = static_cast( round(std::chrono::duration(d).count() * config.rps)); client->rps_req_pending += n; client->rps_duration_started += util::duration_from(static_cast(n) / config.rps); if (client->rps_req_pending == 0) { return; } auto nreq = session->max_concurrent_streams() - client->rps_req_inflight; if (nreq == 0) { return; } nreq = config.is_timing_based_mode() ? std::max(nreq, client->req_left) : std::min(nreq, client->req_left); nreq = std::min(nreq, client->rps_req_pending); client->rps_req_inflight += nreq; client->rps_req_pending -= nreq; for (; nreq > 0; --nreq) { if (client->submit_request() != 0) { client->process_request_failure(); break; } } client->signal_write(); } } // namespace namespace { // Called when an a connection has been inactive for a set period of time // or a fixed amount of time after all requests have been made on a // connection void conn_timeout_cb(EV_P_ ev_timer *w, int revents) { auto client = static_cast(w->data); ev_timer_stop(client->worker->loop, &client->conn_inactivity_watcher); ev_timer_stop(client->worker->loop, &client->conn_active_watcher); if (util::check_socket_connected(client->fd)) { client->timeout(); } } } // namespace namespace { bool check_stop_client_request_timeout(Client *client, ev_timer *w) { if (client->req_left == 0) { // no more requests to make, stop timer ev_timer_stop(client->worker->loop, w); return true; } return false; } } // namespace namespace { void client_request_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto client = static_cast(w->data); if (client->streams.size() >= config.max_concurrent_streams) { ev_timer_stop(client->worker->loop, w); return; } if (client->submit_request() != 0) { ev_timer_stop(client->worker->loop, w); client->process_request_failure(); return; } client->signal_write(); if (check_stop_client_request_timeout(client, w)) { return; } auto duration = config.timings[client->reqidx] - config.timings[client->reqidx - 1]; while (duration < std::chrono::duration(1e-9)) { if (client->submit_request() != 0) { ev_timer_stop(client->worker->loop, w); client->process_request_failure(); return; } client->signal_write(); if (check_stop_client_request_timeout(client, w)) { return; } duration = config.timings[client->reqidx] - config.timings[client->reqidx - 1]; } client->request_timeout_watcher.repeat = util::ev_tstamp_from(duration); ev_timer_again(client->worker->loop, &client->request_timeout_watcher); } } // namespace Client::Client(uint32_t id, Worker *worker, size_t req_todo) : wb(&worker->mcpool), cstat{}, worker(worker), ssl(nullptr), #ifdef ENABLE_HTTP3 quic{}, #endif // defined(ENABLE_HTTP3) next_addr(config.addrs), current_addr(nullptr), reqidx(0), state(CLIENT_IDLE), req_todo(req_todo), req_left(req_todo), req_inflight(0), req_started(0), req_done(0), id(id), fd(-1), local_addr{}, new_connection_requested(false), final(false), rps_req_pending(0), rps_req_inflight(0) { if (req_todo == 0) { // this means infinite number of requests are to be made // This ensures that number of requests are unbounded // Just a positive number is fine, we chose the first positive number req_left = 1; } ev_io_init(&wev, writecb, 0, EV_WRITE); ev_io_init(&rev, readcb, 0, EV_READ); wev.data = this; rev.data = this; ev_timer_init(&conn_inactivity_watcher, conn_timeout_cb, 0., worker->config->conn_inactivity_timeout); conn_inactivity_watcher.data = this; ev_timer_init(&conn_active_watcher, conn_timeout_cb, worker->config->conn_active_timeout, 0.); conn_active_watcher.data = this; ev_timer_init(&request_timeout_watcher, client_request_timeout_cb, 0., 0.); request_timeout_watcher.data = this; ev_timer_init(&rps_watcher, rps_cb, 0., 0.); rps_watcher.data = this; #ifdef ENABLE_HTTP3 ev_timer_init(&quic.pkt_timer, quic_pkt_timeout_cb, 0., 0.); quic.pkt_timer.data = this; # ifndef UDP_SEGMENT quic.tx.no_gso = true; # endif // !defined(UDP_SEGMENT) if (config.is_quic()) { ev_set_priority(&rev, EV_MAXPRI); quic.tx.data = std::make_unique(QUIC_TX_DATALEN); } ngtcp2_ccerr_default(&quic.last_error); #endif // defined(ENABLE_HTTP3) } Client::~Client() { disconnect(); // Free ssl before freeing QUIC resources because // libngtcp2_crypto_ossl requires that ngtcp2_conn is still alive. if (ssl) { SSL_free(ssl); } #ifdef ENABLE_HTTP3 if (config.is_quic()) { quic_free(); } #endif // defined(ENABLE_HTTP3) worker->sample_client_stat(&cstat); ++worker->client_smp.n; } int Client::do_read() { return readfn(*this); } int Client::do_write() { return writefn(*this); } int Client::make_socket(addrinfo *addr) { int rv; if (config.is_quic()) { #ifdef ENABLE_HTTP3 fd = util::create_nonblock_udp_socket(addr->ai_family); if (fd == -1) { return -1; } # ifdef UDP_GRO int val = 1; if (setsockopt(fd, IPPROTO_UDP, UDP_GRO, &val, sizeof(val)) != 0) { std::cerr << "setsockopt UDP_GRO failed" << std::endl; return -1; } # endif // defined(UDP_GRO) rv = util::bind_any_addr_udp(fd, addr->ai_family); if (rv != 0) { close(fd); fd = -1; return -1; } socklen_t addrlen = sizeof(local_addr.su.storage); rv = getsockname(fd, &local_addr.su.sa, &addrlen); if (rv == -1) { return -1; } local_addr.len = addrlen; if (quic_init(&local_addr.su.sa, local_addr.len, addr->ai_addr, addr->ai_addrlen) != 0) { std::cerr << "quic_init failed" << std::endl; return -1; } #endif // defined(ENABLE_HTTP3) } else { fd = util::create_nonblock_socket(addr->ai_family); if (fd == -1) { return -1; } if (config.scheme == "https") { if (!ssl) { ssl = SSL_new(worker->ssl_ctx); } SSL_set_connect_state(ssl); } } if (ssl) { if (!config.sni.empty()) { SSL_set_tlsext_host_name(ssl, config.sni.c_str()); } else if (!util::numeric_host(config.host.c_str())) { SSL_set_tlsext_host_name(ssl, config.host.c_str()); } } if (config.is_quic()) { return 0; } rv = ::connect(fd, addr->ai_addr, addr->ai_addrlen); if (rv != 0 && errno != EINPROGRESS) { if (ssl) { SSL_free(ssl); ssl = nullptr; } close(fd); fd = -1; return -1; } return 0; } int Client::connect() { int rv; if (!worker->config->is_timing_based_mode() || worker->current_phase == Phase::MAIN_DURATION) { record_client_start_time(); clear_connect_times(); record_connect_start_time(); } else if (worker->current_phase == Phase::INITIAL_IDLE) { worker->current_phase = Phase::WARM_UP; std::cout << "Warm-up started for thread #" << worker->id << "." << std::endl; ev_timer_start(worker->loop, &worker->warmup_watcher); } if (worker->config->conn_inactivity_timeout > 0.) { ev_timer_again(worker->loop, &conn_inactivity_watcher); } if (current_addr) { rv = make_socket(current_addr); if (rv == -1) { return -1; } } else { addrinfo *addr = nullptr; while (next_addr) { addr = next_addr; next_addr = next_addr->ai_next; rv = make_socket(addr); if (rv == 0) { break; } } if (fd == -1) { return -1; } assert(addr); current_addr = addr; } ev_io_set(&rev, fd, EV_READ); ev_io_set(&wev, fd, EV_WRITE); ev_io_start(worker->loop, &wev); if (config.is_quic()) { #ifdef ENABLE_HTTP3 ev_io_start(worker->loop, &rev); readfn = &Client::read_quic; writefn = &Client::write_quic; #endif // defined(ENABLE_HTTP3) } else { writefn = &Client::connected; } return 0; } void Client::timeout() { process_timedout_streams(); disconnect(); } void Client::restart_timeout() { if (worker->config->conn_inactivity_timeout > 0.) { ev_timer_again(worker->loop, &conn_inactivity_watcher); } } int Client::try_again_or_fail() { disconnect(); if (new_connection_requested) { new_connection_requested = false; if (req_left) { if (worker->current_phase == Phase::MAIN_DURATION) { // At the moment, we don't have a facility to re-start request // already in in-flight. Make them fail. worker->stats.req_failed += req_inflight; worker->stats.req_error += req_inflight; req_inflight = 0; } else if (worker->current_phase == Phase::DURATION_OVER) { // fix a race condition when h2load is sending connection: close over h1 // prevents new clients from spawning after the test should have ended. return -1; } // Keep using current address if (connect() == 0) { return 0; } std::cerr << "client could not connect to host" << std::endl; } } process_abandoned_streams(); return -1; } void Client::fail() { disconnect(); process_abandoned_streams(); } void Client::disconnect() { record_client_end_time(); #ifdef ENABLE_HTTP3 if (config.is_quic()) { quic_close_connection(); } #endif // defined(ENABLE_HTTP3) #ifdef ENABLE_HTTP3 ev_timer_stop(worker->loop, &quic.pkt_timer); #endif // defined(ENABLE_HTTP3) ev_timer_stop(worker->loop, &conn_inactivity_watcher); ev_timer_stop(worker->loop, &conn_active_watcher); ev_timer_stop(worker->loop, &rps_watcher); ev_timer_stop(worker->loop, &request_timeout_watcher); streams.clear(); session.reset(); wb.reset(); state = CLIENT_IDLE; ev_io_stop(worker->loop, &wev); ev_io_stop(worker->loop, &rev); if (ssl) { if (config.is_quic()) { SSL_free(ssl); ssl = nullptr; } else { SSL_set_shutdown(ssl, SSL_get_shutdown(ssl) | SSL_RECEIVED_SHUTDOWN); ERR_clear_error(); if (SSL_shutdown(ssl) != 1) { SSL_free(ssl); ssl = nullptr; } } } if (fd != -1) { shutdown(fd, SHUT_WR); close(fd); fd = -1; } final = false; } int Client::submit_request() { if (session->submit_request() != 0) { return -1; } if (worker->current_phase != Phase::MAIN_DURATION) { return 0; } ++worker->stats.req_started; ++req_started; ++req_inflight; if (!worker->config->is_timing_based_mode()) { --req_left; } // if an active timeout is set and this is the last request to be submitted // on this connection, start the active timeout. if (worker->config->conn_active_timeout > 0. && req_left == 0) { ev_timer_start(worker->loop, &conn_active_watcher); } return 0; } void Client::process_timedout_streams() { if (worker->current_phase != Phase::MAIN_DURATION) { return; } for (auto &p : streams) { auto &req_stat = p.second.req_stat; if (!req_stat.completed) { req_stat.stream_close_time = std::chrono::steady_clock::now(); } } worker->stats.req_timedout += req_inflight; process_abandoned_streams(); } void Client::process_abandoned_streams() { if (worker->current_phase != Phase::MAIN_DURATION) { return; } auto req_abandoned = req_inflight + req_left; worker->stats.req_failed += req_abandoned; worker->stats.req_error += req_abandoned; req_inflight = 0; req_left = 0; } void Client::process_request_failure() { if (worker->current_phase != Phase::MAIN_DURATION) { return; } worker->stats.req_failed += req_left; worker->stats.req_error += req_left; req_left = 0; if (req_inflight == 0) { terminate_session(); } std::cout << "Process Request Failure:" << worker->stats.req_failed << std::endl; } #ifndef NGHTTP2_OPENSSL_IS_BORINGSSL namespace { void print_server_tmp_key(SSL *ssl) { EVP_PKEY *key; if (!SSL_get_server_tmp_key(ssl, &key)) { return; } auto key_del = defer(EVP_PKEY_free, key); std::cout << "Server Temp Key: "; auto pkey_id = EVP_PKEY_id(key); switch (pkey_id) { case EVP_PKEY_RSA: std::cout << "RSA " << EVP_PKEY_bits(key) << " bits" << std::endl; break; case EVP_PKEY_DH: std::cout << "DH " << EVP_PKEY_bits(key) << " bits" << std::endl; break; case EVP_PKEY_EC: { # if OPENSSL_3_0_0_API std::array curve_name; const char *cname; if (!EVP_PKEY_get_utf8_string_param(key, "group", curve_name.data(), curve_name.size(), nullptr)) { cname = ""; } else { cname = curve_name.data(); } # else // !OPENSSL_3_0_0_API auto ec = EVP_PKEY_get1_EC_KEY(key); auto ec_del = defer(EC_KEY_free, ec); auto nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); auto cname = EC_curve_nid2nist(nid); if (!cname) { cname = OBJ_nid2sn(nid); if (!cname) { cname = ""; } } # endif // !OPENSSL_3_0_0_API std::cout << "ECDH " << cname << " " << EVP_PKEY_bits(key) << " bits" << std::endl; break; } default: std::cout << OBJ_nid2sn(pkey_id) << " " << EVP_PKEY_bits(key) << " bits" << std::endl; break; } } } // namespace #endif // !defined(NGHTTP2_OPENSSL_IS_BORINGSSL) void Client::report_tls_info() { if (worker->id == 0 && !worker->tls_info_report_done) { worker->tls_info_report_done = true; auto cipher = SSL_get_current_cipher(ssl); std::cout << "TLS Protocol: " << tls::get_tls_protocol(ssl) << "\n" << "Cipher: " << SSL_CIPHER_get_name(cipher) << std::endl; #ifndef NGHTTP2_OPENSSL_IS_BORINGSSL print_server_tmp_key(ssl); #endif // !defined(NGHTTP2_OPENSSL_IS_BORINGSSL) } } void Client::report_app_info() { if (worker->id == 0 && !worker->app_info_report_done) { worker->app_info_report_done = true; std::cout << "Application protocol: " << selected_proto << std::endl; } } void Client::terminate_session() { #ifdef ENABLE_HTTP3 if (config.is_quic()) { quic.close_requested = true; } #endif // defined(ENABLE_HTTP3) if (session) { session->terminate(); } // http1 session needs writecb to tear down session. signal_write(); } void Client::on_request(int64_t stream_id) { streams[stream_id] = Stream(); } void Client::on_header(int64_t stream_id, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen) { auto itr = streams.find(stream_id); if (itr == std::ranges::end(streams)) { return; } auto &stream = (*itr).second; if (worker->current_phase != Phase::MAIN_DURATION) { // If the stream is for warm-up phase, then mark as a success // But we do not update the count for 2xx, 3xx, etc status codes // Same has been done in on_status_code function stream.status_success = 1; return; } if (stream.status_success == -1 && namelen == 7 && ":status"sv == as_string_view(name, namelen)) { int status = 0; for (auto c : std::span{value, valuelen}) { if (util::is_digit(as_signed(c))) { status *= 10; status += c - '0'; if (status > 999) { stream.status_success = 0; return; } } else { break; } } if (status < 200) { return; } stream.req_stat.status = status; if (status >= 200 && status < 300) { ++worker->stats.status[2]; stream.status_success = 1; } else if (status < 400) { ++worker->stats.status[3]; stream.status_success = 1; } else if (status < 600) { ++worker->stats.status[static_cast(status / 100)]; stream.status_success = 0; } else { stream.status_success = 0; } } } void Client::on_status_code(int64_t stream_id, uint16_t status) { auto itr = streams.find(stream_id); if (itr == std::ranges::end(streams)) { return; } auto &stream = (*itr).second; if (worker->current_phase != Phase::MAIN_DURATION) { stream.status_success = 1; return; } stream.req_stat.status = status; if (status >= 200 && status < 300) { ++worker->stats.status[2]; stream.status_success = 1; } else if (status < 400) { ++worker->stats.status[3]; stream.status_success = 1; } else if (status < 600) { ++worker->stats.status[status / 100]; stream.status_success = 0; } else { stream.status_success = 0; } } void Client::on_stream_close(int64_t stream_id, bool success, bool final) { if (worker->current_phase == Phase::MAIN_DURATION) { if (req_inflight > 0) { --req_inflight; } auto req_stat = get_req_stat(stream_id); if (!req_stat) { return; } req_stat->stream_close_time = std::chrono::steady_clock::now(); if (success) { req_stat->completed = true; ++worker->stats.req_success; ++cstat.req_success; if (streams[stream_id].status_success == 1) { ++worker->stats.req_status_success; } else { ++worker->stats.req_failed; } worker->sample_req_stat(req_stat); // Count up in successful cases only ++worker->request_times_smp.n; } else { ++worker->stats.req_failed; ++worker->stats.req_error; } ++worker->stats.req_done; ++req_done; if (worker->config->log_fd != -1) { auto start = std::chrono::duration_cast( req_stat->request_wall_time.time_since_epoch()); auto delta = std::chrono::duration_cast( req_stat->stream_close_time - req_stat->request_time); std::array buf; auto p = std::ranges::begin(buf); p = util::utos(as_unsigned(start.count()), p); *p++ = '\t'; if (success) { p = util::utos(as_unsigned(req_stat->status), p); } else { *p++ = '-'; *p++ = '1'; } *p++ = '\t'; p = util::utos(as_unsigned(delta.count()), p); *p++ = '\n'; auto nwrite = static_cast(std::ranges::distance(std::ranges::begin(buf), p)); assert(nwrite <= buf.size()); while (write(worker->config->log_fd, buf.data(), nwrite) == -1 && errno == EINTR) ; } } worker->report_progress(); streams.erase(stream_id); if (req_left == 0 && req_inflight == 0) { terminate_session(); return; } if (!final && req_left > 0) { if (config.timing_script) { if (!ev_is_active(&request_timeout_watcher)) { ev_feed_event(worker->loop, &request_timeout_watcher, EV_TIMER); } } else if (!config.rps_enabled()) { if (submit_request() != 0) { process_request_failure(); } } else if (rps_req_pending) { --rps_req_pending; if (submit_request() != 0) { process_request_failure(); } } else { assert(rps_req_inflight); --rps_req_inflight; } } } RequestStat *Client::get_req_stat(int64_t stream_id) { auto it = streams.find(stream_id); if (it == std::ranges::end(streams)) { return nullptr; } return &(*it).second.req_stat; } int Client::connection_made() { if (ssl) { report_tls_info(); const unsigned char *next_proto = nullptr; unsigned int next_proto_len; SSL_get0_alpn_selected(ssl, &next_proto, &next_proto_len); if (next_proto) { auto proto = as_string_view(next_proto, next_proto_len); if (config.is_quic()) { #ifdef ENABLE_HTTP3 assert(session); if ("h3"sv != proto && "h3-29"sv != proto) { return -1; } #endif // defined(ENABLE_HTTP3) } else if (util::check_h2_is_selected(proto)) { session = std::make_unique(this); } else if (NGHTTP2_H1_1 == proto) { session = std::make_unique(this); } // Just assign next_proto to selected_proto anyway to show the // negotiation result. selected_proto = proto; } else if (config.is_quic()) { std::cerr << "QUIC requires ALPN negotiation" << std::endl; return -1; } else { std::cout << "No protocol negotiated. Fallback behaviour may be activated" << std::endl; for (const auto &proto : config.alpn_list) { if (NGHTTP2_H1_1_ALPN == proto) { std::cout << "Server does not support ALPN. Falling back to HTTP/1.1." << std::endl; session = std::make_unique(this); selected_proto = NGHTTP2_H1_1; break; } } } if (!selected_proto.empty()) { report_app_info(); } if (!session) { std::cout << "No supported protocol was negotiated. Supported protocols were:" << std::endl; for (const auto &proto : config.alpn_list) { std::cout << proto.substr(1) << std::endl; } disconnect(); return -1; } } else { switch (config.no_tls_proto) { case Config::PROTO_HTTP2: session = std::make_unique(this); selected_proto = NGHTTP2_CLEARTEXT_PROTO_VERSION_ID; break; case Config::PROTO_HTTP1_1: session = std::make_unique(this); selected_proto = NGHTTP2_H1_1; break; default: // unreachable assert(0); } report_app_info(); } state = CLIENT_CONNECTED; session->on_connect(); record_connect_time(); if (config.rps_enabled()) { rps_watcher.repeat = std::max(0.01, 1. / config.rps); ev_timer_again(worker->loop, &rps_watcher); rps_duration_started = std::chrono::steady_clock::now(); } if (config.rps_enabled()) { assert(req_left); ++rps_req_inflight; if (submit_request() != 0) { process_request_failure(); } } else if (!config.timing_script) { auto nreq = config.is_timing_based_mode() ? std::max(req_left, session->max_concurrent_streams()) : std::min(req_left, session->max_concurrent_streams()); for (; nreq > 0; --nreq) { if (submit_request() != 0) { process_request_failure(); break; } } } else { auto duration = config.timings[reqidx]; while (duration < std::chrono::duration(1e-9)) { if (submit_request() != 0) { process_request_failure(); break; } duration = config.timings[reqidx]; if (reqidx == 0) { // if reqidx wraps around back to 0, we uses up all lines and // should break break; } } if (duration >= std::chrono::duration(1e-9)) { // double check since we may have break due to reqidx wraps // around back to 0 request_timeout_watcher.repeat = util::ev_tstamp_from(duration); ev_timer_again(worker->loop, &request_timeout_watcher); } } signal_write(); return 0; } int Client::on_read(const uint8_t *data, size_t len) { auto rv = session->on_read(data, len); if (worker->current_phase == Phase::MAIN_DURATION) { worker->stats.bytes_total += len; } if (rv != 0) { return -1; } signal_write(); return 0; } int Client::on_write() { if (wb.rleft() >= BACKOFF_WRITE_BUFFER_THRES) { return 0; } if (session->on_write() != 0) { return -1; } return 0; } int Client::read_clear() { uint8_t buf[8_k]; for (;;) { ssize_t nread; while ((nread = read(fd, buf, sizeof(buf))) == -1 && errno == EINTR) ; if (nread == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return 0; } return -1; } if (nread == 0) { return -1; } if (on_read(buf, as_unsigned(nread)) != 0) { return -1; } } return 0; } int Client::write_clear() { std::array iov; for (;;) { if (on_write() != 0) { return -1; } auto iovcnt = wb.riovec(iov.data(), iov.size()); if (iovcnt == 0) { break; } ssize_t nwrite; while ((nwrite = writev(fd, iov.data(), iovcnt)) == -1 && errno == EINTR) ; if (nwrite == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { ev_io_start(worker->loop, &wev); return 0; } return -1; } wb.drain(as_unsigned(nwrite)); } ev_io_stop(worker->loop, &wev); return 0; } int Client::connected() { if (!util::check_socket_connected(fd)) { return ERR_CONNECT_FAIL; } ev_io_start(worker->loop, &rev); ev_io_stop(worker->loop, &wev); if (ssl) { SSL_set_fd(ssl, fd); readfn = &Client::tls_handshake; writefn = &Client::tls_handshake; return do_write(); } readfn = &Client::read_clear; writefn = &Client::write_clear; if (connection_made() != 0) { return -1; } return 0; } int Client::tls_handshake() { ERR_clear_error(); auto rv = SSL_do_handshake(ssl); if (rv <= 0) { auto err = SSL_get_error(ssl, rv); switch (err) { case SSL_ERROR_WANT_READ: ev_io_stop(worker->loop, &wev); return 0; case SSL_ERROR_WANT_WRITE: ev_io_start(worker->loop, &wev); return 0; default: return -1; } } ev_io_stop(worker->loop, &wev); readfn = &Client::read_tls; writefn = &Client::write_tls; if (connection_made() != 0) { return -1; } return 0; } int Client::read_tls() { uint8_t buf[8_k]; ERR_clear_error(); for (;;) { auto rv = SSL_read(ssl, buf, sizeof(buf)); if (rv <= 0) { auto err = SSL_get_error(ssl, rv); switch (err) { case SSL_ERROR_WANT_READ: return 0; case SSL_ERROR_WANT_WRITE: // renegotiation started return -1; default: return -1; } } if (on_read(buf, static_cast(rv)) != 0) { return -1; } } } int Client::write_tls() { ERR_clear_error(); struct iovec iov; for (;;) { if (on_write() != 0) { return -1; } auto iovcnt = wb.riovec(&iov, 1); if (iovcnt == 0) { break; } auto rv = SSL_write(ssl, iov.iov_base, static_cast(iov.iov_len)); if (rv <= 0) { auto err = SSL_get_error(ssl, rv); switch (err) { case SSL_ERROR_WANT_READ: // renegotiation started return -1; case SSL_ERROR_WANT_WRITE: ev_io_start(worker->loop, &wev); return 0; default: return -1; } } wb.drain(static_cast(rv)); } ev_io_stop(worker->loop, &wev); return 0; } #ifdef ENABLE_HTTP3 // Returns remaining bytes if sendmsg is blocked. std::span Client::write_udp(const sockaddr *addr, socklen_t addrlen, std::span data, size_t gso_size) { if (quic.tx.no_gso && data.size() > gso_size) { for (; !data.empty();) { auto len = std::min(data.size(), gso_size); if (!write_udp(addr, addrlen, data.first(len), len).empty()) { return data; } data = data.subspan(len); } return {}; } iovec msg_iov{ .iov_base = const_cast(data.data()), .iov_len = data.size(), }; msghdr msg{ .msg_name = const_cast(addr), .msg_namelen = addrlen, .msg_iov = &msg_iov, .msg_iovlen = 1, }; # ifdef UDP_SEGMENT std::array msg_ctrl{}; if (data.size() > gso_size) { msg.msg_control = msg_ctrl.data(); msg.msg_controllen = msg_ctrl.size(); auto cm = CMSG_FIRSTHDR(&msg); cm->cmsg_level = SOL_UDP; cm->cmsg_type = UDP_SEGMENT; cm->cmsg_len = CMSG_LEN(sizeof(uint16_t)); auto n = static_cast(gso_size); memcpy(CMSG_DATA(cm), &n, sizeof(n)); } # endif // defined(UDP_SEGMENT) auto nwrite = sendmsg(fd, &msg, 0); if (nwrite < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return data; } if (errno == EIO && !quic.tx.no_gso) { quic.tx.no_gso = true; return write_udp(addr, addrlen, data, gso_size); } std::cerr << "sendmsg: errno=" << errno << std::endl; } else { worker->stats.udp_dgram_sent += (data.size() + gso_size - 1) / gso_size; } ev_io_stop(worker->loop, &wev); return {}; } #endif // defined(ENABLE_HTTP3) void Client::record_request_time(RequestStat *req_stat) { req_stat->request_time = std::chrono::steady_clock::now(); req_stat->request_wall_time = std::chrono::system_clock::now(); } void Client::record_connect_start_time() { cstat.connect_start_time = std::chrono::steady_clock::now(); } void Client::record_connect_time() { cstat.connect_time = std::chrono::steady_clock::now(); } void Client::record_ttfb() { if (recorded(cstat.ttfb)) { return; } cstat.ttfb = std::chrono::steady_clock::now(); } void Client::clear_connect_times() { cstat.connect_start_time = std::chrono::steady_clock::time_point(); cstat.connect_time = std::chrono::steady_clock::time_point(); cstat.ttfb = std::chrono::steady_clock::time_point(); } void Client::record_client_start_time() { // Record start time only once at the very first connection is going // to be made. if (recorded(cstat.client_start_time)) { return; } cstat.client_start_time = std::chrono::steady_clock::now(); } void Client::record_client_end_time() { // Unlike client_start_time, we overwrite client_end_time. This // handles multiple connect/disconnect for HTTP/1.1 benchmark. cstat.client_end_time = std::chrono::steady_clock::now(); } void Client::signal_write() { ev_io_start(worker->loop, &wev); } void Client::try_new_connection() { new_connection_requested = true; } namespace { unsigned int get_ev_loop_flags() { if (ev_supported_backends() & ~ev_recommended_backends() & EVBACKEND_KQUEUE) { return ev_recommended_backends() | EVBACKEND_KQUEUE; } return 0; } } // namespace Worker::Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t req_todo, size_t nclients, size_t rate, size_t max_samples, Config *config) : randgen(util::make_mt19937()), stats(req_todo, nclients), loop(ev_loop_new(get_ev_loop_flags())), ssl_ctx(ssl_ctx), config(config), id(id), tls_info_report_done(false), app_info_report_done(false), nconns_made(0), nclients(nclients), nreqs_per_client(req_todo / nclients), nreqs_rem(req_todo % nclients), rate(rate), max_samples(max_samples), next_client_id(0) { if (!config->is_rate_mode() && !config->is_timing_based_mode()) { progress_interval = std::max(static_cast(1), req_todo / 10); } else { progress_interval = std::max(static_cast(1), nclients / 10); } // Below timeout is not needed in case of timing-based benchmarking // create timer that will go off every rate_period ev_timer_init(&timeout_watcher, rate_period_timeout_w_cb, 0., config->rate_period); timeout_watcher.data = this; if (config->is_timing_based_mode()) { stats.req_stats.reserve(std::max(req_todo, max_samples)); stats.client_stats.reserve(std::max(nclients, max_samples)); } else { stats.req_stats.reserve(std::min(req_todo, max_samples)); stats.client_stats.reserve(std::min(nclients, max_samples)); } sampling_init(request_times_smp, max_samples); sampling_init(client_smp, max_samples); ev_timer_init(&duration_watcher, duration_timeout_cb, config->duration, 0.); duration_watcher.data = this; ev_timer_init(&warmup_watcher, warmup_timeout_cb, config->warm_up_time, 0.); warmup_watcher.data = this; if (config->is_timing_based_mode()) { current_phase = Phase::INITIAL_IDLE; } else { current_phase = Phase::MAIN_DURATION; } } Worker::~Worker() { ev_timer_stop(loop, &timeout_watcher); ev_timer_stop(loop, &duration_watcher); ev_timer_stop(loop, &warmup_watcher); ev_loop_destroy(loop); } void Worker::stop_all_clients() { for (auto client : clients) { if (client) { client->terminate_session(); } } } void Worker::free_client(Client *deleted_client) { for (auto &client : clients) { if (client == deleted_client) { client->req_todo = client->req_done; stats.req_todo += client->req_todo; auto index = as_unsigned(&client - &clients[0]); clients[index] = nullptr; return; } } } void Worker::run() { if (!config->is_rate_mode() && !config->is_timing_based_mode()) { for (size_t i = 0; i < nclients; ++i) { auto req_todo = nreqs_per_client; if (nreqs_rem > 0) { ++req_todo; --nreqs_rem; } auto client = std::make_unique(next_client_id++, this, req_todo); if (client->connect() != 0) { std::cerr << "client could not connect to host" << std::endl; client->fail(); } else { client.release(); } } } else if (config->is_rate_mode()) { ev_timer_again(loop, &timeout_watcher); // call callback so that we don't waste the first rate_period rate_period_timeout_w_cb(loop, &timeout_watcher, 0); } else { // call the callback to start for one single time rate_period_timeout_w_cb(loop, &timeout_watcher, 0); } ev_run(loop, 0); } namespace { template void sample(Sampling &smp, Stats &stats, Stat *s) { ++smp.n; if (stats.size() < smp.max_samples) { stats.push_back(*s); return; } auto d = std::uniform_int_distribution(0, smp.n - 1); auto i = d(gen); if (i < smp.max_samples) { stats[i] = *s; } } } // namespace void Worker::sample_req_stat(RequestStat *req_stat) { sample(request_times_smp, stats.req_stats, req_stat); } void Worker::sample_client_stat(ClientStat *cstat) { sample(client_smp, stats.client_stats, cstat); } void Worker::report_progress() { if (id != 0 || config->is_rate_mode() || stats.req_done % progress_interval || config->is_timing_based_mode()) { return; } std::cout << "progress: " << stats.req_done * 100 / stats.req_todo << "% done" << std::endl; } void Worker::report_rate_progress() { if (id != 0 || nconns_made % progress_interval) { return; } std::cout << "progress: " << nconns_made * 100 / nclients << "% of clients started" << std::endl; } namespace { // Returns percentage of number of samples within mean +/- sd. double within_sd(const std::vector &samples, double mean, double sd) { if (samples.size() == 0) { return 0.0; } auto lower = mean - sd; auto upper = mean + sd; auto m = std::ranges::count_if( samples, [&lower, &upper](double t) { return lower <= t && t <= upper; }); return (static_cast(m) / static_cast(samples.size())) * 100; } } // namespace namespace { // Computes statistics using |samples|. The min, max, mean, sd, and // percentage of number of samples within mean +/- sd are computed. // If |sampling| is true, this computes sample variance. Otherwise, // population variance. SDStat compute_time_stat(const std::vector &samples, bool sampling = false) { if (samples.empty()) { return {0.0, 0.0, 0.0, 0.0, 0.0}; } // standard deviation calculated using Rapid calculation method: // https://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods double a = 0, q = 0; size_t n = 0; double sum = 0; auto res = SDStat{std::numeric_limits::max(), std::numeric_limits::min()}; for (const auto &t : samples) { ++n; res.min = std::min(res.min, t); res.max = std::max(res.max, t); sum += t; auto na = a + (t - a) / static_cast(n); q += (t - a) * (t - na); a = na; } assert(n > 0); res.mean = sum / static_cast(n); res.sd = sqrt(q / static_cast(sampling && n > 1 ? n - 1 : n)); res.within_sd = within_sd(samples, res.mean, res.sd); return res; } } // namespace namespace { SDStats process_time_stats(const std::vector> &workers) { auto request_times_sampling = false; auto client_times_sampling = false; size_t nrequest_times = 0; size_t nclient_times = 0; for (const auto &w : workers) { nrequest_times += w->stats.req_stats.size(); request_times_sampling = w->request_times_smp.n > w->stats.req_stats.size(); nclient_times += w->stats.client_stats.size(); client_times_sampling = w->client_smp.n > w->stats.client_stats.size(); } std::vector request_times; request_times.reserve(nrequest_times); std::vector connect_times, ttfb_times, rps_values; connect_times.reserve(nclient_times); ttfb_times.reserve(nclient_times); rps_values.reserve(nclient_times); for (const auto &w : workers) { for (const auto &req_stat : w->stats.req_stats) { if (!req_stat.completed) { continue; } request_times.push_back( std::chrono::duration_cast>( req_stat.stream_close_time - req_stat.request_time) .count()); } const auto &stat = w->stats; for (const auto &cstat : stat.client_stats) { if (recorded(cstat.client_start_time) && recorded(cstat.client_end_time)) { auto t = std::chrono::duration_cast>( cstat.client_end_time - cstat.client_start_time) .count(); if (t > 1e-9) { rps_values.push_back(static_cast(cstat.req_success) / t); } } // We will get connect event before FFTB. if (!recorded(cstat.connect_start_time) || !recorded(cstat.connect_time)) { continue; } connect_times.push_back( std::chrono::duration_cast>( cstat.connect_time - cstat.connect_start_time) .count()); if (!recorded(cstat.ttfb)) { continue; } ttfb_times.push_back( std::chrono::duration_cast>( cstat.ttfb - cstat.connect_start_time) .count()); } } return {compute_time_stat(request_times, request_times_sampling), compute_time_stat(connect_times, client_times_sampling), compute_time_stat(ttfb_times, client_times_sampling), compute_time_stat(rps_values, client_times_sampling)}; } } // namespace namespace { void resolve_host() { if (config.base_uri_unix) { auto res = std::make_unique(); res->ai_family = config.unix_addr.sun_family; res->ai_socktype = SOCK_STREAM; res->ai_addrlen = sizeof(config.unix_addr); res->ai_addr = static_cast(static_cast(&config.unix_addr)); config.addrs = res.release(); return; } int rv; addrinfo *res; addrinfo hints{ .ai_flags = AI_ADDRCONFIG, .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, }; const auto &resolve_host = config.connect_to_host.empty() ? config.host : config.connect_to_host; auto port = config.connect_to_port == 0 ? config.port : config.connect_to_port; rv = getaddrinfo(resolve_host.c_str(), util::utos(port).c_str(), &hints, &res); if (rv != 0) { std::cerr << "getaddrinfo() failed: " << gai_strerror(rv) << std::endl; exit(EXIT_FAILURE); } if (res == nullptr) { std::cerr << "No address returned" << std::endl; exit(EXIT_FAILURE); } config.addrs = res; } } // namespace namespace { std::string get_reqline(const char *uri, const urlparse_url &u) { std::string reqline; if (util::has_uri_field(u, URLPARSE_PATH)) { reqline = util::get_uri_field(uri, u, URLPARSE_PATH); } else { reqline = "/"; } if (util::has_uri_field(u, URLPARSE_QUERY)) { reqline += '?'; reqline += util::get_uri_field(uri, u, URLPARSE_QUERY); } return reqline; } } // namespace namespace { constexpr auto UNIX_PATH_PREFIX = "unix:"sv; } // namespace namespace { bool parse_base_uri(const std::string_view &base_uri) { urlparse_url u; if (urlparse_parse_url(base_uri.data(), base_uri.size(), 0, &u) != 0 || !util::has_uri_field(u, URLPARSE_SCHEMA) || !util::has_uri_field(u, URLPARSE_HOST)) { return false; } config.scheme = util::get_uri_field(base_uri.data(), u, URLPARSE_SCHEMA); config.host = util::get_uri_field(base_uri.data(), u, URLPARSE_HOST); config.default_port = util::get_default_port(base_uri.data(), u); if (util::has_uri_field(u, URLPARSE_PORT)) { config.port = u.port; } else { config.port = config.default_port; } return true; } } // namespace namespace { // Use std::vector::iterator explicitly, without that, // urlparse_url u{} fails with clang-3.4. std::vector parse_uris(std::vector::iterator first, std::vector::iterator last) { std::vector reqlines; if (first == last) { std::cerr << "no URI available" << std::endl; exit(EXIT_FAILURE); } if (!config.has_base_uri()) { if (!parse_base_uri(*first)) { std::cerr << "invalid URI: " << *first << std::endl; exit(EXIT_FAILURE); } config.base_uri = *first; } for (; first != last; ++first) { urlparse_url u; auto uri = (*first).c_str(); if (urlparse_parse_url(uri, (*first).size(), 0, &u) != 0) { std::cerr << "invalid URI: " << uri << std::endl; exit(EXIT_FAILURE); } reqlines.push_back(get_reqline(uri, u)); } return reqlines; } } // namespace namespace { std::vector read_uri_from_file(std::istream &infile) { std::vector uris; std::string line_uri; while (std::getline(infile, line_uri)) { uris.push_back(line_uri); } return uris; } } // namespace namespace { void read_script_from_file( std::istream &infile, std::vector &timings, std::vector &uris) { std::string script_line; int line_count = 0; while (std::getline(infile, script_line)) { line_count++; if (script_line.empty()) { std::cerr << "Empty line detected at line " << line_count << ". Ignoring and continuing." << std::endl; continue; } std::size_t pos = script_line.find("\t"); if (pos == std::string::npos) { std::cerr << "Invalid line format detected, no tab character at line " << line_count << ". \n\t" << script_line << std::endl; exit(EXIT_FAILURE); } const char *start = script_line.c_str(); char *end; auto v = std::strtod(start, &end); errno = 0; if (v < 0.0 || !std::isfinite(v) || end == start || errno != 0) { auto error = errno; std::cerr << "Time value error at line " << line_count << ". \n\t" << "value = " << script_line.substr(0, pos) << std::endl; if (error != 0) { std::cerr << "\t" << strerror(error) << std::endl; } exit(EXIT_FAILURE); } timings.emplace_back( std::chrono::duration_cast( std::chrono::duration(v))); uris.push_back(script_line.substr(pos + 1, script_line.size())); } } } // namespace namespace { std::unique_ptr create_worker(uint32_t id, SSL_CTX *ssl_ctx, size_t nreqs, size_t nclients, size_t rate, size_t max_samples) { std::stringstream rate_report; if (config.is_rate_mode() && nclients > rate) { rate_report << "Up to " << rate << " client(s) will be created every " << util::duration_str(config.rate_period) << " "; } if (config.is_timing_based_mode()) { std::cout << "spawning thread #" << id << ": " << nclients << " total client(s). Timing-based test with " << config.warm_up_time << "s of warm-up time and " << config.duration << "s of main duration for measurements." << std::endl; } else { std::cout << "spawning thread #" << id << ": " << nclients << " total client(s). " << rate_report.str() << nreqs << " total requests" << std::endl; } if (config.is_rate_mode()) { return std::make_unique(id, ssl_ctx, nreqs, nclients, rate, max_samples, &config); } else { // Here rate is same as client because the rate_timeout callback // will be called only once return std::make_unique(id, ssl_ctx, nreqs, nclients, nclients, max_samples, &config); } } } // namespace namespace { int parse_header_table_size(uint32_t &dst, const char *opt, const char *optarg) { auto n = util::parse_uint_with_unit(optarg); if (!n) { std::cerr << "--" << opt << ": Bad option value: " << optarg << std::endl; return -1; } if (n > std::numeric_limits::max()) { std::cerr << "--" << opt << ": Value too large. It should be less than or equal to " << std::numeric_limits::max() << std::endl; return -1; } dst = static_cast(*n); return 0; } } // namespace namespace { std::string make_http_authority(const Config &config) { std::string host; if (util::numeric_host(config.host.c_str(), AF_INET6)) { host += '['; host += config.host; host += ']'; } else { host = config.host; } if (config.port != config.default_port) { host += ':'; host += util::utos(config.port); } return host; } } // namespace namespace { void print_version(std::ostream &out) { out << "h2load nghttp2/" NGHTTP2_VERSION << std::endl; } } // namespace namespace { void print_usage(std::ostream &out) { out << R"(Usage: h2load [OPTIONS]... [URI]... benchmarking tool for HTTP/2 server)" << std::endl; } } // namespace namespace { constexpr auto DEFAULT_ALPN_LIST = "h2,http/1.1"sv; } // namespace namespace { void print_help(std::ostream &out) { print_usage(out); auto config = Config(); out << R"( Specify URI to access. Multiple URIs can be specified. URIs are used in this order for each client. All URIs are used, then first URI is used and then 2nd URI, and so on. The scheme, host and port in the subsequent URIs, if present, are ignored. Those in the first URI are used solely. Definition of a base URI overrides all scheme, host or port values. Options: -n, --requests= Number of requests across all clients. If it is used with --timing-script-file option, this option specifies the number of requests each client performs rather than the number of requests across all clients. This option is ignored if timing-based benchmarking is enabled (see --duration option). Default: )" << config.nreqs << R"( -c, --clients= Number of concurrent clients. With -r option, this specifies the maximum number of connections to be made. Default: )" << config.nclients << R"( -t, --threads= Number of native threads. Default: )" << config.nthreads << R"( -i, --input-file= Path of a file with multiple URIs are separated by EOLs. This option will disable URIs getting from command-line. If '-' is given as , URIs will be read from stdin. URIs are used in this order for each client. All URIs are used, then first URI is used and then 2nd URI, and so on. The scheme, host and port in the subsequent URIs, if present, are ignored. Those in the first URI are used solely. Definition of a base URI overrides all scheme, host or port values. -m, --max-concurrent-streams= Max concurrent streams to issue per session. When http/1.1 is used, this specifies the number of HTTP pipelining requests in-flight. Default: 1 -f, --max-frame-size= Maximum frame size that the local endpoint is willing to receive. Default: )" << util::utos_unit(config.max_frame_size) << R"( -w, --window-bits= Sets the stream level initial window size to (2**)-1. For QUIC, is capped to 26 (roughly 64MiB). It defaults to 24 (16MiB) for QUIC, and 30 for other protocols. -W, --connection-window-bits= Sets the connection level initial window size to (2**)-1. Default: )" << config.connection_window_bits << R"( -H, --header=
Add/Override a header to the requests. --ciphers= Set allowed cipher list for TLSv1.2 or earlier. The format of the string is described in OpenSSL ciphers(1). Default: )" << config.ciphers << R"( --tls13-ciphers= Set allowed cipher list for TLSv1.3. The format of the string is described in OpenSSL ciphers(1). Default: )" << config.tls13_ciphers << R"( -p, --no-tls-proto= Specify ALPN identifier of the protocol to be used when accessing http URI without SSL/TLS. Available protocols: )" << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"( and )" << NGHTTP2_H1_1 << R"( Default: )" << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"( -d, --data= Post FILE to server. The request method is changed to POST. For http/1.1 connection, if -d is used, the maximum number of in-flight pipelined requests is set to 1. -r, --rate= Specifies the fixed rate at which connections are created. The rate must be a positive integer, representing the number of connections to be made per rate period. The maximum number of connections to be made is given in -c option. This rate will be distributed among threads as evenly as possible. For example, with -t2 and -r4, each thread gets 2 connections per period. When the rate is 0, the program will run as it normally does, creating connections at whatever variable rate it wants. The default value for this option is 0. -r and -D are mutually exclusive. --rate-period= Specifies the time period between creating connections. The period must be a positive number, representing the length of the period in time. This option is ignored if the rate option is not used. The default value for this option is 1s. -D, --duration= Specifies the main duration for the measurements in case of timing-based benchmarking. -D and -r are mutually exclusive. --warm-up-time= Specifies the time period before starting the actual measurements, in case of timing-based benchmarking. Needs to provided along with -D option. -T, --connection-active-timeout= Specifies the maximum time that h2load is willing to keep a connection open, regardless of the activity on said connection. must be a positive integer, specifying the amount of time to wait. When no timeout value is set (either active or inactive), h2load will keep a connection open indefinitely, waiting for a response. -N, --connection-inactivity-timeout= Specifies the amount of time that h2load is willing to wait to see activity on a given connection. must be a positive integer, specifying the amount of time to wait. When no timeout value is set (either active or inactive), h2load will keep a connection open indefinitely, waiting for a response. --timing-script-file= Path of a file containing one or more lines separated by EOLs. Each script line is composed of two tab-separated fields. The first field represents the time offset from the start of execution, expressed as a positive value of milliseconds with microsecond resolution. The second field represents the URI. This option will disable URIs getting from command-line. If '-' is given as , script lines will be read from stdin. Script lines are used in order for each client. If -n is given, it must be less than or equal to the number of script lines, larger values are clamped to the number of script lines. If -n is not given, the number of requests will default to the number of script lines. The scheme, host and port defined in the first URI are used solely. Values contained in other URIs, if present, are ignored. Definition of a base URI overrides all scheme, host or port values. --timing-script-file and --rps are mutually exclusive. -B, --base-uri=(|unix:) Specify URI from which the scheme, host and port will be used for all requests. The base URI overrides all values defined either at the command line or inside input files. If argument starts with "unix:", then the rest of the argument will be treated as UNIX domain socket path. The connection is made through that path instead of TCP. In this case, scheme is inferred from the first URI appeared in the command line or inside input files as usual. --alpn-list= Comma delimited list of ALPN protocol identifier sorted in the order of preference. That means most desirable protocol comes first. The parameter must be delimited by a single comma only and any white spaces are treated as a part of protocol string. Default: )" << DEFAULT_ALPN_LIST << R"( --h1 Short hand for --alpn-list=http/1.1 --no-tls-proto=http/1.1, which effectively force http/1.1 for both http and https URI. --header-table-size= Specify decoder header table size. Default: )" << util::utos_unit(config.header_table_size) << R"( --encoder-header-table-size= Specify encoder header table size. The decoder (server) specifies the maximum dynamic table size it accepts. Then the negotiated dynamic table size is the minimum of this option value and the value which server specified. Default: )" << util::utos_unit(config.encoder_header_table_size) << R"( --log-file= Write per-request information to a file as tab-separated columns: start time as microseconds since epoch; HTTP status code; microseconds until end of response. More columns may be added later. Rows are ordered by end-of- response time when using one worker thread, but may appear slightly out of order with multiple threads due to buffering. Status code is -1 for failed streams. --qlog-file-base= Enable qlog output and specify base file name for qlogs. Qlog is emitted for each connection. For a given base name "base", each output file name becomes "base.M.N.sqlog" where M is worker ID and N is client ID (e.g. "base.0.3.sqlog"). Only effective in QUIC runs. --connect-to=[:] Host and port to connect instead of using the authority in . --rps= Specify request per second for each client. --rps and --timing-script-file are mutually exclusive. --groups= Specify the supported groups. Default: )" << config.groups << R"( --no-udp-gso Disable UDP GSO. --max-udp-payload-size= Specify the maximum outgoing UDP datagram payload size. --ktls Enable ktls. --sni= Send in TLS SNI, overriding the host name specified in URI. -v, --verbose Output debug information. --version Display version information and exit. -h, --help Display this help and exit. -- The argument is an integer and an optional unit (e.g., 10K is 10 * 1024). Units are K, M and G (powers of 1024). The argument is an integer and an optional unit (e.g., 1s is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms (hours, minutes, seconds and milliseconds, respectively). If a unit is omitted, a second is used as unit.)" << std::endl; } } // namespace int main(int argc, char **argv) { std::string datafile; std::string logfile; bool nreqs_set_manually = false; auto window_bits_set_manually = false; while (1) { static int flag = 0; constexpr static option long_options[] = { {"requests", required_argument, nullptr, 'n'}, {"clients", required_argument, nullptr, 'c'}, {"data", required_argument, nullptr, 'd'}, {"threads", required_argument, nullptr, 't'}, {"max-concurrent-streams", required_argument, nullptr, 'm'}, {"window-bits", required_argument, nullptr, 'w'}, {"max-frame-size", required_argument, nullptr, 'f'}, {"connection-window-bits", required_argument, nullptr, 'W'}, {"input-file", required_argument, nullptr, 'i'}, {"header", required_argument, nullptr, 'H'}, {"no-tls-proto", required_argument, nullptr, 'p'}, {"verbose", no_argument, nullptr, 'v'}, {"help", no_argument, nullptr, 'h'}, {"version", no_argument, &flag, 1}, {"ciphers", required_argument, &flag, 2}, {"rate", required_argument, nullptr, 'r'}, {"connection-active-timeout", required_argument, nullptr, 'T'}, {"connection-inactivity-timeout", required_argument, nullptr, 'N'}, {"duration", required_argument, nullptr, 'D'}, {"timing-script-file", required_argument, &flag, 3}, {"base-uri", required_argument, nullptr, 'B'}, {"npn-list", required_argument, &flag, 4}, {"rate-period", required_argument, &flag, 5}, {"h1", no_argument, &flag, 6}, {"header-table-size", required_argument, &flag, 7}, {"encoder-header-table-size", required_argument, &flag, 8}, {"warm-up-time", required_argument, &flag, 9}, {"log-file", required_argument, &flag, 10}, {"connect-to", required_argument, &flag, 11}, {"rps", required_argument, &flag, 12}, {"groups", required_argument, &flag, 13}, {"tls13-ciphers", required_argument, &flag, 14}, {"no-udp-gso", no_argument, &flag, 15}, {"qlog-file-base", required_argument, &flag, 16}, {"max-udp-payload-size", required_argument, &flag, 17}, {"ktls", no_argument, &flag, 18}, {"alpn-list", required_argument, &flag, 19}, {"sni", required_argument, &flag, 20}, {nullptr, 0, nullptr, 0}}; int option_index = 0; auto c = getopt_long(argc, argv, "hvW:c:d:m:n:p:t:w:f:H:i:r:T:N:D:B:", long_options, &option_index); if (c == -1) { break; } switch (c) { case 'n': { auto n = util::parse_uint(optarg); if (!n) { std::cerr << "-n: bad option value: " << optarg << std::endl; exit(EXIT_FAILURE); } config.nreqs = static_cast(*n); nreqs_set_manually = true; break; } case 'c': { auto n = util::parse_uint(optarg); if (!n) { std::cerr << "-c: bad option value: " << optarg << std::endl; exit(EXIT_FAILURE); } config.nclients = static_cast(*n); break; } case 'd': datafile = optarg; break; case 't': { #ifdef NOTHREADS std::cerr << "-t: WARNING: Threading disabled at build time, " << "no threads created." << std::endl; #else // !defined(NOTHREADS) auto n = util::parse_uint(optarg); if (!n) { std::cerr << "-t: bad option value: " << optarg << std::endl; exit(EXIT_FAILURE); } config.nthreads = static_cast(*n); #endif // !defined(NOTHREADS) break; } case 'm': { auto n = util::parse_uint(optarg); if (!n) { std::cerr << "-m: bad option value: " << optarg << std::endl; exit(EXIT_FAILURE); } config.max_concurrent_streams = static_cast(*n); break; } case 'w': case 'W': { auto n = util::parse_uint(optarg); if (!n || n > 30) { std::cerr << "-" << static_cast(c) << ": specify the integer in the range [0, 30], inclusive" << std::endl; exit(EXIT_FAILURE); } if (c == 'w') { window_bits_set_manually = true; config.window_bits = static_cast(*n); } else { config.connection_window_bits = static_cast(*n); } break; } case 'f': { auto n = util::parse_uint_with_unit(optarg); if (!n) { std::cerr << "--max-frame-size: bad option value: " << optarg << std::endl; exit(EXIT_FAILURE); } if (static_cast(*n) < 16_k) { std::cerr << "--max-frame-size: minimum 16384" << std::endl; exit(EXIT_FAILURE); } if (static_cast(*n) > 16_m - 1) { std::cerr << "--max-frame-size: maximum 16777215" << std::endl; exit(EXIT_FAILURE); } config.max_frame_size = static_cast(*n); break; } case 'H': { char *header = optarg; // Skip first possible ':' in the header name auto name_end = strchr(optarg + 1, ':'); if (!name_end || (header[0] == ':' && header + 1 == name_end)) { std::cerr << "-H: invalid header: " << optarg << std::endl; exit(EXIT_FAILURE); } *name_end = 0; auto value = name_end + 1; while (isspace(*value)) { value++; } if (*value == 0) { // This could also be a valid case for suppressing a header // similar to curl std::cerr << "-H: invalid header - value missing: " << optarg << std::endl; exit(EXIT_FAILURE); } // Note that there is no processing currently to handle multiple // message-header fields with the same field name util::tolower(header, name_end, header); config.custom_headers.emplace_back(header, value); break; } case 'i': config.ifile = optarg; break; case 'p': { auto proto = std::string_view{optarg}; if (util::strieq(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID ""sv, proto)) { config.no_tls_proto = Config::PROTO_HTTP2; } else if (util::strieq(NGHTTP2_H1_1, proto)) { config.no_tls_proto = Config::PROTO_HTTP1_1; } else { std::cerr << "-p: unsupported protocol " << proto << std::endl; exit(EXIT_FAILURE); } break; } case 'r': { auto n = util::parse_uint(optarg); if (!n) { std::cerr << "-r: bad option value: " << optarg << std::endl; exit(EXIT_FAILURE); } if (n == 0) { std::cerr << "-r: the rate at which connections are made " << "must be positive." << std::endl; exit(EXIT_FAILURE); } config.rate = static_cast(*n); break; } case 'T': { auto d = util::parse_duration_with_unit(optarg); if (!d) { std::cerr << "-T: bad value for the conn_active_timeout wait time: " << optarg << std::endl; exit(EXIT_FAILURE); } config.conn_active_timeout = *d; break; } case 'N': { auto d = util::parse_duration_with_unit(optarg); if (!d) { std::cerr << "-N: bad value for the conn_inactivity_timeout wait time: " << optarg << std::endl; exit(EXIT_FAILURE); } config.conn_inactivity_timeout = *d; break; } case 'B': { auto arg = std::string_view{optarg}; config.base_uri = ""; config.base_uri_unix = false; if (util::istarts_with(arg, UNIX_PATH_PREFIX)) { // UNIX domain socket path sockaddr_un un; auto path = std::string_view{std::ranges::begin(arg) + UNIX_PATH_PREFIX.size(), std::ranges::end(arg)}; if (path.size() == 0 || path.size() + 1 > sizeof(un.sun_path)) { std::cerr << "--base-uri: invalid UNIX domain socket path: " << arg << std::endl; exit(EXIT_FAILURE); } config.base_uri_unix = true; auto &unix_addr = config.unix_addr; std::ranges::copy(path, unix_addr.sun_path); unix_addr.sun_path[path.size()] = '\0'; unix_addr.sun_family = AF_UNIX; break; } if (!parse_base_uri(arg)) { std::cerr << "--base-uri: invalid base URI: " << arg << std::endl; exit(EXIT_FAILURE); } config.base_uri = arg; break; } case 'D': { auto d = util::parse_duration_with_unit(optarg); if (!d) { std::cerr << "-D: value error " << optarg << std::endl; exit(EXIT_FAILURE); } config.duration = *d; break; } case 'v': config.verbose = true; break; case 'h': print_help(std::cout); exit(EXIT_SUCCESS); case '?': util::show_candidates(argv[optind - 1], long_options); exit(EXIT_FAILURE); case 0: switch (flag) { case 1: // version option print_version(std::cout); exit(EXIT_SUCCESS); case 2: // ciphers option config.ciphers = optarg; break; case 3: // timing-script option config.ifile = optarg; config.timing_script = true; break; case 5: { // rate-period auto d = util::parse_duration_with_unit(optarg); if (!d) { std::cerr << "--rate-period: value error " << optarg << std::endl; exit(EXIT_FAILURE); } config.rate_period = *d; break; } case 6: // --h1 config.alpn_list = util::parse_config_str_list("http/1.1"sv); config.no_tls_proto = Config::PROTO_HTTP1_1; break; case 7: // --header-table-size if (parse_header_table_size(config.header_table_size, "header-table-size", optarg) != 0) { exit(EXIT_FAILURE); } break; case 8: // --encoder-header-table-size if (parse_header_table_size(config.encoder_header_table_size, "encoder-header-table-size", optarg) != 0) { exit(EXIT_FAILURE); } break; case 9: { // --warm-up-time auto d = util::parse_duration_with_unit(optarg); if (!d) { std::cerr << "--warm-up-time: value error " << optarg << std::endl; exit(EXIT_FAILURE); } config.warm_up_time = *d; break; } case 10: // --log-file logfile = optarg; break; case 11: { // --connect-to auto p = util::split_hostport(std::string_view{optarg}); int64_t port = 0; if (p.first.empty() || (!p.second.empty() && (port = util::parse_uint(p.second).value_or(-1)) == -1)) { std::cerr << "--connect-to: Invalid value " << optarg << std::endl; exit(EXIT_FAILURE); } config.connect_to_host = p.first; config.connect_to_port = static_cast(port); break; } case 12: { char *end; auto v = std::strtod(optarg, &end); if (end == optarg || *end != '\0' || !std::isfinite(v) || 1. / v < 1e-6) { std::cerr << "--rps: Invalid value " << optarg << std::endl; exit(EXIT_FAILURE); } config.rps = v; break; } case 13: // --groups config.groups = optarg; break; case 14: // --tls13-ciphers config.tls13_ciphers = optarg; break; case 15: // --no-udp-gso config.no_udp_gso = true; break; case 16: // --qlog-file-base config.qlog_file_base = optarg; break; case 17: { // --max-udp-payload-size auto n = util::parse_uint_with_unit(optarg); if (!n) { std::cerr << "--max-udp-payload-size: bad option value: " << optarg << std::endl; exit(EXIT_FAILURE); } if (static_cast(*n) > 64_k) { std::cerr << "--max-udp-payload-size: must not exceed 65536" << std::endl; exit(EXIT_FAILURE); } config.max_udp_payload_size = static_cast(*n); break; } case 18: // --ktls config.ktls = true; break; case 4: // npn-list option std::cerr << "--npn-list: deprecated. Use --alpn-list instead." << std::endl; // fall through case 19: // alpn-list option config.alpn_list = util::parse_config_str_list(std::string_view{optarg}); break; case 20: // --sni config.sni = optarg; break; } break; default: break; } } if (argc == optind) { if (config.ifile.empty()) { std::cerr << "no URI or input file given" << std::endl; exit(EXIT_FAILURE); } } if (config.nclients == 0) { std::cerr << "-c: the number of clients must be strictly greater than 0." << std::endl; exit(EXIT_FAILURE); } if (config.alpn_list.empty()) { config.alpn_list = util::parse_config_str_list(DEFAULT_ALPN_LIST); } // serialize the APLN tokens for (auto &proto : config.alpn_list) { proto.insert(std::ranges::begin(proto), static_cast(proto.size())); } if (config.is_quic() && !window_bits_set_manually) { config.window_bits = 24; } std::vector reqlines; if (config.ifile.empty()) { std::vector uris; std::ranges::copy(&argv[optind], &argv[argc], std::back_inserter(uris)); reqlines = parse_uris(std::ranges::begin(uris), std::ranges::end(uris)); } else { std::vector uris; if (!config.timing_script) { if (config.ifile == "-") { uris = read_uri_from_file(std::cin); } else { std::ifstream infile(config.ifile); if (!infile) { std::cerr << "cannot read input file: " << config.ifile << std::endl; exit(EXIT_FAILURE); } uris = read_uri_from_file(infile); } } else { if (config.ifile == "-") { read_script_from_file(std::cin, config.timings, uris); } else { std::ifstream infile(config.ifile); if (!infile) { std::cerr << "cannot read input file: " << config.ifile << std::endl; exit(EXIT_FAILURE); } read_script_from_file(infile, config.timings, uris); } if (nreqs_set_manually) { if (config.nreqs > uris.size()) { std::cerr << "-n: the number of requests must be less than or equal " "to the number of timing script entries. Setting number " "of requests to " << uris.size() << std::endl; config.nreqs = uris.size(); } } else { config.nreqs = uris.size(); } } reqlines = parse_uris(std::ranges::begin(uris), std::ranges::end(uris)); } if (reqlines.empty()) { std::cerr << "No URI given" << std::endl; exit(EXIT_FAILURE); } if (config.is_timing_based_mode() && config.is_rate_mode()) { std::cerr << "-r, -D: they are mutually exclusive." << std::endl; exit(EXIT_FAILURE); } if (config.timing_script && config.rps_enabled()) { std::cerr << "--timing-script-file, --rps: they are mutually exclusive." << std::endl; exit(EXIT_FAILURE); } if (config.nreqs == 0 && !config.is_timing_based_mode()) { std::cerr << "-n: the number of requests must be strictly greater than 0 " "if timing-based test is not being run." << std::endl; exit(EXIT_FAILURE); } if (config.max_concurrent_streams == 0) { std::cerr << "-m: the max concurrent streams must be strictly greater " << "than 0." << std::endl; exit(EXIT_FAILURE); } if (config.nthreads == 0) { std::cerr << "-t: the number of threads must be strictly greater than 0." << std::endl; exit(EXIT_FAILURE); } if (config.nthreads > std::thread::hardware_concurrency()) { std::cerr << "-t: warning: the number of threads is greater than hardware " << "cores." << std::endl; } // With timing script, we don't distribute config.nreqs to each // client or thread. if (!config.timing_script && config.nreqs < config.nclients && !config.is_timing_based_mode()) { std::cerr << "-n, -c: the number of requests must be greater than or " << "equal to the clients." << std::endl; exit(EXIT_FAILURE); } if (config.nclients < config.nthreads) { std::cerr << "-c, -t: the number of clients must be greater than or equal " << "to the number of threads." << std::endl; exit(EXIT_FAILURE); } if (config.is_timing_based_mode()) { config.nreqs = 0; } if (config.is_rate_mode()) { if (config.rate < config.nthreads) { std::cerr << "-r, -t: the connection rate must be greater than or equal " << "to the number of threads." << std::endl; exit(EXIT_FAILURE); } if (config.rate > config.nclients) { std::cerr << "-r, -c: the connection rate must be smaller than or equal " "to the number of clients." << std::endl; exit(EXIT_FAILURE); } } if (!datafile.empty()) { config.data_fd = open(datafile.c_str(), O_RDONLY | O_BINARY); if (config.data_fd == -1) { std::cerr << "-d: Could not open file " << datafile << std::endl; exit(EXIT_FAILURE); } struct stat data_stat; if (fstat(config.data_fd, &data_stat) == -1) { std::cerr << "-d: Could not stat file " << datafile << std::endl; exit(EXIT_FAILURE); } config.data_length = data_stat.st_size; auto addr = mmap(nullptr, static_cast(config.data_length), PROT_READ, MAP_SHARED, config.data_fd, 0); if (addr == MAP_FAILED) { std::cerr << "-d: Could not mmap file " << datafile << std::endl; exit(EXIT_FAILURE); } config.data = static_cast(addr); } if (!logfile.empty()) { config.log_fd = open(logfile.c_str(), O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP); if (config.log_fd == -1) { std::cerr << "--log-file: Could not open file " << logfile << std::endl; exit(EXIT_FAILURE); } } if (!config.qlog_file_base.empty() && !config.is_quic()) { std::cerr << "Warning: --qlog-file-base: only effective in quic, ignoring." << std::endl; } struct sigaction act{}; act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, nullptr); #ifdef ENABLE_HTTP3 # if defined(HAVE_LIBNGTCP2_CRYPTO_QUICTLS) || \ defined(HAVE_LIBNGTCP2_CRYPTO_LIBRESSL) if (ngtcp2_crypto_quictls_init() != 0) { std::cerr << "ngtcp2_crypto_quictls_init failed" << std::endl; exit(EXIT_FAILURE); } # endif // defined(HAVE_LIBNGTCP2_CRYPTO_QUICTLS) || // defined(HAVE_LIBNGTCP2_CRYPTO_LIBRESSL) # ifdef HAVE_LIBNGTCP2_CRYPTO_OSSL if (ngtcp2_crypto_ossl_init() != 0) { std::cerr << "ngtcp2_crypto_ossl_init failed" << std::endl; exit(EXIT_FAILURE); } # endif // defined(HAVE_LIBNGTCP2_CRYPTO_OSSL) #endif // defined(ENABLE_HTTP3) auto ssl_ctx = SSL_CTX_new(TLS_client_method()); if (!ssl_ctx) { std::cerr << "Failed to create SSL_CTX: " << ERR_error_string(ERR_get_error(), nullptr) << std::endl; exit(EXIT_FAILURE); } auto ssl_opts = static_cast( (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); #ifdef SSL_OP_ENABLE_KTLS if (config.ktls) { ssl_opts |= SSL_OP_ENABLE_KTLS; } #endif // defined(SSL_OP_ENABLE_KTLS) SSL_CTX_set_options(ssl_ctx, ssl_opts); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); if (config.is_quic()) { #ifdef ENABLE_HTTP3 # if defined(HAVE_LIBNGTCP2_CRYPTO_QUICTLS) || \ defined(HAVE_LIBNGTCP2_CRYPTO_LIBRESSL) if (ngtcp2_crypto_quictls_configure_client_context(ssl_ctx) != 0) { std::cerr << "ngtcp2_crypto_quictls_configure_client_context failed" << std::endl; exit(EXIT_FAILURE); } # endif // defined(HAVE_LIBNGTCP2_CRYPTO_QUICTLS) || // defined(HAVE_LIBNGTCP2_CRYPTO_LIBRESSL) # ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL if (ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) { std::cerr << "ngtcp2_crypto_boringssl_configure_client_context failed" << std::endl; exit(EXIT_FAILURE); } # endif // defined(HAVE_LIBNGTCP2_CRYPTO_BORINGSSL) # ifdef HAVE_LIBNGTCP2_CRYPTO_WOLFSSL if (ngtcp2_crypto_wolfssl_configure_client_context(ssl_ctx) != 0) { std::cerr << "ngtcp2_crypto_wolfssl_configure_client_context failed" << std::endl; exit(EXIT_FAILURE); } # endif // defined(HAVE_LIBNGTCP2_CRYPTO_WOLFSSL) #endif // defined(ENABLE_HTTP3) } else if (nghttp2::tls::ssl_ctx_set_proto_versions( ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION, nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) { std::cerr << "Could not set TLS versions" << std::endl; exit(EXIT_FAILURE); } if (SSL_CTX_set_cipher_list(ssl_ctx, config.ciphers.c_str()) == 0) { std::cerr << "SSL_CTX_set_cipher_list with " << config.ciphers << " failed: " << ERR_error_string(ERR_get_error(), nullptr) << std::endl; exit(EXIT_FAILURE); } #if defined(NGHTTP2_GENUINE_OPENSSL) || \ defined(NGHTTP2_OPENSSL_IS_LIBRESSL) || defined(NGHTTP2_OPENSSL_IS_WOLFSSL) if (SSL_CTX_set_ciphersuites(ssl_ctx, config.tls13_ciphers.c_str()) == 0) { std::cerr << "SSL_CTX_set_ciphersuites with " << config.tls13_ciphers << " failed: " << ERR_error_string(ERR_get_error(), nullptr) << std::endl; exit(EXIT_FAILURE); } #endif // defined(NGHTTP2_GENUINE_OPENSSL) || // defined(NGHTTP2_OPENSSL_IS_LIBRESSL) || // defined(NGHTTP2_OPENSSL_IS_WOLFSSL) if (SSL_CTX_set1_groups_list(ssl_ctx, config.groups.c_str()) != 1) { std::cerr << "SSL_CTX_set1_groups_list failed" << std::endl; exit(EXIT_FAILURE); } std::vector proto_list; for (const auto &proto : config.alpn_list) { std::ranges::copy(proto, std::back_inserter(proto_list)); } SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), static_cast(proto_list.size())); if (tls::setup_keylog_callback(ssl_ctx) != 0) { std::cerr << "Failed to setup keylog" << std::endl; exit(EXIT_FAILURE); } #if defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && defined(HAVE_LIBBROTLI) if (!SSL_CTX_add_cert_compression_alg( ssl_ctx, nghttp2::tls::CERTIFICATE_COMPRESSION_ALGO_BROTLI, nghttp2::tls::cert_compress, nghttp2::tls::cert_decompress)) { std::cerr << "SSL_CTX_add_cert_compression_alg failed" << std::endl; exit(EXIT_FAILURE); } #endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && // defined(HAVE_LIBBROTLI) std::string user_agent = "h2load nghttp2/" NGHTTP2_VERSION; Headers shared_nva; shared_nva.emplace_back(":scheme", config.scheme); shared_nva.emplace_back(":authority", make_http_authority(config)); shared_nva.emplace_back(":method", config.data_fd == -1 ? "GET" : "POST"); shared_nva.emplace_back("user-agent", user_agent); // list header fields that can be overridden. auto override_hdrs = std::to_array( {":authority", "host", ":method", ":scheme", "user-agent"}); for (auto &kv : config.custom_headers) { if (util::contains(override_hdrs, kv.name)) { // override header for (auto &nv : shared_nva) { if ((nv.name == ":authority" && kv.name == "host") || (nv.name == kv.name)) { nv.value = kv.value; } } } else { // add additional headers shared_nva.push_back(kv); } } std::string content_length_str; if (config.data_fd != -1) { content_length_str = util::utos(as_unsigned(config.data_length)); } auto method_it = std::ranges::find_if( shared_nva, [](const Header &nv) { return nv.name == ":method"; }); assert(method_it != std::ranges::end(shared_nva)); config.h1reqs.reserve(reqlines.size()); config.nva.reserve(reqlines.size()); for (auto &req : reqlines) { // For HTTP/1.1 auto h1req = (*method_it).value; h1req += ' '; h1req += req; h1req += " HTTP/1.1\r\n"; for (auto &nv : shared_nva) { if (nv.name == ":authority") { h1req += "Host: "; h1req += nv.value; h1req += "\r\n"; continue; } if (nv.name[0] == ':') { continue; } h1req += nv.name; h1req += ": "; h1req += nv.value; h1req += "\r\n"; } if (!content_length_str.empty()) { h1req += "Content-Length: "; h1req += content_length_str; h1req += "\r\n"; } h1req += "\r\n"; config.h1reqs.push_back(std::move(h1req)); // For nghttp2 std::vector nva; // 2 for :path, and possible content-length nva.reserve(2 + shared_nva.size()); nva.push_back(http2::make_field_v(":path"sv, req)); for (auto &nv : shared_nva) { nva.push_back(http2::make_field_nv(nv.name, nv.value)); } if (!content_length_str.empty()) { nva.push_back( http2::make_field_nv("content-length"sv, content_length_str)); } config.nva.push_back(std::move(nva)); } // Don't DOS our server! if (config.host == "nghttp2.org") { std::cerr << "Using h2load against public server " << config.host << " should be prohibited." << std::endl; exit(EXIT_FAILURE); } resolve_host(); std::cout << "starting benchmark..." << std::endl; std::vector> workers; workers.reserve(config.nthreads); #ifndef NOTHREADS size_t nreqs_per_thread = 0; size_t nreqs_rem = 0; if (!config.timing_script) { nreqs_per_thread = config.nreqs / config.nthreads; nreqs_rem = config.nreqs % config.nthreads; } auto nclients_per_thread = config.nclients / config.nthreads; auto nclients_rem = config.nclients % config.nthreads; auto rate_per_thread = config.rate / config.nthreads; auto rate_per_thread_rem = config.rate % config.nthreads; size_t max_samples_per_thread = std::max(static_cast(256), MAX_SAMPLES / config.nthreads); std::mutex mu; std::condition_variable cv; auto ready = false; std::vector> futures; for (size_t i = 0; i < config.nthreads; ++i) { auto rate = rate_per_thread; if (rate_per_thread_rem > 0) { --rate_per_thread_rem; ++rate; } auto nclients = nclients_per_thread; if (nclients_rem > 0) { --nclients_rem; ++nclients; } size_t nreqs; if (config.timing_script) { // With timing script, each client issues config.nreqs requests. // We divide nreqs by number of clients in Worker ctor to // distribute requests to those clients evenly, so multiply // config.nreqs here by config.nclients. nreqs = config.nreqs * nclients; } else { nreqs = nreqs_per_thread; if (nreqs_rem > 0) { --nreqs_rem; ++nreqs; } } workers.push_back(create_worker(static_cast(i), ssl_ctx, nreqs, nclients, rate, max_samples_per_thread)); auto &worker = workers.back(); futures.push_back( std::async(std::launch::async, [&worker, &mu, &cv, &ready]() { { std::unique_lock ulk(mu); cv.wait(ulk, [&ready] { return ready; }); } worker->run(); # ifdef NGHTTP2_OPENSSL_IS_WOLFSSL wc_ecc_fp_free(); # endif // defined(NGHTTP2_OPENSSL_IS_WOLFSSL) })); } { std::lock_guard lg(mu); ready = true; cv.notify_all(); } auto start = std::chrono::steady_clock::now(); for (auto &fut : futures) { fut.get(); } #else // defined(NOTHREADS) auto rate = config.rate; auto nclients = config.nclients; auto nreqs = config.timing_script ? config.nreqs * config.nclients : config.nreqs; workers.push_back( create_worker(0, ssl_ctx, nreqs, nclients, rate, MAX_SAMPLES)); auto start = std::chrono::steady_clock::now(); workers.back()->run(); #endif // defined(NOTHREADS) auto end = std::chrono::steady_clock::now(); auto duration = std::chrono::duration_cast(end - start); Stats stats(0, 0); for (const auto &w : workers) { const auto &s = w->stats; stats.req_todo += s.req_todo; stats.req_started += s.req_started; stats.req_done += s.req_done; stats.req_timedout += s.req_timedout; stats.req_success += s.req_success; stats.req_status_success += s.req_status_success; stats.req_failed += s.req_failed; stats.req_error += s.req_error; stats.bytes_total += s.bytes_total; stats.bytes_head += s.bytes_head; stats.bytes_head_decomp += s.bytes_head_decomp; stats.bytes_body += s.bytes_body; stats.udp_dgram_recv += s.udp_dgram_recv; stats.udp_dgram_sent += s.udp_dgram_sent; for (size_t i = 0; i < stats.status.size(); ++i) { stats.status[i] += s.status[i]; } } auto ts = process_time_stats(workers); // Requests which have not been issued due to connection errors, are // counted towards req_failed and req_error. auto req_not_issued = (stats.req_todo - stats.req_status_success - stats.req_failed); stats.req_failed += req_not_issued; stats.req_error += req_not_issued; // UI is heavily inspired by weighttp[1] and wrk[2] // // [1] https://github.com/lighttpd/weighttp // [2] https://github.com/wg/wrk double rps = 0; int64_t bps = 0; if (duration.count() > 0) { if (config.is_timing_based_mode()) { // we only want to consider the main duration if warm-up is given rps = static_cast(stats.req_success) / config.duration; bps = static_cast(static_cast(stats.bytes_total) / config.duration); } else { auto secd = std::chrono::duration_cast< std::chrono::duration>(duration); rps = static_cast(stats.req_success) / secd.count(); bps = static_cast(static_cast(stats.bytes_total) / secd.count()); } } double header_space_savings = 0.; if (stats.bytes_head_decomp > 0) { header_space_savings = 1. - static_cast(stats.bytes_head) / static_cast(stats.bytes_head_decomp); } std::cout << std::fixed << std::setprecision(2) << R"( finished in )" << util::format_duration(duration) << ", " << rps << " req/s, " << util::utos_funit(as_unsigned(bps)) << R"(B/s requests: )" << stats.req_todo << " total, " << stats.req_started << " started, " << stats.req_done << " done, " << stats.req_status_success << " succeeded, " << stats.req_failed << " failed, " << stats.req_error << " errored, " << stats.req_timedout << R"( timeout status codes: )" << stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, " << stats.status[4] << " 4xx, " << stats.status[5] << R"( 5xx traffic: )" << util::utos_funit(as_unsigned(stats.bytes_total)) << "B (" << stats.bytes_total << ") total, " << util::utos_funit(as_unsigned(stats.bytes_head)) << "B (" << stats.bytes_head << ") headers (space savings " << header_space_savings * 100 << "%), " << util::utos_funit(as_unsigned(stats.bytes_body)) << "B (" << stats.bytes_body << R"() data)" << std::endl; #ifdef ENABLE_HTTP3 if (config.is_quic()) { std::cout << "UDP datagram: " << stats.udp_dgram_sent << " sent, " << stats.udp_dgram_recv << " received" << std::endl; } #endif // defined(ENABLE_HTTP3) std::cout << R"( min max mean sd +/- sd time for request: )" << std::setw(10) << util::format_duration(ts.request.min) << " " << std::setw(10) << util::format_duration(ts.request.max) << " " << std::setw(10) << util::format_duration(ts.request.mean) << " " << std::setw(10) << util::format_duration(ts.request.sd) << std::setw(9) << util::dtos(ts.request.within_sd) << "%" << "\ntime for connect: " << std::setw(10) << util::format_duration(ts.connect.min) << " " << std::setw(10) << util::format_duration(ts.connect.max) << " " << std::setw(10) << util::format_duration(ts.connect.mean) << " " << std::setw(10) << util::format_duration(ts.connect.sd) << std::setw(9) << util::dtos(ts.connect.within_sd) << "%" << "\ntime to 1st byte: " << std::setw(10) << util::format_duration(ts.ttfb.min) << " " << std::setw(10) << util::format_duration(ts.ttfb.max) << " " << std::setw(10) << util::format_duration(ts.ttfb.mean) << " " << std::setw(10) << util::format_duration(ts.ttfb.sd) << std::setw(9) << util::dtos(ts.ttfb.within_sd) << "%" << "\nreq/s : " << std::setw(10) << ts.rps.min << " " << std::setw(10) << ts.rps.max << " " << std::setw(10) << ts.rps.mean << " " << std::setw(10) << ts.rps.sd << std::setw(9) << util::dtos(ts.rps.within_sd) << "%" << std::endl; SSL_CTX_free(ssl_ctx); if (config.log_fd != -1) { close(config.log_fd); } return 0; } } // namespace h2load int main(int argc, char **argv) { return h2load::main(argc, argv); } nghttp2-1.68.0/src/PaxHeaders/siphash_test.cc0000644000000000000000000000013215077107271016055 xustar0030 mtime=1761382073.000444106 30 atime=1761382080.105411485 30 ctime=1761382109.277299869 nghttp2-1.68.0/src/siphash_test.cc0000644000175100017510000000405515077107271016451 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2025 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "siphash_test.h" #include #include #include #include "munitxx.h" #include #include "siphash.h" namespace nghttp2 { namespace { const MunitTest tests[]{ munit_void_test(test_siphash), munit_test_end(), }; } // namespace const MunitSuite siphash_suite{ "/siphash", tests, nullptr, 1, MUNIT_SUITE_OPTION_NONE, }; void test_siphash(void) { std::array key_bytes; std::iota(std::ranges::begin(key_bytes), std::ranges::end(key_bytes), 0); std::array key; memcpy(key.data(), key_bytes.data(), key_bytes.size()); if constexpr (std::endian::native == std::endian::big) { key[0] = byteswap(key[0]); key[1] = byteswap(key[1]); } std::array input; std::iota(std::ranges::begin(input), std::ranges::end(input), 0); assert_uint64(0xa129ca6149be45e5ull, ==, siphash24(key, input)); } } // namespace nghttp2 nghttp2-1.68.0/src/PaxHeaders/shrpx_tls_test.cc0000644000000000000000000000013215077107270016443 xustar0030 mtime=1761382072.999444111 30 atime=1761382106.246310242 30 ctime=1761382109.242299971 nghttp2-1.68.0/src/shrpx_tls_test.cc0000644000175100017510000002651715077107270017046 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_tls_test.h" #include "munitxx.h" #include "shrpx_tls.h" #include "shrpx_log.h" #include "util.h" #include "template.h" #include "ssl_compat.h" using namespace nghttp2; namespace shrpx { namespace { const MunitTest tests[]{ munit_void_test(test_shrpx_tls_create_lookup_tree), munit_void_test(test_shrpx_tls_cert_lookup_tree_add_ssl_ctx), munit_void_test(test_shrpx_tls_tls_hostname_match), munit_void_test(test_shrpx_tls_verify_numeric_hostname), munit_void_test(test_shrpx_tls_verify_dns_hostname), munit_test_end(), }; } // namespace const MunitSuite tls_suite{ "/tls", tests, nullptr, 1, MUNIT_SUITE_OPTION_NONE, }; void test_shrpx_tls_create_lookup_tree(void) { auto tree = std::make_unique(); constexpr std::string_view hostnames[] = { "example.com"sv, // 0 "www.example.org"sv, // 1 "*www.example.org"sv, // 2 "xy*.host.domain"sv, // 3 "*yy.host.domain"sv, // 4 "nghttp2.sourceforge.net"sv, // 5 "sourceforge.net"sv, // 6 "sourceforge.net"sv, // 7, duplicate "*.foo.bar"sv, // 8, oo.bar is suffix of *.foo.bar "oo.bar"sv // 9 }; auto num = array_size(hostnames); for (size_t idx = 0; idx < num; ++idx) { tree->add_cert(hostnames[idx], idx); } tree->dump(); assert_ssize(0, ==, tree->lookup(hostnames[0])); assert_ssize(1, ==, tree->lookup(hostnames[1])); assert_ssize(2, ==, tree->lookup("2www.example.org"sv)); assert_ssize(-1, ==, tree->lookup("www2.example.org"sv)); assert_ssize(3, ==, tree->lookup("xy1.host.domain"sv)); // Does not match *yy.host.domain, because * must match at least 1 // character. assert_ssize(-1, ==, tree->lookup("yy.host.domain"sv)); assert_ssize(4, ==, tree->lookup("xyy.host.domain"sv)); assert_ssize(-1, ==, tree->lookup(""sv)); assert_ssize(5, ==, tree->lookup(hostnames[5])); assert_ssize(6, ==, tree->lookup(hostnames[6])); static constexpr char h6[] = "pdylay.sourceforge.net"; for (size_t i = 0; i < 7; ++i) { assert_ssize(-1, ==, tree->lookup(std::string_view{h6 + i, str_size(h6) - i})); } assert_ssize(8, ==, tree->lookup("x.foo.bar"sv)); assert_ssize(9, ==, tree->lookup(hostnames[9])); constexpr std::string_view names[] = { "rab"sv, // 1 "zab"sv, // 2 "zzub"sv, // 3 "ab"sv // 4 }; num = array_size(names); tree = std::make_unique(); for (size_t idx = 0; idx < num; ++idx) { tree->add_cert(names[idx], idx); } for (size_t i = 0; i < num; ++i) { assert_ssize((ssize_t)i, ==, tree->lookup(names[i])); } } // We use cfssl to generate key pairs. // // CA self-signed key pairs generation: // // $ cfssl genkey -initca ca.nghttp2.org.csr.json | // cfssljson -bare ca.nghttp2.org // // Create CSR: // // $ cfssl genkey test.nghttp2.org.csr.json | cfssljson -bare test.nghttp2.org // $ cfssl genkey test.example.com.csr.json | cfssljson -bare test.example.com // // Sign CSR: // // $ cfssl sign -ca ca.nghttp2.org.pem -ca-key ca.nghttp2.org-key.pem // -config=ca-config.json -profile=server test.nghttp2.org.csr | // cfssljson -bare test.nghttp2.org // // $ cfssl sign -ca ca.nghttp2.org.pem -ca-key ca.nghttp2.org-key.pem // -config=ca-config.json -profile=server test.example.com.csr | // cfssljson -bare test.example.com // void test_shrpx_tls_cert_lookup_tree_add_ssl_ctx(void) { int rv; static constexpr char nghttp2_certfile[] = NGHTTP2_SRC_DIR "/test.nghttp2.org.pem"; auto nghttp2_ssl_ctx = SSL_CTX_new(TLS_server_method()); auto nghttp2_ssl_ctx_del = defer(SSL_CTX_free, nghttp2_ssl_ctx); auto nghttp2_tls_ctx_data = std::make_unique(); SSL_CTX_set_app_data(nghttp2_ssl_ctx, nghttp2_tls_ctx_data.get()); rv = SSL_CTX_use_certificate_chain_file(nghttp2_ssl_ctx, nghttp2_certfile); assert_int(1, ==, rv); static constexpr char examples_certfile[] = NGHTTP2_SRC_DIR "/test.example.com.pem"; auto examples_ssl_ctx = SSL_CTX_new(TLS_server_method()); auto examples_ssl_ctx_del = defer(SSL_CTX_free, examples_ssl_ctx); auto examples_tls_ctx_data = std::make_unique(); SSL_CTX_set_app_data(examples_ssl_ctx, examples_tls_ctx_data.get()); rv = SSL_CTX_use_certificate_chain_file(examples_ssl_ctx, examples_certfile); assert_int(1, ==, rv); tls::CertLookupTree tree; std::vector> indexed_ssl_ctx; rv = tls::cert_lookup_tree_add_ssl_ctx(&tree, indexed_ssl_ctx, nghttp2_ssl_ctx); assert_int(0, ==, rv); rv = tls::cert_lookup_tree_add_ssl_ctx(&tree, indexed_ssl_ctx, examples_ssl_ctx); assert_int(0, ==, rv); assert_ssize(-1, ==, tree.lookup("not-used.nghttp2.org"sv)); #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL assert_ssize(0, ==, tree.lookup("www.test.nghttp2.org"sv)); assert_ssize(1, ==, tree.lookup("w.test.nghttp2.org"sv)); assert_ssize(2, ==, tree.lookup("test.nghttp2.org"sv)); #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) assert_ssize(0, ==, tree.lookup("test.nghttp2.org"sv)); assert_ssize(1, ==, tree.lookup("w.test.nghttp2.org"sv)); assert_ssize(2, ==, tree.lookup("www.test.nghttp2.org"sv)); #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) assert_ssize(3, ==, tree.lookup("test.example.com"sv)); } template bool tls_hostname_match_wrapper(const char (&pattern)[N], const char (&hostname)[M]) { return tls::tls_hostname_match(std::string_view{pattern, N}, std::string_view{hostname, M}); } void test_shrpx_tls_tls_hostname_match(void) { assert_true(tls_hostname_match_wrapper("example.com", "example.com")); assert_true(tls_hostname_match_wrapper("example.com", "EXAMPLE.com")); // check wildcard assert_true(tls_hostname_match_wrapper("*.example.com", "www.example.com")); assert_true(tls_hostname_match_wrapper("*w.example.com", "www.example.com")); assert_true( tls_hostname_match_wrapper("www*.example.com", "www1.example.com")); assert_true( tls_hostname_match_wrapper("www*.example.com", "WWW12.EXAMPLE.com")); // at least 2 dots are required after '*' assert_false(tls_hostname_match_wrapper("*.com", "example.com")); assert_false(tls_hostname_match_wrapper("*", "example.com")); // '*' must be in left most label assert_false( tls_hostname_match_wrapper("blog.*.example.com", "blog.my.example.com")); // prefix is wrong assert_false( tls_hostname_match_wrapper("client*.example.com", "server.example.com")); // '*' must match at least one character assert_false( tls_hostname_match_wrapper("www*.example.com", "www.example.com")); assert_false(tls_hostname_match_wrapper("example.com", "nghttp2.org")); assert_false(tls_hostname_match_wrapper("www.example.com", "example.com")); assert_false(tls_hostname_match_wrapper("example.com", "www.example.com")); } static X509 *load_cert(const char *path) { auto f = fopen(path, "r"); auto cert = PEM_read_X509(f, nullptr, nullptr, nullptr); fclose(f); return cert; } static Address parse_addr(const char *ipaddr) { addrinfo hints{}; hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_NUMERICHOST; #ifdef AI_NUMERICSERV hints.ai_flags |= AI_NUMERICSERV; #endif // defined(AI_NUMERICSERV) addrinfo *res = nullptr; auto rv = getaddrinfo(ipaddr, "443", &hints, &res); assert_int(0, ==, rv); assert_not_null(res); Address addr; addr.len = res->ai_addrlen; memcpy(&addr.su, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); return addr; } void test_shrpx_tls_verify_numeric_hostname(void) { { // Successful IPv4 address match in SAN static constexpr auto ipaddr = "127.0.0.1"sv; auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/verify_hostname.crt"); auto addr = parse_addr(ipaddr.data()); auto rv = tls::verify_numeric_hostname(cert, ipaddr, &addr); assert_int(0, ==, rv); X509_free(cert); } { // Successful IPv6 address match in SAN static constexpr auto ipaddr = "::1"sv; auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/verify_hostname.crt"); auto addr = parse_addr(ipaddr.data()); auto rv = tls::verify_numeric_hostname(cert, ipaddr, &addr); assert_int(0, ==, rv); X509_free(cert); } { // Unsuccessful IPv4 address match in SAN static constexpr auto ipaddr = "192.168.0.127"sv; auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/verify_hostname.crt"); auto addr = parse_addr(ipaddr.data()); auto rv = tls::verify_numeric_hostname(cert, ipaddr, &addr); assert_int(-1, ==, rv); X509_free(cert); } { // CommonName is not used if SAN is available static constexpr auto ipaddr = "192.168.0.1"sv; auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/ipaddr.crt"); auto addr = parse_addr(ipaddr.data()); auto rv = tls::verify_numeric_hostname(cert, ipaddr, &addr); assert_int(-1, ==, rv); X509_free(cert); } { // Successful IPv4 address match in CommonName static constexpr auto ipaddr = "127.0.0.1"sv; auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/nosan_ip.crt"); auto addr = parse_addr(ipaddr.data()); auto rv = tls::verify_numeric_hostname(cert, ipaddr, &addr); assert_int(0, ==, rv); X509_free(cert); } } void test_shrpx_tls_verify_dns_hostname(void) { { // Successful exact DNS name match in SAN auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/verify_hostname.crt"); auto rv = tls::verify_dns_hostname(cert, "nghttp2.example.com"sv); assert_int(0, ==, rv); X509_free(cert); } { // Successful wildcard DNS name match in SAN auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/verify_hostname.crt"); auto rv = tls::verify_dns_hostname(cert, "www.nghttp2.example.com"sv); assert_int(0, ==, rv); X509_free(cert); } { // CommonName is not used if SAN is available. auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/verify_hostname.crt"); auto rv = tls::verify_dns_hostname(cert, "localhost"sv); assert_int(-1, ==, rv); X509_free(cert); } { // Successful DNS name match in CommonName auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/nosan.crt"); auto rv = tls::verify_dns_hostname(cert, "localhost"sv); assert_int(0, ==, rv); X509_free(cert); } } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_downstream_queue.cc0000644000000000000000000000013115077107270020170 xustar0030 mtime=1761382072.993444139 29 atime=1761382106.10831085 30 ctime=1761382109.105300367 nghttp2-1.68.0/src/shrpx_downstream_queue.cc0000644000175100017510000001216615077107270020567 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_downstream_queue.h" #include #include #include "shrpx_downstream.h" namespace shrpx { DownstreamQueue::HostEntry::HostEntry(ImmutableString &&key) : key(std::move(key)), num_active(0) {} DownstreamQueue::DownstreamQueue(size_t conn_max_per_host, bool unified_host) : conn_max_per_host_(conn_max_per_host == 0 ? std::numeric_limits::max() : conn_max_per_host), unified_host_(unified_host) {} DownstreamQueue::~DownstreamQueue() { dlist_delete_all(downstreams_); for (auto &p : host_entries_) { auto &ent = p.second; dlist_delete_all(ent.blocked); } } void DownstreamQueue::add_pending(std::unique_ptr downstream) { downstream->set_dispatch_state(DispatchState::PENDING); downstreams_.append(downstream.release()); } void DownstreamQueue::mark_failure(Downstream *downstream) { downstream->set_dispatch_state(DispatchState::FAILURE); } DownstreamQueue::HostEntry & DownstreamQueue::find_host_entry(const std::string_view &host) { auto itr = host_entries_.find(host); if (itr == std::ranges::end(host_entries_)) { auto key = ImmutableString{host}; auto key_ref = as_string_view(key); std::tie(itr, std::ignore) = host_entries_.emplace(key_ref, HostEntry(std::move(key))); } return (*itr).second; } std::string_view DownstreamQueue::make_host_key(const std::string_view &host) const { return unified_host_ ? ""sv : host; } std::string_view DownstreamQueue::make_host_key(Downstream *downstream) const { return make_host_key(downstream->request().authority); } void DownstreamQueue::mark_active(Downstream *downstream) { auto &ent = find_host_entry(make_host_key(downstream)); ++ent.num_active; downstream->set_dispatch_state(DispatchState::ACTIVE); } void DownstreamQueue::mark_blocked(Downstream *downstream) { auto &ent = find_host_entry(make_host_key(downstream)); downstream->set_dispatch_state(DispatchState::BLOCKED); auto link = new BlockedLink{}; downstream->attach_blocked_link(link); ent.blocked.append(link); } bool DownstreamQueue::can_activate(const std::string_view &host) const { auto itr = host_entries_.find(make_host_key(host)); if (itr == std::ranges::end(host_entries_)) { return true; } auto &ent = (*itr).second; return ent.num_active < conn_max_per_host_; } namespace { bool remove_host_entry_if_empty(const DownstreamQueue::HostEntry &ent, DownstreamQueue::HostEntryMap &host_entries, const std::string_view &host) { if (ent.blocked.empty() && ent.num_active == 0) { host_entries.erase(host); return true; } return false; } } // namespace Downstream *DownstreamQueue::remove_and_get_blocked(Downstream *downstream, bool next_blocked) { // Delete downstream when this function returns. auto delptr = std::unique_ptr(downstream); downstreams_.remove(downstream); auto host = make_host_key(downstream); auto &ent = find_host_entry(host); if (downstream->get_dispatch_state() == DispatchState::ACTIVE) { --ent.num_active; } else { // For those downstreams deleted while in blocked state auto link = downstream->detach_blocked_link(); if (link) { ent.blocked.remove(link); delete link; } } if (remove_host_entry_if_empty(ent, host_entries_, host)) { return nullptr; } if (!next_blocked || ent.num_active >= conn_max_per_host_) { return nullptr; } auto link = ent.blocked.head; if (!link) { return nullptr; } auto next_downstream = link->downstream; auto link2 = next_downstream->detach_blocked_link(); // This is required with --disable-assert. (void)link2; assert(link2 == link); ent.blocked.remove(link); delete link; remove_host_entry_if_empty(ent, host_entries_, host); return next_downstream; } Downstream *DownstreamQueue::get_downstreams() const { return downstreams_.head; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx.cc0000644000000000000000000000013115077107270014521 xustar0030 mtime=1761382072.990444153 29 atime=1761382106.24231026 30 ctime=1761382109.238299982 nghttp2-1.68.0/src/shrpx.cc0000644000175100017510000056110515077107270015122 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx.h" #include #include #include #ifdef HAVE_SYS_SOCKET_H # include #endif // defined(HAVE_SYS_SOCKET_H) #include #ifdef HAVE_NETDB_H # include #endif // defined(HAVE_NETDB_H) #include #ifdef HAVE_NETINET_IN_H # include #endif // defined(HAVE_NETINET_IN_H) #ifdef HAVE_ARPA_INET_H # include #endif // defined(HAVE_ARPA_INET_H) #ifdef HAVE_UNISTD_H # include #endif // defined(HAVE_UNISTD_H) #include #ifdef HAVE_SYSLOG_H # include #endif // defined(HAVE_SYSLOG_H) #ifdef HAVE_LIMITS_H # include #endif // defined(HAVE_LIMITS_H) #ifdef HAVE_SYS_TIME_H # include #endif // defined(HAVE_SYS_TIME_H) #include #ifdef HAVE_LIBSYSTEMD # include #endif // defined(HAVE_LIBSYSTEMD) #ifdef HAVE_LIBBPF # include #endif // defined(HAVE_LIBBPF) #include #include #include #include #include #include #include #include #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include # include # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include #include #ifdef ENABLE_HTTP3 # include # include # if defined(HAVE_LIBNGTCP2_CRYPTO_QUICTLS) || \ defined(HAVE_LIBNGTCP2_CRYPTO_LIBRESSL) # include # endif // defined(HAVE_LIBNGTCP2_CRYPTO_QUICTLS) || // defined(HAVE_LIBNGTCP2_CRYPTO_LIBRESSL) # ifdef HAVE_LIBNGTCP2_CRYPTO_OSSL # include # endif // defined(HAVE_LIBNGTCP2_CRYPTO_OSSL) #endif // defined(ENABLE_HTTP3) #include "shrpx_config.h" #include "shrpx_tls.h" #include "shrpx_log_config.h" #include "shrpx_worker.h" #include "shrpx_http2_upstream.h" #include "shrpx_http2_session.h" #include "shrpx_worker_process.h" #include "shrpx_process.h" #include "shrpx_signal.h" #include "shrpx_connection.h" #include "shrpx_log.h" #include "shrpx_http.h" #include "util.h" #include "app_helper.h" #include "tls.h" #include "template.h" #include "allocator.h" #include "xsi_strerror.h" extern char **environ; using namespace nghttp2; namespace shrpx { // Prefix of environment variables to tell new binary the listening // UNIX domain socket's file descriptor and path. They are not // close-on-exec. The value must be comma separated 3 parameters: // unix,,. is file descriptor. is a path to // UNIX domain socket. constexpr auto ENV_ACCEPT_PREFIX = "NGHTTPX_ACCEPT_"sv; // This environment variable contains PID of the original main // process, assuming that it created this main process as a result of // SIGUSR2. The new main process is expected to send QUIT signal to // the original main process to shut it down gracefully. constexpr auto ENV_ORIG_PID = "NGHTTPX_ORIG_PID"sv; // Prefix of environment variables to tell new binary the QUIC IPC // file descriptor and Worker ID of the lingering worker process. The // value must be comma separated parameters: // // ,,,..., // // is the file descriptor. is the I-th Worker ID // in hex encoded string. constexpr auto ENV_QUIC_WORKER_PROCESS_PREFIX = "NGHTTPX_QUIC_WORKER_PROCESS_"sv; // This configuration is fixed at the first startup of the main // process, and does not change after subsequent reloadings. struct StartupConfig { // This contains all options given in command-line. std::vector> cmdcfgs; // The current working directory where this process started. char *cwd; // The pointer to original argv (not sure why we have this?) char **original_argv; // The pointer to argv, this is a deep copy of original argv. char **argv; // The number of elements in argv. size_t argc; }; namespace { StartupConfig suconfig; } // namespace struct InheritedUNIXDomainAddr { // UNIX domain socket path. std::string_view path; int fd; bool used; }; namespace { void signal_cb(struct ev_loop *loop, ev_signal *w, int revents); } // namespace namespace { void worker_process_child_cb(struct ev_loop *loop, ev_child *w, int revents); } // namespace struct WorkerProcess { WorkerProcess(struct ev_loop *loop, pid_t worker_pid, int ipc_fd #ifdef ENABLE_HTTP3 , int quic_ipc_fd, std::vector worker_ids, uint16_t seq #endif // defined(ENABLE_HTTP3) ) : loop(loop), worker_pid(worker_pid), ipc_fd(ipc_fd) #ifdef ENABLE_HTTP3 , quic_ipc_fd(quic_ipc_fd), worker_ids(std::move(worker_ids)), seq(seq) #endif // defined(ENABLE_HTTP3) { ev_child_init(&worker_process_childev, worker_process_child_cb, worker_pid, 0); worker_process_childev.data = this; ev_child_start(loop, &worker_process_childev); } ~WorkerProcess() { ev_child_stop(loop, &worker_process_childev); #ifdef ENABLE_HTTP3 if (quic_ipc_fd != -1) { close(quic_ipc_fd); } #endif // defined(ENABLE_HTTP3) if (ipc_fd != -1) { shutdown(ipc_fd, SHUT_WR); close(ipc_fd); } } ev_child worker_process_childev; struct ev_loop *loop; pid_t worker_pid; int ipc_fd; std::chrono::steady_clock::time_point termination_deadline; #ifdef ENABLE_HTTP3 int quic_ipc_fd; std::vector worker_ids; uint16_t seq; #endif // defined(ENABLE_HTTP3) }; namespace { void reload_config(); } // namespace namespace { std::deque> worker_processes; #ifdef ENABLE_HTTP3 uint16_t worker_process_seq; #endif // defined(ENABLE_HTTP3) } // namespace namespace { ev_timer worker_process_grace_period_timer; } // namespace namespace { void worker_process_grace_period_timercb(struct ev_loop *loop, ev_timer *w, int revents) { auto now = std::chrono::steady_clock::now(); auto next_repeat = std::chrono::steady_clock::duration::zero(); auto r = std::ranges::remove_if(worker_processes, [&now, &next_repeat](auto &wp) { if (wp->termination_deadline.time_since_epoch().count() == 0) { return false; } auto d = wp->termination_deadline - now; if (d.count() > 0) { if (next_repeat == std::chrono::steady_clock::duration::zero() || d < next_repeat) { next_repeat = d; } return false; } LOG(NOTICE) << "Deleting worker process pid=" << wp->worker_pid << " because its grace shutdown period is over"; return true; }); worker_processes.erase(std::ranges::begin(r), std::ranges::end(r)); if (next_repeat.count() > 0) { w->repeat = util::ev_tstamp_from(next_repeat); ev_timer_again(loop, w); return; } ev_timer_stop(loop, w); } } // namespace namespace { void worker_process_set_termination_deadline(WorkerProcess *wp, struct ev_loop *loop) { auto config = get_config(); if (!(config->worker_process_grace_shutdown_period > 0.)) { return; } wp->termination_deadline = std::chrono::steady_clock::now() + util::duration_from(config->worker_process_grace_shutdown_period); if (!ev_is_active(&worker_process_grace_period_timer)) { worker_process_grace_period_timer.repeat = config->worker_process_grace_shutdown_period; ev_timer_again(loop, &worker_process_grace_period_timer); } } } // namespace namespace { void worker_process_add(std::unique_ptr wp) { worker_processes.push_back(std::move(wp)); } } // namespace namespace { void worker_process_remove(const WorkerProcess *wp, struct ev_loop *loop) { auto it = std::ranges::find_if(worker_processes, [wp](auto &s) { return s.get() == wp; }); if (it != std::ranges::end(worker_processes)) { worker_processes.erase(it); if (worker_processes.empty()) { ev_timer_stop(loop, &worker_process_grace_period_timer); } } } } // namespace namespace { void worker_process_adjust_limit() { auto config = get_config(); if (config->max_worker_processes && worker_processes.size() > config->max_worker_processes) { worker_processes.pop_front(); } } } // namespace namespace { void worker_process_remove_all(struct ev_loop *loop) { std::deque>().swap(worker_processes); ev_timer_stop(loop, &worker_process_grace_period_timer); } } // namespace namespace { // Send signal |signum| to all worker processes, and clears // worker_processes. void worker_process_kill(int signum, struct ev_loop *loop) { for (auto &s : worker_processes) { if (s->worker_pid == -1) { continue; } kill(s->worker_pid, signum); } worker_process_remove_all(loop); } } // namespace namespace { int save_pid() { std::array errbuf; auto config = get_config(); static constexpr auto SUFFIX = ".XXXXXX"sv; auto &pid_file = config->pid_file; auto len = config->pid_file.size() + SUFFIX.size(); auto buf = std::make_unique(len + 1); auto p = buf.get(); p = std::ranges::copy(pid_file, p).out; p = std::ranges::copy(SUFFIX, p).out; *p = '\0'; auto temp_path = buf.get(); auto fd = mkstemp(temp_path); if (fd == -1) { auto error = errno; LOG(ERROR) << "Could not save PID to file " << pid_file << ": " << xsi_strerror(error, errbuf.data(), errbuf.size()); return -1; } auto content = util::utos(as_unsigned(config->pid)) + '\n'; if (write(fd, content.c_str(), content.size()) == -1) { auto error = errno; LOG(ERROR) << "Could not save PID to file " << pid_file << ": " << xsi_strerror(error, errbuf.data(), errbuf.size()); return -1; } if (fsync(fd) == -1) { auto error = errno; LOG(ERROR) << "Could not save PID to file " << pid_file << ": " << xsi_strerror(error, errbuf.data(), errbuf.size()); return -1; } close(fd); if (rename(temp_path, pid_file.data()) == -1) { auto error = errno; LOG(ERROR) << "Could not save PID to file " << pid_file << ": " << xsi_strerror(error, errbuf.data(), errbuf.size()); unlink(temp_path); return -1; } if (config->uid != 0) { if (chown(pid_file.data(), config->uid, config->gid) == -1) { auto error = errno; LOG(WARN) << "Changing owner of pid file " << pid_file << " failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); } } return 0; } } // namespace namespace { void shrpx_sd_notifyf(int unset_environment, const char *format, ...) { #ifdef HAVE_LIBSYSTEMD va_list args; va_start(args, format); sd_notifyf(unset_environment, format, va_arg(args, char *)); va_end(args); #endif // defined(HAVE_LIBSYSTEMD) } } // namespace namespace { void exec_binary() { int rv; sigset_t oldset; std::array errbuf; LOG(NOTICE) << "Executing new binary"; shrpx_sd_notifyf(0, "RELOADING=1"); rv = shrpx_signal_block_all(&oldset); if (rv != 0) { auto error = errno; LOG(ERROR) << "Blocking all signals failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); return; } auto pid = fork(); if (pid != 0) { if (pid == -1) { auto error = errno; LOG(ERROR) << "fork() failed errno=" << error; } else { // update PID tracking information in systemd shrpx_sd_notifyf(0, "MAINPID=%d\n", pid); } rv = shrpx_signal_set(&oldset); if (rv != 0) { auto error = errno; LOG(FATAL) << "Restoring signal mask failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); exit(EXIT_FAILURE); } return; } // child process shrpx_signal_unset_main_proc_ign_handler(); rv = shrpx_signal_unblock_all(); if (rv != 0) { auto error = errno; LOG(ERROR) << "Unblocking all signals failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); nghttp2_Exit(EXIT_FAILURE); } auto exec_path = util::get_exec_path(suconfig.argc, suconfig.argv, suconfig.cwd); if (!exec_path) { LOG(ERROR) << "Could not resolve the executable path"; nghttp2_Exit(EXIT_FAILURE); } auto argv = std::make_unique(suconfig.argc + 1); argv[0] = exec_path; for (size_t i = 1; i < suconfig.argc; ++i) { argv[i] = suconfig.argv[i]; } argv[suconfig.argc] = nullptr; size_t envlen = 0; for (char **p = environ; *p; ++p, ++envlen) ; auto config = get_config(); auto &listenerconf = config->conn.listener; // 2 for ENV_ORIG_PID and terminal nullptr. auto envp = std::make_unique(envlen + listenerconf.addrs.size() + worker_processes.size() + 2); size_t envidx = 0; std::vector fd_envs; for (size_t i = 0; i < listenerconf.addrs.size(); ++i) { auto &addr = listenerconf.addrs[i]; if (!addr.host_unix) { continue; } auto s = std::string{ENV_ACCEPT_PREFIX}; s += util::utos(i + 1); s += "=unix,"; s += util::utos(as_unsigned(addr.fd)); s += ','; s += addr.host; fd_envs.emplace_back(s); envp[envidx++] = const_cast(fd_envs.back().c_str()); } auto ipc_fd_str = std::string{ENV_ORIG_PID}; ipc_fd_str += '='; ipc_fd_str += util::utos(as_unsigned(config->pid)); envp[envidx++] = const_cast(ipc_fd_str.c_str()); #ifdef ENABLE_HTTP3 std::vector quic_lwps; for (size_t i = 0; i < worker_processes.size(); ++i) { auto &wp = worker_processes[i]; auto s = std::string{ENV_QUIC_WORKER_PROCESS_PREFIX}; s += util::utos(i + 1); s += '='; s += util::utos(as_unsigned(wp->quic_ipc_fd)); for (auto &wid : wp->worker_ids) { s += ','; s += util::format_hex(as_uint8_span(std::span{&wid, 1})); } quic_lwps.emplace_back(s); envp[envidx++] = const_cast(quic_lwps.back().c_str()); } #endif // defined(ENABLE_HTTP3) for (size_t i = 0; i < envlen; ++i) { auto env = std::string_view{environ[i]}; if (util::starts_with(env, ENV_ACCEPT_PREFIX) || util::starts_with(env, ENV_ORIG_PID) || util::starts_with(env, ENV_QUIC_WORKER_PROCESS_PREFIX)) { continue; } envp[envidx++] = environ[i]; } envp[envidx++] = nullptr; if (LOG_ENABLED(INFO)) { LOG(INFO) << "cmdline"; for (size_t i = 0; argv[i]; ++i) { LOG(INFO) << i << ": " << argv[i]; } LOG(INFO) << "environ"; for (size_t i = 0; envp[i]; ++i) { LOG(INFO) << i << ": " << envp[i]; } } // restores original stderr restore_original_fds(); // reloading finished shrpx_sd_notifyf(0, "READY=1"); if (execve(argv[0], argv.get(), envp.get()) == -1) { auto error = errno; LOG(ERROR) << "execve failed: errno=" << error; nghttp2_Exit(EXIT_FAILURE); } } } // namespace namespace { void ipc_send(WorkerProcess *wp, uint8_t ipc_event) { std::array errbuf; ssize_t nwrite; while ((nwrite = write(wp->ipc_fd, &ipc_event, 1)) == -1 && errno == EINTR) ; if (nwrite < 0) { auto error = errno; LOG(ERROR) << "Could not send IPC event to worker process: " << xsi_strerror(error, errbuf.data(), errbuf.size()); return; } if (nwrite == 0) { LOG(ERROR) << "Could not send IPC event due to pipe overflow"; return; } } } // namespace namespace { void reopen_log(WorkerProcess *wp) { LOG(NOTICE) << "Reopening log files: main process"; auto config = get_config(); auto &loggingconf = config->logging; (void)reopen_log_files(loggingconf); redirect_stderr_to_errorlog(loggingconf); ipc_send(wp, SHRPX_IPC_REOPEN_LOG); } } // namespace namespace { void signal_cb(struct ev_loop *loop, ev_signal *w, int revents) { switch (w->signum) { case REOPEN_LOG_SIGNAL: for (auto &wp : worker_processes) { reopen_log(wp.get()); } return; case EXEC_BINARY_SIGNAL: exec_binary(); return; case GRACEFUL_SHUTDOWN_SIGNAL: { auto &listenerconf = get_config()->conn.listener; for (auto &addr : listenerconf.addrs) { if (addr.host_unix) { close(addr.fd); } } for (auto &wp : worker_processes) { ipc_send(wp.get(), SHRPX_IPC_GRACEFUL_SHUTDOWN); worker_process_set_termination_deadline(wp.get(), loop); } return; } case RELOAD_SIGNAL: reload_config(); return; default: worker_process_kill(w->signum, loop); ev_break(loop); return; } } } // namespace namespace { void worker_process_child_cb(struct ev_loop *loop, ev_child *w, int revents) { auto wp = static_cast(w->data); log_chld(w->rpid, w->rstatus, "Worker process"); worker_process_remove(wp, loop); if (worker_processes.empty()) { ev_break(loop); } } } // namespace namespace { int create_unix_domain_server_socket( UpstreamAddr &faddr, std::vector &iaddrs) { std::array errbuf; auto found = std::ranges::find_if(iaddrs, [&faddr](const auto &ia) { return !ia.used && ia.path == faddr.host; }); if (found != std::ranges::end(iaddrs)) { LOG(NOTICE) << "Listening on UNIX domain socket " << faddr.host << (faddr.tls ? ", tls" : ""); (*found).used = true; faddr.fd = (*found).fd; faddr.hostport = "localhost"sv; return 0; } #ifdef SOCK_NONBLOCK auto fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); if (fd == -1) { auto error = errno; LOG(FATAL) << "socket() syscall failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); return -1; } #else // !defined(SOCK_NONBLOCK) auto fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd == -1) { auto error = errno; LOG(FATAL) << "socket() syscall failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); return -1; } util::make_socket_nonblocking(fd); #endif // !defined(SOCK_NONBLOCK) int val = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, static_cast(sizeof(val))) == -1) { auto error = errno; LOG(FATAL) << "Failed to set SO_REUSEADDR option to listener socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); return -1; } sockaddr_union addr; addr.un.sun_family = AF_UNIX; if (faddr.host.size() + 1 > sizeof(addr.un.sun_path)) { LOG(FATAL) << "UNIX domain socket path " << faddr.host << " is too long > " << sizeof(addr.un.sun_path); close(fd); return -1; } // copy path including terminal NULL std::ranges::copy_n(faddr.host.data(), as_signed(faddr.host.size() + 1), addr.un.sun_path); // unlink (remove) already existing UNIX domain socket path unlink(faddr.host.data()); if (bind(fd, &addr.sa, sizeof(addr.un)) != 0) { auto error = errno; LOG(FATAL) << "Failed to bind UNIX domain socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); return -1; } auto &listenerconf = get_config()->conn.listener; if (listen(fd, listenerconf.backlog) != 0) { auto error = errno; LOG(FATAL) << "Failed to listen to UNIX domain socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); return -1; } LOG(NOTICE) << "Listening on UNIX domain socket " << faddr.host << (faddr.tls ? ", tls" : ""); faddr.fd = fd; faddr.hostport = "localhost"sv; return 0; } } // namespace namespace { // Returns array of InheritedUNIXDomainAddr constructed from |config|. This // function is intended to be used when reloading configuration, and // |config| is usually a current configuration. std::vector get_inherited_unix_domain_socket_from_config(BlockAllocator &balloc, Config *config) { auto &listenerconf = config->conn.listener; std::vector iaddrs; for (auto &addr : listenerconf.addrs) { if (!addr.host_unix) { continue; } iaddrs.emplace_back(InheritedUNIXDomainAddr{ .path = addr.host, .fd = addr.fd, }); } return iaddrs; } } // namespace namespace { // Returns array of InheritedUNIXDomainAddr constructed from environment // variables. std::vector get_inherited_unix_domain_socket_from_env(Config *config) { std::vector iaddrs; for (size_t i = 1;; ++i) { auto name = std::string{ENV_ACCEPT_PREFIX}; name += util::utos(i); auto env = getenv(name.c_str()); if (!env) { break; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "Read env " << name << "=" << env; } auto end_type = strchr(env, ','); if (!end_type) { continue; } auto type = std::string_view(env, end_type); auto value = end_type + 1; if (type != "unix"sv) { continue; } auto endfd = strchr(value, ','); if (!endfd) { continue; } auto fd = util::parse_uint(std::string_view{value, endfd}); if (!fd) { LOG(WARN) << "Could not parse file descriptor from " << std::string_view{value, endfd}; continue; } auto path = endfd + 1; if (strlen(path) == 0) { LOG(WARN) << "Empty UNIX domain socket path (fd=" << *fd << ")"; close(static_cast(*fd)); continue; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "Inherit UNIX domain socket fd=" << *fd << ", path=" << path; } iaddrs.emplace_back(InheritedUNIXDomainAddr{ .path = make_string_ref(config->balloc, std::string_view{path}), .fd = static_cast(*fd), }); } return iaddrs; } } // namespace namespace { // Closes all sockets which are not reused. void close_unused_inherited_addr( const std::vector &iaddrs) { for (auto &ia : iaddrs) { if (ia.used) { continue; } close(ia.fd); } } } // namespace namespace { // Returns the PID of the original main process from environment // variable ENV_ORIG_PID. pid_t get_orig_pid_from_env() { auto s = getenv(ENV_ORIG_PID.data()); if (s == nullptr) { return -1; } return static_cast(util::parse_uint(s).value_or(-1)); } } // namespace #ifdef ENABLE_HTTP3 namespace { std::vector inherited_quic_lingering_worker_processes; } // namespace namespace { std::vector get_inherited_quic_lingering_worker_process_from_env() { std::vector lwps; for (size_t i = 1;; ++i) { auto name = std::string{ENV_QUIC_WORKER_PROCESS_PREFIX}; name += util::utos(i); auto env = getenv(name.c_str()); if (!env) { break; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "Read env " << name << "=" << env; } auto envend = env + strlen(env); auto end_fd = std::ranges::find(env, envend, ','); if (end_fd == envend) { continue; } auto fd = util::parse_uint(std::string_view{env, end_fd}); if (!fd) { LOG(WARN) << "Could not parse file descriptor from " << std::string_view{env, static_cast(end_fd - env)}; continue; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "Inherit worker process QUIC IPC socket fd=" << *fd; } util::make_socket_closeonexec(static_cast(*fd)); std::vector worker_ids; auto p = end_fd + 1; for (;;) { auto end = std::ranges::find(p, envend, ','); auto hex_wid = std::string_view{p, end}; if (hex_wid.size() != SHRPX_QUIC_WORKER_IDLEN * 2 || !util::is_hex_string(hex_wid)) { LOG(WARN) << "Found invalid WorkerID=" << hex_wid; break; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "Inherit worker process WorkerID=" << hex_wid; } worker_ids.emplace_back(); util::decode_hex(hex_wid, reinterpret_cast(&worker_ids.back())); if (end == envend) { break; } p = end + 1; } lwps.emplace_back(std::move(worker_ids), *fd); } if (!lwps.empty()) { const auto &lwp = lwps.back(); if (!lwp.worker_ids.empty() && worker_process_seq <= lwp.worker_ids[0].worker_process) { worker_process_seq = lwp.worker_ids[0].worker_process; ++worker_process_seq; } } return lwps; } } // namespace #endif // defined(ENABLE_HTTP3) namespace { int create_unix_domain_listener_socket( Config *config, std::vector &iaddrs) { std::array errbuf; auto &listenerconf = config->conn.listener; for (auto &addr : listenerconf.addrs) { if (!addr.host_unix) { continue; } if (create_unix_domain_server_socket(addr, iaddrs) != 0) { return -1; } if (config->uid != 0) { // fd is not associated to inode, so we cannot use fchown(2) // here. https://lkml.org/lkml/2004/11/1/84 if (chown(addr.host.data(), config->uid, config->gid) == -1) { auto error = errno; LOG(WARN) << "Changing owner of UNIX domain socket " << addr.host << " failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); } } } return 0; } } // namespace namespace { int call_daemon() { #ifdef HAVE_LIBSYSTEMD if (sd_booted() && (getenv("NOTIFY_SOCKET") != nullptr)) { LOG(NOTICE) << "Daemonising disabled under systemd"; chdir("/"); return 0; } #endif // defined(HAVE_LIBSYSTEMD) return util::daemonize(0, 0); } } // namespace namespace { // Opens IPC socket used to communicate with worker proess. The // communication is unidirectional; that is main process sends // messages to the worker process. On success, ipc_fd[0] is for // reading, and ipc_fd[1] for writing, just like pipe(2). int create_ipc_socket(std::span ipc_fd) { std::array errbuf; int rv; rv = pipe(ipc_fd.data()); if (rv == -1) { auto error = errno; LOG(WARN) << "Failed to create pipe to communicate worker process: " << xsi_strerror(error, errbuf.data(), errbuf.size()); return -1; } for (auto fd : ipc_fd) { util::make_socket_nonblocking(fd); util::make_socket_closeonexec(fd); } return 0; } } // namespace namespace { int create_worker_process_ready_ipc_socket(std::span ipc_fd) { std::array errbuf; int rv; rv = socketpair(AF_UNIX, SOCK_DGRAM, 0, ipc_fd.data()); if (rv == -1) { auto error = errno; LOG(WARN) << "Failed to create socket pair to communicate worker process " "readiness: " << xsi_strerror(error, errbuf.data(), errbuf.size()); return -1; } for (auto fd : ipc_fd) { util::make_socket_closeonexec(fd); } util::make_socket_nonblocking(ipc_fd[0]); return 0; } } // namespace #ifdef ENABLE_HTTP3 namespace { int create_quic_ipc_socket(std::span quic_ipc_fd) { std::array errbuf; int rv; rv = socketpair(AF_UNIX, SOCK_DGRAM, 0, quic_ipc_fd.data()); if (rv == -1) { auto error = errno; LOG(WARN) << "Failed to create socket pair to communicate worker process: " << xsi_strerror(error, errbuf.data(), errbuf.size()); return -1; } for (auto fd : quic_ipc_fd) { util::make_socket_nonblocking(fd); } return 0; } } // namespace namespace { int generate_worker_id(std::vector &worker_ids, uint16_t wp_seq, const Config *config) { auto &apiconf = config->api; auto &quicconf = config->quic; size_t num_wid; if (config->single_thread) { num_wid = 1; } else { num_wid = config->num_worker; // API endpoint occupies the one dedicated worker thread. // Although such worker never gets QUIC traffic, we create Worker // ID for it to make code a bit simpler. if (apiconf.enabled) { ++num_wid; } } worker_ids.resize(num_wid); uint16_t idx = 0; for (auto &wid : worker_ids) { wid.server = quicconf.server_id; wid.worker_process = wp_seq; wid.thread = idx++; } return 0; } } // namespace namespace { std::vector collect_quic_lingering_worker_processes() { std::vector quic_lwps{ std::ranges::begin(inherited_quic_lingering_worker_processes), std::ranges::end(inherited_quic_lingering_worker_processes)}; for (auto &wp : worker_processes) { quic_lwps.emplace_back(wp->worker_ids, wp->quic_ipc_fd); } return quic_lwps; } } // namespace #endif // defined(ENABLE_HTTP3) namespace { ev_signal reopen_log_signalev; ev_signal exec_binary_signalev; ev_signal graceful_shutdown_signalev; ev_signal reload_signalev; } // namespace namespace { void start_signal_watchers(struct ev_loop *loop) { ev_signal_init(&reopen_log_signalev, signal_cb, REOPEN_LOG_SIGNAL); ev_signal_start(loop, &reopen_log_signalev); ev_signal_init(&exec_binary_signalev, signal_cb, EXEC_BINARY_SIGNAL); ev_signal_start(loop, &exec_binary_signalev); ev_signal_init(&graceful_shutdown_signalev, signal_cb, GRACEFUL_SHUTDOWN_SIGNAL); ev_signal_start(loop, &graceful_shutdown_signalev); ev_signal_init(&reload_signalev, signal_cb, RELOAD_SIGNAL); ev_signal_start(loop, &reload_signalev); } } // namespace namespace { void shutdown_signal_watchers(struct ev_loop *loop) { ev_signal_stop(loop, &reload_signalev); ev_signal_stop(loop, &graceful_shutdown_signalev); ev_signal_stop(loop, &exec_binary_signalev); ev_signal_stop(loop, &reopen_log_signalev); } } // namespace namespace { // A pair of connected socket with which a worker process tells main // process that it is ready for service. A worker process writes its // PID to worker_process_ready_ipc_fd[1] and main process reads it // from worker_process_ready_ipc_fd[0]. std::array worker_process_ready_ipc_fd; } // namespace namespace { ev_io worker_process_ready_ipcev; } // namespace namespace { // PID received via NGHTTPX_ORIG_PID environment variable. pid_t orig_pid = -1; } // namespace namespace { void worker_process_ready_ipc_readcb(struct ev_loop *loop, ev_io *w, int revents) { std::array buf; ssize_t nread; while ((nread = read(w->fd, buf.data(), buf.size())) == -1 && errno == EINTR) ; if (nread == -1) { std::array errbuf; auto error = errno; LOG(ERROR) << "Failed to read data from worker process ready IPC channel: " << xsi_strerror(error, errbuf.data(), errbuf.size()); return; } if (nread == 0) { return; } if (nread != sizeof(pid_t)) { LOG(ERROR) << "Read " << nread << " bytes from worker process ready IPC channel"; return; } pid_t pid; memcpy(&pid, buf.data(), sizeof(pid)); LOG(NOTICE) << "Worker process pid=" << pid << " is ready"; for (auto &wp : worker_processes) { // Send graceful shutdown signal to all worker processes prior to // pid. if (wp->worker_pid == pid) { break; } LOG(INFO) << "Sending graceful shutdown event to worker process pid=" << wp->worker_pid; ipc_send(wp.get(), SHRPX_IPC_GRACEFUL_SHUTDOWN); worker_process_set_termination_deadline(wp.get(), loop); } if (orig_pid != -1) { LOG(NOTICE) << "Send QUIT signal to the original main process to tell " "that we are ready to serve requests."; kill(orig_pid, SIGQUIT); orig_pid = -1; } } } // namespace namespace { void start_worker_process_ready_ipc_watcher(struct ev_loop *loop) { ev_io_init(&worker_process_ready_ipcev, worker_process_ready_ipc_readcb, worker_process_ready_ipc_fd[0], EV_READ); ev_io_start(loop, &worker_process_ready_ipcev); } } // namespace namespace { void shutdown_worker_process_ready_ipc_watcher(struct ev_loop *loop) { ev_io_stop(loop, &worker_process_ready_ipcev); } } // namespace namespace { // Creates worker process, and returns PID of worker process. On // success, file descriptor for IPC (send only) is assigned to // |main_ipc_fd|. In child process, we will close file descriptors // which are inherited from previous configuration/process, but not // used in the current configuration. pid_t fork_worker_process(int &main_ipc_fd #ifdef ENABLE_HTTP3 , int &wp_quic_ipc_fd #endif // defined(ENABLE_HTTP3) , const std::vector &iaddrs #ifdef ENABLE_HTTP3 , std::vector worker_ids, std::vector quic_lwps #endif // defined(ENABLE_HTTP3) ) { std::array errbuf; int rv; sigset_t oldset; std::array ipc_fd; rv = create_ipc_socket(ipc_fd); if (rv != 0) { return -1; } #ifdef ENABLE_HTTP3 std::array quic_ipc_fd; rv = create_quic_ipc_socket(quic_ipc_fd); if (rv != 0) { return -1; } #endif // defined(ENABLE_HTTP3) rv = shrpx_signal_block_all(&oldset); if (rv != 0) { auto error = errno; LOG(ERROR) << "Blocking all signals failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(ipc_fd[0]); close(ipc_fd[1]); return -1; } auto config = get_config(); pid_t pid = 0; if (!config->single_process) { pid = fork(); } if (pid == 0) { // We are in new process now, update pid for logger. log_config()->pid = getpid(); ev_loop_fork(EV_DEFAULT); for (auto &addr : config->conn.listener.addrs) { if (addr.host_unix) { util::make_socket_closeonexec(addr.fd); } } #ifdef ENABLE_HTTP3 util::make_socket_closeonexec(quic_ipc_fd[0]); for (auto &lwp : quic_lwps) { util::make_socket_closeonexec(lwp.quic_ipc_fd); } for (auto &wp : worker_processes) { util::make_socket_closeonexec(wp->quic_ipc_fd); // Do not close quic_ipc_fd. wp->quic_ipc_fd = -1; } #endif // defined(ENABLE_HTTP3) if (!config->single_process) { close(worker_process_ready_ipc_fd[0]); shutdown_worker_process_ready_ipc_watcher(EV_DEFAULT); shutdown_signal_watchers(EV_DEFAULT); } // Remove all WorkerProcesses to stop any registered watcher on // default loop. worker_process_remove_all(EV_DEFAULT); close_unused_inherited_addr(iaddrs); shrpx_signal_set_worker_proc_ign_handler(); rv = shrpx_signal_unblock_all(); if (rv != 0) { auto error = errno; LOG(FATAL) << "Unblocking all signals failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); if (config->single_process) { exit(EXIT_FAILURE); } else { nghttp2_Exit(EXIT_FAILURE); } } if (!config->single_process) { close(ipc_fd[1]); #ifdef ENABLE_HTTP3 close(quic_ipc_fd[1]); #endif // defined(ENABLE_HTTP3) } WorkerProcessConfig wpconf{ .ipc_fd = ipc_fd[0], .ready_ipc_fd = worker_process_ready_ipc_fd[1], #ifdef ENABLE_HTTP3 .worker_ids = std::move(worker_ids), .quic_ipc_fd = quic_ipc_fd[0], .quic_lingering_worker_processes = std::move(quic_lwps), #endif // defined(ENABLE_HTTP3) }; rv = worker_process_event_loop(&wpconf); if (rv != 0) { LOG(FATAL) << "Worker process returned error"; if (config->single_process) { exit(EXIT_FAILURE); } else { nghttp2_Exit(EXIT_FAILURE); } } LOG(NOTICE) << "Worker process shutting down momentarily"; // call exit(...) instead of nghttp2_Exit to get leak sanitizer report if (config->single_process) { exit(EXIT_SUCCESS); } else { nghttp2_Exit(EXIT_SUCCESS); } } // parent process if (pid == -1) { auto error = errno; LOG(ERROR) << "Could not spawn worker process: " << xsi_strerror(error, errbuf.data(), errbuf.size()); } rv = shrpx_signal_set(&oldset); if (rv != 0) { auto error = errno; LOG(FATAL) << "Restoring signal mask failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); exit(EXIT_FAILURE); } if (pid == -1) { close(ipc_fd[0]); close(ipc_fd[1]); #ifdef ENABLE_HTTP3 close(quic_ipc_fd[0]); close(quic_ipc_fd[1]); #endif // defined(ENABLE_HTTP3) return -1; } close(ipc_fd[0]); #ifdef ENABLE_HTTP3 close(quic_ipc_fd[0]); #endif // defined(ENABLE_HTTP3) main_ipc_fd = ipc_fd[1]; #ifdef ENABLE_HTTP3 wp_quic_ipc_fd = quic_ipc_fd[1]; #endif // ENABLE_HTTP3 LOG(NOTICE) << "Worker process [" << pid << "] spawned"; return pid; } } // namespace namespace { int event_loop() { std::array errbuf; shrpx_signal_set_main_proc_ign_handler(); auto config = mod_config(); if (config->daemon) { if (call_daemon() == -1) { auto error = errno; LOG(FATAL) << "Failed to daemonize: " << xsi_strerror(error, errbuf.data(), errbuf.size()); return -1; } // We get new PID after successful daemon(). mod_config()->pid = getpid(); // daemon redirects stderr file descriptor to /dev/null, so we // need this. redirect_stderr_to_errorlog(config->logging); } // update systemd PID tracking shrpx_sd_notifyf(0, "MAINPID=%d\n", config->pid); { auto iaddrs = get_inherited_unix_domain_socket_from_env(config); if (create_unix_domain_listener_socket(config, iaddrs) != 0) { return -1; } close_unused_inherited_addr(iaddrs); } orig_pid = get_orig_pid_from_env(); #ifdef ENABLE_HTTP3 inherited_quic_lingering_worker_processes = get_inherited_quic_lingering_worker_process_from_env(); #endif // ENABLE_HTTP3 auto loop = ev_default_loop(config->ev_loop_flags); int ipc_fd = 0; #ifdef ENABLE_HTTP3 int quic_ipc_fd = 0; auto quic_lwps = collect_quic_lingering_worker_processes(); std::vector worker_ids; if (generate_worker_id(worker_ids, worker_process_seq, config) != 0) { return -1; } #endif // ENABLE_HTTP3 if (!config->single_process) { start_signal_watchers(loop); } create_worker_process_ready_ipc_socket(worker_process_ready_ipc_fd); start_worker_process_ready_ipc_watcher(loop); auto pid = fork_worker_process(ipc_fd #ifdef ENABLE_HTTP3 , quic_ipc_fd #endif // ENABLE_HTTP3 , {} #ifdef ENABLE_HTTP3 , worker_ids, std::move(quic_lwps) #endif // ENABLE_HTTP3 ); if (pid == -1) { return -1; } ev_timer_init(&worker_process_grace_period_timer, worker_process_grace_period_timercb, 0., 0.); worker_process_add(std::make_unique( loop, pid, ipc_fd #ifdef ENABLE_HTTP3 , quic_ipc_fd, std::move(worker_ids), worker_process_seq++ #endif // ENABLE_HTTP3 )); // Write PID file when we are ready to accept connection from peer. // This makes easier to write restart script for nghttpx. Because // when we know that PID file is recreated, it means we can send // QUIT signal to the old process to make it shutdown gracefully. if (!config->pid_file.empty()) { save_pid(); } shrpx_sd_notifyf(0, "READY=1"); ev_run(loop, 0); ev_timer_stop(loop, &worker_process_grace_period_timer); shutdown_worker_process_ready_ipc_watcher(loop); // config is now stale if reload has happened. if (!get_config()->single_process) { shutdown_signal_watchers(loop); } return 0; } } // namespace namespace { // Returns true if regular file or symbolic link |path| exists. bool conf_exists(const char *path) { struct stat buf; int rv = stat(path, &buf); return rv == 0 && (buf.st_mode & (S_IFREG | S_IFLNK)); } } // namespace namespace { constexpr auto DEFAULT_ALPN_LIST = "h2,http/1.1"sv; } // namespace namespace { constexpr auto DEFAULT_TLS_MIN_PROTO_VERSION = "TLSv1.2"sv; #ifdef TLS1_3_VERSION constexpr auto DEFAULT_TLS_MAX_PROTO_VERSION = "TLSv1.3"sv; #else // !TLS1_3_VERSION constexpr auto DEFAULT_TLS_MAX_PROTO_VERSION = "TLSv1.2"sv; #endif // !TLS1_3_VERSION } // namespace namespace { constexpr auto DEFAULT_ACCESSLOG_FORMAT = R"($remote_addr - - [$time_local] )" R"("$request" $status $body_bytes_sent )" R"("$http_referer" "$http_user_agent")"sv; } // namespace namespace { void fill_default_config(Config *config) { config->num_worker = 1; config->conf_path = "/etc/nghttpx/nghttpx.conf"sv; config->pid = getpid(); #ifdef NOTHREADS config->single_thread = true; #endif // NOTHREADS if (ev_supported_backends() & ~ev_recommended_backends() & EVBACKEND_KQUEUE) { config->ev_loop_flags = ev_recommended_backends() | EVBACKEND_KQUEUE; } auto &tlsconf = config->tls; { auto &ticketconf = tlsconf.ticket; { auto &memcachedconf = ticketconf.memcached; memcachedconf.max_retry = 3; memcachedconf.max_fail = 2; memcachedconf.interval = 10_min; memcachedconf.family = AF_UNSPEC; } ticketconf.cipher = EVP_aes_128_cbc(); } { auto &dyn_recconf = tlsconf.dyn_rec; dyn_recconf.warmup_threshold = 1_m; dyn_recconf.idle_timeout = 1_s; } tlsconf.session_timeout = std::chrono::hours(12); tlsconf.ciphers = nghttp2::tls::DEFAULT_CIPHER_LIST; tlsconf.tls13_ciphers = nghttp2::tls::DEFAULT_TLS13_CIPHER_LIST; tlsconf.client.ciphers = nghttp2::tls::DEFAULT_CIPHER_LIST; tlsconf.client.tls13_ciphers = nghttp2::tls::DEFAULT_TLS13_CIPHER_LIST; tlsconf.min_proto_version = tls::proto_version_from_string(DEFAULT_TLS_MIN_PROTO_VERSION); tlsconf.max_proto_version = tls::proto_version_from_string(DEFAULT_TLS_MAX_PROTO_VERSION); tlsconf.max_early_data = 16_k; tlsconf.groups = "X25519:P-256:P-384:P-521"sv; auto &httpconf = config->http; httpconf.server_name = "nghttpx"sv; httpconf.no_host_rewrite = true; httpconf.request_header_field_buffer = 64_k; httpconf.max_request_header_fields = 100; httpconf.response_header_field_buffer = 64_k; httpconf.max_response_header_fields = 500; httpconf.redirect_https_port = "443"sv; httpconf.max_requests = std::numeric_limits::max(); httpconf.xfp.add = true; httpconf.xfp.strip_incoming = true; httpconf.early_data.strip_incoming = true; httpconf.timeout.header = 1_min; auto &http2conf = config->http2; { auto &upstreamconf = http2conf.upstream; { auto &timeoutconf = upstreamconf.timeout; timeoutconf.settings = 10_s; } // window size for HTTP/2 upstream connection per stream. 2**16-1 // = 64KiB-1, which is HTTP/2 default. upstreamconf.window_size = 64_k - 1; // HTTP/2 has connection-level flow control. The default window // size for HTTP/2 is 64KiB - 1. upstreamconf.connection_window_size = 64_k - 1; upstreamconf.max_concurrent_streams = 100; upstreamconf.encoder_dynamic_table_size = 4_k; upstreamconf.decoder_dynamic_table_size = 4_k; nghttp2_option_new(&upstreamconf.option); nghttp2_option_set_no_auto_window_update(upstreamconf.option, 1); nghttp2_option_set_no_recv_client_magic(upstreamconf.option, 1); nghttp2_option_set_max_deflate_dynamic_table_size( upstreamconf.option, upstreamconf.encoder_dynamic_table_size); nghttp2_option_set_server_fallback_rfc7540_priorities(upstreamconf.option, 1); nghttp2_option_set_builtin_recv_extension_type(upstreamconf.option, NGHTTP2_PRIORITY_UPDATE); // For API endpoint, we enable automatic window update. This is // because we are a sink. nghttp2_option_new(&upstreamconf.alt_mode_option); nghttp2_option_set_no_recv_client_magic(upstreamconf.alt_mode_option, 1); nghttp2_option_set_max_deflate_dynamic_table_size( upstreamconf.alt_mode_option, upstreamconf.encoder_dynamic_table_size); } http2conf.timeout.stream_write = 1_min; { auto &downstreamconf = http2conf.downstream; { auto &timeoutconf = downstreamconf.timeout; timeoutconf.settings = 10_s; } downstreamconf.window_size = 64_k - 1; downstreamconf.connection_window_size = (1u << 31) - 1; downstreamconf.max_concurrent_streams = 100; downstreamconf.encoder_dynamic_table_size = 4_k; downstreamconf.decoder_dynamic_table_size = 4_k; nghttp2_option_new(&downstreamconf.option); nghttp2_option_set_no_auto_window_update(downstreamconf.option, 1); nghttp2_option_set_peer_max_concurrent_streams(downstreamconf.option, 100); nghttp2_option_set_max_deflate_dynamic_table_size( downstreamconf.option, downstreamconf.encoder_dynamic_table_size); } #ifdef ENABLE_HTTP3 auto &quicconf = config->quic; { auto &upstreamconf = quicconf.upstream; { auto &timeoutconf = upstreamconf.timeout; timeoutconf.idle = 30_s; } auto &bpfconf = quicconf.bpf; bpfconf.prog_file = PKGLIBDIR "/reuseport_kern.o"sv; upstreamconf.congestion_controller = NGTCP2_CC_ALGO_CUBIC; upstreamconf.initial_rtt = static_cast(NGTCP2_DEFAULT_INITIAL_RTT) / NGTCP2_SECONDS; } if (RAND_bytes(reinterpret_cast(&quicconf.server_id), sizeof(quicconf.server_id)) != 1) { assert(0); abort(); } auto &http3conf = config->http3; { auto &upstreamconf = http3conf.upstream; upstreamconf.max_concurrent_streams = 100; upstreamconf.window_size = 256_k; upstreamconf.connection_window_size = 1_m; upstreamconf.max_window_size = 6_m; upstreamconf.max_connection_window_size = 8_m; } #endif // ENABLE_HTTP3 auto &loggingconf = config->logging; { auto &accessconf = loggingconf.access; accessconf.format = parse_log_format(config->balloc, DEFAULT_ACCESSLOG_FORMAT); auto &errorconf = loggingconf.error; errorconf.file = "/dev/stderr"sv; } loggingconf.syslog_facility = LOG_DAEMON; loggingconf.severity = NOTICE; auto &connconf = config->conn; { auto &listenerconf = connconf.listener; { // Default accept() backlog listenerconf.backlog = 65536; listenerconf.timeout.sleep = 30_s; } } { auto &upstreamconf = connconf.upstream; { auto &timeoutconf = upstreamconf.timeout; // Idle timeout for HTTP2 upstream connection timeoutconf.http2_idle = 3_min; // Idle timeout for HTTP3 upstream connection timeoutconf.http3_idle = 3_min; // Write timeout for HTTP2/non-HTTP2 upstream connection timeoutconf.write = 30_s; // Keep alive (idle) timeout for HTTP/1 upstream connection timeoutconf.idle = 1_min; } } { connconf.downstream = std::make_shared(); auto &downstreamconf = *connconf.downstream; { auto &timeoutconf = downstreamconf.timeout; // Read/Write timeouts for downstream connection timeoutconf.read = 1_min; timeoutconf.write = 30_s; // Timeout for pooled (idle) connections timeoutconf.idle_read = 2_s; timeoutconf.connect = 30_s; timeoutconf.max_backoff = 120_s; } downstreamconf.connections_per_host = 8; downstreamconf.request_buffer_size = 16_k; downstreamconf.response_buffer_size = 128_k; downstreamconf.family = AF_UNSPEC; } auto &apiconf = config->api; apiconf.max_request_body = 32_m; auto &dnsconf = config->dns; { auto &timeoutconf = dnsconf.timeout; timeoutconf.cache = 10_s; timeoutconf.lookup = 250_ms; } dnsconf.max_try = 3; } } // namespace namespace { void print_version(std::ostream &out) { out << "nghttpx nghttp2/" NGHTTP2_VERSION #ifdef ENABLE_HTTP3 " ngtcp2/" NGTCP2_VERSION " nghttp3/" NGHTTP3_VERSION #endif // ENABLE_HTTP3 << std::endl; } } // namespace namespace { void print_usage(std::ostream &out) { out << R"(Usage: nghttpx [OPTIONS]... [ ] A reverse proxy for HTTP/3, HTTP/2, and HTTP/1.)" << std::endl; } } // namespace namespace { void print_help(std::ostream &out) { auto config = get_config(); print_usage(out); out << R"( Set path to server's private key. Required unless "no-tls" parameter is used in --frontend option. Set path to server's certificate. Required unless "no-tls" parameter is used in --frontend option. Options: The options are categorized into several groups. Connections: -b, --backend=(,|unix:)[;[[:...]][[;]...] Set backend host and port. The multiple backend addresses are accepted by repeating this option. UNIX domain socket can be specified by prefixing path name with "unix:" (e.g., unix:/var/run/backend.sock). Optionally, if s are given, the backend address is only used if request matches the pattern. The pattern matching is closely designed to ServeMux in net/http package of Go programming language. consists of path, host + path or just host. The path must start with "/". If it ends with "/", it matches all request path in its subtree. To deal with the request to the directory without trailing slash, the path which ends with "/" also matches the request path which only lacks trailing '/' (e.g., path "/foo/" matches request path "/foo"). If it does not end with "/", it performs exact match against the request path. If host is given, it performs a match against the request host. For a request received on the frontend listener with "sni-fwd" parameter enabled, SNI host is used instead of a request host. If host alone is given, "/" is appended to it, so that it matches all request paths under the host (e.g., specifying "nghttp2.org" equals to "nghttp2.org/"). CONNECT method is treated specially. It does not have path, and we don't allow empty path. To workaround this, we assume that CONNECT method has "/" as path. Patterns with host take precedence over patterns with just path. Then, longer patterns take precedence over shorter ones. Host can include "*" in the left most position to indicate wildcard match (only suffix match is done). The "*" must match at least one character. For example, host pattern "*.nghttp2.org" matches against "www.nghttp2.org" and "git.ngttp2.org", but does not match against "nghttp2.org". The exact hosts match takes precedence over the wildcard hosts match. If path part ends with "*", it is treated as wildcard path. The wildcard path behaves differently from the normal path. For normal path, match is made around the boundary of path component separator,"/". On the other hand, the wildcard path does not take into account the path component separator. All paths which include the wildcard path without last "*" as prefix, and are strictly longer than wildcard path without last "*" are matched. "*" must match at least one character. For example, the pattern "/foo*" matches "/foo/" and "/foobar". But it does not match "/foo", or "/fo". If is omitted or empty string, "/" is used as pattern, which matches all request paths (catch-all pattern). The catch-all backend must be given. When doing a match, nghttpx made some normalization to pattern, request host and path. For host part, they are converted to lower case. For path part, percent-encoded unreserved characters defined in RFC 3986 are decoded, and any dot-segments (".." and ".") are resolved and removed. For example, -b'127.0.0.1,8080;nghttp2.org/httpbin/' matches the request host "nghttp2.org" and the request path "/httpbin/get", but does not match the request host "nghttp2.org" and the request path "/index.html". The multiple s can be specified, delimiting them by ":". Specifying -b'127.0.0.1,8080;nghttp2.org:www.nghttp2.org' has the same effect to specify -b'127.0.0.1,8080;nghttp2.org' and -b'127.0.0.1,8080;www.nghttp2.org'. The backend addresses sharing same are grouped together forming load balancing group. Several parameters are accepted after . The parameters are delimited by ";". The available parameters are: "proto=", "tls", "sni=", "fall=", "rise=", "affinity=", "dns", "redirect-if-not-tls", "upgrade-scheme", "mruby=", "read-timeout=", "write-timeout=", "group=", "group-weight=", "weight=", and "dnf". The parameter consists of keyword, and optionally followed by "=" and value. For example, the parameter "proto=h2" consists of the keyword "proto" and value "h2". The parameter "tls" consists of the keyword "tls" without value. Each parameter is described as follows. The backend application protocol can be specified using optional "proto" parameter, and in the form of "proto=". should be one of the following list without quotes: "h2", "http/1.1". The default value of is "http/1.1". Note that usually "h2" refers to HTTP/2 over TLS. But in this option, it may mean HTTP/2 over cleartext TCP unless "tls" keyword is used (see below). TLS can be enabled by specifying optional "tls" parameter. TLS is not enabled by default. With "sni=" parameter, it can override the TLS SNI field value with given . This will default to the backend name The feature to detect whether backend is online or offline can be enabled using optional "fall" and "rise" parameters. Using "fall=" parameter, if nghttpx cannot connect to a this backend times in a row, this backend is assumed to be offline, and it is excluded from load balancing. If is 0, this backend never be excluded from load balancing whatever times nghttpx cannot connect to it, and this is the default. There is also "rise=" parameter. After backend was excluded from load balancing group, nghttpx periodically attempts to make a connection to the failed backend, and if the connection is made successfully times in a row, the backend is assumed to be online, and it is now eligible for load balancing target. If is 0, a backend is permanently offline, once it goes in that state, and this is the default behaviour. The session affinity is enabled using "affinity=" parameter. If "ip" is given in , client IP based session affinity is enabled. If "cookie" is given in , cookie based session affinity is enabled. If "none" is given in , session affinity is disabled, and this is the default. The session affinity is enabled per . If at least one backend has "affinity" parameter, and its is not "none", session affinity is enabled for all backend servers sharing the same . It is advised to set "affinity" parameter to all backend explicitly if session affinity is desired. The session affinity may break if one of the backend gets unreachable, or backend settings are reloaded or replaced by API. If "affinity=cookie" is used, the additional configuration is required. "affinity-cookie-name=" must be used to specify a name of cookie to use. Optionally, "affinity-cookie-path=" can be used to specify a path which cookie is applied. The optional "affinity-cookie-secure=" controls the Secure attribute of a cookie. The default value is "auto", and the Secure attribute is determined by a request scheme. If a request scheme is "https", then Secure attribute is set. Otherwise, it is not set. If is "yes", the Secure attribute is always set. If is "no", the Secure attribute is always omitted. "affinity-cookie-stickiness=" controls stickiness of this affinity. If is "loose", removing or adding a backend server might break the affinity and the request might be forwarded to a different backend server. If is "strict", removing the designated backend server breaks affinity, but adding new backend server does not cause breakage. If the designated backend server becomes unavailable, new backend server is chosen as if the request does not have an affinity cookie. defaults to "loose". By default, name resolution of backend host name is done at start up, or reloading configuration. If "dns" parameter is given, name resolution takes place dynamically. This is useful if backend address changes frequently. If "dns" is given, name resolution of backend host name at start up, or reloading configuration is skipped. If "redirect-if-not-tls" parameter is used, the matched backend requires that frontend connection is TLS encrypted. If it isn't, nghttpx responds to the request with 308 status code, and https URI the client should use instead is included in Location header field. The port number in redirect URI is 443 by default, and can be changed using --redirect-https-port option. If at least one backend has "redirect-if-not-tls" parameter, this feature is enabled for all backend servers sharing the same . It is advised to set "redirect-if-no-tls" parameter to all backends explicitly if this feature is desired. If "upgrade-scheme" parameter is used along with "tls" parameter, HTTP/2 :scheme pseudo header field is changed to "https" from "http" when forwarding a request to this particular backend. This is a workaround for a backend server which requires "https" :scheme pseudo header field on TLS encrypted connection. "mruby=" parameter specifies a path to mruby script file which is invoked when this pattern is matched. All backends which share the same pattern must have the same mruby path. "read-timeout=" and "write-timeout=" parameters specify the read and write timeout of the backend connection when this pattern is matched. All backends which share the same pattern must have the same timeouts. If these timeouts are entirely omitted for a pattern, --backend-read-timeout and --backend-write-timeout are used. "group=" parameter specifies the name of group this backend address belongs to. By default, it belongs to the unnamed default group. The name of group is unique per pattern. "group-weight=" parameter specifies the weight of the group. The higher weight gets more frequently selected by the load balancing algorithm. must be [1, 256] inclusive. The weight 8 has 4 times more weight than 2. must be the same for all addresses which share the same . If "group-weight" is omitted in an address, but the other address which belongs to the same group specifies "group-weight", its weight is used. If no "group-weight" is specified for all addresses, the weight of a group becomes 1. "group" and "group-weight" are ignored if session affinity is enabled. "weight=" parameter specifies the weight of the backend address inside a group which this address belongs to. The higher weight gets more frequently selected by the load balancing algorithm. must be [1, 256] inclusive. The weight 8 has 4 times more weight than weight 2. If this parameter is omitted, weight becomes 1. "weight" is ignored if session affinity is enabled. If "dnf" parameter is specified, an incoming request is not forwarded to a backend and just consumed along with the request body (actually a backend server never be contacted). It is expected that the HTTP response is generated by mruby script (see "mruby=" parameter above). "dnf" is an abbreviation of "do not forward". Since ";" and ":" are used as delimiter, must not contain these characters. In order to include ":" in , one has to specify "%3A" (which is percent-encoded from of ":") instead. Since ";" has special meaning in shell, the option value must be quoted. Default: )" << DEFAULT_DOWNSTREAM_HOST << "," << DEFAULT_DOWNSTREAM_PORT << R"( -f, --frontend=(,|unix:)[[;]...] Set frontend host and port. If is '*', it assumes all addresses including both IPv4 and IPv6. UNIX domain socket can be specified by prefixing path name with "unix:" (e.g., unix:/var/run/nghttpx.sock). This option can be used multiple times to listen to multiple addresses. This option can take 0 or more parameters, which are described below. Note that "api" and "healthmon" parameters are mutually exclusive. Optionally, TLS can be disabled by specifying "no-tls" parameter. TLS is enabled by default. If "sni-fwd" parameter is used, when performing a match to select a backend server, SNI host name received from the client is used instead of the request host. See --backend option about the pattern match. To make this frontend as API endpoint, specify "api" parameter. This is disabled by default. It is important to limit the access to the API frontend. Otherwise, someone may change the backend server, and break your services, or expose confidential information to the outside the world. To make this frontend as health monitor endpoint, specify "healthmon" parameter. This is disabled by default. Any requests which come through this address are replied with 200 HTTP status, without no body. To accept PROXY protocol version 1 and 2 on frontend connection, specify "proxyproto" parameter. This is disabled by default. To receive HTTP/3 (QUIC) traffic, specify "quic" parameter. It makes nghttpx listen on UDP port rather than TCP port. UNIX domain socket, "api", and "healthmon" parameters cannot be used with "quic" parameter. Default: *,3000 --backlog= Set listen backlog size. Default: )" << config->conn.listener.backlog << R"( --backend-address-family=(auto|IPv4|IPv6) Specify address family of backend connections. If "auto" is given, both IPv4 and IPv6 are considered. If "IPv4" is given, only IPv4 address is considered. If "IPv6" is given, only IPv6 address is considered. Default: auto --backend-http-proxy-uri= Specify proxy URI in the form http://[:@]:. If a proxy requires authentication, specify and . Note that they must be properly percent-encoded. This proxy is used when the backend connection is HTTP/2. First, make a CONNECT request to the proxy and it connects to the backend on behalf of nghttpx. This forms tunnel. After that, nghttpx performs SSL/TLS handshake with the downstream through the tunnel. The timeouts when connecting and making CONNECT request can be specified by --backend-read-timeout and --backend-write-timeout options. Performance: -n, --workers= Set the number of worker threads. Default: )" << config->num_worker << R"( --single-thread Run everything in one thread inside the worker process. This feature is provided for better debugging experience, or for the platforms which lack thread support. If threading is disabled, this option is always enabled. --read-rate= Set maximum average read rate on frontend connection. Setting 0 to this option means read rate is unlimited. Default: )" << config->conn.upstream.ratelimit.read.rate << R"( --read-burst= Set maximum read burst size on frontend connection. Setting 0 to this option means read burst size is unlimited. Default: )" << config->conn.upstream.ratelimit.read.burst << R"( --write-rate= Set maximum average write rate on frontend connection. Setting 0 to this option means write rate is unlimited. Default: )" << config->conn.upstream.ratelimit.write.rate << R"( --write-burst= Set maximum write burst size on frontend connection. Setting 0 to this option means write burst size is unlimited. Default: )" << config->conn.upstream.ratelimit.write.burst << R"( --worker-read-rate= Set maximum average read rate on frontend connection per worker. Setting 0 to this option means read rate is unlimited. Not implemented yet. Default: 0 --worker-read-burst= Set maximum read burst size on frontend connection per worker. Setting 0 to this option means read burst size is unlimited. Not implemented yet. Default: 0 --worker-write-rate= Set maximum average write rate on frontend connection per worker. Setting 0 to this option means write rate is unlimited. Not implemented yet. Default: 0 --worker-write-burst= Set maximum write burst size on frontend connection per worker. Setting 0 to this option means write burst size is unlimited. Not implemented yet. Default: 0 --worker-frontend-connections= Set maximum number of simultaneous connections frontend accepts. Setting 0 means unlimited. Default: )" << config->conn.upstream.worker_connections << R"( --backend-connections-per-host= Set maximum number of backend concurrent connections (and/or streams in case of HTTP/2) per origin host. This option is meaningful when --http2-proxy option is used. The origin host is determined by authority portion of request URI (or :authority header field for HTTP/2). To limit the number of connections per frontend for default mode, use --backend-connections-per-frontend. Default: )" << config->conn.downstream->connections_per_host << R"( --backend-connections-per-frontend= Set maximum number of backend concurrent connections (and/or streams in case of HTTP/2) per frontend. This option is only used for default mode. 0 means unlimited. To limit the number of connections per host with --http2-proxy option, use --backend-connections-per-host. Default: )" << config->conn.downstream->connections_per_frontend << R"( --rlimit-nofile= Set maximum number of open files (RLIMIT_NOFILE) to . If 0 is given, nghttpx does not set the limit. Default: )" << config->rlimit_nofile << R"( --rlimit-memlock= Set maximum number of bytes of memory that may be locked into RAM. If 0 is given, nghttpx does not set the limit. Default: )" << config->rlimit_memlock << R"( --backend-request-buffer= Set buffer size used to store backend request. Default: )" << util::utos_unit(config->conn.downstream->request_buffer_size) << R"( --backend-response-buffer= Set buffer size used to store backend response. Default: )" << util::utos_unit(config->conn.downstream->response_buffer_size) << R"( --fastopen= Enables "TCP Fast Open" for the listening socket and limits the maximum length for the queue of connections that have not yet completed the three-way handshake. If value is 0 then fast open is disabled. Default: )" << config->conn.listener.fastopen << R"( --no-kqueue Don't use kqueue. This option is only applicable for the platforms which have kqueue. For other platforms, this option will be simply ignored. Timeout: --frontend-http2-idle-timeout= Specify idle timeout for HTTP/2 frontend connection. If no active streams exist for this duration, connection is closed. Default: )" << util::duration_str(config->conn.upstream.timeout.http2_idle) << R"( --frontend-http3-idle-timeout= Specify idle timeout for HTTP/3 frontend connection. If no active streams exist for this duration, connection is closed. Default: )" << util::duration_str(config->conn.upstream.timeout.http3_idle) << R"( --frontend-write-timeout= Specify write timeout for all frontend connections. Default: )" << util::duration_str(config->conn.upstream.timeout.write) << R"( --frontend-keep-alive-timeout= Specify keep-alive timeout for frontend HTTP/1 connection. Default: )" << util::duration_str(config->conn.upstream.timeout.idle) << R"( --frontend-header-timeout= Specify duration that the server waits for an HTTP request header fields to be received completely. On timeout, HTTP/1 and HTTP/2 connections are closed. For HTTP/3, the stream is shutdown, and the connection itself is left intact. Default: )" << util::duration_str(config->http.timeout.header) << R"( --stream-read-timeout= Specify read timeout for HTTP/2 streams. 0 means no timeout. Default: )" << util::duration_str(config->http2.timeout.stream_read) << R"( --stream-write-timeout= Specify write timeout for HTTP/2 streams. 0 means no timeout. Default: )" << util::duration_str(config->http2.timeout.stream_write) << R"( --backend-read-timeout= Specify read timeout for backend connection. Default: )" << util::duration_str(config->conn.downstream->timeout.read) << R"( --backend-write-timeout= Specify write timeout for backend connection. Default: )" << util::duration_str(config->conn.downstream->timeout.write) << R"( --backend-connect-timeout= Specify timeout before establishing TCP connection to backend. Default: )" << util::duration_str(config->conn.downstream->timeout.connect) << R"( --backend-keep-alive-timeout= Specify keep-alive timeout for backend HTTP/1 connection. Default: )" << util::duration_str(config->conn.downstream->timeout.idle_read) << R"( --listener-disable-timeout= After accepting connection failed, connection listener is disabled for a given amount of time. Specifying 0 disables this feature. Default: )" << util::duration_str(config->conn.listener.timeout.sleep) << R"( --frontend-http2-setting-timeout= Specify timeout before SETTINGS ACK is received from client. Default: )" << util::duration_str(config->http2.upstream.timeout.settings) << R"( --backend-http2-settings-timeout= Specify timeout before SETTINGS ACK is received from backend server. Default: )" << util::duration_str(config->http2.downstream.timeout.settings) << R"( --backend-max-backoff= Specify maximum backoff interval. This is used when doing health check against offline backend (see "fail" parameter in --backend option). It is also used to limit the maximum interval to temporarily disable backend when nghttpx failed to connect to it. These intervals are calculated using exponential backoff, and consecutive failed attempts increase the interval. This option caps its maximum value. Default: )" << util::duration_str(config->conn.downstream->timeout.max_backoff) << R"( SSL/TLS: --ciphers= Set allowed cipher list for frontend connection. The format of the string is described in OpenSSL ciphers(1). This option sets cipher suites for TLSv1.2. Use --tls13-ciphers for TLSv1.3. Default: )" << config->tls.ciphers << R"( --tls13-ciphers= Set allowed cipher list for frontend connection. The format of the string is described in OpenSSL ciphers(1). This option sets cipher suites for TLSv1.3. Use --ciphers for TLSv1.2. Default: )" << config->tls.tls13_ciphers << R"( --client-ciphers= Set allowed cipher list for backend connection. The format of the string is described in OpenSSL ciphers(1). This option sets cipher suites for TLSv1.2. Use --tls13-client-ciphers for TLSv1.3. Default: )" << config->tls.client.ciphers << R"( --tls13-client-ciphers= Set allowed cipher list for backend connection. The format of the string is described in OpenSSL ciphers(1). This option sets cipher suites for TLSv1.3. Use --client-ciphers for TLSv1.2. Default: )" << config->tls.client.tls13_ciphers << R"( --groups= Set the supported group list for frontend connections. is a colon separated list of group NID or names in the preference order. The supported curves depend on the linked OpenSSL library. This function requires OpenSSL >= 1.0.2. Default: )" << config->tls.groups << R"( -k, --insecure Don't verify backend server's certificate if TLS is enabled for backend connections. --cacert= Set path to trusted CA certificate file. It is used in backend TLS connections to verify peer's certificate. The file must be in PEM format. It can contain multiple certificates. If the linked OpenSSL is configured to load system wide certificates, they are loaded at startup regardless of this option. --private-key-passwd-file= Path to file that contains password for the server's private key. If none is given and the private key is password protected it'll be requested interactively. --subcert=:[[;]...] Specify additional certificate and private key file. nghttpx will choose certificates based on the hostname indicated by client using TLS SNI extension. If nghttpx is built with OpenSSL >= 1.0.2, the signature algorithms (e.g., ECDSA+SHA256) presented by client are also taken into consideration. This allows nghttpx to send ML-DSA or ECDSA certificate to modern clients, while sending RSA based certificate to older clients. This option can be used multiple times. Additional parameter can be specified in . The available is "sct-dir=". "sct-dir=" specifies the path to directory which contains *.sct files for TLS signed_certificate_timestamp extension (RFC 6962). This feature requires OpenSSL >= 1.0.2. See also --tls-sct-dir option. --dh-param-file= Path to file that contains DH parameters in PEM format. Without this option, DHE cipher suites are not available. --alpn-list= Comma delimited list of ALPN protocol identifier sorted in the order of preference. That means most desirable protocol comes first. The parameter must be delimited by a single comma only and any white spaces are treated as a part of protocol string. Default: )" << DEFAULT_ALPN_LIST << R"( --verify-client Require and verify client certificate. --verify-client-cacert= Path to file that contains CA certificates to verify client certificate. The file must be in PEM format. It can contain multiple certificates. --verify-client-tolerate-expired Accept expired client certificate. Operator should handle the expired client certificate by some means (e.g., mruby script). Otherwise, this option might cause a security risk. --client-private-key-file= Path to file that contains client private key used in backend client authentication. --client-cert-file= Path to file that contains client certificate used in backend client authentication. --tls-min-proto-version= Specify minimum SSL/TLS protocol. The name matching is done in case-insensitive manner. The versions between --tls-min-proto-version and --tls-max-proto-version are enabled. If the protocol list advertised by client does not overlap this range, you will receive the error message "unknown protocol". The available versions are: )" #ifdef TLS1_3_VERSION "TLSv1.3 and " #endif // TLS1_3_VERSION "TLSv1.2" R"( Default: )" << DEFAULT_TLS_MIN_PROTO_VERSION << R"( --tls-max-proto-version= Specify maximum SSL/TLS protocol. The name matching is done in case-insensitive manner. The versions between --tls-min-proto-version and --tls-max-proto-version are enabled. If the protocol list advertised by client does not overlap this range, you will receive the error message "unknown protocol". The available versions are: )" #ifdef TLS1_3_VERSION "TLSv1.3 and " #endif // TLS1_3_VERSION "TLSv1.2" R"( Default: )" << DEFAULT_TLS_MAX_PROTO_VERSION << R"( --tls-ticket-key-file= Path to file that contains random data to construct TLS session ticket parameters. If aes-128-cbc is given in --tls-ticket-key-cipher, the file must contain exactly 48 bytes. If aes-256-cbc is given in --tls-ticket-key-cipher, the file must contain exactly 80 bytes. This options can be used repeatedly to specify multiple ticket parameters. If several files are given, only the first key is used to encrypt TLS session tickets. Other keys are accepted but server will issue new session ticket with first key. This allows session key rotation. Please note that key rotation does not occur automatically. User should rearrange files or change options values and restart nghttpx gracefully. If opening or reading given file fails, all loaded keys are discarded and it is treated as if none of this option is given. If this option is not given or an error occurred while opening or reading a file, key is generated every 1 hour internally and they are valid for 12 hours. This is recommended if ticket key sharing between nghttpx instances is not required. --tls-ticket-key-memcached=,[;tls] Specify address of memcached server to get TLS ticket keys for session resumption. This enables shared TLS ticket key between multiple nghttpx instances. nghttpx does not set TLS ticket key to memcached. The external ticket key generator is required. nghttpx just gets TLS ticket keys from memcached, and use them, possibly replacing current set of keys. It is up to extern TLS ticket key generator to rotate keys frequently. See "TLS SESSION TICKET RESUMPTION" section in manual page to know the data format in memcached entry. Optionally, memcached connection can be encrypted with TLS by specifying "tls" parameter. --tls-ticket-key-memcached-address-family=(auto|IPv4|IPv6) Specify address family of memcached connections to get TLS ticket keys. If "auto" is given, both IPv4 and IPv6 are considered. If "IPv4" is given, only IPv4 address is considered. If "IPv6" is given, only IPv6 address is considered. Default: auto --tls-ticket-key-memcached-interval= Set interval to get TLS ticket keys from memcached. Default: )" << util::duration_str(config->tls.ticket.memcached.interval) << R"( --tls-ticket-key-memcached-max-retry= Set maximum number of consecutive retries before abandoning TLS ticket key retrieval. If this number is reached, the attempt is considered as failure, and "failure" count is incremented by 1, which contributed to the value controlled --tls-ticket-key-memcached-max-fail option. Default: )" << config->tls.ticket.memcached.max_retry << R"( --tls-ticket-key-memcached-max-fail= Set maximum number of consecutive failure before disabling TLS ticket until next scheduled key retrieval. Default: )" << config->tls.ticket.memcached.max_fail << R"( --tls-ticket-key-cipher= Specify cipher to encrypt TLS session ticket. Specify either aes-128-cbc or aes-256-cbc. By default, aes-128-cbc is used. --tls-ticket-key-memcached-cert-file= Path to client certificate for memcached connections to get TLS ticket keys. --tls-ticket-key-memcached-private-key-file= Path to client private key for memcached connections to get TLS ticket keys. --tls-dyn-rec-warmup-threshold= Specify the threshold size for TLS dynamic record size behaviour. During a TLS session, after the threshold number of bytes have been written, the TLS record size will be increased to the maximum allowed (16K). The max record size will continue to be used on the active TLS session. After --tls-dyn-rec-idle-timeout has elapsed, the record size is reduced to 1300 bytes. Specify 0 to always use the maximum record size, regardless of idle period. This behaviour applies to all TLS based frontends, and TLS HTTP/2 backends. Default: )" << util::utos_unit(config->tls.dyn_rec.warmup_threshold) << R"( --tls-dyn-rec-idle-timeout= Specify TLS dynamic record size behaviour timeout. See --tls-dyn-rec-warmup-threshold for more information. This behaviour applies to all TLS based frontends, and TLS HTTP/2 backends. Default: )" << util::duration_str(config->tls.dyn_rec.idle_timeout) << R"( --no-http2-cipher-block-list Allow block listed cipher suite on frontend HTTP/2 connection. See https://tools.ietf.org/html/rfc7540#appendix-A for the complete HTTP/2 cipher suites block list. --client-no-http2-cipher-block-list Allow block listed cipher suite on backend HTTP/2 connection. See https://tools.ietf.org/html/rfc7540#appendix-A for the complete HTTP/2 cipher suites block list. --tls-sct-dir= Specifies the directory where *.sct files exist. All *.sct files in are read, and sent as extension_data of TLS signed_certificate_timestamp (RFC 6962) to client. These *.sct files are for the certificate specified in positional command-line argument , or certificate option in configuration file. For additional certificates, use --subcert option. This option requires OpenSSL >= 1.0.2. --psk-secrets= Read list of PSK identity and secrets from . This is used for frontend connection. The each line of input file is formatted as :, where is PSK identity, and is secret in hex. An empty line, and line which starts with '#' are skipped. The default enabled cipher list might not contain any PSK cipher suite. In that case, desired PSK cipher suites must be enabled using --ciphers option. The desired PSK cipher suite may be block listed by HTTP/2. To use those cipher suites with HTTP/2, consider to use --no-http2-cipher-block-list option. But be aware its implications. --client-psk-secrets= Read PSK identity and secrets from . This is used for backend connection. The each line of input file is formatted as :, where is PSK identity, and is secret in hex. An empty line, and line which starts with '#' are skipped. The first identity and secret pair encountered is used. The default enabled cipher list might not contain any PSK cipher suite. In that case, desired PSK cipher suites must be enabled using --client-ciphers option. The desired PSK cipher suite may be block listed by HTTP/2. To use those cipher suites with HTTP/2, consider to use --client-no-http2-cipher-block-list option. But be aware its implications. --tls-no-postpone-early-data By default, except for QUIC connections, nghttpx postpones forwarding HTTP requests sent in early data, including those sent in partially in it, until TLS handshake finishes. If all backend server recognizes "Early-Data" header field, using this option makes nghttpx not postpone forwarding request and get full potential of 0-RTT data. --tls-max-early-data= Sets the maximum amount of 0-RTT data that server accepts. Default: )" << util::utos_unit(config->tls.max_early_data) << R"( --tls-ktls Enable ktls. HTTP/2: -c, --frontend-http2-max-concurrent-streams= Set the maximum number of the concurrent streams in one frontend HTTP/2 session. Default: )" << config->http2.upstream.max_concurrent_streams << R"( --backend-http2-max-concurrent-streams= Set the maximum number of the concurrent streams in one backend HTTP/2 session. This sets maximum number of concurrent opened pushed streams. The maximum number of concurrent requests are set by a remote server. Default: )" << config->http2.downstream.max_concurrent_streams << R"( --frontend-http2-window-size= Sets the per-stream initial window size of HTTP/2 frontend connection. Default: )" << config->http2.upstream.window_size << R"( --frontend-http2-connection-window-size= Sets the per-connection window size of HTTP/2 frontend connection. Default: )" << config->http2.upstream.connection_window_size << R"( --backend-http2-window-size= Sets the initial window size of HTTP/2 backend connection. Default: )" << config->http2.downstream.window_size << R"( --backend-http2-connection-window-size= Sets the per-connection window size of HTTP/2 backend connection. Default: )" << config->http2.downstream.connection_window_size << R"( --http2-no-cookie-crumbling Don't crumble cookie header field. --padding= Add at most bytes to a HTTP/2 frame payload as padding. Specify 0 to disable padding. This option is meant for debugging purpose and not intended to enhance protocol security. --no-server-push Disable HTTP/2 server push. Server push is supported by default mode and HTTP/2 frontend via Link header field. It is also supported if both frontend and backend are HTTP/2 in default mode. In this case, server push from backend session is relayed to frontend, and server push via Link header field is also supported. --frontend-http2-optimize-write-buffer-size (Experimental) Enable write buffer size optimization in frontend HTTP/2 TLS connection. This optimization aims to reduce write buffer size so that it only contains bytes which can send immediately. This makes server more responsive to prioritized HTTP/2 stream because the buffering of lower priority stream is reduced. This option is only effective on recent Linux platform. --frontend-http2-optimize-window-size (Experimental) Automatically tune connection level window size of frontend HTTP/2 TLS connection. If this feature is enabled, connection window size starts with the default window size, 65535 bytes. nghttpx automatically adjusts connection window size based on TCP receiving window size. The maximum window size is capped by the value specified by --frontend-http2-connection-window-size. Since the stream is subject to stream level window size, it should be adjusted using --frontend-http2-window-size option as well. This option is only effective on recent Linux platform. --frontend-http2-encoder-dynamic-table-size= Specify the maximum dynamic table size of HPACK encoder in the frontend HTTP/2 connection. The decoder (client) specifies the maximum dynamic table size it accepts. Then the negotiated dynamic table size is the minimum of this option value and the value which client specified. Default: )" << util::utos_unit(config->http2.upstream.encoder_dynamic_table_size) << R"( --frontend-http2-decoder-dynamic-table-size= Specify the maximum dynamic table size of HPACK decoder in the frontend HTTP/2 connection. Default: )" << util::utos_unit(config->http2.upstream.decoder_dynamic_table_size) << R"( --backend-http2-encoder-dynamic-table-size= Specify the maximum dynamic table size of HPACK encoder in the backend HTTP/2 connection. The decoder (backend) specifies the maximum dynamic table size it accepts. Then the negotiated dynamic table size is the minimum of this option value and the value which backend specified. Default: )" << util::utos_unit(config->http2.downstream.encoder_dynamic_table_size) << R"( --backend-http2-decoder-dynamic-table-size= Specify the maximum dynamic table size of HPACK decoder in the backend HTTP/2 connection. Default: )" << util::utos_unit(config->http2.downstream.decoder_dynamic_table_size) << R"( Mode: (default mode) Accept HTTP/2, and HTTP/1.1 over SSL/TLS. "no-tls" parameter is used in --frontend option, accept HTTP/2 and HTTP/1.1 over cleartext TCP. The incoming HTTP/1.1 connection can be upgraded to HTTP/2 through HTTP Upgrade. -s, --http2-proxy Like default mode, but enable forward proxy. This is so called HTTP/2 proxy mode. Logging: -L, --log-level= Set the severity level of log output. must be one of INFO, NOTICE, WARN, ERROR and FATAL. Default: NOTICE --accesslog-file= Set path to write access log. To reopen file, send USR1 signal to nghttpx. --accesslog-syslog Send access log to syslog. If this option is used, --accesslog-file option is ignored. --accesslog-format= Specify format string for access log. The default format is combined format. The following variables are available: * $remote_addr: client IP address. * $time_local: local time in Common Log format. * $time_iso8601: local time in ISO 8601 format. * $request: HTTP request line. * $status: HTTP response status code. * $body_bytes_sent: the number of bytes sent to client as response body. * $http_: value of HTTP request header where '_' in is replaced with '-'. * $remote_port: client port. * $server_port: server port. * $request_time: request processing time in seconds with milliseconds resolution. * $pid: PID of the running process. * $alpn: ALPN identifier of the protocol which generates the response. For HTTP/1, ALPN is always http/1.1, regardless of minor version. * $tls_cipher: cipher used for SSL/TLS connection. * $tls_client_fingerprint_sha256: SHA-256 fingerprint of client certificate. * $tls_client_fingerprint_sha1: SHA-1 fingerprint of client certificate. * $tls_client_subject_name: subject name in client certificate. * $tls_client_issuer_name: issuer name in client certificate. * $tls_client_serial: serial number in client certificate. * $tls_protocol: protocol for SSL/TLS connection. * $tls_session_id: session ID for SSL/TLS connection. * $tls_session_reused: "r" if SSL/TLS session was reused. Otherwise, "." * $tls_sni: SNI server name for SSL/TLS connection. * $backend_host: backend host used to fulfill the request. "-" if backend host is not available. * $backend_port: backend port used to fulfill the request. "-" if backend host is not available. * $method: HTTP method * $path: Request path including query. For CONNECT request, authority is recorded. * $path_without_query: $path up to the first '?' character. For CONNECT request, authority is recorded. * $protocol_version: HTTP version (e.g., HTTP/1.1, HTTP/2) The variable can be enclosed by "{" and "}" for disambiguation (e.g., ${remote_addr}). Default: )" << DEFAULT_ACCESSLOG_FORMAT << R"( --accesslog-write-early Write access log when response header fields are received from backend rather than when request transaction finishes. --errorlog-file= Set path to write error log. To reopen file, send USR1 signal to nghttpx. stderr will be redirected to the error log file unless --errorlog-syslog is used. Default: )" << config->logging.error.file << R"( --errorlog-syslog Send error log to syslog. If this option is used, --errorlog-file option is ignored. --syslog-facility= Set syslog facility to . Default: )" << str_syslog_facility(config->logging.syslog_facility) << R"( HTTP: --add-x-forwarded-for Append X-Forwarded-For header field to the downstream request. --strip-incoming-x-forwarded-for Strip X-Forwarded-For header field from inbound client requests. --no-add-x-forwarded-proto Don't append additional X-Forwarded-Proto header field to the backend request. If inbound client sets X-Forwarded-Proto, and --no-strip-incoming-x-forwarded-proto option is used, they are passed to the backend. --no-strip-incoming-x-forwarded-proto Don't strip X-Forwarded-Proto header field from inbound client requests. --add-forwarded= Append RFC 7239 Forwarded header field with parameters specified in comma delimited list . The supported parameters are "by", "for", "host", and "proto". By default, the value of "by" and "for" parameters are obfuscated string. See --forwarded-by and --forwarded-for options respectively. Note that nghttpx does not translate non-standard X-Forwarded-* header fields into Forwarded header field, and vice versa. --strip-incoming-forwarded Strip Forwarded header field from inbound client requests. --forwarded-by=(obfuscated|ip|) Specify the parameter value sent out with "by" parameter of Forwarded header field. If "obfuscated" is given, the string is randomly generated at startup. If "ip" is given, the interface address of the connection, including port number, is sent with "by" parameter. In case of UNIX domain socket, "localhost" is used instead of address and port. User can also specify the static obfuscated string. The limitation is that it must start with "_", and only consists of character set [A-Za-z0-9._-], as described in RFC 7239. Default: obfuscated --forwarded-for=(obfuscated|ip) Specify the parameter value sent out with "for" parameter of Forwarded header field. If "obfuscated" is given, the string is randomly generated for each client connection. If "ip" is given, the remote client address of the connection, without port number, is sent with "for" parameter. In case of UNIX domain socket, "localhost" is used instead of address. Default: obfuscated --no-via Don't append to Via header field. If Via header field is received, it is left unaltered. --no-strip-incoming-early-data Don't strip Early-Data header field from inbound client requests. --no-location-rewrite Don't rewrite location header field in default mode. When --http2-proxy is used, location header field will not be altered regardless of this option. --host-rewrite Rewrite host and :authority header fields in default mode. When --http2-proxy is used, these headers will not be altered regardless of this option. --altsvc= Specify protocol ID, port, host and origin of alternative service. , and are optional. Empty and are allowed and they are treated as nothing is specified. They are advertised in alt-svc header field only in HTTP/1.1 frontend. This option can be used multiple times to specify multiple alternative services. Example: --altsvc="h2,443,,,ma=3600; persist=1" --http2-altsvc= Just like --altsvc option, but this altsvc is only sent in HTTP/2 frontend. --add-request-header=
Specify additional header field to add to request header set. The field name must be lowercase. This option just appends header field and won't replace anything already set. This option can be used several times to specify multiple header fields. Example: --add-request-header="foo: bar" --add-response-header=
Specify additional header field to add to response header set. The field name must be lowercase. This option just appends header field and won't replace anything already set. This option can be used several times to specify multiple header fields. Example: --add-response-header="foo: bar" --request-header-field-buffer= Set maximum buffer size for incoming HTTP request header field list. This is the sum of header name and value in bytes. If trailer fields exist, they are counted towards this number. Default: )" << util::utos_unit(config->http.request_header_field_buffer) << R"( --max-request-header-fields= Set maximum number of incoming HTTP request header fields. If trailer fields exist, they are counted towards this number. Default: )" << config->http.max_request_header_fields << R"( --response-header-field-buffer= Set maximum buffer size for incoming HTTP response header field list. This is the sum of header name and value in bytes. If trailer fields exist, they are counted towards this number. Default: )" << util::utos_unit(config->http.response_header_field_buffer) << R"( --max-response-header-fields= Set maximum number of incoming HTTP response header fields. If trailer fields exist, they are counted towards this number. Default: )" << config->http.max_response_header_fields << R"( --error-page=(|*)= Set file path to custom error page served when nghttpx originally generates HTTP error status code . must be greater than or equal to 400, and at most 599. If "*" is used instead of , it matches all HTTP status code. If error status code comes from backend server, the custom error pages are not used. --server-name= Change server response header field value to . Default: )" << config->http.server_name << R"( --no-server-rewrite Don't rewrite server header field in default mode. When --http2-proxy is used, these headers will not be altered regardless of this option. --redirect-https-port= Specify the port number which appears in Location header field when redirect to HTTPS URI is made due to "redirect-if-not-tls" parameter in --backend option. Default: )" << config->http.redirect_https_port << R"( --require-http-scheme Always require http or https scheme in HTTP request. It also requires that https scheme must be used for an encrypted connection. Otherwise, http scheme must be used. This option is recommended for a server deployment which directly faces clients and the services it provides only require http or https scheme. API: --api-max-request-body= Set the maximum size of request body for API request. Default: )" << util::utos_unit(config->api.max_request_body) << R"( DNS: --dns-cache-timeout= Set duration that cached DNS results remain valid. Note that nghttpx caches the unsuccessful results as well. Default: )" << util::duration_str(config->dns.timeout.cache) << R"( --dns-lookup-timeout= Set timeout that DNS server is given to respond to the initial DNS query. For the 2nd and later queries, server is given time based on this timeout, and it is scaled linearly. Default: )" << util::duration_str(config->dns.timeout.lookup) << R"( --dns-max-try= Set the number of DNS query before nghttpx gives up name lookup. Default: )" << config->dns.max_try << R"( --frontend-max-requests= The number of requests that single frontend connection can process. For HTTP/2, this is the number of streams in one HTTP/2 connection. For HTTP/1, this is the number of keep alive requests. This is hint to nghttpx, and it may allow additional few requests. The default value is unlimited. Debug: --frontend-http2-dump-request-header= Dumps request headers received by HTTP/2 frontend to the file denoted in . The output is done in HTTP/1 header field format and each header block is followed by an empty line. This option is not thread safe and MUST NOT be used with option -n, where >= 2. --frontend-http2-dump-response-header= Dumps response headers sent from HTTP/2 frontend to the file denoted in . The output is done in HTTP/1 header field format and each header block is followed by an empty line. This option is not thread safe and MUST NOT be used with option -n, where >= 2. -o, --frontend-frame-debug Print HTTP/2 frames in frontend to stderr. This option is not thread safe and MUST NOT be used with option -n=N, where N >= 2. Process: -D, --daemon Run in a background. If -D is used, the current working directory is changed to '/'. --pid-file= Set path to save PID of this program. --user= Run this program as . This option is intended to be used to drop root privileges. --single-process Run this program in a single process mode for debugging purpose. Without this option, nghttpx creates at least 2 processes: main and worker processes. If this option is used, main and worker are unified into a single process. nghttpx still spawns additional process if neverbleed is used. In the single process mode, the signal handling feature is disabled. --max-worker-processes= The maximum number of worker processes. nghttpx spawns new worker process when it reloads its configuration. The previous worker process enters graceful termination period and will terminate when it finishes handling the existing connections. However, if reloading configurations happen very frequently, the worker processes might be piled up if they take a bit long time to finish the existing connections. With this option, if the number of worker processes exceeds the given value, the oldest worker process is terminated immediately. Specifying 0 means no limit and it is the default behaviour. --worker-process-grace-shutdown-period= Maximum period for a worker process to terminate gracefully. When a worker process enters in graceful shutdown period (e.g., when nghttpx reloads its configuration) and it does not finish handling the existing connections in the given period of time, it is immediately terminated. Specifying 0 means no limit and it is the default behaviour. Scripting: --mruby-file= Set mruby script file --ignore-per-pattern-mruby-error Ignore mruby compile error for per-pattern mruby script file. If error occurred, it is treated as if no mruby file were specified for the pattern. )"; #ifdef ENABLE_HTTP3 out << R"( HTTP/3 and QUIC: --frontend-quic-idle-timeout= Specify an idle timeout for QUIC connection. Default: )" << util::duration_str(config->quic.upstream.timeout.idle) << R"( --frontend-quic-debug-log Output QUIC debug log to /dev/stderr. --quic-bpf-program-file= Specify a path to eBPF program file reuseport_kern.o to direct an incoming QUIC UDP datagram to a correct socket. Default: )" << config->quic.bpf.prog_file << R"( --frontend-quic-early-data Enable early data on frontend QUIC connections. nghttpx sends "Early-Data" header field to a backend server if a request is received in early data and handshake has not finished. All backend servers should deal with possibly replayed requests. --frontend-quic-qlog-dir= Specify a directory where a qlog file is written for frontend QUIC connections. A qlog file is created per each QUIC connection. The file name is ISO8601 basic format, followed by "-", server Source Connection ID and ".sqlog". --frontend-quic-require-token Require an address validation token for a frontend QUIC connection. Server sends a token in Retry packet or NEW_TOKEN frame in the previous connection. --frontend-quic-congestion-controller= Specify a congestion controller algorithm for a frontend QUIC connection. should be either "cubic" or "bbr". Default: )" << (config->quic.upstream.congestion_controller == NGTCP2_CC_ALGO_CUBIC ? "cubic" : "bbr") << R"( --frontend-quic-secret-file= Path to file that contains secure random data to be used as QUIC keying materials. It is used to derive keys for encrypting tokens and Connection IDs. It is not used to encrypt QUIC packets. Each line of this file must contain exactly 136 bytes hex-encoded string (when decoded the byte string is 68 bytes long). The first 3 bits of decoded byte string are used to identify the keying material. An empty line or a line which starts '#' is ignored. The file can contain more than one keying materials. Because the identifier is 3 bits, at most 8 keying materials are read and the remaining data is discarded. The first keying material in the file is primarily used for encryption and decryption for new connection. The other ones are used to decrypt data for the existing connections. Specifying multiple keying materials enables key rotation. Please note that key rotation does not occur automatically. User should update files or change options values and restart nghttpx gracefully. If opening or reading given file fails, all loaded keying materials are discarded and it is treated as if none of this option is given. If this option is not given or an error occurred while opening or reading a file, a keying material is generated internally on startup and reload. --quic-server-id= Specify server ID encoded in Connection ID to identify this particular server instance. Connection ID is encrypted and this part is not visible in public. It must be 4 bytes long and must be encoded in hex string (which is 8 bytes long). If this option is omitted, a random server ID is generated on startup and configuration reload. --frontend-quic-initial-rtt= Specify the initial RTT of the frontend QUIC connection. Default: )" << util::duration_str(config->quic.upstream.initial_rtt) << R"( --no-quic-bpf Disable eBPF. --frontend-http3-window-size= Sets the per-stream initial window size of HTTP/3 frontend connection. Default: )" << util::utos_unit(as_unsigned(config->http3.upstream.window_size)) << R"( --frontend-http3-connection-window-size= Sets the per-connection window size of HTTP/3 frontend connection. Default: )" << util::utos_unit( as_unsigned(config->http3.upstream.connection_window_size)) << R"( --frontend-http3-max-window-size= Sets the maximum per-stream window size of HTTP/3 frontend connection. The window size is adjusted based on the receiving rate of stream data. The initial value is the value specified by --frontend-http3-window-size and the window size grows up to bytes. Default: )" << util::utos_unit(as_unsigned(config->http3.upstream.max_window_size)) << R"( --frontend-http3-max-connection-window-size= Sets the maximum per-connection window size of HTTP/3 frontend connection. The window size is adjusted based on the receiving rate of stream data. The initial value is the value specified by --frontend-http3-connection-window-size and the window size grows up to bytes. Default: )" << util::utos_unit( as_unsigned(config->http3.upstream.max_connection_window_size)) << R"( --frontend-http3-max-concurrent-streams= Set the maximum number of the concurrent streams in one frontend HTTP/3 connection. Default: )" << config->http3.upstream.max_concurrent_streams << R"( )"; #endif // ENABLE_HTTP3 out << R"( Misc: --conf= Load configuration from . Please note that nghttpx always tries to read the default configuration file if --conf is not given. Default: )" << config->conf_path << R"( --include= Load additional configurations from . File is read when configuration parser encountered this option. This option can be used multiple times, or even recursively. -v, --version Print version and exit. -h, --help Print this help and exit. -- The argument is an integer and an optional unit (e.g., 10K is 10 * 1024). Units are K, M and G (powers of 1024). The argument is an integer and an optional unit (e.g., 1s is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms (hours, minutes, seconds and milliseconds, respectively). If a unit is omitted, a second is used as unit.)" << std::endl; } } // namespace namespace { int process_options( Config *config, std::vector> &cmdcfgs) { std::array errbuf; std::unordered_map pattern_addr_indexer; if (conf_exists(config->conf_path.data())) { LOG(NOTICE) << "Loading configuration from " << config->conf_path; std::unordered_set include_set; if (load_config(config, config->conf_path.data(), include_set, pattern_addr_indexer) == -1) { LOG(FATAL) << "Failed to load configuration from " << config->conf_path; return -1; } assert(include_set.empty()); } // Reopen log files using configurations in file reopen_log_files(config->logging); { std::unordered_set include_set; for (auto &p : cmdcfgs) { if (parse_config(config, p.first, p.second, include_set, pattern_addr_indexer) == -1) { LOG(FATAL) << "Failed to parse command-line argument."; return -1; } } assert(include_set.empty()); } Log::set_severity_level(config->logging.severity); auto &loggingconf = config->logging; if (loggingconf.access.syslog || loggingconf.error.syslog) { openlog("nghttpx", LOG_NDELAY | LOG_NOWAIT | LOG_PID, loggingconf.syslog_facility); } if (reopen_log_files(config->logging) != 0) { LOG(FATAL) << "Failed to open log file"; return -1; } redirect_stderr_to_errorlog(loggingconf); if (config->uid != 0) { if (log_config()->accesslog_fd != -1 && fchown(log_config()->accesslog_fd, config->uid, config->gid) == -1) { auto error = errno; LOG(WARN) << "Changing owner of access log file failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); } if (log_config()->errorlog_fd != -1 && fchown(log_config()->errorlog_fd, config->uid, config->gid) == -1) { auto error = errno; LOG(WARN) << "Changing owner of error log file failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); } } if (config->single_thread) { LOG(WARN) << "single-thread: Set workers to 1"; config->num_worker = 1; } auto &http2conf = config->http2; { auto &dumpconf = http2conf.upstream.debug.dump; if (!dumpconf.request_header_file.empty()) { auto path = dumpconf.request_header_file.data(); auto f = open_file_for_write(path); if (f == nullptr) { LOG(FATAL) << "Failed to open http2 upstream request header file: " << path; return -1; } dumpconf.request_header = f; if (config->uid != 0) { if (chown(path, config->uid, config->gid) == -1) { auto error = errno; LOG(WARN) << "Changing owner of http2 upstream request header file " << path << " failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); } } } if (!dumpconf.response_header_file.empty()) { auto path = dumpconf.response_header_file.data(); auto f = open_file_for_write(path); if (f == nullptr) { LOG(FATAL) << "Failed to open http2 upstream response header file: " << path; return -1; } dumpconf.response_header = f; if (config->uid != 0) { if (chown(path, config->uid, config->gid) == -1) { auto error = errno; LOG(WARN) << "Changing owner of http2 upstream response header file" << " " << path << " failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); } } } } auto &tlsconf = config->tls; if (tlsconf.alpn_list.empty()) { tlsconf.alpn_list = util::split_str(DEFAULT_ALPN_LIST, ','); } if (!tlsconf.tls_proto_list.empty()) { tlsconf.tls_proto_mask = tls::create_tls_proto_mask(tlsconf.tls_proto_list); } // TODO We depends on the ordering of protocol version macro in // OpenSSL. if (tlsconf.min_proto_version > tlsconf.max_proto_version) { LOG(ERROR) << "tls-max-proto-version must be equal to or larger than " "tls-min-proto-version"; return -1; } if (tls::set_alpn_prefs(tlsconf.alpn_prefs, tlsconf.alpn_list) != 0) { return -1; } auto &listenerconf = config->conn.listener; auto &upstreamconf = config->conn.upstream; if (listenerconf.addrs.empty()) { UpstreamAddr addr{ .host = "*"sv, .port = 3000, .family = AF_INET, .tls = true, }; listenerconf.addrs.push_back(addr); addr.family = AF_INET6; addr.index = 1; listenerconf.addrs.push_back(std::move(addr)); } if (upstreamconf.worker_connections == 0) { upstreamconf.worker_connections = std::numeric_limits::max(); } if (tls::upstream_tls_enabled(config->conn) && (tlsconf.private_key_file.empty() || tlsconf.cert_file.empty())) { LOG(FATAL) << "TLS private key and certificate files are required. " "Specify them in command-line, or in configuration file " "using private-key-file and certificate-file options."; return -1; } if (configure_downstream_group(config, config->http2_proxy, false, tlsconf) != 0) { return -1; } std::array hostport_buf; auto &proxy = config->downstream_http_proxy; if (!proxy.host.empty()) { auto hostport = util::make_hostport(proxy.host, proxy.port, std::ranges::begin(hostport_buf)); if (resolve_hostname(&proxy.addr, proxy.host.data(), proxy.port, AF_UNSPEC) == -1) { LOG(FATAL) << "Resolving backend HTTP proxy address failed: " << hostport; return -1; } LOG(NOTICE) << "Backend HTTP proxy address: " << hostport << " -> " << util::to_numeric_addr(&proxy.addr); } { auto &memcachedconf = tlsconf.ticket.memcached; if (!memcachedconf.host.empty()) { auto hostport = util::make_hostport(memcachedconf.host, memcachedconf.port, std::ranges::begin(hostport_buf)); if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.data(), memcachedconf.port, memcachedconf.family) == -1) { LOG(FATAL) << "Resolving memcached address for TLS ticket key failed: " << hostport; return -1; } LOG(NOTICE) << "Memcached address for TLS ticket key: " << hostport << " -> " << util::to_numeric_addr(&memcachedconf.addr); if (memcachedconf.tls) { LOG(NOTICE) << "Connection to memcached for TLS ticket key will be " "encrypted by TLS"; } } } if (config->rlimit_nofile) { struct rlimit lim = {static_cast(config->rlimit_nofile), static_cast(config->rlimit_nofile)}; if (setrlimit(RLIMIT_NOFILE, &lim) != 0) { auto error = errno; LOG(WARN) << "Setting rlimit-nofile failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); } } #ifdef RLIMIT_MEMLOCK if (config->rlimit_memlock) { struct rlimit lim = {static_cast(config->rlimit_memlock), static_cast(config->rlimit_memlock)}; if (setrlimit(RLIMIT_MEMLOCK, &lim) != 0) { auto error = errno; LOG(WARN) << "Setting rlimit-memlock failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); } } #endif // RLIMIT_MEMLOCK auto &fwdconf = config->http.forwarded; if (fwdconf.by_node_type == ForwardedNode::OBFUSCATED && fwdconf.by_obfuscated.empty()) { // 2 for '_' and terminal NULL auto iov = make_byte_ref(config->balloc, SHRPX_OBFUSCATED_NODE_LENGTH + 2); auto p = std::ranges::begin(iov); *p++ = '_'; auto gen = util::make_mt19937(); p = util::random_alpha_digit(p, p + SHRPX_OBFUSCATED_NODE_LENGTH, gen); *p = '\0'; fwdconf.by_obfuscated = as_string_view(std::ranges::begin(iov), p); } if (config->http2.upstream.debug.frame_debug) { // To make it sync to logging set_output(stderr); if (isatty(fileno(stdout))) { set_color_output(true); } reset_timer(); } config->http2.upstream.callbacks = create_http2_upstream_callbacks(); config->http2.downstream.callbacks = create_http2_downstream_callbacks(); if (!config->http.altsvcs.empty()) { config->http.altsvc_header_value = http::create_altsvc_header_value(config->balloc, config->http.altsvcs); } if (!config->http.http2_altsvcs.empty()) { config->http.http2_altsvc_header_value = http::create_altsvc_header_value( config->balloc, config->http.http2_altsvcs); } return 0; } } // namespace namespace { // Closes file descriptor which are opened for listeners in config, // and are not inherited from |iaddrs|. void close_not_inherited_fd( Config *config, const std::vector &iaddrs) { auto &listenerconf = config->conn.listener; for (auto &addr : listenerconf.addrs) { if (!addr.host_unix) { continue; } auto inherited = std::ranges::find_if( iaddrs, [&addr](const auto &iaddr) { return addr.fd == iaddr.fd; }); if (inherited != std::ranges::end(iaddrs)) { continue; } close(addr.fd); } } } // namespace namespace { void reload_config() { int rv; LOG(NOTICE) << "Reloading configuration"; auto cur_config = mod_config(); auto new_config = std::make_unique(); fill_default_config(new_config.get()); new_config->conf_path = make_string_ref(new_config->balloc, cur_config->conf_path); // daemon option is ignored here. new_config->daemon = cur_config->daemon; // loop is reused, and ev_loop_flags gets ignored new_config->ev_loop_flags = cur_config->ev_loop_flags; new_config->config_revision = cur_config->config_revision + 1; rv = process_options(new_config.get(), suconfig.cmdcfgs); if (rv != 0) { LOG(ERROR) << "Failed to process new configuration"; return; } auto iaddrs = get_inherited_unix_domain_socket_from_config(new_config->balloc, cur_config); if (create_unix_domain_listener_socket(new_config.get(), iaddrs) != 0) { close_not_inherited_fd(new_config.get(), iaddrs); return; } // According to libev documentation, flags are ignored since we have // already created first default loop. auto loop = ev_default_loop(new_config->ev_loop_flags); int ipc_fd = 0; #ifdef ENABLE_HTTP3 int quic_ipc_fd = 0; auto quic_lwps = collect_quic_lingering_worker_processes(); std::vector worker_ids; if (generate_worker_id(worker_ids, worker_process_seq, new_config.get()) != 0) { close_not_inherited_fd(new_config.get(), iaddrs); return; } #endif // ENABLE_HTTP3 // fork_worker_process and forked child process assumes new // configuration can be obtained from get_config(). auto old_config = replace_config(std::move(new_config)); auto pid = fork_worker_process(ipc_fd #ifdef ENABLE_HTTP3 , quic_ipc_fd #endif // ENABLE_HTTP3 , iaddrs #ifdef ENABLE_HTTP3 , worker_ids, std::move(quic_lwps) #endif // ENABLE_HTTP3 ); if (pid == -1) { LOG(ERROR) << "Failed to process new configuration"; new_config = replace_config(std::move(old_config)); close_not_inherited_fd(new_config.get(), iaddrs); return; } close_unused_inherited_addr(iaddrs); worker_process_add(std::make_unique( loop, pid, ipc_fd #ifdef ENABLE_HTTP3 , quic_ipc_fd, std::move(worker_ids), worker_process_seq++ #endif // ENABLE_HTTP3 )); worker_process_adjust_limit(); if (!get_config()->pid_file.empty()) { save_pid(); } } } // namespace int main(int argc, char **argv) { int rv; std::array errbuf; #ifdef HAVE_LIBBPF libbpf_set_strict_mode(LIBBPF_STRICT_ALL); #endif // HAVE_LIBBPF Log::set_severity_level(NOTICE); create_config(); fill_default_config(mod_config()); // make copy of stderr store_original_fds(); // First open log files with default configuration, so that we can // log errors/warnings while reading configuration files. reopen_log_files(get_config()->logging); suconfig.original_argv = argv; // We have to copy argv, since getopt_long may change its content. suconfig.argc = static_cast(argc); suconfig.argv = new char *[static_cast(argc)]; for (int i = 0; i < argc; ++i) { suconfig.argv[i] = strdup(argv[i]); if (suconfig.argv[i] == nullptr) { auto error = errno; LOG(FATAL) << "failed to copy argv: " << xsi_strerror(error, errbuf.data(), errbuf.size()); exit(EXIT_FAILURE); } } suconfig.cwd = getcwd(nullptr, 0); if (suconfig.cwd == nullptr) { auto error = errno; LOG(FATAL) << "failed to get current working directory: errno=" << error; exit(EXIT_FAILURE); } auto &cmdcfgs = suconfig.cmdcfgs; while (1) { static int flag = 0; static constexpr option long_options[] = { {SHRPX_OPT_DAEMON.data(), no_argument, nullptr, 'D'}, {SHRPX_OPT_LOG_LEVEL.data(), required_argument, nullptr, 'L'}, {SHRPX_OPT_BACKEND.data(), required_argument, nullptr, 'b'}, {SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS.data(), required_argument, nullptr, 'c'}, {SHRPX_OPT_FRONTEND.data(), required_argument, nullptr, 'f'}, {"help", no_argument, nullptr, 'h'}, {SHRPX_OPT_INSECURE.data(), no_argument, nullptr, 'k'}, {SHRPX_OPT_WORKERS.data(), required_argument, nullptr, 'n'}, {SHRPX_OPT_CLIENT_PROXY.data(), no_argument, nullptr, 'p'}, {SHRPX_OPT_HTTP2_PROXY.data(), no_argument, nullptr, 's'}, {"version", no_argument, nullptr, 'v'}, {SHRPX_OPT_FRONTEND_FRAME_DEBUG.data(), no_argument, nullptr, 'o'}, {SHRPX_OPT_ADD_X_FORWARDED_FOR.data(), no_argument, &flag, 1}, {SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT.data(), required_argument, &flag, 2}, {SHRPX_OPT_FRONTEND_READ_TIMEOUT.data(), required_argument, &flag, 3}, {SHRPX_OPT_FRONTEND_WRITE_TIMEOUT.data(), required_argument, &flag, 4}, {SHRPX_OPT_BACKEND_READ_TIMEOUT.data(), required_argument, &flag, 5}, {SHRPX_OPT_BACKEND_WRITE_TIMEOUT.data(), required_argument, &flag, 6}, {SHRPX_OPT_ACCESSLOG_FILE.data(), required_argument, &flag, 7}, {SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT.data(), required_argument, &flag, 8}, {SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS.data(), required_argument, &flag, 9}, {SHRPX_OPT_PID_FILE.data(), required_argument, &flag, 10}, {SHRPX_OPT_USER.data(), required_argument, &flag, 11}, {"conf", required_argument, &flag, 12}, {SHRPX_OPT_SYSLOG_FACILITY.data(), required_argument, &flag, 14}, {SHRPX_OPT_BACKLOG.data(), required_argument, &flag, 15}, {SHRPX_OPT_CIPHERS.data(), required_argument, &flag, 16}, {SHRPX_OPT_CLIENT.data(), no_argument, &flag, 17}, {SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS.data(), required_argument, &flag, 18}, {SHRPX_OPT_CACERT.data(), required_argument, &flag, 19}, {SHRPX_OPT_BACKEND_IPV4.data(), no_argument, &flag, 20}, {SHRPX_OPT_BACKEND_IPV6.data(), no_argument, &flag, 21}, {SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE.data(), required_argument, &flag, 22}, {SHRPX_OPT_NO_VIA.data(), no_argument, &flag, 23}, {SHRPX_OPT_SUBCERT.data(), required_argument, &flag, 24}, {SHRPX_OPT_HTTP2_BRIDGE.data(), no_argument, &flag, 25}, {SHRPX_OPT_BACKEND_HTTP_PROXY_URI.data(), required_argument, &flag, 26}, {SHRPX_OPT_BACKEND_NO_TLS.data(), no_argument, &flag, 27}, {SHRPX_OPT_OCSP_STARTUP.data(), no_argument, &flag, 28}, {SHRPX_OPT_FRONTEND_NO_TLS.data(), no_argument, &flag, 29}, {SHRPX_OPT_NO_VERIFY_OCSP.data(), no_argument, &flag, 30}, {SHRPX_OPT_BACKEND_TLS_SNI_FIELD.data(), required_argument, &flag, 31}, {SHRPX_OPT_DH_PARAM_FILE.data(), required_argument, &flag, 33}, {SHRPX_OPT_READ_RATE.data(), required_argument, &flag, 34}, {SHRPX_OPT_READ_BURST.data(), required_argument, &flag, 35}, {SHRPX_OPT_WRITE_RATE.data(), required_argument, &flag, 36}, {SHRPX_OPT_WRITE_BURST.data(), required_argument, &flag, 37}, {SHRPX_OPT_NPN_LIST.data(), required_argument, &flag, 38}, {SHRPX_OPT_VERIFY_CLIENT.data(), no_argument, &flag, 39}, {SHRPX_OPT_VERIFY_CLIENT_CACERT.data(), required_argument, &flag, 40}, {SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE.data(), required_argument, &flag, 41}, {SHRPX_OPT_CLIENT_CERT_FILE.data(), required_argument, &flag, 42}, {SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER.data(), required_argument, &flag, 43}, {SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER.data(), required_argument, &flag, 44}, {SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING.data(), no_argument, &flag, 45}, {SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS.data(), required_argument, &flag, 46}, {SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS.data(), required_argument, &flag, 47}, {SHRPX_OPT_TLS_PROTO_LIST.data(), required_argument, &flag, 48}, {SHRPX_OPT_PADDING.data(), required_argument, &flag, 49}, {SHRPX_OPT_WORKER_READ_RATE.data(), required_argument, &flag, 50}, {SHRPX_OPT_WORKER_READ_BURST.data(), required_argument, &flag, 51}, {SHRPX_OPT_WORKER_WRITE_RATE.data(), required_argument, &flag, 52}, {SHRPX_OPT_WORKER_WRITE_BURST.data(), required_argument, &flag, 53}, {SHRPX_OPT_ALTSVC.data(), required_argument, &flag, 54}, {SHRPX_OPT_ADD_RESPONSE_HEADER.data(), required_argument, &flag, 55}, {SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS.data(), required_argument, &flag, 56}, {SHRPX_OPT_ACCESSLOG_SYSLOG.data(), no_argument, &flag, 57}, {SHRPX_OPT_ERRORLOG_FILE.data(), required_argument, &flag, 58}, {SHRPX_OPT_ERRORLOG_SYSLOG.data(), no_argument, &flag, 59}, {SHRPX_OPT_STREAM_READ_TIMEOUT.data(), required_argument, &flag, 60}, {SHRPX_OPT_STREAM_WRITE_TIMEOUT.data(), required_argument, &flag, 61}, {SHRPX_OPT_NO_LOCATION_REWRITE.data(), no_argument, &flag, 62}, {SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST.data(), required_argument, &flag, 63}, {SHRPX_OPT_LISTENER_DISABLE_TIMEOUT.data(), required_argument, &flag, 64}, {SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR.data(), no_argument, &flag, 65}, {SHRPX_OPT_ACCESSLOG_FORMAT.data(), required_argument, &flag, 66}, {SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND.data(), required_argument, &flag, 67}, {SHRPX_OPT_TLS_TICKET_KEY_FILE.data(), required_argument, &flag, 68}, {SHRPX_OPT_RLIMIT_NOFILE.data(), required_argument, &flag, 69}, {SHRPX_OPT_BACKEND_RESPONSE_BUFFER.data(), required_argument, &flag, 71}, {SHRPX_OPT_BACKEND_REQUEST_BUFFER.data(), required_argument, &flag, 72}, {SHRPX_OPT_NO_HOST_REWRITE.data(), no_argument, &flag, 73}, {SHRPX_OPT_NO_SERVER_PUSH.data(), no_argument, &flag, 74}, {SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER.data(), required_argument, &flag, 76}, {SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE.data(), required_argument, &flag, 77}, {SHRPX_OPT_OCSP_UPDATE_INTERVAL.data(), required_argument, &flag, 78}, {SHRPX_OPT_NO_OCSP.data(), no_argument, &flag, 79}, {SHRPX_OPT_HEADER_FIELD_BUFFER.data(), required_argument, &flag, 80}, {SHRPX_OPT_MAX_HEADER_FIELDS.data(), required_argument, &flag, 81}, {SHRPX_OPT_ADD_REQUEST_HEADER.data(), required_argument, &flag, 82}, {SHRPX_OPT_INCLUDE.data(), required_argument, &flag, 83}, {SHRPX_OPT_TLS_TICKET_KEY_CIPHER.data(), required_argument, &flag, 84}, {SHRPX_OPT_HOST_REWRITE.data(), no_argument, &flag, 85}, {SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED.data(), required_argument, &flag, 86}, {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED.data(), required_argument, &flag, 87}, {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_INTERVAL.data(), required_argument, &flag, 88}, {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY.data(), required_argument, &flag, 89}, {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL.data(), required_argument, &flag, 90}, {SHRPX_OPT_MRUBY_FILE.data(), required_argument, &flag, 91}, {SHRPX_OPT_ACCEPT_PROXY_PROTOCOL.data(), no_argument, &flag, 93}, {SHRPX_OPT_FASTOPEN.data(), required_argument, &flag, 94}, {SHRPX_OPT_TLS_DYN_REC_WARMUP_THRESHOLD.data(), required_argument, &flag, 95}, {SHRPX_OPT_TLS_DYN_REC_IDLE_TIMEOUT.data(), required_argument, &flag, 96}, {SHRPX_OPT_ADD_FORWARDED.data(), required_argument, &flag, 97}, {SHRPX_OPT_STRIP_INCOMING_FORWARDED.data(), no_argument, &flag, 98}, {SHRPX_OPT_FORWARDED_BY.data(), required_argument, &flag, 99}, {SHRPX_OPT_FORWARDED_FOR.data(), required_argument, &flag, 100}, {SHRPX_OPT_RESPONSE_HEADER_FIELD_BUFFER.data(), required_argument, &flag, 101}, {SHRPX_OPT_MAX_RESPONSE_HEADER_FIELDS.data(), required_argument, &flag, 102}, {SHRPX_OPT_NO_HTTP2_CIPHER_BLACK_LIST.data(), no_argument, &flag, 103}, {SHRPX_OPT_REQUEST_HEADER_FIELD_BUFFER.data(), required_argument, &flag, 104}, {SHRPX_OPT_MAX_REQUEST_HEADER_FIELDS.data(), required_argument, &flag, 105}, {SHRPX_OPT_BACKEND_HTTP1_TLS.data(), no_argument, &flag, 106}, {SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_TLS.data(), no_argument, &flag, 108}, {SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE.data(), required_argument, &flag, 109}, {SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE.data(), required_argument, &flag, 110}, {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_TLS.data(), no_argument, &flag, 111}, {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_CERT_FILE.data(), required_argument, &flag, 112}, {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE.data(), required_argument, &flag, 113}, {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY.data(), required_argument, &flag, 114}, {SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY.data(), required_argument, &flag, 115}, {SHRPX_OPT_BACKEND_ADDRESS_FAMILY.data(), required_argument, &flag, 116}, {SHRPX_OPT_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS.data(), required_argument, &flag, 117}, {SHRPX_OPT_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS.data(), required_argument, &flag, 118}, {SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND.data(), required_argument, &flag, 119}, {SHRPX_OPT_BACKEND_TLS.data(), no_argument, &flag, 120}, {SHRPX_OPT_BACKEND_CONNECTIONS_PER_HOST.data(), required_argument, &flag, 121}, {SHRPX_OPT_ERROR_PAGE.data(), required_argument, &flag, 122}, {SHRPX_OPT_NO_KQUEUE.data(), no_argument, &flag, 123}, {SHRPX_OPT_FRONTEND_HTTP2_SETTINGS_TIMEOUT.data(), required_argument, &flag, 124}, {SHRPX_OPT_BACKEND_HTTP2_SETTINGS_TIMEOUT.data(), required_argument, &flag, 125}, {SHRPX_OPT_API_MAX_REQUEST_BODY.data(), required_argument, &flag, 126}, {SHRPX_OPT_BACKEND_MAX_BACKOFF.data(), required_argument, &flag, 127}, {SHRPX_OPT_SERVER_NAME.data(), required_argument, &flag, 128}, {SHRPX_OPT_NO_SERVER_REWRITE.data(), no_argument, &flag, 129}, {SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE.data(), no_argument, &flag, 130}, {SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE.data(), no_argument, &flag, 131}, {SHRPX_OPT_FRONTEND_HTTP2_WINDOW_SIZE.data(), required_argument, &flag, 132}, {SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE.data(), required_argument, &flag, 133}, {SHRPX_OPT_BACKEND_HTTP2_WINDOW_SIZE.data(), required_argument, &flag, 134}, {SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE.data(), required_argument, &flag, 135}, {SHRPX_OPT_FRONTEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE.data(), required_argument, &flag, 136}, {SHRPX_OPT_FRONTEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE.data(), required_argument, &flag, 137}, {SHRPX_OPT_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE.data(), required_argument, &flag, 138}, {SHRPX_OPT_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE.data(), required_argument, &flag, 139}, {SHRPX_OPT_ECDH_CURVES.data(), required_argument, &flag, 140}, {SHRPX_OPT_TLS_SCT_DIR.data(), required_argument, &flag, 141}, {SHRPX_OPT_BACKEND_CONNECT_TIMEOUT.data(), required_argument, &flag, 142}, {SHRPX_OPT_DNS_CACHE_TIMEOUT.data(), required_argument, &flag, 143}, {SHRPX_OPT_DNS_LOOKUP_TIMEOUT.data(), required_argument, &flag, 144}, {SHRPX_OPT_DNS_MAX_TRY.data(), required_argument, &flag, 145}, {SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT.data(), required_argument, &flag, 146}, {SHRPX_OPT_PSK_SECRETS.data(), required_argument, &flag, 147}, {SHRPX_OPT_CLIENT_PSK_SECRETS.data(), required_argument, &flag, 148}, {SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST.data(), no_argument, &flag, 149}, {SHRPX_OPT_CLIENT_CIPHERS.data(), required_argument, &flag, 150}, {SHRPX_OPT_ACCESSLOG_WRITE_EARLY.data(), no_argument, &flag, 151}, {SHRPX_OPT_TLS_MIN_PROTO_VERSION.data(), required_argument, &flag, 152}, {SHRPX_OPT_TLS_MAX_PROTO_VERSION.data(), required_argument, &flag, 153}, {SHRPX_OPT_REDIRECT_HTTPS_PORT.data(), required_argument, &flag, 154}, {SHRPX_OPT_FRONTEND_MAX_REQUESTS.data(), required_argument, &flag, 155}, {SHRPX_OPT_SINGLE_THREAD.data(), no_argument, &flag, 156}, {SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO.data(), no_argument, &flag, 157}, {SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO.data(), no_argument, &flag, 158}, {SHRPX_OPT_SINGLE_PROCESS.data(), no_argument, &flag, 159}, {SHRPX_OPT_VERIFY_CLIENT_TOLERATE_EXPIRED.data(), no_argument, &flag, 160}, {SHRPX_OPT_IGNORE_PER_PATTERN_MRUBY_ERROR.data(), no_argument, &flag, 161}, {SHRPX_OPT_TLS_NO_POSTPONE_EARLY_DATA.data(), no_argument, &flag, 162}, {SHRPX_OPT_TLS_MAX_EARLY_DATA.data(), required_argument, &flag, 163}, {SHRPX_OPT_TLS13_CIPHERS.data(), required_argument, &flag, 164}, {SHRPX_OPT_TLS13_CLIENT_CIPHERS.data(), required_argument, &flag, 165}, {SHRPX_OPT_NO_STRIP_INCOMING_EARLY_DATA.data(), no_argument, &flag, 166}, {SHRPX_OPT_NO_HTTP2_CIPHER_BLOCK_LIST.data(), no_argument, &flag, 167}, {SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLOCK_LIST.data(), no_argument, &flag, 168}, {SHRPX_OPT_QUIC_BPF_PROGRAM_FILE.data(), required_argument, &flag, 169}, {SHRPX_OPT_NO_QUIC_BPF.data(), no_argument, &flag, 170}, {SHRPX_OPT_HTTP2_ALTSVC.data(), required_argument, &flag, 171}, {SHRPX_OPT_FRONTEND_HTTP3_READ_TIMEOUT.data(), required_argument, &flag, 172}, {SHRPX_OPT_FRONTEND_QUIC_IDLE_TIMEOUT.data(), required_argument, &flag, 173}, {SHRPX_OPT_FRONTEND_QUIC_DEBUG_LOG.data(), no_argument, &flag, 174}, {SHRPX_OPT_FRONTEND_HTTP3_WINDOW_SIZE.data(), required_argument, &flag, 175}, {SHRPX_OPT_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE.data(), required_argument, &flag, 176}, {SHRPX_OPT_FRONTEND_HTTP3_MAX_WINDOW_SIZE.data(), required_argument, &flag, 177}, {SHRPX_OPT_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE.data(), required_argument, &flag, 178}, {SHRPX_OPT_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS.data(), required_argument, &flag, 179}, {SHRPX_OPT_FRONTEND_QUIC_EARLY_DATA.data(), no_argument, &flag, 180}, {SHRPX_OPT_FRONTEND_QUIC_QLOG_DIR.data(), required_argument, &flag, 181}, {SHRPX_OPT_FRONTEND_QUIC_REQUIRE_TOKEN.data(), no_argument, &flag, 182}, {SHRPX_OPT_FRONTEND_QUIC_CONGESTION_CONTROLLER.data(), required_argument, &flag, 183}, {SHRPX_OPT_QUIC_SERVER_ID.data(), required_argument, &flag, 185}, {SHRPX_OPT_FRONTEND_QUIC_SECRET_FILE.data(), required_argument, &flag, 186}, {SHRPX_OPT_RLIMIT_MEMLOCK.data(), required_argument, &flag, 187}, {SHRPX_OPT_MAX_WORKER_PROCESSES.data(), required_argument, &flag, 188}, {SHRPX_OPT_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD.data(), required_argument, &flag, 189}, {SHRPX_OPT_FRONTEND_QUIC_INITIAL_RTT.data(), required_argument, &flag, 190}, {SHRPX_OPT_REQUIRE_HTTP_SCHEME.data(), no_argument, &flag, 191}, {SHRPX_OPT_TLS_KTLS.data(), no_argument, &flag, 192}, {SHRPX_OPT_ALPN_LIST.data(), required_argument, &flag, 193}, {SHRPX_OPT_FRONTEND_HEADER_TIMEOUT.data(), required_argument, &flag, 194}, {SHRPX_OPT_FRONTEND_HTTP2_IDLE_TIMEOUT.data(), required_argument, &flag, 195}, {SHRPX_OPT_FRONTEND_HTTP3_IDLE_TIMEOUT.data(), required_argument, &flag, 196}, {SHRPX_OPT_GROUPS.data(), required_argument, &flag, 197}, {nullptr, 0, nullptr, 0}}; int option_index = 0; int c = getopt_long(argc, argv, "DL:b:c:f:hkn:opsv", long_options, &option_index); if (c == -1) { break; } switch (c) { case 'D': cmdcfgs.emplace_back(SHRPX_OPT_DAEMON, "yes"sv); break; case 'L': cmdcfgs.emplace_back(SHRPX_OPT_LOG_LEVEL, std::string_view{optarg}); break; case 'b': cmdcfgs.emplace_back(SHRPX_OPT_BACKEND, std::string_view{optarg}); break; case 'c': cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS, std::string_view{optarg}); break; case 'f': cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND, std::string_view{optarg}); break; case 'h': print_help(std::cout); exit(EXIT_SUCCESS); case 'k': cmdcfgs.emplace_back(SHRPX_OPT_INSECURE, "yes"sv); break; case 'n': cmdcfgs.emplace_back(SHRPX_OPT_WORKERS, std::string_view{optarg}); break; case 'o': cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_FRAME_DEBUG, "yes"sv); break; case 'p': cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PROXY, "yes"sv); break; case 's': cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_PROXY, "yes"sv); break; case 'v': print_version(std::cout); exit(EXIT_SUCCESS); case '?': util::show_candidates(argv[optind - 1], long_options); exit(EXIT_FAILURE); case 0: switch (flag) { case 1: // --add-x-forwarded-for cmdcfgs.emplace_back(SHRPX_OPT_ADD_X_FORWARDED_FOR, "yes"sv); break; case 2: // --frontend-http2-read-timeout cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT, std::string_view{optarg}); break; case 3: // --frontend-read-timeout cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_READ_TIMEOUT, std::string_view{optarg}); break; case 4: // --frontend-write-timeout cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_WRITE_TIMEOUT, std::string_view{optarg}); break; case 5: // --backend-read-timeout cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_READ_TIMEOUT, std::string_view{optarg}); break; case 6: // --backend-write-timeout cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_WRITE_TIMEOUT, std::string_view{optarg}); break; case 7: cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_FILE, std::string_view{optarg}); break; case 8: // --backend-keep-alive-timeout cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT, std::string_view{optarg}); break; case 9: // --frontend-http2-window-bits cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS, std::string_view{optarg}); break; case 10: cmdcfgs.emplace_back(SHRPX_OPT_PID_FILE, std::string_view{optarg}); break; case 11: cmdcfgs.emplace_back(SHRPX_OPT_USER, std::string_view{optarg}); break; case 12: // --conf mod_config()->conf_path = make_string_ref(mod_config()->balloc, std::string_view{optarg}); break; case 14: // --syslog-facility cmdcfgs.emplace_back(SHRPX_OPT_SYSLOG_FACILITY, std::string_view{optarg}); break; case 15: // --backlog cmdcfgs.emplace_back(SHRPX_OPT_BACKLOG, std::string_view{optarg}); break; case 16: // --ciphers cmdcfgs.emplace_back(SHRPX_OPT_CIPHERS, std::string_view{optarg}); break; case 17: // --client cmdcfgs.emplace_back(SHRPX_OPT_CLIENT, "yes"sv); break; case 18: // --backend-http2-window-bits cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS, std::string_view{optarg}); break; case 19: // --cacert cmdcfgs.emplace_back(SHRPX_OPT_CACERT, std::string_view{optarg}); break; case 20: // --backend-ipv4 cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_IPV4, "yes"sv); break; case 21: // --backend-ipv6 cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_IPV6, "yes"sv); break; case 22: // --private-key-passwd-file cmdcfgs.emplace_back(SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE, std::string_view{optarg}); break; case 23: // --no-via cmdcfgs.emplace_back(SHRPX_OPT_NO_VIA, "yes"sv); break; case 24: // --subcert cmdcfgs.emplace_back(SHRPX_OPT_SUBCERT, std::string_view{optarg}); break; case 25: // --http2-bridge cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_BRIDGE, "yes"sv); break; case 26: // --backend-http-proxy-uri cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP_PROXY_URI, std::string_view{optarg}); break; case 27: // --backend-no-tls cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_NO_TLS, "yes"sv); break; case 28: // --ocsp-startup cmdcfgs.emplace_back(SHRPX_OPT_OCSP_STARTUP, "yes"sv); break; case 29: // --frontend-no-tls cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_NO_TLS, "yes"sv); break; case 30: // --no-verify-ocsp cmdcfgs.emplace_back(SHRPX_OPT_NO_VERIFY_OCSP, "yes"sv); break; case 31: // --backend-tls-sni-field cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_TLS_SNI_FIELD, std::string_view{optarg}); break; case 33: // --dh-param-file cmdcfgs.emplace_back(SHRPX_OPT_DH_PARAM_FILE, std::string_view{optarg}); break; case 34: // --read-rate cmdcfgs.emplace_back(SHRPX_OPT_READ_RATE, std::string_view{optarg}); break; case 35: // --read-burst cmdcfgs.emplace_back(SHRPX_OPT_READ_BURST, std::string_view{optarg}); break; case 36: // --write-rate cmdcfgs.emplace_back(SHRPX_OPT_WRITE_RATE, std::string_view{optarg}); break; case 37: // --write-burst cmdcfgs.emplace_back(SHRPX_OPT_WRITE_BURST, std::string_view{optarg}); break; case 38: // --npn-list cmdcfgs.emplace_back(SHRPX_OPT_NPN_LIST, std::string_view{optarg}); break; case 39: // --verify-client cmdcfgs.emplace_back(SHRPX_OPT_VERIFY_CLIENT, "yes"sv); break; case 40: // --verify-client-cacert cmdcfgs.emplace_back(SHRPX_OPT_VERIFY_CLIENT_CACERT, std::string_view{optarg}); break; case 41: // --client-private-key-file cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE, std::string_view{optarg}); break; case 42: // --client-cert-file cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_CERT_FILE, std::string_view{optarg}); break; case 43: // --frontend-http2-dump-request-header cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER, std::string_view{optarg}); break; case 44: // --frontend-http2-dump-response-header cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER, std::string_view{optarg}); break; case 45: // --http2-no-cookie-crumbling cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING, "yes"sv); break; case 46: // --frontend-http2-connection-window-bits cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS, std::string_view{optarg}); break; case 47: // --backend-http2-connection-window-bits cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS, std::string_view{optarg}); break; case 48: // --tls-proto-list cmdcfgs.emplace_back(SHRPX_OPT_TLS_PROTO_LIST, std::string_view{optarg}); break; case 49: // --padding cmdcfgs.emplace_back(SHRPX_OPT_PADDING, std::string_view{optarg}); break; case 50: // --worker-read-rate cmdcfgs.emplace_back(SHRPX_OPT_WORKER_READ_RATE, std::string_view{optarg}); break; case 51: // --worker-read-burst cmdcfgs.emplace_back(SHRPX_OPT_WORKER_READ_BURST, std::string_view{optarg}); break; case 52: // --worker-write-rate cmdcfgs.emplace_back(SHRPX_OPT_WORKER_WRITE_RATE, std::string_view{optarg}); break; case 53: // --worker-write-burst cmdcfgs.emplace_back(SHRPX_OPT_WORKER_WRITE_BURST, std::string_view{optarg}); break; case 54: // --altsvc cmdcfgs.emplace_back(SHRPX_OPT_ALTSVC, std::string_view{optarg}); break; case 55: // --add-response-header cmdcfgs.emplace_back(SHRPX_OPT_ADD_RESPONSE_HEADER, std::string_view{optarg}); break; case 56: // --worker-frontend-connections cmdcfgs.emplace_back(SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS, std::string_view{optarg}); break; case 57: // --accesslog-syslog cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_SYSLOG, "yes"sv); break; case 58: // --errorlog-file cmdcfgs.emplace_back(SHRPX_OPT_ERRORLOG_FILE, std::string_view{optarg}); break; case 59: // --errorlog-syslog cmdcfgs.emplace_back(SHRPX_OPT_ERRORLOG_SYSLOG, "yes"sv); break; case 60: // --stream-read-timeout cmdcfgs.emplace_back(SHRPX_OPT_STREAM_READ_TIMEOUT, std::string_view{optarg}); break; case 61: // --stream-write-timeout cmdcfgs.emplace_back(SHRPX_OPT_STREAM_WRITE_TIMEOUT, std::string_view{optarg}); break; case 62: // --no-location-rewrite cmdcfgs.emplace_back(SHRPX_OPT_NO_LOCATION_REWRITE, "yes"sv); break; case 63: // --backend-http1-connections-per-host cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST, std::string_view{optarg}); break; case 64: // --listener-disable-timeout cmdcfgs.emplace_back(SHRPX_OPT_LISTENER_DISABLE_TIMEOUT, std::string_view{optarg}); break; case 65: // --strip-incoming-x-forwarded-for cmdcfgs.emplace_back(SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR, "yes"sv); break; case 66: // --accesslog-format cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_FORMAT, std::string_view{optarg}); break; case 67: // --backend-http1-connections-per-frontend cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND, std::string_view{optarg}); break; case 68: // --tls-ticket-key-file cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_FILE, std::string_view{optarg}); break; case 69: // --rlimit-nofile cmdcfgs.emplace_back(SHRPX_OPT_RLIMIT_NOFILE, std::string_view{optarg}); break; case 71: // --backend-response-buffer cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_RESPONSE_BUFFER, std::string_view{optarg}); break; case 72: // --backend-request-buffer cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_REQUEST_BUFFER, std::string_view{optarg}); break; case 73: // --no-host-rewrite cmdcfgs.emplace_back(SHRPX_OPT_NO_HOST_REWRITE, "yes"sv); break; case 74: // --no-server-push cmdcfgs.emplace_back(SHRPX_OPT_NO_SERVER_PUSH, "yes"sv); break; case 76: // --backend-http2-connections-per-worker cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER, std::string_view{optarg}); break; case 77: // --fetch-ocsp-response-file cmdcfgs.emplace_back(SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE, std::string_view{optarg}); break; case 78: // --ocsp-update-interval cmdcfgs.emplace_back(SHRPX_OPT_OCSP_UPDATE_INTERVAL, std::string_view{optarg}); break; case 79: // --no-ocsp cmdcfgs.emplace_back(SHRPX_OPT_NO_OCSP, "yes"sv); break; case 80: // --header-field-buffer cmdcfgs.emplace_back(SHRPX_OPT_HEADER_FIELD_BUFFER, std::string_view{optarg}); break; case 81: // --max-header-fields cmdcfgs.emplace_back(SHRPX_OPT_MAX_HEADER_FIELDS, std::string_view{optarg}); break; case 82: // --add-request-header cmdcfgs.emplace_back(SHRPX_OPT_ADD_REQUEST_HEADER, std::string_view{optarg}); break; case 83: // --include cmdcfgs.emplace_back(SHRPX_OPT_INCLUDE, std::string_view{optarg}); break; case 84: // --tls-ticket-key-cipher cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_CIPHER, std::string_view{optarg}); break; case 85: // --host-rewrite cmdcfgs.emplace_back(SHRPX_OPT_HOST_REWRITE, "yes"sv); break; case 86: // --tls-session-cache-memcached cmdcfgs.emplace_back(SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED, std::string_view{optarg}); break; case 87: // --tls-ticket-key-memcached cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED, std::string_view{optarg}); break; case 88: // --tls-ticket-key-memcached-interval cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_INTERVAL, std::string_view{optarg}); break; case 89: // --tls-ticket-key-memcached-max-retry cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY, std::string_view{optarg}); break; case 90: // --tls-ticket-key-memcached-max-fail cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL, std::string_view{optarg}); break; case 91: // --mruby-file cmdcfgs.emplace_back(SHRPX_OPT_MRUBY_FILE, std::string_view{optarg}); break; case 93: // --accept-proxy-protocol cmdcfgs.emplace_back(SHRPX_OPT_ACCEPT_PROXY_PROTOCOL, "yes"sv); break; case 94: // --fastopen cmdcfgs.emplace_back(SHRPX_OPT_FASTOPEN, std::string_view{optarg}); break; case 95: // --tls-dyn-rec-warmup-threshold cmdcfgs.emplace_back(SHRPX_OPT_TLS_DYN_REC_WARMUP_THRESHOLD, std::string_view{optarg}); break; case 96: // --tls-dyn-rec-idle-timeout cmdcfgs.emplace_back(SHRPX_OPT_TLS_DYN_REC_IDLE_TIMEOUT, std::string_view{optarg}); break; case 97: // --add-forwarded cmdcfgs.emplace_back(SHRPX_OPT_ADD_FORWARDED, std::string_view{optarg}); break; case 98: // --strip-incoming-forwarded cmdcfgs.emplace_back(SHRPX_OPT_STRIP_INCOMING_FORWARDED, "yes"sv); break; case 99: // --forwarded-by cmdcfgs.emplace_back(SHRPX_OPT_FORWARDED_BY, std::string_view{optarg}); break; case 100: // --forwarded-for cmdcfgs.emplace_back(SHRPX_OPT_FORWARDED_FOR, std::string_view{optarg}); break; case 101: // --response-header-field-buffer cmdcfgs.emplace_back(SHRPX_OPT_RESPONSE_HEADER_FIELD_BUFFER, std::string_view{optarg}); break; case 102: // --max-response-header-fields cmdcfgs.emplace_back(SHRPX_OPT_MAX_RESPONSE_HEADER_FIELDS, std::string_view{optarg}); break; case 103: // --no-http2-cipher-black-list cmdcfgs.emplace_back(SHRPX_OPT_NO_HTTP2_CIPHER_BLACK_LIST, "yes"sv); break; case 104: // --request-header-field-buffer cmdcfgs.emplace_back(SHRPX_OPT_REQUEST_HEADER_FIELD_BUFFER, std::string_view{optarg}); break; case 105: // --max-request-header-fields cmdcfgs.emplace_back(SHRPX_OPT_MAX_REQUEST_HEADER_FIELDS, std::string_view{optarg}); break; case 106: // --backend-http1-tls cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP1_TLS, "yes"sv); break; case 108: // --tls-session-cache-memcached-tls cmdcfgs.emplace_back(SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_TLS, "yes"sv); break; case 109: // --tls-session-cache-memcached-cert-file cmdcfgs.emplace_back(SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE, std::string_view{optarg}); break; case 110: // --tls-session-cache-memcached-private-key-file cmdcfgs.emplace_back( SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE, std::string_view{optarg}); break; case 111: // --tls-ticket-key-memcached-tls cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_TLS, "yes"sv); break; case 112: // --tls-ticket-key-memcached-cert-file cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_CERT_FILE, std::string_view{optarg}); break; case 113: // --tls-ticket-key-memcached-private-key-file cmdcfgs.emplace_back( SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE, std::string_view{optarg}); break; case 114: // --tls-ticket-key-memcached-address-family cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY, std::string_view{optarg}); break; case 115: // --tls-session-cache-memcached-address-family cmdcfgs.emplace_back( SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY, std::string_view{optarg}); break; case 116: // --backend-address-family cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_ADDRESS_FAMILY, std::string_view{optarg}); break; case 117: // --frontend-http2-max-concurrent-streams cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS, std::string_view{optarg}); break; case 118: // --backend-http2-max-concurrent-streams cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS, std::string_view{optarg}); break; case 119: // --backend-connections-per-frontend cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND, std::string_view{optarg}); break; case 120: // --backend-tls cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_TLS, "yes"sv); break; case 121: // --backend-connections-per-host cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_CONNECTIONS_PER_HOST, std::string_view{optarg}); break; case 122: // --error-page cmdcfgs.emplace_back(SHRPX_OPT_ERROR_PAGE, std::string_view{optarg}); break; case 123: // --no-kqueue cmdcfgs.emplace_back(SHRPX_OPT_NO_KQUEUE, "yes"sv); break; case 124: // --frontend-http2-settings-timeout cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_SETTINGS_TIMEOUT, std::string_view{optarg}); break; case 125: // --backend-http2-settings-timeout cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_SETTINGS_TIMEOUT, std::string_view{optarg}); break; case 126: // --api-max-request-body cmdcfgs.emplace_back(SHRPX_OPT_API_MAX_REQUEST_BODY, std::string_view{optarg}); break; case 127: // --backend-max-backoff cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_MAX_BACKOFF, std::string_view{optarg}); break; case 128: // --server-name cmdcfgs.emplace_back(SHRPX_OPT_SERVER_NAME, std::string_view{optarg}); break; case 129: // --no-server-rewrite cmdcfgs.emplace_back(SHRPX_OPT_NO_SERVER_REWRITE, "yes"sv); break; case 130: // --frontend-http2-optimize-write-buffer-size cmdcfgs.emplace_back( SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE, "yes"sv); break; case 131: // --frontend-http2-optimize-window-size cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE, "yes"sv); break; case 132: // --frontend-http2-window-size cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_WINDOW_SIZE, std::string_view{optarg}); break; case 133: // --frontend-http2-connection-window-size cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE, std::string_view{optarg}); break; case 134: // --backend-http2-window-size cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_WINDOW_SIZE, std::string_view{optarg}); break; case 135: // --backend-http2-connection-window-size cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE, std::string_view{optarg}); break; case 136: // --frontend-http2-encoder-dynamic-table-size cmdcfgs.emplace_back( SHRPX_OPT_FRONTEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE, std::string_view{optarg}); break; case 137: // --frontend-http2-decoder-dynamic-table-size cmdcfgs.emplace_back( SHRPX_OPT_FRONTEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE, std::string_view{optarg}); break; case 138: // --backend-http2-encoder-dynamic-table-size cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE, std::string_view{optarg}); break; case 139: // --backend-http2-decoder-dynamic-table-size cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE, std::string_view{optarg}); break; case 140: // --ecdh-curves cmdcfgs.emplace_back(SHRPX_OPT_ECDH_CURVES, std::string_view{optarg}); break; case 141: // --tls-sct-dir cmdcfgs.emplace_back(SHRPX_OPT_TLS_SCT_DIR, std::string_view{optarg}); break; case 142: // --backend-connect-timeout cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_CONNECT_TIMEOUT, std::string_view{optarg}); break; case 143: // --dns-cache-timeout cmdcfgs.emplace_back(SHRPX_OPT_DNS_CACHE_TIMEOUT, std::string_view{optarg}); break; case 144: // --dns-lookup-timeou cmdcfgs.emplace_back(SHRPX_OPT_DNS_LOOKUP_TIMEOUT, std::string_view{optarg}); break; case 145: // --dns-max-try cmdcfgs.emplace_back(SHRPX_OPT_DNS_MAX_TRY, std::string_view{optarg}); break; case 146: // --frontend-keep-alive-timeout cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT, std::string_view{optarg}); break; case 147: // --psk-secrets cmdcfgs.emplace_back(SHRPX_OPT_PSK_SECRETS, std::string_view{optarg}); break; case 148: // --client-psk-secrets cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PSK_SECRETS, std::string_view{optarg}); break; case 149: // --client-no-http2-cipher-black-list cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST, "yes"sv); break; case 150: // --client-ciphers cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_CIPHERS, std::string_view{optarg}); break; case 151: // --accesslog-write-early cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_WRITE_EARLY, "yes"sv); break; case 152: // --tls-min-proto-version cmdcfgs.emplace_back(SHRPX_OPT_TLS_MIN_PROTO_VERSION, std::string_view{optarg}); break; case 153: // --tls-max-proto-version cmdcfgs.emplace_back(SHRPX_OPT_TLS_MAX_PROTO_VERSION, std::string_view{optarg}); break; case 154: // --redirect-https-port cmdcfgs.emplace_back(SHRPX_OPT_REDIRECT_HTTPS_PORT, std::string_view{optarg}); break; case 155: // --frontend-max-requests cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_MAX_REQUESTS, std::string_view{optarg}); break; case 156: // --single-thread cmdcfgs.emplace_back(SHRPX_OPT_SINGLE_THREAD, "yes"sv); break; case 157: // --no-add-x-forwarded-proto cmdcfgs.emplace_back(SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO, "yes"sv); break; case 158: // --no-strip-incoming-x-forwarded-proto cmdcfgs.emplace_back(SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO, "yes"sv); break; case 159: // --single-process cmdcfgs.emplace_back(SHRPX_OPT_SINGLE_PROCESS, "yes"sv); break; case 160: // --verify-client-tolerate-expired cmdcfgs.emplace_back(SHRPX_OPT_VERIFY_CLIENT_TOLERATE_EXPIRED, "yes"sv); break; case 161: // --ignore-per-pattern-mruby-error cmdcfgs.emplace_back(SHRPX_OPT_IGNORE_PER_PATTERN_MRUBY_ERROR, "yes"sv); break; case 162: // --tls-no-postpone-early-data cmdcfgs.emplace_back(SHRPX_OPT_TLS_NO_POSTPONE_EARLY_DATA, "yes"sv); break; case 163: // --tls-max-early-data cmdcfgs.emplace_back(SHRPX_OPT_TLS_MAX_EARLY_DATA, std::string_view{optarg}); break; case 164: // --tls13-ciphers cmdcfgs.emplace_back(SHRPX_OPT_TLS13_CIPHERS, std::string_view{optarg}); break; case 165: // --tls13-client-ciphers cmdcfgs.emplace_back(SHRPX_OPT_TLS13_CLIENT_CIPHERS, std::string_view{optarg}); break; case 166: // --no-strip-incoming-early-data cmdcfgs.emplace_back(SHRPX_OPT_NO_STRIP_INCOMING_EARLY_DATA, "yes"sv); break; case 167: // --no-http2-cipher-block-list cmdcfgs.emplace_back(SHRPX_OPT_NO_HTTP2_CIPHER_BLOCK_LIST, "yes"sv); break; case 168: // --client-no-http2-cipher-block-list cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLOCK_LIST, "yes"sv); break; case 169: // --quic-bpf-program-file cmdcfgs.emplace_back(SHRPX_OPT_QUIC_BPF_PROGRAM_FILE, std::string_view{optarg}); break; case 170: // --no-quic-bpf cmdcfgs.emplace_back(SHRPX_OPT_NO_QUIC_BPF, "yes"sv); break; case 171: // --http2-altsvc cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_ALTSVC, std::string_view{optarg}); break; case 172: // --frontend-http3-read-timeout cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP3_READ_TIMEOUT, std::string_view{optarg}); break; case 173: // --frontend-quic-idle-timeout cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_IDLE_TIMEOUT, std::string_view{optarg}); break; case 174: // --frontend-quic-debug-log cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_DEBUG_LOG, "yes"sv); break; case 175: // --frontend-http3-window-size cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP3_WINDOW_SIZE, std::string_view{optarg}); break; case 176: // --frontend-http3-connection-window-size cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE, std::string_view{optarg}); break; case 177: // --frontend-http3-max-window-size cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP3_MAX_WINDOW_SIZE, std::string_view{optarg}); break; case 178: // --frontend-http3-max-connection-window-size cmdcfgs.emplace_back( SHRPX_OPT_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE, std::string_view{optarg}); break; case 179: // --frontend-http3-max-concurrent-streams cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS, std::string_view{optarg}); break; case 180: // --frontend-quic-early-data cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_EARLY_DATA, "yes"sv); break; case 181: // --frontend-quic-qlog-dir cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_QLOG_DIR, std::string_view{optarg}); break; case 182: // --frontend-quic-require-token cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_REQUIRE_TOKEN, "yes"sv); break; case 183: // --frontend-quic-congestion-controller cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_CONGESTION_CONTROLLER, std::string_view{optarg}); break; case 185: // --quic-server-id cmdcfgs.emplace_back(SHRPX_OPT_QUIC_SERVER_ID, std::string_view{optarg}); break; case 186: // --frontend-quic-secret-file cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_SECRET_FILE, std::string_view{optarg}); break; case 187: // --rlimit-memlock cmdcfgs.emplace_back(SHRPX_OPT_RLIMIT_MEMLOCK, std::string_view{optarg}); break; case 188: // --max-worker-processes cmdcfgs.emplace_back(SHRPX_OPT_MAX_WORKER_PROCESSES, std::string_view{optarg}); break; case 189: // --worker-process-grace-shutdown-period cmdcfgs.emplace_back(SHRPX_OPT_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD, std::string_view{optarg}); break; case 190: // --frontend-quic-initial-rtt cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_INITIAL_RTT, std::string_view{optarg}); break; case 191: // --require-http-scheme cmdcfgs.emplace_back(SHRPX_OPT_REQUIRE_HTTP_SCHEME, "yes"sv); break; case 192: // --tls-ktls cmdcfgs.emplace_back(SHRPX_OPT_TLS_KTLS, "yes"sv); break; case 193: // --alpn-list cmdcfgs.emplace_back(SHRPX_OPT_ALPN_LIST, std::string_view{optarg}); break; case 194: // --frontend-header-timeout cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HEADER_TIMEOUT, std::string_view{optarg}); break; case 195: // --frontend-http2-idle-timeout cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_IDLE_TIMEOUT, std::string_view{optarg}); break; case 196: // --frontend-http3-idle-timeout cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP3_IDLE_TIMEOUT, std::string_view{optarg}); break; case 197: // --groups cmdcfgs.emplace_back(SHRPX_OPT_GROUPS, std::string_view{optarg}); break; default: break; } break; default: break; } } if (argc - optind >= 2) { cmdcfgs.emplace_back(SHRPX_OPT_PRIVATE_KEY_FILE, std::string_view{argv[optind++]}); cmdcfgs.emplace_back(SHRPX_OPT_CERTIFICATE_FILE, std::string_view{argv[optind++]}); } #ifdef ENABLE_HTTP3 # if defined(HAVE_LIBNGTCP2_CRYPTO_QUICTLS) || \ defined(HAVE_LIBNGTCP2_CRYPTO_LIBRESSL) if (ngtcp2_crypto_quictls_init() != 0) { LOG(FATAL) << "ngtcp2_crypto_quictls_init failed"; exit(EXIT_FAILURE); } # endif // defined(HAVE_LIBNGTCP2_CRYPTO_QUICTLS) || // defined(HAVE_LIBNGTCP2_CRYPTO_LIBRESSL) # ifdef HAVE_LIBNGTCP2_CRYPTO_OSSL if (ngtcp2_crypto_ossl_init() != 0) { LOG(FATAL) << "ngtcp2_crypto_ossl_init failed"; exit(EXIT_FAILURE); } # endif // defined(HAVE_LIBNGTCP2_CRYPTO_OSSL) #endif // defined(ENABLE_HTTP3) rv = process_options(mod_config(), cmdcfgs); if (rv != 0) { return -1; } if (event_loop() != 0) { return -1; } LOG(NOTICE) << "Shutdown momentarily"; return 0; } } // namespace shrpx int main(int argc, char **argv) { return run_app(shrpx::main, argc, argv); } nghttp2-1.68.0/src/PaxHeaders/shrpx_http_test.h0000644000000000000000000000013215077107270016462 xustar0030 mtime=1761382072.995444129 30 atime=1761382106.257310194 30 ctime=1761382109.254299936 nghttp2-1.68.0/src/shrpx_http_test.h0000644000175100017510000000334315077107270017055 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_HTTP_TEST_H #define SHRPX_HTTP_TEST_H #ifdef HAVE_CONFIG_H # include #endif // defined(HAVE_CONFIG_H) #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" namespace shrpx { extern const MunitSuite http_suite; munit_void_test_decl(test_shrpx_http_create_forwarded) munit_void_test_decl(test_shrpx_http_create_via_header_value) munit_void_test_decl(test_shrpx_http_create_affinity_cookie) munit_void_test_decl(test_shrpx_http_create_altsvc_header_value) munit_void_test_decl(test_shrpx_http_check_http_scheme) } // namespace shrpx #endif // !defined(SHRPX_HTTP_TEST_H) nghttp2-1.68.0/src/PaxHeaders/shrpx.h0000644000000000000000000000013215077107270014364 xustar0030 mtime=1761382072.990444153 30 atime=1761382106.243310255 30 ctime=1761382109.240299977 nghttp2-1.68.0/src/shrpx.h0000644000175100017510000000407615077107270014763 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_H #define SHRPX_H #ifdef HAVE_CONFIG_H # include #endif // defined(HAVE_CONFIG_H) #include #ifdef HAVE_SYS_SOCKET_H # include #endif // defined(HAVE_SYS_SOCKET_H) #include #define NGHTTP2_NO_SSIZE_T #ifndef HAVE__EXIT # define nghttp2_Exit(status) _exit(status) #else // defined(HAVE__EXIT) # define nghttp2_Exit(status) _Exit(status) #endif // defined(HAVE__EXIT) #define DIE() nghttp2_Exit(EXIT_FAILURE) #if defined(HAVE_DECL_INITGROUPS) && !HAVE_DECL_INITGROUPS inline int initgroups(const char *user, gid_t group) { return 0; } #endif // defined(HAVE_DECL_INITGROUPS) && !HAVE_DECL_INITGROUPS #ifndef HAVE_BPF_STATS_TYPE /* Newer kernel should have this defined in linux/bpf.h */ enum bpf_stats_type { BPF_STATS_RUN_TIME = 0, }; #endif // !defined(HAVE_BPF_STATS_TYPE) #ifdef NOTHREADS # define thread_local #endif // defined(NOTHREADS) #endif // !defined(SHRPX_H) nghttp2-1.68.0/src/PaxHeaders/h2load_quic.h0000644000000000000000000000013215077107270015412 xustar0030 mtime=1761382072.986444171 30 atime=1761382106.225310334 30 ctime=1761382109.221300031 nghttp2-1.68.0/src/h2load_quic.h0000644000175100017510000000270015077107270016001 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2019 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef H2LOAD_QUIC_H #define H2LOAD_QUIC_H #include "nghttp2_config.h" #include #include "h2load.h" namespace h2load { inline constexpr size_t QUIC_TX_DATALEN = 64_k; void quic_pkt_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents); } // namespace h2load #endif // !defined(H2LOAD_QUIC_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_http.cc0000644000000000000000000000013215077107270015561 xustar0030 mtime=1761382072.993444139 30 atime=1761382106.114310823 30 ctime=1761382109.110300352 nghttp2-1.68.0/src/shrpx_http.cc0000644000175100017510000002137315077107270016157 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_http.h" #include "shrpx_config.h" #include "shrpx_log.h" #include "http2.h" #include "util.h" using namespace nghttp2; namespace shrpx { namespace http { std::string_view create_error_html(BlockAllocator &balloc, unsigned int http_status) { auto &httpconf = get_config()->http; const auto &error_pages = httpconf.error_pages; for (const auto &page : error_pages) { if (page.http_status == 0 || page.http_status == http_status) { return as_string_view(page.content); } } auto status_string = http2::stringify_status(balloc, http_status); auto reason_phrase = http2::get_reason_phrase(http_status); return concat_string_ref( balloc, R"()"sv, status_string, " "sv, reason_phrase, "

"sv, status_string, " "sv, reason_phrase, "

"sv, httpconf.server_name, "
"sv); } std::string_view create_forwarded(BlockAllocator &balloc, uint32_t params, const std::string_view &node_by, const std::string_view &node_for, const std::string_view &host, const std::string_view &proto) { size_t len = 0; if ((params & FORWARDED_BY) && !node_by.empty()) { len += str_size("by=\"") + node_by.size() + str_size("\";"); } if ((params & FORWARDED_FOR) && !node_for.empty()) { len += str_size("for=\"") + node_for.size() + str_size("\";"); } if ((params & FORWARDED_HOST) && !host.empty()) { len += str_size("host=\"") + host.size() + str_size("\";"); } if ((params & FORWARDED_PROTO) && !proto.empty()) { len += str_size("proto=") + proto.size() + str_size(";"); } auto iov = make_byte_ref(balloc, len + 1); auto p = std::ranges::begin(iov); if ((params & FORWARDED_BY) && !node_by.empty()) { // This must be quoted-string unless it is obfuscated version // (which starts with "_") or some special value (e.g., // "localhost" for UNIX domain socket), since ':' is not allowed // in token. ':' is used to separate host and port. if (node_by[0] == '_' || node_by[0] == 'l') { p = std::ranges::copy("by="sv, p).out; p = std::ranges::copy(node_by, p).out; *p++ = ';'; } else { p = std::ranges::copy("by=\""sv, p).out; p = std::ranges::copy(node_by, p).out; p = std::ranges::copy("\";"sv, p).out; } } if ((params & FORWARDED_FOR) && !node_for.empty()) { // We only quote IPv6 literal address only, which starts with '['. if (node_for[0] == '[') { p = std::ranges::copy("for=\""sv, p).out; p = std::ranges::copy(node_for, p).out; p = std::ranges::copy("\";"sv, p).out; } else { p = std::ranges::copy("for="sv, p).out; p = std::ranges::copy(node_for, p).out; *p++ = ';'; } } if ((params & FORWARDED_HOST) && !host.empty()) { // Just be quoted to skip checking characters. p = std::ranges::copy("host=\""sv, p).out; p = std::ranges::copy(host, p).out; p = std::ranges::copy("\";"sv, p).out; } if ((params & FORWARDED_PROTO) && !proto.empty()) { // Scheme production rule only allow characters which are all in // token. p = std::ranges::copy("proto="sv, p).out; p = std::ranges::copy(proto, p).out; *p++ = ';'; } if (std::ranges::begin(iov) == p) { return ""sv; } --p; *p = '\0'; return as_string_view(std::ranges::begin(iov), p); } std::string colorize_headers(const std::string_view &hdrs) { std::string nhdrs; auto p = std::ranges::find(hdrs, '\n'); if (p == std::ranges::end(hdrs)) { // Not valid HTTP header return std::string{hdrs}; } nhdrs.append(std::ranges::begin(hdrs), ++p); while (1) { auto np = std::ranges::find(p, std::ranges::end(hdrs), ':'); if (np == std::ranges::end(hdrs)) { nhdrs.append(p, std::ranges::end(hdrs)); break; } nhdrs += TTY_HTTP_HD; nhdrs.append(p, np); nhdrs += TTY_RST; auto redact = util::strieq("authorization"sv, std::string_view{p, np}); p = np; np = std::ranges::find(p, std::ranges::end(hdrs), '\n'); if (redact) { nhdrs.append(": "sv); } else { nhdrs.append(p, np); } if (np == std::ranges::end(hdrs)) { return nhdrs; } nhdrs += '\n'; p = np + 1; } return nhdrs; } nghttp2_ssize select_padding_callback(nghttp2_session *session, const nghttp2_frame *frame, size_t max_payload, void *user_data) { return as_signed( std::min(max_payload, frame->hd.length + get_config()->padding)); } std::string_view create_affinity_cookie(BlockAllocator &balloc, const std::string_view &name, uint32_t affinity_cookie, const std::string_view &path, bool secure) { static constexpr auto PATH_PREFIX = "; Path="sv; static constexpr auto SECURE = "; Secure"sv; // =[; Path=][; Secure] size_t len = name.size() + 1 + 8; if (!path.empty()) { len += PATH_PREFIX.size() + path.size(); } if (secure) { len += SECURE.size(); } auto iov = make_byte_ref(balloc, len + 1); auto p = std::ranges::copy(name, std::ranges::begin(iov)).out; *p++ = '='; affinity_cookie = htonl(affinity_cookie); p = util::format_hex(as_uint8_span(std::span{&affinity_cookie, 1}), p); if (!path.empty()) { p = std::ranges::copy(PATH_PREFIX, p).out; p = std::ranges::copy(path, p).out; } if (secure) { p = std::ranges::copy(SECURE, p).out; } *p = '\0'; return as_string_view(std::ranges::begin(iov), p); } bool require_cookie_secure_attribute(SessionAffinityCookieSecure secure, const std::string_view &scheme) { switch (secure) { case SessionAffinityCookieSecure::AUTO: return scheme == "https"sv; case SessionAffinityCookieSecure::YES: return true; default: return false; } } std::string_view create_altsvc_header_value(BlockAllocator &balloc, const std::vector &altsvcs) { // =":"; size_t len = 0; if (altsvcs.empty()) { return ""sv; } for (auto &altsvc : altsvcs) { len += util::percent_encode_tokenlen(altsvc.protocol_id); len += str_size("=\""); len += util::quote_stringlen(altsvc.host); len += str_size(":"); len += altsvc.service.size(); len += str_size("\""); if (!altsvc.params.empty()) { len += str_size("; "); len += altsvc.params.size(); } } // ", " between items. len += (altsvcs.size() - 1) * 2; // We will write additional ", " at the end, and cut it later. auto iov = make_byte_ref(balloc, len + 2); auto p = std::ranges::begin(iov); for (auto &altsvc : altsvcs) { p = util::percent_encode_token(altsvc.protocol_id, p); p = std::ranges::copy("=\""sv, p).out; p = util::quote_string(altsvc.host, p); *p++ = ':'; p = std::ranges::copy(altsvc.service, p).out; *p++ = '"'; if (!altsvc.params.empty()) { p = std::ranges::copy("; "sv, p).out; p = std::ranges::copy(altsvc.params, p).out; } p = std::ranges::copy(", "sv, p).out; } p -= 2; *p = '\0'; assert(static_cast(p - std::ranges::begin(iov)) == len); return as_string_view(std::ranges::begin(iov), p); } bool check_http_scheme(const std::string_view &scheme, bool encrypted) { return encrypted ? scheme == "https"sv : scheme == "http"sv; } } // namespace http } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_connection.cc0000644000000000000000000000013215077107270016741 xustar0030 mtime=1761382072.992444143 30 atime=1761382106.137310722 30 ctime=1761382109.133300286 nghttp2-1.68.0/src/shrpx_connection.cc0000644000175100017510000006224115077107270017336 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_connection.h" #ifdef HAVE_UNISTD_H # include #endif // defined(HAVE_UNISTD_H) #include #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include "shrpx_tls.h" #include "shrpx_log.h" #include "memchunk.h" #include "util.h" using namespace nghttp2; using namespace std::chrono_literals; namespace shrpx { Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl, MemchunkPool *mcpool, ev_tstamp write_timeout, ev_tstamp read_timeout, const RateLimitConfig &write_limit, const RateLimitConfig &read_limit, IOCb writecb, IOCb readcb, TimerCb timeoutcb, void *data, size_t tls_dyn_rec_warmup_threshold, ev_tstamp tls_dyn_rec_idle_timeout, Proto proto) : #ifdef ENABLE_HTTP3 conn_ref{nullptr, this}, #endif // defined(ENABLE_HTTP3) tls{DefaultMemchunks(mcpool)}, wlimit(loop, &wev, write_limit.rate, write_limit.burst), rlimit(loop, &rev, read_limit.rate, read_limit.burst, this), loop(loop), data(data), fd(fd), tls_dyn_rec_warmup_threshold(tls_dyn_rec_warmup_threshold), tls_dyn_rec_idle_timeout(util::duration_from(tls_dyn_rec_idle_timeout)), proto(proto), read_timeout(read_timeout) { ev_io_init(&wev, writecb, fd, EV_WRITE); ev_io_init(&rev, readcb, proto == Proto::HTTP3 ? 0 : fd, EV_READ); wev.data = this; rev.data = this; ev_timer_init(&wt, timeoutcb, 0., write_timeout); ev_timer_init(&rt, timeoutcb, 0., read_timeout); wt.data = this; rt.data = this; if (ssl) { set_ssl(ssl); } } Connection::~Connection() { disconnect(); } void Connection::disconnect() { if (tls.ssl) { if (proto != Proto::HTTP3) { SSL_set_shutdown(tls.ssl, SSL_get_shutdown(tls.ssl) | SSL_RECEIVED_SHUTDOWN); ERR_clear_error(); SSL_shutdown(tls.ssl); } // Unset app data here, so that ngtcp2_conn never be used by // libngtcp2_crypto_ossl that may be called by SSL_free. SSL_set_app_data(tls.ssl, nullptr); SSL_free(tls.ssl); tls.ssl = nullptr; tls.last_write_idle = {}; tls.warmup_writelen = 0; tls.last_writelen = 0; tls.last_readlen = 0; tls.initial_handshake_done = false; tls.reneg_started = false; tls.sct_requested = false; tls.early_data_finish = false; } if (proto != Proto::HTTP3 && fd != -1) { shutdown(fd, SHUT_WR); close(fd); fd = -1; } // Stop watchers here because they could be activated in // SSL_shutdown(). ev_timer_stop(loop, &rt); ev_timer_stop(loop, &wt); rlimit.stopw(); wlimit.stopw(); } void Connection::prepare_client_handshake() { SSL_set_connect_state(tls.ssl); // This prevents SSL_read_early_data from being called. tls.early_data_finish = true; } void Connection::prepare_server_handshake() { SSL_set_accept_state(tls.ssl); tls.server_handshake = true; } void Connection::set_ssl(SSL *ssl) { tls.ssl = ssl; SSL_set_app_data(tls.ssl, this); } int Connection::tls_handshake() { wlimit.stopw(); ev_timer_stop(loop, &wt); if (tls.initial_handshake_done) { return write_tls_pending_handshake(); } if (SSL_get_fd(tls.ssl) == -1) { SSL_set_fd(tls.ssl, fd); } int rv; #if defined(NGHTTP2_GENUINE_OPENSSL) || \ defined(NGHTTP2_OPENSSL_IS_BORINGSSL) || \ (defined(NGHTTP2_OPENSSL_IS_WOLFSSL) && defined(WOLFSSL_EARLY_DATA)) auto &tlsconf = get_config()->tls; std::array buf; #endif // defined(NGHTTP2_GENUINE_OPENSSL) || // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) || // (defined(NGHTTP2_OPENSSL_IS_WOLFSSL) && // defined(WOLFSSL_EARLY_DATA)) ERR_clear_error(); #ifdef NGHTTP2_GENUINE_OPENSSL if (!tls.server_handshake || tls.early_data_finish) { rv = SSL_do_handshake(tls.ssl); } else { for (;;) { size_t nread; rv = SSL_read_early_data(tls.ssl, buf.data(), buf.size(), &nread); if (rv == SSL_READ_EARLY_DATA_ERROR) { // If we have early data, and server sends ServerHello, assume // that handshake is completed in server side, and start // processing request. If we don't exit handshake code here, // server waits for EndOfEarlyData and Finished message from // client, which voids the purpose of 0-RTT data. The left // over of handshake is done through write_tls or read_tls. if (tlsconf.no_postpone_early_data && tls.earlybuf.rleft()) { rv = 1; } break; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "tls: read early data " << nread << " bytes"; } tls.earlybuf.append(buf.data(), nread); if (rv == SSL_READ_EARLY_DATA_FINISH) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "tls: read all early data; total " << tls.earlybuf.rleft() << " bytes"; } tls.early_data_finish = true; // The same reason stated above. if (tlsconf.no_postpone_early_data && tls.earlybuf.rleft()) { rv = 1; } else { ERR_clear_error(); rv = SSL_do_handshake(tls.ssl); } break; } } } #elif defined(NGHTTP2_OPENSSL_IS_WOLFSSL) && defined(WOLFSSL_EARLY_DATA) if (!tls.server_handshake || tls.early_data_finish) { rv = SSL_do_handshake(tls.ssl); } else { for (;;) { size_t nread = 0; rv = SSL_read_early_data(tls.ssl, buf.data(), buf.size(), &nread); if (rv < 0) { if (SSL_get_error(tls.ssl, rv) == SSL_ERROR_WANT_READ) { if (tlsconf.no_postpone_early_data && tls.earlybuf.rleft()) { rv = 1; } break; } /* It looks like we are here if there is no early data. */ tls.early_data_finish = true; ERR_clear_error(); rv = SSL_do_handshake(tls.ssl); break; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "tls: read early data " << nread << " bytes"; } tls.earlybuf.append(buf.data(), nread); if (rv == 0) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "tls: read all early data; total " << tls.earlybuf.rleft() << " bytes"; } tls.early_data_finish = true; // The same reason stated above. if (tlsconf.no_postpone_early_data && tls.earlybuf.rleft()) { rv = 1; } else { ERR_clear_error(); rv = SSL_do_handshake(tls.ssl); } break; } } } #else // !defined(NGHTTP2_GENUINE_OPENSSL) && // (!defined(NGHTTP2_OPENSSL_IS_WOLFSSL) || // !defined(WOLFSSL_EARLY_DATA)) rv = SSL_do_handshake(tls.ssl); #endif // !defined(NGHTTP2_GENUINE_OPENSSL) && // (!defined(NGHTTP2_OPENSSL_IS_WOLFSSL) || // !defined(WOLFSSL_EARLY_DATA)) if (rv <= 0) { auto err = SSL_get_error(tls.ssl, rv); switch (err) { case SSL_ERROR_WANT_READ: break; case SSL_ERROR_WANT_WRITE: wlimit.startw(); ev_timer_again(loop, &wt); break; case SSL_ERROR_SSL: { if (LOG_ENABLED(INFO)) { LOG(INFO) << "tls: handshake libssl error: " << ERR_error_string(ERR_get_error(), nullptr); } return SHRPX_ERR_NETWORK; } default: if (LOG_ENABLED(INFO)) { LOG(INFO) << "tls: handshake libssl error " << err; } return SHRPX_ERR_NETWORK; } } if (rv != 1) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "tls: handshake is still in progress"; } return SHRPX_ERR_INPROGRESS; } #ifdef NGHTTP2_OPENSSL_IS_BORINGSSL if (!tlsconf.no_postpone_early_data && SSL_in_early_data(tls.ssl) && SSL_in_init(tls.ssl)) { auto nread = SSL_read(tls.ssl, buf.data(), buf.size()); if (nread <= 0) { auto err = SSL_get_error(tls.ssl, nread); switch (err) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: break; case SSL_ERROR_ZERO_RETURN: return SHRPX_ERR_EOF; case SSL_ERROR_SSL: if (LOG_ENABLED(INFO)) { LOG(INFO) << "SSL_read: " << ERR_error_string(ERR_get_error(), nullptr); } return SHRPX_ERR_NETWORK; default: if (LOG_ENABLED(INFO)) { LOG(INFO) << "SSL_read: SSL_get_error returned " << err; } return SHRPX_ERR_NETWORK; } } else { tls.earlybuf.append(buf.data(), static_cast(nread)); } if (SSL_in_init(tls.ssl)) { return SHRPX_ERR_INPROGRESS; } } #endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) // Handshake was done rv = check_http2_requirement(); if (rv != 0) { return -1; } tls.initial_handshake_done = true; return write_tls_pending_handshake(); } int Connection::write_tls_pending_handshake() { #ifdef NGHTTP2_OPENSSL_IS_BORINGSSL if (!SSL_in_init(tls.ssl)) { // This will send a session ticket. auto nwrite = SSL_write(tls.ssl, "", 0); if (nwrite < 0) { auto err = SSL_get_error(tls.ssl, nwrite); switch (err) { case SSL_ERROR_WANT_READ: if (LOG_ENABLED(INFO)) { LOG(INFO) << "Close connection due to TLS renegotiation"; } return SHRPX_ERR_NETWORK; case SSL_ERROR_WANT_WRITE: break; case SSL_ERROR_SSL: if (LOG_ENABLED(INFO)) { LOG(INFO) << "SSL_write: " << ERR_error_string(ERR_get_error(), nullptr); } return SHRPX_ERR_NETWORK; default: if (LOG_ENABLED(INFO)) { LOG(INFO) << "SSL_write: SSL_get_error returned " << err; } return SHRPX_ERR_NETWORK; } } } #endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) // We have to start read watcher, since later stage of code expects // this. rlimit.startw(); // We may have whole request in tls.rbuf. This means that we don't // get notified further read event. This is especially true for // HTTP/1.1. handle_tls_pending_read(); if (LOG_ENABLED(INFO)) { LOG(INFO) << "SSL/TLS handshake completed"; nghttp2::tls::TLSSessionInfo tls_info{}; if (nghttp2::tls::get_tls_session_info(&tls_info, tls.ssl)) { LOG(INFO) << "cipher=" << tls_info.cipher << " protocol=" << tls_info.protocol << " resumption=" << (tls_info.session_reused ? "yes" : "no") << " session_id=" << util::format_hex(std::span{tls_info.session_id, tls_info.session_id_length}); } } return 0; } int Connection::check_http2_requirement() { const unsigned char *next_proto = nullptr; unsigned int next_proto_len; SSL_get0_alpn_selected(tls.ssl, &next_proto, &next_proto_len); if (next_proto == nullptr || !util::check_h2_is_selected(as_string_view(next_proto, next_proto_len))) { return 0; } if (!nghttp2::tls::check_http2_tls_version(tls.ssl)) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "TLSv1.2 was not negotiated. HTTP/2 must not be used."; } return -1; } auto check_block_list = false; if (tls.server_handshake) { check_block_list = !get_config()->tls.no_http2_cipher_block_list; } else { check_block_list = !get_config()->tls.client.no_http2_cipher_block_list; } if (check_block_list && nghttp2::tls::check_http2_cipher_block_list(tls.ssl)) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "The negotiated cipher suite is in HTTP/2 cipher suite " "block list. HTTP/2 must not be used."; } return -1; } return 0; } namespace { constexpr size_t SHRPX_SMALL_WRITE_LIMIT = 1300; } // namespace size_t Connection::get_tls_write_limit() { if (tls_dyn_rec_warmup_threshold == 0) { return std::numeric_limits::max(); } auto t = std::chrono::steady_clock::now(); if (tls.last_write_idle.time_since_epoch().count() >= 0 && t - tls.last_write_idle > tls_dyn_rec_idle_timeout) { // Time out, use small record size tls.warmup_writelen = 0; return SHRPX_SMALL_WRITE_LIMIT; } if (tls.warmup_writelen >= tls_dyn_rec_warmup_threshold) { return std::numeric_limits::max(); } return SHRPX_SMALL_WRITE_LIMIT; } void Connection::update_tls_warmup_writelen(size_t n) { if (tls.warmup_writelen < tls_dyn_rec_warmup_threshold) { tls.warmup_writelen += n; } } void Connection::start_tls_write_idle() { if (tls.last_write_idle.time_since_epoch().count() < 0) { tls.last_write_idle = std::chrono::steady_clock::now(); } } nghttp2_ssize Connection::write_tls(const void *data, size_t len) { // SSL_write requires the same arguments (buf pointer and its // length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. // get_write_limit() may return smaller length than previously // passed to SSL_write, which violates OpenSSL assumption. To avoid // this, we keep last length passed to SSL_write to // tls.last_writelen if SSL_write indicated I/O blocking. if (tls.last_writelen == 0) { len = std::min(len, wlimit.avail()); len = std::min(len, get_tls_write_limit()); if (len == 0) { return 0; } } else { len = tls.last_writelen; tls.last_writelen = 0; } tls.last_write_idle = std::chrono::steady_clock::time_point(-1s); ERR_clear_error(); #ifdef NGHTTP2_GENUINE_OPENSSL int rv; if (SSL_is_init_finished(tls.ssl)) { rv = SSL_write(tls.ssl, data, static_cast(len)); } else { size_t nwrite; rv = SSL_write_early_data(tls.ssl, data, len, &nwrite); // Use the same semantics with SSL_write. if (rv == 1) { rv = static_cast(nwrite); } } #else // !defined(NGHTTP2_GENUINE_OPENSSL) auto rv = SSL_write(tls.ssl, data, static_cast(len)); #endif // !defined(NGHTTP2_GENUINE_OPENSSL) if (rv <= 0) { auto err = SSL_get_error(tls.ssl, rv); switch (err) { case SSL_ERROR_WANT_READ: if (LOG_ENABLED(INFO)) { LOG(INFO) << "Close connection due to TLS renegotiation"; } return SHRPX_ERR_NETWORK; case SSL_ERROR_WANT_WRITE: tls.last_writelen = len; wlimit.startw(); ev_timer_again(loop, &wt); return 0; case SSL_ERROR_SSL: if (LOG_ENABLED(INFO)) { LOG(INFO) << "SSL_write: " << ERR_error_string(ERR_get_error(), nullptr); } return SHRPX_ERR_NETWORK; default: if (LOG_ENABLED(INFO)) { LOG(INFO) << "SSL_write: SSL_get_error returned " << err; } return SHRPX_ERR_NETWORK; } } wlimit.drain(static_cast(rv)); if (ev_is_active(&wt)) { ev_timer_again(loop, &wt); } update_tls_warmup_writelen(static_cast(rv)); return rv; } nghttp2_ssize Connection::read_tls(void *data, size_t len) { ERR_clear_error(); #if defined(NGHTTP2_GENUINE_OPENSSL) || \ defined(NGHTTP2_OPENSSL_IS_BORINGSSL) || defined(NGHTTP2_OPENSSL_IS_WOLFSSL) if (tls.earlybuf.rleft()) { return as_signed(tls.earlybuf.remove(data, len)); } #endif // defined(NGHTTP2_GENUINE_OPENSSL) || // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) || // defined(NGHTTP2_OPENSSL_IS_WOLFSSL) // SSL_read requires the same arguments (buf pointer and its // length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. // rlimit_.avail() or rlimit_.avail() may return different length // than the length previously passed to SSL_read, which violates // OpenSSL assumption. To avoid this, we keep last length passed // to SSL_read to tls_last_readlen_ if SSL_read indicated I/O // blocking. if (tls.last_readlen == 0) { len = std::min(len, rlimit.avail()); if (len == 0) { return 0; } } else { len = tls.last_readlen; tls.last_readlen = 0; } #ifdef NGHTTP2_GENUINE_OPENSSL if (!tls.early_data_finish) { // TLSv1.3 handshake is still going on. size_t nread; auto rv = SSL_read_early_data(tls.ssl, data, len, &nread); if (rv == SSL_READ_EARLY_DATA_ERROR) { auto err = SSL_get_error(tls.ssl, rv); switch (err) { case SSL_ERROR_WANT_READ: tls.last_readlen = len; return 0; case SSL_ERROR_SSL: if (LOG_ENABLED(INFO)) { LOG(INFO) << "SSL_read: " << ERR_error_string(ERR_get_error(), nullptr); } return SHRPX_ERR_NETWORK; default: if (LOG_ENABLED(INFO)) { LOG(INFO) << "SSL_read: SSL_get_error returned " << err; } return SHRPX_ERR_NETWORK; } } if (LOG_ENABLED(INFO)) { LOG(INFO) << "tls: read early data " << nread << " bytes"; } if (rv == SSL_READ_EARLY_DATA_FINISH) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "tls: read all early data"; } tls.early_data_finish = true; // We may have stopped write watcher in write_tls. wlimit.startw(); } rlimit.drain(nread); return as_signed(nread); } #endif // defined(NGHTTP2_GENUINE_OPENSSL) #if defined(NGHTTP2_OPENSSL_IS_WOLFSSL) && defined(WOLFSSL_EARLY_DATA) if (!tls.early_data_finish) { // TLSv1.3 handshake is still going on. size_t nread = 0; auto rv = SSL_read_early_data(tls.ssl, data, len, &nread); if (rv < 0) { auto err = SSL_get_error(tls.ssl, rv); switch (err) { case SSL_ERROR_WANT_READ: tls.last_readlen = len; return 0; case SSL_ERROR_SSL: if (LOG_ENABLED(INFO)) { LOG(INFO) << "SSL_read: " << ERR_error_string(ERR_get_error(), nullptr); } return SHRPX_ERR_NETWORK; default: if (LOG_ENABLED(INFO)) { LOG(INFO) << "SSL_read: SSL_get_error returned " << err; } return SHRPX_ERR_NETWORK; } } if (LOG_ENABLED(INFO)) { LOG(INFO) << "tls: read early data " << nread << " bytes"; } if (rv == 0) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "tls: read all early data"; } tls.early_data_finish = true; // We may have stopped write watcher in write_tls. wlimit.startw(); } rlimit.drain(nread); return as_signed(nread); } #endif // defined(NGHTTP2_OPENSSL_IS_WOLFSSL) && // defined(WOLFSSL_EARLY_DATA) auto rv = SSL_read(tls.ssl, data, static_cast(len)); if (rv <= 0) { auto err = SSL_get_error(tls.ssl, rv); switch (err) { case SSL_ERROR_WANT_READ: tls.last_readlen = len; return 0; case SSL_ERROR_WANT_WRITE: if (LOG_ENABLED(INFO)) { LOG(INFO) << "Close connection due to TLS renegotiation"; } return SHRPX_ERR_NETWORK; case SSL_ERROR_ZERO_RETURN: return SHRPX_ERR_EOF; case SSL_ERROR_SSL: if (LOG_ENABLED(INFO)) { LOG(INFO) << "SSL_read: " << ERR_error_string(ERR_get_error(), nullptr); } return SHRPX_ERR_NETWORK; default: if (LOG_ENABLED(INFO)) { LOG(INFO) << "SSL_read: SSL_get_error returned " << err; } return SHRPX_ERR_NETWORK; } } rlimit.drain(static_cast(rv)); return rv; } nghttp2_ssize Connection::write_clear(const void *data, size_t len) { len = std::min(len, wlimit.avail()); if (len == 0) { return 0; } ssize_t nwrite; while ((nwrite = write(fd, data, len)) == -1 && errno == EINTR) ; if (nwrite == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { wlimit.startw(); ev_timer_again(loop, &wt); return 0; } return SHRPX_ERR_NETWORK; } wlimit.drain(as_unsigned(nwrite)); if (ev_is_active(&wt)) { ev_timer_again(loop, &wt); } return nwrite; } nghttp2_ssize Connection::writev_clear(struct iovec *iov, int iovcnt) { iovcnt = limit_iovec(iov, iovcnt, wlimit.avail()); if (iovcnt == 0) { return 0; } ssize_t nwrite; while ((nwrite = writev(fd, iov, iovcnt)) == -1 && errno == EINTR) ; if (nwrite == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { wlimit.startw(); ev_timer_again(loop, &wt); return 0; } return SHRPX_ERR_NETWORK; } wlimit.drain(as_unsigned(nwrite)); if (ev_is_active(&wt)) { ev_timer_again(loop, &wt); } return nwrite; } nghttp2_ssize Connection::read_clear(void *data, size_t len) { len = std::min(len, rlimit.avail()); if (len == 0) { return 0; } ssize_t nread; while ((nread = read(fd, data, len)) == -1 && errno == EINTR) ; if (nread == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return 0; } return SHRPX_ERR_NETWORK; } if (nread == 0) { return SHRPX_ERR_EOF; } rlimit.drain(as_unsigned(nread)); return nread; } nghttp2_ssize Connection::read_nolim_clear(void *data, size_t len) { ssize_t nread; while ((nread = read(fd, data, len)) == -1 && errno == EINTR) ; if (nread == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return 0; } return SHRPX_ERR_NETWORK; } if (nread == 0) { return SHRPX_ERR_EOF; } return nread; } nghttp2_ssize Connection::peek_clear(void *data, size_t len) { ssize_t nread; while ((nread = recv(fd, data, len, MSG_PEEK)) == -1 && errno == EINTR) ; if (nread == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return 0; } return SHRPX_ERR_NETWORK; } if (nread == 0) { return SHRPX_ERR_EOF; } return nread; } void Connection::handle_tls_pending_read() { if (!ev_is_active(&rev)) { return; } rlimit.handle_tls_pending_read(); } int Connection::get_tcp_hint(TCPHint *hint) const { #if defined(TCP_INFO) && defined(TCP_NOTSENT_LOWAT) struct tcp_info tcp_info; socklen_t tcp_info_len = sizeof(tcp_info); int rv; rv = getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcp_info, &tcp_info_len); if (rv != 0) { return -1; } auto avail_packets = tcp_info.tcpi_snd_cwnd > tcp_info.tcpi_unacked ? tcp_info.tcpi_snd_cwnd - tcp_info.tcpi_unacked : 0; // http://www.slideshare.net/kazuho/programming-tcp-for-responsiveness // TODO 29 (5 (header) + 8 (explicit nonce) + 16 (tag)) is TLS // overhead for AES-GCM. For CHACHA20_POLY1305, it is 21 since it // does not need 8 bytes explicit nonce. // // For TLSv1.3, AES-GCM and CHACHA20_POLY1305 overhead are now 22 // bytes (5 (header) + 1 (ContentType) + 16 (tag)). size_t tls_overhead; # ifdef TLS1_3_VERSION if (SSL_version(tls.ssl) == TLS1_3_VERSION) { tls_overhead = 22; } else # endif // defined(TLS1_3_VERSION) { tls_overhead = 29; } auto writable_size = (avail_packets + 2) * (tcp_info.tcpi_snd_mss - tls_overhead); if (writable_size > 16_k) { writable_size = writable_size & ~(16_k - 1); } else { if (writable_size < 536) { LOG(INFO) << "writable_size is too small: " << writable_size; } // TODO is this required? writable_size = std::max(writable_size, static_cast(536 * 2)); } // if (LOG_ENABLED(INFO)) { // LOG(INFO) << "snd_cwnd=" << tcp_info.tcpi_snd_cwnd // << ", unacked=" << tcp_info.tcpi_unacked // << ", snd_mss=" << tcp_info.tcpi_snd_mss // << ", rtt=" << tcp_info.tcpi_rtt << "us" // << ", rcv_space=" << tcp_info.tcpi_rcv_space // << ", writable=" << writable_size; // } hint->write_buffer_size = writable_size; // TODO tcpi_rcv_space is considered as rwin, is that correct? hint->rwin = tcp_info.tcpi_rcv_space; return 0; #else // !defined(TCP_INFO) || !defined(TCP_NOTSENT_LOWAT) return -1; #endif // !defined(TCP_INFO) || !defined(TCP_NOTSENT_LOWAT) } void Connection::again_rt(ev_tstamp t) { read_timeout = t; rt.repeat = t; ev_timer_again(loop, &rt); last_read = std::chrono::steady_clock::now(); } void Connection::again_rt() { rt.repeat = read_timeout; ev_timer_again(loop, &rt); last_read = std::chrono::steady_clock::now(); } bool Connection::expired_rt() { auto delta = read_timeout - util::ev_tstamp_from( std::chrono::steady_clock::now() - last_read); if (delta < 1e-9) { return true; } rt.repeat = delta; ev_timer_again(loop, &rt); return false; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_config_test.cc0000644000000000000000000000013115077107270017105 xustar0030 mtime=1761382072.991444148 29 atime=1761382106.25131022 30 ctime=1761382109.247299956 nghttp2-1.68.0/src/shrpx_config_test.cc0000644000175100017510000002137015077107270017501 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_config_test.h" #ifdef HAVE_UNISTD_H # include #endif // defined(HAVE_UNISTD_H) #include #include "munitxx.h" #include "shrpx_config.h" #include "shrpx_log.h" using namespace std::literals; namespace shrpx { namespace { const MunitTest tests[]{ munit_void_test(test_shrpx_config_parse_header), munit_void_test(test_shrpx_config_parse_log_format), munit_void_test(test_shrpx_config_read_tls_ticket_key_file), munit_void_test(test_shrpx_config_read_tls_ticket_key_file_aes_256), munit_test_end(), }; } // namespace const MunitSuite config_suite{ "/config_suite", tests, nullptr, 1, MUNIT_SUITE_OPTION_NONE, }; void test_shrpx_config_parse_header(void) { BlockAllocator balloc(4096, 4096); auto p = parse_header(balloc, "a: b"sv); assert_stdsv_equal("a"sv, p.name); assert_stdsv_equal("b"sv, p.value); p = parse_header(balloc, "a: b"sv); assert_stdsv_equal("a"sv, p.name); assert_stdsv_equal("b"sv, p.value); p = parse_header(balloc, ":a: b"sv); assert_true(p.name.empty()); p = parse_header(balloc, "a: :b"sv); assert_stdsv_equal("a"sv, p.name); assert_stdsv_equal(":b"sv, p.value); p = parse_header(balloc, ": b"sv); assert_true(p.name.empty()); p = parse_header(balloc, "alpha: bravo charlie"sv); assert_stdsv_equal("alpha", p.name); assert_stdsv_equal("bravo charlie", p.value); p = parse_header(balloc, "a,: b"sv); assert_true(p.name.empty()); p = parse_header(balloc, "a: b\x0a"sv); assert_true(p.name.empty()); } void test_shrpx_config_parse_log_format(void) { BlockAllocator balloc(4096, 4096); auto res = parse_log_format( balloc, R"($remote_addr - $remote_user [$time_local] )" R"("$request" $status $body_bytes_sent )" R"("${http_referer}" $http_host "$http_user_agent")"sv); assert_size(16, ==, res.size()); assert_enum_class(LogFragmentType::REMOTE_ADDR, ==, res[0].type); assert_enum_class(LogFragmentType::LITERAL, ==, res[1].type); assert_stdsv_equal(" - $remote_user ["sv, res[1].value); assert_enum_class(LogFragmentType::TIME_LOCAL, ==, res[2].type); assert_enum_class(LogFragmentType::LITERAL, ==, res[3].type); assert_stdsv_equal("] \""sv, res[3].value); assert_enum_class(LogFragmentType::REQUEST, ==, res[4].type); assert_enum_class(LogFragmentType::LITERAL, ==, res[5].type); assert_stdsv_equal("\" "sv, res[5].value); assert_enum_class(LogFragmentType::STATUS, ==, res[6].type); assert_enum_class(LogFragmentType::LITERAL, ==, res[7].type); assert_stdsv_equal(" "sv, res[7].value); assert_enum_class(LogFragmentType::BODY_BYTES_SENT, ==, res[8].type); assert_enum_class(LogFragmentType::LITERAL, ==, res[9].type); assert_stdsv_equal(" \""sv, res[9].value); assert_enum_class(LogFragmentType::HTTP, ==, res[10].type); assert_stdsv_equal("referer"sv, res[10].value); assert_enum_class(LogFragmentType::LITERAL, ==, res[11].type); assert_stdsv_equal("\" "sv, res[11].value); assert_enum_class(LogFragmentType::AUTHORITY, ==, res[12].type); assert_enum_class(LogFragmentType::LITERAL, ==, res[13].type); assert_stdsv_equal(" \""sv, res[13].value); assert_enum_class(LogFragmentType::HTTP, ==, res[14].type); assert_stdsv_equal("user-agent"sv, res[14].value); assert_enum_class(LogFragmentType::LITERAL, ==, res[15].type); assert_stdsv_equal("\""sv, res[15].value); res = parse_log_format(balloc, "$"sv); assert_size(1, ==, res.size()); assert_enum_class(LogFragmentType::LITERAL, ==, res[0].type); assert_stdsv_equal("$"sv, res[0].value); res = parse_log_format(balloc, "${"sv); assert_size(1, ==, res.size()); assert_enum_class(LogFragmentType::LITERAL, ==, res[0].type); assert_stdsv_equal("${"sv, res[0].value); res = parse_log_format(balloc, "${a"sv); assert_size(1, ==, res.size()); assert_enum_class(LogFragmentType::LITERAL, ==, res[0].type); assert_stdsv_equal("${a"sv, res[0].value); res = parse_log_format(balloc, "${a "sv); assert_size(1, ==, res.size()); assert_enum_class(LogFragmentType::LITERAL, ==, res[0].type); assert_stdsv_equal("${a "sv, res[0].value); res = parse_log_format(balloc, "$$remote_addr"sv); assert_size(2, ==, res.size()); assert_enum_class(LogFragmentType::LITERAL, ==, res[0].type); assert_stdsv_equal("$"sv, res[0].value); assert_enum_class(LogFragmentType::REMOTE_ADDR, ==, res[1].type); assert_stdsv_equal(""sv, res[1].value); } void test_shrpx_config_read_tls_ticket_key_file(void) { char file1[] = "/tmp/nghttpx-unittest.XXXXXX"; auto fd1 = mkstemp(file1); assert_int(-1, !=, fd1); assert_ssize( 48, ==, write(fd1, "0..............12..............34..............5", 48)); char file2[] = "/tmp/nghttpx-unittest.XXXXXX"; auto fd2 = mkstemp(file2); assert_int(-1, !=, fd2); assert_ssize( 48, ==, write(fd2, "6..............78..............9a..............b", 48)); close(fd1); close(fd2); auto ticket_keys = read_tls_ticket_key_file({std::string_view{file1}, std::string_view{file2}}, EVP_aes_128_cbc(), EVP_sha256()); unlink(file1); unlink(file2); assert_not_null(ticket_keys.get()); assert_size(2, ==, ticket_keys->keys.size()); auto key = &ticket_keys->keys[0]; assert_true(std::ranges::equal(key->data.name, "0..............1"sv)); assert_true(std::ranges::equal(std::span{key->data.enc_key}.first(16), "2..............3"sv)); assert_true(std::ranges::equal(std::span{key->data.hmac_key}.first(16), "4..............5"sv)); assert_size(16, ==, key->hmac_keylen); key = &ticket_keys->keys[1]; assert_true(std::ranges::equal(key->data.name, "6..............7"sv)); assert_true(std::ranges::equal(std::span{key->data.enc_key}.first(16), "8..............9"sv)); assert_true(std::ranges::equal(std::span{key->data.hmac_key}.first(16), "a..............b"sv)); assert_size(16, ==, key->hmac_keylen); } void test_shrpx_config_read_tls_ticket_key_file_aes_256(void) { char file1[] = "/tmp/nghttpx-unittest.XXXXXX"; auto fd1 = mkstemp(file1); assert_int(-1, !=, fd1); assert_ssize(80, ==, write(fd1, "0..............12..............................34..." "...........................5", 80)); char file2[] = "/tmp/nghttpx-unittest.XXXXXX"; auto fd2 = mkstemp(file2); assert_int(-1, !=, fd2); assert_ssize(80, ==, write(fd2, "6..............78..............................9a..." "...........................b", 80)); close(fd1); close(fd2); auto ticket_keys = read_tls_ticket_key_file({std::string_view{file1}, std::string_view{file2}}, EVP_aes_256_cbc(), EVP_sha256()); unlink(file1); unlink(file2); assert_not_null(ticket_keys.get()); assert_size(2, ==, ticket_keys->keys.size()); auto key = &ticket_keys->keys[0]; assert_true(std::ranges::equal(key->data.name, "0..............1"sv)); assert_true(std::ranges::equal(key->data.enc_key, "2..............................3"sv)); assert_true(std::ranges::equal(key->data.hmac_key, "4..............................5"sv)); key = &ticket_keys->keys[1]; assert_true(std::ranges::equal(key->data.name, "6..............7"sv)); assert_true(std::ranges::equal(key->data.enc_key, "8..............................9"sv)); assert_true(std::ranges::equal(key->data.hmac_key, "a..............................b"sv)); } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_mruby.cc0000644000000000000000000000013115077107270015737 xustar0029 mtime=1761382072.99744412 30 atime=1761382106.179310537 30 ctime=1761382109.175300164 nghttp2-1.68.0/src/shrpx_mruby.cc0000644000175100017510000001423615077107270016336 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_mruby.h" #include #include #include "shrpx_downstream.h" #include "shrpx_config.h" #include "shrpx_mruby_module.h" #include "shrpx_downstream_connection.h" #include "shrpx_log.h" namespace shrpx { namespace mruby { MRubyContext::MRubyContext(mrb_state *mrb, mrb_value app, mrb_value env) : mrb_(mrb), app_(std::move(app)), env_(std::move(env)) {} MRubyContext::~MRubyContext() { if (mrb_) { mrb_close(mrb_); } } int MRubyContext::run_app(Downstream *downstream, int phase) { if (!mrb_) { return 0; } MRubyAssocData data{downstream, phase}; mrb_->ud = &data; int rv = 0; auto ai = mrb_gc_arena_save(mrb_); auto ai_d = defer([ai, this]() { mrb_gc_arena_restore(mrb_, ai); }); const char *method; switch (phase) { case PHASE_REQUEST: if (!mrb_respond_to(mrb_, app_, mrb_intern_lit(mrb_, "on_req"))) { return 0; } method = "on_req"; break; case PHASE_RESPONSE: if (!mrb_respond_to(mrb_, app_, mrb_intern_lit(mrb_, "on_resp"))) { return 0; } method = "on_resp"; break; default: assert(0); abort(); } auto res = mrb_funcall(mrb_, app_, method, 1, env_); (void)res; if (mrb_->exc) { // If response has been committed, ignore error if (downstream->get_response_state() != DownstreamState::MSG_COMPLETE) { rv = -1; } auto exc = mrb_obj_value(mrb_->exc); auto inspect = mrb_inspect(mrb_, exc); LOG(ERROR) << "Exception caught while executing mruby code: " << mrb_str_to_cstr(mrb_, inspect); } mrb_->ud = nullptr; return rv; } int MRubyContext::run_on_request_proc(Downstream *downstream) { return run_app(downstream, PHASE_REQUEST); } int MRubyContext::run_on_response_proc(Downstream *downstream) { return run_app(downstream, PHASE_RESPONSE); } void MRubyContext::delete_downstream(Downstream *downstream) { if (!mrb_) { return; } delete_downstream_from_module(mrb_, downstream); } namespace { mrb_value instantiate_app(mrb_state *mrb, RProc *proc) { mrb->ud = nullptr; auto res = mrb_top_run(mrb, proc, mrb_top_self(mrb), 0); if (mrb->exc) { auto exc = mrb_obj_value(mrb->exc); auto inspect = mrb_inspect(mrb, exc); LOG(ERROR) << "Exception caught while executing mruby code: " << mrb_str_to_cstr(mrb, inspect); return mrb_nil_value(); } return res; } } // namespace // Based on // https://github.com/h2o/h2o/blob/master/lib/handler/mruby.c. It is // very hard to write these kind of code because mruby has almost no // documentation about compiling or generating code, at least at the // time of this writing. RProc *compile(mrb_state *mrb, const std::string_view &filename) { if (filename.empty()) { return nullptr; } auto infile = fopen(filename.data(), "rb"); if (infile == nullptr) { LOG(ERROR) << "Could not open mruby file " << filename; return nullptr; } auto infile_d = defer(fclose, infile); auto mrbc = mrb_ccontext_new(mrb); if (mrbc == nullptr) { LOG(ERROR) << "mrb_context_new failed"; return nullptr; } auto mrbc_d = defer(mrb_ccontext_free, mrb, mrbc); auto parser = mrb_parse_file(mrb, infile, nullptr); if (parser == nullptr) { LOG(ERROR) << "mrb_parse_nstring failed"; return nullptr; } auto parser_d = defer(mrb_parser_free, parser); if (parser->nerr != 0) { LOG(ERROR) << "mruby parser detected parse error"; return nullptr; } auto proc = mrb_generate_code(mrb, parser); if (proc == nullptr) { LOG(ERROR) << "mrb_generate_code failed"; return nullptr; } return proc; } std::unique_ptr create_mruby_context(const std::string_view &filename) { if (filename.empty()) { return std::make_unique(nullptr, mrb_nil_value(), mrb_nil_value()); } auto mrb = mrb_open(); if (mrb == nullptr) { LOG(ERROR) << "mrb_open failed"; return nullptr; } auto ai = mrb_gc_arena_save(mrb); auto req_proc = compile(mrb, filename); if (!req_proc) { mrb_gc_arena_restore(mrb, ai); LOG(ERROR) << "Could not compile mruby code " << filename; mrb_close(mrb); return nullptr; } auto env = init_module(mrb); auto app = instantiate_app(mrb, req_proc); if (mrb_nil_p(app)) { mrb_gc_arena_restore(mrb, ai); LOG(ERROR) << "Could not instantiate mruby app from " << filename; mrb_close(mrb); return nullptr; } mrb_gc_arena_restore(mrb, ai); // TODO These are not necessary, because we retain app and env? mrb_gc_protect(mrb, env); mrb_gc_protect(mrb, app); return std::make_unique(mrb, std::move(app), std::move(env)); } mrb_sym intern_ptr(mrb_state *mrb, void *ptr) { auto p = reinterpret_cast(ptr); return mrb_intern(mrb, reinterpret_cast(&p), sizeof(p)); } void check_phase(mrb_state *mrb, int phase, int phase_mask) { if ((phase & phase_mask) == 0) { mrb_raise(mrb, E_RUNTIME_ERROR, "operation was not allowed in this phase"); } } } // namespace mruby } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/test.nghttp2.org.pem0000644000000000000000000000013115077107271016704 xustar0030 mtime=1761382073.001444102 29 atime=1761382080.10641148 30 ctime=1761382109.283299852 nghttp2-1.68.0/src/test.nghttp2.org.pem0000644000175100017510000000267415077107271017306 0ustar00runnerrunner-----BEGIN CERTIFICATE----- MIIEDjCCAvagAwIBAgIUQBCY8Nre85JT1c7P+HbXUF9yzg8wDQYJKoZIhvcNAQEL BQAwXjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoT GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAxMOY2EubmdodHRwMi5v cmcwHhcNMTYwNjI1MDkzMzAwWhcNMjYwNjIzMDkzMzAwWjBkMQswCQYDVQQGEwJB VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0 cyBQdHkgTHRkMR0wGwYDVQQDExRub3QtdXNlZC5uZ2h0dHAyLm9yZzCCASIwDQYJ KoZIhvcNAQEBBQADggEPADCCAQoCggEBAO6eiimt3LUvkq/539rkyjbU7Ibg1c07 AU22hsnY/KtGA5k95wpzj1l77xtTfoyb+aXAFzdmZ1fVKRodzeYPwmkXBU4EEPKP jZQl2AXTyFwCfipG8ZYpg8N5/cSF33i+vldkLjza6oep9t8cA4OW6CmjZKX+1+Tq 409T4ee0hH19aKdxl28JAD9vt1pccWGLNIbUatoB/e6vSeDJYRzw2qYvrihrPtm9 culMYH+Jw+YxFnT5slanirPn0+I2tp/t4Lo6XTqNpYOdOKedIC+Mz/M4QJXKpUAp niRi9cRcwrZtsB0KO+lx0lnGuKSe68X9qCHbRO4pL4BZU1V5xtU90xECAwEAAaOB vTCBujAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0T AQH/BAIwADAdBgNVHQ4EFgQUGlxgxowH6jrQiyyFpCbwPkCXXIYwHwYDVR0jBBgw FoAU0DnFVHVlxrFEkv1ULqnO4ua924YwRQYDVR0RBD4wPIIQVEVTVC5OR0hUVFAy Lk9SR4ISKi50ZXN0Lm5naHR0cDIub3JnghR3KncudGVzdC5uZ2h0dHAyLm9yZzAN BgkqhkiG9w0BAQsFAAOCAQEANCqM6ocfqOpgDEHYOOQTGFHJIptQhS3kRYAdTIo2 G8XvGCoy+CDYe1GAUWbxE090+a1I1rsYMHcWKJnjKaCBZid7KMhyayIvrmgEsOCh L8iLf3bxkCoyIAmCpxJwa3LMxm2QQLtRx8AoMXWf+N8are4HY6MLNn6aP4zaTrTZ H+WkjKIh7WjSHtW/ro666PCXJDCCdRXljOf8v/fff3bYiLg8o70RBp7OFM0HaPtK wCfcLLxBeoVIncWswB6GtVUFhLeGjepDzWpuDHOdw6DtpghwSXvWFu9bRtl+x02m LAGfJ0kJrpYGfr9UB51NFX3aM/D3p2zxrjKwR2b59vJEcA== -----END CERTIFICATE----- nghttp2-1.68.0/src/PaxHeaders/util.cc0000644000000000000000000000013115077107271014333 xustar0030 mtime=1761382073.001444102 29 atime=1761382080.10641148 30 ctime=1761382109.058300502 nghttp2-1.68.0/src/util.cc0000644000175100017510000013627715077107271014744 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "util.h" #include #ifdef HAVE_SYS_SOCKET_H # include #endif // defined(HAVE_SYS_SOCKET_H) #ifdef HAVE_NETDB_H # include #endif // defined(HAVE_NETDB_H) #include #ifdef HAVE_FCNTL_H # include #endif // defined(HAVE_FCNTL_H) #ifdef HAVE_NETINET_IN_H # include #endif // defined(HAVE_NETINET_IN_H) #ifdef HAVE_NETINET_IP_H # include #endif // defined(HAVE_NETINET_IP_H) #include #ifdef _WIN32 # include #else // !defined(_WIN32) # include #endif // !defined(_WIN32) #ifdef HAVE_ARPA_INET_H # include #endif // defined(HAVE_ARPA_INET_H) #include #include #include #include #include #include #include #include #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include #include "timegm.h" namespace nghttp2 { namespace util { #ifndef _WIN32 namespace { int nghttp2_inet_pton(int af, const char *src, void *dst) { return inet_pton(af, src, dst); } } // namespace #else // defined(_WIN32) namespace { // inet_pton-wrapper for Windows int nghttp2_inet_pton(int af, const char *src, void *dst) { # if _WIN32_WINNT >= 0x0600 return InetPtonA(af, src, dst); # else // _WIN32_WINNT < 0x0600 // the function takes a 'char*', so we need to make a copy char addr[INET6_ADDRSTRLEN + 1]; strncpy(addr, src, sizeof(addr)); addr[sizeof(addr) - 1] = 0; int size = sizeof(struct in6_addr); if (WSAStringToAddress(addr, af, nullptr, (LPSOCKADDR)dst, &size) == 0) return 1; return 0; # endif // _WIN32_WINNT < 0x0600 } } // namespace #endif // defined(_WIN32) namespace { template requires(std::indirectly_writable) O cpydig2(uint32_t n, O result) { return std::ranges::copy_n(utos_digits.data() + n * 2, 2, result).out; } } // namespace namespace { template requires(std::indirectly_writable) O cpydig3(uint32_t n, O result) { *result++ = '0' + static_cast((n / 100) % 10); return std::ranges::copy_n(utos_digits.data() + (n % 100) * 2, 2, result).out; } } // namespace namespace { template requires(std::indirectly_writable) O cpydig4(uint32_t n, O result) { result = std::ranges::copy_n(utos_digits.data() + (n / 100) * 2, 2, result).out; return std::ranges::copy_n(utos_digits.data() + (n % 100) * 2, 2, result).out; } } // namespace namespace { constexpr auto MONTH = std::to_array({ "Jan"sv, "Feb"sv, "Mar"sv, "Apr"sv, "May"sv, "Jun"sv, "Jul"sv, "Aug"sv, "Sep"sv, "Oct"sv, "Nov"sv, "Dec"sv, }); constexpr auto WEEKDAY = std::to_array({ "Sun"sv, "Mon"sv, "Tue"sv, "Wed"sv, "Thu"sv, "Fri"sv, "Sat"sv, }); } // namespace std::string format_http_date(const std::chrono::system_clock::time_point &tp) { // Sat, 27 Sep 2014 06:31:15 GMT std::string res(29 + /* NUL */ 1, 0); auto s = format_http_date(res.data(), tp); res.resize(s.size()); return res; } std::string format_iso8601(const std::chrono::system_clock::time_point &tp) { // 2014-11-15T12:58:24.741Z // 2014-11-15T12:58:24.741+09:00 std::string res(29 + /* NUL */ 1, 0); auto s = format_iso8601(res.data(), tp); res.resize(s.size()); return res; } #ifdef HAVE_STD_CHRONO_TIME_ZONE namespace { const std::chrono::time_zone *get_current_time_zone() { static auto tz = std::chrono::current_zone(); return tz; } } // namespace std::string_view format_iso8601(char *out, const std::chrono::system_clock::time_point &tp) { return format_iso8601(out, tp, get_current_time_zone()); } std::string_view format_iso8601(char *out, const std::chrono::system_clock::time_point &tp, const std::chrono::time_zone *tz) { auto t = std::chrono::floor(tp); auto zt = std::chrono::zoned_time{tz, t}; auto lt = zt.get_local_time(); auto days = std::chrono::floor(lt); auto ymd = std::chrono::year_month_day{days}; auto p = out; p = cpydig4(as_unsigned(static_cast(ymd.year())), p); *p++ = '-'; p = cpydig2(static_cast(ymd.month()), p); *p++ = '-'; p = cpydig2(static_cast(ymd.day()), p); *p++ = 'T'; auto hms = std::chrono::hh_mm_ss{lt - days}; p = cpydig2(static_cast(hms.hours().count()), p); *p++ = ':'; p = cpydig2(static_cast(hms.minutes().count()), p); *p++ = ':'; p = cpydig2(static_cast(hms.seconds().count()), p); *p++ = '.'; p = cpydig3(static_cast(hms.subseconds().count()), p); auto sys_info = zt.get_info(); auto gmtoff = std::chrono::floor(sys_info.offset).count(); if (gmtoff == 0) { *p++ = 'Z'; } else { if (gmtoff > 0) { *p++ = '+'; } else { *p++ = '-'; gmtoff = -gmtoff; } p = cpydig2(static_cast(gmtoff / 60), p); *p++ = ':'; p = cpydig2(static_cast(gmtoff % 60), p); } *p = '\0'; return {out, p}; } std::string_view format_iso8601_basic(char *out, const std::chrono::system_clock::time_point &tp) { return format_iso8601_basic(out, tp, get_current_time_zone()); } std::string_view format_iso8601_basic(char *out, const std::chrono::system_clock::time_point &tp, const std::chrono::time_zone *tz) { auto t = std::chrono::floor(tp); auto zt = std::chrono::zoned_time{tz, t}; auto lt = zt.get_local_time(); auto days = std::chrono::floor(lt); auto ymd = std::chrono::year_month_day{days}; auto p = out; p = cpydig4(as_unsigned(static_cast(ymd.year())), p); p = cpydig2(static_cast(ymd.month()), p); p = cpydig2(static_cast(ymd.day()), p); *p++ = 'T'; auto hms = std::chrono::hh_mm_ss{lt - days}; p = cpydig2(static_cast(hms.hours().count()), p); p = cpydig2(static_cast(hms.minutes().count()), p); p = cpydig2(static_cast(hms.seconds().count()), p); *p++ = '.'; p = cpydig3(static_cast(hms.subseconds().count()), p); auto sys_info = zt.get_info(); auto gmtoff = std::chrono::floor(sys_info.offset).count(); if (gmtoff == 0) { *p++ = 'Z'; } else { if (gmtoff > 0) { *p++ = '+'; } else { *p++ = '-'; gmtoff = -gmtoff; } p = cpydig2(static_cast(gmtoff / 60), p); p = cpydig2(static_cast(gmtoff % 60), p); } *p = '\0'; return {out, p}; } std::string_view format_common_log(char *out, const std::chrono::system_clock::time_point &tp) { return format_common_log(out, tp, get_current_time_zone()); } std::string_view format_common_log(char *out, const std::chrono::system_clock::time_point &tp, const std::chrono::time_zone *tz) { auto t = std::chrono::floor(tp); auto zt = std::chrono::zoned_time{tz, t}; auto lt = zt.get_local_time(); auto days = std::chrono::floor(lt); auto ymd = std::chrono::year_month_day{days}; auto p = out; p = cpydig2(static_cast(ymd.day()), p); *p++ = '/'; p = std::ranges::copy(MONTH[static_cast(ymd.month()) - 1], p).out; *p++ = '/'; p = cpydig4(as_unsigned(static_cast(ymd.year())), p); *p++ = ':'; auto hms = std::chrono::hh_mm_ss{lt - days}; p = cpydig2(static_cast(hms.hours().count()), p); *p++ = ':'; p = cpydig2(static_cast(hms.minutes().count()), p); *p++ = ':'; p = cpydig2(static_cast(hms.seconds().count()), p); *p++ = ' '; auto sys_info = zt.get_info(); auto gmtoff = std::chrono::floor(sys_info.offset).count(); if (gmtoff >= 0) { *p++ = '+'; } else { *p++ = '-'; gmtoff = -gmtoff; } p = cpydig2(static_cast(gmtoff / 60), p); p = cpydig2(static_cast(gmtoff % 60), p); *p = '\0'; return {out, p}; } std::string_view format_http_date(char *out, const std::chrono::system_clock::time_point &tp) { auto t = std::chrono::floor(tp); auto days = std::chrono::floor(t); auto ymd = std::chrono::year_month_day{days}; auto weekday = std::chrono::weekday{ymd}; auto p = out; p = std::ranges::copy(WEEKDAY[weekday.c_encoding()], p).out; *p++ = ','; *p++ = ' '; p = cpydig2(static_cast(ymd.day()), p); *p++ = ' '; p = std::ranges::copy(MONTH[static_cast(ymd.month()) - 1], p).out; *p++ = ' '; p = cpydig4(as_unsigned(static_cast(ymd.year())), p); *p++ = ' '; auto hms = std::chrono::hh_mm_ss{t - days}; p = cpydig2(static_cast(hms.hours().count()), p); *p++ = ':'; p = cpydig2(static_cast(hms.minutes().count()), p); *p++ = ':'; p = cpydig2(static_cast(hms.seconds().count()), p); p = std::ranges::copy(" GMT"sv, p).out; *p = '\0'; return {out, p}; } #else // !defined(HAVE_STD_CHRONO_TIME_ZONE) namespace { char *iso8601_date(char *out, const std::chrono::system_clock::time_point &tp) { auto ms = std::chrono::floor(tp.time_since_epoch()) .count(); time_t sec = ms / 1000; tm tms; if (localtime_r(&sec, &tms) == nullptr) { return out; } auto p = out; p = cpydig4(static_cast(tms.tm_year + 1900), p); *p++ = '-'; p = cpydig2(static_cast(tms.tm_mon + 1), p); *p++ = '-'; p = cpydig2(static_cast(tms.tm_mday), p); *p++ = 'T'; p = cpydig2(static_cast(tms.tm_hour), p); *p++ = ':'; p = cpydig2(static_cast(tms.tm_min), p); *p++ = ':'; p = cpydig2(static_cast(tms.tm_sec), p); *p++ = '.'; p = cpydig3(static_cast(ms % 1000), p); # ifdef HAVE_STRUCT_TM_TM_GMTOFF auto gmtoff = tms.tm_gmtoff; # else // !defined(HAVE_STRUCT_TM_TM_GMTOFF) auto gmtoff = nghttp2_timegm(&tms) - sec; # endif // !defined(HAVE_STRUCT_TM_TM_GMTOFF) if (gmtoff == 0) { *p++ = 'Z'; } else { if (gmtoff > 0) { *p++ = '+'; } else { *p++ = '-'; gmtoff = -gmtoff; } p = cpydig2(static_cast(gmtoff / 3600), p); *p++ = ':'; p = cpydig2(static_cast((gmtoff % 3600) / 60), p); } return p; } } // namespace std::string_view format_iso8601(char *out, const std::chrono::system_clock::time_point &tp) { auto p = iso8601_date(out, tp); *p = '\0'; return std::string_view{out, p}; } namespace { char *iso8601_basic_date(char *out, const std::chrono::system_clock::time_point &tp) { auto ms = std::chrono::floor(tp.time_since_epoch()) .count(); time_t sec = ms / 1000; tm tms; if (localtime_r(&sec, &tms) == nullptr) { return out; } auto p = out; p = cpydig4(static_cast(tms.tm_year + 1900), p); p = cpydig2(static_cast(tms.tm_mon + 1), p); p = cpydig2(static_cast(tms.tm_mday), p); *p++ = 'T'; p = cpydig2(static_cast(tms.tm_hour), p); p = cpydig2(static_cast(tms.tm_min), p); p = cpydig2(static_cast(tms.tm_sec), p); *p++ = '.'; p = cpydig3(static_cast(ms % 1000), p); # ifdef HAVE_STRUCT_TM_TM_GMTOFF auto gmtoff = tms.tm_gmtoff; # else // !defined(HAVE_STRUCT_TM_TM_GMTOFF) auto gmtoff = nghttp2_timegm(&tms) - sec; # endif // !defined(HAVE_STRUCT_TM_TM_GMTOFF) if (gmtoff == 0) { *p++ = 'Z'; } else { if (gmtoff > 0) { *p++ = '+'; } else { *p++ = '-'; gmtoff = -gmtoff; } p = cpydig2(static_cast(gmtoff / 3600), p); p = cpydig2(static_cast((gmtoff % 3600) / 60), p); } return p; } } // namespace std::string_view format_iso8601_basic(char *out, const std::chrono::system_clock::time_point &tp) { auto p = iso8601_basic_date(out, tp); *p = '\0'; return {out, p}; } namespace { char *common_log_date(char *out, const std::chrono::system_clock::time_point &tp) { time_t t = std::chrono::floor(tp.time_since_epoch()).count(); struct tm tms; if (localtime_r(&t, &tms) == nullptr) { return out; } auto p = out; p = cpydig2(static_cast(tms.tm_mday), p); *p++ = '/'; p = std::ranges::copy(MONTH[static_cast(tms.tm_mon)], p).out; *p++ = '/'; p = cpydig4(static_cast(tms.tm_year + 1900), p); *p++ = ':'; p = cpydig2(static_cast(tms.tm_hour), p); *p++ = ':'; p = cpydig2(static_cast(tms.tm_min), p); *p++ = ':'; p = cpydig2(static_cast(tms.tm_sec), p); *p++ = ' '; # ifdef HAVE_STRUCT_TM_TM_GMTOFF auto gmtoff = tms.tm_gmtoff; # else // !defined(HAVE_STRUCT_TM_TM_GMTOFF) auto gmtoff = nghttp2_timegm(&tms) - t; # endif // !defined(HAVE_STRUCT_TM_TM_GMTOFF) if (gmtoff >= 0) { *p++ = '+'; } else { *p++ = '-'; gmtoff = -gmtoff; } p = cpydig2(static_cast(gmtoff / 3600), p); p = cpydig2(static_cast((gmtoff % 3600) / 60), p); return p; } } // namespace std::string_view format_common_log(char *out, const std::chrono::system_clock::time_point &tp) { auto p = common_log_date(out, tp); *p = '\0'; return {out, p}; } namespace { char *http_date(char *out, const std::chrono::system_clock::time_point &tp) { time_t t = std::chrono::floor(tp.time_since_epoch()).count(); struct tm tms; if (gmtime_r(&t, &tms) == nullptr) { return out; } auto p = out; p = std::ranges::copy(WEEKDAY[static_cast(tms.tm_wday)], p).out; *p++ = ','; *p++ = ' '; p = cpydig2(static_cast(tms.tm_mday), p); *p++ = ' '; p = std::ranges::copy(MONTH[static_cast(tms.tm_mon)], p).out; *p++ = ' '; p = cpydig4(static_cast(tms.tm_year + 1900), p); *p++ = ' '; p = cpydig2(static_cast(tms.tm_hour), p); *p++ = ':'; p = cpydig2(static_cast(tms.tm_min), p); *p++ = ':'; p = cpydig2(static_cast(tms.tm_sec), p); p = std::ranges::copy(" GMT"sv, p).out; return p; } } // namespace std::string_view format_http_date(char *out, const std::chrono::system_clock::time_point &tp) { auto p = http_date(out, tp); *p = '\0'; return {out, p}; } #endif // !defined(HAVE_STD_CHRONO_TIME_ZONE) time_t parse_http_date(const std::string_view &s) { tm tm{}; #ifdef _WIN32 // there is no strptime - use std::get_time std::stringstream sstr(s.data()); sstr >> std::get_time(&tm, "%a, %d %b %Y %H:%M:%S GMT"); if (sstr.fail()) { return 0; } #else // !defined(_WIN32) char *r = strptime(s.data(), "%a, %d %b %Y %H:%M:%S GMT", &tm); if (r == 0) { return 0; } #endif // !defined(_WIN32) return nghttp2_timegm_without_yday(&tm); } time_t parse_openssl_asn1_time_print(const std::string_view &s) { tm tm{}; auto r = strptime(s.data(), "%b %d %H:%M:%S %Y GMT", &tm); if (r == nullptr) { return 0; } return nghttp2_timegm_without_yday(&tm); } void to_token68(std::string &base64str) { for (auto it = std::ranges::begin(base64str); it != std::ranges::end(base64str); ++it) { switch (*it) { case '+': *it = '-'; break; case '/': *it = '_'; break; case '=': base64str.erase(it, std::ranges::end(base64str)); return; } } } std::string_view to_base64(BlockAllocator &balloc, const std::string_view &token68str) { // At most 3 padding '=' auto len = token68str.size() + 3; auto iov = make_byte_ref(balloc, len + 1); auto p = std::ranges::transform(token68str, std::ranges::begin(iov), [](char c) { switch (c) { case '-': return '+'; case '_': return '/'; default: return c; } }).out; auto rem = token68str.size() & 0x3; if (rem) { p = std::ranges::fill_n(p, as_signed(4 - rem), '='); } *p = '\0'; return as_string_view(std::ranges::begin(iov), p); } namespace { // Calculates Damerau–Levenshtein distance between c-string a and b // with given costs. swapcost, subcost, addcost and delcost are cost // to swap 2 adjacent characters, substitute characters, add character // and delete character respectively. uint32_t levenshtein(const std::string_view &a, const std::string_view &b, uint32_t swapcost, uint32_t subcost, uint32_t addcost, uint32_t delcost) { auto dp = std::vector>(3, std::vector(b.size() + 1)); for (uint32_t i = 0; i <= static_cast(b.size()); ++i) { dp[1][i] = i * addcost; } for (uint32_t i = 1; i <= static_cast(a.size()); ++i) { dp[0][0] = i * delcost; for (uint32_t j = 1; j <= static_cast(b.size()); ++j) { dp[0][j] = dp[1][j - 1] + (a[i - 1] == b[j - 1] ? 0 : subcost); if (i >= 2 && j >= 2 && a[i - 1] != b[j - 1] && a[i - 2] == b[j - 1] && a[i - 1] == b[j - 2]) { dp[0][j] = std::min(dp[0][j], dp[2][j - 2] + swapcost); } dp[0][j] = std::min(dp[0][j], std::min(dp[1][j] + delcost, dp[0][j - 1] + addcost)); } std::ranges::rotate(dp, std::ranges::begin(dp) + 2); } return dp[1][b.size()]; } } // namespace void show_candidates(const char *unkopt, const option *options) { for (; *unkopt == '-'; ++unkopt) ; if (*unkopt == '\0') { return; } auto unkoptend = unkopt; for (; *unkoptend && *unkoptend != '='; ++unkoptend) ; auto unkoptlen = unkoptend - unkopt; if (unkoptlen == 0) { return; } int prefix_match = 0; auto cands = std::vector>(); for (size_t i = 0; options[i].name != nullptr; ++i) { auto opt = std::string_view{options[i].name}; auto unk = std::string_view{unkopt, static_cast(unkoptlen)}; // Use cost 0 for prefix match if (istarts_with(opt, unk)) { if (opt.size() == unk.size()) { // Exact match, then we don't show any candidates. return; } ++prefix_match; cands.emplace_back(0, opt); continue; } // Use cost 0 for suffix match, but match at least 3 characters if (unk.size() >= 3 && iends_with(opt, unk)) { cands.emplace_back(0, options[i].name); continue; } // cost values are borrowed from git, help.c. auto sim = levenshtein(unk, opt, 0, 2, 1, 3); cands.emplace_back(sim, options[i].name); } if (prefix_match == 1 || cands.empty()) { return; } std::ranges::sort(cands); auto threshold = cands[0].first; // threshold value is a magic value. if (threshold > 6) { return; } std::cerr << "\nDid you mean:\n"; for (auto &item : cands) { if (item.first > threshold) { break; } std::cerr << "\t--" << item.second << "\n"; } } bool has_uri_field(const urlparse_url &u, urlparse_url_fields field) { return u.field_set & (1 << field); } bool fieldeq(const char *uri1, const urlparse_url &u1, const char *uri2, const urlparse_url &u2, urlparse_url_fields field) { if (!has_uri_field(u1, field)) { if (!has_uri_field(u2, field)) { return true; } else { return false; } } else if (!has_uri_field(u2, field)) { return false; } if (u1.field_data[field].len != u2.field_data[field].len) { return false; } return memcmp(uri1 + u1.field_data[field].off, uri2 + u2.field_data[field].off, u1.field_data[field].len) == 0; } bool fieldeq(const char *uri, const urlparse_url &u, urlparse_url_fields field, const char *t) { return fieldeq(uri, u, field, std::string_view{t}); } bool fieldeq(const char *uri, const urlparse_url &u, urlparse_url_fields field, const std::string_view &t) { if (!has_uri_field(u, field)) { return t.empty(); } auto &f = u.field_data[field]; return std::string_view{uri + f.off, f.len} == t; } std::string_view get_uri_field(const char *uri, const urlparse_url &u, urlparse_url_fields field) { if (!util::has_uri_field(u, field)) { return ""sv; } return std::string_view{uri + u.field_data[field].off, u.field_data[field].len}; } uint16_t get_default_port(const char *uri, const urlparse_url &u) { if (util::fieldeq(uri, u, URLPARSE_SCHEMA, "https")) { return 443; } else if (util::fieldeq(uri, u, URLPARSE_SCHEMA, "http")) { return 80; } else { return 443; } } bool porteq(const char *uri1, const urlparse_url &u1, const char *uri2, const urlparse_url &u2) { uint16_t port1, port2; port1 = util::has_uri_field(u1, URLPARSE_PORT) ? u1.port : get_default_port(uri1, u1); port2 = util::has_uri_field(u2, URLPARSE_PORT) ? u2.port : get_default_port(uri2, u2); return port1 == port2; } void write_uri_field(std::ostream &o, const char *uri, const urlparse_url &u, urlparse_url_fields field) { if (util::has_uri_field(u, field)) { o.write(uri + u.field_data[field].off, u.field_data[field].len); } } bool numeric_host(const char *hostname) { return numeric_host(hostname, AF_INET) || numeric_host(hostname, AF_INET6); } bool numeric_host(const char *hostname, int family) { int rv; std::array dst; rv = nghttp2_inet_pton(family, hostname, dst.data()); return rv == 1; } std::string numeric_name(const struct sockaddr *sa, socklen_t salen) { std::array host; auto rv = getnameinfo(sa, salen, host.data(), host.size(), nullptr, 0, NI_NUMERICHOST); if (rv != 0) { return "unknown"; } return host.data(); } std::string to_numeric_addr(const Address *addr) { return to_numeric_addr(&addr->su.sa, addr->len); } std::string to_numeric_addr(const struct sockaddr *sa, socklen_t salen) { auto family = sa->sa_family; #ifndef _WIN32 if (family == AF_UNIX) { return reinterpret_cast(sa)->sun_path; } #endif // !defined(_WIN32) std::array hostbuf; std::array servbuf; auto rv = getnameinfo(sa, salen, hostbuf.data(), hostbuf.size(), servbuf.data(), servbuf.size(), NI_NUMERICHOST | NI_NUMERICSERV); if (rv != 0) { return "unknown"; } auto host = std::string_view{hostbuf.data()}; auto serv = std::string_view{servbuf.data()}; std::string s; char *p; if (family == AF_INET6) { s.resize(host.size() + serv.size() + 2 + 1); p = &s[0]; *p++ = '['; p = std::ranges::copy(host, p).out; *p++ = ']'; } else { s.resize(host.size() + serv.size() + 1); p = &s[0]; p = std::ranges::copy(host, p).out; } *p++ = ':'; std::ranges::copy(serv, p); return s; } void set_port(Address &addr, uint16_t port) { switch (addr.su.storage.ss_family) { case AF_INET: addr.su.in.sin_port = htons(port); break; case AF_INET6: addr.su.in6.sin6_port = htons(port); break; } } uint16_t get_port(const sockaddr_union *su) { switch (su->storage.ss_family) { case AF_INET: return ntohs(su->in.sin_port); case AF_INET6: return ntohs(su->in6.sin6_port); default: return 0; } } bool quic_prohibited_port(uint16_t port) { switch (port) { case 1900: case 5353: case 11211: case 20800: case 27015: return true; default: return port < 1024; } } std::string ascii_dump(const uint8_t *data, size_t len) { std::string res; for (size_t i = 0; i < len; ++i) { auto c = data[i]; if (c >= 0x20 && c < 0x7f) { res += as_signed(c); } else { res += '.'; } } return res; } char *get_exec_path(size_t argc, char **const argv, const char *cwd) { if (argc == 0 || cwd == nullptr) { return nullptr; } auto argv0 = argv[0]; auto len = strlen(argv0); char *path; if (argv0[0] == '/') { path = static_cast(malloc(len + 1)); if (path == nullptr) { return nullptr; } memcpy(path, argv0, len + 1); } else { auto cwdlen = strlen(cwd); path = static_cast(malloc(len + 1 + cwdlen + 1)); if (path == nullptr) { return nullptr; } memcpy(path, cwd, cwdlen); path[cwdlen] = '/'; memcpy(path + cwdlen + 1, argv0, len + 1); } return path; } bool check_path(const std::string &path) { // We don't like '\' in path. return !path.empty() && path[0] == '/' && path.find('\\') == std::string::npos && path.find("/../") == std::string::npos && path.find("/./") == std::string::npos && !util::ends_with(path, "/.."sv) && !util::ends_with(path, "/."sv); } int64_t to_time64(const timeval &tv) { return tv.tv_sec * 1000000 + tv.tv_usec; } bool check_h2_is_selected(const std::string_view &proto) { return NGHTTP2_H2 == proto; } namespace { bool select_proto(const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, const std::string_view &key) { for (auto p = in, end = in + inlen; p + key.size() <= end; p += *p + 1) { if (std::ranges::equal(key, as_string_view(p, key.size()))) { *out = p + 1; *outlen = *p; return true; } } return false; } } // namespace bool select_h2(const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen) { return select_proto(out, outlen, in, inlen, NGHTTP2_H2_ALPN); } bool select_protocol(const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, std::vector proto_list) { for (const auto &proto : proto_list) { if (select_proto(out, outlen, in, inlen, proto)) { return true; } } return false; } std::vector split_str(const std::string_view &s, char delim) { size_t len = 1; auto last = std::ranges::end(s); std::string_view::const_iterator d; for (auto first = std::ranges::begin(s); (d = std::ranges::find(first, last, delim)) != last; ++len, first = d + 1) ; auto list = std::vector(len); len = 0; for (auto first = std::ranges::begin(s);; ++len) { auto stop = std::ranges::find(first, last, delim); list[len] = std::string_view{first, stop}; if (stop == last) { break; } first = stop + 1; } return list; } std::vector split_str(const std::string_view &s, char delim, size_t n) { if (n == 0) { return split_str(s, delim); } if (n == 1) { return {s}; } size_t len = 1; auto last = std::ranges::end(s); std::string_view::const_iterator d; for (auto first = std::ranges::begin(s); len < n && (d = std::ranges::find(first, last, delim)) != last; ++len, first = d + 1) ; auto list = std::vector(len); len = 0; for (auto first = std::ranges::begin(s);; ++len) { if (len == n - 1) { list[len] = std::string_view{first, last}; break; } auto stop = std::ranges::find(first, last, delim); list[len] = std::string_view{first, stop}; if (stop == last) { break; } first = stop + 1; } return list; } std::vector parse_config_str_list(const std::string_view &s, char delim) { auto sublist = split_str(s, delim); auto res = std::vector(); res.reserve(sublist.size()); for (const auto &s : sublist) { res.emplace_back(std::ranges::begin(s), std::ranges::end(s)); } return res; } int make_socket_closeonexec(int fd) { #ifdef _WIN32 (void)fd; return 0; #else // !defined(_WIN32) int flags; int rv; while ((flags = fcntl(fd, F_GETFD)) == -1 && errno == EINTR) ; while ((rv = fcntl(fd, F_SETFD, flags | FD_CLOEXEC)) == -1 && errno == EINTR) ; return rv; #endif // !defined(_WIN32) } int make_socket_nonblocking(int fd) { int rv; #ifdef _WIN32 u_long mode = 1; rv = ioctlsocket(fd, FIONBIO, &mode); #else // !defined(_WIN32) int flags; while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR) ; while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR) ; #endif // !defined(_WIN32) return rv; } int make_socket_nodelay(int fd) { int val = 1; if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&val), sizeof(val)) == -1) { return -1; } return 0; } int create_nonblock_socket(int family) { #ifdef SOCK_NONBLOCK auto fd = socket(family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); if (fd == -1) { return -1; } #else // !defined(SOCK_NONBLOCK) auto fd = socket(family, SOCK_STREAM, 0); if (fd == -1) { return -1; } make_socket_nonblocking(fd); make_socket_closeonexec(fd); #endif // !defined(SOCK_NONBLOCK) if (family == AF_INET || family == AF_INET6) { make_socket_nodelay(fd); } return fd; } int create_nonblock_udp_socket(int family) { #ifdef SOCK_NONBLOCK auto fd = socket(family, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); if (fd == -1) { return -1; } #else // !defined(SOCK_NONBLOCK) auto fd = socket(family, SOCK_DGRAM, 0); if (fd == -1) { return -1; } make_socket_nonblocking(fd); make_socket_closeonexec(fd); #endif // !defined(SOCK_NONBLOCK) return fd; } int bind_any_addr_udp(int fd, int family) { addrinfo *res, *rp; int rv; addrinfo hints{ .ai_flags = AI_PASSIVE, .ai_family = family, .ai_socktype = SOCK_DGRAM, }; rv = getaddrinfo(nullptr, "0", &hints, &res); if (rv != 0) { return -1; } for (rp = res; rp; rp = rp->ai_next) { if (bind(fd, rp->ai_addr, rp->ai_addrlen) != -1) { break; } } freeaddrinfo(res); if (!rp) { return -1; } return 0; } bool check_socket_connected(int fd) { int error; socklen_t len = sizeof(error); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&error, &len) != 0) { return false; } return error == 0; } int get_socket_error(int fd) { int error; socklen_t len = sizeof(error); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&error, &len) != 0) { return -1; } return error; } bool ipv6_numeric_addr(const char *host) { uint8_t dst[16]; return nghttp2_inet_pton(AF_INET6, host, dst) == 1; } namespace { std::optional> parse_uint_digits(const std::string_view &s) { if (s.empty()) { return {}; } constexpr int64_t max = std::numeric_limits::max(); int64_t n = 0; size_t i = 0; for (auto c : s) { if (!is_digit(c)) { break; } if (n > max / 10) { return {}; } n *= 10; if (n > max - (c - '0')) { return {}; } n += c - '0'; ++i; } if (i == 0) { return {}; } return std::pair{n, s.substr(i)}; } } // namespace std::optional parse_uint_with_unit(const std::string_view &s) { auto r = parse_uint_digits(s); if (!r) { return {}; } auto [n, rest] = *r; if (rest.empty()) { return n; } if (rest.size() != 1) { return {}; } int mul = 1; switch (rest[0]) { case 'K': case 'k': mul = 1 << 10; break; case 'M': case 'm': mul = 1 << 20; break; case 'G': case 'g': mul = 1 << 30; break; default: return {}; } constexpr int64_t max = std::numeric_limits::max(); if (n > max / mul) { return {}; } return n * mul; } std::optional parse_uint(const std::string_view &s) { auto r = parse_uint_digits(s); if (!r || !(*r).second.empty()) { return {}; } return (*r).first; } std::optional parse_duration_with_unit(const std::string_view &s) { constexpr auto max = std::numeric_limits::max(); auto r = parse_uint_digits(s); if (!r) { return {}; } auto [n, rest] = *r; if (rest.empty()) { return static_cast(n); } switch (rest[0]) { case 'S': case 's': // seconds if (rest.size() != 1) { return {}; } return static_cast(n); case 'M': case 'm': if (rest.size() == 1) { // minutes if (n > max / 60) { return {}; } return static_cast(n) * 60; } if (rest.size() != 2 || (rest[1] != 's' && rest[1] != 'S')) { return {}; } // milliseconds return static_cast(n) / 1000.; case 'H': case 'h': // hours if (rest.size() != 1) { return {}; } if (n > max / 3600) { return {}; } return static_cast(n) * 3600; default: return {}; } } std::string duration_str(double t) { if (t == 0.) { return "0"; } auto frac = static_cast(t * 1000) % 1000; if (frac > 0) { return utos(static_cast(t * 1000)) + "ms"; } auto v = static_cast(t); if (v % 60) { return utos(v) + "s"; } v /= 60; if (v % 60) { return utos(v) + "m"; } v /= 60; return utos(v) + "h"; } std::string format_duration(const std::chrono::microseconds &u) { auto unit = "us"sv; int d = 0; auto t = as_unsigned(u.count()); if (t >= 1000000) { d = 1000000; unit = "s"sv; } else if (t >= 1000) { d = 1000; unit = "ms"sv; } else { return utos(t).append(unit); } return dtos(static_cast(t) / d).append(unit); } std::string format_duration(double t) { auto unit = "us"sv; if (t >= 1.) { unit = "s"sv; } else if (t >= 0.001) { t *= 1000.; unit = "ms"sv; } else { t *= 1000000.; return utos(static_cast(t)).append(unit); } return dtos(t).append(unit); } std::string dtos(double n) { auto m = as_unsigned(llround(100. * n)); auto f = utos(m % 100); return utos(m / 100) + "." + (f.size() == 1 ? "0" : "") + f; } std::string_view make_http_hostport(BlockAllocator &balloc, const std::string_view &host, uint16_t port) { auto iov = make_byte_ref(balloc, host.size() + 2 + 1 + 5 + 1); return make_http_hostport(host, port, std::ranges::begin(iov)); } std::string_view make_hostport(BlockAllocator &balloc, const std::string_view &host, uint16_t port) { auto iov = make_byte_ref(balloc, host.size() + 2 + 1 + 5 + 1); return make_hostport(host, port, std::ranges::begin(iov)); } namespace { uint8_t *hexdump_addr(uint8_t *dest, size_t addr) { // Lower 32 bits are displayed. return format_hex(static_cast(addr), dest); } } // namespace namespace { uint8_t *hexdump_ascii(uint8_t *dest, const uint8_t *data, size_t datalen) { *dest++ = '|'; for (size_t i = 0; i < datalen; ++i) { if (0x20 <= data[i] && data[i] <= 0x7e) { *dest++ = data[i]; } else { *dest++ = '.'; } } *dest++ = '|'; return dest; } } // namespace namespace { uint8_t *hexdump8(uint8_t *dest, const uint8_t *data, size_t datalen) { size_t i; for (i = 0; i < datalen; ++i) { dest = format_hex(data[i], dest); *dest++ = ' '; } for (; i < 8; ++i) { *dest++ = ' '; *dest++ = ' '; *dest++ = ' '; } return dest; } } // namespace namespace { uint8_t *hexdump16(uint8_t *dest, const uint8_t *data, size_t datalen) { if (datalen > 8) { dest = hexdump8(dest, data, 8); *dest++ = ' '; dest = hexdump8(dest, data + 8, datalen - 8); *dest++ = ' '; } else { dest = hexdump8(dest, data, datalen); *dest++ = ' '; dest = hexdump8(dest, nullptr, 0); *dest++ = ' '; } return dest; } } // namespace namespace { uint8_t *hexdump_line(uint8_t *dest, const uint8_t *data, size_t datalen, size_t addr) { dest = hexdump_addr(dest, addr); *dest++ = ' '; *dest++ = ' '; dest = hexdump16(dest, data, datalen); return hexdump_ascii(dest, data, datalen); } } // namespace namespace { int hexdump_write(int fd, const uint8_t *data, size_t datalen) { ssize_t nwrite; for (; (nwrite = write(fd, data, datalen)) == -1 && errno == EINTR;) ; if (nwrite == -1) { return -1; } return 0; } } // namespace int hexdump(FILE *out, const void *data, size_t datalen) { if (datalen == 0) { return 0; } // min_space is the additional minimum space that the buffer must // accept, which is the size of a single full line output + one // repeat line marker ("*\n"). If the remaining buffer size is less // than that, flush the buffer and reset. constexpr size_t min_space = 79 + 2; auto fd = fileno(out); std::array buf; auto last = buf.data(); auto in = reinterpret_cast(data); auto repeated = false; for (size_t offset = 0; offset < datalen; offset += 16) { auto n = datalen - offset; auto s = in + offset; if (n >= 16) { n = 16; if (offset > 0) { if (std::ranges::equal(s - 16, s, s, s + 16)) { if (repeated) { continue; } repeated = true; *last++ = '*'; *last++ = '\n'; continue; } repeated = false; } } last = hexdump_line(last, s, n, offset); *last++ = '\n'; auto len = static_cast(last - buf.data()); if (len + min_space > buf.size()) { if (hexdump_write(fd, buf.data(), len) != 0) { return -1; } last = buf.data(); } } last = hexdump_addr(last, datalen); *last++ = '\n'; auto len = static_cast(last - buf.data()); if (len) { return hexdump_write(fd, buf.data(), len); } return 0; } void put_uint16be(uint8_t *buf, uint16_t n) { uint16_t x = htons(n); memcpy(buf, &x, sizeof(uint16_t)); } void put_uint32be(uint8_t *buf, uint32_t n) { uint32_t x = htonl(n); memcpy(buf, &x, sizeof(uint32_t)); } uint16_t get_uint16(const uint8_t *data) { uint16_t n; memcpy(&n, data, sizeof(uint16_t)); return ntohs(n); } uint32_t get_uint32(const uint8_t *data) { uint32_t n; memcpy(&n, data, sizeof(uint32_t)); return ntohl(n); } uint64_t get_uint64(const uint8_t *data) { uint64_t n = 0; n += static_cast(data[0]) << 56; n += static_cast(data[1]) << 48; n += static_cast(data[2]) << 40; n += static_cast(data[3]) << 32; n += static_cast(data[4]) << 24; n += static_cast(data[5]) << 16; n += static_cast(data[6]) << 8; n += data[7]; return n; } int read_mime_types(std::unordered_map &res, const char *filename) { std::ifstream infile(filename); if (!infile) { return -1; } auto delim_pred = [](char c) { return c == ' ' || c == '\t'; }; std::string line; while (std::getline(infile, line)) { if (line.empty() || line[0] == '#') { continue; } auto type_end = std::ranges::find_if(line, delim_pred); if (type_end == std::ranges::begin(line)) { continue; } auto ext_end = type_end; for (;;) { auto ext_start = std::ranges::find_if_not(ext_end, std::ranges::end(line), delim_pred); if (ext_start == std::ranges::end(line)) { break; } ext_end = std::ranges::find_if(ext_start, std::ranges::end(line), delim_pred); res.emplace(std::string(ext_start, ext_end), std::string(std::ranges::begin(line), type_end)); } } return 0; } // Returns x**y double int_pow(double x, size_t y) { auto res = 1.; for (; y; --y) { res *= x; } return res; } uint32_t hash32(const std::string_view &s) { /* 32 bit FNV-1a: http://isthe.com/chongo/tech/comp/fnv/ */ uint32_t h = 2166136261u; size_t i; for (i = 0; i < s.size(); ++i) { h ^= static_cast(s[i]); h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24); } return h; } namespace { int message_digest(uint8_t *res, const EVP_MD *meth, const std::string_view &s) { int rv; auto ctx = EVP_MD_CTX_new(); if (ctx == nullptr) { return -1; } auto ctx_deleter = defer(EVP_MD_CTX_free, ctx); rv = EVP_DigestInit_ex(ctx, meth, nullptr); if (rv != 1) { return -1; } rv = EVP_DigestUpdate(ctx, s.data(), s.size()); if (rv != 1) { return -1; } auto mdlen = static_cast(EVP_MD_size(meth)); rv = EVP_DigestFinal_ex(ctx, res, &mdlen); if (rv != 1) { return -1; } return 0; } } // namespace int sha256(uint8_t *res, const std::string_view &s) { return message_digest(res, EVP_sha256(), s); } int sha1(uint8_t *res, const std::string_view &s) { return message_digest(res, EVP_sha1(), s); } std::string_view extract_host(const std::string_view &hostport) { if (hostport.empty()) { return ""sv; } if (hostport[0] == '[') { // assume this is IPv6 numeric address auto p = std::ranges::find(hostport, ']'); if (p == std::ranges::end(hostport)) { return ""sv; } if (p + 1 < std::ranges::end(hostport) && *(p + 1) != ':') { return ""sv; } return std::string_view{std::ranges::begin(hostport), p + 1}; } auto p = std::ranges::find(hostport, ':'); if (p == std::ranges::begin(hostport)) { return ""sv; } return std::string_view{std::ranges::begin(hostport), p}; } std::pair split_hostport(const std::string_view &hostport) { if (hostport.empty()) { return {}; } if (hostport[0] == '[') { // assume this is IPv6 numeric address auto p = std::ranges::find(hostport, ']'); if (p == std::ranges::end(hostport)) { return {}; } if (p + 1 == std::ranges::end(hostport)) { return {std::string_view{std::ranges::begin(hostport) + 1, p}, {}}; } if (*(p + 1) != ':' || p + 2 == std::ranges::end(hostport)) { return {}; } return {std::string_view{std::ranges::begin(hostport) + 1, p}, std::string_view{p + 2, std::ranges::end(hostport)}}; } auto p = std::ranges::find(hostport, ':'); if (p == std::ranges::begin(hostport)) { return {}; } if (p == std::ranges::end(hostport)) { return {std::string_view{std::ranges::begin(hostport), p}, {}}; } if (p + 1 == std::ranges::end(hostport)) { return {}; } return {std::string_view{std::ranges::begin(hostport), p}, std::string_view{p + 1, std::ranges::end(hostport)}}; } std::mt19937 make_mt19937() { std::random_device rd; return std::mt19937(rd()); } int daemonize(int nochdir, int noclose) { #ifdef __APPLE__ pid_t pid; pid = fork(); if (pid == -1) { return -1; } else if (pid > 0) { _exit(EXIT_SUCCESS); } if (setsid() == -1) { return -1; } pid = fork(); if (pid == -1) { return -1; } else if (pid > 0) { _exit(EXIT_SUCCESS); } if (nochdir == 0) { if (chdir("/") == -1) { return -1; } } if (noclose == 0) { if (freopen("/dev/null", "r", stdin) == nullptr) { return -1; } if (freopen("/dev/null", "w", stdout) == nullptr) { return -1; } if (freopen("/dev/null", "w", stderr) == nullptr) { return -1; } } return 0; #elif defined(__sgi) // TODO nochdir and noclose are ignored. _daemonize is called with // hard-coded zeros to preserve original behavior due to lack of a // test environment. return _daemonize(0, 0, 0, 0); #else // !defined(__APPLE__) && !defined(__sgi) return daemon(nochdir, noclose); #endif // !defined(__APPLE__) && !defined(__sgi) } std::string_view rstrip(BlockAllocator &balloc, const std::string_view &s) { auto it = std::ranges::rbegin(s); for (; it != std::ranges::rend(s) && (*it == ' ' || *it == '\t'); ++it) ; auto len = as_unsigned(it - std::ranges::rbegin(s)); if (len == 0) { return s; } return make_string_ref(balloc, std::string_view{s.data(), s.size() - len}); } void secure_random(uint8_t *dest, size_t destlen) { auto rv = RAND_bytes(dest, static_cast(destlen)); if (rv != 1) { assert(0); abort(); } } #ifdef ENABLE_HTTP3 int msghdr_get_local_addr(Address &dest, msghdr *msg, int family) { switch (family) { case AF_INET: for (auto cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { in_pktinfo pktinfo; memcpy(&pktinfo, CMSG_DATA(cmsg), sizeof(pktinfo)); dest.len = sizeof(dest.su.in); auto &sa = dest.su.in; sa.sin_family = AF_INET; sa.sin_addr = pktinfo.ipi_addr; return 0; } } return -1; case AF_INET6: for (auto cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { in6_pktinfo pktinfo; memcpy(&pktinfo, CMSG_DATA(cmsg), sizeof(pktinfo)); dest.len = sizeof(dest.su.in6); auto &sa = dest.su.in6; sa.sin6_family = AF_INET6; sa.sin6_addr = pktinfo.ipi6_addr; return 0; } } return -1; } return -1; } uint8_t msghdr_get_ecn(msghdr *msg, int family) { switch (family) { case AF_INET: for (auto cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { if (cmsg->cmsg_level == IPPROTO_IP && # ifdef __APPLE__ cmsg->cmsg_type == IP_RECVTOS # else // !defined(__APPLE__) cmsg->cmsg_type == IP_TOS # endif // !defined(__APPLE__) && cmsg->cmsg_len) { return *reinterpret_cast(CMSG_DATA(cmsg)) & IPTOS_ECN_MASK; } } return 0; case AF_INET6: for (auto cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_TCLASS && cmsg->cmsg_len) { unsigned int tos; memcpy(&tos, CMSG_DATA(cmsg), sizeof(tos)); return tos & IPTOS_ECN_MASK; } } return 0; } return 0; } size_t msghdr_get_udp_gro(msghdr *msg) { int gso_size = 0; # ifdef UDP_GRO for (auto cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { if (cmsg->cmsg_level == SOL_UDP && cmsg->cmsg_type == UDP_GRO) { memcpy(&gso_size, CMSG_DATA(cmsg), sizeof(gso_size)); break; } } # endif // defined(UDP_GRO) return static_cast(gso_size); } #endif // defined(ENABLE_HTTP3) } // namespace util } // namespace nghttp2 nghttp2-1.68.0/src/PaxHeaders/h2load_http1_session.h0000644000000000000000000000013215077107270017254 xustar0030 mtime=1761382072.985444176 30 atime=1761382106.219310361 30 ctime=1761382109.216300046 nghttp2-1.68.0/src/h2load_http1_session.h0000644000175100017510000000354415077107270017652 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 British Broadcasting Corporation * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef H2LOAD_HTTP1_SESSION_H #define H2LOAD_HTTP1_SESSION_H #include "h2load_session.h" #include #include "llhttp.h" namespace h2load { struct Client; class Http1Session : public Session { public: Http1Session(Client *client); virtual ~Http1Session(); virtual void on_connect(); virtual int submit_request(); virtual int on_read(const uint8_t *data, size_t len); virtual int on_write(); virtual void terminate(); virtual size_t max_concurrent_streams(); Client *get_client(); int32_t stream_req_counter_; int32_t stream_resp_counter_; private: Client *client_; llhttp_t htp_; bool complete_; }; } // namespace h2load #endif // !defined(H2LOAD_HTTP1_SESSION_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_mruby_module.cc0000644000000000000000000000013115077107270017304 xustar0029 mtime=1761382072.99744412 30 atime=1761382106.182310524 30 ctime=1761382109.178300156 nghttp2-1.68.0/src/shrpx_mruby_module.cc0000644000175100017510000000702515077107270017701 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_mruby_module.h" #include #include #include #include #include #include "shrpx_mruby.h" #include "shrpx_mruby_module_env.h" #include "shrpx_mruby_module_request.h" #include "shrpx_mruby_module_response.h" namespace shrpx { namespace mruby { namespace { mrb_value create_env(mrb_state *mrb) { auto module = mrb_module_get(mrb, "Nghttpx"); auto env_class = mrb_class_get_under(mrb, module, "Env"); auto request_class = mrb_class_get_under(mrb, module, "Request"); auto response_class = mrb_class_get_under(mrb, module, "Response"); auto env = mrb_obj_new(mrb, env_class, 0, nullptr); auto req = mrb_obj_new(mrb, request_class, 0, nullptr); auto resp = mrb_obj_new(mrb, response_class, 0, nullptr); mrb_iv_set(mrb, env, mrb_intern_lit(mrb, "req"), req); mrb_iv_set(mrb, env, mrb_intern_lit(mrb, "resp"), resp); return env; } } // namespace void delete_downstream_from_module(mrb_state *mrb, Downstream *downstream) { auto module = mrb_module_get(mrb, "Nghttpx"); auto env = mrb_obj_iv_get(mrb, reinterpret_cast(module), mrb_intern_lit(mrb, "env")); if (mrb_nil_p(env)) { return; } mrb_iv_remove(mrb, env, intern_ptr(mrb, downstream)); } mrb_value init_module(mrb_state *mrb) { auto module = mrb_define_module(mrb, "Nghttpx"); mrb_define_const(mrb, module, "REQUEST_PHASE", mrb_fixnum_value(PHASE_REQUEST)); mrb_define_const(mrb, module, "RESPONSE_PHASE", mrb_fixnum_value(PHASE_RESPONSE)); init_env_class(mrb, module); init_request_class(mrb, module); init_response_class(mrb, module); return create_env(mrb); } mrb_value create_headers_hash(mrb_state *mrb, const HeaderRefs &headers) { auto hash = mrb_hash_new(mrb); for (auto &hd : headers) { if (hd.name.empty() || hd.name[0] == ':') { continue; } auto ai = mrb_gc_arena_save(mrb); auto key = mrb_str_new(mrb, hd.name.data(), static_cast(hd.name.size())); auto ary = mrb_hash_get(mrb, hash, key); if (mrb_nil_p(ary)) { ary = mrb_ary_new(mrb); mrb_hash_set(mrb, hash, key, ary); } mrb_ary_push( mrb, ary, mrb_str_new(mrb, hd.value.data(), static_cast(hd.value.size()))); mrb_gc_arena_restore(mrb, ai); } return hash; } } // namespace mruby } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/tls.h0000644000000000000000000000013115077107271014022 xustar0030 mtime=1761382073.001444102 29 atime=1761382080.10641148 30 ctime=1761382109.070300468 nghttp2-1.68.0/src/tls.h0000644000175100017510000001106215077107271014413 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef TLS_H #define TLS_H #include "nghttp2_config.h" #include #include #include "ssl_compat.h" using namespace std::literals; #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) namespace nghttp2 { namespace tls { // Recommended general purpose "Intermediate compatibility" cipher // suites for TLSv1.2 by mozilla. // // https://wiki.mozilla.org/Security/Server_Side_TLS inline constexpr auto DEFAULT_CIPHER_LIST = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-" "AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-" "POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-" "AES256-GCM-SHA384"sv; // Recommended general purpose "Modern compatibility" cipher suites // for TLSv1.3 by mozilla. // // https://wiki.mozilla.org/Security/Server_Side_TLS inline constexpr auto DEFAULT_TLS13_CIPHER_LIST = #if defined(NGHTTP2_GENUINE_OPENSSL) || \ defined(NGHTTP2_OPENSSL_IS_LIBRESSL) || defined(NGHTTP2_OPENSSL_IS_WOLFSSL) "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:" "TLS_CHACHA20_POLY1305_SHA256"sv #else // !defined(NGHTTP2_GENUINE_OPENSSL) && // !defined(NGHTTP2_OPENSSL_IS_LIBRESSL) && // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) ""sv #endif // !defined(NGHTTP2_GENUINE_OPENSSL) && // !defined(NGHTTP2_OPENSSL_IS_LIBRESSL) && // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) ; inline constexpr auto NGHTTP2_TLS_MIN_VERSION = TLS1_VERSION; #ifdef TLS1_3_VERSION inline constexpr auto NGHTTP2_TLS_MAX_VERSION = TLS1_3_VERSION; #else // !defined(TLS1_3_VERSION) inline constexpr auto NGHTTP2_TLS_MAX_VERSION = TLS1_2_VERSION; #endif // !defined(TLS1_3_VERSION) std::string_view get_tls_protocol(SSL *ssl); struct TLSSessionInfo { const char *cipher; std::string_view protocol; const uint8_t *session_id; bool session_reused; size_t session_id_length; }; TLSSessionInfo *get_tls_session_info(TLSSessionInfo *tls_info, SSL *ssl); // Returns true iff the negotiated protocol is TLSv1.2. bool check_http2_tls_version(SSL *ssl); // Returns true iff the negotiated cipher suite is in HTTP/2 cipher // block list. bool check_http2_cipher_block_list(SSL *ssl); // Returns true if SSL/TLS requirement for HTTP/2 is fulfilled. // To fulfill the requirement, the following 2 terms must be hold: // // 1. The negotiated protocol must be TLSv1.2. // 2. The negotiated cipher cuite is not listed in the block list // described in RFC 7540. bool check_http2_requirement(SSL *ssl); // Sets TLS min and max versions to |ssl_ctx|. This function returns // 0 if it succeeds, or -1. int ssl_ctx_set_proto_versions(SSL_CTX *ssl_ctx, int min, int max); inline constexpr uint16_t CERTIFICATE_COMPRESSION_ALGO_BROTLI = 2; #if defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && defined(HAVE_LIBBROTLI) int cert_compress(SSL *ssl, CBB *out, const uint8_t *in, size_t in_len); int cert_decompress(SSL *ssl, CRYPTO_BUFFER **out, size_t uncompressed_len, const uint8_t *in, size_t in_len); #endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && // defined(HAVE_LIBBROTLI) // Setup keylog callback. It returns 0 if it succeeds, or -1. int setup_keylog_callback(SSL_CTX *ssl_ctx); } // namespace tls } // namespace nghttp2 #endif // !defined(TLS_H) nghttp2-1.68.0/src/PaxHeaders/util_test.h0000644000000000000000000000013115077107271015234 xustar0030 mtime=1761382073.002444097 29 atime=1761382080.10641148 30 ctime=1761382109.262299913 nghttp2-1.68.0/src/util_test.h0000644000175100017510000000717415077107271015636 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTIL_TEST_H #define UTIL_TEST_H #ifdef HAVE_CONFIG_H # include #endif // defined(HAVE_CONFIG_H) #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" namespace shrpx { extern const MunitSuite util_suite; munit_void_test_decl(test_util_streq) munit_void_test_decl(test_util_strieq) munit_void_test_decl(test_util_tolower) munit_void_test_decl(test_util_to_base64) munit_void_test_decl(test_util_to_token68) munit_void_test_decl(test_util_percent_encode_token) munit_void_test_decl(test_util_percent_decode) munit_void_test_decl(test_util_quote_string) munit_void_test_decl(test_util_utox) munit_void_test_decl(test_util_http_date) munit_void_test_decl(test_util_select_h2) munit_void_test_decl(test_util_ipv6_numeric_addr) munit_void_test_decl(test_util_count_digit) munit_void_test_decl(test_util_utos) munit_void_test_decl(test_util_make_string_ref_uint) munit_void_test_decl(test_util_utos_unit) munit_void_test_decl(test_util_utos_funit) munit_void_test_decl(test_util_parse_uint_with_unit) munit_void_test_decl(test_util_parse_uint) munit_void_test_decl(test_util_parse_duration_with_unit) munit_void_test_decl(test_util_duration_str) munit_void_test_decl(test_util_format_duration) munit_void_test_decl(test_util_starts_with) munit_void_test_decl(test_util_ends_with) munit_void_test_decl(test_util_parse_http_date) munit_void_test_decl(test_util_localtime_date) munit_void_test_decl(test_util_get_uint64) munit_void_test_decl(test_util_parse_config_str_list) munit_void_test_decl(test_util_make_http_hostport) munit_void_test_decl(test_util_make_hostport) munit_void_test_decl(test_util_random_alpha_digit) munit_void_test_decl(test_util_format_hex) munit_void_test_decl(test_util_format_upper_hex) munit_void_test_decl(test_util_is_hex_string) munit_void_test_decl(test_util_decode_hex) munit_void_test_decl(test_util_extract_host) munit_void_test_decl(test_util_split_hostport) munit_void_test_decl(test_util_split_str) munit_void_test_decl(test_util_rstrip) munit_void_test_decl(test_util_contains) munit_void_test_decl(test_util_hex_to_uint) munit_void_test_decl(test_util_is_alpha) munit_void_test_decl(test_util_is_digit) munit_void_test_decl(test_util_is_hex_digit) munit_void_test_decl(test_util_in_rfc3986_unreserved_chars) munit_void_test_decl(test_util_in_rfc3986_sub_delims) munit_void_test_decl(test_util_in_token) munit_void_test_decl(test_util_in_attr_char) munit_void_test_decl(test_util_upcase) munit_void_test_decl(test_util_to_numeric_addr) } // namespace shrpx #endif // !defined(UTIL_TEST_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_downstream.h0000644000000000000000000000013215077107270016627 xustar0030 mtime=1761382072.992444143 30 atime=1761382106.097310898 30 ctime=1761382109.093300401 nghttp2-1.68.0/src/shrpx_downstream.h0000644000175100017510000005426115077107270017227 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_DOWNSTREAM_H #define SHRPX_DOWNSTREAM_H #include "shrpx.h" #include #include #include #include #include #include #include #include #ifdef ENABLE_HTTP3 # include #endif // defined(ENABLE_HTTP3) #include "llhttp.h" #include "shrpx_io_control.h" #include "shrpx_log_config.h" #include "http2.h" #include "memchunk.h" #include "allocator.h" using namespace nghttp2; namespace shrpx { class Upstream; class DownstreamConnection; struct BlockedLink; struct DownstreamAddrGroup; struct DownstreamAddr; class FieldStore { public: FieldStore(BlockAllocator &balloc, size_t headers_initial_capacity) : content_length(-1), balloc_(balloc), buffer_size_(0), header_key_prev_(false), trailer_key_prev_(false) { headers_.reserve(headers_initial_capacity); } const HeaderRefs &headers() const { return headers_; } const HeaderRefs &trailers() const { return trailers_; } HeaderRefs &headers() { return headers_; } HeaderRefs &trailers() { return trailers_; } const void add_extra_buffer_size(size_t n) { buffer_size_ += n; } size_t buffer_size() const { return buffer_size_; } size_t num_fields() const { return headers_.size() + trailers_.size(); } // Returns pointer to the header field with the name |name|. If // multiple header have |name| as name, return last occurrence from // the beginning. If no such header is found, returns nullptr. const HeaderRefs::value_type *header(int32_t token) const; HeaderRefs::value_type *header(int32_t token); // Returns pointer to the header field with the name |name|. If no // such header is found, returns nullptr. const HeaderRefs::value_type *header(const std::string_view &name) const; void add_header_token(const std::string_view &name, const std::string_view &value, bool no_index, int32_t token); // Adds header field name |name|. First, the copy of header field // name pointed by name.c_str() of length name.size() is made, and // stored. void alloc_add_header_name(const std::string_view &name); void append_last_header_key(const std::string_view &data); void append_last_header_value(const std::string_view &data); bool header_key_prev() const { return header_key_prev_; } // Parses content-length, and records it in the field. If there are // multiple Content-Length, returns -1. int parse_content_length(); // Empties headers. void clear_headers(); void add_trailer_token(const std::string_view &name, const std::string_view &value, bool no_index, int32_t token); // Adds trailer field name |name|. First, the copy of trailer field // name pointed by name.c_str() of length name.size() is made, and // stored. void alloc_add_trailer_name(const std::string_view &name); void append_last_trailer_key(const std::string_view &data); void append_last_trailer_value(const std::string_view &data); bool trailer_key_prev() const { return trailer_key_prev_; } // erase_content_length_and_transfer_encoding erases content-length // and transfer-encoding header fields. void erase_content_length_and_transfer_encoding(); // content-length, -1 if it is unknown. int64_t content_length; private: BlockAllocator &balloc_; HeaderRefs headers_; // trailer fields. For HTTP/1.1, trailer fields are only included // with chunked encoding. For HTTP/2, there is no such limit. HeaderRefs trailers_; // Sum of the length of name and value in headers_ and trailers_. // This could also be increased by add_extra_buffer_size() to take // into account for request URI in case of HTTP/1.x request. size_t buffer_size_; bool header_key_prev_; bool trailer_key_prev_; }; // Protocols allowed in HTTP/2 :protocol header field. enum class ConnectProto { NONE, WEBSOCKET, }; struct Request { Request(BlockAllocator &balloc) : fs(balloc, 16), recv_body_length(0), unconsumed_body_length(0), method(-1), http_major(1), http_minor(1), connect_proto(ConnectProto::NONE), upgrade_request(false), http2_upgrade_seen(false), connection_close(false), http2_expect_body(false), no_authority(false), forwarded_once(false) {} void consume(size_t len) { assert(unconsumed_body_length >= len); unconsumed_body_length -= len; } bool regular_connect_method() const { return method == HTTP_CONNECT && connect_proto == ConnectProto::NONE; } bool extended_connect_method() const { return connect_proto != ConnectProto::NONE; } FieldStore fs; // Timestamp when all request header fields are received. std::shared_ptr tstamp; // Request scheme. For HTTP/2, this is :scheme header field value. // For HTTP/1.1, this is deduced from URI or connection. std::string_view scheme; // Request authority. This is HTTP/2 :authority header field value // or host header field value. We may deduce it from absolute-form // HTTP/1 request. We also store authority-form HTTP/1 request. // This could be empty if request comes from HTTP/1.0 without Host // header field and origin-form. std::string_view authority; // Request path, including query component. For HTTP/1.1, this is // request-target. For HTTP/2, this is :path header field value. // For CONNECT request, this is empty. std::string_view path; // This is original authority which cannot be changed by per-pattern // mruby script. std::string_view orig_authority; // This is original path which cannot be changed by per-pattern // mruby script. std::string_view orig_path; // the length of request body received so far int64_t recv_body_length; // The number of bytes not consumed by the application yet. size_t unconsumed_body_length; int method; // HTTP major and minor version int http_major, http_minor; // connect_proto specified in HTTP/2 :protocol pseudo header field // which enables extended CONNECT method. This field is also set if // WebSocket upgrade is requested in h1 frontend for convenience. ConnectProto connect_proto; // Returns true if the request is HTTP upgrade (HTTP Upgrade or // CONNECT method). Upgrade to HTTP/2 is excluded. For HTTP/2 // Upgrade, check get_http2_upgrade_request(). bool upgrade_request; // true if h2c is seen in Upgrade header field. bool http2_upgrade_seen; bool connection_close; // true if this is HTTP/2, and request body is expected. Note that // we don't take into account HTTP method here. bool http2_expect_body; // true if request does not have any information about authority. // This happens when: For HTTP/2 request, :authority is missing. // For HTTP/1 request, origin or asterisk form is used. bool no_authority; // true if backend selection is done for request once. // orig_authority and orig_path have the authority and path which // are used for the first backend selection. bool forwarded_once; }; struct Response { Response(BlockAllocator &balloc) : fs(balloc, 32), recv_body_length(0), unconsumed_body_length(0), http_status(0), http_major(1), http_minor(1), connection_close(false), headers_only(false) {} void consume(size_t len) { assert(unconsumed_body_length >= len); unconsumed_body_length -= len; } // returns true if a resource denoted by scheme, authority, and path // has already been pushed. bool is_resource_pushed(const std::string_view &scheme, const std::string_view &authority, const std::string_view &path) const { if (!pushed_resources) { return false; } return std::ranges::find(*pushed_resources, std::make_tuple(scheme, authority, path)) != std::ranges::end(*pushed_resources); } // remember that a resource denoted by scheme, authority, and path // is pushed. void resource_pushed(const std::string_view &scheme, const std::string_view &authority, const std::string_view &path) { if (!pushed_resources) { pushed_resources = std::make_unique>>(); } pushed_resources->emplace_back(scheme, authority, path); } FieldStore fs; // array of the tuple of scheme, authority, and path of pushed // resource. This is required because RFC 8297 says that server // typically includes header fields appeared in non-final response // header fields in final response header fields. Without checking // that a particular resource has already been pushed, or not, we // end up pushing the same resource at least twice. It is unknown // that we should use more complex data structure (e.g., std::set) // to find the resources faster. std::unique_ptr>> pushed_resources; // the length of response body received so far int64_t recv_body_length; // The number of bytes not consumed by the application yet. This is // mainly for HTTP/2 backend. size_t unconsumed_body_length; // HTTP status code unsigned int http_status; int http_major, http_minor; bool connection_close; // true if response only consists of HEADERS, and it bears // END_STREAM. This is used to tell Http2Upstream that it can send // response with single HEADERS with END_STREAM flag only. bool headers_only; }; enum class DownstreamState { INITIAL, HEADER_COMPLETE, MSG_COMPLETE, STREAM_CLOSED, CONNECT_FAIL, MSG_RESET, // header contains invalid header field. We can safely send error // response (502) to a client. MSG_BAD_HEADER, // header fields in HTTP/1 request exceed the configuration limit. // This state is only transitioned from INITIAL state, and solely // used to signal 431 status code to the client. HTTP1_REQUEST_HEADER_TOO_LARGE, }; enum class DispatchState { NONE, PENDING, BLOCKED, ACTIVE, FAILURE, }; class Downstream { public: Downstream(Upstream *upstream, MemchunkPool *mcpool, int64_t stream_id); ~Downstream(); void reset_upstream(Upstream *upstream); Upstream *get_upstream() const; void set_stream_id(int64_t stream_id); int64_t get_stream_id() const; void set_assoc_stream_id(int64_t stream_id); int64_t get_assoc_stream_id() const; void pause_read(IOCtrlReason reason); int resume_read(IOCtrlReason reason, size_t consumed); void force_resume_read(); // Set stream ID for downstream HTTP2 connection. void set_downstream_stream_id(int64_t stream_id); int64_t get_downstream_stream_id() const; int attach_downstream_connection(std::unique_ptr dconn); void detach_downstream_connection(); DownstreamConnection *get_downstream_connection(); // Returns dconn_ and nullifies dconn_. std::unique_ptr pop_downstream_connection(); // Returns true if output buffer is full. If underlying dconn_ is // NULL, this function always returns false. bool request_buf_full(); // Returns true if upgrade (HTTP Upgrade or CONNECT) is succeeded in // h1 backend. This should not depend on inspect_http1_response(). void check_upgrade_fulfilled_http1(); // Returns true if upgrade (HTTP Upgrade or CONNECT) is succeeded in // h2 backend. void check_upgrade_fulfilled_http2(); // Returns true if the upgrade is succeeded as a result of the call // check_upgrade_fulfilled_http*(). HTTP/2 Upgrade is excluded. bool get_upgraded() const; // Inspects HTTP/2 request. void inspect_http2_request(); // Inspects HTTP/1 request. This checks whether the request is // upgrade request and tranfer-encoding etc. void inspect_http1_request(); // Returns true if the request is HTTP Upgrade for HTTP/2 bool get_http2_upgrade_request() const; // Returns the value of HTTP2-Settings request header field. std::string_view get_http2_settings() const; // downstream request API const Request &request() const { return req_; } Request &request() { return req_; } // Count number of crumbled cookies size_t count_crumble_request_cookie(); // Crumbles (split cookie by ";") in request_headers_ and adds them // in |nva|. Headers::no_index is inherited. void crumble_request_cookie(std::vector &nva); // Assembles request cookies. The opposite operation against // crumble_request_cookie(). std::string_view assemble_request_cookie(); void set_request_start_time(std::chrono::high_resolution_clock::time_point time); const std::chrono::high_resolution_clock::time_point & get_request_start_time() const; int push_request_headers(); bool get_chunked_request() const; void set_chunked_request(bool f); int push_upload_data_chunk(const uint8_t *data, size_t datalen); int end_upload_data(); // Validates that received request body length and content-length // matches. bool validate_request_recv_body_length() const; void set_request_downstream_host(const std::string_view &host); bool expect_response_body() const; bool expect_response_trailer() const; void set_request_state(DownstreamState state); DownstreamState get_request_state() const; DefaultMemchunks *get_request_buf(); void set_request_pending(bool f); bool get_request_pending() const; void set_request_header_sent(bool f); bool get_request_header_sent() const; // Returns true if request is ready to be submitted to downstream. // When sending pending request, get_request_pending() should be // checked too because this function may return true when // get_request_pending() returns false. bool request_submission_ready() const; DefaultMemchunks *get_blocked_request_buf(); bool get_blocked_request_data_eof() const; void set_blocked_request_data_eof(bool f); // downstream response API const Response &response() const { return resp_; } Response &response() { return resp_; } // Rewrites the location response header field. void rewrite_location_response_header(const std::string_view &upstream_scheme); bool get_chunked_response() const; void set_chunked_response(bool f); void set_response_state(DownstreamState state); DownstreamState get_response_state() const; DefaultMemchunks *get_response_buf(); bool response_buf_full(); // Validates that received response body length and content-length // matches. bool validate_response_recv_body_length() const; uint32_t get_response_rst_stream_error_code() const; void set_response_rst_stream_error_code(uint32_t error_code); // Inspects HTTP/1 response. This checks tranfer-encoding etc. void inspect_http1_response(); // Clears some of member variables for response. void reset_response(); // True if the response is non-final (1xx status code). Note that // if connection was upgraded, 101 status code is treated as final. bool get_non_final_response() const; // True if protocol version used by client supports non final // response. Only HTTP/1.1 and HTTP/2 clients support it. bool supports_non_final_response() const; void set_expect_final_response(bool f); bool get_expect_final_response() const; // Call this method when there is incoming data in downstream // connection. int on_read(); void repeat_header_timer(); void stop_header_timer(); // Resets upstream read timer. If it is active, timeout value is // reset. If it is not active, timer will be started. void reset_upstream_rtimer(); // Resets upstream write timer. If it is active, timeout value is // reset. If it is not active, timer will be started. This // function also resets read timer if it has been started. void reset_upstream_wtimer(); // Makes sure that upstream write timer is started. If it has been // started, do nothing. Otherwise, write timer will be started. void ensure_upstream_wtimer(); // Disables upstream read timer. void disable_upstream_rtimer(); // Disables upstream write timer. void disable_upstream_wtimer(); // Downstream timer functions. They works in a similar way just // like the upstream timer function. void reset_downstream_rtimer(); void reset_downstream_wtimer(); void ensure_downstream_wtimer(); void disable_downstream_rtimer(); void disable_downstream_wtimer(); // Returns true if accesslog can be written for this downstream. bool accesslog_ready() const; // Increment retry count void add_retry(); // true if retry attempt should not be done. bool no_more_retry() const; DispatchState get_dispatch_state() const; void set_dispatch_state(DispatchState s); void attach_blocked_link(BlockedLink *l); BlockedLink *detach_blocked_link(); // Returns true if downstream_connection can be detached and reused. bool can_detach_downstream_connection() const; DefaultMemchunks pop_response_buf(); BlockAllocator &get_block_allocator(); void add_rcbuf(nghttp2_rcbuf *rcbuf); #ifdef ENABLE_HTTP3 void add_rcbuf(nghttp3_rcbuf *rcbuf); #endif // defined(ENABLE_HTTP3) void set_downstream_addr_group(const std::shared_ptr &group); void set_addr(const DownstreamAddr *addr); const DownstreamAddr *get_addr() const; void set_accesslog_written(bool f); // Finds affinity cookie from request header fields. The name of // cookie is given in |name|. If an affinity cookie is found, it is // assigned to a member function, and is returned. If it is not // found, or is malformed, returns 0. uint32_t find_affinity_cookie(const std::string_view &name); // Set |h| as affinity cookie. void renew_affinity_cookie(uint32_t h); // Returns affinity cookie to send. If it does not need to be sent, // for example, because the value is retrieved from a request header // field, returns 0. uint32_t get_affinity_cookie_to_send() const; void set_ws_key(const std::string_view &key); bool get_expect_100_continue() const; bool get_stop_reading() const; void set_stop_reading(bool f); enum { EVENT_ERROR = 0x1, EVENT_TIMEOUT = 0x2, }; Downstream *dlnext, *dlprev; // the length of response body sent to upstream client int64_t response_sent_body_length; private: BlockAllocator balloc_; std::vector rcbufs_; #ifdef ENABLE_HTTP3 std::vector rcbufs3_; #endif // defined(ENABLE_HTTP3) Request req_; Response resp_; std::chrono::high_resolution_clock::time_point request_start_time_; // host we requested to downstream. This is used to rewrite // location header field to decide the location should be rewritten // or not. std::string_view request_downstream_host_; // Data arrived in frontend before sending header fields to backend // are stored in this buffer. DefaultMemchunks blocked_request_buf_; DefaultMemchunks request_buf_; DefaultMemchunks response_buf_; // The Sec-WebSocket-Key field sent to the peer. This field is used // if frontend uses RFC 8441 WebSocket bootstrapping via HTTP/2. std::string_view ws_key_; ev_timer header_timer_; ev_timer upstream_rtimer_; ev_timer upstream_wtimer_; ev_timer downstream_rtimer_; ev_timer downstream_wtimer_; Upstream *upstream_; std::unique_ptr dconn_; // only used by HTTP/2 upstream BlockedLink *blocked_link_; // The backend address used to fulfill this request. These are for // logging purpose. std::shared_ptr group_; const DownstreamAddr *addr_; // How many times we tried in backend connection size_t num_retry_; // The stream ID in frontend connection int64_t stream_id_; // The associated stream ID in frontend connection if this is pushed // stream. int64_t assoc_stream_id_; // stream ID in backend connection int64_t downstream_stream_id_; // RST_STREAM error_code from downstream HTTP2 connection uint32_t response_rst_stream_error_code_; // An affinity cookie value. uint32_t affinity_cookie_; // request state DownstreamState request_state_; // response state DownstreamState response_state_; // only used by HTTP/2 upstream DispatchState dispatch_state_; // true if the connection is upgraded (HTTP Upgrade or CONNECT), // excluding upgrade to HTTP/2. bool upgraded_; // true if backend request uses chunked transfer-encoding bool chunked_request_; // true if response to client uses chunked transfer-encoding bool chunked_response_; // true if we have not got final response code bool expect_final_response_; // true if downstream request is pending because backend connection // has not been established or should be checked before use; // currently used only with HTTP/2 connection. bool request_pending_; // true if downstream request header is considered to be sent. bool request_header_sent_; // true if access.log has been written. bool accesslog_written_; // true if affinity cookie is generated for this request. bool new_affinity_cookie_; // true if eof is received from client before sending header fields // to backend. bool blocked_request_data_eof_; // true if request contains "expect: 100-continue" header field. bool expect_100_continue_; bool stop_reading_; }; } // namespace shrpx #endif // !defined(SHRPX_DOWNSTREAM_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_downstream_queue.h0000644000000000000000000000013215077107270020033 xustar0030 mtime=1761382072.993444139 30 atime=1761382106.110310841 30 ctime=1761382109.106300364 nghttp2-1.68.0/src/shrpx_downstream_queue.h0000644000175100017510000001027615077107270020431 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_DOWNSTREAM_QUEUE_H #define SHRPX_DOWNSTREAM_QUEUE_H #include "shrpx.h" #include #include #include #include "template.h" using namespace nghttp2; namespace shrpx { class Downstream; // Link entry in HostEntry.blocked and downstream because downstream // could be deleted in anytime and we'd like to find Downstream in // O(1). Downstream has field to link back to this object. struct BlockedLink { Downstream *downstream; BlockedLink *dlnext, *dlprev; }; class DownstreamQueue { public: struct HostEntry { HostEntry(ImmutableString &&key); HostEntry(HostEntry &&) = default; HostEntry &operator=(HostEntry &&) = default; HostEntry(const HostEntry &) = delete; HostEntry &operator=(const HostEntry &) = delete; // Key that associates this object ImmutableString key; // Set of stream ID that blocked by conn_max_per_host_. DList blocked; // The number of connections currently made to this host. size_t num_active; }; using HostEntryMap = std::unordered_map; // conn_max_per_host == 0 means no limit for downstream connection. DownstreamQueue(size_t conn_max_per_host = 0, bool unified_host = true); ~DownstreamQueue(); // Add |downstream| to this queue. This is entry point for // Downstream object. void add_pending(std::unique_ptr downstream); // Set |downstream| to failure state, which means that downstream // failed to connect to backend. void mark_failure(Downstream *downstream); // Set |downstream| to active state, which means that downstream // connection has started. void mark_active(Downstream *downstream); // Set |downstream| to blocked state, which means that download // connection was blocked because conn_max_per_host_ limit. void mark_blocked(Downstream *downstream); // Returns true if we can make downstream connection to given // |host|. bool can_activate(const std::string_view &host) const; // Removes and frees |downstream| object. If |downstream| is in // DispatchState::ACTIVE, and |next_blocked| is true, this function // may return Downstream object with the same target host in // DispatchState::BLOCKED if its connection is now not blocked by // conn_max_per_host_ limit. Downstream *remove_and_get_blocked(Downstream *downstream, bool next_blocked = true); Downstream *get_downstreams() const; HostEntry &find_host_entry(const std::string_view &host); std::string_view make_host_key(const std::string_view &host) const; std::string_view make_host_key(Downstream *downstream) const; private: // Per target host structure to keep track of the number of // connections to the same host. HostEntryMap host_entries_; DList downstreams_; // Maximum number of concurrent connections to the same host. size_t conn_max_per_host_; // true if downstream host is treated as the same. Used for reverse // proxying. bool unified_host_; }; } // namespace shrpx #endif // !defined(SHRPX_DOWNSTREAM_QUEUE_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_dns_tracker.cc0000644000000000000000000000013215077107270017101 xustar0030 mtime=1761382072.992444143 30 atime=1761382106.169310581 30 ctime=1761382109.165300193 nghttp2-1.68.0/src/shrpx_dns_tracker.cc0000644000175100017510000002171715077107270017501 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_dns_tracker.h" #include "shrpx_config.h" #include "shrpx_log.h" #include "util.h" namespace shrpx { namespace { void gccb(struct ev_loop *loop, ev_timer *w, int revents) { auto dns_tracker = static_cast(w->data); dns_tracker->gc(); } } // namespace DNSTracker::DNSTracker(struct ev_loop *loop, int family) : loop_(loop), family_(family) { ev_timer_init(&gc_timer_, gccb, 0., 12_h); gc_timer_.data = this; } DNSTracker::~DNSTracker() { ev_timer_stop(loop_, &gc_timer_); for (auto &p : ents_) { auto &qlist = p.second.qlist; while (!qlist.empty()) { auto head = qlist.head; qlist.remove(head); head->status = DNSResolverStatus::ERROR; head->in_qlist = false; // TODO Not sure we should call callback here, or it is even be // safe to do that. } } } ResolverEntry DNSTracker::make_entry(std::unique_ptr resolv, ImmutableString host, DNSResolverStatus status, const Address *result) { auto &dnsconf = get_config()->dns; auto ent = ResolverEntry{ .host = std::move(host), .resolv = std::move(resolv), .status = status, }; switch (status) { case DNSResolverStatus::ERROR: case DNSResolverStatus::OK: ent.expiry = std::chrono::steady_clock::now() + util::duration_from(dnsconf.timeout.cache); break; default: break; } if (result) { ent.result = *result; } return ent; } void DNSTracker::update_entry(ResolverEntry &ent, std::unique_ptr resolv, DNSResolverStatus status, const Address *result) { auto &dnsconf = get_config()->dns; ent.resolv = std::move(resolv); ent.status = status; switch (status) { case DNSResolverStatus::ERROR: case DNSResolverStatus::OK: ent.expiry = std::chrono::steady_clock::now() + util::duration_from(dnsconf.timeout.cache); break; default: break; } if (result) { ent.result = *result; } } DNSResolverStatus DNSTracker::resolve(Address *result, DNSQuery *dnsq) { int rv; auto it = ents_.find(dnsq->host); if (it == std::ranges::end(ents_)) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "DNS entry not found for " << dnsq->host; } auto resolv = std::make_unique(loop_, family_); auto host_copy = ImmutableString{dnsq->host}; auto host = as_string_view(host_copy); rv = resolv->resolve(host); if (rv != 0) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "Name lookup failed for " << host; } ents_.emplace(host, make_entry(nullptr, std::move(host_copy), DNSResolverStatus::ERROR, nullptr)); start_gc_timer(); return DNSResolverStatus::ERROR; } switch (resolv->get_status(result)) { case DNSResolverStatus::ERROR: if (LOG_ENABLED(INFO)) { LOG(INFO) << "Name lookup failed for " << host; } ents_.emplace(host, make_entry(nullptr, std::move(host_copy), DNSResolverStatus::ERROR, nullptr)); start_gc_timer(); return DNSResolverStatus::ERROR; case DNSResolverStatus::OK: if (LOG_ENABLED(INFO)) { LOG(INFO) << "Name lookup succeeded: " << host << " -> " << util::numeric_name(&result->su.sa, result->len); } ents_.emplace(host, make_entry(nullptr, std::move(host_copy), DNSResolverStatus::OK, result)); start_gc_timer(); return DNSResolverStatus::OK; case DNSResolverStatus::RUNNING: { auto p = ents_.emplace(host, make_entry(std::move(resolv), std::move(host_copy), DNSResolverStatus::RUNNING, nullptr)); start_gc_timer(); auto &ent = (*p.first).second; add_to_qlist(ent, dnsq); return DNSResolverStatus::RUNNING; } default: assert(0); } } auto &ent = (*it).second; if (ent.status != DNSResolverStatus::RUNNING && ent.expiry < std::chrono::steady_clock::now()) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "DNS entry found for " << dnsq->host << ", but it has been expired"; } auto resolv = std::make_unique(loop_, family_); auto host = as_string_view(ent.host); rv = resolv->resolve(host); if (rv != 0) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "Name lookup failed for " << host; } update_entry(ent, nullptr, DNSResolverStatus::ERROR, nullptr); return DNSResolverStatus::ERROR; } switch (resolv->get_status(result)) { case DNSResolverStatus::ERROR: if (LOG_ENABLED(INFO)) { LOG(INFO) << "Name lookup failed for " << host; } update_entry(ent, nullptr, DNSResolverStatus::ERROR, nullptr); return DNSResolverStatus::ERROR; case DNSResolverStatus::OK: if (LOG_ENABLED(INFO)) { LOG(INFO) << "Name lookup succeeded: " << host << " -> " << util::numeric_name(&result->su.sa, result->len); } update_entry(ent, nullptr, DNSResolverStatus::OK, result); return DNSResolverStatus::OK; case DNSResolverStatus::RUNNING: update_entry(ent, std::move(resolv), DNSResolverStatus::RUNNING, nullptr); add_to_qlist(ent, dnsq); return DNSResolverStatus::RUNNING; default: assert(0); } } switch (ent.status) { case DNSResolverStatus::RUNNING: if (LOG_ENABLED(INFO)) { LOG(INFO) << "Waiting for name lookup complete for " << dnsq->host; } ent.qlist.append(dnsq); dnsq->in_qlist = true; return DNSResolverStatus::RUNNING; case DNSResolverStatus::ERROR: if (LOG_ENABLED(INFO)) { LOG(INFO) << "Name lookup failed for " << dnsq->host << " (cached)"; } return DNSResolverStatus::ERROR; case DNSResolverStatus::OK: if (LOG_ENABLED(INFO)) { LOG(INFO) << "Name lookup succeeded (cached): " << dnsq->host << " -> " << util::numeric_name(&ent.result.su.sa, ent.result.len); } if (result) { memcpy(result, &ent.result, sizeof(*result)); } return DNSResolverStatus::OK; default: assert(0); abort(); } } void DNSTracker::add_to_qlist(ResolverEntry &ent, DNSQuery *dnsq) { ent.resolv->set_complete_cb( [&ent](DNSResolverStatus status, const Address *result) { auto &qlist = ent.qlist; while (!qlist.empty()) { auto head = qlist.head; qlist.remove(head); head->status = status; head->in_qlist = false; auto cb = head->cb; cb(status, result); } auto &dnsconf = get_config()->dns; ent.resolv.reset(); ent.status = status; ent.expiry = std::chrono::steady_clock::now() + util::duration_from(dnsconf.timeout.cache); if (ent.status == DNSResolverStatus::OK) { ent.result = *result; } }); ent.qlist.append(dnsq); dnsq->in_qlist = true; } void DNSTracker::cancel(DNSQuery *dnsq) { if (!dnsq->in_qlist) { return; } auto it = ents_.find(dnsq->host); if (it == std::ranges::end(ents_)) { return; } auto &ent = (*it).second; ent.qlist.remove(dnsq); dnsq->in_qlist = false; } void DNSTracker::start_gc_timer() { if (ev_is_active(&gc_timer_)) { return; } ev_timer_again(loop_, &gc_timer_); } void DNSTracker::gc() { if (LOG_ENABLED(INFO)) { LOG(INFO) << "Starting removing expired DNS cache entries"; } auto now = std::chrono::steady_clock::now(); for (auto it = std::ranges::begin(ents_); it != std::ranges::end(ents_);) { auto &ent = (*it).second; if (ent.expiry >= now) { ++it; continue; } it = ents_.erase(it); } if (ents_.empty()) { ev_timer_stop(loop_, &gc_timer_); } } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_http_test.cc0000644000000000000000000000013215077107270016620 xustar0030 mtime=1761382072.995444129 30 atime=1761382106.256310198 30 ctime=1761382109.253299939 nghttp2-1.68.0/src/shrpx_http_test.cc0000644000175100017510000001336515077107270017220 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_http_test.h" #ifdef HAVE_UNISTD_H # include #endif // defined(HAVE_UNISTD_H) #include #include "munitxx.h" #include "shrpx_http.h" #include "shrpx_config.h" #include "shrpx_log.h" using namespace std::literals; namespace shrpx { namespace { const MunitTest tests[]{ munit_void_test(test_shrpx_http_create_forwarded), munit_void_test(test_shrpx_http_create_via_header_value), munit_void_test(test_shrpx_http_create_affinity_cookie), munit_void_test(test_shrpx_http_create_altsvc_header_value), munit_void_test(test_shrpx_http_check_http_scheme), munit_test_end(), }; } // namespace const MunitSuite http_suite{ "/http", tests, nullptr, 1, MUNIT_SUITE_OPTION_NONE, }; void test_shrpx_http_create_forwarded(void) { BlockAllocator balloc(1024, 1024); assert_stdsv_equal( "by=\"example.com:3000\";for=\"[::1]\";host=\"www.example.com\";" "proto=https"sv, http::create_forwarded( balloc, FORWARDED_BY | FORWARDED_FOR | FORWARDED_HOST | FORWARDED_PROTO, "example.com:3000"sv, "[::1]"sv, "www.example.com"sv, "https"sv)); assert_stdsv_equal("for=192.168.0.1"sv, http::create_forwarded(balloc, FORWARDED_FOR, "alpha"sv, "192.168.0.1"sv, "bravo"sv, "charlie"sv)); assert_stdsv_equal( "by=_hidden;for=\"[::1]\""sv, http::create_forwarded(balloc, FORWARDED_BY | FORWARDED_FOR, "_hidden"sv, "[::1]"sv, ""sv, ""sv)); assert_stdsv_equal( "by=\"[::1]\";for=_hidden"sv, http::create_forwarded(balloc, FORWARDED_BY | FORWARDED_FOR, "[::1]"sv, "_hidden"sv, ""sv, ""sv)); assert_stdsv_equal(""sv, http::create_forwarded(balloc, FORWARDED_BY | FORWARDED_FOR | FORWARDED_HOST | FORWARDED_PROTO, ""sv, ""sv, ""sv, ""sv)); } void test_shrpx_http_create_via_header_value(void) { std::array buf; auto end = http::create_via_header_value(std::ranges::begin(buf), 1, 1); assert_stdstring_equal("1.1 nghttpx", (std::string{std::ranges::begin(buf), end})); std::ranges::fill(buf, '\0'); end = http::create_via_header_value(std::ranges::begin(buf), 2, 0); assert_stdstring_equal("2 nghttpx", (std::string{std::ranges::begin(buf), end})); } void test_shrpx_http_create_affinity_cookie(void) { BlockAllocator balloc(1024, 1024); std::string_view c; c = http::create_affinity_cookie(balloc, "cookie-val"sv, 0xf1e2d3c4u, ""sv, false); assert_stdsv_equal("cookie-val=f1e2d3c4"sv, c); c = http::create_affinity_cookie(balloc, "alpha"sv, 0x00000000u, ""sv, true); assert_stdsv_equal("alpha=00000000; Secure"sv, c); c = http::create_affinity_cookie(balloc, "bravo"sv, 0x01111111u, "bar"sv, false); assert_stdsv_equal("bravo=01111111; Path=bar"sv, c); c = http::create_affinity_cookie(balloc, "charlie"sv, 0x01111111u, "bar"sv, true); assert_stdsv_equal("charlie=01111111; Path=bar; Secure"sv, c); } void test_shrpx_http_create_altsvc_header_value(void) { { BlockAllocator balloc(1024, 1024); std::vector altsvcs{ AltSvc{ .protocol_id = "h3"sv, .host = "127.0.0.1"sv, .service = "443"sv, .params = "ma=3600"sv, }, }; assert_stdsv_equal(R"(h3="127.0.0.1:443"; ma=3600)"sv, http::create_altsvc_header_value(balloc, altsvcs)); } { BlockAllocator balloc(1024, 1024); std::vector altsvcs{ AltSvc{ .protocol_id = "h3"sv, .service = "443"sv, .params = "ma=3600"sv, }, AltSvc{ .protocol_id = "h3%"sv, .host = "\"foo\""sv, .service = "4433"sv, }, }; assert_stdsv_equal(R"(h3=":443"; ma=3600, h3%25="\"foo\":4433")"sv, http::create_altsvc_header_value(balloc, altsvcs)); } } void test_shrpx_http_check_http_scheme(void) { assert_true(http::check_http_scheme("https"sv, true)); assert_false(http::check_http_scheme("https"sv, false)); assert_false(http::check_http_scheme("http"sv, true)); assert_true(http::check_http_scheme("http"sv, false)); assert_false(http::check_http_scheme("foo"sv, true)); assert_false(http::check_http_scheme("foo"sv, false)); assert_false(http::check_http_scheme(""sv, true)); assert_false(http::check_http_scheme(""sv, false)); } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/test.example.com.pem0000644000000000000000000000013115077107271016740 xustar0030 mtime=1761382073.001444102 29 atime=1761382080.10641148 30 ctime=1761382109.281299858 nghttp2-1.68.0/src/test.example.com.pem0000644000175100017510000000252315077107271017333 0ustar00runnerrunner-----BEGIN CERTIFICATE----- MIIDwTCCAqmgAwIBAgIUDhKNhGRUq1TSHD6aG2k4TRR8iA0wDQYJKoZIhvcNAQEL BQAwXjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoT GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAxMOY2EubmdodHRwMi5v cmcwHhcNMTYwNjI1MDkzNzAwWhcNMjYwNjIzMDkzNzAwWjBgMQswCQYDVQQGEwJB VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0 cyBQdHkgTHRkMRkwFwYDVQQDExB0ZXN0LmV4YW1wbGUuY29tMIIBIjANBgkqhkiG 9w0BAQEFAAOCAQ8AMIIBCgKCAQEArf2UBsEh/xwd/4WZfVFf5sMyWcns/1idF2Fr oLDwqVUYRlxpU/KbrIG8X8v3w4cVP/xOXd1y9Q+W9OLK2YScAeHeE97mHZXbcpow UxvTDv/GNIHHXK/yvYM8R2EEnZR71qXdoXsCakv/aG2ewkkvA108eEbk0u7RxNmI sYsO0Y4iBtwBM/MSkAYPfWdrdHhY9z0l4M8GAyUOuZc0t6j0zw3fzkjqmVgGEJvc VzvSgZIzMLqSzvC89viXtj1pyzQjMLgmuDbs0l47uRHpZXgMGVyF3UuQipPzvhO7 G/ZbX/4kUU5bPabdtvPbjju7dE5PfGrKOThM6HS93Y7QvYTUtwIDAQABo3UwczAO BgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIw ADAdBgNVHQ4EFgQUm8jn1FICope9qUce6ORQ0CtbmhYwHwYDVR0jBBgwFoAU0DnF VHVlxrFEkv1ULqnO4ua924YwDQYJKoZIhvcNAQELBQADggEBAD7RPz/5rAnS1MNP JfAj1TXZSBwlYgtmJL65yaFB6a1SNSTo15deAm/1Vl10LbmYdV4sVnGKeZjhKNk+ bvVzetUSUS7Rh1fHtxlivJFkG1VrvPu9b416l2aKftBiaNyAWXbyjqXwLYli6Ehk uu6jZd0040Ggh7bY+KMSnDFDrp7Rar7OvGu9Iovs+sPdkc/iEbvwEiXdMjf3gwkT Wqx6br1VDLzhD83HAsFA9tt5fv6KTf91UgJnCmOi81Uo6fSEJG84g32T25gwwmCK q4U049aGF/f4u3QuWDsfYqNePycurAg3m5PC0wCoqvpY2u/q+PGbjWMi2PfZsF8U imgl/L0= -----END CERTIFICATE----- nghttp2-1.68.0/src/PaxHeaders/http2_test.cc0000644000000000000000000000013215077107270015456 xustar0030 mtime=1761382072.987444166 30 atime=1761382106.261310176 30 ctime=1761382109.258299924 nghttp2-1.68.0/src/http2_test.cc0000644000175100017510000012061415077107270016052 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "http2_test.h" #include #include #include #include "munitxx.h" #include "urlparse.h" #include "http2.h" #include "util.h" using namespace nghttp2; using namespace std::literals; #define MAKE_NV(K, V) \ { \ (uint8_t *)K, (uint8_t *)V, sizeof(K) - 1, \ sizeof(V) - 1, NGHTTP2_NV_FLAG_NONE, \ } namespace shrpx { namespace { const MunitTest tests[]{ munit_void_test(test_http2_add_header), munit_void_test(test_http2_get_header), munit_void_test(test_http2_copy_headers_to_nva), munit_void_test(test_http2_build_http1_headers_from_headers), munit_void_test(test_http2_rewrite_location_uri), munit_void_test(test_http2_parse_http_status_code), munit_void_test(test_http2_index_header), munit_void_test(test_http2_lookup_token), munit_void_test(test_http2_parse_link_header), munit_void_test(test_http2_path_join), munit_void_test(test_http2_normalize_path), munit_void_test(test_http2_rewrite_clean_path), munit_void_test(test_http2_get_pure_path_component), munit_void_test(test_http2_construct_push_component), munit_void_test(test_http2_contains_trailers), munit_void_test(test_http2_check_transfer_encoding), munit_void_test(test_http2_capitalize), munit_test_end(), }; } // namespace const MunitSuite http2_suite{ "/http2", tests, nullptr, 1, MUNIT_SUITE_OPTION_NONE, }; namespace { void check_nv(const HeaderRef &a, const nghttp2_nv *b) { assert_size(a.name.size(), ==, b->namelen); assert_size(a.value.size(), ==, b->valuelen); assert_memory_equal(b->namelen, a.name.data(), b->name); assert_memory_equal(b->valuelen, a.value.data(), b->value); } } // namespace void test_http2_add_header(void) { auto nva = Headers(); http2::add_header(nva, "alpha"sv, "123"sv, false, -1); assert_true(Headers::value_type("alpha", "123") == nva[0]); assert_false(nva[0].no_index); nva.clear(); http2::add_header(nva, "alpha"sv, ""sv, true, -1); assert_true(Headers::value_type("alpha", "") == nva[0]); assert_true(nva[0].no_index); nva.clear(); http2::add_header(nva, "a"sv, "b"sv, false, -1); assert_true(Headers::value_type("a", "b") == nva[0]); nva.clear(); http2::add_header(nva, "te"sv, "trailers"sv, false, http2::HD_TE); assert_int32(http2::HD_TE, ==, nva[0].token); } void test_http2_get_header(void) { auto nva = Headers{{"alpha", "1"}, {"bravo", "2"}, {"bravo", "3"}, {"charlie", "4"}, {"delta", "5"}, {"echo", "6"}, {"content-length", "7"}}; const Headers::value_type *rv; rv = http2::get_header(nva, "delta"sv); assert_not_null(rv); assert_stdstring_equal("delta", rv->name); rv = http2::get_header(nva, "bravo"sv); assert_not_null(rv); assert_stdstring_equal("bravo", rv->name); rv = http2::get_header(nva, "foxtrot"sv); assert_null(rv); } namespace { auto headers = HeaderRefs{{"alpha"sv, "0"sv, true}, {"bravo"sv, "1"sv}, {"connection"sv, "2"sv, false, http2::HD_CONNECTION}, {"connection"sv, "3"sv, false, http2::HD_CONNECTION}, {"delta"sv, "4"sv}, {"expect"sv, "5"sv}, {"foxtrot"sv, "6"sv}, {"tango"sv, "7"sv}, {"te"sv, "8"sv, false, http2::HD_TE}, {"te"sv, "9"sv, false, http2::HD_TE}, {"x-forwarded-proto"sv, "10"sv, false, http2::HD_X_FORWARDED_FOR}, {"x-forwarded-proto"sv, "11"sv, false, http2::HD_X_FORWARDED_FOR}, {"zulu"sv, "12"sv}}; } // namespace namespace { auto headers2 = HeaderRefs{ {"x-forwarded-for"sv, "xff1"sv, false, http2::HD_X_FORWARDED_FOR}, {"x-forwarded-for"sv, "xff2"sv, false, http2::HD_X_FORWARDED_FOR}, {"x-forwarded-proto"sv, "xfp1"sv, false, http2::HD_X_FORWARDED_PROTO}, {"x-forwarded-proto"sv, "xfp2"sv, false, http2::HD_X_FORWARDED_PROTO}, {"forwarded"sv, "fwd1"sv, false, http2::HD_FORWARDED}, {"forwarded"sv, "fwd2"sv, false, http2::HD_FORWARDED}, {"via"sv, "via1"sv, false, http2::HD_VIA}, {"via"sv, "via2"sv, false, http2::HD_VIA}, }; } // namespace void test_http2_copy_headers_to_nva(void) { auto ans = std::vector{0, 1, 4, 5, 6, 7, 12}; std::vector nva; http2::copy_headers_to_nva_nocopy(nva, headers, http2::HDOP_STRIP_X_FORWARDED_FOR); assert_size(7, ==, nva.size()); for (size_t i = 0; i < ans.size(); ++i) { check_nv(headers[ans[i]], &nva[i]); if (ans[i] == 0) { assert_uint8((NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE | NGHTTP2_NV_FLAG_NO_INDEX), ==, nva[i].flags); } else { assert_uint8( (NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE), ==, nva[i].flags); } } nva.clear(); http2::copy_headers_to_nva(nva, headers, http2::HDOP_STRIP_X_FORWARDED_FOR); assert_size(7, ==, nva.size()); for (size_t i = 0; i < ans.size(); ++i) { check_nv(headers[ans[i]], &nva[i]); if (ans[i] == 0) { assert_true(nva[i].flags & NGHTTP2_NV_FLAG_NO_INDEX); } else { assert_false(nva[i].flags); } } nva.clear(); auto ans2 = std::vector{0, 2, 4, 6}; http2::copy_headers_to_nva(nva, headers2, http2::HDOP_NONE); assert_size(ans2.size(), ==, nva.size()); for (size_t i = 0; i < ans2.size(); ++i) { check_nv(headers2[ans2[i]], &nva[i]); } nva.clear(); http2::copy_headers_to_nva(nva, headers2, http2::HDOP_STRIP_ALL); assert_true(nva.empty()); } void test_http2_build_http1_headers_from_headers(void) { MemchunkPool pool; DefaultMemchunks buf(&pool); http2::build_http1_headers_from_headers(&buf, headers, http2::HDOP_STRIP_X_FORWARDED_FOR); auto hdrs = std::string(buf.head->pos, buf.head->last); assert_stdstring_equal("Alpha: 0\r\n" "Bravo: 1\r\n" "Delta: 4\r\n" "Expect: 5\r\n" "Foxtrot: 6\r\n" "Tango: 7\r\n" "Te: 8\r\n" "Te: 9\r\n" "Zulu: 12\r\n", hdrs); buf.reset(); http2::build_http1_headers_from_headers(&buf, headers2, http2::HDOP_NONE); hdrs = std::string(buf.head->pos, buf.head->last); assert_stdstring_equal("X-Forwarded-For: xff1\r\n" "X-Forwarded-Proto: xfp1\r\n" "Forwarded: fwd1\r\n" "Via: via1\r\n", hdrs); buf.reset(); http2::build_http1_headers_from_headers(&buf, headers2, http2::HDOP_STRIP_ALL); assert_size(0, ==, buf.rleft()); } namespace { void check_rewrite_location_uri(const std::string &want, const std::string &uri, const std::string &match_host, const std::string &req_authority, const std::string &upstream_scheme) { BlockAllocator balloc(4096, 4096); urlparse_url u; assert_int(0, ==, urlparse_parse_url(uri.c_str(), uri.size(), 0, &u)); auto got = http2::rewrite_location_uri(balloc, uri, u, match_host, req_authority, upstream_scheme); assert_stdsv_equal(want, got); } } // namespace void test_http2_rewrite_location_uri(void) { check_rewrite_location_uri("https://localhost:3000/alpha?bravo#charlie", "http://localhost:3001/alpha?bravo#charlie", "localhost:3001", "localhost:3000", "https"); check_rewrite_location_uri("https://localhost/", "http://localhost:3001/", "localhost", "localhost", "https"); check_rewrite_location_uri("http://localhost/", "http://localhost:3001/", "localhost", "localhost", "http"); check_rewrite_location_uri("http://localhost:443/", "http://localhost:3001/", "localhost", "localhost:443", "http"); check_rewrite_location_uri("https://localhost:80/", "http://localhost:3001/", "localhost", "localhost:80", "https"); check_rewrite_location_uri("", "http://localhost:3001/", "127.0.0.1", "127.0.0.1", "https"); check_rewrite_location_uri("https://localhost:3000/", "http://localhost:3001/", "localhost", "localhost:3000", "https"); check_rewrite_location_uri("https://localhost:3000/", "http://localhost/", "localhost", "localhost:3000", "https"); // match_host != req_authority check_rewrite_location_uri("https://example.org", "http://127.0.0.1:8080", "127.0.0.1", "example.org", "https"); check_rewrite_location_uri("", "http://example.org", "127.0.0.1", "example.org", "https"); } void test_http2_parse_http_status_code(void) { assert_int(200, ==, http2::parse_http_status_code("200"sv)); assert_int(102, ==, http2::parse_http_status_code("102"sv)); assert_int(-1, ==, http2::parse_http_status_code("099"sv)); assert_int(-1, ==, http2::parse_http_status_code("99"sv)); assert_int(-1, ==, http2::parse_http_status_code("-1"sv)); assert_int(-1, ==, http2::parse_http_status_code("20a"sv)); assert_int(-1, ==, http2::parse_http_status_code(""sv)); } void test_http2_index_header(void) { http2::HeaderIndex hdidx; http2::init_hdidx(hdidx); http2::index_header(hdidx, http2::HD__AUTHORITY, 0); http2::index_header(hdidx, -1, 1); assert_int16(0, ==, hdidx[http2::HD__AUTHORITY]); } void test_http2_lookup_token(void) { assert_int(http2::HD__AUTHORITY, ==, http2::lookup_token(":authority"sv)); assert_int(-1, ==, http2::lookup_token(":authorit"sv)); assert_int(-1, ==, http2::lookup_token(":Authority"sv)); assert_int(http2::HD_EXPECT, ==, http2::lookup_token("expect"sv)); } void test_http2_parse_link_header(void) { { // only URI appears; we don't extract URI unless it bears rel=preload auto res = http2::parse_link_header(""sv); assert_size(0, ==, res.size()); } { // URI url should be extracted auto res = http2::parse_link_header("; rel=preload"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // With extra link-param. URI url should be extracted auto res = http2::parse_link_header("; rel=preload; as=file"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // With extra link-param. URI url should be extracted auto res = http2::parse_link_header("; as=file; rel=preload"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // With extra link-param and quote-string. URI url should be // extracted auto res = http2::parse_link_header(R"(; rel=preload; title="foo,bar")"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // With extra link-param and quote-string. URI url should be // extracted auto res = http2::parse_link_header(R"(; title="foo,bar"; rel=preload)"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // ',' after quote-string auto res = http2::parse_link_header( R"(; title="foo,bar", ; rel=preload)"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url2"sv, res[0].uri); } { // Only first URI should be extracted. auto res = http2::parse_link_header("; rel=preload, "sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // Both have rel=preload, so both urls should be extracted auto res = http2::parse_link_header("; rel=preload, ; rel=preload"sv); assert_size(2, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); assert_stdsv_equal("url2"sv, res[1].uri); } { // Second URI uri should be extracted. auto res = http2::parse_link_header(", ;rel=preload"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url2"sv, res[0].uri); } { // Error if input ends with ';' auto res = http2::parse_link_header(";rel=preload;"sv); assert_size(0, ==, res.size()); } { // Error if link header ends with ';' auto res = http2::parse_link_header(";rel=preload;, "sv); assert_size(0, ==, res.size()); } { // OK if input ends with ',' auto res = http2::parse_link_header(";rel=preload,"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // Multiple repeated ','s between fields is OK auto res = http2::parse_link_header(",,,;rel=preload"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url2"sv, res[0].uri); } { // Error if url is not enclosed by <> auto res = http2::parse_link_header("url>;rel=preload"sv); assert_size(0, ==, res.size()); } { // Error if url is not enclosed by <> auto res = http2::parse_link_header(";rel=preload; as="sv); assert_size(0, ==, res.size()); } { // Empty parameter value is not allowed auto res = http2::parse_link_header(";as=;rel=preload"sv); assert_size(0, ==, res.size()); } { // Empty parameter value is not allowed auto res = http2::parse_link_header(";as=, ;rel=preload"sv); assert_size(0, ==, res.size()); } { // Empty parameter name is not allowed auto res = http2::parse_link_header("; =file; rel=preload"sv); assert_size(0, ==, res.size()); } { // Without whitespaces auto res = http2::parse_link_header( ";as=file;rel=preload,;rel=preload"sv); assert_size(2, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); assert_stdsv_equal("url2"sv, res[1].uri); } { // link-extension may have no value auto res = http2::parse_link_header("; as; rel=preload"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // ext-name-star auto res = http2::parse_link_header("; foo*=bar; rel=preload"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // '*' is not allowed expect for trailing one auto res = http2::parse_link_header("; *=bar; rel=preload"sv); assert_size(0, ==, res.size()); } { // '*' is not allowed expect for trailing one auto res = http2::parse_link_header("; foo*bar=buzz; rel=preload"sv); assert_size(0, ==, res.size()); } { // ext-name-star must be followed by '=' auto res = http2::parse_link_header("; foo*; rel=preload"sv); assert_size(0, ==, res.size()); } { // '>' is not followed by ';' auto res = http2::parse_link_header(" rel=preload"sv); assert_size(0, ==, res.size()); } { // Starting with whitespace is no problem. auto res = http2::parse_link_header(" ; rel=preload"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // preload is a prefix of bogus rel parameter value auto res = http2::parse_link_header("; rel=preloadx"sv); assert_size(0, ==, res.size()); } { // preload in relation-types list auto res = http2::parse_link_header(R"(; rel="preload")"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // preload in relation-types list followed by another parameter auto res = http2::parse_link_header(R"(; rel="preload foo")"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // preload in relation-types list following another parameter auto res = http2::parse_link_header(R"(; rel="foo preload")"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // preload in relation-types list between other parameters auto res = http2::parse_link_header(R"(; rel="foo preload bar")"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // preload in relation-types list between other parameters auto res = http2::parse_link_header(R"(; rel="foo preload bar")"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // no preload in relation-types list auto res = http2::parse_link_header(R"(; rel="foo")"sv); assert_size(0, ==, res.size()); } { // no preload in relation-types list, multiple unrelated elements. auto res = http2::parse_link_header(R"(; rel="foo bar")"sv); assert_size(0, ==, res.size()); } { // preload in relation-types list, followed by another link-value. auto res = http2::parse_link_header(R"(; rel="preload", )"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // preload in relation-types list, following another link-value. auto res = http2::parse_link_header(R"(, ; rel="preload")"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url2"sv, res[0].uri); } { // preload in relation-types list, followed by another link-param. auto res = http2::parse_link_header(R"(; rel="preload"; as="font")"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // preload in relation-types list, followed by character other // than ';' or ',' auto res = http2::parse_link_header(R"(; rel="preload".)"sv); assert_size(0, ==, res.size()); } { // preload in relation-types list, followed by ';' but it // terminates input auto res = http2::parse_link_header(R"(; rel="preload";)"sv); assert_size(0, ==, res.size()); } { // preload in relation-types list, followed by ',' but it // terminates input auto res = http2::parse_link_header(R"(; rel="preload",)"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // preload in relation-types list but there is preceding white // space. auto res = http2::parse_link_header(R"(; rel=" preload")"sv); assert_size(0, ==, res.size()); } { // preload in relation-types list but there is trailing white // space. auto res = http2::parse_link_header(R"(; rel="preload ")"sv); assert_size(0, ==, res.size()); } { // backslash escaped characters in quoted-string auto res = http2::parse_link_header( R"(; rel=preload; title="foo\"baz\"bar")"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // anchor="" is acceptable auto res = http2::parse_link_header(R"(; rel=preload; anchor="")"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // With anchor="#foo", url should be ignored auto res = http2::parse_link_header(R"(; rel=preload; anchor="#foo")"sv); assert_size(0, ==, res.size()); } { // With anchor=f, url should be ignored auto res = http2::parse_link_header("; rel=preload; anchor=f"sv); assert_size(0, ==, res.size()); } { // First url is ignored With anchor="#foo", but url should be // accepted. auto res = http2::parse_link_header( R"(; rel=preload; anchor="#foo", ; rel=preload)"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url2"sv, res[0].uri); } { // With loadpolicy="next", url should be ignored auto res = http2::parse_link_header(R"(; rel=preload; loadpolicy="next")"sv); assert_size(0, ==, res.size()); } { // url should be picked up if empty loadpolicy is specified auto res = http2::parse_link_header(R"(; rel=preload; loadpolicy="")"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // case-insensitive match auto res = http2::parse_link_header( R"(; rel=preload; ANCHOR="#foo", ; )" R"(REL=PRELOAD, ; REL="foo PRELOAD bar")"sv); assert_size(2, ==, res.size()); assert_stdsv_equal("url2"sv, res[0].uri); assert_stdsv_equal("url3"sv, res[1].uri); } { // nopush at the end of input auto res = http2::parse_link_header("; rel=preload; nopush"sv); assert_size(0, ==, res.size()); } { // nopush followed by ';' auto res = http2::parse_link_header("; rel=preload; nopush; foo"sv); assert_size(0, ==, res.size()); } { // nopush followed by ',' auto res = http2::parse_link_header("; nopush; rel=preload"sv); assert_size(0, ==, res.size()); } { // string whose prefix is nopush auto res = http2::parse_link_header("; nopushyes; rel=preload"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } { // rel=preload twice auto res = http2::parse_link_header("; rel=preload; rel=preload"sv); assert_size(1, ==, res.size()); assert_stdsv_equal("url"sv, res[0].uri); } } void test_http2_path_join(void) { { auto base = "/"sv; auto rel = "/"sv; assert_stdstring_equal("/", http2::path_join(base, ""sv, rel, ""sv)); } { auto base = "/"sv; auto rel = "/alpha"sv; assert_stdstring_equal("/alpha", http2::path_join(base, ""sv, rel, ""sv)); } { // rel ends with trailing '/' auto base = "/"sv; auto rel = "/alpha/"sv; assert_stdstring_equal("/alpha/", http2::path_join(base, ""sv, rel, ""sv)); } { // rel contains multiple components auto base = "/"sv; auto rel = "/alpha/bravo"sv; assert_stdstring_equal("/alpha/bravo", http2::path_join(base, ""sv, rel, ""sv)); } { // rel is relative auto base = "/"sv; auto rel = "alpha/bravo"sv; assert_stdstring_equal("/alpha/bravo", http2::path_join(base, ""sv, rel, ""sv)); } { // rel is relative and base ends without /, which means it refers // to file. auto base = "/alpha"sv; auto rel = "bravo/charlie"sv; assert_stdstring_equal("/bravo/charlie", http2::path_join(base, ""sv, rel, ""sv)); } { // rel contains repeated '/'s auto base = "/"sv; auto rel = "/alpha/////bravo/////"sv; assert_stdstring_equal("/alpha/bravo/", http2::path_join(base, ""sv, rel, ""sv)); } { // base ends with '/', so '..' eats 'bravo' auto base = "/alpha/bravo/"sv; auto rel = "../charlie/delta"sv; assert_stdstring_equal("/alpha/charlie/delta", http2::path_join(base, ""sv, rel, ""sv)); } { // base does not end with '/', so '..' eats 'alpha/bravo' auto base = "/alpha/bravo"sv; auto rel = "../charlie"sv; assert_stdstring_equal("/charlie", http2::path_join(base, ""sv, rel, ""sv)); } { // 'charlie' is eaten by following '..' auto base = "/alpha/bravo/"sv; auto rel = "../charlie/../delta"sv; assert_stdstring_equal("/alpha/delta", http2::path_join(base, ""sv, rel, ""sv)); } { // excessive '..' results in '/' auto base = "/alpha/bravo/"sv; auto rel = "../../../"sv; assert_stdstring_equal("/", http2::path_join(base, ""sv, rel, ""sv)); } { // excessive '..' and path component auto base = "/alpha/bravo/"sv; auto rel = "../../../charlie"sv; assert_stdstring_equal("/charlie", http2::path_join(base, ""sv, rel, ""sv)); } { // rel ends with '..' auto base = "/alpha/bravo/"sv; auto rel = "charlie/.."sv; assert_stdstring_equal("/alpha/bravo/", http2::path_join(base, ""sv, rel, ""sv)); } { // base empty and rel contains '..' auto base = ""sv; auto rel = "charlie/.."sv; assert_stdstring_equal("/", http2::path_join(base, ""sv, rel, ""sv)); } { // '.' is ignored auto base = "/"sv; auto rel = "charlie/././././delta"sv; assert_stdstring_equal("/charlie/delta", http2::path_join(base, ""sv, rel, ""sv)); } { // trailing '.' is ignored auto base = "/"sv; auto rel = "charlie/."sv; assert_stdstring_equal("/charlie/", http2::path_join(base, ""sv, rel, ""sv)); } { // query auto base = "/"sv; auto rel = "/"sv; auto relq = "q"sv; assert_stdstring_equal("/?q", http2::path_join(base, ""sv, rel, relq)); } { // empty rel and query auto base = "/alpha"sv; auto rel = ""sv; auto relq = "q"sv; assert_stdstring_equal("/alpha?q", http2::path_join(base, ""sv, rel, relq)); } { // both rel and query are empty auto base = "/alpha"sv; auto baseq = "r"sv; auto rel = ""sv; auto relq = ""sv; assert_stdstring_equal("/alpha?r", http2::path_join(base, baseq, rel, relq)); } { // empty base auto base = ""sv; auto rel = "/alpha"sv; assert_stdstring_equal("/alpha", http2::path_join(base, ""sv, rel, ""sv)); } { // everything is empty assert_stdstring_equal("/", http2::path_join(""sv, ""sv, ""sv, ""sv)); } { // only baseq is not empty auto base = ""sv; auto baseq = "r"sv; auto rel = ""sv; assert_stdstring_equal("/?r", http2::path_join(base, baseq, rel, ""sv)); } { // path starts with multiple '/'s. auto base = ""sv; auto baseq = ""sv; auto rel = "//alpha//bravo"sv; auto relq = "charlie"sv; assert_stdstring_equal("/alpha/bravo?charlie", http2::path_join(base, baseq, rel, relq)); } // Test cases from RFC 3986, section 5.4. constexpr auto base = "/b/c/d;p"sv; constexpr auto baseq = "q"sv; { auto rel = "g"sv; auto relq = ""sv; assert_stdstring_equal("/b/c/g", http2::path_join(base, baseq, rel, relq)); } { auto rel = "./g"sv; auto relq = ""sv; assert_stdstring_equal("/b/c/g", http2::path_join(base, baseq, rel, relq)); } { auto rel = "g/"sv; auto relq = ""sv; assert_stdstring_equal("/b/c/g/", http2::path_join(base, baseq, rel, relq)); } { auto rel = "/g"sv; auto relq = ""sv; assert_stdstring_equal("/g", http2::path_join(base, baseq, rel, relq)); } { auto rel = ""sv; auto relq = "y"sv; assert_stdstring_equal("/b/c/d;p?y", http2::path_join(base, baseq, rel, relq)); } { auto rel = "g"sv; auto relq = "y"sv; assert_stdstring_equal("/b/c/g?y", http2::path_join(base, baseq, rel, relq)); } { auto rel = ";x"sv; auto relq = ""sv; assert_stdstring_equal("/b/c/;x", http2::path_join(base, baseq, rel, relq)); } { auto rel = "g;x"sv; auto relq = ""sv; assert_stdstring_equal("/b/c/g;x", http2::path_join(base, baseq, rel, relq)); } { auto rel = "g;x"sv; auto relq = "y"sv; assert_stdstring_equal("/b/c/g;x?y", http2::path_join(base, baseq, rel, relq)); } { auto rel = ""sv; auto relq = ""sv; assert_stdstring_equal("/b/c/d;p?q", http2::path_join(base, baseq, rel, relq)); } { auto rel = "."sv; auto relq = ""sv; assert_stdstring_equal("/b/c/", http2::path_join(base, baseq, rel, relq)); } { auto rel = "./"sv; auto relq = ""sv; assert_stdstring_equal("/b/c/", http2::path_join(base, baseq, rel, relq)); } { auto rel = ".."sv; auto relq = ""sv; assert_stdstring_equal("/b/", http2::path_join(base, baseq, rel, relq)); } { auto rel = "../"sv; auto relq = ""sv; assert_stdstring_equal("/b/", http2::path_join(base, baseq, rel, relq)); } { auto rel = "../g"sv; auto relq = ""sv; assert_stdstring_equal("/b/g", http2::path_join(base, baseq, rel, relq)); } { auto rel = "../.."sv; auto relq = ""sv; assert_stdstring_equal("/", http2::path_join(base, baseq, rel, relq)); } { auto rel = "../../"sv; auto relq = ""sv; assert_stdstring_equal("/", http2::path_join(base, baseq, rel, relq)); } { auto rel = "../../g"sv; auto relq = ""sv; assert_stdstring_equal("/g", http2::path_join(base, baseq, rel, relq)); } { auto rel = "../../../g"sv; auto relq = ""sv; assert_stdstring_equal("/g", http2::path_join(base, baseq, rel, relq)); } { auto rel = "../../../../g"sv; auto relq = ""sv; assert_stdstring_equal("/g", http2::path_join(base, baseq, rel, relq)); } { auto rel = "/./g"sv; auto relq = ""sv; assert_stdstring_equal("/g", http2::path_join(base, baseq, rel, relq)); } { auto rel = "/../g"sv; auto relq = ""sv; assert_stdstring_equal("/g", http2::path_join(base, baseq, rel, relq)); } { auto rel = "g."sv; auto relq = ""sv; assert_stdstring_equal("/b/c/g.", http2::path_join(base, baseq, rel, relq)); } { auto rel = ".g"sv; auto relq = ""sv; assert_stdstring_equal("/b/c/.g", http2::path_join(base, baseq, rel, relq)); } { auto rel = "g.."sv; auto relq = ""sv; assert_stdstring_equal("/b/c/g..", http2::path_join(base, baseq, rel, relq)); } { auto rel = "..g"sv; auto relq = ""sv; assert_stdstring_equal("/b/c/..g", http2::path_join(base, baseq, rel, relq)); } { auto rel = "./../g"sv; auto relq = ""sv; assert_stdstring_equal("/b/g", http2::path_join(base, baseq, rel, relq)); } { auto rel = "./g/."sv; auto relq = ""sv; assert_stdstring_equal("/b/c/g/", http2::path_join(base, baseq, rel, relq)); } { auto rel = "g/./h"sv; auto relq = ""sv; assert_stdstring_equal("/b/c/g/h", http2::path_join(base, baseq, rel, relq)); } { auto rel = "g/../h"sv; auto relq = ""sv; assert_stdstring_equal("/b/c/h", http2::path_join(base, baseq, rel, relq)); } { auto rel = "g;x=1/./y"sv; auto relq = ""sv; assert_stdstring_equal("/b/c/g;x=1/y", http2::path_join(base, baseq, rel, relq)); } { auto rel = "g;x=1/../y"sv; auto relq = ""sv; assert_stdstring_equal("/b/c/y", http2::path_join(base, baseq, rel, relq)); } } void test_http2_normalize_path(void) { assert_stdstring_equal( "/alpha/charlie", http2::normalize_path("/alpha/bravo/../charlie"sv, ""sv)); assert_stdstring_equal("/alpha", http2::normalize_path("/a%6c%70%68%61"sv, ""sv)); assert_stdstring_equal("/alpha%2F%3A", http2::normalize_path("/alpha%2f%3a"sv, ""sv)); assert_stdstring_equal("/%2F", http2::normalize_path("%2f"sv, ""sv)); assert_stdstring_equal("/%f", http2::normalize_path("%f"sv, ""sv)); assert_stdstring_equal("/%", http2::normalize_path("%"sv, ""sv)); assert_stdstring_equal("/", http2::normalize_path(""sv, ""sv)); assert_stdstring_equal("/alpha?bravo", http2::normalize_path("/alpha"sv, "bravo"sv)); } void test_http2_rewrite_clean_path(void) { BlockAllocator balloc(4096, 4096); // unreserved characters assert_stdsv_equal("/alpha/bravo/"sv, http2::rewrite_clean_path(balloc, "/alpha/%62ravo/"sv)); // percent-encoding is converted to upper case. assert_stdsv_equal("/delta%3A"sv, http2::rewrite_clean_path(balloc, "/delta%3a"sv)); // path component is normalized before matching assert_stdsv_equal("/alpha/bravo/"sv, http2::rewrite_clean_path( balloc, "/alpha/charlie/%2e././bravo/delta/.."sv)); assert_stdsv_equal("alpha%3a"sv, http2::rewrite_clean_path(balloc, "alpha%3a"sv)); assert_stdsv_equal(""sv, http2::rewrite_clean_path(balloc, ""sv)); assert_stdsv_equal("/alpha?bravo"sv, http2::rewrite_clean_path(balloc, "//alpha?bravo"sv)); } void test_http2_get_pure_path_component(void) { assert_stdsv_equal("/"sv, http2::get_pure_path_component("/"sv)); assert_stdsv_equal("/foo"sv, http2::get_pure_path_component("/foo"sv)); assert_stdsv_equal( "/bar"sv, http2::get_pure_path_component("https://example.org/bar"sv)); assert_stdsv_equal("/alpha"sv, http2::get_pure_path_component( "https://example.org/alpha?q=a"sv)); assert_stdsv_equal("/bravo"sv, http2::get_pure_path_component( "https://example.org/bravo?q=a#fragment"sv)); assert_stdsv_equal(""sv, http2::get_pure_path_component("\x01\x02"sv)); } void test_http2_construct_push_component(void) { BlockAllocator balloc(4096, 4096); std::string_view base, uri; std::string_view scheme, authority, path; base = "/b/"sv; uri = "https://example.org/foo"sv; assert_int(0, ==, http2::construct_push_component(balloc, scheme, authority, path, base, uri)); assert_stdsv_equal("https"sv, scheme); assert_stdsv_equal("example.org"sv, authority); assert_stdsv_equal("/foo"sv, path); scheme = ""sv; authority = ""sv; path = ""sv; uri = "/foo/bar?q=a"sv; assert_int(0, ==, http2::construct_push_component(balloc, scheme, authority, path, base, uri)); assert_stdsv_equal(""sv, scheme); assert_stdsv_equal(""sv, authority); assert_stdsv_equal("/foo/bar?q=a"sv, path); scheme = ""sv; authority = ""sv; path = ""sv; uri = "foo/../bar?q=a"sv; assert_int(0, ==, http2::construct_push_component(balloc, scheme, authority, path, base, uri)); assert_stdsv_equal(""sv, scheme); assert_stdsv_equal(""sv, authority); assert_stdsv_equal("/b/bar?q=a"sv, path); scheme = ""sv; authority = ""sv; path = ""sv; uri = ""sv; assert_int(-1, ==, http2::construct_push_component(balloc, scheme, authority, path, base, uri)); scheme = ""sv; authority = ""sv; path = ""sv; uri = "?q=a"sv; assert_int(0, ==, http2::construct_push_component(balloc, scheme, authority, path, base, uri)); assert_stdsv_equal(""sv, scheme); assert_stdsv_equal(""sv, authority); assert_stdsv_equal("/b/?q=a"sv, path); } void test_http2_contains_trailers(void) { assert_false(http2::contains_trailers(""sv)); assert_true(http2::contains_trailers("trailers"sv)); // Match must be case-insensitive. assert_true(http2::contains_trailers("TRAILERS"sv)); assert_false(http2::contains_trailers("trailer"sv)); assert_false(http2::contains_trailers("trailers 3"sv)); assert_true(http2::contains_trailers("trailers,"sv)); assert_true(http2::contains_trailers("trailers,foo"sv)); assert_true(http2::contains_trailers("foo,trailers"sv)); assert_true(http2::contains_trailers("foo,trailers,bar"sv)); assert_true(http2::contains_trailers("foo, trailers ,bar"sv)); assert_true(http2::contains_trailers(",trailers"sv)); } void test_http2_check_transfer_encoding(void) { assert_true(http2::check_transfer_encoding("chunked"sv)); assert_true(http2::check_transfer_encoding("foo,chunked"sv)); assert_true(http2::check_transfer_encoding("foo, chunked"sv)); assert_true(http2::check_transfer_encoding("foo , chunked"sv)); assert_true(http2::check_transfer_encoding("chunked;foo=bar"sv)); assert_true(http2::check_transfer_encoding("chunked ; foo=bar"sv)); assert_true(http2::check_transfer_encoding(R"(chunked;foo="bar")"sv)); assert_true( http2::check_transfer_encoding(R"(chunked;foo="\bar\"";FOO=BAR)"sv)); assert_true(http2::check_transfer_encoding(R"(chunked;foo="")"sv)); assert_true(http2::check_transfer_encoding(R"(chunked;foo="bar" , gzip)"sv)); assert_false(http2::check_transfer_encoding(""sv)); assert_false(http2::check_transfer_encoding(",chunked"sv)); assert_false(http2::check_transfer_encoding("chunked,"sv)); assert_false(http2::check_transfer_encoding("chunked, "sv)); assert_false(http2::check_transfer_encoding("foo,,chunked"sv)); assert_false(http2::check_transfer_encoding("chunked;foo"sv)); assert_false(http2::check_transfer_encoding("chunked;"sv)); assert_false(http2::check_transfer_encoding("chunked;foo=bar;"sv)); assert_false(http2::check_transfer_encoding("chunked;?=bar"sv)); assert_false(http2::check_transfer_encoding("chunked;=bar"sv)); assert_false(http2::check_transfer_encoding("chunked;;"sv)); assert_false(http2::check_transfer_encoding("chunked?"sv)); assert_false(http2::check_transfer_encoding(","sv)); assert_false(http2::check_transfer_encoding(" "sv)); assert_false(http2::check_transfer_encoding(";"sv)); assert_false(http2::check_transfer_encoding("\""sv)); assert_false(http2::check_transfer_encoding(R"(chunked;foo="bar)"sv)); assert_false(http2::check_transfer_encoding(R"(chunked;foo="bar\)"sv)); assert_false(http2::check_transfer_encoding(R"(chunked;foo="bar\)" "\x0a" R"(")"sv)); assert_false(http2::check_transfer_encoding(R"(chunked;foo=")" "\x0a" R"(")"sv)); assert_false(http2::check_transfer_encoding(R"(chunked;foo="bar",,gzip)"sv)); } void test_http2_capitalize(void) { MemchunkPool pool; DefaultMemchunks m{&pool}; iovec iov; { http2::capitalize(&m, "content-length"sv); auto iovcnt = m.riovec(&iov, 1); assert_int(1, ==, iovcnt); assert_stdsv_equal( "Content-Length"sv, (std::string_view{static_cast(iov.iov_base), iov.iov_len})); m.reset(); } { http2::capitalize(&m, "altsvc"sv); auto iovcnt = m.riovec(&iov, 1); assert_int(1, ==, iovcnt); assert_stdsv_equal( "Altsvc"sv, (std::string_view{static_cast(iov.iov_base), iov.iov_len})); m.reset(); } { http2::capitalize(&m, "altsvc-"sv); auto iovcnt = m.riovec(&iov, 1); assert_int(1, ==, iovcnt); assert_stdsv_equal( "Altsvc-"sv, (std::string_view{static_cast(iov.iov_base), iov.iov_len})); m.reset(); } { http2::capitalize(&m, "alt--svc"sv); auto iovcnt = m.riovec(&iov, 1); assert_int(1, ==, iovcnt); assert_stdsv_equal( "Alt--Svc"sv, (std::string_view{static_cast(iov.iov_base), iov.iov_len})); m.reset(); } { http2::capitalize(&m, "sec-websocket----------------key"sv); auto iovcnt = m.riovec(&iov, 1); assert_int(1, ==, iovcnt); assert_stdsv_equal( "Sec-Websocket----------------Key"sv, (std::string_view{static_cast(iov.iov_base), iov.iov_len})); m.reset(); } { http2::capitalize(&m, "content--------------------length"sv); auto iovcnt = m.riovec(&iov, 1); assert_int(1, ==, iovcnt); assert_stdsv_equal( "Content--------------------Length"sv, (std::string_view{static_cast(iov.iov_base), iov.iov_len})); m.reset(); } { http2::capitalize(&m, "content--------------------length-"sv); auto iovcnt = m.riovec(&iov, 1); assert_int(1, ==, iovcnt); assert_stdsv_equal( "Content--------------------Length-"sv, (std::string_view{static_cast(iov.iov_base), iov.iov_len})); m.reset(); } } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/template_test.cc0000644000000000000000000000013215077107271016231 xustar0030 mtime=1761382073.001444102 30 atime=1761382080.105411485 30 ctime=1761382109.271299887 nghttp2-1.68.0/src/template_test.cc0000644000175100017510000001214215077107271016621 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "template_test.h" #include #include #include #include "munitxx.h" #include "template.h" using namespace std::literals; namespace nghttp2 { namespace { const MunitTest tests[]{ munit_void_test(test_template_immutable_string), munit_void_test(test_template_as_uint8_span), munit_void_test(test_template_as_string_view), munit_void_test(test_template_as_string_view), munit_test_end(), }; } // namespace const MunitSuite template_suite{ "/template", tests, nullptr, 1, MUNIT_SUITE_OPTION_NONE, }; void test_template_immutable_string(void) { ImmutableString null; assert_string_equal("", null.c_str()); assert_size(0, ==, null.size()); assert_true(null.empty()); ImmutableString from_cstr("alpha"); assert_string_equal("alpha", from_cstr.c_str()); assert_size(5, ==, from_cstr.size()); assert_false(from_cstr.empty()); assert_true("alpha" == from_cstr); assert_true(from_cstr == "alpha"); assert_true(std::string("alpha") == from_cstr); assert_true(from_cstr == std::string("alpha")); ImmutableString from_stdstr("alpha"s); assert_true("alpha" == from_stdstr); // copy constructor ImmutableString src("charlie"); ImmutableString copy = src; assert_string_equal("charlie", copy.c_str()); assert_size(7, ==, copy.size()); // copy assignment ImmutableString copy2; copy2 = src; assert_string_equal("charlie", copy2.c_str()); assert_size(7, ==, copy2.size()); // move constructor ImmutableString move = std::move(copy); assert_string_equal("charlie", move.c_str()); assert_size(7, ==, move.size()); assert_string_equal("", copy.c_str()); assert_size(0, ==, copy.size()); // move assignment move = std::move(from_cstr); assert_string_equal("alpha", move.c_str()); assert_size(5, ==, move.size()); assert_string_equal("", from_cstr.c_str()); assert_size(0, ==, from_cstr.size()); // from string literal auto from_lit = "bravo"_is; assert_string_equal("bravo", from_lit.c_str()); assert_size(5, ==, from_lit.size()); // equality ImmutableString eq("delta"); assert_true("delta1" != eq); assert_true("delt" != eq); assert_true(eq != "delta1"); assert_true(eq != "delt"); // operator[] ImmutableString br_op("foxtrot"); assert_char('f', ==, br_op[0]); assert_char('o', ==, br_op[1]); assert_char('t', ==, br_op[6]); assert_char('\0', ==, br_op[7]); // operator==(const ImmutableString &, const ImmutableString &) { ImmutableString a("foo"); ImmutableString b("foo"); ImmutableString c("fo"); assert_true(a == b); assert_true(a != c); assert_true(c != b); } // operator<< { ImmutableString a("foo"); std::stringstream ss; ss << a; assert_stdstring_equal("foo", ss.str()); } // operator +=(std::string &, const ImmutableString &) { std::string a = "alpha"; a += ImmutableString("bravo"); assert_stdstring_equal("alphabravo", a); } } void test_template_as_uint8_span(void) { uint32_t a[2]; memcpy(&a, "\xc0\xc1\xc2\xc3\xf0\xf1\xf2\xf3", sizeof(a)); // dynamic extent auto s = as_uint8_span(std::span{a, 2}); assert_size(sizeof(a), ==, s.size()); assert_size(std::dynamic_extent, ==, s.extent); assert_memory_equal(s.size(), &a, s.data()); // non-dynamic extent auto t = as_uint8_span(std::span{a, 2}); assert_size(sizeof(a), ==, t.size()); assert_size(sizeof(a), ==, t.extent); assert_memory_equal(t.size(), &a, t.data()); } void test_template_as_string_view(void) { { auto a = std::to_array({'a', 'l', 'p', 'h', 'a'}); assert_stdsv_equal("alpha"sv, as_string_view(a)); assert_stdsv_equal( "alpha"sv, as_string_view(std::ranges::begin(a), std::ranges::end(a))); assert_stdsv_equal("alp"sv, as_string_view(std::ranges::begin(a), 3)); } { auto s = ""s; assert_stdsv_equal(""sv, as_string_view(s)); assert_stdsv_equal( ""sv, as_string_view(std::ranges::begin(s), std::ranges::end(s))); } } } // namespace nghttp2 nghttp2-1.68.0/src/PaxHeaders/h2load_http2_session.h0000644000000000000000000000013115077107270017254 xustar0030 mtime=1761382072.985444176 29 atime=1761382106.21731037 30 ctime=1761382109.213300055 nghttp2-1.68.0/src/h2load_http2_session.h0000644000175100017510000000333315077107270017647 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef H2LOAD_HTTP2_SESSION_H #define H2LOAD_HTTP2_SESSION_H #include "h2load_session.h" #include namespace h2load { struct Client; class Http2Session : public Session { public: Http2Session(Client *client); virtual ~Http2Session(); virtual void on_connect(); virtual int submit_request(); virtual int on_read(const uint8_t *data, size_t len); virtual int on_write(); virtual void terminate(); virtual size_t max_concurrent_streams(); private: Client *client_; nghttp2_session *session_; }; } // namespace h2load #endif // H2LOAD_HTTP2_SESSION_H nghttp2-1.68.0/src/PaxHeaders/network.h0000644000000000000000000000013215077107270014711 xustar0030 mtime=1761382072.988444162 30 atime=1761382106.231310308 30 ctime=1761382109.227300014 nghttp2-1.68.0/src/network.h0000644000175100017510000000367415077107270015313 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NETWORK_H #define NETWORK_H #ifdef HAVE_CONFIG_H # include #endif // defined(HAVE_CONFIG_H) #include #ifdef HAVE_SYS_SOCKET_H # include #endif // defined(HAVE_SYS_SOCKET_H) #ifdef _WIN32 # include #else // !defined(_WIN32) # include #endif // !defined(_WIN32) #ifdef HAVE_NETINET_IN_H # include #endif // defined(HAVE_NETINET_IN_H) #ifdef HAVE_ARPA_INET_H # include #endif // defined(HAVE_ARPA_INET_H) namespace nghttp2 { union sockaddr_union { sockaddr_storage storage; sockaddr sa; sockaddr_in6 in6; sockaddr_in in; #ifndef _WIN32 sockaddr_un un; #endif // !defined(_WIN32) }; struct Address { socklen_t len; union sockaddr_union su; }; } // namespace nghttp2 #endif // !defined(NETWORK_H) nghttp2-1.68.0/src/PaxHeaders/memchunk_test.cc0000644000000000000000000000013215077107270016224 xustar0030 mtime=1761382072.988444162 30 atime=1761382106.271310132 30 ctime=1761382109.268299895 nghttp2-1.68.0/src/memchunk_test.cc0000644000175100017510000001645215077107270016624 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "memchunk_test.h" #include "munitxx.h" #include #include "memchunk.h" #include "util.h" using namespace std::literals; namespace nghttp2 { namespace { const MunitTest tests[]{ munit_void_test(test_pool_recycle), munit_void_test(test_memchunks_append), munit_void_test(test_memchunks_drain), munit_void_test(test_memchunks_riovec), munit_void_test(test_memchunks_recycle), munit_void_test(test_memchunks_reset), munit_void_test(test_memchunks_reserve), munit_void_test(test_memchunkbuffer_drain_reset), munit_test_end(), }; } // namespace const MunitSuite memchunk_suite{ "/memchunk", tests, nullptr, 1, MUNIT_SUITE_OPTION_NONE, }; void test_pool_recycle(void) { MemchunkPool pool; assert_null(pool.pool); assert_size(0, ==, pool.poolsize); assert_null(pool.freelist); auto m1 = pool.get(); assert_ptr_equal(m1, pool.pool); assert_size(MemchunkPool::value_type::size, ==, pool.poolsize); assert_null(pool.freelist); auto m2 = pool.get(); assert_ptr_equal(m2, pool.pool); assert_size(2 * MemchunkPool::value_type::size, ==, pool.poolsize); assert_null(pool.freelist); assert_ptr_equal(m1, m2->knext); assert_null(m1->knext); auto m3 = pool.get(); assert_ptr_equal(m3, pool.pool); assert_size(3 * MemchunkPool::value_type::size, ==, pool.poolsize); assert_null(pool.freelist); pool.recycle(m3); assert_ptr_equal(m3, pool.pool); assert_size(3 * MemchunkPool::value_type::size, ==, pool.poolsize); assert_ptr_equal(m3, pool.freelist); auto m4 = pool.get(); assert_ptr_equal(m3, m4); assert_ptr_equal(m4, pool.pool); assert_size(3 * MemchunkPool::value_type::size, ==, pool.poolsize); assert_null(pool.freelist); pool.recycle(m2); pool.recycle(m1); assert_ptr_equal(m1, pool.freelist); assert_ptr_equal(m2, m1->next); assert_null(m2->next); } using Memchunk16 = Memchunk<16>; using MemchunkPool16 = Pool; using Memchunks16 = Memchunks; using MemchunkBuffer16 = MemchunkBuffer; void test_memchunks_append(void) { MemchunkPool16 pool; Memchunks16 chunks(&pool); chunks.append("012"sv); auto m = chunks.tail; assert_size(3, ==, m->len()); assert_size(13, ==, m->left()); chunks.append("3456789abcdef@"sv); assert_size(16, ==, m->len()); assert_size(0, ==, m->left()); m = chunks.tail; assert_size(1, ==, m->len()); assert_size(15, ==, m->left()); assert_size(17, ==, chunks.rleft()); char buf[16]; size_t nread; nread = chunks.remove(buf, 8); assert_size(8, ==, nread); assert_memory_equal(nread, "01234567", buf); assert_size(9, ==, chunks.rleft()); nread = chunks.remove(buf, sizeof(buf)); assert_size(9, ==, nread); assert_memory_equal(nread, "89abcdef@", buf); assert_size(0, ==, chunks.rleft()); assert_null(chunks.head); assert_null(chunks.tail); assert_size(32, ==, pool.poolsize); } void test_memchunks_drain(void) { MemchunkPool16 pool; Memchunks16 chunks(&pool); chunks.append("0123456789"sv); size_t nread; nread = chunks.drain(3); assert_size(3, ==, nread); char buf[16]; nread = chunks.remove(buf, sizeof(buf)); assert_size(7, ==, nread); assert_memory_equal(nread, "3456789", buf); } void test_memchunks_riovec(void) { MemchunkPool16 pool; Memchunks16 chunks(&pool); std::array buf{}; chunks.append(buf.data(), buf.size()); std::array iov; auto iovcnt = chunks.riovec(iov.data(), iov.size()); auto m = chunks.head; assert_int(2, ==, iovcnt); assert_ptr_equal(m->buf.data(), iov[0].iov_base); assert_size(m->len(), ==, iov[0].iov_len); m = m->next; assert_ptr_equal(m->buf.data(), iov[1].iov_base); assert_size(m->len(), ==, iov[1].iov_len); chunks.drain(2 * 16); iovcnt = chunks.riovec(iov.data(), iov.size()); assert_int(1, ==, iovcnt); m = chunks.head; assert_ptr_equal(m->buf.data(), iov[0].iov_base); assert_size(m->len(), ==, iov[0].iov_len); } void test_memchunks_recycle(void) { MemchunkPool16 pool; { Memchunks16 chunks(&pool); std::array buf{}; chunks.append(buf.data(), buf.size()); } assert_size(32, ==, pool.poolsize); assert_not_null(pool.freelist); auto m = pool.freelist; m = m->next; assert_not_null(m); assert_null(m->next); } void test_memchunks_reset(void) { MemchunkPool16 pool; Memchunks16 chunks(&pool); std::array b{}; chunks.append(b.data(), b.size()); assert_size(32, ==, chunks.rleft()); chunks.reset(); assert_size(0, ==, chunks.rleft()); assert_null(chunks.head); assert_null(chunks.tail); auto m = pool.freelist; assert_not_null(m); assert_not_null(m->next); assert_null(m->next->next); } void test_memchunks_reserve(void) { MemchunkPool16 pool; Memchunks16 chunks(&pool); std::array iov; chunks.append(8, [](auto result) { return std::ranges::copy("foobar00"sv, std::move(result)).out; }); assert_size(8, ==, chunks.rleft()); auto iovcnt = chunks.riovec(iov.data(), iov.size()); assert_int(1, ==, iovcnt); assert_stdsv_equal( "foobar00"sv, (std::string_view{reinterpret_cast(iov[0].iov_base), iov[0].iov_len})); chunks.reset(); chunks.append("012345678"sv); chunks.append(8, [](auto result) { return std::ranges::copy("foobar00"sv, std::move(result)).out; }); assert_size(17, ==, chunks.rleft()); iovcnt = chunks.riovec(iov.data(), iov.size()); assert_int(2, ==, iovcnt); assert_stdsv_equal( "012345678"sv, (std::string_view{reinterpret_cast(iov[0].iov_base), iov[0].iov_len})); assert_stdsv_equal( "foobar00"sv, (std::string_view{reinterpret_cast(iov[1].iov_base), iov[1].iov_len})); } void test_memchunkbuffer_drain_reset(void) { MemchunkPool16 pool; MemchunkBuffer16 buf(&pool); buf.ensure_chunk(); auto data = "0123456789"sv; std::ranges::copy(data, buf.begin()); buf.write(data.size()); auto nread = buf.drain_reset(3); assert_size(3, ==, nread); assert_true(buf.begin() == buf.chunk->pos); assert_size(7, ==, buf.rleft()); assert_true(buf.begin() + buf.rleft() == buf.chunk->last); } } // namespace nghttp2 nghttp2-1.68.0/src/PaxHeaders/shrpx_connect_blocker.cc0000644000000000000000000000013215077107270017734 xustar0030 mtime=1761382072.991444148 30 atime=1761382106.127310766 30 ctime=1761382109.123300315 nghttp2-1.68.0/src/shrpx_connect_blocker.cc0000644000175100017510000000753615077107270020337 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_connect_blocker.h" #include "shrpx_config.h" #include "shrpx_log.h" namespace shrpx { namespace { void connect_blocker_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto connect_blocker = static_cast(w->data); if (LOG_ENABLED(INFO)) { LOG(INFO) << "Unblock"; } connect_blocker->call_unblock_func(); } } // namespace ConnectBlocker::ConnectBlocker(std::mt19937 &gen, struct ev_loop *loop, std::function block_func, std::function unblock_func) : gen_(gen), block_func_(std::move(block_func)), unblock_func_(std::move(unblock_func)), loop_(loop), fail_count_(0), offline_(false) { ev_timer_init(&timer_, connect_blocker_cb, 0., 0.); timer_.data = this; } ConnectBlocker::~ConnectBlocker() { ev_timer_stop(loop_, &timer_); } bool ConnectBlocker::blocked() const { return ev_is_active(&timer_); } void ConnectBlocker::on_success() { if (ev_is_active(&timer_)) { return; } fail_count_ = 0; } // Use the similar backoff algorithm described in // https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md namespace { constexpr size_t MAX_BACKOFF_EXP = 10; constexpr auto MULTIPLIER = 1.6; constexpr auto JITTER = 0.2; } // namespace void ConnectBlocker::on_failure() { if (ev_is_active(&timer_)) { return; } call_block_func(); ++fail_count_; auto base_backoff = util::int_pow(MULTIPLIER, std::min(MAX_BACKOFF_EXP, fail_count_)); auto dist = std::uniform_real_distribution<>(-JITTER * base_backoff, JITTER * base_backoff); auto &downstreamconf = *get_config()->conn.downstream; auto backoff = std::min(downstreamconf.timeout.max_backoff, base_backoff + dist(gen_)); LOG(WARN) << "Could not connect " << fail_count_ << " times in a row; sleep for " << backoff << " seconds"; ev_timer_set(&timer_, backoff, 0.); ev_timer_start(loop_, &timer_); } size_t ConnectBlocker::get_fail_count() const { return fail_count_; } void ConnectBlocker::offline() { if (offline_) { return; } if (!ev_is_active(&timer_)) { call_block_func(); } offline_ = true; ev_timer_stop(loop_, &timer_); ev_timer_set(&timer_, std::numeric_limits::max(), 0.); ev_timer_start(loop_, &timer_); } void ConnectBlocker::online() { ev_timer_stop(loop_, &timer_); call_unblock_func(); fail_count_ = 0; offline_ = false; } bool ConnectBlocker::in_offline() const { return offline_; } void ConnectBlocker::call_block_func() { if (block_func_) { block_func_(); } } void ConnectBlocker::call_unblock_func() { if (unblock_func_) { unblock_func_(); } } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/http2_test.h0000644000000000000000000000013215077107270015320 xustar0030 mtime=1761382072.987444166 30 atime=1761382106.262310172 30 ctime=1761382109.259299921 nghttp2-1.68.0/src/http2_test.h0000644000175100017510000000442015077107270015710 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef HTTP2_TEST_H #define HTTP2_TEST_H #ifdef HAVE_CONFIG_H # include #endif // defined(HAVE_CONFIG_H) #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" namespace shrpx { extern const MunitSuite http2_suite; munit_void_test_decl(test_http2_add_header) munit_void_test_decl(test_http2_get_header) munit_void_test_decl(test_http2_copy_headers_to_nva) munit_void_test_decl(test_http2_build_http1_headers_from_headers) munit_void_test_decl(test_http2_rewrite_location_uri) munit_void_test_decl(test_http2_parse_http_status_code) munit_void_test_decl(test_http2_index_header) munit_void_test_decl(test_http2_lookup_token) munit_void_test_decl(test_http2_parse_link_header) munit_void_test_decl(test_http2_path_join) munit_void_test_decl(test_http2_normalize_path) munit_void_test_decl(test_http2_rewrite_clean_path) munit_void_test_decl(test_http2_get_pure_path_component) munit_void_test_decl(test_http2_construct_push_component) munit_void_test_decl(test_http2_contains_trailers) munit_void_test_decl(test_http2_check_transfer_encoding) munit_void_test_decl(test_http2_capitalize) } // namespace shrpx #endif // !defined(HTTP2_TEST_H) nghttp2-1.68.0/src/PaxHeaders/CMakeLists.txt0000644000000000000000000000013215077107270015607 xustar0030 mtime=1761382072.983444185 30 atime=1761382106.283310079 30 ctime=1761382109.280299861 nghttp2-1.68.0/src/CMakeLists.txt0000644000175100017510000001464015077107270016204 0ustar00runnerrunnerfile(GLOB c_sources *.c) set_source_files_properties(${c_sources} PROPERTIES COMPILE_FLAGS "${WARNCFLAGS}") file(GLOB cxx_sources *.cc) set_source_files_properties(${cxx_sources} PROPERTIES COMPILE_FLAGS "${WARNCXXFLAGS} ${CXX1XCXXFLAGS}") include_directories( "${CMAKE_CURRENT_SOURCE_DIR}/includes" "${CMAKE_CURRENT_SOURCE_DIR}/../third-party/urlparse" "${CMAKE_CURRENT_SOURCE_DIR}/../third-party/llhttp/include" ${JEMALLOC_INCLUDE_DIRS} ${LIBXML2_INCLUDE_DIRS} ${LIBEV_INCLUDE_DIRS} ${LIBNGHTTP3_INCLUDE_DIRS} ${LIBNGTCP2_INCLUDE_DIRS} ${LIBNGTCP2_CRYPTO_QUICTLS_INCLUDE_DIRS} ${LIBNGTCP2_CRYPTO_LIBRESSL_INCLUDE_DIRS} ${LIBNGTCP2_CRYPTO_WOLFSSL_INCLUDE_DIRS} ${LIBNGTCP2_CRYPTO_OSSL_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS} ${WOLFSSL_INCLUDE_DIRS} ${LIBCARES_INCLUDE_DIRS} ${JANSSON_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS} ${LIBBPF_INCLUDE_DIRS} ${LIBBROTLIENC_INCLUDE_DIRS} ${LIBBROTLIDEC_INCLUDE_DIRS} ) # XXX per-target? link_libraries( nghttp2 ${JEMALLOC_LIBRARIES} ${LIBXML2_LIBRARIES} ${LIBEV_LIBRARIES} ${LIBNGHTTP3_LIBRARIES} ${LIBNGTCP2_LIBRARIES} ${LIBNGTCP2_CRYPTO_QUICTLS_LIBRARIES} ${LIBNGTCP2_CRYPTO_LIBRESSL_LIBRARIES} ${LIBNGTCP2_CRYPTO_WOLFSSL_LIBRARIES} ${LIBNGTCP2_CRYPTO_OSSL_LIBRARIES} ${OPENSSL_LIBRARIES} ${WOLFSSL_LIBRARIES} ${LIBCARES_LIBRARIES} ${JANSSON_LIBRARIES} ${ZLIB_LIBRARIES} ${APP_LIBRARIES} ${LIBBPF_LIBRARIES} ${LIBBROTLIENC_LIBRARIES} ${LIBBROTLIDEC_LIBRARIES} ) if(ENABLE_APP) set(HELPER_OBJECTS util.cc http2.cc timegm.c app_helper.cc nghttp2_gzip.c ) # nghttp client set(NGHTTP_SOURCES ${HELPER_OBJECTS} nghttp.cc tls.cc ) if(HAVE_LIBXML2) list(APPEND NGHTTP_SOURCES HtmlParser.cc) endif() # nghttpd set(NGHTTPD_SOURCES ${HELPER_OBJECTS} nghttpd.cc tls.cc HttpServer.cc ) # h2load set(H2LOAD_SOURCES util.cc http2.cc h2load.cc timegm.c tls.cc h2load_http2_session.cc h2load_http1_session.cc ) if(ENABLE_HTTP3) list(APPEND H2LOAD_SOURCES h2load_http3_session.cc h2load_quic.cc ) endif() # Common libnhttpx sources (used for nghttpx and unit tests) set(NGHTTPX_SRCS util.cc http2.cc timegm.c app_helper.cc tls.cc shrpx_config.cc shrpx_accept_handler.cc shrpx_connection_handler.cc shrpx_client_handler.cc shrpx_http2_upstream.cc shrpx_https_upstream.cc shrpx_downstream.cc shrpx_downstream_connection.cc shrpx_http_downstream_connection.cc shrpx_http2_downstream_connection.cc shrpx_http2_session.cc shrpx_downstream_queue.cc shrpx_log.cc shrpx_http.cc shrpx_io_control.cc shrpx_tls.cc shrpx_worker.cc shrpx_log_config.cc shrpx_connect_blocker.cc shrpx_live_check.cc shrpx_downstream_connection_pool.cc shrpx_rate_limit.cc shrpx_connection.cc shrpx_memcached_dispatcher.cc shrpx_memcached_connection.cc shrpx_worker_process.cc shrpx_signal.cc shrpx_router.cc shrpx_api_downstream_connection.cc shrpx_health_monitor_downstream_connection.cc shrpx_null_downstream_connection.cc shrpx_dns_resolver.cc shrpx_dual_dns_resolver.cc shrpx_dns_tracker.cc xsi_strerror.c ) if(HAVE_MRUBY) list(APPEND NGHTTPX_SRCS shrpx_mruby.cc shrpx_mruby_module.cc shrpx_mruby_module_env.cc shrpx_mruby_module_request.cc shrpx_mruby_module_response.cc ) endif() if(ENABLE_HTTP3) list(APPEND NGHTTPX_SRCS shrpx_quic.cc shrpx_quic_listener.cc shrpx_quic_connection_handler.cc shrpx_http3_upstream.cc http3.cc siphash.cc ) endif() add_library(nghttpx_static STATIC ${NGHTTPX_SRCS}) set_target_properties(nghttpx_static PROPERTIES ARCHIVE_OUTPUT_NAME nghttpx) set(NGHTTPX-bin_SOURCES shrpx.cc ) if(HAVE_SYSTEMD) target_link_libraries(nghttpx_static ${SYSTEMD_LIBRARIES}) target_compile_definitions(nghttpx_static PUBLIC HAVE_LIBSYSTEMD) target_include_directories(nghttpx_static PUBLIC ${SYSTEMD_INCLUDE_DIRS}) endif() if(HAVE_MRUBY) target_link_libraries(nghttpx_static mruby-lib) endif() if(HAVE_NEVERBLEED) target_link_libraries(nghttpx_static neverbleed) endif() if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING) set(NGHTTPX_UNITTEST_SOURCES shrpx-unittest.cc shrpx_tls_test.cc shrpx_downstream_test.cc shrpx_config_test.cc shrpx_worker_test.cc shrpx_http_test.cc shrpx_router_test.cc http2_test.cc util_test.cc nghttp2_gzip_test.c nghttp2_gzip.c buffer_test.cc memchunk_test.cc template_test.cc base64_test.cc ${CMAKE_SOURCE_DIR}/tests/munit/munit.c ) if(ENABLE_HTTP3) list(APPEND NGHTTPX_UNITTEST_SOURCES siphash_test.cc) endif() add_executable(nghttpx-unittest EXCLUDE_FROM_ALL ${NGHTTPX_UNITTEST_SOURCES} $ $ ) target_include_directories(nghttpx-unittest PRIVATE ${CMAKE_SOURCE_DIR}/tests/munit ) target_compile_definitions(nghttpx-unittest PRIVATE "-DNGHTTP2_SRC_DIR=\"${CMAKE_SOURCE_DIR}/src\"" ) target_link_libraries(nghttpx-unittest nghttpx_static) if(HAVE_MRUBY) target_link_libraries(nghttpx-unittest mruby-lib) endif() if(HAVE_NEVERBLEED) target_link_libraries(nghttpx-unittest neverbleed) endif() add_test(nghttpx-unittest nghttpx-unittest) add_dependencies(check nghttpx-unittest) endif() add_executable(nghttp ${NGHTTP_SOURCES} $ $ ) add_executable(nghttpd ${NGHTTPD_SOURCES} $ $ ) add_executable(nghttpx ${NGHTTPX-bin_SOURCES} $ $ ) target_compile_definitions(nghttpx PRIVATE "-DPKGDATADIR=\"${PKGDATADIR}\"" "-DPKGLIBDIR=\"${PKGLIBDIR}\"" ) target_link_libraries(nghttpx nghttpx_static) add_executable(h2load ${H2LOAD_SOURCES} $ $ ) install(TARGETS nghttp nghttpd nghttpx h2load) endif() if(ENABLE_HPACK_TOOLS) set(inflatehd_SOURCES inflatehd.cc comp_helper.c ) set(deflatehd_SOURCES deflatehd.cc comp_helper.c util.cc timegm.c ) add_executable(inflatehd ${inflatehd_SOURCES}) add_executable(deflatehd ${deflatehd_SOURCES}) install(TARGETS inflatehd deflatehd) endif() nghttp2-1.68.0/src/PaxHeaders/Makefile.in0000644000000000000000000000013215077107305015113 xustar0030 mtime=1761382085.756386356 30 atime=1761382103.875320687 30 ctime=1761382109.056300508 nghttp2-1.68.0/src/Makefile.in0000644000175100017510000074423515077107305015522 0ustar00runnerrunner# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # nghttp2 - HTTP/2 C Library # Copyright (c) 2012 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ bin_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) check_PROGRAMS = $(am__EXEEXT_3) TESTS = $(am__EXEEXT_3) @ENABLE_APP_TRUE@am__append_1 = nghttp nghttpd nghttpx h2load @ENABLE_APP_TRUE@@HAVE_LIBXML2_TRUE@am__append_2 = HtmlParser.cc @ENABLE_APP_TRUE@@ENABLE_HTTP3_TRUE@am__append_3 = \ @ENABLE_APP_TRUE@@ENABLE_HTTP3_TRUE@ h2load_http3_session.cc h2load_http3_session.h \ @ENABLE_APP_TRUE@@ENABLE_HTTP3_TRUE@ h2load_quic.cc h2load_quic.h @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@am__append_4 = \ @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@ shrpx_mruby.cc shrpx_mruby.h \ @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@ shrpx_mruby_module.cc shrpx_mruby_module.h \ @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@ shrpx_mruby_module_env.cc shrpx_mruby_module_env.h \ @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@ shrpx_mruby_module_request.cc shrpx_mruby_module_request.h \ @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@ shrpx_mruby_module_response.cc shrpx_mruby_module_response.h @ENABLE_APP_TRUE@@ENABLE_HTTP3_TRUE@am__append_5 = \ @ENABLE_APP_TRUE@@ENABLE_HTTP3_TRUE@ shrpx_quic.cc shrpx_quic.h \ @ENABLE_APP_TRUE@@ENABLE_HTTP3_TRUE@ shrpx_quic_listener.cc shrpx_quic_listener.h \ @ENABLE_APP_TRUE@@ENABLE_HTTP3_TRUE@ shrpx_quic_connection_handler.cc shrpx_quic_connection_handler.h \ @ENABLE_APP_TRUE@@ENABLE_HTTP3_TRUE@ shrpx_http3_upstream.cc shrpx_http3_upstream.h \ @ENABLE_APP_TRUE@@ENABLE_HTTP3_TRUE@ http3.cc http3.h \ @ENABLE_APP_TRUE@@ENABLE_HTTP3_TRUE@ siphash.cc siphash.h @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@am__append_6 = \ @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@ -I${top_srcdir}/third-party/mruby/include @LIBMRUBY_CFLAGS@ @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@am__append_7 = -L${top_builddir}/third-party/mruby/build/lib @LIBMRUBY_LIBS@ @ENABLE_APP_TRUE@@HAVE_NEVERBLEED_TRUE@am__append_8 = -I${top_srcdir}/third-party/neverbleed @ENABLE_APP_TRUE@@HAVE_NEVERBLEED_TRUE@am__append_9 = ${top_builddir}/third-party/libneverbleed.la @ENABLE_APP_TRUE@am__append_10 = nghttpx-unittest @ENABLE_APP_TRUE@@ENABLE_HTTP3_TRUE@am__append_11 = siphash_test.cc siphash_test.h @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@am__append_12 = \ @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@ -I${top_srcdir}/third-party/mruby/include @LIBMRUBY_CFLAGS@ @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@am__append_13 = \ @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@ -L${top_builddir}/third-party/mruby/build/lib @LIBMRUBY_LIBS@ @ENABLE_APP_TRUE@@HAVE_NEVERBLEED_TRUE@am__append_14 = -I${top_srcdir}/third-party/neverbleed @ENABLE_APP_TRUE@@HAVE_NEVERBLEED_TRUE@am__append_15 = ${top_builddir}/third-party/libneverbleed.la @ENABLE_APP_TRUE@am__append_16 = nghttpx-unittest @ENABLE_HPACK_TOOLS_TRUE@am__append_17 = inflatehd deflatehd subdir = src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = @ENABLE_APP_TRUE@am__EXEEXT_1 = nghttp$(EXEEXT) nghttpd$(EXEEXT) \ @ENABLE_APP_TRUE@ nghttpx$(EXEEXT) h2load$(EXEEXT) @ENABLE_HPACK_TOOLS_TRUE@am__EXEEXT_2 = inflatehd$(EXEEXT) \ @ENABLE_HPACK_TOOLS_TRUE@ deflatehd$(EXEEXT) am__installdirs = "$(DESTDIR)$(bindir)" @ENABLE_APP_TRUE@am__EXEEXT_3 = nghttpx-unittest$(EXEEXT) PROGRAMS = $(bin_PROGRAMS) LIBRARIES = $(noinst_LIBRARIES) ARFLAGS = cru AM_V_AR = $(am__v_AR_@AM_V@) am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libnghttpx_a_AR = $(AR) $(ARFLAGS) libnghttpx_a_LIBADD = am__libnghttpx_a_SOURCES_DIST = util.cc util.h http2.cc http2.h \ timegm.c timegm.h base64.h app_helper.cc app_helper.h tls.cc \ tls.h ssl_compat.h shrpx_config.cc shrpx_config.h \ shrpx_error.h shrpx_accept_handler.cc shrpx_accept_handler.h \ shrpx_connection_handler.cc shrpx_connection_handler.h \ shrpx_client_handler.cc shrpx_client_handler.h \ shrpx_upstream.h shrpx_http2_upstream.cc \ shrpx_http2_upstream.h shrpx_https_upstream.cc \ shrpx_https_upstream.h shrpx_downstream.cc shrpx_downstream.h \ shrpx_downstream_connection.cc shrpx_downstream_connection.h \ shrpx_http_downstream_connection.cc \ shrpx_http_downstream_connection.h \ shrpx_http2_downstream_connection.cc \ shrpx_http2_downstream_connection.h shrpx_http2_session.cc \ shrpx_http2_session.h shrpx_downstream_queue.cc \ shrpx_downstream_queue.h shrpx_log.cc shrpx_log.h \ shrpx_http.cc shrpx_http.h shrpx_io_control.cc \ shrpx_io_control.h shrpx_tls.cc shrpx_tls.h shrpx_worker.cc \ shrpx_worker.h shrpx_log_config.cc shrpx_log_config.h \ shrpx_connect_blocker.cc shrpx_connect_blocker.h \ shrpx_live_check.cc shrpx_live_check.h \ shrpx_downstream_connection_pool.cc \ shrpx_downstream_connection_pool.h shrpx_rate_limit.cc \ shrpx_rate_limit.h shrpx_connection.cc shrpx_connection.h \ shrpx_memcached_dispatcher.cc shrpx_memcached_dispatcher.h \ shrpx_memcached_connection.cc shrpx_memcached_connection.h \ shrpx_memcached_request.h shrpx_memcached_result.h \ shrpx_worker_process.cc shrpx_worker_process.h shrpx_process.h \ shrpx_signal.cc shrpx_signal.h shrpx_router.cc shrpx_router.h \ shrpx_api_downstream_connection.cc \ shrpx_api_downstream_connection.h \ shrpx_health_monitor_downstream_connection.cc \ shrpx_health_monitor_downstream_connection.h \ shrpx_null_downstream_connection.cc \ shrpx_null_downstream_connection.h shrpx_dns_resolver.cc \ shrpx_dns_resolver.h shrpx_dual_dns_resolver.cc \ shrpx_dual_dns_resolver.h shrpx_dns_tracker.cc \ shrpx_dns_tracker.h buffer.h memchunk.h template.h allocator.h \ xsi_strerror.c xsi_strerror.h shrpx_mruby.cc shrpx_mruby.h \ shrpx_mruby_module.cc shrpx_mruby_module.h \ shrpx_mruby_module_env.cc shrpx_mruby_module_env.h \ shrpx_mruby_module_request.cc shrpx_mruby_module_request.h \ shrpx_mruby_module_response.cc shrpx_mruby_module_response.h \ shrpx_quic.cc shrpx_quic.h shrpx_quic_listener.cc \ shrpx_quic_listener.h shrpx_quic_connection_handler.cc \ shrpx_quic_connection_handler.h shrpx_http3_upstream.cc \ shrpx_http3_upstream.h http3.cc http3.h siphash.cc siphash.h @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@am__objects_1 = libnghttpx_a-shrpx_mruby.$(OBJEXT) \ @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@ libnghttpx_a-shrpx_mruby_module.$(OBJEXT) \ @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@ libnghttpx_a-shrpx_mruby_module_env.$(OBJEXT) \ @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@ libnghttpx_a-shrpx_mruby_module_request.$(OBJEXT) \ @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@ libnghttpx_a-shrpx_mruby_module_response.$(OBJEXT) @ENABLE_APP_TRUE@@ENABLE_HTTP3_TRUE@am__objects_2 = libnghttpx_a-shrpx_quic.$(OBJEXT) \ @ENABLE_APP_TRUE@@ENABLE_HTTP3_TRUE@ libnghttpx_a-shrpx_quic_listener.$(OBJEXT) \ @ENABLE_APP_TRUE@@ENABLE_HTTP3_TRUE@ libnghttpx_a-shrpx_quic_connection_handler.$(OBJEXT) \ @ENABLE_APP_TRUE@@ENABLE_HTTP3_TRUE@ libnghttpx_a-shrpx_http3_upstream.$(OBJEXT) \ @ENABLE_APP_TRUE@@ENABLE_HTTP3_TRUE@ libnghttpx_a-http3.$(OBJEXT) \ @ENABLE_APP_TRUE@@ENABLE_HTTP3_TRUE@ libnghttpx_a-siphash.$(OBJEXT) @ENABLE_APP_TRUE@am__objects_3 = libnghttpx_a-util.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-http2.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-timegm.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-app_helper.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-tls.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_config.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_accept_handler.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_connection_handler.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_client_handler.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_http2_upstream.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_https_upstream.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_downstream.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_downstream_connection.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_http_downstream_connection.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_http2_downstream_connection.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_http2_session.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_downstream_queue.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_log.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_http.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_io_control.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_tls.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_worker.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_log_config.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_connect_blocker.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_live_check.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_downstream_connection_pool.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_rate_limit.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_connection.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_memcached_dispatcher.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_memcached_connection.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_worker_process.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_signal.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_router.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_api_downstream_connection.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_health_monitor_downstream_connection.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_null_downstream_connection.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_dns_resolver.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_dual_dns_resolver.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-shrpx_dns_tracker.$(OBJEXT) \ @ENABLE_APP_TRUE@ libnghttpx_a-xsi_strerror.$(OBJEXT) \ @ENABLE_APP_TRUE@ $(am__objects_1) $(am__objects_2) @ENABLE_APP_TRUE@am_libnghttpx_a_OBJECTS = $(am__objects_3) libnghttpx_a_OBJECTS = $(am_libnghttpx_a_OBJECTS) am__deflatehd_SOURCES_DIST = deflatehd.cc comp_helper.c comp_helper.h \ util.cc util.h timegm.c timegm.h @ENABLE_HPACK_TOOLS_TRUE@am__objects_4 = comp_helper.$(OBJEXT) \ @ENABLE_HPACK_TOOLS_TRUE@ util.$(OBJEXT) timegm.$(OBJEXT) @ENABLE_HPACK_TOOLS_TRUE@am_deflatehd_OBJECTS = deflatehd.$(OBJEXT) \ @ENABLE_HPACK_TOOLS_TRUE@ $(am__objects_4) deflatehd_OBJECTS = $(am_deflatehd_OBJECTS) deflatehd_LDADD = $(LDADD) deflatehd_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \ $(top_builddir)/third-party/liburlparse.la \ $(top_builddir)/third-party/libllhttp.la AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am__h2load_SOURCES_DIST = util.cc util.h http2.cc http2.h h2load.cc \ h2load.h timegm.c timegm.h tls.cc tls.h ssl_compat.h \ h2load_session.h h2load_http2_session.cc \ h2load_http2_session.h h2load_http1_session.cc \ h2load_http1_session.h h2load_http3_session.cc \ h2load_http3_session.h h2load_quic.cc h2load_quic.h @ENABLE_APP_TRUE@@ENABLE_HTTP3_TRUE@am__objects_5 = h2load_http3_session.$(OBJEXT) \ @ENABLE_APP_TRUE@@ENABLE_HTTP3_TRUE@ h2load_quic.$(OBJEXT) @ENABLE_APP_TRUE@am_h2load_OBJECTS = util.$(OBJEXT) http2.$(OBJEXT) \ @ENABLE_APP_TRUE@ h2load.$(OBJEXT) timegm.$(OBJEXT) \ @ENABLE_APP_TRUE@ tls.$(OBJEXT) h2load_http2_session.$(OBJEXT) \ @ENABLE_APP_TRUE@ h2load_http1_session.$(OBJEXT) \ @ENABLE_APP_TRUE@ $(am__objects_5) h2load_OBJECTS = $(am_h2load_OBJECTS) h2load_LDADD = $(LDADD) h2load_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \ $(top_builddir)/third-party/liburlparse.la \ $(top_builddir)/third-party/libllhttp.la am__inflatehd_SOURCES_DIST = inflatehd.cc comp_helper.c comp_helper.h \ util.cc util.h timegm.c timegm.h @ENABLE_HPACK_TOOLS_TRUE@am_inflatehd_OBJECTS = inflatehd.$(OBJEXT) \ @ENABLE_HPACK_TOOLS_TRUE@ $(am__objects_4) inflatehd_OBJECTS = $(am_inflatehd_OBJECTS) inflatehd_LDADD = $(LDADD) inflatehd_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \ $(top_builddir)/third-party/liburlparse.la \ $(top_builddir)/third-party/libllhttp.la am__nghttp_SOURCES_DIST = util.cc http2.cc timegm.c app_helper.cc \ nghttp2_gzip.c util.h http2.h timegm.h app_helper.h \ nghttp2_config.h nghttp2_gzip.h network.h nghttp.cc nghttp.h \ HtmlParser.cc HtmlParser.h tls.cc tls.h ssl_compat.h @ENABLE_APP_TRUE@am__objects_6 = util.$(OBJEXT) http2.$(OBJEXT) \ @ENABLE_APP_TRUE@ timegm.$(OBJEXT) app_helper.$(OBJEXT) \ @ENABLE_APP_TRUE@ nghttp2_gzip.$(OBJEXT) am__objects_7 = @ENABLE_APP_TRUE@@HAVE_LIBXML2_TRUE@am__objects_8 = \ @ENABLE_APP_TRUE@@HAVE_LIBXML2_TRUE@ HtmlParser.$(OBJEXT) @ENABLE_APP_TRUE@am__objects_9 = $(am__objects_8) @ENABLE_APP_TRUE@am_nghttp_OBJECTS = $(am__objects_6) $(am__objects_7) \ @ENABLE_APP_TRUE@ nghttp.$(OBJEXT) $(am__objects_9) \ @ENABLE_APP_TRUE@ $(am__objects_7) tls.$(OBJEXT) nghttp_OBJECTS = $(am_nghttp_OBJECTS) nghttp_LDADD = $(LDADD) nghttp_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \ $(top_builddir)/third-party/liburlparse.la \ $(top_builddir)/third-party/libllhttp.la am__nghttpd_SOURCES_DIST = util.cc http2.cc timegm.c app_helper.cc \ nghttp2_gzip.c util.h http2.h timegm.h app_helper.h \ nghttp2_config.h nghttp2_gzip.h network.h nghttpd.cc tls.cc \ tls.h ssl_compat.h HttpServer.cc HttpServer.h @ENABLE_APP_TRUE@am_nghttpd_OBJECTS = $(am__objects_6) \ @ENABLE_APP_TRUE@ $(am__objects_7) nghttpd.$(OBJEXT) \ @ENABLE_APP_TRUE@ tls.$(OBJEXT) HttpServer.$(OBJEXT) nghttpd_OBJECTS = $(am_nghttpd_OBJECTS) nghttpd_LDADD = $(LDADD) nghttpd_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \ $(top_builddir)/third-party/liburlparse.la \ $(top_builddir)/third-party/libllhttp.la am__nghttpx_SOURCES_DIST = shrpx.cc shrpx.h @ENABLE_APP_TRUE@am_nghttpx_OBJECTS = nghttpx-shrpx.$(OBJEXT) nghttpx_OBJECTS = $(am_nghttpx_OBJECTS) am__DEPENDENCIES_1 = $(top_builddir)/lib/libnghttp2.la \ $(top_builddir)/third-party/liburlparse.la \ $(top_builddir)/third-party/libllhttp.la am__DEPENDENCIES_2 = @ENABLE_APP_TRUE@nghttpx_DEPENDENCIES = libnghttpx.a \ @ENABLE_APP_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \ @ENABLE_APP_TRUE@ $(am__append_9) am__nghttpx_unittest_SOURCES_DIST = shrpx-unittest.cc \ shrpx_tls_test.cc shrpx_tls_test.h shrpx_downstream_test.cc \ shrpx_downstream_test.h shrpx_config_test.cc \ shrpx_config_test.h shrpx_worker_test.cc shrpx_worker_test.h \ shrpx_http_test.cc shrpx_http_test.h shrpx_router_test.cc \ shrpx_router_test.h http2_test.cc http2_test.h util_test.cc \ util_test.h nghttp2_gzip_test.c nghttp2_gzip_test.h \ nghttp2_gzip.c nghttp2_gzip.h buffer_test.cc buffer_test.h \ memchunk_test.cc memchunk_test.h template_test.cc \ template_test.h base64_test.cc base64_test.h \ $(top_srcdir)/tests/munit/munit.c \ $(top_srcdir)/tests/munit/munit.h \ $(top_srcdir)/tests/munit/munitxx.h siphash_test.cc \ siphash_test.h am__dirstamp = $(am__leading_dot)dirstamp @ENABLE_APP_TRUE@@ENABLE_HTTP3_TRUE@am__objects_10 = nghttpx_unittest-siphash_test.$(OBJEXT) @ENABLE_APP_TRUE@am_nghttpx_unittest_OBJECTS = \ @ENABLE_APP_TRUE@ nghttpx_unittest-shrpx-unittest.$(OBJEXT) \ @ENABLE_APP_TRUE@ nghttpx_unittest-shrpx_tls_test.$(OBJEXT) \ @ENABLE_APP_TRUE@ nghttpx_unittest-shrpx_downstream_test.$(OBJEXT) \ @ENABLE_APP_TRUE@ nghttpx_unittest-shrpx_config_test.$(OBJEXT) \ @ENABLE_APP_TRUE@ nghttpx_unittest-shrpx_worker_test.$(OBJEXT) \ @ENABLE_APP_TRUE@ nghttpx_unittest-shrpx_http_test.$(OBJEXT) \ @ENABLE_APP_TRUE@ nghttpx_unittest-shrpx_router_test.$(OBJEXT) \ @ENABLE_APP_TRUE@ nghttpx_unittest-http2_test.$(OBJEXT) \ @ENABLE_APP_TRUE@ nghttpx_unittest-util_test.$(OBJEXT) \ @ENABLE_APP_TRUE@ nghttpx_unittest-nghttp2_gzip_test.$(OBJEXT) \ @ENABLE_APP_TRUE@ nghttpx_unittest-nghttp2_gzip.$(OBJEXT) \ @ENABLE_APP_TRUE@ nghttpx_unittest-buffer_test.$(OBJEXT) \ @ENABLE_APP_TRUE@ nghttpx_unittest-memchunk_test.$(OBJEXT) \ @ENABLE_APP_TRUE@ nghttpx_unittest-template_test.$(OBJEXT) \ @ENABLE_APP_TRUE@ nghttpx_unittest-base64_test.$(OBJEXT) \ @ENABLE_APP_TRUE@ $(top_builddir)/tests/munit/nghttpx_unittest-munit.$(OBJEXT) \ @ENABLE_APP_TRUE@ $(am__objects_10) nghttpx_unittest_OBJECTS = $(am_nghttpx_unittest_OBJECTS) @ENABLE_APP_TRUE@nghttpx_unittest_DEPENDENCIES = libnghttpx.a \ @ENABLE_APP_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \ @ENABLE_APP_TRUE@ $(am__append_15) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = $(top_builddir)/tests/munit/$(DEPDIR)/nghttpx_unittest-munit.Po \ ./$(DEPDIR)/HtmlParser.Po ./$(DEPDIR)/HttpServer.Po \ ./$(DEPDIR)/app_helper.Po ./$(DEPDIR)/comp_helper.Po \ ./$(DEPDIR)/deflatehd.Po ./$(DEPDIR)/h2load.Po \ ./$(DEPDIR)/h2load_http1_session.Po \ ./$(DEPDIR)/h2load_http2_session.Po \ ./$(DEPDIR)/h2load_http3_session.Po ./$(DEPDIR)/h2load_quic.Po \ ./$(DEPDIR)/http2.Po ./$(DEPDIR)/inflatehd.Po \ ./$(DEPDIR)/libnghttpx_a-app_helper.Po \ ./$(DEPDIR)/libnghttpx_a-http2.Po \ ./$(DEPDIR)/libnghttpx_a-http3.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_accept_handler.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_api_downstream_connection.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_client_handler.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_config.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_connect_blocker.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_connection.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_connection_handler.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_dns_resolver.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_dns_tracker.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_downstream.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_downstream_connection.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_downstream_connection_pool.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_downstream_queue.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_dual_dns_resolver.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_health_monitor_downstream_connection.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_http.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_http2_downstream_connection.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_http2_session.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_http2_upstream.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_http3_upstream.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_http_downstream_connection.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_https_upstream.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_io_control.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_live_check.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_log.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_log_config.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_memcached_connection.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_memcached_dispatcher.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_mruby.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_env.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_request.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_response.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_null_downstream_connection.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_quic.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_quic_connection_handler.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_quic_listener.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_rate_limit.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_router.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_signal.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_tls.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_worker.Po \ ./$(DEPDIR)/libnghttpx_a-shrpx_worker_process.Po \ ./$(DEPDIR)/libnghttpx_a-siphash.Po \ ./$(DEPDIR)/libnghttpx_a-timegm.Po \ ./$(DEPDIR)/libnghttpx_a-tls.Po \ ./$(DEPDIR)/libnghttpx_a-util.Po \ ./$(DEPDIR)/libnghttpx_a-xsi_strerror.Po ./$(DEPDIR)/nghttp.Po \ ./$(DEPDIR)/nghttp2_gzip.Po ./$(DEPDIR)/nghttpd.Po \ ./$(DEPDIR)/nghttpx-shrpx.Po \ ./$(DEPDIR)/nghttpx_unittest-base64_test.Po \ ./$(DEPDIR)/nghttpx_unittest-buffer_test.Po \ ./$(DEPDIR)/nghttpx_unittest-http2_test.Po \ ./$(DEPDIR)/nghttpx_unittest-memchunk_test.Po \ ./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Po \ ./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Po \ ./$(DEPDIR)/nghttpx_unittest-shrpx-unittest.Po \ ./$(DEPDIR)/nghttpx_unittest-shrpx_config_test.Po \ ./$(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Po \ ./$(DEPDIR)/nghttpx_unittest-shrpx_http_test.Po \ ./$(DEPDIR)/nghttpx_unittest-shrpx_router_test.Po \ ./$(DEPDIR)/nghttpx_unittest-shrpx_tls_test.Po \ ./$(DEPDIR)/nghttpx_unittest-shrpx_worker_test.Po \ ./$(DEPDIR)/nghttpx_unittest-siphash_test.Po \ ./$(DEPDIR)/nghttpx_unittest-template_test.Po \ ./$(DEPDIR)/nghttpx_unittest-util_test.Po \ ./$(DEPDIR)/timegm.Po ./$(DEPDIR)/tls.Po ./$(DEPDIR)/util.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(libnghttpx_a_SOURCES) $(deflatehd_SOURCES) \ $(h2load_SOURCES) $(inflatehd_SOURCES) $(nghttp_SOURCES) \ $(nghttpd_SOURCES) $(nghttpx_SOURCES) \ $(nghttpx_unittest_SOURCES) DIST_SOURCES = $(am__libnghttpx_a_SOURCES_DIST) \ $(am__deflatehd_SOURCES_DIST) $(am__h2load_SOURCES_DIST) \ $(am__inflatehd_SOURCES_DIST) $(am__nghttp_SOURCES_DIST) \ $(am__nghttpd_SOURCES_DIST) $(am__nghttpx_SOURCES_DIST) \ $(am__nghttpx_unittest_SOURCES_DIST) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ check recheck distdir distdir-am am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` am__tty_colors_dummy = \ mgn= red= grn= lgn= blu= brg= std=; \ am__color_tests=no am__tty_colors = { \ $(am__tty_colors_dummy); \ if test "X$(AM_COLOR_TESTS)" = Xno; then \ am__color_tests=no; \ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ am__color_tests=yes; \ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ am__color_tests=yes; \ fi; \ if test $$am__color_tests = yes; then \ red=''; \ grn=''; \ lgn=''; \ blu=''; \ mgn=''; \ brg=''; \ std=''; \ fi; \ } am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__recheck_rx = ^[ ]*:recheck:[ ]* am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* # A command that, given a newline-separated list of test names on the # standard input, print the name of the tests that are to be re-run # upon "make recheck". am__list_recheck_tests = $(AWK) '{ \ recheck = 1; \ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ { \ if (rc < 0) \ { \ if ((getline line2 < ($$0 ".log")) < 0) \ recheck = 0; \ break; \ } \ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ { \ recheck = 0; \ break; \ } \ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ { \ break; \ } \ }; \ if (recheck) \ print $$0; \ close ($$0 ".trs"); \ close ($$0 ".log"); \ }' # A command that, given a newline-separated list of test names on the # standard input, create the global log from their .trs and .log files. am__create_global_log = $(AWK) ' \ function fatal(msg) \ { \ print "fatal: making $@: " msg | "cat >&2"; \ exit 1; \ } \ function rst_section(header) \ { \ print header; \ len = length(header); \ for (i = 1; i <= len; i = i + 1) \ printf "="; \ printf "\n\n"; \ } \ { \ copy_in_global_log = 1; \ global_test_result = "RUN"; \ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ { \ if (rc < 0) \ fatal("failed to read from " $$0 ".trs"); \ if (line ~ /$(am__global_test_result_rx)/) \ { \ sub("$(am__global_test_result_rx)", "", line); \ sub("[ ]*$$", "", line); \ global_test_result = line; \ } \ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ copy_in_global_log = 0; \ }; \ if (copy_in_global_log) \ { \ rst_section(global_test_result ": " $$0); \ while ((rc = (getline line < ($$0 ".log"))) != 0) \ { \ if (rc < 0) \ fatal("failed to read from " $$0 ".log"); \ print line; \ }; \ printf "\n"; \ }; \ close ($$0 ".trs"); \ close ($$0 ".log"); \ }' # Restructured Text title. am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } # Solaris 10 'make', and several other traditional 'make' implementations, # pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it # by disabling -e (using the XSI extension "set +e") if it's set. am__sh_e_setup = case $$- in *e*) set +e;; esac # Default flags passed to test drivers. am__common_driver_flags = \ --color-tests "$$am__color_tests" \ --enable-hard-errors "$$am__enable_hard_errors" \ --expect-failure "$$am__expect_failure" # To be inserted before the command running the test. Creates the # directory for the log if needed. Stores in $dir the directory # containing $f, in $tst the test, in $log the log. Executes the # developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and # passes TESTS_ENVIRONMENT. Set up options for the wrapper that # will run the test scripts (or their associated LOG_COMPILER, if # thy have one). am__check_pre = \ $(am__sh_e_setup); \ $(am__vpath_adj_setup) $(am__vpath_adj) \ $(am__tty_colors); \ srcdir=$(srcdir); export srcdir; \ case "$@" in \ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ *) am__odir=.;; \ esac; \ test "x$$am__odir" = x"." || test -d "$$am__odir" \ || $(MKDIR_P) "$$am__odir" || exit $$?; \ if test -f "./$$f"; then dir=./; \ elif test -f "$$f"; then dir=; \ else dir="$(srcdir)/"; fi; \ tst=$$dir$$f; log='$@'; \ if test -n '$(DISABLE_HARD_ERRORS)'; then \ am__enable_hard_errors=no; \ else \ am__enable_hard_errors=yes; \ fi; \ case " $(XFAIL_TESTS) " in \ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ am__expect_failure=yes;; \ *) \ am__expect_failure=no;; \ esac; \ $(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) # A shell command to get the names of the tests scripts with any registered # extension removed (i.e., equivalently, the names of the test logs, with # the '.log' extension removed). The result is saved in the shell variable # '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, # we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", # since that might cause problem with VPATH rewrites for suffix-less tests. # See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. am__set_TESTS_bases = \ bases='$(TEST_LOGS)'; \ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ bases=`echo $$bases` AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)' RECHECK_LOGS = $(TEST_LOGS) TEST_SUITE_LOG = test-suite.log TEST_EXTENSIONS = @EXEEXT@ .test LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) am__set_b = \ case '$@' in \ */*) \ case '$*' in \ */*) b='$*';; \ *) b=`echo '$@' | sed 's/\.log$$//'`; \ esac;; \ *) \ b='$*';; \ esac am__test_logs1 = $(TESTS:=.log) am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) TEST_LOGS = $(am__test_logs2:.test.log=.log) TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ $(TEST_LOG_FLAGS) DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp \ $(top_srcdir)/test-driver DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPLDFLAGS = @APPLDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BPFCFLAGS = @BPFCFLAGS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CXX = @CXX@ CXX1XCXXFLAGS = @CXX1XCXXFLAGS@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXTRABPFCFLAGS = @EXTRABPFCFLAGS@ EXTRACFLAG = @EXTRACFLAG@ EXTRA_DEFS = @EXTRA_DEFS@ FGREP = @FGREP@ FILECMD = @FILECMD@ GREP = @GREP@ HAVE_CXX20 = @HAVE_CXX20@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ JANSSON_CFLAGS = @JANSSON_CFLAGS@ JANSSON_LIBS = @JANSSON_LIBS@ JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ JEMALLOC_LIBS = @JEMALLOC_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBBPF_CFLAGS = @LIBBPF_CFLAGS@ LIBBPF_LIBS = @LIBBPF_LIBS@ LIBBROTLIDEC_CFLAGS = @LIBBROTLIDEC_CFLAGS@ LIBBROTLIDEC_LIBS = @LIBBROTLIDEC_LIBS@ LIBBROTLIENC_CFLAGS = @LIBBROTLIENC_CFLAGS@ LIBBROTLIENC_LIBS = @LIBBROTLIENC_LIBS@ LIBCARES_CFLAGS = @LIBCARES_CFLAGS@ LIBCARES_LIBS = @LIBCARES_LIBS@ LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ LIBEV_CFLAGS = @LIBEV_CFLAGS@ LIBEV_LIBS = @LIBEV_LIBS@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@ LIBMRUBY_LIBS = @LIBMRUBY_LIBS@ LIBNGHTTP3_CFLAGS = @LIBNGHTTP3_CFLAGS@ LIBNGHTTP3_LIBS = @LIBNGHTTP3_LIBS@ LIBNGTCP2_CFLAGS = @LIBNGTCP2_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS = @LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_LIBS = @LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS = @LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS@ LIBNGTCP2_CRYPTO_LIBRESSL_LIBS = @LIBNGTCP2_CRYPTO_LIBRESSL_LIBS@ LIBNGTCP2_CRYPTO_OSSL_CFLAGS = @LIBNGTCP2_CRYPTO_OSSL_CFLAGS@ LIBNGTCP2_CRYPTO_OSSL_LIBS = @LIBNGTCP2_CRYPTO_OSSL_LIBS@ LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS = @LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ LIBNGTCP2_CRYPTO_QUICTLS_LIBS = @LIBNGTCP2_CRYPTO_QUICTLS_LIBS@ LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS = @LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS@ LIBNGTCP2_CRYPTO_WOLFSSL_LIBS = @LIBNGTCP2_CRYPTO_WOLFSSL_LIBS@ LIBNGTCP2_LIBS = @LIBNGTCP2_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBTOOL_LDFLAGS = @LIBTOOL_LDFLAGS@ LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ LIBXML2_LIBS = @LIBXML2_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_AGE = @LT_AGE@ LT_CURRENT = @LT_CURRENT@ LT_REVISION = @LT_REVISION@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ SYSTEMD_LIBS = @SYSTEMD_LIBS@ TESTLDADD = @TESTLDADD@ VERSION = @VERSION@ WARNCFLAGS = @WARNCFLAGS@ WARNCXXFLAGS = @WARNCXXFLAGS@ WOLFSSL_CFLAGS = @WOLFSSL_CFLAGS@ WOLFSSL_LIBS = @WOLFSSL_LIBS@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SUBDIRS = testdata EXTRA_DIST = \ CMakeLists.txt \ test.example.com.pem \ test.nghttp2.org.pem AM_CFLAGS = $(WARNCFLAGS) AM_CXXFLAGS = $(WARNCXXFLAGS) $(CXX1XCXXFLAGS) AM_CPPFLAGS = \ -DPKGDATADIR='"$(pkgdatadir)"' \ -DPKGLIBDIR='"$(pkglibdir)"' \ -I$(top_srcdir)/lib/includes \ -I$(top_builddir)/lib/includes \ -I$(top_srcdir)/lib \ -I$(top_srcdir)/third-party/urlparse \ -I$(top_srcdir)/third-party/llhttp/include \ @JEMALLOC_CFLAGS@ \ @LIBXML2_CFLAGS@ \ @LIBEV_CFLAGS@ \ @LIBNGHTTP3_CFLAGS@ \ @LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS@ \ @LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ \ @LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS@ \ @LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ \ @LIBNGTCP2_CRYPTO_OSSL_CFLAGS@ \ @LIBNGTCP2_CFLAGS@ \ @WOLFSSL_CFLAGS@ \ @OPENSSL_CFLAGS@ \ @LIBCARES_CFLAGS@ \ @JANSSON_CFLAGS@ \ @LIBBPF_CFLAGS@ \ @ZLIB_CFLAGS@ \ @LIBBROTLIENC_CFLAGS@ \ @LIBBROTLIDEC_CFLAGS@ \ @EXTRA_DEFS@ \ @DEFS@ AM_LDFLAGS = @LIBTOOL_LDFLAGS@ LDADD = $(top_builddir)/lib/libnghttp2.la \ $(top_builddir)/third-party/liburlparse.la \ $(top_builddir)/third-party/libllhttp.la \ @JEMALLOC_LIBS@ \ @LIBXML2_LIBS@ \ @LIBEV_LIBS@ \ @LIBNGHTTP3_LIBS@ \ @LIBNGTCP2_CRYPTO_WOLFSSL_LIBS@ \ @LIBNGTCP2_CRYPTO_QUICTLS_LIBS@ \ @LIBNGTCP2_CRYPTO_LIBRESSL_LIBS@ \ @LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ \ @LIBNGTCP2_CRYPTO_OSSL_LIBS@ \ @LIBNGTCP2_LIBS@ \ @WOLFSSL_LIBS@ \ @OPENSSL_LIBS@ \ @LIBCARES_LIBS@ \ @SYSTEMD_LIBS@ \ @JANSSON_LIBS@ \ @LIBBPF_LIBS@ \ @ZLIB_LIBS@ \ @LIBBROTLIENC_LIBS@ \ @LIBBROTLIDEC_LIBS@ \ @APPLDFLAGS@ @ENABLE_APP_TRUE@HELPER_OBJECTS = util.cc \ @ENABLE_APP_TRUE@ http2.cc timegm.c app_helper.cc nghttp2_gzip.c @ENABLE_APP_TRUE@HELPER_HFILES = util.h \ @ENABLE_APP_TRUE@ http2.h timegm.h app_helper.h nghttp2_config.h \ @ENABLE_APP_TRUE@ nghttp2_gzip.h network.h @ENABLE_APP_TRUE@HTML_PARSER_OBJECTS = $(am__append_2) @ENABLE_APP_TRUE@HTML_PARSER_HFILES = HtmlParser.h @ENABLE_APP_TRUE@nghttp_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttp.cc nghttp.h \ @ENABLE_APP_TRUE@ ${HTML_PARSER_OBJECTS} ${HTML_PARSER_HFILES} \ @ENABLE_APP_TRUE@ tls.cc tls.h ssl_compat.h @ENABLE_APP_TRUE@nghttpd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttpd.cc \ @ENABLE_APP_TRUE@ tls.cc tls.h ssl_compat.h \ @ENABLE_APP_TRUE@ HttpServer.cc HttpServer.h @ENABLE_APP_TRUE@h2load_SOURCES = util.cc util.h http2.cc http2.h \ @ENABLE_APP_TRUE@ h2load.cc h2load.h timegm.c timegm.h tls.cc \ @ENABLE_APP_TRUE@ tls.h ssl_compat.h h2load_session.h \ @ENABLE_APP_TRUE@ h2load_http2_session.cc \ @ENABLE_APP_TRUE@ h2load_http2_session.h \ @ENABLE_APP_TRUE@ h2load_http1_session.cc \ @ENABLE_APP_TRUE@ h2load_http1_session.h $(am__append_3) @ENABLE_APP_TRUE@NGHTTPX_SRCS = util.cc util.h http2.cc http2.h \ @ENABLE_APP_TRUE@ timegm.c timegm.h base64.h app_helper.cc \ @ENABLE_APP_TRUE@ app_helper.h tls.cc tls.h ssl_compat.h \ @ENABLE_APP_TRUE@ shrpx_config.cc shrpx_config.h shrpx_error.h \ @ENABLE_APP_TRUE@ shrpx_accept_handler.cc \ @ENABLE_APP_TRUE@ shrpx_accept_handler.h \ @ENABLE_APP_TRUE@ shrpx_connection_handler.cc \ @ENABLE_APP_TRUE@ shrpx_connection_handler.h \ @ENABLE_APP_TRUE@ shrpx_client_handler.cc \ @ENABLE_APP_TRUE@ shrpx_client_handler.h shrpx_upstream.h \ @ENABLE_APP_TRUE@ shrpx_http2_upstream.cc \ @ENABLE_APP_TRUE@ shrpx_http2_upstream.h \ @ENABLE_APP_TRUE@ shrpx_https_upstream.cc \ @ENABLE_APP_TRUE@ shrpx_https_upstream.h shrpx_downstream.cc \ @ENABLE_APP_TRUE@ shrpx_downstream.h \ @ENABLE_APP_TRUE@ shrpx_downstream_connection.cc \ @ENABLE_APP_TRUE@ shrpx_downstream_connection.h \ @ENABLE_APP_TRUE@ shrpx_http_downstream_connection.cc \ @ENABLE_APP_TRUE@ shrpx_http_downstream_connection.h \ @ENABLE_APP_TRUE@ shrpx_http2_downstream_connection.cc \ @ENABLE_APP_TRUE@ shrpx_http2_downstream_connection.h \ @ENABLE_APP_TRUE@ shrpx_http2_session.cc shrpx_http2_session.h \ @ENABLE_APP_TRUE@ shrpx_downstream_queue.cc \ @ENABLE_APP_TRUE@ shrpx_downstream_queue.h shrpx_log.cc \ @ENABLE_APP_TRUE@ shrpx_log.h shrpx_http.cc shrpx_http.h \ @ENABLE_APP_TRUE@ shrpx_io_control.cc shrpx_io_control.h \ @ENABLE_APP_TRUE@ shrpx_tls.cc shrpx_tls.h shrpx_worker.cc \ @ENABLE_APP_TRUE@ shrpx_worker.h shrpx_log_config.cc \ @ENABLE_APP_TRUE@ shrpx_log_config.h shrpx_connect_blocker.cc \ @ENABLE_APP_TRUE@ shrpx_connect_blocker.h shrpx_live_check.cc \ @ENABLE_APP_TRUE@ shrpx_live_check.h \ @ENABLE_APP_TRUE@ shrpx_downstream_connection_pool.cc \ @ENABLE_APP_TRUE@ shrpx_downstream_connection_pool.h \ @ENABLE_APP_TRUE@ shrpx_rate_limit.cc shrpx_rate_limit.h \ @ENABLE_APP_TRUE@ shrpx_connection.cc shrpx_connection.h \ @ENABLE_APP_TRUE@ shrpx_memcached_dispatcher.cc \ @ENABLE_APP_TRUE@ shrpx_memcached_dispatcher.h \ @ENABLE_APP_TRUE@ shrpx_memcached_connection.cc \ @ENABLE_APP_TRUE@ shrpx_memcached_connection.h \ @ENABLE_APP_TRUE@ shrpx_memcached_request.h \ @ENABLE_APP_TRUE@ shrpx_memcached_result.h \ @ENABLE_APP_TRUE@ shrpx_worker_process.cc \ @ENABLE_APP_TRUE@ shrpx_worker_process.h shrpx_process.h \ @ENABLE_APP_TRUE@ shrpx_signal.cc shrpx_signal.h \ @ENABLE_APP_TRUE@ shrpx_router.cc shrpx_router.h \ @ENABLE_APP_TRUE@ shrpx_api_downstream_connection.cc \ @ENABLE_APP_TRUE@ shrpx_api_downstream_connection.h \ @ENABLE_APP_TRUE@ shrpx_health_monitor_downstream_connection.cc \ @ENABLE_APP_TRUE@ shrpx_health_monitor_downstream_connection.h \ @ENABLE_APP_TRUE@ shrpx_null_downstream_connection.cc \ @ENABLE_APP_TRUE@ shrpx_null_downstream_connection.h \ @ENABLE_APP_TRUE@ shrpx_dns_resolver.cc shrpx_dns_resolver.h \ @ENABLE_APP_TRUE@ shrpx_dual_dns_resolver.cc \ @ENABLE_APP_TRUE@ shrpx_dual_dns_resolver.h \ @ENABLE_APP_TRUE@ shrpx_dns_tracker.cc shrpx_dns_tracker.h \ @ENABLE_APP_TRUE@ buffer.h memchunk.h template.h allocator.h \ @ENABLE_APP_TRUE@ xsi_strerror.c xsi_strerror.h $(am__append_4) \ @ENABLE_APP_TRUE@ $(am__append_5) @ENABLE_APP_TRUE@noinst_LIBRARIES = libnghttpx.a @ENABLE_APP_TRUE@libnghttpx_a_SOURCES = ${NGHTTPX_SRCS} @ENABLE_APP_TRUE@libnghttpx_a_CPPFLAGS = ${AM_CPPFLAGS} \ @ENABLE_APP_TRUE@ $(am__append_6) $(am__append_8) @ENABLE_APP_TRUE@nghttpx_SOURCES = shrpx.cc shrpx.h @ENABLE_APP_TRUE@nghttpx_CPPFLAGS = ${libnghttpx_a_CPPFLAGS} @ENABLE_APP_TRUE@nghttpx_LDADD = libnghttpx.a ${LDADD} $(am__append_7) \ @ENABLE_APP_TRUE@ $(am__append_9) @ENABLE_APP_TRUE@nghttpx_unittest_SOURCES = shrpx-unittest.cc \ @ENABLE_APP_TRUE@ shrpx_tls_test.cc shrpx_tls_test.h \ @ENABLE_APP_TRUE@ shrpx_downstream_test.cc \ @ENABLE_APP_TRUE@ shrpx_downstream_test.h shrpx_config_test.cc \ @ENABLE_APP_TRUE@ shrpx_config_test.h shrpx_worker_test.cc \ @ENABLE_APP_TRUE@ shrpx_worker_test.h shrpx_http_test.cc \ @ENABLE_APP_TRUE@ shrpx_http_test.h shrpx_router_test.cc \ @ENABLE_APP_TRUE@ shrpx_router_test.h http2_test.cc \ @ENABLE_APP_TRUE@ http2_test.h util_test.cc util_test.h \ @ENABLE_APP_TRUE@ nghttp2_gzip_test.c nghttp2_gzip_test.h \ @ENABLE_APP_TRUE@ nghttp2_gzip.c nghttp2_gzip.h buffer_test.cc \ @ENABLE_APP_TRUE@ buffer_test.h memchunk_test.cc \ @ENABLE_APP_TRUE@ memchunk_test.h template_test.cc \ @ENABLE_APP_TRUE@ template_test.h base64_test.cc base64_test.h \ @ENABLE_APP_TRUE@ $(top_srcdir)/tests/munit/munit.c \ @ENABLE_APP_TRUE@ $(top_srcdir)/tests/munit/munit.h \ @ENABLE_APP_TRUE@ $(top_srcdir)/tests/munit/munitxx.h \ @ENABLE_APP_TRUE@ $(am__append_11) @ENABLE_APP_TRUE@nghttpx_unittest_CPPFLAGS = ${AM_CPPFLAGS} \ @ENABLE_APP_TRUE@ -I$(top_srcdir)/tests/munit \ @ENABLE_APP_TRUE@ -DNGHTTP2_SRC_DIR=\"$(top_srcdir)/src\" \ @ENABLE_APP_TRUE@ $(am__append_12) $(am__append_14) @ENABLE_APP_TRUE@nghttpx_unittest_LDADD = libnghttpx.a ${LDADD} \ @ENABLE_APP_TRUE@ @TESTLDADD@ $(am__append_13) $(am__append_15) @ENABLE_HPACK_TOOLS_TRUE@HPACK_TOOLS_COMMON_SRCS = \ @ENABLE_HPACK_TOOLS_TRUE@ comp_helper.c comp_helper.h \ @ENABLE_HPACK_TOOLS_TRUE@ util.cc util.h \ @ENABLE_HPACK_TOOLS_TRUE@ timegm.c timegm.h @ENABLE_HPACK_TOOLS_TRUE@inflatehd_SOURCES = inflatehd.cc $(HPACK_TOOLS_COMMON_SRCS) @ENABLE_HPACK_TOOLS_TRUE@deflatehd_SOURCES = deflatehd.cc $(HPACK_TOOLS_COMMON_SRCS) all: all-recursive .SUFFIXES: .SUFFIXES: .c .cc .lo .log .o .obj .test .test$(EXEEXT) .trs $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu src/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list clean-checkPROGRAMS: @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libnghttpx.a: $(libnghttpx_a_OBJECTS) $(libnghttpx_a_DEPENDENCIES) $(EXTRA_libnghttpx_a_DEPENDENCIES) $(AM_V_at)-rm -f libnghttpx.a $(AM_V_AR)$(libnghttpx_a_AR) libnghttpx.a $(libnghttpx_a_OBJECTS) $(libnghttpx_a_LIBADD) $(AM_V_at)$(RANLIB) libnghttpx.a deflatehd$(EXEEXT): $(deflatehd_OBJECTS) $(deflatehd_DEPENDENCIES) $(EXTRA_deflatehd_DEPENDENCIES) @rm -f deflatehd$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(deflatehd_OBJECTS) $(deflatehd_LDADD) $(LIBS) h2load$(EXEEXT): $(h2load_OBJECTS) $(h2load_DEPENDENCIES) $(EXTRA_h2load_DEPENDENCIES) @rm -f h2load$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(h2load_OBJECTS) $(h2load_LDADD) $(LIBS) inflatehd$(EXEEXT): $(inflatehd_OBJECTS) $(inflatehd_DEPENDENCIES) $(EXTRA_inflatehd_DEPENDENCIES) @rm -f inflatehd$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(inflatehd_OBJECTS) $(inflatehd_LDADD) $(LIBS) nghttp$(EXEEXT): $(nghttp_OBJECTS) $(nghttp_DEPENDENCIES) $(EXTRA_nghttp_DEPENDENCIES) @rm -f nghttp$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(nghttp_OBJECTS) $(nghttp_LDADD) $(LIBS) nghttpd$(EXEEXT): $(nghttpd_OBJECTS) $(nghttpd_DEPENDENCIES) $(EXTRA_nghttpd_DEPENDENCIES) @rm -f nghttpd$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(nghttpd_OBJECTS) $(nghttpd_LDADD) $(LIBS) nghttpx$(EXEEXT): $(nghttpx_OBJECTS) $(nghttpx_DEPENDENCIES) $(EXTRA_nghttpx_DEPENDENCIES) @rm -f nghttpx$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(nghttpx_OBJECTS) $(nghttpx_LDADD) $(LIBS) $(top_builddir)/tests/munit/$(am__dirstamp): @$(MKDIR_P) $(top_builddir)/tests/munit @: > $(top_builddir)/tests/munit/$(am__dirstamp) $(top_builddir)/tests/munit/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) $(top_builddir)/tests/munit/$(DEPDIR) @: > $(top_builddir)/tests/munit/$(DEPDIR)/$(am__dirstamp) $(top_builddir)/tests/munit/nghttpx_unittest-munit.$(OBJEXT): \ $(top_builddir)/tests/munit/$(am__dirstamp) \ $(top_builddir)/tests/munit/$(DEPDIR)/$(am__dirstamp) nghttpx-unittest$(EXEEXT): $(nghttpx_unittest_OBJECTS) $(nghttpx_unittest_DEPENDENCIES) $(EXTRA_nghttpx_unittest_DEPENDENCIES) @rm -f nghttpx-unittest$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(nghttpx_unittest_OBJECTS) $(nghttpx_unittest_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f $(top_builddir)/tests/munit/*.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@$(top_builddir)/tests/munit/$(DEPDIR)/nghttpx_unittest-munit.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HtmlParser.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpServer.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app_helper.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/comp_helper.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/deflatehd.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load_http1_session.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load_http2_session.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load_http3_session.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load_quic.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http2.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inflatehd.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-app_helper.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-http2.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-http3.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_accept_handler.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_api_downstream_connection.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_client_handler.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_config.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_connect_blocker.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_connection.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_connection_handler.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_dns_resolver.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_dns_tracker.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_downstream.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_downstream_connection.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_downstream_connection_pool.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_downstream_queue.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_dual_dns_resolver.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_health_monitor_downstream_connection.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_http.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_http2_downstream_connection.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_http2_session.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_http2_upstream.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_http3_upstream.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_http_downstream_connection.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_https_upstream.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_io_control.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_live_check.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_log.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_log_config.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_memcached_connection.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_memcached_dispatcher.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_mruby.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_env.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_request.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_response.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_null_downstream_connection.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_quic.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_quic_connection_handler.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_quic_listener.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_rate_limit.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_router.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_signal.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_tls.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_worker.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_worker_process.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-siphash.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-timegm.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-tls.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-util.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-xsi_strerror.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_gzip.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpd.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx-shrpx.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-base64_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-buffer_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-http2_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-memchunk_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx-unittest.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_config_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_http_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_router_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_tls_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_worker_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-siphash_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-template_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-util_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timegm.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< libnghttpx_a-timegm.o: timegm.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnghttpx_a-timegm.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-timegm.Tpo -c -o libnghttpx_a-timegm.o `test -f 'timegm.c' || echo '$(srcdir)/'`timegm.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-timegm.Tpo $(DEPDIR)/libnghttpx_a-timegm.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timegm.c' object='libnghttpx_a-timegm.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnghttpx_a-timegm.o `test -f 'timegm.c' || echo '$(srcdir)/'`timegm.c libnghttpx_a-timegm.obj: timegm.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnghttpx_a-timegm.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-timegm.Tpo -c -o libnghttpx_a-timegm.obj `if test -f 'timegm.c'; then $(CYGPATH_W) 'timegm.c'; else $(CYGPATH_W) '$(srcdir)/timegm.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-timegm.Tpo $(DEPDIR)/libnghttpx_a-timegm.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timegm.c' object='libnghttpx_a-timegm.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnghttpx_a-timegm.obj `if test -f 'timegm.c'; then $(CYGPATH_W) 'timegm.c'; else $(CYGPATH_W) '$(srcdir)/timegm.c'; fi` libnghttpx_a-xsi_strerror.o: xsi_strerror.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnghttpx_a-xsi_strerror.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-xsi_strerror.Tpo -c -o libnghttpx_a-xsi_strerror.o `test -f 'xsi_strerror.c' || echo '$(srcdir)/'`xsi_strerror.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-xsi_strerror.Tpo $(DEPDIR)/libnghttpx_a-xsi_strerror.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xsi_strerror.c' object='libnghttpx_a-xsi_strerror.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnghttpx_a-xsi_strerror.o `test -f 'xsi_strerror.c' || echo '$(srcdir)/'`xsi_strerror.c libnghttpx_a-xsi_strerror.obj: xsi_strerror.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnghttpx_a-xsi_strerror.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-xsi_strerror.Tpo -c -o libnghttpx_a-xsi_strerror.obj `if test -f 'xsi_strerror.c'; then $(CYGPATH_W) 'xsi_strerror.c'; else $(CYGPATH_W) '$(srcdir)/xsi_strerror.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-xsi_strerror.Tpo $(DEPDIR)/libnghttpx_a-xsi_strerror.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xsi_strerror.c' object='libnghttpx_a-xsi_strerror.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnghttpx_a-xsi_strerror.obj `if test -f 'xsi_strerror.c'; then $(CYGPATH_W) 'xsi_strerror.c'; else $(CYGPATH_W) '$(srcdir)/xsi_strerror.c'; fi` nghttpx_unittest-nghttp2_gzip_test.o: nghttp2_gzip_test.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nghttpx_unittest-nghttp2_gzip_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Tpo -c -o nghttpx_unittest-nghttp2_gzip_test.o `test -f 'nghttp2_gzip_test.c' || echo '$(srcdir)/'`nghttp2_gzip_test.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Tpo $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nghttp2_gzip_test.c' object='nghttpx_unittest-nghttp2_gzip_test.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nghttpx_unittest-nghttp2_gzip_test.o `test -f 'nghttp2_gzip_test.c' || echo '$(srcdir)/'`nghttp2_gzip_test.c nghttpx_unittest-nghttp2_gzip_test.obj: nghttp2_gzip_test.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nghttpx_unittest-nghttp2_gzip_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Tpo -c -o nghttpx_unittest-nghttp2_gzip_test.obj `if test -f 'nghttp2_gzip_test.c'; then $(CYGPATH_W) 'nghttp2_gzip_test.c'; else $(CYGPATH_W) '$(srcdir)/nghttp2_gzip_test.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Tpo $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nghttp2_gzip_test.c' object='nghttpx_unittest-nghttp2_gzip_test.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nghttpx_unittest-nghttp2_gzip_test.obj `if test -f 'nghttp2_gzip_test.c'; then $(CYGPATH_W) 'nghttp2_gzip_test.c'; else $(CYGPATH_W) '$(srcdir)/nghttp2_gzip_test.c'; fi` nghttpx_unittest-nghttp2_gzip.o: nghttp2_gzip.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nghttpx_unittest-nghttp2_gzip.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Tpo -c -o nghttpx_unittest-nghttp2_gzip.o `test -f 'nghttp2_gzip.c' || echo '$(srcdir)/'`nghttp2_gzip.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Tpo $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nghttp2_gzip.c' object='nghttpx_unittest-nghttp2_gzip.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nghttpx_unittest-nghttp2_gzip.o `test -f 'nghttp2_gzip.c' || echo '$(srcdir)/'`nghttp2_gzip.c nghttpx_unittest-nghttp2_gzip.obj: nghttp2_gzip.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nghttpx_unittest-nghttp2_gzip.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Tpo -c -o nghttpx_unittest-nghttp2_gzip.obj `if test -f 'nghttp2_gzip.c'; then $(CYGPATH_W) 'nghttp2_gzip.c'; else $(CYGPATH_W) '$(srcdir)/nghttp2_gzip.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Tpo $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nghttp2_gzip.c' object='nghttpx_unittest-nghttp2_gzip.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nghttpx_unittest-nghttp2_gzip.obj `if test -f 'nghttp2_gzip.c'; then $(CYGPATH_W) 'nghttp2_gzip.c'; else $(CYGPATH_W) '$(srcdir)/nghttp2_gzip.c'; fi` $(top_builddir)/tests/munit/nghttpx_unittest-munit.o: $(top_builddir)/tests/munit/munit.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT $(top_builddir)/tests/munit/nghttpx_unittest-munit.o -MD -MP -MF $(top_builddir)/tests/munit/$(DEPDIR)/nghttpx_unittest-munit.Tpo -c -o $(top_builddir)/tests/munit/nghttpx_unittest-munit.o `test -f '$(top_builddir)/tests/munit/munit.c' || echo '$(srcdir)/'`$(top_builddir)/tests/munit/munit.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(top_builddir)/tests/munit/$(DEPDIR)/nghttpx_unittest-munit.Tpo $(top_builddir)/tests/munit/$(DEPDIR)/nghttpx_unittest-munit.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_builddir)/tests/munit/munit.c' object='$(top_builddir)/tests/munit/nghttpx_unittest-munit.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o $(top_builddir)/tests/munit/nghttpx_unittest-munit.o `test -f '$(top_builddir)/tests/munit/munit.c' || echo '$(srcdir)/'`$(top_builddir)/tests/munit/munit.c $(top_builddir)/tests/munit/nghttpx_unittest-munit.obj: $(top_builddir)/tests/munit/munit.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT $(top_builddir)/tests/munit/nghttpx_unittest-munit.obj -MD -MP -MF $(top_builddir)/tests/munit/$(DEPDIR)/nghttpx_unittest-munit.Tpo -c -o $(top_builddir)/tests/munit/nghttpx_unittest-munit.obj `if test -f '$(top_builddir)/tests/munit/munit.c'; then $(CYGPATH_W) '$(top_builddir)/tests/munit/munit.c'; else $(CYGPATH_W) '$(srcdir)/$(top_builddir)/tests/munit/munit.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(top_builddir)/tests/munit/$(DEPDIR)/nghttpx_unittest-munit.Tpo $(top_builddir)/tests/munit/$(DEPDIR)/nghttpx_unittest-munit.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_builddir)/tests/munit/munit.c' object='$(top_builddir)/tests/munit/nghttpx_unittest-munit.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o $(top_builddir)/tests/munit/nghttpx_unittest-munit.obj `if test -f '$(top_builddir)/tests/munit/munit.c'; then $(CYGPATH_W) '$(top_builddir)/tests/munit/munit.c'; else $(CYGPATH_W) '$(srcdir)/$(top_builddir)/tests/munit/munit.c'; fi` .cc.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cc.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cc.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< libnghttpx_a-util.o: util.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-util.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-util.Tpo -c -o libnghttpx_a-util.o `test -f 'util.cc' || echo '$(srcdir)/'`util.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-util.Tpo $(DEPDIR)/libnghttpx_a-util.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='util.cc' object='libnghttpx_a-util.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-util.o `test -f 'util.cc' || echo '$(srcdir)/'`util.cc libnghttpx_a-util.obj: util.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-util.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-util.Tpo -c -o libnghttpx_a-util.obj `if test -f 'util.cc'; then $(CYGPATH_W) 'util.cc'; else $(CYGPATH_W) '$(srcdir)/util.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-util.Tpo $(DEPDIR)/libnghttpx_a-util.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='util.cc' object='libnghttpx_a-util.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-util.obj `if test -f 'util.cc'; then $(CYGPATH_W) 'util.cc'; else $(CYGPATH_W) '$(srcdir)/util.cc'; fi` libnghttpx_a-http2.o: http2.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-http2.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-http2.Tpo -c -o libnghttpx_a-http2.o `test -f 'http2.cc' || echo '$(srcdir)/'`http2.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-http2.Tpo $(DEPDIR)/libnghttpx_a-http2.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='http2.cc' object='libnghttpx_a-http2.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-http2.o `test -f 'http2.cc' || echo '$(srcdir)/'`http2.cc libnghttpx_a-http2.obj: http2.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-http2.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-http2.Tpo -c -o libnghttpx_a-http2.obj `if test -f 'http2.cc'; then $(CYGPATH_W) 'http2.cc'; else $(CYGPATH_W) '$(srcdir)/http2.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-http2.Tpo $(DEPDIR)/libnghttpx_a-http2.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='http2.cc' object='libnghttpx_a-http2.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-http2.obj `if test -f 'http2.cc'; then $(CYGPATH_W) 'http2.cc'; else $(CYGPATH_W) '$(srcdir)/http2.cc'; fi` libnghttpx_a-app_helper.o: app_helper.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-app_helper.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-app_helper.Tpo -c -o libnghttpx_a-app_helper.o `test -f 'app_helper.cc' || echo '$(srcdir)/'`app_helper.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-app_helper.Tpo $(DEPDIR)/libnghttpx_a-app_helper.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='app_helper.cc' object='libnghttpx_a-app_helper.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-app_helper.o `test -f 'app_helper.cc' || echo '$(srcdir)/'`app_helper.cc libnghttpx_a-app_helper.obj: app_helper.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-app_helper.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-app_helper.Tpo -c -o libnghttpx_a-app_helper.obj `if test -f 'app_helper.cc'; then $(CYGPATH_W) 'app_helper.cc'; else $(CYGPATH_W) '$(srcdir)/app_helper.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-app_helper.Tpo $(DEPDIR)/libnghttpx_a-app_helper.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='app_helper.cc' object='libnghttpx_a-app_helper.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-app_helper.obj `if test -f 'app_helper.cc'; then $(CYGPATH_W) 'app_helper.cc'; else $(CYGPATH_W) '$(srcdir)/app_helper.cc'; fi` libnghttpx_a-tls.o: tls.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-tls.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-tls.Tpo -c -o libnghttpx_a-tls.o `test -f 'tls.cc' || echo '$(srcdir)/'`tls.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-tls.Tpo $(DEPDIR)/libnghttpx_a-tls.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tls.cc' object='libnghttpx_a-tls.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-tls.o `test -f 'tls.cc' || echo '$(srcdir)/'`tls.cc libnghttpx_a-tls.obj: tls.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-tls.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-tls.Tpo -c -o libnghttpx_a-tls.obj `if test -f 'tls.cc'; then $(CYGPATH_W) 'tls.cc'; else $(CYGPATH_W) '$(srcdir)/tls.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-tls.Tpo $(DEPDIR)/libnghttpx_a-tls.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tls.cc' object='libnghttpx_a-tls.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-tls.obj `if test -f 'tls.cc'; then $(CYGPATH_W) 'tls.cc'; else $(CYGPATH_W) '$(srcdir)/tls.cc'; fi` libnghttpx_a-shrpx_config.o: shrpx_config.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_config.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_config.Tpo -c -o libnghttpx_a-shrpx_config.o `test -f 'shrpx_config.cc' || echo '$(srcdir)/'`shrpx_config.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_config.Tpo $(DEPDIR)/libnghttpx_a-shrpx_config.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_config.cc' object='libnghttpx_a-shrpx_config.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_config.o `test -f 'shrpx_config.cc' || echo '$(srcdir)/'`shrpx_config.cc libnghttpx_a-shrpx_config.obj: shrpx_config.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_config.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_config.Tpo -c -o libnghttpx_a-shrpx_config.obj `if test -f 'shrpx_config.cc'; then $(CYGPATH_W) 'shrpx_config.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_config.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_config.Tpo $(DEPDIR)/libnghttpx_a-shrpx_config.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_config.cc' object='libnghttpx_a-shrpx_config.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_config.obj `if test -f 'shrpx_config.cc'; then $(CYGPATH_W) 'shrpx_config.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_config.cc'; fi` libnghttpx_a-shrpx_accept_handler.o: shrpx_accept_handler.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_accept_handler.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_accept_handler.Tpo -c -o libnghttpx_a-shrpx_accept_handler.o `test -f 'shrpx_accept_handler.cc' || echo '$(srcdir)/'`shrpx_accept_handler.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_accept_handler.Tpo $(DEPDIR)/libnghttpx_a-shrpx_accept_handler.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_accept_handler.cc' object='libnghttpx_a-shrpx_accept_handler.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_accept_handler.o `test -f 'shrpx_accept_handler.cc' || echo '$(srcdir)/'`shrpx_accept_handler.cc libnghttpx_a-shrpx_accept_handler.obj: shrpx_accept_handler.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_accept_handler.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_accept_handler.Tpo -c -o libnghttpx_a-shrpx_accept_handler.obj `if test -f 'shrpx_accept_handler.cc'; then $(CYGPATH_W) 'shrpx_accept_handler.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_accept_handler.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_accept_handler.Tpo $(DEPDIR)/libnghttpx_a-shrpx_accept_handler.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_accept_handler.cc' object='libnghttpx_a-shrpx_accept_handler.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_accept_handler.obj `if test -f 'shrpx_accept_handler.cc'; then $(CYGPATH_W) 'shrpx_accept_handler.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_accept_handler.cc'; fi` libnghttpx_a-shrpx_connection_handler.o: shrpx_connection_handler.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_connection_handler.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_connection_handler.Tpo -c -o libnghttpx_a-shrpx_connection_handler.o `test -f 'shrpx_connection_handler.cc' || echo '$(srcdir)/'`shrpx_connection_handler.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_connection_handler.Tpo $(DEPDIR)/libnghttpx_a-shrpx_connection_handler.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_connection_handler.cc' object='libnghttpx_a-shrpx_connection_handler.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_connection_handler.o `test -f 'shrpx_connection_handler.cc' || echo '$(srcdir)/'`shrpx_connection_handler.cc libnghttpx_a-shrpx_connection_handler.obj: shrpx_connection_handler.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_connection_handler.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_connection_handler.Tpo -c -o libnghttpx_a-shrpx_connection_handler.obj `if test -f 'shrpx_connection_handler.cc'; then $(CYGPATH_W) 'shrpx_connection_handler.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_connection_handler.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_connection_handler.Tpo $(DEPDIR)/libnghttpx_a-shrpx_connection_handler.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_connection_handler.cc' object='libnghttpx_a-shrpx_connection_handler.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_connection_handler.obj `if test -f 'shrpx_connection_handler.cc'; then $(CYGPATH_W) 'shrpx_connection_handler.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_connection_handler.cc'; fi` libnghttpx_a-shrpx_client_handler.o: shrpx_client_handler.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_client_handler.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_client_handler.Tpo -c -o libnghttpx_a-shrpx_client_handler.o `test -f 'shrpx_client_handler.cc' || echo '$(srcdir)/'`shrpx_client_handler.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_client_handler.Tpo $(DEPDIR)/libnghttpx_a-shrpx_client_handler.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_client_handler.cc' object='libnghttpx_a-shrpx_client_handler.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_client_handler.o `test -f 'shrpx_client_handler.cc' || echo '$(srcdir)/'`shrpx_client_handler.cc libnghttpx_a-shrpx_client_handler.obj: shrpx_client_handler.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_client_handler.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_client_handler.Tpo -c -o libnghttpx_a-shrpx_client_handler.obj `if test -f 'shrpx_client_handler.cc'; then $(CYGPATH_W) 'shrpx_client_handler.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_client_handler.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_client_handler.Tpo $(DEPDIR)/libnghttpx_a-shrpx_client_handler.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_client_handler.cc' object='libnghttpx_a-shrpx_client_handler.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_client_handler.obj `if test -f 'shrpx_client_handler.cc'; then $(CYGPATH_W) 'shrpx_client_handler.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_client_handler.cc'; fi` libnghttpx_a-shrpx_http2_upstream.o: shrpx_http2_upstream.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_http2_upstream.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_http2_upstream.Tpo -c -o libnghttpx_a-shrpx_http2_upstream.o `test -f 'shrpx_http2_upstream.cc' || echo '$(srcdir)/'`shrpx_http2_upstream.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_http2_upstream.Tpo $(DEPDIR)/libnghttpx_a-shrpx_http2_upstream.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_http2_upstream.cc' object='libnghttpx_a-shrpx_http2_upstream.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_http2_upstream.o `test -f 'shrpx_http2_upstream.cc' || echo '$(srcdir)/'`shrpx_http2_upstream.cc libnghttpx_a-shrpx_http2_upstream.obj: shrpx_http2_upstream.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_http2_upstream.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_http2_upstream.Tpo -c -o libnghttpx_a-shrpx_http2_upstream.obj `if test -f 'shrpx_http2_upstream.cc'; then $(CYGPATH_W) 'shrpx_http2_upstream.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_http2_upstream.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_http2_upstream.Tpo $(DEPDIR)/libnghttpx_a-shrpx_http2_upstream.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_http2_upstream.cc' object='libnghttpx_a-shrpx_http2_upstream.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_http2_upstream.obj `if test -f 'shrpx_http2_upstream.cc'; then $(CYGPATH_W) 'shrpx_http2_upstream.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_http2_upstream.cc'; fi` libnghttpx_a-shrpx_https_upstream.o: shrpx_https_upstream.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_https_upstream.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_https_upstream.Tpo -c -o libnghttpx_a-shrpx_https_upstream.o `test -f 'shrpx_https_upstream.cc' || echo '$(srcdir)/'`shrpx_https_upstream.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_https_upstream.Tpo $(DEPDIR)/libnghttpx_a-shrpx_https_upstream.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_https_upstream.cc' object='libnghttpx_a-shrpx_https_upstream.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_https_upstream.o `test -f 'shrpx_https_upstream.cc' || echo '$(srcdir)/'`shrpx_https_upstream.cc libnghttpx_a-shrpx_https_upstream.obj: shrpx_https_upstream.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_https_upstream.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_https_upstream.Tpo -c -o libnghttpx_a-shrpx_https_upstream.obj `if test -f 'shrpx_https_upstream.cc'; then $(CYGPATH_W) 'shrpx_https_upstream.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_https_upstream.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_https_upstream.Tpo $(DEPDIR)/libnghttpx_a-shrpx_https_upstream.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_https_upstream.cc' object='libnghttpx_a-shrpx_https_upstream.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_https_upstream.obj `if test -f 'shrpx_https_upstream.cc'; then $(CYGPATH_W) 'shrpx_https_upstream.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_https_upstream.cc'; fi` libnghttpx_a-shrpx_downstream.o: shrpx_downstream.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_downstream.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_downstream.Tpo -c -o libnghttpx_a-shrpx_downstream.o `test -f 'shrpx_downstream.cc' || echo '$(srcdir)/'`shrpx_downstream.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_downstream.Tpo $(DEPDIR)/libnghttpx_a-shrpx_downstream.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_downstream.cc' object='libnghttpx_a-shrpx_downstream.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_downstream.o `test -f 'shrpx_downstream.cc' || echo '$(srcdir)/'`shrpx_downstream.cc libnghttpx_a-shrpx_downstream.obj: shrpx_downstream.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_downstream.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_downstream.Tpo -c -o libnghttpx_a-shrpx_downstream.obj `if test -f 'shrpx_downstream.cc'; then $(CYGPATH_W) 'shrpx_downstream.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_downstream.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_downstream.Tpo $(DEPDIR)/libnghttpx_a-shrpx_downstream.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_downstream.cc' object='libnghttpx_a-shrpx_downstream.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_downstream.obj `if test -f 'shrpx_downstream.cc'; then $(CYGPATH_W) 'shrpx_downstream.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_downstream.cc'; fi` libnghttpx_a-shrpx_downstream_connection.o: shrpx_downstream_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_downstream_connection.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_downstream_connection.Tpo -c -o libnghttpx_a-shrpx_downstream_connection.o `test -f 'shrpx_downstream_connection.cc' || echo '$(srcdir)/'`shrpx_downstream_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_downstream_connection.Tpo $(DEPDIR)/libnghttpx_a-shrpx_downstream_connection.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_downstream_connection.cc' object='libnghttpx_a-shrpx_downstream_connection.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_downstream_connection.o `test -f 'shrpx_downstream_connection.cc' || echo '$(srcdir)/'`shrpx_downstream_connection.cc libnghttpx_a-shrpx_downstream_connection.obj: shrpx_downstream_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_downstream_connection.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_downstream_connection.Tpo -c -o libnghttpx_a-shrpx_downstream_connection.obj `if test -f 'shrpx_downstream_connection.cc'; then $(CYGPATH_W) 'shrpx_downstream_connection.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_downstream_connection.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_downstream_connection.Tpo $(DEPDIR)/libnghttpx_a-shrpx_downstream_connection.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_downstream_connection.cc' object='libnghttpx_a-shrpx_downstream_connection.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_downstream_connection.obj `if test -f 'shrpx_downstream_connection.cc'; then $(CYGPATH_W) 'shrpx_downstream_connection.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_downstream_connection.cc'; fi` libnghttpx_a-shrpx_http_downstream_connection.o: shrpx_http_downstream_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_http_downstream_connection.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_http_downstream_connection.Tpo -c -o libnghttpx_a-shrpx_http_downstream_connection.o `test -f 'shrpx_http_downstream_connection.cc' || echo '$(srcdir)/'`shrpx_http_downstream_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_http_downstream_connection.Tpo $(DEPDIR)/libnghttpx_a-shrpx_http_downstream_connection.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_http_downstream_connection.cc' object='libnghttpx_a-shrpx_http_downstream_connection.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_http_downstream_connection.o `test -f 'shrpx_http_downstream_connection.cc' || echo '$(srcdir)/'`shrpx_http_downstream_connection.cc libnghttpx_a-shrpx_http_downstream_connection.obj: shrpx_http_downstream_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_http_downstream_connection.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_http_downstream_connection.Tpo -c -o libnghttpx_a-shrpx_http_downstream_connection.obj `if test -f 'shrpx_http_downstream_connection.cc'; then $(CYGPATH_W) 'shrpx_http_downstream_connection.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_http_downstream_connection.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_http_downstream_connection.Tpo $(DEPDIR)/libnghttpx_a-shrpx_http_downstream_connection.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_http_downstream_connection.cc' object='libnghttpx_a-shrpx_http_downstream_connection.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_http_downstream_connection.obj `if test -f 'shrpx_http_downstream_connection.cc'; then $(CYGPATH_W) 'shrpx_http_downstream_connection.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_http_downstream_connection.cc'; fi` libnghttpx_a-shrpx_http2_downstream_connection.o: shrpx_http2_downstream_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_http2_downstream_connection.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_http2_downstream_connection.Tpo -c -o libnghttpx_a-shrpx_http2_downstream_connection.o `test -f 'shrpx_http2_downstream_connection.cc' || echo '$(srcdir)/'`shrpx_http2_downstream_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_http2_downstream_connection.Tpo $(DEPDIR)/libnghttpx_a-shrpx_http2_downstream_connection.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_http2_downstream_connection.cc' object='libnghttpx_a-shrpx_http2_downstream_connection.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_http2_downstream_connection.o `test -f 'shrpx_http2_downstream_connection.cc' || echo '$(srcdir)/'`shrpx_http2_downstream_connection.cc libnghttpx_a-shrpx_http2_downstream_connection.obj: shrpx_http2_downstream_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_http2_downstream_connection.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_http2_downstream_connection.Tpo -c -o libnghttpx_a-shrpx_http2_downstream_connection.obj `if test -f 'shrpx_http2_downstream_connection.cc'; then $(CYGPATH_W) 'shrpx_http2_downstream_connection.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_http2_downstream_connection.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_http2_downstream_connection.Tpo $(DEPDIR)/libnghttpx_a-shrpx_http2_downstream_connection.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_http2_downstream_connection.cc' object='libnghttpx_a-shrpx_http2_downstream_connection.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_http2_downstream_connection.obj `if test -f 'shrpx_http2_downstream_connection.cc'; then $(CYGPATH_W) 'shrpx_http2_downstream_connection.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_http2_downstream_connection.cc'; fi` libnghttpx_a-shrpx_http2_session.o: shrpx_http2_session.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_http2_session.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_http2_session.Tpo -c -o libnghttpx_a-shrpx_http2_session.o `test -f 'shrpx_http2_session.cc' || echo '$(srcdir)/'`shrpx_http2_session.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_http2_session.Tpo $(DEPDIR)/libnghttpx_a-shrpx_http2_session.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_http2_session.cc' object='libnghttpx_a-shrpx_http2_session.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_http2_session.o `test -f 'shrpx_http2_session.cc' || echo '$(srcdir)/'`shrpx_http2_session.cc libnghttpx_a-shrpx_http2_session.obj: shrpx_http2_session.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_http2_session.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_http2_session.Tpo -c -o libnghttpx_a-shrpx_http2_session.obj `if test -f 'shrpx_http2_session.cc'; then $(CYGPATH_W) 'shrpx_http2_session.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_http2_session.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_http2_session.Tpo $(DEPDIR)/libnghttpx_a-shrpx_http2_session.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_http2_session.cc' object='libnghttpx_a-shrpx_http2_session.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_http2_session.obj `if test -f 'shrpx_http2_session.cc'; then $(CYGPATH_W) 'shrpx_http2_session.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_http2_session.cc'; fi` libnghttpx_a-shrpx_downstream_queue.o: shrpx_downstream_queue.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_downstream_queue.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_downstream_queue.Tpo -c -o libnghttpx_a-shrpx_downstream_queue.o `test -f 'shrpx_downstream_queue.cc' || echo '$(srcdir)/'`shrpx_downstream_queue.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_downstream_queue.Tpo $(DEPDIR)/libnghttpx_a-shrpx_downstream_queue.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_downstream_queue.cc' object='libnghttpx_a-shrpx_downstream_queue.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_downstream_queue.o `test -f 'shrpx_downstream_queue.cc' || echo '$(srcdir)/'`shrpx_downstream_queue.cc libnghttpx_a-shrpx_downstream_queue.obj: shrpx_downstream_queue.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_downstream_queue.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_downstream_queue.Tpo -c -o libnghttpx_a-shrpx_downstream_queue.obj `if test -f 'shrpx_downstream_queue.cc'; then $(CYGPATH_W) 'shrpx_downstream_queue.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_downstream_queue.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_downstream_queue.Tpo $(DEPDIR)/libnghttpx_a-shrpx_downstream_queue.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_downstream_queue.cc' object='libnghttpx_a-shrpx_downstream_queue.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_downstream_queue.obj `if test -f 'shrpx_downstream_queue.cc'; then $(CYGPATH_W) 'shrpx_downstream_queue.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_downstream_queue.cc'; fi` libnghttpx_a-shrpx_log.o: shrpx_log.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_log.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_log.Tpo -c -o libnghttpx_a-shrpx_log.o `test -f 'shrpx_log.cc' || echo '$(srcdir)/'`shrpx_log.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_log.Tpo $(DEPDIR)/libnghttpx_a-shrpx_log.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_log.cc' object='libnghttpx_a-shrpx_log.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_log.o `test -f 'shrpx_log.cc' || echo '$(srcdir)/'`shrpx_log.cc libnghttpx_a-shrpx_log.obj: shrpx_log.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_log.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_log.Tpo -c -o libnghttpx_a-shrpx_log.obj `if test -f 'shrpx_log.cc'; then $(CYGPATH_W) 'shrpx_log.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_log.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_log.Tpo $(DEPDIR)/libnghttpx_a-shrpx_log.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_log.cc' object='libnghttpx_a-shrpx_log.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_log.obj `if test -f 'shrpx_log.cc'; then $(CYGPATH_W) 'shrpx_log.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_log.cc'; fi` libnghttpx_a-shrpx_http.o: shrpx_http.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_http.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_http.Tpo -c -o libnghttpx_a-shrpx_http.o `test -f 'shrpx_http.cc' || echo '$(srcdir)/'`shrpx_http.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_http.Tpo $(DEPDIR)/libnghttpx_a-shrpx_http.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_http.cc' object='libnghttpx_a-shrpx_http.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_http.o `test -f 'shrpx_http.cc' || echo '$(srcdir)/'`shrpx_http.cc libnghttpx_a-shrpx_http.obj: shrpx_http.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_http.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_http.Tpo -c -o libnghttpx_a-shrpx_http.obj `if test -f 'shrpx_http.cc'; then $(CYGPATH_W) 'shrpx_http.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_http.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_http.Tpo $(DEPDIR)/libnghttpx_a-shrpx_http.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_http.cc' object='libnghttpx_a-shrpx_http.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_http.obj `if test -f 'shrpx_http.cc'; then $(CYGPATH_W) 'shrpx_http.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_http.cc'; fi` libnghttpx_a-shrpx_io_control.o: shrpx_io_control.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_io_control.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_io_control.Tpo -c -o libnghttpx_a-shrpx_io_control.o `test -f 'shrpx_io_control.cc' || echo '$(srcdir)/'`shrpx_io_control.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_io_control.Tpo $(DEPDIR)/libnghttpx_a-shrpx_io_control.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_io_control.cc' object='libnghttpx_a-shrpx_io_control.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_io_control.o `test -f 'shrpx_io_control.cc' || echo '$(srcdir)/'`shrpx_io_control.cc libnghttpx_a-shrpx_io_control.obj: shrpx_io_control.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_io_control.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_io_control.Tpo -c -o libnghttpx_a-shrpx_io_control.obj `if test -f 'shrpx_io_control.cc'; then $(CYGPATH_W) 'shrpx_io_control.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_io_control.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_io_control.Tpo $(DEPDIR)/libnghttpx_a-shrpx_io_control.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_io_control.cc' object='libnghttpx_a-shrpx_io_control.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_io_control.obj `if test -f 'shrpx_io_control.cc'; then $(CYGPATH_W) 'shrpx_io_control.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_io_control.cc'; fi` libnghttpx_a-shrpx_tls.o: shrpx_tls.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_tls.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_tls.Tpo -c -o libnghttpx_a-shrpx_tls.o `test -f 'shrpx_tls.cc' || echo '$(srcdir)/'`shrpx_tls.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_tls.Tpo $(DEPDIR)/libnghttpx_a-shrpx_tls.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_tls.cc' object='libnghttpx_a-shrpx_tls.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_tls.o `test -f 'shrpx_tls.cc' || echo '$(srcdir)/'`shrpx_tls.cc libnghttpx_a-shrpx_tls.obj: shrpx_tls.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_tls.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_tls.Tpo -c -o libnghttpx_a-shrpx_tls.obj `if test -f 'shrpx_tls.cc'; then $(CYGPATH_W) 'shrpx_tls.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_tls.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_tls.Tpo $(DEPDIR)/libnghttpx_a-shrpx_tls.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_tls.cc' object='libnghttpx_a-shrpx_tls.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_tls.obj `if test -f 'shrpx_tls.cc'; then $(CYGPATH_W) 'shrpx_tls.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_tls.cc'; fi` libnghttpx_a-shrpx_worker.o: shrpx_worker.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_worker.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_worker.Tpo -c -o libnghttpx_a-shrpx_worker.o `test -f 'shrpx_worker.cc' || echo '$(srcdir)/'`shrpx_worker.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_worker.Tpo $(DEPDIR)/libnghttpx_a-shrpx_worker.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_worker.cc' object='libnghttpx_a-shrpx_worker.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_worker.o `test -f 'shrpx_worker.cc' || echo '$(srcdir)/'`shrpx_worker.cc libnghttpx_a-shrpx_worker.obj: shrpx_worker.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_worker.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_worker.Tpo -c -o libnghttpx_a-shrpx_worker.obj `if test -f 'shrpx_worker.cc'; then $(CYGPATH_W) 'shrpx_worker.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_worker.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_worker.Tpo $(DEPDIR)/libnghttpx_a-shrpx_worker.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_worker.cc' object='libnghttpx_a-shrpx_worker.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_worker.obj `if test -f 'shrpx_worker.cc'; then $(CYGPATH_W) 'shrpx_worker.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_worker.cc'; fi` libnghttpx_a-shrpx_log_config.o: shrpx_log_config.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_log_config.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_log_config.Tpo -c -o libnghttpx_a-shrpx_log_config.o `test -f 'shrpx_log_config.cc' || echo '$(srcdir)/'`shrpx_log_config.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_log_config.Tpo $(DEPDIR)/libnghttpx_a-shrpx_log_config.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_log_config.cc' object='libnghttpx_a-shrpx_log_config.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_log_config.o `test -f 'shrpx_log_config.cc' || echo '$(srcdir)/'`shrpx_log_config.cc libnghttpx_a-shrpx_log_config.obj: shrpx_log_config.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_log_config.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_log_config.Tpo -c -o libnghttpx_a-shrpx_log_config.obj `if test -f 'shrpx_log_config.cc'; then $(CYGPATH_W) 'shrpx_log_config.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_log_config.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_log_config.Tpo $(DEPDIR)/libnghttpx_a-shrpx_log_config.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_log_config.cc' object='libnghttpx_a-shrpx_log_config.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_log_config.obj `if test -f 'shrpx_log_config.cc'; then $(CYGPATH_W) 'shrpx_log_config.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_log_config.cc'; fi` libnghttpx_a-shrpx_connect_blocker.o: shrpx_connect_blocker.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_connect_blocker.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_connect_blocker.Tpo -c -o libnghttpx_a-shrpx_connect_blocker.o `test -f 'shrpx_connect_blocker.cc' || echo '$(srcdir)/'`shrpx_connect_blocker.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_connect_blocker.Tpo $(DEPDIR)/libnghttpx_a-shrpx_connect_blocker.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_connect_blocker.cc' object='libnghttpx_a-shrpx_connect_blocker.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_connect_blocker.o `test -f 'shrpx_connect_blocker.cc' || echo '$(srcdir)/'`shrpx_connect_blocker.cc libnghttpx_a-shrpx_connect_blocker.obj: shrpx_connect_blocker.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_connect_blocker.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_connect_blocker.Tpo -c -o libnghttpx_a-shrpx_connect_blocker.obj `if test -f 'shrpx_connect_blocker.cc'; then $(CYGPATH_W) 'shrpx_connect_blocker.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_connect_blocker.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_connect_blocker.Tpo $(DEPDIR)/libnghttpx_a-shrpx_connect_blocker.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_connect_blocker.cc' object='libnghttpx_a-shrpx_connect_blocker.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_connect_blocker.obj `if test -f 'shrpx_connect_blocker.cc'; then $(CYGPATH_W) 'shrpx_connect_blocker.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_connect_blocker.cc'; fi` libnghttpx_a-shrpx_live_check.o: shrpx_live_check.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_live_check.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_live_check.Tpo -c -o libnghttpx_a-shrpx_live_check.o `test -f 'shrpx_live_check.cc' || echo '$(srcdir)/'`shrpx_live_check.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_live_check.Tpo $(DEPDIR)/libnghttpx_a-shrpx_live_check.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_live_check.cc' object='libnghttpx_a-shrpx_live_check.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_live_check.o `test -f 'shrpx_live_check.cc' || echo '$(srcdir)/'`shrpx_live_check.cc libnghttpx_a-shrpx_live_check.obj: shrpx_live_check.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_live_check.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_live_check.Tpo -c -o libnghttpx_a-shrpx_live_check.obj `if test -f 'shrpx_live_check.cc'; then $(CYGPATH_W) 'shrpx_live_check.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_live_check.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_live_check.Tpo $(DEPDIR)/libnghttpx_a-shrpx_live_check.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_live_check.cc' object='libnghttpx_a-shrpx_live_check.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_live_check.obj `if test -f 'shrpx_live_check.cc'; then $(CYGPATH_W) 'shrpx_live_check.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_live_check.cc'; fi` libnghttpx_a-shrpx_downstream_connection_pool.o: shrpx_downstream_connection_pool.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_downstream_connection_pool.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_downstream_connection_pool.Tpo -c -o libnghttpx_a-shrpx_downstream_connection_pool.o `test -f 'shrpx_downstream_connection_pool.cc' || echo '$(srcdir)/'`shrpx_downstream_connection_pool.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_downstream_connection_pool.Tpo $(DEPDIR)/libnghttpx_a-shrpx_downstream_connection_pool.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_downstream_connection_pool.cc' object='libnghttpx_a-shrpx_downstream_connection_pool.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_downstream_connection_pool.o `test -f 'shrpx_downstream_connection_pool.cc' || echo '$(srcdir)/'`shrpx_downstream_connection_pool.cc libnghttpx_a-shrpx_downstream_connection_pool.obj: shrpx_downstream_connection_pool.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_downstream_connection_pool.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_downstream_connection_pool.Tpo -c -o libnghttpx_a-shrpx_downstream_connection_pool.obj `if test -f 'shrpx_downstream_connection_pool.cc'; then $(CYGPATH_W) 'shrpx_downstream_connection_pool.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_downstream_connection_pool.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_downstream_connection_pool.Tpo $(DEPDIR)/libnghttpx_a-shrpx_downstream_connection_pool.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_downstream_connection_pool.cc' object='libnghttpx_a-shrpx_downstream_connection_pool.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_downstream_connection_pool.obj `if test -f 'shrpx_downstream_connection_pool.cc'; then $(CYGPATH_W) 'shrpx_downstream_connection_pool.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_downstream_connection_pool.cc'; fi` libnghttpx_a-shrpx_rate_limit.o: shrpx_rate_limit.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_rate_limit.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_rate_limit.Tpo -c -o libnghttpx_a-shrpx_rate_limit.o `test -f 'shrpx_rate_limit.cc' || echo '$(srcdir)/'`shrpx_rate_limit.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_rate_limit.Tpo $(DEPDIR)/libnghttpx_a-shrpx_rate_limit.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_rate_limit.cc' object='libnghttpx_a-shrpx_rate_limit.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_rate_limit.o `test -f 'shrpx_rate_limit.cc' || echo '$(srcdir)/'`shrpx_rate_limit.cc libnghttpx_a-shrpx_rate_limit.obj: shrpx_rate_limit.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_rate_limit.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_rate_limit.Tpo -c -o libnghttpx_a-shrpx_rate_limit.obj `if test -f 'shrpx_rate_limit.cc'; then $(CYGPATH_W) 'shrpx_rate_limit.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_rate_limit.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_rate_limit.Tpo $(DEPDIR)/libnghttpx_a-shrpx_rate_limit.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_rate_limit.cc' object='libnghttpx_a-shrpx_rate_limit.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_rate_limit.obj `if test -f 'shrpx_rate_limit.cc'; then $(CYGPATH_W) 'shrpx_rate_limit.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_rate_limit.cc'; fi` libnghttpx_a-shrpx_connection.o: shrpx_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_connection.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_connection.Tpo -c -o libnghttpx_a-shrpx_connection.o `test -f 'shrpx_connection.cc' || echo '$(srcdir)/'`shrpx_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_connection.Tpo $(DEPDIR)/libnghttpx_a-shrpx_connection.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_connection.cc' object='libnghttpx_a-shrpx_connection.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_connection.o `test -f 'shrpx_connection.cc' || echo '$(srcdir)/'`shrpx_connection.cc libnghttpx_a-shrpx_connection.obj: shrpx_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_connection.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_connection.Tpo -c -o libnghttpx_a-shrpx_connection.obj `if test -f 'shrpx_connection.cc'; then $(CYGPATH_W) 'shrpx_connection.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_connection.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_connection.Tpo $(DEPDIR)/libnghttpx_a-shrpx_connection.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_connection.cc' object='libnghttpx_a-shrpx_connection.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_connection.obj `if test -f 'shrpx_connection.cc'; then $(CYGPATH_W) 'shrpx_connection.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_connection.cc'; fi` libnghttpx_a-shrpx_memcached_dispatcher.o: shrpx_memcached_dispatcher.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_memcached_dispatcher.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_memcached_dispatcher.Tpo -c -o libnghttpx_a-shrpx_memcached_dispatcher.o `test -f 'shrpx_memcached_dispatcher.cc' || echo '$(srcdir)/'`shrpx_memcached_dispatcher.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_memcached_dispatcher.Tpo $(DEPDIR)/libnghttpx_a-shrpx_memcached_dispatcher.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_memcached_dispatcher.cc' object='libnghttpx_a-shrpx_memcached_dispatcher.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_memcached_dispatcher.o `test -f 'shrpx_memcached_dispatcher.cc' || echo '$(srcdir)/'`shrpx_memcached_dispatcher.cc libnghttpx_a-shrpx_memcached_dispatcher.obj: shrpx_memcached_dispatcher.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_memcached_dispatcher.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_memcached_dispatcher.Tpo -c -o libnghttpx_a-shrpx_memcached_dispatcher.obj `if test -f 'shrpx_memcached_dispatcher.cc'; then $(CYGPATH_W) 'shrpx_memcached_dispatcher.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_memcached_dispatcher.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_memcached_dispatcher.Tpo $(DEPDIR)/libnghttpx_a-shrpx_memcached_dispatcher.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_memcached_dispatcher.cc' object='libnghttpx_a-shrpx_memcached_dispatcher.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_memcached_dispatcher.obj `if test -f 'shrpx_memcached_dispatcher.cc'; then $(CYGPATH_W) 'shrpx_memcached_dispatcher.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_memcached_dispatcher.cc'; fi` libnghttpx_a-shrpx_memcached_connection.o: shrpx_memcached_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_memcached_connection.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_memcached_connection.Tpo -c -o libnghttpx_a-shrpx_memcached_connection.o `test -f 'shrpx_memcached_connection.cc' || echo '$(srcdir)/'`shrpx_memcached_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_memcached_connection.Tpo $(DEPDIR)/libnghttpx_a-shrpx_memcached_connection.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_memcached_connection.cc' object='libnghttpx_a-shrpx_memcached_connection.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_memcached_connection.o `test -f 'shrpx_memcached_connection.cc' || echo '$(srcdir)/'`shrpx_memcached_connection.cc libnghttpx_a-shrpx_memcached_connection.obj: shrpx_memcached_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_memcached_connection.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_memcached_connection.Tpo -c -o libnghttpx_a-shrpx_memcached_connection.obj `if test -f 'shrpx_memcached_connection.cc'; then $(CYGPATH_W) 'shrpx_memcached_connection.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_memcached_connection.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_memcached_connection.Tpo $(DEPDIR)/libnghttpx_a-shrpx_memcached_connection.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_memcached_connection.cc' object='libnghttpx_a-shrpx_memcached_connection.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_memcached_connection.obj `if test -f 'shrpx_memcached_connection.cc'; then $(CYGPATH_W) 'shrpx_memcached_connection.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_memcached_connection.cc'; fi` libnghttpx_a-shrpx_worker_process.o: shrpx_worker_process.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_worker_process.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_worker_process.Tpo -c -o libnghttpx_a-shrpx_worker_process.o `test -f 'shrpx_worker_process.cc' || echo '$(srcdir)/'`shrpx_worker_process.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_worker_process.Tpo $(DEPDIR)/libnghttpx_a-shrpx_worker_process.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_worker_process.cc' object='libnghttpx_a-shrpx_worker_process.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_worker_process.o `test -f 'shrpx_worker_process.cc' || echo '$(srcdir)/'`shrpx_worker_process.cc libnghttpx_a-shrpx_worker_process.obj: shrpx_worker_process.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_worker_process.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_worker_process.Tpo -c -o libnghttpx_a-shrpx_worker_process.obj `if test -f 'shrpx_worker_process.cc'; then $(CYGPATH_W) 'shrpx_worker_process.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_worker_process.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_worker_process.Tpo $(DEPDIR)/libnghttpx_a-shrpx_worker_process.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_worker_process.cc' object='libnghttpx_a-shrpx_worker_process.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_worker_process.obj `if test -f 'shrpx_worker_process.cc'; then $(CYGPATH_W) 'shrpx_worker_process.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_worker_process.cc'; fi` libnghttpx_a-shrpx_signal.o: shrpx_signal.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_signal.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_signal.Tpo -c -o libnghttpx_a-shrpx_signal.o `test -f 'shrpx_signal.cc' || echo '$(srcdir)/'`shrpx_signal.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_signal.Tpo $(DEPDIR)/libnghttpx_a-shrpx_signal.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_signal.cc' object='libnghttpx_a-shrpx_signal.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_signal.o `test -f 'shrpx_signal.cc' || echo '$(srcdir)/'`shrpx_signal.cc libnghttpx_a-shrpx_signal.obj: shrpx_signal.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_signal.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_signal.Tpo -c -o libnghttpx_a-shrpx_signal.obj `if test -f 'shrpx_signal.cc'; then $(CYGPATH_W) 'shrpx_signal.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_signal.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_signal.Tpo $(DEPDIR)/libnghttpx_a-shrpx_signal.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_signal.cc' object='libnghttpx_a-shrpx_signal.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_signal.obj `if test -f 'shrpx_signal.cc'; then $(CYGPATH_W) 'shrpx_signal.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_signal.cc'; fi` libnghttpx_a-shrpx_router.o: shrpx_router.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_router.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_router.Tpo -c -o libnghttpx_a-shrpx_router.o `test -f 'shrpx_router.cc' || echo '$(srcdir)/'`shrpx_router.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_router.Tpo $(DEPDIR)/libnghttpx_a-shrpx_router.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_router.cc' object='libnghttpx_a-shrpx_router.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_router.o `test -f 'shrpx_router.cc' || echo '$(srcdir)/'`shrpx_router.cc libnghttpx_a-shrpx_router.obj: shrpx_router.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_router.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_router.Tpo -c -o libnghttpx_a-shrpx_router.obj `if test -f 'shrpx_router.cc'; then $(CYGPATH_W) 'shrpx_router.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_router.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_router.Tpo $(DEPDIR)/libnghttpx_a-shrpx_router.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_router.cc' object='libnghttpx_a-shrpx_router.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_router.obj `if test -f 'shrpx_router.cc'; then $(CYGPATH_W) 'shrpx_router.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_router.cc'; fi` libnghttpx_a-shrpx_api_downstream_connection.o: shrpx_api_downstream_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_api_downstream_connection.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_api_downstream_connection.Tpo -c -o libnghttpx_a-shrpx_api_downstream_connection.o `test -f 'shrpx_api_downstream_connection.cc' || echo '$(srcdir)/'`shrpx_api_downstream_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_api_downstream_connection.Tpo $(DEPDIR)/libnghttpx_a-shrpx_api_downstream_connection.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_api_downstream_connection.cc' object='libnghttpx_a-shrpx_api_downstream_connection.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_api_downstream_connection.o `test -f 'shrpx_api_downstream_connection.cc' || echo '$(srcdir)/'`shrpx_api_downstream_connection.cc libnghttpx_a-shrpx_api_downstream_connection.obj: shrpx_api_downstream_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_api_downstream_connection.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_api_downstream_connection.Tpo -c -o libnghttpx_a-shrpx_api_downstream_connection.obj `if test -f 'shrpx_api_downstream_connection.cc'; then $(CYGPATH_W) 'shrpx_api_downstream_connection.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_api_downstream_connection.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_api_downstream_connection.Tpo $(DEPDIR)/libnghttpx_a-shrpx_api_downstream_connection.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_api_downstream_connection.cc' object='libnghttpx_a-shrpx_api_downstream_connection.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_api_downstream_connection.obj `if test -f 'shrpx_api_downstream_connection.cc'; then $(CYGPATH_W) 'shrpx_api_downstream_connection.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_api_downstream_connection.cc'; fi` libnghttpx_a-shrpx_health_monitor_downstream_connection.o: shrpx_health_monitor_downstream_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_health_monitor_downstream_connection.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_health_monitor_downstream_connection.Tpo -c -o libnghttpx_a-shrpx_health_monitor_downstream_connection.o `test -f 'shrpx_health_monitor_downstream_connection.cc' || echo '$(srcdir)/'`shrpx_health_monitor_downstream_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_health_monitor_downstream_connection.Tpo $(DEPDIR)/libnghttpx_a-shrpx_health_monitor_downstream_connection.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_health_monitor_downstream_connection.cc' object='libnghttpx_a-shrpx_health_monitor_downstream_connection.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_health_monitor_downstream_connection.o `test -f 'shrpx_health_monitor_downstream_connection.cc' || echo '$(srcdir)/'`shrpx_health_monitor_downstream_connection.cc libnghttpx_a-shrpx_health_monitor_downstream_connection.obj: shrpx_health_monitor_downstream_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_health_monitor_downstream_connection.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_health_monitor_downstream_connection.Tpo -c -o libnghttpx_a-shrpx_health_monitor_downstream_connection.obj `if test -f 'shrpx_health_monitor_downstream_connection.cc'; then $(CYGPATH_W) 'shrpx_health_monitor_downstream_connection.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_health_monitor_downstream_connection.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_health_monitor_downstream_connection.Tpo $(DEPDIR)/libnghttpx_a-shrpx_health_monitor_downstream_connection.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_health_monitor_downstream_connection.cc' object='libnghttpx_a-shrpx_health_monitor_downstream_connection.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_health_monitor_downstream_connection.obj `if test -f 'shrpx_health_monitor_downstream_connection.cc'; then $(CYGPATH_W) 'shrpx_health_monitor_downstream_connection.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_health_monitor_downstream_connection.cc'; fi` libnghttpx_a-shrpx_null_downstream_connection.o: shrpx_null_downstream_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_null_downstream_connection.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_null_downstream_connection.Tpo -c -o libnghttpx_a-shrpx_null_downstream_connection.o `test -f 'shrpx_null_downstream_connection.cc' || echo '$(srcdir)/'`shrpx_null_downstream_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_null_downstream_connection.Tpo $(DEPDIR)/libnghttpx_a-shrpx_null_downstream_connection.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_null_downstream_connection.cc' object='libnghttpx_a-shrpx_null_downstream_connection.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_null_downstream_connection.o `test -f 'shrpx_null_downstream_connection.cc' || echo '$(srcdir)/'`shrpx_null_downstream_connection.cc libnghttpx_a-shrpx_null_downstream_connection.obj: shrpx_null_downstream_connection.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_null_downstream_connection.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_null_downstream_connection.Tpo -c -o libnghttpx_a-shrpx_null_downstream_connection.obj `if test -f 'shrpx_null_downstream_connection.cc'; then $(CYGPATH_W) 'shrpx_null_downstream_connection.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_null_downstream_connection.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_null_downstream_connection.Tpo $(DEPDIR)/libnghttpx_a-shrpx_null_downstream_connection.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_null_downstream_connection.cc' object='libnghttpx_a-shrpx_null_downstream_connection.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_null_downstream_connection.obj `if test -f 'shrpx_null_downstream_connection.cc'; then $(CYGPATH_W) 'shrpx_null_downstream_connection.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_null_downstream_connection.cc'; fi` libnghttpx_a-shrpx_dns_resolver.o: shrpx_dns_resolver.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_dns_resolver.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_dns_resolver.Tpo -c -o libnghttpx_a-shrpx_dns_resolver.o `test -f 'shrpx_dns_resolver.cc' || echo '$(srcdir)/'`shrpx_dns_resolver.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_dns_resolver.Tpo $(DEPDIR)/libnghttpx_a-shrpx_dns_resolver.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_dns_resolver.cc' object='libnghttpx_a-shrpx_dns_resolver.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_dns_resolver.o `test -f 'shrpx_dns_resolver.cc' || echo '$(srcdir)/'`shrpx_dns_resolver.cc libnghttpx_a-shrpx_dns_resolver.obj: shrpx_dns_resolver.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_dns_resolver.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_dns_resolver.Tpo -c -o libnghttpx_a-shrpx_dns_resolver.obj `if test -f 'shrpx_dns_resolver.cc'; then $(CYGPATH_W) 'shrpx_dns_resolver.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_dns_resolver.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_dns_resolver.Tpo $(DEPDIR)/libnghttpx_a-shrpx_dns_resolver.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_dns_resolver.cc' object='libnghttpx_a-shrpx_dns_resolver.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_dns_resolver.obj `if test -f 'shrpx_dns_resolver.cc'; then $(CYGPATH_W) 'shrpx_dns_resolver.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_dns_resolver.cc'; fi` libnghttpx_a-shrpx_dual_dns_resolver.o: shrpx_dual_dns_resolver.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_dual_dns_resolver.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_dual_dns_resolver.Tpo -c -o libnghttpx_a-shrpx_dual_dns_resolver.o `test -f 'shrpx_dual_dns_resolver.cc' || echo '$(srcdir)/'`shrpx_dual_dns_resolver.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_dual_dns_resolver.Tpo $(DEPDIR)/libnghttpx_a-shrpx_dual_dns_resolver.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_dual_dns_resolver.cc' object='libnghttpx_a-shrpx_dual_dns_resolver.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_dual_dns_resolver.o `test -f 'shrpx_dual_dns_resolver.cc' || echo '$(srcdir)/'`shrpx_dual_dns_resolver.cc libnghttpx_a-shrpx_dual_dns_resolver.obj: shrpx_dual_dns_resolver.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_dual_dns_resolver.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_dual_dns_resolver.Tpo -c -o libnghttpx_a-shrpx_dual_dns_resolver.obj `if test -f 'shrpx_dual_dns_resolver.cc'; then $(CYGPATH_W) 'shrpx_dual_dns_resolver.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_dual_dns_resolver.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_dual_dns_resolver.Tpo $(DEPDIR)/libnghttpx_a-shrpx_dual_dns_resolver.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_dual_dns_resolver.cc' object='libnghttpx_a-shrpx_dual_dns_resolver.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_dual_dns_resolver.obj `if test -f 'shrpx_dual_dns_resolver.cc'; then $(CYGPATH_W) 'shrpx_dual_dns_resolver.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_dual_dns_resolver.cc'; fi` libnghttpx_a-shrpx_dns_tracker.o: shrpx_dns_tracker.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_dns_tracker.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_dns_tracker.Tpo -c -o libnghttpx_a-shrpx_dns_tracker.o `test -f 'shrpx_dns_tracker.cc' || echo '$(srcdir)/'`shrpx_dns_tracker.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_dns_tracker.Tpo $(DEPDIR)/libnghttpx_a-shrpx_dns_tracker.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_dns_tracker.cc' object='libnghttpx_a-shrpx_dns_tracker.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_dns_tracker.o `test -f 'shrpx_dns_tracker.cc' || echo '$(srcdir)/'`shrpx_dns_tracker.cc libnghttpx_a-shrpx_dns_tracker.obj: shrpx_dns_tracker.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_dns_tracker.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_dns_tracker.Tpo -c -o libnghttpx_a-shrpx_dns_tracker.obj `if test -f 'shrpx_dns_tracker.cc'; then $(CYGPATH_W) 'shrpx_dns_tracker.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_dns_tracker.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_dns_tracker.Tpo $(DEPDIR)/libnghttpx_a-shrpx_dns_tracker.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_dns_tracker.cc' object='libnghttpx_a-shrpx_dns_tracker.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_dns_tracker.obj `if test -f 'shrpx_dns_tracker.cc'; then $(CYGPATH_W) 'shrpx_dns_tracker.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_dns_tracker.cc'; fi` libnghttpx_a-shrpx_mruby.o: shrpx_mruby.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_mruby.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_mruby.Tpo -c -o libnghttpx_a-shrpx_mruby.o `test -f 'shrpx_mruby.cc' || echo '$(srcdir)/'`shrpx_mruby.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_mruby.Tpo $(DEPDIR)/libnghttpx_a-shrpx_mruby.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_mruby.cc' object='libnghttpx_a-shrpx_mruby.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_mruby.o `test -f 'shrpx_mruby.cc' || echo '$(srcdir)/'`shrpx_mruby.cc libnghttpx_a-shrpx_mruby.obj: shrpx_mruby.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_mruby.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_mruby.Tpo -c -o libnghttpx_a-shrpx_mruby.obj `if test -f 'shrpx_mruby.cc'; then $(CYGPATH_W) 'shrpx_mruby.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_mruby.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_mruby.Tpo $(DEPDIR)/libnghttpx_a-shrpx_mruby.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_mruby.cc' object='libnghttpx_a-shrpx_mruby.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_mruby.obj `if test -f 'shrpx_mruby.cc'; then $(CYGPATH_W) 'shrpx_mruby.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_mruby.cc'; fi` libnghttpx_a-shrpx_mruby_module.o: shrpx_mruby_module.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_mruby_module.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_mruby_module.Tpo -c -o libnghttpx_a-shrpx_mruby_module.o `test -f 'shrpx_mruby_module.cc' || echo '$(srcdir)/'`shrpx_mruby_module.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_mruby_module.Tpo $(DEPDIR)/libnghttpx_a-shrpx_mruby_module.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_mruby_module.cc' object='libnghttpx_a-shrpx_mruby_module.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_mruby_module.o `test -f 'shrpx_mruby_module.cc' || echo '$(srcdir)/'`shrpx_mruby_module.cc libnghttpx_a-shrpx_mruby_module.obj: shrpx_mruby_module.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_mruby_module.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_mruby_module.Tpo -c -o libnghttpx_a-shrpx_mruby_module.obj `if test -f 'shrpx_mruby_module.cc'; then $(CYGPATH_W) 'shrpx_mruby_module.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_mruby_module.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_mruby_module.Tpo $(DEPDIR)/libnghttpx_a-shrpx_mruby_module.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_mruby_module.cc' object='libnghttpx_a-shrpx_mruby_module.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_mruby_module.obj `if test -f 'shrpx_mruby_module.cc'; then $(CYGPATH_W) 'shrpx_mruby_module.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_mruby_module.cc'; fi` libnghttpx_a-shrpx_mruby_module_env.o: shrpx_mruby_module_env.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_mruby_module_env.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_mruby_module_env.Tpo -c -o libnghttpx_a-shrpx_mruby_module_env.o `test -f 'shrpx_mruby_module_env.cc' || echo '$(srcdir)/'`shrpx_mruby_module_env.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_mruby_module_env.Tpo $(DEPDIR)/libnghttpx_a-shrpx_mruby_module_env.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_mruby_module_env.cc' object='libnghttpx_a-shrpx_mruby_module_env.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_mruby_module_env.o `test -f 'shrpx_mruby_module_env.cc' || echo '$(srcdir)/'`shrpx_mruby_module_env.cc libnghttpx_a-shrpx_mruby_module_env.obj: shrpx_mruby_module_env.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_mruby_module_env.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_mruby_module_env.Tpo -c -o libnghttpx_a-shrpx_mruby_module_env.obj `if test -f 'shrpx_mruby_module_env.cc'; then $(CYGPATH_W) 'shrpx_mruby_module_env.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_mruby_module_env.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_mruby_module_env.Tpo $(DEPDIR)/libnghttpx_a-shrpx_mruby_module_env.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_mruby_module_env.cc' object='libnghttpx_a-shrpx_mruby_module_env.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_mruby_module_env.obj `if test -f 'shrpx_mruby_module_env.cc'; then $(CYGPATH_W) 'shrpx_mruby_module_env.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_mruby_module_env.cc'; fi` libnghttpx_a-shrpx_mruby_module_request.o: shrpx_mruby_module_request.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_mruby_module_request.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_mruby_module_request.Tpo -c -o libnghttpx_a-shrpx_mruby_module_request.o `test -f 'shrpx_mruby_module_request.cc' || echo '$(srcdir)/'`shrpx_mruby_module_request.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_mruby_module_request.Tpo $(DEPDIR)/libnghttpx_a-shrpx_mruby_module_request.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_mruby_module_request.cc' object='libnghttpx_a-shrpx_mruby_module_request.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_mruby_module_request.o `test -f 'shrpx_mruby_module_request.cc' || echo '$(srcdir)/'`shrpx_mruby_module_request.cc libnghttpx_a-shrpx_mruby_module_request.obj: shrpx_mruby_module_request.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_mruby_module_request.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_mruby_module_request.Tpo -c -o libnghttpx_a-shrpx_mruby_module_request.obj `if test -f 'shrpx_mruby_module_request.cc'; then $(CYGPATH_W) 'shrpx_mruby_module_request.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_mruby_module_request.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_mruby_module_request.Tpo $(DEPDIR)/libnghttpx_a-shrpx_mruby_module_request.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_mruby_module_request.cc' object='libnghttpx_a-shrpx_mruby_module_request.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_mruby_module_request.obj `if test -f 'shrpx_mruby_module_request.cc'; then $(CYGPATH_W) 'shrpx_mruby_module_request.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_mruby_module_request.cc'; fi` libnghttpx_a-shrpx_mruby_module_response.o: shrpx_mruby_module_response.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_mruby_module_response.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_mruby_module_response.Tpo -c -o libnghttpx_a-shrpx_mruby_module_response.o `test -f 'shrpx_mruby_module_response.cc' || echo '$(srcdir)/'`shrpx_mruby_module_response.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_mruby_module_response.Tpo $(DEPDIR)/libnghttpx_a-shrpx_mruby_module_response.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_mruby_module_response.cc' object='libnghttpx_a-shrpx_mruby_module_response.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_mruby_module_response.o `test -f 'shrpx_mruby_module_response.cc' || echo '$(srcdir)/'`shrpx_mruby_module_response.cc libnghttpx_a-shrpx_mruby_module_response.obj: shrpx_mruby_module_response.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_mruby_module_response.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_mruby_module_response.Tpo -c -o libnghttpx_a-shrpx_mruby_module_response.obj `if test -f 'shrpx_mruby_module_response.cc'; then $(CYGPATH_W) 'shrpx_mruby_module_response.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_mruby_module_response.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_mruby_module_response.Tpo $(DEPDIR)/libnghttpx_a-shrpx_mruby_module_response.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_mruby_module_response.cc' object='libnghttpx_a-shrpx_mruby_module_response.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_mruby_module_response.obj `if test -f 'shrpx_mruby_module_response.cc'; then $(CYGPATH_W) 'shrpx_mruby_module_response.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_mruby_module_response.cc'; fi` libnghttpx_a-shrpx_quic.o: shrpx_quic.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_quic.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_quic.Tpo -c -o libnghttpx_a-shrpx_quic.o `test -f 'shrpx_quic.cc' || echo '$(srcdir)/'`shrpx_quic.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_quic.Tpo $(DEPDIR)/libnghttpx_a-shrpx_quic.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_quic.cc' object='libnghttpx_a-shrpx_quic.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_quic.o `test -f 'shrpx_quic.cc' || echo '$(srcdir)/'`shrpx_quic.cc libnghttpx_a-shrpx_quic.obj: shrpx_quic.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_quic.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_quic.Tpo -c -o libnghttpx_a-shrpx_quic.obj `if test -f 'shrpx_quic.cc'; then $(CYGPATH_W) 'shrpx_quic.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_quic.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_quic.Tpo $(DEPDIR)/libnghttpx_a-shrpx_quic.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_quic.cc' object='libnghttpx_a-shrpx_quic.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_quic.obj `if test -f 'shrpx_quic.cc'; then $(CYGPATH_W) 'shrpx_quic.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_quic.cc'; fi` libnghttpx_a-shrpx_quic_listener.o: shrpx_quic_listener.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_quic_listener.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_quic_listener.Tpo -c -o libnghttpx_a-shrpx_quic_listener.o `test -f 'shrpx_quic_listener.cc' || echo '$(srcdir)/'`shrpx_quic_listener.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_quic_listener.Tpo $(DEPDIR)/libnghttpx_a-shrpx_quic_listener.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_quic_listener.cc' object='libnghttpx_a-shrpx_quic_listener.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_quic_listener.o `test -f 'shrpx_quic_listener.cc' || echo '$(srcdir)/'`shrpx_quic_listener.cc libnghttpx_a-shrpx_quic_listener.obj: shrpx_quic_listener.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_quic_listener.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_quic_listener.Tpo -c -o libnghttpx_a-shrpx_quic_listener.obj `if test -f 'shrpx_quic_listener.cc'; then $(CYGPATH_W) 'shrpx_quic_listener.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_quic_listener.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_quic_listener.Tpo $(DEPDIR)/libnghttpx_a-shrpx_quic_listener.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_quic_listener.cc' object='libnghttpx_a-shrpx_quic_listener.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_quic_listener.obj `if test -f 'shrpx_quic_listener.cc'; then $(CYGPATH_W) 'shrpx_quic_listener.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_quic_listener.cc'; fi` libnghttpx_a-shrpx_quic_connection_handler.o: shrpx_quic_connection_handler.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_quic_connection_handler.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_quic_connection_handler.Tpo -c -o libnghttpx_a-shrpx_quic_connection_handler.o `test -f 'shrpx_quic_connection_handler.cc' || echo '$(srcdir)/'`shrpx_quic_connection_handler.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_quic_connection_handler.Tpo $(DEPDIR)/libnghttpx_a-shrpx_quic_connection_handler.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_quic_connection_handler.cc' object='libnghttpx_a-shrpx_quic_connection_handler.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_quic_connection_handler.o `test -f 'shrpx_quic_connection_handler.cc' || echo '$(srcdir)/'`shrpx_quic_connection_handler.cc libnghttpx_a-shrpx_quic_connection_handler.obj: shrpx_quic_connection_handler.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_quic_connection_handler.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_quic_connection_handler.Tpo -c -o libnghttpx_a-shrpx_quic_connection_handler.obj `if test -f 'shrpx_quic_connection_handler.cc'; then $(CYGPATH_W) 'shrpx_quic_connection_handler.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_quic_connection_handler.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_quic_connection_handler.Tpo $(DEPDIR)/libnghttpx_a-shrpx_quic_connection_handler.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_quic_connection_handler.cc' object='libnghttpx_a-shrpx_quic_connection_handler.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_quic_connection_handler.obj `if test -f 'shrpx_quic_connection_handler.cc'; then $(CYGPATH_W) 'shrpx_quic_connection_handler.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_quic_connection_handler.cc'; fi` libnghttpx_a-shrpx_http3_upstream.o: shrpx_http3_upstream.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_http3_upstream.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_http3_upstream.Tpo -c -o libnghttpx_a-shrpx_http3_upstream.o `test -f 'shrpx_http3_upstream.cc' || echo '$(srcdir)/'`shrpx_http3_upstream.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_http3_upstream.Tpo $(DEPDIR)/libnghttpx_a-shrpx_http3_upstream.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_http3_upstream.cc' object='libnghttpx_a-shrpx_http3_upstream.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_http3_upstream.o `test -f 'shrpx_http3_upstream.cc' || echo '$(srcdir)/'`shrpx_http3_upstream.cc libnghttpx_a-shrpx_http3_upstream.obj: shrpx_http3_upstream.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_http3_upstream.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_http3_upstream.Tpo -c -o libnghttpx_a-shrpx_http3_upstream.obj `if test -f 'shrpx_http3_upstream.cc'; then $(CYGPATH_W) 'shrpx_http3_upstream.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_http3_upstream.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_http3_upstream.Tpo $(DEPDIR)/libnghttpx_a-shrpx_http3_upstream.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_http3_upstream.cc' object='libnghttpx_a-shrpx_http3_upstream.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_http3_upstream.obj `if test -f 'shrpx_http3_upstream.cc'; then $(CYGPATH_W) 'shrpx_http3_upstream.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_http3_upstream.cc'; fi` libnghttpx_a-http3.o: http3.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-http3.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-http3.Tpo -c -o libnghttpx_a-http3.o `test -f 'http3.cc' || echo '$(srcdir)/'`http3.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-http3.Tpo $(DEPDIR)/libnghttpx_a-http3.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='http3.cc' object='libnghttpx_a-http3.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-http3.o `test -f 'http3.cc' || echo '$(srcdir)/'`http3.cc libnghttpx_a-http3.obj: http3.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-http3.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-http3.Tpo -c -o libnghttpx_a-http3.obj `if test -f 'http3.cc'; then $(CYGPATH_W) 'http3.cc'; else $(CYGPATH_W) '$(srcdir)/http3.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-http3.Tpo $(DEPDIR)/libnghttpx_a-http3.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='http3.cc' object='libnghttpx_a-http3.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-http3.obj `if test -f 'http3.cc'; then $(CYGPATH_W) 'http3.cc'; else $(CYGPATH_W) '$(srcdir)/http3.cc'; fi` libnghttpx_a-siphash.o: siphash.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-siphash.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-siphash.Tpo -c -o libnghttpx_a-siphash.o `test -f 'siphash.cc' || echo '$(srcdir)/'`siphash.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-siphash.Tpo $(DEPDIR)/libnghttpx_a-siphash.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='siphash.cc' object='libnghttpx_a-siphash.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-siphash.o `test -f 'siphash.cc' || echo '$(srcdir)/'`siphash.cc libnghttpx_a-siphash.obj: siphash.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-siphash.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-siphash.Tpo -c -o libnghttpx_a-siphash.obj `if test -f 'siphash.cc'; then $(CYGPATH_W) 'siphash.cc'; else $(CYGPATH_W) '$(srcdir)/siphash.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-siphash.Tpo $(DEPDIR)/libnghttpx_a-siphash.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='siphash.cc' object='libnghttpx_a-siphash.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-siphash.obj `if test -f 'siphash.cc'; then $(CYGPATH_W) 'siphash.cc'; else $(CYGPATH_W) '$(srcdir)/siphash.cc'; fi` nghttpx-shrpx.o: shrpx.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx-shrpx.o -MD -MP -MF $(DEPDIR)/nghttpx-shrpx.Tpo -c -o nghttpx-shrpx.o `test -f 'shrpx.cc' || echo '$(srcdir)/'`shrpx.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx-shrpx.Tpo $(DEPDIR)/nghttpx-shrpx.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx.cc' object='nghttpx-shrpx.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx-shrpx.o `test -f 'shrpx.cc' || echo '$(srcdir)/'`shrpx.cc nghttpx-shrpx.obj: shrpx.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx-shrpx.obj -MD -MP -MF $(DEPDIR)/nghttpx-shrpx.Tpo -c -o nghttpx-shrpx.obj `if test -f 'shrpx.cc'; then $(CYGPATH_W) 'shrpx.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx-shrpx.Tpo $(DEPDIR)/nghttpx-shrpx.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx.cc' object='nghttpx-shrpx.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx-shrpx.obj `if test -f 'shrpx.cc'; then $(CYGPATH_W) 'shrpx.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx.cc'; fi` nghttpx_unittest-shrpx-unittest.o: shrpx-unittest.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx-unittest.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Tpo -c -o nghttpx_unittest-shrpx-unittest.o `test -f 'shrpx-unittest.cc' || echo '$(srcdir)/'`shrpx-unittest.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Tpo $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx-unittest.cc' object='nghttpx_unittest-shrpx-unittest.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx-unittest.o `test -f 'shrpx-unittest.cc' || echo '$(srcdir)/'`shrpx-unittest.cc nghttpx_unittest-shrpx-unittest.obj: shrpx-unittest.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx-unittest.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Tpo -c -o nghttpx_unittest-shrpx-unittest.obj `if test -f 'shrpx-unittest.cc'; then $(CYGPATH_W) 'shrpx-unittest.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx-unittest.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Tpo $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx-unittest.cc' object='nghttpx_unittest-shrpx-unittest.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx-unittest.obj `if test -f 'shrpx-unittest.cc'; then $(CYGPATH_W) 'shrpx-unittest.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx-unittest.cc'; fi` nghttpx_unittest-shrpx_tls_test.o: shrpx_tls_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_tls_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_tls_test.Tpo -c -o nghttpx_unittest-shrpx_tls_test.o `test -f 'shrpx_tls_test.cc' || echo '$(srcdir)/'`shrpx_tls_test.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_tls_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_tls_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_tls_test.cc' object='nghttpx_unittest-shrpx_tls_test.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_tls_test.o `test -f 'shrpx_tls_test.cc' || echo '$(srcdir)/'`shrpx_tls_test.cc nghttpx_unittest-shrpx_tls_test.obj: shrpx_tls_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_tls_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_tls_test.Tpo -c -o nghttpx_unittest-shrpx_tls_test.obj `if test -f 'shrpx_tls_test.cc'; then $(CYGPATH_W) 'shrpx_tls_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_tls_test.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_tls_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_tls_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_tls_test.cc' object='nghttpx_unittest-shrpx_tls_test.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_tls_test.obj `if test -f 'shrpx_tls_test.cc'; then $(CYGPATH_W) 'shrpx_tls_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_tls_test.cc'; fi` nghttpx_unittest-shrpx_downstream_test.o: shrpx_downstream_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_downstream_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Tpo -c -o nghttpx_unittest-shrpx_downstream_test.o `test -f 'shrpx_downstream_test.cc' || echo '$(srcdir)/'`shrpx_downstream_test.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_downstream_test.cc' object='nghttpx_unittest-shrpx_downstream_test.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_downstream_test.o `test -f 'shrpx_downstream_test.cc' || echo '$(srcdir)/'`shrpx_downstream_test.cc nghttpx_unittest-shrpx_downstream_test.obj: shrpx_downstream_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_downstream_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Tpo -c -o nghttpx_unittest-shrpx_downstream_test.obj `if test -f 'shrpx_downstream_test.cc'; then $(CYGPATH_W) 'shrpx_downstream_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_downstream_test.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_downstream_test.cc' object='nghttpx_unittest-shrpx_downstream_test.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_downstream_test.obj `if test -f 'shrpx_downstream_test.cc'; then $(CYGPATH_W) 'shrpx_downstream_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_downstream_test.cc'; fi` nghttpx_unittest-shrpx_config_test.o: shrpx_config_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_config_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Tpo -c -o nghttpx_unittest-shrpx_config_test.o `test -f 'shrpx_config_test.cc' || echo '$(srcdir)/'`shrpx_config_test.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_config_test.cc' object='nghttpx_unittest-shrpx_config_test.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_config_test.o `test -f 'shrpx_config_test.cc' || echo '$(srcdir)/'`shrpx_config_test.cc nghttpx_unittest-shrpx_config_test.obj: shrpx_config_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_config_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Tpo -c -o nghttpx_unittest-shrpx_config_test.obj `if test -f 'shrpx_config_test.cc'; then $(CYGPATH_W) 'shrpx_config_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_config_test.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_config_test.cc' object='nghttpx_unittest-shrpx_config_test.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_config_test.obj `if test -f 'shrpx_config_test.cc'; then $(CYGPATH_W) 'shrpx_config_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_config_test.cc'; fi` nghttpx_unittest-shrpx_worker_test.o: shrpx_worker_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_worker_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_worker_test.Tpo -c -o nghttpx_unittest-shrpx_worker_test.o `test -f 'shrpx_worker_test.cc' || echo '$(srcdir)/'`shrpx_worker_test.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_worker_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_worker_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_worker_test.cc' object='nghttpx_unittest-shrpx_worker_test.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_worker_test.o `test -f 'shrpx_worker_test.cc' || echo '$(srcdir)/'`shrpx_worker_test.cc nghttpx_unittest-shrpx_worker_test.obj: shrpx_worker_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_worker_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_worker_test.Tpo -c -o nghttpx_unittest-shrpx_worker_test.obj `if test -f 'shrpx_worker_test.cc'; then $(CYGPATH_W) 'shrpx_worker_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_worker_test.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_worker_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_worker_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_worker_test.cc' object='nghttpx_unittest-shrpx_worker_test.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_worker_test.obj `if test -f 'shrpx_worker_test.cc'; then $(CYGPATH_W) 'shrpx_worker_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_worker_test.cc'; fi` nghttpx_unittest-shrpx_http_test.o: shrpx_http_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_http_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_http_test.Tpo -c -o nghttpx_unittest-shrpx_http_test.o `test -f 'shrpx_http_test.cc' || echo '$(srcdir)/'`shrpx_http_test.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_http_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_http_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_http_test.cc' object='nghttpx_unittest-shrpx_http_test.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_http_test.o `test -f 'shrpx_http_test.cc' || echo '$(srcdir)/'`shrpx_http_test.cc nghttpx_unittest-shrpx_http_test.obj: shrpx_http_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_http_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_http_test.Tpo -c -o nghttpx_unittest-shrpx_http_test.obj `if test -f 'shrpx_http_test.cc'; then $(CYGPATH_W) 'shrpx_http_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_http_test.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_http_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_http_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_http_test.cc' object='nghttpx_unittest-shrpx_http_test.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_http_test.obj `if test -f 'shrpx_http_test.cc'; then $(CYGPATH_W) 'shrpx_http_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_http_test.cc'; fi` nghttpx_unittest-shrpx_router_test.o: shrpx_router_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_router_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_router_test.Tpo -c -o nghttpx_unittest-shrpx_router_test.o `test -f 'shrpx_router_test.cc' || echo '$(srcdir)/'`shrpx_router_test.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_router_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_router_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_router_test.cc' object='nghttpx_unittest-shrpx_router_test.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_router_test.o `test -f 'shrpx_router_test.cc' || echo '$(srcdir)/'`shrpx_router_test.cc nghttpx_unittest-shrpx_router_test.obj: shrpx_router_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_router_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_router_test.Tpo -c -o nghttpx_unittest-shrpx_router_test.obj `if test -f 'shrpx_router_test.cc'; then $(CYGPATH_W) 'shrpx_router_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_router_test.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_router_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_router_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_router_test.cc' object='nghttpx_unittest-shrpx_router_test.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_router_test.obj `if test -f 'shrpx_router_test.cc'; then $(CYGPATH_W) 'shrpx_router_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_router_test.cc'; fi` nghttpx_unittest-http2_test.o: http2_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-http2_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-http2_test.Tpo -c -o nghttpx_unittest-http2_test.o `test -f 'http2_test.cc' || echo '$(srcdir)/'`http2_test.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-http2_test.Tpo $(DEPDIR)/nghttpx_unittest-http2_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='http2_test.cc' object='nghttpx_unittest-http2_test.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-http2_test.o `test -f 'http2_test.cc' || echo '$(srcdir)/'`http2_test.cc nghttpx_unittest-http2_test.obj: http2_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-http2_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-http2_test.Tpo -c -o nghttpx_unittest-http2_test.obj `if test -f 'http2_test.cc'; then $(CYGPATH_W) 'http2_test.cc'; else $(CYGPATH_W) '$(srcdir)/http2_test.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-http2_test.Tpo $(DEPDIR)/nghttpx_unittest-http2_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='http2_test.cc' object='nghttpx_unittest-http2_test.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-http2_test.obj `if test -f 'http2_test.cc'; then $(CYGPATH_W) 'http2_test.cc'; else $(CYGPATH_W) '$(srcdir)/http2_test.cc'; fi` nghttpx_unittest-util_test.o: util_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-util_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-util_test.Tpo -c -o nghttpx_unittest-util_test.o `test -f 'util_test.cc' || echo '$(srcdir)/'`util_test.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-util_test.Tpo $(DEPDIR)/nghttpx_unittest-util_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='util_test.cc' object='nghttpx_unittest-util_test.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-util_test.o `test -f 'util_test.cc' || echo '$(srcdir)/'`util_test.cc nghttpx_unittest-util_test.obj: util_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-util_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-util_test.Tpo -c -o nghttpx_unittest-util_test.obj `if test -f 'util_test.cc'; then $(CYGPATH_W) 'util_test.cc'; else $(CYGPATH_W) '$(srcdir)/util_test.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-util_test.Tpo $(DEPDIR)/nghttpx_unittest-util_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='util_test.cc' object='nghttpx_unittest-util_test.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-util_test.obj `if test -f 'util_test.cc'; then $(CYGPATH_W) 'util_test.cc'; else $(CYGPATH_W) '$(srcdir)/util_test.cc'; fi` nghttpx_unittest-buffer_test.o: buffer_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-buffer_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-buffer_test.Tpo -c -o nghttpx_unittest-buffer_test.o `test -f 'buffer_test.cc' || echo '$(srcdir)/'`buffer_test.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-buffer_test.Tpo $(DEPDIR)/nghttpx_unittest-buffer_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='buffer_test.cc' object='nghttpx_unittest-buffer_test.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-buffer_test.o `test -f 'buffer_test.cc' || echo '$(srcdir)/'`buffer_test.cc nghttpx_unittest-buffer_test.obj: buffer_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-buffer_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-buffer_test.Tpo -c -o nghttpx_unittest-buffer_test.obj `if test -f 'buffer_test.cc'; then $(CYGPATH_W) 'buffer_test.cc'; else $(CYGPATH_W) '$(srcdir)/buffer_test.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-buffer_test.Tpo $(DEPDIR)/nghttpx_unittest-buffer_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='buffer_test.cc' object='nghttpx_unittest-buffer_test.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-buffer_test.obj `if test -f 'buffer_test.cc'; then $(CYGPATH_W) 'buffer_test.cc'; else $(CYGPATH_W) '$(srcdir)/buffer_test.cc'; fi` nghttpx_unittest-memchunk_test.o: memchunk_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-memchunk_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-memchunk_test.Tpo -c -o nghttpx_unittest-memchunk_test.o `test -f 'memchunk_test.cc' || echo '$(srcdir)/'`memchunk_test.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-memchunk_test.Tpo $(DEPDIR)/nghttpx_unittest-memchunk_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='memchunk_test.cc' object='nghttpx_unittest-memchunk_test.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-memchunk_test.o `test -f 'memchunk_test.cc' || echo '$(srcdir)/'`memchunk_test.cc nghttpx_unittest-memchunk_test.obj: memchunk_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-memchunk_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-memchunk_test.Tpo -c -o nghttpx_unittest-memchunk_test.obj `if test -f 'memchunk_test.cc'; then $(CYGPATH_W) 'memchunk_test.cc'; else $(CYGPATH_W) '$(srcdir)/memchunk_test.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-memchunk_test.Tpo $(DEPDIR)/nghttpx_unittest-memchunk_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='memchunk_test.cc' object='nghttpx_unittest-memchunk_test.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-memchunk_test.obj `if test -f 'memchunk_test.cc'; then $(CYGPATH_W) 'memchunk_test.cc'; else $(CYGPATH_W) '$(srcdir)/memchunk_test.cc'; fi` nghttpx_unittest-template_test.o: template_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-template_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-template_test.Tpo -c -o nghttpx_unittest-template_test.o `test -f 'template_test.cc' || echo '$(srcdir)/'`template_test.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-template_test.Tpo $(DEPDIR)/nghttpx_unittest-template_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='template_test.cc' object='nghttpx_unittest-template_test.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-template_test.o `test -f 'template_test.cc' || echo '$(srcdir)/'`template_test.cc nghttpx_unittest-template_test.obj: template_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-template_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-template_test.Tpo -c -o nghttpx_unittest-template_test.obj `if test -f 'template_test.cc'; then $(CYGPATH_W) 'template_test.cc'; else $(CYGPATH_W) '$(srcdir)/template_test.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-template_test.Tpo $(DEPDIR)/nghttpx_unittest-template_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='template_test.cc' object='nghttpx_unittest-template_test.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-template_test.obj `if test -f 'template_test.cc'; then $(CYGPATH_W) 'template_test.cc'; else $(CYGPATH_W) '$(srcdir)/template_test.cc'; fi` nghttpx_unittest-base64_test.o: base64_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-base64_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-base64_test.Tpo -c -o nghttpx_unittest-base64_test.o `test -f 'base64_test.cc' || echo '$(srcdir)/'`base64_test.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-base64_test.Tpo $(DEPDIR)/nghttpx_unittest-base64_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='base64_test.cc' object='nghttpx_unittest-base64_test.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-base64_test.o `test -f 'base64_test.cc' || echo '$(srcdir)/'`base64_test.cc nghttpx_unittest-base64_test.obj: base64_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-base64_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-base64_test.Tpo -c -o nghttpx_unittest-base64_test.obj `if test -f 'base64_test.cc'; then $(CYGPATH_W) 'base64_test.cc'; else $(CYGPATH_W) '$(srcdir)/base64_test.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-base64_test.Tpo $(DEPDIR)/nghttpx_unittest-base64_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='base64_test.cc' object='nghttpx_unittest-base64_test.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-base64_test.obj `if test -f 'base64_test.cc'; then $(CYGPATH_W) 'base64_test.cc'; else $(CYGPATH_W) '$(srcdir)/base64_test.cc'; fi` nghttpx_unittest-siphash_test.o: siphash_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-siphash_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-siphash_test.Tpo -c -o nghttpx_unittest-siphash_test.o `test -f 'siphash_test.cc' || echo '$(srcdir)/'`siphash_test.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-siphash_test.Tpo $(DEPDIR)/nghttpx_unittest-siphash_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='siphash_test.cc' object='nghttpx_unittest-siphash_test.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-siphash_test.o `test -f 'siphash_test.cc' || echo '$(srcdir)/'`siphash_test.cc nghttpx_unittest-siphash_test.obj: siphash_test.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-siphash_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-siphash_test.Tpo -c -o nghttpx_unittest-siphash_test.obj `if test -f 'siphash_test.cc'; then $(CYGPATH_W) 'siphash_test.cc'; else $(CYGPATH_W) '$(srcdir)/siphash_test.cc'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-siphash_test.Tpo $(DEPDIR)/nghttpx_unittest-siphash_test.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='siphash_test.cc' object='nghttpx_unittest-siphash_test.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-siphash_test.obj `if test -f 'siphash_test.cc'; then $(CYGPATH_W) 'siphash_test.cc'; else $(CYGPATH_W) '$(srcdir)/siphash_test.cc'; fi` mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags # Recover from deleted '.trs' file; this should ensure that # "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create # both 'foo.log' and 'foo.trs'. Break the recipe in two subshells # to avoid problems with "make -n". .log.trs: rm -f $< $@ $(MAKE) $(AM_MAKEFLAGS) $< # Leading 'am--fnord' is there to ensure the list of targets does not # expand to empty, as could happen e.g. with make check TESTS=''. am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) am--force-recheck: @: $(TEST_SUITE_LOG): $(TEST_LOGS) @$(am__set_TESTS_bases); \ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ redo_bases=`for i in $$bases; do \ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ done`; \ if test -n "$$redo_bases"; then \ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ if $(am__make_dryrun); then :; else \ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ fi; \ if test -n "$$am__remaking_logs"; then \ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ "recursion detected" >&2; \ elif test -n "$$redo_logs"; then \ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ fi; \ if $(am__make_dryrun); then :; else \ st=0; \ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ for i in $$redo_bases; do \ test -f $$i.trs && test -r $$i.trs \ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ test -f $$i.log && test -r $$i.log \ || { echo "$$errmsg $$i.log" >&2; st=1; }; \ done; \ test $$st -eq 0 || exit 1; \ fi @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ ws='[ ]'; \ results=`for b in $$bases; do echo $$b.trs; done`; \ test -n "$$results" || results=/dev/null; \ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ if test `expr $$fail + $$xpass + $$error` -eq 0; then \ success=true; \ else \ success=false; \ fi; \ br='==================='; br=$$br$$br$$br$$br; \ result_count () \ { \ if test x"$$1" = x"--maybe-color"; then \ maybe_colorize=yes; \ elif test x"$$1" = x"--no-color"; then \ maybe_colorize=no; \ else \ echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ shift; \ desc=$$1 count=$$2; \ if test $$maybe_colorize = yes && test $$count -gt 0; then \ color_start=$$3 color_end=$$std; \ else \ color_start= color_end=; \ fi; \ echo "$${color_start}# $$desc $$count$${color_end}"; \ }; \ create_testsuite_report () \ { \ result_count $$1 "TOTAL:" $$all "$$brg"; \ result_count $$1 "PASS: " $$pass "$$grn"; \ result_count $$1 "SKIP: " $$skip "$$blu"; \ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ result_count $$1 "FAIL: " $$fail "$$red"; \ result_count $$1 "XPASS:" $$xpass "$$red"; \ result_count $$1 "ERROR:" $$error "$$mgn"; \ }; \ { \ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ $(am__rst_title); \ create_testsuite_report --no-color; \ echo; \ echo ".. contents:: :depth: 2"; \ echo; \ for b in $$bases; do echo $$b; done \ | $(am__create_global_log); \ } >$(TEST_SUITE_LOG).tmp || exit 1; \ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ if $$success; then \ col="$$grn"; \ else \ col="$$red"; \ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ fi; \ echo "$${col}$$br$${std}"; \ echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}"; \ echo "$${col}$$br$${std}"; \ create_testsuite_report --maybe-color; \ echo "$$col$$br$$std"; \ if $$success; then :; else \ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ if test -n "$(PACKAGE_BUGREPORT)"; then \ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ fi; \ echo "$$col$$br$$std"; \ fi; \ $$success || exit 1 check-TESTS: $(check_PROGRAMS) @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ log_list=`for i in $$bases; do echo $$i.log; done`; \ trs_list=`for i in $$bases; do echo $$i.trs; done`; \ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ exit $$?; recheck: all $(check_PROGRAMS) @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ bases=`for i in $$bases; do echo $$i; done \ | $(am__list_recheck_tests)` || exit 1; \ log_list=`for i in $$bases; do echo $$i.log; done`; \ log_list=`echo $$log_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ am__force_recheck=am--force-recheck \ TEST_LOGS="$$log_list"; \ exit $$? nghttpx-unittest.log: nghttpx-unittest$(EXEEXT) @p='nghttpx-unittest$(EXEEXT)'; \ b='nghttpx-unittest'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) .test.log: @p='$<'; \ $(am__set_b); \ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) @am__EXEEXT_TRUE@.test$(EXEEXT).log: @am__EXEEXT_TRUE@ @p='$<'; \ @am__EXEEXT_TRUE@ $(am__set_b); \ @am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ @am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ @am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ @am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) $(MAKE) $(AM_MAKEFLAGS) check-TESTS check: check-recursive all-am: Makefile $(PROGRAMS) $(LIBRARIES) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(bindir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -test -z "$(top_builddir)/tests/munit/$(DEPDIR)/$(am__dirstamp)" || rm -f $(top_builddir)/tests/munit/$(DEPDIR)/$(am__dirstamp) -test -z "$(top_builddir)/tests/munit/$(am__dirstamp)" || rm -f $(top_builddir)/tests/munit/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \ clean-libtool clean-noinstLIBRARIES mostlyclean-am distclean: distclean-recursive -rm -f $(top_builddir)/tests/munit/$(DEPDIR)/nghttpx_unittest-munit.Po -rm -f ./$(DEPDIR)/HtmlParser.Po -rm -f ./$(DEPDIR)/HttpServer.Po -rm -f ./$(DEPDIR)/app_helper.Po -rm -f ./$(DEPDIR)/comp_helper.Po -rm -f ./$(DEPDIR)/deflatehd.Po -rm -f ./$(DEPDIR)/h2load.Po -rm -f ./$(DEPDIR)/h2load_http1_session.Po -rm -f ./$(DEPDIR)/h2load_http2_session.Po -rm -f ./$(DEPDIR)/h2load_http3_session.Po -rm -f ./$(DEPDIR)/h2load_quic.Po -rm -f ./$(DEPDIR)/http2.Po -rm -f ./$(DEPDIR)/inflatehd.Po -rm -f ./$(DEPDIR)/libnghttpx_a-app_helper.Po -rm -f ./$(DEPDIR)/libnghttpx_a-http2.Po -rm -f ./$(DEPDIR)/libnghttpx_a-http3.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_accept_handler.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_api_downstream_connection.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_client_handler.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_config.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_connect_blocker.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_connection.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_connection_handler.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_dns_resolver.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_dns_tracker.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_downstream.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_downstream_connection.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_downstream_connection_pool.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_downstream_queue.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_dual_dns_resolver.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_health_monitor_downstream_connection.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http2_downstream_connection.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http2_session.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http2_upstream.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http3_upstream.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http_downstream_connection.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_https_upstream.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_io_control.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_live_check.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_log.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_log_config.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_memcached_connection.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_memcached_dispatcher.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_mruby.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_env.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_request.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_response.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_null_downstream_connection.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_quic.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_quic_connection_handler.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_quic_listener.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_rate_limit.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_router.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_signal.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_tls.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_worker.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_worker_process.Po -rm -f ./$(DEPDIR)/libnghttpx_a-siphash.Po -rm -f ./$(DEPDIR)/libnghttpx_a-timegm.Po -rm -f ./$(DEPDIR)/libnghttpx_a-tls.Po -rm -f ./$(DEPDIR)/libnghttpx_a-util.Po -rm -f ./$(DEPDIR)/libnghttpx_a-xsi_strerror.Po -rm -f ./$(DEPDIR)/nghttp.Po -rm -f ./$(DEPDIR)/nghttp2_gzip.Po -rm -f ./$(DEPDIR)/nghttpd.Po -rm -f ./$(DEPDIR)/nghttpx-shrpx.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-base64_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-buffer_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-http2_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-memchunk_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx-unittest.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_config_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_http_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_router_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_tls_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_worker_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-siphash_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-template_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-util_test.Po -rm -f ./$(DEPDIR)/timegm.Po -rm -f ./$(DEPDIR)/tls.Po -rm -f ./$(DEPDIR)/util.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-binPROGRAMS install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f $(top_builddir)/tests/munit/$(DEPDIR)/nghttpx_unittest-munit.Po -rm -f ./$(DEPDIR)/HtmlParser.Po -rm -f ./$(DEPDIR)/HttpServer.Po -rm -f ./$(DEPDIR)/app_helper.Po -rm -f ./$(DEPDIR)/comp_helper.Po -rm -f ./$(DEPDIR)/deflatehd.Po -rm -f ./$(DEPDIR)/h2load.Po -rm -f ./$(DEPDIR)/h2load_http1_session.Po -rm -f ./$(DEPDIR)/h2load_http2_session.Po -rm -f ./$(DEPDIR)/h2load_http3_session.Po -rm -f ./$(DEPDIR)/h2load_quic.Po -rm -f ./$(DEPDIR)/http2.Po -rm -f ./$(DEPDIR)/inflatehd.Po -rm -f ./$(DEPDIR)/libnghttpx_a-app_helper.Po -rm -f ./$(DEPDIR)/libnghttpx_a-http2.Po -rm -f ./$(DEPDIR)/libnghttpx_a-http3.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_accept_handler.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_api_downstream_connection.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_client_handler.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_config.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_connect_blocker.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_connection.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_connection_handler.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_dns_resolver.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_dns_tracker.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_downstream.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_downstream_connection.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_downstream_connection_pool.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_downstream_queue.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_dual_dns_resolver.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_health_monitor_downstream_connection.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http2_downstream_connection.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http2_session.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http2_upstream.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http3_upstream.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http_downstream_connection.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_https_upstream.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_io_control.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_live_check.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_log.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_log_config.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_memcached_connection.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_memcached_dispatcher.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_mruby.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_env.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_request.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_response.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_null_downstream_connection.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_quic.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_quic_connection_handler.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_quic_listener.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_rate_limit.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_router.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_signal.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_tls.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_worker.Po -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_worker_process.Po -rm -f ./$(DEPDIR)/libnghttpx_a-siphash.Po -rm -f ./$(DEPDIR)/libnghttpx_a-timegm.Po -rm -f ./$(DEPDIR)/libnghttpx_a-tls.Po -rm -f ./$(DEPDIR)/libnghttpx_a-util.Po -rm -f ./$(DEPDIR)/libnghttpx_a-xsi_strerror.Po -rm -f ./$(DEPDIR)/nghttp.Po -rm -f ./$(DEPDIR)/nghttp2_gzip.Po -rm -f ./$(DEPDIR)/nghttpd.Po -rm -f ./$(DEPDIR)/nghttpx-shrpx.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-base64_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-buffer_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-http2_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-memchunk_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx-unittest.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_config_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_http_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_router_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_tls_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_worker_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-siphash_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-template_test.Po -rm -f ./$(DEPDIR)/nghttpx_unittest-util_test.Po -rm -f ./$(DEPDIR)/timegm.Po -rm -f ./$(DEPDIR)/tls.Po -rm -f ./$(DEPDIR)/util.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-binPROGRAMS .MAKE: $(am__recursive_targets) check-am install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ am--depfiles check check-TESTS check-am clean \ clean-binPROGRAMS clean-checkPROGRAMS clean-generic \ clean-libtool clean-noinstLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-binPROGRAMS \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs installdirs-am \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am recheck tags tags-am uninstall \ uninstall-am uninstall-binPROGRAMS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nghttp2-1.68.0/src/PaxHeaders/shrpx_tls_test.h0000644000000000000000000000013215077107270016305 xustar0030 mtime=1761382072.999444111 30 atime=1761382106.247310238 30 ctime=1761382109.243299968 nghttp2-1.68.0/src/shrpx_tls_test.h0000644000175100017510000000333415077107270016700 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_TLS_TEST_H #define SHRPX_TLS_TEST_H #ifdef HAVE_CONFIG_H # include #endif // defined(HAVE_CONFIG_H) #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" namespace shrpx { extern const MunitSuite tls_suite; munit_void_test_decl(test_shrpx_tls_create_lookup_tree) munit_void_test_decl(test_shrpx_tls_cert_lookup_tree_add_ssl_ctx) munit_void_test_decl(test_shrpx_tls_tls_hostname_match) munit_void_test_decl(test_shrpx_tls_verify_numeric_hostname) munit_void_test_decl(test_shrpx_tls_verify_dns_hostname) } // namespace shrpx #endif // !defined(SHRPX_TLS_TEST_H) nghttp2-1.68.0/src/PaxHeaders/siphash.cc0000644000000000000000000000013215077107271015016 xustar0030 mtime=1761382073.000444106 30 atime=1761382080.105411485 30 ctime=1761382109.201300089 nghttp2-1.68.0/src/siphash.cc0000644000175100017510000000712015077107271015406 0ustar00runnerrunner/* Copyright 2019 The BoringSSL Authors * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2025 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include "siphash.h" namespace { auto CRYPTO_load_u64_le(std::span in) { uint64_t v; memcpy(&v, in.data(), sizeof(v)); if constexpr (std::endian::native == std::endian::big) { return byteswap(v); } return v; } } // namespace namespace { constexpr void siphash_round(uint64_t v[4]) { v[0] += v[1]; v[2] += v[3]; v[1] = std::rotl(v[1], 13); v[3] = std::rotl(v[3], 16); v[1] ^= v[0]; v[3] ^= v[2]; v[0] = std::rotl(v[0], 32); v[2] += v[1]; v[0] += v[3]; v[1] = std::rotl(v[1], 17); v[3] = std::rotl(v[3], 21); v[1] ^= v[2]; v[3] ^= v[0]; v[2] = std::rotl(v[2], 32); } } // namespace uint64_t siphash24(std::span key, std::span input) { const auto orig_input_len = input.size(); uint64_t v[]{ key[0] ^ UINT64_C(0x736f6d6570736575), key[1] ^ UINT64_C(0x646f72616e646f6d), key[0] ^ UINT64_C(0x6c7967656e657261), key[1] ^ UINT64_C(0x7465646279746573), }; while (input.size() >= sizeof(uint64_t)) { auto m = CRYPTO_load_u64_le(input.first()); v[3] ^= m; siphash_round(v); siphash_round(v); v[0] ^= m; input = input.subspan(sizeof(uint64_t)); } std::array last_block{}; std::ranges::copy(input, std::ranges::begin(last_block)); last_block.back() = orig_input_len & 0xff; auto last_block_word = CRYPTO_load_u64_le(last_block); v[3] ^= last_block_word; siphash_round(v); siphash_round(v); v[0] ^= last_block_word; v[2] ^= 0xff; siphash_round(v); siphash_round(v); siphash_round(v); siphash_round(v); return v[0] ^ v[1] ^ v[2] ^ v[3]; } nghttp2-1.68.0/src/PaxHeaders/shrpx_router.h0000644000000000000000000000013215077107270015764 xustar0030 mtime=1761382072.999444111 30 atime=1761382106.155310643 30 ctime=1761382109.151300234 nghttp2-1.68.0/src/shrpx_router.h0000644000175100017510000001001415077107270016350 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_ROUTER_H #define SHRPX_ROUTER_H #include "shrpx.h" #include #include #include "allocator.h" using namespace nghttp2; namespace shrpx { struct RNode { RNode(); RNode(const std::string_view &s, ssize_t index, ssize_t wildcard_index); RNode(RNode &&) = default; RNode(const RNode &) = delete; RNode &operator=(RNode &&) = default; RNode &operator=(const RNode &) = delete; // Next RNode, sorted by s[0]. std::vector> next; // Stores pointer to the string this node represents. Not // NULL-terminated. std::string_view s; // Index of pattern if match ends in this node. Note that we don't // store duplicated pattern. ssize_t index; // Index of wildcard pattern if query includes this node as prefix // and it still has suffix to match. Note that we don't store // duplicated pattern. ssize_t wildcard_index; }; class Router { public: Router(); ~Router(); Router(Router &&) = default; Router(const Router &) = delete; Router &operator=(Router &&) = default; Router &operator=(const Router &) = delete; // Adds route |pattern| with its |index|. If same pattern has // already been added, the existing index is returned. If // |wildcard| is true, |pattern| is considered as wildcard pattern, // and all paths which have the |pattern| as prefix and are strictly // longer than |pattern| match. The wildcard pattern only works // with match(const std::string_view&, const std::string_view&). size_t add_route(const std::string_view &pattern, size_t index, bool wildcard = false); // Returns the matched index of pattern. -1 if there is no match. ssize_t match(const std::string_view &host, const std::string_view &path) const; // Returns the matched index of pattern |s|. -1 if there is no // match. ssize_t match(const std::string_view &s) const; // Returns the matched index of pattern if a pattern is a suffix of // |s|, otherwise -1. If |*last_node| is not nullptr, it specifies // the first node to start matching. If it is nullptr, match will // start from scratch. When the match was found (the return value // is not -1), |*nread| has the number of bytes matched in |s|, and // |*last_node| has the last matched node. One can continue to // match the longer pattern using the returned |*last_node| to the // another invocation of this function until it returns -1. ssize_t match_prefix(size_t *nread, const RNode **last_node, const std::string_view &s) const; void add_node(RNode *node, const std::string_view &pattern, ssize_t index, ssize_t wildcard_index); void dump() const; private: BlockAllocator balloc_; // The root node of Patricia tree. This is special node and its s // field is nulptr, and len field is 0. RNode root_; }; } // namespace shrpx #endif // !defined(SHRPX_ROUTER_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_health_monitor_downstream_connection.cc0000644000000000000000000000013215077107270024300 xustar0030 mtime=1761382072.993444139 30 atime=1761382106.159310625 30 ctime=1761382109.155300222 nghttp2-1.68.0/src/shrpx_health_monitor_downstream_connection.cc0000644000175100017510000000677015077107270024702 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_health_monitor_downstream_connection.h" #include "shrpx_client_handler.h" #include "shrpx_upstream.h" #include "shrpx_downstream.h" #include "shrpx_log.h" namespace shrpx { HealthMonitorDownstreamConnection::HealthMonitorDownstreamConnection() {} HealthMonitorDownstreamConnection::~HealthMonitorDownstreamConnection() {} int HealthMonitorDownstreamConnection::attach_downstream( Downstream *downstream) { if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream; } downstream_ = downstream; return 0; } void HealthMonitorDownstreamConnection::detach_downstream( Downstream *downstream) { if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream; } downstream_ = nullptr; } int HealthMonitorDownstreamConnection::push_request_headers() { downstream_->set_request_header_sent(true); auto src = downstream_->get_blocked_request_buf(); auto dest = downstream_->get_request_buf(); src->remove(*dest); return 0; } int HealthMonitorDownstreamConnection::push_upload_data_chunk( const uint8_t *data, size_t datalen) { return 0; } int HealthMonitorDownstreamConnection::end_upload_data() { auto upstream = downstream_->get_upstream(); auto &resp = downstream_->response(); resp.http_status = 200; resp.fs.add_header_token("content-length"sv, "0"sv, false, http2::HD_CONTENT_LENGTH); if (upstream->send_reply(downstream_, nullptr, 0) != 0) { return -1; } return 0; } void HealthMonitorDownstreamConnection::pause_read(IOCtrlReason reason) {} int HealthMonitorDownstreamConnection::resume_read(IOCtrlReason reason, size_t consumed) { return 0; } void HealthMonitorDownstreamConnection::force_resume_read() {} int HealthMonitorDownstreamConnection::on_read() { return 0; } int HealthMonitorDownstreamConnection::on_write() { return 0; } void HealthMonitorDownstreamConnection::on_upstream_change(Upstream *upstream) { } bool HealthMonitorDownstreamConnection::poolable() const { return false; } const std::shared_ptr & HealthMonitorDownstreamConnection::get_downstream_addr_group() const { static std::shared_ptr s; return s; } DownstreamAddr *HealthMonitorDownstreamConnection::get_addr() const { return nullptr; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_process.h0000644000000000000000000000013215077107270016122 xustar0030 mtime=1761382072.998444116 30 atime=1761382106.150310665 30 ctime=1761382109.146300248 nghttp2-1.68.0/src/shrpx_process.h0000644000175100017510000000261015077107270016511 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_PROCESS_H #define SHRPX_PROCESS_H #include "shrpx.h" namespace shrpx { inline constexpr uint8_t SHRPX_IPC_REOPEN_LOG = 1; inline constexpr uint8_t SHRPX_IPC_GRACEFUL_SHUTDOWN = 2; } // namespace shrpx #endif // !defined(SHRPX_PROCESS_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_quic_listener.h0000644000000000000000000000013215077107270017312 xustar0030 mtime=1761382072.998444116 30 atime=1761382106.196310462 30 ctime=1761382109.192300115 nghttp2-1.68.0/src/shrpx_quic_listener.h0000644000175100017510000000305315077107270017703 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2021 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_QUIC_LISTENER_H #define SHRPX_QUIC_LISTENER_H #include "shrpx.h" #include namespace shrpx { struct UpstreamAddr; class Worker; class QUICListener { public: QUICListener(const UpstreamAddr *faddr, Worker *worker); ~QUICListener(); void on_read(); private: const UpstreamAddr *faddr_; Worker *worker_; ev_io rev_; }; } // namespace shrpx #endif // !defined(SHRPX_QUIC_LISTENER_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_http2_session.h0000644000000000000000000000013215077107270017250 xustar0030 mtime=1761382072.994444134 30 atime=1761382106.107310854 30 ctime=1761382109.103300372 nghttp2-1.68.0/src/shrpx_http2_session.h0000644000175100017510000002233215077107270017642 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_HTTP2_SESSION_H #define SHRPX_HTTP2_SESSION_H #include "shrpx.h" #include #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include #include #include "llhttp.h" #include "shrpx_connection.h" #include "buffer.h" #include "template.h" using namespace nghttp2; namespace shrpx { class Http2DownstreamConnection; class Worker; class Downstream; struct DownstreamAddrGroup; struct DownstreamAddr; struct DNSQuery; struct StreamData { StreamData *dlnext, *dlprev; Http2DownstreamConnection *dconn; }; enum class FreelistZone { // Http2Session object is not linked in any freelist. NONE, // Http2Session object is linked in address scope // http2_extra_freelist. EXTRA, // Http2Session object is about to be deleted, and it does not // belong to any linked list. GONE }; enum class Http2SessionState { // Disconnected DISCONNECTED, // Connecting proxy and making CONNECT request PROXY_CONNECTING, // Tunnel is established with proxy PROXY_CONNECTED, // Establishing tunnel is failed PROXY_FAILED, // Connecting to downstream and/or performing SSL/TLS handshake CONNECTING, // Connected to downstream CONNECTED, // Connection is started to fail CONNECT_FAILING, // Resolving host name RESOLVING_NAME, }; enum class ConnectionCheck { // Connection checking is not required NONE, // Connection checking is required REQUIRED, // Connection checking has been started STARTED, }; class Http2Session { public: Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, Worker *worker, const std::shared_ptr &group, DownstreamAddr *addr); ~Http2Session(); // If hard is true, all pending requests are abandoned and // associated ClientHandlers will be deleted. int disconnect(bool hard = false); int initiate_connection(); int resolve_name(); void add_downstream_connection(Http2DownstreamConnection *dconn); void remove_downstream_connection(Http2DownstreamConnection *dconn); void remove_stream_data(StreamData *sd); int submit_request(Http2DownstreamConnection *dconn, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider2 *data_prd); int submit_rst_stream(int32_t stream_id, uint32_t error_code); int terminate_session(uint32_t error_code); nghttp2_session *get_session() const; int resume_data(Http2DownstreamConnection *dconn); int connection_made(); int do_read(); int do_write(); int on_read(const uint8_t *data, size_t datalen); int on_write(); int connected(); int read_clear(); int write_clear(); int tls_handshake(); int read_tls(); int write_tls(); // This is a special write function which just stop write event // watcher. int write_void(); int downstream_read_proxy(const uint8_t *data, size_t datalen); int downstream_connect_proxy(); int downstream_read(const uint8_t *data, size_t datalen); int downstream_write(); int noop(); int read_noop(const uint8_t *data, size_t datalen); int write_noop(); void signal_write(); struct ev_loop *get_loop() const; ev_io *get_wev(); Http2SessionState get_state() const; void set_state(Http2SessionState state); void start_settings_timer(); void stop_settings_timer(); SSL *get_ssl() const; int consume(int32_t stream_id, size_t len); // Returns true if request can be issued on downstream connection. bool can_push_request(const Downstream *downstream) const; // Initiates the connection checking if downstream connection has // been established and connection checking is required. void start_checking_connection(); // Resets connection check timer to timeout |t|. After timeout, we // require connection checking. If connection checking is already // enabled, this timeout is for PING ACK timeout. void reset_connection_check_timer(ev_tstamp t); void reset_connection_check_timer_if_not_checking(); // Signals that connection is alive. Internally // reset_connection_check_timer() is called. void connection_alive(); // Change connection check state. void set_connection_check_state(ConnectionCheck state); ConnectionCheck get_connection_check_state() const; bool should_hard_fail() const; void submit_pending_requests(); DownstreamAddr *get_addr() const; const std::shared_ptr &get_downstream_addr_group() const; int handle_downstream_push_promise(Downstream *downstream, int32_t promised_stream_id); int handle_downstream_push_promise_complete(Downstream *downstream, Downstream *promised_downstream); // Returns number of downstream connections, including pushed // streams. size_t get_num_dconns() const; // Adds to group scope http2_avail_freelist. void add_to_avail_freelist(); // Adds to address scope http2_extra_freelist. void add_to_extra_freelist(); // Removes this object from any freelist. If this object is not // linked from any freelist, this function does nothing. void remove_from_freelist(); // Removes this object form any freelist, and marks this object as // not schedulable. void exclude_from_scheduling(); // Returns true if the maximum concurrency is reached. In other // words, the number of currently participated streams in this // session is equal or greater than the max concurrent streams limit // advertised by server. If |extra| is nonzero, it is added to the // number of current concurrent streams when comparing against // server initiated concurrency limit. bool max_concurrency_reached(size_t extra = 0) const; DefaultMemchunks *get_request_buf(); void on_timeout(); // This is called periodically using ev_prepare watcher, and if // group_ is retired (backend has been replaced), send GOAWAY to // shutdown the connection. void check_retire(); // Returns address used to connect to backend. Could be nullptr. const Address *get_raddr() const; // This is called when SETTINGS frame without ACK flag set is // received. void on_settings_received(const nghttp2_frame *frame); bool get_allow_connect_proto() const; using ReadBuf = Buffer<8_k>; Http2Session *dlnext, *dlprev; private: Connection conn_; DefaultMemchunks wb_; ev_timer settings_timer_; // This timer has 2 purpose: when it first timeout, set // connection_check_state_ = ConnectionCheck::REQUIRED. After // connection check has started, this timer is started again and // traps PING ACK timeout. ev_timer connchk_timer_; // timer to initiate connection. usually, this fires immediately. ev_timer initiate_connection_timer_; ev_prepare prep_; DList dconns_; DList streams_; std::function read_, write_; std::function on_read_; std::function on_write_; // Used to parse the response from HTTP proxy std::unique_ptr proxy_htp_; Worker *worker_; // NULL if no TLS is configured SSL_CTX *ssl_ctx_; std::shared_ptr group_; // Address of remote endpoint DownstreamAddr *addr_; nghttp2_session *session_; // Actual remote address used to contact backend. This is initially // nullptr, and may point to either &addr_->addr, // resolved_addr_.get(), or HTTP proxy's address structure. const Address *raddr_; // Resolved IP address if dns parameter is used std::unique_ptr
resolved_addr_; std::unique_ptr dns_query_; Http2SessionState state_; ConnectionCheck connection_check_state_; FreelistZone freelist_zone_; // true if SETTINGS without ACK is received from peer. bool settings_recved_; // true if peer enables RFC 8441 CONNECT protocol. bool allow_connect_proto_; }; nghttp2_session_callbacks *create_http2_downstream_callbacks(); } // namespace shrpx #endif // !defined(SHRPX_HTTP2_SESSION_H) nghttp2-1.68.0/src/PaxHeaders/http3.h0000644000000000000000000000013215077107270014262 xustar0030 mtime=1761382072.988444162 30 atime=1761382106.204310427 30 ctime=1761382109.200300092 nghttp2-1.68.0/src/http3.h0000644000175100017510000000561415077107270014660 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2021 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef HTTP3_H #define HTTP3_H #include "nghttp2_config.h" #include #include #include #include #include "http2.h" #include "template.h" namespace nghttp2 { namespace http3 { // Create nghttp3_nv from |name|, |value| and |flags|. inline nghttp3_nv make_field_flags(const std::string_view &name, const std::string_view &value, uint8_t flags = NGHTTP3_NV_FLAG_NONE) { auto ns = as_uint8_span(std::span{name}); auto vs = as_uint8_span(std::span{value}); return {const_cast(ns.data()), const_cast(vs.data()), ns.size(), vs.size(), flags}; } // Creates nghttp3_nv from |name|, |value| and |flags|. nghttp3 // library does not copy them. inline nghttp3_nv make_field(const std::string_view &name, const std::string_view &value, uint8_t flags = NGHTTP3_NV_FLAG_NONE) { return make_field_flags(name, value, static_cast(NGHTTP3_NV_FLAG_NO_COPY_NAME | NGHTTP3_NV_FLAG_NO_COPY_VALUE | flags)); } // Returns NGHTTP3_NV_FLAG_NEVER_INDEX if |never_index| is true, // otherwise NGHTTP3_NV_FLAG_NONE. inline uint8_t never_index(bool never_index) { return never_index ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE; } // Just like copy_headers_to_nva(), but this adds // NGHTTP3_NV_FLAG_NO_COPY_NAME and NGHTTP3_NV_FLAG_NO_COPY_VALUE. void copy_headers_to_nva_nocopy(std::vector &nva, const HeaderRefs &headers, uint32_t flags); } // namespace http3 } // namespace nghttp2 #endif // !defined(HTTP3_H) nghttp2-1.68.0/src/PaxHeaders/deflatehd.cc0000644000000000000000000000013015077107270015274 xustar0029 mtime=1761382072.98444418 30 atime=1761382106.208310409 29 ctime=1761382109.20430008 nghttp2-1.68.0/src/deflatehd.cc0000644000175100017510000003041015077107270015664 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifdef HAVE_CONFIG_H # include #endif // defined(HAVE_CONFIG_H) #ifdef HAVE_UNISTD_H # include #endif // defined(HAVE_UNISTD_H) #include #include #include #include #include #include #include #include #include #define NGHTTP2_NO_SSIZE_T #include #include "template.h" #include "comp_helper.h" #include "util.h" namespace nghttp2 { typedef struct { size_t table_size; size_t deflate_table_size; int http1text; int dump_header_table; } deflate_config; static deflate_config config; static size_t input_sum; static size_t output_sum; static char to_hex_digit(uint8_t n) { if (n > 9) { return static_cast(n - 10 + 'a'); } return static_cast(n + '0'); } static void to_hex(char *dest, const uint8_t *src, size_t len) { size_t i; for (i = 0; i < len; ++i) { *dest++ = to_hex_digit(src[i] >> 4); *dest++ = to_hex_digit(src[i] & 0xf); } } static void output_to_json(nghttp2_hd_deflater *deflater, const uint8_t *buf, size_t buflen, size_t inputlen, const std::vector &nva, int seq) { auto hex = std::vector(buflen * 2); auto obj = json_object(); auto comp_ratio = inputlen == 0 ? 0.0 : static_cast(buflen) / static_cast(inputlen) * 100; json_object_set_new(obj, "seq", json_integer(seq)); json_object_set_new(obj, "input_length", json_integer(static_cast(inputlen))); json_object_set_new(obj, "output_length", json_integer(static_cast(buflen))); json_object_set_new(obj, "percentage_of_original_size", json_real(comp_ratio)); if (buflen == 0) { json_object_set_new(obj, "wire", json_string("")); } else { to_hex(hex.data(), buf, buflen); json_object_set_new(obj, "wire", json_pack("s#", hex.data(), hex.size())); } json_object_set_new(obj, "headers", dump_headers(nva.data(), nva.size())); if (seq == 0) { // We only change the header table size only once at the beginning json_object_set_new( obj, "header_table_size", json_integer(static_cast(config.table_size))); } if (config.dump_header_table) { json_object_set_new(obj, "header_table", dump_deflate_header_table(deflater)); } json_dumpf(obj, stdout, JSON_PRESERVE_ORDER | JSON_INDENT(2)); printf("\n"); json_decref(obj); } static void deflate_hd(nghttp2_hd_deflater *deflater, const std::vector &nva, size_t inputlen, int seq) { std::array buf; auto rv = nghttp2_hd_deflate_hd2(deflater, buf.data(), buf.size(), (nghttp2_nv *)nva.data(), nva.size()); if (rv < 0) { fprintf(stderr, "deflate failed with error code %zd at %d\n", rv, seq); exit(EXIT_FAILURE); } input_sum += inputlen; output_sum += as_unsigned(rv); output_to_json(deflater, buf.data(), as_unsigned(rv), inputlen, nva, seq); } static int deflate_hd_json(json_t *obj, nghttp2_hd_deflater *deflater, int seq) { size_t inputlen = 0; auto js = json_object_get(obj, "headers"); if (js == nullptr) { fprintf(stderr, "'headers' key is missing at %d\n", seq); return -1; } if (!json_is_array(js)) { fprintf(stderr, "The value of 'headers' key must be an array at %d\n", seq); return -1; } auto len = json_array_size(js); auto nva = std::vector(len); for (size_t i = 0; i < len; ++i) { auto nv_pair = json_array_get(js, i); const char *name; json_t *value; if (!json_is_object(nv_pair) || json_object_size(nv_pair) != 1) { fprintf(stderr, "bad formatted name/value pair object at %d\n", seq); return -1; } json_object_foreach(nv_pair, name, value) { nva[i].name = (uint8_t *)name; nva[i].namelen = strlen(name); if (!json_is_string(value)) { fprintf(stderr, "value is not string at %d\n", seq); return -1; } nva[i].value = (uint8_t *)json_string_value(value); nva[i].valuelen = strlen(json_string_value(value)); nva[i].flags = NGHTTP2_NV_FLAG_NONE; } inputlen += nva[i].namelen + nva[i].valuelen; } deflate_hd(deflater, nva, inputlen, seq); return 0; } static nghttp2_hd_deflater *init_deflater() { nghttp2_hd_deflater *deflater; nghttp2_hd_deflate_new(&deflater, config.deflate_table_size); if (config.table_size != NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) { nghttp2_hd_deflate_change_table_size(deflater, config.table_size); } return deflater; } static void deinit_deflater(nghttp2_hd_deflater *deflater) { nghttp2_hd_deflate_del(deflater); } static int perform(void) { json_error_t error; auto json = json_loadf(stdin, 0, &error); if (json == nullptr) { fprintf(stderr, "JSON loading failed\n"); exit(EXIT_FAILURE); } auto cases = json_object_get(json, "cases"); if (cases == nullptr) { fprintf(stderr, "Missing 'cases' key in root object\n"); exit(EXIT_FAILURE); } if (!json_is_array(cases)) { fprintf(stderr, "'cases' must be JSON array\n"); exit(EXIT_FAILURE); } auto deflater = init_deflater(); output_json_header(); auto len = json_array_size(cases); for (size_t i = 0; i < len; ++i) { auto obj = json_array_get(cases, i); if (!json_is_object(obj)) { fprintf(stderr, "Unexpected JSON type at %zu. It should be object.\n", i); continue; } if (deflate_hd_json(obj, deflater, static_cast(i)) != 0) { continue; } if (i + 1 < len) { printf(",\n"); } } output_json_footer(); deinit_deflater(deflater); json_decref(json); return 0; } static int perform_from_http1text(void) { char line[1 << 14]; int seq = 0; auto deflater = init_deflater(); output_json_header(); for (;;) { std::vector nva; int end = 0; size_t inputlen = 0; for (;;) { char *rv = fgets(line, sizeof(line), stdin); char *val, *val_end; if (rv == nullptr) { end = 1; break; } else if (line[0] == '\n') { break; } nva.emplace_back(); auto &nv = nva.back(); val = strchr(line + 1, ':'); if (val == nullptr) { fprintf(stderr, "Bad HTTP/1 header field format at %d.\n", seq); exit(EXIT_FAILURE); } *val = '\0'; ++val; for (; *val && (*val == ' ' || *val == '\t'); ++val) ; for (val_end = val; *val_end && (*val_end != '\r' && *val_end != '\n'); ++val_end) ; *val_end = '\0'; nv.namelen = strlen(line); nv.valuelen = strlen(val); nv.name = (uint8_t *)strdup(line); nv.value = (uint8_t *)strdup(val); nv.flags = NGHTTP2_NV_FLAG_NONE; inputlen += nv.namelen + nv.valuelen; } if (!end) { if (seq > 0) { printf(",\n"); } deflate_hd(deflater, nva, inputlen, seq); } for (auto &nv : nva) { free(nv.name); free(nv.value); } if (end) break; ++seq; } output_json_footer(); deinit_deflater(deflater); return 0; } static void print_help(void) { std::cout << R"(HPACK HTTP/2 header encoder Usage: deflatehd [OPTIONS] < INPUT Reads JSON data or HTTP/1-style header fields from stdin and outputs deflated header block in JSON array. For the JSON input, the root JSON object must contain "context" key, which indicates which compression context is used. If it is "request", request compression context is used. Otherwise, response compression context is used. The value of "cases" key contains the sequence of input header set. They share the same compression context and are processed in the order they appear. Each item in the sequence is a JSON object and it must have at least "headers" key. Its value is an array of a JSON object containing exactly one name/value pair. Example: { "context": "request", "cases": [ { "headers": [ { ":method": "GET" }, { ":path": "/" } ] }, { "headers": [ { ":method": "POST" }, { ":path": "/" } ] } ] } With -t option, the program can accept more familiar HTTP/1 style header field block. Each header set must be followed by one empty line: Example: :method: GET :scheme: https :path: / :method: POST user-agent: nghttp2 The output of this program can be used as input for inflatehd. OPTIONS: -t, --http1text Use HTTP/1 style header field text as input. Each header set is delimited by single empty line. -s, --table-size= Set dynamic table size. In the HPACK specification, this value is denoted by SETTINGS_HEADER_TABLE_SIZE. Default: 4096 -S, --deflate-table-size= Use first N bytes of dynamic header table buffer. Default: 4096 -d, --dump-header-table Output dynamic header table.)" << std::endl; } constexpr static struct option long_options[] = { {"http1text", no_argument, nullptr, 't'}, {"table-size", required_argument, nullptr, 's'}, {"deflate-table-size", required_argument, nullptr, 'S'}, {"dump-header-table", no_argument, nullptr, 'd'}, {nullptr, 0, nullptr, 0}}; int main(int argc, char **argv) { config.table_size = 4_k; config.deflate_table_size = 4_k; config.http1text = 0; config.dump_header_table = 0; while (1) { int option_index = 0; int c = getopt_long(argc, argv, "S:dhs:t", long_options, &option_index); if (c == -1) { break; } switch (c) { case 'h': print_help(); exit(EXIT_SUCCESS); case 't': // --http1text config.http1text = 1; break; case 's': { // --table-size auto n = util::parse_uint(optarg); if (!n) { fprintf(stderr, "-s: Bad option value\n"); exit(EXIT_FAILURE); } config.table_size = static_cast(*n); break; } case 'S': { // --deflate-table-size auto n = util::parse_uint(optarg); if (!n) { fprintf(stderr, "-S: Bad option value\n"); exit(EXIT_FAILURE); } config.deflate_table_size = static_cast(*n); break; } case 'd': // --dump-header-table config.dump_header_table = 1; break; case '?': exit(EXIT_FAILURE); default: break; } } if (config.http1text) { perform_from_http1text(); } else { perform(); } auto comp_ratio = input_sum == 0 ? 0.0 : static_cast(output_sum) / static_cast(input_sum); fprintf(stderr, "Overall: input=%zu output=%zu ratio=%.02f\n", input_sum, output_sum, comp_ratio); return 0; } } // namespace nghttp2 int main(int argc, char **argv) { return nghttp2::run_app(nghttp2::main, argc, argv); } nghttp2-1.68.0/src/PaxHeaders/shrpx_rate_limit.cc0000644000000000000000000000013215077107270016733 xustar0030 mtime=1761382072.998444116 30 atime=1761382106.134310735 30 ctime=1761382109.130300294 nghttp2-1.68.0/src/shrpx_rate_limit.cc0000644000175100017510000000603215077107270017324 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_rate_limit.h" #include #include "shrpx_connection.h" #include "shrpx_log.h" namespace shrpx { namespace { void regencb(struct ev_loop *loop, ev_timer *w, int revents) { auto r = static_cast(w->data); r->regen(); } } // namespace RateLimit::RateLimit(struct ev_loop *loop, ev_io *w, size_t rate, size_t burst, Connection *conn) : w_(w), loop_(loop), conn_(conn), rate_(rate), burst_(burst), avail_(burst), startw_req_(false) { ev_timer_init(&t_, regencb, 0., 1.); t_.data = this; if (rate_ > 0) { ev_timer_again(loop_, &t_); } } RateLimit::~RateLimit() { ev_timer_stop(loop_, &t_); } size_t RateLimit::avail() const { if (rate_ == 0) { return std::numeric_limits::max(); } return avail_; } void RateLimit::drain(size_t n) { if (rate_ == 0) { return; } n = std::min(avail_, n); avail_ -= n; if (avail_ == 0) { ev_io_stop(loop_, w_); } } void RateLimit::regen() { if (rate_ == 0) { return; } if (avail_ + rate_ > burst_) { avail_ = burst_; } else { avail_ += rate_; } if (w_->fd >= 0 && avail_ > 0 && startw_req_) { ev_io_start(loop_, w_); handle_tls_pending_read(); } } void RateLimit::startw() { if (w_->fd < 0) { return; } startw_req_ = true; if (rate_ == 0 || avail_ > 0) { ev_io_start(loop_, w_); handle_tls_pending_read(); return; } } void RateLimit::stopw() { startw_req_ = false; ev_io_stop(loop_, w_); } void RateLimit::handle_tls_pending_read() { if (!conn_ || !conn_->tls.ssl || (SSL_pending(conn_->tls.ssl) == 0 && (!conn_->tls.initial_handshake_done || conn_->tls.earlybuf.rleft() == 0))) { return; } // Note that ev_feed_event works without starting watcher, but we // only call this function if watcher is active. ev_feed_event(loop_, w_, EV_READ); } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx-unittest.cc0000644000000000000000000000013215077107270016377 xustar0030 mtime=1761382072.989444157 30 atime=1761382106.244310251 30 ctime=1761382109.241299973 nghttp2-1.68.0/src/shrpx-unittest.cc0000644000175100017510000000457115077107270016776 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifdef HAVE_CONFIG_H # include #endif // defined(HAVE_CONFIG_H) #include "munit.h" // include test cases' include files here #include "shrpx_tls_test.h" #include "shrpx_downstream_test.h" #include "shrpx_config_test.h" #include "shrpx_worker_test.h" #include "http2_test.h" #include "util_test.h" #include "nghttp2_gzip_test.h" #include "buffer_test.h" #include "memchunk_test.h" #include "template_test.h" #include "shrpx_http_test.h" #include "base64_test.h" #include "shrpx_config.h" #include "tls.h" #include "shrpx_router_test.h" #include "shrpx_log.h" #ifdef ENABLE_HTTP3 # include "siphash_test.h" #endif // defined(ENABLE_HTTP3) int main(int argc, char *argv[]) { shrpx::create_config(); const MunitSuite suites[] = { shrpx::tls_suite, shrpx::downstream_suite, shrpx::config_suite, shrpx::worker_suite, shrpx::http_suite, shrpx::router_suite, shrpx::http2_suite, shrpx::util_suite, gzip_suite, buffer_suite, memchunk_suite, template_suite, base64_suite, #ifdef ENABLE_HTTP3 siphash_suite, #endif // defined(ENABLE_HTTP3) {}, }; const MunitSuite suite = { "", nullptr, suites, 1, MUNIT_SUITE_OPTION_NONE, }; return munit_suite_main(&suite, nullptr, argc, argv); } nghttp2-1.68.0/src/PaxHeaders/shrpx_upstream.h0000644000000000000000000000013215077107270016304 xustar0030 mtime=1761382072.999444111 30 atime=1761382106.089310933 30 ctime=1761382109.085300424 nghttp2-1.68.0/src/shrpx_upstream.h0000644000175100017510000001151415077107270016676 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_UPSTREAM_H #define SHRPX_UPSTREAM_H #include "shrpx.h" #include "shrpx_io_control.h" #include "memchunk.h" using namespace nghttp2; namespace shrpx { class ClientHandler; class Downstream; class DownstreamConnection; class Upstream { public: virtual ~Upstream() {} virtual int on_read() = 0; virtual int on_write() = 0; virtual int on_timeout(Downstream *downstream) { return 0; } virtual int on_downstream_abort_request(Downstream *downstream, unsigned int status_code) = 0; // Called when the current request is aborted without forwarding it // to backend, and it should be redirected to https URI. virtual int on_downstream_abort_request_with_https_redirect(Downstream *downstream) = 0; virtual int downstream_read(DownstreamConnection *dconn) = 0; virtual int downstream_write(DownstreamConnection *dconn) = 0; virtual int downstream_eof(DownstreamConnection *dconn) = 0; virtual int downstream_error(DownstreamConnection *dconn, int events) = 0; virtual ClientHandler *get_client_handler() const = 0; virtual int on_downstream_header_complete(Downstream *downstream) = 0; virtual int on_downstream_body(Downstream *downstream, const uint8_t *data, size_t len, bool flush) = 0; virtual int on_downstream_body_complete(Downstream *downstream) = 0; virtual void on_handler_delete() = 0; // Called when downstream connection for |downstream| is reset. // Currently this is only used by Http2Session. If |no_retry| is // true, another connection attempt using new DownstreamConnection // is not allowed. virtual int on_downstream_reset(Downstream *downstream, bool no_retry) = 0; virtual void pause_read(IOCtrlReason reason) = 0; virtual int resume_read(IOCtrlReason reason, Downstream *downstream, size_t consumed) = 0; virtual int send_reply(Downstream *downstream, const uint8_t *body, size_t bodylen) = 0; // Starts server push. The |downstream| is an associated stream for // the pushed resource. This function returns 0 if it succeeds, // otherwise -1. virtual int initiate_push(Downstream *downstream, const std::string_view &uri) = 0; // Fills response data in |iov| whose capacity is |iovcnt|. Returns // the number of iovs filled. virtual int response_riovec(struct iovec *iov, int iovcnt) const = 0; virtual void response_drain(size_t n) = 0; virtual bool response_empty() const = 0; // Called when PUSH_PROMISE was started in downstream. The // associated downstream is given as |downstream|. The promised // stream ID is given as |promised_stream_id|. If upstream supports // server push for the corresponding upstream connection, it should // return Downstream object for pushed stream. Otherwise, returns // nullptr. virtual Downstream * on_downstream_push_promise(Downstream *downstream, int32_t promised_stream_id) = 0; // Called when PUSH_PROMISE frame was completely received in // downstream. The associated downstream is given as |downstream|. // This function returns 0 if it succeeds, or -1. virtual int on_downstream_push_promise_complete(Downstream *downstream, Downstream *promised_downstream) = 0; // Returns true if server push is enabled in upstream connection. virtual bool push_enabled() const = 0; // Cancels promised downstream. This function is called when // PUSH_PROMISE for |promised_downstream| is not submitted to // upstream session. virtual void cancel_premature_downstream(Downstream *promised_downstream) = 0; }; } // namespace shrpx #endif // !defined(SHRPX_UPSTREAM_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_http3_upstream.cc0000644000000000000000000000013215077107270017564 xustar0030 mtime=1761382072.994444134 30 atime=1761382106.200310444 30 ctime=1761382109.196300104 nghttp2-1.68.0/src/shrpx_http3_upstream.cc0000644000175100017510000024334415077107270020166 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2021 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_http3_upstream.h" #include #include #include #include #include #include #include "shrpx_client_handler.h" #include "shrpx_downstream.h" #include "shrpx_downstream_connection.h" #include "shrpx_log.h" #include "shrpx_quic.h" #include "shrpx_worker.h" #include "shrpx_http.h" #include "shrpx_connection_handler.h" #ifdef HAVE_MRUBY # include "shrpx_mruby.h" #endif // defined(HAVE_MRUBY) #include "http3.h" #include "util.h" namespace shrpx { namespace { void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { auto upstream = static_cast(w->data); if (upstream->handle_expiry() != 0) { goto fail; } upstream->get_client_handler()->signal_write(); return; fail: auto handler = upstream->get_client_handler(); delete handler; } } // namespace namespace { void shutdown_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto upstream = static_cast(w->data); auto handler = upstream->get_client_handler(); if (upstream->submit_goaway() != 0) { delete handler; } } } // namespace namespace { void prepare_cb(struct ev_loop *loop, ev_prepare *w, int revent) { auto upstream = static_cast(w->data); auto handler = upstream->get_client_handler(); if (upstream->check_shutdown() != 0) { delete handler; } } } // namespace namespace { size_t downstream_queue_size(Worker *worker) { auto &downstreamconf = *worker->get_downstream_config(); if (get_config()->http2_proxy) { return downstreamconf.connections_per_host; } return downstreamconf.connections_per_frontend; } } // namespace namespace { ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) { auto conn = static_cast(conn_ref->user_data); auto handler = static_cast(conn->data); auto upstream = static_cast(handler->get_upstream()); return upstream->get_conn(); } } // namespace namespace { constexpr size_t QUIC_TX_DATALEN = 64_k; } // namespace Http3Upstream::Http3Upstream(ClientHandler *handler) : handler_{handler}, qlog_fd_{-1}, hashed_scid_{}, conn_{nullptr}, #if OPENSSL_3_5_0_API ossl_ctx_{nullptr}, #endif // OPENSSL_3_5_0_API, httpconn_{nullptr}, downstream_queue_{downstream_queue_size(handler->get_worker()), !get_config()->http2_proxy}, tx_{ .data = std::unique_ptr(new uint8_t[QUIC_TX_DATALEN]), #ifndef UDP_SEGMENT .no_gso = true, #endif // !defined(UDP_SEGMENT) } { auto conn = handler_->get_connection(); conn->conn_ref.get_conn = shrpx::get_conn; ev_timer_init(&timer_, timeoutcb, 0., 0.); timer_.data = this; ngtcp2_ccerr_default(&last_error_); ev_timer_init(&shutdown_timer_, shutdown_timeout_cb, 0., 0.); shutdown_timer_.data = this; ev_prepare_init(&prep_, prepare_cb); prep_.data = this; ev_prepare_start(handler_->get_loop(), &prep_); } Http3Upstream::~Http3Upstream() { auto loop = handler_->get_loop(); ev_prepare_stop(loop, &prep_); ev_timer_stop(loop, &shutdown_timer_); ev_timer_stop(loop, &timer_); nghttp3_conn_del(httpconn_); #if OPENSSL_3_5_0_API ngtcp2_crypto_ossl_ctx_del(ossl_ctx_); #endif // OPENSSL_3_5_0_API ngtcp2_conn_del(conn_); if (qlog_fd_ != -1) { close(qlog_fd_); } } namespace { void log_printf(void *user_data, const char *fmt, ...) { va_list ap; std::array buf; va_start(ap, fmt); auto nwrite = vsnprintf(buf.data(), buf.size(), fmt, ap); va_end(ap); if (static_cast(nwrite) >= buf.size()) { nwrite = buf.size() - 1; } buf[as_unsigned(nwrite++)] = '\n'; while (write(fileno(stderr), buf.data(), static_cast(nwrite)) == -1 && errno == EINTR) ; } } // namespace namespace { void qlog_write(void *user_data, uint32_t flags, const void *data, size_t datalen) { auto upstream = static_cast(user_data); upstream->qlog_write(data, datalen, flags & NGTCP2_QLOG_WRITE_FLAG_FIN); } } // namespace void Http3Upstream::qlog_write(const void *data, size_t datalen, bool fin) { assert(qlog_fd_ != -1); while (write(qlog_fd_, data, datalen) == -1 && errno == EINTR) ; if (fin) { close(qlog_fd_); qlog_fd_ = -1; } } namespace { void rand_bytes(uint8_t *dest, size_t destlen) { auto rv = RAND_bytes(dest, static_cast(destlen)); if (rv != 1) { assert(0); abort(); } } } // namespace namespace { void rand(uint8_t *dest, size_t destlen, const ngtcp2_rand_ctx *rand_ctx) { rand_bytes(dest, destlen); } } // namespace namespace { int get_new_connection_id(ngtcp2_conn *conn, ngtcp2_cid *cid, uint8_t *token, size_t cidlen, void *user_data) { auto upstream = static_cast(user_data); auto handler = upstream->get_client_handler(); auto worker = handler->get_worker(); auto conn_handler = worker->get_connection_handler(); auto &qkms = conn_handler->get_quic_keying_materials(); auto &qkm = qkms->keying_materials.front(); assert(SHRPX_QUIC_SCIDLEN == cidlen); if (generate_quic_connection_id(*cid, worker->get_worker_id(), qkm.id, qkm.cid_encryption_ctx) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } if (generate_quic_stateless_reset_token(token, *cid, qkm.secret.data(), qkm.secret.size()) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } auto quic_connection_handler = worker->get_quic_connection_handler(); quic_connection_handler->add_connection_id(*cid, handler); return 0; } } // namespace namespace { int remove_connection_id(ngtcp2_conn *conn, const ngtcp2_cid *cid, void *user_data) { auto upstream = static_cast(user_data); auto handler = upstream->get_client_handler(); auto worker = handler->get_worker(); auto quic_conn_handler = worker->get_quic_connection_handler(); quic_conn_handler->remove_connection_id(*cid); return 0; } } // namespace void Http3Upstream::http_begin_request_headers(int64_t stream_id) { auto downstream = std::make_unique(this, handler_->get_mcpool(), stream_id); nghttp3_conn_set_stream_user_data(httpconn_, stream_id, downstream.get()); downstream->reset_upstream_rtimer(); downstream->repeat_header_timer(); handler_->stop_read_timer(); auto &req = downstream->request(); req.http_major = 3; req.http_minor = 0; add_pending_downstream(std::move(downstream)); } void Http3Upstream::add_pending_downstream( std::unique_ptr downstream) { downstream_queue_.add_pending(std::move(downstream)); } namespace { int recv_stream_data(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id, uint64_t offset, const uint8_t *data, size_t datalen, void *user_data, void *stream_user_data) { auto upstream = static_cast(user_data); if (upstream->recv_stream_data(flags, stream_id, {data, datalen}) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Http3Upstream::recv_stream_data(uint32_t flags, int64_t stream_id, std::span data) { assert(httpconn_); auto nconsumed = nghttp3_conn_read_stream2( httpconn_, stream_id, data.data(), data.size(), flags & NGTCP2_STREAM_DATA_FLAG_FIN, ngtcp2_conn_get_timestamp(conn_)); if (nconsumed < 0) { ULOG(ERROR, this) << "nghttp3_conn_read_stream2: " << nghttp3_strerror(static_cast(nconsumed)); ngtcp2_ccerr_set_application_error( &last_error_, nghttp3_err_infer_quic_app_error_code(static_cast(nconsumed)), nullptr, 0); return -1; } ngtcp2_conn_extend_max_stream_offset(conn_, stream_id, as_unsigned(nconsumed)); ngtcp2_conn_extend_max_offset(conn_, as_unsigned(nconsumed)); return 0; } namespace { int stream_close(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id, uint64_t app_error_code, void *user_data, void *stream_user_data) { auto upstream = static_cast(user_data); if (!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) { app_error_code = NGHTTP3_H3_NO_ERROR; } if (upstream->stream_close(stream_id, app_error_code) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Http3Upstream::stream_close(int64_t stream_id, uint64_t app_error_code) { if (!httpconn_) { return 0; } auto rv = nghttp3_conn_close_stream(httpconn_, stream_id, app_error_code); switch (rv) { case 0: break; case NGHTTP3_ERR_STREAM_NOT_FOUND: if (ngtcp2_is_bidi_stream(stream_id)) { ngtcp2_conn_extend_max_streams_bidi(conn_, 1); } break; default: ULOG(ERROR, this) << "nghttp3_conn_close_stream: " << nghttp3_strerror(rv); ngtcp2_ccerr_set_application_error( &last_error_, nghttp3_err_infer_quic_app_error_code(rv), nullptr, 0); return -1; } return 0; } namespace { int acked_stream_data_offset(ngtcp2_conn *conn, int64_t stream_id, uint64_t offset, uint64_t datalen, void *user_data, void *stream_user_data) { auto upstream = static_cast(user_data); if (upstream->acked_stream_data_offset(stream_id, datalen) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Http3Upstream::acked_stream_data_offset(int64_t stream_id, uint64_t datalen) { if (!httpconn_) { return 0; } auto rv = nghttp3_conn_add_ack_offset(httpconn_, stream_id, datalen); if (rv != 0) { ULOG(ERROR, this) << "nghttp3_conn_add_ack_offset: " << nghttp3_strerror(rv); return -1; } return 0; } namespace { int extend_max_stream_data(ngtcp2_conn *conn, int64_t stream_id, uint64_t max_data, void *user_data, void *stream_user_data) { auto upstream = static_cast(user_data); if (upstream->extend_max_stream_data(stream_id) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Http3Upstream::extend_max_stream_data(int64_t stream_id) { if (!httpconn_) { return 0; } auto rv = nghttp3_conn_unblock_stream(httpconn_, stream_id); if (rv != 0) { ULOG(ERROR, this) << "nghttp3_conn_unblock_stream: " << nghttp3_strerror(rv); return -1; } return 0; } namespace { int extend_max_remote_streams_bidi(ngtcp2_conn *conn, uint64_t max_streams, void *user_data) { auto upstream = static_cast(user_data); upstream->extend_max_remote_streams_bidi(max_streams); return 0; } } // namespace void Http3Upstream::extend_max_remote_streams_bidi(uint64_t max_streams) { nghttp3_conn_set_max_client_streams_bidi(httpconn_, max_streams); } namespace { int stream_reset(ngtcp2_conn *conn, int64_t stream_id, uint64_t final_size, uint64_t app_error_code, void *user_data, void *stream_user_data) { auto upstream = static_cast(user_data); if (upstream->http_shutdown_stream_read(stream_id) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Http3Upstream::http_shutdown_stream_read(int64_t stream_id) { if (!httpconn_) { return 0; } auto rv = nghttp3_conn_shutdown_stream_read(httpconn_, stream_id); if (rv != 0) { ULOG(ERROR, this) << "nghttp3_conn_shutdown_stream_read: " << nghttp3_strerror(rv); return -1; } return 0; } namespace { int stream_stop_sending(ngtcp2_conn *conn, int64_t stream_id, uint64_t app_error_code, void *user_data, void *stream_user_data) { auto upstream = static_cast(user_data); if (upstream->http_shutdown_stream_read(stream_id) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } } // namespace namespace { int handshake_completed(ngtcp2_conn *conn, void *user_data) { auto upstream = static_cast(user_data); if (upstream->handshake_completed() != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Http3Upstream::handshake_completed() { handler_->set_alpn_from_conn(); auto alpn = handler_->get_alpn(); if (alpn.empty()) { ULOG(ERROR, this) << "NO ALPN was negotiated"; return -1; } auto path = ngtcp2_conn_get_path(conn_); return send_new_token(&path->remote); } namespace { int path_validation(ngtcp2_conn *conn, uint32_t flags, const ngtcp2_path *path, const ngtcp2_path *old_path, ngtcp2_path_validation_result res, void *user_data) { if (res != NGTCP2_PATH_VALIDATION_RESULT_SUCCESS || !(flags & NGTCP2_PATH_VALIDATION_FLAG_NEW_TOKEN)) { return 0; } auto upstream = static_cast(user_data); if (upstream->send_new_token(&path->remote) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Http3Upstream::send_new_token(const ngtcp2_addr *remote_addr) { auto worker = handler_->get_worker(); auto conn_handler = worker->get_connection_handler(); auto &qkms = conn_handler->get_quic_keying_materials(); auto &qkm = qkms->keying_materials.front(); std::array tokenbuf; auto token = generate_token(tokenbuf, remote_addr->addr, remote_addr->addrlen, qkm.secret, qkm.id); if (!token) { return -1; } assert(token->size() == NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN + 1); auto rv = ngtcp2_conn_submit_new_token(conn_, token->data(), token->size()); if (rv != 0) { ULOG(ERROR, this) << "ngtcp2_conn_submit_new_token: " << ngtcp2_strerror(rv); return -1; } return 0; } namespace { int recv_tx_key(ngtcp2_conn *conn, ngtcp2_encryption_level level, void *user_data) { if (level != NGTCP2_ENCRYPTION_LEVEL_1RTT) { return 0; } auto upstream = static_cast(user_data); if (upstream->setup_httpconn() != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Http3Upstream::init(const UpstreamAddr *faddr, const Address &remote_addr, const Address &local_addr, const ngtcp2_pkt_hd &initial_hd, const ngtcp2_cid *odcid, std::span token, ngtcp2_token_type token_type) { int rv; auto worker = handler_->get_worker(); auto conn_handler = worker->get_connection_handler(); auto callbacks = ngtcp2_callbacks{ .recv_client_initial = ngtcp2_crypto_recv_client_initial_cb, .recv_crypto_data = ngtcp2_crypto_recv_crypto_data_cb, .handshake_completed = shrpx::handshake_completed, .encrypt = ngtcp2_crypto_encrypt_cb, .decrypt = ngtcp2_crypto_decrypt_cb, .hp_mask = ngtcp2_crypto_hp_mask_cb, .recv_stream_data = shrpx::recv_stream_data, .acked_stream_data_offset = shrpx::acked_stream_data_offset, .stream_close = shrpx::stream_close, .rand = rand, .get_new_connection_id = get_new_connection_id, .remove_connection_id = remove_connection_id, .update_key = ngtcp2_crypto_update_key_cb, .path_validation = shrpx::path_validation, .stream_reset = shrpx::stream_reset, .extend_max_remote_streams_bidi = shrpx::extend_max_remote_streams_bidi, .extend_max_stream_data = shrpx::extend_max_stream_data, .delete_crypto_aead_ctx = ngtcp2_crypto_delete_crypto_aead_ctx_cb, .delete_crypto_cipher_ctx = ngtcp2_crypto_delete_crypto_cipher_ctx_cb, .get_path_challenge_data = ngtcp2_crypto_get_path_challenge_data_cb, .stream_stop_sending = shrpx::stream_stop_sending, .recv_tx_key = shrpx::recv_tx_key, }; auto config = get_config(); auto &quicconf = config->quic; auto &http3conf = config->http3; auto &qkms = conn_handler->get_quic_keying_materials(); auto &qkm = qkms->keying_materials.front(); ngtcp2_cid scid; if (generate_quic_connection_id(scid, worker->get_worker_id(), qkm.id, qkm.cid_encryption_ctx) != 0) { return -1; } ngtcp2_settings settings; ngtcp2_settings_default(&settings); if (quicconf.upstream.debug.log) { settings.log_printf = log_printf; } if (!quicconf.upstream.qlog.dir.empty()) { auto fd = open_qlog_file(quicconf.upstream.qlog.dir, scid); if (fd != -1) { qlog_fd_ = fd; settings.qlog_write = shrpx::qlog_write; } } settings.initial_ts = quic_timestamp(); settings.initial_rtt = static_cast(quicconf.upstream.initial_rtt * NGTCP2_SECONDS); settings.cc_algo = quicconf.upstream.congestion_controller; settings.max_window = static_cast(http3conf.upstream.max_connection_window_size); settings.max_stream_window = static_cast(http3conf.upstream.max_window_size); settings.rand_ctx.native_handle = &worker->get_randgen(); settings.token = token.data(); settings.tokenlen = token.size(); settings.token_type = token_type; settings.initial_pkt_num = std::uniform_int_distribution( 0, std::numeric_limits::max())(worker->get_randgen()); ngtcp2_transport_params params; ngtcp2_transport_params_default(¶ms); params.initial_max_streams_bidi = http3conf.upstream.max_concurrent_streams; // The minimum number of unidirectional streams required for HTTP/3. params.initial_max_streams_uni = 3; params.initial_max_data = static_cast(http3conf.upstream.connection_window_size); params.initial_max_stream_data_bidi_remote = static_cast(http3conf.upstream.window_size); params.initial_max_stream_data_uni = static_cast(http3conf.upstream.window_size); params.max_idle_timeout = static_cast(quicconf.upstream.timeout.idle * NGTCP2_SECONDS); #ifdef NGHTTP2_OPENSSL_IS_BORINGSSL if (quicconf.upstream.early_data) { ngtcp2_transport_params early_data_params; ngtcp2_transport_params_default(&early_data_params); early_data_params.initial_max_stream_data_bidi_local = params.initial_max_stream_data_bidi_local; early_data_params.initial_max_stream_data_bidi_remote = params.initial_max_stream_data_bidi_remote; early_data_params.initial_max_stream_data_uni = params.initial_max_stream_data_uni; early_data_params.initial_max_data = params.initial_max_data; early_data_params.initial_max_streams_bidi = params.initial_max_streams_bidi; early_data_params.initial_max_streams_uni = params.initial_max_streams_uni; // TODO include HTTP/3 SETTINGS std::array quic_early_data_ctx; auto quic_early_data_ctxlen = ngtcp2_transport_params_encode( quic_early_data_ctx.data(), quic_early_data_ctx.size(), &early_data_params); assert(quic_early_data_ctxlen > 0); assert(static_cast(quic_early_data_ctxlen) <= quic_early_data_ctx.size()); if (SSL_set_quic_early_data_context( handler_->get_ssl(), quic_early_data_ctx.data(), as_unsigned(quic_early_data_ctxlen)) != 1) { ULOG(ERROR, this) << "SSL_set_quic_early_data_context failed"; return -1; } } #endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) if (odcid) { params.original_dcid = *odcid; params.retry_scid = initial_hd.dcid; params.retry_scid_present = 1; } else { params.original_dcid = initial_hd.dcid; } params.original_dcid_present = 1; rv = generate_quic_stateless_reset_token( params.stateless_reset_token, scid, qkm.secret.data(), qkm.secret.size()); if (rv != 0) { ULOG(ERROR, this) << "generate_quic_stateless_reset_token failed"; return -1; } params.stateless_reset_token_present = 1; auto path = ngtcp2_path{ { const_cast(&local_addr.su.sa), local_addr.len, }, { const_cast(&remote_addr.su.sa), remote_addr.len, }, const_cast(faddr), }; rv = ngtcp2_conn_server_new(&conn_, &initial_hd.scid, &scid, &path, initial_hd.version, &callbacks, &settings, ¶ms, nullptr, this); if (rv != 0) { ULOG(ERROR, this) << "ngtcp2_conn_server_new: " << ngtcp2_strerror(rv); return -1; } #if OPENSSL_3_5_0_API auto ssl = handler_->get_ssl(); rv = ngtcp2_crypto_ossl_configure_server_session(ssl); if (rv != 0) { ULOG(ERROR, this) << "ngtcp2_crypto_ossl_configure_server_session failed"; return -1; } rv = ngtcp2_crypto_ossl_ctx_new(&ossl_ctx_, ssl); if (rv != 0) { ULOG(ERROR, this) << "ngtcp2_crypto_ossl_ctx_new failed with error code " << rv; return -1; } ngtcp2_conn_set_tls_native_handle(conn_, ossl_ctx_); #else // !OPENSSL_3_5_0_API ngtcp2_conn_set_tls_native_handle(conn_, handler_->get_ssl()); #endif // !OPENSSL_3_5_0_API auto quic_connection_handler = worker->get_quic_connection_handler(); if (generate_quic_hashed_connection_id(hashed_scid_, remote_addr, local_addr, initial_hd.dcid) != 0) { return -1; } quic_connection_handler->add_connection_id(hashed_scid_, handler_); quic_connection_handler->add_connection_id(scid, handler_); return 0; } int Http3Upstream::on_read() { return 0; } int Http3Upstream::on_write() { int rv; if (tx_.send_blocked) { rv = send_blocked_packet(); if (rv != 0) { return -1; } if (tx_.send_blocked) { return 0; } } handler_->get_connection()->wlimit.stopw(); if (write_streams() != 0) { return -1; } if (httpconn_ && nghttp3_conn_is_drained(httpconn_)) { return -1; } reset_timer(); return 0; } namespace { ngtcp2_ssize write_pkt(ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_tstamp ts, void *user_data) { auto upstream = static_cast(user_data); return upstream->write_pkt(path, pi, dest, destlen, ts); } } // namespace ngtcp2_ssize Http3Upstream::write_pkt(ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_tstamp ts) { std::array vec; int rv; for (;;) { int64_t stream_id = -1; int fin = 0; nghttp3_ssize sveccnt = 0; if (httpconn_ && ngtcp2_conn_get_max_data_left(conn_)) { sveccnt = nghttp3_conn_writev_stream(httpconn_, &stream_id, &fin, vec.data(), vec.size()); if (sveccnt < 0) { ULOG(ERROR, this) << "nghttp3_conn_writev_stream: " << nghttp3_strerror(static_cast(sveccnt)); ngtcp2_ccerr_set_application_error( &last_error_, nghttp3_err_infer_quic_app_error_code(static_cast(sveccnt)), nullptr, 0); return NGTCP2_ERR_CALLBACK_FAILURE; } } ngtcp2_ssize ndatalen; auto v = vec.data(); auto vcnt = static_cast(sveccnt); uint32_t flags = NGTCP2_WRITE_STREAM_FLAG_MORE | NGTCP2_WRITE_STREAM_FLAG_PADDING; if (fin) { flags |= NGTCP2_WRITE_STREAM_FLAG_FIN; } auto nwrite = ngtcp2_conn_writev_stream( conn_, path, pi, dest, destlen, &ndatalen, flags, stream_id, reinterpret_cast(v), vcnt, ts); if (nwrite < 0) { switch (nwrite) { case NGTCP2_ERR_STREAM_DATA_BLOCKED: assert(ndatalen == -1); nghttp3_conn_block_stream(httpconn_, stream_id); continue; case NGTCP2_ERR_STREAM_SHUT_WR: assert(ndatalen == -1); nghttp3_conn_shutdown_stream_write(httpconn_, stream_id); continue; case NGTCP2_ERR_WRITE_MORE: assert(ndatalen >= 0); rv = nghttp3_conn_add_write_offset(httpconn_, stream_id, as_unsigned(ndatalen)); if (rv != 0) { ULOG(ERROR, this) << "nghttp3_conn_add_write_offset: " << nghttp3_strerror(rv); ngtcp2_ccerr_set_application_error( &last_error_, nghttp3_err_infer_quic_app_error_code(rv), nullptr, 0); return NGTCP2_ERR_CALLBACK_FAILURE; } continue; } assert(ndatalen == -1); ULOG(ERROR, this) << "ngtcp2_conn_writev_stream: " << ngtcp2_strerror(static_cast(nwrite)); ngtcp2_ccerr_set_liberr(&last_error_, static_cast(nwrite), nullptr, 0); return NGTCP2_ERR_CALLBACK_FAILURE; } if (ndatalen >= 0) { rv = nghttp3_conn_add_write_offset(httpconn_, stream_id, as_unsigned(ndatalen)); if (rv != 0) { ULOG(ERROR, this) << "nghttp3_conn_add_write_offset: " << nghttp3_strerror(rv); ngtcp2_ccerr_set_application_error( &last_error_, nghttp3_err_infer_quic_app_error_code(rv), nullptr, 0); return NGTCP2_ERR_CALLBACK_FAILURE; } } return nwrite; } } int Http3Upstream::write_streams() { ngtcp2_path_storage ps; ngtcp2_pkt_info pi; auto txbuf = std::span{tx_.data.get(), QUIC_TX_DATALEN}; size_t gso_size = 0; ngtcp2_path_storage_zero(&ps); auto nwrite = ngtcp2_conn_write_aggregate_pkt( conn_, &ps.path, &pi, txbuf.data(), txbuf.size(), &gso_size, shrpx::write_pkt, quic_timestamp()); if (nwrite < 0) { return handle_error(); } if (nwrite == 0) { return 0; } send_packet(ps.path, pi, txbuf.first(static_cast(nwrite)), gso_size); return 0; } void Http3Upstream::send_packet(const ngtcp2_path &path, const ngtcp2_pkt_info &pi, const std::span data, size_t gso_size) { auto faddr = static_cast(path.user_data); auto [rest, rv] = send_packet(faddr, path.remote.addr, path.remote.addrlen, path.local.addr, path.local.addrlen, pi, data, gso_size); if (rv == SHRPX_ERR_SEND_BLOCKED) { on_send_blocked(path, pi, rest, rest.size()); signal_write_upstream_addr(faddr); } } int Http3Upstream::on_timeout(Downstream *downstream) { if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "Stream timeout stream_id=" << downstream->get_stream_id(); } shutdown_stream(downstream, NGHTTP3_H3_INTERNAL_ERROR); handler_->signal_write(); return 0; } int Http3Upstream::on_downstream_abort_request(Downstream *downstream, unsigned int status_code) { int rv; rv = error_reply(downstream, status_code); if (rv != 0) { return -1; } handler_->signal_write(); return 0; } int Http3Upstream::on_downstream_abort_request_with_https_redirect( Downstream *downstream) { assert(0); abort(); } namespace { uint64_t infer_upstream_shutdown_stream_error_code(uint32_t downstream_error_code) { // NGHTTP2_REFUSED_STREAM is important because it tells upstream // client to retry. switch (downstream_error_code) { case NGHTTP2_NO_ERROR: return NGHTTP3_H3_NO_ERROR; case NGHTTP2_REFUSED_STREAM: return NGHTTP3_H3_REQUEST_REJECTED; default: return NGHTTP3_H3_INTERNAL_ERROR; } } } // namespace int Http3Upstream::downstream_read(DownstreamConnection *dconn) { auto downstream = dconn->get_downstream(); if (downstream->get_response_state() == DownstreamState::MSG_RESET) { // The downstream stream was reset (canceled). In this case, // RST_STREAM to the upstream and delete downstream connection // here. Deleting downstream will be taken place at // on_stream_close_callback. shutdown_stream(downstream, infer_upstream_shutdown_stream_error_code( downstream->get_response_rst_stream_error_code())); downstream->pop_downstream_connection(); // dconn was deleted dconn = nullptr; } else if (downstream->get_response_state() == DownstreamState::MSG_BAD_HEADER) { if (error_reply(downstream, 502) != 0) { return -1; } downstream->pop_downstream_connection(); // dconn was deleted dconn = nullptr; } else { auto rv = downstream->on_read(); if (rv == SHRPX_ERR_EOF) { if (downstream->get_request_header_sent()) { return downstream_eof(dconn); } return SHRPX_ERR_RETRY; } if (rv == SHRPX_ERR_DCONN_CANCELED) { downstream->pop_downstream_connection(); handler_->signal_write(); return 0; } if (rv != 0) { if (rv != SHRPX_ERR_NETWORK) { if (LOG_ENABLED(INFO)) { DCLOG(INFO, dconn) << "HTTP parser failure"; } } return downstream_error(dconn, Downstream::EVENT_ERROR); } if (downstream->can_detach_downstream_connection()) { // Keep-alive downstream->detach_downstream_connection(); } } handler_->signal_write(); // At this point, downstream may be deleted. return 0; } int Http3Upstream::downstream_write(DownstreamConnection *dconn) { int rv; rv = dconn->on_write(); if (rv == SHRPX_ERR_NETWORK) { return downstream_error(dconn, Downstream::EVENT_ERROR); } if (rv != 0) { return rv; } return 0; } int Http3Upstream::downstream_eof(DownstreamConnection *dconn) { auto downstream = dconn->get_downstream(); if (LOG_ENABLED(INFO)) { DCLOG(INFO, dconn) << "EOF. stream_id=" << downstream->get_stream_id(); } // Delete downstream connection. If we don't delete it here, it will // be pooled in on_stream_close_callback. downstream->pop_downstream_connection(); // dconn was deleted dconn = nullptr; // downstream will be deleted in on_stream_close_callback. if (downstream->get_response_state() == DownstreamState::HEADER_COMPLETE) { // Server may indicate the end of the request by EOF if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "Downstream body was ended by EOF"; } downstream->set_response_state(DownstreamState::MSG_COMPLETE); // For tunneled connection, MSG_COMPLETE signals // downstream_read_data_callback to send RST_STREAM after pending // response body is sent. This is needed to ensure that RST_STREAM // is sent after all pending data are sent. if (on_downstream_body_complete(downstream) != 0) { return -1; } } else if (downstream->get_response_state() != DownstreamState::MSG_COMPLETE) { // If stream was not closed, then we set MSG_COMPLETE and let // on_stream_close_callback delete downstream. if (error_reply(downstream, 502) != 0) { return -1; } } handler_->signal_write(); // At this point, downstream may be deleted. return 0; } int Http3Upstream::downstream_error(DownstreamConnection *dconn, int events) { auto downstream = dconn->get_downstream(); if (LOG_ENABLED(INFO)) { if (events & Downstream::EVENT_ERROR) { DCLOG(INFO, dconn) << "Downstream network/general error"; } else { DCLOG(INFO, dconn) << "Timeout"; } if (downstream->get_upgraded()) { DCLOG(INFO, dconn) << "Note: this is tunnel connection"; } } // Delete downstream connection. If we don't delete it here, it will // be pooled in on_stream_close_callback. downstream->pop_downstream_connection(); // dconn was deleted dconn = nullptr; if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { // For SSL tunneling, we issue RST_STREAM. For other types of // stream, we don't have to do anything since response was // complete. if (downstream->get_upgraded()) { shutdown_stream(downstream, NGHTTP3_H3_NO_ERROR); } } else { if (downstream->get_response_state() == DownstreamState::HEADER_COMPLETE) { if (downstream->get_upgraded()) { if (on_downstream_body_complete(downstream) != 0) { return -1; } } else { shutdown_stream(downstream, NGHTTP3_H3_INTERNAL_ERROR); } } else { unsigned int status; if (events & Downstream::EVENT_TIMEOUT) { if (downstream->get_request_header_sent()) { status = 504; } else { status = 408; } } else { status = 502; } if (error_reply(downstream, status) != 0) { return -1; } } downstream->set_response_state(DownstreamState::MSG_COMPLETE); } handler_->signal_write(); // At this point, downstream may be deleted. return 0; } ClientHandler *Http3Upstream::get_client_handler() const { return handler_; } namespace { nghttp3_ssize downstream_read_data_callback(nghttp3_conn *conn, int64_t stream_id, nghttp3_vec *vec, size_t veccnt, uint32_t *pflags, void *conn_user_data, void *stream_user_data) { auto upstream = static_cast(conn_user_data); auto downstream = static_cast(stream_user_data); assert(downstream); auto body = downstream->get_response_buf(); assert(body); if (downstream->get_response_state() != DownstreamState::MSG_COMPLETE && body->rleft_mark() == 0) { downstream->disable_upstream_wtimer(); return NGHTTP3_ERR_WOULDBLOCK; } downstream->reset_upstream_wtimer(); veccnt = static_cast(body->riovec_mark( reinterpret_cast(vec), static_cast(veccnt))); if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE && body->rleft_mark() == 0) { *pflags |= NGHTTP3_DATA_FLAG_EOF; } assert((*pflags & NGHTTP3_DATA_FLAG_EOF) || veccnt); downstream->response_sent_body_length += nghttp3_vec_len(vec, veccnt); if ((*pflags & NGHTTP3_DATA_FLAG_EOF) && upstream->shutdown_stream_read(stream_id, NGHTTP3_H3_NO_ERROR) != 0) { return NGHTTP3_ERR_CALLBACK_FAILURE; } return as_signed(veccnt); } } // namespace int Http3Upstream::on_downstream_header_complete(Downstream *downstream) { int rv; const auto &req = downstream->request(); auto &resp = downstream->response(); auto &balloc = downstream->get_block_allocator(); if (LOG_ENABLED(INFO)) { if (downstream->get_non_final_response()) { DLOG(INFO, downstream) << "HTTP non-final response header"; } else { DLOG(INFO, downstream) << "HTTP response header completed"; } } auto config = get_config(); auto &httpconf = config->http; if (!config->http2_proxy && !httpconf.no_location_rewrite) { downstream->rewrite_location_response_header(req.scheme); } #ifdef HAVE_MRUBY if (!downstream->get_non_final_response()) { auto dconn = downstream->get_downstream_connection(); const auto &group = dconn->get_downstream_addr_group(); if (group) { const auto &dmruby_ctx = group->shared_addr->mruby_ctx; if (dmruby_ctx->run_on_response_proc(downstream) != 0) { if (error_reply(downstream, 500) != 0) { return -1; } // Returning -1 will signal deletion of dconn. return -1; } if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { return -1; } } auto worker = handler_->get_worker(); auto mruby_ctx = worker->get_mruby_context(); if (mruby_ctx->run_on_response_proc(downstream) != 0) { if (error_reply(downstream, 500) != 0) { return -1; } // Returning -1 will signal deletion of dconn. return -1; } if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { return -1; } } #endif // defined(HAVE_MRUBY) auto nva = std::vector(); // 4 means :status and possible server, via, and set-cookie (for // affinity cookie) header field. nva.reserve(resp.fs.headers().size() + 4 + httpconf.add_response_headers.size()); if (downstream->get_non_final_response()) { auto response_status = http2::stringify_status(balloc, resp.http_status); nva.push_back(http3::make_field(":status"sv, response_status)); http3::copy_headers_to_nva_nocopy(nva, resp.fs.headers(), http2::HDOP_STRIP_ALL); if (LOG_ENABLED(INFO)) { log_response_headers(downstream, nva); } rv = nghttp3_conn_submit_info(httpconn_, downstream->get_stream_id(), nva.data(), nva.size()); resp.fs.clear_headers(); if (rv != 0) { ULOG(FATAL, this) << "nghttp3_conn_submit_info() failed"; return -1; } return 0; } auto striphd_flags = static_cast(http2::HDOP_STRIP_ALL & ~http2::HDOP_STRIP_VIA); std::string_view response_status; if (req.connect_proto == ConnectProto::WEBSOCKET && resp.http_status == 101) { response_status = http2::stringify_status(balloc, 200); striphd_flags |= http2::HDOP_STRIP_SEC_WEBSOCKET_ACCEPT; } else { response_status = http2::stringify_status(balloc, resp.http_status); } nva.push_back(http3::make_field(":status"sv, response_status)); http3::copy_headers_to_nva_nocopy(nva, resp.fs.headers(), striphd_flags); if (!config->http2_proxy && !httpconf.no_server_rewrite) { nva.push_back(http3::make_field("server"sv, httpconf.server_name)); } else { auto server = resp.fs.header(http2::HD_SERVER); if (server) { nva.push_back(http3::make_field("server"sv, (*server).value)); } } if (!req.regular_connect_method() || !downstream->get_upgraded()) { auto affinity_cookie = downstream->get_affinity_cookie_to_send(); if (affinity_cookie) { auto dconn = downstream->get_downstream_connection(); assert(dconn); auto &group = dconn->get_downstream_addr_group(); auto &shared_addr = group->shared_addr; auto &cookieconf = shared_addr->affinity.cookie; auto secure = http::require_cookie_secure_attribute(cookieconf.secure, req.scheme); auto cookie_str = http::create_affinity_cookie( balloc, cookieconf.name, affinity_cookie, cookieconf.path, secure); nva.push_back(http3::make_field("set-cookie"sv, cookie_str)); } } auto via = resp.fs.header(http2::HD_VIA); if (httpconf.no_via) { if (via) { nva.push_back(http3::make_field("via"sv, (*via).value)); } } else { // we don't create more than 16 bytes in // http::create_via_header_value. size_t len = 16; if (via) { len += via->value.size() + 2; } auto iov = make_byte_ref(balloc, len + 1); auto p = std::ranges::begin(iov); if (via) { p = std::ranges::copy(via->value, p).out; p = std::ranges::copy(", "sv, p).out; } p = http::create_via_header_value(p, resp.http_major, resp.http_minor); *p = '\0'; nva.push_back( http3::make_field("via"sv, as_string_view(std::ranges::begin(iov), p))); } for (auto &p : httpconf.add_response_headers) { nva.push_back(http3::make_field(p.name, p.value)); } if (LOG_ENABLED(INFO)) { log_response_headers(downstream, nva); } auto priority = resp.fs.header(http2::HD_PRIORITY); if (priority) { nghttp3_pri pri; if (nghttp3_conn_get_stream_priority(httpconn_, &pri, downstream->get_stream_id()) == 0 && nghttp3_pri_parse_priority( &pri, reinterpret_cast(priority->value.data()), priority->value.size()) == 0) { rv = nghttp3_conn_set_server_stream_priority( httpconn_, downstream->get_stream_id(), &pri); if (rv != 0) { ULOG(ERROR, this) << "nghttp3_conn_set_server_stream_priority: " << nghttp3_strerror(rv); } } } nghttp3_data_reader data_read{ .read_data = downstream_read_data_callback, }; nghttp3_data_reader *data_readptr; if (downstream->expect_response_body() || downstream->expect_response_trailer()) { data_readptr = &data_read; } else { data_readptr = nullptr; } rv = nghttp3_conn_submit_response(httpconn_, downstream->get_stream_id(), nva.data(), nva.size(), data_readptr); if (rv != 0) { ULOG(FATAL, this) << "nghttp3_conn_submit_response() failed"; return -1; } if (data_readptr) { downstream->reset_upstream_wtimer(); } else if (shutdown_stream_read(downstream->get_stream_id(), NGHTTP3_H3_NO_ERROR) != 0) { return -1; } return 0; } int Http3Upstream::on_downstream_body(Downstream *downstream, const uint8_t *data, size_t len, bool flush) { auto body = downstream->get_response_buf(); body->append(data, len); if (flush) { nghttp3_conn_resume_stream(httpconn_, downstream->get_stream_id()); downstream->ensure_upstream_wtimer(); } return 0; } int Http3Upstream::on_downstream_body_complete(Downstream *downstream) { if (LOG_ENABLED(INFO)) { DLOG(INFO, downstream) << "HTTP response completed"; } auto &resp = downstream->response(); if (!downstream->validate_response_recv_body_length()) { shutdown_stream(downstream, NGHTTP3_H3_GENERAL_PROTOCOL_ERROR); resp.connection_close = true; return 0; } if (!downstream->get_upgraded()) { const auto &trailers = resp.fs.trailers(); if (!trailers.empty()) { std::vector nva; nva.reserve(trailers.size()); http3::copy_headers_to_nva_nocopy(nva, trailers, http2::HDOP_STRIP_ALL); if (!nva.empty()) { auto rv = nghttp3_conn_submit_trailers( httpconn_, downstream->get_stream_id(), nva.data(), nva.size()); if (rv != 0) { ULOG(FATAL, this) << "nghttp3_conn_submit_trailers() failed: " << nghttp3_strerror(rv); return -1; } } } } nghttp3_conn_resume_stream(httpconn_, downstream->get_stream_id()); downstream->ensure_upstream_wtimer(); return 0; } void Http3Upstream::on_handler_delete() { for (auto d = downstream_queue_.get_downstreams(); d; d = d->dlnext) { if (d->get_dispatch_state() == DispatchState::ACTIVE && d->accesslog_ready()) { handler_->write_accesslog(d); } } auto worker = handler_->get_worker(); auto quic_conn_handler = worker->get_quic_connection_handler(); std::vector scids(ngtcp2_conn_get_scid(conn_, nullptr) + 1); ngtcp2_conn_get_scid(conn_, scids.data()); scids.back() = hashed_scid_; for (auto &cid : scids) { quic_conn_handler->remove_connection_id(cid); } switch (last_error_.type) { case NGTCP2_CCERR_TYPE_IDLE_CLOSE: case NGTCP2_CCERR_TYPE_DROP_CONN: case NGTCP2_CCERR_TYPE_RETRY: return; default: break; } // If this is not idle close, send CONNECTION_CLOSE. if (!ngtcp2_conn_in_closing_period(conn_) && !ngtcp2_conn_in_draining_period(conn_)) { ngtcp2_path_storage ps; ngtcp2_pkt_info pi; conn_close_.resize(SHRPX_QUIC_CONN_CLOSE_PKTLEN); ngtcp2_path_storage_zero(&ps); ngtcp2_ccerr ccerr; ngtcp2_ccerr_default(&ccerr); if (worker->get_graceful_shutdown() && !ngtcp2_conn_get_handshake_completed(conn_)) { ccerr.error_code = NGTCP2_CONNECTION_REFUSED; } auto nwrite = ngtcp2_conn_write_connection_close( conn_, &ps.path, &pi, conn_close_.data(), conn_close_.size(), &ccerr, quic_timestamp()); if (nwrite < 0) { if (nwrite != NGTCP2_ERR_INVALID_STATE) { ULOG(ERROR, this) << "ngtcp2_conn_write_connection_close: " << ngtcp2_strerror(static_cast(nwrite)); } return; } conn_close_.resize(as_unsigned(nwrite)); send_packet(static_cast(ps.path.user_data), ps.path.remote.addr, ps.path.remote.addrlen, ps.path.local.addr, ps.path.local.addrlen, pi, conn_close_, conn_close_.size()); } auto d = static_cast(ngtcp2_conn_get_pto(conn_) * 3) / NGTCP2_SECONDS; if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "Enter close-wait period " << d << "s with " << conn_close_.size() << " bytes sentinel packet"; } auto cw = std::make_unique(worker, std::move(scids), std::move(conn_close_), d); quic_conn_handler->add_close_wait(cw.release()); } int Http3Upstream::on_downstream_reset(Downstream *downstream, bool no_retry) { int rv; if (downstream->get_dispatch_state() != DispatchState::ACTIVE) { // This is error condition when we failed push_request_headers() // in initiate_downstream(). Otherwise, we have // DispatchState::ACTIVE state, or we did not set // DownstreamConnection. downstream->pop_downstream_connection(); handler_->signal_write(); return 0; } if (!downstream->request_submission_ready()) { if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { // We have got all response body already. Send it off. downstream->pop_downstream_connection(); return 0; } // pushed stream is handled here shutdown_stream(downstream, NGHTTP3_H3_INTERNAL_ERROR); downstream->pop_downstream_connection(); handler_->signal_write(); return 0; } downstream->pop_downstream_connection(); downstream->add_retry(); std::unique_ptr dconn; rv = 0; if (no_retry || downstream->no_more_retry()) { goto fail; } // downstream connection is clean; we can retry with new // downstream connection. for (;;) { auto dconn = handler_->get_downstream_connection(rv, downstream); if (!dconn) { goto fail; } rv = downstream->attach_downstream_connection(std::move(dconn)); if (rv == 0) { break; } } rv = downstream->push_request_headers(); if (rv != 0) { goto fail; } return 0; fail: if (rv == SHRPX_ERR_TLS_REQUIRED) { assert(0); abort(); } rv = on_downstream_abort_request(downstream, 502); if (rv != 0) { shutdown_stream(downstream, NGHTTP3_H3_INTERNAL_ERROR); } downstream->pop_downstream_connection(); handler_->signal_write(); return 0; } void Http3Upstream::pause_read(IOCtrlReason reason) {} int Http3Upstream::resume_read(IOCtrlReason reason, Downstream *downstream, size_t consumed) { consume(downstream->get_stream_id(), consumed); auto &req = downstream->request(); req.consume(consumed); handler_->signal_write(); return 0; } int Http3Upstream::send_reply(Downstream *downstream, const uint8_t *body, size_t bodylen) { int rv; nghttp3_data_reader data_read, *data_read_ptr = nullptr; const auto &req = downstream->request(); if (req.method != HTTP_HEAD && bodylen) { data_read.read_data = downstream_read_data_callback; data_read_ptr = &data_read; auto buf = downstream->get_response_buf(); buf->append(body, bodylen); } const auto &resp = downstream->response(); auto config = get_config(); auto &httpconf = config->http; auto &balloc = downstream->get_block_allocator(); const auto &headers = resp.fs.headers(); auto nva = std::vector(); // 2 for :status and server nva.reserve(2 + headers.size() + httpconf.add_response_headers.size()); auto response_status = http2::stringify_status(balloc, resp.http_status); nva.push_back(http3::make_field(":status"sv, response_status)); for (auto &kv : headers) { if (kv.name.empty() || kv.name[0] == ':') { continue; } switch (kv.token) { case http2::HD_CONNECTION: case http2::HD_KEEP_ALIVE: case http2::HD_PROXY_CONNECTION: case http2::HD_TE: case http2::HD_TRANSFER_ENCODING: case http2::HD_UPGRADE: continue; } nva.push_back( http3::make_field(kv.name, kv.value, http3::never_index(kv.no_index))); } if (!resp.fs.header(http2::HD_SERVER)) { nva.push_back(http3::make_field("server"sv, config->http.server_name)); } for (auto &p : httpconf.add_response_headers) { nva.push_back(http3::make_field(p.name, p.value)); } rv = nghttp3_conn_submit_response(httpconn_, downstream->get_stream_id(), nva.data(), nva.size(), data_read_ptr); if (nghttp3_err_is_fatal(rv)) { ULOG(FATAL, this) << "nghttp3_conn_submit_response() failed: " << nghttp3_strerror(rv); return -1; } downstream->set_response_state(DownstreamState::MSG_COMPLETE); if (data_read_ptr) { downstream->reset_upstream_wtimer(); } if (shutdown_stream_read(downstream->get_stream_id(), NGHTTP3_H3_NO_ERROR) != 0) { return -1; } return 0; } int Http3Upstream::initiate_push(Downstream *downstream, const std::string_view &uri) { return 0; } int Http3Upstream::response_riovec(struct iovec *iov, int iovcnt) const { return 0; } void Http3Upstream::response_drain(size_t n) {} bool Http3Upstream::response_empty() const { return false; } Downstream * Http3Upstream::on_downstream_push_promise(Downstream *downstream, int32_t promised_stream_id) { return nullptr; } int Http3Upstream::on_downstream_push_promise_complete( Downstream *downstream, Downstream *promised_downstream) { return 0; } bool Http3Upstream::push_enabled() const { return false; } void Http3Upstream::cancel_premature_downstream( Downstream *promised_downstream) {} int Http3Upstream::on_read(const UpstreamAddr *faddr, const Address &remote_addr, const Address &local_addr, const ngtcp2_pkt_info &pi, std::span data) { int rv; auto path = ngtcp2_path{ { const_cast(&local_addr.su.sa), local_addr.len, }, { const_cast(&remote_addr.su.sa), remote_addr.len, }, const_cast(faddr), }; rv = ngtcp2_conn_read_pkt(conn_, &path, &pi, data.data(), data.size(), quic_timestamp()); if (rv != 0) { switch (rv) { case NGTCP2_ERR_DRAINING: return -1; case NGTCP2_ERR_RETRY: { auto worker = handler_->get_worker(); auto quic_conn_handler = worker->get_quic_connection_handler(); if (worker->get_graceful_shutdown()) { ngtcp2_ccerr_set_transport_error(&last_error_, NGTCP2_CONNECTION_REFUSED, nullptr, 0); return handle_error(); } ngtcp2_version_cid vc; rv = ngtcp2_pkt_decode_version_cid(&vc, data.data(), data.size(), SHRPX_QUIC_SCIDLEN); if (rv != 0) { return -1; } // Overwrite error if any is set ngtcp2_ccerr_set_liberr(&last_error_, rv, nullptr, 0); quic_conn_handler->send_retry( handler_->get_upstream_addr(), vc.version, {vc.dcid, vc.dcidlen}, {vc.scid, vc.scidlen}, remote_addr, local_addr, data.size() * 3); return -1; } case NGTCP2_ERR_CRYPTO: if (!last_error_.error_code) { ngtcp2_ccerr_set_tls_alert( &last_error_, ngtcp2_conn_get_tls_alert(conn_), nullptr, 0); } break; case NGTCP2_ERR_DROP_CONN: // Overwrite error if any is set ngtcp2_ccerr_set_liberr(&last_error_, rv, nullptr, 0); return -1; default: if (!last_error_.error_code) { ngtcp2_ccerr_set_liberr(&last_error_, rv, nullptr, 0); } } ULOG(ERROR, this) << "ngtcp2_conn_read_pkt: " << ngtcp2_strerror(rv); return handle_error(); } return 0; } std::pair, int> Http3Upstream::send_packet(const UpstreamAddr *faddr, const sockaddr *remote_sa, socklen_t remote_salen, const sockaddr *local_sa, socklen_t local_salen, const ngtcp2_pkt_info &pi, std::span data, size_t gso_size) { if (tx_.no_gso) { for (; !data.empty();) { auto len = std::min(gso_size, data.size()); auto rv = quic_send_packet(faddr, remote_sa, remote_salen, local_sa, local_salen, pi, data.first(len), gso_size); if (rv != 0) { switch (rv) { case -EAGAIN: #if EAGAIN != EWOULDBLOCK case -EWOULDBLOCK: #endif // EAGAIN != EWOULDBLOCK return {data, SHRPX_ERR_SEND_BLOCKED}; default: return {data, -1}; } } data = data.subspan(len); } return {{}, 0}; } auto rv = quic_send_packet(faddr, remote_sa, remote_salen, local_sa, local_salen, pi, data, gso_size); switch (rv) { case 0: return {{}, 0}; // With GSO, sendmsg may fail with EINVAL if UDP payload is too // large. case -EINVAL: case -EMSGSIZE: // Let the packet lost. break; case -EAGAIN: #if EAGAIN != EWOULDBLOCK case -EWOULDBLOCK: #endif // EAGAIN != EWOULDBLOCK return {data, SHRPX_ERR_SEND_BLOCKED}; case -EIO: if (tx_.no_gso) { break; } tx_.no_gso = true; return send_packet(faddr, remote_sa, remote_salen, local_sa, local_salen, pi, data, gso_size); default: break; } return {{}, -1}; } void Http3Upstream::on_send_blocked(const ngtcp2_path &path, const ngtcp2_pkt_info &pi, std::span data, size_t gso_size) { assert(!tx_.send_blocked); assert(gso_size); tx_.send_blocked = true; auto &p = tx_.blocked; memcpy(&p.local_addr.su, path.local.addr, path.local.addrlen); memcpy(&p.remote_addr.su, path.remote.addr, path.remote.addrlen); p.local_addr.len = path.local.addrlen; p.remote_addr.len = path.remote.addrlen; p.faddr = static_cast(path.user_data); p.pi = pi; p.data = data; p.gso_size = gso_size; } int Http3Upstream::send_blocked_packet() { assert(tx_.send_blocked); auto &p = tx_.blocked; auto [rest, rv] = send_packet(p.faddr, &p.remote_addr.su.sa, p.remote_addr.len, &p.local_addr.su.sa, p.local_addr.len, p.pi, p.data, p.gso_size); if (rv == SHRPX_ERR_SEND_BLOCKED) { p.data = rest; signal_write_upstream_addr(p.faddr); return 0; } tx_.send_blocked = false; return 0; } void Http3Upstream::signal_write_upstream_addr(const UpstreamAddr *faddr) { auto conn = handler_->get_connection(); if (faddr->fd != conn->wev.fd) { if (ev_is_active(&conn->wev)) { ev_io_stop(handler_->get_loop(), &conn->wev); } ev_io_set(&conn->wev, faddr->fd, EV_WRITE); } conn->wlimit.startw(); } int Http3Upstream::handle_error() { if (ngtcp2_conn_in_closing_period(conn_) || ngtcp2_conn_in_draining_period(conn_)) { return -1; } ngtcp2_path_storage ps; ngtcp2_pkt_info pi; ngtcp2_path_storage_zero(&ps); auto ts = quic_timestamp(); conn_close_.resize(SHRPX_QUIC_CONN_CLOSE_PKTLEN); auto nwrite = ngtcp2_conn_write_connection_close(conn_, &ps.path, &pi, conn_close_.data(), conn_close_.size(), &last_error_, ts); if (nwrite < 0) { ULOG(ERROR, this) << "ngtcp2_conn_write_connection_close: " << ngtcp2_strerror(static_cast(nwrite)); return -1; } conn_close_.resize(static_cast(nwrite)); if (nwrite == 0) { return -1; } send_packet(static_cast(ps.path.user_data), ps.path.remote.addr, ps.path.remote.addrlen, ps.path.local.addr, ps.path.local.addrlen, pi, conn_close_, conn_close_.size()); return -1; } int Http3Upstream::handle_expiry() { int rv; auto ts = quic_timestamp(); rv = ngtcp2_conn_handle_expiry(conn_, ts); if (rv != 0) { if (rv == NGTCP2_ERR_IDLE_CLOSE) { ULOG(INFO, this) << "Idle connection timeout"; } else { ULOG(ERROR, this) << "ngtcp2_conn_handle_expiry: " << ngtcp2_strerror(rv); } ngtcp2_ccerr_set_liberr(&last_error_, rv, nullptr, 0); return handle_error(); } return 0; } void Http3Upstream::reset_timer() { auto ts = quic_timestamp(); auto expiry_ts = ngtcp2_conn_get_expiry(conn_); auto loop = handler_->get_loop(); if (expiry_ts <= ts) { ev_feed_event(loop, &timer_, EV_TIMER); return; } timer_.repeat = static_cast(expiry_ts - ts) / NGTCP2_SECONDS; ev_timer_again(loop, &timer_); } namespace { int http_deferred_consume(nghttp3_conn *conn, int64_t stream_id, size_t nconsumed, void *user_data, void *stream_user_data) { auto upstream = static_cast(user_data); upstream->consume(stream_id, nconsumed); return 0; } } // namespace namespace { int http_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, uint64_t datalen, void *user_data, void *stream_user_data) { auto upstream = static_cast(user_data); auto downstream = static_cast(stream_user_data); assert(downstream); if (upstream->http_acked_stream_data(downstream, datalen) != 0) { return NGHTTP3_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Http3Upstream::http_acked_stream_data(Downstream *downstream, uint64_t datalen) { if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "Stream " << downstream->get_stream_id() << " " << datalen << " bytes acknowledged"; } auto body = downstream->get_response_buf(); auto drained = body->drain_mark(datalen); (void)drained; assert(datalen == drained); if (downstream->resume_read(SHRPX_NO_BUFFER, datalen) != 0) { return -1; } return 0; } namespace { int http_begin_request_headers(nghttp3_conn *conn, int64_t stream_id, void *user_data, void *stream_user_data) { if (!ngtcp2_is_bidi_stream(stream_id)) { return 0; } auto upstream = static_cast(user_data); upstream->http_begin_request_headers(stream_id); return 0; } } // namespace namespace { int http_recv_request_header(nghttp3_conn *conn, int64_t stream_id, int32_t token, nghttp3_rcbuf *name, nghttp3_rcbuf *value, uint8_t flags, void *user_data, void *stream_user_data) { auto upstream = static_cast(user_data); auto downstream = static_cast(stream_user_data); if (!downstream || downstream->get_stop_reading()) { return 0; } if (upstream->http_recv_request_header(downstream, token, name, value, flags, /* trailer = */ false) != 0) { return NGHTTP3_ERR_CALLBACK_FAILURE; } return 0; } } // namespace namespace { int http_recv_request_trailer(nghttp3_conn *conn, int64_t stream_id, int32_t token, nghttp3_rcbuf *name, nghttp3_rcbuf *value, uint8_t flags, void *user_data, void *stream_user_data) { auto upstream = static_cast(user_data); auto downstream = static_cast(stream_user_data); if (!downstream || downstream->get_stop_reading()) { return 0; } if (upstream->http_recv_request_header(downstream, token, name, value, flags, /* trailer = */ true) != 0) { return NGHTTP3_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Http3Upstream::http_recv_request_header(Downstream *downstream, int32_t h3token, nghttp3_rcbuf *name, nghttp3_rcbuf *value, uint8_t flags, bool trailer) { auto namebuf = nghttp3_rcbuf_get_buf(name); auto valuebuf = nghttp3_rcbuf_get_buf(value); auto &req = downstream->request(); auto config = get_config(); auto &httpconf = config->http; if (req.fs.buffer_size() + namebuf.len + valuebuf.len > httpconf.request_header_field_buffer || req.fs.num_fields() >= httpconf.max_request_header_fields) { downstream->set_stop_reading(true); if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { return 0; } if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "Too large or many header field size=" << req.fs.buffer_size() + namebuf.len + valuebuf.len << ", num=" << req.fs.num_fields() + 1; } // just ignore if this is a trailer part. if (trailer) { if (shutdown_stream_read(downstream->get_stream_id(), NGHTTP3_H3_NO_ERROR) != 0) { return -1; } return 0; } if (error_reply(downstream, 431) != 0) { return -1; } return 0; } auto nameref = as_string_view(namebuf.base, namebuf.len); auto valueref = as_string_view(valuebuf.base, valuebuf.len); auto token = http2::lookup_token(nameref); auto no_index = flags & NGHTTP3_NV_FLAG_NEVER_INDEX; downstream->add_rcbuf(name); downstream->add_rcbuf(value); if (trailer) { req.fs.add_trailer_token(nameref, valueref, no_index, token); return 0; } req.fs.add_header_token(nameref, valueref, no_index, token); return 0; } namespace { int http_end_request_headers(nghttp3_conn *conn, int64_t stream_id, int fin, void *user_data, void *stream_user_data) { auto upstream = static_cast(user_data); auto downstream = static_cast(stream_user_data); if (!downstream || downstream->get_stop_reading()) { return 0; } if (upstream->http_end_request_headers(downstream, fin) != 0) { return NGHTTP3_ERR_CALLBACK_FAILURE; } downstream->reset_upstream_rtimer(); downstream->stop_header_timer(); return 0; } } // namespace int Http3Upstream::http_end_request_headers(Downstream *downstream, int fin) { auto lgconf = log_config(); lgconf->update_tstamp(std::chrono::system_clock::now()); auto &req = downstream->request(); req.tstamp = lgconf->tstamp; if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { return 0; } auto &nva = req.fs.headers(); if (LOG_ENABLED(INFO)) { std::stringstream ss; for (auto &nv : nva) { if (nv.name == "authorization"sv) { ss << TTY_HTTP_HD << nv.name << TTY_RST << ": \n"; continue; } ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n"; } ULOG(INFO, this) << "HTTP request headers. stream_id=" << downstream->get_stream_id() << "\n" << ss.str(); } auto content_length = req.fs.header(http2::HD_CONTENT_LENGTH); if (content_length) { // libnghttp3 guarantees this can be parsed req.fs.content_length = util::parse_uint(content_length->value).value_or(-1); } // presence of mandatory header fields are guaranteed by libnghttp3. auto authority = req.fs.header(http2::HD__AUTHORITY); auto path = req.fs.header(http2::HD__PATH); auto method = req.fs.header(http2::HD__METHOD); auto scheme = req.fs.header(http2::HD__SCHEME); auto method_token = http2::lookup_method_token(method->value); if (method_token == -1) { if (error_reply(downstream, 501) != 0) { return -1; } return 0; } auto faddr = handler_->get_upstream_addr(); auto config = get_config(); // For HTTP/2 proxy, we require :authority. if (method_token != HTTP_CONNECT && config->http2_proxy && faddr->alt_mode == UpstreamAltMode::NONE && !authority) { shutdown_stream(downstream, NGHTTP3_H3_GENERAL_PROTOCOL_ERROR); return 0; } req.method = method_token; if (scheme) { req.scheme = scheme->value; } // nghttp2 library guarantees either :authority or host exist if (!authority) { req.no_authority = true; authority = req.fs.header(http2::HD_HOST); } if (authority) { req.authority = authority->value; } if (path) { if (method_token == HTTP_OPTIONS && path->value == "*"sv) { // Server-wide OPTIONS request. Path is empty. } else if (config->http2_proxy && faddr->alt_mode == UpstreamAltMode::NONE) { req.path = path->value; } else { req.path = http2::rewrite_clean_path(downstream->get_block_allocator(), path->value); } } auto connect_proto = req.fs.header(http2::HD__PROTOCOL); if (connect_proto) { if (connect_proto->value != "websocket"sv) { if (error_reply(downstream, 400) != 0) { return -1; } return 0; } req.connect_proto = ConnectProto::WEBSOCKET; } if (!fin) { req.http2_expect_body = true; } else if (req.fs.content_length == -1) { req.fs.content_length = 0; } downstream->inspect_http2_request(); downstream->set_request_state(DownstreamState::HEADER_COMPLETE); if (config->http.require_http_scheme && !http::check_http_scheme(req.scheme, /* encrypted = */ true)) { if (error_reply(downstream, 400) != 0) { return -1; } } #ifdef HAVE_MRUBY auto worker = handler_->get_worker(); auto mruby_ctx = worker->get_mruby_context(); if (mruby_ctx->run_on_request_proc(downstream) != 0) { if (error_reply(downstream, 500) != 0) { return -1; } return 0; } #endif // defined(HAVE_MRUBY) if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { return 0; } start_downstream(downstream); return 0; } void Http3Upstream::start_downstream(Downstream *downstream) { if (downstream_queue_.can_activate(downstream->request().authority)) { initiate_downstream(downstream); return; } downstream_queue_.mark_blocked(downstream); } void Http3Upstream::initiate_downstream(Downstream *downstream) { int rv; #ifdef HAVE_MRUBY DownstreamConnection *dconn_ptr; #endif // defined(HAVE_MRUBY) for (;;) { auto dconn = handler_->get_downstream_connection(rv, downstream); if (!dconn) { if (rv == SHRPX_ERR_TLS_REQUIRED) { assert(0); abort(); } rv = error_reply(downstream, 502); if (rv != 0) { shutdown_stream(downstream, NGHTTP3_H3_INTERNAL_ERROR); } downstream->set_request_state(DownstreamState::CONNECT_FAIL); downstream_queue_.mark_failure(downstream); return; } #ifdef HAVE_MRUBY dconn_ptr = dconn.get(); #endif // defined(HAVE_MRUBY) rv = downstream->attach_downstream_connection(std::move(dconn)); if (rv == 0) { break; } } #ifdef HAVE_MRUBY const auto &group = dconn_ptr->get_downstream_addr_group(); if (group) { const auto &mruby_ctx = group->shared_addr->mruby_ctx; if (mruby_ctx->run_on_request_proc(downstream) != 0) { if (error_reply(downstream, 500) != 0) { shutdown_stream(downstream, NGHTTP3_H3_INTERNAL_ERROR); } downstream_queue_.mark_failure(downstream); return; } if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { return; } } #endif // defined(HAVE_MRUBY) rv = downstream->push_request_headers(); if (rv != 0) { if (error_reply(downstream, 502) != 0) { shutdown_stream(downstream, NGHTTP3_H3_INTERNAL_ERROR); } downstream_queue_.mark_failure(downstream); return; } downstream_queue_.mark_active(downstream); auto &req = downstream->request(); if (!req.http2_expect_body) { rv = downstream->end_upload_data(); if (rv != 0) { shutdown_stream(downstream, NGHTTP3_H3_INTERNAL_ERROR); } } } namespace { int http_recv_data(nghttp3_conn *conn, int64_t stream_id, const uint8_t *data, size_t datalen, void *user_data, void *stream_user_data) { auto upstream = static_cast(user_data); auto downstream = static_cast(stream_user_data); if (upstream->http_recv_data(downstream, {data, datalen}) != 0) { return NGHTTP3_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Http3Upstream::http_recv_data(Downstream *downstream, std::span data) { downstream->reset_upstream_rtimer(); if (downstream->push_upload_data_chunk(data.data(), data.size()) != 0) { if (downstream->get_response_state() != DownstreamState::MSG_COMPLETE) { shutdown_stream(downstream, NGHTTP3_H3_INTERNAL_ERROR); } consume(downstream->get_stream_id(), data.size()); return 0; } return 0; } namespace { int http_end_stream(nghttp3_conn *conn, int64_t stream_id, void *user_data, void *stream_user_data) { auto upstream = static_cast(user_data); auto downstream = static_cast(stream_user_data); if (!downstream || downstream->get_stop_reading()) { return 0; } if (upstream->http_end_stream(downstream) != 0) { return NGHTTP3_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Http3Upstream::http_end_stream(Downstream *downstream) { downstream->disable_upstream_rtimer(); if (downstream->end_upload_data() != 0) { if (downstream->get_response_state() != DownstreamState::MSG_COMPLETE) { shutdown_stream(downstream, NGHTTP3_H3_INTERNAL_ERROR); } } downstream->set_request_state(DownstreamState::MSG_COMPLETE); return 0; } namespace { int http_stream_close(nghttp3_conn *conn, int64_t stream_id, uint64_t app_error_code, void *conn_user_data, void *stream_user_data) { auto upstream = static_cast(conn_user_data); auto downstream = static_cast(stream_user_data); if (!downstream) { return 0; } if (upstream->http_stream_close(downstream, app_error_code) != 0) { return NGHTTP3_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Http3Upstream::http_stream_close(Downstream *downstream, uint64_t app_error_code) { auto stream_id = downstream->get_stream_id(); if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "Stream stream_id=" << stream_id << " is being closed with app_error_code=" << app_error_code; auto body = downstream->get_response_buf(); ULOG(INFO, this) << "response unacked_left=" << body->rleft() << " not_sent=" << body->rleft_mark(); } auto &req = downstream->request(); consume(stream_id, req.unconsumed_body_length); req.unconsumed_body_length = 0; ngtcp2_conn_extend_max_streams_bidi(conn_, 1); if (downstream->get_request_state() == DownstreamState::CONNECT_FAIL) { remove_downstream(downstream); // downstream was deleted return 0; } if (downstream->can_detach_downstream_connection()) { // Keep-alive downstream->detach_downstream_connection(); } downstream->set_request_state(DownstreamState::STREAM_CLOSED); // At this point, downstream read may be paused. // If shrpx_downstream::push_request_headers() failed, the // error is handled here. remove_downstream(downstream); // downstream was deleted return 0; } namespace { int http_stop_sending(nghttp3_conn *conn, int64_t stream_id, uint64_t app_error_code, void *user_data, void *stream_user_data) { auto upstream = static_cast(user_data); if (upstream->http_stop_sending(stream_id, app_error_code) != 0) { return NGHTTP3_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Http3Upstream::http_stop_sending(int64_t stream_id, uint64_t app_error_code) { auto rv = ngtcp2_conn_shutdown_stream_read(conn_, 0, stream_id, app_error_code); if (ngtcp2_err_is_fatal(rv)) { ULOG(ERROR, this) << "ngtcp2_conn_shutdown_stream_read: " << ngtcp2_strerror(rv); return -1; } return 0; } namespace { int http_reset_stream(nghttp3_conn *conn, int64_t stream_id, uint64_t app_error_code, void *user_data, void *stream_user_data) { auto upstream = static_cast(user_data); if (upstream->http_reset_stream(stream_id, app_error_code) != 0) { return NGHTTP3_ERR_CALLBACK_FAILURE; } return 0; } } // namespace int Http3Upstream::http_reset_stream(int64_t stream_id, uint64_t app_error_code) { auto rv = ngtcp2_conn_shutdown_stream_write(conn_, 0, stream_id, app_error_code); if (ngtcp2_err_is_fatal(rv)) { ULOG(ERROR, this) << "ngtcp2_conn_shutdown_stream_write: " << ngtcp2_strerror(rv); return -1; } return 0; } int Http3Upstream::setup_httpconn() { int rv; if (ngtcp2_conn_get_streams_uni_left(conn_) < 3) { return -1; } nghttp3_callbacks callbacks{ .acked_stream_data = shrpx::http_acked_stream_data, .stream_close = shrpx::http_stream_close, .recv_data = shrpx::http_recv_data, .deferred_consume = http_deferred_consume, .begin_headers = shrpx::http_begin_request_headers, .recv_header = shrpx::http_recv_request_header, .end_headers = shrpx::http_end_request_headers, .recv_trailer = shrpx::http_recv_request_trailer, .stop_sending = shrpx::http_stop_sending, .end_stream = shrpx::http_end_stream, .reset_stream = shrpx::http_reset_stream, .rand = shrpx::rand_bytes, }; auto config = get_config(); nghttp3_settings settings; nghttp3_settings_default(&settings); settings.qpack_max_dtable_capacity = 4_k; if (!config->http2_proxy) { settings.enable_connect_protocol = 1; } auto mem = nghttp3_mem_default(); rv = nghttp3_conn_server_new(&httpconn_, &callbacks, &settings, mem, this); if (rv != 0) { ULOG(ERROR, this) << "nghttp3_conn_server_new: " << nghttp3_strerror(rv); return -1; } auto params = ngtcp2_conn_get_local_transport_params(conn_); nghttp3_conn_set_max_client_streams_bidi(httpconn_, params->initial_max_streams_bidi); int64_t ctrl_stream_id; rv = ngtcp2_conn_open_uni_stream(conn_, &ctrl_stream_id, nullptr); if (rv != 0) { ULOG(ERROR, this) << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv); return -1; } rv = nghttp3_conn_bind_control_stream(httpconn_, ctrl_stream_id); if (rv != 0) { ULOG(ERROR, this) << "nghttp3_conn_bind_control_stream: " << nghttp3_strerror(rv); return -1; } int64_t qpack_enc_stream_id, qpack_dec_stream_id; rv = ngtcp2_conn_open_uni_stream(conn_, &qpack_enc_stream_id, nullptr); if (rv != 0) { ULOG(ERROR, this) << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv); return -1; } rv = ngtcp2_conn_open_uni_stream(conn_, &qpack_dec_stream_id, nullptr); if (rv != 0) { ULOG(ERROR, this) << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv); return -1; } rv = nghttp3_conn_bind_qpack_streams(httpconn_, qpack_enc_stream_id, qpack_dec_stream_id); if (rv != 0) { ULOG(ERROR, this) << "nghttp3_conn_bind_qpack_streams: " << nghttp3_strerror(rv); return -1; } return 0; } int Http3Upstream::error_reply(Downstream *downstream, unsigned int status_code) { int rv; auto &resp = downstream->response(); auto &balloc = downstream->get_block_allocator(); auto html = http::create_error_html(balloc, status_code); resp.http_status = status_code; nghttp3_data_reader data_read, *data_read_ptr = nullptr; const auto &req = downstream->request(); if (req.method != HTTP_HEAD) { data_read.read_data = downstream_read_data_callback; data_read_ptr = &data_read; auto body = downstream->get_response_buf(); body->append(html); } downstream->set_response_state(DownstreamState::MSG_COMPLETE); auto lgconf = log_config(); lgconf->update_tstamp(std::chrono::system_clock::now()); auto response_status = http2::stringify_status(balloc, status_code); auto content_length = util::make_string_ref_uint(balloc, html.size()); auto date = make_string_ref(balloc, lgconf->tstamp->time_http); auto nva = std::to_array( {http3::make_field(":status"sv, response_status), http3::make_field("content-type"sv, "text/html; charset=UTF-8"sv), http3::make_field("server"sv, get_config()->http.server_name), http3::make_field("content-length"sv, content_length), http3::make_field("date"sv, date)}); rv = nghttp3_conn_submit_response(httpconn_, downstream->get_stream_id(), nva.data(), nva.size(), data_read_ptr); if (nghttp3_err_is_fatal(rv)) { ULOG(FATAL, this) << "nghttp3_conn_submit_response() failed: " << nghttp3_strerror(rv); return -1; } downstream->reset_upstream_wtimer(); if (shutdown_stream_read(downstream->get_stream_id(), NGHTTP3_H3_NO_ERROR) != 0) { return -1; } return 0; } int Http3Upstream::shutdown_stream(Downstream *downstream, uint64_t app_error_code) { auto stream_id = downstream->get_stream_id(); if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "Shutdown stream_id=" << stream_id << " with app_error_code=" << app_error_code; } auto rv = ngtcp2_conn_shutdown_stream(conn_, 0, stream_id, app_error_code); if (rv != 0) { ULOG(FATAL, this) << "ngtcp2_conn_shutdown_stream() failed: " << ngtcp2_strerror(rv); return -1; } return 0; } int Http3Upstream::shutdown_stream_read(int64_t stream_id, uint64_t app_error_code) { auto rv = ngtcp2_conn_shutdown_stream_read(conn_, 0, stream_id, NGHTTP3_H3_NO_ERROR); if (ngtcp2_err_is_fatal(rv)) { ULOG(FATAL, this) << "ngtcp2_conn_shutdown_stream_read: " << ngtcp2_strerror(rv); return -1; } return 0; } void Http3Upstream::consume(int64_t stream_id, size_t nconsumed) { ngtcp2_conn_extend_max_stream_offset(conn_, stream_id, nconsumed); ngtcp2_conn_extend_max_offset(conn_, nconsumed); } void Http3Upstream::remove_downstream(Downstream *downstream) { if (downstream->accesslog_ready()) { handler_->write_accesslog(downstream); } nghttp3_conn_set_stream_user_data(httpconn_, downstream->get_stream_id(), nullptr); auto next_downstream = downstream_queue_.remove_and_get_blocked(downstream); if (next_downstream) { initiate_downstream(next_downstream); } if (downstream_queue_.get_downstreams() == nullptr) { // There is no downstream at the moment. Start idle timer now. handler_->repeat_read_timer(); } } void Http3Upstream::log_response_headers( Downstream *downstream, const std::vector &nva) const { std::stringstream ss; for (auto &nv : nva) { ss << TTY_HTTP_HD << as_string_view(nv.name, nv.namelen) << TTY_RST << ": " << as_string_view(nv.value, nv.valuelen) << "\n"; } ULOG(INFO, this) << "HTTP response headers. stream_id=" << downstream->get_stream_id() << "\n" << ss.str(); } int Http3Upstream::check_shutdown() { auto worker = handler_->get_worker(); if (!worker->get_graceful_shutdown()) { return 0; } ev_prepare_stop(handler_->get_loop(), &prep_); return start_graceful_shutdown(); } int Http3Upstream::start_graceful_shutdown() { int rv; if (ev_is_active(&shutdown_timer_)) { return 0; } if (!httpconn_) { return -1; } rv = nghttp3_conn_submit_shutdown_notice(httpconn_); if (rv != 0) { ULOG(FATAL, this) << "nghttp3_conn_submit_shutdown_notice: " << nghttp3_strerror(rv); return -1; } handler_->signal_write(); auto t = ngtcp2_conn_get_pto(conn_); ev_timer_set(&shutdown_timer_, static_cast(t * 3) / NGTCP2_SECONDS, 0.); ev_timer_start(handler_->get_loop(), &shutdown_timer_); return 0; } int Http3Upstream::submit_goaway() { int rv; rv = nghttp3_conn_shutdown(httpconn_); if (rv != 0) { ULOG(FATAL, this) << "nghttp3_conn_shutdown: " << nghttp3_strerror(rv); return -1; } handler_->signal_write(); return 0; } int Http3Upstream::open_qlog_file(const std::string_view &dir, const ngtcp2_cid &scid) const { std::array buf; auto path = std::string{dir}; path += '/'; path += util::format_iso8601_basic(buf.data(), std::chrono::system_clock::now()); path += '-'; path += util::format_hex(std::span{scid.data, scid.datalen}); path += ".sqlog"; int fd; #ifdef O_CLOEXEC while ((fd = open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP)) == -1 && errno == EINTR) ; #else // !defined(O_CLOEXEC) while ((fd = open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP)) == -1 && errno == EINTR) ; if (fd != -1) { util::make_socket_closeonexec(fd); } #endif // !defined(O_CLOEXEC) if (fd == -1) { auto error = errno; ULOG(ERROR, this) << "Failed to open qlog file " << path << ": errno=" << error; return -1; } return fd; } ngtcp2_conn *Http3Upstream::get_conn() const { return conn_; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/http3.cc0000644000000000000000000000013215077107270014420 xustar0030 mtime=1761382072.988444162 30 atime=1761382106.202310436 30 ctime=1761382109.198300098 nghttp2-1.68.0/src/http3.cc0000644000175100017510000000775115077107270015022 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2021 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "http3.h" namespace nghttp2 { namespace http3 { namespace { void copy_headers_to_nva_internal(std::vector &nva, const HeaderRefs &headers, uint8_t nv_flags, uint32_t flags) { auto it_forwarded = std::ranges::end(headers); auto it_xff = std::ranges::end(headers); auto it_xfp = std::ranges::end(headers); auto it_via = std::ranges::end(headers); for (auto it = std::ranges::begin(headers); it != std::ranges::end(headers); ++it) { auto kv = &(*it); if (kv->name.empty() || kv->name[0] == ':') { continue; } switch (kv->token) { case http2::HD_COOKIE: case http2::HD_CONNECTION: case http2::HD_HOST: case http2::HD_HTTP2_SETTINGS: case http2::HD_KEEP_ALIVE: case http2::HD_PROXY_CONNECTION: case http2::HD_SERVER: case http2::HD_TE: case http2::HD_TRANSFER_ENCODING: case http2::HD_UPGRADE: continue; case http2::HD_EARLY_DATA: if (flags & http2::HDOP_STRIP_EARLY_DATA) { continue; } break; case http2::HD_SEC_WEBSOCKET_ACCEPT: if (flags & http2::HDOP_STRIP_SEC_WEBSOCKET_ACCEPT) { continue; } break; case http2::HD_SEC_WEBSOCKET_KEY: if (flags & http2::HDOP_STRIP_SEC_WEBSOCKET_KEY) { continue; } break; case http2::HD_FORWARDED: if (flags & http2::HDOP_STRIP_FORWARDED) { continue; } if (it_forwarded == std::ranges::end(headers)) { it_forwarded = it; continue; } kv = &(*it_forwarded); it_forwarded = it; break; case http2::HD_X_FORWARDED_FOR: if (flags & http2::HDOP_STRIP_X_FORWARDED_FOR) { continue; } if (it_xff == std::ranges::end(headers)) { it_xff = it; continue; } kv = &(*it_xff); it_xff = it; break; case http2::HD_X_FORWARDED_PROTO: if (flags & http2::HDOP_STRIP_X_FORWARDED_PROTO) { continue; } if (it_xfp == std::ranges::end(headers)) { it_xfp = it; continue; } kv = &(*it_xfp); it_xfp = it; break; case http2::HD_VIA: if (flags & http2::HDOP_STRIP_VIA) { continue; } if (it_via == std::ranges::end(headers)) { it_via = it; continue; } kv = &(*it_via); it_via = it; break; } nva.push_back(make_field_flags(kv->name, kv->value, nv_flags | never_index(kv->no_index))); } } } // namespace void copy_headers_to_nva_nocopy(std::vector &nva, const HeaderRefs &headers, uint32_t flags) { copy_headers_to_nva_internal( nva, headers, NGHTTP3_NV_FLAG_NO_COPY_NAME | NGHTTP3_NV_FLAG_NO_COPY_VALUE, flags); } } // namespace http3 } // namespace nghttp2 nghttp2-1.68.0/src/PaxHeaders/shrpx_worker_process.cc0000644000000000000000000000013215077107271017652 xustar0030 mtime=1761382073.000444106 30 atime=1761382080.105411485 30 ctime=1761382109.143300257 nghttp2-1.68.0/src/shrpx_worker_process.cc0000644000175100017510000005223015077107271020244 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_worker_process.h" #include #ifdef HAVE_UNISTD_H # include #endif // defined(HAVE_UNISTD_H) #include #include #include #include #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include #include #include "shrpx_config.h" #include "shrpx_connection_handler.h" #include "shrpx_log_config.h" #include "shrpx_worker.h" #include "shrpx_accept_handler.h" #include "shrpx_http2_upstream.h" #include "shrpx_http2_session.h" #include "shrpx_memcached_dispatcher.h" #include "shrpx_memcached_request.h" #include "shrpx_process.h" #include "shrpx_tls.h" #include "shrpx_log.h" #include "util.h" #include "app_helper.h" #include "template.h" #include "xsi_strerror.h" using namespace nghttp2; namespace shrpx { namespace { void drop_privileges( #ifdef HAVE_NEVERBLEED neverbleed_t *nb #endif // defined(HAVE_NEVERBLEED) ) { std::array errbuf; auto config = get_config(); if (getuid() == 0 && config->uid != 0) { #ifdef HAVE_NEVERBLEED if (nb) { neverbleed_setuidgid(nb, config->user.data(), 1); } #endif // defined(HAVE_NEVERBLEED) if (initgroups(config->user.data(), #ifndef __APPLE__ config->gid #else // defined(__APPLE__) static_cast(config->gid) #endif // defined(__APPLE__) ) != 0) { auto error = errno; LOG(FATAL) << "Could not change supplementary groups: " << xsi_strerror(error, errbuf.data(), errbuf.size()); exit(EXIT_FAILURE); } if (setgid(config->gid) != 0) { auto error = errno; LOG(FATAL) << "Could not change gid: " << xsi_strerror(error, errbuf.data(), errbuf.size()); exit(EXIT_FAILURE); } if (setuid(config->uid) != 0) { auto error = errno; LOG(FATAL) << "Could not change uid: " << xsi_strerror(error, errbuf.data(), errbuf.size()); exit(EXIT_FAILURE); } if (setuid(0) != -1) { LOG(FATAL) << "Still have root privileges?"; exit(EXIT_FAILURE); } } } } // namespace namespace { void graceful_shutdown(ConnectionHandler *conn_handler) { if (conn_handler->get_graceful_shutdown()) { return; } LOG(NOTICE) << "Graceful shutdown signal received"; conn_handler->set_graceful_shutdown(true); conn_handler->graceful_shutdown_worker(); auto single_worker = conn_handler->get_single_worker(); if (single_worker) { auto worker_stat = single_worker->get_worker_stat(); if (worker_stat->num_connections == 0 && worker_stat->num_close_waits == 0) { ev_break(conn_handler->get_loop()); } return; } } } // namespace namespace { void reopen_log(ConnectionHandler *conn_handler) { LOG(NOTICE) << "Reopening log files: worker process (thread main)"; auto config = get_config(); auto &loggingconf = config->logging; (void)reopen_log_files(loggingconf); redirect_stderr_to_errorlog(loggingconf); conn_handler->worker_reopen_log_files(); } } // namespace namespace { void ipc_readcb(struct ev_loop *loop, ev_io *w, int revents) { auto conn_handler = static_cast(w->data); std::array buf; ssize_t nread; while ((nread = read(w->fd, buf.data(), buf.size())) == -1 && errno == EINTR) ; if (nread == -1) { auto error = errno; LOG(ERROR) << "Failed to read data from ipc channel: errno=" << error; return; } if (nread == 0) { // IPC socket closed. Perform immediate shutdown. LOG(FATAL) << "IPC socket is closed. Perform immediate shutdown."; nghttp2_Exit(EXIT_FAILURE); } for (size_t i = 0; i < as_unsigned(nread); ++i) { switch (buf[i]) { case SHRPX_IPC_GRACEFUL_SHUTDOWN: graceful_shutdown(conn_handler); break; case SHRPX_IPC_REOPEN_LOG: reopen_log(conn_handler); break; } } } } // namespace #ifdef ENABLE_HTTP3 namespace { void quic_ipc_readcb(struct ev_loop *loop, ev_io *w, int revents) { auto conn_handler = static_cast(w->data); if (conn_handler->quic_ipc_read() != 0) { LOG(ERROR) << "Failed to read data from QUIC IPC channel"; return; } } } // namespace #endif // defined(ENABLE_HTTP3) namespace { int generate_ticket_key(TicketKey &ticket_key) { ticket_key.cipher = get_config()->tls.ticket.cipher; ticket_key.hmac = EVP_sha256(); ticket_key.hmac_keylen = static_cast(EVP_MD_size(ticket_key.hmac)); assert(static_cast(EVP_CIPHER_key_length(ticket_key.cipher)) <= ticket_key.data.enc_key.size()); assert(ticket_key.hmac_keylen <= ticket_key.data.hmac_key.size()); if (LOG_ENABLED(INFO)) { LOG(INFO) << "enc_keylen=" << EVP_CIPHER_key_length(ticket_key.cipher) << ", hmac_keylen=" << ticket_key.hmac_keylen; } if (RAND_bytes(reinterpret_cast(&ticket_key.data), sizeof(ticket_key.data)) == 0) { return -1; } return 0; } } // namespace namespace { void renew_ticket_key_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto conn_handler = static_cast(w->data); const auto &old_ticket_keys = conn_handler->get_ticket_keys(); auto ticket_keys = std::make_shared(); LOG(NOTICE) << "Renew new ticket keys"; // If old_ticket_keys is not empty, it should contain at least 2 // keys: one for encryption, and last one for the next encryption // key but decryption only. The keys in between are old keys and // decryption only. The next key is provided to ensure to mitigate // possible problem when one worker encrypt new key, but one worker, // which did not take the that key yet, and cannot decrypt it. // // We keep keys for get_config()->tls_session_timeout seconds. The // default is 12 hours. Thus the maximum ticket vector size is 12. if (old_ticket_keys) { auto &old_keys = old_ticket_keys->keys; auto &new_keys = ticket_keys->keys; assert(!old_keys.empty()); auto max_tickets = static_cast(std::chrono::duration_cast( get_config()->tls.session_timeout) .count()); new_keys.resize(std::min(max_tickets, old_keys.size() + 1)); std::ranges::copy_n(std::ranges::begin(old_keys), as_signed(new_keys.size() - 1), std::ranges::begin(new_keys) + 1); } else { ticket_keys->keys.resize(1); } auto &new_key = ticket_keys->keys[0]; if (generate_ticket_key(new_key) != 0) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "failed to generate ticket key"; } conn_handler->set_ticket_keys(nullptr); conn_handler->set_ticket_keys_to_worker(nullptr); return; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "ticket keys generation done"; assert(ticket_keys->keys.size() >= 1); LOG(INFO) << 0 << " enc+dec: " << util::format_hex(ticket_keys->keys[0].data.name); for (size_t i = 1; i < ticket_keys->keys.size(); ++i) { auto &key = ticket_keys->keys[i]; LOG(INFO) << i << " dec: " << util::format_hex(key.data.name); } } conn_handler->set_ticket_keys(ticket_keys); conn_handler->set_ticket_keys_to_worker(ticket_keys); } } // namespace namespace { void memcached_get_ticket_key_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto conn_handler = static_cast(w->data); auto dispatcher = conn_handler->get_tls_ticket_key_memcached_dispatcher(); auto req = std::make_unique(); req->key = "nghttpx:tls-ticket-key"; req->op = MemcachedOp::GET; req->cb = [conn_handler, w](MemcachedRequest *req, MemcachedResult res) { switch (res.status_code) { case MemcachedStatusCode::NO_ERROR: break; case MemcachedStatusCode::EXT_NETWORK_ERROR: conn_handler->on_tls_ticket_key_network_error(w); return; default: conn_handler->on_tls_ticket_key_not_found(w); return; } // |version (4bytes)|len (2bytes)|key (variable length)|... // (len, key) pairs are repeated as necessary. auto &value = res.value; if (value.size() < 4) { LOG(WARN) << "Memcached: tls ticket key value is too small: got " << value.size(); conn_handler->on_tls_ticket_key_not_found(w); return; } auto p = value.data(); auto version = util::get_uint32(p); // Currently supported version is 1. if (version != 1) { LOG(WARN) << "Memcached: tls ticket key version: want 1, got " << version; conn_handler->on_tls_ticket_key_not_found(w); return; } auto end = p + value.size(); p += 4; auto &ticketconf = get_config()->tls.ticket; size_t expectedlen; size_t enc_keylen; size_t hmac_keylen; if (ticketconf.cipher == EVP_aes_128_cbc()) { expectedlen = 48; enc_keylen = 16; hmac_keylen = 16; } else if (ticketconf.cipher == EVP_aes_256_cbc()) { expectedlen = 80; enc_keylen = 32; hmac_keylen = 32; } else { return; } auto ticket_keys = std::make_shared(); for (; p != end;) { if (end - p < 2) { LOG(WARN) << "Memcached: tls ticket key data is too small"; conn_handler->on_tls_ticket_key_not_found(w); return; } auto len = util::get_uint16(p); p += 2; if (len != expectedlen) { LOG(WARN) << "Memcached: wrong tls ticket key size: want " << expectedlen << ", got " << len; conn_handler->on_tls_ticket_key_not_found(w); return; } if (p + len > end) { LOG(WARN) << "Memcached: too short tls ticket key payload: want " << len << ", got " << (end - p); conn_handler->on_tls_ticket_key_not_found(w); return; } auto key = TicketKey(); key.cipher = ticketconf.cipher; key.hmac = EVP_sha256(); key.hmac_keylen = hmac_keylen; p = std::ranges::copy_n(p, key.data.name.size(), std::ranges::begin(key.data.name)) .in; p = std::ranges::copy_n(p, as_signed(enc_keylen), std::ranges::begin(key.data.enc_key)) .in; p = std::ranges::copy_n(p, as_signed(hmac_keylen), std::ranges::begin(key.data.hmac_key)) .in; ticket_keys->keys.push_back(std::move(key)); } conn_handler->on_tls_ticket_key_get_success(ticket_keys, w); }; if (LOG_ENABLED(INFO)) { LOG(INFO) << "Memcached: tls ticket key get request sent"; } dispatcher->add_request(std::move(req)); } } // namespace #ifdef HAVE_NEVERBLEED namespace { void nb_child_cb(struct ev_loop *loop, ev_child *w, int revents) { log_chld(w->rpid, w->rstatus, "neverbleed process"); ev_child_stop(loop, w); LOG(FATAL) << "neverbleed process exited; aborting now"; nghttp2_Exit(EXIT_FAILURE); } } // namespace #endif // defined(HAVE_NEVERBLEED) namespace { int send_ready_event(int ready_ipc_fd) { std::array errbuf; auto pid = getpid(); ssize_t nwrite; while ((nwrite = write(ready_ipc_fd, &pid, sizeof(pid))) == -1 && errno == EINTR) ; if (nwrite < 0) { auto error = errno; LOG(ERROR) << "Writing PID to ready IPC channel failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); return -1; } return 0; } } // namespace int worker_process_event_loop(WorkerProcessConfig *wpconf) { int rv; std::array errbuf; (void)errbuf; auto config = get_config(); if (reopen_log_files(config->logging) != 0) { LOG(FATAL) << "Failed to open log file"; return -1; } rv = ares_library_init(ARES_LIB_INIT_ALL); if (rv != 0) { LOG(FATAL) << "ares_library_init failed: " << ares_strerror(rv); return -1; } auto loop = EV_DEFAULT; auto gen = util::make_mt19937(); #ifdef HAVE_NEVERBLEED std::array nb_errbuf; auto nb = std::make_unique(); if (neverbleed_init(nb.get(), nb_errbuf.data()) != 0) { LOG(FATAL) << "neverbleed_init failed: " << nb_errbuf.data(); return -1; } LOG(NOTICE) << "neverbleed process [" << nb->daemon_pid << "] spawned"; ev_child nb_childev; ev_child_init(&nb_childev, nb_child_cb, nb->daemon_pid, 0); nb_childev.data = nullptr; ev_child_start(loop, &nb_childev); #endif // defined(HAVE_NEVERBLEED) auto conn_handler = std::make_unique(loop, gen); #ifdef HAVE_NEVERBLEED conn_handler->set_neverbleed(nb.get()); #endif // defined(HAVE_NEVERBLEED) #ifdef ENABLE_HTTP3 conn_handler->set_quic_ipc_fd(wpconf->quic_ipc_fd); conn_handler->set_quic_lingering_worker_processes( wpconf->quic_lingering_worker_processes); #endif // defined(ENABLE_HTTP3) MemchunkPool mcpool; ev_timer renew_ticket_key_timer; if (tls::upstream_tls_enabled(config->conn)) { auto &ticketconf = config->tls.ticket; auto &memcachedconf = ticketconf.memcached; if (!memcachedconf.host.empty()) { SSL_CTX *ssl_ctx = nullptr; if (memcachedconf.tls) { ssl_ctx = conn_handler->create_tls_ticket_key_memcached_ssl_ctx(); } conn_handler->set_tls_ticket_key_memcached_dispatcher( std::make_unique(&ticketconf.memcached.addr, loop, ssl_ctx, memcachedconf.host, &mcpool, gen)); ev_timer_init(&renew_ticket_key_timer, memcached_get_ticket_key_cb, 0., 0.); renew_ticket_key_timer.data = conn_handler.get(); // Get first ticket keys. memcached_get_ticket_key_cb(loop, &renew_ticket_key_timer, 0); } else { bool auto_tls_ticket_key = true; if (!ticketconf.files.empty()) { if (!ticketconf.cipher_given) { LOG(WARN) << "It is strongly recommended to specify " "--tls-ticket-key-cipher=aes-128-cbc (or " "tls-ticket-key-cipher=aes-128-cbc in configuration file) " "when --tls-ticket-key-file is used for the smooth " "transition when the default value of --tls-ticket-key-cipher " "becomes aes-256-cbc"; } auto ticket_keys = read_tls_ticket_key_file( ticketconf.files, ticketconf.cipher, EVP_sha256()); if (!ticket_keys) { LOG(WARN) << "Use internal session ticket key generator"; } else { conn_handler->set_ticket_keys(std::move(ticket_keys)); auto_tls_ticket_key = false; } } if (auto_tls_ticket_key) { // Generate new ticket key every 1hr. ev_timer_init(&renew_ticket_key_timer, renew_ticket_key_cb, 0., 1_h); renew_ticket_key_timer.data = conn_handler.get(); ev_timer_again(loop, &renew_ticket_key_timer); // Generate first session ticket key before running workers. renew_ticket_key_cb(loop, &renew_ticket_key_timer, 0); } } } #ifdef ENABLE_HTTP3 auto &quicconf = config->quic; std::shared_ptr qkms; if (!quicconf.upstream.secret_file.empty()) { qkms = read_quic_secret_file(quicconf.upstream.secret_file); if (!qkms) { LOG(WARN) << "Use QUIC keying materials generated internally"; } } if (!qkms) { qkms = std::make_shared(); qkms->keying_materials.resize(1); auto &qkm = qkms->keying_materials.front(); if (RAND_bytes(qkm.reserved.data(), static_cast( qkm.reserved.size())) != 1) { LOG(ERROR) << "Failed to generate QUIC secret reserved data"; return -1; } if (RAND_bytes(qkm.secret.data(), static_cast( qkm.secret.size())) != 1) { LOG(ERROR) << "Failed to generate QUIC secret"; return -1; } if (RAND_bytes(qkm.salt.data(), static_cast( qkm.salt.size())) != 1) { LOG(ERROR) << "Failed to generate QUIC salt"; return -1; } } for (auto &qkm : qkms->keying_materials) { if (generate_quic_connection_id_encryption_key(qkm.cid_encryption_key, qkm.secret, qkm.salt) != 0) { LOG(ERROR) << "Failed to generate QUIC Connection ID encryption key"; return -1; } qkm.cid_encryption_ctx = EVP_CIPHER_CTX_new(); if (!EVP_EncryptInit_ex(qkm.cid_encryption_ctx, EVP_aes_128_ecb(), nullptr, qkm.cid_encryption_key.data(), nullptr)) { LOG(ERROR) << "Failed to initialize QUIC Connection ID encryption context"; return -1; } EVP_CIPHER_CTX_set_padding(qkm.cid_encryption_ctx, 0); qkm.cid_decryption_ctx = EVP_CIPHER_CTX_new(); if (!EVP_DecryptInit_ex(qkm.cid_decryption_ctx, EVP_aes_128_ecb(), nullptr, qkm.cid_encryption_key.data(), nullptr)) { LOG(ERROR) << "Failed to initialize QUIC Connection ID decryption context"; return -1; } EVP_CIPHER_CTX_set_padding(qkm.cid_decryption_ctx, 0); } conn_handler->set_quic_keying_materials(std::move(qkms)); conn_handler->set_worker_ids(wpconf->worker_ids); conn_handler->set_quic_lingering_worker_processes( wpconf->quic_lingering_worker_processes); #endif // defined(ENABLE_HTTP3) if (config->single_thread) { rv = conn_handler->create_single_worker(); if (rv != 0) { return -1; } } else { #ifndef NOTHREADS sigset_t set; sigemptyset(&set); sigaddset(&set, SIGCHLD); rv = pthread_sigmask(SIG_BLOCK, &set, nullptr); if (rv != 0) { LOG(ERROR) << "Blocking SIGCHLD failed: " << xsi_strerror(rv, errbuf.data(), errbuf.size()); return -1; } #endif // !defined(NOTHREADS) rv = conn_handler->create_worker_thread(config->num_worker); if (rv != 0) { return -1; } #ifndef NOTHREADS rv = pthread_sigmask(SIG_UNBLOCK, &set, nullptr); if (rv != 0) { LOG(ERROR) << "Unblocking SIGCHLD failed: " << xsi_strerror(rv, errbuf.data(), errbuf.size()); return -1; } #endif // !defined(NOTHREADS) } // UNIX domain sockets are copied in AcceptHandler. No need to keep // the original. for (auto &addr : config->conn.listener.addrs) { if (addr.host_unix) { close(addr.fd); } } #if defined(ENABLE_HTTP3) && defined(HAVE_LIBBPF) conn_handler->unload_bpf_objects(); #endif // defined(ENABLE_HTTP3) && defined(HAVE_LIBBPF) drop_privileges( #ifdef HAVE_NEVERBLEED nb.get() #endif // defined(HAVE_NEVERBLEED) ); ev_io ipcev; ev_io_init(&ipcev, ipc_readcb, wpconf->ipc_fd, EV_READ); ipcev.data = conn_handler.get(); ev_io_start(loop, &ipcev); #ifdef ENABLE_HTTP3 ev_io quic_ipcev; ev_io_init(&quic_ipcev, quic_ipc_readcb, wpconf->quic_ipc_fd, EV_READ); quic_ipcev.data = conn_handler.get(); ev_io_start(loop, &quic_ipcev); #endif // defined(ENABLE_HTTP3) if (LOG_ENABLED(INFO)) { LOG(INFO) << "Entering event loop"; } if (send_ready_event(wpconf->ready_ipc_fd) != 0) { return -1; } ev_run(loop, 0); // Destroy SSL_CTX held in conn_handler before killing neverbleed // daemon. Otherwise priv_rsa_finish yields "write error" and // worker process aborts. conn_handler.reset(); #ifdef HAVE_NEVERBLEED assert(nb->daemon_pid > 0); rv = kill(nb->daemon_pid, SIGTERM); if (rv != 0) { auto error = errno; LOG(ERROR) << "Could not send signal to neverbleed daemon: errno=" << error; } while ((rv = waitpid(nb->daemon_pid, nullptr, 0)) == -1 && errno == EINTR) ; if (rv == -1) { auto error = errno; LOG(ERROR) << "Error occurred while we were waiting for the completion " "of neverbleed process: errno=" << error; } #endif // defined(HAVE_NEVERBLEED) ares_library_cleanup(); return 0; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_rate_limit.h0000644000000000000000000000013215077107270016575 xustar0030 mtime=1761382072.998444116 30 atime=1761382106.136310726 30 ctime=1761382109.132300289 nghttp2-1.68.0/src/shrpx_rate_limit.h0000644000175100017510000000441615077107270017172 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_RATE_LIMIT_H #define SHRPX_RATE_LIMIT_H #include "shrpx.h" #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) namespace shrpx { struct Connection; class RateLimit { public: // We need |conn| object to check that it has unread bytes for TLS // connection. RateLimit(struct ev_loop *loop, ev_io *w, size_t rate, size_t burst, Connection *conn = nullptr); ~RateLimit(); size_t avail() const; void drain(size_t n); void regen(); void startw(); void stopw(); // Feeds event if conn_->tls object has unread bytes. This is // required since it is buffered in conn_->tls object, io event is // not generated unless new incoming data is received. void handle_tls_pending_read(); private: ev_timer t_; ev_io *w_; struct ev_loop *loop_; Connection *conn_; size_t rate_; size_t burst_; size_t avail_; bool startw_req_; }; } // namespace shrpx #endif // !defined(SHRPX_RATE_LIMIT_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_http2_downstream_connection.h0000644000000000000000000000013215077107270022167 xustar0030 mtime=1761382072.994444134 30 atime=1761382106.104310867 30 ctime=1761382109.101300378 nghttp2-1.68.0/src/shrpx_http2_downstream_connection.h0000644000175100017510000000604115077107270022560 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_HTTP2_DOWNSTREAM_CONNECTION_H #define SHRPX_HTTP2_DOWNSTREAM_CONNECTION_H #include "shrpx.h" #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include #include "shrpx_downstream_connection.h" namespace shrpx { struct StreamData; class Http2Session; class DownstreamConnectionPool; class Http2DownstreamConnection : public DownstreamConnection { public: Http2DownstreamConnection(Http2Session *http2session); virtual ~Http2DownstreamConnection(); virtual int attach_downstream(Downstream *downstream); virtual void detach_downstream(Downstream *downstream); virtual int push_request_headers(); virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen); virtual int end_upload_data(); virtual void pause_read(IOCtrlReason reason) {} virtual int resume_read(IOCtrlReason reason, size_t consumed); virtual void force_resume_read() {} virtual int on_read(); virtual int on_write(); virtual int on_timeout(); virtual void on_upstream_change(Upstream *upstream) {} // This object is not poolable because we don't have facility to // migrate to another Http2Session object. virtual bool poolable() const { return false; } virtual const std::shared_ptr & get_downstream_addr_group() const; virtual DownstreamAddr *get_addr() const; int send(); void attach_stream_data(StreamData *sd); StreamData *detach_stream_data(); int submit_rst_stream(Downstream *downstream, uint32_t error_code = NGHTTP2_INTERNAL_ERROR); Http2DownstreamConnection *dlnext, *dlprev; private: Http2Session *http2session_; StreamData *sd_; }; } // namespace shrpx #endif // !defined(SHRPX_HTTP2_DOWNSTREAM_CONNECTION_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_http_downstream_connection.cc0000644000000000000000000000013115077107270022242 xustar0030 mtime=1761382072.995444129 30 atime=1761382106.101310881 29 ctime=1761382109.09730039 nghttp2-1.68.0/src/shrpx_http_downstream_connection.cc0000644000175100017510000012663515077107270022650 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_http_downstream_connection.h" #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include "shrpx_client_handler.h" #include "shrpx_upstream.h" #include "shrpx_downstream.h" #include "shrpx_config.h" #include "shrpx_error.h" #include "shrpx_http.h" #include "shrpx_log_config.h" #include "shrpx_connect_blocker.h" #include "shrpx_downstream_connection_pool.h" #include "shrpx_worker.h" #include "shrpx_http2_session.h" #include "shrpx_tls.h" #include "shrpx_log.h" #include "http2.h" #include "util.h" using namespace nghttp2; namespace shrpx { namespace { void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { auto conn = static_cast(w->data); auto dconn = static_cast(conn->data); if (w == &conn->rt && !conn->expired_rt()) { return; } if (LOG_ENABLED(INFO)) { DCLOG(INFO, dconn) << "Time out"; } auto downstream = dconn->get_downstream(); auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); auto &resp = downstream->response(); // Do this so that dconn is not pooled resp.connection_close = true; if (upstream->downstream_error(dconn, Downstream::EVENT_TIMEOUT) != 0) { delete handler; } } } // namespace namespace { void retry_downstream_connection(Downstream *downstream, unsigned int status_code) { auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); assert(!downstream->get_request_header_sent()); downstream->add_retry(); if (downstream->no_more_retry()) { delete handler; return; } downstream->pop_downstream_connection(); auto buf = downstream->get_request_buf(); buf->reset(); int rv; for (;;) { auto ndconn = handler->get_downstream_connection(rv, downstream); if (!ndconn) { break; } if (downstream->attach_downstream_connection(std::move(ndconn)) != 0) { continue; } if (downstream->push_request_headers() == 0) { return; } } downstream->set_request_state(DownstreamState::CONNECT_FAIL); if (rv == SHRPX_ERR_TLS_REQUIRED) { rv = upstream->on_downstream_abort_request_with_https_redirect(downstream); } else { rv = upstream->on_downstream_abort_request(downstream, status_code); } if (rv != 0) { delete handler; } } } // namespace namespace { void connect_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { auto conn = static_cast(w->data); auto dconn = static_cast(conn->data); auto addr = dconn->get_addr(); auto raddr = dconn->get_raddr(); DCLOG(WARN, dconn) << "Connect time out; addr=" << util::to_numeric_addr(raddr); downstream_failure(addr, raddr); auto downstream = dconn->get_downstream(); retry_downstream_connection(downstream, 504); } } // namespace namespace { void backend_retry(Downstream *downstream) { retry_downstream_connection(downstream, 502); } } // namespace namespace { void readcb(struct ev_loop *loop, ev_io *w, int revents) { int rv; auto conn = static_cast(w->data); auto dconn = static_cast(conn->data); auto downstream = dconn->get_downstream(); auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); rv = upstream->downstream_read(dconn); if (rv != 0) { if (rv == SHRPX_ERR_RETRY) { backend_retry(downstream); return; } delete handler; } } } // namespace namespace { void writecb(struct ev_loop *loop, ev_io *w, int revents) { int rv; auto conn = static_cast(w->data); auto dconn = static_cast(conn->data); auto downstream = dconn->get_downstream(); auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); rv = upstream->downstream_write(dconn); if (rv == SHRPX_ERR_RETRY) { backend_retry(downstream); return; } if (rv != 0) { delete handler; } } } // namespace namespace { void connectcb(struct ev_loop *loop, ev_io *w, int revents) { auto conn = static_cast(w->data); auto dconn = static_cast(conn->data); auto downstream = dconn->get_downstream(); if (dconn->connected() != 0) { backend_retry(downstream); return; } writecb(loop, w, revents); } } // namespace HttpDownstreamConnection::HttpDownstreamConnection( const std::shared_ptr &group, DownstreamAddr *addr, struct ev_loop *loop, Worker *worker) : conn_(loop, -1, nullptr, worker->get_mcpool(), group->shared_addr->timeout.write, group->shared_addr->timeout.read, {}, {}, connectcb, readcb, connect_timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold, get_config()->tls.dyn_rec.idle_timeout, Proto::HTTP1), on_read_(&HttpDownstreamConnection::noop), on_write_(&HttpDownstreamConnection::noop), signal_write_(&HttpDownstreamConnection::noop), worker_(worker), ssl_ctx_(worker->get_cl_ssl_ctx()), group_(group), addr_(addr), raddr_(nullptr), ioctrl_(&conn_.rlimit), response_htp_{0}, first_write_done_(false), reusable_(true), request_header_written_(false) {} HttpDownstreamConnection::~HttpDownstreamConnection() { if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "Deleted"; } if (dns_query_) { auto dns_tracker = worker_->get_dns_tracker(); dns_tracker->cancel(dns_query_.get()); } } int HttpDownstreamConnection::attach_downstream(Downstream *downstream) { int rv; if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream; } downstream_ = downstream; rv = initiate_connection(); if (rv != 0) { downstream_ = nullptr; return rv; } return 0; } namespace { int htp_msg_begincb(llhttp_t *htp); int htp_hdr_keycb(llhttp_t *htp, const char *data, size_t len); int htp_hdr_valcb(llhttp_t *htp, const char *data, size_t len); int htp_hdrs_completecb(llhttp_t *htp); int htp_bodycb(llhttp_t *htp, const char *data, size_t len); int htp_msg_completecb(llhttp_t *htp); } // namespace namespace { constexpr llhttp_settings_t htp_hooks = { .on_message_begin = htp_msg_begincb, .on_header_field = htp_hdr_keycb, .on_header_value = htp_hdr_valcb, .on_headers_complete = htp_hdrs_completecb, .on_body = htp_bodycb, .on_message_complete = htp_msg_completecb, }; } // namespace int HttpDownstreamConnection::initiate_connection() { int rv; auto worker_blocker = worker_->get_connect_blocker(); if (worker_blocker->blocked()) { if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "Worker wide backend connection was blocked temporarily"; } return SHRPX_ERR_NETWORK; } auto &downstreamconf = *worker_->get_downstream_config(); if (conn_.fd == -1) { auto check_dns_result = dns_query_.get() != nullptr; if (check_dns_result) { assert(addr_->dns); } auto &connect_blocker = addr_->connect_blocker; if (connect_blocker->blocked()) { if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "Backend server " << addr_->host << ":" << addr_->port << " was not available temporarily"; } return SHRPX_ERR_NETWORK; } Address *raddr; if (addr_->dns) { if (!check_dns_result) { auto dns_query = std::make_unique( addr_->host, [this](DNSResolverStatus status, const Address *result) { int rv; if (status == DNSResolverStatus::OK) { *this->resolved_addr_ = *result; } rv = this->initiate_connection(); if (rv != 0) { // This callback destroys |this|. auto downstream = this->downstream_; backend_retry(downstream); } }); auto dns_tracker = worker_->get_dns_tracker(); if (!resolved_addr_) { resolved_addr_ = std::make_unique
(); } switch (dns_tracker->resolve(resolved_addr_.get(), dns_query.get())) { case DNSResolverStatus::ERROR: downstream_failure(addr_, nullptr); return SHRPX_ERR_NETWORK; case DNSResolverStatus::RUNNING: dns_query_ = std::move(dns_query); return 0; case DNSResolverStatus::OK: break; default: assert(0); } } else { switch (dns_query_->status) { case DNSResolverStatus::ERROR: dns_query_.reset(); downstream_failure(addr_, nullptr); return SHRPX_ERR_NETWORK; case DNSResolverStatus::OK: dns_query_.reset(); break; default: assert(0); } } raddr = resolved_addr_.get(); util::set_port(*resolved_addr_, addr_->port); } else { raddr = &addr_->addr; } conn_.fd = util::create_nonblock_socket(raddr->su.storage.ss_family); if (conn_.fd == -1) { auto error = errno; DCLOG(WARN, this) << "socket() failed; addr=" << util::to_numeric_addr(raddr) << ", errno=" << error; worker_blocker->on_failure(); return SHRPX_ERR_NETWORK; } worker_blocker->on_success(); rv = connect(conn_.fd, &raddr->su.sa, raddr->len); if (rv != 0 && errno != EINPROGRESS) { auto error = errno; DCLOG(WARN, this) << "connect() failed; addr=" << util::to_numeric_addr(raddr) << ", errno=" << error; downstream_failure(addr_, raddr); return SHRPX_ERR_NETWORK; } if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "Connecting to downstream server"; } raddr_ = raddr; if (addr_->tls) { assert(ssl_ctx_); auto ssl = tls::create_ssl(ssl_ctx_); if (!ssl) { return -1; } tls::setup_downstream_http1_alpn(ssl); conn_.set_ssl(ssl); conn_.tls.client_session_cache = &addr_->tls_session_cache; auto sni_name = addr_->sni.empty() ? addr_->host : addr_->sni; if (!util::numeric_host(sni_name.data())) { SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name.data()); } auto session = tls::reuse_tls_session(addr_->tls_session_cache); if (session) { SSL_set_session(conn_.tls.ssl, session); SSL_SESSION_free(session); } conn_.prepare_client_handshake(); } ev_io_set(&conn_.wev, conn_.fd, EV_WRITE); ev_io_set(&conn_.rev, conn_.fd, EV_READ); conn_.wlimit.startw(); conn_.wt.repeat = downstreamconf.timeout.connect; ev_timer_again(conn_.loop, &conn_.wt); } else { // we may set read timer cb to idle_timeoutcb. Reset again. ev_set_cb(&conn_.rt, timeoutcb); if (conn_.read_timeout < group_->shared_addr->timeout.read) { conn_.read_timeout = group_->shared_addr->timeout.read; conn_.last_read = std::chrono::steady_clock::now(); } else { conn_.again_rt(group_->shared_addr->timeout.read); } ev_set_cb(&conn_.rev, readcb); on_write_ = &HttpDownstreamConnection::write_first; first_write_done_ = false; request_header_written_ = false; } llhttp_init(&response_htp_, HTTP_RESPONSE, &htp_hooks); response_htp_.data = downstream_; return 0; } int HttpDownstreamConnection::push_request_headers() { if (request_header_written_) { signal_write(); return 0; } const auto &req = downstream_->request(); auto &balloc = downstream_->get_block_allocator(); auto connect_method = req.regular_connect_method(); auto config = get_config(); auto &httpconf = config->http; request_header_written_ = true; // For HTTP/1.0 request, there is no authority in request. In that // case, we use backend server's host nonetheless. auto authority = addr_->hostport; auto no_host_rewrite = httpconf.no_host_rewrite || config->http2_proxy || connect_method; if (no_host_rewrite && !req.authority.empty()) { authority = req.authority; } downstream_->set_request_downstream_host(authority); auto buf = downstream_->get_request_buf(); // Assume that method and request path do not contain \r\n. auto meth = http2::to_method_string( req.connect_proto == ConnectProto::WEBSOCKET ? HTTP_GET : req.method); buf->append(meth); buf->append(' '); if (connect_method) { buf->append(authority); } else if (config->http2_proxy) { // Construct absolute-form request target because we are going to // send a request to a HTTP/1 proxy. assert(!req.scheme.empty()); buf->append(req.scheme); buf->append("://"sv); buf->append(authority); buf->append(req.path); } else if (req.method == HTTP_OPTIONS && req.path.empty()) { // Server-wide OPTIONS buf->append('*'); } else { buf->append(req.path); } buf->append(" HTTP/1.1\r\nHost: "sv); buf->append(authority); buf->append("\r\n"sv); auto &fwdconf = httpconf.forwarded; auto &xffconf = httpconf.xff; auto &xfpconf = httpconf.xfp; auto &earlydataconf = httpconf.early_data; uint32_t build_flags = (fwdconf.strip_incoming ? http2::HDOP_STRIP_FORWARDED : 0) | (xffconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_FOR : 0) | (xfpconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_PROTO : 0) | (earlydataconf.strip_incoming ? http2::HDOP_STRIP_EARLY_DATA : 0) | ((req.http_major == 3 || req.http_major == 2) ? http2::HDOP_STRIP_SEC_WEBSOCKET_KEY : 0); http2::build_http1_headers_from_headers(buf, req.fs.headers(), build_flags); auto cookie = downstream_->assemble_request_cookie(); if (!cookie.empty()) { buf->append("Cookie: "sv); buf->append(cookie); buf->append("\r\n"sv); } // set transfer-encoding only when content-length is unknown and // request body is expected. if (req.method != HTTP_CONNECT && req.http2_expect_body && req.fs.content_length == -1) { downstream_->set_chunked_request(true); buf->append("Transfer-Encoding: chunked\r\n"sv); } if (req.connect_proto == ConnectProto::WEBSOCKET) { if (req.http_major == 3 || req.http_major == 2) { std::array nonce; if (RAND_bytes(nonce.data(), nonce.size()) != 1) { return -1; } auto iov = make_byte_ref(balloc, base64::encode_length(nonce.size()) + 1); auto p = base64::encode(nonce, std::ranges::begin(iov)); *p = '\0'; auto key = as_string_view(std::ranges::begin(iov), p); downstream_->set_ws_key(key); buf->append("Sec-Websocket-Key: "sv); buf->append(key); buf->append("\r\n"sv); } buf->append("Upgrade: websocket\r\nConnection: Upgrade\r\n"sv); } else if (!connect_method && req.upgrade_request) { auto connection = req.fs.header(http2::HD_CONNECTION); if (connection) { buf->append("Connection: "sv); buf->append((*connection).value); buf->append("\r\n"sv); } auto upgrade = req.fs.header(http2::HD_UPGRADE); if (upgrade) { buf->append("Upgrade: "sv); buf->append((*upgrade).value); buf->append("\r\n"sv); } } else if (req.connection_close) { buf->append("Connection: close\r\n"sv); } auto upstream = downstream_->get_upstream(); auto handler = upstream->get_client_handler(); #if defined(NGHTTP2_GENUINE_OPENSSL) || \ defined(NGHTTP2_OPENSSL_IS_BORINGSSL) || defined(NGHTTP2_OPENSSL_IS_WOLFSSL) auto conn = handler->get_connection(); if (conn->tls.ssl && !SSL_is_init_finished(conn->tls.ssl)) { buf->append("Early-Data: 1\r\n"sv); } #endif // defined(NGHTTP2_GENUINE_OPENSSL) || // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) || // defined(NGHTTP2_OPENSSL_IS_WOLFSSL) auto fwd = fwdconf.strip_incoming ? nullptr : req.fs.header(http2::HD_FORWARDED); if (fwdconf.params) { auto params = fwdconf.params; if (config->http2_proxy || connect_method) { params &= static_cast(~FORWARDED_PROTO); } auto value = http::create_forwarded( balloc, params, handler->get_forwarded_by(), handler->get_forwarded_for(), req.authority, req.scheme); if (fwd || !value.empty()) { buf->append("Forwarded: "sv); if (fwd) { buf->append(fwd->value); if (!value.empty()) { buf->append(", "sv); } } buf->append(value); buf->append("\r\n"sv); } } else if (fwd) { buf->append("Forwarded: "sv); buf->append(fwd->value); buf->append("\r\n"sv); } auto xff = xffconf.strip_incoming ? nullptr : req.fs.header(http2::HD_X_FORWARDED_FOR); if (xffconf.add) { buf->append("X-Forwarded-For: "sv); if (xff) { buf->append((*xff).value); buf->append(", "sv); } buf->append(client_handler_->get_ipaddr()); buf->append("\r\n"sv); } else if (xff) { buf->append("X-Forwarded-For: "sv); buf->append((*xff).value); buf->append("\r\n"sv); } if (!config->http2_proxy && !connect_method) { auto xfp = xfpconf.strip_incoming ? nullptr : req.fs.header(http2::HD_X_FORWARDED_PROTO); if (xfpconf.add) { buf->append("X-Forwarded-Proto: "sv); if (xfp) { buf->append((*xfp).value); buf->append(", "sv); } assert(!req.scheme.empty()); buf->append(req.scheme); buf->append("\r\n"sv); } else if (xfp) { buf->append("X-Forwarded-Proto: "sv); buf->append((*xfp).value); buf->append("\r\n"sv); } } auto via = req.fs.header(http2::HD_VIA); if (httpconf.no_via) { if (via) { buf->append("Via: "sv); buf->append((*via).value); buf->append("\r\n"sv); } } else { buf->append("Via: "sv); if (via) { buf->append((*via).value); buf->append(", "sv); } buf->append(16, std::bind_front(http::ViaValueGenerator{}, req.http_major, req.http_minor)); buf->append("\r\n"sv); } for (auto &p : httpconf.add_request_headers) { buf->append(p.name); buf->append(": "sv); buf->append(p.value); buf->append("\r\n"sv); } buf->append("\r\n"sv); if (LOG_ENABLED(INFO)) { std::string nhdrs; for (auto chunk = buf->head; chunk; chunk = chunk->next) { nhdrs.append(chunk->pos, chunk->last); } if (log_config()->errorlog_tty) { nhdrs = http::colorize_headers(nhdrs); } DCLOG(INFO, this) << "HTTP request headers. stream_id=" << downstream_->get_stream_id() << "\n" << nhdrs; } // Don't call signal_write() if we anticipate request body. We call // signal_write() when we received request body chunk, and it // enables us to send headers and data in one writev system call. if (req.method == HTTP_CONNECT || downstream_->get_blocked_request_buf()->rleft() || (!req.http2_expect_body && req.fs.content_length == 0) || downstream_->get_expect_100_continue()) { signal_write(); } return 0; } int HttpDownstreamConnection::process_blocked_request_buf() { auto src = downstream_->get_blocked_request_buf(); if (src->rleft()) { auto dest = downstream_->get_request_buf(); auto chunked = downstream_->get_chunked_request(); if (chunked) { dest->append(sizeof(size_t) * 2, std::bind_front(util::CompactHexFormatter{}, src->rleft())); dest->append("\r\n"sv); } src->copy(*dest); if (chunked) { dest->append("\r\n"sv); } } if (downstream_->get_blocked_request_data_eof() && downstream_->get_chunked_request()) { end_upload_data_chunk(); } return 0; } int HttpDownstreamConnection::push_upload_data_chunk(const uint8_t *data, size_t datalen) { if (!downstream_->get_request_header_sent()) { auto output = downstream_->get_blocked_request_buf(); auto &req = downstream_->request(); output->append(data, datalen); req.unconsumed_body_length += datalen; if (request_header_written_) { signal_write(); } return 0; } auto chunked = downstream_->get_chunked_request(); auto output = downstream_->get_request_buf(); if (chunked) { output->append(sizeof(datalen) * 2, std::bind_front(util::CompactHexFormatter{}, datalen)); output->append("\r\n"sv); } output->append(data, datalen); if (chunked) { output->append("\r\n"sv); } signal_write(); return 0; } int HttpDownstreamConnection::end_upload_data() { if (!downstream_->get_request_header_sent()) { downstream_->set_blocked_request_data_eof(true); if (request_header_written_) { signal_write(); } return 0; } signal_write(); if (!downstream_->get_chunked_request()) { return 0; } end_upload_data_chunk(); return 0; } void HttpDownstreamConnection::end_upload_data_chunk() { const auto &req = downstream_->request(); auto output = downstream_->get_request_buf(); const auto &trailers = req.fs.trailers(); if (trailers.empty()) { output->append("0\r\n\r\n"sv); } else { output->append("0\r\n"sv); http2::build_http1_headers_from_headers(output, trailers, http2::HDOP_STRIP_ALL); output->append("\r\n"sv); } } namespace { void remove_from_pool(HttpDownstreamConnection *dconn) { auto addr = dconn->get_addr(); auto &dconn_pool = addr->dconn_pool; dconn_pool->remove_downstream_connection(dconn); } } // namespace namespace { void idle_readcb(struct ev_loop *loop, ev_io *w, int revents) { auto conn = static_cast(w->data); auto dconn = static_cast(conn->data); if (LOG_ENABLED(INFO)) { DCLOG(INFO, dconn) << "Idle connection EOF"; } remove_from_pool(dconn); // dconn was deleted } } // namespace namespace { void idle_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { auto conn = static_cast(w->data); auto dconn = static_cast(conn->data); if (w == &conn->rt && !conn->expired_rt()) { return; } if (LOG_ENABLED(INFO)) { DCLOG(INFO, dconn) << "Idle connection timeout"; } remove_from_pool(dconn); // dconn was deleted } } // namespace void HttpDownstreamConnection::detach_downstream(Downstream *downstream) { if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream; } downstream_ = nullptr; ev_set_cb(&conn_.rev, idle_readcb); ioctrl_.force_resume_read(); auto &downstreamconf = *worker_->get_downstream_config(); ev_set_cb(&conn_.rt, idle_timeoutcb); if (conn_.read_timeout < downstreamconf.timeout.idle_read) { conn_.read_timeout = downstreamconf.timeout.idle_read; conn_.last_read = std::chrono::steady_clock::now(); } else { conn_.again_rt(downstreamconf.timeout.idle_read); } conn_.wlimit.stopw(); ev_timer_stop(conn_.loop, &conn_.wt); } void HttpDownstreamConnection::pause_read(IOCtrlReason reason) { ioctrl_.pause_read(reason); } int HttpDownstreamConnection::resume_read(IOCtrlReason reason, size_t consumed) { auto &downstreamconf = *worker_->get_downstream_config(); if (downstream_->get_response_buf()->rleft() <= downstreamconf.request_buffer_size / 2) { ioctrl_.resume_read(reason); } return 0; } void HttpDownstreamConnection::force_resume_read() { ioctrl_.force_resume_read(); } namespace { int htp_msg_begincb(llhttp_t *htp) { auto downstream = static_cast(htp->data); if (downstream->get_response_state() != DownstreamState::INITIAL) { return -1; } return 0; } } // namespace namespace { int htp_hdrs_completecb(llhttp_t *htp) { auto downstream = static_cast(htp->data); auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); const auto &req = downstream->request(); auto &resp = downstream->response(); int rv; auto &balloc = downstream->get_block_allocator(); for (auto &kv : resp.fs.headers()) { kv.value = util::rstrip(balloc, kv.value); if (kv.token == http2::HD_TRANSFER_ENCODING && !http2::check_transfer_encoding(kv.value)) { return -1; } } auto config = get_config(); auto &loggingconf = config->logging; resp.http_status = htp->status_code; resp.http_major = htp->http_major; resp.http_minor = htp->http_minor; if (resp.http_major > 1 || req.http_minor > 1) { resp.http_major = 1; resp.http_minor = 1; return -1; } auto dconn = downstream->get_downstream_connection(); downstream->set_downstream_addr_group(dconn->get_downstream_addr_group()); downstream->set_addr(dconn->get_addr()); // Server MUST NOT send Transfer-Encoding with a status code 1xx or // 204. Also server MUST NOT send Transfer-Encoding with a status // code 2xx to a CONNECT request. Same holds true with // Content-Length. if (resp.http_status == 204) { if (resp.fs.header(http2::HD_TRANSFER_ENCODING)) { return -1; } // Some server send content-length: 0 for 204. Until they get // fixed, we accept, but ignore it. // Calling parse_content_length() detects duplicated // content-length header fields. if (resp.fs.parse_content_length() != 0) { return -1; } if (resp.fs.content_length == 0) { resp.fs.erase_content_length_and_transfer_encoding(); } else if (resp.fs.content_length != -1) { return -1; } } else if (resp.http_status / 100 == 1 || (resp.http_status / 100 == 2 && req.method == HTTP_CONNECT)) { // Server MUST NOT send Content-Length and Transfer-Encoding in // these responses. resp.fs.erase_content_length_and_transfer_encoding(); } else if (resp.fs.parse_content_length() != 0) { downstream->set_response_state(DownstreamState::MSG_BAD_HEADER); return -1; } // Check upgrade before processing non-final response, since if // upgrade succeeded, 101 response is treated as final in nghttpx. downstream->check_upgrade_fulfilled_http1(); if (downstream->get_non_final_response()) { // Reset content-length because we reuse same Downstream for the // next response. resp.fs.content_length = -1; // For non-final response code, we just call // on_downstream_header_complete() without changing response // state. rv = upstream->on_downstream_header_complete(downstream); if (rv != 0) { return -1; } // Ignore response body for non-final response. return 1; } resp.connection_close = !llhttp_should_keep_alive(htp); downstream->set_response_state(DownstreamState::HEADER_COMPLETE); downstream->inspect_http1_response(); if (htp->flags & F_CHUNKED) { downstream->set_chunked_response(true); } auto transfer_encoding = resp.fs.header(http2::HD_TRANSFER_ENCODING); if (transfer_encoding && !downstream->get_chunked_response()) { resp.connection_close = true; } if (downstream->get_upgraded()) { // content-length must be ignored for upgraded connection. resp.fs.content_length = -1; resp.connection_close = true; // transfer-encoding not applied to upgraded connection downstream->set_chunked_response(false); } else if (http2::legacy_http1(req.http_major, req.http_minor)) { if (resp.fs.content_length == -1) { resp.connection_close = true; } downstream->set_chunked_response(false); } else if (!downstream->expect_response_body()) { downstream->set_chunked_response(false); } if (loggingconf.access.write_early && downstream->accesslog_ready()) { handler->write_accesslog(downstream); downstream->set_accesslog_written(true); } if (upstream->on_downstream_header_complete(downstream) != 0) { return -1; } if (downstream->get_upgraded()) { // Upgrade complete, read until EOF in both ends if (upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0) != 0) { return -1; } downstream->set_request_state(DownstreamState::HEADER_COMPLETE); if (LOG_ENABLED(INFO)) { LOG(INFO) << "HTTP upgrade success. stream_id=" << downstream->get_stream_id(); } } // Ignore the response body. HEAD response may contain // Content-Length or Transfer-Encoding: chunked. Some server send // 304 status code with nonzero Content-Length, but without response // body. See // https://tools.ietf.org/html/rfc7230#section-3.3 // TODO It seems that the cases other than HEAD are handled by // llhttp. Need test. return !http2::expect_response_body(req.method, resp.http_status); } } // namespace namespace { int ensure_header_field_buffer(const Downstream *downstream, const HttpConfig &httpconf, size_t len) { auto &resp = downstream->response(); if (resp.fs.buffer_size() + len > httpconf.response_header_field_buffer) { if (LOG_ENABLED(INFO)) { DLOG(INFO, downstream) << "Too large header header field size=" << resp.fs.buffer_size() + len; } return -1; } return 0; } } // namespace namespace { int ensure_max_header_fields(const Downstream *downstream, const HttpConfig &httpconf) { auto &resp = downstream->response(); if (resp.fs.num_fields() >= httpconf.max_response_header_fields) { if (LOG_ENABLED(INFO)) { DLOG(INFO, downstream) << "Too many header field num=" << resp.fs.num_fields() + 1; } return -1; } return 0; } } // namespace namespace { int htp_hdr_keycb(llhttp_t *htp, const char *data, size_t len) { auto downstream = static_cast(htp->data); auto &resp = downstream->response(); auto &httpconf = get_config()->http; if (ensure_header_field_buffer(downstream, httpconf, len) != 0) { return -1; } auto name = std::string_view{data, len}; if (downstream->get_response_state() == DownstreamState::INITIAL) { if (resp.fs.header_key_prev()) { resp.fs.append_last_header_key(name); } else { if (ensure_max_header_fields(downstream, httpconf) != 0) { return -1; } resp.fs.alloc_add_header_name(name); } } else { // trailer part if (resp.fs.trailer_key_prev()) { resp.fs.append_last_trailer_key(name); } else { if (ensure_max_header_fields(downstream, httpconf) != 0) { // Could not ignore this trailer field easily, since we may // get its value in htp_hdr_valcb, and it will be added to // wrong place or crash if trailer fields are currently empty. return -1; } resp.fs.alloc_add_trailer_name(name); } } return 0; } } // namespace namespace { int htp_hdr_valcb(llhttp_t *htp, const char *data, size_t len) { auto downstream = static_cast(htp->data); auto &resp = downstream->response(); auto &httpconf = get_config()->http; if (ensure_header_field_buffer(downstream, httpconf, len) != 0) { return -1; } auto value = std::string_view{data, len}; if (downstream->get_response_state() == DownstreamState::INITIAL) { resp.fs.append_last_header_value(value); } else { resp.fs.append_last_trailer_value(value); } return 0; } } // namespace namespace { int htp_bodycb(llhttp_t *htp, const char *data, size_t len) { auto downstream = static_cast(htp->data); auto &resp = downstream->response(); resp.recv_body_length += len; return downstream->get_upstream()->on_downstream_body( downstream, reinterpret_cast(data), len, true); } } // namespace namespace { int htp_msg_completecb(llhttp_t *htp) { auto downstream = static_cast(htp->data); auto &resp = downstream->response(); auto &balloc = downstream->get_block_allocator(); for (auto &kv : resp.fs.trailers()) { kv.value = util::rstrip(balloc, kv.value); } // llhttp does not treat "200 connection established" response // against CONNECT request, and in that case, this function is not // called. But if HTTP Upgrade is made (e.g., WebSocket), this // function is called, and llhttp_execute() returns just after that. if (downstream->get_upgraded()) { return 0; } if (downstream->get_non_final_response()) { downstream->reset_response(); return 0; } downstream->set_response_state(DownstreamState::MSG_COMPLETE); // Block reading another response message from (broken?) // server. This callback is not called if the connection is // tunneled. downstream->pause_read(SHRPX_MSG_BLOCK); return downstream->get_upstream()->on_downstream_body_complete(downstream); } } // namespace int HttpDownstreamConnection::write_first() { int rv; process_blocked_request_buf(); if (conn_.tls.ssl) { rv = write_tls(); } else { rv = write_clear(); } if (rv != 0) { return SHRPX_ERR_RETRY; } if (conn_.tls.ssl) { on_write_ = &HttpDownstreamConnection::write_tls; } else { on_write_ = &HttpDownstreamConnection::write_clear; } first_write_done_ = true; downstream_->set_request_header_sent(true); auto buf = downstream_->get_blocked_request_buf(); buf->reset(); // upstream->resume_read() might be called in // write_tls()/write_clear(), but before blocked_request_buf_ is // reset. So upstream read might still be blocked. Let's do it // again here. auto input = downstream_->get_request_buf(); if (input->rleft() == 0) { auto upstream = downstream_->get_upstream(); auto &req = downstream_->request(); upstream->resume_read(SHRPX_NO_BUFFER, downstream_, req.unconsumed_body_length); } return 0; } int HttpDownstreamConnection::read_clear() { conn_.last_read = std::chrono::steady_clock::now(); std::array buf; int rv; for (;;) { auto nread = conn_.read_clear(buf.data(), buf.size()); if (nread == 0) { return 0; } if (nread < 0) { if (nread == SHRPX_ERR_EOF && !downstream_->get_upgraded()) { auto htperr = llhttp_finish(&response_htp_); if (htperr != HPE_OK) { if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "HTTP response ended prematurely: " << llhttp_errno_name(htperr); } return -1; } } return static_cast(nread); } rv = process_input(buf.data(), as_unsigned(nread)); if (rv != 0) { return rv; } if (!ev_is_active(&conn_.rev)) { return 0; } } } int HttpDownstreamConnection::write_clear() { conn_.last_read = std::chrono::steady_clock::now(); auto upstream = downstream_->get_upstream(); auto input = downstream_->get_request_buf(); std::array iov; while (input->rleft() > 0) { auto iovcnt = input->riovec(iov.data(), iov.size()); auto nwrite = conn_.writev_clear(iov.data(), iovcnt); if (nwrite == 0) { return 0; } if (nwrite < 0) { if (!first_write_done_) { return static_cast(nwrite); } // We may have pending data in receive buffer which may contain // part of response body. So keep reading. Invoke read event // to get read(2) error just in case. ev_feed_event(conn_.loop, &conn_.rev, EV_READ); on_write_ = &HttpDownstreamConnection::noop; reusable_ = false; break; } input->drain(as_unsigned(nwrite)); } conn_.wlimit.stopw(); ev_timer_stop(conn_.loop, &conn_.wt); if (input->rleft() == 0) { auto &req = downstream_->request(); upstream->resume_read(SHRPX_NO_BUFFER, downstream_, req.unconsumed_body_length); } return 0; } int HttpDownstreamConnection::tls_handshake() { ERR_clear_error(); conn_.last_read = std::chrono::steady_clock::now(); auto rv = conn_.tls_handshake(); if (rv == SHRPX_ERR_INPROGRESS) { return 0; } if (rv < 0) { downstream_failure(addr_, raddr_); return rv; } if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "SSL/TLS handshake completed"; } if (!get_config()->tls.insecure && tls::check_cert(conn_.tls.ssl, addr_, raddr_) != 0) { downstream_failure(addr_, raddr_); return -1; } auto &connect_blocker = addr_->connect_blocker; signal_write_ = &HttpDownstreamConnection::actual_signal_write; connect_blocker->on_success(); ev_set_cb(&conn_.rt, timeoutcb); ev_set_cb(&conn_.wt, timeoutcb); on_read_ = &HttpDownstreamConnection::read_tls; on_write_ = &HttpDownstreamConnection::write_first; // TODO Check negotiated ALPN return on_write(); } int HttpDownstreamConnection::read_tls() { conn_.last_read = std::chrono::steady_clock::now(); ERR_clear_error(); std::array buf; int rv; for (;;) { auto nread = conn_.read_tls(buf.data(), buf.size()); if (nread == 0) { return 0; } if (nread < 0) { if (nread == SHRPX_ERR_EOF && !downstream_->get_upgraded()) { auto htperr = llhttp_finish(&response_htp_); if (htperr != HPE_OK) { if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "HTTP response ended prematurely: " << llhttp_errno_name(htperr); } return -1; } } return static_cast(nread); } rv = process_input(buf.data(), as_unsigned(nread)); if (rv != 0) { return rv; } if (!ev_is_active(&conn_.rev)) { return 0; } } } int HttpDownstreamConnection::write_tls() { conn_.last_read = std::chrono::steady_clock::now(); ERR_clear_error(); auto upstream = downstream_->get_upstream(); auto input = downstream_->get_request_buf(); struct iovec iov; while (input->rleft() > 0) { auto iovcnt = input->riovec(&iov, 1); if (iovcnt != 1) { assert(0); return -1; } auto nwrite = conn_.write_tls(iov.iov_base, iov.iov_len); if (nwrite == 0) { return 0; } if (nwrite < 0) { if (!first_write_done_) { return static_cast(nwrite); } // We may have pending data in receive buffer which may contain // part of response body. So keep reading. Invoke read event // to get read(2) error just in case. ev_feed_event(conn_.loop, &conn_.rev, EV_READ); on_write_ = &HttpDownstreamConnection::noop; reusable_ = false; break; } input->drain(as_unsigned(nwrite)); } conn_.wlimit.stopw(); ev_timer_stop(conn_.loop, &conn_.wt); if (input->rleft() == 0) { auto &req = downstream_->request(); upstream->resume_read(SHRPX_NO_BUFFER, downstream_, req.unconsumed_body_length); } return 0; } int HttpDownstreamConnection::process_input(const uint8_t *data, size_t datalen) { int rv; if (downstream_->get_upgraded()) { // For upgraded connection, just pass data to the upstream. rv = downstream_->get_upstream()->on_downstream_body(downstream_, data, datalen, true); if (rv != 0) { return rv; } if (downstream_->response_buf_full()) { downstream_->pause_read(SHRPX_NO_BUFFER); return 0; } return 0; } auto htperr = llhttp_execute(&response_htp_, reinterpret_cast(data), datalen); auto nproc = htperr == HPE_OK ? datalen : static_cast(reinterpret_cast( llhttp_get_error_pos(&response_htp_)) - data); if (htperr != HPE_OK && (!downstream_->get_upgraded() || htperr != HPE_PAUSED_UPGRADE)) { // Handling early return (in other words, response was hijacked by // mruby scripting). if (downstream_->get_response_state() == DownstreamState::MSG_COMPLETE) { return SHRPX_ERR_DCONN_CANCELED; } if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "HTTP parser failure: " << "(" << llhttp_errno_name(htperr) << ") " << llhttp_get_error_reason(&response_htp_); } return -1; } if (downstream_->get_upgraded()) { if (nproc < datalen) { // Data from data + nproc are for upgraded protocol. rv = downstream_->get_upstream()->on_downstream_body( downstream_, data + nproc, datalen - nproc, true); if (rv != 0) { return rv; } if (downstream_->response_buf_full()) { downstream_->pause_read(SHRPX_NO_BUFFER); return 0; } } return 0; } if (downstream_->response_buf_full()) { downstream_->pause_read(SHRPX_NO_BUFFER); return 0; } return 0; } int HttpDownstreamConnection::connected() { auto &connect_blocker = addr_->connect_blocker; auto sock_error = util::get_socket_error(conn_.fd); if (sock_error != 0) { conn_.wlimit.stopw(); DCLOG(WARN, this) << "Backend connect failed; addr=" << util::to_numeric_addr(raddr_) << ": errno=" << sock_error; downstream_failure(addr_, raddr_); return -1; } if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "Connected to downstream host"; } // Reset timeout for write. Previously, we set timeout for connect. conn_.wt.repeat = group_->shared_addr->timeout.write; ev_timer_again(conn_.loop, &conn_.wt); conn_.rlimit.startw(); conn_.again_rt(); ev_set_cb(&conn_.wev, writecb); if (conn_.tls.ssl) { on_read_ = &HttpDownstreamConnection::tls_handshake; on_write_ = &HttpDownstreamConnection::tls_handshake; return 0; } signal_write_ = &HttpDownstreamConnection::actual_signal_write; connect_blocker->on_success(); ev_set_cb(&conn_.rt, timeoutcb); ev_set_cb(&conn_.wt, timeoutcb); on_read_ = &HttpDownstreamConnection::read_clear; on_write_ = &HttpDownstreamConnection::write_first; return 0; } int HttpDownstreamConnection::on_read() { return on_read_(*this); } int HttpDownstreamConnection::on_write() { return on_write_(*this); } void HttpDownstreamConnection::on_upstream_change(Upstream *upstream) {} void HttpDownstreamConnection::signal_write() { signal_write_(*this); } int HttpDownstreamConnection::actual_signal_write() { ev_feed_event(conn_.loop, &conn_.wev, EV_WRITE); return 0; } int HttpDownstreamConnection::noop() { return 0; } const std::shared_ptr & HttpDownstreamConnection::get_downstream_addr_group() const { return group_; } DownstreamAddr *HttpDownstreamConnection::get_addr() const { return addr_; } bool HttpDownstreamConnection::poolable() const { return !group_->retired && reusable_; } const Address *HttpDownstreamConnection::get_raddr() const { return raddr_; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/HtmlParser.h0000644000000000000000000000013215077107270015301 xustar0030 mtime=1761382072.983444185 30 atime=1761382106.236310286 30 ctime=1761382109.233299997 nghttp2-1.68.0/src/HtmlParser.h0000644000175100017510000000503415077107270015673 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef HTML_PARSER_H #define HTML_PARSER_H #include "nghttp2_config.h" #include #include #ifdef HAVE_LIBXML2 # include #endif // defined(HAVE_LIBXML2) namespace nghttp2 { enum ResourceType { REQ_CSS = 1, REQ_JS, REQ_UNBLOCK_JS, REQ_IMG, REQ_OTHERS, }; struct ParserData { std::string base_uri; std::vector> links; // > 0 if we are inside "head" element. int inside_head; ParserData(const std::string &base_uri); }; #ifdef HAVE_LIBXML2 class HtmlParser { public: HtmlParser(const std::string &base_uri); ~HtmlParser(); int parse_chunk(const char *chunk, size_t size, int fin); const std::vector> &get_links() const; void clear_links(); private: int parse_chunk_internal(const char *chunk, size_t size, int fin); std::string base_uri_; htmlParserCtxtPtr parser_ctx_; ParserData parser_data_; }; #else // !defined(HAVE_LIBXML2) class HtmlParser { public: HtmlParser(const std::string &base_uri) {} int parse_chunk(const char *chunk, size_t size, int fin) { return 0; } const std::vector> &get_links() const { return links_; } void clear_links() {} private: std::vector> links_; }; #endif // !defined(HAVE_LIBXML2) } // namespace nghttp2 #endif // !defined(HTML_PARSER_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_http_downstream_connection.h0000644000000000000000000000013215077107270022105 xustar0030 mtime=1761382072.995444129 30 atime=1761382106.102310876 30 ctime=1761382109.098300387 nghttp2-1.68.0/src/shrpx_http_downstream_connection.h0000644000175100017510000000754015077107270022503 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_HTTP_DOWNSTREAM_CONNECTION_H #define SHRPX_HTTP_DOWNSTREAM_CONNECTION_H #include "shrpx.h" #include "llhttp.h" #include "shrpx_downstream_connection.h" #include "shrpx_io_control.h" #include "shrpx_connection.h" namespace shrpx { class DownstreamConnectionPool; class Worker; struct DownstreamAddrGroup; struct DownstreamAddr; struct DNSQuery; class HttpDownstreamConnection : public DownstreamConnection { public: HttpDownstreamConnection(const std::shared_ptr &group, DownstreamAddr *addr, struct ev_loop *loop, Worker *worker); virtual ~HttpDownstreamConnection(); virtual int attach_downstream(Downstream *downstream); virtual void detach_downstream(Downstream *downstream); virtual int push_request_headers(); virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen); virtual int end_upload_data(); void end_upload_data_chunk(); virtual void pause_read(IOCtrlReason reason); virtual int resume_read(IOCtrlReason reason, size_t consumed); virtual void force_resume_read(); virtual int on_read(); virtual int on_write(); virtual void on_upstream_change(Upstream *upstream); virtual bool poolable() const; virtual const std::shared_ptr & get_downstream_addr_group() const; virtual DownstreamAddr *get_addr() const; int initiate_connection(); int write_first(); int read_clear(); int write_clear(); int read_tls(); int write_tls(); int process_input(const uint8_t *data, size_t datalen); int tls_handshake(); int connected(); void signal_write(); int actual_signal_write(); // Returns address used to connect to backend. Could be nullptr. const Address *get_raddr() const; int noop(); int process_blocked_request_buf(); private: Connection conn_; std::function on_read_, on_write_, signal_write_; Worker *worker_; // nullptr if TLS is not used. SSL_CTX *ssl_ctx_; std::shared_ptr group_; // Address of remote endpoint DownstreamAddr *addr_; // Actual remote address used to contact backend. This is initially // nullptr, and may point to either &addr_->addr, or // resolved_addr_.get(). const Address *raddr_; // Resolved IP address if dns parameter is used std::unique_ptr
resolved_addr_; std::unique_ptr dns_query_; IOControl ioctrl_; llhttp_t response_htp_; // true if first write succeeded. bool first_write_done_; // true if this object can be reused bool reusable_; // true if request header is written to request buffer. bool request_header_written_; }; } // namespace shrpx #endif // !defined(SHRPX_HTTP_DOWNSTREAM_CONNECTION_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_log.h0000644000000000000000000000013215077107270015225 xustar0030 mtime=1761382072.996444125 30 atime=1761382106.112310832 30 ctime=1761382109.109300355 nghttp2-1.68.0/src/shrpx_log.h0000644000175100017510000002211015077107270015611 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_LOG_H #define SHRPX_LOG_H #include "shrpx.h" #include #include #include #include #include #include #include "shrpx_log_config.h" #include "tls.h" #include "template.h" #include "util.h" using namespace nghttp2; #define ENABLE_LOG 1 #define LOG_ENABLED(SEVERITY) (ENABLE_LOG && shrpx::Log::log_enabled(SEVERITY)) #ifdef __FILE_NAME__ # define NGHTTP2_FILE_NAME __FILE_NAME__ #else // !defined(__FILE_NAME__) # define NGHTTP2_FILE_NAME __FILE__ #endif // !defined(__FILE_NAME__) #define LOG(SEVERITY) shrpx::Log(SEVERITY, NGHTTP2_FILE_NAME, __LINE__) // Listener log #define LLOG(SEVERITY, LISTEN) \ (shrpx::Log(SEVERITY, NGHTTP2_FILE_NAME, __LINE__) \ << "[LISTEN:" << LISTEN << "] ") // Worker log #define WLOG(SEVERITY, WORKER) \ (shrpx::Log(SEVERITY, NGHTTP2_FILE_NAME, __LINE__) \ << "[WORKER:" << WORKER << "] ") // ClientHandler log #define CLOG(SEVERITY, CLIENT_HANDLER) \ (shrpx::Log(SEVERITY, NGHTTP2_FILE_NAME, __LINE__) \ << "[CLIENT_HANDLER:" << CLIENT_HANDLER << "] ") // Upstream log #define ULOG(SEVERITY, UPSTREAM) \ (shrpx::Log(SEVERITY, NGHTTP2_FILE_NAME, __LINE__) \ << "[UPSTREAM:" << UPSTREAM << "] ") // Downstream log #define DLOG(SEVERITY, DOWNSTREAM) \ (shrpx::Log(SEVERITY, NGHTTP2_FILE_NAME, __LINE__) \ << "[DOWNSTREAM:" << DOWNSTREAM << "] ") // Downstream connection log #define DCLOG(SEVERITY, DCONN) \ (shrpx::Log(SEVERITY, NGHTTP2_FILE_NAME, __LINE__) \ << "[DCONN:" << DCONN << "] ") // Downstream HTTP2 session log #define SSLOG(SEVERITY, HTTP2) \ (shrpx::Log(SEVERITY, NGHTTP2_FILE_NAME, __LINE__) \ << "[DHTTP2:" << HTTP2 << "] ") // Memcached connection log #define MCLOG(SEVERITY, MCONN) \ (shrpx::Log(SEVERITY, NGHTTP2_FILE_NAME, __LINE__) \ << "[MCONN:" << MCONN << "] ") namespace shrpx { class Downstream; struct DownstreamAddr; struct LoggingConfig; enum SeverityLevel { INFO, NOTICE, WARN, ERROR, FATAL }; using LogBuffer = std::array; class Log { public: Log(int severity, const char *filename, int linenum); ~Log(); Log &operator<<(const std::string &s); Log &operator<<(const std::string_view &s); Log &operator<<(const char *s); Log &operator<<(const ImmutableString &s); template Log &operator<<(T n) { if (full_) { return *this; } if (n >= 0) { return *this << as_unsigned(n); } if (flags_ & fmt_hex) { write_hex(as_unsigned(n)); return *this; } if (wleft() < std::numeric_limits::digits10 + 1 + /* sign */ 1) { full_ = true; return *this; } *last_++ = '-'; last_ = util::utos( static_cast>(0u - as_unsigned(n)), last_); update_full(); return *this; } template Log &operator<<(T n) { if (full_) { return *this; } if (flags_ & fmt_hex) { write_hex(n); return *this; } if (wleft() < std::numeric_limits::digits10 + 1) { full_ = true; return *this; } last_ = util::utos(n, last_); update_full(); return *this; } Log &operator<<(float n) { return *this << static_cast(n); } Log &operator<<(double n); Log &operator<<(long double n); Log &operator<<(bool n); Log &operator<<(const void *p); template Log &operator<<(const std::shared_ptr &ptr) { return *this << ptr.get(); } Log &operator<<(void (*func)(Log &log)) { func(*this); return *this; } template requires(!std::is_array_v>) void write_seq(R &&r) { if (full_) { return; } auto n = std::min(wleft(), static_cast(std::ranges::distance(r))); last_ = std::ranges::copy(std::views::take(r, as_signed(n)), last_).out; update_full(); } template void write_hex(T n) { if (full_) { return; } if (wleft() < "0x"sv.size() + sizeof(T) * 2) { full_ = true; return; } *last_++ = '0'; *last_++ = 'x'; last_ = util::format_hex(n, last_); update_full(); } static void set_severity_level(int severity); // Returns the severity level by |name|. Returns -1 if |name| is // unknown. static int get_severity_level_by_name(const std::string_view &name); static bool log_enabled(int severity) { return severity >= severity_thres_; } enum { fmt_dec = 0x00, fmt_hex = 0x01, }; void set_flags(uint32_t flags) { flags_ = flags; } private: size_t rleft() { return as_unsigned(last_ - begin_); } size_t wleft() { return as_unsigned(end_ - last_ - /* terminal NUL or LF */ 1); } void update_full() { full_ = wleft() == 0; } LogBuffer &buf_; uint8_t *begin_; uint8_t *end_; uint8_t *last_; std::string_view filename_; uint32_t flags_; int severity_; int linenum_; bool full_; static int severity_thres_; }; namespace log { void hex(Log &log); void dec(Log &log); } // namespace log #define TTY_HTTP_HD (log_config()->errorlog_tty ? "\033[1;34m" : "") #define TTY_RST (log_config()->errorlog_tty ? "\033[0m" : "") enum class LogFragmentType { NONE, LITERAL, REMOTE_ADDR, TIME_LOCAL, TIME_ISO8601, REQUEST, STATUS, BODY_BYTES_SENT, HTTP, AUTHORITY, REMOTE_PORT, SERVER_PORT, REQUEST_TIME, PID, ALPN, TLS_CIPHER, SSL_CIPHER = TLS_CIPHER, TLS_PROTOCOL, SSL_PROTOCOL = TLS_PROTOCOL, TLS_SESSION_ID, SSL_SESSION_ID = TLS_SESSION_ID, TLS_SESSION_REUSED, SSL_SESSION_REUSED = TLS_SESSION_REUSED, TLS_SNI, TLS_CLIENT_FINGERPRINT_SHA1, TLS_CLIENT_FINGERPRINT_SHA256, TLS_CLIENT_ISSUER_NAME, TLS_CLIENT_SERIAL, TLS_CLIENT_SUBJECT_NAME, BACKEND_HOST, BACKEND_PORT, METHOD, PATH, PATH_WITHOUT_QUERY, PROTOCOL_VERSION, }; struct LogFragment { LogFragment(LogFragmentType type, std::string_view value = ""sv) : type(type), value(std::move(value)) {} LogFragmentType type; std::string_view value; }; struct LogSpec { Downstream *downstream; std::string_view remote_addr; std::string_view alpn; std::string_view sni; SSL *ssl; std::chrono::high_resolution_clock::time_point request_end_time; std::string_view remote_port; uint16_t server_port; pid_t pid; }; void upstream_accesslog(const std::vector &lf, const LogSpec &lgsp); int reopen_log_files(const LoggingConfig &loggingconf); // Logs message when process whose pid is |pid| and exist status is // |rstatus| exited. The |msg| is prepended to the log message. void log_chld(pid_t pid, int rstatus, const char *msg); void redirect_stderr_to_errorlog(const LoggingConfig &loggingconf); // Makes internal copy of stderr (and possibly stdout in the future), // which is then used as pointer to /dev/stderr or /proc/self/fd/2 void store_original_fds(); // Restores the original stderr that was stored with copy_original_fds // Used just before execv void restore_original_fds(); // Closes |fd| which was returned by open_log_file (see below) // and sets it to -1. In the case that |fd| points to stdout or // stderr, or is -1, the descriptor is not closed (but still set to -1). void close_log_file(int &fd); // Opens |path| with O_APPEND enabled. If file does not exist, it is // created first. This function returns file descriptor referring the // opened file if it succeeds, or -1. int open_log_file(const char *path); } // namespace shrpx #endif // !defined(SHRPX_LOG_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_dual_dns_resolver.h0000644000000000000000000000013215077107270020156 xustar0030 mtime=1761382072.993444139 30 atime=1761382106.168310586 30 ctime=1761382109.164300196 nghttp2-1.68.0/src/shrpx_dual_dns_resolver.h0000644000175100017510000000466515077107270020561 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_DUAL_DNS_RESOLVER_H #define SHRPX_DUAL_DNS_RESOLVER_H #include "shrpx.h" #include #include "shrpx_dns_resolver.h" using namespace nghttp2; namespace shrpx { // DualDNSResolver performs name resolution for both A and AAAA // records at the same time. The first successful return (or if we // have both successful results, prefer to AAAA) is chosen. This is // wrapper around 2 DNSResolver inside. resolve(), get_status(), and // how CompleteCb is called have the same semantics with DNSResolver. class DualDNSResolver { public: // |family| controls IP version preference. If |family| == // AF_UNSPEC, bot A and AAAA lookups are performed. If |family| == // AF_INET, only A lookup is performed. If |family| == AF_INET6, // only AAAA lookup is performed. DualDNSResolver(struct ev_loop *loop, int family); // Resolves |host|. |host| must be NULL-terminated string. int resolve(const std::string_view &host); CompleteCb get_complete_cb() const; void set_complete_cb(CompleteCb cb); DNSResolverStatus get_status(Address *result) const; private: // IP version preference. int family_; // For A record DNSResolver resolv4_; // For AAAA record DNSResolver resolv6_; CompleteCb complete_cb_; }; } // namespace shrpx #endif // !defined(SHRPX_DUAL_DNS_RESOLVER_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_connection_handler.h0000644000000000000000000000013215077107270020300 xustar0030 mtime=1761382072.992444143 30 atime=1761382106.085310951 30 ctime=1761382109.081300436 nghttp2-1.68.0/src/shrpx_connection_handler.h0000644000175100017510000002235715077107270020701 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_CONNECTION_HANDLER_H #define SHRPX_CONNECTION_HANDLER_H #include "shrpx.h" #include #ifdef HAVE_SYS_SOCKET_H # include #endif // defined(HAVE_SYS_SOCKET_H) #include #include #include #include #ifndef NOTHREADS # include #endif // !defined(NOTHREADS) #ifdef HAVE_LIBBPF # include #endif // defined(HAVE_LIBBPF) #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include #ifdef HAVE_NEVERBLEED # include #endif // defined(HAVE_NEVERBLEED) #include "shrpx_downstream_connection_pool.h" #include "shrpx_config.h" namespace shrpx { class Http2Session; class ConnectBlocker; class Worker; struct WorkerStat; struct TicketKeys; class MemcachedDispatcher; struct UpstreamAddr; namespace tls { class CertLookupTree; } // namespace tls // SerialEvent is an event sent from Worker thread. enum class SerialEventType { NONE, REPLACE_DOWNSTREAM, }; struct SerialEvent { // ctor for event uses DownstreamConfig SerialEvent(SerialEventType type, const std::shared_ptr &downstreamconf) : type(type), downstreamconf(downstreamconf) {} SerialEventType type; std::shared_ptr downstreamconf; }; #ifdef ENABLE_HTTP3 # ifdef HAVE_LIBBPF struct BPFRef { bpf_object *obj; bpf_map *reuseport_array; bpf_map *worker_id_map; }; # endif // defined(HAVE_LIBBPF) // QUIC IPC message type. enum class QUICIPCType { NONE, // Send forwarded QUIC UDP datagram and its metadata. DGRAM_FORWARD, }; // WorkerProcesses which are in graceful shutdown period. struct QUICLingeringWorkerProcess { QUICLingeringWorkerProcess(std::vector worker_ids, int quic_ipc_fd) : worker_ids{std::move(worker_ids)}, quic_ipc_fd{quic_ipc_fd} {} std::vector worker_ids; // Socket to send QUIC IPC message to this worker process. int quic_ipc_fd; }; #endif // defined(ENABLE_HTTP3) class ConnectionHandler { public: ConnectionHandler(struct ev_loop *loop, std::mt19937 &gen); ~ConnectionHandler(); // Creates Worker object for single threaded configuration. int create_single_worker(); // Creates |num| Worker objects for multi threaded configuration. // The |num| must be strictly more than 1. int create_worker_thread(size_t num); void set_ticket_keys_to_worker(const std::shared_ptr &ticket_keys); void worker_reopen_log_files(); void set_ticket_keys(std::shared_ptr ticket_keys); const std::shared_ptr &get_ticket_keys() const; struct ev_loop *get_loop() const; Worker *get_single_worker() const; void graceful_shutdown_worker(); void set_graceful_shutdown(bool f); bool get_graceful_shutdown() const; void join_worker(); void set_tls_ticket_key_memcached_dispatcher( std::unique_ptr dispatcher); MemcachedDispatcher *get_tls_ticket_key_memcached_dispatcher() const; void on_tls_ticket_key_network_error(ev_timer *w); void on_tls_ticket_key_not_found(ev_timer *w); void on_tls_ticket_key_get_success(const std::shared_ptr &ticket_keys, ev_timer *w); void schedule_next_tls_ticket_key_memcached_get(ev_timer *w); SSL_CTX *create_tls_ticket_key_memcached_ssl_ctx(); // Returns the SSL_CTX at all_ssl_ctx_[idx]. This does not perform // array bound checking. SSL_CTX *get_ssl_ctx(size_t idx) const; const std::vector &get_indexed_ssl_ctx(size_t idx) const; #ifdef ENABLE_HTTP3 const std::vector &get_quic_indexed_ssl_ctx(size_t idx) const; int forward_quic_packet(const UpstreamAddr *faddr, const Address &remote_addr, const Address &local_addr, const ngtcp2_pkt_info &pi, const WorkerID &wid, std::span data); void set_quic_keying_materials(std::shared_ptr qkms); const std::shared_ptr &get_quic_keying_materials() const; void set_worker_ids(std::vector worker_ids); Worker *find_worker(const WorkerID &wid) const; void set_quic_lingering_worker_processes( const std::vector &quic_lwps); // Return matching QUICLingeringWorkerProcess which has a Worker ID // such that |dcid| starts with it. If no such // QUICLingeringWorkerProcess, it returns nullptr. QUICLingeringWorkerProcess * match_quic_lingering_worker_process_worker_id(const WorkerID &wid); int forward_quic_packet_to_lingering_worker_process( QUICLingeringWorkerProcess *quic_lwp, const Address &remote_addr, const Address &local_addr, const ngtcp2_pkt_info &pi, std::span data); void set_quic_ipc_fd(int fd); int quic_ipc_read(); # ifdef HAVE_LIBBPF std::vector &get_quic_bpf_refs(); void unload_bpf_objects(); # endif // defined(HAVE_LIBBPF) #endif // defined(ENABLE_HTTP3) #ifdef HAVE_NEVERBLEED void set_neverbleed(neverbleed_t *nb); #endif // defined(HAVE_NEVERBLEED) // Send SerialEvent SerialEventType::REPLACE_DOWNSTREAM to this // object. void send_replace_downstream( const std::shared_ptr &downstreamconf); // Internal function to send |ev| to this object. void send_serial_event(SerialEvent ev); // Handles SerialEvents received. void handle_serial_event(); // Sends WorkerEvent to make them replace downstream. void worker_replace_downstream(std::shared_ptr downstreamconf); private: // Stores all SSL_CTX objects. std::vector all_ssl_ctx_; // Stores all SSL_CTX objects in a way that its index is stored in // cert_tree. The SSL_CTXs stored in the same index share the same // hostname, but could have different signature algorithm. The // selection among them are performed by hostname presented by SNI, // and signature algorithm presented by client. std::vector> indexed_ssl_ctx_; #ifdef ENABLE_HTTP3 std::vector worker_ids_; std::vector lingering_worker_ids_; int quic_ipc_fd_; std::vector quic_lingering_worker_processes_; # ifdef HAVE_LIBBPF std::vector quic_bpf_refs_; # endif // defined(HAVE_LIBBPF) std::shared_ptr quic_keying_materials_; std::vector quic_all_ssl_ctx_; std::vector> quic_indexed_ssl_ctx_; #endif // defined(ENABLE_HTTP3) std::mt19937 &gen_; // ev_loop for each worker std::vector worker_loops_; // Worker instances when multi threaded mode (-nN, N >= 2) is used. // If at least one frontend enables API request, we allocate 1 // additional worker dedicated to API request . std::vector> workers_; // mutex for serial event resive buffer handling std::mutex serial_event_mu_; // SerialEvent receive buffer std::vector serial_events_; // Worker instance used when single threaded mode (-n1) is used. // Otherwise, nullptr and workers_ has instances of Worker instead. std::unique_ptr single_worker_; std::unique_ptr cert_tree_; #ifdef ENABLE_HTTP3 std::unique_ptr quic_cert_tree_; #endif // defined(ENABLE_HTTP3) std::unique_ptr tls_ticket_key_memcached_dispatcher_; // Current TLS session ticket keys. Note that TLS connection does // not refer to this field directly. They use TicketKeys object in // Worker object. std::shared_ptr ticket_keys_; struct ev_loop *loop_; #ifdef HAVE_NEVERBLEED neverbleed_t *nb_; #endif // defined(HAVE_NEVERBLEED) ev_async thread_join_asyncev_; ev_async serial_event_asyncev_; #ifndef NOTHREADS std::future thread_join_fut_; #endif // defined(NOTHREADS) size_t tls_ticket_key_memcached_get_retry_count_; size_t tls_ticket_key_memcached_fail_count_; unsigned int worker_round_robin_cnt_; bool graceful_shutdown_; }; } // namespace shrpx #endif // !defined(SHRPX_CONNECTION_HANDLER_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_dns_resolver.cc0000644000000000000000000000013215077107270017307 xustar0030 mtime=1761382072.992444143 30 atime=1761382106.164310603 30 ctime=1761382109.160300208 nghttp2-1.68.0/src/shrpx_dns_resolver.cc0000644000175100017510000002115415077107270017702 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_dns_resolver.h" #include #include #include "shrpx_log.h" #include "shrpx_connection.h" #include "shrpx_config.h" namespace shrpx { namespace { void sock_state_cb(void *data, int s, int read, int write) { auto resolv = static_cast(data); if (resolv->get_status(nullptr) != DNSResolverStatus::RUNNING) { return; } if (read) { resolv->start_rev(s); } else { resolv->stop_rev(s); } if (write) { resolv->start_wev(s); } else { resolv->stop_wev(s); } } } // namespace namespace { void addrinfo_cb(void *arg, int status, int timeouts, ares_addrinfo *result) { auto resolv = static_cast(arg); resolv->on_result(status, result); ares_freeaddrinfo(result); } } // namespace namespace { void process_result(DNSResolver *resolv) { auto cb = resolv->get_complete_cb(); if (!cb) { return; } Address result; auto status = resolv->get_status(&result); switch (status) { case DNSResolverStatus::OK: case DNSResolverStatus::ERROR: cb(status, &result); break; default: break; } // resolv may be deleted here. } } // namespace namespace { void readcb(struct ev_loop *loop, ev_io *w, int revents) { auto resolv = static_cast(w->data); resolv->on_read(w->fd); process_result(resolv); } } // namespace namespace { void writecb(struct ev_loop *loop, ev_io *w, int revents) { auto resolv = static_cast(w->data); resolv->on_write(w->fd); process_result(resolv); } } // namespace namespace { void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { auto resolv = static_cast(w->data); resolv->on_timeout(); process_result(resolv); } } // namespace namespace { void stop_ev(struct ev_loop *loop, const std::vector> &evs) { for (auto &w : evs) { ev_io_stop(loop, w.get()); } } } // namespace DNSResolver::DNSResolver(struct ev_loop *loop) : result_{}, loop_(loop), channel_(nullptr), family_(AF_UNSPEC), status_(DNSResolverStatus::IDLE) { ev_timer_init(&timer_, timeoutcb, 0., 0.); timer_.data = this; } DNSResolver::~DNSResolver() { if (channel_) { ares_destroy(channel_); } stop_ev(loop_, revs_); stop_ev(loop_, wevs_); ev_timer_stop(loop_, &timer_); } int DNSResolver::resolve(const std::string_view &name, int family) { if (status_ != DNSResolverStatus::IDLE) { return -1; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "Start resolving host " << name << " in IPv" << (family == AF_INET ? "4" : "6"); } name_ = name; family_ = family; int rv; auto &dnsconf = get_config()->dns; ares_options opts{ .timeout = static_cast(dnsconf.timeout.lookup * 1000), .tries = static_cast(dnsconf.max_try), .sock_state_cb = sock_state_cb, .sock_state_cb_data = this, }; auto optmask = ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES; ares_channel chan; rv = ares_init_options(&chan, &opts, optmask); if (rv != ARES_SUCCESS) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "ares_init_options failed: " << ares_strerror(rv); } status_ = DNSResolverStatus::ERROR; return -1; } channel_ = chan; status_ = DNSResolverStatus::RUNNING; ares_addrinfo_hints hints{ .ai_family = family_, }; ares_getaddrinfo(channel_, name_.data(), nullptr, &hints, addrinfo_cb, this); reset_timeout(); return 0; } int DNSResolver::on_read(int fd) { return handle_event(fd, ARES_SOCKET_BAD); } int DNSResolver::on_write(int fd) { return handle_event(ARES_SOCKET_BAD, fd); } int DNSResolver::on_timeout() { return handle_event(ARES_SOCKET_BAD, ARES_SOCKET_BAD); } int DNSResolver::handle_event(int rfd, int wfd) { if (status_ == DNSResolverStatus::IDLE) { return -1; } ares_process_fd(channel_, rfd, wfd); switch (status_) { case DNSResolverStatus::RUNNING: reset_timeout(); return 0; case DNSResolverStatus::OK: return 0; case DNSResolverStatus::ERROR: return -1; default: // Unreachable assert(0); abort(); } } void DNSResolver::reset_timeout() { if (status_ != DNSResolverStatus::RUNNING) { return; } timeval tvout; auto tv = ares_timeout(channel_, nullptr, &tvout); if (tv == nullptr) { return; } // To avoid that timer_.repeat becomes 0, which makes ev_timer_again // useless, add tiny fraction of time. timer_.repeat = static_cast(tv->tv_sec) + static_cast(tv->tv_usec) / 1000000. + 1e-9; ev_timer_again(loop_, &timer_); } DNSResolverStatus DNSResolver::get_status(Address *result) const { if (status_ != DNSResolverStatus::OK) { return status_; } if (result) { memcpy(result, &result_, sizeof(result_)); } return status_; } namespace { void start_ev(std::vector> &evs, struct ev_loop *loop, int fd, int event, IOCb cb, void *data) { for (auto &w : evs) { if (w->fd == fd) { return; } } for (auto &w : evs) { if (w->fd == -1) { ev_io_set(w.get(), fd, event); ev_io_start(loop, w.get()); return; } } auto w = std::make_unique(); ev_io_init(w.get(), cb, fd, event); w->data = data; ev_io_start(loop, w.get()); evs.emplace_back(std::move(w)); } } // namespace namespace { void stop_ev(std::vector> &evs, struct ev_loop *loop, int fd, int event) { for (auto &w : evs) { if (w->fd == fd) { ev_io_stop(loop, w.get()); ev_io_set(w.get(), -1, event); return; } } } } // namespace void DNSResolver::start_rev(int fd) { start_ev(revs_, loop_, fd, EV_READ, readcb, this); } void DNSResolver::stop_rev(int fd) { stop_ev(revs_, loop_, fd, EV_READ); } void DNSResolver::start_wev(int fd) { start_ev(wevs_, loop_, fd, EV_WRITE, writecb, this); } void DNSResolver::stop_wev(int fd) { stop_ev(wevs_, loop_, fd, EV_WRITE); } void DNSResolver::on_result(int status, ares_addrinfo *ai) { stop_ev(loop_, revs_); stop_ev(loop_, wevs_); ev_timer_stop(loop_, &timer_); if (status != ARES_SUCCESS) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "Name lookup for " << name_ << " failed: " << ares_strerror(status); } status_ = DNSResolverStatus::ERROR; return; } auto ap = ai->nodes; for (; ap; ap = ap->ai_next) { switch (ap->ai_family) { case AF_INET: status_ = DNSResolverStatus::OK; result_.len = sizeof(result_.su.in); assert(sizeof(result_.su.in) == ap->ai_addrlen); memcpy(&result_.su.in, ap->ai_addr, sizeof(result_.su.in)); break; case AF_INET6: status_ = DNSResolverStatus::OK; result_.len = sizeof(result_.su.in6); assert(sizeof(result_.su.in6) == ap->ai_addrlen); memcpy(&result_.su.in6, ap->ai_addr, sizeof(result_.su.in6)); break; default: continue; } break; } if (!ap) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "Name lookup for " << name_ << " failed: no address returned"; } status_ = DNSResolverStatus::ERROR; return; } if (status_ == DNSResolverStatus::OK) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "Name lookup succeeded: " << name_ << " -> " << util::numeric_name(&result_.su.sa, result_.len); } return; } status_ = DNSResolverStatus::ERROR; } void DNSResolver::set_complete_cb(CompleteCb cb) { completeCb_ = std::move(cb); } CompleteCb DNSResolver::get_complete_cb() const { return completeCb_; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_api_downstream_connection.cc0000644000000000000000000000013215077107270022035 xustar0030 mtime=1761382072.990444153 30 atime=1761382106.156310638 30 ctime=1761382109.152300231 nghttp2-1.68.0/src/shrpx_api_downstream_connection.cc0000644000175100017510000003022015077107270022422 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_api_downstream_connection.h" #include #include #include #include #include "shrpx_client_handler.h" #include "shrpx_upstream.h" #include "shrpx_downstream.h" #include "shrpx_worker.h" #include "shrpx_connection_handler.h" #include "shrpx_log.h" namespace shrpx { namespace { const auto backendconfig_endpoint = APIEndpoint{ "/api/v1beta1/backendconfig"sv, true, (1 << API_METHOD_POST) | (1 << API_METHOD_PUT), &APIDownstreamConnection::handle_backendconfig, }; const auto configrevision_endpoint = APIEndpoint{ "/api/v1beta1/configrevision"sv, true, (1 << API_METHOD_GET), &APIDownstreamConnection::handle_configrevision, }; } // namespace namespace { // The method string. This must be same order of APIMethod. constexpr std::string_view API_METHOD_STRING[] = { "GET"sv, "POST"sv, "PUT"sv, }; } // namespace APIDownstreamConnection::APIDownstreamConnection(Worker *worker) : worker_(worker), api_(nullptr), fd_(-1), shutdown_read_(false) {} APIDownstreamConnection::~APIDownstreamConnection() { if (fd_ != -1) { close(fd_); } } int APIDownstreamConnection::attach_downstream(Downstream *downstream) { if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream; } downstream_ = downstream; return 0; } void APIDownstreamConnection::detach_downstream(Downstream *downstream) { if (LOG_ENABLED(INFO)) { DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream; } downstream_ = nullptr; } int APIDownstreamConnection::send_reply(unsigned int http_status, APIStatusCode api_status, const std::string_view &data) { shutdown_read_ = true; auto upstream = downstream_->get_upstream(); auto &resp = downstream_->response(); resp.http_status = http_status; auto &balloc = downstream_->get_block_allocator(); std::string_view api_status_str; switch (api_status) { case APIStatusCode::SUCCESS: api_status_str = "Success"sv; break; case APIStatusCode::FAILURE: api_status_str = "Failure"sv; break; default: assert(0); } static constexpr auto M1 = "{\"status\":\""sv; static constexpr auto M2 = "\",\"code\":"sv; static constexpr auto M3 = "}"sv; // 3 is the number of digits in http_status, assuming it is 3 digits // number. auto buflen = M1.size() + M2.size() + M3.size() + data.size() + api_status_str.size() + 3; auto buf = make_byte_ref(balloc, buflen); auto p = std::ranges::begin(buf); p = std::ranges::copy(M1, p).out; p = std::ranges::copy(api_status_str, p).out; p = std::ranges::copy(M2, p).out; p = util::utos(http_status, p); p = std::ranges::copy(data, p).out; p = std::ranges::copy(M3, p).out; buf = buf.first(as_unsigned(p - std::ranges::begin(buf))); auto content_length = util::make_string_ref_uint(balloc, buf.size()); resp.fs.add_header_token("content-length"sv, content_length, false, http2::HD_CONTENT_LENGTH); switch (http_status) { case 400: case 405: case 413: resp.fs.add_header_token("connection"sv, "close"sv, false, http2::HD_CONNECTION); break; } if (upstream->send_reply(downstream_, buf.data(), buf.size()) != 0) { return -1; } return 0; } namespace { const APIEndpoint *lookup_api(const std::string_view &path) { switch (path.size()) { case 26: switch (path[25]) { case 'g': if (util::streq("/api/v1beta1/backendconfi"sv, path.substr(0, 25))) { return &backendconfig_endpoint; } break; } break; case 27: switch (path[26]) { case 'n': if (util::streq("/api/v1beta1/configrevisio"sv, path.substr(0, 26))) { return &configrevision_endpoint; } break; } break; } return nullptr; } } // namespace int APIDownstreamConnection::push_request_headers() { auto &req = downstream_->request(); auto path = std::string_view{std::ranges::begin(req.path), std::ranges::find(req.path, '?')}; api_ = lookup_api(path); if (!api_) { send_reply(404, APIStatusCode::FAILURE); return 0; } switch (req.method) { case HTTP_GET: if (!(api_->allowed_methods & (1 << API_METHOD_GET))) { error_method_not_allowed(); return 0; } break; case HTTP_POST: if (!(api_->allowed_methods & (1 << API_METHOD_POST))) { error_method_not_allowed(); return 0; } break; case HTTP_PUT: if (!(api_->allowed_methods & (1 << API_METHOD_PUT))) { error_method_not_allowed(); return 0; } break; default: error_method_not_allowed(); return 0; } // This works with req.fs.content_length == -1 if (req.fs.content_length > static_cast(get_config()->api.max_request_body)) { send_reply(413, APIStatusCode::FAILURE); return 0; } switch (req.method) { case HTTP_POST: case HTTP_PUT: { char tempname[] = "/tmp/nghttpx-api.XXXXXX"; #ifdef HAVE_MKOSTEMP fd_ = mkostemp(tempname, O_CLOEXEC); #else // !defined(HAVE_MKOSTEMP) fd_ = mkstemp(tempname); #endif // !defined(HAVE_MKOSTEMP) if (fd_ == -1) { send_reply(500, APIStatusCode::FAILURE); return 0; } #ifndef HAVE_MKOSTEMP util::make_socket_closeonexec(fd_); #endif // !defined(HAVE_MKOSTEMP) unlink(tempname); break; } } downstream_->set_request_header_sent(true); auto src = downstream_->get_blocked_request_buf(); auto dest = downstream_->get_request_buf(); src->remove(*dest); return 0; } int APIDownstreamConnection::error_method_not_allowed() { auto &resp = downstream_->response(); size_t len = 0; for (uint8_t i = 0; i < API_METHOD_MAX; ++i) { if (api_->allowed_methods & (1 << i)) { // The length of method + ", " len += API_METHOD_STRING[i].size() + 2; } } assert(len > 0); auto &balloc = downstream_->get_block_allocator(); auto iov = make_byte_ref(balloc, len + 1); auto p = std::ranges::begin(iov); for (uint8_t i = 0; i < API_METHOD_MAX; ++i) { if (api_->allowed_methods & (1 << i)) { auto &s = API_METHOD_STRING[i]; p = std::ranges::copy(s, p).out; p = std::ranges::copy(", "sv, p).out; } } p -= 2; *p = '\0'; resp.fs.add_header_token( "allow"sv, as_string_view(std::ranges::begin(iov), p), false, -1); return send_reply(405, APIStatusCode::FAILURE); } int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data, size_t datalen) { if (shutdown_read_ || !api_->require_body) { return 0; } auto &req = downstream_->request(); auto &apiconf = get_config()->api; if (static_cast(req.recv_body_length) > apiconf.max_request_body) { send_reply(413, APIStatusCode::FAILURE); return 0; } ssize_t nwrite; while ((nwrite = write(fd_, data, datalen)) == -1 && errno == EINTR) ; if (nwrite == -1) { auto error = errno; LOG(ERROR) << "Could not write API request body: errno=" << error; send_reply(500, APIStatusCode::FAILURE); return 0; } // We don't have to call Upstream::resume_read() here, because // request buffer is effectively unlimited. Actually, we cannot // call it here since it could recursively call this function again. return 0; } int APIDownstreamConnection::end_upload_data() { if (shutdown_read_) { return 0; } return api_->handler(*this); } int APIDownstreamConnection::handle_backendconfig() { auto &req = downstream_->request(); if (req.recv_body_length == 0) { send_reply(200, APIStatusCode::SUCCESS); return 0; } auto rp = mmap(nullptr, static_cast(req.recv_body_length), PROT_READ, MAP_SHARED, fd_, 0); if (rp == reinterpret_cast(-1)) { send_reply(500, APIStatusCode::FAILURE); return 0; } auto unmapper = defer(munmap, rp, req.recv_body_length); Config new_config{}; new_config.conn.downstream = std::make_shared(); const auto &downstreamconf = new_config.conn.downstream; auto config = get_config(); auto &src = config->conn.downstream; downstreamconf->timeout = src->timeout; downstreamconf->connections_per_host = src->connections_per_host; downstreamconf->connections_per_frontend = src->connections_per_frontend; downstreamconf->request_buffer_size = src->request_buffer_size; downstreamconf->response_buffer_size = src->response_buffer_size; downstreamconf->family = src->family; std::unordered_set include_set; std::unordered_map pattern_addr_indexer; for (auto first = reinterpret_cast(rp), last = first + req.recv_body_length; first != last;) { auto eol = std::ranges::find(first, last, '\n'); if (eol == last) { break; } if (first == eol || *first == '#') { first = ++eol; continue; } auto eq = std::ranges::find(first, eol, '='); if (eq == eol) { send_reply(400, APIStatusCode::FAILURE); return 0; } auto opt = std::string_view{first, eq}; auto optval = std::string_view{eq + 1, eol}; auto optid = option_lookup_token(opt); switch (optid) { case SHRPX_OPTID_BACKEND: break; default: first = ++eol; continue; } if (parse_config(&new_config, optid, opt, optval, include_set, pattern_addr_indexer) != 0) { send_reply(400, APIStatusCode::FAILURE); return 0; } first = ++eol; } auto &tlsconf = config->tls; if (configure_downstream_group(&new_config, config->http2_proxy, true, tlsconf) != 0) { send_reply(400, APIStatusCode::FAILURE); return 0; } auto conn_handler = worker_->get_connection_handler(); conn_handler->send_replace_downstream(downstreamconf); send_reply(200, APIStatusCode::SUCCESS); return 0; } int APIDownstreamConnection::handle_configrevision() { auto config = get_config(); auto &balloc = downstream_->get_block_allocator(); // Construct the following string: // , // "data":{ // "configRevision": N // } auto data = concat_string_ref( balloc, R"(,"data":{"configRevision":)"sv, util::make_string_ref_uint(balloc, config->config_revision), "}"sv); send_reply(200, APIStatusCode::SUCCESS, data); return 0; } void APIDownstreamConnection::pause_read(IOCtrlReason reason) {} int APIDownstreamConnection::resume_read(IOCtrlReason reason, size_t consumed) { return 0; } void APIDownstreamConnection::force_resume_read() {} int APIDownstreamConnection::on_read() { return 0; } int APIDownstreamConnection::on_write() { return 0; } void APIDownstreamConnection::on_upstream_change(Upstream *upstream) {} bool APIDownstreamConnection::poolable() const { return false; } const std::shared_ptr & APIDownstreamConnection::get_downstream_addr_group() const { static std::shared_ptr s; return s; } DownstreamAddr *APIDownstreamConnection::get_addr() const { return nullptr; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_downstream_connection.cc0000644000000000000000000000013215077107270021204 xustar0030 mtime=1761382072.992444143 30 atime=1761382106.098310894 30 ctime=1761382109.094300398 nghttp2-1.68.0/src/shrpx_downstream_connection.cc0000644000175100017510000000332515077107270021577 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_downstream_connection.h" #include "shrpx_client_handler.h" #include "shrpx_downstream.h" #include "shrpx_log.h" namespace shrpx { DownstreamConnection::DownstreamConnection() : client_handler_(nullptr), downstream_(nullptr) {} DownstreamConnection::~DownstreamConnection() {} void DownstreamConnection::set_client_handler(ClientHandler *handler) { client_handler_ = handler; } ClientHandler *DownstreamConnection::get_client_handler() { return client_handler_; } Downstream *DownstreamConnection::get_downstream() { return downstream_; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_api_downstream_connection.h0000644000000000000000000000013215077107270021677 xustar0030 mtime=1761382072.990444153 30 atime=1761382106.158310629 30 ctime=1761382109.153300228 nghttp2-1.68.0/src/shrpx_api_downstream_connection.h0000644000175100017510000000706315077107270022275 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_API_DOWNSTREAM_CONNECTION_H #define SHRPX_API_DOWNSTREAM_CONNECTION_H #include "shrpx_downstream_connection.h" #include "template.h" using namespace nghttp2; using namespace std::literals; namespace shrpx { class Worker; // If new method is added, don't forget to update API_METHOD_STRING as // well. enum APIMethod { API_METHOD_GET, API_METHOD_POST, API_METHOD_PUT, API_METHOD_MAX, }; // API status code, which is independent from HTTP status code. But // generally, 2xx code for SUCCESS, and otherwise FAILURE. enum class APIStatusCode { SUCCESS, FAILURE, }; class APIDownstreamConnection; struct APIEndpoint { // Endpoint path. It must start with "/api/". std::string_view path; // true if we evaluate request body. bool require_body; // Allowed methods. This is bitwise OR of one or more of (1 << // APIMethod value). uint8_t allowed_methods; std::function handler; }; class APIDownstreamConnection : public DownstreamConnection { public: APIDownstreamConnection(Worker *worker); virtual ~APIDownstreamConnection(); virtual int attach_downstream(Downstream *downstream); virtual void detach_downstream(Downstream *downstream); virtual int push_request_headers(); virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen); virtual int end_upload_data(); virtual void pause_read(IOCtrlReason reason); virtual int resume_read(IOCtrlReason reason, size_t consumed); virtual void force_resume_read(); virtual int on_read(); virtual int on_write(); virtual void on_upstream_change(Upstream *upstream); // true if this object is poolable. virtual bool poolable() const; virtual const std::shared_ptr & get_downstream_addr_group() const; virtual DownstreamAddr *get_addr() const; int send_reply(unsigned int http_status, APIStatusCode api_status, const std::string_view &data = ""sv); int error_method_not_allowed(); // Handles backendconfig API request. int handle_backendconfig(); // Handles configrevision API request. int handle_configrevision(); private: Worker *worker_; // This points to the requested APIEndpoint struct. const APIEndpoint *api_; // The file descriptor for temporary file to store request body. int fd_; // true if we stop reading request body. bool shutdown_read_; }; } // namespace shrpx #endif // !defined(SHRPX_API_DOWNSTREAM_CONNECTION_H) nghttp2-1.68.0/src/PaxHeaders/base64_test.h0000644000000000000000000000013115077107270015342 xustar0029 mtime=1761382072.98444418 30 atime=1761382106.278310101 30 ctime=1761382109.275299875 nghttp2-1.68.0/src/base64_test.h0000644000175100017510000000300415077107270015730 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef BASE64_TEST_H #define BASE64_TEST_H #ifdef HAVE_CONFIG_H # include #endif // defined(HAVE_CONFIG_H) #define MUNIT_ENABLE_ASSERT_ALIASES #include "munit.h" namespace nghttp2 { extern const MunitSuite base64_suite; munit_void_test_decl(test_base64_encode) munit_void_test_decl(test_base64_decode) } // namespace nghttp2 #endif // !defined(BASE64_TEST_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_downstream.cc0000644000000000000000000000013215077107270016765 xustar0030 mtime=1761382072.992444143 30 atime=1761382106.095310907 30 ctime=1761382109.091300407 nghttp2-1.68.0/src/shrpx_downstream.cc0000644000175100017510000010112115077107270017351 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_downstream.h" #include #include #include "urlparse.h" #include "shrpx_upstream.h" #include "shrpx_client_handler.h" #include "shrpx_config.h" #include "shrpx_error.h" #include "shrpx_downstream_connection.h" #include "shrpx_downstream_queue.h" #include "shrpx_worker.h" #include "shrpx_http2_session.h" #include "shrpx_log.h" #ifdef HAVE_MRUBY # include "shrpx_mruby.h" #endif // defined(HAVE_MRUBY) #include "util.h" #include "http2.h" namespace shrpx { namespace { void header_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { auto downstream = static_cast(w->data); auto upstream = downstream->get_upstream(); if (LOG_ENABLED(INFO)) { DLOG(INFO, downstream) << "request header timeout stream_id=" << downstream->get_stream_id(); } downstream->disable_upstream_rtimer(); downstream->disable_upstream_wtimer(); upstream->on_timeout(downstream); } } // namespace namespace { void upstream_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { auto downstream = static_cast(w->data); auto upstream = downstream->get_upstream(); auto which = revents == EV_READ ? "read" : "write"; if (LOG_ENABLED(INFO)) { DLOG(INFO, downstream) << "upstream timeout stream_id=" << downstream->get_stream_id() << " event=" << which; } downstream->disable_upstream_rtimer(); downstream->disable_upstream_wtimer(); upstream->on_timeout(downstream); } } // namespace namespace { void upstream_rtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { upstream_timeoutcb(loop, w, EV_READ); } } // namespace namespace { void upstream_wtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { upstream_timeoutcb(loop, w, EV_WRITE); } } // namespace namespace { void downstream_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { auto downstream = static_cast(w->data); auto which = revents == EV_READ ? "read" : "write"; if (LOG_ENABLED(INFO)) { DLOG(INFO, downstream) << "downstream timeout stream_id=" << downstream->get_downstream_stream_id() << " event=" << which; } downstream->disable_downstream_rtimer(); downstream->disable_downstream_wtimer(); auto dconn = downstream->get_downstream_connection(); if (dconn) { dconn->on_timeout(); } } } // namespace namespace { void downstream_rtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { downstream_timeoutcb(loop, w, EV_READ); } } // namespace namespace { void downstream_wtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { downstream_timeoutcb(loop, w, EV_WRITE); } } // namespace // upstream could be nullptr for unittests Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool, int64_t stream_id) : dlnext(nullptr), dlprev(nullptr), response_sent_body_length(0), balloc_(1024, 1024), req_(balloc_), resp_(balloc_), request_start_time_(std::chrono::high_resolution_clock::now()), blocked_request_buf_(mcpool), request_buf_(mcpool), response_buf_(mcpool), upstream_(upstream), blocked_link_(nullptr), addr_(nullptr), num_retry_(0), stream_id_(stream_id), assoc_stream_id_(-1), downstream_stream_id_(-1), response_rst_stream_error_code_(NGHTTP2_NO_ERROR), affinity_cookie_(0), request_state_(DownstreamState::INITIAL), response_state_(DownstreamState::INITIAL), dispatch_state_(DispatchState::NONE), upgraded_(false), chunked_request_(false), chunked_response_(false), expect_final_response_(false), request_pending_(false), request_header_sent_(false), accesslog_written_(false), new_affinity_cookie_(false), blocked_request_data_eof_(false), expect_100_continue_(false), stop_reading_(false) { auto config = get_config(); auto &httpconf = config->http; ev_timer_init(&header_timer_, header_timeoutcb, 0., httpconf.timeout.header); auto &timeoutconf = config->http2.timeout; ev_timer_init(&upstream_rtimer_, &upstream_rtimeoutcb, 0., timeoutconf.stream_read); ev_timer_init(&upstream_wtimer_, &upstream_wtimeoutcb, 0., timeoutconf.stream_write); ev_timer_init(&downstream_rtimer_, &downstream_rtimeoutcb, 0., timeoutconf.stream_read); ev_timer_init(&downstream_wtimer_, &downstream_wtimeoutcb, 0., timeoutconf.stream_write); header_timer_.data = this; upstream_rtimer_.data = this; upstream_wtimer_.data = this; downstream_rtimer_.data = this; downstream_wtimer_.data = this; rcbufs_.reserve(32); #ifdef ENABLE_HTTP3 rcbufs3_.reserve(32); #endif // defined(ENABLE_HTTP3) } Downstream::~Downstream() { if (LOG_ENABLED(INFO)) { DLOG(INFO, this) << "Deleting"; } // check nullptr for unittest if (upstream_) { auto loop = upstream_->get_client_handler()->get_loop(); ev_timer_stop(loop, &upstream_rtimer_); ev_timer_stop(loop, &upstream_wtimer_); ev_timer_stop(loop, &downstream_rtimer_); ev_timer_stop(loop, &downstream_wtimer_); ev_timer_stop(loop, &header_timer_); #ifdef HAVE_MRUBY auto handler = upstream_->get_client_handler(); auto worker = handler->get_worker(); auto mruby_ctx = worker->get_mruby_context(); mruby_ctx->delete_downstream(this); #endif // defined(HAVE_MRUBY) } #ifdef HAVE_MRUBY if (dconn_) { const auto &group = dconn_->get_downstream_addr_group(); if (group) { const auto &mruby_ctx = group->shared_addr->mruby_ctx; mruby_ctx->delete_downstream(this); } } #endif // defined(HAVE_MRUBY) // DownstreamConnection may refer to this object. Delete it now // explicitly. dconn_.reset(); #ifdef ENABLE_HTTP3 for (auto rcbuf : rcbufs3_) { nghttp3_rcbuf_decref(rcbuf); } #endif // defined(ENABLE_HTTP3) for (auto rcbuf : rcbufs_) { nghttp2_rcbuf_decref(rcbuf); } if (LOG_ENABLED(INFO)) { DLOG(INFO, this) << "Deleted"; } } int Downstream::attach_downstream_connection( std::unique_ptr dconn) { if (dconn->attach_downstream(this) != 0) { return -1; } dconn_ = std::move(dconn); return 0; } void Downstream::detach_downstream_connection() { if (!dconn_) { return; } #ifdef HAVE_MRUBY const auto &group = dconn_->get_downstream_addr_group(); if (group) { const auto &mruby_ctx = group->shared_addr->mruby_ctx; mruby_ctx->delete_downstream(this); } #endif // defined(HAVE_MRUBY) dconn_->detach_downstream(this); auto handler = dconn_->get_client_handler(); handler->pool_downstream_connection( std::unique_ptr(dconn_.release())); } DownstreamConnection *Downstream::get_downstream_connection() { return dconn_.get(); } std::unique_ptr Downstream::pop_downstream_connection() { #ifdef HAVE_MRUBY if (!dconn_) { return nullptr; } const auto &group = dconn_->get_downstream_addr_group(); if (group) { const auto &mruby_ctx = group->shared_addr->mruby_ctx; mruby_ctx->delete_downstream(this); } #endif // defined(HAVE_MRUBY) return std::unique_ptr(dconn_.release()); } void Downstream::pause_read(IOCtrlReason reason) { if (dconn_) { dconn_->pause_read(reason); } } int Downstream::resume_read(IOCtrlReason reason, size_t consumed) { if (dconn_) { return dconn_->resume_read(reason, consumed); } return 0; } void Downstream::force_resume_read() { if (dconn_) { dconn_->force_resume_read(); } } namespace { const HeaderRefs::value_type * search_header_linear_backwards(const HeaderRefs &headers, const std::string_view &name) { for (auto it = headers.rbegin(); it != headers.rend(); ++it) { auto &kv = *it; if (kv.name == name) { return &kv; } } return nullptr; } } // namespace std::string_view Downstream::assemble_request_cookie() { size_t len = 0; for (auto &kv : req_.fs.headers()) { if (kv.token != http2::HD_COOKIE || kv.value.empty()) { continue; } len += kv.value.size() + str_size("; "); } auto iov = make_byte_ref(balloc_, len + 1); auto p = std::ranges::begin(iov); for (auto &kv : req_.fs.headers()) { if (kv.token != http2::HD_COOKIE || kv.value.empty()) { continue; } auto end = std::ranges::end(kv.value); for (auto it = std::ranges::begin(kv.value) + kv.value.size(); it != std::ranges::begin(kv.value); --it) { auto c = *(it - 1); if (c == ' ' || c == ';') { continue; } end = it; break; } p = std::ranges::copy(std::ranges::begin(kv.value), end, p).out; p = std::ranges::copy("; "sv, p).out; } // cut trailing "; " if (p - std::ranges::begin(iov) >= 2) { p -= 2; } return as_string_view(std::ranges::begin(iov), p); } uint32_t Downstream::find_affinity_cookie(const std::string_view &name) { for (auto &kv : req_.fs.headers()) { if (kv.token != http2::HD_COOKIE) { continue; } for (auto it = std::ranges::begin(kv.value); it != std::ranges::end(kv.value);) { if (*it == '\t' || *it == ' ' || *it == ';') { ++it; continue; } auto end = std::ranges::find(it, std::ranges::end(kv.value), '='); if (end == std::ranges::end(kv.value)) { return 0; } if (name != std::string_view{it, end}) { it = std::ranges::find(it, std::ranges::end(kv.value), ';'); continue; } it = std::ranges::find(end + 1, std::ranges::end(kv.value), ';'); auto val = std::string_view{end + 1, it}; if (val.size() != 8) { return 0; } uint32_t h = 0; for (auto c : val) { auto n = util::hex_to_uint(c); if (n == 256) { return 0; } h <<= 4; h += n; } affinity_cookie_ = h; return h; } } return 0; } size_t Downstream::count_crumble_request_cookie() { size_t n = 0; for (auto &kv : req_.fs.headers()) { if (kv.token != http2::HD_COOKIE) { continue; } for (auto it = std::ranges::begin(kv.value); it != std::ranges::end(kv.value);) { if (*it == '\t' || *it == ' ' || *it == ';') { ++it; continue; } it = std::ranges::find(it, std::ranges::end(kv.value), ';'); ++n; } } return n; } void Downstream::crumble_request_cookie(std::vector &nva) { for (auto &kv : req_.fs.headers()) { if (kv.token != http2::HD_COOKIE) { continue; } for (auto it = std::ranges::begin(kv.value); it != std::ranges::end(kv.value);) { if (*it == '\t' || *it == ' ' || *it == ';') { ++it; continue; } auto first = it; it = std::ranges::find(it, std::ranges::end(kv.value), ';'); nva.push_back({(uint8_t *)"cookie", (uint8_t *)first, str_size("cookie"), (size_t)(it - first), (uint8_t)(NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE | (kv.no_index ? NGHTTP2_NV_FLAG_NO_INDEX : 0))}); } } } namespace { void add_header(size_t &sum, HeaderRefs &headers, const std::string_view &name, const std::string_view &value, bool no_index, int32_t token) { sum += name.size() + value.size(); headers.emplace_back(name, value, no_index, token); } } // namespace namespace { std::string_view alloc_header_name(BlockAllocator &balloc, const std::string_view &name) { auto iov = make_byte_ref(balloc, name.size() + 1); auto p = util::tolower(name, std::ranges::begin(iov)); *p = '\0'; return as_string_view(std::ranges::begin(iov), p); } } // namespace namespace { void append_last_header_key(BlockAllocator &balloc, bool &key_prev, size_t &sum, HeaderRefs &headers, const std::string_view &data) { assert(key_prev); sum += data.size(); auto &item = headers.back(); auto name = realloc_concat_string_ref( balloc, item.name, std::views::transform(data, util::lowcase)); item.name = name; item.token = http2::lookup_token(item.name); } } // namespace namespace { void append_last_header_value(BlockAllocator &balloc, bool &key_prev, size_t &sum, HeaderRefs &headers, const std::string_view &data) { key_prev = false; sum += data.size(); auto &item = headers.back(); item.value = realloc_concat_string_ref(balloc, item.value, data); } } // namespace int FieldStore::parse_content_length() { content_length = -1; for (auto &kv : headers_) { if (kv.token != http2::HD_CONTENT_LENGTH) { continue; } auto len = util::parse_uint(kv.value); if (!len) { return -1; } if (content_length != -1) { return -1; } content_length = *len; } return 0; } const HeaderRefs::value_type *FieldStore::header(int32_t token) const { for (auto it = headers_.rbegin(); it != headers_.rend(); ++it) { auto &kv = *it; if (kv.token == token) { return &kv; } } return nullptr; } HeaderRefs::value_type *FieldStore::header(int32_t token) { for (auto it = headers_.rbegin(); it != headers_.rend(); ++it) { auto &kv = *it; if (kv.token == token) { return &kv; } } return nullptr; } const HeaderRefs::value_type * FieldStore::header(const std::string_view &name) const { return search_header_linear_backwards(headers_, name); } void FieldStore::add_header_token(const std::string_view &name, const std::string_view &value, bool no_index, int32_t token) { shrpx::add_header(buffer_size_, headers_, name, value, no_index, token); } void FieldStore::alloc_add_header_name(const std::string_view &name) { auto name_ref = alloc_header_name(balloc_, name); auto token = http2::lookup_token(name_ref); add_header_token(name_ref, ""sv, false, token); header_key_prev_ = true; } void FieldStore::append_last_header_key(const std::string_view &data) { shrpx::append_last_header_key(balloc_, header_key_prev_, buffer_size_, headers_, data); } void FieldStore::append_last_header_value(const std::string_view &data) { shrpx::append_last_header_value(balloc_, header_key_prev_, buffer_size_, headers_, data); } void FieldStore::clear_headers() { headers_.clear(); header_key_prev_ = false; } void FieldStore::add_trailer_token(const std::string_view &name, const std::string_view &value, bool no_index, int32_t token) { // Header size limit should be applied to all header and trailer // fields combined. shrpx::add_header(buffer_size_, trailers_, name, value, no_index, token); } void FieldStore::alloc_add_trailer_name(const std::string_view &name) { auto name_ref = alloc_header_name(balloc_, name); auto token = http2::lookup_token(name_ref); add_trailer_token(name_ref, ""sv, false, token); trailer_key_prev_ = true; } void FieldStore::append_last_trailer_key(const std::string_view &data) { shrpx::append_last_header_key(balloc_, trailer_key_prev_, buffer_size_, trailers_, data); } void FieldStore::append_last_trailer_value(const std::string_view &data) { shrpx::append_last_header_value(balloc_, trailer_key_prev_, buffer_size_, trailers_, data); } void FieldStore::erase_content_length_and_transfer_encoding() { for (auto &kv : headers_) { switch (kv.token) { case http2::HD_CONTENT_LENGTH: case http2::HD_TRANSFER_ENCODING: kv.name = ""sv; kv.token = -1; break; } } } void Downstream::set_request_start_time( std::chrono::high_resolution_clock::time_point time) { request_start_time_ = std::move(time); } const std::chrono::high_resolution_clock::time_point & Downstream::get_request_start_time() const { return request_start_time_; } void Downstream::reset_upstream(Upstream *upstream) { upstream_ = upstream; if (dconn_) { dconn_->on_upstream_change(upstream); } } Upstream *Downstream::get_upstream() const { return upstream_; } void Downstream::set_stream_id(int64_t stream_id) { stream_id_ = stream_id; } int64_t Downstream::get_stream_id() const { return stream_id_; } void Downstream::set_request_state(DownstreamState state) { request_state_ = state; } DownstreamState Downstream::get_request_state() const { return request_state_; } bool Downstream::get_chunked_request() const { return chunked_request_; } void Downstream::set_chunked_request(bool f) { chunked_request_ = f; } bool Downstream::request_buf_full() { auto handler = upstream_->get_client_handler(); auto faddr = handler->get_upstream_addr(); auto worker = handler->get_worker(); // We don't check buffer size here for API endpoint. if (faddr->alt_mode == UpstreamAltMode::API) { return false; } if (dconn_) { auto &downstreamconf = *worker->get_downstream_config(); return blocked_request_buf_.rleft() + request_buf_.rleft() >= downstreamconf.request_buffer_size; } return false; } DefaultMemchunks *Downstream::get_request_buf() { return &request_buf_; } // Call this function after this object is attached to // Downstream. Otherwise, the program will crash. int Downstream::push_request_headers() { if (!dconn_) { DLOG(INFO, this) << "dconn_ is NULL"; return -1; } return dconn_->push_request_headers(); } int Downstream::push_upload_data_chunk(const uint8_t *data, size_t datalen) { req_.recv_body_length += datalen; if (!dconn_ && !request_header_sent_) { blocked_request_buf_.append(data, datalen); req_.unconsumed_body_length += datalen; return 0; } // Assumes that request headers have already been pushed to output // buffer using push_request_headers(). if (!dconn_) { DLOG(INFO, this) << "dconn_ is NULL"; return -1; } if (dconn_->push_upload_data_chunk(data, datalen) != 0) { return -1; } req_.unconsumed_body_length += datalen; return 0; } int Downstream::end_upload_data() { if (!dconn_ && !request_header_sent_) { blocked_request_data_eof_ = true; return 0; } if (!dconn_) { DLOG(INFO, this) << "dconn_ is NULL"; return -1; } return dconn_->end_upload_data(); } void Downstream::rewrite_location_response_header( const std::string_view &upstream_scheme) { auto hd = resp_.fs.header(http2::HD_LOCATION); if (!hd) { return; } if (request_downstream_host_.empty() || req_.authority.empty()) { return; } urlparse_url u; auto rv = urlparse_parse_url(hd->value.data(), hd->value.size(), 0, &u); if (rv != 0) { return; } auto new_uri = http2::rewrite_location_uri(balloc_, hd->value, u, request_downstream_host_, req_.authority, upstream_scheme); if (new_uri.empty()) { return; } hd->value = new_uri; } bool Downstream::get_chunked_response() const { return chunked_response_; } void Downstream::set_chunked_response(bool f) { chunked_response_ = f; } int Downstream::on_read() { if (!dconn_) { DLOG(INFO, this) << "dconn_ is NULL"; return -1; } return dconn_->on_read(); } void Downstream::set_response_state(DownstreamState state) { response_state_ = state; } DownstreamState Downstream::get_response_state() const { return response_state_; } DefaultMemchunks *Downstream::get_response_buf() { return &response_buf_; } bool Downstream::response_buf_full() { if (dconn_) { auto handler = upstream_->get_client_handler(); auto worker = handler->get_worker(); auto &downstreamconf = *worker->get_downstream_config(); return response_buf_.rleft() >= downstreamconf.response_buffer_size; } return false; } bool Downstream::validate_request_recv_body_length() const { if (req_.fs.content_length == -1) { return true; } if (req_.fs.content_length != req_.recv_body_length) { if (LOG_ENABLED(INFO)) { DLOG(INFO, this) << "request invalid bodylen: content-length=" << req_.fs.content_length << ", received=" << req_.recv_body_length; } return false; } return true; } bool Downstream::validate_response_recv_body_length() const { if (!expect_response_body() || resp_.fs.content_length == -1) { return true; } if (resp_.fs.content_length != resp_.recv_body_length) { if (LOG_ENABLED(INFO)) { DLOG(INFO, this) << "response invalid bodylen: content-length=" << resp_.fs.content_length << ", received=" << resp_.recv_body_length; } return false; } return true; } void Downstream::check_upgrade_fulfilled_http2() { // This handles nonzero req_.connect_proto and h1 frontend requests // WebSocket upgrade. upgraded_ = (req_.method == HTTP_CONNECT || req_.connect_proto == ConnectProto::WEBSOCKET) && resp_.http_status / 100 == 2; } void Downstream::check_upgrade_fulfilled_http1() { if (req_.method == HTTP_CONNECT) { if (req_.connect_proto == ConnectProto::WEBSOCKET) { if (resp_.http_status != 101) { return; } // This is done for HTTP/2 frontend only. auto accept = resp_.fs.header(http2::HD_SEC_WEBSOCKET_ACCEPT); if (!accept) { return; } std::array accept_buf; auto expected = http2::make_websocket_accept_token(accept_buf.data(), ws_key_); upgraded_ = !expected.empty() && expected == accept->value; } else { upgraded_ = resp_.http_status / 100 == 2; } return; } if (resp_.http_status == 101) { // TODO Do more strict checking for upgrade headers upgraded_ = req_.upgrade_request; return; } } void Downstream::inspect_http2_request() { if (req_.method == HTTP_CONNECT) { req_.upgrade_request = true; } } void Downstream::inspect_http1_request() { if (req_.method == HTTP_CONNECT) { req_.upgrade_request = true; } else if (req_.http_minor > 0) { auto upgrade = req_.fs.header(http2::HD_UPGRADE); if (upgrade) { const auto &val = upgrade->value; // TODO Perform more strict checking for upgrade headers if (NGHTTP2_CLEARTEXT_PROTO_VERSION_ID ""sv == val) { req_.http2_upgrade_seen = true; } else { req_.upgrade_request = true; // TODO Should we check Sec-WebSocket-Key, and // Sec-WebSocket-Version as well? if (util::strieq("websocket"sv, val)) { req_.connect_proto = ConnectProto::WEBSOCKET; } } } } auto transfer_encoding = req_.fs.header(http2::HD_TRANSFER_ENCODING); if (transfer_encoding) { req_.fs.content_length = -1; } auto expect = req_.fs.header(http2::HD_EXPECT); expect_100_continue_ = expect && util::strieq(expect->value, "100-continue"sv); } void Downstream::inspect_http1_response() { auto transfer_encoding = resp_.fs.header(http2::HD_TRANSFER_ENCODING); if (transfer_encoding) { resp_.fs.content_length = -1; } } void Downstream::reset_response() { resp_.http_status = 0; resp_.http_major = 1; resp_.http_minor = 1; } bool Downstream::get_non_final_response() const { return !upgraded_ && resp_.http_status / 100 == 1; } bool Downstream::supports_non_final_response() const { return req_.http_major == 3 || req_.http_major == 2 || (req_.http_major == 1 && req_.http_minor == 1); } bool Downstream::get_upgraded() const { return upgraded_; } bool Downstream::get_http2_upgrade_request() const { return req_.http2_upgrade_seen && req_.fs.header(http2::HD_HTTP2_SETTINGS) && response_state_ == DownstreamState::INITIAL; } std::string_view Downstream::get_http2_settings() const { auto http2_settings = req_.fs.header(http2::HD_HTTP2_SETTINGS); if (!http2_settings) { return ""sv; } return http2_settings->value; } void Downstream::set_downstream_stream_id(int64_t stream_id) { downstream_stream_id_ = stream_id; } int64_t Downstream::get_downstream_stream_id() const { return downstream_stream_id_; } uint32_t Downstream::get_response_rst_stream_error_code() const { return response_rst_stream_error_code_; } void Downstream::set_response_rst_stream_error_code(uint32_t error_code) { response_rst_stream_error_code_ = error_code; } void Downstream::set_expect_final_response(bool f) { expect_final_response_ = f; } bool Downstream::get_expect_final_response() const { return expect_final_response_; } bool Downstream::expect_response_body() const { return !resp_.headers_only && http2::expect_response_body(req_.method, resp_.http_status); } bool Downstream::expect_response_trailer() const { // In HTTP/2, if final response HEADERS does not bear END_STREAM it // is possible trailer fields might come, regardless of request // method or status code. return !resp_.headers_only && (resp_.http_major == 3 || resp_.http_major == 2); } void Downstream::repeat_header_timer() { auto loop = upstream_->get_client_handler()->get_loop(); ev_timer_again(loop, &header_timer_); } void Downstream::stop_header_timer() { auto loop = upstream_->get_client_handler()->get_loop(); ev_timer_stop(loop, &header_timer_); } namespace { void reset_timer(struct ev_loop *loop, ev_timer *w) { ev_timer_again(loop, w); } } // namespace namespace { void try_reset_timer(struct ev_loop *loop, ev_timer *w) { if (!ev_is_active(w)) { return; } ev_timer_again(loop, w); } } // namespace namespace { void ensure_timer(struct ev_loop *loop, ev_timer *w) { if (ev_is_active(w)) { return; } ev_timer_again(loop, w); } } // namespace namespace { void disable_timer(struct ev_loop *loop, ev_timer *w) { ev_timer_stop(loop, w); } } // namespace void Downstream::reset_upstream_rtimer() { if (get_config()->http2.timeout.stream_read == 0.) { return; } auto loop = upstream_->get_client_handler()->get_loop(); reset_timer(loop, &upstream_rtimer_); } void Downstream::reset_upstream_wtimer() { auto loop = upstream_->get_client_handler()->get_loop(); auto &timeoutconf = get_config()->http2.timeout; if (timeoutconf.stream_write != 0.) { reset_timer(loop, &upstream_wtimer_); } if (timeoutconf.stream_read != 0.) { try_reset_timer(loop, &upstream_rtimer_); } } void Downstream::ensure_upstream_wtimer() { if (get_config()->http2.timeout.stream_write == 0.) { return; } auto loop = upstream_->get_client_handler()->get_loop(); ensure_timer(loop, &upstream_wtimer_); } void Downstream::disable_upstream_rtimer() { if (get_config()->http2.timeout.stream_read == 0.) { return; } auto loop = upstream_->get_client_handler()->get_loop(); disable_timer(loop, &upstream_rtimer_); } void Downstream::disable_upstream_wtimer() { if (get_config()->http2.timeout.stream_write == 0.) { return; } auto loop = upstream_->get_client_handler()->get_loop(); disable_timer(loop, &upstream_wtimer_); } void Downstream::reset_downstream_rtimer() { if (get_config()->http2.timeout.stream_read == 0.) { return; } auto loop = upstream_->get_client_handler()->get_loop(); reset_timer(loop, &downstream_rtimer_); } void Downstream::reset_downstream_wtimer() { auto loop = upstream_->get_client_handler()->get_loop(); auto &timeoutconf = get_config()->http2.timeout; if (timeoutconf.stream_write != 0.) { reset_timer(loop, &downstream_wtimer_); } if (timeoutconf.stream_read != 0.) { try_reset_timer(loop, &downstream_rtimer_); } } void Downstream::ensure_downstream_wtimer() { if (get_config()->http2.timeout.stream_write == 0.) { return; } auto loop = upstream_->get_client_handler()->get_loop(); ensure_timer(loop, &downstream_wtimer_); } void Downstream::disable_downstream_rtimer() { if (get_config()->http2.timeout.stream_read == 0.) { return; } auto loop = upstream_->get_client_handler()->get_loop(); disable_timer(loop, &downstream_rtimer_); } void Downstream::disable_downstream_wtimer() { if (get_config()->http2.timeout.stream_write == 0.) { return; } auto loop = upstream_->get_client_handler()->get_loop(); disable_timer(loop, &downstream_wtimer_); } bool Downstream::accesslog_ready() const { return !accesslog_written_ && resp_.http_status > 0; } void Downstream::add_retry() { ++num_retry_; } bool Downstream::no_more_retry() const { return num_retry_ > 50; } void Downstream::set_request_downstream_host(const std::string_view &host) { request_downstream_host_ = host; } void Downstream::set_request_pending(bool f) { request_pending_ = f; } bool Downstream::get_request_pending() const { return request_pending_; } void Downstream::set_request_header_sent(bool f) { request_header_sent_ = f; } bool Downstream::get_request_header_sent() const { return request_header_sent_; } bool Downstream::request_submission_ready() const { return (request_state_ == DownstreamState::HEADER_COMPLETE || request_state_ == DownstreamState::MSG_COMPLETE) && (request_pending_ || !request_header_sent_) && response_state_ == DownstreamState::INITIAL; } DispatchState Downstream::get_dispatch_state() const { return dispatch_state_; } void Downstream::set_dispatch_state(DispatchState s) { dispatch_state_ = s; } void Downstream::attach_blocked_link(BlockedLink *l) { assert(!blocked_link_); l->downstream = this; blocked_link_ = l; } BlockedLink *Downstream::detach_blocked_link() { auto link = blocked_link_; blocked_link_ = nullptr; return link; } bool Downstream::can_detach_downstream_connection() const { // We should check request and response buffer. If request buffer // is not empty, then we might leave downstream connection in weird // state, especially for HTTP/1.1 return dconn_ && response_state_ == DownstreamState::MSG_COMPLETE && request_state_ == DownstreamState::MSG_COMPLETE && !upgraded_ && !resp_.connection_close && request_buf_.rleft() == 0; } DefaultMemchunks Downstream::pop_response_buf() { return std::move(response_buf_); } void Downstream::set_assoc_stream_id(int64_t stream_id) { assoc_stream_id_ = stream_id; } int64_t Downstream::get_assoc_stream_id() const { return assoc_stream_id_; } BlockAllocator &Downstream::get_block_allocator() { return balloc_; } void Downstream::add_rcbuf(nghttp2_rcbuf *rcbuf) { nghttp2_rcbuf_incref(rcbuf); rcbufs_.push_back(rcbuf); } #ifdef ENABLE_HTTP3 void Downstream::add_rcbuf(nghttp3_rcbuf *rcbuf) { nghttp3_rcbuf_incref(rcbuf); rcbufs3_.push_back(rcbuf); } #endif // defined(ENABLE_HTTP3) void Downstream::set_downstream_addr_group( const std::shared_ptr &group) { group_ = group; } void Downstream::set_addr(const DownstreamAddr *addr) { addr_ = addr; } const DownstreamAddr *Downstream::get_addr() const { return addr_; } void Downstream::set_accesslog_written(bool f) { accesslog_written_ = f; } void Downstream::renew_affinity_cookie(uint32_t h) { affinity_cookie_ = h; new_affinity_cookie_ = true; } uint32_t Downstream::get_affinity_cookie_to_send() const { if (new_affinity_cookie_) { return affinity_cookie_; } return 0; } DefaultMemchunks *Downstream::get_blocked_request_buf() { return &blocked_request_buf_; } bool Downstream::get_blocked_request_data_eof() const { return blocked_request_data_eof_; } void Downstream::set_blocked_request_data_eof(bool f) { blocked_request_data_eof_ = f; } void Downstream::set_ws_key(const std::string_view &key) { ws_key_ = key; } bool Downstream::get_expect_100_continue() const { return expect_100_continue_; } bool Downstream::get_stop_reading() const { return stop_reading_; } void Downstream::set_stop_reading(bool f) { stop_reading_ = f; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/base64.h0000644000000000000000000000013115077107270014303 xustar0029 mtime=1761382072.98444418 30 atime=1761382106.069311022 30 ctime=1761382109.065300482 nghttp2-1.68.0/src/base64.h0000644000175100017510000001503015077107270014673 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef BASE64_H #define BASE64_H #include "nghttp2_config.h" #include #include "template.h" #include "allocator.h" namespace nghttp2 { namespace base64 { inline constexpr char B64_CHARS[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', }; constexpr size_t encode_length(size_t n) { return (n + 2) / 3 * 4; } template requires(std::indirectly_writable) constexpr O encode(I first, I last, O result) { using result_type = std::iter_value_t; auto len = std::ranges::distance(first, last); if (len == 0) { return result; } auto p = result; for (;;) { len = std::ranges::distance(first, last); if (len < 3) { break; } auto n = static_cast(static_cast(*first++) << 16); n += static_cast(static_cast(*first++) << 8); n += static_cast(*first++); *p++ = static_cast(B64_CHARS[n >> 18]); *p++ = static_cast(B64_CHARS[(n >> 12) & 0x3fu]); *p++ = static_cast(B64_CHARS[(n >> 6) & 0x3fu]); *p++ = static_cast(B64_CHARS[n & 0x3fu]); } switch (len) { case 2: { auto n = static_cast(static_cast(*first++) << 16); n += static_cast(static_cast(*first++) << 8); *p++ = static_cast(B64_CHARS[n >> 18]); *p++ = static_cast(B64_CHARS[(n >> 12) & 0x3fu]); *p++ = static_cast(B64_CHARS[(n >> 6) & 0x3fu]); *p++ = '='; break; } case 1: { auto n = static_cast(static_cast(*first++) << 16); *p++ = static_cast(B64_CHARS[n >> 18]); *p++ = static_cast(B64_CHARS[(n >> 12) & 0x3fu]); *p++ = '='; *p++ = '='; break; } } return p; } template requires(std::indirectly_writable && !std::is_array_v>) constexpr O encode(R &&r, O result) { return encode(std::ranges::begin(r), std::ranges::end(r), std::move(result)); } template requires(!std::is_array_v>) constexpr std::string encode(R &&r) { std::string res; auto len = std::ranges::size(r); if (len == 0) { return res; } res.resize((len + 2) / 3 * 4); encode(std::forward(r), std::ranges::begin(res)); return res; } inline constexpr int B64_INDEX_TABLE[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; template requires(std::indirectly_writable) constexpr O decode(I first, I last, O result) { using result_type = std::iter_value_t; assert(std::ranges::distance(first, last) % 4 == 0); auto p = result; for (; first != last;) { uint32_t n = 0; for (int i = 1; i <= 4; ++i, ++first) { auto idx = B64_INDEX_TABLE[static_cast(*first)]; if (idx == -1) { if (i <= 2) { return result; } if (i == 3) { if (*first == '=' && *std::ranges::next(first, 1) == '=' && std::ranges::next(first, 2) == last) { *p++ = static_cast(n >> 16); return p; } return result; } if (*first == '=' && std::ranges::next(first, 1) == last) { *p++ = static_cast(n >> 16); *p++ = n >> 8 & 0xffu; return p; } return result; } n += static_cast(idx) << (24 - i * 6); } *p++ = static_cast(n >> 16); *p++ = n >> 8 & 0xffu; *p++ = n & 0xffu; } return p; } template requires(!std::is_array_v>) std::span decode(BlockAllocator &balloc, R &&r) { auto len = std::ranges::size(r); if (len % 4 != 0) { return {}; } auto iov = make_byte_ref(balloc, len / 4 * 3 + 1); auto p = std::ranges::begin(iov); p = decode(std::ranges::begin(r), std::ranges::end(r), p); *p = '\0'; return {std::ranges::begin(iov), p}; } } // namespace base64 } // namespace nghttp2 #endif // !defined(BASE64_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_tls.h0000644000000000000000000000013215077107270015246 xustar0030 mtime=1761382072.999444111 30 atime=1761382106.120310797 30 ctime=1761382109.116300335 nghttp2-1.68.0/src/shrpx_tls.h0000644000175100017510000002744115077107270015646 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_TLS_H #define SHRPX_TLS_H #include "shrpx.h" #include #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include #ifdef HAVE_NEVERBLEED # include #endif // defined(HAVE_NEVERBLEED) #include "network.h" #include "shrpx_config.h" #include "shrpx_router.h" namespace shrpx { class ClientHandler; class Worker; class DownstreamConnectionPool; struct DownstreamAddr; struct UpstreamAddr; namespace tls { struct TLSSessionCache { // ASN1 representation of SSL_SESSION object. See // i2d_SSL_SESSION(3SSL). std::vector session_data; // The last time stamp when this cache entry is created or updated. std::chrono::steady_clock::time_point last_updated; }; // This struct stores the additional information per SSL_CTX. This is // attached to SSL_CTX using SSL_CTX_set_app_data(). struct TLSContextData { // SCT data formatted so that this can be directly sent as // extension_data of signed_certificate_timestamp. std::vector sct_data; // cert_type is the type of certificate (e.g., // NGHTTP2_CERT_TYPE_ECDSA). int cert_type; }; // Create server side SSL_CTX SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file, const std::vector &sct_data #ifdef HAVE_NEVERBLEED , neverbleed_t *nb #endif // defined(HAVE_NEVERBLEED) ); // Create client side SSL_CTX. This does not configure ALPN settings. SSL_CTX *create_ssl_client_context( #ifdef HAVE_NEVERBLEED neverbleed_t *nb, #endif // defined(HAVE_NEVERBLEED) const std::string_view &cacert, const std::string_view &cert_file, const std::string_view &private_key_file); ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr, socklen_t addrlen, const UpstreamAddr *faddr); // Check peer's certificate against given |address| and |host|. int check_cert(SSL *ssl, const Address *addr, const std::string_view &host); // Check peer's certificate against given host name described in // |addr| and numeric address in |raddr|. Note that |raddr| might not // point to &addr->addr. int check_cert(SSL *ssl, const DownstreamAddr *addr, const Address *raddr); // Verify |cert| using numeric IP address. |hostname| and |addr| // should contain the same numeric IP address. This function returns // 0 if it succeeds, or -1. int verify_numeric_hostname(X509 *cert, const std::string_view &hostname, const Address *addr); // Verify |cert| using DNS name hostname. This function returns 0 if // it succeeds, or -1. int verify_dns_hostname(X509 *cert, const std::string_view &hostname); struct WildcardRevPrefix { WildcardRevPrefix(const std::string_view &prefix, size_t idx) : prefix(std::ranges::begin(prefix), std::ranges::end(prefix)), idx(idx) {} // "Prefix" of wildcard pattern. It is reversed from original form. // For example, if the original wildcard is "test*.nghttp2.org", // prefix would be "tset". ImmutableString prefix; // The index of SSL_CTX. See ConnectionHandler::get_ssl_ctx(). size_t idx; }; struct WildcardPattern { // Wildcard host sharing only suffix is probably rare, so we just do // linear search. std::vector rev_prefix; }; class CertLookupTree { public: CertLookupTree(); // Adds hostname pattern |hostname| to the lookup tree, associating // value |index|. When the queried host matches this pattern, // |index| is returned. We support wildcard pattern. The left most // '*' is considered as wildcard character, and it must match at // least one character. If the same pattern has been already added, // this function does not alter the tree, and returns the existing // matching index. // // The caller should lower-case |hostname| since this function does // do that, and lookup function performs case-sensitive match. // // TODO Treat wildcard pattern described as RFC 6125. // // This function returns the index. It returns -1 if it fails // (e.g., hostname is too long). If the returned index equals to // |index|, then hostname is added to the tree with the value // |index|. If it is not -1, and does not equal to |index|, same // hostname has already been added to the tree. ssize_t add_cert(const std::string_view &hostname, size_t index); // Looks up index using the given |hostname|. The exact match takes // precedence over wildcard match. For wildcard match, longest // match (sum of matched suffix and prefix length in bytes) is // preferred, breaking a tie with longer suffix. // // The caller should lower-case |hostname| since this function // performs case-sensitive match. ssize_t lookup(const std::string_view &hostname); // Dumps the contents of this lookup tree to stderr. void dump() const; private: // Exact match Router router_; // Wildcard reversed suffix match. The returned index is into // wildcard_patterns_. Router rev_wildcard_router_; // Stores wildcard suffix patterns. std::vector wildcard_patterns_; }; // Adds hostnames in certificate in |ssl_ctx| to lookup tree |lt|. // The subjectAltNames and commonName are considered as eligible // hostname. If there is at least one dNSName in subjectAltNames, // commonName is not considered. |ssl_ctx| is also added to // |indexed_ssl_ctx|. This function returns 0 if it succeeds, or -1. int cert_lookup_tree_add_ssl_ctx( CertLookupTree *lt, std::vector> &indexed_ssl_ctx, SSL_CTX *ssl_ctx); // Returns true if |proto| is included in the // protocol list |protos|. bool in_proto_list(const std::vector &protos, const std::string_view &proto); // Returns true if security requirement for HTTP/2 is fulfilled. bool check_http2_requirement(SSL *ssl); // Returns SSL/TLS option mask to disable SSL/TLS protocol version not // included in |tls_proto_list|. The returned mask can be directly // passed to SSL_CTX_set_options(). nghttp2_ssl_op_type create_tls_proto_mask(const std::vector &tls_proto_list); int set_alpn_prefs(std::vector &out, const std::vector &protos); // Setups server side SSL_CTX. This function inspects get_config() // and if upstream_no_tls is true, returns nullptr. Otherwise // construct default SSL_CTX. If subcerts are available // (get_config()->subcerts), caller should provide CertLookupTree // object as |cert_tree| parameter, otherwise SNI does not work. All // the created SSL_CTX is stored into |all_ssl_ctx|. They are also // added to |indexed_ssl_ctx|. |cert_tree| uses its index to // associate hostname to the SSL_CTX. SSL_CTX * setup_server_ssl_context(std::vector &all_ssl_ctx, std::vector> &indexed_ssl_ctx, CertLookupTree *cert_tree #ifdef HAVE_NEVERBLEED , neverbleed_t *nb #endif // defined(HAVE_NEVERBLEED) ); #ifdef ENABLE_HTTP3 SSL_CTX *setup_quic_server_ssl_context( std::vector &all_ssl_ctx, std::vector> &indexed_ssl_ctx, CertLookupTree *cert_tree # ifdef HAVE_NEVERBLEED , neverbleed_t *nb # endif // defined(HAVE_NEVERBLEED) ); #endif // defined(ENABLE_HTTP3) // Setups client side SSL_CTX. SSL_CTX *setup_downstream_client_ssl_context( #ifdef HAVE_NEVERBLEED neverbleed_t *nb #endif // defined(HAVE_NEVERBLEED) ); // Sets ALPN settings in |SSL| suitable for HTTP/2 use. void setup_downstream_http2_alpn(SSL *ssl); // Sets ALPN settings in |SSL| suitable for HTTP/1.1 use. void setup_downstream_http1_alpn(SSL *ssl); // Creates CertLookupTree. If frontend is configured not to use TLS, // this function returns nullptr. std::unique_ptr create_cert_lookup_tree(); SSL *create_ssl(SSL_CTX *ssl_ctx); // Returns true if SSL/TLS is enabled on upstream bool upstream_tls_enabled(const ConnectionConfig &connconf); // Performs TLS hostname match. |pattern| can contain wildcard // character '*', which matches prefix of target hostname. There are // several restrictions to make wildcard work. The matching algorithm // is based on RFC 6125. bool tls_hostname_match(const std::string_view &pattern, const std::string_view &hostname); // Caches |session|. |session| is serialized into ASN1 // representation, and stored. |t| is used as a time stamp. // Depending on the existing cache's time stamp, |session| might not // be cached. void try_cache_tls_session(TLSSessionCache *cache, SSL_SESSION *session, const std::chrono::steady_clock::time_point &t); // Returns cached session associated |addr|. If no cache entry is // found associated to |addr|, nullptr will be returned. SSL_SESSION *reuse_tls_session(const TLSSessionCache &addr); // Loads certificate form file |filename|. The caller should delete // the returned object using X509_free(). X509 *load_certificate(const char *filename); // Returns TLS version from |v|. The returned value is defined in // OpenSSL header file. This function returns -1 if |v| is not valid // TLS version string. int proto_version_from_string(const std::string_view &v); // Stores fingerprint of |x| in |dst| of length |dstlen|. |md| // specifies hash function to use, and |dstlen| must be large enough // to include hash value (e.g., 32 bytes for SHA-256). This function // returns the number of bytes written in |dst|, or -1. ssize_t get_x509_fingerprint(uint8_t *dst, size_t dstlen, const X509 *x, const EVP_MD *md); // Returns subject name of |x|. If this function fails to get subject // name, it returns an empty string. std::string_view get_x509_subject_name(BlockAllocator &balloc, X509 *x); // Returns issuer name of |x|. If this function fails to get issuer // name, it returns an empty string. std::string_view get_x509_issuer_name(BlockAllocator &balloc, X509 *x); // Returns serial number of |x|. If this function fails to get serial // number, it returns an empty string. number std::string_view get_x509_serial(BlockAllocator &balloc, X509 *x); // Fills NotBefore of |x| in |t|. This function returns 0 if it // succeeds, or -1. int get_x509_not_before(time_t &t, X509 *x); // Fills NotAfter of |x| in |t|. This function returns 0 if it // succeeds, or -1. int get_x509_not_after(time_t &t, X509 *x); } // namespace tls } // namespace shrpx #endif // !defined(SHRPX_TLS_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_memcached_connection.h0000644000000000000000000000013115077107270020570 xustar0029 mtime=1761382072.99744412 30 atime=1761382106.143310696 30 ctime=1761382109.139300268 nghttp2-1.68.0/src/shrpx_memcached_connection.h0000644000175100017510000001075615077107270021172 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_MEMCACHED_CONNECTION_H #define SHRPX_MEMCACHED_CONNECTION_H #include "shrpx.h" #include #include #include #include "shrpx_connection.h" #include "shrpx_tls.h" #include "shrpx_connect_blocker.h" #include "buffer.h" #include "network.h" using namespace nghttp2; namespace shrpx { struct MemcachedRequest; enum class MemcachedOp : uint8_t; enum class MemcachedStatusCode : uint16_t; enum class MemcachedParseState { HEADER24, EXTRA, VALUE, }; // Stores state when parsing response from memcached server struct MemcachedParseContext { // Buffer for value, dynamically allocated. std::vector value; // cas in response uint64_t cas; // keylen in response size_t keylen; // extralen in response size_t extralen; // totalbody in response. The length of value is totalbody - // extralen - keylen. size_t totalbody; // Number of bytes left to read variable length field. size_t read_left; // Parser state; see enum above MemcachedParseState state; // status_code in response MemcachedStatusCode status_code; // op in response MemcachedOp op; }; struct MemcachedSendbuf { // Buffer for header + extra + key Buffer<512> headbuf; // MemcachedRequest associated to this object MemcachedRequest *req; // Number of bytes left when sending value size_t send_value_left; // Returns the number of bytes this object transmits. size_t left() const { return headbuf.rleft() + send_value_left; } }; inline constexpr uint8_t MEMCACHED_REQ_MAGIC = 0x80; inline constexpr uint8_t MEMCACHED_RES_MAGIC = 0x81; // MemcachedConnection implements part of memcached binary protocol. // This is not full brown implementation. Just the part we need is // implemented. We only use GET and ADD. // // https://github.com/memcached/memcached/blob/master/doc/protocol-binary.xml // https://code.google.com/p/memcached/wiki/MemcacheBinaryProtocol class MemcachedConnection { public: MemcachedConnection(const Address *addr, struct ev_loop *loop, SSL_CTX *ssl_ctx, const std::string_view &sni_name, MemchunkPool *mcpool, std::mt19937 &gen); ~MemcachedConnection(); void disconnect(); int add_request(std::unique_ptr req); int initiate_connection(); int connected(); int on_write(); int on_read(); int write_clear(); int read_clear(); int tls_handshake(); int write_tls(); int read_tls(); size_t fill_request_buffer(struct iovec *iov, size_t iovlen); void drain_send_queue(size_t nwrite); void make_request(MemcachedSendbuf *sendbuf, MemcachedRequest *req); int parse_packet(); size_t serialized_size(MemcachedRequest *req); void signal_write(); int noop(); void reconnect_or_fail(); private: Connection conn_; std::deque> recvq_; std::deque> sendq_; std::deque sendbufv_; std::function do_read_, do_write_; std::string_view sni_name_; tls::TLSSessionCache tls_session_cache_; ConnectBlocker connect_blocker_; MemcachedParseContext parse_state_; const Address *addr_; SSL_CTX *ssl_ctx_; // Sum of the bytes to be transmitted in sendbufv_. size_t sendsum_; size_t try_count_; bool connected_; Buffer<8_k> recvbuf_; }; } // namespace shrpx #endif // !defined(SHRPX_MEMCACHED_CONNECTION_H) nghttp2-1.68.0/src/PaxHeaders/tls.cc0000644000000000000000000000013115077107271014160 xustar0030 mtime=1761382073.001444102 29 atime=1761382080.10641148 30 ctime=1761382109.069300471 nghttp2-1.68.0/src/tls.cc0000644000175100017510000001576615077107271014570 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "tls.h" #include #include #include #include #include #include #ifdef HAVE_LIBBROTLI # include # include #endif // defined(HAVE_LIBBROTLI) #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) namespace nghttp2 { namespace tls { std::string_view get_tls_protocol(SSL *ssl) { switch (SSL_version(ssl)) { case SSL2_VERSION: return "SSLv2"sv; case SSL3_VERSION: return "SSLv3"sv; #ifdef TLS1_3_VERSION case TLS1_3_VERSION: return "TLSv1.3"sv; #endif // defined(TLS1_3_VERSION) case TLS1_2_VERSION: return "TLSv1.2"sv; case TLS1_1_VERSION: return "TLSv1.1"sv; case TLS1_VERSION: return "TLSv1"sv; default: return "unknown"sv; } } TLSSessionInfo *get_tls_session_info(TLSSessionInfo *tls_info, SSL *ssl) { if (!ssl) { return nullptr; } auto session = SSL_get_session(ssl); if (!session) { return nullptr; } tls_info->cipher = SSL_get_cipher_name(ssl); tls_info->protocol = get_tls_protocol(ssl); tls_info->session_reused = SSL_session_reused(ssl); unsigned int session_id_length; tls_info->session_id = SSL_SESSION_get_id(session, &session_id_length); tls_info->session_id_length = session_id_length; return tls_info; } /* Conditional logic w/ lookup tables to check if id is one of the the block listed cipher suites for HTTP/2 described in RFC 7540. https://github.com/jay/http2_blacklisted_ciphers */ #define IS_CIPHER_BANNED_METHOD2(id) \ ((0x0000 <= id && id <= 0x00FF && \ "\xFF\xFF\xFF\xCF\xFF\xFF\xFF\xFF\x7F\x00\x00\x00\x80\x3F\x00\x00" \ "\xF0\xFF\xFF\x3F\xF3\xF3\xFF\xFF\x3F\x00\x00\x00\x00\x00\x00\x80" \ [(id & 0xFF) / 8] & \ (1 << (id % 8))) || \ (0xC000 <= id && id <= 0xC0FF && \ "\xFE\xFF\xFF\xFF\xFF\x67\xFE\xFF\xFF\xFF\x33\xCF\xFC\xCF\xFF\xCF" \ "\x3C\xF3\xFC\x3F\x33\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \ [(id & 0xFF) / 8] & \ (1 << (id % 8)))) bool check_http2_cipher_block_list(SSL *ssl) { int id = SSL_CIPHER_get_id(SSL_get_current_cipher(ssl)) & 0xFFFFFF; return IS_CIPHER_BANNED_METHOD2(id); } bool check_http2_tls_version(SSL *ssl) { auto tls_ver = SSL_version(ssl); return tls_ver >= TLS1_2_VERSION; } bool check_http2_requirement(SSL *ssl) { return check_http2_tls_version(ssl) && !check_http2_cipher_block_list(ssl); } int ssl_ctx_set_proto_versions(SSL_CTX *ssl_ctx, int min, int max) { if (SSL_CTX_set_min_proto_version( ssl_ctx, static_cast(min)) != 1 || SSL_CTX_set_max_proto_version( ssl_ctx, static_cast(max)) != 1) { return -1; } return 0; } #if defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && defined(HAVE_LIBBROTLI) int cert_compress(SSL *ssl, CBB *out, const uint8_t *in, size_t in_len) { uint8_t *dest; auto compressed_size = BrotliEncoderMaxCompressedSize(in_len); if (compressed_size == 0) { return 0; } if (!CBB_reserve(out, &dest, compressed_size)) { return 0; } if (BrotliEncoderCompress(BROTLI_MAX_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_MODE_GENERIC, in_len, in, &compressed_size, dest) != BROTLI_TRUE) { return 0; } if (!CBB_did_write(out, compressed_size)) { return 0; } return 1; } int cert_decompress(SSL *ssl, CRYPTO_BUFFER **out, size_t uncompressed_len, const uint8_t *in, size_t in_len) { uint8_t *dest; auto buf = CRYPTO_BUFFER_alloc(&dest, uncompressed_len); auto len = uncompressed_len; if (BrotliDecoderDecompress(in_len, in, &len, dest) != BROTLI_DECODER_RESULT_SUCCESS) { CRYPTO_BUFFER_free(buf); return 0; } if (uncompressed_len != len) { CRYPTO_BUFFER_free(buf); return 0; } *out = buf; return 1; } #endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && // defined(HAVE_LIBBROTLI) #if defined(NGHTTP2_GENUINE_OPENSSL) || \ defined(NGHTTP2_OPENSSL_IS_BORINGSSL) || \ defined(NGHTTP2_OPENSSL_IS_LIBRESSL) || \ (defined(NGHTTP2_OPENSSL_IS_WOLFSSL) && defined(HAVE_SECRET_CALLBACK)) namespace { std::ofstream keylog_file; void keylog_callback(const SSL *ssl, const char *line) { keylog_file.write(line, static_cast(strlen(line))); keylog_file.put('\n'); keylog_file.flush(); } } // namespace int setup_keylog_callback(SSL_CTX *ssl_ctx) { auto keylog_filename = getenv("SSLKEYLOGFILE"); if (!keylog_filename) { return 0; } keylog_file.open(keylog_filename, std::ios_base::app); if (!keylog_file) { return -1; } SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); return 0; } #else // !defined(NGHTTP2_GENUINE_OPENSSL) && // !defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && // !defined(NGHTTP2_OPENSSL_IS_LIBRESSL) && // (!defined(NGHTTP2_OPENSSL_IS_WOLFSSL) || // !defined(HAVE_SECRET_CALLBACK)) int setup_keylog_callback(SSL_CTX *ssl_ctx) { return 0; } #endif // !defined(NGHTTP2_GENUINE_OPENSSL) && // !defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && // !defined(NGHTTP2_OPENSSL_IS_LIBRESSL) && // (!defined(NGHTTP2_OPENSSL_IS_WOLFSSL) || // !defined(HAVE_SECRET_CALLBACK)) } // namespace tls } // namespace nghttp2 nghttp2-1.68.0/src/PaxHeaders/timegm.c0000644000000000000000000000013115077107271014475 xustar0030 mtime=1761382073.001444102 29 atime=1761382080.10641148 30 ctime=1761382109.063300488 nghttp2-1.68.0/src/timegm.c0000644000175100017510000000546015077107271015073 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "timegm.h" #include /* Counter the number of leap year in the range [0, y). The |y| is the year, including century (e.g., 2012) */ static int count_leap_year(int y) { y -= 1; return y / 4 - y / 100 + y / 400; } /* Based on the algorithm of Python 2.7 calendar.timegm. */ time_t nghttp2_timegm(struct tm *tm) { int days; int num_leap_year; int64_t t; if (tm->tm_mon > 11) { return -1; } num_leap_year = count_leap_year(tm->tm_year + 1900) - count_leap_year(1970); days = (tm->tm_year - 70) * 365 + num_leap_year + tm->tm_yday; t = ((int64_t)days * 24 + tm->tm_hour) * 3600 + tm->tm_min * 60 + tm->tm_sec; if (sizeof(time_t) == 4) { if (t < INT32_MIN || t > INT32_MAX) { return -1; } } return (time_t)t; } /* Returns nonzero if the |y| is the leap year. The |y| is the year, including century (e.g., 2012) */ static int is_leap_year(int y) { return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0); } /* The number of days before ith month begins */ static int daysum[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; time_t nghttp2_timegm_without_yday(struct tm *tm) { int days; int num_leap_year; int64_t t; if (tm->tm_mon > 11) { return -1; } num_leap_year = count_leap_year(tm->tm_year + 1900) - count_leap_year(1970); days = (tm->tm_year - 70) * 365 + num_leap_year + daysum[tm->tm_mon] + tm->tm_mday - 1; if (tm->tm_mon >= 2 && is_leap_year(tm->tm_year + 1900)) { ++days; } t = ((int64_t)days * 24 + tm->tm_hour) * 3600 + tm->tm_min * 60 + tm->tm_sec; if (sizeof(time_t) == 4) { if (t < INT32_MIN || t > INT32_MAX) { return -1; } } return (time_t)t; } nghttp2-1.68.0/src/PaxHeaders/shrpx_mruby_module_response.cc0000644000000000000000000000013215077107270021223 xustar0030 mtime=1761382072.998444116 30 atime=1761382106.189310493 30 ctime=1761382109.185300135 nghttp2-1.68.0/src/shrpx_mruby_module_response.cc0000644000175100017510000002716515077107270021626 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_mruby_module_response.h" #include #include #include #include #include "shrpx_downstream.h" #include "shrpx_upstream.h" #include "shrpx_client_handler.h" #include "shrpx_mruby.h" #include "shrpx_mruby_module.h" #include "shrpx_log.h" #include "util.h" #include "http2.h" namespace shrpx { namespace mruby { namespace { mrb_value response_init(mrb_state *mrb, mrb_value self) { return self; } } // namespace namespace { mrb_value response_get_http_version_major(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; const auto &resp = downstream->response(); return mrb_fixnum_value(resp.http_major); } } // namespace namespace { mrb_value response_get_http_version_minor(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; const auto &resp = downstream->response(); return mrb_fixnum_value(resp.http_minor); } } // namespace namespace { mrb_value response_get_status(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; const auto &resp = downstream->response(); return mrb_fixnum_value(resp.http_status); } } // namespace namespace { mrb_value response_set_status(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto &resp = downstream->response(); mrb_int status; mrb_get_args(mrb, "i", &status); // We don't support 1xx status code for mruby scripting yet. if (status < 200 || status > 999) { mrb_raise(mrb, E_RUNTIME_ERROR, "invalid status; it should be [200, 999], inclusive"); } resp.http_status = static_cast(status); return self; } } // namespace namespace { mrb_value response_get_headers(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; const auto &resp = downstream->response(); return create_headers_hash(mrb, resp.fs.headers()); } } // namespace namespace { mrb_value response_mod_header(mrb_state *mrb, mrb_value self, bool repl) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto &resp = downstream->response(); auto &balloc = downstream->get_block_allocator(); mrb_value key, values; mrb_get_args(mrb, "So", &key, &values); if (RSTRING_LEN(key) == 0) { mrb_raise(mrb, E_RUNTIME_ERROR, "empty key is not allowed"); } auto ai = mrb_gc_arena_save(mrb); key = mrb_funcall(mrb, key, "downcase", 0); auto keyref = make_string_ref( balloc, std::string_view{RSTRING_PTR(key), static_cast(RSTRING_LEN(key))}); mrb_gc_arena_restore(mrb, ai); auto token = http2::lookup_token(keyref); if (repl) { size_t p = 0; auto &headers = resp.fs.headers(); for (size_t i = 0; i < headers.size(); ++i) { auto &kv = headers[i]; if (kv.name == keyref) { continue; } if (i != p) { headers[p] = std::move(kv); } ++p; } headers.resize(p); } if (mrb_array_p(values)) { auto n = RARRAY_LEN(values); for (int i = 0; i < n; ++i) { auto value = mrb_ary_ref(mrb, values, i); if (!mrb_string_p(value)) { mrb_raise(mrb, E_RUNTIME_ERROR, "value must be string"); } resp.fs.add_header_token( keyref, make_string_ref( balloc, std::string_view{RSTRING_PTR(value), static_cast(RSTRING_LEN(value))}), false, token); } } else if (mrb_string_p(values)) { resp.fs.add_header_token( keyref, make_string_ref( balloc, std::string_view{RSTRING_PTR(values), static_cast(RSTRING_LEN(values))}), false, token); } else { mrb_raise(mrb, E_RUNTIME_ERROR, "value must be string"); } return mrb_nil_value(); } } // namespace namespace { mrb_value response_set_header(mrb_state *mrb, mrb_value self) { return response_mod_header(mrb, self, true); } } // namespace namespace { mrb_value response_add_header(mrb_state *mrb, mrb_value self) { return response_mod_header(mrb, self, false); } } // namespace namespace { mrb_value response_clear_headers(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto &resp = downstream->response(); resp.fs.clear_headers(); return mrb_nil_value(); } } // namespace namespace { mrb_value response_return(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto &req = downstream->request(); auto &resp = downstream->response(); int rv; auto &balloc = downstream->get_block_allocator(); if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { mrb_raise(mrb, E_RUNTIME_ERROR, "response has already been committed"); } const char *val; mrb_int vallen; mrb_get_args(mrb, "|s", &val, &vallen); const uint8_t *body = nullptr; size_t bodylen = 0; if (resp.http_status == 0) { resp.http_status = 200; } if (downstream->expect_response_body() && vallen > 0) { body = reinterpret_cast(val); bodylen = as_unsigned(vallen); } auto cl = resp.fs.header(http2::HD_CONTENT_LENGTH); if (resp.http_status == 204 || (resp.http_status == 200 && req.method == HTTP_CONNECT)) { if (cl) { // Delete content-length here http2::erase_header(cl); } resp.fs.content_length = -1; } else { auto content_length = util::make_string_ref_uint(balloc, as_unsigned(vallen)); if (cl) { cl->value = content_length; } else { resp.fs.add_header_token("content-length"sv, content_length, false, http2::HD_CONTENT_LENGTH); } resp.fs.content_length = vallen; } auto date = resp.fs.header(http2::HD_DATE); if (!date) { auto lgconf = log_config(); lgconf->update_tstamp(std::chrono::system_clock::now()); resp.fs.add_header_token("date"sv, make_string_ref(balloc, lgconf->tstamp->time_http), false, http2::HD_DATE); } auto upstream = downstream->get_upstream(); rv = upstream->send_reply(downstream, body, bodylen); if (rv != 0) { mrb_raise(mrb, E_RUNTIME_ERROR, "could not send response"); } auto handler = upstream->get_client_handler(); handler->signal_write(); return self; } } // namespace namespace { mrb_value response_send_info(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; auto &resp = downstream->response(); int rv; if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { mrb_raise(mrb, E_RUNTIME_ERROR, "response has already been committed"); } mrb_int http_status; mrb_value hash; mrb_get_args(mrb, "iH", &http_status, &hash); if (http_status / 100 != 1) { mrb_raise(mrb, E_RUNTIME_ERROR, "status_code must be in range [100, 199], inclusive"); } auto &balloc = downstream->get_block_allocator(); auto keys = mrb_hash_keys(mrb, hash); auto keyslen = RARRAY_LEN(keys); for (int i = 0; i < keyslen; ++i) { auto key = mrb_ary_ref(mrb, keys, i); if (!mrb_string_p(key)) { mrb_raise(mrb, E_RUNTIME_ERROR, "key must be string"); } auto values = mrb_hash_get(mrb, hash, key); auto ai = mrb_gc_arena_save(mrb); key = mrb_funcall(mrb, key, "downcase", 0); auto keyref = make_string_ref( balloc, std::string_view{RSTRING_PTR(key), static_cast(RSTRING_LEN(key))}); mrb_gc_arena_restore(mrb, ai); auto token = http2::lookup_token(keyref); if (mrb_array_p(values)) { auto n = RARRAY_LEN(values); for (int i = 0; i < n; ++i) { auto value = mrb_ary_ref(mrb, values, i); if (!mrb_string_p(value)) { mrb_raise(mrb, E_RUNTIME_ERROR, "value must be string"); } resp.fs.add_header_token( keyref, make_string_ref( balloc, std::string_view{RSTRING_PTR(value), static_cast(RSTRING_LEN(value))}), false, token); } } else if (mrb_string_p(values)) { resp.fs.add_header_token( keyref, make_string_ref( balloc, std::string_view{RSTRING_PTR(values), static_cast(RSTRING_LEN(values))}), false, token); } else { mrb_raise(mrb, E_RUNTIME_ERROR, "value must be string"); } } resp.http_status = static_cast(http_status); auto upstream = downstream->get_upstream(); rv = upstream->on_downstream_header_complete(downstream); if (rv != 0) { mrb_raise(mrb, E_RUNTIME_ERROR, "could not send non-final response"); } auto handler = upstream->get_client_handler(); handler->signal_write(); return self; } } // namespace void init_response_class(mrb_state *mrb, RClass *module) { auto response_class = mrb_define_class_under(mrb, module, "Response", mrb->object_class); mrb_define_method(mrb, response_class, "initialize", response_init, MRB_ARGS_NONE()); mrb_define_method(mrb, response_class, "http_version_major", response_get_http_version_major, MRB_ARGS_NONE()); mrb_define_method(mrb, response_class, "http_version_minor", response_get_http_version_minor, MRB_ARGS_NONE()); mrb_define_method(mrb, response_class, "status", response_get_status, MRB_ARGS_NONE()); mrb_define_method(mrb, response_class, "status=", response_set_status, MRB_ARGS_REQ(1)); mrb_define_method(mrb, response_class, "headers", response_get_headers, MRB_ARGS_NONE()); mrb_define_method(mrb, response_class, "add_header", response_add_header, MRB_ARGS_REQ(2)); mrb_define_method(mrb, response_class, "set_header", response_set_header, MRB_ARGS_REQ(2)); mrb_define_method(mrb, response_class, "clear_headers", response_clear_headers, MRB_ARGS_NONE()); mrb_define_method(mrb, response_class, "return", response_return, MRB_ARGS_OPT(1)); mrb_define_method(mrb, response_class, "send_info", response_send_info, MRB_ARGS_REQ(2)); } } // namespace mruby } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_log_config.cc0000644000000000000000000000013215077107270016710 xustar0030 mtime=1761382072.996444125 30 atime=1761382106.124310779 30 ctime=1761382109.120300323 nghttp2-1.68.0/src/shrpx_log_config.cc0000644000175100017510000000552315077107270017305 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_log_config.h" #include #include #include #include "util.h" using namespace nghttp2; namespace shrpx { Timestamp::Timestamp(const std::chrono::system_clock::time_point &tp) { time_local = util::format_common_log(time_local_buf.data(), tp); time_iso8601 = util::format_iso8601(time_iso8601_buf.data(), tp); time_http = util::format_http_date(time_http_buf.data(), tp); } LogConfig::LogConfig() : time_str_updated(std::chrono::system_clock::now()), tstamp(std::make_shared(time_str_updated)), pid(getpid()), accesslog_fd(-1), errorlog_fd(-1), errorlog_tty(false) { auto tid = std::this_thread::get_id(); auto tid_hash = util::hash32(std::string_view{reinterpret_cast(&tid), sizeof(tid)}); thread_id = util::format_hex(as_uint8_span(std::span{&tid_hash, 1})); } LogConfig *log_config() { static thread_local LogConfig config; return &config; } void LogConfig::update_tstamp_millis( const std::chrono::system_clock::time_point &now) { if (std::chrono::duration_cast( now.time_since_epoch()) == std::chrono::duration_cast( time_str_updated.time_since_epoch())) { return; } time_str_updated = now; tstamp = std::make_shared(now); } void LogConfig::update_tstamp( const std::chrono::system_clock::time_point &now) { if (std::chrono::duration_cast( now.time_since_epoch()) == std::chrono::duration_cast( time_str_updated.time_since_epoch())) { return; } time_str_updated = now; tstamp = std::make_shared(now); } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_worker_process.h0000644000000000000000000000013215077107271017514 xustar0030 mtime=1761382073.000444106 30 atime=1761382080.105411485 30 ctime=1761382109.144300254 nghttp2-1.68.0/src/shrpx_worker_process.h0000644000175100017510000000441315077107271020106 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_WORKER_PROCESS_H #define SHRPX_WORKER_PROCESS_H #include "shrpx.h" #include #include #include "shrpx_connection_handler.h" #ifdef ENABLE_HTTP3 # include "shrpx_quic.h" #endif // defined(ENABLE_HTTP3) namespace shrpx { class ConnectionHandler; struct WorkerProcessConfig { // IPC socket to read event from main process int ipc_fd; // IPC socket to tell that a worker process is ready for service. int ready_ipc_fd; // IPv4 or UNIX domain socket, or -1 if not used int server_fd; // IPv6 socket, or -1 if not used int server_fd6; #ifdef ENABLE_HTTP3 // Worker IDs for the new worker process. std::vector worker_ids; // IPC socket to read forwarded QUIC UDP datagram from the current // worker process. int quic_ipc_fd; // Lingering worker processes which were created before this worker // process to forward QUIC UDP datagram during reload. std::vector quic_lingering_worker_processes; #endif // defined(ENABLE_HTTP3) }; int worker_process_event_loop(WorkerProcessConfig *wpconf); } // namespace shrpx #endif // !defined(SHRPX_WORKER_PROCESS_H) nghttp2-1.68.0/src/PaxHeaders/Makefile.am0000644000000000000000000000013215077107270015103 xustar0030 mtime=1761382072.983444185 30 atime=1761382085.619386943 30 ctime=1761382109.055300511 nghttp2-1.68.0/src/Makefile.am0000644000175100017510000002106215077107270015474 0ustar00runnerrunner# nghttp2 - HTTP/2 C Library # Copyright (c) 2012 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SUBDIRS = testdata EXTRA_DIST = \ CMakeLists.txt \ test.example.com.pem \ test.nghttp2.org.pem bin_PROGRAMS = check_PROGRAMS = TESTS = AM_CFLAGS = $(WARNCFLAGS) AM_CXXFLAGS = $(WARNCXXFLAGS) $(CXX1XCXXFLAGS) AM_CPPFLAGS = \ -DPKGDATADIR='"$(pkgdatadir)"' \ -DPKGLIBDIR='"$(pkglibdir)"' \ -I$(top_srcdir)/lib/includes \ -I$(top_builddir)/lib/includes \ -I$(top_srcdir)/lib \ -I$(top_srcdir)/third-party/urlparse \ -I$(top_srcdir)/third-party/llhttp/include \ @JEMALLOC_CFLAGS@ \ @LIBXML2_CFLAGS@ \ @LIBEV_CFLAGS@ \ @LIBNGHTTP3_CFLAGS@ \ @LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS@ \ @LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ \ @LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS@ \ @LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ \ @LIBNGTCP2_CRYPTO_OSSL_CFLAGS@ \ @LIBNGTCP2_CFLAGS@ \ @WOLFSSL_CFLAGS@ \ @OPENSSL_CFLAGS@ \ @LIBCARES_CFLAGS@ \ @JANSSON_CFLAGS@ \ @LIBBPF_CFLAGS@ \ @ZLIB_CFLAGS@ \ @LIBBROTLIENC_CFLAGS@ \ @LIBBROTLIDEC_CFLAGS@ \ @EXTRA_DEFS@ \ @DEFS@ AM_LDFLAGS = @LIBTOOL_LDFLAGS@ LDADD = $(top_builddir)/lib/libnghttp2.la \ $(top_builddir)/third-party/liburlparse.la \ $(top_builddir)/third-party/libllhttp.la \ @JEMALLOC_LIBS@ \ @LIBXML2_LIBS@ \ @LIBEV_LIBS@ \ @LIBNGHTTP3_LIBS@ \ @LIBNGTCP2_CRYPTO_WOLFSSL_LIBS@ \ @LIBNGTCP2_CRYPTO_QUICTLS_LIBS@ \ @LIBNGTCP2_CRYPTO_LIBRESSL_LIBS@ \ @LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ \ @LIBNGTCP2_CRYPTO_OSSL_LIBS@ \ @LIBNGTCP2_LIBS@ \ @WOLFSSL_LIBS@ \ @OPENSSL_LIBS@ \ @LIBCARES_LIBS@ \ @SYSTEMD_LIBS@ \ @JANSSON_LIBS@ \ @LIBBPF_LIBS@ \ @ZLIB_LIBS@ \ @LIBBROTLIENC_LIBS@ \ @LIBBROTLIDEC_LIBS@ \ @APPLDFLAGS@ if ENABLE_APP bin_PROGRAMS += nghttp nghttpd nghttpx HELPER_OBJECTS = util.cc \ http2.cc timegm.c app_helper.cc nghttp2_gzip.c HELPER_HFILES = util.h \ http2.h timegm.h app_helper.h nghttp2_config.h \ nghttp2_gzip.h network.h HTML_PARSER_OBJECTS = HTML_PARSER_HFILES = HtmlParser.h if HAVE_LIBXML2 HTML_PARSER_OBJECTS += HtmlParser.cc endif # HAVE_LIBXML2 nghttp_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttp.cc nghttp.h \ ${HTML_PARSER_OBJECTS} ${HTML_PARSER_HFILES} \ tls.cc tls.h ssl_compat.h nghttpd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttpd.cc \ tls.cc tls.h ssl_compat.h \ HttpServer.cc HttpServer.h bin_PROGRAMS += h2load h2load_SOURCES = util.cc util.h \ http2.cc http2.h h2load.cc h2load.h \ timegm.c timegm.h \ tls.cc tls.h ssl_compat.h \ h2load_session.h \ h2load_http2_session.cc h2load_http2_session.h \ h2load_http1_session.cc h2load_http1_session.h if ENABLE_HTTP3 h2load_SOURCES += \ h2load_http3_session.cc h2load_http3_session.h \ h2load_quic.cc h2load_quic.h endif # ENABLE_HTTP3 NGHTTPX_SRCS = \ util.cc util.h http2.cc http2.h timegm.c timegm.h base64.h \ app_helper.cc app_helper.h \ tls.cc tls.h ssl_compat.h \ shrpx_config.cc shrpx_config.h \ shrpx_error.h \ shrpx_accept_handler.cc shrpx_accept_handler.h \ shrpx_connection_handler.cc shrpx_connection_handler.h \ shrpx_client_handler.cc shrpx_client_handler.h \ shrpx_upstream.h \ shrpx_http2_upstream.cc shrpx_http2_upstream.h \ shrpx_https_upstream.cc shrpx_https_upstream.h \ shrpx_downstream.cc shrpx_downstream.h \ shrpx_downstream_connection.cc shrpx_downstream_connection.h \ shrpx_http_downstream_connection.cc shrpx_http_downstream_connection.h \ shrpx_http2_downstream_connection.cc shrpx_http2_downstream_connection.h \ shrpx_http2_session.cc shrpx_http2_session.h \ shrpx_downstream_queue.cc shrpx_downstream_queue.h \ shrpx_log.cc shrpx_log.h \ shrpx_http.cc shrpx_http.h \ shrpx_io_control.cc shrpx_io_control.h \ shrpx_tls.cc shrpx_tls.h \ shrpx_worker.cc shrpx_worker.h \ shrpx_log_config.cc shrpx_log_config.h \ shrpx_connect_blocker.cc shrpx_connect_blocker.h \ shrpx_live_check.cc shrpx_live_check.h \ shrpx_downstream_connection_pool.cc shrpx_downstream_connection_pool.h \ shrpx_rate_limit.cc shrpx_rate_limit.h \ shrpx_connection.cc shrpx_connection.h \ shrpx_memcached_dispatcher.cc shrpx_memcached_dispatcher.h \ shrpx_memcached_connection.cc shrpx_memcached_connection.h \ shrpx_memcached_request.h \ shrpx_memcached_result.h \ shrpx_worker_process.cc shrpx_worker_process.h \ shrpx_process.h \ shrpx_signal.cc shrpx_signal.h \ shrpx_router.cc shrpx_router.h \ shrpx_api_downstream_connection.cc shrpx_api_downstream_connection.h \ shrpx_health_monitor_downstream_connection.cc \ shrpx_health_monitor_downstream_connection.h \ shrpx_null_downstream_connection.cc shrpx_null_downstream_connection.h \ shrpx_dns_resolver.cc shrpx_dns_resolver.h \ shrpx_dual_dns_resolver.cc shrpx_dual_dns_resolver.h \ shrpx_dns_tracker.cc shrpx_dns_tracker.h \ buffer.h memchunk.h template.h allocator.h \ xsi_strerror.c xsi_strerror.h if HAVE_MRUBY NGHTTPX_SRCS += \ shrpx_mruby.cc shrpx_mruby.h \ shrpx_mruby_module.cc shrpx_mruby_module.h \ shrpx_mruby_module_env.cc shrpx_mruby_module_env.h \ shrpx_mruby_module_request.cc shrpx_mruby_module_request.h \ shrpx_mruby_module_response.cc shrpx_mruby_module_response.h endif # HAVE_MRUBY if ENABLE_HTTP3 NGHTTPX_SRCS += \ shrpx_quic.cc shrpx_quic.h \ shrpx_quic_listener.cc shrpx_quic_listener.h \ shrpx_quic_connection_handler.cc shrpx_quic_connection_handler.h \ shrpx_http3_upstream.cc shrpx_http3_upstream.h \ http3.cc http3.h \ siphash.cc siphash.h endif # ENABLE_HTTP3 noinst_LIBRARIES = libnghttpx.a libnghttpx_a_SOURCES = ${NGHTTPX_SRCS} libnghttpx_a_CPPFLAGS = ${AM_CPPFLAGS} nghttpx_SOURCES = shrpx.cc shrpx.h nghttpx_CPPFLAGS = ${libnghttpx_a_CPPFLAGS} nghttpx_LDADD = libnghttpx.a ${LDADD} if HAVE_MRUBY libnghttpx_a_CPPFLAGS += \ -I${top_srcdir}/third-party/mruby/include @LIBMRUBY_CFLAGS@ nghttpx_LDADD += -L${top_builddir}/third-party/mruby/build/lib @LIBMRUBY_LIBS@ endif # HAVE_MRUBY if HAVE_NEVERBLEED libnghttpx_a_CPPFLAGS += -I${top_srcdir}/third-party/neverbleed nghttpx_LDADD += ${top_builddir}/third-party/libneverbleed.la endif # HAVE_NEVERBLEED check_PROGRAMS += nghttpx-unittest nghttpx_unittest_SOURCES = shrpx-unittest.cc \ shrpx_tls_test.cc shrpx_tls_test.h \ shrpx_downstream_test.cc shrpx_downstream_test.h \ shrpx_config_test.cc shrpx_config_test.h \ shrpx_worker_test.cc shrpx_worker_test.h \ shrpx_http_test.cc shrpx_http_test.h \ shrpx_router_test.cc shrpx_router_test.h \ http2_test.cc http2_test.h \ util_test.cc util_test.h \ nghttp2_gzip_test.c nghttp2_gzip_test.h \ nghttp2_gzip.c nghttp2_gzip.h \ buffer_test.cc buffer_test.h \ memchunk_test.cc memchunk_test.h \ template_test.cc template_test.h \ base64_test.cc base64_test.h \ $(top_srcdir)/tests/munit/munit.c $(top_srcdir)/tests/munit/munit.h \ $(top_srcdir)/tests/munit/munitxx.h if ENABLE_HTTP3 nghttpx_unittest_SOURCES += siphash_test.cc siphash_test.h endif # ENABLE_HTTP3 nghttpx_unittest_CPPFLAGS = ${AM_CPPFLAGS} \ -I$(top_srcdir)/tests/munit \ -DNGHTTP2_SRC_DIR=\"$(top_srcdir)/src\" nghttpx_unittest_LDADD = libnghttpx.a ${LDADD} @TESTLDADD@ if HAVE_MRUBY nghttpx_unittest_CPPFLAGS += \ -I${top_srcdir}/third-party/mruby/include @LIBMRUBY_CFLAGS@ nghttpx_unittest_LDADD += \ -L${top_builddir}/third-party/mruby/build/lib @LIBMRUBY_LIBS@ endif # HAVE_MRUBY if HAVE_NEVERBLEED nghttpx_unittest_CPPFLAGS += -I${top_srcdir}/third-party/neverbleed nghttpx_unittest_LDADD += ${top_builddir}/third-party/libneverbleed.la endif # HAVE_NEVERBLEED TESTS += nghttpx-unittest endif # ENABLE_APP if ENABLE_HPACK_TOOLS bin_PROGRAMS += inflatehd deflatehd HPACK_TOOLS_COMMON_SRCS = \ comp_helper.c comp_helper.h \ util.cc util.h \ timegm.c timegm.h inflatehd_SOURCES = inflatehd.cc $(HPACK_TOOLS_COMMON_SRCS) deflatehd_SOURCES = deflatehd.cc $(HPACK_TOOLS_COMMON_SRCS) endif # ENABLE_HPACK_TOOLS nghttp2-1.68.0/src/PaxHeaders/shrpx_memcached_dispatcher.h0000644000000000000000000000013115077107270020557 xustar0029 mtime=1761382072.99744412 30 atime=1761382106.141310704 30 ctime=1761382109.137300274 nghttp2-1.68.0/src/shrpx_memcached_dispatcher.h0000644000175100017510000000414615077107270021155 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_MEMCACHED_DISPATCHER_H #define SHRPX_MEMCACHED_DISPATCHER_H #include "shrpx.h" #include #include #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !NGHTTP2_OPENSSL_IS_WOLFSSL # include #endif // !NGHTTP2_OPENSSL_IS_WOLFSSL #include "memchunk.h" #include "network.h" using namespace nghttp2; namespace shrpx { struct MemcachedRequest; class MemcachedConnection; class MemcachedDispatcher { public: MemcachedDispatcher(const Address *addr, struct ev_loop *loop, SSL_CTX *ssl_ctx, const std::string_view &sni_name, MemchunkPool *mcpool, std::mt19937 &gen); ~MemcachedDispatcher(); int add_request(std::unique_ptr req); private: struct ev_loop *loop_; std::unique_ptr mconn_; }; } // namespace shrpx #endif // SHRPX_MEMCACHED_DISPATCHER_H nghttp2-1.68.0/src/PaxHeaders/shrpx_downstream_connection_pool.cc0000644000000000000000000000013015077107270022233 xustar0030 mtime=1761382072.993444139 30 atime=1761382106.132310744 28 ctime=1761382109.1283003 nghttp2-1.68.0/src/shrpx_downstream_connection_pool.cc0000644000175100017510000000405515077107270022631 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_downstream_connection_pool.h" #include "shrpx_downstream_connection.h" namespace shrpx { DownstreamConnectionPool::DownstreamConnectionPool() {} DownstreamConnectionPool::~DownstreamConnectionPool() { remove_all(); } void DownstreamConnectionPool::remove_all() { for (auto dconn : pool_) { delete dconn; } pool_.clear(); } void DownstreamConnectionPool::add_downstream_connection( std::unique_ptr dconn) { pool_.insert(dconn.release()); } std::unique_ptr DownstreamConnectionPool::pop_downstream_connection() { if (pool_.empty()) { return nullptr; } auto it = std::ranges::begin(pool_); auto dconn = std::unique_ptr(*it); pool_.erase(it); return dconn; } void DownstreamConnectionPool::remove_downstream_connection( DownstreamConnection *dconn) { pool_.erase(dconn); delete dconn; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_http2_session.cc0000644000000000000000000000013215077107270017406 xustar0030 mtime=1761382072.994444134 30 atime=1761382106.106310859 30 ctime=1761382109.102300375 nghttp2-1.68.0/src/shrpx_http2_session.cc0000644000175100017510000020705215077107270020004 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_http2_session.h" #include #ifdef HAVE_UNISTD_H # include #endif // defined(HAVE_UNISTD_H) #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include "shrpx_upstream.h" #include "shrpx_downstream.h" #include "shrpx_config.h" #include "shrpx_error.h" #include "shrpx_http2_downstream_connection.h" #include "shrpx_client_handler.h" #include "shrpx_tls.h" #include "shrpx_http.h" #include "shrpx_worker.h" #include "shrpx_connect_blocker.h" #include "shrpx_log.h" #include "http2.h" #include "util.h" #include "base64.h" #include "tls.h" using namespace nghttp2; namespace shrpx { namespace { constexpr ev_tstamp CONNCHK_TIMEOUT = 5.; constexpr ev_tstamp CONNCHK_PING_TIMEOUT = 1.; } // namespace namespace { constexpr size_t MAX_BUFFER_SIZE = 32_k; } // namespace namespace { void connchk_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto http2session = static_cast(w->data); ev_timer_stop(loop, w); switch (http2session->get_connection_check_state()) { case ConnectionCheck::STARTED: // ping timeout; disconnect if (LOG_ENABLED(INFO)) { SSLOG(INFO, http2session) << "ping timeout"; } delete http2session; return; default: if (LOG_ENABLED(INFO)) { SSLOG(INFO, http2session) << "connection check required"; } http2session->set_connection_check_state(ConnectionCheck::REQUIRED); } } } // namespace namespace { void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto http2session = static_cast(w->data); if (LOG_ENABLED(INFO)) { SSLOG(INFO, http2session) << "SETTINGS timeout"; } downstream_failure(http2session->get_addr(), http2session->get_raddr()); if (http2session->terminate_session(NGHTTP2_SETTINGS_TIMEOUT) != 0) { delete http2session; return; } http2session->signal_write(); } } // namespace namespace { void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { auto conn = static_cast(w->data); auto http2session = static_cast(conn->data); if (w == &conn->rt && !conn->expired_rt()) { return; } if (LOG_ENABLED(INFO)) { SSLOG(INFO, http2session) << "Timeout"; } http2session->on_timeout(); delete http2session; } } // namespace namespace { void readcb(struct ev_loop *loop, ev_io *w, int revents) { int rv; auto conn = static_cast(w->data); auto http2session = static_cast(conn->data); rv = http2session->do_read(); if (rv != 0) { delete http2session; return; } http2session->connection_alive(); } } // namespace namespace { void writecb(struct ev_loop *loop, ev_io *w, int revents) { int rv; auto conn = static_cast(w->data); auto http2session = static_cast(conn->data); rv = http2session->do_write(); if (rv != 0) { delete http2session; return; } http2session->reset_connection_check_timer_if_not_checking(); } } // namespace namespace { void initiate_connection_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto http2session = static_cast(w->data); ev_timer_stop(loop, w); if (http2session->initiate_connection() != 0) { if (LOG_ENABLED(INFO)) { SSLOG(INFO, http2session) << "Could not initiate backend connection"; } delete http2session; return; } } } // namespace namespace { void prepare_cb(struct ev_loop *loop, ev_prepare *w, int revents) { auto http2session = static_cast(w->data); http2session->check_retire(); } } // namespace Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, Worker *worker, const std::shared_ptr &group, DownstreamAddr *addr) : dlnext(nullptr), dlprev(nullptr), conn_(loop, -1, nullptr, worker->get_mcpool(), group->shared_addr->timeout.write, group->shared_addr->timeout.read, {}, {}, writecb, readcb, timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold, get_config()->tls.dyn_rec.idle_timeout, Proto::HTTP2), wb_(worker->get_mcpool()), worker_(worker), ssl_ctx_(ssl_ctx), group_(group), addr_(addr), session_(nullptr), raddr_(nullptr), state_(Http2SessionState::DISCONNECTED), connection_check_state_(ConnectionCheck::NONE), freelist_zone_(FreelistZone::NONE), settings_recved_(false), allow_connect_proto_(false) { read_ = write_ = &Http2Session::noop; on_read_ = &Http2Session::read_noop; on_write_ = &Http2Session::write_noop; // We will reuse this many times, so use repeat timeout value. The // timeout value is set later. ev_timer_init(&connchk_timer_, connchk_timeout_cb, 0., 0.); connchk_timer_.data = this; // SETTINGS ACK timeout is 10 seconds for now. We will reuse this // many times, so use repeat timeout value. ev_timer_init(&settings_timer_, settings_timeout_cb, 0., 0.); settings_timer_.data = this; ev_timer_init(&initiate_connection_timer_, initiate_connection_cb, 0., 0.); initiate_connection_timer_.data = this; ev_prepare_init(&prep_, prepare_cb); prep_.data = this; ev_prepare_start(loop, &prep_); } Http2Session::~Http2Session() { exclude_from_scheduling(); disconnect(should_hard_fail()); } int Http2Session::disconnect(bool hard) { if (LOG_ENABLED(INFO)) { SSLOG(INFO, this) << "Disconnecting"; } nghttp2_session_del(session_); session_ = nullptr; wb_.reset(); if (dns_query_) { auto dns_tracker = worker_->get_dns_tracker(); dns_tracker->cancel(dns_query_.get()); } conn_.rlimit.stopw(); conn_.wlimit.stopw(); ev_prepare_stop(conn_.loop, &prep_); ev_timer_stop(conn_.loop, &initiate_connection_timer_); ev_timer_stop(conn_.loop, &settings_timer_); ev_timer_stop(conn_.loop, &connchk_timer_); read_ = write_ = &Http2Session::noop; on_read_ = &Http2Session::read_noop; on_write_ = &Http2Session::write_noop; conn_.disconnect(); if (proxy_htp_) { proxy_htp_.reset(); } connection_check_state_ = ConnectionCheck::NONE; state_ = Http2SessionState::DISCONNECTED; // When deleting Http2DownstreamConnection, it calls this object's // remove_downstream_connection(). The multiple // Http2DownstreamConnection objects belong to the same // ClientHandler object if upstream is h2. So be careful when you // delete ClientHandler here. // // We allow creating new pending Http2DownstreamConnection with this // object. Upstream::on_downstream_reset() may add // Http2DownstreamConnection to another Http2Session. for (auto dc = dconns_.head; dc;) { auto next = dc->dlnext; auto downstream = dc->get_downstream(); auto upstream = downstream->get_upstream(); // Failure is allowed only for HTTP/1 upstream where upstream is // not shared by multiple Downstreams. if (upstream->on_downstream_reset(downstream, hard) != 0) { delete upstream->get_client_handler(); } // dc was deleted dc = next; } auto streams = std::move(streams_); for (auto s = streams.head; s;) { auto next = s->dlnext; delete s; s = next; } return 0; } int Http2Session::resolve_name() { auto dns_query = std::make_unique( addr_->host, [this](DNSResolverStatus status, const Address *result) { int rv; if (status == DNSResolverStatus::OK) { *resolved_addr_ = *result; util::set_port(*this->resolved_addr_, this->addr_->port); } rv = this->initiate_connection(); if (rv != 0) { delete this; } }); resolved_addr_ = std::make_unique
(); auto dns_tracker = worker_->get_dns_tracker(); switch (dns_tracker->resolve(resolved_addr_.get(), dns_query.get())) { case DNSResolverStatus::ERROR: return -1; case DNSResolverStatus::RUNNING: dns_query_ = std::move(dns_query); state_ = Http2SessionState::RESOLVING_NAME; return 0; case DNSResolverStatus::OK: util::set_port(*resolved_addr_, addr_->port); return 0; default: assert(0); abort(); } } namespace { int htp_hdrs_completecb(llhttp_t *htp); } // namespace namespace { constexpr llhttp_settings_t htp_hooks = { .on_headers_complete = htp_hdrs_completecb, }; } // namespace int Http2Session::initiate_connection() { int rv = 0; auto worker_blocker = worker_->get_connect_blocker(); if (state_ == Http2SessionState::DISCONNECTED || state_ == Http2SessionState::RESOLVING_NAME) { if (worker_blocker->blocked()) { if (LOG_ENABLED(INFO)) { SSLOG(INFO, this) << "Worker wide backend connection was blocked temporarily"; } return -1; } } auto &downstreamconf = *get_config()->conn.downstream; const auto &proxy = get_config()->downstream_http_proxy; if (!proxy.host.empty() && state_ == Http2SessionState::DISCONNECTED) { if (LOG_ENABLED(INFO)) { SSLOG(INFO, this) << "Connecting to the proxy " << proxy.host << ":" << proxy.port; } conn_.fd = util::create_nonblock_socket(proxy.addr.su.storage.ss_family); if (conn_.fd == -1) { auto error = errno; SSLOG(WARN, this) << "Backend proxy socket() failed; addr=" << util::to_numeric_addr(&proxy.addr) << ", errno=" << error; worker_blocker->on_failure(); return -1; } rv = connect(conn_.fd, &proxy.addr.su.sa, proxy.addr.len); if (rv != 0 && errno != EINPROGRESS) { auto error = errno; SSLOG(WARN, this) << "Backend proxy connect() failed; addr=" << util::to_numeric_addr(&proxy.addr) << ", errno=" << error; worker_blocker->on_failure(); return -1; } raddr_ = &proxy.addr; worker_blocker->on_success(); ev_io_set(&conn_.rev, conn_.fd, EV_READ); ev_io_set(&conn_.wev, conn_.fd, EV_WRITE); conn_.wlimit.startw(); conn_.wt.repeat = downstreamconf.timeout.connect; ev_timer_again(conn_.loop, &conn_.wt); write_ = &Http2Session::connected; on_read_ = &Http2Session::downstream_read_proxy; on_write_ = &Http2Session::downstream_connect_proxy; proxy_htp_ = std::make_unique(); llhttp_init(proxy_htp_.get(), HTTP_RESPONSE, &htp_hooks); proxy_htp_->data = this; state_ = Http2SessionState::PROXY_CONNECTING; return 0; } if (state_ == Http2SessionState::DISCONNECTED || state_ == Http2SessionState::PROXY_CONNECTED || state_ == Http2SessionState::RESOLVING_NAME) { if (LOG_ENABLED(INFO)) { if (state_ != Http2SessionState::RESOLVING_NAME) { SSLOG(INFO, this) << "Connecting to downstream server"; } } if (addr_->tls) { assert(ssl_ctx_); if (state_ != Http2SessionState::RESOLVING_NAME) { auto ssl = tls::create_ssl(ssl_ctx_); if (!ssl) { return -1; } tls::setup_downstream_http2_alpn(ssl); conn_.set_ssl(ssl); conn_.tls.client_session_cache = &addr_->tls_session_cache; auto sni_name = addr_->sni.empty() ? addr_->host : addr_->sni; if (!util::numeric_host(sni_name.data())) { // TLS extensions: SNI. There is no documentation about the return // code for this function (actually this is macro wrapping SSL_ctrl // at the time of this writing). SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name.data()); } auto tls_session = tls::reuse_tls_session(addr_->tls_session_cache); if (tls_session) { SSL_set_session(conn_.tls.ssl, tls_session); SSL_SESSION_free(tls_session); } } if (state_ == Http2SessionState::DISCONNECTED) { if (addr_->dns) { rv = resolve_name(); if (rv != 0) { downstream_failure(addr_, nullptr); return -1; } if (state_ == Http2SessionState::RESOLVING_NAME) { return 0; } raddr_ = resolved_addr_.get(); } else { raddr_ = &addr_->addr; } } if (state_ == Http2SessionState::RESOLVING_NAME) { if (dns_query_->status == DNSResolverStatus::ERROR) { downstream_failure(addr_, nullptr); return -1; } assert(dns_query_->status == DNSResolverStatus::OK); state_ = Http2SessionState::DISCONNECTED; dns_query_.reset(); raddr_ = resolved_addr_.get(); } // If state_ == Http2SessionState::PROXY_CONNECTED, we have // connected to the proxy using conn_.fd and tunnel has been // established. if (state_ == Http2SessionState::DISCONNECTED) { assert(conn_.fd == -1); conn_.fd = util::create_nonblock_socket(raddr_->su.storage.ss_family); if (conn_.fd == -1) { auto error = errno; SSLOG(WARN, this) << "socket() failed; addr=" << util::to_numeric_addr(raddr_) << ", errno=" << error; worker_blocker->on_failure(); return -1; } worker_blocker->on_success(); rv = connect(conn_.fd, // TODO maybe not thread-safe? &raddr_->su.sa, raddr_->len); if (rv != 0 && errno != EINPROGRESS) { auto error = errno; SSLOG(WARN, this) << "connect() failed; addr=" << util::to_numeric_addr(raddr_) << ", errno=" << error; downstream_failure(addr_, raddr_); return -1; } ev_io_set(&conn_.rev, conn_.fd, EV_READ); ev_io_set(&conn_.wev, conn_.fd, EV_WRITE); } conn_.prepare_client_handshake(); } else { if (state_ == Http2SessionState::DISCONNECTED) { // Without TLS and proxy. if (addr_->dns) { rv = resolve_name(); if (rv != 0) { downstream_failure(addr_, nullptr); return -1; } if (state_ == Http2SessionState::RESOLVING_NAME) { return 0; } raddr_ = resolved_addr_.get(); } else { raddr_ = &addr_->addr; } } if (state_ == Http2SessionState::RESOLVING_NAME) { if (dns_query_->status == DNSResolverStatus::ERROR) { downstream_failure(addr_, nullptr); return -1; } assert(dns_query_->status == DNSResolverStatus::OK); state_ = Http2SessionState::DISCONNECTED; dns_query_.reset(); raddr_ = resolved_addr_.get(); } if (state_ == Http2SessionState::DISCONNECTED) { // Without TLS and proxy. assert(conn_.fd == -1); conn_.fd = util::create_nonblock_socket(raddr_->su.storage.ss_family); if (conn_.fd == -1) { auto error = errno; SSLOG(WARN, this) << "socket() failed; addr=" << util::to_numeric_addr(raddr_) << ", errno=" << error; worker_blocker->on_failure(); return -1; } worker_blocker->on_success(); rv = connect(conn_.fd, &raddr_->su.sa, raddr_->len); if (rv != 0 && errno != EINPROGRESS) { auto error = errno; SSLOG(WARN, this) << "connect() failed; addr=" << util::to_numeric_addr(raddr_) << ", errno=" << error; downstream_failure(addr_, raddr_); return -1; } ev_io_set(&conn_.rev, conn_.fd, EV_READ); ev_io_set(&conn_.wev, conn_.fd, EV_WRITE); } } // We have been already connected when no TLS and proxy is used. if (state_ == Http2SessionState::PROXY_CONNECTED) { on_read_ = &Http2Session::read_noop; on_write_ = &Http2Session::write_noop; return connected(); } write_ = &Http2Session::connected; state_ = Http2SessionState::CONNECTING; conn_.wlimit.startw(); conn_.wt.repeat = downstreamconf.timeout.connect; ev_timer_again(conn_.loop, &conn_.wt); return 0; } // Unreachable assert(0); return 0; } namespace { int htp_hdrs_completecb(llhttp_t *htp) { auto http2session = static_cast(htp->data); // We only read HTTP header part. If tunneling succeeds, response // body is a different protocol (HTTP/2 in this case), we don't read // them here. // We just check status code here if (htp->status_code / 100 == 2) { if (LOG_ENABLED(INFO)) { SSLOG(INFO, http2session) << "Tunneling success"; } http2session->set_state(Http2SessionState::PROXY_CONNECTED); return HPE_PAUSED; } SSLOG(WARN, http2session) << "Tunneling failed: " << htp->status_code; http2session->set_state(Http2SessionState::PROXY_FAILED); return HPE_PAUSED; } } // namespace int Http2Session::downstream_read_proxy(const uint8_t *data, size_t datalen) { auto htperr = llhttp_execute(proxy_htp_.get(), reinterpret_cast(data), datalen); if (htperr == HPE_PAUSED) { switch (state_) { case Http2SessionState::PROXY_CONNECTED: // Initiate SSL/TLS handshake through established tunnel. if (initiate_connection() != 0) { return -1; } return 0; case Http2SessionState::PROXY_FAILED: return -1; default: break; } // should not be here assert(0); } if (htperr != HPE_OK) { return -1; } return 0; } int Http2Session::downstream_connect_proxy() { if (LOG_ENABLED(INFO)) { SSLOG(INFO, this) << "Connected to the proxy"; } std::string req = "CONNECT "; req.append(addr_->hostport.data(), addr_->hostport.size()); if (addr_->port == 80 || addr_->port == 443) { req += ':'; req += util::utos(addr_->port); } req += " HTTP/1.1\r\nHost: "; req += addr_->host; req += "\r\n"; const auto &proxy = get_config()->downstream_http_proxy; if (!proxy.userinfo.empty()) { req += "Proxy-Authorization: Basic "; req += base64::encode(proxy.userinfo); req += "\r\n"; } req += "\r\n"; if (LOG_ENABLED(INFO)) { SSLOG(INFO, this) << "HTTP proxy request headers\n" << req; } wb_.append(req); on_write_ = &Http2Session::write_noop; signal_write(); return 0; } void Http2Session::add_downstream_connection(Http2DownstreamConnection *dconn) { dconns_.append(dconn); ++addr_->num_dconn; } void Http2Session::remove_downstream_connection( Http2DownstreamConnection *dconn) { --addr_->num_dconn; dconns_.remove(dconn); dconn->detach_stream_data(); if (LOG_ENABLED(INFO)) { SSLOG(INFO, this) << "Remove downstream"; } if (freelist_zone_ == FreelistZone::NONE && !max_concurrency_reached()) { if (LOG_ENABLED(INFO)) { SSLOG(INFO, this) << "Append to http2_extra_freelist, addr=" << addr_ << ", freelist.size=" << addr_->http2_extra_freelist.size(); } add_to_extra_freelist(); } } void Http2Session::remove_stream_data(StreamData *sd) { streams_.remove(sd); if (sd->dconn) { sd->dconn->detach_stream_data(); } delete sd; } int Http2Session::submit_request(Http2DownstreamConnection *dconn, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider2 *data_prd) { assert(state_ == Http2SessionState::CONNECTED); auto sd = std::make_unique(); sd->dlnext = sd->dlprev = nullptr; // TODO Specify nullptr to pri_spec for now auto stream_id = nghttp2_submit_request2(session_, nullptr, nva, nvlen, data_prd, sd.get()); if (stream_id < 0) { SSLOG(FATAL, this) << "nghttp2_submit_request2() failed: " << nghttp2_strerror(stream_id); return -1; } dconn->attach_stream_data(sd.get()); dconn->get_downstream()->set_downstream_stream_id(stream_id); streams_.append(sd.release()); return 0; } int Http2Session::submit_rst_stream(int32_t stream_id, uint32_t error_code) { assert(state_ == Http2SessionState::CONNECTED); if (LOG_ENABLED(INFO)) { SSLOG(INFO, this) << "RST_STREAM stream_id=" << stream_id << " with error_code=" << error_code; } int rv = nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, stream_id, error_code); if (rv != 0) { SSLOG(FATAL, this) << "nghttp2_submit_rst_stream() failed: " << nghttp2_strerror(rv); return -1; } return 0; } nghttp2_session *Http2Session::get_session() const { return session_; } int Http2Session::resume_data(Http2DownstreamConnection *dconn) { assert(state_ == Http2SessionState::CONNECTED); auto downstream = dconn->get_downstream(); int rv = nghttp2_session_resume_data( session_, static_cast(downstream->get_downstream_stream_id())); switch (rv) { case 0: case NGHTTP2_ERR_INVALID_ARGUMENT: return 0; default: SSLOG(FATAL, this) << "nghttp2_resume_session() failed: " << nghttp2_strerror(rv); return -1; } } namespace { void call_downstream_readcb(Http2Session *http2session, Downstream *downstream) { auto upstream = downstream->get_upstream(); if (!upstream) { return; } if (upstream->downstream_read(downstream->get_downstream_connection()) != 0) { delete upstream->get_client_handler(); } } } // namespace namespace { int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *user_data) { auto http2session = static_cast(user_data); if (LOG_ENABLED(INFO)) { SSLOG(INFO, http2session) << "Stream stream_id=" << stream_id << " is being closed with error code " << error_code; } auto sd = static_cast( nghttp2_session_get_stream_user_data(session, stream_id)); if (sd == 0) { // We might get this close callback when pushed streams are // closed. return 0; } auto dconn = sd->dconn; if (dconn) { auto downstream = dconn->get_downstream(); auto upstream = downstream->get_upstream(); if (downstream->get_downstream_stream_id() % 2 == 0 && downstream->get_request_state() == DownstreamState::INITIAL) { // Downstream is canceled in backend before it is submitted in // frontend session. // This will avoid to send RST_STREAM to backend downstream->set_response_state(DownstreamState::MSG_RESET); upstream->cancel_premature_downstream(downstream); } else { if (downstream->get_upgraded() && downstream->get_response_state() == DownstreamState::HEADER_COMPLETE) { // For tunneled connection, we have to submit RST_STREAM to // upstream *after* whole response body is sent. We just set // MSG_COMPLETE here. Upstream will take care of that. downstream->get_upstream()->on_downstream_body_complete(downstream); downstream->set_response_state(DownstreamState::MSG_COMPLETE); } else if (error_code == NGHTTP2_NO_ERROR) { switch (downstream->get_response_state()) { case DownstreamState::MSG_COMPLETE: case DownstreamState::MSG_BAD_HEADER: break; default: downstream->set_response_state(DownstreamState::MSG_RESET); } } else if (downstream->get_response_state() != DownstreamState::MSG_BAD_HEADER) { downstream->set_response_state(DownstreamState::MSG_RESET); } if (downstream->get_response_state() == DownstreamState::MSG_RESET && downstream->get_response_rst_stream_error_code() == NGHTTP2_NO_ERROR) { downstream->set_response_rst_stream_error_code(error_code); } call_downstream_readcb(http2session, downstream); } // dconn may be deleted } // The life time of StreamData ends here http2session->remove_stream_data(sd); return 0; } } // namespace void Http2Session::start_settings_timer() { auto &downstreamconf = get_config()->http2.downstream; ev_timer_set(&settings_timer_, downstreamconf.timeout.settings, 0.); ev_timer_start(conn_.loop, &settings_timer_); } void Http2Session::stop_settings_timer() { ev_timer_stop(conn_.loop, &settings_timer_); } namespace { int on_header_callback2(nghttp2_session *session, const nghttp2_frame *frame, nghttp2_rcbuf *name, nghttp2_rcbuf *value, uint8_t flags, void *user_data) { auto http2session = static_cast(user_data); auto sd = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); if (!sd || !sd->dconn) { return 0; } auto downstream = sd->dconn->get_downstream(); auto namebuf = nghttp2_rcbuf_get_buf(name); auto valuebuf = nghttp2_rcbuf_get_buf(value); auto &resp = downstream->response(); auto &httpconf = get_config()->http; switch (frame->hd.type) { case NGHTTP2_HEADERS: { auto trailer = frame->headers.cat == NGHTTP2_HCAT_HEADERS && !downstream->get_expect_final_response(); if (resp.fs.buffer_size() + namebuf.len + valuebuf.len > httpconf.response_header_field_buffer || resp.fs.num_fields() >= httpconf.max_response_header_fields) { if (LOG_ENABLED(INFO)) { DLOG(INFO, downstream) << "Too large or many header field size=" << resp.fs.buffer_size() + namebuf.len + valuebuf.len << ", num=" << resp.fs.num_fields() + 1; } if (trailer) { // We don't care trailer part exceeds header size limit; just // discard it. return 0; } return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } auto nameref = as_string_view(namebuf.base, namebuf.len); auto valueref = as_string_view(valuebuf.base, valuebuf.len); auto token = http2::lookup_token(nameref); auto no_index = flags & NGHTTP2_NV_FLAG_NO_INDEX; downstream->add_rcbuf(name); downstream->add_rcbuf(value); if (trailer) { // just store header fields for trailer part resp.fs.add_trailer_token(nameref, valueref, no_index, token); return 0; } resp.fs.add_header_token(nameref, valueref, no_index, token); return 0; } case NGHTTP2_PUSH_PROMISE: { auto promised_stream_id = frame->push_promise.promised_stream_id; auto promised_sd = static_cast( nghttp2_session_get_stream_user_data(session, promised_stream_id)); if (!promised_sd || !promised_sd->dconn) { http2session->submit_rst_stream(promised_stream_id, NGHTTP2_CANCEL); return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } auto promised_downstream = promised_sd->dconn->get_downstream(); auto namebuf = nghttp2_rcbuf_get_buf(name); auto valuebuf = nghttp2_rcbuf_get_buf(value); assert(promised_downstream); auto &promised_req = promised_downstream->request(); // We use request header limit for PUSH_PROMISE if (promised_req.fs.buffer_size() + namebuf.len + valuebuf.len > httpconf.request_header_field_buffer || promised_req.fs.num_fields() >= httpconf.max_request_header_fields) { if (LOG_ENABLED(INFO)) { DLOG(INFO, downstream) << "Too large or many header field size=" << promised_req.fs.buffer_size() + namebuf.len + valuebuf.len << ", num=" << promised_req.fs.num_fields() + 1; } return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } promised_downstream->add_rcbuf(name); promised_downstream->add_rcbuf(value); auto nameref = as_string_view(namebuf.base, namebuf.len); auto valueref = as_string_view(valuebuf.base, valuebuf.len); auto token = http2::lookup_token(nameref); promised_req.fs.add_header_token(nameref, valueref, flags & NGHTTP2_NV_FLAG_NO_INDEX, token); return 0; } } return 0; } } // namespace namespace { int on_invalid_header_callback2(nghttp2_session *session, const nghttp2_frame *frame, nghttp2_rcbuf *name, nghttp2_rcbuf *value, uint8_t flags, void *user_data) { auto http2session = static_cast(user_data); auto sd = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); if (!sd || !sd->dconn) { return 0; } int32_t stream_id; if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { stream_id = frame->push_promise.promised_stream_id; } else { stream_id = frame->hd.stream_id; } if (LOG_ENABLED(INFO)) { auto namebuf = nghttp2_rcbuf_get_buf(name); auto valuebuf = nghttp2_rcbuf_get_buf(value); SSLOG(INFO, http2session) << "Invalid header field for stream_id=" << stream_id << " in frame type=" << static_cast(frame->hd.type) << ": name=[" << as_string_view(namebuf.base, namebuf.len) << "], value=[" << as_string_view(valuebuf.base, valuebuf.len) << "]"; } http2session->submit_rst_stream(stream_id, NGHTTP2_PROTOCOL_ERROR); return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } } // namespace namespace { int on_begin_headers_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { auto http2session = static_cast(user_data); switch (frame->hd.type) { case NGHTTP2_HEADERS: { if (frame->headers.cat != NGHTTP2_HCAT_RESPONSE && frame->headers.cat != NGHTTP2_HCAT_PUSH_RESPONSE) { return 0; } auto sd = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); if (!sd || !sd->dconn) { http2session->submit_rst_stream(frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR); return 0; } return 0; } case NGHTTP2_PUSH_PROMISE: { auto promised_stream_id = frame->push_promise.promised_stream_id; auto sd = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); if (!sd || !sd->dconn) { http2session->submit_rst_stream(promised_stream_id, NGHTTP2_CANCEL); return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } auto downstream = sd->dconn->get_downstream(); assert(downstream); assert(downstream->get_downstream_stream_id() == frame->hd.stream_id); if (http2session->handle_downstream_push_promise(downstream, promised_stream_id) != 0) { http2session->submit_rst_stream(promised_stream_id, NGHTTP2_CANCEL); return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } return 0; } } return 0; } } // namespace namespace { int on_response_headers(Http2Session *http2session, Downstream *downstream, nghttp2_session *session, const nghttp2_frame *frame) { int rv; auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); const auto &req = downstream->request(); auto &resp = downstream->response(); auto &nva = resp.fs.headers(); auto config = get_config(); auto &loggingconf = config->logging; downstream->set_expect_final_response(false); auto status = resp.fs.header(http2::HD__STATUS); // libnghttp2 guarantees this exists and can be parsed assert(status); auto status_code = http2::parse_http_status_code(status->value); resp.http_status = as_unsigned(status_code); resp.http_major = 2; resp.http_minor = 0; downstream->set_downstream_addr_group( http2session->get_downstream_addr_group()); downstream->set_addr(http2session->get_addr()); if (LOG_ENABLED(INFO)) { std::stringstream ss; for (auto &nv : nva) { ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n"; } SSLOG(INFO, http2session) << "HTTP response headers. stream_id=" << frame->hd.stream_id << "\n" << ss.str(); } if (downstream->get_non_final_response()) { if (LOG_ENABLED(INFO)) { SSLOG(INFO, http2session) << "This is non-final response."; } downstream->set_expect_final_response(true); rv = upstream->on_downstream_header_complete(downstream); // Now Dowstream's response headers are erased. if (rv != 0) { http2session->submit_rst_stream(frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR); downstream->set_response_state(DownstreamState::MSG_RESET); } return 0; } downstream->set_response_state(DownstreamState::HEADER_COMPLETE); downstream->check_upgrade_fulfilled_http2(); if (downstream->get_upgraded()) { resp.connection_close = true; // On upgrade success, both ends can send data if (upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0) != 0) { // If resume_read fails, just drop connection. Not ideal. delete handler; return -1; } downstream->set_request_state(DownstreamState::HEADER_COMPLETE); if (LOG_ENABLED(INFO)) { SSLOG(INFO, http2session) << "HTTP upgrade success. stream_id=" << frame->hd.stream_id; } } else { auto content_length = resp.fs.header(http2::HD_CONTENT_LENGTH); if (content_length) { // libnghttp2 guarantees this can be parsed resp.fs.content_length = util::parse_uint(content_length->value).value_or(-1); } if (resp.fs.content_length == -1 && downstream->expect_response_body()) { // Here we have response body but Content-Length is not known in // advance. if (req.http_major <= 0 || (req.http_major == 1 && req.http_minor == 0)) { // We simply close connection for pre-HTTP/1.1 in this case. resp.connection_close = true; } else { // Otherwise, use chunked encoding to keep upstream connection // open. In HTTP2, we are supposed not to receive // transfer-encoding. resp.fs.add_header_token("transfer-encoding"sv, "chunked"sv, false, http2::HD_TRANSFER_ENCODING); downstream->set_chunked_response(true); } } } if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { resp.headers_only = true; } if (loggingconf.access.write_early && downstream->accesslog_ready()) { handler->write_accesslog(downstream); downstream->set_accesslog_written(true); } rv = upstream->on_downstream_header_complete(downstream); if (rv != 0) { // Handling early return (in other words, response was hijacked by // mruby scripting). if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { http2session->submit_rst_stream(frame->hd.stream_id, NGHTTP2_CANCEL); } else { http2session->submit_rst_stream(frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR); downstream->set_response_state(DownstreamState::MSG_RESET); } } return 0; } } // namespace namespace { int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { int rv; auto http2session = static_cast(user_data); switch (frame->hd.type) { case NGHTTP2_DATA: { auto sd = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); if (!sd || !sd->dconn) { return 0; } auto downstream = sd->dconn->get_downstream(); auto upstream = downstream->get_upstream(); rv = upstream->on_downstream_body(downstream, nullptr, 0, true); if (rv != 0) { http2session->submit_rst_stream(frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR); downstream->set_response_state(DownstreamState::MSG_RESET); } else if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { downstream->disable_downstream_rtimer(); if (downstream->get_response_state() == DownstreamState::HEADER_COMPLETE) { downstream->set_response_state(DownstreamState::MSG_COMPLETE); rv = upstream->on_downstream_body_complete(downstream); if (rv != 0) { downstream->set_response_state(DownstreamState::MSG_RESET); } } } call_downstream_readcb(http2session, downstream); return 0; } case NGHTTP2_HEADERS: { auto sd = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); if (!sd || !sd->dconn) { return 0; } auto downstream = sd->dconn->get_downstream(); if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE || frame->headers.cat == NGHTTP2_HCAT_PUSH_RESPONSE) { rv = on_response_headers(http2session, downstream, session, frame); if (rv != 0) { return 0; } } else if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) { if (downstream->get_expect_final_response()) { rv = on_response_headers(http2session, downstream, session, frame); if (rv != 0) { return 0; } } } if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { downstream->disable_downstream_rtimer(); if (downstream->get_response_state() == DownstreamState::HEADER_COMPLETE) { downstream->set_response_state(DownstreamState::MSG_COMPLETE); auto upstream = downstream->get_upstream(); rv = upstream->on_downstream_body_complete(downstream); if (rv != 0) { downstream->set_response_state(DownstreamState::MSG_RESET); } } } else { downstream->reset_downstream_rtimer(); } // This may delete downstream call_downstream_readcb(http2session, downstream); return 0; } case NGHTTP2_RST_STREAM: { auto sd = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); if (sd && sd->dconn) { auto downstream = sd->dconn->get_downstream(); downstream->set_response_rst_stream_error_code( frame->rst_stream.error_code); call_downstream_readcb(http2session, downstream); } return 0; } case NGHTTP2_SETTINGS: { if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { http2session->on_settings_received(frame); return 0; } http2session->stop_settings_timer(); auto addr = http2session->get_addr(); auto &connect_blocker = addr->connect_blocker; connect_blocker->on_success(); return 0; } case NGHTTP2_PING: if (frame->hd.flags & NGHTTP2_FLAG_ACK) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "PING ACK received"; } http2session->connection_alive(); } return 0; case NGHTTP2_PUSH_PROMISE: { auto promised_stream_id = frame->push_promise.promised_stream_id; if (LOG_ENABLED(INFO)) { SSLOG(INFO, http2session) << "Received downstream PUSH_PROMISE stream_id=" << frame->hd.stream_id << ", promised_stream_id=" << promised_stream_id; } auto sd = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); if (!sd || !sd->dconn) { http2session->submit_rst_stream(promised_stream_id, NGHTTP2_CANCEL); return 0; } auto downstream = sd->dconn->get_downstream(); assert(downstream); assert(downstream->get_downstream_stream_id() == frame->hd.stream_id); auto promised_sd = static_cast( nghttp2_session_get_stream_user_data(session, promised_stream_id)); if (!promised_sd || !promised_sd->dconn) { http2session->submit_rst_stream(promised_stream_id, NGHTTP2_CANCEL); return 0; } auto promised_downstream = promised_sd->dconn->get_downstream(); assert(promised_downstream); if (http2session->handle_downstream_push_promise_complete( downstream, promised_downstream) != 0) { http2session->submit_rst_stream(promised_stream_id, NGHTTP2_CANCEL); return 0; } return 0; } case NGHTTP2_GOAWAY: if (LOG_ENABLED(INFO)) { auto debug_data = util::ascii_dump(frame->goaway.opaque_data, frame->goaway.opaque_data_len); SSLOG(INFO, http2session) << "GOAWAY received: last-stream-id=" << frame->goaway.last_stream_id << ", error_code=" << frame->goaway.error_code << ", debug_data=" << debug_data; } return 0; default: return 0; } } } // namespace namespace { int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data) { int rv; auto http2session = static_cast(user_data); auto sd = static_cast( nghttp2_session_get_stream_user_data(session, stream_id)); if (!sd || !sd->dconn) { http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR); if (http2session->consume(stream_id, len) != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } return 0; } auto downstream = sd->dconn->get_downstream(); if (!downstream->expect_response_body()) { http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR); if (http2session->consume(stream_id, len) != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } return 0; } // We don't want DATA after non-final response, which is illegal in // HTTP. if (downstream->get_non_final_response()) { http2session->submit_rst_stream(stream_id, NGHTTP2_PROTOCOL_ERROR); if (http2session->consume(stream_id, len) != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } return 0; } downstream->reset_downstream_rtimer(); auto &resp = downstream->response(); resp.recv_body_length += len; resp.unconsumed_body_length += len; auto upstream = downstream->get_upstream(); rv = upstream->on_downstream_body(downstream, data, len, false); if (rv != 0) { http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR); if (http2session->consume(stream_id, len) != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } downstream->set_response_state(DownstreamState::MSG_RESET); } call_downstream_readcb(http2session, downstream); return 0; } } // namespace namespace { int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { auto http2session = static_cast(user_data); if (frame->hd.type == NGHTTP2_DATA || frame->hd.type == NGHTTP2_HEADERS) { auto sd = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); if (!sd || !sd->dconn) { return 0; } auto downstream = sd->dconn->get_downstream(); if (frame->hd.type == NGHTTP2_HEADERS && frame->headers.cat == NGHTTP2_HCAT_REQUEST) { downstream->set_request_header_sent(true); auto src = downstream->get_blocked_request_buf(); if (src->rleft()) { auto dest = downstream->get_request_buf(); src->remove(*dest); if (http2session->resume_data(sd->dconn) != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } downstream->ensure_downstream_wtimer(); } } if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { return 0; } downstream->reset_downstream_rtimer(); return 0; } if (frame->hd.type == NGHTTP2_SETTINGS && (frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { http2session->start_settings_timer(); } return 0; } } // namespace namespace { int on_frame_not_send_callback(nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, void *user_data) { auto http2session = static_cast(user_data); if (LOG_ENABLED(INFO)) { SSLOG(INFO, http2session) << "Failed to send control frame type=" << static_cast(frame->hd.type) << ", lib_error_code=" << lib_error_code << ": " << nghttp2_strerror(lib_error_code); } if (frame->hd.type != NGHTTP2_HEADERS || lib_error_code == NGHTTP2_ERR_STREAM_CLOSED || lib_error_code == NGHTTP2_ERR_STREAM_CLOSING) { return 0; } auto sd = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); if (!sd) { return 0; } if (!sd->dconn) { return 0; } auto downstream = sd->dconn->get_downstream(); if (lib_error_code == NGHTTP2_ERR_START_STREAM_NOT_ALLOWED) { // Migrate to another downstream connection. auto upstream = downstream->get_upstream(); if (upstream->on_downstream_reset(downstream, false)) { // This should be done for h1 upstream only. Deleting // ClientHandler for h2 upstream may lead to crash. delete upstream->get_client_handler(); } return 0; } // To avoid stream hanging around, flag DownstreamState::MSG_RESET. downstream->set_response_state(DownstreamState::MSG_RESET); call_downstream_readcb(http2session, downstream); return 0; } } // namespace namespace { constexpr auto PADDING = std::array{}; } // namespace namespace { int send_data_callback(nghttp2_session *session, nghttp2_frame *frame, const uint8_t *framehd, size_t length, nghttp2_data_source *source, void *user_data) { auto http2session = static_cast(user_data); auto sd = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); if (sd == nullptr) { return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } auto dconn = sd->dconn; auto downstream = dconn->get_downstream(); auto input = downstream->get_request_buf(); auto wb = http2session->get_request_buf(); size_t padlen = 0; wb->append(framehd, 9); if (frame->data.padlen > 0) { padlen = frame->data.padlen - 1; wb->append(static_cast(padlen)); } input->remove(*wb, length); wb->append(PADDING.data(), padlen); if (input->rleft() == 0) { downstream->disable_downstream_wtimer(); } else { downstream->reset_downstream_wtimer(); } if (length > 0) { // This is important because it will handle flow control // stuff. if (downstream->get_upstream()->resume_read(SHRPX_NO_BUFFER, downstream, length) != 0) { // In this case, downstream may be deleted. return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } // Here sd->dconn could be nullptr, because // Upstream::resume_read() may delete downstream which will delete // dconn. Is this still really true? } return 0; } } // namespace nghttp2_session_callbacks *create_http2_downstream_callbacks() { int rv; nghttp2_session_callbacks *callbacks; rv = nghttp2_session_callbacks_new(&callbacks); if (rv != 0) { return nullptr; } nghttp2_session_callbacks_set_on_stream_close_callback( callbacks, on_stream_close_callback); nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, on_frame_recv_callback); nghttp2_session_callbacks_set_on_data_chunk_recv_callback( callbacks, on_data_chunk_recv_callback); nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, on_frame_send_callback); nghttp2_session_callbacks_set_on_frame_not_send_callback( callbacks, on_frame_not_send_callback); nghttp2_session_callbacks_set_on_header_callback2(callbacks, on_header_callback2); nghttp2_session_callbacks_set_on_invalid_header_callback2( callbacks, on_invalid_header_callback2); nghttp2_session_callbacks_set_on_begin_headers_callback( callbacks, on_begin_headers_callback); nghttp2_session_callbacks_set_send_data_callback(callbacks, send_data_callback); if (get_config()->padding) { nghttp2_session_callbacks_set_select_padding_callback2( callbacks, http::select_padding_callback); } nghttp2_session_callbacks_set_rand_callback(callbacks, util::secure_random); return callbacks; } int Http2Session::connection_made() { int rv; state_ = Http2SessionState::CONNECTED; on_write_ = &Http2Session::downstream_write; on_read_ = &Http2Session::downstream_read; if (addr_->tls) { const unsigned char *next_proto = nullptr; unsigned int next_proto_len = 0; SSL_get0_alpn_selected(conn_.tls.ssl, &next_proto, &next_proto_len); if (!next_proto) { downstream_failure(addr_, raddr_); return -1; } auto proto = as_string_view(next_proto, next_proto_len); if (LOG_ENABLED(INFO)) { SSLOG(INFO, this) << "Negotiated next protocol: " << proto; } if (!util::check_h2_is_selected(proto)) { downstream_failure(addr_, raddr_); return -1; } } auto config = get_config(); auto &http2conf = config->http2; rv = nghttp2_session_client_new2(&session_, http2conf.downstream.callbacks, this, http2conf.downstream.option); if (rv != 0) { return -1; } std::array entry; size_t nentry = 3; entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; entry[0].value = static_cast(http2conf.downstream.max_concurrent_streams); entry[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; entry[1].value = as_unsigned(http2conf.downstream.window_size); entry[2].settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES; entry[2].value = 1; if (http2conf.no_server_push || config->http2_proxy) { entry[nentry].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; entry[nentry].value = 0; ++nentry; } if (http2conf.downstream.decoder_dynamic_table_size != NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) { entry[nentry].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; entry[nentry].value = static_cast(http2conf.downstream.decoder_dynamic_table_size); ++nentry; } rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), nentry); if (rv != 0) { return -1; } rv = nghttp2_session_set_local_window_size( session_, NGHTTP2_FLAG_NONE, 0, http2conf.downstream.connection_window_size); if (rv != 0) { return -1; } reset_connection_check_timer(CONNCHK_TIMEOUT); submit_pending_requests(); signal_write(); return 0; } int Http2Session::do_read() { return read_(*this); } int Http2Session::do_write() { return write_(*this); } int Http2Session::on_read(const uint8_t *data, size_t datalen) { return on_read_(*this, data, datalen); } int Http2Session::on_write() { return on_write_(*this); } int Http2Session::downstream_read(const uint8_t *data, size_t datalen) { auto rv = nghttp2_session_mem_recv2(session_, data, datalen); if (rv < 0) { SSLOG(ERROR, this) << "nghttp2_session_mem_recv2() returned error: " << nghttp2_strerror(static_cast(rv)); return -1; } if (nghttp2_session_want_read(session_) == 0 && nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) { if (LOG_ENABLED(INFO)) { SSLOG(INFO, this) << "No more read/write for this HTTP2 session"; } return -1; } signal_write(); return 0; } int Http2Session::downstream_write() { for (;;) { const uint8_t *data; auto datalen = nghttp2_session_mem_send2(session_, &data); if (datalen < 0) { SSLOG(ERROR, this) << "nghttp2_session_mem_send2() returned error: " << nghttp2_strerror(static_cast(datalen)); return -1; } if (datalen == 0) { break; } wb_.append(data, as_unsigned(datalen)); if (wb_.rleft() >= MAX_BUFFER_SIZE) { break; } } if (nghttp2_session_want_read(session_) == 0 && nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) { if (LOG_ENABLED(INFO)) { SSLOG(INFO, this) << "No more read/write for this session"; } return -1; } return 0; } void Http2Session::signal_write() { switch (state_) { case Http2SessionState::DISCONNECTED: if (!ev_is_active(&initiate_connection_timer_)) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "Start connecting to backend server"; } // Since the timer is set to 0., these will feed 2 events. We // will stop the timer in the initiate_connection_timer_ to void // 2nd event. ev_timer_start(conn_.loop, &initiate_connection_timer_); ev_feed_event(conn_.loop, &initiate_connection_timer_, 0); } break; case Http2SessionState::CONNECTED: conn_.wlimit.startw(); break; default: break; } } struct ev_loop *Http2Session::get_loop() const { return conn_.loop; } ev_io *Http2Session::get_wev() { return &conn_.wev; } Http2SessionState Http2Session::get_state() const { return state_; } void Http2Session::set_state(Http2SessionState state) { state_ = state; } int Http2Session::terminate_session(uint32_t error_code) { int rv; rv = nghttp2_session_terminate_session(session_, error_code); if (rv != 0) { return -1; } return 0; } SSL *Http2Session::get_ssl() const { return conn_.tls.ssl; } int Http2Session::consume(int32_t stream_id, size_t len) { int rv; if (!session_) { return 0; } rv = nghttp2_session_consume(session_, stream_id, len); if (rv != 0) { SSLOG(WARN, this) << "nghttp2_session_consume() returned error: " << nghttp2_strerror(rv); return -1; } return 0; } bool Http2Session::can_push_request(const Downstream *downstream) const { auto &req = downstream->request(); return state_ == Http2SessionState::CONNECTED && connection_check_state_ == ConnectionCheck::NONE && (req.connect_proto == ConnectProto::NONE || settings_recved_); } void Http2Session::start_checking_connection() { if (state_ != Http2SessionState::CONNECTED || connection_check_state_ != ConnectionCheck::REQUIRED) { return; } connection_check_state_ = ConnectionCheck::STARTED; SSLOG(INFO, this) << "Start checking connection"; // If connection is down, we may get error when writing data. Issue // ping frame to see whether connection is alive. nghttp2_submit_ping(session_, NGHTTP2_FLAG_NONE, nullptr); // set ping timeout and start timer again reset_connection_check_timer(CONNCHK_PING_TIMEOUT); signal_write(); } void Http2Session::reset_connection_check_timer(ev_tstamp t) { connchk_timer_.repeat = t; ev_timer_again(conn_.loop, &connchk_timer_); } void Http2Session::reset_connection_check_timer_if_not_checking() { if (connection_check_state_ != ConnectionCheck::NONE) { return; } reset_connection_check_timer(CONNCHK_TIMEOUT); } void Http2Session::connection_alive() { reset_connection_check_timer(CONNCHK_TIMEOUT); if (connection_check_state_ == ConnectionCheck::NONE) { return; } if (LOG_ENABLED(INFO)) { SSLOG(INFO, this) << "Connection alive"; } connection_check_state_ = ConnectionCheck::NONE; submit_pending_requests(); } void Http2Session::submit_pending_requests() { for (auto dconn = dconns_.head; dconn; dconn = dconn->dlnext) { auto downstream = dconn->get_downstream(); if (!downstream->get_request_pending() || !downstream->request_submission_ready()) { continue; } auto &req = downstream->request(); if (req.connect_proto != ConnectProto::NONE && !settings_recved_) { continue; } auto upstream = downstream->get_upstream(); if (dconn->push_request_headers() != 0) { if (LOG_ENABLED(INFO)) { SSLOG(INFO, this) << "backend request failed"; } upstream->on_downstream_abort_request(downstream, 400); continue; } upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0); } } void Http2Session::set_connection_check_state(ConnectionCheck state) { connection_check_state_ = state; } ConnectionCheck Http2Session::get_connection_check_state() const { return connection_check_state_; } int Http2Session::noop() { return 0; } int Http2Session::read_noop(const uint8_t *data, size_t datalen) { return 0; } int Http2Session::write_noop() { return 0; } int Http2Session::connected() { auto sock_error = util::get_socket_error(conn_.fd); if (sock_error != 0) { SSLOG(WARN, this) << "Backend connect failed; addr=" << util::to_numeric_addr(raddr_) << ": errno=" << sock_error; downstream_failure(addr_, raddr_); return -1; } if (LOG_ENABLED(INFO)) { SSLOG(INFO, this) << "Connection established"; } // Reset timeout for write. Previously, we set timeout for connect. conn_.wt.repeat = group_->shared_addr->timeout.write; ev_timer_again(conn_.loop, &conn_.wt); conn_.rlimit.startw(); conn_.again_rt(); read_ = &Http2Session::read_clear; write_ = &Http2Session::write_clear; if (state_ == Http2SessionState::PROXY_CONNECTING) { return do_write(); } if (conn_.tls.ssl) { read_ = &Http2Session::tls_handshake; write_ = &Http2Session::tls_handshake; return do_write(); } if (connection_made() != 0) { state_ = Http2SessionState::CONNECT_FAILING; return -1; } return 0; } int Http2Session::read_clear() { conn_.last_read = std::chrono::steady_clock::now(); std::array buf; for (;;) { auto nread = conn_.read_clear(buf.data(), buf.size()); if (nread == 0) { return write_clear(); } if (nread < 0) { return static_cast(nread); } if (on_read(buf.data(), as_unsigned(nread)) != 0) { return -1; } } } int Http2Session::write_clear() { conn_.last_read = std::chrono::steady_clock::now(); std::array iov; for (;;) { if (wb_.rleft() > 0) { auto iovcnt = wb_.riovec(iov.data(), iov.size()); auto nwrite = conn_.writev_clear(iov.data(), iovcnt); if (nwrite == 0) { return 0; } if (nwrite < 0) { // We may have pending data in receive buffer which may // contain part of response body. So keep reading. Invoke // read event to get read(2) error just in case. ev_feed_event(conn_.loop, &conn_.rev, EV_READ); write_ = &Http2Session::write_void; break; } wb_.drain(as_unsigned(nwrite)); continue; } if (on_write() != 0) { return -1; } if (wb_.rleft() == 0) { break; } } conn_.wlimit.stopw(); ev_timer_stop(conn_.loop, &conn_.wt); return 0; } int Http2Session::tls_handshake() { conn_.last_read = std::chrono::steady_clock::now(); ERR_clear_error(); auto rv = conn_.tls_handshake(); if (rv == SHRPX_ERR_INPROGRESS) { return 0; } if (rv < 0) { downstream_failure(addr_, raddr_); return rv; } if (LOG_ENABLED(INFO)) { SSLOG(INFO, this) << "SSL/TLS handshake completed"; } if (!get_config()->tls.insecure && tls::check_cert(conn_.tls.ssl, addr_, raddr_) != 0) { downstream_failure(addr_, raddr_); return -1; } read_ = &Http2Session::read_tls; write_ = &Http2Session::write_tls; if (connection_made() != 0) { state_ = Http2SessionState::CONNECT_FAILING; return -1; } return 0; } int Http2Session::read_tls() { conn_.last_read = std::chrono::steady_clock::now(); std::array buf; ERR_clear_error(); for (;;) { auto nread = conn_.read_tls(buf.data(), buf.size()); if (nread == 0) { return write_tls(); } if (nread < 0) { return static_cast(nread); } if (on_read(buf.data(), as_unsigned(nread)) != 0) { return -1; } } } int Http2Session::write_tls() { conn_.last_read = std::chrono::steady_clock::now(); ERR_clear_error(); struct iovec iov; for (;;) { if (wb_.rleft() > 0) { auto iovcnt = wb_.riovec(&iov, 1); if (iovcnt != 1) { assert(0); return -1; } auto nwrite = conn_.write_tls(iov.iov_base, iov.iov_len); if (nwrite == 0) { return 0; } if (nwrite < 0) { // We may have pending data in receive buffer which may // contain part of response body. So keep reading. Invoke // read event to get read(2) error just in case. ev_feed_event(conn_.loop, &conn_.rev, EV_READ); write_ = &Http2Session::write_void; break; } wb_.drain(as_unsigned(nwrite)); continue; } if (on_write() != 0) { return -1; } if (wb_.rleft() == 0) { conn_.start_tls_write_idle(); break; } } conn_.wlimit.stopw(); ev_timer_stop(conn_.loop, &conn_.wt); return 0; } int Http2Session::write_void() { conn_.wlimit.stopw(); return 0; } bool Http2Session::should_hard_fail() const { switch (state_) { case Http2SessionState::PROXY_CONNECTING: case Http2SessionState::PROXY_FAILED: return true; case Http2SessionState::DISCONNECTED: { const auto &proxy = get_config()->downstream_http_proxy; return !proxy.host.empty(); } default: return false; } } DownstreamAddr *Http2Session::get_addr() const { return addr_; } int Http2Session::handle_downstream_push_promise(Downstream *downstream, int32_t promised_stream_id) { auto upstream = downstream->get_upstream(); if (!upstream->push_enabled()) { return -1; } auto promised_downstream = upstream->on_downstream_push_promise(downstream, promised_stream_id); if (!promised_downstream) { return -1; } // Now we have Downstream object for pushed stream. // promised_downstream->get_stream() still returns 0. auto handler = upstream->get_client_handler(); auto promised_dconn = std::make_unique(this); promised_dconn->set_client_handler(handler); auto ptr = promised_dconn.get(); if (promised_downstream->attach_downstream_connection( std::move(promised_dconn)) != 0) { return -1; } auto promised_sd = std::make_unique(); nghttp2_session_set_stream_user_data(session_, promised_stream_id, promised_sd.get()); ptr->attach_stream_data(promised_sd.get()); streams_.append(promised_sd.release()); return 0; } int Http2Session::handle_downstream_push_promise_complete( Downstream *downstream, Downstream *promised_downstream) { auto &promised_req = promised_downstream->request(); auto &promised_balloc = promised_downstream->get_block_allocator(); auto authority = promised_req.fs.header(http2::HD__AUTHORITY); auto path = promised_req.fs.header(http2::HD__PATH); auto method = promised_req.fs.header(http2::HD__METHOD); auto scheme = promised_req.fs.header(http2::HD__SCHEME); if (!authority) { authority = promised_req.fs.header(http2::HD_HOST); } auto method_token = http2::lookup_method_token(method->value); if (method_token == -1) { if (LOG_ENABLED(INFO)) { SSLOG(INFO, this) << "Unrecognized method: " << method->value; } return -1; } // TODO Rewrite authority if we enabled rewrite host. But we // really don't know how to rewrite host. Should we use the same // host in associated stream? if (authority) { promised_req.authority = authority->value; } promised_req.method = method_token; // libnghttp2 ensures that we don't have CONNECT method in // PUSH_PROMISE, and guarantees that :scheme exists. if (scheme) { promised_req.scheme = scheme->value; } // For server-wide OPTIONS request, path is empty. if (method_token != HTTP_OPTIONS || path->value != "*"sv) { promised_req.path = http2::rewrite_clean_path(promised_balloc, path->value); } promised_downstream->inspect_http2_request(); auto upstream = promised_downstream->get_upstream(); promised_downstream->set_request_state(DownstreamState::MSG_COMPLETE); promised_downstream->set_request_header_sent(true); if (upstream->on_downstream_push_promise_complete(downstream, promised_downstream) != 0) { return -1; } return 0; } size_t Http2Session::get_num_dconns() const { return dconns_.size(); } bool Http2Session::max_concurrency_reached(size_t extra) const { if (!session_) { return dconns_.size() + extra >= 100; } // If session does not allow further requests, it effectively means // that maximum concurrency is reached. return !nghttp2_session_check_request_allowed(session_) || dconns_.size() + extra >= nghttp2_session_get_remote_settings( session_, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); } const std::shared_ptr & Http2Session::get_downstream_addr_group() const { return group_; } void Http2Session::add_to_extra_freelist() { if (freelist_zone_ != FreelistZone::NONE) { return; } if (LOG_ENABLED(INFO)) { SSLOG(INFO, this) << "Append to http2_extra_freelist, addr=" << addr_ << ", freelist.size=" << addr_->http2_extra_freelist.size(); } freelist_zone_ = FreelistZone::EXTRA; addr_->http2_extra_freelist.append(this); } void Http2Session::remove_from_freelist() { switch (freelist_zone_) { case FreelistZone::NONE: return; case FreelistZone::EXTRA: if (LOG_ENABLED(INFO)) { SSLOG(INFO, this) << "Remove from http2_extra_freelist, addr=" << addr_ << ", freelist.size=" << addr_->http2_extra_freelist.size(); } addr_->http2_extra_freelist.remove(this); break; case FreelistZone::GONE: return; } freelist_zone_ = FreelistZone::NONE; } void Http2Session::exclude_from_scheduling() { remove_from_freelist(); freelist_zone_ = FreelistZone::GONE; } DefaultMemchunks *Http2Session::get_request_buf() { return &wb_; } void Http2Session::on_timeout() { switch (state_) { case Http2SessionState::PROXY_CONNECTING: { auto worker_blocker = worker_->get_connect_blocker(); worker_blocker->on_failure(); break; } case Http2SessionState::CONNECTING: SSLOG(WARN, this) << "Connect time out; addr=" << util::to_numeric_addr(raddr_); downstream_failure(addr_, raddr_); break; default: break; } } void Http2Session::check_retire() { if (!group_->retired) { return; } ev_prepare_stop(conn_.loop, &prep_); if (!session_) { return; } auto last_stream_id = nghttp2_session_get_last_proc_stream_id(session_); nghttp2_submit_goaway(session_, NGHTTP2_FLAG_NONE, last_stream_id, NGHTTP2_NO_ERROR, nullptr, 0); signal_write(); } const Address *Http2Session::get_raddr() const { return raddr_; } void Http2Session::on_settings_received(const nghttp2_frame *frame) { // TODO This effectively disallows nghttpx to change its behaviour // based on the 2nd SETTINGS. if (settings_recved_) { return; } settings_recved_ = true; for (size_t i = 0; i < frame->settings.niv; ++i) { auto &ent = frame->settings.iv[i]; if (ent.settings_id == NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL) { allow_connect_proto_ = true; break; } } submit_pending_requests(); } bool Http2Session::get_allow_connect_proto() const { return allow_connect_proto_; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/util_test.cc0000644000000000000000000000013115077107271015372 xustar0030 mtime=1761382073.002444097 29 atime=1761382080.10641148 30 ctime=1761382109.260299919 nghttp2-1.68.0/src/util_test.cc0000644000175100017510000012544715077107271016000 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "util_test.h" #include #ifdef HAVE_SYS_SOCKET_H # include #endif // defined(HAVE_SYS_SOCKET_H) #ifdef HAVE_NETDB_H # include #endif // defined(HAVE_NETDB_H) #include #include #include #include "munitxx.h" #include #include "util.h" #include "template.h" using namespace nghttp2; using namespace std::literals; namespace shrpx { namespace { const MunitTest tests[]{ munit_void_test(test_util_streq), munit_void_test(test_util_strieq), munit_void_test(test_util_tolower), munit_void_test(test_util_to_base64), munit_void_test(test_util_to_token68), munit_void_test(test_util_percent_encode_token), munit_void_test(test_util_percent_decode), munit_void_test(test_util_quote_string), munit_void_test(test_util_utox), munit_void_test(test_util_http_date), munit_void_test(test_util_select_h2), munit_void_test(test_util_ipv6_numeric_addr), munit_void_test(test_util_contains), munit_void_test(test_util_utos), munit_void_test(test_util_make_string_ref_uint), munit_void_test(test_util_utos_unit), munit_void_test(test_util_utos_funit), munit_void_test(test_util_parse_uint_with_unit), munit_void_test(test_util_parse_uint), munit_void_test(test_util_parse_duration_with_unit), munit_void_test(test_util_duration_str), munit_void_test(test_util_format_duration), munit_void_test(test_util_starts_with), munit_void_test(test_util_ends_with), munit_void_test(test_util_parse_http_date), munit_void_test(test_util_localtime_date), munit_void_test(test_util_get_uint64), munit_void_test(test_util_parse_config_str_list), munit_void_test(test_util_make_http_hostport), munit_void_test(test_util_make_hostport), munit_void_test(test_util_random_alpha_digit), munit_void_test(test_util_format_hex), munit_void_test(test_util_format_upper_hex), munit_void_test(test_util_is_hex_string), munit_void_test(test_util_decode_hex), munit_void_test(test_util_extract_host), munit_void_test(test_util_split_hostport), munit_void_test(test_util_split_str), munit_void_test(test_util_rstrip), munit_void_test(test_util_contains), munit_void_test(test_util_hex_to_uint), munit_void_test(test_util_is_alpha), munit_void_test(test_util_is_digit), munit_void_test(test_util_is_hex_digit), munit_void_test(test_util_in_rfc3986_unreserved_chars), munit_void_test(test_util_in_rfc3986_sub_delims), munit_void_test(test_util_in_token), munit_void_test(test_util_in_attr_char), munit_void_test(test_util_upcase), munit_void_test(test_util_to_numeric_addr), munit_test_end(), }; } // namespace const MunitSuite util_suite{ "/util", tests, nullptr, 1, MUNIT_SUITE_OPTION_NONE, }; void test_util_streq(void) { assert_true(util::streq("alpha"sv, "alpha"sv)); assert_false(util::streq("alphabravo"sv, "alpha"sv)); assert_false(util::streq("alpha"sv, "alphA"sv)); assert_false(util::streq(""sv, "a"sv)); assert_true(util::streq(""sv, ""sv)); assert_false(util::streq("alpha"sv, ""sv)); } void test_util_strieq(void) { assert_true(util::strieq("alpha"sv, "alpha"sv)); assert_true(util::strieq("alpha"sv, "AlPhA"sv)); assert_true(util::strieq(""sv, ""sv)); assert_false(util::strieq("alpha"sv, "AlPhA "sv)); assert_false(util::strieq(""sv, "AlPhA "sv)); assert_true(util::strieq("alpha"sv, "alpha"sv)); assert_true(util::strieq("alpha"sv, "AlPhA"sv)); assert_true(util::strieq(""sv, ""sv)); assert_false(util::strieq("alpha"sv, "AlPhA "sv)); assert_false(util::strieq(""sv, "AlPhA "sv)); } void test_util_tolower(void) { std::array buf; { assert_stdsv_equal( "alpha"sv, (std::string_view{std::ranges::begin(buf), util::tolower("alPha"sv, std::ranges::begin(buf))})); } { auto s = "alPha"sv; assert_stdsv_equal( "alpha"sv, (std::string_view{std::ranges::begin(buf), util::tolower(std::ranges::begin(s), std::ranges::end(s), std::ranges::begin(buf))})); } { assert_stdsv_equal( ""sv, (std::string_view{std::ranges::begin(buf), util::tolower(""sv, std::ranges::begin(buf))})); } { std::string s = "AlpHA\x00BraVO"s; util::tolower(s, std::ranges::begin(s)); assert_stdstring_equal("alpha\x00bravo"s, s); } { std::string s = "\xbe\xef"s; util::tolower(s, std::ranges::begin(s)); assert_stdstring_equal("\xbe\xef"s, s); } } void test_util_to_base64(void) { BlockAllocator balloc(4096, 4096); assert_stdsv_equal("AAA++B/="sv, util::to_base64(balloc, "AAA--B_"sv)); assert_stdsv_equal("AAA++B/B"sv, util::to_base64(balloc, "AAA--B_B"sv)); } void test_util_to_token68(void) { std::string x = "AAA++B/="; util::to_token68(x); assert_stdstring_equal("AAA--B_", x); x = "AAA++B/B"; util::to_token68(x); assert_stdstring_equal("AAA--B_B", x); } void test_util_percent_encode_token(void) { std::array buf; assert_stdsv_equal( "h2"sv, as_string_view( std::ranges::begin(buf), util::percent_encode_token("h2"sv, std::ranges::begin(buf)))); assert_size("h2"sv.size(), ==, util::percent_encode_tokenlen("h2"sv)); assert_stdsv_equal( "h3~"sv, as_string_view( std::ranges::begin(buf), util::percent_encode_token("h3~"sv, std::ranges::begin(buf)))); assert_size("h3~"sv.size(), ==, util::percent_encode_tokenlen("h3~"sv)); assert_stdsv_equal("100%25"sv, as_string_view(std::ranges::begin(buf), util::percent_encode_token( "100%"sv, std::ranges::begin(buf)))); assert_size("100%25"sv.size(), ==, util::percent_encode_tokenlen("100%"sv)); assert_stdsv_equal("http%202"sv, as_string_view(std::ranges::begin(buf), util::percent_encode_token( "http 2"sv, std::ranges::begin(buf)))); assert_size("http%202"sv.size(), ==, util::percent_encode_tokenlen("http 2"sv)); } void test_util_percent_decode(void) { { std::string s = "%66%6F%6f%62%61%72"; assert_stdstring_equal("foobar", util::percent_decode(std::ranges::begin(s), std::ranges::end(s))); } { std::string s = "%66%6"; assert_stdstring_equal("f%6", util::percent_decode(s)); } { std::string s = "%66%"; assert_stdstring_equal( "f%", util::percent_decode(std::ranges::begin(s), std::ranges::end(s))); } BlockAllocator balloc(1024, 1024); assert_stdsv_equal("foobar"sv, util::percent_decode(balloc, "%66%6F%6f%62%61%72"sv)); assert_stdsv_equal("f%6"sv, util::percent_decode(balloc, "%66%6"sv)); assert_stdsv_equal("f%"sv, util::percent_decode(balloc, "%66%"sv)); } void test_util_quote_string(void) { BlockAllocator balloc(4096, 4096); assert_stdsv_equal("alpha"sv, util::quote_string(balloc, "alpha"sv)); assert_stdsv_equal(""sv, util::quote_string(balloc, ""sv)); assert_stdsv_equal("\\\"alpha\\\""sv, util::quote_string(balloc, "\"alpha\""sv)); assert_size("\\\"alpha\\\""sv.size(), ==, util::quote_stringlen("\"alpha\""sv)); assert_size(0, ==, util::quote_stringlen(""sv)); } void test_util_utox(void) { std::array buf; assert_stdsv_equal( "0"sv, (std::string_view{std::ranges::begin(buf), util::utox(0, std::ranges::begin(buf))})); assert_stdsv_equal( "1"sv, (std::string_view{std::ranges::begin(buf), util::utox(1, std::ranges::begin(buf))})); assert_stdsv_equal( "F"sv, (std::string_view{std::ranges::begin(buf), util::utox(15, std::ranges::begin(buf))})); assert_stdsv_equal( "10"sv, (std::string_view{std::ranges::begin(buf), util::utox(16, std::ranges::begin(buf))})); assert_stdsv_equal( "3B9ACA07"sv, (std::string_view{std::ranges::begin(buf), util::utox(1000000007, std::ranges::begin(buf))})); assert_stdsv_equal("B5EA98F3663B14A"sv, (std::string_view{std::ranges::begin(buf), util::utox(819278614785929546, std::ranges::begin(buf))})); assert_stdsv_equal( "100000000"sv, (std::string_view{std::ranges::begin(buf), util::utox(1LL << 32, std::ranges::begin(buf))})); } void test_util_http_date(void) { assert_stdstring_equal( "Thu, 01 Jan 1970 00:00:00 GMT"s, util::format_http_date(std::chrono::system_clock::time_point())); assert_stdstring_equal( "Wed, 29 Feb 2012 09:15:16 GMT"s, util::format_http_date(std::chrono::system_clock::from_time_t(1330506916))); std::array http_buf; assert_stdsv_equal( "Thu, 01 Jan 1970 00:00:00 GMT"sv, util::format_http_date(http_buf.data(), std::chrono::system_clock::time_point())); assert_stdsv_equal( "Wed, 29 Feb 2012 09:15:16 GMT"sv, util::format_http_date(http_buf.data(), std::chrono::system_clock::from_time_t(1330506916))); } void test_util_select_h2(void) { const unsigned char *out = nullptr; unsigned char outlen = 0; // Check single entry and select it. const unsigned char t1[] = "\x2h2"; assert_true(util::select_h2(&out, &outlen, t1, sizeof(t1) - 1)); assert_memory_equal(NGHTTP2_PROTO_VERSION_ID_LEN, NGHTTP2_PROTO_VERSION_ID, out); assert_uchar(NGHTTP2_PROTO_VERSION_ID_LEN, ==, outlen); out = nullptr; outlen = 0; // Check the case where id is correct but length is invalid and too // long. const unsigned char t2[] = "\x3h2"; assert_false(util::select_h2(&out, &outlen, t2, sizeof(t2) - 1)); // Check the case where h2 is located after bogus ID. const unsigned char t3[] = "\x2h3\x2h2"; assert_true(util::select_h2(&out, &outlen, t3, sizeof(t3) - 1)); assert_memory_equal(NGHTTP2_PROTO_VERSION_ID_LEN, NGHTTP2_PROTO_VERSION_ID, out); assert_uchar(NGHTTP2_PROTO_VERSION_ID_LEN, ==, outlen); out = nullptr; outlen = 0; // Check the case that last entry's length is invalid and too long. const unsigned char t4[] = "\x2h3\x6h2"; assert_false(util::select_h2(&out, &outlen, t4, sizeof(t4) - 1)); // Check the case that all entries are not supported. const unsigned char t5[] = "\x2h3\x2h4"; assert_false(util::select_h2(&out, &outlen, t5, sizeof(t5) - 1)); } void test_util_ipv6_numeric_addr(void) { assert_true(util::ipv6_numeric_addr("::1")); assert_true( util::ipv6_numeric_addr("2001:0db8:85a3:0042:1000:8a2e:0370:7334")); // IPv4 assert_false(util::ipv6_numeric_addr("127.0.0.1")); // not numeric address assert_false(util::ipv6_numeric_addr("localhost")); } void test_util_count_digit(void) { assert_size(1, ==, util::count_digit(0u)); assert_size(1, ==, util::count_digit(1u)); assert_size(1, ==, util::count_digit(9u)); assert_size(2, ==, util::count_digit(10u)); assert_size(2, ==, util::count_digit(99u)); assert_size(3, ==, util::count_digit(100u)); assert_size(3, ==, util::count_digit(999u)); assert_size(4, ==, util::count_digit(1'000u)); assert_size(4, ==, util::count_digit(9'999u)); assert_size(5, ==, util::count_digit(10'000u)); assert_size(5, ==, util::count_digit(99'999u)); assert_size(6, ==, util::count_digit(100'000u)); assert_size(6, ==, util::count_digit(999'999u)); assert_size(7, ==, util::count_digit(1'000'000u)); assert_size(7, ==, util::count_digit(9'999'999u)); assert_size(8, ==, util::count_digit(10'000'000u)); assert_size(8, ==, util::count_digit(99'999'999u)); assert_size(9, ==, util::count_digit(100'000'000u)); assert_size(9, ==, util::count_digit(999'999'999u)); assert_size(10, ==, util::count_digit(1'000'000'000u)); assert_size(10, ==, util::count_digit(9'999'999'999u)); assert_size(11, ==, util::count_digit(10'000'000'000u)); assert_size(11, ==, util::count_digit(99'999'999'999u)); assert_size(12, ==, util::count_digit(100'000'000'000u)); assert_size(12, ==, util::count_digit(999'999'999'999u)); assert_size(13, ==, util::count_digit(1'000'000'000'000u)); assert_size(13, ==, util::count_digit(9'999'999'999'999u)); assert_size(14, ==, util::count_digit(10'000'000'000'000u)); assert_size(14, ==, util::count_digit(99'999'999'999'999u)); assert_size(15, ==, util::count_digit(100'000'000'000'000u)); assert_size(15, ==, util::count_digit(999'999'999'999'999u)); assert_size(16, ==, util::count_digit(1'000'000'000'000'000u)); assert_size(16, ==, util::count_digit(9'999'999'999'999'999u)); assert_size(17, ==, util::count_digit(10'000'000'000'000'000u)); assert_size(17, ==, util::count_digit(99'999'999'999'999'999u)); assert_size(18, ==, util::count_digit(100'000'000'000'000'000u)); assert_size(18, ==, util::count_digit(999'999'999'999'999'999u)); assert_size(19, ==, util::count_digit(1'000'000'000'000'000'000u)); assert_size(19, ==, util::count_digit(9'999'999'999'999'999'999u)); assert_size(20, ==, util::count_digit(10'000'000'000'000'000'000u)); assert_size(20, ==, util::count_digit(std::numeric_limits::max())); } void test_util_utos(void) { char buf[32]; assert_stdstring_equal("123"s, (std::string{buf, util::utos(123u, buf)})); assert_stdstring_equal("0"s, util::utos(0u)); assert_stdstring_equal("123"s, util::utos(123u)); assert_stdstring_equal("123"s, util::utos(static_cast(123))); assert_stdstring_equal("123"s, util::utos(static_cast(123))); assert_stdstring_equal("18446744073709551615"s, util::utos(18'446'744'073'709'551'615u)); assert_stdsv_equal("0"sv, (std::string_view{buf, util::utos(0u, buf)})); assert_stdsv_equal("1"sv, (std::string_view{buf, util::utos(1u, buf)})); assert_stdsv_equal("9"sv, (std::string_view{buf, util::utos(9u, buf)})); assert_stdsv_equal("10"sv, (std::string_view{buf, util::utos(10u, buf)})); assert_stdsv_equal("99"sv, (std::string_view{buf, util::utos(99u, buf)})); assert_stdsv_equal("100"sv, (std::string_view{buf, util::utos(100u, buf)})); assert_stdsv_equal("999"sv, (std::string_view{buf, util::utos(999u, buf)})); assert_stdsv_equal("1000"sv, (std::string_view{buf, util::utos(1'000u, buf)})); assert_stdsv_equal("9999"sv, (std::string_view{buf, util::utos(9'999u, buf)})); assert_stdsv_equal("10000"sv, (std::string_view{buf, util::utos(10'000u, buf)})); assert_stdsv_equal("99999"sv, (std::string_view{buf, util::utos(99'999u, buf)})); assert_stdsv_equal("100000"sv, (std::string_view{buf, util::utos(100'000u, buf)})); assert_stdsv_equal("999999"sv, (std::string_view{buf, util::utos(999'999u, buf)})); assert_stdsv_equal("1000000"sv, (std::string_view{buf, util::utos(1'000'000u, buf)})); assert_stdsv_equal("9999999"sv, (std::string_view{buf, util::utos(9'999'999u, buf)})); assert_stdsv_equal("10000000"sv, (std::string_view{buf, util::utos(10'000'000u, buf)})); assert_stdsv_equal("99999999"sv, (std::string_view{buf, util::utos(99'999'999u, buf)})); assert_stdsv_equal("100000000"sv, (std::string_view{buf, util::utos(100'000'000u, buf)})); assert_stdsv_equal("999999999"sv, (std::string_view{buf, util::utos(999'999'999u, buf)})); assert_stdsv_equal("1000000000"sv, (std::string_view{buf, util::utos(1'000'000'000u, buf)})); assert_stdsv_equal("9999999999"sv, (std::string_view{buf, util::utos(9'999'999'999u, buf)})); assert_stdsv_equal("10000000000"sv, (std::string_view{buf, util::utos(10'000'000'000u, buf)})); assert_stdsv_equal("99999999999"sv, (std::string_view{buf, util::utos(99'999'999'999u, buf)})); assert_stdsv_equal( "100000000000"sv, (std::string_view{buf, util::utos(100'000'000'000u, buf)})); assert_stdsv_equal( "999999999999"sv, (std::string_view{buf, util::utos(999'999'999'999u, buf)})); assert_stdsv_equal( "1000000000000"sv, (std::string_view{buf, util::utos(1'000'000'000'000u, buf)})); assert_stdsv_equal( "9999999999999"sv, (std::string_view{buf, util::utos(9'999'999'999'999u, buf)})); assert_stdsv_equal( "10000000000000"sv, (std::string_view{buf, util::utos(10'000'000'000'000u, buf)})); assert_stdsv_equal( "99999999999999"sv, (std::string_view{buf, util::utos(99'999'999'999'999u, buf)})); assert_stdsv_equal( "100000000000000"sv, (std::string_view{buf, util::utos(100'000'000'000'000u, buf)})); assert_stdsv_equal( "999999999999999"sv, (std::string_view{buf, util::utos(999'999'999'999'999u, buf)})); assert_stdsv_equal( "1000000000000000"sv, (std::string_view{buf, util::utos(1'000'000'000'000'000u, buf)})); assert_stdsv_equal( "9999999999999999"sv, (std::string_view{buf, util::utos(9'999'999'999'999'999u, buf)})); assert_stdsv_equal( "10000000000000000"sv, (std::string_view{buf, util::utos(10'000'000'000'000'000u, buf)})); assert_stdsv_equal( "99999999999999999"sv, (std::string_view{buf, util::utos(99'999'999'999'999'999u, buf)})); assert_stdsv_equal( "100000000000000000"sv, (std::string_view{buf, util::utos(100'000'000'000'000'000u, buf)})); assert_stdsv_equal( "999999999999999999"sv, (std::string_view{buf, util::utos(999'999'999'999'999'999u, buf)})); assert_stdsv_equal( "1000000000000000000"sv, (std::string_view{buf, util::utos(1'000'000'000'000'000'000u, buf)})); assert_stdsv_equal( "9999999999999999999"sv, (std::string_view{buf, util::utos(9'999'999'999'999'999'999u, buf)})); assert_stdsv_equal( "10000000000000000000"sv, (std::string_view{buf, util::utos(10'000'000'000'000'000'000u, buf)})); assert_stdsv_equal( "18446744073709551615"sv, (std::string_view{buf, util::utos(std::numeric_limits::max(), buf)})); } void test_util_make_string_ref_uint(void) { BlockAllocator balloc(1024, 1024); assert_stdsv_equal("0"sv, util::make_string_ref_uint(balloc, 0u)); assert_stdsv_equal("123"sv, util::make_string_ref_uint(balloc, 123u)); assert_stdsv_equal( "18446744073709551615"sv, util::make_string_ref_uint(balloc, 18446744073709551615ULL)); } void test_util_utos_unit(void) { assert_stdstring_equal("0", util::utos_unit(0u)); assert_stdstring_equal("1023", util::utos_unit(1023u)); assert_stdstring_equal("1K", util::utos_unit(1024u)); assert_stdstring_equal("1K", util::utos_unit(1025u)); assert_stdstring_equal("1M", util::utos_unit(1u << 20)); assert_stdstring_equal("1G", util::utos_unit(1u << 30)); assert_stdstring_equal("1024G", util::utos_unit(1ULL << 40)); } void test_util_utos_funit(void) { assert_stdstring_equal("0", util::utos_funit(0u)); assert_stdstring_equal("1023", util::utos_funit(1023u)); assert_stdstring_equal("1.00K", util::utos_funit(1024u)); assert_stdstring_equal("1.00K", util::utos_funit(1025u)); assert_stdstring_equal("1.09K", util::utos_funit(1119u)); assert_stdstring_equal("1.27K", util::utos_funit(1300u)); assert_stdstring_equal("1.00M", util::utos_funit(1u << 20)); assert_stdstring_equal("1.18M", util::utos_funit(1234567u)); assert_stdstring_equal("1.00G", util::utos_funit(1u << 30)); assert_stdstring_equal("4492450797.23G", util::utos_funit(4823732313248234343ULL)); assert_stdstring_equal("1024.00G", util::utos_funit(1ULL << 40)); } void test_util_parse_uint_with_unit(void) { assert_int64(0, ==, util::parse_uint_with_unit("0").value_or(-1)); assert_int64(1023, ==, util::parse_uint_with_unit("1023").value_or(-1)); assert_int64(1024, ==, util::parse_uint_with_unit("1k").value_or(-1)); assert_int64(2048, ==, util::parse_uint_with_unit("2K").value_or(-1)); assert_int64(1 << 20, ==, util::parse_uint_with_unit("1m").value_or(-1)); assert_int64(1 << 21, ==, util::parse_uint_with_unit("2M").value_or(-1)); assert_int64(1 << 30, ==, util::parse_uint_with_unit("1g").value_or(-1)); assert_int64(1LL << 31, ==, util::parse_uint_with_unit("2G").value_or(-1)); assert_int64(9223372036854775807LL, ==, util::parse_uint_with_unit("9223372036854775807").value_or(-1)); // check overflow case assert_false(util::parse_uint_with_unit("9223372036854775808")); assert_false(util::parse_uint_with_unit("10000000000000000000")); assert_false(util::parse_uint_with_unit("9223372036854775807G")); // bad characters assert_false(util::parse_uint_with_unit("1.1")); assert_false(util::parse_uint_with_unit("1a")); assert_false(util::parse_uint_with_unit("a1")); assert_false(util::parse_uint_with_unit("1T")); assert_false(util::parse_uint_with_unit("")); } void test_util_parse_uint(void) { assert_int64(0, ==, util::parse_uint("0").value_or(-1)); assert_int64(1023, ==, util::parse_uint("1023").value_or(-1)); assert_false(util::parse_uint("1k")); assert_int64(9223372036854775807LL, ==, util::parse_uint("9223372036854775807").value_or(-1)); // check overflow case assert_false(util::parse_uint("9223372036854775808")); assert_false(util::parse_uint("10000000000000000000")); // bad characters assert_false(util::parse_uint("1.1")); assert_false(util::parse_uint("1a")); assert_false(util::parse_uint("a1")); assert_false(util::parse_uint("1T")); assert_false(util::parse_uint("")); } void test_util_parse_duration_with_unit(void) { auto inf = std::numeric_limits::infinity(); assert_double(0., ==, util::parse_duration_with_unit("0").value_or(inf)); assert_double(123., ==, util::parse_duration_with_unit("123").value_or(inf)); assert_double(123., ==, util::parse_duration_with_unit("123s").value_or(inf)); assert_double(0.500, ==, util::parse_duration_with_unit("500ms").value_or(inf)); assert_double(123., ==, util::parse_duration_with_unit("123S").value_or(inf)); assert_double(0.500, ==, util::parse_duration_with_unit("500MS").value_or(inf)); assert_double(180, ==, util::parse_duration_with_unit("3m").value_or(inf)); assert_double(3600 * 5, ==, util::parse_duration_with_unit("5h").value_or(inf)); // check overflow case assert_false(util::parse_duration_with_unit("9223372036854775808")); // bad characters assert_false(util::parse_duration_with_unit("0u")); assert_false(util::parse_duration_with_unit("0xs")); assert_false(util::parse_duration_with_unit("0mt")); assert_false(util::parse_duration_with_unit("0mss")); assert_false(util::parse_duration_with_unit("s")); assert_false(util::parse_duration_with_unit("ms")); } void test_util_duration_str(void) { assert_stdstring_equal("0", util::duration_str(0.)); assert_stdstring_equal("1s", util::duration_str(1.)); assert_stdstring_equal("500ms", util::duration_str(0.5)); assert_stdstring_equal("1500ms", util::duration_str(1.5)); assert_stdstring_equal("2m", util::duration_str(120.)); assert_stdstring_equal("121s", util::duration_str(121.)); assert_stdstring_equal("1h", util::duration_str(3600.)); } void test_util_format_duration(void) { assert_stdstring_equal("0us", util::format_duration(std::chrono::microseconds(0))); assert_stdstring_equal("999us", util::format_duration(std::chrono::microseconds(999))); assert_stdstring_equal( "1.00ms", util::format_duration(std::chrono::microseconds(1000))); assert_stdstring_equal( "1.09ms", util::format_duration(std::chrono::microseconds(1090))); assert_stdstring_equal( "1.01ms", util::format_duration(std::chrono::microseconds(1009))); assert_stdstring_equal( "999.99ms", util::format_duration(std::chrono::microseconds(999990))); assert_stdstring_equal( "1.00s", util::format_duration(std::chrono::microseconds(1000000))); assert_stdstring_equal( "1.05s", util::format_duration(std::chrono::microseconds(1050000))); assert_stdstring_equal("0us", util::format_duration(0.)); assert_stdstring_equal("999us", util::format_duration(0.000999)); assert_stdstring_equal("1.00ms", util::format_duration(0.001)); assert_stdstring_equal("1.09ms", util::format_duration(0.00109)); assert_stdstring_equal("1.01ms", util::format_duration(0.001009)); assert_stdstring_equal("999.99ms", util::format_duration(0.99999)); assert_stdstring_equal("1.00s", util::format_duration(1.)); assert_stdstring_equal("1.05s", util::format_duration(1.05)); } void test_util_starts_with(void) { assert_true(util::starts_with("foo"sv, "foo"sv)); assert_true(util::starts_with("fooo"sv, "foo"sv)); assert_true(util::starts_with("ofoo"sv, ""sv)); assert_false(util::starts_with("ofoo"sv, "foo"sv)); assert_true(util::istarts_with("FOO"sv, "fOO"sv)); assert_true(util::istarts_with("ofoo"sv, ""sv)); assert_true(util::istarts_with("fOOo"sv, "Foo"sv)); assert_false(util::istarts_with("ofoo"sv, "foo"sv)); } void test_util_ends_with(void) { assert_true(util::ends_with("foo"sv, "foo"sv)); assert_true(util::ends_with("foo"sv, ""sv)); assert_true(util::ends_with("ofoo"sv, "foo"sv)); assert_false(util::ends_with("ofoo"sv, "fo"sv)); assert_true(util::iends_with("fOo"sv, "Foo"sv)); assert_true(util::iends_with("foo"sv, ""sv)); assert_true(util::iends_with("oFoo"sv, "fOO"sv)); assert_false(util::iends_with("ofoo"sv, "fo"sv)); } void test_util_parse_http_date(void) { assert_int64(1001939696, ==, util::parse_http_date("Mon, 1 Oct 2001 12:34:56 GMT"sv)); } void test_util_localtime_date(void) { std::array buf; #ifdef HAVE_STD_CHRONO_TIME_ZONE assert_stdsv_equal( "2001-10-02T00:34:56.123+12:00"sv, util::format_iso8601(buf.data(), std::chrono::system_clock::time_point( std::chrono::milliseconds(1001939696123LL)), std::chrono::locate_zone("Pacific/Auckland"sv))); assert_stdsv_equal( "20011002T003456.123+1200"sv, util::format_iso8601_basic(buf.data(), std::chrono::system_clock::time_point( std::chrono::milliseconds(1001939696123LL)), std::chrono::locate_zone("Pacific/Auckland"sv))); assert_stdsv_equal( "02/Oct/2001:00:34:56 +1200"sv, util::format_common_log(buf.data(), std::chrono::system_clock::from_time_t(1001939696), std::chrono::locate_zone("Pacific/Auckland"sv))); assert_stdsv_equal( "2001-10-01T12:34:56.123Z"sv, util::format_iso8601(buf.data(), std::chrono::system_clock::time_point( std::chrono::milliseconds(1001939696123LL)), std::chrono::locate_zone("GMT"sv))); assert_stdsv_equal( "20011001T123456.123Z"sv, util::format_iso8601_basic(buf.data(), std::chrono::system_clock::time_point( std::chrono::milliseconds(1001939696123LL)), std::chrono::locate_zone("GMT"sv))); assert_stdsv_equal( "01/Oct/2001:12:34:56 +0000"sv, util::format_common_log(buf.data(), std::chrono::system_clock::from_time_t(1001939696), std::chrono::locate_zone("GMT"sv))); #else // !defined(HAVE_STD_CHRONO_TIME_ZONE) auto tz = getenv("TZ"); if (tz) { tz = strdup(tz); } # ifdef __linux__ setenv("TZ", "NZST-12:00:00:00", 1); # else // !defined(__linux__) setenv("TZ", ":Pacific/Auckland", 1); # endif // !defined(__linux__) tzset(); assert_stdsv_equal( "2001-10-02T00:34:56.123+12:00"sv, util::format_iso8601(buf.data(), std::chrono::system_clock::time_point( std::chrono::milliseconds(1001939696123LL)))); assert_stdsv_equal( "20011002T003456.123+1200"sv, util::format_iso8601_basic(buf.data(), std::chrono::system_clock::time_point( std::chrono::milliseconds(1001939696123LL)))); assert_stdsv_equal( "02/Oct/2001:00:34:56 +1200"sv, util::format_common_log( buf.data(), std::chrono::system_clock::from_time_t(1001939696))); if (tz) { setenv("TZ", tz, 1); free(tz); } else { unsetenv("TZ"); } tzset(); #endif // !defined(HAVE_STD_CHRONO_TIME_ZONE) } void test_util_get_uint64(void) { { auto v = std::to_array( {0x01, 0x12, 0x34, 0x56, 0xff, 0x9a, 0xab, 0xbc}); auto n = util::get_uint64(v.data()); assert_uint64(0x01123456ff9aabbcULL, ==, n); } { auto v = std::to_array( {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}); auto n = util::get_uint64(v.data()); assert_uint64(0xffffffffffffffffULL, ==, n); } } void test_util_parse_config_str_list(void) { auto res = util::parse_config_str_list("a"sv); assert_size(1, ==, res.size()); assert_stdstring_equal("a", res[0]); res = util::parse_config_str_list("a,"sv); assert_size(2, ==, res.size()); assert_stdstring_equal("a", res[0]); assert_stdstring_equal("", res[1]); res = util::parse_config_str_list(":a::"sv, ':'); assert_size(4, ==, res.size()); assert_stdstring_equal("", res[0]); assert_stdstring_equal("a", res[1]); assert_stdstring_equal("", res[2]); assert_stdstring_equal("", res[3]); res = util::parse_config_str_list(""sv); assert_size(1, ==, res.size()); assert_stdstring_equal("", res[0]); res = util::parse_config_str_list("alpha,bravo,charlie"sv); assert_size(3, ==, res.size()); assert_stdstring_equal("alpha", res[0]); assert_stdstring_equal("bravo", res[1]); assert_stdstring_equal("charlie", res[2]); } void test_util_make_http_hostport(void) { BlockAllocator balloc(4096, 4096); assert_stdsv_equal("localhost"sv, util::make_http_hostport(balloc, "localhost"sv, 80)); assert_stdsv_equal("[::1]"sv, util::make_http_hostport(balloc, "::1"sv, 443)); assert_stdsv_equal("localhost:3000"sv, util::make_http_hostport(balloc, "localhost"sv, 3000)); } void test_util_make_hostport(void) { std::array hostport_buf; assert_stdsv_equal( "localhost:80"sv, util::make_hostport("localhost"sv, 80, std::ranges::begin(hostport_buf))); assert_stdsv_equal( "[::1]:443"sv, util::make_hostport("::1"sv, 443, std::ranges::begin(hostport_buf))); BlockAllocator balloc(4096, 4096); assert_stdsv_equal("localhost:80"sv, util::make_hostport(balloc, "localhost"sv, 80)); assert_stdsv_equal("[::1]:443"sv, util::make_hostport(balloc, "::1"sv, 443)); } void test_util_random_alpha_digit(void) { std::random_device rd; std::mt19937 gen(rd()); std::array data; auto p = util::random_alpha_digit(std::ranges::begin(data), std::ranges::end(data), gen); assert_true(std::ranges::end(data) == p); for (auto b : data) { assert_true(('A' <= b && b <= 'Z') || ('a' <= b && b <= 'z') || ('0' <= b && b <= '9')); } } void test_util_format_hex(void) { BlockAllocator balloc(4096, 4096); assert_stdsv_equal("0ff0"sv, util::format_hex(balloc, "\x0f\xf0"sv)); assert_stdsv_equal(""sv, util::format_hex(balloc, ""sv)); std::string o; o.resize(4); assert_true(std::ranges::end(o) == util::format_hex("\xbe\xef"sv, std::ranges::begin(o))); assert_stdstring_equal("beef"s, o); assert_stdstring_equal("beef"s, util::format_hex("\xbe\xef"sv)); std::array buf; assert_stdsv_equal( "00"sv, (std::string_view{std::ranges::begin(buf), util::format_hex(static_cast(0u), std::ranges::begin(buf))})); assert_stdsv_equal( "ec"sv, (std::string_view{std::ranges::begin(buf), util::format_hex(static_cast(0xecu), std::ranges::begin(buf))})); assert_stdsv_equal( "00000000"sv, (std::string_view{std::ranges::begin(buf), util::format_hex(0u, std::ranges::begin(buf))})); assert_stdsv_equal( "0000ab01"sv, (std::string_view{std::ranges::begin(buf), util::format_hex(0xab01u, std::ranges::begin(buf))})); assert_stdsv_equal( "deadbeefbaadf00d"sv, (std::string_view{ std::ranges::begin(buf), util::format_hex(0xdeadbeefbaadf00du, std::ranges::begin(buf))})); assert_stdsv_equal( "ffffffffffffffff"sv, (std::string_view{std::ranges::begin(buf), util::format_hex(std::numeric_limits::max(), std::ranges::begin(buf))})); } void test_util_format_upper_hex(void) { std::array buf; assert_stdsv_equal( "00"sv, (std::string_view{std::ranges::begin(buf), util::format_upper_hex(0, std::ranges::begin(buf))})); assert_stdsv_equal( "0A"sv, (std::string_view{std::ranges::begin(buf), util::format_upper_hex(0xa, std::ranges::begin(buf))})); assert_stdsv_equal( "7C"sv, (std::string_view{std::ranges::begin(buf), util::format_upper_hex(0x07c, std::ranges::begin(buf))})); assert_stdsv_equal( "EB"sv, (std::string_view{std::ranges::begin(buf), util::format_upper_hex(0xeb, std::ranges::begin(buf))})); assert_stdsv_equal( "FF"sv, (std::string_view{std::ranges::begin(buf), util::format_upper_hex(0xff, std::ranges::begin(buf))})); } void test_util_is_hex_string(void) { assert_true(util::is_hex_string(""sv)); assert_true(util::is_hex_string("0123456789abcdef"sv)); assert_true(util::is_hex_string("0123456789ABCDEF"sv)); assert_false(util::is_hex_string("000"sv)); assert_false(util::is_hex_string("XX"sv)); } void test_util_decode_hex(void) { BlockAllocator balloc(4096, 4096); assert_stdsv_equal("\x0f\xf0"sv, as_string_view(util::decode_hex(balloc, "0ff0"sv))); assert_stdsv_equal(""sv, as_string_view(util::decode_hex(balloc, ""sv))); } void test_util_extract_host(void) { assert_stdsv_equal("foo"sv, util::extract_host("foo"sv)); assert_stdsv_equal("foo"sv, util::extract_host("foo:"sv)); assert_stdsv_equal("foo"sv, util::extract_host("foo:0"sv)); assert_stdsv_equal("[::1]"sv, util::extract_host("[::1]"sv)); assert_stdsv_equal("[::1]"sv, util::extract_host("[::1]:"sv)); assert_true(util::extract_host(":foo"sv).empty()); assert_true(util::extract_host("[::1"sv).empty()); assert_true(util::extract_host("[::1]0"sv).empty()); assert_true(util::extract_host(""sv).empty()); } void test_util_split_hostport(void) { assert_true(std::make_pair("foo"sv, ""sv) == util::split_hostport("foo"sv)); assert_true(std::make_pair("foo"sv, "80"sv) == util::split_hostport("foo:80"sv)); assert_true(std::make_pair("::1"sv, "80"sv) == util::split_hostport("[::1]:80"sv)); assert_true(std::make_pair("::1"sv, ""sv) == util::split_hostport("[::1]"sv)); assert_true(std::make_pair(""sv, ""sv) == util::split_hostport(""sv)); assert_true(std::make_pair(""sv, ""sv) == util::split_hostport("[::1]:"sv)); assert_true(std::make_pair(""sv, ""sv) == util::split_hostport("foo:"sv)); assert_true(std::make_pair(""sv, ""sv) == util::split_hostport("[::1:"sv)); assert_true(std::make_pair(""sv, ""sv) == util::split_hostport("[::1]80"sv)); } void test_util_split_str(void) { assert_true(std::vector{""sv} == util::split_str(""sv, ',')); assert_true(std::vector{"alpha"sv} == util::split_str("alpha"sv, ',')); assert_true((std::vector{"alpha"sv, ""sv}) == util::split_str("alpha,"sv, ',')); assert_true((std::vector{"alpha"sv, "bravo"sv}) == util::split_str("alpha,bravo"sv, ',')); assert_true( (std::vector{"alpha"sv, "bravo"sv, "charlie"sv}) == util::split_str("alpha,bravo,charlie"sv, ',')); assert_true( (std::vector{"alpha"sv, "bravo"sv, "charlie"sv}) == util::split_str("alpha,bravo,charlie"sv, ',', 0)); assert_true(std::vector{""sv} == util::split_str(""sv, ',', 1)); assert_true(std::vector{""sv} == util::split_str(""sv, ',', 2)); assert_true((std::vector{"alpha"sv, "bravo,charlie"sv}) == util::split_str("alpha,bravo,charlie"sv, ',', 2)); assert_true(std::vector{"alpha"sv} == util::split_str("alpha"sv, ',', 2)); assert_true((std::vector{"alpha"sv, ""sv}) == util::split_str("alpha,"sv, ',', 2)); assert_true(std::vector{"alpha"sv} == util::split_str("alpha"sv, ',', 0)); assert_true(std::vector{"alpha,bravo,charlie"sv} == util::split_str("alpha,bravo,charlie"sv, ',', 1)); } void test_util_rstrip(void) { BlockAllocator balloc(4096, 4096); assert_stdsv_equal("alpha"sv, util::rstrip(balloc, "alpha"sv)); assert_stdsv_equal("alpha"sv, util::rstrip(balloc, "alpha "sv)); assert_stdsv_equal("alpha"sv, util::rstrip(balloc, "alpha \t"sv)); assert_stdsv_equal(""sv, util::rstrip(balloc, ""sv)); assert_stdsv_equal(""sv, util::rstrip(balloc, "\t\t\t "sv)); } void test_util_contains(void) { assert_true(util::contains("alphabravo"sv, 'a')); assert_true(util::contains("alphabravo"sv, 'o')); assert_false(util::contains("alphabravo"sv, 'x')); assert_false(util::contains(""sv, ' ')); } void test_util_hex_to_uint(void) { for (size_t i = 0; i < 256; ++i) { auto c = static_cast(i); if (!util::is_hex_digit(c)) { assert_uint32(256, ==, util::hex_to_uint(c)); } } for (uint32_t i = 0; i < 10; ++i) { assert_uint32(i, ==, util::hex_to_uint(static_cast('0' + i))); } for (uint32_t i = 0; i < 6; ++i) { assert_uint32(i + 10, ==, util::hex_to_uint(static_cast('A' + i))); } for (uint32_t i = 0; i < 6; ++i) { assert_uint32(i + 10, ==, util::hex_to_uint(static_cast('a' + i))); } } void test_util_is_alpha(void) { for (size_t i = 0; i < 256; ++i) { auto c = static_cast(i); if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) { assert_true(util::is_alpha(c)); } else { assert_false(util::is_alpha(c)); } } } void test_util_is_digit(void) { for (size_t i = 0; i < 256; ++i) { auto c = static_cast(i); if ('0' <= c && c <= '9') { assert_true(util::is_digit(c)); } else { assert_false(util::is_digit(c)); } } } void test_util_is_hex_digit(void) { for (size_t i = 0; i < 256; ++i) { auto c = static_cast(i); if (util::is_digit(c) || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f')) { assert_true(util::is_hex_digit(c)); } else { assert_false(util::is_hex_digit(c)); } } } void test_util_in_rfc3986_unreserved_chars(void) { for (size_t i = 0; i < 256; ++i) { auto c = static_cast(i); switch (c) { case '-': case '.': case '_': case '~': assert_true(util::in_rfc3986_unreserved_chars(c)); break; default: if (util::is_digit(c) || util::is_alpha(c)) { assert_true(util::in_rfc3986_unreserved_chars(c)); } else { assert_false(util::in_rfc3986_unreserved_chars(c)); } } } } void test_util_in_rfc3986_sub_delims(void) { for (size_t i = 0; i < 256; ++i) { auto c = static_cast(i); switch (c) { case '!': case '$': case '&': case '\'': case '(': case ')': case '*': case '+': case ',': case ';': case '=': assert_true(util::in_rfc3986_sub_delims(c)); break; default: assert_false(util::in_rfc3986_sub_delims(c)); } } } void test_util_in_token(void) { for (size_t i = 0; i < 256; ++i) { auto c = static_cast(i); switch (c) { case '!': case '#': case '$': case '%': case '&': case '\'': case '*': case '+': case '-': case '.': case '^': case '_': case '`': case '|': case '~': assert_true(util::in_token(c)); break; default: if (util::is_digit(c) || util::is_alpha(c)) { assert_true(util::in_token(c)); } else { assert_false(util::in_token(c)); } } } } void test_util_in_attr_char(void) { for (size_t i = 0; i < 256; ++i) { auto c = static_cast(i); switch (c) { case '%': case '\'': case '*': assert_false(util::in_attr_char(c)); break; default: if (util::in_token(c)) { assert_true(util::in_attr_char(c)); } else { assert_false(util::in_attr_char(c)); } } } } void test_util_upcase(void) { for (size_t i = 0; i < 256; ++i) { auto c = static_cast(i); if ('a' <= c && c <= 'z') { assert_char(static_cast(c - 'a' + 'A'), ==, util::upcase(c)); } else { assert_char(c, ==, util::upcase(c)); } } } void test_util_to_numeric_addr(void) { addrinfo hints{ .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV, .ai_family = AF_UNSPEC, }; addrinfo *res; auto rv = getaddrinfo("192.168.0.1", "443", &hints, &res); assert_int(0, ==, rv); assert_stdstring_equal("192.168.0.1:443"s, util::to_numeric_addr(res->ai_addr, res->ai_addrlen)); freeaddrinfo(res); rv = getaddrinfo("2001:0db8:85a3:0000:0000:8a2e:0370:7334", "4443", &hints, &res); assert_int(0, ==, rv); assert_stdstring_equal("[2001:db8:85a3::8a2e:370:7334]:4443"s, util::to_numeric_addr(res->ai_addr, res->ai_addrlen)); freeaddrinfo(res); } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/memchunk.h0000644000000000000000000000013215077107270015027 xustar0030 mtime=1761382072.988444162 30 atime=1761382106.173310563 30 ctime=1761382109.169300182 nghttp2-1.68.0/src/memchunk.h0000644000175100017510000003331615077107270015425 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MEMCHUNK_H #define MEMCHUNK_H #include "nghttp2_config.h" #include #ifdef _WIN32 /* Structure for scatter/gather I/O. */ struct iovec { void *iov_base; /* Pointer to data. */ size_t iov_len; /* Length of data. */ }; #else // !defined(_WIN32) # include #endif // !defined(_WIN32) #include #include #include #include #include #include #include #include "template.h" namespace nghttp2 { #define DEFAULT_WR_IOVCNT 16 #if defined(IOV_MAX) && IOV_MAX < DEFAULT_WR_IOVCNT # define MAX_WR_IOVCNT IOV_MAX #else // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT # define MAX_WR_IOVCNT DEFAULT_WR_IOVCNT #endif // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT template struct Memchunk { Memchunk(Memchunk *next_chunk) : pos(std::ranges::begin(buf)), last(pos), knext(next_chunk), next(nullptr) {} size_t len() const { return as_unsigned(last - pos); } size_t left() const { return static_cast(std::ranges::end(buf) - last); } void reset() { pos = last = std::ranges::begin(buf); } std::array buf; uint8_t *pos, *last; Memchunk *knext; Memchunk *next; static const size_t size = N; }; template struct Pool { Pool() : pool(nullptr), freelist(nullptr), poolsize(0), freelistsize(0) {} ~Pool() { clear(); } T *get() { if (freelist) { auto m = freelist; freelist = freelist->next; m->next = nullptr; m->reset(); freelistsize -= T::size; return m; } pool = new T{pool}; poolsize += T::size; return pool; } void recycle(T *m) { m->next = freelist; freelist = m; freelistsize += T::size; } void clear() { freelist = nullptr; freelistsize = 0; for (auto p = pool; p;) { auto knext = p->knext; delete p; p = knext; } pool = nullptr; poolsize = 0; } using value_type = T; T *pool; T *freelist; size_t poolsize; size_t freelistsize; }; template struct Memchunks { Memchunks(Pool *pool) : pool(pool), head(nullptr), tail(nullptr), len(0), mark(nullptr), mark_pos(nullptr), mark_offset(0) {} Memchunks(const Memchunks &) = delete; Memchunks(Memchunks &&other) noexcept : pool{other.pool}, // keep other.pool head{std::exchange(other.head, nullptr)}, tail{std::exchange(other.tail, nullptr)}, len{std::exchange(other.len, 0)}, mark{std::exchange(other.mark, nullptr)}, mark_pos{std::exchange(other.mark_pos, nullptr)}, mark_offset{std::exchange(other.mark_offset, 0)} {} Memchunks &operator=(const Memchunks &) = delete; Memchunks &operator=(Memchunks &&other) noexcept { if (this == &other) { return *this; } reset(); pool = other.pool; head = std::exchange(other.head, nullptr); tail = std::exchange(other.tail, nullptr); len = std::exchange(other.len, 0); mark = std::exchange(other.mark, nullptr); mark_pos = std::exchange(other.mark_pos, nullptr); mark_offset = std::exchange(other.mark_offset, 0); return *this; } ~Memchunks() { if (!pool) { return; } for (auto m = head; m;) { auto next = m->next; pool->recycle(m); m = next; } } void append(char c) { if (!tail) { head = tail = pool->get(); } else if (tail->left() == 0) { tail->next = pool->get(); tail = tail->next; } *tail->last++ = as_unsigned(c); ++len; } template void append(I first, I last) { if (first == last) { return; } if (!tail) { head = tail = pool->get(); } for (;;) { auto n = std::min(static_cast(std::ranges::distance(first, last)), tail->left()); auto iores = std::ranges::copy_n(first, as_signed(n), tail->last); first = iores.in; tail->last = iores.out; len += n; if (first == last) { break; } tail->next = pool->get(); tail = tail->next; } return; } void append(const void *src, size_t count) { auto s = static_cast(src); append(s, s + count); } template requires(!std::is_array_v>) void append(R &&r) { append(std::ranges::begin(r), std::ranges::end(r)); } // first ensures that at least |max_count| bytes are available to // store in the current buffer, assuming that the chunk size of the // underlying Memchunk is at least |max_count| bytes. Then call // |f|(tail->last) to write data into buffer directly. |f| must not // write more than |max_count| bytes. It must return the position // of the buffer past the last position written. template requires(std::invocable && std::is_same_v, uint8_t *>) void append(size_t max_count, F f) { if (!tail) { head = tail = pool->get(); } else if (tail->left() < max_count) { tail->next = pool->get(); tail = tail->next; } assert(tail->left() >= max_count); auto last = f(tail->last); len += static_cast(last - tail->last); tail->last = last; } size_t copy(Memchunks &dest) { auto m = head; while (m) { dest.append(m->pos, m->len()); m = m->next; } return len; } size_t remove(void *dest, size_t count) { assert(mark == nullptr); if (!tail || count == 0) { return 0; } auto first = static_cast(dest); auto last = first + count; auto m = head; while (m) { auto next = m->next; auto n = std::min(static_cast(last - first), m->len()); assert(m->len()); auto iores = std::ranges::copy_n(m->pos, as_signed(n), first); m->pos = iores.in; first = iores.out; len -= n; if (m->len() > 0) { break; } pool->recycle(m); m = next; } head = m; if (head == nullptr) { tail = nullptr; } return as_unsigned(first - static_cast(dest)); } size_t remove(Memchunks &dest, size_t count) { assert(mark == nullptr); if (!tail || count == 0) { return 0; } auto left = count; auto m = head; while (m) { auto next = m->next; auto n = std::min(left, m->len()); assert(m->len()); dest.append(m->pos, n); m->pos += n; len -= n; left -= n; if (m->len() > 0) { break; } pool->recycle(m); m = next; } head = m; if (head == nullptr) { tail = nullptr; } return count - left; } size_t remove(Memchunks &dest) { assert(pool == dest.pool); assert(mark == nullptr); if (head == nullptr) { return 0; } auto n = len; if (dest.tail == nullptr) { dest.head = head; } else { dest.tail->next = head; } dest.tail = tail; dest.len += len; head = tail = nullptr; len = 0; return n; } size_t drain(size_t count) { assert(mark == nullptr); auto ndata = count; auto m = head; while (m) { auto next = m->next; auto n = std::min(count, m->len()); m->pos += n; count -= n; len -= n; if (m->len() > 0) { break; } pool->recycle(m); m = next; } head = m; if (head == nullptr) { tail = nullptr; } return ndata - count; } size_t drain_mark(size_t count) { auto ndata = count; auto m = head; while (m) { auto next = m->next; auto n = std::min(count, m->len()); m->pos += n; count -= n; len -= n; mark_offset -= n; if (m->len() > 0) { assert(mark != m || m->pos <= mark_pos); break; } if (mark == m) { assert(m->pos <= mark_pos); mark = nullptr; mark_pos = nullptr; mark_offset = 0; } pool->recycle(m); m = next; } head = m; if (head == nullptr) { tail = nullptr; } return ndata - count; } int riovec(struct iovec *iov, int iovcnt) const { if (!head) { return 0; } auto m = head; int i; for (i = 0; i < iovcnt && m; ++i, m = m->next) { iov[i].iov_base = m->pos; iov[i].iov_len = m->len(); } return i; } int riovec_mark(struct iovec *iov, int iovcnt) { if (!head || iovcnt == 0) { return 0; } int i = 0; Memchunk *m; if (mark) { if (mark_pos != mark->last) { iov[0].iov_base = mark_pos; iov[0].iov_len = mark->len() - as_unsigned(mark_pos - mark->pos); mark_pos = mark->last; mark_offset += iov[0].iov_len; i = 1; } m = mark->next; } else { i = 0; m = head; } for (; i < iovcnt && m; ++i, m = m->next) { iov[i].iov_base = m->pos; iov[i].iov_len = m->len(); mark = m; mark_pos = m->last; mark_offset += m->len(); } return i; } size_t rleft() const { return len; } size_t rleft_mark() const { return len - mark_offset; } void reset() { for (auto m = head; m;) { auto next = m->next; pool->recycle(m); m = next; } len = 0; head = tail = mark = nullptr; mark_pos = nullptr; mark_offset = 0; } Pool *pool; Memchunk *head, *tail; size_t len; Memchunk *mark; uint8_t *mark_pos; size_t mark_offset; }; using Memchunk16K = Memchunk<16_k>; using MemchunkPool = Pool; using DefaultMemchunks = Memchunks; inline int limit_iovec(struct iovec *iov, int iovcnt, size_t max) { if (max == 0) { return 0; } for (int i = 0; i < iovcnt; ++i) { auto d = std::min(max, iov[i].iov_len); iov[i].iov_len = d; max -= d; if (max == 0) { return i + 1; } } return iovcnt; } // MemchunkBuffer is similar to Buffer, but it uses pooled Memchunk // for its underlying buffer. template struct MemchunkBuffer { MemchunkBuffer(Pool *pool) : pool(pool), chunk(nullptr) {} MemchunkBuffer(const MemchunkBuffer &) = delete; MemchunkBuffer(MemchunkBuffer &&other) noexcept : pool(other.pool), chunk(other.chunk) { other.chunk = nullptr; } MemchunkBuffer &operator=(const MemchunkBuffer &) = delete; MemchunkBuffer &operator=(MemchunkBuffer &&other) noexcept { if (this == &other) { return *this; } pool = other.pool; chunk = other.chunk; other.chunk = nullptr; return *this; } ~MemchunkBuffer() { if (!pool || !chunk) { return; } pool->recycle(chunk); } // Ensures that the underlying buffer is allocated. void ensure_chunk() { if (chunk) { return; } chunk = pool->get(); } // Releases the underlying buffer. void release_chunk() { if (!chunk) { return; } pool->recycle(chunk); chunk = nullptr; } // Returns true if the underlying buffer is allocated. bool chunk_avail() const { return chunk != nullptr; } // The functions below must be called after the underlying buffer is // allocated (use ensure_chunk). // MemchunkBuffer provides the same interface functions with Buffer. // Since we has chunk as a member variable, pos and last are // implemented as wrapper functions. uint8_t *pos() const { return chunk->pos; } uint8_t *last() const { return chunk->last; } size_t rleft() const { return chunk->len(); } size_t wleft() const { return chunk->left(); } size_t write(const void *src, size_t count) { count = std::min(count, wleft()); auto p = static_cast(src); chunk->last = std::ranges::copy_n(p, count, chunk->last).out; return count; } size_t write(size_t count) { count = std::min(count, wleft()); chunk->last += count; return count; } size_t drain(size_t count) { count = std::min(count, rleft()); chunk->pos += count; return count; } size_t drain_reset(size_t count) { count = std::min(count, rleft()); chunk->last = std::ranges::copy(chunk->pos + count, chunk->last, std::ranges::begin(chunk->buf)) .out; chunk->pos = std::ranges::begin(chunk->buf); return count; } void reset() { chunk->reset(); } uint8_t *begin() { return std::ranges::begin(chunk->buf); } uint8_t &operator[](size_t n) { return chunk->buf[n]; } const uint8_t &operator[](size_t n) const { return chunk->buf[n]; } Pool *pool; Memchunk *chunk; }; using DefaultMemchunkBuffer = MemchunkBuffer; } // namespace nghttp2 #endif // !defined(MEMCHUNK_H) nghttp2-1.68.0/src/PaxHeaders/base64_test.cc0000644000000000000000000000013115077107270015500 xustar0029 mtime=1761382072.98444418 30 atime=1761382106.277310105 30 ctime=1761382109.274299878 nghttp2-1.68.0/src/base64_test.cc0000644000175100017510000000655115077107270016100 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "base64_test.h" #include #include #include "munitxx.h" #include #include "base64.h" using namespace std::literals; namespace nghttp2 { namespace { const MunitTest tests[]{ munit_void_test(test_base64_encode), munit_void_test(test_base64_decode), munit_test_end(), }; } // namespace const MunitSuite base64_suite{ "/base64", tests, nullptr, 1, MUNIT_SUITE_OPTION_NONE, }; void test_base64_encode(void) { { std::string in = "\xff"; auto out = base64::encode(in); assert_stdstring_equal("/w==", out); } { std::string in = "\xff\xfe"; auto out = base64::encode(in); assert_stdstring_equal("//4=", out); } { std::string in = "\xff\xfe\xfd"; auto out = base64::encode(in); assert_stdstring_equal("//79", out); } { std::string in = "\xff\xfe\xfd\xfc"; auto out = base64::encode(in); assert_stdstring_equal("//79/A==", out); } } void test_base64_decode(void) { BlockAllocator balloc(4096, 4096); { auto in = "/w=="sv; assert_stdsv_equal("\xff"sv, as_string_view(base64::decode(balloc, in))); } { auto in = "//4="sv; assert_stdsv_equal("\xff\xfe"sv, as_string_view(base64::decode(balloc, in))); } { auto in = "//79"sv; assert_stdsv_equal("\xff\xfe\xfd"sv, as_string_view(base64::decode(balloc, in))); } { auto in = "//79/A=="sv; assert_stdsv_equal("\xff\xfe\xfd\xfc"sv, as_string_view(base64::decode(balloc, in))); } { // we check the number of valid input must be multiples of 4 auto in = "//79="sv; assert_stdsv_equal(""sv, as_string_view(base64::decode(balloc, in))); } { // ending invalid character at the boundary of multiples of 4 is // bad auto in = "bmdodHRw\n"sv; assert_stdsv_equal(""sv, as_string_view(base64::decode(balloc, in))); } { // after seeing '=', subsequent input must be also '='. auto in = "//79/A=A"sv; assert_stdsv_equal(""sv, as_string_view(base64::decode(balloc, in))); } { // additional '=' at the end is bad auto in = "//79/A======"sv; assert_stdsv_equal(""sv, as_string_view(base64::decode(balloc, in))); } } } // namespace nghttp2 nghttp2-1.68.0/src/PaxHeaders/shrpx_mruby_module_request.h0000644000000000000000000000013115077107270020716 xustar0029 mtime=1761382072.99744412 30 atime=1761382106.188310497 30 ctime=1761382109.184300138 nghttp2-1.68.0/src/shrpx_mruby_module_request.h0000644000175100017510000000267015077107270021314 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_MRUBY_MODULE_REQUEST_H #define SHRPX_MRUBY_MODULE_REQUEST_H #include "shrpx.h" #include namespace shrpx { namespace mruby { void init_request_class(mrb_state *mrb, RClass *module); } // namespace mruby } // namespace shrpx #endif // !defined(SHRPX_MRUBY_MODULE_REQUEST_H) nghttp2-1.68.0/src/PaxHeaders/siphash.h0000644000000000000000000000013215077107271014660 xustar0030 mtime=1761382073.000444106 30 atime=1761382080.105411485 30 ctime=1761382109.202300086 nghttp2-1.68.0/src/siphash.h0000644000175100017510000000475415077107271015262 0ustar00runnerrunner/* Copyright 2019 The BoringSSL Authors * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2025 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SIPHASH_H #define SIPHASH_H #include #include #include #include // SipHash is a fast, secure PRF that is often used for hash tables. // siphash24 implements SipHash-2-4. See // https://131002.net/siphash/siphash.pdf uint64_t siphash24(std::span key, std::span input); // Define here to be usable in tests. template T byteswap(T v) { auto c = std::bit_cast>(v); std::ranges::reverse(c); return std::bit_cast(c); } #endif // !defined(SIPHASH_H) nghttp2-1.68.0/src/PaxHeaders/timegm.h0000644000000000000000000000013115077107271014502 xustar0030 mtime=1761382073.001444102 29 atime=1761382080.10641148 30 ctime=1761382109.064300485 nghttp2-1.68.0/src/timegm.h0000644000175100017510000000326315077107271015077 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef TIMEGM_H #define TIMEGM_H #ifdef HAVE_CONFIG_H # include #endif /* defined(HAVE_CONFIG_H) */ #include #ifdef __cplusplus extern "C" { #endif /* defined(__cplusplus) */ time_t nghttp2_timegm(struct tm *tm); /* Just like nghttp2_timegm, but without using tm->tm_yday. This is useful if we use tm from strptime, since some platforms do not calculate tm_yday with that call. */ time_t nghttp2_timegm_without_yday(struct tm *tm); #ifdef __cplusplus } #endif /* defined(__cplusplus) */ #endif /* !defined(TIMEGM_H) */ nghttp2-1.68.0/src/PaxHeaders/shrpx_accept_handler.h0000644000000000000000000000013215077107270017400 xustar0030 mtime=1761382072.990444153 30 atime=1761382106.082310964 30 ctime=1761382109.078300445 nghttp2-1.68.0/src/shrpx_accept_handler.h0000644000175100017510000000316415077107270017774 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2014 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_ACCEPT_HANDLER_H #define SHRPX_ACCEPT_HANDLER_H #include "shrpx.h" #include namespace shrpx { class Worker; struct UpstreamAddr; class AcceptHandler { public: AcceptHandler(Worker *worker, const UpstreamAddr *faddr); ~AcceptHandler(); void accept_connection(); void enable(); void disable(); int get_fd() const; private: ev_io wev_; Worker *worker_; const UpstreamAddr *faddr_; }; } // namespace shrpx #endif // !defined(SHRPX_ACCEPT_HANDLER_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_worker.h0000644000000000000000000000013215077107271015756 xustar0030 mtime=1761382073.000444106 30 atime=1761382080.105411485 30 ctime=1761382109.119300326 nghttp2-1.68.0/src/shrpx_worker.h0000644000175100017510000003441415077107271016354 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_WORKER_H #define SHRPX_WORKER_H #include "shrpx.h" #include #include #include #include #include #include #include #ifndef NOTHREADS # include #endif // !defined(NOTHREADS) #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include #include "shrpx_config.h" #include "shrpx_downstream_connection_pool.h" #include "memchunk.h" #include "shrpx_tls.h" #include "shrpx_live_check.h" #include "shrpx_connect_blocker.h" #include "shrpx_dns_tracker.h" #ifdef ENABLE_HTTP3 # include "shrpx_quic_connection_handler.h" # include "shrpx_quic.h" #endif // defined(ENABLE_HTTP3) #include "allocator.h" using namespace nghttp2; namespace shrpx { class Http2Session; class ConnectBlocker; struct UpstreamAddr; class ConnectionHandler; class AcceptHandler; #ifdef ENABLE_HTTP3 class QUICListener; #endif // defined(ENABLE_HTTP3) #ifdef HAVE_MRUBY namespace mruby { class MRubyContext; } // namespace mruby #endif // defined(HAVE_MRUBY) namespace tls { class CertLookupTree; } // namespace tls struct WeightGroup; struct DownstreamAddr { Address addr; // backend address. If |host_unix| is true, this is UNIX domain // socket path. std::string_view host; std::string_view hostport; // backend port. 0 if |host_unix| is true. uint16_t port; // true if |host| contains UNIX domain socket path. bool host_unix; // sni field to send remote server if TLS is enabled. std::string_view sni; std::unique_ptr connect_blocker; std::unique_ptr live_check; // Connection pool for this particular address if session affinity // is enabled std::unique_ptr dconn_pool; size_t fall; size_t rise; // Client side TLS session cache tls::TLSSessionCache tls_session_cache; // List of Http2Session which is not fully utilized (i.e., the // server advertised maximum concurrency is not reached). We will // coalesce as much stream as possible in one Http2Session to fully // utilize TCP connection. DList http2_extra_freelist; WeightGroup *wg; // total number of streams created in HTTP/2 connections for this // address. size_t num_dconn; // the sequence number of this address to randomize the order access // threads. size_t seq; // Application protocol used in this backend Proto proto; // cycle is used to prioritize this address. Lower value takes // higher priority. uint32_t cycle; // penalty which is applied to the next cycle calculation. uint32_t pending_penalty; // Weight of this address inside a weight group. Its range is [1, // 256], inclusive. uint32_t weight; // name of group which this address belongs to. std::string_view group; // Weight of the weight group which this address belongs to. Its // range is [1, 256], inclusive. uint32_t group_weight; // affinity hash for this address. It is assigned when strict // stickiness is enabled. uint32_t affinity_hash; // true if TLS is used in this backend bool tls; // true if dynamic DNS is enabled bool dns; // true if :scheme pseudo header field should be upgraded to secure // variant (e.g., "https") when forwarding request to a backend // connected by TLS connection. bool upgrade_scheme; // true if this address is queued. bool queued; }; inline constexpr uint32_t MAX_DOWNSTREAM_ADDR_WEIGHT = 256; struct DownstreamAddrEntry { DownstreamAddr *addr; size_t seq; uint32_t cycle; }; struct DownstreamAddrEntryGreater { bool operator()(const DownstreamAddrEntry &lhs, const DownstreamAddrEntry &rhs) const { auto d = lhs.cycle - rhs.cycle; if (d == 0) { return rhs.seq < lhs.seq; } return d <= 2 * MAX_DOWNSTREAM_ADDR_WEIGHT - 1; } }; struct WeightGroup { std::priority_queue, DownstreamAddrEntryGreater> pq; std::string_view name; size_t seq; uint32_t weight; uint32_t cycle; uint32_t pending_penalty; // true if this object is queued. bool queued; }; struct WeightGroupEntry { WeightGroup *wg; size_t seq; uint32_t cycle; }; struct WeightGroupEntryGreater { bool operator()(const WeightGroupEntry &lhs, const WeightGroupEntry &rhs) const { auto d = lhs.cycle - rhs.cycle; if (d == 0) { return rhs.seq < lhs.seq; } return d <= 2 * MAX_DOWNSTREAM_ADDR_WEIGHT - 1; } }; struct SharedDownstreamAddr { SharedDownstreamAddr() : balloc(1024, 1024), affinity{SessionAffinity::NONE}, redirect_if_not_tls{false}, dnf{false}, timeout{} {} SharedDownstreamAddr(const SharedDownstreamAddr &) = delete; SharedDownstreamAddr(SharedDownstreamAddr &&) = delete; SharedDownstreamAddr &operator=(const SharedDownstreamAddr &) = delete; SharedDownstreamAddr &operator=(SharedDownstreamAddr &&) = delete; BlockAllocator balloc; std::vector addrs; std::vector wgs; std::priority_queue, WeightGroupEntryGreater> pq; // Bunch of session affinity hash. Only used if affinity == // SessionAffinity::IP. std::vector affinity_hash; // Maps affinity hash of each DownstreamAddr to its index in addrs. // It is only assigned when strict stickiness is enabled. std::unordered_map affinity_hash_map; #ifdef HAVE_MRUBY std::shared_ptr mruby_ctx; #endif // defined(HAVE_MRUBY) // Configuration for session affinity AffinityConfig affinity; // Session affinity // true if this group requires that client connection must be TLS, // and the request must be redirected to https URI. bool redirect_if_not_tls; // true if a request should not be forwarded to a backend. bool dnf; // Timeouts for backend connection. struct { ev_tstamp read; ev_tstamp write; } timeout; }; struct DownstreamAddrGroup { DownstreamAddrGroup(); ~DownstreamAddrGroup(); DownstreamAddrGroup(const DownstreamAddrGroup &) = delete; DownstreamAddrGroup(DownstreamAddrGroup &&) = delete; DownstreamAddrGroup &operator=(const DownstreamAddrGroup &) = delete; DownstreamAddrGroup &operator=(DownstreamAddrGroup &&) = delete; ImmutableString pattern; std::shared_ptr shared_addr; // true if this group is no longer used for new request. If this is // true, the connection made using one of address in shared_addr // must not be pooled. bool retired; }; struct WorkerStat { size_t num_connections; size_t num_close_waits; }; #ifdef ENABLE_HTTP3 struct QUICPacket { QUICPacket(size_t upstream_addr_index, const Address &remote_addr, const Address &local_addr, const ngtcp2_pkt_info &pi, std::span data) : upstream_addr_index{upstream_addr_index}, remote_addr{remote_addr}, local_addr{local_addr}, pi{pi}, data{std::ranges::begin(data), std::ranges::end(data)} {} QUICPacket() : upstream_addr_index{}, remote_addr{}, local_addr{}, pi{} {} size_t upstream_addr_index; Address remote_addr; Address local_addr; ngtcp2_pkt_info pi; std::vector data; }; #endif // defined(ENABLE_HTTP3) enum class WorkerEventType { REOPEN_LOG = 0x02, GRACEFUL_SHUTDOWN = 0x03, REPLACE_DOWNSTREAM = 0x04, #ifdef ENABLE_HTTP3 QUIC_PKT_FORWARD = 0x05, #endif // defined(ENABLE_HTTP3) }; struct WorkerEvent { WorkerEventType type; std::shared_ptr downstreamconf; #ifdef ENABLE_HTTP3 std::unique_ptr quic_pkt; #endif // defined(ENABLE_HTTP3) }; class Worker { public: Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, tls::CertLookupTree *cert_tree, #ifdef ENABLE_HTTP3 SSL_CTX *quic_sv_ssl_ctx, tls::CertLookupTree *quic_cert_tree, WorkerID wid, #endif // defined(ENABLE_HTTP3) size_t index, const std::shared_ptr &ticket_keys, ConnectionHandler *conn_handler, std::shared_ptr downstreamconf); ~Worker(); void run_async(); void wait(); void process_events(); void send(WorkerEvent event); tls::CertLookupTree *get_cert_lookup_tree() const; #ifdef ENABLE_HTTP3 tls::CertLookupTree *get_quic_cert_lookup_tree() const; #endif // defined(ENABLE_HTTP3) // These 2 functions make a lock m_ to get/set ticket keys // atomically. std::shared_ptr get_ticket_keys(); void set_ticket_keys(std::shared_ptr ticket_keys); WorkerStat *get_worker_stat(); struct ev_loop *get_loop() const; SSL_CTX *get_sv_ssl_ctx() const; SSL_CTX *get_cl_ssl_ctx() const; #ifdef ENABLE_HTTP3 SSL_CTX *get_quic_sv_ssl_ctx() const; #endif // defined(ENABLE_HTTP3) void set_graceful_shutdown(bool f); bool get_graceful_shutdown() const; MemchunkPool *get_mcpool(); void schedule_clear_mcpool(); std::mt19937 &get_randgen(); #ifdef HAVE_MRUBY int create_mruby_context(); mruby::MRubyContext *get_mruby_context() const; #endif // defined(HAVE_MRUBY) std::vector> & get_downstream_addr_groups(); ConnectBlocker *get_connect_blocker() const; const DownstreamConfig *get_downstream_config() const; void replace_downstream_config(std::shared_ptr downstreamconf); ConnectionHandler *get_connection_handler() const; int setup_server_socket(); void delete_listener(); void accept_pending_connection(); int create_tcp_server_socket(UpstreamAddr &addr); void enable_listener(); void disable_listener(); void sleep_listener(ev_tstamp t); #ifdef ENABLE_HTTP3 QUICConnectionHandler *get_quic_connection_handler(); int setup_quic_server_socket(); const WorkerID &get_worker_id() const; # ifdef HAVE_LIBBPF bool should_attach_bpf() const; bool should_update_bpf_map() const; uint32_t compute_sk_index() const; # endif // defined(HAVE_LIBBPF) int create_quic_server_socket(UpstreamAddr &addr); // Returns a pointer to UpstreamAddr which matches |local_addr|. const UpstreamAddr *find_quic_upstream_addr(const Address &local_addr); #endif // defined(ENABLE_HTTP3) DNSTracker *get_dns_tracker(); int handle_connection(int fd, sockaddr *addr, socklen_t addrlen, const UpstreamAddr *faddr); private: #ifndef NOTHREADS std::future fut_; #endif // !defined(NOTHREADS) // Unique index of this worker. size_t index_; std::mutex m_; std::deque q_; std::mt19937 randgen_; ev_async w_; ev_timer mcpool_clear_timer_; ev_timer proc_wev_timer_; ev_timer disable_listener_timer_; MemchunkPool mcpool_; WorkerStat worker_stat_; DNSTracker dns_tracker_; std::vector upstream_addrs_; std::vector> listeners_; #ifdef ENABLE_HTTP3 WorkerID worker_id_; std::vector quic_upstream_addrs_; std::vector> quic_listeners_; #endif // defined(ENABLE_HTTP3) std::shared_ptr downstreamconf_; #ifdef HAVE_MRUBY std::unique_ptr mruby_ctx_; #endif // defined(HAVE_MRUBY) struct ev_loop *loop_; // Following fields are shared across threads if // get_config()->tls_ctx_per_worker == true. SSL_CTX *sv_ssl_ctx_; SSL_CTX *cl_ssl_ctx_; tls::CertLookupTree *cert_tree_; ConnectionHandler *conn_handler_; #ifdef ENABLE_HTTP3 SSL_CTX *quic_sv_ssl_ctx_; tls::CertLookupTree *quic_cert_tree_; QUICConnectionHandler quic_conn_handler_; #endif // defined(ENABLE_HTTP3) #ifdef HAVE_ATOMIC_STD_SHARED_PTR std::atomic> ticket_keys_; #else // !defined(HAVE_ATOMIC_STD_SHARED_PTR) std::mutex ticket_keys_m_; std::shared_ptr ticket_keys_; #endif // !defined(HAVE_ATOMIC_STD_SHARED_PTR) std::vector> downstream_addr_groups_; // Worker level blocker for downstream connection. For example, // this is used when file descriptor is exhausted. std::unique_ptr connect_blocker_; bool graceful_shutdown_; }; // Selects group based on request's |hostport| and |path|. |hostport| // is the value taken from :authority or host header field, and may // contain port. The |path| may contain query part. We require the // catch-all pattern in place, so this function always selects one // group. The catch-all group index is given in |catch_all|. All // patterns are given in |groups|. size_t match_downstream_addr_group( const RouterConfig &routerconfig, const std::string_view &hostport, const std::string_view &path, const std::vector> &groups, size_t catch_all, BlockAllocator &balloc); // Calls this function if connecting to backend failed. |raddr| is // the actual address used to connect to backend, and it could be // nullptr. This function may schedule live check. void downstream_failure(DownstreamAddr *addr, const Address *raddr); } // namespace shrpx #endif // !defined(SHRPX_WORKER_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_worker_test.cc0000644000000000000000000000013215077107271017153 xustar0030 mtime=1761382073.000444106 30 atime=1761382080.105411485 30 ctime=1761382109.250299947 nghttp2-1.68.0/src/shrpx_worker_test.cc0000644000175100017510000002432715077107271017553 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_worker_test.h" #ifdef HAVE_UNISTD_H # include #endif // defined(HAVE_UNISTD_H) #include #include "munitxx.h" #include "shrpx_worker.h" #include "shrpx_connect_blocker.h" #include "shrpx_log.h" namespace shrpx { namespace { const MunitTest tests[]{ munit_void_test(test_shrpx_worker_match_downstream_addr_group), munit_test_end(), }; } // namespace const MunitSuite worker_suite{ "/worker", tests, nullptr, 1, MUNIT_SUITE_OPTION_NONE, }; void test_shrpx_worker_match_downstream_addr_group(void) { auto groups = std::vector>(); for (auto &s : {"nghttp2.org/", "nghttp2.org/alpha/bravo/", "nghttp2.org/alpha/charlie", "nghttp2.org/delta%3A", "www.nghttp2.org/", "[::1]/", "nghttp2.org/alpha/bravo/delta", // Check that match is done in the single node "example.com/alpha/bravo", "192.168.0.1/alpha/", "/golf/"}) { auto g = std::make_shared(); g->pattern = ImmutableString(s); groups.push_back(std::move(g)); } BlockAllocator balloc(1024, 1024); RouterConfig routerconf; auto &router = routerconf.router; auto &wcrouter = routerconf.rev_wildcard_router; auto &wp = routerconf.wildcard_patterns; for (size_t i = 0; i < groups.size(); ++i) { auto &g = groups[i]; router.add_route(std::string_view{std::ranges::begin(g->pattern), std::ranges::end(g->pattern)}, i); } assert_size(0, ==, match_downstream_addr_group(routerconf, "nghttp2.org"sv, "/"sv, groups, 255, balloc)); // port is removed assert_size(0, ==, match_downstream_addr_group(routerconf, "nghttp2.org:8080"sv, "/"sv, groups, 255, balloc)); // host is case-insensitive assert_size(4, ==, match_downstream_addr_group(routerconf, "WWW.nghttp2.org"sv, "/alpha"sv, groups, 255, balloc)); assert_size(1, ==, match_downstream_addr_group(routerconf, "nghttp2.org"sv, "/alpha/bravo/"sv, groups, 255, balloc)); // /alpha/bravo also matches /alpha/bravo/ assert_size(1, ==, match_downstream_addr_group(routerconf, "nghttp2.org"sv, "/alpha/bravo"sv, groups, 255, balloc)); // path part is case-sensitive assert_size(0, ==, match_downstream_addr_group(routerconf, "nghttp2.org"sv, "/Alpha/bravo"sv, groups, 255, balloc)); assert_size(1, ==, match_downstream_addr_group(routerconf, "nghttp2.org"sv, "/alpha/bravo/charlie"sv, groups, 255, balloc)); assert_size(2, ==, match_downstream_addr_group(routerconf, "nghttp2.org"sv, "/alpha/charlie"sv, groups, 255, balloc)); // pattern which does not end with '/' must match its entirely. So // this matches to group 0, not group 2. assert_size(0, ==, match_downstream_addr_group(routerconf, "nghttp2.org"sv, "/alpha/charlie/"sv, groups, 255, balloc)); assert_size(255, ==, match_downstream_addr_group(routerconf, "example.org"sv, "/"sv, groups, 255, balloc)); assert_size( 255, ==, match_downstream_addr_group(routerconf, ""sv, "/"sv, groups, 255, balloc)); assert_size(255, ==, match_downstream_addr_group(routerconf, ""sv, "alpha"sv, groups, 255, balloc)); assert_size(255, ==, match_downstream_addr_group(routerconf, "foo/bar"sv, "/"sv, groups, 255, balloc)); // If path is "*", only match with host + "/"). assert_size(0, ==, match_downstream_addr_group(routerconf, "nghttp2.org"sv, "*"sv, groups, 255, balloc)); assert_size(5, ==, match_downstream_addr_group(routerconf, "[::1]"sv, "/"sv, groups, 255, balloc)); assert_size(5, ==, match_downstream_addr_group(routerconf, "[::1]:8080"sv, "/"sv, groups, 255, balloc)); assert_size(255, ==, match_downstream_addr_group(routerconf, "[::1"sv, "/"sv, groups, 255, balloc)); assert_size(255, ==, match_downstream_addr_group(routerconf, "[::1]8000"sv, "/"sv, groups, 255, balloc)); // Check the case where adding route extends tree assert_size(6, ==, match_downstream_addr_group(routerconf, "nghttp2.org"sv, "/alpha/bravo/delta"sv, groups, 255, balloc)); assert_size(1, ==, match_downstream_addr_group(routerconf, "nghttp2.org"sv, "/alpha/bravo/delta/"sv, groups, 255, balloc)); // Check the case where query is done in a single node assert_size(7, ==, match_downstream_addr_group(routerconf, "example.com"sv, "/alpha/bravo"sv, groups, 255, balloc)); assert_size(255, ==, match_downstream_addr_group(routerconf, "example.com"sv, "/alpha/bravo/"sv, groups, 255, balloc)); assert_size(255, ==, match_downstream_addr_group(routerconf, "example.com"sv, "/alpha"sv, groups, 255, balloc)); // Check the case where quey is done in a single node assert_size(8, ==, match_downstream_addr_group(routerconf, "192.168.0.1"sv, "/alpha"sv, groups, 255, balloc)); assert_size(8, ==, match_downstream_addr_group(routerconf, "192.168.0.1"sv, "/alpha/"sv, groups, 255, balloc)); assert_size(8, ==, match_downstream_addr_group(routerconf, "192.168.0.1"sv, "/alpha/bravo"sv, groups, 255, balloc)); assert_size(255, ==, match_downstream_addr_group(routerconf, "192.168.0.1"sv, "/alph"sv, groups, 255, balloc)); assert_size(255, ==, match_downstream_addr_group(routerconf, "192.168.0.1"sv, "/"sv, groups, 255, balloc)); // Test for wildcard hosts auto g1 = std::make_shared(); g1->pattern = "git.nghttp2.org"_is; groups.push_back(std::move(g1)); auto g2 = std::make_shared(); g2->pattern = ".nghttp2.org"_is; groups.push_back(std::move(g2)); auto g3 = std::make_shared(); g3->pattern = ".local"_is; groups.push_back(std::move(g3)); wp.emplace_back("git.nghttp2.org"sv); wcrouter.add_route("gro.2ptthgn.tig"sv, 0); wp.back().router.add_route("/echo/"sv, 10); wp.emplace_back(".nghttp2.org"sv); wcrouter.add_route("gro.2ptthgn."sv, 1); wp.back().router.add_route("/echo/"sv, 11); wp.back().router.add_route("/echo/foxtrot"sv, 12); wp.emplace_back(".local"sv); wcrouter.add_route("lacol."sv, 2); wp.back().router.add_route("/"sv, 13); assert_size(11, ==, match_downstream_addr_group(routerconf, "git.nghttp2.org"sv, "/echo"sv, groups, 255, balloc)); assert_size(10, ==, match_downstream_addr_group(routerconf, "0git.nghttp2.org"sv, "/echo"sv, groups, 255, balloc)); assert_size(11, ==, match_downstream_addr_group(routerconf, "it.nghttp2.org"sv, "/echo"sv, groups, 255, balloc)); assert_size(255, ==, match_downstream_addr_group(routerconf, ".nghttp2.org"sv, "/echo/foxtrot"sv, groups, 255, balloc)); assert_size(9, ==, match_downstream_addr_group(routerconf, "alpha.nghttp2.org"sv, "/golf"sv, groups, 255, balloc)); assert_size(0, ==, match_downstream_addr_group(routerconf, "nghttp2.org"sv, "/echo"sv, groups, 255, balloc)); assert_size(13, ==, match_downstream_addr_group(routerconf, "test.local"sv, ""sv, groups, 255, balloc)); } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_memcached_dispatcher.cc0000644000000000000000000000013115077107270020715 xustar0029 mtime=1761382072.99744412 30 atime=1761382106.139310713 30 ctime=1761382109.136300277 nghttp2-1.68.0/src/shrpx_memcached_dispatcher.cc0000644000175100017510000000404515077107270021311 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_memcached_dispatcher.h" #include "shrpx_memcached_request.h" #include "shrpx_memcached_connection.h" #include "shrpx_config.h" #include "shrpx_log.h" namespace shrpx { MemcachedDispatcher::MemcachedDispatcher(const Address *addr, struct ev_loop *loop, SSL_CTX *ssl_ctx, const std::string_view &sni_name, MemchunkPool *mcpool, std::mt19937 &gen) : loop_(loop), mconn_(std::make_unique(addr, loop_, ssl_ctx, sni_name, mcpool, gen)) {} MemcachedDispatcher::~MemcachedDispatcher() {} int MemcachedDispatcher::add_request(std::unique_ptr req) { if (mconn_->add_request(std::move(req)) != 0) { return -1; } return 0; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_quic.cc0000644000000000000000000000013115077107270015542 xustar0030 mtime=1761382072.998444116 29 atime=1761382106.19231048 30 ctime=1761382109.188300127 nghttp2-1.68.0/src/shrpx_quic.cc0000644000175100017510000003016715077107270016142 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2021 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_quic.h" #include #include #include #include #include #include #include #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include "shrpx_config.h" #include "shrpx_log.h" #include "util.h" #include "xsi_strerror.h" bool operator==(const ngtcp2_cid &lhs, const ngtcp2_cid &rhs) { return ngtcp2_cid_eq(&lhs, &rhs); } namespace shrpx { ngtcp2_tstamp quic_timestamp() { return static_cast( std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()) .count()); } int quic_send_packet(const UpstreamAddr *faddr, const sockaddr *remote_sa, socklen_t remote_salen, const sockaddr *local_sa, socklen_t local_salen, const ngtcp2_pkt_info &pi, std::span data, size_t gso_size) { assert(gso_size); iovec msg_iov = { .iov_base = const_cast(data.data()), .iov_len = data.size(), }; uint8_t msg_ctrl[CMSG_SPACE(sizeof(int)) + #ifdef UDP_SEGMENT CMSG_SPACE(sizeof(uint16_t)) + #endif // defined(UDP_SEGMENT) CMSG_SPACE(sizeof(in6_pktinfo))]{}; msghdr msg{ .msg_name = const_cast(remote_sa), .msg_namelen = remote_salen, .msg_iov = &msg_iov, .msg_iovlen = 1, .msg_control = msg_ctrl, .msg_controllen = sizeof(msg_ctrl), }; size_t controllen = 0; auto cm = CMSG_FIRSTHDR(&msg); switch (local_sa->sa_family) { case AF_INET: { controllen += CMSG_SPACE(sizeof(in_pktinfo)); cm->cmsg_level = IPPROTO_IP; cm->cmsg_type = IP_PKTINFO; cm->cmsg_len = CMSG_LEN(sizeof(in_pktinfo)); auto addrin = reinterpret_cast(const_cast(local_sa)); in_pktinfo pktinfo{ .ipi_spec_dst = addrin->sin_addr, }; memcpy(CMSG_DATA(cm), &pktinfo, sizeof(pktinfo)); break; } case AF_INET6: { controllen += CMSG_SPACE(sizeof(in6_pktinfo)); cm->cmsg_level = IPPROTO_IPV6; cm->cmsg_type = IPV6_PKTINFO; cm->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo)); auto addrin = reinterpret_cast(const_cast(local_sa)); in6_pktinfo pktinfo{ .ipi6_addr = addrin->sin6_addr, }; memcpy(CMSG_DATA(cm), &pktinfo, sizeof(pktinfo)); break; } default: assert(0); } #ifdef UDP_SEGMENT if (data.size() > gso_size) { controllen += CMSG_SPACE(sizeof(uint16_t)); cm = CMSG_NXTHDR(&msg, cm); cm->cmsg_level = SOL_UDP; cm->cmsg_type = UDP_SEGMENT; cm->cmsg_len = CMSG_LEN(sizeof(uint16_t)); auto n = static_cast(gso_size); memcpy(CMSG_DATA(cm), &n, sizeof(n)); } #endif // defined(UDP_SEGMENT) controllen += CMSG_SPACE(sizeof(int)); cm = CMSG_NXTHDR(&msg, cm); cm->cmsg_len = CMSG_LEN(sizeof(int)); unsigned int tos = pi.ecn; memcpy(CMSG_DATA(cm), &tos, sizeof(tos)); switch (local_sa->sa_family) { case AF_INET: cm->cmsg_level = IPPROTO_IP; cm->cmsg_type = IP_TOS; break; case AF_INET6: cm->cmsg_level = IPPROTO_IPV6; cm->cmsg_type = IPV6_TCLASS; break; default: assert(0); } msg.msg_controllen = #ifndef __APPLE__ controllen #else // defined(__APPLE__) static_cast(controllen) #endif // defined(__APPLE__) ; ssize_t nwrite; do { nwrite = sendmsg(faddr->fd, &msg, 0); } while (nwrite == -1 && errno == EINTR); if (nwrite == -1) { if (LOG_ENABLED(INFO)) { auto error = errno; LOG(INFO) << "sendmsg failed: errno=" << error; } return -errno; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "QUIC sent packet: local=" << util::to_numeric_addr(local_sa, local_salen) << " remote=" << util::to_numeric_addr(remote_sa, remote_salen) << " ecn=" << log::hex << pi.ecn << log::dec << " " << nwrite << " bytes"; } assert(static_cast(nwrite) == data.size()); return 0; } int generate_quic_retry_connection_id(ngtcp2_cid &cid, uint32_t server_id, uint8_t km_id, EVP_CIPHER_CTX *ctx) { if (RAND_bytes(cid.data, SHRPX_QUIC_SCIDLEN) != 1) { return -1; } cid.datalen = SHRPX_QUIC_SCIDLEN; cid.data[0] = (cid.data[0] & (~SHRPX_QUIC_DCID_KM_ID_MASK)) | km_id; auto p = cid.data + SHRPX_QUIC_CID_WORKER_ID_OFFSET; std::ranges::copy_n(reinterpret_cast(&server_id), sizeof(server_id), p); return encrypt_quic_connection_id(p, p, ctx); } int generate_quic_connection_id(ngtcp2_cid &cid, const WorkerID &wid, uint8_t km_id, EVP_CIPHER_CTX *ctx) { if (RAND_bytes(cid.data, SHRPX_QUIC_SCIDLEN) != 1) { return -1; } cid.datalen = SHRPX_QUIC_SCIDLEN; cid.data[0] = (cid.data[0] & (~SHRPX_QUIC_DCID_KM_ID_MASK)) | km_id; auto p = cid.data + SHRPX_QUIC_CID_WORKER_ID_OFFSET; std::ranges::copy_n(reinterpret_cast(&wid), sizeof(wid), p); return encrypt_quic_connection_id(p, p, ctx); } int encrypt_quic_connection_id(uint8_t *dest, const uint8_t *src, EVP_CIPHER_CTX *ctx) { int len; if (!EVP_EncryptUpdate(ctx, dest, &len, src, SHRPX_QUIC_DECRYPTED_DCIDLEN) || !EVP_EncryptFinal_ex(ctx, dest + len, &len)) { return -1; } return 0; } int decrypt_quic_connection_id(ConnectionID &dest, const uint8_t *src, EVP_CIPHER_CTX *ctx) { int len; auto p = reinterpret_cast(&dest); if (!EVP_DecryptUpdate(ctx, p, &len, src, SHRPX_QUIC_DECRYPTED_DCIDLEN) || !EVP_DecryptFinal_ex(ctx, p + len, &len)) { return -1; } return 0; } int generate_quic_hashed_connection_id(ngtcp2_cid &dest, const Address &remote_addr, const Address &local_addr, const ngtcp2_cid &cid) { auto ctx = EVP_MD_CTX_new(); auto d = defer(EVP_MD_CTX_free, ctx); std::array h; auto hlen = static_cast(EVP_MD_size(EVP_sha256())); if (!EVP_DigestInit_ex(ctx, EVP_sha256(), nullptr) || !EVP_DigestUpdate(ctx, &remote_addr.su.sa, remote_addr.len) || !EVP_DigestUpdate(ctx, &local_addr.su.sa, local_addr.len) || !EVP_DigestUpdate(ctx, cid.data, cid.datalen) || !EVP_DigestFinal_ex(ctx, h.data(), &hlen)) { return -1; } assert(hlen == h.size()); std::ranges::copy_n(std::ranges::begin(h), sizeof(dest.data), std::ranges::begin(dest.data)); dest.datalen = sizeof(dest.data); return 0; } int generate_quic_stateless_reset_token(uint8_t *token, const ngtcp2_cid &cid, const uint8_t *secret, size_t secretlen) { if (ngtcp2_crypto_generate_stateless_reset_token(token, secret, secretlen, &cid) != 0) { return -1; } return 0; } std::optional> generate_retry_token(std::span token, uint32_t version, const sockaddr *sa, socklen_t salen, const ngtcp2_cid &retry_scid, const ngtcp2_cid &odcid, std::span secret) { auto t = static_cast( std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) .count()); auto tokenlen = ngtcp2_crypto_generate_retry_token( token.data(), secret.data(), secret.size(), version, sa, salen, &retry_scid, &odcid, t); if (tokenlen < 0) { return {}; } return token.first(as_unsigned(tokenlen)); } int verify_retry_token(ngtcp2_cid &odcid, std::span token, uint32_t version, const ngtcp2_cid &dcid, const sockaddr *sa, socklen_t salen, std::span secret) { auto t = static_cast( std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) .count()); if (ngtcp2_crypto_verify_retry_token( &odcid, token.data(), token.size(), secret.data(), secret.size(), version, sa, salen, &dcid, 10 * NGTCP2_SECONDS, t) != 0) { return -1; } return 0; } std::optional> generate_token(std::span token, const sockaddr *sa, size_t salen, std::span secret, uint8_t km_id) { auto t = static_cast( std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) .count()); auto tokenlen = ngtcp2_crypto_generate_regular_token( token.data(), secret.data(), secret.size(), sa, static_cast(salen), t); if (tokenlen < 0) { return {}; } token[as_unsigned(tokenlen++)] = km_id; return token.first(as_unsigned(tokenlen)); } int verify_token(std::span token, const sockaddr *sa, socklen_t salen, std::span secret) { if (token.empty()) { return -1; } auto t = static_cast( std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) .count()); if (ngtcp2_crypto_verify_regular_token( token.data(), token.size() - 1, secret.data(), secret.size(), sa, salen, 3600 * NGTCP2_SECONDS, t) != 0) { return -1; } return 0; } int generate_quic_connection_id_encryption_key(std::span key, std::span secret, std::span salt) { static constexpr auto info = "connection id encryption key"sv; ngtcp2_crypto_md sha256; ngtcp2_crypto_md_init( &sha256, reinterpret_cast(const_cast(EVP_sha256()))); if (ngtcp2_crypto_hkdf(key.data(), key.size(), &sha256, secret.data(), secret.size(), salt.data(), salt.size(), reinterpret_cast(info.data()), info.size()) != 0) { return -1; } return 0; } const QUICKeyingMaterial * select_quic_keying_material(const QUICKeyingMaterials &qkms, uint8_t km_id) { for (auto &qkm : qkms.keying_materials) { if (km_id == qkm.id) { return &qkm; } } return &qkms.keying_materials.front(); } std::span generate_siphash_key() { // Use the same technique rust does. thread_local static auto key = []() { std::array key; auto s = as_writable_uint8_span(std::span{key}); auto rv = RAND_bytes(s.data(), s.size()); if (rv != 1) { assert(0); abort(); } return key; }(); ++key[0]; return key; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_memcached_result.h0000644000000000000000000000013015077107270017746 xustar0029 mtime=1761382072.99744412 30 atime=1761382106.146310683 29 ctime=1761382109.14230026 nghttp2-1.68.0/src/shrpx_memcached_result.h0000644000175100017510000000333415077107270020343 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_MEMCACHED_RESULT_H #define SHRPX_MEMCACHED_RESULT_H #include "shrpx.h" #include namespace shrpx { enum class MemcachedStatusCode : uint16_t { NO_ERROR, EXT_NETWORK_ERROR = 0x1001, }; struct MemcachedResult { MemcachedResult(MemcachedStatusCode status_code) : status_code(status_code) {} MemcachedResult(MemcachedStatusCode status_code, std::vector value) : value(std::move(value)), status_code(status_code) {} std::vector value; MemcachedStatusCode status_code; }; } // namespace shrpx #endif // !defined(SHRPX_MEMCACHED_RESULT_H) nghttp2-1.68.0/src/PaxHeaders/testdata0000644000000000000000000000013215077107335014605 xustar0030 mtime=1761382109.316299757 30 atime=1761382109.800298358 30 ctime=1761382109.316299757 nghttp2-1.68.0/src/testdata/0000755000175100017510000000000015077107335015252 5ustar00runnerrunnernghttp2-1.68.0/src/testdata/PaxHeaders/verify_hostname.crt0000644000000000000000000000013115077107271020574 xustar0030 mtime=1761382073.001444102 29 atime=1761382080.10641148 30 ctime=1761382109.317299754 nghttp2-1.68.0/src/testdata/verify_hostname.crt0000644000175100017510000000107215077107271021165 0ustar00runnerrunner-----BEGIN CERTIFICATE----- MIIBeTCCAR6gAwIBAgIBATAKBggqhkjOPQQDAjAUMRIwEAYDVQQDEwlsb2NhbGhv c3QwHhcNMjMwMzE1MTIzNzU1WhcNMzMwMTIxMTIzNzU1WjAUMRIwEAYDVQQDEwls b2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATMHcWmb55fi0KHNDwM cYzVTAOfzJf44AqrqC+Pq2zW/ig8tPZbXf3eA/Vvp07Di+yWmuo3fGatUcY4nsx+ Jd62o2EwXzAOBgNVHQ8BAf8EBAMCB4AwTQYDVR0RBEYwRIITbmdodHRwMi5leGFt cGxlLmNvbYIVKi5uZ2h0dHAyLmV4YW1wbGUuY29thwR/AAABhxAAAAAAAAAAAAAA AAAAAAABMAoGCCqGSM49BAMCA0kAMEYCIQDQJFRJ3Ah4cGy7bwpkzVYeTgG+NhDa 55F4dPtJp9dS8wIhALQ9qf379lke1jVHg2t84iZLo3bL23RgICMezEYvqO3K -----END CERTIFICATE----- nghttp2-1.68.0/src/testdata/PaxHeaders/ipaddr.crt0000644000000000000000000000013115077107271016635 xustar0030 mtime=1761382073.001444102 29 atime=1761382080.10641148 30 ctime=1761382109.313299766 nghttp2-1.68.0/src/testdata/ipaddr.crt0000644000175100017510000000107615077107271017232 0ustar00runnerrunner-----BEGIN CERTIFICATE----- MIIBfDCCASKgAwIBAgIBATAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwsxOTIuMTY4 LjAuMTAeFw0yMzAzMTUxMjQ5MDBaFw0zMzAxMjExMjQ5MDBaMBYxFDASBgNVBAMT CzE5Mi4xNjguMC4xMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERDh3hNne6xGM fOrf7ln5EFnlLpk98qadBx3MKjG5gAfMYHzf/S7v19G608sH1LtabubV+Tvjllon K56G2Gk0+6NhMF8wDgYDVR0PAQH/BAQDAgeAME0GA1UdEQRGMESCE25naHR0cDIu ZXhhbXBsZS5jb22CFSoubmdodHRwMi5leGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAA AAAAAAAAAAAAATAKBggqhkjOPQQDAgNIADBFAiEA3jZzO49MYccR5mYS08qVUCdh HsEAC8GhRXFwL6zvf2ACIFAJrca2zTU4QRjV6V+LGRHc2ZocE2e7wFTLobblmDfB -----END CERTIFICATE----- nghttp2-1.68.0/src/testdata/PaxHeaders/Makefile.in0000644000000000000000000000013215077107305016724 xustar0030 mtime=1761382085.770386296 30 atime=1761382103.893320607 30 ctime=1761382109.311299771 nghttp2-1.68.0/src/testdata/Makefile.in0000644000175100017510000003745115077107305017326 0ustar00runnerrunner# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # nghttp2 - HTTP/2 C Library # Copyright (c) 2023 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = src/testdata ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPLDFLAGS = @APPLDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BPFCFLAGS = @BPFCFLAGS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CXX = @CXX@ CXX1XCXXFLAGS = @CXX1XCXXFLAGS@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXTRABPFCFLAGS = @EXTRABPFCFLAGS@ EXTRACFLAG = @EXTRACFLAG@ EXTRA_DEFS = @EXTRA_DEFS@ FGREP = @FGREP@ FILECMD = @FILECMD@ GREP = @GREP@ HAVE_CXX20 = @HAVE_CXX20@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ JANSSON_CFLAGS = @JANSSON_CFLAGS@ JANSSON_LIBS = @JANSSON_LIBS@ JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ JEMALLOC_LIBS = @JEMALLOC_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBBPF_CFLAGS = @LIBBPF_CFLAGS@ LIBBPF_LIBS = @LIBBPF_LIBS@ LIBBROTLIDEC_CFLAGS = @LIBBROTLIDEC_CFLAGS@ LIBBROTLIDEC_LIBS = @LIBBROTLIDEC_LIBS@ LIBBROTLIENC_CFLAGS = @LIBBROTLIENC_CFLAGS@ LIBBROTLIENC_LIBS = @LIBBROTLIENC_LIBS@ LIBCARES_CFLAGS = @LIBCARES_CFLAGS@ LIBCARES_LIBS = @LIBCARES_LIBS@ LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ LIBEV_CFLAGS = @LIBEV_CFLAGS@ LIBEV_LIBS = @LIBEV_LIBS@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@ LIBMRUBY_LIBS = @LIBMRUBY_LIBS@ LIBNGHTTP3_CFLAGS = @LIBNGHTTP3_CFLAGS@ LIBNGHTTP3_LIBS = @LIBNGHTTP3_LIBS@ LIBNGTCP2_CFLAGS = @LIBNGTCP2_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS = @LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_LIBS = @LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS = @LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS@ LIBNGTCP2_CRYPTO_LIBRESSL_LIBS = @LIBNGTCP2_CRYPTO_LIBRESSL_LIBS@ LIBNGTCP2_CRYPTO_OSSL_CFLAGS = @LIBNGTCP2_CRYPTO_OSSL_CFLAGS@ LIBNGTCP2_CRYPTO_OSSL_LIBS = @LIBNGTCP2_CRYPTO_OSSL_LIBS@ LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS = @LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ LIBNGTCP2_CRYPTO_QUICTLS_LIBS = @LIBNGTCP2_CRYPTO_QUICTLS_LIBS@ LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS = @LIBNGTCP2_CRYPTO_WOLFSSL_CFLAGS@ LIBNGTCP2_CRYPTO_WOLFSSL_LIBS = @LIBNGTCP2_CRYPTO_WOLFSSL_LIBS@ LIBNGTCP2_LIBS = @LIBNGTCP2_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBTOOL_LDFLAGS = @LIBTOOL_LDFLAGS@ LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ LIBXML2_LIBS = @LIBXML2_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_AGE = @LT_AGE@ LT_CURRENT = @LT_CURRENT@ LT_REVISION = @LT_REVISION@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ SYSTEMD_LIBS = @SYSTEMD_LIBS@ TESTLDADD = @TESTLDADD@ VERSION = @VERSION@ WARNCFLAGS = @WARNCFLAGS@ WARNCXXFLAGS = @WARNCXXFLAGS@ WOLFSSL_CFLAGS = @WOLFSSL_CFLAGS@ WOLFSSL_LIBS = @WOLFSSL_LIBS@ ZLIB_CFLAGS = @ZLIB_CFLAGS@ ZLIB_LIBS = @ZLIB_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. EXTRA_DIST = \ ipaddr.crt \ nosan.crt \ nosan_ip.crt \ verify_hostname.crt all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/testdata/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu src/testdata/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nghttp2-1.68.0/src/testdata/PaxHeaders/nosan_ip.crt0000644000000000000000000000013015077107271017177 xustar0030 mtime=1761382073.001444102 29 atime=1761382080.10641148 29 ctime=1761382109.31529976 nghttp2-1.68.0/src/testdata/nosan_ip.crt0000644000175100017510000000071515077107271017574 0ustar00runnerrunner-----BEGIN CERTIFICATE----- MIIBJzCBz6ADAgECAgEBMAoGCCqGSM49BAMCMBQxEjAQBgNVBAMTCTEyNy4wLjAu MTAeFw0yMzAzMTUxMjQ1MTVaFw0zMzAxMjExMjQ1MTVaMBQxEjAQBgNVBAMTCTEy Ny4wLjAuMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOXGPfSzXoeD7jszmAQO qAhak5HQMTmj32Q/xqO9WmCnXRQ+T06701o6q1hjotrC/HdMk9kabsKHc9V7Bk4O zkGjEjAQMA4GA1UdDwEB/wQEAwIHgDAKBggqhkjOPQQDAgNHADBEAiAI3fKrkNTN IEo9qI8bd/pZ6on4d9vLcnHtqYhcuWZGTwIgW2zYMwASLUw4H1k/prBtTEEJOahJ bvFs3oMbJEprQ+g= -----END CERTIFICATE----- nghttp2-1.68.0/src/testdata/PaxHeaders/Makefile.am0000644000000000000000000000013115077107271016714 xustar0030 mtime=1761382073.001444102 29 atime=1761382080.10641148 30 ctime=1761382109.310299774 nghttp2-1.68.0/src/testdata/Makefile.am0000644000175100017510000000226615077107271017313 0ustar00runnerrunner# nghttp2 - HTTP/2 C Library # Copyright (c) 2023 Tatsuhiro Tsujikawa # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. EXTRA_DIST = \ ipaddr.crt \ nosan.crt \ nosan_ip.crt \ verify_hostname.crt nghttp2-1.68.0/src/testdata/PaxHeaders/nosan.crt0000644000000000000000000000013115077107271016510 xustar0030 mtime=1761382073.001444102 29 atime=1761382080.10641148 30 ctime=1761382109.314299762 nghttp2-1.68.0/src/testdata/nosan.crt0000644000175100017510000000071515077107271017104 0ustar00runnerrunner-----BEGIN CERTIFICATE----- MIIBKDCBz6ADAgECAgEBMAoGCCqGSM49BAMCMBQxEjAQBgNVBAMTCWxvY2FsaG9z dDAeFw0yMzAzMTUxMjQzMzhaFw0zMzAxMjExMjQzMzhaMBQxEjAQBgNVBAMTCWxv Y2FsaG9zdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIEpWYgtXtcx0uJ2oFPK RiII93iw5ITMrhMfBXQ0SzCfkUdvCJ0gNW+3isTBu4Jt0URpgP37eGwiJf2wPApq KpajEjAQMA4GA1UdDwEB/wQEAwIHgDAKBggqhkjOPQQDAgNIADBFAiEA4IYil4G4 cMxaVkcAnMGgiSdn7/qIgdhFB0Vx5AOd+EUCIGubRPhsXAJXvG//cK25mmxi3Wax r7AgRKuDtWxn2bCO -----END CERTIFICATE----- nghttp2-1.68.0/src/PaxHeaders/comp_helper.c0000644000000000000000000000013115077107270015507 xustar0029 mtime=1761382072.98444418 30 atime=1761382106.209310405 30 ctime=1761382109.205300078 nghttp2-1.68.0/src/comp_helper.c0000644000175100017510000001103015077107270016073 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2013 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "comp_helper.h" #include static void dump_val(json_t *jent, const char *key, uint8_t *val, size_t len) { json_object_set_new(jent, key, json_pack("s#", val, len)); } #define NGHTTP2_HD_ENTRY_OVERHEAD 32 json_t *dump_deflate_header_table(nghttp2_hd_deflater *deflater) { json_t *obj, *entries; size_t i; size_t len = nghttp2_hd_deflate_get_num_table_entries(deflater); obj = json_object(); entries = json_array(); /* The first index of dynamic table is 62 */ for (i = 62; i <= len; ++i) { const nghttp2_nv *nv = nghttp2_hd_deflate_get_table_entry(deflater, i); json_t *outent = json_object(); json_object_set_new(outent, "index", json_integer((json_int_t)i)); dump_val(outent, "name", nv->name, nv->namelen); dump_val(outent, "value", nv->value, nv->valuelen); json_object_set_new(outent, "size", json_integer((json_int_t)(nv->namelen + nv->valuelen + NGHTTP2_HD_ENTRY_OVERHEAD))); json_array_append_new(entries, outent); } json_object_set_new(obj, "entries", entries); json_object_set_new( obj, "size", json_integer( (json_int_t)nghttp2_hd_deflate_get_dynamic_table_size(deflater))); json_object_set_new( obj, "max_size", json_integer( (json_int_t)nghttp2_hd_deflate_get_max_dynamic_table_size(deflater))); return obj; } json_t *dump_inflate_header_table(nghttp2_hd_inflater *inflater) { json_t *obj, *entries; size_t i; size_t len = nghttp2_hd_inflate_get_num_table_entries(inflater); obj = json_object(); entries = json_array(); /* The first index of dynamic table is 62 */ for (i = 62; i <= len; ++i) { const nghttp2_nv *nv = nghttp2_hd_inflate_get_table_entry(inflater, i); json_t *outent = json_object(); json_object_set_new(outent, "index", json_integer((json_int_t)i)); dump_val(outent, "name", nv->name, nv->namelen); dump_val(outent, "value", nv->value, nv->valuelen); json_object_set_new(outent, "size", json_integer((json_int_t)(nv->namelen + nv->valuelen + NGHTTP2_HD_ENTRY_OVERHEAD))); json_array_append_new(entries, outent); } json_object_set_new(obj, "entries", entries); json_object_set_new( obj, "size", json_integer( (json_int_t)nghttp2_hd_inflate_get_dynamic_table_size(inflater))); json_object_set_new( obj, "max_size", json_integer( (json_int_t)nghttp2_hd_inflate_get_max_dynamic_table_size(inflater))); return obj; } json_t *dump_header(const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen) { json_t *nv_pair = json_object(); char *cname = malloc(namelen + 1); if (cname == NULL) { return NULL; } memcpy(cname, name, namelen); cname[namelen] = '\0'; json_object_set_new(nv_pair, cname, json_pack("s#", value, valuelen)); free(cname); return nv_pair; } json_t *dump_headers(const nghttp2_nv *nva, size_t nvlen) { json_t *headers; size_t i; headers = json_array(); for (i = 0; i < nvlen; ++i) { json_array_append_new(headers, dump_header(nva[i].name, nva[i].namelen, nva[i].value, nva[i].valuelen)); } return headers; } void output_json_header(void) { printf("{\n" " \"cases\":\n" " [\n"); } void output_json_footer(void) { printf(" ]\n" "}\n"); } nghttp2-1.68.0/src/PaxHeaders/nghttp.h0000644000000000000000000000013215077107270014524 xustar0030 mtime=1761382072.989444157 30 atime=1761382106.234310295 30 ctime=1761382109.230300005 nghttp2-1.68.0/src/nghttp.h0000644000175100017510000002213015077107270015112 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NGHTTP_H #define NGHTTP_H #include "nghttp2_config.h" #include #ifdef HAVE_SYS_SOCKET_H # include #endif // defined(HAVE_SYS_SOCKET_H) #ifdef HAVE_NETDB_H # include #endif // defined(HAVE_NETDB_H) #include #include #include #include #include #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include #define NGHTTP2_NO_SSIZE_T #include #include "llhttp.h" #include "memchunk.h" #include "http2.h" #include "nghttp2_gzip.h" #include "template.h" namespace nghttp2 { class HtmlParser; struct Config { Config(); ~Config(); Headers headers; Headers trailer; std::vector extpris; std::string certfile; std::string keyfile; std::string datafile; std::string harfile; std::string scheme_override; std::string host_override; nghttp2_option *http2_option; int64_t header_table_size; int64_t min_header_table_size; int64_t encoder_header_table_size; size_t padding; size_t max_concurrent_streams; size_t peer_max_concurrent_streams; int multiply; // milliseconds ev_tstamp timeout; int window_bits; int connection_window_bits; int verbose; uint16_t port_override; bool null_out; bool remote_name; bool get_assets; bool stat; bool upgrade; bool continuation; bool no_content_length; bool hexdump; bool no_push; bool expect_continue; bool verify_peer; bool ktls; }; enum class RequestState { INITIAL, ON_REQUEST, ON_RESPONSE, ON_COMPLETE }; struct RequestTiming { // The point in time when request is started to be sent. // Corresponds to requestStart in Resource Timing TR. std::chrono::steady_clock::time_point request_start_time; // The point in time when first byte of response is received. // Corresponds to responseStart in Resource Timing TR. std::chrono::steady_clock::time_point response_start_time; // The point in time when last byte of response is received. // Corresponds to responseEnd in Resource Timing TR. std::chrono::steady_clock::time_point response_end_time; RequestState state; RequestTiming() : state(RequestState::INITIAL) {} }; struct Request; // forward declaration for ContinueTimer struct ContinueTimer { ContinueTimer(struct ev_loop *loop, Request *req); ~ContinueTimer(); void start(); void stop(); // Schedules an immediate run of the continue callback on the loop, if the // callback has not already been run void dispatch_continue(); struct ev_loop *loop; ev_timer timer; }; struct Request { // For pushed request, |uri| is empty and |u| is zero-cleared. Request(const std::string &uri, const urlparse_url &u, const nghttp2_data_provider2 *data_prd, int64_t data_length, const nghttp2_extpri &extpri, int level = 0); ~Request(); void init_inflater(); void init_html_parser(); int update_html_parser(const uint8_t *data, size_t len, int fin); std::string make_reqpath() const; bool is_ipv6_literal_addr() const; Headers::value_type *get_res_header(int32_t token); Headers::value_type *get_req_header(int32_t token); void record_request_start_time(); void record_response_start_time(); void record_response_end_time(); // Returns scheme taking into account overridden scheme. std::string_view get_real_scheme() const; // Returns request host, without port, taking into account // overridden host. std::string_view get_real_host() const; // Returns request port, taking into account overridden host, port, // and scheme. uint16_t get_real_port() const; Headers res_nva; Headers req_nva; std::string method; // URI without fragment std::string uri; urlparse_url u; nghttp2_extpri extpri; RequestTiming timing; int64_t data_length; int64_t data_offset; // Number of bytes received from server int64_t response_len; nghttp2_gzip *inflater; std::unique_ptr html_parser; const nghttp2_data_provider2 *data_prd; size_t header_buffer_size; int32_t stream_id; int status; // Recursion level: 0: first entity, 1: entity linked from first entity int level; http2::HeaderIndex res_hdidx; // used for incoming PUSH_PROMISE http2::HeaderIndex req_hdidx; bool expect_final_response; // only assigned if this request is using Expect/Continue std::unique_ptr continue_timer; }; struct SessionTiming { // The point in time when operation was started. Corresponds to // startTime in Resource Timing TR, but recorded in system clock time. std::chrono::system_clock::time_point system_start_time; // Same as above, but recorded in steady clock time. std::chrono::steady_clock::time_point start_time; // The point in time when DNS resolution was completed. Corresponds // to domainLookupEnd in Resource Timing TR. std::chrono::steady_clock::time_point domain_lookup_end_time; // The point in time when connection was established or SSL/TLS // handshake was completed. Corresponds to connectEnd in Resource // Timing TR. std::chrono::steady_clock::time_point connect_end_time; }; enum class ClientState { IDLE, CONNECTED }; struct HttpClient { HttpClient(const nghttp2_session_callbacks *callbacks, struct ev_loop *loop, SSL_CTX *ssl_ctx); ~HttpClient(); bool need_upgrade() const; int resolve_host(const std::string &host, uint16_t port); int initiate_connection(); void disconnect(); int noop(); int read_clear(); int write_clear(); int connected(); int tls_handshake(); int read_tls(); int write_tls(); int do_read(); int do_write(); int on_upgrade_connect(); int on_upgrade_read(const uint8_t *data, size_t len); int on_read(const uint8_t *data, size_t len); int on_write(); int connection_made(); void connect_fail(); void request_done(Request *req); void signal_write(); bool all_requests_processed() const; void update_hostport(); bool add_request(const std::string &uri, const nghttp2_data_provider2 *data_prd, int64_t data_length, const nghttp2_extpri &extpri, int level = 0); void record_start_time(); void record_domain_lookup_end_time(); void record_connect_end_time(); #ifdef HAVE_JANSSON void output_har(FILE *outfile); #endif // defined(HAVE_JANSSON) MemchunkPool mcpool; DefaultMemchunks wb; std::vector> reqvec; // Insert path already added in reqvec to prevent multiple request // for 1 resource. std::unordered_set path_cache; std::string scheme; std::string host; std::string hostport; // Used for parse the HTTP upgrade response from server std::unique_ptr htp; SessionTiming timing; ev_io wev; ev_io rev; ev_timer wt; ev_timer rt; ev_timer settings_timer; std::function readfn, writefn; std::function on_readfn; std::function on_writefn; nghttp2_session *session; const nghttp2_session_callbacks *callbacks; struct ev_loop *loop; SSL_CTX *ssl_ctx; SSL *ssl; addrinfo *addrs; addrinfo *next_addr; addrinfo *cur_addr; // The number of completed requests, including failed ones. size_t complete; // The number of requests that local endpoint received END_STREAM // from peer. size_t success; // The length of settings_payload size_t settings_payloadlen; ClientState state; // The HTTP status code of the response message of HTTP Upgrade. unsigned int upgrade_response_status_code; int fd; // true if the response message of HTTP Upgrade request is fully // received. It is not relevant the upgrade succeeds, or not. bool upgrade_response_complete; // SETTINGS payload sent as token68 in HTTP Upgrade std::array settings_payload; enum { ERR_CONNECT_FAIL = -100 }; }; } // namespace nghttp2 #endif // !defined(NGHTTP_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_null_downstream_connection.h0000644000000000000000000000013215077107270022100 xustar0030 mtime=1761382072.998444116 30 atime=1761382106.163310607 30 ctime=1761382109.159300211 nghttp2-1.68.0/src/shrpx_null_downstream_connection.h0000644000175100017510000000455415077107270022500 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2021 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_NULL_DOWNSTREAM_CONNECTION_H #define SHRPX_NULL_DOWNSTREAM_CONNECTION_H #include "shrpx_downstream_connection.h" #include "template.h" using namespace nghttp2; namespace shrpx { class NullDownstreamConnection : public DownstreamConnection { public: NullDownstreamConnection(const std::shared_ptr &group); virtual ~NullDownstreamConnection(); virtual int attach_downstream(Downstream *downstream); virtual void detach_downstream(Downstream *downstream); virtual int push_request_headers(); virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen); virtual int end_upload_data(); virtual void pause_read(IOCtrlReason reason); virtual int resume_read(IOCtrlReason reason, size_t consumed); virtual void force_resume_read(); virtual int on_read(); virtual int on_write(); virtual void on_upstream_change(Upstream *upstream); // true if this object is poolable. virtual bool poolable() const; virtual const std::shared_ptr & get_downstream_addr_group() const; virtual DownstreamAddr *get_addr() const; private: std::shared_ptr group_; }; } // namespace shrpx #endif // !defined(SHRPX_NULL_DOWNSTREAM_CONNECTION_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_health_monitor_downstream_connection.h0000644000000000000000000000013215077107270024142 xustar0030 mtime=1761382072.993444139 30 atime=1761382106.160310621 30 ctime=1761382109.156300219 nghttp2-1.68.0/src/shrpx_health_monitor_downstream_connection.h0000644000175100017510000000443215077107270024535 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2016 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_HEALTH_MONITOR_DOWNSTREAM_CONNECTION_H #define SHRPX_HEALTH_MONITOR_DOWNSTREAM_CONNECTION_H #include "shrpx_downstream_connection.h" namespace shrpx { class Worker; class HealthMonitorDownstreamConnection : public DownstreamConnection { public: HealthMonitorDownstreamConnection(); virtual ~HealthMonitorDownstreamConnection(); virtual int attach_downstream(Downstream *downstream); virtual void detach_downstream(Downstream *downstream); virtual int push_request_headers(); virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen); virtual int end_upload_data(); virtual void pause_read(IOCtrlReason reason); virtual int resume_read(IOCtrlReason reason, size_t consumed); virtual void force_resume_read(); virtual int on_read(); virtual int on_write(); virtual void on_upstream_change(Upstream *upstream); // true if this object is poolable. virtual bool poolable() const; virtual const std::shared_ptr & get_downstream_addr_group() const; virtual DownstreamAddr *get_addr() const; }; } // namespace shrpx #endif // !defined(SHRPX_HEALTH_MONITOR_DOWNSTREAM_CONNECTION_H) nghttp2-1.68.0/src/PaxHeaders/util.h0000644000000000000000000000012715077107271014202 xustar0030 mtime=1761382073.001444102 29 atime=1761382080.10641148 28 ctime=1761382109.0593005 nghttp2-1.68.0/src/util.h0000644000175100017510000013035715077107271014577 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTIL_H #define UTIL_H #include "nghttp2_config.h" #ifdef HAVE_UNISTD_H # include #endif // defined(HAVE_UNISTD_H) #include #ifdef HAVE_NETDB_H # include #endif // defined(HAVE_NETDB_H) #ifdef __QNX__ # include #endif // defined(__QNX__) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBEV # include #endif // defined(HAVE_LIBEV) #include "urlparse.h" #include "template.h" #include "network.h" #include "allocator.h" using namespace std::literals; namespace nghttp2 { inline constexpr auto NGHTTP2_H2_ALPN = "\x2h2"sv; inline constexpr auto NGHTTP2_H2 = "h2"sv; inline constexpr auto NGHTTP2_H1_1_ALPN = "\x8http/1.1"sv; inline constexpr auto NGHTTP2_H1_1 = "http/1.1"sv; namespace util { template Pred> consteval auto pred_tbl_gen256(Pred pred) { std::array tbl; for (size_t i = 0; i < tbl.size(); ++i) { tbl[i] = pred(i); } return tbl; } consteval auto alpha_pred(size_t i) noexcept { return ('A' <= i && i <= 'Z') || ('a' <= i && i <= 'z'); } inline constexpr auto is_alpha_tbl = pred_tbl_gen256(alpha_pred); constexpr bool is_alpha(char c) noexcept { return is_alpha_tbl[static_cast(c)]; } consteval auto digit_pred(size_t i) noexcept { return '0' <= i && i <= '9'; } inline constexpr auto is_digit_tbl = pred_tbl_gen256(digit_pred); constexpr bool is_digit(char c) noexcept { return is_digit_tbl[static_cast(c)]; } consteval auto hex_digit_pred(size_t i) noexcept { return digit_pred(i) || ('A' <= i && i <= 'F') || ('a' <= i && i <= 'f'); } inline constexpr auto is_hex_digit_tbl = pred_tbl_gen256(hex_digit_pred); constexpr bool is_hex_digit(char c) noexcept { return is_hex_digit_tbl[static_cast(c)]; } // Returns true if a range [|first|, |last|) is hex string. template constexpr bool is_hex_string(I first, I last) { return !(std::ranges::distance(first, last) & 1) && std::ranges::all_of(first, last, is_hex_digit); } // Returns true if |r| is hex string. template requires(!std::is_array_v>) constexpr bool is_hex_string(R &&r) { return is_hex_string(std::ranges::begin(r), std::ranges::end(r)); } consteval auto rfc3986_unreserved_chars_pred(size_t i) noexcept { switch (i) { case '-': case '.': case '_': case '~': return true; } return digit_pred(i) || alpha_pred(i); } inline constexpr auto in_rfc3986_unreserved_chars_tbl = pred_tbl_gen256(rfc3986_unreserved_chars_pred); constexpr bool in_rfc3986_unreserved_chars(char c) noexcept { return in_rfc3986_unreserved_chars_tbl[static_cast(c)]; } consteval auto rfc3986_sub_delims_pred(size_t i) noexcept { switch (i) { case '!': case '$': case '&': case '\'': case '(': case ')': case '*': case '+': case ',': case ';': case '=': return true; } return false; } inline constexpr auto in_rfc3986_sub_delims_tbl = pred_tbl_gen256(rfc3986_sub_delims_pred); constexpr bool in_rfc3986_sub_delims(char c) noexcept { return in_rfc3986_sub_delims_tbl[static_cast(c)]; } consteval auto token_pred(size_t i) noexcept { switch (i) { case '!': case '#': case '$': case '%': case '&': case '\'': case '*': case '+': case '-': case '.': case '^': case '_': case '`': case '|': case '~': return true; } return digit_pred(i) || alpha_pred(i); } inline constexpr auto in_token_tbl = pred_tbl_gen256(token_pred); // Returns true if |c| is in token (HTTP-p1, Section 3.2.6) constexpr bool in_token(char c) noexcept { return in_token_tbl[static_cast(c)]; } consteval auto attr_char_pred(size_t i) noexcept { switch (i) { case '*': case '\'': case '%': return false; } return token_pred(i); } inline constexpr auto in_attr_char_tbl = pred_tbl_gen256(attr_char_pred); constexpr bool in_attr_char(char c) noexcept { return in_attr_char_tbl[static_cast(c)]; } inline constexpr auto hex_to_uint_tbl = []() { std::array tbl; std::ranges::fill(tbl, 256); for (char i = '0'; i <= '9'; ++i) { tbl[static_cast(i)] = static_cast(i - '0'); } for (char i = 'A'; i <= 'F'; ++i) { tbl[static_cast(i)] = static_cast(i - 'A' + 10); } for (char i = 'a'; i <= 'f'; ++i) { tbl[static_cast(i)] = static_cast(i - 'a' + 10); } return tbl; }(); // Returns integer corresponding to hex notation |c|. If // is_hex_digit(c) is false, it returns 256. constexpr uint32_t hex_to_uint(char c) noexcept { return hex_to_uint_tbl[static_cast(c)]; } template requires(std::indirectly_copyable) constexpr O percent_decode(I first, I last, O result) { using result_type = std::iter_value_t; for (; first != last; ++first) { if (*first != '%') { *result++ = static_cast(*first); continue; } auto dig1 = std::ranges::next(first, 1); if (dig1 == last || !is_hex_digit(*dig1)) { *result++ = static_cast(*first); continue; } auto dig2 = std::ranges::next(dig1, 1); if (dig2 == last || !is_hex_digit(*dig2)) { *result++ = static_cast(*first); continue; } *result++ = static_cast((hex_to_uint(*dig1) << 4) | hex_to_uint(*dig2)); first = dig2; } return result; } template constexpr std::string percent_decode(I first, I last) { std::string result; result.resize(as_unsigned(std::ranges::distance(first, last))); auto p = percent_decode(std::move(first), std::move(last), std::ranges::begin(result)); result.resize( as_unsigned(std::ranges::distance(std::ranges::begin(result), p))); return result; } template requires(!std::is_array_v>) constexpr std::string percent_decode(R &&r) { return percent_decode(std::ranges::begin(r), std::ranges::end(r)); } template requires(!std::is_array_v>) std::string_view percent_decode(BlockAllocator &balloc, R &&r) { auto iov = make_byte_ref(balloc, std::ranges::size(r) + 1); auto p = percent_decode(std::ranges::begin(r), std::ranges::end(r), std::ranges::begin(iov)); *p = '\0'; return as_string_view(std::ranges::begin(iov), p); } // Quote a range [|first|, |last|) and stores the result in another // range, beginning at |result|. It returns an output iterator to the // element past the last element stored. Currently, this function // just replace '"' with '\"'. template requires(std::indirectly_copyable) constexpr O quote_string(I first, I last, O result) noexcept { for (; first != last; ++first) { if (*first == '"') { *result++ = '\\'; *result++ = '"'; } else { *result++ = static_cast>(*first); } } return result; } template requires(std::indirectly_copyable, O> && !std::is_array_v>) constexpr O quote_string(R &&r, O result) { return quote_string(std::ranges::begin(r), std::ranges::end(r), std::move(result)); } template requires(!std::is_array_v>) std::string_view quote_string(BlockAllocator &balloc, R &&r) { auto cnt = std::ranges::count(r, '"'); if (cnt == 0) { return make_string_ref(balloc, std::forward(r)); } auto iov = make_byte_ref(balloc, std::ranges::size(r) + static_cast(cnt) + 1); auto p = quote_string(std::forward(r), std::ranges::begin(iov)); *p = '\0'; return as_string_view(std::ranges::begin(iov), p); } // Returns the number of bytes written by quote_string with the same // |r| parameter. The return value does not include a terminal NUL // byte. template requires(!std::is_array_v>) constexpr size_t quote_stringlen(R &&r) { size_t n = 0; for (auto c : r) { if (c == '"') { n += 2; } else { ++n; } } return n; } inline constexpr auto hexdigits = []() { constexpr char LOWER_XDIGITS[] = "0123456789abcdef"; std::array tbl; for (size_t i = 0; i < 256; ++i) { tbl[i * 2] = LOWER_XDIGITS[static_cast(i >> 4)]; tbl[i * 2 + 1] = LOWER_XDIGITS[static_cast(i & 0xf)]; } return tbl; }(); // Converts a range [|first|, |last|) in hex format, and stores the // result in another range, beginning at |result|. It returns an // output iterator to the element past the last element stored. template requires(std::indirectly_writable && sizeof(std::iter_value_t) == sizeof(uint8_t)) constexpr O format_hex(I first, I last, O result) { for (; first != last; ++first) { result = std::ranges::copy_n( hexdigits.data() + static_cast(*first) * 2, 2, result) .out; } return result; } // Converts |R| in hex format, and stores the result in another range, // beginning at |result|. It returns an output iterator to the // element past the last element stored. template requires(std::indirectly_writable && !std::is_array_v> && sizeof(std::ranges::range_value_t) == sizeof(uint8_t)) constexpr O format_hex(R &&r, O result) { return format_hex(std::ranges::begin(r), std::ranges::end(r), std::move(result)); } // Converts |R| in hex format, and stores the result in a buffer // allocated by |balloc|. It returns std::string_view that is backed by the // allocated buffer. The returned string is NULL terminated. template requires(!std::is_array_v> && sizeof(std::ranges::range_value_t) == sizeof(uint8_t)) std::string_view format_hex(BlockAllocator &balloc, R &&r) { auto iov = make_byte_ref(balloc, std::ranges::size(r) * 2 + 1); auto p = format_hex(std::forward(r), std::ranges::begin(iov)); *p = '\0'; return as_string_view(std::ranges::begin(iov), p); } // Converts |R| in hex format, and returns the result. template requires(!std::is_array_v> && sizeof(std::ranges::range_value_t) == sizeof(uint8_t)) constexpr std::string format_hex(R &&r) { std::string res; res.resize(as_unsigned(std::ranges::distance(r) * 2)); format_hex(std::forward(r), std::ranges::begin(res)); return res; } template requires(std::indirectly_writable) constexpr O format_hex(T n, O result) { if constexpr (sizeof(n) == 1) { return std::ranges::copy_n(hexdigits.data() + n * 2, 2, result).out; } if constexpr (std::endian::native == std::endian::little) { auto end = reinterpret_cast(&n); auto p = end + sizeof(n); for (; p != end; --p) { result = std::ranges::copy_n(hexdigits.data() + *(p - 1) * 2, 2, result).out; } } else { auto p = reinterpret_cast(&n); auto end = p + sizeof(n); for (; p != end; ++p) { result = std::ranges::copy_n(hexdigits.data() + *p * 2, 2, result).out; } } return result; } inline constexpr auto upper_hexdigits = []() { constexpr char UPPER_XDIGITS[] = "0123456789ABCDEF"; std::array tbl; for (size_t i = 0; i < 256; ++i) { tbl[i * 2] = UPPER_XDIGITS[static_cast(i >> 4)]; tbl[i * 2 + 1] = UPPER_XDIGITS[static_cast(i & 0xf)]; } return tbl; }(); template requires(std::indirectly_writable) constexpr O format_upper_hex(uint8_t c, O result) { return std::ranges::copy_n(upper_hexdigits.data() + c * 2, 2, result).out; } // decode_hex decodes hex string in a range [|first|, |last|), and // stores the result in another range, beginning at |result|. It // returns an output iterator to the element past the last element // stored. This function assumes a range [|first|, |last|) is hex // string, that is is_hex_string(|first|, |last|) == true. template requires(std::indirectly_writable) constexpr O decode_hex(I first, I last, O result) { for (; first != last; first = std::ranges::next(first, 2)) { *result++ = static_cast>( (hex_to_uint(*first) << 4) | hex_to_uint(*std::ranges::next(first, 1))); } return result; } // decode_hex decodes hex string |r|, and stores the result in another // range, beginning at |result|. It returns an output iterator to the // element past the last element stored. This function assumes |r| is // hex string, that is is_hex_string(r) == true. template requires(std::indirectly_writable && !std::is_array_v>) constexpr O decode_hex(R &&r, O result) { return decode_hex(std::ranges::begin(r), std::ranges::end(r), std::move(result)); } // decode_hex decodes hex string in a range [|first|, |last|), returns // the decoded byte string, which is not NULL terminated. This // function assumes a range [|first|, |last|) is hex string, that is // is_hex_string(|first|, |last|) == true. template std::span decode_hex(BlockAllocator &balloc, I first, I last) { auto iov = make_byte_ref(balloc, as_unsigned(std::ranges::distance(first, last) / 2)); auto p = decode_hex(std::move(first), std::move(last), std::ranges::begin(iov)); return {std::ranges::begin(iov), p}; } // decode_hex decodes hex string |r|, returns the decoded byte string, // which is not NULL terminated. This function assumes |r| is hex // string, that is is_hex_string(r) == true. template requires(!std::is_array_v>) std::span decode_hex(BlockAllocator &balloc, R &&r) { return decode_hex(balloc, std::ranges::begin(r), std::ranges::end(r)); } // Percent encode a range [|first|, |last|) if a character is not in // token or '%'. template requires(std::indirectly_copyable) constexpr O percent_encode_token(I first, I last, O result) noexcept { using result_type = std::iter_value_t; for (; first != last; ++first) { auto c = static_cast(*first); if (c != '%' && in_token(as_signed(c))) { *result++ = static_cast(c); continue; } *result++ = '%'; result = format_upper_hex(c, result); } return result; } template requires(std::indirectly_copyable, O> && !std::is_array_v>) constexpr O percent_encode_token(R &&r, O result) { return percent_encode_token(std::ranges::begin(r), std::ranges::end(r), std::move(result)); } // Returns the number of bytes written by percent_encode_token with // the same |r| parameter. The return value does not include a // terminal NUL byte. template requires(!std::is_array_v>) constexpr size_t percent_encode_tokenlen(R &&r) noexcept { size_t n = 0; for (auto c : r) { if (c != '%' && in_token(c)) { ++n; continue; } // percent-encoded character '%ff' n += 3; } return n; } time_t parse_http_date(const std::string_view &s); // Parses time formatted as "MMM DD HH:MM:SS YYYY [GMT]" (e.g., Feb 3 // 00:55:52 2015 GMT), which is specifically used by OpenSSL // ASN1_TIME_print(). time_t parse_openssl_asn1_time_print(const std::string_view &s); inline constexpr auto upcase_tbl = []() { std::array tbl; for (size_t i = 0; i < 256; ++i) { if ('a' <= i && i <= 'z') { tbl[i] = static_cast(i - 'a' + 'A'); } else { tbl[i] = static_cast(i); } } return tbl; }(); constexpr char upcase(char c) noexcept { return upcase_tbl[static_cast(c)]; } inline constexpr uint8_t lowcase_tbl[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, }; constexpr char lowcase(char c) noexcept { return static_cast(lowcase_tbl[static_cast(c)]); } template constexpr bool starts_with(R1 &&s, R2 &&prefix) { auto prefixlen = std::ranges::distance(prefix); return std::ranges::distance(s) >= prefixlen && std::ranges::equal(std::views::take(std::forward(s), prefixlen), std::forward(prefix)); } struct CaseCmp { constexpr bool operator()(char lhs, char rhs) const noexcept { return lowcase(lhs) == lowcase(rhs); } }; template constexpr bool istarts_with(R1 &&s, R2 &&prefix) { auto prefixlen = std::ranges::distance(prefix); return std::ranges::distance(s) >= prefixlen && std::ranges::equal(std::views::take(std::forward(s), prefixlen), std::forward(prefix), CaseCmp()); } template constexpr bool ends_with(R1 &&s, R2 &&suffix) { auto slen = std::ranges::distance(s); auto suffixlen = std::ranges::distance(suffix); return slen >= suffixlen && std::ranges::equal( std::views::drop(std::forward(s), slen - suffixlen), std::forward(suffix)); } template constexpr bool iends_with(R1 &&s, R2 &&suffix) { auto slen = std::ranges::distance(s); auto suffixlen = std::ranges::distance(suffix); return slen >= suffixlen && std::ranges::equal( std::views::drop(std::forward(s), slen - suffixlen), std::forward(suffix), CaseCmp()); } template constexpr bool strieq(R1 &&a, R2 &&b) { return std::ranges::equal(std::forward(a), std::forward(b), CaseCmp()); } template constexpr bool streq(R1 &&a, R2 &&b) { return std::ranges::equal(std::forward(a), std::forward(b)); } // Converts characters in a range [|first|, |last|) to lowercase, and // stores the result in another range, beginning at |result|. It // returns an output iterator to the element past the last element // stored. template requires(std::indirectly_copyable) constexpr O tolower(I first, I last, O result) { return std::ranges::transform(std::move(first), std::move(last), std::move(result), lowcase) .out; } // Converts characters in a range |r| to lowercase, and stores the // result in another range, beginning at |result|. It returns an // output iterator to the element past the last element stored. template requires(std::indirectly_copyable, O> && !std::is_array_v>) constexpr O tolower(R &&r, O result) { return std::ranges::transform(std::forward(r), std::move(result), lowcase) .out; } // Returns string representation of |n| with 2 fractional digits. std::string dtos(double n); inline constexpr auto count_digit_tbl = []() { std::array::digits10> tbl; uint64_t x = 1; for (size_t i = 0; i < tbl.size(); ++i) { x *= 10; tbl[i] = x - 1; } return tbl; }(); // count_digit returns the minimum number of digits to represent |x| // in base 10. // // credit: // https://lemire.me/blog/2025/01/07/counting-the-digits-of-64-bit-integers/ template constexpr size_t count_digit(T x) { auto y = static_cast(19 * (std::numeric_limits::digits - 1 - std::countl_zero(static_cast(x | 1))) >> 6); y += x > count_digit_tbl[y]; return y + 1; } inline constexpr auto utos_digits = []() { std::array a; for (size_t i = 0; i < 100; ++i) { a[i * 2] = '0' + static_cast(i / 10); a[i * 2 + 1] = '0' + static_cast(i % 10); } return a; }(); struct UIntFormatter { template requires(std::indirectly_writable) constexpr O operator()(T n, O result) { using result_type = std::iter_value_t; if (n < 10) { *result++ = static_cast('0' + static_cast(n)); return result; } if (n < 100) { return std::ranges::copy_n(utos_digits.data() + n * 2, 2, result).out; } std::ranges::advance(result, as_signed(count_digit(n))); auto p = result; for (; n >= 100; n /= 100) { std::ranges::advance(p, -2); std::ranges::copy_n(utos_digits.data() + (n % 100) * 2, 2, p); } if (n < 10) { *--p = static_cast('0' + static_cast(n)); return result; } std::ranges::advance(p, -2); std::ranges::copy_n(utos_digits.data() + n * 2, 2, p); return result; } }; template requires(std::indirectly_writable) constexpr O utos(T n, O result) { return UIntFormatter{}(std::move(n), std::move(result)); } template constexpr std::string utos(T n) { using namespace std::literals; if (n == 0) { return "0"s; } std::string res; res.resize(count_digit(n)); utos(n, std::ranges::begin(res)); return res; } template std::string_view make_string_ref_uint(BlockAllocator &balloc, T n) { auto iov = make_byte_ref( balloc, count_digit(static_cast>(n)) + 1); auto p = std::ranges::begin(iov); p = util::utos(n, p); *p = '\0'; return as_string_view(std::ranges::begin(iov), p); } template constexpr std::string utos_unit(T n) { char u; if (n >= (1 << 30)) { u = 'G'; n /= (1 << 30); } else if (n >= (1 << 20)) { u = 'M'; n /= (1 << 20); } else if (n >= (1 << 10)) { u = 'K'; n /= (1 << 10); } else { return utos(n); } return utos(n) + u; } // Like utos_unit(), but 2 digits fraction part is followed. template constexpr std::string utos_funit(T n) { char u; int b; if (n >= (1 << 30)) { u = 'G'; b = 30; } else if (n >= (1 << 20)) { u = 'M'; b = 20; } else if (n >= (1 << 10)) { u = 'K'; b = 10; } else { return utos(n); } return dtos(static_cast(n) / (1 << b)) + u; } struct CompactHexFormatter { template requires(std::indirectly_writable) O operator()(T n, O result) { using result_type = std::iter_value_t; if (n == 0) { *result++ = '0'; return result; } if constexpr (std::endian::native == std::endian::little) { auto end = reinterpret_cast(&n); auto p = end + sizeof(n); for (; p != end && *(p - 1) == 0; --p) ; // Workaround for bogus UBSAN error assert(p != end); if (*(p - 1) < 16) { *result++ = static_cast(upper_hexdigits[*--p * 2 + 1]); } for (; p != end; --p) { result = format_upper_hex(*(p - 1), result); } } else { auto p = reinterpret_cast(&n); auto end = p + sizeof(n); for (; p != end && *p == 0; ++p) ; if (*p < 16) { *result++ = static_cast(upper_hexdigits[*p++ * 2 + 1]); } for (; p != end; ++p) { result = format_upper_hex(*p, result); } } return result; } }; template requires(std::indirectly_writable) O utox(T n, O result) { return CompactHexFormatter{}(std::move(n), std::move(result)); } void to_token68(std::string &base64str); std::string_view to_base64(BlockAllocator &balloc, const std::string_view &token68str); void show_candidates(const char *unkopt, const option *options); bool has_uri_field(const urlparse_url &u, urlparse_url_fields field); bool fieldeq(const char *uri1, const urlparse_url &u1, const char *uri2, const urlparse_url &u2, urlparse_url_fields field); bool fieldeq(const char *uri, const urlparse_url &u, urlparse_url_fields field, const char *t); bool fieldeq(const char *uri, const urlparse_url &u, urlparse_url_fields field, const std::string_view &t); std::string_view get_uri_field(const char *uri, const urlparse_url &u, urlparse_url_fields field); uint16_t get_default_port(const char *uri, const urlparse_url &u); bool porteq(const char *uri1, const urlparse_url &u1, const char *uri2, const urlparse_url &u2); void write_uri_field(std::ostream &o, const char *uri, const urlparse_url &u, urlparse_url_fields field); bool numeric_host(const char *hostname); bool numeric_host(const char *hostname, int family); // Returns numeric address string of |addr|. If getnameinfo() is // failed, "unknown" is returned. std::string numeric_name(const struct sockaddr *sa, socklen_t salen); // Returns string representation of numeric address and port of // |addr|. If address family is AF_UNIX, this return path to UNIX // domain socket. Otherwise, the format is like :. For // IPv6 address, address is enclosed by square brackets ([]). std::string to_numeric_addr(const Address *addr); std::string to_numeric_addr(const struct sockaddr *sa, socklen_t salen); // Sets |port| to |addr|. void set_port(Address &addr, uint16_t port); // Get port from |su|. uint16_t get_port(const sockaddr_union *su); // Returns true if |port| is prohibited as a QUIC client port. bool quic_prohibited_port(uint16_t port); // Returns ASCII dump of |data| of length |len|. Only ASCII printable // characters are preserved. Other characters are replaced with ".". std::string ascii_dump(const uint8_t *data, size_t len); // Returns absolute path of executable path. If argc == 0 or |cwd| is // nullptr, this function returns nullptr. If argv[0] starts with // '/', this function returns argv[0]. Otherwise return cwd + "/" + // argv[0]. If non-null is returned, it is NULL-terminated string and // dynamically allocated by malloc. The caller is responsible to free // it. char *get_exec_path(size_t argc, char **const argv, const char *cwd); // Validates path so that it does not contain directory traversal // vector. Returns true if path is safe. The |path| must start with // "/" otherwise returns false. This function should be called after // percent-decode was performed. bool check_path(const std::string &path); // Returns the |tv| value as 64 bit integer using a microsecond as an // unit. int64_t to_time64(const timeval &tv); // Returns true if ALPN ID |proto| is supported HTTP/2 protocol // identifier. bool check_h2_is_selected(const std::string_view &proto); // Selects h2 protocol ALPN ID if one of supported h2 versions are // present in |in| of length inlen. Returns true if h2 version is // selected. bool select_h2(const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen); // Selects protocol ALPN ID if one of identifiers contained in |protolist| is // present in |in| of length inlen. Returns true if identifier is // selected. bool select_protocol(const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, std::vector proto_list); // Parses delimited strings in |s| and returns the array of substring, // delimited by |delim|. The any white spaces around substring are // treated as a part of substring. std::vector parse_config_str_list(const std::string_view &s, char delim = ','); // Parses delimited strings in |s| and returns Substrings in |s| // delimited by |delim|. The any white spaces around substring are // treated as a part of substring. std::vector split_str(const std::string_view &s, char delim); // Behaves like split_str, but this variant splits at most |n| - 1 // times and returns at most |n| sub-strings. If |n| is zero, it // falls back to split_str. std::vector split_str(const std::string_view &s, char delim, size_t n); // Writes given time |tp| in Common Log format (e.g., // 03/Jul/2014:00:19:38 +0900) in buffer pointed by |out|. The buffer // must be at least 27 bytes, including terminal NULL byte. This // function returns std::string_view wrapping the buffer pointed by |out|, // and this string is terminated by NULL. std::string_view format_common_log(char *out, const std::chrono::system_clock::time_point &tp); #ifdef HAVE_STD_CHRONO_TIME_ZONE // Works like above but with a given time zone. std::string_view format_common_log(char *out, const std::chrono::system_clock::time_point &tp, const std::chrono::time_zone *tz); #endif // defined(HAVE_STD_CHRONO_TIME_ZONE) // Returns given time |tp| in ISO 8601 format (e.g., // 2014-11-15T12:58:24.741Z or 2014-11-15T12:58:24.741+09:00). std::string format_iso8601(const std::chrono::system_clock::time_point &tp); // Writes given time |tp| in ISO 8601 format (e.g., // 2014-11-15T12:58:24.741Z or 2014-11-15T12:58:24.741+09:00) in // buffer pointed by |out|. The buffer must be at least 30 bytes, // including terminal NULL byte. This function returns std::string_view // wrapping the buffer pointed by |out|, and this string is terminated // by NULL. std::string_view format_iso8601(char *out, const std::chrono::system_clock::time_point &tp); #ifdef HAVE_STD_CHRONO_TIME_ZONE // Works like above but with a given time zone. std::string_view format_iso8601(char *out, const std::chrono::system_clock::time_point &tp, const std::chrono::time_zone *tz); #endif // defined(HAVE_STD_CHRONO_TIME_ZONE) // Writes given time |tp| in ISO 8601 basic format (e.g., // 20141115T125824.741Z or 20141115T125824.741+0900) in buffer pointed // by |out|. The buffer must be at least 25 bytes, including terminal // NULL byte. This function returns std::string_view wrapping the buffer // pointed by |out|, and this string is terminated by NULL. std::string_view format_iso8601_basic(char *out, const std::chrono::system_clock::time_point &tp); #ifdef HAVE_STD_CHRONO_TIME_ZONE // Works like above but with a given time zone. std::string_view format_iso8601_basic(char *out, const std::chrono::system_clock::time_point &tp, const std::chrono::time_zone *tz); #endif // defined(HAVE_STD_CHRONO_TIME_ZONE) // Returns given time |tp| in HTTP Date format (e.g., Mon, 10 Oct 2016 // 10:25:58 GMT) std::string format_http_date(const std::chrono::system_clock::time_point &tp); // Writes given time |tp| in HTTP Date format (e.g., Mon, 10 Oct 2016 // 10:25:58 GMT) in buffer pointed by |out|. The buffer must be at // least 30 bytes, including terminal NULL byte. This function // returns std::string_view wrapping the buffer pointed by |out|, and this // string is terminated by NULL. std::string_view format_http_date(char *out, const std::chrono::system_clock::time_point &tp); // Return the system precision of the template parameter |Clock| as // a nanosecond value of type |Rep| template Rep clock_precision() { std::chrono::duration duration = typename Clock::duration(1); return duration.count(); } #ifdef HAVE_LIBEV template Duration duration_from(ev_tstamp d) { return std::chrono::duration_cast(std::chrono::duration(d)); } template ev_tstamp ev_tstamp_from(const Duration &d) { return std::chrono::duration(d).count(); } #endif // defined(HAVE_LIBEV) int make_socket_closeonexec(int fd); int make_socket_nonblocking(int fd); int make_socket_nodelay(int fd); int create_nonblock_socket(int family); int create_nonblock_udp_socket(int family); int bind_any_addr_udp(int fd, int family); bool check_socket_connected(int fd); // Returns the error code (errno) by inspecting SO_ERROR of given // |fd|. This function returns the error code if it succeeds, or -1. // Returning 0 means no error. int get_socket_error(int fd); // Returns true if |host| is IPv6 numeric address (e.g., ::1) bool ipv6_numeric_addr(const char *host); // Parses |s| as unsigned integer and returns the parsed integer. // Additionally, if |s| ends with 'k', 'm', 'g' and its upper case // characters, multiply the integer by 1024, 1024 * 1024 and 1024 * // 1024 respectively. If there is an error, returns no value. std::optional parse_uint_with_unit(const std::string_view &s); // Parses |s| as unsigned integer and returns the parsed integer.. std::optional parse_uint(const std::string_view &s); // Parses |s| as unsigned integer and returns the parsed integer // casted to double. If |s| ends with "s", the parsed value's unit is // a second. If |s| ends with "ms", the unit is millisecond. // Similarly, it also supports 'm' and 'h' for minutes and hours // respectively. If none of them are given, the unit is second. This // function returns no value if error occurs. std::optional parse_duration_with_unit(const std::string_view &s); // Returns string representation of time duration |t|. If t has // fractional part (at least more than or equal to 1e-3), |t| is // multiplied by 1000 and the unit "ms" is appended. Otherwise, |t| // is left as is and "s" is appended. std::string duration_str(double t); // Returns string representation of time duration |t|. It appends // unit after the formatting. The available units are s, ms and us. // The unit which is equal to or less than |t| is used and 2 // fractional digits follow. std::string format_duration(const std::chrono::microseconds &u); // Just like above, but this takes |t| as seconds. std::string format_duration(double t); // The maximum buffer size including terminal NULL to store the result // of make_hostport. inline constexpr size_t max_hostport = NI_MAXHOST + /* [] for IPv6 */ 2 + /* : */ 1 + /* port */ 5 + /* terminal NULL */ 1; // Just like make_http_hostport(), but doesn't treat 80 and 443 // specially. std::string_view make_hostport(BlockAllocator &balloc, const std::string_view &host, uint16_t port); template requires(std::indirectly_writable) std::string_view make_hostport(const std::string_view &host, uint16_t port, O result) { auto ipv6 = ipv6_numeric_addr(host.data()); auto p = result; if (ipv6) { *p++ = '['; } p = std::ranges::copy(host, p).out; if (ipv6) { *p++ = ']'; } *p++ = ':'; p = utos(port, p); *p = '\0'; return as_string_view(result, p); } // Creates "host:port" string using given |host| and |port|. If // |host| is numeric IPv6 address (e.g., ::1), it is enclosed by "[" // and "]". If |port| is 80 or 443, port part is omitted. std::string_view make_http_hostport(BlockAllocator &balloc, const std::string_view &host, uint16_t port); template requires(std::indirectly_writable) std::string_view make_http_hostport(const std::string_view &host, uint16_t port, O result) { if (port != 80 && port != 443) { return make_hostport(host, port, std::move(result)); } auto ipv6 = ipv6_numeric_addr(host.data()); auto p = result; if (ipv6) { *p++ = '['; } p = std::ranges::copy(host, p).out; if (ipv6) { *p++ = ']'; } *p = '\0'; return as_string_view(result, p); } // hexdump dumps |data| of length |datalen| in the format similar to // hexdump(1) with -C option. This function returns 0 if it succeeds, // or -1. int hexdump(FILE *out, const void *data, size_t datalen); // Copies 2 byte unsigned integer |n| in host byte order to |buf| in // network byte order. void put_uint16be(uint8_t *buf, uint16_t n); // Copies 4 byte unsigned integer |n| in host byte order to |buf| in // network byte order. void put_uint32be(uint8_t *buf, uint32_t n); // Retrieves 2 byte unsigned integer stored in |data| in network byte // order and returns it in host byte order. uint16_t get_uint16(const uint8_t *data); // Retrieves 4 byte unsigned integer stored in |data| in network byte // order and returns it in host byte order. uint32_t get_uint32(const uint8_t *data); // Retrieves 8 byte unsigned integer stored in |data| in network byte // order and returns it in host byte order. uint64_t get_uint64(const uint8_t *data); // Reads mime types file (see /etc/mime.types), and stores extension // -> MIME type map in |res|. This function returns 0 if it succeeds, // or -1. int read_mime_types(std::unordered_map &res, const char *filename); // Fills random alpha and digit byte to the range [|first|, |last|). // Returns the one beyond the |last|. template OutputIt random_alpha_digit(OutputIt first, OutputIt last, Generator &gen) { // If we use uint8_t instead char, gcc 6.2.0 complains by shouting // char-array initialized from wide string. static constexpr char s[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; std::uniform_int_distribution<> dis(0, 26 * 2 + 10 - 1); for (; first != last; ++first) { *first = static_cast>(s[dis(gen)]); } return first; } // Fills random bytes to the range [|first|, |last|). template void random_bytes(O first, O last, Generator &&gen) { std::uniform_int_distribution dis; std::ranges::generate(std::move(first), std::move(last), [&dis, &gen]() { return dis(gen); }); } // Fills the buffer pointed by |data| of length |destlen| with the // secure random bytes. void secure_random(uint8_t *dest, size_t destlen); // Shuffles the range [|first|, |last|] by calling swap function // |swap| for each pair. |swap| takes 2 iterators. If |swap| is // noop, no modification is made. template void shuffle(I first, I last, Generator &&gen, Swap swap) { auto len = std::ranges::distance(first, last); if (len < 2) { return; } using dist_type = std::uniform_int_distribution; using param_type = dist_type::param_type; dist_type d; for (decltype(len) i = 0; i < len - 1; ++i) { swap(first + i, first + d(gen, param_type(i, len - 1))); } } template requires(!std::is_array_v>) void shuffle(R &&r, Generator &&gen, Swap swap) { return shuffle(std::ranges::begin(r), std::ranges::end(r), std::forward(gen), std::move(swap)); } // Returns x**y double int_pow(double x, size_t y); uint32_t hash32(const std::string_view &s); // Computes SHA-256 of |s|, and stores it in |buf|. This function // returns 0 if it succeeds, or -1. int sha256(uint8_t *buf, const std::string_view &s); // Computes SHA-1 of |s|, and stores it in |buf|. This function // returns 0 if it succeeds, or -1. int sha1(uint8_t *buf, const std::string_view &s); // Returns host from |hostport|. If host cannot be found in // |hostport|, returns empty string. The returned string might not be // NULL-terminated. std::string_view extract_host(const std::string_view &hostport); // split_hostport splits host and port in |hostport|. Unlike // extract_host, square brackets enclosing host name is stripped. If // port is not available, it returns empty string in the second // string. The returned string might not be NULL-terminated. On any // error, it returns a pair which has empty strings. std::pair split_hostport(const std::string_view &hostport); // Returns new std::mt19937 object. std::mt19937 make_mt19937(); // daemonize calls daemon(3). If __APPLE__ is defined, it implements // daemon() using fork(). int daemonize(int nochdir, int noclose); // Returns |s| from which trailing white spaces (SPC or HTAB) are // removed. If any white spaces are removed, new string is allocated // by |balloc| and returned. Otherwise, the copy of |s| is returned // without allocation. std::string_view rstrip(BlockAllocator &balloc, const std::string_view &s); // contains returns true if |r| contains |value|. template requires(!std::is_array_v>) bool contains(R &&r, const T &value) { return std::ranges::find(r, value) != std::ranges::end(r); } // contains returns true if |value| is contained in a range [|first|, // |last|). template constexpr bool contains(I first, I last, const T &value) { return std::ranges::find(std::move(first), last, value) != last; } #ifdef ENABLE_HTTP3 int msghdr_get_local_addr(Address &dest, msghdr *msg, int family); uint8_t msghdr_get_ecn(msghdr *msg, int family); // msghdr_get_udp_gro returns UDP_GRO value from |msg|. If UDP_GRO is // not found, or UDP_GRO is not supported, this function returns 0. size_t msghdr_get_udp_gro(msghdr *msg); #endif // defined(ENABLE_HTTP3) } // namespace util } // namespace nghttp2 #endif // !defined(UTIL_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_quic_connection_handler.cc0000644000000000000000000000013215077107270021457 xustar0030 mtime=1761382072.998444116 30 atime=1761382106.197310458 30 ctime=1761382109.193300112 nghttp2-1.68.0/src/shrpx_quic_connection_handler.cc0000644000175100017510000006053715077107270022062 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2021 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_quic_connection_handler.h" #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include # include #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include #include #include "shrpx_worker.h" #include "shrpx_client_handler.h" #include "shrpx_log.h" #include "shrpx_http3_upstream.h" #include "shrpx_connection_handler.h" namespace shrpx { namespace { void stateless_reset_bucket_regen_timercb(struct ev_loop *loop, ev_timer *w, int revents) { auto quic_conn_handler = static_cast(w->data); quic_conn_handler->on_stateless_reset_bucket_regen(); } } // namespace QUICConnectionHandler::QUICConnectionHandler(Worker *worker) : worker_{worker}, stateless_reset_bucket_{SHRPX_QUIC_STATELESS_RESET_BURST} { ev_timer_init(&stateless_reset_bucket_regen_timer_, stateless_reset_bucket_regen_timercb, 0., 1.); stateless_reset_bucket_regen_timer_.data = this; } QUICConnectionHandler::~QUICConnectionHandler() { ev_timer_stop(worker_->get_loop(), &stateless_reset_bucket_regen_timer_); } int QUICConnectionHandler::handle_packet(const UpstreamAddr *faddr, const Address &remote_addr, const Address &local_addr, const ngtcp2_pkt_info &pi, std::span data) { int rv; ngtcp2_version_cid vc; rv = ngtcp2_pkt_decode_version_cid(&vc, data.data(), data.size(), SHRPX_QUIC_SCIDLEN); switch (rv) { case 0: break; case NGTCP2_ERR_VERSION_NEGOTIATION: send_version_negotiation(faddr, vc.version, {vc.dcid, vc.dcidlen}, {vc.scid, vc.scidlen}, remote_addr, local_addr); return 0; default: return 0; } auto config = get_config(); ngtcp2_cid dcid_key; ngtcp2_cid_init(&dcid_key, vc.dcid, vc.dcidlen); auto conn_handler = worker_->get_connection_handler(); ClientHandler *handler; auto &quicconf = config->quic; auto it = connections_.find(dcid_key); if (it == std::ranges::end(connections_)) { auto cwit = close_waits_.find(dcid_key); if (cwit != std::ranges::end(close_waits_)) { auto cw = (*cwit).second; cw->handle_packet(faddr, remote_addr, local_addr, pi, data); return 0; } if (data[0] & 0x80) { if (generate_quic_hashed_connection_id(dcid_key, remote_addr, local_addr, dcid_key) != 0) { return 0; } it = connections_.find(dcid_key); if (it == std::ranges::end(connections_)) { auto cwit = close_waits_.find(dcid_key); if (cwit != std::ranges::end(close_waits_)) { auto cw = (*cwit).second; cw->handle_packet(faddr, remote_addr, local_addr, pi, data); return 0; } } } } if (it == std::ranges::end(connections_)) { ConnectionID decrypted_dcid; auto &qkms = conn_handler->get_quic_keying_materials(); const QUICKeyingMaterial *qkm = nullptr; if (vc.dcidlen == SHRPX_QUIC_SCIDLEN) { qkm = select_quic_keying_material( *qkms.get(), vc.dcid[0] & SHRPX_QUIC_DCID_KM_ID_MASK); if (decrypt_quic_connection_id(decrypted_dcid, vc.dcid + SHRPX_QUIC_CID_WORKER_ID_OFFSET, qkm->cid_decryption_ctx) != 0) { return 0; } if (qkm != &qkms->keying_materials.front() || decrypted_dcid.worker != worker_->get_worker_id()) { auto quic_lwp = conn_handler->match_quic_lingering_worker_process_worker_id( decrypted_dcid.worker); if (quic_lwp) { if (conn_handler->forward_quic_packet_to_lingering_worker_process( quic_lwp, remote_addr, local_addr, pi, data) == 0) { return 0; } return 0; } } } // new connection auto &upstreamconf = config->conn.upstream; if (worker_->get_worker_stat()->num_connections >= upstreamconf.worker_connections) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "Too many connections >=" << upstreamconf.worker_connections; } return 0; } ngtcp2_pkt_hd hd; ngtcp2_cid odcid, *podcid = nullptr; std::span token; ngtcp2_token_type token_type = NGTCP2_TOKEN_TYPE_UNKNOWN; switch (ngtcp2_accept(&hd, data.data(), data.size())) { case 0: { // If we get Initial and it has the Worker ID of this worker, it // is likely that client is intentionally use the prefix. Just // drop it. if (vc.dcidlen == SHRPX_QUIC_SCIDLEN) { if (qkm != &qkms->keying_materials.front()) { qkm = &qkms->keying_materials.front(); if (decrypt_quic_connection_id( decrypted_dcid, vc.dcid + SHRPX_QUIC_CID_WORKER_ID_OFFSET, qkm->cid_decryption_ctx) != 0) { return 0; } } if (decrypted_dcid.worker == worker_->get_worker_id()) { return 0; } } if (worker_->get_graceful_shutdown()) { send_connection_close(faddr, hd.version, hd.dcid, hd.scid, remote_addr, local_addr, NGTCP2_CONNECTION_REFUSED, data.size() * 3); return 0; } if (hd.tokenlen == 0) { if (quicconf.upstream.require_token) { send_retry(faddr, vc.version, {vc.dcid, vc.dcidlen}, {vc.scid, vc.scidlen}, remote_addr, local_addr, data.size() * 3); return 0; } break; } switch (hd.token[0]) { case NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY: { if (vc.dcidlen != SHRPX_QUIC_SCIDLEN) { // Initial packets with Retry token must have DCID chosen by // server. return 0; } auto qkm = select_quic_keying_material( *qkms.get(), vc.dcid[0] & SHRPX_QUIC_DCID_KM_ID_MASK); if (verify_retry_token(odcid, {hd.token, hd.tokenlen}, hd.version, hd.dcid, &remote_addr.su.sa, remote_addr.len, qkm->secret) != 0) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "Failed to validate Retry token from remote=" << util::to_numeric_addr(&remote_addr); } // 2nd Retry packet is not allowed, so send CONNECTION_CLOSE // with INVALID_TOKEN. send_connection_close(faddr, hd.version, hd.dcid, hd.scid, remote_addr, local_addr, NGTCP2_INVALID_TOKEN, data.size() * 3); return 0; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "Successfully validated Retry token from remote=" << util::to_numeric_addr(&remote_addr); } podcid = &odcid; token = {hd.token, hd.tokenlen}; token_type = NGTCP2_TOKEN_TYPE_RETRY; break; } case NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR: { // If a token is a regular token, it must be at least // NGTCP2_MIN_INITIAL_DCIDLEN bytes long. if (vc.dcidlen < NGTCP2_MIN_INITIAL_DCIDLEN) { return 0; } if (hd.tokenlen != NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN + 1) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "Failed to validate token from remote=" << util::to_numeric_addr(&remote_addr); } if (quicconf.upstream.require_token) { send_retry(faddr, vc.version, {vc.dcid, vc.dcidlen}, {vc.scid, vc.scidlen}, remote_addr, local_addr, data.size() * 3); return 0; } break; } auto qkm = select_quic_keying_material( *qkms.get(), hd.token[NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN]); if (verify_token({hd.token, hd.tokenlen}, &remote_addr.su.sa, remote_addr.len, qkm->secret) != 0) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "Failed to validate token from remote=" << util::to_numeric_addr(&remote_addr); } if (quicconf.upstream.require_token) { send_retry(faddr, vc.version, {vc.dcid, vc.dcidlen}, {vc.scid, vc.scidlen}, remote_addr, local_addr, data.size() * 3); return 0; } break; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "Successfully validated token from remote=" << util::to_numeric_addr(&remote_addr); } token = {hd.token, hd.tokenlen}; token_type = NGTCP2_TOKEN_TYPE_NEW_TOKEN; break; } default: if (quicconf.upstream.require_token) { send_retry(faddr, vc.version, {vc.dcid, vc.dcidlen}, {vc.scid, vc.scidlen}, remote_addr, local_addr, data.size() * 3); return 0; } break; } break; } default: if (!(data[0] & 0x80) && vc.dcidlen == SHRPX_QUIC_SCIDLEN && decrypted_dcid.worker != worker_->get_worker_id()) { if (!config->single_thread && conn_handler->forward_quic_packet( faddr, remote_addr, local_addr, pi, decrypted_dcid.worker, data) == 0) { return 0; } if (data.size() >= SHRPX_QUIC_SCIDLEN + 21) { send_stateless_reset(faddr, data.size(), {vc.dcid, vc.dcidlen}, remote_addr, local_addr); } } return 0; } handler = handle_new_connection(faddr, remote_addr, local_addr, hd, podcid, token, token_type); if (handler == nullptr) { return 0; } } else { handler = (*it).second; } if (handler->read_quic(faddr, remote_addr, local_addr, pi, data) != 0) { delete handler; return 0; } handler->signal_write(); return 0; } ClientHandler *QUICConnectionHandler::handle_new_connection( const UpstreamAddr *faddr, const Address &remote_addr, const Address &local_addr, const ngtcp2_pkt_hd &hd, const ngtcp2_cid *odcid, std::span token, ngtcp2_token_type token_type) { std::array host; std::array service; int rv; rv = getnameinfo(&remote_addr.su.sa, remote_addr.len, host.data(), host.size(), service.data(), service.size(), NI_NUMERICHOST | NI_NUMERICSERV); if (rv != 0) { LOG(ERROR) << "getnameinfo() failed: " << gai_strerror(rv); return nullptr; } auto ssl_ctx = worker_->get_quic_sv_ssl_ctx(); assert(ssl_ctx); auto ssl = tls::create_ssl(ssl_ctx); if (ssl == nullptr) { return nullptr; } #if !OPENSSL_3_5_0_API && \ (defined(NGHTTP2_GENUINE_OPENSSL) || defined(NGHTTP2_OPENSSL_IS_WOLFSSL)) assert(SSL_is_quic(ssl)); #endif // !OPENSSL_3_5_0_API && (defined(NGHTTP2_GENUINE_OPENSSL) || // defined(NGHTTP2_OPENSSL_IS_WOLFSSL)) SSL_set_accept_state(ssl); auto config = get_config(); auto &quicconf = config->quic; if (quicconf.upstream.early_data) { #if OPENSSL_3_5_0_API SSL_set_quic_tls_early_data_enabled(ssl, 1); #elif defined(NGHTTP2_GENUINE_OPENSSL) || \ (defined(NGHTTP2_OPENSSL_IS_WOLFSSL) && defined(WOLFSSL_EARLY_DATA)) SSL_set_quic_early_data_enabled(ssl, 1); #elif defined(NGHTTP2_OPENSSL_IS_BORINGSSL) SSL_set_early_data_enabled(ssl, 1); #endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) } // Disable TLS session ticket if we don't have working ticket // keys. if (!worker_->get_ticket_keys()) { SSL_set_options(ssl, SSL_OP_NO_TICKET); } auto handler = std::make_unique( worker_, faddr->fd, ssl, std::string_view{host.data()}, std::string_view{service.data()}, remote_addr.su.sa.sa_family, faddr); auto &fwdconf = config->http.forwarded; if (fwdconf.params & FORWARDED_BY) { handler->set_local_hostport(&local_addr.su.sa, local_addr.len); } auto upstream = std::make_unique(handler.get()); if (upstream->init(faddr, remote_addr, local_addr, hd, odcid, token, token_type) != 0) { return nullptr; } handler->setup_http3_upstream(std::move(upstream)); return handler.release(); } namespace { uint32_t generate_reserved_version(const Address &addr, uint32_t version) { uint32_t h = 0x811C9DC5u; const uint8_t *p = reinterpret_cast(&addr.su.sa); const uint8_t *ep = p + addr.len; for (; p != ep; ++p) { h ^= *p; h *= 0x01000193u; } version = htonl(version); p = (const uint8_t *)&version; ep = p + sizeof(version); for (; p != ep; ++p) { h ^= *p; h *= 0x01000193u; } h &= 0xf0f0f0f0u; h |= 0x0a0a0a0au; return h; } } // namespace int QUICConnectionHandler::send_retry( const UpstreamAddr *faddr, uint32_t version, std::span ini_dcid, std::span ini_scid, const Address &remote_addr, const Address &local_addr, size_t max_pktlen) { std::array host; std::array port; if (getnameinfo(&remote_addr.su.sa, remote_addr.len, host.data(), host.size(), port.data(), port.size(), NI_NUMERICHOST | NI_NUMERICSERV) != 0) { return -1; } auto config = get_config(); auto &quicconf = config->quic; auto conn_handler = worker_->get_connection_handler(); auto &qkms = conn_handler->get_quic_keying_materials(); auto &qkm = qkms->keying_materials.front(); ngtcp2_cid retry_scid; if (generate_quic_retry_connection_id(retry_scid, quicconf.server_id, qkm.id, qkm.cid_encryption_ctx) != 0) { return -1; } ngtcp2_cid idcid, iscid; ngtcp2_cid_init(&idcid, ini_dcid.data(), ini_dcid.size()); ngtcp2_cid_init(&iscid, ini_scid.data(), ini_scid.size()); std::array tokenbuf; auto token = generate_retry_token(tokenbuf, version, &remote_addr.su.sa, remote_addr.len, retry_scid, idcid, qkm.secret); if (!token) { return -1; } std::vector buf; buf.resize(std::min(max_pktlen, static_cast(256))); auto nwrite = ngtcp2_crypto_write_retry(buf.data(), buf.size(), version, &iscid, &retry_scid, &idcid, token->data(), token->size()); if (nwrite < 0) { LOG(ERROR) << "ngtcp2_crypto_write_retry: " << ngtcp2_strerror(static_cast(nwrite)); return -1; } buf.resize(as_unsigned(nwrite)); quic_send_packet(faddr, &remote_addr.su.sa, remote_addr.len, &local_addr.su.sa, local_addr.len, ngtcp2_pkt_info{}, buf, buf.size()); if (generate_quic_hashed_connection_id(idcid, remote_addr, local_addr, idcid) != 0) { return -1; } auto d = static_cast(NGTCP2_DEFAULT_INITIAL_RTT * 3) / NGTCP2_SECONDS; if (LOG_ENABLED(INFO)) { LOG(INFO) << "Enter close-wait period " << d << "s with " << buf.size() << " bytes sentinel packet"; } auto cw = std::make_unique(worker_, std::vector{idcid}, std::move(buf), d); add_close_wait(cw.release()); return 0; } int QUICConnectionHandler::send_version_negotiation( const UpstreamAddr *faddr, uint32_t version, std::span ini_dcid, std::span ini_scid, const Address &remote_addr, const Address &local_addr) { auto sv = std::to_array({ generate_reserved_version(remote_addr, version), NGTCP2_PROTO_VER_V1, }); std::array buf; uint8_t rand_byte; util::random_bytes(&rand_byte, &rand_byte + 1, worker_->get_randgen()); auto nwrite = ngtcp2_pkt_write_version_negotiation( buf.data(), buf.size(), rand_byte, ini_scid.data(), ini_scid.size(), ini_dcid.data(), ini_dcid.size(), sv.data(), sv.size()); if (nwrite < 0) { LOG(ERROR) << "ngtcp2_pkt_write_version_negotiation: " << ngtcp2_strerror(static_cast(nwrite)); return -1; } auto pkt = std::span{buf}.first(as_unsigned(nwrite)); return quic_send_packet(faddr, &remote_addr.su.sa, remote_addr.len, &local_addr.su.sa, local_addr.len, ngtcp2_pkt_info{}, pkt, pkt.size()); } int QUICConnectionHandler::send_stateless_reset(const UpstreamAddr *faddr, size_t pktlen, std::span dcid, const Address &remote_addr, const Address &local_addr) { if (stateless_reset_bucket_ == 0) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "Stateless Reset bucket has been depleted"; } return 0; } --stateless_reset_bucket_; if (!ev_is_active(&stateless_reset_bucket_regen_timer_)) { ev_timer_again(worker_->get_loop(), &stateless_reset_bucket_regen_timer_); } std::array token; ngtcp2_cid cid; ngtcp2_cid_init(&cid, dcid.data(), dcid.size()); auto conn_handler = worker_->get_connection_handler(); auto &qkms = conn_handler->get_quic_keying_materials(); auto &qkm = qkms->keying_materials.front(); if (auto rv = generate_quic_stateless_reset_token( token.data(), cid, qkm.secret.data(), qkm.secret.size()); rv != 0) { return -1; } // SCID + minimum expansion - NGTCP2_STATELESS_RESET_TOKENLEN constexpr size_t max_rand_byteslen = NGTCP2_MAX_CIDLEN + 22 - NGTCP2_STATELESS_RESET_TOKENLEN; size_t rand_byteslen; if (pktlen <= 43) { // As per // https://datatracker.ietf.org/doc/html/rfc9000#section-10.3 rand_byteslen = pktlen - NGTCP2_STATELESS_RESET_TOKENLEN - 1; } else { rand_byteslen = max_rand_byteslen; } std::array rand_bytes; if (RAND_bytes(rand_bytes.data(), static_cast( rand_byteslen)) != 1) { return -1; } std::array buf; auto nwrite = ngtcp2_pkt_write_stateless_reset( buf.data(), buf.size(), token.data(), rand_bytes.data(), rand_byteslen); if (nwrite < 0) { LOG(ERROR) << "ngtcp2_pkt_write_stateless_reset: " << ngtcp2_strerror(static_cast(nwrite)); return -1; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "Send stateless_reset to remote=" << util::to_numeric_addr(&remote_addr) << " dcid=" << util::format_hex(dcid); } auto pkt = std::span{buf}.first(as_unsigned(nwrite)); return quic_send_packet(faddr, &remote_addr.su.sa, remote_addr.len, &local_addr.su.sa, local_addr.len, ngtcp2_pkt_info{}, pkt, pkt.size()); } int QUICConnectionHandler::send_connection_close( const UpstreamAddr *faddr, uint32_t version, const ngtcp2_cid &ini_dcid, const ngtcp2_cid &ini_scid, const Address &remote_addr, const Address &local_addr, uint64_t error_code, size_t max_pktlen) { std::array buf; max_pktlen = std::min(max_pktlen, buf.size()); auto nwrite = ngtcp2_crypto_write_connection_close( buf.data(), max_pktlen, version, &ini_scid, &ini_dcid, error_code, nullptr, 0); if (nwrite < 0) { LOG(ERROR) << "ngtcp2_crypto_write_connection_close failed"; return -1; } if (LOG_ENABLED(INFO)) { LOG(INFO) << "Send Initial CONNECTION_CLOSE with error_code=" << log::hex << error_code << log::dec << " to remote=" << util::to_numeric_addr(&remote_addr) << " dcid=" << util::format_hex(std::span{ini_scid.data, ini_scid.datalen}) << " scid=" << util::format_hex(std::span{ini_dcid.data, ini_dcid.datalen}); } auto pkt = std::span{buf}.first(as_unsigned(nwrite)); return quic_send_packet(faddr, &remote_addr.su.sa, remote_addr.len, &local_addr.su.sa, local_addr.len, ngtcp2_pkt_info{}, pkt, pkt.size()); } void QUICConnectionHandler::add_connection_id(const ngtcp2_cid &cid, ClientHandler *handler) { connections_.emplace(cid, handler); } void QUICConnectionHandler::remove_connection_id(const ngtcp2_cid &cid) { connections_.erase(cid); } void QUICConnectionHandler::add_close_wait(CloseWait *cw) { for (auto &cid : cw->scids) { close_waits_.emplace(cid, cw); } } void QUICConnectionHandler::remove_close_wait(const CloseWait *cw) { for (auto &cid : cw->scids) { close_waits_.erase(cid); } } void QUICConnectionHandler::on_stateless_reset_bucket_regen() { assert(stateless_reset_bucket_ < SHRPX_QUIC_STATELESS_RESET_BURST); if (++stateless_reset_bucket_ == SHRPX_QUIC_STATELESS_RESET_BURST) { ev_timer_stop(worker_->get_loop(), &stateless_reset_bucket_regen_timer_); } } static void close_wait_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { auto cw = static_cast(w->data); if (LOG_ENABLED(INFO)) { LOG(INFO) << "close-wait period finished"; } auto quic_conn_handler = cw->worker->get_quic_connection_handler(); quic_conn_handler->remove_close_wait(cw); delete cw; } CloseWait::CloseWait(Worker *worker, std::vector scids, std::vector pkt, ev_tstamp period) : worker{worker}, scids{std::move(scids)}, pkt{std::move(pkt)}, bytes_recv{0}, bytes_sent{0}, num_pkts_recv{0}, next_pkts_recv{1} { ++worker->get_worker_stat()->num_close_waits; ev_timer_init(&timer, close_wait_timeoutcb, period, 0.); timer.data = this; ev_timer_start(worker->get_loop(), &timer); } CloseWait::~CloseWait() { auto loop = worker->get_loop(); ev_timer_stop(loop, &timer); auto worker_stat = worker->get_worker_stat(); --worker_stat->num_close_waits; if (worker->get_graceful_shutdown() && worker_stat->num_connections == 0 && worker_stat->num_close_waits == 0) { ev_break(loop); } } int CloseWait::handle_packet(const UpstreamAddr *faddr, const Address &remote_addr, const Address &local_addr, const ngtcp2_pkt_info &pi, std::span data) { if (pkt.empty()) { return 0; } ++num_pkts_recv; bytes_recv += data.size(); if (bytes_sent + pkt.size() > 3 * bytes_recv || next_pkts_recv > num_pkts_recv) { return 0; } auto rv = quic_send_packet(faddr, &remote_addr.su.sa, remote_addr.len, &local_addr.su.sa, local_addr.len, ngtcp2_pkt_info{}, pkt, pkt.size()); if (rv != 0) { return -1; } next_pkts_recv *= 2; bytes_sent += pkt.size(); return 0; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_http2_upstream.h0000644000000000000000000000013215077107270017425 xustar0030 mtime=1761382072.994444134 30 atime=1761382106.091310925 30 ctime=1761382109.087300419 nghttp2-1.68.0/src/shrpx_http2_upstream.h0000644000175100017510000001300515077107270020014 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_HTTP2_UPSTREAM_H #define SHRPX_HTTP2_UPSTREAM_H #include "shrpx.h" #include #include #include #include "shrpx_upstream.h" #include "shrpx_downstream_queue.h" #include "memchunk.h" #include "buffer.h" using namespace nghttp2; namespace shrpx { class ClientHandler; class HttpsUpstream; class Http2Upstream : public Upstream { public: Http2Upstream(ClientHandler *handler); virtual ~Http2Upstream(); virtual int on_read(); virtual int on_write(); virtual int on_timeout(Downstream *downstream); virtual int on_downstream_abort_request(Downstream *downstream, unsigned int status_code); virtual int on_downstream_abort_request_with_https_redirect(Downstream *downstream); virtual ClientHandler *get_client_handler() const; virtual int downstream_read(DownstreamConnection *dconn); virtual int downstream_write(DownstreamConnection *dconn); virtual int downstream_eof(DownstreamConnection *dconn); virtual int downstream_error(DownstreamConnection *dconn, int events); void add_pending_downstream(std::unique_ptr downstream); void remove_downstream(Downstream *downstream); int rst_stream(Downstream *downstream, uint32_t error_code); int terminate_session(uint32_t error_code); int error_reply(Downstream *downstream, unsigned int status_code); virtual void pause_read(IOCtrlReason reason); virtual int resume_read(IOCtrlReason reason, Downstream *downstream, size_t consumed); virtual int on_downstream_header_complete(Downstream *downstream); virtual int on_downstream_body(Downstream *downstream, const uint8_t *data, size_t len, bool flush); virtual int on_downstream_body_complete(Downstream *downstream); virtual void on_handler_delete(); virtual int on_downstream_reset(Downstream *downstream, bool no_retry); virtual int send_reply(Downstream *downstream, const uint8_t *body, size_t bodylen); virtual int initiate_push(Downstream *downstream, const std::string_view &uri); virtual int response_riovec(struct iovec *iov, int iovcnt) const; virtual void response_drain(size_t n); virtual bool response_empty() const; virtual Downstream *on_downstream_push_promise(Downstream *downstream, int32_t promised_stream_id); virtual int on_downstream_push_promise_complete(Downstream *downstream, Downstream *promised_downstream); virtual bool push_enabled() const; virtual void cancel_premature_downstream(Downstream *promised_downstream); bool get_flow_control() const; // Perform HTTP/2 upgrade from |upstream|. On success, this object // takes ownership of the |upstream|. This function returns 0 if it // succeeds, or -1. int upgrade_upstream(HttpsUpstream *upstream); void start_settings_timer(); void stop_settings_timer(); int consume(int32_t stream_id, size_t len); void log_response_headers(Downstream *downstream, const std::vector &nva) const; void start_downstream(Downstream *downstream); void initiate_downstream(Downstream *downstream); void submit_goaway(); void check_shutdown(); // Starts graceful shutdown period. void start_graceful_shutdown(); int prepare_push_promise(Downstream *downstream); int submit_push_promise(const std::string_view &scheme, const std::string_view &authority, const std::string_view &path, Downstream *downstream); // Called when new request has started. void on_start_request(const nghttp2_frame *frame); int on_request_headers(Downstream *downstream, const nghttp2_frame *frame); DefaultMemchunks *get_response_buf(); size_t get_max_buffer_size() const; int redirect_to_https(Downstream *downstream); private: DefaultMemchunks wb_; std::unique_ptr pre_upstream_; DownstreamQueue downstream_queue_; ev_timer settings_timer_; ev_timer shutdown_timer_; ev_prepare prep_; ClientHandler *handler_; nghttp2_session *session_; size_t max_buffer_size_; // The number of requests seen so far. size_t num_requests_; bool flow_control_; }; nghttp2_session_callbacks *create_http2_upstream_callbacks(); } // namespace shrpx #endif // !defined(SHRPX_HTTP2_UPSTREAM_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_http2_upstream.cc0000644000000000000000000000013215077107270017563 xustar0030 mtime=1761382072.994444134 30 atime=1761382106.090310929 30 ctime=1761382109.086300422 nghttp2-1.68.0/src/shrpx_http2_upstream.cc0000644000175100017510000021257215077107270020164 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_http2_upstream.h" #include #include #include #include #include "shrpx_client_handler.h" #include "shrpx_https_upstream.h" #include "shrpx_downstream.h" #include "shrpx_downstream_connection.h" #include "shrpx_config.h" #include "shrpx_http.h" #include "shrpx_worker.h" #include "shrpx_http2_session.h" #include "shrpx_log.h" #ifdef HAVE_MRUBY # include "shrpx_mruby.h" #endif // defined(HAVE_MRUBY) #include "http2.h" #include "util.h" #include "base64.h" #include "app_helper.h" #include "template.h" using namespace nghttp2; namespace shrpx { namespace { constexpr size_t MAX_BUFFER_SIZE = 32_k; } // namespace namespace { int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *user_data) { auto upstream = static_cast(user_data); if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "Stream stream_id=" << stream_id << " is being closed"; } auto downstream = static_cast( nghttp2_session_get_stream_user_data(session, stream_id)); if (!downstream) { return 0; } auto &req = downstream->request(); upstream->consume(stream_id, req.unconsumed_body_length); req.unconsumed_body_length = 0; if (downstream->get_request_state() == DownstreamState::CONNECT_FAIL) { upstream->remove_downstream(downstream); // downstream was deleted return 0; } if (downstream->can_detach_downstream_connection()) { // Keep-alive downstream->detach_downstream_connection(); } downstream->set_request_state(DownstreamState::STREAM_CLOSED); // At this point, downstream read may be paused. // If shrpx_downstream::push_request_headers() failed, the // error is handled here. upstream->remove_downstream(downstream); // downstream was deleted // How to test this case? Request sufficient large download // and make client send RST_STREAM after it gets first DATA // frame chunk. return 0; } } // namespace int Http2Upstream::upgrade_upstream(HttpsUpstream *http) { int rv; auto &balloc = http->get_downstream()->get_block_allocator(); auto http2_settings = http->get_downstream()->get_http2_settings(); http2_settings = util::to_base64(balloc, http2_settings); auto settings_payload = base64::decode(balloc, http2_settings); rv = nghttp2_session_upgrade2( session_, settings_payload.data(), settings_payload.size(), http->get_downstream()->request().method == HTTP_HEAD, nullptr); if (rv != 0) { if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "nghttp2_session_upgrade() returned error: " << nghttp2_strerror(rv); } return -1; } pre_upstream_.reset(http); auto downstream = http->pop_downstream(); downstream->reset_upstream(this); downstream->set_stream_id(1); downstream->reset_upstream_rtimer(); downstream->set_stream_id(1); auto ptr = downstream.get(); nghttp2_session_set_stream_user_data(session_, 1, ptr); downstream_queue_.add_pending(std::move(downstream)); downstream_queue_.mark_active(ptr); // TODO This might not be necessary handler_->stop_read_timer(); if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "Connection upgraded to HTTP/2"; } return 0; } void Http2Upstream::start_settings_timer() { ev_timer_start(handler_->get_loop(), &settings_timer_); } void Http2Upstream::stop_settings_timer() { ev_timer_stop(handler_->get_loop(), &settings_timer_); } namespace { int on_header_callback2(nghttp2_session *session, const nghttp2_frame *frame, nghttp2_rcbuf *name, nghttp2_rcbuf *value, uint8_t flags, void *user_data) { auto namebuf = nghttp2_rcbuf_get_buf(name); auto valuebuf = nghttp2_rcbuf_get_buf(value); auto config = get_config(); if (config->http2.upstream.debug.frame_debug) { verbose_on_header_callback(session, frame, namebuf.base, namebuf.len, valuebuf.base, valuebuf.len, flags, user_data); } if (frame->hd.type != NGHTTP2_HEADERS) { return 0; } auto upstream = static_cast(user_data); auto downstream = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); if (!downstream) { return 0; } auto &req = downstream->request(); auto &httpconf = config->http; if (req.fs.buffer_size() + namebuf.len + valuebuf.len > httpconf.request_header_field_buffer || req.fs.num_fields() >= httpconf.max_request_header_fields) { if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { return 0; } if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "Too large or many header field size=" << req.fs.buffer_size() + namebuf.len + valuebuf.len << ", num=" << req.fs.num_fields() + 1; } // just ignore header fields if this is trailer part. if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) { return 0; } if (upstream->error_reply(downstream, 431) != 0) { return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } return 0; } auto nameref = as_string_view(namebuf.base, namebuf.len); auto valueref = as_string_view(valuebuf.base, valuebuf.len); auto token = http2::lookup_token(nameref); auto no_index = flags & NGHTTP2_NV_FLAG_NO_INDEX; downstream->add_rcbuf(name); downstream->add_rcbuf(value); if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) { // just store header fields for trailer part req.fs.add_trailer_token(nameref, valueref, no_index, token); return 0; } req.fs.add_header_token(nameref, valueref, no_index, token); return 0; } } // namespace namespace { int on_begin_headers_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { auto upstream = static_cast(user_data); if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) { return 0; } if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "Received upstream request HEADERS stream_id=" << frame->hd.stream_id; } upstream->on_start_request(frame); return 0; } } // namespace void Http2Upstream::on_start_request(const nghttp2_frame *frame) { auto downstream = std::make_unique(this, handler_->get_mcpool(), frame->hd.stream_id); nghttp2_session_set_stream_user_data(session_, frame->hd.stream_id, downstream.get()); downstream->reset_upstream_rtimer(); auto config = get_config(); auto &httpconf = config->http; handler_->reset_upstream_read_timeout(httpconf.timeout.header); auto &req = downstream->request(); // Although, we deprecated minor version from HTTP/2, we supply // minor version 0 to use via header field in a conventional way. req.http_major = 2; req.http_minor = 0; add_pending_downstream(std::move(downstream)); ++num_requests_; if (httpconf.max_requests <= num_requests_) { start_graceful_shutdown(); } } int Http2Upstream::on_request_headers(Downstream *downstream, const nghttp2_frame *frame) { auto lgconf = log_config(); lgconf->update_tstamp(std::chrono::system_clock::now()); auto &req = downstream->request(); req.tstamp = lgconf->tstamp; if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { return 0; } auto &nva = req.fs.headers(); if (LOG_ENABLED(INFO)) { std::stringstream ss; for (auto &nv : nva) { if (nv.name == "authorization"sv) { ss << TTY_HTTP_HD << nv.name << TTY_RST << ": \n"; continue; } ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n"; } ULOG(INFO, this) << "HTTP request headers. stream_id=" << downstream->get_stream_id() << "\n" << ss.str(); } auto config = get_config(); auto &dump = config->http2.upstream.debug.dump; if (dump.request_header) { http2::dump_nv(dump.request_header, nva); } auto content_length = req.fs.header(http2::HD_CONTENT_LENGTH); if (content_length) { // libnghttp2 guarantees this can be parsed req.fs.content_length = util::parse_uint(content_length->value).value_or(-1); } // presence of mandatory header fields are guaranteed by libnghttp2. auto authority = req.fs.header(http2::HD__AUTHORITY); auto path = req.fs.header(http2::HD__PATH); auto method = req.fs.header(http2::HD__METHOD); auto scheme = req.fs.header(http2::HD__SCHEME); auto method_token = http2::lookup_method_token(method->value); if (method_token == -1) { if (error_reply(downstream, 501) != 0) { return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } return 0; } auto faddr = handler_->get_upstream_addr(); // For HTTP/2 proxy, we require :authority. if (method_token != HTTP_CONNECT && config->http2_proxy && faddr->alt_mode == UpstreamAltMode::NONE && !authority) { rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR); return 0; } req.method = method_token; if (scheme) { req.scheme = scheme->value; } // nghttp2 library guarantees either :authority or host exist if (!authority) { req.no_authority = true; authority = req.fs.header(http2::HD_HOST); } if (authority) { req.authority = authority->value; } if (path) { if (method_token == HTTP_OPTIONS && path->value == "*"sv) { // Server-wide OPTIONS request. Path is empty. } else if (config->http2_proxy && faddr->alt_mode == UpstreamAltMode::NONE) { req.path = path->value; } else { req.path = http2::rewrite_clean_path(downstream->get_block_allocator(), path->value); } } auto connect_proto = req.fs.header(http2::HD__PROTOCOL); if (connect_proto) { if (connect_proto->value != "websocket"sv) { if (error_reply(downstream, 400) != 0) { return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } return 0; } req.connect_proto = ConnectProto::WEBSOCKET; } if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { req.http2_expect_body = true; } else if (req.fs.content_length == -1) { // If END_STREAM flag is set to HEADERS frame, we are sure that // content-length is 0. req.fs.content_length = 0; } downstream->inspect_http2_request(); downstream->set_request_state(DownstreamState::HEADER_COMPLETE); if (config->http.require_http_scheme && !http::check_http_scheme(req.scheme, handler_->get_ssl() != nullptr)) { if (error_reply(downstream, 400) != 0) { return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } return 0; } #ifdef HAVE_MRUBY auto worker = handler_->get_worker(); auto mruby_ctx = worker->get_mruby_context(); if (mruby_ctx->run_on_request_proc(downstream) != 0) { if (error_reply(downstream, 500) != 0) { return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } return 0; } #endif // defined(HAVE_MRUBY) if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { downstream->disable_upstream_rtimer(); downstream->set_request_state(DownstreamState::MSG_COMPLETE); } if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { return 0; } start_downstream(downstream); return 0; } void Http2Upstream::start_downstream(Downstream *downstream) { if (downstream_queue_.can_activate(downstream->request().authority)) { initiate_downstream(downstream); return; } downstream_queue_.mark_blocked(downstream); } void Http2Upstream::initiate_downstream(Downstream *downstream) { int rv; #ifdef HAVE_MRUBY DownstreamConnection *dconn_ptr; #endif // defined(HAVE_MRUBY) for (;;) { auto dconn = handler_->get_downstream_connection(rv, downstream); if (!dconn) { if (rv == SHRPX_ERR_TLS_REQUIRED) { rv = redirect_to_https(downstream); } else { rv = error_reply(downstream, 502); } if (rv != 0) { rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); } downstream->set_request_state(DownstreamState::CONNECT_FAIL); downstream_queue_.mark_failure(downstream); return; } #ifdef HAVE_MRUBY dconn_ptr = dconn.get(); #endif // defined(HAVE_MRUBY) rv = downstream->attach_downstream_connection(std::move(dconn)); if (rv == 0) { break; } } #ifdef HAVE_MRUBY const auto &group = dconn_ptr->get_downstream_addr_group(); if (group) { const auto &mruby_ctx = group->shared_addr->mruby_ctx; if (mruby_ctx->run_on_request_proc(downstream) != 0) { if (error_reply(downstream, 500) != 0) { rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); } downstream_queue_.mark_failure(downstream); return; } if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { return; } } #endif // defined(HAVE_MRUBY) rv = downstream->push_request_headers(); if (rv != 0) { if (error_reply(downstream, 502) != 0) { rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); } downstream_queue_.mark_failure(downstream); return; } downstream_queue_.mark_active(downstream); auto &req = downstream->request(); if (!req.http2_expect_body) { rv = downstream->end_upload_data(); if (rv != 0) { rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); } } return; } namespace { int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { if (get_config()->http2.upstream.debug.frame_debug) { verbose_on_frame_recv_callback(session, frame, user_data); } auto upstream = static_cast(user_data); auto handler = upstream->get_client_handler(); switch (frame->hd.type) { case NGHTTP2_DATA: { auto downstream = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); if (!downstream) { return 0; } if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { downstream->disable_upstream_rtimer(); if (downstream->end_upload_data() != 0) { if (downstream->get_response_state() != DownstreamState::MSG_COMPLETE) { upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); } } downstream->set_request_state(DownstreamState::MSG_COMPLETE); } return 0; } case NGHTTP2_HEADERS: { auto downstream = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); if (!downstream) { return 0; } if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { downstream->reset_upstream_rtimer(); handler->stop_read_timer(); return upstream->on_request_headers(downstream, frame); } if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { downstream->disable_upstream_rtimer(); if (downstream->end_upload_data() != 0) { if (downstream->get_response_state() != DownstreamState::MSG_COMPLETE) { upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); } } downstream->set_request_state(DownstreamState::MSG_COMPLETE); } return 0; } case NGHTTP2_SETTINGS: if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { return 0; } upstream->stop_settings_timer(); return 0; case NGHTTP2_GOAWAY: if (LOG_ENABLED(INFO)) { auto debug_data = util::ascii_dump(frame->goaway.opaque_data, frame->goaway.opaque_data_len); ULOG(INFO, upstream) << "GOAWAY received: last-stream-id=" << frame->goaway.last_stream_id << ", error_code=" << frame->goaway.error_code << ", debug_data=" << debug_data; } return 0; default: return 0; } } } // namespace namespace { int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data) { auto upstream = static_cast(user_data); auto downstream = static_cast( nghttp2_session_get_stream_user_data(session, stream_id)); if (!downstream) { if (upstream->consume(stream_id, len) != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } return 0; } downstream->reset_upstream_rtimer(); if (downstream->push_upload_data_chunk(data, len) != 0) { if (downstream->get_response_state() != DownstreamState::MSG_COMPLETE) { upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); } if (upstream->consume(stream_id, len) != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } return 0; } return 0; } } // namespace namespace { int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { if (get_config()->http2.upstream.debug.frame_debug) { verbose_on_frame_send_callback(session, frame, user_data); } auto upstream = static_cast(user_data); auto handler = upstream->get_client_handler(); switch (frame->hd.type) { case NGHTTP2_DATA: case NGHTTP2_HEADERS: { if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { return 0; } // RST_STREAM if request is still incomplete. auto stream_id = frame->hd.stream_id; auto downstream = static_cast( nghttp2_session_get_stream_user_data(session, stream_id)); if (!downstream) { return 0; } // For tunneling, issue RST_STREAM to finish the stream. if (downstream->get_upgraded() || nghttp2_session_get_stream_remote_close(session, stream_id) == 0) { if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "Send RST_STREAM to " << (downstream->get_upgraded() ? "tunneled " : "") << "stream stream_id=" << downstream->get_stream_id() << " to finish off incomplete request"; } upstream->rst_stream(downstream, NGHTTP2_NO_ERROR); } return 0; } case NGHTTP2_SETTINGS: if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { upstream->start_settings_timer(); } return 0; case NGHTTP2_PUSH_PROMISE: { auto promised_stream_id = frame->push_promise.promised_stream_id; if (nghttp2_session_get_stream_user_data(session, promised_stream_id)) { // In case of push from backend, downstream object was already // created. return 0; } auto promised_downstream = std::make_unique( upstream, handler->get_mcpool(), promised_stream_id); auto &req = promised_downstream->request(); // As long as we use nghttp2_session_mem_send2(), setting stream // user data here should not fail. This is because this callback // is called just after frame was serialized. So no worries about // hanging Downstream. nghttp2_session_set_stream_user_data(session, promised_stream_id, promised_downstream.get()); promised_downstream->set_assoc_stream_id(frame->hd.stream_id); promised_downstream->disable_upstream_rtimer(); req.http_major = 2; req.http_minor = 0; req.fs.content_length = 0; req.http2_expect_body = false; auto &promised_balloc = promised_downstream->get_block_allocator(); for (size_t i = 0; i < frame->push_promise.nvlen; ++i) { auto &nv = frame->push_promise.nva[i]; auto name = make_string_ref(promised_balloc, as_string_view(nv.name, nv.namelen)); auto value = make_string_ref(promised_balloc, as_string_view(nv.value, nv.valuelen)); auto token = http2::lookup_token(name); switch (token) { case http2::HD__METHOD: req.method = http2::lookup_method_token(value); break; case http2::HD__SCHEME: req.scheme = value; break; case http2::HD__AUTHORITY: req.authority = value; break; case http2::HD__PATH: req.path = http2::rewrite_clean_path(promised_balloc, value); break; } req.fs.add_header_token(name, value, nv.flags & NGHTTP2_NV_FLAG_NO_INDEX, token); } promised_downstream->inspect_http2_request(); promised_downstream->set_request_state(DownstreamState::MSG_COMPLETE); // a bit weird but start_downstream() expects that given // downstream is in pending queue. auto ptr = promised_downstream.get(); upstream->add_pending_downstream(std::move(promised_downstream)); #ifdef HAVE_MRUBY auto worker = handler->get_worker(); auto mruby_ctx = worker->get_mruby_context(); if (mruby_ctx->run_on_request_proc(ptr) != 0) { if (upstream->error_reply(ptr, 500) != 0) { upstream->rst_stream(ptr, NGHTTP2_INTERNAL_ERROR); return 0; } return 0; } #endif // defined(HAVE_MRUBY) upstream->start_downstream(ptr); return 0; } case NGHTTP2_GOAWAY: if (LOG_ENABLED(INFO)) { auto debug_data = util::ascii_dump(frame->goaway.opaque_data, frame->goaway.opaque_data_len); ULOG(INFO, upstream) << "Sending GOAWAY: last-stream-id=" << frame->goaway.last_stream_id << ", error_code=" << frame->goaway.error_code << ", debug_data=" << debug_data; } return 0; default: return 0; } } } // namespace namespace { int on_frame_not_send_callback(nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, void *user_data) { auto upstream = static_cast(user_data); if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "Failed to send control frame type=" << static_cast(frame->hd.type) << ", lib_error_code=" << lib_error_code << ":" << nghttp2_strerror(lib_error_code); } if (frame->hd.type == NGHTTP2_HEADERS && lib_error_code != NGHTTP2_ERR_STREAM_CLOSED && lib_error_code != NGHTTP2_ERR_STREAM_CLOSING) { // To avoid stream hanging around, issue RST_STREAM. auto downstream = static_cast( nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); if (downstream) { upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); } } return 0; } } // namespace namespace { constexpr auto PADDING = std::array{}; } // namespace namespace { int send_data_callback(nghttp2_session *session, nghttp2_frame *frame, const uint8_t *framehd, size_t length, nghttp2_data_source *source, void *user_data) { auto downstream = static_cast(source->ptr); auto upstream = static_cast(downstream->get_upstream()); auto body = downstream->get_response_buf(); auto wb = upstream->get_response_buf(); size_t padlen = 0; wb->append(framehd, 9); if (frame->data.padlen > 0) { padlen = frame->data.padlen - 1; wb->append(static_cast(padlen)); } body->remove(*wb, length); wb->append(PADDING.data(), padlen); if (body->rleft() == 0) { downstream->disable_upstream_wtimer(); } else { downstream->reset_upstream_wtimer(); } if (length > 0 && downstream->resume_read(SHRPX_NO_BUFFER, length) != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } // We have to add length here, so that we can log this amount of // data transferred. downstream->response_sent_body_length += length; auto max_buffer_size = upstream->get_max_buffer_size(); return wb->rleft() >= max_buffer_size ? NGHTTP2_ERR_PAUSE : 0; } } // namespace namespace { uint32_t infer_upstream_rst_stream_error_code(uint32_t downstream_error_code) { // NGHTTP2_REFUSED_STREAM is important because it tells upstream // client to retry. switch (downstream_error_code) { case NGHTTP2_NO_ERROR: case NGHTTP2_REFUSED_STREAM: return downstream_error_code; default: return NGHTTP2_INTERNAL_ERROR; } } } // namespace namespace { void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto upstream = static_cast(w->data); auto handler = upstream->get_client_handler(); ULOG(INFO, upstream) << "SETTINGS timeout"; if (upstream->terminate_session(NGHTTP2_SETTINGS_TIMEOUT) != 0) { delete handler; return; } handler->signal_write(); } } // namespace namespace { void shutdown_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto upstream = static_cast(w->data); auto handler = upstream->get_client_handler(); upstream->submit_goaway(); handler->signal_write(); } } // namespace namespace { void prepare_cb(struct ev_loop *loop, ev_prepare *w, int revents) { auto upstream = static_cast(w->data); upstream->check_shutdown(); } } // namespace void Http2Upstream::submit_goaway() { auto last_stream_id = nghttp2_session_get_last_proc_stream_id(session_); nghttp2_submit_goaway(session_, NGHTTP2_FLAG_NONE, last_stream_id, NGHTTP2_NO_ERROR, nullptr, 0); } void Http2Upstream::check_shutdown() { auto worker = handler_->get_worker(); if (!worker->get_graceful_shutdown()) { return; } ev_prepare_stop(handler_->get_loop(), &prep_); start_graceful_shutdown(); } void Http2Upstream::start_graceful_shutdown() { int rv; if (ev_is_active(&shutdown_timer_)) { return; } rv = nghttp2_submit_shutdown_notice(session_); if (rv != 0) { ULOG(FATAL, this) << "nghttp2_submit_shutdown_notice() failed: " << nghttp2_strerror(rv); return; } handler_->signal_write(); ev_timer_start(handler_->get_loop(), &shutdown_timer_); } nghttp2_session_callbacks *create_http2_upstream_callbacks() { int rv; nghttp2_session_callbacks *callbacks; rv = nghttp2_session_callbacks_new(&callbacks); if (rv != 0) { return nullptr; } nghttp2_session_callbacks_set_on_stream_close_callback( callbacks, on_stream_close_callback); nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, on_frame_recv_callback); nghttp2_session_callbacks_set_on_data_chunk_recv_callback( callbacks, on_data_chunk_recv_callback); nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, on_frame_send_callback); nghttp2_session_callbacks_set_on_frame_not_send_callback( callbacks, on_frame_not_send_callback); nghttp2_session_callbacks_set_on_header_callback2(callbacks, on_header_callback2); nghttp2_session_callbacks_set_on_begin_headers_callback( callbacks, on_begin_headers_callback); nghttp2_session_callbacks_set_send_data_callback(callbacks, send_data_callback); auto config = get_config(); if (config->padding) { nghttp2_session_callbacks_set_select_padding_callback2( callbacks, http::select_padding_callback); } if (config->http2.upstream.debug.frame_debug) { nghttp2_session_callbacks_set_error_callback2(callbacks, verbose_error_callback); } nghttp2_session_callbacks_set_rand_callback(callbacks, util::secure_random); return callbacks; } namespace { size_t downstream_queue_size(Worker *worker) { auto &downstreamconf = *worker->get_downstream_config(); if (get_config()->http2_proxy) { return downstreamconf.connections_per_host; } return downstreamconf.connections_per_frontend; } } // namespace Http2Upstream::Http2Upstream(ClientHandler *handler) : wb_(handler->get_worker()->get_mcpool()), downstream_queue_(downstream_queue_size(handler->get_worker()), !get_config()->http2_proxy), handler_(handler), session_(nullptr), max_buffer_size_(MAX_BUFFER_SIZE), num_requests_(0) { int rv; auto config = get_config(); auto &http2conf = config->http2; auto faddr = handler_->get_upstream_addr(); rv = nghttp2_session_server_new2(&session_, http2conf.upstream.callbacks, this, faddr->alt_mode != UpstreamAltMode::NONE ? http2conf.upstream.alt_mode_option : http2conf.upstream.option); assert(rv == 0); flow_control_ = true; // TODO Maybe call from outside? std::array entry; size_t nentry = 3; entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; entry[0].value = static_cast(http2conf.upstream.max_concurrent_streams); entry[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; if (faddr->alt_mode != UpstreamAltMode::NONE) { entry[1].value = (1u << 31) - 1; } else { entry[1].value = as_unsigned(http2conf.upstream.window_size); } entry[2].settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES; entry[2].value = 1; if (!config->http2_proxy) { entry[nentry].settings_id = NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL; entry[nentry].value = 1; ++nentry; } if (http2conf.upstream.decoder_dynamic_table_size != NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) { entry[nentry].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; entry[nentry].value = static_cast(http2conf.upstream.decoder_dynamic_table_size); ++nentry; } rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), nentry); if (rv != 0) { ULOG(ERROR, this) << "nghttp2_submit_settings() returned error: " << nghttp2_strerror(rv); } auto window_size = faddr->alt_mode != UpstreamAltMode::NONE ? std::numeric_limits::max() : http2conf.upstream.optimize_window_size ? std::min(http2conf.upstream.connection_window_size, NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE) : http2conf.upstream.connection_window_size; rv = nghttp2_session_set_local_window_size(session_, NGHTTP2_FLAG_NONE, 0, window_size); if (rv != 0) { ULOG(ERROR, this) << "nghttp2_session_set_local_window_size() returned error: " << nghttp2_strerror(rv); } // We wait for SETTINGS ACK at least 10 seconds. ev_timer_init(&settings_timer_, settings_timeout_cb, http2conf.upstream.timeout.settings, 0.); settings_timer_.data = this; // timer for 2nd GOAWAY. HTTP/2 spec recommend 1 RTT. We wait for // 2 seconds. ev_timer_init(&shutdown_timer_, shutdown_timeout_cb, 2., 0); shutdown_timer_.data = this; ev_prepare_init(&prep_, prepare_cb); prep_.data = this; ev_prepare_start(handler_->get_loop(), &prep_); #if defined(TCP_INFO) && defined(TCP_NOTSENT_LOWAT) if (http2conf.upstream.optimize_write_buffer_size) { auto conn = handler_->get_connection(); conn->tls_dyn_rec_warmup_threshold = 0; uint32_t pollout_thres = 1; rv = setsockopt(conn->fd, IPPROTO_TCP, TCP_NOTSENT_LOWAT, &pollout_thres, static_cast(sizeof(pollout_thres))); if (rv != 0) { if (LOG_ENABLED(INFO)) { auto error = errno; LOG(INFO) << "setsockopt(TCP_NOTSENT_LOWAT, " << pollout_thres << ") failed: errno=" << error; } } } #endif // defined(TCP_INFO) && defined(TCP_NOTSENT_LOWAT) handler_->reset_upstream_read_timeout( config->conn.upstream.timeout.http2_idle); handler_->signal_write(); } Http2Upstream::~Http2Upstream() { nghttp2_session_del(session_); ev_prepare_stop(handler_->get_loop(), &prep_); ev_timer_stop(handler_->get_loop(), &shutdown_timer_); ev_timer_stop(handler_->get_loop(), &settings_timer_); } int Http2Upstream::on_read() { auto rb = handler_->get_rb(); auto rlimit = handler_->get_rlimit(); if (rb->rleft()) { auto rv = nghttp2_session_mem_recv2(session_, rb->pos(), rb->rleft()); if (rv < 0) { if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) { ULOG(ERROR, this) << "nghttp2_session_mem_recv2() returned error: " << nghttp2_strerror(static_cast(rv)); } return -1; } // nghttp2_session_mem_recv2 should consume all input bytes on // success. assert(static_cast(rv) == rb->rleft()); rb->reset(); rlimit->startw(); } if (nghttp2_session_want_read(session_) == 0 && nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) { if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "No more read/write for this HTTP2 session"; } return -1; } handler_->signal_write(); return 0; } // After this function call, downstream may be deleted. int Http2Upstream::on_write() { int rv; auto config = get_config(); auto &http2conf = config->http2; if ((http2conf.upstream.optimize_write_buffer_size || http2conf.upstream.optimize_window_size) && handler_->get_ssl()) { auto conn = handler_->get_connection(); TCPHint hint; rv = conn->get_tcp_hint(&hint); if (rv == 0) { if (http2conf.upstream.optimize_write_buffer_size) { max_buffer_size_ = std::min(MAX_BUFFER_SIZE, hint.write_buffer_size); } if (http2conf.upstream.optimize_window_size) { auto faddr = handler_->get_upstream_addr(); if (faddr->alt_mode == UpstreamAltMode::NONE) { auto window_size = std::min(http2conf.upstream.connection_window_size, static_cast(hint.rwin * 2)); rv = nghttp2_session_set_local_window_size( session_, NGHTTP2_FLAG_NONE, 0, window_size); if (rv != 0) { if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "nghttp2_session_set_local_window_size() with window_size=" << window_size << " failed: " << nghttp2_strerror(rv); } } } } } } for (;;) { if (wb_.rleft() >= max_buffer_size_) { return 0; } const uint8_t *data; auto datalen = nghttp2_session_mem_send2(session_, &data); if (datalen < 0) { ULOG(ERROR, this) << "nghttp2_session_mem_send2() returned error: " << nghttp2_strerror(static_cast(datalen)); return -1; } if (datalen == 0) { break; } wb_.append(data, as_unsigned(datalen)); } if (nghttp2_session_want_read(session_) == 0 && nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) { if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "No more read/write for this HTTP2 session"; } return -1; } return 0; } ClientHandler *Http2Upstream::get_client_handler() const { return handler_; } int Http2Upstream::downstream_read(DownstreamConnection *dconn) { auto downstream = dconn->get_downstream(); if (downstream->get_response_state() == DownstreamState::MSG_RESET) { // The downstream stream was reset (canceled). In this case, // RST_STREAM to the upstream and delete downstream connection // here. Deleting downstream will be taken place at // on_stream_close_callback. rst_stream(downstream, infer_upstream_rst_stream_error_code( downstream->get_response_rst_stream_error_code())); downstream->pop_downstream_connection(); // dconn was deleted dconn = nullptr; } else if (downstream->get_response_state() == DownstreamState::MSG_BAD_HEADER) { if (error_reply(downstream, 502) != 0) { return -1; } downstream->pop_downstream_connection(); // dconn was deleted dconn = nullptr; } else { auto rv = downstream->on_read(); if (rv == SHRPX_ERR_EOF) { if (downstream->get_request_header_sent()) { return downstream_eof(dconn); } return SHRPX_ERR_RETRY; } if (rv == SHRPX_ERR_DCONN_CANCELED) { downstream->pop_downstream_connection(); handler_->signal_write(); return 0; } if (rv != 0) { if (rv != SHRPX_ERR_NETWORK) { if (LOG_ENABLED(INFO)) { DCLOG(INFO, dconn) << "HTTP parser failure"; } } return downstream_error(dconn, Downstream::EVENT_ERROR); } if (downstream->can_detach_downstream_connection()) { // Keep-alive downstream->detach_downstream_connection(); } } handler_->signal_write(); // At this point, downstream may be deleted. return 0; } int Http2Upstream::downstream_write(DownstreamConnection *dconn) { int rv; rv = dconn->on_write(); if (rv == SHRPX_ERR_NETWORK) { return downstream_error(dconn, Downstream::EVENT_ERROR); } if (rv != 0) { return rv; } return 0; } int Http2Upstream::downstream_eof(DownstreamConnection *dconn) { auto downstream = dconn->get_downstream(); if (LOG_ENABLED(INFO)) { DCLOG(INFO, dconn) << "EOF. stream_id=" << downstream->get_stream_id(); } // Delete downstream connection. If we don't delete it here, it will // be pooled in on_stream_close_callback. downstream->pop_downstream_connection(); // dconn was deleted dconn = nullptr; // downstream will be deleted in on_stream_close_callback. if (downstream->get_response_state() == DownstreamState::HEADER_COMPLETE) { // Server may indicate the end of the request by EOF if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "Downstream body was ended by EOF"; } downstream->set_response_state(DownstreamState::MSG_COMPLETE); // For tunneled connection, MSG_COMPLETE signals // downstream_data_read_callback to send RST_STREAM after pending // response body is sent. This is needed to ensure that RST_STREAM // is sent after all pending data are sent. on_downstream_body_complete(downstream); } else if (downstream->get_response_state() != DownstreamState::MSG_COMPLETE) { // If stream was not closed, then we set MSG_COMPLETE and let // on_stream_close_callback delete downstream. if (error_reply(downstream, 502) != 0) { return -1; } } handler_->signal_write(); // At this point, downstream may be deleted. return 0; } int Http2Upstream::downstream_error(DownstreamConnection *dconn, int events) { auto downstream = dconn->get_downstream(); if (LOG_ENABLED(INFO)) { if (events & Downstream::EVENT_ERROR) { DCLOG(INFO, dconn) << "Downstream network/general error"; } else { DCLOG(INFO, dconn) << "Timeout"; } if (downstream->get_upgraded()) { DCLOG(INFO, dconn) << "Note: this is tunnel connection"; } } // Delete downstream connection. If we don't delete it here, it will // be pooled in on_stream_close_callback. downstream->pop_downstream_connection(); // dconn was deleted dconn = nullptr; if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { // For SSL tunneling, we issue RST_STREAM. For other types of // stream, we don't have to do anything since response was // complete. if (downstream->get_upgraded()) { rst_stream(downstream, NGHTTP2_NO_ERROR); } } else { if (downstream->get_response_state() == DownstreamState::HEADER_COMPLETE) { if (downstream->get_upgraded()) { on_downstream_body_complete(downstream); } else { rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); } } else { unsigned int status; if (events & Downstream::EVENT_TIMEOUT) { if (downstream->get_request_header_sent()) { status = 504; } else { status = 408; } } else { status = 502; } if (error_reply(downstream, status) != 0) { return -1; } } downstream->set_response_state(DownstreamState::MSG_COMPLETE); } handler_->signal_write(); // At this point, downstream may be deleted. return 0; } int Http2Upstream::rst_stream(Downstream *downstream, uint32_t error_code) { if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "RST_STREAM stream_id=" << downstream->get_stream_id() << " with error_code=" << error_code; } int rv; rv = nghttp2_submit_rst_stream( session_, NGHTTP2_FLAG_NONE, static_cast(downstream->get_stream_id()), error_code); if (rv < NGHTTP2_ERR_FATAL) { ULOG(FATAL, this) << "nghttp2_submit_rst_stream() failed: " << nghttp2_strerror(rv); return -1; } return 0; } int Http2Upstream::terminate_session(uint32_t error_code) { int rv; rv = nghttp2_session_terminate_session(session_, error_code); if (rv != 0) { return -1; } return 0; } namespace { nghttp2_ssize downstream_data_read_callback(nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *user_data) { int rv; auto downstream = static_cast(source->ptr); auto body = downstream->get_response_buf(); assert(body); auto upstream = static_cast(user_data); const auto &resp = downstream->response(); auto nread = std::min(body->rleft(), length); auto max_buffer_size = upstream->get_max_buffer_size(); auto buffer = upstream->get_response_buf(); if (max_buffer_size < std::min(nread, static_cast(256)) + 9 + buffer->rleft()) { if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "Buffer is almost full. Skip write DATA"; } return NGHTTP2_ERR_PAUSE; } nread = std::min(nread, max_buffer_size - 9 - buffer->rleft()); auto body_empty = body->rleft() == nread; *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY; if (body_empty && downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { *data_flags |= NGHTTP2_DATA_FLAG_EOF; if (!downstream->get_upgraded()) { const auto &trailers = resp.fs.trailers(); if (!trailers.empty()) { std::vector nva; nva.reserve(trailers.size()); http2::copy_headers_to_nva_nocopy(nva, trailers, http2::HDOP_STRIP_ALL); if (!nva.empty()) { rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size()); if (rv != 0) { if (nghttp2_is_fatal(rv)) { return NGHTTP2_ERR_CALLBACK_FAILURE; } } else { *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; } } } } } if (nread == 0 && ((*data_flags) & NGHTTP2_DATA_FLAG_EOF) == 0) { downstream->disable_upstream_wtimer(); return NGHTTP2_ERR_DEFERRED; } return as_signed(nread); } } // namespace int Http2Upstream::send_reply(Downstream *downstream, const uint8_t *body, size_t bodylen) { int rv; nghttp2_data_provider2 data_prd, *data_prd_ptr = nullptr; const auto &req = downstream->request(); if (req.method != HTTP_HEAD && bodylen) { data_prd.source.ptr = downstream; data_prd.read_callback = downstream_data_read_callback; data_prd_ptr = &data_prd; auto buf = downstream->get_response_buf(); buf->append(body, bodylen); } const auto &resp = downstream->response(); auto config = get_config(); auto &httpconf = config->http; auto &balloc = downstream->get_block_allocator(); const auto &headers = resp.fs.headers(); auto nva = std::vector(); // 2 for :status and server nva.reserve(2 + headers.size() + httpconf.add_response_headers.size()); auto response_status = http2::stringify_status(balloc, resp.http_status); nva.push_back(http2::make_field(":status"sv, response_status)); for (auto &kv : headers) { if (kv.name.empty() || kv.name[0] == ':') { continue; } switch (kv.token) { case http2::HD_CONNECTION: case http2::HD_KEEP_ALIVE: case http2::HD_PROXY_CONNECTION: case http2::HD_TE: case http2::HD_TRANSFER_ENCODING: case http2::HD_UPGRADE: continue; } nva.push_back( http2::make_field(kv.name, kv.value, http2::no_index(kv.no_index))); } if (!resp.fs.header(http2::HD_SERVER)) { nva.push_back(http2::make_field("server"sv, config->http.server_name)); } for (auto &p : httpconf.add_response_headers) { nva.push_back(http2::make_field(p.name, p.value)); } rv = nghttp2_submit_response2( session_, static_cast(downstream->get_stream_id()), nva.data(), nva.size(), data_prd_ptr); if (nghttp2_is_fatal(rv)) { ULOG(FATAL, this) << "nghttp2_submit_response2() failed: " << nghttp2_strerror(rv); return -1; } downstream->set_response_state(DownstreamState::MSG_COMPLETE); if (data_prd_ptr) { downstream->reset_upstream_wtimer(); } return 0; } int Http2Upstream::error_reply(Downstream *downstream, unsigned int status_code) { int rv; auto &resp = downstream->response(); auto &balloc = downstream->get_block_allocator(); auto html = http::create_error_html(balloc, status_code); resp.http_status = status_code; nghttp2_data_provider2 data_prd, *data_prd_ptr = nullptr; const auto &req = downstream->request(); if (req.method != HTTP_HEAD) { data_prd.source.ptr = downstream; data_prd.read_callback = downstream_data_read_callback; data_prd_ptr = &data_prd; auto body = downstream->get_response_buf(); body->append(html); } downstream->set_response_state(DownstreamState::MSG_COMPLETE); auto lgconf = log_config(); lgconf->update_tstamp(std::chrono::system_clock::now()); auto response_status = http2::stringify_status(balloc, status_code); auto content_length = util::make_string_ref_uint(balloc, html.size()); auto date = make_string_ref(balloc, lgconf->tstamp->time_http); auto nva = std::to_array( {http2::make_field(":status"sv, response_status), http2::make_field("content-type"sv, "text/html; charset=UTF-8"sv), http2::make_field("server"sv, get_config()->http.server_name), http2::make_field("content-length"sv, content_length), http2::make_field("date"sv, date)}); rv = nghttp2_submit_response2( session_, static_cast(downstream->get_stream_id()), nva.data(), nva.size(), data_prd_ptr); if (rv < NGHTTP2_ERR_FATAL) { ULOG(FATAL, this) << "nghttp2_submit_response2() failed: " << nghttp2_strerror(rv); return -1; } downstream->reset_upstream_wtimer(); return 0; } void Http2Upstream::add_pending_downstream( std::unique_ptr downstream) { downstream_queue_.add_pending(std::move(downstream)); } void Http2Upstream::remove_downstream(Downstream *downstream) { if (downstream->accesslog_ready()) { handler_->write_accesslog(downstream); } nghttp2_session_set_stream_user_data( session_, static_cast(downstream->get_stream_id()), nullptr); auto next_downstream = downstream_queue_.remove_and_get_blocked(downstream); if (next_downstream) { initiate_downstream(next_downstream); } if (downstream_queue_.get_downstreams() == nullptr) { // There is no downstream at the moment. Start idle timer now. auto config = get_config(); auto &upstreamconf = config->conn.upstream; handler_->reset_upstream_read_timeout(upstreamconf.timeout.http2_idle); } } // WARNING: Never call directly or indirectly nghttp2_session_send or // nghttp2_session_recv. These calls may delete downstream. int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { int rv; const auto &req = downstream->request(); auto &resp = downstream->response(); auto &balloc = downstream->get_block_allocator(); if (LOG_ENABLED(INFO)) { if (downstream->get_non_final_response()) { DLOG(INFO, downstream) << "HTTP non-final response header"; } else { DLOG(INFO, downstream) << "HTTP response header completed"; } } auto config = get_config(); auto &httpconf = config->http; if (!config->http2_proxy && !httpconf.no_location_rewrite) { downstream->rewrite_location_response_header(req.scheme); } #ifdef HAVE_MRUBY if (!downstream->get_non_final_response()) { auto dconn = downstream->get_downstream_connection(); const auto &group = dconn->get_downstream_addr_group(); if (group) { const auto &dmruby_ctx = group->shared_addr->mruby_ctx; if (dmruby_ctx->run_on_response_proc(downstream) != 0) { if (error_reply(downstream, 500) != 0) { return -1; } // Returning -1 will signal deletion of dconn. return -1; } if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { return -1; } } auto worker = handler_->get_worker(); auto mruby_ctx = worker->get_mruby_context(); if (mruby_ctx->run_on_response_proc(downstream) != 0) { if (error_reply(downstream, 500) != 0) { return -1; } // Returning -1 will signal deletion of dconn. return -1; } if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { return -1; } } #endif // defined(HAVE_MRUBY) auto &http2conf = config->http2; // We need some conditions that must be fulfilled to initiate server // push. // // * Server push is disabled for http2 proxy or client proxy, since // incoming headers are mixed origins. We don't know how to // reliably determine the authority yet. // // * We need non-final response or 200 response code for associated // resource. This is too restrictive, we will review this later. // // * We requires GET or POST for associated resource. Probably we // don't want to push for HEAD request. Not sure other methods // are also eligible for push. if (!http2conf.no_server_push && nghttp2_session_get_remote_settings(session_, NGHTTP2_SETTINGS_ENABLE_PUSH) == 1 && !config->http2_proxy && (downstream->get_stream_id() % 2) && resp.fs.header(http2::HD_LINK) && (downstream->get_non_final_response() || resp.http_status == 200) && (req.method == HTTP_GET || req.method == HTTP_POST)) { if (prepare_push_promise(downstream) != 0) { // Continue to send response even if push was failed. } } auto nva = std::vector(); // 6 means :status and possible server, via, x-http2-push, alt-svc, // and set-cookie (for affinity cookie) header field. nva.reserve(resp.fs.headers().size() + 6 + httpconf.add_response_headers.size()); if (downstream->get_non_final_response()) { auto response_status = http2::stringify_status(balloc, resp.http_status); nva.push_back(http2::make_field(":status"sv, response_status)); http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers(), http2::HDOP_STRIP_ALL); if (LOG_ENABLED(INFO)) { log_response_headers(downstream, nva); } rv = nghttp2_submit_headers(session_, NGHTTP2_FLAG_NONE, static_cast(downstream->get_stream_id()), nullptr, nva.data(), nva.size(), nullptr); resp.fs.clear_headers(); if (rv != 0) { ULOG(FATAL, this) << "nghttp2_submit_headers() failed"; return -1; } return 0; } auto striphd_flags = static_cast(http2::HDOP_STRIP_ALL & ~http2::HDOP_STRIP_VIA); std::string_view response_status; if (req.connect_proto == ConnectProto::WEBSOCKET && resp.http_status == 101) { response_status = http2::stringify_status(balloc, 200); striphd_flags |= http2::HDOP_STRIP_SEC_WEBSOCKET_ACCEPT; } else { response_status = http2::stringify_status(balloc, resp.http_status); } nva.push_back(http2::make_field(":status"sv, response_status)); http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers(), striphd_flags); if (!config->http2_proxy && !httpconf.no_server_rewrite) { nva.push_back(http2::make_field("server"sv, httpconf.server_name)); } else { auto server = resp.fs.header(http2::HD_SERVER); if (server) { nva.push_back(http2::make_field("server"sv, (*server).value)); } } if (!req.regular_connect_method() || !downstream->get_upgraded()) { auto affinity_cookie = downstream->get_affinity_cookie_to_send(); if (affinity_cookie) { auto dconn = downstream->get_downstream_connection(); assert(dconn); auto &group = dconn->get_downstream_addr_group(); auto &shared_addr = group->shared_addr; auto &cookieconf = shared_addr->affinity.cookie; auto secure = http::require_cookie_secure_attribute(cookieconf.secure, req.scheme); auto cookie_str = http::create_affinity_cookie( balloc, cookieconf.name, affinity_cookie, cookieconf.path, secure); nva.push_back(http2::make_field("set-cookie"sv, cookie_str)); } } if (!resp.fs.header(http2::HD_ALT_SVC)) { // We won't change or alter alt-svc from backend for now if (!httpconf.http2_altsvc_header_value.empty()) { nva.push_back( http2::make_field("alt-svc"sv, httpconf.http2_altsvc_header_value)); } } auto via = resp.fs.header(http2::HD_VIA); if (httpconf.no_via) { if (via) { nva.push_back(http2::make_field("via"sv, (*via).value)); } } else { // we don't create more than 16 bytes in // http::create_via_header_value. size_t len = 16; if (via) { len += via->value.size() + 2; } auto iov = make_byte_ref(balloc, len + 1); auto p = std::ranges::begin(iov); if (via) { p = std::ranges::copy(via->value, p).out; p = std::ranges::copy(", "sv, p).out; } p = http::create_via_header_value(p, resp.http_major, resp.http_minor); *p = '\0'; nva.push_back( http2::make_field("via"sv, as_string_view(std::ranges::begin(iov), p))); } for (auto &p : httpconf.add_response_headers) { nva.push_back(http2::make_field(p.name, p.value)); } if (downstream->get_stream_id() % 2 == 0) { // This header field is basically for human on client side to // figure out that the resource is pushed. nva.push_back(http2::make_field("x-http2-push"sv, "1"sv)); } if (LOG_ENABLED(INFO)) { log_response_headers(downstream, nva); } if (http2conf.upstream.debug.dump.response_header) { http2::dump_nv(http2conf.upstream.debug.dump.response_header, nva.data(), nva.size()); } auto priority = resp.fs.header(http2::HD_PRIORITY); if (priority) { nghttp2_extpri extpri; if (nghttp2_session_get_extpri_stream_priority( session_, &extpri, static_cast(downstream->get_stream_id())) == 0 && nghttp2_extpri_parse_priority( &extpri, reinterpret_cast(priority->value.data()), priority->value.size()) == 0) { rv = nghttp2_session_change_extpri_stream_priority( session_, static_cast(downstream->get_stream_id()), &extpri, /* ignore_client_signal = */ 1); if (rv != 0) { ULOG(ERROR, this) << "nghttp2_session_change_extpri_stream_priority: " << nghttp2_strerror(rv); } } } nghttp2_data_provider2 data_prd; data_prd.source.ptr = downstream; data_prd.read_callback = downstream_data_read_callback; nghttp2_data_provider2 *data_prdptr; if (downstream->expect_response_body() || downstream->expect_response_trailer()) { data_prdptr = &data_prd; } else { data_prdptr = nullptr; } rv = nghttp2_submit_response2( session_, static_cast(downstream->get_stream_id()), nva.data(), nva.size(), data_prdptr); if (rv != 0) { ULOG(FATAL, this) << "nghttp2_submit_response2() failed"; return -1; } if (data_prdptr) { downstream->reset_upstream_wtimer(); } return 0; } // WARNING: Never call directly or indirectly nghttp2_session_send or // nghttp2_session_recv. These calls may delete downstream. int Http2Upstream::on_downstream_body(Downstream *downstream, const uint8_t *data, size_t len, bool flush) { auto body = downstream->get_response_buf(); body->append(data, len); if (flush) { nghttp2_session_resume_data( session_, static_cast(downstream->get_stream_id())); downstream->ensure_upstream_wtimer(); } return 0; } // WARNING: Never call directly or indirectly nghttp2_session_send or // nghttp2_session_recv. These calls may delete downstream. int Http2Upstream::on_downstream_body_complete(Downstream *downstream) { if (LOG_ENABLED(INFO)) { DLOG(INFO, downstream) << "HTTP response completed"; } auto &resp = downstream->response(); if (!downstream->validate_response_recv_body_length()) { rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR); resp.connection_close = true; return 0; } nghttp2_session_resume_data( session_, static_cast(downstream->get_stream_id())); downstream->ensure_upstream_wtimer(); return 0; } bool Http2Upstream::get_flow_control() const { return flow_control_; } void Http2Upstream::pause_read(IOCtrlReason reason) {} int Http2Upstream::resume_read(IOCtrlReason reason, Downstream *downstream, size_t consumed) { if (get_flow_control()) { if (consume(static_cast(downstream->get_stream_id()), consumed) != 0) { return -1; } auto &req = downstream->request(); req.consume(consumed); } handler_->signal_write(); return 0; } int Http2Upstream::on_downstream_abort_request(Downstream *downstream, unsigned int status_code) { int rv; rv = error_reply(downstream, status_code); if (rv != 0) { return -1; } handler_->signal_write(); return 0; } int Http2Upstream::on_downstream_abort_request_with_https_redirect( Downstream *downstream) { int rv; rv = redirect_to_https(downstream); if (rv != 0) { return -1; } handler_->signal_write(); return 0; } int Http2Upstream::redirect_to_https(Downstream *downstream) { auto &req = downstream->request(); if (req.regular_connect_method() || req.scheme != "http"sv) { return error_reply(downstream, 400); } auto authority = util::extract_host(req.authority); if (authority.empty()) { return error_reply(downstream, 400); } auto &balloc = downstream->get_block_allocator(); auto config = get_config(); auto &httpconf = config->http; std::string_view loc; if (httpconf.redirect_https_port == "443"sv) { loc = concat_string_ref(balloc, "https://"sv, authority, req.path); } else { loc = concat_string_ref(balloc, "https://"sv, authority, ":"sv, httpconf.redirect_https_port, req.path); } auto &resp = downstream->response(); resp.http_status = 308; resp.fs.add_header_token("location"sv, loc, false, http2::HD_LOCATION); return send_reply(downstream, nullptr, 0); } int Http2Upstream::consume(int32_t stream_id, size_t len) { int rv; auto faddr = handler_->get_upstream_addr(); if (faddr->alt_mode != UpstreamAltMode::NONE) { return 0; } rv = nghttp2_session_consume(session_, stream_id, len); if (rv != 0) { ULOG(WARN, this) << "nghttp2_session_consume() returned error: " << nghttp2_strerror(rv); return -1; } return 0; } void Http2Upstream::log_response_headers( Downstream *downstream, const std::vector &nva) const { std::stringstream ss; for (auto &nv : nva) { ss << TTY_HTTP_HD << as_string_view(nv.name, nv.namelen) << TTY_RST << ": " << as_string_view(nv.value, nv.valuelen) << "\n"; } ULOG(INFO, this) << "HTTP response headers. stream_id=" << downstream->get_stream_id() << "\n" << ss.str(); } int Http2Upstream::on_timeout(Downstream *downstream) { if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "Stream timeout stream_id=" << downstream->get_stream_id(); } rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); handler_->signal_write(); return 0; } void Http2Upstream::on_handler_delete() { for (auto d = downstream_queue_.get_downstreams(); d; d = d->dlnext) { if (d->get_dispatch_state() == DispatchState::ACTIVE && d->accesslog_ready()) { handler_->write_accesslog(d); } } } int Http2Upstream::on_downstream_reset(Downstream *downstream, bool no_retry) { int rv; if (downstream->get_dispatch_state() != DispatchState::ACTIVE) { // This is error condition when we failed push_request_headers() // in initiate_downstream(). Otherwise, we have // DispatchState::ACTIVE state, or we did not set // DownstreamConnection. downstream->pop_downstream_connection(); handler_->signal_write(); return 0; } if (!downstream->request_submission_ready()) { if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { // We have got all response body already. Send it off. downstream->pop_downstream_connection(); return 0; } // pushed stream is handled here rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); downstream->pop_downstream_connection(); handler_->signal_write(); return 0; } downstream->pop_downstream_connection(); downstream->add_retry(); std::unique_ptr dconn; rv = 0; if (no_retry || downstream->no_more_retry()) { goto fail; } // downstream connection is clean; we can retry with new // downstream connection. for (;;) { auto dconn = handler_->get_downstream_connection(rv, downstream); if (!dconn) { goto fail; } rv = downstream->attach_downstream_connection(std::move(dconn)); if (rv == 0) { break; } } rv = downstream->push_request_headers(); if (rv != 0) { goto fail; } return 0; fail: if (rv == SHRPX_ERR_TLS_REQUIRED) { rv = on_downstream_abort_request_with_https_redirect(downstream); } else { rv = on_downstream_abort_request(downstream, 502); } if (rv != 0) { rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); } downstream->pop_downstream_connection(); handler_->signal_write(); return 0; } int Http2Upstream::prepare_push_promise(Downstream *downstream) { int rv; const auto &req = downstream->request(); auto &resp = downstream->response(); auto base = http2::get_pure_path_component(req.path); if (base.empty()) { return 0; } auto &balloc = downstream->get_block_allocator(); for (auto &kv : resp.fs.headers()) { if (kv.token != http2::HD_LINK) { continue; } for (auto &link : http2::parse_link_header(kv.value)) { std::string_view scheme, authority, path; rv = http2::construct_push_component(balloc, scheme, authority, path, base, link.uri); if (rv != 0) { continue; } if (scheme.empty()) { scheme = req.scheme; } if (authority.empty()) { authority = req.authority; } if (resp.is_resource_pushed(scheme, authority, path)) { continue; } rv = submit_push_promise(scheme, authority, path, downstream); if (rv != 0) { return -1; } resp.resource_pushed(scheme, authority, path); } } return 0; } int Http2Upstream::submit_push_promise(const std::string_view &scheme, const std::string_view &authority, const std::string_view &path, Downstream *downstream) { const auto &req = downstream->request(); std::vector nva; // 4 for :method, :scheme, :path and :authority nva.reserve(4 + req.fs.headers().size()); // just use "GET" for now nva.push_back(http2::make_field(":method"sv, "GET"sv)); nva.push_back(http2::make_field(":scheme"sv, scheme)); nva.push_back(http2::make_field(":path"sv, path)); nva.push_back(http2::make_field(":authority"sv, authority)); for (auto &kv : req.fs.headers()) { switch (kv.token) { // TODO generate referer case http2::HD__AUTHORITY: case http2::HD__SCHEME: case http2::HD__METHOD: case http2::HD__PATH: continue; case http2::HD_ACCEPT_ENCODING: case http2::HD_ACCEPT_LANGUAGE: case http2::HD_CACHE_CONTROL: case http2::HD_HOST: case http2::HD_USER_AGENT: nva.push_back( http2::make_field(kv.name, kv.value, http2::no_index(kv.no_index))); break; } } auto promised_stream_id = nghttp2_submit_push_promise( session_, NGHTTP2_FLAG_NONE, static_cast(downstream->get_stream_id()), nva.data(), nva.size(), nullptr); if (promised_stream_id < 0) { if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "nghttp2_submit_push_promise() failed: " << nghttp2_strerror(promised_stream_id); } if (nghttp2_is_fatal(promised_stream_id)) { return -1; } return 0; } if (LOG_ENABLED(INFO)) { std::stringstream ss; for (auto &nv : nva) { ss << TTY_HTTP_HD << as_string_view(nv.name, nv.namelen) << TTY_RST << ": " << as_string_view(nv.value, nv.valuelen) << "\n"; } ULOG(INFO, this) << "HTTP push request headers. promised_stream_id=" << promised_stream_id << "\n" << ss.str(); } return 0; } bool Http2Upstream::push_enabled() const { auto config = get_config(); return !(config->http2.no_server_push || nghttp2_session_get_remote_settings( session_, NGHTTP2_SETTINGS_ENABLE_PUSH) == 0 || config->http2_proxy); } int Http2Upstream::initiate_push(Downstream *downstream, const std::string_view &uri) { int rv; if (uri.empty() || !push_enabled() || (downstream->get_stream_id() % 2) == 0) { return 0; } const auto &req = downstream->request(); auto base = http2::get_pure_path_component(req.path); if (base.empty()) { return -1; } auto &balloc = downstream->get_block_allocator(); std::string_view scheme, authority, path; rv = http2::construct_push_component(balloc, scheme, authority, path, base, uri); if (rv != 0) { return -1; } if (scheme.empty()) { scheme = req.scheme; } if (authority.empty()) { authority = req.authority; } auto &resp = downstream->response(); if (resp.is_resource_pushed(scheme, authority, path)) { return 0; } rv = submit_push_promise(scheme, authority, path, downstream); if (rv != 0) { return -1; } resp.resource_pushed(scheme, authority, path); return 0; } int Http2Upstream::response_riovec(struct iovec *iov, int iovcnt) const { if (iovcnt == 0 || wb_.rleft() == 0) { return 0; } return wb_.riovec(iov, iovcnt); } void Http2Upstream::response_drain(size_t n) { wb_.drain(n); } bool Http2Upstream::response_empty() const { return wb_.rleft() == 0; } DefaultMemchunks *Http2Upstream::get_response_buf() { return &wb_; } Downstream * Http2Upstream::on_downstream_push_promise(Downstream *downstream, int32_t promised_stream_id) { // promised_stream_id is for backend HTTP/2 session, not for // frontend. auto promised_downstream = std::make_unique(this, handler_->get_mcpool(), 0); auto &promised_req = promised_downstream->request(); promised_downstream->set_downstream_stream_id(promised_stream_id); // Set associated stream in frontend promised_downstream->set_assoc_stream_id(downstream->get_stream_id()); promised_downstream->disable_upstream_rtimer(); promised_req.http_major = 2; promised_req.http_minor = 0; promised_req.fs.content_length = 0; promised_req.http2_expect_body = false; auto ptr = promised_downstream.get(); add_pending_downstream(std::move(promised_downstream)); downstream_queue_.mark_active(ptr); return ptr; } int Http2Upstream::on_downstream_push_promise_complete( Downstream *downstream, Downstream *promised_downstream) { std::vector nva; const auto &promised_req = promised_downstream->request(); const auto &headers = promised_req.fs.headers(); nva.reserve(headers.size()); for (auto &kv : headers) { nva.push_back( http2::make_field_nv(kv.name, kv.value, http2::no_index(kv.no_index))); } auto promised_stream_id = nghttp2_submit_push_promise( session_, NGHTTP2_FLAG_NONE, static_cast(downstream->get_stream_id()), nva.data(), nva.size(), promised_downstream); if (promised_stream_id < 0) { return -1; } promised_downstream->set_stream_id(promised_stream_id); return 0; } void Http2Upstream::cancel_premature_downstream( Downstream *promised_downstream) { if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "Remove premature promised stream " << promised_downstream; } downstream_queue_.remove_and_get_blocked(promised_downstream, false); } size_t Http2Upstream::get_max_buffer_size() const { return max_buffer_size_; } } // namespace shrpx nghttp2-1.68.0/src/PaxHeaders/shrpx_mruby_module_response.h0000644000000000000000000000013115077107270021064 xustar0030 mtime=1761382072.998444116 30 atime=1761382106.191310484 29 ctime=1761382109.18730013 nghttp2-1.68.0/src/shrpx_mruby_module_response.h0000644000175100017510000000267415077107270021466 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_MRUBY_MODULE_RESPONSE_H #define SHRPX_MRUBY_MODULE_RESPONSE_H #include "shrpx.h" #include namespace shrpx { namespace mruby { void init_response_class(mrb_state *mrb, RClass *module); } // namespace mruby } // namespace shrpx #endif // !defined(SHRPX_MRUBY_MODULE_RESPONSE_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_mruby_module.h0000644000000000000000000000013015077107270017145 xustar0029 mtime=1761382072.99744412 29 atime=1761382106.18331052 30 ctime=1761382109.179300153 nghttp2-1.68.0/src/shrpx_mruby_module.h0000644000175100017510000000314715077107270017544 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_MRUBY_MODULE_H #define SHRPX_MRUBY_MODULE_H #include "shrpx.h" #include #include "http2.h" using namespace nghttp2; namespace shrpx { class Downstream; namespace mruby { mrb_value init_module(mrb_state *mrb); void delete_downstream_from_module(mrb_state *mrb, Downstream *downstream); mrb_value create_headers_hash(mrb_state *mrb, const HeaderRefs &headers); } // namespace mruby } // namespace shrpx #endif // !defined(SHRPX_MRUBY_MODULE_H) nghttp2-1.68.0/src/PaxHeaders/shrpx_signal.cc0000644000000000000000000000013115077107270016056 xustar0030 mtime=1761382072.999444111 29 atime=1761382106.15131066 30 ctime=1761382109.147300245 nghttp2-1.68.0/src/shrpx_signal.cc0000644000175100017510000000647415077107270016462 0ustar00runnerrunner/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_signal.h" #include #include "shrpx_log.h" #include "template.h" using namespace nghttp2; namespace shrpx { int shrpx_signal_block_all(sigset_t *oldset) { sigset_t newset; sigfillset(&newset); #ifndef NOTHREADS int rv; rv = pthread_sigmask(SIG_SETMASK, &newset, oldset); if (rv != 0) { errno = rv; return -1; } return 0; #else // defined(NOTHREADS) return sigprocmask(SIG_SETMASK, &newset, oldset); #endif // defined(NOTHREADS) } int shrpx_signal_unblock_all() { sigset_t newset; sigemptyset(&newset); #ifndef NOTHREADS int rv; rv = pthread_sigmask(SIG_SETMASK, &newset, nullptr); if (rv != 0) { errno = rv; return -1; } return 0; #else // defined(NOTHREADS) return sigprocmask(SIG_SETMASK, &newset, nullptr); #endif // defined(NOTHREADS) } int shrpx_signal_set(sigset_t *set) { #ifndef NOTHREADS int rv; rv = pthread_sigmask(SIG_SETMASK, set, nullptr); if (rv != 0) { errno = rv; return -1; } return 0; #else // defined(NOTHREADS) return sigprocmask(SIG_SETMASK, set, nullptr); #endif // defined(NOTHREADS) } namespace { template int signal_set_handler(void (*handler)(int), Signals &&sigs) { struct sigaction act{}; act.sa_handler = handler; sigemptyset(&act.sa_mask); int rv; for (auto sig : sigs) { rv = sigaction(sig, &act, nullptr); if (rv != 0) { return -1; } } return 0; } } // namespace namespace { constexpr auto main_proc_ign_signals = std::to_array({SIGPIPE}); } // namespace namespace { constexpr auto worker_proc_ign_signals = std::to_array({REOPEN_LOG_SIGNAL, EXEC_BINARY_SIGNAL, GRACEFUL_SHUTDOWN_SIGNAL, RELOAD_SIGNAL, SIGPIPE}); } // namespace int shrpx_signal_set_main_proc_ign_handler() { return signal_set_handler(SIG_IGN, main_proc_ign_signals); } int shrpx_signal_unset_main_proc_ign_handler() { return signal_set_handler(SIG_DFL, main_proc_ign_signals); } int shrpx_signal_set_worker_proc_ign_handler() { return signal_set_handler(SIG_IGN, worker_proc_ign_signals); } int shrpx_signal_unset_worker_proc_ign_handler() { return signal_set_handler(SIG_DFL, worker_proc_ign_signals); } } // namespace shrpx nghttp2-1.68.0/PaxHeaders/README0000644000000000000000000000013215077107270013140 xustar0030 mtime=1761382072.961444287 30 atime=1761382104.852316383 30 ctime=1761382107.837304032 nghttp2-1.68.0/README0000644000175100017510000000001715077107270013526 0ustar00runnerrunnerSee README.rst